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