3
* @brief Internet Control Message Protocol (ICMPv4)
7
* @author Alexander Batyukov
8
* @author Nikolay Korotky
9
* - remove callback interface
11
* @author Vladimir Sokolov
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>
36
#include <embox/net/pack.h>
37
#include <embox/net/proto.h>
39
#include <kernel/time/ktime.h>
41
EMBOX_NET_PROTO(ETH_P_IP, IPPROTO_ICMP, icmp_rcv,
42
net_proto_handle_error_none);
44
static int icmp_send(uint8_t type, uint8_t code, const void *body,
45
size_t body_sz, struct sk_buff *skb) {
49
if (ip_out_ops == NULL) {
50
log_error("ip_out_ops not implemented");
52
return -ENOSYS; /* error: not implemented */
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);
59
log_error("make_pack return %d", ret);
61
return ret; /* error: see ret */
63
else if (size != sizeof *icmp_hdr(skb) + body_sz) {
64
log_error("message is too big");
66
return -EMSGSIZE; /* error: message is too big */
69
icmp_build(icmp_hdr(skb), type, code, body, body_sz);
70
icmp_set_check_field(icmp_hdr(skb), ip_hdr(skb));
72
assert(ip_out_ops->snd_pack != NULL);
73
return ip_out_ops->snd_pack(skb);
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;
83
assert(emb_iph != NULL);
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");
91
return 0; /* error: invalid length */
94
if (!ip_check_version(emb_iph)) {
95
log_error("not ipv4");
97
return 0; /* error: not ipv4 */
100
if (ip_hdr(skb)->daddr != emb_iph->saddr) {
101
log_error("not my embedded packet");
103
return 0; /* error: not my embedded packet */
106
error_info = extra_info << 16 | icmph->code << 8 | icmph->type;
108
raw_err(skb, error_info);
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);
124
static int icmp_hnd_dest_unreach(const struct icmphdr *icmph,
125
const struct icmpbody_dest_unreach *dest_unreach,
126
struct sk_buff *skb) {
129
switch (icmph->code) {
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:
142
len = ip_data_length(ip_hdr(skb));
143
if (sizeof *icmph + sizeof *dest_unreach > len) {
144
log_error("invalid length %zu", len);
146
return 0; /* error: invalid length */
148
len -= sizeof *icmph + sizeof *dest_unreach;
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);
156
static int icmp_hnd_source_quench(const struct icmphdr *icmph,
157
const struct icmpbody_source_quench *source_quench,
158
struct sk_buff *skb) {
161
if (icmph->code != 0) {
162
log_error("bad code 0x%x", icmph->code);
164
return 0; /* error: bad code */
167
len = ip_data_length(ip_hdr(skb));
168
if (sizeof *icmph + sizeof *source_quench > len) {
169
log_error("invalid length %zu", len);
171
return 0; /* error: invalid length */
173
len -= sizeof *icmph + sizeof *source_quench;
175
return icmp_notify_an_error(icmph, &source_quench->msg[0],
179
static int icmp_hnd_echo_request(const struct icmphdr *icmph,
180
const struct icmpbody_echo *echo_req,
181
struct sk_buff *skb) {
183
struct icmpbody_echo *echo_rep;
185
if (icmph->code != 0) {
186
log_error("bad code 0x%x", icmph->code);
188
return 0; /* error: bad code */
191
len = ip_data_length(ip_hdr(skb));
192
if (sizeof *icmph + sizeof *echo_req > len) {
193
log_error("invalid length %zu", len);
195
return 0; /* error: invalid length */
197
len -= sizeof *icmph + sizeof *echo_req;
199
echo_rep = &icmp_hdr(skb)->body.echo;
201
return icmp_send(ICMP_ECHO_REPLY, 0, echo_rep,
202
sizeof *echo_rep + len, skb);
205
static int icmp_hnd_param_prob(const struct icmphdr *icmph,
206
const struct icmpbody_param_prob *param_prob,
207
struct sk_buff *skb) {
210
switch (icmph->code) {
212
log_error("bad code 0x%x", icmph->code);
214
return 0; /* error: bad code */
216
case ICMP_PTR_UNUSED:
220
len = ip_data_length(ip_hdr(skb));
221
if (sizeof *icmph + sizeof *param_prob > len) {
223
return 0; /* error: invalid length */
225
len -= sizeof *icmph + sizeof *param_prob;
227
return icmp_notify_an_error(icmph, ¶m_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 *)¶m_prob->msg[0])),
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;
241
struct icmpbody_timestamp *tstamp_rep;
243
if (icmph->code != 0) {
245
return 0; /* error: bad code */
248
if (sizeof *icmph + sizeof *tstamp_req
249
!= ip_data_length(ip_hdr(skb))) {
251
return 0; /* error: invalid length */
254
gettimeofday(&tv, NULL);
255
ms = timeval_to_ms(&tv);
256
msec_since_12am = ms % (SEC_PER_DAY * MSEC_PER_SEC);
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);
262
return icmp_send(ICMP_TIMESTAMP_REPLY, 0, tstamp_rep,
263
sizeof *tstamp_rep, skb);
266
static int icmp_rcv(struct sk_buff *skb) {
267
struct icmphdr *icmph;
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)));
275
return 0; /* error: invalid length */
278
if (NULL == skb_declone(skb)) {
279
log_error("can't declone data");
281
return -ENOMEM; /* error: can't declone data */
284
icmph = icmp_hdr(skb);
285
assert(icmph != NULL);
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");
292
return 0; /* error: bad checksum */
295
switch (icmph->type) {
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:
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);
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);
325
int icmp_discard(struct sk_buff *skb, uint8_t type, uint8_t code,
328
#pragma clang diagnostic push
329
#pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end"
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;
342
#pragma clang diagnostic pop
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,
354
ip_hdr(skb)->saddr, 0)
355
|| ip_is_local(ip_hdr(skb)->daddr, 0))
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");
364
return 0; /* error: inappropriate packet */
368
#pragma clang diagnostic push
369
#pragma clang diagnostic ignored "-Wvarargs"
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));
384
body_msg = &body.dest_unreach.msg[0];
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];
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);
399
body_msg = &body.redirect.msg[0];
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];
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;
415
body_msg = &body.param_prob.msg[0];
419
#pragma clang diagnostic pop // -Wvarargs
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);
426
if (NULL == skb_declone(skb)) {
427
log_error("can't declone data");
429
return -ENOMEM; /* error: can't declone data */
432
return icmp_send(type, code, &body,
433
sizeof body - sizeof body.__body_msg_storage + body_msg_sz,