tizen 2.3.1 release
[external/qemu.git] / roms / ipxe / 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 <ipxe/in.h>
8 #include <ipxe/ip6.h>
9 #include <ipxe/ndp.h>
10 #include <ipxe/list.h>
11 #include <ipxe/icmp6.h>
12 #include <ipxe/tcpip.h>
13 #include <ipxe/socket.h>
14 #include <ipxe/iobuf.h>
15 #include <ipxe/netdevice.h>
16 #include <ipxe/if_ether.h>
17
18 /* Unspecified IP6 address */
19 static struct in6_addr ip6_none = {
20         .in6_u.u6_addr32 = { 0,0,0,0 }
21 };
22
23 /** An IPv6 routing table entry */
24 struct ipv6_miniroute {
25         /* List of miniroutes */
26         struct list_head list;
27
28         /* Network device */
29         struct net_device *netdev;
30
31         /* Destination prefix */
32         struct in6_addr prefix;
33         /* Prefix length */
34         int prefix_len;
35         /* IPv6 address of interface */
36         struct in6_addr address;
37         /* Gateway address */
38         struct in6_addr gateway;
39 };
40
41 /** List of IPv6 miniroutes */
42 static LIST_HEAD ( miniroutes );
43
44 /**
45  * Add IPv6 minirouting table entry
46  *
47  * @v netdev            Network device
48  * @v prefix            Destination prefix
49  * @v address           Address of the interface
50  * @v gateway           Gateway address (or ::0 for no gateway)
51  * @ret miniroute       Routing table entry, or NULL
52  */
53 static struct ipv6_miniroute * __malloc 
54 add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
55                      int prefix_len, struct in6_addr address,
56                      struct in6_addr gateway ) {
57         struct ipv6_miniroute *miniroute;
58         
59         miniroute = malloc ( sizeof ( *miniroute ) );
60         if ( miniroute ) {
61                 /* Record routing information */
62                 miniroute->netdev = netdev_get ( netdev );
63                 miniroute->prefix = prefix;
64                 miniroute->prefix_len = prefix_len;
65                 miniroute->address = address;
66                 miniroute->gateway = gateway;
67                 
68                 /* Add miniroute to list of miniroutes */
69                 if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
70                         list_add_tail ( &miniroute->list, &miniroutes );
71                 } else {
72                         list_add ( &miniroute->list, &miniroutes );
73                 }
74         }
75
76         return miniroute;
77 }
78
79 /**
80  * Delete IPv6 minirouting table entry
81  *
82  * @v miniroute         Routing table entry
83  */
84 static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
85         netdev_put ( miniroute->netdev );
86         list_del ( &miniroute->list );
87         free ( miniroute );
88 }
89
90 /**
91  * Add IPv6 interface
92  *
93  * @v netdev    Network device
94  * @v prefix    Destination prefix
95  * @v address   Address of the interface
96  * @v gateway   Gateway address (or ::0 for no gateway)
97  */
98 int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
99                        int prefix_len, struct in6_addr address,
100                        struct in6_addr gateway ) {
101         struct ipv6_miniroute *miniroute;
102
103         /* Clear any existing address for this net device */
104         del_ipv6_address ( netdev );
105
106         /* Add new miniroute */
107         miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
108                                          gateway );
109         if ( ! miniroute )
110                 return -ENOMEM;
111
112         return 0;
113 }
114
115 /**
116  * Remove IPv6 interface
117  *
118  * @v netdev    Network device
119  */
120 void del_ipv6_address ( struct net_device *netdev ) {
121         struct ipv6_miniroute *miniroute;
122
123         list_for_each_entry ( miniroute, &miniroutes, list ) {
124                 if ( miniroute->netdev == netdev ) {
125                         del_ipv6_miniroute ( miniroute );
126                         break;
127                 }
128         }
129 }
130
131 /**
132  * Calculate TCPIP checksum
133  *
134  * @v iobuf     I/O buffer
135  * @v tcpip     TCP/IP protocol
136  *
137  * This function constructs the pseudo header and completes the checksum in the
138  * upper layer header.
139  */
140 static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
141         struct ip6_header *ip6hdr = iobuf->data;
142         struct ipv6_pseudo_header pshdr;
143
144         /* Calculate pseudo header */
145         memset ( &pshdr, 0, sizeof ( pshdr ) );
146         pshdr.src = ip6hdr->src;
147         pshdr.dest = ip6hdr->dest;
148         pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
149         pshdr.nxt_hdr = ip6hdr->nxt_hdr;
150
151         /* Update checksum value */
152         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
153 }
154
155 /**
156  * Dump IP6 header for debugging
157  *
158  * ip6hdr       IPv6 header
159  */
160 void ipv6_dump ( struct ip6_header *ip6hdr ) {
161         DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
162               inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
163               ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
164 }
165
166 /**
167  * Transmit IP6 packet
168  *
169  * iobuf                I/O buffer
170  * tcpip        TCP/IP protocol
171  * st_dest      Destination socket address
172  *
173  * This function prepends the IPv6 headers to the payload an transmits it.
174  */
175 static int ipv6_tx ( struct io_buffer *iobuf,
176                      struct tcpip_protocol *tcpip,
177                      struct sockaddr_tcpip *st_src __unused,
178                      struct sockaddr_tcpip *st_dest,
179                      struct net_device *netdev,
180                      uint16_t *trans_csum ) {
181         struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
182         struct in6_addr next_hop;
183         struct ipv6_miniroute *miniroute;
184         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
185         const uint8_t *ll_dest = ll_dest_buf;
186         int rc;
187
188         /* Construct the IPv6 packet */
189         struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
190         memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
191         ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
192         ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
193         ip6hdr->nxt_hdr = tcpip->tcpip_proto;
194         ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
195
196         /* Determine the next hop address and interface
197          *
198          * TODO: Implement the routing table.
199          */
200         next_hop = dest->sin6_addr;
201         list_for_each_entry ( miniroute, &miniroutes, list ) {
202                 if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix,
203                                         miniroute->prefix_len ) == 0 ) ||
204                      ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
205                         netdev = miniroute->netdev;
206                         ip6hdr->src = miniroute->address;
207                         if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
208                                 next_hop = miniroute->gateway;
209                         }
210                         break;
211                 }
212         }
213         /* No network interface identified */
214         if ( !netdev ) {
215                 DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
216                 rc = -ENETUNREACH;
217                 goto err;
218         }
219
220         /* Complete the transport layer checksum */
221         if ( trans_csum )
222                 *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
223
224         /* Print IPv6 header */
225         ipv6_dump ( ip6hdr );
226         
227         /* Resolve link layer address */
228         if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
229                 ll_dest_buf[0] = 0x33;
230                 ll_dest_buf[1] = 0x33;
231                 ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
232                 ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
233                 ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
234                 ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
235         } else {
236                 /* Unicast address needs to be resolved by NDP */
237                 if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
238                                           ll_dest_buf ) ) != 0 ) {
239                         DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
240                         goto err;
241                 }
242         }
243
244         /* Transmit packet */
245         return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest,
246                         netdev->ll_addr );
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_dest           Link-layer destination address
290  * @v ll_source         Link-layer source address
291  * @v flags             Packet flags
292  *
293  * This function processes a IPv6 packet
294  */
295 static int ipv6_rx ( struct io_buffer *iobuf,
296                      __unused struct net_device *netdev,
297                      __unused const void *ll_dest,
298                      __unused const void *ll_source,
299                      __unused unsigned int flags ) {
300
301         struct ip6_header *ip6hdr = iobuf->data;
302         union {
303                 struct sockaddr_in6 sin6;
304                 struct sockaddr_tcpip st;
305         } src, dest;
306
307         /* Sanity check */
308         if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
309                 DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
310                 goto drop;
311         }
312
313         /* TODO: Verify checksum */
314
315         /* Print IP6 header for debugging */
316         ipv6_dump ( ip6hdr );
317
318         /* Check header version */
319         if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
320                 DBG ( "Invalid protocol version\n" );
321                 goto drop;
322         }
323
324         /* Check the payload length */
325         if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
326                 DBG ( "Inconsistent packet length (%d bytes)\n",
327                         ip6hdr->payload_len );
328                 goto drop;
329         }
330
331         /* Ignore the traffic class and flow control values */
332
333         /* Construct socket address */
334         memset ( &src, 0, sizeof ( src ) );
335         src.sin6.sin_family = AF_INET6;
336         src.sin6.sin6_addr = ip6hdr->src;
337         memset ( &dest, 0, sizeof ( dest ) );
338         dest.sin6.sin_family = AF_INET6;
339         dest.sin6.sin6_addr = ip6hdr->dest;
340
341         /* Strip header */
342         iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
343                                                         sizeof ( *ip6hdr ) );
344         iob_pull ( iobuf, sizeof ( *ip6hdr ) );
345
346         /* Send it to the transport layer */
347         return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
348
349   drop:
350         DBG ( "Packet dropped\n" );
351         free_iob ( iobuf );
352         return -1;
353 }
354
355 /**
356  * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
357  */
358 char * inet6_ntoa ( struct in6_addr in6 ) {
359         static char buf[40];
360         uint16_t *bytes = ( uint16_t* ) &in6;
361         sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
362                         bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
363         return buf;
364 }
365
366 static const char * ipv6_ntoa ( const void *net_addr ) {
367         return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
368 }
369
370 /** IPv6 protocol */
371 struct net_protocol ipv6_protocol __net_protocol = {
372         .name = "IPv6",
373         .net_proto = htons ( ETH_P_IPV6 ),
374         .net_addr_len = sizeof ( struct in6_addr ),
375         .rx = ipv6_rx,
376         .ntoa = ipv6_ntoa,
377 };
378
379 /** IPv6 TCPIP net protocol */
380 struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = {
381         .name = "IPv6",
382         .sa_family = AF_INET6,
383         .tx = ipv6_tx,
384 };