diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index b1b719dbe..b4c10531b 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -46,41 +46,10 @@ struct arphdr { } __attribute__ ((packed)); -struct arp_entry { - int avail; - grub_net_network_level_address_t nl_address; - grub_net_link_level_address_t ll_address; -}; - -static struct arp_entry arp_table[10]; -static grub_int8_t new_table_entry = -1; - -static void -arp_init_table (void) -{ - grub_memset (arp_table, 0, sizeof (arp_table)); - new_table_entry = 0; -} - -static struct arp_entry * -arp_find_entry (const grub_net_network_level_address_t *proto) -{ - unsigned i; - for (i = 0; i < ARRAY_SIZE (arp_table); i++) - { - if (arp_table[i].avail == 1 && - grub_net_addr_cmp (&arp_table[i].nl_address, proto) == 0) - return &(arp_table[i]); - } - return NULL; -} - grub_err_t -grub_net_arp_resolve (struct grub_net_network_level_interface *inf, - const grub_net_network_level_address_t *proto_addr, - grub_net_link_level_address_t *hw_addr) +grub_net_arp_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr) { - struct arp_entry *entry; struct grub_net_buff nb; struct arphdr *arp_header; grub_net_link_level_address_t target_hw_addr; @@ -90,32 +59,11 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, grub_size_t addrlen; grub_uint16_t etherpro; - if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 - && proto_addr->ipv4 == 0xffffffff) - { - hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memset (hw_addr->mac, -1, 6); - return GRUB_ERR_NONE; - } - - /* Check cache table. */ - entry = arp_find_entry (proto_addr); - if (entry) - { - *hw_addr = entry->ll_address; - return GRUB_ERR_NONE; - } - if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) { addrlen = 4; etherpro = GRUB_NET_ETHERTYPE_IP; } - else if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) - { - addrlen = 16; - etherpro = GRUB_NET_ETHERTYPE_IP6; - } else return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "unsupported address family"); @@ -141,45 +89,39 @@ grub_net_arp_resolve (struct grub_net_network_level_interface *inf, aux += 6; /* Sender protocol address */ - if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) - grub_memcpy (aux, &inf->address.ipv4, 4); - else - grub_memcpy (aux, &inf->address.ipv6, 16); + grub_memcpy (aux, &inf->address.ipv4, 4); aux += addrlen; /* Target hardware address */ for (i = 0; i < 6; i++) aux[i] = 0x00; aux += 6; /* Target protocol address */ - if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) - grub_memcpy (aux, &proto_addr->ipv4, 4); - else - grub_memcpy (aux, &proto_addr->ipv6, 16); + grub_memcpy (aux, &proto_addr->ipv4, 4); grub_memset (&target_hw_addr.mac, 0xff, 6); send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); for (i = 0; i < GRUB_NET_TRIES; i++) { - entry = arp_find_entry (proto_addr); - if (entry) - { - grub_memcpy (hw_addr, &entry->ll_address, sizeof (*hw_addr)); - return GRUB_ERR_NONE; - } + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + return GRUB_ERR_NONE; grub_net_poll_cards (GRUB_NET_INTERVAL); + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + return GRUB_ERR_NONE; + send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP); } - return grub_error (GRUB_ERR_TIMEOUT, "timeout: could not resolve hardware address"); + return GRUB_ERR_NONE; } grub_err_t -grub_net_arp_receive (struct grub_net_buff *nb) +grub_net_arp_receive (struct grub_net_buff *nb, + struct grub_net_card *card) { struct arphdr *arp_header = (struct arphdr *) nb->data; - struct arp_entry *entry; grub_uint8_t *sender_hardware_address; grub_uint8_t *target_hardware_address; grub_net_network_level_address_t sender_addr, target_addr; + grub_net_link_level_address_t sender_hw_addr; struct grub_net_network_level_interface *inf; grub_uint8_t *sender_protocol_address, *target_protocol_address; @@ -196,35 +138,13 @@ grub_net_arp_receive (struct grub_net_buff *nb) grub_memcpy (&sender_addr.ipv4, sender_protocol_address, 4); grub_memcpy (&target_addr.ipv4, target_protocol_address, 4); } - else if (grub_be_to_cpu16 (arp_header->pro) == GRUB_NET_ETHERTYPE_IP6 - && arp_header->pln == 16) - { - sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; - target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - grub_memcpy (&sender_addr.ipv6, sender_protocol_address, 16); - grub_memcpy (&target_addr.ipv6, target_protocol_address, 16); - } else return GRUB_ERR_NONE; - - /* Check if the sender is in the cache table. */ - entry = arp_find_entry (&sender_addr); - /* Update sender hardware address. */ - if (entry) - grub_memcpy (entry->ll_address.mac, sender_hardware_address, 6); - else - { - /* Add sender to cache table. */ - if (new_table_entry == -1) - arp_init_table (); - entry = &(arp_table[new_table_entry]); - entry->avail = 1; - entry->nl_address = sender_addr; - grub_memcpy (entry->ll_address.mac, sender_hardware_address, 6); - new_table_entry++; - if (new_table_entry == ARRAY_SIZE (arp_table)) - new_table_entry = 0; - } + + sender_hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (sender_hw_addr.mac, &sender_hardware_address, + sizeof (sender_hw_addr.mac)); + grub_net_link_layer_add_address (card, &sender_addr, &sender_hw_addr, 1); FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index 8d7f66830..6b5db9aaa 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -80,8 +80,8 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, } grub_err_t -grub_net_recv_ethernet_packet (struct grub_net_buff * nb, - struct grub_net_card * card) +grub_net_recv_ethernet_packet (struct grub_net_buff *nb, + struct grub_net_card *card) { struct etherhdr *eth; struct llchdr *llch; @@ -118,7 +118,7 @@ grub_net_recv_ethernet_packet (struct grub_net_buff * nb, { /* ARP packet. */ case GRUB_NET_ETHERTYPE_ARP: - grub_net_arp_receive (nb); + grub_net_arp_receive (nb, card); grub_netbuff_free (nb); return GRUB_ERR_NONE; /* IP packet. */ diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c index ac12c5733..1f9278910 100644 --- a/grub-core/net/icmp6.c +++ b/grub-core/net/icmp6.c @@ -43,10 +43,15 @@ struct router_adv grub_uint8_t options[0]; } __attribute__ ((packed)); -struct prefix_option +struct option_header { grub_uint8_t type; grub_uint8_t len; +} __attribute__ ((packed)); + +struct prefix_option +{ + struct option_header header; grub_uint8_t prefixlen; grub_uint8_t flags; grub_uint32_t valid_lifetime; @@ -55,6 +60,18 @@ struct prefix_option grub_uint64_t prefix[2]; } __attribute__ ((packed)); +struct neighbour_solicit +{ + grub_uint32_t reserved; + grub_uint64_t target[2]; +} __attribute__ ((packed)); + +struct neighbour_advertise +{ + grub_uint32_t flags; + grub_uint64_t target[2]; +} __attribute__ ((packed)); + enum { FLAG_SLAAC = 0x40 @@ -64,7 +81,22 @@ enum { ICMP6_ECHO = 128, ICMP6_ECHO_REPLY = 129, - ICMP6_ROUTER_ADVERTISE = 134 + ICMP6_ROUTER_ADVERTISE = 134, + ICMP6_NEIGHBOUR_SOLICIT = 135, + ICMP6_NEIGHBOUR_ADVERTISE = 136, + }; + +enum + { + OPTION_SOURCE_LINK_LAYER_ADDRESS = 1, + OPTION_TARGET_LINK_LAYER_ADDRESS = 2, + OPTION_PREFIX = 3 + }; + +enum + { + FLAG_SOLICITED = (1 << 30), + FLAG_OVERRIDE = (1 << 29) }; grub_err_t @@ -72,7 +104,8 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, struct grub_net_card *card, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *source, - const grub_net_network_level_address_t *dest) + const grub_net_network_level_address_t *dest, + grub_uint8_t ttl) { struct icmp_header *icmph; grub_err_t err; @@ -107,7 +140,10 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, err = grub_netbuff_pull (nb, sizeof (*icmph)); if (err) - return err; + { + grub_netbuff_free (nb); + return err; + } grub_dprintf ("net", "ICMPv6 message: %02x, %02x\n", icmph->type, icmph->code); @@ -154,24 +190,180 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, grub_netbuff_free (nb_reply); return err; } - case ICMP6_ROUTER_ADVERTISE: + case ICMP6_NEIGHBOUR_SOLICIT: { + struct neighbour_solicit *nbh; + struct grub_net_buff *nb_reply; + struct option_header *ohdr; + struct neighbour_advertise *adv; + struct icmp_header *icmphr; grub_uint8_t *ptr; + if (icmph->code) break; + if (ttl != 0xff) + break; + nbh = (struct neighbour_solicit *) nb->data; err = grub_netbuff_pull (nb, sizeof (struct router_adv)); if (err) - return err; - for (ptr = (grub_uint8_t *) nb->data + sizeof (struct router_adv); - ptr < nb->tail; ) { - grub_dprintf ("net", "option %02x, %02x\n", ptr[0], ptr[1]); - if (ptr + 2 >= nb->tail || ptr[1] == 0) + grub_netbuff_free (nb); + return err; + } + for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail; + ptr += ohdr->len * 8) + { + ohdr = (struct option_header *) ptr; + if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail) { grub_netbuff_free (nb); return GRUB_ERR_NONE; } - if (ptr[0] == 3 && ptr[1] == 4) + if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS + && ohdr->len == 1) + { + grub_net_link_level_address_t ll_address; + ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + grub_net_link_layer_add_address (card, source, &ll_address, 0); + } + } + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) + { + if (inf->card == card + && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && grub_memcmp (&inf->address.ipv6, &nbh->target, 16) == 0) + break; + } + if (!inf) + break; + + nb_reply = grub_netbuff_alloc (sizeof (struct neighbour_advertise) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb_reply) + { + grub_netbuff_free (nb); + return grub_errno; + } + err = grub_netbuff_reserve (nb_reply, + sizeof (struct neighbour_advertise) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (err) + goto ndp_fail; + + err = grub_netbuff_push (nb_reply, 6); + if (err) + goto ndp_fail; + grub_memcpy (nb_reply->data, inf->hwaddress.mac, 6); + err = grub_netbuff_push (nb_reply, sizeof (*ohdr)); + if (err) + goto ndp_fail; + ohdr = (struct option_header *) nb_reply->data; + ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS; + ohdr->len = 1; + err = grub_netbuff_push (nb_reply, sizeof (*adv)); + if (err) + goto ndp_fail; + adv = (struct neighbour_advertise *) nb_reply->data; + adv->flags = grub_cpu_to_be32_compile_time (FLAG_SOLICITED + | FLAG_OVERRIDE); + grub_memcpy (&adv->target, &nbh->target, 16); + + err = grub_netbuff_push (nb_reply, sizeof (*icmphr)); + if (err) + goto ndp_fail; + icmphr = (struct icmp_header *) nb_reply->data; + icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = grub_net_ip_transport_checksum (nb_reply, + GRUB_NET_IP_ICMPV6, + &inf->address, + source); + err = grub_net_send_ip_packet (inf, source, nb_reply, + GRUB_NET_IP_ICMPV6); + + ndp_fail: + grub_netbuff_free (nb); + grub_netbuff_free (nb_reply); + return err; + } + case ICMP6_NEIGHBOUR_ADVERTISE: + { + struct neighbour_advertise *nbh; + grub_uint8_t *ptr; + struct option_header *ohdr; + + if (icmph->code) + break; + if (ttl != 0xff) + break; + nbh = (struct neighbour_advertise *) nb->data; + err = grub_netbuff_pull (nb, sizeof (*nbh)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail; + ptr += ohdr->len * 8) + { + ohdr = (struct option_header *) ptr; + if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (ohdr->type == OPTION_TARGET_LINK_LAYER_ADDRESS + && ohdr->len == 1) + { + grub_net_link_level_address_t ll_address; + ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + grub_net_link_layer_add_address (card, source, &ll_address, 0); + } + } + break; + } + case ICMP6_ROUTER_ADVERTISE: + { + grub_uint8_t *ptr; + struct option_header *ohdr; + if (icmph->code) + break; + err = grub_netbuff_pull (nb, sizeof (struct router_adv)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + for (ptr = (grub_uint8_t *) nb->data; ptr < nb->tail; + ptr += ohdr->len * 8) + { + ohdr = (struct option_header *) ptr; + if (ohdr->len == 0 || ptr + 8 * ohdr->len > nb->tail) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (ohdr->type == OPTION_SOURCE_LINK_LAYER_ADDRESS + && ohdr->len == 1) + { + grub_net_link_level_address_t ll_address; + ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + grub_net_link_layer_add_address (card, source, &ll_address, 0); + } + if (ohdr->type == OPTION_PREFIX && ohdr->len == 4) { struct prefix_option *opt = (struct prefix_option *) ptr; struct grub_net_slaac_mac_list *slaac; @@ -181,7 +373,6 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, > grub_be_to_cpu32 (opt->valid_lifetime)) || opt->prefixlen != 64) { - ptr += ptr[1] * 8; grub_dprintf ("net", "discarded prefix: %d, %d, %d, %d\n", !(opt->flags & FLAG_SLAAC), (grub_be_to_cpu64 (opt->prefix[0]) >> 48) == 0xfe80, @@ -223,7 +414,6 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, } } } - ptr += ptr[1] * 8; } if (ptr != nb->tail) break; @@ -233,3 +423,91 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, grub_netbuff_free (nb); return GRUB_ERR_NONE; } + +grub_err_t +grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr) +{ + struct grub_net_buff *nb; + grub_err_t err = GRUB_ERR_NONE; + int i; + struct option_header *ohdr; + struct neighbour_solicit *sol; + struct icmp_header *icmphr; + grub_net_network_level_address_t multicast; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_be_to_cpu64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = (grub_be_to_cpu64_compile_time (0x01ff000000ULL) + | (proto_addr->ipv6[1] + & grub_be_to_cpu64_compile_time (0xffffff))); + + nb = grub_netbuff_alloc (sizeof (struct neighbour_solicit) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + if (!nb) + return grub_errno; + err = grub_netbuff_reserve (nb, + sizeof (struct neighbour_solicit) + + sizeof (struct option_header) + + 6 + + sizeof (struct icmp_header) + + GRUB_NET_OUR_IPV6_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE); + err = grub_netbuff_push (nb, 6); + if (err) + goto fail; + + grub_memcpy (nb->data, inf->hwaddress.mac, 6); + err = grub_netbuff_push (nb, sizeof (*ohdr)); + if (err) + goto fail; + + ohdr = (struct option_header *) nb->data; + ohdr->type = OPTION_TARGET_LINK_LAYER_ADDRESS; + ohdr->len = 1; + err = grub_netbuff_push (nb, sizeof (*sol)); + if (err) + goto fail; + + sol = (struct neighbour_solicit *) nb->data; + sol->reserved = 0; + grub_memcpy (&sol->target, &proto_addr->ipv6, 16); + + err = grub_netbuff_push (nb, sizeof (*icmphr)); + if (err) + goto fail; + + icmphr = (struct icmp_header *) nb->data; + icmphr->type = ICMP6_NEIGHBOUR_ADVERTISE; + icmphr->code = 0; + icmphr->checksum = 0; + icmphr->checksum = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_ICMPV6, + &inf->address, + &multicast); + err = grub_net_send_ip_packet (inf, &multicast, nb, + GRUB_NET_IP_ICMPV6); + if (err) + goto fail; + + for (i = 0; i < GRUB_NET_TRIES; i++) + { + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + break; + grub_net_poll_cards (GRUB_NET_INTERVAL); + if (grub_net_link_layer_resolve_check (inf, proto_addr)) + break; + err = grub_net_send_ip_packet (inf, &multicast, nb, + GRUB_NET_IP_ICMPV6); + if (err) + break; + } + + fail: + grub_netbuff_free (nb); + return err; +} diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 18593da49..27b8221fb 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -87,6 +87,7 @@ struct reassemble grub_uint8_t *asm_buffer; grub_size_t total_len; grub_size_t cur_ptr; + grub_uint8_t ttl; }; struct reassemble *reassembles; @@ -192,7 +193,7 @@ grub_net_send_ip4_packet (struct grub_net_network_level_interface * inf, COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV4_HEADER_SIZE == sizeof (*iph)); /* Determine link layer target address via ARP. */ - err = grub_net_arp_resolve (inf, target, &ll_target_addr); + err = grub_net_link_layer_resolve (inf, target, &ll_target_addr); if (err) return err; @@ -225,10 +226,12 @@ handle_dgram (struct grub_net_buff *nb, const grub_net_link_level_address_t *hwaddress, grub_net_ip_protocol_t proto, const grub_net_network_level_address_t *source, - const grub_net_network_level_address_t *dest) + const grub_net_network_level_address_t *dest, + grub_uint8_t ttl) { struct grub_net_network_level_interface *inf = NULL; grub_err_t err; + int multicast = 0; /* DHCP needs special treatment since we don't know IP yet. */ { @@ -280,17 +283,39 @@ handle_dgram (struct grub_net_buff *nb, && grub_net_addr_cmp (&inf->address, dest) == 0 && grub_net_hwaddr_cmp (&inf->hwaddress, hwaddress) == 0) break; + /* Solicited node multicast. */ + if (inf->card == card + && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && dest->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && dest->ipv6[0] == grub_be_to_cpu64_compile_time (0xff02ULL << 48) + && dest->ipv6[1] == (grub_be_to_cpu64_compile_time (0x01ff000000ULL) + | (inf->address.ipv6[1] + & grub_be_to_cpu64_compile_time (0xffffff))) + && hwaddress->type == GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET + && hwaddress->mac[0] == 0x33 && hwaddress->mac[1] == 0x33 + && hwaddress->mac[2] == 0xff + && hwaddress->mac[3] == ((grub_be_to_cpu64 (inf->address.ipv6[1]) + >> 16) & 0xff) + && hwaddress->mac[4] == ((grub_be_to_cpu64 (inf->address.ipv6[1]) + >> 8) & 0xff) + && hwaddress->mac[5] == ((grub_be_to_cpu64 (inf->address.ipv6[1]) + >> 0) & 0xff)) + { + multicast = 1; + break; + } } if (!inf && !(dest->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 - && dest->ipv6[0] == grub_be_to_cpu64 (0xff02ULL << 48) - && dest->ipv6[1] == grub_be_to_cpu64 (1))) + && dest->ipv6[0] == grub_be_to_cpu64_compile_time (0xff02ULL + << 48) + && dest->ipv6[1] == grub_be_to_cpu64_compile_time (1))) { - grub_dprintf ("net", "undirected dgram discarded: %x, %lx, %lx\n", - dest->type, dest->ipv6[0], dest->ipv6[1]); grub_netbuff_free (nb); return GRUB_ERR_NONE; } + if (multicast) + inf = NULL; switch (proto) { @@ -301,7 +326,7 @@ handle_dgram (struct grub_net_buff *nb, case GRUB_NET_IP_ICMP: return grub_net_recv_icmp_packet (nb, inf, source); case GRUB_NET_IP_ICMPV6: - return grub_net_recv_icmp6_packet (nb, card, inf, source, dest); + return grub_net_recv_icmp6_packet (nb, card, inf, source, dest, ttl); default: grub_netbuff_free (nb); break; @@ -414,7 +439,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, dest.ipv4 = iph->dest; return handle_dgram (nb, card, hwaddress, iph->protocol, - &source, &dest); + &source, &dest, iph->ttl); } for (prev = &reassembles, rsm = *prev; rsm; prev = &rsm->next, rsm = *prev) @@ -442,8 +467,10 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, rsm->asm_buffer = 0; rsm->total_len = 0; rsm->cur_ptr = 0; + rsm->ttl = 0xff; } - + if (rsm->ttl > iph->ttl) + rsm->ttl = iph->ttl; rsm->last_time = grub_get_time_ms (); free_old_fragments (); @@ -479,6 +506,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, grub_uint32_t dst; grub_net_network_level_address_t source; grub_net_network_level_address_t dest; + grub_uint8_t ttl; nb_top_p = grub_priority_queue_top (rsm->pq); if (!nb_top_p) @@ -521,6 +549,7 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, proto = rsm->proto; src = rsm->source; dst = rsm->dest; + ttl = rsm->ttl; rsm->asm_buffer = 0; res_len = rsm->total_len; @@ -541,7 +570,8 @@ grub_net_recv_ip4_packets (struct grub_net_buff * nb, dest.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; dest.ipv4 = dst; - return handle_dgram (ret, card, hwaddress, proto, &source, &dest); + return handle_dgram (ret, card, hwaddress, proto, &source, &dest, + ttl); } return GRUB_ERR_NONE; @@ -560,7 +590,7 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, COMPILE_TIME_ASSERT (GRUB_NET_OUR_IPV6_HEADER_SIZE == sizeof (*iph)); /* Determine link layer target address via ARP. */ - err = grub_net_arp_resolve (inf, target, &ll_target_addr); + err = grub_net_link_layer_resolve (inf, target, &ll_target_addr); if (err) return err; @@ -571,14 +601,14 @@ grub_net_send_ip6_packet (struct grub_net_network_level_interface * inf, iph = (struct ip6hdr *) nb->data; iph->version_class_flow = grub_cpu_to_be32 ((6 << 28)); - iph->len = grub_cpu_to_be16 (nb->tail - nb->data) - sizeof (*iph); + iph->len = grub_cpu_to_be16 (nb->tail - nb->data - sizeof (*iph)); iph->protocol = proto; iph->ttl = 0xff; grub_memcpy (&iph->src, inf->address.ipv6, sizeof (iph->src)); grub_memcpy (&iph->dest, target->ipv6, sizeof (iph->dest)); return send_ethernet_packet (inf, nb, ll_target_addr, - GRUB_NET_ETHERTYPE_IP); + GRUB_NET_ETHERTYPE_IP6); } grub_err_t @@ -608,14 +638,6 @@ grub_net_recv_ip6_packets (struct grub_net_buff * nb, grub_net_network_level_address_t source; grub_net_network_level_address_t dest; - if ((grub_be_to_cpu32 (iph->version_class_flow) >> 28) != 6) - { - grub_dprintf ("net", "Bad IP version: %d\n", - (grub_be_to_cpu32 (iph->version_class_flow) >> 28)); - grub_netbuff_free (nb); - return GRUB_ERR_NONE; - } - if (nb->tail - nb->data < (grub_ssize_t) sizeof (*iph)) { grub_dprintf ("net", "IP packet too short: %" PRIdGRUB_SSIZE "\n", @@ -660,7 +682,7 @@ grub_net_recv_ip6_packets (struct grub_net_buff * nb, grub_memcpy (dest.ipv6, &iph->dest, sizeof (dest.ipv6)); return handle_dgram (nb, card, hwaddress, iph->protocol, - &source, &dest); + &source, &dest, iph->ttl); } grub_err_t diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 1c6d47757..1134223b2 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,144 @@ struct grub_net_card *grub_net_cards = NULL; struct grub_net_network_level_protocol *grub_net_network_level_protocols = NULL; static struct grub_fs grub_net_fs; +struct grub_net_link_layer_entry { + int avail; + grub_net_network_level_address_t nl_address; + grub_net_link_level_address_t ll_address; +}; + +#define LINK_LAYER_CACHE_SIZE 256 + +static struct grub_net_link_layer_entry * +link_layer_find_entry (const grub_net_network_level_address_t *proto, + const struct grub_net_card *card) +{ + unsigned i; + if (!card->link_layer_table) + return NULL; + for (i = 0; i < LINK_LAYER_CACHE_SIZE; i++) + { + if (card->link_layer_table[i].avail == 1 + && grub_net_addr_cmp (&card->link_layer_table[i].nl_address, + proto) == 0) + return &card->link_layer_table[i]; + } + return NULL; +} + +void +grub_net_link_layer_add_address (struct grub_net_card *card, + const grub_net_network_level_address_t *nl, + const grub_net_link_level_address_t *ll, + int override) +{ + struct grub_net_link_layer_entry *entry; + + /* Check if the sender is in the cache table. */ + entry = link_layer_find_entry (nl, card); + /* Update sender hardware address. */ + if (entry && override) + grub_memcpy (&entry->ll_address, ll, sizeof (entry->ll_address)); + if (entry) + return; + + /* Add sender to cache table. */ + if (card->link_layer_table == NULL) + card->link_layer_table = grub_zalloc (LINK_LAYER_CACHE_SIZE + * sizeof (card->link_layer_table[0])); + entry = &(card->link_layer_table[card->new_ll_entry]); + entry->avail = 1; + grub_memcpy (&entry->ll_address, ll, sizeof (entry->ll_address)); + grub_memcpy (&entry->nl_address, nl, sizeof (entry->nl_address)); + card->new_ll_entry++; + if (card->new_ll_entry == LINK_LAYER_CACHE_SIZE) + card->new_ll_entry = 0; +} + +int +grub_net_link_layer_resolve_check (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr) +{ + struct grub_net_link_layer_entry *entry; + + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && proto_addr->ipv4 == 0xffffffff) + return 1; + entry = link_layer_find_entry (proto_addr, inf->card); + if (entry) + return 1; + return 0; +} + +grub_err_t +grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr, + grub_net_link_level_address_t *hw_addr) +{ + struct grub_net_link_layer_entry *entry; + grub_err_t err; + + if ((proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4 + && proto_addr->ipv4 == 0xffffffff) + || proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV + || (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && proto_addr->ipv6[0] == grub_be_to_cpu64_compile_time (0xff02ULL + << 48) + && proto_addr->ipv6[1] == (grub_be_to_cpu64_compile_time (1)))) + { + hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + grub_memset (hw_addr->mac, -1, 6); + return GRUB_ERR_NONE; + } + + if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6 + && ((grub_be_to_cpu64 (proto_addr->ipv6[0]) >> 56) == 0xff)) + { + hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + hw_addr->mac[0] = 0x33; + hw_addr->mac[1] = 0x33; + hw_addr->mac[2] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 24) & 0xff); + hw_addr->mac[3] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 16) & 0xff); + hw_addr->mac[4] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 8) & 0xff); + hw_addr->mac[5] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 0) & 0xff); + return GRUB_ERR_NONE; + } + + + + /* Check cache table. */ + entry = link_layer_find_entry (proto_addr, inf->card); + if (entry) + { + *hw_addr = entry->ll_address; + return GRUB_ERR_NONE; + } + switch (proto_addr->type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + err = grub_net_arp_send_request (inf, proto_addr); + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + err = grub_net_icmp6_send_request (inf, proto_addr); + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: + return grub_error (GRUB_ERR_BUG, "shouldn't reach here"); + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported address type %d", proto_addr->type); + } + if (err) + return err; + entry = link_layer_find_entry (proto_addr, inf->card); + if (entry) + { + *hw_addr = entry->ll_address; + return GRUB_ERR_NONE; + } + return grub_error (GRUB_ERR_TIMEOUT, + "timeout: could not resolve hardware address"); +} + void grub_net_card_unregister (struct grub_net_card *card) { diff --git a/include/grub/net.h b/include/grub/net.h index a4785d48c..e3f7525be 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -105,6 +105,8 @@ struct grub_net_slaac_mac_list char *name; }; +struct grub_net_link_layer_entry; + struct grub_net_card { struct grub_net_card *next; @@ -118,6 +120,8 @@ struct grub_net_card grub_uint64_t last_poll; grub_size_t mtu; struct grub_net_slaac_mac_list *slaac_list; + grub_ssize_t new_ll_entry; + struct grub_net_link_layer_entry *link_layer_table; union { #ifdef GRUB_MACHINE_EFI @@ -455,6 +459,19 @@ grub_net_network_level_interface_unregister (struct grub_net_network_level_inter void grub_net_tcp_retransmit (void); +void +grub_net_link_layer_add_address (struct grub_net_card *card, + const grub_net_network_level_address_t *nl, + const grub_net_link_level_address_t *ll, + int override); +int +grub_net_link_layer_resolve_check (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr); +grub_err_t +grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr, + grub_net_link_level_address_t *hw_addr); + extern char *grub_net_default_server; #define GRUB_NET_TRIES 40 diff --git a/include/grub/net/arp.h b/include/grub/net/arp.h index 49682184e..bb1703622 100644 --- a/include/grub/net/arp.h +++ b/include/grub/net/arp.h @@ -21,10 +21,11 @@ #include #include -extern grub_err_t grub_net_arp_receive(struct grub_net_buff *nb); +extern grub_err_t grub_net_arp_receive (struct grub_net_buff *nb, + struct grub_net_card *card); -extern grub_err_t grub_net_arp_resolve(struct grub_net_network_level_interface *inf, - const grub_net_network_level_address_t *addr, - grub_net_link_level_address_t *hw_addr); +grub_err_t +grub_net_arp_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr); #endif diff --git a/include/grub/net/ip.h b/include/grub/net/ip.h index e086f4411..78053de60 100644 --- a/include/grub/net/ip.h +++ b/include/grub/net/ip.h @@ -64,7 +64,8 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, struct grub_net_card *card, struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *source, - const grub_net_network_level_address_t *dest); + const grub_net_network_level_address_t *dest, + grub_uint8_t ttl); grub_err_t grub_net_recv_udp_packet (struct grub_net_buff *nb, struct grub_net_network_level_interface *inf, @@ -83,5 +84,8 @@ grub_net_ip_transport_checksum (struct grub_net_buff *nb, struct grub_net_network_level_interface * grub_net_ipv6_get_link_local (struct grub_net_card *card, const grub_net_link_level_address_t *hwaddr); +grub_err_t +grub_net_icmp6_send_request (struct grub_net_network_level_interface *inf, + const grub_net_network_level_address_t *proto_addr); #endif diff --git a/include/grub/types.h b/include/grub/types.h index 84bc97455..61cfb0291 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -154,6 +154,18 @@ typedef grub_uint64_t grub_disk_addr_t; #define grub_swap_bytes16_compile_time(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) #define grub_swap_bytes32_compile_time(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) & 0xff000000UL) >> 24)) +#define grub_swap_bytes64_compile_time(x) \ +({ \ + grub_uint64_t _x = (x); \ + (grub_uint64_t) ((_x << 56) \ + | ((_x & (grub_uint64_t) 0xFF00ULL) << 40) \ + | ((_x & (grub_uint64_t) 0xFF0000ULL) << 24) \ + | ((_x & (grub_uint64_t) 0xFF000000ULL) << 8) \ + | ((_x & (grub_uint64_t) 0xFF00000000ULL) >> 8) \ + | ((_x & (grub_uint64_t) 0xFF0000000000ULL) >> 24) \ + | ((_x & (grub_uint64_t) 0xFF000000000000ULL) >> 40) \ + | (_x >> 56)); \ +}) #if defined(__GNUC__) && (__GNUC__ > 3) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3) static inline grub_uint32_t grub_swap_bytes32(grub_uint32_t x) @@ -202,6 +214,9 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) ((grub_uint16_t) (x)) # define grub_be_to_cpu32(x) ((grub_uint32_t) (x)) # define grub_be_to_cpu64(x) ((grub_uint64_t) (x)) +# define grub_cpu_to_be32_compile_time(x) ((grub_uint32_t) (x)) +# define grub_cpu_to_be64_compile_time(x) ((grub_uint64_t) (x)) +# define grub_be_to_cpu64_compile_time(x) ((grub_uint64_t) (x)) # define grub_cpu_to_le32_compile_time(x) grub_swap_bytes32_compile_time(x) # define grub_cpu_to_le16_compile_time(x) grub_swap_bytes16_compile_time(x) #else /* ! WORDS_BIGENDIAN */ @@ -217,8 +232,12 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) grub_swap_bytes16(x) # define grub_be_to_cpu32(x) grub_swap_bytes32(x) # define grub_be_to_cpu64(x) grub_swap_bytes64(x) +# define grub_cpu_to_be32_compile_time(x) grub_swap_bytes32_compile_time(x) +# define grub_cpu_to_be64_compile_time(x) grub_swap_bytes64_compile_time(x) +# define grub_be_to_cpu64_compile_time(x) grub_swap_bytes64_compile_time(x) # define grub_cpu_to_le16_compile_time(x) ((grub_uint16_t) (x)) # define grub_cpu_to_le32_compile_time(x) ((grub_uint32_t) (x)) + #endif /* ! WORDS_BIGENDIAN */ #endif /* ! GRUB_TYPES_HEADER */