Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / net / icmp6.c
blob4fc343d7292ffedfa8cb3c49111dfb93b347aff9
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 } __attribute__ ((packed));
30 struct ping_header
32 grub_uint16_t id;
33 grub_uint16_t seq;
34 } __attribute__ ((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 } __attribute__ ((packed));
46 struct option_header
48 grub_uint8_t type;
49 grub_uint8_t len;
50 } __attribute__ ((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 prefered_lifetime;
59 grub_uint32_t reserved;
60 grub_uint64_t prefix[2];
61 } __attribute__ ((packed));
63 struct neighbour_solicit
65 grub_uint32_t reserved;
66 grub_uint64_t target[2];
67 } __attribute__ ((packed));
69 struct neighbour_advertise
71 grub_uint32_t flags;
72 grub_uint64_t target[2];
73 } __attribute__ ((packed));
75 enum
77 FLAG_SLAAC = 0x40
80 enum
82 ICMP6_ECHO = 128,
83 ICMP6_ECHO_REPLY = 129,
84 ICMP6_ROUTER_ADVERTISE = 134,
85 ICMP6_NEIGHBOUR_SOLICIT = 135,
86 ICMP6_NEIGHBOUR_ADVERTISE = 136,
89 enum
91 OPTION_SOURCE_LINK_LAYER_ADDRESS = 1,
92 OPTION_TARGET_LINK_LAYER_ADDRESS = 2,
93 OPTION_PREFIX = 3
96 enum
98 FLAG_SOLICITED = (1 << 30),
99 FLAG_OVERRIDE = (1 << 29)
102 grub_err_t
103 grub_net_recv_icmp6_packet (struct grub_net_buff *nb,
104 struct grub_net_card *card,
105 struct grub_net_network_level_interface *inf,
106 const grub_net_link_level_address_t *ll_src,
107 const grub_net_network_level_address_t *source,
108 const grub_net_network_level_address_t *dest,
109 grub_uint8_t ttl)
111 struct icmp_header *icmph;
112 grub_err_t err;
113 grub_uint16_t checksum;
115 icmph = (struct icmp_header *) nb->data;
117 if (nb->tail - nb->data < (grub_ssize_t) sizeof (*icmph))
119 grub_netbuff_free (nb);
120 return GRUB_ERR_NONE;
123 checksum = icmph->checksum;
124 icmph->checksum = 0;
125 if (checksum != grub_net_ip_transport_checksum (nb,
126 GRUB_NET_IP_ICMPV6,
127 source,
128 dest))
130 grub_dprintf ("net", "invalid ICMPv6 checksum: %04x instead of %04x\n",
131 checksum,
132 grub_net_ip_transport_checksum (nb,
133 GRUB_NET_IP_ICMPV6,
134 source,
135 dest));
136 icmph->checksum = checksum;
137 grub_netbuff_free (nb);
138 return GRUB_ERR_NONE;
140 icmph->checksum = checksum;
142 err = grub_netbuff_pull (nb, sizeof (*icmph));
143 if (err)
145 grub_netbuff_free (nb);
146 return err;
149 grub_dprintf ("net", "ICMPv6 message: %02x, %02x\n",
150 icmph->type, icmph->code);
151 switch (icmph->type)
153 case ICMP6_ECHO:
154 /* Don't accept multicast pings. */
155 if (!inf)
156 break;
158 struct grub_net_buff *nb_reply;
159 struct icmp_header *icmphr;
160 if (icmph->code)
161 break;
162 nb_reply = grub_netbuff_alloc (nb->tail - nb->data + 512);
163 if (!nb_reply)
165 grub_netbuff_free (nb);
166 return grub_errno;
168 err = grub_netbuff_reserve (nb_reply, nb->tail - nb->data + 512);
169 if (err)
170 goto ping_fail;
171 err = grub_netbuff_push (nb_reply, nb->tail - nb->data);
172 if (err)
173 goto ping_fail;
174 grub_memcpy (nb_reply->data, nb->data, nb->tail - nb->data);
175 err = grub_netbuff_push (nb_reply, sizeof (*icmphr));
176 if (err)
177 goto ping_fail;
178 icmphr = (struct icmp_header *) nb_reply->data;
179 icmphr->type = ICMP6_ECHO_REPLY;
180 icmphr->code = 0;
181 icmphr->checksum = 0;
182 icmphr->checksum = grub_net_ip_transport_checksum (nb_reply,
183 GRUB_NET_IP_ICMPV6,
184 &inf->address,
185 source);
186 err = grub_net_send_ip_packet (inf, source, ll_src, nb_reply,
187 GRUB_NET_IP_ICMPV6);
189 ping_fail:
190 grub_netbuff_free (nb);
191 grub_netbuff_free (nb_reply);
192 return err;
194 case ICMP6_NEIGHBOUR_SOLICIT:
196 struct neighbour_solicit *nbh;
197 struct grub_net_buff *nb_reply;
198 struct option_header *ohdr;
199 struct neighbour_advertise *adv;
200 struct icmp_header *icmphr;
201 grub_uint8_t *ptr;
203 if (icmph->code)
204 break;
205 if (ttl != 0xff)
206 break;
207 nbh = (struct neighbour_solicit *) nb->data;
208 err = grub_netbuff_pull (nb, sizeof (struct router_adv));
209 if (err)
211 grub_netbuff_free (nb);
212 return err;
214 for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
215 ptr += ohdr->len * 8)
217 ohdr = (struct option_header *) ptr;
218 if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail)
220 grub_netbuff_free (nb);
221 return GRUB_ERR_NONE;
223 if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS
224 && ohdr->len == 1)
226 grub_net_link_level_address_t ll_address;
227 ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
228 grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
229 grub_net_link_layer_add_address (card, source, &ll_address, 0);
232 FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
234 if (inf->card == card
235 && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6
236 && grub_memcmp (&inf->address.ipv6, &nbh->target, 16) == 0)
237 break;
239 if (!inf)
240 break;
242 nb_reply = grub_netbuff_alloc (sizeof (struct neighbour_advertise)
243 + sizeof (struct option_header)
245 + sizeof (struct icmp_header)
246 + GRUB_NET_OUR_IPV6_HEADER_SIZE
247 + GRUB_NET_MAX_LINK_HEADER_SIZE);
248 if (!nb_reply)
250 grub_netbuff_free (nb);
251 return grub_errno;
253 err = grub_netbuff_reserve (nb_reply,
254 sizeof (struct neighbour_advertise)
255 + sizeof (struct option_header)
257 + sizeof (struct icmp_header)
258 + GRUB_NET_OUR_IPV6_HEADER_SIZE
259 + GRUB_NET_MAX_LINK_HEADER_SIZE);
260 if (err)
261 goto ndp_fail;
263 err = grub_netbuff_push (nb_reply, 6);
264 if (err)
265 goto ndp_fail;
266 grub_memcpy (nb_reply->data, inf->hwaddress.mac, 6);
267 err = grub_netbuff_push (nb_reply, sizeof (*ohdr));
268 if (err)
269 goto ndp_fail;
270 ohdr = (struct option_header *) nb_reply->data;
271 ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS;
272 ohdr->len = 1;
273 err = grub_netbuff_push (nb_reply, sizeof (*adv));
274 if (err)
275 goto ndp_fail;
276 adv = (struct neighbour_advertise *) nb_reply->data;
277 adv->flags = grub_cpu_to_be32_compile_time (FLAG_SOLICITED
278 | FLAG_OVERRIDE);
279 grub_memcpy (&adv->target, &nbh->target, 16);
281 err = grub_netbuff_push (nb_reply, sizeof (*icmphr));
282 if (err)
283 goto ndp_fail;
284 icmphr = (struct icmp_header *) nb_reply->data;
285 icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE;
286 icmphr->code = 0;
287 icmphr->checksum = 0;
288 icmphr->checksum = grub_net_ip_transport_checksum (nb_reply,
289 GRUB_NET_IP_ICMPV6,
290 &inf->address,
291 source);
292 err = grub_net_send_ip_packet (inf, source, ll_src, nb_reply,
293 GRUB_NET_IP_ICMPV6);
295 ndp_fail:
296 grub_netbuff_free (nb);
297 grub_netbuff_free (nb_reply);
298 return err;
300 case ICMP6_NEIGHBOUR_ADVERTISE:
302 struct neighbour_advertise *nbh;
303 grub_uint8_t *ptr;
304 struct option_header *ohdr;
306 if (icmph->code)
307 break;
308 if (ttl != 0xff)
309 break;
310 nbh = (struct neighbour_advertise *) nb->data;
311 err = grub_netbuff_pull (nb, sizeof (*nbh));
312 if (err)
314 grub_netbuff_free (nb);
315 return err;
318 for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
319 ptr += ohdr->len * 8)
321 ohdr = (struct option_header *) ptr;
322 if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail)
324 grub_netbuff_free (nb);
325 return GRUB_ERR_NONE;
327 if (ohdr->type == OPTION_TARGET_LINK_LAYER_ADDRESS
328 && ohdr->len == 1)
330 grub_net_link_level_address_t ll_address;
331 ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
332 grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
333 grub_net_link_layer_add_address (card, source, &ll_address, 0);
336 break;
338 case ICMP6_ROUTER_ADVERTISE:
340 grub_uint8_t *ptr;
341 struct option_header *ohdr;
342 if (icmph->code)
343 break;
344 err = grub_netbuff_pull (nb, sizeof (struct router_adv));
345 if (err)
347 grub_netbuff_free (nb);
348 return err;
350 for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail;
351 ptr += ohdr->len * 8)
353 ohdr = (struct option_header *) ptr;
354 if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail)
356 grub_netbuff_free (nb);
357 return GRUB_ERR_NONE;
359 if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS
360 && ohdr->len == 1)
362 grub_net_link_level_address_t ll_address;
363 ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
364 grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac));
365 grub_net_link_layer_add_address (card, source, &ll_address, 0);
367 if (ohdr->type == OPTION_PREFIX && ohdr->len == 4)
369 struct prefix_option *opt = (struct prefix_option *) ptr;
370 struct grub_net_slaac_mac_list *slaac;
371 if (!(opt->flags & FLAG_SLAAC)
372 || (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80
373 || (grub_be_to_cpu32 (opt->prefered_lifetime)
374 > grub_be_to_cpu32 (opt->valid_lifetime))
375 || opt->prefixlen != 64)
377 grub_dprintf ("net", "discarded prefix: %d, %d, %d, %d\n",
378 !(opt->flags & FLAG_SLAAC),
379 (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80,
380 (grub_be_to_cpu32 (opt->prefered_lifetime)
381 > grub_be_to_cpu32 (opt->valid_lifetime)),
382 opt->prefixlen != 64);
383 continue;
385 for (slaac = card->slaac_list; slaac; slaac = slaac->next)
387 grub_net_network_level_address_t addr;
388 grub_net_network_level_netaddress_t netaddr;
390 if (slaac->address.type
391 != GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET)
392 continue;
393 addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
394 addr.ipv6[0] = opt->prefix[0];
395 addr.ipv6[1] = grub_net_ipv6_get_id (&slaac->address);
396 netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
397 netaddr.ipv6.base[0] = opt->prefix[0];
398 netaddr.ipv6.base[1] = 0;
399 netaddr.ipv6.masksize = 64;
401 FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
403 if (inf->card == card
404 && grub_net_addr_cmp (&inf->address, &addr) == 0)
405 break;
407 /* Update lease time if needed here once we have
408 lease times. */
409 if (inf)
410 continue;
412 grub_dprintf ("net", "creating slaac\n");
415 char name[grub_strlen (slaac->name)
416 + sizeof (":XXXXXXXXXXXXXXXXXXXX")];
417 grub_snprintf (name, sizeof (name), "%s:%d",
418 slaac->name, slaac->slaac_counter++);
419 inf = grub_net_add_addr (name,
420 card, &addr,
421 &slaac->address, 0);
422 grub_net_add_route (name, netaddr, inf);
427 if (ptr != nb->tail)
428 break;
432 grub_netbuff_free (nb);
433 return GRUB_ERR_NONE;
436 grub_err_t
437 grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf,
438 const grub_net_network_level_address_t *proto_addr)
440 struct grub_net_buff *nb;
441 grub_err_t err = GRUB_ERR_NONE;
442 int i;
443 struct option_header *ohdr;
444 struct neighbour_solicit *sol;
445 struct icmp_header *icmphr;
446 grub_net_network_level_address_t multicast;
447 grub_net_link_level_address_t ll_multicast;
448 grub_uint8_t *nbd;
449 multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
450 multicast.ipv6[0] = grub_be_to_cpu64_compile_time (0xff02ULL << 48);
451 multicast.ipv6[1] = (grub_be_to_cpu64_compile_time (0x01ff000000ULL)
452 | (proto_addr->ipv6[1]
453 & grub_be_to_cpu64_compile_time (0xffffff)));
455 err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
456 if (err)
457 return err;
459 nb = grub_netbuff_alloc (sizeof (struct neighbour_solicit)
460 + sizeof (struct option_header)
462 + sizeof (struct icmp_header)
463 + GRUB_NET_OUR_IPV6_HEADER_SIZE
464 + GRUB_NET_MAX_LINK_HEADER_SIZE);
465 if (!nb)
466 return grub_errno;
467 err = grub_netbuff_reserve (nb,
468 sizeof (struct neighbour_solicit)
469 + sizeof (struct option_header)
471 + sizeof (struct icmp_header)
472 + GRUB_NET_OUR_IPV6_HEADER_SIZE
473 + GRUB_NET_MAX_LINK_HEADER_SIZE);
474 err = grub_netbuff_push (nb, 6);
475 if (err)
476 goto fail;
478 grub_memcpy (nb->data, inf->hwaddress.mac, 6);
479 err = grub_netbuff_push (nb, sizeof (*ohdr));
480 if (err)
481 goto fail;
483 ohdr = (struct option_header *) nb->data;
484 ohdr->type = OPTION_SOURCE_LINK_LAYER_ADDRESS;
485 ohdr->len = 1;
486 err = grub_netbuff_push (nb, sizeof (*sol));
487 if (err)
488 goto fail;
490 sol = (struct neighbour_solicit *) nb->data;
491 sol->reserved = 0;
492 grub_memcpy (&sol->target, &proto_addr->ipv6, 16);
494 err = grub_netbuff_push (nb, sizeof (*icmphr));
495 if (err)
496 goto fail;
498 icmphr = (struct icmp_header *) nb->data;
499 icmphr->type = ICMP6_NEIGHBOUR_SOLICIT;
500 icmphr->code = 0;
501 icmphr->checksum = 0;
502 icmphr->checksum = grub_net_ip_transport_checksum (nb,
503 GRUB_NET_IP_ICMPV6,
504 &inf->address,
505 &multicast);
506 nbd = nb->data;
507 err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
508 GRUB_NET_IP_ICMPV6);
509 if (err)
510 goto fail;
512 for (i = 0; i < GRUB_NET_TRIES; i++)
514 if (grub_net_link_layer_resolve_check (inf, proto_addr))
515 break;
516 grub_net_poll_cards (GRUB_NET_INTERVAL, 0);
517 if (grub_net_link_layer_resolve_check (inf, proto_addr))
518 break;
519 nb->data = nbd;
520 err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
521 GRUB_NET_IP_ICMPV6);
522 if (err)
523 break;
526 fail:
527 grub_netbuff_free (nb);
528 return err;