From: Matthew Iselin <[email protected]> Note: does not yet time out if no router advertisements are received.
Signed-off-by: Matthew Iselin <[email protected]> --- src/include/gpxe/icmp6.h | 2 - src/include/gpxe/ndp.h | 12 +++ src/net/icmpv6.c | 42 ----------- src/net/ndp.c | 171 +++++++++++++++++++++++++++++++++++++++++++++- src/usr/ip6mgmt.c | 18 ++++- 5 files changed, 197 insertions(+), 48 deletions(-) diff --git a/src/include/gpxe/icmp6.h b/src/include/gpxe/icmp6.h index 23b7b56..6040258 100644 --- a/src/include/gpxe/icmp6.h +++ b/src/include/gpxe/icmp6.h @@ -65,8 +65,6 @@ int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest ); -int icmp6_send_rsolicit ( struct net_device *netdev ); - int icmp6_send_advert ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest ); #endif /* _GPXE_ICMP6_H */ diff --git a/src/include/gpxe/ndp.h b/src/include/gpxe/ndp.h index be387ce..b8a00d7 100644 --- a/src/include/gpxe/ndp.h +++ b/src/include/gpxe/ndp.h @@ -11,6 +11,7 @@ #include <gpxe/tcpip.h> struct icmp6_net_protocol; +struct job_interface; #define NDP_STATE_INVALID 0 #define NDP_STATE_INCOMPLETE 1 @@ -19,6 +20,15 @@ struct icmp6_net_protocol; #define NDP_STATE_PROBE 4 #define NDP_STATE_STALE 5 +#define RSOLICIT_STATE_INVALID 0 +#define RSOLICIT_STATE_PENDING 1 +#define RSOLICIT_STATE_COMPLETE 2 +#define RSOLICIT_STATE_ALMOST 3 + +#define RSOLICIT_CODE_NONE 0 +#define RSOLICIT_CODE_MANAGED 1 +#define RSOLICIT_CODE_OTHERCONF 2 + #define NDP_OPTION_SOURCE_LL 1 #define NDP_OPTION_TARGET_LL 2 #define NDP_OPTION_PREFIX_INFO 3 @@ -90,6 +100,8 @@ struct prefix_option int ndp_resolve ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest, void *dest_ll_addr ); +int ndp_send_rsolicit ( struct net_device *netdev, struct job_interface *job ); + int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest, struct net_device *netdev, struct icmp6_net_protocol *net_protocol ); diff --git a/src/net/icmpv6.c b/src/net/icmpv6.c index f24b98b..ac4e308 100644 --- a/src/net/icmpv6.c +++ b/src/net/icmpv6.c @@ -68,48 +68,6 @@ int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unuse } /** - * Send router solicitation packet - * - * @v netdev Network device - * @v src Source address - * @v dest Destination address - * - * This function prepares a neighbour solicitation packet and sends it to the - * network layer. - */ -int icmp6_send_rsolicit ( struct net_device *netdev ) { - union { - struct sockaddr_in6 sin6; - struct sockaddr_tcpip st; - } st_dest; - struct router_solicit *solicit; - struct io_buffer *iobuf = alloc_iob ( sizeof ( *solicit ) + MIN_IOB_LEN ); - - iob_reserve ( iobuf, MAX_HDR_LEN ); - solicit = iob_put ( iobuf, sizeof ( *solicit ) ); - - /* Fill up the headers */ - memset ( solicit, 0, sizeof ( *solicit ) ); - solicit->type = ICMP6_ROUTER_SOLICIT; - solicit->code = 0; - - /* Partial checksum */ - solicit->csum = 0; - solicit->csum = tcpip_chksum ( solicit, sizeof ( *solicit ) ); - - /* Solicited multicast address - FF02::2 (all routers on local network) */ - memset(&st_dest.sin6, 0, sizeof(st_dest.sin6)); - st_dest.sin6.sin_family = AF_INET6; - st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff; - st_dest.sin6.sin6_addr.in6_u.u6_addr8[1] = 0x2; - st_dest.sin6.sin6_addr.in6_u.u6_addr8[15] = 0x2; - - /* Send packet over IP6 */ - return ipv6_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st, - netdev, &solicit->csum ); -} - -/** * Send neighbour advertisement packet * * @v netdev Network device diff --git a/src/net/ndp.c b/src/net/ndp.c index be1fa94..e0c53d8 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -9,6 +9,7 @@ #include <gpxe/icmp6.h> #include <gpxe/ip6.h> #include <gpxe/netdevice.h> +#include <gpxe/job.h> /** @file * @@ -31,6 +32,20 @@ struct ndp_entry { int state; }; +/** A pending router solicitation. */ +struct pending_rsolicit { + /** Network device for the solicit. */ + struct net_device *netdev; + /** State of the solicitation. */ + int state; + /** Status code after handling the solicit. */ + int code; + /** Job control interface */ + struct job_interface job; + /** Reference counter */ + struct refcnt refcnt; +}; + /** Number of entries in the neighbour cache table */ #define NUM_NDP_ENTRIES 4 @@ -40,6 +55,37 @@ static struct ndp_entry ndp_table[NUM_NDP_ENTRIES]; static unsigned int next_new_ndp_entry = 0; +/** The pending solicit table */ +static struct pending_rsolicit solicit_table[NUM_NDP_ENTRIES]; +#define solicit_table_end &solicit_table[NUM_NDP_ENTRIES] + +static unsigned int next_new_solicit_entry = 0; + +/** + * Handle kill() event received via job control interface + * + * @v job Router solicit job control interface + */ +static void rsolicit_job_kill ( struct job_interface *job ) { + struct pending_rsolicit *entry = + container_of ( job, struct pending_rsolicit, job ); + + /* Terminate. */ + entry->code = 0; + entry->state = RSOLICIT_STATE_INVALID; + + /* Clean up. */ + job_nullify ( &entry->job ); + job_done ( &entry->job, -ECANCELED ); +} + +/** Router solicit job control interface operations */ +static struct job_interface_operations rsolicit_job_operations = { + .done = ignore_job_done, + .kill = rsolicit_job_kill, + .progress = ignore_job_progress, +}; + /** * Find entry in the neighbour cache * @@ -59,6 +105,24 @@ ndp_find_entry ( struct in6_addr *in6 ) { } /** + * Find a pending router solicitation for an interface. + * + * @v netdev Interface for the solicitation. + */ +static struct pending_rsolicit * +solicit_find_entry ( struct net_device *netdev ) { + struct pending_rsolicit *entry; + + for ( entry = solicit_table ; entry < solicit_table_end ; entry++ ) { + if ( ( entry->netdev == netdev ) && + ( entry->state == RSOLICIT_STATE_PENDING ) ) { + return entry; + } + } + return NULL; +} + +/** * Add NDP entry * * @v netdev Network device @@ -87,6 +151,25 @@ add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6, } /** + * Add pending solicit entry + * + * @v netdev Network device + * @v state State of the entry - one of the RSOLICIT_STATE_XXX values + */ +static struct pending_rsolicit * +add_solicit_entry ( struct net_device *netdev, int state ) { + struct pending_rsolicit *entry; + entry = &solicit_table[next_new_solicit_entry++ % NUM_NDP_ENTRIES]; + + /* Fill up entry */ + entry->netdev = netdev; + entry->state = state; + entry->code = RSOLICIT_CODE_NONE; + + return entry; +} + +/** * Resolve the link-layer address * * @v netdev Network device @@ -141,6 +224,71 @@ int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest, } /** + * Send router solicitation packet + * + * @v netdev Network device + * @v src Source address + * @v dest Destination address + * + * This function prepares a neighbour solicitation packet and sends it to the + * network layer. + */ +int ndp_send_rsolicit ( struct net_device *netdev, struct job_interface *job ) { + union { + struct sockaddr_in6 sin6; + struct sockaddr_tcpip st; + } st_dest; + struct router_solicit *solicit; + struct io_buffer *iobuf = alloc_iob ( sizeof ( *solicit ) + MIN_IOB_LEN ); + struct pending_rsolicit *entry; + int rc = 0; + + iob_reserve ( iobuf, MAX_HDR_LEN ); + solicit = iob_put ( iobuf, sizeof ( *solicit ) ); + + /* Fill up the headers */ + memset ( solicit, 0, sizeof ( *solicit ) ); + solicit->type = ICMP6_ROUTER_SOLICIT; + solicit->code = 0; + + /* Partial checksum */ + solicit->csum = 0; + solicit->csum = tcpip_chksum ( solicit, sizeof ( *solicit ) ); + + /* Solicited multicast address - FF02::2 (all routers on local network) */ + memset(&st_dest.sin6, 0, sizeof(st_dest.sin6)); + st_dest.sin6.sin_family = AF_INET6; + st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff; + st_dest.sin6.sin6_addr.in6_u.u6_addr8[1] = 0x2; + st_dest.sin6.sin6_addr.in6_u.u6_addr8[15] = 0x2; + + /* Add an entry for this solicitation. */ + entry = add_solicit_entry ( netdev, RSOLICIT_STATE_ALMOST ); + + /* Set up a job for the solicit. */ + job_init ( &entry->job, &rsolicit_job_operations, &entry->refcnt ); + + /* Send packet over IP6 */ + rc = ipv6_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st, + netdev, &solicit->csum ); + + /* Return. */ + if ( rc == 0 ) { + entry->state = RSOLICIT_STATE_PENDING; + + job_plug_plug ( &entry->job, job ); + ref_put ( &entry->refcnt ); + return 0; + } else { + entry->state = RSOLICIT_STATE_INVALID; + + rsolicit_job_kill ( &entry->job ); + ref_put ( &entry->refcnt ); + return rc; + } +} + +/** * Process Router Advertisement * * @v iobuf I/O buffer containing the data. @@ -158,12 +306,23 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src uint8_t prefix_len = 0; size_t offset = sizeof ( struct router_advert ), ll_size; + /* Verify that there's a pending solicit */ + struct pending_rsolicit *pending = solicit_find_entry ( netdev ); + if ( ! pending ) { + DBG ( "ndp: unsolicited router advertisement, ignoring\n" ); + return rc; + } + memset ( &host_addr, 0, sizeof ( host_addr ) ); - /* Verify that we shouldn't be trying DHCPv6 instead. */ + /* Router advertisement flags */ if ( ntohs ( radvert->hops_flags ) & RADVERT_MANAGED ) { DBG ( "ndp: router advertisement suggests DHCPv6\n" ); - return 0; + pending->code |= RSOLICIT_CODE_MANAGED; + } + if ( ntohs ( radvert->hops_flags ) & RADVERT_OTHERCONF ) { + DBG ( "ndp: router advertisement suggests DHCPv6 for additional information\n" ); + pending->code |= RSOLICIT_CODE_OTHERCONF; } /* Parse options. */ @@ -220,6 +379,10 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src if ( rc ) { DBG ( "ndp: couldn't generate a prefix from a router advertisement\n" ); + pending->code = RSOLICIT_CODE_NONE; /* Clear flags. */ + + job_done ( &pending->job, -ENOENT ); + return 0; } @@ -228,6 +391,10 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src DBG ( "ndp: autoconfigured %s/%d via a router advertisement\n", inet6_ntoa( host_addr ), prefix_len); add_ipv6_address ( netdev, host_addr, prefix_len, host_addr, router_addr ); + + job_done ( &pending->job, pending->code ); + + pending->state = RSOLICIT_STATE_INVALID; } return 0; diff --git a/src/usr/ip6mgmt.c b/src/usr/ip6mgmt.c index 9b8e9cb..2f84261 100644 --- a/src/usr/ip6mgmt.c +++ b/src/usr/ip6mgmt.c @@ -73,8 +73,22 @@ int ip6_autoconf ( struct net_device *netdev ) { add_ipv6_address ( netdev, ip6addr, 10, ip6addr, ip6zero ); /* Solicit routers on the network. */ - icmp6_send_rsolicit ( netdev ); + if ( ( rc = ndp_send_rsolicit ( netdev, &monojob ) ) == 0 ) { + rc = monojob_wait ( "" ); + } + + if ( rc < 0 ) { + DBG ( "ipv6: router solicitation failed\n" ); + } else { + if ( rc & RSOLICIT_CODE_MANAGED ) { + DBG ( "ipv6: should use dhcp6 server\n" ); + } else if ( rc & RSOLICIT_CODE_OTHERCONF ) { + DBG ( "ipv6: some more info is available via dhcp6\n" ); + } else { + DBG ( "ipv6: autoconfiguration complete\n" ); + } + } - return 0; + return rc; } -- 1.7.2.5 _______________________________________________ gPXE-devel mailing list [email protected] http://etherboot.org/mailman/listinfo/gpxe-devel
