summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Brown2013-11-15 16:12:25 +0100
committerMichael Brown2013-11-15 16:22:54 +0100
commit6b1eee04527969f21ab65245b67cf9efa4b4aea8 (patch)
treecc174ea4309f6decfdcd7d93891c038cf54dcc49 /src
parent[build] Update build system for Syslinux 6.x (diff)
downloadipxe-6b1eee04527969f21ab65245b67cf9efa4b4aea8.tar.gz
ipxe-6b1eee04527969f21ab65245b67cf9efa4b4aea8.tar.xz
ipxe-6b1eee04527969f21ab65245b67cf9efa4b4aea8.zip
[ipv6] Separate the concepts of prefix and address creation
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>
Diffstat (limited to 'src')
-rw-r--r--src/include/ipxe/ipv6.h20
-rw-r--r--src/net/ipv6.c204
-rw-r--r--src/net/ndp.c97
-rw-r--r--src/usr/route_ipv6.c4
4 files changed, 201 insertions, 124 deletions
diff --git a/src/include/ipxe/ipv6.h b/src/include/ipxe/ipv6.h
index 3cb87826..c4a9f15e 100644
--- a/src/include/ipxe/ipv6.h
+++ b/src/include/ipxe/ipv6.h
@@ -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 */
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index b9619a1a..b4f33f0d 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -68,6 +68,24 @@ static uint32_t ipv6col ( struct in6_addr *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
*
* @v netdev Network device
@@ -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;
}
@@ -920,53 +999,6 @@ struct setting_type setting_type_ipv6 __setting_type = {
};
/**
- * 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
*
* @v netdev 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 );
+ }
}
}
diff --git a/src/net/ndp.c b/src/net/ndp.c
index f56bbe92..862e31ef 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -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;
}
diff --git a/src/usr/route_ipv6.c b/src/usr/route_ipv6.c
index 8a6fbde3..6045f85b 100644
--- a/src/usr/route_ipv6.c
+++ b/src/usr/route_ipv6.c
@@ -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" );