embox

Форк
0
/
ip_fragment.c 
324 строки · 7.6 Кб
1
/**
2
 * @file
3
 * @brief IP Fragmentation
4
 *
5
 * @date 25.11.2011
6
 * @author Alexander Kalmuk
7
 */
8

9
#include <errno.h>
10
#include <string.h>
11
#include <arpa/inet.h>
12

13
#include <net/l3/ipv4/ip_fragment.h>
14
#include <net/netdevice.h>
15
#include <net/skbuff.h>
16
#include <net/l3/icmpv4.h>
17
#include <net/l3/ipv4/ip.h>
18

19
#include <mem/objalloc.h>
20
#include <kernel/time/timer.h>
21
#include <kernel/time/time.h>
22

23
#include <util/math.h>
24
#include <lib/libds/dlist.h>
25
#include <util/log.h>
26

27
#include <framework/mod/options.h>
28

29
#define MAX_BUFS_CNT       OPTION_GET(NUMBER, max_uncomplete_cnt)
30
#define IP_FRAGMENTED_SUPP OPTION_GET(NUMBER, ip_fragmented_support)
31

32
/**
33
 * Datagram receive buffer
34
 */
35
struct dgram_buf {
36
	struct sk_buff_head fragments;
37
	struct dlist_head   next_buf; /* linked list pointers */
38
	struct buf_id {
39
		in_addr_t         saddr;
40
		in_addr_t         daddr;
41
		uint16_t          id;
42
		uint8_t           protocol;
43
	} buf_id;
44
	int               is_last_frag_received;
45
	int               meat;
46
	int               len; /* total length of original datagram */
47
	int               buf_ttl;
48
	int               is_deleted;
49
};
50

51
static DLIST_DEFINE(__dgram_buf_list);
52
static struct sys_timer ip_frag_timer;
53

54
OBJALLOC_DEF(__dgram_bufs, struct dgram_buf, MAX_BUFS_CNT);
55

56
#define df_flag(skb) (ntohs(skb->nh.iph->frag_off) & IP_DF)
57

58
#define TIMER_TICK 1000
59

60
static struct dgram_buf *ip_buf_create(struct iphdr *iph);
61
static void buf_delete(struct dgram_buf *buf);
62
static void ip_buf_add_skb(struct dgram_buf *buf, struct sk_buff *skb);
63
static struct sk_buff *build_packet(struct dgram_buf *buf);
64

65
static inline void buf_set_deleted(struct dgram_buf *buf) {
66
	buf->is_deleted = 1;
67
	skb_queue_purge(&buf->fragments);
68
}
69

70
static inline int ip_offset(struct sk_buff *skb) {
71
	int offset;
72

73
	offset = ntohs(skb->nh.iph->frag_off);
74
	offset &= IP_OFFSET;
75
	offset <<= 3;
76

77
	return offset;
78
}
79

80
static void ttl_handler(struct sys_timer *timer, void *param) {
81
	struct dgram_buf *buf = NULL;
82
	int i;
83

84
	i = 0;
85

86
	dlist_foreach_entry(buf, &__dgram_buf_list, next_buf) {
87
		i ++;
88
		if (buf->is_deleted) {
89
			continue;
90
		}
91

92
		if (buf->buf_ttl == 0) {
93
			/*icmp_send(buf->next_skbuff, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);*/
94
			buf_set_deleted(buf);
95
		} else {
96
			buf->buf_ttl -= 1;
97
		}
98
	}
99

100
	if (i == 0) {
101
		timer_stop(timer);
102
	}
103
}
104

105
static inline struct dgram_buf *ip_find(struct iphdr *iph) {
106
	struct dgram_buf *buf = NULL;
107

108
	assert(iph);
109

110
	dlist_foreach_entry(buf, &__dgram_buf_list, next_buf) {
111
		if (buf->is_deleted) {
112
			buf_delete(buf);
113
			continue;
114
		}
115
		if (buf->buf_id.daddr == iph->daddr
116
			&& buf->buf_id.saddr == iph->saddr
117
			&& buf->buf_id.protocol == iph->proto
118
			&& buf->buf_id.id == iph->id) {
119
			return buf;
120
		}
121
	}
122

123
	return NULL;
124
}
125

126
static void ip_buf_add_skb(struct dgram_buf *buf, struct sk_buff *skb) {
127
	int offset, data_len, end;
128

129
	assert(buf && skb);
130

131
	/* We use ttl >> 4 just to make sure time to wait is not very big.
132
	 * Usually, ttl is 64, so ttl >> 4 = 4 seconds */
133
	buf->buf_ttl = max(buf->buf_ttl, skb->nh.iph->ttl >> 4);
134

135
	offset = ip_offset(skb);
136

137
	data_len = skb->len - (skb->h.raw - skb->mac.raw);
138
	end = offset + data_len;
139

140
	skb_queue_push(&buf->fragments, skb);
141

142
	buf->meat += data_len;
143
	if (end > buf->len) {
144
		buf->len = end;
145
	}
146
}
147

148
static struct sk_buff *build_packet(struct dgram_buf *buf) {
149
	struct sk_buff *skb, *skb_iter;
150
	int offset = 0;
151
	int ihlen;
152

153
	assert(buf);
154

155
	skb_iter = skb_queue_front(&buf->fragments);
156
	assert(skb_iter);
157

158
	ihlen = (skb_iter->h.raw - skb_iter->mac.raw);
159
	skb = skb_alloc(buf->len + ihlen);
160
	/* Strange:
161
	 *	- it might return NULL, because length is too big now.
162
	 *	- ihlen has upper limit. So it's more wise to has such
163
	 *	amount of extra space in the pool (NOT shared with ICMP)
164
	 */
165
	assert(skb);
166

167
	/* copy and concatenate dat. Queue is NOT sorted by offset! */
168
	while((skb_iter = skb_queue_pop(&buf->fragments))) {
169
		offset = ip_offset(skb_iter);
170
		if (offset == 0) {
171
			memcpy(skb->mac.raw, skb_iter->mac.raw, skb_iter->len);
172
			/* Terrible. Some pointers might be NULL here. sk pointer is omitted */
173
			skb->nh.raw = skb->mac.raw + (skb_iter->nh.raw - skb_iter->mac.raw);
174
			skb->h.raw = skb->nh.raw + IP_HEADER_SIZE(ip_hdr(skb_iter));
175
			skb->nh.iph->tot_len = htons(buf->len + IP_HEADER_SIZE(ip_hdr(skb_iter)));
176
			skb->dev = skb_iter->dev;
177
		}
178
		memcpy(skb->mac.raw + ihlen + offset, skb_iter->mac.raw + ihlen,
179
				skb_iter->len - ihlen);
180
		skb_free(skb_iter);
181
	}
182

183
	/* recalculate length */
184
	skb->len = buf->len + ihlen;
185
	buf_set_deleted(buf);
186

187
	return skb;
188
}
189

190
static struct dgram_buf *ip_buf_create(struct iphdr *iph) {
191
	struct dgram_buf *buf;
192

193
	assert(iph);
194

195
	buf = (struct dgram_buf*) objalloc(&__dgram_bufs);
196
	if (!buf) {
197
		return NULL;
198
	}
199

200
	if (!timer_is_inited(&ip_frag_timer)) {
201
		timer_init(&ip_frag_timer, TIMER_PERIODIC, ttl_handler, NULL);
202
		log_debug("timer init");
203
	}
204
	timer_start(&ip_frag_timer, ms2jiffies(TIMER_TICK));
205

206
	skb_queue_init(&buf->fragments);
207
	dlist_head_init(&buf->next_buf);
208
	dlist_add_prev(&buf->next_buf, &__dgram_buf_list);
209

210
	buf->buf_id.protocol = iph->proto;
211
	buf->buf_id.id = iph->id;
212
	buf->buf_id.saddr = iph->saddr;
213
	buf->buf_id.daddr = iph->daddr;
214
	buf->len = 0;
215
	buf->is_last_frag_received = 0;
216
	buf->meat = 0;
217
	buf->buf_ttl = MSL;
218
	buf->is_deleted = 0;
219

220
	return buf;
221
}
222

223
static void buf_delete(struct dgram_buf *buf) {
224
	dlist_del(&buf->next_buf);
225
	objfree(&__dgram_bufs, (void*)buf);
226
}
227

228
static struct sk_buff *ip_frag_build(const struct sk_buff *big_skb, int frag_offset,
229
		int frag_size, int mf_flag) {
230
	struct sk_buff * frag;
231
	int len = big_skb->dev->hdr_len + IP_HEADER_SIZE(big_skb->nh.iph);
232

233
	if (unlikely(!(frag = skb_alloc(frag_size)))) {
234
		return NULL;
235
	}
236

237
	/* Copy IP and MAC headers */
238
	memcpy(frag->mac.raw, big_skb->mac.raw, len);
239
	/* Copy IP content */
240
	memcpy(frag->mac.raw + len, big_skb->mac.raw + frag_offset, frag_size - len);
241
	frag->nh.raw = frag->mac.raw + big_skb->dev->hdr_len;
242
	frag->nh.iph->frag_off = htons(
243
				(((frag_offset - len) >> 3) /* data offset / 8 */) | mf_flag);
244
	frag->nh.iph->tot_len = htons(frag_size - big_skb->dev->hdr_len);
245

246
	return frag;
247
}
248

249
struct sk_buff *ip_defrag(struct sk_buff *skb) {
250
	struct dgram_buf *buf;
251
	int mf_flag;
252

253
	assert(skb);
254

255
	/* if it is not complete packet */
256
	if (!(IP_FRAGMENTED_SUPP) || df_flag(skb)) {
257
		/* For some reason we don't like situation when someone used forced fragmentation */
258
		skb_free(skb);
259
		skb = (sk_buff_t *)NULL;
260
		return skb;
261
	}
262

263
	buf = ip_find(skb->nh.iph);
264
	if (!buf) {
265
		buf = ip_buf_create(skb->nh.iph);
266
	}
267
	assert(buf);
268

269
	ip_buf_add_skb(buf, skb);
270

271
	mf_flag = ntohs(skb->nh.iph->frag_off) & IP_MF;
272
	buf->is_last_frag_received = !mf_flag;
273

274
	if (buf->is_last_frag_received && buf->meat == buf->len) {
275
		return build_packet(buf);
276
	}
277

278
	return NULL;
279
}
280

281
int ip_frag(const struct sk_buff *skb, uint32_t mtu,
282
		struct sk_buff_head *tx_buf) {
283
	struct sk_buff *fragment;
284
	int len;
285
	int offset; /* offset from skb start (== mac.raw) */
286
	int align_MTU;
287

288
	assert(skb != NULL);
289
	assert(skb->dev != NULL);
290

291
	skb_queue_init(tx_buf);
292
	if (!IP_FRAGMENTED_SUPP) {
293
		skb_queue_push(tx_buf, (struct sk_buff *) skb);
294
		return 0;
295
	}
296

297
	offset = len = skb->dev->hdr_len + IP_HEADER_SIZE(skb->nh.iph);
298

299
	/* Note: correct MTU, because fragment offset must divide on 8*/
300
	align_MTU = mtu - (mtu - len) % 8;
301

302
	/* copy sk_buff without last fragment. All this fragments have size MTU */
303
	while (offset < skb->len - align_MTU + len) {
304
		fragment = ip_frag_build(skb, offset, align_MTU, IP_MF);
305
		if (!fragment) {
306
			skb_queue_purge(tx_buf);
307
			return -ENOMEM;
308
		}
309
		skb_queue_push(tx_buf, fragment);
310
		offset += (align_MTU - len);
311
	}
312

313
	/* copy last fragment */
314
	if (offset < skb->len) {
315
		fragment = ip_frag_build(skb, offset, skb->len - offset + len, 0);
316
		if (!fragment) {
317
			skb_queue_purge(tx_buf);
318
			return -ENOMEM;
319
		}
320
		skb_queue_push(tx_buf, fragment);
321
	}
322

323
	return 0;
324
}
325

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

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

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

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