[contrib] Allow Network Protocol header to display in rom-o-matic
[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/retry.h>
18 #include <gpxe/timer.h>
19 #include <gpxe/dhcp6.h>
20
21 struct net_protocol ipv6_protocol;
22
23 #define is_linklocal( a ) ( ( (a).in6_u.u6_addr16[0] & htons ( 0xFE80 ) ) == htons ( 0xFE80 ) )
24
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 ) )
35
36 char * inet6_ntoa ( struct in6_addr in6 );
37
38 static int ipv6_check ( struct net_device *netdev, const void *net_addr );
39
40 /* Unspecified IP6 address */
41 static struct in6_addr ip6_none = {
42         .in6_u.u6_addr32 = { 0,0,0,0 }
43 };
44
45 /** An IPv6 routing table entry */
46 struct ipv6_miniroute {
47         /* List of miniroutes */
48         struct list_head list;
49
50         /* Network device */
51         struct net_device *netdev;
52
53         /* Destination prefix */
54         struct in6_addr prefix;
55         /* Prefix length */
56         int prefix_len;
57         /* IPv6 address of interface */
58         struct in6_addr address;
59         /* Gateway address */
60         struct in6_addr gateway;
61 };
62
63 /** List of IPv6 miniroutes */
64 static LIST_HEAD ( miniroutes );
65
66 /** List of fragment reassembly buffers */
67 static LIST_HEAD ( frag_buffers );
68
69 /**
70  * Generate an EUI-64 from a given link-local address.
71  *
72  * @v out               pointer to buffer to receive the EUI-64
73  * @v ll                pointer to buffer containing the link-local address
74  */
75 void ipv6_generate_eui64 ( uint8_t *out, uint8_t *ll ) {
76         assert ( out != 0 );
77         assert ( ll != 0 );
78         
79         /* Create an EUI-64 identifier. */
80         memcpy( out, ll, 3 );
81         memcpy( out + 5, ll + 3, 3 );
82         out[3] = 0xFF;
83         out[4] = 0xFE;
84         
85         /* Designate that this is in fact an EUI-64. */
86         out[0] |= 0x2;
87 }
88
89 /**
90  * Verifies that a prefix matches another one.
91  *
92  * @v p1                first prefix
93  * @v p2                second prefix
94  * @v len               prefix length in bits to compare
95  * @ret int             0 if a match, nonzero otherwise
96  */
97 int ipv6_match_prefix ( struct in6_addr *p1, struct in6_addr *p2, size_t len ) {
98         uint8_t ip1, ip2;
99         size_t offset, bits;
100         int rc = 0;
101
102         /* Check for a prefix match on the route. */
103         if ( ! memcmp ( p1, p2, len / 8 ) ) {
104                 rc = 0;
105
106                 /* Handle extra bits in the prefix. */
107                 if ( ( len % 8 ) ||
108                      ( len < 8 ) ) {
109                         DBG ( "ipv6: prefix is not aligned to a byte.\n" );
110
111                         /* Compare the remaining bits. */
112                         offset = len / 8;
113                         bits = len % 8;
114
115                         ip1 = p1->in6_u.u6_addr8[offset];
116                         ip2 = p2->in6_u.u6_addr8[offset];
117                         if ( ! ( ( ip1 & (0xFF >> (8 - bits)) ) &
118                              ( ip2 ) ) ) {
119                                 rc = 1;
120                         }
121                 }
122         } else {
123                 rc = 1;
124         }
125
126         return rc;
127 }
128
129 /**
130  * Add IPv6 minirouting table entry
131  *
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
137  */
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;
143         
144         DBG( "ipv6 add: %s/%d ", inet6_ntoa ( address ), prefix_len );
145         DBG( "gw %s\n", inet6_ntoa( gateway ) );
146
147         miniroute = malloc ( sizeof ( *miniroute ) );
148         if ( 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;
155
156                 /* Add miniroute to list of miniroutes */
157                 if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
158                         list_add_tail ( &miniroute->list, &miniroutes );
159                 } else {
160                         list_add ( &miniroute->list, &miniroutes );
161                 }
162         }
163
164         return miniroute;
165 }
166
167 /**
168  * Delete IPv6 minirouting table entry
169  *
170  * @v miniroute         Routing table entry
171  */
172 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
173         
174         DBG ( "ipv6 del: %s/%d\n", inet6_ntoa(miniroute->address),
175                                    miniroute->prefix_len );
176         
177         netdev_put ( miniroute->netdev );
178         list_del ( &miniroute->list );
179         free ( miniroute );
180 }
181
182 /**
183  * Add IPv6 interface
184  *
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)
189  */
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;
194
195         /* Clear any existing address for this net device */
196         /* del_ipv6_address ( netdev ); */
197
198         /* Add new miniroute */
199         miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
200                                          gateway );
201         if ( ! miniroute )
202                 return -ENOMEM;
203
204         return 0;
205 }
206
207 /**
208  * Remove IPv6 interface
209  *
210  * @v netdev    Network device
211  */
212 void del_ipv6_address ( struct net_device *netdev ) {
213         struct ipv6_miniroute *miniroute;
214
215         list_for_each_entry ( miniroute, &miniroutes, list ) {
216                 if ( miniroute->netdev == netdev ) {
217                         del_ipv6_miniroute ( miniroute );
218                         break;
219                 }
220         }
221 }
222
223 /**
224  * Calculate TX checksum
225  *
226  * @v iobuf     I/O buffer
227  * @v csum      Partial checksum.
228  *
229  * This function constructs the pseudo header and completes the checksum in the
230  * upper layer header.
231  */
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;
235
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;
242
243         /* Update checksum value */
244         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
245 }
246
247 /**
248  * Calculate TCPIP checksum with the given values
249  *
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.
255  *
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.
259  */
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;
263
264         /* Calculate pseudo header */
265         memset ( &pshdr, 0, sizeof ( pshdr ) );
266         pshdr.src = *src;
267         pshdr.dest = *dst;
268         pshdr.len = htons ( length );
269         pshdr.nxt_hdr = next_hdr;
270
271         /* Update checksum value */
272         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
273 }
274
275 /**
276  * Dump IP6 header for debugging
277  *
278  * ip6hdr       IPv6 header
279  */
280 void ipv6_dump ( struct ip6_header *ip6hdr ) {
281         /* Because inet6_ntoa returns a static char[16], each call needs to be
282          * separate. */
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 ) );
286 }
287
288 /**
289  * Transmit IP6 packet
290  *
291  * iobuf                I/O buffer
292  * tcpip        TCP/IP protocol
293  * st_dest      Destination socket address
294  *
295  * This function prepends the IPv6 headers to the payload an transmits it.
296  */
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;
309         
310         /* Check for multicast transmission. */
311         multicast = dest->sin6_addr.in6_u.u6_addr8[0] == 0xFF;
312
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
320         
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 ) ) {
326                         continue;
327                 }
328                 
329                 /* Link-local route? */
330                 linklocal = is_linklocal ( miniroute->address ); // (.in6_u.u6_addr16[0] & htons(0xFE80)) == htons(0xFE80);
331
332                 /* Handle link-local for multicast. */
333                 if ( multicast )
334                 {
335                         /* Link-local scope? */
336                         if ( is_linklocal ( next_hop ) ) { // .in6_u.u6_addr8[0] & 0x2 ) {
337                                 if ( linklocal ) {
338                                         netdev = miniroute->netdev;
339                                         ip6hdr->src = miniroute->address;
340                                         break;
341                                 } else {
342                                         /* Should be link-local address. */
343                                         continue;
344                                 }
345                         } else {
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;
351                                 break;
352                         }
353                 }
354                 
355                 /* Check for a prefix match on the route. */
356                 rc = ipv6_match_prefix ( &next_hop, &miniroute->prefix, miniroute->prefix_len );
357                 
358                 /* Matched? */
359                 if( rc == 0 ) {
360                         netdev = miniroute->netdev;
361                         ip6hdr->src = miniroute->address;
362                         break;
363                 }
364                 
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;
370                 }
371         }
372         /* No network interface identified */
373         if ( ( ! netdev ) ) {
374                 DBG ( "No route to host %s\n", inet6_ntoa ( dest->sin6_addr ) );
375                 rc = -ENETUNREACH;
376                 goto err;
377         } else if ( ! IP6_EQUAL ( gateway, ip6_none ) ) {
378                 next_hop = gateway;
379         }
380         
381         /* Add the next hop to the packet. */
382         ip6hdr->dest = dest->sin6_addr;
383
384         /* Complete the transport layer checksum */
385         if ( trans_csum )
386                 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
387
388         /* Print IPv6 header */
389         /* ipv6_dump ( ip6hdr ); */
390
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];
399         } else {
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 ) );
404                         goto err;
405                 }
406         }
407
408         /* Transmit packet */
409         return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
410
411   err:
412         free_iob ( iobuf );
413         return rc;
414 }
415
416 /**
417  * Fragment reassembly counter timeout
418  *
419  * @v timer     Retry timer
420  * @v over      If asserted, the timer is greater than @c MAX_TIMEOUT 
421  */
422 static void ipv6_frag_expired ( struct retry_timer *timer __unused,
423                                 int over ) {
424         if ( over ) {
425                 DBG ( "Fragment reassembly timeout" );
426                 /* Free the fragment buffer */
427         }
428 }
429
430 /**
431  * Free fragment buffer
432  *
433  * @v fragbug   Fragment buffer
434  */
435 static void free_fragbuf ( struct frag_buffer *fragbuf ) {
436         free ( fragbuf );
437 }
438
439 /**
440  * Get the "next header" field and free a fragment buffer for a given iobuf.
441  *
442  * @v iobuf     I/O buffer to reference.
443  */
444 static int frag_next_hdr ( struct io_buffer *iobuf ) {
445         struct frag_buffer *fragbuf;
446         int rc = IP6_NO_HEADER;
447         
448         list_for_each_entry ( fragbuf, &frag_buffers, list ) {
449                 if ( fragbuf->frag_iob == iobuf ) {
450                         rc = fragbuf->next_hdr;
451                         free_fragbuf ( fragbuf );
452                 }
453         }
454         
455         return rc;
456 }
457
458 /**
459  * Fragment reassembler
460  *
461  * @v iobuf             I/O buffer, fragment of the datagram
462  * @ret frag_iob        Reassembled packet, or NULL
463  */
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;
468         
469         struct sockaddr_in6 *src = ( struct sockaddr_in6 * ) st_src;
470         
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 );
474         
475         /**
476          * Check if the fragment belongs to any fragment series
477          */
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 ) ) {
481                         /**
482                          * Check if the packet is the expected fragment
483                          * 
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
487                          */
488                         if ( iob_len ( fragbuf->frag_iob ) == offset ) {
489                                 /**
490                                  * Append the contents of the fragment to the
491                                  * reassembled I/O buffer
492                                  */
493                                 iob_pull ( iobuf, sizeof ( *frag_hdr ) );
494                                 memcpy ( iob_put ( fragbuf->frag_iob,
495                                                         iob_len ( iobuf ) ),
496                                          iobuf->data, iob_len ( iobuf ) );
497                                 free_iob ( iobuf );
498
499                                 /** Check if the fragment series is over */
500                                 if ( ! ( flags & IP6_MORE_FRAGMENTS ) ) {
501                                         iobuf = fragbuf->frag_iob;
502                                         return iobuf;
503                                 }
504
505                         } else {
506                                 /* Discard the fragment series */
507                                 free_fragbuf ( fragbuf );
508                                 free_iob ( iobuf );
509                         }
510                         return NULL;
511                 }
512         }
513         
514         /** Check if the fragment is the first in the fragment series */
515         if ( ( flags & IP6_MORE_FRAGMENTS ) && ( offset == 0 ) ) {
516         
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;
522
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 ) );
528                 free_iob ( iobuf );
529
530                 /* Set the reassembly timer */
531                 timer_init ( &fragbuf->frag_timer, ipv6_frag_expired );
532                 start_timer_fixed ( &fragbuf->frag_timer, IP6_FRAG_TIMEOUT );
533
534                 /* Add the fragment buffer to the list of fragment buffers */
535                 list_add ( &fragbuf->list, &frag_buffers );
536         }
537         
538         return NULL;
539 }
540
541 /**
542  * Process next IP6 header
543  *
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.
550  *
551  * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
552  */
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;
559         
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 );
564                 if ( reassembled ) {
565                         /* Reassembled, pass to upper layer. */
566                         nxt_hdr = frag_next_hdr ( reassembled );
567                         iobuf = reassembled;
568                         if ( nxt_hdr == IP6_FRAGMENT ) {
569                                 DBG ( "ip6: recursive fragment, dropping\n" );
570                                 return -EINVAL;
571                         }
572                         
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 );
577                 } else {
578                         return 0;
579                 }
580         }
581         
582         switch ( nxt_hdr ) {
583         case IP6_PAD:
584         case IP6_PADN:
585                 return 0; /* Padding options. */
586         
587         case IP6_AUTHENTICATION:
588                 /* Handle authentication. */
589         case IP6_ESP:
590                 /* Handle an encapsulated security payload. */
591                 DBG ( "Function not implemented for header %d\n", nxt_hdr );
592                 return -ENOSYS;
593         
594         /* Can ignore these. */
595         case IP6_HOPBYHOP:
596         case IP6_ROUTING:
597         case IP6_DEST_OPTS:
598                 DBG ( "ip6: ignoring header %d\n", nxt_hdr );
599                 break;
600         case IP6_NO_HEADER:
601                 DBG ( "No next header\n" );
602                 return 0;
603         case IP6_ICMP6:
604                 return icmp6_rx ( iobuf, src, dest, netdev, phcsm );
605         }
606         /* Next header is not a IPv6 extension header */
607         return tcpip_rx ( iobuf, nxt_hdr, src, dest, phcsm );
608 }
609
610 /**
611  * Iterate over IP6 headers, processing each one.
612  *
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.
618  */
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;
623         int flags, rc = 0;
624         
625         /* Handle packets without options. */
626         if ( ! is_ext_hdr ( nxt_hdr ) ) {
627                 return ipv6_process_nxt_hdr ( iobuf, nxt_hdr, src, dest,
628                                               netdev, phcsm );
629         }
630         
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;
635         }
636         
637         /* Iterate over the option list. */
638         while ( iob_len ( iobuf ) ) {
639                 flags = nxt_hdr >> 6;
640                 
641                 DBG ( "about to process header %x\n", nxt_hdr );
642                 
643                 rc = ipv6_process_nxt_hdr ( iobuf, nxt_hdr,
644                                             src, dest, netdev, phcsm );
645                 if ( rc == -EINVAL ) { /* Invalid packet/header? */
646                         return rc;
647                 }
648                 
649                 /* Processing completes after a fragment is received. */
650                 if ( nxt_hdr == IP6_FRAGMENT ) {
651                         DBG ( "handled a fragment, breaking\n" );
652                         break;
653                 } else if ( ! is_ext_hdr ( nxt_hdr ) ) {
654                         DBG ( "no more extension headers, iob probably invalid!\n" );
655                         break;
656                 }
657                 
658                 if ( rc != 0 ) { /* Ignore all other errors. */
659                         DBG ( "ip6: unsupported extension header encountered, ignoring\n" );
660                         rc = 0;
661                 }
662         
663                 nxt_hdr = opt->type;
664                 opt = iob_pull ( iobuf, opt->len );
665                 
666                 /* Stop processing if there are no more headers. */
667                 if ( nxt_hdr == IP6_NO_HEADER ) {
668                         break;
669                 }
670         }
671         
672         return rc;
673 }
674
675 /**
676  * Process incoming IP6 packets
677  *
678  * @v iobuf             I/O buffer
679  * @v netdev            Network device
680  * @v ll_source         Link-layer source address
681  *
682  * This function processes a IPv6 packet
683  */
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;
688         union {
689                 struct sockaddr_in6 sin6;
690                 struct sockaddr_tcpip st;
691         } src, dest;
692         uint16_t phcsm = 0;
693         int rc = 0;
694
695         /* Sanity check */
696         if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
697                 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
698                 rc = -EINVAL;
699                 goto drop;
700         }
701
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;
709
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" );
714                         rc = -EINVAL;
715                         goto drop;
716                 }
717         }
718
719         /* Print IP6 header for debugging */
720         ipv6_dump ( ip6hdr );
721
722         /* Check header version */
723         if ( ( ntohl( ip6hdr->ver_traffic_class_flow_label ) & 0xf0000000 ) != 0x60000000 ) {
724                 DBG ( "Invalid protocol version\n" );
725                 rc = -EINVAL;
726                 goto drop;
727         }
728
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 );
733                 rc = -EINVAL;
734                 goto drop;
735         }
736
737         /* Ignore the traffic class and flow control values */
738
739         /* Calculate the psuedo-header checksum before the IP6 header is
740          * stripped away. */
741         phcsm = ipv6_tx_csum ( iobuf, TCPIP_EMPTY_CSUM );
742
743         /* Strip header */
744         iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
745                                                         sizeof ( *ip6hdr ) );
746         iob_pull ( iobuf, sizeof ( *ip6hdr ) );
747
748         /* Send it to the transport layer */
749         return ipv6_process_headers ( iobuf, ip6hdr->nxt_hdr, &src.st,
750                                       &dest.st, netdev, phcsm );
751
752   drop:
753         DBG ( "IP6 packet dropped\n" );
754         free_iob ( iobuf );
755         return rc;
756 }
757
758 /**
759  * Convert an IPv6 address to a string.
760  *
761  * @v in6   Address to convert to string.
762  *
763  * Converts an IPv6 address to a string, and applies zero-compression as needed
764  * to condense the address for easier reading/typing.
765  */
766 char * inet6_ntoa ( struct in6_addr in6 ) {
767         static char buf[40];
768         uint16_t *bytes = ( uint16_t* ) &in6;
769         size_t i = 0, longest = 0, tmp = 0, long_idx = ~0;
770         
771         /* ::0 */
772         if ( IP6_EQUAL ( in6, ip6_none ) ) {
773                 tmp = sprintf ( buf, "::0" );
774                 buf[tmp] = 0;
775                 return buf;
776         }
777
778         /* Determine the longest string of zeroes for zero-compression. */
779         for ( ; i < 8; i++ ) {
780                 if ( !bytes[i] )
781                         tmp++;
782                 else if(tmp > longest) {
783                         longest = tmp;
784                         long_idx = i - longest;
785                         
786                         tmp = 0;
787                 }
788         }
789         
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)) {
793                 longest = tmp;
794                 long_idx = 8 - longest;
795         }
796
797         /* Inject into the buffer. */
798         tmp = 0;
799         for ( i = 0; i < 8; i++ ) {
800                 /* Should we skip over a string of zeroes? */
801                 if ( i == long_idx ) {
802                         i += longest;
803                         tmp += sprintf( buf + tmp, ":" );
804
805                         /* Handle end-of-string. */
806                         if(i > 7)
807                                 break;
808                 }
809
810                 /* Insert this component of the address. */
811                 tmp += sprintf(buf + tmp, "%x", ntohs(bytes[i]));
812
813                 /* Add the next colon, if needed. */
814                 if ( i < 7 )
815                         tmp += sprintf( buf + tmp, ":" );
816         }
817
818         buf[tmp] = 0;
819
820         return buf;
821 }
822
823 /**
824  * Convert a string to an IPv6 address.
825  *
826  * @v in6   String to convert to an address.
827  */
828 int inet6_aton ( const char *cp, struct in6_addr *inp ) {
829         char convbuf[40];
830         char *tmp = convbuf, *next = convbuf;
831         size_t i = 0;
832         int ok;
833         char c;
834         
835         /* Verify a valid address. */
836         while ( ( c = cp[i++] ) ) {
837                 ok = c == ':';
838                 ok = ok || ( ( c >= '0' ) && ( c <= '9' ) );
839                 ok = ok || ( ( c >= 'a' ) && ( c <= 'f' ) );
840                 ok = ok || ( ( c >= 'A' ) && ( c <= 'F' ) );
841                 
842                 if ( ! ok ) {
843                         return 0;
844                 }
845         }
846         if ( ! i ) {
847                 return 0;
848         }
849         
850         strcpy ( convbuf, cp );
851         
852         DBG ( "ipv6 converting %s to an in6_addr\n", cp );
853         
854         /* Handle the first part of the address (or all of it if no zero-compression. */
855         i = 0;
856         while ( ( next = strchr ( next, ':' ) ) ) {
857                 /* Cater for zero-compression. */
858                 if ( *tmp == ':' )
859                         break;
860                 
861                 /* Convert to integer. */
862                 inp->s6_addr16[i++] = htons( strtoul ( tmp, 0, 16 ) );
863                 
864                 *next++ = 0;
865                 tmp = next;
866         }
867         
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 ) );
872         }
873         else
874         {
875                 /* Handle zero-compression now (go backwards). */
876                 i = 7;
877                 if ( i && ( *tmp == ':' ) ) {
878                         next = strrchr ( next, ':' );
879                         do
880                         {
881                                 tmp = next + 1;
882                                 *next-- = 0;
883         
884                                 /* Convert to integer. */
885                                 inp->s6_addr16[i--] = htons( strtoul ( tmp, 0, 16 ) );
886                         } while ( ( next = strrchr ( next, ':' ) ) );
887                 }
888         }
889         
890         return 1;
891 }
892
893 static const char * ipv6_ntoa ( const void *net_addr ) {
894         return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
895 }
896
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;
900
901         list_for_each_entry ( miniroute, &miniroutes, list ) {
902                 if ( ( miniroute->netdev == netdev ) &&
903                      ( ! memcmp ( &miniroute->address, address, 16 ) ) ) {
904                         /* Found matching address */
905                         return 0;
906                 }
907         }
908         return -ENOENT;
909 }
910
911 /** IPv6 protocol */
912 struct net_protocol ipv6_protocol __net_protocol = {
913         .name = "IPV6",
914         .net_proto = htons ( ETH_P_IPV6 ),
915         .net_addr_len = sizeof ( struct in6_addr ),
916         .rx = ipv6_rx,
917         .ntoa = ipv6_ntoa,
918 };
919
920 /** IPv6 TCPIP net protocol */
921 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
922         .name = "IPv6",
923         .sa_family = AF_INET6,
924         .tx = ipv6_tx,
925 };
926
927 /** IPv6 ICMPv6 protocol, for NDP */
928 struct icmp6_net_protocol ipv6_icmp6_protocol __icmp6_net_protocol = {
929         .net_protocol = &ipv6_protocol,
930         .check = ipv6_check,
931 };
932
933
934
935 /******************************************************************************
936  *
937  * Settings
938  *
939  ******************************************************************************
940  */
941
942 /** IPv6 address setting */
943 struct setting ip6_setting __setting = {
944         .name = "ip6",
945         .description = "IPv6 address",
946         .tag = DHCP6_OPT_IAADDR,
947         .type = &setting_type_ipv6,
948 };
949
950 /** IPv6 prefix setting */
951 struct setting prefix_setting __setting = {
952         .name = "prefix",
953         .description = "IPv6 address prefix length",
954         .tag = 0,
955         .type = &setting_type_int32,
956 };
957
958 /** Default IPv6 gateway setting */
959 struct setting gateway6_setting __setting = {
960         .name = "gateway6",
961         .description = "IPv6 Default gateway",
962         .tag = 0,
963         .type = &setting_type_ipv4,
964 };
965
966 /**
967  * Create IPv6 routes based on configured settings.
968  *
969  * @ret rc              Return status code
970  */
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;
978         long prefix = 0;
979         int rc = 0;
980         
981         /* Create a route for each configured network device */
982         for_each_netdev ( netdev ) {
983                 settings = netdev_settings ( netdev );
984         
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 );
989         
990                 /* Sanity check! */
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. */
994                 }
995         
996                 /* Remove any existing routes for this address. */
997                 list_for_each_entry_safe ( miniroute, tmp, &miniroutes, list ) {
998                         if ( ! ipv6_match_prefix ( &address,
999                                                  &miniroute->prefix,
1000                                                  prefix ) ) {
1001                                 DBG ( "ipv6: existing route for a configured setting, deleting\n" );
1002                                 del_ipv6_miniroute ( miniroute );
1003                         }
1004                 }
1005                 
1006                 /* Configure route */
1007                 rc = add_ipv6_address ( netdev, address, prefix,
1008                                         address, gateway );
1009                 if ( ! rc )
1010                         return rc;
1011         }
1012         
1013         return 0;
1014 }
1015
1016 /** IPv6 settings applicator */
1017 struct settings_applicator ipv6_settings_applicator __settings_applicator = {
1018         .apply = ipv6_create_routes,
1019 };
1020
1021 /* Drag in ICMP6 and DHCP6 */
1022 REQUIRE_OBJECT ( icmpv6 );
1023 REQUIRE_OBJECT ( dhcp6 );