]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipv6] Automatically choose source for link-local and multicast destinations
authorMichael Brown <mcb30@ipxe.org>
Fri, 25 Oct 2013 13:41:37 +0000 (14:41 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 25 Oct 2013 13:44:06 +0000 (14:44 +0100)
When transmitting to a link-local or multicast destination address,
use the network device's link-local address as the source address if
no explicit source address has been specified.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/net/ipv6.c

index dcaad3714ec90aad4cb2c3b99fae0ac081abea01..540ed05b6f7ccfbcd6831082af4de1006af29fd2 100644 (file)
@@ -93,10 +93,10 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) {
  *
  * @v miniroute                Routing table entry
  * @v address          IPv6 address
- * @ret is_local       Address is within this entry's local network
+ * @ret is_on_link     Address is within this entry's local network
  */
-static int ipv6_is_local ( struct ipv6_miniroute *miniroute,
-                          struct in6_addr *address ) {
+static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
+                            struct in6_addr *address ) {
        unsigned int i;
 
        for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
@@ -192,7 +192,6 @@ static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
 static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
                                            struct in6_addr **dest ) {
        struct ipv6_miniroute *miniroute;
-       int local;
 
        /* Find first usable route in routing table */
        list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
@@ -201,25 +200,32 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
                if ( ! netdev_is_open ( miniroute->netdev ) )
                        continue;
 
-               /* For link-local addresses, skip devices that are not
-                * the specified network device.
-                */
-               if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) &&
-                    ( miniroute->netdev->index != scope_id ) )
-                       continue;
+               if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) ||
+                    IN6_IS_ADDR_MULTICAST ( *dest ) ) {
 
-               /* Skip non-gateway devices for which the prefix does
-                * not match.
-                */
-               local = ipv6_is_local ( miniroute, *dest );
-               if ( ! ( local || miniroute->has_router ) )
-                       continue;
+                       /* If destination is non-global, and the scope ID
+                        * matches this network device, then use this route.
+                        */
+                       if ( miniroute->netdev->index == scope_id )
+                               return miniroute;
 
-               /* Update next hop if applicable */
-               if ( ! local )
-                       *dest = &miniroute->router;
+               } else {
 
-               return miniroute;
+                       /* If destination is an on-link global
+                        * address, then use this route.
+                        */
+                       if ( ipv6_is_on_link ( miniroute, *dest ) )
+                               return miniroute;
+
+                       /* If destination is an off-link global
+                        * address, and we have a default gateway,
+                        * then use this route.
+                        */
+                       if ( miniroute->has_router ) {
+                               *dest = &miniroute->router;
+                               return miniroute;
+                       }
+               }
        }
 
        return NULL;
@@ -363,6 +369,7 @@ static int ipv6_tx ( struct io_buffer *iobuf,
        struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
        struct ipv6_miniroute *miniroute;
        struct ipv6_header *iphdr;
+       struct in6_addr *src = NULL;
        struct in6_addr *next_hop;
        uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
        const void *ll_dest;
@@ -381,15 +388,9 @@ static int ipv6_tx ( struct io_buffer *iobuf,
 
        /* Use routing table to identify next hop and transmitting netdev */
        next_hop = &iphdr->dest;
-       if ( sin6_src ) {
-               memcpy ( &iphdr->src, &sin6_src->sin6_addr,
-                        sizeof ( iphdr->src ) );
-       }
-       if ( ( ! IN6_IS_ADDR_MULTICAST ( next_hop ) ) &&
-            ( ( miniroute = ipv6_route ( sin6_dest->sin6_scope_id,
-                                         &next_hop ) ) != NULL ) ) {
-               memcpy ( &iphdr->src, &miniroute->address,
-                        sizeof ( iphdr->src ) );
+       if ( ( miniroute = ipv6_route ( sin6_dest->sin6_scope_id,
+                                       &next_hop ) ) != NULL ) {
+               src = &miniroute->address;
                netdev = miniroute->netdev;
        }
        if ( ! netdev ) {
@@ -398,6 +399,9 @@ static int ipv6_tx ( struct io_buffer *iobuf,
                rc = -ENETUNREACH;
                goto err;
        }
+       if ( sin6_src )
+               src = &sin6_src->sin6_addr;
+       memcpy ( &iphdr->src, src, sizeof ( iphdr->src ) );
 
        /* Fix up checksums */
        if ( trans_csum ) {
@@ -897,7 +901,7 @@ int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix,
 
        /* Delete any existing SLAAC miniroutes for this prefix */
        list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) {
-               if ( ipv6_is_local ( miniroute, &address ) )
+               if ( ipv6_is_on_link ( miniroute, &address ) )
                        del_ipv6_miniroute ( miniroute );
        }