edae0385228a6579d91dea7f1a6decfe27b60e2b
[people/meteger/gpxe.git] / src / net / ipv6.c
1 #include <errno.h>
2 #include <stdint.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <byteswap.h>
7 #include <gpxe/in.h>
8 #include <gpxe/ip6.h>
9 #include <gpxe/ndp.h>
10 #include <gpxe/list.h>
11 #include <gpxe/icmp6.h>
12 #include <gpxe/tcpip.h>
13 #include <gpxe/socket.h>
14 #include <gpxe/iobuf.h>
15 #include <gpxe/netdevice.h>
16 #include <gpxe/if_ether.h>
17 #include <gpxe/dhcp6.h>
18
19 struct net_protocol ipv6_protocol;
20
21 char * inet6_ntoa ( struct in6_addr in6 );
22
23 /* Unspecified IP6 address */
24 static struct in6_addr ip6_none = {
25         .in6_u.u6_addr32 = { 0,0,0,0 }
26 };
27
28 /** An IPv6 routing table entry */
29 struct ipv6_miniroute {
30         /* List of miniroutes */
31         struct list_head list;
32
33         /* Network device */
34         struct net_device *netdev;
35
36         /* Destination prefix */
37         struct in6_addr prefix;
38         /* Prefix length */
39         int prefix_len;
40         /* IPv6 address of interface */
41         struct in6_addr address;
42         /* Gateway address */
43         struct in6_addr gateway;
44 };
45
46 /** List of IPv6 miniroutes */
47 static LIST_HEAD ( miniroutes );
48
49 /**
50  * Generate an EUI-64 from a given link-local address.
51  *
52  * @v out               pointer to buffer to receive the EUI-64
53  * @v ll                pointer to buffer containing the link-local address
54  */
55 void ipv6_generate_eui64 ( uint8_t *out, uint8_t *ll ) {
56         assert ( out != 0 );
57         assert ( ll != 0 );
58         
59         /* Create an EUI-64 identifier. */
60         memcpy( out, ll, 3 );
61         memcpy( out + 5, ll + 3, 3 );
62         out[3] = 0xFF;
63         out[4] = 0xFE;
64         
65         /* Designate that this is in fact an EUI-64. */
66         out[0] |= 0x2;
67 }
68
69 /**
70  * Verifies that a prefix matches another one.
71  *
72  * @v p1                first prefix
73  * @v p2                second prefix
74  * @v len               prefix length in bits to compare
75  * @ret int             0 if a match, nonzero otherwise
76  */
77 int ipv6_match_prefix ( struct in6_addr *p1, struct in6_addr *p2, size_t len ) {
78         uint8_t ip1, ip2;
79         size_t offset, bits;
80         int rc = 0;
81
82         /* Check for a prefix match on the route. */
83         if ( ! memcmp ( p1, p2, len / 8 ) ) {
84                 rc = 0;
85
86                 /* Handle extra bits in the prefix. */
87                 if ( ( len % 8 ) ||
88                      ( len < 8 ) ) {
89                         DBG ( "ipv6: prefix is not aligned to a byte.\n" );
90
91                         /* Compare the remaining bits. */
92                         offset = len / 8;
93                         bits = len % 8;
94
95                         ip1 = p1->in6_u.u6_addr8[offset];
96                         ip2 = p2->in6_u.u6_addr8[offset];
97                         if ( ! ( ( ip1 & (0xFF >> (8 - bits)) ) &
98                              ( ip2 ) ) ) {
99                                 rc = 1;
100                         }
101                 }
102         } else {
103                 rc = 1;
104         }
105
106         return rc;
107 }
108
109 /**
110  * Add IPv6 minirouting table entry
111  *
112  * @v netdev            Network device
113  * @v prefix            Destination prefix (in bits)
114  * @v address           Address of the interface
115  * @v gateway           Gateway address (or ::0 for no gateway)
116  * @ret miniroute       Routing table entry, or NULL
117  */
118 static struct ipv6_miniroute * __malloc
119 add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
120                      int prefix_len, struct in6_addr address,
121                      struct in6_addr gateway ) {
122         struct ipv6_miniroute *miniroute;
123         
124         DBG( "ipv6 add: %s/%d ", inet6_ntoa ( address ), prefix_len );
125         DBG( "gw %s\n", inet6_ntoa( gateway ) );
126
127         miniroute = malloc ( sizeof ( *miniroute ) );
128         if ( miniroute ) {
129                 /* Record routing information */
130                 miniroute->netdev = netdev_get ( netdev );
131                 miniroute->prefix = prefix;
132                 miniroute->prefix_len = prefix_len;
133                 miniroute->address = address;
134                 miniroute->gateway = gateway;
135
136                 /* Add miniroute to list of miniroutes */
137                 if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
138                         list_add_tail ( &miniroute->list, &miniroutes );
139                 } else {
140                         list_add ( &miniroute->list, &miniroutes );
141                 }
142         }
143
144         return miniroute;
145 }
146
147 /**
148  * Delete IPv6 minirouting table entry
149  *
150  * @v miniroute         Routing table entry
151  */
152 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
153         
154         DBG ( "ipv6 del: %s/%d\n", inet6_ntoa(miniroute->address),
155                                    miniroute->prefix_len );
156         
157         netdev_put ( miniroute->netdev );
158         list_del ( &miniroute->list );
159         free ( miniroute );
160 }
161
162 /**
163  * Add IPv6 interface
164  *
165  * @v netdev    Network device
166  * @v prefix    Destination prefix
167  * @v address   Address of the interface
168  * @v gateway   Gateway address (or ::0 for no gateway)
169  */
170 int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
171                        int prefix_len, struct in6_addr address,
172                        struct in6_addr gateway ) {
173         struct ipv6_miniroute *miniroute;
174
175         /* Clear any existing address for this net device */
176         /* del_ipv6_address ( netdev ); */
177
178         /* Add new miniroute */
179         miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
180                                          gateway );
181         if ( ! miniroute )
182                 return -ENOMEM;
183
184         return 0;
185 }
186
187 /**
188  * Remove IPv6 interface
189  *
190  * @v netdev    Network device
191  */
192 void del_ipv6_address ( struct net_device *netdev ) {
193         struct ipv6_miniroute *miniroute;
194
195         list_for_each_entry ( miniroute, &miniroutes, list ) {
196                 if ( miniroute->netdev == netdev ) {
197                         del_ipv6_miniroute ( miniroute );
198                         break;
199                 }
200         }
201 }
202
203 /**
204  * Calculate TCPIP checksum
205  *
206  * @v iobuf     I/O buffer
207  * @v tcpip     TCP/IP protocol
208  *
209  * This function constructs the pseudo header and completes the checksum in the
210  * upper layer header.
211  */
212 static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
213         struct ip6_header *ip6hdr = iobuf->data;
214         struct ipv6_pseudo_header pshdr;
215
216         /* Calculate pseudo header */
217         memset ( &pshdr, 0, sizeof ( pshdr ) );
218         pshdr.src = ip6hdr->src;
219         pshdr.dest = ip6hdr->dest;
220         pshdr.len = ip6hdr->payload_len;
221         pshdr.nxt_hdr = ip6hdr->nxt_hdr;
222
223         /* Update checksum value */
224         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
225 }
226
227 /**
228  * Dump IP6 header for debugging
229  *
230  * ip6hdr       IPv6 header
231  */
232 void ipv6_dump ( struct ip6_header *ip6hdr ) {
233         /* Because inet6_ntoa returns a static char[16], each call needs to be
234          * separate. */
235         DBG ( "IP6 %p src %s ", ip6hdr, inet6_ntoa( ip6hdr->src ) );
236         DBG ( "dest %s nxt_hdr %d len %d\n", inet6_ntoa ( ip6hdr->dest ),
237                   ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
238 }
239
240 /**
241  * Transmit IP6 packet
242  *
243  * iobuf                I/O buffer
244  * tcpip        TCP/IP protocol
245  * st_dest      Destination socket address
246  *
247  * This function prepends the IPv6 headers to the payload an transmits it.
248  */
249 int ipv6_tx ( struct io_buffer *iobuf,
250               struct tcpip_protocol *tcpip,
251               struct sockaddr_tcpip *st_src __unused,
252               struct sockaddr_tcpip *st_dest,
253               struct net_device *netdev,
254               uint16_t *trans_csum ) {
255         struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
256         struct in6_addr next_hop, gateway = ip6_none;
257         struct ipv6_miniroute *miniroute;
258         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
259         const uint8_t *ll_dest = ll_dest_buf;
260         int rc, multicast, linklocal;
261         
262         /* Check for multicast transmission. */
263         multicast = dest->sin6_addr.in6_u.u6_addr8[0] == 0xFF;
264
265         /* Construct the IPv6 packet */
266         struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
267         memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
268         ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
269         ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
270         ip6hdr->nxt_hdr = tcpip->tcpip_proto;
271         ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
272         
273         /* Determine the next hop address and interface. */
274         next_hop = dest->sin6_addr;
275         list_for_each_entry ( miniroute, &miniroutes, list ) {
276                 /* Check for specific netdev */
277                 if ( netdev && ( miniroute->netdev != netdev ) ) {
278                         continue;
279                 }
280                 
281                 /* Link-local route? */
282                 linklocal = (miniroute->address.in6_u.u6_addr16[0] & htons(0xFE80)) == htons(0xFE80);
283
284                 /* Handle link-local for multicast. */
285                 if ( multicast )
286                 {
287                         /* Link-local scope? */
288                         if ( next_hop.in6_u.u6_addr8[0] & 0x2 ) {
289                                 if ( linklocal ) {
290                                         netdev = miniroute->netdev;
291                                         ip6hdr->src = miniroute->address;
292                                         break;
293                                 } else {
294                                         /* Should be link-local address. */
295                                         continue;
296                                 }
297                         } else {
298                                 /* Can we route on this interface?
299                                    (assume non-link-local means routable) */
300                                 if ( ! linklocal ) {
301                                         netdev = miniroute->netdev;
302                                         ip6hdr->src = miniroute->address;
303                                         break;
304                                 }
305                         }
306                 }
307                 
308                 /* Check for a prefix match on the route. */
309                 rc = ipv6_match_prefix ( &next_hop, &miniroute->prefix, miniroute->prefix_len );
310                 
311                 /* Matched? */
312                 if( rc == 0 ) {
313                         netdev = miniroute->netdev;
314                         ip6hdr->src = miniroute->address;
315                         break;
316                 }
317                 
318                 if ( ( ! ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) &&
319                      ( IP6_EQUAL ( gateway, ip6_none ) ) ) {
320                         netdev = miniroute->netdev;
321                         ip6hdr->src = miniroute->address;
322                         gateway = miniroute->gateway;
323                 }
324         }
325         /* No network interface identified */
326         if ( ( ! netdev ) ) {
327                 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
328                 rc = -ENETUNREACH;
329                 goto err;
330         } else if ( ! IP6_EQUAL ( gateway, ip6_none ) ) {
331                 next_hop = gateway;
332         }
333         
334         /* Add the next hop to the packet. */
335         ip6hdr->dest = dest->sin6_addr;
336
337         /* Complete the transport layer checksum */
338         if ( trans_csum )
339                 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
340
341         /* Print IPv6 header */
342         /* ipv6_dump ( ip6hdr ); */
343
344         /* Resolve link layer address */
345         if ( next_hop.in6_u.u6_addr8[0] == 0xFF ) {
346                 ll_dest_buf[0] = 0x33;
347                 ll_dest_buf[1] = 0x33;
348                 ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
349                 ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
350                 ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
351                 ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
352         } else {
353                 /* Unicast address needs to be resolved by NDP */
354                 if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
355                                           ll_dest_buf ) ) != 0 ) {
356                         DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
357                         goto err;
358                 }
359         }
360
361         /* Transmit packet */
362         return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
363
364   err:
365         free_iob ( iobuf );
366         return rc;
367 }
368
369 /**
370  * Process next IP6 header
371  *
372  * @v iobuf     I/O buffer
373  * @v nxt_hdr   Next header number
374  * @v src       Source socket address
375  * @v dest      Destination socket address
376  * @v netdev    Net device the packet arrived on
377  * @v phcsm Partial checksum over the IPv6 psuedo-header.
378  *
379  * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
380  */
381 static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
382                 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
383                 struct net_device *netdev, uint16_t phcsm ) {
384         switch ( nxt_hdr ) {
385         case IP6_HOPBYHOP:
386         case IP6_ROUTING:
387         case IP6_FRAGMENT:
388         case IP6_AUTHENTICATION:
389         case IP6_DEST_OPTS:
390         case IP6_ESP:
391                 DBG ( "Function not implemented for header %d\n", nxt_hdr );
392                 return -ENOSYS;
393         case IP6_ICMP6:
394                 return icmp6_rx ( iobuf, src, dest, netdev, phcsm );
395         case IP6_NO_HEADER:
396                 DBG ( "No next header\n" );
397                 return 0;
398         }
399         /* Next header is not a IPv6 extension header */
400         return tcpip_rx ( iobuf, nxt_hdr, src, dest, phcsm );
401 }
402
403 /**
404  * Process incoming IP6 packets
405  *
406  * @v iobuf             I/O buffer
407  * @v netdev            Network device
408  * @v ll_source         Link-layer source address
409  *
410  * This function processes a IPv6 packet
411  */
412 static int ipv6_rx ( struct io_buffer *iobuf,
413                      __unused struct net_device *netdev,
414                      __unused const void *ll_source ) {
415         struct ip6_header *ip6hdr = iobuf->data;
416         union {
417                 struct sockaddr_in6 sin6;
418                 struct sockaddr_tcpip st;
419         } src, dest;
420         uint16_t phcsm = 0;
421
422         /* Sanity check */
423         if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
424                 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
425                 goto drop;
426         }
427
428         /* Print IP6 header for debugging */
429         /* ipv6_dump ( ip6hdr ); */
430
431         /* Check header version */
432         if ( ( ntohl( ip6hdr->ver_traffic_class_flow_label ) & 0xf0000000 ) != 0x60000000 ) {
433                 DBG ( "Invalid protocol version\n" );
434                 goto drop;
435         }
436
437         /* Check the payload length */
438         if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
439                 DBG ( "Inconsistent packet length (%d bytes)\n",
440                         ip6hdr->payload_len );
441                 goto drop;
442         }
443
444         /* Ignore the traffic class and flow control values */
445
446         /* Construct socket address */
447         memset ( &src, 0, sizeof ( src ) );
448         src.sin6.sin_family = AF_INET6;
449         src.sin6.sin6_addr = ip6hdr->src;
450         memset ( &dest, 0, sizeof ( dest ) );
451         dest.sin6.sin_family = AF_INET6;
452         dest.sin6.sin6_addr = ip6hdr->dest;
453
454         /* Calculate the psuedo-header checksum before the IP6 header is
455          * stripped away. */
456         phcsm = ipv6_tx_csum ( iobuf, 0 );
457
458         /* Strip header */
459         iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
460                                                         sizeof ( *ip6hdr ) );
461         iob_pull ( iobuf, sizeof ( *ip6hdr ) );
462
463         /* Send it to the transport layer */
464         return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st,
465                                       netdev, phcsm );
466
467   drop:
468         DBG ( "IP6 packet dropped\n" );
469         free_iob ( iobuf );
470         return -1;
471 }
472
473 /**
474  * Convert an IPv6 address to a string.
475  *
476  * @v in6   Address to convert to string.
477  *
478  * Converts an IPv6 address to a string, and applies zero-compression as needed
479  * to condense the address for easier reading/typing.
480  */
481 char * inet6_ntoa ( struct in6_addr in6 ) {
482         static char buf[40];
483         uint16_t *bytes = ( uint16_t* ) &in6;
484         size_t i = 0, longest = 0, tmp = 0, long_idx = ~0;
485         
486         /* ::0 */
487         if ( IP6_EQUAL ( in6, ip6_none ) ) {
488                 tmp = sprintf ( buf, "::0" );
489                 buf[tmp] = 0;
490                 return buf;
491         }
492
493         /* Determine the longest string of zeroes for zero-compression. */
494         for ( ; i < 8; i++ ) {
495                 if ( !bytes[i] )
496                         tmp++;
497                 else if(tmp > longest) {
498                         longest = tmp;
499                         long_idx = i - longest;
500                         
501                         tmp = 0;
502                 }
503         }
504         
505         /* Check for last word being zero. This will cause long_idx to be zero,
506          * which confuses the actual buffer fill code. */
507         if(tmp && (tmp > longest)) {
508                 longest = tmp;
509                 long_idx = 8 - longest;
510         }
511
512         /* Inject into the buffer. */
513         tmp = 0;
514         for ( i = 0; i < 8; i++ ) {
515                 /* Should we skip over a string of zeroes? */
516                 if ( i == long_idx ) {
517                         i += longest;
518                         tmp += sprintf( buf + tmp, ":" );
519
520                         /* Handle end-of-string. */
521                         if(i > 7)
522                                 break;
523                 }
524
525                 /* Insert this component of the address. */
526                 tmp += sprintf(buf + tmp, "%x", ntohs(bytes[i]));
527
528                 /* Add the next colon, if needed. */
529                 if ( i < 7 )
530                         tmp += sprintf( buf + tmp, ":" );
531         }
532
533         buf[tmp] = 0;
534
535         return buf;
536 }
537
538 /**
539  * Convert a string to an IPv6 address.
540  *
541  * @v in6   String to convert to an address.
542  */
543 int inet6_aton ( const char *cp, struct in6_addr *inp ) {
544         char convbuf[40];
545         char *tmp = convbuf, *next = convbuf;
546         size_t i = 0;
547         int ok;
548         char c;
549         
550         /* Verify a valid address. */
551         while ( ( c = cp[i++] ) ) {
552                 ok = c == ':';
553                 ok = ok || ( ( c >= '0' ) && ( c <= '9' ) );
554                 ok = ok || ( ( c >= 'a' ) && ( c <= 'f' ) );
555                 ok = ok || ( ( c >= 'A' ) && ( c <= 'F' ) );
556                 
557                 if ( ! ok ) {
558                         return 0;
559                 }
560         }
561         if ( ! i ) {
562                 return 0;
563         }
564         
565         strcpy ( convbuf, cp );
566         
567         DBG ( "ipv6 converting %s to an in6_addr\n", cp );
568         
569         /* Handle the first part of the address (or all of it if no zero-compression. */
570         i = 0;
571         while ( ( next = strchr ( next, ':' ) ) ) {
572                 /* Cater for zero-compression. */
573                 if ( *tmp == ':' )
574                         break;
575                 
576                 /* Convert to integer. */
577                 inp->s6_addr16[i++] = htons( strtoul ( tmp, 0, 16 ) );
578                 
579                 *next++ = 0;
580                 tmp = next;
581         }
582         
583         /* Handle the case where no zero-compression is needed, but every word
584          * was filled in the address. */
585         if ( ( i == 7 ) && ( *tmp != ':' ) ) {
586                 inp->s6_addr16[i++] = htons( strtoul ( tmp, 0, 16 ) );
587         }
588         else
589         {
590                 /* Handle zero-compression now (go backwards). */
591                 i = 7;
592                 if ( i && ( *tmp == ':' ) ) {
593                         next = strrchr ( next, ':' );
594                         do
595                         {
596                                 tmp = next + 1;
597                                 *next-- = 0;
598         
599                                 /* Convert to integer. */
600                                 inp->s6_addr16[i--] = htons( strtoul ( tmp, 0, 16 ) );
601                         } while ( ( next = strrchr ( next, ':' ) ) );
602                 }
603         }
604         
605         return 1;
606 }
607
608 static const char * ipv6_ntoa ( const void *net_addr ) {
609         return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
610 }
611
612 static int ipv6_check ( struct net_device *netdev, const void *net_addr ) {
613         const struct in6_addr *address = net_addr;
614         struct ipv6_miniroute *miniroute;
615
616         list_for_each_entry ( miniroute, &miniroutes, list ) {
617                 if ( ( miniroute->netdev == netdev ) &&
618                      ( ! memcmp ( &miniroute->address, address, 16 ) ) ) {
619                         /* Found matching address */
620                         return 0;
621                 }
622         }
623         return -ENOENT;
624 }
625
626 /** IPv6 protocol */
627 struct net_protocol ipv6_protocol __net_protocol = {
628         .name = "IPV6",
629         .net_proto = htons ( ETH_P_IPV6 ),
630         .net_addr_len = sizeof ( struct in6_addr ),
631         .rx = ipv6_rx,
632         .ntoa = ipv6_ntoa,
633 };
634
635 /** IPv6 TCPIP net protocol */
636 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
637         .name = "IPv6",
638         .sa_family = AF_INET6,
639         .tx = ipv6_tx,
640 };
641
642 /** IPv6 ICMPv6 protocol, for NDP */
643 struct icmp6_net_protocol ipv6_icmp6_protocol __icmp6_net_protocol = {
644         .net_protocol = &ipv6_protocol,
645         .check = ipv6_check,
646 };
647
648
649
650 /******************************************************************************
651  *
652  * Settings
653  *
654  ******************************************************************************
655  */
656
657 /** IPv6 address setting */
658 struct setting ip6_setting __setting = {
659         .name = "ip6",
660         .description = "IPv6 address",
661         .tag = DHCP6_OPT_IAADDR,
662         .type = &setting_type_ipv6,
663 };
664
665 /** IPv6 prefix setting */
666 struct setting prefix_setting __setting = {
667         .name = "prefix",
668         .description = "IPv6 address prefix length",
669         .tag = 0,
670         .type = &setting_type_int16,
671 };
672
673 /** Default IPv6 gateway setting */
674 struct setting gateway6_setting __setting = {
675         .name = "gateway6",
676         .description = "IPv6 Default gateway",
677         .tag = 0,
678         .type = &setting_type_ipv4,
679 };
680
681 /**
682  * Create IPv6 routes based on configured settings.
683  *
684  * @ret rc              Return status code
685  */
686 static int ipv6_create_routes ( void ) {
687         struct ipv6_miniroute *miniroute;
688         struct ipv6_miniroute *tmp;
689         struct net_device *netdev;
690         struct settings *settings;
691         struct in6_addr address;
692         struct in6_addr gateway;
693         long prefix = 0;
694         int rc = 0;
695         
696         /* Create a route for each configured network device */
697         for_each_netdev ( netdev ) {
698                 settings = netdev_settings ( netdev );
699         
700                 /* Read the settings first. We may need to clear routes. */
701                 fetch_ipv6_setting ( settings, &ip6_setting, &address );
702                 fetch_ipv6_setting ( settings, &gateway6_setting, &gateway );
703                 fetch_int_setting ( settings, &prefix_setting, &prefix );
704         
705                 /* Sanity check! */
706                 if ( ( prefix == 0 ) || ( prefix > 128 ) ) {
707                         DBG ( "dhcp6: attempt to apply settings without a valid prefix, ignoring\n" );
708                         return -EINVAL;
709                 }
710         
711                 /* Remove any existing routes for this address. */
712                 list_for_each_entry_safe ( miniroute, tmp, &miniroutes, list ) {
713                         if ( ipv6_match_prefix ( &address,
714                                                  &miniroute->prefix,
715                                                  prefix ) ) {
716                                 DBG ( "dhcp6: existing route for a configured setting, deleting\n" );
717                                 del_ipv6_miniroute ( miniroute );
718                         }
719                 }
720                 
721                 /* Configure route */
722                 rc = add_ipv6_address ( netdev, address, prefix,
723                                                address, gateway );
724                 if ( ! rc )
725                         return rc;
726         }
727         
728         return 0;
729 }
730
731 /** IPv6 settings applicator */
732 struct settings_applicator ipv6_settings_applicator __settings_applicator = {
733         .apply = ipv6_create_routes,
734 };
735
736 /* Drag in ICMP6 and DHCP6 */
737 REQUIRE_OBJECT ( icmpv6 );
738 REQUIRE_OBJECT ( dhcp6 );