Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / net / icmp6.c
blob78223946031686a256c71e3baa133f489a1e7dce
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/net.h>
20 #include <grub/net/ip.h>
21 #include <grub/net/netbuff.h>
23 struct icmp_header
25 grub_uint8_t type;
26 grub_uint8_t code;
27 grub_uint16_t checksum;
28 } GRUB_PACKED;
30 struct ping_header
32 grub_uint16_t id;
33 grub_uint16_t seq;
34 } GRUB_PACKED;
36 struct router_adv
38 grub_uint8_t ttl;
39 grub_uint8_t flags;
40 grub_uint16_t router_lifetime;
41 grub_uint32_t reachable_time;
42 grub_uint32_t retrans_timer;
43 grub_uint8_t options[0];
44 } GRUB_PACKED;
46 struct option_header
48 grub_uint8_t type;
49 grub_uint8_t len;
50 } GRUB_PACKED;
52 struct prefix_option
54 struct option_header header;
55 grub_uint8_t prefixlen;
56 grub_uint8_t flags;
57 grub_uint32_t valid_lifetime;
58 grub_uint32_t preferred_lifetime;
59 grub_uint32_t reserved;
60 grub_uint64_t prefix[2];
61 } GRUB_PACKED;
63 struct neighbour_solicit
65 grub_uint32_t reserved;
66 grub_uint64_t target[2];
67 } GRUB_PACKED;
69 struct neighbour_advertise
71 grub_uint32_t flags;
72 grub_uint64_t target[2];
73 } GRUB_PACKED;
75 struct router_solicit
77 grub_uint32_t reserved;
78 } GRUB_PACKED;
80 enum
82 FLAG_SLAAC = 0x40
85 enum
87 ICMP6_ECHO = 128,
88 ICMP6_ECHO_REPLY = 129,
89 ICMP6_ROUTER_SOLICIT = 133,
90 ICMP6_ROUTER_ADVERTISE = 134,
91 ICMP6_NEIGHBOUR_SOLICIT = 135,
92 ICMP6_NEIGHBOUR_ADVERTISE = 136,
95 enum
97 OPTION_SOURCE_LINK_LAYER_ADDRESS = 1,
98 OPTION_TARGET_LINK_LAYER_ADDRESS = 2,
99 OPTION_PREFIX = 3
102 enum
104 FLAG_SOLICITED = (1 << 30),
105 FLAG_OVERRIDE = (1 << 29)
108 grub_err_t
109 grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
110 struct grub_net_card *card,
111 struct grub_net_network_level_interface *inf,
112 const grub_net_link_level_address_t *ll_src,
113 const grub_net_network_level_address_t *source,
114 const grub_net_network_level_address_t *dest,
115 grub_uint8_t ttl)
117 struct icmp_header *icmph;
118 grub_err_t err;
119 grub_uint16_t checksum;
121 icmph = (struct icmp_header *) nb->data;
123 if (nb->tail - nb->data < (grub_ssize_t) sizeof (*icmph))
125 grub_netbuff_free (nb);
126 return GRUB_ERR_NONE;
129 checksum = icmph->checksum;
130 icmph->checksum = 0;
131 if (checksum != grub_net_ip_transport_checksum (nb,
132 GRUB_NET_IP_ICMPV6,
133 source,
134 dest))
136 grub_dprintf ("net", "invalid ICMPv6 checksum: %04x instead of %04x\n",
137 checksum,
138 grub_net_ip_transport_checksum (nb,
139 GRUB_NET_IP_ICMPV6,
140 source,
141 dest));
142 icmph->checksum = checksum;
143 grub_netbuff_free (nb);
144 return GRUB_ERR_NONE;
146 icmph->checksum = checksum;
148 err = grub_netbuff_pull (nb, sizeof (*icmph));
149 if (err)
151 grub_netbuff_free (nb);
152 return err;
155 grub_dprintf ("net", "ICMPv6 message: %02x, %02x\n",
156 icmph->type, icmph->code);
157 switch (icmph->type)
159 case ICMP6_ECHO:
160 /* Don't accept multicast pings. */
161 if (!inf)
162 break;
164 struct grub_net_buff *nb_reply;
165 struct icmp_header *icmphr;
166 if (icmph->code)
167 break;
168 nb_reply = grub_netbuff_alloc (nb->tail - nb->data + 512);
169 if (!nb_reply)
171 grub_netbuff_free (nb);
172 return grub_errno;
174 err = grub_netbuff_reserve (nb_reply, nb->tail - nb->data + 512);
175 if (err)
176 goto ping_fail;
177 err = grub_netbuff_push (nb_reply, nb->tail - nb->data);
178 if (err)
179 goto ping_fail;
180 grub_memcpy (nb_reply->data, nb->data, nb->tail - nb->data);
181 err = grub_netbuff_push (nb_reply, sizeof (*icmphr));
182 if (err)
183 goto ping_fail;
184 icmphr = (struct icmp_header *) nb_reply->data;
185 icmphr->type = ICMP6_ECHO_REPLY;
186 icmphr->code = 0;
187 icmphr->checksum = 0;
188 icmphr->checksum = grub_net_ip_transport_checksum (nb_reply,
189 GRUB_NET_IP_ICMPV6,
190 &inf->address,
191 source);
192 err = grub_net_send_ip_packet (inf, source, ll_src, nb_reply,
193 GRUB_NET_IP_ICMPV6);
195 ping_fail:
196 grub_netbuff_free (nb);
197 grub_netbuff_free (nb_reply);
198 return err;
200 case ICMP6_NEIGHBOUR_SOLICIT:
202 struct neighbour_solicit *nbh;
203 struct grub_net_buff *nb_reply;
204 struct option_header *ohdr;
205 struct neighbour_advertise *adv;
206 struct icmp_header *icmphr;
207 grub_uint8_t *ptr;
209 if (icmph->code)
210 break;
211 if (ttl != 0xff)
212 break;
213 nbh = (struct neighbour_solicit *) nb->data;
214 err = grub_netbuff_pull (nb, sizeof (*nbh));
215 if (err)
217 grub_netbuff_free (nb);
218 return err;
220 for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
221 ptr += ohdr->len * 8)
223 ohdr = (struct option_header *) ptr;
224 if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail)
226 grub_netbuff_free (nb);
227 return GRUB_ERR_NONE;
229 if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS
230 && ohdr->len == 1)
232 grub_net_link_level_address_t ll_address;
233 ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
234 grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
235 grub_net_link_layer_add_address (card, source, &ll_address, 0);
238 FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
240 if (inf->card == card
241 && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6
242 && grub_memcmp (&inf->address.ipv6, &nbh->target, 16) == 0)
243 break;
245 if (!inf)
246 break;
248 nb_reply = grub_netbuff_alloc (sizeof (struct neighbour_advertise)
249 + sizeof (struct option_header)
251 + sizeof (struct icmp_header)
252 + GRUB_NET_OUR_IPV6_HEADER_SIZE
253 + GRUB_NET_MAX_LINK_HEADER_SIZE);
254 if (!nb_reply)
256 grub_netbuff_free (nb);
257 return grub_errno;
259 err = grub_netbuff_reserve (nb_reply,
260 sizeof (struct neighbour_advertise)
261 + sizeof (struct option_header)
263 + sizeof (struct icmp_header)
264 + GRUB_NET_OUR_IPV6_HEADER_SIZE
265 + GRUB_NET_MAX_LINK_HEADER_SIZE);
266 if (err)
267 goto ndp_fail;
269 err = grub_netbuff_push (nb_reply, 6);
270 if (err)
271 goto ndp_fail;
272 grub_memcpy (nb_reply->data, inf->hwaddress.mac, 6);
273 err = grub_netbuff_push (nb_reply, sizeof (*ohdr));
274 if (err)
275 goto ndp_fail;
276 ohdr = (struct option_header *) nb_reply->data;
277 ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS;
278 ohdr->len = 1;
279 err = grub_netbuff_push (nb_reply, sizeof (*adv));
280 if (err)
281 goto ndp_fail;
282 adv = (struct neighbour_advertise *) nb_reply->data;
283 adv->flags = grub_cpu_to_be32_compile_time (FLAG_SOLICITED
284 | FLAG_OVERRIDE);
285 grub_memcpy (&adv->target, &nbh->target, 16);
287 err = grub_netbuff_push (nb_reply, sizeof (*icmphr));
288 if (err)
289 goto ndp_fail;
290 icmphr = (struct icmp_header *) nb_reply->data;
291 icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE;
292 icmphr->code = 0;
293 icmphr->checksum = 0;
294 icmphr->checksum = grub_net_ip_transport_checksum (nb_reply,
295 GRUB_NET_IP_ICMPV6,
296 &inf->address,
297 source);
298 err = grub_net_send_ip_packet (inf, source, ll_src, nb_reply,
299 GRUB_NET_IP_ICMPV6);
301 ndp_fail:
302 grub_netbuff_free (nb);
303 grub_netbuff_free (nb_reply);
304 return err;
306 case ICMP6_NEIGHBOUR_ADVERTISE:
308 struct neighbour_advertise *nbh;
309 grub_uint8_t *ptr;
310 struct option_header *ohdr;
312 if (icmph->code)
313 break;
314 if (ttl != 0xff)
315 break;
316 nbh = (struct neighbour_advertise *) nb->data;
317 err = grub_netbuff_pull (nb, sizeof (*nbh));
318 if (err)
320 grub_netbuff_free (nb);
321 return err;
324 for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
325 ptr += ohdr->len * 8)
327 ohdr = (struct option_header *) ptr;
328 if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail)
330 grub_netbuff_free (nb);
331 return GRUB_ERR_NONE;
333 if (ohdr->type == OPTION_TARGET_LINK_LAYER_ADDRESS
334 && ohdr->len == 1)
336 grub_net_link_level_address_t ll_address;
337 ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
338 grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
339 grub_net_link_layer_add_address (card, source, &ll_address, 0);
342 break;
344 case ICMP6_ROUTER_ADVERTISE:
346 grub_uint8_t *ptr;
347 struct option_header *ohdr;
348 if (icmph->code)
349 break;
350 err = grub_netbuff_pull (nb, sizeof (struct router_adv));
351 if (err)
353 grub_netbuff_free (nb);
354 return err;
356 for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
357 ptr += ohdr->len * 8)
359 ohdr = (struct option_header *) ptr;
360 if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail)
362 grub_netbuff_free (nb);
363 return GRUB_ERR_NONE;
365 if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS
366 && ohdr->len == 1)
368 grub_net_link_level_address_t ll_address;
369 ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
370 grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
371 grub_net_link_layer_add_address (card, source, &ll_address, 0);
373 if (ohdr->type == OPTION_PREFIX && ohdr->len == 4)
375 struct prefix_option *opt = (struct prefix_option *) ptr;
376 struct grub_net_slaac_mac_list *slaac;
377 if (!(opt->flags & FLAG_SLAAC)
378 || (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80
379 || (grub_be_to_cpu32 (opt->preferred_lifetime)
380 > grub_be_to_cpu32 (opt->valid_lifetime))
381 || opt->prefixlen != 64)
383 grub_dprintf ("net", "discarded prefix: %d, %d, %d, %d\n",
384 !(opt->flags & FLAG_SLAAC),
385 (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80,
386 (grub_be_to_cpu32 (opt->preferred_lifetime)
387 > grub_be_to_cpu32 (opt->valid_lifetime)),
388 opt->prefixlen != 64);
389 continue;
391 for (slaac = card->slaac_list; slaac; slaac = slaac->next)
393 grub_net_network_level_address_t addr;
394 grub_net_network_level_netaddress_t netaddr;
396 if (slaac->address.type
397 != GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET)
398 continue;
399 addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
400 addr.ipv6[0] = opt->prefix[0];
401 addr.ipv6[1] = grub_net_ipv6_get_id (&slaac->address);
402 netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
403 netaddr.ipv6.base[0] = opt->prefix[0];
404 netaddr.ipv6.base[1] = 0;
405 netaddr.ipv6.masksize = 64;
407 FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
409 if (inf->card == card
410 && grub_net_addr_cmp (&inf->address, &addr) == 0)
411 break;
413 /* Update lease time if needed here once we have
414 lease times. */
415 if (inf)
416 continue;
418 grub_dprintf ("net", "creating slaac\n");
421 char *name;
422 name = grub_xasprintf ("%s:%d",
423 slaac->name, slaac->slaac_counter++);
424 if (!name)
426 grub_errno = GRUB_ERR_NONE;
427 continue;
429 inf = grub_net_add_addr (name,
430 card, &addr,
431 &slaac->address, 0);
432 grub_net_add_route (name, netaddr, inf);
433 grub_free (name);
438 if (ptr != nb->tail)
439 break;
443 grub_netbuff_free (nb);
444 return GRUB_ERR_NONE;
447 grub_err_t
448 grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf,
449 const grub_net_network_level_address_t *proto_addr)
451 struct grub_net_buff *nb;
452 grub_err_t err = GRUB_ERR_NONE;
453 int i;
454 struct option_header *ohdr;
455 struct neighbour_solicit *sol;
456 struct icmp_header *icmphr;
457 grub_net_network_level_address_t multicast;
458 grub_net_link_level_address_t ll_multicast;
459 grub_uint8_t *nbd;
460 multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
461 multicast.ipv6[0] = grub_be_to_cpu64_compile_time (0xff02ULL << 48);
462 multicast.ipv6[1] = (grub_be_to_cpu64_compile_time (0x01ff000000ULL)
463 | (proto_addr->ipv6[1]
464 & grub_be_to_cpu64_compile_time (0xffffff)));
466 err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
467 if (err)
468 return err;
470 nb = grub_netbuff_alloc (sizeof (struct neighbour_solicit)
471 + sizeof (struct option_header)
473 + sizeof (struct icmp_header)
474 + GRUB_NET_OUR_IPV6_HEADER_SIZE
475 + GRUB_NET_MAX_LINK_HEADER_SIZE);
476 if (!nb)
477 return grub_errno;
478 err = grub_netbuff_reserve (nb,
479 sizeof (struct neighbour_solicit)
480 + sizeof (struct option_header)
482 + sizeof (struct icmp_header)
483 + GRUB_NET_OUR_IPV6_HEADER_SIZE
484 + GRUB_NET_MAX_LINK_HEADER_SIZE);
485 err = grub_netbuff_push (nb, 6);
486 if (err)
487 goto fail;
489 grub_memcpy (nb->data, inf->hwaddress.mac, 6);
490 err = grub_netbuff_push (nb, sizeof (*ohdr));
491 if (err)
492 goto fail;
494 ohdr = (struct option_header *) nb->data;
495 ohdr->type = OPTION_SOURCE_LINK_LAYER_ADDRESS;
496 ohdr->len = 1;
497 err = grub_netbuff_push (nb, sizeof (*sol));
498 if (err)
499 goto fail;
501 sol = (struct neighbour_solicit *) nb->data;
502 sol->reserved = 0;
503 grub_memcpy (&sol->target, &proto_addr->ipv6, 16);
505 err = grub_netbuff_push (nb, sizeof (*icmphr));
506 if (err)
507 goto fail;
509 icmphr = (struct icmp_header *) nb->data;
510 icmphr->type = ICMP6_NEIGHBOUR_SOLICIT;
511 icmphr->code = 0;
512 icmphr->checksum = 0;
513 icmphr->checksum = grub_net_ip_transport_checksum (nb,
514 GRUB_NET_IP_ICMPV6,
515 &inf->address,
516 &multicast);
517 nbd = nb->data;
518 err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
519 GRUB_NET_IP_ICMPV6);
520 if (err)
521 goto fail;
523 for (i = 0; i < GRUB_NET_TRIES; i++)
525 if (grub_net_link_layer_resolve_check (inf, proto_addr))
526 break;
527 grub_net_poll_cards (GRUB_NET_INTERVAL + (i * GRUB_NET_INTERVAL_ADDITION),
529 if (grub_net_link_layer_resolve_check (inf, proto_addr))
530 break;
531 nb->data = nbd;
532 err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
533 GRUB_NET_IP_ICMPV6);
534 if (err)
535 break;
538 fail:
539 grub_netbuff_free (nb);
540 return err;
543 grub_err_t
544 grub_net_icmp6_send_router_solicit (struct grub_net_network_level_interface *inf)
546 struct grub_net_buff *nb;
547 grub_err_t err = GRUB_ERR_NONE;
548 grub_net_network_level_address_t multicast;
549 grub_net_link_level_address_t ll_multicast;
550 struct option_header *ohdr;
551 struct router_solicit *sol;
552 struct icmp_header *icmphr;
554 multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
555 multicast.ipv6[0] = grub_cpu_to_be64 (0xff02ULL << 48);
556 multicast.ipv6[1] = grub_cpu_to_be64 (0x02ULL);
558 err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
559 if (err)
560 return err;
562 nb = grub_netbuff_alloc (sizeof (struct router_solicit)
563 + sizeof (struct option_header)
565 + sizeof (struct icmp_header)
566 + GRUB_NET_OUR_IPV6_HEADER_SIZE
567 + GRUB_NET_MAX_LINK_HEADER_SIZE);
568 if (!nb)
569 return grub_errno;
570 err = grub_netbuff_reserve (nb,
571 sizeof (struct router_solicit)
572 + sizeof (struct option_header)
574 + sizeof (struct icmp_header)
575 + GRUB_NET_OUR_IPV6_HEADER_SIZE
576 + GRUB_NET_MAX_LINK_HEADER_SIZE);
577 if (err)
578 goto fail;
580 err = grub_netbuff_push (nb, 6);
581 if (err)
582 goto fail;
584 grub_memcpy (nb->data, inf->hwaddress.mac, 6);
586 err = grub_netbuff_push (nb, sizeof (*ohdr));
587 if (err)
588 goto fail;
590 ohdr = (struct option_header *) nb->data;
591 ohdr->type = OPTION_SOURCE_LINK_LAYER_ADDRESS;
592 ohdr->len = 1;
594 err = grub_netbuff_push (nb, sizeof (*sol));
595 if (err)
596 goto fail;
598 sol = (struct router_solicit *) nb->data;
599 sol->reserved = 0;
601 err = grub_netbuff_push (nb, sizeof (*icmphr));
602 if (err)
603 goto fail;
605 icmphr = (struct icmp_header *) nb->data;
606 icmphr->type = ICMP6_ROUTER_SOLICIT;
607 icmphr->code = 0;
608 icmphr->checksum = 0;
609 icmphr->checksum = grub_net_ip_transport_checksum (nb,
610 GRUB_NET_IP_ICMPV6,
611 &inf->address,
612 &multicast);
613 err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
614 GRUB_NET_IP_ICMPV6);
615 fail:
616 grub_netbuff_free (nb);
617 return err;