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/retry.h>
18 #include <gpxe/timer.h>
19 #include <gpxe/dhcp6.h>
21 struct net_protocol ipv6_protocol;
23 #define is_linklocal( a ) ( ( (a).in6_u.u6_addr16[0] & htons ( 0xFE80 ) ) == htons ( 0xFE80 ) )
25 #define is_ext_hdr( nxt_hdr ) ( \
26 ( nxt_hdr == IP6_HOPBYHOP ) || \
27 ( nxt_hdr == IP6_PAD ) || \
28 ( nxt_hdr == IP6_PADN ) || \
29 ( nxt_hdr == IP6_ROUTING ) || \
30 ( nxt_hdr == IP6_FRAGMENT ) || \
31 ( nxt_hdr == IP6_AUTHENTICATION ) || \
32 ( nxt_hdr == IP6_DEST_OPTS ) || \
33 ( nxt_hdr == IP6_ESP ) || \
34 ( nxt_hdr == IP6_NO_HEADER ) )
36 char * inet6_ntoa ( struct in6_addr in6 );
38 static int ipv6_check ( struct net_device *netdev, const void *net_addr );
40 /* Unspecified IP6 address */
41 static struct in6_addr ip6_none = {
42 .in6_u.u6_addr32 = { 0,0,0,0 }
45 /** An IPv6 routing table entry */
46 struct ipv6_miniroute {
47 /* List of miniroutes */
48 struct list_head list;
51 struct net_device *netdev;
53 /* Destination prefix */
54 struct in6_addr prefix;
57 /* IPv6 address of interface */
58 struct in6_addr address;
60 struct in6_addr gateway;
63 /** List of IPv6 miniroutes */
64 static LIST_HEAD ( miniroutes );
66 /** List of fragment reassembly buffers */
67 static LIST_HEAD ( frag_buffers );
70 * Generate an EUI-64 from a given link-local address.
72 * @v out pointer to buffer to receive the EUI-64
73 * @v ll pointer to buffer containing the link-local address
75 void ipv6_generate_eui64 ( uint8_t *out, uint8_t *ll ) {
79 /* Create an EUI-64 identifier. */
81 memcpy( out + 5, ll + 3, 3 );
85 /* Designate that this is in fact an EUI-64. */
90 * Verifies that a prefix matches another one.
94 * @v len prefix length in bits to compare
95 * @ret int 0 if a match, nonzero otherwise
97 int ipv6_match_prefix ( struct in6_addr *p1, struct in6_addr *p2, size_t len ) {
102 /* Check for a prefix match on the route. */
103 if ( ! memcmp ( p1, p2, len / 8 ) ) {
106 /* Handle extra bits in the prefix. */
109 DBG ( "ipv6: prefix is not aligned to a byte.\n" );
111 /* Compare the remaining bits. */
115 ip1 = p1->in6_u.u6_addr8[offset];
116 ip2 = p2->in6_u.u6_addr8[offset];
117 if ( ! ( ( ip1 & (0xFF >> (8 - bits)) ) &
130 * Add IPv6 minirouting table entry
132 * @v netdev Network device
133 * @v prefix Destination prefix (in bits)
134 * @v address Address of the interface
135 * @v gateway Gateway address (or ::0 for no gateway)
136 * @ret miniroute Routing table entry, or NULL
138 static struct ipv6_miniroute * __malloc
139 add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
140 int prefix_len, struct in6_addr address,
141 struct in6_addr gateway ) {
142 struct ipv6_miniroute *miniroute;
144 DBG( "ipv6 add: %s/%d ", inet6_ntoa ( address ), prefix_len );
145 DBG( "gw %s\n", inet6_ntoa( gateway ) );
147 miniroute = malloc ( sizeof ( *miniroute ) );
149 /* Record routing information */
150 miniroute->netdev = netdev_get ( netdev );
151 miniroute->prefix = prefix;
152 miniroute->prefix_len = prefix_len;
153 miniroute->address = address;
154 miniroute->gateway = gateway;
156 /* Add miniroute to list of miniroutes */
157 if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
158 list_add_tail ( &miniroute->list, &miniroutes );
160 list_add ( &miniroute->list, &miniroutes );
168 * Delete IPv6 minirouting table entry
170 * @v miniroute Routing table entry
172 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
174 DBG ( "ipv6 del: %s/%d\n", inet6_ntoa(miniroute->address),
175 miniroute->prefix_len );
177 netdev_put ( miniroute->netdev );
178 list_del ( &miniroute->list );
185 * @v netdev Network device
186 * @v prefix Destination prefix
187 * @v address Address of the interface
188 * @v gateway Gateway address (or ::0 for no gateway)
190 int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
191 int prefix_len, struct in6_addr address,
192 struct in6_addr gateway ) {
193 struct ipv6_miniroute *miniroute;
195 /* Clear any existing address for this net device */
196 /* del_ipv6_address ( netdev ); */
198 /* Add new miniroute */
199 miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
208 * Remove IPv6 interface
210 * @v netdev Network device
212 void del_ipv6_address ( struct net_device *netdev ) {
213 struct ipv6_miniroute *miniroute;
215 list_for_each_entry ( miniroute, &miniroutes, list ) {
216 if ( miniroute->netdev == netdev ) {
217 del_ipv6_miniroute ( miniroute );
224 * Calculate TX checksum
226 * @v iobuf I/O buffer
227 * @v csum Partial checksum.
229 * This function constructs the pseudo header and completes the checksum in the
230 * upper layer header.
232 static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
233 struct ip6_header *ip6hdr = iobuf->data;
234 struct ipv6_pseudo_header pshdr;
236 /* Calculate pseudo header */
237 memset ( &pshdr, 0, sizeof ( pshdr ) );
238 pshdr.src = ip6hdr->src;
239 pshdr.dest = ip6hdr->dest;
240 pshdr.len = ip6hdr->payload_len;
241 pshdr.nxt_hdr = ip6hdr->nxt_hdr;
243 /* Update checksum value */
244 return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
248 * Calculate TCPIP checksum with the given values
250 * @v csum Partial checksum.
251 * @v next_hdr Next header in the packet.
252 * @v length Total data length, in host byte order.
253 * @v src Source address of the packet.
254 * @v dst Destination address of the packet.
256 * This function constructs the pseudo header and completes the checksum in the
257 * upper layer header. It is to be used where an IP6 header is not available, or
258 * fully valid, such as after fragment reassembly.
260 static uint16_t ipv6_tx_csum_nohdr ( uint16_t csum, uint8_t next_hdr, uint16_t length,
261 struct in6_addr *src, struct in6_addr *dst ) {
262 struct ipv6_pseudo_header pshdr;
264 /* Calculate pseudo header */
265 memset ( &pshdr, 0, sizeof ( pshdr ) );
268 pshdr.len = htons ( length );
269 pshdr.nxt_hdr = next_hdr;
271 /* Update checksum value */
272 return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
276 * Dump IP6 header for debugging
280 void ipv6_dump ( struct ip6_header *ip6hdr ) {
281 /* Because inet6_ntoa returns a static char[16], each call needs to be
283 DBG ( "IP6 %p src %s ", ip6hdr, inet6_ntoa( ip6hdr->src ) );
284 DBG ( "dest %s nxt_hdr %d len %d\n", inet6_ntoa ( ip6hdr->dest ),
285 ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
289 * Transmit IP6 packet
292 * tcpip TCP/IP protocol
293 * st_dest Destination socket address
295 * This function prepends the IPv6 headers to the payload an transmits it.
297 int ipv6_tx ( struct io_buffer *iobuf,
298 struct tcpip_protocol *tcpip,
299 struct sockaddr_tcpip *st_src __unused,
300 struct sockaddr_tcpip *st_dest,
301 struct net_device *netdev,
302 uint16_t *trans_csum ) {
303 struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
304 struct in6_addr next_hop, gateway = ip6_none;
305 struct ipv6_miniroute *miniroute;
306 uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
307 const uint8_t *ll_dest = ll_dest_buf;
308 int rc, multicast, linklocal;
310 /* Check for multicast transmission. */
311 multicast = dest->sin6_addr.in6_u.u6_addr8[0] == 0xFF;
313 /* Construct the IPv6 packet */
314 struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
315 memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
316 ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
317 ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
318 ip6hdr->nxt_hdr = tcpip->tcpip_proto;
319 ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
321 /* Determine the next hop address and interface. */
322 next_hop = dest->sin6_addr;
323 list_for_each_entry ( miniroute, &miniroutes, list ) {
324 /* Check for specific netdev */
325 if ( netdev && ( miniroute->netdev != netdev ) ) {
329 /* Link-local route? */
330 linklocal = is_linklocal ( miniroute->address ); // (.in6_u.u6_addr16[0] & htons(0xFE80)) == htons(0xFE80);
332 /* Handle link-local for multicast. */
335 /* Link-local scope? */
336 if ( is_linklocal ( next_hop ) ) { // .in6_u.u6_addr8[0] & 0x2 ) {
338 netdev = miniroute->netdev;
339 ip6hdr->src = miniroute->address;
342 /* Should be link-local address. */
346 /* Assume we can TX on this interface, even if
347 * it is link-local. For multicast this should
348 * not be too much of a problem. */
349 netdev = miniroute->netdev;
350 ip6hdr->src = miniroute->address;
355 /* Check for a prefix match on the route. */
356 rc = ipv6_match_prefix ( &next_hop, &miniroute->prefix, miniroute->prefix_len );
360 netdev = miniroute->netdev;
361 ip6hdr->src = miniroute->address;
365 if ( ( ! ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) &&
366 ( IP6_EQUAL ( gateway, ip6_none ) ) ) {
367 netdev = miniroute->netdev;
368 ip6hdr->src = miniroute->address;
369 gateway = miniroute->gateway;
372 /* No network interface identified */
373 if ( ( ! netdev ) ) {
374 DBG ( "No route to host %s\n", inet6_ntoa ( dest->sin6_addr ) );
377 } else if ( ! IP6_EQUAL ( gateway, ip6_none ) ) {
381 /* Add the next hop to the packet. */
382 ip6hdr->dest = dest->sin6_addr;
384 /* Complete the transport layer checksum */
386 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
388 /* Print IPv6 header */
389 /* ipv6_dump ( ip6hdr ); */
391 /* Resolve link layer address */
392 if ( next_hop.in6_u.u6_addr8[0] == 0xFF ) {
393 ll_dest_buf[0] = 0x33;
394 ll_dest_buf[1] = 0x33;
395 ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
396 ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
397 ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
398 ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
400 /* Unicast address needs to be resolved by NDP */
401 if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
402 ll_dest_buf ) ) != 0 ) {
403 DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
408 /* Transmit packet */
409 return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
417 * Fragment reassembly counter timeout
419 * @v timer Retry timer
420 * @v over If asserted, the timer is greater than @c MAX_TIMEOUT
422 static void ipv6_frag_expired ( struct retry_timer *timer __unused,
425 DBG ( "Fragment reassembly timeout" );
426 /* Free the fragment buffer */
431 * Free fragment buffer
433 * @v fragbug Fragment buffer
435 static void free_fragbuf ( struct frag_buffer *fragbuf ) {
440 * Get the "next header" field and free a fragment buffer for a given iobuf.
442 * @v iobuf I/O buffer to reference.
444 static int frag_next_hdr ( struct io_buffer *iobuf ) {
445 struct frag_buffer *fragbuf;
446 int rc = IP6_NO_HEADER;
448 list_for_each_entry ( fragbuf, &frag_buffers, list ) {
449 if ( fragbuf->frag_iob == iobuf ) {
450 rc = fragbuf->next_hdr;
451 free_fragbuf ( fragbuf );
459 * Fragment reassembler
461 * @v iobuf I/O buffer, fragment of the datagram
462 * @ret frag_iob Reassembled packet, or NULL
464 static struct io_buffer * ipv6_reassemble ( struct io_buffer * iobuf,
465 struct sockaddr_tcpip *st_src ) {
466 struct ip6_frag_hdr *frag_hdr = iobuf->data;
467 struct frag_buffer *fragbuf;
469 struct sockaddr_in6 *src = ( struct sockaddr_in6 * ) st_src;
471 /* Lift the flags and offset out. */
472 uint16_t offset = ntohs ( frag_hdr->offset_flags ) & ~0x7;
473 uint16_t flags = ntohs ( frag_hdr->offset_flags );
476 * Check if the fragment belongs to any fragment series
478 list_for_each_entry ( fragbuf, &frag_buffers, list ) {
479 if ( fragbuf->ident == frag_hdr->ident &&
480 IP6_EQUAL( fragbuf->src.s6_addr, src->sin6_addr ) ) {
482 * Check if the packet is the expected fragment
484 * The offset of the new packet must be equal to the
485 * length of the data accumulated so far (the length of
486 * the reassembled I/O buffer
488 if ( iob_len ( fragbuf->frag_iob ) == offset ) {
490 * Append the contents of the fragment to the
491 * reassembled I/O buffer
493 iob_pull ( iobuf, sizeof ( *frag_hdr ) );
494 memcpy ( iob_put ( fragbuf->frag_iob,
496 iobuf->data, iob_len ( iobuf ) );
499 /** Check if the fragment series is over */
500 if ( ! ( flags & IP6_MORE_FRAGMENTS ) ) {
501 iobuf = fragbuf->frag_iob;
506 /* Discard the fragment series */
507 free_fragbuf ( fragbuf );
514 /** Check if the fragment is the first in the fragment series */
515 if ( ( flags & IP6_MORE_FRAGMENTS ) && ( offset == 0 ) ) {
517 /** Create a new fragment buffer */
518 fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) );
519 fragbuf->ident = frag_hdr->ident;
520 fragbuf->src = src->sin6_addr;
521 fragbuf->next_hdr = frag_hdr->next_hdr;
523 /* Set up the reassembly I/O buffer */
524 fragbuf->frag_iob = alloc_iob ( IP6_FRAG_IOB_SIZE );
525 iob_pull ( iobuf, sizeof ( *frag_hdr ) );
526 memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ),
527 iobuf->data, iob_len ( iobuf ) );
530 /* Set the reassembly timer */
531 timer_init ( &fragbuf->frag_timer, ipv6_frag_expired );
532 start_timer_fixed ( &fragbuf->frag_timer, IP6_FRAG_TIMEOUT );
534 /* Add the fragment buffer to the list of fragment buffers */
535 list_add ( &fragbuf->list, &frag_buffers );
542 * Process next IP6 header
544 * @v iobuf I/O buffer
545 * @v nxt_hdr Next header number
546 * @v src Source socket address
547 * @v dest Destination socket address
548 * @v netdev Net device the packet arrived on
549 * @v phcsm Partial checksum over the IPv6 psuedo-header.
551 * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
553 static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
554 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
555 struct net_device *netdev, uint16_t phcsm ) {
556 struct io_buffer *reassembled;
557 struct sockaddr_in6 *src_in = ( struct sockaddr_in6 * ) src;
558 struct sockaddr_in6 *dest_in = ( struct sockaddr_in6 * ) dest;
560 /* Special handling for fragments - to avoid having to recursively call
561 * this function in order to handle the packet. */
562 if ( nxt_hdr == IP6_FRAGMENT ) {
563 reassembled = ipv6_reassemble ( iobuf, src );
565 /* Reassembled, pass to upper layer. */
566 nxt_hdr = frag_next_hdr ( reassembled );
568 if ( nxt_hdr == IP6_FRAGMENT ) {
569 DBG ( "ip6: recursive fragment, dropping\n" );
573 /* Update the checksum. */
574 phcsm = ipv6_tx_csum_nohdr ( TCPIP_EMPTY_CSUM,
575 nxt_hdr, iob_len ( reassembled ),
576 &src_in->sin6_addr, &dest_in->sin6_addr );
585 return 0; /* Padding options. */
587 case IP6_AUTHENTICATION:
588 /* Handle authentication. */
590 /* Handle an encapsulated security payload. */
591 DBG ( "Function not implemented for header %d\n", nxt_hdr );
594 /* Can ignore these. */
598 DBG ( "ip6: ignoring header %d\n", nxt_hdr );
601 DBG ( "No next header\n" );
604 return icmp6_rx ( iobuf, src, dest, netdev, phcsm );
606 /* Next header is not a IPv6 extension header */
607 return tcpip_rx ( iobuf, nxt_hdr, src, dest, phcsm );
611 * Iterate over IP6 headers, processing each one.
613 * @v iobuf I/O buffer
614 * @v src Source socket address
615 * @v dest Destination socket address
616 * @v netdev Net device the packet arrived on
617 * @v phcsm Partial checksum over the IPv6 psuedo-header.
619 static int ipv6_process_headers ( struct io_buffer *iobuf, uint8_t nxt_hdr,
620 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest,
621 struct net_device *netdev, uint16_t phcsm ) {
622 struct ip6_opt_hdr *opt = iobuf->data;
625 /* Handle packets without options. */
626 if ( ! is_ext_hdr ( nxt_hdr ) ) {
627 return ipv6_process_nxt_hdr ( iobuf, nxt_hdr, src, dest,
631 /* Hop by hop header has a special indicator in nxt_hdr, that matches
632 * PAD and PADn options. So we special-case it. */
633 if ( nxt_hdr == IP6_HOPBYHOP_FIRST ) {
634 nxt_hdr = IP6_HOPBYHOP;
637 /* Iterate over the option list. */
638 while ( iob_len ( iobuf ) ) {
639 flags = nxt_hdr >> 6;
641 DBG ( "about to process header %x\n", nxt_hdr );
643 rc = ipv6_process_nxt_hdr ( iobuf, nxt_hdr,
644 src, dest, netdev, phcsm );
645 if ( rc == -EINVAL ) { /* Invalid packet/header? */
649 /* Processing completes after a fragment is received. */
650 if ( nxt_hdr == IP6_FRAGMENT ) {
651 DBG ( "handled a fragment, breaking\n" );
653 } else if ( ! is_ext_hdr ( nxt_hdr ) ) {
654 DBG ( "no more extension headers, iob probably invalid!\n" );
658 if ( rc != 0 ) { /* Ignore all other errors. */
659 DBG ( "ip6: unsupported extension header encountered, ignoring\n" );
664 opt = iob_pull ( iobuf, opt->len );
666 /* Stop processing if there are no more headers. */
667 if ( nxt_hdr == IP6_NO_HEADER ) {
676 * Process incoming IP6 packets
678 * @v iobuf I/O buffer
679 * @v netdev Network device
680 * @v ll_source Link-layer source address
682 * This function processes a IPv6 packet
684 static int ipv6_rx ( struct io_buffer *iobuf,
685 __unused struct net_device *netdev,
686 __unused const void *ll_source ) {
687 struct ip6_header *ip6hdr = iobuf->data;
689 struct sockaddr_in6 sin6;
690 struct sockaddr_tcpip st;
696 if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
697 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
702 /* Construct socket address */
703 memset ( &src, 0, sizeof ( src ) );
704 src.sin6.sin_family = AF_INET6;
705 src.sin6.sin6_addr = ip6hdr->src;
706 memset ( &dest, 0, sizeof ( dest ) );
707 dest.sin6.sin_family = AF_INET6;
708 dest.sin6.sin6_addr = ip6hdr->dest;
710 /* Check destination - always allow multicast. */
711 if ( dest.sin6.sin6_addr.s6_addr[0] != 0xFF ) {
712 if ( ipv6_check ( netdev, &dest.sin6.sin6_addr ) ) {
713 DBG ( "IP6: packet not for us\n" );
719 /* Print IP6 header for debugging */
720 ipv6_dump ( ip6hdr );
722 /* Check header version */
723 if ( ( ntohl( ip6hdr->ver_traffic_class_flow_label ) & 0xf0000000 ) != 0x60000000 ) {
724 DBG ( "Invalid protocol version\n" );
729 /* Check the payload length */
730 if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
731 DBG ( "Inconsistent packet length (%d bytes)\n",
732 ip6hdr->payload_len );
737 /* Ignore the traffic class and flow control values */
739 /* Calculate the psuedo-header checksum before the IP6 header is
741 phcsm = ipv6_tx_csum ( iobuf, TCPIP_EMPTY_CSUM );
744 iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
745 sizeof ( *ip6hdr ) );
746 iob_pull ( iobuf, sizeof ( *ip6hdr ) );
748 /* Send it to the transport layer */
749 return ipv6_process_headers ( iobuf, ip6hdr->nxt_hdr, &src.st,
750 &dest.st, netdev, phcsm );
753 DBG ( "IP6 packet dropped\n" );
759 * Convert an IPv6 address to a string.
761 * @v in6 Address to convert to string.
763 * Converts an IPv6 address to a string, and applies zero-compression as needed
764 * to condense the address for easier reading/typing.
766 char * inet6_ntoa ( struct in6_addr in6 ) {
768 uint16_t *bytes = ( uint16_t* ) &in6;
769 size_t i = 0, longest = 0, tmp = 0, long_idx = ~0;
772 if ( IP6_EQUAL ( in6, ip6_none ) ) {
773 tmp = sprintf ( buf, "::0" );
778 /* Determine the longest string of zeroes for zero-compression. */
779 for ( ; i < 8; i++ ) {
782 else if(tmp > longest) {
784 long_idx = i - longest;
790 /* Check for last word being zero. This will cause long_idx to be zero,
791 * which confuses the actual buffer fill code. */
792 if(tmp && (tmp > longest)) {
794 long_idx = 8 - longest;
797 /* Inject into the buffer. */
799 for ( i = 0; i < 8; i++ ) {
800 /* Should we skip over a string of zeroes? */
801 if ( i == long_idx ) {
803 tmp += sprintf( buf + tmp, ":" );
805 /* Handle end-of-string. */
810 /* Insert this component of the address. */
811 tmp += sprintf(buf + tmp, "%x", ntohs(bytes[i]));
813 /* Add the next colon, if needed. */
815 tmp += sprintf( buf + tmp, ":" );
824 * Convert a string to an IPv6 address.
826 * @v in6 String to convert to an address.
828 int inet6_aton ( const char *cp, struct in6_addr *inp ) {
830 char *tmp = convbuf, *next = convbuf;
835 /* Verify a valid address. */
836 while ( ( c = cp[i++] ) ) {
838 ok = ok || ( ( c >= '0' ) && ( c <= '9' ) );
839 ok = ok || ( ( c >= 'a' ) && ( c <= 'f' ) );
840 ok = ok || ( ( c >= 'A' ) && ( c <= 'F' ) );
850 strcpy ( convbuf, cp );
852 DBG ( "ipv6 converting %s to an in6_addr\n", cp );
854 /* Handle the first part of the address (or all of it if no zero-compression. */
856 while ( ( next = strchr ( next, ':' ) ) ) {
857 /* Cater for zero-compression. */
861 /* Convert to integer. */
862 inp->s6_addr16[i++] = htons( strtoul ( tmp, 0, 16 ) );
868 /* Handle the case where no zero-compression is needed, but every word
869 * was filled in the address. */
870 if ( ( i == 7 ) && ( *tmp != ':' ) ) {
871 inp->s6_addr16[i++] = htons( strtoul ( tmp, 0, 16 ) );
875 /* Handle zero-compression now (go backwards). */
877 if ( i && ( *tmp == ':' ) ) {
878 next = strrchr ( next, ':' );
884 /* Convert to integer. */
885 inp->s6_addr16[i--] = htons( strtoul ( tmp, 0, 16 ) );
886 } while ( ( next = strrchr ( next, ':' ) ) );
893 static const char * ipv6_ntoa ( const void *net_addr ) {
894 return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
897 static int ipv6_check ( struct net_device *netdev, const void *net_addr ) {
898 const struct in6_addr *address = net_addr;
899 struct ipv6_miniroute *miniroute;
901 list_for_each_entry ( miniroute, &miniroutes, list ) {
902 if ( ( miniroute->netdev == netdev ) &&
903 ( ! memcmp ( &miniroute->address, address, 16 ) ) ) {
904 /* Found matching address */
912 struct net_protocol ipv6_protocol __net_protocol = {
914 .net_proto = htons ( ETH_P_IPV6 ),
915 .net_addr_len = sizeof ( struct in6_addr ),
920 /** IPv6 TCPIP net protocol */
921 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
923 .sa_family = AF_INET6,
927 /** IPv6 ICMPv6 protocol, for NDP */
928 struct icmp6_net_protocol ipv6_icmp6_protocol __icmp6_net_protocol = {
929 .net_protocol = &ipv6_protocol,
935 /******************************************************************************
939 ******************************************************************************
942 /** IPv6 address setting */
943 struct setting ip6_setting __setting = {
945 .description = "IPv6 address",
946 .tag = DHCP6_OPT_IAADDR,
947 .type = &setting_type_ipv6,
950 /** IPv6 prefix setting */
951 struct setting prefix_setting __setting = {
953 .description = "IPv6 address prefix length",
955 .type = &setting_type_int32,
958 /** Default IPv6 gateway setting */
959 struct setting gateway6_setting __setting = {
961 .description = "IPv6 Default gateway",
963 .type = &setting_type_ipv4,
967 * Create IPv6 routes based on configured settings.
969 * @ret rc Return status code
971 static int ipv6_create_routes ( void ) {
972 struct ipv6_miniroute *miniroute;
973 struct ipv6_miniroute *tmp;
974 struct net_device *netdev;
975 struct settings *settings;
976 struct in6_addr address;
977 struct in6_addr gateway;
981 /* Create a route for each configured network device */
982 for_each_netdev ( netdev ) {
983 settings = netdev_settings ( netdev );
985 /* Read the settings first. We may need to clear routes. */
986 fetch_ipv6_setting ( settings, &ip6_setting, &address );
987 fetch_ipv6_setting ( settings, &gateway6_setting, &gateway );
988 fetch_int_setting ( settings, &prefix_setting, &prefix );
991 if ( ( prefix <= 0 ) || ( prefix > 128 ) ) {
992 DBG ( "ipv6: attempt to apply settings without a valid prefix, ignoring\n" );
993 continue; /* Simply ignore this setting. */
996 /* Remove any existing routes for this address. */
997 list_for_each_entry_safe ( miniroute, tmp, &miniroutes, list ) {
998 if ( ! ipv6_match_prefix ( &address,
1001 DBG ( "ipv6: existing route for a configured setting, deleting\n" );
1002 del_ipv6_miniroute ( miniroute );
1006 /* Configure route */
1007 rc = add_ipv6_address ( netdev, address, prefix,
1016 /** IPv6 settings applicator */
1017 struct settings_applicator ipv6_settings_applicator __settings_applicator = {
1018 .apply = ipv6_create_routes,
1021 /* Drag in ICMP6 and DHCP6 */
1022 REQUIRE_OBJECT ( icmpv6 );
1023 REQUIRE_OBJECT ( dhcp6 );