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
00021 static struct in6_addr ip6_none = {
00022 .in6_u.u6_addr32 = { 0,0,0,0 }
00023 };
00024
00025
00026 struct ipv6_miniroute {
00027
00028 struct list_head list;
00029
00030
00031 struct net_device *netdev;
00032
00033
00034 struct in6_addr prefix;
00035
00036 int prefix_len;
00037
00038 struct in6_addr address;
00039
00040 struct in6_addr gateway;
00041 };
00042
00043
00044 static LIST_HEAD ( miniroutes );
00045
00046
00047
00048
00049
00050
00051
00052
00053
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
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
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
00083
00084
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
00094
00095
00096
00097
00098
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
00106 del_ipv6_address ( netdev );
00107
00108
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
00119
00120
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
00135
00136
00137
00138
00139
00140
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
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
00154 return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
00155 }
00156
00157
00158
00159
00160
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
00170
00171
00172
00173
00174
00175
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
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 );
00194 ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
00195 ip6hdr->nxt_hdr = tcpip->tcpip_proto;
00196 ip6hdr->hop_limit = IP6_HOP_LIMIT;
00197
00198
00199
00200
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
00216 if ( !netdev ) {
00217 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
00218 rc = -ENETUNREACH;
00219 goto err;
00220 }
00221
00222
00223 if ( trans_csum )
00224 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
00225
00226
00227 ipv6_dump ( ip6hdr );
00228
00229
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
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
00247 return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
00248
00249 err:
00250 free_iob ( iobuf );
00251 return rc;
00252 }
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
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
00282 return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 );
00283 }
00284
00285
00286
00287
00288
00289
00290
00291
00292
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
00305 if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
00306 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
00307 goto drop;
00308 }
00309
00310
00311
00312
00313 ipv6_dump ( ip6hdr );
00314
00315
00316 if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
00317 DBG ( "Invalid protocol version\n" );
00318 goto drop;
00319 }
00320
00321
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
00329
00330
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
00339 iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
00340 sizeof ( *ip6hdr ) );
00341 iob_pull ( iobuf, sizeof ( *ip6hdr ) );
00342
00343
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
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
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
00377 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
00378 .name = "IPv6",
00379 .sa_family = AF_INET6,
00380 .tx = ipv6_tx,
00381 };