ipv6.c

Go to the documentation of this file.
00001 #include <errno.h>
00002 #include <stdint.h>
00003 #include <string.h>
00004 #include <stdlib.h>
00005 #include <stdio.h>
00006 #include <byteswap.h>
00007 #include <gpxe/in.h>
00008 #include <gpxe/ip6.h>
00009 #include <gpxe/ndp.h>
00010 #include <gpxe/list.h>
00011 #include <gpxe/icmp6.h>
00012 #include <gpxe/tcpip.h>
00013 #include <gpxe/socket.h>
00014 #include <gpxe/iobuf.h>
00015 #include <gpxe/netdevice.h>
00016 #include <gpxe/if_ether.h>
00017 
00018 struct net_protocol ipv6_protocol;
00019 
00020 /* Unspecified IP6 address */
00021 static struct in6_addr ip6_none = {
00022         .in6_u.u6_addr32 = { 0,0,0,0 }
00023 };
00024 
00025 /** An IPv6 routing table entry */
00026 struct ipv6_miniroute {
00027         /* List of miniroutes */
00028         struct list_head list;
00029 
00030         /* Network device */
00031         struct net_device *netdev;
00032 
00033         /* Destination prefix */
00034         struct in6_addr prefix;
00035         /* Prefix length */
00036         int prefix_len;
00037         /* IPv6 address of interface */
00038         struct in6_addr address;
00039         /* Gateway address */
00040         struct in6_addr gateway;
00041 };
00042 
00043 /** List of IPv6 miniroutes */
00044 static LIST_HEAD ( miniroutes );
00045 
00046 /**
00047  * Add IPv6 minirouting table entry
00048  *
00049  * @v netdev            Network device
00050  * @v prefix            Destination prefix
00051  * @v address           Address of the interface
00052  * @v gateway           Gateway address (or ::0 for no gateway)
00053  * @ret miniroute       Routing table entry, or NULL
00054  */
00055 static struct ipv6_miniroute * __malloc 
00056 add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
00057                      int prefix_len, struct in6_addr address,
00058                      struct in6_addr gateway ) {
00059         struct ipv6_miniroute *miniroute;
00060         
00061         miniroute = malloc ( sizeof ( *miniroute ) );
00062         if ( miniroute ) {
00063                 /* Record routing information */
00064                 miniroute->netdev = netdev_get ( netdev );
00065                 miniroute->prefix = prefix;
00066                 miniroute->prefix_len = prefix_len;
00067                 miniroute->address = address;
00068                 miniroute->gateway = gateway;
00069                 
00070                 /* Add miniroute to list of miniroutes */
00071                 if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
00072                         list_add_tail ( &miniroute->list, &miniroutes );
00073                 } else {
00074                         list_add ( &miniroute->list, &miniroutes );
00075                 }
00076         }
00077 
00078         return miniroute;
00079 }
00080 
00081 /**
00082  * Delete IPv6 minirouting table entry
00083  *
00084  * @v miniroute         Routing table entry
00085  */
00086 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
00087         netdev_put ( miniroute->netdev );
00088         list_del ( &miniroute->list );
00089         free ( miniroute );
00090 }
00091 
00092 /**
00093  * Add IPv6 interface
00094  *
00095  * @v netdev    Network device
00096  * @v prefix    Destination prefix
00097  * @v address   Address of the interface
00098  * @v gateway   Gateway address (or ::0 for no gateway)
00099  */
00100 int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
00101                        int prefix_len, struct in6_addr address,
00102                        struct in6_addr gateway ) {
00103         struct ipv6_miniroute *miniroute;
00104 
00105         /* Clear any existing address for this net device */
00106         del_ipv6_address ( netdev );
00107 
00108         /* Add new miniroute */
00109         miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
00110                                          gateway );
00111         if ( ! miniroute )
00112                 return -ENOMEM;
00113 
00114         return 0;
00115 }
00116 
00117 /**
00118  * Remove IPv6 interface
00119  *
00120  * @v netdev    Network device
00121  */
00122 void del_ipv6_address ( struct net_device *netdev ) {
00123         struct ipv6_miniroute *miniroute;
00124 
00125         list_for_each_entry ( miniroute, &miniroutes, list ) {
00126                 if ( miniroute->netdev == netdev ) {
00127                         del_ipv6_miniroute ( miniroute );
00128                         break;
00129                 }
00130         }
00131 }
00132 
00133 /**
00134  * Calculate TCPIP checksum
00135  *
00136  * @v iobuf     I/O buffer
00137  * @v tcpip     TCP/IP protocol
00138  *
00139  * This function constructs the pseudo header and completes the checksum in the
00140  * upper layer header.
00141  */
00142 static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
00143         struct ip6_header *ip6hdr = iobuf->data;
00144         struct ipv6_pseudo_header pshdr;
00145 
00146         /* Calculate pseudo header */
00147         memset ( &pshdr, 0, sizeof ( pshdr ) );
00148         pshdr.src = ip6hdr->src;
00149         pshdr.dest = ip6hdr->dest;
00150         pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
00151         pshdr.nxt_hdr = ip6hdr->nxt_hdr;
00152 
00153         /* Update checksum value */
00154         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
00155 }
00156 
00157 /**
00158  * Dump IP6 header for debugging
00159  *
00160  * ip6hdr       IPv6 header
00161  */
00162 void ipv6_dump ( struct ip6_header *ip6hdr ) {
00163         DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
00164               inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
00165               ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
00166 }
00167 
00168 /**
00169  * Transmit IP6 packet
00170  *
00171  * iobuf                I/O buffer
00172  * tcpip        TCP/IP protocol
00173  * st_dest      Destination socket address
00174  *
00175  * This function prepends the IPv6 headers to the payload an transmits it.
00176  */
00177 static int ipv6_tx ( struct io_buffer *iobuf,
00178                      struct tcpip_protocol *tcpip,
00179                      struct sockaddr_tcpip *st_src __unused,
00180                      struct sockaddr_tcpip *st_dest,
00181                      struct net_device *netdev,
00182                      uint16_t *trans_csum ) {
00183         struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
00184         struct in6_addr next_hop;
00185         struct ipv6_miniroute *miniroute;
00186         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
00187         const uint8_t *ll_dest = ll_dest_buf;
00188         int rc;
00189 
00190         /* Construct the IPv6 packet */
00191         struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
00192         memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
00193         ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
00194         ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
00195         ip6hdr->nxt_hdr = tcpip->tcpip_proto;
00196         ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
00197 
00198         /* Determine the next hop address and interface
00199          *
00200          * TODO: Implement the routing table.
00201          */
00202         next_hop = dest->sin6_addr;
00203         list_for_each_entry ( miniroute, &miniroutes, list ) {
00204                 if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix,
00205                                         miniroute->prefix_len ) == 0 ) ||
00206                      ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
00207                         netdev = miniroute->netdev;
00208                         ip6hdr->src = miniroute->address;
00209                         if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
00210                                 next_hop = miniroute->gateway;
00211                         }
00212                         break;
00213                 }
00214         }
00215         /* No network interface identified */
00216         if ( !netdev ) {
00217                 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
00218                 rc = -ENETUNREACH;
00219                 goto err;
00220         }
00221 
00222         /* Complete the transport layer checksum */
00223         if ( trans_csum )
00224                 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
00225 
00226         /* Print IPv6 header */
00227         ipv6_dump ( ip6hdr );
00228         
00229         /* Resolve link layer address */
00230         if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
00231                 ll_dest_buf[0] = 0x33;
00232                 ll_dest_buf[1] = 0x33;
00233                 ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
00234                 ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
00235                 ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
00236                 ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
00237         } else {
00238                 /* Unicast address needs to be resolved by NDP */
00239                 if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
00240                                           ll_dest_buf ) ) != 0 ) {
00241                         DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
00242                         goto err;
00243                 }
00244         }
00245 
00246         /* Transmit packet */
00247         return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
00248 
00249   err:
00250         free_iob ( iobuf );
00251         return rc;
00252 }
00253 
00254 /**
00255  * Process next IP6 header
00256  *
00257  * @v iobuf     I/O buffer
00258  * @v nxt_hdr   Next header number
00259  * @v src       Source socket address
00260  * @v dest      Destination socket address
00261  *
00262  * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
00263  */
00264 static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
00265                 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
00266         switch ( nxt_hdr ) {
00267         case IP6_HOPBYHOP: 
00268         case IP6_ROUTING: 
00269         case IP6_FRAGMENT: 
00270         case IP6_AUTHENTICATION: 
00271         case IP6_DEST_OPTS: 
00272         case IP6_ESP: 
00273                 DBG ( "Function not implemented for header %d\n", nxt_hdr );
00274                 return -ENOSYS;
00275         case IP6_ICMP6: 
00276                 break;
00277         case IP6_NO_HEADER: 
00278                 DBG ( "No next header\n" );
00279                 return 0;
00280         }
00281         /* Next header is not a IPv6 extension header */
00282         return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ );
00283 }
00284 
00285 /**
00286  * Process incoming IP6 packets
00287  *
00288  * @v iobuf             I/O buffer
00289  * @v netdev            Network device
00290  * @v ll_source         Link-layer source address
00291  *
00292  * This function processes a IPv6 packet
00293  */
00294 static int ipv6_rx ( struct io_buffer *iobuf,
00295                      __unused struct net_device *netdev,
00296                      __unused const void *ll_source ) {
00297 
00298         struct ip6_header *ip6hdr = iobuf->data;
00299         union {
00300                 struct sockaddr_in6 sin6;
00301                 struct sockaddr_tcpip st;
00302         } src, dest;
00303 
00304         /* Sanity check */
00305         if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
00306                 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
00307                 goto drop;
00308         }
00309 
00310         /* TODO: Verify checksum */
00311 
00312         /* Print IP6 header for debugging */
00313         ipv6_dump ( ip6hdr );
00314 
00315         /* Check header version */
00316         if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
00317                 DBG ( "Invalid protocol version\n" );
00318                 goto drop;
00319         }
00320 
00321         /* Check the payload length */
00322         if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
00323                 DBG ( "Inconsistent packet length (%d bytes)\n",
00324                         ip6hdr->payload_len );
00325                 goto drop;
00326         }
00327 
00328         /* Ignore the traffic class and flow control values */
00329 
00330         /* Construct socket address */
00331         memset ( &src, 0, sizeof ( src ) );
00332         src.sin6.sin_family = AF_INET6;
00333         src.sin6.sin6_addr = ip6hdr->src;
00334         memset ( &dest, 0, sizeof ( dest ) );
00335         dest.sin6.sin_family = AF_INET6;
00336         dest.sin6.sin6_addr = ip6hdr->dest;
00337 
00338         /* Strip header */
00339         iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
00340                                                         sizeof ( *ip6hdr ) );
00341         iob_pull ( iobuf, sizeof ( *ip6hdr ) );
00342 
00343         /* Send it to the transport layer */
00344         return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
00345 
00346   drop:
00347         DBG ( "Packet dropped\n" );
00348         free_iob ( iobuf );
00349         return -1;
00350 }
00351 
00352 /**
00353  * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
00354  */
00355 char * inet6_ntoa ( struct in6_addr in6 ) {
00356         static char buf[40];
00357         uint16_t *bytes = ( uint16_t* ) &in6;
00358         sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
00359                         bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
00360         return buf;
00361 }
00362 
00363 static const char * ipv6_ntoa ( const void *net_addr ) {
00364         return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
00365 }
00366 
00367 /** IPv6 protocol */
00368 struct net_protocol ipv6_protocol __net_protocol = {
00369         .name = "IPv6",
00370         .net_proto = htons ( ETH_P_IPV6 ),
00371         .net_addr_len = sizeof ( struct in6_addr ),
00372         .rx = ipv6_rx,
00373         .ntoa = ipv6_ntoa,
00374 };
00375 
00376 /** IPv6 TCPIP net protocol */
00377 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
00378         .name = "IPv6",
00379         .sa_family = AF_INET6,
00380         .tx = ipv6_tx,
00381 };

Generated on Tue Apr 6 20:01:10 2010 for gPXE by  doxygen 1.5.7.1