iPXE
ipv6.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER );
00021 
00022 #include <stdint.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <strings.h>
00027 #include <errno.h>
00028 #include <assert.h>
00029 #include <byteswap.h>
00030 #include <ipxe/iobuf.h>
00031 #include <ipxe/tcpip.h>
00032 #include <ipxe/if_ether.h>
00033 #include <ipxe/crc32.h>
00034 #include <ipxe/fragment.h>
00035 #include <ipxe/ipstat.h>
00036 #include <ipxe/ndp.h>
00037 #include <ipxe/ipv6.h>
00038 
00039 /** @file
00040  *
00041  * IPv6 protocol
00042  *
00043  */
00044 
00045 /* Disambiguate the various error causes */
00046 #define EINVAL_LEN __einfo_error ( EINFO_EINVAL_LEN )
00047 #define EINFO_EINVAL_LEN \
00048         __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid length" )
00049 #define ENOTSUP_VER __einfo_error ( EINFO_ENOTSUP_VER )
00050 #define EINFO_ENOTSUP_VER \
00051         __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported version" )
00052 #define ENOTSUP_HDR __einfo_error ( EINFO_ENOTSUP_HDR )
00053 #define EINFO_ENOTSUP_HDR \
00054         __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported header type" )
00055 #define ENOTSUP_OPT __einfo_error ( EINFO_ENOTSUP_OPT )
00056 #define EINFO_ENOTSUP_OPT \
00057         __einfo_uniqify ( EINFO_ENOTSUP, 0x03, "Unsupported option" )
00058 
00059 /** List of IPv6 miniroutes */
00060 struct list_head ipv6_miniroutes = LIST_HEAD_INIT ( ipv6_miniroutes );
00061 
00062 /** IPv6 statistics */
00063 static struct ip_statistics ipv6_stats;
00064 
00065 /** IPv6 statistics family */
00066 struct ip_statistics_family
00067 ipv6_statistics_family __ip_statistics_family ( IP_STATISTICS_IPV6 ) = {
00068         .version = 6,
00069         .stats = &ipv6_stats,
00070 };
00071 
00072 /**
00073  * Determine debugging colour for IPv6 debug messages
00074  *
00075  * @v in                IPv6 address
00076  * @ret col             Debugging colour (for DBGC())
00077  */
00078 static uint32_t ipv6col ( struct in6_addr *in ) {
00079         return crc32_le ( 0, in, sizeof ( *in ) );
00080 }
00081 
00082 /**
00083  * Determine IPv6 address scope
00084  *
00085  * @v addr              IPv6 address
00086  * @ret scope           Address scope
00087  */
00088 static unsigned int ipv6_scope ( const struct in6_addr *addr ) {
00089 
00090         /* Multicast addresses directly include a scope field */
00091         if ( IN6_IS_ADDR_MULTICAST ( addr ) )
00092                 return ipv6_multicast_scope ( addr );
00093 
00094         /* Link-local addresses have link-local scope */
00095         if ( IN6_IS_ADDR_LINKLOCAL ( addr ) )
00096                 return IPV6_SCOPE_LINK_LOCAL;
00097 
00098         /* Site-local addresses have site-local scope */
00099         if ( IN6_IS_ADDR_SITELOCAL ( addr ) )
00100                 return IPV6_SCOPE_SITE_LOCAL;
00101 
00102         /* Unique local addresses do not directly map to a defined
00103          * scope.  They effectively have a scope which is wider than
00104          * link-local but narrower than global.  Since the only
00105          * multicast packets that we transmit are link-local, we can
00106          * simply choose an arbitrary scope between link-local and
00107          * global.
00108          */
00109         if ( IN6_IS_ADDR_ULA ( addr ) )
00110                 return IPV6_SCOPE_ORGANISATION_LOCAL;
00111 
00112         /* All other addresses are assumed to be global */
00113         return IPV6_SCOPE_GLOBAL;
00114 }
00115 
00116 /**
00117  * Dump IPv6 routing table entry
00118  *
00119  * @v miniroute         Routing table entry
00120  */
00121 static inline __attribute__ (( always_inline )) void
00122 ipv6_dump_miniroute ( struct ipv6_miniroute *miniroute ) {
00123         struct net_device *netdev = miniroute->netdev;
00124 
00125         DBGC ( netdev, "IPv6 %s has %s %s/%d", netdev->name,
00126                ( ( miniroute->flags & IPV6_HAS_ADDRESS ) ?
00127                  "address" : "prefix" ),
00128                inet6_ntoa ( &miniroute->address ), miniroute->prefix_len );
00129         if ( miniroute->flags & IPV6_HAS_ROUTER )
00130                 DBGC ( netdev, " router %s", inet6_ntoa ( &miniroute->router ));
00131         DBGC ( netdev, "\n" );
00132 }
00133 
00134 /**
00135  * Check if network device has a specific IPv6 address
00136  *
00137  * @v netdev            Network device
00138  * @v addr              IPv6 address
00139  * @ret has_addr        Network device has this IPv6 address
00140  */
00141 int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) {
00142         struct ipv6_miniroute *miniroute;
00143 
00144         list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
00145                 if ( ( miniroute->netdev == netdev ) &&
00146                      ( miniroute->flags & IPV6_HAS_ADDRESS ) &&
00147                      ( memcmp ( &miniroute->address, addr,
00148                                 sizeof ( miniroute->address ) ) == 0 ) ) {
00149                         /* Found matching address */
00150                         return 1;
00151                 }
00152         }
00153         return 0;
00154 }
00155 
00156 /**
00157  * Count matching bits of an IPv6 routing table entry prefix
00158  *
00159  * @v miniroute         Routing table entry
00160  * @v address           IPv6 address
00161  * @ret match_len       Number of matching prefix bits
00162  */
00163 static unsigned int ipv6_match_len ( struct ipv6_miniroute *miniroute,
00164                                      struct in6_addr *address ) {
00165         unsigned int match_len = 0;
00166         unsigned int i;
00167         uint32_t diff;
00168 
00169         for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
00170                             sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
00171 
00172                 diff = ntohl ( ~( ( ~( address->s6_addr32[i] ^
00173                                        miniroute->address.s6_addr32[i] ) )
00174                                   & miniroute->prefix_mask.s6_addr32[i] ) );
00175                 match_len += 32;
00176                 if ( diff ) {
00177                         match_len -= flsl ( diff );
00178                         break;
00179                 }
00180         }
00181 
00182         return match_len;
00183 }
00184 
00185 /**
00186  * Find IPv6 routing table entry for a given address
00187  *
00188  * @v netdev            Network device
00189  * @v address           IPv6 address
00190  * @ret miniroute       Routing table entry, or NULL if not found
00191  */
00192 static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev,
00193                                                 struct in6_addr *address ) {
00194         struct ipv6_miniroute *miniroute;
00195         unsigned int match_len;
00196 
00197         list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
00198                 if ( miniroute->netdev != netdev )
00199                         continue;
00200                 match_len = ipv6_match_len ( miniroute, address );
00201                 if ( match_len < miniroute->prefix_len )
00202                         continue;
00203                 return miniroute;
00204         }
00205         return NULL;
00206 }
00207 
00208 /**
00209  * Add IPv6 routing table entry
00210  *
00211  * @v netdev            Network device
00212  * @v address           IPv6 address (or prefix)
00213  * @v prefix_len        Prefix length
00214  * @v router            Router address (if any)
00215  * @ret rc              Return status code
00216  */
00217 int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address,
00218                          unsigned int prefix_len, struct in6_addr *router ) {
00219         struct ipv6_miniroute *miniroute;
00220         uint8_t *prefix_mask;
00221         unsigned int remaining;
00222         unsigned int i;
00223 
00224         /* Find or create routing table entry */
00225         miniroute = ipv6_miniroute ( netdev, address );
00226         if ( miniroute ) {
00227 
00228                 /* Remove from existing position in routing table */
00229                 list_del ( &miniroute->list );
00230 
00231         } else {
00232 
00233                 /* Create new routing table entry */
00234                 miniroute = zalloc ( sizeof ( *miniroute ) );
00235                 if ( ! miniroute )
00236                         return -ENOMEM;
00237                 miniroute->netdev = netdev_get ( netdev );
00238                 memcpy ( &miniroute->address, address,
00239                          sizeof ( miniroute->address ) );
00240 
00241                 /* Default to prefix length of 64 if none specified */
00242                 if ( ! prefix_len )
00243                         prefix_len = IPV6_DEFAULT_PREFIX_LEN;
00244                 miniroute->prefix_len = prefix_len;
00245                 assert ( prefix_len <= IPV6_MAX_PREFIX_LEN );
00246 
00247                 /* Construct prefix mask */
00248                 remaining = prefix_len;
00249                 for ( prefix_mask = miniroute->prefix_mask.s6_addr ;
00250                       remaining >= 8 ; prefix_mask++, remaining -= 8 ) {
00251                         *prefix_mask = 0xff;
00252                 }
00253                 if ( remaining )
00254                         *prefix_mask <<= ( 8 - remaining );
00255         }
00256 
00257         /* Add to start of routing table */
00258         list_add ( &miniroute->list, &ipv6_miniroutes );
00259 
00260         /* Set or update address, if applicable */
00261         for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) /
00262                             sizeof ( address->s6_addr32[0] ) ) ; i++ ) {
00263                 if ( ( address->s6_addr32[i] &
00264                        ~miniroute->prefix_mask.s6_addr32[i] ) != 0 ) {
00265                         memcpy ( &miniroute->address, address,
00266                                  sizeof ( miniroute->address ) );
00267                         miniroute->flags |= IPV6_HAS_ADDRESS;
00268                 }
00269         }
00270         if ( miniroute->prefix_len == IPV6_MAX_PREFIX_LEN )
00271                 miniroute->flags |= IPV6_HAS_ADDRESS;
00272 
00273         /* Update scope */
00274         miniroute->scope = ipv6_scope ( &miniroute->address );
00275 
00276         /* Set or update router, if applicable */
00277         if ( router ) {
00278                 memcpy ( &miniroute->router, router,
00279                          sizeof ( miniroute->router ) );
00280                 miniroute->flags |= IPV6_HAS_ROUTER;
00281         }
00282 
00283         ipv6_dump_miniroute ( miniroute );
00284         return 0;
00285 }
00286 
00287 /**
00288  * Delete IPv6 minirouting table entry
00289  *
00290  * @v miniroute         Routing table entry
00291  */
00292 void ipv6_del_miniroute ( struct ipv6_miniroute *miniroute ) {
00293 
00294         netdev_put ( miniroute->netdev );
00295         list_del ( &miniroute->list );
00296         free ( miniroute );
00297 }
00298 
00299 /**
00300  * Perform IPv6 routing
00301  *
00302  * @v scope_id          Destination address scope ID (for link-local addresses)
00303  * @v dest              Final destination address
00304  * @ret dest            Next hop destination address
00305  * @ret miniroute       Routing table entry to use, or NULL if no route
00306  */
00307 struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
00308                                      struct in6_addr **dest ) {
00309         struct ipv6_miniroute *miniroute;
00310         struct ipv6_miniroute *chosen = NULL;
00311         unsigned int best = 0;
00312         unsigned int match_len;
00313         unsigned int score;
00314         unsigned int scope;
00315 
00316         /* Calculate destination address scope */
00317         scope = ipv6_scope ( *dest );
00318 
00319         /* Find first usable route in routing table */
00320         list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) {
00321 
00322                 /* Skip closed network devices */
00323                 if ( ! netdev_is_open ( miniroute->netdev ) )
00324                         continue;
00325 
00326                 /* Skip entries with no usable source address */
00327                 if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) )
00328                         continue;
00329 
00330                 /* Skip entries with a non-matching scope ID, if
00331                  * destination specifies a scope ID.
00332                  */
00333                 if ( scope_id && ( miniroute->netdev->index != scope_id ) )
00334                         continue;
00335 
00336                 /* Skip entries that are out of scope */
00337                 if ( miniroute->scope < scope )
00338                         continue;
00339 
00340                 /* Calculate match length */
00341                 match_len = ipv6_match_len ( miniroute, *dest );
00342 
00343                 /* If destination is on-link, then use this route */
00344                 if ( match_len >= miniroute->prefix_len )
00345                         return miniroute;
00346 
00347                 /* If destination is unicast, then skip off-link
00348                  * entries with no router.
00349                  */
00350                 if ( ! ( IN6_IS_ADDR_MULTICAST ( *dest ) ||
00351                          ( miniroute->flags & IPV6_HAS_ROUTER ) ) )
00352                         continue;
00353 
00354                 /* Choose best route, defined as being the route with
00355                  * the smallest viable scope.  If two routes both have
00356                  * the same scope, then prefer the route with the
00357                  * longest match length.
00358                  */
00359                 score = ( ( ( IPV6_SCOPE_MAX + 1 - miniroute->scope ) << 8 )
00360                           + match_len );
00361                 if ( score > best ) {
00362                         chosen = miniroute;
00363                         best = score;
00364                 }
00365         }
00366 
00367         /* Return chosen route, if any */
00368         if ( chosen ) {
00369                 if ( ! IN6_IS_ADDR_MULTICAST ( *dest ) )
00370                         *dest = &chosen->router;
00371                 return chosen;
00372         }
00373 
00374         return NULL;
00375 }
00376 
00377 /**
00378  * Determine transmitting network device
00379  *
00380  * @v st_dest           Destination network-layer address
00381  * @ret netdev          Transmitting network device, or NULL
00382  */
00383 static struct net_device * ipv6_netdev ( struct sockaddr_tcpip *st_dest ) {
00384         struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
00385         struct in6_addr *dest = &sin6_dest->sin6_addr;
00386         struct ipv6_miniroute *miniroute;
00387 
00388         /* Find routing table entry */
00389         miniroute = ipv6_route ( sin6_dest->sin6_scope_id, &dest );
00390         if ( ! miniroute )
00391                 return NULL;
00392 
00393         return miniroute->netdev;
00394 }
00395 
00396 /**
00397  * Check that received options can be safely ignored
00398  *
00399  * @v iphdr             IPv6 header
00400  * @v options           Options extension header
00401  * @v len               Maximum length of header
00402  * @ret rc              Return status code
00403  */
00404 static int ipv6_check_options ( struct ipv6_header *iphdr,
00405                                 struct ipv6_options_header *options,
00406                                 size_t len ) {
00407         struct ipv6_option *option = options->options;
00408         struct ipv6_option *end = ( ( ( void * ) options ) + len );
00409 
00410         while ( option < end ) {
00411                 if ( ! IPV6_CAN_IGNORE_OPT ( option->type ) ) {
00412                         DBGC ( ipv6col ( &iphdr->src ), "IPv6 unrecognised "
00413                                "option type %#02x:\n", option->type );
00414                         DBGC_HDA ( ipv6col ( &iphdr->src ), 0,
00415                                    options, len );
00416                         return -ENOTSUP_OPT;
00417                 }
00418                 if ( option->type == IPV6_OPT_PAD1 ) {
00419                         option = ( ( ( void * ) option ) + 1 );
00420                 } else {
00421                         option = ( ( ( void * ) option->value ) + option->len );
00422                 }
00423         }
00424         return 0;
00425 }
00426 
00427 /**
00428  * Check if fragment matches fragment reassembly buffer
00429  *
00430  * @v fragment          Fragment reassembly buffer
00431  * @v iobuf             I/O buffer
00432  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00433  * @ret is_fragment     Fragment matches this reassembly buffer
00434  */
00435 static int ipv6_is_fragment ( struct fragment *fragment,
00436                               struct io_buffer *iobuf, size_t hdrlen ) {
00437         struct ipv6_header *frag_iphdr = fragment->iobuf->data;
00438         struct ipv6_fragment_header *frag_fhdr =
00439                 ( fragment->iobuf->data + fragment->hdrlen -
00440                   sizeof ( *frag_fhdr ) );
00441         struct ipv6_header *iphdr = iobuf->data;
00442         struct ipv6_fragment_header *fhdr =
00443                 ( iobuf->data + hdrlen - sizeof ( *fhdr ) );
00444 
00445         return ( ( memcmp ( &iphdr->src, &frag_iphdr->src,
00446                             sizeof ( iphdr->src ) ) == 0 ) &&
00447                  ( fhdr->ident == frag_fhdr->ident ) );
00448 }
00449 
00450 /**
00451  * Get fragment offset
00452  *
00453  * @v iobuf             I/O buffer
00454  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00455  * @ret offset          Offset
00456  */
00457 static size_t ipv6_fragment_offset ( struct io_buffer *iobuf, size_t hdrlen ) {
00458         struct ipv6_fragment_header *fhdr =
00459                 ( iobuf->data + hdrlen - sizeof ( *fhdr ) );
00460 
00461         return ( ntohs ( fhdr->offset_more ) & IPV6_MASK_OFFSET );
00462 }
00463 
00464 /**
00465  * Check if more fragments exist
00466  *
00467  * @v iobuf             I/O buffer
00468  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00469  * @ret more_frags      More fragments exist
00470  */
00471 static int ipv6_more_fragments ( struct io_buffer *iobuf, size_t hdrlen ) {
00472         struct ipv6_fragment_header *fhdr =
00473                 ( iobuf->data + hdrlen - sizeof ( *fhdr ) );
00474 
00475         return ( fhdr->offset_more & htons ( IPV6_MASK_MOREFRAGS ) );
00476 }
00477 
00478 /** Fragment reassembler */
00479 static struct fragment_reassembler ipv6_reassembler = {
00480         .list = LIST_HEAD_INIT ( ipv6_reassembler.list ),
00481         .is_fragment = ipv6_is_fragment,
00482         .fragment_offset = ipv6_fragment_offset,
00483         .more_fragments = ipv6_more_fragments,
00484         .stats = &ipv6_stats,
00485 };
00486 
00487 /**
00488  * Calculate IPv6 pseudo-header checksum
00489  *
00490  * @v iphdr             IPv6 header
00491  * @v len               Payload length
00492  * @v next_header       Next header type
00493  * @v csum              Existing checksum
00494  * @ret csum            Updated checksum
00495  */
00496 static uint16_t ipv6_pshdr_chksum ( struct ipv6_header *iphdr, size_t len,
00497                                     int next_header, uint16_t csum ) {
00498         struct ipv6_pseudo_header pshdr;
00499 
00500         /* Build pseudo-header */
00501         memcpy ( &pshdr.src, &iphdr->src, sizeof ( pshdr.src ) );
00502         memcpy ( &pshdr.dest, &iphdr->dest, sizeof ( pshdr.dest ) );
00503         pshdr.len = htonl ( len );
00504         memset ( pshdr.zero, 0, sizeof ( pshdr.zero ) );
00505         pshdr.next_header = next_header;
00506 
00507         /* Update the checksum value */
00508         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
00509 }
00510 
00511 /**
00512  * Transmit IPv6 packet
00513  *
00514  * @v iobuf             I/O buffer
00515  * @v tcpip             Transport-layer protocol
00516  * @v st_src            Source network-layer address
00517  * @v st_dest           Destination network-layer address
00518  * @v netdev            Network device to use if no route found, or NULL
00519  * @v trans_csum        Transport-layer checksum to complete, or NULL
00520  * @ret rc              Status
00521  *
00522  * This function expects a transport-layer segment and prepends the
00523  * IPv6 header
00524  */
00525 static int ipv6_tx ( struct io_buffer *iobuf,
00526                      struct tcpip_protocol *tcpip_protocol,
00527                      struct sockaddr_tcpip *st_src,
00528                      struct sockaddr_tcpip *st_dest,
00529                      struct net_device *netdev,
00530                      uint16_t *trans_csum ) {
00531         struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src );
00532         struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
00533         struct ipv6_miniroute *miniroute;
00534         struct ipv6_header *iphdr;
00535         struct in6_addr *src = NULL;
00536         struct in6_addr *next_hop;
00537         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
00538         const void *ll_dest;
00539         size_t len;
00540         int rc;
00541 
00542         /* Update statistics */
00543         ipv6_stats.out_requests++;
00544 
00545         /* Fill up the IPv6 header, except source address */
00546         len = iob_len ( iobuf );
00547         iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
00548         memset ( iphdr, 0, sizeof ( *iphdr ) );
00549         iphdr->ver_tc_label = htonl ( IPV6_VER );
00550         iphdr->len = htons ( len );
00551         iphdr->next_header = tcpip_protocol->tcpip_proto;
00552         iphdr->hop_limit = IPV6_HOP_LIMIT;
00553         memcpy ( &iphdr->dest, &sin6_dest->sin6_addr, sizeof ( iphdr->dest ) );
00554 
00555         /* Use routing table to identify next hop and transmitting netdev */
00556         next_hop = &iphdr->dest;
00557         if ( ( miniroute = ipv6_route ( sin6_dest->sin6_scope_id,
00558                                         &next_hop ) ) != NULL ) {
00559                 src = &miniroute->address;
00560                 netdev = miniroute->netdev;
00561         }
00562         if ( ! netdev ) {
00563                 DBGC ( ipv6col ( &iphdr->dest ), "IPv6 has no route to %s\n",
00564                        inet6_ntoa ( &iphdr->dest ) );
00565                 ipv6_stats.out_no_routes++;
00566                 rc = -ENETUNREACH;
00567                 goto err;
00568         }
00569         if ( sin6_src && ! IN6_IS_ADDR_UNSPECIFIED ( &sin6_src->sin6_addr ) )
00570                 src = &sin6_src->sin6_addr;
00571         if ( src )
00572                 memcpy ( &iphdr->src, src, sizeof ( iphdr->src ) );
00573 
00574         /* Fix up checksums */
00575         if ( trans_csum ) {
00576                 *trans_csum = ipv6_pshdr_chksum ( iphdr, len,
00577                                                   tcpip_protocol->tcpip_proto,
00578                                                   *trans_csum );
00579                 if ( ! *trans_csum )
00580                         *trans_csum = tcpip_protocol->zero_csum;
00581         }
00582 
00583         /* Print IPv6 header for debugging */
00584         DBGC2 ( ipv6col ( &iphdr->dest ), "IPv6 TX %s->",
00585                 inet6_ntoa ( &iphdr->src ) );
00586         DBGC2 ( ipv6col ( &iphdr->dest ), "%s len %zd next %d\n",
00587                 inet6_ntoa ( &iphdr->dest ), len, iphdr->next_header );
00588 
00589         /* Calculate link-layer destination address, if possible */
00590         if ( IN6_IS_ADDR_MULTICAST ( next_hop ) ) {
00591                 /* Multicast address */
00592                 ipv6_stats.out_mcast_pkts++;
00593                 if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET6, next_hop,
00594                                                            ll_dest_buf ) ) !=0){
00595                         DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not hash "
00596                                "multicast %s: %s\n", inet6_ntoa ( next_hop ),
00597                                strerror ( rc ) );
00598                         goto err;
00599                 }
00600                 ll_dest = ll_dest_buf;
00601         } else {
00602                 /* Unicast address */
00603                 ll_dest = NULL;
00604         }
00605 
00606         /* Update statistics */
00607         ipv6_stats.out_transmits++;
00608         ipv6_stats.out_octets += iob_len ( iobuf );
00609 
00610         /* Hand off to link layer (via NDP if applicable) */
00611         if ( ll_dest ) {
00612                 if ( ( rc = net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest,
00613                                      netdev->ll_addr ) ) != 0 ) {
00614                         DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not "
00615                                "transmit packet via %s: %s\n",
00616                                netdev->name, strerror ( rc ) );
00617                         return rc;
00618                 }
00619         } else {
00620                 if ( ( rc = ndp_tx ( iobuf, netdev, next_hop, &iphdr->src,
00621                                      netdev->ll_addr ) ) != 0 ) {
00622                         DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not "
00623                                "transmit packet via %s: %s\n",
00624                                netdev->name, strerror ( rc ) );
00625                         return rc;
00626                 }
00627         }
00628 
00629         return 0;
00630 
00631  err:
00632         free_iob ( iobuf );
00633         return rc;
00634 }
00635 
00636 /**
00637  * Process incoming IPv6 packets
00638  *
00639  * @v iobuf             I/O buffer
00640  * @v netdev            Network device
00641  * @v ll_dest           Link-layer destination address
00642  * @v ll_source         Link-layer destination source
00643  * @v flags             Packet flags
00644  * @ret rc              Return status code
00645  *
00646  * This function expects an IPv6 network datagram. It processes the
00647  * headers and sends it to the transport layer.
00648  */
00649 static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
00650                      const void *ll_dest __unused,
00651                      const void *ll_source __unused,
00652                      unsigned int flags __unused ) {
00653         struct ipv6_header *iphdr = iobuf->data;
00654         union ipv6_extension_header *ext;
00655         union {
00656                 struct sockaddr_in6 sin6;
00657                 struct sockaddr_tcpip st;
00658         } src, dest;
00659         uint16_t pshdr_csum;
00660         size_t len;
00661         size_t hdrlen;
00662         size_t extlen;
00663         int this_header;
00664         int next_header;
00665         int rc;
00666 
00667         /* Update statistics */
00668         ipv6_stats.in_receives++;
00669         ipv6_stats.in_octets += iob_len ( iobuf );
00670         if ( flags & LL_BROADCAST ) {
00671                 ipv6_stats.in_bcast_pkts++;
00672         } else if ( flags & LL_MULTICAST ) {
00673                 ipv6_stats.in_mcast_pkts++;
00674         }
00675 
00676         /* Sanity check the IPv6 header */
00677         if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) {
00678                 DBGC ( ipv6col ( &iphdr->src ), "IPv6 packet too short at %zd "
00679                        "bytes (min %zd bytes)\n", iob_len ( iobuf ),
00680                        sizeof ( *iphdr ) );
00681                 rc = -EINVAL_LEN;
00682                 goto err_header;
00683         }
00684         if ( ( iphdr->ver_tc_label & htonl ( IPV6_MASK_VER ) ) !=
00685              htonl ( IPV6_VER ) ) {
00686                 DBGC ( ipv6col ( &iphdr->src ), "IPv6 version %#08x not "
00687                        "supported\n", ntohl ( iphdr->ver_tc_label ) );
00688                 rc = -ENOTSUP_VER;
00689                 goto err_header;
00690         }
00691 
00692         /* Truncate packet to specified length */
00693         len = ntohs ( iphdr->len );
00694         if ( len > iob_len ( iobuf ) ) {
00695                 DBGC ( ipv6col ( &iphdr->src ), "IPv6 length too long at %zd "
00696                        "bytes (packet is %zd bytes)\n", len, iob_len ( iobuf ));
00697                 ipv6_stats.in_truncated_pkts++;
00698                 rc = -EINVAL_LEN;
00699                 goto err_other;
00700         }
00701         iob_unput ( iobuf, ( iob_len ( iobuf ) - len - sizeof ( *iphdr ) ) );
00702         hdrlen = sizeof ( *iphdr );
00703 
00704         /* Print IPv6 header for debugging */
00705         DBGC2 ( ipv6col ( &iphdr->src ), "IPv6 RX %s<-",
00706                 inet6_ntoa ( &iphdr->dest ) );
00707         DBGC2 ( ipv6col ( &iphdr->src ), "%s len %zd next %d\n",
00708                 inet6_ntoa ( &iphdr->src ), len, iphdr->next_header );
00709 
00710         /* Discard unicast packets not destined for us */
00711         if ( ( ! ( flags & LL_MULTICAST ) ) &&
00712              ( ! ipv6_has_addr ( netdev, &iphdr->dest ) ) ) {
00713                 DBGC ( ipv6col ( &iphdr->src ), "IPv6 discarding non-local "
00714                        "unicast packet for %s\n", inet6_ntoa ( &iphdr->dest ) );
00715                 ipv6_stats.in_addr_errors++;
00716                 rc = -EPIPE;
00717                 goto err_other;
00718         }
00719 
00720         /* Process any extension headers */
00721         next_header = iphdr->next_header;
00722         while ( 1 ) {
00723 
00724                 /* Extract extension header */
00725                 this_header = next_header;
00726                 ext = ( iobuf->data + hdrlen );
00727                 extlen = sizeof ( ext->pad );
00728                 if ( iob_len ( iobuf ) < ( hdrlen + extlen ) ) {
00729                         DBGC ( ipv6col ( &iphdr->src ), "IPv6 too short for "
00730                                "extension header type %d at %zd bytes (min "
00731                                "%zd bytes)\n", this_header,
00732                                ( iob_len ( iobuf ) - hdrlen ), extlen );
00733                         rc = -EINVAL_LEN;
00734                         goto err_header;
00735                 }
00736 
00737                 /* Determine size of extension header (if applicable) */
00738                 if ( ( this_header == IPV6_HOPBYHOP ) ||
00739                      ( this_header == IPV6_DESTINATION ) ||
00740                      ( this_header == IPV6_ROUTING ) ) {
00741                         /* Length field is present */
00742                         extlen += ext->common.len;
00743                 } else if ( this_header == IPV6_FRAGMENT ) {
00744                         /* Length field is reserved and ignored (RFC2460) */
00745                 } else {
00746                         /* Not an extension header; assume rest is payload */
00747                         break;
00748                 }
00749                 if ( iob_len ( iobuf ) < ( hdrlen + extlen ) ) {
00750                         DBGC ( ipv6col ( &iphdr->src ), "IPv6 too short for "
00751                                "extension header type %d at %zd bytes (min "
00752                                "%zd bytes)\n", this_header,
00753                                ( iob_len ( iobuf ) - hdrlen ), extlen );
00754                         rc = -EINVAL_LEN;
00755                         goto err_header;
00756                 }
00757                 hdrlen += extlen;
00758                 next_header = ext->common.next_header;
00759                 DBGC2 ( ipv6col ( &iphdr->src ), "IPv6 RX %s<-",
00760                         inet6_ntoa ( &iphdr->dest ) );
00761                 DBGC2 ( ipv6col ( &iphdr->src ), "%s ext type %d len %zd next "
00762                         "%d\n", inet6_ntoa ( &iphdr->src ), this_header,
00763                         extlen, next_header );
00764 
00765                 /* Process this extension header */
00766                 if ( ( this_header == IPV6_HOPBYHOP ) ||
00767                      ( this_header == IPV6_DESTINATION ) ) {
00768 
00769                         /* Check that all options can be ignored */
00770                         if ( ( rc = ipv6_check_options ( iphdr, &ext->options,
00771                                                          extlen ) ) != 0 )
00772                                 goto err_header;
00773 
00774                 } else if ( this_header == IPV6_FRAGMENT ) {
00775 
00776                         /* Reassemble fragments */
00777                         iobuf = fragment_reassemble ( &ipv6_reassembler, iobuf,
00778                                                       &hdrlen );
00779                         if ( ! iobuf )
00780                                 return 0;
00781                         iphdr = iobuf->data;
00782                 }
00783         }
00784 
00785         /* Construct socket address, calculate pseudo-header checksum,
00786          * and hand off to transport layer
00787          */
00788         memset ( &src, 0, sizeof ( src ) );
00789         src.sin6.sin6_family = AF_INET6;
00790         memcpy ( &src.sin6.sin6_addr, &iphdr->src,
00791                  sizeof ( src.sin6.sin6_addr ) );
00792         src.sin6.sin6_scope_id = netdev->index;
00793         memset ( &dest, 0, sizeof ( dest ) );
00794         dest.sin6.sin6_family = AF_INET6;
00795         memcpy ( &dest.sin6.sin6_addr, &iphdr->dest,
00796                  sizeof ( dest.sin6.sin6_addr ) );
00797         dest.sin6.sin6_scope_id = netdev->index;
00798         iob_pull ( iobuf, hdrlen );
00799         pshdr_csum = ipv6_pshdr_chksum ( iphdr, iob_len ( iobuf ),
00800                                          next_header, TCPIP_EMPTY_CSUM );
00801         if ( ( rc = tcpip_rx ( iobuf, netdev, next_header, &src.st, &dest.st,
00802                                pshdr_csum, &ipv6_stats ) ) != 0 ) {
00803                 DBGC ( ipv6col ( &src.sin6.sin6_addr ), "IPv6 received packet "
00804                                 "rejected by stack: %s\n", strerror ( rc ) );
00805                 return rc;
00806         }
00807 
00808         return 0;
00809 
00810  err_header:
00811         ipv6_stats.in_hdr_errors++;
00812  err_other:
00813         free_iob ( iobuf );
00814         return rc;
00815 }
00816 
00817 /**
00818  * Parse IPv6 address
00819  *
00820  * @v string            IPv6 address string
00821  * @ret in              IPv6 address to fill in
00822  * @ret rc              Return status code
00823  */
00824 int inet6_aton ( const char *string, struct in6_addr *in ) {
00825         uint16_t *word = in->s6_addr16;
00826         uint16_t *end = ( word + ( sizeof ( in->s6_addr16 ) /
00827                                    sizeof ( in->s6_addr16[0] ) ) );
00828         uint16_t *pad = NULL;
00829         const char *nptr = string;
00830         char *endptr;
00831         unsigned long value;
00832         size_t pad_len;
00833         size_t move_len;
00834 
00835         /* Parse string */
00836         while ( 1 ) {
00837 
00838                 /* Parse current word */
00839                 value = strtoul ( nptr, &endptr, 16 );
00840                 if ( value > 0xffff ) {
00841                         DBG ( "IPv6 invalid word value %#lx in \"%s\"\n",
00842                               value, string );
00843                         return -EINVAL;
00844                 }
00845                 *(word++) = htons ( value );
00846 
00847                 /* Parse separator */
00848                 if ( ! *endptr )
00849                         break;
00850                 if ( *endptr != ':' ) {
00851                         DBG ( "IPv6 invalid separator '%c' in \"%s\"\n",
00852                               *endptr, string );
00853                         return -EINVAL;
00854                 }
00855                 if ( ( endptr == nptr ) && ( nptr != string ) ) {
00856                         if ( pad ) {
00857                                 DBG ( "IPv6 invalid multiple \"::\" in "
00858                                       "\"%s\"\n", string );
00859                                 return -EINVAL;
00860                         }
00861                         pad = word;
00862                 }
00863                 nptr = ( endptr + 1 );
00864 
00865                 /* Check for overrun */
00866                 if ( word == end ) {
00867                         DBG ( "IPv6 too many words in \"%s\"\n", string );
00868                         return -EINVAL;
00869                 }
00870         }
00871 
00872         /* Insert padding if specified */
00873         if ( pad ) {
00874                 move_len = ( ( ( void * ) word ) - ( ( void * ) pad ) );
00875                 pad_len = ( ( ( void * ) end ) - ( ( void * ) word ) );
00876                 memmove ( ( ( ( void * ) pad ) + pad_len ), pad, move_len );
00877                 memset ( pad, 0, pad_len );
00878         } else if ( word != end ) {
00879                 DBG ( "IPv6 underlength address \"%s\"\n", string );
00880                 return -EINVAL;
00881         }
00882 
00883         return 0;
00884 }
00885 
00886 /**
00887  * Convert IPv6 address to standard notation
00888  *
00889  * @v in                IPv6 address
00890  * @ret string          IPv6 address string in canonical format
00891  *
00892  * RFC5952 defines the canonical format for IPv6 textual representation.
00893  */
00894 char * inet6_ntoa ( const struct in6_addr *in ) {
00895         static char buf[41]; /* ":xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx" */
00896         char *out = buf;
00897         char *longest_start = NULL;
00898         char *start = NULL;
00899         int longest_len = 1;
00900         int len = 0;
00901         char *dest;
00902         unsigned int i;
00903         uint16_t value;
00904 
00905         /* Format address, keeping track of longest run of zeros */
00906         for ( i = 0 ; i < ( sizeof ( in->s6_addr16 ) /
00907                             sizeof ( in->s6_addr16[0] ) ) ; i++ ) {
00908                 value = ntohs ( in->s6_addr16[i] );
00909                 if ( value == 0 ) {
00910                         if ( len++ == 0 )
00911                                 start = out;
00912                         if ( len > longest_len ) {
00913                                 longest_start = start;
00914                                 longest_len = len;
00915                         }
00916                 } else {
00917                         len = 0;
00918                 }
00919                 out += sprintf ( out, ":%x", value );
00920         }
00921 
00922         /* Abbreviate longest run of zeros, if applicable */
00923         if ( longest_start ) {
00924                 dest = strcpy ( ( longest_start + 1 ),
00925                                 ( longest_start + ( 2 * longest_len ) ) );
00926                 if ( dest[0] == '\0' )
00927                         dest[1] = '\0';
00928                 dest[0] = ':';
00929         }
00930         return ( ( longest_start == buf ) ? buf : ( buf + 1 ) );
00931 }
00932 
00933 /**
00934  * Transcribe IPv6 address
00935  *
00936  * @v net_addr          IPv6 address
00937  * @ret string          IPv6 address in standard notation
00938  *
00939  */
00940 static const char * ipv6_ntoa ( const void *net_addr ) {
00941         return inet6_ntoa ( net_addr );
00942 }
00943 
00944 /**
00945  * Transcribe IPv6 socket address
00946  *
00947  * @v sa                Socket address
00948  * @ret string          Socket address in standard notation
00949  */
00950 static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) {
00951         static char buf[ 39 /* "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx" */ +
00952                          1 /* "%" */ + NETDEV_NAME_LEN + 1 /* NUL */ ];
00953         struct sockaddr_in6 *sin6 = ( ( struct sockaddr_in6 * ) sa );
00954         struct in6_addr *in = &sin6->sin6_addr;
00955         struct net_device *netdev;
00956         const char *netdev_name;
00957 
00958         /* Identify network device, if applicable */
00959         if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) {
00960                 netdev = find_netdev_by_index ( sin6->sin6_scope_id );
00961                 netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
00962         } else {
00963                 netdev_name = NULL;
00964         }
00965 
00966         /* Format socket address */
00967         snprintf ( buf, sizeof ( buf ), "%s%s%s", inet6_ntoa ( in ),
00968                    ( netdev_name ? "%" : "" ),
00969                    ( netdev_name ? netdev_name : "" ) );
00970         return buf;
00971 }
00972 
00973 /**
00974  * Parse IPv6 socket address
00975  *
00976  * @v string            Socket address string
00977  * @v sa                Socket address to fill in
00978  * @ret rc              Return status code
00979  */
00980 static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
00981         struct sockaddr_in6 *sin6 = ( ( struct sockaddr_in6 * ) sa );
00982         struct in6_addr in;
00983         struct net_device *netdev;
00984         size_t len;
00985         char *tmp;
00986         char *in_string;
00987         char *netdev_string;
00988         int rc;
00989 
00990         /* Create modifiable copy of string */
00991         tmp = strdup ( string );
00992         if ( ! tmp ) {
00993                 rc = -ENOMEM;
00994                 goto err_alloc;
00995         }
00996         in_string = tmp;
00997 
00998         /* Strip surrounding "[...]", if present */
00999         len = strlen ( in_string );
01000         if ( ( in_string[0] == '[' ) && ( in_string[ len - 1 ] == ']' ) ) {
01001                 in_string[ len - 1 ] = '\0';
01002                 in_string++;
01003         }
01004 
01005         /* Split at network device name, if present */
01006         netdev_string = strchr ( in_string, '%' );
01007         if ( netdev_string )
01008                 *(netdev_string++) = '\0';
01009 
01010         /* Parse IPv6 address portion */
01011         if ( ( rc = inet6_aton ( in_string, &in ) ) != 0 )
01012                 goto err_inet6_aton;
01013 
01014         /* Parse scope ID, if applicable */
01015         if ( netdev_string ) {
01016 
01017                 /* Parse explicit network device name, if present */
01018                 netdev = find_netdev ( netdev_string );
01019                 if ( ! netdev ) {
01020                         rc = -ENODEV;
01021                         goto err_find_netdev;
01022                 }
01023                 sin6->sin6_scope_id = netdev->index;
01024 
01025         } else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) ||
01026                     IN6_IS_ADDR_MULTICAST ( &in ) ) {
01027 
01028                 /* If no network device is explicitly specified for a
01029                  * link-local or multicast address, default to using
01030                  * "netX" (if existent).
01031                  */
01032                 netdev = last_opened_netdev();
01033                 if ( netdev )
01034                         sin6->sin6_scope_id = netdev->index;
01035         }
01036 
01037         /* Copy IPv6 address portion to socket address */
01038         memcpy ( &sin6->sin6_addr, &in, sizeof ( sin6->sin6_addr ) );
01039 
01040  err_find_netdev:
01041  err_inet6_aton:
01042         free ( tmp );
01043  err_alloc:
01044         return rc;
01045 }
01046 
01047 /** IPv6 protocol */
01048 struct net_protocol ipv6_protocol __net_protocol = {
01049         .name = "IPv6",
01050         .net_proto = htons ( ETH_P_IPV6 ),
01051         .net_addr_len = sizeof ( struct in6_addr ),
01052         .rx = ipv6_rx,
01053         .ntoa = ipv6_ntoa,
01054 };
01055 
01056 /** IPv6 TCPIP net protocol */
01057 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
01058         .name = "IPv6",
01059         .sa_family = AF_INET6,
01060         .header_len = sizeof ( struct ipv6_header ),
01061         .net_protocol = &ipv6_protocol,
01062         .tx = ipv6_tx,
01063         .netdev = ipv6_netdev,
01064 };
01065 
01066 /** IPv6 socket address converter */
01067 struct sockaddr_converter ipv6_sockaddr_converter __sockaddr_converter = {
01068         .family = AF_INET6,
01069         .ntoa = ipv6_sock_ntoa,
01070         .aton = ipv6_sock_aton,
01071 };
01072 
01073 /**
01074  * Parse IPv6 address setting value
01075  *
01076  * @v type              Setting type
01077  * @v value             Formatted setting value
01078  * @v buf               Buffer to contain raw value
01079  * @v len               Length of buffer
01080  * @ret len             Length of raw value, or negative error
01081  */
01082 int parse_ipv6_setting ( const struct setting_type *type __unused,
01083                          const char *value, void *buf, size_t len ) {
01084         struct in6_addr ipv6;
01085         int rc;
01086 
01087         /* Parse IPv6 address */
01088         if ( ( rc = inet6_aton ( value, &ipv6 ) ) != 0 )
01089                 return rc;
01090 
01091         /* Copy to buffer */
01092         if ( len > sizeof ( ipv6 ) )
01093                 len = sizeof ( ipv6 );
01094         memcpy ( buf, &ipv6, len );
01095 
01096         return ( sizeof ( ipv6 ) );
01097 }
01098 
01099 /**
01100  * Format IPv6 address setting value
01101  *
01102  * @v type              Setting type
01103  * @v raw               Raw setting value
01104  * @v raw_len           Length of raw setting value
01105  * @v buf               Buffer to contain formatted value
01106  * @v len               Length of buffer
01107  * @ret len             Length of formatted value, or negative error
01108  */
01109 int format_ipv6_setting ( const struct setting_type *type __unused,
01110                           const void *raw, size_t raw_len, char *buf,
01111                           size_t len ) {
01112         const struct in6_addr *ipv6 = raw;
01113 
01114         if ( raw_len < sizeof ( *ipv6 ) )
01115                 return -EINVAL;
01116         return snprintf ( buf, len, "%s", inet6_ntoa ( ipv6 ) );
01117 }
01118 
01119 /** IPv6 settings scope */
01120 const struct settings_scope ipv6_settings_scope;
01121 
01122 /** IPv6 address setting */
01123 const struct setting ip6_setting __setting ( SETTING_IP6, ip6 ) = {
01124         .name = "ip6",
01125         .description = "IPv6 address",
01126         .type = &setting_type_ipv6,
01127         .scope = &ipv6_settings_scope,
01128 };
01129 
01130 /** IPv6 prefix length setting */
01131 const struct setting len6_setting __setting ( SETTING_IP6, len6 ) = {
01132         .name = "len6",
01133         .description = "IPv6 prefix length",
01134         .type = &setting_type_int8,
01135         .scope = &ipv6_settings_scope,
01136 };
01137 
01138 /** Default gateway setting */
01139 const struct setting gateway6_setting __setting ( SETTING_IP6, gateway6 ) = {
01140         .name = "gateway6",
01141         .description = "IPv6 gateway",
01142         .type = &setting_type_ipv6,
01143         .scope = &ipv6_settings_scope,
01144 };
01145 
01146 /**
01147  * Check applicability of IPv6 link-local address setting
01148  *
01149  * @v settings          Settings block
01150  * @v setting           Setting to fetch
01151  * @ret applies         Setting applies within this settings block
01152  */
01153 static int ipv6_applies ( struct settings *settings __unused,
01154                           const struct setting *setting ) {
01155 
01156         return ( setting->scope == &ipv6_settings_scope );
01157 }
01158 
01159 /**
01160  * Fetch IPv6 link-local address setting
01161  *
01162  * @v settings          Settings block
01163  * @v setting           Setting to fetch
01164  * @v data              Buffer to fill with setting data
01165  * @v len               Length of buffer
01166  * @ret len             Length of setting data, or negative error
01167  */
01168 static int ipv6_fetch ( struct settings *settings, struct setting *setting,
01169                         void *data, size_t len ) {
01170         struct net_device *netdev =
01171                 container_of ( settings->parent, struct net_device,
01172                                settings.settings );
01173         struct in6_addr ip6;
01174         uint8_t *len6;
01175         int prefix_len;
01176         int rc;
01177 
01178         /* Construct link-local address from EUI-64 as per RFC 2464 */
01179         memset ( &ip6, 0, sizeof ( ip6 ) );
01180         prefix_len = ipv6_link_local ( &ip6, netdev );
01181         if ( prefix_len < 0 ) {
01182                 rc = prefix_len;
01183                 return rc;
01184         }
01185 
01186         /* Handle setting */
01187         if ( setting_cmp ( setting, &ip6_setting ) == 0 ) {
01188 
01189                 /* Return link-local ip6 */
01190                 if ( len > sizeof ( ip6 ) )
01191                         len = sizeof ( ip6 );
01192                 memcpy ( data, &ip6, len );
01193                 return sizeof ( ip6 );
01194 
01195         } else if ( setting_cmp ( setting, &len6_setting ) == 0 ) {
01196 
01197                 /* Return prefix length */
01198                 if ( len ) {
01199                         len6 = data;
01200                         *len6 = prefix_len;
01201                 }
01202                 return sizeof ( *len6 );
01203 
01204         }
01205 
01206         return -ENOENT;
01207 }
01208 
01209 /** IPv6 link-local address settings operations */
01210 static struct settings_operations ipv6_settings_operations = {
01211         .applies = ipv6_applies,
01212         .fetch = ipv6_fetch,
01213 };
01214 
01215 /** IPv6 link-local address settings */
01216 struct ipv6_settings {
01217         /** Reference counter */
01218         struct refcnt refcnt;
01219         /** Settings interface */
01220         struct settings settings;
01221 };
01222 
01223 /**
01224  * Register IPv6 link-local address settings
01225  *
01226  * @v netdev            Network device
01227  * @ret rc              Return status code
01228  */
01229 static int ipv6_register_settings ( struct net_device *netdev ) {
01230         struct settings *parent = netdev_settings ( netdev );
01231         struct ipv6_settings *ipv6set;
01232         int rc;
01233 
01234         /* Allocate and initialise structure */
01235         ipv6set = zalloc ( sizeof ( *ipv6set ) );
01236         if ( ! ipv6set ) {
01237                 rc = -ENOMEM;
01238                 goto err_alloc;
01239         }
01240         ref_init ( &ipv6set->refcnt, NULL );
01241         settings_init ( &ipv6set->settings, &ipv6_settings_operations,
01242                         &ipv6set->refcnt, &ipv6_settings_scope );
01243         ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL;
01244 
01245         /* Register settings */
01246         if ( ( rc = register_settings ( &ipv6set->settings, parent,
01247                                         IPV6_SETTINGS_NAME ) ) != 0 )
01248                 goto err_register;
01249 
01250  err_register:
01251         ref_put ( &ipv6set->refcnt );
01252  err_alloc:
01253         return rc;
01254 }
01255 
01256 /** IPv6 network device driver */
01257 struct net_driver ipv6_driver __net_driver = {
01258         .name = "IPv6",
01259         .probe = ipv6_register_settings,
01260 };
01261 
01262 /**
01263  * Create IPv6 routing table based on configured settings
01264  *
01265  * @v netdev            Network device
01266  * @v settings          Settings block
01267  * @ret rc              Return status code
01268  */
01269 static int ipv6_create_routes ( struct net_device *netdev,
01270                                 struct settings *settings ) {
01271         struct settings *child;
01272         struct settings *origin;
01273         struct in6_addr ip6_buf;
01274         struct in6_addr gateway6_buf;
01275         struct in6_addr *ip6 = &ip6_buf;
01276         struct in6_addr *gateway6 = &gateway6_buf;
01277         uint8_t len6;
01278         size_t len;
01279         int rc;
01280 
01281         /* First, create routing table for any child settings.  We do
01282          * this depth-first and in reverse order so that the end
01283          * result reflects the relative priorities of the settings
01284          * blocks.
01285          */
01286         list_for_each_entry_reverse ( child, &settings->children, siblings )
01287                 ipv6_create_routes ( netdev, child );
01288 
01289         /* Fetch IPv6 address, if any */
01290         len = fetch_setting ( settings, &ip6_setting, &origin, NULL,
01291                               ip6, sizeof ( *ip6 ) );
01292         if ( ( len != sizeof ( *ip6 ) ) || ( origin != settings ) )
01293                 return 0;
01294 
01295         /* Fetch prefix length, if defined */
01296         len = fetch_setting ( settings, &len6_setting, &origin, NULL,
01297                               &len6, sizeof ( len6 ) );
01298         if ( ( len != sizeof ( len6 ) ) || ( origin != settings ) )
01299                 len6 = 0;
01300         if ( len6 > IPV6_MAX_PREFIX_LEN )
01301                 len6 = IPV6_MAX_PREFIX_LEN;
01302 
01303         /* Fetch gateway, if defined */
01304         len = fetch_setting ( settings, &gateway6_setting, &origin, NULL,
01305                               gateway6, sizeof ( *gateway6 ) );
01306         if ( ( len != sizeof ( *gateway6 ) ) || ( origin != settings ) )
01307                 gateway6 = NULL;
01308 
01309         /* Create or update route */
01310         if ( ( rc = ipv6_add_miniroute ( netdev, ip6, len6, gateway6 ) ) != 0){
01311                 DBGC ( netdev, "IPv6 %s could not add route: %s\n",
01312                        netdev->name, strerror ( rc ) );
01313                 return rc;
01314         }
01315 
01316         return 0;
01317 }
01318 
01319 /**
01320  * Create IPv6 routing table based on configured settings
01321  *
01322  * @ret rc              Return status code
01323  */
01324 static int ipv6_create_all_routes ( void ) {
01325         struct ipv6_miniroute *miniroute;
01326         struct ipv6_miniroute *tmp;
01327         struct net_device *netdev;
01328         struct settings *settings;
01329         int rc;
01330 
01331         /* Delete all existing routes */
01332         list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list )
01333                 ipv6_del_miniroute ( miniroute );
01334 
01335         /* Create routes for each configured network device */
01336         for_each_netdev ( netdev ) {
01337                 settings = netdev_settings ( netdev );
01338                 if ( ( rc = ipv6_create_routes ( netdev, settings ) ) != 0 )
01339                         return rc;
01340         }
01341 
01342         return 0;
01343 }
01344 
01345 /** IPv6 settings applicator */
01346 struct settings_applicator ipv6_settings_applicator __settings_applicator = {
01347         .apply = ipv6_create_all_routes,
01348 };
01349 
01350 /* Drag in objects via ipv6_protocol */
01351 REQUIRING_SYMBOL ( ipv6_protocol );
01352 
01353 /* Drag in ICMPv6 */
01354 REQUIRE_OBJECT ( icmpv6 );
01355 
01356 /* Drag in NDP */
01357 REQUIRE_OBJECT ( ndp );