123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933 |
- /*
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
- * Copyright (C) 2006 Nikhil Chandru Rao
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- #include <string.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <byteswap.h>
- #include <ipxe/list.h>
- #include <ipxe/in.h>
- #include <ipxe/arp.h>
- #include <ipxe/if_ether.h>
- #include <ipxe/iobuf.h>
- #include <ipxe/netdevice.h>
- #include <ipxe/ip.h>
- #include <ipxe/tcpip.h>
- #include <ipxe/dhcp.h>
- #include <ipxe/settings.h>
- #include <ipxe/fragment.h>
- #include <ipxe/ipstat.h>
- #include <ipxe/profile.h>
-
- /** @file
- *
- * IPv4 protocol
- *
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- /* Unique IP datagram identification number (high byte) */
- static uint8_t next_ident_high = 0;
-
- /** List of IPv4 miniroutes */
- struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
-
- /** IPv4 statistics */
- static struct ip_statistics ipv4_stats;
-
- /** IPv4 statistics family */
- struct ip_statistics_family
- ipv4_stats_family __ip_statistics_family ( IP_STATISTICS_IPV4 ) = {
- .version = 4,
- .stats = &ipv4_stats,
- };
-
- /** Transmit profiler */
- static struct profiler ipv4_tx_profiler __profiler = { .name = "ipv4.tx" };
-
- /** Receive profiler */
- static struct profiler ipv4_rx_profiler __profiler = { .name = "ipv4.rx" };
-
- /**
- * Add IPv4 minirouting table entry
- *
- * @v netdev Network device
- * @v address IPv4 address
- * @v netmask Subnet mask
- * @v gateway Gateway address (if any)
- * @ret rc Return status code
- */
- static int add_ipv4_miniroute ( struct net_device *netdev,
- struct in_addr address, struct in_addr netmask,
- struct in_addr gateway ) {
- struct ipv4_miniroute *miniroute;
-
- DBGC ( netdev, "IPv4 add %s", inet_ntoa ( address ) );
- DBGC ( netdev, "/%s ", inet_ntoa ( netmask ) );
- if ( gateway.s_addr )
- DBGC ( netdev, "gw %s ", inet_ntoa ( gateway ) );
- DBGC ( netdev, "via %s\n", netdev->name );
-
- /* Allocate and populate miniroute structure */
- miniroute = malloc ( sizeof ( *miniroute ) );
- if ( ! miniroute ) {
- DBGC ( netdev, "IPv4 could not add miniroute\n" );
- return -ENOMEM;
- }
-
- /* Record routing information */
- miniroute->netdev = netdev_get ( netdev );
- miniroute->address = address;
- miniroute->netmask = netmask;
- miniroute->gateway = gateway;
-
- /* Add to end of list if we have a gateway, otherwise
- * to start of list.
- */
- if ( gateway.s_addr ) {
- list_add_tail ( &miniroute->list, &ipv4_miniroutes );
- } else {
- list_add ( &miniroute->list, &ipv4_miniroutes );
- }
-
- return 0;
- }
-
- /**
- * Delete IPv4 minirouting table entry
- *
- * @v miniroute Routing table entry
- */
- static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) {
- struct net_device *netdev = miniroute->netdev;
-
- DBGC ( netdev, "IPv4 del %s", inet_ntoa ( miniroute->address ) );
- DBGC ( netdev, "/%s ", inet_ntoa ( miniroute->netmask ) );
- if ( miniroute->gateway.s_addr )
- DBGC ( netdev, "gw %s ", inet_ntoa ( miniroute->gateway ) );
- DBGC ( netdev, "via %s\n", miniroute->netdev->name );
-
- netdev_put ( miniroute->netdev );
- list_del ( &miniroute->list );
- free ( miniroute );
- }
-
- /**
- * Perform IPv4 routing
- *
- * @v scope_id Destination address scope ID
- * @v dest Final destination address
- * @ret dest Next hop destination address
- * @ret miniroute Routing table entry to use, or NULL if no route
- *
- * If the route requires use of a gateway, the next hop destination
- * address will be overwritten with the gateway address.
- */
- static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
- struct in_addr *dest ) {
- struct ipv4_miniroute *miniroute;
-
- /* Find first usable route in routing table */
- list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
-
- /* Skip closed network devices */
- if ( ! netdev_is_open ( miniroute->netdev ) )
- continue;
-
- if ( IN_IS_MULTICAST ( dest->s_addr ) ) {
-
- /* If destination is non-global, and the scope ID
- * matches this network device, then use this route.
- */
- if ( miniroute->netdev->index == scope_id )
- return miniroute;
-
- } else {
-
- /* If destination is an on-link global
- * address, then use this route.
- */
- if ( ( ( dest->s_addr ^ miniroute->address.s_addr )
- & miniroute->netmask.s_addr ) == 0 )
- return miniroute;
-
- /* If destination is an off-link global
- * address, and we have a default gateway,
- * then use this route.
- */
- if ( miniroute->gateway.s_addr ) {
- *dest = miniroute->gateway;
- return miniroute;
- }
- }
- }
-
- return NULL;
- }
-
- /**
- * Determine transmitting network device
- *
- * @v st_dest Destination network-layer address
- * @ret netdev Transmitting network device, or NULL
- */
- static struct net_device * ipv4_netdev ( struct sockaddr_tcpip *st_dest ) {
- struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
- struct in_addr dest = sin_dest->sin_addr;
- struct ipv4_miniroute *miniroute;
-
- /* Find routing table entry */
- miniroute = ipv4_route ( sin_dest->sin_scope_id, &dest );
- if ( ! miniroute )
- return NULL;
-
- return miniroute->netdev;
- }
-
- /**
- * Check if IPv4 fragment matches fragment reassembly buffer
- *
- * @v fragment Fragment reassembly buffer
- * @v iobuf I/O buffer
- * @v hdrlen Length of non-fragmentable potion of I/O buffer
- * @ret is_fragment Fragment matches this reassembly buffer
- */
- static int ipv4_is_fragment ( struct fragment *fragment,
- struct io_buffer *iobuf,
- size_t hdrlen __unused ) {
- struct iphdr *frag_iphdr = fragment->iobuf->data;
- struct iphdr *iphdr = iobuf->data;
-
- return ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
- ( iphdr->ident == frag_iphdr->ident ) );
- }
-
- /**
- * Get IPv4 fragment offset
- *
- * @v iobuf I/O buffer
- * @v hdrlen Length of non-fragmentable potion of I/O buffer
- * @ret offset Offset
- */
- static size_t ipv4_fragment_offset ( struct io_buffer *iobuf,
- size_t hdrlen __unused ) {
- struct iphdr *iphdr = iobuf->data;
-
- return ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
- }
-
- /**
- * Check if more fragments exist
- *
- * @v iobuf I/O buffer
- * @v hdrlen Length of non-fragmentable potion of I/O buffer
- * @ret more_frags More fragments exist
- */
- static int ipv4_more_fragments ( struct io_buffer *iobuf,
- size_t hdrlen __unused ) {
- struct iphdr *iphdr = iobuf->data;
-
- return ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) );
- }
-
- /** IPv4 fragment reassembler */
- static struct fragment_reassembler ipv4_reassembler = {
- .list = LIST_HEAD_INIT ( ipv4_reassembler.list ),
- .is_fragment = ipv4_is_fragment,
- .fragment_offset = ipv4_fragment_offset,
- .more_fragments = ipv4_more_fragments,
- .stats = &ipv4_stats,
- };
-
- /**
- * Add IPv4 pseudo-header checksum to existing checksum
- *
- * @v iobuf I/O buffer
- * @v csum Existing checksum
- * @ret csum Updated checksum
- */
- static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) {
- struct ipv4_pseudo_header pshdr;
- struct iphdr *iphdr = iobuf->data;
- size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
-
- /* Build pseudo-header */
- pshdr.src = iphdr->src;
- pshdr.dest = iphdr->dest;
- pshdr.zero_padding = 0x00;
- pshdr.protocol = iphdr->protocol;
- pshdr.len = htons ( iob_len ( iobuf ) - hdrlen );
-
- /* Update the checksum value */
- return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
- }
-
- /**
- * Transmit IP packet
- *
- * @v iobuf I/O buffer
- * @v tcpip Transport-layer protocol
- * @v st_src Source network-layer address
- * @v st_dest Destination network-layer address
- * @v netdev Network device to use if no route found, or NULL
- * @v trans_csum Transport-layer checksum to complete, or NULL
- * @ret rc Status
- *
- * This function expects a transport-layer segment and prepends the IP header
- */
- static int ipv4_tx ( struct io_buffer *iobuf,
- struct tcpip_protocol *tcpip_protocol,
- struct sockaddr_tcpip *st_src,
- struct sockaddr_tcpip *st_dest,
- struct net_device *netdev,
- uint16_t *trans_csum ) {
- struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) );
- struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src );
- struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest );
- struct ipv4_miniroute *miniroute;
- struct in_addr next_hop;
- struct in_addr netmask = { .s_addr = 0 };
- uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
- const void *ll_dest;
- int rc;
-
- /* Start profiling */
- profile_start ( &ipv4_tx_profiler );
-
- /* Update statistics */
- ipv4_stats.out_requests++;
-
- /* Fill up the IP header, except source address */
- memset ( iphdr, 0, sizeof ( *iphdr ) );
- iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
- iphdr->service = IP_TOS;
- iphdr->len = htons ( iob_len ( iobuf ) );
- iphdr->ttl = IP_TTL;
- iphdr->protocol = tcpip_protocol->tcpip_proto;
- iphdr->dest = sin_dest->sin_addr;
-
- /* Use routing table to identify next hop and transmitting netdev */
- next_hop = iphdr->dest;
- if ( sin_src )
- iphdr->src = sin_src->sin_addr;
- if ( ( next_hop.s_addr != INADDR_BROADCAST ) &&
- ( ( miniroute = ipv4_route ( sin_dest->sin_scope_id,
- &next_hop ) ) != NULL ) ) {
- iphdr->src = miniroute->address;
- netmask = miniroute->netmask;
- netdev = miniroute->netdev;
- }
- if ( ! netdev ) {
- DBGC ( sin_dest->sin_addr, "IPv4 has no route to %s\n",
- inet_ntoa ( iphdr->dest ) );
- ipv4_stats.out_no_routes++;
- rc = -ENETUNREACH;
- goto err;
- }
-
- /* (Ab)use the "ident" field to convey metadata about the
- * network device statistics into packet traces. Useful for
- * extracting debug information from non-debug builds.
- */
- iphdr->ident = htons ( ( (++next_ident_high) << 8 ) |
- ( ( netdev->rx_stats.bad & 0xf ) << 4 ) |
- ( ( netdev->rx_stats.good & 0xf ) << 0 ) );
-
- /* Fix up checksums */
- if ( trans_csum ) {
- *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum );
- if ( ! *trans_csum )
- *trans_csum = tcpip_protocol->zero_csum;
- }
- iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
-
- /* Print IP4 header for debugging */
- DBGC2 ( sin_dest->sin_addr, "IPv4 TX %s->", inet_ntoa ( iphdr->src ) );
- DBGC2 ( sin_dest->sin_addr, "%s len %d proto %d id %04x csum %04x\n",
- inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ),
- iphdr->protocol, ntohs ( iphdr->ident ),
- ntohs ( iphdr->chksum ) );
-
- /* Calculate link-layer destination address, if possible */
- if ( ( ( next_hop.s_addr ^ INADDR_BROADCAST ) & ~netmask.s_addr ) == 0){
- /* Broadcast address */
- ipv4_stats.out_bcast_pkts++;
- ll_dest = netdev->ll_broadcast;
- } else if ( IN_IS_MULTICAST ( next_hop.s_addr ) ) {
- /* Multicast address */
- ipv4_stats.out_mcast_pkts++;
- if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET, &next_hop,
- ll_dest_buf ) ) !=0){
- DBGC ( sin_dest->sin_addr, "IPv4 could not hash "
- "multicast %s: %s\n",
- inet_ntoa ( next_hop ), strerror ( rc ) );
- goto err;
- }
- ll_dest = ll_dest_buf;
- } else {
- /* Unicast address */
- ll_dest = NULL;
- }
-
- /* Update statistics */
- ipv4_stats.out_transmits++;
- ipv4_stats.out_octets += iob_len ( iobuf );
-
- /* Hand off to link layer (via ARP if applicable) */
- if ( ll_dest ) {
- if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest,
- netdev->ll_addr ) ) != 0 ) {
- DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
- "packet via %s: %s\n",
- netdev->name, strerror ( rc ) );
- return rc;
- }
- } else {
- if ( ( rc = arp_tx ( iobuf, netdev, &ipv4_protocol, &next_hop,
- &iphdr->src, netdev->ll_addr ) ) != 0 ) {
- DBGC ( sin_dest->sin_addr, "IPv4 could not transmit "
- "packet via %s: %s\n",
- netdev->name, strerror ( rc ) );
- return rc;
- }
- }
-
- profile_stop ( &ipv4_tx_profiler );
- return 0;
-
- err:
- free_iob ( iobuf );
- return rc;
- }
-
- /**
- * Check if network device has any IPv4 address
- *
- * @v netdev Network device
- * @ret has_any_addr Network device has any IPv4 address
- */
- int ipv4_has_any_addr ( struct net_device *netdev ) {
- struct ipv4_miniroute *miniroute;
-
- list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
- if ( miniroute->netdev == netdev )
- return 1;
- }
- return 0;
- }
-
- /**
- * Check if network device has a specific IPv4 address
- *
- * @v netdev Network device
- * @v addr IPv4 address
- * @ret has_addr Network device has this IPv4 address
- */
- static int ipv4_has_addr ( struct net_device *netdev, struct in_addr addr ) {
- struct ipv4_miniroute *miniroute;
-
- list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
- if ( ( miniroute->netdev == netdev ) &&
- ( miniroute->address.s_addr == addr.s_addr ) ) {
- /* Found matching address */
- return 1;
- }
- }
- return 0;
- }
-
- /**
- * Process incoming packets
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
- * @v ll_dest Link-layer destination address
- * @v ll_source Link-layer destination source
- * @v flags Packet flags
- * @ret rc Return status code
- *
- * This function expects an IP4 network datagram. It processes the headers
- * and sends it to the transport layer.
- */
- static int ipv4_rx ( struct io_buffer *iobuf,
- struct net_device *netdev,
- const void *ll_dest __unused,
- const void *ll_source __unused,
- unsigned int flags ) {
- struct iphdr *iphdr = iobuf->data;
- size_t hdrlen;
- size_t len;
- union {
- struct sockaddr_in sin;
- struct sockaddr_tcpip st;
- } src, dest;
- uint16_t csum;
- uint16_t pshdr_csum;
- int rc;
-
- /* Start profiling */
- profile_start ( &ipv4_rx_profiler );
-
- /* Update statistics */
- ipv4_stats.in_receives++;
- ipv4_stats.in_octets += iob_len ( iobuf );
- if ( flags & LL_BROADCAST ) {
- ipv4_stats.in_bcast_pkts++;
- } else if ( flags & LL_MULTICAST ) {
- ipv4_stats.in_mcast_pkts++;
- }
-
- /* Sanity check the IPv4 header */
- if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) {
- DBGC ( iphdr->src, "IPv4 packet too short at %zd bytes (min "
- "%zd bytes)\n", iob_len ( iobuf ), sizeof ( *iphdr ) );
- goto err_header;
- }
- if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
- DBGC ( iphdr->src, "IPv4 version %#02x not supported\n",
- iphdr->verhdrlen );
- goto err_header;
- }
- hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
- if ( hdrlen < sizeof ( *iphdr ) ) {
- DBGC ( iphdr->src, "IPv4 header too short at %zd bytes (min "
- "%zd bytes)\n", hdrlen, sizeof ( *iphdr ) );
- goto err_header;
- }
- if ( hdrlen > iob_len ( iobuf ) ) {
- DBGC ( iphdr->src, "IPv4 header too long at %zd bytes "
- "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) );
- goto err_header;
- }
- if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
- DBGC ( iphdr->src, "IPv4 checksum incorrect (is %04x "
- "including checksum field, should be 0000)\n", csum );
- goto err_header;
- }
- len = ntohs ( iphdr->len );
- if ( len < hdrlen ) {
- DBGC ( iphdr->src, "IPv4 length too short at %zd bytes "
- "(header is %zd bytes)\n", len, hdrlen );
- goto err_header;
- }
- if ( len > iob_len ( iobuf ) ) {
- DBGC ( iphdr->src, "IPv4 length too long at %zd bytes "
- "(packet is %zd bytes)\n", len, iob_len ( iobuf ) );
- ipv4_stats.in_truncated_pkts++;
- goto err_other;
- }
-
- /* Truncate packet to correct length */
- iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
-
- /* Print IPv4 header for debugging */
- DBGC2 ( iphdr->src, "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
- DBGC2 ( iphdr->src, "%s len %d proto %d id %04x csum %04x\n",
- inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol,
- ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) );
-
- /* Discard unicast packets not destined for us */
- if ( ( ! ( flags & LL_MULTICAST ) ) &&
- ipv4_has_any_addr ( netdev ) &&
- ( ! ipv4_has_addr ( netdev, iphdr->dest ) ) ) {
- DBGC ( iphdr->src, "IPv4 discarding non-local unicast packet "
- "for %s\n", inet_ntoa ( iphdr->dest ) );
- ipv4_stats.in_addr_errors++;
- goto err_other;
- }
-
- /* Perform fragment reassembly if applicable */
- if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) {
- /* Pass the fragment to fragment_reassemble() which returns
- * either a fully reassembled I/O buffer or NULL.
- */
- iobuf = fragment_reassemble ( &ipv4_reassembler, iobuf,
- &hdrlen );
- if ( ! iobuf )
- return 0;
- iphdr = iobuf->data;
- }
-
- /* Construct socket addresses, calculate pseudo-header
- * checksum, and hand off to transport layer
- */
- memset ( &src, 0, sizeof ( src ) );
- src.sin.sin_family = AF_INET;
- src.sin.sin_addr = iphdr->src;
- memset ( &dest, 0, sizeof ( dest ) );
- dest.sin.sin_family = AF_INET;
- dest.sin.sin_addr = iphdr->dest;
- pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
- iob_pull ( iobuf, hdrlen );
- if ( ( rc = tcpip_rx ( iobuf, netdev, iphdr->protocol, &src.st,
- &dest.st, pshdr_csum, &ipv4_stats ) ) != 0 ) {
- DBGC ( src.sin.sin_addr, "IPv4 received packet rejected by "
- "stack: %s\n", strerror ( rc ) );
- return rc;
- }
-
- profile_stop ( &ipv4_rx_profiler );
- return 0;
-
- err_header:
- ipv4_stats.in_hdr_errors++;
- err_other:
- free_iob ( iobuf );
- return -EINVAL;
- }
-
- /**
- * Check existence of IPv4 address for ARP
- *
- * @v netdev Network device
- * @v net_addr Network-layer address
- * @ret rc Return status code
- */
- static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) {
- const struct in_addr *address = net_addr;
-
- if ( ipv4_has_addr ( netdev, *address ) )
- return 0;
-
- return -ENOENT;
- }
-
- /**
- * Parse IPv4 address
- *
- * @v string IPv4 address string
- * @ret in IPv4 address to fill in
- * @ret ok IPv4 address is valid
- *
- * Note that this function returns nonzero iff the address is valid,
- * to match the standard BSD API function of the same name. Unlike
- * most other iPXE functions, a zero therefore indicates failure.
- */
- int inet_aton ( const char *string, struct in_addr *in ) {
- const char *separator = "...";
- uint8_t *byte = ( ( uint8_t * ) in );
- char *endp;
- unsigned long value;
-
- while ( 1 ) {
- value = strtoul ( string, &endp, 0 );
- if ( string == endp )
- return 0;
- if ( value > 0xff )
- return 0;
- *(byte++) = value;
- if ( *endp != *separator )
- return 0;
- if ( ! *(separator++) )
- return 1;
- string = ( endp + 1 );
- }
- }
-
- /**
- * Convert IPv4 address to dotted-quad notation
- *
- * @v in IPv4 address
- * @ret string IPv4 address in dotted-quad notation
- */
- char * inet_ntoa ( struct in_addr in ) {
- static char buf[16]; /* "xxx.xxx.xxx.xxx" */
- uint8_t *bytes = ( uint8_t * ) ∈
-
- sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
- return buf;
- }
-
- /**
- * Transcribe IPv4 address
- *
- * @v net_addr IPv4 address
- * @ret string IPv4 address in dotted-quad notation
- *
- */
- static const char * ipv4_ntoa ( const void *net_addr ) {
- return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
- }
-
- /**
- * Transcribe IPv4 socket address
- *
- * @v sa Socket address
- * @ret string Socket address in standard notation
- */
- static const char * ipv4_sock_ntoa ( struct sockaddr *sa ) {
- struct sockaddr_in *sin = ( ( struct sockaddr_in * ) sa );
-
- return inet_ntoa ( sin->sin_addr );
- }
-
- /**
- * Parse IPv4 socket address
- *
- * @v string Socket address string
- * @v sa Socket address to fill in
- * @ret rc Return status code
- */
- static int ipv4_sock_aton ( const char *string, struct sockaddr *sa ) {
- struct sockaddr_in *sin = ( ( struct sockaddr_in * ) sa );
- struct in_addr in;
-
- if ( inet_aton ( string, &in ) ) {
- sin->sin_addr = in;
- return 0;
- }
- return -EINVAL;
- }
-
- /** IPv4 protocol */
- struct net_protocol ipv4_protocol __net_protocol = {
- .name = "IP",
- .net_proto = htons ( ETH_P_IP ),
- .net_addr_len = sizeof ( struct in_addr ),
- .rx = ipv4_rx,
- .ntoa = ipv4_ntoa,
- };
-
- /** IPv4 TCPIP net protocol */
- struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = {
- .name = "IPv4",
- .sa_family = AF_INET,
- .header_len = sizeof ( struct iphdr ),
- .net_protocol = &ipv4_protocol,
- .tx = ipv4_tx,
- .netdev = ipv4_netdev,
- };
-
- /** IPv4 ARP protocol */
- struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = {
- .net_protocol = &ipv4_protocol,
- .check = ipv4_arp_check,
- };
-
- /** IPv4 socket address converter */
- struct sockaddr_converter ipv4_sockaddr_converter __sockaddr_converter = {
- .family = AF_INET,
- .ntoa = ipv4_sock_ntoa,
- .aton = ipv4_sock_aton,
- };
-
- /******************************************************************************
- *
- * Settings
- *
- ******************************************************************************
- */
-
- /**
- * Parse IPv4 address setting value
- *
- * @v type Setting type
- * @v value Formatted setting value
- * @v buf Buffer to contain raw value
- * @v len Length of buffer
- * @ret len Length of raw value, or negative error
- */
- int parse_ipv4_setting ( const struct setting_type *type __unused,
- const char *value, void *buf, size_t len ) {
- struct in_addr ipv4;
-
- /* Parse IPv4 address */
- if ( inet_aton ( value, &ipv4 ) == 0 )
- return -EINVAL;
-
- /* Copy to buffer */
- if ( len > sizeof ( ipv4 ) )
- len = sizeof ( ipv4 );
- memcpy ( buf, &ipv4, len );
-
- return ( sizeof ( ipv4 ) );
- }
-
- /**
- * Format IPv4 address setting value
- *
- * @v type Setting type
- * @v raw Raw setting value
- * @v raw_len Length of raw setting value
- * @v buf Buffer to contain formatted value
- * @v len Length of buffer
- * @ret len Length of formatted value, or negative error
- */
- int format_ipv4_setting ( const struct setting_type *type __unused,
- const void *raw, size_t raw_len, char *buf,
- size_t len ) {
- const struct in_addr *ipv4 = raw;
-
- if ( raw_len < sizeof ( *ipv4 ) )
- return -EINVAL;
- return snprintf ( buf, len, "%s", inet_ntoa ( *ipv4 ) );
- }
-
- /** IPv4 address setting */
- const struct setting ip_setting __setting ( SETTING_IP4, ip ) = {
- .name = "ip",
- .description = "IP address",
- .tag = DHCP_EB_YIADDR,
- .type = &setting_type_ipv4,
- };
-
- /** IPv4 subnet mask setting */
- const struct setting netmask_setting __setting ( SETTING_IP4, netmask ) = {
- .name = "netmask",
- .description = "Subnet mask",
- .tag = DHCP_SUBNET_MASK,
- .type = &setting_type_ipv4,
- };
-
- /** Default gateway setting */
- const struct setting gateway_setting __setting ( SETTING_IP4, gateway ) = {
- .name = "gateway",
- .description = "Default gateway",
- .tag = DHCP_ROUTERS,
- .type = &setting_type_ipv4,
- };
-
- /**
- * Send gratuitous ARP, if applicable
- *
- * @v netdev Network device
- * @v address IPv4 address
- * @v netmask Subnet mask
- * @v gateway Gateway address (if any)
- * @ret rc Return status code
- */
- static int ipv4_gratuitous_arp ( struct net_device *netdev,
- struct in_addr address,
- struct in_addr netmask __unused,
- struct in_addr gateway __unused ) {
- int rc;
-
- /* Do nothing if network device already has this IPv4 address */
- if ( ipv4_has_addr ( netdev, address ) )
- return 0;
-
- /* Transmit gratuitous ARP */
- DBGC ( netdev, "IPv4 sending gratuitous ARP for %s via %s\n",
- inet_ntoa ( address ), netdev->name );
- if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, &address,
- &address ) ) != 0 ) {
- DBGC ( netdev, "IPv4 could not transmit gratuitous ARP: %s\n",
- strerror ( rc ) );
- /* Treat failures as non-fatal */
- }
-
- return 0;
- }
-
- /**
- * Process IPv4 network device settings
- *
- * @v apply Application method
- * @ret rc Return status code
- */
- static int ipv4_settings ( int ( * apply ) ( struct net_device *netdev,
- struct in_addr address,
- struct in_addr netmask,
- struct in_addr gateway ) ) {
- struct net_device *netdev;
- struct settings *settings;
- struct in_addr address = { 0 };
- struct in_addr netmask = { 0 };
- struct in_addr gateway = { 0 };
- int rc;
-
- /* Process settings for each network device */
- for_each_netdev ( netdev ) {
-
- /* Get network device settings */
- settings = netdev_settings ( netdev );
-
- /* Get IPv4 address */
- address.s_addr = 0;
- fetch_ipv4_setting ( settings, &ip_setting, &address );
- if ( ! address.s_addr )
- continue;
-
- /* Get subnet mask */
- fetch_ipv4_setting ( settings, &netmask_setting, &netmask );
-
- /* Calculate default netmask, if necessary */
- if ( ! netmask.s_addr ) {
- if ( IN_IS_CLASSA ( address.s_addr ) ) {
- netmask.s_addr = INADDR_NET_CLASSA;
- } else if ( IN_IS_CLASSB ( address.s_addr ) ) {
- netmask.s_addr = INADDR_NET_CLASSB;
- } else if ( IN_IS_CLASSC ( address.s_addr ) ) {
- netmask.s_addr = INADDR_NET_CLASSC;
- }
- }
-
- /* Get default gateway, if present */
- fetch_ipv4_setting ( settings, &gateway_setting, &gateway );
-
- /* Apply settings */
- if ( ( rc = apply ( netdev, address, netmask, gateway ) ) != 0 )
- return rc;
- }
-
- return 0;
- }
-
- /**
- * Create IPv4 routing table based on configured settings
- *
- * @ret rc Return status code
- */
- static int ipv4_create_routes ( void ) {
- struct ipv4_miniroute *miniroute;
- struct ipv4_miniroute *tmp;
- int rc;
-
- /* Send gratuitous ARPs for any new IPv4 addresses */
- ipv4_settings ( ipv4_gratuitous_arp );
-
- /* Delete all existing routes */
- list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list )
- del_ipv4_miniroute ( miniroute );
-
- /* Create a route for each configured network device */
- if ( ( rc = ipv4_settings ( add_ipv4_miniroute ) ) != 0 )
- return rc;
-
- return 0;
- }
-
- /** IPv4 settings applicator */
- struct settings_applicator ipv4_settings_applicator __settings_applicator = {
- .apply = ipv4_create_routes,
- };
-
- /* Drag in objects via ipv4_protocol */
- REQUIRING_SYMBOL ( ipv4_protocol );
-
- /* Drag in ICMPv4 */
- REQUIRE_OBJECT ( icmpv4 );
|