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