Merge branch 'master' of git://git.etherboot.org/scm/gpxe
[people/meteger/gpxe.git] / src / net / ipv6.c
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 
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  */
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  */
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  */
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  */
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  */
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  */
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;
187         int rc;
188
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
196
197         /* Determine the next hop address and interface
198          *
199          * TODO: Implement the routing table.
200          */
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;
210                         }
211                         break;
212                 }
213         }
214         /* No network interface identified */
215         if ( !netdev ) {
216                 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
217                 rc = -ENETUNREACH;
218                 goto err;
219         }
220
221         /* Complete the transport layer checksum */
222         if ( trans_csum )
223                 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
224
225         /* Print IPv6 header */
226         ipv6_dump ( ip6hdr );
227         
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];
236         } else {
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 ) );
241                         goto err;
242                 }
243         }
244
245         /* Transmit packet */
246         return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
247
248   err:
249         free_iob ( iobuf );
250         return rc;
251 }
252
253 /**
254  * Process next IP6 header
255  *
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
260  *
261  * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
262  */
263 static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
264                 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
265         switch ( nxt_hdr ) {
266         case IP6_HOPBYHOP: 
267         case IP6_ROUTING: 
268         case IP6_FRAGMENT: 
269         case IP6_AUTHENTICATION: 
270         case IP6_DEST_OPTS: 
271         case IP6_ESP: 
272                 DBG ( "Function not implemented for header %d\n", nxt_hdr );
273                 return -ENOSYS;
274         case IP6_ICMP6: 
275                 break;
276         case IP6_NO_HEADER: 
277                 DBG ( "No next header\n" );
278                 return 0;
279         }
280         /* Next header is not a IPv6 extension header */
281         return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ );
282 }
283
284 /**
285  * Process incoming IP6 packets
286  *
287  * @v iobuf             I/O buffer
288  * @v netdev            Network device
289  * @v ll_source         Link-layer source address
290  *
291  * This function processes a IPv6 packet
292  */
293 static int ipv6_rx ( struct io_buffer *iobuf,
294                      __unused struct net_device *netdev,
295                      __unused const void *ll_source ) {
296
297         struct ip6_header *ip6hdr = iobuf->data;
298         union {
299                 struct sockaddr_in6 sin6;
300                 struct sockaddr_tcpip st;
301         } src, dest;
302
303         /* Sanity check */
304         if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
305                 DBG ( "Packet too short (%d bytes)\n", iob_len ( iobuf ) );
306                 goto drop;
307         }
308
309         /* TODO: Verify checksum */
310
311         /* Print IP6 header for debugging */
312         ipv6_dump ( ip6hdr );
313
314         /* Check header version */
315         if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
316                 DBG ( "Invalid protocol version\n" );
317                 goto drop;
318         }
319
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 );
324                 goto drop;
325         }
326
327         /* Ignore the traffic class and flow control values */
328
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;
336
337         /* Strip header */
338         iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
339                                                         sizeof ( *ip6hdr ) );
340         iob_pull ( iobuf, sizeof ( *ip6hdr ) );
341
342         /* Send it to the transport layer */
343         return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
344
345   drop:
346         DBG ( "Packet dropped\n" );
347         free_iob ( iobuf );
348         return -1;
349 }
350
351 /**
352  * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
353  */
354 char * inet6_ntoa ( struct in6_addr in6 ) {
355         static char buf[40];
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] );
359         return buf;
360 }
361
362 static const char * ipv6_ntoa ( const void *net_addr ) {
363         return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
364 }
365
366 /** IPv6 protocol */
367 struct net_protocol ipv6_protocol __net_protocol = {
368         .name = "IPv6",
369         .net_proto = htons ( ETH_P_IPV6 ),
370         .net_addr_len = sizeof ( struct in6_addr ),
371         .rx = ipv6_rx,
372         .ntoa = ipv6_ntoa,
373 };
374
375 /** IPv6 TCPIP net protocol */
376 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
377         .name = "IPv6",
378         .sa_family = AF_INET6,
379         .tx = ipv6_tx,
380 };