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