Home | History | Annotate | Download | only in net
      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 /* Unspecified IP6 address */
     21 static struct in6_addr ip6_none = {
     22         .in6_u.u6_addr32 = { 0,0,0,0 }
     23 };
     24 
     25 /** An IPv6 routing table entry */
     26 struct ipv6_miniroute {
     27 	/* List of miniroutes */
     28 	struct list_head list;
     29 
     30 	/* Network device */
     31 	struct net_device *netdev;
     32 
     33 	/* Destination prefix */
     34 	struct in6_addr prefix;
     35 	/* Prefix length */
     36 	int prefix_len;
     37 	/* IPv6 address of interface */
     38 	struct in6_addr address;
     39 	/* Gateway address */
     40 	struct in6_addr gateway;
     41 };
     42 
     43 /** List of IPv6 miniroutes */
     44 static LIST_HEAD ( miniroutes );
     45 
     46 /**
     47  * Add IPv6 minirouting table entry
     48  *
     49  * @v netdev		Network device
     50  * @v prefix		Destination prefix
     51  * @v address		Address of the interface
     52  * @v gateway		Gateway address (or ::0 for no gateway)
     53  * @ret miniroute	Routing table entry, or NULL
     54  */
     55 static struct ipv6_miniroute * __malloc
     56 add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
     57 		     int prefix_len, struct in6_addr address,
     58 		     struct in6_addr gateway ) {
     59 	struct ipv6_miniroute *miniroute;
     60 
     61 	miniroute = malloc ( sizeof ( *miniroute ) );
     62 	if ( miniroute ) {
     63 		/* Record routing information */
     64 		miniroute->netdev = netdev_get ( netdev );
     65 		miniroute->prefix = prefix;
     66 		miniroute->prefix_len = prefix_len;
     67 		miniroute->address = address;
     68 		miniroute->gateway = gateway;
     69 
     70 		/* Add miniroute to list of miniroutes */
     71 		if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
     72 			list_add_tail ( &miniroute->list, &miniroutes );
     73 		} else {
     74 			list_add ( &miniroute->list, &miniroutes );
     75 		}
     76 	}
     77 
     78 	return miniroute;
     79 }
     80 
     81 /**
     82  * Delete IPv6 minirouting table entry
     83  *
     84  * @v miniroute		Routing table entry
     85  */
     86 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
     87 	netdev_put ( miniroute->netdev );
     88 	list_del ( &miniroute->list );
     89 	free ( miniroute );
     90 }
     91 
     92 /**
     93  * Add IPv6 interface
     94  *
     95  * @v netdev	Network device
     96  * @v prefix	Destination prefix
     97  * @v address	Address of the interface
     98  * @v gateway	Gateway address (or ::0 for no gateway)
     99  */
    100 int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
    101 		       int prefix_len, struct in6_addr address,
    102 		       struct in6_addr gateway ) {
    103 	struct ipv6_miniroute *miniroute;
    104 
    105 	/* Clear any existing address for this net device */
    106 	del_ipv6_address ( netdev );
    107 
    108 	/* Add new miniroute */
    109 	miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
    110 					 gateway );
    111 	if ( ! miniroute )
    112 		return -ENOMEM;
    113 
    114 	return 0;
    115 }
    116 
    117 /**
    118  * Remove IPv6 interface
    119  *
    120  * @v netdev	Network device
    121  */
    122 void del_ipv6_address ( struct net_device *netdev ) {
    123 	struct ipv6_miniroute *miniroute;
    124 
    125 	list_for_each_entry ( miniroute, &miniroutes, list ) {
    126 		if ( miniroute->netdev == netdev ) {
    127 			del_ipv6_miniroute ( miniroute );
    128 			break;
    129 		}
    130 	}
    131 }
    132 
    133 /**
    134  * Calculate TCPIP checksum
    135  *
    136  * @v iobuf	I/O buffer
    137  * @v tcpip	TCP/IP protocol
    138  *
    139  * This function constructs the pseudo header and completes the checksum in the
    140  * upper layer header.
    141  */
    142 static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
    143 	struct ip6_header *ip6hdr = iobuf->data;
    144 	struct ipv6_pseudo_header pshdr;
    145 
    146 	/* Calculate pseudo header */
    147 	memset ( &pshdr, 0, sizeof ( pshdr ) );
    148 	pshdr.src = ip6hdr->src;
    149 	pshdr.dest = ip6hdr->dest;
    150 	pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
    151 	pshdr.nxt_hdr = ip6hdr->nxt_hdr;
    152 
    153 	/* Update checksum value */
    154 	return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
    155 }
    156 
    157 /**
    158  * Dump IP6 header for debugging
    159  *
    160  * ip6hdr	IPv6 header
    161  */
    162 void ipv6_dump ( struct ip6_header *ip6hdr ) {
    163 	DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
    164 	      inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
    165 	      ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
    166 }
    167 
    168 /**
    169  * Transmit IP6 packet
    170  *
    171  * iobuf		I/O buffer
    172  * tcpip	TCP/IP protocol
    173  * st_dest	Destination socket address
    174  *
    175  * This function prepends the IPv6 headers to the payload an transmits it.
    176  */
    177 static int ipv6_tx ( struct io_buffer *iobuf,
    178 		     struct tcpip_protocol *tcpip,
    179 		     struct sockaddr_tcpip *st_src __unused,
    180 		     struct sockaddr_tcpip *st_dest,
    181 		     struct net_device *netdev,
    182 		     uint16_t *trans_csum ) {
    183 	struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
    184 	struct in6_addr next_hop;
    185 	struct ipv6_miniroute *miniroute;
    186 	uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
    187 	const uint8_t *ll_dest = ll_dest_buf;
    188 	int rc;
    189 
    190 	/* Construct the IPv6 packet */
    191 	struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
    192 	memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
    193 	ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
    194 	ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
    195 	ip6hdr->nxt_hdr = tcpip->tcpip_proto;
    196 	ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
    197 
    198 	/* Determine the next hop address and interface
    199 	 *
    200 	 * TODO: Implement the routing table.
    201 	 */
    202 	next_hop = dest->sin6_addr;
    203 	list_for_each_entry ( miniroute, &miniroutes, list ) {
    204 		if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix,
    205 					miniroute->prefix_len ) == 0 ) ||
    206 		     ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
    207 			netdev = miniroute->netdev;
    208 			ip6hdr->src = miniroute->address;
    209 			if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
    210 				next_hop = miniroute->gateway;
    211 			}
    212 			break;
    213 		}
    214 	}
    215 	/* No network interface identified */
    216 	if ( !netdev ) {
    217 		DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
    218 		rc = -ENETUNREACH;
    219 		goto err;
    220 	}
    221 
    222 	/* Complete the transport layer checksum */
    223 	if ( trans_csum )
    224 		*trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
    225 
    226 	/* Print IPv6 header */
    227 	ipv6_dump ( ip6hdr );
    228 
    229 	/* Resolve link layer address */
    230 	if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
    231 		ll_dest_buf[0] = 0x33;
    232 		ll_dest_buf[1] = 0x33;
    233 		ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
    234 		ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
    235 		ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
    236 		ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
    237 	} else {
    238 		/* Unicast address needs to be resolved by NDP */
    239 		if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
    240 					  ll_dest_buf ) ) != 0 ) {
    241 			DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
    242 			goto err;
    243 		}
    244 	}
    245 
    246 	/* Transmit packet */
    247 	return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
    248 
    249   err:
    250 	free_iob ( iobuf );
    251 	return rc;
    252 }
    253 
    254 /**
    255  * Process next IP6 header
    256  *
    257  * @v iobuf	I/O buffer
    258  * @v nxt_hdr	Next header number
    259  * @v src	Source socket address
    260  * @v dest	Destination socket address
    261  *
    262  * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
    263  */
    264 static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
    265 		struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
    266 	switch ( nxt_hdr ) {
    267 	case IP6_HOPBYHOP:
    268 	case IP6_ROUTING:
    269 	case IP6_FRAGMENT:
    270 	case IP6_AUTHENTICATION:
    271 	case IP6_DEST_OPTS:
    272 	case IP6_ESP:
    273 		DBG ( "Function not implemented for header %d\n", nxt_hdr );
    274 		return -ENOSYS;
    275 	case IP6_ICMP6:
    276 		break;
    277 	case IP6_NO_HEADER:
    278 		DBG ( "No next header\n" );
    279 		return 0;
    280 	}
    281 	/* Next header is not a IPv6 extension header */
    282 	return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ );
    283 }
    284 
    285 /**
    286  * Process incoming IP6 packets
    287  *
    288  * @v iobuf		I/O buffer
    289  * @v netdev		Network device
    290  * @v ll_source		Link-layer source address
    291  *
    292  * This function processes a IPv6 packet
    293  */
    294 static int ipv6_rx ( struct io_buffer *iobuf,
    295 		     __unused struct net_device *netdev,
    296 		     __unused const void *ll_source ) {
    297 
    298 	struct ip6_header *ip6hdr = iobuf->data;
    299 	union {
    300 		struct sockaddr_in6 sin6;
    301 		struct sockaddr_tcpip st;
    302 	} src, dest;
    303 
    304 	/* Sanity check */
    305 	if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
    306 		DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
    307 		goto drop;
    308 	}
    309 
    310 	/* TODO: Verify checksum */
    311 
    312 	/* Print IP6 header for debugging */
    313 	ipv6_dump ( ip6hdr );
    314 
    315 	/* Check header version */
    316 	if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
    317 		DBG ( "Invalid protocol version\n" );
    318 		goto drop;
    319 	}
    320 
    321 	/* Check the payload length */
    322 	if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
    323 		DBG ( "Inconsistent packet length (%d bytes)\n",
    324 			ip6hdr->payload_len );
    325 		goto drop;
    326 	}
    327 
    328 	/* Ignore the traffic class and flow control values */
    329 
    330 	/* Construct socket address */
    331 	memset ( &src, 0, sizeof ( src ) );
    332 	src.sin6.sin_family = AF_INET6;
    333 	src.sin6.sin6_addr = ip6hdr->src;
    334 	memset ( &dest, 0, sizeof ( dest ) );
    335 	dest.sin6.sin_family = AF_INET6;
    336 	dest.sin6.sin6_addr = ip6hdr->dest;
    337 
    338 	/* Strip header */
    339 	iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
    340 							sizeof ( *ip6hdr ) );
    341 	iob_pull ( iobuf, sizeof ( *ip6hdr ) );
    342 
    343 	/* Send it to the transport layer */
    344 	return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
    345 
    346   drop:
    347 	DBG ( "Packet dropped\n" );
    348 	free_iob ( iobuf );
    349 	return -1;
    350 }
    351 
    352 /**
    353  * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
    354  */
    355 char * inet6_ntoa ( struct in6_addr in6 ) {
    356 	static char buf[40];
    357 	uint16_t *bytes = ( uint16_t* ) &in6;
    358 	sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
    359 			bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
    360 	return buf;
    361 }
    362 
    363 static const char * ipv6_ntoa ( const void *net_addr ) {
    364 	return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
    365 }
    366 
    367 /** IPv6 protocol */
    368 struct net_protocol ipv6_protocol __net_protocol = {
    369 	.name = "IPv6",
    370 	.net_proto = htons ( ETH_P_IPV6 ),
    371 	.net_addr_len = sizeof ( struct in6_addr ),
    372 	.rx = ipv6_rx,
    373 	.ntoa = ipv6_ntoa,
    374 };
    375 
    376 /** IPv6 TCPIP net protocol */
    377 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
    378 	.name = "IPv6",
    379 	.sa_family = AF_INET6,
    380 	.tx = ipv6_tx,
    381 };
    382