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
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
* 8);
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
* 8);
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
;
196 struct ipv6_miniroute
*miniroute
;
197 uint8_t ll_dest_buf
[MAX_LL_ADDR_LEN
];
198 const uint8_t *ll_dest
= ll_dest_buf
;
201 /* Construct the IPv6 packet */
202 struct ip6_header
*ip6hdr
= iob_push ( iobuf
, sizeof ( *ip6hdr
) );
203 memset ( ip6hdr
, 0, sizeof ( *ip6hdr
) );
204 ip6hdr
->ver_traffic_class_flow_label
= htonl ( 0x60000000 );//IP6_VERSION;
205 ip6hdr
->payload_len
= htons ( iob_len ( iobuf
) - sizeof ( *ip6hdr
) );
206 ip6hdr
->nxt_hdr
= tcpip
->tcpip_proto
;
207 ip6hdr
->hop_limit
= IP6_HOP_LIMIT
; // 255
209 /* Determine the next hop address and interface
211 * TODO: Implement the routing table.
213 next_hop
= dest
->sin6_addr
;
214 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
215 if ( ( memcmp ( &ip6hdr
->dest
, &miniroute
->prefix
,
216 miniroute
->prefix_len
) == 0 ) ||
217 ( IP6_EQUAL ( miniroute
->gateway
, ip6_none
) ) ) {
218 netdev
= miniroute
->netdev
;
219 ip6hdr
->src
= miniroute
->address
;
220 if ( ! ( IS_UNSPECIFIED ( miniroute
->gateway
) ) ) {
221 next_hop
= miniroute
->gateway
;
226 /* No network interface identified */
228 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr
->dest
) );
233 /* Complete the transport layer checksum */
235 *trans_csum
= ipv6_tx_csum ( iobuf
, *trans_csum
);
237 /* Print IPv6 header */
238 ipv6_dump ( ip6hdr
);
240 /* Resolve link layer address */
241 if ( next_hop
.in6_u
.u6_addr8
[0] == 0xff ) {
242 ll_dest_buf
[0] = 0x33;
243 ll_dest_buf
[1] = 0x33;
244 ll_dest_buf
[2] = next_hop
.in6_u
.u6_addr8
[12];
245 ll_dest_buf
[3] = next_hop
.in6_u
.u6_addr8
[13];
246 ll_dest_buf
[4] = next_hop
.in6_u
.u6_addr8
[14];
247 ll_dest_buf
[5] = next_hop
.in6_u
.u6_addr8
[15];
249 /* Unicast address needs to be resolved by NDP */
250 if ( ( rc
= ndp_resolve ( netdev
, &next_hop
, &ip6hdr
->src
,
251 ll_dest_buf
) ) != 0 ) {
252 DBG ( "No entry for %s\n", inet6_ntoa ( next_hop
) );
257 /* Transmit packet */
258 return net_tx ( iobuf
, netdev
, &ipv6_protocol
, ll_dest
);
266 * Process next IP6 header
268 * @v iobuf I/O buffer
269 * @v nxt_hdr Next header number
270 * @v src Source socket address
271 * @v dest Destination socket address
272 * @v phcsm Partial checksum over the IPv6 psuedo-header.
274 * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
276 static int ipv6_process_nxt_hdr ( struct io_buffer
*iobuf
, uint8_t nxt_hdr
,
277 struct sockaddr_tcpip
*src
, struct sockaddr_tcpip
*dest
,
283 case IP6_AUTHENTICATION
:
286 DBG ( "Function not implemented for header %d\n", nxt_hdr
);
291 DBG ( "No next header\n" );
294 /* Next header is not a IPv6 extension header */
295 return tcpip_rx ( iobuf
, nxt_hdr
, src
, dest
, phcsm
);
299 * Process incoming IP6 packets
301 * @v iobuf I/O buffer
302 * @v netdev Network device
303 * @v ll_source Link-layer source address
305 * This function processes a IPv6 packet
307 static int ipv6_rx ( struct io_buffer
*iobuf
,
308 __unused
struct net_device
*netdev
,
309 __unused
const void *ll_source
) {
311 struct ip6_header
*ip6hdr
= iobuf
->data
;
313 struct sockaddr_in6 sin6
;
314 struct sockaddr_tcpip st
;
319 if ( iob_len ( iobuf
) < sizeof ( *ip6hdr
) ) {
320 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf
) );
324 /* Print IP6 header for debugging */
325 ipv6_dump ( ip6hdr
);
327 /* Check header version */
328 if ( ( ntohl( ip6hdr
->ver_traffic_class_flow_label
) & 0xf0000000 ) != 0x60000000 ) {
329 DBG ( "Invalid protocol version\n" );
333 /* Check the payload length */
334 if ( ntohs ( ip6hdr
->payload_len
) > iob_len ( iobuf
) ) {
335 DBG ( "Inconsistent packet length (%d bytes)\n",
336 ip6hdr
->payload_len
);
340 /* Ignore the traffic class and flow control values */
342 /* Construct socket address */
343 memset ( &src
, 0, sizeof ( src
) );
344 src
.sin6
.sin_family
= AF_INET6
;
345 src
.sin6
.sin6_addr
= ip6hdr
->src
;
346 memset ( &dest
, 0, sizeof ( dest
) );
347 dest
.sin6
.sin_family
= AF_INET6
;
348 dest
.sin6
.sin6_addr
= ip6hdr
->dest
;
350 /* Calculate the psuedo-header checksum before the IP6 header is
352 phcsm
= ipv6_tx_csum ( iobuf
, 0 );
355 iob_unput ( iobuf
, iob_len ( iobuf
) - ntohs ( ip6hdr
->payload_len
) -
356 sizeof ( *ip6hdr
) );
357 iob_pull ( iobuf
, sizeof ( *ip6hdr
) );
359 /* Send it to the transport layer */
360 return ipv6_process_nxt_hdr ( iobuf
, ip6hdr
->nxt_hdr
, &src
.st
, &dest
.st
,
364 DBG ( "IP6 packet dropped\n" );
370 * Convert an IPv6 address to a string.
372 * @v in6 Address to convert to string.
374 * Converts an IPv6 address to a string, and applies zero-compression as needed
375 * to condense the address for easier reading/typing.
377 char * inet6_ntoa ( struct in6_addr in6
) {
379 uint16_t *bytes
= ( uint16_t* ) &in6
;
380 size_t i
= 0, longest
= 0, tmp
= 0, long_idx
= ~0;
383 if ( IP6_EQUAL ( in6
, ip6_none
) ) {
384 tmp
= sprintf ( buf
, "::0" );
389 /* Determine the longest string of zeroes for zero-compression. */
390 for ( ; i
< 8; i
++ ) {
393 else if(tmp
> longest
) {
395 long_idx
= i
- longest
;
401 /* Check for last word being zero. This will cause long_idx to be zero,
402 * which confuses the actual buffer fill code. */
403 if(tmp
&& (tmp
> longest
)) {
405 long_idx
= 8 - longest
;
408 /* Inject into the buffer. */
410 for ( i
= 0; i
< 8; i
++ ) {
411 /* Should we skip over a string of zeroes? */
412 if ( i
== long_idx
) {
414 tmp
+= sprintf( buf
+ tmp
, ":" );
416 /* Handle end-of-string. */
421 /* Insert this component of the address. */
422 tmp
+= sprintf(buf
+ tmp
, "%x", ntohs(bytes
[i
]));
424 /* Add the next colon, if needed. */
426 tmp
+= sprintf( buf
+ tmp
, ":" );
434 static const char * ipv6_ntoa ( const void *net_addr
) {
435 return inet6_ntoa ( * ( ( struct in6_addr
* ) net_addr
) );
439 struct net_protocol ipv6_protocol __net_protocol
= {
441 .net_proto
= htons ( ETH_P_IPV6
),
442 .net_addr_len
= sizeof ( struct in6_addr
),
447 /** IPv6 TCPIP net protocol */
448 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol
= {
450 .sa_family
= AF_INET6
,