xref: /mOS-networking-stack/core/src/eth_out.c (revision a5e1a556)
1 #include <stdio.h>
2 
3 #include <stdint.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <assert.h>
7 
8 #ifdef DARWIN
9 #include <netinet/if_ether.h>
10 #include <netinet/tcp.h>
11 #else
12 #include <linux/if_ether.h>
13 #include <linux/tcp.h>
14 #endif
15 #include <string.h>
16 #include <netinet/ip.h>
17 
18 #include "mtcp.h"
19 #include "arp.h"
20 #include "eth_out.h"
21 #include "debug.h"
22 #include "mos_api.h"
23 #include "config.h"
24 
25 #ifndef TRUE
26 #define TRUE (1)
27 #endif
28 
29 #ifndef FALSE
30 #define FALSE (0)
31 #endif
32 
33 #ifndef ERROR
34 #define ERROR (-1)
35 #endif
36 
37 #define MAX(a, b) ((a)>(b)?(a):(b))
38 #define MIN(a, b) ((a)<(b)?(a):(b))
39 
40 #define MAX_WINDOW_SIZE 65535
41 
42 /*----------------------------------------------------------------------------*/
43 enum ETH_BUFFER_RETURN {BUF_RET_MAYBE, BUF_RET_ALWAYS};
44 /*----------------------------------------------------------------------------*/
45 #if !(E_PSIO || USE_CHUNK_BUF)
46 /* XXX - ignored at the moment (this is disabled by default) */
47 inline void
48 InitWriteChunks(struct ps_handle* handle, struct ps_chunk *w_chunk)
49 {
50 	int i, ret;
51 	for (i = 0; i < ETH_NUM; i++)
52 	{
53 		ret = ps_alloc_chunk(handle, &w_chunk[i]);
54 		if (ret != 0)
55 		{
56 			perror("ps_alloc_chunk");
57 			exit(1);
58 		}
59 		w_chunk[i].queue.ifindex = i;
60 		w_chunk[i].recv_blocking = 0;
61 		w_chunk[i].cnt = 0;
62 	}
63 }
64 /*----------------------------------------------------------------------------*/
65 int
66 FlushWriteBuffer(struct mtcp_thread_context* ctx, int ifidx)
67 {
68 	int ret = 0;
69 	struct ps_chunk* w_chunk = ctx->w_chunk;
70 	mtcp_manager_t mtcp = ctx->mtcp_manager;
71 	int i;
72 	int drop = 0;
73 	assert(ctx != NULL);
74 	assert(w_chunk != NULL);
75 
76 	if (w_chunk[ifidx].cnt > 0) {
77 
78 		STAT_COUNT(mtcp->runstat.rounds_tx_try);
79 
80 		ret = ps_send_chunk(ctx->handle, &w_chunk[ifidx]);
81 		drop = ctx->w_chunk[ifidx].cnt - ret;
82 
83 		if (ret < 0) {
84 			TRACE_ERROR("ps_send_chunk failed to send chunks, %d:%d\n",
85 					ifidx, w_chunk[ifidx].cnt);
86 			return ret;
87 		} else {
88 #ifdef NETSTAT
89 			mtcp->nstat.tx_packets[ifidx] += ret;
90 #endif /* NETSTAT */
91 
92 			for (i = 0; i < ret; i++) {
93 #ifdef PKTDUMP
94 				DumpPacket(mtcp,
95 						w_chunk[ifidx].buf + w_chunk[ifidx].info[i].offset,
96 						w_chunk[ifidx].info[i].len, "OUT", ifidx);
97 #endif /* PKTDUMP */
98 
99 #ifdef NETSTAT
100 				mtcp->nstat.tx_bytes[ifidx] += w_chunk[ifidx].info[i].len + 24;
101 #endif /* NETSTAT */
102 			}
103 
104 #ifdef NETSTAT
105 			if (ret != w_chunk[ifidx].cnt) {
106 				mtcp->nstat.tx_drops[ifidx] += (w_chunk[ifidx].cnt - ret);
107 			}
108 #endif /* NETSTAT */
109 
110 			if (ret == 0) {
111 				return ret;
112 			}
113 		}
114 
115 #ifdef PKTDUMP
116 		thread_printf(mtcp, mtcp->log_fp, "sent chunks, ret: %d (tries: %d)\n",
117 				ret, w_chunk[ifidx].cnt);
118 		thread_printf(mtcp, mtcp->log_fp, "======================================"
119 					"======================================================"
120 					"====================\n\n");
121 #endif /* PKTDUMP */
122 
123 		if (drop > 0) {
124 			ctx->w_chunk[ifidx].cnt = drop;
125 			for (i = 0; i < drop; i++) {
126 				ctx->w_chunk[ifidx].info[i].len =
127 						ctx->w_chunk[ifidx].info[ret + i].len;
128 				ctx->w_chunk[ifidx].info[i].offset =
129 					ctx->w_chunk[ifidx].info[ret + i].offset;
130 			}
131 			ctx->w_off[ifidx] = ctx->w_chunk[ifidx].info[drop - 1].offset +
132 					(ctx->w_chunk[ifidx].info[drop - 1].len + 63) / 64 * 64;
133 			ctx->w_cur_idx[ifidx] += ret;
134 		} else {
135 			ctx->w_chunk[ifidx].cnt = 0;
136 			ctx->w_off[ifidx] = 0;
137 			ctx->w_cur_idx[ifidx] = 0;
138 		}
139 
140 	}
141 
142 	return ret;
143 }
144 /*----------------------------------------------------------------------------*/
145 static inline char *
146 GetWriteBuffer(struct mtcp_thread_context *ctx, int method, int ifidx, int len)
147 {
148 	struct ps_chunk *w_chunk = ctx->w_chunk;
149 	uint32_t *w_off = ctx->w_off;
150 	int w_idx;
151 
152 	assert(w_chunk != NULL);
153 	assert(w_off != NULL);
154 
155 	if (ifidx < 0 || ifidx >= g_config.mos->netdev_table->num )
156 		return NULL;
157 
158 	//pthread_mutex_lock(&ctx->send_lock);
159 
160 	if (ctx->w_cur_idx[ifidx] + w_chunk[ifidx].cnt >= MAX_SEND_PCK_CHUNK) {
161 		if (method == BUF_RET_MAYBE) {
162 			return NULL;
163 		} else if (method == BUF_RET_ALWAYS) {
164 			if (FlushWriteBuffer(ctx, ifidx) <= 0)
165 				return NULL;
166 		} else {
167 			assert(0);
168 		}
169 	}
170 
171 	assert(ctx->w_cur_idx[ifidx] + w_chunk[ifidx].cnt < MAX_SEND_PCK_CHUNK);
172 	assert(w_off[ifidx] < MAX_PACKET_SIZE * MAX_CHUNK_SIZE);
173 
174 	w_idx = w_chunk[ifidx].cnt++;
175 	w_chunk[ifidx].info[w_idx].len = len;
176 	w_chunk[ifidx].info[w_idx].offset = w_off[ifidx];
177 	w_off[ifidx] += (len + 63) / 64 * 64;
178 
179 	//pthread_mutex_unlock(&ctx->send_lock);
180 
181 	return (w_chunk[ifidx].buf + w_chunk[ifidx].info[w_idx].offset);
182 }
183 /*----------------------------------------------------------------------------*/
184 #else /* E_PSIO */
185 int
186 FlushSendChunkBuf(mtcp_manager_t mtcp, int nif)
187 {
188 	return 0;
189 }
190 #endif /* E_PSIO */
191 /*----------------------------------------------------------------------------*/
192 inline void
193 FillOutPacketEthContext(struct pkt_ctx *pctx, uint32_t cur_ts, int out_ifidx,
194 						struct ethhdr *ethh, int eth_len)
195 {
196 	pctx->p.cur_ts = cur_ts;
197 	pctx->in_ifidx = -1;
198 	pctx->out_ifidx = out_ifidx;
199 	pctx->p.ethh = ethh;
200 	pctx->p.eth_len = eth_len;
201 }
202 /*----------------------------------------------------------------------------*/
203 uint8_t *
204 EthernetOutput(struct mtcp_manager *mtcp, struct pkt_ctx *pctx,
205 		uint16_t h_proto, int nif, unsigned char* dst_haddr, uint16_t iplen,
206 		uint32_t cur_ts)
207 {
208 	uint8_t *buf;
209 	struct ethhdr *ethh;
210 	int i;
211 #if E_PSIO || USE_CHUNK_BUF
212 	/*
213 	 * -sanity check-
214 	 * return early if no interface is set (if routing entry does not exist)
215 	 */
216 	if (nif < 0) {
217 		TRACE_INFO("No interface set!\n");
218 		return NULL;
219 	}
220 	if (!mtcp->iom->get_wptr) {
221 		TRACE_INFO("get_wptr() in io_module is undefined.");
222 		return NULL;
223 	}
224 	buf = mtcp->iom->get_wptr(mtcp->ctx, nif, iplen + ETHERNET_HEADER_LEN);
225 #else
226 	buf = GetWriteBuffer(mtcp->ctx,
227 			BUF_RET_MAYBE, nif, iplen + ETHERNET_HEADER_LEN);
228 #endif
229 	if (!buf) {
230 		TRACE_DBG("Failed to get available write buffer\n");
231 		return NULL;
232 	}
233 
234 	ethh = (struct ethhdr *)buf;
235 	for (i = 0; i < ETH_ALEN; i++) {
236 		ethh->h_source[i] = g_config.mos->netdev_table->ent[nif]->haddr[i];
237 		ethh->h_dest[i] = dst_haddr[i];
238 	}
239 	ethh->h_proto = htons(h_proto);
240 
241 	if (pctx)
242 		FillOutPacketEthContext(pctx, cur_ts, nif,
243 					ethh, iplen + ETHERNET_HEADER_LEN);
244 
245 	return (uint8_t *)(ethh + 1);
246 }
247 /*----------------------------------------------------------------------------*/
248 void
249 ForwardEthernetFrame(struct mtcp_manager *mtcp, struct pkt_ctx *pctx)
250 {
251 	uint8_t *buf;
252 
253 	if (g_config.mos->nic_forward_table != NULL) {
254 		pctx->out_ifidx =
255 			g_config.mos->nic_forward_table->nic_fwd_table[pctx->in_ifidx];
256 
257 		if (pctx->out_ifidx == -1) {
258 			TRACE_DBG("Could not find outgoing index (index)!\n");
259 			return;
260 		}
261 
262 		if (!mtcp->iom->get_wptr) {
263 			TRACE_INFO("get_wptr() in io_module is undefined.");
264 			return;
265 		}
266 
267 		buf = mtcp->iom->get_wptr(mtcp->ctx, pctx->out_ifidx, pctx->p.eth_len);
268 
269 		if (!buf) {
270 			TRACE_DBG("Failed to get available write buffer\n");
271 			return;
272 		}
273 
274 		memcpy(buf, pctx->p.ethh, pctx->p.eth_len);
275 	} else {
276 		TRACE_DBG("Ethernet forwarding table entry does not exist.\n");
277 	}
278 }
279 /*----------------------------------------------------------------------------*/
280