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