Verify checksums on the RX datapath.
[gpxe.git] / src / net / ipv4.c
1 #include <string.h>
2 #include <stdint.h>
3 #include <errno.h>
4 #include <byteswap.h>
5 #include <malloc.h>
6 #include <vsprintf.h>
7 #include <gpxe/list.h>
8 #include <gpxe/in.h>
9 #include <gpxe/arp.h>
10 #include <gpxe/if_ether.h>
11 #include <gpxe/pkbuff.h>
12 #include <gpxe/netdevice.h>
13 #include "uip/uip.h"
14 #include <gpxe/ip.h>
15 #include <gpxe/tcpip.h>
16
17 /** @file
18  *
19  * IPv4 protocol
20  *
21  * The gPXE IP stack is currently implemented on top of the uIP
22  * protocol stack.  This file provides wrappers around uIP so that
23  * higher-level protocol implementations do not need to talk directly
24  * to uIP (which has a somewhat baroque API).
25  *
26  */
27
28 /* Unique IP datagram identification number */
29 static uint16_t next_ident = 0;
30
31 struct net_protocol ipv4_protocol;
32
33 /** An IPv4 address/routing table entry */
34 struct ipv4_miniroute {
35         /** List of miniroutes */
36         struct list_head list;
37         /** Network device */
38         struct net_device *netdev;
39         /** IPv4 address */
40         struct in_addr address;
41         /** Subnet mask */
42         struct in_addr netmask;
43         /** Gateway address */
44         struct in_addr gateway;
45 };
46
47 /** List of IPv4 miniroutes */
48 static LIST_HEAD ( miniroutes );
49
50 /** List of fragment reassembly buffers */
51 static LIST_HEAD ( frag_buffers );
52
53 /**
54  * Add IPv4 interface
55  *
56  * @v netdev    Network device
57  * @v address   IPv4 address
58  * @v netmask   Subnet mask
59  * @v gateway   Gateway address (or @c INADDR_NONE for no gateway)
60  * @ret rc      Return status code
61  *
62  */
63 int add_ipv4_address ( struct net_device *netdev, struct in_addr address,
64                        struct in_addr netmask, struct in_addr gateway ) {
65         struct ipv4_miniroute *miniroute;
66
67         /* Allocate and populate miniroute structure */
68         miniroute = malloc ( sizeof ( *miniroute ) );
69         if ( ! miniroute )
70                 return -ENOMEM;
71         miniroute->netdev = netdev;
72         miniroute->address = address;
73         miniroute->netmask = netmask;
74         miniroute->gateway = gateway;
75         
76         /* Add to end of list if we have a gateway, otherwise to start
77          * of list.
78          */
79         if ( gateway.s_addr != INADDR_NONE ) {
80                 list_add_tail ( &miniroute->list, &miniroutes );
81         } else {
82                 list_add ( &miniroute->list, &miniroutes );
83         }
84         return 0;
85 }
86
87 /**
88  * Remove IPv4 interface
89  *
90  * @v netdev    Network device
91  */
92 void del_ipv4_address ( struct net_device *netdev ) {
93         struct ipv4_miniroute *miniroute;
94
95         list_for_each_entry ( miniroute, &miniroutes, list ) {
96                 if ( miniroute->netdev == netdev ) {
97                         list_del ( &miniroute->list );
98                         break;
99                 }
100         }
101 }
102
103 /**
104  * Perform IPv4 routing
105  *
106  * @v dest              Final destination address
107  * @ret dest            Next hop destination address
108  * @ret miniroute       Routing table entry to use, or NULL if no route
109  */
110 static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
111         struct ipv4_miniroute *miniroute;
112         int local;
113         int has_gw;
114
115         list_for_each_entry ( miniroute, &miniroutes, list ) {
116                 local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
117                             & miniroute->netmask.s_addr ) == 0 );
118                 has_gw = ( miniroute->gateway.s_addr != INADDR_NONE );
119                 if ( local || has_gw ) {
120                         if ( ! local )
121                                 *dest = miniroute->gateway;
122                         return miniroute;
123                 }
124         }
125
126         return NULL;
127 }
128
129 /**
130  * Fragment reassembly counter timeout
131  *
132  * @v timer     Retry timer
133  * @v over      If asserted, the timer is greater than @c MAX_TIMEOUT 
134  */
135 static void ipv4_frag_expired ( struct retry_timer *timer __unused,
136                                 int over ) {
137         if ( over ) {
138                 DBG ( "Fragment reassembly timeout" );
139                 /* Free the fragment buffer */
140         }
141 }
142
143 /**
144  * Free fragment buffer
145  *
146  * @v fragbug   Fragment buffer
147  */
148 static void free_fragbuf ( struct frag_buffer *fragbuf ) {
149         if ( fragbuf ) {
150                 free_dma ( fragbuf, sizeof ( *fragbuf ) );
151         }
152 }
153
154 /**
155  * Fragment reassembler
156  *
157  * @v pkb               Packet buffer, fragment of the datagram
158  * @ret frag_pkb        Reassembled packet, or NULL
159  */
160 static struct pk_buff * ipv4_reassemble ( struct pk_buff * pkb ) {
161         struct iphdr *iphdr = pkb->data;
162         struct frag_buffer *fragbuf;
163         
164         /**
165          * Check if the fragment belongs to any fragment series
166          */
167         list_for_each_entry ( fragbuf, &frag_buffers, list ) {
168                 if ( fragbuf->ident == iphdr->ident &&
169                      fragbuf->src.s_addr == iphdr->src.s_addr ) {
170                         /**
171                          * Check if the packet is the expected fragment
172                          * 
173                          * The offset of the new packet must be equal to the
174                          * length of the data accumulated so far (the length of
175                          * the reassembled packet buffer
176                          */
177                         if ( pkb_len ( fragbuf->frag_pkb ) == 
178                               ( iphdr->frags & IP_MASK_OFFSET ) ) {
179                                 /**
180                                  * Append the contents of the fragment to the
181                                  * reassembled packet buffer
182                                  */
183                                 pkb_pull ( pkb, sizeof ( *iphdr ) );
184                                 memcpy ( pkb_put ( fragbuf->frag_pkb,
185                                                         pkb_len ( pkb ) ),
186                                          pkb->data, pkb_len ( pkb ) );
187                                 free_pkb ( pkb );
188
189                                 /** Check if the fragment series is over */
190                                 if ( !iphdr->frags & IP_MASK_MOREFRAGS ) {
191                                         pkb = fragbuf->frag_pkb;
192                                         free_fragbuf ( fragbuf );
193                                         return pkb;
194                                 }
195
196                         } else {
197                                 /* Discard the fragment series */
198                                 free_fragbuf ( fragbuf );
199                                 free_pkb ( pkb );
200                         }
201                         return NULL;
202                 }
203         }
204         
205         /** Check if the fragment is the first in the fragment series */
206         if ( iphdr->frags & IP_MASK_MOREFRAGS &&
207                         ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) {
208         
209                 /** Create a new fragment buffer */
210                 fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) );
211                 fragbuf->ident = iphdr->ident;
212                 fragbuf->src = iphdr->src;
213
214                 /* Set up the reassembly packet buffer */
215                 fragbuf->frag_pkb = alloc_pkb ( IP_FRAG_PKB_SIZE );
216                 pkb_pull ( pkb, sizeof ( *iphdr ) );
217                 memcpy ( pkb_put ( fragbuf->frag_pkb, pkb_len ( pkb ) ),
218                          pkb->data, pkb_len ( pkb ) );
219                 free_pkb ( pkb );
220
221                 /* Set the reassembly timer */
222                 fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT;
223                 fragbuf->frag_timer.expired = ipv4_frag_expired;
224                 start_timer ( &fragbuf->frag_timer );
225
226                 /* Add the fragment buffer to the list of fragment buffers */
227                 list_add ( &fragbuf->list, &frag_buffers );
228         }
229         
230         return NULL;
231 }
232
233 /**
234  * Add IPv4 pseudo-header checksum to existing checksum
235  *
236  * @v pkb               Packet buffer
237  * @v csum              Existing checksum
238  * @ret csum            Updated checksum
239  */
240 static uint16_t ipv4_pshdr_chksum ( struct pk_buff *pkb, uint16_t csum ) {
241         struct ipv4_pseudo_header pshdr;
242         struct iphdr *iphdr = pkb->data;
243         size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
244
245         /* Build pseudo-header */
246         pshdr.src = iphdr->src;
247         pshdr.dest = iphdr->dest;
248         pshdr.zero_padding = 0x00;
249         pshdr.protocol = iphdr->protocol;
250         pshdr.len = htons ( pkb_len ( pkb ) - hdrlen );
251
252         /* Update the checksum value */
253         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
254 }
255
256 /**
257  * Determine link-layer address
258  *
259  * @v dest              IPv4 destination address
260  * @v src               IPv4 source address
261  * @v netdev            Network device
262  * @v ll_dest           Link-layer destination address buffer
263  * @ret rc              Return status code
264  */
265 static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
266                           struct net_device *netdev, uint8_t *ll_dest ) {
267         struct ll_protocol *ll_protocol = netdev->ll_protocol;
268         uint8_t *dest_bytes = ( ( uint8_t * ) &dest );
269
270         if ( dest.s_addr == INADDR_BROADCAST ) {
271                 /* Broadcast address */
272                 memcpy ( ll_dest, ll_protocol->ll_broadcast,
273                          ll_protocol->ll_addr_len );
274                 return 0;
275         } else if ( IN_MULTICAST ( dest.s_addr ) ) {
276                 /* Special case: IPv4 multicast over Ethernet.  This
277                  * code may need to be generalised once we find out
278                  * what happens for other link layers.
279                  */
280                 ll_dest[0] = 0x01;
281                 ll_dest[1] = 0x00;
282                 ll_dest[2] = 0x5e;
283                 ll_dest[3] = dest_bytes[1] & 0x7f;
284                 ll_dest[4] = dest_bytes[2];
285                 ll_dest[5] = dest_bytes[3];
286                 return 0;
287         } else {
288                 /* Unicast address: resolve via ARP */
289                 return arp_resolve ( netdev, &ipv4_protocol, &dest,
290                                      &src, ll_dest );
291         }
292 }
293
294 /**
295  * Transmit IP packet
296  *
297  * @v pkb               Packet buffer
298  * @v tcpip             Transport-layer protocol
299  * @v st_dest           Destination network-layer address
300  * @v trans_csum        Transport-layer checksum to complete, or NULL
301  * @ret rc              Status
302  *
303  * This function expects a transport-layer segment and prepends the IP header
304  */
305 static int ipv4_tx ( struct pk_buff *pkb,
306                      struct tcpip_protocol *tcpip_protocol,
307                      struct sockaddr_tcpip *st_dest, uint16_t *trans_csum ) {
308         struct iphdr *iphdr = pkb_push ( pkb, sizeof ( *iphdr ) );
309         struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
310         struct ipv4_miniroute *miniroute;
311         struct in_addr next_hop;
312         uint8_t ll_dest[MAX_LL_ADDR_LEN];
313         int rc;
314
315         /* Fill up the IP header, except source address */
316         iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
317         iphdr->service = IP_TOS;
318         iphdr->len = htons ( pkb_len ( pkb ) ); 
319         iphdr->ident = htons ( ++next_ident );
320         iphdr->frags = 0;
321         iphdr->ttl = IP_TTL;
322         iphdr->protocol = tcpip_protocol->tcpip_proto;
323         iphdr->chksum = 0;
324         iphdr->dest = sin_dest->sin_addr;
325
326         /* Use routing table to identify next hop and transmitting netdev */
327         next_hop = iphdr->dest;
328         miniroute = ipv4_route ( &next_hop );
329         if ( ! miniroute ) {
330                 DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) );
331                 rc = -EHOSTUNREACH;
332                 goto err;
333         }
334         iphdr->src = miniroute->address;
335
336         /* Determine link-layer destination address */
337         if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, miniroute->netdev,
338                                    ll_dest ) ) != 0 ) {
339                 DBG ( "IPv4 has no link-layer address for %s\n",
340                       inet_ntoa ( iphdr->dest ) );
341                 goto err;
342         }
343
344         /* Fix up checksums */
345         if ( trans_csum )
346                 *trans_csum = ipv4_pshdr_chksum ( pkb, *trans_csum );
347         iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
348
349         /* Print IP4 header for debugging */
350         DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) );
351         DBG ( "%s len %d proto %d id %04x csum %04x\n",
352               inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol,
353               ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
354
355         /* Hand off to link layer */
356         return net_tx ( pkb, miniroute->netdev, &ipv4_protocol, ll_dest );
357
358  err:
359         free_pkb ( pkb );
360         return rc;
361 }
362
363 /**
364  * Process incoming packets
365  *
366  * @v pkb       Packet buffer
367  * @v netdev    Network device
368  * @v ll_source Link-layer destination source
369  *
370  * This function expects an IP4 network datagram. It processes the headers 
371  * and sends it to the transport layer.
372  */
373 static int ipv4_rx ( struct pk_buff *pkb, struct net_device *netdev __unused,
374                      const void *ll_source __unused ) {
375         struct iphdr *iphdr = pkb->data;
376         size_t hdrlen;
377         size_t len;
378         union {
379                 struct sockaddr_in sin;
380                 struct sockaddr_tcpip st;
381         } src, dest;
382         uint16_t csum;
383         uint16_t pshdr_csum;
384
385         /* Sanity check the IPv4 header */
386         if ( pkb_len ( pkb ) < sizeof ( *iphdr ) ) {
387                 DBG ( "IPv4 packet too short at %d bytes (min %d bytes)\n",
388                       pkb_len ( pkb ), sizeof ( *iphdr ) );
389                 goto err;
390         }
391         if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
392                 DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen );
393                 goto err;
394         }
395         hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
396         if ( hdrlen < sizeof ( *iphdr ) ) {
397                 DBG ( "IPv4 header too short at %d bytes (min %d bytes)\n",
398                       hdrlen, sizeof ( *iphdr ) );
399                 goto err;
400         }
401         if ( hdrlen > pkb_len ( pkb ) ) {
402                 DBG ( "IPv4 header too long at %d bytes "
403                       "(packet is %d bytes)\n", hdrlen, pkb_len ( pkb ) );
404                 goto err;
405         }
406         if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
407                 DBG ( "IPv4 checksum incorrect (is %04x including checksum "
408                       "field, should be 0000)\n", csum );
409                 goto err;
410         }
411         len = ntohs ( iphdr->len );
412         if ( len < hdrlen ) {
413                 DBG ( "IPv4 length too short at %d bytes "
414                       "(header is %d bytes)\n", len, hdrlen );
415                 goto err;
416         }
417         if ( len > pkb_len ( pkb ) ) {
418                 DBG ( "IPv4 length too long at %d bytes "
419                       "(packet is %d bytes)\n", len, pkb_len ( pkb ) );
420                 goto err;
421         }
422
423         /* Print IPv4 header for debugging */
424         DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
425         DBG ( "%s len %d proto %d id %04x csum %04x\n",
426               inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
427               ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
428
429         /* Truncate packet to correct length, calculate pseudo-header
430          * checksum and then strip off the IPv4 header.
431          */
432         pkb_unput ( pkb, ( pkb_len ( pkb ) - len ) );
433         pshdr_csum = ipv4_pshdr_chksum ( pkb, TCPIP_EMPTY_CSUM );
434         pkb_pull ( pkb, hdrlen );
435
436         /* Fragment reassembly */
437         if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) || 
438              ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) {
439                 /* Pass the fragment to ipv4_reassemble() which either
440                  * returns a fully reassembled packet buffer or NULL.
441                  */
442                 pkb = ipv4_reassemble ( pkb );
443                 if ( ! pkb )
444                         return 0;
445         }
446
447         /* Construct socket addresses and hand off to transport layer */
448         memset ( &src, 0, sizeof ( src ) );
449         src.sin.sin_family = AF_INET;
450         src.sin.sin_addr = iphdr->src;
451         memset ( &dest, 0, sizeof ( dest ) );
452         dest.sin.sin_family = AF_INET;
453         dest.sin.sin_addr = iphdr->dest;
454         return tcpip_rx ( pkb, iphdr->protocol, &src.st, &dest.st, pshdr_csum);
455
456  err:
457         free_pkb ( pkb );
458         return -EINVAL;
459 }
460
461 /** 
462  * Check existence of IPv4 address for ARP
463  *
464  * @v netdev            Network device
465  * @v net_addr          Network-layer address
466  * @ret rc              Return status code
467  */
468 static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
469         const struct in_addr *address = net_addr;
470         struct ipv4_miniroute *miniroute;
471
472         list_for_each_entry ( miniroute, &miniroutes, list ) {
473                 if ( ( miniroute->netdev == netdev ) &&
474                      ( miniroute->address.s_addr == address->s_addr ) ) {
475                         /* Found matching address */
476                         return 0;
477                 }
478         }
479         return -ENOENT;
480 }
481
482 /**
483  * Convert IPv4 address to dotted-quad notation
484  *
485  * @v in        IP address
486  * @ret string  IP address in dotted-quad notation
487  */
488 char * inet_ntoa ( struct in_addr in ) {
489         static char buf[16]; /* "xxx.xxx.xxx.xxx" */
490         uint8_t *bytes = ( uint8_t * ) &in;
491         
492         sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
493         return buf;
494 }
495
496 /**
497  * Transcribe IP address
498  *
499  * @v net_addr  IP address
500  * @ret string  IP address in dotted-quad notation
501  *
502  */
503 static const char * ipv4_ntoa ( const void *net_addr ) {
504         return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
505 }
506
507 /** IPv4 protocol */
508 struct net_protocol ipv4_protocol __net_protocol = {
509         .name = "IP",
510         .net_proto = htons ( ETH_P_IP ),
511         .net_addr_len = sizeof ( struct in_addr ),
512         .rx = ipv4_rx,
513         .ntoa = ipv4_ntoa,
514 };
515
516 /** IPv4 TCPIP net protocol */
517 struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = {
518         .name = "IPv4",
519         .sa_family = AF_INET,
520         .tx = ipv4_tx,
521 };
522
523 /** IPv4 ARP protocol */
524 struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = {
525         .net_protocol = &ipv4_protocol,
526         .check = ipv4_arp_check,
527 };