iPXE
ipv4.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  * Copyright (C) 2006 Nikhil Chandru Rao
00004  *
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License as
00007  * published by the Free Software Foundation; either version 2 of the
00008  * License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful, but
00011  * WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00018  * 02110-1301, USA.
00019  *
00020  * You can also choose to distribute this program under the terms of
00021  * the Unmodified Binary Distribution Licence (as given in the file
00022  * COPYING.UBDL), provided that you have satisfied its requirements.
00023  */
00024 
00025 #include <string.h>
00026 #include <stdint.h>
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <errno.h>
00030 #include <byteswap.h>
00031 #include <ipxe/list.h>
00032 #include <ipxe/in.h>
00033 #include <ipxe/arp.h>
00034 #include <ipxe/if_ether.h>
00035 #include <ipxe/iobuf.h>
00036 #include <ipxe/netdevice.h>
00037 #include <ipxe/ip.h>
00038 #include <ipxe/tcpip.h>
00039 #include <ipxe/dhcp.h>
00040 #include <ipxe/settings.h>
00041 #include <ipxe/fragment.h>
00042 #include <ipxe/ipstat.h>
00043 #include <ipxe/profile.h>
00044 
00045 /** @file
00046  *
00047  * IPv4 protocol
00048  *
00049  */
00050 
00051 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00052 
00053 /* Unique IP datagram identification number (high byte) */
00054 static uint8_t next_ident_high = 0;
00055 
00056 /** List of IPv4 miniroutes */
00057 struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
00058 
00059 /** IPv4 statistics */
00060 static struct ip_statistics ipv4_stats;
00061 
00062 /** IPv4 statistics family */
00063 struct ip_statistics_family
00064 ipv4_stats_family __ip_statistics_family ( IP_STATISTICS_IPV4 ) = {
00065         .version = 4,
00066         .stats = &ipv4_stats,
00067 };
00068 
00069 /** Transmit profiler */
00070 static struct profiler ipv4_tx_profiler __profiler = { .name = "ipv4.tx" };
00071 
00072 /** Receive profiler */
00073 static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" };
00074 
00075 /**
00076  * Add IPv4 minirouting table entry
00077  *
00078  * @v netdev            Network device
00079  * @v address           IPv4 address
00080  * @v netmask           Subnet mask
00081  * @v gateway           Gateway address (if any)
00082  * @ret rc              Return status code
00083  */
00084 static int add_ipv4_miniroute ( struct net_device *netdev,
00085                                 struct in_addr address, struct in_addr netmask,
00086                                 struct in_addr gateway ) {
00087         struct ipv4_miniroute *miniroute;
00088 
00089         DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
00090         DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) );
00091         if ( gateway.s_addr )
00092                 DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) );
00093         DBGC ( netdev, "via %s\n", netdev->name );
00094 
00095         /* Allocate and populate miniroute structure */
00096         miniroute = malloc ( sizeof ( *miniroute ) );
00097         if ( ! miniroute ) {
00098                 DBGC ( netdev, "IPv4 could not add miniroute\n" );
00099                 return -ENOMEM;
00100         }
00101 
00102         /* Record routing information */
00103         miniroute->netdev = netdev_get ( netdev );
00104         miniroute->address = address;
00105         miniroute->netmask = netmask;
00106         miniroute->gateway = gateway;
00107                 
00108         /* Add to end of list if we have a gateway, otherwise
00109          * to start of list.
00110          */
00111         if ( gateway.s_addr ) {
00112                 list_add_tail ( &miniroute->list, &ipv4_miniroutes );
00113         } else {
00114                 list_add ( &miniroute->list, &ipv4_miniroutes );
00115         }
00116 
00117         return 0;
00118 }
00119 
00120 /**
00121  * Delete IPv4 minirouting table entry
00122  *
00123  * @v miniroute         Routing table entry
00124  */
00125 static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
00126         struct net_device *netdev = miniroute->netdev;
00127 
00128         DBGC ( netdev, "IPv4 del %s", inet_ntoa ( miniroute->address ) );
00129         DBGC ( netdev, "/%s ", inet_ntoa ( miniroute->netmask ) );
00130         if ( miniroute->gateway.s_addr )
00131                 DBGC ( netdev, "gw %s ", inet_ntoa ( miniroute->gateway ) );
00132         DBGC ( netdev, "via %s\n", miniroute->netdev->name );
00133 
00134         netdev_put ( miniroute->netdev );
00135         list_del ( &miniroute->list );
00136         free ( miniroute );
00137 }
00138 
00139 /**
00140  * Perform IPv4 routing
00141  *
00142  * @v scope_id          Destination address scope ID
00143  * @v dest              Final destination address
00144  * @ret dest            Next hop destination address
00145  * @ret miniroute       Routing table entry to use, or NULL if no route
00146  *
00147  * If the route requires use of a gateway, the next hop destination
00148  * address will be overwritten with the gateway address.
00149  */
00150 static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
00151                                             struct in_addr *dest ) {
00152         struct ipv4_miniroute *miniroute;
00153 
00154         /* Find first usable route in routing table */
00155         list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
00156 
00157                 /* Skip closed network devices */
00158                 if ( ! netdev_is_open ( miniroute->netdev ) )
00159                         continue;
00160 
00161                 if ( IN_IS_MULTICAST ( dest->s_addr ) ) {
00162 
00163                         /* If destination is non-global, and the scope ID
00164                          * matches this network device, then use this route.
00165                          */
00166                         if ( miniroute->netdev->index == scope_id )
00167                                 return miniroute;
00168 
00169                 } else {
00170 
00171                         /* If destination is an on-link global
00172                          * address, then use this route.
00173                          */
00174                         if ( ( ( dest->s_addr ^ miniroute->address.s_addr )
00175                                & miniroute->netmask.s_addr ) == 0 )
00176                                 return miniroute;
00177 
00178                         /* If destination is an off-link global
00179                          * address, and we have a default gateway,
00180                          * then use this route.
00181                          */
00182                         if ( miniroute->gateway.s_addr ) {
00183                                 *dest = miniroute->gateway;
00184                                 return miniroute;
00185                         }
00186                 }
00187         }
00188 
00189         return NULL;
00190 }
00191 
00192 /**
00193  * Determine transmitting network device
00194  *
00195  * @v st_dest           Destination network-layer address
00196  * @ret netdev          Transmitting network device, or NULL
00197  */
00198 static struct net_device * ipv4_netdev ( struct sockaddr_tcpip *st_dest ) {
00199         struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
00200         struct in_addr dest = sin_dest->sin_addr;
00201         struct ipv4_miniroute *miniroute;
00202 
00203         /* Find routing table entry */
00204         miniroute = ipv4_route ( sin_dest->sin_scope_id, &dest );
00205         if ( ! miniroute )
00206                 return NULL;
00207 
00208         return miniroute->netdev;
00209 }
00210 
00211 /**
00212  * Check if IPv4 fragment matches fragment reassembly buffer
00213  *
00214  * @v fragment          Fragment reassembly buffer
00215  * @v iobuf             I/O buffer
00216  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00217  * @ret is_fragment     Fragment matches this reassembly buffer
00218  */
00219 static int ipv4_is_fragment ( struct fragment *fragment,
00220                               struct io_buffer *iobuf,
00221                               size_t hdrlen __unused ) {
00222         struct iphdr *frag_iphdr = fragment->iobuf->data;
00223         struct iphdr *iphdr = iobuf->data;
00224 
00225         return ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
00226                  ( iphdr->ident == frag_iphdr->ident ) );
00227 }
00228 
00229 /**
00230  * Get IPv4 fragment offset
00231  *
00232  * @v iobuf             I/O buffer
00233  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00234  * @ret offset          Offset
00235  */
00236 static size_t ipv4_fragment_offset ( struct io_buffer *iobuf,
00237                                      size_t hdrlen __unused ) {
00238         struct iphdr *iphdr = iobuf->data;
00239 
00240         return ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
00241 }
00242 
00243 /**
00244  * Check if more fragments exist
00245  *
00246  * @v iobuf             I/O buffer
00247  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00248  * @ret more_frags      More fragments exist
00249  */
00250 static int ipv4_more_fragments ( struct io_buffer *iobuf,
00251                                  size_t hdrlen __unused ) {
00252         struct iphdr *iphdr = iobuf->data;
00253 
00254         return ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) );
00255 }
00256 
00257 /** IPv4 fragment reassembler */
00258 static struct fragment_reassembler ipv4_reassembler = {
00259         .list = LIST_HEAD_INIT ( ipv4_reassembler.list ),
00260         .is_fragment = ipv4_is_fragment,
00261         .fragment_offset = ipv4_fragment_offset,
00262         .more_fragments = ipv4_more_fragments,
00263         .stats = &ipv4_stats,
00264 };
00265 
00266 /**
00267  * Add IPv4 pseudo-header checksum to existing checksum
00268  *
00269  * @v iobuf             I/O buffer
00270  * @v csum              Existing checksum
00271  * @ret csum            Updated checksum
00272  */
00273 static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) {
00274         struct ipv4_pseudo_header pshdr;
00275         struct iphdr *iphdr = iobuf->data;
00276         size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
00277 
00278         /* Build pseudo-header */
00279         pshdr.src = iphdr->src;
00280         pshdr.dest = iphdr->dest;
00281         pshdr.zero_padding = 0x00;
00282         pshdr.protocol = iphdr->protocol;
00283         pshdr.len = htons ( iob_len ( iobuf ) - hdrlen );
00284 
00285         /* Update the checksum value */
00286         return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
00287 }
00288 
00289 /**
00290  * Transmit IP packet
00291  *
00292  * @v iobuf             I/O buffer
00293  * @v tcpip             Transport-layer protocol
00294  * @v st_src            Source network-layer address
00295  * @v st_dest           Destination network-layer address
00296  * @v netdev            Network device to use if no route found, or NULL
00297  * @v trans_csum        Transport-layer checksum to complete, or NULL
00298  * @ret rc              Status
00299  *
00300  * This function expects a transport-layer segment and prepends the IP header
00301  */
00302 static int ipv4_tx ( struct io_buffer *iobuf,
00303                      struct tcpip_protocol *tcpip_protocol,
00304                      struct sockaddr_tcpip *st_src,
00305                      struct sockaddr_tcpip *st_dest,
00306                      struct net_device *netdev,
00307                      uint16_t *trans_csum ) {
00308         struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
00309         struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
00310         struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
00311         struct ipv4_miniroute *miniroute;
00312         struct in_addr next_hop;
00313         struct in_addr netmask = { .s_addr = 0 };
00314         uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
00315         const void *ll_dest;
00316         int rc;
00317 
00318         /* Start profiling */
00319         profile_start ( &ipv4_tx_profiler );
00320 
00321         /* Update statistics */
00322         ipv4_stats.out_requests++;
00323 
00324         /* Fill up the IP header, except source address */
00325         memset ( iphdr, 0, sizeof ( *iphdr ) );
00326         iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
00327         iphdr->service = IP_TOS;
00328         iphdr->len = htons ( iob_len ( iobuf ) );       
00329         iphdr->ttl = IP_TTL;
00330         iphdr->protocol = tcpip_protocol->tcpip_proto;
00331         iphdr->dest = sin_dest->sin_addr;
00332 
00333         /* Use routing table to identify next hop and transmitting netdev */
00334         next_hop = iphdr->dest;
00335         if ( sin_src )
00336                 iphdr->src = sin_src->sin_addr;
00337         if ( ( next_hop.s_addr != INADDR_BROADCAST ) &&
00338              ( ( miniroute = ipv4_route ( sin_dest->sin_scope_id,
00339                                           &next_hop ) ) != NULL ) ) {
00340                 iphdr->src = miniroute->address;
00341                 netmask = miniroute->netmask;
00342                 netdev = miniroute->netdev;
00343         }
00344         if ( ! netdev ) {
00345                 DBGC ( sin_dest->sin_addr, "IPv4 has no route to %s\n",
00346                        inet_ntoa ( iphdr->dest ) );
00347                 ipv4_stats.out_no_routes++;
00348                 rc = -ENETUNREACH;
00349                 goto err;
00350         }
00351 
00352         /* (Ab)use the "ident" field to convey metadata about the
00353          * network device statistics into packet traces.  Useful for
00354          * extracting debug information from non-debug builds.
00355          */
00356         iphdr->ident = htons ( ( (++next_ident_high) << 8 ) |
00357                                ( ( netdev->rx_stats.bad & 0xf ) << 4 ) |
00358                                ( ( netdev->rx_stats.good & 0xf ) << 0 ) );
00359 
00360         /* Fix up checksums */
00361         if ( trans_csum ) {
00362                 *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum );
00363                 if ( ! *trans_csum )
00364                         *trans_csum = tcpip_protocol->zero_csum;
00365         }
00366         iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
00367 
00368         /* Print IP4 header for debugging */
00369         DBGC2 ( sin_dest->sin_addr, "IPv4 TX %s->", inet_ntoa ( iphdr->src ) );
00370         DBGC2 ( sin_dest->sin_addr, "%s len %d proto %d id %04x csum %04x\n",
00371                 inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ),
00372                 iphdr->protocol, ntohs ( iphdr->ident ),
00373                 ntohs ( iphdr->chksum ) );
00374 
00375         /* Calculate link-layer destination address, if possible */
00376         if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){
00377                 /* Broadcast address */
00378                 ipv4_stats.out_bcast_pkts++;
00379                 ll_dest = netdev->ll_broadcast;
00380         } else if ( IN_IS_MULTICAST ( next_hop.s_addr ) ) {
00381                 /* Multicast address */
00382                 ipv4_stats.out_mcast_pkts++;
00383                 if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET, &next_hop,
00384                                                            ll_dest_buf ) ) !=0){
00385                         DBGC ( sin_dest->sin_addr, "IPv4 could not hash "
00386                                "multicast %s: %s\n",
00387                                inet_ntoa ( next_hop ), strerror ( rc ) );
00388                         goto err;
00389                 }
00390                 ll_dest = ll_dest_buf;
00391         } else {
00392                 /* Unicast address */
00393                 ll_dest = NULL;
00394         }
00395 
00396         /* Update statistics */
00397         ipv4_stats.out_transmits++;
00398         ipv4_stats.out_octets += iob_len ( iobuf );
00399 
00400         /* Hand off to link layer (via ARP if applicable) */
00401         if ( ll_dest ) {
00402                 if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest,
00403                                      netdev->ll_addr ) ) != 0 ) {
00404                         DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
00405                                "packet via %s: %s\n",
00406                                netdev->name, strerror ( rc ) );
00407                         return rc;
00408                 }
00409         } else {
00410                 if ( ( rc = arp_tx ( iobuf, netdev, &ipv4_protocol, &next_hop,
00411                                      &iphdr->src, netdev->ll_addr ) ) != 0 ) {
00412                         DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
00413                                "packet via %s: %s\n",
00414                                netdev->name, strerror ( rc ) );
00415                         return rc;
00416                 }
00417         }
00418 
00419         profile_stop ( &ipv4_tx_profiler );
00420         return 0;
00421 
00422  err:
00423         free_iob ( iobuf );
00424         return rc;
00425 }
00426 
00427 /**
00428  * Check if network device has any IPv4 address
00429  *
00430  * @v netdev            Network device
00431  * @ret has_any_addr    Network device has any IPv4 address
00432  */
00433 int ipv4_has_any_addr ( struct net_device *netdev ) {
00434         struct ipv4_miniroute *miniroute;
00435 
00436         list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
00437                 if ( miniroute->netdev == netdev )
00438                         return 1;
00439         }
00440         return 0;
00441 }
00442 
00443 /**
00444  * Check if network device has a specific IPv4 address
00445  *
00446  * @v netdev            Network device
00447  * @v addr              IPv4 address
00448  * @ret has_addr        Network device has this IPv4 address
00449  */
00450 static int ipv4_has_addr ( struct net_device *netdev, struct in_addr addr ) {
00451         struct ipv4_miniroute *miniroute;
00452 
00453         list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
00454                 if ( ( miniroute->netdev == netdev ) &&
00455                      ( miniroute->address.s_addr == addr.s_addr ) ) {
00456                         /* Found matching address */
00457                         return 1;
00458                 }
00459         }
00460         return 0;
00461 }
00462 
00463 /**
00464  * Process incoming packets
00465  *
00466  * @v iobuf             I/O buffer
00467  * @v netdev            Network device
00468  * @v ll_dest           Link-layer destination address
00469  * @v ll_source         Link-layer destination source
00470  * @v flags             Packet flags
00471  * @ret rc              Return status code
00472  *
00473  * This function expects an IP4 network datagram. It processes the headers 
00474  * and sends it to the transport layer.
00475  */
00476 static int ipv4_rx ( struct io_buffer *iobuf,
00477                      struct net_device *netdev,
00478                      const void *ll_dest __unused,
00479                      const void *ll_source __unused,
00480                      unsigned int flags ) {
00481         struct iphdr *iphdr = iobuf->data;
00482         size_t hdrlen;
00483         size_t len;
00484         union {
00485                 struct sockaddr_in sin;
00486                 struct sockaddr_tcpip st;
00487         } src, dest;
00488         uint16_t csum;
00489         uint16_t pshdr_csum;
00490         int rc;
00491 
00492         /* Start profiling */
00493         profile_start ( &ipv4_rx_profiler );
00494 
00495         /* Update statistics */
00496         ipv4_stats.in_receives++;
00497         ipv4_stats.in_octets += iob_len ( iobuf );
00498         if ( flags & LL_BROADCAST ) {
00499                 ipv4_stats.in_bcast_pkts++;
00500         } else if ( flags & LL_MULTICAST ) {
00501                 ipv4_stats.in_mcast_pkts++;
00502         }
00503 
00504         /* Sanity check the IPv4 header */
00505         if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) {
00506                 DBGC ( iphdr->src, "IPv4 packet too short at %zd bytes (min "
00507                        "%zd bytes)\n", iob_len ( iobuf ), sizeof ( *iphdr ) );
00508                 goto err_header;
00509         }
00510         if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
00511                 DBGC ( iphdr->src, "IPv4 version %#02x not supported\n",
00512                        iphdr->verhdrlen );
00513                 goto err_header;
00514         }
00515         hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
00516         if ( hdrlen < sizeof ( *iphdr ) ) {
00517                 DBGC ( iphdr->src, "IPv4 header too short at %zd bytes (min "
00518                        "%zd bytes)\n", hdrlen, sizeof ( *iphdr ) );
00519                 goto err_header;
00520         }
00521         if ( hdrlen > iob_len ( iobuf ) ) {
00522                 DBGC ( iphdr->src, "IPv4 header too long at %zd bytes "
00523                        "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) );
00524                 goto err_header;
00525         }
00526         if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
00527                 DBGC ( iphdr->src, "IPv4 checksum incorrect (is %04x "
00528                        "including checksum field, should be 0000)\n", csum );
00529                 goto err_header;
00530         }
00531         len = ntohs ( iphdr->len );
00532         if ( len < hdrlen ) {
00533                 DBGC ( iphdr->src, "IPv4 length too short at %zd bytes "
00534                        "(header is %zd bytes)\n", len, hdrlen );
00535                 goto err_header;
00536         }
00537         if ( len > iob_len ( iobuf ) ) {
00538                 DBGC ( iphdr->src, "IPv4 length too long at %zd bytes "
00539                        "(packet is %zd bytes)\n", len, iob_len ( iobuf ) );
00540                 ipv4_stats.in_truncated_pkts++;
00541                 goto err_other;
00542         }
00543 
00544         /* Truncate packet to correct length */
00545         iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
00546 
00547         /* Print IPv4 header for debugging */
00548         DBGC2 ( iphdr->src, "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
00549         DBGC2 ( iphdr->src, "%s len %d proto %d id %04x csum %04x\n",
00550                 inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
00551                 ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
00552 
00553         /* Discard unicast packets not destined for us */
00554         if ( ( ! ( flags & LL_MULTICAST ) ) &&
00555              ( iphdr->dest.s_addr != INADDR_BROADCAST ) &&
00556              ipv4_has_any_addr ( netdev ) &&
00557              ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) {
00558                 DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet "
00559                        "for %s\n", inet_ntoa ( iphdr->dest ) );
00560                 ipv4_stats.in_addr_errors++;
00561                 goto err_other;
00562         }
00563 
00564         /* Perform fragment reassembly if applicable */
00565         if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) {
00566                 /* Pass the fragment to fragment_reassemble() which returns
00567                  * either a fully reassembled I/O buffer or NULL.
00568                  */
00569                 iobuf = fragment_reassemble ( &ipv4_reassembler, iobuf,
00570                                               &hdrlen );
00571                 if ( ! iobuf )
00572                         return 0;
00573                 iphdr = iobuf->data;
00574         }
00575 
00576         /* Construct socket addresses, calculate pseudo-header
00577          * checksum, and hand off to transport layer
00578          */
00579         memset ( &src, 0, sizeof ( src ) );
00580         src.sin.sin_family = AF_INET;
00581         src.sin.sin_addr = iphdr->src;
00582         memset ( &dest, 0, sizeof ( dest ) );
00583         dest.sin.sin_family = AF_INET;
00584         dest.sin.sin_addr = iphdr->dest;
00585         pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
00586         iob_pull ( iobuf, hdrlen );
00587         if ( ( rc = tcpip_rx ( iobuf, netdev, iphdr->protocol, &src.st,
00588                                &dest.st, pshdr_csum, &ipv4_stats ) ) != 0 ) {
00589                 DBGC ( src.sin.sin_addr, "IPv4 received packet rejected by "
00590                        "stack: %s\n", strerror ( rc ) );
00591                 return rc;
00592         }
00593 
00594         profile_stop ( &ipv4_rx_profiler );
00595         return 0;
00596 
00597  err_header:
00598         ipv4_stats.in_hdr_errors++;
00599  err_other:
00600         free_iob ( iobuf );
00601         return -EINVAL;
00602 }
00603 
00604 /** 
00605  * Check existence of IPv4 address for ARP
00606  *
00607  * @v netdev            Network device
00608  * @v net_addr          Network-layer address
00609  * @ret rc              Return status code
00610  */
00611 static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
00612         const struct in_addr *address = net_addr;
00613 
00614         if ( ipv4_has_addr ( netdev, *address ) )
00615                 return 0;
00616 
00617         return -ENOENT;
00618 }
00619 
00620 /**
00621  * Parse IPv4 address
00622  *
00623  * @v string            IPv4 address string
00624  * @ret in              IPv4 address to fill in
00625  * @ret ok              IPv4 address is valid
00626  *
00627  * Note that this function returns nonzero iff the address is valid,
00628  * to match the standard BSD API function of the same name.  Unlike
00629  * most other iPXE functions, a zero therefore indicates failure.
00630  */
00631 int inet_aton ( const char *string, struct in_addr *in ) {
00632         const char *separator = "...";
00633         uint8_t *byte = ( ( uint8_t * ) in );
00634         char *endp;
00635         unsigned long value;
00636 
00637         while ( 1 ) {
00638                 value = strtoul ( string, &endp, 0 );
00639                 if ( string == endp )
00640                         return 0;
00641                 if ( value > 0xff )
00642                         return 0;
00643                 *(byte++) = value;
00644                 if ( *endp != *separator )
00645                         return 0;
00646                 if ( ! *(separator++) )
00647                         return 1;
00648                 string = ( endp + 1 );
00649         }
00650 }
00651 
00652 /**
00653  * Convert IPv4 address to dotted-quad notation
00654  *
00655  * @v in                IPv4 address
00656  * @ret string          IPv4 address in dotted-quad notation
00657  */
00658 char * inet_ntoa ( struct in_addr in ) {
00659         static char buf[16]; /* "xxx.xxx.xxx.xxx" */
00660         uint8_t *bytes = ( uint8_t * ) &in;
00661         
00662         sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
00663         return buf;
00664 }
00665 
00666 /**
00667  * Transcribe IPv4 address
00668  *
00669  * @v net_addr          IPv4 address
00670  * @ret string          IPv4 address in dotted-quad notation
00671  *
00672  */
00673 static const char * ipv4_ntoa ( const void *net_addr ) {
00674         return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
00675 }
00676 
00677 /**
00678  * Transcribe IPv4 socket address
00679  *
00680  * @v sa                Socket address
00681  * @ret string          Socket address in standard notation
00682  */
00683 static const char * ipv4_sock_ntoa ( struct sockaddr *sa ) {
00684         struct sockaddr_in *sin = ( ( struct sockaddr_in * ) sa );
00685 
00686         return inet_ntoa ( sin->sin_addr );
00687 }
00688 
00689 /**
00690  * Parse IPv4 socket address
00691  *
00692  * @v string            Socket address string
00693  * @v sa                Socket address to fill in
00694  * @ret rc              Return status code
00695  */
00696 static int ipv4_sock_aton ( const char *string, struct sockaddr *sa ) {
00697         struct sockaddr_in *sin = ( ( struct sockaddr_in * ) sa );
00698         struct in_addr in;
00699 
00700         if ( inet_aton ( string, &in ) ) {
00701                 sin->sin_addr = in;
00702                 return 0;
00703         }
00704         return -EINVAL;
00705 }
00706 
00707 /** IPv4 protocol */
00708 struct net_protocol ipv4_protocol __net_protocol = {
00709         .name = "IP",
00710         .net_proto = htons ( ETH_P_IP ),
00711         .net_addr_len = sizeof ( struct in_addr ),
00712         .rx = ipv4_rx,
00713         .ntoa = ipv4_ntoa,
00714 };
00715 
00716 /** IPv4 TCPIP net protocol */
00717 struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = {
00718         .name = "IPv4",
00719         .sa_family = AF_INET,
00720         .header_len = sizeof ( struct iphdr ),
00721         .net_protocol = &ipv4_protocol,
00722         .tx = ipv4_tx,
00723         .netdev = ipv4_netdev,
00724 };
00725 
00726 /** IPv4 ARP protocol */
00727 struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = {
00728         .net_protocol = &ipv4_protocol,
00729         .check = ipv4_arp_check,
00730 };
00731 
00732 /** IPv4 socket address converter */
00733 struct sockaddr_converter ipv4_sockaddr_converter __sockaddr_converter = {
00734         .family = AF_INET,
00735         .ntoa = ipv4_sock_ntoa,
00736         .aton = ipv4_sock_aton,
00737 };
00738 
00739 /******************************************************************************
00740  *
00741  * Settings
00742  *
00743  ******************************************************************************
00744  */
00745 
00746 /**
00747  * Parse IPv4 address setting value
00748  *
00749  * @v type              Setting type
00750  * @v value             Formatted setting value
00751  * @v buf               Buffer to contain raw value
00752  * @v len               Length of buffer
00753  * @ret len             Length of raw value, or negative error
00754  */
00755 int parse_ipv4_setting ( const struct setting_type *type __unused,
00756                          const char *value, void *buf, size_t len ) {
00757         struct in_addr ipv4;
00758 
00759         /* Parse IPv4 address */
00760         if ( inet_aton ( value, &ipv4 ) == 0 )
00761                 return -EINVAL;
00762 
00763         /* Copy to buffer */
00764         if ( len > sizeof ( ipv4 ) )
00765                 len = sizeof ( ipv4 );
00766         memcpy ( buf, &ipv4, len );
00767 
00768         return ( sizeof ( ipv4 ) );
00769 }
00770 
00771 /**
00772  * Format IPv4 address setting value
00773  *
00774  * @v type              Setting type
00775  * @v raw               Raw setting value
00776  * @v raw_len           Length of raw setting value
00777  * @v buf               Buffer to contain formatted value
00778  * @v len               Length of buffer
00779  * @ret len             Length of formatted value, or negative error
00780  */
00781 int format_ipv4_setting ( const struct setting_type *type __unused,
00782                           const void *raw, size_t raw_len, char *buf,
00783                           size_t len ) {
00784         const struct in_addr *ipv4 = raw;
00785 
00786         if ( raw_len < sizeof ( *ipv4 ) )
00787                 return -EINVAL;
00788         return snprintf ( buf, len, "%s", inet_ntoa ( *ipv4 ) );
00789 }
00790 
00791 /** IPv4 address setting */
00792 const struct setting ip_setting __setting ( SETTING_IP4, ip ) = {
00793         .name = "ip",
00794         .description = "IP address",
00795         .tag = DHCP_EB_YIADDR,
00796         .type = &setting_type_ipv4,
00797 };
00798 
00799 /** IPv4 subnet mask setting */
00800 const struct setting netmask_setting __setting ( SETTING_IP4, netmask ) = {
00801         .name = "netmask",
00802         .description = "Subnet mask",
00803         .tag = DHCP_SUBNET_MASK,
00804         .type = &setting_type_ipv4,
00805 };
00806 
00807 /** Default gateway setting */
00808 const struct setting gateway_setting __setting ( SETTING_IP4, gateway ) = {
00809         .name = "gateway",
00810         .description = "Default gateway",
00811         .tag = DHCP_ROUTERS,
00812         .type = &setting_type_ipv4,
00813 };
00814 
00815 /**
00816  * Send gratuitous ARP, if applicable
00817  *
00818  * @v netdev            Network device
00819  * @v address           IPv4 address
00820  * @v netmask           Subnet mask
00821  * @v gateway           Gateway address (if any)
00822  * @ret rc              Return status code
00823  */
00824 static int ipv4_gratuitous_arp ( struct net_device *netdev,
00825                                  struct in_addr address,
00826                                  struct in_addr netmask __unused,
00827                                  struct in_addr gateway __unused ) {
00828         int rc;
00829 
00830         /* Do nothing if network device already has this IPv4 address */
00831         if ( ipv4_has_addr ( netdev, address ) )
00832                 return 0;
00833 
00834         /* Transmit gratuitous ARP */
00835         DBGC ( netdev, "IPv4 sending gratuitous ARP for %s via %s\n",
00836                inet_ntoa ( address ), netdev->name );
00837         if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, &address,
00838                                      &address ) ) != 0 ) {
00839                 DBGC ( netdev, "IPv4 could not transmit gratuitous ARP: %s\n",
00840                        strerror ( rc ) );
00841                 /* Treat failures as non-fatal */
00842         }
00843 
00844         return 0;
00845 }
00846 
00847 /**
00848  * Process IPv4 network device settings
00849  *
00850  * @v apply             Application method
00851  * @ret rc              Return status code
00852  */
00853 static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
00854                                              struct in_addr address,
00855                                              struct in_addr netmask,
00856                                              struct in_addr gateway ) ) {
00857         struct net_device *netdev;
00858         struct settings *settings;
00859         struct in_addr address = { 0 };
00860         struct in_addr netmask = { 0 };
00861         struct in_addr gateway = { 0 };
00862         int rc;
00863 
00864         /* Process settings for each network device */
00865         for_each_netdev ( netdev ) {
00866 
00867                 /* Get network device settings */
00868                 settings = netdev_settings ( netdev );
00869 
00870                 /* Get IPv4 address */
00871                 address.s_addr = 0;
00872                 fetch_ipv4_setting ( settings, &ip_setting, &address );
00873                 if ( ! address.s_addr )
00874                         continue;
00875 
00876                 /* Get subnet mask */
00877                 fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
00878 
00879                 /* Calculate default netmask, if necessary */
00880                 if ( ! netmask.s_addr ) {
00881                         if ( IN_IS_CLASSA ( address.s_addr ) ) {
00882                                 netmask.s_addr = INADDR_NET_CLASSA;
00883                         } else if ( IN_IS_CLASSB ( address.s_addr ) ) {
00884                                 netmask.s_addr = INADDR_NET_CLASSB;
00885                         } else if ( IN_IS_CLASSC ( address.s_addr ) ) {
00886                                 netmask.s_addr = INADDR_NET_CLASSC;
00887                         }
00888                 }
00889 
00890                 /* Get default gateway, if present */
00891                 fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
00892 
00893                 /* Apply settings */
00894                 if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 )
00895                         return rc;
00896         }
00897 
00898         return 0;
00899 }
00900 
00901 /**
00902  * Create IPv4 routing table based on configured settings
00903  *
00904  * @ret rc              Return status code
00905  */
00906 static int ipv4_create_routes ( void ) {
00907         struct ipv4_miniroute *miniroute;
00908         struct ipv4_miniroute *tmp;
00909         int rc;
00910 
00911         /* Send gratuitous ARPs for any new IPv4 addresses */
00912         ipv4_settings ( ipv4_gratuitous_arp );
00913 
00914         /* Delete all existing routes */
00915         list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
00916                 del_ipv4_miniroute ( miniroute );
00917 
00918         /* Create a route for each configured network device */
00919         if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 )
00920                 return rc;
00921 
00922         return 0;
00923 }
00924 
00925 /** IPv4 settings applicator */
00926 struct settings_applicator ipv4_settings_applicator __settings_applicator = {
00927         .apply = ipv4_create_routes,
00928 };
00929 
00930 /* Drag in objects via ipv4_protocol */
00931 REQUIRING_SYMBOL ( ipv4_protocol );
00932 
00933 /* Drag in ICMPv4 */
00934 REQUIRE_OBJECT ( icmpv4 );