1801891bd22709ef88fe088a2530d12d214f2392
[gpxe.git] / src / net / ipv6.c
1 #include <errno.h>
2 #include <stdint.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <malloc.h>
6 #include <vsprintf.h>
7 #include <byteswap.h>
8 #include <gpxe/in.h>
9 #include <gpxe/ip6.h>
10 #include <gpxe/ndp.h>
11 #include <gpxe/list.h>
12 #include <gpxe/icmp6.h>
13 #include <gpxe/tcpip.h>
14 #include <gpxe/socket.h>
15 #include <gpxe/pkbuff.h>
16 #include <gpxe/netdevice.h>
17 #include <gpxe/if_ether.h>
18
19 struct net_protocol ipv6_protocol;
20
21 /* Unspecified IP6 address */
22 static struct in6_addr ip6_none = {
23         .in6_u.u6_addr32[0] = 0,
24         .in6_u.u6_addr32[1] = 0,
25         .in6_u.u6_addr32[2] = 0,
26         .in6_u.u6_addr32[3] = 0,
27 };
28
29 /** An IPv6 routing table entry */
30 struct ipv6_miniroute {
31         /* List of miniroutes */
32         struct list_head list;
33
34         /* Network device */
35         struct net_device *netdev;
36         /** Reference to network device */
37         struct reference netdev_ref;
38
39         /* Destination prefix */
40         struct in6_addr prefix;
41         /* Prefix length */
42         int prefix_len;
43         /* IPv6 address of interface */
44         struct in6_addr address;
45         /* Gateway address */
46         struct in6_addr gateway;
47 };
48
49 /** List of IPv6 miniroutes */
50 static LIST_HEAD ( miniroutes );
51
52 static void ipv6_forget_netdev ( struct reference *ref );
53
54 /**
55  * Add IPv6 minirouting table entry
56  *
57  * @v netdev            Network device
58  * @v prefix            Destination prefix
59  * @v address           Address of the interface
60  * @v gateway           Gateway address (or ::0 for no gateway)
61  * @ret miniroute       Routing table entry, or NULL
62  */
63 static struct ipv6_miniroute * add_ipv6_miniroute ( struct net_device *netdev,
64                                                     struct in6_addr prefix,
65                                                     int prefix_len,
66                                                     struct in6_addr address,
67                                                     struct in6_addr gateway ) {
68         struct ipv6_miniroute *miniroute;
69         
70         miniroute = malloc ( sizeof ( *miniroute ) );
71         if ( miniroute ) {
72                 /* Record routing information */
73                 miniroute->netdev = netdev;
74                 miniroute->prefix = prefix;
75                 miniroute->prefix_len = prefix_len;
76                 miniroute->address = address;
77                 miniroute->gateway = gateway;
78                 
79                 /* Add miniroute to list of miniroutes */
80                 if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
81                         list_add_tail ( &miniroute->list, &miniroutes );
82                 } else {
83                         list_add ( &miniroute->list, &miniroutes );
84                 }
85
86                 /* Record reference to net_device */
87                 miniroute->netdev_ref.forget = ipv6_forget_netdev;
88                 ref_add ( &miniroute->netdev_ref, &netdev->references );
89         }
90
91         return miniroute;
92 }
93
94 /**
95  * Delete IPv6 minirouting table entry
96  *
97  * @v miniroute         Routing table entry
98  */
99 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
100         ref_del ( &miniroute->netdev_ref );
101         list_del ( &miniroute->list );
102         free ( miniroute );
103 }
104
105 /**
106  * Forget reference to net_device
107  *
108  * @v ref               Persistent reference
109  */
110 static void ipv6_forget_netdev ( struct reference *ref ) {
111         struct ipv6_miniroute *miniroute
112                 = container_of ( ref, struct ipv6_miniroute, netdev_ref );
113
114         del_ipv6_miniroute ( miniroute );
115 }
116
117 /**
118  * Add IPv6 interface
119  *
120  * @v netdev    Network device
121  * @v prefix    Destination prefix
122  * @v address   Address of the interface
123  * @v gateway   Gateway address (or ::0 for no gateway)
124  */
125 int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
126                        int prefix_len, struct in6_addr address,
127                        struct in6_addr gateway ) {
128         struct ipv6_miniroute *miniroute;
129
130         /* Clear any existing address for this net device */
131         del_ipv6_address ( netdev );
132
133         /* Add new miniroute */
134         miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
135                                          gateway );
136         if ( ! miniroute )
137                 return -ENOMEM;
138
139         return 0;
140 }
141
142 /**
143  * Remove IPv6 interface
144  *
145  * @v netdev    Network device
146  */
147 void del_ipv6_address ( struct net_device *netdev ) {
148         struct ipv6_miniroute *miniroute;
149
150         list_for_each_entry ( miniroute, &miniroutes, list ) {
151                 if ( miniroute->netdev == netdev ) {
152                         del_ipv6_miniroute ( miniroute );
153                         break;
154                 }
155         }
156 }
157
158 /**
159  * Calculate TCPIP checksum
160  *
161  * @v pkb       Packet buffer
162  * @v tcpip     TCP/IP protocol
163  *
164  * This function constructs the pseudo header and completes the checksum in the
165  * upper layer header.
166  */
167 static uint16_t ipv6_tx_csum ( struct pk_buff *pkb, uint16_t csum ) {
168         struct ip6_header *ip6hdr = pkb->data;
169         struct ipv6_pseudo_header pshdr;
170
171         /* Calculate pseudo header */
172         memset ( &pshdr, 0, sizeof ( pshdr ) );
173         pshdr.src = ip6hdr->src;
174         pshdr.dest = ip6hdr->dest;
175         pshdr.len = htons ( pkb_len ( pkb ) - sizeof ( *ip6hdr ) );
176         pshdr.nxt_hdr = ip6hdr->nxt_hdr;
177
178         /* Update checksum value */
179         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
180 }
181
182 /**
183  * Dump IP6 header for debugging
184  *
185  * ip6hdr       IPv6 header
186  */
187 void ipv6_dump ( struct ip6_header *ip6hdr ) {
188         DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
189               inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
190               ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
191 }
192
193 /**
194  * Transmit IP6 packet
195  *
196  * pkb          Packet buffer
197  * tcpip        TCP/IP protocol
198  * st_dest      Destination socket address
199  *
200  * This function prepends the IPv6 headers to the payload an transmits it.
201  */
202 static int ipv6_tx ( struct pk_buff *pkb,
203                      struct tcpip_protocol *tcpip,
204                      struct sockaddr_tcpip *st_dest,
205                      uint16_t *trans_csum ) {
206         struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
207         struct in6_addr next_hop;
208         struct ipv6_miniroute *miniroute;
209         struct net_device *netdev = NULL;
210         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
211         const uint8_t *ll_dest = ll_dest_buf;
212         int rc;
213
214         /* Construct the IPv6 packet */
215         struct ip6_header *ip6hdr = pkb_push ( pkb, sizeof ( *ip6hdr ) );
216         memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
217         ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
218         ip6hdr->payload_len = htons ( pkb_len ( pkb ) - sizeof ( *ip6hdr ) );
219         ip6hdr->nxt_hdr = tcpip->tcpip_proto;
220         ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
221
222         /* Determine the next hop address and interface
223          *
224          * TODO: Implement the routing table.
225          */
226         next_hop = dest->sin6_addr;
227         list_for_each_entry ( miniroute, &miniroutes, list ) {
228                 if ( ( strncmp ( &ip6hdr->dest, &miniroute->prefix,
229                                         miniroute->prefix_len ) == 0 ) ||
230                      ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
231                         netdev = miniroute->netdev;
232                         ip6hdr->src = miniroute->address;
233                         if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
234                                 next_hop = miniroute->gateway;
235                         }
236                         break;
237                 }
238         }
239         /* No network interface identified */
240         if ( !netdev ) {
241                 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
242                 rc = -EHOSTUNREACH;
243                 goto err;
244         }
245
246         /* Complete the transport layer checksum */
247         if ( trans_csum )
248                 *trans_csum = ipv6_tx_csum ( pkb, *trans_csum );
249
250         /* Print IPv6 header */
251         ipv6_dump ( ip6hdr );
252         
253         /* Resolve link layer address */
254         if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
255                 ll_dest_buf[0] = 0x33;
256                 ll_dest_buf[1] = 0x33;
257                 ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
258                 ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
259                 ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
260                 ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
261         } else {
262                 /* Unicast address needs to be resolved by NDP */
263                 if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
264                                           ll_dest_buf ) ) != 0 ) {
265                         DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
266                         goto err;
267                 }
268         }
269
270         /* Transmit packet */
271         return net_tx ( pkb, netdev, &ipv6_protocol, ll_dest );
272
273   err:
274         free_pkb ( pkb );
275         return rc;
276 }
277
278 /**
279  * Process next IP6 header
280  *
281  * @v pkb       Packet buffer
282  * @v nxt_hdr   Next header number
283  * @v src       Source socket address
284  * @v dest      Destination socket address
285  *
286  * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
287  */
288 static int ipv6_process_nxt_hdr ( struct pk_buff *pkb, uint8_t nxt_hdr,
289                 struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
290         switch ( nxt_hdr ) {
291         case IP6_HOPBYHOP: 
292         case IP6_ROUTING: 
293         case IP6_FRAGMENT: 
294         case IP6_AUTHENTICATION: 
295         case IP6_DEST_OPTS: 
296         case IP6_ESP: 
297                 DBG ( "Function not implemented for header %d\n", nxt_hdr );
298                 return -ENOSYS;
299         case IP6_ICMP6: 
300                 break;
301         case IP6_NO_HEADER: 
302                 DBG ( "No next header\n" );
303                 return 0;
304         }
305         /* Next header is not a IPv6 extension header */
306         return tcpip_rx ( pkb, nxt_hdr, src, dest, 0 /* fixme */ );
307 }
308
309 /**
310  * Process incoming IP6 packets
311  *
312  * @v pkb               Packet buffer
313  * @v netdev            Network device
314  * @v ll_source         Link-layer source address
315  *
316  * This function processes a IPv6 packet
317  */
318 static int ipv6_rx ( struct pk_buff *pkb,
319                      struct net_device *netdev,
320                      const void *ll_source ) {
321
322         struct ip6_header *ip6hdr = pkb->data;
323         union {
324                 struct sockaddr_in6 sin6;
325                 struct sockaddr_tcpip st;
326         } src, dest;
327
328         /* Sanity check */
329         if ( pkb_len ( pkb ) < sizeof ( *ip6hdr ) ) {
330                 DBG ( "Packet too short (%d bytes)\n", pkb_len ( pkb ) );
331                 goto drop;
332         }
333
334         /* TODO: Verify checksum */
335
336         /* Print IP6 header for debugging */
337         ipv6_dump ( ip6hdr );
338
339         /* Check header version */
340         if ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 != 0x60000000 ) {
341                 DBG ( "Invalid protocol version\n" );
342                 goto drop;
343         }
344
345         /* Check the payload length */
346         if ( ntohs ( ip6hdr->payload_len ) > pkb_len ( pkb ) ) {
347                 DBG ( "Inconsistent packet length (%d bytes)\n",
348                         ip6hdr->payload_len );
349                 goto drop;
350         }
351
352         /* Ignore the traffic class and flow control values */
353
354         /* Construct socket address */
355         memset ( &src, 0, sizeof ( src ) );
356         src.sin6.sin_family = AF_INET6;
357         src.sin6.sin6_addr = ip6hdr->src;
358         memset ( &dest, 0, sizeof ( dest ) );
359         dest.sin6.sin_family = AF_INET6;
360         dest.sin6.sin6_addr = ip6hdr->dest;
361
362         /* Strip header */
363         pkb_unput ( pkb, pkb_len ( pkb ) - ntohs ( ip6hdr->payload_len ) -
364                                                         sizeof ( *ip6hdr ) );
365         pkb_pull ( pkb, sizeof ( *ip6hdr ) );
366
367         /* Send it to the transport layer */
368         return ipv6_process_nxt_hdr ( pkb, ip6hdr->nxt_hdr, &src.st, &dest.st );
369
370   drop:
371         DBG ( "Packet dropped\n" );
372         free_pkb ( pkb );
373         return -1;
374 }
375
376 /**
377  * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
378  */
379 char * inet6_ntoa ( struct in6_addr in6 ) {
380         static char buf[40];
381         uint16_t *bytes = ( uint16_t* ) &in6;
382         sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
383                         bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
384         return buf;
385 }
386
387 static const char * ipv6_ntoa ( const void *net_addr ) {
388         return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
389 }
390
391 /** IPv6 protocol */
392 struct net_protocol ipv6_protocol __net_protocol = {
393         .name = "IPv6",
394         .net_proto = htons ( ETH_P_IPV6 ),
395         .net_addr_len = sizeof ( struct in6_addr ),
396         .rx = ipv6_rx,
397         .ntoa = ipv6_ntoa,
398 };
399
400 /** IPv6 TCPIP net protocol */
401 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
402         .name = "IPv6",
403         .sa_family = AF_INET6,
404         .tx = ipv6_tx,
405 };