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