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