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>
18 struct net_protocol ipv6_protocol
;
20 char * inet6_ntoa ( struct in6_addr in6
);
22 /* Unspecified IP6 address */
23 static struct in6_addr ip6_none
= {
24 .in6_u
.u6_addr32
= { 0,0,0,0 }
27 /** An IPv6 routing table entry */
28 struct ipv6_miniroute
{
29 /* List of miniroutes */
30 struct list_head list
;
33 struct net_device
*netdev
;
35 /* Destination prefix */
36 struct in6_addr prefix
;
39 /* IPv6 address of interface */
40 struct in6_addr address
;
42 struct in6_addr gateway
;
45 /** List of IPv6 miniroutes */
46 static LIST_HEAD ( miniroutes
);
49 * Generate an EUI-64 from a given link-local address.
51 * @v out pointer to buffer to receive the EUI-64
52 * @v ll pointer to buffer containing the link-local address
54 void ipv6_generate_eui64 ( uint8_t *out
, uint8_t *ll
) {
58 /* Create an EUI-64 identifier. */
60 memcpy( out
+ 5, ll
+ 3, 3 );
64 /* Designate that this is in fact an EUI-64. */
69 * Add IPv6 minirouting table entry
71 * @v netdev Network device
72 * @v prefix Destination prefix (in bits)
73 * @v address Address of the interface
74 * @v gateway Gateway address (or ::0 for no gateway)
75 * @ret miniroute Routing table entry, or NULL
77 static struct ipv6_miniroute
* __malloc
78 add_ipv6_miniroute ( struct net_device
*netdev
, struct in6_addr prefix
,
79 int prefix_len
, struct in6_addr address
,
80 struct in6_addr gateway
) {
81 struct ipv6_miniroute
*miniroute
;
83 DBG( "ipv6 add: %s/%d ", inet6_ntoa ( address
), prefix_len
);
84 DBG( "gw %s\n", inet6_ntoa( gateway
) );
86 miniroute
= malloc ( sizeof ( *miniroute
) );
88 /* Record routing information */
89 miniroute
->netdev
= netdev_get ( netdev
);
90 miniroute
->prefix
= prefix
;
91 miniroute
->prefix_len
= prefix_len
;
92 miniroute
->address
= address
;
93 miniroute
->gateway
= gateway
;
95 /* Add miniroute to list of miniroutes */
96 if ( !IP6_EQUAL ( gateway
, ip6_none
) ) {
97 list_add_tail ( &miniroute
->list
, &miniroutes
);
99 list_add ( &miniroute
->list
, &miniroutes
);
107 * Delete IPv6 minirouting table entry
109 * @v miniroute Routing table entry
111 static void del_ipv6_miniroute ( struct ipv6_miniroute
*miniroute
) {
113 DBG ( "ipv6 del: %s/%d\n", inet6_ntoa(miniroute
->address
),
114 miniroute
->prefix_len
);
116 netdev_put ( miniroute
->netdev
);
117 list_del ( &miniroute
->list
);
124 * @v netdev Network device
125 * @v prefix Destination prefix
126 * @v address Address of the interface
127 * @v gateway Gateway address (or ::0 for no gateway)
129 int add_ipv6_address ( struct net_device
*netdev
, struct in6_addr prefix
,
130 int prefix_len
, struct in6_addr address
,
131 struct in6_addr gateway
) {
132 struct ipv6_miniroute
*miniroute
;
134 /* Clear any existing address for this net device */
135 /* del_ipv6_address ( netdev ); */
137 /* Add new miniroute */
138 miniroute
= add_ipv6_miniroute ( netdev
, prefix
, prefix_len
, address
,
147 * Remove IPv6 interface
149 * @v netdev Network device
151 void del_ipv6_address ( struct net_device
*netdev
) {
152 struct ipv6_miniroute
*miniroute
;
154 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
155 if ( miniroute
->netdev
== netdev
) {
156 del_ipv6_miniroute ( miniroute
);
163 * Calculate TCPIP checksum
165 * @v iobuf I/O buffer
166 * @v tcpip TCP/IP protocol
168 * This function constructs the pseudo header and completes the checksum in the
169 * upper layer header.
171 static uint16_t ipv6_tx_csum ( struct io_buffer
*iobuf
, uint16_t csum
) {
172 struct ip6_header
*ip6hdr
= iobuf
->data
;
173 struct ipv6_pseudo_header pshdr
;
175 /* Calculate pseudo header */
176 memset ( &pshdr
, 0, sizeof ( pshdr
) );
177 pshdr
.src
= ip6hdr
->src
;
178 pshdr
.dest
= ip6hdr
->dest
;
179 pshdr
.len
= htons ( iob_len ( iobuf
) - sizeof ( *ip6hdr
) );
180 pshdr
.nxt_hdr
= ip6hdr
->nxt_hdr
;
182 /* Update checksum value */
183 return tcpip_continue_chksum ( csum
, &pshdr
, sizeof ( pshdr
) );
187 * Dump IP6 header for debugging
191 void ipv6_dump ( struct ip6_header
*ip6hdr
) {
192 /* Because inet6_ntoa returns a static char[16], each call needs to be
194 DBG ( "IP6 %p src %s ", ip6hdr
, inet6_ntoa( ip6hdr
->src
) );
195 DBG ( "dest %s nxt_hdr %d len %d\n", inet6_ntoa ( ip6hdr
->dest
),
196 ip6hdr
->nxt_hdr
, ntohs ( ip6hdr
->payload_len
) );
200 * Transmit IP6 packet
203 * tcpip TCP/IP protocol
204 * st_dest Destination socket address
206 * This function prepends the IPv6 headers to the payload an transmits it.
208 static int ipv6_tx ( struct io_buffer
*iobuf
,
209 struct tcpip_protocol
*tcpip
,
210 struct sockaddr_tcpip
*st_src __unused
,
211 struct sockaddr_tcpip
*st_dest
,
212 struct net_device
*netdev
,
213 uint16_t *trans_csum
) {
214 struct sockaddr_in6
*dest
= ( struct sockaddr_in6
* ) st_dest
;
215 struct in6_addr next_hop
, gateway
= ip6_none
;
216 struct ipv6_miniroute
*miniroute
;
217 uint8_t ll_dest_buf
[MAX_LL_ADDR_LEN
], ip1
, ip2
;
218 const uint8_t *ll_dest
= ll_dest_buf
;
219 int rc
, multicast
, linklocal
, bits
, offset
;
221 /* Check for multicast transmission. */
222 multicast
= dest
->sin6_addr
.in6_u
.u6_addr8
[0] == 0xFF;
224 /* Construct the IPv6 packet */
225 struct ip6_header
*ip6hdr
= iob_push ( iobuf
, sizeof ( *ip6hdr
) );
226 memset ( ip6hdr
, 0, sizeof ( *ip6hdr
) );
227 ip6hdr
->ver_traffic_class_flow_label
= htonl ( 0x60000000 );//IP6_VERSION;
228 ip6hdr
->payload_len
= htons ( iob_len ( iobuf
) - sizeof ( *ip6hdr
) );
229 ip6hdr
->nxt_hdr
= tcpip
->tcpip_proto
;
230 ip6hdr
->hop_limit
= IP6_HOP_LIMIT
; // 255
232 /* Determine the next hop address and interface. */
233 next_hop
= dest
->sin6_addr
;
234 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
235 /* Check for specific netdev */
236 if ( netdev
&& ( miniroute
->netdev
!= netdev
) ) {
240 /* Link-local route? */
241 linklocal
= (miniroute
->address
.in6_u
.u6_addr16
[0] & htons(0xFE80)) == htons(0xFE80);
243 /* Handle link-local for multicast. */
246 /* Link-local scope? */
247 if ( next_hop
.in6_u
.u6_addr8
[0] & 0x2 ) {
249 netdev
= miniroute
->netdev
;
250 ip6hdr
->src
= miniroute
->address
;
253 /* Should be link-local address. */
257 /* Can we route on this interface?
258 (assume non-link-local means routable) */
260 netdev
= miniroute
->netdev
;
261 ip6hdr
->src
= miniroute
->address
;
267 /* Check for a prefix match on the route. */
268 if ( ! memcmp ( &next_hop
, &miniroute
->prefix
, miniroute
->prefix_len
/ 8 ) ) {
271 /* Handle extra bits in the prefix. */
272 if ( ( miniroute
->prefix_len
% 8 ) ||
273 ( miniroute
->prefix_len
< 8 ) ) {
274 DBG ( "ipv6: prefix is not aligned to a byte.\n" );
276 /* Compare the remaining bits. */
277 offset
= miniroute
->prefix_len
/ 8;
278 bits
= miniroute
->prefix_len
% 8;
280 ip1
= next_hop
.in6_u
.u6_addr8
[offset
];
281 ip2
= miniroute
->prefix
.in6_u
.u6_addr8
[offset
];
282 if ( ! ( ( ip1
& (0xFF >> (8 - bits
)) ) &
293 netdev
= miniroute
->netdev
;
294 ip6hdr
->src
= miniroute
->address
;
298 if ( ( ! ( IP6_EQUAL ( miniroute
->gateway
, ip6_none
) ) ) &&
299 ( IP6_EQUAL ( gateway
, ip6_none
) ) ) {
300 netdev
= miniroute
->netdev
;
301 ip6hdr
->src
= miniroute
->address
;
302 gateway
= miniroute
->gateway
;
305 /* No network interface identified */
306 if ( ( ! netdev
) ) {
307 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr
->dest
) );
310 } else if ( ! IP6_EQUAL ( gateway
, ip6_none
) ) {
314 /* Add the next hop to the packet. */
315 ip6hdr
->dest
= dest
->sin6_addr
;
317 /* Complete the transport layer checksum */
319 *trans_csum
= ipv6_tx_csum ( iobuf
, *trans_csum
);
321 /* Print IPv6 header */
322 /* ipv6_dump ( ip6hdr ); */
324 /* Resolve link layer address */
325 if ( next_hop
.in6_u
.u6_addr8
[0] == 0xFF ) {
326 ll_dest_buf
[0] = 0x33;
327 ll_dest_buf
[1] = 0x33;
328 ll_dest_buf
[2] = next_hop
.in6_u
.u6_addr8
[12];
329 ll_dest_buf
[3] = next_hop
.in6_u
.u6_addr8
[13];
330 ll_dest_buf
[4] = next_hop
.in6_u
.u6_addr8
[14];
331 ll_dest_buf
[5] = next_hop
.in6_u
.u6_addr8
[15];
333 /* Unicast address needs to be resolved by NDP */
334 if ( ( rc
= ndp_resolve ( netdev
, &next_hop
, &ip6hdr
->src
,
335 ll_dest_buf
) ) != 0 ) {
336 DBG ( "No entry for %s\n", inet6_ntoa ( next_hop
) );
341 /* Transmit packet */
342 return net_tx ( iobuf
, netdev
, &ipv6_protocol
, ll_dest
);
350 * Process next IP6 header
352 * @v iobuf I/O buffer
353 * @v nxt_hdr Next header number
354 * @v src Source socket address
355 * @v dest Destination socket address
356 * @v netdev Net device the packet arrived on
357 * @v phcsm Partial checksum over the IPv6 psuedo-header.
359 * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
361 static int ipv6_process_nxt_hdr ( struct io_buffer
*iobuf
, uint8_t nxt_hdr
,
362 struct sockaddr_tcpip
*src
, struct sockaddr_tcpip
*dest
,
363 struct net_device
*netdev
, uint16_t phcsm
) {
368 case IP6_AUTHENTICATION
:
371 DBG ( "Function not implemented for header %d\n", nxt_hdr
);
374 return icmp6_rx ( iobuf
, src
, dest
, netdev
, phcsm
);
376 DBG ( "No next header\n" );
379 /* Next header is not a IPv6 extension header */
380 return tcpip_rx ( iobuf
, nxt_hdr
, src
, dest
, phcsm
);
384 * Process incoming IP6 packets
386 * @v iobuf I/O buffer
387 * @v netdev Network device
388 * @v ll_source Link-layer source address
390 * This function processes a IPv6 packet
392 static int ipv6_rx ( struct io_buffer
*iobuf
,
393 __unused
struct net_device
*netdev
,
394 __unused
const void *ll_source
) {
396 struct ip6_header
*ip6hdr
= iobuf
->data
;
398 struct sockaddr_in6 sin6
;
399 struct sockaddr_tcpip st
;
404 if ( iob_len ( iobuf
) < sizeof ( *ip6hdr
) ) {
405 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf
) );
409 /* Print IP6 header for debugging */
410 /* ipv6_dump ( ip6hdr ); */
412 /* Check header version */
413 if ( ( ntohl( ip6hdr
->ver_traffic_class_flow_label
) & 0xf0000000 ) != 0x60000000 ) {
414 DBG ( "Invalid protocol version\n" );
418 /* Check the payload length */
419 if ( ntohs ( ip6hdr
->payload_len
) > iob_len ( iobuf
) ) {
420 DBG ( "Inconsistent packet length (%d bytes)\n",
421 ip6hdr
->payload_len
);
425 /* Ignore the traffic class and flow control values */
427 /* Construct socket address */
428 memset ( &src
, 0, sizeof ( src
) );
429 src
.sin6
.sin_family
= AF_INET6
;
430 src
.sin6
.sin6_addr
= ip6hdr
->src
;
431 memset ( &dest
, 0, sizeof ( dest
) );
432 dest
.sin6
.sin_family
= AF_INET6
;
433 dest
.sin6
.sin6_addr
= ip6hdr
->dest
;
435 /* Calculate the psuedo-header checksum before the IP6 header is
437 phcsm
= ipv6_tx_csum ( iobuf
, 0 );
440 iob_unput ( iobuf
, iob_len ( iobuf
) - ntohs ( ip6hdr
->payload_len
) -
441 sizeof ( *ip6hdr
) );
442 iob_pull ( iobuf
, sizeof ( *ip6hdr
) );
444 /* Send it to the transport layer */
445 return ipv6_process_nxt_hdr ( iobuf
, ip6hdr
->nxt_hdr
, &src
.st
, &dest
.st
,
449 DBG ( "IP6 packet dropped\n" );
455 * Convert an IPv6 address to a string.
457 * @v in6 Address to convert to string.
459 * Converts an IPv6 address to a string, and applies zero-compression as needed
460 * to condense the address for easier reading/typing.
462 char * inet6_ntoa ( struct in6_addr in6
) {
464 uint16_t *bytes
= ( uint16_t* ) &in6
;
465 size_t i
= 0, longest
= 0, tmp
= 0, long_idx
= ~0;
468 if ( IP6_EQUAL ( in6
, ip6_none
) ) {
469 tmp
= sprintf ( buf
, "::0" );
474 /* Determine the longest string of zeroes for zero-compression. */
475 for ( ; i
< 8; i
++ ) {
478 else if(tmp
> longest
) {
480 long_idx
= i
- longest
;
486 /* Check for last word being zero. This will cause long_idx to be zero,
487 * which confuses the actual buffer fill code. */
488 if(tmp
&& (tmp
> longest
)) {
490 long_idx
= 8 - longest
;
493 /* Inject into the buffer. */
495 for ( i
= 0; i
< 8; i
++ ) {
496 /* Should we skip over a string of zeroes? */
497 if ( i
== long_idx
) {
499 tmp
+= sprintf( buf
+ tmp
, ":" );
501 /* Handle end-of-string. */
506 /* Insert this component of the address. */
507 tmp
+= sprintf(buf
+ tmp
, "%x", ntohs(bytes
[i
]));
509 /* Add the next colon, if needed. */
511 tmp
+= sprintf( buf
+ tmp
, ":" );
520 * Convert a string to an IPv6 address.
522 * @v in6 String to convert to an address.
524 int inet6_aton ( const char *cp
, struct in6_addr
*inp
) {
526 char *tmp
= convbuf
, *next
= convbuf
;
527 size_t i
= 0, len
= strlen ( cp
);
531 /* Verify a valid address. */
536 for ( ; i
< len
; i
++ ) {
540 ok
= ok
|| ( ( c
>= '0' ) && ( c
<= '9' ) );
541 ok
= ok
|| ( ( c
>= 'a' ) && ( c
<= 'f' ) );
542 ok
= ok
|| ( ( c
>= 'A' ) && ( c
<= 'F' ) );
549 strcpy ( convbuf
, cp
);
551 DBG ( "ipv6 converting %s to an in6_addr\n", cp
);
553 /* Handle the first part of the address (or all of it if no zero-compression. */
555 while ( ( next
= strchr ( next
, ':' ) ) ) {
556 /* Cater for zero-compression. */
560 /* Convert to integer. */
561 inp
->s6_addr16
[i
++] = htons( strtoul ( tmp
, 0, 16 ) );
567 /* Handle the case where no zero-compression is needed, but every word
568 * was filled in the address. */
569 if ( ( i
== 7 ) && ( *tmp
!= ':' ) ) {
570 inp
->s6_addr16
[i
++] = htons( strtoul ( tmp
, 0, 16 ) );
574 /* Handle zero-compression now (go backwards). */
576 if ( i
&& ( *tmp
== ':' ) ) {
577 next
= strrchr ( next
, ':' );
583 /* Convert to integer. */
584 inp
->s6_addr16
[i
--] = htons( strtoul ( tmp
, 0, 16 ) );
585 } while ( ( next
= strrchr ( next
, ':' ) ) );
592 static const char * ipv6_ntoa ( const void *net_addr
) {
593 return inet6_ntoa ( * ( ( struct in6_addr
* ) net_addr
) );
596 static int ipv6_check ( struct net_device
*netdev
, const void *net_addr
) {
597 const struct in6_addr
*address
= net_addr
;
598 struct ipv6_miniroute
*miniroute
;
600 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
601 if ( ( miniroute
->netdev
== netdev
) &&
602 ( ! memcmp ( &miniroute
->address
, address
, 16 ) ) ) {
603 /* Found matching address */
611 struct net_protocol ipv6_protocol __net_protocol
= {
613 .net_proto
= htons ( ETH_P_IPV6
),
614 .net_addr_len
= sizeof ( struct in6_addr
),
619 /** IPv6 TCPIP net protocol */
620 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol
= {
622 .sa_family
= AF_INET6
,
626 /** IPv6 ICMPv6 protocol, for NDP */
627 struct icmp6_net_protocol ipv6_icmp6_protocol __icmp6_net_protocol
= {
628 .net_protocol
= &ipv6_protocol
,