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