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/>.
20 #include <grub/net/ip.h>
21 #include <grub/net/netbuff.h>
27 grub_uint16_t checksum
;
40 grub_uint16_t router_lifetime
;
41 grub_uint32_t reachable_time
;
42 grub_uint32_t retrans_timer
;
43 grub_uint8_t options
[0];
54 struct option_header header
;
55 grub_uint8_t prefixlen
;
57 grub_uint32_t valid_lifetime
;
58 grub_uint32_t preferred_lifetime
;
59 grub_uint32_t reserved
;
60 grub_uint64_t prefix
[2];
63 struct neighbour_solicit
65 grub_uint32_t reserved
;
66 grub_uint64_t target
[2];
69 struct neighbour_advertise
72 grub_uint64_t target
[2];
77 grub_uint32_t reserved
;
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,
97 OPTION_SOURCE_LINK_LAYER_ADDRESS
= 1,
98 OPTION_TARGET_LINK_LAYER_ADDRESS
= 2,
104 FLAG_SOLICITED
= (1 << 30),
105 FLAG_OVERRIDE
= (1 << 29)
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
,
117 struct icmp_header
*icmph
;
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
;
131 if (checksum
!= grub_net_ip_transport_checksum (nb
,
136 grub_dprintf ("net", "invalid ICMPv6 checksum: %04x instead of %04x\n",
138 grub_net_ip_transport_checksum (nb
,
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
));
151 grub_netbuff_free (nb
);
155 grub_dprintf ("net", "ICMPv6 message: %02x, %02x\n",
156 icmph
->type
, icmph
->code
);
160 /* Don't accept multicast pings. */
164 struct grub_net_buff
*nb_reply
;
165 struct icmp_header
*icmphr
;
168 nb_reply
= grub_netbuff_alloc (nb
->tail
- nb
->data
+ 512);
171 grub_netbuff_free (nb
);
174 err
= grub_netbuff_reserve (nb_reply
, nb
->tail
- nb
->data
+ 512);
177 err
= grub_netbuff_push (nb_reply
, nb
->tail
- nb
->data
);
180 grub_memcpy (nb_reply
->data
, nb
->data
, nb
->tail
- nb
->data
);
181 err
= grub_netbuff_push (nb_reply
, sizeof (*icmphr
));
184 icmphr
= (struct icmp_header
*) nb_reply
->data
;
185 icmphr
->type
= ICMP6_ECHO_REPLY
;
187 icmphr
->checksum
= 0;
188 icmphr
->checksum
= grub_net_ip_transport_checksum (nb_reply
,
192 err
= grub_net_send_ip_packet (inf
, source
, ll_src
, nb_reply
,
196 grub_netbuff_free (nb
);
197 grub_netbuff_free (nb_reply
);
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
;
213 nbh
= (struct neighbour_solicit
*) nb
->data
;
214 err
= grub_netbuff_pull (nb
, sizeof (*nbh
));
217 grub_netbuff_free (nb
);
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
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)
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
);
256 grub_netbuff_free (nb
);
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
);
269 err
= grub_netbuff_push (nb_reply
, 6);
272 grub_memcpy (nb_reply
->data
, inf
->hwaddress
.mac
, 6);
273 err
= grub_netbuff_push (nb_reply
, sizeof (*ohdr
));
276 ohdr
= (struct option_header
*) nb_reply
->data
;
277 ohdr
->type
= OPTION_TARGET_LINK_LAYER_ADDRESS
;
279 err
= grub_netbuff_push (nb_reply
, sizeof (*adv
));
282 adv
= (struct neighbour_advertise
*) nb_reply
->data
;
283 adv
->flags
= grub_cpu_to_be32_compile_time (FLAG_SOLICITED
285 grub_memcpy (&adv
->target
, &nbh
->target
, 16);
287 err
= grub_netbuff_push (nb_reply
, sizeof (*icmphr
));
290 icmphr
= (struct icmp_header
*) nb_reply
->data
;
291 icmphr
->type
= ICMP6_NEIGHBOUR_ADVERTISE
;
293 icmphr
->checksum
= 0;
294 icmphr
->checksum
= grub_net_ip_transport_checksum (nb_reply
,
298 err
= grub_net_send_ip_packet (inf
, source
, ll_src
, nb_reply
,
302 grub_netbuff_free (nb
);
303 grub_netbuff_free (nb_reply
);
306 case ICMP6_NEIGHBOUR_ADVERTISE
:
308 struct neighbour_advertise
*nbh
;
310 struct option_header
*ohdr
;
316 nbh
= (struct neighbour_advertise
*) nb
->data
;
317 err
= grub_netbuff_pull (nb
, sizeof (*nbh
));
320 grub_netbuff_free (nb
);
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
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);
344 case ICMP6_ROUTER_ADVERTISE
:
347 struct option_header
*ohdr
;
350 err
= grub_netbuff_pull (nb
, sizeof (struct router_adv
));
353 grub_netbuff_free (nb
);
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
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);
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
)
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)
413 /* Update lease time if needed here once we have
418 grub_dprintf ("net", "creating slaac\n");
422 name
= grub_xasprintf ("%s:%d",
423 slaac
->name
, slaac
->slaac_counter
++);
426 grub_errno
= GRUB_ERR_NONE
;
429 inf
= grub_net_add_addr (name
,
432 grub_net_add_route (name
, netaddr
, inf
);
443 grub_netbuff_free (nb
);
444 return GRUB_ERR_NONE
;
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
;
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
;
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
);
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
);
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);
489 grub_memcpy (nb
->data
, inf
->hwaddress
.mac
, 6);
490 err
= grub_netbuff_push (nb
, sizeof (*ohdr
));
494 ohdr
= (struct option_header
*) nb
->data
;
495 ohdr
->type
= OPTION_SOURCE_LINK_LAYER_ADDRESS
;
497 err
= grub_netbuff_push (nb
, sizeof (*sol
));
501 sol
= (struct neighbour_solicit
*) nb
->data
;
503 grub_memcpy (&sol
->target
, &proto_addr
->ipv6
, 16);
505 err
= grub_netbuff_push (nb
, sizeof (*icmphr
));
509 icmphr
= (struct icmp_header
*) nb
->data
;
510 icmphr
->type
= ICMP6_NEIGHBOUR_SOLICIT
;
512 icmphr
->checksum
= 0;
513 icmphr
->checksum
= grub_net_ip_transport_checksum (nb
,
518 err
= grub_net_send_ip_packet (inf
, &multicast
, &ll_multicast
, nb
,
523 for (i
= 0; i
< GRUB_NET_TRIES
; i
++)
525 if (grub_net_link_layer_resolve_check (inf
, proto_addr
))
527 grub_net_poll_cards (GRUB_NET_INTERVAL
+ (i
* GRUB_NET_INTERVAL_ADDITION
),
529 if (grub_net_link_layer_resolve_check (inf
, proto_addr
))
532 err
= grub_net_send_ip_packet (inf
, &multicast
, &ll_multicast
, nb
,
539 grub_netbuff_free (nb
);
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
);
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
);
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
);
580 err
= grub_netbuff_push (nb
, 6);
584 grub_memcpy (nb
->data
, inf
->hwaddress
.mac
, 6);
586 err
= grub_netbuff_push (nb
, sizeof (*ohdr
));
590 ohdr
= (struct option_header
*) nb
->data
;
591 ohdr
->type
= OPTION_SOURCE_LINK_LAYER_ADDRESS
;
594 err
= grub_netbuff_push (nb
, sizeof (*sol
));
598 sol
= (struct router_solicit
*) nb
->data
;
601 err
= grub_netbuff_push (nb
, sizeof (*icmphr
));
605 icmphr
= (struct icmp_header
*) nb
->data
;
606 icmphr
->type
= ICMP6_ROUTER_SOLICIT
;
608 icmphr
->checksum
= 0;
609 icmphr
->checksum
= grub_net_ip_transport_checksum (nb
,
613 err
= grub_net_send_ip_packet (inf
, &multicast
, &ll_multicast
, nb
,
616 grub_netbuff_free (nb
);