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 * Add IPv6 minirouting table entry
51 * @v netdev Network device
52 * @v prefix Destination prefix (in bits)
53 * @v address Address of the interface
54 * @v gateway Gateway address (or ::0 for no gateway)
55 * @ret miniroute Routing table entry, or NULL
57 static struct ipv6_miniroute
* __malloc
58 add_ipv6_miniroute ( struct net_device
*netdev
, struct in6_addr prefix
,
59 int prefix_len
, struct in6_addr address
,
60 struct in6_addr gateway
) {
61 struct ipv6_miniroute
*miniroute
;
63 DBG( "ipv6 add: %s/%d ", inet6_ntoa ( address
), prefix_len
);
64 DBG( "gw %s\n", inet6_ntoa( gateway
) );
66 miniroute
= malloc ( sizeof ( *miniroute
) );
68 /* Record routing information */
69 miniroute
->netdev
= netdev_get ( netdev
);
70 miniroute
->prefix
= prefix
;
71 miniroute
->prefix_len
= prefix_len
;
72 miniroute
->address
= address
;
73 miniroute
->gateway
= gateway
;
75 /* Add miniroute to list of miniroutes */
76 if ( !IP6_EQUAL ( gateway
, ip6_none
) ) {
77 list_add_tail ( &miniroute
->list
, &miniroutes
);
79 list_add ( &miniroute
->list
, &miniroutes
);
87 * Delete IPv6 minirouting table entry
89 * @v miniroute Routing table entry
91 static void del_ipv6_miniroute ( struct ipv6_miniroute
*miniroute
) {
93 DBG ( "ipv6 del: %s/%d\n", inet6_ntoa(miniroute
->address
),
94 miniroute
->prefix_len
);
96 netdev_put ( miniroute
->netdev
);
97 list_del ( &miniroute
->list
);
104 * @v netdev Network device
105 * @v prefix Destination prefix
106 * @v address Address of the interface
107 * @v gateway Gateway address (or ::0 for no gateway)
109 int add_ipv6_address ( struct net_device
*netdev
, struct in6_addr prefix
,
110 int prefix_len
, struct in6_addr address
,
111 struct in6_addr gateway
) {
112 struct ipv6_miniroute
*miniroute
;
114 /* Clear any existing address for this net device */
115 /* del_ipv6_address ( netdev ); */
117 /* Add new miniroute */
118 miniroute
= add_ipv6_miniroute ( netdev
, prefix
, prefix_len
, address
,
127 * Remove IPv6 interface
129 * @v netdev Network device
131 void del_ipv6_address ( struct net_device
*netdev
) {
132 struct ipv6_miniroute
*miniroute
;
134 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
135 if ( miniroute
->netdev
== netdev
) {
136 del_ipv6_miniroute ( miniroute
);
143 * Calculate TCPIP checksum
145 * @v iobuf I/O buffer
146 * @v tcpip TCP/IP protocol
148 * This function constructs the pseudo header and completes the checksum in the
149 * upper layer header.
151 static uint16_t ipv6_tx_csum ( struct io_buffer
*iobuf
, uint16_t csum
) {
152 struct ip6_header
*ip6hdr
= iobuf
->data
;
153 struct ipv6_pseudo_header pshdr
;
155 /* Calculate pseudo header */
156 memset ( &pshdr
, 0, sizeof ( pshdr
) );
157 pshdr
.src
= ip6hdr
->src
;
158 pshdr
.dest
= ip6hdr
->dest
;
159 pshdr
.len
= htons ( iob_len ( iobuf
) - sizeof ( *ip6hdr
) );
160 pshdr
.nxt_hdr
= ip6hdr
->nxt_hdr
;
162 /* Update checksum value */
163 return tcpip_continue_chksum ( csum
, &pshdr
, sizeof ( pshdr
) );
167 * Dump IP6 header for debugging
171 void ipv6_dump ( struct ip6_header
*ip6hdr
) {
172 /* Because inet6_ntoa returns a static char[16], each call needs to be
174 DBG ( "IP6 %p src %s ", ip6hdr
, inet6_ntoa( ip6hdr
->src
) );
175 DBG ( "dest %s nxt_hdr %d len %d\n", inet6_ntoa ( ip6hdr
->dest
),
176 ip6hdr
->nxt_hdr
, ntohs ( ip6hdr
->payload_len
) );
180 * Transmit IP6 packet
183 * tcpip TCP/IP protocol
184 * st_dest Destination socket address
186 * This function prepends the IPv6 headers to the payload an transmits it.
188 static int ipv6_tx ( struct io_buffer
*iobuf
,
189 struct tcpip_protocol
*tcpip
,
190 struct sockaddr_tcpip
*st_src __unused
,
191 struct sockaddr_tcpip
*st_dest
,
192 struct net_device
*netdev
,
193 uint16_t *trans_csum
) {
194 struct sockaddr_in6
*dest
= ( struct sockaddr_in6
* ) st_dest
;
195 struct in6_addr next_hop
, gateway
= ip6_none
;
196 struct ipv6_miniroute
*miniroute
;
197 uint8_t ll_dest_buf
[MAX_LL_ADDR_LEN
], ip1
, ip2
;
198 const uint8_t *ll_dest
= ll_dest_buf
;
199 int rc
, multicast
, linklocal
, bits
, offset
;
201 /* Check for multicast transmission. */
202 multicast
= dest
->sin6_addr
.in6_u
.u6_addr8
[0] == 0xFF;
204 /* Construct the IPv6 packet */
205 struct ip6_header
*ip6hdr
= iob_push ( iobuf
, sizeof ( *ip6hdr
) );
206 memset ( ip6hdr
, 0, sizeof ( *ip6hdr
) );
207 ip6hdr
->ver_traffic_class_flow_label
= htonl ( 0x60000000 );//IP6_VERSION;
208 ip6hdr
->payload_len
= htons ( iob_len ( iobuf
) - sizeof ( *ip6hdr
) );
209 ip6hdr
->nxt_hdr
= tcpip
->tcpip_proto
;
210 ip6hdr
->hop_limit
= IP6_HOP_LIMIT
; // 255
212 /* Determine the next hop address and interface. */
213 next_hop
= dest
->sin6_addr
;
214 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
215 /* Link-local route? */
216 linklocal
= (miniroute
->address
.in6_u
.u6_addr16
[0] & htons(0xFE80)) == htons(0xFE80);
218 /* Handle link-local for multicast. */
221 /* Link-local scope? */
222 if ( next_hop
.in6_u
.u6_addr8
[0] & 0x2 ) {
224 netdev
= miniroute
->netdev
;
225 ip6hdr
->src
= miniroute
->address
;
228 /* Should be link-local address. */
232 /* Can we route on this interface?
233 (assume non-link-local means routable) */
235 netdev
= miniroute
->netdev
;
236 ip6hdr
->src
= miniroute
->address
;
242 /* Check for a prefix match on the route. */
243 if ( ! memcmp ( &next_hop
, &miniroute
->prefix
, miniroute
->prefix_len
/ 8 ) ) {
246 /* Handle extra bits in the prefix. */
247 if ( ( miniroute
->prefix_len
% 8 ) ||
248 ( miniroute
->prefix_len
< 8 ) ) {
249 DBG ( "ipv6: prefix is not aligned to a byte.\n" );
251 /* Compare the remaining bits. */
252 offset
= miniroute
->prefix_len
/ 8;
253 bits
= miniroute
->prefix_len
% 8;
255 ip1
= next_hop
.in6_u
.u6_addr8
[offset
];
256 ip2
= miniroute
->prefix
.in6_u
.u6_addr8
[offset
];
257 if ( ! ( ( ip1
& (0xFF >> (8 - bits
)) ) &
268 netdev
= miniroute
->netdev
;
269 ip6hdr
->src
= miniroute
->address
;
273 if ( ( ! ( IP6_EQUAL ( miniroute
->gateway
, ip6_none
) ) ) &&
274 ( IP6_EQUAL ( gateway
, ip6_none
) ) ) {
275 netdev
= miniroute
->netdev
;
276 ip6hdr
->src
= miniroute
->address
;
277 gateway
= miniroute
->gateway
;
280 /* No network interface identified */
281 if ( ( ! netdev
) ) {
282 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr
->dest
) );
285 } else if ( ! IP6_EQUAL ( gateway
, ip6_none
) ) {
289 /* Add the next hop to the packet. */
290 ip6hdr
->dest
= dest
->sin6_addr
;
292 /* Complete the transport layer checksum */
294 *trans_csum
= ipv6_tx_csum ( iobuf
, *trans_csum
);
296 /* Print IPv6 header */
297 /* ipv6_dump ( ip6hdr ); */
299 /* Resolve link layer address */
300 if ( next_hop
.in6_u
.u6_addr8
[0] == 0xFF ) {
301 ll_dest_buf
[0] = 0x33;
302 ll_dest_buf
[1] = 0x33;
303 ll_dest_buf
[2] = next_hop
.in6_u
.u6_addr8
[12];
304 ll_dest_buf
[3] = next_hop
.in6_u
.u6_addr8
[13];
305 ll_dest_buf
[4] = next_hop
.in6_u
.u6_addr8
[14];
306 ll_dest_buf
[5] = next_hop
.in6_u
.u6_addr8
[15];
308 /* Unicast address needs to be resolved by NDP */
309 if ( ( rc
= ndp_resolve ( netdev
, &next_hop
, &ip6hdr
->src
,
310 ll_dest_buf
) ) != 0 ) {
311 DBG ( "No entry for %s\n", inet6_ntoa ( next_hop
) );
316 /* Transmit packet */
317 return net_tx ( iobuf
, netdev
, &ipv6_protocol
, ll_dest
);
325 * Process next IP6 header
327 * @v iobuf I/O buffer
328 * @v nxt_hdr Next header number
329 * @v src Source socket address
330 * @v dest Destination socket address
331 * @v netdev Net device the packet arrived on
332 * @v phcsm Partial checksum over the IPv6 psuedo-header.
334 * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
336 static int ipv6_process_nxt_hdr ( struct io_buffer
*iobuf
, uint8_t nxt_hdr
,
337 struct sockaddr_tcpip
*src
, struct sockaddr_tcpip
*dest
,
338 struct net_device
*netdev
, uint16_t phcsm
) {
343 case IP6_AUTHENTICATION
:
346 DBG ( "Function not implemented for header %d\n", nxt_hdr
);
349 return icmp6_rx ( iobuf
, src
, dest
, netdev
, phcsm
);
351 DBG ( "No next header\n" );
354 /* Next header is not a IPv6 extension header */
355 return tcpip_rx ( iobuf
, nxt_hdr
, src
, dest
, phcsm
);
359 * Process incoming IP6 packets
361 * @v iobuf I/O buffer
362 * @v netdev Network device
363 * @v ll_source Link-layer source address
365 * This function processes a IPv6 packet
367 static int ipv6_rx ( struct io_buffer
*iobuf
,
368 __unused
struct net_device
*netdev
,
369 __unused
const void *ll_source
) {
371 struct ip6_header
*ip6hdr
= iobuf
->data
;
373 struct sockaddr_in6 sin6
;
374 struct sockaddr_tcpip st
;
379 if ( iob_len ( iobuf
) < sizeof ( *ip6hdr
) ) {
380 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf
) );
384 /* Print IP6 header for debugging */
385 /* ipv6_dump ( ip6hdr ); */
387 /* Check header version */
388 if ( ( ntohl( ip6hdr
->ver_traffic_class_flow_label
) & 0xf0000000 ) != 0x60000000 ) {
389 DBG ( "Invalid protocol version\n" );
393 /* Check the payload length */
394 if ( ntohs ( ip6hdr
->payload_len
) > iob_len ( iobuf
) ) {
395 DBG ( "Inconsistent packet length (%d bytes)\n",
396 ip6hdr
->payload_len
);
400 /* Ignore the traffic class and flow control values */
402 /* Construct socket address */
403 memset ( &src
, 0, sizeof ( src
) );
404 src
.sin6
.sin_family
= AF_INET6
;
405 src
.sin6
.sin6_addr
= ip6hdr
->src
;
406 memset ( &dest
, 0, sizeof ( dest
) );
407 dest
.sin6
.sin_family
= AF_INET6
;
408 dest
.sin6
.sin6_addr
= ip6hdr
->dest
;
410 /* Calculate the psuedo-header checksum before the IP6 header is
412 phcsm
= ipv6_tx_csum ( iobuf
, 0 );
415 iob_unput ( iobuf
, iob_len ( iobuf
) - ntohs ( ip6hdr
->payload_len
) -
416 sizeof ( *ip6hdr
) );
417 iob_pull ( iobuf
, sizeof ( *ip6hdr
) );
419 /* Send it to the transport layer */
420 return ipv6_process_nxt_hdr ( iobuf
, ip6hdr
->nxt_hdr
, &src
.st
, &dest
.st
,
424 DBG ( "IP6 packet dropped\n" );
430 * Convert an IPv6 address to a string.
432 * @v in6 Address to convert to string.
434 * Converts an IPv6 address to a string, and applies zero-compression as needed
435 * to condense the address for easier reading/typing.
437 char * inet6_ntoa ( struct in6_addr in6
) {
439 uint16_t *bytes
= ( uint16_t* ) &in6
;
440 size_t i
= 0, longest
= 0, tmp
= 0, long_idx
= ~0;
443 if ( IP6_EQUAL ( in6
, ip6_none
) ) {
444 tmp
= sprintf ( buf
, "::0" );
449 /* Determine the longest string of zeroes for zero-compression. */
450 for ( ; i
< 8; i
++ ) {
453 else if(tmp
> longest
) {
455 long_idx
= i
- longest
;
461 /* Check for last word being zero. This will cause long_idx to be zero,
462 * which confuses the actual buffer fill code. */
463 if(tmp
&& (tmp
> longest
)) {
465 long_idx
= 8 - longest
;
468 /* Inject into the buffer. */
470 for ( i
= 0; i
< 8; i
++ ) {
471 /* Should we skip over a string of zeroes? */
472 if ( i
== long_idx
) {
474 tmp
+= sprintf( buf
+ tmp
, ":" );
476 /* Handle end-of-string. */
481 /* Insert this component of the address. */
482 tmp
+= sprintf(buf
+ tmp
, "%x", ntohs(bytes
[i
]));
484 /* Add the next colon, if needed. */
486 tmp
+= sprintf( buf
+ tmp
, ":" );
495 * Convert a string to an IPv6 address.
497 * @v in6 String to convert to an address.
499 int inet6_aton ( const char *cp
, struct in6_addr
*inp
) {
501 char *tmp
= convbuf
, *next
= convbuf
;
504 strcpy ( convbuf
, cp
);
506 DBG ( "ipv6 converting %s to an in6_addr\n", cp
);
508 /* Handle the first part of the address (or all of it if no zero-compression. */
509 while ( ( next
= strchr ( next
, ':' ) ) ) {
510 /* Cater for zero-compression. */
514 /* Convert to integer. */
515 inp
->s6_addr16
[i
++] = htons( strtoul ( tmp
, 0, 16 ) );
521 /* Handle the case where no zero-compression is needed, but every word
522 * was filled in the address. */
523 if ( ( i
== 7 ) && ( *tmp
!= ':' ) ) {
524 inp
->s6_addr16
[i
++] = htons( strtoul ( tmp
, 0, 16 ) );
528 /* Handle zero-compression now (go backwards). */
530 if ( i
&& ( *tmp
== ':' ) ) {
531 next
= strrchr ( next
, ':' );
537 /* Convert to integer. */
538 inp
->s6_addr16
[i
--] = htons( strtoul ( tmp
, 0, 16 ) );
539 } while ( ( next
= strrchr ( next
, ':' ) ) );
546 static const char * ipv6_ntoa ( const void *net_addr
) {
547 return inet6_ntoa ( * ( ( struct in6_addr
* ) net_addr
) );
550 static int ipv6_check ( struct net_device
*netdev
, const void *net_addr
) {
551 const struct in6_addr
*address
= net_addr
;
552 struct ipv6_miniroute
*miniroute
;
554 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
555 if ( ( miniroute
->netdev
== netdev
) &&
556 ( ! memcmp ( &miniroute
->address
, address
, 16 ) ) ) {
557 /* Found matching address */
565 struct net_protocol ipv6_protocol __net_protocol
= {
567 .net_proto
= htons ( ETH_P_IPV6
),
568 .net_addr_len
= sizeof ( struct in6_addr
),
573 /** IPv6 TCPIP net protocol */
574 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol
= {
576 .sa_family
= AF_INET6
,
580 /** IPv6 ICMPv6 protocol, for NDP */
581 struct icmp6_net_protocol ipv6_icmp6_protocol __icmp6_net_protocol
= {
582 .net_protocol
= &ipv6_protocol
,