embox

Форк
0
/
ip_output.c 
355 строк · 8.6 Кб
1
/**
2
 * @file
3
 * @brief The Internet Protocol (IP) output module.
4
 *
5
 * @date 03.12.09
6
 * @author Nikolay Korotky
7
 * @author Vladimir Sokolov
8
 * @author Ilia Vaprol
9
 */
10

11
#include <errno.h>
12

13
#include <util/math.h>
14

15
#include <net/netdevice.h>
16
#include <net/inetdevice.h>
17

18
#include <net/l3/ipv4/ip.h>
19
#include <net/l4/udp.h>
20
#include <net/socket/inet_sock.h>
21

22
#include <net/l3/route.h>
23
#include <net/l0/net_tx.h>
24
#include <net/lib/bootp.h>
25
#include <net/l3/ipv4/ip_fragment.h>
26
#include <net/skbuff.h>
27
#include <net/l3/icmpv4.h>
28
#include <linux/in.h>
29
#include <net/netfilter.h>
30
#include <kernel/printk.h>
31
#include <net/if_packet.h>
32
#include <net/l2/ethernet.h>
33

34
#include <embox/net/pack.h>
35
#include <net/lib/ipv4.h>
36

37
#define IP_DEBUG 0
38
#if IP_DEBUG
39
#include <arpa/inet.h>
40
#include <kernel/printk.h>
41
#define DBG(x) x
42
#else
43
#define DBG(x)
44
#endif
45

46
static const struct net_pack_out_ops ip_out_ops_struct;
47
const struct net_pack_out_ops *const ip_out_ops
48
		= &ip_out_ops_struct;
49

50
static int ip_xmit(struct sk_buff *skb) {
51
	int ret;
52
	in_addr_t daddr;
53
	struct net_header_info hdr_info;
54

55
	assert(skb != NULL);
56
	assert(skb->nh.iph != NULL);
57

58
	hdr_info.type = ETH_P_IP;
59
	hdr_info.dst_hw = NULL;
60
	hdr_info.src_hw = skb->dev->dev_addr;
61

62
	/* it's loopback/local or broadcast address? */
63
#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)
64
	if (ip_is_local_net_ns(skb->nh.iph->daddr, IP_LOCAL_BROADCAST,
65
				skb->dev->net_ns)) {
66
#else
67
	if (ip_is_local(skb->nh.iph->daddr, IP_LOCAL_BROADCAST)) {
68
#endif
69
		hdr_info.dst_p = NULL;
70
	}
71
	else {
72
		/* get dest ip from route table */
73
#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)
74
		ret = rt_fib_route_ip_net_ns(skb->nh.iph->daddr, &daddr,
75
					skb->dev->net_ns);
76
#else
77
		ret = rt_fib_route_ip(skb->nh.iph->daddr, &daddr);
78
#endif
79
		if (ret != 0) {
80
			DBG(printk("ip_xmit: unknown target for %s\n",
81
						inet_ntoa(*(struct in_addr *)&daddr)));
82
			skb_free(skb);
83
			return ret;
84
		}
85

86
		hdr_info.dst_p = &daddr;
87
		hdr_info.p_len = sizeof daddr;
88
	}
89

90
	return net_tx(skb, &hdr_info);
91
}
92

93
/* Fragments skb and sends it to the interface.
94
 * Returns -1 in case of error
95
 * As side effect frees incoming skb
96
 */
97
static int fragment_skb_and_send(struct sk_buff *skb, struct net_device *dev) {
98
	int ret;
99
	struct sk_buff_head tx_buf;
100
	struct sk_buff *s_tmp;
101

102
	skb->dev = dev;
103
	ret = ip_frag(skb, dev->mtu, &tx_buf);
104
	if (ret != 0) {
105
		skb_free(skb);
106
		return ret;
107
	}
108
	skb_free(skb);
109

110
	while (NULL != (s_tmp = skb_queue_pop(&tx_buf))) {
111
		s_tmp->dev = dev;
112
		ip_set_check_field(s_tmp->nh.iph);
113
		ret = ip_xmit(s_tmp);
114
		if (ret != 0) {
115
			break;
116
		}
117
	}
118
	skb_queue_purge(&tx_buf);
119
	return ret;
120
}
121

122

123
int ip_forward(struct sk_buff *skb) {
124
	iphdr_t *iph = ip_hdr(skb);
125
	int optlen = IP_HEADER_SIZE(iph) - IP_MIN_HEADER_SIZE;
126
	struct rt_entry *best_route;
127

128
#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)
129
	best_route = rt_fib_get_best_net_ns(iph->daddr,
130
						NULL, skb->dev->net_ns);
131
#else
132
	best_route = rt_fib_get_best(iph->daddr, NULL);
133
#endif
134

135
	/* Drop broadcast and multicast addresses of 2 and 3 layers
136
	 * Note, that some kinds of those addresses we can't get here, because
137
	 * they processed in other part of code - see ip_is_local(,true,xxx);
138
	 * And, of course, loopback packets must not be processed here
139
	 */
140
	if ((pkt_type(skb) != PACKET_HOST) || ipv4_is_multicast(iph->daddr)) {
141
		skb_free(skb);
142
		return 0;
143
	}
144

145
	/* IP Options is a security violation
146
	 * Try to return packet as close as possible, so check it before ttl processsing (RFC 1812)
147
	 */
148
	if (unlikely(optlen)) {
149
		icmp_discard(skb, ICMP_PARAM_PROB, ICMP_PTR_ERROR,
150
				(uint8_t)IP_MIN_HEADER_SIZE);
151
		return -1;
152
	}
153

154
	/* Check TTL and decrease it.
155
	 * We believe that this skb is ours and we can modify it
156
	 */
157
	if (unlikely(iph->ttl <= 1)) {
158
		icmp_discard(skb, ICMP_TIME_EXCEED, ICMP_TTL_EXCEED);
159
		return -1;
160
	}
161
	iph->ttl--; /* All routes have the same length */
162
	ip_set_check_field(iph);
163

164
	/* Check no route */
165
	if (!best_route) {
166
		icmp_discard(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
167
		return -1;
168
	}
169

170
	/* Should we send ICMP redirect */
171
	if (skb->dev == best_route->dev) {
172
		struct sk_buff *s_new = skb_copy(skb);
173
		if (s_new) {
174
			if (best_route->rt_gateway == INADDR_ANY) {
175
				in_addr_t daddr = iph->daddr; /* Extract addr to avoid unaligned pointer dereference */
176
				icmp_discard(s_new, ICMP_REDIRECT,
177
						ICMP_HOST_REDIRECT, &daddr);
178
			}
179
			else {
180
				icmp_discard(s_new, ICMP_REDIRECT,
181
						ICMP_NET_REDIRECT, best_route->rt_gateway);
182
			}
183
		}
184
		/* We can still proceed here */
185
	}
186

187
	if (ip_route(skb, NULL, best_route) < 0) {
188
		/* So we have something like arp problem */
189
		if (best_route->rt_gateway == INADDR_ANY) {
190
			icmp_discard(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH);
191
		} else {
192
			icmp_discard(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
193
		}
194
		return -1;
195
	}
196

197
	/* Fragment packet, if it's required */
198
	if (skb->len > best_route->dev->mtu) {
199
		if (!(iph->frag_off & htons(IP_DF))) {
200
			/* We can perform fragmentation */
201
			return fragment_skb_and_send(skb, best_route->dev);
202
		} else {
203
			/* Fragmentation is disabled */
204
			icmp_discard(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
205
					  (uint16_t)best_route->dev->mtu); /* Support RFC 1191 */
206
			return -1;
207
		}
208
	}
209

210
	return ip_xmit(skb);
211
}
212

213
static in_addr_t ip_get_dest_addr(const struct inet_sock *in_sk,
214
		const struct sockaddr *to,
215
		struct sk_buff **out_skb) {
216

217
	const struct sockaddr_in *to_in;
218

219
	to_in = (const struct sockaddr_in *)to;
220

221
	if (to_in != NULL) {
222
		return to_in->sin_addr.s_addr;
223
	} else if (in_sk != NULL) {
224
		return in_sk->dst_in.sin_addr.s_addr;
225
	} else {
226
		return (*out_skb)->nh.iph->saddr; /* make a reply */
227
	}
228
}
229

230
static int ip_make(const struct sock *sk,
231
		const struct sockaddr *to,
232
		size_t *data_size,
233
		struct sk_buff **out_skb) {
234

235
	size_t hdr_size, max_size;
236
	struct sk_buff *skb;
237
	struct net_device *dev;
238
	const struct inet_sock *in_sk;
239
	uint8_t proto;
240
	in_addr_t src_ip, dst_ip;
241
	int ret;
242
	int ip_length;
243

244
	assert(out_skb);
245
	assert(sk || *out_skb);
246
	assert(data_size);
247

248
	assert((to == NULL) || (to->sa_family == AF_INET));
249

250
	in_sk = to_const_inet_sock(sk);
251

252
	dst_ip = ip_get_dest_addr(in_sk, to, out_skb);
253

254
#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)
255
	net_namespace_p net_ns;
256
	fill_net_ns_from_sk(net_ns, sk, out_skb);
257
	ret = rt_fib_out_dev_net_ns(dst_ip, in_sk != NULL ? &in_sk->sk : NULL,
258
			&dev, net_ns);
259
#else
260
	ret = rt_fib_out_dev(dst_ip, in_sk != NULL ? &in_sk->sk : NULL, &dev);
261
#endif
262
	if (ret != 0) {
263
		DBG(printk("ip_make: unknown device for %s\n",
264
					inet_ntoa(*(struct in_addr *)&dst_ip)));
265
		return ret;
266
	}
267
	assert(dev != NULL);
268

269
	assert(inetdev_get_by_dev(dev) != NULL);
270
	//src_ip = inetdev_get_by_dev(dev)->ifa_address; /* TODO it's better! */
271
#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)
272
	ret = rt_fib_source_ip_net_ns(dst_ip, dev, &src_ip, dev->net_ns);
273
#else
274
	ret = rt_fib_source_ip(dst_ip, dev, &src_ip);
275
#endif
276
	if (ret != 0) {
277
		DBG(printk("ip_make: can't resolve source ip for %s\n",
278
					inet_ntoa(*(struct in_addr *)&dst_ip)));
279
		return ret;
280
	}
281

282
	proto = in_sk != NULL ? in_sk->sk.opt.so_protocol
283
			: (*out_skb)->nh.iph->proto;
284

285
	if (sk) {
286
		ip_length = ip_header_size((struct sock *)sk);
287
	} else {
288
		ip_length = IP_MIN_HEADER_SIZE;
289
	}
290

291

292
	hdr_size = dev->hdr_len + ip_length;
293
	max_size = min(dev->mtu, skb_max_size());
294
	if (hdr_size > max_size) {
295
		DBG(printk("ip_make: hdr_size %zu is too big (max %zu)\n",
296
					hdr_size, max_size));
297
		return -EMSGSIZE;
298
	}
299

300
#if 0
301
	/* TODO ip fragmentation accepts packets size <= 64 KB,
302
	 * so it would be something like this:
303
	 * *data_size = min(*data_size, 64000 - hdr_size); */
304
	*data_size = min(*data_size, max_size - hdr_size);
305
#endif
306

307
	skb = skb_realloc(hdr_size + *data_size, *out_skb);
308
	if (skb == NULL) {
309
		DBG(printk("ip_make: can't realloc packet for size %zu\n",
310
					hdr_size + *data_size));
311
		return -ENOMEM;
312
	}
313

314
	skb->dev = dev;
315
	skb->nh.raw = skb->mac.raw + dev->hdr_len;
316
	skb->h.raw = skb->nh.raw + ip_length;
317

318
	ip_build(skb->nh.iph, ip_length + *data_size,
319
			64, proto, src_ip, dst_ip);
320
	if (IP_MIN_HEADER_SIZE < ip_length) {
321
		ip_header_make_secure((struct sock *)sk, skb);
322
	}
323

324
	*out_skb = skb;
325

326
	return 0;
327
}
328

329
static int ip_snd(struct sk_buff *skb) {
330
	static uint16_t global_id = 1230;
331

332
	assert(skb != NULL);
333

334
	if (0 != nf_test_skb(NF_CHAIN_OUTPUT, NF_TARGET_ACCEPT, skb)) {
335
		DBG(printk("ip_snd: dropped by output netfilter\n"));
336
		skb_free(skb);
337
		return 0;
338
	}
339

340
	ip_set_id_field(skb->nh.iph, global_id++);
341
	ip_set_check_field(skb->nh.iph);
342

343
	if (skb->len > skb->dev->mtu) {
344
		if (!(skb->nh.iph->frag_off & htons(IP_DF))) {
345
			return fragment_skb_and_send(skb, skb->dev);
346
		}
347
	}
348

349
	return ip_xmit(skb);
350
}
351

352
static const struct net_pack_out_ops ip_out_ops_struct = {
353
	.make_pack = &ip_make,
354
	.snd_pack = &ip_snd
355
};
356

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.