• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
add_ipv6_miniroute(struct net_device * netdev,struct in6_addr prefix,int prefix_len,struct in6_addr address,struct in6_addr gateway)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  */
del_ipv6_miniroute(struct ipv6_miniroute * miniroute)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  */
add_ipv6_address(struct net_device * netdev,struct in6_addr prefix,int prefix_len,struct in6_addr address,struct in6_addr 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;
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  */
del_ipv6_address(struct net_device * netdev)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  */
ipv6_tx_csum(struct io_buffer * iobuf,uint16_t csum)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  */
ipv6_dump(struct ip6_header * ip6hdr)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  */
ipv6_tx(struct io_buffer * iobuf,struct tcpip_protocol * tcpip,struct sockaddr_tcpip * st_src __unused,struct sockaddr_tcpip * st_dest,struct net_device * netdev,uint16_t * trans_csum)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  */
ipv6_process_nxt_hdr(struct io_buffer * iobuf,uint8_t nxt_hdr,struct sockaddr_tcpip * src,struct sockaddr_tcpip * dest)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  */
ipv6_rx(struct io_buffer * iobuf,__unused struct net_device * netdev,__unused const void * ll_source)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  */
inet6_ntoa(struct in6_addr in6)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 
ipv6_ntoa(const void * net_addr)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