]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
more ipv6 code. Now ipv6 ping succeeds
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 12 Oct 2011 21:15:02 +0000 (23:15 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Wed, 12 Oct 2011 21:15:02 +0000 (23:15 +0200)
grub-core/net/arp.c
grub-core/net/ethernet.c
grub-core/net/icmp6.c
grub-core/net/ip.c
grub-core/net/net.c
include/grub/net.h
include/grub/net/arp.h
include/grub/net/ip.h
include/grub/types.h

index b1b719dbe6d2f8dc338ae4cb42bbddb4eb67e5cb..b4c10531bb611fced503d500c9be241b7dc5801d 100644 (file)
@@ -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)
   {
index 8d7f6683031e7b985dbf917fd1606a8962bfb033..6b5db9aaa9c174d81b2a3fb38f2da2e79ed4baa2 100644 (file)
@@ -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.  */
index ac12c5733e40c1d22541852439031a72f9d84b23..1f9278910c89bb56053ecd0975fd4b0a124c8043 100644 (file)
@@ -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_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)
+         {
+           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);
+             }
+         }
+       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)
-         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);
+             }
+           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;
+}
index 18593da4947f64d16a90f3f4405128e809358753..27b8221fbf002069f0e64338543f5000d33d5741 100644 (file)
@@ -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
index 1c6d47757d700b7bdeff6ad918257df1227814ee..1134223b2cb4d426cd4c7091a91d5a615a128feb 100644 (file)
@@ -27,6 +27,7 @@
 #include <grub/command.h>
 #include <grub/env.h>
 #include <grub/net/ethernet.h>
+#include <grub/net/arp.h>
 #include <grub/net/ip.h>
 #include <grub/loader.h>
 #include <grub/bufio.h>
@@ -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)
 {
index a4785d48c02cb6d4199809f5ab88805200a0a7fb..e3f7525be909ebedc47b16d0a7ced70e3c2b8670 100644 (file)
@@ -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
index 49682184e50b9633f831016a7098cc2ed967ca2f..bb1703622e1529479618e7f71b0d5e88adb631d1 100644 (file)
 #include <grub/misc.h>
 #include <grub/net.h>
 
-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 
index e086f4411f38277707a986ac4cab32c83b0aa198..78053de60bdc25d6688f81519063e11aa09c7ef6 100644 (file)
@@ -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 
index 84bc97455ff4590e82aa0683efd65678c3481368..61cfb0291ed88fc91415351fb27360128473bfc5 100644 (file)
@@ -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 */