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