From c8323bfcb5f9fc2de2ae25e3a83abd9bef92d66d Mon Sep 17 00:00:00 2001 From: Matthew Iselin Date: Sat, 16 Jul 2011 22:38:27 +1000 Subject: [PATCH] [ipv6] Modify router solicits to provide metadata This patch allows router solicitation to return extra data to the requestor, if requested, which allows DHCPv6 to assign a fully routable address for use later. Signed-off-by: Matthew Iselin Signed-off-by: Marty Connor --- src/include/gpxe/ndp.h | 11 +++++++++- src/net/ndp.c | 44 ++++++++++++++++++++++++++++++-------- src/net/udp/dhcp6.c | 57 ++++++++++++++++++++++++++++++++++++-------------- src/usr/ip6mgmt.c | 2 +- 4 files changed, 87 insertions(+), 27 deletions(-) diff --git a/src/include/gpxe/ndp.h b/src/include/gpxe/ndp.h index b8a00d7e..c1899401 100644 --- a/src/include/gpxe/ndp.h +++ b/src/include/gpxe/ndp.h @@ -35,6 +35,13 @@ struct job_interface; #define NDP_OPTION_REDIRECT 4 #define NDP_OPTION_MTU 5 +struct rsolicit_info { + struct in6_addr router; + struct in6_addr prefix; + int prefix_length; + int no_address; /* No address assignment takes place via this adv. */ +}; + struct neighbour_solicit { uint8_t type; uint8_t code; @@ -100,7 +107,9 @@ 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_send_rsolicit ( struct net_device *netdev, + struct job_interface *job, + struct rsolicit_info *meta ); int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest, struct net_device *netdev, diff --git a/src/net/ndp.c b/src/net/ndp.c index dbe943cf..6ef2c2cd 100644 --- a/src/net/ndp.c +++ b/src/net/ndp.c @@ -44,6 +44,8 @@ struct pending_rsolicit { struct job_interface job; /** Reference counter */ struct refcnt refcnt; + /** Metadata to fill when we receive an advertisement. */ + struct rsolicit_info *meta; }; /** Number of entries in the neighbour cache table */ @@ -165,6 +167,7 @@ add_solicit_entry ( struct net_device *netdev, int state ) { entry->netdev = netdev; entry->state = state; entry->code = RSOLICIT_CODE_NONE; + entry->meta = NULL; return entry; } @@ -228,12 +231,16 @@ int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest, * * @v netdev Network device * @v src Source address + * @v meta (optional) Pointer to struct to fill with information + * when an advertisement arrives. * @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 ) { +int ndp_send_rsolicit ( struct net_device *netdev, + struct job_interface *job, + struct rsolicit_info *meta ) { union { struct sockaddr_in6 sin6; struct sockaddr_tcpip st; @@ -264,6 +271,7 @@ int ndp_send_rsolicit ( struct net_device *netdev, struct job_interface *job ) { /* Add an entry for this solicitation. */ entry = add_solicit_entry ( netdev, RSOLICIT_STATE_ALMOST ); + entry->meta = meta; /* Set up a job for the solicit. */ job_init ( &entry->job, &rsolicit_job_operations, &entry->refcnt ); @@ -301,10 +309,11 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src struct router_advert *radvert = iobuf->data; struct ndp_option *options = iobuf->data + sizeof(struct router_advert); struct in6_addr router_addr = ( ( struct sockaddr_in6 * ) st_src )->sin6_addr; - struct in6_addr host_addr; + struct in6_addr host_addr, prefix; int rc = -ENOENT; uint8_t prefix_len = 0; size_t offset = sizeof ( struct router_advert ), ll_size; + int can_autoconf = 0; /* Can we autoconfigure from the prefix? */ /* Verify that there's a pending solicit */ struct pending_rsolicit *pending = solicit_find_entry ( netdev ); @@ -346,7 +355,8 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src } /* Copy the prefix first and then add the EUI-64 */ - memcpy( &host_addr.s6_addr, opt->prefix, prefix_len / 8 ); + memcpy ( &prefix.s6_addr, opt->prefix, prefix_len / 8 ); + memcpy ( &host_addr.s6_addr, &prefix.s6_addr, prefix_len / 8 ); /* Create an IPv6 address for this station based on the prefix. */ ll_size = netdev->ll_protocol->ll_addr_len; @@ -355,6 +365,13 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src } else { ipv6_generate_eui64 ( host_addr.s6_addr + 8, netdev->ll_addr ); } + + /* Get flags. */ + can_autoconf = opt->flags_rsvd & ( 1 << 6 ); + if ( ! can_autoconf ) + DBG ( "ndp: got a prefix, but can't use it for SLAAC\n" ); + else + DBG ( "ndp: can use prefix for SLAAC\n" ); rc = 0; } @@ -385,17 +402,26 @@ int ndp_process_radvert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src return 0; } + + /* Fill in information if we need to. */ + if ( pending->meta != NULL ) { + DBG ( "ndp: filling meta information\n" ); + pending->meta->router = router_addr; + pending->meta->prefix = prefix; + pending->meta->prefix_length = prefix_len; + pending->meta->no_address = ! can_autoconf; + } /* Configure a route based on this router if none exists. */ - if ( net_protocol->check ( netdev, &host_addr ) ) { + if ( can_autoconf && net_protocol->check ( netdev, &host_addr ) ) { 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; + add_ipv6_address ( netdev, prefix, prefix_len, host_addr, router_addr ); } + + /* Completed without error. */ + job_done ( &pending->job, pending->code ); + pending->state = RSOLICIT_STATE_INVALID; return 0; } diff --git a/src/net/udp/dhcp6.c b/src/net/udp/dhcp6.c index aed6dab7..3ad25465 100644 --- a/src/net/udp/dhcp6.c +++ b/src/net/udp/dhcp6.c @@ -34,11 +34,13 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include #include #include +#include #include /* Get an option encapsulated inside another option. */ @@ -100,6 +102,9 @@ struct dhcp6_session { /** Settings to apply as a result of a DHCPv6 session. */ struct settings *settings; + + /** Information about the router to use for address assignment. */ + struct rsolicit_info router; }; static struct dhcp6_session_state dhcp6_solicit; @@ -353,20 +358,30 @@ int dhcp6_handle_option ( struct dhcp6_session *dhcp, DBG ( "dhcp6: assigned address is %s\n", inet6_ntoa ( addr->addr ) ); if ( completed ) { - /* Store the completed IPv6 address. */ - store_setting ( parent, - &ip6_setting, - &addr->addr, - sizeof ( struct in6_addr ) ); - - /* Add a fully-routable version now. */ - /* TODO: set address properly. */ - DBG ( "dhcp6: fixme, need to assign address properly\n" ); - add_ipv6_address ( dhcp->netdev, - addr->addr, - 128, - addr->addr, - addr->addr ); + if ( dhcp->router.no_address ) { + /* Store the completed IPv6 address. */ + store_setting ( parent, + &ip6_setting, + &addr->addr, + sizeof ( struct in6_addr ) ); + store_setting ( parent, + &gateway6_setting, + &dhcp->router, + sizeof ( struct in6_addr ) ); + store_setting ( parent, + &prefix_setting, + &dhcp->router.prefix_length, + sizeof ( dhcp->router.prefix_length ) ); + + /* Add a fully-routable version now. */ + add_ipv6_address ( dhcp->netdev, + dhcp->router.prefix, + dhcp->router.prefix_length, + addr->addr, + dhcp->router.router ); + } else { + DBG ( "dhcp6: not adding an address as SLAAC has done that\n" ); + } } else { dhcp->offer = addr->addr; } @@ -391,6 +406,8 @@ int dhcp6_handle_option ( struct dhcp6_session *dhcp, break; case DHCP6_OPT_DNS_DOMAINS: DBG ( "dhcp6: DNS search domains option\n" ); + + /* TODO: set DNS search domain, needs parsing though. */ break; case DHCP6_OPT_SERVERID: /* Verify the DUID if we already store one. */ @@ -404,8 +421,7 @@ int dhcp6_handle_option ( struct dhcp6_session *dhcp, DBG ( "dhcp6: server DUID is valid\n" ); } } else { - /* Grab in the server DUID for this session. */ - /* TODO: check for sane length */ + /* Grab in the server DUID for this session. */ dhcp->server_duid = malloc ( datalen ); dhcp->server_duid_len = datalen; memcpy ( dhcp->server_duid, iobuf->data, datalen ); @@ -745,6 +761,15 @@ int start_dhcp6 ( struct job_interface *job, struct net_device *netdev, int only dhcp = zalloc ( sizeof ( *dhcp ) ); if ( ! dhcp ) return -ENOMEM; + + + /* Get information about routers on this network first. */ + rc = ndp_send_rsolicit ( netdev, &monojob, &dhcp->router ); + if ( rc != 0 ) + DBG ( "dhcp6: can't find a router on the network, continuing\n" ); + else + monojob_wait ( "" ); + ref_init ( &dhcp->refcnt, dhcp6_free ); job_init ( &dhcp->job, &dhcp6_job_operations, &dhcp->refcnt ); xfer_init ( &dhcp->xfer, &dhcp6_xfer_operations, &dhcp->refcnt ); diff --git a/src/usr/ip6mgmt.c b/src/usr/ip6mgmt.c index afcbe393..21d30d6c 100644 --- a/src/usr/ip6mgmt.c +++ b/src/usr/ip6mgmt.c @@ -76,7 +76,7 @@ int ip6_autoconf ( struct net_device *netdev ) { add_ipv6_address ( netdev, ip6addr, 64, ip6addr, ip6zero ); /* Solicit routers on the network. */ - if ( ( rc = ndp_send_rsolicit ( netdev, &monojob ) ) == 0 ) { + if ( ( rc = ndp_send_rsolicit ( netdev, &monojob, NULL ) ) == 0 ) { rc = monojob_wait ( "" ); } -- 2.11.4.GIT