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