embox

Форк
0
/
icmpv4.c 
435 строк · 11.1 Кб
1
/**
2
 * @file
3
 * @brief Internet Control Message Protocol (ICMPv4)
4
 * @details RFC 792
5
 *
6
 * @date 14.03.09
7
 * @author Alexander Batyukov
8
 * @author Nikolay Korotky
9
 * 		- remove callback interface
10
 * 		- major refactoring
11
 * @author Vladimir Sokolov
12
 * @author Ilia Vaprol
13
 */
14

15

16
#include <errno.h>
17
#include <assert.h>
18
#include <string.h>
19
#include <arpa/inet.h>
20
#include <stdarg.h>
21
#include <stdint.h>
22
#include <time.h>
23

24
#include <util/math.h>
25
#include <util/log.h>
26

27
#include <net/inetdevice.h>
28
#include <net/l3/ipv4/ip.h>
29
#include <net/l3/icmpv4.h>
30
#include <net/lib/icmpv4.h>
31
#include <net/socket/raw.h>
32
#include <net/l2/ethernet.h>
33
#include <net/if_packet.h>
34
#include <net/lib/ipv4.h>
35

36
#include <embox/net/pack.h>
37
#include <embox/net/proto.h>
38

39
#include <kernel/time/ktime.h>
40

41
EMBOX_NET_PROTO(ETH_P_IP, IPPROTO_ICMP, icmp_rcv,
42
		net_proto_handle_error_none);
43

44
static int icmp_send(uint8_t type, uint8_t code, const void *body,
45
		size_t body_sz, struct sk_buff *skb) {
46
	int ret;
47
	size_t size;
48

49
	if (ip_out_ops == NULL) {
50
		log_error("ip_out_ops not implemented");
51
		skb_free(skb);
52
		return -ENOSYS; /* error: not implemented */
53
	}
54

55
	size = sizeof *icmp_hdr(skb) + body_sz;
56
	assert(ip_out_ops->make_pack != NULL);
57
	ret = ip_out_ops->make_pack(NULL, NULL, &size, &skb);
58
	if (ret != 0) {
59
		log_error("make_pack return %d", ret);
60
		skb_free(skb);
61
		return ret; /* error: see ret */
62
	}
63
	else if (size != sizeof *icmp_hdr(skb) + body_sz) {
64
		log_error("message is too big");
65
		skb_free(skb);
66
		return -EMSGSIZE; /* error: message is too big */
67
	}
68

69
	icmp_build(icmp_hdr(skb), type, code, body, body_sz);
70
	icmp_set_check_field(icmp_hdr(skb), ip_hdr(skb));
71

72
	assert(ip_out_ops->snd_pack != NULL);
73
	return ip_out_ops->snd_pack(skb);
74
}
75

76
static int icmp_notify_an_error(const struct icmphdr *icmph,
77
		const void *msg, size_t msg_sz, uint16_t extra_info,
78
		int only_raw, struct sk_buff *skb) {
79
	const struct iphdr *emb_iph;
80
	uint32_t error_info;
81

82
	emb_iph = msg;
83
	assert(emb_iph != NULL);
84

85
	if ((msg_sz < IP_MIN_HEADER_SIZE)
86
			|| (IP_HEADER_SIZE(emb_iph) < IP_MIN_HEADER_SIZE)
87
			|| (ntohs(emb_iph->tot_len) < IP_HEADER_SIZE(emb_iph))
88
			|| (msg_sz < IP_HEADER_SIZE(emb_iph))) {
89
		log_error("invalid length");
90
		skb_free(skb);
91
		return 0; /* error: invalid length */
92
	}
93

94
	if (!ip_check_version(emb_iph)) {
95
		log_error("not ipv4");
96
		skb_free(skb);
97
		return 0; /* error: not ipv4 */
98
	}
99

100
	if (ip_hdr(skb)->daddr != emb_iph->saddr) {
101
		log_error("not my embedded packet");
102
		skb_free(skb);
103
		return 0; /* error: not my embedded packet */
104
	}
105

106
	error_info = extra_info << 16 | icmph->code << 8 | icmph->type;
107

108
	raw_err(skb, error_info);
109

110
	if (!only_raw) {
111
		const struct net_proto *nproto;
112
		nproto = net_proto_lookup(ETH_P_IP, emb_iph->proto);
113
		if (nproto != NULL) {
114
			assert(nproto->handle_error != NULL);
115
			nproto->handle_error(skb, error_info);
116
		}
117
	}
118

119
	skb_free(skb);
120

121
	return 0;
122
}
123

124
static int icmp_hnd_dest_unreach(const struct icmphdr *icmph,
125
		const struct icmpbody_dest_unreach *dest_unreach,
126
		struct sk_buff *skb) {
127
	size_t len;
128

129
	switch (icmph->code) {
130
	default:
131
		skb_free(skb);
132
		return 0; /* error: bad code */
133
	case ICMP_NET_UNREACH:
134
	case ICMP_HOST_UNREACH:
135
	case ICMP_PROT_UNREACH:
136
	case ICMP_PORT_UNREACH:
137
	case ICMP_FRAG_NEEDED:
138
	case ICMP_SR_FAILED:
139
		break;
140
	}
141

142
	len = ip_data_length(ip_hdr(skb));
143
	if (sizeof *icmph + sizeof *dest_unreach > len) {
144
		log_error("invalid length %zu", len);
145
		skb_free(skb);
146
		return 0; /* error: invalid length */
147
	}
148
	len -= sizeof *icmph + sizeof *dest_unreach;
149

150
	return icmp_notify_an_error(icmph, &dest_unreach->msg[0], len,
151
			icmph->code == ICMP_FRAG_NEEDED
152
				? ntohs(dest_unreach->mtu) : 0,
153
			icmph->code == ICMP_FRAG_NEEDED, skb);
154
}
155

156
static int icmp_hnd_source_quench(const struct icmphdr *icmph,
157
		const struct icmpbody_source_quench *source_quench,
158
		struct sk_buff *skb) {
159
	size_t len;
160

161
	if (icmph->code != 0) {
162
		log_error("bad code 0x%x", icmph->code);
163
		skb_free(skb);
164
		return 0; /* error: bad code */
165
	}
166

167
	len = ip_data_length(ip_hdr(skb));
168
	if (sizeof *icmph + sizeof *source_quench > len) {
169
		log_error("invalid length %zu", len);
170
		skb_free(skb);
171
		return 0; /* error: invalid length */
172
	}
173
	len -= sizeof *icmph + sizeof *source_quench;
174

175
	return icmp_notify_an_error(icmph, &source_quench->msg[0],
176
			len, 0, 0, skb);
177
}
178

179
static int icmp_hnd_echo_request(const struct icmphdr *icmph,
180
		const struct icmpbody_echo *echo_req,
181
		struct sk_buff *skb) {
182
	size_t len;
183
	struct icmpbody_echo *echo_rep;
184

185
	if (icmph->code != 0) {
186
		log_error("bad code 0x%x", icmph->code);
187
		skb_free(skb);
188
		return 0; /* error: bad code */
189
	}
190

191
	len = ip_data_length(ip_hdr(skb));
192
	if (sizeof *icmph + sizeof *echo_req > len) {
193
		log_error("invalid length %zu", len);
194
		skb_free(skb);
195
		return 0; /* error: invalid length */
196
	}
197
	len -= sizeof *icmph + sizeof *echo_req;
198

199
	echo_rep = &icmp_hdr(skb)->body.echo;
200

201
	return icmp_send(ICMP_ECHO_REPLY, 0, echo_rep,
202
			sizeof *echo_rep + len, skb);
203
}
204

205
static int icmp_hnd_param_prob(const struct icmphdr *icmph,
206
		const struct icmpbody_param_prob *param_prob,
207
		struct sk_buff *skb) {
208
	size_t len;
209

210
	switch (icmph->code) {
211
	default:
212
		log_error("bad code 0x%x", icmph->code);
213
		skb_free(skb);
214
		return 0; /* error: bad code */
215
	case ICMP_PTR_ERROR:
216
	case ICMP_PTR_UNUSED:
217
		break;
218
	}
219

220
	len = ip_data_length(ip_hdr(skb));
221
	if (sizeof *icmph + sizeof *param_prob > len) {
222
		skb_free(skb);
223
		return 0; /* error: invalid length */
224
	}
225
	len -= sizeof *icmph + sizeof *param_prob;
226

227
	return icmp_notify_an_error(icmph, &param_prob->msg[0], len,
228
			icmph->code == ICMP_PTR_ERROR ? param_prob->ptr : 0,
229
			(icmph->code == ICMP_PTR_ERROR)
230
				&& (param_prob->ptr < IP_HEADER_SIZE(
231
						(const struct iphdr *)&param_prob->msg[0])),
232
			skb);
233
}
234

235
static int icmp_hnd_timestamp_request(const struct icmphdr *icmph,
236
		const struct icmpbody_timestamp *tstamp_req,
237
		struct sk_buff *skb) {
238
	uint32_t msec_since_12am;
239
	struct timeval tv;
240
	time64_t ms;
241
	struct icmpbody_timestamp *tstamp_rep;
242

243
	if (icmph->code != 0) {
244
		skb_free(skb);
245
		return 0; /* error: bad code */
246
	}
247

248
	if (sizeof *icmph + sizeof *tstamp_req
249
			!= ip_data_length(ip_hdr(skb))) {
250
		skb_free(skb);
251
		return 0; /* error: invalid length */
252
	}
253

254
	gettimeofday(&tv, NULL);
255
	ms = timeval_to_ms(&tv);
256
	msec_since_12am = ms % (SEC_PER_DAY * MSEC_PER_SEC);
257

258
	tstamp_rep = &icmp_hdr(skb)->body.timestamp;
259
	tstamp_rep->orig = tstamp_req->trans;
260
	tstamp_rep->recv = tstamp_rep->trans = htonl(msec_since_12am);
261

262
	return icmp_send(ICMP_TIMESTAMP_REPLY, 0, tstamp_rep,
263
			sizeof *tstamp_rep, skb);
264
}
265

266
static int icmp_rcv(struct sk_buff *skb) {
267
	struct icmphdr *icmph;
268
	uint16_t old_check;
269

270
	log_debug("%p len %zu", skb, skb->len);
271
	if (sizeof *icmph > ip_data_length(ip_hdr(skb))) {
272
		log_error("invalid length (%zu > %zu)",
273
				sizeof *icmph, ip_data_length(ip_hdr(skb)));
274
		skb_free(skb);
275
		return 0; /* error: invalid length */
276
	}
277

278
	if (NULL == skb_declone(skb)) {
279
		log_error("can't declone data");
280
		skb_free(skb);
281
		return -ENOMEM; /* error: can't declone data */
282
	}
283

284
	icmph = icmp_hdr(skb);
285
	assert(icmph != NULL);
286

287
	old_check = icmph->check;
288
	icmp_set_check_field(icmph, ip_hdr(skb));
289
	if (old_check != icmph->check) {
290
		log_error("bad checksum");
291
		skb_free(skb);
292
		return 0; /* error: bad checksum */
293
	}
294

295
	switch (icmph->type) {
296
	default:
297
		log_error("icmp_rcv: unknown type: %hhu\n", icmph->type);
298
		break; /* error: unknown type */
299
	case ICMP_ECHO_REPLY:
300
	case ICMP_TIMESTAMP_REPLY:
301
	case ICMP_INFO_REPLY:
302
		break;
303
	case ICMP_DEST_UNREACH:
304
		return icmp_hnd_dest_unreach(icmph, &icmph->body.dest_unreach, skb);
305
	case ICMP_SOURCE_QUENCH:
306
		return icmp_hnd_source_quench(icmph,
307
				&icmph->body.source_quench, skb);
308
	case ICMP_REDIRECT:
309
	case ICMP_TIME_EXCEED:
310
	case ICMP_INFO_REQUEST:
311
		break; /* error: not implemented */
312
	case ICMP_ECHO_REQUEST:
313
		return icmp_hnd_echo_request(icmph, &icmph->body.echo, skb);
314
	case ICMP_PARAM_PROB:
315
		return icmp_hnd_param_prob(icmph, &icmph->body.param_prob, skb);
316
	case ICMP_TIMESTAMP_REQUEST:
317
		return icmp_hnd_timestamp_request(icmph,
318
				&icmph->body.timestamp, skb);
319
	}
320

321
	skb_free(skb);
322
	return 0;
323
}
324

325
int icmp_discard(struct sk_buff *skb, uint8_t type, uint8_t code,
326
		...) {
327
#ifdef __clang__
328
#pragma clang diagnostic push
329
#pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end"
330
#endif
331
	struct {
332
		union {
333
			struct icmpbody_dest_unreach dest_unreach;
334
			struct icmpbody_source_quench source_quench;
335
			struct icmpbody_redirect redirect;
336
			struct icmpbody_time_exceed time_exceed;
337
			struct icmpbody_param_prob param_prob;
338
		} __attribute__((packed));
339
		char __body_msg_storage[ICMP_DISCARD_MAX_SIZE];
340
	} __attribute__((packed)) body;
341
#ifdef __clang__
342
#pragma clang diagnostic pop
343
#endif
344
	va_list extra;
345
	uint8_t *body_msg;
346
	size_t body_msg_sz;
347
#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)
348
	if (!(ip_is_local_net_ns(
349
			ip_hdr(skb)->saddr, 0, skb->dev->net_ns)
350
			|| ip_is_local_net_ns(ip_hdr(skb)->daddr, 0,
351
						skb->dev->net_ns))
352
#else
353
	if (!(ip_is_local(
354
			ip_hdr(skb)->saddr, 0)
355
			|| ip_is_local(ip_hdr(skb)->daddr, 0))
356
#endif
357
			|| (ip_hdr(skb)->frag_off & htons(IP_OFFSET))
358
			|| (ip_data_length(ip_hdr(skb)) < ICMP_DISCARD_MIN_SIZE)
359
			|| (ip_hdr(skb)->proto != IPPROTO_ICMP)
360
			|| (skb->h.raw = skb->nh.raw + IP_HEADER_SIZE(ip_hdr(skb)),
361
			ICMP_TYPE_ERROR(icmp_hdr(skb)->type))) {
362
		log_error("inappropriate packet");
363
		skb_free(skb);
364
		return 0; /* error: inappropriate packet */
365
	}
366

367
#ifdef __clang__
368
#pragma clang diagnostic push
369
#pragma clang diagnostic ignored "-Wvarargs"
370
#endif
371
	switch (type) {
372
	default:
373
		assertf(0, "bad type for discard");
374
		body_msg = (uint8_t *)&body.__body_msg_storage[0];
375
		break; /* error: bad type for discard */
376
	case ICMP_DEST_UNREACH:
377
		assertf(code < __ICMP_DEST_UNREACH_MAX,
378
				"incorrect code for type");
379
		va_start(extra, code);
380
		body.dest_unreach.zero = 0;
381
		body.dest_unreach.mtu = code != ICMP_FRAG_NEEDED ? 0
382
				: htons((uint16_t)va_arg(extra, int));
383
		va_end(extra);
384
		body_msg = &body.dest_unreach.msg[0];
385
		break;
386
	case ICMP_SOURCE_QUENCH:
387
		assertf(code == 0, "incorrect code for type");
388
		body.source_quench.zero = 0;
389
		body_msg = &body.source_quench.msg[0];
390
		break;
391
	case ICMP_REDIRECT:
392
		assertf(code < __ICMP_REDIRECT_MAX,
393
				"incorrect code for type");
394
		va_start(extra, code);
395
		memcpy(&body.redirect.gateway,
396
				va_arg(extra, struct in_addr *),
397
				sizeof body.redirect.gateway);
398
		va_end(extra);
399
		body_msg = &body.redirect.msg[0];
400
		break;
401
	case ICMP_TIME_EXCEED:
402
		assertf(code < __ICMP_TIME_EXCEED_MAX,
403
				"incorrect code for type");
404
		body.time_exceed.zero = 0;
405
		body_msg = &body.time_exceed.msg[0];
406
		break;
407
	case ICMP_PARAM_PROB:
408
		assertf(code < __ICMP_PARAM_PROB_MAX,
409
				"incorrect code for type");
410
		va_start(extra, code);
411
		body.param_prob.ptr = code != ICMP_PTR_ERROR ? 0
412
				: (uint8_t)va_arg(extra, int);
413
		body.param_prob.zero1 = body.param_prob.zero2 = 0;
414
		va_end(extra);
415
		body_msg = &body.param_prob.msg[0];
416
		break;
417
	}
418
#ifdef __clang__
419
#pragma clang diagnostic pop // -Wvarargs
420
#endif
421

422
	body_msg_sz = min(ip_data_length(ip_hdr(skb)),
423
			sizeof body.__body_msg_storage);
424
	memcpy(body_msg, ip_hdr(skb), body_msg_sz);
425

426
	if (NULL == skb_declone(skb)) {
427
		log_error("can't declone data");
428
		skb_free(skb);
429
		return -ENOMEM; /* error: can't declone data */
430
	}
431

432
	return icmp_send(type, code, &body,
433
			sizeof body - sizeof body.__body_msg_storage + body_msg_sz,
434
			skb);
435
}
436

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

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

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

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