]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipv6] Separate the concepts of prefix and address creation
authorMichael Brown <mcb30@ipxe.org>
Fri, 15 Nov 2013 15:12:25 +0000 (15:12 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 15 Nov 2013 15:22:54 +0000 (15:22 +0000)
Allow for IPv6 routing table entries to be created for an on-link
prefix where a local address has not yet been assigned to the network
device.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/ipv6.h
src/net/ipv6.c
src/net/ndp.c
src/usr/route_ipv6.c

index 3cb87826229557f19a8e8aac475f635d92e2363a..c4a9f15e3fededdbef16c9ca486db3448dce5db5 100644 (file)
@@ -160,16 +160,24 @@ struct ipv6_miniroute {
        /** Network device */
        struct net_device *netdev;
 
-       /** IPv6 address */
+       /** IPv6 address (or prefix if no address is defined) */
        struct in6_addr address;
        /** Prefix length */
        unsigned int prefix_len;
        /** IPv6 prefix mask (derived from prefix length) */
        struct in6_addr prefix_mask;
-       /** Router address is present */
-       int has_router;
        /** Router address */
        struct in6_addr router;
+       /** Flags */
+       unsigned int flags;
+};
+
+/** IPv6 address/routing table entry flags */
+enum ipv6_miniroute_flags {
+       /** Routing table entry address is valid */
+       IPV6_HAS_ADDRESS = 0x0001,
+       /** Routing table entry router address is valid */
+       IPV6_HAS_ROUTER = 0x0002,
 };
 
 /**
@@ -235,7 +243,9 @@ extern struct list_head ipv6_miniroutes;
 extern struct net_protocol ipv6_protocol __net_protocol;
 
 extern int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr );
-extern int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix,
-                       unsigned int prefix_len, struct in6_addr *router );
+extern int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix,
+                            unsigned int prefix_len, struct in6_addr *router );
+extern int ipv6_set_address ( struct net_device *netdev,
+                             struct in6_addr *address );
 
 #endif /* _IPXE_IPV6_H */
index b9619a1aa863ff4c292ce9d3ea767669a620d0f8..b4f33f0d0f8906480c40d5ad78a7489aee3997a5 100644 (file)
@@ -67,6 +67,24 @@ static uint32_t ipv6col ( struct in6_addr *in ) {
        return crc32_le ( 0, in, sizeof ( *in ) );
 }
 
+/**
+ * Dump IPv6 routing table entry
+ *
+ * @v miniroute                Routing table entry
+ */
+static inline __attribute__ (( always_inline )) void
+ipv6_dump_miniroute ( struct ipv6_miniroute *miniroute ) {
+       struct net_device *netdev = miniroute->netdev;
+
+       DBGC ( netdev, "IPv6 %s has %s %s/%d", netdev->name,
+              ( ( miniroute->flags & IPV6_HAS_ADDRESS ) ?
+                "address" : "prefix" ),
+              inet6_ntoa ( &miniroute->address ), miniroute->prefix_len );
+       if ( miniroute->flags & IPV6_HAS_ROUTER )
+               DBGC ( netdev, " router %s", inet6_ntoa ( &miniroute->router ));
+       DBGC ( netdev, "\n" );
+}
+
 /**
  * Check if network device has a specific IPv6 address
  *
@@ -79,6 +97,7 @@ int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) {
 
        list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
                if ( ( miniroute->netdev == netdev ) &&
+                    ( miniroute->flags & IPV6_HAS_ADDRESS ) &&
                     ( memcmp ( &miniroute->address, addr,
                                sizeof ( miniroute->address ) ) == 0 ) ) {
                        /* Found matching address */
@@ -109,31 +128,45 @@ static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute,
 }
 
 /**
- * Add IPv6 minirouting table entry
+ * Find IPv6 routing table entry for a given address
  *
  * @v netdev           Network device
  * @v address          IPv6 address
+ * @ret miniroute      Routing table entry, or NULL if not found
+ */
+static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
+                                               struct in6_addr *address ) {
+       struct ipv6_miniroute *miniroute;
+
+       list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
+               if ( ( miniroute->netdev == netdev ) &&
+                    ipv6_is_on_link ( miniroute, address ) ) {
+                       return miniroute;
+               }
+       }
+       return NULL;
+}
+
+/**
+ * Add IPv6 routing table entry
+ *
+ * @v netdev           Network device
+ * @v address          IPv6 address (or prefix)
  * @v prefix_len       Prefix length
- * @v router           Router address (or NULL)
+ * @v flags            Flags
  * @ret miniroute      Routing table entry, or NULL on failure
  */
-static struct ipv6_miniroute * __malloc
-add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr *address,
-                    unsigned int prefix_len, struct in6_addr *router ) {
+static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev,
+                                                   struct in6_addr *address,
+                                                   unsigned int prefix_len,
+                                                   unsigned int flags ) {
        struct ipv6_miniroute *miniroute;
        uint8_t *prefix_mask;
 
-       DBGC ( netdev, "IPv6 add %s/%d ", inet6_ntoa ( address ), prefix_len );
-       if ( router )
-               DBGC ( netdev, "router %s ", inet6_ntoa ( router ) );
-       DBGC ( netdev, "via %s\n", netdev->name );
-
-       /* Allocate and populate miniroute structure */
+       /* Create routing table entry */
        miniroute = zalloc ( sizeof ( *miniroute ) );
        if ( ! miniroute )
                return NULL;
-
-       /* Record routing information */
        miniroute->netdev = netdev_get ( netdev );
        memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) );
        miniroute->prefix_len = prefix_len;
@@ -144,41 +177,83 @@ add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr *address,
        }
        if ( prefix_len )
                *prefix_mask <<= ( 8 - prefix_len );
+       miniroute->flags = flags;
+       list_add ( &miniroute->list, &ipv6_miniroutes );
+       ipv6_dump_miniroute ( miniroute );
+
+       return miniroute;
+}
+
+/**
+ * Define IPv6 on-link prefix
+ *
+ * @v netdev           Network device
+ * @v prefix           IPv6 address prefix
+ * @v prefix_len       Prefix length
+ * @v router           Router address (or NULL)
+ * @ret rc             Return status code
+ */
+int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix,
+                     unsigned int prefix_len, struct in6_addr *router ) {
+       struct ipv6_miniroute *miniroute;
+       int changed;
+
+       /* Find or create routing table entry */
+       miniroute = ipv6_miniroute ( netdev, prefix );
+       if ( ! miniroute )
+               miniroute = ipv6_add_miniroute ( netdev, prefix, prefix_len, 0);
+       if ( ! miniroute )
+               return -ENOMEM;
+
+       /* Record router and add to start or end of list as appropriate */
+       list_del ( &miniroute->list );
        if ( router ) {
-               miniroute->has_router = 1;
+               changed = ( ( ! ( miniroute->flags & IPV6_HAS_ROUTER ) ) ||
+                           ( memcmp ( &miniroute->router, router,
+                                      sizeof ( miniroute->router ) ) != 0 ) );
+               miniroute->flags |= IPV6_HAS_ROUTER;
                memcpy ( &miniroute->router, router,
                         sizeof ( miniroute->router ) );
-       }
-
-       /* Add to end of list if we have a gateway, otherwise to start
-        * of list.
-        */
-       if ( router ) {
                list_add_tail ( &miniroute->list, &ipv6_miniroutes );
        } else {
+               changed = ( miniroute->flags & IPV6_HAS_ROUTER );
+               miniroute->flags &= ~IPV6_HAS_ROUTER;
                list_add ( &miniroute->list, &ipv6_miniroutes );
        }
+       if ( changed )
+               ipv6_dump_miniroute ( miniroute );
 
-       return miniroute;
+       return 0;
 }
 
 /**
- * Delete IPv6 minirouting table entry
+ * Add IPv6 on-link address
  *
- * @v miniroute                Routing table entry
+ * @v netdev           Network device
+ * @v address          IPv6 address
+ * @ret rc             Return status code
+ *
+ * An on-link prefix for the address must already exist.
  */
-static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
-       struct net_device *netdev = miniroute->netdev;
+int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) {
+       struct ipv6_miniroute *miniroute;
+       int changed;
+
+       /* Find routing table entry */
+       miniroute = ipv6_miniroute ( netdev, address );
+       if ( ! miniroute )
+               return -EADDRNOTAVAIL;
 
-       DBGC ( netdev, "IPv6 del %s/%d ", inet6_ntoa ( &miniroute->address ),
-              miniroute->prefix_len );
-       if ( miniroute->has_router )
-               DBGC ( netdev, "router %s ", inet6_ntoa ( &miniroute->router ));
-       DBGC ( netdev, "via %s\n", netdev->name );
+       /* Record address */
+       changed = ( ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) ||
+                   ( memcmp ( &miniroute->address, address,
+                              sizeof ( miniroute->address ) ) != 0 ) );
+       memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) );
+       miniroute->flags |= IPV6_HAS_ADDRESS;
+       if ( changed )
+               ipv6_dump_miniroute ( miniroute );
 
-       netdev_put ( miniroute->netdev );
-       list_del ( &miniroute->list );
-       free ( miniroute );
+       return 0;
 }
 
 /**
@@ -200,6 +275,10 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
                if ( ! netdev_is_open ( miniroute->netdev ) )
                        continue;
 
+               /* Skip routing table entries with no usable source address */
+               if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
+                       continue;
+
                if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) ||
                     IN6_IS_ADDR_MULTICAST ( *dest ) ) {
 
@@ -221,7 +300,7 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
                         * address, and we have a default gateway,
                         * then use this route.
                         */
-                       if ( miniroute->has_router ) {
+                       if ( miniroute->flags & IPV6_HAS_ROUTER ) {
                                *dest = &miniroute->router;
                                return miniroute;
                        }
@@ -919,53 +998,6 @@ struct setting_type setting_type_ipv6 __setting_type = {
        .format = format_ipv6_setting,
 };
 
-/**
- * Perform IPv6 stateless address autoconfiguration (SLAAC)
- *
- * @v netdev           Network device
- * @v prefix           Prefix
- * @v prefix_len       Prefix length
- * @v router           Router address (or NULL)
- * @ret rc             Return status code
- */
-int ipv6_slaac ( struct net_device *netdev, struct in6_addr *prefix,
-                unsigned int prefix_len, struct in6_addr *router ) {
-       struct ipv6_miniroute *miniroute;
-       struct ipv6_miniroute *tmp;
-       struct in6_addr address;
-       int check_prefix_len;
-       int rc;
-
-       /* Construct local address */
-       memcpy ( &address, prefix, sizeof ( address ) );
-       check_prefix_len = ipv6_eui64 ( &address, netdev );
-       if ( check_prefix_len < 0 ) {
-               rc = check_prefix_len;
-               DBGC ( netdev, "IPv6 %s could not construct SLAAC address: "
-                      "%s\n", netdev->name, strerror ( rc ) );
-               return rc;
-       }
-       if ( check_prefix_len != ( int ) prefix_len ) {
-               DBGC ( netdev, "IPv6 %s incorrect SLAAC prefix length %d "
-                      "(expected %d)\n", netdev->name, prefix_len,
-                      check_prefix_len );
-               return -EINVAL;
-       }
-
-       /* Delete any existing SLAAC miniroutes for this prefix */
-       list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) {
-               if ( ipv6_is_on_link ( miniroute, &address ) )
-                       del_ipv6_miniroute ( miniroute );
-       }
-
-       /* Add miniroute */
-       miniroute = add_ipv6_miniroute ( netdev, &address, prefix_len, router );
-       if ( ! miniroute )
-               return -ENOMEM;
-
-       return 0;
-}
-
 /**
  * Create IPv6 network device
  *
@@ -989,7 +1021,8 @@ static int ipv6_probe ( struct net_device *netdev ) {
        }
 
        /* Create link-local address for this network device */
-       miniroute = add_ipv6_miniroute ( netdev, &address, prefix_len, NULL );
+       miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len,
+                                        IPV6_HAS_ADDRESS );
        if ( ! miniroute )
                return -ENOMEM;
 
@@ -1007,8 +1040,11 @@ static void ipv6_remove ( struct net_device *netdev ) {
 
        /* Delete all miniroutes for this network device */
        list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) {
-               if ( miniroute->netdev == netdev )
-                       del_ipv6_miniroute ( miniroute );
+               if ( miniroute->netdev == netdev ) {
+                       netdev_put ( miniroute->netdev );
+                       list_del ( &miniroute->list );
+                       free ( miniroute );
+               }
        }
 }
 
index f56bbe928d2f99259f8483e500d3b451519f8e5b..862e31ef40843c4b88ff982b7cf1a43ac1ce1619 100644 (file)
@@ -88,8 +88,8 @@ static int ndp_tx_ll_addr ( struct net_device *netdev,
        /* Transmit packet */
        if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest,
                               netdev, &ndp->icmp.chksum ) ) != 0 ) {
-               DBGC ( netdev, "NDP could not transmit packet: %s\n",
-                      strerror ( rc ) );
+               DBGC ( netdev, "NDP %s could not transmit packet: %s\n",
+                      netdev->name, strerror ( rc ) );
                return rc;
        }
 
@@ -205,8 +205,9 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
        /* Sanity check */
        if ( offsetof ( typeof ( *ll_addr_opt ),
                        ll_addr[ll_protocol->ll_addr_len] ) > len ) {
-               DBGC ( netdev, "NDP neighbour solicitation link-layer address "
-                      "option too short at %zd bytes\n", len );
+               DBGC ( netdev, "NDP %s neighbour solicitation link-layer "
+                      "address option too short at %zd bytes\n",
+                      netdev->name, len );
                return -EINVAL;
        }
 
@@ -214,8 +215,8 @@ ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev,
        if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
                                       &sin6_src->sin6_addr,
                                       ll_addr_opt->ll_addr ) ) != 0 ) {
-               DBGC ( netdev, "NDP could not define %s => %s: %s\n",
-                      inet6_ntoa ( &sin6_src->sin6_addr ),
+               DBGC ( netdev, "NDP %s could not define %s => %s: %s\n",
+                      netdev->name, inet6_ntoa ( &sin6_src->sin6_addr ),
                       ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
                       strerror ( rc ) );
                return rc;
@@ -260,16 +261,17 @@ ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev,
        /* Sanity check */
        if ( offsetof ( typeof ( *ll_addr_opt ),
                        ll_addr[ll_protocol->ll_addr_len] ) > len ) {
-               DBGC ( netdev, "NDP neighbour advertisement link-layer address "
-                      "option too short at %zd bytes\n", len );
+               DBGC ( netdev, "NDP %s neighbour advertisement link-layer "
+                      "address option too short at %zd bytes\n",
+                      netdev->name, len );
                return -EINVAL;
        }
 
        /* Update neighbour cache entry, if any */
        if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target,
                                       ll_addr_opt->ll_addr ) ) != 0 ) {
-               DBGC ( netdev, "NDP could not update %s => %s: %s\n",
-                      inet6_ntoa ( &neigh->target ),
+               DBGC ( netdev, "NDP %s could not update %s => %s: %s\n",
+                      netdev->name, inet6_ntoa ( &neigh->target ),
                       ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
                       strerror ( rc ) );
                return rc;
@@ -300,8 +302,8 @@ ndp_rx_router_advertisement_ll_source ( struct net_device *netdev,
        /* Sanity check */
        if ( offsetof ( typeof ( *ll_addr_opt ),
                        ll_addr[ll_protocol->ll_addr_len] ) > len ) {
-               DBGC ( netdev, "NDP router advertisement link-layer address "
-                      "option too short at %zd bytes\n", len );
+               DBGC ( netdev, "NDP %s router advertisement link-layer address "
+                      "option too short at %zd bytes\n", netdev->name, len );
                return -EINVAL;
        }
 
@@ -309,8 +311,8 @@ ndp_rx_router_advertisement_ll_source ( struct net_device *netdev,
        if ( ( rc = neighbour_define ( netdev, &ipv6_protocol,
                                       &sin6_src->sin6_addr,
                                       ll_addr_opt->ll_addr ) ) != 0 ) {
-               DBGC ( netdev, "NDP could not define %s => %s: %s\n",
-                      inet6_ntoa ( &sin6_src->sin6_addr ),
+               DBGC ( netdev, "NDP %s could not define %s => %s: %s\n",
+                      netdev->name, inet6_ntoa ( &sin6_src->sin6_addr ),
                       ll_protocol->ntoa ( ll_addr_opt->ll_addr ),
                       strerror ( rc ) );
                return rc;
@@ -337,16 +339,18 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
        struct ndp_router_advertisement_header *radv = &ndp->radv;
        struct ndp_prefix_information_option *prefix_opt = &option->prefix;
        struct in6_addr *router = &sin6_src->sin6_addr;
+       struct in6_addr address;
+       int prefix_len;
        int rc;
 
        /* Sanity check */
        if ( sizeof ( *prefix_opt ) > len ) {
-               DBGC ( netdev, "NDP router advertisement prefix option too "
-                      "short at %zd bytes\n", len );
+               DBGC ( netdev, "NDP %s router advertisement prefix option too "
+                      "short at %zd bytes\n", netdev->name, len );
                return -EINVAL;
        }
-       DBGC ( netdev, "NDP found %sdefault router %s ",
-              ( radv->lifetime ? "" : "non-" ),
+       DBGC ( netdev, "NDP %s found %sdefault router %s ",
+              netdev->name, ( radv->lifetime ? "" : "non-" ),
               inet6_ntoa ( &sin6_src->sin6_addr ) );
        DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n",
               ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ),
@@ -354,17 +358,41 @@ ndp_rx_router_advertisement_prefix ( struct net_device *netdev,
               inet6_ntoa ( &prefix_opt->prefix ),
               prefix_opt->prefix_len );
 
+       /* Ignore off-link prefixes */
+       if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) )
+               return 0;
+
+       /* Define prefix */
+       if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix,
+                                     prefix_opt->prefix_len,
+                                     ( radv->lifetime ?
+                                       router : NULL ) ) ) != 0 ) {
+               DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n",
+                      netdev->name, inet6_ntoa ( &prefix_opt->prefix ),
+                      prefix_opt->prefix_len, strerror ( rc ) );
+               return rc;
+       }
+
        /* Perform stateless address autoconfiguration, if applicable */
-       if ( ( prefix_opt->flags &
-              ( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) ==
-            ( NDP_PREFIX_ON_LINK | NDP_PREFIX_AUTONOMOUS ) ) {
-               if ( ( rc = ipv6_slaac ( netdev, &prefix_opt->prefix,
-                                        prefix_opt->prefix_len,
-                                        ( radv->lifetime ?
-                                          router : NULL ) ) ) != 0 ) {
-                       DBGC ( netdev, "NDP could not autoconfigure prefix %s/"
-                              "%d: %s\n", inet6_ntoa ( &prefix_opt->prefix ),
-                              prefix_opt->prefix_len, strerror ( rc ) );
+       if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) {
+               memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) );
+               prefix_len = ipv6_eui64 ( &address, netdev );
+               if ( prefix_len < 0 ) {
+                       rc = prefix_len;
+                       DBGC ( netdev, "NDP %s could not construct SLAAC "
+                              "address: %s\n", netdev->name, strerror ( rc ) );
+                       return rc;
+               }
+               if ( prefix_len != prefix_opt->prefix_len ) {
+                       DBGC ( netdev, "NDP %s incorrect SLAAC prefix length "
+                              "%d (expected %d)\n", netdev->name,
+                              prefix_opt->prefix_len, prefix_len );
+                       return -EINVAL;
+               }
+               if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) {
+                       DBGC ( netdev, "NDP %s could not set address %s: %s\n",
+                              netdev->name, inet6_ntoa ( &address ),
+                              strerror ( rc ) );
                        return rc;
                }
        }
@@ -467,8 +495,8 @@ static int ndp_rx_options ( struct net_device *netdev,
 
        /* Sanity check */
        if ( len < offset ) {
-               DBGC ( netdev, "NDP packet too short at %zd bytes (min %zd "
-                      "bytes)\n", len, offset );
+               DBGC ( netdev, "NDP %s packet too short at %zd bytes (min %zd "
+                      "bytes)\n", netdev->name, len, offset );
                return -EINVAL;
        }
 
@@ -482,7 +510,8 @@ static int ndp_rx_options ( struct net_device *netdev,
                     ( option->header.blocks == 0 ) ||
                     ( remaining < ( option->header.blocks *
                                     NDP_OPTION_BLKSZ ) ) ) {
-                       DBGC ( netdev, "NDP bad option length:\n" );
+                       DBGC ( netdev, "NDP %s bad option length:\n",
+                              netdev->name );
                        DBGC_HDA ( netdev, 0, option, remaining );
                        return -EINVAL;
                }
@@ -715,9 +744,9 @@ static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
                stateful = ( flags & NDP_ROUTER_MANAGED );
                if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
                                           stateful ) ) != 0 ) {
-                       DBGC ( netdev, "NDP could not start state%s DHCPv6: "
-                              "%s\n", ( stateful ? "ful" : "less" ),
-                              strerror ( rc ) );
+                       DBGC ( netdev, "NDP %s could not start state%s DHCPv6: "
+                              "%s\n", netdev->name,
+                              ( stateful ? "ful" : "less" ), strerror ( rc ) );
                        ipv6conf_done ( ipv6conf, rc );
                        return rc;
                }
index 8a6fbde3750b65964300416fec315a4468119d66..6045f85bb6a6d01bac2199bd362d1fddc71c2da0 100644 (file)
@@ -44,8 +44,10 @@ static void route_ipv6_print ( struct net_device *netdev ) {
                printf ( "%s: %s/%d", netdev->name,
                         inet6_ntoa ( &miniroute->address ),
                         miniroute->prefix_len );
-               if ( miniroute->has_router )
+               if ( miniroute->flags & IPV6_HAS_ROUTER )
                        printf ( " gw %s", inet6_ntoa ( &miniroute->router ) );
+               if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
+                       printf ( " (no address)" );
                if ( ! netdev_is_open ( miniroute->netdev ) )
                        printf ( " (inaccessible)" );
                printf ( "\n" );