xref: /mOS-networking-stack/core/src/ip_out.c (revision 76404edc)
1 #include <string.h>
2 
3 #include "ip_out.h"
4 #include "ip_in.h"
5 #include "eth_out.h"
6 #include "arp.h"
7 #include "debug.h"
8 #include "config.h"
9 
10 /*----------------------------------------------------------------------------*/
11 inline int
12 GetOutputInterface(uint32_t daddr)
13 {
14 	int nif = -1;
15 	int i;
16 	int prefix = -1;
17 
18 #ifdef ENABLE_DPDKR
19 	/* currently we assume that virtual ports are just looping back */
20 	return 0;
21 #endif
22 
23 	/* Longest prefix matching */
24 	for (i = 0; i < g_config.mos->route_table->num; i++) {
25 		if ((daddr & g_config.mos->route_table->ent[i]->mask)
26 			== g_config.mos->route_table->ent[i]->masked_ip) {
27 			if (g_config.mos->route_table->ent[i]->prefix > prefix) {
28 				nif = g_config.mos->route_table->ent[i]->nif;
29 				prefix = g_config.mos->route_table->ent[i]->prefix;
30 			}
31 		}
32 	}
33 
34 	if (nif < 0) {
35 		uint8_t *da = (uint8_t *)&daddr;
36 		TRACE_ERROR("[WARNING] No route to %u.%u.%u.%u\n",
37 				da[0], da[1], da[2], da[3]);
38 		assert(0);
39 	}
40 
41 	return nif;
42 }
43 /*----------------------------------------------------------------------------*/
44 void
45 ForwardIPPacket(mtcp_manager_t mtcp, struct pkt_ctx *pctx)
46 {
47 	unsigned char * haddr;
48 	struct iphdr *iph;
49 	uint32_t daddr = 0;
50 
51 	if (g_config.mos->nic_forward_table != NULL) {
52 		pctx->out_ifidx =
53 			g_config.mos->nic_forward_table->nic_fwd_table[pctx->in_ifidx];
54 		if (pctx->out_ifidx != -1) {
55 			haddr = pctx->p.ethh->h_dest;
56 			goto fast_tx;
57 		}
58 	}
59 
60 	/* set daddr for easy code writing */
61 	daddr = pctx->p.iph->daddr;
62 
63 	if (pctx->out_ifidx < 0) {
64 		pctx->out_ifidx = GetOutputInterface(pctx->p.iph->daddr);
65 		if (pctx->out_ifidx < 0) {
66 			return;
67 		}
68 	}
69 
70 	haddr = GetDestinationHWaddr(daddr);
71 	if (!haddr) {
72 #ifdef RUN_ARP
73 		/* ARP requests will not be created if it's a standalone middlebox */
74 		if (!pctx->forward)
75 			RequestARP(mtcp, daddr,
76 				   pctx->out_ifidx, pctx->p.cur_ts);
77 #else
78 		uint8_t *da = (uint8_t *)&(pctx->p.iph->daddr);
79 		TRACE_INFO("[WARNING] The destination IP %u.%u.%u.%u "
80 			  "is not in ARP table!\n",
81 			  da[0], da[1], da[2], da[3]);
82 #endif
83 		return;
84 	}
85 
86 #ifdef SHARE_IO_BUFFER
87 	if (likely(mtcp->iom->set_wptr != NULL)) {
88 		int i;
89 		for (i = 0; i < ETH_ALEN; i++) {
90 			pctx->p.ethh->h_source[i] = g_config.mos->netdev_table->ent[pctx->out_ifidx]->haddr[i];
91 			pctx->p.ethh->h_dest[i] = haddr[i];
92 		}
93 		mtcp->iom->set_wptr(mtcp->ctx, pctx->out_ifidx, pctx->in_ifidx, pctx->batch_index);
94 		return;
95 	}
96 #endif
97 
98  fast_tx:
99 	iph = (struct iphdr *) EthernetOutput (mtcp, pctx, ETH_P_IP,
100 			pctx->out_ifidx, haddr, pctx->p.ip_len, pctx->p.cur_ts);
101 	if (iph)
102 		memcpy(iph, pctx->p.iph, pctx->p.ip_len);
103 	else
104 		TRACE_ERROR("Failed to send a packet\n"
105 				"NULL = EthernetOutput(%p, %d, %d, %p, %d)\n",
106 				mtcp, ETH_P_IP, pctx->out_ifidx, haddr, pctx->p.ip_len);
107 #ifdef PKTDUMP
108 	DumpPacket(mtcp,
109 			(char *)iph - sizeof(struct ethhdr),
110 			pctx->p.ip_len + sizeof(struct ethhdr),
111 			"OUT", pctx->out_ifidx);
112 #endif
113 }
114 /*----------------------------------------------------------------------------*/
115 inline void
116 FillOutPacketIPContext(struct pkt_ctx *pctx, struct iphdr *iph, int ip_len)
117 {
118 	pctx->p.iph = iph;
119 	pctx->p.ip_len = ip_len;
120 
121 	return;
122 }
123 /*----------------------------------------------------------------------------*/
124 uint8_t *
125 IPOutputStandalone(struct mtcp_manager *mtcp,
126 		uint16_t ip_id, uint32_t saddr, uint32_t daddr, uint16_t tcplen,
127 		struct pkt_ctx *pctx, uint32_t cur_ts)
128 {
129 	struct iphdr *iph;
130 	int nif;
131 	unsigned char * haddr;
132 	int rc = -1;
133 
134 	nif = GetOutputInterface(daddr);
135 	if (nif < 0)
136 		return NULL;
137 
138 	haddr = GetDestinationHWaddr(daddr);
139 	if (!haddr) {
140 #ifdef RUN_ARP
141 		/* ARP requests will not be created if it's a standalone middlebox */
142 		/* tcp will retry sending the packet later */
143 		if (!pctx->forward)
144 			RequestARP(mtcp, daddr, nif, mtcp->cur_ts);
145 #else
146 		uint8_t *da = (uint8_t *)&daddr;
147 		TRACE_INFO("[WARNING] The destination IP %u.%u.%u.%u "
148 			"is not in ARP table!\n",
149 			da[0], da[1], da[2], da[3]);
150 #endif
151 		return NULL;
152 	}
153 
154 	iph = (struct iphdr *)EthernetOutput(mtcp, pctx,
155 			ETH_P_IP, nif, haddr, tcplen + IP_HEADER_LEN, cur_ts);
156 	if (!iph) {
157 		return NULL;
158 	}
159 
160 	iph->ihl = IP_HEADER_LEN >> 2;
161 	iph->version = 4;
162 	iph->tos = 0;
163 	iph->tot_len = htons(IP_HEADER_LEN + tcplen);
164 	iph->id = htons(ip_id);
165 	iph->frag_off = htons(0x4000);	// no fragmentation
166 	iph->ttl = 64;
167 	iph->protocol = IPPROTO_TCP;
168 	iph->saddr = saddr;
169 	iph->daddr = daddr;
170 	iph->check = 0;
171 
172 	/* offload IP checkum if possible */
173 	if (likely(mtcp->iom->dev_ioctl != NULL))
174 		rc = mtcp->iom->dev_ioctl(mtcp->ctx, nif, PKT_TX_IP_CSUM, iph);
175 	/* otherwise calculate IP checksum in S/W */
176 	if (rc == -1)
177 		iph->check = ip_fast_csum(iph, iph->ihl);
178 
179 	FillOutPacketIPContext(pctx, iph, tcplen + IP_HEADER_LEN);
180 
181 	return (uint8_t *)(iph + 1);
182 }
183 /*----------------------------------------------------------------------------*/
184 uint8_t *
185 IPOutput(struct mtcp_manager *mtcp, tcp_stream *stream, uint16_t tcplen,
186 			struct pkt_ctx *pctx, uint32_t cur_ts)
187 {
188 	struct iphdr *iph;
189 	int nif;
190 	unsigned char *haddr;
191 	int rc = -1;
192 
193 	if (stream->sndvar->nif_out >= 0) {
194 		nif = stream->sndvar->nif_out;
195 	} else {
196 		nif = GetOutputInterface(stream->daddr);
197 		stream->sndvar->nif_out = nif;
198 	}
199 
200 	haddr = GetDestinationHWaddr(stream->daddr);
201 	if (!haddr) {
202 #ifdef RUN_ARP
203 		/* if not found in the arp table, send arp request and return NULL */
204 		/* ARP requests will not be created if it's a standalone middlebox */
205 		/* tcp will retry sending the packet later */
206 		if (!pctx->forward)
207 			RequestARP(mtcp, stream->daddr, stream->sndvar->nif_out, mtcp->cur_ts);
208 #else
209 		uint8_t *da = (uint8_t *)&stream->daddr;
210 		TRACE_INFO("[WARNING] The destination IP %u.%u.%u.%u "
211 			  "is not in ARP table!\n",
212 			  da[0], da[1], da[2], da[3]);
213 #endif
214 		return NULL;
215 	}
216 	iph = (struct iphdr *)EthernetOutput(mtcp, pctx, ETH_P_IP,
217 			stream->sndvar->nif_out, haddr, tcplen + IP_HEADER_LEN, cur_ts);
218 	if (!iph) {
219 		return NULL;
220 	}
221 
222 	iph->ihl = IP_HEADER_LEN >> 2;
223 	iph->version = 4;
224 	iph->tos = 0;
225 	iph->tot_len = htons(IP_HEADER_LEN + tcplen);
226 	iph->id = htons(stream->sndvar->ip_id++);
227 	iph->frag_off = htons(0x4000);	// no fragmentation
228 	iph->ttl = 64;
229 	iph->protocol = IPPROTO_TCP;
230 	iph->saddr = stream->saddr;
231 	iph->daddr = stream->daddr;
232 	iph->check = 0;
233 
234 	/* offload IP checkum if possible */
235 	if (likely(mtcp->iom->dev_ioctl != NULL))
236 		rc = mtcp->iom->dev_ioctl(mtcp->ctx, nif, PKT_TX_IP_CSUM, iph);
237 	/* otherwise calculate IP checksum in S/W */
238 	if (rc == -1)
239 		iph->check = ip_fast_csum(iph, iph->ihl);
240 
241 	FillOutPacketIPContext(pctx, iph, tcplen + IP_HEADER_LEN);
242 
243 	return (uint8_t *)(iph + 1);
244 }
245