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 /* Unspecified IP6 address */
21 static struct in6_addr ip6_none
= {
22 .in6_u
.u6_addr32
= { 0,0,0,0 }
25 /** An IPv6 routing table entry */
26 struct ipv6_miniroute
{
27 /* List of miniroutes */
28 struct list_head list
;
31 struct net_device
*netdev
;
33 /* Destination prefix */
34 struct in6_addr prefix
;
37 /* IPv6 address of interface */
38 struct in6_addr address
;
40 struct in6_addr gateway
;
43 /** List of IPv6 miniroutes */
44 static LIST_HEAD ( miniroutes
);
47 * Add IPv6 minirouting table entry
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
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
;
61 miniroute
= malloc ( sizeof ( *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
;
70 /* Add miniroute to list of miniroutes */
71 if ( !IP6_EQUAL ( gateway
, ip6_none
) ) {
72 list_add_tail ( &miniroute
->list
, &miniroutes
);
74 list_add ( &miniroute
->list
, &miniroutes
);
82 * Delete IPv6 minirouting table entry
84 * @v miniroute Routing table entry
86 static void del_ipv6_miniroute ( struct ipv6_miniroute
*miniroute
) {
87 netdev_put ( miniroute
->netdev
);
88 list_del ( &miniroute
->list
);
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)
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
;
105 /* Clear any existing address for this net device */
106 del_ipv6_address ( netdev
);
108 /* Add new miniroute */
109 miniroute
= add_ipv6_miniroute ( netdev
, prefix
, prefix_len
, address
,
118 * Remove IPv6 interface
120 * @v netdev Network device
122 void del_ipv6_address ( struct net_device
*netdev
) {
123 struct ipv6_miniroute
*miniroute
;
125 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
126 if ( miniroute
->netdev
== netdev
) {
127 del_ipv6_miniroute ( miniroute
);
134 * Calculate TCPIP checksum
136 * @v iobuf I/O buffer
137 * @v tcpip TCP/IP protocol
139 * This function constructs the pseudo header and completes the checksum in the
140 * upper layer header.
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
;
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
;
153 /* Update checksum value */
154 return tcpip_continue_chksum ( csum
, &pshdr
, sizeof ( pshdr
) );
158 * Dump IP6 header for debugging
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
) );
169 * Transmit IP6 packet
172 * tcpip TCP/IP protocol
173 * st_dest Destination socket address
175 * This function prepends the IPv6 headers to the payload an transmits it.
177 static int ipv6_tx ( struct io_buffer
*iobuf
,
178 struct tcpip_protocol
*tcpip
,
179 struct sockaddr_tcpip
*st_dest
,
180 struct net_device
*netdev
,
181 uint16_t *trans_csum
) {
182 struct sockaddr_in6
*dest
= ( struct sockaddr_in6
* ) st_dest
;
183 struct in6_addr next_hop
;
184 struct ipv6_miniroute
*miniroute
;
185 uint8_t ll_dest_buf
[MAX_LL_ADDR_LEN
];
186 const uint8_t *ll_dest
= ll_dest_buf
;
189 /* Construct the IPv6 packet */
190 struct ip6_header
*ip6hdr
= iob_push ( iobuf
, sizeof ( *ip6hdr
) );
191 memset ( ip6hdr
, 0, sizeof ( *ip6hdr
) );
192 ip6hdr
->ver_traffic_class_flow_label
= htonl ( 0x60000000 );//IP6_VERSION;
193 ip6hdr
->payload_len
= htons ( iob_len ( iobuf
) - sizeof ( *ip6hdr
) );
194 ip6hdr
->nxt_hdr
= tcpip
->tcpip_proto
;
195 ip6hdr
->hop_limit
= IP6_HOP_LIMIT
; // 255
197 /* Determine the next hop address and interface
199 * TODO: Implement the routing table.
201 next_hop
= dest
->sin6_addr
;
202 list_for_each_entry ( miniroute
, &miniroutes
, list
) {
203 if ( ( memcmp ( &ip6hdr
->dest
, &miniroute
->prefix
,
204 miniroute
->prefix_len
) == 0 ) ||
205 ( IP6_EQUAL ( miniroute
->gateway
, ip6_none
) ) ) {
206 netdev
= miniroute
->netdev
;
207 ip6hdr
->src
= miniroute
->address
;
208 if ( ! ( IS_UNSPECIFIED ( miniroute
->gateway
) ) ) {
209 next_hop
= miniroute
->gateway
;
214 /* No network interface identified */
216 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr
->dest
) );
221 /* Complete the transport layer checksum */
223 *trans_csum
= ipv6_tx_csum ( iobuf
, *trans_csum
);
225 /* Print IPv6 header */
226 ipv6_dump ( ip6hdr
);
228 /* Resolve link layer address */
229 if ( next_hop
.in6_u
.u6_addr8
[0] == 0xff ) {
230 ll_dest_buf
[0] = 0x33;
231 ll_dest_buf
[1] = 0x33;
232 ll_dest_buf
[2] = next_hop
.in6_u
.u6_addr8
[12];
233 ll_dest_buf
[3] = next_hop
.in6_u
.u6_addr8
[13];
234 ll_dest_buf
[4] = next_hop
.in6_u
.u6_addr8
[14];
235 ll_dest_buf
[5] = next_hop
.in6_u
.u6_addr8
[15];
237 /* Unicast address needs to be resolved by NDP */
238 if ( ( rc
= ndp_resolve ( netdev
, &next_hop
, &ip6hdr
->src
,
239 ll_dest_buf
) ) != 0 ) {
240 DBG ( "No entry for %s\n", inet6_ntoa ( next_hop
) );
245 /* Transmit packet */
246 return net_tx ( iobuf
, netdev
, &ipv6_protocol
, ll_dest
);
254 * Process next IP6 header
256 * @v iobuf I/O buffer
257 * @v nxt_hdr Next header number
258 * @v src Source socket address
259 * @v dest Destination socket address
261 * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
263 static int ipv6_process_nxt_hdr ( struct io_buffer
*iobuf
, uint8_t nxt_hdr
,
264 struct sockaddr_tcpip
*src
, struct sockaddr_tcpip
*dest
) {
269 case IP6_AUTHENTICATION
:
272 DBG ( "Function not implemented for header %d\n", nxt_hdr
);
277 DBG ( "No next header\n" );
280 /* Next header is not a IPv6 extension header */
281 return tcpip_rx ( iobuf
, nxt_hdr
, src
, dest
, 0 /* fixme */ );
285 * Process incoming IP6 packets
287 * @v iobuf I/O buffer
288 * @v netdev Network device
289 * @v ll_source Link-layer source address
291 * This function processes a IPv6 packet
293 static int ipv6_rx ( struct io_buffer
*iobuf
,
294 __unused
struct net_device
*netdev
,
295 __unused
const void *ll_source
) {
297 struct ip6_header
*ip6hdr
= iobuf
->data
;
299 struct sockaddr_in6 sin6
;
300 struct sockaddr_tcpip st
;
304 if ( iob_len ( iobuf
) < sizeof ( *ip6hdr
) ) {
305 DBG ( "Packet too short (%d bytes)\n", iob_len ( iobuf
) );
309 /* TODO: Verify checksum */
311 /* Print IP6 header for debugging */
312 ipv6_dump ( ip6hdr
);
314 /* Check header version */
315 if ( ( ip6hdr
->ver_traffic_class_flow_label
& 0xf0000000 ) != 0x60000000 ) {
316 DBG ( "Invalid protocol version\n" );
320 /* Check the payload length */
321 if ( ntohs ( ip6hdr
->payload_len
) > iob_len ( iobuf
) ) {
322 DBG ( "Inconsistent packet length (%d bytes)\n",
323 ip6hdr
->payload_len
);
327 /* Ignore the traffic class and flow control values */
329 /* Construct socket address */
330 memset ( &src
, 0, sizeof ( src
) );
331 src
.sin6
.sin_family
= AF_INET6
;
332 src
.sin6
.sin6_addr
= ip6hdr
->src
;
333 memset ( &dest
, 0, sizeof ( dest
) );
334 dest
.sin6
.sin_family
= AF_INET6
;
335 dest
.sin6
.sin6_addr
= ip6hdr
->dest
;
338 iob_unput ( iobuf
, iob_len ( iobuf
) - ntohs ( ip6hdr
->payload_len
) -
339 sizeof ( *ip6hdr
) );
340 iob_pull ( iobuf
, sizeof ( *ip6hdr
) );
342 /* Send it to the transport layer */
343 return ipv6_process_nxt_hdr ( iobuf
, ip6hdr
->nxt_hdr
, &src
.st
, &dest
.st
);
346 DBG ( "Packet dropped\n" );
352 * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
354 char * inet6_ntoa ( struct in6_addr in6
) {
356 uint16_t *bytes
= ( uint16_t* ) &in6
;
357 sprintf ( buf
, "%x:%x:%x:%x:%x:%x:%x:%x", bytes
[0], bytes
[1], bytes
[2],
358 bytes
[3], bytes
[4], bytes
[5], bytes
[6], bytes
[7] );
362 static const char * ipv6_ntoa ( const void *net_addr
) {
363 return inet6_ntoa ( * ( ( struct in6_addr
* ) net_addr
) );
367 struct net_protocol ipv6_protocol __net_protocol
= {
369 .net_proto
= htons ( ETH_P_IPV6
),
370 .net_addr_len
= sizeof ( struct in6_addr
),
375 /** IPv6 TCPIP net protocol */
376 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol
= {
378 .sa_family
= AF_INET6
,