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