diff options
Diffstat (limited to 'qemu/roms/ipxe/src/net')
85 files changed, 0 insertions, 50463 deletions
diff --git a/qemu/roms/ipxe/src/net/80211/net80211.c b/qemu/roms/ipxe/src/net/80211/net80211.c deleted file mode 100644 index d4970ad5c..000000000 --- a/qemu/roms/ipxe/src/net/80211/net80211.c +++ /dev/null @@ -1,2838 +0,0 @@ -/* - * The iPXE 802.11 MAC layer. - * - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <string.h> -#include <byteswap.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <ipxe/settings.h> -#include <ipxe/if_arp.h> -#include <ipxe/ethernet.h> -#include <ipxe/ieee80211.h> -#include <ipxe/netdevice.h> -#include <ipxe/net80211.h> -#include <ipxe/sec80211.h> -#include <ipxe/timer.h> -#include <ipxe/nap.h> -#include <ipxe/errortab.h> -#include <ipxe/net80211_err.h> - -/** @file - * - * 802.11 device management - */ - -/** List of 802.11 devices */ -static struct list_head net80211_devices = LIST_HEAD_INIT ( net80211_devices ); - -/** Set of device operations that does nothing */ -static struct net80211_device_operations net80211_null_ops; - -/** Information associated with a received management packet - * - * This is used to keep beacon signal strengths in a parallel queue to - * the beacons themselves. - */ -struct net80211_rx_info { - int signal; - struct list_head list; -}; - -/** Context for a probe operation */ -struct net80211_probe_ctx { - /** 802.11 device to probe on */ - struct net80211_device *dev; - - /** Value of keep_mgmt before probe was started */ - int old_keep_mgmt; - - /** If scanning actively, pointer to probe packet to send */ - struct io_buffer *probe; - - /** If non-"", the ESSID to limit ourselves to */ - const char *essid; - - /** Time probe was started */ - u32 ticks_start; - - /** Time last useful beacon was received */ - u32 ticks_beacon; - - /** Time channel was last changed */ - u32 ticks_channel; - - /** Time to stay on each channel */ - u32 hop_time; - - /** Channels to hop by when changing channel */ - int hop_step; - - /** List of best beacons for each network found so far */ - struct list_head *beacons; -}; - -/** Context for the association task */ -struct net80211_assoc_ctx { - /** Next authentication method to try using */ - int method; - - /** Time (in ticks) of the last sent association-related packet */ - int last_packet; - - /** Number of times we have tried sending it */ - int times_tried; -}; - -/** - * Detect secure 802.11 network when security support is not available - * - * @return -ENOTSUP, always. - */ -__weak int sec80211_detect ( struct io_buffer *iob __unused, - enum net80211_security_proto *secprot __unused, - enum net80211_crypto_alg *crypt __unused ) { - return -ENOTSUP; -} - -/** - * @defgroup net80211_netdev Network device interface functions - * @{ - */ -static int net80211_netdev_open ( struct net_device *netdev ); -static void net80211_netdev_close ( struct net_device *netdev ); -static int net80211_netdev_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ); -static void net80211_netdev_poll ( struct net_device *netdev ); -static void net80211_netdev_irq ( struct net_device *netdev, int enable ); -/** @} */ - -/** - * @defgroup net80211_linklayer 802.11 link-layer protocol functions - * @{ - */ -static int net80211_ll_push ( struct net_device *netdev, - struct io_buffer *iobuf, const void *ll_dest, - const void *ll_source, uint16_t net_proto ); -static int net80211_ll_pull ( struct net_device *netdev, - struct io_buffer *iobuf, const void **ll_dest, - const void **ll_source, uint16_t * net_proto, - unsigned int *flags ); -/** @} */ - -/** - * @defgroup net80211_help 802.11 helper functions - * @{ - */ -static void net80211_add_channels ( struct net80211_device *dev, int start, - int len, int txpower ); -static void net80211_filter_hw_channels ( struct net80211_device *dev ); -static void net80211_set_rtscts_rate ( struct net80211_device *dev ); -static int net80211_process_capab ( struct net80211_device *dev, - u16 capab ); -static int net80211_process_ie ( struct net80211_device *dev, - union ieee80211_ie *ie, void *ie_end ); -static union ieee80211_ie * -net80211_marshal_request_info ( struct net80211_device *dev, - union ieee80211_ie *ie ); -/** @} */ - -/** - * @defgroup net80211_assoc_ll 802.11 association handling functions - * @{ - */ -static void net80211_step_associate ( struct net80211_device *dev ); -static void net80211_handle_auth ( struct net80211_device *dev, - struct io_buffer *iob ); -static void net80211_handle_assoc_reply ( struct net80211_device *dev, - struct io_buffer *iob ); -static int net80211_send_disassoc ( struct net80211_device *dev, int reason, - int deauth ); -static void net80211_handle_mgmt ( struct net80211_device *dev, - struct io_buffer *iob, int signal ); -/** @} */ - -/** - * @defgroup net80211_frag 802.11 fragment handling functions - * @{ - */ -static void net80211_free_frags ( struct net80211_device *dev, int fcid ); -static struct io_buffer *net80211_accum_frags ( struct net80211_device *dev, - int fcid, int nfrags, int size ); -static void net80211_rx_frag ( struct net80211_device *dev, - struct io_buffer *iob, int signal ); -/** @} */ - -/** - * @defgroup net80211_settings 802.11 settings handlers - * @{ - */ -static int net80211_check_settings_update ( void ); - -/** 802.11 settings applicator - * - * When the SSID is changed, this will cause any open devices to - * re-associate; when the encryption key is changed, we similarly - * update their state. - */ -struct settings_applicator net80211_applicator __settings_applicator = { - .apply = net80211_check_settings_update, -}; - -/** The network name to associate with - * - * If this is blank, we scan for all networks and use the one with the - * greatest signal strength. - */ -const struct setting net80211_ssid_setting __setting ( SETTING_NETDEV_EXTRA, - ssid ) = { - .name = "ssid", - .description = "Wireless SSID", - .type = &setting_type_string, -}; - -/** Whether to use active scanning - * - * In order to associate with a hidden SSID, it's necessary to use an - * active scan (send probe packets). If this setting is nonzero, an - * active scan on the 2.4GHz band will be used to associate. - */ -const struct setting net80211_active_setting __setting ( SETTING_NETDEV_EXTRA, - active-scan ) = { - .name = "active-scan", - .description = "Actively scan for wireless networks", - .type = &setting_type_int8, -}; - -/** The cryptographic key to use - * - * For hex WEP keys, as is common, this must be entered using the - * normal iPXE method for entering hex settings; an ASCII string of - * hex characters will not behave as expected. - */ -const struct setting net80211_key_setting __setting ( SETTING_NETDEV_EXTRA, - key ) = { - .name = "key", - .description = "Wireless encryption key", - .type = &setting_type_string, -}; - -/** @} */ - - -/* ---------- net_device wrapper ---------- */ - -/** - * Open 802.11 device and start association - * - * @v netdev Wrapping network device - * @ret rc Return status code - * - * This sets up a default conservative set of channels for probing, - * and starts the auto-association task unless the @c - * NET80211_NO_ASSOC flag is set in the wrapped 802.11 device's @c - * state field. - */ -static int net80211_netdev_open ( struct net_device *netdev ) -{ - struct net80211_device *dev = netdev->priv; - int rc = 0; - - if ( dev->op == &net80211_null_ops ) - return -EFAULT; - - if ( dev->op->open ) - rc = dev->op->open ( dev ); - - if ( rc < 0 ) - return rc; - - if ( ! ( dev->state & NET80211_NO_ASSOC ) ) - net80211_autoassociate ( dev ); - - return 0; -} - -/** - * Close 802.11 device - * - * @v netdev Wrapping network device. - * - * If the association task is running, this will stop it. - */ -static void net80211_netdev_close ( struct net_device *netdev ) -{ - struct net80211_device *dev = netdev->priv; - - if ( dev->state & NET80211_WORKING ) - process_del ( &dev->proc_assoc ); - - /* Send disassociation frame to AP, to be polite */ - if ( dev->state & NET80211_ASSOCIATED ) - net80211_send_disassoc ( dev, IEEE80211_REASON_LEAVING, 0 ); - - if ( dev->handshaker && dev->handshaker->stop && - dev->handshaker->started ) - dev->handshaker->stop ( dev ); - - free ( dev->crypto ); - free ( dev->handshaker ); - dev->crypto = NULL; - dev->handshaker = NULL; - - netdev_link_down ( netdev ); - dev->state = 0; - - if ( dev->op->close ) - dev->op->close ( dev ); -} - -/** - * Transmit packet on 802.11 device - * - * @v netdev Wrapping network device - * @v iobuf I/O buffer - * @ret rc Return status code - * - * If encryption is enabled for the currently associated network, the - * packet will be encrypted prior to transmission. - */ -static int net80211_netdev_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ) -{ - struct net80211_device *dev = netdev->priv; - struct ieee80211_frame *hdr = iobuf->data; - int rc = -ENOSYS; - - if ( dev->crypto && ! ( hdr->fc & IEEE80211_FC_PROTECTED ) && - ( ( hdr->fc & IEEE80211_FC_TYPE ) == IEEE80211_TYPE_DATA ) ) { - struct io_buffer *niob = dev->crypto->encrypt ( dev->crypto, - iobuf ); - if ( ! niob ) - return -ENOMEM; /* only reason encryption could fail */ - - /* Free the non-encrypted iob */ - netdev_tx_complete ( netdev, iobuf ); - - /* Transmit the encrypted iob; the Protected flag is - set, so we won't recurse into here again */ - netdev_tx ( netdev, niob ); - - /* Don't transmit the freed packet */ - return 0; - } - - if ( dev->op->transmit ) - rc = dev->op->transmit ( dev, iobuf ); - - return rc; -} - -/** - * Poll 802.11 device for received packets and completed transmissions - * - * @v netdev Wrapping network device - */ -static void net80211_netdev_poll ( struct net_device *netdev ) -{ - struct net80211_device *dev = netdev->priv; - - if ( dev->op->poll ) - dev->op->poll ( dev ); -} - -/** - * Enable or disable interrupts for 802.11 device - * - * @v netdev Wrapping network device - * @v enable Whether to enable interrupts - */ -static void net80211_netdev_irq ( struct net_device *netdev, int enable ) -{ - struct net80211_device *dev = netdev->priv; - - if ( dev->op->irq ) - dev->op->irq ( dev, enable ); -} - -/** Network device operations for a wrapped 802.11 device */ -static struct net_device_operations net80211_netdev_ops = { - .open = net80211_netdev_open, - .close = net80211_netdev_close, - .transmit = net80211_netdev_transmit, - .poll = net80211_netdev_poll, - .irq = net80211_netdev_irq, -}; - - -/* ---------- 802.11 link-layer protocol ---------- */ - -/** - * Determine whether a transmission rate uses ERP/OFDM - * - * @v rate Rate in 100 kbps units - * @ret is_erp TRUE if the rate is an ERP/OFDM rate - * - * 802.11b supports rates of 1.0, 2.0, 5.5, and 11.0 Mbps; any other - * rate than these on the 2.4GHz spectrum is an ERP (802.11g) rate. - */ -static inline int net80211_rate_is_erp ( u16 rate ) -{ - if ( rate == 10 || rate == 20 || rate == 55 || rate == 110 ) - return 0; - return 1; -} - - -/** - * Calculate one frame's contribution to 802.11 duration field - * - * @v dev 802.11 device - * @v bytes Amount of data to calculate duration for - * @ret dur Duration field in microseconds - * - * To avoid multiple stations attempting to transmit at once, 802.11 - * provides that every packet shall include a duration field - * specifying a length of time for which the wireless medium will be - * reserved after it is transmitted. The duration is measured in - * microseconds and is calculated with respect to the current - * physical-layer parameters of the 802.11 device. - * - * For an unfragmented data or management frame, or the last fragment - * of a fragmented frame, the duration captures only the 10 data bytes - * of one ACK; call once with bytes = 10. - * - * For a fragment of a data or management rame that will be followed - * by more fragments, the duration captures an ACK, the following - * fragment, and its ACK; add the results of three calls, two with - * bytes = 10 and one with bytes set to the next fragment's size. - * - * For an RTS control frame, the duration captures the responding CTS, - * the frame being sent, and its ACK; add the results of three calls, - * two with bytes = 10 and one with bytes set to the next frame's size - * (assuming unfragmented). - * - * For a CTS-to-self control frame, the duration captures the frame - * being protected and its ACK; add the results of two calls, one with - * bytes = 10 and one with bytes set to the next frame's size. - * - * No other frame types are currently supported by iPXE. - */ -u16 net80211_duration ( struct net80211_device *dev, int bytes, u16 rate ) -{ - struct net80211_channel *chan = &dev->channels[dev->channel]; - u32 kbps = rate * 100; - - if ( chan->band == NET80211_BAND_5GHZ || net80211_rate_is_erp ( rate ) ) { - /* OFDM encoding (802.11a/g) */ - int bits_per_symbol = ( kbps * 4 ) / 1000; /* 4us/symbol */ - int bits = 22 + ( bytes << 3 ); /* 22-bit PLCP */ - int symbols = ( bits + bits_per_symbol - 1 ) / bits_per_symbol; - - return 16 + 20 + ( symbols * 4 ); /* 16us SIFS, 20us preamble */ - } else { - /* CCK encoding (802.11b) */ - int phy_time = 144 + 48; /* preamble + PLCP */ - int bits = bytes << 3; - int data_time = ( bits * 1000 + kbps - 1 ) / kbps; - - if ( dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE ) - phy_time >>= 1; - - return 10 + phy_time + data_time; /* 10us SIFS */ - } -} - -/** - * Add 802.11 link-layer header - * - * @v netdev Wrapping network device - * @v iobuf I/O buffer - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v net_proto Network-layer protocol, in network byte order - * @ret rc Return status code - * - * This adds both the 802.11 frame header and the 802.2 LLC/SNAP - * header used on data packets. - * - * We also check here for state of the link that would make it invalid - * to send a data packet; every data packet must pass through here, - * and no non-data packet (e.g. management frame) should. - */ -static int net80211_ll_push ( struct net_device *netdev, - struct io_buffer *iobuf, const void *ll_dest, - const void *ll_source, uint16_t net_proto ) -{ - struct net80211_device *dev = netdev->priv; - struct ieee80211_frame *hdr = iob_push ( iobuf, - IEEE80211_LLC_HEADER_LEN + - IEEE80211_TYP_FRAME_HEADER_LEN ); - struct ieee80211_llc_snap_header *lhdr = - ( void * ) hdr + IEEE80211_TYP_FRAME_HEADER_LEN; - - /* We can't send data packets if we're not associated. */ - if ( ! ( dev->state & NET80211_ASSOCIATED ) ) { - if ( dev->assoc_rc ) - return dev->assoc_rc; - return -ENETUNREACH; - } - - hdr->fc = IEEE80211_THIS_VERSION | IEEE80211_TYPE_DATA | - IEEE80211_STYPE_DATA | IEEE80211_FC_TODS; - - /* We don't send fragmented frames, so duration is the time - for an SIFS + 10-byte ACK. */ - hdr->duration = net80211_duration ( dev, 10, dev->rates[dev->rate] ); - - memcpy ( hdr->addr1, dev->bssid, ETH_ALEN ); - memcpy ( hdr->addr2, ll_source, ETH_ALEN ); - memcpy ( hdr->addr3, ll_dest, ETH_ALEN ); - - hdr->seq = IEEE80211_MAKESEQ ( ++dev->last_tx_seqnr, 0 ); - - lhdr->dsap = IEEE80211_LLC_DSAP; - lhdr->ssap = IEEE80211_LLC_SSAP; - lhdr->ctrl = IEEE80211_LLC_CTRL; - memset ( lhdr->oui, 0x00, 3 ); - lhdr->ethertype = net_proto; - - return 0; -} - -/** - * Remove 802.11 link-layer header - * - * @v netdev Wrapping network device - * @v iobuf I/O buffer - * @ret ll_dest Link-layer destination address - * @ret ll_source Link-layer source - * @ret net_proto Network-layer protocol, in network byte order - * @ret flags Packet flags - * @ret rc Return status code - * - * This expects and removes both the 802.11 frame header and the 802.2 - * LLC/SNAP header that are used on data packets. - */ -static int net80211_ll_pull ( struct net_device *netdev __unused, - struct io_buffer *iobuf, - const void **ll_dest, const void **ll_source, - uint16_t * net_proto, unsigned int *flags ) -{ - struct ieee80211_frame *hdr = iobuf->data; - struct ieee80211_llc_snap_header *lhdr = - ( void * ) hdr + IEEE80211_TYP_FRAME_HEADER_LEN; - - /* Bunch of sanity checks */ - if ( iob_len ( iobuf ) < IEEE80211_TYP_FRAME_HEADER_LEN + - IEEE80211_LLC_HEADER_LEN ) { - DBGC ( netdev->priv, "802.11 %p packet too short (%zd bytes)\n", - netdev->priv, iob_len ( iobuf ) ); - return -EINVAL_PKT_TOO_SHORT; - } - - if ( ( hdr->fc & IEEE80211_FC_VERSION ) != IEEE80211_THIS_VERSION ) { - DBGC ( netdev->priv, "802.11 %p packet invalid version %04x\n", - netdev->priv, hdr->fc & IEEE80211_FC_VERSION ); - return -EINVAL_PKT_VERSION; - } - - if ( ( hdr->fc & IEEE80211_FC_TYPE ) != IEEE80211_TYPE_DATA || - ( hdr->fc & IEEE80211_FC_SUBTYPE ) != IEEE80211_STYPE_DATA ) { - DBGC ( netdev->priv, "802.11 %p packet not data/data (fc=%04x)\n", - netdev->priv, hdr->fc ); - return -EINVAL_PKT_NOT_DATA; - } - - if ( ( hdr->fc & ( IEEE80211_FC_TODS | IEEE80211_FC_FROMDS ) ) != - IEEE80211_FC_FROMDS ) { - DBGC ( netdev->priv, "802.11 %p packet not from DS (fc=%04x)\n", - netdev->priv, hdr->fc ); - return -EINVAL_PKT_NOT_FROMDS; - } - - if ( lhdr->dsap != IEEE80211_LLC_DSAP || lhdr->ssap != IEEE80211_LLC_SSAP || - lhdr->ctrl != IEEE80211_LLC_CTRL || lhdr->oui[0] || lhdr->oui[1] || - lhdr->oui[2] ) { - DBGC ( netdev->priv, "802.11 %p LLC header is not plain EtherType " - "encapsulator: %02x->%02x [%02x] %02x:%02x:%02x %04x\n", - netdev->priv, lhdr->dsap, lhdr->ssap, lhdr->ctrl, - lhdr->oui[0], lhdr->oui[1], lhdr->oui[2], lhdr->ethertype ); - return -EINVAL_PKT_LLC_HEADER; - } - - iob_pull ( iobuf, sizeof ( *hdr ) + sizeof ( *lhdr ) ); - - *ll_dest = hdr->addr1; - *ll_source = hdr->addr3; - *net_proto = lhdr->ethertype; - *flags = ( ( is_multicast_ether_addr ( hdr->addr1 ) ? - LL_MULTICAST : 0 ) | - ( is_broadcast_ether_addr ( hdr->addr1 ) ? - LL_BROADCAST : 0 ) ); - return 0; -} - -/** 802.11 link-layer protocol */ -static struct ll_protocol net80211_ll_protocol __ll_protocol = { - .name = "802.11", - .push = net80211_ll_push, - .pull = net80211_ll_pull, - .init_addr = eth_init_addr, - .ntoa = eth_ntoa, - .mc_hash = eth_mc_hash, - .eth_addr = eth_eth_addr, - .eui64 = eth_eui64, - .ll_proto = htons ( ARPHRD_ETHER ), /* "encapsulated Ethernet" */ - .hw_addr_len = ETH_ALEN, - .ll_addr_len = ETH_ALEN, - .ll_header_len = IEEE80211_TYP_FRAME_HEADER_LEN + - IEEE80211_LLC_HEADER_LEN, -}; - - -/* ---------- 802.11 network management API ---------- */ - -/** - * Get 802.11 device from wrapping network device - * - * @v netdev Wrapping network device - * @ret dev 802.11 device wrapped by network device, or NULL - * - * Returns NULL if the network device does not wrap an 802.11 device. - */ -struct net80211_device * net80211_get ( struct net_device *netdev ) -{ - struct net80211_device *dev; - - list_for_each_entry ( dev, &net80211_devices, list ) { - if ( netdev->priv == dev ) - return netdev->priv; - } - - return NULL; -} - -/** - * Set state of 802.11 device keeping management frames - * - * @v dev 802.11 device - * @v enable Whether to keep management frames - * @ret oldenab Whether management frames were enabled before this call - * - * If enable is TRUE, beacon, probe, and action frames will be kept - * and may be retrieved by calling net80211_mgmt_dequeue(). - */ -int net80211_keep_mgmt ( struct net80211_device *dev, int enable ) -{ - int oldenab = dev->keep_mgmt; - - dev->keep_mgmt = enable; - return oldenab; -} - -/** - * Get 802.11 management frame - * - * @v dev 802.11 device - * @ret signal Signal strength of returned management frame - * @ret iob I/O buffer, or NULL if no management frame is queued - * - * Frames will only be returned by this function if - * net80211_keep_mgmt() has been previously called with enable set to - * TRUE. - * - * The calling function takes ownership of the returned I/O buffer. - */ -struct io_buffer * net80211_mgmt_dequeue ( struct net80211_device *dev, - int *signal ) -{ - struct io_buffer *iobuf; - struct net80211_rx_info *rxi; - - list_for_each_entry ( rxi, &dev->mgmt_info_queue, list ) { - list_del ( &rxi->list ); - if ( signal ) - *signal = rxi->signal; - free ( rxi ); - - assert ( ! list_empty ( &dev->mgmt_queue ) ); - iobuf = list_first_entry ( &dev->mgmt_queue, struct io_buffer, - list ); - list_del ( &iobuf->list ); - return iobuf; - } - - return NULL; -} - -/** - * Transmit 802.11 management frame - * - * @v dev 802.11 device - * @v fc Frame Control flags for management frame - * @v dest Destination access point - * @v iob I/O buffer - * @ret rc Return status code - * - * The @a fc argument must contain at least an IEEE 802.11 management - * subtype number (e.g. IEEE80211_STYPE_PROBE_REQ). If it contains - * IEEE80211_FC_PROTECTED, the frame will be encrypted prior to - * transmission. - * - * It is required that @a iob have at least 24 bytes of headroom - * reserved before its data start. - */ -int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6], - struct io_buffer *iob ) -{ - struct ieee80211_frame *hdr = iob_push ( iob, - IEEE80211_TYP_FRAME_HEADER_LEN ); - - hdr->fc = IEEE80211_THIS_VERSION | IEEE80211_TYPE_MGMT | - ( fc & ~IEEE80211_FC_PROTECTED ); - hdr->duration = net80211_duration ( dev, 10, dev->rates[dev->rate] ); - hdr->seq = IEEE80211_MAKESEQ ( ++dev->last_tx_seqnr, 0 ); - - memcpy ( hdr->addr1, dest, ETH_ALEN ); /* DA = RA */ - memcpy ( hdr->addr2, dev->netdev->ll_addr, ETH_ALEN ); /* SA = TA */ - memcpy ( hdr->addr3, dest, ETH_ALEN ); /* BSSID */ - - if ( fc & IEEE80211_FC_PROTECTED ) { - if ( ! dev->crypto ) - return -EINVAL_CRYPTO_REQUEST; - - struct io_buffer *eiob = dev->crypto->encrypt ( dev->crypto, - iob ); - free_iob ( iob ); - iob = eiob; - } - - return netdev_tx ( dev->netdev, iob ); -} - - -/* ---------- Driver API ---------- */ - -/** 802.11 association process descriptor */ -static struct process_descriptor net80211_process_desc = - PROC_DESC ( struct net80211_device, proc_assoc, - net80211_step_associate ); - -/** - * Allocate 802.11 device - * - * @v priv_size Size of driver-private allocation area - * @ret dev Newly allocated 802.11 device - * - * This function allocates a net_device with space in its private area - * for both the net80211_device it will wrap and the driver-private - * data space requested. It initializes the link-layer-specific parts - * of the net_device, and links the net80211_device to the net_device - * appropriately. - */ -struct net80211_device * net80211_alloc ( size_t priv_size ) -{ - struct net80211_device *dev; - struct net_device *netdev = - alloc_netdev ( sizeof ( *dev ) + priv_size ); - - if ( ! netdev ) - return NULL; - - netdev->ll_protocol = &net80211_ll_protocol; - netdev->ll_broadcast = eth_broadcast; - netdev->max_pkt_len = IEEE80211_MAX_DATA_LEN; - netdev_init ( netdev, &net80211_netdev_ops ); - - dev = netdev->priv; - dev->netdev = netdev; - dev->priv = ( u8 * ) dev + sizeof ( *dev ); - dev->op = &net80211_null_ops; - - process_init_stopped ( &dev->proc_assoc, &net80211_process_desc, - &netdev->refcnt ); - INIT_LIST_HEAD ( &dev->mgmt_queue ); - INIT_LIST_HEAD ( &dev->mgmt_info_queue ); - - return dev; -} - -/** - * Register 802.11 device with network stack - * - * @v dev 802.11 device - * @v ops 802.11 device operations - * @v hw 802.11 hardware information - * - * This also registers the wrapping net_device with the higher network - * layers. - */ -int net80211_register ( struct net80211_device *dev, - struct net80211_device_operations *ops, - struct net80211_hw_info *hw ) -{ - dev->op = ops; - dev->hw = malloc ( sizeof ( *hw ) ); - if ( ! dev->hw ) - return -ENOMEM; - - memcpy ( dev->hw, hw, sizeof ( *hw ) ); - memcpy ( dev->netdev->hw_addr, hw->hwaddr, ETH_ALEN ); - - /* Set some sensible channel defaults for driver's open() function */ - memcpy ( dev->channels, dev->hw->channels, - NET80211_MAX_CHANNELS * sizeof ( dev->channels[0] ) ); - dev->channel = 0; - - /* Mark device as not supporting interrupts, if applicable */ - if ( ! ops->irq ) - dev->netdev->state |= NETDEV_IRQ_UNSUPPORTED; - - list_add_tail ( &dev->list, &net80211_devices ); - return register_netdev ( dev->netdev ); -} - -/** - * Unregister 802.11 device from network stack - * - * @v dev 802.11 device - * - * After this call, the device operations are cleared so that they - * will not be called. - */ -void net80211_unregister ( struct net80211_device *dev ) -{ - unregister_netdev ( dev->netdev ); - list_del ( &dev->list ); - dev->op = &net80211_null_ops; -} - -/** - * Free 802.11 device - * - * @v dev 802.11 device - * - * The device should be unregistered before this function is called. - */ -void net80211_free ( struct net80211_device *dev ) -{ - free ( dev->hw ); - rc80211_free ( dev->rctl ); - netdev_nullify ( dev->netdev ); - netdev_put ( dev->netdev ); -} - - -/* ---------- 802.11 network management workhorse code ---------- */ - -/** - * Set state of 802.11 device - * - * @v dev 802.11 device - * @v clear Bitmask of flags to clear - * @v set Bitmask of flags to set - * @v status Status or reason code for most recent operation - * - * If @a status represents a reason code, it should be OR'ed with - * NET80211_IS_REASON. - * - * Clearing authentication also clears association; clearing - * association also clears security handshaking state. Clearing - * association removes the link-up flag from the wrapping net_device, - * but setting it does not automatically set the flag; that is left to - * the judgment of higher-level code. - */ -static inline void net80211_set_state ( struct net80211_device *dev, - short clear, short set, - u16 status ) -{ - /* The conditions in this function are deliberately formulated - to be decidable at compile-time in most cases. Since clear - and set are generally passed as constants, the body of this - function can be reduced down to a few statements by the - compiler. */ - - const int statmsk = NET80211_STATUS_MASK | NET80211_IS_REASON; - - if ( clear & NET80211_PROBED ) - clear |= NET80211_AUTHENTICATED; - - if ( clear & NET80211_AUTHENTICATED ) - clear |= NET80211_ASSOCIATED; - - if ( clear & NET80211_ASSOCIATED ) - clear |= NET80211_CRYPTO_SYNCED; - - dev->state = ( dev->state & ~clear ) | set; - dev->state = ( dev->state & ~statmsk ) | ( status & statmsk ); - - if ( clear & NET80211_ASSOCIATED ) - netdev_link_down ( dev->netdev ); - - if ( ( clear | set ) & NET80211_ASSOCIATED ) - dev->op->config ( dev, NET80211_CFG_ASSOC ); - - if ( status != 0 ) { - if ( status & NET80211_IS_REASON ) - dev->assoc_rc = -E80211_REASON ( status ); - else - dev->assoc_rc = -E80211_STATUS ( status ); - netdev_link_err ( dev->netdev, dev->assoc_rc ); - } -} - -/** - * Add channels to 802.11 device - * - * @v dev 802.11 device - * @v start First channel number to add - * @v len Number of channels to add - * @v txpower TX power (dBm) to allow on added channels - * - * To replace the current list of channels instead of adding to it, - * set the nr_channels field of the 802.11 device to 0 before calling - * this function. - */ -static void net80211_add_channels ( struct net80211_device *dev, int start, - int len, int txpower ) -{ - int i, chan = start; - - for ( i = dev->nr_channels; len-- && i < NET80211_MAX_CHANNELS; i++ ) { - dev->channels[i].channel_nr = chan; - dev->channels[i].maxpower = txpower; - dev->channels[i].hw_value = 0; - - if ( chan >= 1 && chan <= 14 ) { - dev->channels[i].band = NET80211_BAND_2GHZ; - if ( chan == 14 ) - dev->channels[i].center_freq = 2484; - else - dev->channels[i].center_freq = 2407 + 5 * chan; - chan++; - } else { - dev->channels[i].band = NET80211_BAND_5GHZ; - dev->channels[i].center_freq = 5000 + 5 * chan; - chan += 4; - } - } - - dev->nr_channels = i; -} - -/** - * Filter 802.11 device channels for hardware capabilities - * - * @v dev 802.11 device - * - * Hardware may support fewer channels than regulatory restrictions - * allow; this function filters out channels in dev->channels that are - * not supported by the hardware list in dev->hwinfo. It also copies - * over the net80211_channel::hw_value and limits maximum TX power - * appropriately. - * - * Channels are matched based on center frequency, ignoring band and - * channel number. - * - * If the driver specifies no supported channels, the effect will be - * as though all were supported. - */ -static void net80211_filter_hw_channels ( struct net80211_device *dev ) -{ - int delta = 0, i = 0; - int old_freq = dev->channels[dev->channel].center_freq; - struct net80211_channel *chan, *hwchan; - - if ( ! dev->hw->nr_channels ) - return; - - dev->channel = 0; - for ( chan = dev->channels; chan < dev->channels + dev->nr_channels; - chan++, i++ ) { - int ok = 0; - for ( hwchan = dev->hw->channels; - hwchan < dev->hw->channels + dev->hw->nr_channels; - hwchan++ ) { - if ( hwchan->center_freq == chan->center_freq ) { - ok = 1; - break; - } - } - - if ( ! ok ) - delta++; - else { - chan->hw_value = hwchan->hw_value; - if ( hwchan->maxpower != 0 && - chan->maxpower > hwchan->maxpower ) - chan->maxpower = hwchan->maxpower; - if ( old_freq == chan->center_freq ) - dev->channel = i - delta; - if ( delta ) - chan[-delta] = *chan; - } - } - - dev->nr_channels -= delta; - - if ( dev->channels[dev->channel].center_freq != old_freq ) - dev->op->config ( dev, NET80211_CFG_CHANNEL ); -} - -/** - * Update 802.11 device state to reflect received capabilities field - * - * @v dev 802.11 device - * @v capab Capabilities field in beacon, probe, or association frame - * @ret rc Return status code - */ -static int net80211_process_capab ( struct net80211_device *dev, - u16 capab ) -{ - u16 old_phy = dev->phy_flags; - - if ( ( capab & ( IEEE80211_CAPAB_MANAGED | IEEE80211_CAPAB_ADHOC ) ) != - IEEE80211_CAPAB_MANAGED ) { - DBGC ( dev, "802.11 %p cannot handle IBSS network\n", dev ); - return -ENOSYS; - } - - dev->phy_flags &= ~( NET80211_PHY_USE_SHORT_PREAMBLE | - NET80211_PHY_USE_SHORT_SLOT ); - - if ( capab & IEEE80211_CAPAB_SHORT_PMBL ) - dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE; - - if ( capab & IEEE80211_CAPAB_SHORT_SLOT ) - dev->phy_flags |= NET80211_PHY_USE_SHORT_SLOT; - - if ( old_phy != dev->phy_flags ) - dev->op->config ( dev, NET80211_CFG_PHY_PARAMS ); - - return 0; -} - -/** - * Update 802.11 device state to reflect received information elements - * - * @v dev 802.11 device - * @v ie Pointer to first information element - * @v ie_end Pointer to tail of packet I/O buffer - * @ret rc Return status code - */ -static int net80211_process_ie ( struct net80211_device *dev, - union ieee80211_ie *ie, void *ie_end ) -{ - u16 old_rate = dev->rates[dev->rate]; - u16 old_phy = dev->phy_flags; - int have_rates = 0, i; - int ds_channel = 0; - int changed = 0; - int band = dev->channels[dev->channel].band; - - if ( ! ieee80211_ie_bound ( ie, ie_end ) ) - return 0; - - for ( ; ie; ie = ieee80211_next_ie ( ie, ie_end ) ) { - switch ( ie->id ) { - case IEEE80211_IE_SSID: - if ( ie->len <= 32 ) { - memcpy ( dev->essid, ie->ssid, ie->len ); - dev->essid[ie->len] = 0; - } - break; - - case IEEE80211_IE_RATES: - case IEEE80211_IE_EXT_RATES: - if ( ! have_rates ) { - dev->nr_rates = 0; - dev->basic_rates = 0; - have_rates = 1; - } - for ( i = 0; i < ie->len && - dev->nr_rates < NET80211_MAX_RATES; i++ ) { - u8 rid = ie->rates[i]; - u16 rate = ( rid & 0x7f ) * 5; - - if ( rid & 0x80 ) - dev->basic_rates |= - ( 1 << dev->nr_rates ); - - dev->rates[dev->nr_rates++] = rate; - } - - break; - - case IEEE80211_IE_DS_PARAM: - if ( dev->channel < dev->nr_channels && ds_channel == - dev->channels[dev->channel].channel_nr ) - break; - ds_channel = ie->ds_param.current_channel; - net80211_change_channel ( dev, ds_channel ); - break; - - case IEEE80211_IE_COUNTRY: - dev->nr_channels = 0; - - DBGC ( dev, "802.11 %p setting country regulations " - "for %c%c\n", dev, ie->country.name[0], - ie->country.name[1] ); - for ( i = 0; i < ( ie->len - 3 ) / 3; i++ ) { - union ieee80211_ie_country_triplet *t = - &ie->country.triplet[i]; - if ( t->first > 200 ) { - DBGC ( dev, "802.11 %p ignoring regulatory " - "extension information\n", dev ); - } else { - net80211_add_channels ( dev, - t->band.first_channel, - t->band.nr_channels, - t->band.max_txpower ); - } - } - net80211_filter_hw_channels ( dev ); - break; - - case IEEE80211_IE_ERP_INFO: - dev->phy_flags &= ~( NET80211_PHY_USE_PROTECTION | - NET80211_PHY_USE_SHORT_PREAMBLE ); - if ( ie->erp_info & IEEE80211_ERP_USE_PROTECTION ) - dev->phy_flags |= NET80211_PHY_USE_PROTECTION; - if ( ! ( ie->erp_info & IEEE80211_ERP_BARKER_LONG ) ) - dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE; - break; - } - } - - if ( have_rates ) { - /* Allow only those rates that are also supported by - the hardware. */ - int delta = 0, j; - - dev->rate = 0; - for ( i = 0; i < dev->nr_rates; i++ ) { - int ok = 0; - for ( j = 0; j < dev->hw->nr_rates[band]; j++ ) { - if ( dev->hw->rates[band][j] == dev->rates[i] ){ - ok = 1; - break; - } - } - - if ( ! ok ) - delta++; - else { - dev->rates[i - delta] = dev->rates[i]; - if ( old_rate == dev->rates[i] ) - dev->rate = i - delta; - } - } - - dev->nr_rates -= delta; - - /* Sort available rates - sorted subclumps tend to already - exist, so insertion sort works well. */ - for ( i = 1; i < dev->nr_rates; i++ ) { - u16 rate = dev->rates[i]; - u32 tmp, br, mask; - - for ( j = i - 1; j >= 0 && dev->rates[j] >= rate; j-- ) - dev->rates[j + 1] = dev->rates[j]; - dev->rates[j + 1] = rate; - - /* Adjust basic_rates to match by rotating the - bits from bit j+1 to bit i left one position. */ - mask = ( ( 1 << i ) - 1 ) & ~( ( 1 << ( j + 1 ) ) - 1 ); - br = dev->basic_rates; - tmp = br & ( 1 << i ); - br = ( br & ~( mask | tmp ) ) | ( ( br & mask ) << 1 ); - br |= ( tmp >> ( i - j - 1 ) ); - dev->basic_rates = br; - } - - net80211_set_rtscts_rate ( dev ); - - if ( dev->rates[dev->rate] != old_rate ) - changed |= NET80211_CFG_RATE; - } - - if ( dev->hw->flags & NET80211_HW_NO_SHORT_PREAMBLE ) - dev->phy_flags &= ~NET80211_PHY_USE_SHORT_PREAMBLE; - if ( dev->hw->flags & NET80211_HW_NO_SHORT_SLOT ) - dev->phy_flags &= ~NET80211_PHY_USE_SHORT_SLOT; - - if ( old_phy != dev->phy_flags ) - changed |= NET80211_CFG_PHY_PARAMS; - - if ( changed ) - dev->op->config ( dev, changed ); - - return 0; -} - -/** - * Create information elements for outgoing probe or association packet - * - * @v dev 802.11 device - * @v ie Pointer to start of information element area - * @ret next_ie Pointer to first byte after added information elements - */ -static union ieee80211_ie * -net80211_marshal_request_info ( struct net80211_device *dev, - union ieee80211_ie *ie ) -{ - int i; - - ie->id = IEEE80211_IE_SSID; - ie->len = strlen ( dev->essid ); - memcpy ( ie->ssid, dev->essid, ie->len ); - - ie = ieee80211_next_ie ( ie, NULL ); - - ie->id = IEEE80211_IE_RATES; - ie->len = dev->nr_rates; - if ( ie->len > 8 ) - ie->len = 8; - - for ( i = 0; i < ie->len; i++ ) { - ie->rates[i] = dev->rates[i] / 5; - if ( dev->basic_rates & ( 1 << i ) ) - ie->rates[i] |= 0x80; - } - - ie = ieee80211_next_ie ( ie, NULL ); - - if ( dev->rsn_ie && dev->rsn_ie->id == IEEE80211_IE_RSN ) { - memcpy ( ie, dev->rsn_ie, dev->rsn_ie->len + 2 ); - ie = ieee80211_next_ie ( ie, NULL ); - } - - if ( dev->nr_rates > 8 ) { - /* 802.11 requires we use an Extended Basic Rates IE - for the rates beyond the eighth. */ - - ie->id = IEEE80211_IE_EXT_RATES; - ie->len = dev->nr_rates - 8; - - for ( ; i < dev->nr_rates; i++ ) { - ie->rates[i - 8] = dev->rates[i] / 5; - if ( dev->basic_rates & ( 1 << i ) ) - ie->rates[i - 8] |= 0x80; - } - - ie = ieee80211_next_ie ( ie, NULL ); - } - - if ( dev->rsn_ie && dev->rsn_ie->id == IEEE80211_IE_VENDOR ) { - memcpy ( ie, dev->rsn_ie, dev->rsn_ie->len + 2 ); - ie = ieee80211_next_ie ( ie, NULL ); - } - - return ie; -} - -/** Seconds to wait after finding a network, to possibly find better APs for it - * - * This is used when a specific SSID to scan for is specified. - */ -#define NET80211_PROBE_GATHER 1 - -/** Seconds to wait after finding a network, to possibly find other networks - * - * This is used when an empty SSID is specified, to scan for all - * networks. - */ -#define NET80211_PROBE_GATHER_ALL 2 - -/** Seconds to allow a probe to take if no network has been found */ -#define NET80211_PROBE_TIMEOUT 6 - -/** - * Begin probe of 802.11 networks - * - * @v dev 802.11 device - * @v essid SSID to probe for, or "" to accept any (may not be NULL) - * @v active Whether to use active scanning - * @ret ctx Probe context - * - * Active scanning may only be used on channels 1-11 in the 2.4GHz - * band, due to iPXE's lack of a complete regulatory database. If - * active scanning is used, probe packets will be sent on each - * channel; this can allow association with hidden-SSID networks if - * the SSID is properly specified. - * - * A @c NULL return indicates an out-of-memory condition. - * - * The returned context must be periodically passed to - * net80211_probe_step() until that function returns zero. - */ -struct net80211_probe_ctx * net80211_probe_start ( struct net80211_device *dev, - const char *essid, - int active ) -{ - struct net80211_probe_ctx *ctx = zalloc ( sizeof ( *ctx ) ); - - if ( ! ctx ) - return NULL; - - assert ( netdev_is_open ( dev->netdev ) ); - - ctx->dev = dev; - ctx->old_keep_mgmt = net80211_keep_mgmt ( dev, 1 ); - ctx->essid = essid; - if ( dev->essid != ctx->essid ) - strcpy ( dev->essid, ctx->essid ); - - if ( active ) { - struct ieee80211_probe_req *probe_req; - union ieee80211_ie *ie; - - ctx->probe = alloc_iob ( 128 ); - iob_reserve ( ctx->probe, IEEE80211_TYP_FRAME_HEADER_LEN ); - probe_req = ctx->probe->data; - - ie = net80211_marshal_request_info ( dev, - probe_req->info_element ); - - iob_put ( ctx->probe, ( void * ) ie - ctx->probe->data ); - } - - ctx->ticks_start = currticks(); - ctx->ticks_beacon = 0; - ctx->ticks_channel = currticks(); - ctx->hop_time = ticks_per_sec() / ( active ? 2 : 6 ); - - /* - * Channels on 2.4GHz overlap, and the most commonly used - * are 1, 6, and 11. We'll get a result faster if we check - * every 5 channels, but in order to hit all of them the - * number of channels must be relatively prime to 5. If it's - * not, tweak the hop. - */ - ctx->hop_step = 5; - while ( dev->nr_channels % ctx->hop_step == 0 && ctx->hop_step > 1 ) - ctx->hop_step--; - - ctx->beacons = malloc ( sizeof ( *ctx->beacons ) ); - INIT_LIST_HEAD ( ctx->beacons ); - - dev->channel = 0; - dev->op->config ( dev, NET80211_CFG_CHANNEL ); - - return ctx; -} - -/** - * Continue probe of 802.11 networks - * - * @v ctx Probe context returned by net80211_probe_start() - * @ret rc Probe status - * - * The return code will be 0 if the probe is still going on (and this - * function should be called again), a positive number if the probe - * completed successfully, or a negative error code if the probe - * failed for that reason. - * - * Whether the probe succeeded or failed, you must call - * net80211_probe_finish_all() or net80211_probe_finish_best() - * (depending on whether you want information on all networks or just - * the best-signal one) in order to release the probe context. A - * failed probe may still have acquired some valid data. - */ -int net80211_probe_step ( struct net80211_probe_ctx *ctx ) -{ - struct net80211_device *dev = ctx->dev; - u32 start_timeout = NET80211_PROBE_TIMEOUT * ticks_per_sec(); - u32 gather_timeout = ticks_per_sec(); - u32 now = currticks(); - struct io_buffer *iob; - int signal; - int rc; - char ssid[IEEE80211_MAX_SSID_LEN + 1]; - - gather_timeout *= ( ctx->essid[0] ? NET80211_PROBE_GATHER : - NET80211_PROBE_GATHER_ALL ); - - /* Time out if necessary */ - if ( now >= ctx->ticks_start + start_timeout ) - return list_empty ( ctx->beacons ) ? -ETIMEDOUT : +1; - - if ( ctx->ticks_beacon > 0 && now >= ctx->ticks_start + gather_timeout ) - return +1; - - /* Change channels if necessary */ - if ( now >= ctx->ticks_channel + ctx->hop_time ) { - dev->channel = ( dev->channel + ctx->hop_step ) - % dev->nr_channels; - dev->op->config ( dev, NET80211_CFG_CHANNEL ); - udelay ( dev->hw->channel_change_time ); - - ctx->ticks_channel = now; - - if ( ctx->probe ) { - struct io_buffer *siob = ctx->probe; /* to send */ - - /* make a copy for future use */ - iob = alloc_iob ( siob->tail - siob->head ); - iob_reserve ( iob, iob_headroom ( siob ) ); - memcpy ( iob_put ( iob, iob_len ( siob ) ), - siob->data, iob_len ( siob ) ); - - ctx->probe = iob; - rc = net80211_tx_mgmt ( dev, IEEE80211_STYPE_PROBE_REQ, - eth_broadcast, - iob_disown ( siob ) ); - if ( rc ) { - DBGC ( dev, "802.11 %p send probe failed: " - "%s\n", dev, strerror ( rc ) ); - return rc; - } - } - } - - /* Check for new management packets */ - while ( ( iob = net80211_mgmt_dequeue ( dev, &signal ) ) != NULL ) { - struct ieee80211_frame *hdr; - struct ieee80211_beacon *beacon; - union ieee80211_ie *ie; - struct net80211_wlan *wlan; - u16 type; - - hdr = iob->data; - type = hdr->fc & IEEE80211_FC_SUBTYPE; - beacon = ( struct ieee80211_beacon * ) hdr->data; - - if ( type != IEEE80211_STYPE_BEACON && - type != IEEE80211_STYPE_PROBE_RESP ) { - DBGC2 ( dev, "802.11 %p probe: non-beacon\n", dev ); - goto drop; - } - - if ( ( void * ) beacon->info_element >= iob->tail ) { - DBGC ( dev, "802.11 %p probe: beacon with no IEs\n", - dev ); - goto drop; - } - - ie = beacon->info_element; - - if ( ! ieee80211_ie_bound ( ie, iob->tail ) ) - ie = NULL; - - while ( ie && ie->id != IEEE80211_IE_SSID ) - ie = ieee80211_next_ie ( ie, iob->tail ); - - if ( ! ie ) { - DBGC ( dev, "802.11 %p probe: beacon with no SSID\n", - dev ); - goto drop; - } - - memcpy ( ssid, ie->ssid, ie->len ); - ssid[ie->len] = 0; - - if ( ctx->essid[0] && strcmp ( ctx->essid, ssid ) != 0 ) { - DBGC2 ( dev, "802.11 %p probe: beacon with wrong SSID " - "(%s)\n", dev, ssid ); - goto drop; - } - - /* See if we've got an entry for this network */ - list_for_each_entry ( wlan, ctx->beacons, list ) { - if ( strcmp ( wlan->essid, ssid ) != 0 ) - continue; - - if ( signal < wlan->signal ) { - DBGC2 ( dev, "802.11 %p probe: beacon for %s " - "(%s) with weaker signal %d\n", dev, - ssid, eth_ntoa ( hdr->addr3 ), signal ); - goto drop; - } - - goto fill; - } - - /* No entry yet - make one */ - wlan = zalloc ( sizeof ( *wlan ) ); - strcpy ( wlan->essid, ssid ); - list_add_tail ( &wlan->list, ctx->beacons ); - - /* Whether we're using an old entry or a new one, fill - it with new data. */ - fill: - memcpy ( wlan->bssid, hdr->addr3, ETH_ALEN ); - wlan->signal = signal; - wlan->channel = dev->channels[dev->channel].channel_nr; - - /* Copy this I/O buffer into a new wlan->beacon; the - * iob we've got probably came from the device driver - * and may have the full 2.4k allocation, which we - * don't want to keep around wasting memory. - */ - free_iob ( wlan->beacon ); - wlan->beacon = alloc_iob ( iob_len ( iob ) ); - memcpy ( iob_put ( wlan->beacon, iob_len ( iob ) ), - iob->data, iob_len ( iob ) ); - - if ( ( rc = sec80211_detect ( wlan->beacon, &wlan->handshaking, - &wlan->crypto ) ) == -ENOTSUP ) { - struct ieee80211_beacon *beacon = - ( struct ieee80211_beacon * ) hdr->data; - - if ( beacon->capability & IEEE80211_CAPAB_PRIVACY ) { - DBG ( "802.11 %p probe: secured network %s but " - "encryption support not compiled in\n", - dev, wlan->essid ); - wlan->handshaking = NET80211_SECPROT_UNKNOWN; - wlan->crypto = NET80211_CRYPT_UNKNOWN; - } else { - wlan->handshaking = NET80211_SECPROT_NONE; - wlan->crypto = NET80211_CRYPT_NONE; - } - } else if ( rc != 0 ) { - DBGC ( dev, "802.11 %p probe warning: network " - "%s with unidentifiable security " - "settings: %s\n", dev, wlan->essid, - strerror ( rc ) ); - } - - ctx->ticks_beacon = now; - - DBGC2 ( dev, "802.11 %p probe: good beacon for %s (%s)\n", - dev, wlan->essid, eth_ntoa ( wlan->bssid ) ); - - drop: - free_iob ( iob ); - } - - return 0; -} - - -/** - * Finish probe of 802.11 networks, returning best-signal network found - * - * @v ctx Probe context - * @ret wlan Best-signal network found, or @c NULL if none were found - * - * If net80211_probe_start() was called with a particular SSID - * parameter as filter, only a network with that SSID (matching - * case-sensitively) can be returned from this function. - */ -struct net80211_wlan * -net80211_probe_finish_best ( struct net80211_probe_ctx *ctx ) -{ - struct net80211_wlan *best = NULL, *wlan; - - if ( ! ctx ) - return NULL; - - list_for_each_entry ( wlan, ctx->beacons, list ) { - if ( ! best || best->signal < wlan->signal ) - best = wlan; - } - - if ( best ) - list_del ( &best->list ); - else - DBGC ( ctx->dev, "802.11 %p probe: found nothing for '%s'\n", - ctx->dev, ctx->essid ); - - net80211_free_wlanlist ( ctx->beacons ); - - net80211_keep_mgmt ( ctx->dev, ctx->old_keep_mgmt ); - - if ( ctx->probe ) - free_iob ( ctx->probe ); - - free ( ctx ); - - return best; -} - - -/** - * Finish probe of 802.11 networks, returning all networks found - * - * @v ctx Probe context - * @ret list List of net80211_wlan detailing networks found - * - * If net80211_probe_start() was called with a particular SSID - * parameter as filter, this will always return either an empty or a - * one-element list. - */ -struct list_head *net80211_probe_finish_all ( struct net80211_probe_ctx *ctx ) -{ - struct list_head *beacons = ctx->beacons; - - if ( ! ctx ) - return NULL; - - net80211_keep_mgmt ( ctx->dev, ctx->old_keep_mgmt ); - - if ( ctx->probe ) - free_iob ( ctx->probe ); - - free ( ctx ); - - return beacons; -} - - -/** - * Free WLAN structure - * - * @v wlan WLAN structure to free - */ -void net80211_free_wlan ( struct net80211_wlan *wlan ) -{ - if ( wlan ) { - free_iob ( wlan->beacon ); - free ( wlan ); - } -} - - -/** - * Free list of WLAN structures - * - * @v list List of WLAN structures to free - */ -void net80211_free_wlanlist ( struct list_head *list ) -{ - struct net80211_wlan *wlan, *tmp; - - if ( ! list ) - return; - - list_for_each_entry_safe ( wlan, tmp, list, list ) { - list_del ( &wlan->list ); - net80211_free_wlan ( wlan ); - } - - free ( list ); -} - - -/** Number of ticks to wait for replies to association management frames */ -#define ASSOC_TIMEOUT TICKS_PER_SEC - -/** Number of times to try sending a particular association management frame */ -#define ASSOC_RETRIES 2 - -/** - * Step 802.11 association process - * - * @v dev 802.11 device - */ -static void net80211_step_associate ( struct net80211_device *dev ) -{ - int rc = 0; - int status = dev->state & NET80211_STATUS_MASK; - - /* - * We use a sort of state machine implemented using bits in - * the dev->state variable. At each call, we take the - * logically first step that has not yet succeeded; either it - * has not been tried yet, it's being retried, or it failed. - * If it failed, we return an error indication; otherwise we - * perform the step. If it succeeds, RX handling code will set - * the appropriate status bit for us. - * - * Probe works a bit differently, since we have to step it - * on every call instead of waiting for a packet to arrive - * that will set the completion bit for us. - */ - - /* If we're waiting for a reply, check for timeout condition */ - if ( dev->state & NET80211_WAITING ) { - /* Sanity check */ - if ( ! dev->associating ) - return; - - if ( currticks() - dev->ctx.assoc->last_packet > ASSOC_TIMEOUT ) { - /* Timed out - fail if too many retries, or retry */ - dev->ctx.assoc->times_tried++; - if ( ++dev->ctx.assoc->times_tried > ASSOC_RETRIES ) { - rc = -ETIMEDOUT; - goto fail; - } - } else { - /* Didn't time out - let it keep going */ - return; - } - } else { - if ( dev->state & NET80211_PROBED ) - dev->ctx.assoc->times_tried = 0; - } - - if ( ! ( dev->state & NET80211_PROBED ) ) { - /* state: probe */ - - if ( ! dev->ctx.probe ) { - /* start probe */ - int active = fetch_intz_setting ( NULL, - &net80211_active_setting ); - int band = dev->hw->bands; - - if ( active ) - band &= ~NET80211_BAND_BIT_5GHZ; - - rc = net80211_prepare_probe ( dev, band, active ); - if ( rc ) - goto fail; - - dev->ctx.probe = net80211_probe_start ( dev, dev->essid, - active ); - if ( ! dev->ctx.probe ) { - dev->assoc_rc = -ENOMEM; - goto fail; - } - } - - rc = net80211_probe_step ( dev->ctx.probe ); - if ( ! rc ) { - return; /* still going */ - } - - dev->associating = net80211_probe_finish_best ( dev->ctx.probe ); - dev->ctx.probe = NULL; - if ( ! dev->associating ) { - if ( rc > 0 ) /* "successful" probe found nothing */ - rc = -ETIMEDOUT; - goto fail; - } - - /* If we probed using a broadcast SSID, record that - fact for the settings applicator before we clobber - it with the specific SSID we've chosen. */ - if ( ! dev->essid[0] ) - dev->state |= NET80211_AUTO_SSID; - - DBGC ( dev, "802.11 %p found network %s (%s)\n", dev, - dev->associating->essid, - eth_ntoa ( dev->associating->bssid ) ); - - dev->ctx.assoc = zalloc ( sizeof ( *dev->ctx.assoc ) ); - if ( ! dev->ctx.assoc ) { - rc = -ENOMEM; - goto fail; - } - - dev->state |= NET80211_PROBED; - dev->ctx.assoc->method = IEEE80211_AUTH_OPEN_SYSTEM; - - return; - } - - /* Record time of sending the packet we're about to send, for timeout */ - dev->ctx.assoc->last_packet = currticks(); - - if ( ! ( dev->state & NET80211_AUTHENTICATED ) ) { - /* state: prepare and authenticate */ - - if ( status != IEEE80211_STATUS_SUCCESS ) { - /* we tried authenticating already, but failed */ - int method = dev->ctx.assoc->method; - - if ( method == IEEE80211_AUTH_OPEN_SYSTEM && - ( status == IEEE80211_STATUS_AUTH_CHALL_INVALID || - status == IEEE80211_STATUS_AUTH_ALGO_UNSUPP ) ) { - /* Maybe this network uses Shared Key? */ - dev->ctx.assoc->method = - IEEE80211_AUTH_SHARED_KEY; - } else { - goto fail; - } - } - - DBGC ( dev, "802.11 %p authenticating with method %d\n", dev, - dev->ctx.assoc->method ); - - rc = net80211_prepare_assoc ( dev, dev->associating ); - if ( rc ) - goto fail; - - rc = net80211_send_auth ( dev, dev->associating, - dev->ctx.assoc->method ); - if ( rc ) - goto fail; - - return; - } - - if ( ! ( dev->state & NET80211_ASSOCIATED ) ) { - /* state: associate */ - - if ( status != IEEE80211_STATUS_SUCCESS ) - goto fail; - - DBGC ( dev, "802.11 %p associating\n", dev ); - - if ( dev->handshaker && dev->handshaker->start && - ! dev->handshaker->started ) { - rc = dev->handshaker->start ( dev ); - if ( rc < 0 ) - goto fail; - dev->handshaker->started = 1; - } - - rc = net80211_send_assoc ( dev, dev->associating ); - if ( rc ) - goto fail; - - return; - } - - if ( ! ( dev->state & NET80211_CRYPTO_SYNCED ) ) { - /* state: crypto sync */ - DBGC ( dev, "802.11 %p security handshaking\n", dev ); - - if ( ! dev->handshaker || ! dev->handshaker->step ) { - dev->state |= NET80211_CRYPTO_SYNCED; - return; - } - - rc = dev->handshaker->step ( dev ); - - if ( rc < 0 ) { - /* Only record the returned error if we're - still marked as associated, because an - asynchronous error will have already been - reported to net80211_deauthenticate() and - assoc_rc thereby set. */ - if ( dev->state & NET80211_ASSOCIATED ) - dev->assoc_rc = rc; - rc = 0; - goto fail; - } - - if ( rc > 0 ) { - dev->assoc_rc = 0; - dev->state |= NET80211_CRYPTO_SYNCED; - } - return; - } - - /* state: done! */ - netdev_link_up ( dev->netdev ); - dev->assoc_rc = 0; - dev->state &= ~NET80211_WORKING; - - free ( dev->ctx.assoc ); - dev->ctx.assoc = NULL; - - net80211_free_wlan ( dev->associating ); - dev->associating = NULL; - - dev->rctl = rc80211_init ( dev ); - - process_del ( &dev->proc_assoc ); - - DBGC ( dev, "802.11 %p associated with %s (%s)\n", dev, - dev->essid, eth_ntoa ( dev->bssid ) ); - - return; - - fail: - dev->state &= ~( NET80211_WORKING | NET80211_WAITING ); - if ( rc ) - dev->assoc_rc = rc; - - netdev_link_err ( dev->netdev, dev->assoc_rc ); - - /* We never reach here from the middle of a probe, so we don't - need to worry about freeing dev->ctx.probe. */ - - if ( dev->state & NET80211_PROBED ) { - free ( dev->ctx.assoc ); - dev->ctx.assoc = NULL; - } - - net80211_free_wlan ( dev->associating ); - dev->associating = NULL; - - process_del ( &dev->proc_assoc ); - - DBGC ( dev, "802.11 %p association failed (state=%04x): " - "%s\n", dev, dev->state, strerror ( dev->assoc_rc ) ); - - /* Try it again: */ - net80211_autoassociate ( dev ); -} - -/** - * Check for 802.11 SSID or key updates - * - * This acts as a settings applicator; if the user changes netX/ssid, - * and netX is currently open, the association task will be invoked - * again. If the user changes the encryption key, the current security - * handshaker will be asked to update its state to match; if that is - * impossible without reassociation, we reassociate. - */ -static int net80211_check_settings_update ( void ) -{ - struct net80211_device *dev; - char ssid[IEEE80211_MAX_SSID_LEN + 1]; - int key_reassoc; - - list_for_each_entry ( dev, &net80211_devices, list ) { - if ( ! netdev_is_open ( dev->netdev ) ) - continue; - - key_reassoc = 0; - if ( dev->handshaker && dev->handshaker->change_key && - dev->handshaker->change_key ( dev ) < 0 ) - key_reassoc = 1; - - fetch_string_setting ( netdev_settings ( dev->netdev ), - &net80211_ssid_setting, ssid, - IEEE80211_MAX_SSID_LEN + 1 ); - - if ( key_reassoc || - ( ! ( ! ssid[0] && ( dev->state & NET80211_AUTO_SSID ) ) && - strcmp ( ssid, dev->essid ) != 0 ) ) { - DBGC ( dev, "802.11 %p updating association: " - "%s -> %s\n", dev, dev->essid, ssid ); - net80211_autoassociate ( dev ); - } - } - - return 0; -} - -/** - * Start 802.11 association process - * - * @v dev 802.11 device - * - * If the association process is running, it will be restarted. - */ -void net80211_autoassociate ( struct net80211_device *dev ) -{ - if ( ! ( dev->state & NET80211_WORKING ) ) { - DBGC2 ( dev, "802.11 %p spawning association process\n", dev ); - process_add ( &dev->proc_assoc ); - } else { - DBGC2 ( dev, "802.11 %p restarting association\n", dev ); - } - - /* Clean up everything an earlier association process might - have been in the middle of using */ - if ( dev->associating ) - net80211_free_wlan ( dev->associating ); - - if ( ! ( dev->state & NET80211_PROBED ) ) - net80211_free_wlan ( - net80211_probe_finish_best ( dev->ctx.probe ) ); - else - free ( dev->ctx.assoc ); - - /* Reset to a clean state */ - fetch_string_setting ( netdev_settings ( dev->netdev ), - &net80211_ssid_setting, dev->essid, - IEEE80211_MAX_SSID_LEN + 1 ); - dev->ctx.probe = NULL; - dev->associating = NULL; - dev->assoc_rc = 0; - net80211_set_state ( dev, NET80211_PROBED, NET80211_WORKING, 0 ); -} - -/** - * Pick TX rate for RTS/CTS packets based on data rate - * - * @v dev 802.11 device - * - * The RTS/CTS rate is the fastest TX rate marked as "basic" that is - * not faster than the data rate. - */ -static void net80211_set_rtscts_rate ( struct net80211_device *dev ) -{ - u16 datarate = dev->rates[dev->rate]; - u16 rtsrate = 0; - int rts_idx = -1; - int i; - - for ( i = 0; i < dev->nr_rates; i++ ) { - u16 rate = dev->rates[i]; - - if ( ! ( dev->basic_rates & ( 1 << i ) ) || rate > datarate ) - continue; - - if ( rate > rtsrate ) { - rtsrate = rate; - rts_idx = i; - } - } - - /* If this is in initialization, we might not have any basic - rates; just use the first data rate in that case. */ - if ( rts_idx < 0 ) - rts_idx = 0; - - dev->rtscts_rate = rts_idx; -} - -/** - * Set data transmission rate for 802.11 device - * - * @v dev 802.11 device - * @v rate Rate to set, as index into @c dev->rates array - */ -void net80211_set_rate_idx ( struct net80211_device *dev, int rate ) -{ - assert ( netdev_is_open ( dev->netdev ) ); - - if ( rate >= 0 && rate < dev->nr_rates && rate != dev->rate ) { - DBGC2 ( dev, "802.11 %p changing rate from %d->%d Mbps\n", - dev, dev->rates[dev->rate] / 10, - dev->rates[rate] / 10 ); - - dev->rate = rate; - net80211_set_rtscts_rate ( dev ); - dev->op->config ( dev, NET80211_CFG_RATE ); - } -} - -/** - * Configure 802.11 device to transmit on a certain channel - * - * @v dev 802.11 device - * @v channel Channel number (1-11 for 2.4GHz) to transmit on - */ -int net80211_change_channel ( struct net80211_device *dev, int channel ) -{ - int i, oldchan = dev->channel; - - assert ( netdev_is_open ( dev->netdev ) ); - - for ( i = 0; i < dev->nr_channels; i++ ) { - if ( dev->channels[i].channel_nr == channel ) { - dev->channel = i; - break; - } - } - - if ( i == dev->nr_channels ) - return -ENOENT; - - if ( i != oldchan ) - return dev->op->config ( dev, NET80211_CFG_CHANNEL ); - - return 0; -} - -/** - * Prepare 802.11 device channel and rate set for scanning - * - * @v dev 802.11 device - * @v band RF band(s) on which to prepare for scanning - * @v active Whether the scanning will be active - * @ret rc Return status code - */ -int net80211_prepare_probe ( struct net80211_device *dev, int band, - int active ) -{ - assert ( netdev_is_open ( dev->netdev ) ); - - if ( active && ( band & NET80211_BAND_BIT_5GHZ ) ) { - DBGC ( dev, "802.11 %p cannot perform active scanning on " - "5GHz band\n", dev ); - return -EINVAL_ACTIVE_SCAN; - } - - if ( band == 0 ) { - /* This can happen for a 5GHz-only card with 5GHz - scanning masked out by an active request. */ - DBGC ( dev, "802.11 %p asked to prepare for scanning nothing\n", - dev ); - return -EINVAL_ACTIVE_SCAN; - } - - dev->nr_channels = 0; - - if ( active ) - net80211_add_channels ( dev, 1, 11, NET80211_REG_TXPOWER ); - else { - if ( band & NET80211_BAND_BIT_2GHZ ) - net80211_add_channels ( dev, 1, 14, - NET80211_REG_TXPOWER ); - if ( band & NET80211_BAND_BIT_5GHZ ) - net80211_add_channels ( dev, 36, 8, - NET80211_REG_TXPOWER ); - } - - net80211_filter_hw_channels ( dev ); - - /* Use channel 1 for now */ - dev->channel = 0; - dev->op->config ( dev, NET80211_CFG_CHANNEL ); - - /* Always do active probes at lowest (presumably first) speed */ - dev->rate = 0; - dev->nr_rates = 1; - dev->rates[0] = dev->hw->rates[dev->channels[0].band][0]; - dev->op->config ( dev, NET80211_CFG_RATE ); - - return 0; -} - -/** - * Prepare 802.11 device channel and rate set for communication - * - * @v dev 802.11 device - * @v wlan WLAN to prepare for communication with - * @ret rc Return status code - */ -int net80211_prepare_assoc ( struct net80211_device *dev, - struct net80211_wlan *wlan ) -{ - struct ieee80211_frame *hdr = wlan->beacon->data; - struct ieee80211_beacon *beacon = - ( struct ieee80211_beacon * ) hdr->data; - struct net80211_handshaker *handshaker; - int rc; - - assert ( netdev_is_open ( dev->netdev ) ); - - net80211_set_state ( dev, NET80211_ASSOCIATED, 0, 0 ); - memcpy ( dev->bssid, wlan->bssid, ETH_ALEN ); - strcpy ( dev->essid, wlan->essid ); - - free ( dev->rsn_ie ); - dev->rsn_ie = NULL; - - dev->last_beacon_timestamp = beacon->timestamp; - dev->tx_beacon_interval = 1024 * beacon->beacon_interval; - - /* Barring an IE that tells us the channel outright, assume - the channel we heard this AP best on is the channel it's - communicating on. */ - net80211_change_channel ( dev, wlan->channel ); - - rc = net80211_process_capab ( dev, beacon->capability ); - if ( rc ) - return rc; - - rc = net80211_process_ie ( dev, beacon->info_element, - wlan->beacon->tail ); - if ( rc ) - return rc; - - /* Associate at the lowest rate so we know it'll get through */ - dev->rate = 0; - dev->op->config ( dev, NET80211_CFG_RATE ); - - /* Free old handshaker and crypto, if they exist */ - if ( dev->handshaker && dev->handshaker->stop && - dev->handshaker->started ) - dev->handshaker->stop ( dev ); - free ( dev->handshaker ); - dev->handshaker = NULL; - free ( dev->crypto ); - free ( dev->gcrypto ); - dev->crypto = dev->gcrypto = NULL; - - /* Find new security handshaker to use */ - for_each_table_entry ( handshaker, NET80211_HANDSHAKERS ) { - if ( handshaker->protocol == wlan->handshaking ) { - dev->handshaker = zalloc ( sizeof ( *handshaker ) + - handshaker->priv_len ); - if ( ! dev->handshaker ) - return -ENOMEM; - - memcpy ( dev->handshaker, handshaker, - sizeof ( *handshaker ) ); - dev->handshaker->priv = ( ( void * ) dev->handshaker + - sizeof ( *handshaker ) ); - break; - } - } - - if ( ( wlan->handshaking != NET80211_SECPROT_NONE ) && - ! dev->handshaker ) { - DBGC ( dev, "802.11 %p no support for handshaking scheme %d\n", - dev, wlan->handshaking ); - return -( ENOTSUP | ( wlan->handshaking << 8 ) ); - } - - /* Initialize security handshaker */ - if ( dev->handshaker ) { - rc = dev->handshaker->init ( dev ); - if ( rc < 0 ) - return rc; - } - - return 0; -} - -/** - * Send 802.11 initial authentication frame - * - * @v dev 802.11 device - * @v wlan WLAN to authenticate with - * @v method Authentication method - * @ret rc Return status code - * - * @a method may be 0 for Open System authentication or 1 for Shared - * Key authentication. Open System provides no security in association - * whatsoever, relying on encryption for confidentiality, but Shared - * Key actively introduces security problems and is very rarely used. - */ -int net80211_send_auth ( struct net80211_device *dev, - struct net80211_wlan *wlan, int method ) -{ - struct io_buffer *iob = alloc_iob ( 64 ); - struct ieee80211_auth *auth; - - net80211_set_state ( dev, 0, NET80211_WAITING, 0 ); - iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN ); - auth = iob_put ( iob, sizeof ( *auth ) ); - auth->algorithm = method; - auth->tx_seq = 1; - auth->status = 0; - - return net80211_tx_mgmt ( dev, IEEE80211_STYPE_AUTH, wlan->bssid, iob ); -} - -/** - * Handle receipt of 802.11 authentication frame - * - * @v dev 802.11 device - * @v iob I/O buffer - * - * If the authentication method being used is Shared Key, and the - * frame that was received included challenge text, the frame is - * encrypted using the cryptosystem currently in effect and sent back - * to the AP to complete the authentication. - */ -static void net80211_handle_auth ( struct net80211_device *dev, - struct io_buffer *iob ) -{ - struct ieee80211_frame *hdr = iob->data; - struct ieee80211_auth *auth = - ( struct ieee80211_auth * ) hdr->data; - - if ( auth->tx_seq & 1 ) { - DBGC ( dev, "802.11 %p authentication received improperly " - "directed frame (seq. %d)\n", dev, auth->tx_seq ); - net80211_set_state ( dev, NET80211_WAITING, 0, - IEEE80211_STATUS_FAILURE ); - return; - } - - if ( auth->status != IEEE80211_STATUS_SUCCESS ) { - DBGC ( dev, "802.11 %p authentication failed: status %d\n", - dev, auth->status ); - net80211_set_state ( dev, NET80211_WAITING, 0, - auth->status ); - return; - } - - if ( auth->algorithm == IEEE80211_AUTH_SHARED_KEY && ! dev->crypto ) { - DBGC ( dev, "802.11 %p can't perform shared-key authentication " - "without a cryptosystem\n", dev ); - net80211_set_state ( dev, NET80211_WAITING, 0, - IEEE80211_STATUS_FAILURE ); - return; - } - - if ( auth->algorithm == IEEE80211_AUTH_SHARED_KEY && - auth->tx_seq == 2 ) { - /* Since the iob we got is going to be freed as soon - as we return, we can do some in-place - modification. */ - auth->tx_seq = 3; - auth->status = 0; - - memcpy ( hdr->addr2, hdr->addr1, ETH_ALEN ); - memcpy ( hdr->addr1, hdr->addr3, ETH_ALEN ); - - netdev_tx ( dev->netdev, - dev->crypto->encrypt ( dev->crypto, iob ) ); - return; - } - - net80211_set_state ( dev, NET80211_WAITING, NET80211_AUTHENTICATED, - IEEE80211_STATUS_SUCCESS ); - - return; -} - -/** - * Send 802.11 association frame - * - * @v dev 802.11 device - * @v wlan WLAN to associate with - * @ret rc Return status code - */ -int net80211_send_assoc ( struct net80211_device *dev, - struct net80211_wlan *wlan ) -{ - struct io_buffer *iob = alloc_iob ( 128 ); - struct ieee80211_assoc_req *assoc; - union ieee80211_ie *ie; - - net80211_set_state ( dev, 0, NET80211_WAITING, 0 ); - - iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN ); - assoc = iob->data; - - assoc->capability = IEEE80211_CAPAB_MANAGED; - if ( ! ( dev->hw->flags & NET80211_HW_NO_SHORT_PREAMBLE ) ) - assoc->capability |= IEEE80211_CAPAB_SHORT_PMBL; - if ( ! ( dev->hw->flags & NET80211_HW_NO_SHORT_SLOT ) ) - assoc->capability |= IEEE80211_CAPAB_SHORT_SLOT; - if ( wlan->crypto ) - assoc->capability |= IEEE80211_CAPAB_PRIVACY; - - assoc->listen_interval = 1; - - ie = net80211_marshal_request_info ( dev, assoc->info_element ); - - DBGP ( "802.11 %p about to send association request:\n", dev ); - DBGP_HD ( iob->data, ( void * ) ie - iob->data ); - - iob_put ( iob, ( void * ) ie - iob->data ); - - return net80211_tx_mgmt ( dev, IEEE80211_STYPE_ASSOC_REQ, - wlan->bssid, iob ); -} - -/** - * Handle receipt of 802.11 association reply frame - * - * @v dev 802.11 device - * @v iob I/O buffer - */ -static void net80211_handle_assoc_reply ( struct net80211_device *dev, - struct io_buffer *iob ) -{ - struct ieee80211_frame *hdr = iob->data; - struct ieee80211_assoc_resp *assoc = - ( struct ieee80211_assoc_resp * ) hdr->data; - - net80211_process_capab ( dev, assoc->capability ); - net80211_process_ie ( dev, assoc->info_element, iob->tail ); - - if ( assoc->status != IEEE80211_STATUS_SUCCESS ) { - DBGC ( dev, "802.11 %p association failed: status %d\n", - dev, assoc->status ); - net80211_set_state ( dev, NET80211_WAITING, 0, - assoc->status ); - return; - } - - /* ESSID was filled before the association request was sent */ - memcpy ( dev->bssid, hdr->addr3, ETH_ALEN ); - dev->aid = assoc->aid; - - net80211_set_state ( dev, NET80211_WAITING, NET80211_ASSOCIATED, - IEEE80211_STATUS_SUCCESS ); -} - - -/** - * Send 802.11 disassociation frame - * - * @v dev 802.11 device - * @v reason Reason for disassociation - * @v deauth If TRUE, send deauthentication instead of disassociation - * @ret rc Return status code - */ -static int net80211_send_disassoc ( struct net80211_device *dev, int reason, - int deauth ) -{ - struct io_buffer *iob = alloc_iob ( 64 ); - struct ieee80211_disassoc *disassoc; - - if ( ! ( dev->state & NET80211_ASSOCIATED ) ) - return -EINVAL; - - net80211_set_state ( dev, NET80211_ASSOCIATED, 0, 0 ); - iob_reserve ( iob, IEEE80211_TYP_FRAME_HEADER_LEN ); - disassoc = iob_put ( iob, sizeof ( *disassoc ) ); - disassoc->reason = reason; - - return net80211_tx_mgmt ( dev, deauth ? IEEE80211_STYPE_DEAUTH : - IEEE80211_STYPE_DISASSOC, dev->bssid, iob ); -} - - -/** - * Deauthenticate from current network and try again - * - * @v dev 802.11 device - * @v rc Return status code indicating reason - * - * The deauthentication will be sent using an 802.11 "unspecified - * reason", as is common, but @a rc will be set as a link-up - * error to aid the user in debugging. - */ -void net80211_deauthenticate ( struct net80211_device *dev, int rc ) -{ - net80211_send_disassoc ( dev, IEEE80211_REASON_UNSPECIFIED, 1 ); - dev->assoc_rc = rc; - netdev_link_err ( dev->netdev, rc ); - - net80211_autoassociate ( dev ); -} - - -/** Smoothing factor (1-7) for link quality calculation */ -#define LQ_SMOOTH 7 - -/** - * Update link quality information based on received beacon - * - * @v dev 802.11 device - * @v iob I/O buffer containing beacon - * @ret rc Return status code - */ -static void net80211_update_link_quality ( struct net80211_device *dev, - struct io_buffer *iob ) -{ - struct ieee80211_frame *hdr = iob->data; - struct ieee80211_beacon *beacon; - u32 dt, rxi; - - if ( ! ( dev->state & NET80211_ASSOCIATED ) ) - return; - - beacon = ( struct ieee80211_beacon * ) hdr->data; - dt = ( u32 ) ( beacon->timestamp - dev->last_beacon_timestamp ); - rxi = dev->rx_beacon_interval; - - rxi = ( LQ_SMOOTH * rxi ) + ( ( 8 - LQ_SMOOTH ) * dt ); - dev->rx_beacon_interval = rxi >> 3; - - dev->last_beacon_timestamp = beacon->timestamp; -} - - -/** - * Handle receipt of 802.11 management frame - * - * @v dev 802.11 device - * @v iob I/O buffer - * @v signal Signal strength of received frame - */ -static void net80211_handle_mgmt ( struct net80211_device *dev, - struct io_buffer *iob, int signal ) -{ - struct ieee80211_frame *hdr = iob->data; - struct ieee80211_disassoc *disassoc; - u16 stype = hdr->fc & IEEE80211_FC_SUBTYPE; - int keep = 0; - int is_deauth = ( stype == IEEE80211_STYPE_DEAUTH ); - - if ( ( hdr->fc & IEEE80211_FC_TYPE ) != IEEE80211_TYPE_MGMT ) { - free_iob ( iob ); - return; /* only handle management frames */ - } - - switch ( stype ) { - /* We reconnect on deauthentication and disassociation. */ - case IEEE80211_STYPE_DEAUTH: - case IEEE80211_STYPE_DISASSOC: - disassoc = ( struct ieee80211_disassoc * ) hdr->data; - net80211_set_state ( dev, is_deauth ? NET80211_AUTHENTICATED : - NET80211_ASSOCIATED, 0, - NET80211_IS_REASON | disassoc->reason ); - DBGC ( dev, "802.11 %p %s: reason %d\n", - dev, is_deauth ? "deauthenticated" : "disassociated", - disassoc->reason ); - - /* Try to reassociate, in case it's transient. */ - net80211_autoassociate ( dev ); - - break; - - /* We handle authentication and association. */ - case IEEE80211_STYPE_AUTH: - if ( ! ( dev->state & NET80211_AUTHENTICATED ) ) - net80211_handle_auth ( dev, iob ); - break; - - case IEEE80211_STYPE_ASSOC_RESP: - case IEEE80211_STYPE_REASSOC_RESP: - if ( ! ( dev->state & NET80211_ASSOCIATED ) ) - net80211_handle_assoc_reply ( dev, iob ); - break; - - /* We pass probes and beacons onto network scanning - code. Pass actions for future extensibility. */ - case IEEE80211_STYPE_BEACON: - net80211_update_link_quality ( dev, iob ); - /* fall through */ - case IEEE80211_STYPE_PROBE_RESP: - case IEEE80211_STYPE_ACTION: - if ( dev->keep_mgmt ) { - struct net80211_rx_info *rxinf; - rxinf = zalloc ( sizeof ( *rxinf ) ); - if ( ! rxinf ) { - DBGC ( dev, "802.11 %p out of memory\n", dev ); - break; - } - rxinf->signal = signal; - list_add_tail ( &iob->list, &dev->mgmt_queue ); - list_add_tail ( &rxinf->list, &dev->mgmt_info_queue ); - keep = 1; - } - break; - - case IEEE80211_STYPE_PROBE_REQ: - /* Some nodes send these broadcast. Ignore them. */ - break; - - case IEEE80211_STYPE_ASSOC_REQ: - case IEEE80211_STYPE_REASSOC_REQ: - /* We should never receive these, only send them. */ - DBGC ( dev, "802.11 %p received strange management request " - "(%04x)\n", dev, stype ); - break; - - default: - DBGC ( dev, "802.11 %p received unimplemented management " - "packet (%04x)\n", dev, stype ); - break; - } - - if ( ! keep ) - free_iob ( iob ); -} - -/* ---------- Packet handling functions ---------- */ - -/** - * Free buffers used by 802.11 fragment cache entry - * - * @v dev 802.11 device - * @v fcid Fragment cache entry index - * - * After this function, the referenced entry will be marked unused. - */ -static void net80211_free_frags ( struct net80211_device *dev, int fcid ) -{ - int j; - struct net80211_frag_cache *frag = &dev->frags[fcid]; - - for ( j = 0; j < 16; j++ ) { - if ( frag->iob[j] ) { - free_iob ( frag->iob[j] ); - frag->iob[j] = NULL; - } - } - - frag->seqnr = 0; - frag->start_ticks = 0; - frag->in_use = 0; -} - -/** - * Accumulate 802.11 fragments into one I/O buffer - * - * @v dev 802.11 device - * @v fcid Fragment cache entry index - * @v nfrags Number of fragments received - * @v size Sum of sizes of all fragments, including headers - * @ret iob I/O buffer containing reassembled packet - * - * This function does not free the fragment buffers. - */ -static struct io_buffer *net80211_accum_frags ( struct net80211_device *dev, - int fcid, int nfrags, int size ) -{ - struct net80211_frag_cache *frag = &dev->frags[fcid]; - int hdrsize = IEEE80211_TYP_FRAME_HEADER_LEN; - int nsize = size - hdrsize * ( nfrags - 1 ); - int i; - - struct io_buffer *niob = alloc_iob ( nsize ); - struct ieee80211_frame *hdr; - - /* Add the header from the first one... */ - memcpy ( iob_put ( niob, hdrsize ), frag->iob[0]->data, hdrsize ); - - /* ... and all the data from all of them. */ - for ( i = 0; i < nfrags; i++ ) { - int len = iob_len ( frag->iob[i] ) - hdrsize; - memcpy ( iob_put ( niob, len ), - frag->iob[i]->data + hdrsize, len ); - } - - /* Turn off the fragment bit. */ - hdr = niob->data; - hdr->fc &= ~IEEE80211_FC_MORE_FRAG; - - return niob; -} - -/** - * Handle receipt of 802.11 fragment - * - * @v dev 802.11 device - * @v iob I/O buffer containing fragment - * @v signal Signal strength with which fragment was received - */ -static void net80211_rx_frag ( struct net80211_device *dev, - struct io_buffer *iob, int signal ) -{ - struct ieee80211_frame *hdr = iob->data; - int fragnr = IEEE80211_FRAG ( hdr->seq ); - - if ( fragnr == 0 && ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) { - /* start a frag cache entry */ - int i, newest = -1; - u32 curr_ticks = currticks(), newest_ticks = 0; - u32 timeout = ticks_per_sec() * NET80211_FRAG_TIMEOUT; - - for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) { - if ( dev->frags[i].in_use == 0 ) - break; - - if ( dev->frags[i].start_ticks + timeout >= - curr_ticks ) { - net80211_free_frags ( dev, i ); - break; - } - - if ( dev->frags[i].start_ticks > newest_ticks ) { - newest = i; - newest_ticks = dev->frags[i].start_ticks; - } - } - - /* If we're being sent more concurrent fragmented - packets than we can handle, drop the newest so the - older ones have time to complete. */ - if ( i == NET80211_NR_CONCURRENT_FRAGS ) { - i = newest; - net80211_free_frags ( dev, i ); - } - - dev->frags[i].in_use = 1; - dev->frags[i].seqnr = IEEE80211_SEQNR ( hdr->seq ); - dev->frags[i].start_ticks = currticks(); - dev->frags[i].iob[0] = iob; - return; - } else { - int i; - for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) { - if ( dev->frags[i].in_use && dev->frags[i].seqnr == - IEEE80211_SEQNR ( hdr->seq ) ) - break; - } - if ( i == NET80211_NR_CONCURRENT_FRAGS ) { - /* Drop non-first not-in-cache fragments */ - DBGC ( dev, "802.11 %p dropped fragment fc=%04x " - "seq=%04x\n", dev, hdr->fc, hdr->seq ); - free_iob ( iob ); - return; - } - - dev->frags[i].iob[fragnr] = iob; - - if ( ! ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) { - int j, size = 0; - for ( j = 0; j < fragnr; j++ ) { - size += iob_len ( dev->frags[i].iob[j] ); - if ( dev->frags[i].iob[j] == NULL ) - break; - } - if ( j == fragnr ) { - /* We've got everything */ - struct io_buffer *niob = - net80211_accum_frags ( dev, i, fragnr, - size ); - net80211_free_frags ( dev, i ); - net80211_rx ( dev, niob, signal, 0 ); - } else { - DBGC ( dev, "802.11 %p dropping fragmented " - "packet due to out-of-order arrival, " - "fc=%04x seq=%04x\n", dev, hdr->fc, - hdr->seq ); - net80211_free_frags ( dev, i ); - } - } - } -} - -/** - * Handle receipt of 802.11 frame - * - * @v dev 802.11 device - * @v iob I/O buffer - * @v signal Received signal strength - * @v rate Bitrate at which frame was received, in 100 kbps units - * - * If the rate or signal is unknown, 0 should be passed. - */ -void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob, - int signal, u16 rate ) -{ - struct ieee80211_frame *hdr = iob->data; - u16 type = hdr->fc & IEEE80211_FC_TYPE; - if ( ( hdr->fc & IEEE80211_FC_VERSION ) != IEEE80211_THIS_VERSION ) - goto drop; /* drop invalid-version packets */ - - if ( type == IEEE80211_TYPE_CTRL ) - goto drop; /* we don't handle control packets, - the hardware does */ - - if ( dev->last_rx_seq == hdr->seq ) - goto drop; /* avoid duplicate packet */ - dev->last_rx_seq = hdr->seq; - - if ( dev->hw->flags & NET80211_HW_RX_HAS_FCS ) { - /* discard the FCS */ - iob_unput ( iob, 4 ); - } - - /* Only decrypt packets from our BSSID, to avoid spurious errors */ - if ( ( hdr->fc & IEEE80211_FC_PROTECTED ) && - ! memcmp ( hdr->addr2, dev->bssid, ETH_ALEN ) ) { - /* Decrypt packet; record and drop if it fails */ - struct io_buffer *niob; - struct net80211_crypto *crypto = dev->crypto; - - if ( ! dev->crypto ) { - DBGC ( dev, "802.11 %p cannot decrypt packet " - "without a cryptosystem\n", dev ); - goto drop_crypt; - } - - if ( ( hdr->addr1[0] & 1 ) && dev->gcrypto ) { - /* Use group decryption if needed */ - crypto = dev->gcrypto; - } - - niob = crypto->decrypt ( crypto, iob ); - if ( ! niob ) { - DBGC ( dev, "802.11 %p decryption error\n", dev ); - goto drop_crypt; - } - free_iob ( iob ); - iob = niob; - hdr = iob->data; - } - - dev->last_signal = signal; - - /* Fragments go into the frag cache or get dropped. */ - if ( IEEE80211_FRAG ( hdr->seq ) != 0 - || ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) { - net80211_rx_frag ( dev, iob, signal ); - return; - } - - /* Management frames get handled, enqueued, or dropped. */ - if ( type == IEEE80211_TYPE_MGMT ) { - net80211_handle_mgmt ( dev, iob, signal ); - return; - } - - /* Data frames get dropped or sent to the net_device. */ - if ( ( hdr->fc & IEEE80211_FC_SUBTYPE ) != IEEE80211_STYPE_DATA ) - goto drop; /* drop QoS, CFP, or null data packets */ - - /* Update rate-control algorithm */ - if ( dev->rctl ) - rc80211_update_rx ( dev, hdr->fc & IEEE80211_FC_RETRY, rate ); - - /* Pass packet onward */ - if ( dev->state & NET80211_ASSOCIATED ) { - netdev_rx ( dev->netdev, iob ); - return; - } - - /* No association? Drop it. */ - goto drop; - - drop_crypt: - netdev_rx_err ( dev->netdev, NULL, EINVAL_CRYPTO_REQUEST ); - drop: - DBGC2 ( dev, "802.11 %p dropped packet fc=%04x seq=%04x\n", dev, - hdr->fc, hdr->seq ); - free_iob ( iob ); - return; -} - -/** Indicate an error in receiving a packet - * - * @v dev 802.11 device - * @v iob I/O buffer with received packet, or NULL - * @v rc Error code - * - * This logs the error with the wrapping net_device, and frees iob if - * it is passed. - */ -void net80211_rx_err ( struct net80211_device *dev, - struct io_buffer *iob, int rc ) -{ - netdev_rx_err ( dev->netdev, iob, rc ); -} - -/** Indicate the completed transmission of a packet - * - * @v dev 802.11 device - * @v iob I/O buffer of transmitted packet - * @v retries Number of times this packet was retransmitted - * @v rc Error code, or 0 for success - * - * This logs an error with the wrapping net_device if one occurred, - * and removes and frees the I/O buffer from its TX queue. The - * provided retry information is used to tune our transmission rate. - * - * If the packet did not need to be retransmitted because it was - * properly ACKed the first time, @a retries should be 0. - */ -void net80211_tx_complete ( struct net80211_device *dev, - struct io_buffer *iob, int retries, int rc ) -{ - /* Update rate-control algorithm */ - if ( dev->rctl ) - rc80211_update_tx ( dev, retries, rc ); - - /* Pass completion onward */ - netdev_tx_complete_err ( dev->netdev, iob, rc ); -} - -/** Common 802.11 errors */ -struct errortab common_wireless_errors[] __errortab = { - __einfo_errortab ( EINFO_EINVAL_CRYPTO_REQUEST ), - __einfo_errortab ( EINFO_ECONNRESET_UNSPECIFIED ), - __einfo_errortab ( EINFO_ECONNRESET_INACTIVITY ), - __einfo_errortab ( EINFO_ECONNRESET_4WAY_TIMEOUT ), - __einfo_errortab ( EINFO_ECONNRESET_8021X_FAILURE ), - __einfo_errortab ( EINFO_ECONNREFUSED_FAILURE ), - __einfo_errortab ( EINFO_ECONNREFUSED_ASSOC_DENIED ), - __einfo_errortab ( EINFO_ECONNREFUSED_AUTH_ALGO_UNSUPP ), -}; - -/* Drag in objects via net80211_ll_protocol */ -REQUIRING_SYMBOL ( net80211_ll_protocol ); - -/* Drag in 802.11 configuration */ -REQUIRE_OBJECT ( config_net80211 ); diff --git a/qemu/roms/ipxe/src/net/80211/rc80211.c b/qemu/roms/ipxe/src/net/80211/rc80211.c deleted file mode 100644 index eea3bc908..000000000 --- a/qemu/roms/ipxe/src/net/80211/rc80211.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Simple 802.11 rate-control algorithm for iPXE. - * - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdlib.h> -#include <ipxe/net80211.h> - -/** - * @file - * - * Simple 802.11 rate-control algorithm - */ - -/** @page rc80211 Rate control philosophy - * - * We want to maximize our transmission speed, to the extent that we - * can do that without dropping undue numbers of packets. We also - * don't want to take up very much code space, so our algorithm has to - * be pretty simple - * - * When we receive a packet, we know what rate it was transmitted at, - * and whether it had to be retransmitted to get to us. - * - * When we send a packet, we hear back how many times it had to be - * retried to get through, and whether it got through at all. - * - * Indications of TX success are more reliable than RX success, but RX - * information helps us know where to start. - * - * To handle all of this, we keep for each rate and each direction (TX - * and RX separately) some state information for the most recent - * packets on that rate and the number of packets for which we have - * information. The state is a 32-bit unsigned integer in which two - * bits represent a packet: 11 if it went through well, 10 if it went - * through with one retry, 01 if it went through with more than one - * retry, or 00 if it didn't go through at all. We define the - * "goodness" for a particular (rate, direction) combination as the - * sum of all the 2-bit fields, times 33, divided by the number of - * 2-bit fields containing valid information (16 except when we're - * starting out). The number produced is between 0 and 99; we use -1 - * for rates with less than 4 RX packets or 1 TX, as an indicator that - * we do not have enough information to rely on them. - * - * In deciding which rates are best, we find the weighted average of - * TX and RX goodness, where the weighting is by number of packets - * with data and TX packets are worth 4 times as much as RX packets. - * The weighted average is called "net goodness" and is also a number - * between 0 and 99. If 3 consecutive packets fail transmission - * outright, we automatically ratchet down the rate; otherwise, we - * switch to the best rate whenever the current rate's goodness falls - * below some threshold, and try increasing our rate when the goodness - * is very high. - * - * This system is optimized for iPXE's style of usage. Because normal - * operation always involves receiving something, we'll make our way - * to the best rate pretty quickly. We tend to follow the lead of the - * sending AP in choosing rates, but we won't use rates for long that - * don't work well for us in transmission. We assume iPXE won't be - * running for long enough that rate patterns will change much, so we - * don't have to keep time counters or the like. And if this doesn't - * work well in practice there are many ways it could be tweaked. - * - * To avoid staying at 1Mbps for a long time, we don't track any - * transmitted packets until we've set our rate based on received - * packets. - */ - -/** Two-bit packet status indicator for a packet with no retries */ -#define RC_PKT_OK 0x3 - -/** Two-bit packet status indicator for a packet with one retry */ -#define RC_PKT_RETRIED_ONCE 0x2 - -/** Two-bit packet status indicator for a TX packet with multiple retries - * - * It is not possible to tell whether an RX packet had one or multiple - * retries; we rely instead on the fact that failed RX packets won't - * get to us at all, so if we receive a lot of RX packets on a certain - * rate it must be pretty good. - */ -#define RC_PKT_RETRIED_MULTI 0x1 - -/** Two-bit packet status indicator for a TX packet that was never ACKed - * - * It is not possible to tell whether an RX packet was setn if it - * didn't get through to us, but if we don't see one we won't increase - * the goodness for its rate. This asymmetry is part of why TX packets - * are weighted much more heavily than RX. - */ -#define RC_PKT_FAILED 0x0 - -/** Number of times to weight TX packets more heavily than RX packets */ -#define RC_TX_FACTOR 4 - -/** Number of consecutive failed TX packets that cause an automatic rate drop */ -#define RC_TX_EMERG_FAIL 3 - -/** Minimum net goodness below which we will search for a better rate */ -#define RC_GOODNESS_MIN 85 - -/** Maximum net goodness above which we will try to increase our rate */ -#define RC_GOODNESS_MAX 95 - -/** Minimum (num RX + @c RC_TX_FACTOR * num TX) to use a certain rate */ -#define RC_UNCERTAINTY_THRESH 4 - -/** TX direction */ -#define TX 0 - -/** RX direction */ -#define RX 1 - -/** A rate control context */ -struct rc80211_ctx -{ - /** Goodness state for each rate, TX and RX */ - u32 goodness[2][NET80211_MAX_RATES]; - - /** Number of packets recorded for each rate */ - u8 count[2][NET80211_MAX_RATES]; - - /** Indication of whether we've set the device rate yet */ - int started; - - /** Counter of all packets sent and received */ - int packets; -}; - -/** - * Initialize rate-control algorithm - * - * @v dev 802.11 device - * @ret ctx Rate-control context, to be stored in @c dev->rctl - */ -struct rc80211_ctx * rc80211_init ( struct net80211_device *dev __unused ) -{ - struct rc80211_ctx *ret = zalloc ( sizeof ( *ret ) ); - return ret; -} - -/** - * Calculate net goodness for a certain rate - * - * @v ctx Rate-control context - * @v rate_idx Index of rate to calculate net goodness for - */ -static int rc80211_calc_net_goodness ( struct rc80211_ctx *ctx, - int rate_idx ) -{ - int sum[2], num[2], dir, pkt; - - for ( dir = 0; dir < 2; dir++ ) { - u32 good = ctx->goodness[dir][rate_idx]; - - num[dir] = ctx->count[dir][rate_idx]; - sum[dir] = 0; - - for ( pkt = 0; pkt < num[dir]; pkt++ ) - sum[dir] += ( good >> ( 2 * pkt ) ) & 0x3; - } - - if ( ( num[TX] * RC_TX_FACTOR + num[RX] ) < RC_UNCERTAINTY_THRESH ) - return -1; - - return ( 33 * ( sum[TX] * RC_TX_FACTOR + sum[RX] ) / - ( num[TX] * RC_TX_FACTOR + num[RX] ) ); -} - -/** - * Determine the best rate to switch to and return it - * - * @v dev 802.11 device - * @ret rate_idx Index of the best rate to switch to - */ -static int rc80211_pick_best ( struct net80211_device *dev ) -{ - struct rc80211_ctx *ctx = dev->rctl; - int best_net_good = 0, best_rate = -1, i; - - for ( i = 0; i < dev->nr_rates; i++ ) { - int net_good = rc80211_calc_net_goodness ( ctx, i ); - - if ( net_good > best_net_good || - ( best_net_good > RC_GOODNESS_MIN && - net_good > RC_GOODNESS_MIN ) ) { - best_net_good = net_good; - best_rate = i; - } - } - - if ( best_rate >= 0 ) { - int old_good = rc80211_calc_net_goodness ( ctx, dev->rate ); - if ( old_good != best_net_good ) - DBGC ( ctx, "802.11 RC %p switching from goodness " - "%d to %d\n", ctx, old_good, best_net_good ); - - ctx->started = 1; - return best_rate; - } - - return dev->rate; -} - -/** - * Set 802.11 device rate - * - * @v dev 802.11 device - * @v rate_idx Index of rate to switch to - * - * This is a thin wrapper around net80211_set_rate_idx to insert a - * debugging message where appropriate. - */ -static inline void rc80211_set_rate ( struct net80211_device *dev, - int rate_idx ) -{ - DBGC ( dev->rctl, "802.11 RC %p changing rate %d->%d Mbps\n", dev->rctl, - dev->rates[dev->rate] / 10, dev->rates[rate_idx] / 10 ); - - net80211_set_rate_idx ( dev, rate_idx ); -} - -/** - * Check rate-control state and change rate if necessary - * - * @v dev 802.11 device - */ -static void rc80211_maybe_set_new ( struct net80211_device *dev ) -{ - struct rc80211_ctx *ctx = dev->rctl; - int net_good; - - net_good = rc80211_calc_net_goodness ( ctx, dev->rate ); - - if ( ! ctx->started ) { - rc80211_set_rate ( dev, rc80211_pick_best ( dev ) ); - return; - } - - if ( net_good < 0 ) /* insufficient data */ - return; - - if ( net_good > RC_GOODNESS_MAX && dev->rate + 1 < dev->nr_rates ) { - int higher = rc80211_calc_net_goodness ( ctx, dev->rate + 1 ); - if ( higher > net_good || higher < 0 ) - rc80211_set_rate ( dev, dev->rate + 1 ); - else - rc80211_set_rate ( dev, rc80211_pick_best ( dev ) ); - } - - if ( net_good < RC_GOODNESS_MIN ) { - rc80211_set_rate ( dev, rc80211_pick_best ( dev ) ); - } -} - -/** - * Update rate-control state - * - * @v dev 802.11 device - * @v direction One of the direction constants TX or RX - * @v rate_idx Index of rate at which packet was sent or received - * @v retries Number of times packet was retried before success - * @v failed If nonzero, the packet failed to get through - */ -static void rc80211_update ( struct net80211_device *dev, int direction, - int rate_idx, int retries, int failed ) -{ - struct rc80211_ctx *ctx = dev->rctl; - u32 goodness = ctx->goodness[direction][rate_idx]; - - if ( ctx->count[direction][rate_idx] < 16 ) - ctx->count[direction][rate_idx]++; - - goodness <<= 2; - if ( failed ) - goodness |= RC_PKT_FAILED; - else if ( retries > 1 ) - goodness |= RC_PKT_RETRIED_MULTI; - else if ( retries ) - goodness |= RC_PKT_RETRIED_ONCE; - else - goodness |= RC_PKT_OK; - - ctx->goodness[direction][rate_idx] = goodness; - - ctx->packets++; - - rc80211_maybe_set_new ( dev ); -} - -/** - * Update rate-control state for transmitted packet - * - * @v dev 802.11 device - * @v retries Number of times packet was transmitted before success - * @v rc Return status code for transmission - */ -void rc80211_update_tx ( struct net80211_device *dev, int retries, int rc ) -{ - struct rc80211_ctx *ctx = dev->rctl; - - if ( ! ctx->started ) - return; - - rc80211_update ( dev, TX, dev->rate, retries, rc ); - - /* Check if the last RC_TX_EMERG_FAIL packets have all failed */ - if ( ! ( ctx->goodness[TX][dev->rate] & - ( ( 1 << ( 2 * RC_TX_EMERG_FAIL ) ) - 1 ) ) ) { - if ( dev->rate == 0 ) - DBGC ( dev->rctl, "802.11 RC %p saw %d consecutive " - "failed TX, but cannot lower rate any further\n", - dev->rctl, RC_TX_EMERG_FAIL ); - else { - DBGC ( dev->rctl, "802.11 RC %p lowering rate (%d->%d " - "Mbps) due to %d consecutive TX failures\n", - dev->rctl, dev->rates[dev->rate] / 10, - dev->rates[dev->rate - 1] / 10, - RC_TX_EMERG_FAIL ); - - rc80211_set_rate ( dev, dev->rate - 1 ); - } - } -} - -/** - * Update rate-control state for received packet - * - * @v dev 802.11 device - * @v retry Whether the received packet had been retransmitted - * @v rate Rate at which packet was received, in 100 kbps units - */ -void rc80211_update_rx ( struct net80211_device *dev, int retry, u16 rate ) -{ - int ridx; - - for ( ridx = 0; ridx < dev->nr_rates && dev->rates[ridx] != rate; - ridx++ ) - ; - if ( ridx >= dev->nr_rates ) - return; /* couldn't find the rate */ - - rc80211_update ( dev, RX, ridx, retry, 0 ); -} - -/** - * Free rate-control context - * - * @v ctx Rate-control context - */ -void rc80211_free ( struct rc80211_ctx *ctx ) -{ - free ( ctx ); -} diff --git a/qemu/roms/ipxe/src/net/80211/sec80211.c b/qemu/roms/ipxe/src/net/80211/sec80211.c deleted file mode 100644 index d1bc75e90..000000000 --- a/qemu/roms/ipxe/src/net/80211/sec80211.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <ipxe/ieee80211.h> -#include <ipxe/net80211.h> -#include <ipxe/sec80211.h> - -/** @file - * - * General secured-network routines required whenever any secure - * network support at all is compiled in. This involves things like - * installing keys, determining the type of security used by a probed - * network, and some small helper functions that take advantage of - * static data in this file. - */ - -/* Unsupported cryptosystem error numbers */ -#define ENOTSUP_WEP __einfo_error ( EINFO_ENOTSUP_WEP ) -#define EINFO_ENOTSUP_WEP __einfo_uniqify ( EINFO_ENOTSUP, \ - ( 0x10 | NET80211_CRYPT_WEP ), "WEP not supported" ) -#define ENOTSUP_TKIP __einfo_error ( EINFO_ENOTSUP_TKIP ) -#define EINFO_ENOTSUP_TKIP __einfo_uniqify ( EINFO_ENOTSUP, \ - ( 0x10 | NET80211_CRYPT_TKIP ), "TKIP not supported" ) -#define ENOTSUP_CCMP __einfo_error ( EINFO_ENOTSUP_CCMP ) -#define EINFO_ENOTSUP_CCMP __einfo_uniqify ( EINFO_ENOTSUP, \ - ( 0x10 | NET80211_CRYPT_CCMP ), "CCMP not supported" ) -#define ENOTSUP_CRYPT( crypt ) \ - EUNIQ ( EINFO_ENOTSUP, ( 0x10 | (crypt) ), \ - ENOTSUP_WEP, ENOTSUP_TKIP, ENOTSUP_CCMP ) - -/** Mapping from net80211 crypto/secprot types to RSN OUI descriptors */ -struct descriptor_map { - /** Value of net80211_crypto_alg or net80211_security_proto */ - u32 net80211_type; - - /** OUI+type in appropriate byte order, masked to exclude vendor */ - u32 oui_type; -}; - -/** Magic number in @a oui_type showing end of list */ -#define END_MAGIC 0xFFFFFFFF - -/** Mapping between net80211 cryptosystems and 802.11i cipher IDs */ -static struct descriptor_map rsn_cipher_map[] = { - { .net80211_type = NET80211_CRYPT_WEP, - .oui_type = IEEE80211_RSN_CTYPE_WEP40 }, - - { .net80211_type = NET80211_CRYPT_WEP, - .oui_type = IEEE80211_RSN_CTYPE_WEP104 }, - - { .net80211_type = NET80211_CRYPT_TKIP, - .oui_type = IEEE80211_RSN_CTYPE_TKIP }, - - { .net80211_type = NET80211_CRYPT_CCMP, - .oui_type = IEEE80211_RSN_CTYPE_CCMP }, - - { .net80211_type = NET80211_CRYPT_UNKNOWN, - .oui_type = END_MAGIC }, -}; - -/** Mapping between net80211 handshakers and 802.11i AKM IDs */ -static struct descriptor_map rsn_akm_map[] = { - { .net80211_type = NET80211_SECPROT_EAP, - .oui_type = IEEE80211_RSN_ATYPE_8021X }, - - { .net80211_type = NET80211_SECPROT_PSK, - .oui_type = IEEE80211_RSN_ATYPE_PSK }, - - { .net80211_type = NET80211_SECPROT_UNKNOWN, - .oui_type = END_MAGIC }, -}; - - -/** - * Install 802.11 cryptosystem - * - * @v which Pointer to the cryptosystem structure to install in - * @v crypt Cryptosystem ID number - * @v key Encryption key to use - * @v len Length of encryption key - * @v rsc Initial receive sequence counter, if applicable - * @ret rc Return status code - * - * The encryption key will not be accessed via the provided pointer - * after this function returns, so you may keep it on the stack. - * - * @a which must point to either @c dev->crypto (for the normal case - * of installing a unicast cryptosystem) or @c dev->gcrypto (to - * install a cryptosystem that will be used only for decrypting - * group-source frames). - */ -int sec80211_install ( struct net80211_crypto **which, - enum net80211_crypto_alg crypt, - const void *key, int len, const void *rsc ) -{ - struct net80211_crypto *crypto = *which; - struct net80211_crypto *tbl_crypto; - - /* Remove old crypto if it exists */ - free ( *which ); - *which = NULL; - - if ( crypt == NET80211_CRYPT_NONE ) { - DBG ( "802.11-Sec not installing null cryptography\n" ); - return 0; - } - - /* Find cryptosystem to use */ - for_each_table_entry ( tbl_crypto, NET80211_CRYPTOS ) { - if ( tbl_crypto->algorithm == crypt ) { - crypto = zalloc ( sizeof ( *crypto ) + - tbl_crypto->priv_len ); - if ( ! crypto ) { - DBG ( "802.11-Sec out of memory\n" ); - return -ENOMEM; - } - - memcpy ( crypto, tbl_crypto, sizeof ( *crypto ) ); - crypto->priv = ( ( void * ) crypto + - sizeof ( *crypto ) ); - break; - } - } - - if ( ! crypto ) { - DBG ( "802.11-Sec no support for cryptosystem %d\n", crypt ); - return -ENOTSUP_CRYPT ( crypt ); - } - - *which = crypto; - - DBG ( "802.11-Sec installing cryptosystem %d as %p with key of " - "length %d\n", crypt, crypto, len ); - - return crypto->init ( crypto, key, len, rsc ); -} - - -/** - * Determine net80211 crypto or handshaking type value to return for RSN info - * - * @v rsnp Pointer to next descriptor count field in RSN IE - * @v rsn_end Pointer to end of RSN IE - * @v map Descriptor map to use - * @v tbl_start Start of linker table to examine for iPXE support - * @v tbl_end End of linker table to examine for iPXE support - * @ret rsnp Updated to point to first byte after descriptors - * @ret map_ent Descriptor map entry of translation to use - * - * The entries in the linker table must be either net80211_crypto or - * net80211_handshaker structures, and @a tbl_stride must be set to - * sizeof() the appropriate one. - * - * This function expects @a rsnp to point at a two-byte descriptor - * count followed by a list of four-byte cipher or AKM descriptors; it - * will return @c NULL if the input packet is malformed, and otherwise - * set @a rsnp to the first byte it has not looked at. It will return - * the first cipher in the list that is supported by the current build - * of iPXE, or the first of all if none are supported. - * - * We play rather fast and loose with type checking, because this - * function is only called from two well-defined places in the - * RSN-checking code. Don't try to use it for anything else. - */ -static struct descriptor_map * rsn_pick_desc ( u8 **rsnp, u8 *rsn_end, - struct descriptor_map *map, - void *tbl_start, void *tbl_end ) -{ - int ndesc; - int ok = 0; - struct descriptor_map *map_ent, *map_ret = NULL; - u8 *rsn = *rsnp; - void *tblp; - size_t tbl_stride = ( map == rsn_cipher_map ? - sizeof ( struct net80211_crypto ) : - sizeof ( struct net80211_handshaker ) ); - - if ( map != rsn_cipher_map && map != rsn_akm_map ) - return NULL; - - /* Determine which types we support */ - for ( tblp = tbl_start; tblp < tbl_end; tblp += tbl_stride ) { - struct net80211_crypto *crypto = tblp; - struct net80211_handshaker *hs = tblp; - - if ( map == rsn_cipher_map ) - ok |= ( 1 << crypto->algorithm ); - else - ok |= ( 1 << hs->protocol ); - } - - /* RSN sanity checks */ - if ( rsn + 2 > rsn_end ) { - DBG ( "RSN detect: malformed descriptor count\n" ); - return NULL; - } - - ndesc = *( u16 * ) rsn; - rsn += 2; - - if ( ! ndesc ) { - DBG ( "RSN detect: no descriptors\n" ); - return NULL; - } - - /* Determine which net80211 crypto types are listed */ - while ( ndesc-- ) { - u32 desc; - - if ( rsn + 4 > rsn_end ) { - DBG ( "RSN detect: malformed descriptor (%d left)\n", - ndesc ); - return NULL; - } - - desc = *( u32 * ) rsn; - rsn += 4; - - for ( map_ent = map; map_ent->oui_type != END_MAGIC; map_ent++ ) - if ( map_ent->oui_type == ( desc & OUI_TYPE_MASK ) ) - break; - - /* Use first cipher as a fallback */ - if ( ! map_ret ) - map_ret = map_ent; - - /* Once we find one we support, use it */ - if ( ok & ( 1 << map_ent->net80211_type ) ) { - map_ret = map_ent; - break; - } - } - - if ( ndesc > 0 ) - rsn += 4 * ndesc; - - *rsnp = rsn; - return map_ret; -} - - -/** - * Find the RSN or WPA information element in the provided beacon frame - * - * @v ie Pointer to first information element to check - * @v ie_end Pointer to end of information element space - * @ret is_rsn TRUE if returned IE is RSN, FALSE if it's WPA - * @ret end Pointer to byte immediately after last byte of data - * @ret data Pointer to first byte of data (the `version' field) - * - * If both an RSN and a WPA information element are found, this - * function will return the first one seen, which by ordering rules - * should always prefer the newer RSN IE. - * - * If no RSN or WPA infomration element is found, returns @c NULL and - * leaves @a is_rsn and @a end in an undefined state. - * - * This function will not return a pointer to an information element - * that states it extends past the tail of the io_buffer, or whose @a - * version field is incorrect. - */ -u8 * sec80211_find_rsn ( union ieee80211_ie *ie, void *ie_end, - int *is_rsn, u8 **end ) -{ - u8 *rsn = NULL; - - if ( ! ieee80211_ie_bound ( ie, ie_end ) ) - return NULL; - - while ( ie ) { - if ( ie->id == IEEE80211_IE_VENDOR && - ie->vendor.oui == IEEE80211_WPA_OUI_VEN ) { - DBG ( "RSN detect: old-style WPA IE found\n" ); - rsn = &ie->vendor.data[0]; - *end = rsn + ie->len - 4; - *is_rsn = 0; - } else if ( ie->id == IEEE80211_IE_RSN ) { - DBG ( "RSN detect: 802.11i RSN IE found\n" ); - rsn = ( u8 * ) &ie->rsn.version; - *end = rsn + ie->len; - *is_rsn = 1; - } - - if ( rsn && ( *end > ( u8 * ) ie_end || rsn >= *end || - *( u16 * ) rsn != IEEE80211_RSN_VERSION ) ) { - DBG ( "RSN detect: malformed RSN IE or unknown " - "version, keep trying\n" ); - rsn = NULL; - } - - if ( rsn ) - break; - - ie = ieee80211_next_ie ( ie, ie_end ); - } - - if ( ! ie ) { - DBG ( "RSN detect: no RSN IE found\n" ); - return NULL; - } - - return rsn; -} - - -/** - * Detect crypto and AKM types from RSN information element - * - * @v is_rsn If TRUE, IE is a new-style RSN information element - * @v start Pointer to first byte of @a version field - * @v end Pointer to first byte not in the RSN IE - * @ret secprot Security handshaking protocol used by network - * @ret crypt Cryptosystem used by network - * @ret rc Return status code - * - * If the IE cannot be parsed, returns an error indication and leaves - * @a secprot and @a crypt unchanged. - */ -int sec80211_detect_ie ( int is_rsn, u8 *start, u8 *end, - enum net80211_security_proto *secprot, - enum net80211_crypto_alg *crypt ) -{ - enum net80211_security_proto sp; - enum net80211_crypto_alg cr; - struct descriptor_map *map; - u8 *rsn = start; - - /* Set some defaults */ - cr = ( is_rsn ? NET80211_CRYPT_CCMP : NET80211_CRYPT_TKIP ); - sp = NET80211_SECPROT_EAP; - - rsn += 2; /* version - already checked */ - rsn += 4; /* group cipher - we don't use it here */ - - if ( rsn >= end ) - goto done; - - /* Pick crypto algorithm */ - map = rsn_pick_desc ( &rsn, end, rsn_cipher_map, - table_start ( NET80211_CRYPTOS ), - table_end ( NET80211_CRYPTOS ) ); - if ( ! map ) - goto invalid_rsn; - - cr = map->net80211_type; - - if ( rsn >= end ) - goto done; - - /* Pick handshaking algorithm */ - map = rsn_pick_desc ( &rsn, end, rsn_akm_map, - table_start ( NET80211_HANDSHAKERS ), - table_end ( NET80211_HANDSHAKERS ) ); - if ( ! map ) - goto invalid_rsn; - - sp = map->net80211_type; - - done: - DBG ( "RSN detect: OK, crypto type %d, secprot type %d\n", cr, sp ); - *secprot = sp; - *crypt = cr; - return 0; - - invalid_rsn: - DBG ( "RSN detect: invalid RSN IE\n" ); - return -EINVAL; -} - - -/** - * Detect the cryptosystem and handshaking protocol used by an 802.11 network - * - * @v iob I/O buffer containing beacon frame - * @ret secprot Security handshaking protocol used by network - * @ret crypt Cryptosystem used by network - * @ret rc Return status code - * - * This function uses weak linkage, as it must be called from generic - * contexts but should only be linked in if some encryption is - * supported; you must test its address against @c NULL before calling - * it. If it does not exist, any network with the PRIVACY bit set in - * beacon->capab should be considered unknown. - */ -int sec80211_detect ( struct io_buffer *iob, - enum net80211_security_proto *secprot, - enum net80211_crypto_alg *crypt ) -{ - struct ieee80211_frame *hdr = iob->data; - struct ieee80211_beacon *beacon = - ( struct ieee80211_beacon * ) hdr->data; - u8 *rsn, *rsn_end; - int is_rsn, rc; - - *crypt = NET80211_CRYPT_UNKNOWN; - *secprot = NET80211_SECPROT_UNKNOWN; - - /* Find RSN or WPA IE */ - if ( ! ( rsn = sec80211_find_rsn ( beacon->info_element, iob->tail, - &is_rsn, &rsn_end ) ) ) { - /* No security IE at all; either WEP or no security. */ - *secprot = NET80211_SECPROT_NONE; - - if ( beacon->capability & IEEE80211_CAPAB_PRIVACY ) - *crypt = NET80211_CRYPT_WEP; - else - *crypt = NET80211_CRYPT_NONE; - - return 0; - } - - /* Determine type of security */ - if ( ( rc = sec80211_detect_ie ( is_rsn, rsn, rsn_end, secprot, - crypt ) ) == 0 ) - return 0; - - /* If we get here, the RSN IE was invalid */ - - *crypt = NET80211_CRYPT_UNKNOWN; - *secprot = NET80211_SECPROT_UNKNOWN; - DBG ( "Failed to handle RSN IE:\n" ); - DBG_HD ( rsn, rsn_end - rsn ); - return rc; -} - - -/** - * Determine RSN descriptor for specified net80211 ID - * - * @v id net80211 ID value - * @v rsnie Whether to return a new-format (RSN IE) descriptor - * @v map Map to use in translation - * @ret desc RSN descriptor, or 0 on error - * - * If @a rsnie is false, returns an old-format (WPA vendor IE) - * descriptor. - */ -static u32 rsn_get_desc ( unsigned id, int rsnie, struct descriptor_map *map ) -{ - u32 vendor = ( rsnie ? IEEE80211_RSN_OUI : IEEE80211_WPA_OUI ); - - for ( ; map->oui_type != END_MAGIC; map++ ) { - if ( map->net80211_type == id ) - return map->oui_type | vendor; - } - - return 0; -} - -/** - * Determine RSN descriptor for specified net80211 cryptosystem number - * - * @v crypt Cryptosystem number - * @v rsnie Whether to return a new-format (RSN IE) descriptor - * @ret desc RSN descriptor - * - * If @a rsnie is false, returns an old-format (WPA vendor IE) - * descriptor. - */ -u32 sec80211_rsn_get_crypto_desc ( enum net80211_crypto_alg crypt, int rsnie ) -{ - return rsn_get_desc ( crypt, rsnie, rsn_cipher_map ); -} - -/** - * Determine RSN descriptor for specified net80211 handshaker number - * - * @v secprot Handshaker number - * @v rsnie Whether to return a new-format (RSN IE) descriptor - * @ret desc RSN descriptor - * - * If @a rsnie is false, returns an old-format (WPA vendor IE) - * descriptor. - */ -u32 sec80211_rsn_get_akm_desc ( enum net80211_security_proto secprot, - int rsnie ) -{ - return rsn_get_desc ( secprot, rsnie, rsn_akm_map ); -} - -/** - * Determine net80211 cryptosystem number from RSN descriptor - * - * @v desc RSN descriptor - * @ret crypt net80211 cryptosystem enumeration value - */ -enum net80211_crypto_alg sec80211_rsn_get_net80211_crypt ( u32 desc ) -{ - struct descriptor_map *map = rsn_cipher_map; - - for ( ; map->oui_type != END_MAGIC; map++ ) { - if ( map->oui_type == ( desc & OUI_TYPE_MASK ) ) - break; - } - - return map->net80211_type; -} diff --git a/qemu/roms/ipxe/src/net/80211/wep.c b/qemu/roms/ipxe/src/net/80211/wep.c deleted file mode 100644 index e22ac8998..000000000 --- a/qemu/roms/ipxe/src/net/80211/wep.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <ipxe/net80211.h> -#include <ipxe/sec80211.h> -#include <ipxe/crypto.h> -#include <ipxe/arc4.h> -#include <ipxe/crc32.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -/** @file - * - * The WEP wireless encryption method (insecure!) - * - * The data field in a WEP-encrypted packet contains a 3-byte - * initialisation vector, one-byte Key ID field (only the bottom two - * bits are ever used), encrypted data, and a 4-byte encrypted CRC of - * the plaintext data, called the ICV. To decrypt it, the IV is - * prepended to the shared key and the data stream (including ICV) is - * run through the ARC4 stream cipher; if the ICV matches a CRC32 - * calculated on the plaintext, the packet is valid. - * - * For efficiency and code-size reasons, this file assumes it is - * running on a little-endian machine. - */ - -/** Length of WEP initialisation vector */ -#define WEP_IV_LEN 3 - -/** Length of WEP key ID byte */ -#define WEP_KID_LEN 1 - -/** Length of WEP ICV checksum */ -#define WEP_ICV_LEN 4 - -/** Maximum length of WEP key */ -#define WEP_MAX_KEY 16 - -/** Amount of data placed before the encrypted bytes */ -#define WEP_HEADER_LEN 4 - -/** Amount of data placed after the encrypted bytes */ -#define WEP_TRAILER_LEN 4 - -/** Total WEP overhead bytes */ -#define WEP_OVERHEAD 8 - -/** Context for WEP encryption and decryption */ -struct wep_ctx -{ - /** Encoded WEP key - * - * The actual key bytes are stored beginning at offset 3, to - * leave room for easily inserting the IV before a particular - * operation. - */ - u8 key[WEP_IV_LEN + WEP_MAX_KEY]; - - /** Length of WEP key (not including IV bytes) */ - int keylen; - - /** ARC4 context */ - struct arc4_ctx arc4; -}; - -/** - * Initialize WEP algorithm - * - * @v crypto 802.11 cryptographic algorithm - * @v key WEP key to use - * @v keylen Length of WEP key - * @v rsc Initial receive sequence counter (unused) - * @ret rc Return status code - * - * Standard key lengths are 5 and 13 bytes; 16-byte keys are - * occasionally supported as an extension to the standard. - */ -static int wep_init ( struct net80211_crypto *crypto, const void *key, - int keylen, const void *rsc __unused ) -{ - struct wep_ctx *ctx = crypto->priv; - - ctx->keylen = ( keylen > WEP_MAX_KEY ? WEP_MAX_KEY : keylen ); - memcpy ( ctx->key + WEP_IV_LEN, key, ctx->keylen ); - - return 0; -} - -/** - * Encrypt packet using WEP - * - * @v crypto 802.11 cryptographic algorithm - * @v iob I/O buffer of plaintext packet - * @ret eiob Newly allocated I/O buffer for encrypted packet, or NULL - * - * If memory allocation fails, @c NULL is returned. - */ -static struct io_buffer * wep_encrypt ( struct net80211_crypto *crypto, - struct io_buffer *iob ) -{ - struct wep_ctx *ctx = crypto->priv; - struct io_buffer *eiob; - struct ieee80211_frame *hdr; - const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; - int datalen = iob_len ( iob ) - hdrlen; - int newlen = hdrlen + datalen + WEP_OVERHEAD; - u32 iv, icv; - - eiob = alloc_iob ( newlen ); - if ( ! eiob ) - return NULL; - - memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); - hdr = eiob->data; - hdr->fc |= IEEE80211_FC_PROTECTED; - - /* Calculate IV, put it in the header (with key ID byte = 0), and - set it up at the start of the encryption key. */ - iv = random() & 0xffffff; /* IV in bottom 3 bytes, top byte = KID = 0 */ - memcpy ( iob_put ( eiob, WEP_HEADER_LEN ), &iv, WEP_HEADER_LEN ); - memcpy ( ctx->key, &iv, WEP_IV_LEN ); - - /* Encrypt the data using RC4 */ - cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key, - ctx->keylen + WEP_IV_LEN ); - cipher_encrypt ( &arc4_algorithm, &ctx->arc4, iob->data + hdrlen, - iob_put ( eiob, datalen ), datalen ); - - /* Add ICV */ - icv = ~crc32_le ( ~0, iob->data + hdrlen, datalen ); - cipher_encrypt ( &arc4_algorithm, &ctx->arc4, &icv, - iob_put ( eiob, WEP_ICV_LEN ), WEP_ICV_LEN ); - - return eiob; -} - -/** - * Decrypt packet using WEP - * - * @v crypto 802.11 cryptographic algorithm - * @v eiob I/O buffer of encrypted packet - * @ret iob Newly allocated I/O buffer for plaintext packet, or NULL - * - * If a consistency check for the decryption fails (usually indicating - * an invalid key), @c NULL is returned. - */ -static struct io_buffer * wep_decrypt ( struct net80211_crypto *crypto, - struct io_buffer *eiob ) -{ - struct wep_ctx *ctx = crypto->priv; - struct io_buffer *iob; - struct ieee80211_frame *hdr; - const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; - int datalen = iob_len ( eiob ) - hdrlen - WEP_OVERHEAD; - int newlen = hdrlen + datalen; - u32 iv, icv, crc; - - iob = alloc_iob ( newlen ); - if ( ! iob ) - return NULL; - - memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen ); - hdr = iob->data; - hdr->fc &= ~IEEE80211_FC_PROTECTED; - - /* Strip off IV and use it to initialize cryptosystem */ - memcpy ( &iv, eiob->data + hdrlen, 4 ); - iv &= 0xffffff; /* ignore key ID byte */ - memcpy ( ctx->key, &iv, WEP_IV_LEN ); - - /* Decrypt the data using RC4 */ - cipher_setkey ( &arc4_algorithm, &ctx->arc4, ctx->key, - ctx->keylen + WEP_IV_LEN ); - cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen + - WEP_HEADER_LEN, iob_put ( iob, datalen ), datalen ); - - /* Strip off ICV and verify it */ - cipher_decrypt ( &arc4_algorithm, &ctx->arc4, eiob->data + hdrlen + - WEP_HEADER_LEN + datalen, &icv, WEP_ICV_LEN ); - crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen ); - if ( crc != icv ) { - DBGC ( crypto, "WEP %p CRC mismatch: expect %08x, get %08x\n", - crypto, icv, crc ); - free_iob ( iob ); - return NULL; - } - return iob; -} - -/** WEP cryptosystem for 802.11 */ -struct net80211_crypto wep_crypto __net80211_crypto = { - .algorithm = NET80211_CRYPT_WEP, - .init = wep_init, - .encrypt = wep_encrypt, - .decrypt = wep_decrypt, - .priv_len = sizeof ( struct wep_ctx ), -}; - -/** - * Initialize trivial 802.11 security handshaker - * - * @v dev 802.11 device - * @v ctx Security handshaker - * - * This simply fetches a WEP key from netX/key, and if it exists, - * installs WEP cryptography on the 802.11 device. No real handshaking - * is performed. - */ -static int trivial_init ( struct net80211_device *dev ) -{ - u8 key[WEP_MAX_KEY]; /* support up to 128-bit keys */ - int len; - int rc; - - if ( dev->associating && - dev->associating->crypto == NET80211_CRYPT_NONE ) - return 0; /* no crypto? OK. */ - - len = fetch_raw_setting ( netdev_settings ( dev->netdev ), - &net80211_key_setting, key, WEP_MAX_KEY ); - - if ( len <= 0 ) { - DBGC ( dev, "802.11 %p cannot do WEP without a key\n", dev ); - return -EACCES; - } - - /* Full 128-bit keys are a nonstandard extension, but they're - utterly trivial to support, so we do. */ - if ( len != 5 && len != 13 && len != 16 ) { - DBGC ( dev, "802.11 %p invalid WEP key length %d\n", - dev, len ); - return -EINVAL; - } - - DBGC ( dev, "802.11 %p installing %d-bit WEP\n", dev, len * 8 ); - - rc = sec80211_install ( &dev->crypto, NET80211_CRYPT_WEP, key, len, - NULL ); - if ( rc < 0 ) - return rc; - - return 0; -} - -/** - * Check for key change on trivial 802.11 security handshaker - * - * @v dev 802.11 device - * @v ctx Security handshaker - */ -static int trivial_change_key ( struct net80211_device *dev ) -{ - u8 key[WEP_MAX_KEY]; - int len; - int change = 0; - - /* If going from WEP to clear, or something else to WEP, reassociate. */ - if ( ! dev->crypto || ( dev->crypto->init != wep_init ) ) - change ^= 1; - - len = fetch_raw_setting ( netdev_settings ( dev->netdev ), - &net80211_key_setting, key, WEP_MAX_KEY ); - if ( len <= 0 ) - change ^= 1; - - /* Changing crypto type => return nonzero to reassociate. */ - if ( change ) - return -EINVAL; - - /* Going from no crypto to still no crypto => nothing to do. */ - if ( len <= 0 ) - return 0; - - /* Otherwise, reinitialise WEP with new key. */ - return wep_init ( dev->crypto, key, len, NULL ); -} - -/** Trivial 802.11 security handshaker */ -struct net80211_handshaker trivial_handshaker __net80211_handshaker = { - .protocol = NET80211_SECPROT_NONE, - .init = trivial_init, - .change_key = trivial_change_key, - .priv_len = 0, -}; diff --git a/qemu/roms/ipxe/src/net/80211/wpa.c b/qemu/roms/ipxe/src/net/80211/wpa.c deleted file mode 100644 index 77f66d825..000000000 --- a/qemu/roms/ipxe/src/net/80211/wpa.c +++ /dev/null @@ -1,916 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <ipxe/net80211.h> -#include <ipxe/sec80211.h> -#include <ipxe/wpa.h> -#include <ipxe/eapol.h> -#include <ipxe/crypto.h> -#include <ipxe/arc4.h> -#include <ipxe/crc32.h> -#include <ipxe/sha1.h> -#include <ipxe/hmac.h> -#include <ipxe/list.h> -#include <ipxe/ethernet.h> -#include <ipxe/rbg.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> - -/** @file - * - * Handler for the aspects of WPA handshaking that are independent of - * 802.1X/PSK or TKIP/CCMP; this mostly involves the 4-Way Handshake. - */ - -/** List of WPA contexts in active use. */ -struct list_head wpa_contexts = LIST_HEAD_INIT ( wpa_contexts ); - - -/** - * Return an error code and deauthenticate - * - * @v ctx WPA common context - * @v rc Return status code - * @ret rc The passed return status code - */ -static int wpa_fail ( struct wpa_common_ctx *ctx, int rc ) -{ - net80211_deauthenticate ( ctx->dev, rc ); - return rc; -} - - -/** - * Find a cryptosystem handler structure from a crypto ID - * - * @v crypt Cryptosystem ID - * @ret crypto Cryptosystem handler structure - * - * If support for @a crypt is not compiled in to iPXE, or if @a crypt - * is NET80211_CRYPT_UNKNOWN, returns @c NULL. - */ -static struct net80211_crypto * -wpa_find_cryptosystem ( enum net80211_crypto_alg crypt ) -{ - struct net80211_crypto *crypto; - - for_each_table_entry ( crypto, NET80211_CRYPTOS ) { - if ( crypto->algorithm == crypt ) - return crypto; - } - - return NULL; -} - - -/** - * Find WPA key integrity and encryption handler from key version field - * - * @v ver Version bits of EAPOL-Key info field - * @ret kie Key integrity and encryption handler - */ -struct wpa_kie * wpa_find_kie ( int version ) -{ - struct wpa_kie *kie; - - for_each_table_entry ( kie, WPA_KIES ) { - if ( kie->version == version ) - return kie; - } - - return NULL; -} - - -/** - * Construct RSN or WPA information element - * - * @v dev 802.11 device - * @ret ie_ret RSN or WPA information element - * @ret rc Return status code - * - * This function allocates, fills, and returns a RSN or WPA - * information element suitable for including in an association - * request frame to the network identified by @c dev->associating. - * If it is impossible to construct an information element consistent - * with iPXE's capabilities that is compatible with that network, or - * if none should be sent because that network's beacon included no - * security information, returns an error indication and leaves - * @a ie_ret unchanged. - * - * The returned IE will be of the same type (RSN or WPA) as was - * included in the beacon for the network it is destined for. - */ -int wpa_make_rsn_ie ( struct net80211_device *dev, union ieee80211_ie **ie_ret ) -{ - u8 *rsn, *rsn_end; - int is_rsn; - u32 group_cipher; - enum net80211_crypto_alg gcrypt; - int ie_len; - u8 *iep; - struct ieee80211_ie_rsn *ie; - struct ieee80211_frame *hdr; - struct ieee80211_beacon *beacon; - - if ( ! dev->associating ) { - DBG ( "WPA: Can't make RSN IE for a non-associating device\n" ); - return -EINVAL; - } - - hdr = dev->associating->beacon->data; - beacon = ( struct ieee80211_beacon * ) hdr->data; - rsn = sec80211_find_rsn ( beacon->info_element, - dev->associating->beacon->tail, &is_rsn, - &rsn_end ); - if ( ! rsn ) { - DBG ( "WPA: Can't make RSN IE when we didn't get one\n" ); - return -EINVAL; - } - - rsn += 2; /* skip version */ - group_cipher = *( u32 * ) rsn; - gcrypt = sec80211_rsn_get_net80211_crypt ( group_cipher ); - - if ( ! wpa_find_cryptosystem ( gcrypt ) || - ! wpa_find_cryptosystem ( dev->associating->crypto ) ) { - DBG ( "WPA: No support for (GC:%d, PC:%d)\n", - gcrypt, dev->associating->crypto ); - return -ENOTSUP; - } - - /* Everything looks good - make our IE. */ - - /* WPA IEs need 4 more bytes for the OUI+type */ - ie_len = ieee80211_rsn_size ( 1, 1, 0, is_rsn ) + ( 4 * ! is_rsn ); - iep = malloc ( ie_len ); - if ( ! iep ) - return -ENOMEM; - - *ie_ret = ( union ieee80211_ie * ) iep; - - /* Store ID and length bytes. */ - *iep++ = ( is_rsn ? IEEE80211_IE_RSN : IEEE80211_IE_VENDOR ); - *iep++ = ie_len - 2; - - /* Store OUI+type for WPA IEs. */ - if ( ! is_rsn ) { - *( u32 * ) iep = IEEE80211_WPA_OUI_VEN; - iep += 4; - } - - /* If this is a WPA IE, the id and len bytes in the - ieee80211_ie_rsn structure will not be valid, but by doing - the cast we can fill all the other fields much more - readily. */ - - ie = ( struct ieee80211_ie_rsn * ) ( iep - 2 ); - ie->version = IEEE80211_RSN_VERSION; - ie->group_cipher = group_cipher; - ie->pairwise_count = 1; - ie->pairwise_cipher[0] = - sec80211_rsn_get_crypto_desc ( dev->associating->crypto, - is_rsn ); - ie->akm_count = 1; - ie->akm_list[0] = - sec80211_rsn_get_akm_desc ( dev->associating->handshaking, - is_rsn ); - if ( is_rsn ) { - ie->rsn_capab = 0; - ie->pmkid_count = 0; - } - - return 0; -} - - -/** - * Set up generic WPA support to handle 4-Way Handshake - * - * @v dev 802.11 device - * @v ctx WPA common context - * @v pmk Pairwise Master Key to use for session - * @v pmk_len Length of PMK, almost always 32 - * @ret rc Return status code - */ -int wpa_start ( struct net80211_device *dev, struct wpa_common_ctx *ctx, - const void *pmk, size_t pmk_len ) -{ - struct io_buffer *iob; - struct ieee80211_frame *hdr; - struct ieee80211_beacon *beacon; - u8 *ap_rsn_ie = NULL, *ap_rsn_ie_end; - - if ( ! dev->rsn_ie || ! dev->associating ) - return -EINVAL; - - ctx->dev = dev; - memcpy ( ctx->pmk, pmk, ctx->pmk_len = pmk_len ); - ctx->state = WPA_READY; - ctx->replay = ~0ULL; - - iob = dev->associating->beacon; - hdr = iob->data; - beacon = ( struct ieee80211_beacon * ) hdr->data; - ap_rsn_ie = sec80211_find_rsn ( beacon->info_element, iob->tail, - &ctx->ap_rsn_is_rsn, &ap_rsn_ie_end ); - if ( ap_rsn_ie ) { - ctx->ap_rsn_ie = malloc ( ap_rsn_ie_end - ap_rsn_ie ); - if ( ! ctx->ap_rsn_ie ) - return -ENOMEM; - memcpy ( ctx->ap_rsn_ie, ap_rsn_ie, ap_rsn_ie_end - ap_rsn_ie ); - ctx->ap_rsn_ie_len = ap_rsn_ie_end - ap_rsn_ie; - } else { - return -ENOENT; - } - - ctx->crypt = dev->associating->crypto; - ctx->gcrypt = NET80211_CRYPT_UNKNOWN; - - list_add_tail ( &ctx->list, &wpa_contexts ); - return 0; -} - - -/** - * Disable handling of received WPA handshake frames - * - * @v dev 802.11 device - */ -void wpa_stop ( struct net80211_device *dev ) -{ - struct wpa_common_ctx *ctx, *tmp; - - list_for_each_entry_safe ( ctx, tmp, &wpa_contexts, list ) { - if ( ctx->dev == dev ) { - free ( ctx->ap_rsn_ie ); - ctx->ap_rsn_ie = NULL; - list_del ( &ctx->list ); - } - } -} - - -/** - * Derive pairwise transient key - * - * @v ctx WPA common context - */ -static void wpa_derive_ptk ( struct wpa_common_ctx *ctx ) -{ - struct { - u8 mac1[ETH_ALEN]; - u8 mac2[ETH_ALEN]; - u8 nonce1[WPA_NONCE_LEN]; - u8 nonce2[WPA_NONCE_LEN]; - } __attribute__ (( packed )) ptk_data; - - /* The addresses and nonces are stored in numerical order (!) */ - - if ( memcmp ( ctx->dev->netdev->ll_addr, ctx->dev->bssid, - ETH_ALEN ) < 0 ) { - memcpy ( ptk_data.mac1, ctx->dev->netdev->ll_addr, ETH_ALEN ); - memcpy ( ptk_data.mac2, ctx->dev->bssid, ETH_ALEN ); - } else { - memcpy ( ptk_data.mac1, ctx->dev->bssid, ETH_ALEN ); - memcpy ( ptk_data.mac2, ctx->dev->netdev->ll_addr, ETH_ALEN ); - } - - if ( memcmp ( ctx->Anonce, ctx->Snonce, WPA_NONCE_LEN ) < 0 ) { - memcpy ( ptk_data.nonce1, ctx->Anonce, WPA_NONCE_LEN ); - memcpy ( ptk_data.nonce2, ctx->Snonce, WPA_NONCE_LEN ); - } else { - memcpy ( ptk_data.nonce1, ctx->Snonce, WPA_NONCE_LEN ); - memcpy ( ptk_data.nonce2, ctx->Anonce, WPA_NONCE_LEN ); - } - - DBGC2 ( ctx, "WPA %p A1 %s, A2 %s\n", ctx, eth_ntoa ( ptk_data.mac1 ), - eth_ntoa ( ptk_data.mac2 ) ); - DBGC2 ( ctx, "WPA %p Nonce1, Nonce2:\n", ctx ); - DBGC2_HD ( ctx, ptk_data.nonce1, WPA_NONCE_LEN ); - DBGC2_HD ( ctx, ptk_data.nonce2, WPA_NONCE_LEN ); - - prf_sha1 ( ctx->pmk, ctx->pmk_len, - "Pairwise key expansion", - &ptk_data, sizeof ( ptk_data ), - &ctx->ptk, sizeof ( ctx->ptk ) ); - - DBGC2 ( ctx, "WPA %p PTK:\n", ctx ); - DBGC2_HD ( ctx, &ctx->ptk, sizeof ( ctx->ptk ) ); -} - - -/** - * Install pairwise transient key - * - * @v ctx WPA common context - * @v len Key length (16 for CCMP, 32 for TKIP) - * @ret rc Return status code - */ -static inline int wpa_install_ptk ( struct wpa_common_ctx *ctx, int len ) -{ - DBGC ( ctx, "WPA %p: installing %d-byte pairwise transient key\n", - ctx, len ); - DBGC2_HD ( ctx, &ctx->ptk.tk, len ); - - return sec80211_install ( &ctx->dev->crypto, ctx->crypt, - &ctx->ptk.tk, len, NULL ); -} - -/** - * Install group transient key - * - * @v ctx WPA common context - * @v len Key length (16 for CCMP, 32 for TKIP) - * @v rsc Receive sequence counter field in EAPOL-Key packet - * @ret rc Return status code - */ -static inline int wpa_install_gtk ( struct wpa_common_ctx *ctx, int len, - const void *rsc ) -{ - DBGC ( ctx, "WPA %p: installing %d-byte group transient key\n", - ctx, len ); - DBGC2_HD ( ctx, &ctx->gtk.tk, len ); - - return sec80211_install ( &ctx->dev->gcrypto, ctx->gcrypt, - &ctx->gtk.tk, len, rsc ); -} - -/** - * Search for group transient key, and install it if found - * - * @v ctx WPA common context - * @v ie Pointer to first IE in key data field - * @v ie_end Pointer to first byte not in key data field - * @v rsc Receive sequence counter field in EAPOL-Key packet - * @ret rc Return status code - */ -static int wpa_maybe_install_gtk ( struct wpa_common_ctx *ctx, - union ieee80211_ie *ie, void *ie_end, - const void *rsc ) -{ - struct wpa_kde *kde; - - if ( ! ieee80211_ie_bound ( ie, ie_end ) ) - return -ENOENT; - - while ( ie ) { - if ( ie->id == IEEE80211_IE_VENDOR && - ie->vendor.oui == WPA_KDE_GTK ) - break; - - ie = ieee80211_next_ie ( ie, ie_end ); - } - - if ( ! ie ) - return -ENOENT; - - if ( ie->len - 6u > sizeof ( ctx->gtk.tk ) ) { - DBGC ( ctx, "WPA %p: GTK KDE is too long (%d bytes, max %zd)\n", - ctx, ie->len - 4, sizeof ( ctx->gtk.tk ) ); - return -EINVAL; - } - - /* XXX We ignore key ID for now. */ - kde = ( struct wpa_kde * ) ie; - memcpy ( &ctx->gtk.tk, &kde->gtk_encap.gtk, kde->len - 6 ); - - return wpa_install_gtk ( ctx, kde->len - 6, rsc ); -} - - -/** - * Allocate I/O buffer for construction of outgoing EAPOL-Key frame - * - * @v kdlen Maximum number of bytes in the Key Data field - * @ret iob Newly allocated I/O buffer - * - * The returned buffer will have space reserved for the link-layer and - * EAPOL headers, and will have @c iob->tail pointing to the start of - * the Key Data field. Thus, it is necessary to use iob_put() in - * filling the Key Data. - */ -static struct io_buffer * wpa_alloc_frame ( int kdlen ) -{ - struct io_buffer *ret = alloc_iob ( sizeof ( struct eapol_key_pkt ) + - kdlen + EAPOL_HDR_LEN + - MAX_LL_HEADER_LEN ); - if ( ! ret ) - return NULL; - - iob_reserve ( ret, MAX_LL_HEADER_LEN + EAPOL_HDR_LEN ); - memset ( iob_put ( ret, sizeof ( struct eapol_key_pkt ) ), 0, - sizeof ( struct eapol_key_pkt ) ); - - return ret; -} - - -/** - * Send EAPOL-Key packet - * - * @v iob I/O buffer, with sufficient headroom for headers - * @v dev 802.11 device - * @v kie Key integrity and encryption handler - * @v is_rsn If TRUE, handshake uses new RSN format - * @ret rc Return status code - * - * If a KIE is specified, the MIC will be filled in before transmission. - */ -static int wpa_send_eapol ( struct io_buffer *iob, struct wpa_common_ctx *ctx, - struct wpa_kie *kie ) -{ - struct eapol_key_pkt *pkt = iob->data; - struct eapol_frame *eapol = iob_push ( iob, EAPOL_HDR_LEN ); - - pkt->info = htons ( pkt->info ); - pkt->keysize = htons ( pkt->keysize ); - pkt->datalen = htons ( pkt->datalen ); - pkt->replay = cpu_to_be64 ( pkt->replay ); - eapol->version = EAPOL_THIS_VERSION; - eapol->type = EAPOL_TYPE_KEY; - eapol->length = htons ( iob->tail - iob->data - sizeof ( *eapol ) ); - - memset ( pkt->mic, 0, sizeof ( pkt->mic ) ); - if ( kie ) - kie->mic ( &ctx->ptk.kck, eapol, EAPOL_HDR_LEN + - sizeof ( *pkt ) + ntohs ( pkt->datalen ), - pkt->mic ); - - return net_tx ( iob, ctx->dev->netdev, &eapol_protocol, - ctx->dev->bssid, ctx->dev->netdev->ll_addr ); -} - - -/** - * Send second frame in 4-Way Handshake - * - * @v ctx WPA common context - * @v pkt First frame, to which this is a reply - * @v is_rsn If TRUE, handshake uses new RSN format - * @v kie Key integrity and encryption handler - * @ret rc Return status code - */ -static int wpa_send_2_of_4 ( struct wpa_common_ctx *ctx, - struct eapol_key_pkt *pkt, int is_rsn, - struct wpa_kie *kie ) -{ - struct io_buffer *iob = wpa_alloc_frame ( ctx->dev->rsn_ie->len + 2 ); - struct eapol_key_pkt *npkt; - - if ( ! iob ) - return -ENOMEM; - - npkt = iob->data; - memcpy ( npkt, pkt, sizeof ( *pkt ) ); - npkt->info &= ~EAPOL_KEY_INFO_KEY_ACK; - npkt->info |= EAPOL_KEY_INFO_KEY_MIC; - if ( is_rsn ) - npkt->keysize = 0; - memcpy ( npkt->nonce, ctx->Snonce, sizeof ( npkt->nonce ) ); - npkt->datalen = ctx->dev->rsn_ie->len + 2; - memcpy ( iob_put ( iob, npkt->datalen ), ctx->dev->rsn_ie, - npkt->datalen ); - - DBGC ( ctx, "WPA %p: sending 2/4\n", ctx ); - - return wpa_send_eapol ( iob, ctx, kie ); -} - - -/** - * Handle receipt of first frame in 4-Way Handshake - * - * @v ctx WPA common context - * @v pkt EAPOL-Key packet - * @v is_rsn If TRUE, frame uses new RSN format - * @v kie Key integrity and encryption handler - * @ret rc Return status code - */ -static int wpa_handle_1_of_4 ( struct wpa_common_ctx *ctx, - struct eapol_key_pkt *pkt, int is_rsn, - struct wpa_kie *kie ) -{ - if ( ctx->state == WPA_WAITING ) - return -EINVAL; - - ctx->state = WPA_WORKING; - memcpy ( ctx->Anonce, pkt->nonce, sizeof ( ctx->Anonce ) ); - if ( ! ctx->have_Snonce ) { - rbg_generate ( NULL, 0, 0, ctx->Snonce, - sizeof ( ctx->Snonce ) ); - ctx->have_Snonce = 1; - } - - DBGC ( ctx, "WPA %p: received 1/4, looks OK\n", ctx ); - - wpa_derive_ptk ( ctx ); - - return wpa_send_2_of_4 ( ctx, pkt, is_rsn, kie ); -} - - -/** - * Send fourth frame in 4-Way Handshake, or second in Group Key Handshake - * - * @v ctx WPA common context - * @v pkt EAPOL-Key packet for frame to which we're replying - * @v is_rsn If TRUE, frame uses new RSN format - * @v kie Key integrity and encryption handler - * @ret rc Return status code - */ -static int wpa_send_final ( struct wpa_common_ctx *ctx, - struct eapol_key_pkt *pkt, int is_rsn, - struct wpa_kie *kie ) -{ - struct io_buffer *iob = wpa_alloc_frame ( 0 ); - struct eapol_key_pkt *npkt; - - if ( ! iob ) - return -ENOMEM; - - npkt = iob->data; - memcpy ( npkt, pkt, sizeof ( *pkt ) ); - npkt->info &= ~( EAPOL_KEY_INFO_KEY_ACK | EAPOL_KEY_INFO_INSTALL | - EAPOL_KEY_INFO_KEY_ENC ); - if ( is_rsn ) - npkt->keysize = 0; - memset ( npkt->nonce, 0, sizeof ( npkt->nonce ) ); - memset ( npkt->iv, 0, sizeof ( npkt->iv ) ); - npkt->datalen = 0; - - if ( npkt->info & EAPOL_KEY_INFO_TYPE ) - DBGC ( ctx, "WPA %p: sending 4/4\n", ctx ); - else - DBGC ( ctx, "WPA %p: sending 2/2\n", ctx ); - - return wpa_send_eapol ( iob, ctx, kie ); - -} - - -/** - * Handle receipt of third frame in 4-Way Handshake - * - * @v ctx WPA common context - * @v pkt EAPOL-Key packet - * @v is_rsn If TRUE, frame uses new RSN format - * @v kie Key integrity and encryption handler - * @ret rc Return status code - */ -static int wpa_handle_3_of_4 ( struct wpa_common_ctx *ctx, - struct eapol_key_pkt *pkt, int is_rsn, - struct wpa_kie *kie ) -{ - int rc; - u8 *this_rsn, *this_rsn_end; - u8 *new_rsn, *new_rsn_end; - int this_is_rsn, new_is_rsn; - - if ( ctx->state == WPA_WAITING ) - return -EINVAL; - - ctx->state = WPA_WORKING; - - /* Check nonce */ - if ( memcmp ( ctx->Anonce, pkt->nonce, WPA_NONCE_LEN ) != 0 ) { - DBGC ( ctx, "WPA %p ALERT: nonce mismatch in 3/4\n", ctx ); - return wpa_fail ( ctx, -EACCES ); - } - - /* Check RSN IE */ - this_rsn = sec80211_find_rsn ( ( union ieee80211_ie * ) pkt->data, - pkt->data + pkt->datalen, - &this_is_rsn, &this_rsn_end ); - if ( this_rsn ) - new_rsn = sec80211_find_rsn ( ( union ieee80211_ie * ) - this_rsn_end, - pkt->data + pkt->datalen, - &new_is_rsn, &new_rsn_end ); - else - new_rsn = NULL; - - if ( ! ctx->ap_rsn_ie || ! this_rsn || - ctx->ap_rsn_ie_len != ( this_rsn_end - this_rsn ) || - ctx->ap_rsn_is_rsn != this_is_rsn || - memcmp ( ctx->ap_rsn_ie, this_rsn, ctx->ap_rsn_ie_len ) != 0 ) { - DBGC ( ctx, "WPA %p ALERT: RSN mismatch in 3/4\n", ctx ); - DBGC2 ( ctx, "WPA %p RSNs (in 3/4, in beacon):\n", ctx ); - DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn ); - DBGC2_HD ( ctx, ctx->ap_rsn_ie, ctx->ap_rsn_ie_len ); - return wpa_fail ( ctx, -EACCES ); - } - - /* Don't switch if they just supplied both styles of IE - simultaneously; we need two RSN IEs or two WPA IEs to - switch ciphers. They'll be immediately consecutive because - of ordering guarantees. */ - if ( new_rsn && this_is_rsn == new_is_rsn ) { - struct net80211_wlan *assoc = ctx->dev->associating; - DBGC ( ctx, "WPA %p: accommodating bait-and-switch tactics\n", - ctx ); - DBGC2 ( ctx, "WPA %p RSNs (in 3/4+beacon, new in 3/4):\n", - ctx ); - DBGC2_HD ( ctx, this_rsn, this_rsn_end - this_rsn ); - DBGC2_HD ( ctx, new_rsn, new_rsn_end - new_rsn ); - - if ( ( rc = sec80211_detect_ie ( new_is_rsn, new_rsn, - new_rsn_end, - &assoc->handshaking, - &assoc->crypto ) ) != 0 ) - DBGC ( ctx, "WPA %p: bait-and-switch invalid, staying " - "with original request\n", ctx ); - } else { - new_rsn = this_rsn; - new_is_rsn = this_is_rsn; - new_rsn_end = this_rsn_end; - } - - /* Grab group cryptosystem ID */ - ctx->gcrypt = sec80211_rsn_get_net80211_crypt ( *( u32 * ) - ( new_rsn + 2 ) ); - - /* Check for a GTK, if info field is encrypted */ - if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) { - rc = wpa_maybe_install_gtk ( ctx, - ( union ieee80211_ie * ) pkt->data, - pkt->data + pkt->datalen, - pkt->rsc ); - if ( rc < 0 ) { - DBGC ( ctx, "WPA %p did not install GTK in 3/4: %s\n", - ctx, strerror ( rc ) ); - if ( rc != -ENOENT ) - return wpa_fail ( ctx, rc ); - } - } - - DBGC ( ctx, "WPA %p: received 3/4, looks OK\n", ctx ); - - /* Send final message */ - rc = wpa_send_final ( ctx, pkt, is_rsn, kie ); - if ( rc < 0 ) - return wpa_fail ( ctx, rc ); - - /* Install PTK */ - rc = wpa_install_ptk ( ctx, pkt->keysize ); - if ( rc < 0 ) { - DBGC ( ctx, "WPA %p failed to install PTK: %s\n", ctx, - strerror ( rc ) ); - return wpa_fail ( ctx, rc ); - } - - /* Mark us as needing a new Snonce if we rekey */ - ctx->have_Snonce = 0; - - /* Done! */ - ctx->state = WPA_SUCCESS; - return 0; -} - - -/** - * Handle receipt of first frame in Group Key Handshake - * - * @v ctx WPA common context - * @v pkt EAPOL-Key packet - * @v is_rsn If TRUE, frame uses new RSN format - * @v kie Key integrity and encryption handler - * @ret rc Return status code - */ -static int wpa_handle_1_of_2 ( struct wpa_common_ctx *ctx, - struct eapol_key_pkt *pkt, int is_rsn, - struct wpa_kie *kie ) -{ - int rc; - - /* - * WPA and RSN do this completely differently. - * - * The idea of encoding the GTK (or PMKID, or various other - * things) into a KDE that looks like an information element - * is an RSN innovation; old WPA code never encapsulates - * things like that. If it looks like an info element, it - * really is (for the WPA IE check in frames 2/4 and 3/4). The - * "key data encrypted" bit in the info field is also specific - * to RSN. - * - * So from an old WPA host, 3/4 does not contain an - * encapsulated GTK. The first frame of the GK handshake - * contains it, encrypted, but without a KDE wrapper, and with - * the key ID field (which iPXE doesn't use) shoved away in - * the reserved bits in the info field, and the TxRx bit - * stealing the Install bit's spot. - */ - - if ( is_rsn && ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) ) { - rc = wpa_maybe_install_gtk ( ctx, - ( union ieee80211_ie * ) pkt->data, - pkt->data + pkt->datalen, - pkt->rsc ); - if ( rc < 0 ) { - DBGC ( ctx, "WPA %p: failed to install GTK in 1/2: " - "%s\n", ctx, strerror ( rc ) ); - return wpa_fail ( ctx, rc ); - } - } else { - rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data, - &pkt->datalen ); - if ( rc < 0 ) { - DBGC ( ctx, "WPA %p: failed to decrypt GTK: %s\n", - ctx, strerror ( rc ) ); - return rc; /* non-fatal */ - } - if ( pkt->datalen > sizeof ( ctx->gtk.tk ) ) { - DBGC ( ctx, "WPA %p: too much GTK data (%d > %zd)\n", - ctx, pkt->datalen, sizeof ( ctx->gtk.tk ) ); - return wpa_fail ( ctx, -EINVAL ); - } - - memcpy ( &ctx->gtk.tk, pkt->data, pkt->datalen ); - wpa_install_gtk ( ctx, pkt->datalen, pkt->rsc ); - } - - DBGC ( ctx, "WPA %p: received 1/2, looks OK\n", ctx ); - - return wpa_send_final ( ctx, pkt, is_rsn, kie ); -} - - -/** - * Handle receipt of EAPOL-Key frame for WPA - * - * @v iob I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Source link-layer address - */ -static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev, - const void *ll_dest __unused, - const void *ll_source ) -{ - struct net80211_device *dev = net80211_get ( netdev ); - struct eapol_key_pkt *pkt = iob->data; - int is_rsn, found_ctx; - struct wpa_common_ctx *ctx; - int rc = 0; - struct wpa_kie *kie; - u8 their_mic[16], our_mic[16]; - - if ( pkt->type != EAPOL_KEY_TYPE_WPA && - pkt->type != EAPOL_KEY_TYPE_RSN ) { - DBG ( "EAPOL-Key: packet not of 802.11 type\n" ); - rc = -EINVAL; - goto drop; - } - - is_rsn = ( pkt->type == EAPOL_KEY_TYPE_RSN ); - - if ( ! dev ) { - DBG ( "EAPOL-Key: packet not from 802.11\n" ); - rc = -EINVAL; - goto drop; - } - - if ( memcmp ( dev->bssid, ll_source, ETH_ALEN ) != 0 ) { - DBG ( "EAPOL-Key: packet not from associated AP\n" ); - rc = -EINVAL; - goto drop; - } - - if ( ! ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_ACK ) ) { - DBG ( "EAPOL-Key: packet sent in wrong direction\n" ); - rc = -EINVAL; - goto drop; - } - - found_ctx = 0; - list_for_each_entry ( ctx, &wpa_contexts, list ) { - if ( ctx->dev == dev ) { - found_ctx = 1; - break; - } - } - - if ( ! found_ctx ) { - DBG ( "EAPOL-Key: no WPA context to handle packet for %p\n", - dev ); - rc = -ENOENT; - goto drop; - } - - if ( ( void * ) ( pkt + 1 ) + ntohs ( pkt->datalen ) > iob->tail ) { - DBGC ( ctx, "WPA %p: packet truncated (has %zd extra bytes, " - "states %d)\n", ctx, iob->tail - ( void * ) ( pkt + 1 ), - ntohs ( pkt->datalen ) ); - rc = -EINVAL; - goto drop; - } - - /* Get a handle on key integrity/encryption handler */ - kie = wpa_find_kie ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION ); - if ( ! kie ) { - DBGC ( ctx, "WPA %p: no support for packet version %d\n", ctx, - ntohs ( pkt->info ) & EAPOL_KEY_INFO_VERSION ); - rc = wpa_fail ( ctx, -ENOTSUP ); - goto drop; - } - - /* Check MIC */ - if ( ntohs ( pkt->info ) & EAPOL_KEY_INFO_KEY_MIC ) { - memcpy ( their_mic, pkt->mic, sizeof ( pkt->mic ) ); - memset ( pkt->mic, 0, sizeof ( pkt->mic ) ); - kie->mic ( &ctx->ptk.kck, ( void * ) pkt - EAPOL_HDR_LEN, - EAPOL_HDR_LEN + sizeof ( *pkt ) + - ntohs ( pkt->datalen ), our_mic ); - DBGC2 ( ctx, "WPA %p MIC comparison (theirs, ours):\n", ctx ); - DBGC2_HD ( ctx, their_mic, 16 ); - DBGC2_HD ( ctx, our_mic, 16 ); - if ( memcmp ( their_mic, our_mic, sizeof ( pkt->mic ) ) != 0 ) { - DBGC ( ctx, "WPA %p: EAPOL MIC failure\n", ctx ); - goto drop; - } - } - - /* Fix byte order to local */ - pkt->info = ntohs ( pkt->info ); - pkt->keysize = ntohs ( pkt->keysize ); - pkt->datalen = ntohs ( pkt->datalen ); - pkt->replay = be64_to_cpu ( pkt->replay ); - - /* Check replay counter */ - if ( ctx->replay != ~0ULL && ctx->replay >= pkt->replay ) { - DBGC ( ctx, "WPA %p ALERT: Replay detected! " - "(%08x:%08x >= %08x:%08x)\n", ctx, - ( u32 ) ( ctx->replay >> 32 ), ( u32 ) ctx->replay, - ( u32 ) ( pkt->replay >> 32 ), ( u32 ) pkt->replay ); - rc = 0; /* ignore without error */ - goto drop; - } - ctx->replay = pkt->replay; - - /* Decrypt key data */ - if ( pkt->info & EAPOL_KEY_INFO_KEY_ENC ) { - rc = kie->decrypt ( &ctx->ptk.kek, pkt->iv, pkt->data, - &pkt->datalen ); - if ( rc < 0 ) { - DBGC ( ctx, "WPA %p: failed to decrypt packet: %s\n", - ctx, strerror ( rc ) ); - goto drop; - } - } - - /* Hand it off to appropriate handler */ - switch ( pkt->info & ( EAPOL_KEY_INFO_TYPE | - EAPOL_KEY_INFO_KEY_MIC ) ) { - case EAPOL_KEY_TYPE_PTK: - rc = wpa_handle_1_of_4 ( ctx, pkt, is_rsn, kie ); - break; - - case EAPOL_KEY_TYPE_PTK | EAPOL_KEY_INFO_KEY_MIC: - rc = wpa_handle_3_of_4 ( ctx, pkt, is_rsn, kie ); - break; - - case EAPOL_KEY_TYPE_GTK | EAPOL_KEY_INFO_KEY_MIC: - rc = wpa_handle_1_of_2 ( ctx, pkt, is_rsn, kie ); - break; - - default: - DBGC ( ctx, "WPA %p: Invalid combination of key flags %04x\n", - ctx, pkt->info ); - rc = -EINVAL; - break; - } - - drop: - free_iob ( iob ); - return rc; -} - -struct eapol_handler eapol_key_handler __eapol_handler = { - .type = EAPOL_TYPE_KEY, - .rx = eapol_key_rx, -}; - -/* WPA always needs EAPOL in order to be useful */ -REQUIRING_SYMBOL ( eapol_key_handler ); -REQUIRE_OBJECT ( eapol ); diff --git a/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c b/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c deleted file mode 100644 index a073c6a3c..000000000 --- a/qemu/roms/ipxe/src/net/80211/wpa_ccmp.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <string.h> -#include <ipxe/net80211.h> -#include <ipxe/crypto.h> -#include <ipxe/hmac.h> -#include <ipxe/sha1.h> -#include <ipxe/aes.h> -#include <ipxe/wpa.h> -#include <byteswap.h> -#include <errno.h> - -/** @file - * - * Backend for WPA using the CCMP encryption method - */ - -/** Context for CCMP encryption and decryption */ -struct ccmp_ctx -{ - /** AES context - only ever used for encryption */ - u8 aes_ctx[AES_CTX_SIZE]; - - /** Most recently sent packet number */ - u64 tx_seq; - - /** Most recently received packet number */ - u64 rx_seq; -}; - -/** Header structure at the beginning of CCMP frame data */ -struct ccmp_head -{ - u8 pn_lo[2]; /**< Bytes 0 and 1 of packet number */ - u8 _rsvd; /**< Reserved byte */ - u8 kid; /**< Key ID and ExtIV byte */ - u8 pn_hi[4]; /**< Bytes 2-5 (2 first) of packet number */ -} __attribute__ (( packed )); - - -/** CCMP header overhead */ -#define CCMP_HEAD_LEN 8 - -/** CCMP MIC trailer overhead */ -#define CCMP_MIC_LEN 8 - -/** CCMP nonce length */ -#define CCMP_NONCE_LEN 13 - -/** CCMP nonce structure */ -struct ccmp_nonce -{ - u8 prio; /**< Packet priority, 0 for non-QoS */ - u8 a2[ETH_ALEN]; /**< Address 2 from packet header (sender) */ - u8 pn[6]; /**< Packet number */ -} __attribute__ (( packed )); - -/** CCMP additional authentication data length (for non-QoS, non-WDS frames) */ -#define CCMP_AAD_LEN 22 - -/** CCMP additional authentication data structure */ -struct ccmp_aad -{ - u16 fc; /**< Frame Control field */ - u8 a1[6]; /**< Address 1 */ - u8 a2[6]; /**< Address 2 */ - u8 a3[6]; /**< Address 3 */ - u16 seq; /**< Sequence Control field */ - /* Address 4 and QoS Control are included if present */ -} __attribute__ (( packed )); - -/** Mask for Frame Control field in AAD */ -#define CCMP_AAD_FC_MASK 0xC38F - -/** Mask for Sequence Control field in AAD */ -#define CCMP_AAD_SEQ_MASK 0x000F - - -/** - * Convert 6-byte LSB packet number to 64-bit integer - * - * @v pn Pointer to 6-byte packet number - * @ret v 64-bit integer value of @a pn - */ -static u64 pn_to_u64 ( const u8 *pn ) -{ - int i; - u64 ret = 0; - - for ( i = 5; i >= 0; i-- ) { - ret <<= 8; - ret |= pn[i]; - } - - return ret; -} - -/** - * Convert 64-bit integer to 6-byte packet number - * - * @v v 64-bit integer - * @v msb If TRUE, reverse the output PN to be in MSB order - * @ret pn 6-byte packet number - * - * The PN is stored in LSB order in the packet header and in MSB order - * in the nonce. WHYYYYY? - */ -static void u64_to_pn ( u64 v, u8 *pn, int msb ) -{ - int i; - u8 *pnp = pn + ( msb ? 5 : 0 ); - int delta = ( msb ? -1 : +1 ); - - for ( i = 0; i < 6; i++ ) { - *pnp = v & 0xFF; - pnp += delta; - v >>= 8; - } -} - -/** Value for @a msb argument of u64_to_pn() for MSB output */ -#define PN_MSB 1 - -/** Value for @a msb argument of u64_to_pn() for LSB output */ -#define PN_LSB 0 - - - -/** - * Initialise CCMP state and install key - * - * @v crypto CCMP cryptosystem structure - * @v key Pointer to 16-byte temporal key to install - * @v keylen Length of key (16 bytes) - * @v rsc Initial receive sequence counter - */ -static int ccmp_init ( struct net80211_crypto *crypto, const void *key, - int keylen, const void *rsc ) -{ - struct ccmp_ctx *ctx = crypto->priv; - - if ( keylen != 16 ) - return -EINVAL; - - if ( rsc ) - ctx->rx_seq = pn_to_u64 ( rsc ); - - cipher_setkey ( &aes_algorithm, ctx->aes_ctx, key, keylen ); - - return 0; -} - - -/** - * Encrypt or decrypt data stream using AES in Counter mode - * - * @v ctx CCMP cryptosystem context - * @v nonce Nonce value, 13 bytes - * @v srcv Data to encrypt or decrypt - * @v len Number of bytes pointed to by @a src - * @v msrcv MIC value to encrypt or decrypt (may be NULL) - * @ret destv Encrypted or decrypted data - * @ret mdestv Encrypted or decrypted MIC value - * - * This assumes CCMP parameters of L=2 and M=8. The algorithm is - * defined in RFC 3610. - */ -static void ccmp_ctr_xor ( struct ccmp_ctx *ctx, const void *nonce, - const void *srcv, void *destv, int len, - const void *msrcv, void *mdestv ) -{ - u8 A[16], S[16]; - u16 ctr; - int i; - const u8 *src = srcv, *msrc = msrcv; - u8 *dest = destv, *mdest = mdestv; - - A[0] = 0x01; /* flags, L' = L - 1 = 1, other bits rsvd */ - memcpy ( A + 1, nonce, CCMP_NONCE_LEN ); - - if ( msrcv ) { - A[14] = A[15] = 0; - - cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 ); - - for ( i = 0; i < 8; i++ ) { - *mdest++ = *msrc++ ^ S[i]; - } - } - - for ( ctr = 1 ;; ctr++ ) { - A[14] = ctr >> 8; - A[15] = ctr & 0xFF; - - cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, A, S, 16 ); - - for ( i = 0; i < len && i < 16; i++ ) - *dest++ = *src++ ^ S[i]; - - if ( len <= 16 ) - break; /* we're done */ - - len -= 16; - } -} - - -/** - * Advance one block in CBC-MAC calculation - * - * @v aes_ctx AES encryption context with key set - * @v B Cleartext block to incorporate (16 bytes) - * @v X Previous ciphertext block (16 bytes) - * @ret B Clobbered - * @ret X New ciphertext block (16 bytes) - * - * This function does X := E[key] ( X ^ B ). - */ -static void ccmp_feed_cbc_mac ( void *aes_ctx, u8 *B, u8 *X ) -{ - int i; - for ( i = 0; i < 16; i++ ) - B[i] ^= X[i]; - cipher_encrypt ( &aes_algorithm, aes_ctx, B, X, 16 ); -} - - -/** - * Calculate MIC on plaintext data using CBC-MAC - * - * @v ctx CCMP cryptosystem context - * @v nonce Nonce value, 13 bytes - * @v data Data to calculate MIC over - * @v datalen Length of @a data - * @v aad Additional authentication data, for MIC but not encryption - * @ret mic MIC value (unencrypted), 8 bytes - * - * @a aadlen is assumed to be 22 bytes long, as it always is for - * 802.11 use when transmitting non-QoS, not-between-APs frames (the - * only type we deal with). - */ -static void ccmp_cbc_mac ( struct ccmp_ctx *ctx, const void *nonce, - const void *data, u16 datalen, - const void *aad, void *mic ) -{ - u8 X[16], B[16]; - - /* Zeroth block: flags, nonce, length */ - - /* Rsv AAD - M'- - L'- - * 0 1 0 1 1 0 0 1 for an 8-byte MAC and 2-byte message length - */ - B[0] = 0x59; - memcpy ( B + 1, nonce, CCMP_NONCE_LEN ); - B[14] = datalen >> 8; - B[15] = datalen & 0xFF; - - cipher_encrypt ( &aes_algorithm, ctx->aes_ctx, B, X, 16 ); - - /* First block: AAD length field and 14 bytes of AAD */ - B[0] = 0; - B[1] = CCMP_AAD_LEN; - memcpy ( B + 2, aad, 14 ); - - ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X ); - - /* Second block: Remaining 8 bytes of AAD, 8 bytes zero pad */ - memcpy ( B, aad + 14, 8 ); - memset ( B + 8, 0, 8 ); - - ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X ); - - /* Message blocks */ - while ( datalen ) { - if ( datalen >= 16 ) { - memcpy ( B, data, 16 ); - datalen -= 16; - } else { - memcpy ( B, data, datalen ); - memset ( B + datalen, 0, 16 - datalen ); - datalen = 0; - } - - ccmp_feed_cbc_mac ( ctx->aes_ctx, B, X ); - - data += 16; - } - - /* Get MIC from final value of X */ - memcpy ( mic, X, 8 ); -} - - -/** - * Encapsulate and encrypt a packet using CCMP - * - * @v crypto CCMP cryptosystem - * @v iob I/O buffer containing cleartext packet - * @ret eiob I/O buffer containing encrypted packet - */ -struct io_buffer * ccmp_encrypt ( struct net80211_crypto *crypto, - struct io_buffer *iob ) -{ - struct ccmp_ctx *ctx = crypto->priv; - struct ieee80211_frame *hdr = iob->data; - struct io_buffer *eiob; - const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; - int datalen = iob_len ( iob ) - hdrlen; - struct ccmp_head head; - struct ccmp_nonce nonce; - struct ccmp_aad aad; - u8 mic[8], tx_pn[6]; - void *edata, *emic; - - ctx->tx_seq++; - u64_to_pn ( ctx->tx_seq, tx_pn, PN_LSB ); - - /* Allocate memory */ - eiob = alloc_iob ( iob_len ( iob ) + CCMP_HEAD_LEN + CCMP_MIC_LEN ); - if ( ! eiob ) - return NULL; - - /* Copy frame header */ - memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); - hdr = eiob->data; - hdr->fc |= IEEE80211_FC_PROTECTED; - - /* Fill in packet number and extended IV */ - memcpy ( head.pn_lo, tx_pn, 2 ); - memcpy ( head.pn_hi, tx_pn + 2, 4 ); - head.kid = 0x20; /* have Extended IV, key ID 0 */ - head._rsvd = 0; - memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) ); - - /* Form nonce */ - nonce.prio = 0; - memcpy ( nonce.a2, hdr->addr2, ETH_ALEN ); - u64_to_pn ( ctx->tx_seq, nonce.pn, PN_MSB ); - - /* Form additional authentication data */ - aad.fc = hdr->fc & CCMP_AAD_FC_MASK; - memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */ - aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK; - - /* Calculate MIC over the data */ - ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, mic ); - - /* Copy and encrypt data and MIC */ - edata = iob_put ( eiob, datalen ); - emic = iob_put ( eiob, CCMP_MIC_LEN ); - ccmp_ctr_xor ( ctx, &nonce, - iob->data + hdrlen, edata, datalen, - mic, emic ); - - /* Done! */ - DBGC2 ( ctx, "WPA-CCMP %p: encrypted packet %p -> %p\n", ctx, - iob, eiob ); - - return eiob; -} - -/** - * Decrypt a packet using CCMP - * - * @v crypto CCMP cryptosystem - * @v eiob I/O buffer containing encrypted packet - * @ret iob I/O buffer containing cleartext packet - */ -static struct io_buffer * ccmp_decrypt ( struct net80211_crypto *crypto, - struct io_buffer *eiob ) -{ - struct ccmp_ctx *ctx = crypto->priv; - struct ieee80211_frame *hdr; - struct io_buffer *iob; - const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; - int datalen = iob_len ( eiob ) - hdrlen - CCMP_HEAD_LEN - CCMP_MIC_LEN; - struct ccmp_head *head; - struct ccmp_nonce nonce; - struct ccmp_aad aad; - u8 rx_pn[6], their_mic[8], our_mic[8]; - - iob = alloc_iob ( hdrlen + datalen ); - if ( ! iob ) - return NULL; - - /* Copy frame header */ - memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen ); - hdr = iob->data; - hdr->fc &= ~IEEE80211_FC_PROTECTED; - - /* Check and update RX packet number */ - head = eiob->data + hdrlen; - memcpy ( rx_pn, head->pn_lo, 2 ); - memcpy ( rx_pn + 2, head->pn_hi, 4 ); - - if ( pn_to_u64 ( rx_pn ) <= ctx->rx_seq ) { - DBGC ( ctx, "WPA-CCMP %p: packet received out of order " - "(%012llx <= %012llx)\n", ctx, pn_to_u64 ( rx_pn ), - ctx->rx_seq ); - free_iob ( iob ); - return NULL; - } - - ctx->rx_seq = pn_to_u64 ( rx_pn ); - DBGC2 ( ctx, "WPA-CCMP %p: RX packet number %012llx\n", ctx, ctx->rx_seq ); - - /* Form nonce */ - nonce.prio = 0; - memcpy ( nonce.a2, hdr->addr2, ETH_ALEN ); - u64_to_pn ( ctx->rx_seq, nonce.pn, PN_MSB ); - - /* Form additional authentication data */ - aad.fc = ( hdr->fc & CCMP_AAD_FC_MASK ) | IEEE80211_FC_PROTECTED; - memcpy ( aad.a1, hdr->addr1, 3 * ETH_ALEN ); /* all 3 at once */ - aad.seq = hdr->seq & CCMP_AAD_SEQ_MASK; - - /* Copy-decrypt data and MIC */ - ccmp_ctr_xor ( ctx, &nonce, eiob->data + hdrlen + sizeof ( *head ), - iob_put ( iob, datalen ), datalen, - eiob->tail - CCMP_MIC_LEN, their_mic ); - - /* Check MIC */ - ccmp_cbc_mac ( ctx, &nonce, iob->data + hdrlen, datalen, &aad, - our_mic ); - - if ( memcmp ( their_mic, our_mic, CCMP_MIC_LEN ) != 0 ) { - DBGC2 ( ctx, "WPA-CCMP %p: MIC failure\n", ctx ); - free_iob ( iob ); - return NULL; - } - - DBGC2 ( ctx, "WPA-CCMP %p: decrypted packet %p -> %p\n", ctx, - eiob, iob ); - - return iob; -} - - -/** CCMP cryptosystem */ -struct net80211_crypto ccmp_crypto __net80211_crypto = { - .algorithm = NET80211_CRYPT_CCMP, - .init = ccmp_init, - .encrypt = ccmp_encrypt, - .decrypt = ccmp_decrypt, - .priv_len = sizeof ( struct ccmp_ctx ), -}; - - - - -/** - * Calculate HMAC-SHA1 MIC for EAPOL-Key frame - * - * @v kck Key Confirmation Key, 16 bytes - * @v msg Message to calculate MIC over - * @v len Number of bytes to calculate MIC over - * @ret mic Calculated MIC, 16 bytes long - */ -static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len, - void *mic ) -{ - u8 sha1_ctx[SHA1_CTX_SIZE]; - u8 kckb[16]; - u8 hash[SHA1_DIGEST_SIZE]; - size_t kck_len = 16; - - memcpy ( kckb, kck, kck_len ); - - hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len ); - hmac_update ( &sha1_algorithm, sha1_ctx, msg, len ); - hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash ); - - memcpy ( mic, hash, 16 ); -} - -/** - * Decrypt key data in EAPOL-Key frame - * - * @v kek Key Encryption Key, 16 bytes - * @v iv Initialisation vector, 16 bytes (unused) - * @v msg Message to decrypt - * @v len Length of message - * @ret msg Decrypted message in place of original - * @ret len Adjusted downward for 8 bytes of overhead - * @ret rc Return status code - * - * The returned message may still contain padding of 0xDD followed by - * zero or more 0x00 octets. It is impossible to remove the padding - * without parsing the IEs in the packet (another design decision that - * tends to make one question the 802.11i committee's intelligence...) - */ -static int ccmp_kie_decrypt ( const void *kek, const void *iv __unused, - void *msg, u16 *len ) -{ - if ( *len % 8 != 0 ) - return -EINVAL; - - if ( aes_unwrap ( kek, msg, msg, *len / 8 - 1 ) != 0 ) - return -EINVAL; - - *len -= 8; - - return 0; -} - -/** CCMP-style key integrity and encryption handler */ -struct wpa_kie ccmp_kie __wpa_kie = { - .version = EAPOL_KEY_VERSION_WPA2, - .mic = ccmp_kie_mic, - .decrypt = ccmp_kie_decrypt, -}; diff --git a/qemu/roms/ipxe/src/net/80211/wpa_psk.c b/qemu/roms/ipxe/src/net/80211/wpa_psk.c deleted file mode 100644 index 71190b139..000000000 --- a/qemu/roms/ipxe/src/net/80211/wpa_psk.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <string.h> -#include <ipxe/net80211.h> -#include <ipxe/sha1.h> -#include <ipxe/wpa.h> -#include <errno.h> - -/** @file - * - * Frontend for WPA using a pre-shared key. - */ - -/** - * Initialise WPA-PSK state - * - * @v dev 802.11 device - * @ret rc Return status code - */ -static int wpa_psk_init ( struct net80211_device *dev ) -{ - return wpa_make_rsn_ie ( dev, &dev->rsn_ie ); -} - -/** - * Start WPA-PSK authentication - * - * @v dev 802.11 device - * @ret rc Return status code - */ -static int wpa_psk_start ( struct net80211_device *dev ) -{ - char passphrase[64+1]; - u8 pmk[WPA_PMK_LEN]; - int len; - struct wpa_common_ctx *ctx = dev->handshaker->priv; - - len = fetch_string_setting ( netdev_settings ( dev->netdev ), - &net80211_key_setting, passphrase, - 64 + 1 ); - - if ( len <= 0 ) { - DBGC ( ctx, "WPA-PSK %p: no passphrase provided!\n", ctx ); - net80211_deauthenticate ( dev, -EACCES ); - return -EACCES; - } - - pbkdf2_sha1 ( passphrase, len, dev->essid, strlen ( dev->essid ), - 4096, pmk, WPA_PMK_LEN ); - - DBGC ( ctx, "WPA-PSK %p: derived PMK from passphrase `%s':\n", ctx, - passphrase ); - DBGC_HD ( ctx, pmk, WPA_PMK_LEN ); - - return wpa_start ( dev, ctx, pmk, WPA_PMK_LEN ); -} - -/** - * Step WPA-PSK authentication - * - * @v dev 802.11 device - * @ret rc Return status code - */ -static int wpa_psk_step ( struct net80211_device *dev ) -{ - struct wpa_common_ctx *ctx = dev->handshaker->priv; - - switch ( ctx->state ) { - case WPA_SUCCESS: - return 1; - case WPA_FAILURE: - return -EACCES; - default: - return 0; - } -} - -/** - * Do-nothing function; you can't change a WPA key post-authentication - * - * @v dev 802.11 device - * @ret rc Return status code - */ -static int wpa_psk_no_change_key ( struct net80211_device *dev __unused ) -{ - return 0; -} - -/** - * Disable handling of received WPA authentication frames - * - * @v dev 802.11 device - */ -static void wpa_psk_stop ( struct net80211_device *dev ) -{ - wpa_stop ( dev ); -} - -/** WPA-PSK security handshaker */ -struct net80211_handshaker wpa_psk_handshaker __net80211_handshaker = { - .protocol = NET80211_SECPROT_PSK, - .init = wpa_psk_init, - .start = wpa_psk_start, - .step = wpa_psk_step, - .change_key = wpa_psk_no_change_key, - .stop = wpa_psk_stop, - .priv_len = sizeof ( struct wpa_common_ctx ), -}; diff --git a/qemu/roms/ipxe/src/net/80211/wpa_tkip.c b/qemu/roms/ipxe/src/net/80211/wpa_tkip.c deleted file mode 100644 index 3b1934b59..000000000 --- a/qemu/roms/ipxe/src/net/80211/wpa_tkip.c +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <string.h> -#include <ipxe/net80211.h> -#include <ipxe/crypto.h> -#include <ipxe/hmac.h> -#include <ipxe/sha1.h> -#include <ipxe/md5.h> -#include <ipxe/crc32.h> -#include <ipxe/arc4.h> -#include <ipxe/wpa.h> -#include <byteswap.h> -#include <errno.h> - -/** @file - * - * Backend for WPA using the TKIP encryption standard. - */ - -/** Context for one direction of TKIP, either encryption or decryption */ -struct tkip_dir_ctx -{ - /** High 32 bits of last sequence counter value used */ - u32 tsc_hi; - - /** Low 32 bits of last sequence counter value used */ - u16 tsc_lo; - - /** MAC address used to derive TTAK */ - u8 mac[ETH_ALEN]; - - /** If TRUE, TTAK is valid */ - u16 ttak_ok; - - /** TKIP-mixed transmit address and key, depends on tsc_hi and MAC */ - u16 ttak[5]; -}; - -/** Context for TKIP encryption and decryption */ -struct tkip_ctx -{ - /** Temporal key to use */ - struct tkip_tk tk; - - /** State for encryption */ - struct tkip_dir_ctx enc; - - /** State for decryption */ - struct tkip_dir_ctx dec; -}; - -/** Header structure at the beginning of TKIP frame data */ -struct tkip_head -{ - u8 tsc1; /**< High byte of low 16 bits of TSC */ - u8 seed1; /**< Second byte of WEP seed */ - u8 tsc0; /**< Low byte of TSC */ - u8 kid; /**< Key ID and ExtIV byte */ - u32 tsc_hi; /**< High 32 bits of TSC, as an ExtIV */ -} __attribute__ (( packed )); - - -/** TKIP header overhead (IV + KID + ExtIV) */ -#define TKIP_HEAD_LEN 8 - -/** TKIP trailer overhead (MIC + ICV) [assumes unfragmented] */ -#define TKIP_FOOT_LEN 12 - -/** TKIP MIC length */ -#define TKIP_MIC_LEN 8 - -/** TKIP ICV length */ -#define TKIP_ICV_LEN 4 - - -/** TKIP S-box */ -static const u16 Sbox[256] = { - 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, - 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, - 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, - 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, - 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, - 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, - 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, - 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, - 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, - 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, - 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, - 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, - 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, - 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, - 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, - 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, - 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, - 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, - 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, - 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, - 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, - 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, - 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, - 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, - 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, - 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, - 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, - 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, - 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, - 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, - 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, - 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, -}; - -/** - * Perform S-box mapping on a 16-bit value - * - * @v v Value to perform S-box mapping on - * @ret Sv S-box mapped value - */ -static inline u16 S ( u16 v ) -{ - return Sbox[v & 0xFF] ^ bswap_16 ( Sbox[v >> 8] ); -} - -/** - * Rotate 16-bit value right - * - * @v v Value to rotate - * @v bits Number of bits to rotate by - * @ret rotv Rotated value - */ -static inline u16 ror16 ( u16 v, int bits ) -{ - return ( v >> bits ) | ( v << ( 16 - bits ) ); -} - -/** - * Rotate 32-bit value right - * - * @v v Value to rotate - * @v bits Number of bits to rotate by - * @ret rotv Rotated value - */ -static inline u32 ror32 ( u32 v, int bits ) -{ - return ( v >> bits ) | ( v << ( 32 - bits ) ); -} - -/** - * Rotate 32-bit value left - * - * @v v Value to rotate - * @v bits Number of bits to rotate by - * @ret rotv Rotated value - */ -static inline u32 rol32 ( u32 v, int bits ) -{ - return ( v << bits ) | ( v >> ( 32 - bits ) ); -} - - -/** - * Initialise TKIP state and install key - * - * @v crypto TKIP cryptosystem structure - * @v key Pointer to tkip_tk to install - * @v keylen Length of key (32 bytes) - * @v rsc Initial receive sequence counter - */ -static int tkip_init ( struct net80211_crypto *crypto, const void *key, - int keylen, const void *rsc ) -{ - struct tkip_ctx *ctx = crypto->priv; - const u8 *rscb = rsc; - - if ( keylen != sizeof ( ctx->tk ) ) - return -EINVAL; - - if ( rscb ) { - ctx->dec.tsc_lo = ( rscb[1] << 8 ) | rscb[0]; - ctx->dec.tsc_hi = ( ( rscb[5] << 24 ) | ( rscb[4] << 16 ) | - ( rscb[3] << 8 ) | rscb[2] ); - } - - memcpy ( &ctx->tk, key, sizeof ( ctx->tk ) ); - - return 0; -} - -/** - * Perform TKIP key mixing, phase 1 - * - * @v dctx TKIP directional context - * @v tk TKIP temporal key - * @v mac MAC address of transmitter - * - * This recomputes the TTAK in @a dctx if necessary, and sets - * @c dctx->ttak_ok. - */ -static void tkip_mix_1 ( struct tkip_dir_ctx *dctx, struct tkip_tk *tk, u8 *mac ) -{ - int i, j; - - if ( dctx->ttak_ok && ! memcmp ( mac, dctx->mac, ETH_ALEN ) ) - return; - - memcpy ( dctx->mac, mac, ETH_ALEN ); - - dctx->ttak[0] = dctx->tsc_hi & 0xFFFF; - dctx->ttak[1] = dctx->tsc_hi >> 16; - dctx->ttak[2] = ( mac[1] << 8 ) | mac[0]; - dctx->ttak[3] = ( mac[3] << 8 ) | mac[2]; - dctx->ttak[4] = ( mac[5] << 8 ) | mac[4]; - - for ( i = 0; i < 8; i++ ) { - j = 2 * ( i & 1 ); - - dctx->ttak[0] += S ( dctx->ttak[4] ^ ( ( tk->key[1 + j] << 8 ) | - tk->key[0 + j] ) ); - dctx->ttak[1] += S ( dctx->ttak[0] ^ ( ( tk->key[5 + j] << 8 ) | - tk->key[4 + j] ) ); - dctx->ttak[2] += S ( dctx->ttak[1] ^ ( ( tk->key[9 + j] << 8 ) | - tk->key[8 + j] ) ); - dctx->ttak[3] += S ( dctx->ttak[2] ^ ( ( tk->key[13+ j] << 8 ) | - tk->key[12+ j] ) ); - dctx->ttak[4] += S ( dctx->ttak[3] ^ ( ( tk->key[1 + j] << 8 ) | - tk->key[0 + j] ) ) + i; - } - - dctx->ttak_ok = 1; -} - -/** - * Perform TKIP key mixing, phase 2 - * - * @v dctx TKIP directional context - * @v tk TKIP temporal key - * @ret key ARC4 key, 16 bytes long - */ -static void tkip_mix_2 ( struct tkip_dir_ctx *dctx, struct tkip_tk *tk, - void *key ) -{ - u8 *kb = key; - u16 ppk[6]; - int i; - - memcpy ( ppk, dctx->ttak, sizeof ( dctx->ttak ) ); - ppk[5] = dctx->ttak[4] + dctx->tsc_lo; - - ppk[0] += S ( ppk[5] ^ ( ( tk->key[1] << 8 ) | tk->key[0] ) ); - ppk[1] += S ( ppk[0] ^ ( ( tk->key[3] << 8 ) | tk->key[2] ) ); - ppk[2] += S ( ppk[1] ^ ( ( tk->key[5] << 8 ) | tk->key[4] ) ); - ppk[3] += S ( ppk[2] ^ ( ( tk->key[7] << 8 ) | tk->key[6] ) ); - ppk[4] += S ( ppk[3] ^ ( ( tk->key[9] << 8 ) | tk->key[8] ) ); - ppk[5] += S ( ppk[4] ^ ( ( tk->key[11] << 8 ) | tk->key[10] ) ); - - ppk[0] += ror16 ( ppk[5] ^ ( ( tk->key[13] << 8 ) | tk->key[12] ), 1 ); - ppk[1] += ror16 ( ppk[0] ^ ( ( tk->key[15] << 8 ) | tk->key[14] ), 1 ); - ppk[2] += ror16 ( ppk[1], 1 ); - ppk[3] += ror16 ( ppk[2], 1 ); - ppk[4] += ror16 ( ppk[3], 1 ); - ppk[5] += ror16 ( ppk[4], 1 ); - - kb[0] = dctx->tsc_lo >> 8; - kb[1] = ( ( dctx->tsc_lo >> 8 ) | 0x20 ) & 0x7F; - kb[2] = dctx->tsc_lo & 0xFF; - kb[3] = ( ( ppk[5] ^ ( ( tk->key[1] << 8 ) | tk->key[0] ) ) >> 1 ) - & 0xFF; - - for ( i = 0; i < 6; i++ ) { - kb[4 + 2*i] = ppk[i] & 0xFF; - kb[5 + 2*i] = ppk[i] >> 8; - } -} - -/** - * Update Michael message integrity code based on next 32-bit word of data - * - * @v V Michael code state (two 32-bit words) - * @v word Next 32-bit word of data - */ -static void tkip_feed_michael ( u32 *V, u32 word ) -{ - V[0] ^= word; - V[1] ^= rol32 ( V[0], 17 ); - V[0] += V[1]; - V[1] ^= ( ( V[0] & 0xFF00FF00 ) >> 8 ) | ( ( V[0] & 0x00FF00FF ) << 8 ); - V[0] += V[1]; - V[1] ^= rol32 ( V[0], 3 ); - V[0] += V[1]; - V[1] ^= ror32 ( V[0], 2 ); - V[0] += V[1]; -} - -/** - * Calculate Michael message integrity code - * - * @v key MIC key to use (8 bytes) - * @v da Destination link-layer address - * @v sa Source link-layer address - * @v data Start of data to calculate over - * @v len Length of header + data - * @ret mic Calculated Michael MIC (8 bytes) - */ -static void tkip_michael ( const void *key, const void *da, const void *sa, - const void *data, size_t len, void *mic ) -{ - u32 V[2]; /* V[0] = "l", V[1] = "r" in 802.11 */ - union { - u8 byte[12]; - u32 word[3]; - } cap; - const u8 *ptr = data; - const u8 *end = ptr + len; - int i; - - memcpy ( V, key, sizeof ( V ) ); - V[0] = le32_to_cpu ( V[0] ); - V[1] = le32_to_cpu ( V[1] ); - - /* Feed in header (we assume non-QoS, so Priority = 0) */ - memcpy ( &cap.byte[0], da, ETH_ALEN ); - memcpy ( &cap.byte[6], sa, ETH_ALEN ); - tkip_feed_michael ( V, le32_to_cpu ( cap.word[0] ) ); - tkip_feed_michael ( V, le32_to_cpu ( cap.word[1] ) ); - tkip_feed_michael ( V, le32_to_cpu ( cap.word[2] ) ); - tkip_feed_michael ( V, 0 ); - - /* Feed in data */ - while ( ptr + 4 <= end ) { - tkip_feed_michael ( V, le32_to_cpu ( *( u32 * ) ptr ) ); - ptr += 4; - } - - /* Add unaligned part and padding */ - for ( i = 0; ptr < end; i++ ) - cap.byte[i] = *ptr++; - cap.byte[i++] = 0x5a; - for ( ; i < 8; i++ ) - cap.byte[i] = 0; - - /* Feed in padding */ - tkip_feed_michael ( V, le32_to_cpu ( cap.word[0] ) ); - tkip_feed_michael ( V, le32_to_cpu ( cap.word[1] ) ); - - /* Output MIC */ - V[0] = cpu_to_le32 ( V[0] ); - V[1] = cpu_to_le32 ( V[1] ); - memcpy ( mic, V, sizeof ( V ) ); -} - -/** - * Encrypt a packet using TKIP - * - * @v crypto TKIP cryptosystem - * @v iob I/O buffer containing cleartext packet - * @ret eiob I/O buffer containing encrypted packet - */ -static struct io_buffer * tkip_encrypt ( struct net80211_crypto *crypto, - struct io_buffer *iob ) -{ - struct tkip_ctx *ctx = crypto->priv; - struct ieee80211_frame *hdr = iob->data; - struct io_buffer *eiob; - struct arc4_ctx arc4; - u8 key[16]; - struct tkip_head head; - u8 mic[8]; - u32 icv; - const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; - int datalen = iob_len ( iob ) - hdrlen; - - ctx->enc.tsc_lo++; - if ( ctx->enc.tsc_lo == 0 ) { - ctx->enc.tsc_hi++; - ctx->enc.ttak_ok = 0; - } - - tkip_mix_1 ( &ctx->enc, &ctx->tk, hdr->addr2 ); - tkip_mix_2 ( &ctx->enc, &ctx->tk, key ); - - eiob = alloc_iob ( iob_len ( iob ) + TKIP_HEAD_LEN + TKIP_FOOT_LEN ); - if ( ! eiob ) - return NULL; - - /* Copy frame header */ - memcpy ( iob_put ( eiob, hdrlen ), iob->data, hdrlen ); - hdr = eiob->data; - hdr->fc |= IEEE80211_FC_PROTECTED; - - /* Fill in IV and key ID byte, and extended IV */ - memcpy ( &head, key, 3 ); - head.kid = 0x20; /* have Extended IV, key ID 0 */ - head.tsc_hi = cpu_to_le32 ( ctx->enc.tsc_hi ); - memcpy ( iob_put ( eiob, sizeof ( head ) ), &head, sizeof ( head ) ); - - /* Copy and encrypt the data */ - cipher_setkey ( &arc4_algorithm, &arc4, key, 16 ); - cipher_encrypt ( &arc4_algorithm, &arc4, iob->data + hdrlen, - iob_put ( eiob, datalen ), datalen ); - - /* Add MIC */ - hdr = iob->data; - tkip_michael ( &ctx->tk.mic.tx, hdr->addr3, hdr->addr2, - iob->data + hdrlen, datalen, mic ); - cipher_encrypt ( &arc4_algorithm, &arc4, mic, - iob_put ( eiob, sizeof ( mic ) ), sizeof ( mic ) ); - - /* Add ICV */ - icv = crc32_le ( ~0, iob->data + hdrlen, datalen ); - icv = crc32_le ( icv, mic, sizeof ( mic ) ); - icv = cpu_to_le32 ( ~icv ); - cipher_encrypt ( &arc4_algorithm, &arc4, &icv, - iob_put ( eiob, TKIP_ICV_LEN ), TKIP_ICV_LEN ); - - DBGC2 ( ctx, "WPA-TKIP %p: encrypted packet %p -> %p\n", ctx, - iob, eiob ); - - return eiob; -} - -/** - * Decrypt a packet using TKIP - * - * @v crypto TKIP cryptosystem - * @v eiob I/O buffer containing encrypted packet - * @ret iob I/O buffer containing cleartext packet - */ -static struct io_buffer * tkip_decrypt ( struct net80211_crypto *crypto, - struct io_buffer *eiob ) -{ - struct tkip_ctx *ctx = crypto->priv; - struct ieee80211_frame *hdr; - struct io_buffer *iob; - const int hdrlen = IEEE80211_TYP_FRAME_HEADER_LEN; - int datalen = iob_len ( eiob ) - hdrlen - TKIP_HEAD_LEN - TKIP_FOOT_LEN; - struct tkip_head *head; - struct arc4_ctx arc4; - u16 rx_tsc_lo; - u8 key[16]; - u8 mic[8]; - u32 icv, crc; - - iob = alloc_iob ( hdrlen + datalen + TKIP_FOOT_LEN ); - if ( ! iob ) - return NULL; - - /* Copy frame header */ - memcpy ( iob_put ( iob, hdrlen ), eiob->data, hdrlen ); - hdr = iob->data; - hdr->fc &= ~IEEE80211_FC_PROTECTED; - - /* Check and update TSC */ - head = eiob->data + hdrlen; - rx_tsc_lo = ( head->tsc1 << 8 ) | head->tsc0; - - if ( head->tsc_hi < ctx->dec.tsc_hi || - ( head->tsc_hi == ctx->dec.tsc_hi && - rx_tsc_lo <= ctx->dec.tsc_lo ) ) { - DBGC ( ctx, "WPA-TKIP %p: packet received out of order " - "(%08x:%04x <= %08x:%04x)\n", ctx, head->tsc_hi, - rx_tsc_lo, ctx->dec.tsc_hi, ctx->dec.tsc_lo ); - free_iob ( iob ); - return NULL; - } - ctx->dec.tsc_lo = rx_tsc_lo; - if ( ctx->dec.tsc_hi != head->tsc_hi ) { - ctx->dec.ttak_ok = 0; - ctx->dec.tsc_hi = head->tsc_hi; - } - - /* Calculate key */ - tkip_mix_1 ( &ctx->dec, &ctx->tk, hdr->addr2 ); - tkip_mix_2 ( &ctx->dec, &ctx->tk, key ); - - /* Copy-decrypt data, MIC, ICV */ - cipher_setkey ( &arc4_algorithm, &arc4, key, 16 ); - cipher_decrypt ( &arc4_algorithm, &arc4, - eiob->data + hdrlen + TKIP_HEAD_LEN, - iob_put ( iob, datalen ), datalen + TKIP_FOOT_LEN ); - - /* Check ICV */ - icv = le32_to_cpu ( *( u32 * ) ( iob->tail + TKIP_MIC_LEN ) ); - crc = ~crc32_le ( ~0, iob->data + hdrlen, datalen + TKIP_MIC_LEN ); - if ( crc != icv ) { - DBGC ( ctx, "WPA-TKIP %p CRC mismatch: expect %08x, get %08x\n", - ctx, icv, crc ); - free_iob ( iob ); - return NULL; - } - - /* Check MIC */ - tkip_michael ( &ctx->tk.mic.rx, hdr->addr1, hdr->addr3, - iob->data + hdrlen, datalen, mic ); - if ( memcmp ( mic, iob->tail, TKIP_MIC_LEN ) != 0 ) { - DBGC ( ctx, "WPA-TKIP %p ALERT! MIC failure\n", ctx ); - /* XXX we should do the countermeasures here */ - free_iob ( iob ); - return NULL; - } - - DBGC2 ( ctx, "WPA-TKIP %p: decrypted packet %p -> %p\n", ctx, - eiob, iob ); - - return iob; -} - -/** TKIP cryptosystem */ -struct net80211_crypto tkip_crypto __net80211_crypto = { - .algorithm = NET80211_CRYPT_TKIP, - .init = tkip_init, - .encrypt = tkip_encrypt, - .decrypt = tkip_decrypt, - .priv_len = sizeof ( struct tkip_ctx ), -}; - - - - -/** - * Calculate HMAC-MD5 MIC for EAPOL-Key frame - * - * @v kck Key Confirmation Key, 16 bytes - * @v msg Message to calculate MIC over - * @v len Number of bytes to calculate MIC over - * @ret mic Calculated MIC, 16 bytes long - */ -static void tkip_kie_mic ( const void *kck, const void *msg, size_t len, - void *mic ) -{ - uint8_t ctx[MD5_CTX_SIZE]; - u8 kckb[16]; - size_t kck_len = 16; - - memcpy ( kckb, kck, kck_len ); - - hmac_init ( &md5_algorithm, ctx, kckb, &kck_len ); - hmac_update ( &md5_algorithm, ctx, msg, len ); - hmac_final ( &md5_algorithm, ctx, kckb, &kck_len, mic ); -} - -/** - * Decrypt key data in EAPOL-Key frame - * - * @v kek Key Encryption Key, 16 bytes - * @v iv Initialisation vector, 16 bytes - * @v msg Message to decrypt - * @v len Length of message - * @ret msg Decrypted message in place of original - * @ret len Unchanged - * @ret rc Always 0 for success - */ -static int tkip_kie_decrypt ( const void *kek, const void *iv, - void *msg, u16 *len ) -{ - u8 key[32]; - memcpy ( key, iv, 16 ); - memcpy ( key + 16, kek, 16 ); - - arc4_skip ( key, 32, 256, msg, msg, *len ); - - return 0; -} - - -/** TKIP-style key integrity and encryption handler */ -struct wpa_kie tkip_kie __wpa_kie = { - .version = EAPOL_KEY_VERSION_WPA, - .mic = tkip_kie_mic, - .decrypt = tkip_kie_decrypt, -}; diff --git a/qemu/roms/ipxe/src/net/aoe.c b/qemu/roms/ipxe/src/net/aoe.c deleted file mode 100644 index 2da8655b4..000000000 --- a/qemu/roms/ipxe/src/net/aoe.c +++ /dev/null @@ -1,1061 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/list.h> -#include <ipxe/if_ether.h> -#include <ipxe/iobuf.h> -#include <ipxe/uaccess.h> -#include <ipxe/netdevice.h> -#include <ipxe/features.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/uri.h> -#include <ipxe/open.h> -#include <ipxe/ata.h> -#include <ipxe/device.h> -#include <ipxe/aoe.h> - -/** @file - * - * AoE protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 ); - -struct net_protocol aoe_protocol __net_protocol; - -/****************************************************************************** - * - * AoE devices and commands - * - ****************************************************************************** - */ - -/** List of all AoE devices */ -static LIST_HEAD ( aoe_devices ); - -/** List of active AoE commands */ -static LIST_HEAD ( aoe_commands ); - -/** An AoE device */ -struct aoe_device { - /** Reference counter */ - struct refcnt refcnt; - - /** Network device */ - struct net_device *netdev; - /** ATA command issuing interface */ - struct interface ata; - - /** Major number */ - uint16_t major; - /** Minor number */ - uint8_t minor; - /** Target MAC address */ - uint8_t target[MAX_LL_ADDR_LEN]; - - /** Saved timeout value */ - unsigned long timeout; - - /** Configuration command interface */ - struct interface config; - /** Device is configued */ - int configured; -}; - -/** An AoE command */ -struct aoe_command { - /** Reference count */ - struct refcnt refcnt; - /** AOE device */ - struct aoe_device *aoedev; - /** List of active commands */ - struct list_head list; - - /** ATA command interface */ - struct interface ata; - - /** ATA command */ - struct ata_cmd command; - /** Command type */ - struct aoe_command_type *type; - /** Command tag */ - uint32_t tag; - - /** Retransmission timer */ - struct retry_timer timer; -}; - -/** An AoE command type */ -struct aoe_command_type { - /** - * Calculate length of AoE command IU - * - * @v aoecmd AoE command - * @ret len Length of command IU - */ - size_t ( * cmd_len ) ( struct aoe_command *aoecmd ); - /** - * Build AoE command IU - * - * @v aoecmd AoE command - * @v data Command IU - * @v len Length of command IU - */ - void ( * cmd ) ( struct aoe_command *aoecmd, void *data, size_t len ); - /** - * Handle AoE response IU - * - * @v aoecmd AoE command - * @v data Response IU - * @v len Length of response IU - * @v ll_source Link-layer source address - * @ret rc Return status code - */ - int ( * rsp ) ( struct aoe_command *aoecmd, const void *data, - size_t len, const void *ll_source ); -}; - -/** - * Get reference to AoE device - * - * @v aoedev AoE device - * @ret aoedev AoE device - */ -static inline __attribute__ (( always_inline )) struct aoe_device * -aoedev_get ( struct aoe_device *aoedev ) { - ref_get ( &aoedev->refcnt ); - return aoedev; -} - -/** - * Drop reference to AoE device - * - * @v aoedev AoE device - */ -static inline __attribute__ (( always_inline )) void -aoedev_put ( struct aoe_device *aoedev ) { - ref_put ( &aoedev->refcnt ); -} - -/** - * Get reference to AoE command - * - * @v aoecmd AoE command - * @ret aoecmd AoE command - */ -static inline __attribute__ (( always_inline )) struct aoe_command * -aoecmd_get ( struct aoe_command *aoecmd ) { - ref_get ( &aoecmd->refcnt ); - return aoecmd; -} - -/** - * Drop reference to AoE command - * - * @v aoecmd AoE command - */ -static inline __attribute__ (( always_inline )) void -aoecmd_put ( struct aoe_command *aoecmd ) { - ref_put ( &aoecmd->refcnt ); -} - -/** - * Name AoE device - * - * @v aoedev AoE device - * @ret name AoE device name - */ -static const char * aoedev_name ( struct aoe_device *aoedev ) { - static char buf[16]; - - snprintf ( buf, sizeof ( buf ), "%s/e%d.%d", aoedev->netdev->name, - aoedev->major, aoedev->minor ); - return buf; -} - -/** - * Free AoE command - * - * @v refcnt Reference counter - */ -static void aoecmd_free ( struct refcnt *refcnt ) { - struct aoe_command *aoecmd = - container_of ( refcnt, struct aoe_command, refcnt ); - - assert ( ! timer_running ( &aoecmd->timer ) ); - assert ( list_empty ( &aoecmd->list ) ); - - aoedev_put ( aoecmd->aoedev ); - free ( aoecmd ); -} - -/** - * Close AoE command - * - * @v aoecmd AoE command - * @v rc Reason for close - */ -static void aoecmd_close ( struct aoe_command *aoecmd, int rc ) { - struct aoe_device *aoedev = aoecmd->aoedev; - - /* Stop timer */ - stop_timer ( &aoecmd->timer ); - - /* Preserve the timeout value for subsequent commands */ - aoedev->timeout = aoecmd->timer.timeout; - - /* Remove from list of commands */ - if ( ! list_empty ( &aoecmd->list ) ) { - list_del ( &aoecmd->list ); - INIT_LIST_HEAD ( &aoecmd->list ); - aoecmd_put ( aoecmd ); - } - - /* Shut down interfaces */ - intf_shutdown ( &aoecmd->ata, rc ); -} - -/** - * Transmit AoE command request - * - * @v aoecmd AoE command - * @ret rc Return status code - */ -static int aoecmd_tx ( struct aoe_command *aoecmd ) { - struct aoe_device *aoedev = aoecmd->aoedev; - struct net_device *netdev = aoedev->netdev; - struct io_buffer *iobuf; - struct aoehdr *aoehdr; - size_t cmd_len; - int rc; - - /* Sanity check */ - assert ( netdev != NULL ); - - /* If we are transmitting anything that requires a response, - * start the retransmission timer. Do this before attempting - * to allocate the I/O buffer, in case allocation itself - * fails. - */ - start_timer ( &aoecmd->timer ); - - /* Create outgoing I/O buffer */ - cmd_len = aoecmd->type->cmd_len ( aoecmd ); - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + cmd_len ); - if ( ! iobuf ) - return -ENOMEM; - iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - aoehdr = iob_put ( iobuf, cmd_len ); - - /* Fill AoE header */ - memset ( aoehdr, 0, sizeof ( *aoehdr ) ); - aoehdr->ver_flags = AOE_VERSION; - aoehdr->major = htons ( aoedev->major ); - aoehdr->minor = aoedev->minor; - aoehdr->tag = htonl ( aoecmd->tag ); - aoecmd->type->cmd ( aoecmd, iobuf->data, iob_len ( iobuf ) ); - - /* Send packet */ - if ( ( rc = net_tx ( iobuf, netdev, &aoe_protocol, aoedev->target, - netdev->ll_addr ) ) != 0 ) { - DBGC ( aoedev, "AoE %s/%08x could not transmit: %s\n", - aoedev_name ( aoedev ), aoecmd->tag, - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Receive AoE command response - * - * @v aoecmd AoE command - * @v iobuf I/O buffer - * @v ll_source Link-layer source address - * @ret rc Return status code - */ -static int aoecmd_rx ( struct aoe_command *aoecmd, struct io_buffer *iobuf, - const void *ll_source ) { - struct aoe_device *aoedev = aoecmd->aoedev; - struct aoehdr *aoehdr = iobuf->data; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) { - DBGC ( aoedev, "AoE %s/%08x received underlength response " - "(%zd bytes)\n", aoedev_name ( aoedev ), - aoecmd->tag, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - if ( ( ntohs ( aoehdr->major ) != aoedev->major ) || - ( aoehdr->minor != aoedev->minor ) ) { - DBGC ( aoedev, "AoE %s/%08x received response for incorrect " - "device e%d.%d\n", aoedev_name ( aoedev ), aoecmd->tag, - ntohs ( aoehdr->major ), aoehdr->minor ); - rc = -EINVAL; - goto done; - } - - /* Catch command failures */ - if ( aoehdr->ver_flags & AOE_FL_ERROR ) { - DBGC ( aoedev, "AoE %s/%08x terminated in error\n", - aoedev_name ( aoedev ), aoecmd->tag ); - aoecmd_close ( aoecmd, -EIO ); - rc = -EIO; - goto done; - } - - /* Hand off to command completion handler */ - if ( ( rc = aoecmd->type->rsp ( aoecmd, iobuf->data, iob_len ( iobuf ), - ll_source ) ) != 0 ) - goto done; - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); - - /* Terminate command */ - aoecmd_close ( aoecmd, rc ); - - return rc; -} - -/** - * Handle AoE retry timer expiry - * - * @v timer AoE retry timer - * @v fail Failure indicator - */ -static void aoecmd_expired ( struct retry_timer *timer, int fail ) { - struct aoe_command *aoecmd = - container_of ( timer, struct aoe_command, timer ); - - if ( fail ) { - aoecmd_close ( aoecmd, -ETIMEDOUT ); - } else { - aoecmd_tx ( aoecmd ); - } -} - -/** - * Calculate length of AoE ATA command IU - * - * @v aoecmd AoE command - * @ret len Length of command IU - */ -static size_t aoecmd_ata_cmd_len ( struct aoe_command *aoecmd ) { - struct ata_cmd *command = &aoecmd->command; - - return ( sizeof ( struct aoehdr ) + sizeof ( struct aoeata ) + - command->data_out_len ); -} - -/** - * Build AoE ATA command IU - * - * @v aoecmd AoE command - * @v data Command IU - * @v len Length of command IU - */ -static void aoecmd_ata_cmd ( struct aoe_command *aoecmd, - void *data, size_t len ) { - struct aoe_device *aoedev = aoecmd->aoedev; - struct ata_cmd *command = &aoecmd->command; - struct aoehdr *aoehdr = data; - struct aoeata *aoeata = &aoehdr->payload[0].ata; - - /* Sanity check */ - linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, __fix_ata_h__ ); - assert ( len == ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) + - command->data_out_len ) ); - - /* Build IU */ - aoehdr->command = AOE_CMD_ATA; - memset ( aoeata, 0, sizeof ( *aoeata ) ); - aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 ) | - ( command->cb.device & ATA_DEV_SLAVE ) | - ( command->data_out_len ? AOE_FL_WRITE : 0 ) ); - aoeata->err_feat = command->cb.err_feat.bytes.cur; - aoeata->count = command->cb.count.native; - aoeata->cmd_stat = command->cb.cmd_stat; - aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native ); - if ( ! command->cb.lba48 ) - aoeata->lba.bytes[3] |= - ( command->cb.device & ATA_DEV_MASK ); - copy_from_user ( aoeata->data, command->data_out, 0, - command->data_out_len ); - - DBGC2 ( aoedev, "AoE %s/%08x ATA cmd %02x:%02x:%02x:%02x:%08llx", - aoedev_name ( aoedev ), aoecmd->tag, aoeata->aflags, - aoeata->err_feat, aoeata->count, aoeata->cmd_stat, - aoeata->lba.u64 ); - if ( command->data_out_len ) - DBGC2 ( aoedev, " out %04zx", command->data_out_len ); - if ( command->data_in_len ) - DBGC2 ( aoedev, " in %04zx", command->data_in_len ); - DBGC2 ( aoedev, "\n" ); -} - -/** - * Handle AoE ATA response IU - * - * @v aoecmd AoE command - * @v data Response IU - * @v len Length of response IU - * @v ll_source Link-layer source address - * @ret rc Return status code - */ -static int aoecmd_ata_rsp ( struct aoe_command *aoecmd, const void *data, - size_t len, const void *ll_source __unused ) { - struct aoe_device *aoedev = aoecmd->aoedev; - struct ata_cmd *command = &aoecmd->command; - const struct aoehdr *aoehdr = data; - const struct aoeata *aoeata = &aoehdr->payload[0].ata; - size_t data_len; - - /* Sanity check */ - if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) ) ) { - DBGC ( aoedev, "AoE %s/%08x received underlength ATA response " - "(%zd bytes)\n", aoedev_name ( aoedev ), - aoecmd->tag, len ); - return -EINVAL; - } - data_len = ( len - ( sizeof ( *aoehdr ) + sizeof ( *aoeata ) ) ); - DBGC2 ( aoedev, "AoE %s/%08x ATA rsp %02x in %04zx\n", - aoedev_name ( aoedev ), aoecmd->tag, aoeata->cmd_stat, - data_len ); - - /* Check for command failure */ - if ( aoeata->cmd_stat & ATA_STAT_ERR ) { - DBGC ( aoedev, "AoE %s/%08x status %02x\n", - aoedev_name ( aoedev ), aoecmd->tag, aoeata->cmd_stat ); - return -EIO; - } - - /* Check data-in length is sufficient. (There may be trailing - * garbage due to Ethernet minimum-frame-size padding.) - */ - if ( data_len < command->data_in_len ) { - DBGC ( aoedev, "AoE %s/%08x data-in underrun (received %zd, " - "expected %zd)\n", aoedev_name ( aoedev ), aoecmd->tag, - data_len, command->data_in_len ); - return -ERANGE; - } - - /* Copy out data payload */ - copy_to_user ( command->data_in, 0, aoeata->data, - command->data_in_len ); - - return 0; -} - -/** AoE ATA command */ -static struct aoe_command_type aoecmd_ata = { - .cmd_len = aoecmd_ata_cmd_len, - .cmd = aoecmd_ata_cmd, - .rsp = aoecmd_ata_rsp, -}; - -/** - * Calculate length of AoE configuration command IU - * - * @v aoecmd AoE command - * @ret len Length of command IU - */ -static size_t aoecmd_cfg_cmd_len ( struct aoe_command *aoecmd __unused ) { - return ( sizeof ( struct aoehdr ) + sizeof ( struct aoecfg ) ); -} - -/** - * Build AoE configuration command IU - * - * @v aoecmd AoE command - * @v data Command IU - * @v len Length of command IU - */ -static void aoecmd_cfg_cmd ( struct aoe_command *aoecmd, - void *data, size_t len ) { - struct aoe_device *aoedev = aoecmd->aoedev; - struct aoehdr *aoehdr = data; - struct aoecfg *aoecfg = &aoehdr->payload[0].cfg; - - /* Sanity check */ - assert ( len == ( sizeof ( *aoehdr ) + sizeof ( *aoecfg ) ) ); - - /* Build IU */ - aoehdr->command = AOE_CMD_CONFIG; - memset ( aoecfg, 0, sizeof ( *aoecfg ) ); - - DBGC ( aoedev, "AoE %s/%08x CONFIG cmd\n", - aoedev_name ( aoedev ), aoecmd->tag ); -} - -/** - * Handle AoE configuration response IU - * - * @v aoecmd AoE command - * @v data Response IU - * @v len Length of response IU - * @v ll_source Link-layer source address - * @ret rc Return status code - */ -static int aoecmd_cfg_rsp ( struct aoe_command *aoecmd, const void *data, - size_t len, const void *ll_source ) { - struct aoe_device *aoedev = aoecmd->aoedev; - struct ll_protocol *ll_protocol = aoedev->netdev->ll_protocol; - const struct aoehdr *aoehdr = data; - const struct aoecfg *aoecfg = &aoehdr->payload[0].cfg; - - /* Sanity check */ - if ( len < ( sizeof ( *aoehdr ) + sizeof ( *aoecfg ) ) ) { - DBGC ( aoedev, "AoE %s/%08x received underlength " - "configuration response (%zd bytes)\n", - aoedev_name ( aoedev ), aoecmd->tag, len ); - return -EINVAL; - } - DBGC ( aoedev, "AoE %s/%08x CONFIG rsp buf %04x fw %04x scnt %02x\n", - aoedev_name ( aoedev ), aoecmd->tag, ntohs ( aoecfg->bufcnt ), - aoecfg->fwver, aoecfg->scnt ); - - /* Record target MAC address */ - memcpy ( aoedev->target, ll_source, ll_protocol->ll_addr_len ); - DBGC ( aoedev, "AoE %s has MAC address %s\n", - aoedev_name ( aoedev ), ll_protocol->ntoa ( aoedev->target ) ); - - return 0; -} - -/** AoE configuration command */ -static struct aoe_command_type aoecmd_cfg = { - .cmd_len = aoecmd_cfg_cmd_len, - .cmd = aoecmd_cfg_cmd, - .rsp = aoecmd_cfg_rsp, -}; - -/** AoE command ATA interface operations */ -static struct interface_operation aoecmd_ata_op[] = { - INTF_OP ( intf_close, struct aoe_command *, aoecmd_close ), -}; - -/** AoE command ATA interface descriptor */ -static struct interface_descriptor aoecmd_ata_desc = - INTF_DESC ( struct aoe_command, ata, aoecmd_ata_op ); - -/** - * Identify AoE command by tag - * - * @v tag Command tag - * @ret aoecmd AoE command, or NULL - */ -static struct aoe_command * aoecmd_find_tag ( uint32_t tag ) { - struct aoe_command *aoecmd; - - list_for_each_entry ( aoecmd, &aoe_commands, list ) { - if ( aoecmd->tag == tag ) - return aoecmd; - } - return NULL; -} - -/** - * Choose an AoE command tag - * - * @ret tag New tag, or negative error - */ -static int aoecmd_new_tag ( void ) { - static uint16_t tag_idx; - unsigned int i; - - for ( i = 0 ; i < 65536 ; i++ ) { - tag_idx++; - if ( aoecmd_find_tag ( tag_idx ) == NULL ) - return ( AOE_TAG_MAGIC | tag_idx ); - } - return -EADDRINUSE; -} - -/** - * Create AoE command - * - * @v aoedev AoE device - * @v type AoE command type - * @ret aoecmd AoE command - */ -static struct aoe_command * aoecmd_create ( struct aoe_device *aoedev, - struct aoe_command_type *type ) { - struct aoe_command *aoecmd; - int tag; - - /* Allocate command tag */ - tag = aoecmd_new_tag(); - if ( tag < 0 ) - return NULL; - - /* Allocate and initialise structure */ - aoecmd = zalloc ( sizeof ( *aoecmd ) ); - if ( ! aoecmd ) - return NULL; - ref_init ( &aoecmd->refcnt, aoecmd_free ); - list_add ( &aoecmd->list, &aoe_commands ); - intf_init ( &aoecmd->ata, &aoecmd_ata_desc, &aoecmd->refcnt ); - timer_init ( &aoecmd->timer, aoecmd_expired, &aoecmd->refcnt ); - aoecmd->aoedev = aoedev_get ( aoedev ); - aoecmd->type = type; - aoecmd->tag = tag; - - /* Preserve timeout from last completed command */ - aoecmd->timer.timeout = aoedev->timeout; - - /* Return already mortalised. (Reference is held by command list.) */ - return aoecmd; -} - -/** - * Issue AoE ATA command - * - * @v aoedev AoE device - * @v parent Parent interface - * @v command ATA command - * @ret tag Command tag, or negative error - */ -static int aoedev_ata_command ( struct aoe_device *aoedev, - struct interface *parent, - struct ata_cmd *command ) { - struct net_device *netdev = aoedev->netdev; - struct aoe_command *aoecmd; - - /* Fail immediately if net device is closed */ - if ( ! netdev_is_open ( netdev ) ) { - DBGC ( aoedev, "AoE %s cannot issue command while net device " - "is closed\n", aoedev_name ( aoedev ) ); - return -EWOULDBLOCK; - } - - /* Create command */ - aoecmd = aoecmd_create ( aoedev, &aoecmd_ata ); - if ( ! aoecmd ) - return -ENOMEM; - memcpy ( &aoecmd->command, command, sizeof ( aoecmd->command ) ); - - /* Attempt to send command. Allow failures to be handled by - * the retry timer. - */ - aoecmd_tx ( aoecmd ); - - /* Attach to parent interface, leave reference with command - * list, and return. - */ - intf_plug_plug ( &aoecmd->ata, parent ); - return aoecmd->tag; -} - -/** - * Issue AoE configuration command - * - * @v aoedev AoE device - * @v parent Parent interface - * @ret tag Command tag, or negative error - */ -static int aoedev_cfg_command ( struct aoe_device *aoedev, - struct interface *parent ) { - struct aoe_command *aoecmd; - - /* Create command */ - aoecmd = aoecmd_create ( aoedev, &aoecmd_cfg ); - if ( ! aoecmd ) - return -ENOMEM; - - /* Attempt to send command. Allow failures to be handled by - * the retry timer. - */ - aoecmd_tx ( aoecmd ); - - /* Attach to parent interface, leave reference with command - * list, and return. - */ - intf_plug_plug ( &aoecmd->ata, parent ); - return aoecmd->tag; -} - -/** - * Free AoE device - * - * @v refcnt Reference count - */ -static void aoedev_free ( struct refcnt *refcnt ) { - struct aoe_device *aoedev = - container_of ( refcnt, struct aoe_device, refcnt ); - - netdev_put ( aoedev->netdev ); - free ( aoedev ); -} - -/** - * Close AoE device - * - * @v aoedev AoE device - * @v rc Reason for close - */ -static void aoedev_close ( struct aoe_device *aoedev, int rc ) { - struct aoe_command *aoecmd; - struct aoe_command *tmp; - - /* Shut down interfaces */ - intf_shutdown ( &aoedev->ata, rc ); - intf_shutdown ( &aoedev->config, rc ); - - /* Shut down any active commands */ - list_for_each_entry_safe ( aoecmd, tmp, &aoe_commands, list ) { - if ( aoecmd->aoedev != aoedev ) - continue; - aoecmd_get ( aoecmd ); - aoecmd_close ( aoecmd, rc ); - aoecmd_put ( aoecmd ); - } -} - -/** - * Check AoE device flow-control window - * - * @v aoedev AoE device - * @ret len Length of window - */ -static size_t aoedev_window ( struct aoe_device *aoedev ) { - return ( aoedev->configured ? ~( ( size_t ) 0 ) : 0 ); -} - -/** - * Handle AoE device configuration completion - * - * @v aoedev AoE device - * @v rc Reason for completion - */ -static void aoedev_config_done ( struct aoe_device *aoedev, int rc ) { - - /* Shut down interface */ - intf_shutdown ( &aoedev->config, rc ); - - /* Close device on failure */ - if ( rc != 0 ) { - aoedev_close ( aoedev, rc ); - return; - } - - /* Mark device as configured */ - aoedev->configured = 1; - xfer_window_changed ( &aoedev->ata ); -} - -/** - * Identify device underlying AoE device - * - * @v aoedev AoE device - * @ret device Underlying device - */ -static struct device * aoedev_identify_device ( struct aoe_device *aoedev ) { - return aoedev->netdev->dev; -} - -/** - * Describe AoE device in an ACPI table - * - * @v aoedev AoE device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int aoedev_describe ( struct aoe_device *aoedev, - struct acpi_description_header *acpi, - size_t len ) { - struct abft_table *abft = - container_of ( acpi, struct abft_table, acpi ); - - /* Sanity check */ - if ( len < sizeof ( *abft ) ) - return -ENOBUFS; - - /* Populate table */ - abft->acpi.signature = cpu_to_le32 ( ABFT_SIG ); - abft->acpi.length = cpu_to_le32 ( sizeof ( *abft ) ); - abft->acpi.revision = 1; - abft->shelf = cpu_to_le16 ( aoedev->major ); - abft->slot = aoedev->minor; - memcpy ( abft->mac, aoedev->netdev->ll_addr, sizeof ( abft->mac ) ); - - return 0; -} - -/** AoE device ATA interface operations */ -static struct interface_operation aoedev_ata_op[] = { - INTF_OP ( ata_command, struct aoe_device *, aoedev_ata_command ), - INTF_OP ( xfer_window, struct aoe_device *, aoedev_window ), - INTF_OP ( intf_close, struct aoe_device *, aoedev_close ), - INTF_OP ( acpi_describe, struct aoe_device *, aoedev_describe ), - INTF_OP ( identify_device, struct aoe_device *, - aoedev_identify_device ), -}; - -/** AoE device ATA interface descriptor */ -static struct interface_descriptor aoedev_ata_desc = - INTF_DESC ( struct aoe_device, ata, aoedev_ata_op ); - -/** AoE device configuration interface operations */ -static struct interface_operation aoedev_config_op[] = { - INTF_OP ( intf_close, struct aoe_device *, aoedev_config_done ), -}; - -/** AoE device configuration interface descriptor */ -static struct interface_descriptor aoedev_config_desc = - INTF_DESC ( struct aoe_device, config, aoedev_config_op ); - -/** - * Open AoE device - * - * @v parent Parent interface - * @v netdev Network device - * @v major Device major number - * @v minor Device minor number - * @ret rc Return status code - */ -static int aoedev_open ( struct interface *parent, struct net_device *netdev, - unsigned int major, unsigned int minor ) { - struct aoe_device *aoedev; - int rc; - - /* Allocate and initialise structure */ - aoedev = zalloc ( sizeof ( *aoedev ) ); - if ( ! aoedev ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &aoedev->refcnt, aoedev_free ); - intf_init ( &aoedev->ata, &aoedev_ata_desc, &aoedev->refcnt ); - intf_init ( &aoedev->config, &aoedev_config_desc, &aoedev->refcnt ); - aoedev->netdev = netdev_get ( netdev ); - aoedev->major = major; - aoedev->minor = minor; - memcpy ( aoedev->target, netdev->ll_broadcast, - netdev->ll_protocol->ll_addr_len ); - - /* Initiate configuration */ - if ( ( rc = aoedev_cfg_command ( aoedev, &aoedev->config ) ) < 0 ) { - DBGC ( aoedev, "AoE %s could not initiate configuration: %s\n", - aoedev_name ( aoedev ), strerror ( rc ) ); - goto err_config; - } - - /* Attach ATA device to parent interface */ - if ( ( rc = ata_open ( parent, &aoedev->ata, ATA_DEV_MASTER, - AOE_MAX_COUNT ) ) != 0 ) { - DBGC ( aoedev, "AoE %s could not create ATA device: %s\n", - aoedev_name ( aoedev ), strerror ( rc ) ); - goto err_ata_open; - } - - /* Mortalise self and return */ - ref_put ( &aoedev->refcnt ); - return 0; - - err_ata_open: - err_config: - aoedev_close ( aoedev, rc ); - ref_put ( &aoedev->refcnt ); - err_zalloc: - return rc; -} - -/****************************************************************************** - * - * AoE network protocol - * - ****************************************************************************** - */ - -/** - * Process incoming AoE packets - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int aoe_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - const void *ll_dest __unused, - const void *ll_source, - unsigned int flags __unused ) { - struct aoehdr *aoehdr = iobuf->data; - struct aoe_command *aoecmd; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) { - DBG ( "AoE received underlength packet (%zd bytes)\n", - iob_len ( iobuf ) ); - rc = -EINVAL; - goto err_sanity; - } - if ( ( aoehdr->ver_flags & AOE_VERSION_MASK ) != AOE_VERSION ) { - DBG ( "AoE received packet for unsupported protocol version " - "%02x\n", ( aoehdr->ver_flags & AOE_VERSION_MASK ) ); - rc = -EPROTONOSUPPORT; - goto err_sanity; - } - if ( ! ( aoehdr->ver_flags & AOE_FL_RESPONSE ) ) { - DBG ( "AoE received request packet\n" ); - rc = -EOPNOTSUPP; - goto err_sanity; - } - - /* Demultiplex amongst active AoE commands */ - aoecmd = aoecmd_find_tag ( ntohl ( aoehdr->tag ) ); - if ( ! aoecmd ) { - DBG ( "AoE received packet for unused tag %08x\n", - ntohl ( aoehdr->tag ) ); - rc = -ENOENT; - goto err_demux; - } - - /* Pass received frame to command */ - aoecmd_get ( aoecmd ); - if ( ( rc = aoecmd_rx ( aoecmd, iob_disown ( iobuf ), - ll_source ) ) != 0 ) - goto err_rx; - - err_rx: - aoecmd_put ( aoecmd ); - err_demux: - err_sanity: - free_iob ( iobuf ); - return rc; -} - -/** AoE protocol */ -struct net_protocol aoe_protocol __net_protocol = { - .name = "AoE", - .net_proto = htons ( ETH_P_AOE ), - .rx = aoe_rx, -}; - -/****************************************************************************** - * - * AoE URIs - * - ****************************************************************************** - */ - -/** - * Parse AoE URI - * - * @v uri URI - * @ret major Major device number - * @ret minor Minor device number - * @ret rc Return status code - * - * An AoE URI has the form "aoe:e<major>.<minor>". - */ -static int aoe_parse_uri ( struct uri *uri, unsigned int *major, - unsigned int *minor ) { - const char *ptr; - char *end; - - /* Check for URI with opaque portion */ - if ( ! uri->opaque ) - return -EINVAL; - ptr = uri->opaque; - - /* Check for initial 'e' */ - if ( *ptr != 'e' ) - return -EINVAL; - ptr++; - - /* Parse major device number */ - *major = strtoul ( ptr, &end, 10 ); - if ( *end != '.' ) - return -EINVAL; - ptr = ( end + 1 ); - - /* Parse minor device number */ - *minor = strtoul ( ptr, &end, 10 ); - if ( *end ) - return -EINVAL; - - return 0; -} - -/** - * Open AoE URI - * - * @v parent Parent interface - * @v uri URI - * @ret rc Return status code - */ -static int aoe_open ( struct interface *parent, struct uri *uri ) { - struct net_device *netdev; - unsigned int major; - unsigned int minor; - int rc; - - /* Identify network device. This is something of a hack, but - * the AoE URI scheme that has been in use for some time now - * provides no way to specify a particular device. - */ - netdev = last_opened_netdev(); - if ( ! netdev ) { - DBG ( "AoE cannot identify network device\n" ); - return -ENODEV; - } - - /* Parse URI */ - if ( ( rc = aoe_parse_uri ( uri, &major, &minor ) ) != 0 ) { - DBG ( "AoE cannot parse URI\n" ); - return rc; - } - - /* Open AoE device */ - if ( ( rc = aoedev_open ( parent, netdev, major, minor ) ) != 0 ) - return rc; - - return 0; -} - -/** AoE URI opener */ -struct uri_opener aoe_uri_opener __uri_opener = { - .scheme = "aoe", - .open = aoe_open, -}; diff --git a/qemu/roms/ipxe/src/net/arp.c b/qemu/roms/ipxe/src/net/arp.c deleted file mode 100644 index 1e27c44e7..000000000 --- a/qemu/roms/ipxe/src/net/arp.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <ipxe/if_ether.h> -#include <ipxe/if_arp.h> -#include <ipxe/iobuf.h> -#include <ipxe/netdevice.h> -#include <ipxe/neighbour.h> -#include <ipxe/arp.h> - -/** @file - * - * Address Resolution Protocol - * - * This file implements the address resolution protocol as defined in - * RFC826. The implementation is media-independent and - * protocol-independent; it is not limited to Ethernet or to IPv4. - * - */ - -struct net_protocol arp_protocol __net_protocol; - -/** - * Transmit ARP request - * - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @v net_source Source network-layer address - * @ret rc Return status code - */ -int arp_tx_request ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest, const void *net_source ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct io_buffer *iobuf; - struct arphdr *arphdr; - int rc; - - /* Allocate ARP packet */ - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) + - ( 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) ) ); - if ( ! iobuf ) - return -ENOMEM; - iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - - /* Build up ARP request */ - arphdr = iob_put ( iobuf, sizeof ( *arphdr ) ); - arphdr->ar_hrd = ll_protocol->ll_proto; - arphdr->ar_hln = ll_protocol->ll_addr_len; - arphdr->ar_pro = net_protocol->net_proto; - arphdr->ar_pln = net_protocol->net_addr_len; - arphdr->ar_op = htons ( ARPOP_REQUEST ); - memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ), - netdev->ll_addr, ll_protocol->ll_addr_len ); - memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), - net_source, net_protocol->net_addr_len ); - memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ), - 0, ll_protocol->ll_addr_len ); - memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), - net_dest, net_protocol->net_addr_len ); - - /* Transmit ARP request */ - if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol, - netdev->ll_broadcast, netdev->ll_addr ) ) != 0 ) { - DBGC ( netdev, "ARP %s %s %s could not transmit request: %s\n", - netdev->name, net_protocol->name, - net_protocol->ntoa ( net_dest ), strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** ARP neighbour discovery protocol */ -struct neighbour_discovery arp_discovery = { - .name = "ARP", - .tx_request = arp_tx_request, -}; - -/** - * Identify ARP protocol - * - * @v net_proto Network-layer protocol, in network-endian order - * @ret arp_net_protocol ARP protocol, or NULL - * - */ -static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) { - struct arp_net_protocol *arp_net_protocol; - - for_each_table_entry ( arp_net_protocol, ARP_NET_PROTOCOLS ) { - if ( arp_net_protocol->net_protocol->net_proto == net_proto ) - return arp_net_protocol; - } - return NULL; -} - -/** - * Process incoming ARP packets - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_dest __unused, - const void *ll_source __unused, - unsigned int flags __unused ) { - struct arphdr *arphdr = iobuf->data; - struct arp_net_protocol *arp_net_protocol; - struct net_protocol *net_protocol; - struct ll_protocol *ll_protocol; - int rc; - - /* Identify network-layer and link-layer protocols */ - arp_net_protocol = arp_find_protocol ( arphdr->ar_pro ); - if ( ! arp_net_protocol ) { - rc = -EPROTONOSUPPORT; - goto done; - } - net_protocol = arp_net_protocol->net_protocol; - ll_protocol = netdev->ll_protocol; - - /* Sanity checks */ - if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) || - ( arphdr->ar_hln != ll_protocol->ll_addr_len ) || - ( arphdr->ar_pln != net_protocol->net_addr_len ) ) { - rc = -EINVAL; - goto done; - } - - /* Update neighbour cache entry for this sender, if any */ - neighbour_update ( netdev, net_protocol, arp_sender_pa ( arphdr ), - arp_sender_ha ( arphdr ) ); - - /* If it's not a request, there's nothing more to do */ - if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) ) { - rc = 0; - goto done; - } - - /* See if we own the target protocol address */ - if ( arp_net_protocol->check ( netdev, arp_target_pa ( arphdr ) ) != 0){ - rc = 0; - goto done; - } - - /* Change request to a reply */ - DBGC2 ( netdev, "ARP %s %s %s reply => %s %s\n", - netdev->name, net_protocol->name, - net_protocol->ntoa ( arp_target_pa ( arphdr ) ), - ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) ); - arphdr->ar_op = htons ( ARPOP_REPLY ); - memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ), - arphdr->ar_hln + arphdr->ar_pln ); - memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln ); - - /* Send reply */ - if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol, - arp_target_ha ( arphdr ), - netdev->ll_addr ) ) != 0 ) { - DBGC ( netdev, "ARP %s %s %s could not transmit reply: %s\n", - netdev->name, net_protocol->name, - net_protocol->ntoa ( arp_target_pa ( arphdr ) ), - strerror ( rc ) ); - goto done; - } - - /* Success */ - rc = 0; - - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Transcribe ARP address - * - * @v net_addr ARP address - * @ret string "<ARP>" - * - * This operation is meaningless for the ARP protocol. - */ -static const char * arp_ntoa ( const void *net_addr __unused ) { - return "<ARP>"; -} - -/** ARP network protocol */ -struct net_protocol arp_protocol __net_protocol = { - .name = "ARP", - .net_proto = htons ( ETH_P_ARP ), - .rx = arp_rx, - .ntoa = arp_ntoa, -}; diff --git a/qemu/roms/ipxe/src/net/dhcpopts.c b/qemu/roms/ipxe/src/net/dhcpopts.c deleted file mode 100644 index cdb632b46..000000000 --- a/qemu/roms/ipxe/src/net/dhcpopts.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <ipxe/dhcp.h> -#include <ipxe/dhcpopts.h> - -/** @file - * - * DHCP options - * - */ - -/** - * Obtain printable version of a DHCP option tag - * - * @v tag DHCP option tag - * @ret name String representation of the tag - * - */ -static inline char * dhcp_tag_name ( unsigned int tag ) { - static char name[8]; - - if ( DHCP_IS_ENCAP_OPT ( tag ) ) { - snprintf ( name, sizeof ( name ), "%d.%d", - DHCP_ENCAPSULATOR ( tag ), - DHCP_ENCAPSULATED ( tag ) ); - } else { - snprintf ( name, sizeof ( name ), "%d", tag ); - } - return name; -} - -/** - * Get pointer to DHCP option - * - * @v options DHCP options block - * @v offset Offset within options block - * @ret option DHCP option - */ -static inline __attribute__ (( always_inline )) struct dhcp_option * -dhcp_option ( struct dhcp_options *options, unsigned int offset ) { - return ( ( struct dhcp_option * ) ( options->data + offset ) ); -} - -/** - * Get offset of a DHCP option - * - * @v options DHCP options block - * @v option DHCP option - * @ret offset Offset within options block - */ -static inline __attribute__ (( always_inline )) int -dhcp_option_offset ( struct dhcp_options *options, - struct dhcp_option *option ) { - return ( ( ( void * ) option ) - options->data ); -} - -/** - * Calculate length of any DHCP option - * - * @v option DHCP option - * @ret len Length (including tag and length field) - */ -static unsigned int dhcp_option_len ( struct dhcp_option *option ) { - if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) { - return 1; - } else { - return ( option->len + DHCP_OPTION_HEADER_LEN ); - } -} - -/** - * Find DHCP option within DHCP options block, and its encapsulator (if any) - * - * @v options DHCP options block - * @v tag DHCP option tag to search for - * @ret encap_offset Offset of encapsulating DHCP option - * @ret offset Offset of DHCP option, or negative error - * - * Searches for the DHCP option matching the specified tag within the - * DHCP option block. Encapsulated options may be searched for by - * using DHCP_ENCAP_OPT() to construct the tag value. - * - * If the option is encapsulated, and @c encap_offset is non-NULL, it - * will be filled in with the offset of the encapsulating option. - * - * This routine is designed to be paranoid. It does not assume that - * the option data is well-formatted, and so must guard against flaws - * such as options missing a @c DHCP_END terminator, or options whose - * length would take them beyond the end of the data block. - */ -static int find_dhcp_option_with_encap ( struct dhcp_options *options, - unsigned int tag, - int *encap_offset ) { - unsigned int original_tag __attribute__ (( unused )) = tag; - struct dhcp_option *option; - int offset = 0; - ssize_t remaining = options->used_len; - unsigned int option_len; - - /* Sanity check */ - if ( tag == DHCP_PAD ) - return -ENOENT; - - /* Search for option */ - while ( remaining ) { - /* Calculate length of this option. Abort processing - * if the length is malformed (i.e. takes us beyond - * the end of the data block). - */ - option = dhcp_option ( options, offset ); - option_len = dhcp_option_len ( option ); - remaining -= option_len; - if ( remaining < 0 ) - break; - /* Check for explicit end marker */ - if ( option->tag == DHCP_END ) { - if ( tag == DHCP_END ) - /* Special case where the caller is interested - * in whether we have this marker or not. - */ - return offset; - else - break; - } - /* Check for matching tag */ - if ( option->tag == tag ) { - DBGC ( options, "DHCPOPT %p found %s (length %d)\n", - options, dhcp_tag_name ( original_tag ), - option_len ); - return offset; - } - /* Check for start of matching encapsulation block */ - if ( DHCP_IS_ENCAP_OPT ( tag ) && - ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) { - if ( encap_offset ) - *encap_offset = offset; - /* Continue search within encapsulated option block */ - tag = DHCP_ENCAPSULATED ( tag ); - remaining = option_len; - offset += DHCP_OPTION_HEADER_LEN; - continue; - } - offset += option_len; - } - - return -ENOENT; -} - -/** - * Refuse to reallocate DHCP option block - * - * @v options DHCP option block - * @v len New length - * @ret rc Return status code - */ -int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len ) { - return ( ( len <= options->alloc_len ) ? 0 : -ENOSPC ); -} - -/** - * Resize a DHCP option - * - * @v options DHCP option block - * @v offset Offset of option to resize - * @v encap_offset Offset of encapsulating offset (or -ve for none) - * @v old_len Old length (including header) - * @v new_len New length (including header) - * @ret rc Return status code - */ -static int resize_dhcp_option ( struct dhcp_options *options, - int offset, int encap_offset, - size_t old_len, size_t new_len ) { - struct dhcp_option *encapsulator; - struct dhcp_option *option; - ssize_t delta = ( new_len - old_len ); - size_t old_alloc_len; - size_t new_used_len; - size_t new_encapsulator_len; - void *source; - void *dest; - int rc; - - /* Check for sufficient space */ - if ( new_len > DHCP_MAX_LEN ) { - DBGC ( options, "DHCPOPT %p overlength option\n", options ); - return -ENOSPC; - } - new_used_len = ( options->used_len + delta ); - - /* Expand options block, if necessary */ - if ( new_used_len > options->alloc_len ) { - /* Reallocate options block */ - old_alloc_len = options->alloc_len; - if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){ - DBGC ( options, "DHCPOPT %p could not reallocate to " - "%zd bytes\n", options, new_used_len ); - return rc; - } - /* Clear newly allocated space */ - memset ( ( options->data + old_alloc_len ), 0, - ( options->alloc_len - old_alloc_len ) ); - } - - /* Update encapsulator, if applicable */ - if ( encap_offset >= 0 ) { - encapsulator = dhcp_option ( options, encap_offset ); - new_encapsulator_len = ( encapsulator->len + delta ); - if ( new_encapsulator_len > DHCP_MAX_LEN ) { - DBGC ( options, "DHCPOPT %p overlength encapsulator\n", - options ); - return -ENOSPC; - } - encapsulator->len = new_encapsulator_len; - } - - /* Update used length */ - options->used_len = new_used_len; - - /* Move remainder of option data */ - option = dhcp_option ( options, offset ); - source = ( ( ( void * ) option ) + old_len ); - dest = ( ( ( void * ) option ) + new_len ); - memmove ( dest, source, ( new_used_len - offset - new_len ) ); - - /* Shrink options block, if applicable */ - if ( new_used_len < options->alloc_len ) { - if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ){ - DBGC ( options, "DHCPOPT %p could not reallocate to " - "%zd bytes\n", options, new_used_len ); - return rc; - } - } - - return 0; -} - -/** - * Set value of DHCP option - * - * @v options DHCP option block - * @v tag DHCP option tag - * @v data New value for DHCP option - * @v len Length of value, in bytes - * @ret offset Offset of DHCP option, or negative error - * - * Sets the value of a DHCP option within the options block. The - * option may or may not already exist. Encapsulators will be created - * (and deleted) as necessary. - * - * This call may fail due to insufficient space in the options block. - * If it does fail, and the option existed previously, the option will - * be left with its original value. - */ -static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, - const void *data, size_t len ) { - static const uint8_t empty_encap[] = { DHCP_END }; - int offset; - int encap_offset = -1; - int creation_offset; - struct dhcp_option *option; - unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag ); - size_t old_len = 0; - size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 ); - int rc; - - /* Sanity check */ - if ( tag == DHCP_PAD ) - return -ENOTTY; - - creation_offset = find_dhcp_option_with_encap ( options, DHCP_END, - NULL ); - if ( creation_offset < 0 ) - creation_offset = options->used_len; - /* Find old instance of this option, if any */ - offset = find_dhcp_option_with_encap ( options, tag, &encap_offset ); - if ( offset >= 0 ) { - old_len = dhcp_option_len ( dhcp_option ( options, offset ) ); - DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n", - options, dhcp_tag_name ( tag ), old_len, new_len ); - } else { - DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n", - options, dhcp_tag_name ( tag ), new_len ); - } - - /* Ensure that encapsulator exists, if required */ - if ( encap_tag ) { - if ( encap_offset < 0 ) { - encap_offset = - set_dhcp_option ( options, encap_tag, - empty_encap, - sizeof ( empty_encap ) ); - } - if ( encap_offset < 0 ) - return encap_offset; - creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN ); - } - - /* Create new option if necessary */ - if ( offset < 0 ) - offset = creation_offset; - - /* Resize option to fit new data */ - if ( ( rc = resize_dhcp_option ( options, offset, encap_offset, - old_len, new_len ) ) != 0 ) - return rc; - - /* Copy new data into option, if applicable */ - if ( len ) { - option = dhcp_option ( options, offset ); - option->tag = tag; - option->len = len; - memcpy ( &option->data, data, len ); - } - - /* Delete encapsulator if there's nothing else left in it */ - if ( encap_offset >= 0 ) { - option = dhcp_option ( options, encap_offset ); - if ( option->len <= 1 ) - set_dhcp_option ( options, encap_tag, NULL, 0 ); - } - - return offset; -} - -/** - * Check applicability of DHCP option setting - * - * @v tag Setting tag number - * @ret applies Setting applies to this option block - */ -int dhcpopt_applies ( unsigned int tag ) { - - return ( tag && ( tag <= DHCP_ENCAP_OPT ( DHCP_MAX_OPTION, - DHCP_MAX_OPTION ) ) ); -} - -/** - * Store value of DHCP option setting - * - * @v options DHCP option block - * @v tag Setting tag number - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, - const void *data, size_t len ) { - int offset; - - offset = set_dhcp_option ( options, tag, data, len ); - if ( offset < 0 ) - return offset; - return 0; -} - -/** - * Fetch value of DHCP option setting - * - * @v options DHCP option block - * @v tag Setting tag number - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, - void *data, size_t len ) { - int offset; - struct dhcp_option *option; - size_t option_len; - - offset = find_dhcp_option_with_encap ( options, tag, NULL ); - if ( offset < 0 ) - return offset; - - option = dhcp_option ( options, offset ); - option_len = option->len; - if ( len > option_len ) - len = option_len; - memcpy ( data, option->data, len ); - - return option_len; -} - -/** - * Recalculate length of DHCP options block - * - * @v options Uninitialised DHCP option block - * - * The "used length" field will be updated based on scanning through - * the block to find the end of the options. - */ -void dhcpopt_update_used_len ( struct dhcp_options *options ) { - struct dhcp_option *option; - int offset = 0; - ssize_t remaining = options->alloc_len; - unsigned int option_len; - - /* Find last non-pad option */ - options->used_len = 0; - while ( remaining ) { - option = dhcp_option ( options, offset ); - option_len = dhcp_option_len ( option ); - remaining -= option_len; - if ( remaining < 0 ) - break; - offset += option_len; - if ( option->tag != DHCP_PAD ) - options->used_len = offset; - } -} - -/** - * Initialise prepopulated block of DHCP options - * - * @v options Uninitialised DHCP option block - * @v data Memory for DHCP option data - * @v alloc_len Length of memory for DHCP option data - * @v realloc DHCP option block reallocator - * - * The memory content must already be filled with valid DHCP options. - * A zeroed block counts as a block of valid DHCP options. - */ -void dhcpopt_init ( struct dhcp_options *options, void *data, size_t alloc_len, - int ( * realloc ) ( struct dhcp_options *options, - size_t len ) ) { - - /* Fill in fields */ - options->data = data; - options->alloc_len = alloc_len; - options->realloc = realloc; - - /* Update length */ - dhcpopt_update_used_len ( options ); - - DBGC ( options, "DHCPOPT %p created (data %p lengths %#zx,%#zx)\n", - options, options->data, options->used_len, options->alloc_len ); -} diff --git a/qemu/roms/ipxe/src/net/dhcppkt.c b/qemu/roms/ipxe/src/net/dhcppkt.c deleted file mode 100644 index 4e64f85e4..000000000 --- a/qemu/roms/ipxe/src/net/dhcppkt.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <ipxe/netdevice.h> -#include <ipxe/dhcp.h> -#include <ipxe/dhcpopts.h> -#include <ipxe/dhcppkt.h> - -/** @file - * - * DHCP packets - * - */ - -/**************************************************************************** - * - * DHCP packet raw interface - * - */ - -/** - * Calculate used length of an IPv4 field within a DHCP packet - * - * @v data Field data - * @v len Length of field - * @ret used Used length of field - */ -static size_t used_len_ipv4 ( const void *data, size_t len __unused ) { - const struct in_addr *in = data; - - return ( in->s_addr ? sizeof ( *in ) : 0 ); -} - -/** - * Calculate used length of a string field within a DHCP packet - * - * @v data Field data - * @v len Length of field - * @ret used Used length of field - */ -static size_t used_len_string ( const void *data, size_t len ) { - return strnlen ( data, len ); -} - -/** A dedicated field within a DHCP packet */ -struct dhcp_packet_field { - /** Settings tag number */ - unsigned int tag; - /** Offset within DHCP packet */ - uint16_t offset; - /** Length of field */ - uint16_t len; - /** Calculate used length of field - * - * @v data Field data - * @v len Length of field - * @ret used Used length of field - */ - size_t ( * used_len ) ( const void *data, size_t len ); -}; - -/** Declare a dedicated field within a DHCP packet - * - * @v _tag Settings tag number - * @v _field Field name - * @v _used_len Function to calculate used length of field - */ -#define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \ - .tag = (_tag), \ - .offset = offsetof ( struct dhcphdr, _field ), \ - .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \ - .used_len = _used_len, \ - } - -/** Dedicated fields within a DHCP packet */ -static struct dhcp_packet_field dhcp_packet_fields[] = { - DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr, used_len_ipv4 ), - DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr, used_len_ipv4 ), - DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname, used_len_string ), - DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file, used_len_string ), -}; - -/** - * Get address of a DHCP packet field - * - * @v dhcphdr DHCP packet header - * @v field DHCP packet field - * @ret data Packet field data - */ -static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr, - struct dhcp_packet_field *field ) { - return ( ( ( void * ) dhcphdr ) + field->offset ); -} - -/** - * Find DHCP packet field corresponding to settings tag number - * - * @v tag Settings tag number - * @ret field DHCP packet field, or NULL - */ -static struct dhcp_packet_field * -find_dhcp_packet_field ( unsigned int tag ) { - struct dhcp_packet_field *field; - unsigned int i; - - for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) / - sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) { - field = &dhcp_packet_fields[i]; - if ( field->tag == tag ) - return field; - } - return NULL; -} - -/** - * Check applicability of DHCP setting - * - * @v dhcppkt DHCP packet - * @v tag Setting tag number - * @ret applies Setting applies within this settings block - */ -static int dhcppkt_applies ( struct dhcp_packet *dhcppkt __unused, - unsigned int tag ) { - - return dhcpopt_applies ( tag ); -} - -/** - * Store value of DHCP packet setting - * - * @v dhcppkt DHCP packet - * @v tag Setting tag number - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, - const void *data, size_t len ) { - struct dhcp_packet_field *field; - void *field_data; - - /* If this is a special field, fill it in */ - if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { - if ( len > field->len ) - return -ENOSPC; - field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); - memset ( field_data, 0, field->len ); - memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ), - data, len ); - /* Erase any equivalent option from the options block */ - dhcpopt_store ( &dhcppkt->options, tag, NULL, 0 ); - return 0; - } - - /* Otherwise, use the generic options block */ - return dhcpopt_store ( &dhcppkt->options, tag, data, len ); -} - -/** - * Fetch value of DHCP packet setting - * - * @v dhcppkt DHCP packet - * @v tag Setting tag number - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, - void *data, size_t len ) { - struct dhcp_packet_field *field; - void *field_data; - size_t field_len = 0; - - /* Identify special field, if any */ - if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { - field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); - field_len = field->used_len ( field_data, field->len ); - } - - /* Return special field, if it exists and is populated */ - if ( field_len ) { - if ( len > field_len ) - len = field_len; - memcpy ( data, field_data, len ); - return field_len; - } - - /* Otherwise, use the generic options block */ - return dhcpopt_fetch ( &dhcppkt->options, tag, data, len ); -} - -/**************************************************************************** - * - * DHCP packet settings interface - * - */ - -/** - * Check applicability of DHCP setting - * - * @v settings Settings block - * @v setting Setting - * @ret applies Setting applies within this settings block - */ -static int dhcppkt_settings_applies ( struct settings *settings, - const struct setting *setting ) { - struct dhcp_packet *dhcppkt = - container_of ( settings, struct dhcp_packet, settings ); - - return ( ( setting->scope == NULL ) && - dhcppkt_applies ( dhcppkt, setting->tag ) ); -} - -/** - * Store value of DHCP setting - * - * @v settings Settings block - * @v setting Setting to store - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -static int dhcppkt_settings_store ( struct settings *settings, - const struct setting *setting, - const void *data, size_t len ) { - struct dhcp_packet *dhcppkt = - container_of ( settings, struct dhcp_packet, settings ); - - return dhcppkt_store ( dhcppkt, setting->tag, data, len ); -} - -/** - * Fetch value of DHCP setting - * - * @v settings Settings block, or NULL to search all blocks - * @v setting Setting to fetch - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int dhcppkt_settings_fetch ( struct settings *settings, - struct setting *setting, - void *data, size_t len ) { - struct dhcp_packet *dhcppkt = - container_of ( settings, struct dhcp_packet, settings ); - - return dhcppkt_fetch ( dhcppkt, setting->tag, data, len ); -} - -/** DHCP settings operations */ -static struct settings_operations dhcppkt_settings_operations = { - .applies = dhcppkt_settings_applies, - .store = dhcppkt_settings_store, - .fetch = dhcppkt_settings_fetch, -}; - -/**************************************************************************** - * - * Constructor - * - */ - -/** - * Initialise DHCP packet - * - * @v dhcppkt DHCP packet structure to fill in - * @v data DHCP packet raw data - * @v max_len Length of raw data buffer - * - * Initialise a DHCP packet structure from a data buffer containing a - * DHCP packet. - */ -void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data, - size_t len ) { - ref_init ( &dhcppkt->refcnt, NULL ); - dhcppkt->dhcphdr = data; - dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options, - ( len - offsetof ( struct dhcphdr, options ) ), - dhcpopt_no_realloc ); - settings_init ( &dhcppkt->settings, &dhcppkt_settings_operations, - &dhcppkt->refcnt, NULL ); -} diff --git a/qemu/roms/ipxe/src/net/eapol.c b/qemu/roms/ipxe/src/net/eapol.c deleted file mode 100644 index eb0362994..000000000 --- a/qemu/roms/ipxe/src/net/eapol.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/** @file - * - * 802.1X Extensible Authentication Protocol over LANs demultiplexer - * - */ - -#include <ipxe/netdevice.h> -#include <ipxe/iobuf.h> -#include <ipxe/if_ether.h> -#include <ipxe/eapol.h> -#include <errno.h> -#include <byteswap.h> - -/** - * Receive EAPOL network-layer packet - * - * @v iob I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * - * This function takes ownership of the I/O buffer passed to it. - */ -static int eapol_rx ( struct io_buffer *iob, struct net_device *netdev, - const void *ll_dest, const void *ll_source, - unsigned int flags __unused ) { - struct eapol_frame *eapol = iob->data; - struct eapol_handler *handler; - - if ( iob_len ( iob ) < EAPOL_HDR_LEN ) { - free_iob ( iob ); - return -EINVAL; - } - - for_each_table_entry ( handler, EAPOL_HANDLERS ) { - if ( handler->type == eapol->type ) { - iob_pull ( iob, EAPOL_HDR_LEN ); - return handler->rx ( iob, netdev, ll_dest, ll_source ); - } - } - - free_iob ( iob ); - return -( ENOTSUP | ( ( eapol->type & 0x1f ) << 8 ) ); -} - -/** - * Transcribe EAPOL network-layer address - * - * @v net_addr Network-layer address - * @ret str String representation of network-layer address - * - * EAPOL doesn't have network-layer addresses, so we just return the - * string @c "<EAPOL>". - */ -static const char * eapol_ntoa ( const void *net_addr __unused ) -{ - return "<EAPOL>"; -} - -/** EAPOL network protocol */ -struct net_protocol eapol_protocol __net_protocol = { - .name = "EAPOL", - .rx = eapol_rx, - .ntoa = eapol_ntoa, - .net_proto = htons ( ETH_P_EAPOL ), -}; diff --git a/qemu/roms/ipxe/src/net/eth_slow.c b/qemu/roms/ipxe/src/net/eth_slow.c deleted file mode 100644 index 049c26cb3..000000000 --- a/qemu/roms/ipxe/src/net/eth_slow.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <ipxe/iobuf.h> -#include <ipxe/netdevice.h> -#include <ipxe/if_ether.h> -#include <ipxe/ethernet.h> -#include <ipxe/eth_slow.h> - -/** @file - * - * Ethernet slow protocols - * - * We implement a very simple passive LACP entity, that pretends that - * each port is the only port on an individual system. We avoid the - * need for timeout logic (and retaining local state about our - * partner) by requesting the same timeout period (1s or 30s) as our - * partner requests, and then simply responding to every packet the - * partner sends us. - */ - -struct net_protocol eth_slow_protocol __net_protocol; - -/** Slow protocols multicast address */ -static const uint8_t eth_slow_address[ETH_ALEN] = - { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; - -/** - * Name LACP TLV type - * - * @v type LACP TLV type - * @ret name Name of LACP TLV type - */ -static inline __attribute__ (( always_inline )) const char * -eth_slow_lacp_tlv_name ( uint8_t type ) { - switch ( type ) { - case ETH_SLOW_TLV_TERMINATOR: return "terminator"; - case ETH_SLOW_TLV_LACP_ACTOR: return "actor"; - case ETH_SLOW_TLV_LACP_PARTNER: return "partner"; - case ETH_SLOW_TLV_LACP_COLLECTOR: return "collector"; - default: return "<invalid>"; - } -} - -/** - * Name marker TLV type - * - * @v type Marker TLV type - * @ret name Name of marker TLV type - */ -static inline __attribute__ (( always_inline )) const char * -eth_slow_marker_tlv_name ( uint8_t type ) { - switch ( type ) { - case ETH_SLOW_TLV_TERMINATOR: return "terminator"; - case ETH_SLOW_TLV_MARKER_REQUEST: return "request"; - case ETH_SLOW_TLV_MARKER_RESPONSE: return "response"; - default: return "<invalid>"; - } -} - -/** - * Name LACP state - * - * @v state LACP state - * @ret name LACP state name - */ -static const char * eth_slow_lacp_state_name ( uint8_t state ) { - static char state_chars[] = "AFGSRTLX"; - unsigned int i; - - for ( i = 0 ; i < 8 ; i++ ) { - state_chars[i] |= 0x20; - if ( state & ( 1 << i ) ) - state_chars[i] &= ~0x20; - } - return state_chars; -} - -/** - * Dump LACP packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v label "RX" or "TX" - */ -static void eth_slow_lacp_dump ( struct io_buffer *iobuf, - struct net_device *netdev, - const char *label ) { - union eth_slow_packet *eth_slow = iobuf->data; - struct eth_slow_lacp *lacp = ð_slow->lacp; - - DBGC ( netdev, - "SLOW %s %s LACP actor (%04x,%s,%04x,%02x,%04x) [%s]\n", - netdev->name, label, ntohs ( lacp->actor.system_priority ), - eth_ntoa ( lacp->actor.system ), - ntohs ( lacp->actor.key ), - ntohs ( lacp->actor.port_priority ), - ntohs ( lacp->actor.port ), - eth_slow_lacp_state_name ( lacp->actor.state ) ); - DBGC ( netdev, - "SLOW %s %s LACP partner (%04x,%s,%04x,%02x,%04x) [%s]\n", - netdev->name, label, ntohs ( lacp->partner.system_priority ), - eth_ntoa ( lacp->partner.system ), - ntohs ( lacp->partner.key ), - ntohs ( lacp->partner.port_priority ), - ntohs ( lacp->partner.port ), - eth_slow_lacp_state_name ( lacp->partner.state ) ); - DBGC ( netdev, "SLOW %s %s LACP collector %04x (%d us)\n", - netdev->name, label, ntohs ( lacp->collector.max_delay ), - ( ntohs ( lacp->collector.max_delay ) * 10 ) ); - DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); -} - -/** - * Process incoming LACP packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @ret rc Return status code - */ -static int eth_slow_lacp_rx ( struct io_buffer *iobuf, - struct net_device *netdev ) { - union eth_slow_packet *eth_slow = iobuf->data; - struct eth_slow_lacp *lacp = ð_slow->lacp; - - eth_slow_lacp_dump ( iobuf, netdev, "RX" ); - - /* Build response */ - memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) ); - memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) ); - memset ( &lacp->collector, 0, sizeof ( lacp->collector ) ); - lacp->collector.tlv.type = ETH_SLOW_TLV_LACP_COLLECTOR; - lacp->collector.tlv.length = ETH_SLOW_TLV_LACP_COLLECTOR_LEN; - memcpy ( &lacp->partner, &lacp->actor, sizeof ( lacp->partner ) ); - lacp->partner.tlv.type = ETH_SLOW_TLV_LACP_PARTNER; - lacp->partner.tlv.length = ETH_SLOW_TLV_LACP_PARTNER_LEN; - memset ( &lacp->partner.reserved, 0, - sizeof ( lacp->partner.reserved ) ); - memset ( &lacp->actor, 0, sizeof ( lacp->actor ) ); - lacp->actor.tlv.type = ETH_SLOW_TLV_LACP_ACTOR; - lacp->actor.tlv.length = ETH_SLOW_TLV_LACP_ACTOR_LEN; - lacp->actor.system_priority = htons ( LACP_SYSTEM_PRIORITY_MAX ); - memcpy ( lacp->actor.system, netdev->ll_addr, - sizeof ( lacp->actor.system ) ); - lacp->actor.key = htons ( 1 ); - lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX ); - lacp->actor.port = htons ( 1 ); - lacp->actor.state = ( LACP_STATE_AGGREGATABLE | - LACP_STATE_IN_SYNC | - LACP_STATE_COLLECTING | - LACP_STATE_DISTRIBUTING | - ( lacp->partner.state & LACP_STATE_FAST ) ); - lacp->header.version = ETH_SLOW_LACP_VERSION; - - /* Send response */ - eth_slow_lacp_dump ( iobuf, netdev, "TX" ); - return net_tx ( iobuf, netdev, ð_slow_protocol, eth_slow_address, - netdev->ll_addr ); -} - -/** - * Dump marker packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v label "RX" or "TX" - */ -static void eth_slow_marker_dump ( struct io_buffer *iobuf, - struct net_device *netdev, - const char *label ) { - union eth_slow_packet *eth_slow = iobuf->data; - struct eth_slow_marker *marker = ð_slow->marker; - - DBGC ( netdev, "SLOW %s %s marker %s port %04x system %s xact %08x\n", - netdev->name, label, - eth_slow_marker_tlv_name ( marker->marker.tlv.type ), - ntohs ( marker->marker.port ), - eth_ntoa ( marker->marker.system ), - ntohl ( marker->marker.xact ) ); - DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); -} - -/** - * Process incoming marker packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @ret rc Return status code - */ -static int eth_slow_marker_rx ( struct io_buffer *iobuf, - struct net_device *netdev ) { - union eth_slow_packet *eth_slow = iobuf->data; - struct eth_slow_marker *marker = ð_slow->marker; - - eth_slow_marker_dump ( iobuf, netdev, "RX" ); - - if ( marker->marker.tlv.type == ETH_SLOW_TLV_MARKER_REQUEST ) { - /* Send marker response */ - marker->marker.tlv.type = ETH_SLOW_TLV_MARKER_RESPONSE; - eth_slow_marker_dump ( iobuf, netdev, "TX" ); - return net_tx ( iobuf, netdev, ð_slow_protocol, - eth_slow_address, netdev->ll_addr ); - } else { - /* Discard all other marker packets */ - free_iob ( iobuf ); - return -EINVAL; - } -} - -/** - * Process incoming slow packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int eth_slow_rx ( struct io_buffer *iobuf, - struct net_device *netdev, - const void *ll_dest __unused, - const void *ll_source __unused, - unsigned int flags __unused ) { - union eth_slow_packet *eth_slow = iobuf->data; - - /* Sanity checks */ - if ( iob_len ( iobuf ) < sizeof ( *eth_slow ) ) { - free_iob ( iobuf ); - return -EINVAL; - } - - /* Handle according to subtype */ - switch ( eth_slow->header.subtype ) { - case ETH_SLOW_SUBTYPE_LACP: - return eth_slow_lacp_rx ( iobuf, netdev ); - case ETH_SLOW_SUBTYPE_MARKER: - return eth_slow_marker_rx ( iobuf, netdev ); - default: - DBGC ( netdev, "SLOW %s RX unknown subtype %02x\n", - netdev->name, eth_slow->header.subtype ); - free_iob ( iobuf ); - return -EINVAL; - } -} - -/** Slow protocol */ -struct net_protocol eth_slow_protocol __net_protocol = { - .name = "Slow", - .net_proto = htons ( ETH_P_SLOW ), - .rx = eth_slow_rx, -}; diff --git a/qemu/roms/ipxe/src/net/ethernet.c b/qemu/roms/ipxe/src/net/ethernet.c deleted file mode 100644 index 6ddf05344..000000000 --- a/qemu/roms/ipxe/src/net/ethernet.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/if_arp.h> -#include <ipxe/if_ether.h> -#include <ipxe/in.h> -#include <ipxe/netdevice.h> -#include <ipxe/iobuf.h> -#include <ipxe/ethernet.h> - -/** @file - * - * Ethernet protocol - * - */ - -/** Ethernet broadcast MAC address */ -uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - -/** - * Check if Ethernet packet has an 802.3 LLC header - * - * @v ethhdr Ethernet header - * @ret is_llc Packet has 802.3 LLC header - */ -static inline int eth_is_llc_packet ( struct ethhdr *ethhdr ) { - uint8_t len_msb; - - /* Check if the protocol field contains a value short enough - * to be a frame length. The slightly convoluted form of the - * comparison is designed to reduce to a single x86 - * instruction. - */ - len_msb = *( ( uint8_t * ) ðhdr->h_protocol ); - return ( len_msb < 0x06 ); -} - -/** - * Add Ethernet link-layer header - * - * @v netdev Network device - * @v iobuf I/O buffer - * @v ll_dest Link-layer destination address - * @v ll_source Source link-layer address - * @v net_proto Network-layer protocol, in network-byte order - * @ret rc Return status code - */ -int eth_push ( struct net_device *netdev __unused, struct io_buffer *iobuf, - const void *ll_dest, const void *ll_source, - uint16_t net_proto ) { - struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) ); - - /* Build Ethernet header */ - memcpy ( ethhdr->h_dest, ll_dest, ETH_ALEN ); - memcpy ( ethhdr->h_source, ll_source, ETH_ALEN ); - ethhdr->h_protocol = net_proto; - - return 0; -} - -/** - * Remove Ethernet link-layer header - * - * @v netdev Network device - * @v iobuf I/O buffer - * @ret ll_dest Link-layer destination address - * @ret ll_source Source link-layer address - * @ret net_proto Network-layer protocol, in network-byte order - * @ret flags Packet flags - * @ret rc Return status code - */ -int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, - const void **ll_dest, const void **ll_source, - uint16_t *net_proto, unsigned int *flags ) { - struct ethhdr *ethhdr = iobuf->data; - uint16_t *llc_proto; - - /* Sanity check. While in theory we could receive a one-byte - * packet, this will never happen in practice and performing - * the combined length check here avoids the need for an - * additional comparison if we detect an LLC frame. - */ - if ( iob_len ( iobuf ) < ( sizeof ( *ethhdr ) + sizeof ( *llc_proto ))){ - DBG ( "Ethernet packet too short (%zd bytes)\n", - iob_len ( iobuf ) ); - return -EINVAL; - } - - /* Strip off Ethernet header */ - iob_pull ( iobuf, sizeof ( *ethhdr ) ); - - /* Fill in required fields */ - *ll_dest = ethhdr->h_dest; - *ll_source = ethhdr->h_source; - *net_proto = ethhdr->h_protocol; - *flags = ( ( is_multicast_ether_addr ( ethhdr->h_dest ) ? - LL_MULTICAST : 0 ) | - ( is_broadcast_ether_addr ( ethhdr->h_dest ) ? - LL_BROADCAST : 0 ) ); - - /* If this is an LLC frame (with a length in place of the - * protocol field), then use the next two bytes (which happen - * to be the LLC DSAP and SSAP) as the protocol. This allows - * for minimal-overhead support for receiving (rare) LLC - * frames, without requiring a full LLC protocol layer. - */ - if ( eth_is_llc_packet ( ethhdr ) ) { - llc_proto = ( ðhdr->h_protocol + 1 ); - *net_proto = *llc_proto; - } - - return 0; -} - -/** - * Initialise Ethernet address - * - * @v hw_addr Hardware address - * @v ll_addr Link-layer address - */ -void eth_init_addr ( const void *hw_addr, void *ll_addr ) { - memcpy ( ll_addr, hw_addr, ETH_ALEN ); -} - -/** - * Generate random Ethernet address - * - * @v hw_addr Generated hardware address - */ -void eth_random_addr ( void *hw_addr ) { - uint8_t *addr = hw_addr; - unsigned int i; - - for ( i = 0 ; i < ETH_ALEN ; i++ ) - addr[i] = random(); - addr[0] &= ~0x01; /* Clear multicast bit */ - addr[0] |= 0x02; /* Set locally-assigned bit */ -} - -/** - * Transcribe Ethernet address - * - * @v ll_addr Link-layer address - * @ret string Link-layer address in human-readable format - */ -const char * eth_ntoa ( const void *ll_addr ) { - static char buf[18]; /* "00:00:00:00:00:00" */ - const uint8_t *eth_addr = ll_addr; - - sprintf ( buf, "%02x:%02x:%02x:%02x:%02x:%02x", - eth_addr[0], eth_addr[1], eth_addr[2], - eth_addr[3], eth_addr[4], eth_addr[5] ); - return buf; -} - -/** - * Hash multicast address - * - * @v af Address family - * @v net_addr Network-layer address - * @v ll_addr Link-layer address to fill in - * @ret rc Return status code - */ -int eth_mc_hash ( unsigned int af, const void *net_addr, void *ll_addr ) { - const uint8_t *net_addr_bytes = net_addr; - uint8_t *ll_addr_bytes = ll_addr; - - switch ( af ) { - case AF_INET: - ll_addr_bytes[0] = 0x01; - ll_addr_bytes[1] = 0x00; - ll_addr_bytes[2] = 0x5e; - ll_addr_bytes[3] = net_addr_bytes[1] & 0x7f; - ll_addr_bytes[4] = net_addr_bytes[2]; - ll_addr_bytes[5] = net_addr_bytes[3]; - return 0; - case AF_INET6: - ll_addr_bytes[0] = 0x33; - ll_addr_bytes[1] = 0x33; - memcpy ( &ll_addr_bytes[2], &net_addr_bytes[12], 4 ); - return 0; - default: - return -ENOTSUP; - } -} - -/** - * Generate Ethernet-compatible compressed link-layer address - * - * @v ll_addr Link-layer address - * @v eth_addr Ethernet-compatible address to fill in - */ -int eth_eth_addr ( const void *ll_addr, void *eth_addr ) { - memcpy ( eth_addr, ll_addr, ETH_ALEN ); - return 0; -} - -/** - * Generate EUI-64 address - * - * @v ll_addr Link-layer address - * @v eui64 EUI-64 address to fill in - * @ret rc Return status code - */ -int eth_eui64 ( const void *ll_addr, void *eui64 ) { - - memcpy ( ( eui64 + 0 ), ( ll_addr + 0 ), 3 ); - memcpy ( ( eui64 + 5 ), ( ll_addr + 3 ), 3 ); - *( ( uint16_t * ) ( eui64 + 3 ) ) = htons ( 0xfffe ); - return 0; -} - -/** Ethernet protocol */ -struct ll_protocol ethernet_protocol __ll_protocol = { - .name = "Ethernet", - .ll_proto = htons ( ARPHRD_ETHER ), - .hw_addr_len = ETH_ALEN, - .ll_addr_len = ETH_ALEN, - .ll_header_len = ETH_HLEN, - .push = eth_push, - .pull = eth_pull, - .init_addr = eth_init_addr, - .ntoa = eth_ntoa, - .mc_hash = eth_mc_hash, - .eth_addr = eth_eth_addr, - .eui64 = eth_eui64, -}; - -/** - * Allocate Ethernet device - * - * @v priv_size Size of driver private data - * @ret netdev Network device, or NULL - */ -struct net_device * alloc_etherdev ( size_t priv_size ) { - struct net_device *netdev; - - netdev = alloc_netdev ( priv_size ); - if ( netdev ) { - netdev->ll_protocol = ðernet_protocol; - netdev->ll_broadcast = eth_broadcast; - netdev->max_pkt_len = ETH_FRAME_LEN; - } - return netdev; -} - -/* Drag in objects via ethernet_protocol */ -REQUIRING_SYMBOL ( ethernet_protocol ); - -/* Drag in Ethernet configuration */ -REQUIRE_OBJECT ( config_ethernet ); - -/* Drag in Ethernet slow protocols */ -REQUIRE_OBJECT ( eth_slow ); diff --git a/qemu/roms/ipxe/src/net/fakedhcp.c b/qemu/roms/ipxe/src/net/fakedhcp.c deleted file mode 100644 index b6c456a59..000000000 --- a/qemu/roms/ipxe/src/net/fakedhcp.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <ipxe/settings.h> -#include <ipxe/netdevice.h> -#include <ipxe/dhcppkt.h> -#include <ipxe/fakedhcp.h> - -/** @file - * - * Fake DHCP packets - * - */ - -/** - * Copy settings to DHCP packet - * - * @v dest Destination DHCP packet - * @v source Source settings block - * @v encapsulator Encapsulating setting tag number, or zero - * @ret rc Return status code - */ -static int copy_encap_settings ( struct dhcp_packet *dest, - struct settings *source, - unsigned int encapsulator ) { - struct setting setting = { .name = "" }; - unsigned int subtag; - unsigned int tag; - void *data; - int len; - int rc; - - for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) { - tag = DHCP_ENCAP_OPT ( encapsulator, subtag ); - switch ( tag ) { - case DHCP_EB_ENCAP: - case DHCP_VENDOR_ENCAP: - /* Process encapsulated settings */ - if ( ( rc = copy_encap_settings ( dest, source, - tag ) ) != 0 ) - return rc; - break; - default: - /* Copy setting, if present */ - setting.tag = tag; - len = fetch_raw_setting_copy ( source, &setting, &data); - if ( len >= 0 ) { - rc = dhcppkt_store ( dest, tag, data, len ); - free ( data ); - if ( rc != 0 ) - return rc; - } - break; - } - } - - return 0; -} - -/** - * Copy settings to DHCP packet - * - * @v dest Destination DHCP packet - * @v source Source settings block - * @ret rc Return status code - */ -static int copy_settings ( struct dhcp_packet *dest, - struct settings *source ) { - return copy_encap_settings ( dest, source, 0 ); -} - -/** - * Create fake DHCPDISCOVER packet - * - * @v netdev Network device - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Used by external code. - */ -int create_fakedhcpdiscover ( struct net_device *netdev, - void *data, size_t max_len ) { - struct dhcp_packet dhcppkt; - struct in_addr ciaddr = { 0 }; - int rc; - - if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, DHCPDISCOVER, - dhcp_last_xid, ciaddr, data, - max_len ) ) != 0 ) { - DBG ( "Could not create DHCPDISCOVER: %s\n", - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Create fake DHCPACK packet - * - * @v netdev Network device - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Used by external code. - */ -int create_fakedhcpack ( struct net_device *netdev, - void *data, size_t max_len ) { - struct dhcp_packet dhcppkt; - int rc; - - /* Create base DHCPACK packet */ - if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, - dhcp_last_xid, NULL, 0, - data, max_len ) ) != 0 ) { - DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) ); - return rc; - } - - /* Merge in globally-scoped settings, then netdev-specific - * settings. Do it in this order so that netdev-specific - * settings take precedence regardless of stated priorities. - */ - if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) { - DBG ( "Could not set DHCPACK global settings: %s\n", - strerror ( rc ) ); - return rc; - } - if ( ( rc = copy_settings ( &dhcppkt, - netdev_settings ( netdev ) ) ) != 0 ) { - DBG ( "Could not set DHCPACK netdev settings: %s\n", - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Create fake PXE Boot Server ACK packet - * - * @v netdev Network device - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Used by external code. - */ -int create_fakepxebsack ( struct net_device *netdev, - void *data, size_t max_len ) { - struct dhcp_packet dhcppkt; - struct settings *proxy_settings; - struct settings *pxebs_settings; - int rc; - - /* Identify available settings */ - proxy_settings = find_settings ( PROXYDHCP_SETTINGS_NAME ); - pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); - if ( ( ! proxy_settings ) && ( ! pxebs_settings ) ) { - /* No PXE boot server; return the regular DHCPACK */ - return create_fakedhcpack ( netdev, data, max_len ); - } - - /* Create base DHCPACK packet */ - if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, - dhcp_last_xid, NULL, 0, - data, max_len ) ) != 0 ) { - DBG ( "Could not create PXE BS ACK: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Merge in ProxyDHCP options */ - if ( proxy_settings && - ( ( rc = copy_settings ( &dhcppkt, proxy_settings ) ) != 0 ) ) { - DBG ( "Could not copy ProxyDHCP settings: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Merge in BootServerDHCP options, if present */ - if ( pxebs_settings && - ( ( rc = copy_settings ( &dhcppkt, pxebs_settings ) ) != 0 ) ) { - DBG ( "Could not copy PXE BS settings: %s\n", - strerror ( rc ) ); - return rc; - } - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/fc.c b/qemu/roms/ipxe/src/net/fc.c deleted file mode 100644 index 2e8070272..000000000 --- a/qemu/roms/ipxe/src/net/fc.c +++ /dev/null @@ -1,1947 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/refcnt.h> -#include <ipxe/list.h> -#include <ipxe/tables.h> -#include <ipxe/timer.h> -#include <ipxe/retry.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/iobuf.h> -#include <ipxe/fc.h> -#include <ipxe/fcels.h> -#include <ipxe/fcns.h> - -/** @file - * - * Fibre Channel - * - */ - -/** List of Fibre Channel ports */ -LIST_HEAD ( fc_ports ); - -/** List of Fibre Channel peers */ -LIST_HEAD ( fc_peers ); - -/****************************************************************************** - * - * Well-known addresses - * - ****************************************************************************** - */ - -/** Unassigned port ID */ -struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } }; - -/** F_Port contoller port ID */ -struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } }; - -/** Generic services port ID */ -struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } }; - -/** Point-to-point low port ID */ -struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } }; - -/** Point-to-point high port ID */ -struct fc_port_id fc_ptp_high_port_id = { .bytes = { 0x01, 0x01, 0x02 } }; - -/****************************************************************************** - * - * Utility functions - * - ****************************************************************************** - */ - -/** - * Format Fibre Channel port ID - * - * @v id Fibre Channel port ID - * @ret id_text Port ID text - */ -const char * fc_id_ntoa ( const struct fc_port_id *id ) { - static char id_text[ FC_PORT_ID_STRLEN + 1 /* NUL */ ]; - - snprintf ( id_text, sizeof ( id_text ), "%02x.%02x.%02x", - id->bytes[0], id->bytes[1], id->bytes[2] ); - return id_text; -} - -/** - * Parse Fibre Channel port ID - * - * @v id_text Port ID text - * @ret id Fibre Channel port ID - * @ret rc Return status code - */ -int fc_id_aton ( const char *id_text, struct fc_port_id *id ) { - char *ptr = ( ( char * ) id_text ); - unsigned int i = 0; - - while ( 1 ) { - id->bytes[i++] = strtoul ( ptr, &ptr, 16 ); - if ( i == sizeof ( id->bytes ) ) - return ( ( *ptr == '\0' ) ? 0 : -EINVAL ); - if ( *ptr != '.' ) - return -EINVAL; - ptr++; - } -} - -/** - * Format Fibre Channel WWN - * - * @v wwn Fibre Channel WWN - * @ret wwn_text WWN text - */ -const char * fc_ntoa ( const struct fc_name *wwn ) { - static char wwn_text[ FC_NAME_STRLEN + 1 /* NUL */ ]; - - snprintf ( wwn_text, sizeof ( wwn_text ), - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - wwn->bytes[0], wwn->bytes[1], wwn->bytes[2], wwn->bytes[3], - wwn->bytes[4], wwn->bytes[5], wwn->bytes[6], wwn->bytes[7] ); - return wwn_text; -} - -/** - * Parse Fibre Channel WWN - * - * @v wwn_text WWN text - * @ret wwn Fibre Channel WWN - * @ret rc Return status code - */ -int fc_aton ( const char *wwn_text, struct fc_name *wwn ) { - char *ptr = ( ( char * ) wwn_text ); - unsigned int i = 0; - - while ( 1 ) { - wwn->bytes[i++] = strtoul ( ptr, &ptr, 16 ); - if ( i == sizeof ( wwn->bytes ) ) - return ( ( *ptr == '\0' ) ? 0 : -EINVAL ); - if ( *ptr != ':' ) - return -EINVAL; - ptr++; - } -} - -/** - * Fill Fibre Channel socket address - * - * @v sa_fc Fibre Channel socket address to fill in - * @v id Fibre Channel port ID - * @ret sa Socket address - */ -struct sockaddr * fc_fill_sockaddr ( struct sockaddr_fc *sa_fc, - struct fc_port_id *id ) { - union { - struct sockaddr sa; - struct sockaddr_fc fc; - } *u = container_of ( sa_fc, typeof ( *u ), fc ); - - memset ( sa_fc, 0, sizeof ( *sa_fc ) ); - sa_fc->sfc_family = AF_FC; - memcpy ( &sa_fc->sfc_port_id, id, sizeof ( sa_fc->sfc_port_id ) ); - return &u->sa; -} - -/****************************************************************************** - * - * Fibre Channel link state - * - ****************************************************************************** - */ - -/** Default link status code */ -#define EUNKNOWN_LINK_STATUS __einfo_error ( EINFO_EUNKNOWN_LINK_STATUS ) -#define EINFO_EUNKNOWN_LINK_STATUS \ - __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" ) - -/** - * Mark Fibre Channel link as up - * - * @v link Fibre Channel link state monitor - */ -static void fc_link_up ( struct fc_link_state *link ) { - - /* Stop retry timer */ - stop_timer ( &link->timer ); - - /* Record link state */ - link->rc = 0; -} - -/** - * Mark Fibre Channel link as down - * - * @v link Fibre Channel link state monitor - * @v rc Link state - */ -static void fc_link_err ( struct fc_link_state *link, int rc ) { - - /* Record link state */ - if ( rc == 0 ) - rc = -EUNKNOWN_LINK_STATUS; - link->rc = rc; - - /* Schedule another link examination */ - start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY ); -} - -/** - * Examine Fibre Channel link state - * - * @v link Fibre Channel link state monitor - */ -static void fc_link_examine ( struct fc_link_state *link ) { - - link->examine ( link ); -} - -/** - * Handle Fibre Channel link retry timer expiry - */ -static void fc_link_expired ( struct retry_timer *timer, int over __unused ) { - struct fc_link_state *link = - container_of ( timer, struct fc_link_state, timer ); - - /* Schedule another link examination */ - start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY ); - - /* Examine link */ - fc_link_examine ( link ); -} - -/** - * Initialise Fibre Channel link state monitor - * - * @v link Fibre Channel link state monitor - * @v examine Examine link state method - * @v refcnt Reference counter - */ -static void fc_link_init ( struct fc_link_state *link, - void ( * examine ) ( struct fc_link_state *link ), - struct refcnt *refcnt ) { - - link->rc = -EUNKNOWN_LINK_STATUS; - timer_init ( &link->timer, fc_link_expired, refcnt ); - link->examine = examine; -} - -/** - * Start monitoring Fibre Channel link state - * - * @v link Fibre Channel link state monitor - */ -static void fc_link_start ( struct fc_link_state *link ) { - start_timer_nodelay ( &link->timer ); -} - -/** - * Stop monitoring Fibre Channel link state - * - * @v link Fibre Channel link state monitor - */ -static void fc_link_stop ( struct fc_link_state *link ) { - stop_timer ( &link->timer ); -} - -/****************************************************************************** - * - * Fibre Channel exchanges - * - ****************************************************************************** - */ - -/** A Fibre Channel exchange */ -struct fc_exchange { - /** Reference count */ - struct refcnt refcnt; - /** Fibre Channel port */ - struct fc_port *port; - /** List of active exchanges within this port */ - struct list_head list; - - /** Peer port ID */ - struct fc_port_id peer_port_id; - /** Data structure type */ - unsigned int type; - /** Flags */ - unsigned int flags; - /** Local exchange ID */ - uint16_t xchg_id; - /** Peer exchange ID */ - uint16_t peer_xchg_id; - /** Active sequence ID */ - uint8_t seq_id; - /** Active sequence count */ - uint16_t seq_cnt; - - /** Timeout timer */ - struct retry_timer timer; - - /** Upper-layer protocol interface */ - struct interface ulp; -}; - -/** Fibre Channel exchange flags */ -enum fc_exchange_flags { - /** We are the exchange originator */ - FC_XCHG_ORIGINATOR = 0x0001, - /** We have the sequence initiative */ - FC_XCHG_SEQ_INITIATIVE = 0x0002, - /** This is the first sequence of the exchange */ - FC_XCHG_SEQ_FIRST = 0x0004, -}; - -/** Fibre Channel timeout */ -#define FC_TIMEOUT ( 1 * TICKS_PER_SEC ) - -/** - * Create local Fibre Channel exchange identifier - * - * @ret xchg_id Local exchange ID - */ -static unsigned int fc_new_xchg_id ( void ) { - static uint16_t next_id = 0x0000; - - /* We must avoid using FC_RX_ID_UNKNOWN (0xffff) */ - next_id += 2; - return next_id; -} - -/** - * Create local Fibre Channel sequence identifier - * - * @ret seq_id Local sequence identifier - */ -static unsigned int fc_new_seq_id ( void ) { - static uint8_t seq_id = 0x00; - - return (++seq_id); -} - -/** - * Free Fibre Channel exchange - * - * @v refcnt Reference count - */ -static void fc_xchg_free ( struct refcnt *refcnt ) { - struct fc_exchange *xchg = - container_of ( refcnt, struct fc_exchange, refcnt ); - - assert ( ! timer_running ( &xchg->timer ) ); - assert ( list_empty ( &xchg->list ) ); - - fc_port_put ( xchg->port ); - free ( xchg ); -} - -/** - * Close Fibre Channel exchange - * - * @v xchg Fibre Channel exchange - * @v rc Reason for close - */ -static void fc_xchg_close ( struct fc_exchange *xchg, int rc ) { - struct fc_port *port = xchg->port; - - if ( rc != 0 ) { - DBGC2 ( port, "FCXCHG %s/%04x closed: %s\n", - port->name, xchg->xchg_id, strerror ( rc ) ); - } - - /* Stop timer */ - stop_timer ( &xchg->timer ); - - /* If list still holds a reference, remove from list of open - * exchanges and drop list's reference. - */ - if ( ! list_empty ( &xchg->list ) ) { - list_del ( &xchg->list ); - INIT_LIST_HEAD ( &xchg->list ); - ref_put ( &xchg->refcnt ); - } - - /* Shutdown interfaces */ - intf_shutdown ( &xchg->ulp, rc ); -} - -/** - * Handle exchange timeout - * - * @v timer Timeout timer - * @v over Failure indicator - */ -static void fc_xchg_expired ( struct retry_timer *timer, int over __unused ) { - struct fc_exchange *xchg = - container_of ( timer, struct fc_exchange, timer ); - struct fc_port *port = xchg->port; - - DBGC ( port, "FCXCHG %s/%04x timed out\n", port->name, xchg->xchg_id ); - - /* Terminate the exchange */ - fc_xchg_close ( xchg, -ETIMEDOUT ); -} - -/** - * Check Fibre Channel exchange window - * - * @v xchg Fibre Channel exchange - * @ret len Length opf window - */ -static size_t fc_xchg_window ( struct fc_exchange *xchg __unused ) { - - /* We don't currently store the path MTU */ - return FC_LOGIN_DEFAULT_MTU; -} - -/** - * Allocate Fibre Channel I/O buffer - * - * @v xchg Fibre Channel exchange - * @v len Payload length - * @ret iobuf I/O buffer, or NULL - */ -static struct io_buffer * fc_xchg_alloc_iob ( struct fc_exchange *xchg, - size_t len ) { - struct fc_port *port = xchg->port; - struct io_buffer *iobuf; - - iobuf = xfer_alloc_iob ( &port->transport, - ( sizeof ( struct fc_frame_header ) + len ) ); - if ( iobuf ) { - iob_reserve ( iobuf, sizeof ( struct fc_frame_header ) ); - } - return iobuf; -} - -/** - * Transmit data as part of a Fibre Channel exchange - * - * @v xchg Fibre Channel exchange - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fc_xchg_tx ( struct fc_exchange *xchg, struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct fc_port *port = xchg->port; - struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest ); - struct fc_frame_header *fchdr; - unsigned int r_ctl; - unsigned int f_ctl_es; - int rc; - - /* Sanity checks */ - if ( ! ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) ) { - DBGC ( port, "FCXCHG %s/%04x cannot transmit while not " - "holding sequence initiative\n", - port->name, xchg->xchg_id ); - rc = -EBUSY; - goto done; - } - - /* Calculate routing control */ - switch ( xchg->type ) { - case FC_TYPE_ELS: - r_ctl = FC_R_CTL_ELS; - if ( meta->flags & XFER_FL_RESPONSE ) { - r_ctl |= FC_R_CTL_SOL_CTRL; - } else { - r_ctl |= FC_R_CTL_UNSOL_CTRL; - } - break; - case FC_TYPE_CT: - r_ctl = FC_R_CTL_DATA; - if ( meta->flags & XFER_FL_RESPONSE ) { - r_ctl |= FC_R_CTL_SOL_CTRL; - } else { - r_ctl |= FC_R_CTL_UNSOL_CTRL; - } - break; - default: - r_ctl = FC_R_CTL_DATA; - switch ( meta->flags & - ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) { - case ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ): - r_ctl |= FC_R_CTL_CMD_STAT; - break; - case ( XFER_FL_CMD_STAT ): - r_ctl |= FC_R_CTL_UNSOL_CMD; - break; - case ( XFER_FL_RESPONSE ): - r_ctl |= FC_R_CTL_SOL_DATA; - break; - default: - r_ctl |= FC_R_CTL_UNSOL_DATA; - break; - } - break; - } - - /* Calculate exchange and sequence control */ - f_ctl_es = 0; - if ( ! ( xchg->flags & FC_XCHG_ORIGINATOR ) ) - f_ctl_es |= FC_F_CTL_ES_RESPONDER; - if ( xchg->flags & FC_XCHG_SEQ_FIRST ) - f_ctl_es |= FC_F_CTL_ES_FIRST; - if ( meta->flags & XFER_FL_OUT ) - f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_LAST ); - if ( meta->flags & XFER_FL_OVER ) - f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_TRANSFER ); - - /* Create frame header */ - fchdr = iob_push ( iobuf, sizeof ( *fchdr ) ); - memset ( fchdr, 0, sizeof ( *fchdr ) ); - fchdr->r_ctl = r_ctl; - memcpy ( &fchdr->d_id, - ( dest ? &dest->sfc_port_id : &xchg->peer_port_id ), - sizeof ( fchdr->d_id ) ); - memcpy ( &fchdr->s_id, &port->port_id, sizeof ( fchdr->s_id ) ); - fchdr->type = xchg->type; - fchdr->f_ctl_es = f_ctl_es; - fchdr->seq_id = xchg->seq_id; - fchdr->seq_cnt = htons ( xchg->seq_cnt++ ); - fchdr->ox_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ? - xchg->xchg_id : xchg->peer_xchg_id ); - fchdr->rx_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ? - xchg->peer_xchg_id : xchg->xchg_id ); - if ( meta->flags & XFER_FL_ABS_OFFSET ) { - fchdr->f_ctl_misc |= FC_F_CTL_MISC_REL_OFF; - fchdr->parameter = htonl ( meta->offset ); - } - - /* Relinquish sequence initiative if applicable */ - if ( meta->flags & XFER_FL_OVER ) { - xchg->flags &= ~( FC_XCHG_SEQ_INITIATIVE | FC_XCHG_SEQ_FIRST ); - xchg->seq_cnt = 0; - } - - /* Reset timeout */ - start_timer_fixed ( &xchg->timer, FC_TIMEOUT ); - - /* Deliver frame */ - if ( ( rc = xfer_deliver_iob ( &port->transport, - iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( port, "FCXCHG %s/%04x cannot transmit: %s\n", - port->name, xchg->xchg_id, strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** Mapping from Fibre Channel routing control information to xfer metadata */ -static const uint8_t fc_r_ctl_info_meta_flags[ FC_R_CTL_INFO_MASK + 1 ] = { - [FC_R_CTL_UNCAT] = ( 0 ), - [FC_R_CTL_SOL_DATA] = ( XFER_FL_RESPONSE ), - [FC_R_CTL_UNSOL_CTRL] = ( XFER_FL_CMD_STAT ), - [FC_R_CTL_SOL_CTRL] = ( XFER_FL_CMD_STAT ), - [FC_R_CTL_UNSOL_DATA] = ( 0 ), - [FC_R_CTL_DATA_DESC] = ( XFER_FL_CMD_STAT ), - [FC_R_CTL_UNSOL_CMD] = ( XFER_FL_CMD_STAT ), - [FC_R_CTL_CMD_STAT] = ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ), -}; - -/** - * Receive data as part of a Fibre Channel exchange - * - * @v xchg Fibre Channel exchange - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fc_xchg_rx ( struct fc_exchange *xchg, struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct fc_port *port = xchg->port; - struct fc_frame_header *fchdr = iobuf->data; - struct xfer_metadata fc_meta; - struct sockaddr_fc src; - struct sockaddr_fc dest; - int rc; - - /* Record peer exchange ID */ - xchg->peer_xchg_id = - ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ? - fchdr->rx_id : fchdr->ox_id ); - - /* Sequence checks */ - if ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) { - DBGC ( port, "FCXCHG %s/%04x received frame while holding " - "sequence initiative\n", port->name, xchg->xchg_id ); - rc = -EBUSY; - goto done; - } - if ( ntohs ( fchdr->seq_cnt ) != xchg->seq_cnt ) { - DBGC ( port, "FCXCHG %s/%04x received out-of-order frame %d " - "(expected %d)\n", port->name, xchg->xchg_id, - ntohs ( fchdr->seq_cnt ), xchg->seq_cnt ); - rc = -EPIPE; - goto done; - } - if ( xchg->seq_cnt == 0 ) - xchg->seq_id = fchdr->seq_id; - xchg->seq_cnt++; - if ( fchdr->seq_id != xchg->seq_id ) { - DBGC ( port, "FCXCHG %s/%04x received frame for incorrect " - "sequence %02x (expected %02x)\n", port->name, - xchg->xchg_id, fchdr->seq_id, xchg->seq_id ); - rc = -EPIPE; - goto done; - } - - /* Check for end of sequence and transfer of sequence initiative */ - if ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) { - xchg->seq_cnt = 0; - if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) { - xchg->flags |= FC_XCHG_SEQ_INITIATIVE; - xchg->seq_id = fc_new_seq_id(); - } - } - - /* Construct metadata */ - memset ( &fc_meta, 0, sizeof ( fc_meta ) ); - fc_meta.flags = - fc_r_ctl_info_meta_flags[ fchdr->r_ctl & FC_R_CTL_INFO_MASK ]; - if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) { - fc_meta.flags |= XFER_FL_OVER; - } - if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) && - ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) { - fc_meta.flags |= XFER_FL_OUT; - } - if ( fchdr->f_ctl_misc & FC_F_CTL_MISC_REL_OFF ) { - fc_meta.flags |= XFER_FL_ABS_OFFSET; - fc_meta.offset = ntohl ( fchdr->parameter ); - } - fc_meta.src = fc_fill_sockaddr ( &src, &fchdr->s_id ); - fc_meta.dest = fc_fill_sockaddr ( &dest, &fchdr->d_id ); - - /* Reset timeout */ - start_timer_fixed ( &xchg->timer, FC_TIMEOUT ); - - /* Deliver via exchange's ULP interface */ - iob_pull ( iobuf, sizeof ( *fchdr ) ); - if ( ( rc = xfer_deliver ( &xchg->ulp, iob_disown ( iobuf ), - &fc_meta ) ) != 0 ) { - DBGC ( port, "FCXCHG %s/%04x cannot deliver frame: %s\n", - port->name, xchg->xchg_id, strerror ( rc ) ); - goto done; - } - - /* Close exchange if applicable */ - if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) && - ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) { - fc_xchg_close ( xchg, 0 ); - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** Fibre Channel exchange ULP interface operations */ -static struct interface_operation fc_xchg_ulp_op[] = { - INTF_OP ( xfer_deliver, struct fc_exchange *, fc_xchg_tx ), - INTF_OP ( xfer_alloc_iob, struct fc_exchange *, fc_xchg_alloc_iob ), - INTF_OP ( xfer_window, struct fc_exchange *, fc_xchg_window ), - INTF_OP ( intf_close, struct fc_exchange *, fc_xchg_close ), -}; - -/** Fibre Channel exchange ULP interface descriptor */ -static struct interface_descriptor fc_xchg_ulp_desc = - INTF_DESC ( struct fc_exchange, ulp, fc_xchg_ulp_op ); - -/** - * Create new Fibre Channel exchange - * - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret xchg Exchange, or NULL - */ -static struct fc_exchange * fc_xchg_create ( struct fc_port *port, - struct fc_port_id *peer_port_id, - unsigned int type ) { - struct fc_exchange *xchg; - - /* Allocate and initialise structure */ - xchg = zalloc ( sizeof ( *xchg ) ); - if ( ! xchg ) - return NULL; - ref_init ( &xchg->refcnt, fc_xchg_free ); - intf_init ( &xchg->ulp, &fc_xchg_ulp_desc, &xchg->refcnt ); - timer_init ( &xchg->timer, fc_xchg_expired, &xchg->refcnt ); - xchg->port = fc_port_get ( port ); - memcpy ( &xchg->peer_port_id, peer_port_id, - sizeof ( xchg->peer_port_id ) ); - xchg->type = type; - xchg->xchg_id = fc_new_xchg_id(); - xchg->peer_xchg_id = FC_RX_ID_UNKNOWN; - xchg->seq_id = fc_new_seq_id(); - - /* Transfer reference to list of exchanges and return */ - list_add ( &xchg->list, &port->xchgs ); - return xchg; -} - -/** - * Originate a new Fibre Channel exchange - * - * @v parent Interface to which to attach - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret xchg_id Exchange ID, or negative error - */ -int fc_xchg_originate ( struct interface *parent, struct fc_port *port, - struct fc_port_id *peer_port_id, unsigned int type ) { - struct fc_exchange *xchg; - - /* Allocate and initialise structure */ - xchg = fc_xchg_create ( port, peer_port_id, type ); - if ( ! xchg ) - return -ENOMEM; - xchg->flags = ( FC_XCHG_ORIGINATOR | FC_XCHG_SEQ_INITIATIVE | - FC_XCHG_SEQ_FIRST ); - - DBGC2 ( port, "FCXCHG %s/%04x originating to %s (type %02x)\n", - port->name, xchg->xchg_id, fc_id_ntoa ( &xchg->peer_port_id ), - xchg->type ); - - /* Attach to parent interface and return */ - intf_plug_plug ( &xchg->ulp, parent ); - return xchg->xchg_id; -} - -/** - * Open a new responder Fibre Channel exchange - * - * @v port Fibre Channel port - * @v fchdr Fibre Channel frame header - * @ret xchg Fibre Channel exchange, or NULL - */ -static struct fc_exchange * fc_xchg_respond ( struct fc_port *port, - struct fc_frame_header *fchdr ) { - struct fc_exchange *xchg; - struct fc_responder *responder; - unsigned int type = fchdr->type; - int rc; - - /* Allocate and initialise structure */ - xchg = fc_xchg_create ( port, &fchdr->s_id, type ); - if ( ! xchg ) - return NULL; - xchg->seq_id = fchdr->seq_id; - - DBGC2 ( port, "FCXCHG %s/%04x responding to %s xchg %04x (type " - "%02x)\n", port->name, xchg->xchg_id, - fc_id_ntoa ( &xchg->peer_port_id ), - ntohs ( fchdr->ox_id ), xchg->type ); - - /* Find a responder, if any */ - for_each_table_entry ( responder, FC_RESPONDERS ) { - if ( responder->type == type ) { - if ( ( rc = responder->respond ( &xchg->ulp, port, - &fchdr->d_id, - &fchdr->s_id ) ) !=0 ){ - DBGC ( port, "FCXCHG %s/%04x could not " - "respond: %s\n", port->name, - xchg->xchg_id, strerror ( rc ) ); - } - } - break; - } - - /* We may or may not have a ULP attached at this point, but - * the exchange does exist. - */ - return xchg; -} - -/****************************************************************************** - * - * Fibre Channel ports - * - ****************************************************************************** - */ - -/** - * Close Fibre Channel port - * - * @v port Fibre Channel port - * @v rc Reason for close - */ -static void fc_port_close ( struct fc_port *port, int rc ) { - struct fc_exchange *xchg; - struct fc_exchange *tmp; - - DBGC ( port, "FCPORT %s closed\n", port->name ); - - /* Log out port, if necessary */ - if ( fc_link_ok ( &port->link ) ) - fc_port_logout ( port, rc ); - - /* Stop link monitor */ - fc_link_stop ( &port->link ); - - /* Shut down interfaces */ - intf_shutdown ( &port->transport, rc ); - intf_shutdown ( &port->flogi, rc ); - intf_shutdown ( &port->ns_plogi, rc ); - - /* Shut down any remaining exchanges */ - list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list ) - fc_xchg_close ( xchg, rc ); - - /* Remove from list of ports */ - list_del ( &port->list ); - INIT_LIST_HEAD ( &port->list ); -} - -/** - * Identify Fibre Channel exchange by local exchange ID - * - * @v port Fibre Channel port - * @v xchg_id Local exchange ID - * @ret xchg Fibre Channel exchange, or NULL - */ -static struct fc_exchange * fc_port_demux ( struct fc_port *port, - unsigned int xchg_id ) { - struct fc_exchange *xchg; - - list_for_each_entry ( xchg, &port->xchgs, list ) { - if ( xchg->xchg_id == xchg_id ) - return xchg; - } - return NULL; -} - -/** - * Handle received frame from Fibre Channel port - * - * @v port Fibre Channel port - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fc_port_deliver ( struct fc_port *port, struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct fc_frame_header *fchdr = iobuf->data; - unsigned int xchg_id; - struct fc_exchange *xchg; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *fchdr ) ) { - DBGC ( port, "FCPORT %s received underlength frame (%zd " - "bytes)\n", port->name, iob_len ( iobuf ) ); - rc = -EINVAL; - goto err_sanity; - } - - /* Verify local port ID */ - if ( ( memcmp ( &fchdr->d_id, &port->port_id, - sizeof ( fchdr->d_id ) ) != 0 ) && - ( memcmp ( &fchdr->d_id, &fc_f_port_id, - sizeof ( fchdr->d_id ) ) != 0 ) && - ( memcmp ( &port->port_id, &fc_empty_port_id, - sizeof ( port->port_id ) ) != 0 ) ) { - DBGC ( port, "FCPORT %s received frame for incorrect port ID " - "%s\n", port->name, fc_id_ntoa ( &fchdr->d_id ) ); - rc = -ENOTCONN; - goto err_port_id; - } - - /* Demultiplex amongst active exchanges */ - xchg_id = ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ? - fchdr->ox_id : fchdr->rx_id ); - xchg = fc_port_demux ( port, xchg_id ); - - /* If we have no active exchange and this frame starts a new - * exchange, try to create a new responder exchange - */ - if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_FIRST ) && - ( fchdr->seq_cnt == 0 ) ) { - - /* Create new exchange */ - xchg = fc_xchg_respond ( port, fchdr ); - if ( ! xchg ) { - DBGC ( port, "FCPORT %s cannot create new exchange\n", - port->name ); - rc = -ENOMEM; - goto err_respond; - } - } - - /* Fail if no exchange exists */ - if ( ! xchg ) { - DBGC ( port, "FCPORT %s xchg %04x unknown\n", - port->name, xchg_id ); - rc = -ENOTCONN; - goto err_no_xchg; - } - - /* Pass received frame to exchange */ - ref_get ( &xchg->refcnt ); - if ( ( rc = fc_xchg_rx ( xchg, iob_disown ( iobuf ), meta ) ) != 0 ) - goto err_xchg_rx; - - err_xchg_rx: - ref_put ( &xchg->refcnt ); - err_no_xchg: - err_respond: - err_port_id: - err_sanity: - free_iob ( iobuf ); - return rc; -} - -/** - * Log in Fibre Channel port - * - * @v port Fibre Channel port - * @v port_id Local port ID - * @v link_node_wwn Link node name - * @v link_port_wwn Link port name - * @v has_fabric Link is to a fabric - * @ret rc Return status code - */ -int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id, - const struct fc_name *link_node_wwn, - const struct fc_name *link_port_wwn, int has_fabric ) { - struct fc_peer *peer; - struct fc_peer *tmp; - int rc; - - /* Perform implicit logout if logged in and details differ */ - if ( fc_link_ok ( &port->link ) && - ( ( ( !! ( port->flags & FC_PORT_HAS_FABRIC ) ) != - ( !! has_fabric ) ) || - ( memcmp ( &port->link_node_wwn, link_node_wwn, - sizeof ( port->link_node_wwn ) ) != 0 ) || - ( memcmp ( &port->link_port_wwn, link_port_wwn, - sizeof ( port->link_port_wwn ) ) != 0 ) || - ( has_fabric && - ( memcmp ( &port->port_id, port_id, - sizeof ( port->port_id ) ) != 0 ) ) ) ) { - fc_port_logout ( port, 0 ); - } - - /* Log in, if applicable */ - if ( ! fc_link_ok ( &port->link ) ) { - - /* Record link port name */ - memcpy ( &port->link_node_wwn, link_node_wwn, - sizeof ( port->link_node_wwn ) ); - memcpy ( &port->link_port_wwn, link_port_wwn, - sizeof ( port->link_port_wwn ) ); - DBGC ( port, "FCPORT %s logged in to %s", - port->name, fc_ntoa ( &port->link_node_wwn ) ); - DBGC ( port, " port %s\n", fc_ntoa ( &port->link_port_wwn ) ); - - /* Calculate local (and possibly remote) port IDs */ - if ( has_fabric ) { - port->flags |= FC_PORT_HAS_FABRIC; - memcpy ( &port->port_id, port_id, - sizeof ( port->port_id ) ); - } else { - port->flags &= ~FC_PORT_HAS_FABRIC; - if ( memcmp ( &port->port_wwn, link_port_wwn, - sizeof ( port->port_wwn ) ) > 0 ) { - memcpy ( &port->port_id, &fc_ptp_high_port_id, - sizeof ( port->port_id ) ); - memcpy ( &port->ptp_link_port_id, - &fc_ptp_low_port_id, - sizeof ( port->ptp_link_port_id ) ); - } else { - memcpy ( &port->port_id, &fc_ptp_low_port_id, - sizeof ( port->port_id ) ); - memcpy ( &port->ptp_link_port_id, - &fc_ptp_high_port_id, - sizeof ( port->ptp_link_port_id ) ); - } - } - DBGC ( port, "FCPORT %s logged in via a %s, with local ID " - "%s\n", port->name, - ( ( port->flags & FC_PORT_HAS_FABRIC ) ? - "fabric" : "point-to-point link" ), - fc_id_ntoa ( &port->port_id ) ); - } - - /* Log in to name server, if attached to a fabric */ - if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) { - - DBGC ( port, "FCPORT %s attempting login to name server\n", - port->name ); - - intf_restart ( &port->ns_plogi, -ECANCELED ); - if ( ( rc = fc_els_plogi ( &port->ns_plogi, port, - &fc_gs_port_id ) ) != 0 ) { - DBGC ( port, "FCPORT %s could not initiate name " - "server PLOGI: %s\n", - port->name, strerror ( rc ) ); - fc_port_logout ( port, rc ); - return rc; - } - } - - /* Record login */ - fc_link_up ( &port->link ); - - /* Notify peers of link state change */ - list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) { - fc_peer_get ( peer ); - fc_link_examine ( &peer->link ); - fc_peer_put ( peer ); - } - - return 0; -} - -/** - * Log out Fibre Channel port - * - * @v port Fibre Channel port - * @v rc Reason for logout - */ -void fc_port_logout ( struct fc_port *port, int rc ) { - struct fc_peer *peer; - struct fc_peer *tmp; - - DBGC ( port, "FCPORT %s logged out: %s\n", - port->name, strerror ( rc ) ); - - /* Erase port details */ - memset ( &port->port_id, 0, sizeof ( port->port_id ) ); - port->flags = 0; - - /* Record logout */ - fc_link_err ( &port->link, rc ); - - /* Notify peers of link state change */ - list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) { - fc_peer_get ( peer ); - fc_link_examine ( &peer->link ); - fc_peer_put ( peer ); - } -} - -/** - * Handle FLOGI completion - * - * @v port Fibre Channel port - * @v rc Reason for completion - */ -static void fc_port_flogi_done ( struct fc_port *port, int rc ) { - - intf_restart ( &port->flogi, rc ); - - if ( rc != 0 ) - fc_port_logout ( port, rc ); -} - -/** - * Handle name server PLOGI completion - * - * @v port Fibre Channel port - * @v rc Reason for completion - */ -static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) { - - intf_restart ( &port->ns_plogi, rc ); - - if ( rc == 0 ) { - port->flags |= FC_PORT_HAS_NS; - DBGC ( port, "FCPORT %s logged in to name server\n", - port->name ); - } else { - DBGC ( port, "FCPORT %s could not log in to name server: %s\n", - port->name, strerror ( rc ) ); - /* Absence of a name server is not a fatal error */ - } -} - -/** - * Examine Fibre Channel port link state - * - * @ link Fibre Channel link state monitor - */ -static void fc_port_examine ( struct fc_link_state *link ) { - struct fc_port *port = container_of ( link, struct fc_port, link ); - int rc; - - /* Do nothing if already logged in */ - if ( fc_link_ok ( &port->link ) ) - return; - - DBGC ( port, "FCPORT %s attempting login\n", port->name ); - - /* Try to create FLOGI ELS */ - intf_restart ( &port->flogi, -ECANCELED ); - if ( ( rc = fc_els_flogi ( &port->flogi, port ) ) != 0 ) { - DBGC ( port, "FCPORT %s could not initiate FLOGI: %s\n", - port->name, strerror ( rc ) ); - fc_port_logout ( port, rc ); - return; - } -} - -/** - * Handle change of flow control window - * - * @v port Fibre Channel port - */ -static void fc_port_window_changed ( struct fc_port *port ) { - size_t window; - - /* Check if transport layer is ready */ - window = xfer_window ( &port->transport ); - if ( window > 0 ) { - - /* Transport layer is ready. Start login if the link - * is not already up. - */ - if ( ! fc_link_ok ( &port->link ) ) - fc_link_start ( &port->link ); - - } else { - - /* Transport layer is not ready. Log out port and - * wait for transport layer before attempting log in - * again. - */ - fc_port_logout ( port, -ENOTCONN ); - fc_link_stop ( &port->link ); - } -} - -/** Fibre Channel port transport interface operations */ -static struct interface_operation fc_port_transport_op[] = { - INTF_OP ( xfer_deliver, struct fc_port *, fc_port_deliver ), - INTF_OP ( xfer_window_changed, struct fc_port *, - fc_port_window_changed ), - INTF_OP ( intf_close, struct fc_port *, fc_port_close ), -}; - -/** Fibre Channel port transport interface descriptor */ -static struct interface_descriptor fc_port_transport_desc = - INTF_DESC ( struct fc_port, transport, fc_port_transport_op ); - -/** Fibre Channel port FLOGI interface operations */ -static struct interface_operation fc_port_flogi_op[] = { - INTF_OP ( intf_close, struct fc_port *, fc_port_flogi_done ), -}; - -/** Fibre Channel port FLOGI interface descriptor */ -static struct interface_descriptor fc_port_flogi_desc = - INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op ); - -/** Fibre Channel port name server PLOGI interface operations */ -static struct interface_operation fc_port_ns_plogi_op[] = { - INTF_OP ( intf_close, struct fc_port *, fc_port_ns_plogi_done ), -}; - -/** Fibre Channel port name server PLOGI interface descriptor */ -static struct interface_descriptor fc_port_ns_plogi_desc = - INTF_DESC ( struct fc_port, ns_plogi, fc_port_ns_plogi_op ); - -/** - * Create Fibre Channel port - * - * @v transport Transport interface - * @v node Fibre Channel node name - * @v port Fibre Channel port name - * @v name Symbolic port name - * @ret rc Return status code - */ -int fc_port_open ( struct interface *transport, const struct fc_name *node_wwn, - const struct fc_name *port_wwn, const char *name ) { - struct fc_port *port; - - /* Allocate and initialise structure */ - port = zalloc ( sizeof ( *port ) ); - if ( ! port ) - return -ENOMEM; - ref_init ( &port->refcnt, NULL ); - intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt ); - fc_link_init ( &port->link, fc_port_examine, &port->refcnt ); - intf_init ( &port->flogi, &fc_port_flogi_desc, &port->refcnt ); - intf_init ( &port->ns_plogi, &fc_port_ns_plogi_desc, &port->refcnt ); - list_add_tail ( &port->list, &fc_ports ); - INIT_LIST_HEAD ( &port->xchgs ); - memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) ); - memcpy ( &port->port_wwn, port_wwn, sizeof ( port->port_wwn ) ); - snprintf ( port->name, sizeof ( port->name ), "%s", name ); - - DBGC ( port, "FCPORT %s opened as %s", - port->name, fc_ntoa ( &port->node_wwn ) ); - DBGC ( port, " port %s\n", fc_ntoa ( &port->port_wwn ) ); - - /* Attach to transport layer, mortalise self, and return */ - intf_plug_plug ( &port->transport, transport ); - ref_put ( &port->refcnt ); - return 0; -} - -/** - * Find Fibre Channel port by name - * - * @v name Fibre Channel port name - * @ret port Fibre Channel port, or NULL - */ -struct fc_port * fc_port_find ( const char *name ) { - struct fc_port *port; - - list_for_each_entry ( port, &fc_ports, list ) { - if ( strcmp ( name, port->name ) == 0 ) - return port; - } - return NULL; -} - -/****************************************************************************** - * - * Fibre Channel peers - * - ****************************************************************************** - */ - -/** - * Close Fibre Channel peer - * - * @v peer Fibre Channel peer - * @v rc Reason for close - */ -static void fc_peer_close ( struct fc_peer *peer, int rc ) { - - DBGC ( peer, "FCPEER %s closed: %s\n", - fc_ntoa ( &peer->port_wwn ) , strerror ( rc ) ); - - /* Sanity check */ - assert ( list_empty ( &peer->ulps ) ); - - /* Stop link timer */ - fc_link_stop ( &peer->link ); - - /* Shut down interfaces */ - intf_shutdown ( &peer->plogi, rc ); - - /* Remove from list of peers */ - list_del ( &peer->list ); - INIT_LIST_HEAD ( &peer->list ); -} - -/** - * Increment Fibre Channel peer active usage count - * - * @v peer Fibre Channel peer - */ -static void fc_peer_increment ( struct fc_peer *peer ) { - - /* Increment our usage count */ - peer->usage++; -} - -/** - * Decrement Fibre Channel peer active usage count - * - * @v peer Fibre Channel peer - */ -static void fc_peer_decrement ( struct fc_peer *peer ) { - - /* Sanity check */ - assert ( peer->usage > 0 ); - - /* Decrement our usage count and log out if we reach zero */ - if ( --(peer->usage) == 0 ) - fc_peer_logout ( peer, 0 ); -} - -/** - * Log in Fibre Channel peer - * - * @v peer Fibre Channel peer - * @v port Fibre Channel port - * @v port_id Port ID - * @ret rc Return status code - */ -int fc_peer_login ( struct fc_peer *peer, struct fc_port *port, - struct fc_port_id *port_id ) { - struct fc_ulp *ulp; - struct fc_ulp *tmp; - - /* Perform implicit logout if logged in and details differ */ - if ( fc_link_ok ( &peer->link ) && - ( ( peer->port != port ) || - ( memcmp ( &peer->port_id, port_id, - sizeof ( peer->port_id ) ) !=0 ) ) ) { - fc_peer_logout ( peer, 0 ); - } - - /* Log in, if applicable */ - if ( ! fc_link_ok ( &peer->link ) ) { - - /* Record peer details */ - assert ( peer->port == NULL ); - peer->port = fc_port_get ( port ); - memcpy ( &peer->port_id, port_id, sizeof ( peer->port_id ) ); - DBGC ( peer, "FCPEER %s logged in via %s as %s\n", - fc_ntoa ( &peer->port_wwn ), peer->port->name, - fc_id_ntoa ( &peer->port_id ) ); - - /* Add login reference */ - fc_peer_get ( peer ); - } - - /* Record login */ - fc_link_up ( &peer->link ); - - /* Notify ULPs of link state change */ - list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) { - fc_ulp_get ( ulp ); - fc_link_examine ( &ulp->link ); - fc_ulp_put ( ulp ); - } - - return 0; -} - -/** - * Log out Fibre Channel peer - * - * @v peer Fibre Channel peer - * @v rc Reason for logout - */ -void fc_peer_logout ( struct fc_peer *peer, int rc ) { - struct fc_ulp *ulp; - struct fc_ulp *tmp; - - DBGC ( peer, "FCPEER %s logged out: %s\n", - fc_ntoa ( &peer->port_wwn ), strerror ( rc ) ); - - /* Drop login reference, if applicable */ - if ( fc_link_ok ( &peer->link ) ) - fc_peer_put ( peer ); - - /* Erase peer details */ - fc_port_put ( peer->port ); - peer->port = NULL; - - /* Record logout */ - fc_link_err ( &peer->link, rc ); - - /* Notify ULPs of link state change */ - list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) { - fc_ulp_get ( ulp ); - fc_link_examine ( &ulp->link ); - fc_ulp_put ( ulp ); - } - - /* Close peer if there are no active users */ - if ( peer->usage == 0 ) - fc_peer_close ( peer, rc ); -} - -/** - * Handle PLOGI completion - * - * @v peer Fibre Channel peer - * @v rc Reason for completion - */ -static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) { - - intf_restart ( &peer->plogi, rc ); - - if ( rc != 0 ) - fc_peer_logout ( peer, rc ); -} - -/** - * Initiate PLOGI - * - * @v peer Fibre Channel peer - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret rc Return status code - */ -static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port, - struct fc_port_id *peer_port_id ) { - int rc; - - /* Try to create PLOGI ELS */ - intf_restart ( &peer->plogi, -ECANCELED ); - if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) { - DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n", - fc_ntoa ( &peer->port_wwn ), strerror ( rc ) ); - fc_peer_logout ( peer, rc ); - return rc; - } - - return 0; -} - -/** - * Examine Fibre Channel peer link state - * - * @ link Fibre Channel link state monitor - */ -static void fc_peer_examine ( struct fc_link_state *link ) { - struct fc_peer *peer = container_of ( link, struct fc_peer, link ); - struct fc_port *port; - int rc; - - /* Check to see if underlying port link has gone down */ - if ( peer->port && ( ! fc_link_ok ( &peer->port->link ) ) ) { - fc_peer_logout ( peer, -ENOTCONN ); - return; - } - - /* Do nothing if already logged in */ - if ( fc_link_ok ( &peer->link ) ) - return; - - DBGC ( peer, "FCPEER %s attempting login\n", - fc_ntoa ( &peer->port_wwn ) ); - - /* Sanity check */ - assert ( peer->port == NULL ); - - /* First, look for a port with the peer attached via a - * point-to-point link. - */ - list_for_each_entry ( port, &fc_ports, list ) { - if ( fc_link_ok ( &port->link ) && - ( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) && - ( memcmp ( &peer->port_wwn, &port->link_port_wwn, - sizeof ( peer->port_wwn ) ) == 0 ) ) { - /* Use this peer port ID, and stop looking */ - fc_peer_plogi ( peer, port, &port->ptp_link_port_id ); - return; - } - } - - /* If the peer is not directly attached, try initiating a name - * server lookup on any suitable ports. - */ - list_for_each_entry ( port, &fc_ports, list ) { - if ( fc_link_ok ( &port->link ) && - ( port->flags & FC_PORT_HAS_FABRIC ) && - ( port->flags & FC_PORT_HAS_NS ) ) { - if ( ( rc = fc_ns_query ( peer, port, - fc_peer_plogi ) ) != 0 ) { - DBGC ( peer, "FCPEER %s could not attempt " - "name server lookup on %s: %s\n", - fc_ntoa ( &peer->port_wwn ), port->name, - strerror ( rc ) ); - /* Non-fatal */ - } - } - } -} - -/** Fibre Channel peer PLOGI interface operations */ -static struct interface_operation fc_peer_plogi_op[] = { - INTF_OP ( intf_close, struct fc_peer *, fc_peer_plogi_done ), -}; - -/** Fibre Channel peer PLOGI interface descriptor */ -static struct interface_descriptor fc_peer_plogi_desc = - INTF_DESC ( struct fc_peer, plogi, fc_peer_plogi_op ); - -/** - * Create Fibre Channel peer - * - * @v port_wwn Node name - * @ret peer Fibre Channel peer, or NULL - */ -static struct fc_peer * fc_peer_create ( const struct fc_name *port_wwn ) { - struct fc_peer *peer; - - /* Allocate and initialise structure */ - peer = zalloc ( sizeof ( *peer ) ); - if ( ! peer ) - return NULL; - ref_init ( &peer->refcnt, NULL ); - fc_link_init ( &peer->link, fc_peer_examine, &peer->refcnt ); - intf_init ( &peer->plogi, &fc_peer_plogi_desc, &peer->refcnt ); - list_add_tail ( &peer->list, &fc_peers ); - memcpy ( &peer->port_wwn, port_wwn, sizeof ( peer->port_wwn ) ); - INIT_LIST_HEAD ( &peer->ulps ); - - /* Start link monitor */ - fc_link_start ( &peer->link ); - - DBGC ( peer, "FCPEER %s created\n", fc_ntoa ( &peer->port_wwn ) ); - return peer; -} - -/** - * Get Fibre Channel peer by node name - * - * @v port_wwn Node name - * @ret peer Fibre Channel peer, or NULL - */ -struct fc_peer * fc_peer_get_wwn ( const struct fc_name *port_wwn ) { - struct fc_peer *peer; - - /* Look for an existing peer */ - list_for_each_entry ( peer, &fc_peers, list ) { - if ( memcmp ( &peer->port_wwn, port_wwn, - sizeof ( peer->port_wwn ) ) == 0 ) - return fc_peer_get ( peer ); - } - - /* Create a new peer */ - peer = fc_peer_create ( port_wwn ); - if ( ! peer ) - return NULL; - - return peer; -} - -/** - * Get Fibre Channel peer by port ID - * - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret peer Fibre Channel peer, or NULL - */ -struct fc_peer * fc_peer_get_port_id ( struct fc_port *port, - const struct fc_port_id *peer_port_id ){ - struct fc_peer *peer; - - /* Look for an existing peer */ - list_for_each_entry ( peer, &fc_peers, list ) { - if ( ( peer->port == port ) && - ( memcmp ( &peer->port_id, peer_port_id, - sizeof ( peer->port_id ) ) == 0 ) ) - return fc_peer_get ( peer ); - } - - /* Cannot create a new peer, since we have no port name to use */ - return NULL; -} - -/****************************************************************************** - * - * Fibre Channel upper-layer protocols - * - ****************************************************************************** - */ - -/** - * Free Fibre Channel upper-layer protocol - * - * @v refcnt Reference count - */ -static void fc_ulp_free ( struct refcnt *refcnt ) { - struct fc_ulp *ulp = container_of ( refcnt, struct fc_ulp, refcnt ); - - fc_peer_put ( ulp->peer ); - free ( ulp ); -} - -/** - * Close Fibre Channel upper-layer protocol - * - * @v ulp Fibre Channel upper-layer protocol - * @v rc Reason for close - */ -static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) { - - DBGC ( ulp, "FCULP %s/%02x closed: %s\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) ); - - /* Sanity check */ - assert ( list_empty ( &ulp->users ) ); - - /* Stop link monitor */ - fc_link_stop ( &ulp->link ); - - /* Shut down interfaces */ - intf_shutdown ( &ulp->prli, rc ); - - /* Remove from list of ULPs */ - list_del ( &ulp->list ); - INIT_LIST_HEAD ( &ulp->list ); -} - -/** - * Attach Fibre Channel upper-layer protocol user - * - * @v ulp Fibre Channel upper-layer protocol - * @v user Fibre Channel upper-layer protocol user - */ -void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) { - - /* Sanity check */ - assert ( user->ulp == NULL ); - - /* Increment peer's usage count */ - fc_peer_increment ( ulp->peer ); - - /* Attach user */ - user->ulp = fc_ulp_get ( ulp ); - list_add ( &user->list, &ulp->users ); -} - -/** - * Detach Fibre Channel upper-layer protocol user - * - * @v user Fibre Channel upper-layer protocol user - */ -void fc_ulp_detach ( struct fc_ulp_user *user ) { - struct fc_ulp *ulp = user->ulp; - - /* Do nothing if not attached */ - if ( ! ulp ) - return; - - /* Sanity checks */ - list_check_contains_entry ( user, &ulp->users, list ); - - /* Detach user and log out if no users remain */ - list_del ( &user->list ); - if ( list_empty ( &ulp->users ) ) - fc_ulp_logout ( ulp, 0 ); - - /* Decrement our peer's usage count */ - fc_peer_decrement ( ulp->peer ); - - /* Drop reference */ - user->ulp = NULL; - fc_ulp_put ( ulp ); -} - -/** - * Log in Fibre Channel upper-layer protocol - * - * @v ulp Fibre Channel upper-layer protocol - * @v param Service parameters - * @v param_len Length of service parameters - * @v originated Login was originated by us - * @ret rc Return status code - */ -int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, - int originated ) { - struct fc_ulp_user *user; - struct fc_ulp_user *tmp; - - /* Perform implicit logout if logged in and service parameters differ */ - if ( fc_link_ok ( &ulp->link ) && - ( ( ulp->param_len != param_len ) || - ( memcmp ( ulp->param, param, ulp->param_len ) != 0 ) ) ) { - fc_ulp_logout ( ulp, 0 ); - } - - /* Work around a bug in some versions of the Linux Fibre - * Channel stack, which fail to fully initialise image pairs - * established via a PRLI originated by the Linux stack - * itself. - */ - if ( originated ) - ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK; - if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) { - DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around " - "Linux bug\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); - fc_link_stop ( &ulp->link ); - fc_link_start ( &ulp->link ); - return 0; - } - - /* Log in, if applicable */ - if ( ! fc_link_ok ( &ulp->link ) ) { - - /* Record service parameters */ - assert ( ulp->param == NULL ); - assert ( ulp->param_len == 0 ); - ulp->param = malloc ( param_len ); - if ( ! ulp->param ) { - DBGC ( ulp, "FCULP %s/%02x could not record " - "parameters\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); - return -ENOMEM; - } - memcpy ( ulp->param, param, param_len ); - ulp->param_len = param_len; - DBGC ( ulp, "FCULP %s/%02x logged in with parameters:\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); - DBGC_HDA ( ulp, 0, ulp->param, ulp->param_len ); - - /* Add login reference */ - fc_ulp_get ( ulp ); - } - - /* Record login */ - fc_link_up ( &ulp->link ); - - /* Notify users of link state change */ - list_for_each_entry_safe ( user, tmp, &ulp->users, list ) { - fc_ulp_user_get ( user ); - user->examine ( user ); - fc_ulp_user_put ( user ); - } - - return 0; -} - -/** - * Log out Fibre Channel upper-layer protocol - * - * @v ulp Fibre Channel upper-layer protocol - * @v rc Reason for logout - */ -void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) { - struct fc_ulp_user *user; - struct fc_ulp_user *tmp; - - DBGC ( ulp, "FCULP %s/%02x logged out: %s\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) ); - - /* Drop login reference, if applicable */ - if ( fc_link_ok ( &ulp->link ) ) - fc_ulp_put ( ulp ); - - /* Discard service parameters */ - free ( ulp->param ); - ulp->param = NULL; - ulp->param_len = 0; - ulp->flags = 0; - - /* Record logout */ - fc_link_err ( &ulp->link, rc ); - - /* Notify users of link state change */ - list_for_each_entry_safe ( user, tmp, &ulp->users, list ) { - fc_ulp_user_get ( user ); - user->examine ( user ); - fc_ulp_user_put ( user ); - } - - /* Close ULP if there are no clients attached */ - if ( list_empty ( &ulp->users ) ) - fc_ulp_close ( ulp, rc ); -} - -/** - * Handle PRLI completion - * - * @v ulp Fibre Channel upper-layer protocol - * @v rc Reason for completion - */ -static void fc_ulp_prli_done ( struct fc_ulp *ulp, int rc ) { - - intf_restart ( &ulp->prli, rc ); - - if ( rc != 0 ) - fc_ulp_logout ( ulp, rc ); -} - -/** - * Examine Fibre Channel upper-layer protocol link state - * - * @ link Fibre Channel link state monitor - */ -static void fc_ulp_examine ( struct fc_link_state *link ) { - struct fc_ulp *ulp = container_of ( link, struct fc_ulp, link ); - int rc; - - /* Check to see if underlying peer link has gone down */ - if ( ! fc_link_ok ( &ulp->peer->link ) ) { - fc_ulp_logout ( ulp, -ENOTCONN ); - return; - } - - /* Do nothing if already logged in */ - if ( fc_link_ok ( &ulp->link ) && - ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) - return; - - DBGC ( ulp, "FCULP %s/%02x attempting login\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); - - /* Try to create PRLI ELS */ - intf_restart ( &ulp->prli, -ECANCELED ); - if ( ( rc = fc_els_prli ( &ulp->prli, ulp->peer->port, - &ulp->peer->port_id, ulp->type ) ) != 0 ) { - DBGC ( ulp, "FCULP %s/%02x could not initiate PRLI: %s\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, - strerror ( rc ) ); - fc_ulp_logout ( ulp, rc ); - return; - } -} - -/** Fibre Channel upper-layer protocol PRLI interface operations */ -static struct interface_operation fc_ulp_prli_op[] = { - INTF_OP ( intf_close, struct fc_ulp *, fc_ulp_prli_done ), -}; - -/** Fibre Channel upper-layer protocol PRLI interface descriptor */ -static struct interface_descriptor fc_ulp_prli_desc = - INTF_DESC ( struct fc_ulp, prli, fc_ulp_prli_op ); - -/** - * Create Fibre Channel upper-layer protocl - * - * @v peer Fibre Channel peer - * @v type Type - * @ret ulp Fibre Channel upper-layer protocol, or NULL - */ -static struct fc_ulp * fc_ulp_create ( struct fc_peer *peer, - unsigned int type ) { - struct fc_ulp *ulp; - - /* Allocate and initialise structure */ - ulp = zalloc ( sizeof ( *ulp ) ); - if ( ! ulp ) - return NULL; - ref_init ( &ulp->refcnt, fc_ulp_free ); - fc_link_init ( &ulp->link, fc_ulp_examine, &ulp->refcnt ); - intf_init ( &ulp->prli, &fc_ulp_prli_desc, &ulp->refcnt ); - ulp->peer = fc_peer_get ( peer ); - list_add_tail ( &ulp->list, &peer->ulps ); - ulp->type = type; - INIT_LIST_HEAD ( &ulp->users ); - - /* Start link state monitor */ - fc_link_start ( &ulp->link ); - - DBGC ( ulp, "FCULP %s/%02x created\n", - fc_ntoa ( &ulp->peer->port_wwn ), ulp->type ); - return ulp; -} - -/** - * Get Fibre Channel upper-layer protocol by peer and type - * - * @v peer Fibre Channel peer - * @v type Type - * @ret ulp Fibre Channel upper-layer protocol, or NULL - */ -static struct fc_ulp * fc_ulp_get_type ( struct fc_peer *peer, - unsigned int type ) { - struct fc_ulp *ulp; - - /* Look for an existing ULP */ - list_for_each_entry ( ulp, &peer->ulps, list ) { - if ( ulp->type == type ) - return fc_ulp_get ( ulp ); - } - - /* Create a new ULP */ - ulp = fc_ulp_create ( peer, type ); - if ( ! ulp ) - return NULL; - - return ulp; -} - -/** - * Get Fibre Channel upper-layer protocol by port name and type - * - * @v port_wwn Port name - * @v type Type - * @ret ulp Fibre Channel upper-layer protocol, or NULL - */ -struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn, - unsigned int type ) { - struct fc_ulp *ulp; - struct fc_peer *peer; - - /* Get peer */ - peer = fc_peer_get_wwn ( port_wwn ); - if ( ! peer ) - goto err_peer_get_wwn; - - /* Get ULP */ - ulp = fc_ulp_get_type ( peer, type ); - if ( ! ulp ) - goto err_ulp_get_type; - - /* Drop temporary reference to peer */ - fc_peer_put ( peer ); - - return ulp; - - fc_ulp_put ( ulp ); - err_ulp_get_type: - fc_peer_put ( peer ); - err_peer_get_wwn: - return NULL; -} - -/** - * Get Fibre Channel upper-layer protocol by port ID and type - * - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @v type Type - * @ret ulp Fibre Channel upper-layer protocol, or NULL - */ -struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port, - const struct fc_port_id *peer_port_id, - unsigned int type ) { - struct fc_ulp *ulp; - struct fc_peer *peer; - - /* Get peer */ - peer = fc_peer_get_port_id ( port, peer_port_id ); - if ( ! peer ) - goto err_peer_get_wwn; - - /* Get ULP */ - ulp = fc_ulp_get_type ( peer, type ); - if ( ! ulp ) - goto err_ulp_get_type; - - /* Drop temporary reference to peer */ - fc_peer_put ( peer ); - - return ulp; - - fc_ulp_put ( ulp ); - err_ulp_get_type: - fc_peer_put ( peer ); - err_peer_get_wwn: - return NULL; -} - -/* Drag in objects via fc_ports */ -REQUIRING_SYMBOL ( fc_ports ); - -/* Drag in Fibre Channel configuration */ -REQUIRE_OBJECT ( config_fc ); diff --git a/qemu/roms/ipxe/src/net/fcels.c b/qemu/roms/ipxe/src/net/fcels.c deleted file mode 100644 index 5fc27cef4..000000000 --- a/qemu/roms/ipxe/src/net/fcels.c +++ /dev/null @@ -1,1343 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/iobuf.h> -#include <ipxe/process.h> -#include <ipxe/fc.h> -#include <ipxe/fcels.h> - -/** @file - * - * Fibre Channel Extended Link Services - * - */ - -/** Fibre Channel ELS transaction debug message format */ -#define FCELS_FMT "FCELS %s %s %s %s" - -/** Fibre Channel ELS transaction debug message arguments */ -#define FCELS_ARGS( els ) \ - (els)->port->name, \ - ( (els)->handler ? (els)->handler->name : "unknown ELS" ), \ - ( fc_els_is_request ( els ) ? "to" : "from" ), \ - fc_id_ntoa ( &(els)->peer_port_id ) - -struct fc_els_handler fc_els_unknown_handler __fc_els_handler; - -/** - * Free Fibre Channel ELS transaction - * - * @v refcnt Reference count - */ -static void fc_els_free ( struct refcnt *refcnt ) { - struct fc_els *els = container_of ( refcnt, struct fc_els, refcnt ); - - assert ( ! process_running ( &els->process ) ); - fc_port_put ( els->port ); - free ( els ); -} - -/** - * Close Fibre Channel ELS transaction - * - * @v els Fibre Channel ELS transaction - * @v rc Reason for close - */ -static void fc_els_close ( struct fc_els *els, int rc ) { - - if ( rc != 0 ) { - DBGC ( els, FCELS_FMT " complete (%s)\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - } - - /* Stop process */ - process_del ( &els->process ); - - /* Shut down interfaces */ - intf_shutdown ( &els->xchg, rc ); - intf_shutdown ( &els->job, rc ); -} - -/** - * Detect Fibre Channel ELS frame handler - * - * @v els Fibre Channel ELS transaction - * @v command ELS command code - * @ret handler ELS handler, or NULL - */ -static struct fc_els_handler * fc_els_detect ( struct fc_els *els, - const void *data, - size_t len ) { - const struct fc_els_frame_common *frame = data; - struct fc_els_handler *handler; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *frame ) ) - return NULL; - - /* Try each handler in turn */ - for_each_table_entry ( handler, FC_ELS_HANDLERS ) { - if ( ( rc = handler->detect ( els, data, len ) ) == 0 ) - return handler; - } - - return NULL; -} - -/** - * Transmit Fibre Channel ELS frame - * - * @v els Fibre Channel ELS transaction - * @v data Data to transmit - * @v len Length of data - * @ret rc Return status code - */ -int fc_els_tx ( struct fc_els *els, const void *data, size_t len ) { - struct xfer_metadata meta; - struct sockaddr_fc dest; - int rc; - - DBGC2 ( els, FCELS_FMT " transmitting:\n", FCELS_ARGS ( els ) ); - DBGC2_HDA ( els, 0, data, len ); - - /* Construct metadata */ - memset ( &meta, 0, sizeof ( meta ) ); - meta.flags = ( fc_els_is_request ( els ) ? - XFER_FL_OVER : ( XFER_FL_RESPONSE | XFER_FL_OUT ) ); - meta.dest = fc_fill_sockaddr ( &dest, &els->peer_port_id ); - - /* Transmit frame */ - if ( ( rc = xfer_deliver_raw_meta ( &els->xchg, data, len, - &meta ) ) != 0 ) { - DBGC ( els, FCELS_FMT " could not deliver frame: %s\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Receive Fibre Channel ELS frame - * - * @v els Fibre Channel ELS transaction - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fc_els_rx ( struct fc_els *els, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct fc_els_frame_common *frame = iobuf->data; - struct sockaddr_fc *src = ( ( struct sockaddr_fc * ) meta->src ); - struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest ); - size_t len = iob_len ( iobuf ); - int rc; - - /* Sanity check */ - if ( len < sizeof ( *frame ) ) { - DBGC ( els, FCELS_FMT " received underlength frame:\n", - FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, frame, len ); - rc = -EINVAL; - goto done; - } - if ( ! src ) { - DBGC ( els, FCELS_FMT " received frame missing source " - "address:\n", FCELS_ARGS ( els ) ); - rc = -EINVAL; - goto done; - } - if ( ! dest ) { - DBGC ( els, FCELS_FMT " received frame missing destination " - "address:\n", FCELS_ARGS ( els ) ); - rc = -EINVAL; - goto done; - } - - /* Check for rejection responses */ - if ( fc_els_is_request ( els ) && - ( frame->command != FC_ELS_LS_ACC ) ) { - DBGC ( els, FCELS_FMT " rejected:\n", FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, frame, len ); - rc = -EACCES; - goto done; - } - - /* Update port IDs */ - memcpy ( &els->port_id, &dest->sfc_port_id, sizeof ( els->port_id ) ); - memcpy ( &els->peer_port_id, &src->sfc_port_id, - sizeof ( els->peer_port_id ) ); - - /* Determine handler, if necessary */ - if ( ! els->handler ) - els->handler = fc_els_detect ( els, frame, len ); - if ( ! els->handler ) - els->handler = &fc_els_unknown_handler; - - DBGC2 ( els, FCELS_FMT " received:\n", FCELS_ARGS ( els ) ); - DBGC2_HDA ( els, 0, frame, len ); - - /* Handle received frame */ - if ( ( rc = els->handler->rx ( els, frame, len ) ) != 0 ) { - DBGC ( els, FCELS_FMT " could not handle received frame: " - "%s\n", FCELS_ARGS ( els ), strerror ( rc ) ); - DBGC_HDA ( els, 0, frame, len ); - goto done; - } - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); - - /* Close transaction */ - fc_els_close ( els, rc ); - - return rc; -} - -/** Fibre Channel ELS exchange interface operations */ -static struct interface_operation fc_els_xchg_op[] = { - INTF_OP ( xfer_deliver, struct fc_els *, fc_els_rx ), - INTF_OP ( intf_close, struct fc_els *, fc_els_close ), -}; - -/** Fibre Channel ELS exchange interface descriptor */ -static struct interface_descriptor fc_els_xchg_desc = - INTF_DESC ( struct fc_els, xchg, fc_els_xchg_op ); - -/** Fibre Channel ELS job control interface operations */ -static struct interface_operation fc_els_job_op[] = { - INTF_OP ( intf_close, struct fc_els *, fc_els_close ), -}; - -/** Fibre Channel ELS job control interface descriptor */ -static struct interface_descriptor fc_els_job_desc = - INTF_DESC ( struct fc_els, job, fc_els_job_op ); - -/** - * Fibre Channel ELS process - * - * @v els Fibre Channel ELS transaction - */ -static void fc_els_step ( struct fc_els *els ) { - int xchg_id; - int rc; - - /* Sanity check */ - assert ( fc_els_is_request ( els ) ); - - /* Create exchange */ - if ( ( xchg_id = fc_xchg_originate ( &els->xchg, els->port, - &els->peer_port_id, - FC_TYPE_ELS ) ) < 0 ) { - rc = xchg_id; - DBGC ( els, FCELS_FMT " could not create exchange: %s\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - fc_els_close ( els, rc ); - return; - } - - /* Transmit request */ - if ( ( rc = els->handler->tx ( els ) ) != 0 ) { - DBGC ( els, FCELS_FMT " could not transmit request: %s\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - fc_els_close ( els, rc ); - return; - } -} - -/** Fibre Channel ELS process descriptor */ -static struct process_descriptor fc_els_process_desc = - PROC_DESC_ONCE ( struct fc_els, process, fc_els_step ); - -/** - * Create ELS transaction - * - * @v port Fibre Channel port - * @v port_id Local port ID - * @v peer_port_id Peer port ID - * @ret els Fibre Channel ELS transaction, or NULL - */ -static struct fc_els * fc_els_create ( struct fc_port *port, - struct fc_port_id *port_id, - struct fc_port_id *peer_port_id ) { - struct fc_els *els; - - /* Allocate and initialise structure */ - els = zalloc ( sizeof ( *els ) ); - if ( ! els ) - return NULL; - ref_init ( &els->refcnt, fc_els_free ); - intf_init ( &els->job, &fc_els_job_desc, &els->refcnt ); - intf_init ( &els->xchg, &fc_els_xchg_desc, &els->refcnt ); - process_init_stopped ( &els->process, &fc_els_process_desc, - &els->refcnt ); - els->port = fc_port_get ( port ); - memcpy ( &els->port_id, port_id, sizeof ( els->port_id ) ); - memcpy ( &els->peer_port_id, peer_port_id, - sizeof ( els->peer_port_id ) ); - return els; -} - -/** - * Create ELS request - * - * @v job Parent job-control interface - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @v handler ELS handler - * @ret rc Return status code - */ -int fc_els_request ( struct interface *job, struct fc_port *port, - struct fc_port_id *peer_port_id, - struct fc_els_handler *handler ) { - struct fc_els *els; - - /* Allocate and initialise structure */ - els = fc_els_create ( port, &port->port_id, peer_port_id ); - if ( ! els ) - return -ENOMEM; - els->handler = handler; - els->flags = FC_ELS_REQUEST; - process_add ( &els->process ); - - /* Attach to parent job interface, mortalise self, and return */ - intf_plug_plug ( &els->job, job ); - ref_put ( &els->refcnt ); - return 0; -} - -/** - * Create ELS response - * - * @v xchg Exchange interface - * @v port Fibre Channel port - * @v port_id Local port ID - * @v peer_port_id Peer port ID - * @ret rc Return status code - */ -static int fc_els_respond ( struct interface *xchg, struct fc_port *port, - struct fc_port_id *port_id, - struct fc_port_id *peer_port_id ) { - struct fc_els *els; - - /* Allocate and initialise structure */ - els = fc_els_create ( port, port_id, peer_port_id ); - if ( ! els ) - return -ENOMEM; - - /* Attach to exchange interface, mortalise self, and return */ - intf_plug_plug ( &els->xchg, xchg ); - ref_put ( &els->refcnt ); - return 0; -} - -/** Fibre Channel ELS responder */ -struct fc_responder fc_els_responder __fc_responder = { - .type = FC_TYPE_ELS, - .respond = fc_els_respond, -}; - -/****************************************************************************** - * - * Unknown ELS handler - * - ****************************************************************************** - */ - -/** - * Transmit unknown ELS request - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_unknown_tx ( struct fc_els *els __unused ) { - return -ENOTSUP; -} - -/** - * Transmit unknown ELS response - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_unknown_tx_response ( struct fc_els *els ) { - struct fc_ls_rjt_frame ls_rjt; - - /* Construct LS_RJT */ - memset ( &ls_rjt, 0, sizeof ( ls_rjt ) ); - ls_rjt.command = FC_ELS_LS_RJT; - ls_rjt.reason = FC_ELS_RJT_UNSUPPORTED; - - /* Transmit LS_RJT */ - return fc_els_tx ( els, &ls_rjt, sizeof ( ls_rjt ) ); -} - -/** - * Receive unknown ELS - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_unknown_rx ( struct fc_els *els, void *data, size_t len ) { - int rc; - - DBGC ( els, FCELS_FMT ":\n", FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, data, len ); - - /* Transmit response, if applicable */ - if ( ! fc_els_is_request ( els ) ) { - if ( ( rc = fc_els_unknown_tx_response ( els ) ) != 0 ) - return rc; - } - - return 0; -} - -/** - * Detect unknown ELS - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_unknown_detect ( struct fc_els *els __unused, - const void *data __unused, - size_t len __unused ) { - return -ENOTSUP; -} - -/** Unknown ELS handler */ -struct fc_els_handler fc_els_unknown_handler __fc_els_handler = { - .name = "UNKNOWN", - .tx = fc_els_unknown_tx, - .rx = fc_els_unknown_rx, - .detect = fc_els_unknown_detect, -}; - -/****************************************************************************** - * - * FLOGI - * - ****************************************************************************** - */ - -/** - * Transmit FLOGI - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_flogi_tx ( struct fc_els *els ) { - struct fc_login_frame flogi; - - /* Construct FLOGI */ - memset ( &flogi, 0, sizeof ( flogi ) ); - flogi.command = fc_els_tx_command ( els, FC_ELS_FLOGI ); - flogi.common.version = htons ( FC_LOGIN_VERSION ); - flogi.common.credit = htons ( FC_LOGIN_DEFAULT_B2B ); - flogi.common.flags = htons ( FC_LOGIN_CONTINUOUS_OFFSET ); - flogi.common.mtu = htons ( FC_LOGIN_DEFAULT_MTU ); - memcpy ( &flogi.port_wwn, &els->port->port_wwn, - sizeof ( flogi.port_wwn ) ); - memcpy ( &flogi.node_wwn, &els->port->node_wwn, - sizeof ( flogi.node_wwn ) ); - flogi.class3.flags = htons ( FC_LOGIN_CLASS_VALID | - FC_LOGIN_CLASS_SEQUENTIAL ); - - /* Transmit FLOGI */ - return fc_els_tx ( els, &flogi, sizeof ( flogi ) ); -} - -/** - * Receive FLOGI - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_flogi_rx ( struct fc_els *els, void *data, size_t len ) { - struct fc_login_frame *flogi = data; - int has_fabric; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *flogi ) ) { - DBGC ( els, FCELS_FMT " received underlength frame:\n", - FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, data, len ); - return -EINVAL; - } - - /* Extract parameters */ - has_fabric = ( flogi->common.flags & htons ( FC_LOGIN_F_PORT ) ); - DBGC ( els, FCELS_FMT " has node %s\n", FCELS_ARGS ( els ), - fc_ntoa ( &flogi->node_wwn ) ); - DBGC ( els, FCELS_FMT " has port %s\n", FCELS_ARGS ( els ), - fc_ntoa ( &flogi->port_wwn ) ); - if ( has_fabric ) { - DBGC ( els, FCELS_FMT " has fabric with", FCELS_ARGS ( els ) ); - DBGC ( els, " local ID %s\n", fc_id_ntoa ( &els->port_id ) ); - } else { - DBGC ( els, FCELS_FMT " has point-to-point link\n", - FCELS_ARGS ( els ) ); - } - - /* Log in port */ - if ( ( rc = fc_port_login ( els->port, &els->port_id, &flogi->node_wwn, - &flogi->port_wwn, has_fabric ) ) != 0 ) { - DBGC ( els, FCELS_FMT " could not log in port: %s\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - return rc; - } - - /* Send any responses to the newly-assigned peer port ID, if - * applicable. - */ - if ( ! has_fabric ) { - memcpy ( &els->peer_port_id, &els->port->ptp_link_port_id, - sizeof ( els->peer_port_id ) ); - } - - /* Transmit response, if applicable */ - if ( ! fc_els_is_request ( els ) ) { - if ( ( rc = fc_els_flogi_tx ( els ) ) != 0 ) - return rc; - } - - return 0; -} - -/** - * Detect FLOGI - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_flogi_detect ( struct fc_els *els __unused, const void *data, - size_t len __unused ) { - const struct fc_login_frame *flogi = data; - - /* Check for FLOGI */ - if ( flogi->command != FC_ELS_FLOGI ) - return -EINVAL; - - return 0; -} - -/** FLOGI ELS handler */ -struct fc_els_handler fc_els_flogi_handler __fc_els_handler = { - .name = "FLOGI", - .tx = fc_els_flogi_tx, - .rx = fc_els_flogi_rx, - .detect = fc_els_flogi_detect, -}; - -/** - * Create FLOGI request - * - * @v parent Parent interface - * @v port Fibre Channel port - * @ret rc Return status code - */ -int fc_els_flogi ( struct interface *parent, struct fc_port *port ) { - - return fc_els_request ( parent, port, &fc_f_port_id, - &fc_els_flogi_handler ); -} - -/****************************************************************************** - * - * PLOGI - * - ****************************************************************************** - */ - -/** - * Transmit PLOGI - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_plogi_tx ( struct fc_els *els ) { - struct fc_login_frame plogi; - - /* Construct PLOGI */ - memset ( &plogi, 0, sizeof ( plogi ) ); - plogi.command = fc_els_tx_command ( els, FC_ELS_PLOGI ); - plogi.common.version = htons ( FC_LOGIN_VERSION ); - plogi.common.credit = htons ( FC_LOGIN_DEFAULT_B2B ); - plogi.common.flags = htons ( FC_LOGIN_CONTINUOUS_OFFSET ); - plogi.common.mtu = htons ( FC_LOGIN_DEFAULT_MTU ); - plogi.common.u.plogi.max_seq = htons ( FC_LOGIN_DEFAULT_MAX_SEQ ); - plogi.common.u.plogi.rel_offs = htons ( FC_LOGIN_DEFAULT_REL_OFFS ); - plogi.common.e_d_tov = htonl ( FC_LOGIN_DEFAULT_E_D_TOV ); - memcpy ( &plogi.port_wwn, &els->port->port_wwn, - sizeof ( plogi.port_wwn ) ); - memcpy ( &plogi.node_wwn, &els->port->node_wwn, - sizeof ( plogi.node_wwn ) ); - plogi.class3.flags = htons ( FC_LOGIN_CLASS_VALID | - FC_LOGIN_CLASS_SEQUENTIAL ); - plogi.class3.mtu = htons ( FC_LOGIN_DEFAULT_MTU ); - plogi.class3.max_seq = htons ( FC_LOGIN_DEFAULT_MAX_SEQ ); - plogi.class3.max_seq_per_xchg = 1; - - /* Transmit PLOGI */ - return fc_els_tx ( els, &plogi, sizeof ( plogi ) ); -} - -/** - * Receive PLOGI - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_plogi_rx ( struct fc_els *els, void *data, size_t len ) { - struct fc_login_frame *plogi = data; - struct fc_peer *peer; - int rc; - - /* Sanity checks */ - if ( len < sizeof ( *plogi ) ) { - DBGC ( els, FCELS_FMT " received underlength frame:\n", - FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, data, len ); - rc = -EINVAL; - goto err_sanity; - } - if ( ! fc_link_ok ( &els->port->link ) ) { - DBGC ( els, FCELS_FMT " received while port link is down\n", - FCELS_ARGS ( els ) ); - rc = -EINVAL; - goto err_sanity; - } - - /* Extract parameters */ - DBGC ( els, FCELS_FMT " has node %s\n", FCELS_ARGS ( els ), - fc_ntoa ( &plogi->node_wwn ) ); - DBGC ( els, FCELS_FMT " has port %s as %s\n", - FCELS_ARGS ( els ), fc_ntoa ( &plogi->port_wwn ), - fc_id_ntoa ( &els->peer_port_id ) ); - - /* Get peer */ - peer = fc_peer_get_wwn ( &plogi->port_wwn ); - if ( ! peer ) { - DBGC ( els, FCELS_FMT " could not create peer\n", - FCELS_ARGS ( els ) ); - rc = -ENOMEM; - goto err_peer_get_wwn; - } - - /* Record login */ - if ( ( rc = fc_peer_login ( peer, els->port, - &els->peer_port_id ) ) != 0 ) { - DBGC ( els, FCELS_FMT " could not log in peer: %s\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - goto err_login; - } - - /* Transmit response, if applicable */ - if ( ! fc_els_is_request ( els ) ) { - if ( ( rc = fc_els_plogi_tx ( els ) ) != 0 ) - goto err_plogi_tx; - } - - /* Drop temporary reference to peer */ - fc_peer_put ( peer ); - - return 0; - - err_plogi_tx: - err_login: - fc_peer_put ( peer ); - err_peer_get_wwn: - err_sanity: - return rc; -} - -/** - * Detect PLOGI - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_plogi_detect ( struct fc_els *els __unused, const void *data, - size_t len __unused ) { - const struct fc_login_frame *plogi = data; - - /* Check for PLOGI */ - if ( plogi->command != FC_ELS_PLOGI ) - return -EINVAL; - - return 0; -} - -/** PLOGI ELS handler */ -struct fc_els_handler fc_els_plogi_handler __fc_els_handler = { - .name = "PLOGI", - .tx = fc_els_plogi_tx, - .rx = fc_els_plogi_rx, - .detect = fc_els_plogi_detect, -}; - -/** - * Create PLOGI request - * - * @v parent Parent interface - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret rc Return status code - */ -int fc_els_plogi ( struct interface *parent, struct fc_port *port, - struct fc_port_id *peer_port_id ) { - - return fc_els_request ( parent, port, peer_port_id, - &fc_els_plogi_handler ); -} - -/****************************************************************************** - * - * LOGO - * - ****************************************************************************** - */ - -/** - * Transmit LOGO request - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_logo_tx ( struct fc_els *els ) { - struct fc_logout_request_frame logo; - - /* Construct LOGO */ - memset ( &logo, 0, sizeof ( logo ) ); - logo.command = FC_ELS_LOGO; - memcpy ( &logo.port_id, &els->port->port_id, sizeof ( logo.port_id ) ); - memcpy ( &logo.port_wwn, &els->port->port_wwn, - sizeof ( logo.port_wwn ) ); - - /* Transmit LOGO */ - return fc_els_tx ( els, &logo, sizeof ( logo ) ); -} - -/** - * Transmit LOGO response - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_logo_tx_response ( struct fc_els *els ) { - struct fc_logout_response_frame logo; - - /* Construct LOGO */ - memset ( &logo, 0, sizeof ( logo ) ); - logo.command = FC_ELS_LS_ACC; - - /* Transmit LOGO */ - return fc_els_tx ( els, &logo, sizeof ( logo ) ); -} - -/** - * Log out individual peer or whole port as applicable - * - * @v els Fibre Channel ELS transaction - * @v port_id Peer port ID - */ -static void fc_els_logo_logout ( struct fc_els *els, - struct fc_port_id *peer_port_id ) { - struct fc_peer *peer; - - if ( ( memcmp ( peer_port_id, &fc_f_port_id, - sizeof ( *peer_port_id ) ) == 0 ) || - ( memcmp ( peer_port_id, &els->port->port_id, - sizeof ( *peer_port_id ) ) == 0 ) ) { - fc_port_logout ( els->port, 0 ); - } else { - peer = fc_peer_get_port_id ( els->port, peer_port_id ); - if ( peer ) { - fc_peer_logout ( peer, 0 ); - fc_peer_put ( peer ); - } - } -} - -/** - * Receive LOGO request - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_logo_rx_request ( struct fc_els *els, void *data, - size_t len ) { - struct fc_logout_request_frame *logo = data; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *logo ) ) { - DBGC ( els, FCELS_FMT " received underlength frame:\n", - FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, data, len ); - return -EINVAL; - } - - DBGC ( els, FCELS_FMT " has port %s as %s\n", FCELS_ARGS ( els ), - fc_ntoa ( &logo->port_wwn ), fc_id_ntoa ( &logo->port_id ) ); - - /* Log out individual peer or whole port as applicable */ - fc_els_logo_logout ( els, &logo->port_id ); - - /* Transmit repsonse */ - if ( ( rc = fc_els_logo_tx_response ( els ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Receive LOGO response - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_logo_rx_response ( struct fc_els *els, void *data __unused, - size_t len __unused ) { - - /* Log out individual peer or whole port as applicable */ - fc_els_logo_logout ( els, &els->peer_port_id ); - - return 0; -} - -/** - * Receive LOGO - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_logo_rx ( struct fc_els *els, void *data, size_t len ) { - - if ( fc_els_is_request ( els ) ) { - return fc_els_logo_rx_response ( els, data, len ); - } else { - return fc_els_logo_rx_request ( els, data, len ); - } -} - -/** - * Detect LOGO - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_logo_detect ( struct fc_els *els __unused, const void *data, - size_t len __unused ) { - const struct fc_logout_request_frame *logo = data; - - /* Check for LOGO */ - if ( logo->command != FC_ELS_LOGO ) - return -EINVAL; - - return 0; -} - -/** LOGO ELS handler */ -struct fc_els_handler fc_els_logo_handler __fc_els_handler = { - .name = "LOGO", - .tx = fc_els_logo_tx, - .rx = fc_els_logo_rx, - .detect = fc_els_logo_detect, -}; - -/** - * Create LOGO request - * - * @v parent Parent interface - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret rc Return status code - */ -int fc_els_logo ( struct interface *parent, struct fc_port *port, - struct fc_port_id *peer_port_id ) { - - return fc_els_request ( parent, port, peer_port_id, - &fc_els_logo_handler ); -} - -/****************************************************************************** - * - * PRLI - * - ****************************************************************************** - */ - -/** - * Find PRLI descriptor - * - * @v type Upper-layer protocol type - * @ret descriptor PRLI descriptor, or NULL - */ -static struct fc_els_prli_descriptor * -fc_els_prli_descriptor ( unsigned int type ) { - struct fc_els_prli_descriptor *descriptor; - - for_each_table_entry ( descriptor, FC_ELS_PRLI_DESCRIPTORS ) { - if ( descriptor->type == type ) - return descriptor; - } - return NULL; -} - -/** - * Transmit PRLI - * - * @v els Fibre Channel ELS transaction - * @v descriptor ELS PRLI descriptor - * @v param Service parameters - * @ret rc Return status code - */ -int fc_els_prli_tx ( struct fc_els *els, - struct fc_els_prli_descriptor *descriptor, void *param ) { - struct { - struct fc_prli_frame frame; - uint8_t param[descriptor->param_len]; - } __attribute__ (( packed )) prli; - struct fc_ulp *ulp; - int rc; - - /* Get ULP */ - ulp = fc_ulp_get_port_id_type ( els->port, &els->peer_port_id, - descriptor->type ); - if ( ! ulp ) { - rc = -ENOMEM; - goto err_get_port_id_type; - } - - /* Build frame for transmission */ - memset ( &prli, 0, sizeof ( prli ) ); - prli.frame.command = fc_els_tx_command ( els, FC_ELS_PRLI ); - prli.frame.page_len = - ( sizeof ( prli.frame.page ) + sizeof ( prli.param ) ); - prli.frame.len = htons ( sizeof ( prli ) ); - prli.frame.page.type = descriptor->type; - if ( fc_els_is_request ( els ) ) { - prli.frame.page.flags |= htons ( FC_PRLI_ESTABLISH ); - } else if ( fc_link_ok ( &ulp->link ) ) { - prli.frame.page.flags |= htons ( FC_PRLI_ESTABLISH | - FC_PRLI_RESPONSE_SUCCESS ); - } - memcpy ( &prli.param, param, sizeof ( prli.param ) ); - - /* Transmit frame */ - if ( ( rc = fc_els_tx ( els, &prli, sizeof ( prli ) ) ) != 0 ) - goto err_tx; - - /* Drop temporary reference to ULP */ - fc_ulp_put ( ulp ); - - return 0; - - err_tx: - fc_ulp_put ( ulp ); - err_get_port_id_type: - return rc; -} - -/** - * Receive PRLI - * - * @v els Fibre Channel ELS transaction - * @v descriptor ELS PRLI descriptor - * @v frame ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -int fc_els_prli_rx ( struct fc_els *els, - struct fc_els_prli_descriptor *descriptor, - void *data, size_t len ) { - struct { - struct fc_prli_frame frame; - uint8_t param[descriptor->param_len]; - } __attribute__ (( packed )) *prli = data; - struct fc_ulp *ulp; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *prli ) ) { - DBGC ( els, FCELS_FMT " received underlength frame:\n", - FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, data, len ); - rc = -EINVAL; - goto err_sanity; - } - - DBGC ( els, FCELS_FMT " has parameters:\n", FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, prli->param, sizeof ( prli->param ) ); - - /* Get ULP */ - ulp = fc_ulp_get_port_id_type ( els->port, &els->peer_port_id, - descriptor->type ); - if ( ! ulp ) { - rc = -ENOMEM; - goto err_get_port_id_type; - } - - /* Sanity check */ - if ( ! fc_link_ok ( &ulp->peer->link ) ) { - DBGC ( els, FCELS_FMT " received while peer link is down\n", - FCELS_ARGS ( els ) ); - rc = -EINVAL; - goto err_link; - } - - /* Log in ULP, if applicable */ - if ( prli->frame.page.flags & htons ( FC_PRLI_ESTABLISH ) ) { - if ( ( rc = fc_ulp_login ( ulp, prli->param, - sizeof ( prli->param ), - fc_els_is_request ( els ) ) ) != 0 ){ - DBGC ( els, FCELS_FMT " could not log in ULP: %s\n", - FCELS_ARGS ( els ), strerror ( rc ) ); - goto err_login; - } - } else { - if ( fc_els_is_request ( els ) ) { - fc_ulp_logout ( ulp, -EACCES ); - } else { - /* This is just an information-gathering PRLI; do not - * log in or out - */ - } - } - - /* Transmit response, if applicable */ - if ( ! fc_els_is_request ( els ) ) { - if ( ( rc = els->handler->tx ( els ) ) != 0 ) - goto err_tx; - } - - /* Drop temporary reference to ULP */ - fc_ulp_put ( ulp ); - - return 0; - - err_tx: - err_login: - err_link: - fc_ulp_put ( ulp ); - err_get_port_id_type: - err_sanity: - return rc; -} - -/** - * Detect PRLI - * - * @v els Fibre Channel ELS transaction - * @v descriptor ELS PRLI descriptor - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -int fc_els_prli_detect ( struct fc_els *els __unused, - struct fc_els_prli_descriptor *descriptor, - const void *data, size_t len ) { - const struct { - struct fc_prli_frame frame; - uint8_t param[descriptor->param_len]; - } __attribute__ (( packed )) *prli = data; - - /* Check for PRLI */ - if ( prli->frame.command != FC_ELS_PRLI ) - return -EINVAL; - - /* Check for sufficient length to contain service parameter page */ - if ( len < sizeof ( *prli ) ) - return -EINVAL; - - /* Check for upper-layer protocol type */ - if ( prli->frame.page.type != descriptor->type ) - return -EINVAL; - - return 0; -} - -/** - * Create PRLI request - * - * @v parent Parent interface - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @v type Upper-layer protocol type - * @ret rc Return status code - */ -int fc_els_prli ( struct interface *parent, struct fc_port *port, - struct fc_port_id *peer_port_id, unsigned int type ) { - struct fc_els_prli_descriptor *descriptor; - - /* Find a PRLI descriptor */ - descriptor = fc_els_prli_descriptor ( type ); - if ( ! descriptor ) - return -ENOTSUP; - - return fc_els_request ( parent, port, peer_port_id, - descriptor->handler ); -} - -/****************************************************************************** - * - * RTV - * - ****************************************************************************** - */ - -/** - * Transmit RTV response - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_rtv_tx_response ( struct fc_els *els ) { - struct fc_rtv_response_frame rtv; - - /* Construct RTV */ - memset ( &rtv, 0, sizeof ( rtv ) ); - rtv.command = FC_ELS_LS_ACC; - rtv.e_d_tov = htonl ( FC_LOGIN_DEFAULT_E_D_TOV ); - - /* Transmit RTV */ - return fc_els_tx ( els, &rtv, sizeof ( rtv ) ); -} - -/** - * Receive RTV - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_rtv_rx ( struct fc_els *els, void *data __unused, - size_t len __unused ) { - int rc; - - DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) ); - - /* Transmit response */ - if ( ! fc_els_is_request ( els ) ) { - if ( ( rc = fc_els_rtv_tx_response ( els ) ) != 0 ) - return rc; - } - - return 0; -} - -/** - * Detect RTV - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_rtv_detect ( struct fc_els *els __unused, const void *data, - size_t len __unused ) { - const struct fc_rtv_request_frame *rtv = data; - - /* Check for RTV */ - if ( rtv->command != FC_ELS_RTV ) - return -EINVAL; - - return 0; -} - -/** RTV ELS handler */ -struct fc_els_handler fc_els_rtv_handler __fc_els_handler = { - .name = "RTV", - .tx = fc_els_unknown_tx, - .rx = fc_els_rtv_rx, - .detect = fc_els_rtv_detect, -}; - -/****************************************************************************** - * - * ECHO - * - ****************************************************************************** - */ - -/** ECHO request data */ -struct fc_echo_request_frame { - /** ECHO frame header */ - struct fc_echo_frame_header echo; - /** Magic marker */ - uint32_t magic; -} __attribute__ (( packed )); - -/** ECHO magic marker */ -#define FC_ECHO_MAGIC 0x69505845 - -/** - * Transmit ECHO - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fc_els_echo_tx ( struct fc_els *els ) { - struct fc_echo_request_frame echo; - - /* Construct ECHO */ - memset ( &echo, 0, sizeof ( echo ) ); - echo.echo.command = FC_ELS_ECHO; - echo.magic = htonl ( FC_ECHO_MAGIC ); - - /* Transmit ECHO */ - return fc_els_tx ( els, &echo, sizeof ( echo ) ); -} - -/** - * Receive ECHO request - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_echo_rx_request ( struct fc_els *els, void *data, - size_t len ) { - struct { - struct fc_echo_frame_header echo; - char payload[ len - sizeof ( struct fc_echo_frame_header ) ]; - } *echo = data; - int rc; - - DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) ); - - /* Transmit response */ - echo->echo.command = FC_ELS_LS_ACC; - if ( ( rc = fc_els_tx ( els, echo, sizeof ( *echo ) ) ) != 0 ) - return rc; - - /* Nothing to do */ - return 0; -} - -/** - * Receive ECHO response - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_echo_rx_response ( struct fc_els *els, void *data, - size_t len ) { - struct fc_echo_request_frame *echo = data; - - DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) ); - - /* Check response is correct */ - if ( ( len != sizeof ( *echo ) ) || - ( echo->magic != htonl ( FC_ECHO_MAGIC ) ) ) { - DBGC ( els, FCELS_FMT " received bad echo response\n", - FCELS_ARGS ( els ) ); - DBGC_HDA ( els, 0, data, len ); - return -EIO; - } - - return 0; -} - -/** - * Receive ECHO - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_echo_rx ( struct fc_els *els, void *data, size_t len ) { - - if ( fc_els_is_request ( els ) ) { - return fc_els_echo_rx_response ( els, data, len ); - } else { - return fc_els_echo_rx_request ( els, data, len ); - } -} - -/** - * Detect ECHO - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fc_els_echo_detect ( struct fc_els *els __unused, const void *data, - size_t len __unused ) { - const struct fc_echo_frame_header *echo = data; - - /* Check for ECHO */ - if ( echo->command != FC_ELS_ECHO ) - return -EINVAL; - - return 0; -} - -/** ECHO ELS handler */ -struct fc_els_handler fc_els_echo_handler __fc_els_handler = { - .name = "ECHO", - .tx = fc_els_echo_tx, - .rx = fc_els_echo_rx, - .detect = fc_els_echo_detect, -}; diff --git a/qemu/roms/ipxe/src/net/fcns.c b/qemu/roms/ipxe/src/net/fcns.c deleted file mode 100644 index be4dfea24..000000000 --- a/qemu/roms/ipxe/src/net/fcns.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/interface.h> -#include <ipxe/iobuf.h> -#include <ipxe/process.h> -#include <ipxe/xfer.h> -#include <ipxe/fc.h> -#include <ipxe/fcns.h> - -/** @file - * - * Fibre Channel name server lookups - * - */ - -/** A Fibre Channel name server query */ -struct fc_ns_query { - /** Reference count */ - struct refcnt refcnt; - /** Fibre Channel exchange */ - struct interface xchg; - - /** Fibre Channel peer */ - struct fc_peer *peer; - /** Fibre Channel port */ - struct fc_port *port; - - /** Process */ - struct process process; - /** Success handler - * - * @v peer Fibre Channel peer - * @v port Fibre Channel port - * @v peer_port_id Peer port ID - * @ret rc Return status code - */ - int ( * done ) ( struct fc_peer *peer, struct fc_port *port, - struct fc_port_id *peer_port_id ); -}; - -/** - * Free name server query - * - * @v refcnt Reference count - */ -static void fc_ns_query_free ( struct refcnt *refcnt ) { - struct fc_ns_query *query = - container_of ( refcnt, struct fc_ns_query, refcnt ); - - fc_peer_put ( query->peer ); - fc_port_put ( query->port ); - free ( query ); -} - -/** - * Close name server query - * - * @v query Name server query - * @v rc Reason for close - */ -static void fc_ns_query_close ( struct fc_ns_query *query, int rc ) { - - /* Stop process */ - process_del ( &query->process ); - - /* Shut down interfaces */ - intf_shutdown ( &query->xchg, rc ); -} - -/** - * Receive name server query response - * - * @v query Name server query - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fc_ns_query_deliver ( struct fc_ns_query *query, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - union fc_ns_response *resp = iobuf->data; - struct fc_port_id *peer_port_id; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( resp->ct ) ) { - DBGC ( query, "FCNS %p received underlength response (%zd " - "bytes)\n", query, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - - /* Handle response */ - switch ( ntohs ( resp->ct.code ) ) { - case FC_GS_ACCEPT: - if ( iob_len ( iobuf ) < sizeof ( resp->gid_pn ) ) { - DBGC ( query, "FCNS %p received underlength accept " - "response (%zd bytes)\n", - query, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - peer_port_id = &resp->gid_pn.port_id.port_id; - DBGC ( query, "FCNS %p resolved %s to %s via %s\n", - query, fc_ntoa ( &query->peer->port_wwn ), - fc_id_ntoa ( peer_port_id ), query->port->name ); - if ( ( rc = query->done ( query->peer, query->port, - peer_port_id ) ) != 0 ) - goto done; - break; - case FC_GS_REJECT: - DBGC ( query, "FCNS %p rejected (reason %02x explanation " - "%02x)\n", query, resp->reject.ct.reason, - resp->reject.ct.explanation ); - break; - default: - DBGC ( query, "FCNS %p received invalid response code %04x\n", - query, ntohs ( resp->ct.code ) ); - rc = -ENOTSUP; - goto done; - } - - rc = 0; - done: - free_iob ( iobuf ); - fc_ns_query_close ( query, rc ); - return rc; -} - -/** - * Name server query process - * - * @v query Name server query - */ -static void fc_ns_query_step ( struct fc_ns_query *query ) { - struct xfer_metadata meta; - struct fc_ns_gid_pn_request gid_pn; - int xchg_id; - int rc; - - /* Create exchange */ - if ( ( xchg_id = fc_xchg_originate ( &query->xchg, query->port, - &fc_gs_port_id, - FC_TYPE_CT ) ) < 0 ) { - rc = xchg_id; - DBGC ( query, "FCNS %p could not create exchange: %s\n", - query, strerror ( rc ) ); - fc_ns_query_close ( query, rc ); - return; - } - - /* Construct query request */ - memset ( &gid_pn, 0, sizeof ( gid_pn ) ); - gid_pn.ct.revision = FC_CT_REVISION; - gid_pn.ct.type = FC_GS_TYPE_DS; - gid_pn.ct.subtype = FC_DS_SUBTYPE_NAME; - gid_pn.ct.code = htons ( FC_NS_GET ( FC_NS_PORT_NAME, FC_NS_PORT_ID )); - memcpy ( &gid_pn.port_wwn, &query->peer->port_wwn, - sizeof ( gid_pn.port_wwn ) ); - memset ( &meta, 0, sizeof ( meta ) ); - meta.flags = XFER_FL_OVER; - - /* Send query */ - if ( ( rc = xfer_deliver_raw_meta ( &query->xchg, &gid_pn, - sizeof ( gid_pn ), &meta ) ) != 0){ - DBGC ( query, "FCNS %p could not deliver query: %s\n", - query, strerror ( rc ) ); - fc_ns_query_close ( query, rc ); - return; - } -} - -/** Name server exchange interface operations */ -static struct interface_operation fc_ns_query_xchg_op[] = { - INTF_OP ( xfer_deliver, struct fc_ns_query *, fc_ns_query_deliver ), - INTF_OP ( intf_close, struct fc_ns_query *, fc_ns_query_close ), -}; - -/** Name server exchange interface descriptor */ -static struct interface_descriptor fc_ns_query_xchg_desc = - INTF_DESC ( struct fc_ns_query, xchg, fc_ns_query_xchg_op ); - -/** Name server process descriptor */ -static struct process_descriptor fc_ns_query_process_desc = - PROC_DESC_ONCE ( struct fc_ns_query, process, fc_ns_query_step ); - -/** - * Issue Fibre Channel name server query - * - * @v peer Fibre Channel peer - * @v port Fibre Channel port - * @ret rc Return status code - */ -int fc_ns_query ( struct fc_peer *peer, struct fc_port *port, - int ( * done ) ( struct fc_peer *peer, struct fc_port *port, - struct fc_port_id *peer_port_id ) ) { - struct fc_ns_query *query; - - /* Allocate and initialise structure */ - query = zalloc ( sizeof ( *query ) ); - if ( ! query ) - return -ENOMEM; - ref_init ( &query->refcnt, fc_ns_query_free ); - intf_init ( &query->xchg, &fc_ns_query_xchg_desc, &query->refcnt ); - process_init ( &query->process, &fc_ns_query_process_desc, - &query->refcnt ); - query->peer = fc_peer_get ( peer ); - query->port = fc_port_get ( port ); - query->done = done; - - DBGC ( query, "FCNS %p querying %s via %s\n", - query, fc_ntoa ( &query->peer->port_wwn ), port->name ); - - /* Mortalise self and return */ - ref_put ( &query->refcnt ); - return 0; -} diff --git a/qemu/roms/ipxe/src/net/fcoe.c b/qemu/roms/ipxe/src/net/fcoe.c deleted file mode 100644 index c3258f15e..000000000 --- a/qemu/roms/ipxe/src/net/fcoe.c +++ /dev/null @@ -1,1233 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <stdlib.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/if_ether.h> -#include <ipxe/if_arp.h> -#include <ipxe/iobuf.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/netdevice.h> -#include <ipxe/ethernet.h> -#include <ipxe/vlan.h> -#include <ipxe/features.h> -#include <ipxe/errortab.h> -#include <ipxe/device.h> -#include <ipxe/crc32.h> -#include <ipxe/retry.h> -#include <ipxe/timer.h> -#include <ipxe/fc.h> -#include <ipxe/fip.h> -#include <ipxe/fcoe.h> - -/** @file - * - * FCoE protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "FCoE", DHCP_EB_FEATURE_FCOE, 1 ); - -/* Disambiguate the various error causes */ -#define EINVAL_UNDERLENGTH __einfo_error ( EINFO_EINVAL_UNDERLENGTH ) -#define EINFO_EINVAL_UNDERLENGTH \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Underlength packet" ) -#define EINVAL_SOF __einfo_error ( EINFO_EINVAL_SOF ) -#define EINFO_EINVAL_SOF \ - __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid SoF delimiter" ) -#define EINVAL_CRC __einfo_error ( EINFO_EINVAL_CRC ) -#define EINFO_EINVAL_CRC \ - __einfo_uniqify ( EINFO_EINVAL, 0x03, "Invalid CRC (not stripped?)" ) -#define EINVAL_EOF __einfo_error ( EINFO_EINVAL_EOF ) -#define EINFO_EINVAL_EOF \ - __einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid EoF delimiter" ) - -/** An FCoE port */ -struct fcoe_port { - /** Reference count */ - struct refcnt refcnt; - /** List of FCoE ports */ - struct list_head list; - /** Transport interface */ - struct interface transport; - /** Network device */ - struct net_device *netdev; - - /** Node WWN */ - union fcoe_name node_wwn; - /** Port WWN */ - union fcoe_name port_wwn; - - /** FIP retransmission timer */ - struct retry_timer timer; - /** FIP timeout counter */ - unsigned int timeouts; - /** Flags */ - unsigned int flags; - /** FCoE forwarder priority */ - unsigned int priority; - /** Keepalive delay (in ms) */ - unsigned int keepalive; - /** FCoE forwarder MAC address */ - uint8_t fcf_mac[ETH_ALEN]; - /** Local MAC address */ - uint8_t local_mac[ETH_ALEN]; -}; - -/** FCoE flags */ -enum fcoe_flags { - /** Underlying network device is available */ - FCOE_HAVE_NETWORK = 0x0001, - /** We have selected an FCoE forwarder to use */ - FCOE_HAVE_FCF = 0x0002, - /** We have a FIP-capable FCoE forwarder available to be used */ - FCOE_HAVE_FIP_FCF = 0x0004, - /** FCoE forwarder supports server-provided MAC addresses */ - FCOE_FCF_ALLOWS_SPMA = 0x0008, - /** An alternative VLAN has been found */ - FCOE_VLAN_FOUND = 0x0010, - /** VLAN discovery has timed out */ - FCOE_VLAN_TIMED_OUT = 0x0020, -}; - -struct net_protocol fcoe_protocol __net_protocol; -struct net_protocol fip_protocol __net_protocol; - -/** FCoE All-FCoE-MACs address */ -static uint8_t all_fcoe_macs[ETH_ALEN] = - { 0x01, 0x10, 0x18, 0x01, 0x00, 0x00 }; - -/** FCoE All-ENode-MACs address */ -static uint8_t all_enode_macs[ETH_ALEN] = - { 0x01, 0x10, 0x18, 0x01, 0x00, 0x01 }; - -/** FCoE All-FCF-MACs address */ -static uint8_t all_fcf_macs[ETH_ALEN] = - { 0x01, 0x10, 0x18, 0x01, 0x00, 0x02 }; - -/** Default FCoE forwarded MAC address */ -static uint8_t default_fcf_mac[ETH_ALEN] = - { 0x0e, 0xfc, 0x00, 0xff, 0xff, 0xfe }; - -/** Maximum number of VLAN requests before giving up on VLAN discovery */ -#define FCOE_MAX_VLAN_REQUESTS 2 - -/** Delay between retrying VLAN requests */ -#define FCOE_VLAN_RETRY_DELAY ( TICKS_PER_SEC ) - -/** Delay between retrying polling VLAN requests */ -#define FCOE_VLAN_POLL_DELAY ( 30 * TICKS_PER_SEC ) - -/** Maximum number of FIP solicitations before giving up on FIP */ -#define FCOE_MAX_FIP_SOLICITATIONS 2 - -/** Delay between retrying FIP solicitations */ -#define FCOE_FIP_RETRY_DELAY ( TICKS_PER_SEC ) - -/** Maximum number of missing discovery advertisements */ -#define FCOE_MAX_FIP_MISSING_KEEPALIVES 4 - -/** List of FCoE ports */ -static LIST_HEAD ( fcoe_ports ); - -/****************************************************************************** - * - * FCoE protocol - * - ****************************************************************************** - */ - -/** - * Identify FCoE port by network device - * - * @v netdev Network device - * @ret fcoe FCoE port, or NULL - */ -static struct fcoe_port * fcoe_demux ( struct net_device *netdev ) { - struct fcoe_port *fcoe; - - list_for_each_entry ( fcoe, &fcoe_ports, list ) { - if ( fcoe->netdev == netdev ) - return fcoe; - } - return NULL; -} - -/** - * Reset FCoE port - * - * @v fcoe FCoE port - */ -static void fcoe_reset ( struct fcoe_port *fcoe ) { - - /* Detach FC port, if any */ - intf_restart ( &fcoe->transport, -ECANCELED ); - - /* Reset any FIP state */ - stop_timer ( &fcoe->timer ); - fcoe->timeouts = 0; - fcoe->flags = 0; - fcoe->priority = ( FIP_LOWEST_PRIORITY + 1 ); - fcoe->keepalive = 0; - memcpy ( fcoe->fcf_mac, default_fcf_mac, - sizeof ( fcoe->fcf_mac ) ); - memcpy ( fcoe->local_mac, fcoe->netdev->ll_addr, - sizeof ( fcoe->local_mac ) ); - - /* Start FIP solicitation if network is available */ - if ( netdev_is_open ( fcoe->netdev ) && - netdev_link_ok ( fcoe->netdev ) ) { - fcoe->flags |= FCOE_HAVE_NETWORK; - start_timer_nodelay ( &fcoe->timer ); - DBGC ( fcoe, "FCoE %s starting %s\n", fcoe->netdev->name, - ( vlan_can_be_trunk ( fcoe->netdev ) ? - "VLAN discovery" : "FIP solicitation" ) ); - } - - /* Send notification of window change */ - xfer_window_changed ( &fcoe->transport ); -} - -/** - * Transmit FCoE packet - * - * @v fcoe FCoE port - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fcoe_deliver ( struct fcoe_port *fcoe, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct fc_frame_header *fchdr = iobuf->data; - struct fc_els_frame_common *els = ( iobuf->data + sizeof ( *fchdr ) ); - struct fcoe_header *fcoehdr; - struct fcoe_footer *fcoeftr; - struct fip_header *fiphdr; - struct fip_login *fipflogi; - struct fip_mac_address *fipmac; - uint32_t crc; - struct net_protocol *net_protocol; - void *ll_source; - int rc; - - /* Send as FIP or FCoE as appropriate */ - if ( ( fchdr->r_ctl == ( FC_R_CTL_ELS | FC_R_CTL_UNSOL_CTRL ) ) && - ( els->command == FC_ELS_FLOGI ) && - ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ) { - - /* Create FIP FLOGI descriptor */ - fipflogi = iob_push ( iobuf, - offsetof ( typeof ( *fipflogi ), fc ) ); - memset ( fipflogi, 0, offsetof ( typeof ( *fipflogi ), fc ) ); - fipflogi->type = FIP_FLOGI; - fipflogi->len = ( iob_len ( iobuf ) / 4 ); - - /* Create FIP MAC address descriptor */ - fipmac = iob_put ( iobuf, sizeof ( *fipmac ) ); - memset ( fipmac, 0, sizeof ( *fipmac ) ); - fipmac->type = FIP_MAC_ADDRESS; - fipmac->len = ( sizeof ( *fipmac ) / 4 ); - if ( fcoe->flags & FCOE_FCF_ALLOWS_SPMA ) { - memcpy ( fipmac->mac, fcoe->netdev->ll_addr, - sizeof ( fipmac->mac ) ); - } - - /* Create FIP header */ - fiphdr = iob_push ( iobuf, sizeof ( *fiphdr ) ); - memset ( fiphdr, 0, sizeof ( *fiphdr ) ); - fiphdr->version = FIP_VERSION; - fiphdr->code = htons ( FIP_CODE_ELS ); - fiphdr->subcode = FIP_ELS_REQUEST; - fiphdr->len = - htons ( ( iob_len ( iobuf ) - sizeof ( *fiphdr ) ) / 4); - fiphdr->flags = ( ( fcoe->flags & FCOE_FCF_ALLOWS_SPMA ) ? - htons ( FIP_SP ) : htons ( FIP_FP ) ); - - /* Send as FIP packet from netdev's own MAC address */ - net_protocol = &fip_protocol; - ll_source = fcoe->netdev->ll_addr; - - } else { - - /* Calculate CRC */ - crc = crc32_le ( ~((uint32_t)0), iobuf->data, - iob_len ( iobuf ) ); - - /* Create FCoE header */ - fcoehdr = iob_push ( iobuf, sizeof ( *fcoehdr ) ); - memset ( fcoehdr, 0, sizeof ( *fcoehdr ) ); - fcoehdr->sof = ( ( fchdr->seq_cnt == ntohs ( 0 ) ) ? - FCOE_SOF_I3 : FCOE_SOF_N3 ); - - /* Create FCoE footer */ - fcoeftr = iob_put ( iobuf, sizeof ( *fcoeftr ) ); - memset ( fcoeftr, 0, sizeof ( *fcoeftr ) ); - fcoeftr->crc = cpu_to_le32 ( crc ^ ~((uint32_t)0) ); - fcoeftr->eof = ( ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ? - FCOE_EOF_T : FCOE_EOF_N ); - - /* Send as FCoE packet from FCoE MAC address */ - net_protocol = &fcoe_protocol; - ll_source = fcoe->local_mac; - } - - /* Transmit packet */ - if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, net_protocol, - fcoe->fcf_mac, ll_source ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not transmit: %s\n", - fcoe->netdev->name, strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Allocate FCoE I/O buffer - * - * @v len Payload length - * @ret iobuf I/O buffer, or NULL - */ -static struct io_buffer * fcoe_alloc_iob ( struct fcoe_port *fcoe __unused, - size_t len ) { - struct io_buffer *iobuf; - - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( struct fcoe_header ) + - len + sizeof ( struct fcoe_footer ) ); - if ( iobuf ) { - iob_reserve ( iobuf, ( MAX_LL_HEADER_LEN + - sizeof ( struct fcoe_header ) ) ); - } - return iobuf; -} - -/** - * Process incoming FCoE packets - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_dest, const void *ll_source, - unsigned int flags __unused ) { - struct fcoe_header *fcoehdr; - struct fcoe_footer *fcoeftr; - struct fcoe_port *fcoe; - int rc; - - /* Identify FCoE port */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE received frame for net device %s missing FCoE " - "port\n", netdev->name ); - rc = -ENOTCONN; - goto done; - } - - /* Discard packets not destined for us */ - if ( ( memcmp ( fcoe->local_mac, ll_dest, - sizeof ( fcoe->local_mac ) ) != 0 ) && - ( memcmp ( default_fcf_mac, ll_dest, - sizeof ( default_fcf_mac ) ) != 0 ) ) { - DBGC2 ( fcoe, "FCoE %s ignoring packet for %s\n", - fcoe->netdev->name, eth_ntoa ( ll_dest ) ); - rc = -ENOTCONN; - goto done; - } - - /* Sanity check */ - if ( iob_len ( iobuf ) < ( sizeof ( *fcoehdr ) + sizeof ( *fcoeftr ) )){ - DBGC ( fcoe, "FCoE %s received under-length frame (%zd " - "bytes)\n", fcoe->netdev->name, iob_len ( iobuf ) ); - rc = -EINVAL_UNDERLENGTH; - goto done; - } - - /* Strip header and footer */ - fcoehdr = iobuf->data; - iob_pull ( iobuf, sizeof ( *fcoehdr ) ); - fcoeftr = ( iobuf->data + iob_len ( iobuf ) - sizeof ( *fcoeftr ) ); - iob_unput ( iobuf, sizeof ( *fcoeftr ) ); - - /* Validity checks */ - if ( fcoehdr->version != FCOE_FRAME_VER ) { - DBGC ( fcoe, "FCoE %s received unsupported frame version " - "%02x\n", fcoe->netdev->name, fcoehdr->version ); - rc = -EPROTONOSUPPORT; - goto done; - } - if ( ! ( ( fcoehdr->sof == FCOE_SOF_I3 ) || - ( fcoehdr->sof == FCOE_SOF_N3 ) ) ) { - DBGC ( fcoe, "FCoE %s received unsupported start-of-frame " - "delimiter %02x\n", fcoe->netdev->name, fcoehdr->sof ); - rc = -EINVAL_SOF; - goto done; - } - if ( ( le32_to_cpu ( fcoeftr->crc ) ^ ~((uint32_t)0) ) != - crc32_le ( ~((uint32_t)0), iobuf->data, iob_len ( iobuf ) ) ) { - DBGC ( fcoe, "FCoE %s received invalid CRC\n", - fcoe->netdev->name ); - rc = -EINVAL_CRC; - goto done; - } - if ( ! ( ( fcoeftr->eof == FCOE_EOF_N ) || - ( fcoeftr->eof == FCOE_EOF_T ) ) ) { - DBGC ( fcoe, "FCoE %s received unsupported end-of-frame " - "delimiter %02x\n", fcoe->netdev->name, fcoeftr->eof ); - rc = -EINVAL_EOF; - goto done; - } - - /* Record FCF address if applicable */ - if ( ( fcoe->flags & FCOE_HAVE_FCF ) && - ( ! ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ) ) { - memcpy ( &fcoe->fcf_mac, ll_source, sizeof ( fcoe->fcf_mac ) ); - } - - /* Hand off via transport interface */ - if ( ( rc = xfer_deliver_iob ( &fcoe->transport, - iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not deliver frame: %s\n", - fcoe->netdev->name, strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Check FCoE flow control window - * - * @v fcoe FCoE port - * @ret len Length of window - */ -static size_t fcoe_window ( struct fcoe_port *fcoe ) { - return ( ( fcoe->flags & FCOE_HAVE_FCF ) ? ~( ( size_t ) 0 ) : 0 ); -} - -/** - * Close FCoE port - * - * @v fcoe FCoE port - * @v rc Reason for close - */ -static void fcoe_close ( struct fcoe_port *fcoe, int rc ) { - - stop_timer ( &fcoe->timer ); - intf_shutdown ( &fcoe->transport, rc ); - netdev_put ( fcoe->netdev ); - list_del ( &fcoe->list ); - ref_put ( &fcoe->refcnt ); -} - -/** - * Identify device underlying FCoE port - * - * @v fcoe FCoE port - * @ret device Underlying device - */ -static struct device * fcoe_identify_device ( struct fcoe_port *fcoe ) { - return fcoe->netdev->dev; -} - -/** FCoE transport interface operations */ -static struct interface_operation fcoe_transport_op[] = { - INTF_OP ( xfer_deliver, struct fcoe_port *, fcoe_deliver ), - INTF_OP ( xfer_alloc_iob, struct fcoe_port *, fcoe_alloc_iob ), - INTF_OP ( xfer_window, struct fcoe_port *, fcoe_window ), - INTF_OP ( intf_close, struct fcoe_port *, fcoe_close ), - INTF_OP ( identify_device, struct fcoe_port *, - fcoe_identify_device ), -}; - -/** FCoE transport interface descriptor */ -static struct interface_descriptor fcoe_transport_desc = - INTF_DESC ( struct fcoe_port, transport, fcoe_transport_op ); - -/****************************************************************************** - * - * FIP protocol - * - ****************************************************************************** - */ - -/** - * Parse FIP packet into descriptor set - * - * @v fcoe FCoE port - * @v fiphdr FIP header - * @v len Length of FIP packet - * @v descs Descriptor set to fill in - * @ret rc Return status code - */ -static int fcoe_fip_parse ( struct fcoe_port *fcoe, struct fip_header *fiphdr, - size_t len, struct fip_descriptors *descs ) { - union fip_descriptor *desc; - size_t descs_len; - size_t desc_len; - size_t desc_offset; - unsigned int desc_type; - - /* Check FIP version */ - if ( fiphdr->version != FIP_VERSION ) { - DBGC ( fcoe, "FCoE %s received unsupported FIP version %02x\n", - fcoe->netdev->name, fiphdr->version ); - return -EINVAL; - } - - /* Check length */ - descs_len = ( ntohs ( fiphdr->len ) * 4 ); - if ( ( sizeof ( *fiphdr ) + descs_len ) > len ) { - DBGC ( fcoe, "FCoE %s received bad descriptor list length\n", - fcoe->netdev->name ); - return -EINVAL; - } - - /* Parse descriptor list */ - memset ( descs, 0, sizeof ( *descs ) ); - for ( desc_offset = 0 ; - desc_offset <= ( descs_len - sizeof ( desc->common ) ) ; - desc_offset += desc_len ) { - - /* Find descriptor and validate length */ - desc = ( ( ( void * ) ( fiphdr + 1 ) ) + desc_offset ); - desc_type = desc->common.type; - desc_len = ( desc->common.len * 4 ); - if ( desc_len == 0 ) { - DBGC ( fcoe, "FCoE %s received zero-length " - "descriptor\n", fcoe->netdev->name ); - return -EINVAL; - } - if ( ( desc_offset + desc_len ) > descs_len ) { - DBGC ( fcoe, "FCoE %s descriptor overrun\n", - fcoe->netdev->name ); - return -EINVAL; - } - - /* Handle descriptors that we understand */ - if ( ( desc_type > FIP_RESERVED ) && - ( desc_type < FIP_NUM_DESCRIPTOR_TYPES ) ) { - /* Use only the first instance of a descriptor */ - if ( descs->desc[desc_type] == NULL ) - descs->desc[desc_type] = desc; - continue; - } - - /* Abort if we cannot understand a critical descriptor */ - if ( FIP_IS_CRITICAL ( desc_type ) ) { - DBGC ( fcoe, "FCoE %s cannot understand critical " - "descriptor type %02x\n", - fcoe->netdev->name, desc_type ); - return -ENOTSUP; - } - - /* Ignore non-critical descriptors that we cannot understand */ - } - - return 0; -} - -/** - * Send FIP VLAN request - * - * @v fcoe FCoE port - * @ret rc Return status code - */ -static int fcoe_fip_tx_vlan ( struct fcoe_port *fcoe ) { - struct io_buffer *iobuf; - struct { - struct fip_header hdr; - struct fip_mac_address mac_address; - } __attribute__ (( packed )) *request; - int rc; - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *request ) ); - if ( ! iobuf ) - return -ENOMEM; - iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - - /* Construct VLAN request */ - request = iob_put ( iobuf, sizeof ( *request ) ); - memset ( request, 0, sizeof ( *request ) ); - request->hdr.version = FIP_VERSION; - request->hdr.code = htons ( FIP_CODE_VLAN ); - request->hdr.subcode = FIP_VLAN_REQUEST; - request->hdr.len = htons ( ( sizeof ( *request ) - - sizeof ( request->hdr ) ) / 4 ); - request->mac_address.type = FIP_MAC_ADDRESS; - request->mac_address.len = - ( sizeof ( request->mac_address ) / 4 ); - memcpy ( request->mac_address.mac, fcoe->netdev->ll_addr, - sizeof ( request->mac_address.mac ) ); - - /* Send VLAN request */ - if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, - &fip_protocol, all_fcf_macs, - fcoe->netdev->ll_addr ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not send VLAN request: " - "%s\n", fcoe->netdev->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle received FIP VLAN notification - * - * @v fcoe FCoE port - * @v descs Descriptor list - * @v flags Flags - * @ret rc Return status code - */ -static int fcoe_fip_rx_vlan ( struct fcoe_port *fcoe, - struct fip_descriptors *descs, - unsigned int flags __unused ) { - struct fip_mac_address *mac_address = fip_mac_address ( descs ); - struct fip_vlan *vlan = fip_vlan ( descs ); - unsigned int tag; - int rc; - - /* Sanity checks */ - if ( ! mac_address ) { - DBGC ( fcoe, "FCoE %s received VLAN notification missing MAC " - "address\n", fcoe->netdev->name ); - return -EINVAL; - } - if ( ! vlan ) { - DBGC ( fcoe, "FCoE %s received VLAN notification missing VLAN " - "tag\n", fcoe->netdev->name ); - return -EINVAL; - } - - /* Create VLAN */ - tag = ntohs ( vlan->vlan ); - DBGC ( fcoe, "FCoE %s creating VLAN %d for FCF %s\n", - fcoe->netdev->name, tag, eth_ntoa ( mac_address->mac ) ); - if ( ( rc = vlan_create ( fcoe->netdev, tag, - FCOE_VLAN_PRIORITY ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not create VLAN %d: %s\n", - fcoe->netdev->name, tag, strerror ( rc ) ); - return rc; - } - - /* Record that a VLAN was found. This FCoE port will play no - * further active role; the real FCoE traffic will use the - * port automatically created for the new VLAN device. - */ - fcoe->flags |= FCOE_VLAN_FOUND; - - return 0; -} - -/** - * Send FIP discovery solicitation - * - * @v fcoe FCoE port - * @ret rc Return status code - */ -static int fcoe_fip_tx_solicitation ( struct fcoe_port *fcoe ) { - struct io_buffer *iobuf; - struct { - struct fip_header hdr; - struct fip_mac_address mac_address; - struct fip_name_id name_id; - struct fip_max_fcoe_size max_fcoe_size; - } __attribute__ (( packed )) *solicitation; - int rc; - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *solicitation ) ); - if ( ! iobuf ) - return -ENOMEM; - iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - - /* Construct discovery solicitation */ - solicitation = iob_put ( iobuf, sizeof ( *solicitation ) ); - memset ( solicitation, 0, sizeof ( *solicitation ) ); - solicitation->hdr.version = FIP_VERSION; - solicitation->hdr.code = htons ( FIP_CODE_DISCOVERY ); - solicitation->hdr.subcode = FIP_DISCOVERY_SOLICIT; - solicitation->hdr.len = htons ( ( sizeof ( *solicitation ) - - sizeof ( solicitation->hdr ) ) / 4 ); - solicitation->hdr.flags = htons ( FIP_FP | FIP_SP ); - solicitation->mac_address.type = FIP_MAC_ADDRESS; - solicitation->mac_address.len = - ( sizeof ( solicitation->mac_address ) / 4 ); - memcpy ( solicitation->mac_address.mac, fcoe->netdev->ll_addr, - sizeof ( solicitation->mac_address.mac ) ); - solicitation->name_id.type = FIP_NAME_ID; - solicitation->name_id.len = ( sizeof ( solicitation->name_id ) / 4 ); - memcpy ( &solicitation->name_id.name, &fcoe->node_wwn.fc, - sizeof ( solicitation->name_id.name ) ); - solicitation->max_fcoe_size.type = FIP_MAX_FCOE_SIZE; - solicitation->max_fcoe_size.len = - ( sizeof ( solicitation->max_fcoe_size ) / 4 ); - solicitation->max_fcoe_size.mtu = - htons ( ETH_MAX_MTU - sizeof ( struct fcoe_header ) - - sizeof ( struct fcoe_footer ) ); - - /* Send discovery solicitation */ - if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, - &fip_protocol, all_fcf_macs, - fcoe->netdev->ll_addr ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not send discovery solicitation: " - "%s\n", fcoe->netdev->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle received FIP discovery advertisement - * - * @v fcoe FCoE port - * @v descs Descriptor list - * @v flags Flags - * @ret rc Return status code - */ -static int fcoe_fip_rx_advertisement ( struct fcoe_port *fcoe, - struct fip_descriptors *descs, - unsigned int flags ) { - struct fip_priority *priority = fip_priority ( descs ); - struct fip_mac_address *mac_address = fip_mac_address ( descs ); - struct fip_fka_adv_p *fka_adv_p = fip_fka_adv_p ( descs ); - - /* Sanity checks */ - if ( ! priority ) { - DBGC ( fcoe, "FCoE %s received advertisement missing " - "priority\n", fcoe->netdev->name ); - return -EINVAL; - } - if ( ! mac_address ) { - DBGC ( fcoe, "FCoE %s received advertisement missing MAC " - "address\n", fcoe->netdev->name ); - return -EINVAL; - } - if ( ! fka_adv_p ) { - DBGC ( fcoe, "FCoE %s received advertisement missing FKA ADV " - "period\n", fcoe->netdev->name ); - return -EINVAL; - } - - if ( ! ( fcoe->flags & FCOE_HAVE_FCF ) ) { - - /* We are soliciting for an FCF. Store the highest - * (i.e. lowest-valued) priority solicited - * advertisement that we receive. - */ - if ( ( ( flags & ( FIP_A | FIP_S | FIP_F ) ) == - ( FIP_A | FIP_S | FIP_F ) ) && - ( priority->priority < fcoe->priority ) ) { - - fcoe->flags |= FCOE_HAVE_FIP_FCF; - fcoe->priority = priority->priority; - if ( fka_adv_p->flags & FIP_NO_KEEPALIVE ) { - fcoe->keepalive = 0; - } else { - fcoe->keepalive = ntohl ( fka_adv_p->period ); - } - fcoe->flags &= ~FCOE_FCF_ALLOWS_SPMA; - if ( flags & FIP_SP ) - fcoe->flags |= FCOE_FCF_ALLOWS_SPMA; - memcpy ( fcoe->fcf_mac, mac_address->mac, - sizeof ( fcoe->fcf_mac ) ); - DBGC ( fcoe, "FCoE %s selected FCF %s (pri %d", - fcoe->netdev->name, eth_ntoa ( fcoe->fcf_mac ), - fcoe->priority ); - if ( fcoe->keepalive ) { - DBGC ( fcoe, ", FKA ADV %dms", - fcoe->keepalive ); - } - DBGC ( fcoe, ", %cPMA)\n", - ( ( fcoe->flags & FCOE_FCF_ALLOWS_SPMA ) ? - 'S' : 'F' ) ); - } - - } else if ( fcoe->flags & FCOE_HAVE_FIP_FCF ) { - - /* We are checking that the FCF remains alive. Reset - * the timeout counter if this is an advertisement - * from our forwarder. - */ - if ( memcmp ( fcoe->fcf_mac, mac_address->mac, - sizeof ( fcoe->fcf_mac ) ) == 0 ) { - fcoe->timeouts = 0; - } - - } else { - - /* We are operating in non-FIP mode and have received - * a FIP advertisement. Reset the link in order to - * attempt FIP. - */ - fcoe_reset ( fcoe ); - - } - - return 0; -} - -/** - * Handle received FIP ELS response - * - * @v fcoe FCoE port - * @v descs Descriptor list - * @v flags Flags - * @ret rc Return status code - */ -static int fcoe_fip_rx_els_response ( struct fcoe_port *fcoe, - struct fip_descriptors *descs, - unsigned int flags __unused ) { - struct fip_els *flogi = fip_flogi ( descs ); - struct fip_mac_address *mac_address = fip_mac_address ( descs ); - void *frame; - size_t frame_len; - int rc; - - /* Sanity checks */ - if ( ! flogi ) { - DBGC ( fcoe, "FCoE %s received ELS response missing FLOGI\n", - fcoe->netdev->name ); - return -EINVAL; - } - if ( ! mac_address ) { - DBGC ( fcoe, "FCoE %s received ELS response missing MAC " - "address\n", fcoe->netdev->name ); - return -EINVAL; - } - - /* Record local MAC address */ - memcpy ( fcoe->local_mac, mac_address->mac, sizeof ( fcoe->local_mac )); - DBGC ( fcoe, "FCoE %s using local MAC %s\n", - fcoe->netdev->name, eth_ntoa ( fcoe->local_mac ) ); - - /* Hand off via transport interface */ - frame = &flogi->fc; - frame_len = ( ( flogi->len * 4 ) - offsetof ( typeof ( *flogi ), fc ) ); - if ( ( rc = xfer_deliver_raw ( &fcoe->transport, frame, - frame_len ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not deliver FIP FLOGI frame: %s\n", - fcoe->netdev->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Send FIP keepalive - * - * @v fcoe FCoE port - * @ret rc Return status code - */ -static int fcoe_fip_tx_keepalive ( struct fcoe_port *fcoe ) { - struct io_buffer *iobuf; - struct { - struct fip_header hdr; - struct fip_mac_address mac_address; - } __attribute__ (( packed )) *keepalive; - int rc; - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *keepalive ) ); - if ( ! iobuf ) - return -ENOMEM; - iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); - - /* Construct keepalive */ - keepalive = iob_put ( iobuf, sizeof ( *keepalive ) ); - memset ( keepalive, 0, sizeof ( *keepalive ) ); - keepalive->hdr.version = FIP_VERSION; - keepalive->hdr.code = htons ( FIP_CODE_MAINTAIN ); - keepalive->hdr.subcode = FIP_MAINTAIN_KEEP_ALIVE; - keepalive->hdr.len = htons ( ( sizeof ( *keepalive ) - - sizeof ( keepalive->hdr ) ) / 4 ); - keepalive->mac_address.type = FIP_MAC_ADDRESS; - keepalive->mac_address.len = - ( sizeof ( keepalive->mac_address ) / 4 ); - memcpy ( keepalive->mac_address.mac, fcoe->netdev->ll_addr, - sizeof ( keepalive->mac_address.mac ) ); - - /* Send keepalive */ - if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, - &fip_protocol, fcoe->fcf_mac, - fcoe->netdev->ll_addr ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not send keepalive: %s\n", - fcoe->netdev->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** A FIP handler */ -struct fip_handler { - /** Protocol code */ - uint16_t code; - /** Protocol subcode */ - uint8_t subcode; - /** - * Receive FIP packet - * - * @v fcoe FCoE port - * @v descs Descriptor list - * @v flags Flags - * @ret rc Return status code - */ - int ( * rx ) ( struct fcoe_port *fcoe, struct fip_descriptors *descs, - unsigned int flags ); -}; - -/** FIP handlers */ -static struct fip_handler fip_handlers[] = { - { FIP_CODE_VLAN, FIP_VLAN_NOTIFY, - fcoe_fip_rx_vlan }, - { FIP_CODE_DISCOVERY, FIP_DISCOVERY_ADVERTISE, - fcoe_fip_rx_advertisement }, - { FIP_CODE_ELS, FIP_ELS_RESPONSE, - fcoe_fip_rx_els_response }, -}; - -/** - * Process incoming FIP packets - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int fcoe_fip_rx ( struct io_buffer *iobuf, - struct net_device *netdev, - const void *ll_dest, - const void *ll_source __unused, - unsigned int flags __unused ) { - struct fip_header *fiphdr = iobuf->data; - struct fip_descriptors descs; - struct fip_handler *handler; - struct fcoe_port *fcoe; - unsigned int i; - int rc; - - /* Identify FCoE port */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE received FIP frame for net device %s missing FCoE " - "port\n", netdev->name ); - rc = -ENOTCONN; - goto done; - } - - /* Discard packets not destined for us */ - if ( ( memcmp ( fcoe->netdev->ll_addr, ll_dest, ETH_ALEN ) != 0 ) && - ( memcmp ( all_fcoe_macs, ll_dest, - sizeof ( all_fcoe_macs ) ) != 0 ) && - ( memcmp ( all_enode_macs, ll_dest, - sizeof ( all_enode_macs ) ) != 0 ) ) { - DBGC2 ( fcoe, "FCoE %s ignoring FIP packet for %s\n", - fcoe->netdev->name, eth_ntoa ( ll_dest ) ); - rc = -ENOTCONN; - goto done; - } - - /* Parse FIP packet */ - if ( ( rc = fcoe_fip_parse ( fcoe, fiphdr, iob_len ( iobuf ), - &descs ) ) != 0 ) - goto done; - - /* Find a suitable handler */ - for ( i = 0 ; i < ( sizeof ( fip_handlers ) / - sizeof ( fip_handlers[0] ) ) ; i++ ) { - handler = &fip_handlers[i]; - if ( ( handler->code == ntohs ( fiphdr->code ) ) && - ( handler->subcode == fiphdr->subcode ) ) { - rc = handler->rx ( fcoe, &descs, - ntohs ( fiphdr->flags ) ); - goto done; - } - } - DBGC ( fcoe, "FCoE %s received unsupported FIP code %04x.%02x\n", - fcoe->netdev->name, ntohs ( fiphdr->code ), fiphdr->subcode ); - rc = -ENOTSUP; - - done: - free_iob ( iobuf ); - return rc; -} - -/****************************************************************************** - * - * FCoE ports - * - ****************************************************************************** - */ - -/** - * Handle FCoE timer expiry - * - * @v timer FIP timer - * @v over Timer expired - */ -static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { - struct fcoe_port *fcoe = - container_of ( timer, struct fcoe_port, timer ); - int rc; - - /* Sanity check */ - assert ( fcoe->flags & FCOE_HAVE_NETWORK ); - - /* Increment the timeout counter */ - fcoe->timeouts++; - - if ( vlan_can_be_trunk ( fcoe->netdev ) && - ! ( fcoe->flags & FCOE_VLAN_TIMED_OUT ) ) { - - /* If we have already found a VLAN, send infrequent - * VLAN requests, in case VLAN information changes. - */ - if ( fcoe->flags & FCOE_VLAN_FOUND ) { - fcoe->flags &= ~FCOE_VLAN_FOUND; - fcoe->timeouts = 0; - start_timer_fixed ( &fcoe->timer, - FCOE_VLAN_POLL_DELAY ); - fcoe_fip_tx_vlan ( fcoe ); - return; - } - - /* If we have not yet found a VLAN, and we have not - * yet timed out and given up on finding one, then - * send a VLAN request and wait. - */ - if ( fcoe->timeouts <= FCOE_MAX_VLAN_REQUESTS ) { - start_timer_fixed ( &fcoe->timer, - FCOE_VLAN_RETRY_DELAY ); - fcoe_fip_tx_vlan ( fcoe ); - return; - } - - /* We have timed out waiting for a VLAN; proceed to - * FIP discovery. - */ - fcoe->flags |= FCOE_VLAN_TIMED_OUT; - fcoe->timeouts = 0; - DBGC ( fcoe, "FCoE %s giving up on VLAN discovery\n", - fcoe->netdev->name ); - start_timer_nodelay ( &fcoe->timer ); - - } else if ( ! ( fcoe->flags & FCOE_HAVE_FCF ) ) { - - /* If we have not yet found a FIP-capable forwarder, - * and we have not yet timed out and given up on - * finding one, then send a FIP solicitation and wait. - */ - start_timer_fixed ( &fcoe->timer, FCOE_FIP_RETRY_DELAY ); - if ( ( ! ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ) && - ( fcoe->timeouts <= FCOE_MAX_FIP_SOLICITATIONS ) ) { - fcoe_fip_tx_solicitation ( fcoe ); - return; - } - - /* Attach Fibre Channel port */ - if ( ( rc = fc_port_open ( &fcoe->transport, &fcoe->node_wwn.fc, - &fcoe->port_wwn.fc, - fcoe->netdev->name ) ) != 0 ) { - DBGC ( fcoe, "FCoE %s could not create FC port: %s\n", - fcoe->netdev->name, strerror ( rc ) ); - /* We will try again on the next timer expiry */ - return; - } - stop_timer ( &fcoe->timer ); - - /* Either we have found a FIP-capable forwarder, or we - * have timed out and will fall back to pre-FIP mode. - */ - fcoe->flags |= FCOE_HAVE_FCF; - fcoe->timeouts = 0; - DBGC ( fcoe, "FCoE %s using %sFIP FCF %s\n", fcoe->netdev->name, - ( ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ? "" : "non-" ), - eth_ntoa ( fcoe->fcf_mac ) ); - - /* Start sending keepalives if applicable */ - if ( fcoe->keepalive ) - start_timer_nodelay ( &fcoe->timer ); - - /* Send notification of window change */ - xfer_window_changed ( &fcoe->transport ); - - } else { - - /* Send keepalive */ - start_timer_fixed ( &fcoe->timer, - ( ( fcoe->keepalive * TICKS_PER_SEC ) / 1000 ) ); - fcoe_fip_tx_keepalive ( fcoe ); - - /* Abandon FCF if we have not seen its advertisements */ - if ( fcoe->timeouts > FCOE_MAX_FIP_MISSING_KEEPALIVES ) { - DBGC ( fcoe, "FCoE %s abandoning FCF %s\n", - fcoe->netdev->name, eth_ntoa ( fcoe->fcf_mac )); - fcoe_reset ( fcoe ); - } - } -} - -/** - * Create FCoE port - * - * @v netdev Network device - * @ret rc Return status code - */ -static int fcoe_probe ( struct net_device *netdev ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct fcoe_port *fcoe; - int rc; - - /* Sanity check */ - if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) ) { - /* Not an error; simply skip this net device */ - DBG ( "FCoE skipping non-Ethernet device %s\n", netdev->name ); - rc = 0; - goto err_non_ethernet; - } - - /* Allocate and initialise structure */ - fcoe = zalloc ( sizeof ( *fcoe ) ); - if ( ! fcoe ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &fcoe->refcnt, NULL ); - intf_init ( &fcoe->transport, &fcoe_transport_desc, &fcoe->refcnt ); - timer_init ( &fcoe->timer, fcoe_expired, &fcoe->refcnt ); - fcoe->netdev = netdev_get ( netdev ); - - /* Construct node and port names */ - fcoe->node_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE ); - memcpy ( &fcoe->node_wwn.fcoe.mac, netdev->ll_addr, - sizeof ( fcoe->node_wwn.fcoe.mac ) ); - fcoe->port_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE_EXTENDED ); - memcpy ( &fcoe->port_wwn.fcoe.mac, netdev->ll_addr, - sizeof ( fcoe->port_wwn.fcoe.mac ) ); - - DBGC ( fcoe, "FCoE %s is %s", fcoe->netdev->name, - fc_ntoa ( &fcoe->node_wwn.fc ) ); - DBGC ( fcoe, " port %s\n", fc_ntoa ( &fcoe->port_wwn.fc ) ); - - /* Transfer reference to port list */ - list_add ( &fcoe->list, &fcoe_ports ); - return 0; - - netdev_put ( fcoe->netdev ); - err_zalloc: - err_non_ethernet: - return rc; -} - -/** - * Handle FCoE port device or link state change - * - * @v netdev Network device - */ -static void fcoe_notify ( struct net_device *netdev ) { - struct fcoe_port *fcoe; - - /* Sanity check */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE notification for net device %s missing FCoE " - "port\n", netdev->name ); - return; - } - - /* Reset the FCoE link if necessary */ - if ( ! ( netdev_is_open ( netdev ) && - netdev_link_ok ( netdev ) && - ( fcoe->flags & FCOE_HAVE_NETWORK ) ) ) { - fcoe_reset ( fcoe ); - } -} - -/** - * Destroy FCoE port - * - * @v netdev Network device - */ -static void fcoe_remove ( struct net_device *netdev ) { - struct fcoe_port *fcoe; - - /* Sanity check */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE removal of net device %s missing FCoE port\n", - netdev->name ); - return; - } - - /* Close FCoE device */ - fcoe_close ( fcoe, 0 ); -} - -/** FCoE driver */ -struct net_driver fcoe_driver __net_driver = { - .name = "FCoE", - .probe = fcoe_probe, - .notify = fcoe_notify, - .remove = fcoe_remove, -}; - -/** FCoE protocol */ -struct net_protocol fcoe_protocol __net_protocol = { - .name = "FCoE", - .net_proto = htons ( ETH_P_FCOE ), - .rx = fcoe_rx, -}; - -/** FIP protocol */ -struct net_protocol fip_protocol __net_protocol = { - .name = "FIP", - .net_proto = htons ( ETH_P_FIP ), - .rx = fcoe_fip_rx, -}; - -/** Human-readable message for CRC errors - * - * It seems as though several drivers neglect to strip the Ethernet - * CRC, which will cause the FCoE footer to be misplaced and result - * (coincidentally) in an "invalid CRC" error from FCoE. - */ -struct errortab fcoe_errors[] __errortab = { - __einfo_errortab ( EINFO_EINVAL_CRC ), -}; diff --git a/qemu/roms/ipxe/src/net/fcp.c b/qemu/roms/ipxe/src/net/fcp.c deleted file mode 100644 index 930bf7dd4..000000000 --- a/qemu/roms/ipxe/src/net/fcp.c +++ /dev/null @@ -1,1096 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/refcnt.h> -#include <ipxe/list.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/iobuf.h> -#include <ipxe/open.h> -#include <ipxe/process.h> -#include <ipxe/uri.h> -#include <ipxe/acpi.h> -#include <ipxe/scsi.h> -#include <ipxe/device.h> -#include <ipxe/edd.h> -#include <ipxe/fc.h> -#include <ipxe/fcels.h> -#include <ipxe/fcp.h> - -/** @file - * - * Fibre Channel Protocol - * - */ - -/* Disambiguate the various error causes */ -#define ERANGE_READ_DATA_ORDERING \ - __einfo_error ( EINFO_ERANGE_READ_DATA_ORDERING ) -#define EINFO_ERANGE_READ_DATA_ORDERING \ - __einfo_uniqify ( EINFO_ERANGE, 0x01, "Read data out of order" ) -#define ERANGE_READ_DATA_OVERRUN \ - __einfo_error ( EINFO_ERANGE_READ_DATA_OVERRUN ) -#define EINFO_ERANGE_READ_DATA_OVERRUN \ - __einfo_uniqify ( EINFO_ERANGE, 0x02, "Read data overrun" ) -#define ERANGE_WRITE_DATA_STUCK \ - __einfo_error ( EINFO_ERANGE_WRITE_DATA_STUCK ) -#define EINFO_ERANGE_WRITE_DATA_STUCK \ - __einfo_uniqify ( EINFO_ERANGE, 0x03, "Write data stuck" ) -#define ERANGE_WRITE_DATA_OVERRUN \ - __einfo_error ( EINFO_ERANGE_WRITE_DATA_OVERRUN ) -#define EINFO_ERANGE_WRITE_DATA_OVERRUN \ - __einfo_uniqify ( EINFO_ERANGE, 0x04, "Write data overrun" ) -#define ERANGE_DATA_UNDERRUN \ - __einfo_error ( EINFO_ERANGE_DATA_UNDERRUN ) -#define EINFO_ERANGE_DATA_UNDERRUN \ - __einfo_uniqify ( EINFO_ERANGE, 0x05, "Data underrun" ) - -/****************************************************************************** - * - * PRLI - * - ****************************************************************************** - */ - -struct fc_els_prli_descriptor fcp_prli_descriptor __fc_els_prli_descriptor; - -/** - * Transmit FCP PRLI - * - * @v els Fibre Channel ELS transaction - * @ret rc Return status code - */ -static int fcp_prli_tx ( struct fc_els *els ) { - struct fcp_prli_service_parameters param; - - /* Build service parameter page */ - memset ( ¶m, 0, sizeof ( param ) ); - param.flags = htonl ( FCP_PRLI_NO_READ_RDY | FCP_PRLI_INITIATOR ); - - return fc_els_prli_tx ( els, &fcp_prli_descriptor, ¶m ); -} - -/** - * Receive FCP PRLI - * - * @v els Fibre Channel ELS transaction - * @v frame ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fcp_prli_rx ( struct fc_els *els, void *data, size_t len ) { - return fc_els_prli_rx ( els, &fcp_prli_descriptor, data, len ); -} - -/** - * Detect FCP PRLI - * - * @v els Fibre Channel ELS transaction - * @v data ELS frame - * @v len Length of ELS frame - * @ret rc Return status code - */ -static int fcp_prli_detect ( struct fc_els *els, const void *data, - size_t len ) { - return fc_els_prli_detect ( els, &fcp_prli_descriptor, data, len ); -} - -/** FCP PRLI ELS handler */ -struct fc_els_handler fcp_prli_handler __fc_els_handler = { - .name = "PRLI-FCP", - .tx = fcp_prli_tx, - .rx = fcp_prli_rx, - .detect = fcp_prli_detect, -}; - -/** FCP PRLI descriptor */ -struct fc_els_prli_descriptor fcp_prli_descriptor __fc_els_prli_descriptor = { - .type = FC_TYPE_FCP, - .param_len = sizeof ( struct fcp_prli_service_parameters ), - .handler = &fcp_prli_handler, -}; - -/****************************************************************************** - * - * FCP devices and commands - * - ****************************************************************************** - */ - -/** An FCP device */ -struct fcp_device { - /** Reference count */ - struct refcnt refcnt; - /** Fibre Channel upper-layer protocol user */ - struct fc_ulp_user user; - /** SCSI command issuing interface */ - struct interface scsi; - /** List of active commands */ - struct list_head fcpcmds; - - /** Fibre Channel WWN (for boot firmware table) */ - struct fc_name wwn; - /** SCSI LUN (for boot firmware table) */ - struct scsi_lun lun; -}; - -/** An FCP command */ -struct fcp_command { - /** Reference count */ - struct refcnt refcnt; - /** FCP SCSI device */ - struct fcp_device *fcpdev; - /** List of active commands */ - struct list_head list; - /** SCSI command interface */ - struct interface scsi; - /** Fibre Channel exchange interface */ - struct interface xchg; - /** Send process */ - struct process process; - /** Send current IU - * - * @v fcpcmd FCP command - * @ret rc Return status code - */ - int ( * send ) ( struct fcp_command *fcpcmd ); - /** SCSI command */ - struct scsi_cmd command; - /** Data offset within command */ - size_t offset; - /** Length of data remaining to be sent within this IU */ - size_t remaining; - /** Exchange ID */ - uint16_t xchg_id; -}; - -/** - * Get reference to FCP device - * - * @v fcpdev FCP device - * @ret fcpdev FCP device - */ -static inline __attribute__ (( always_inline )) struct fcp_device * -fcpdev_get ( struct fcp_device *fcpdev ) { - ref_get ( &fcpdev->refcnt ); - return fcpdev; -} - -/** - * Drop reference to FCP device - * - * @v fcpdev FCP device - */ -static inline __attribute__ (( always_inline )) void -fcpdev_put ( struct fcp_device *fcpdev ) { - ref_put ( &fcpdev->refcnt ); -} - -/** - * Get reference to FCP command - * - * @v fcpcmd FCP command - * @ret fcpcmd FCP command - */ -static inline __attribute__ (( always_inline )) struct fcp_command * -fcpcmd_get ( struct fcp_command *fcpcmd ) { - ref_get ( &fcpcmd->refcnt ); - return fcpcmd; -} - -/** - * Drop reference to FCP command - * - * @v fcpcmd FCP command - */ -static inline __attribute__ (( always_inline )) void -fcpcmd_put ( struct fcp_command *fcpcmd ) { - ref_put ( &fcpcmd->refcnt ); -} - -/** - * Start FCP command sending - * - * @v fcpcmd FCP command - * @v send Send method - */ -static inline __attribute__ (( always_inline )) void -fcpcmd_start_send ( struct fcp_command *fcpcmd, - int ( * send ) ( struct fcp_command *fcpcmd ) ) { - fcpcmd->send = send; - process_add ( &fcpcmd->process ); -} - -/** - * Stop FCP command sending - * - * @v fcpcmd FCP command - */ -static inline __attribute__ (( always_inline )) void -fcpcmd_stop_send ( struct fcp_command *fcpcmd ) { - process_del ( &fcpcmd->process ); -} - -/** - * Free FCP command - * - * @v refcnt Reference count - */ -static void fcpcmd_free ( struct refcnt *refcnt ) { - struct fcp_command *fcpcmd = - container_of ( refcnt, struct fcp_command, refcnt ); - - /* Remove from list of commands */ - list_del ( &fcpcmd->list ); - fcpdev_put ( fcpcmd->fcpdev ); - - /* Free command */ - free ( fcpcmd ); -} - -/** - * Close FCP command - * - * @v fcpcmd FCP command - * @v rc Reason for close - */ -static void fcpcmd_close ( struct fcp_command *fcpcmd, int rc ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - - if ( rc != 0 ) { - DBGC ( fcpdev, "FCP %p xchg %04x closed: %s\n", - fcpdev, fcpcmd->xchg_id, strerror ( rc ) ); - } - - /* Stop sending */ - fcpcmd_stop_send ( fcpcmd ); - - /* Shut down interfaces */ - intf_shutdown ( &fcpcmd->scsi, rc ); - intf_shutdown ( &fcpcmd->xchg, rc ); -} - -/** - * Close FCP command in error - * - * @v fcpcmd FCP command - * @v rc Reason for close - */ -static void fcpcmd_close_err ( struct fcp_command *fcpcmd, int rc ) { - if ( rc == 0 ) - rc = -EPIPE; - fcpcmd_close ( fcpcmd, rc ); -} - -/** - * Send FCP command IU - * - * @v fcpcmd FCP command - * @ret rc Return status code - */ -static int fcpcmd_send_cmnd ( struct fcp_command *fcpcmd ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - struct scsi_cmd *command = &fcpcmd->command; - struct io_buffer *iobuf; - struct fcp_cmnd *cmnd; - struct xfer_metadata meta; - int rc; - - /* Sanity check */ - if ( command->data_in_len && command->data_out_len ) { - DBGC ( fcpdev, "FCP %p xchg %04x cannot handle bidirectional " - "command\n", fcpdev, fcpcmd->xchg_id ); - return -ENOTSUP; - } - - /* Allocate I/O buffer */ - iobuf = xfer_alloc_iob ( &fcpcmd->xchg, sizeof ( *cmnd ) ); - if ( ! iobuf ) { - DBGC ( fcpdev, "FCP %p xchg %04x cannot allocate command IU\n", - fcpdev, fcpcmd->xchg_id ); - return -ENOMEM; - } - - /* Construct command IU frame */ - cmnd = iob_put ( iobuf, sizeof ( *cmnd ) ); - memset ( cmnd, 0, sizeof ( *cmnd ) ); - memcpy ( &cmnd->lun, &command->lun, sizeof ( cmnd->lun ) ); - assert ( ! ( command->data_in_len && command->data_out_len ) ); - if ( command->data_in_len ) - cmnd->dirn |= FCP_CMND_RDDATA; - if ( command->data_out_len ) - cmnd->dirn |= FCP_CMND_WRDATA; - memcpy ( &cmnd->cdb, &fcpcmd->command.cdb, sizeof ( cmnd->cdb ) ); - cmnd->len = htonl ( command->data_in_len + command->data_out_len ); - memset ( &meta, 0, sizeof ( meta ) ); - meta.flags = ( XFER_FL_CMD_STAT | XFER_FL_OVER ); - DBGC2 ( fcpdev, "FCP %p xchg %04x CMND " SCSI_CDB_FORMAT " %04x\n", - fcpdev, fcpcmd->xchg_id, SCSI_CDB_DATA ( cmnd->cdb ), - ntohl ( cmnd->len ) ); - - /* No further data to send within this IU */ - fcpcmd_stop_send ( fcpcmd ); - - /* Send command IU frame */ - if ( ( rc = xfer_deliver ( &fcpcmd->xchg, iob_disown ( iobuf ), - &meta ) ) != 0 ) { - DBGC ( fcpdev, "FCP %p xchg %04x cannot deliver command IU: " - "%s\n", fcpdev, fcpcmd->xchg_id, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle FCP read data IU - * - * @v fcpcmd FCP command - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fcpcmd_recv_rddata ( struct fcp_command *fcpcmd, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - struct scsi_cmd *command = &fcpcmd->command; - size_t offset = meta->offset; - size_t len = iob_len ( iobuf ); - int rc; - - /* Sanity checks */ - if ( ! ( meta->flags & XFER_FL_ABS_OFFSET ) ) { - DBGC ( fcpdev, "FCP %p xchg %04x read data missing offset\n", - fcpdev, fcpcmd->xchg_id ); - rc = -ERANGE_READ_DATA_ORDERING; - goto done; - } - if ( offset != fcpcmd->offset ) { - DBGC ( fcpdev, "FCP %p xchg %04x read data out of order " - "(expected %zd, received %zd)\n", - fcpdev, fcpcmd->xchg_id, fcpcmd->offset, offset ); - rc = -ERANGE_READ_DATA_ORDERING; - goto done; - } - if ( ( offset + len ) > command->data_in_len ) { - DBGC ( fcpdev, "FCP %p xchg %04x read data overrun (max %zd, " - "received %zd)\n", fcpdev, fcpcmd->xchg_id, - command->data_in_len, ( offset + len ) ); - rc = -ERANGE_READ_DATA_OVERRUN; - goto done; - } - DBGC2 ( fcpdev, "FCP %p xchg %04x RDDATA [%08zx,%08zx)\n", - fcpdev, fcpcmd->xchg_id, offset, ( offset + len ) ); - - /* Copy to user buffer */ - copy_to_user ( command->data_in, offset, iobuf->data, len ); - fcpcmd->offset += len; - assert ( fcpcmd->offset <= command->data_in_len ); - - rc = 0; - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Send FCP write data IU - * - * @v fcpcmd FCP command - * @ret rc Return status code - */ -static int fcpcmd_send_wrdata ( struct fcp_command *fcpcmd ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - struct scsi_cmd *command = &fcpcmd->command; - struct io_buffer *iobuf; - struct xfer_metadata meta; - size_t len; - int rc; - - /* Calculate length to be sent */ - len = xfer_window ( &fcpcmd->xchg ); - if ( len > fcpcmd->remaining ) - len = fcpcmd->remaining; - - /* Sanity checks */ - if ( len == 0 ) { - DBGC ( fcpdev, "FCP %p xchg %04x write data stuck\n", - fcpdev, fcpcmd->xchg_id ); - return -ERANGE_WRITE_DATA_STUCK; - } - if ( ( fcpcmd->offset + len ) > command->data_out_len ) { - DBGC ( fcpdev, "FCP %p xchg %04x write data overrun (max %zd, " - "requested %zd)\n", fcpdev, fcpcmd->xchg_id, - command->data_out_len, ( fcpcmd->offset + len ) ); - return -ERANGE_WRITE_DATA_OVERRUN; - } - - /* Allocate I/O buffer */ - iobuf = xfer_alloc_iob ( &fcpcmd->xchg, len ); - if ( ! iobuf ) { - DBGC ( fcpdev, "FCP %p xchg %04x cannot allocate write data " - "IU for %zd bytes\n", fcpdev, fcpcmd->xchg_id, len ); - return -ENOMEM; - } - - /* Construct data IU frame */ - copy_from_user ( iob_put ( iobuf, len ), command->data_out, - fcpcmd->offset, len ); - memset ( &meta, 0, sizeof ( meta ) ); - meta.flags = ( XFER_FL_RESPONSE | XFER_FL_ABS_OFFSET ); - meta.offset = fcpcmd->offset; - DBGC2 ( fcpdev, "FCP %p xchg %04x WRDATA [%08zx,%04zx)\n", - fcpdev, fcpcmd->xchg_id, fcpcmd->offset, - ( fcpcmd->offset + iob_len ( iobuf ) ) ); - - /* Calculate amount of data remaining to be sent within this IU */ - assert ( len <= fcpcmd->remaining ); - fcpcmd->offset += len; - fcpcmd->remaining -= len; - assert ( fcpcmd->offset <= command->data_out_len ); - if ( fcpcmd->remaining == 0 ) { - fcpcmd_stop_send ( fcpcmd ); - meta.flags |= XFER_FL_OVER; - } - - /* Send data IU frame */ - if ( ( rc = xfer_deliver ( &fcpcmd->xchg, iob_disown ( iobuf ), - &meta ) ) != 0 ) { - DBGC ( fcpdev, "FCP %p xchg %04x cannot deliver write data " - "IU: %s\n", fcpdev, fcpcmd->xchg_id, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle FCP transfer ready IU - * - * @v fcpcmd FCP command - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fcpcmd_recv_xfer_rdy ( struct fcp_command *fcpcmd, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - struct fcp_xfer_rdy *xfer_rdy = iobuf->data; - int rc; - - /* Sanity checks */ - if ( iob_len ( iobuf ) != sizeof ( *xfer_rdy ) ) { - DBGC ( fcpdev, "FCP %p xchg %04x received invalid transfer " - "ready IU:\n", fcpdev, fcpcmd->xchg_id ); - DBGC_HDA ( fcpdev, 0, iobuf->data, iob_len ( iobuf ) ); - rc = -EPROTO; - goto done; - } - if ( ntohl ( xfer_rdy->offset ) != fcpcmd->offset ) { - /* We do not advertise out-of-order delivery */ - DBGC ( fcpdev, "FCP %p xchg %04x cannot support out-of-order " - "delivery (expected %zd, requested %d)\n", - fcpdev, fcpcmd->xchg_id, fcpcmd->offset, - ntohl ( xfer_rdy->offset ) ); - rc = -EPROTO; - goto done; - } - DBGC2 ( fcpdev, "FCP %p xchg %04x XFER_RDY [%08x,%08x)\n", - fcpdev, fcpcmd->xchg_id, ntohl ( xfer_rdy->offset ), - ( ntohl ( xfer_rdy->offset ) + ntohl ( xfer_rdy->len ) ) ); - - /* Start sending requested data */ - fcpcmd->remaining = ntohl ( xfer_rdy->len ); - fcpcmd_start_send ( fcpcmd, fcpcmd_send_wrdata ); - - rc = 0; - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Handle FCP response IU - * - * @v fcpcmd FCP command - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fcpcmd_recv_rsp ( struct fcp_command *fcpcmd, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - struct scsi_cmd *command = &fcpcmd->command; - struct fcp_rsp *rsp = iobuf->data; - struct scsi_rsp response; - int rc; - - /* Sanity check */ - if ( ( iob_len ( iobuf ) < sizeof ( *rsp ) ) || - ( iob_len ( iobuf ) < ( sizeof ( *rsp ) + - fcp_rsp_response_data_len ( rsp ) + - fcp_rsp_sense_data_len ( rsp ) ) ) ) { - DBGC ( fcpdev, "FCP %p xchg %04x received invalid response " - "IU:\n", fcpdev, fcpcmd->xchg_id ); - DBGC_HDA ( fcpdev, 0, iobuf->data, iob_len ( iobuf ) ); - rc = -EPROTO; - goto done; - } - DBGC2 ( fcpdev, "FCP %p xchg %04x RSP stat %02x resid %08x flags %02x" - "%s%s%s%s\n", fcpdev, fcpcmd->xchg_id, rsp->status, - ntohl ( rsp->residual ), rsp->flags, - ( ( rsp->flags & FCP_RSP_RESPONSE_LEN_VALID ) ? " resp" : "" ), - ( ( rsp->flags & FCP_RSP_SENSE_LEN_VALID ) ? " sense" : "" ), - ( ( rsp->flags & FCP_RSP_RESIDUAL_OVERRUN ) ? " over" : "" ), - ( ( rsp->flags & FCP_RSP_RESIDUAL_UNDERRUN ) ? " under" : "" )); - if ( fcp_rsp_response_data ( rsp ) ) { - DBGC2 ( fcpdev, "FCP %p xchg %04x response data:\n", - fcpdev, fcpcmd->xchg_id ); - DBGC2_HDA ( fcpdev, 0, fcp_rsp_response_data ( rsp ), - fcp_rsp_response_data_len ( rsp ) ); - } - if ( fcp_rsp_sense_data ( rsp ) ) { - DBGC2 ( fcpdev, "FCP %p xchg %04x sense data:\n", - fcpdev, fcpcmd->xchg_id ); - DBGC2_HDA ( fcpdev, 0, fcp_rsp_sense_data ( rsp ), - fcp_rsp_sense_data_len ( rsp ) ); - } - - /* Check for locally-detected command underrun */ - if ( ( rsp->status == 0 ) && - ( fcpcmd->offset != ( command->data_in_len + - command->data_out_len ) ) ) { - DBGC ( fcpdev, "FCP %p xchg %04x data underrun (expected %zd, " - "got %zd)\n", fcpdev, fcpcmd->xchg_id, - ( command->data_in_len + command->data_out_len ), - fcpcmd->offset ); - rc = -ERANGE_DATA_UNDERRUN; - goto done; - } - - /* Build SCSI response */ - memset ( &response, 0, sizeof ( response ) ); - response.status = rsp->status; - if ( rsp->flags & ( FCP_RSP_RESIDUAL_OVERRUN | - FCP_RSP_RESIDUAL_UNDERRUN ) ) { - response.overrun = ntohl ( rsp->residual ); - if ( rsp->flags & FCP_RSP_RESIDUAL_UNDERRUN ) - response.overrun = -response.overrun; - } - scsi_parse_sense ( fcp_rsp_sense_data ( rsp ), - fcp_rsp_sense_data_len ( rsp ), &response.sense ); - - /* Free buffer before sending response, to minimise - * out-of-memory errors. - */ - free_iob ( iob_disown ( iobuf ) ); - - /* Send SCSI response */ - scsi_response ( &fcpcmd->scsi, &response ); - - /* Terminate command */ - fcpcmd_close ( fcpcmd, 0 ); - - rc = 0; - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Handle unknown FCP IU - * - * @v fcpcmd FCP command - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fcpcmd_recv_unknown ( struct fcp_command *fcpcmd, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct fcp_device *fcpdev = fcpcmd->fcpdev; - - DBGC ( fcpdev, "FCP %p xchg %04x received unknown IU:\n", - fcpdev, fcpcmd->xchg_id ); - DBGC_HDA ( fcpdev, 0, iobuf->data, iob_len ( iobuf ) ); - free_iob ( iobuf ); - return -EPROTO; -} - -/** - * Transmit FCP frame - * - * @v fcpcmd FCP command - */ -static void fcpcmd_step ( struct fcp_command *fcpcmd ) { - int rc; - - /* Send the current IU */ - if ( ( rc = fcpcmd->send ( fcpcmd ) ) != 0 ) { - /* Treat failure as a fatal error */ - fcpcmd_close ( fcpcmd, rc ); - } -} - -/** - * Receive FCP frame - * - * @v fcpcmd FCP command - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int fcpcmd_deliver ( struct fcp_command *fcpcmd, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - int ( * fcpcmd_recv ) ( struct fcp_command *fcpcmd, - struct io_buffer *iobuf, - struct xfer_metadata *meta ); - int rc; - - /* Determine handler */ - switch ( meta->flags & ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) { - case ( XFER_FL_RESPONSE ) : - fcpcmd_recv = fcpcmd_recv_rddata; - break; - case ( XFER_FL_CMD_STAT ) : - fcpcmd_recv = fcpcmd_recv_xfer_rdy; - break; - case ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) : - fcpcmd_recv = fcpcmd_recv_rsp; - break; - default: - fcpcmd_recv = fcpcmd_recv_unknown; - break; - } - - /* Handle IU */ - if ( ( rc = fcpcmd_recv ( fcpcmd, iob_disown ( iobuf ), meta ) ) != 0 ){ - /* Treat any error as fatal to the command */ - fcpcmd_close ( fcpcmd, rc ); - } - - return rc; -} - -/** FCP command SCSI interface operations */ -static struct interface_operation fcpcmd_scsi_op[] = { - INTF_OP ( intf_close, struct fcp_command *, fcpcmd_close ), -}; - -/** FCP command SCSI interface descriptor */ -static struct interface_descriptor fcpcmd_scsi_desc = - INTF_DESC_PASSTHRU ( struct fcp_command, scsi, fcpcmd_scsi_op, xchg ); - -/** FCP command Fibre Channel exchange interface operations */ -static struct interface_operation fcpcmd_xchg_op[] = { - INTF_OP ( xfer_deliver, struct fcp_command *, fcpcmd_deliver ), - INTF_OP ( intf_close, struct fcp_command *, fcpcmd_close_err ), -}; - -/** FCP command Fibre Channel exchange interface descriptor */ -static struct interface_descriptor fcpcmd_xchg_desc = - INTF_DESC_PASSTHRU ( struct fcp_command, xchg, fcpcmd_xchg_op, scsi ); - -/** FCP command process descriptor */ -static struct process_descriptor fcpcmd_process_desc = - PROC_DESC ( struct fcp_command, process, fcpcmd_step ); - -/** - * Issue FCP SCSI command - * - * @v fcpdev FCP device - * @v parent Parent interface - * @v command SCSI command - * @ret tag Command tag, or negative error - */ -static int fcpdev_scsi_command ( struct fcp_device *fcpdev, - struct interface *parent, - struct scsi_cmd *command ) { - struct fcp_prli_service_parameters *param = fcpdev->user.ulp->param; - struct fcp_command *fcpcmd; - int xchg_id; - int rc; - - /* Check link */ - if ( ( rc = fcpdev->user.ulp->link.rc ) != 0 ) { - DBGC ( fcpdev, "FCP %p could not issue command while link is " - "down: %s\n", fcpdev, strerror ( rc ) ); - goto err_link; - } - - /* Check target capability */ - assert ( param != NULL ); - assert ( fcpdev->user.ulp->param_len >= sizeof ( *param ) ); - if ( ! ( param->flags & htonl ( FCP_PRLI_TARGET ) ) ) { - DBGC ( fcpdev, "FCP %p could not issue command: not a target\n", - fcpdev ); - rc = -ENOTTY; - goto err_target; - } - - /* Allocate and initialise structure */ - fcpcmd = zalloc ( sizeof ( *fcpcmd ) ); - if ( ! fcpcmd ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &fcpcmd->refcnt, fcpcmd_free ); - intf_init ( &fcpcmd->scsi, &fcpcmd_scsi_desc, &fcpcmd->refcnt ); - intf_init ( &fcpcmd->xchg, &fcpcmd_xchg_desc, &fcpcmd->refcnt ); - process_init_stopped ( &fcpcmd->process, &fcpcmd_process_desc, - &fcpcmd->refcnt ); - fcpcmd->fcpdev = fcpdev_get ( fcpdev ); - list_add ( &fcpcmd->list, &fcpdev->fcpcmds ); - memcpy ( &fcpcmd->command, command, sizeof ( fcpcmd->command ) ); - - /* Create new exchange */ - if ( ( xchg_id = fc_xchg_originate ( &fcpcmd->xchg, - fcpdev->user.ulp->peer->port, - &fcpdev->user.ulp->peer->port_id, - FC_TYPE_FCP ) ) < 0 ) { - rc = xchg_id; - DBGC ( fcpdev, "FCP %p could not create exchange: %s\n", - fcpdev, strerror ( rc ) ); - goto err_xchg_originate; - } - fcpcmd->xchg_id = xchg_id; - - /* Start sending command IU */ - fcpcmd_start_send ( fcpcmd, fcpcmd_send_cmnd ); - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &fcpcmd->scsi, parent ); - ref_put ( &fcpcmd->refcnt ); - return ( FCP_TAG_MAGIC | fcpcmd->xchg_id ); - - err_xchg_originate: - fcpcmd_close ( fcpcmd, rc ); - ref_put ( &fcpcmd->refcnt ); - err_zalloc: - err_target: - err_link: - return rc; -} - -/** - * Close FCP device - * - * @v fcpdev FCP device - * @v rc Reason for close - */ -static void fcpdev_close ( struct fcp_device *fcpdev, int rc ) { - struct fcp_command *fcpcmd; - struct fcp_command *tmp; - - DBGC ( fcpdev, "FCP %p closed: %s\n", fcpdev, strerror ( rc ) ); - - /* Shut down interfaces */ - intf_shutdown ( &fcpdev->scsi, rc ); - - /* Shut down any active commands */ - list_for_each_entry_safe ( fcpcmd, tmp, &fcpdev->fcpcmds, list ) { - fcpcmd_get ( fcpcmd ); - fcpcmd_close ( fcpcmd, rc ); - fcpcmd_put ( fcpcmd ); - } - - /* Drop reference to ULP */ - fc_ulp_detach ( &fcpdev->user ); -} - -/** - * Check FCP device flow-control window - * - * @v fcpdev FCP device - * @ret len Length of window - */ -static size_t fcpdev_window ( struct fcp_device *fcpdev ) { - return ( fc_link_ok ( &fcpdev->user.ulp->link ) ? - ~( ( size_t ) 0 ) : 0 ); -} - -/** - * Describe FCP device in an ACPI table - * - * @v fcpdev FCP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int fcpdev_acpi_describe ( struct fcp_device *fcpdev, - struct acpi_description_header *acpi, - size_t len ) { - - DBGC ( fcpdev, "FCP %p cannot yet describe device in an ACPI table\n", - fcpdev ); - ( void ) acpi; - ( void ) len; - return 0; -} - -/** - * Describe FCP device using EDD - * - * @v fcpdev FCP device - * @v type EDD interface type - * @v path EDD device path - * @ret rc Return status code - */ -static int fcpdev_edd_describe ( struct fcp_device *fcpdev, - struct edd_interface_type *type, - union edd_device_path *path ) { - union { - struct fc_name fc; - uint64_t u64; - } wwn; - union { - struct scsi_lun scsi; - uint64_t u64; - } lun; - - type->type = cpu_to_le64 ( EDD_INTF_TYPE_FIBRE ); - memcpy ( &wwn.fc, &fcpdev->wwn, sizeof ( wwn.fc ) ); - path->fibre.wwn = be64_to_cpu ( wwn.u64 ); - memcpy ( &lun.scsi, &fcpdev->lun, sizeof ( lun.scsi ) ); - path->fibre.lun = be64_to_cpu ( lun.u64 ); - return 0; -} - -/** - * Identify device underlying FCP device - * - * @v fcpdev FCP device - * @ret device Underlying device - */ -static struct device * fcpdev_identify_device ( struct fcp_device *fcpdev ) { - - /* We know the underlying device only if the link is up; - * otherwise we don't have a port to examine. - */ - if ( ! fc_link_ok ( &fcpdev->user.ulp->link ) ) { - DBGC ( fcpdev, "FCP %p doesn't know underlying device " - "until link is up\n", fcpdev ); - return NULL; - } - - /* Hand off to port's transport interface */ - assert ( fcpdev->user.ulp->peer->port != NULL ); - return identify_device ( &fcpdev->user.ulp->peer->port->transport ); -} - -/** FCP device SCSI interface operations */ -static struct interface_operation fcpdev_scsi_op[] = { - INTF_OP ( scsi_command, struct fcp_device *, fcpdev_scsi_command ), - INTF_OP ( xfer_window, struct fcp_device *, fcpdev_window ), - INTF_OP ( intf_close, struct fcp_device *, fcpdev_close ), - INTF_OP ( acpi_describe, struct fcp_device *, fcpdev_acpi_describe ), - INTF_OP ( edd_describe, struct fcp_device *, fcpdev_edd_describe ), - INTF_OP ( identify_device, struct fcp_device *, - fcpdev_identify_device ), -}; - -/** FCP device SCSI interface descriptor */ -static struct interface_descriptor fcpdev_scsi_desc = - INTF_DESC ( struct fcp_device, scsi, fcpdev_scsi_op ); - -/** - * Examine FCP ULP link state - * - * @v user Fibre Channel upper-layer protocol user - */ -static void fcpdev_examine ( struct fc_ulp_user *user ) { - struct fcp_device *fcpdev = - container_of ( user, struct fcp_device, user ); - - if ( fc_link_ok ( &fcpdev->user.ulp->link ) ) { - DBGC ( fcpdev, "FCP %p link is up\n", fcpdev ); - } else { - DBGC ( fcpdev, "FCP %p link is down: %s\n", - fcpdev, strerror ( fcpdev->user.ulp->link.rc ) ); - } - - /* Notify SCSI layer of window change */ - xfer_window_changed ( &fcpdev->scsi ); -} - -/** - * Open FCP device - * - * @v parent Parent interface - * @v wwn Fibre Channel WWN - * @v lun SCSI LUN - * @ret rc Return status code - */ -static int fcpdev_open ( struct interface *parent, struct fc_name *wwn, - struct scsi_lun *lun ) { - struct fc_ulp *ulp; - struct fcp_device *fcpdev; - int rc; - - /* Get Fibre Channel ULP interface */ - ulp = fc_ulp_get_wwn_type ( wwn, FC_TYPE_FCP ); - if ( ! ulp ) { - rc = -ENOMEM; - goto err_ulp_get; - } - - /* Allocate and initialise structure */ - fcpdev = zalloc ( sizeof ( *fcpdev ) ); - if ( ! fcpdev ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &fcpdev->refcnt, NULL ); - intf_init ( &fcpdev->scsi, &fcpdev_scsi_desc, &fcpdev->refcnt ); - INIT_LIST_HEAD ( &fcpdev->fcpcmds ); - fc_ulp_user_init ( &fcpdev->user, fcpdev_examine, &fcpdev->refcnt ); - - DBGC ( fcpdev, "FCP %p opened for %s\n", fcpdev, fc_ntoa ( wwn ) ); - - /* Attach to Fibre Channel ULP */ - fc_ulp_attach ( ulp, &fcpdev->user ); - - /* Preserve parameters required for boot firmware table */ - memcpy ( &fcpdev->wwn, wwn, sizeof ( fcpdev->wwn ) ); - memcpy ( &fcpdev->lun, lun, sizeof ( fcpdev->lun ) ); - - /* Attach SCSI device to parent interface */ - if ( ( rc = scsi_open ( parent, &fcpdev->scsi, lun ) ) != 0 ) { - DBGC ( fcpdev, "FCP %p could not create SCSI device: %s\n", - fcpdev, strerror ( rc ) ); - goto err_scsi_open; - } - - /* Drop temporary reference to ULP */ - fc_ulp_put ( ulp ); - - /* Mortalise self and return */ - ref_put ( &fcpdev->refcnt ); - return 0; - - err_scsi_open: - fcpdev_close ( fcpdev, rc ); - ref_put ( &fcpdev->refcnt ); - err_zalloc: - fc_ulp_put ( ulp ); - err_ulp_get: - return rc; -} - -/****************************************************************************** - * - * FCP URIs - * - ****************************************************************************** - */ - -/** - * Parse FCP URI - * - * @v uri URI - * @ret wwn Fibre Channel WWN - * @ret lun SCSI LUN - * @ret rc Return status code - * - * An FCP URI has the form "fcp:<wwn>:<lun>" or "fcp://<wwn>/<lun>" - */ -static int fcp_parse_uri ( struct uri *uri, struct fc_name *wwn, - struct scsi_lun *lun ) { - char wwn_buf[ FC_NAME_STRLEN + 1 /* NUL */ ]; - const char *wwn_text; - const char *lun_text; - int rc; - - /* Extract WWN and LUN texts from URI */ - if ( uri->opaque ) { - /* "fcp:<wwn>:<lun>" */ - if ( snprintf ( wwn_buf, sizeof ( wwn_buf ), "%s", - uri->opaque ) < ( FC_NAME_STRLEN + 1 /* : */ ) ) - return -EINVAL; - if ( uri->opaque[FC_NAME_STRLEN] != ':' ) - return -EINVAL; - wwn_text = wwn_buf; - lun_text = &uri->opaque[FC_NAME_STRLEN + 1]; - } else { - /* If host exists, path must also exist */ - if ( ! ( uri->host && uri->path ) ) - return -EINVAL; - if ( uri->path[0] != '/' ) - return -EINVAL; - wwn_text = uri->host; - lun_text = ( uri->path + 1 ); - } - - /* Parse WWN */ - if ( ( rc = fc_aton ( wwn_text, wwn ) ) != 0 ) - return rc; - - /* Parse LUN */ - if ( ( rc = scsi_parse_lun ( lun_text, lun ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Open FCP URI - * - * @v parent Parent interface - * @v uri URI - * @ret rc Return status code - */ -static int fcp_open ( struct interface *parent, struct uri *uri ) { - struct fc_name wwn; - struct scsi_lun lun; - int rc; - - /* Parse URI */ - if ( ( rc = fcp_parse_uri ( uri, &wwn, &lun ) ) != 0 ) - return rc; - - /* Open FCP device */ - if ( ( rc = fcpdev_open ( parent, &wwn, &lun ) ) != 0 ) - return rc; - - return 0; -} - -/** FCP URI opener */ -struct uri_opener fcp_uri_opener __uri_opener = { - .scheme = "fcp", - .open = fcp_open, -}; diff --git a/qemu/roms/ipxe/src/net/fragment.c b/qemu/roms/ipxe/src/net/fragment.c deleted file mode 100644 index 781b9bc60..000000000 --- a/qemu/roms/ipxe/src/net/fragment.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <ipxe/retry.h> -#include <ipxe/timer.h> -#include <ipxe/ipstat.h> -#include <ipxe/fragment.h> - -/** @file - * - * Fragment reassembly - * - */ - -/** - * Expire fragment reassembly buffer - * - * @v timer Retry timer - * @v fail Failure indicator - */ -static void fragment_expired ( struct retry_timer *timer, int fail __unused ) { - struct fragment *fragment = - container_of ( timer, struct fragment, timer ); - - DBGC ( fragment, "FRAG %p expired\n", fragment ); - free_iob ( fragment->iobuf ); - list_del ( &fragment->list ); - fragment->fragments->stats->reasm_fails++; - free ( fragment ); -} - -/** - * Find fragment reassembly buffer - * - * @v fragments Fragment reassembler - * @v iobuf I/O buffer - * @v hdrlen Length of non-fragmentable potion of I/O buffer - * @ret fragment Fragment reassembly buffer, or NULL if not found - */ -static struct fragment * fragment_find ( struct fragment_reassembler *fragments, - struct io_buffer *iobuf, - size_t hdrlen ) { - struct fragment *fragment; - - list_for_each_entry ( fragment, &fragments->list, list ) { - if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) ) - return fragment; - } - return NULL; -} - -/** - * Reassemble packet - * - * @v fragments Fragment reassembler - * @v iobuf I/O buffer - * @v hdrlen Length of non-fragmentable potion of I/O buffer - * @ret iobuf Reassembled packet, or NULL - * - * This function takes ownership of the I/O buffer. Note that the - * length of the non-fragmentable portion may be modified. - */ -struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments, - struct io_buffer *iobuf, - size_t *hdrlen ) { - struct fragment *fragment; - struct io_buffer *new_iobuf; - size_t new_len; - size_t offset; - size_t expected_offset; - int more_frags; - - /* Update statistics */ - fragments->stats->reasm_reqds++; - - /* Find matching fragment reassembly buffer, if any */ - fragment = fragment_find ( fragments, iobuf, *hdrlen ); - - /* Drop out-of-order fragments */ - offset = fragments->fragment_offset ( iobuf, *hdrlen ); - expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) - - fragment->hdrlen ) : 0 ); - if ( offset != expected_offset ) { - DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment " - "[%zd,%zd), expected [%zd,...)\n", fragment, offset, - ( offset + iob_len ( iobuf ) - *hdrlen ), - expected_offset ); - goto drop; - } - - /* Create or extend fragment reassembly buffer as applicable */ - if ( ! fragment ) { - - /* Create new fragment reassembly buffer */ - fragment = zalloc ( sizeof ( *fragment ) ); - if ( ! fragment ) - goto drop; - list_add ( &fragment->list, &fragments->list ); - fragment->iobuf = iobuf; - fragment->hdrlen = *hdrlen; - timer_init ( &fragment->timer, fragment_expired, NULL ); - fragment->fragments = fragments; - DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment, - ( iob_len ( iobuf ) - *hdrlen ) ); - - } else { - - /* Check if this is the final fragment */ - more_frags = fragments->more_fragments ( iobuf, *hdrlen ); - DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment, - offset, ( offset + iob_len ( iobuf ) - *hdrlen ), - ( more_frags ? "" : " complete" ) ); - - /* Extend fragment reassembly buffer. Preserve I/O - * buffer headroom to allow for code which modifies - * and resends the buffer (e.g. ICMP echo responses). - */ - iob_pull ( iobuf, *hdrlen ); - new_len = ( iob_headroom ( fragment->iobuf ) + - iob_len ( fragment->iobuf ) + iob_len ( iobuf ) ); - new_iobuf = alloc_iob ( new_len ); - if ( ! new_iobuf ) { - DBGC ( fragment, "FRAG %p could not extend reassembly " - "buffer to %zd bytes\n", fragment, new_len ); - goto drop; - } - iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) ); - memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ), - fragment->iobuf->data, iob_len ( fragment->iobuf ) ); - memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ), - iobuf->data, iob_len ( iobuf ) ); - free_iob ( fragment->iobuf ); - fragment->iobuf = new_iobuf; - free_iob ( iobuf ); - - /* Stop fragment reassembly timer */ - stop_timer ( &fragment->timer ); - - /* If this is the final fragment, return it */ - if ( ! more_frags ) { - iobuf = fragment->iobuf; - *hdrlen = fragment->hdrlen; - list_del ( &fragment->list ); - free ( fragment ); - fragments->stats->reasm_oks++; - return iobuf; - } - } - - /* (Re)start fragment reassembly timer */ - start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT ); - - return NULL; - - drop: - fragments->stats->reasm_fails++; - free_iob ( iobuf ); - return NULL; -} diff --git a/qemu/roms/ipxe/src/net/icmp.c b/qemu/roms/ipxe/src/net/icmp.c deleted file mode 100644 index 5371277e4..000000000 --- a/qemu/roms/ipxe/src/net/icmp.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <ipxe/iobuf.h> -#include <ipxe/in.h> -#include <ipxe/tcpip.h> -#include <ipxe/ping.h> -#include <ipxe/crc32.h> -#include <ipxe/icmp.h> - -/** @file - * - * ICMP protocol - * - */ - -/** - * Identify ICMP echo protocol - * - * @v st_family Address family - * @ret echo_protocol ICMP echo protocol, or NULL - */ -static struct icmp_echo_protocol * icmp_echo_protocol ( sa_family_t family ) { - struct icmp_echo_protocol *echo_protocol; - - for_each_table_entry ( echo_protocol, ICMP_ECHO_PROTOCOLS ) { - if ( echo_protocol->family == family ) - return echo_protocol; - } - return NULL; -} - -/** - * - * Determine debugging colour for ICMP debug messages - * - * @v st_peer Peer address - * @ret col Debugging colour (for DBGC()) - */ -static uint32_t icmpcol ( struct sockaddr_tcpip *st_peer ) { - - return crc32_le ( 0, st_peer, sizeof ( *st_peer ) ); -} - -/** - * Transmit ICMP echo packet - * - * @v iobuf I/O buffer - * @v st_dest Destination socket address - * @v echo_protocol ICMP echo protocol - * @ret rc Return status code - */ -static int icmp_tx_echo ( struct io_buffer *iobuf, - struct sockaddr_tcpip *st_dest, - struct icmp_echo_protocol *echo_protocol ) { - struct icmp_echo *echo = iobuf->data; - int rc; - - /* Set ICMP type and (re)calculate checksum */ - echo->icmp.chksum = 0; - echo->icmp.chksum = tcpip_chksum ( echo, iob_len ( iobuf ) ); - - /* Transmit packet */ - if ( ( rc = tcpip_tx ( iobuf, echo_protocol->tcpip_protocol, NULL, - st_dest, NULL, - ( echo_protocol->net_checksum ? - &echo->icmp.chksum : NULL ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Transmit ICMP echo request - * - * @v iobuf I/O buffer - * @v st_dest Destination socket address - * @ret rc Return status code - */ -int icmp_tx_echo_request ( struct io_buffer *iobuf, - struct sockaddr_tcpip *st_dest ) { - struct icmp_echo *echo = iobuf->data; - struct icmp_echo_protocol *echo_protocol; - int rc; - - /* Identify ICMP echo protocol */ - echo_protocol = icmp_echo_protocol ( st_dest->st_family ); - if ( ! echo_protocol ) { - DBGC ( icmpcol ( st_dest ), "ICMP TX echo request unknown " - "address family %d\n", st_dest->st_family ); - free_iob ( iobuf ); - return -ENOTSUP; - } - - /* Set type */ - echo->icmp.type = echo_protocol->request; - - /* Transmit request */ - DBGC ( icmpcol ( st_dest ), "ICMP TX echo request id %04x seq %04x\n", - ntohs ( echo->ident ), ntohs ( echo->sequence ) ); - if ( ( rc = icmp_tx_echo ( iobuf, st_dest, echo_protocol ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Transmit ICMP echo reply - * - * @v iobuf I/O buffer - * @v st_dest Destination socket address - * @ret rc Return status code - */ -static int icmp_tx_echo_reply ( struct io_buffer *iobuf, - struct sockaddr_tcpip *st_dest, - struct icmp_echo_protocol *echo_protocol ) { - struct icmp_echo *echo = iobuf->data; - int rc; - - /* Set type */ - echo->icmp.type = echo_protocol->reply; - - /* Transmit reply */ - DBGC ( icmpcol ( st_dest ), "ICMP TX echo reply id %04x seq %04x\n", - ntohs ( echo->ident ), ntohs ( echo->sequence ) ); - if ( ( rc = icmp_tx_echo ( iobuf, st_dest, echo_protocol ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Process a received ICMP echo request - * - * @v iobuf I/O buffer - * @v st_src Source socket address - * @v echo_protocol ICMP echo protocol - * @ret rc Return status code - */ -int icmp_rx_echo_request ( struct io_buffer *iobuf, - struct sockaddr_tcpip *st_src, - struct icmp_echo_protocol *echo_protocol ) { - struct icmp_echo *echo = iobuf->data; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *echo ) ) { - DBGC ( icmpcol ( st_src ), "ICMP RX echo request too short at " - "%zd bytes (min %zd bytes)\n", - iob_len ( iobuf ), sizeof ( *echo ) ); - free_iob ( iobuf ); - return -EINVAL; - } - DBGC ( icmpcol ( st_src ), "ICMP RX echo request id %04x seq %04x\n", - ntohs ( echo->ident ), ntohs ( echo->sequence ) ); - - /* Transmit echo reply */ - if ( ( rc = icmp_tx_echo_reply ( iobuf, st_src, echo_protocol ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Process a received ICMP echo request - * - * @v iobuf I/O buffer - * @v st_src Source socket address - * @ret rc Return status code - */ -int icmp_rx_echo_reply ( struct io_buffer *iobuf, - struct sockaddr_tcpip *st_src ) { - struct icmp_echo *echo = iobuf->data; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *echo ) ) { - DBGC ( icmpcol ( st_src ), "ICMP RX echo reply too short at " - "%zd bytes (min %zd bytes)\n", - iob_len ( iobuf ), sizeof ( *echo ) ); - free_iob ( iobuf ); - return -EINVAL; - } - DBGC ( icmpcol ( st_src ), "ICMP RX echo reply id %04x seq %04x\n", - ntohs ( echo->ident ), ntohs ( echo->sequence ) ); - - /* Deliver to ping protocol */ - if ( ( rc = ping_rx ( iobuf, st_src ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Receive ping reply (when no ping protocol is present) - * - * @v iobuf I/O buffer - * @v st_src Source socket address - * @ret rc Return status code - */ -__weak int ping_rx ( struct io_buffer *iobuf, - struct sockaddr_tcpip *st_src __unused ) { - free_iob ( iobuf ); - return 0; -} diff --git a/qemu/roms/ipxe/src/net/icmpv4.c b/qemu/roms/ipxe/src/net/icmpv4.c deleted file mode 100644 index 0858ff37f..000000000 --- a/qemu/roms/ipxe/src/net/icmpv4.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <string.h> -#include <errno.h> -#include <ipxe/iobuf.h> -#include <ipxe/in.h> -#include <ipxe/tcpip.h> -#include <ipxe/icmp.h> - -/** @file - * - * ICMPv4 protocol - * - */ - -struct icmp_echo_protocol icmpv4_echo_protocol __icmp_echo_protocol; - -/** - * Process a received packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v st_src Partially-filled source address - * @v st_dest Partially-filled destination address - * @v pshdr_csum Pseudo-header checksum - * @ret rc Return status code - */ -static int icmpv4_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest __unused, - uint16_t pshdr_csum __unused ) { - struct icmp_header *icmp = iobuf->data; - size_t len = iob_len ( iobuf ); - unsigned int csum; - unsigned int type; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *icmp ) ) { - DBG ( "ICMP packet too short at %zd bytes (min %zd bytes)\n", - len, sizeof ( *icmp ) ); - rc = -EINVAL; - goto discard; - } - - /* Verify checksum */ - csum = tcpip_chksum ( icmp, len ); - if ( csum != 0 ) { - DBG ( "ICMP checksum incorrect (is %04x, should be 0000)\n", - csum ); - DBG_HD ( icmp, len ); - rc = -EINVAL; - goto discard; - } - - /* Handle ICMP packet */ - type = icmp->type; - switch ( type ) { - case ICMP_ECHO_REQUEST: - return icmp_rx_echo_request ( iobuf, st_src, - &icmpv4_echo_protocol ); - case ICMP_ECHO_REPLY: - return icmp_rx_echo_reply ( iobuf, st_src ); - default: - DBG ( "ICMP ignoring type %d\n", type ); - rc = 0; - break; - } - - discard: - free_iob ( iobuf ); - return rc; -} - -/** ICMPv4 TCP/IP protocol */ -struct tcpip_protocol icmpv4_protocol __tcpip_protocol = { - .name = "ICMPv4", - .rx = icmpv4_rx, - .tcpip_proto = IP_ICMP, -}; - -/** ICMPv4 echo protocol */ -struct icmp_echo_protocol icmpv4_echo_protocol __icmp_echo_protocol = { - .family = AF_INET, - .request = ICMP_ECHO_REQUEST, - .reply = ICMP_ECHO_REPLY, - .tcpip_protocol = &icmpv4_protocol, - .net_checksum = 0, -}; diff --git a/qemu/roms/ipxe/src/net/icmpv6.c b/qemu/roms/ipxe/src/net/icmpv6.c deleted file mode 100644 index 8555aaf0b..000000000 --- a/qemu/roms/ipxe/src/net/icmpv6.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/tcpip.h> -#include <ipxe/ping.h> -#include <ipxe/icmpv6.h> - -/** @file - * - * ICMPv6 protocol - * - */ - -/* Disambiguate the various error causes */ -#define EHOSTUNREACH_ROUTE \ - __einfo_error ( EINFO_EHOSTUNREACH_ROUTE ) -#define EINFO_EHOSTUNREACH_ROUTE \ - __einfo_uniqify ( EINFO_EHOSTUNREACH, 0, \ - "No route to destination" ) -#define EHOSTUNREACH_PROHIBITED \ - __einfo_error ( EINFO_EHOSTUNREACH_PROHIBITED ) -#define EINFO_EHOSTUNREACH_PROHIBITED \ - __einfo_uniqify ( EINFO_EHOSTUNREACH, 1, \ - "Communication administratively prohibited" ) -#define EHOSTUNREACH_ADDRESS \ - __einfo_error ( EINFO_EHOSTUNREACH_ADDRESS ) -#define EINFO_EHOSTUNREACH_ADDRESS \ - __einfo_uniqify ( EINFO_EHOSTUNREACH, 3, \ - "Address unreachable" ) -#define EHOSTUNREACH_PORT \ - __einfo_error ( EINFO_EHOSTUNREACH_PORT ) -#define EINFO_EHOSTUNREACH_PORT \ - __einfo_uniqify ( EINFO_EHOSTUNREACH, 4, \ - "Port unreachable" ) -#define EHOSTUNREACH_CODE( code ) \ - EUNIQ ( EINFO_EHOSTUNREACH, ( (code) & 0x1f ), \ - EHOSTUNREACH_ROUTE, EHOSTUNREACH_PROHIBITED, \ - EHOSTUNREACH_ADDRESS, EHOSTUNREACH_PORT ) - -#define ETIMEDOUT_HOP \ - __einfo_error ( EINFO_ETIMEDOUT_HOP ) -#define EINFO_ETIMEDOUT_HOP \ - __einfo_uniqify ( EINFO_ETIMEDOUT, 0, \ - "Hop limit exceeded in transit" ) -#define ETIMEDOUT_REASSEMBLY \ - __einfo_error ( EINFO_ETIMEDOUT_REASSEMBLY ) -#define EINFO_ETIMEDOUT_REASSEMBLY \ - __einfo_uniqify ( EINFO_ETIMEDOUT, 1, \ - "Fragment reassembly time exceeded" ) -#define ETIMEDOUT_CODE( code ) \ - EUNIQ ( EINFO_ETIMEDOUT, ( (code) & 0x1f ), \ - ETIMEDOUT_HOP, ETIMEDOUT_REASSEMBLY ) - -#define EPROTO_BAD_HEADER \ - __einfo_error ( EINFO_EPROTO_BAD_HEADER ) -#define EINFO_EPROTO_BAD_HEADER \ - __einfo_uniqify ( EINFO_EPROTO, 0, \ - "Erroneous header field" ) -#define EPROTO_NEXT_HEADER \ - __einfo_error ( EINFO_EPROTO_NEXT_HEADER ) -#define EINFO_EPROTO_NEXT_HEADER \ - __einfo_uniqify ( EINFO_EPROTO, 1, \ - "Unrecognised next header type" ) -#define EPROTO_OPTION \ - __einfo_error ( EINFO_EPROTO_OPTION ) -#define EINFO_EPROTO_OPTION \ - __einfo_uniqify ( EINFO_EPROTO, 2, \ - "Unrecognised IPv6 option" ) -#define EPROTO_CODE( code ) \ - EUNIQ ( EINFO_EPROTO, ( (code) & 0x1f ), \ - EPROTO_BAD_HEADER, EPROTO_NEXT_HEADER, EPROTO_OPTION ) - -struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol; - -/** - * Process received ICMPv6 echo request packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v sin6_src Source socket address - * @v sin6_dest Destination socket address - * @ret rc Return status code - */ -static int icmpv6_rx_echo_request ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - struct sockaddr_in6 *sin6_src, - struct sockaddr_in6 *sin6_dest __unused ) { - struct sockaddr_tcpip *st_src = - ( ( struct sockaddr_tcpip * ) sin6_src ); - - return icmp_rx_echo_request ( iobuf, st_src, &icmpv6_echo_protocol ); -} - -/** ICMPv6 echo request handler */ -struct icmpv6_handler icmpv6_echo_request_handler __icmpv6_handler = { - .type = ICMPV6_ECHO_REQUEST, - .rx = icmpv6_rx_echo_request, -}; - -/** - * Process received ICMPv6 echo reply packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v sin6_src Source socket address - * @v sin6_dest Destination socket address - * @ret rc Return status code - */ -static int icmpv6_rx_echo_reply ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - struct sockaddr_in6 *sin6_src, - struct sockaddr_in6 *sin6_dest __unused ) { - struct sockaddr_tcpip *st_src = - ( ( struct sockaddr_tcpip * ) sin6_src ); - - return icmp_rx_echo_reply ( iobuf, st_src ); -} - -/** ICMPv6 echo reply handler */ -struct icmpv6_handler icmpv6_echo_reply_handler __icmpv6_handler = { - .type = ICMPV6_ECHO_REPLY, - .rx = icmpv6_rx_echo_reply, -}; - -/** - * Identify ICMPv6 handler - * - * @v type ICMPv6 type - * @ret handler ICMPv6 handler, or NULL if not found - */ -static struct icmpv6_handler * icmpv6_handler ( unsigned int type ) { - struct icmpv6_handler *handler; - - for_each_table_entry ( handler, ICMPV6_HANDLERS ) { - if ( handler->type == type ) - return handler; - } - return NULL; -} - -/** - * Process a received packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v st_src Partially-filled source address - * @v st_dest Partially-filled destination address - * @v pshdr_csum Pseudo-header checksum - * @ret rc Return status code - */ -static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, - struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) { - struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src ); - struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest ); - struct icmp_header *icmp = iobuf->data; - size_t len = iob_len ( iobuf ); - struct icmpv6_handler *handler; - unsigned int csum; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *icmp ) ) { - DBGC ( netdev, "ICMPv6 packet too short at %zd bytes (min %zd " - "bytes)\n", len, sizeof ( *icmp ) ); - rc = -EINVAL; - goto done; - } - - /* Verify checksum */ - csum = tcpip_continue_chksum ( pshdr_csum, icmp, len ); - if ( csum != 0 ) { - DBGC ( netdev, "ICMPv6 checksum incorrect (is %04x, should be " - "0000)\n", csum ); - DBGC_HDA ( netdev, 0, icmp, len ); - rc = -EINVAL; - goto done; - } - - /* Identify handler */ - handler = icmpv6_handler ( icmp->type ); - if ( ! handler ) { - switch ( icmp->type ) { - case ICMPV6_DESTINATION_UNREACHABLE: - rc = -EHOSTUNREACH_CODE ( icmp->code ); - break; - case ICMPV6_PACKET_TOO_BIG: - rc = -ERANGE; - break; - case ICMPV6_TIME_EXCEEDED: - rc = -ETIMEDOUT_CODE ( icmp->code ); - break; - case ICMPV6_PARAMETER_PROBLEM: - rc = -EPROTO_CODE ( icmp->code ); - break; - default: - DBGC ( netdev, "ICMPv6 unrecognised type %d code %d\n", - icmp->type, icmp->code ); - rc = -ENOTSUP; - break; - }; - goto done; - } - - /* Pass to handler */ - if ( ( rc = handler->rx ( iob_disown ( iobuf ), netdev, sin6_src, - sin6_dest ) ) != 0 ) { - DBGC ( netdev, "ICMPv6 could not handle type %d: %s\n", - icmp->type, strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** ICMPv6 TCP/IP protocol */ -struct tcpip_protocol icmpv6_protocol __tcpip_protocol = { - .name = "ICMPv6", - .rx = icmpv6_rx, - .tcpip_proto = IP_ICMP6, -}; - -/** ICMPv6 echo protocol */ -struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol = { - .family = AF_INET6, - .request = ICMPV6_ECHO_REQUEST, - .reply = ICMPV6_ECHO_REPLY, - .tcpip_protocol = &icmpv6_protocol, - .net_checksum = 1, -}; diff --git a/qemu/roms/ipxe/src/net/infiniband.c b/qemu/roms/ipxe/src/net/infiniband.c deleted file mode 100644 index 2e3d76d54..000000000 --- a/qemu/roms/ipxe/src/net/infiniband.c +++ /dev/null @@ -1,1015 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <byteswap.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/list.h> -#include <ipxe/errortab.h> -#include <ipxe/if_arp.h> -#include <ipxe/netdevice.h> -#include <ipxe/iobuf.h> -#include <ipxe/process.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_mi.h> -#include <ipxe/ib_sma.h> - -/** @file - * - * Infiniband protocol - * - */ - -/** List of Infiniband devices */ -struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices ); - -/** List of open Infiniband devices, in reverse order of opening */ -static struct list_head open_ib_devices = LIST_HEAD_INIT ( open_ib_devices ); - -/* Disambiguate the various possible EINPROGRESSes */ -#define EINPROGRESS_INIT __einfo_error ( EINFO_EINPROGRESS_INIT ) -#define EINFO_EINPROGRESS_INIT __einfo_uniqify \ - ( EINFO_EINPROGRESS, 0x01, "Initialising" ) -#define EINPROGRESS_ARMED __einfo_error ( EINFO_EINPROGRESS_ARMED ) -#define EINFO_EINPROGRESS_ARMED __einfo_uniqify \ - ( EINFO_EINPROGRESS, 0x02, "Armed" ) - -/** Human-readable message for the link statuses */ -struct errortab infiniband_errors[] __errortab = { - __einfo_errortab ( EINFO_EINPROGRESS_INIT ), - __einfo_errortab ( EINFO_EINPROGRESS_ARMED ), -}; - -/*************************************************************************** - * - * Completion queues - * - *************************************************************************** - */ - -/** - * Create completion queue - * - * @v ibdev Infiniband device - * @v num_cqes Number of completion queue entries - * @v op Completion queue operations - * @ret cq New completion queue - */ -struct ib_completion_queue * -ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, - struct ib_completion_queue_operations *op ) { - struct ib_completion_queue *cq; - int rc; - - DBGC ( ibdev, "IBDEV %p creating completion queue\n", ibdev ); - - /* Allocate and initialise data structure */ - cq = zalloc ( sizeof ( *cq ) ); - if ( ! cq ) - goto err_alloc_cq; - cq->ibdev = ibdev; - list_add ( &cq->list, &ibdev->cqs ); - cq->num_cqes = num_cqes; - INIT_LIST_HEAD ( &cq->work_queues ); - cq->op = op; - - /* Perform device-specific initialisation and get CQN */ - if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not initialise completion " - "queue: %s\n", ibdev, strerror ( rc ) ); - goto err_dev_create_cq; - } - - DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) " - "with CQN %#lx\n", ibdev, num_cqes, cq, - ib_cq_get_drvdata ( cq ), cq->cqn ); - return cq; - - ibdev->op->destroy_cq ( ibdev, cq ); - err_dev_create_cq: - list_del ( &cq->list ); - free ( cq ); - err_alloc_cq: - return NULL; -} - -/** - * Destroy completion queue - * - * @v ibdev Infiniband device - * @v cq Completion queue - */ -void ib_destroy_cq ( struct ib_device *ibdev, - struct ib_completion_queue *cq ) { - DBGC ( ibdev, "IBDEV %p destroying completion queue %#lx\n", - ibdev, cq->cqn ); - assert ( list_empty ( &cq->work_queues ) ); - ibdev->op->destroy_cq ( ibdev, cq ); - list_del ( &cq->list ); - free ( cq ); -} - -/** - * Poll completion queue - * - * @v ibdev Infiniband device - * @v cq Completion queue - */ -void ib_poll_cq ( struct ib_device *ibdev, - struct ib_completion_queue *cq ) { - struct ib_work_queue *wq; - - /* Poll completion queue */ - ibdev->op->poll_cq ( ibdev, cq ); - - /* Refill receive work queues */ - list_for_each_entry ( wq, &cq->work_queues, list ) { - if ( ! wq->is_send ) - ib_refill_recv ( ibdev, wq->qp ); - } -} - -/*************************************************************************** - * - * Work queues - * - *************************************************************************** - */ - -/** - * Create queue pair - * - * @v ibdev Infiniband device - * @v type Queue pair type - * @v num_send_wqes Number of send work queue entries - * @v send_cq Send completion queue - * @v num_recv_wqes Number of receive work queue entries - * @v recv_cq Receive completion queue - * @v op Queue pair operations - * @ret qp Queue pair - * - * The queue pair will be left in the INIT state; you must call - * ib_modify_qp() before it is ready to use for sending and receiving. - */ -struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, - enum ib_queue_pair_type type, - unsigned int num_send_wqes, - struct ib_completion_queue *send_cq, - unsigned int num_recv_wqes, - struct ib_completion_queue *recv_cq, - struct ib_queue_pair_operations *op ) { - struct ib_queue_pair *qp; - size_t total_size; - int rc; - - DBGC ( ibdev, "IBDEV %p creating queue pair\n", ibdev ); - - /* Allocate and initialise data structure */ - total_size = ( sizeof ( *qp ) + - ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ) + - ( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) ); - qp = zalloc ( total_size ); - if ( ! qp ) - goto err_alloc_qp; - qp->ibdev = ibdev; - list_add ( &qp->list, &ibdev->qps ); - qp->type = type; - qp->send.qp = qp; - qp->send.is_send = 1; - qp->send.cq = send_cq; - list_add ( &qp->send.list, &send_cq->work_queues ); - qp->send.psn = ( random() & 0xffffffUL ); - qp->send.num_wqes = num_send_wqes; - qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) ); - qp->recv.qp = qp; - qp->recv.cq = recv_cq; - list_add ( &qp->recv.list, &recv_cq->work_queues ); - qp->recv.psn = ( random() & 0xffffffUL ); - qp->recv.num_wqes = num_recv_wqes; - qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) + - ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) )); - INIT_LIST_HEAD ( &qp->mgids ); - qp->op = op; - - /* Perform device-specific initialisation and get QPN */ - if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not initialise queue pair: " - "%s\n", ibdev, strerror ( rc ) ); - goto err_dev_create_qp; - } - DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n", - ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn ); - DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n", - ibdev, qp->qpn, num_send_wqes, qp->send.iobufs, - qp->recv.iobufs ); - DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n", - ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs, - ( ( ( void * ) qp ) + total_size ) ); - - /* Calculate externally-visible QPN */ - switch ( type ) { - case IB_QPT_SMI: - qp->ext_qpn = IB_QPN_SMI; - break; - case IB_QPT_GSI: - qp->ext_qpn = IB_QPN_GSI; - break; - default: - qp->ext_qpn = qp->qpn; - break; - } - if ( qp->ext_qpn != qp->qpn ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx has external QPN %#lx\n", - ibdev, qp->qpn, qp->ext_qpn ); - } - - return qp; - - ibdev->op->destroy_qp ( ibdev, qp ); - err_dev_create_qp: - list_del ( &qp->send.list ); - list_del ( &qp->recv.list ); - list_del ( &qp->list ); - free ( qp ); - err_alloc_qp: - return NULL; -} - -/** - * Modify queue pair - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @ret rc Return status code - */ -int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { - int rc; - - DBGC ( ibdev, "IBDEV %p modifying QPN %#lx\n", ibdev, qp->qpn ); - - if ( ( rc = ibdev->op->modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not modify QPN %#lx: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Destroy queue pair - * - * @v ibdev Infiniband device - * @v qp Queue pair - */ -void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { - struct io_buffer *iobuf; - unsigned int i; - - DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n", - ibdev, qp->qpn ); - - assert ( list_empty ( &qp->mgids ) ); - - /* Perform device-specific destruction */ - ibdev->op->destroy_qp ( ibdev, qp ); - - /* Complete any remaining I/O buffers with errors */ - for ( i = 0 ; i < qp->send.num_wqes ; i++ ) { - if ( ( iobuf = qp->send.iobufs[i] ) != NULL ) - ib_complete_send ( ibdev, qp, iobuf, -ECANCELED ); - } - for ( i = 0 ; i < qp->recv.num_wqes ; i++ ) { - if ( ( iobuf = qp->recv.iobufs[i] ) != NULL ) { - ib_complete_recv ( ibdev, qp, NULL, NULL, iobuf, - -ECANCELED ); - } - } - - /* Remove work queues from completion queue */ - list_del ( &qp->send.list ); - list_del ( &qp->recv.list ); - - /* Free QP */ - list_del ( &qp->list ); - free ( qp ); -} - -/** - * Find queue pair by QPN - * - * @v ibdev Infiniband device - * @v qpn Queue pair number - * @ret qp Queue pair, or NULL - */ -struct ib_queue_pair * ib_find_qp_qpn ( struct ib_device *ibdev, - unsigned long qpn ) { - struct ib_queue_pair *qp; - - list_for_each_entry ( qp, &ibdev->qps, list ) { - if ( ( qpn == qp->qpn ) || ( qpn == qp->ext_qpn ) ) - return qp; - } - return NULL; -} - -/** - * Find queue pair by multicast GID - * - * @v ibdev Infiniband device - * @v gid Multicast GID - * @ret qp Queue pair, or NULL - */ -struct ib_queue_pair * ib_find_qp_mgid ( struct ib_device *ibdev, - union ib_gid *gid ) { - struct ib_queue_pair *qp; - struct ib_multicast_gid *mgid; - - list_for_each_entry ( qp, &ibdev->qps, list ) { - list_for_each_entry ( mgid, &qp->mgids, list ) { - if ( memcmp ( &mgid->gid, gid, - sizeof ( mgid->gid ) ) == 0 ) { - return qp; - } - } - } - return NULL; -} - -/** - * Find work queue belonging to completion queue - * - * @v cq Completion queue - * @v qpn Queue pair number - * @v is_send Find send work queue (rather than receive) - * @ret wq Work queue, or NULL if not found - */ -struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq, - unsigned long qpn, int is_send ) { - struct ib_work_queue *wq; - - list_for_each_entry ( wq, &cq->work_queues, list ) { - if ( ( wq->qp->qpn == qpn ) && ( wq->is_send == is_send ) ) - return wq; - } - return NULL; -} - -/** - * Post send work queue entry - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v dest Destination address vector - * @v iobuf I/O buffer - * @ret rc Return status code - */ -int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_address_vector *dest, - struct io_buffer *iobuf ) { - struct ib_address_vector dest_copy; - int rc; - - /* Check queue fill level */ - if ( qp->send.fill >= qp->send.num_wqes ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx send queue full\n", - ibdev, qp->qpn ); - return -ENOBUFS; - } - - /* Use default address vector if none specified */ - if ( ! dest ) - dest = &qp->av; - - /* Make modifiable copy of address vector */ - memcpy ( &dest_copy, dest, sizeof ( dest_copy ) ); - dest = &dest_copy; - - /* Fill in optional parameters in address vector */ - if ( ! dest->qkey ) - dest->qkey = qp->qkey; - if ( ! dest->rate ) - dest->rate = IB_RATE_2_5; - - /* Post to hardware */ - if ( ( rc = ibdev->op->post_send ( ibdev, qp, dest, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx could not post send WQE: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); - return rc; - } - - qp->send.fill++; - return 0; -} - -/** - * Post receive work queue entry - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v iobuf I/O buffer - * @ret rc Return status code - */ -int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct io_buffer *iobuf ) { - int rc; - - /* Check packet length */ - if ( iob_tailroom ( iobuf ) < IB_MAX_PAYLOAD_SIZE ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx wrong RX buffer size (%zd)\n", - ibdev, qp->qpn, iob_tailroom ( iobuf ) ); - return -EINVAL; - } - - /* Check queue fill level */ - if ( qp->recv.fill >= qp->recv.num_wqes ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx receive queue full\n", - ibdev, qp->qpn ); - return -ENOBUFS; - } - - /* Post to hardware */ - if ( ( rc = ibdev->op->post_recv ( ibdev, qp, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %#lx could not post receive WQE: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); - return rc; - } - - qp->recv.fill++; - return 0; -} - -/** - * Complete send work queue entry - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v iobuf I/O buffer - * @v rc Completion status code - */ -void ib_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct io_buffer *iobuf, int rc ) { - - if ( qp->send.cq->op->complete_send ) { - qp->send.cq->op->complete_send ( ibdev, qp, iobuf, rc ); - } else { - free_iob ( iobuf ); - } - qp->send.fill--; -} - -/** - * Complete receive work queue entry - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v dest Destination address vector, or NULL - * @v source Source address vector, or NULL - * @v iobuf I/O buffer - * @v rc Completion status code - */ -void ib_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_address_vector *dest, - struct ib_address_vector *source, - struct io_buffer *iobuf, int rc ) { - - if ( qp->recv.cq->op->complete_recv ) { - qp->recv.cq->op->complete_recv ( ibdev, qp, dest, source, - iobuf, rc ); - } else { - free_iob ( iobuf ); - } - qp->recv.fill--; -} - -/** - * Refill receive work queue - * - * @v ibdev Infiniband device - * @v qp Queue pair - */ -void ib_refill_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { - struct io_buffer *iobuf; - int rc; - - /* Keep filling while unfilled entries remain */ - while ( qp->recv.fill < qp->recv.num_wqes ) { - - /* Allocate I/O buffer */ - iobuf = qp->op->alloc_iob ( IB_MAX_PAYLOAD_SIZE ); - if ( ! iobuf ) { - /* Non-fatal; we will refill on next attempt */ - return; - } - - /* Post I/O buffer */ - if ( ( rc = ib_post_recv ( ibdev, qp, iobuf ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not refill: %s\n", - ibdev, strerror ( rc ) ); - free_iob ( iobuf ); - /* Give up */ - return; - } - } -} - -/*************************************************************************** - * - * Link control - * - *************************************************************************** - */ - -/** - * Get link state - * - * @v ibdev Infiniband device - * @ret rc Link status code - */ -int ib_link_rc ( struct ib_device *ibdev ) { - switch ( ibdev->port_state ) { - case IB_PORT_STATE_DOWN: return -ENOTCONN; - case IB_PORT_STATE_INIT: return -EINPROGRESS_INIT; - case IB_PORT_STATE_ARMED: return -EINPROGRESS_ARMED; - case IB_PORT_STATE_ACTIVE: return 0; - default: return -EINVAL; - } -} - -/** - * Textual representation of Infiniband link state - * - * @v ibdev Infiniband device - * @ret link_text Link state text - */ -static const char * ib_link_state_text ( struct ib_device *ibdev ) { - switch ( ibdev->port_state ) { - case IB_PORT_STATE_DOWN: return "DOWN"; - case IB_PORT_STATE_INIT: return "INIT"; - case IB_PORT_STATE_ARMED: return "ARMED"; - case IB_PORT_STATE_ACTIVE: return "ACTIVE"; - default: return "UNKNOWN"; - } -} - -/** - * Notify drivers of Infiniband device or link state change - * - * @v ibdev Infiniband device - */ -static void ib_notify ( struct ib_device *ibdev ) { - struct ib_driver *driver; - - for_each_table_entry ( driver, IB_DRIVERS ) - driver->notify ( ibdev ); -} - -/** - * Notify of Infiniband link state change - * - * @v ibdev Infiniband device - */ -void ib_link_state_changed ( struct ib_device *ibdev ) { - - DBGC ( ibdev, "IBDEV %p link state is %s\n", - ibdev, ib_link_state_text ( ibdev ) ); - - /* Notify drivers of link state change */ - ib_notify ( ibdev ); -} - -/** - * Open port - * - * @v ibdev Infiniband device - * @ret rc Return status code - */ -int ib_open ( struct ib_device *ibdev ) { - int rc; - - /* Increment device open request counter */ - if ( ibdev->open_count++ > 0 ) { - /* Device was already open; do nothing */ - return 0; - } - - /* Open device */ - if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not open: %s\n", - ibdev, strerror ( rc ) ); - goto err_open; - } - - /* Create subnet management interface */ - ibdev->smi = ib_create_mi ( ibdev, IB_QPT_SMI ); - if ( ! ibdev->smi ) { - DBGC ( ibdev, "IBDEV %p could not create SMI\n", ibdev ); - rc = -ENOMEM; - goto err_create_smi; - } - - /* Create subnet management agent */ - if ( ( rc = ib_create_sma ( ibdev, ibdev->smi ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not create SMA: %s\n", - ibdev, strerror ( rc ) ); - goto err_create_sma; - } - - /* Create general services interface */ - ibdev->gsi = ib_create_mi ( ibdev, IB_QPT_GSI ); - if ( ! ibdev->gsi ) { - DBGC ( ibdev, "IBDEV %p could not create GSI\n", ibdev ); - rc = -ENOMEM; - goto err_create_gsi; - } - - /* Add to head of open devices list */ - list_add ( &ibdev->open_list, &open_ib_devices ); - - /* Notify drivers of device state change */ - ib_notify ( ibdev ); - - assert ( ibdev->open_count == 1 ); - return 0; - - ib_destroy_mi ( ibdev, ibdev->gsi ); - err_create_gsi: - ib_destroy_sma ( ibdev, ibdev->smi ); - err_create_sma: - ib_destroy_mi ( ibdev, ibdev->smi ); - err_create_smi: - ibdev->op->close ( ibdev ); - err_open: - assert ( ibdev->open_count == 1 ); - ibdev->open_count = 0; - return rc; -} - -/** - * Close port - * - * @v ibdev Infiniband device - */ -void ib_close ( struct ib_device *ibdev ) { - - /* Decrement device open request counter */ - ibdev->open_count--; - - /* Close device if this was the last remaining requested opening */ - if ( ibdev->open_count == 0 ) { - ib_notify ( ibdev ); - list_del ( &ibdev->open_list ); - ib_destroy_mi ( ibdev, ibdev->gsi ); - ib_destroy_sma ( ibdev, ibdev->smi ); - ib_destroy_mi ( ibdev, ibdev->smi ); - ibdev->op->close ( ibdev ); - ibdev->port_state = IB_PORT_STATE_DOWN; - } -} - -/*************************************************************************** - * - * Multicast - * - *************************************************************************** - */ - -/** - * Attach to multicast group - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v gid Multicast GID - * @ret rc Return status code - * - * Note that this function handles only the local device's attachment - * to the multicast GID; it does not issue the relevant MADs to join - * the multicast group on the subnet. - */ -int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, - union ib_gid *gid ) { - struct ib_multicast_gid *mgid; - int rc; - - /* Sanity check */ - assert ( qp != NULL ); - - /* Add to software multicast GID list */ - mgid = zalloc ( sizeof ( *mgid ) ); - if ( ! mgid ) { - rc = -ENOMEM; - goto err_alloc_mgid; - } - memcpy ( &mgid->gid, gid, sizeof ( mgid->gid ) ); - list_add ( &mgid->list, &qp->mgids ); - - /* Add to hardware multicast GID list */ - if ( ( rc = ibdev->op->mcast_attach ( ibdev, qp, gid ) ) != 0 ) - goto err_dev_mcast_attach; - - return 0; - - err_dev_mcast_attach: - list_del ( &mgid->list ); - free ( mgid ); - err_alloc_mgid: - return rc; -} - -/** - * Detach from multicast group - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v gid Multicast GID - */ -void ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp, - union ib_gid *gid ) { - struct ib_multicast_gid *mgid; - - /* Sanity check */ - assert ( qp != NULL ); - - /* Remove from hardware multicast GID list */ - ibdev->op->mcast_detach ( ibdev, qp, gid ); - - /* Remove from software multicast GID list */ - list_for_each_entry ( mgid, &qp->mgids, list ) { - if ( memcmp ( &mgid->gid, gid, sizeof ( mgid->gid ) ) == 0 ) { - list_del ( &mgid->list ); - free ( mgid ); - break; - } - } -} - -/*************************************************************************** - * - * Miscellaneous - * - *************************************************************************** - */ - -/** - * Count Infiniband HCA ports - * - * @v ibdev Infiniband device - * @ret num_ports Number of ports - */ -int ib_count_ports ( struct ib_device *ibdev ) { - struct ib_device *tmp; - int num_ports = 0; - - /* Search for IB devices with the same physical device to - * identify port count. - */ - for_each_ibdev ( tmp ) { - if ( tmp->dev == ibdev->dev ) - num_ports++; - } - return num_ports; -} - -/** - * Set port information - * - * @v ibdev Infiniband device - * @v mad Set port information MAD - */ -int ib_set_port_info ( struct ib_device *ibdev, union ib_mad *mad ) { - int rc; - - /* Adapters with embedded SMAs do not need to support this method */ - if ( ! ibdev->op->set_port_info ) { - DBGC ( ibdev, "IBDEV %p does not support setting port " - "information\n", ibdev ); - return -ENOTSUP; - } - - if ( ( rc = ibdev->op->set_port_info ( ibdev, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not set port information: %s\n", - ibdev, strerror ( rc ) ); - return rc; - } - - return 0; -}; - -/** - * Set partition key table - * - * @v ibdev Infiniband device - * @v mad Set partition key table MAD - */ -int ib_set_pkey_table ( struct ib_device *ibdev, union ib_mad *mad ) { - int rc; - - /* Adapters with embedded SMAs do not need to support this method */ - if ( ! ibdev->op->set_pkey_table ) { - DBGC ( ibdev, "IBDEV %p does not support setting partition " - "key table\n", ibdev ); - return -ENOTSUP; - } - - if ( ( rc = ibdev->op->set_pkey_table ( ibdev, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not set partition key table: " - "%s\n", ibdev, strerror ( rc ) ); - return rc; - } - - return 0; -}; - -/*************************************************************************** - * - * Event queues - * - *************************************************************************** - */ - -/** - * Poll event queue - * - * @v ibdev Infiniband device - */ -void ib_poll_eq ( struct ib_device *ibdev ) { - struct ib_completion_queue *cq; - - /* Poll device's event queue */ - ibdev->op->poll_eq ( ibdev ); - - /* Poll all completion queues */ - list_for_each_entry ( cq, &ibdev->cqs, list ) - ib_poll_cq ( ibdev, cq ); -} - -/** - * Single-step the Infiniband event queue - * - * @v process Infiniband event queue process - */ -static void ib_step ( struct process *process __unused ) { - struct ib_device *ibdev; - - list_for_each_entry ( ibdev, &open_ib_devices, open_list ) - ib_poll_eq ( ibdev ); -} - -/** Infiniband event queue process */ -PERMANENT_PROCESS ( ib_process, ib_step ); - -/*************************************************************************** - * - * Infiniband device creation/destruction - * - *************************************************************************** - */ - -/** - * Allocate Infiniband device - * - * @v priv_size Size of driver private data area - * @ret ibdev Infiniband device, or NULL - */ -struct ib_device * alloc_ibdev ( size_t priv_size ) { - struct ib_device *ibdev; - void *drv_priv; - size_t total_len; - - total_len = ( sizeof ( *ibdev ) + priv_size ); - ibdev = zalloc ( total_len ); - if ( ibdev ) { - drv_priv = ( ( ( void * ) ibdev ) + sizeof ( *ibdev ) ); - ib_set_drvdata ( ibdev, drv_priv ); - INIT_LIST_HEAD ( &ibdev->list ); - INIT_LIST_HEAD ( &ibdev->open_list ); - INIT_LIST_HEAD ( &ibdev->cqs ); - INIT_LIST_HEAD ( &ibdev->qps ); - ibdev->port_state = IB_PORT_STATE_DOWN; - ibdev->lid = IB_LID_NONE; - ibdev->pkey = IB_PKEY_DEFAULT; - } - return ibdev; -} - -/** - * Register Infiniband device - * - * @v ibdev Infiniband device - * @ret rc Return status code - */ -int register_ibdev ( struct ib_device *ibdev ) { - struct ib_driver *driver; - int rc; - - /* Add to device list */ - ibdev_get ( ibdev ); - list_add_tail ( &ibdev->list, &ib_devices ); - DBGC ( ibdev, "IBDEV %p registered (phys %s)\n", ibdev, - ibdev->dev->name ); - - /* Probe device */ - for_each_table_entry ( driver, IB_DRIVERS ) { - if ( ( rc = driver->probe ( ibdev ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not add %s device: %s\n", - ibdev, driver->name, strerror ( rc ) ); - goto err_probe; - } - } - - return 0; - - err_probe: - for_each_table_entry_continue_reverse ( driver, IB_DRIVERS ) - driver->remove ( ibdev ); - list_del ( &ibdev->list ); - ibdev_put ( ibdev ); - return rc; -} - -/** - * Unregister Infiniband device - * - * @v ibdev Infiniband device - */ -void unregister_ibdev ( struct ib_device *ibdev ) { - struct ib_driver *driver; - - /* Remove device */ - for_each_table_entry_reverse ( driver, IB_DRIVERS ) - driver->remove ( ibdev ); - - /* Remove from device list */ - list_del ( &ibdev->list ); - ibdev_put ( ibdev ); - DBGC ( ibdev, "IBDEV %p unregistered\n", ibdev ); -} - -/** - * Find Infiniband device by GID - * - * @v gid GID - * @ret ibdev Infiniband device, or NULL - */ -struct ib_device * find_ibdev ( union ib_gid *gid ) { - struct ib_device *ibdev; - - for_each_ibdev ( ibdev ) { - if ( memcmp ( gid, &ibdev->gid, sizeof ( *gid ) ) == 0 ) - return ibdev; - } - return NULL; -} - -/** - * Get most recently opened Infiniband device - * - * @ret ibdev Most recently opened Infiniband device, or NULL - */ -struct ib_device * last_opened_ibdev ( void ) { - struct ib_device *ibdev; - - ibdev = list_first_entry ( &open_ib_devices, struct ib_device, - open_list ); - if ( ! ibdev ) - return NULL; - - assert ( ibdev->open_count != 0 ); - return ibdev; -} - -/* Drag in objects via register_ibdev() */ -REQUIRING_SYMBOL ( register_ibdev ); - -/* Drag in Infiniband configuration */ -REQUIRE_OBJECT ( config_infiniband ); - -/* Drag in IPoIB */ -REQUIRE_OBJECT ( ipoib ); diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_cm.c b/qemu/roms/ipxe/src/net/infiniband/ib_cm.c deleted file mode 100644 index 85982f09d..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_cm.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_mi.h> -#include <ipxe/ib_pathrec.h> -#include <ipxe/ib_cm.h> - -/** - * @file - * - * Infiniband communication management - * - */ - -/** List of connections */ -static LIST_HEAD ( ib_cm_conns ); - -/** - * Find connection by local communication ID - * - * @v local_id Local communication ID - * @ret conn Connection, or NULL - */ -static struct ib_connection * ib_cm_find ( uint32_t local_id ) { - struct ib_connection *conn; - - list_for_each_entry ( conn, &ib_cm_conns, list ) { - if ( conn->local_id == local_id ) - return conn; - } - return NULL; -} - -/** - * Send "ready to use" response - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v av Address vector - * @v local_id Local communication ID - * @v remote_id Remote communication ID - * @ret rc Return status code - */ -static int ib_cm_send_rtu ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - struct ib_address_vector *av, - uint32_t local_id, uint32_t remote_id ) { - union ib_mad mad; - struct ib_cm_ready_to_use *rtu = &mad.cm.cm_data.ready_to_use; - int rc; - - /* Construct "ready to use" response */ - memset ( &mad, 0, sizeof ( mad ) ); - mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; - mad.hdr.class_version = IB_CM_CLASS_VERSION; - mad.hdr.method = IB_MGMT_METHOD_SEND; - mad.hdr.attr_id = htons ( IB_CM_ATTR_READY_TO_USE ); - rtu->local_id = htonl ( local_id ); - rtu->remote_id = htonl ( remote_id ); - if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){ - DBG ( "CM could not send RTU: %s\n", strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle duplicate connection replies - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - * @ret rc Return status code - * - * If a "ready to use" MAD is lost, the peer may resend the connection - * reply. We have to respond to these with duplicate "ready to use" - * MADs, otherwise the peer may time out and drop the connection. - */ -static void ib_cm_recv_rep ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_cm_connect_reply *rep = &mad->cm.cm_data.connect_reply; - struct ib_connection *conn; - uint32_t local_id = ntohl ( rep->remote_id ); - int rc; - - /* Identify connection */ - conn = ib_cm_find ( local_id ); - if ( conn ) { - /* Try to send "ready to use" reply */ - if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id, - conn->remote_id ) ) != 0 ) { - /* Ignore errors; the remote end will retry */ - } - } else { - DBG ( "CM unidentified connection %08x\n", local_id ); - } -} - -/** - * Send reply to disconnection request - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v av Address vector - * @v local_id Local communication ID - * @v remote_id Remote communication ID - * @ret rc Return status code - */ -static int ib_cm_send_drep ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - struct ib_address_vector *av, - uint32_t local_id, uint32_t remote_id ) { - union ib_mad mad; - struct ib_cm_disconnect_reply *drep = &mad.cm.cm_data.disconnect_reply; - int rc; - - /* Construct reply to disconnection request */ - memset ( &mad, 0, sizeof ( mad ) ); - mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; - mad.hdr.class_version = IB_CM_CLASS_VERSION; - mad.hdr.method = IB_MGMT_METHOD_SEND; - mad.hdr.attr_id = htons ( IB_CM_ATTR_DISCONNECT_REPLY ); - drep->local_id = htonl ( local_id ); - drep->remote_id = htonl ( remote_id ); - if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){ - DBG ( "CM could not send DREP: %s\n", strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle disconnection requests - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - * @ret rc Return status code - */ -static void ib_cm_recv_dreq ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_cm_disconnect_request *dreq = - &mad->cm.cm_data.disconnect_request; - struct ib_connection *conn; - uint32_t local_id = ntohl ( dreq->remote_id ); - uint32_t remote_id = ntohl ( dreq->local_id ); - int rc; - - /* Identify connection */ - conn = ib_cm_find ( local_id ); - if ( conn ) { - /* Notify upper layer */ - conn->op->changed ( ibdev, conn->qp, conn, -ENOTCONN, - &dreq->private_data, - sizeof ( dreq->private_data ) ); - } else { - DBG ( "CM unidentified connection %08x\n", local_id ); - } - - /* Send reply */ - if ( ( rc = ib_cm_send_drep ( ibdev, mi, av, local_id, - remote_id ) ) != 0 ) { - /* Ignore errors; the remote end will retry */ - } -}; - -/** Communication management agents */ -struct ib_mad_agent ib_cm_agent[] __ib_mad_agent = { - { - .mgmt_class = IB_MGMT_CLASS_CM, - .class_version = IB_CM_CLASS_VERSION, - .attr_id = htons ( IB_CM_ATTR_CONNECT_REPLY ), - .handle = ib_cm_recv_rep, - }, - { - .mgmt_class = IB_MGMT_CLASS_CM, - .class_version = IB_CM_CLASS_VERSION, - .attr_id = htons ( IB_CM_ATTR_DISCONNECT_REQUEST ), - .handle = ib_cm_recv_dreq, - }, -}; - -/** - * Convert connection rejection reason to return status code - * - * @v reason Rejection reason (in network byte order) - * @ret rc Return status code - */ -static int ib_cm_rejection_reason_to_rc ( uint16_t reason ) { - switch ( reason ) { - case htons ( IB_CM_REJECT_BAD_SERVICE_ID ) : - return -ENODEV; - case htons ( IB_CM_REJECT_STALE_CONN ) : - return -EALREADY; - case htons ( IB_CM_REJECT_CONSUMER ) : - return -ENOTTY; - default: - return -EPERM; - } -} - -/** - * Handle connection request transaction completion - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v madx Management transaction - * @v rc Status code - * @v mad Received MAD (or NULL on error) - * @v av Source address vector (or NULL on error) - */ -static void ib_cm_req_complete ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - struct ib_mad_transaction *madx, - int rc, union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_connection *conn = ib_madx_get_ownerdata ( madx ); - struct ib_queue_pair *qp = conn->qp; - struct ib_cm_common *common = &mad->cm.cm_data.common; - struct ib_cm_connect_reply *rep = &mad->cm.cm_data.connect_reply; - struct ib_cm_connect_reject *rej = &mad->cm.cm_data.connect_reject; - void *private_data = NULL; - size_t private_data_len = 0; - - /* Report failures */ - if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) - rc = -EIO; - if ( rc != 0 ) { - DBGC ( conn, "CM %p connection request failed: %s\n", - conn, strerror ( rc ) ); - goto out; - } - - /* Record remote communication ID */ - conn->remote_id = ntohl ( common->local_id ); - - /* Handle response */ - switch ( mad->hdr.attr_id ) { - - case htons ( IB_CM_ATTR_CONNECT_REPLY ) : - /* Extract fields */ - qp->av.qpn = ( ntohl ( rep->local_qpn ) >> 8 ); - qp->send.psn = ( ntohl ( rep->starting_psn ) >> 8 ); - private_data = &rep->private_data; - private_data_len = sizeof ( rep->private_data ); - DBGC ( conn, "CM %p connected to QPN %lx PSN %x\n", - conn, qp->av.qpn, qp->send.psn ); - - /* Modify queue pair */ - if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( conn, "CM %p could not modify queue pair: %s\n", - conn, strerror ( rc ) ); - goto out; - } - - /* Send "ready to use" reply */ - if ( ( rc = ib_cm_send_rtu ( ibdev, mi, av, conn->local_id, - conn->remote_id ) ) != 0 ) { - /* Treat as non-fatal */ - rc = 0; - } - break; - - case htons ( IB_CM_ATTR_CONNECT_REJECT ) : - /* Extract fields */ - DBGC ( conn, "CM %p connection rejected (reason %d)\n", - conn, ntohs ( rej->reason ) ); - /* Private data is valid only for a Consumer Reject */ - if ( rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) { - private_data = &rej->private_data; - private_data_len = sizeof ( rej->private_data ); - } - rc = ib_cm_rejection_reason_to_rc ( rej->reason ); - break; - - default: - DBGC ( conn, "CM %p unexpected response (attribute %04x)\n", - conn, ntohs ( mad->hdr.attr_id ) ); - rc = -ENOTSUP; - break; - } - - out: - /* Destroy the completed transaction */ - ib_destroy_madx ( ibdev, ibdev->gsi, madx ); - conn->madx = NULL; - - /* Hand off to the upper completion handler */ - conn->op->changed ( ibdev, qp, conn, rc, private_data, - private_data_len ); -} - -/** Connection request operations */ -static struct ib_mad_transaction_operations ib_cm_req_op = { - .complete = ib_cm_req_complete, -}; - -/** - * Handle connection path transaction completion - * - * @v ibdev Infiniband device - * @v path Path - * @v rc Status code - * @v av Address vector, or NULL on error - */ -static void ib_cm_path_complete ( struct ib_device *ibdev, - struct ib_path *path, int rc, - struct ib_address_vector *av ) { - struct ib_connection *conn = ib_path_get_ownerdata ( path ); - struct ib_queue_pair *qp = conn->qp; - union ib_mad mad; - struct ib_cm_connect_request *req = &mad.cm.cm_data.connect_request; - size_t private_data_len; - - /* Report failures */ - if ( rc != 0 ) { - DBGC ( conn, "CM %p path lookup failed: %s\n", - conn, strerror ( rc ) ); - conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 ); - goto out; - } - - /* Update queue pair peer path */ - memcpy ( &qp->av, av, sizeof ( qp->av ) ); - - /* Construct connection request */ - memset ( &mad, 0, sizeof ( mad ) ); - mad.hdr.mgmt_class = IB_MGMT_CLASS_CM; - mad.hdr.class_version = IB_CM_CLASS_VERSION; - mad.hdr.method = IB_MGMT_METHOD_SEND; - mad.hdr.attr_id = htons ( IB_CM_ATTR_CONNECT_REQUEST ); - req->local_id = htonl ( conn->local_id ); - memcpy ( &req->service_id, &conn->service_id, - sizeof ( req->service_id ) ); - memcpy ( &req->local_ca, &ibdev->node_guid, sizeof ( req->local_ca ) ); - req->local_qpn__responder_resources = htonl ( ( qp->qpn << 8 ) | 1 ); - req->local_eecn__initiator_depth = htonl ( ( 0 << 8 ) | 1 ); - req->remote_eecn__remote_timeout__service_type__ee_flow_ctrl = - htonl ( ( 0x14 << 3 ) | ( IB_CM_TRANSPORT_RC << 1 ) | - ( 0 << 0 ) ); - req->starting_psn__local_timeout__retry_count = - htonl ( ( qp->recv.psn << 8 ) | ( 0x14 << 3 ) | - ( 0x07 << 0 ) ); - req->pkey = htons ( ibdev->pkey ); - req->payload_mtu__rdc_exists__rnr_retry = - ( ( IB_MTU_2048 << 4 ) | ( 1 << 3 ) | ( 0x07 << 0 ) ); - req->max_cm_retries__srq = ( ( 0x0f << 4 ) | ( 0 << 3 ) ); - req->primary.local_lid = htons ( ibdev->lid ); - req->primary.remote_lid = htons ( conn->qp->av.lid ); - memcpy ( &req->primary.local_gid, &ibdev->gid, - sizeof ( req->primary.local_gid ) ); - memcpy ( &req->primary.remote_gid, &conn->qp->av.gid, - sizeof ( req->primary.remote_gid ) ); - req->primary.flow_label__rate = - htonl ( ( 0 << 12 ) | ( conn->qp->av.rate << 0 ) ); - req->primary.hop_limit = 0; - req->primary.sl__subnet_local = - ( ( conn->qp->av.sl << 4 ) | ( 1 << 3 ) ); - req->primary.local_ack_timeout = ( 0x13 << 3 ); - private_data_len = conn->private_data_len; - if ( private_data_len > sizeof ( req->private_data ) ) - private_data_len = sizeof ( req->private_data ); - memcpy ( &req->private_data, &conn->private_data, private_data_len ); - - /* Create connection request */ - av->qpn = IB_QPN_GSI; - av->qkey = IB_QKEY_GSI; - conn->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, av, - &ib_cm_req_op ); - if ( ! conn->madx ) { - DBGC ( conn, "CM %p could not create connection request\n", - conn ); - conn->op->changed ( ibdev, qp, conn, rc, NULL, 0 ); - goto out; - } - ib_madx_set_ownerdata ( conn->madx, conn ); - - out: - /* Destroy the completed transaction */ - ib_destroy_path ( ibdev, path ); - conn->path = NULL; -} - -/** Connection path operations */ -static struct ib_path_operations ib_cm_path_op = { - .complete = ib_cm_path_complete, -}; - -/** - * Create connection to remote QP - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v dgid Target GID - * @v service_id Target service ID - * @v private_data Connection request private data - * @v private_data_len Length of connection request private data - * @v op Connection operations - * @ret conn Connection - */ -struct ib_connection * -ib_create_conn ( struct ib_device *ibdev, struct ib_queue_pair *qp, - union ib_gid *dgid, union ib_guid *service_id, - void *private_data, size_t private_data_len, - struct ib_connection_operations *op ) { - struct ib_connection *conn; - - /* Allocate and initialise request */ - conn = zalloc ( sizeof ( *conn ) + private_data_len ); - if ( ! conn ) - goto err_alloc_conn; - conn->ibdev = ibdev; - conn->qp = qp; - memset ( &qp->av, 0, sizeof ( qp->av ) ); - qp->av.gid_present = 1; - memcpy ( &qp->av.gid, dgid, sizeof ( qp->av.gid ) ); - conn->local_id = random(); - memcpy ( &conn->service_id, service_id, sizeof ( conn->service_id ) ); - conn->op = op; - conn->private_data_len = private_data_len; - memcpy ( &conn->private_data, private_data, private_data_len ); - - /* Create path */ - conn->path = ib_create_path ( ibdev, &qp->av, &ib_cm_path_op ); - if ( ! conn->path ) - goto err_create_path; - ib_path_set_ownerdata ( conn->path, conn ); - - /* Add to list of connections */ - list_add ( &conn->list, &ib_cm_conns ); - - DBGC ( conn, "CM %p created for IBDEV %p QPN %lx\n", - conn, ibdev, qp->qpn ); - DBGC ( conn, "CM %p connecting to " IB_GID_FMT " " IB_GUID_FMT "\n", - conn, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); - - return conn; - - ib_destroy_path ( ibdev, conn->path ); - err_create_path: - free ( conn ); - err_alloc_conn: - return NULL; -} - -/** - * Destroy connection to remote QP - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v conn Connection - */ -void ib_destroy_conn ( struct ib_device *ibdev, - struct ib_queue_pair *qp __unused, - struct ib_connection *conn ) { - - list_del ( &conn->list ); - if ( conn->madx ) - ib_destroy_madx ( ibdev, ibdev->gsi, conn->madx ); - if ( conn->path ) - ib_destroy_path ( ibdev, conn->path ); - free ( conn ); -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_cmrc.c b/qemu/roms/ipxe/src/net/infiniband/ib_cmrc.c deleted file mode 100644 index 1cc0fcfef..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_cmrc.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -FILE_LICENCE ( BSD2 ); - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/process.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_cm.h> -#include <ipxe/ib_cmrc.h> - -/** - * @file - * - * Infiniband Communication-managed Reliable Connections - * - */ - -/** CMRC number of send WQEs - * - * This is a policy decision. - */ -#define IB_CMRC_NUM_SEND_WQES 4 - -/** CMRC number of receive WQEs - * - * This is a policy decision. - */ -#define IB_CMRC_NUM_RECV_WQES 2 - -/** CMRC number of completion queue entries - * - * This is a policy decision - */ -#define IB_CMRC_NUM_CQES 8 - -/** An Infiniband Communication-Managed Reliable Connection */ -struct ib_cmrc_connection { - /** Reference count */ - struct refcnt refcnt; - /** Data transfer interface */ - struct interface xfer; - /** Infiniband device */ - struct ib_device *ibdev; - /** Completion queue */ - struct ib_completion_queue *cq; - /** Queue pair */ - struct ib_queue_pair *qp; - /** Connection */ - struct ib_connection *conn; - /** Destination GID */ - union ib_gid dgid; - /** Service ID */ - union ib_guid service_id; - /** QP is connected */ - int connected; - /** Shutdown process */ - struct process shutdown; -}; - -/** - * Shut down CMRC connection gracefully - * - * @v cmrc Communication-Managed Reliable Connection - * - * The Infiniband data structures are not reference-counted or - * guarded. It is therefore unsafe to shut them down while we may be - * in the middle of a callback from the Infiniband stack (e.g. in a - * receive completion handler). - * - * This shutdown process will run some time after the call to - * ib_cmrc_close(), after control has returned out of the Infiniband - * core, and will shut down the Infiniband interfaces cleanly. - * - * The shutdown process holds an implicit reference on the CMRC - * connection, ensuring that the structure is not freed before the - * shutdown process has run. - */ -static void ib_cmrc_shutdown ( struct ib_cmrc_connection *cmrc ) { - - DBGC ( cmrc, "CMRC %p shutting down\n", cmrc ); - - /* Shut down Infiniband interface */ - ib_destroy_conn ( cmrc->ibdev, cmrc->qp, cmrc->conn ); - ib_destroy_qp ( cmrc->ibdev, cmrc->qp ); - ib_destroy_cq ( cmrc->ibdev, cmrc->cq ); - ib_close ( cmrc->ibdev ); - - /* Drop the remaining reference */ - ref_put ( &cmrc->refcnt ); -} - -/** - * Close CMRC connection - * - * @v cmrc Communication-Managed Reliable Connection - * @v rc Reason for close - */ -static void ib_cmrc_close ( struct ib_cmrc_connection *cmrc, int rc ) { - - /* Close data transfer interface */ - intf_shutdown ( &cmrc->xfer, rc ); - - /* Schedule shutdown process */ - process_add ( &cmrc->shutdown ); -} - -/** - * Handle change of CMRC connection status - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v conn Connection - * @v rc_cm Connection status code - * @v private_data Private data, if available - * @v private_data_len Length of private data - */ -static void ib_cmrc_changed ( struct ib_device *ibdev __unused, - struct ib_queue_pair *qp, - struct ib_connection *conn __unused, int rc_cm, - void *private_data, size_t private_data_len ) { - struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp ); - int rc_xfer; - - /* Record connection status */ - if ( rc_cm == 0 ) { - DBGC ( cmrc, "CMRC %p connected\n", cmrc ); - cmrc->connected = 1; - } else { - DBGC ( cmrc, "CMRC %p disconnected: %s\n", - cmrc, strerror ( rc_cm ) ); - cmrc->connected = 0; - } - - /* Pass up any private data */ - DBGC2 ( cmrc, "CMRC %p received private data:\n", cmrc ); - DBGC2_HDA ( cmrc, 0, private_data, private_data_len ); - if ( private_data && - ( rc_xfer = xfer_deliver_raw ( &cmrc->xfer, private_data, - private_data_len ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not deliver private data: %s\n", - cmrc, strerror ( rc_xfer ) ); - ib_cmrc_close ( cmrc, rc_xfer ); - return; - } - - /* Notify upper connection of window change */ - xfer_window_changed ( &cmrc->xfer ); - - /* If we are disconnected, close the upper connection */ - if ( rc_cm != 0 ) { - ib_cmrc_close ( cmrc, rc_cm ); - return; - } -} - -/** CMRC connection operations */ -static struct ib_connection_operations ib_cmrc_conn_op = { - .changed = ib_cmrc_changed, -}; - -/** - * Handle CMRC send completion - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void ib_cmrc_complete_send ( struct ib_device *ibdev __unused, - struct ib_queue_pair *qp, - struct io_buffer *iobuf, int rc ) { - struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp ); - - /* Free the completed I/O buffer */ - free_iob ( iobuf ); - - /* Close the connection on any send errors */ - if ( rc != 0 ) { - DBGC ( cmrc, "CMRC %p send error: %s\n", - cmrc, strerror ( rc ) ); - ib_cmrc_close ( cmrc, rc ); - return; - } -} - -/** - * Handle CMRC receive completion - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v dest Destination address vector, or NULL - * @v source Source address vector, or NULL - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void ib_cmrc_complete_recv ( struct ib_device *ibdev __unused, - struct ib_queue_pair *qp, - struct ib_address_vector *dest __unused, - struct ib_address_vector *source __unused, - struct io_buffer *iobuf, int rc ) { - struct ib_cmrc_connection *cmrc = ib_qp_get_ownerdata ( qp ); - - /* Close the connection on any receive errors */ - if ( rc != 0 ) { - DBGC ( cmrc, "CMRC %p receive error: %s\n", - cmrc, strerror ( rc ) ); - free_iob ( iobuf ); - ib_cmrc_close ( cmrc, rc ); - return; - } - - DBGC2 ( cmrc, "CMRC %p received:\n", cmrc ); - DBGC2_HDA ( cmrc, 0, iobuf->data, iob_len ( iobuf ) ); - - /* Pass up data */ - if ( ( rc = xfer_deliver_iob ( &cmrc->xfer, iobuf ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not deliver data: %s\n", - cmrc, strerror ( rc ) ); - ib_cmrc_close ( cmrc, rc ); - return; - } -} - -/** Infiniband CMRC completion operations */ -static struct ib_completion_queue_operations ib_cmrc_completion_ops = { - .complete_send = ib_cmrc_complete_send, - .complete_recv = ib_cmrc_complete_recv, -}; - -/** Infiniband CMRC queue pair operations */ -static struct ib_queue_pair_operations ib_cmrc_queue_pair_ops = { - .alloc_iob = alloc_iob, -}; - -/** - * Send data via CMRC - * - * @v cmrc CMRC connection - * @v iobuf Datagram I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int ib_cmrc_xfer_deliver ( struct ib_cmrc_connection *cmrc, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - int rc; - - /* If no connection has yet been attempted, send this datagram - * as the CM REQ private data. Otherwise, send it via the QP. - */ - if ( ! cmrc->connected ) { - - /* Abort if we have already sent a CM connection request */ - if ( cmrc->conn ) { - DBGC ( cmrc, "CMRC %p attempt to send before " - "connection is complete\n", cmrc ); - rc = -EIO; - goto out; - } - - /* Send via CM connection request */ - cmrc->conn = ib_create_conn ( cmrc->ibdev, cmrc->qp, - &cmrc->dgid, &cmrc->service_id, - iobuf->data, iob_len ( iobuf ), - &ib_cmrc_conn_op ); - if ( ! cmrc->conn ) { - DBGC ( cmrc, "CMRC %p could not connect\n", cmrc ); - rc = -ENOMEM; - goto out; - } - - } else { - - /* Send via QP */ - if ( ( rc = ib_post_send ( cmrc->ibdev, cmrc->qp, NULL, - iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not send: %s\n", - cmrc, strerror ( rc ) ); - goto out; - } - - } - return 0; - - out: - /* Free the I/O buffer if necessary */ - free_iob ( iobuf ); - - /* Close the connection on any errors */ - if ( rc != 0 ) - ib_cmrc_close ( cmrc, rc ); - - return rc; -} - -/** - * Check CMRC flow control window - * - * @v cmrc CMRC connection - * @ret len Length of window - */ -static size_t ib_cmrc_xfer_window ( struct ib_cmrc_connection *cmrc ) { - - /* We indicate a window only when we are successfully - * connected. - */ - return ( cmrc->connected ? IB_MAX_PAYLOAD_SIZE : 0 ); -} - -/** - * Identify device underlying CMRC connection - * - * @v cmrc CMRC connection - * @ret device Underlying device - */ -static struct device * -ib_cmrc_identify_device ( struct ib_cmrc_connection *cmrc ) { - return cmrc->ibdev->dev; -} - -/** CMRC data transfer interface operations */ -static struct interface_operation ib_cmrc_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct ib_cmrc_connection *, - ib_cmrc_xfer_deliver ), - INTF_OP ( xfer_window, struct ib_cmrc_connection *, - ib_cmrc_xfer_window ), - INTF_OP ( intf_close, struct ib_cmrc_connection *, ib_cmrc_close ), - INTF_OP ( identify_device, struct ib_cmrc_connection *, - ib_cmrc_identify_device ), -}; - -/** CMRC data transfer interface descriptor */ -static struct interface_descriptor ib_cmrc_xfer_desc = - INTF_DESC ( struct ib_cmrc_connection, xfer, ib_cmrc_xfer_operations ); - -/** CMRC shutdown process descriptor */ -static struct process_descriptor ib_cmrc_shutdown_desc = - PROC_DESC_ONCE ( struct ib_cmrc_connection, shutdown, - ib_cmrc_shutdown ); - -/** - * Open CMRC connection - * - * @v xfer Data transfer interface - * @v ibdev Infiniband device - * @v dgid Destination GID - * @v service_id Service ID - * @ret rc Returns status code - */ -int ib_cmrc_open ( struct interface *xfer, struct ib_device *ibdev, - union ib_gid *dgid, union ib_guid *service_id ) { - struct ib_cmrc_connection *cmrc; - int rc; - - /* Allocate and initialise structure */ - cmrc = zalloc ( sizeof ( *cmrc ) ); - if ( ! cmrc ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &cmrc->refcnt, NULL ); - intf_init ( &cmrc->xfer, &ib_cmrc_xfer_desc, &cmrc->refcnt ); - cmrc->ibdev = ibdev; - memcpy ( &cmrc->dgid, dgid, sizeof ( cmrc->dgid ) ); - memcpy ( &cmrc->service_id, service_id, sizeof ( cmrc->service_id ) ); - process_init_stopped ( &cmrc->shutdown, &ib_cmrc_shutdown_desc, - &cmrc->refcnt ); - - /* Open Infiniband device */ - if ( ( rc = ib_open ( ibdev ) ) != 0 ) { - DBGC ( cmrc, "CMRC %p could not open device: %s\n", - cmrc, strerror ( rc ) ); - goto err_open; - } - - /* Create completion queue */ - cmrc->cq = ib_create_cq ( ibdev, IB_CMRC_NUM_CQES, - &ib_cmrc_completion_ops ); - if ( ! cmrc->cq ) { - DBGC ( cmrc, "CMRC %p could not create completion queue\n", - cmrc ); - rc = -ENOMEM; - goto err_create_cq; - } - - /* Create queue pair */ - cmrc->qp = ib_create_qp ( ibdev, IB_QPT_RC, IB_CMRC_NUM_SEND_WQES, - cmrc->cq, IB_CMRC_NUM_RECV_WQES, cmrc->cq, - &ib_cmrc_queue_pair_ops ); - if ( ! cmrc->qp ) { - DBGC ( cmrc, "CMRC %p could not create queue pair\n", cmrc ); - rc = -ENOMEM; - goto err_create_qp; - } - ib_qp_set_ownerdata ( cmrc->qp, cmrc ); - DBGC ( cmrc, "CMRC %p using QPN %lx\n", cmrc, cmrc->qp->qpn ); - - /* Attach to parent interface, transfer reference (implicitly) - * to our shutdown process, and return. - */ - intf_plug_plug ( &cmrc->xfer, xfer ); - return 0; - - ib_destroy_qp ( ibdev, cmrc->qp ); - err_create_qp: - ib_destroy_cq ( ibdev, cmrc->cq ); - err_create_cq: - ib_close ( ibdev ); - err_open: - ref_put ( &cmrc->refcnt ); - err_alloc: - return rc; -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c b/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c deleted file mode 100644 index fc4ff7f0a..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_mcast.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <ipxe/list.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_mi.h> -#include <ipxe/ib_mcast.h> - -/** @file - * - * Infiniband multicast groups - * - */ - -/** - * Generate multicast membership MAD - * - * @v ibdev Infiniband device - * @v gid Multicast GID - * @v join Join (rather than leave) group - * @v mad MAD to fill in - */ -static void ib_mcast_mad ( struct ib_device *ibdev, union ib_gid *gid, - int join, union ib_mad *mad ) { - struct ib_mad_sa *sa = &mad->sa; - - /* Construct multicast membership record request */ - memset ( sa, 0, sizeof ( *sa ) ); - sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; - sa->mad_hdr.class_version = IB_SA_CLASS_VERSION; - sa->mad_hdr.method = - ( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE ); - sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC ); - sa->sa_hdr.comp_mask[1] = - htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | - IB_SA_MCMEMBER_REC_JOIN_STATE ); - sa->sa_data.mc_member_record.scope__join_state = 1; - memcpy ( &sa->sa_data.mc_member_record.mgid, gid, - sizeof ( sa->sa_data.mc_member_record.mgid ) ); - memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid, - sizeof ( sa->sa_data.mc_member_record.port_gid ) ); -} - -/** - * Handle multicast membership record join response - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v madx Management transaction - * @v rc Status code - * @v mad Received MAD (or NULL on error) - * @v av Source address vector (or NULL on error) - */ -static void ib_mcast_complete ( struct ib_device *ibdev, - struct ib_mad_interface *mi __unused, - struct ib_mad_transaction *madx, - int rc, union ib_mad *mad, - struct ib_address_vector *av __unused ) { - struct ib_mc_membership *membership = ib_madx_get_ownerdata ( madx ); - struct ib_queue_pair *qp = membership->qp; - union ib_gid *gid = &membership->gid; - struct ib_mc_member_record *mc_member_record = - &mad->sa.sa_data.mc_member_record; - int joined; - unsigned long qkey; - - /* Report failures */ - if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) - rc = -ENOTCONN; - if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx join failed: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); - goto out; - } - - /* Extract values from MAD */ - joined = ( mad->hdr.method == IB_MGMT_METHOD_GET_RESP ); - qkey = ntohl ( mc_member_record->qkey ); - DBGC ( ibdev, "IBDEV %p QPN %lx %s " IB_GID_FMT " qkey %lx\n", - ibdev, qp->qpn, ( joined ? "joined" : "left" ), - IB_GID_ARGS ( gid ), qkey ); - - /* Set queue key */ - qp->qkey = qkey; - if ( ( rc = ib_modify_qp ( ibdev, qp ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not modify qkey: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); - goto out; - } - - out: - /* Destroy the completed transaction */ - ib_destroy_madx ( ibdev, mi, madx ); - membership->madx = NULL; - - /* Hand off to upper completion handler */ - membership->complete ( ibdev, qp, membership, rc, mad ); -} - -/** Multicast membership management transaction completion operations */ -static struct ib_mad_transaction_operations ib_mcast_op = { - .complete = ib_mcast_complete, -}; - -/** - * Join multicast group - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v membership Multicast group membership - * @v gid Multicast GID to join - * @v joined Join completion handler - * @ret rc Return status code - */ -int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_mc_membership *membership, union ib_gid *gid, - void ( * complete ) ( struct ib_device *ibdev, - struct ib_queue_pair *qp, - struct ib_mc_membership *membership, - int rc, union ib_mad *mad ) ) { - union ib_mad mad; - int rc; - - DBGC ( ibdev, "IBDEV %p QPN %lx joining " IB_GID_FMT "\n", - ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); - - /* Sanity check */ - assert ( qp != NULL ); - - /* Initialise structure */ - membership->qp = qp; - memcpy ( &membership->gid, gid, sizeof ( membership->gid ) ); - membership->complete = complete; - - /* Attach queue pair to multicast GID */ - if ( ( rc = ib_mcast_attach ( ibdev, qp, gid ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not attach: %s\n", - ibdev, qp->qpn, strerror ( rc ) ); - goto err_mcast_attach; - } - - /* Initiate multicast membership join */ - ib_mcast_mad ( ibdev, gid, 1, &mad ); - membership->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, - &ib_mcast_op ); - if ( ! membership->madx ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not create join " - "transaction\n", ibdev, qp->qpn ); - rc = -ENOMEM; - goto err_create_madx; - } - ib_madx_set_ownerdata ( membership->madx, membership ); - - return 0; - - ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx ); - err_create_madx: - ib_mcast_detach ( ibdev, qp, gid ); - err_mcast_attach: - return rc; -} - -/** - * Leave multicast group - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v membership Multicast group membership - */ -void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, - struct ib_mc_membership *membership ) { - union ib_gid *gid = &membership->gid; - union ib_mad mad; - int rc; - - DBGC ( ibdev, "IBDEV %p QPN %lx leaving " IB_GID_FMT "\n", - ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); - - /* Sanity check */ - assert ( qp != NULL ); - - /* Detach from multicast GID */ - ib_mcast_detach ( ibdev, qp, &membership->gid ); - - /* Cancel multicast membership join, if applicable */ - if ( membership->madx ) { - ib_destroy_madx ( ibdev, ibdev->gsi, membership->madx ); - membership->madx = NULL; - } - - /* Send a single group leave MAD */ - ib_mcast_mad ( ibdev, &membership->gid, 0, &mad ); - if ( ( rc = ib_mi_send ( ibdev, ibdev->gsi, &mad, NULL ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p QPN %lx could not send leave request: " - "%s\n", ibdev, qp->qpn, strerror ( rc ) ); - } -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_mi.c b/qemu/roms/ipxe/src/net/infiniband/ib_mi.c deleted file mode 100644 index b43212974..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_mi.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <byteswap.h> -#include <ipxe/infiniband.h> -#include <ipxe/iobuf.h> -#include <ipxe/ib_mi.h> - -/** - * @file - * - * Infiniband management interfaces - * - */ - -/** Management interface number of send WQEs - * - * This is a policy decision. - */ -#define IB_MI_NUM_SEND_WQES 4 - -/** Management interface number of receive WQEs - * - * This is a policy decision. - */ -#define IB_MI_NUM_RECV_WQES 2 - -/** Management interface number of completion queue entries - * - * This is a policy decision - */ -#define IB_MI_NUM_CQES 8 - -/** TID magic signature */ -#define IB_MI_TID_MAGIC ( ( 'i' << 24 ) | ( 'P' << 16 ) | ( 'X' << 8 ) | 'E' ) - -/** TID to use for next MAD */ -static unsigned int next_tid; - -/** - * Handle received MAD - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - * @ret rc Return status code - */ -static int ib_mi_handle ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_mad_hdr *hdr = &mad->hdr; - struct ib_mad_transaction *madx; - struct ib_mad_agent *agent; - - /* Look for a matching transaction by TID */ - list_for_each_entry ( madx, &mi->madx, list ) { - if ( memcmp ( &hdr->tid, &madx->mad.hdr.tid, - sizeof ( hdr->tid ) ) != 0 ) - continue; - /* Found a matching transaction */ - madx->op->complete ( ibdev, mi, madx, 0, mad, av ); - return 0; - } - - /* If there is no matching transaction, look for a listening agent */ - for_each_table_entry ( agent, IB_MAD_AGENTS ) { - if ( ( ( agent->mgmt_class & IB_MGMT_CLASS_MASK ) != - ( hdr->mgmt_class & IB_MGMT_CLASS_MASK ) ) || - ( agent->class_version != hdr->class_version ) || - ( agent->attr_id != hdr->attr_id ) ) - continue; - /* Found a matching agent */ - agent->handle ( ibdev, mi, mad, av ); - return 0; - } - - /* Otherwise, ignore it */ - DBGC ( mi, "MI %p RX TID %08x%08x ignored\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); - return -ENOTSUP; -} - -/** - * Complete receive via management interface - * - * - * @v ibdev Infiniband device - * @v qp Queue pair - * @v dest Destination address vector - * @v source Source address vector - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void ib_mi_complete_recv ( struct ib_device *ibdev, - struct ib_queue_pair *qp, - struct ib_address_vector *dest __unused, - struct ib_address_vector *source, - struct io_buffer *iobuf, int rc ) { - struct ib_mad_interface *mi = ib_qp_get_ownerdata ( qp ); - union ib_mad *mad; - struct ib_mad_hdr *hdr; - - /* Ignore errors */ - if ( rc != 0 ) { - DBGC ( mi, "MI %p RX error: %s\n", mi, strerror ( rc ) ); - goto out; - } - - /* Sanity checks */ - if ( iob_len ( iobuf ) != sizeof ( *mad ) ) { - DBGC ( mi, "MI %p RX bad size (%zd bytes)\n", - mi, iob_len ( iobuf ) ); - DBGC_HDA ( mi, 0, iobuf->data, iob_len ( iobuf ) ); - goto out; - } - mad = iobuf->data; - hdr = &mad->hdr; - if ( hdr->base_version != IB_MGMT_BASE_VERSION ) { - DBGC ( mi, "MI %p RX unsupported base version %x\n", - mi, hdr->base_version ); - DBGC_HDA ( mi, 0, mad, sizeof ( *mad ) ); - goto out; - } - DBGC ( mi, "MI %p RX TID %08x%08x (%02x,%02x,%02x,%04x) status " - "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), - hdr->mgmt_class, hdr->class_version, hdr->method, - ntohs ( hdr->attr_id ), ntohs ( hdr->status ) ); - DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) ); - - /* Handle MAD */ - if ( ( rc = ib_mi_handle ( ibdev, mi, mad, source ) ) != 0 ) - goto out; - - out: - free_iob ( iobuf ); -} - -/** Management interface completion operations */ -static struct ib_completion_queue_operations ib_mi_completion_ops = { - .complete_recv = ib_mi_complete_recv, -}; - -/** Management interface queue pair operations */ -static struct ib_queue_pair_operations ib_mi_queue_pair_ops = { - .alloc_iob = alloc_iob, -}; - -/** - * Transmit MAD - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad MAD - * @v av Destination address vector - * @ret rc Return status code - */ -int ib_mi_send ( struct ib_device *ibdev, struct ib_mad_interface *mi, - union ib_mad *mad, struct ib_address_vector *av ) { - struct ib_mad_hdr *hdr = &mad->hdr; - struct io_buffer *iobuf; - int rc; - - /* Set common fields */ - hdr->base_version = IB_MGMT_BASE_VERSION; - if ( ( hdr->tid[0] == 0 ) && ( hdr->tid[1] == 0 ) ) { - hdr->tid[0] = htonl ( IB_MI_TID_MAGIC ); - hdr->tid[1] = htonl ( ++next_tid ); - } - DBGC ( mi, "MI %p TX TID %08x%08x (%02x,%02x,%02x,%04x) status " - "%04x\n", mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), - hdr->mgmt_class, hdr->class_version, hdr->method, - ntohs ( hdr->attr_id ), ntohs ( hdr->status ) ); - DBGC2_HDA ( mi, 0, mad, sizeof ( *mad ) ); - - /* Construct directed route portion of response, if necessary */ - if ( hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) { - struct ib_mad_smp *smp = &mad->smp; - unsigned int hop_pointer; - unsigned int hop_count; - - smp->mad_hdr.status |= htons ( IB_SMP_STATUS_D_INBOUND ); - hop_pointer = smp->mad_hdr.class_specific.smp.hop_pointer; - hop_count = smp->mad_hdr.class_specific.smp.hop_count; - assert ( hop_count == hop_pointer ); - if ( hop_pointer < ( sizeof ( smp->return_path.hops ) / - sizeof ( smp->return_path.hops[0] ) ) ) { - smp->return_path.hops[hop_pointer] = ibdev->port; - } else { - DBGC ( mi, "MI %p TX TID %08x%08x invalid hop pointer " - "%d\n", mi, ntohl ( hdr->tid[0] ), - ntohl ( hdr->tid[1] ), hop_pointer ); - return -EINVAL; - } - } - - /* Construct I/O buffer */ - iobuf = alloc_iob ( sizeof ( *mad ) ); - if ( ! iobuf ) { - DBGC ( mi, "MI %p could not allocate buffer for TID " - "%08x%08x\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); - return -ENOMEM; - } - memcpy ( iob_put ( iobuf, sizeof ( *mad ) ), mad, sizeof ( *mad ) ); - - /* Send I/O buffer */ - if ( ( rc = ib_post_send ( ibdev, mi->qp, av, iobuf ) ) != 0 ) { - DBGC ( mi, "MI %p TX TID %08x%08x failed: %s\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ), - strerror ( rc ) ); - free_iob ( iobuf ); - return rc; - } - - return 0; -} - -/** - * Handle management transaction timer expiry - * - * @v timer Retry timer - * @v expired Failure indicator - */ -static void ib_mi_timer_expired ( struct retry_timer *timer, int expired ) { - struct ib_mad_transaction *madx = - container_of ( timer, struct ib_mad_transaction, timer ); - struct ib_mad_interface *mi = madx->mi; - struct ib_device *ibdev = mi->ibdev; - struct ib_mad_hdr *hdr = &madx->mad.hdr; - - /* Abandon transaction if we have tried too many times */ - if ( expired ) { - DBGC ( mi, "MI %p abandoning TID %08x%08x\n", - mi, ntohl ( hdr->tid[0] ), ntohl ( hdr->tid[1] ) ); - madx->op->complete ( ibdev, mi, madx, -ETIMEDOUT, NULL, NULL ); - return; - } - - /* Restart retransmission timer */ - start_timer ( timer ); - - /* Resend MAD */ - ib_mi_send ( ibdev, mi, &madx->mad, &madx->av ); -} - -/** - * Create management transaction - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad MAD to send - * @v av Destination address, or NULL to use SM's GSI - * @v op Management transaction operations - * @ret madx Management transaction, or NULL - */ -struct ib_mad_transaction * -ib_create_madx ( struct ib_device *ibdev, struct ib_mad_interface *mi, - union ib_mad *mad, struct ib_address_vector *av, - struct ib_mad_transaction_operations *op ) { - struct ib_mad_transaction *madx; - - /* Allocate and initialise structure */ - madx = zalloc ( sizeof ( *madx ) ); - if ( ! madx ) - return NULL; - timer_init ( &madx->timer, ib_mi_timer_expired, NULL ); - madx->mi = mi; - madx->op = op; - - /* Determine address vector */ - if ( av ) { - memcpy ( &madx->av, av, sizeof ( madx->av ) ); - } else { - madx->av.lid = ibdev->sm_lid; - madx->av.sl = ibdev->sm_sl; - madx->av.qpn = IB_QPN_GSI; - madx->av.qkey = IB_QKEY_GSI; - } - - /* Copy MAD */ - memcpy ( &madx->mad, mad, sizeof ( madx->mad ) ); - - /* Add to list and start timer to send initial MAD */ - list_add ( &madx->list, &mi->madx ); - start_timer_nodelay ( &madx->timer ); - - return madx; -} - -/** - * Destroy management transaction - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v madx Management transaction - */ -void ib_destroy_madx ( struct ib_device *ibdev __unused, - struct ib_mad_interface *mi __unused, - struct ib_mad_transaction *madx ) { - - /* Stop timer and remove from list */ - stop_timer ( &madx->timer ); - list_del ( &madx->list ); - - /* Free transaction */ - free ( madx ); -} - -/** - * Create management interface - * - * @v ibdev Infiniband device - * @v type Queue pair type - * @ret mi Management agent, or NULL - */ -struct ib_mad_interface * ib_create_mi ( struct ib_device *ibdev, - enum ib_queue_pair_type type ) { - struct ib_mad_interface *mi; - int rc; - - /* Allocate and initialise fields */ - mi = zalloc ( sizeof ( *mi ) ); - if ( ! mi ) - goto err_alloc; - mi->ibdev = ibdev; - INIT_LIST_HEAD ( &mi->madx ); - - /* Create completion queue */ - mi->cq = ib_create_cq ( ibdev, IB_MI_NUM_CQES, &ib_mi_completion_ops ); - if ( ! mi->cq ) { - DBGC ( mi, "MI %p could not allocate completion queue\n", mi ); - goto err_create_cq; - } - - /* Create queue pair */ - mi->qp = ib_create_qp ( ibdev, type, IB_MI_NUM_SEND_WQES, mi->cq, - IB_MI_NUM_RECV_WQES, mi->cq, - &ib_mi_queue_pair_ops ); - if ( ! mi->qp ) { - DBGC ( mi, "MI %p could not allocate queue pair\n", mi ); - goto err_create_qp; - } - ib_qp_set_ownerdata ( mi->qp, mi ); - DBGC ( mi, "MI %p (%s) running on QPN %#lx\n", - mi, ( ( type == IB_QPT_SMI ) ? "SMI" : "GSI" ), mi->qp->qpn ); - - /* Set queue key */ - mi->qp->qkey = ( ( type == IB_QPT_SMI ) ? IB_QKEY_SMI : IB_QKEY_GSI ); - if ( ( rc = ib_modify_qp ( ibdev, mi->qp ) ) != 0 ) { - DBGC ( mi, "MI %p could not set queue key: %s\n", - mi, strerror ( rc ) ); - goto err_modify_qp; - } - - /* Fill receive ring */ - ib_refill_recv ( ibdev, mi->qp ); - return mi; - - err_modify_qp: - ib_destroy_qp ( ibdev, mi->qp ); - err_create_qp: - ib_destroy_cq ( ibdev, mi->cq ); - err_create_cq: - free ( mi ); - err_alloc: - return NULL; -} - -/** - * Destroy management interface - * - * @v mi Management interface - */ -void ib_destroy_mi ( struct ib_device *ibdev, struct ib_mad_interface *mi ) { - struct ib_mad_transaction *madx; - struct ib_mad_transaction *tmp; - - /* Flush any outstanding requests */ - list_for_each_entry_safe ( madx, tmp, &mi->madx, list ) { - DBGC ( mi, "MI %p destroyed while TID %08x%08x in progress\n", - mi, ntohl ( madx->mad.hdr.tid[0] ), - ntohl ( madx->mad.hdr.tid[1] ) ); - madx->op->complete ( ibdev, mi, madx, -ECANCELED, NULL, NULL ); - } - - ib_destroy_qp ( ibdev, mi->qp ); - ib_destroy_cq ( ibdev, mi->cq ); - free ( mi ); -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_packet.c b/qemu/roms/ipxe/src/net/infiniband/ib_packet.c deleted file mode 100644 index d3a22d309..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_packet.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/iobuf.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_packet.h> - -/** - * @file - * - * Infiniband Packet Formats - * - */ - -/** - * Add IB headers - * - * @v ibdev Infiniband device - * @v iobuf I/O buffer to contain headers - * @v qp Queue pair - * @v payload_len Payload length - * @v dest Destination address vector - * @ret rc Return status code - */ -int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf, - struct ib_queue_pair *qp, size_t payload_len, - const struct ib_address_vector *dest ) { - struct ib_local_route_header *lrh; - struct ib_global_route_header *grh; - struct ib_base_transport_header *bth; - struct ib_datagram_extended_transport_header *deth; - size_t orig_iob_len = iob_len ( iobuf ); - size_t pad_len; - size_t lrh_len; - size_t grh_len; - unsigned int vl; - unsigned int lnh; - - DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n", - ibdev, ibdev->lid, qp->ext_qpn, dest->lid, dest->qpn, - dest->qkey ); - - /* Calculate packet length */ - pad_len = ( (-payload_len) & 0x3 ); - payload_len += pad_len; - payload_len += 4; /* ICRC */ - - /* Reserve space for headers */ - orig_iob_len = iob_len ( iobuf ); - deth = iob_push ( iobuf, sizeof ( *deth ) ); - bth = iob_push ( iobuf, sizeof ( *bth ) ); - grh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len ); - grh = ( dest->gid_present ? - iob_push ( iobuf, sizeof ( *grh ) ) : NULL ); - lrh = iob_push ( iobuf, sizeof ( *lrh ) ); - lrh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len ); - - /* Construct LRH */ - vl = ( ( qp->ext_qpn == IB_QPN_SMI ) ? IB_VL_SMP : IB_VL_DEFAULT ); - lrh->vl__lver = ( vl << 4 ); - lnh = ( grh ? IB_LNH_GRH : IB_LNH_BTH ); - lrh->sl__lnh = ( ( dest->sl << 4 ) | lnh ); - lrh->dlid = htons ( dest->lid ); - lrh->length = htons ( lrh_len >> 2 ); - lrh->slid = htons ( ibdev->lid ); - - /* Construct GRH, if required */ - if ( grh ) { - grh->ipver__tclass__flowlabel = - htonl ( IB_GRH_IPVER_IPv6 << 28 ); - grh->paylen = htons ( grh_len ); - grh->nxthdr = IB_GRH_NXTHDR_IBA; - grh->hoplmt = 0; - memcpy ( &grh->sgid, &ibdev->gid, sizeof ( grh->sgid ) ); - memcpy ( &grh->dgid, &dest->gid, sizeof ( grh->dgid ) ); - } - - /* Construct BTH */ - bth->opcode = BTH_OPCODE_UD_SEND; - bth->se__m__padcnt__tver = ( pad_len << 4 ); - bth->pkey = htons ( ibdev->pkey ); - bth->dest_qp = htonl ( dest->qpn ); - bth->ack__psn = htonl ( ( qp->send.psn++ ) & 0xffffffUL ); - - /* Construct DETH */ - deth->qkey = htonl ( dest->qkey ); - deth->src_qp = htonl ( qp->ext_qpn ); - - DBGCP_HDA ( ibdev, 0, iobuf->data, - ( iob_len ( iobuf ) - orig_iob_len ) ); - - return 0; -} - -/** - * Remove IB headers - * - * @v ibdev Infiniband device - * @v iobuf I/O buffer containing headers - * @v qp Queue pair to fill in, or NULL - * @v payload_len Payload length to fill in, or NULL - * @v dest Destination address vector to fill in - * @v source Source address vector to fill in - * @ret rc Return status code - */ -int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, - struct ib_queue_pair **qp, size_t *payload_len, - struct ib_address_vector *dest, - struct ib_address_vector *source ) { - struct ib_local_route_header *lrh; - struct ib_global_route_header *grh; - struct ib_base_transport_header *bth; - struct ib_datagram_extended_transport_header *deth; - size_t orig_iob_len = iob_len ( iobuf ); - unsigned int lnh; - size_t pad_len; - - /* Clear return values */ - if ( qp ) - *qp = NULL; - if ( payload_len ) - *payload_len = 0; - memset ( dest, 0, sizeof ( *dest ) ); - memset ( source, 0, sizeof ( *source ) ); - - /* Extract LRH */ - if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n", - ibdev, iob_len ( iobuf ) ); - return -EINVAL; - } - lrh = iobuf->data; - iob_pull ( iobuf, sizeof ( *lrh ) ); - dest->lid = ntohs ( lrh->dlid ); - dest->sl = ( lrh->sl__lnh >> 4 ); - source->lid = ntohs ( lrh->slid ); - source->sl = ( lrh->sl__lnh >> 4 ); - lnh = ( lrh->sl__lnh & 0x3 ); - - /* Reject unsupported packets */ - if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) { - DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n", - ibdev, lnh ); - return -ENOTSUP; - } - - /* Extract GRH, if present */ - if ( lnh == IB_LNH_GRH ) { - if ( iob_len ( iobuf ) < sizeof ( *grh ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) " - "for GRH\n", ibdev, iob_len ( iobuf ) ); - return -EINVAL; - } - grh = iobuf->data; - iob_pull ( iobuf, sizeof ( *grh ) ); - dest->gid_present = 1; - memcpy ( &dest->gid, &grh->dgid, sizeof ( dest->gid ) ); - source->gid_present = 1; - memcpy ( &source->gid, &grh->sgid, sizeof ( source->gid ) ); - } else { - grh = NULL; - } - - /* Extract BTH */ - if ( iob_len ( iobuf ) < sizeof ( *bth ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n", - ibdev, iob_len ( iobuf ) ); - return -EINVAL; - } - bth = iobuf->data; - iob_pull ( iobuf, sizeof ( *bth ) ); - if ( bth->opcode != BTH_OPCODE_UD_SEND ) { - DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n", - ibdev, bth->opcode ); - return -ENOTSUP; - } - dest->qpn = ntohl ( bth->dest_qp ); - - /* Extract DETH */ - if ( iob_len ( iobuf ) < sizeof ( *deth ) ) { - DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n", - ibdev, iob_len ( iobuf ) ); - return -EINVAL; - } - deth = iobuf->data; - iob_pull ( iobuf, sizeof ( *deth ) ); - source->qpn = ntohl ( deth->src_qp ); - source->qkey = ntohl ( deth->qkey ); - - /* Calculate payload length, if applicable */ - if ( payload_len ) { - pad_len = ( ( bth->se__m__padcnt__tver >> 4 ) & 0x3 ); - *payload_len = ( ( ntohs ( lrh->length ) << 2 ) - - ( orig_iob_len - iob_len ( iobuf ) ) - - pad_len - 4 /* ICRC */ ); - } - - /* Determine destination QP, if applicable */ - if ( qp ) { - if ( IB_LID_MULTICAST ( dest->lid ) && grh ) { - if ( ! ( *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ))){ - DBGC ( ibdev, "IBDEV %p RX for unknown MGID " - IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( &grh->dgid ) ); - return -ENODEV; - } - } else { - if ( ! ( *qp = ib_find_qp_qpn ( ibdev, dest->qpn ) ) ) { - DBGC ( ibdev, "IBDEV %p RX for nonexistent " - "QPN %lx\n", ibdev, dest->qpn ); - return -ENODEV; - } - } - assert ( *qp ); - } - - DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n", - ibdev, dest->lid, ( IB_LID_MULTICAST ( dest->lid ) ? - ( qp ? (*qp)->ext_qpn : -1UL ) : dest->qpn ), - source->lid, source->qpn, ntohl ( deth->qkey ) ); - DBGCP_HDA ( ibdev, 0, - ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ), - ( orig_iob_len - iob_len ( iobuf ) ) ); - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c b/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c deleted file mode 100644 index f9cbab87f..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_pathrec.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <byteswap.h> -#include <errno.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_mi.h> -#include <ipxe/ib_pathrec.h> - -/** @file - * - * Infiniband path lookups - * - */ - -/** - * Handle path transaction completion - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v madx Management transaction - * @v rc Status code - * @v mad Received MAD (or NULL on error) - * @v av Source address vector (or NULL on error) - */ -static void ib_path_complete ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - struct ib_mad_transaction *madx, - int rc, union ib_mad *mad, - struct ib_address_vector *av __unused ) { - struct ib_path *path = ib_madx_get_ownerdata ( madx ); - union ib_gid *dgid = &path->av.gid; - struct ib_path_record *pathrec = &mad->sa.sa_data.path_record; - - /* Report failures */ - if ( ( rc == 0 ) && ( mad->hdr.status != htons ( IB_MGMT_STATUS_OK ) )) - rc = -ENETUNREACH; - if ( rc != 0 ) { - DBGC ( ibdev, "IBDEV %p path lookup for " IB_GID_FMT - " failed: %s\n", - ibdev, IB_GID_ARGS ( dgid ), strerror ( rc ) ); - goto out; - } - - /* Extract values from MAD */ - path->av.lid = ntohs ( pathrec->dlid ); - path->av.sl = ( pathrec->reserved__sl & 0x0f ); - path->av.rate = ( pathrec->rate_selector__rate & 0x3f ); - DBGC ( ibdev, "IBDEV %p path to " IB_GID_FMT " is %04x sl %d rate " - "%d\n", ibdev, IB_GID_ARGS ( dgid ), path->av.lid, path->av.sl, - path->av.rate ); - - out: - /* Destroy the completed transaction */ - ib_destroy_madx ( ibdev, mi, madx ); - path->madx = NULL; - - /* Hand off to upper completion handler */ - path->op->complete ( ibdev, path, rc, &path->av ); -} - -/** Path transaction completion operations */ -static struct ib_mad_transaction_operations ib_path_op = { - .complete = ib_path_complete, -}; - -/** - * Create path - * - * @v ibdev Infiniband device - * @v av Address vector to complete - * @v op Path operations - * @ret path Path - */ -struct ib_path * -ib_create_path ( struct ib_device *ibdev, struct ib_address_vector *av, - struct ib_path_operations *op ) { - struct ib_path *path; - union ib_mad mad; - struct ib_mad_sa *sa = &mad.sa; - - /* Allocate and initialise structure */ - path = zalloc ( sizeof ( *path ) ); - if ( ! path ) - goto err_alloc_path; - path->ibdev = ibdev; - memcpy ( &path->av, av, sizeof ( path->av ) ); - path->op = op; - - /* Construct path request */ - memset ( sa, 0, sizeof ( *sa ) ); - sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; - sa->mad_hdr.class_version = IB_SA_CLASS_VERSION; - sa->mad_hdr.method = IB_MGMT_METHOD_GET; - sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC ); - sa->sa_hdr.comp_mask[1] = - htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID ); - memcpy ( &sa->sa_data.path_record.dgid, &path->av.gid, - sizeof ( sa->sa_data.path_record.dgid ) ); - memcpy ( &sa->sa_data.path_record.sgid, &ibdev->gid, - sizeof ( sa->sa_data.path_record.sgid ) ); - - /* Create management transaction */ - path->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL, - &ib_path_op ); - if ( ! path->madx ) - goto err_create_madx; - ib_madx_set_ownerdata ( path->madx, path ); - - return path; - - ib_destroy_madx ( ibdev, ibdev->gsi, path->madx ); - err_create_madx: - free ( path ); - err_alloc_path: - return NULL; -} - -/** - * Destroy path - * - * @v ibdev Infiniband device - * @v path Path - */ -void ib_destroy_path ( struct ib_device *ibdev, struct ib_path *path ) { - - if ( path->madx ) - ib_destroy_madx ( ibdev, ibdev->gsi, path->madx ); - free ( path ); -} - -/** Number of path cache entries - * - * Must be a power of two. - */ -#define IB_NUM_CACHED_PATHS 4 - -/** A cached path */ -struct ib_cached_path { - /** Path */ - struct ib_path *path; -}; - -/** Path cache */ -static struct ib_cached_path ib_path_cache[IB_NUM_CACHED_PATHS]; - -/** Oldest path cache entry index */ -static unsigned int ib_path_cache_idx; - -/** - * Find path cache entry - * - * @v ibdev Infiniband device - * @v dgid Destination GID - * @ret path Path cache entry, or NULL - */ -static struct ib_cached_path * -ib_find_path_cache_entry ( struct ib_device *ibdev, union ib_gid *dgid ) { - struct ib_cached_path *cached; - unsigned int i; - - for ( i = 0 ; i < IB_NUM_CACHED_PATHS ; i++ ) { - cached = &ib_path_cache[i]; - if ( ! cached->path ) - continue; - if ( cached->path->ibdev != ibdev ) - continue; - if ( memcmp ( &cached->path->av.gid, dgid, - sizeof ( cached->path->av.gid ) ) != 0 ) - continue; - return cached; - } - - return NULL; -} - -/** - * Handle cached path transaction completion - * - * @v ibdev Infiniband device - * @v path Path - * @v rc Status code - * @v av Address vector, or NULL on error - */ -static void ib_cached_path_complete ( struct ib_device *ibdev, - struct ib_path *path, int rc, - struct ib_address_vector *av __unused ) { - struct ib_cached_path *cached = ib_path_get_ownerdata ( path ); - - /* If the transaction failed, erase the cache entry */ - if ( rc != 0 ) { - /* Destroy the old cache entry */ - ib_destroy_path ( ibdev, path ); - memset ( cached, 0, sizeof ( *cached ) ); - return; - } - - /* Do not destroy the completed transaction; we still need to - * refer to the resolved path. - */ -} - -/** Cached path transaction completion operations */ -static struct ib_path_operations ib_cached_path_op = { - .complete = ib_cached_path_complete, -}; - -/** - * Resolve path - * - * @v ibdev Infiniband device - * @v av Address vector to complete - * @ret rc Return status code - * - * This provides a non-transactional way to resolve a path, via a - * cache similar to ARP. - */ -int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) { - union ib_gid *gid = &av->gid; - struct ib_cached_path *cached; - unsigned int cache_idx; - - /* Sanity check */ - if ( ! av->gid_present ) { - DBGC ( ibdev, "IBDEV %p attempt to look up path without GID\n", - ibdev ); - return -EINVAL; - } - - /* Look in cache for a matching entry */ - cached = ib_find_path_cache_entry ( ibdev, gid ); - if ( cached && cached->path->av.lid ) { - /* Populated entry found */ - av->lid = cached->path->av.lid; - av->rate = cached->path->av.rate; - av->sl = cached->path->av.sl; - DBGC2 ( ibdev, "IBDEV %p cache hit for " IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( gid ) ); - return 0; - } - DBGC ( ibdev, "IBDEV %p cache miss for " IB_GID_FMT "%s\n", ibdev, - IB_GID_ARGS ( gid ), ( cached ? " (in progress)" : "" ) ); - - /* If lookup is already in progress, do nothing */ - if ( cached ) - return -ENOENT; - - /* Locate a new cache entry to use */ - cache_idx = ( (ib_path_cache_idx++) % IB_NUM_CACHED_PATHS ); - cached = &ib_path_cache[cache_idx]; - - /* Destroy the old cache entry */ - if ( cached->path ) - ib_destroy_path ( ibdev, cached->path ); - memset ( cached, 0, sizeof ( *cached ) ); - - /* Create new path */ - cached->path = ib_create_path ( ibdev, av, &ib_cached_path_op ); - if ( ! cached->path ) { - DBGC ( ibdev, "IBDEV %p could not create path\n", - ibdev ); - return -ENOMEM; - } - ib_path_set_ownerdata ( cached->path, cached ); - - /* Not found yet */ - return -ENOENT; -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_sma.c b/qemu/roms/ipxe/src/net/infiniband/ib_sma.c deleted file mode 100644 index a05d7c924..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_sma.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <unistd.h> -#include <byteswap.h> -#include <ipxe/settings.h> -#include <ipxe/infiniband.h> -#include <ipxe/iobuf.h> -#include <ipxe/ib_mi.h> -#include <ipxe/ib_sma.h> - -/** - * @file - * - * Infiniband Subnet Management Agent - * - */ - -/** - * Node information - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - */ -static void ib_sma_node_info ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_node_info *node_info = &mad->smp.smp_data.node_info; - int rc; - - /* Fill in information */ - memset ( node_info, 0, sizeof ( *node_info ) ); - node_info->base_version = IB_MGMT_BASE_VERSION; - node_info->class_version = IB_SMP_CLASS_VERSION; - node_info->node_type = IB_NODE_TYPE_HCA; - node_info->num_ports = ib_count_ports ( ibdev ); - memcpy ( &node_info->sys_guid, &ibdev->node_guid, - sizeof ( node_info->sys_guid ) ); - memcpy ( &node_info->node_guid, &ibdev->node_guid, - sizeof ( node_info->node_guid ) ); - memcpy ( &node_info->port_guid, &ibdev->gid.s.guid, - sizeof ( node_info->port_guid ) ); - node_info->partition_cap = htons ( 1 ); - node_info->local_port_num = ibdev->port; - - /* Send GetResponse */ - mad->hdr.method = IB_MGMT_METHOD_GET_RESP; - if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) { - DBGC ( mi, "SMA %p could not send NodeInfo GetResponse: %s\n", - mi, strerror ( rc ) ); - return; - } -} - -/** - * Node description - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - */ -static void ib_sma_node_desc ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_node_desc *node_desc = &mad->smp.smp_data.node_desc; - union ib_guid *guid = &ibdev->node_guid; - char hostname[ sizeof ( node_desc->node_string ) ]; - int hostname_len; - int rc; - - /* Fill in information */ - memset ( node_desc, 0, sizeof ( *node_desc ) ); - hostname_len = fetch_string_setting ( NULL, &hostname_setting, - hostname, sizeof ( hostname ) ); - snprintf ( node_desc->node_string, sizeof ( node_desc->node_string ), - "iPXE %s%s%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)", - hostname, ( ( hostname_len >= 0 ) ? " " : "" ), - guid->bytes[0], guid->bytes[1], guid->bytes[2], - guid->bytes[3], guid->bytes[4], guid->bytes[5], - guid->bytes[6], guid->bytes[7], ibdev->dev->name ); - - /* Send GetResponse */ - mad->hdr.method = IB_MGMT_METHOD_GET_RESP; - if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) { - DBGC ( mi, "SMA %p could not send NodeDesc GetResponse: %s\n", - mi, strerror ( rc ) ); - return; - } -} - -/** - * GUID information - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - */ -static void ib_sma_guid_info ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_guid_info *guid_info = &mad->smp.smp_data.guid_info; - int rc; - - /* Fill in information */ - memset ( guid_info, 0, sizeof ( *guid_info ) ); - memcpy ( guid_info->guid[0], &ibdev->gid.s.guid, - sizeof ( guid_info->guid[0] ) ); - - /* Send GetResponse */ - mad->hdr.method = IB_MGMT_METHOD_GET_RESP; - if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) { - DBGC ( mi, "SMA %p could not send GuidInfo GetResponse: %s\n", - mi, strerror ( rc ) ); - return; - } -} - -/** - * Set port information - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @ret rc Return status code - */ -static int ib_sma_set_port_info ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad ) { - const struct ib_port_info *port_info = &mad->smp.smp_data.port_info; - unsigned int link_width_enabled; - unsigned int link_speed_enabled; - int rc; - - /* Set parameters */ - memcpy ( &ibdev->gid.s.prefix, port_info->gid_prefix, - sizeof ( ibdev->gid.s.prefix ) ); - ibdev->lid = ntohs ( port_info->lid ); - ibdev->sm_lid = ntohs ( port_info->mastersm_lid ); - if ( ( link_width_enabled = port_info->link_width_enabled ) ) - ibdev->link_width_enabled = link_width_enabled; - if ( ( link_speed_enabled = - ( port_info->link_speed_active__link_speed_enabled & 0xf ) ) ) - ibdev->link_speed_enabled = link_speed_enabled; - ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf ); - DBGC ( mi, "SMA %p set LID %04x SMLID %04x link width %02x speed " - "%02x\n", mi, ibdev->lid, ibdev->sm_lid, - ibdev->link_width_enabled, ibdev->link_speed_enabled ); - - /* Update parameters on device */ - if ( ( rc = ib_set_port_info ( ibdev, mad ) ) != 0 ) { - DBGC ( mi, "SMA %p could not set port information: %s\n", - mi, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Port information - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - */ -static void ib_sma_port_info ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_port_info *port_info = &mad->smp.smp_data.port_info; - int rc; - - /* Set parameters if applicable */ - if ( mad->hdr.method == IB_MGMT_METHOD_SET ) { - if ( ( rc = ib_sma_set_port_info ( ibdev, mi, mad ) ) != 0 ) { - mad->hdr.status = - htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR ); - /* Fall through to generate GetResponse */ - } - } - - /* Fill in information */ - memset ( port_info, 0, sizeof ( *port_info ) ); - memcpy ( port_info->gid_prefix, &ibdev->gid.s.prefix, - sizeof ( port_info->gid_prefix ) ); - port_info->lid = ntohs ( ibdev->lid ); - port_info->mastersm_lid = ntohs ( ibdev->sm_lid ); - port_info->local_port_num = ibdev->port; - port_info->link_width_enabled = ibdev->link_width_enabled; - port_info->link_width_supported = ibdev->link_width_supported; - port_info->link_width_active = ibdev->link_width_active; - port_info->link_speed_supported__port_state = - ( ( ibdev->link_speed_supported << 4 ) | ibdev->port_state ); - port_info->port_phys_state__link_down_def_state = - ( ( IB_PORT_PHYS_STATE_POLLING << 4 ) | - IB_PORT_PHYS_STATE_POLLING ); - port_info->link_speed_active__link_speed_enabled = - ( ( ibdev->link_speed_active << 4 ) | - ibdev->link_speed_enabled ); - port_info->neighbour_mtu__mastersm_sl = - ( ( IB_MTU_2048 << 4 ) | ibdev->sm_sl ); - port_info->vl_cap__init_type = ( IB_VL_0 << 4 ); - port_info->init_type_reply__mtu_cap = IB_MTU_2048; - port_info->operational_vls__enforcement = ( IB_VL_0 << 4 ); - port_info->guid_cap = 1; - - /* Send GetResponse */ - mad->hdr.method = IB_MGMT_METHOD_GET_RESP; - if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) { - DBGC ( mi, "SMA %p could not send PortInfo GetResponse: %s\n", - mi, strerror ( rc ) ); - return; - } -} - -/** - * Set partition key table - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @ret rc Return status code - */ -static int ib_sma_set_pkey_table ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad ) { - struct ib_pkey_table *pkey_table = &mad->smp.smp_data.pkey_table; - int rc; - - /* Set parameters */ - ibdev->pkey = ntohs ( pkey_table->pkey[0] ); - DBGC ( mi, "SMA %p set pkey %04x\n", mi, ibdev->pkey ); - - /* Update parameters on device */ - if ( ( rc = ib_set_pkey_table ( ibdev, mad ) ) != 0 ) { - DBGC ( mi, "SMA %p could not set pkey table: %s\n", - mi, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Partition key table - * - * @v ibdev Infiniband device - * @v mi Management interface - * @v mad Received MAD - * @v av Source address vector - */ -static void ib_sma_pkey_table ( struct ib_device *ibdev, - struct ib_mad_interface *mi, - union ib_mad *mad, - struct ib_address_vector *av ) { - struct ib_pkey_table *pkey_table = &mad->smp.smp_data.pkey_table; - int rc; - - /* Set parameters, if applicable */ - if ( mad->hdr.method == IB_MGMT_METHOD_SET ) { - if ( ( rc = ib_sma_set_pkey_table ( ibdev, mi, mad ) ) != 0 ) { - mad->hdr.status = - htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR ); - /* Fall through to generate GetResponse */ - } - } - - /* Fill in information */ - mad->hdr.method = IB_MGMT_METHOD_GET_RESP; - memset ( pkey_table, 0, sizeof ( *pkey_table ) ); - pkey_table->pkey[0] = htons ( ibdev->pkey ); - - /* Send GetResponse */ - mad->hdr.method = IB_MGMT_METHOD_GET_RESP; - if ( ( rc = ib_mi_send ( ibdev, mi, mad, av ) ) != 0 ) { - DBGC ( mi, "SMA %p could not send PKeyTable GetResponse: %s\n", - mi, strerror ( rc ) ); - return; - } -} - -/** Subnet management agent */ -struct ib_mad_agent ib_sma_agent[] __ib_mad_agent = { - { - .mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED, - .class_version = IB_SMP_CLASS_VERSION, - .attr_id = htons ( IB_SMP_ATTR_NODE_INFO ), - .handle = ib_sma_node_info, - }, - { - .mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED, - .class_version = IB_SMP_CLASS_VERSION, - .attr_id = htons ( IB_SMP_ATTR_NODE_DESC ), - .handle = ib_sma_node_desc, - }, - { - .mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED, - .class_version = IB_SMP_CLASS_VERSION, - .attr_id = htons ( IB_SMP_ATTR_GUID_INFO ), - .handle = ib_sma_guid_info, - }, - { - .mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED, - .class_version = IB_SMP_CLASS_VERSION, - .attr_id = htons ( IB_SMP_ATTR_PORT_INFO ), - .handle = ib_sma_port_info, - }, - { - .mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED, - .class_version = IB_SMP_CLASS_VERSION, - .attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE ), - .handle = ib_sma_pkey_table, - }, -}; - -/** - * Create subnet management agent and interface - * - * @v ibdev Infiniband device - * @v mi Management interface - * @ret rc Return status code - */ -int ib_create_sma ( struct ib_device *ibdev, struct ib_mad_interface *mi ) { - - /* Nothing to do */ - DBGC ( ibdev, "IBDEV %p SMA using SMI %p\n", ibdev, mi ); - - return 0; -} - -/** - * Destroy subnet management agent and interface - * - * @v ibdev Infiniband device - * @v mi Management interface - */ -void ib_destroy_sma ( struct ib_device *ibdev __unused, - struct ib_mad_interface *mi __unused ) { - /* Nothing to do */ -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_smc.c b/qemu/roms/ipxe/src/net/infiniband/ib_smc.c deleted file mode 100644 index c1741b26c..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_smc.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <byteswap.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_smc.h> - -/** - * @file - * - * Infiniband Subnet Management Client - * - */ - -/** - * Issue local MAD - * - * @v ibdev Infiniband device - * @v attr_id Attribute ID, in network byte order - * @v attr_mod Attribute modifier, in network byte order - * @v local_mad Method for issuing local MADs - * @v mad Management datagram to fill in - * @ret rc Return status code - */ -static int ib_smc_mad ( struct ib_device *ibdev, uint16_t attr_id, - uint32_t attr_mod, ib_local_mad_t local_mad, - union ib_mad *mad ) { - int rc; - - /* Construct MAD */ - memset ( mad, 0, sizeof ( *mad ) ); - mad->hdr.base_version = IB_MGMT_BASE_VERSION; - mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED; - mad->hdr.class_version = 1; - mad->hdr.method = IB_MGMT_METHOD_GET; - mad->hdr.attr_id = attr_id; - mad->hdr.attr_mod = attr_mod; - - /* Issue MAD */ - if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Get node information - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @v mad Management datagram to fill in - * @ret rc Return status code - */ -static int ib_smc_get_node_info ( struct ib_device *ibdev, - ib_local_mad_t local_mad, - union ib_mad *mad ) { - int rc; - - /* Issue MAD */ - if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_NODE_INFO ), 0, - local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get node info: %s\n", - ibdev, strerror ( rc ) ); - return rc; - } - return 0; -} - -/** - * Get port information - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @v mad Management datagram to fill in - * @ret rc Return status code - */ -static int ib_smc_get_port_info ( struct ib_device *ibdev, - ib_local_mad_t local_mad, - union ib_mad *mad ) { - int rc; - - /* Issue MAD */ - if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_PORT_INFO ), - htonl ( ibdev->port ), local_mad, mad )) !=0){ - DBGC ( ibdev, "IBDEV %p could not get port info: %s\n", - ibdev, strerror ( rc ) ); - return rc; - } - return 0; -} - -/** - * Get GUID information - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @v mad Management datagram to fill in - * @ret rc Return status code - */ -static int ib_smc_get_guid_info ( struct ib_device *ibdev, - ib_local_mad_t local_mad, - union ib_mad *mad ) { - int rc; - - /* Issue MAD */ - if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_GUID_INFO ), 0, - local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n", - ibdev, strerror ( rc ) ); - return rc; - } - return 0; -} - -/** - * Get partition key table - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @v mad Management datagram to fill in - * @ret rc Return status code - */ -static int ib_smc_get_pkey_table ( struct ib_device *ibdev, - ib_local_mad_t local_mad, - union ib_mad *mad ) { - int rc; - - /* Issue MAD */ - if ( ( rc = ib_smc_mad ( ibdev, htons ( IB_SMP_ATTR_PKEY_TABLE ), 0, - local_mad, mad ) ) != 0 ) { - DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n", - ibdev, strerror ( rc ) ); - return rc; - } - return 0; -} - -/** - * Get Infiniband parameters using SMC - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @ret rc Return status code - */ -static int ib_smc_get ( struct ib_device *ibdev, ib_local_mad_t local_mad ) { - union ib_mad mad; - struct ib_node_info *node_info = &mad.smp.smp_data.node_info; - struct ib_port_info *port_info = &mad.smp.smp_data.port_info; - struct ib_guid_info *guid_info = &mad.smp.smp_data.guid_info; - struct ib_pkey_table *pkey_table = &mad.smp.smp_data.pkey_table; - int rc; - - /* Node info gives us the node GUID */ - if ( ( rc = ib_smc_get_node_info ( ibdev, local_mad, &mad ) ) != 0 ) - return rc; - memcpy ( &ibdev->node_guid, &node_info->node_guid, - sizeof ( ibdev->node_guid ) ); - - /* Port info gives us the link state, the first half of the - * port GID and the SM LID. - */ - if ( ( rc = ib_smc_get_port_info ( ibdev, local_mad, &mad ) ) != 0 ) - return rc; - memcpy ( &ibdev->gid.s.prefix, port_info->gid_prefix, - sizeof ( ibdev->gid.s.prefix ) ); - ibdev->lid = ntohs ( port_info->lid ); - ibdev->sm_lid = ntohs ( port_info->mastersm_lid ); - ibdev->link_width_enabled = port_info->link_width_enabled; - ibdev->link_width_supported = port_info->link_width_supported; - ibdev->link_width_active = port_info->link_width_active; - ibdev->link_speed_supported = - ( port_info->link_speed_supported__port_state >> 4 ); - ibdev->port_state = - ( port_info->link_speed_supported__port_state & 0xf ); - ibdev->link_speed_active = - ( port_info->link_speed_active__link_speed_enabled >> 4 ); - ibdev->link_speed_enabled = - ( port_info->link_speed_active__link_speed_enabled & 0xf ); - ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf ); - - /* GUID info gives us the second half of the port GID */ - if ( ( rc = ib_smc_get_guid_info ( ibdev, local_mad, &mad ) ) != 0 ) - return rc; - memcpy ( &ibdev->gid.s.guid, guid_info->guid[0], - sizeof ( ibdev->gid.s.guid ) ); - - /* Get partition key */ - if ( ( rc = ib_smc_get_pkey_table ( ibdev, local_mad, &mad ) ) != 0 ) - return rc; - ibdev->pkey = ntohs ( pkey_table->pkey[0] ); - - DBGC ( ibdev, "IBDEV %p port GID is " IB_GID_FMT "\n", - ibdev, IB_GID_ARGS ( &ibdev->gid ) ); - - return 0; -} - -/** - * Initialise Infiniband parameters using SMC - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @ret rc Return status code - */ -int ib_smc_init ( struct ib_device *ibdev, ib_local_mad_t local_mad ) { - int rc; - - /* Get MAD parameters */ - if ( ( rc = ib_smc_get ( ibdev, local_mad ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Update Infiniband parameters using SMC - * - * @v ibdev Infiniband device - * @v local_mad Method for issuing local MADs - * @ret rc Return status code - */ -int ib_smc_update ( struct ib_device *ibdev, ib_local_mad_t local_mad ) { - int rc; - - /* Get MAD parameters */ - if ( ( rc = ib_smc_get ( ibdev, local_mad ) ) != 0 ) - return rc; - - /* Notify Infiniband core of potential link state change */ - ib_link_state_changed ( ibdev ); - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/infiniband/ib_srp.c b/qemu/roms/ipxe/src/net/infiniband/ib_srp.c deleted file mode 100644 index 3700184c0..000000000 --- a/qemu/roms/ipxe/src/net/infiniband/ib_srp.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright (C) 2009 Fen Systems Ltd <mbrown@fensystems.co.uk>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -FILE_LICENCE ( BSD2 ); - -#include <stdlib.h> -#include <errno.h> -#include <ipxe/interface.h> -#include <ipxe/uri.h> -#include <ipxe/open.h> -#include <ipxe/base16.h> -#include <ipxe/acpi.h> -#include <ipxe/srp.h> -#include <ipxe/infiniband.h> -#include <ipxe/ib_cmrc.h> -#include <ipxe/ib_srp.h> - -/** - * @file - * - * SCSI RDMA Protocol over Infiniband - * - */ - -/* Disambiguate the various possible EINVALs */ -#define EINVAL_BYTE_STRING_LEN __einfo_error ( EINFO_EINVAL_BYTE_STRING_LEN ) -#define EINFO_EINVAL_BYTE_STRING_LEN __einfo_uniqify \ - ( EINFO_EINVAL, 0x01, "Invalid byte string length" ) -#define EINVAL_INTEGER __einfo_error ( EINFO_EINVAL_INTEGER ) -#define EINFO_EINVAL_INTEGER __einfo_uniqify \ - ( EINFO_EINVAL, 0x03, "Invalid integer" ) -#define EINVAL_RP_TOO_SHORT __einfo_error ( EINFO_EINVAL_RP_TOO_SHORT ) -#define EINFO_EINVAL_RP_TOO_SHORT __einfo_uniqify \ - ( EINFO_EINVAL, 0x04, "Root path too short" ) - -/****************************************************************************** - * - * IB SRP devices - * - ****************************************************************************** - */ - -/** An Infiniband SRP device */ -struct ib_srp_device { - /** Reference count */ - struct refcnt refcnt; - - /** SRP transport interface */ - struct interface srp; - /** CMRC interface */ - struct interface cmrc; - - /** Infiniband device */ - struct ib_device *ibdev; - - /** Destination GID (for boot firmware table) */ - union ib_gid dgid; - /** Service ID (for boot firmware table) */ - union ib_guid service_id; -}; - -/** - * Free IB SRP device - * - * @v refcnt Reference count - */ -static void ib_srp_free ( struct refcnt *refcnt ) { - struct ib_srp_device *ib_srp = - container_of ( refcnt, struct ib_srp_device, refcnt ); - - ibdev_put ( ib_srp->ibdev ); - free ( ib_srp ); -} - -/** - * Close IB SRP device - * - * @v ib_srp IB SRP device - * @v rc Reason for close - */ -static void ib_srp_close ( struct ib_srp_device *ib_srp, int rc ) { - - /* Shut down interfaces */ - intf_shutdown ( &ib_srp->cmrc, rc ); - intf_shutdown ( &ib_srp->srp, rc ); -} - -/** - * Describe IB SRP device in an ACPI table - * - * @v srpdev SRP device - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int ib_srp_describe ( struct ib_srp_device *ib_srp, - struct acpi_description_header *acpi, - size_t len ) { - struct ib_device *ibdev = ib_srp->ibdev; - struct sbft_table *sbft = - container_of ( acpi, struct sbft_table, acpi ); - struct sbft_ib_subtable *ib_sbft; - size_t used; - - /* Sanity check */ - if ( acpi->signature != SBFT_SIG ) - return -EINVAL; - - /* Append IB subtable to existing table */ - used = le32_to_cpu ( sbft->acpi.length ); - sbft->ib_offset = cpu_to_le16 ( used ); - ib_sbft = ( ( ( void * ) sbft ) + used ); - used += sizeof ( *ib_sbft ); - if ( used > len ) - return -ENOBUFS; - sbft->acpi.length = cpu_to_le32 ( used ); - - /* Populate subtable */ - memcpy ( &ib_sbft->sgid, &ibdev->gid, sizeof ( ib_sbft->sgid ) ); - memcpy ( &ib_sbft->dgid, &ib_srp->dgid, sizeof ( ib_sbft->dgid ) ); - memcpy ( &ib_sbft->service_id, &ib_srp->service_id, - sizeof ( ib_sbft->service_id ) ); - ib_sbft->pkey = cpu_to_le16 ( ibdev->pkey ); - - return 0; -} - -/** IB SRP CMRC interface operations */ -static struct interface_operation ib_srp_cmrc_op[] = { - INTF_OP ( intf_close, struct ib_srp_device *, ib_srp_close ), -}; - -/** IB SRP CMRC interface descriptor */ -static struct interface_descriptor ib_srp_cmrc_desc = - INTF_DESC_PASSTHRU ( struct ib_srp_device, cmrc, ib_srp_cmrc_op, srp ); - -/** IB SRP SRP interface operations */ -static struct interface_operation ib_srp_srp_op[] = { - INTF_OP ( acpi_describe, struct ib_srp_device *, ib_srp_describe ), - INTF_OP ( intf_close, struct ib_srp_device *, ib_srp_close ), -}; - -/** IB SRP SRP interface descriptor */ -static struct interface_descriptor ib_srp_srp_desc = - INTF_DESC_PASSTHRU ( struct ib_srp_device, srp, ib_srp_srp_op, cmrc ); - -/** - * Open IB SRP device - * - * @v block Block control interface - * @v ibdev Infiniband device - * @v dgid Destination GID - * @v service_id Service ID - * @v initiator Initiator port ID - * @v target Target port ID - * @v lun SCSI LUN - * @ret rc Return status code - */ -static int ib_srp_open ( struct interface *block, struct ib_device *ibdev, - union ib_gid *dgid, union ib_guid *service_id, - union srp_port_id *initiator, - union srp_port_id *target, struct scsi_lun *lun ) { - struct ib_srp_device *ib_srp; - int rc; - - /* Allocate and initialise structure */ - ib_srp = zalloc ( sizeof ( *ib_srp ) ); - if ( ! ib_srp ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &ib_srp->refcnt, ib_srp_free ); - intf_init ( &ib_srp->srp, &ib_srp_srp_desc, &ib_srp->refcnt ); - intf_init ( &ib_srp->cmrc, &ib_srp_cmrc_desc, &ib_srp->refcnt ); - ib_srp->ibdev = ibdev_get ( ibdev ); - DBGC ( ib_srp, "IBSRP %p for " IB_GID_FMT " " IB_GUID_FMT "\n", - ib_srp, IB_GID_ARGS ( dgid ), IB_GUID_ARGS ( service_id ) ); - - /* Preserve parameters required for boot firmware table */ - memcpy ( &ib_srp->dgid, dgid, sizeof ( ib_srp->dgid ) ); - memcpy ( &ib_srp->service_id, service_id, - sizeof ( ib_srp->service_id ) ); - - /* Open CMRC socket */ - if ( ( rc = ib_cmrc_open ( &ib_srp->cmrc, ibdev, dgid, - service_id ) ) != 0 ) { - DBGC ( ib_srp, "IBSRP %p could not open CMRC socket: %s\n", - ib_srp, strerror ( rc ) ); - goto err_cmrc_open; - } - - /* Attach SRP device to parent interface */ - if ( ( rc = srp_open ( block, &ib_srp->srp, initiator, target, - ibdev->rdma_key, lun ) ) != 0 ) { - DBGC ( ib_srp, "IBSRP %p could not create SRP device: %s\n", - ib_srp, strerror ( rc ) ); - goto err_srp_open; - } - - /* Mortalise self and return */ - ref_put ( &ib_srp->refcnt ); - return 0; - - err_srp_open: - err_cmrc_open: - ib_srp_close ( ib_srp, rc ); - ref_put ( &ib_srp->refcnt ); - err_zalloc: - return rc; -} - -/****************************************************************************** - * - * IB SRP URIs - * - ****************************************************************************** - */ - -/** IB SRP parse flags */ -enum ib_srp_parse_flags { - IB_SRP_PARSE_REQUIRED = 0x0000, - IB_SRP_PARSE_OPTIONAL = 0x8000, - IB_SRP_PARSE_FLAG_MASK = 0xf000, -}; - -/** IB SRP root path parameters */ -struct ib_srp_root_path { - /** Source GID */ - union ib_gid sgid; - /** Initiator port ID */ - union ib_srp_initiator_port_id initiator; - /** Destination GID */ - union ib_gid dgid; - /** Partition key */ - uint16_t pkey; - /** Service ID */ - union ib_guid service_id; - /** SCSI LUN */ - struct scsi_lun lun; - /** Target port ID */ - union ib_srp_target_port_id target; -}; - -/** - * Parse IB SRP root path byte-string value - * - * @v rp_comp Root path component string - * @v default_value Default value to use if component string is empty - * @ret value Value - */ -static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes, - unsigned int size_flags ) { - size_t size = ( size_flags & ~IB_SRP_PARSE_FLAG_MASK ); - size_t rp_comp_len = strlen ( rp_comp ); - int decoded_size; - - /* Allow optional components to be empty */ - if ( ( rp_comp_len == 0 ) && - ( size_flags & IB_SRP_PARSE_OPTIONAL ) ) - return 0; - - /* Check string length */ - if ( rp_comp_len != ( 2 * size ) ) - return -EINVAL_BYTE_STRING_LEN; - - /* Parse byte string */ - decoded_size = base16_decode ( rp_comp, bytes, size ); - if ( decoded_size < 0 ) - return decoded_size; - - return 0; -} - -/** - * Parse IB SRP root path integer value - * - * @v rp_comp Root path component string - * @v default_value Default value to use if component string is empty - * @ret value Value - */ -static int ib_srp_parse_integer ( const char *rp_comp, int default_value ) { - int value; - char *end; - - value = strtoul ( rp_comp, &end, 16 ); - if ( *end ) - return -EINVAL_INTEGER; - - if ( end == rp_comp ) - return default_value; - - return value; -} - -/** - * Parse IB SRP root path source GID - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_sgid ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - struct ib_device *ibdev; - - /* Default to the GID of the last opened Infiniband device */ - if ( ( ibdev = last_opened_ibdev() ) != NULL ) - memcpy ( &rp->sgid, &ibdev->gid, sizeof ( rp->sgid ) ); - - return ib_srp_parse_byte_string ( rp_comp, rp->sgid.bytes, - ( sizeof ( rp->sgid ) | - IB_SRP_PARSE_OPTIONAL ) ); -} - -/** - * Parse IB SRP root path initiator identifier extension - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_initiator_id_ext ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - union ib_srp_initiator_port_id *port_id = &rp->initiator; - - return ib_srp_parse_byte_string ( rp_comp, port_id->ib.id_ext.bytes, - ( sizeof ( port_id->ib.id_ext ) | - IB_SRP_PARSE_OPTIONAL ) ); -} - -/** - * Parse IB SRP root path initiator HCA GUID - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_initiator_hca_guid ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - union ib_srp_initiator_port_id *port_id = &rp->initiator; - - /* Default to the GUID portion of the source GID */ - memcpy ( &port_id->ib.hca_guid, &rp->sgid.s.guid, - sizeof ( port_id->ib.hca_guid ) ); - - return ib_srp_parse_byte_string ( rp_comp, port_id->ib.hca_guid.bytes, - ( sizeof ( port_id->ib.hca_guid ) | - IB_SRP_PARSE_OPTIONAL ) ); -} - -/** - * Parse IB SRP root path destination GID - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_dgid ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - return ib_srp_parse_byte_string ( rp_comp, rp->dgid.bytes, - ( sizeof ( rp->dgid ) | - IB_SRP_PARSE_REQUIRED ) ); -} - -/** - * Parse IB SRP root path partition key - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_pkey ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - int pkey; - - if ( ( pkey = ib_srp_parse_integer ( rp_comp, IB_PKEY_DEFAULT ) ) < 0 ) - return pkey; - rp->pkey = pkey; - return 0; -} - -/** - * Parse IB SRP root path service ID - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_service_id ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - return ib_srp_parse_byte_string ( rp_comp, rp->service_id.bytes, - ( sizeof ( rp->service_id ) | - IB_SRP_PARSE_REQUIRED ) ); -} - -/** - * Parse IB SRP root path LUN - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_lun ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - return scsi_parse_lun ( rp_comp, &rp->lun ); -} - -/** - * Parse IB SRP root path target identifier extension - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_target_id_ext ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - union ib_srp_target_port_id *port_id = &rp->target; - - return ib_srp_parse_byte_string ( rp_comp, port_id->ib.id_ext.bytes, - ( sizeof ( port_id->ib.id_ext ) | - IB_SRP_PARSE_REQUIRED ) ); -} - -/** - * Parse IB SRP root path target I/O controller GUID - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_target_ioc_guid ( const char *rp_comp, - struct ib_srp_root_path *rp ) { - union ib_srp_target_port_id *port_id = &rp->target; - - return ib_srp_parse_byte_string ( rp_comp, port_id->ib.ioc_guid.bytes, - ( sizeof ( port_id->ib.ioc_guid ) | - IB_SRP_PARSE_REQUIRED ) ); -} - -/** IB SRP root path component parser */ -struct ib_srp_root_path_parser { - /** - * Parse IB SRP root path component - * - * @v rp_comp Root path component string - * @v rp IB SRP root path - * @ret rc Return status code - */ - int ( * parse ) ( const char *rp_comp, struct ib_srp_root_path *rp ); -}; - -/** IB SRP root path components */ -static struct ib_srp_root_path_parser ib_srp_rp_parser[] = { - { ib_srp_parse_sgid }, - { ib_srp_parse_initiator_id_ext }, - { ib_srp_parse_initiator_hca_guid }, - { ib_srp_parse_dgid }, - { ib_srp_parse_pkey }, - { ib_srp_parse_service_id }, - { ib_srp_parse_lun }, - { ib_srp_parse_target_id_ext }, - { ib_srp_parse_target_ioc_guid }, -}; - -/** Number of IB SRP root path components */ -#define IB_SRP_NUM_RP_COMPONENTS \ - ( sizeof ( ib_srp_rp_parser ) / sizeof ( ib_srp_rp_parser[0] ) ) - -/** - * Parse IB SRP root path - * - * @v rp_string Root path string - * @v rp IB SRP root path - * @ret rc Return status code - */ -static int ib_srp_parse_root_path ( const char *rp_string, - struct ib_srp_root_path *rp ) { - struct ib_srp_root_path_parser *parser; - char rp_string_copy[ strlen ( rp_string ) + 1 ]; - char *rp_comp[IB_SRP_NUM_RP_COMPONENTS]; - char *rp_string_tmp = rp_string_copy; - unsigned int i = 0; - int rc; - - /* Split root path into component parts */ - strcpy ( rp_string_copy, rp_string ); - while ( 1 ) { - rp_comp[i++] = rp_string_tmp; - if ( i == IB_SRP_NUM_RP_COMPONENTS ) - break; - for ( ; *rp_string_tmp != ':' ; rp_string_tmp++ ) { - if ( ! *rp_string_tmp ) { - DBG ( "IBSRP root path \"%s\" too short\n", - rp_string ); - return -EINVAL_RP_TOO_SHORT; - } - } - *(rp_string_tmp++) = '\0'; - } - - /* Parse root path components */ - for ( i = 0 ; i < IB_SRP_NUM_RP_COMPONENTS ; i++ ) { - parser = &ib_srp_rp_parser[i]; - if ( ( rc = parser->parse ( rp_comp[i], rp ) ) != 0 ) { - DBG ( "IBSRP could not parse \"%s\" in root path " - "\"%s\": %s\n", rp_comp[i], rp_string, - strerror ( rc ) ); - return rc; - } - } - - return 0; -} - -/** - * Open IB SRP URI - * - * @v parent Parent interface - * @v uri URI - * @ret rc Return status code - */ -static int ib_srp_open_uri ( struct interface *parent, struct uri *uri ) { - struct ib_srp_root_path rp; - struct ib_device *ibdev; - int rc; - - /* Parse URI */ - if ( ! uri->opaque ) - return -EINVAL; - memset ( &rp, 0, sizeof ( rp ) ); - if ( ( rc = ib_srp_parse_root_path ( uri->opaque, &rp ) ) != 0 ) - return rc; - - /* Identify Infiniband device */ - ibdev = find_ibdev ( &rp.sgid ); - if ( ! ibdev ) { - DBG ( "IBSRP could not identify Infiniband device\n" ); - return -ENODEV; - } - - /* Open IB SRP device */ - if ( ( rc = ib_srp_open ( parent, ibdev, &rp.dgid, &rp.service_id, - &rp.initiator.srp, &rp.target.srp, - &rp.lun ) ) != 0 ) - return rc; - - return 0; -} - -/** IB SRP URI opener */ -struct uri_opener ib_srp_uri_opener __uri_opener = { - .scheme = "ib_srp", - .open = ib_srp_open_uri, -}; diff --git a/qemu/roms/ipxe/src/net/iobpad.c b/qemu/roms/ipxe/src/net/iobpad.c deleted file mode 100644 index 936b4bde4..000000000 --- a/qemu/roms/ipxe/src/net/iobpad.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * I/O buffer padding - * - */ - -#include <string.h> -#include <ipxe/iobuf.h> - -/** - * Pad I/O buffer - * - * @v iobuf I/O buffer - * @v min_len Minimum length - * - * This function pads and aligns I/O buffers, for devices that - * aren't capable of padding in hardware, or that require specific - * alignment in TX buffers. The packet data will end up aligned to a - * multiple of @c IOB_ALIGN. - * - * @c min_len must not exceed @v IOB_ZLEN. - */ -void iob_pad ( struct io_buffer *iobuf, size_t min_len ) { - void *data; - size_t len; - size_t headroom; - signed int pad_len; - - assert ( min_len <= IOB_ZLEN ); - - /* Move packet data to start of I/O buffer. This will both - * align the data (since I/O buffers are aligned to - * IOB_ALIGN) and give us sufficient space for the - * zero-padding - */ - data = iobuf->data; - len = iob_len ( iobuf ); - headroom = iob_headroom ( iobuf ); - iob_push ( iobuf, headroom ); - memmove ( iobuf->data, data, len ); - iob_unput ( iobuf, headroom ); - - /* Pad to minimum packet length */ - pad_len = ( min_len - iob_len ( iobuf ) ); - if ( pad_len > 0 ) - memset ( iob_put ( iobuf, pad_len ), 0, pad_len ); -} diff --git a/qemu/roms/ipxe/src/net/ipv4.c b/qemu/roms/ipxe/src/net/ipv4.c deleted file mode 100644 index a54784049..000000000 --- a/qemu/roms/ipxe/src/net/ipv4.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - * 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 miniroute Routing table entry, or NULL - */ -static struct ipv4_miniroute * __malloc -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 NULL; - } - - /* 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 miniroute; -} - -/** - * 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 ); - 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 ), - .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_IP, 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_IP, netmask ) = { - .name = "netmask", - .description = "Subnet mask", - .tag = DHCP_SUBNET_MASK, - .type = &setting_type_ipv4, -}; - -/** Default gateway setting */ -const struct setting gateway_setting __setting ( SETTING_IP, gateway ) = { - .name = "gateway", - .description = "Default gateway", - .tag = DHCP_ROUTERS, - .type = &setting_type_ipv4, -}; - -/** - * 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; - struct net_device *netdev; - struct settings *settings; - struct in_addr address = { 0 }; - struct in_addr netmask = { 0 }; - struct in_addr gateway = { 0 }; - - /* 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 */ - for_each_netdev ( netdev ) { - 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 ); - /* Configure route */ - miniroute = add_ipv4_miniroute ( netdev, address, - netmask, gateway ); - if ( ! miniroute ) - return -ENOMEM; - } - - 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 ); diff --git a/qemu/roms/ipxe/src/net/ipv6.c b/qemu/roms/ipxe/src/net/ipv6.c deleted file mode 100644 index a75e72ddb..000000000 --- a/qemu/roms/ipxe/src/net/ipv6.c +++ /dev/null @@ -1,1125 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/iobuf.h> -#include <ipxe/tcpip.h> -#include <ipxe/if_ether.h> -#include <ipxe/crc32.h> -#include <ipxe/fragment.h> -#include <ipxe/ipstat.h> -#include <ipxe/ndp.h> -#include <ipxe/ipv6.h> - -/** @file - * - * IPv6 protocol - * - */ - -/* Disambiguate the various error causes */ -#define EINVAL_LEN __einfo_error ( EINFO_EINVAL_LEN ) -#define EINFO_EINVAL_LEN \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid length" ) -#define ENOTSUP_VER __einfo_error ( EINFO_ENOTSUP_VER ) -#define EINFO_ENOTSUP_VER \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported version" ) -#define ENOTSUP_HDR __einfo_error ( EINFO_ENOTSUP_HDR ) -#define EINFO_ENOTSUP_HDR \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported header type" ) -#define ENOTSUP_OPT __einfo_error ( EINFO_ENOTSUP_OPT ) -#define EINFO_ENOTSUP_OPT \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x03, "Unsupported option" ) - -/** List of IPv6 miniroutes */ -struct list_head ipv6_miniroutes = LIST_HEAD_INIT ( ipv6_miniroutes ); - -/** IPv6 statistics */ -static struct ip_statistics ipv6_stats; - -/** IPv6 statistics family */ -struct ip_statistics_family -ipv6_statistics_family __ip_statistics_family ( IP_STATISTICS_IPV6 ) = { - .version = 6, - .stats = &ipv6_stats, -}; - -/** - * Determine debugging colour for IPv6 debug messages - * - * @v in IPv6 address - * @ret col Debugging colour (for DBGC()) - */ -static uint32_t ipv6col ( struct in6_addr *in ) { - return crc32_le ( 0, in, sizeof ( *in ) ); -} - -/** - * Dump IPv6 routing table entry - * - * @v miniroute Routing table entry - */ -static inline __attribute__ (( always_inline )) void -ipv6_dump_miniroute ( struct ipv6_miniroute *miniroute ) { - struct net_device *netdev = miniroute->netdev; - - DBGC ( netdev, "IPv6 %s has %s %s/%d", netdev->name, - ( ( miniroute->flags & IPV6_HAS_ADDRESS ) ? - "address" : "prefix" ), - inet6_ntoa ( &miniroute->address ), miniroute->prefix_len ); - if ( miniroute->flags & IPV6_HAS_ROUTER ) - DBGC ( netdev, " router %s", inet6_ntoa ( &miniroute->router )); - DBGC ( netdev, "\n" ); -} - -/** - * Check if network device has a specific IPv6 address - * - * @v netdev Network device - * @v addr IPv6 address - * @ret has_addr Network device has this IPv6 address - */ -int ipv6_has_addr ( struct net_device *netdev, struct in6_addr *addr ) { - struct ipv6_miniroute *miniroute; - - list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { - if ( ( miniroute->netdev == netdev ) && - ( miniroute->flags & IPV6_HAS_ADDRESS ) && - ( memcmp ( &miniroute->address, addr, - sizeof ( miniroute->address ) ) == 0 ) ) { - /* Found matching address */ - return 1; - } - } - return 0; -} - -/** - * Check if IPv6 address is within a routing table entry's local network - * - * @v miniroute Routing table entry - * @v address IPv6 address - * @ret is_on_link Address is within this entry's local network - */ -static int ipv6_is_on_link ( struct ipv6_miniroute *miniroute, - struct in6_addr *address ) { - unsigned int i; - - for ( i = 0 ; i < ( sizeof ( address->s6_addr32 ) / - sizeof ( address->s6_addr32[0] ) ) ; i++ ) { - if ( (( address->s6_addr32[i] ^ miniroute->address.s6_addr32[i]) - & miniroute->prefix_mask.s6_addr32[i] ) != 0 ) - return 0; - } - return 1; -} - -/** - * Find IPv6 routing table entry for a given address - * - * @v netdev Network device - * @v address IPv6 address - * @ret miniroute Routing table entry, or NULL if not found - */ -static struct ipv6_miniroute * ipv6_miniroute ( struct net_device *netdev, - struct in6_addr *address ) { - struct ipv6_miniroute *miniroute; - - list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { - if ( ( miniroute->netdev == netdev ) && - ipv6_is_on_link ( miniroute, address ) ) { - return miniroute; - } - } - return NULL; -} - -/** - * Add IPv6 routing table entry - * - * @v netdev Network device - * @v address IPv6 address (or prefix) - * @v prefix_len Prefix length - * @v flags Flags - * @ret miniroute Routing table entry, or NULL on failure - */ -static struct ipv6_miniroute * ipv6_add_miniroute ( struct net_device *netdev, - struct in6_addr *address, - unsigned int prefix_len, - unsigned int flags ) { - struct ipv6_miniroute *miniroute; - uint8_t *prefix_mask; - - /* Create routing table entry */ - miniroute = zalloc ( sizeof ( *miniroute ) ); - if ( ! miniroute ) - return NULL; - miniroute->netdev = netdev_get ( netdev ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->prefix_len = prefix_len; - assert ( prefix_len <= ( 8 * sizeof ( miniroute->prefix_mask ) ) ); - for ( prefix_mask = miniroute->prefix_mask.s6_addr ; prefix_len >= 8 ; - prefix_mask++, prefix_len -= 8 ) { - *prefix_mask = 0xff; - } - if ( prefix_len ) - *prefix_mask <<= ( 8 - prefix_len ); - miniroute->flags = flags; - list_add ( &miniroute->list, &ipv6_miniroutes ); - ipv6_dump_miniroute ( miniroute ); - - return miniroute; -} - -/** - * Define IPv6 on-link prefix - * - * @v netdev Network device - * @v prefix IPv6 address prefix - * @v prefix_len Prefix length - * @v router Router address (or NULL) - * @ret rc Return status code - */ -int ipv6_set_prefix ( struct net_device *netdev, struct in6_addr *prefix, - unsigned int prefix_len, struct in6_addr *router ) { - struct ipv6_miniroute *miniroute; - int changed; - - /* Find or create routing table entry */ - miniroute = ipv6_miniroute ( netdev, prefix ); - if ( ! miniroute ) - miniroute = ipv6_add_miniroute ( netdev, prefix, prefix_len, 0); - if ( ! miniroute ) - return -ENOMEM; - - /* Record router and add to start or end of list as appropriate */ - list_del ( &miniroute->list ); - if ( router ) { - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ROUTER ) ) || - ( memcmp ( &miniroute->router, router, - sizeof ( miniroute->router ) ) != 0 ) ); - miniroute->flags |= IPV6_HAS_ROUTER; - memcpy ( &miniroute->router, router, - sizeof ( miniroute->router ) ); - list_add_tail ( &miniroute->list, &ipv6_miniroutes ); - } else { - changed = ( miniroute->flags & IPV6_HAS_ROUTER ); - miniroute->flags &= ~IPV6_HAS_ROUTER; - list_add ( &miniroute->list, &ipv6_miniroutes ); - } - if ( changed ) - ipv6_dump_miniroute ( miniroute ); - - return 0; -} - -/** - * Add IPv6 on-link address - * - * @v netdev Network device - * @v address IPv6 address - * @ret rc Return status code - * - * An on-link prefix for the address must already exist. - */ -int ipv6_set_address ( struct net_device *netdev, struct in6_addr *address ) { - struct ipv6_miniroute *miniroute; - int changed; - - /* Find routing table entry */ - miniroute = ipv6_miniroute ( netdev, address ); - if ( ! miniroute ) - return -EADDRNOTAVAIL; - - /* Record address */ - changed = ( ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) || - ( memcmp ( &miniroute->address, address, - sizeof ( miniroute->address ) ) != 0 ) ); - memcpy ( &miniroute->address, address, sizeof ( miniroute->address ) ); - miniroute->flags |= IPV6_HAS_ADDRESS; - if ( changed ) - ipv6_dump_miniroute ( miniroute ); - - return 0; -} - -/** - * Perform IPv6 routing - * - * @v scope_id Destination address scope ID (for link-local addresses) - * @v dest Final destination address - * @ret dest Next hop destination address - * @ret miniroute Routing table entry to use, or NULL if no route - */ -static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, - struct in6_addr **dest ) { - struct ipv6_miniroute *miniroute; - - /* Find first usable route in routing table */ - list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { - - /* Skip closed network devices */ - if ( ! netdev_is_open ( miniroute->netdev ) ) - continue; - - /* Skip routing table entries with no usable source address */ - if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) - continue; - - if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) { - - /* 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 ( ipv6_is_on_link ( miniroute, *dest ) ) - return miniroute; - - /* If destination is an off-link global - * address, and we have a default gateway, - * then use this route. - */ - if ( miniroute->flags & IPV6_HAS_ROUTER ) { - *dest = &miniroute->router; - 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 * ipv6_netdev ( struct sockaddr_tcpip *st_dest ) { - struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest ); - struct in6_addr *dest = &sin6_dest->sin6_addr; - struct ipv6_miniroute *miniroute; - - /* Find routing table entry */ - miniroute = ipv6_route ( sin6_dest->sin6_scope_id, &dest ); - if ( ! miniroute ) - return NULL; - - return miniroute->netdev; -} - -/** - * Check that received options can be safely ignored - * - * @v iphdr IPv6 header - * @v options Options extension header - * @v len Maximum length of header - * @ret rc Return status code - */ -static int ipv6_check_options ( struct ipv6_header *iphdr, - struct ipv6_options_header *options, - size_t len ) { - struct ipv6_option *option = options->options; - struct ipv6_option *end = ( ( ( void * ) options ) + len ); - - while ( option < end ) { - if ( ! IPV6_CAN_IGNORE_OPT ( option->type ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 unrecognised " - "option type %#02x:\n", option->type ); - DBGC_HDA ( ipv6col ( &iphdr->src ), 0, - options, len ); - return -ENOTSUP_OPT; - } - if ( option->type == IPV6_OPT_PAD1 ) { - option = ( ( ( void * ) option ) + 1 ); - } else { - option = ( ( ( void * ) option->value ) + option->len ); - } - } - return 0; -} - -/** - * Check if 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 ipv6_is_fragment ( struct fragment *fragment, - struct io_buffer *iobuf, size_t hdrlen ) { - struct ipv6_header *frag_iphdr = fragment->iobuf->data; - struct ipv6_fragment_header *frag_fhdr = - ( fragment->iobuf->data + fragment->hdrlen - - sizeof ( *frag_fhdr ) ); - struct ipv6_header *iphdr = iobuf->data; - struct ipv6_fragment_header *fhdr = - ( iobuf->data + hdrlen - sizeof ( *fhdr ) ); - - return ( ( memcmp ( &iphdr->src, &frag_iphdr->src, - sizeof ( iphdr->src ) ) == 0 ) && - ( fhdr->ident == frag_fhdr->ident ) ); -} - -/** - * Get fragment offset - * - * @v iobuf I/O buffer - * @v hdrlen Length of non-fragmentable potion of I/O buffer - * @ret offset Offset - */ -static size_t ipv6_fragment_offset ( struct io_buffer *iobuf, size_t hdrlen ) { - struct ipv6_fragment_header *fhdr = - ( iobuf->data + hdrlen - sizeof ( *fhdr ) ); - - return ( ntohs ( fhdr->offset_more ) & IPV6_MASK_OFFSET ); -} - -/** - * 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 ipv6_more_fragments ( struct io_buffer *iobuf, size_t hdrlen ) { - struct ipv6_fragment_header *fhdr = - ( iobuf->data + hdrlen - sizeof ( *fhdr ) ); - - return ( fhdr->offset_more & htons ( IPV6_MASK_MOREFRAGS ) ); -} - -/** Fragment reassembler */ -static struct fragment_reassembler ipv6_reassembler = { - .list = LIST_HEAD_INIT ( ipv6_reassembler.list ), - .is_fragment = ipv6_is_fragment, - .fragment_offset = ipv6_fragment_offset, - .more_fragments = ipv6_more_fragments, - .stats = &ipv6_stats, -}; - -/** - * Calculate IPv6 pseudo-header checksum - * - * @v iphdr IPv6 header - * @v len Payload length - * @v next_header Next header type - * @v csum Existing checksum - * @ret csum Updated checksum - */ -static uint16_t ipv6_pshdr_chksum ( struct ipv6_header *iphdr, size_t len, - int next_header, uint16_t csum ) { - struct ipv6_pseudo_header pshdr; - - /* Build pseudo-header */ - memcpy ( &pshdr.src, &iphdr->src, sizeof ( pshdr.src ) ); - memcpy ( &pshdr.dest, &iphdr->dest, sizeof ( pshdr.dest ) ); - pshdr.len = htonl ( len ); - memset ( pshdr.zero, 0, sizeof ( pshdr.zero ) ); - pshdr.next_header = next_header; - - /* Update the checksum value */ - return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) ); -} - -/** - * Transmit IPv6 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 - * IPv6 header - */ -static int ipv6_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 sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src ); - struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest ); - struct ipv6_miniroute *miniroute; - struct ipv6_header *iphdr; - struct in6_addr *src = NULL; - struct in6_addr *next_hop; - uint8_t ll_dest_buf[MAX_LL_ADDR_LEN]; - const void *ll_dest; - size_t len; - int rc; - - /* Update statistics */ - ipv6_stats.out_requests++; - - /* Fill up the IPv6 header, except source address */ - len = iob_len ( iobuf ); - iphdr = iob_push ( iobuf, sizeof ( *iphdr ) ); - memset ( iphdr, 0, sizeof ( *iphdr ) ); - iphdr->ver_tc_label = htonl ( IPV6_VER ); - iphdr->len = htons ( len ); - iphdr->next_header = tcpip_protocol->tcpip_proto; - iphdr->hop_limit = IPV6_HOP_LIMIT; - memcpy ( &iphdr->dest, &sin6_dest->sin6_addr, sizeof ( iphdr->dest ) ); - - /* Use routing table to identify next hop and transmitting netdev */ - next_hop = &iphdr->dest; - if ( ( miniroute = ipv6_route ( sin6_dest->sin6_scope_id, - &next_hop ) ) != NULL ) { - src = &miniroute->address; - netdev = miniroute->netdev; - } - if ( ! netdev ) { - DBGC ( ipv6col ( &iphdr->dest ), "IPv6 has no route to %s\n", - inet6_ntoa ( &iphdr->dest ) ); - ipv6_stats.out_no_routes++; - rc = -ENETUNREACH; - goto err; - } - if ( sin6_src && ! IN6_IS_ADDR_UNSPECIFIED ( &sin6_src->sin6_addr ) ) - src = &sin6_src->sin6_addr; - if ( src ) - memcpy ( &iphdr->src, src, sizeof ( iphdr->src ) ); - - /* Fix up checksums */ - if ( trans_csum ) { - *trans_csum = ipv6_pshdr_chksum ( iphdr, len, - tcpip_protocol->tcpip_proto, - *trans_csum ); - } - - /* Print IPv6 header for debugging */ - DBGC2 ( ipv6col ( &iphdr->dest ), "IPv6 TX %s->", - inet6_ntoa ( &iphdr->src ) ); - DBGC2 ( ipv6col ( &iphdr->dest ), "%s len %zd next %d\n", - inet6_ntoa ( &iphdr->dest ), len, iphdr->next_header ); - - /* Calculate link-layer destination address, if possible */ - if ( IN6_IS_ADDR_MULTICAST ( next_hop ) ) { - /* Multicast address */ - ipv6_stats.out_mcast_pkts++; - if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET6, next_hop, - ll_dest_buf ) ) !=0){ - DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not hash " - "multicast %s: %s\n", inet6_ntoa ( next_hop ), - strerror ( rc ) ); - goto err; - } - ll_dest = ll_dest_buf; - } else { - /* Unicast address */ - ll_dest = NULL; - } - - /* Update statistics */ - ipv6_stats.out_transmits++; - ipv6_stats.out_octets += iob_len ( iobuf ); - - /* Hand off to link layer (via NDP if applicable) */ - if ( ll_dest ) { - if ( ( rc = net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest, - netdev->ll_addr ) ) != 0 ) { - DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not " - "transmit packet via %s: %s\n", - netdev->name, strerror ( rc ) ); - return rc; - } - } else { - if ( ( rc = ndp_tx ( iobuf, netdev, next_hop, &iphdr->src, - netdev->ll_addr ) ) != 0 ) { - DBGC ( ipv6col ( &iphdr->dest ), "IPv6 could not " - "transmit packet via %s: %s\n", - netdev->name, strerror ( rc ) ); - return rc; - } - } - - return 0; - - err: - free_iob ( iobuf ); - return rc; -} - -/** - * Process incoming IPv6 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 IPv6 network datagram. It processes the - * headers and sends it to the transport layer. - */ -static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_dest __unused, - const void *ll_source __unused, - unsigned int flags __unused ) { - struct ipv6_header *iphdr = iobuf->data; - union ipv6_extension_header *ext; - union { - struct sockaddr_in6 sin6; - struct sockaddr_tcpip st; - } src, dest; - uint16_t pshdr_csum; - size_t len; - size_t hdrlen; - size_t extlen; - int this_header; - int next_header; - int rc; - - /* Update statistics */ - ipv6_stats.in_receives++; - ipv6_stats.in_octets += iob_len ( iobuf ); - if ( flags & LL_BROADCAST ) { - ipv6_stats.in_bcast_pkts++; - } else if ( flags & LL_MULTICAST ) { - ipv6_stats.in_mcast_pkts++; - } - - /* Sanity check the IPv6 header */ - if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 packet too short at %zd " - "bytes (min %zd bytes)\n", iob_len ( iobuf ), - sizeof ( *iphdr ) ); - rc = -EINVAL_LEN; - goto err_header; - } - if ( ( iphdr->ver_tc_label & htonl ( IPV6_MASK_VER ) ) != - htonl ( IPV6_VER ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 version %#08x not " - "supported\n", ntohl ( iphdr->ver_tc_label ) ); - rc = -ENOTSUP_VER; - goto err_header; - } - - /* Truncate packet to specified length */ - len = ntohs ( iphdr->len ); - if ( len > iob_len ( iobuf ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 length too long at %zd " - "bytes (packet is %zd bytes)\n", len, iob_len ( iobuf )); - ipv6_stats.in_truncated_pkts++; - rc = -EINVAL_LEN; - goto err_other; - } - iob_unput ( iobuf, ( iob_len ( iobuf ) - len - sizeof ( *iphdr ) ) ); - hdrlen = sizeof ( *iphdr ); - - /* Print IPv6 header for debugging */ - DBGC2 ( ipv6col ( &iphdr->src ), "IPv6 RX %s<-", - inet6_ntoa ( &iphdr->dest ) ); - DBGC2 ( ipv6col ( &iphdr->src ), "%s len %zd next %d\n", - inet6_ntoa ( &iphdr->src ), len, iphdr->next_header ); - - /* Discard unicast packets not destined for us */ - if ( ( ! ( flags & LL_MULTICAST ) ) && - ( ! ipv6_has_addr ( netdev, &iphdr->dest ) ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 discarding non-local " - "unicast packet for %s\n", inet6_ntoa ( &iphdr->dest ) ); - ipv6_stats.in_addr_errors++; - rc = -EPIPE; - goto err_other; - } - - /* Process any extension headers */ - next_header = iphdr->next_header; - while ( 1 ) { - - /* Extract extension header */ - this_header = next_header; - ext = ( iobuf->data + hdrlen ); - extlen = sizeof ( ext->pad ); - if ( iob_len ( iobuf ) < ( hdrlen + extlen ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 too short for " - "extension header type %d at %zd bytes (min " - "%zd bytes)\n", this_header, - ( iob_len ( iobuf ) - hdrlen ), extlen ); - rc = -EINVAL_LEN; - goto err_header; - } - - /* Determine size of extension header (if applicable) */ - if ( ( this_header == IPV6_HOPBYHOP ) || - ( this_header == IPV6_DESTINATION ) || - ( this_header == IPV6_ROUTING ) ) { - /* Length field is present */ - extlen += ext->common.len; - } else if ( this_header == IPV6_FRAGMENT ) { - /* Length field is reserved and ignored (RFC2460) */ - } else { - /* Not an extension header; assume rest is payload */ - break; - } - if ( iob_len ( iobuf ) < ( hdrlen + extlen ) ) { - DBGC ( ipv6col ( &iphdr->src ), "IPv6 too short for " - "extension header type %d at %zd bytes (min " - "%zd bytes)\n", this_header, - ( iob_len ( iobuf ) - hdrlen ), extlen ); - rc = -EINVAL_LEN; - goto err_header; - } - hdrlen += extlen; - next_header = ext->common.next_header; - DBGC2 ( ipv6col ( &iphdr->src ), "IPv6 RX %s<-", - inet6_ntoa ( &iphdr->dest ) ); - DBGC2 ( ipv6col ( &iphdr->src ), "%s ext type %d len %zd next " - "%d\n", inet6_ntoa ( &iphdr->src ), this_header, - extlen, next_header ); - - /* Process this extension header */ - if ( ( this_header == IPV6_HOPBYHOP ) || - ( this_header == IPV6_DESTINATION ) ) { - - /* Check that all options can be ignored */ - if ( ( rc = ipv6_check_options ( iphdr, &ext->options, - extlen ) ) != 0 ) - goto err_header; - - } else if ( this_header == IPV6_FRAGMENT ) { - - /* Reassemble fragments */ - iobuf = fragment_reassemble ( &ipv6_reassembler, iobuf, - &hdrlen ); - if ( ! iobuf ) - return 0; - iphdr = iobuf->data; - } - } - - /* Construct socket address, calculate pseudo-header checksum, - * and hand off to transport layer - */ - memset ( &src, 0, sizeof ( src ) ); - src.sin6.sin6_family = AF_INET6; - memcpy ( &src.sin6.sin6_addr, &iphdr->src, - sizeof ( src.sin6.sin6_addr ) ); - src.sin6.sin6_scope_id = netdev->index; - memset ( &dest, 0, sizeof ( dest ) ); - dest.sin6.sin6_family = AF_INET6; - memcpy ( &dest.sin6.sin6_addr, &iphdr->dest, - sizeof ( dest.sin6.sin6_addr ) ); - dest.sin6.sin6_scope_id = netdev->index; - iob_pull ( iobuf, hdrlen ); - pshdr_csum = ipv6_pshdr_chksum ( iphdr, iob_len ( iobuf ), - next_header, TCPIP_EMPTY_CSUM ); - if ( ( rc = tcpip_rx ( iobuf, netdev, next_header, &src.st, &dest.st, - pshdr_csum, &ipv6_stats ) ) != 0 ) { - DBGC ( ipv6col ( &src.sin6.sin6_addr ), "IPv6 received packet " - "rejected by stack: %s\n", strerror ( rc ) ); - return rc; - } - - return 0; - - err_header: - ipv6_stats.in_hdr_errors++; - err_other: - free_iob ( iobuf ); - return rc; -} - -/** - * Parse IPv6 address - * - * @v string IPv6 address string - * @ret in IPv6 address to fill in - * @ret rc Return status code - */ -int inet6_aton ( const char *string, struct in6_addr *in ) { - uint16_t *word = in->s6_addr16; - uint16_t *end = ( word + ( sizeof ( in->s6_addr16 ) / - sizeof ( in->s6_addr16[0] ) ) ); - uint16_t *pad = NULL; - const char *nptr = string; - char *endptr; - unsigned long value; - size_t pad_len; - size_t move_len; - - /* Parse string */ - while ( 1 ) { - - /* Parse current word */ - value = strtoul ( nptr, &endptr, 16 ); - if ( value > 0xffff ) { - DBG ( "IPv6 invalid word value %#lx in \"%s\"\n", - value, string ); - return -EINVAL; - } - *(word++) = htons ( value ); - - /* Parse separator */ - if ( ! *endptr ) - break; - if ( *endptr != ':' ) { - DBG ( "IPv6 invalid separator '%c' in \"%s\"\n", - *endptr, string ); - return -EINVAL; - } - if ( ( endptr == nptr ) && ( nptr != string ) ) { - if ( pad ) { - DBG ( "IPv6 invalid multiple \"::\" in " - "\"%s\"\n", string ); - return -EINVAL; - } - pad = word; - } - nptr = ( endptr + 1 ); - - /* Check for overrun */ - if ( word == end ) { - DBG ( "IPv6 too many words in \"%s\"\n", string ); - return -EINVAL; - } - } - - /* Insert padding if specified */ - if ( pad ) { - move_len = ( ( ( void * ) word ) - ( ( void * ) pad ) ); - pad_len = ( ( ( void * ) end ) - ( ( void * ) word ) ); - memmove ( ( ( ( void * ) pad ) + pad_len ), pad, move_len ); - memset ( pad, 0, pad_len ); - } else if ( word != end ) { - DBG ( "IPv6 underlength address \"%s\"\n", string ); - return -EINVAL; - } - - return 0; -} - -/** - * Convert IPv6 address to standard notation - * - * @v in IPv6 address - * @ret string IPv6 address string in canonical format - * - * RFC5952 defines the canonical format for IPv6 textual representation. - */ -char * inet6_ntoa ( const struct in6_addr *in ) { - static char buf[41]; /* ":xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx" */ - char *out = buf; - char *longest_start = NULL; - char *start = NULL; - int longest_len = 1; - int len = 0; - char *dest; - unsigned int i; - uint16_t value; - - /* Format address, keeping track of longest run of zeros */ - for ( i = 0 ; i < ( sizeof ( in->s6_addr16 ) / - sizeof ( in->s6_addr16[0] ) ) ; i++ ) { - value = ntohs ( in->s6_addr16[i] ); - if ( value == 0 ) { - if ( len++ == 0 ) - start = out; - if ( len > longest_len ) { - longest_start = start; - longest_len = len; - } - } else { - len = 0; - } - out += sprintf ( out, ":%x", value ); - } - - /* Abbreviate longest run of zeros, if applicable */ - if ( longest_start ) { - dest = strcpy ( ( longest_start + 1 ), - ( longest_start + ( 2 * longest_len ) ) ); - if ( dest[0] == '\0' ) - dest[1] = '\0'; - dest[0] = ':'; - } - return ( ( longest_start == buf ) ? buf : ( buf + 1 ) ); -} - -/** - * Transcribe IPv6 address - * - * @v net_addr IPv6 address - * @ret string IPv6 address in standard notation - * - */ -static const char * ipv6_ntoa ( const void *net_addr ) { - return inet6_ntoa ( net_addr ); -} - -/** - * Transcribe IPv6 socket address - * - * @v sa Socket address - * @ret string Socket address in standard notation - */ -static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { - static char buf[ 39 /* "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx" */ + - 1 /* "%" */ + NETDEV_NAME_LEN + 1 /* NUL */ ]; - struct sockaddr_in6 *sin6 = ( ( struct sockaddr_in6 * ) sa ); - struct in6_addr *in = &sin6->sin6_addr; - struct net_device *netdev; - const char *netdev_name; - - /* Identify network device, if applicable */ - if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) { - netdev = find_netdev_by_index ( sin6->sin6_scope_id ); - netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); - } else { - netdev_name = NULL; - } - - /* Format socket address */ - snprintf ( buf, sizeof ( buf ), "%s%s%s", inet6_ntoa ( in ), - ( netdev_name ? "%" : "" ), - ( netdev_name ? netdev_name : "" ) ); - return buf; -} - -/** - * Parse IPv6 socket address - * - * @v string Socket address string - * @v sa Socket address to fill in - * @ret rc Return status code - */ -static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { - struct sockaddr_in6 *sin6 = ( ( struct sockaddr_in6 * ) sa ); - struct in6_addr in; - struct net_device *netdev; - size_t len; - char *tmp; - char *in_string; - char *netdev_string; - int rc; - - /* Create modifiable copy of string */ - tmp = strdup ( string ); - if ( ! tmp ) { - rc = -ENOMEM; - goto err_alloc; - } - in_string = tmp; - - /* Strip surrounding "[...]", if present */ - len = strlen ( in_string ); - if ( ( in_string[0] == '[' ) && ( in_string[ len - 1 ] == ']' ) ) { - in_string[ len - 1 ] = '\0'; - in_string++; - } - - /* Split at network device name, if present */ - netdev_string = strchr ( in_string, '%' ); - if ( netdev_string ) - *(netdev_string++) = '\0'; - - /* Parse IPv6 address portion */ - if ( ( rc = inet6_aton ( in_string, &in ) ) != 0 ) - goto err_inet6_aton; - - /* Parse scope ID, if applicable */ - if ( netdev_string ) { - - /* Parse explicit network device name, if present */ - netdev = find_netdev ( netdev_string ); - if ( ! netdev ) { - rc = -ENODEV; - goto err_find_netdev; - } - sin6->sin6_scope_id = netdev->index; - - } else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) { - - /* If no network device is explicitly specified for a - * link-local or multicast address, default to using - * "netX" (if existent). - */ - netdev = last_opened_netdev(); - if ( netdev ) - sin6->sin6_scope_id = netdev->index; - } - - /* Copy IPv6 address portion to socket address */ - memcpy ( &sin6->sin6_addr, &in, sizeof ( sin6->sin6_addr ) ); - - err_find_netdev: - err_inet6_aton: - free ( tmp ); - err_alloc: - return rc; -} - -/** IPv6 protocol */ -struct net_protocol ipv6_protocol __net_protocol = { - .name = "IPv6", - .net_proto = htons ( ETH_P_IPV6 ), - .net_addr_len = sizeof ( struct in6_addr ), - .rx = ipv6_rx, - .ntoa = ipv6_ntoa, -}; - -/** IPv6 TCPIP net protocol */ -struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = { - .name = "IPv6", - .sa_family = AF_INET6, - .header_len = sizeof ( struct ipv6_header ), - .tx = ipv6_tx, - .netdev = ipv6_netdev, -}; - -/** IPv6 socket address converter */ -struct sockaddr_converter ipv6_sockaddr_converter __sockaddr_converter = { - .family = AF_INET6, - .ntoa = ipv6_sock_ntoa, - .aton = ipv6_sock_aton, -}; - -/** - * Parse IPv6 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_ipv6_setting ( const struct setting_type *type __unused, - const char *value, void *buf, size_t len ) { - struct in6_addr ipv6; - int rc; - - /* Parse IPv6 address */ - if ( ( rc = inet6_aton ( value, &ipv6 ) ) != 0 ) - return rc; - - /* Copy to buffer */ - if ( len > sizeof ( ipv6 ) ) - len = sizeof ( ipv6 ); - memcpy ( buf, &ipv6, len ); - - return ( sizeof ( ipv6 ) ); -} - -/** - * Format IPv6 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_ipv6_setting ( const struct setting_type *type __unused, - const void *raw, size_t raw_len, char *buf, - size_t len ) { - const struct in6_addr *ipv6 = raw; - - if ( raw_len < sizeof ( *ipv6 ) ) - return -EINVAL; - return snprintf ( buf, len, "%s", inet6_ntoa ( ipv6 ) ); -} - -/** - * Create IPv6 network device - * - * @v netdev Network device - * @ret rc Return status code - */ -static int ipv6_probe ( struct net_device *netdev ) { - struct ipv6_miniroute *miniroute; - struct in6_addr address; - int prefix_len; - int rc; - - /* Construct link-local address from EUI-64 as per RFC 2464 */ - memset ( &address, 0, sizeof ( address ) ); - prefix_len = ipv6_link_local ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "IPv6 %s could not construct link-local " - "address: %s\n", netdev->name, strerror ( rc ) ); - return rc; - } - - /* Create link-local address for this network device */ - miniroute = ipv6_add_miniroute ( netdev, &address, prefix_len, - IPV6_HAS_ADDRESS ); - if ( ! miniroute ) - return -ENOMEM; - - return 0; -} - -/** - * Destroy IPv6 network device - * - * @v netdev Network device - */ -static void ipv6_remove ( struct net_device *netdev ) { - struct ipv6_miniroute *miniroute; - struct ipv6_miniroute *tmp; - - /* Delete all miniroutes for this network device */ - list_for_each_entry_safe ( miniroute, tmp, &ipv6_miniroutes, list ) { - if ( miniroute->netdev == netdev ) { - netdev_put ( miniroute->netdev ); - list_del ( &miniroute->list ); - free ( miniroute ); - } - } -} - -/** IPv6 network device driver */ -struct net_driver ipv6_driver __net_driver = { - .name = "IPv6", - .probe = ipv6_probe, - .remove = ipv6_remove, -}; - -/* Drag in objects via ipv6_protocol */ -REQUIRING_SYMBOL ( ipv6_protocol ); - -/* Drag in ICMPv6 */ -REQUIRE_OBJECT ( icmpv6 ); - -/* Drag in NDP */ -REQUIRE_OBJECT ( ndp ); diff --git a/qemu/roms/ipxe/src/net/ndp.c b/qemu/roms/ipxe/src/net/ndp.c deleted file mode 100644 index e62f7d5cb..000000000 --- a/qemu/roms/ipxe/src/net/ndp.c +++ /dev/null @@ -1,1010 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/tcpip.h> -#include <ipxe/ipv6.h> -#include <ipxe/icmpv6.h> -#include <ipxe/neighbour.h> -#include <ipxe/dhcpv6.h> -#include <ipxe/ndp.h> - -/** @file - * - * IPv6 neighbour discovery protocol - * - */ - -static int -ipv6conf_rx_router_advertisement ( struct net_device *netdev, - struct ndp_router_advertisement_header *radv, - size_t len ); - -/** - * Transmit NDP packet with link-layer address option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v sin6_dest Destination socket address - * @v data NDP header - * @v len Size of NDP header - * @v option_type NDP option type - * @ret rc Return status code - */ -static int ndp_tx_ll_addr ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - struct sockaddr_in6 *sin6_dest, - const void *data, size_t len, - unsigned int option_type ) { - struct sockaddr_tcpip *st_src = - ( ( struct sockaddr_tcpip * ) sin6_src ); - struct sockaddr_tcpip *st_dest = - ( ( struct sockaddr_tcpip * ) sin6_dest ); - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct io_buffer *iobuf; - struct ndp_ll_addr_option *ll_addr_opt; - union ndp_header *ndp; - size_t option_len; - int rc; - - /* Allocate and populate buffer */ - option_len = ( ( sizeof ( *ll_addr_opt ) + - ll_protocol->ll_addr_len + NDP_OPTION_BLKSZ - 1 ) & - ~( NDP_OPTION_BLKSZ - 1 ) ); - iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len + option_len ); - if ( ! iobuf ) - return -ENOMEM; - iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN ); - memcpy ( iob_put ( iobuf, len ), data, len ); - ll_addr_opt = iob_put ( iobuf, option_len ); - ll_addr_opt->header.type = option_type; - ll_addr_opt->header.blocks = ( option_len / NDP_OPTION_BLKSZ ); - memcpy ( ll_addr_opt->ll_addr, netdev->ll_addr, - ll_protocol->ll_addr_len ); - ndp = iobuf->data; - ndp->icmp.chksum = tcpip_chksum ( ndp, ( len + option_len ) ); - - /* Transmit packet */ - if ( ( rc = tcpip_tx ( iobuf, &icmpv6_protocol, st_src, st_dest, - netdev, &ndp->icmp.chksum ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not transmit packet: %s\n", - netdev->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Transmit NDP neighbour discovery request - * - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @v net_source Source network-layer address - * @ret rc Return status code - */ -static int ndp_tx_request ( struct net_device *netdev, - struct net_protocol *net_protocol __unused, - const void *net_dest, const void *net_source ) { - struct sockaddr_in6 sin6_src; - struct sockaddr_in6 sin6_dest; - struct ndp_neighbour_header neigh; - int rc; - - /* Construct source address */ - memset ( &sin6_src, 0, sizeof ( sin6_src ) ); - sin6_src.sin6_family = AF_INET6; - memcpy ( &sin6_src.sin6_addr, net_source, - sizeof ( sin6_src.sin6_addr ) ); - - /* Construct multicast destination address */ - memset ( &sin6_dest, 0, sizeof ( sin6_dest ) ); - sin6_dest.sin6_family = AF_INET6; - sin6_dest.sin6_scope_id = netdev->index; - ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest ); - - /* Construct neighbour header */ - memset ( &neigh, 0, sizeof ( neigh ) ); - neigh.icmp.type = ICMPV6_NEIGHBOUR_SOLICITATION; - memcpy ( &neigh.target, net_dest, sizeof ( neigh.target ) ); - - /* Transmit neighbour discovery packet */ - if ( ( rc = ndp_tx_ll_addr ( netdev, &sin6_src, &sin6_dest, &neigh, - sizeof ( neigh ), - NDP_OPT_LL_SOURCE ) ) != 0 ) - return rc; - - return 0; -} - -/** NDP neighbour discovery protocol */ -struct neighbour_discovery ndp_discovery = { - .name = "NDP", - .tx_request = ndp_tx_request, -}; - -/** - * Transmit NDP router solicitation - * - * @v netdev Network device - * @ret rc Return status code - */ -static int ndp_tx_router_solicitation ( struct net_device *netdev ) { - struct ndp_router_solicitation_header rsol; - struct sockaddr_in6 sin6_dest; - int rc; - - /* Construct multicast destination address */ - memset ( &sin6_dest, 0, sizeof ( sin6_dest ) ); - sin6_dest.sin6_family = AF_INET6; - sin6_dest.sin6_scope_id = netdev->index; - ipv6_all_routers ( &sin6_dest.sin6_addr ); - - /* Construct router solicitation */ - memset ( &rsol, 0, sizeof ( rsol ) ); - rsol.icmp.type = ICMPV6_ROUTER_SOLICITATION; - - /* Transmit packet */ - if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, &sin6_dest, &rsol, - sizeof ( rsol ), NDP_OPT_LL_SOURCE ) ) !=0) - return rc; - - return 0; -} - -/** - * Process NDP neighbour solicitation source link-layer address option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP packet - * @v option NDP option - * @v len NDP option length - * @ret rc Return status code - */ -static int -ndp_rx_neighbour_solicitation_ll_source ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - union ndp_header *ndp, - union ndp_option *option, - size_t len ) { - struct ndp_neighbour_header *neigh = &ndp->neigh; - struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr; - struct ll_protocol *ll_protocol = netdev->ll_protocol; - int rc; - - /* Silently ignore neighbour solicitations for addresses we do - * not own. - */ - if ( ! ipv6_has_addr ( netdev, &neigh->target ) ) - return 0; - - /* Sanity check */ - if ( offsetof ( typeof ( *ll_addr_opt ), - ll_addr[ll_protocol->ll_addr_len] ) > len ) { - DBGC ( netdev, "NDP %s neighbour solicitation link-layer " - "address option too short at %zd bytes\n", - netdev->name, len ); - return -EINVAL; - } - - /* Create or update neighbour cache entry */ - if ( ( rc = neighbour_define ( netdev, &ipv6_protocol, - &sin6_src->sin6_addr, - ll_addr_opt->ll_addr ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not define %s => %s: %s\n", - netdev->name, inet6_ntoa ( &sin6_src->sin6_addr ), - ll_protocol->ntoa ( ll_addr_opt->ll_addr ), - strerror ( rc ) ); - return rc; - } - - /* Convert neighbour header to advertisement */ - memset ( neigh, 0, offsetof ( typeof ( *neigh ), target ) ); - neigh->icmp.type = ICMPV6_NEIGHBOUR_ADVERTISEMENT; - neigh->flags = ( NDP_NEIGHBOUR_SOLICITED | NDP_NEIGHBOUR_OVERRIDE ); - - /* Send neighbour advertisement */ - if ( ( rc = ndp_tx_ll_addr ( netdev, NULL, sin6_src, neigh, - sizeof ( *neigh ), - NDP_OPT_LL_TARGET ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Process NDP neighbour advertisement target link-layer address option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP packet - * @v option NDP option - * @v len NDP option length - * @ret rc Return status code - */ -static int -ndp_rx_neighbour_advertisement_ll_target ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src - __unused, - union ndp_header *ndp, - union ndp_option *option, - size_t len ) { - struct ndp_neighbour_header *neigh = &ndp->neigh; - struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr; - struct ll_protocol *ll_protocol = netdev->ll_protocol; - int rc; - - /* Sanity check */ - if ( offsetof ( typeof ( *ll_addr_opt ), - ll_addr[ll_protocol->ll_addr_len] ) > len ) { - DBGC ( netdev, "NDP %s neighbour advertisement link-layer " - "address option too short at %zd bytes\n", - netdev->name, len ); - return -EINVAL; - } - - /* Update neighbour cache entry, if any */ - if ( ( rc = neighbour_update ( netdev, &ipv6_protocol, &neigh->target, - ll_addr_opt->ll_addr ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not update %s => %s: %s\n", - netdev->name, inet6_ntoa ( &neigh->target ), - ll_protocol->ntoa ( ll_addr_opt->ll_addr ), - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Process NDP router advertisement source link-layer address option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP packet - * @v option NDP option - * @v len NDP option length - * @ret rc Return status code - */ -static int -ndp_rx_router_advertisement_ll_source ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - union ndp_header *ndp __unused, - union ndp_option *option, size_t len ) { - struct ndp_ll_addr_option *ll_addr_opt = &option->ll_addr; - struct ll_protocol *ll_protocol = netdev->ll_protocol; - int rc; - - /* Sanity check */ - if ( offsetof ( typeof ( *ll_addr_opt ), - ll_addr[ll_protocol->ll_addr_len] ) > len ) { - DBGC ( netdev, "NDP %s router advertisement link-layer address " - "option too short at %zd bytes\n", netdev->name, len ); - return -EINVAL; - } - - /* Define neighbour cache entry */ - if ( ( rc = neighbour_define ( netdev, &ipv6_protocol, - &sin6_src->sin6_addr, - ll_addr_opt->ll_addr ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not define %s => %s: %s\n", - netdev->name, inet6_ntoa ( &sin6_src->sin6_addr ), - ll_protocol->ntoa ( ll_addr_opt->ll_addr ), - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Process NDP router advertisement prefix information option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP packet - * @v option NDP option - * @v len NDP option length - * @ret rc Return status code - */ -static int -ndp_rx_router_advertisement_prefix ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - union ndp_header *ndp, - union ndp_option *option, size_t len ) { - struct ndp_router_advertisement_header *radv = &ndp->radv; - struct ndp_prefix_information_option *prefix_opt = &option->prefix; - struct in6_addr *router = &sin6_src->sin6_addr; - struct in6_addr address; - int prefix_len; - int rc; - - /* Sanity check */ - if ( sizeof ( *prefix_opt ) > len ) { - DBGC ( netdev, "NDP %s router advertisement prefix option too " - "short at %zd bytes\n", netdev->name, len ); - return -EINVAL; - } - DBGC ( netdev, "NDP %s found %sdefault router %s ", - netdev->name, ( radv->lifetime ? "" : "non-" ), - inet6_ntoa ( &sin6_src->sin6_addr ) ); - DBGC ( netdev, "for %s-link %sautonomous prefix %s/%d\n", - ( ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ? "on" : "off" ), - ( ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) ? "" : "non-" ), - inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len ); - - /* Ignore off-link prefixes */ - if ( ! ( prefix_opt->flags & NDP_PREFIX_ON_LINK ) ) - return 0; - - /* Define prefix */ - if ( ( rc = ipv6_set_prefix ( netdev, &prefix_opt->prefix, - prefix_opt->prefix_len, - ( radv->lifetime ? - router : NULL ) ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not define prefix %s/%d: %s\n", - netdev->name, inet6_ntoa ( &prefix_opt->prefix ), - prefix_opt->prefix_len, strerror ( rc ) ); - return rc; - } - - /* Perform stateless address autoconfiguration, if applicable */ - if ( prefix_opt->flags & NDP_PREFIX_AUTONOMOUS ) { - memcpy ( &address, &prefix_opt->prefix, sizeof ( address ) ); - prefix_len = ipv6_eui64 ( &address, netdev ); - if ( prefix_len < 0 ) { - rc = prefix_len; - DBGC ( netdev, "NDP %s could not construct SLAAC " - "address: %s\n", netdev->name, strerror ( rc ) ); - return rc; - } - if ( prefix_len != prefix_opt->prefix_len ) { - DBGC ( netdev, "NDP %s incorrect SLAAC prefix length " - "%d (expected %d)\n", netdev->name, - prefix_opt->prefix_len, prefix_len ); - return -EINVAL; - } - if ( ( rc = ipv6_set_address ( netdev, &address ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not set address %s: %s\n", - netdev->name, inet6_ntoa ( &address ), - strerror ( rc ) ); - return rc; - } - } - - return 0; -} - -/** An NDP option handler */ -struct ndp_option_handler { - /** ICMPv6 type */ - uint8_t icmp_type; - /** Option type */ - uint8_t option_type; - /** - * Handle received option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP packet - * @v option NDP option - * @ret rc Return status code - */ - int ( * rx ) ( struct net_device *netdev, struct sockaddr_in6 *sin6_src, - union ndp_header *ndp, union ndp_option *option, - size_t len ); -}; - -/** NDP option handlers */ -static struct ndp_option_handler ndp_option_handlers[] = { - { - .icmp_type = ICMPV6_NEIGHBOUR_SOLICITATION, - .option_type = NDP_OPT_LL_SOURCE, - .rx = ndp_rx_neighbour_solicitation_ll_source, - }, - { - .icmp_type = ICMPV6_NEIGHBOUR_ADVERTISEMENT, - .option_type = NDP_OPT_LL_TARGET, - .rx = ndp_rx_neighbour_advertisement_ll_target, - }, - { - .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT, - .option_type = NDP_OPT_LL_SOURCE, - .rx = ndp_rx_router_advertisement_ll_source, - }, - { - .icmp_type = ICMPV6_ROUTER_ADVERTISEMENT, - .option_type = NDP_OPT_PREFIX, - .rx = ndp_rx_router_advertisement_prefix, - }, -}; - -/** - * Process received NDP option - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP packet - * @v option NDP option - * @v len Option length - * @ret rc Return status code - */ -static int ndp_rx_option ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src, union ndp_header *ndp, - union ndp_option *option, size_t len ) { - struct ndp_option_handler *handler; - unsigned int i; - - /* Locate a suitable option handler, if any */ - for ( i = 0 ; i < ( sizeof ( ndp_option_handlers ) / - sizeof ( ndp_option_handlers[0] ) ) ; i++ ) { - handler = &ndp_option_handlers[i]; - if ( ( handler->icmp_type == ndp->icmp.type ) && - ( handler->option_type == option->header.type ) ) { - return handler->rx ( netdev, sin6_src, ndp, - option, len ); - } - } - - /* Silently ignore unknown options as per RFC 4861 */ - return 0; -} - -/** - * Process received NDP packet options - * - * @v netdev Network device - * @v sin6_src Source socket address - * @v ndp NDP header - * @v offset Offset to NDP options - * @v len Length of NDP packet - * @ret rc Return status code - */ -static int ndp_rx_options ( struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - union ndp_header *ndp, size_t offset, size_t len ) { - union ndp_option *option; - size_t remaining; - size_t option_len; - int rc; - - /* Sanity check */ - if ( len < offset ) { - DBGC ( netdev, "NDP %s packet too short at %zd bytes (min %zd " - "bytes)\n", netdev->name, len, offset ); - return -EINVAL; - } - - /* Search for option */ - option = ( ( ( void * ) ndp ) + offset ); - remaining = ( len - offset ); - while ( remaining ) { - - /* Sanity check */ - if ( ( remaining < sizeof ( option->header ) ) || - ( option->header.blocks == 0 ) || - ( remaining < ( option->header.blocks * - NDP_OPTION_BLKSZ ) ) ) { - DBGC ( netdev, "NDP %s bad option length:\n", - netdev->name ); - DBGC_HDA ( netdev, 0, option, remaining ); - return -EINVAL; - } - option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - - /* Handle option */ - if ( ( rc = ndp_rx_option ( netdev, sin6_src, ndp, option, - option_len ) ) != 0 ) - return rc; - - /* Move to next option */ - option = ( ( ( void * ) option ) + option_len ); - remaining -= option_len; - } - - return 0; -} - -/** - * Process received NDP neighbour solicitation or advertisement - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v sin6_src Source socket address - * @v sin6_dest Destination socket address - * @ret rc Return status code - */ -static int ndp_rx_neighbour ( struct io_buffer *iobuf, - struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - struct sockaddr_in6 *sin6_dest __unused ) { - union ndp_header *ndp = iobuf->data; - struct ndp_neighbour_header *neigh = &ndp->neigh; - size_t len = iob_len ( iobuf ); - int rc; - - /* Process options */ - if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp, - offsetof ( typeof ( *neigh ), option ), - len ) ) != 0 ) - goto err_options; - - err_options: - free_iob ( iobuf ); - return rc; -} - -/** - * Process received NDP router advertisement - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v sin6_src Source socket address - * @v sin6_dest Destination socket address - * @ret rc Return status code - */ -static int -ndp_rx_router_advertisement ( struct io_buffer *iobuf, - struct net_device *netdev, - struct sockaddr_in6 *sin6_src, - struct sockaddr_in6 *sin6_dest __unused ) { - union ndp_header *ndp = iobuf->data; - struct ndp_router_advertisement_header *radv = &ndp->radv; - size_t len = iob_len ( iobuf ); - int rc; - - /* Process options */ - if ( ( rc = ndp_rx_options ( netdev, sin6_src, ndp, - offsetof ( typeof ( *radv ), option ), - len ) ) != 0 ) - goto err_options; - - /* Pass to IPv6 autoconfiguration */ - if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv, - len ) ) != 0 ) - goto err_ipv6conf; - - err_ipv6conf: - err_options: - free_iob ( iobuf ); - return rc; -} - -/** NDP ICMPv6 handlers */ -struct icmpv6_handler ndp_handlers[] __icmpv6_handler = { - { - .type = ICMPV6_NEIGHBOUR_SOLICITATION, - .rx = ndp_rx_neighbour, - }, - { - .type = ICMPV6_NEIGHBOUR_ADVERTISEMENT, - .rx = ndp_rx_neighbour, - }, - { - .type = ICMPV6_ROUTER_ADVERTISEMENT, - .rx = ndp_rx_router_advertisement, - }, -}; - -/**************************************************************************** - * - * NDP settings - * - */ - -/** An NDP settings block */ -struct ndp_settings { - /** Reference counter */ - struct refcnt refcnt; - /** Settings interface */ - struct settings settings; - /** Length of NDP options */ - size_t len; - /** NDP options */ - union ndp_option option[0]; -}; - -/** NDP settings scope */ -static const struct settings_scope ndp_settings_scope; - -/** - * Construct NDP tag - * - * @v type NDP option type - * @v offset Starting offset of data - * @ret tag NDP tag - */ -#define NDP_TAG( type, offset ) ( ( (offset) << 8 ) | (type) ) - -/** - * Extract NDP tag type - * - * @v tag NDP tag - * @ret type NDP option type - */ -#define NDP_TAG_TYPE( tag ) ( (tag) & 0xff ) - -/** - * Extract NDP tag offset - * - * @v tag NDP tag - * @ret offset Starting offset of data - */ -#define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 ) - -/** - * Check applicability of NDP setting - * - * @v settings Settings block - * @v setting Setting to fetch - * @ret applies Setting applies within this settings block - */ -static int ndp_applies ( struct settings *settings __unused, - const struct setting *setting ) { - - return ( setting->scope == &ndp_settings_scope ); -} - -/** - * Fetch value of NDP setting - * - * @v settings Settings block - * @v setting Setting to fetch - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int ndp_fetch ( struct settings *settings, - struct setting *setting, - void *data, size_t len ) { - struct ndp_settings *ndpset = - container_of ( settings, struct ndp_settings, settings ); - struct net_device *netdev = - container_of ( settings->parent, struct net_device, - settings.settings ); - union ndp_option *option; - unsigned int type = NDP_TAG_TYPE ( setting->tag ); - unsigned int offset = NDP_TAG_OFFSET ( setting->tag ); - size_t remaining; - size_t option_len; - size_t payload_len; - - /* Scan through NDP options for requested type. We can assume - * that the options are well-formed, otherwise they would have - * been rejected prior to being stored. - */ - option = ndpset->option; - remaining = ndpset->len; - while ( remaining ) { - - /* Calculate option length */ - option_len = ( option->header.blocks * NDP_OPTION_BLKSZ ); - - /* If this is the requested option, return it */ - if ( option->header.type == type ) { - - /* Sanity check */ - if ( offset > option_len ) { - DBGC ( netdev, "NDP %s option %d too short\n", - netdev->name, type ); - return -EINVAL; - } - payload_len = ( option_len - offset ); - - /* Copy data to output buffer */ - if ( len > payload_len ) - len = payload_len; - memcpy ( data, ( ( ( void * ) option ) + offset ), len); - return payload_len; - } - - /* Move to next option */ - option = ( ( ( void * ) option ) + option_len ); - remaining -= option_len; - } - - return -ENOENT; -} - -/** NDP settings operations */ -static struct settings_operations ndp_settings_operations = { - .applies = ndp_applies, - .fetch = ndp_fetch, -}; - -/** - * Register NDP settings - * - * @v netdev Network device - * @v option NDP options - * @v len Length of options - * @ret rc Return status code - */ -static int ndp_register_settings ( struct net_device *netdev, - union ndp_option *option, size_t len ) { - struct settings *parent = netdev_settings ( netdev ); - struct ndp_settings *ndpset; - int rc; - - /* Allocate and initialise structure */ - ndpset = zalloc ( sizeof ( *ndpset ) + len ); - if ( ! ndpset ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &ndpset->refcnt, NULL ); - settings_init ( &ndpset->settings, &ndp_settings_operations, - &ndpset->refcnt, &ndp_settings_scope ); - ndpset->len = len; - memcpy ( ndpset->option, option, len ); - - /* Register settings */ - if ( ( rc = register_settings ( &ndpset->settings, parent, - NDP_SETTINGS_NAME ) ) != 0 ) - goto err_register; - - err_register: - ref_put ( &ndpset->refcnt ); - err_alloc: - return rc; -} - -/** DNS server setting */ -const struct setting ndp_dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { - .name = "dns6", - .description = "DNS server", - .tag = NDP_TAG ( NDP_OPT_RDNSS, - offsetof ( struct ndp_rdnss_option, addresses ) ), - .type = &setting_type_ipv6, - .scope = &ndp_settings_scope, -}; - -/** DNS search list setting */ -const struct setting ndp_dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { - .name = "dnssl", - .description = "DNS search list", - .tag = NDP_TAG ( NDP_OPT_DNSSL, - offsetof ( struct ndp_dnssl_option, names ) ), - .type = &setting_type_dnssl, - .scope = &ndp_settings_scope, -}; - -/**************************************************************************** - * - * IPv6 autoconfiguration - * - */ - -/** An IPv6 configurator */ -struct ipv6conf { - /** Reference count */ - struct refcnt refcnt; - /** List of configurators */ - struct list_head list; - - /** Job control interface */ - struct interface job; - /** DHCPv6 interface */ - struct interface dhcp; - - /** Network device being configured */ - struct net_device *netdev; - - /** Retransmission timer */ - struct retry_timer timer; -}; - -/** List of IPv6 configurators */ -static LIST_HEAD ( ipv6confs ); - -/** - * Free IPv6 configurator - * - * @v refcnt Reference count - */ -static void ipv6conf_free ( struct refcnt *refcnt ) { - struct ipv6conf *ipv6conf = - container_of ( refcnt, struct ipv6conf, refcnt ); - - netdev_put ( ipv6conf->netdev ); - free ( ipv6conf ); -} - -/** - * Identify IPv6 configurator by network device - * - * @v netdev Network device - * @ret ipv6 IPv6 configurator, or NULL - */ -static struct ipv6conf * ipv6conf_demux ( struct net_device *netdev ) { - struct ipv6conf *ipv6conf; - - list_for_each_entry ( ipv6conf, &ipv6confs, list ) { - if ( ipv6conf->netdev == netdev ) - return ipv6conf; - } - return NULL; -} - -/** - * Finish IPv6 autoconfiguration - * - * @v ipv6 IPv6 configurator - * @v rc Reason for finishing - */ -static void ipv6conf_done ( struct ipv6conf *ipv6conf, int rc ) { - - /* Shut down interfaces */ - intf_shutdown ( &ipv6conf->job, rc ); - intf_shutdown ( &ipv6conf->dhcp, rc ); - - /* Stop timer */ - stop_timer ( &ipv6conf->timer ); - - /* Remove from list and drop list's reference */ - list_del ( &ipv6conf->list ); - ref_put ( &ipv6conf->refcnt ); -} - -/** - * Handle IPv6 configurator timer expiry - * - * @v timer Retry timer - * @v fail Failure indicator - */ -static void ipv6conf_expired ( struct retry_timer *timer, int fail ) { - struct ipv6conf *ipv6conf = - container_of ( timer, struct ipv6conf, timer ); - - /* If we have failed, terminate autoconfiguration */ - if ( fail ) { - ipv6conf_done ( ipv6conf, -ETIMEDOUT ); - return; - } - - /* Otherwise, transmit router solicitation and restart timer */ - start_timer ( &ipv6conf->timer ); - ndp_tx_router_solicitation ( ipv6conf->netdev ); -} - -/** - * Handle router advertisement during IPv6 autoconfiguration - * - * @v netdev Network device - * @v radv Router advertisement - * @v len Length of router advertisement - * @ret rc Return status code - * - * This function assumes that the router advertisement is well-formed, - * since it must have already passed through option processing. - */ -static int -ipv6conf_rx_router_advertisement ( struct net_device *netdev, - struct ndp_router_advertisement_header *radv, - size_t len ) { - struct ipv6conf *ipv6conf; - size_t option_len; - int stateful; - int rc; - - /* Identify IPv6 configurator, if any */ - ipv6conf = ipv6conf_demux ( netdev ); - if ( ! ipv6conf ) { - /* Not an error; router advertisements are processed - * as a background activity even when no explicit - * autoconfiguration is taking place. - */ - return 0; - } - - /* If this is not the first solicited router advertisement, ignore it */ - if ( ! timer_running ( &ipv6conf->timer ) ) - return 0; - - /* Stop router solicitation timer */ - stop_timer ( &ipv6conf->timer ); - - /* Register NDP settings */ - option_len = ( len - offsetof ( typeof ( *radv ), option ) ); - if ( ( rc = ndp_register_settings ( netdev, radv->option, - option_len ) ) != 0 ) - return rc; - - /* Start DHCPv6 if required */ - if ( radv->flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) { - stateful = ( radv->flags & NDP_ROUTER_MANAGED ); - if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev, - stateful ) ) != 0 ) { - DBGC ( netdev, "NDP %s could not start state%s DHCPv6: " - "%s\n", netdev->name, - ( stateful ? "ful" : "less" ), strerror ( rc ) ); - ipv6conf_done ( ipv6conf, rc ); - return rc; - } - return 0; - } - - /* Otherwise, terminate autoconfiguration */ - ipv6conf_done ( ipv6conf, 0 ); - - return 0; -} - -/** IPv6 configurator job interface operations */ -static struct interface_operation ipv6conf_job_op[] = { - INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ), -}; - -/** IPv6 configurator job interface descriptor */ -static struct interface_descriptor ipv6conf_job_desc = - INTF_DESC ( struct ipv6conf, job, ipv6conf_job_op ); - -/** IPv6 configurator DHCPv6 interface operations */ -static struct interface_operation ipv6conf_dhcp_op[] = { - INTF_OP ( intf_close, struct ipv6conf *, ipv6conf_done ), -}; - -/** IPv6 configurator DHCPv6 interface descriptor */ -static struct interface_descriptor ipv6conf_dhcp_desc = - INTF_DESC ( struct ipv6conf, dhcp, ipv6conf_dhcp_op ); - -/** - * Start IPv6 autoconfiguration - * - * @v job Job control interface - * @v netdev Network device - * @ret rc Return status code - */ -int start_ipv6conf ( struct interface *job, struct net_device *netdev ) { - struct ipv6conf *ipv6conf; - - /* Allocate and initialise structure */ - ipv6conf = zalloc ( sizeof ( *ipv6conf ) ); - if ( ! ipv6conf ) - return -ENOMEM; - ref_init ( &ipv6conf->refcnt, ipv6conf_free ); - intf_init ( &ipv6conf->job, &ipv6conf_job_desc, &ipv6conf->refcnt ); - intf_init ( &ipv6conf->dhcp, &ipv6conf_dhcp_desc, &ipv6conf->refcnt ); - timer_init ( &ipv6conf->timer, ipv6conf_expired, &ipv6conf->refcnt ); - ipv6conf->netdev = netdev_get ( netdev ); - - /* Start timer to initiate router solicitation */ - start_timer_nodelay ( &ipv6conf->timer ); - - /* Attach parent interface, transfer reference to list, and return */ - intf_plug_plug ( &ipv6conf->job, job ); - list_add ( &ipv6conf->list, &ipv6confs ); - return 0; -} - -/** IPv6 network device configurator */ -struct net_device_configurator ipv6_configurator __net_device_configurator = { - .name = "ipv6", - .start = start_ipv6conf, -}; diff --git a/qemu/roms/ipxe/src/net/neighbour.c b/qemu/roms/ipxe/src/net/neighbour.c deleted file mode 100644 index 7f66d9992..000000000 --- a/qemu/roms/ipxe/src/net/neighbour.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <ipxe/iobuf.h> -#include <ipxe/retry.h> -#include <ipxe/timer.h> -#include <ipxe/malloc.h> -#include <ipxe/neighbour.h> - -/** @file - * - * Neighbour discovery - * - * This file implements the abstract functions of neighbour discovery, - * independent of the underlying network protocol (e.g. ARP or NDP). - * - */ - -/** Neighbour discovery minimum timeout */ -#define NEIGHBOUR_MIN_TIMEOUT ( TICKS_PER_SEC / 8 ) - -/** Neighbour discovery maximum timeout */ -#define NEIGHBOUR_MAX_TIMEOUT ( TICKS_PER_SEC * 3 ) - -/** The neighbour cache */ -struct list_head neighbours = LIST_HEAD_INIT ( neighbours ); - -static void neighbour_expired ( struct retry_timer *timer, int over ); - -/** - * Free neighbour cache entry - * - * @v refcnt Reference count - */ -static void neighbour_free ( struct refcnt *refcnt ) { - struct neighbour *neighbour = - container_of ( refcnt, struct neighbour, refcnt ); - - /* Sanity check */ - assert ( list_empty ( &neighbour->tx_queue ) ); - - /* Drop reference to network device */ - netdev_put ( neighbour->netdev ); - - /* Free neighbour */ - free ( neighbour ); -} - -/** - * Create neighbour cache entry - * - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @ret neighbour Neighbour cache entry, or NULL if allocation failed - */ -static struct neighbour * neighbour_create ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest ) { - struct neighbour *neighbour; - - /* Allocate and initialise entry */ - neighbour = zalloc ( sizeof ( *neighbour ) ); - if ( ! neighbour ) - return NULL; - ref_init ( &neighbour->refcnt, neighbour_free ); - neighbour->netdev = netdev_get ( netdev ); - neighbour->net_protocol = net_protocol; - memcpy ( neighbour->net_dest, net_dest, - net_protocol->net_addr_len ); - timer_init ( &neighbour->timer, neighbour_expired, &neighbour->refcnt ); - set_timer_limits ( &neighbour->timer, NEIGHBOUR_MIN_TIMEOUT, - NEIGHBOUR_MAX_TIMEOUT ); - INIT_LIST_HEAD ( &neighbour->tx_queue ); - - /* Transfer ownership to cache */ - list_add ( &neighbour->list, &neighbours ); - - DBGC ( neighbour, "NEIGHBOUR %s %s %s created\n", netdev->name, - net_protocol->name, net_protocol->ntoa ( net_dest ) ); - return neighbour; -} - -/** - * Find neighbour cache entry - * - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @ret neighbour Neighbour cache entry, or NULL if not found - */ -static struct neighbour * neighbour_find ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest ) { - struct neighbour *neighbour; - - list_for_each_entry ( neighbour, &neighbours, list ) { - if ( ( neighbour->netdev == netdev ) && - ( neighbour->net_protocol == net_protocol ) && - ( memcmp ( neighbour->net_dest, net_dest, - net_protocol->net_addr_len ) == 0 ) ) { - - /* Move to start of cache */ - list_del ( &neighbour->list ); - list_add ( &neighbour->list, &neighbours ); - - return neighbour; - } - } - return NULL; -} - -/** - * Start neighbour discovery - * - * @v neighbour Neighbour cache entry - * @v discovery Neighbour discovery protocol - * @v net_source Source network-layer address - */ -static void neighbour_discover ( struct neighbour *neighbour, - struct neighbour_discovery *discovery, - const void *net_source ) { - struct net_device *netdev = neighbour->netdev; - struct net_protocol *net_protocol = neighbour->net_protocol; - - /* Record discovery protocol and source network-layer address */ - neighbour->discovery = discovery; - memcpy ( neighbour->net_source, net_source, - net_protocol->net_addr_len ); - - /* Start timer to trigger neighbour discovery */ - start_timer_nodelay ( &neighbour->timer ); - - DBGC ( neighbour, "NEIGHBOUR %s %s %s discovering via %s\n", - netdev->name, net_protocol->name, - net_protocol->ntoa ( neighbour->net_dest ), - neighbour->discovery->name ); -} - -/** - * Complete neighbour discovery - * - * @v neighbour Neighbour cache entry - * @v ll_dest Destination link-layer address - */ -static void neighbour_discovered ( struct neighbour *neighbour, - const void *ll_dest ) { - struct net_device *netdev = neighbour->netdev; - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct net_protocol *net_protocol = neighbour->net_protocol; - struct io_buffer *iobuf; - int rc; - - /* Fill in link-layer address */ - memcpy ( neighbour->ll_dest, ll_dest, ll_protocol->ll_addr_len ); - DBGC ( neighbour, "NEIGHBOUR %s %s %s is %s %s\n", netdev->name, - net_protocol->name, net_protocol->ntoa ( neighbour->net_dest ), - ll_protocol->name, ll_protocol->ntoa ( neighbour->ll_dest ) ); - - /* Stop retransmission timer */ - stop_timer ( &neighbour->timer ); - - /* Transmit any packets in queue. Take out a temporary - * reference on the entry to prevent it from going out of - * scope during the call to net_tx(). - */ - ref_get ( &neighbour->refcnt ); - while ( ( iobuf = list_first_entry ( &neighbour->tx_queue, - struct io_buffer, list )) != NULL){ - DBGC2 ( neighbour, "NEIGHBOUR %s %s %s transmitting deferred " - "packet\n", netdev->name, net_protocol->name, - net_protocol->ntoa ( neighbour->net_dest ) ); - list_del ( &iobuf->list ); - if ( ( rc = net_tx ( iobuf, netdev, net_protocol, ll_dest, - netdev->ll_addr ) ) != 0 ) { - DBGC ( neighbour, "NEIGHBOUR %s %s %s could not " - "transmit deferred packet: %s\n", - netdev->name, net_protocol->name, - net_protocol->ntoa ( neighbour->net_dest ), - strerror ( rc ) ); - /* Ignore error and continue */ - } - } - ref_put ( &neighbour->refcnt ); -} - -/** - * Destroy neighbour cache entry - * - * @v neighbour Neighbour cache entry - * @v rc Reason for destruction - */ -static void neighbour_destroy ( struct neighbour *neighbour, int rc ) { - struct net_device *netdev = neighbour->netdev; - struct net_protocol *net_protocol = neighbour->net_protocol; - struct io_buffer *iobuf; - - /* Take ownership from cache */ - list_del ( &neighbour->list ); - - /* Stop timer */ - stop_timer ( &neighbour->timer ); - - /* Discard any outstanding I/O buffers */ - while ( ( iobuf = list_first_entry ( &neighbour->tx_queue, - struct io_buffer, list )) != NULL){ - DBGC2 ( neighbour, "NEIGHBOUR %s %s %s discarding deferred " - "packet: %s\n", netdev->name, net_protocol->name, - net_protocol->ntoa ( neighbour->net_dest ), - strerror ( rc ) ); - list_del ( &iobuf->list ); - netdev_tx_err ( neighbour->netdev, iobuf, rc ); - } - - DBGC ( neighbour, "NEIGHBOUR %s %s %s destroyed: %s\n", netdev->name, - net_protocol->name, net_protocol->ntoa ( neighbour->net_dest ), - strerror ( rc ) ); - - /* Drop remaining reference */ - ref_put ( &neighbour->refcnt ); -} - -/** - * Handle neighbour timer expiry - * - * @v timer Retry timer - * @v fail Failure indicator - */ -static void neighbour_expired ( struct retry_timer *timer, int fail ) { - struct neighbour *neighbour = - container_of ( timer, struct neighbour, timer ); - struct net_device *netdev = neighbour->netdev; - struct net_protocol *net_protocol = neighbour->net_protocol; - struct neighbour_discovery *discovery = - neighbour->discovery; - const void *net_dest = neighbour->net_dest; - const void *net_source = neighbour->net_source; - int rc; - - /* If we have failed, destroy the cache entry */ - if ( fail ) { - neighbour_destroy ( neighbour, -ETIMEDOUT ); - return; - } - - /* Restart the timer */ - start_timer ( &neighbour->timer ); - - /* Transmit neighbour request */ - if ( ( rc = discovery->tx_request ( netdev, net_protocol, net_dest, - net_source ) ) != 0 ) { - DBGC ( neighbour, "NEIGHBOUR %s %s %s could not transmit %s " - "request: %s\n", netdev->name, net_protocol->name, - net_protocol->ntoa ( neighbour->net_dest ), - neighbour->discovery->name, strerror ( rc ) ); - /* Retransmit when timer expires */ - return; - } -} - -/** - * Transmit packet, determining link-layer address via neighbour discovery - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v discovery Neighbour discovery protocol - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @v net_source Source network-layer address - * @v ll_source Source link-layer address - * @ret rc Return status code - */ -int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev, - struct net_protocol *net_protocol, const void *net_dest, - struct neighbour_discovery *discovery, - const void *net_source, const void *ll_source ) { - struct neighbour *neighbour; - - /* Find or create neighbour cache entry */ - neighbour = neighbour_find ( netdev, net_protocol, net_dest ); - if ( ! neighbour ) { - neighbour = neighbour_create ( netdev, net_protocol, net_dest ); - if ( ! neighbour ) - return -ENOMEM; - neighbour_discover ( neighbour, discovery, net_source ); - } - - /* If a link-layer address is available then transmit - * immediately, otherwise queue for later transmission. - */ - if ( neighbour_has_ll_dest ( neighbour ) ) { - return net_tx ( iobuf, netdev, net_protocol, neighbour->ll_dest, - ll_source ); - } else { - DBGC2 ( neighbour, "NEIGHBOUR %s %s %s deferring packet\n", - netdev->name, net_protocol->name, - net_protocol->ntoa ( net_dest ) ); - list_add_tail ( &iobuf->list, &neighbour->tx_queue ); - return 0; - } -} - -/** - * Update existing neighbour cache entry - * - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @v ll_dest Destination link-layer address - * @ret rc Return status code - */ -int neighbour_update ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest, const void *ll_dest ) { - struct neighbour *neighbour; - - /* Find neighbour cache entry */ - neighbour = neighbour_find ( netdev, net_protocol, net_dest ); - if ( ! neighbour ) - return -ENOENT; - - /* Set destination address */ - neighbour_discovered ( neighbour, ll_dest ); - - return 0; -} - -/** - * Define neighbour cache entry - * - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v net_dest Destination network-layer address - * @v ll_dest Destination link-layer address, if known - * @ret rc Return status code - */ -int neighbour_define ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest, const void *ll_dest ) { - struct neighbour *neighbour; - - /* Find or create neighbour cache entry */ - neighbour = neighbour_find ( netdev, net_protocol, net_dest ); - if ( ! neighbour ) { - neighbour = neighbour_create ( netdev, net_protocol, net_dest ); - if ( ! neighbour ) - return -ENOMEM; - } - - /* Set destination address */ - neighbour_discovered ( neighbour, ll_dest ); - - return 0; -} - -/** - * Update neighbour cache on network device state change or removal - * - * @v netdev Network device - */ -static void neighbour_flush ( struct net_device *netdev ) { - struct neighbour *neighbour; - struct neighbour *tmp; - - /* Remove all neighbour cache entries when a network device is closed */ - if ( ! netdev_is_open ( netdev ) ) { - list_for_each_entry_safe ( neighbour, tmp, &neighbours, list ) - neighbour_destroy ( neighbour, -ENODEV ); - } -} - -/** Neighbour driver (for net device notifications) */ -struct net_driver neighbour_net_driver __net_driver = { - .name = "Neighbour", - .notify = neighbour_flush, - .remove = neighbour_flush, -}; - -/** - * Discard some cached neighbour entries - * - * @ret discarded Number of cached items discarded - */ -static unsigned int neighbour_discard ( void ) { - struct neighbour *neighbour; - - /* Drop oldest cache entry, if any */ - neighbour = list_last_entry ( &neighbours, struct neighbour, list ); - if ( neighbour ) { - neighbour_destroy ( neighbour, -ENOBUFS ); - return 1; - } else { - return 0; - } -} - -/** - * Neighbour cache discarder - * - * Neighbour cache entries are deemed to have a high replacement cost, - * since flushing an active neighbour cache entry midway through a TCP - * transfer will cause substantial disruption. - */ -struct cache_discarder neighbour_discarder __cache_discarder (CACHE_EXPENSIVE)={ - .discard = neighbour_discard, -}; diff --git a/qemu/roms/ipxe/src/net/netdev_settings.c b/qemu/roms/ipxe/src/net/netdev_settings.c deleted file mode 100644 index edd4c4b9f..000000000 --- a/qemu/roms/ipxe/src/net/netdev_settings.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/dhcp.h> -#include <ipxe/dhcpopts.h> -#include <ipxe/settings.h> -#include <ipxe/device.h> -#include <ipxe/netdevice.h> -#include <ipxe/init.h> - -/** @file - * - * Network device configuration settings - * - */ - -/** Network device predefined settings */ -const struct setting mac_setting __setting ( SETTING_NETDEV, mac ) = { - .name = "mac", - .description = "MAC address", - .type = &setting_type_hex, -}; -const struct setting bustype_setting __setting ( SETTING_NETDEV, bustype ) = { - .name = "bustype", - .description = "Bus type", - .type = &setting_type_string, -}; -const struct setting busloc_setting __setting ( SETTING_NETDEV, busloc ) = { - .name = "busloc", - .description = "Bus location", - .type = &setting_type_uint32, -}; -const struct setting busid_setting __setting ( SETTING_NETDEV, busid ) = { - .name = "busid", - .description = "Bus ID", - .type = &setting_type_hex, -}; -const struct setting chip_setting __setting ( SETTING_NETDEV, chip ) = { - .name = "chip", - .description = "Chip", - .type = &setting_type_string, -}; - -/** - * Store MAC address setting - * - * @v netdev Network device - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -static int netdev_store_mac ( struct net_device *netdev, - const void *data, size_t len ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - - /* Record new MAC address */ - if ( data ) { - if ( len != netdev->ll_protocol->ll_addr_len ) - return -EINVAL; - memcpy ( netdev->ll_addr, data, len ); - } else { - /* Reset MAC address if clearing setting */ - ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr ); - } - - return 0; -} - -/** - * Fetch MAC address setting - * - * @v netdev Network device - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int netdev_fetch_mac ( struct net_device *netdev, void *data, - size_t len ) { - - if ( len > netdev->ll_protocol->ll_addr_len ) - len = netdev->ll_protocol->ll_addr_len; - memcpy ( data, netdev->ll_addr, len ); - return netdev->ll_protocol->ll_addr_len; -} - -/** - * Fetch bus type setting - * - * @v netdev Network device - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int netdev_fetch_bustype ( struct net_device *netdev, void *data, - size_t len ) { - static const char *bustypes[] = { - [BUS_TYPE_PCI] = "PCI", - [BUS_TYPE_ISAPNP] = "ISAPNP", - [BUS_TYPE_EISA] = "EISA", - [BUS_TYPE_MCA] = "MCA", - [BUS_TYPE_ISA] = "ISA", - [BUS_TYPE_TAP] = "TAP", - [BUS_TYPE_EFI] = "EFI", - [BUS_TYPE_XEN] = "XEN", - [BUS_TYPE_HV] = "HV", - [BUS_TYPE_USB] = "USB", - }; - struct device_description *desc = &netdev->dev->desc; - const char *bustype; - - assert ( desc->bus_type < ( sizeof ( bustypes ) / - sizeof ( bustypes[0] ) ) ); - bustype = bustypes[desc->bus_type]; - assert ( bustype != NULL ); - strncpy ( data, bustype, len ); - return strlen ( bustype ); -} - -/** - * Fetch bus location setting - * - * @v netdev Network device - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int netdev_fetch_busloc ( struct net_device *netdev, void *data, - size_t len ) { - struct device_description *desc = &netdev->dev->desc; - uint32_t busloc; - - busloc = cpu_to_be32 ( desc->location ); - if ( len > sizeof ( busloc ) ) - len = sizeof ( busloc ); - memcpy ( data, &busloc, len ); - return sizeof ( busloc ); -} - -/** - * Fetch bus ID setting - * - * @v netdev Network device - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int netdev_fetch_busid ( struct net_device *netdev, void *data, - size_t len ) { - struct device_description *desc = &netdev->dev->desc; - struct dhcp_netdev_desc dhcp_desc; - - dhcp_desc.type = desc->bus_type; - dhcp_desc.vendor = htons ( desc->vendor ); - dhcp_desc.device = htons ( desc->device ); - if ( len > sizeof ( dhcp_desc ) ) - len = sizeof ( dhcp_desc ); - memcpy ( data, &dhcp_desc, len ); - return sizeof ( dhcp_desc ); -} - -/** - * Fetch chip setting - * - * @v netdev Network device - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int netdev_fetch_chip ( struct net_device *netdev, void *data, - size_t len ) { - const char *chip = netdev->dev->driver_name; - - strncpy ( data, chip, len ); - return strlen ( chip ); -} - -/** A network device setting operation */ -struct netdev_setting_operation { - /** Setting */ - const struct setting *setting; - /** Store setting (or NULL if not supported) - * - * @v netdev Network device - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ - int ( * store ) ( struct net_device *netdev, const void *data, - size_t len ); - /** Fetch setting - * - * @v netdev Network device - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ - int ( * fetch ) ( struct net_device *netdev, void *data, size_t len ); -}; - -/** Network device settings */ -static struct netdev_setting_operation netdev_setting_operations[] = { - { &mac_setting, netdev_store_mac, netdev_fetch_mac }, - { &bustype_setting, NULL, netdev_fetch_bustype }, - { &busloc_setting, NULL, netdev_fetch_busloc }, - { &busid_setting, NULL, netdev_fetch_busid }, - { &chip_setting, NULL, netdev_fetch_chip }, -}; - -/** - * Store value of network device setting - * - * @v settings Settings block - * @v setting Setting to store - * @v data Setting data, or NULL to clear setting - * @v len Length of setting data - * @ret rc Return status code - */ -static int netdev_store ( struct settings *settings, - const struct setting *setting, - const void *data, size_t len ) { - struct net_device *netdev = container_of ( settings, struct net_device, - settings.settings ); - struct netdev_setting_operation *op; - unsigned int i; - - /* Handle network device-specific settings */ - for ( i = 0 ; i < ( sizeof ( netdev_setting_operations ) / - sizeof ( netdev_setting_operations[0] ) ) ; i++ ) { - op = &netdev_setting_operations[i]; - if ( setting_cmp ( setting, op->setting ) == 0 ) { - if ( op->store ) { - return op->store ( netdev, data, len ); - } else { - return -ENOTSUP; - } - } - } - - return generic_settings_store ( settings, setting, data, len ); -} - -/** - * Fetch value of network device setting - * - * @v settings Settings block - * @v setting Setting to fetch - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int netdev_fetch ( struct settings *settings, struct setting *setting, - void *data, size_t len ) { - struct net_device *netdev = container_of ( settings, struct net_device, - settings.settings ); - struct netdev_setting_operation *op; - unsigned int i; - - /* Handle network device-specific settings */ - for ( i = 0 ; i < ( sizeof ( netdev_setting_operations ) / - sizeof ( netdev_setting_operations[0] ) ) ; i++ ) { - op = &netdev_setting_operations[i]; - if ( setting_cmp ( setting, op->setting ) == 0 ) - return op->fetch ( netdev, data, len ); - } - - return generic_settings_fetch ( settings, setting, data, len ); -} - -/** - * Clear network device settings - * - * @v settings Settings block - */ -static void netdev_clear ( struct settings *settings ) { - generic_settings_clear ( settings ); -} - -/** Network device configuration settings operations */ -struct settings_operations netdev_settings_operations = { - .store = netdev_store, - .fetch = netdev_fetch, - .clear = netdev_clear, -}; - -/** - * Redirect "netX" settings block - * - * @v settings Settings block - * @ret settings Underlying settings block - */ -static struct settings * netdev_redirect ( struct settings *settings ) { - struct net_device *netdev; - - /* Redirect to most recently opened network device */ - netdev = last_opened_netdev(); - if ( netdev ) { - return netdev_settings ( netdev ); - } else { - return settings; - } -} - -/** "netX" settings operations */ -static struct settings_operations netdev_redirect_settings_operations = { - .redirect = netdev_redirect, -}; - -/** "netX" settings */ -static struct settings netdev_redirect_settings = { - .refcnt = NULL, - .siblings = LIST_HEAD_INIT ( netdev_redirect_settings.siblings ), - .children = LIST_HEAD_INIT ( netdev_redirect_settings.children ), - .op = &netdev_redirect_settings_operations, -}; - -/** Initialise "netX" settings */ -static void netdev_redirect_settings_init ( void ) { - int rc; - - if ( ( rc = register_settings ( &netdev_redirect_settings, NULL, - "netX" ) ) != 0 ) { - DBG ( "Could not register netX settings: %s\n", - strerror ( rc ) ); - return; - } -} - -/** "netX" settings initialiser */ -struct init_fn netdev_redirect_settings_init_fn __init_fn ( INIT_LATE ) = { - .initialise = netdev_redirect_settings_init, -}; diff --git a/qemu/roms/ipxe/src/net/netdevice.c b/qemu/roms/ipxe/src/net/netdevice.c deleted file mode 100644 index 7c40a2ac8..000000000 --- a/qemu/roms/ipxe/src/net/netdevice.c +++ /dev/null @@ -1,1272 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <byteswap.h> -#include <string.h> -#include <errno.h> -#include <config/general.h> -#include <ipxe/if_ether.h> -#include <ipxe/iobuf.h> -#include <ipxe/tables.h> -#include <ipxe/process.h> -#include <ipxe/init.h> -#include <ipxe/malloc.h> -#include <ipxe/device.h> -#include <ipxe/errortab.h> -#include <ipxe/profile.h> -#include <ipxe/fault.h> -#include <ipxe/vlan.h> -#include <ipxe/netdevice.h> - -/** @file - * - * Network device management - * - */ - -/** List of network devices */ -struct list_head net_devices = LIST_HEAD_INIT ( net_devices ); - -/** List of open network devices, in reverse order of opening */ -static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices ); - -/** Network device index */ -static unsigned int netdev_index = 0; - -/** Network polling profiler */ -static struct profiler net_poll_profiler __profiler = { .name = "net.poll" }; - -/** Network receive profiler */ -static struct profiler net_rx_profiler __profiler = { .name = "net.rx" }; - -/** Network transmit profiler */ -static struct profiler net_tx_profiler __profiler = { .name = "net.tx" }; - -/** Default unknown link status code */ -#define EUNKNOWN_LINK_STATUS __einfo_error ( EINFO_EUNKNOWN_LINK_STATUS ) -#define EINFO_EUNKNOWN_LINK_STATUS \ - __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" ) - -/** Default not-yet-attempted-configuration status code */ -#define EUNUSED_CONFIG __einfo_error ( EINFO_EUNUSED_CONFIG ) -#define EINFO_EUNUSED_CONFIG \ - __einfo_uniqify ( EINFO_EINPROGRESS, 0x02, "Unused" ) - -/** Default configuration-in-progress status code */ -#define EINPROGRESS_CONFIG __einfo_error ( EINFO_EINPROGRESS_CONFIG ) -#define EINFO_EINPROGRESS_CONFIG \ - __einfo_uniqify ( EINFO_EINPROGRESS, 0x03, "Incomplete" ) - -/** Default link-down status code */ -#define ENOTCONN_LINK_DOWN __einfo_error ( EINFO_ENOTCONN_LINK_DOWN ) -#define EINFO_ENOTCONN_LINK_DOWN \ - __einfo_uniqify ( EINFO_ENOTCONN, 0x01, "Down" ) - -/** Human-readable message for the default link statuses */ -struct errortab netdev_errors[] __errortab = { - __einfo_errortab ( EINFO_EUNKNOWN_LINK_STATUS ), - __einfo_errortab ( EINFO_ENOTCONN_LINK_DOWN ), - __einfo_errortab ( EINFO_EUNUSED_CONFIG ), - __einfo_errortab ( EINFO_EINPROGRESS_CONFIG ), -}; - -/** - * Check whether or not network device has a link-layer address - * - * @v netdev Network device - * @ret has_ll_addr Network device has a link-layer address - */ -static int netdev_has_ll_addr ( struct net_device *netdev ) { - uint8_t *ll_addr = netdev->ll_addr; - size_t remaining = sizeof ( netdev->ll_addr ); - - while ( remaining-- ) { - if ( *(ll_addr++) != 0 ) - return 1; - } - return 0; -} - -/** - * Notify drivers of network device or link state change - * - * @v netdev Network device - */ -static void netdev_notify ( struct net_device *netdev ) { - struct net_driver *driver; - - for_each_table_entry ( driver, NET_DRIVERS ) { - if ( driver->notify ) - driver->notify ( netdev ); - } -} - -/** - * Freeze network device receive queue processing - * - * @v netdev Network device - */ -void netdev_rx_freeze ( struct net_device *netdev ) { - - /* Mark receive queue processing as frozen */ - netdev->state |= NETDEV_RX_FROZEN; - - /* Notify drivers of change */ - netdev_notify ( netdev ); -} - -/** - * Unfreeze network device receive queue processing - * - * @v netdev Network device - */ -void netdev_rx_unfreeze ( struct net_device *netdev ) { - - /* Mark receive queue processing as not frozen */ - netdev->state &= ~NETDEV_RX_FROZEN; - - /* Notify drivers of change */ - netdev_notify ( netdev ); -} - -/** - * Mark network device as having a specific link state - * - * @v netdev Network device - * @v rc Link status code - */ -void netdev_link_err ( struct net_device *netdev, int rc ) { - - /* Stop link block timer */ - stop_timer ( &netdev->link_block ); - - /* Record link state */ - netdev->link_rc = rc; - if ( netdev->link_rc == 0 ) { - DBGC ( netdev, "NETDEV %s link is up\n", netdev->name ); - } else { - DBGC ( netdev, "NETDEV %s link is down: %s\n", - netdev->name, strerror ( netdev->link_rc ) ); - } - - /* Notify drivers of link state change */ - netdev_notify ( netdev ); -} - -/** - * Mark network device as having link down - * - * @v netdev Network device - */ -void netdev_link_down ( struct net_device *netdev ) { - - /* Avoid clobbering a more detailed link status code, if one - * is already set. - */ - if ( ( netdev->link_rc == 0 ) || - ( netdev->link_rc == -EUNKNOWN_LINK_STATUS ) ) { - netdev_link_err ( netdev, -ENOTCONN_LINK_DOWN ); - } -} - -/** - * Mark network device link as being blocked - * - * @v netdev Network device - * @v timeout Timeout (in ticks) - */ -void netdev_link_block ( struct net_device *netdev, unsigned long timeout ) { - - /* Start link block timer */ - if ( ! netdev_link_blocked ( netdev ) ) { - DBGC ( netdev, "NETDEV %s link blocked for %ld ticks\n", - netdev->name, timeout ); - } - start_timer_fixed ( &netdev->link_block, timeout ); -} - -/** - * Mark network device link as being unblocked - * - * @v netdev Network device - */ -void netdev_link_unblock ( struct net_device *netdev ) { - - /* Stop link block timer */ - if ( netdev_link_blocked ( netdev ) ) - DBGC ( netdev, "NETDEV %s link unblocked\n", netdev->name ); - stop_timer ( &netdev->link_block ); -} - -/** - * Handle network device link block timer expiry - * - * @v timer Link block timer - * @v fail Failure indicator - */ -static void netdev_link_block_expired ( struct retry_timer *timer, - int fail __unused ) { - struct net_device *netdev = - container_of ( timer, struct net_device, link_block ); - - /* Assume link is no longer blocked */ - DBGC ( netdev, "NETDEV %s link block expired\n", netdev->name ); -} - -/** - * Record network device statistic - * - * @v stats Network device statistics - * @v rc Status code - */ -static void netdev_record_stat ( struct net_device_stats *stats, int rc ) { - struct net_device_error *error; - struct net_device_error *least_common_error; - unsigned int i; - - /* If this is not an error, just update the good counter */ - if ( rc == 0 ) { - stats->good++; - return; - } - - /* Update the bad counter */ - stats->bad++; - - /* Locate the appropriate error record */ - least_common_error = &stats->errors[0]; - for ( i = 0 ; i < ( sizeof ( stats->errors ) / - sizeof ( stats->errors[0] ) ) ; i++ ) { - error = &stats->errors[i]; - /* Update matching record, if found */ - if ( error->rc == rc ) { - error->count++; - return; - } - if ( error->count < least_common_error->count ) - least_common_error = error; - } - - /* Overwrite the least common error record */ - least_common_error->rc = rc; - least_common_error->count = 1; -} - -/** - * Transmit raw packet via network device - * - * @v netdev Network device - * @v iobuf I/O buffer - * @ret rc Return status code - * - * Transmits the packet via the specified network device. This - * function takes ownership of the I/O buffer. - */ -int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { - int rc; - - DBGC2 ( netdev, "NETDEV %s transmitting %p (%p+%zx)\n", - netdev->name, iobuf, iobuf->data, iob_len ( iobuf ) ); - profile_start ( &net_tx_profiler ); - - /* Enqueue packet */ - list_add_tail ( &iobuf->list, &netdev->tx_queue ); - - /* Avoid calling transmit() on unopened network devices */ - if ( ! netdev_is_open ( netdev ) ) { - rc = -ENETUNREACH; - goto err; - } - - /* Discard packet (for test purposes) if applicable */ - if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) - goto err; - - /* Transmit packet */ - if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 ) - goto err; - - profile_stop ( &net_tx_profiler ); - return 0; - - err: - netdev_tx_complete_err ( netdev, iobuf, rc ); - return rc; -} - -/** - * Defer transmitted packet - * - * @v netdev Network device - * @v iobuf I/O buffer - * - * Drivers may call netdev_tx_defer() if there is insufficient space - * in the transmit descriptor ring. Any packets deferred in this way - * will be automatically retransmitted as soon as space becomes - * available (i.e. as soon as the driver calls netdev_tx_complete()). - * - * The packet must currently be in the network device's TX queue. - * - * Drivers utilising netdev_tx_defer() must ensure that space in the - * transmit descriptor ring is freed up @b before calling - * netdev_tx_complete(). For example, if the ring is modelled using a - * producer counter and a consumer counter, then the consumer counter - * must be incremented before the call to netdev_tx_complete(). - * Failure to do this will cause the retransmitted packet to be - * immediately redeferred (which will result in out-of-order - * transmissions and other nastiness). - */ -void netdev_tx_defer ( struct net_device *netdev, struct io_buffer *iobuf ) { - - /* Catch data corruption as early as possible */ - list_check_contains_entry ( iobuf, &netdev->tx_queue, list ); - - /* Remove from transmit queue */ - list_del ( &iobuf->list ); - - /* Add to deferred transmit queue */ - list_add_tail ( &iobuf->list, &netdev->tx_deferred ); - - /* Record "out of space" statistic */ - netdev_tx_err ( netdev, NULL, -ENOBUFS ); -} - -/** - * Discard transmitted packet - * - * @v netdev Network device - * @v iobuf I/O buffer, or NULL - * @v rc Packet status code - * - * The packet is discarded and a TX error is recorded. This function - * takes ownership of the I/O buffer. - */ -void netdev_tx_err ( struct net_device *netdev, - struct io_buffer *iobuf, int rc ) { - - /* Update statistics counter */ - netdev_record_stat ( &netdev->tx_stats, rc ); - if ( rc == 0 ) { - DBGC2 ( netdev, "NETDEV %s transmission %p complete\n", - netdev->name, iobuf ); - } else { - DBGC ( netdev, "NETDEV %s transmission %p failed: %s\n", - netdev->name, iobuf, strerror ( rc ) ); - } - - /* Discard packet */ - free_iob ( iobuf ); -} - -/** - * Complete network transmission - * - * @v netdev Network device - * @v iobuf I/O buffer - * @v rc Packet status code - * - * The packet must currently be in the network device's TX queue. - */ -void netdev_tx_complete_err ( struct net_device *netdev, - struct io_buffer *iobuf, int rc ) { - - /* Catch data corruption as early as possible */ - list_check_contains_entry ( iobuf, &netdev->tx_queue, list ); - - /* Dequeue and free I/O buffer */ - list_del ( &iobuf->list ); - netdev_tx_err ( netdev, iobuf, rc ); - - /* Transmit first pending packet, if any */ - if ( ( iobuf = list_first_entry ( &netdev->tx_deferred, - struct io_buffer, list ) ) != NULL ) { - list_del ( &iobuf->list ); - netdev_tx ( netdev, iobuf ); - } -} - -/** - * Complete network transmission - * - * @v netdev Network device - * @v rc Packet status code - * - * Completes the oldest outstanding packet in the TX queue. - */ -void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) { - struct io_buffer *iobuf; - - if ( ( iobuf = list_first_entry ( &netdev->tx_queue, struct io_buffer, - list ) ) != NULL ) { - netdev_tx_complete_err ( netdev, iobuf, rc ); - } -} - -/** - * Flush device's transmit queue - * - * @v netdev Network device - */ -static void netdev_tx_flush ( struct net_device *netdev ) { - - /* Discard any packets in the TX queue. This will also cause - * any packets in the deferred TX queue to be discarded - * automatically. - */ - while ( ! list_empty ( &netdev->tx_queue ) ) { - netdev_tx_complete_next_err ( netdev, -ECANCELED ); - } - assert ( list_empty ( &netdev->tx_queue ) ); - assert ( list_empty ( &netdev->tx_deferred ) ); -} - -/** - * Add packet to receive queue - * - * @v netdev Network device - * @v iobuf I/O buffer, or NULL - * - * The packet is added to the network device's RX queue. This - * function takes ownership of the I/O buffer. - */ -void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) { - int rc; - - DBGC2 ( netdev, "NETDEV %s received %p (%p+%zx)\n", - netdev->name, iobuf, iobuf->data, iob_len ( iobuf ) ); - - /* Discard packet (for test purposes) if applicable */ - if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) { - netdev_rx_err ( netdev, iobuf, rc ); - return; - } - - /* Enqueue packet */ - list_add_tail ( &iobuf->list, &netdev->rx_queue ); - - /* Update statistics counter */ - netdev_record_stat ( &netdev->rx_stats, 0 ); -} - -/** - * Discard received packet - * - * @v netdev Network device - * @v iobuf I/O buffer, or NULL - * @v rc Packet status code - * - * The packet is discarded and an RX error is recorded. This function - * takes ownership of the I/O buffer. @c iobuf may be NULL if, for - * example, the net device wishes to report an error due to being - * unable to allocate an I/O buffer. - */ -void netdev_rx_err ( struct net_device *netdev, - struct io_buffer *iobuf, int rc ) { - - DBGC ( netdev, "NETDEV %s failed to receive %p: %s\n", - netdev->name, iobuf, strerror ( rc ) ); - - /* Discard packet */ - free_iob ( iobuf ); - - /* Update statistics counter */ - netdev_record_stat ( &netdev->rx_stats, rc ); -} - -/** - * Poll for completed and received packets on network device - * - * @v netdev Network device - * - * Polls the network device for completed transmissions and received - * packets. Any received packets will be added to the RX packet queue - * via netdev_rx(). - */ -void netdev_poll ( struct net_device *netdev ) { - - if ( netdev_is_open ( netdev ) ) - netdev->op->poll ( netdev ); -} - -/** - * Remove packet from device's receive queue - * - * @v netdev Network device - * @ret iobuf I/O buffer, or NULL - * - * Removes the first packet from the device's RX queue and returns it. - * Ownership of the packet is transferred to the caller. - */ -struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ) { - struct io_buffer *iobuf; - - iobuf = list_first_entry ( &netdev->rx_queue, struct io_buffer, list ); - if ( ! iobuf ) - return NULL; - - list_del ( &iobuf->list ); - return iobuf; -} - -/** - * Flush device's receive queue - * - * @v netdev Network device - */ -static void netdev_rx_flush ( struct net_device *netdev ) { - struct io_buffer *iobuf; - - /* Discard any packets in the RX queue */ - while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) { - netdev_rx_err ( netdev, iobuf, -ECANCELED ); - } -} - -/** - * Finish network device configuration - * - * @v config Network device configuration - * @v rc Reason for completion - */ -static void netdev_config_close ( struct net_device_configuration *config, - int rc ) { - struct net_device_configurator *configurator = config->configurator; - struct net_device *netdev = config->netdev; - - /* Restart interface */ - intf_restart ( &config->job, rc ); - - /* Record configuration result */ - config->rc = rc; - if ( rc == 0 ) { - DBGC ( netdev, "NETDEV %s configured via %s\n", - netdev->name, configurator->name ); - } else { - DBGC ( netdev, "NETDEV %s configuration via %s failed: %s\n", - netdev->name, configurator->name, strerror ( rc ) ); - } -} - -/** Network device configuration interface operations */ -static struct interface_operation netdev_config_ops[] = { - INTF_OP ( intf_close, struct net_device_configuration *, - netdev_config_close ), -}; - -/** Network device configuration interface descriptor */ -static struct interface_descriptor netdev_config_desc = - INTF_DESC ( struct net_device_configuration, job, netdev_config_ops ); - -/** - * Free network device - * - * @v refcnt Network device reference counter - */ -static void free_netdev ( struct refcnt *refcnt ) { - struct net_device *netdev = - container_of ( refcnt, struct net_device, refcnt ); - - stop_timer ( &netdev->link_block ); - netdev_tx_flush ( netdev ); - netdev_rx_flush ( netdev ); - clear_settings ( netdev_settings ( netdev ) ); - free ( netdev ); -} - -/** - * Allocate network device - * - * @v priv_len Length of private data area (net_device::priv) - * @ret netdev Network device, or NULL - * - * Allocates space for a network device and its private data area. - */ -struct net_device * alloc_netdev ( size_t priv_len ) { - struct net_device *netdev; - struct net_device_configurator *configurator; - struct net_device_configuration *config; - unsigned int num_configs; - size_t confs_len; - size_t total_len; - - num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); - confs_len = ( num_configs * sizeof ( netdev->configs[0] ) ); - total_len = ( sizeof ( *netdev ) + confs_len + priv_len ); - netdev = zalloc ( total_len ); - if ( netdev ) { - ref_init ( &netdev->refcnt, free_netdev ); - netdev->link_rc = -EUNKNOWN_LINK_STATUS; - timer_init ( &netdev->link_block, netdev_link_block_expired, - &netdev->refcnt ); - INIT_LIST_HEAD ( &netdev->tx_queue ); - INIT_LIST_HEAD ( &netdev->tx_deferred ); - INIT_LIST_HEAD ( &netdev->rx_queue ); - netdev_settings_init ( netdev ); - config = netdev->configs; - for_each_table_entry ( configurator, NET_DEVICE_CONFIGURATORS ){ - config->netdev = netdev; - config->configurator = configurator; - config->rc = -EUNUSED_CONFIG; - intf_init ( &config->job, &netdev_config_desc, - &netdev->refcnt ); - config++; - } - netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) + - confs_len ); - } - return netdev; -} - -/** - * Register network device - * - * @v netdev Network device - * @ret rc Return status code - * - * Gives the network device a name and adds it to the list of network - * devices. - */ -int register_netdev ( struct net_device *netdev ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct net_driver *driver; - struct net_device *duplicate; - uint32_t seed; - int rc; - - /* Set initial link-layer address, if not already set */ - if ( ! netdev_has_ll_addr ( netdev ) ) { - ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr ); - } - - /* Reject network devices that are already available via a - * different hardware device. - */ - duplicate = find_netdev_by_ll_addr ( ll_protocol, netdev->ll_addr ); - if ( duplicate && ( duplicate->dev != netdev->dev ) ) { - DBGC ( netdev, "NETDEV rejecting duplicate (phys %s) of %s " - "(phys %s)\n", netdev->dev->name, duplicate->name, - duplicate->dev->name ); - rc = -EEXIST; - goto err_duplicate; - } - - /* Record device index and create device name */ - if ( netdev->name[0] == '\0' ) { - snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", - netdev_index ); - } - netdev->index = ++netdev_index; - - /* Use least significant bits of the link-layer address to - * improve the randomness of the (non-cryptographic) random - * number generator. - */ - memcpy ( &seed, ( netdev->ll_addr + ll_protocol->ll_addr_len - - sizeof ( seed ) ), sizeof ( seed ) ); - srand ( rand() ^ seed ); - - /* Add to device list */ - netdev_get ( netdev ); - list_add_tail ( &netdev->list, &net_devices ); - DBGC ( netdev, "NETDEV %s registered (phys %s hwaddr %s)\n", - netdev->name, netdev->dev->name, - netdev_addr ( netdev ) ); - - /* Register per-netdev configuration settings */ - if ( ( rc = register_settings ( netdev_settings ( netdev ), - NULL, netdev->name ) ) != 0 ) { - DBGC ( netdev, "NETDEV %s could not register settings: %s\n", - netdev->name, strerror ( rc ) ); - goto err_register_settings; - } - - /* Probe device */ - for_each_table_entry ( driver, NET_DRIVERS ) { - if ( driver->probe && ( rc = driver->probe ( netdev ) ) != 0 ) { - DBGC ( netdev, "NETDEV %s could not add %s device: " - "%s\n", netdev->name, driver->name, - strerror ( rc ) ); - goto err_probe; - } - } - - return 0; - - err_probe: - for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) { - if ( driver->remove ) - driver->remove ( netdev ); - } - clear_settings ( netdev_settings ( netdev ) ); - unregister_settings ( netdev_settings ( netdev ) ); - err_register_settings: - err_duplicate: - return rc; -} - -/** - * Open network device - * - * @v netdev Network device - * @ret rc Return status code - */ -int netdev_open ( struct net_device *netdev ) { - int rc; - - /* Do nothing if device is already open */ - if ( netdev->state & NETDEV_OPEN ) - return 0; - - DBGC ( netdev, "NETDEV %s opening\n", netdev->name ); - - /* Mark as opened */ - netdev->state |= NETDEV_OPEN; - - /* Open the device */ - if ( ( rc = netdev->op->open ( netdev ) ) != 0 ) - goto err; - - /* Add to head of open devices list */ - list_add ( &netdev->open_list, &open_net_devices ); - - /* Notify drivers of device state change */ - netdev_notify ( netdev ); - - return 0; - - err: - netdev->state &= ~NETDEV_OPEN; - return rc; -} - -/** - * Close network device - * - * @v netdev Network device - */ -void netdev_close ( struct net_device *netdev ) { - unsigned int num_configs; - unsigned int i; - - /* Do nothing if device is already closed */ - if ( ! ( netdev->state & NETDEV_OPEN ) ) - return; - - DBGC ( netdev, "NETDEV %s closing\n", netdev->name ); - - /* Terminate any ongoing configurations. Use intf_close() - * rather than intf_restart() to allow the cancellation to be - * reported back to us if a configuration is actually in - * progress. - */ - num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); - for ( i = 0 ; i < num_configs ; i++ ) - intf_close ( &netdev->configs[i].job, -ECANCELED ); - - /* Remove from open devices list */ - list_del ( &netdev->open_list ); - - /* Mark as closed */ - netdev->state &= ~NETDEV_OPEN; - - /* Notify drivers of device state change */ - netdev_notify ( netdev ); - - /* Close the device */ - netdev->op->close ( netdev ); - - /* Flush TX and RX queues */ - netdev_tx_flush ( netdev ); - netdev_rx_flush ( netdev ); -} - -/** - * Unregister network device - * - * @v netdev Network device - * - * Removes the network device from the list of network devices. - */ -void unregister_netdev ( struct net_device *netdev ) { - struct net_driver *driver; - - /* Ensure device is closed */ - netdev_close ( netdev ); - - /* Remove device */ - for_each_table_entry_reverse ( driver, NET_DRIVERS ) { - if ( driver->remove ) - driver->remove ( netdev ); - } - - /* Unregister per-netdev configuration settings */ - clear_settings ( netdev_settings ( netdev ) ); - unregister_settings ( netdev_settings ( netdev ) ); - - /* Remove from device list */ - DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name ); - list_del ( &netdev->list ); - netdev_put ( netdev ); - - /* Reset network device index if no devices remain */ - if ( list_empty ( &net_devices ) ) - netdev_index = 0; -} - -/** Enable or disable interrupts - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -void netdev_irq ( struct net_device *netdev, int enable ) { - - /* Do nothing if device does not support interrupts */ - if ( ! netdev_irq_supported ( netdev ) ) - return; - - /* Enable or disable device interrupts */ - netdev->op->irq ( netdev, enable ); - - /* Record interrupt enabled state */ - netdev->state &= ~NETDEV_IRQ_ENABLED; - if ( enable ) - netdev->state |= NETDEV_IRQ_ENABLED; -} - -/** - * Get network device by name - * - * @v name Network device name - * @ret netdev Network device, or NULL - */ -struct net_device * find_netdev ( const char *name ) { - struct net_device *netdev; - - /* Allow "netX" shortcut */ - if ( strcmp ( name, "netX" ) == 0 ) - return last_opened_netdev(); - - /* Identify network device by name */ - list_for_each_entry ( netdev, &net_devices, list ) { - if ( strcmp ( netdev->name, name ) == 0 ) - return netdev; - } - - return NULL; -} - -/** - * Get network device by index - * - * @v index Network device index - * @ret netdev Network device, or NULL - */ -struct net_device * find_netdev_by_index ( unsigned int index ) { - struct net_device *netdev; - - /* Identify network device by index */ - list_for_each_entry ( netdev, &net_devices, list ) { - if ( netdev->index == index ) - return netdev; - } - - return NULL; -} - -/** - * Get network device by PCI bus:dev.fn address - * - * @v bus_type Bus type - * @v location Bus location - * @ret netdev Network device, or NULL - */ -struct net_device * find_netdev_by_location ( unsigned int bus_type, - unsigned int location ) { - struct net_device *netdev; - - list_for_each_entry ( netdev, &net_devices, list ) { - if ( ( netdev->dev->desc.bus_type == bus_type ) && - ( netdev->dev->desc.location == location ) ) - return netdev; - } - - return NULL; -} - -/** - * Get network device by link-layer address - * - * @v ll_protocol Link-layer protocol - * @v ll_addr Link-layer address - * @ret netdev Network device, or NULL - */ -struct net_device * find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, - const void *ll_addr ) { - struct net_device *netdev; - - list_for_each_entry ( netdev, &net_devices, list ) { - if ( ( netdev->ll_protocol == ll_protocol ) && - ( memcmp ( netdev->ll_addr, ll_addr, - ll_protocol->ll_addr_len ) == 0 ) ) - return netdev; - } - - return NULL; -} - -/** - * Get most recently opened network device - * - * @ret netdev Most recently opened network device, or NULL - */ -struct net_device * last_opened_netdev ( void ) { - struct net_device *netdev; - - netdev = list_first_entry ( &open_net_devices, struct net_device, - open_list ); - if ( ! netdev ) - return NULL; - - assert ( netdev_is_open ( netdev ) ); - return netdev; -} - -/** - * Transmit network-layer packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v net_protocol Network-layer protocol - * @v ll_dest Destination link-layer address - * @v ll_source Source link-layer address - * @ret rc Return status code - * - * Prepends link-layer headers to the I/O buffer and transmits the - * packet via the specified network device. This function takes - * ownership of the I/O buffer. - */ -int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, - struct net_protocol *net_protocol, const void *ll_dest, - const void *ll_source ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - int rc; - - /* Add link-layer header */ - if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest, ll_source, - net_protocol->net_proto ) ) != 0 ) { - /* Record error for diagnosis */ - netdev_tx_err ( netdev, iobuf, rc ); - return rc; - } - - /* Transmit packet */ - return netdev_tx ( netdev, iobuf ); -} - -/** - * Process received network-layer packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v net_proto Network-layer protocol, in network-byte order - * @v ll_dest Destination link-layer address - * @v ll_source Source link-layer address - * @v flags Packet flags - * @ret rc Return status code - */ -int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, - uint16_t net_proto, const void *ll_dest, const void *ll_source, - unsigned int flags ) { - struct net_protocol *net_protocol; - - /* Hand off to network-layer protocol, if any */ - for_each_table_entry ( net_protocol, NET_PROTOCOLS ) { - if ( net_protocol->net_proto == net_proto ) - return net_protocol->rx ( iobuf, netdev, ll_dest, - ll_source, flags ); - } - - DBGC ( netdev, "NETDEV %s unknown network protocol %04x\n", - netdev->name, ntohs ( net_proto ) ); - free_iob ( iobuf ); - return -ENOTSUP; -} - -/** - * Poll the network stack - * - * This polls all interfaces for received packets, and processes - * packets from the RX queue. - */ -void net_poll ( void ) { - struct net_device *netdev; - struct io_buffer *iobuf; - struct ll_protocol *ll_protocol; - const void *ll_dest; - const void *ll_source; - uint16_t net_proto; - unsigned int flags; - int rc; - - /* Poll and process each network device */ - list_for_each_entry ( netdev, &net_devices, list ) { - - /* Poll for new packets */ - profile_start ( &net_poll_profiler ); - netdev_poll ( netdev ); - profile_stop ( &net_poll_profiler ); - - /* Leave received packets on the queue if receive - * queue processing is currently frozen. This will - * happen when the raw packets are to be manually - * dequeued using netdev_rx_dequeue(), rather than - * processed via the usual networking stack. - */ - if ( netdev_rx_frozen ( netdev ) ) - continue; - - /* Process all received packets */ - while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) { - - DBGC2 ( netdev, "NETDEV %s processing %p (%p+%zx)\n", - netdev->name, iobuf, iobuf->data, - iob_len ( iobuf ) ); - profile_start ( &net_rx_profiler ); - - /* Remove link-layer header */ - ll_protocol = netdev->ll_protocol; - if ( ( rc = ll_protocol->pull ( netdev, iobuf, - &ll_dest, &ll_source, - &net_proto, - &flags ) ) != 0 ) { - free_iob ( iobuf ); - continue; - } - - /* Hand packet to network layer */ - if ( ( rc = net_rx ( iob_disown ( iobuf ), netdev, - net_proto, ll_dest, - ll_source, flags ) ) != 0 ) { - /* Record error for diagnosis */ - netdev_rx_err ( netdev, NULL, rc ); - } - profile_stop ( &net_rx_profiler ); - } - } -} - -/** - * Single-step the network stack - * - * @v process Network stack process - */ -static void net_step ( struct process *process __unused ) { - net_poll(); -} - -/** - * Get the VLAN tag (when VLAN support is not present) - * - * @v netdev Network device - * @ret tag 0, indicating that device is not a VLAN device - */ -__weak unsigned int vlan_tag ( struct net_device *netdev __unused ) { - return 0; -} - -/** - * Identify VLAN device (when VLAN support is not present) - * - * @v trunk Trunk network device - * @v tag VLAN tag - * @ret netdev VLAN device, if any - */ -__weak struct net_device * vlan_find ( struct net_device *trunk __unused, - unsigned int tag __unused ) { - return NULL; -} - -/** Networking stack process */ -PERMANENT_PROCESS ( net_process, net_step ); - -/** - * Discard some cached network device data - * - * @ret discarded Number of cached items discarded - */ -static unsigned int net_discard ( void ) { - struct net_device *netdev; - struct io_buffer *iobuf; - unsigned int discarded = 0; - - /* Try to drop one deferred TX packet from each network device */ - for_each_netdev ( netdev ) { - if ( ( iobuf = list_first_entry ( &netdev->tx_deferred, - struct io_buffer, - list ) ) != NULL ) { - - /* Discard first deferred packet */ - list_del ( &iobuf->list ); - free_iob ( iobuf ); - - /* Report discard */ - discarded++; - } - } - - return discarded; -} - -/** Network device cache discarder */ -struct cache_discarder net_discarder __cache_discarder ( CACHE_NORMAL ) = { - .discard = net_discard, -}; - -/** - * Find network device configurator - * - * @v name Name - * @ret configurator Network device configurator, or NULL - */ -struct net_device_configurator * find_netdev_configurator ( const char *name ) { - struct net_device_configurator *configurator; - - for_each_table_entry ( configurator, NET_DEVICE_CONFIGURATORS ) { - if ( strcmp ( configurator->name, name ) == 0 ) - return configurator; - } - return NULL; -} - -/** - * Start network device configuration - * - * @v netdev Network device - * @v configurator Network device configurator - * @ret rc Return status code - */ -int netdev_configure ( struct net_device *netdev, - struct net_device_configurator *configurator ) { - struct net_device_configuration *config = - netdev_configuration ( netdev, configurator ); - int rc; - - /* Check applicability of configurator */ - if ( ! netdev_configurator_applies ( netdev, configurator ) ) { - DBGC ( netdev, "NETDEV %s does not support configuration via " - "%s\n", netdev->name, configurator->name ); - return -ENOTSUP; - } - - /* Terminate any ongoing configuration */ - intf_restart ( &config->job, -ECANCELED ); - - /* Mark configuration as being in progress */ - config->rc = -EINPROGRESS_CONFIG; - - DBGC ( netdev, "NETDEV %s starting configuration via %s\n", - netdev->name, configurator->name ); - - /* Start configuration */ - if ( ( rc = configurator->start ( &config->job, netdev ) ) != 0 ) { - DBGC ( netdev, "NETDEV %s could not start configuration via " - "%s: %s\n", netdev->name, configurator->name, - strerror ( rc ) ); - config->rc = rc; - return rc; - } - - return 0; -} - -/** - * Start network device configuration via all supported configurators - * - * @v netdev Network device - * @ret rc Return status code - */ -int netdev_configure_all ( struct net_device *netdev ) { - struct net_device_configurator *configurator; - int rc; - - /* Start configuration for each configurator */ - for_each_table_entry ( configurator, NET_DEVICE_CONFIGURATORS ) { - - /* Skip any inapplicable configurators */ - if ( ! netdev_configurator_applies ( netdev, configurator ) ) - continue; - - /* Start configuration */ - if ( ( rc = netdev_configure ( netdev, configurator ) ) != 0 ) - return rc; - } - - return 0; -} - -/** - * Check if network device has a configuration with a specified status code - * - * @v netdev Network device - * @v rc Status code - * @ret has_rc Network device has a configuration with this status code - */ -static int netdev_has_configuration_rc ( struct net_device *netdev, int rc ) { - unsigned int num_configs; - unsigned int i; - - num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); - for ( i = 0 ; i < num_configs ; i++ ) { - if ( netdev->configs[i].rc == rc ) - return 1; - } - return 0; -} - -/** - * Check if network device configuration is in progress - * - * @v netdev Network device - * @ret is_in_progress Network device configuration is in progress - */ -int netdev_configuration_in_progress ( struct net_device *netdev ) { - - return netdev_has_configuration_rc ( netdev, -EINPROGRESS_CONFIG ); -} - -/** - * Check if network device has at least one successful configuration - * - * @v netdev Network device - * @v configurator Configurator - * @ret rc Return status code - */ -int netdev_configuration_ok ( struct net_device *netdev ) { - - return netdev_has_configuration_rc ( netdev, 0 ); -} diff --git a/qemu/roms/ipxe/src/net/nullnet.c b/qemu/roms/ipxe/src/net/nullnet.c deleted file mode 100644 index 2948b38c0..000000000 --- a/qemu/roms/ipxe/src/net/nullnet.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <errno.h> -#include <ipxe/iobuf.h> -#include <ipxe/netdevice.h> - -/** @file - * - * Null network device - * - */ - -static int null_open ( struct net_device *netdev __unused ) { - return -ENODEV; -}; - -static void null_close ( struct net_device *netdev __unused ) { - /* Do nothing */ -}; - -static int null_transmit ( struct net_device *netdev __unused, - struct io_buffer *iobuf __unused ) { - return -ENODEV; -}; - -static void null_poll ( struct net_device *netdev __unused ) { - /* Do nothing */ -} - -static void null_irq ( struct net_device *netdev __unused, - int enable __unused ) { - /* Do nothing */ -} - -struct net_device_operations null_netdev_operations = { - .open = null_open, - .close = null_close, - .transmit = null_transmit, - .poll = null_poll, - .irq = null_irq, -}; diff --git a/qemu/roms/ipxe/src/net/oncrpc/mount.c b/qemu/roms/ipxe/src/net/oncrpc/mount.c deleted file mode 100644 index 8838a147c..000000000 --- a/qemu/roms/ipxe/src/net/oncrpc/mount.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <libgen.h> -#include <byteswap.h> -#include <ipxe/time.h> -#include <ipxe/iobuf.h> -#include <ipxe/open.h> -#include <ipxe/features.h> -#include <ipxe/oncrpc.h> -#include <ipxe/oncrpc_iob.h> -#include <ipxe/nfs.h> -#include <ipxe/mount.h> - -/** @file - * - * NFS MOUNT protocol - * - */ - -/** MNT procedure number */ -#define MOUNT_MNT 1 -/** UMNT procedure number */ -#define MOUNT_UMNT 3 - -/** - * Send a MNT request - * - * @v intf Interface to send the request on - * @v session ONC RPC session - * @v mountpoinrt The path of the directory to mount. - * @ret rc Return status code - */ -int mount_mnt ( struct interface *intf, struct oncrpc_session *session, - const char *mountpoint ) { - struct oncrpc_field fields[] = { - ONCRPC_FIELD ( str, mountpoint ), - ONCRPC_FIELD_END, - }; - - return oncrpc_call ( intf, session, MOUNT_MNT, fields ); -} - -/** - * Send a UMNT request - * - * @v intf Interface to send the request on - * @v session ONC RPC session - * @v mountpoinrt The path of the directory to unmount. - * @ret rc Return status code - */ -int mount_umnt ( struct interface *intf, struct oncrpc_session *session, - const char *mountpoint ) { - struct oncrpc_field fields[] = { - ONCRPC_FIELD ( str, mountpoint ), - ONCRPC_FIELD_END, - }; - - return oncrpc_call ( intf, session, MOUNT_UMNT, fields ); -} - -/** - * Parse an MNT reply - * - * @v mnt_reply A structure where the data will be saved - * @v reply The ONC RPC reply to get data from - * @ret rc Return status code - */ -int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, - struct oncrpc_reply *reply ) { - if ( ! mnt_reply || ! reply ) - return -EINVAL; - - mnt_reply->status = oncrpc_iob_get_int ( reply->data ); - - switch ( mnt_reply->status ) - { - case MNT3_OK: - break; - case MNT3ERR_NOENT: - return -ENOENT; - case MNT3ERR_IO: - return -EIO; - case MNT3ERR_ACCES: - return -EACCES; - case MNT3ERR_NOTDIR: - return -ENOTDIR; - case MNT3ERR_NAMETOOLONG: - return -ENAMETOOLONG; - default: - return -EPROTO; - } - - nfs_iob_get_fh ( reply->data, &mnt_reply->fh ); - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/oncrpc/nfs.c b/qemu/roms/ipxe/src/net/oncrpc/nfs.c deleted file mode 100644 index b6118f91a..000000000 --- a/qemu/roms/ipxe/src/net/oncrpc/nfs.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <libgen.h> -#include <byteswap.h> -#include <ipxe/time.h> -#include <ipxe/iobuf.h> -#include <ipxe/open.h> -#include <ipxe/features.h> -#include <ipxe/nfs.h> -#include <ipxe/oncrpc.h> -#include <ipxe/oncrpc_iob.h> -#include <ipxe/portmap.h> -#include <ipxe/mount.h> -#include <ipxe/settings.h> - -/** @file - * - * Network File System protocol - * - */ - -/** NFS LOOKUP procedure */ -#define NFS_LOOKUP 3 -/** NFS READLINK procedure */ -#define NFS_READLINK 5 -/** NFS READ procedure */ -#define NFS_READ 6 - -/** - * Extract a file handle from the beginning of an I/O buffer - * - * @v io_buf I/O buffer - * @v fh File handle - * @ret size Size of the data read - */ -size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ) { - fh->size = oncrpc_iob_get_int ( io_buf ); - - if ( fh->size > 64 ) - return sizeof ( uint32_t ); - - memcpy (fh->fh, io_buf->data, fh->size ); - iob_pull ( io_buf, fh->size ); - - return fh->size + sizeof ( uint32_t ); -} - -/** - * Add a file handle to the end of an I/O buffer - * - * @v io_buf I/O buffer - * @v fh File handle - * @ret size Size of the data written - */ -size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ) { - size_t s; - - s = oncrpc_iob_add_int ( io_buf, fh->size ); - memcpy ( iob_put ( io_buf, fh->size ), &fh->fh, fh->size ); - - return s + fh->size; -} - -/** - * Send a LOOKUP request - * - * @v intf Interface to send the request on - * @v session ONC RPC session - * @v fh The file handle of the the directory - * @v filename The file name - * @ret rc Return status code - */ -int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, - const struct nfs_fh *fh, const char *filename ) { - struct oncrpc_field fields[] = { - ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), - ONCRPC_FIELD ( str, filename ), - ONCRPC_FIELD_END, - }; - - return oncrpc_call ( intf, session, NFS_LOOKUP, fields ); -} - -/** - * Send a READLINK request - * - * @v intf Interface to send the request on - * @v session ONC RPC session - * @v fh The symlink file handle - * @ret rc Return status code - */ -int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, - const struct nfs_fh *fh ) { - struct oncrpc_field fields[] = { - ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), - ONCRPC_FIELD_END, - }; - - return oncrpc_call ( intf, session, NFS_READLINK, fields ); -} - -/** - * Send a READ request - * - * @v intf Interface to send the request on - * @v session ONC RPC session - * @v fh The file handle - * @v offset Offset - * @v count Byte count - * @ret rc Return status code - */ -int nfs_read ( struct interface *intf, struct oncrpc_session *session, - const struct nfs_fh *fh, uint64_t offset, uint32_t count ) { - struct oncrpc_field fields[] = { - ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), - ONCRPC_FIELD ( int64, offset ), - ONCRPC_FIELD ( int32, count ), - ONCRPC_FIELD_END, - }; - - return oncrpc_call ( intf, session, NFS_READ, fields ); -} - -/** - * Parse a LOOKUP reply - * - * @v lookup_reply A structure where the data will be saved - * @v reply The ONC RPC reply to get data from - * @ret rc Return status code - */ -int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, - struct oncrpc_reply *reply ) { - if ( ! lookup_reply || ! reply ) - return -EINVAL; - - lookup_reply->status = oncrpc_iob_get_int ( reply->data ); - switch ( lookup_reply->status ) - { - case NFS3_OK: - break; - case NFS3ERR_PERM: - return -EPERM; - case NFS3ERR_NOENT: - return -ENOENT; - case NFS3ERR_IO: - return -EIO; - case NFS3ERR_ACCES: - return -EACCES; - case NFS3ERR_NOTDIR: - return -ENOTDIR; - case NFS3ERR_NAMETOOLONG: - return -ENAMETOOLONG; - case NFS3ERR_STALE: - return -ESTALE; - case NFS3ERR_BADHANDLE: - case NFS3ERR_SERVERFAULT: - default: - return -EPROTO; - } - - nfs_iob_get_fh ( reply->data, &lookup_reply->fh ); - - if ( oncrpc_iob_get_int ( reply->data ) == 1 ) - lookup_reply->ent_type = oncrpc_iob_get_int ( reply->data ); - - return 0; -} -/** - * Parse a READLINK reply - * - * @v readlink_reply A structure where the data will be saved - * @v reply The ONC RPC reply to get data from - * @ret rc Return status code - */ -int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, - struct oncrpc_reply *reply ) { - if ( ! readlink_reply || ! reply ) - return -EINVAL; - - readlink_reply->status = oncrpc_iob_get_int ( reply->data ); - switch ( readlink_reply->status ) - { - case NFS3_OK: - break; - case NFS3ERR_IO: - return -EIO; - case NFS3ERR_ACCES: - return -EACCES; - case NFS3ERR_INVAL: - return -EINVAL; - case NFS3ERR_NOTSUPP: - return -ENOTSUP; - case NFS3ERR_STALE: - return -ESTALE; - case NFS3ERR_BADHANDLE: - case NFS3ERR_SERVERFAULT: - default: - return -EPROTO; - } - - if ( oncrpc_iob_get_int ( reply->data ) == 1 ) - iob_pull ( reply->data, 5 * sizeof ( uint32_t ) + - 8 * sizeof ( uint64_t ) ); - - readlink_reply->path_len = oncrpc_iob_get_int ( reply->data ); - readlink_reply->path = reply->data->data; - - return 0; -} - -/** - * Parse a READ reply - * - * @v read_reply A structure where the data will be saved - * @v reply The ONC RPC reply to get data from - * @ret rc Return status code - */ -int nfs_get_read_reply ( struct nfs_read_reply *read_reply, - struct oncrpc_reply *reply ) { - if ( ! read_reply || ! reply ) - return -EINVAL; - - read_reply->status = oncrpc_iob_get_int ( reply->data ); - switch ( read_reply->status ) - { - case NFS3_OK: - break; - case NFS3ERR_PERM: - return -EPERM; - case NFS3ERR_NOENT: - return -ENOENT; - case NFS3ERR_IO: - return -EIO; - case NFS3ERR_NXIO: - return -ENXIO; - case NFS3ERR_ACCES: - return -EACCES; - case NFS3ERR_INVAL: - return -EINVAL; - case NFS3ERR_STALE: - return -ESTALE; - case NFS3ERR_BADHANDLE: - case NFS3ERR_SERVERFAULT: - default: - return -EPROTO; - } - - if ( oncrpc_iob_get_int ( reply->data ) == 1 ) - { - iob_pull ( reply->data, 5 * sizeof ( uint32_t ) ); - read_reply->filesize = oncrpc_iob_get_int64 ( reply->data ); - iob_pull ( reply->data, 7 * sizeof ( uint64_t ) ); - } - - read_reply->count = oncrpc_iob_get_int ( reply->data ); - read_reply->eof = oncrpc_iob_get_int ( reply->data ); - read_reply->data_len = oncrpc_iob_get_int ( reply->data ); - read_reply->data = reply->data->data; - - if ( read_reply->count != read_reply->data_len ) - return -EPROTO; - - return 0; -} - diff --git a/qemu/roms/ipxe/src/net/oncrpc/nfs_open.c b/qemu/roms/ipxe/src/net/oncrpc/nfs_open.c deleted file mode 100644 index c0dceb82f..000000000 --- a/qemu/roms/ipxe/src/net/oncrpc/nfs_open.c +++ /dev/null @@ -1,683 +0,0 @@ -/* - * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <libgen.h> -#include <byteswap.h> -#include <ipxe/time.h> -#include <ipxe/socket.h> -#include <ipxe/tcpip.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/features.h> -#include <ipxe/nfs.h> -#include <ipxe/nfs_open.h> -#include <ipxe/oncrpc.h> -#include <ipxe/oncrpc_iob.h> -#include <ipxe/portmap.h> -#include <ipxe/mount.h> -#include <ipxe/nfs_uri.h> - -/** @file - * - * Network File System protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 ); - -#define NFS_RSIZE 100000 - -enum nfs_pm_state { - NFS_PORTMAP_NONE = 0, - NFS_PORTMAP_MOUNTPORT, - NFS_PORTMAP_NFSPORT, - MFS_PORTMAP_CLOSED, -}; - -enum nfs_mount_state { - NFS_MOUNT_NONE = 0, - NFS_MOUNT_MNT, - NFS_MOUNT_UMNT, - NFS_MOUNT_CLOSED, -}; - -enum nfs_state { - NFS_NONE = 0, - NFS_LOOKUP, - NFS_LOOKUP_SENT, - NFS_READLINK, - NFS_READLINK_SENT, - NFS_READ, - NFS_READ_SENT, - NFS_CLOSED, -}; - -/** - * A NFS request - * - */ -struct nfs_request { - /** Reference counter */ - struct refcnt refcnt; - /** Data transfer interface */ - struct interface xfer; - - struct interface pm_intf; - struct interface mount_intf; - struct interface nfs_intf; - - enum nfs_pm_state pm_state; - enum nfs_mount_state mount_state; - enum nfs_state nfs_state; - - struct oncrpc_session pm_session; - struct oncrpc_session mount_session; - struct oncrpc_session nfs_session; - - struct oncrpc_cred_sys auth_sys; - - char * hostname; - struct nfs_uri uri; - - struct nfs_fh readlink_fh; - struct nfs_fh current_fh; - uint64_t file_offset; - - size_t remaining; - int eof; -}; - -static void nfs_step ( struct nfs_request *nfs ); - -/** - * Free NFS request - * - * @v refcnt Reference counter - */ -static void nfs_free ( struct refcnt *refcnt ) { - struct nfs_request *nfs; - - nfs = container_of ( refcnt, struct nfs_request, refcnt ); - DBGC ( nfs, "NFS_OPEN %p freed\n", nfs ); - - nfs_uri_free ( &nfs->uri ); - - free ( nfs->hostname ); - free ( nfs->auth_sys.hostname ); - free ( nfs ); -} - -/** - * Mark NFS operation as complete - * - * @v nfs NFS request - * @v rc Return status code - */ -static void nfs_done ( struct nfs_request *nfs, int rc ) { - if ( rc == 0 && nfs->nfs_state != NFS_CLOSED ) - rc = -ECONNRESET; - - DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) ); - - intf_shutdown ( &nfs->xfer, rc ); - intf_shutdown ( &nfs->pm_intf, rc ); - intf_shutdown ( &nfs->mount_intf, rc ); - intf_shutdown ( &nfs->nfs_intf, rc ); -} - -static int nfs_connect ( struct interface *intf, uint16_t port, - const char *hostname ) { - struct sockaddr_tcpip peer; - struct sockaddr_tcpip local; - - if ( ! intf || ! hostname || ! port ) - return -EINVAL; - - memset ( &peer, 0, sizeof ( peer ) ); - memset ( &local, 0, sizeof ( local ) ); - peer.st_port = htons ( port ); - - /* Use a local port < 1024 to avoid using the 'insecure' option in - * /etc/exports file. */ - local.st_flags = TCPIP_BIND_PRIVILEGED; - - return xfer_open_named_socket ( intf, SOCK_STREAM, - ( struct sockaddr * ) &peer, hostname, - ( struct sockaddr * ) &local ); -} - -static void nfs_pm_step ( struct nfs_request *nfs ) { - int rc; - - if ( ! xfer_window ( &nfs->pm_intf ) ) - return; - - if ( nfs->pm_state == NFS_PORTMAP_NONE ) { - DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs ); - - rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, - ONCRPC_MOUNT, MOUNT_VERS, - PORTMAP_PROTO_TCP ); - if ( rc != 0 ) - goto err; - - nfs->pm_state++; - return; - } - - if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { - DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs ); - - rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, - ONCRPC_NFS, NFS_VERS, - PORTMAP_PROTO_TCP ); - if ( rc != 0 ) - goto err; - - return; - } - - return; -err: - nfs_done ( nfs, rc ); -} - -static int nfs_pm_deliver ( struct nfs_request *nfs, - struct io_buffer *io_buf, - struct xfer_metadata *meta __unused ) { - int rc; - struct oncrpc_reply reply; - struct portmap_getport_reply getport_reply; - - oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf ); - if ( reply.accept_state != 0 ) - { - rc = -EPROTO; - goto err; - } - - if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) { - DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs ); - - rc = portmap_get_getport_reply ( &getport_reply, &reply ); - if ( rc != 0 ) - goto err; - - rc = nfs_connect ( &nfs->mount_intf, getport_reply.port, - nfs->hostname ); - if ( rc != 0 ) - goto err; - - nfs->pm_state++; - nfs_pm_step ( nfs ); - - goto done; - } - - if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { - DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs ); - - rc = portmap_get_getport_reply ( &getport_reply, &reply ); - if ( rc != 0 ) - goto err; - - rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port, - nfs->hostname ); - if ( rc != 0 ) - goto err; - - intf_shutdown ( &nfs->pm_intf, 0 ); - nfs->pm_state++; - - goto done; - } - - rc = -EPROTO; -err: - nfs_done ( nfs, rc ); -done: - free_iob ( io_buf ); - return 0; -} - -static void nfs_mount_step ( struct nfs_request *nfs ) { - int rc; - - if ( ! xfer_window ( &nfs->mount_intf ) ) - return; - - if ( nfs->mount_state == NFS_MOUNT_NONE ) { - DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs, - nfs_uri_mountpoint ( &nfs->uri ) ); - - rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session, - nfs_uri_mountpoint ( &nfs->uri ) ); - if ( rc != 0 ) - goto err; - - nfs->mount_state++; - return; - } - - if ( nfs->mount_state == NFS_MOUNT_UMNT ) { - DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs ); - - rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session, - nfs_uri_mountpoint ( &nfs->uri ) ); - if ( rc != 0 ) - goto err; - } - - return; -err: - nfs_done ( nfs, rc ); -} - -static int nfs_mount_deliver ( struct nfs_request *nfs, - struct io_buffer *io_buf, - struct xfer_metadata *meta __unused ) { - int rc; - struct oncrpc_reply reply; - struct mount_mnt_reply mnt_reply; - - oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf ); - if ( reply.accept_state != 0 ) - { - rc = -EPROTO; - goto err; - } - - if ( nfs->mount_state == NFS_MOUNT_MNT ) { - DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs ); - rc = mount_get_mnt_reply ( &mnt_reply, &reply ); - if ( rc != 0 ) { - switch ( mnt_reply.status ) { - case MNT3ERR_NOTDIR: - case MNT3ERR_NOENT: - case MNT3ERR_ACCES: - break; - - default: - goto err; - } - - if ( ! strcmp ( nfs_uri_mountpoint ( &nfs->uri ), - "/" ) ) - goto err; - - if ( ( rc = nfs_uri_next_mountpoint ( &nfs->uri ) ) ) - goto err; - - DBGC ( nfs, "NFS_OPEN %p MNT failed retrying with " \ - "%s\n", nfs, nfs_uri_mountpoint ( &nfs->uri ) ); - - nfs->mount_state--; - nfs_mount_step ( nfs ); - - goto done; - } - - nfs->current_fh = mnt_reply.fh; - nfs->nfs_state = NFS_LOOKUP; - nfs_step ( nfs ); - - goto done; - } - - if ( nfs->mount_state == NFS_MOUNT_UMNT ) { - DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs ); - nfs_done ( nfs, 0 ); - - goto done; - } - - rc = -EPROTO; -err: - nfs_done ( nfs, rc ); -done: - free_iob ( io_buf ); - return 0; -} - -static void nfs_step ( struct nfs_request *nfs ) { - int rc; - char *path_component; - - if ( ! xfer_window ( &nfs->nfs_intf ) ) - return; - - if ( nfs->nfs_state == NFS_LOOKUP ) { - path_component = nfs_uri_next_path_component ( &nfs->uri ); - - DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs, - path_component ); - - rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session, - &nfs->current_fh, path_component ); - if ( rc != 0 ) - goto err; - - nfs->nfs_state++; - return; - } - - - if ( nfs->nfs_state == NFS_READLINK ) { - DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs ); - - rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session, - &nfs->readlink_fh ); - if ( rc != 0 ) - goto err; - - nfs->nfs_state++; - return; - } - - if ( nfs->nfs_state == NFS_READ ) { - DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs ); - - rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session, - &nfs->current_fh, nfs->file_offset, - NFS_RSIZE ); - if ( rc != 0 ) - goto err; - - nfs->nfs_state++; - return; - } - - return; -err: - nfs_done ( nfs, rc ); -} - -static int nfs_deliver ( struct nfs_request *nfs, - struct io_buffer *io_buf, - struct xfer_metadata *meta __unused ) { - int rc; - struct oncrpc_reply reply; - - if ( nfs->remaining == 0 ) { - oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf ); - if ( reply.accept_state != 0 ) { - rc = -EPROTO; - goto err; - } - } - - if ( nfs->nfs_state == NFS_LOOKUP_SENT ) { - struct nfs_lookup_reply lookup_reply; - - DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs ); - - rc = nfs_get_lookup_reply ( &lookup_reply, &reply ); - if ( rc != 0 ) - goto err; - - if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) { - nfs->readlink_fh = lookup_reply.fh; - nfs->nfs_state = NFS_READLINK; - } else { - nfs->current_fh = lookup_reply.fh; - - if ( nfs->uri.lookup_pos[0] == '\0' ) - nfs->nfs_state = NFS_READ; - else - nfs->nfs_state--; - } - - nfs_step ( nfs ); - goto done; - } - - if ( nfs->nfs_state == NFS_READLINK_SENT ) { - char *path; - struct nfs_readlink_reply readlink_reply; - - DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs ); - - rc = nfs_get_readlink_reply ( &readlink_reply, &reply ); - if ( rc != 0 ) - goto err; - - if ( readlink_reply.path_len == 0 ) - { - rc = -EINVAL; - goto err; - } - - if ( ! ( path = strndup ( readlink_reply.path, - readlink_reply.path_len ) ) ) - { - rc = -ENOMEM; - goto err; - } - - nfs_uri_symlink ( &nfs->uri, path ); - free ( path ); - - DBGC ( nfs, "NFS_OPEN %p new path: %s\n", nfs, - nfs->uri.path ); - - nfs->nfs_state = NFS_LOOKUP; - nfs_step ( nfs ); - goto done; - } - - if ( nfs->nfs_state == NFS_READ_SENT ) { - if ( nfs->remaining == 0 ) { - DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs ); - - struct nfs_read_reply read_reply; - - rc = nfs_get_read_reply ( &read_reply, &reply ); - if ( rc != 0 ) - goto err; - - if ( nfs->file_offset == 0 ) { - DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n", - nfs, read_reply.filesize ); - - xfer_seek ( &nfs->xfer, read_reply.filesize ); - xfer_seek ( &nfs->xfer, 0 ); - } - - nfs->file_offset += read_reply.count; - nfs->remaining = read_reply.count; - nfs->eof = read_reply.eof; - } - - size_t len = iob_len ( io_buf ); - if ( len > nfs->remaining ) - iob_unput ( io_buf, len - nfs->remaining ); - - nfs->remaining -= iob_len ( io_buf ); - - DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs, - iob_len ( io_buf ) ); - - rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) ); - if ( rc != 0 ) - goto err; - - if ( nfs->remaining == 0 ) { - if ( ! nfs->eof ) { - nfs->nfs_state--; - nfs_step ( nfs ); - } else { - intf_shutdown ( &nfs->nfs_intf, 0 ); - nfs->nfs_state++; - nfs->mount_state++; - nfs_mount_step ( nfs ); - } - } - - return 0; - } - - rc = -EPROTO; -err: - nfs_done ( nfs, rc ); -done: - free_iob ( io_buf ); - return 0; -} - -/***************************************************************************** - * Interfaces - * - */ - -static struct interface_operation nfs_xfer_operations[] = { - INTF_OP ( intf_close, struct nfs_request *, nfs_done ), -}; - -/** NFS data transfer interface descriptor */ -static struct interface_descriptor nfs_xfer_desc = - INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations ); - -static struct interface_operation nfs_pm_operations[] = { - INTF_OP ( intf_close, struct nfs_request *, nfs_done ), - INTF_OP ( xfer_deliver, struct nfs_request *, nfs_pm_deliver ), - INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_pm_step ), -}; - -static struct interface_descriptor nfs_pm_desc = - INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations ); - -static struct interface_operation nfs_mount_operations[] = { - INTF_OP ( intf_close, struct nfs_request *, nfs_done ), - INTF_OP ( xfer_deliver, struct nfs_request *, nfs_mount_deliver ), - INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_mount_step ), -}; - -static struct interface_descriptor nfs_mount_desc = - INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations ); - -static struct interface_operation nfs_operations[] = { - INTF_OP ( intf_close, struct nfs_request *, nfs_done ), - INTF_OP ( xfer_deliver, struct nfs_request *, nfs_deliver ), - INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_step ), -}; - -static struct interface_descriptor nfs_desc = - INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations, - xfer ); - -/***************************************************************************** - * - * URI opener - * - */ - -static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) { - int rc; - - if ( ! uri || ! uri->host || ! uri->path ) - return -EINVAL; - - if ( ( rc = nfs_uri_init ( &nfs->uri, uri ) ) != 0 ) - return rc; - - if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) { - rc = -ENOMEM; - goto err_hostname; - } - - DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, path=%s)\n", - nfs, nfs_uri_mountpoint ( &nfs->uri), nfs->uri.path ); - - return 0; - -err_hostname: - nfs_uri_free ( &nfs->uri ); - return rc; -} - -/** - * Initiate a NFS connection - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int nfs_open ( struct interface *xfer, struct uri *uri ) { - int rc; - struct nfs_request *nfs; - - nfs = zalloc ( sizeof ( *nfs ) ); - if ( ! nfs ) - return -ENOMEM; - - rc = nfs_parse_uri( nfs, uri ); - if ( rc != 0 ) - goto err_uri; - - rc = oncrpc_init_cred_sys ( &nfs->auth_sys ); - if ( rc != 0 ) - goto err_cred; - - ref_init ( &nfs->refcnt, nfs_free ); - intf_init ( &nfs->xfer, &nfs_xfer_desc, &nfs->refcnt ); - intf_init ( &nfs->pm_intf, &nfs_pm_desc, &nfs->refcnt ); - intf_init ( &nfs->mount_intf, &nfs_mount_desc, &nfs->refcnt ); - intf_init ( &nfs->nfs_intf, &nfs_desc, &nfs->refcnt ); - - portmap_init_session ( &nfs->pm_session, &nfs->auth_sys.credential ); - mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential ); - nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential ); - - DBGC ( nfs, "NFS_OPEN %p connecting to port mapper (%s:%d)...\n", nfs, - nfs->hostname, PORTMAP_PORT ); - - rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname ); - if ( rc != 0 ) - goto err_connect; - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &nfs->xfer, xfer ); - ref_put ( &nfs->refcnt ); - - return 0; - -err_connect: - free ( nfs->auth_sys.hostname ); -err_cred: - nfs_uri_free ( &nfs->uri ); - free ( nfs->hostname ); -err_uri: - free ( nfs ); - return rc; -} - -/** NFS URI opener */ -struct uri_opener nfs_uri_opener __uri_opener = { - .scheme = "nfs", - .open = nfs_open, -}; diff --git a/qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c b/qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c deleted file mode 100644 index c4c3f21e9..000000000 --- a/qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2014 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <libgen.h> -#include <ipxe/nfs_uri.h> - -/** @file - * - * Network File System protocol URI handling functions - * - */ - -int nfs_uri_init ( struct nfs_uri *nfs_uri, const struct uri *uri ) { - if ( ! ( nfs_uri->mountpoint = strdup ( uri->path ) ) ) - return -ENOMEM; - - nfs_uri->filename = basename ( nfs_uri->mountpoint ); - if ( strchr ( uri->path, '/' ) != NULL ) - nfs_uri->mountpoint = dirname ( nfs_uri->mountpoint ); - - if ( nfs_uri->filename[0] == '\0' ) { - free ( nfs_uri->mountpoint ); - return -EINVAL; - } - - if ( ! ( nfs_uri->path = strdup ( nfs_uri->filename ) ) ) { - free ( nfs_uri->mountpoint ); - return -ENOMEM; - } - nfs_uri->lookup_pos = nfs_uri->path; - - return 0; -} - -char *nfs_uri_mountpoint ( const struct nfs_uri *uri ) { - if ( uri->mountpoint + 1 == uri->filename || - uri->mountpoint == uri->filename ) - return "/"; - - return uri->mountpoint; -} - -int nfs_uri_next_mountpoint ( struct nfs_uri *uri ) { - char *sep; - - if ( uri->mountpoint + 1 == uri->filename || - uri->mountpoint == uri->filename ) - return -ENOENT; - - sep = strrchr ( uri->mountpoint, '/' ); - uri->filename[-1] = '/'; - uri->filename = sep + 1; - *sep = '\0'; - - free ( uri->path ); - if ( ! ( uri->path = strdup ( uri->filename ) ) ) { - uri->path = NULL; - return -ENOMEM; - } - uri->lookup_pos = uri->path; - - return 0; -} - -int nfs_uri_symlink ( struct nfs_uri *uri, const char *symlink ) { - size_t len; - char *new_path; - - if ( ! uri->path ) - return -EINVAL; - - if ( *symlink == '/' ) - { - if ( strncmp ( symlink, uri->mountpoint, - strlen ( uri->mountpoint ) ) != 0 ) - return -EINVAL; - - len = strlen ( uri->lookup_pos ) + strlen ( symlink ) - \ - strlen ( uri->mountpoint ); - if ( ! ( new_path = malloc ( len * sizeof ( char ) ) ) ) - return -ENOMEM; - - strcpy ( new_path, symlink + strlen ( uri->mountpoint ) ); - strcpy ( new_path + strlen ( new_path ), uri->lookup_pos ); - - } else { - len = strlen ( uri->lookup_pos ) + strlen ( symlink ); - if ( ! ( new_path = malloc ( len * sizeof ( char ) ) ) ) - return -ENOMEM; - - - strcpy ( new_path, symlink ); - strcpy ( new_path + strlen ( new_path ), uri->lookup_pos ); - } - - free ( uri->path ); - uri->lookup_pos = uri->path = new_path; - - return 0; -} - -char *nfs_uri_next_path_component ( struct nfs_uri *uri ) { - char *sep; - char *start; - - if ( ! uri->path ) - return NULL; - - for ( sep = uri->lookup_pos ; *sep != '\0' && *sep != '/'; sep++ ) - ; - - start = uri->lookup_pos; - uri->lookup_pos = sep; - if ( *sep != '\0' ) { - uri->lookup_pos++; - *sep = '\0'; - if ( *start == '\0' ) - return nfs_uri_next_path_component ( uri ); - } - - return start; -} - -void nfs_uri_free ( struct nfs_uri *uri ) { - free ( uri->mountpoint ); - free ( uri->path ); - uri->mountpoint = NULL; - uri->path = NULL; -} diff --git a/qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c b/qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c deleted file mode 100644 index be51805e7..000000000 --- a/qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/socket.h> -#include <ipxe/tcpip.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/features.h> -#include <ipxe/oncrpc.h> -#include <ipxe/oncrpc_iob.h> - -/** @file - * - * SUN ONC RPC protocol - * - */ - -size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, - const struct oncrpc_field fields[] ) { - size_t i; - size_t s = 0; - - struct oncrpc_field f; - - if ( ! io_buf ) - return 0; - - for ( i = 0; fields[i].type != oncrpc_none; i++ ) { - f = fields[i]; - switch ( f.type ) { - case oncrpc_int32: - s += oncrpc_iob_add_int ( io_buf, f.value.int32 ); - break; - - case oncrpc_int64: - s += oncrpc_iob_add_int64 ( io_buf, f.value.int64 ); - break; - - case oncrpc_str: - s += oncrpc_iob_add_string ( io_buf, f.value.str ); - break; - - case oncrpc_array: - s += oncrpc_iob_add_array ( io_buf, - f.value.array.length, - f.value.array.ptr ); - break; - - case oncrpc_intarray: - s += oncrpc_iob_add_intarray ( io_buf, - f.value.intarray.length, - f.value.intarray.ptr ); - break; - - case oncrpc_cred: - s += oncrpc_iob_add_cred ( io_buf, f.value.cred); - break; - - default: - return s; - } - } - - return s; -} - -/** - * Add an array of bytes to the end of an I/O buffer - * - * @v io_buf I/O buffer - * @v val String - * @ret size Size of the data written - * - * In the ONC RPC protocol, every data is four byte paded, we add padding when - * necessary by using oncrpc_align() - */ -size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, - const void *data ) { - size_t padding = oncrpc_align ( length ) - length; - - oncrpc_iob_add_int ( io_buf, length ); - memcpy ( iob_put ( io_buf, length ), data, length ); - memset ( iob_put ( io_buf, padding ), 0, padding ); - - return length + padding + sizeof ( uint32_t ); -} - -/** - * Add an int array to the end of an I/O buffer - * - * @v io_buf I/O buffer - * @v length Length od the array - * @v val Int array - * @ret size Size of the data written - */ -size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, - const uint32_t *array ) { - size_t i; - - oncrpc_iob_add_int ( io_buf, length ); - - for ( i = 0; i < length; ++i ) - oncrpc_iob_add_int ( io_buf, array[i] ); - - return ( ( length + 1 ) * sizeof ( uint32_t ) ); -} - -/** - * Add credential information to the end of an I/O buffer - * - * @v io_buf I/O buffer - * @v cred Credential information - * @ret size Size of the data written - */ -size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, - const struct oncrpc_cred *cred ) { - struct oncrpc_cred_sys *syscred; - size_t s; - - struct oncrpc_field credfields[] = { - ONCRPC_FIELD ( int32, cred->flavor ), - ONCRPC_FIELD ( int32, cred->length ), - ONCRPC_FIELD_END, - }; - - if ( ! io_buf || ! cred ) - return 0; - - s = oncrpc_iob_add_fields ( io_buf, credfields); - - switch ( cred->flavor ) { - case ONCRPC_AUTH_NONE: - break; - - case ONCRPC_AUTH_SYS: - syscred = container_of ( cred, struct oncrpc_cred_sys, - credential ); - - struct oncrpc_field syscredfields[] = { - ONCRPC_FIELD ( int32, syscred->stamp ), - ONCRPC_FIELD ( str, syscred->hostname ), - ONCRPC_FIELD ( int32, syscred->uid ), - ONCRPC_FIELD ( int32, syscred->gid ), - ONCRPC_SUBFIELD ( intarray, syscred->aux_gid_len, - syscred->aux_gid ), - ONCRPC_FIELD_END, - }; - - s += oncrpc_iob_add_fields ( io_buf, syscredfields ); - break; - } - - return s; -} - -/** - * Get credential information from the beginning of an I/O buffer - * - * @v io_buf I/O buffer - * @v cred Struct where the information will be saved - * @ret size Size of the data read - */ -size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, - struct oncrpc_cred *cred ) { - if ( cred == NULL ) - return * ( uint32_t * ) io_buf->data; - - cred->flavor = oncrpc_iob_get_int ( io_buf ); - cred->length = oncrpc_iob_get_int ( io_buf ); - - iob_pull ( io_buf, cred->length ); - - return ( 2 * sizeof ( uint32_t ) + cred->length ); -} diff --git a/qemu/roms/ipxe/src/net/oncrpc/portmap.c b/qemu/roms/ipxe/src/net/oncrpc/portmap.c deleted file mode 100644 index df62221dc..000000000 --- a/qemu/roms/ipxe/src/net/oncrpc/portmap.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/socket.h> -#include <ipxe/tcpip.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/features.h> -#include <ipxe/timer.h> -#include <ipxe/oncrpc.h> -#include <ipxe/oncrpc_iob.h> -#include <ipxe/portmap.h> - -/** @file - * - * PORTMAPPER protocol. - * - */ - -/** PORTMAP GETPORT procedure. */ -#define PORTMAP_GETPORT 3 - -/** - * Send a GETPORT request - * - * @v intf Interface to send the request on - * @v session ONC RPC session - * @v prog ONC RPC program number - * @v vers ONC RPC rogram version number - * @v proto Protocol (TCP or UDP) - * @ret rc Return status code - */ -int portmap_getport ( struct interface *intf, struct oncrpc_session *session, - uint32_t prog, uint32_t vers, uint32_t proto ) { - struct oncrpc_field fields[] = { - ONCRPC_FIELD ( int32, prog ), - ONCRPC_FIELD ( int32, vers ), - ONCRPC_FIELD ( int32, proto ), - ONCRPC_FIELD ( int32, 0 ), /* The port field is only meaningful - in GETPORT reply */ - ONCRPC_FIELD_END, - }; - - return oncrpc_call ( intf, session, PORTMAP_GETPORT, fields ); -} - -/** - * Parse a GETPORT reply - * - * @v getport_reply A structure where the data will be saved - * @v reply The ONC RPC reply to get data from - * @ret rc Return status code - */ -int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, - struct oncrpc_reply *reply ) { - if ( ! getport_reply || ! reply ) - return -EINVAL; - - getport_reply->port = oncrpc_iob_get_int ( reply->data ); - if ( getport_reply == 0 || getport_reply->port >= 65536 ) - return -EINVAL; - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/pccrc.c b/qemu/roms/ipxe/src/net/pccrc.c deleted file mode 100644 index 4cd82cd1c..000000000 --- a/qemu/roms/ipxe/src/net/pccrc.c +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <errno.h> -#include <assert.h> -#include <ipxe/uaccess.h> -#include <ipxe/sha256.h> -#include <ipxe/sha512.h> -#include <ipxe/hmac.h> -#include <ipxe/base16.h> -#include <ipxe/pccrc.h> - -/** @file - * - * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] - * - */ - -/****************************************************************************** - * - * Utility functions - * - ****************************************************************************** - */ - -/** - * Transcribe hash value (for debugging) - * - * @v info Content information - * @v hash Hash value - * @ret string Hash value string - */ -static inline const char * -peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) { - static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ]; - size_t digestsize = info->digestsize; - - /* Sanity check */ - assert ( info != NULL ); - assert ( digestsize != 0 ); - assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) ); - - /* Transcribe hash value */ - base16_encode ( hash, digestsize, buf, sizeof ( buf ) ); - return buf; -} - -/** - * Get raw data - * - * @v info Content information - * @v data Data buffer - * @v offset Starting offset - * @v len Length - * @ret rc Return status code - */ -static int peerdist_info_get ( const struct peerdist_info *info, void *data, - size_t offset, size_t len ) { - - /* Sanity check */ - if ( ( offset > info->raw.len ) || - ( len > ( info->raw.len - offset ) ) ) { - DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n", - info, offset, ( offset + len ), info->raw.len ); - return -ERANGE; - } - - /* Copy data */ - copy_from_user ( data, info->raw.data, offset, len ); - - return 0; -} - -/** - * Populate segment hashes - * - * @v segment Content information segment to fill in - * @v hash Segment hash of data - * @v secret Segment secret - */ -static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, - const void *hash, const void *secret ){ - const struct peerdist_info *info = segment->info; - struct digest_algorithm *digest = info->digest; - uint8_t ctx[digest->ctxsize]; - size_t digestsize = info->digestsize; - size_t secretsize = digestsize; - static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC; - - /* Sanity check */ - assert ( digestsize <= sizeof ( segment->hash ) ); - assert ( digestsize <= sizeof ( segment->secret ) ); - assert ( digestsize <= sizeof ( segment->id ) ); - - /* Get segment hash of data */ - memcpy ( segment->hash, hash, digestsize ); - - /* Get segment secret */ - memcpy ( segment->secret, secret, digestsize ); - - /* Calculate segment identifier */ - hmac_init ( digest, ctx, segment->secret, &secretsize ); - assert ( secretsize == digestsize ); - hmac_update ( digest, ctx, segment->hash, digestsize ); - hmac_update ( digest, ctx, magic, sizeof ( magic ) ); - hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id ); - assert ( secretsize == digestsize ); -} - -/****************************************************************************** - * - * Content Information version 1 - * - ****************************************************************************** - */ - -/** - * Get number of blocks within a block description - * - * @v info Content information - * @v offset Block description offset - * @ret blocks Number of blocks, or negative error - */ -static int peerdist_info_v1_blocks ( const struct peerdist_info *info, - size_t offset ) { - struct peerdist_info_v1_block raw; - unsigned int blocks; - int rc; - - /* Get block description header */ - if ( ( rc = peerdist_info_get ( info, &raw, offset, - sizeof ( raw ) ) ) != 0 ) - return rc; - - /* Calculate number of blocks */ - blocks = le32_to_cpu ( raw.blocks ); - - return blocks; -} - -/** - * Locate block description - * - * @v info Content information - * @v index Segment index - * @ret offset Block description offset, or negative error - */ -static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info, - unsigned int index ) { - size_t digestsize = info->digestsize; - unsigned int i; - size_t offset; - int blocks; - int rc; - - /* Sanity check */ - assert ( index < info->segments ); - - /* Calculate offset of first block description */ - offset = ( sizeof ( struct peerdist_info_v1 ) + - ( info->segments * - sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) ); - - /* Iterate over block descriptions until we find this segment */ - for ( i = 0 ; i < index ; i++ ) { - - /* Get number of blocks */ - blocks = peerdist_info_v1_blocks ( info, offset ); - if ( blocks < 0 ) { - rc = blocks; - DBGC ( info, "PCCRC %p segment %d could not get number " - "of blocks: %s\n", info, i, strerror ( rc ) ); - return rc; - } - - /* Move to next block description */ - offset += sizeof ( peerdist_info_v1_block_t ( digestsize, - blocks ) ); - } - - return offset; -} - -/** - * Populate content information - * - * @v info Content information to fill in - * @ret rc Return status code - */ -static int peerdist_info_v1 ( struct peerdist_info *info ) { - struct peerdist_info_v1 raw; - struct peerdist_info_segment first; - struct peerdist_info_segment last; - size_t first_skip; - size_t last_skip; - size_t last_read; - int rc; - - /* Get raw header */ - if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ - DBGC ( info, "PCCRC %p could not get V1 content information: " - "%s\n", info, strerror ( rc ) ); - return rc; - } - assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) ); - - /* Determine hash algorithm */ - switch ( raw.hash ) { - case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) : - info->digest = &sha256_algorithm; - break; - case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) : - info->digest = &sha384_algorithm; - break; - case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) : - info->digest = &sha512_algorithm; - break; - default: - DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n", - info, le32_to_cpu ( raw.hash ) ); - return -ENOTSUP; - } - info->digestsize = info->digest->digestsize; - assert ( info->digest != NULL ); - DBGC2 ( info, "PCCRC %p using %s[%zd]\n", - info, info->digest->name, ( info->digestsize * 8 ) ); - - /* Calculate number of segments */ - info->segments = le32_to_cpu ( raw.segments ); - - /* Get first segment */ - if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 ) - return rc; - - /* Calculate range start offset */ - info->range.start = first.range.start; - - /* Calculate trimmed range start offset */ - first_skip = le32_to_cpu ( raw.first ); - info->trim.start = ( first.range.start + first_skip ); - - /* Get last segment */ - if ( ( rc = peerdist_info_segment ( info, &last, - ( info->segments - 1 ) ) ) != 0 ) - return rc; - - /* Calculate range end offset */ - info->range.end = last.range.end; - - /* Calculate trimmed range end offset */ - if ( raw.last ) { - /* Explicit length to include from last segment is given */ - last_read = le32_to_cpu ( raw.last ); - last_skip = ( last.index ? 0 : first_skip ); - info->trim.end = ( last.range.start + last_skip + last_read ); - } else { - /* No explicit length given: range extends to end of segment */ - info->trim.end = last.range.end; - } - - return 0; -} - -/** - * Populate content information segment - * - * @v segment Content information segment to fill in - * @ret rc Return status code - */ -static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) { - const struct peerdist_info *info = segment->info; - size_t digestsize = info->digestsize; - peerdist_info_v1_segment_t ( digestsize ) raw; - ssize_t raw_offset; - int blocks; - int rc; - - /* Sanity checks */ - assert ( segment->index < info->segments ); - - /* Get raw description */ - raw_offset = ( sizeof ( struct peerdist_info_v1 ) + - ( segment->index * sizeof ( raw ) ) ); - if ( ( rc = peerdist_info_get ( info, &raw, raw_offset, - sizeof ( raw ) ) ) != 0 ) { - DBGC ( info, "PCCRC %p segment %d could not get segment " - "description: %s\n", info, segment->index, - strerror ( rc ) ); - return rc; - } - - /* Calculate start offset of this segment */ - segment->range.start = le64_to_cpu ( raw.segment.offset ); - - /* Calculate end offset of this segment */ - segment->range.end = ( segment->range.start + - le32_to_cpu ( raw.segment.len ) ); - - /* Calculate block size of this segment */ - segment->blksize = le32_to_cpu ( raw.segment.blksize ); - - /* Locate block description for this segment */ - raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); - if ( raw_offset < 0 ) { - rc = raw_offset; - return rc; - } - - /* Get number of blocks */ - blocks = peerdist_info_v1_blocks ( info, raw_offset ); - if ( blocks < 0 ) { - rc = blocks; - DBGC ( info, "PCCRC %p segment %d could not get number of " - "blocks: %s\n", info, segment->index, strerror ( rc ) ); - return rc; - } - segment->blocks = blocks; - - /* Calculate segment hashes */ - peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); - - return 0; -} - -/** - * Populate content information block - * - * @v block Content information block to fill in - * @ret rc Return status code - */ -static int peerdist_info_v1_block ( struct peerdist_info_block *block ) { - const struct peerdist_info_segment *segment = block->segment; - const struct peerdist_info *info = segment->info; - size_t digestsize = info->digestsize; - peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw; - ssize_t raw_offset; - int rc; - - /* Sanity checks */ - assert ( block->index < segment->blocks ); - - /* Calculate start offset of this block */ - block->range.start = ( segment->range.start + - ( block->index * segment->blksize ) ); - - /* Calculate end offset of this block */ - block->range.end = ( block->range.start + segment->blksize ); - if ( block->range.end > segment->range.end ) - block->range.end = segment->range.end; - - /* Locate block description */ - raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); - if ( raw_offset < 0 ) { - rc = raw_offset; - return rc; - } - - /* Get block hash */ - raw_offset += offsetof ( typeof ( raw ), hash[block->index] ); - if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset, - digestsize ) ) != 0 ) { - DBGC ( info, "PCCRC %p segment %d block %d could not get " - "hash: %s\n", info, segment->index, block->index, - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** Content information version 1 operations */ -static struct peerdist_info_operations peerdist_info_v1_operations = { - .info = peerdist_info_v1, - .segment = peerdist_info_v1_segment, - .block = peerdist_info_v1_block, -}; - -/****************************************************************************** - * - * Content Information version 2 - * - ****************************************************************************** - */ - -/** A segment cursor */ -struct peerdist_info_v2_cursor { - /** Raw data offset */ - size_t offset; - /** Number of segments remaining within this chunk */ - unsigned int remaining; - /** Accumulated segment length */ - size_t len; -}; - -/** - * Initialise segment cursor - * - * @v cursor Segment cursor - */ -static inline void -peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) { - - /* Initialise cursor */ - cursor->offset = ( sizeof ( struct peerdist_info_v2 ) + - sizeof ( struct peerdist_info_v2_chunk ) ); - cursor->remaining = 0; - cursor->len = 0; -} - -/** - * Update segment cursor to next segment description - * - * @v info Content information - * @v offset Current offset - * @v remaining Number of segments remaining within this chunk - * @ret rc Return status code - */ -static int -peerdist_info_v2_cursor_next ( const struct peerdist_info *info, - struct peerdist_info_v2_cursor *cursor ) { - size_t digestsize = info->digestsize; - peerdist_info_v2_segment_t ( digestsize ) raw; - struct peerdist_info_v2_chunk chunk; - int rc; - - /* Get chunk description if applicable */ - if ( ! cursor->remaining ) { - - /* Get chunk description */ - if ( ( rc = peerdist_info_get ( info, &chunk, - ( cursor->offset - - sizeof ( chunk ) ), - sizeof ( chunk ) ) ) != 0 ) - return rc; - - /* Update number of segments remaining */ - cursor->remaining = ( be32_to_cpu ( chunk.len ) / - sizeof ( raw ) ); - } - - /* Get segment description header */ - if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset, - sizeof ( raw.segment ) ) ) != 0 ) - return rc; - - /* Update cursor */ - cursor->offset += sizeof ( raw ); - cursor->remaining--; - if ( ! cursor->remaining ) - cursor->offset += sizeof ( chunk ); - cursor->len += be32_to_cpu ( raw.segment.len ); - - return 0; -} - -/** - * Get number of segments and total length - * - * @v info Content information - * @v len Length to fill in - * @ret rc Number of segments, or negative error - */ -static int peerdist_info_v2_segments ( const struct peerdist_info *info, - size_t *len ) { - struct peerdist_info_v2_cursor cursor; - unsigned int segments; - int rc; - - /* Iterate over all segments */ - for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ; - cursor.offset < info->raw.len ; segments++ ) { - - /* Update segment cursor */ - if ( ( rc = peerdist_info_v2_cursor_next ( info, - &cursor ) ) != 0 ) { - DBGC ( info, "PCCRC %p segment %d could not update " - "segment cursor: %s\n", - info, segments, strerror ( rc ) ); - return rc; - } - } - - /* Record accumulated length */ - *len = cursor.len; - - return segments; -} - -/** - * Populate content information - * - * @v info Content information to fill in - * @ret rc Return status code - */ -static int peerdist_info_v2 ( struct peerdist_info *info ) { - struct peerdist_info_v2 raw; - size_t len = 0; - int segments; - int rc; - - /* Get raw header */ - if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ - DBGC ( info, "PCCRC %p could not get V2 content information: " - "%s\n", info, strerror ( rc ) ); - return rc; - } - assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) ); - - /* Determine hash algorithm */ - switch ( raw.hash ) { - case PEERDIST_INFO_V2_HASH_SHA512_TRUNC : - info->digest = &sha512_algorithm; - info->digestsize = ( 256 / 8 ); - break; - default: - DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n", - info, raw.hash ); - return -ENOTSUP; - } - assert ( info->digest != NULL ); - DBGC2 ( info, "PCCRC %p using %s[%zd]\n", - info, info->digest->name, ( info->digestsize * 8 ) ); - - /* Calculate number of segments and total length */ - segments = peerdist_info_v2_segments ( info, &len ); - if ( segments < 0 ) { - rc = segments; - DBGC ( info, "PCCRC %p could not get segment count and length: " - "%s\n", info, strerror ( rc ) ); - return rc; - } - info->segments = segments; - - /* Calculate range start offset */ - info->range.start = be64_to_cpu ( raw.offset ); - - /* Calculate trimmed range start offset */ - info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) ); - - /* Calculate range end offset */ - info->range.end = ( info->range.start + len ); - - /* Calculate trimmed range end offset */ - info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) : - info->range.end ); - - return 0; -} - -/** - * Populate content information segment - * - * @v segment Content information segment to fill in - * @ret rc Return status code - */ -static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) { - const struct peerdist_info *info = segment->info; - size_t digestsize = info->digestsize; - peerdist_info_v2_segment_t ( digestsize ) raw; - struct peerdist_info_v2_cursor cursor; - unsigned int index; - size_t len; - int rc; - - /* Sanity checks */ - assert ( segment->index < info->segments ); - - /* Iterate over all segments before the target segment */ - for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ; - index < segment->index ; index++ ) { - - /* Update segment cursor */ - if ( ( rc = peerdist_info_v2_cursor_next ( info, - &cursor ) ) != 0 ) { - DBGC ( info, "PCCRC %p segment %d could not update " - "segment cursor: %s\n", - info, index, strerror ( rc ) ); - return rc; - } - } - - /* Get raw description */ - if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset, - sizeof ( raw ) ) ) != 0 ) { - DBGC ( info, "PCCRC %p segment %d could not get segment " - "description: %s\n", - info, segment->index, strerror ( rc ) ); - return rc; - } - - /* Calculate start offset of this segment */ - segment->range.start = ( info->range.start + cursor.len ); - - /* Calculate end offset of this segment */ - len = be32_to_cpu ( raw.segment.len ); - segment->range.end = ( segment->range.start + len ); - - /* Model as a segment containing a single block */ - segment->blocks = 1; - segment->blksize = len; - - /* Calculate segment hashes */ - peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); - - return 0; -} - -/** - * Populate content information block - * - * @v block Content information block to fill in - * @ret rc Return status code - */ -static int peerdist_info_v2_block ( struct peerdist_info_block *block ) { - const struct peerdist_info_segment *segment = block->segment; - const struct peerdist_info *info = segment->info; - size_t digestsize = info->digestsize; - - /* Sanity checks */ - assert ( block->index < segment->blocks ); - - /* Model as a block covering the whole segment */ - memcpy ( &block->range, &segment->range, sizeof ( block->range ) ); - memcpy ( block->hash, segment->hash, digestsize ); - - return 0; -} - -/** Content information version 2 operations */ -static struct peerdist_info_operations peerdist_info_v2_operations = { - .block = peerdist_info_v2_block, - .segment = peerdist_info_v2_segment, - .info = peerdist_info_v2, -}; - -/****************************************************************************** - * - * Content Information - * - ****************************************************************************** - */ - -/** - * Populate content information - * - * @v data Raw data - * @v len Length of raw data - * @v info Content information to fill in - * @ret rc Return status code - */ -int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) { - union peerdist_info_version version; - int rc; - - /* Initialise structure */ - memset ( info, 0, sizeof ( *info ) ); - info->raw.data = data; - info->raw.len = len; - - /* Get version */ - if ( ( rc = peerdist_info_get ( info, &version, 0, - sizeof ( version ) ) ) != 0 ) { - DBGC ( info, "PCCRC %p could not get version: %s\n", - info, strerror ( rc ) ); - return rc; - } - DBGC2 ( info, "PCCRC %p version %d.%d\n", - info, version.major, version.minor ); - - /* Determine version */ - switch ( version.raw ) { - case cpu_to_le16 ( PEERDIST_INFO_V1 ) : - info->op = &peerdist_info_v1_operations; - break; - case cpu_to_le16 ( PEERDIST_INFO_V2 ) : - info->op = &peerdist_info_v2_operations; - break; - default: - DBGC ( info, "PCCRC %p unsupported version %d.%d\n", - info, version.major, version.minor ); - return -ENOTSUP; - } - assert ( info->op != NULL ); - assert ( info->op->info != NULL ); - - /* Populate content information */ - if ( ( rc = info->op->info ( info ) ) != 0 ) - return rc; - - DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with " - "%d segments\n", info, info->range.start, info->range.end, - info->trim.start, info->trim.end, info->segments ); - return 0; -} - -/** - * Populate content information segment - * - * @v info Content information - * @v segment Content information segment to fill in - * @v index Segment index - * @ret rc Return status code - */ -int peerdist_info_segment ( const struct peerdist_info *info, - struct peerdist_info_segment *segment, - unsigned int index ) { - int rc; - - /* Sanity checks */ - assert ( info != NULL ); - assert ( info->op != NULL ); - assert ( info->op->segment != NULL ); - if ( index >= info->segments ) { - DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n", - info, index, info->segments ); - return -ERANGE; - } - - /* Initialise structure */ - memset ( segment, 0, sizeof ( *segment ) ); - segment->info = info; - segment->index = index; - - /* Populate content information segment */ - if ( ( rc = info->op->segment ( segment ) ) != 0 ) - return rc; - - DBGC2 ( info, "PCCRC %p segment %d range [%08zx,%08zx) with %d " - "blocks\n", info, segment->index, segment->range.start, - segment->range.end, segment->blocks ); - DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index, - peerdist_info_hash_ntoa ( info, segment->hash ) ); - DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index, - peerdist_info_hash_ntoa ( info, segment->secret ) ); - DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index, - peerdist_info_hash_ntoa ( info, segment->id ) ); - return 0; -} - -/** - * Populate content information block - * - * @v segment Content information segment - * @v block Content information block to fill in - * @v index Block index - * @ret rc Return status code - */ -int peerdist_info_block ( const struct peerdist_info_segment *segment, - struct peerdist_info_block *block, - unsigned int index ) { - const struct peerdist_info *info = segment->info; - size_t start; - size_t end; - int rc; - - /* Sanity checks */ - assert ( segment != NULL ); - assert ( info != NULL ); - assert ( info->op != NULL ); - assert ( info->op->block != NULL ); - if ( index >= segment->blocks ) { - DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of " - "range\n", info, segment->index, index, segment->blocks); - return -ERANGE; - } - - /* Initialise structure */ - memset ( block, 0, sizeof ( *block ) ); - block->segment = segment; - block->index = index; - - /* Populate content information block */ - if ( ( rc = info->op->block ( block ) ) != 0 ) - return rc; - - /* Calculate trimmed range */ - start = block->range.start; - if ( start < info->trim.start ) - start = info->trim.start; - end = block->range.end; - if ( end > info->trim.end ) - end = info->trim.end; - if ( end < start ) - end = start; - block->trim.start = start; - block->trim.end = end; - - DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n", - info, segment->index, block->index, - peerdist_info_hash_ntoa ( info, block->hash ) ); - DBGC2 ( info, "PCCRC %p segment %d block %d range [%08zx,%08zx) covers " - "[%08zx,%08zx)\n", info, segment->index, block->index, - block->range.start, block->range.end, block->trim.start, - block->trim.end ); - return 0; -} diff --git a/qemu/roms/ipxe/src/net/pccrd.c b/qemu/roms/ipxe/src/net/pccrd.c deleted file mode 100644 index 04b5dd86c..000000000 --- a/qemu/roms/ipxe/src/net/pccrd.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/pccrd.h> - -/** @file - * - * Peer Content Caching and Retrieval: Discovery Protocol [MS-PCCRD] - * - * This protocol manages to ingeniously combine the excessive - * verbosity of XML with a paucity of actual information. For - * example: even in version 2.0 of the protocol it is still not - * possible to discover which peers hold a specific block within a - * given segment. - * - * For added bonus points, version 1.0 of the protocol is specified to - * use a case-sensitive string comparison (for SHA2 digest values) but - * nothing specifies whether the strings in question should be in - * upper or lower case. There are example strings given in the - * specification, but the author skilfully manages to leave the issue - * unresolved by using the somewhat implausible digest value of - * "0200000000000000000000000000000000000000000000000000000000000000". - * - * Just in case you were thinking that the silver lining of the choice - * to use an XML-based protocol would be the ability to generate and - * process messages with standard tools, version 2.0 of the protocol - * places most of the critical information inside a Base64-encoded - * custom binary data structure. Within an XML element, naturally. - * - * I hereby announce this specification to be the 2015 winner of the - * prestigious "UEFI HII API" award for incompetent design. - */ - -/** Discovery request format */ -#define PEERDIST_DISCOVERY_REQUEST \ - "<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ - "<soap:Envelope " \ - "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " \ - "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" " \ - "xmlns:wsd=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" " \ - "xmlns:PeerDist=\"http://schemas.microsoft.com/p2p/" \ - "2007/09/PeerDistributionDiscovery\">" \ - "<soap:Header>" \ - "<wsa:To>" \ - "urn:schemas-xmlsoap-org:ws:2005:04:discovery" \ - "</wsa:To>" \ - "<wsa:Action>" \ - "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" \ - "</wsa:Action>" \ - "<wsa:MessageID>" \ - "urn:uuid:%s" \ - "</wsa:MessageID>" \ - "</soap:Header>" \ - "<soap:Body>" \ - "<wsd:Probe>" \ - "<wsd:Types>" \ - "PeerDist:PeerDistData" \ - "</wsd:Types>" \ - "<wsd:Scopes MatchBy=\"http://schemas.xmlsoap.org/ws/" \ - "2005/04/discovery/strcmp0\">" \ - "%s" \ - "</wsd:Scopes>" \ - "</wsd:Probe>" \ - "</soap:Body>" \ - "</soap:Envelope>" - -/** - * Construct discovery request - * - * @v uuid Message UUID string - * @v id Segment identifier string - * @ret request Discovery request, or NULL on failure - * - * The request is dynamically allocated; the caller must eventually - * free() the request. - */ -char * peerdist_discovery_request ( const char *uuid, const char *id ) { - char *request; - int len; - - /* Construct request */ - len = asprintf ( &request, PEERDIST_DISCOVERY_REQUEST, uuid, id ); - if ( len < 0 ) - return NULL; - - return request; -} - -/** - * Locate discovery reply tag - * - * @v data Reply data (not NUL-terminated) - * @v len Length of reply data - * @v tag XML tag - * @ret found Found tag (or NULL if not found) - */ -static char * peerdist_discovery_reply_tag ( char *data, size_t len, - const char *tag ) { - size_t tag_len = strlen ( tag ); - - /* Search, allowing for the fact that the reply data is not - * cleanly NUL-terminated and may contain embedded NULs due to - * earlier parsing. - */ - for ( ; len >= tag_len ; data++, len-- ) { - if ( strncmp ( data, tag, tag_len ) == 0 ) - return data; - } - return NULL; -} - -/** - * Locate discovery reply values - * - * @v data Reply data (not NUL-terminated, will be modified) - * @v len Length of reply data - * @v name XML tag name - * @ret values Tag values (or NULL if not found) - * - * The reply data is modified by adding NULs and moving characters as - * needed to produce a NUL-separated list of values, terminated with a - * zero-length string. - * - * This is not supposed to be a full XML parser; it's supposed to - * include just enough functionality to allow PeerDist discovery to - * work with existing implementations. - */ -static char * peerdist_discovery_reply_values ( char *data, size_t len, - const char *name ) { - char buf[ 2 /* "</" */ + strlen ( name ) + 1 /* ">" */ + 1 /* NUL */ ]; - char *open; - char *close; - char *start; - char *end; - char *in; - char *out; - char c; - - /* Locate opening tag */ - snprintf ( buf, sizeof ( buf ), "<%s>", name ); - open = peerdist_discovery_reply_tag ( data, len, buf ); - if ( ! open ) - return NULL; - start = ( open + strlen ( buf ) ); - len -= ( start - data ); - data = start; - - /* Locate closing tag */ - snprintf ( buf, sizeof ( buf ), "</%s>", name ); - close = peerdist_discovery_reply_tag ( data, len, buf ); - if ( ! close ) - return NULL; - assert ( close >= open ); - end = close; - - /* Strip initial whitespace, convert other whitespace - * sequences to single NULs, add terminating pair of NULs. - * This will probably overwrite part of the closing tag. - */ - for ( in = start, out = start ; in < end ; in++ ) { - c = *in; - if ( isspace ( c ) ) { - if ( ( out > start ) && ( out[-1] != '\0' ) ) - *(out++) = '\0'; - } else { - *(out++) = c; - } - } - *(out++) = '\0'; - *(out++) = '\0'; - assert ( out < ( close + strlen ( buf ) ) ); - - return start; -} - -/** - * Parse discovery reply - * - * @v data Reply data (not NUL-terminated, will be modified) - * @v len Length of reply data - * @v reply Discovery reply to fill in - * @ret rc Return status code - * - * The discovery reply includes pointers to strings within the - * modified reply data. - */ -int peerdist_discovery_reply ( char *data, size_t len, - struct peerdist_discovery_reply *reply ) { - static const struct peerdist_discovery_block_count zcount = { - .hex = "00000000", - }; - struct peerdist_discovery_block_count *count; - unsigned int max; - unsigned int i; - char *scopes; - char *xaddrs; - char *blockcount; - char *in; - char *out; - size_t skip; - - /* Find <wsd:Scopes> tag */ - scopes = peerdist_discovery_reply_values ( data, len, "wsd:Scopes" ); - if ( ! scopes ) { - DBGC ( reply, "PCCRD %p missing <wsd:Scopes> tag\n", reply ); - return -ENOENT; - } - - /* Find <wsd:XAddrs> tag */ - xaddrs = peerdist_discovery_reply_values ( data, len, "wsd:XAddrs" ); - if ( ! xaddrs ) { - DBGC ( reply, "PCCRD %p missing <wsd:XAddrs> tag\n", reply ); - return -ENOENT; - } - - /* Find <PeerDist:BlockCount> tag */ - blockcount = peerdist_discovery_reply_values ( data, len, - "PeerDist:BlockCount" ); - if ( ! blockcount ) { - DBGC ( reply, "PCCRD %p missing <PeerDist:BlockCount> tag\n", - reply ); - return -ENOENT; - } - - /* Determine maximum number of segments (according to number - * of entries in the block count list). - */ - max = ( strlen ( blockcount ) / sizeof ( *count ) ); - count = container_of ( blockcount, - struct peerdist_discovery_block_count, hex[0] ); - - /* Eliminate any segments with a zero block count */ - for ( i = 0, in = scopes, out = scopes ; *in ; i++, in += skip ) { - - /* Fail if we have overrun the maximum number of segments */ - if ( i >= max ) { - DBGC ( reply, "PCCRD %p too many segment IDs\n", - reply ); - return -EPROTO; - } - - /* Delete segment if block count is zero */ - skip = ( strlen ( in ) + 1 /* NUL */ ); - if ( memcmp ( count[i].hex, zcount.hex, - sizeof ( zcount.hex ) ) == 0 ) - continue; - strcpy ( out, in ); - out += skip; - } - out[0] = '\0'; /* Ensure list is terminated with a zero-length string */ - - /* Fill in discovery reply */ - reply->ids = scopes; - reply->locations = xaddrs; - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/peerblk.c b/qemu/roms/ipxe/src/net/peerblk.c deleted file mode 100644 index fd7ea0893..000000000 --- a/qemu/roms/ipxe/src/net/peerblk.c +++ /dev/null @@ -1,1366 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <ipxe/http.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/uri.h> -#include <ipxe/timer.h> -#include <ipxe/profile.h> -#include <ipxe/fault.h> -#include <ipxe/pccrr.h> -#include <ipxe/peerblk.h> - -/** @file - * - * Peer Content Caching and Retrieval (PeerDist) protocol block downloads - * - */ - -/** PeerDist decryption chunksize - * - * This is a policy decision. - */ -#define PEERBLK_DECRYPT_CHUNKSIZE 2048 - -/** PeerDist raw block download attempt initial progress timeout - * - * This is a policy decision. - */ -#define PEERBLK_RAW_OPEN_TIMEOUT ( 10 * TICKS_PER_SEC ) - -/** PeerDist raw block download attempt ongoing progress timeout - * - * This is a policy decision. - */ -#define PEERBLK_RAW_RX_TIMEOUT ( 15 * TICKS_PER_SEC ) - -/** PeerDist retrieval protocol block download attempt initial progress timeout - * - * This is a policy decision. - */ -#define PEERBLK_RETRIEVAL_OPEN_TIMEOUT ( 3 * TICKS_PER_SEC ) - -/** PeerDist retrieval protocol block download attempt ongoing progress timeout - * - * This is a policy decision. - */ -#define PEERBLK_RETRIEVAL_RX_TIMEOUT ( 5 * TICKS_PER_SEC ) - -/** PeerDist maximum number of full download attempt cycles - * - * This is the maximum number of times that we will try a full cycle - * of download attempts (i.e. a retrieval protocol download attempt - * from each discovered peer plus a raw download attempt from the - * origin server). - * - * This is a policy decision. - */ -#define PEERBLK_MAX_ATTEMPT_CYCLES 4 - -/** PeerDist block download profiler */ -static struct profiler peerblk_download_profiler __profiler = - { .name = "peerblk.download" }; - -/** PeerDist block download attempt success profiler */ -static struct profiler peerblk_attempt_success_profiler __profiler = - { .name = "peerblk.attempt.success" }; - -/** PeerDist block download attempt failure profiler */ -static struct profiler peerblk_attempt_failure_profiler __profiler = - { .name = "peerblk.attempt.failure" }; - -/** PeerDist block download attempt timeout profiler */ -static struct profiler peerblk_attempt_timeout_profiler __profiler = - { .name = "peerblk.attempt.timeout" }; - -/** PeerDist block download discovery success profiler */ -static struct profiler peerblk_discovery_success_profiler __profiler = - { .name = "peerblk.discovery.success" }; - -/** PeerDist block download discovery timeout profiler */ -static struct profiler peerblk_discovery_timeout_profiler __profiler = - { .name = "peerblk.discovery.timeout" }; - -/** - * Get profiling timestamp - * - * @ret timestamp Timestamp - */ -static inline __attribute__ (( always_inline )) unsigned long -peerblk_timestamp ( void ) { - - if ( PROFILING ) { - return currticks(); - } else { - return 0; - } -} - -/** - * Free PeerDist block download - * - * @v refcnt Reference count - */ -static void peerblk_free ( struct refcnt *refcnt ) { - struct peerdist_block *peerblk = - container_of ( refcnt, struct peerdist_block, refcnt ); - - uri_put ( peerblk->uri ); - free ( peerblk->cipherctx ); - free ( peerblk ); -} - -/** - * Reset PeerDist block download attempt - * - * @v peerblk PeerDist block download - * @v rc Reason for reset - */ -static void peerblk_reset ( struct peerdist_block *peerblk, int rc ) { - - /* Stop decryption process */ - process_del ( &peerblk->process ); - - /* Stop timer */ - stop_timer ( &peerblk->timer ); - - /* Abort any current download attempt */ - intf_restart ( &peerblk->raw, rc ); - intf_restart ( &peerblk->retrieval, rc ); - - /* Empty received data buffer */ - xferbuf_free ( &peerblk->buffer ); - peerblk->pos = 0; - - /* Reset digest and free cipher context */ - digest_init ( peerblk->digest, peerblk->digestctx ); - free ( peerblk->cipherctx ); - peerblk->cipherctx = NULL; - peerblk->cipher = NULL; - - /* Reset trim thresholds */ - peerblk->start = ( peerblk->trim.start - peerblk->range.start ); - peerblk->end = ( peerblk->trim.end - peerblk->range.start ); - assert ( peerblk->start <= peerblk->end ); -} - -/** - * Close PeerDist block download - * - * @v peerblk PeerDist block download - * @v rc Reason for close - */ -static void peerblk_close ( struct peerdist_block *peerblk, int rc ) { - unsigned long now = peerblk_timestamp(); - - /* Profile overall block download */ - profile_custom ( &peerblk_download_profiler, - ( now - peerblk->started ) ); - - /* Reset download attempt */ - peerblk_reset ( peerblk, rc ); - - /* Close discovery */ - peerdisc_close ( &peerblk->discovery ); - - /* Shut down all interfaces */ - intf_shutdown ( &peerblk->retrieval, rc ); - intf_shutdown ( &peerblk->raw, rc ); - intf_shutdown ( &peerblk->xfer, rc ); -} - -/** - * Calculate offset within overall download - * - * @v peerblk PeerDist block download - * @v pos Position within incoming data stream - * @ret offset Offset within overall download - */ -static inline __attribute__ (( always_inline )) size_t -peerblk_offset ( struct peerdist_block *peerblk, size_t pos ) { - - return ( ( pos - peerblk->start ) + peerblk->offset ); -} - -/** - * Deliver download attempt data block - * - * @v peerblk PeerDist block download - * @v iobuf I/O buffer - * @v meta Original data transfer metadata - * @v pos Position within incoming data stream - * @ret rc Return status code - */ -static int peerblk_deliver ( struct peerdist_block *peerblk, - struct io_buffer *iobuf, - struct xfer_metadata *meta, size_t pos ) { - struct xfer_metadata xfer_meta; - size_t len = iob_len ( iobuf ); - size_t start = pos; - size_t end = ( pos + len ); - int rc; - - /* Discard zero-length packets and packets which lie entirely - * outside the trimmed range. - */ - if ( ( start >= peerblk->end ) || ( end <= peerblk->start ) || - ( len == 0 ) ) { - free_iob ( iobuf ); - return 0; - } - - /* Truncate data to within trimmed range */ - if ( start < peerblk->start ) { - iob_pull ( iobuf, ( peerblk->start - start ) ); - start = peerblk->start; - } - if ( end > peerblk->end ) { - iob_unput ( iobuf, ( end - peerblk->end ) ); - end = peerblk->end; - } - - /* Construct metadata */ - memcpy ( &xfer_meta, meta, sizeof ( xfer_meta ) ); - xfer_meta.flags |= XFER_FL_ABS_OFFSET; - xfer_meta.offset = peerblk_offset ( peerblk, start ); - - /* Deliver data */ - if ( ( rc = xfer_deliver ( &peerblk->xfer, iob_disown ( iobuf ), - &xfer_meta ) ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d could not deliver data: %s\n", - peerblk, peerblk->segment, peerblk->block, - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Finish PeerDist block download attempt - * - * @v peerblk PeerDist block download - * @v rc Reason for close - */ -static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { - struct digest_algorithm *digest = peerblk->digest; - uint8_t hash[digest->digestsize]; - unsigned long now = peerblk_timestamp(); - - /* Check for errors on completion */ - if ( rc != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d attempt failed: %s\n", - peerblk, peerblk->segment, peerblk->block, - strerror ( rc ) ); - goto err; - } - - /* Check digest */ - digest_final ( digest, peerblk->digestctx, hash ); - if ( memcmp ( hash, peerblk->hash, peerblk->digestsize ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d digest mismatch:\n", - peerblk, peerblk->segment, peerblk->block ); - DBGC_HDA ( peerblk, 0, hash, peerblk->digestsize ); - DBGC_HDA ( peerblk, 0, peerblk->hash, peerblk->digestsize ); - rc = -EIO; - goto err; - } - - /* Profile successful attempt */ - profile_custom ( &peerblk_attempt_success_profiler, - ( now - peerblk->attempted ) ); - - /* Close download */ - peerblk_close ( peerblk, 0 ); - return; - - err: - /* Record failure reason and schedule a retry attempt */ - profile_custom ( &peerblk_attempt_failure_profiler, - ( now - peerblk->attempted ) ); - peerblk_reset ( peerblk, rc ); - peerblk->rc = rc; - start_timer_nodelay ( &peerblk->timer ); -} - -/****************************************************************************** - * - * Raw block download attempts (using an HTTP range request) - * - ****************************************************************************** - */ - -/** - * Open PeerDist raw block download attempt - * - * @v peerblk PeerDist block download - * @ret rc Return status code - */ -static int peerblk_raw_open ( struct peerdist_block *peerblk ) { - struct http_request_range range; - int rc; - - DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting raw range request\n", - peerblk, peerblk->segment, peerblk->block ); - - /* Construct HTTP range */ - memset ( &range, 0, sizeof ( range ) ); - range.start = peerblk->range.start; - range.len = ( peerblk->range.end - peerblk->range.start ); - - /* Initiate range request to retrieve block */ - if ( ( rc = http_open ( &peerblk->raw, &http_get, peerblk->uri, - &range, NULL ) ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d could not create range " - "request: %s\n", peerblk, peerblk->segment, - peerblk->block, strerror ( rc ) ); - return rc; - } - - /* Annul HTTP connection (for testing) if applicable. Do not - * report as an immediate error, in order to test our ability - * to recover from a totally unresponsive HTTP server. - */ - if ( inject_fault ( PEERBLK_ANNUL_RATE ) ) - intf_restart ( &peerblk->raw, 0 ); - - return 0; -} - -/** - * Receive PeerDist raw data - * - * @v peerblk PeerDist block download - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int peerblk_raw_rx ( struct peerdist_block *peerblk, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - size_t len = iob_len ( iobuf ); - size_t pos = peerblk->pos; - size_t mid = ( ( peerblk->range.end - peerblk->range.start ) / 2 ); - int rc; - - /* Corrupt received data (for testing) if applicable */ - inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len ); - - /* Fail if data is delivered out of order, since the streaming - * digest requires strict ordering. - */ - if ( ( rc = xfer_check_order ( meta, &peerblk->pos, len ) ) != 0 ) - goto err; - - /* Add data to digest */ - digest_update ( peerblk->digest, peerblk->digestctx, iobuf->data, len ); - - /* Deliver data */ - if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta, - pos ) ) != 0 ) - goto err; - - /* Extend download attempt timer */ - start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_RX_TIMEOUT ); - - /* Stall download attempt (for testing) if applicable */ - if ( ( pos < mid ) && ( ( pos + len ) >= mid ) && - ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) { - intf_restart ( &peerblk->raw, rc ); - } - - return 0; - - err: - free_iob ( iobuf ); - peerblk_done ( peerblk, rc ); - return rc; -} - -/** - * Close PeerDist raw block download attempt - * - * @v peerblk PeerDist block download - * @v rc Reason for close - */ -static void peerblk_raw_close ( struct peerdist_block *peerblk, int rc ) { - - /* Restart interface */ - intf_restart ( &peerblk->raw, rc ); - - /* Fail immediately if we have an error */ - if ( rc != 0 ) - goto done; - - /* Abort download attempt (for testing) if applicable */ - if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 ) - goto done; - - done: - /* Complete download attempt */ - peerblk_done ( peerblk, rc ); -} - -/****************************************************************************** - * - * Retrieval protocol block download attempts (using HTTP POST) - * - ****************************************************************************** - */ - -/** - * Construct PeerDist retrieval protocol URI - * - * @v location Peer location - * @ret uri Retrieval URI, or NULL on error - */ -static struct uri * peerblk_retrieval_uri ( const char *location ) { - char uri_string[ 7 /* "http://" */ + strlen ( location ) + - sizeof ( PEERDIST_MAGIC_PATH /* includes NUL */ ) ]; - - /* Construct URI string */ - snprintf ( uri_string, sizeof ( uri_string ), - ( "http://%s" PEERDIST_MAGIC_PATH ), location ); - - /* Parse URI string */ - return parse_uri ( uri_string ); -} - -/** - * Open PeerDist retrieval protocol block download attempt - * - * @v peerblk PeerDist block download - * @v location Peer location - * @ret rc Return status code - */ -static int peerblk_retrieval_open ( struct peerdist_block *peerblk, - const char *location ) { - size_t digestsize = peerblk->digestsize; - peerdist_msg_getblks_t ( digestsize, 1, 0 ) req; - peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *rsp; - struct http_request_content content; - struct uri *uri; - int rc; - - DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting retrieval from %s\n", - peerblk, peerblk->segment, peerblk->block, location ); - - /* Construct block fetch request */ - memset ( &req, 0, sizeof ( req ) ); - req.getblks.hdr.version.raw = htonl ( PEERDIST_MSG_GETBLKS_VERSION ); - req.getblks.hdr.type = htonl ( PEERDIST_MSG_GETBLKS_TYPE ); - req.getblks.hdr.len = htonl ( sizeof ( req ) ); - req.getblks.hdr.algorithm = htonl ( PEERDIST_MSG_AES_128_CBC ); - req.segment.segment.digestsize = htonl ( digestsize ); - memcpy ( req.segment.id, peerblk->id, digestsize ); - req.ranges.ranges.count = htonl ( 1 ); - req.ranges.range[0].first = htonl ( peerblk->block ); - req.ranges.range[0].count = htonl ( 1 ); - - /* Construct POST request content */ - memset ( &content, 0, sizeof ( content ) ); - content.data = &req; - content.len = sizeof ( req ); - - /* Construct URI */ - if ( ( uri = peerblk_retrieval_uri ( location ) ) == NULL ) { - rc = -ENOMEM; - goto err_uri; - } - - /* Update trim thresholds */ - peerblk->start += offsetof ( typeof ( *rsp ), msg.vrf ); - peerblk->end += offsetof ( typeof ( *rsp ), msg.vrf ); - - /* Initiate HTTP POST to retrieve block */ - if ( ( rc = http_open ( &peerblk->retrieval, &http_post, uri, - NULL, &content ) ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d could not create retrieval " - "request: %s\n", peerblk, peerblk->segment, - peerblk->block, strerror ( rc ) ); - goto err_open; - } - - /* Annul HTTP connection (for testing) if applicable. Do not - * report as an immediate error, in order to test our ability - * to recover from a totally unresponsive HTTP server. - */ - if ( inject_fault ( PEERBLK_ANNUL_RATE ) ) - intf_restart ( &peerblk->retrieval, 0 ); - - err_open: - uri_put ( uri ); - err_uri: - return rc; -} - -/** - * Receive PeerDist retrieval protocol data - * - * @v peerblk PeerDist block download - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int peerblk_retrieval_rx ( struct peerdist_block *peerblk, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - size_t len = iob_len ( iobuf ); - size_t start; - size_t end; - size_t before; - size_t after; - size_t cut; - int rc; - - /* Some genius at Microsoft thought it would be a great idea - * to place the AES-CBC initialisation vector *after* the - * encrypted data, thereby making it logically impossible to - * decrypt each packet as it arrives. - * - * To work around this mindless stupidity, we deliver the - * ciphertext as-is and later use xfer_buffer() to obtain - * access to the underlying data transfer buffer in order to - * perform the decryption. - * - * There will be some data both before and after the bytes - * corresponding to the trimmed plaintext: a MSG_BLK - * header/footer, some block padding for the AES-CBC cipher, - * and a possibly large quantity of unwanted ciphertext which - * is excluded from the trimmed content range. We store this - * data in a local data transfer buffer. If the amount of - * data to be stored is too large, we will fail allocation and - * so eventually fall back to using a range request (which - * does not require this kind of temporary storage - * allocation). - */ - - /* Corrupt received data (for testing) if applicable */ - inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len ); - - /* Calculate start and end positions of this buffer */ - start = peerblk->pos; - if ( meta->flags & XFER_FL_ABS_OFFSET ) - start = 0; - start += meta->offset; - end = ( start + len ); - - /* Buffer any data before the trimmed content */ - if ( ( start < peerblk->start ) && ( len > 0 ) ) { - - /* Calculate length of data before the trimmed content */ - before = ( peerblk->start - start ); - if ( before > len ) - before = len; - - /* Buffer data before the trimmed content */ - if ( ( rc = xferbuf_write ( &peerblk->buffer, start, - iobuf->data, before ) ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer " - "data: %s\n", peerblk, peerblk->segment, - peerblk->block, strerror ( rc ) ); - goto err; - } - } - - /* Buffer any data after the trimmed content */ - if ( ( end > peerblk->end ) && ( len > 0 ) ) { - - /* Calculate length of data after the trimmed content */ - after = ( end - peerblk->end ); - if ( after > len ) - after = len; - - /* Buffer data after the trimmed content */ - cut = ( peerblk->end - peerblk->start ); - if ( ( rc = xferbuf_write ( &peerblk->buffer, - ( end - after - cut ), - ( iobuf->data + len - after ), - after ) ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer " - "data: %s\n", peerblk, peerblk->segment, - peerblk->block, strerror ( rc ) ); - goto err; - } - } - - /* Deliver any remaining data */ - if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta, - start ) ) != 0 ) - goto err; - - /* Update position */ - peerblk->pos = end; - - /* Extend download attempt timer */ - start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_RX_TIMEOUT ); - - /* Stall download attempt (for testing) if applicable */ - if ( ( start < peerblk->end ) && ( end >= peerblk->end ) && - ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) { - intf_restart ( &peerblk->retrieval, rc ); - } - - return 0; - - err: - free_iob ( iobuf ); - peerblk_done ( peerblk, rc ); - return rc; -} - -/** - * Parse retrieval protocol message header - * - * @v peerblk PeerDist block download - * @ret rc Return status code - */ -static int peerblk_parse_header ( struct peerdist_block *peerblk ) { - struct { - struct peerdist_msg_transport_header hdr; - struct peerdist_msg_header msg; - } __attribute__ (( packed )) *msg = peerblk->buffer.data; - struct cipher_algorithm *cipher; - size_t len = peerblk->buffer.len; - size_t keylen = 0; - int rc; - - /* Check message length */ - if ( len < sizeof ( *msg ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d message too short for header " - "(%zd bytes)\n", peerblk, peerblk->segment, - peerblk->block, len ); - return -ERANGE; - } - - /* Check message type */ - if ( msg->msg.type != htonl ( PEERDIST_MSG_BLK_TYPE ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d unexpected message type " - "%#08x\n", peerblk, peerblk->segment, peerblk->block, - ntohl ( msg->msg.type ) ); - return -EPROTO; - } - - /* Determine cipher algorithm and key length */ - cipher = &aes_cbc_algorithm; - switch ( msg->msg.algorithm ) { - case htonl ( PEERDIST_MSG_PLAINTEXT ) : - cipher = NULL; - break; - case htonl ( PEERDIST_MSG_AES_128_CBC ) : - keylen = ( 128 / 8 ); - break; - case htonl ( PEERDIST_MSG_AES_192_CBC ) : - keylen = ( 192 / 8 ); - break; - case htonl ( PEERDIST_MSG_AES_256_CBC ) : - keylen = ( 256 / 8 ); - break; - default: - DBGC ( peerblk, "PEERBLK %p %d.%d unrecognised algorithm " - "%#08x\n", peerblk, peerblk->segment, peerblk->block, - ntohl ( msg->msg.algorithm ) ); - return -ENOTSUP; - } - DBGC2 ( peerblk, "PEERBLK %p %d.%d using %s with %zd-bit key\n", - peerblk, peerblk->segment, peerblk->block, - ( cipher ? cipher->name : "plaintext" ), ( 8 * keylen ) ); - - /* Sanity check key length against maximum secret length */ - if ( keylen > peerblk->digestsize ) { - DBGC ( peerblk, "PEERBLK %p %d.%d %zd-byte secret too short " - "for %zd-bit key\n", peerblk, peerblk->segment, - peerblk->block, peerblk->digestsize, ( 8 * keylen ) ); - return -EPROTO; - } - - /* Allocate cipher context. Freeing the cipher context (on - * error or otherwise) is handled by peerblk_reset(). - */ - peerblk->cipher = cipher; - assert ( peerblk->cipherctx == NULL ); - peerblk->cipherctx = malloc ( cipher->ctxsize ); - if ( ! peerblk->cipherctx ) - return -ENOMEM; - - /* Initialise cipher */ - if ( ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret, - keylen ) ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n", - peerblk, peerblk->segment, peerblk->block, - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Parse retrieval protocol message segment and block details - * - * @v peerblk PeerDist block download - * @v buf_len Length of buffered data to fill in - * @ret rc Return status code - */ -static int peerblk_parse_block ( struct peerdist_block *peerblk, - size_t *buf_len ) { - size_t digestsize = peerblk->digestsize; - peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data; - size_t len = peerblk->buffer.len; - size_t data_len; - size_t total; - - /* Check message length */ - if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " - "zero-length data (%zd bytes)\n", peerblk, - peerblk->segment, peerblk->block, len ); - return -ERANGE; - } - - /* Check digest size */ - if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) { - DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n", - peerblk, peerblk->segment, peerblk->block, - ntohl ( msg->msg.segment.segment.digestsize ) ); - return -EPROTO; - } - - /* Check segment ID */ - if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) { - DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n", - peerblk, peerblk->segment, peerblk->block ); - return -EPROTO; - } - - /* Check block ID */ - if ( ntohl ( msg->msg.index ) != peerblk->block ) { - DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n", - peerblk, peerblk->segment, peerblk->block, - ntohl ( msg->msg.index ) ); - return -EPROTO; - } - - /* Check for missing blocks */ - data_len = be32_to_cpu ( msg->msg.block.block.len ); - if ( ! data_len ) { - DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n", - peerblk, peerblk->segment, peerblk->block ); - return -ENOENT; - } - - /* Check for underlength blocks */ - if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd " - "bytes)\n", peerblk, peerblk->segment, peerblk->block, - data_len ); - return -ERANGE; - } - - /* Calculate buffered data length (i.e. excluding data which - * was delivered to the final data transfer buffer). - */ - *buf_len = ( data_len - ( peerblk->end - peerblk->start ) ); - - /* Describe data before the trimmed content */ - peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer; - peerblk->decrypt[PEERBLK_BEFORE].offset = - offsetof ( typeof ( *msg ), msg.block.data ); - peerblk->decrypt[PEERBLK_BEFORE].len = - ( peerblk->start - - offsetof ( typeof ( *msg ), msg.block.data ) ); - total = peerblk->decrypt[PEERBLK_BEFORE].len; - - /* Describe data within the trimmed content */ - peerblk->decrypt[PEERBLK_DURING].offset = - peerblk_offset ( peerblk, peerblk->start ); - peerblk->decrypt[PEERBLK_DURING].len = - ( peerblk->end - peerblk->start ); - total += peerblk->decrypt[PEERBLK_DURING].len; - - /* Describe data after the trimmed content */ - peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer; - peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start; - peerblk->decrypt[PEERBLK_AFTER].len = - ( offsetof ( typeof ( *msg ), msg.block.data ) - + *buf_len - peerblk->start ); - total += peerblk->decrypt[PEERBLK_AFTER].len; - - /* Sanity check */ - assert ( total == be32_to_cpu ( msg->msg.block.block.len ) ); - - /* Initialise cipher and digest lengths */ - peerblk->cipher_remaining = total; - peerblk->digest_remaining = - ( peerblk->range.end - peerblk->range.start ); - assert ( peerblk->cipher_remaining >= peerblk->digest_remaining ); - - return 0; -} - -/** - * Parse retrieval protocol message useless details - * - * @v peerblk PeerDist block download - * @v buf_len Length of buffered data - * @v vrf_len Length of uselessness to fill in - * @ret rc Return status code - */ -static int peerblk_parse_useless ( struct peerdist_block *peerblk, - size_t buf_len, size_t *vrf_len ) { - size_t digestsize = peerblk->digestsize; - peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg = - peerblk->buffer.data; - size_t len = peerblk->buffer.len; - - /* Check message length */ - if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " - "zero-length uselessness (%zd bytes)\n", peerblk, - peerblk->segment, peerblk->block, len ); - return -ERANGE; - } - - /* Extract length of uselessness */ - *vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len ); - - return 0; -} - -/** - * Parse retrieval protocol message initialisation vector details - * - * @v peerblk PeerDist block download - * @v buf_len Length of buffered data - * @v vrf_len Length of uselessness - * @ret rc Return status code - */ -static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len, - size_t vrf_len ) { - size_t digestsize = peerblk->digestsize; - size_t blksize = peerblk->cipher->blocksize; - peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg = - peerblk->buffer.data; - size_t len = peerblk->buffer.len; - - /* Check message length */ - if ( len < sizeof ( *msg ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " - "initialisation vector (%zd bytes)\n", peerblk, - peerblk->segment, peerblk->block, len ); - return -ERANGE; - } - - /* Check initialisation vector size */ - if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) { - DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n", - peerblk, peerblk->segment, peerblk->block, - ntohl ( msg->msg.iv.iv.blksize ) ); - return -EPROTO; - } - - /* Set initialisation vector */ - cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data ); - - return 0; -} - -/** - * Read from decryption buffers - * - * @v peerblk PeerDist block download - * @v data Data buffer - * @v len Length to read - * @ret rc Return status code - */ -static int peerblk_decrypt_read ( struct peerdist_block *peerblk, - void *data, size_t len ) { - struct peerdist_block_decrypt *decrypt = peerblk->decrypt; - size_t frag_len; - int rc; - - /* Read from each decryption buffer in turn */ - for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) { - - /* Calculate length to use from this buffer */ - frag_len = decrypt->len; - if ( frag_len > len ) - frag_len = len; - if ( ! frag_len ) - continue; - - /* Read from this buffer */ - if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset, - data, frag_len ) ) != 0 ) - return rc; - } - - return 0; -} - -/** - * Write to decryption buffers and update offsets and lengths - * - * @v peerblk PeerDist block download - * @v data Data buffer - * @v len Length to read - * @ret rc Return status code - */ -static int peerblk_decrypt_write ( struct peerdist_block *peerblk, - const void *data, size_t len ) { - struct peerdist_block_decrypt *decrypt = peerblk->decrypt; - size_t frag_len; - int rc; - - /* Write to each decryption buffer in turn */ - for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) { - - /* Calculate length to use from this buffer */ - frag_len = decrypt->len; - if ( frag_len > len ) - frag_len = len; - if ( ! frag_len ) - continue; - - /* Write to this buffer */ - if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset, - data, frag_len ) ) != 0 ) - return rc; - - /* Update offset and length */ - decrypt->offset += frag_len; - decrypt->len -= frag_len; - } - - return 0; -} - -/** - * Decrypt one chunk of PeerDist retrieval protocol data - * - * @v peerblk PeerDist block download - */ -static void peerblk_decrypt ( struct peerdist_block *peerblk ) { - struct cipher_algorithm *cipher = peerblk->cipher; - struct digest_algorithm *digest = peerblk->digest; - struct xfer_buffer *xferbuf; - size_t cipher_len; - size_t digest_len; - void *data; - int rc; - - /* Sanity check */ - assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 ); - - /* Get the underlying data transfer buffer */ - xferbuf = xfer_buffer ( &peerblk->xfer ); - if ( ! xferbuf ) { - DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data " - "transfer buffer\n", peerblk, peerblk->segment, - peerblk->block ); - rc = -ENOTSUP; - goto err_xfer_buffer; - } - peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf; - - /* Calculate cipher and digest lengths */ - cipher_len = PEERBLK_DECRYPT_CHUNKSIZE; - if ( cipher_len > peerblk->cipher_remaining ) - cipher_len = peerblk->cipher_remaining; - digest_len = cipher_len; - if ( digest_len > peerblk->digest_remaining ) - digest_len = peerblk->digest_remaining; - assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 ); - - /* Allocate temporary data buffer */ - data = malloc ( cipher_len ); - if ( ! data ) { - rc = -ENOMEM; - goto err_alloc_data; - } - - /* Read ciphertext */ - if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){ - DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: " - "%s\n", peerblk, peerblk->segment, peerblk->block, - strerror ( rc ) ); - goto err_read; - } - - /* Decrypt data */ - cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len ); - - /* Add data to digest */ - digest_update ( digest, peerblk->digestctx, data, digest_len ); - - /* Write plaintext */ - if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){ - DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: " - "%s\n", peerblk, peerblk->segment, peerblk->block, - strerror ( rc ) ); - goto err_write; - } - - /* Consume input */ - peerblk->cipher_remaining -= cipher_len; - peerblk->digest_remaining -= digest_len; - - /* Free temporary data buffer */ - free ( data ); - - /* Continue processing until all input is consumed */ - if ( peerblk->cipher_remaining ) - return; - - /* Complete download attempt */ - peerblk_done ( peerblk, 0 ); - return; - - err_write: - err_read: - free ( data ); - err_alloc_data: - err_xfer_buffer: - peerblk_done ( peerblk, rc ); -} - -/** - * Close PeerDist retrieval protocol block download attempt - * - * @v peerblk PeerDist block download - * @v rc Reason for close - */ -static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) { - size_t buf_len; - size_t vrf_len; - - /* Restart interface */ - intf_restart ( &peerblk->retrieval, rc ); - - /* Fail immediately if we have an error */ - if ( rc != 0 ) - goto done; - - /* Abort download attempt (for testing) if applicable */ - if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 ) - goto done; - - /* Parse message header */ - if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 ) - goto done; - - /* Parse message segment and block details */ - if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 ) - goto done; - - /* If the block was plaintext, then there is nothing more to do */ - if ( ! peerblk->cipher ) - goto done; - - /* Parse message useless details */ - if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0) - goto done; - - /* Parse message initialisation vector details */ - if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 ) - goto done; - - /* Fail if decryption length is not aligned to the cipher block size */ - if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) { - DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n", - peerblk, peerblk->segment, peerblk->block, - peerblk->cipher_remaining ); - rc = -EPROTO; - goto done; - } - - /* Stop the download attempt timer: there is no point in - * timing out while decrypting. - */ - stop_timer ( &peerblk->timer ); - - /* Start decryption process */ - process_add ( &peerblk->process ); - return; - - done: - /* Complete download attempt */ - peerblk_done ( peerblk, rc ); -} - -/****************************************************************************** - * - * Retry policy - * - ****************************************************************************** - */ - -/** - * Handle PeerDist retry timer expiry - * - * @v timer Retry timer - * @v over Failure indicator - */ -static void peerblk_expired ( struct retry_timer *timer, int over __unused ) { - struct peerdist_block *peerblk = - container_of ( timer, struct peerdist_block, timer ); - struct peerdisc_segment *segment = peerblk->discovery.segment; - struct peerdisc_peer *head; - unsigned long now = peerblk_timestamp(); - const char *location; - int rc; - - /* Profile discovery timeout, if applicable */ - if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) { - profile_custom ( &peerblk_discovery_timeout_profiler, - ( now - peerblk->started ) ); - DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after " - "%ld ticks\n", peerblk, peerblk->segment, - peerblk->block, timer->timeout ); - } - - /* Profile download timeout, if applicable */ - if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) { - profile_custom ( &peerblk_attempt_timeout_profiler, - ( now - peerblk->attempted ) ); - DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n", - peerblk, peerblk->segment, peerblk->block, - timer->timeout ); - } - - /* Abort any current download attempt */ - peerblk_reset ( peerblk, -ETIMEDOUT ); - - /* Record attempt start time */ - peerblk->attempted = now; - - /* If we have exceeded our maximum number of attempt cycles - * (each cycle comprising a retrieval protocol download from - * each peer in the list followed by a raw download from the - * origin server), then abort the overall download. - */ - head = list_entry ( &segment->peers, struct peerdisc_peer, list ); - if ( ( peerblk->peer == head ) && - ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) { - rc = peerblk->rc; - assert ( rc != 0 ); - goto err; - } - - /* If we have not yet made any download attempts, then move to - * the start of the peer list. - */ - if ( peerblk->peer == NULL ) - peerblk->peer = head; - - /* Attempt retrieval protocol download from next usable peer */ - list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) { - - /* Attempt retrieval protocol download from this peer */ - location = peerblk->peer->location; - if ( ( rc = peerblk_retrieval_open ( peerblk, - location ) ) != 0 ) { - /* Non-fatal: continue to try next peer */ - continue; - } - - /* Start download attempt timer */ - peerblk->rc = -ETIMEDOUT; - start_timer_fixed ( &peerblk->timer, - PEERBLK_RETRIEVAL_OPEN_TIMEOUT ); - return; - } - - /* Attempt raw download */ - if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 ) - goto err; - - /* Start download attempt timer */ - peerblk->rc = -ETIMEDOUT; - start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT ); - return; - - err: - peerblk_close ( peerblk, rc ); -} - -/** - * Handle PeerDist peer discovery - * - * @v discovery PeerDist discovery client - */ -static void peerblk_discovered ( struct peerdisc_client *discovery ) { - struct peerdist_block *peerblk = - container_of ( discovery, struct peerdist_block, discovery ); - unsigned long now = peerblk_timestamp(); - - /* Do nothing unless we are still waiting for the initial - * discovery timeout. - */ - if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) ) - return; - - /* Schedule an immediate retry */ - start_timer_nodelay ( &peerblk->timer ); - - /* Profile discovery success */ - profile_custom ( &peerblk_discovery_success_profiler, - ( now - peerblk->started ) ); -} - -/****************************************************************************** - * - * Opener - * - ****************************************************************************** - */ - -/** PeerDist block download data transfer interface operations */ -static struct interface_operation peerblk_xfer_operations[] = { - INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ), -}; - -/** PeerDist block download data transfer interface descriptor */ -static struct interface_descriptor peerblk_xfer_desc = - INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations ); - -/** PeerDist block download raw data interface operations */ -static struct interface_operation peerblk_raw_operations[] = { - INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ), - INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ), -}; - -/** PeerDist block download raw data interface descriptor */ -static struct interface_descriptor peerblk_raw_desc = - INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations ); - -/** PeerDist block download retrieval protocol interface operations */ -static struct interface_operation peerblk_retrieval_operations[] = { - INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ), - INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close), -}; - -/** PeerDist block download retrieval protocol interface descriptor */ -static struct interface_descriptor peerblk_retrieval_desc = - INTF_DESC ( struct peerdist_block, retrieval, - peerblk_retrieval_operations ); - -/** PeerDist block download decryption process descriptor */ -static struct process_descriptor peerblk_process_desc = - PROC_DESC ( struct peerdist_block, process, peerblk_decrypt ); - -/** PeerDist block download discovery operations */ -static struct peerdisc_client_operations peerblk_discovery_operations = { - .discovered = peerblk_discovered, -}; - -/** - * Open PeerDist block download - * - * @v xfer Data transfer interface - * @v uri Original URI - * @v info Content information block - * @ret rc Return status code - */ -int peerblk_open ( struct interface *xfer, struct uri *uri, - struct peerdist_info_block *block ) { - const struct peerdist_info_segment *segment = block->segment; - const struct peerdist_info *info = segment->info; - struct digest_algorithm *digest = info->digest; - struct peerdist_block *peerblk; - unsigned long timeout; - size_t digestsize; - int rc; - - /* Allocate and initialise structure */ - peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize ); - if ( ! peerblk ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &peerblk->refcnt, peerblk_free ); - intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt ); - intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt ); - intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc, - &peerblk->refcnt ); - peerblk->uri = uri_get ( uri ); - memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) ); - memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) ); - peerblk->offset = ( block->trim.start - info->trim.start ); - peerblk->digest = info->digest; - peerblk->digestsize = digestsize = info->digestsize; - peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) ); - peerblk->segment = segment->index; - memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) ); - memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) ); - peerblk->block = block->index; - memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) ); - xferbuf_malloc_init ( &peerblk->buffer ); - process_init_stopped ( &peerblk->process, &peerblk_process_desc, - &peerblk->refcnt ); - peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations ); - timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt ); - DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..." - "%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment, - peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2], - peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ], - peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ], - peerblk->range.start, peerblk->range.end ); - if ( ( peerblk->trim.start != peerblk->range.start ) || - ( peerblk->trim.end != peerblk->range.end ) ) { - DBGC2 ( peerblk, " covers [%08zx,%08zx)", - peerblk->trim.start, peerblk->trim.end ); - } - DBGC2 ( peerblk, "\n" ); - - /* Open discovery */ - if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id, - peerblk->digestsize ) ) != 0 ) - goto err_open_discovery; - - /* Schedule a retry attempt either immediately (if we already - * have some peers) or after the discovery timeout. - */ - timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ? - ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 ); - start_timer_fixed ( &peerblk->timer, timeout ); - - /* Record start time */ - peerblk->started = peerblk_timestamp(); - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( xfer, &peerblk->xfer ); - ref_put ( &peerblk->refcnt ); - return 0; - - err_open_discovery: - peerblk_close ( peerblk, rc ); - err_alloc: - return rc; -} diff --git a/qemu/roms/ipxe/src/net/peerdisc.c b/qemu/roms/ipxe/src/net/peerdisc.c deleted file mode 100644 index 5b0e98911..000000000 --- a/qemu/roms/ipxe/src/net/peerdisc.c +++ /dev/null @@ -1,551 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/xfer.h> -#include <ipxe/iobuf.h> -#include <ipxe/open.h> -#include <ipxe/tcpip.h> -#include <ipxe/uuid.h> -#include <ipxe/base16.h> -#include <ipxe/netdevice.h> -#include <ipxe/timer.h> -#include <ipxe/fault.h> -#include <ipxe/pccrd.h> -#include <ipxe/peerdisc.h> - -/** @file - * - * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery - * - */ - -/** List of discovery segments */ -static LIST_HEAD ( peerdisc_segments ); - -/** Number of repeated discovery attempts */ -#define PEERDISC_REPEAT_COUNT 2 - -/** Time between repeated discovery attempts */ -#define PEERDISC_REPEAT_TIMEOUT ( 1 * TICKS_PER_SEC ) - -/** Default discovery timeout (in seconds) */ -#define PEERDISC_DEFAULT_TIMEOUT_SECS 2 - -/** Recommended discovery timeout (in seconds) - * - * We reduce the recommended discovery timeout whenever a segment - * fails to discover any peers, and restore the default value whenever - * a valid discovery reply is received. We continue to send discovery - * requests even if the recommended timeout is reduced to zero. - * - * This strategy is intended to minimise discovery delays when no - * peers are available on the network, while allowing downloads to - * quickly switch back to using PeerDist acceleration if new peers - * become available. - */ -unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; - -static struct peerdisc_segment * peerdisc_find ( const char *id ); -static int peerdisc_discovered ( struct peerdisc_segment *segment, - const char *location ); - -/****************************************************************************** - * - * Discovery sockets - * - ****************************************************************************** - */ - -/** - * Open all PeerDist discovery sockets - * - * @ret rc Return status code - */ -static int peerdisc_socket_open ( void ) { - struct peerdisc_socket *socket; - int rc; - - /* Open each socket */ - for_each_table_entry ( socket, PEERDISC_SOCKETS ) { - if ( ( rc = xfer_open_socket ( &socket->xfer, SOCK_DGRAM, - &socket->address.sa, - NULL ) ) != 0 ) { - DBGC ( socket, "PEERDISC %s could not open socket: " - "%s\n", socket->name, strerror ( rc ) ); - goto err; - } - } - - return 0; - - err: - for_each_table_entry_continue_reverse ( socket, PEERDISC_SOCKETS ) - intf_restart ( &socket->xfer, rc ); - return rc; -} - -/** - * Attempt to transmit PeerDist discovery requests on all sockets - * - * @v uuid Message UUID string - * @v id Segment identifier string - */ -static void peerdisc_socket_tx ( const char *uuid, const char *id ) { - struct peerdisc_socket *socket; - struct net_device *netdev; - struct xfer_metadata meta; - union { - struct sockaddr sa; - struct sockaddr_tcpip st; - } address; - char *request; - size_t len; - int rc; - - /* Construct discovery request */ - request = peerdist_discovery_request ( uuid, id ); - if ( ! request ) - goto err_request; - len = strlen ( request ); - - /* Initialise data transfer metadata */ - memset ( &meta, 0, sizeof ( meta ) ); - meta.dest = &address.sa; - - /* Send message on each socket */ - for_each_table_entry ( socket, PEERDISC_SOCKETS ) { - - /* Initialise socket address */ - memcpy ( &address.sa, &socket->address.sa, - sizeof ( address.sa ) ); - - /* Send message on each open network device */ - for_each_netdev ( netdev ) { - - /* Skip unopened network devices */ - if ( ! netdev_is_open ( netdev ) ) - continue; - address.st.st_scope_id = netdev->index; - - /* Discard request (for test purposes) if applicable */ - if ( inject_fault ( PEERDISC_DISCARD_RATE ) ) - continue; - - /* Transmit request */ - if ( ( rc = xfer_deliver_raw_meta ( &socket->xfer, - request, len, - &meta ) ) != 0 ) { - DBGC ( socket, "PEERDISC %s could not transmit " - "via %s: %s\n", socket->name, - netdev->name, strerror ( rc ) ); - /* Contine to try other net devices/sockets */ - continue; - } - } - } - - free ( request ); - err_request: - return; -} - -/** - * Handle received PeerDist discovery reply - * - * @v socket PeerDist discovery socket - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int peerdisc_socket_rx ( struct peerdisc_socket *socket, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct peerdist_discovery_reply reply; - struct peerdisc_segment *segment; - char *id; - char *location; - int rc; - - /* Discard reply (for test purposes) if applicable */ - if ( ( rc = inject_fault ( PEERDISC_DISCARD_RATE ) ) != 0 ) - goto err; - - /* Parse reply */ - if ( ( rc = peerdist_discovery_reply ( iobuf->data, iob_len ( iobuf ), - &reply ) ) != 0 ) { - DBGC ( socket, "PEERDISC %s could not parse reply: %s\n", - socket->name, strerror ( rc ) ); - DBGC_HDA ( socket, 0, iobuf->data, iob_len ( iobuf ) ); - goto err; - } - - /* Any kind of discovery reply indicates that there are active - * peers on a local network, so restore the recommended - * discovery timeout to its default value for future requests. - */ - if ( peerdisc_timeout_secs != PEERDISC_DEFAULT_TIMEOUT_SECS ) { - DBGC ( socket, "PEERDISC %s restoring timeout to %d seconds\n", - socket->name, PEERDISC_DEFAULT_TIMEOUT_SECS ); - } - peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; - - /* Iterate over segment IDs */ - for ( id = reply.ids ; *id ; id += ( strlen ( id ) + 1 /* NUL */ ) ) { - - /* Find corresponding segment */ - segment = peerdisc_find ( id ); - if ( ! segment ) { - DBGC ( socket, "PEERDISC %s ignoring reply for %s\n", - socket->name, id ); - continue; - } - - /* Report all discovered peer locations */ - for ( location = reply.locations ; *location ; - location += ( strlen ( location ) + 1 /* NUL */ ) ) { - - /* Report discovered peer location */ - if ( ( rc = peerdisc_discovered ( segment, - location ) ) != 0 ) - goto err; - } - } - - err: - free_iob ( iobuf ); - return rc; -} - -/** - * Close all PeerDist discovery sockets - * - * @v rc Reason for close - */ -static void peerdisc_socket_close ( int rc ) { - struct peerdisc_socket *socket; - - /* Close all sockets */ - for_each_table_entry ( socket, PEERDISC_SOCKETS ) - intf_restart ( &socket->xfer, rc ); -} - -/** PeerDist discovery socket interface operations */ -static struct interface_operation peerdisc_socket_operations[] = { - INTF_OP ( xfer_deliver, struct peerdisc_socket *, peerdisc_socket_rx ), -}; - -/** PeerDist discovery socket interface descriptor */ -static struct interface_descriptor peerdisc_socket_desc = - INTF_DESC ( struct peerdisc_socket, xfer, peerdisc_socket_operations ); - -/** PeerDist discovery IPv4 socket */ -struct peerdisc_socket peerdisc_socket_ipv4 __peerdisc_socket = { - .name = "IPv4", - .address = { - .sin = { - .sin_family = AF_INET, - .sin_port = htons ( PEERDIST_DISCOVERY_PORT ), - .sin_addr.s_addr = htonl ( PEERDIST_DISCOVERY_IPV4 ), - }, - }, - .xfer = INTF_INIT ( peerdisc_socket_desc ), -}; - -/** PeerDist discovery IPv6 socket */ -struct peerdisc_socket peerdisc_socket_ipv6 __peerdisc_socket = { - .name = "IPv6", - .address = { - .sin6 = { - .sin6_family = AF_INET6, - .sin6_port = htons ( PEERDIST_DISCOVERY_PORT ), - .sin6_addr.s6_addr = PEERDIST_DISCOVERY_IPV6, - }, - }, - .xfer = INTF_INIT ( peerdisc_socket_desc ), -}; - -/****************************************************************************** - * - * Discovery segments - * - ****************************************************************************** - */ - -/** - * Free PeerDist discovery segment - * - * @v refcnt Reference count - */ -static void peerdisc_free ( struct refcnt *refcnt ) { - struct peerdisc_segment *segment = - container_of ( refcnt, struct peerdisc_segment, refcnt ); - struct peerdisc_peer *peer; - struct peerdisc_peer *tmp; - - /* Free all discovered peers */ - list_for_each_entry_safe ( peer, tmp, &segment->peers, list ) { - list_del ( &peer->list ); - free ( peer ); - } - - /* Free segment */ - free ( segment ); -} - -/** - * Find PeerDist discovery segment - * - * @v id Segment ID - * @ret segment PeerDist discovery segment, or NULL if not found - */ -static struct peerdisc_segment * peerdisc_find ( const char *id ) { - struct peerdisc_segment *segment; - - /* Look for a matching segment */ - list_for_each_entry ( segment, &peerdisc_segments, list ) { - if ( strcmp ( id, segment->id ) == 0 ) - return segment; - } - - return NULL; -} - -/** - * Add discovered PeerDist peer - * - * @v segment PeerDist discovery segment - * @v location Peer location - * @ret rc Return status code - */ -static int peerdisc_discovered ( struct peerdisc_segment *segment, - const char *location ) { - struct peerdisc_peer *peer; - struct peerdisc_client *peerdisc; - struct peerdisc_client *tmp; - - /* Ignore duplicate peers */ - list_for_each_entry ( peer, &segment->peers, list ) { - if ( strcmp ( peer->location, location ) == 0 ) { - DBGC2 ( segment, "PEERDISC %p duplicate %s\n", - segment, location ); - return 0; - } - } - DBGC2 ( segment, "PEERDISC %p discovered %s\n", segment, location ); - - /* Allocate and initialise structure */ - peer = zalloc ( sizeof ( *peer ) + strlen ( location ) + 1 /* NUL */ ); - if ( ! peer ) - return -ENOMEM; - strcpy ( peer->location, location ); - - /* Add to end of list of peers */ - list_add_tail ( &peer->list, &segment->peers ); - - /* Notify all clients */ - list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list ) - peerdisc->op->discovered ( peerdisc ); - - return 0; -} - -/** - * Handle discovery timer expiry - * - * @v timer Discovery timer - * @v over Failure indicator - */ -static void peerdisc_expired ( struct retry_timer *timer, int over __unused ) { - struct peerdisc_segment *segment = - container_of ( timer, struct peerdisc_segment, timer ); - - /* Attempt to transmit discovery requests */ - peerdisc_socket_tx ( segment->uuid, segment->id ); - - /* Schedule next transmission, if applicable */ - if ( timer->count < PEERDISC_REPEAT_COUNT ) - start_timer_fixed ( &segment->timer, PEERDISC_REPEAT_TIMEOUT ); -} - -/** - * Create PeerDist discovery segment - * - * @v id Segment ID - * @ret segment PeerDist discovery segment, or NULL on error - */ -static struct peerdisc_segment * peerdisc_create ( const char *id ) { - struct peerdisc_segment *segment; - union { - union uuid uuid; - uint32_t dword[ sizeof ( union uuid ) / sizeof ( uint32_t ) ]; - } random_uuid; - size_t uuid_len; - size_t id_len; - char *uuid; - char *uuid_copy; - char *id_copy; - unsigned int i; - - /* Generate a random message UUID. This does not require high - * quality randomness. - */ - for ( i = 0 ; i < ( sizeof ( random_uuid.dword ) / - sizeof ( random_uuid.dword[0] ) ) ; i++ ) - random_uuid.dword[i] = random(); - uuid = uuid_ntoa ( &random_uuid.uuid ); - - /* Calculate string lengths */ - id_len = ( strlen ( id ) + 1 /* NUL */ ); - uuid_len = ( strlen ( uuid ) + 1 /* NUL */ ); - - /* Allocate and initialise structure */ - segment = zalloc ( sizeof ( *segment ) + id_len + uuid_len ); - if ( ! segment ) - return NULL; - id_copy = ( ( ( void * ) segment ) + sizeof ( *segment ) ); - memcpy ( id_copy, id, id_len ); - uuid_copy = ( ( ( void * ) id_copy ) + id_len ); - memcpy ( uuid_copy, uuid, uuid_len ); - ref_init ( &segment->refcnt, peerdisc_free ); - segment->id = id_copy; - segment->uuid = uuid_copy; - INIT_LIST_HEAD ( &segment->peers ); - INIT_LIST_HEAD ( &segment->clients ); - timer_init ( &segment->timer, peerdisc_expired, &segment->refcnt ); - DBGC2 ( segment, "PEERDISC %p discovering %s\n", segment, segment->id ); - - /* Start discovery timer */ - start_timer_nodelay ( &segment->timer ); - - /* Add to list of segments, transfer reference to list, and return */ - list_add_tail ( &segment->list, &peerdisc_segments ); - return segment; -} - -/** - * Destroy PeerDist discovery segment - * - * @v segment PeerDist discovery segment - */ -static void peerdisc_destroy ( struct peerdisc_segment *segment ) { - - /* Sanity check */ - assert ( list_empty ( &segment->clients ) ); - - /* Stop timer */ - stop_timer ( &segment->timer ); - - /* Remove from list of segments and drop list's reference */ - list_del ( &segment->list ); - ref_put ( &segment->refcnt ); -} - -/****************************************************************************** - * - * Discovery clients - * - ****************************************************************************** - */ - -/** - * Open PeerDist discovery client - * - * @v peerdisc PeerDist discovery client - * @v id Segment ID - * @v len Length of segment ID - * @ret rc Return status code - */ -int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, - size_t len ) { - struct peerdisc_segment *segment; - char id_string[ base16_encoded_len ( len ) + 1 /* NUL */ ]; - char *id_chr; - int rc; - - /* Construct ID string */ - base16_encode ( id, len, id_string, sizeof ( id_string ) ); - for ( id_chr = id_string ; *id_chr ; id_chr++ ) - *id_chr = toupper ( *id_chr ); - - /* Sanity check */ - assert ( peerdisc->segment == NULL ); - - /* Open socket if this is the first segment */ - if ( list_empty ( &peerdisc_segments ) && - ( ( rc = peerdisc_socket_open() ) != 0 ) ) - return rc; - - /* Find or create segment */ - if ( ! ( ( segment = peerdisc_find ( id_string ) ) || - ( segment = peerdisc_create ( id_string ) ) ) ) - return -ENOMEM; - - /* Add to list of clients */ - ref_get ( &segment->refcnt ); - peerdisc->segment = segment; - list_add_tail ( &peerdisc->list, &segment->clients ); - - return 0; -} - -/** - * Close PeerDist discovery client - * - * @v peerdisc PeerDist discovery client - */ -void peerdisc_close ( struct peerdisc_client *peerdisc ) { - struct peerdisc_segment *segment = peerdisc->segment; - - /* Ignore if discovery is already closed */ - if ( ! segment ) - return; - - /* If no peers were discovered, reduce the recommended - * discovery timeout to minimise delays on future requests. - */ - if ( list_empty ( &segment->peers ) && peerdisc_timeout_secs ) { - peerdisc_timeout_secs--; - DBGC ( segment, "PEERDISC %p reducing timeout to %d " - "seconds\n", peerdisc, peerdisc_timeout_secs ); - } - - /* Remove from list of clients */ - peerdisc->segment = NULL; - list_del ( &peerdisc->list ); - ref_put ( &segment->refcnt ); - - /* If this was the last clients, destroy the segment */ - if ( list_empty ( &segment->clients ) ) - peerdisc_destroy ( segment ); - - /* If there are no more segments, close the socket */ - if ( list_empty ( &peerdisc_segments ) ) - peerdisc_socket_close ( 0 ); -} diff --git a/qemu/roms/ipxe/src/net/peerdist.c b/qemu/roms/ipxe/src/net/peerdist.c deleted file mode 100644 index 48933f951..000000000 --- a/qemu/roms/ipxe/src/net/peerdist.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdio.h> -#include <ipxe/http.h> -#include <ipxe/peermux.h> - -/** @file - * - * Peer Content Caching and Retrieval (PeerDist) protocol - * - * This is quite possibly the ugliest protocol I have ever had the - * misfortune to encounter, and I've encountered multicast TFTP. - */ - -/** - * Check whether or not to support PeerDist encoding for this request - * - * @v http HTTP transaction - * @ret supported PeerDist encoding is supported for this request - */ -static int http_peerdist_supported ( struct http_transaction *http ) { - - /* Support PeerDist encoding only if we can directly access an - * underlying data transfer buffer. Direct access is required - * in order to support decryption of data received via the - * retrieval protocol (which provides the AES initialisation - * vector only after all of the encrypted data has been - * received). - * - * This test simultaneously ensures that we do not attempt to - * use PeerDist encoding on a request which is itself a - * PeerDist individual block download, since the individual - * block downloads do not themselves provide direct access to - * an underlying data transfer buffer. - */ - return ( xfer_buffer ( &http->xfer ) != NULL ); -} - -/** - * Format HTTP "X-P2P-PeerDist" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_p2p_peerdist ( struct http_transaction *http, - char *buf, size_t len ) { - int supported = http_peerdist_supported ( http ); - int missing; - - /* PeerDist wants us to inform the server whenever we make a - * request for data that was missing from local peers - * (presumably for statistical purposes only). We use the - * heuristic of assuming that the combination of "this request - * may not itself use PeerDist content encoding" and "this is - * a range request" probably indicates that we are making a - * PeerDist block raw range request for missing data. - */ - missing = ( http->request.range.len && ( ! supported ) ); - - /* Omit header if PeerDist encoding is not supported and we - * are not reporting a missing data request. - */ - if ( ! ( supported || missing ) ) - return 0; - - /* Construct header */ - return snprintf ( buf, len, "Version=1.1%s", - ( missing ? ", MissingDataRequest=true" : "" ) ); -} - -/** HTTP "X-P2P-PeerDist" header */ -struct http_request_header http_request_p2p_peerdist __http_request_header = { - .name = "X-P2P-PeerDist", - .format = http_format_p2p_peerdist, -}; - -/** - * Format HTTP "X-P2P-PeerDistEx" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_p2p_peerdistex ( struct http_transaction *http, - char *buf, size_t len ) { - int supported = http_peerdist_supported ( http ); - - /* Omit header if PeerDist encoding is not supported */ - if ( ! supported ) - return 0; - - /* Construct header */ - return snprintf ( buf, len, ( "MinContentInformation=1.0, " - "MaxContentInformation=2.0" ) ); -} - -/** HTTP "X-P2P-PeerDist" header */ -struct http_request_header http_request_p2p_peerdistex __http_request_header = { - .name = "X-P2P-PeerDistEx", - .format = http_format_p2p_peerdistex, -}; - -/** - * Initialise PeerDist content encoding - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_peerdist_init ( struct http_transaction *http ) { - - return peermux_filter ( &http->content, &http->transfer, http->uri ); -} - -/** PeerDist HTTP content encoding */ -struct http_content_encoding peerdist_encoding __http_content_encoding = { - .name = "peerdist", - .supported = http_peerdist_supported, - .init = http_peerdist_init, -}; diff --git a/qemu/roms/ipxe/src/net/peermux.c b/qemu/roms/ipxe/src/net/peermux.c deleted file mode 100644 index 634c69992..000000000 --- a/qemu/roms/ipxe/src/net/peermux.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <errno.h> -#include <ipxe/uri.h> -#include <ipxe/xferbuf.h> -#include <ipxe/peerblk.h> -#include <ipxe/peermux.h> - -/** @file - * - * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer - * - */ - -/** - * Free PeerDist download multiplexer - * - * @v refcnt Reference count - */ -static void peermux_free ( struct refcnt *refcnt ) { - struct peerdist_multiplexer *peermux = - container_of ( refcnt, struct peerdist_multiplexer, refcnt ); - - uri_put ( peermux->uri ); - xferbuf_free ( &peermux->buffer ); - free ( peermux ); -} - -/** - * Close PeerDist download multiplexer - * - * @v peermux PeerDist download multiplexer - * @v rc Reason for close - */ -static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) { - unsigned int i; - - /* Stop block download initiation process */ - process_del ( &peermux->process ); - - /* Shut down all block downloads */ - for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) - intf_shutdown ( &peermux->block[i].xfer, rc ); - - /* Shut down all other interfaces (which may be connected to - * the same object). - */ - intf_nullify ( &peermux->info ); /* avoid potential loops */ - intf_shutdown ( &peermux->xfer, rc ); - intf_shutdown ( &peermux->info, rc ); -} - -/** - * Receive content information - * - * @v peermux PeerDist download multiplexer - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int peermux_info_deliver ( struct peerdist_multiplexer *peermux, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - int rc; - - /* Add data to buffer */ - if ( ( rc = xferbuf_deliver ( &peermux->buffer, iobuf, meta ) ) != 0 ) - goto err; - - return 0; - - err: - peermux_close ( peermux, rc ); - return rc; -} - -/** - * Close content information interface - * - * @v peermux PeerDist download multiplexer - * @v rc Reason for close - */ -static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){ - struct peerdist_info *info = &peermux->cache.info; - size_t len; - - /* Terminate download on error */ - if ( rc != 0 ) - goto err; - - /* Successfully closing the content information interface - * indicates that the content information has been fully - * received, and initiates the actual PeerDist download. - */ - - /* Shut down content information interface */ - intf_shutdown ( &peermux->info, rc ); - - /* Parse content information */ - if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len, - info ) ) != 0 ) { - DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n", - peermux, strerror ( rc ) ); - goto err; - } - - /* Notify recipient of total download size */ - len = ( info->trim.end - info->trim.start ); - if ( ( rc = xfer_seek ( &peermux->xfer, len ) ) != 0 ) { - DBGC ( peermux, "PEERMUX %p could not presize buffer: %s\n", - peermux, strerror ( rc ) ); - goto err; - } - xfer_seek ( &peermux->xfer, 0 ); - - /* Start block download process */ - process_add ( &peermux->process ); - - return; - - err: - peermux_close ( peermux, rc ); -} - -/** - * Initiate multiplexed block download - * - * @v peermux PeerDist download multiplexer - */ -static void peermux_step ( struct peerdist_multiplexer *peermux ) { - struct peerdist_info *info = &peermux->cache.info; - struct peerdist_info_segment *segment = &peermux->cache.segment; - struct peerdist_info_block *block = &peermux->cache.block; - struct peerdist_multiplexed_block *peermblk; - unsigned int next_segment; - unsigned int next_block; - int rc; - - /* Stop initiation process if all block downloads are busy */ - peermblk = list_first_entry ( &peermux->idle, - struct peerdist_multiplexed_block, list ); - if ( ! peermblk ) { - process_del ( &peermux->process ); - return; - } - - /* Increment block index */ - next_block = ( block->index + 1 ); - - /* Move to first/next segment, if applicable */ - if ( next_block >= segment->blocks ) { - - /* Reset block index */ - next_block = 0; - - /* Calculate segment index */ - next_segment = ( segment->info ? ( segment->index + 1 ) : 0 ); - - /* If we have finished all segments and have no - * remaining block downloads, then we are finished. - */ - if ( next_segment >= info->segments ) { - process_del ( &peermux->process ); - if ( list_empty ( &peermux->busy ) ) - peermux_close ( peermux, 0 ); - return; - } - - /* Get content information segment */ - if ( ( rc = peerdist_info_segment ( info, segment, - next_segment ) ) != 0 ) { - DBGC ( peermux, "PEERMUX %p could not get segment %d " - "information: %s\n", peermux, next_segment, - strerror ( rc ) ); - goto err; - } - } - - /* Get content information block */ - if ( ( rc = peerdist_info_block ( segment, block, next_block ) ) != 0 ){ - DBGC ( peermux, "PEERMUX %p could not get segment %d block " - "%d information: %s\n", peermux, segment->index, - next_block, strerror ( rc ) ); - goto err; - } - - /* Ignore block if it lies entirely outside the trimmed range */ - if ( block->trim.start == block->trim.end ) { - DBGC ( peermux, "PEERMUX %p skipping segment %d block %d\n", - peermux, segment->index, block->index ); - return; - } - - /* Start downloading this block */ - if ( ( rc = peerblk_open ( &peermblk->xfer, peermux->uri, - block ) ) != 0 ) { - DBGC ( peermux, "PEERMUX %p could not start download for " - "segment %d block %d: %s\n", peermux, segment->index, - block->index, strerror ( rc ) ); - goto err; - } - - /* Move to list of busy block downloads */ - list_del ( &peermblk->list ); - list_add_tail ( &peermblk->list, &peermux->busy ); - - return; - - err: - peermux_close ( peermux, rc ); -} - -/** - * Receive data from multiplexed block download - * - * @v peermblk PeerDist multiplexed block download - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int peermux_block_deliver ( struct peerdist_multiplexed_block *peermblk, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct peerdist_multiplexer *peermux = peermblk->peermux; - - /* Sanity check: all block downloads must use absolute - * positions for all deliveries, since they run concurrently. - */ - assert ( meta->flags & XFER_FL_ABS_OFFSET ); - - /* We can't use a simple passthrough interface descriptor, - * since there are multiple block download interfaces. - */ - return xfer_deliver ( &peermux->xfer, iob_disown ( iobuf ), meta ); -} - -/** - * Get multiplexed block download underlying data transfer buffer - * - * @v peermblk PeerDist multiplexed download block - * @ret xferbuf Data transfer buffer, or NULL on error - */ -static struct xfer_buffer * -peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) { - struct peerdist_multiplexer *peermux = peermblk->peermux; - - /* We can't use a simple passthrough interface descriptor, - * since there are multiple block download interfaces. - */ - return xfer_buffer ( &peermux->xfer ); -} - -/** - * Close multiplexed block download - * - * @v peermblk PeerDist multiplexed block download - * @v rc Reason for close - */ -static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk, - int rc ) { - struct peerdist_multiplexer *peermux = peermblk->peermux; - - /* Move to list of idle downloads */ - list_del ( &peermblk->list ); - list_add_tail ( &peermblk->list, &peermux->idle ); - - /* If any error occurred, terminate the whole multiplexer */ - if ( rc != 0 ) { - peermux_close ( peermux, rc ); - return; - } - - /* Restart data transfer interface */ - intf_restart ( &peermblk->xfer, rc ); - - /* Restart block download initiation process */ - process_add ( &peermux->process ); -} - -/** Data transfer interface operations */ -static struct interface_operation peermux_xfer_operations[] = { - INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ), -}; - -/** Data transfer interface descriptor */ -static struct interface_descriptor peermux_xfer_desc = - INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, xfer, - peermux_xfer_operations, info ); - -/** Content information interface operations */ -static struct interface_operation peermux_info_operations[] = { - INTF_OP ( xfer_deliver, struct peerdist_multiplexer *, - peermux_info_deliver ), - INTF_OP ( intf_close, struct peerdist_multiplexer *, - peermux_info_close ), -}; - -/** Content information interface descriptor */ -static struct interface_descriptor peermux_info_desc = - INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, info, - peermux_info_operations, xfer ); - -/** Block download data transfer interface operations */ -static struct interface_operation peermux_block_operations[] = { - INTF_OP ( xfer_deliver, struct peerdist_multiplexed_block *, - peermux_block_deliver ), - INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *, - peermux_block_buffer ), - INTF_OP ( intf_close, struct peerdist_multiplexed_block *, - peermux_block_close ), -}; - -/** Block download data transfer interface descriptor */ -static struct interface_descriptor peermux_block_desc = - INTF_DESC ( struct peerdist_multiplexed_block, xfer, - peermux_block_operations ); - -/** Block download initiation process descriptor */ -static struct process_descriptor peermux_process_desc = - PROC_DESC ( struct peerdist_multiplexer, process, peermux_step ); - -/** - * Add PeerDist content-encoding filter - * - * @v xfer Data transfer interface - * @v info Content information interface - * @v uri Original URI - * @ret rc Return status code - */ -int peermux_filter ( struct interface *xfer, struct interface *info, - struct uri *uri ) { - struct peerdist_multiplexer *peermux; - struct peerdist_multiplexed_block *peermblk; - unsigned int i; - - /* Allocate and initialise structure */ - peermux = zalloc ( sizeof ( *peermux ) ); - if ( ! peermux ) - return -ENOMEM; - ref_init ( &peermux->refcnt, peermux_free ); - intf_init ( &peermux->xfer, &peermux_xfer_desc, &peermux->refcnt ); - intf_init ( &peermux->info, &peermux_info_desc, &peermux->refcnt ); - peermux->uri = uri_get ( uri ); - xferbuf_umalloc_init ( &peermux->buffer, - &peermux->cache.info.raw.data ); - process_init_stopped ( &peermux->process, &peermux_process_desc, - &peermux->refcnt ); - INIT_LIST_HEAD ( &peermux->busy ); - INIT_LIST_HEAD ( &peermux->idle ); - for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) { - peermblk = &peermux->block[i]; - peermblk->peermux = peermux; - list_add_tail ( &peermblk->list, &peermux->idle ); - intf_init ( &peermblk->xfer, &peermux_block_desc, - &peermux->refcnt ); - } - - /* Attach to parent interfaces, mortalise self, and return */ - intf_plug_plug ( &peermux->xfer, xfer ); - intf_plug_plug ( &peermux->info, info ); - ref_put ( &peermux->refcnt ); - return 0; -} diff --git a/qemu/roms/ipxe/src/net/ping.c b/qemu/roms/ipxe/src/net/ping.c deleted file mode 100644 index 3f4fa5c11..000000000 --- a/qemu/roms/ipxe/src/net/ping.c +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/refcnt.h> -#include <ipxe/list.h> -#include <ipxe/iobuf.h> -#include <ipxe/tcpip.h> -#include <ipxe/icmp.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/netdevice.h> -#include <ipxe/ping.h> - -/** @file - * - * ICMP ping protocol - * - */ - -/** - * A ping connection - * - */ -struct ping_connection { - /** Reference counter */ - struct refcnt refcnt; - /** List of ping connections */ - struct list_head list; - - /** Remote socket address */ - struct sockaddr_tcpip peer; - /** Local port number */ - uint16_t port; - - /** Data transfer interface */ - struct interface xfer; -}; - -/** List of registered ping connections */ -static LIST_HEAD ( ping_conns ); - -/** - * Identify ping connection by local port number - * - * @v port Local port number - * @ret ping Ping connection, or NULL - */ -static struct ping_connection * ping_demux ( unsigned int port ) { - struct ping_connection *ping; - - list_for_each_entry ( ping, &ping_conns, list ) { - if ( ping->port == port ) - return ping; - } - return NULL; -} - -/** - * Check if local port number is available - * - * @v port Local port number - * @ret port Local port number, or negative error - */ -static int ping_port_available ( int port ) { - - return ( ping_demux ( port ) ? -EADDRINUSE : port ); -} - -/** - * Process ICMP ping reply - * - * @v iobuf I/O buffer - * @v st_src Source address - * @ret rc Return status code - */ -int ping_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src ) { - struct icmp_echo *echo = iobuf->data; - struct ping_connection *ping; - struct xfer_metadata meta; - int rc; - - /* Sanity check: should already have been checked by ICMP layer */ - assert ( iob_len ( iobuf ) >= sizeof ( *echo ) ); - - /* Identify connection */ - ping = ping_demux ( ntohs ( echo->ident ) ); - DBGC ( ping, "PING %p reply id %#04x seq %#04x\n", - ping, ntohs ( echo->ident ), ntohs ( echo->sequence ) ); - if ( ! ping ) { - rc = -ENOTCONN; - goto discard; - } - - /* Strip header, construct metadata, and pass data to upper layer */ - iob_pull ( iobuf, sizeof ( *echo ) ); - memset ( &meta, 0, sizeof ( meta ) ); - meta.src = ( ( struct sockaddr * ) st_src ); - meta.flags = XFER_FL_ABS_OFFSET; - meta.offset = ntohs ( echo->sequence ); - return xfer_deliver ( &ping->xfer, iob_disown ( iobuf ), &meta ); - - discard: - free_iob ( iobuf ); - return rc; -} - -/** - * Allocate I/O buffer for ping - * - * @v ping Ping connection - * @v len Payload size - * @ret iobuf I/O buffer, or NULL - */ -static struct io_buffer * -ping_alloc_iob ( struct ping_connection *ping __unused, size_t len ) { - size_t header_len; - struct io_buffer *iobuf; - - header_len = ( MAX_LL_NET_HEADER_LEN + sizeof ( struct icmp_echo ) ); - iobuf = alloc_iob ( header_len + len ); - if ( iobuf ) - iob_reserve ( iobuf, header_len ); - return iobuf; -} - -/** - * Deliver datagram as I/O buffer - * - * @v ping Ping connection - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int ping_deliver ( struct ping_connection *ping, struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct icmp_echo *echo = iob_push ( iobuf, sizeof ( *echo ) ); - int rc; - - /* Construct header */ - memset ( echo, 0, sizeof ( *echo ) ); - echo->ident = htons ( ping->port ); - echo->sequence = htons ( meta->offset ); - - /* Transmit echo request */ - if ( ( rc = icmp_tx_echo_request ( iob_disown ( iobuf ), - &ping->peer ) ) != 0 ) { - DBGC ( ping, "PING %p could not transmit: %s\n", - ping, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Close ping connection - * - * @v ping Ping connection - * @v rc Reason for close - */ -static void ping_close ( struct ping_connection *ping, int rc ) { - - /* Close data transfer interface */ - intf_shutdown ( &ping->xfer, rc ); - - /* Remove from list of connections and drop list's reference */ - list_del ( &ping->list ); - ref_put ( &ping->refcnt ); - - DBGC ( ping, "PING %p closed\n", ping ); -} - -/** Ping data transfer interface operations */ -static struct interface_operation ping_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct ping_connection *, ping_deliver ), - INTF_OP ( xfer_alloc_iob, struct ping_connection *, ping_alloc_iob ), - INTF_OP ( intf_close, struct ping_connection *, ping_close ), -}; - -/** Ping data transfer interface descriptor */ -static struct interface_descriptor ping_xfer_desc = - INTF_DESC ( struct ping_connection, xfer, ping_xfer_operations ); - -/** - * Open a ping connection - * - * @v xfer Data transfer interface - * @v peer Peer socket address - * @v local Local socket address, or NULL - * @ret rc Return status code - */ -static int ping_open ( struct interface *xfer, struct sockaddr *peer, - struct sockaddr *local ) { - struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; - struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; - struct ping_connection *ping; - int port; - int rc; - - /* Allocate and initialise structure */ - ping = zalloc ( sizeof ( *ping ) ); - if ( ! ping ) { - rc = -ENOMEM; - goto err_alloc; - } - DBGC ( ping, "PING %p allocated\n", ping ); - ref_init ( &ping->refcnt, NULL ); - intf_init ( &ping->xfer, &ping_xfer_desc, &ping->refcnt ); - memcpy ( &ping->peer, st_peer, sizeof ( ping->peer ) ); - - /* Bind to local port */ - port = tcpip_bind ( st_local, ping_port_available ); - if ( port < 0 ) { - rc = port; - DBGC ( ping, "PING %p could not bind: %s\n", - ping, strerror ( rc ) ); - goto err_bind; - } - ping->port = port; - DBGC ( ping, "PING %p bound to id %#04x\n", ping, port ); - - /* Attach parent interface, transfer reference to connection - * list, and return - */ - intf_plug_plug ( &ping->xfer, xfer ); - list_add ( &ping->list, &ping_conns ); - return 0; - - err_bind: - ref_put ( &ping->refcnt ); - err_alloc: - return rc; -} - -/** Ping IPv4 socket opener */ -struct socket_opener ping_ipv4_socket_opener __socket_opener = { - .semantics = PING_SOCK_ECHO, - .family = AF_INET, - .open = ping_open, -}; - -/** Ping IPv6 socket opener */ -struct socket_opener ping_ipv6_socket_opener __socket_opener = { - .semantics = PING_SOCK_ECHO, - .family = AF_INET6, - .open = ping_open, -}; - -/** Linkage hack */ -int ping_sock_echo = PING_SOCK_ECHO; diff --git a/qemu/roms/ipxe/src/net/rarp.c b/qemu/roms/ipxe/src/net/rarp.c deleted file mode 100644 index c194a404f..000000000 --- a/qemu/roms/ipxe/src/net/rarp.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <byteswap.h> -#include <ipxe/netdevice.h> -#include <ipxe/iobuf.h> -#include <ipxe/if_ether.h> -#include <ipxe/rarp.h> - -/** @file - * - * Reverse Address Resolution Protocol - * - */ - -/** - * Process incoming ARP packets - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - * - * This is a dummy method which simply discards RARP packets. - */ -static int rarp_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - const void *ll_dest __unused, - const void *ll_source __unused, - unsigned int flags __unused ) { - free_iob ( iobuf ); - return 0; -} - - -/** - * Transcribe RARP address - * - * @v net_addr RARP address - * @ret string "<RARP>" - * - * This operation is meaningless for the RARP protocol. - */ -static const char * rarp_ntoa ( const void *net_addr __unused ) { - return "<RARP>"; -} - -/** RARP protocol */ -struct net_protocol rarp_protocol __net_protocol = { - .name = "RARP", - .net_proto = htons ( ETH_P_RARP ), - .rx = rarp_rx, - .ntoa = rarp_ntoa, -}; diff --git a/qemu/roms/ipxe/src/net/retry.c b/qemu/roms/ipxe/src/net/retry.c deleted file mode 100644 index 734567be5..000000000 --- a/qemu/roms/ipxe/src/net/retry.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <ipxe/timer.h> -#include <ipxe/list.h> -#include <ipxe/process.h> -#include <ipxe/init.h> -#include <ipxe/retry.h> - -/** @file - * - * Retry timers - * - * A retry timer is a binary exponential backoff timer. It can be - * used to build automatic retransmission into network protocols. - * - * This implementation of the timer is designed to satisfy RFC 2988 - * and therefore be usable as a TCP retransmission timer. - * - */ - -/* The theoretical minimum that the algorithm in stop_timer() can - * adjust the timeout back down to is seven ticks, so set the minimum - * timeout to at least that value for the sake of consistency. - */ -#define MIN_TIMEOUT 7 - -/** List of running timers */ -static LIST_HEAD ( timers ); - -/** - * Start timer with a specified timeout - * - * @v timer Retry timer - * @v timeout Timeout, in ticks - * - * This starts the timer running with the specified timeout value. If - * stop_timer() is not called before the timer expires, the timer will - * be stopped and the timer's callback function will be called. - */ -void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { - - /* Add to list of running timers (if applicable) */ - if ( ! timer->running ) { - list_add ( &timer->list, &timers ); - ref_get ( timer->refcnt ); - timer->running = 1; - } - - /* Record start time */ - timer->start = currticks(); - - /* Record timeout */ - timer->timeout = timeout; - - DBGC2 ( timer, "Timer %p started at time %ld (expires at %ld)\n", - timer, timer->start, ( timer->start + timer->timeout ) ); -} - -/** - * Start timer - * - * @v timer Retry timer - * - * This starts the timer running with the current timeout value - * (rounded up to the minimum timeout value). If stop_timer() is not - * called before the timer expires, the timer will be stopped and the - * timer's callback function will be called. - */ -void start_timer ( struct retry_timer *timer ) { - unsigned long timeout = timer->timeout; - unsigned long min; - - /* Calculate minimum timeout */ - min = ( timer->min ? timer->min : DEFAULT_MIN_TIMEOUT ); - if ( min < MIN_TIMEOUT ) - min = MIN_TIMEOUT; - - /* Ensure timeout is at least the minimum */ - if ( timeout < min ) - timeout = min; - - /* Start timer with this timeout */ - start_timer_fixed ( timer, timeout ); -} - -/** - * Stop timer - * - * @v timer Retry timer - * - * This stops the timer and updates the timer's timeout value. - */ -void stop_timer ( struct retry_timer *timer ) { - unsigned long old_timeout = timer->timeout; - unsigned long now = currticks(); - unsigned long runtime; - - /* If timer was already stopped, do nothing */ - if ( ! timer->running ) - return; - - list_del ( &timer->list ); - runtime = ( now - timer->start ); - timer->running = 0; - DBGC2 ( timer, "Timer %p stopped at time %ld (ran for %ld)\n", - timer, now, runtime ); - - /* Update timer. Variables are: - * - * r = round-trip time estimate (i.e. runtime) - * t = timeout value (i.e. timer->timeout) - * s = smoothed round-trip time - * - * By choice, we set t = 4s, i.e. allow for four times the - * normal round-trip time to pass before retransmitting. - * - * We want to smooth according to s := ( 7 s + r ) / 8 - * - * Since we don't actually store s, this reduces to - * t := ( 7 t / 8 ) + ( r / 2 ) - * - */ - if ( timer->count ) { - timer->count--; - } else { - timer->timeout -= ( timer->timeout >> 3 ); - timer->timeout += ( runtime >> 1 ); - if ( timer->timeout != old_timeout ) { - DBGC ( timer, "Timer %p timeout updated to %ld\n", - timer, timer->timeout ); - } - } - - ref_put ( timer->refcnt ); -} - -/** - * Handle expired timer - * - * @v timer Retry timer - */ -static void timer_expired ( struct retry_timer *timer ) { - struct refcnt *refcnt = timer->refcnt; - unsigned long max = ( timer->max ? timer->max : DEFAULT_MAX_TIMEOUT ); - int fail; - - /* Stop timer without performing RTT calculations */ - DBGC2 ( timer, "Timer %p stopped at time %ld on expiry\n", - timer, currticks() ); - assert ( timer->running ); - list_del ( &timer->list ); - timer->running = 0; - timer->count++; - - /* Back off the timeout value */ - timer->timeout <<= 1; - if ( ( fail = ( timer->timeout > max ) ) ) - timer->timeout = max; - DBGC ( timer, "Timer %p timeout backed off to %ld\n", - timer, timer->timeout ); - - /* Call expiry callback */ - timer->expired ( timer, fail ); - /* If refcnt is NULL, then timer may already have been freed */ - - ref_put ( refcnt ); -} - -/** - * Poll the retry timer list - * - */ -void retry_poll ( void ) { - struct retry_timer *timer; - unsigned long now = currticks(); - unsigned long used; - - /* Process at most one timer expiry. We cannot process - * multiple expiries in one pass, because one timer expiring - * may end up triggering another timer's deletion from the - * list. - */ - list_for_each_entry ( timer, &timers, list ) { - used = ( now - timer->start ); - if ( used >= timer->timeout ) { - timer_expired ( timer ); - break; - } - } -} - -/** - * Single-step the retry timer list - * - * @v process Retry timer process - */ -static void retry_step ( struct process *process __unused ) { - retry_poll(); -} - -/** Retry timer process */ -PERMANENT_PROCESS ( retry_process, retry_step ); diff --git a/qemu/roms/ipxe/src/net/rndis.c b/qemu/roms/ipxe/src/net/rndis.c deleted file mode 100644 index 8c4fe8b30..000000000 --- a/qemu/roms/ipxe/src/net/rndis.c +++ /dev/null @@ -1,1052 +0,0 @@ -/* - * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** @file - * - * Remote Network Driver Interface Specification - * - */ - -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/iobuf.h> -#include <ipxe/netdevice.h> -#include <ipxe/ethernet.h> -#include <ipxe/device.h> -#include <ipxe/rndis.h> - -/** - * Allocate I/O buffer - * - * @v len Length - * @ret iobuf I/O buffer, or NULL - */ -static struct io_buffer * rndis_alloc_iob ( size_t len ) { - struct rndis_header *header; - struct io_buffer *iobuf; - - /* Allocate I/O buffer and reserve space */ - iobuf = alloc_iob ( sizeof ( *header ) + len ); - if ( iobuf ) - iob_reserve ( iobuf, sizeof ( *header ) ); - - return iobuf; -} - -/** - * Wait for completion - * - * @v rndis RNDIS device - * @v wait_id Request ID - * @ret rc Return status code - */ -static int rndis_wait ( struct rndis_device *rndis, unsigned int wait_id ) { - unsigned int i; - - /* Record query ID */ - rndis->wait_id = wait_id; - - /* Wait for operation to complete */ - for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) { - - /* Check for completion */ - if ( ! rndis->wait_id ) - return rndis->wait_rc; - - /* Poll RNDIS device */ - rndis->op->poll ( rndis ); - - /* Delay for 1ms */ - mdelay ( 1 ); - } - - DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n", - rndis->name, wait_id ); - return -ETIMEDOUT; -} - -/** - * Transmit message - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - * @v type Message type - * @ret rc Return status code - */ -static int rndis_tx_message ( struct rndis_device *rndis, - struct io_buffer *iobuf, unsigned int type ) { - struct rndis_header *header; - int rc; - - /* Prepend RNDIS header */ - header = iob_push ( iobuf, sizeof ( *header ) ); - header->type = cpu_to_le32 ( type ); - header->len = cpu_to_le32 ( iob_len ( iobuf ) ); - - /* Transmit message */ - if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not transmit: %s\n", - rndis->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Complete message transmission - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - * @v rc Packet status code - */ -void rndis_tx_complete_err ( struct rndis_device *rndis, - struct io_buffer *iobuf, int rc ) { - struct net_device *netdev = rndis->netdev; - struct rndis_header *header; - size_t len = iob_len ( iobuf ); - - /* Sanity check */ - if ( len < sizeof ( *header ) ) { - DBGC ( rndis, "RNDIS %s completed underlength transmission:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - netdev_tx_err ( netdev, NULL, -EINVAL ); - return; - } - header = iobuf->data; - - /* Complete buffer */ - if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) { - netdev_tx_complete_err ( netdev, iobuf, rc ); - } else { - free_iob ( iobuf ); - } -} - -/** - * Transmit data packet - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int rndis_tx_data ( struct rndis_device *rndis, - struct io_buffer *iobuf ) { - struct rndis_packet_message *msg; - size_t len = iob_len ( iobuf ); - int rc; - - /* Prepend packet message header */ - msg = iob_push ( iobuf, sizeof ( *msg ) ); - memset ( msg, 0, sizeof ( *msg ) ); - msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) ); - msg->data.len = cpu_to_le32 ( len ); - - /* Transmit message */ - if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Defer transmitted packet - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - * @ret rc Return status code - * - * As with netdev_tx_defer(), the caller must ensure that space in the - * transmit descriptor ring is freed up before calling - * rndis_tx_complete(). - * - * Unlike netdev_tx_defer(), this call may fail. - */ -int rndis_tx_defer ( struct rndis_device *rndis, struct io_buffer *iobuf ) { - struct net_device *netdev = rndis->netdev; - struct rndis_header *header; - struct rndis_packet_message *msg; - - /* Fail unless this was a packet message. Only packet - * messages correspond to I/O buffers in the network device's - * TX queue; other messages cannot be deferred in this way. - */ - assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); - header = iobuf->data; - if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) ) - return -ENOTSUP; - - /* Strip RNDIS header and packet message header, to return - * this packet to the state in which we received it. - */ - iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) ); - - /* Defer packet */ - netdev_tx_defer ( netdev, iobuf ); - - return 0; -} - -/** - * Receive data packet - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - */ -static void rndis_rx_data ( struct rndis_device *rndis, - struct io_buffer *iobuf ) { - struct net_device *netdev = rndis->netdev; - struct rndis_packet_message *msg; - size_t len = iob_len ( iobuf ); - size_t data_offset; - size_t data_len; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *msg ) ) { - DBGC ( rndis, "RNDIS %s received underlength data packet:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_len; - } - msg = iobuf->data; - - /* Locate and sanity check data buffer */ - data_offset = le32_to_cpu ( msg->data.offset ); - data_len = le32_to_cpu ( msg->data.len ); - if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) { - DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_data; - } - - /* Strip non-data portions */ - iob_pull ( iobuf, data_offset ); - iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) ); - - /* Hand off to network stack */ - netdev_rx ( netdev, iob_disown ( iobuf ) ); - - return; - - err_data: - err_len: - /* Report error to network stack */ - netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); -} - -/** - * Transmit initialisation message - * - * @v rndis RNDIS device - * @v id Request ID - * @ret rc Return status code - */ -static int rndis_tx_initialise ( struct rndis_device *rndis, unsigned int id ) { - struct io_buffer *iobuf; - struct rndis_initialise_message *msg; - int rc; - - /* Allocate I/O buffer */ - iobuf = rndis_alloc_iob ( sizeof ( *msg ) ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Construct message */ - msg = iob_put ( iobuf, sizeof ( *msg ) ); - memset ( msg, 0, sizeof ( *msg ) ); - msg->id = id; /* Non-endian */ - msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR ); - msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR ); - msg->mtu = cpu_to_le32 ( RNDIS_MTU ); - - /* Transmit message */ - if ( ( rc = rndis_tx_message ( rndis, iobuf, - RNDIS_INITIALISE_MSG ) ) != 0 ) - goto err_tx; - - return 0; - - err_tx: - free_iob ( iobuf ); - err_alloc: - return rc; -} - -/** - * Receive initialisation completion - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - */ -static void rndis_rx_initialise ( struct rndis_device *rndis, - struct io_buffer *iobuf ) { - struct rndis_initialise_completion *cmplt; - size_t len = iob_len ( iobuf ); - unsigned int id; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *cmplt ) ) { - DBGC ( rndis, "RNDIS %s received underlength initialisation " - "completion:\n", rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_len; - } - cmplt = iobuf->data; - - /* Extract request ID */ - id = cmplt->id; /* Non-endian */ - - /* Check status */ - if ( cmplt->status ) { - DBGC ( rndis, "RNDIS %s received initialisation completion " - "failure %#08x\n", rndis->name, - le32_to_cpu ( cmplt->status ) ); - rc = -EIO; - goto err_status; - } - - /* Success */ - rc = 0; - - err_status: - /* Record completion result if applicable */ - if ( id == rndis->wait_id ) { - rndis->wait_id = 0; - rndis->wait_rc = rc; - } - err_len: - free_iob ( iobuf ); -} - -/** - * Initialise RNDIS - * - * @v rndis RNDIS device - * @ret rc Return status code - */ -static int rndis_initialise ( struct rndis_device *rndis ) { - int rc; - - /* Transmit initialisation message */ - if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 ) - return rc; - - /* Wait for response */ - if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Transmit halt message - * - * @v rndis RNDIS device - * @ret rc Return status code - */ -static int rndis_tx_halt ( struct rndis_device *rndis ) { - struct io_buffer *iobuf; - struct rndis_halt_message *msg; - int rc; - - /* Allocate I/O buffer */ - iobuf = rndis_alloc_iob ( sizeof ( *msg ) ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Construct message */ - msg = iob_put ( iobuf, sizeof ( *msg ) ); - memset ( msg, 0, sizeof ( *msg ) ); - - /* Transmit message */ - if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_HALT_MSG ) ) != 0 ) - goto err_tx; - - return 0; - - err_tx: - free_iob ( iobuf ); - err_alloc: - return rc; -} - -/** - * Halt RNDIS - * - * @v rndis RNDIS device - * @ret rc Return status code - */ -static int rndis_halt ( struct rndis_device *rndis ) { - int rc; - - /* Transmit halt message */ - if ( ( rc = rndis_tx_halt ( rndis ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Transmit OID message - * - * @v rndis RNDIS device - * @v oid Object ID - * @v data New OID value (or NULL to query current value) - * @v len Length of new OID value - * @ret rc Return status code - */ -static int rndis_tx_oid ( struct rndis_device *rndis, unsigned int oid, - const void *data, size_t len ) { - struct io_buffer *iobuf; - struct rndis_oid_message *msg; - unsigned int type; - int rc; - - /* Allocate I/O buffer */ - iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Construct message. We use the OID as the request ID. */ - msg = iob_put ( iobuf, sizeof ( *msg ) ); - memset ( msg, 0, sizeof ( *msg ) ); - msg->id = oid; /* Non-endian */ - msg->oid = cpu_to_le32 ( oid ); - msg->offset = cpu_to_le32 ( sizeof ( *msg ) ); - msg->len = cpu_to_le32 ( len ); - memcpy ( iob_put ( iobuf, len ), data, len ); - - /* Transmit message */ - type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG ); - if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 ) - goto err_tx; - - return 0; - - err_tx: - free_iob ( iobuf ); - err_alloc: - return rc; -} - -/** - * Receive query OID completion - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - */ -static void rndis_rx_query_oid ( struct rndis_device *rndis, - struct io_buffer *iobuf ) { - struct net_device *netdev = rndis->netdev; - struct rndis_query_completion *cmplt; - size_t len = iob_len ( iobuf ); - size_t info_offset; - size_t info_len; - unsigned int id; - void *info; - uint32_t *link_status; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *cmplt ) ) { - DBGC ( rndis, "RNDIS %s received underlength query " - "completion:\n", rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_len; - } - cmplt = iobuf->data; - - /* Extract request ID */ - id = cmplt->id; /* Non-endian */ - - /* Check status */ - if ( cmplt->status ) { - DBGC ( rndis, "RNDIS %s received query completion failure " - "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EIO; - goto err_status; - } - - /* Locate and sanity check information buffer */ - info_offset = le32_to_cpu ( cmplt->offset ); - info_len = le32_to_cpu ( cmplt->len ); - if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) { - DBGC ( rndis, "RNDIS %s query completion information exceeds " - "packet:\n", rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_info; - } - info = ( ( ( void * ) cmplt ) + info_offset ); - - /* Handle OID */ - switch ( id ) { - - case RNDIS_OID_802_3_PERMANENT_ADDRESS: - if ( info_len > sizeof ( netdev->hw_addr ) ) - info_len = sizeof ( netdev->hw_addr ); - memcpy ( netdev->hw_addr, info, info_len ); - break; - - case RNDIS_OID_802_3_CURRENT_ADDRESS: - if ( info_len > sizeof ( netdev->ll_addr ) ) - info_len = sizeof ( netdev->ll_addr ); - memcpy ( netdev->ll_addr, info, info_len ); - break; - - case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: - if ( info_len != sizeof ( *link_status ) ) { - DBGC ( rndis, "RNDIS %s invalid link status:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EPROTO; - goto err_link_status; - } - link_status = info; - if ( *link_status == 0 ) { - DBGC ( rndis, "RNDIS %s link is up\n", rndis->name ); - netdev_link_up ( netdev ); - } else { - DBGC ( rndis, "RNDIS %s link is down: %#08x\n", - rndis->name, le32_to_cpu ( *link_status ) ); - netdev_link_down ( netdev ); - } - break; - - default: - DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n", - rndis->name, id ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EPROTO; - goto err_id; - } - - /* Success */ - rc = 0; - - err_id: - err_link_status: - err_info: - err_status: - /* Record completion result if applicable */ - if ( id == rndis->wait_id ) { - rndis->wait_id = 0; - rndis->wait_rc = rc; - } - err_len: - /* Free I/O buffer */ - free_iob ( iobuf ); -} - -/** - * Receive set OID completion - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - */ -static void rndis_rx_set_oid ( struct rndis_device *rndis, - struct io_buffer *iobuf ) { - struct rndis_set_completion *cmplt; - size_t len = iob_len ( iobuf ); - unsigned int id; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *cmplt ) ) { - DBGC ( rndis, "RNDIS %s received underlength set completion:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_len; - } - cmplt = iobuf->data; - - /* Extract request ID */ - id = cmplt->id; /* Non-endian */ - - /* Check status */ - if ( cmplt->status ) { - DBGC ( rndis, "RNDIS %s received set completion failure " - "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EIO; - goto err_status; - } - - /* Success */ - rc = 0; - - err_status: - /* Record completion result if applicable */ - if ( id == rndis->wait_id ) { - rndis->wait_id = 0; - rndis->wait_rc = rc; - } - err_len: - /* Free I/O buffer */ - free_iob ( iobuf ); -} - -/** - * Query or set OID - * - * @v rndis RNDIS device - * @v oid Object ID - * @v data New OID value (or NULL to query current value) - * @v len Length of new OID value - * @ret rc Return status code - */ -static int rndis_oid ( struct rndis_device *rndis, unsigned int oid, - const void *data, size_t len ) { - int rc; - - /* Transmit query */ - if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 ) - return rc; - - /* Wait for response */ - if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Receive indicate status message - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - */ -static void rndis_rx_status ( struct rndis_device *rndis, - struct io_buffer *iobuf ) { - struct net_device *netdev = rndis->netdev; - struct rndis_indicate_status_message *msg; - size_t len = iob_len ( iobuf ); - unsigned int status; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *msg ) ) { - DBGC ( rndis, "RNDIS %s received underlength status message:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -EINVAL; - goto err_len; - } - msg = iobuf->data; - - /* Extract status */ - status = le32_to_cpu ( msg->status ); - - /* Handle status */ - switch ( msg->status ) { - - case RNDIS_STATUS_MEDIA_CONNECT: - DBGC ( rndis, "RNDIS %s link is up\n", rndis->name ); - netdev_link_up ( netdev ); - break; - - case RNDIS_STATUS_MEDIA_DISCONNECT: - DBGC ( rndis, "RNDIS %s link is down\n", rndis->name ); - netdev_link_down ( netdev ); - break; - - case RNDIS_STATUS_WTF_WORLD: - /* Ignore */ - break; - - default: - DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n", - rndis->name, status ); - DBGC_HDA ( rndis, 0, iobuf->data, len ); - rc = -ENOTSUP; - goto err_status; - } - - /* Free I/O buffer */ - free_iob ( iobuf ); - - return; - - err_status: - err_len: - /* Report error via network device statistics */ - netdev_rx_err ( netdev, iobuf, rc ); -} - -/** - * Receive RNDIS message - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - * @v type Message type - */ -static void rndis_rx_message ( struct rndis_device *rndis, - struct io_buffer *iobuf, unsigned int type ) { - struct net_device *netdev = rndis->netdev; - int rc; - - /* Handle packet */ - switch ( type ) { - - case RNDIS_PACKET_MSG: - rndis_rx_data ( rndis, iob_disown ( iobuf ) ); - break; - - case RNDIS_INITIALISE_CMPLT: - rndis_rx_initialise ( rndis, iob_disown ( iobuf ) ); - break; - - case RNDIS_QUERY_CMPLT: - rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) ); - break; - - case RNDIS_SET_CMPLT: - rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) ); - break; - - case RNDIS_INDICATE_STATUS_MSG: - rndis_rx_status ( rndis, iob_disown ( iobuf ) ); - break; - - default: - DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n", - rndis->name, type ); - DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) ); - rc = -EPROTO; - goto err_type; - } - - return; - - err_type: - /* Report error via network device statistics */ - netdev_rx_err ( netdev, iobuf, rc ); -} - -/** - * Receive packet from underlying transport layer - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - */ -void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) { - struct net_device *netdev = rndis->netdev; - struct rndis_header *header; - unsigned int type; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *header ) ) { - DBGC ( rndis, "RNDIS %s received underlength packet:\n", - rndis->name ); - DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) ); - rc = -EINVAL; - goto drop; - } - header = iobuf->data; - - /* Parse and strip header */ - type = le32_to_cpu ( header->type ); - iob_pull ( iobuf, sizeof ( *header ) ); - - /* Handle message */ - rndis_rx_message ( rndis, iob_disown ( iobuf ), type ); - - return; - - drop: - /* Record error */ - netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); -} - -/** - * Discard packet from underlying transport layer - * - * @v rndis RNDIS device - * @v iobuf I/O buffer - * @v rc Packet status code - */ -void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf, - int rc ) { - struct net_device *netdev = rndis->netdev; - - /* Record error */ - netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); -} - -/** - * Set receive filter - * - * @v rndis RNDIS device - * @v filter Receive filter - * @ret rc Return status code - */ -static int rndis_filter ( struct rndis_device *rndis, unsigned int filter ) { - uint32_t value = cpu_to_le32 ( filter ); - int rc; - - /* Set receive filter */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, - &value, sizeof ( value ) ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not set receive filter to %#08x: " - "%s\n", rndis->name, filter, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Open network device - * - * @v netdev Network device - * @ret rc Return status code - */ -static int rndis_open ( struct net_device *netdev ) { - struct rndis_device *rndis = netdev->priv; - int rc; - - /* Open RNDIS device */ - if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not open: %s\n", - rndis->name, strerror ( rc ) ); - goto err_open; - } - - /* Initialise RNDIS */ - if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) - goto err_initialise; - - /* Set receive filter */ - if ( ( rc = rndis_filter ( rndis, ( RNDIS_FILTER_UNICAST | - RNDIS_FILTER_MULTICAST | - RNDIS_FILTER_ALL_MULTICAST | - RNDIS_FILTER_BROADCAST | - RNDIS_FILTER_PROMISCUOUS ) ) ) != 0) - goto err_set_filter; - - /* Update link status */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, - NULL, 0 ) ) != 0 ) - goto err_query_link; - - return 0; - - err_query_link: - err_set_filter: - rndis_halt ( rndis ); - err_initialise: - rndis->op->close ( rndis ); - err_open: - return rc; -} - -/** - * Close network device - * - * @v netdev Network device - */ -static void rndis_close ( struct net_device *netdev ) { - struct rndis_device *rndis = netdev->priv; - - /* Clear receive filter */ - rndis_filter ( rndis, 0 ); - - /* Halt RNDIS device */ - rndis_halt ( rndis ); - - /* Close RNDIS device */ - rndis->op->close ( rndis ); -} - -/** - * Transmit packet - * - * @v netdev Network device - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int rndis_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ) { - struct rndis_device *rndis = netdev->priv; - - /* Transmit data packet */ - return rndis_tx_data ( rndis, iobuf ); -} - -/** - * Poll for completed and received packets - * - * @v netdev Network device - */ -static void rndis_poll ( struct net_device *netdev ) { - struct rndis_device *rndis = netdev->priv; - - /* Poll RNDIS device */ - rndis->op->poll ( rndis ); -} - -/** Network device operations */ -static struct net_device_operations rndis_operations = { - .open = rndis_open, - .close = rndis_close, - .transmit = rndis_transmit, - .poll = rndis_poll, -}; - -/** - * Allocate RNDIS device - * - * @v priv_len Length of private data - * @ret rndis RNDIS device, or NULL on allocation failure - */ -struct rndis_device * alloc_rndis ( size_t priv_len ) { - struct net_device *netdev; - struct rndis_device *rndis; - - /* Allocate and initialise structure */ - netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len ); - if ( ! netdev ) - return NULL; - netdev_init ( netdev, &rndis_operations ); - rndis = netdev->priv; - rndis->netdev = netdev; - rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) ); - - return rndis; -} - -/** - * Register RNDIS device - * - * @v rndis RNDIS device - * @ret rc Return status code - * - * Note that this routine will open and use the RNDIS device in order - * to query the MAC address. The device must be immediately ready for - * use prior to registration. - */ -int register_rndis ( struct rndis_device *rndis ) { - struct net_device *netdev = rndis->netdev; - int rc; - - /* Assign device name (for debugging) */ - rndis->name = netdev->dev->name; - - /* Register network device */ - if ( ( rc = register_netdev ( netdev ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not register: %s\n", - rndis->name, strerror ( rc ) ); - goto err_register; - } - - /* Open RNDIS device to read MAC addresses */ - if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not open: %s\n", - rndis->name, strerror ( rc ) ); - goto err_open; - } - - /* Initialise RNDIS */ - if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) - goto err_initialise; - - /* Query permanent MAC address */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS, - NULL, 0 ) ) != 0 ) - goto err_query_permanent; - - /* Query current MAC address */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS, - NULL, 0 ) ) != 0 ) - goto err_query_current; - - /* Get link status */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, - NULL, 0 ) ) != 0 ) - goto err_query_link; - - /* Halt RNDIS device */ - rndis_halt ( rndis ); - - /* Close RNDIS device */ - rndis->op->close ( rndis ); - - return 0; - - err_query_link: - err_query_current: - err_query_permanent: - rndis_halt ( rndis ); - err_initialise: - rndis->op->close ( rndis ); - err_open: - unregister_netdev ( netdev ); - err_register: - return rc; -} - -/** - * Unregister RNDIS device - * - * @v rndis RNDIS device - */ -void unregister_rndis ( struct rndis_device *rndis ) { - struct net_device *netdev = rndis->netdev; - - /* Unregister network device */ - unregister_netdev ( netdev ); -} - -/** - * Free RNDIS device - * - * @v rndis RNDIS device - */ -void free_rndis ( struct rndis_device *rndis ) { - struct net_device *netdev = rndis->netdev; - - /* Free network device */ - netdev_nullify ( netdev ); - netdev_put ( netdev ); -} diff --git a/qemu/roms/ipxe/src/net/socket.c b/qemu/roms/ipxe/src/net/socket.c deleted file mode 100644 index 2009ab237..000000000 --- a/qemu/roms/ipxe/src/net/socket.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <errno.h> -#include <ipxe/socket.h> - -/** @file - * - * Sockets - * - */ - -/** - * Transcribe socket address - * - * @v sa Socket address - * @ret string Socket address string - */ -const char * sock_ntoa ( struct sockaddr *sa ) { - struct sockaddr_converter *converter; - - for_each_table_entry ( converter, SOCKADDR_CONVERTERS ) { - if ( converter->family == sa->sa_family ) - return converter->ntoa ( sa ); - } - return NULL; -} - -/** - * Parse socket address - * - * @v string Socket address string - * @v sa Socket address to fill in - * @ret rc Return status code - */ -int sock_aton ( const char *string, struct sockaddr *sa ) { - struct sockaddr_converter *converter; - - for_each_table_entry ( converter, SOCKADDR_CONVERTERS ) { - if ( converter->aton ( string, sa ) == 0 ) { - sa->sa_family = converter->family; - return 0; - } - } - return -EINVAL; -} diff --git a/qemu/roms/ipxe/src/net/stp.c b/qemu/roms/ipxe/src/net/stp.c deleted file mode 100644 index d4e65a1a2..000000000 --- a/qemu/roms/ipxe/src/net/stp.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <errno.h> -#include <byteswap.h> -#include <ipxe/netdevice.h> -#include <ipxe/ethernet.h> -#include <ipxe/iobuf.h> -#include <ipxe/timer.h> -#include <ipxe/stp.h> - -/** @file - * - * Spanning Tree Protocol (STP) - * - */ - -/* Disambiguate the various error causes */ -#define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL ) -#define EINFO_ENOTSUP_PROTOCOL \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ - "Non-STP packet received" ) -#define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION ) -#define EINFO_ENOTSUP_VERSION \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ - "Legacy STP packet received" ) -#define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE ) -#define EINFO_ENOTSUP_TYPE \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ - "Non-RSTP packet received" ) - -/** - * Process incoming STP packets - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_dest __unused, - const void *ll_source __unused, - unsigned int flags __unused ) { - struct stp_bpdu *stp; - unsigned int hello; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *stp ) ) { - DBGC ( netdev, "STP %s received underlength packet (%zd " - "bytes):\n", netdev->name, iob_len ( iobuf ) ); - DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - stp = iobuf->data; - - /* Ignore non-RSTP packets */ - if ( stp->protocol != htons ( STP_PROTOCOL ) ) { - DBGC ( netdev, "STP %s ignoring non-STP packet (protocol " - "%#04x)\n", netdev->name, ntohs ( stp->protocol ) ); - rc = -ENOTSUP_PROTOCOL; - goto done; - } - if ( stp->version < STP_VERSION_RSTP ) { - DBGC ( netdev, "STP %s received legacy STP packet (version " - "%#02x)\n", netdev->name, stp->version ); - rc = -ENOTSUP_VERSION; - goto done; - } - if ( stp->type != STP_TYPE_RSTP ) { - DBGC ( netdev, "STP %s received non-RSTP packet (type %#02x)\n", - netdev->name, stp->type ); - rc = -ENOTSUP_TYPE; - goto done; - } - - /* Dump information */ - DBGC2 ( netdev, "STP %s %s port %#04x flags %#02x hello %d delay %d\n", - netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ), - stp->flags, ntohs ( stp->hello ), ntohs ( stp->delay ) ); - - /* Check if port is forwarding */ - if ( ! ( stp->flags & STP_FL_FORWARDING ) ) { - /* Port is not forwarding: block link for two hello times */ - DBGC ( netdev, "STP %s %s port %#04x flags %#02x is not " - "forwarding\n", - netdev->name, eth_ntoa ( stp->sender.mac ), - ntohs ( stp->port ), stp->flags ); - hello = ( ( ntohs ( stp->hello ) * TICKS_PER_SEC ) / 256 ); - netdev_link_block ( netdev, ( hello * 2 ) ); - rc = -ENETUNREACH; - goto done; - } - - /* Success */ - if ( netdev_link_blocked ( netdev ) ) { - DBGC ( netdev, "STP %s %s port %#04x flags %#02x is " - "forwarding\n", - netdev->name, eth_ntoa ( stp->sender.mac ), - ntohs ( stp->port ), stp->flags ); - } - netdev_link_unblock ( netdev ); - rc = 0; - - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Transcribe STP address - * - * @v net_addr STP address - * @ret string "<STP>" - * - * This operation is meaningless for the STP protocol. - */ -static const char * stp_ntoa ( const void *net_addr __unused ) { - return "<STP>"; -} - -/** STP network protocol */ -struct net_protocol stp_protocol __net_protocol = { - .name = "STP", - .net_proto = htons ( ETH_P_STP ), - .rx = stp_rx, - .ntoa = stp_ntoa, -}; diff --git a/qemu/roms/ipxe/src/net/tcp.c b/qemu/roms/ipxe/src/net/tcp.c deleted file mode 100644 index c69c83b85..000000000 --- a/qemu/roms/ipxe/src/net/tcp.c +++ /dev/null @@ -1,1687 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/timer.h> -#include <ipxe/iobuf.h> -#include <ipxe/malloc.h> -#include <ipxe/init.h> -#include <ipxe/retry.h> -#include <ipxe/refcnt.h> -#include <ipxe/pending.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/netdevice.h> -#include <ipxe/profile.h> -#include <ipxe/process.h> -#include <ipxe/tcpip.h> -#include <ipxe/tcp.h> - -/** @file - * - * TCP protocol - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** A TCP connection */ -struct tcp_connection { - /** Reference counter */ - struct refcnt refcnt; - /** List of TCP connections */ - struct list_head list; - - /** Flags */ - unsigned int flags; - - /** Data transfer interface */ - struct interface xfer; - - /** Remote socket address */ - struct sockaddr_tcpip peer; - /** Local port */ - unsigned int local_port; - /** Maximum segment size */ - size_t mss; - - /** Current TCP state */ - unsigned int tcp_state; - /** Previous TCP state - * - * Maintained only for debug messages - */ - unsigned int prev_tcp_state; - /** Current sequence number - * - * Equivalent to SND.UNA in RFC 793 terminology. - */ - uint32_t snd_seq; - /** Unacknowledged sequence count - * - * Equivalent to (SND.NXT-SND.UNA) in RFC 793 terminology. - */ - uint32_t snd_sent; - /** Send window - * - * Equivalent to SND.WND in RFC 793 terminology - */ - uint32_t snd_win; - /** Current acknowledgement number - * - * Equivalent to RCV.NXT in RFC 793 terminology. - */ - uint32_t rcv_ack; - /** Receive window - * - * Equivalent to RCV.WND in RFC 793 terminology. - */ - uint32_t rcv_win; - /** Received timestamp value - * - * Updated when a packet is received; copied to ts_recent when - * the window is advanced. - */ - uint32_t ts_val; - /** Most recent received timestamp that advanced the window - * - * Equivalent to TS.Recent in RFC 1323 terminology. - */ - uint32_t ts_recent; - /** Send window scale - * - * Equivalent to Snd.Wind.Scale in RFC 1323 terminology - */ - uint8_t snd_win_scale; - /** Receive window scale - * - * Equivalent to Rcv.Wind.Scale in RFC 1323 terminology - */ - uint8_t rcv_win_scale; - - /** Selective acknowledgement list (in host-endian order) */ - struct tcp_sack_block sack[TCP_SACK_MAX]; - - /** Transmit queue */ - struct list_head tx_queue; - /** Receive queue */ - struct list_head rx_queue; - /** Transmission process */ - struct process process; - /** Retransmission timer */ - struct retry_timer timer; - /** Shutdown (TIME_WAIT) timer */ - struct retry_timer wait; - - /** Pending operations for SYN and FIN */ - struct pending_operation pending_flags; - /** Pending operations for transmit queue */ - struct pending_operation pending_data; -}; - -/** TCP flags */ -enum tcp_flags { - /** TCP data transfer interface has been closed */ - TCP_XFER_CLOSED = 0x0001, - /** TCP timestamps are enabled */ - TCP_TS_ENABLED = 0x0002, - /** TCP acknowledgement is pending */ - TCP_ACK_PENDING = 0x0004, - /** TCP selective acknowledgement is enabled */ - TCP_SACK_ENABLED = 0x0008, -}; - -/** TCP internal header - * - * This is the header that replaces the TCP header for packets - * enqueued on the receive queue. - */ -struct tcp_rx_queued_header { - /** SEQ value, in host-endian order - * - * This represents the SEQ value at the time the packet is - * enqueued, and so excludes the SYN, if present. - */ - uint32_t seq; - /** Next SEQ value, in host-endian order */ - uint32_t nxt; - /** Flags - * - * Only FIN is valid within this flags byte; all other flags - * have already been processed by the time the packet is - * enqueued. - */ - uint8_t flags; - /** Reserved */ - uint8_t reserved[3]; -}; - -/** - * List of registered TCP connections - */ -static LIST_HEAD ( tcp_conns ); - -/** Transmit profiler */ -static struct profiler tcp_tx_profiler __profiler = { .name = "tcp.tx" }; - -/** Receive profiler */ -static struct profiler tcp_rx_profiler __profiler = { .name = "tcp.rx" }; - -/** Data transfer profiler */ -static struct profiler tcp_xfer_profiler __profiler = { .name = "tcp.xfer" }; - -/* Forward declarations */ -static struct process_descriptor tcp_process_desc; -static struct interface_descriptor tcp_xfer_desc; -static void tcp_expired ( struct retry_timer *timer, int over ); -static void tcp_wait_expired ( struct retry_timer *timer, int over ); -static struct tcp_connection * tcp_demux ( unsigned int local_port ); -static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, - uint32_t win ); - -/** - * Name TCP state - * - * @v state TCP state - * @ret name Name of TCP state - */ -static inline __attribute__ (( always_inline )) const char * -tcp_state ( int state ) { - switch ( state ) { - case TCP_CLOSED: return "CLOSED"; - case TCP_LISTEN: return "LISTEN"; - case TCP_SYN_SENT: return "SYN_SENT"; - case TCP_SYN_RCVD: return "SYN_RCVD"; - case TCP_ESTABLISHED: return "ESTABLISHED"; - case TCP_FIN_WAIT_1: return "FIN_WAIT_1"; - case TCP_FIN_WAIT_2: return "FIN_WAIT_2"; - case TCP_CLOSING_OR_LAST_ACK: return "CLOSING/LAST_ACK"; - case TCP_TIME_WAIT: return "TIME_WAIT"; - case TCP_CLOSE_WAIT: return "CLOSE_WAIT"; - default: return "INVALID"; - } -} - -/** - * Dump TCP state transition - * - * @v tcp TCP connection - */ -static inline __attribute__ (( always_inline )) void -tcp_dump_state ( struct tcp_connection *tcp ) { - - if ( tcp->tcp_state != tcp->prev_tcp_state ) { - DBGC ( tcp, "TCP %p transitioned from %s to %s\n", tcp, - tcp_state ( tcp->prev_tcp_state ), - tcp_state ( tcp->tcp_state ) ); - } - tcp->prev_tcp_state = tcp->tcp_state; -} - -/** - * Dump TCP flags - * - * @v flags TCP flags - */ -static inline __attribute__ (( always_inline )) void -tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) { - if ( flags & TCP_RST ) - DBGC2 ( tcp, " RST" ); - if ( flags & TCP_SYN ) - DBGC2 ( tcp, " SYN" ); - if ( flags & TCP_PSH ) - DBGC2 ( tcp, " PSH" ); - if ( flags & TCP_FIN ) - DBGC2 ( tcp, " FIN" ); - if ( flags & TCP_ACK ) - DBGC2 ( tcp, " ACK" ); -} - -/*************************************************************************** - * - * Open and close - * - *************************************************************************** - */ - -/** - * Check if local TCP port is available - * - * @v port Local port number - * @ret port Local port number, or negative error - */ -static int tcp_port_available ( int port ) { - - return ( tcp_demux ( port ) ? -EADDRINUSE : port ); -} - -/** - * Open a TCP connection - * - * @v xfer Data transfer interface - * @v peer Peer socket address - * @v local Local socket address, or NULL - * @ret rc Return status code - */ -static int tcp_open ( struct interface *xfer, struct sockaddr *peer, - struct sockaddr *local ) { - struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; - struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; - struct tcp_connection *tcp; - size_t mtu; - int port; - int rc; - - /* Allocate and initialise structure */ - tcp = zalloc ( sizeof ( *tcp ) ); - if ( ! tcp ) - return -ENOMEM; - DBGC ( tcp, "TCP %p allocated\n", tcp ); - ref_init ( &tcp->refcnt, NULL ); - intf_init ( &tcp->xfer, &tcp_xfer_desc, &tcp->refcnt ); - process_init_stopped ( &tcp->process, &tcp_process_desc, &tcp->refcnt ); - timer_init ( &tcp->timer, tcp_expired, &tcp->refcnt ); - timer_init ( &tcp->wait, tcp_wait_expired, &tcp->refcnt ); - tcp->prev_tcp_state = TCP_CLOSED; - tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); - tcp_dump_state ( tcp ); - tcp->snd_seq = random(); - INIT_LIST_HEAD ( &tcp->tx_queue ); - INIT_LIST_HEAD ( &tcp->rx_queue ); - memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); - - /* Calculate MSS */ - mtu = tcpip_mtu ( &tcp->peer ); - if ( ! mtu ) { - DBGC ( tcp, "TCP %p has no route to %s\n", - tcp, sock_ntoa ( peer ) ); - rc = -ENETUNREACH; - goto err; - } - tcp->mss = ( mtu - sizeof ( struct tcp_header ) ); - - /* Bind to local port */ - port = tcpip_bind ( st_local, tcp_port_available ); - if ( port < 0 ) { - rc = port; - DBGC ( tcp, "TCP %p could not bind: %s\n", - tcp, strerror ( rc ) ); - goto err; - } - tcp->local_port = port; - DBGC ( tcp, "TCP %p bound to port %d\n", tcp, tcp->local_port ); - - /* Start timer to initiate SYN */ - start_timer_nodelay ( &tcp->timer ); - - /* Add a pending operation for the SYN */ - pending_get ( &tcp->pending_flags ); - - /* Attach parent interface, transfer reference to connection - * list and return - */ - intf_plug_plug ( &tcp->xfer, xfer ); - list_add ( &tcp->list, &tcp_conns ); - return 0; - - err: - ref_put ( &tcp->refcnt ); - return rc; -} - -/** - * Close TCP connection - * - * @v tcp TCP connection - * @v rc Reason for close - * - * Closes the data transfer interface. If the TCP state machine is in - * a suitable state, the connection will be deleted. - */ -static void tcp_close ( struct tcp_connection *tcp, int rc ) { - struct io_buffer *iobuf; - struct io_buffer *tmp; - - /* Close data transfer interface */ - intf_shutdown ( &tcp->xfer, rc ); - tcp->flags |= TCP_XFER_CLOSED; - - /* If we are in CLOSED, or have otherwise not yet received a - * SYN (i.e. we are in LISTEN or SYN_SENT), just delete the - * connection. - */ - if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) { - - /* Transition to CLOSED for the sake of debugging messages */ - tcp->tcp_state = TCP_CLOSED; - tcp_dump_state ( tcp ); - - /* Free any unprocessed I/O buffers */ - list_for_each_entry_safe ( iobuf, tmp, &tcp->rx_queue, list ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - } - - /* Free any unsent I/O buffers */ - list_for_each_entry_safe ( iobuf, tmp, &tcp->tx_queue, list ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - pending_put ( &tcp->pending_data ); - } - assert ( ! is_pending ( &tcp->pending_data ) ); - - /* Remove pending operations for SYN and FIN, if applicable */ - pending_put ( &tcp->pending_flags ); - pending_put ( &tcp->pending_flags ); - - /* Remove from list and drop reference */ - process_del ( &tcp->process ); - stop_timer ( &tcp->timer ); - stop_timer ( &tcp->wait ); - list_del ( &tcp->list ); - ref_put ( &tcp->refcnt ); - DBGC ( tcp, "TCP %p connection deleted\n", tcp ); - return; - } - - /* If we have not had our SYN acknowledged (i.e. we are in - * SYN_RCVD), pretend that it has been acknowledged so that we - * can send a FIN without breaking things. - */ - if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) - tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 ); - - /* If we have no data remaining to send, start sending FIN */ - if ( list_empty ( &tcp->tx_queue ) && - ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) { - - tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); - tcp_dump_state ( tcp ); - process_add ( &tcp->process ); - - /* Add a pending operation for the FIN */ - pending_get ( &tcp->pending_flags ); - } -} - -/*************************************************************************** - * - * Transmit data path - * - *************************************************************************** - */ - -/** - * Calculate transmission window - * - * @v tcp TCP connection - * @ret len Maximum length that can be sent in a single packet - */ -static size_t tcp_xmit_win ( struct tcp_connection *tcp ) { - size_t len; - - /* Not ready if we're not in a suitable connection state */ - if ( ! TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) - return 0; - - /* Length is the minimum of the receiver's window and the path MTU */ - len = tcp->snd_win; - if ( len > TCP_PATH_MTU ) - len = TCP_PATH_MTU; - - return len; -} - -/** - * Check data-transfer flow control window - * - * @v tcp TCP connection - * @ret len Length of window - */ -static size_t tcp_xfer_window ( struct tcp_connection *tcp ) { - - /* Not ready if data queue is non-empty. This imposes a limit - * of only one unACKed packet in the TX queue at any time; we - * do this to conserve memory usage. - */ - if ( ! list_empty ( &tcp->tx_queue ) ) - return 0; - - /* Return TCP window length */ - return tcp_xmit_win ( tcp ); -} - -/** - * Find selective acknowledgement block - * - * @v tcp TCP connection - * @v seq SEQ value in SACK block (in host-endian order) - * @v sack SACK block to fill in (in host-endian order) - * @ret len Length of SACK block - */ -static uint32_t tcp_sack_block ( struct tcp_connection *tcp, uint32_t seq, - struct tcp_sack_block *sack ) { - struct io_buffer *iobuf; - struct tcp_rx_queued_header *tcpqhdr; - uint32_t left = tcp->rcv_ack; - uint32_t right = left; - - /* Find highest block which does not start after SEQ */ - list_for_each_entry ( iobuf, &tcp->rx_queue, list ) { - tcpqhdr = iobuf->data; - if ( tcp_cmp ( tcpqhdr->seq, right ) > 0 ) { - if ( tcp_cmp ( tcpqhdr->seq, seq ) > 0 ) - break; - left = tcpqhdr->seq; - } - if ( tcp_cmp ( tcpqhdr->nxt, right ) > 0 ) - right = tcpqhdr->nxt; - } - - /* Fail if this block does not contain SEQ */ - if ( tcp_cmp ( right, seq ) < 0 ) - return 0; - - /* Populate SACK block */ - sack->left = left; - sack->right = right; - return ( right - left ); -} - -/** - * Update TCP selective acknowledgement list - * - * @v tcp TCP connection - * @v seq SEQ value in first SACK block (in host-endian order) - * @ret count Number of SACK blocks - */ -static unsigned int tcp_sack ( struct tcp_connection *tcp, uint32_t seq ) { - struct tcp_sack_block sack[TCP_SACK_MAX]; - unsigned int old = 0; - unsigned int new = 0; - unsigned int i; - uint32_t len; - - /* Populate first new SACK block */ - len = tcp_sack_block ( tcp, seq, &sack[0] ); - if ( len ) - new++; - - /* Populate remaining new SACK blocks based on old SACK blocks */ - for ( old = 0 ; old < TCP_SACK_MAX ; old++ ) { - - /* Stop if we run out of space in the new list */ - if ( new == TCP_SACK_MAX ) - break; - - /* Skip empty old SACK blocks */ - if ( tcp->sack[old].left == tcp->sack[old].right ) - continue; - - /* Populate new SACK block */ - len = tcp_sack_block ( tcp, tcp->sack[old].left, &sack[new] ); - if ( len == 0 ) - continue; - - /* Eliminate duplicates */ - for ( i = 0 ; i < new ; i++ ) { - if ( sack[i].left == sack[new].left ) { - new--; - break; - } - } - new++; - } - - /* Update SACK list */ - memset ( tcp->sack, 0, sizeof ( tcp->sack ) ); - memcpy ( tcp->sack, sack, ( new * sizeof ( tcp->sack[0] ) ) ); - return new; -} - -/** - * Process TCP transmit queue - * - * @v tcp TCP connection - * @v max_len Maximum length to process - * @v dest I/O buffer to fill with data, or NULL - * @v remove Remove data from queue - * @ret len Length of data processed - * - * This processes at most @c max_len bytes from the TCP connection's - * transmit queue. Data will be copied into the @c dest I/O buffer - * (if provided) and, if @c remove is true, removed from the transmit - * queue. - */ -static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len, - struct io_buffer *dest, int remove ) { - struct io_buffer *iobuf; - struct io_buffer *tmp; - size_t frag_len; - size_t len = 0; - - list_for_each_entry_safe ( iobuf, tmp, &tcp->tx_queue, list ) { - frag_len = iob_len ( iobuf ); - if ( frag_len > max_len ) - frag_len = max_len; - if ( dest ) { - memcpy ( iob_put ( dest, frag_len ), iobuf->data, - frag_len ); - } - if ( remove ) { - iob_pull ( iobuf, frag_len ); - if ( ! iob_len ( iobuf ) ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - pending_put ( &tcp->pending_data ); - } - } - len += frag_len; - max_len -= frag_len; - } - return len; -} - -/** - * Transmit any outstanding data (with selective acknowledgement) - * - * @v tcp TCP connection - * @v sack_seq SEQ for first selective acknowledgement (if any) - * - * Transmits any outstanding data on the connection. - * - * Note that even if an error is returned, the retransmission timer - * will have been started if necessary, and so the stack will - * eventually attempt to retransmit the failed packet. - */ -static void tcp_xmit_sack ( struct tcp_connection *tcp, uint32_t sack_seq ) { - struct io_buffer *iobuf; - struct tcp_header *tcphdr; - struct tcp_mss_option *mssopt; - struct tcp_window_scale_padded_option *wsopt; - struct tcp_timestamp_padded_option *tsopt; - struct tcp_sack_permitted_padded_option *spopt; - struct tcp_sack_padded_option *sackopt; - struct tcp_sack_block *sack; - void *payload; - unsigned int flags; - unsigned int sack_count; - unsigned int i; - size_t len = 0; - size_t sack_len; - uint32_t seq_len; - uint32_t max_rcv_win; - uint32_t max_representable_win; - int rc; - - /* Start profiling */ - profile_start ( &tcp_tx_profiler ); - - /* If retransmission timer is already running, do nothing */ - if ( timer_running ( &tcp->timer ) ) - return; - - /* Calculate both the actual (payload) and sequence space - * lengths that we wish to transmit. - */ - if ( TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) { - len = tcp_process_tx_queue ( tcp, tcp_xmit_win ( tcp ), - NULL, 0 ); - } - seq_len = len; - flags = TCP_FLAGS_SENDING ( tcp->tcp_state ); - if ( flags & ( TCP_SYN | TCP_FIN ) ) { - /* SYN or FIN consume one byte, and we can never send both */ - assert ( ! ( ( flags & TCP_SYN ) && ( flags & TCP_FIN ) ) ); - seq_len++; - } - tcp->snd_sent = seq_len; - - /* If we have nothing to transmit, stop now */ - if ( ( seq_len == 0 ) && ! ( tcp->flags & TCP_ACK_PENDING ) ) - return; - - /* If we are transmitting anything that requires - * acknowledgement (i.e. consumes sequence space), start the - * retransmission timer. Do this before attempting to - * allocate the I/O buffer, in case allocation itself fails. - */ - if ( seq_len ) - start_timer ( &tcp->timer ); - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( len + TCP_MAX_HEADER_LEN ); - if ( ! iobuf ) { - DBGC ( tcp, "TCP %p could not allocate iobuf for %08x..%08x " - "%08x\n", tcp, tcp->snd_seq, ( tcp->snd_seq + seq_len ), - tcp->rcv_ack ); - return; - } - iob_reserve ( iobuf, TCP_MAX_HEADER_LEN ); - - /* Fill data payload from transmit queue */ - tcp_process_tx_queue ( tcp, len, iobuf, 0 ); - - /* Expand receive window if possible */ - max_rcv_win = xfer_window ( &tcp->xfer ); - if ( max_rcv_win > TCP_MAX_WINDOW_SIZE ) - max_rcv_win = TCP_MAX_WINDOW_SIZE; - max_representable_win = ( 0xffff << tcp->rcv_win_scale ); - if ( max_rcv_win > max_representable_win ) - max_rcv_win = max_representable_win; - max_rcv_win &= ~0x03; /* Keep everything dword-aligned */ - if ( tcp->rcv_win < max_rcv_win ) - tcp->rcv_win = max_rcv_win; - - /* Fill up the TCP header */ - payload = iobuf->data; - if ( flags & TCP_SYN ) { - mssopt = iob_push ( iobuf, sizeof ( *mssopt ) ); - mssopt->kind = TCP_OPTION_MSS; - mssopt->length = sizeof ( *mssopt ); - mssopt->mss = htons ( tcp->mss ); - wsopt = iob_push ( iobuf, sizeof ( *wsopt ) ); - wsopt->nop = TCP_OPTION_NOP; - wsopt->wsopt.kind = TCP_OPTION_WS; - wsopt->wsopt.length = sizeof ( wsopt->wsopt ); - wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE; - spopt = iob_push ( iobuf, sizeof ( *spopt ) ); - memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt ) ); - spopt->spopt.kind = TCP_OPTION_SACK_PERMITTED; - spopt->spopt.length = sizeof ( spopt->spopt ); - } - if ( ( flags & TCP_SYN ) || ( tcp->flags & TCP_TS_ENABLED ) ) { - tsopt = iob_push ( iobuf, sizeof ( *tsopt ) ); - memset ( tsopt->nop, TCP_OPTION_NOP, sizeof ( tsopt->nop ) ); - tsopt->tsopt.kind = TCP_OPTION_TS; - tsopt->tsopt.length = sizeof ( tsopt->tsopt ); - tsopt->tsopt.tsval = htonl ( currticks() ); - tsopt->tsopt.tsecr = htonl ( tcp->ts_recent ); - } - if ( ( tcp->flags & TCP_SACK_ENABLED ) && - ( ! list_empty ( &tcp->rx_queue ) ) && - ( ( sack_count = tcp_sack ( tcp, sack_seq ) ) != 0 ) ) { - sack_len = ( sack_count * sizeof ( *sack ) ); - sackopt = iob_push ( iobuf, ( sizeof ( *sackopt ) + sack_len )); - memset ( sackopt->nop, TCP_OPTION_NOP, sizeof ( sackopt->nop )); - sackopt->sackopt.kind = TCP_OPTION_SACK; - sackopt->sackopt.length = - ( sizeof ( sackopt->sackopt ) + sack_len ); - sack = ( ( ( void * ) sackopt ) + sizeof ( *sackopt ) ); - for ( i = 0 ; i < sack_count ; i++, sack++ ) { - sack->left = htonl ( tcp->sack[i].left ); - sack->right = htonl ( tcp->sack[i].right ); - } - } - if ( len != 0 ) - flags |= TCP_PSH; - tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); - memset ( tcphdr, 0, sizeof ( *tcphdr ) ); - tcphdr->src = htons ( tcp->local_port ); - tcphdr->dest = tcp->peer.st_port; - tcphdr->seq = htonl ( tcp->snd_seq ); - tcphdr->ack = htonl ( tcp->rcv_ack ); - tcphdr->hlen = ( ( payload - iobuf->data ) << 2 ); - tcphdr->flags = flags; - tcphdr->win = htons ( tcp->rcv_win >> tcp->rcv_win_scale ); - tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) ); - - /* Dump header */ - DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x %08x %4zd", - tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ), - ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ), - ntohl ( tcphdr->ack ), len ); - tcp_dump_flags ( tcp, tcphdr->flags ); - DBGC2 ( tcp, "\n" ); - - /* Transmit packet */ - if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL, - &tcphdr->csum ) ) != 0 ) { - DBGC ( tcp, "TCP %p could not transmit %08x..%08x %08x: %s\n", - tcp, tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), - tcp->rcv_ack, strerror ( rc ) ); - return; - } - - /* Clear ACK-pending flag */ - tcp->flags &= ~TCP_ACK_PENDING; - - profile_stop ( &tcp_tx_profiler ); -} - -/** - * Transmit any outstanding data - * - * @v tcp TCP connection - */ -static void tcp_xmit ( struct tcp_connection *tcp ) { - - /* Transmit without an explicit first SACK */ - tcp_xmit_sack ( tcp, tcp->rcv_ack ); -} - -/** TCP process descriptor */ -static struct process_descriptor tcp_process_desc = - PROC_DESC_ONCE ( struct tcp_connection, process, tcp_xmit ); - -/** - * Retransmission timer expired - * - * @v timer Retransmission timer - * @v over Failure indicator - */ -static void tcp_expired ( struct retry_timer *timer, int over ) { - struct tcp_connection *tcp = - container_of ( timer, struct tcp_connection, timer ); - - DBGC ( tcp, "TCP %p timer %s in %s for %08x..%08x %08x\n", tcp, - ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ), - tcp->snd_seq, ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack ); - - assert ( ( tcp->tcp_state == TCP_SYN_SENT ) || - ( tcp->tcp_state == TCP_SYN_RCVD ) || - ( tcp->tcp_state == TCP_ESTABLISHED ) || - ( tcp->tcp_state == TCP_FIN_WAIT_1 ) || - ( tcp->tcp_state == TCP_CLOSE_WAIT ) || - ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) ); - - if ( over ) { - /* If we have finally timed out and given up, - * terminate the connection - */ - tcp->tcp_state = TCP_CLOSED; - tcp_dump_state ( tcp ); - tcp_close ( tcp, -ETIMEDOUT ); - } else { - /* Otherwise, retransmit the packet */ - tcp_xmit ( tcp ); - } -} - -/** - * Shutdown timer expired - * - * @v timer Shutdown timer - * @v over Failure indicator - */ -static void tcp_wait_expired ( struct retry_timer *timer, int over __unused ) { - struct tcp_connection *tcp = - container_of ( timer, struct tcp_connection, wait ); - - assert ( tcp->tcp_state == TCP_TIME_WAIT ); - - DBGC ( tcp, "TCP %p wait complete in %s for %08x..%08x %08x\n", tcp, - tcp_state ( tcp->tcp_state ), tcp->snd_seq, - ( tcp->snd_seq + tcp->snd_sent ), tcp->rcv_ack ); - - tcp->tcp_state = TCP_CLOSED; - tcp_dump_state ( tcp ); - tcp_close ( tcp, 0 ); -} - -/** - * Send RST response to incoming packet - * - * @v in_tcphdr TCP header of incoming packet - * @ret rc Return status code - */ -static int tcp_xmit_reset ( struct tcp_connection *tcp, - struct sockaddr_tcpip *st_dest, - struct tcp_header *in_tcphdr ) { - struct io_buffer *iobuf; - struct tcp_header *tcphdr; - int rc; - - /* Allocate space for dataless TX buffer */ - iobuf = alloc_iob ( TCP_MAX_HEADER_LEN ); - if ( ! iobuf ) { - DBGC ( tcp, "TCP %p could not allocate iobuf for RST " - "%08x..%08x %08x\n", tcp, ntohl ( in_tcphdr->ack ), - ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ) ); - return -ENOMEM; - } - iob_reserve ( iobuf, TCP_MAX_HEADER_LEN ); - - /* Construct RST response */ - tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); - memset ( tcphdr, 0, sizeof ( *tcphdr ) ); - tcphdr->src = in_tcphdr->dest; - tcphdr->dest = in_tcphdr->src; - tcphdr->seq = in_tcphdr->ack; - tcphdr->ack = in_tcphdr->seq; - tcphdr->hlen = ( ( sizeof ( *tcphdr ) / 4 ) << 4 ); - tcphdr->flags = ( TCP_RST | TCP_ACK ); - tcphdr->win = htons ( 0 ); - tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) ); - - /* Dump header */ - DBGC2 ( tcp, "TCP %p TX %d->%d %08x..%08x %08x %4d", - tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ), - ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ), - ntohl ( tcphdr->ack ), 0 ); - tcp_dump_flags ( tcp, tcphdr->flags ); - DBGC2 ( tcp, "\n" ); - - /* Transmit packet */ - if ( ( rc = tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest, - NULL, &tcphdr->csum ) ) != 0 ) { - DBGC ( tcp, "TCP %p could not transmit RST %08x..%08x %08x: " - "%s\n", tcp, ntohl ( in_tcphdr->ack ), - ntohl ( in_tcphdr->ack ), ntohl ( in_tcphdr->seq ), - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/*************************************************************************** - * - * Receive data path - * - *************************************************************************** - */ - -/** - * Identify TCP connection by local port number - * - * @v local_port Local port - * @ret tcp TCP connection, or NULL - */ -static struct tcp_connection * tcp_demux ( unsigned int local_port ) { - struct tcp_connection *tcp; - - list_for_each_entry ( tcp, &tcp_conns, list ) { - if ( tcp->local_port == local_port ) - return tcp; - } - return NULL; -} - -/** - * Parse TCP received options - * - * @v tcp TCP connection - * @v data Raw options data - * @v len Raw options length - * @v options Options structure to fill in - */ -static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, - size_t len, struct tcp_options *options ) { - const void *end = ( data + len ); - const struct tcp_option *option; - unsigned int kind; - - memset ( options, 0, sizeof ( *options ) ); - while ( data < end ) { - option = data; - kind = option->kind; - if ( kind == TCP_OPTION_END ) - return; - if ( kind == TCP_OPTION_NOP ) { - data++; - continue; - } - switch ( kind ) { - case TCP_OPTION_MSS: - options->mssopt = data; - break; - case TCP_OPTION_WS: - options->wsopt = data; - break; - case TCP_OPTION_SACK_PERMITTED: - options->spopt = data; - break; - case TCP_OPTION_SACK: - /* Ignore received SACKs */ - break; - case TCP_OPTION_TS: - options->tsopt = data; - break; - default: - DBGC ( tcp, "TCP %p received unknown option %d\n", - tcp, kind ); - break; - } - data += option->length; - } -} - -/** - * Consume received sequence space - * - * @v tcp TCP connection - * @v seq_len Sequence space length to consume - */ -static void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) { - unsigned int sack; - - /* Sanity check */ - assert ( seq_len > 0 ); - - /* Update acknowledgement number */ - tcp->rcv_ack += seq_len; - - /* Update window */ - if ( tcp->rcv_win > seq_len ) { - tcp->rcv_win -= seq_len; - } else { - tcp->rcv_win = 0; - } - - /* Update timestamp */ - tcp->ts_recent = tcp->ts_val; - - /* Update SACK list */ - for ( sack = 0 ; sack < TCP_SACK_MAX ; sack++ ) { - if ( tcp->sack[sack].left == tcp->sack[sack].right ) - continue; - if ( tcp_cmp ( tcp->sack[sack].left, tcp->rcv_ack ) < 0 ) - tcp->sack[sack].left = tcp->rcv_ack; - if ( tcp_cmp ( tcp->sack[sack].right, tcp->rcv_ack ) < 0 ) - tcp->sack[sack].right = tcp->rcv_ack; - } - - /* Mark ACK as pending */ - tcp->flags |= TCP_ACK_PENDING; -} - -/** - * Handle TCP received SYN - * - * @v tcp TCP connection - * @v seq SEQ value (in host-endian order) - * @v options TCP options - * @ret rc Return status code - */ -static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, - struct tcp_options *options ) { - - /* Synchronise sequence numbers on first SYN */ - if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) { - tcp->rcv_ack = seq; - if ( options->tsopt ) - tcp->flags |= TCP_TS_ENABLED; - if ( options->spopt ) - tcp->flags |= TCP_SACK_ENABLED; - if ( options->wsopt ) { - tcp->snd_win_scale = options->wsopt->scale; - tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE; - } - } - - /* Ignore duplicate SYN */ - if ( seq != tcp->rcv_ack ) - return 0; - - /* Acknowledge SYN */ - tcp_rx_seq ( tcp, 1 ); - - /* Mark SYN as received and start sending ACKs with each packet */ - tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) | - TCP_STATE_RCVD ( TCP_SYN ) ); - - return 0; -} - -/** - * Handle TCP received ACK - * - * @v tcp TCP connection - * @v ack ACK value (in host-endian order) - * @v win WIN value (in host-endian order) - * @ret rc Return status code - */ -static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, - uint32_t win ) { - uint32_t ack_len = ( ack - tcp->snd_seq ); - size_t len; - unsigned int acked_flags; - - /* Check for out-of-range or old duplicate ACKs */ - if ( ack_len > tcp->snd_sent ) { - DBGC ( tcp, "TCP %p received ACK for %08x..%08x, " - "sent only %08x..%08x\n", tcp, tcp->snd_seq, - ( tcp->snd_seq + ack_len ), tcp->snd_seq, - ( tcp->snd_seq + tcp->snd_sent ) ); - - if ( TCP_HAS_BEEN_ESTABLISHED ( tcp->tcp_state ) ) { - /* Just ignore what might be old duplicate ACKs */ - return 0; - } else { - /* Send RST if an out-of-range ACK is received - * on a not-yet-established connection, as per - * RFC 793. - */ - return -EINVAL; - } - } - - /* Update window size */ - tcp->snd_win = win; - - /* Ignore ACKs that don't actually acknowledge any new data. - * (In particular, do not stop the retransmission timer; this - * avoids creating a sorceror's apprentice syndrome when a - * duplicate ACK is received and we still have data in our - * transmit queue.) - */ - if ( ack_len == 0 ) - return 0; - - /* Stop the retransmission timer */ - stop_timer ( &tcp->timer ); - - /* Determine acknowledged flags and data length */ - len = ack_len; - acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) & - ( TCP_SYN | TCP_FIN ) ); - if ( acked_flags ) { - len--; - pending_put ( &tcp->pending_flags ); - } - - /* Update SEQ and sent counters */ - tcp->snd_seq = ack; - tcp->snd_sent = 0; - - /* Remove any acknowledged data from transmit queue */ - tcp_process_tx_queue ( tcp, len, NULL, 1 ); - - /* Mark SYN/FIN as acknowledged if applicable. */ - if ( acked_flags ) - tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags ); - - /* Start sending FIN if we've had all possible data ACKed */ - if ( list_empty ( &tcp->tx_queue ) && - ( tcp->flags & TCP_XFER_CLOSED ) && - ! ( tcp->tcp_state & TCP_STATE_SENT ( TCP_FIN ) ) ) { - tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); - pending_get ( &tcp->pending_flags ); - } - - return 0; -} - -/** - * Handle TCP received data - * - * @v tcp TCP connection - * @v seq SEQ value (in host-endian order) - * @v iobuf I/O buffer - * @ret rc Return status code - * - * This function takes ownership of the I/O buffer. - */ -static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq, - struct io_buffer *iobuf ) { - uint32_t already_rcvd; - uint32_t len; - int rc; - - /* Ignore duplicate or out-of-order data */ - already_rcvd = ( tcp->rcv_ack - seq ); - len = iob_len ( iobuf ); - if ( already_rcvd >= len ) { - free_iob ( iobuf ); - return 0; - } - iob_pull ( iobuf, already_rcvd ); - len -= already_rcvd; - - /* Acknowledge new data */ - tcp_rx_seq ( tcp, len ); - - /* Deliver data to application */ - profile_start ( &tcp_xfer_profiler ); - if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) { - DBGC ( tcp, "TCP %p could not deliver %08x..%08x: %s\n", - tcp, seq, ( seq + len ), strerror ( rc ) ); - return rc; - } - profile_stop ( &tcp_xfer_profiler ); - - return 0; -} - -/** - * Handle TCP received FIN - * - * @v tcp TCP connection - * @v seq SEQ value (in host-endian order) - * @ret rc Return status code - */ -static int tcp_rx_fin ( struct tcp_connection *tcp, uint32_t seq ) { - - /* Ignore duplicate or out-of-order FIN */ - if ( seq != tcp->rcv_ack ) - return 0; - - /* Acknowledge FIN */ - tcp_rx_seq ( tcp, 1 ); - - /* Mark FIN as received */ - tcp->tcp_state |= TCP_STATE_RCVD ( TCP_FIN ); - - /* Close connection */ - tcp_close ( tcp, 0 ); - - return 0; -} - -/** - * Handle TCP received RST - * - * @v tcp TCP connection - * @v seq SEQ value (in host-endian order) - * @ret rc Return status code - */ -static int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) { - - /* Accept RST only if it falls within the window. If we have - * not yet received a SYN, then we have no window to test - * against, so fall back to checking that our SYN has been - * ACKed. - */ - if ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) { - if ( ! tcp_in_window ( seq, tcp->rcv_ack, tcp->rcv_win ) ) - return 0; - } else { - if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) - return 0; - } - - /* Abort connection */ - tcp->tcp_state = TCP_CLOSED; - tcp_dump_state ( tcp ); - tcp_close ( tcp, -ECONNRESET ); - - DBGC ( tcp, "TCP %p connection reset by peer\n", tcp ); - return -ECONNRESET; -} - -/** - * Enqueue received TCP packet - * - * @v tcp TCP connection - * @v seq SEQ value (in host-endian order) - * @v flags TCP flags - * @v iobuf I/O buffer - */ -static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, - uint8_t flags, struct io_buffer *iobuf ) { - struct tcp_rx_queued_header *tcpqhdr; - struct io_buffer *queued; - size_t len; - uint32_t seq_len; - uint32_t nxt; - - /* Calculate remaining flags and sequence length. Note that - * SYN, if present, has already been processed by this point. - */ - flags &= TCP_FIN; - len = iob_len ( iobuf ); - seq_len = ( len + ( flags ? 1 : 0 ) ); - nxt = ( seq + seq_len ); - - /* Discard immediately (to save memory) if: - * - * a) we have not yet received a SYN (and so have no defined - * receive window), or - * b) the packet lies entirely outside the receive window, or - * c) there is no further content to process. - */ - if ( ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) || - ( tcp_cmp ( seq, tcp->rcv_ack + tcp->rcv_win ) >= 0 ) || - ( tcp_cmp ( nxt, tcp->rcv_ack ) < 0 ) || - ( seq_len == 0 ) ) { - free_iob ( iobuf ); - return; - } - - /* Add internal header */ - tcpqhdr = iob_push ( iobuf, sizeof ( *tcpqhdr ) ); - tcpqhdr->seq = seq; - tcpqhdr->nxt = nxt; - tcpqhdr->flags = flags; - - /* Add to RX queue */ - list_for_each_entry ( queued, &tcp->rx_queue, list ) { - tcpqhdr = queued->data; - if ( tcp_cmp ( seq, tcpqhdr->seq ) < 0 ) - break; - } - list_add_tail ( &iobuf->list, &queued->list ); -} - -/** - * Process receive queue - * - * @v tcp TCP connection - */ -static void tcp_process_rx_queue ( struct tcp_connection *tcp ) { - struct io_buffer *iobuf; - struct tcp_rx_queued_header *tcpqhdr; - uint32_t seq; - unsigned int flags; - size_t len; - - /* Process all applicable received buffers. Note that we - * cannot use list_for_each_entry() to iterate over the RX - * queue, since tcp_discard() may remove packets from the RX - * queue while we are processing. - */ - while ( ( iobuf = list_first_entry ( &tcp->rx_queue, struct io_buffer, - list ) ) ) { - - /* Stop processing when we hit the first gap */ - tcpqhdr = iobuf->data; - if ( tcp_cmp ( tcpqhdr->seq, tcp->rcv_ack ) > 0 ) - break; - - /* Strip internal header and remove from RX queue */ - list_del ( &iobuf->list ); - seq = tcpqhdr->seq; - flags = tcpqhdr->flags; - iob_pull ( iobuf, sizeof ( *tcpqhdr ) ); - len = iob_len ( iobuf ); - - /* Handle new data, if any */ - tcp_rx_data ( tcp, seq, iob_disown ( iobuf ) ); - seq += len; - - /* Handle FIN, if present */ - if ( flags & TCP_FIN ) { - tcp_rx_fin ( tcp, seq ); - seq++; - } - } -} - -/** - * Process received packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v st_src Partially-filled source address - * @v st_dest Partially-filled destination address - * @v pshdr_csum Pseudo-header checksum - * @ret rc Return status code - */ -static int tcp_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest __unused, - uint16_t pshdr_csum ) { - struct tcp_header *tcphdr = iobuf->data; - struct tcp_connection *tcp; - struct tcp_options options; - size_t hlen; - uint16_t csum; - uint32_t seq; - uint32_t ack; - uint16_t raw_win; - uint32_t win; - unsigned int flags; - size_t len; - uint32_t seq_len; - size_t old_xfer_window; - int rc; - - /* Start profiling */ - profile_start ( &tcp_rx_profiler ); - - /* Sanity check packet */ - if ( iob_len ( iobuf ) < sizeof ( *tcphdr ) ) { - DBG ( "TCP packet too short at %zd bytes (min %zd bytes)\n", - iob_len ( iobuf ), sizeof ( *tcphdr ) ); - rc = -EINVAL; - goto discard; - } - hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4; - if ( hlen < sizeof ( *tcphdr ) ) { - DBG ( "TCP header too short at %zd bytes (min %zd bytes)\n", - hlen, sizeof ( *tcphdr ) ); - rc = -EINVAL; - goto discard; - } - if ( hlen > iob_len ( iobuf ) ) { - DBG ( "TCP header too long at %zd bytes (max %zd bytes)\n", - hlen, iob_len ( iobuf ) ); - rc = -EINVAL; - goto discard; - } - csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, - iob_len ( iobuf ) ); - if ( csum != 0 ) { - DBG ( "TCP checksum incorrect (is %04x including checksum " - "field, should be 0000)\n", csum ); - rc = -EINVAL; - goto discard; - } - - /* Parse parameters from header and strip header */ - tcp = tcp_demux ( ntohs ( tcphdr->dest ) ); - seq = ntohl ( tcphdr->seq ); - ack = ntohl ( tcphdr->ack ); - raw_win = ntohs ( tcphdr->win ); - flags = tcphdr->flags; - tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ), - ( hlen - sizeof ( *tcphdr ) ), &options ); - if ( tcp && options.tsopt ) - tcp->ts_val = ntohl ( options.tsopt->tsval ); - iob_pull ( iobuf, hlen ); - len = iob_len ( iobuf ); - seq_len = ( len + ( ( flags & TCP_SYN ) ? 1 : 0 ) + - ( ( flags & TCP_FIN ) ? 1 : 0 ) ); - - /* Dump header */ - DBGC2 ( tcp, "TCP %p RX %d<-%d %08x %08x..%08x %4zd", - tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ), - ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ), - ( ntohl ( tcphdr->seq ) + seq_len ), len ); - tcp_dump_flags ( tcp, tcphdr->flags ); - DBGC2 ( tcp, "\n" ); - - /* If no connection was found, silently drop packet */ - if ( ! tcp ) { - rc = -ENOTCONN; - goto discard; - } - - /* Record old data-transfer window */ - old_xfer_window = tcp_xfer_window ( tcp ); - - /* Handle ACK, if present */ - if ( flags & TCP_ACK ) { - win = ( raw_win << tcp->snd_win_scale ); - if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) { - tcp_xmit_reset ( tcp, st_src, tcphdr ); - goto discard; - } - } - - /* Force an ACK if this packet is out of order */ - if ( ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) && - ( seq != tcp->rcv_ack ) ) { - tcp->flags |= TCP_ACK_PENDING; - } - - /* Handle SYN, if present */ - if ( flags & TCP_SYN ) { - tcp_rx_syn ( tcp, seq, &options ); - seq++; - } - - /* Handle RST, if present */ - if ( flags & TCP_RST ) { - if ( ( rc = tcp_rx_rst ( tcp, seq ) ) != 0 ) - goto discard; - } - - /* Enqueue received data */ - tcp_rx_enqueue ( tcp, seq, flags, iob_disown ( iobuf ) ); - - /* Process receive queue */ - tcp_process_rx_queue ( tcp ); - - /* Dump out any state change as a result of the received packet */ - tcp_dump_state ( tcp ); - - /* Schedule transmission of ACK (and any pending data). If we - * have received any out-of-order packets (i.e. if the receive - * queue remains non-empty after processing) then send the ACK - * immediately in order to trigger Fast Retransmission. - */ - if ( list_empty ( &tcp->rx_queue ) ) { - process_add ( &tcp->process ); - } else { - tcp_xmit_sack ( tcp, seq ); - } - - /* If this packet was the last we expect to receive, set up - * timer to expire and cause the connection to be freed. - */ - if ( TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) { - stop_timer ( &tcp->wait ); - start_timer_fixed ( &tcp->wait, ( 2 * TCP_MSL ) ); - } - - /* Notify application if window has changed */ - if ( tcp_xfer_window ( tcp ) != old_xfer_window ) - xfer_window_changed ( &tcp->xfer ); - - profile_stop ( &tcp_rx_profiler ); - return 0; - - discard: - /* Free received packet */ - free_iob ( iobuf ); - return rc; -} - -/** TCP protocol */ -struct tcpip_protocol tcp_protocol __tcpip_protocol = { - .name = "TCP", - .rx = tcp_rx, - .tcpip_proto = IP_TCP, -}; - -/** - * Discard some cached TCP data - * - * @ret discarded Number of cached items discarded - */ -static unsigned int tcp_discard ( void ) { - struct tcp_connection *tcp; - struct io_buffer *iobuf; - unsigned int discarded = 0; - - /* Try to drop one queued RX packet from each connection */ - list_for_each_entry ( tcp, &tcp_conns, list ) { - list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) { - - /* Remove packet from queue */ - list_del ( &iobuf->list ); - free_iob ( iobuf ); - - /* Report discard */ - discarded++; - break; - } - } - - return discarded; -} - -/** TCP cache discarder */ -struct cache_discarder tcp_discarder __cache_discarder ( CACHE_NORMAL ) = { - .discard = tcp_discard, -}; - -/** - * Find first TCP connection that has not yet been closed - * - * @ret tcp First unclosed connection, or NULL - */ -static struct tcp_connection * tcp_first_unclosed ( void ) { - struct tcp_connection *tcp; - - /* Find first connection which has not yet been closed */ - list_for_each_entry ( tcp, &tcp_conns, list ) { - if ( ! ( tcp->flags & TCP_XFER_CLOSED ) ) - return tcp; - } - return NULL; -} - -/** - * Find first TCP connection that has not yet finished all operations - * - * @ret tcp First unfinished connection, or NULL - */ -static struct tcp_connection * tcp_first_unfinished ( void ) { - struct tcp_connection *tcp; - - /* Find first connection which has not yet closed gracefully, - * or which still has a pending transmission (e.g. to ACK the - * received FIN). - */ - list_for_each_entry ( tcp, &tcp_conns, list ) { - if ( ( ! TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) || - process_running ( &tcp->process ) ) { - return tcp; - } - } - return NULL; -} - -/** - * Shut down all TCP connections - * - */ -static void tcp_shutdown ( int booting __unused ) { - struct tcp_connection *tcp; - unsigned long start; - unsigned long elapsed; - - /* Initiate a graceful close of all connections, allowing for - * the fact that the connection list may change as we do so. - */ - while ( ( tcp = tcp_first_unclosed() ) ) { - DBGC ( tcp, "TCP %p closing for shutdown\n", tcp ); - tcp_close ( tcp, -ECANCELED ); - } - - /* Wait for all connections to finish closing gracefully */ - start = currticks(); - while ( ( tcp = tcp_first_unfinished() ) && - ( ( elapsed = ( currticks() - start ) ) < TCP_FINISH_TIMEOUT )){ - step(); - } - - /* Forcibly close any remaining connections */ - while ( ( tcp = list_first_entry ( &tcp_conns, struct tcp_connection, - list ) ) != NULL ) { - tcp->tcp_state = TCP_CLOSED; - tcp_dump_state ( tcp ); - tcp_close ( tcp, -ECANCELED ); - } -} - -/** TCP shutdown function */ -struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_LATE ) = { - .shutdown = tcp_shutdown, -}; - -/*************************************************************************** - * - * Data transfer interface - * - *************************************************************************** - */ - -/** - * Close interface - * - * @v tcp TCP connection - * @v rc Reason for close - */ -static void tcp_xfer_close ( struct tcp_connection *tcp, int rc ) { - - /* Close data transfer interface */ - tcp_close ( tcp, rc ); - - /* Transmit FIN, if possible */ - tcp_xmit ( tcp ); -} - -/** - * Deliver datagram as I/O buffer - * - * @v tcp TCP connection - * @v iobuf Datagram I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int tcp_xfer_deliver ( struct tcp_connection *tcp, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - - /* Enqueue packet */ - list_add_tail ( &iobuf->list, &tcp->tx_queue ); - - /* Each enqueued packet is a pending operation */ - pending_get ( &tcp->pending_data ); - - /* Transmit data, if possible */ - tcp_xmit ( tcp ); - - return 0; -} - -/** TCP data transfer interface operations */ -static struct interface_operation tcp_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct tcp_connection *, tcp_xfer_deliver ), - INTF_OP ( xfer_window, struct tcp_connection *, tcp_xfer_window ), - INTF_OP ( intf_close, struct tcp_connection *, tcp_xfer_close ), -}; - -/** TCP data transfer interface descriptor */ -static struct interface_descriptor tcp_xfer_desc = - INTF_DESC ( struct tcp_connection, xfer, tcp_xfer_operations ); - -/*************************************************************************** - * - * Openers - * - *************************************************************************** - */ - -/** TCP IPv4 socket opener */ -struct socket_opener tcp_ipv4_socket_opener __socket_opener = { - .semantics = TCP_SOCK_STREAM, - .family = AF_INET, - .open = tcp_open, -}; - -/** TCP IPv6 socket opener */ -struct socket_opener tcp_ipv6_socket_opener __socket_opener = { - .semantics = TCP_SOCK_STREAM, - .family = AF_INET6, - .open = tcp_open, -}; - -/** Linkage hack */ -int tcp_sock_stream = TCP_SOCK_STREAM; - -/** - * Open TCP URI - * - * @v xfer Data transfer interface - * @v uri URI - * @ret rc Return status code - */ -static int tcp_open_uri ( struct interface *xfer, struct uri *uri ) { - struct sockaddr_tcpip peer; - - /* Sanity check */ - if ( ! uri->host ) - return -EINVAL; - - memset ( &peer, 0, sizeof ( peer ) ); - peer.st_port = htons ( uri_port ( uri, 0 ) ); - return xfer_open_named_socket ( xfer, SOCK_STREAM, - ( struct sockaddr * ) &peer, - uri->host, NULL ); -} - -/** TCP URI opener */ -struct uri_opener tcp_uri_opener __uri_opener = { - .scheme = "tcp", - .open = tcp_open_uri, -}; - diff --git a/qemu/roms/ipxe/src/net/tcp/ftp.c b/qemu/roms/ipxe/src/net/tcp/ftp.c deleted file mode 100644 index be7a7c3b5..000000000 --- a/qemu/roms/ipxe/src/net/tcp/ftp.c +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <ctype.h> -#include <byteswap.h> -#include <ipxe/socket.h> -#include <ipxe/tcpip.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/features.h> -#include <ipxe/ftp.h> - -/** @file - * - * File transfer protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "FTP", DHCP_EB_FEATURE_FTP, 1 ); - -/** - * FTP states - * - * These @b must be sequential, i.e. a successful FTP session must - * pass through each of these states in order. - */ -enum ftp_state { - FTP_CONNECT = 0, - FTP_USER, - FTP_PASS, - FTP_TYPE, - FTP_SIZE, - FTP_PASV, - FTP_RETR, - FTP_WAIT, - FTP_QUIT, - FTP_DONE, -}; - -/** - * An FTP request - * - */ -struct ftp_request { - /** Reference counter */ - struct refcnt refcnt; - /** Data transfer interface */ - struct interface xfer; - - /** URI being fetched */ - struct uri *uri; - /** FTP control channel interface */ - struct interface control; - /** FTP data channel interface */ - struct interface data; - - /** Current state */ - enum ftp_state state; - /** Buffer to be filled with data received via the control channel */ - char *recvbuf; - /** Remaining size of recvbuf */ - size_t recvsize; - /** FTP status code, as text */ - char status_text[5]; - /** Passive-mode parameters, as text */ - char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */ - /** File size, as text */ - char filesize[20]; -}; - -/** - * Free FTP request - * - * @v refcnt Reference counter - */ -static void ftp_free ( struct refcnt *refcnt ) { - struct ftp_request *ftp = - container_of ( refcnt, struct ftp_request, refcnt ); - - DBGC ( ftp, "FTP %p freed\n", ftp ); - - uri_put ( ftp->uri ); - free ( ftp ); -} - -/** - * Mark FTP operation as complete - * - * @v ftp FTP request - * @v rc Return status code - */ -static void ftp_done ( struct ftp_request *ftp, int rc ) { - - DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) ); - - /* Close all data transfer interfaces */ - intf_shutdown ( &ftp->data, rc ); - intf_shutdown ( &ftp->control, rc ); - intf_shutdown ( &ftp->xfer, rc ); -} - -/***************************************************************************** - * - * FTP control channel - * - */ - -/** An FTP control channel string */ -struct ftp_control_string { - /** Literal portion */ - const char *literal; - /** Variable portion - * - * @v ftp FTP request - * @ret string Variable portion of string - */ - const char * ( *variable ) ( struct ftp_request *ftp ); -}; - -/** - * Retrieve FTP pathname - * - * @v ftp FTP request - * @ret path FTP pathname - */ -static const char * ftp_uri_path ( struct ftp_request *ftp ) { - return ftp->uri->path; -} - -/** - * Retrieve FTP user - * - * @v ftp FTP request - * @ret user FTP user - */ -static const char * ftp_user ( struct ftp_request *ftp ) { - static char *ftp_default_user = "anonymous"; - return ftp->uri->user ? ftp->uri->user : ftp_default_user; -} - -/** - * Retrieve FTP password - * - * @v ftp FTP request - * @ret password FTP password - */ -static const char * ftp_password ( struct ftp_request *ftp ) { - static char *ftp_default_password = "ipxe@ipxe.org"; - return ftp->uri->password ? ftp->uri->password : ftp_default_password; -} - -/** FTP control channel strings */ -static struct ftp_control_string ftp_strings[] = { - [FTP_CONNECT] = { NULL, NULL }, - [FTP_USER] = { "USER ", ftp_user }, - [FTP_PASS] = { "PASS ", ftp_password }, - [FTP_TYPE] = { "TYPE I", NULL }, - [FTP_SIZE] = { "SIZE ", ftp_uri_path }, - [FTP_PASV] = { "PASV", NULL }, - [FTP_RETR] = { "RETR ", ftp_uri_path }, - [FTP_WAIT] = { NULL, NULL }, - [FTP_QUIT] = { "QUIT", NULL }, - [FTP_DONE] = { NULL, NULL }, -}; - -/** - * Parse FTP byte sequence value - * - * @v text Text string - * @v value Value buffer - * @v len Length of value buffer - * - * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd" - * form for IP addresses in PORT commands) into a byte sequence. @c - * *text will be updated to point beyond the end of the parsed byte - * sequence. - * - * This function is safe in the presence of malformed data, though the - * output is undefined. - */ -static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) { - do { - *(value++) = strtoul ( *text, text, 10 ); - if ( **text ) - (*text)++; - } while ( --len ); -} - -/** - * Move to next state and send the appropriate FTP control string - * - * @v ftp FTP request - * - */ -static void ftp_next_state ( struct ftp_request *ftp ) { - struct ftp_control_string *ftp_string; - const char *literal; - const char *variable; - - /* Move to next state */ - if ( ftp->state < FTP_DONE ) - ftp->state++; - - /* Send control string if needed */ - ftp_string = &ftp_strings[ftp->state]; - literal = ftp_string->literal; - variable = ( ftp_string->variable ? - ftp_string->variable ( ftp ) : "" ); - if ( literal ) { - DBGC ( ftp, "FTP %p sending %s%s\n", ftp, literal, variable ); - xfer_printf ( &ftp->control, "%s%s\r\n", literal, variable ); - } -} - -/** - * Handle an FTP control channel response - * - * @v ftp FTP request - * - * This is called once we have received a complete response line. - */ -static void ftp_reply ( struct ftp_request *ftp ) { - char status_major = ftp->status_text[0]; - char separator = ftp->status_text[3]; - - DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text ); - - /* Ignore malformed lines */ - if ( separator != ' ' ) - return; - - /* Ignore "intermediate" responses (1xx codes) */ - if ( status_major == '1' ) - return; - - /* If the SIZE command is not supported by the server, we go to - * the next step. - */ - if ( ( status_major == '5' ) && ( ftp->state == FTP_SIZE ) ) { - ftp_next_state ( ftp ); - return; - } - - /* Anything other than success (2xx) or, in the case of a - * repsonse to a "USER" command, a password prompt (3xx), is a - * fatal error. - */ - if ( ! ( ( status_major == '2' ) || - ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){ - /* Flag protocol error and close connections */ - ftp_done ( ftp, -EPROTO ); - return; - } - - /* Parse file size */ - if ( ftp->state == FTP_SIZE ) { - size_t filesize; - char *endptr; - - /* Parse size */ - filesize = strtoul ( ftp->filesize, &endptr, 10 ); - if ( *endptr != '\0' ) { - DBGC ( ftp, "FTP %p invalid SIZE \"%s\"\n", - ftp, ftp->filesize ); - ftp_done ( ftp, -EPROTO ); - return; - } - - /* Use seek() to notify recipient of filesize */ - DBGC ( ftp, "FTP %p file size is %zd bytes\n", ftp, filesize ); - xfer_seek ( &ftp->xfer, filesize ); - xfer_seek ( &ftp->xfer, 0 ); - } - - /* Open passive connection when we get "PASV" response */ - if ( ftp->state == FTP_PASV ) { - char *ptr = ftp->passive_text; - union { - struct sockaddr_in sin; - struct sockaddr sa; - } sa; - int rc; - - sa.sin.sin_family = AF_INET; - ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr, - sizeof ( sa.sin.sin_addr ) ); - ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port, - sizeof ( sa.sin.sin_port ) ); - if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM, - &sa.sa, NULL ) ) != 0 ) { - DBGC ( ftp, "FTP %p could not open data connection\n", - ftp ); - ftp_done ( ftp, rc ); - return; - } - } - - /* Move to next state and send control string */ - ftp_next_state ( ftp ); - -} - -/** - * Handle new data arriving on FTP control channel - * - * @v ftp FTP request - * @v iob I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - * - * Data is collected until a complete line is received, at which point - * its information is passed to ftp_reply(). - */ -static int ftp_control_deliver ( struct ftp_request *ftp, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - char *data = iobuf->data; - size_t len = iob_len ( iobuf ); - char *recvbuf = ftp->recvbuf; - size_t recvsize = ftp->recvsize; - char c; - - while ( len-- ) { - c = *(data++); - if ( ( c == '\r' ) || ( c == '\n' ) ) { - /* End of line: call ftp_reply() to handle - * completed reply. Avoid calling ftp_reply() - * twice if we receive both \r and \n. - */ - if ( recvbuf != ftp->status_text ) - ftp_reply ( ftp ); - /* Start filling up the status code buffer */ - recvbuf = ftp->status_text; - recvsize = sizeof ( ftp->status_text ) - 1; - } else if ( ( ftp->state == FTP_PASV ) && ( c == '(' ) ) { - /* Start filling up the passive parameter buffer */ - recvbuf = ftp->passive_text; - recvsize = sizeof ( ftp->passive_text ) - 1; - } else if ( ( ftp->state == FTP_PASV ) && ( c == ')' ) ) { - /* Stop filling the passive parameter buffer */ - recvsize = 0; - } else if ( ( ftp->state == FTP_SIZE ) && ( c == ' ' ) ) { - /* Start filling up the file size buffer */ - recvbuf = ftp->filesize; - recvsize = sizeof ( ftp->filesize ) - 1; - } else { - /* Fill up buffer if applicable */ - if ( recvsize > 0 ) { - *(recvbuf++) = c; - recvsize--; - } - } - } - - /* Store for next invocation */ - ftp->recvbuf = recvbuf; - ftp->recvsize = recvsize; - - /* Free I/O buffer */ - free_iob ( iobuf ); - - return 0; -} - -/** FTP control channel interface operations */ -static struct interface_operation ftp_control_operations[] = { - INTF_OP ( xfer_deliver, struct ftp_request *, ftp_control_deliver ), - INTF_OP ( intf_close, struct ftp_request *, ftp_done ), -}; - -/** FTP control channel interface descriptor */ -static struct interface_descriptor ftp_control_desc = - INTF_DESC ( struct ftp_request, control, ftp_control_operations ); - -/***************************************************************************** - * - * FTP data channel - * - */ - -/** - * Handle FTP data channel being closed - * - * @v ftp FTP request - * @v rc Reason for closure - * - * When the data channel is closed, the control channel should be left - * alone; the server will send a completion message via the control - * channel which we'll pick up. - * - * If the data channel is closed due to an error, we abort the request. - */ -static void ftp_data_closed ( struct ftp_request *ftp, int rc ) { - - DBGC ( ftp, "FTP %p data connection closed: %s\n", - ftp, strerror ( rc ) ); - - /* If there was an error, close control channel and record status */ - if ( rc ) { - ftp_done ( ftp, rc ); - } else { - ftp_next_state ( ftp ); - } -} - -/** FTP data channel interface operations */ -static struct interface_operation ftp_data_operations[] = { - INTF_OP ( intf_close, struct ftp_request *, ftp_data_closed ), -}; - -/** FTP data channel interface descriptor */ -static struct interface_descriptor ftp_data_desc = - INTF_DESC_PASSTHRU ( struct ftp_request, data, ftp_data_operations, - xfer ); - -/***************************************************************************** - * - * Data transfer interface - * - */ - -/** FTP data transfer interface operations */ -static struct interface_operation ftp_xfer_operations[] = { - INTF_OP ( intf_close, struct ftp_request *, ftp_done ), -}; - -/** FTP data transfer interface descriptor */ -static struct interface_descriptor ftp_xfer_desc = - INTF_DESC_PASSTHRU ( struct ftp_request, xfer, ftp_xfer_operations, - data ); - -/***************************************************************************** - * - * URI opener - * - */ - -/** - * Check validity of FTP control channel string - * - * @v string String - * @ret rc Return status code - */ -static int ftp_check_string ( const char *string ) { - char c; - - /* The FTP control channel is line-based. Check for invalid - * non-printable characters (e.g. newlines). - */ - while ( ( c = *(string++) ) ) { - if ( ! isprint ( c ) ) - return -EINVAL; - } - return 0; -} - -/** - * Initiate an FTP connection - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int ftp_open ( struct interface *xfer, struct uri *uri ) { - struct ftp_request *ftp; - struct sockaddr_tcpip server; - int rc; - - /* Sanity checks */ - if ( ! uri->host ) - return -EINVAL; - if ( ! uri->path ) - return -EINVAL; - if ( ( rc = ftp_check_string ( uri->path ) ) != 0 ) - return rc; - if ( uri->user && ( ( rc = ftp_check_string ( uri->user ) ) != 0 ) ) - return rc; - if ( uri->password && - ( ( rc = ftp_check_string ( uri->password ) ) != 0 ) ) - return rc; - - /* Allocate and populate structure */ - ftp = zalloc ( sizeof ( *ftp ) ); - if ( ! ftp ) - return -ENOMEM; - ref_init ( &ftp->refcnt, ftp_free ); - intf_init ( &ftp->xfer, &ftp_xfer_desc, &ftp->refcnt ); - intf_init ( &ftp->control, &ftp_control_desc, &ftp->refcnt ); - intf_init ( &ftp->data, &ftp_data_desc, &ftp->refcnt ); - ftp->uri = uri_get ( uri ); - ftp->recvbuf = ftp->status_text; - ftp->recvsize = sizeof ( ftp->status_text ) - 1; - - DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path ); - - /* Open control connection */ - memset ( &server, 0, sizeof ( server ) ); - server.st_port = htons ( uri_port ( uri, FTP_PORT ) ); - if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM, - ( struct sockaddr * ) &server, - uri->host, NULL ) ) != 0 ) - goto err; - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &ftp->xfer, xfer ); - ref_put ( &ftp->refcnt ); - return 0; - - err: - DBGC ( ftp, "FTP %p could not create request: %s\n", - ftp, strerror ( rc ) ); - ftp_done ( ftp, rc ); - ref_put ( &ftp->refcnt ); - return rc; -} - -/** FTP URI opener */ -struct uri_opener ftp_uri_opener __uri_opener = { - .scheme = "ftp", - .open = ftp_open, -}; diff --git a/qemu/roms/ipxe/src/net/tcp/http.c b/qemu/roms/ipxe/src/net/tcp/http.c deleted file mode 100644 index b000ed80f..000000000 --- a/qemu/roms/ipxe/src/net/tcp/http.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) - * - */ - -#include <ipxe/open.h> -#include <ipxe/http.h> -#include <ipxe/features.h> - -FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); - -/** HTTP URI opener */ -struct uri_opener http_uri_opener __uri_opener = { - .scheme = "http", - .open = http_open_uri, -}; - -/** HTTP URI scheme */ -struct http_scheme http_scheme __http_scheme = { - .name = "http", - .port = HTTP_PORT, -}; diff --git a/qemu/roms/ipxe/src/net/tcp/httpauth.c b/qemu/roms/ipxe/src/net/tcp/httpauth.c deleted file mode 100644 index fb6dcd035..000000000 --- a/qemu/roms/ipxe/src/net/tcp/httpauth.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) authentication - * - */ - -#include <stdio.h> -#include <strings.h> -#include <errno.h> -#include <ipxe/http.h> - -/** - * Identify authentication scheme - * - * @v http HTTP transaction - * @v name Scheme name - * @ret auth Authentication scheme, or NULL - */ -static struct http_authentication * http_authentication ( const char *name ) { - struct http_authentication *auth; - - /* Identify authentication scheme */ - for_each_table_entry ( auth, HTTP_AUTHENTICATIONS ) { - if ( strcasecmp ( name, auth->name ) == 0 ) - return auth; - } - - return NULL; -} - -/** An HTTP "WWW-Authenticate" response field */ -struct http_www_authenticate_field { - /** Name */ - const char *name; - /** Offset */ - size_t offset; -}; - -/** Define an HTTP "WWW-Authenticate" response field */ -#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) { \ - .name = #_name, \ - .offset = offsetof ( struct http_transaction, \ - response.auth._name ), \ - } - -/** - * Set HTTP "WWW-Authenticate" response field value - * - * @v http HTTP transaction - * @v field Response field - * @v value Field value - */ -static inline void -http_www_auth_field ( struct http_transaction *http, - struct http_www_authenticate_field *field, char *value ) { - char **ptr; - - ptr = ( ( ( void * ) http ) + field->offset ); - *ptr = value; -} - -/** HTTP "WWW-Authenticate" fields */ -static struct http_www_authenticate_field http_www_auth_fields[] = { - HTTP_WWW_AUTHENTICATE_FIELD ( realm ), - HTTP_WWW_AUTHENTICATE_FIELD ( qop ), - HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ), - HTTP_WWW_AUTHENTICATE_FIELD ( nonce ), - HTTP_WWW_AUTHENTICATE_FIELD ( opaque ), -}; - -/** - * Parse HTTP "WWW-Authenticate" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_www_authenticate ( struct http_transaction *http, - char *line ) { - struct http_www_authenticate_field *field; - char *name; - char *key; - char *value; - unsigned int i; - - /* Get scheme name */ - name = http_token ( &line, NULL ); - if ( ! name ) { - DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n", - http, value ); - return -EPROTO; - } - - /* Identify scheme */ - http->response.auth.auth = http_authentication ( name ); - if ( ! http->response.auth.auth ) { - DBGC ( http, "HTTP %p unrecognised authentication scheme " - "\"%s\"\n", http, name ); - return -ENOTSUP; - } - - /* Process fields */ - while ( ( key = http_token ( &line, &value ) ) ) { - for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / - sizeof ( http_www_auth_fields[0] ) ) ; i++){ - field = &http_www_auth_fields[i]; - if ( strcasecmp ( key, field->name ) == 0 ) - http_www_auth_field ( http, field, value ); - } - } - - /* Allow HTTP request to be retried if the request had not - * already tried authentication. - */ - if ( ! http->request.auth.auth ) - http->response.flags |= HTTP_RESPONSE_RETRY; - - return 0; -} - -/** HTTP "WWW-Authenticate" header */ -struct http_response_header -http_response_www_authenticate __http_response_header = { - .name = "WWW-Authenticate", - .parse = http_parse_www_authenticate, -}; - -/** - * Construct HTTP "Authorization" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_authorization ( struct http_transaction *http, - char *buf, size_t len ) { - struct http_authentication *auth = http->request.auth.auth; - size_t used; - int auth_len; - int rc; - - /* Do nothing unless we have an authentication scheme */ - if ( ! auth ) - return 0; - - /* Construct header */ - used = snprintf ( buf, len, "%s ", auth->name ); - auth_len = auth->format ( http, ( buf + used ), - ( ( used < len ) ? ( len - used ) : 0 ) ); - if ( auth_len < 0 ) { - rc = auth_len; - return rc; - } - used += auth_len; - - return used; -} - -/** HTTP "Authorization" header */ -struct http_request_header http_request_authorization __http_request_header = { - .name = "Authorization", - .format = http_format_authorization, -}; diff --git a/qemu/roms/ipxe/src/net/tcp/httpbasic.c b/qemu/roms/ipxe/src/net/tcp/httpbasic.c deleted file mode 100644 index 7ed7de9e7..000000000 --- a/qemu/roms/ipxe/src/net/tcp/httpbasic.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) Basic authentication - * - */ - -#include <stdio.h> -#include <errno.h> -#include <ipxe/uri.h> -#include <ipxe/base64.h> -#include <ipxe/http.h> - -/* Disambiguate the various error causes */ -#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME ) -#define EINFO_EACCES_USERNAME \ - __einfo_uniqify ( EINFO_EACCES, 0x01, \ - "No username available for Basic authentication" ) - -/** - * Perform HTTP Basic authentication - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_basic_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; - - /* Record username and password */ - if ( ! http->uri->user ) { - DBGC ( http, "HTTP %p has no username for Basic " - "authentication\n", http ); - return -EACCES_USERNAME; - } - req->username = http->uri->user; - req->password = ( http->uri->password ? http->uri->password : "" ); - - return 0; -} - -/** - * Construct HTTP "Authorization" header for Basic authentication - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_basic_auth ( struct http_transaction *http, - char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; - size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ + - strlen ( req->password ) ); - char user_pw[ user_pw_len + 1 /* NUL */ ]; - - /* Sanity checks */ - assert ( req->username != NULL ); - assert ( req->password != NULL ); - - /* Construct "user:password" string */ - snprintf ( user_pw, sizeof ( user_pw ), "%s:%s", - req->username, req->password ); - - /* Construct response */ - return base64_encode ( user_pw, user_pw_len, buf, len ); -} - -/** HTTP Basic authentication scheme */ -struct http_authentication http_basic_auth __http_authentication = { - .name = "Basic", - .authenticate = http_basic_authenticate, - .format = http_format_basic_auth, -}; - -/* Drag in HTTP authentication support */ -REQUIRING_SYMBOL ( http_basic_auth ); -REQUIRE_OBJECT ( httpauth ); diff --git a/qemu/roms/ipxe/src/net/tcp/httpblock.c b/qemu/roms/ipxe/src/net/tcp/httpblock.c deleted file mode 100644 index e124ad2d6..000000000 --- a/qemu/roms/ipxe/src/net/tcp/httpblock.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) block device - * - */ - -#include <stdint.h> -#include <ipxe/uaccess.h> -#include <ipxe/blocktrans.h> -#include <ipxe/blockdev.h> -#include <ipxe/acpi.h> -#include <ipxe/http.h> - -/** Block size used for HTTP block device requests */ -#define HTTP_BLKSIZE 512 - -/** - * Read from block device - * - * @v http HTTP transaction - * @v data Data interface - * @v lba Starting logical block address - * @v count Number of logical blocks - * @v buffer Data buffer - * @v len Length of data buffer - * @ret rc Return status code - */ -int http_block_read ( struct http_transaction *http, struct interface *data, - uint64_t lba, unsigned int count, userptr_t buffer, - size_t len ) { - struct http_request_range range; - int rc; - - /* Sanity check */ - assert ( len == ( count * HTTP_BLKSIZE ) ); - - /* Construct request range descriptor */ - range.start = ( lba * HTTP_BLKSIZE ); - range.len = len; - - /* Start a range request to retrieve the block(s) */ - if ( ( rc = http_open ( data, &http_get, http->uri, &range, - NULL ) ) != 0 ) - goto err_open; - - /* Insert block device translator */ - if ( ( rc = block_translate ( data, buffer, len ) ) != 0 ) { - DBGC ( http, "HTTP %p could not insert block translator: %s\n", - http, strerror ( rc ) ); - goto err_translate; - } - - return 0; - - err_translate: - intf_restart ( data, rc ); - err_open: - return rc; -} - -/** - * Read block device capacity - * - * @v control Control interface - * @v data Data interface - * @ret rc Return status code - */ -int http_block_read_capacity ( struct http_transaction *http, - struct interface *data ) { - int rc; - - /* Start a HEAD request to retrieve the capacity */ - if ( ( rc = http_open ( data, &http_head, http->uri, NULL, - NULL ) ) != 0 ) - goto err_open; - - /* Insert block device translator */ - if ( ( rc = block_translate ( data, UNULL, HTTP_BLKSIZE ) ) != 0 ) { - DBGC ( http, "HTTP %p could not insert block translator: %s\n", - http, strerror ( rc ) ); - goto err_translate; - } - - return 0; - - err_translate: - intf_restart ( data, rc ); - err_open: - return rc; -} - -/** - * Describe device in ACPI table - * - * @v http HTTP transaction - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -int http_acpi_describe ( struct http_transaction *http, - struct acpi_description_header *acpi, size_t len ) { - - DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", - http ); - ( void ) acpi; - ( void ) len; - return 0; -} diff --git a/qemu/roms/ipxe/src/net/tcp/httpconn.c b/qemu/roms/ipxe/src/net/tcp/httpconn.c deleted file mode 100644 index 7e4877b7b..000000000 --- a/qemu/roms/ipxe/src/net/tcp/httpconn.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) connection management - * - */ - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/tcpip.h> -#include <ipxe/uri.h> -#include <ipxe/timer.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/pool.h> -#include <ipxe/http.h> - -/** HTTP pooled connection expiry time */ -#define HTTP_CONN_EXPIRY ( 10 * TICKS_PER_SEC ) - -/** HTTP connection pool */ -static LIST_HEAD ( http_connection_pool ); - -/** - * Identify HTTP scheme - * - * @v uri URI - * @ret scheme HTTP scheme, or NULL - */ -static struct http_scheme * http_scheme ( struct uri *uri ) { - struct http_scheme *scheme; - - /* Sanity check */ - if ( ! uri->scheme ) - return NULL; - - /* Identify scheme */ - for_each_table_entry ( scheme, HTTP_SCHEMES ) { - if ( strcmp ( uri->scheme, scheme->name ) == 0 ) - return scheme; - } - - return NULL; -} - -/** - * Free HTTP connection - * - * @v refcnt Reference count - */ -static void http_conn_free ( struct refcnt *refcnt ) { - struct http_connection *conn = - container_of ( refcnt, struct http_connection, refcnt ); - - /* Free connection */ - uri_put ( conn->uri ); - free ( conn ); -} - -/** - * Close HTTP connection - * - * @v conn HTTP connection - * @v rc Reason for close - */ -static void http_conn_close ( struct http_connection *conn, int rc ) { - - /* Remove from connection pool, if applicable */ - pool_del ( &conn->pool ); - - /* Shut down interfaces */ - intf_shutdown ( &conn->socket, rc ); - intf_shutdown ( &conn->xfer, rc ); - if ( rc == 0 ) { - DBGC2 ( conn, "HTTPCONN %p closed %s://%s\n", - conn, conn->scheme->name, conn->uri->host ); - } else { - DBGC ( conn, "HTTPCONN %p closed %s://%s: %s\n", - conn, conn->scheme->name, conn->uri->host, - strerror ( rc ) ); - } -} - -/** - * Disconnect idle HTTP connection - * - * @v pool Pooled connection - */ -static void http_conn_expired ( struct pooled_connection *pool ) { - struct http_connection *conn = - container_of ( pool, struct http_connection, pool ); - - /* Close connection */ - http_conn_close ( conn, 0 /* Not an error to close idle connection */ ); -} - -/** - * Receive data from transport layer interface - * - * @v http HTTP connection - * @v iobuf I/O buffer - * @v meta Transfer metadata - * @ret rc Return status code - */ -static int http_conn_socket_deliver ( struct http_connection *conn, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - - /* Mark connection as alive */ - pool_alive ( &conn->pool ); - - /* Pass on to data transfer interface */ - return xfer_deliver ( &conn->xfer, iobuf, meta ); -} - -/** - * Close HTTP connection transport layer interface - * - * @v http HTTP connection - * @v rc Reason for close - */ -static void http_conn_socket_close ( struct http_connection *conn, int rc ) { - - /* If we are reopenable (i.e. we are a recycled connection - * from the connection pool, and we have received no data from - * the underlying socket since we were pooled), then suggest - * that the client should reopen the connection. - */ - if ( pool_is_reopenable ( &conn->pool ) ) - pool_reopen ( &conn->xfer ); - - /* Close the connection */ - http_conn_close ( conn, rc ); -} - -/** - * Recycle this connection after closing - * - * @v http HTTP connection - */ -static void http_conn_xfer_recycle ( struct http_connection *conn ) { - - /* Mark connection as recyclable */ - pool_recyclable ( &conn->pool ); - DBGC2 ( conn, "HTTPCONN %p keepalive enabled\n", conn ); -} - -/** - * Close HTTP connection data transfer interface - * - * @v conn HTTP connection - * @v rc Reason for close - */ -static void http_conn_xfer_close ( struct http_connection *conn, int rc ) { - - /* Add to the connection pool if keepalive is enabled and no - * error occurred. - */ - if ( ( rc == 0 ) && pool_is_recyclable ( &conn->pool ) ) { - intf_restart ( &conn->xfer, rc ); - pool_add ( &conn->pool, &http_connection_pool, - HTTP_CONN_EXPIRY ); - DBGC2 ( conn, "HTTPCONN %p pooled %s://%s\n", - conn, conn->scheme->name, conn->uri->host ); - return; - } - - /* Otherwise, close the connection */ - http_conn_close ( conn, rc ); -} - -/** HTTP connection socket interface operations */ -static struct interface_operation http_conn_socket_operations[] = { - INTF_OP ( xfer_deliver, struct http_connection *, - http_conn_socket_deliver ), - INTF_OP ( intf_close, struct http_connection *, - http_conn_socket_close ), -}; - -/** HTTP connection socket interface descriptor */ -static struct interface_descriptor http_conn_socket_desc = - INTF_DESC_PASSTHRU ( struct http_connection, socket, - http_conn_socket_operations, xfer ); - -/** HTTP connection data transfer interface operations */ -static struct interface_operation http_conn_xfer_operations[] = { - INTF_OP ( pool_recycle, struct http_connection *, - http_conn_xfer_recycle ), - INTF_OP ( intf_close, struct http_connection *, - http_conn_xfer_close ), -}; - -/** HTTP connection data transfer interface descriptor */ -static struct interface_descriptor http_conn_xfer_desc = - INTF_DESC_PASSTHRU ( struct http_connection, xfer, - http_conn_xfer_operations, socket ); - -/** - * Connect to an HTTP server - * - * @v xfer Data transfer interface - * @v uri Connection URI - * @ret rc Return status code - * - * HTTP connections are pooled. The caller should be prepared to - * receive a pool_reopen() message. - */ -int http_connect ( struct interface *xfer, struct uri *uri ) { - struct http_connection *conn; - struct http_scheme *scheme; - struct sockaddr_tcpip server; - struct interface *socket; - int rc; - - /* Identify scheme */ - scheme = http_scheme ( uri ); - if ( ! scheme ) - return -ENOTSUP; - - /* Sanity check */ - if ( ! uri->host ) - return -EINVAL; - - /* Look for a reusable connection in the pool */ - list_for_each_entry ( conn, &http_connection_pool, pool.list ) { - - /* Sanity checks */ - assert ( conn->uri != NULL ); - assert ( conn->uri->host != NULL ); - - /* Reuse connection, if possible */ - if ( ( scheme == conn->scheme ) && - ( strcmp ( uri->host, conn->uri->host ) == 0 ) ) { - - /* Remove from connection pool, stop timer, - * attach to parent interface, and return. - */ - pool_del ( &conn->pool ); - intf_plug_plug ( &conn->xfer, xfer ); - DBGC2 ( conn, "HTTPCONN %p reused %s://%s\n", - conn, conn->scheme->name, conn->uri->host ); - return 0; - } - } - - /* Allocate and initialise structure */ - conn = zalloc ( sizeof ( *conn ) ); - ref_init ( &conn->refcnt, http_conn_free ); - conn->uri = uri_get ( uri ); - conn->scheme = scheme; - intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt ); - intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt ); - pool_init ( &conn->pool, http_conn_expired, &conn->refcnt ); - - /* Open socket */ - memset ( &server, 0, sizeof ( server ) ); - server.st_port = htons ( uri_port ( uri, scheme->port ) ); - socket = &conn->socket; - if ( scheme->filter && - ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) ) - goto err_filter; - if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, - ( struct sockaddr * ) &server, - uri->host, NULL ) ) != 0 ) - goto err_open; - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &conn->xfer, xfer ); - ref_put ( &conn->refcnt ); - - DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn, - conn->scheme->name, conn->uri->host, ntohs ( server.st_port ) ); - return 0; - - err_open: - err_filter: - DBGC2 ( conn, "HTTPCONN %p could not create %s://%s: %s\n", - conn, conn->scheme->name, conn->uri->host, strerror ( rc ) ); - http_conn_close ( conn, rc ); - ref_put ( &conn->refcnt ); - return rc; -} diff --git a/qemu/roms/ipxe/src/net/tcp/httpcore.c b/qemu/roms/ipxe/src/net/tcp/httpcore.c deleted file mode 100644 index af3ca9780..000000000 --- a/qemu/roms/ipxe/src/net/tcp/httpcore.c +++ /dev/null @@ -1,1930 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) core functionality - * - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <byteswap.h> -#include <errno.h> -#include <ctype.h> -#include <assert.h> -#include <ipxe/uri.h> -#include <ipxe/refcnt.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/process.h> -#include <ipxe/retry.h> -#include <ipxe/timer.h> -#include <ipxe/linebuf.h> -#include <ipxe/xferbuf.h> -#include <ipxe/blockdev.h> -#include <ipxe/acpi.h> -#include <ipxe/version.h> -#include <ipxe/params.h> -#include <ipxe/profile.h> -#include <ipxe/vsprintf.h> -#include <ipxe/http.h> - -/* Disambiguate the various error causes */ -#define EACCES_401 __einfo_error ( EINFO_EACCES_401 ) -#define EINFO_EACCES_401 \ - __einfo_uniqify ( EINFO_EACCES, 0x01, "HTTP 401 Unauthorized" ) -#define EINVAL_STATUS __einfo_error ( EINFO_EINVAL_STATUS ) -#define EINFO_EINVAL_STATUS \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid status line" ) -#define EINVAL_HEADER __einfo_error ( EINFO_EINVAL_HEADER ) -#define EINFO_EINVAL_HEADER \ - __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid header" ) -#define EINVAL_CONTENT_LENGTH __einfo_error ( EINFO_EINVAL_CONTENT_LENGTH ) -#define EINFO_EINVAL_CONTENT_LENGTH \ - __einfo_uniqify ( EINFO_EINVAL, 0x03, "Invalid content length" ) -#define EINVAL_CHUNK_LENGTH __einfo_error ( EINFO_EINVAL_CHUNK_LENGTH ) -#define EINFO_EINVAL_CHUNK_LENGTH \ - __einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid chunk length" ) -#define EIO_OTHER __einfo_error ( EINFO_EIO_OTHER ) -#define EINFO_EIO_OTHER \ - __einfo_uniqify ( EINFO_EIO, 0x01, "Unrecognised HTTP response code" ) -#define EIO_CONTENT_LENGTH __einfo_error ( EINFO_EIO_CONTENT_LENGTH ) -#define EINFO_EIO_CONTENT_LENGTH \ - __einfo_uniqify ( EINFO_EIO, 0x02, "Content length mismatch" ) -#define EIO_4XX __einfo_error ( EINFO_EIO_4XX ) -#define EINFO_EIO_4XX \ - __einfo_uniqify ( EINFO_EIO, 0x04, "HTTP 4xx Client Error" ) -#define EIO_5XX __einfo_error ( EINFO_EIO_5XX ) -#define EINFO_EIO_5XX \ - __einfo_uniqify ( EINFO_EIO, 0x05, "HTTP 5xx Server Error" ) -#define ENOENT_404 __einfo_error ( EINFO_ENOENT_404 ) -#define EINFO_ENOENT_404 \ - __einfo_uniqify ( EINFO_ENOENT, 0x01, "HTTP 404 Not Found" ) -#define ENOTSUP_CONNECTION __einfo_error ( EINFO_ENOTSUP_CONNECTION ) -#define EINFO_ENOTSUP_CONNECTION \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported connection header" ) -#define ENOTSUP_TRANSFER __einfo_error ( EINFO_ENOTSUP_TRANSFER ) -#define EINFO_ENOTSUP_TRANSFER \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported transfer encoding" ) -#define EPERM_403 __einfo_error ( EINFO_EPERM_403 ) -#define EINFO_EPERM_403 \ - __einfo_uniqify ( EINFO_EPERM, 0x01, "HTTP 403 Forbidden" ) -#define EPROTO_UNSOLICITED __einfo_error ( EINFO_EPROTO_UNSOLICITED ) -#define EINFO_EPROTO_UNSOLICITED \ - __einfo_uniqify ( EINFO_EPROTO, 0x01, "Unsolicited data" ) - -/** Retry delay used when we cannot understand the Retry-After header */ -#define HTTP_RETRY_SECONDS 5 - -/** Receive profiler */ -static struct profiler http_rx_profiler __profiler = { .name = "http.rx" }; - -/** Data transfer profiler */ -static struct profiler http_xfer_profiler __profiler = { .name = "http.xfer" }; - -static struct http_state http_request; -static struct http_state http_headers; -static struct http_state http_trailers; -static struct http_transfer_encoding http_transfer_identity; - -/****************************************************************************** - * - * Methods - * - ****************************************************************************** - */ - -/** HTTP HEAD method */ -struct http_method http_head = { - .name = "HEAD", -}; - -/** HTTP GET method */ -struct http_method http_get = { - .name = "GET", -}; - -/** HTTP POST method */ -struct http_method http_post = { - .name = "POST", -}; - -/****************************************************************************** - * - * Utility functions - * - ****************************************************************************** - */ - -/** - * Handle received HTTP line-buffered data - * - * @v http HTTP transaction - * @v iobuf I/O buffer - * @v linebuf Line buffer - * @ret rc Return status code - */ -static int http_rx_linebuf ( struct http_transaction *http, - struct io_buffer *iobuf, - struct line_buffer *linebuf ) { - int consumed; - int rc; - - /* Buffer received line */ - consumed = line_buffer ( linebuf, iobuf->data, iob_len ( iobuf ) ); - if ( consumed < 0 ) { - rc = consumed; - DBGC ( http, "HTTP %p could not buffer line: %s\n", - http, strerror ( rc ) ); - return rc; - } - - /* Consume line */ - iob_pull ( iobuf, consumed ); - - return 0; -} - -/** - * Get HTTP response token - * - * @v line Line position - * @v value Token value to fill in (if any) - * @ret token Token, or NULL - */ -char * http_token ( char **line, char **value ) { - char *token; - char quote = '\0'; - char c; - - /* Avoid returning uninitialised data */ - if ( value ) - *value = NULL; - - /* Skip any initial whitespace */ - while ( isspace ( **line ) ) - (*line)++; - - /* Check for end of line and record token position */ - if ( ! **line ) - return NULL; - token = *line; - - /* Scan for end of token */ - while ( ( c = **line ) ) { - - /* Terminate if we hit an unquoted whitespace */ - if ( isspace ( c ) && ! quote ) - break; - - /* Terminate if we hit a closing quote */ - if ( c == quote ) - break; - - /* Check for value separator */ - if ( value && ( ! *value ) && ( c == '=' ) ) { - - /* Terminate key portion of token */ - *((*line)++) = '\0'; - - /* Check for quote character */ - c = **line; - if ( ( c == '"' ) || ( c == '\'' ) ) { - quote = c; - (*line)++; - } - - /* Record value portion of token */ - *value = *line; - - } else { - - /* Move to next character */ - (*line)++; - } - } - - /* Terminate token, if applicable */ - if ( c ) - *((*line)++) = '\0'; - - return token; -} - -/****************************************************************************** - * - * Transactions - * - ****************************************************************************** - */ - -/** - * Free HTTP transaction - * - * @v refcnt Reference count - */ -static void http_free ( struct refcnt *refcnt ) { - struct http_transaction *http = - container_of ( refcnt, struct http_transaction, refcnt ); - - empty_line_buffer ( &http->response.headers ); - empty_line_buffer ( &http->linebuf ); - uri_put ( http->uri ); - free ( http ); -} - -/** - * Close HTTP transaction - * - * @v http HTTP transaction - * @v rc Reason for close - */ -static void http_close ( struct http_transaction *http, int rc ) { - - /* Stop process */ - process_del ( &http->process ); - - /* Stop timer */ - stop_timer ( &http->timer ); - - /* Close all interfaces, allowing for the fact that the - * content-decoded and transfer-decoded interfaces may be - * connected to the same object. - */ - intf_shutdown ( &http->conn, rc ); - intf_nullify ( &http->transfer ); - intf_shutdown ( &http->content, rc ); - intf_shutdown ( &http->transfer, rc ); - intf_shutdown ( &http->xfer, rc ); -} - -/** - * Close HTTP transaction with error (even if none specified) - * - * @v http HTTP transaction - * @v rc Reason for close - */ -static void http_close_error ( struct http_transaction *http, int rc ) { - - /* Treat any close as an error */ - http_close ( http, ( rc ? rc : -EPIPE ) ); -} - -/** - * Reopen stale HTTP connection - * - * @v http HTTP transaction - */ -static void http_reopen ( struct http_transaction *http ) { - int rc; - - /* Close existing connection */ - intf_restart ( &http->conn, -ECANCELED ); - - /* Reopen connection */ - if ( ( rc = http_connect ( &http->conn, http->uri ) ) != 0 ) { - DBGC ( http, "HTTP %p could not reconnect: %s\n", - http, strerror ( rc ) ); - goto err_connect; - } - - /* Reset state */ - http->state = &http_request; - - /* Reschedule transmission process */ - process_add ( &http->process ); - - return; - - err_connect: - http_close ( http, rc ); -} - -/** - * Handle retry timer expiry - * - * @v timer Retry timer - * @v over Failure indicator - */ -static void http_expired ( struct retry_timer *timer, int over __unused ) { - struct http_transaction *http = - container_of ( timer, struct http_transaction, timer ); - - /* Reopen connection */ - http_reopen ( http ); -} - -/** - * HTTP transmit process - * - * @v http HTTP transaction - */ -static void http_step ( struct http_transaction *http ) { - int rc; - - /* Do nothing if we have nothing to transmit */ - if ( ! http->state->tx ) - return; - - /* Do nothing until connection is ready */ - if ( ! xfer_window ( &http->conn ) ) - return; - - /* Do nothing until data transfer interface is ready */ - if ( ! xfer_window ( &http->xfer ) ) - return; - - /* Transmit data */ - if ( ( rc = http->state->tx ( http ) ) != 0 ) - goto err; - - return; - - err: - http_close ( http, rc ); -} - -/** - * Handle received HTTP data - * - * @v http HTTP transaction - * @v iobuf I/O buffer - * @v meta Transfer metadata - * @ret rc Return status code - * - * This function takes ownership of the I/O buffer. - */ -static int http_conn_deliver ( struct http_transaction *http, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - int rc; - - /* Handle received data */ - profile_start ( &http_rx_profiler ); - while ( iobuf && iob_len ( iobuf ) ) { - - /* Sanity check */ - if ( ( ! http->state ) || ( ! http->state->rx ) ) { - DBGC ( http, "HTTP %p unexpected data\n", http ); - rc = -EPROTO_UNSOLICITED; - goto err; - } - - /* Receive (some) data */ - if ( ( rc = http->state->rx ( http, &iobuf ) ) != 0 ) - goto err; - } - - /* Free I/O buffer, if applicable */ - free_iob ( iobuf ); - - profile_stop ( &http_rx_profiler ); - return 0; - - err: - free_iob ( iobuf ); - http_close ( http, rc ); - return rc; -} - -/** - * Handle server connection close - * - * @v http HTTP transaction - * @v rc Reason for close - */ -static void http_conn_close ( struct http_transaction *http, int rc ) { - - /* Sanity checks */ - assert ( http->state != NULL ); - assert ( http->state->close != NULL ); - - /* Restart server connection interface */ - intf_restart ( &http->conn, rc ); - - /* Hand off to state-specific method */ - http->state->close ( http, rc ); -} - -/** - * Handle received content-decoded data - * - * @v http HTTP transaction - * @v iobuf I/O buffer - * @v meta Data transfer metadata - */ -static int http_content_deliver ( struct http_transaction *http, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - int rc; - - /* Ignore content if this is anything other than a successful - * transfer. - */ - if ( http->response.rc != 0 ) { - free_iob ( iobuf ); - return 0; - } - - /* Deliver to data transfer interface */ - profile_start ( &http_xfer_profiler ); - if ( ( rc = xfer_deliver ( &http->xfer, iob_disown ( iobuf ), - meta ) ) != 0 ) - return rc; - profile_stop ( &http_xfer_profiler ); - - return 0; -} - -/** - * Get underlying data transfer buffer - * - * @v http HTTP transaction - * @ret xferbuf Data transfer buffer, or NULL on error - */ -static struct xfer_buffer * -http_content_buffer ( struct http_transaction *http ) { - - /* Deny access to the data transfer buffer if this is anything - * other than a successful transfer. - */ - if ( http->response.rc != 0 ) - return NULL; - - /* Hand off to data transfer interface */ - return xfer_buffer ( &http->xfer ); -} - -/** - * Read from block device (when HTTP block device support is not present) - * - * @v http HTTP transaction - * @v data Data interface - * @v lba Starting logical block address - * @v count Number of logical blocks - * @v buffer Data buffer - * @v len Length of data buffer - * @ret rc Return status code - */ -__weak int http_block_read ( struct http_transaction *http __unused, - struct interface *data __unused, - uint64_t lba __unused, unsigned int count __unused, - userptr_t buffer __unused, size_t len __unused ) { - - return -ENOTSUP; -} - -/** - * Read block device capacity (when HTTP block device support is not present) - * - * @v control Control interface - * @v data Data interface - * @ret rc Return status code - */ -__weak int http_block_read_capacity ( struct http_transaction *http __unused, - struct interface *data __unused ) { - - return -ENOTSUP; -} - -/** - * Describe device in ACPI table (when HTTP block device support is not present) - * - * @v http HTTP transaction - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -__weak int http_acpi_describe ( struct http_transaction *http __unused, - struct acpi_description_header *acpi __unused, - size_t len __unused ) { - - return -ENOTSUP; -} - -/** HTTP data transfer interface operations */ -static struct interface_operation http_xfer_operations[] = { - INTF_OP ( block_read, struct http_transaction *, http_block_read ), - INTF_OP ( block_read_capacity, struct http_transaction *, - http_block_read_capacity ), - INTF_OP ( acpi_describe, struct http_transaction *, - http_acpi_describe ), - INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), - INTF_OP ( intf_close, struct http_transaction *, http_close ), -}; - -/** HTTP data transfer interface descriptor */ -static struct interface_descriptor http_xfer_desc = - INTF_DESC_PASSTHRU ( struct http_transaction, xfer, - http_xfer_operations, content ); - -/** HTTP content-decoded interface operations */ -static struct interface_operation http_content_operations[] = { - INTF_OP ( xfer_deliver, struct http_transaction *, - http_content_deliver ), - INTF_OP ( xfer_buffer, struct http_transaction *, http_content_buffer ), - INTF_OP ( intf_close, struct http_transaction *, http_close ), -}; - -/** HTTP content-decoded interface descriptor */ -static struct interface_descriptor http_content_desc = - INTF_DESC_PASSTHRU ( struct http_transaction, content, - http_content_operations, xfer ); - -/** HTTP transfer-decoded interface operations */ -static struct interface_operation http_transfer_operations[] = { - INTF_OP ( intf_close, struct http_transaction *, http_close ), -}; - -/** HTTP transfer-decoded interface descriptor */ -static struct interface_descriptor http_transfer_desc = - INTF_DESC_PASSTHRU ( struct http_transaction, transfer, - http_transfer_operations, conn ); - -/** HTTP server connection interface operations */ -static struct interface_operation http_conn_operations[] = { - INTF_OP ( xfer_deliver, struct http_transaction *, http_conn_deliver ), - INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), - INTF_OP ( pool_reopen, struct http_transaction *, http_reopen ), - INTF_OP ( intf_close, struct http_transaction *, http_conn_close ), -}; - -/** HTTP server connection interface descriptor */ -static struct interface_descriptor http_conn_desc = - INTF_DESC_PASSTHRU ( struct http_transaction, conn, - http_conn_operations, transfer ); - -/** HTTP process descriptor */ -static struct process_descriptor http_process_desc = - PROC_DESC_ONCE ( struct http_transaction, process, http_step ); - -/** - * Open HTTP transaction - * - * @v xfer Data transfer interface - * @v method Request method - * @v uri Request URI - * @v range Content range (if any) - * @v content Request content (if any) - * @ret rc Return status code - */ -int http_open ( struct interface *xfer, struct http_method *method, - struct uri *uri, struct http_request_range *range, - struct http_request_content *content ) { - struct http_transaction *http; - struct uri request_uri; - struct uri request_host; - size_t request_uri_len; - size_t request_host_len; - size_t content_len; - char *request_uri_string; - char *request_host_string; - void *content_data; - int rc; - - /* Calculate request URI length */ - memset ( &request_uri, 0, sizeof ( request_uri ) ); - request_uri.path = ( uri->path ? uri->path : "/" ); - request_uri.query = uri->query; - request_uri_len = - ( format_uri ( &request_uri, NULL, 0 ) + 1 /* NUL */); - - /* Calculate host name length */ - memset ( &request_host, 0, sizeof ( request_host ) ); - request_host.host = uri->host; - request_host.port = uri->port; - request_host_len = - ( format_uri ( &request_host, NULL, 0 ) + 1 /* NUL */ ); - - /* Calculate request content length */ - content_len = ( content ? content->len : 0 ); - - /* Allocate and initialise structure */ - http = zalloc ( sizeof ( *http ) + request_uri_len + request_host_len + - content_len ); - if ( ! http ) { - rc = -ENOMEM; - goto err_alloc; - } - request_uri_string = ( ( ( void * ) http ) + sizeof ( *http ) ); - request_host_string = ( request_uri_string + request_uri_len ); - content_data = ( request_host_string + request_host_len ); - format_uri ( &request_uri, request_uri_string, request_uri_len ); - format_uri ( &request_host, request_host_string, request_host_len ); - ref_init ( &http->refcnt, http_free ); - intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); - intf_init ( &http->content, &http_content_desc, &http->refcnt ); - intf_init ( &http->transfer, &http_transfer_desc, &http->refcnt ); - intf_init ( &http->conn, &http_conn_desc, &http->refcnt ); - intf_plug_plug ( &http->transfer, &http->content ); - process_init ( &http->process, &http_process_desc, &http->refcnt ); - timer_init ( &http->timer, http_expired, &http->refcnt ); - http->uri = uri_get ( uri ); - http->request.method = method; - http->request.uri = request_uri_string; - http->request.host = request_host_string; - if ( range ) { - memcpy ( &http->request.range, range, - sizeof ( http->request.range ) ); - } - if ( content ) { - http->request.content.type = content->type; - http->request.content.data = content_data; - http->request.content.len = content_len; - memcpy ( content_data, content->data, content_len ); - } - http->state = &http_request; - DBGC2 ( http, "HTTP %p %s://%s%s\n", http, http->uri->scheme, - http->request.host, http->request.uri ); - - /* Open connection */ - if ( ( rc = http_connect ( &http->conn, uri ) ) != 0 ) { - DBGC ( http, "HTTP %p could not connect: %s\n", - http, strerror ( rc ) ); - goto err_connect; - } - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &http->xfer, xfer ); - ref_put ( &http->refcnt ); - return 0; - - err_connect: - http_close ( http, rc ); - ref_put ( &http->refcnt ); - err_alloc: - return rc; -} - -/** - * Handle successful transfer completion - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_transfer_complete ( struct http_transaction *http ) { - struct http_authentication *auth; - const char *location; - int rc; - - /* Keep connection alive if applicable */ - if ( http->response.flags & HTTP_RESPONSE_KEEPALIVE ) - pool_recycle ( &http->conn ); - - /* Restart server connection interface */ - intf_restart ( &http->conn, 0 ); - - /* No more data is expected */ - http->state = NULL; - - /* If transaction is successful, then close the - * transfer-decoded interface. The content encoding may - * choose whether or not to immediately terminate the - * transaction. - */ - if ( http->response.rc == 0 ) { - intf_shutdown ( &http->transfer, 0 ); - return 0; - } - - /* Perform redirection, if applicable */ - if ( ( location = http->response.location ) ) { - DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", - http, location ); - if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, - location ) ) != 0 ) { - DBGC ( http, "HTTP %p could not redirect: %s\n", - http, strerror ( rc ) ); - return rc; - } - http_close ( http, 0 ); - return 0; - } - - /* Fail unless a retry is permitted */ - if ( ! ( http->response.flags & HTTP_RESPONSE_RETRY ) ) - return http->response.rc; - - /* Perform authentication, if applicable */ - if ( ( auth = http->response.auth.auth ) ) { - http->request.auth.auth = auth; - DBGC2 ( http, "HTTP %p performing %s authentication\n", - http, auth->name ); - if ( ( rc = auth->authenticate ( http ) ) != 0 ) { - DBGC ( http, "HTTP %p could not authenticate: %s\n", - http, strerror ( rc ) ); - return rc; - } - } - - /* Restart content decoding interfaces (which may be attached - * to the same object). - */ - intf_nullify ( &http->content ); - intf_nullify ( &http->transfer ); - intf_restart ( &http->content, http->response.rc ); - intf_restart ( &http->transfer, http->response.rc ); - http->content.desc = &http_content_desc; - http->transfer.desc = &http_transfer_desc; - intf_plug_plug ( &http->transfer, &http->content ); - http->len = 0; - assert ( http->remaining == 0 ); - - /* Start timer to initiate retry */ - DBGC2 ( http, "HTTP %p retrying after %d seconds\n", - http, http->response.retry_after ); - start_timer_fixed ( &http->timer, - ( http->response.retry_after * TICKS_PER_SEC ) ); - return 0; -} - -/****************************************************************************** - * - * Requests - * - ****************************************************************************** - */ - -/** - * Construct HTTP request headers - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length, or negative error - */ -static int http_format_headers ( struct http_transaction *http, char *buf, - size_t len ) { - struct http_request_header *header; - size_t used; - size_t remaining; - char *line; - int value_len; - int rc; - - /* Construct request line */ - used = ssnprintf ( buf, len, "%s %s HTTP/1.1", - http->request.method->name, http->request.uri ); - if ( used < len ) - DBGC2 ( http, "HTTP %p TX %s\n", http, buf ); - used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - - /* Construct all headers */ - for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) { - - /* Determine header value length */ - value_len = header->format ( http, NULL, 0 ); - if ( value_len < 0 ) { - rc = value_len; - return rc; - } - - /* Skip zero-length headers */ - if ( ! value_len ) - continue; - - /* Construct header */ - line = ( buf + used ); - used += ssnprintf ( ( buf + used ), ( len - used ), "%s: ", - header->name ); - remaining = ( ( used < len ) ? ( len - used ) : 0 ); - used += header->format ( http, ( buf + used ), remaining ); - if ( used < len ) - DBGC2 ( http, "HTTP %p TX %s\n", http, line ); - used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - } - - /* Construct terminating newline */ - used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); - - return used; -} - -/** - * Construct HTTP "Host" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_host ( struct http_transaction *http, char *buf, - size_t len ) { - - /* Construct host URI */ - return snprintf ( buf, len, "%s", http->request.host ); -} - -/** HTTP "Host" header "*/ -struct http_request_header http_request_host __http_request_header = { - .name = "Host", - .format = http_format_host, -}; - -/** - * Construct HTTP "User-Agent" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_user_agent ( struct http_transaction *http __unused, - char *buf, size_t len ) { - - /* Construct user agent */ - return snprintf ( buf, len, "iPXE/%s", product_version ); -} - -/** HTTP "User-Agent" header */ -struct http_request_header http_request_user_agent __http_request_header = { - .name = "User-Agent", - .format = http_format_user_agent, -}; - -/** - * Construct HTTP "Connection" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_connection ( struct http_transaction *http __unused, - char *buf, size_t len ) { - - /* Always request keep-alive */ - return snprintf ( buf, len, "keep-alive" ); -} - -/** HTTP "Connection" header */ -struct http_request_header http_request_connection __http_request_header = { - .name = "Connection", - .format = http_format_connection, -}; - -/** - * Construct HTTP "Range" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_range ( struct http_transaction *http, - char *buf, size_t len ) { - - /* Construct range, if applicable */ - if ( http->request.range.len ) { - return snprintf ( buf, len, "bytes=%zd-%zd", - http->request.range.start, - ( http->request.range.start + - http->request.range.len - 1 ) ); - } else { - return 0; - } -} - -/** HTTP "Range" header */ -struct http_request_header http_request_range __http_request_header = { - .name = "Range", - .format = http_format_range, -}; - -/** - * Construct HTTP "Content-Type" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_content_type ( struct http_transaction *http, - char *buf, size_t len ) { - - /* Construct content type, if applicable */ - if ( http->request.content.type ) { - return snprintf ( buf, len, "%s", http->request.content.type ); - } else { - return 0; - } -} - -/** HTTP "Content-Type" header */ -struct http_request_header http_request_content_type __http_request_header = { - .name = "Content-Type", - .format = http_format_content_type, -}; - -/** - * Construct HTTP "Content-Length" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_content_length ( struct http_transaction *http, - char *buf, size_t len ) { - - /* Construct content length, if applicable */ - if ( http->request.content.len ) { - return snprintf ( buf, len, "%zd", http->request.content.len ); - } else { - return 0; - } -} - -/** HTTP "Content-Length" header */ -struct http_request_header http_request_content_length __http_request_header = { - .name = "Content-Length", - .format = http_format_content_length, -}; - -/** - * Construct HTTP "Accept-Encoding" header - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_accept_encoding ( struct http_transaction *http, - char *buf, size_t len ) { - struct http_content_encoding *encoding; - const char *sep = ""; - size_t used = 0; - - /* Construct list of content encodings */ - for_each_table_entry ( encoding, HTTP_CONTENT_ENCODINGS ) { - if ( encoding->supported && ( ! encoding->supported ( http ) ) ) - continue; - used += ssnprintf ( ( buf + used ), ( len - used ), - "%s%s", sep, encoding->name ); - sep = ", "; - } - - return used; -} - -/** HTTP "Accept-Encoding" header */ -struct http_request_header http_request_accept_encoding __http_request_header ={ - .name = "Accept-Encoding", - .format = http_format_accept_encoding, -}; - -/** - * Transmit request - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_tx_request ( struct http_transaction *http ) { - struct io_buffer *iobuf; - int len; - int check_len; - int rc; - - /* Calculate request length */ - len = http_format_headers ( http, NULL, 0 ); - if ( len < 0 ) { - rc = len; - DBGC ( http, "HTTP %p could not construct request: %s\n", - http, strerror ( rc ) ); - goto err_len; - } - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( len + 1 /* NUL */ + http->request.content.len ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Construct request */ - check_len = http_format_headers ( http, iob_put ( iobuf, len ), - ( len + 1 /* NUL */ ) ); - assert ( check_len == len ); - memcpy ( iob_put ( iobuf, http->request.content.len ), - http->request.content.data, http->request.content.len ); - - /* Deliver request */ - if ( ( rc = xfer_deliver_iob ( &http->conn, - iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( http, "HTTP %p could not deliver request: %s\n", - http, strerror ( rc ) ); - goto err_deliver; - } - - /* Clear any previous response */ - empty_line_buffer ( &http->response.headers ); - memset ( &http->response, 0, sizeof ( http->response ) ); - - /* Move to response headers state */ - http->state = &http_headers; - - return 0; - - err_deliver: - free_iob ( iobuf ); - err_alloc: - err_len: - return rc; -} - -/** HTTP request state */ -static struct http_state http_request = { - .tx = http_tx_request, - .close = http_close_error, -}; - -/****************************************************************************** - * - * Response headers - * - ****************************************************************************** - */ - -/** - * Parse HTTP status line - * - * @v http HTTP transaction - * @v line Status line - * @ret rc Return status code - */ -static int http_parse_status ( struct http_transaction *http, char *line ) { - char *endp; - char *version; - char *vernum; - char *status; - int response_rc; - - DBGC2 ( http, "HTTP %p RX %s\n", http, line ); - - /* Parse HTTP version */ - version = http_token ( &line, NULL ); - if ( ( ! version ) || ( strncmp ( version, "HTTP/", 5 ) != 0 ) ) { - DBGC ( http, "HTTP %p malformed version \"%s\"\n", http, line ); - return -EINVAL_STATUS; - } - - /* Keepalive is enabled by default for anything newer than HTTP/1.0 */ - vernum = ( version + 5 /* "HTTP/" (presence already checked) */ ); - if ( vernum[0] == '0' ) { - /* HTTP/0.x : keepalive not enabled by default */ - } else if ( strncmp ( vernum, "1.0", 3 ) == 0 ) { - /* HTTP/1.0 : keepalive not enabled by default */ - } else { - /* HTTP/1.1 or newer: keepalive enabled by default */ - http->response.flags |= HTTP_RESPONSE_KEEPALIVE; - } - - /* Parse status code */ - status = line; - http->response.status = strtoul ( status, &endp, 10 ); - if ( *endp != ' ' ) { - DBGC ( http, "HTTP %p malformed status code \"%s\"\n", - http, status ); - return -EINVAL_STATUS; - } - - /* Convert HTTP status code to iPXE return status code */ - if ( status[0] == '2' ) { - /* 2xx Success */ - response_rc = 0; - } else if ( status[0] == '3' ) { - /* 3xx Redirection */ - response_rc = -EXDEV; - } else if ( http->response.status == 401 ) { - /* 401 Unauthorized */ - response_rc = -EACCES_401; - } else if ( http->response.status == 403 ) { - /* 403 Forbidden */ - response_rc = -EPERM_403; - } else if ( http->response.status == 404 ) { - /* 404 Not Found */ - response_rc = -ENOENT_404; - } else if ( status[0] == '4' ) { - /* 4xx Client Error (not already specified) */ - response_rc = -EIO_4XX; - } else if ( status[0] == '5' ) { - /* 5xx Server Error */ - response_rc = -EIO_5XX; - } else { - /* Unrecognised */ - response_rc = -EIO_OTHER; - } - http->response.rc = response_rc; - - return 0; -} - -/** - * Parse HTTP header - * - * @v http HTTP transaction - * @v line Header line - * @ret rc Return status code - */ -static int http_parse_header ( struct http_transaction *http, char *line ) { - struct http_response_header *header; - char *name = line; - char *sep; - - DBGC2 ( http, "HTTP %p RX %s\n", http, line ); - - /* Extract header name */ - sep = strstr ( line, ": " ); - if ( ! sep ) { - DBGC ( http, "HTTP %p malformed header \"%s\"\n", http, line ); - return -EINVAL_HEADER; - } - *sep = '\0'; - line = ( sep + 2 /* ": " */ ); - - /* Process header, if recognised */ - for_each_table_entry ( header, HTTP_RESPONSE_HEADERS ) { - if ( strcasecmp ( name, header->name ) == 0 ) - return header->parse ( http, line ); - } - - /* Unrecognised headers should be ignored */ - return 0; -} - -/** - * Parse HTTP response headers - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_parse_headers ( struct http_transaction *http ) { - char *line; - char *next; - int rc; - - /* Get status line */ - line = http->response.headers.data; - assert ( line != NULL ); - next = ( line + strlen ( line ) + 1 /* NUL */ ); - - /* Parse status line */ - if ( ( rc = http_parse_status ( http, line ) ) != 0 ) - return rc; - - /* Process header lines */ - while ( 1 ) { - - /* Move to next line */ - line = next; - next = ( line + strlen ( line ) + 1 /* NUL */ ); - - /* Stop on terminating blank line */ - if ( ! line[0] ) - return 0; - - /* Process header line */ - if ( ( rc = http_parse_header ( http, line ) ) != 0 ) - return rc; - } -} - -/** - * Parse HTTP "Location" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_location ( struct http_transaction *http, char *line ) { - - /* Store location */ - http->response.location = line; - return 0; -} - -/** HTTP "Location" header */ -struct http_response_header http_response_location __http_response_header = { - .name = "Location", - .parse = http_parse_location, -}; - -/** - * Parse HTTP "Transfer-Encoding" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_transfer_encoding ( struct http_transaction *http, - char *line ) { - struct http_transfer_encoding *encoding; - - /* Check for known transfer encodings */ - for_each_table_entry ( encoding, HTTP_TRANSFER_ENCODINGS ) { - if ( strcasecmp ( line, encoding->name ) == 0 ) { - http->response.transfer.encoding = encoding; - return 0; - } - } - - DBGC ( http, "HTTP %p unrecognised Transfer-Encoding \"%s\"\n", - http, line ); - return -ENOTSUP_TRANSFER; -} - -/** HTTP "Transfer-Encoding" header */ -struct http_response_header -http_response_transfer_encoding __http_response_header = { - .name = "Transfer-Encoding", - .parse = http_parse_transfer_encoding, -}; - -/** - * Parse HTTP "Connection" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_connection ( struct http_transaction *http, char *line ) { - - /* Check for known connection intentions */ - if ( strcasecmp ( line, "keep-alive" ) == 0 ) { - http->response.flags |= HTTP_RESPONSE_KEEPALIVE; - return 0; - } - if ( strcasecmp ( line, "close" ) == 0 ) { - http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; - return 0; - } - - DBGC ( http, "HTTP %p unrecognised Connection \"%s\"\n", http, line ); - return -ENOTSUP_CONNECTION; -} - -/** HTTP "Connection" header */ -struct http_response_header http_response_connection __http_response_header = { - .name = "Connection", - .parse = http_parse_connection, -}; - -/** - * Parse HTTP "Content-Length" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_content_length ( struct http_transaction *http, - char *line ) { - char *endp; - - /* Parse length */ - http->response.content.len = strtoul ( line, &endp, 10 ); - if ( *endp != '\0' ) { - DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", - http, line ); - return -EINVAL_CONTENT_LENGTH; - } - - /* Record that we have a content length (since it may be zero) */ - http->response.flags |= HTTP_RESPONSE_CONTENT_LEN; - - return 0; -} - -/** HTTP "Content-Length" header */ -struct http_response_header -http_response_content_length __http_response_header = { - .name = "Content-Length", - .parse = http_parse_content_length, -}; - -/** - * Parse HTTP "Content-Encoding" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_content_encoding ( struct http_transaction *http, - char *line ) { - struct http_content_encoding *encoding; - - /* Check for known content encodings */ - for_each_table_entry ( encoding, HTTP_CONTENT_ENCODINGS ) { - if ( encoding->supported && ( ! encoding->supported ( http ) ) ) - continue; - if ( strcasecmp ( line, encoding->name ) == 0 ) { - http->response.content.encoding = encoding; - return 0; - } - } - - /* Some servers (e.g. Apache) have a habit of specifying - * unwarranted content encodings. For example, if Apache - * detects (via /etc/httpd/conf/magic) that a file's contents - * are gzip-compressed, it will set "Content-Encoding: x-gzip" - * regardless of the client's Accept-Encoding header. The - * only viable way to handle such servers is to treat unknown - * content encodings as equivalent to "identity". - */ - DBGC ( http, "HTTP %p unrecognised Content-Encoding \"%s\"\n", - http, line ); - return 0; -} - -/** HTTP "Content-Encoding" header */ -struct http_response_header -http_response_content_encoding __http_response_header = { - .name = "Content-Encoding", - .parse = http_parse_content_encoding, -}; - -/** - * Parse HTTP "Retry-After" header - * - * @v http HTTP transaction - * @v line Remaining header line - * @ret rc Return status code - */ -static int http_parse_retry_after ( struct http_transaction *http, - char *line ) { - char *endp; - - /* Try to parse value as a simple number of seconds */ - http->response.retry_after = strtoul ( line, &endp, 10 ); - if ( *endp != '\0' ) { - /* For any value which is not a simple number of - * seconds (e.g. a full HTTP date), just retry after a - * fixed delay, since we don't have code able to parse - * full HTTP dates. - */ - http->response.retry_after = HTTP_RETRY_SECONDS; - DBGC ( http, "HTTP %p cannot understand Retry-After \"%s\"; " - "using %d seconds\n", http, line, HTTP_RETRY_SECONDS ); - } - - /* Allow HTTP request to be retried after specified delay */ - http->response.flags |= HTTP_RESPONSE_RETRY; - - return 0; -} - -/** HTTP "Retry-After" header */ -struct http_response_header http_response_retry_after __http_response_header = { - .name = "Retry-After", - .parse = http_parse_retry_after, -}; - -/** - * Handle received HTTP headers - * - * @v http HTTP transaction - * @v iobuf I/O buffer (may be claimed) - * @ret rc Return status code - */ -static int http_rx_headers ( struct http_transaction *http, - struct io_buffer **iobuf ) { - struct http_transfer_encoding *transfer; - struct http_content_encoding *content; - char *line; - int rc; - - /* Buffer header line */ - if ( ( rc = http_rx_linebuf ( http, *iobuf, - &http->response.headers ) ) != 0 ) - return rc; - - /* Wait until we see the empty line marking end of headers */ - line = buffered_line ( &http->response.headers ); - if ( ( line == NULL ) || ( line[0] != '\0' ) ) - return 0; - - /* Process headers */ - if ( ( rc = http_parse_headers ( http ) ) != 0 ) - return rc; - - /* Initialise content encoding, if applicable */ - if ( ( content = http->response.content.encoding ) && - ( ( rc = content->init ( http ) ) != 0 ) ) { - DBGC ( http, "HTTP %p could not initialise %s content " - "encoding: %s\n", http, content->name, strerror ( rc ) ); - return rc; - } - - /* Presize receive buffer, if we have a content length */ - if ( http->response.content.len ) { - xfer_seek ( &http->transfer, http->response.content.len ); - xfer_seek ( &http->transfer, 0 ); - } - - /* Complete transfer if this is a HEAD request */ - if ( http->request.method == &http_head ) { - if ( ( rc = http_transfer_complete ( http ) ) != 0 ) - return rc; - return 0; - } - - /* Default to identity transfer encoding, if none specified */ - if ( ! http->response.transfer.encoding ) - http->response.transfer.encoding = &http_transfer_identity; - - /* Move to transfer encoding-specific data state */ - transfer = http->response.transfer.encoding; - http->state = &transfer->state; - - /* Initialise transfer encoding */ - if ( ( rc = transfer->init ( http ) ) != 0 ) { - DBGC ( http, "HTTP %p could not initialise %s transfer " - "encoding: %s\n", http, transfer->name, strerror ( rc )); - return rc; - } - - return 0; -} - -/** HTTP response headers state */ -static struct http_state http_headers = { - .rx = http_rx_headers, - .close = http_close_error, -}; - -/****************************************************************************** - * - * Identity transfer encoding - * - ****************************************************************************** - */ - -/** - * Initialise transfer encoding - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_init_transfer_identity ( struct http_transaction *http ) { - int rc; - - /* Complete transfer immediately if we have a zero content length */ - if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && - ( http->response.content.len == 0 ) && - ( ( rc = http_transfer_complete ( http ) ) != 0 ) ) - return rc; - - return 0; -} - -/** - * Handle received data - * - * @v http HTTP transaction - * @v iobuf I/O buffer (may be claimed) - * @ret rc Return status code - */ -static int http_rx_transfer_identity ( struct http_transaction *http, - struct io_buffer **iobuf ) { - size_t len = iob_len ( *iobuf ); - int rc; - - /* Update lengths */ - http->len += len; - - /* Fail if this transfer would overrun the expected content - * length (if any). - */ - if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && - ( http->len > http->response.content.len ) ) { - DBGC ( http, "HTTP %p content length overrun\n", http ); - return -EIO_CONTENT_LENGTH; - } - - /* Hand off to content encoding */ - if ( ( rc = xfer_deliver_iob ( &http->transfer, - iob_disown ( *iobuf ) ) ) != 0 ) - return rc; - - /* Complete transfer if we have received the expected content - * length (if any). - */ - if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && - ( http->len == http->response.content.len ) && - ( ( rc = http_transfer_complete ( http ) ) != 0 ) ) - return rc; - - return 0; -} - -/** - * Handle server connection close - * - * @v http HTTP transaction - * @v rc Reason for close - */ -static void http_close_transfer_identity ( struct http_transaction *http, - int rc ) { - - /* Fail if any error occurred */ - if ( rc != 0 ) - goto err; - - /* Fail if we have a content length (since we would have - * already closed the connection if we had received the - * correct content length). - */ - if ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) { - DBGC ( http, "HTTP %p content length underrun\n", http ); - rc = EIO_CONTENT_LENGTH; - goto err; - } - - /* Indicate that transfer is complete */ - if ( ( rc = http_transfer_complete ( http ) ) != 0 ) - goto err; - - return; - - err: - http_close ( http, rc ); -} - -/** Identity transfer encoding */ -static struct http_transfer_encoding http_transfer_identity = { - .name = "identity", - .init = http_init_transfer_identity, - .state = { - .rx = http_rx_transfer_identity, - .close = http_close_transfer_identity, - }, -}; - -/****************************************************************************** - * - * Chunked transfer encoding - * - ****************************************************************************** - */ - -/** - * Initialise transfer encoding - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_init_transfer_chunked ( struct http_transaction *http ) { - - /* Sanity checks */ - assert ( http->remaining == 0 ); - assert ( http->linebuf.len == 0 ); - - return 0; -} - -/** - * Handle received chunk length - * - * @v http HTTP transaction - * @v iobuf I/O buffer (may be claimed) - * @ret rc Return status code - */ -static int http_rx_chunk_len ( struct http_transaction *http, - struct io_buffer **iobuf ) { - char *line; - char *endp; - size_t len; - int rc; - - /* Receive into temporary line buffer */ - if ( ( rc = http_rx_linebuf ( http, *iobuf, &http->linebuf ) ) != 0 ) - return rc; - - /* Wait until we receive a non-empty line */ - line = buffered_line ( &http->linebuf ); - if ( ( line == NULL ) || ( line[0] == '\0' ) ) - return 0; - - /* Parse chunk length */ - http->remaining = strtoul ( line, &endp, 16 ); - if ( *endp != '\0' ) { - DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n", - http, line ); - return -EINVAL_CHUNK_LENGTH; - } - - /* Empty line buffer */ - empty_line_buffer ( &http->linebuf ); - - /* Update expected length */ - len = ( http->len + http->remaining ); - xfer_seek ( &http->transfer, len ); - xfer_seek ( &http->transfer, http->len ); - - /* If chunk length is zero, then move to response trailers state */ - if ( ! http->remaining ) - http->state = &http_trailers; - - return 0; -} - -/** - * Handle received chunk data - * - * @v http HTTP transaction - * @v iobuf I/O buffer (may be claimed) - * @ret rc Return status code - */ -static int http_rx_chunk_data ( struct http_transaction *http, - struct io_buffer **iobuf ) { - struct io_buffer *payload; - uint8_t *crlf; - size_t len; - int rc; - - /* In the common case of a final chunk in a packet which also - * includes the terminating CRLF, strip the terminating CRLF - * (which we would ignore anyway) and hence avoid - * unnecessarily copying the data. - */ - if ( iob_len ( *iobuf ) == ( http->remaining + 2 /* CRLF */ ) ) { - crlf = ( (*iobuf)->data + http->remaining ); - if ( ( crlf[0] == '\r' ) && ( crlf[1] == '\n' ) ) - iob_unput ( (*iobuf), 2 /* CRLF */ ); - } - len = iob_len ( *iobuf ); - - /* Use whole/partial buffer as applicable */ - if ( len <= http->remaining ) { - - /* Whole buffer is to be consumed: decrease remaining - * length and use original I/O buffer as payload. - */ - payload = iob_disown ( *iobuf ); - http->len += len; - http->remaining -= len; - - } else { - - /* Partial buffer is to be consumed: copy data to a - * temporary I/O buffer. - */ - payload = alloc_iob ( http->remaining ); - if ( ! payload ) { - rc = -ENOMEM; - goto err; - } - memcpy ( iob_put ( payload, http->remaining ), (*iobuf)->data, - http->remaining ); - iob_pull ( *iobuf, http->remaining ); - http->len += http->remaining; - http->remaining = 0; - } - - /* Hand off to content encoding */ - if ( ( rc = xfer_deliver_iob ( &http->transfer, - iob_disown ( payload ) ) ) != 0 ) - goto err; - - return 0; - - err: - assert ( payload == NULL ); - return rc; -} - -/** - * Handle received chunked data - * - * @v http HTTP transaction - * @v iobuf I/O buffer (may be claimed) - * @ret rc Return status code - */ -static int http_rx_transfer_chunked ( struct http_transaction *http, - struct io_buffer **iobuf ) { - - /* Handle as chunk length or chunk data as appropriate */ - if ( http->remaining ) { - return http_rx_chunk_data ( http, iobuf ); - } else { - return http_rx_chunk_len ( http, iobuf ); - } -} - -/** Chunked transfer encoding */ -struct http_transfer_encoding http_transfer_chunked __http_transfer_encoding = { - .name = "chunked", - .init = http_init_transfer_chunked, - .state = { - .rx = http_rx_transfer_chunked, - .close = http_close_error, - }, -}; - -/****************************************************************************** - * - * Response trailers - * - ****************************************************************************** - */ - -/** - * Handle received HTTP trailer - * - * @v http HTTP transaction - * @v iobuf I/O buffer (may be claimed) - * @ret rc Return status code - */ -static int http_rx_trailers ( struct http_transaction *http, - struct io_buffer **iobuf ) { - char *line; - int rc; - - /* Buffer trailer line */ - if ( ( rc = http_rx_linebuf ( http, *iobuf, &http->linebuf ) ) != 0 ) - return rc; - - /* Wait until we see the empty line marking end of trailers */ - line = buffered_line ( &http->linebuf ); - if ( ( line == NULL ) || ( line[0] != '\0' ) ) - return 0; - - /* Empty line buffer */ - empty_line_buffer ( &http->linebuf ); - - /* Transfer is complete */ - if ( ( rc = http_transfer_complete ( http ) ) != 0 ) - return rc; - - return 0; -} - -/** HTTP response trailers state */ -static struct http_state http_trailers = { - .rx = http_rx_trailers, - .close = http_close_error, -}; - -/****************************************************************************** - * - * Simple URI openers - * - ****************************************************************************** - */ - -/** - * Construct HTTP parameter list - * - * @v params Parameter list - * @v buf Buffer to contain HTTP POST parameters - * @v len Length of buffer - * @ret len Length of parameter list (excluding terminating NUL) - */ -static size_t http_params ( struct parameters *params, char *buf, size_t len ) { - struct parameter *param; - ssize_t remaining = len; - size_t frag_len; - - /* Add each parameter in the form "key=value", joined with "&" */ - len = 0; - for_each_param ( param, params ) { - - /* Add the "&", if applicable */ - if ( len ) { - if ( remaining > 0 ) - *buf = '&'; - buf++; - len++; - remaining--; - } - - /* URI-encode the key */ - frag_len = uri_encode ( param->key, 0, buf, remaining ); - buf += frag_len; - len += frag_len; - remaining -= frag_len; - - /* Add the "=" */ - if ( remaining > 0 ) - *buf = '='; - buf++; - len++; - remaining--; - - /* URI-encode the value */ - frag_len = uri_encode ( param->value, 0, buf, remaining ); - buf += frag_len; - len += frag_len; - remaining -= frag_len; - } - - /* Ensure string is NUL-terminated even if no parameters are present */ - if ( remaining > 0 ) - *buf = '\0'; - - return len; -} - -/** - * Open HTTP transaction for simple GET URI - * - * @v xfer Data transfer interface - * @v uri Request URI - * @ret rc Return status code - */ -static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) { - - return http_open ( xfer, &http_get, uri, NULL, NULL ); -} - -/** - * Open HTTP transaction for simple POST URI - * - * @v xfer Data transfer interface - * @v uri Request URI - * @ret rc Return status code - */ -static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) { - struct parameters *params = uri->params; - struct http_request_content content; - void *data; - size_t len; - size_t check_len; - int rc; - - /* Calculate length of parameter list */ - len = http_params ( params, NULL, 0 ); - - /* Allocate temporary parameter list */ - data = zalloc ( len + 1 /* NUL */ ); - if ( ! data ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Construct temporary parameter list */ - check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); - assert ( check_len == len ); - - /* Construct request content */ - content.type = "application/x-www-form-urlencoded"; - content.data = data; - content.len = len; - - /* Open HTTP transaction */ - if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 ) - goto err_open; - - err_open: - free ( data ); - err_alloc: - return rc; -} - -/** - * Open HTTP transaction for simple URI - * - * @v xfer Data transfer interface - * @v uri Request URI - * @ret rc Return status code - */ -int http_open_uri ( struct interface *xfer, struct uri *uri ) { - - /* Open GET/POST URI as applicable */ - if ( uri->params ) { - return http_open_post_uri ( xfer, uri ); - } else { - return http_open_get_uri ( xfer, uri ); - } -} - -/* Drag in HTTP extensions */ -REQUIRING_SYMBOL ( http_open ); -REQUIRE_OBJECT ( config_http ); diff --git a/qemu/roms/ipxe/src/net/tcp/httpdigest.c b/qemu/roms/ipxe/src/net/tcp/httpdigest.c deleted file mode 100644 index 626dd7e9d..000000000 --- a/qemu/roms/ipxe/src/net/tcp/httpdigest.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Hyper Text Transfer Protocol (HTTP) Digest authentication - * - */ - -#include <stdio.h> -#include <errno.h> -#include <strings.h> -#include <ipxe/uri.h> -#include <ipxe/md5.h> -#include <ipxe/base16.h> -#include <ipxe/vsprintf.h> -#include <ipxe/http.h> - -/* Disambiguate the various error causes */ -#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME ) -#define EINFO_EACCES_USERNAME \ - __einfo_uniqify ( EINFO_EACCES, 0x01, \ - "No username available for Digest authentication" ) - -/** - * Initialise HTTP Digest - * - * @v ctx Digest context - * @v string Initial string - */ -static void http_digest_init ( struct md5_context *ctx ) { - - /* Initialise MD5 digest */ - digest_init ( &md5_algorithm, ctx ); -} - -/** - * Update HTTP Digest with new data - * - * @v ctx Digest context - * @v string String to append - */ -static void http_digest_update ( struct md5_context *ctx, const char *string ) { - static const char colon = ':'; - - /* Add (possibly colon-separated) field to MD5 digest */ - if ( ctx->len ) - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, string, strlen ( string ) ); -} - -/** - * Finalise HTTP Digest - * - * @v ctx Digest context - * @v out Buffer for digest output - * @v len Buffer length - */ -static void http_digest_final ( struct md5_context *ctx, char *out, - size_t len ) { - uint8_t digest[MD5_DIGEST_SIZE]; - - /* Finalise and base16-encode MD5 digest */ - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), out, len ); -} - -/** - * Perform HTTP Digest authentication - * - * @v http HTTP transaction - * @ret rc Return status code - */ -static int http_digest_authenticate ( struct http_transaction *http ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; - char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; - char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; - static const char md5sess[] = "MD5-sess"; - static const char md5[] = "MD5"; - struct md5_context ctx; - - /* Check for required response parameters */ - if ( ! rsp->realm ) { - DBGC ( http, "HTTP %p has no realm for Digest authentication\n", - http ); - return -EINVAL; - } - if ( ! rsp->nonce ) { - DBGC ( http, "HTTP %p has no nonce for Digest authentication\n", - http ); - return -EINVAL; - } - - /* Record username and password */ - if ( ! http->uri->user ) { - DBGC ( http, "HTTP %p has no username for Digest " - "authentication\n", http ); - return -EACCES_USERNAME; - } - req->username = http->uri->user; - req->password = ( http->uri->password ? http->uri->password : "" ); - - /* Handle quality of protection */ - if ( rsp->qop ) { - - /* Use "auth" in subsequent request */ - req->qop = "auth"; - - /* Generate a client nonce */ - snprintf ( req->cnonce, sizeof ( req->cnonce ), - "%08lx", random() ); - - /* Determine algorithm */ - req->algorithm = md5; - if ( rsp->algorithm && - ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) { - req->algorithm = md5sess; - } - } - - /* Generate HA1 */ - http_digest_init ( &ctx ); - http_digest_update ( &ctx, req->username ); - http_digest_update ( &ctx, rsp->realm ); - http_digest_update ( &ctx, req->password ); - http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); - if ( req->algorithm == md5sess ) { - http_digest_init ( &ctx ); - http_digest_update ( &ctx, ha1 ); - http_digest_update ( &ctx, rsp->nonce ); - http_digest_update ( &ctx, req->cnonce ); - http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); - } - - /* Generate HA2 */ - http_digest_init ( &ctx ); - http_digest_update ( &ctx, http->request.method->name ); - http_digest_update ( &ctx, http->request.uri ); - http_digest_final ( &ctx, ha2, sizeof ( ha2 ) ); - - /* Generate response */ - http_digest_init ( &ctx ); - http_digest_update ( &ctx, ha1 ); - http_digest_update ( &ctx, rsp->nonce ); - if ( req->qop ) { - http_digest_update ( &ctx, HTTP_DIGEST_NC ); - http_digest_update ( &ctx, req->cnonce ); - http_digest_update ( &ctx, req->qop ); - } - http_digest_update ( &ctx, ha2 ); - http_digest_final ( &ctx, req->response, sizeof ( req->response ) ); - - return 0; -} - -/** - * Construct HTTP "Authorization" header for Digest authentication - * - * @v http HTTP transaction - * @v buf Buffer - * @v len Length of buffer - * @ret len Length of header value, or negative error - */ -static int http_format_digest_auth ( struct http_transaction *http, - char *buf, size_t len ) { - struct http_request_auth *req = &http->request.auth; - struct http_response_auth *rsp = &http->response.auth; - size_t used = 0; - - /* Sanity checks */ - assert ( rsp->realm != NULL ); - assert ( rsp->nonce != NULL ); - assert ( req->username != NULL ); - if ( req->qop ) { - assert ( req->algorithm != NULL ); - assert ( req->cnonce[0] != '\0' ); - } - assert ( req->response[0] != '\0' ); - - /* Construct response */ - used += ssnprintf ( ( buf + used ), ( len - used ), - "realm=\"%s\", nonce=\"%s\", uri=\"%s\", " - "username=\"%s\"", rsp->realm, rsp->nonce, - http->request.uri, req->username ); - if ( rsp->opaque ) { - used += ssnprintf ( ( buf + used ), ( len - used ), - ", opaque=\"%s\"", rsp->opaque ); - } - if ( req->qop ) { - used += ssnprintf ( ( buf + used ), ( len - used ), - ", qop=%s, algorithm=%s, cnonce=\"%s\", " - "nc=" HTTP_DIGEST_NC, req->qop, - req->algorithm, req->cnonce ); - } - used += ssnprintf ( ( buf + used ), ( len - used ), - ", response=\"%s\"", req->response ); - - return used; -} - -/** HTTP Digest authentication scheme */ -struct http_authentication http_digest_auth __http_authentication = { - .name = "Digest", - .authenticate = http_digest_authenticate, - .format = http_format_digest_auth, -}; - -/* Drag in HTTP authentication support */ -REQUIRING_SYMBOL ( http_digest_auth ); -REQUIRE_OBJECT ( httpauth ); diff --git a/qemu/roms/ipxe/src/net/tcp/https.c b/qemu/roms/ipxe/src/net/tcp/https.c deleted file mode 100644 index e91000322..000000000 --- a/qemu/roms/ipxe/src/net/tcp/https.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * @file - * - * Secure Hyper Text Transfer Protocol (HTTPS) - * - */ - -#include <ipxe/open.h> -#include <ipxe/tls.h> -#include <ipxe/http.h> -#include <ipxe/features.h> - -FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 ); - -/** HTTPS URI opener */ -struct uri_opener https_uri_opener __uri_opener = { - .scheme = "https", - .open = http_open_uri, -}; - -/** HTTP URI scheme */ -struct http_scheme https_scheme __http_scheme = { - .name = "https", - .port = HTTPS_PORT, - .filter = add_tls, -}; diff --git a/qemu/roms/ipxe/src/net/tcp/iscsi.c b/qemu/roms/ipxe/src/net/tcp/iscsi.c deleted file mode 100644 index 019a4c14e..000000000 --- a/qemu/roms/ipxe/src/net/tcp/iscsi.c +++ /dev/null @@ -1,2122 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/vsprintf.h> -#include <ipxe/socket.h> -#include <ipxe/iobuf.h> -#include <ipxe/uri.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/scsi.h> -#include <ipxe/process.h> -#include <ipxe/uaccess.h> -#include <ipxe/tcpip.h> -#include <ipxe/settings.h> -#include <ipxe/features.h> -#include <ipxe/base16.h> -#include <ipxe/base64.h> -#include <ipxe/ibft.h> -#include <ipxe/iscsi.h> - -/** @file - * - * iSCSI protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); - -/* Disambiguate the various error causes */ -#define EACCES_INCORRECT_TARGET_USERNAME \ - __einfo_error ( EINFO_EACCES_INCORRECT_TARGET_USERNAME ) -#define EINFO_EACCES_INCORRECT_TARGET_USERNAME \ - __einfo_uniqify ( EINFO_EACCES, 0x01, "Incorrect target username" ) -#define EACCES_INCORRECT_TARGET_PASSWORD \ - __einfo_error ( EINFO_EACCES_INCORRECT_TARGET_PASSWORD ) -#define EINFO_EACCES_INCORRECT_TARGET_PASSWORD \ - __einfo_uniqify ( EINFO_EACCES, 0x02, "Incorrect target password" ) -#define EINVAL_ROOT_PATH_TOO_SHORT \ - __einfo_error ( EINFO_EINVAL_ROOT_PATH_TOO_SHORT ) -#define EINFO_EINVAL_ROOT_PATH_TOO_SHORT \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Root path too short" ) -#define EINVAL_BAD_CREDENTIAL_MIX \ - __einfo_error ( EINFO_EINVAL_BAD_CREDENTIAL_MIX ) -#define EINFO_EINVAL_BAD_CREDENTIAL_MIX \ - __einfo_uniqify ( EINFO_EINVAL, 0x02, "Bad credential mix" ) -#define EINVAL_NO_ROOT_PATH \ - __einfo_error ( EINFO_EINVAL_NO_ROOT_PATH ) -#define EINFO_EINVAL_NO_ROOT_PATH \ - __einfo_uniqify ( EINFO_EINVAL, 0x03, "No root path" ) -#define EINVAL_NO_TARGET_IQN \ - __einfo_error ( EINFO_EINVAL_NO_TARGET_IQN ) -#define EINFO_EINVAL_NO_TARGET_IQN \ - __einfo_uniqify ( EINFO_EINVAL, 0x04, "No target IQN" ) -#define EINVAL_NO_INITIATOR_IQN \ - __einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN ) -#define EINFO_EINVAL_NO_INITIATOR_IQN \ - __einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" ) -#define EIO_TARGET_UNAVAILABLE \ - __einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE ) -#define EINFO_EIO_TARGET_UNAVAILABLE \ - __einfo_uniqify ( EINFO_EIO, 0x01, "Target not currently operational" ) -#define EIO_TARGET_NO_RESOURCES \ - __einfo_error ( EINFO_EIO_TARGET_NO_RESOURCES ) -#define EINFO_EIO_TARGET_NO_RESOURCES \ - __einfo_uniqify ( EINFO_EIO, 0x02, "Target out of resources" ) -#define ENOTSUP_INITIATOR_STATUS \ - __einfo_error ( EINFO_ENOTSUP_INITIATOR_STATUS ) -#define EINFO_ENOTSUP_INITIATOR_STATUS \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported initiator status" ) -#define ENOTSUP_OPCODE \ - __einfo_error ( EINFO_ENOTSUP_OPCODE ) -#define EINFO_ENOTSUP_OPCODE \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported opcode" ) -#define ENOTSUP_DISCOVERY \ - __einfo_error ( EINFO_ENOTSUP_DISCOVERY ) -#define EINFO_ENOTSUP_DISCOVERY \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x03, "Discovery not supported" ) -#define ENOTSUP_TARGET_STATUS \ - __einfo_error ( EINFO_ENOTSUP_TARGET_STATUS ) -#define EINFO_ENOTSUP_TARGET_STATUS \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x04, "Unsupported target status" ) -#define ENOTSUP_NOP_IN \ - __einfo_error ( EINFO_ENOTSUP_NOP_IN ) -#define EINFO_ENOTSUP_NOP_IN \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x05, "Unsupported NOP-In received" ) -#define EPERM_INITIATOR_AUTHENTICATION \ - __einfo_error ( EINFO_EPERM_INITIATOR_AUTHENTICATION ) -#define EINFO_EPERM_INITIATOR_AUTHENTICATION \ - __einfo_uniqify ( EINFO_EPERM, 0x01, "Initiator authentication failed" ) -#define EPERM_INITIATOR_AUTHORISATION \ - __einfo_error ( EINFO_EPERM_INITIATOR_AUTHORISATION ) -#define EINFO_EPERM_INITIATOR_AUTHORISATION \ - __einfo_uniqify ( EINFO_EPERM, 0x02, "Initiator not authorised" ) -#define EPROTO_INVALID_CHAP_ALGORITHM \ - __einfo_error ( EINFO_EPROTO_INVALID_CHAP_ALGORITHM ) -#define EINFO_EPROTO_INVALID_CHAP_ALGORITHM \ - __einfo_uniqify ( EINFO_EPROTO, 0x01, "Invalid CHAP algorithm" ) -#define EPROTO_INVALID_CHAP_IDENTIFIER \ - __einfo_error ( EINFO_EPROTO_INVALID_CHAP_IDENTIFIER ) -#define EINFO_EPROTO_INVALID_CHAP_IDENTIFIER \ - __einfo_uniqify ( EINFO_EPROTO, 0x02, "Invalid CHAP identifier" ) -#define EPROTO_INVALID_LARGE_BINARY \ - __einfo_error ( EINFO_EPROTO_INVALID_LARGE_BINARY ) -#define EINFO_EPROTO_INVALID_LARGE_BINARY \ - __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary value" ) -#define EPROTO_INVALID_CHAP_RESPONSE \ - __einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE ) -#define EINFO_EPROTO_INVALID_CHAP_RESPONSE \ - __einfo_uniqify ( EINFO_EPROTO, 0x04, "Invalid CHAP response" ) -#define EPROTO_INVALID_KEY_VALUE_PAIR \ - __einfo_error ( EINFO_EPROTO_INVALID_KEY_VALUE_PAIR ) -#define EINFO_EPROTO_INVALID_KEY_VALUE_PAIR \ - __einfo_uniqify ( EINFO_EPROTO, 0x05, "Invalid key/value pair" ) -#define EPROTO_VALUE_REJECTED \ - __einfo_error ( EINFO_EPROTO_VALUE_REJECTED ) -#define EINFO_EPROTO_VALUE_REJECTED \ - __einfo_uniqify ( EINFO_EPROTO, 0x06, "Parameter rejected" ) - -static void iscsi_start_tx ( struct iscsi_session *iscsi ); -static void iscsi_start_login ( struct iscsi_session *iscsi ); -static void iscsi_start_data_out ( struct iscsi_session *iscsi, - unsigned int datasn ); - -/** - * Finish receiving PDU data into buffer - * - * @v iscsi iSCSI session - */ -static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) { - free ( iscsi->rx_buffer ); - iscsi->rx_buffer = NULL; -} - -/** - * Receive PDU data into buffer - * - * @v iscsi iSCSI session - * @v data Data to receive - * @v len Length of data - * @ret rc Return status code - * - * This can be used when the RX PDU type handler wishes to buffer up - * all received data and process the PDU as a single unit. The caller - * is repsonsible for calling iscsi_rx_buffered_data_done() after - * processing the data. - */ -static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi, - const void *data, size_t len ) { - - /* Allocate buffer on first call */ - if ( ! iscsi->rx_buffer ) { - iscsi->rx_buffer = malloc ( iscsi->rx_len ); - if ( ! iscsi->rx_buffer ) - return -ENOMEM; - } - - /* Copy data to buffer */ - assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len ); - memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len ); - - return 0; -} - -/** - * Free iSCSI session - * - * @v refcnt Reference counter - */ -static void iscsi_free ( struct refcnt *refcnt ) { - struct iscsi_session *iscsi = - container_of ( refcnt, struct iscsi_session, refcnt ); - - free ( iscsi->initiator_iqn ); - free ( iscsi->target_address ); - free ( iscsi->target_iqn ); - free ( iscsi->initiator_username ); - free ( iscsi->initiator_password ); - free ( iscsi->target_username ); - free ( iscsi->target_password ); - chap_finish ( &iscsi->chap ); - iscsi_rx_buffered_data_done ( iscsi ); - free ( iscsi->command ); - free ( iscsi ); -} - -/** - * Shut down iSCSI interface - * - * @v iscsi iSCSI session - * @v rc Reason for close - */ -static void iscsi_close ( struct iscsi_session *iscsi, int rc ) { - - /* A TCP graceful close is still an error from our point of view */ - if ( rc == 0 ) - rc = -ECONNRESET; - - DBGC ( iscsi, "iSCSI %p closed: %s\n", iscsi, strerror ( rc ) ); - - /* Stop transmission process */ - process_del ( &iscsi->process ); - - /* Shut down interfaces */ - intf_shutdown ( &iscsi->socket, rc ); - intf_shutdown ( &iscsi->control, rc ); - intf_shutdown ( &iscsi->data, rc ); -} - -/** - * Assign new iSCSI initiator task tag - * - * @v iscsi iSCSI session - */ -static void iscsi_new_itt ( struct iscsi_session *iscsi ) { - static uint16_t itt_idx; - - iscsi->itt = ( ISCSI_TAG_MAGIC | (++itt_idx) ); -} - -/** - * Open iSCSI transport-layer connection - * - * @v iscsi iSCSI session - * @ret rc Return status code - */ -static int iscsi_open_connection ( struct iscsi_session *iscsi ) { - struct sockaddr_tcpip target; - int rc; - - assert ( iscsi->tx_state == ISCSI_TX_IDLE ); - assert ( iscsi->rx_state == ISCSI_RX_BHS ); - assert ( iscsi->rx_offset == 0 ); - - /* Open socket */ - memset ( &target, 0, sizeof ( target ) ); - target.st_port = htons ( iscsi->target_port ); - if ( ( rc = xfer_open_named_socket ( &iscsi->socket, SOCK_STREAM, - ( struct sockaddr * ) &target, - iscsi->target_address, - NULL ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not open socket: %s\n", - iscsi, strerror ( rc ) ); - return rc; - } - - /* Enter security negotiation phase */ - iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE | - ISCSI_STATUS_STRINGS_SECURITY ); - if ( iscsi->target_username ) - iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_REQUIRED; - - /* Assign new ISID */ - iscsi->isid_iana_qual = ( random() & 0xffff ); - - /* Assign fresh initiator task tag */ - iscsi_new_itt ( iscsi ); - - /* Initiate login */ - iscsi_start_login ( iscsi ); - - return 0; -} - -/** - * Close iSCSI transport-layer connection - * - * @v iscsi iSCSI session - * @v rc Reason for close - * - * Closes the transport-layer connection and resets the session state - * ready to attempt a fresh login. - */ -static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) { - - /* Close all data transfer interfaces */ - intf_restart ( &iscsi->socket, rc ); - - /* Clear connection status */ - iscsi->status = 0; - - /* Reset TX and RX state machines */ - iscsi->tx_state = ISCSI_TX_IDLE; - iscsi->rx_state = ISCSI_RX_BHS; - iscsi->rx_offset = 0; - - /* Free any temporary dynamically allocated memory */ - chap_finish ( &iscsi->chap ); - iscsi_rx_buffered_data_done ( iscsi ); -} - -/** - * Mark iSCSI SCSI operation as complete - * - * @v iscsi iSCSI session - * @v rc Return status code - * @v rsp SCSI response, if any - * - * Note that iscsi_scsi_done() will not close the connection, and must - * therefore be called only when the internal state machines are in an - * appropriate state, otherwise bad things may happen on the next call - * to iscsi_scsi_command(). The general rule is to call - * iscsi_scsi_done() only at the end of receiving a PDU; at this point - * the TX and RX engines should both be idle. - */ -static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc, - struct scsi_rsp *rsp ) { - uint32_t itt = iscsi->itt; - - assert ( iscsi->tx_state == ISCSI_TX_IDLE ); - - /* Clear command */ - free ( iscsi->command ); - iscsi->command = NULL; - - /* Send SCSI response, if any */ - if ( rsp ) - scsi_response ( &iscsi->data, rsp ); - - /* Close SCSI command, if this is still the same command. (It - * is possible that the command interface has already been - * closed as a result of the SCSI response we sent.) - */ - if ( iscsi->itt == itt ) - intf_restart ( &iscsi->data, rc ); -} - -/**************************************************************************** - * - * iSCSI SCSI command issuing - * - */ - -/** - * Build iSCSI SCSI command BHS - * - * @v iscsi iSCSI session - * - * We don't currently support bidirectional commands (i.e. with both - * Data-In and Data-Out segments); these would require providing code - * to generate an AHS, and there doesn't seem to be any need for it at - * the moment. - */ -static void iscsi_start_command ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command; - - assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) ); - - /* Construct BHS and initiate transmission */ - iscsi_start_tx ( iscsi ); - command->opcode = ISCSI_OPCODE_SCSI_COMMAND; - command->flags = ( ISCSI_FLAG_FINAL | - ISCSI_COMMAND_ATTR_SIMPLE ); - if ( iscsi->command->data_in ) - command->flags |= ISCSI_COMMAND_FLAG_READ; - if ( iscsi->command->data_out ) - command->flags |= ISCSI_COMMAND_FLAG_WRITE; - /* lengths left as zero */ - memcpy ( &command->lun, &iscsi->command->lun, - sizeof ( command->lun ) ); - command->itt = htonl ( iscsi->itt ); - command->exp_len = htonl ( iscsi->command->data_in_len | - iscsi->command->data_out_len ); - command->cmdsn = htonl ( iscsi->cmdsn ); - command->expstatsn = htonl ( iscsi->statsn + 1 ); - memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb )); - DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n", - iscsi, SCSI_CDB_DATA ( command->cdb ), - ( iscsi->command->data_in ? "in" : "out" ), - ( iscsi->command->data_in ? - iscsi->command->data_in_len : - iscsi->command->data_out_len ) ); -} - -/** - * Receive data segment of an iSCSI SCSI response PDU - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - */ -static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi, - const void *data, size_t len, - size_t remaining ) { - struct iscsi_bhs_scsi_response *response - = &iscsi->rx_bhs.scsi_response; - struct scsi_rsp rsp; - uint32_t residual_count; - size_t data_len; - int rc; - - /* Buffer up the PDU data */ - if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not buffer SCSI response: %s\n", - iscsi, strerror ( rc ) ); - return rc; - } - if ( remaining ) - return 0; - - /* Parse SCSI response and discard buffer */ - memset ( &rsp, 0, sizeof ( rsp ) ); - rsp.status = response->status; - residual_count = ntohl ( response->residual_count ); - if ( response->flags & ISCSI_DATA_FLAG_OVERFLOW ) { - rsp.overrun = residual_count; - } else if ( response->flags & ISCSI_DATA_FLAG_UNDERFLOW ) { - rsp.overrun = -(residual_count); - } - data_len = ISCSI_DATA_LEN ( response->lengths ); - if ( data_len ) { - scsi_parse_sense ( ( iscsi->rx_buffer + 2 ), ( data_len - 2 ), - &rsp.sense ); - } - iscsi_rx_buffered_data_done ( iscsi ); - - /* Check for errors */ - if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE ) - return -EIO; - - /* Mark as completed */ - iscsi_scsi_done ( iscsi, 0, &rsp ); - return 0; -} - -/** - * Receive data segment of an iSCSI data-in PDU - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - */ -static int iscsi_rx_data_in ( struct iscsi_session *iscsi, - const void *data, size_t len, - size_t remaining ) { - struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in; - unsigned long offset; - - /* Copy data to data-in buffer */ - offset = ntohl ( data_in->offset ) + iscsi->rx_offset; - assert ( iscsi->command != NULL ); - assert ( iscsi->command->data_in ); - assert ( ( offset + len ) <= iscsi->command->data_in_len ); - copy_to_user ( iscsi->command->data_in, offset, data, len ); - - /* Wait for whole SCSI response to arrive */ - if ( remaining ) - return 0; - - /* Mark as completed if status is present */ - if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) { - assert ( ( offset + len ) == iscsi->command->data_in_len ); - assert ( data_in->flags & ISCSI_FLAG_FINAL ); - /* iSCSI cannot return an error status via a data-in */ - iscsi_scsi_done ( iscsi, 0, NULL ); - } - - return 0; -} - -/** - * Receive data segment of an iSCSI R2T PDU - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - */ -static int iscsi_rx_r2t ( struct iscsi_session *iscsi, - const void *data __unused, size_t len __unused, - size_t remaining __unused ) { - struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t; - - /* Record transfer parameters and trigger first data-out */ - iscsi->ttt = ntohl ( r2t->ttt ); - iscsi->transfer_offset = ntohl ( r2t->offset ); - iscsi->transfer_len = ntohl ( r2t->len ); - iscsi_start_data_out ( iscsi, 0 ); - - return 0; -} - -/** - * Build iSCSI data-out BHS - * - * @v iscsi iSCSI session - * @v datasn Data sequence number within the transfer - * - */ -static void iscsi_start_data_out ( struct iscsi_session *iscsi, - unsigned int datasn ) { - struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out; - unsigned long offset; - unsigned long remaining; - unsigned long len; - - /* We always send 512-byte Data-Out PDUs; this removes the - * need to worry about the target's MaxRecvDataSegmentLength. - */ - offset = datasn * 512; - remaining = iscsi->transfer_len - offset; - len = remaining; - if ( len > 512 ) - len = 512; - - /* Construct BHS and initiate transmission */ - iscsi_start_tx ( iscsi ); - data_out->opcode = ISCSI_OPCODE_DATA_OUT; - if ( len == remaining ) - data_out->flags = ( ISCSI_FLAG_FINAL ); - ISCSI_SET_LENGTHS ( data_out->lengths, 0, len ); - data_out->lun = iscsi->command->lun; - data_out->itt = htonl ( iscsi->itt ); - data_out->ttt = htonl ( iscsi->ttt ); - data_out->expstatsn = htonl ( iscsi->statsn + 1 ); - data_out->datasn = htonl ( datasn ); - data_out->offset = htonl ( iscsi->transfer_offset + offset ); - DBGC ( iscsi, "iSCSI %p start data out DataSN %#x len %#lx\n", - iscsi, datasn, len ); -} - -/** - * Complete iSCSI data-out PDU transmission - * - * @v iscsi iSCSI session - * - */ -static void iscsi_data_out_done ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out; - - /* If we haven't reached the end of the sequence, start - * sending the next data-out PDU. - */ - if ( ! ( data_out->flags & ISCSI_FLAG_FINAL ) ) - iscsi_start_data_out ( iscsi, ntohl ( data_out->datasn ) + 1 ); -} - -/** - * Send iSCSI data-out data segment - * - * @v iscsi iSCSI session - * @ret rc Return status code - */ -static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out; - struct io_buffer *iobuf; - unsigned long offset; - size_t len; - size_t pad_len; - - offset = ntohl ( data_out->offset ); - len = ISCSI_DATA_LEN ( data_out->lengths ); - pad_len = ISCSI_DATA_PAD_LEN ( data_out->lengths ); - - assert ( iscsi->command != NULL ); - assert ( iscsi->command->data_out ); - assert ( ( offset + len ) <= iscsi->command->data_out_len ); - - iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) ); - if ( ! iobuf ) - return -ENOMEM; - - copy_from_user ( iob_put ( iobuf, len ), - iscsi->command->data_out, offset, len ); - memset ( iob_put ( iobuf, pad_len ), 0, pad_len ); - - return xfer_deliver_iob ( &iscsi->socket, iobuf ); -} - -/** - * Receive data segment of an iSCSI NOP-In - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - */ -static int iscsi_rx_nop_in ( struct iscsi_session *iscsi, - const void *data __unused, size_t len __unused, - size_t remaining __unused ) { - struct iscsi_nop_in *nop_in = &iscsi->rx_bhs.nop_in; - - DBGC2 ( iscsi, "iSCSI %p received NOP-In\n", iscsi ); - - /* We don't currently have the ability to respond to NOP-Ins - * sent as ping requests, but we can happily accept NOP-Ins - * sent merely to update CmdSN. - */ - if ( nop_in->ttt != htonl ( ISCSI_TAG_RESERVED ) ) { - DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT " - "%08x\n", iscsi, ntohl ( nop_in->ttt ) ); - return -ENOTSUP_NOP_IN; - } - - return 0; -} - -/**************************************************************************** - * - * iSCSI login - * - */ - -/** - * Build iSCSI login request strings - * - * @v iscsi iSCSI session - * - * These are the initial set of strings sent in the first login - * request PDU. We want the following settings: - * - * HeaderDigest=None - * DataDigest=None - * MaxConnections is irrelevant; we make only one connection anyway [4] - * InitialR2T=Yes [1] - * ImmediateData is irrelevant; we never send immediate data [4] - * MaxRecvDataSegmentLength=8192 (default; we don't care) [3] - * MaxBurstLength=262144 (default; we don't care) [3] - * FirstBurstLength=262144 (default; we don't care) - * DefaultTime2Wait=0 [2] - * DefaultTime2Retain=0 [2] - * MaxOutstandingR2T=1 - * DataPDUInOrder=Yes - * DataSequenceInOrder=Yes - * ErrorRecoveryLevel=0 - * - * [1] InitialR2T has an OR resolution function, so the target may - * force us to use it. We therefore simplify our logic by always - * using it. - * - * [2] These ensure that we can safely start a new task once we have - * reconnected after a failure, without having to manually tidy up - * after the old one. - * - * [3] We are quite happy to use the RFC-defined default values for - * these parameters, but some targets (notably OpenSolaris) - * incorrectly assume a default value of zero, so we explicitly - * specify the default values. - * - * [4] We are quite happy to use the RFC-defined default values for - * these parameters, but some targets (notably a QNAP TS-639Pro) fail - * unless they are supplied, so we explicitly specify the default - * values. - */ -static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, - void *data, size_t len ) { - unsigned int used = 0; - const char *auth_method; - - if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) { - /* Default to allowing no authentication */ - auth_method = "None"; - /* If we have a credential to supply, permit CHAP */ - if ( iscsi->initiator_username ) - auth_method = "CHAP,None"; - /* If we have a credential to check, force CHAP */ - if ( iscsi->target_username ) - auth_method = "CHAP"; - used += ssnprintf ( data + used, len - used, - "InitiatorName=%s%c" - "TargetName=%s%c" - "SessionType=Normal%c" - "AuthMethod=%s%c", - iscsi->initiator_iqn, 0, - iscsi->target_iqn, 0, 0, - auth_method, 0 ); - } - - if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) { - used += ssnprintf ( data + used, len - used, "CHAP_A=5%c", 0 ); - } - - if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) { - char buf[ base16_encoded_len ( iscsi->chap.response_len ) + 1 ]; - assert ( iscsi->initiator_username != NULL ); - base16_encode ( iscsi->chap.response, iscsi->chap.response_len, - buf, sizeof ( buf ) ); - used += ssnprintf ( data + used, len - used, - "CHAP_N=%s%cCHAP_R=0x%s%c", - iscsi->initiator_username, 0, buf, 0 ); - } - - if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) { - size_t challenge_len = ( sizeof ( iscsi->chap_challenge ) - 1 ); - char buf[ base16_encoded_len ( challenge_len ) + 1 ]; - base16_encode ( ( iscsi->chap_challenge + 1 ), challenge_len, - buf, sizeof ( buf ) ); - used += ssnprintf ( data + used, len - used, - "CHAP_I=%d%cCHAP_C=0x%s%c", - iscsi->chap_challenge[0], 0, buf, 0 ); - } - - if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) { - used += ssnprintf ( data + used, len - used, - "HeaderDigest=None%c" - "DataDigest=None%c" - "MaxConnections=1%c" - "InitialR2T=Yes%c" - "ImmediateData=No%c" - "MaxRecvDataSegmentLength=8192%c" - "MaxBurstLength=262144%c" - "DefaultTime2Wait=0%c" - "DefaultTime2Retain=0%c" - "MaxOutstandingR2T=1%c" - "DataPDUInOrder=Yes%c" - "DataSequenceInOrder=Yes%c" - "ErrorRecoveryLevel=0%c", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - } - - return used; -} - -/** - * Build iSCSI login request BHS - * - * @v iscsi iSCSI session - */ -static void iscsi_start_login ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request; - int len; - - switch ( iscsi->status & ISCSI_LOGIN_CSG_MASK ) { - case ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION: - DBGC ( iscsi, "iSCSI %p entering security negotiation\n", - iscsi ); - break; - case ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION: - DBGC ( iscsi, "iSCSI %p entering operational negotiation\n", - iscsi ); - break; - default: - assert ( 0 ); - } - - /* Construct BHS and initiate transmission */ - iscsi_start_tx ( iscsi ); - request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST | - ISCSI_FLAG_IMMEDIATE ); - request->flags = ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) | - ISCSI_LOGIN_FLAG_TRANSITION ); - /* version_max and version_min left as zero */ - len = iscsi_build_login_request_strings ( iscsi, NULL, 0 ); - ISCSI_SET_LENGTHS ( request->lengths, 0, len ); - request->isid_iana_en = htonl ( ISCSI_ISID_IANA | - IANA_EN_FEN_SYSTEMS ); - request->isid_iana_qual = htons ( iscsi->isid_iana_qual ); - /* tsih left as zero */ - request->itt = htonl ( iscsi->itt ); - /* cid left as zero */ - request->cmdsn = htonl ( iscsi->cmdsn ); - request->expstatsn = htonl ( iscsi->statsn + 1 ); -} - -/** - * Complete iSCSI login request PDU transmission - * - * @v iscsi iSCSI session - * - */ -static void iscsi_login_request_done ( struct iscsi_session *iscsi ) { - - /* Clear any "strings to send" flags */ - iscsi->status &= ~ISCSI_STATUS_STRINGS_MASK; - - /* Free any dynamically allocated storage used for login */ - chap_finish ( &iscsi->chap ); -} - -/** - * Transmit data segment of an iSCSI login request PDU - * - * @v iscsi iSCSI session - * @ret rc Return status code - * - * For login requests, the data segment consists of the login strings. - */ -static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request; - struct io_buffer *iobuf; - size_t len; - size_t pad_len; - - len = ISCSI_DATA_LEN ( request->lengths ); - pad_len = ISCSI_DATA_PAD_LEN ( request->lengths ); - iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) ); - if ( ! iobuf ) - return -ENOMEM; - iob_put ( iobuf, len ); - iscsi_build_login_request_strings ( iscsi, iobuf->data, len ); - memset ( iob_put ( iobuf, pad_len ), 0, pad_len ); - - return xfer_deliver_iob ( &iscsi->socket, iobuf ); -} - -/** - * Decode large binary value - * - * @v encoded Encoded large binary value - * @v raw Raw data - * @v len Length of data buffer - * @ret len Length of raw data, or negative error - */ -static int iscsi_large_binary_decode ( const char *encoded, uint8_t *raw, - size_t len ) { - - /* Check for initial '0x' or '0b' and decode as appropriate */ - if ( *(encoded++) == '0' ) { - switch ( tolower ( *(encoded++) ) ) { - case 'x' : - return base16_decode ( encoded, raw, len ); - case 'b' : - return base64_decode ( encoded, raw, len ); - } - } - - return -EPROTO_INVALID_LARGE_BINARY; -} - -/** - * Handle iSCSI TargetAddress text value - * - * @v iscsi iSCSI session - * @v value TargetAddress value - * @ret rc Return status code - */ -static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi, - const char *value ) { - char *separator; - - DBGC ( iscsi, "iSCSI %p will redirect to %s\n", iscsi, value ); - - /* Replace target address */ - free ( iscsi->target_address ); - iscsi->target_address = strdup ( value ); - if ( ! iscsi->target_address ) - return -ENOMEM; - - /* Replace target port */ - iscsi->target_port = htons ( ISCSI_PORT ); - separator = strchr ( iscsi->target_address, ':' ); - if ( separator ) { - *separator = '\0'; - iscsi->target_port = strtoul ( ( separator + 1 ), NULL, 0 ); - } - - return 0; -} - -/** - * Handle iSCSI AuthMethod text value - * - * @v iscsi iSCSI session - * @v value AuthMethod value - * @ret rc Return status code - */ -static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, - const char *value ) { - - /* If server requests CHAP, send the CHAP_A string */ - if ( strcmp ( value, "CHAP" ) == 0 ) { - DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n", - iscsi ); - iscsi->status |= ( ISCSI_STATUS_STRINGS_CHAP_ALGORITHM | - ISCSI_STATUS_AUTH_FORWARD_REQUIRED ); - } - - return 0; -} - -/** - * Handle iSCSI CHAP_A text value - * - * @v iscsi iSCSI session - * @v value CHAP_A value - * @ret rc Return status code - */ -static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi, - const char *value ) { - - /* We only ever offer "5" (i.e. MD5) as an algorithm, so if - * the server responds with anything else it is a protocol - * violation. - */ - if ( strcmp ( value, "5" ) != 0 ) { - DBGC ( iscsi, "iSCSI %p got invalid CHAP algorithm \"%s\"\n", - iscsi, value ); - return -EPROTO_INVALID_CHAP_ALGORITHM; - } - - return 0; -} - -/** - * Handle iSCSI CHAP_I text value - * - * @v iscsi iSCSI session - * @v value CHAP_I value - * @ret rc Return status code - */ -static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, - const char *value ) { - unsigned int identifier; - char *endp; - int rc; - - /* The CHAP identifier is an integer value */ - identifier = strtoul ( value, &endp, 0 ); - if ( *endp != '\0' ) { - DBGC ( iscsi, "iSCSI %p saw invalid CHAP identifier \"%s\"\n", - iscsi, value ); - return -EPROTO_INVALID_CHAP_IDENTIFIER; - } - - /* Prepare for CHAP with MD5 */ - chap_finish ( &iscsi->chap ); - if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", - iscsi, strerror ( rc ) ); - return rc; - } - - /* Identifier and secret are the first two components of the - * challenge. - */ - chap_set_identifier ( &iscsi->chap, identifier ); - if ( iscsi->initiator_password ) { - chap_update ( &iscsi->chap, iscsi->initiator_password, - strlen ( iscsi->initiator_password ) ); - } - - return 0; -} - -/** - * Handle iSCSI CHAP_C text value - * - * @v iscsi iSCSI session - * @v value CHAP_C value - * @ret rc Return status code - */ -static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi, - const char *value ) { - uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */ - unsigned int i; - int len; - int rc; - - /* Process challenge */ - len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) ); - if ( len < 0 ) { - rc = len; - DBGC ( iscsi, "iSCSI %p invalid CHAP challenge \"%s\": %s\n", - iscsi, value, strerror ( rc ) ); - return rc; - } - chap_update ( &iscsi->chap, buf, len ); - - /* Build CHAP response */ - DBGC ( iscsi, "iSCSI %p sending CHAP response\n", iscsi ); - chap_respond ( &iscsi->chap ); - iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE; - - /* Send CHAP challenge, if applicable */ - if ( iscsi->target_username ) { - iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE; - /* Generate CHAP challenge data */ - for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) { - iscsi->chap_challenge[i] = random(); - } - } - - return 0; -} - -/** - * Handle iSCSI CHAP_N text value - * - * @v iscsi iSCSI session - * @v value CHAP_N value - * @ret rc Return status code - */ -static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi, - const char *value ) { - - /* The target username isn't actually involved at any point in - * the authentication process; it merely serves to identify - * which password the target is using to generate the CHAP - * response. We unnecessarily verify that the username is as - * expected, in order to provide mildly helpful diagnostics if - * the target is supplying the wrong username/password - * combination. - */ - if ( iscsi->target_username && - ( strcmp ( iscsi->target_username, value ) != 0 ) ) { - DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect " - "(wanted \"%s\")\n", - iscsi, value, iscsi->target_username ); - return -EACCES_INCORRECT_TARGET_USERNAME; - } - - return 0; -} - -/** - * Handle iSCSI CHAP_R text value - * - * @v iscsi iSCSI session - * @v value CHAP_R value - * @ret rc Return status code - */ -static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, - const char *value ) { - uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */ - int len; - int rc; - - /* Generate CHAP response for verification */ - chap_finish ( &iscsi->chap ); - if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", - iscsi, strerror ( rc ) ); - return rc; - } - chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] ); - if ( iscsi->target_password ) { - chap_update ( &iscsi->chap, iscsi->target_password, - strlen ( iscsi->target_password ) ); - } - chap_update ( &iscsi->chap, &iscsi->chap_challenge[1], - ( sizeof ( iscsi->chap_challenge ) - 1 ) ); - chap_respond ( &iscsi->chap ); - - /* Process response */ - len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) ); - if ( len < 0 ) { - rc = len; - DBGC ( iscsi, "iSCSI %p invalid CHAP response \"%s\": %s\n", - iscsi, value, strerror ( rc ) ); - return rc; - } - - /* Check CHAP response */ - if ( len != ( int ) iscsi->chap.response_len ) { - DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n", - iscsi ); - return -EPROTO_INVALID_CHAP_RESPONSE; - } - if ( memcmp ( buf, iscsi->chap.response, len ) != 0 ) { - DBGC ( iscsi, "iSCSI %p incorrect CHAP response \"%s\"\n", - iscsi, value ); - return -EACCES_INCORRECT_TARGET_PASSWORD; - } - - /* Mark session as authenticated */ - iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_OK; - - return 0; -} - -/** An iSCSI text string that we want to handle */ -struct iscsi_string_type { - /** String key - * - * This is the portion preceding the "=" sign, - * e.g. "InitiatorName", "CHAP_A", etc. - */ - const char *key; - /** Handle iSCSI string value - * - * @v iscsi iSCSI session - * @v value iSCSI string value - * @ret rc Return status code - */ - int ( * handle ) ( struct iscsi_session *iscsi, const char *value ); -}; - -/** iSCSI text strings that we want to handle */ -static struct iscsi_string_type iscsi_string_types[] = { - { "TargetAddress", iscsi_handle_targetaddress_value }, - { "AuthMethod", iscsi_handle_authmethod_value }, - { "CHAP_A", iscsi_handle_chap_a_value }, - { "CHAP_I", iscsi_handle_chap_i_value }, - { "CHAP_C", iscsi_handle_chap_c_value }, - { "CHAP_N", iscsi_handle_chap_n_value }, - { "CHAP_R", iscsi_handle_chap_r_value }, - { NULL, NULL } -}; - -/** - * Handle iSCSI string - * - * @v iscsi iSCSI session - * @v string iSCSI string (in "key=value" format) - * @ret rc Return status code - */ -static int iscsi_handle_string ( struct iscsi_session *iscsi, - const char *string ) { - struct iscsi_string_type *type; - const char *separator; - const char *value; - size_t key_len; - int rc; - - /* Find separator */ - separator = strchr ( string, '=' ); - if ( ! separator ) { - DBGC ( iscsi, "iSCSI %p malformed string %s\n", - iscsi, string ); - return -EPROTO_INVALID_KEY_VALUE_PAIR; - } - key_len = ( separator - string ); - value = ( separator + 1 ); - - /* Check for rejections. Since we send only non-rejectable - * values, any rejection is a fatal protocol error. - */ - if ( strcmp ( value, "Reject" ) == 0 ) { - DBGC ( iscsi, "iSCSI %p rejection: %s\n", iscsi, string ); - return -EPROTO_VALUE_REJECTED; - } - - /* Handle key/value pair */ - for ( type = iscsi_string_types ; type->key ; type++ ) { - if ( strncmp ( string, type->key, key_len ) != 0 ) - continue; - DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string ); - if ( ( rc = type->handle ( iscsi, value ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not handle %s: %s\n", - iscsi, string, strerror ( rc ) ); - return rc; - } - return 0; - } - DBGC ( iscsi, "iSCSI %p ignoring %s\n", iscsi, string ); - return 0; -} - -/** - * Handle iSCSI strings - * - * @v iscsi iSCSI session - * @v string iSCSI string buffer - * @v len Length of string buffer - * @ret rc Return status code - */ -static int iscsi_handle_strings ( struct iscsi_session *iscsi, - const char *strings, size_t len ) { - size_t string_len; - int rc; - - /* Handle each string in turn, taking care not to overrun the - * data buffer in case of badly-terminated data. - */ - while ( 1 ) { - string_len = ( strnlen ( strings, len ) + 1 ); - if ( string_len > len ) - break; - if ( ( rc = iscsi_handle_string ( iscsi, strings ) ) != 0 ) - return rc; - strings += string_len; - len -= string_len; - } - return 0; -} - -/** - * Convert iSCSI response status to return status code - * - * @v status_class iSCSI status class - * @v status_detail iSCSI status detail - * @ret rc Return status code - */ -static int iscsi_status_to_rc ( unsigned int status_class, - unsigned int status_detail ) { - switch ( status_class ) { - case ISCSI_STATUS_INITIATOR_ERROR : - switch ( status_detail ) { - case ISCSI_STATUS_INITIATOR_ERROR_AUTHENTICATION : - return -EPERM_INITIATOR_AUTHENTICATION; - case ISCSI_STATUS_INITIATOR_ERROR_AUTHORISATION : - return -EPERM_INITIATOR_AUTHORISATION; - case ISCSI_STATUS_INITIATOR_ERROR_NOT_FOUND : - case ISCSI_STATUS_INITIATOR_ERROR_REMOVED : - return -ENODEV; - default : - return -ENOTSUP_INITIATOR_STATUS; - } - case ISCSI_STATUS_TARGET_ERROR : - switch ( status_detail ) { - case ISCSI_STATUS_TARGET_ERROR_UNAVAILABLE: - return -EIO_TARGET_UNAVAILABLE; - case ISCSI_STATUS_TARGET_ERROR_NO_RESOURCES: - return -EIO_TARGET_NO_RESOURCES; - default: - return -ENOTSUP_TARGET_STATUS; - } - default : - return -EINVAL; - } -} - -/** - * Receive data segment of an iSCSI login response PDU - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - */ -static int iscsi_rx_login_response ( struct iscsi_session *iscsi, - const void *data, size_t len, - size_t remaining ) { - struct iscsi_bhs_login_response *response - = &iscsi->rx_bhs.login_response; - int rc; - - /* Buffer up the PDU data */ - if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n", - iscsi, strerror ( rc ) ); - return rc; - } - if ( remaining ) - return 0; - - /* Process string data and discard string buffer */ - if ( ( rc = iscsi_handle_strings ( iscsi, iscsi->rx_buffer, - iscsi->rx_len ) ) != 0 ) - return rc; - iscsi_rx_buffered_data_done ( iscsi ); - - /* Check for login redirection */ - if ( response->status_class == ISCSI_STATUS_REDIRECT ) { - DBGC ( iscsi, "iSCSI %p redirecting to new server\n", iscsi ); - iscsi_close_connection ( iscsi, 0 ); - if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not redirect: %s\n ", - iscsi, strerror ( rc ) ); - return rc; - } - return 0; - } - - /* Check for fatal errors */ - if ( response->status_class != 0 ) { - DBGC ( iscsi, "iSCSI login failure: class %02x detail %02x\n", - response->status_class, response->status_detail ); - rc = iscsi_status_to_rc ( response->status_class, - response->status_detail ); - return rc; - } - - /* Handle login transitions */ - if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) { - iscsi->status &= ~( ISCSI_STATUS_PHASE_MASK | - ISCSI_STATUS_STRINGS_MASK ); - switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) { - case ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION: - iscsi->status |= - ( ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE | - ISCSI_STATUS_STRINGS_OPERATIONAL ); - break; - case ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE: - iscsi->status |= ISCSI_STATUS_FULL_FEATURE_PHASE; - break; - default: - DBGC ( iscsi, "iSCSI %p got invalid response flags " - "%02x\n", iscsi, response->flags ); - return -EIO; - } - } - - /* Send next login request PDU if we haven't reached the full - * feature phase yet. - */ - if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) != - ISCSI_STATUS_FULL_FEATURE_PHASE ) { - iscsi_start_login ( iscsi ); - return 0; - } - - /* Check that target authentication was successful (if required) */ - if ( ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) && - ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_OK ) ) { - DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass " - "authentication\n", iscsi ); - return -EPROTO; - } - - /* Notify SCSI layer of window change */ - DBGC ( iscsi, "iSCSI %p entering full feature phase\n", iscsi ); - xfer_window_changed ( &iscsi->control ); - - return 0; -} - -/**************************************************************************** - * - * iSCSI to socket interface - * - */ - -/** - * Pause TX engine - * - * @v iscsi iSCSI session - */ -static void iscsi_tx_pause ( struct iscsi_session *iscsi ) { - process_del ( &iscsi->process ); -} - -/** - * Resume TX engine - * - * @v iscsi iSCSI session - */ -static void iscsi_tx_resume ( struct iscsi_session *iscsi ) { - process_add ( &iscsi->process ); -} - -/** - * Start up a new TX PDU - * - * @v iscsi iSCSI session - * - * This initiates the process of sending a new PDU. Only one PDU may - * be in transit at any one time. - */ -static void iscsi_start_tx ( struct iscsi_session *iscsi ) { - - assert ( iscsi->tx_state == ISCSI_TX_IDLE ); - - /* Initialise TX BHS */ - memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) ); - - /* Flag TX engine to start transmitting */ - iscsi->tx_state = ISCSI_TX_BHS; - - /* Start transmission process */ - iscsi_tx_resume ( iscsi ); -} - -/** - * Transmit nothing - * - * @v iscsi iSCSI session - * @ret rc Return status code - */ -static int iscsi_tx_nothing ( struct iscsi_session *iscsi __unused ) { - return 0; -} - -/** - * Transmit basic header segment of an iSCSI PDU - * - * @v iscsi iSCSI session - * @ret rc Return status code - */ -static int iscsi_tx_bhs ( struct iscsi_session *iscsi ) { - return xfer_deliver_raw ( &iscsi->socket, &iscsi->tx_bhs, - sizeof ( iscsi->tx_bhs ) ); -} - -/** - * Transmit data segment of an iSCSI PDU - * - * @v iscsi iSCSI session - * @ret rc Return status code - * - * Handle transmission of part of a PDU data segment. iscsi::tx_bhs - * will be valid when this is called. - */ -static int iscsi_tx_data ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; - - switch ( common->opcode & ISCSI_OPCODE_MASK ) { - case ISCSI_OPCODE_DATA_OUT: - return iscsi_tx_data_out ( iscsi ); - case ISCSI_OPCODE_LOGIN_REQUEST: - return iscsi_tx_login_request ( iscsi ); - default: - /* Nothing to send in other states */ - return 0; - } -} - -/** - * Complete iSCSI PDU transmission - * - * @v iscsi iSCSI session - * - * Called when a PDU has been completely transmitted and the TX state - * machine is about to enter the idle state. iscsi::tx_bhs will be - * valid for the just-completed PDU when this is called. - */ -static void iscsi_tx_done ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; - - /* Stop transmission process */ - iscsi_tx_pause ( iscsi ); - - switch ( common->opcode & ISCSI_OPCODE_MASK ) { - case ISCSI_OPCODE_DATA_OUT: - iscsi_data_out_done ( iscsi ); - break; - case ISCSI_OPCODE_LOGIN_REQUEST: - iscsi_login_request_done ( iscsi ); - break; - default: - /* No action */ - break; - } -} - -/** - * Transmit iSCSI PDU - * - * @v iscsi iSCSI session - * @v buf Temporary data buffer - * @v len Length of temporary data buffer - * - * Constructs data to be sent for the current TX state - */ -static void iscsi_tx_step ( struct iscsi_session *iscsi ) { - struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; - int ( * tx ) ( struct iscsi_session *iscsi ); - enum iscsi_tx_state next_state; - size_t tx_len; - int rc; - - /* Select fragment to transmit */ - while ( 1 ) { - switch ( iscsi->tx_state ) { - case ISCSI_TX_BHS: - tx = iscsi_tx_bhs; - tx_len = sizeof ( iscsi->tx_bhs ); - next_state = ISCSI_TX_AHS; - break; - case ISCSI_TX_AHS: - tx = iscsi_tx_nothing; - tx_len = 0; - next_state = ISCSI_TX_DATA; - break; - case ISCSI_TX_DATA: - tx = iscsi_tx_data; - tx_len = ISCSI_DATA_LEN ( common->lengths ); - next_state = ISCSI_TX_IDLE; - break; - case ISCSI_TX_IDLE: - /* Nothing to do; pause processing */ - iscsi_tx_pause ( iscsi ); - return; - default: - assert ( 0 ); - return; - } - - /* Check for window availability, if needed */ - if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) { - /* Cannot transmit at this point; pause - * processing and wait for window to reopen - */ - iscsi_tx_pause ( iscsi ); - return; - } - - /* Transmit data */ - if ( ( rc = tx ( iscsi ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not transmit: %s\n", - iscsi, strerror ( rc ) ); - /* Transmission errors are fatal */ - iscsi_close ( iscsi, rc ); - return; - } - - /* Move to next state */ - iscsi->tx_state = next_state; - - /* If we have moved to the idle state, mark - * transmission as complete - */ - if ( iscsi->tx_state == ISCSI_TX_IDLE ) - iscsi_tx_done ( iscsi ); - } -} - -/** iSCSI TX process descriptor */ -static struct process_descriptor iscsi_process_desc = - PROC_DESC ( struct iscsi_session, process, iscsi_tx_step ); - -/** - * Receive basic header segment of an iSCSI PDU - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - * - * This fills in iscsi::rx_bhs with the data from the BHS portion of - * the received PDU. - */ -static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data, - size_t len, size_t remaining __unused ) { - memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len ); - if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) { - DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#x\n", - iscsi, iscsi->rx_bhs.common.opcode, - ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) ); - } - return 0; -} - -/** - * Discard portion of an iSCSI PDU. - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - * - * This discards data from a portion of a received PDU. - */ -static int iscsi_rx_discard ( struct iscsi_session *iscsi __unused, - const void *data __unused, size_t len __unused, - size_t remaining __unused ) { - /* Do nothing */ - return 0; -} - -/** - * Receive data segment of an iSCSI PDU - * - * @v iscsi iSCSI session - * @v data Received data - * @v len Length of received data - * @v remaining Data remaining after this data - * @ret rc Return status code - * - * Handle processing of part of a PDU data segment. iscsi::rx_bhs - * will be valid when this is called. - */ -static int iscsi_rx_data ( struct iscsi_session *iscsi, const void *data, - size_t len, size_t remaining ) { - struct iscsi_bhs_common_response *response - = &iscsi->rx_bhs.common_response; - - /* Update cmdsn and statsn */ - iscsi->cmdsn = ntohl ( response->expcmdsn ); - iscsi->statsn = ntohl ( response->statsn ); - - switch ( response->opcode & ISCSI_OPCODE_MASK ) { - case ISCSI_OPCODE_LOGIN_RESPONSE: - return iscsi_rx_login_response ( iscsi, data, len, remaining ); - case ISCSI_OPCODE_SCSI_RESPONSE: - return iscsi_rx_scsi_response ( iscsi, data, len, remaining ); - case ISCSI_OPCODE_DATA_IN: - return iscsi_rx_data_in ( iscsi, data, len, remaining ); - case ISCSI_OPCODE_R2T: - return iscsi_rx_r2t ( iscsi, data, len, remaining ); - case ISCSI_OPCODE_NOP_IN: - return iscsi_rx_nop_in ( iscsi, data, len, remaining ); - default: - if ( remaining ) - return 0; - DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi, - response->opcode ); - return -ENOTSUP_OPCODE; - } -} - -/** - * Receive new data - * - * @v iscsi iSCSI session - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - * - * This handles received PDUs. The receive strategy is to fill in - * iscsi::rx_bhs with the contents of the BHS portion of the PDU, - * throw away any AHS portion, and then process each part of the data - * portion as it arrives. The data processing routine therefore - * always has a full copy of the BHS available, even for portions of - * the data in different packets to the BHS. - */ -static int iscsi_socket_deliver ( struct iscsi_session *iscsi, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct iscsi_bhs_common *common = &iscsi->rx_bhs.common; - int ( * rx ) ( struct iscsi_session *iscsi, const void *data, - size_t len, size_t remaining ); - enum iscsi_rx_state next_state; - size_t frag_len; - size_t remaining; - int rc; - - while ( 1 ) { - switch ( iscsi->rx_state ) { - case ISCSI_RX_BHS: - rx = iscsi_rx_bhs; - iscsi->rx_len = sizeof ( iscsi->rx_bhs ); - next_state = ISCSI_RX_AHS; - break; - case ISCSI_RX_AHS: - rx = iscsi_rx_discard; - iscsi->rx_len = 4 * ISCSI_AHS_LEN ( common->lengths ); - next_state = ISCSI_RX_DATA; - break; - case ISCSI_RX_DATA: - rx = iscsi_rx_data; - iscsi->rx_len = ISCSI_DATA_LEN ( common->lengths ); - next_state = ISCSI_RX_DATA_PADDING; - break; - case ISCSI_RX_DATA_PADDING: - rx = iscsi_rx_discard; - iscsi->rx_len = ISCSI_DATA_PAD_LEN ( common->lengths ); - next_state = ISCSI_RX_BHS; - break; - default: - assert ( 0 ); - rc = -EINVAL; - goto done; - } - - frag_len = iscsi->rx_len - iscsi->rx_offset; - if ( frag_len > iob_len ( iobuf ) ) - frag_len = iob_len ( iobuf ); - remaining = iscsi->rx_len - iscsi->rx_offset - frag_len; - if ( ( rc = rx ( iscsi, iobuf->data, frag_len, - remaining ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not process received " - "data: %s\n", iscsi, strerror ( rc ) ); - goto done; - } - - iscsi->rx_offset += frag_len; - iob_pull ( iobuf, frag_len ); - - /* If all the data for this state has not yet been - * received, stay in this state for now. - */ - if ( iscsi->rx_offset != iscsi->rx_len ) { - rc = 0; - goto done; - } - - iscsi->rx_state = next_state; - iscsi->rx_offset = 0; - } - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); - - /* Destroy session on error */ - if ( rc != 0 ) - iscsi_close ( iscsi, rc ); - - return rc; -} - -/** - * Handle redirection event - * - * @v iscsi iSCSI session - * @v type Location type - * @v args Remaining arguments depend upon location type - * @ret rc Return status code - */ -static int iscsi_vredirect ( struct iscsi_session *iscsi, int type, - va_list args ) { - va_list tmp; - struct sockaddr *peer; - - /* Intercept redirects to a LOCATION_SOCKET and record the IP - * address for the iBFT. This is a bit of a hack, but avoids - * inventing an ioctl()-style call to retrieve the socket - * address from a data-xfer interface. - */ - if ( type == LOCATION_SOCKET ) { - va_copy ( tmp, args ); - ( void ) va_arg ( tmp, int ); /* Discard "semantics" */ - peer = va_arg ( tmp, struct sockaddr * ); - memcpy ( &iscsi->target_sockaddr, peer, - sizeof ( iscsi->target_sockaddr ) ); - va_end ( tmp ); - } - - return xfer_vreopen ( &iscsi->socket, type, args ); -} - -/** iSCSI socket interface operations */ -static struct interface_operation iscsi_socket_operations[] = { - INTF_OP ( xfer_deliver, struct iscsi_session *, iscsi_socket_deliver ), - INTF_OP ( xfer_window_changed, struct iscsi_session *, - iscsi_tx_resume ), - INTF_OP ( xfer_vredirect, struct iscsi_session *, iscsi_vredirect ), - INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), -}; - -/** iSCSI socket interface descriptor */ -static struct interface_descriptor iscsi_socket_desc = - INTF_DESC ( struct iscsi_session, socket, iscsi_socket_operations ); - -/**************************************************************************** - * - * iSCSI command issuing - * - */ - -/** - * Check iSCSI flow-control window - * - * @v iscsi iSCSI session - * @ret len Length of window - */ -static size_t iscsi_scsi_window ( struct iscsi_session *iscsi ) { - - if ( ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) == - ISCSI_STATUS_FULL_FEATURE_PHASE ) && - ( iscsi->command == NULL ) ) { - /* We cannot handle concurrent commands */ - return 1; - } else { - return 0; - } -} - -/** - * Issue iSCSI SCSI command - * - * @v iscsi iSCSI session - * @v parent Parent interface - * @v command SCSI command - * @ret tag Command tag, or negative error - */ -static int iscsi_scsi_command ( struct iscsi_session *iscsi, - struct interface *parent, - struct scsi_cmd *command ) { - - /* This iSCSI implementation cannot handle multiple concurrent - * commands or commands arriving before login is complete. - */ - if ( iscsi_scsi_window ( iscsi ) == 0 ) { - DBGC ( iscsi, "iSCSI %p cannot handle concurrent commands\n", - iscsi ); - return -EOPNOTSUPP; - } - - /* Store command */ - iscsi->command = malloc ( sizeof ( *command ) ); - if ( ! iscsi->command ) - return -ENOMEM; - memcpy ( iscsi->command, command, sizeof ( *command ) ); - - /* Assign new ITT */ - iscsi_new_itt ( iscsi ); - - /* Start sending command */ - iscsi_start_command ( iscsi ); - - /* Attach to parent interface and return */ - intf_plug_plug ( &iscsi->data, parent ); - return iscsi->itt; -} - -/** iSCSI SCSI command-issuing interface operations */ -static struct interface_operation iscsi_control_op[] = { - INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ), - INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ), - INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), - INTF_OP ( acpi_describe, struct iscsi_session *, ibft_describe ), -}; - -/** iSCSI SCSI command-issuing interface descriptor */ -static struct interface_descriptor iscsi_control_desc = - INTF_DESC ( struct iscsi_session, control, iscsi_control_op ); - -/** - * Close iSCSI command - * - * @v iscsi iSCSI session - * @v rc Reason for close - */ -static void iscsi_command_close ( struct iscsi_session *iscsi, int rc ) { - - /* Restart interface */ - intf_restart ( &iscsi->data, rc ); - - /* Treat unsolicited command closures mid-command as fatal, - * because we have no code to handle partially-completed PDUs. - */ - if ( iscsi->command != NULL ) - iscsi_close ( iscsi, ( ( rc == 0 ) ? -ECANCELED : rc ) ); -} - -/** iSCSI SCSI command interface operations */ -static struct interface_operation iscsi_data_op[] = { - INTF_OP ( intf_close, struct iscsi_session *, iscsi_command_close ), -}; - -/** iSCSI SCSI command interface descriptor */ -static struct interface_descriptor iscsi_data_desc = - INTF_DESC ( struct iscsi_session, data, iscsi_data_op ); - -/**************************************************************************** - * - * Instantiator - * - */ - -/** iSCSI root path components (as per RFC4173) */ -enum iscsi_root_path_component { - RP_SERVERNAME = 0, - RP_PROTOCOL, - RP_PORT, - RP_LUN, - RP_TARGETNAME, - NUM_RP_COMPONENTS -}; - -/** iSCSI initiator IQN setting */ -const struct setting initiator_iqn_setting __setting ( SETTING_SANBOOT_EXTRA, - initiator-iqn ) = { - .name = "initiator-iqn", - .description = "iSCSI initiator name", - .tag = DHCP_ISCSI_INITIATOR_IQN, - .type = &setting_type_string, -}; - -/** iSCSI reverse username setting */ -const struct setting reverse_username_setting __setting ( SETTING_AUTH_EXTRA, - reverse-username ) = { - .name = "reverse-username", - .description = "Reverse user name", - .tag = DHCP_EB_REVERSE_USERNAME, - .type = &setting_type_string, -}; - -/** iSCSI reverse password setting */ -const struct setting reverse_password_setting __setting ( SETTING_AUTH_EXTRA, - reverse-password ) = { - .name = "reverse-password", - .description = "Reverse password", - .tag = DHCP_EB_REVERSE_PASSWORD, - .type = &setting_type_string, -}; - -/** - * Parse iSCSI root path - * - * @v iscsi iSCSI session - * @v root_path iSCSI root path (as per RFC4173) - * @ret rc Return status code - */ -static int iscsi_parse_root_path ( struct iscsi_session *iscsi, - const char *root_path ) { - char rp_copy[ strlen ( root_path ) + 1 ]; - char *rp_comp[NUM_RP_COMPONENTS]; - char *rp = rp_copy; - int i = 0; - int rc; - - /* Split root path into component parts */ - strcpy ( rp_copy, root_path ); - while ( 1 ) { - rp_comp[i++] = rp; - if ( i == NUM_RP_COMPONENTS ) - break; - for ( ; *rp != ':' ; rp++ ) { - if ( ! *rp ) { - DBGC ( iscsi, "iSCSI %p root path \"%s\" " - "too short\n", iscsi, root_path ); - return -EINVAL_ROOT_PATH_TOO_SHORT; - } - } - *(rp++) = '\0'; - } - - /* Use root path components to configure iSCSI session */ - iscsi->target_address = strdup ( rp_comp[RP_SERVERNAME] ); - if ( ! iscsi->target_address ) - return -ENOMEM; - iscsi->target_port = strtoul ( rp_comp[RP_PORT], NULL, 10 ); - if ( ! iscsi->target_port ) - iscsi->target_port = ISCSI_PORT; - if ( ( rc = scsi_parse_lun ( rp_comp[RP_LUN], &iscsi->lun ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p invalid LUN \"%s\"\n", - iscsi, rp_comp[RP_LUN] ); - return rc; - } - iscsi->target_iqn = strdup ( rp_comp[RP_TARGETNAME] ); - if ( ! iscsi->target_iqn ) - return -ENOMEM; - - return 0; -} - -/** - * Fetch iSCSI settings - * - * @v iscsi iSCSI session - * @ret rc Return status code - */ -static int iscsi_fetch_settings ( struct iscsi_session *iscsi ) { - char *hostname; - union uuid uuid; - int len; - - /* Fetch relevant settings. Don't worry about freeing on - * error, since iscsi_free() will take care of that anyway. - */ - fetch_string_setting_copy ( NULL, &username_setting, - &iscsi->initiator_username ); - fetch_string_setting_copy ( NULL, &password_setting, - &iscsi->initiator_password ); - fetch_string_setting_copy ( NULL, &reverse_username_setting, - &iscsi->target_username ); - fetch_string_setting_copy ( NULL, &reverse_password_setting, - &iscsi->target_password ); - - /* Use explicit initiator IQN if provided */ - fetch_string_setting_copy ( NULL, &initiator_iqn_setting, - &iscsi->initiator_iqn ); - if ( iscsi->initiator_iqn ) - return 0; - - /* Otherwise, try to construct an initiator IQN from the hostname */ - fetch_string_setting_copy ( NULL, &hostname_setting, &hostname ); - if ( hostname ) { - len = asprintf ( &iscsi->initiator_iqn, - ISCSI_DEFAULT_IQN_PREFIX ":%s", hostname ); - free ( hostname ); - if ( len < 0 ) { - DBGC ( iscsi, "iSCSI %p could not allocate initiator " - "IQN\n", iscsi ); - return -ENOMEM; - } - assert ( iscsi->initiator_iqn ); - return 0; - } - - /* Otherwise, try to construct an initiator IQN from the UUID */ - if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, &uuid ) ) < 0 ) { - DBGC ( iscsi, "iSCSI %p has no suitable initiator IQN\n", - iscsi ); - return -EINVAL_NO_INITIATOR_IQN; - } - if ( ( len = asprintf ( &iscsi->initiator_iqn, - ISCSI_DEFAULT_IQN_PREFIX ":%s", - uuid_ntoa ( &uuid ) ) ) < 0 ) { - DBGC ( iscsi, "iSCSI %p could not allocate initiator IQN\n", - iscsi ); - return -ENOMEM; - } - assert ( iscsi->initiator_iqn ); - - return 0; -} - - -/** - * Check iSCSI authentication details - * - * @v iscsi iSCSI session - * @ret rc Return status code - */ -static int iscsi_check_auth ( struct iscsi_session *iscsi ) { - - /* Check for invalid authentication combinations */ - if ( ( /* Initiator username without password (or vice-versa) */ - ( !! iscsi->initiator_username ) ^ - ( !! iscsi->initiator_password ) ) || - ( /* Target username without password (or vice-versa) */ - ( !! iscsi->target_username ) ^ - ( !! iscsi->target_password ) ) || - ( /* Target (reverse) without initiator (forward) */ - ( iscsi->target_username && - ( ! iscsi->initiator_username ) ) ) ) { - DBGC ( iscsi, "iSCSI %p invalid credentials: initiator " - "%sname,%spw, target %sname,%spw\n", iscsi, - ( iscsi->initiator_username ? "" : "no " ), - ( iscsi->initiator_password ? "" : "no " ), - ( iscsi->target_username ? "" : "no " ), - ( iscsi->target_password ? "" : "no " ) ); - return -EINVAL_BAD_CREDENTIAL_MIX; - } - - return 0; -} - -/** - * Open iSCSI URI - * - * @v parent Parent interface - * @v uri URI - * @ret rc Return status code - */ -static int iscsi_open ( struct interface *parent, struct uri *uri ) { - struct iscsi_session *iscsi; - int rc; - - /* Sanity check */ - if ( ! uri->opaque ) { - rc = -EINVAL_NO_ROOT_PATH; - goto err_sanity_uri; - } - - /* Allocate and initialise structure */ - iscsi = zalloc ( sizeof ( *iscsi ) ); - if ( ! iscsi ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &iscsi->refcnt, iscsi_free ); - intf_init ( &iscsi->control, &iscsi_control_desc, &iscsi->refcnt ); - intf_init ( &iscsi->data, &iscsi_data_desc, &iscsi->refcnt ); - intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt ); - process_init_stopped ( &iscsi->process, &iscsi_process_desc, - &iscsi->refcnt ); - - /* Parse root path */ - if ( ( rc = iscsi_parse_root_path ( iscsi, uri->opaque ) ) != 0 ) - goto err_parse_root_path; - /* Set fields not specified by root path */ - if ( ( rc = iscsi_fetch_settings ( iscsi ) ) != 0 ) - goto err_fetch_settings; - /* Validate authentication */ - if ( ( rc = iscsi_check_auth ( iscsi ) ) != 0 ) - goto err_check_auth; - - /* Sanity checks */ - if ( ! iscsi->target_address ) { - DBGC ( iscsi, "iSCSI %p does not yet support discovery\n", - iscsi ); - rc = -ENOTSUP_DISCOVERY; - goto err_sanity_address; - } - if ( ! iscsi->target_iqn ) { - DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n", - iscsi, uri->opaque ); - rc = -EINVAL_NO_TARGET_IQN; - goto err_sanity_iqn; - } - DBGC ( iscsi, "iSCSI %p initiator %s\n",iscsi, iscsi->initiator_iqn ); - DBGC ( iscsi, "iSCSI %p target %s %s\n", - iscsi, iscsi->target_address, iscsi->target_iqn ); - - /* Open socket */ - if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) - goto err_open_connection; - - /* Attach SCSI device to parent interface */ - if ( ( rc = scsi_open ( parent, &iscsi->control, - &iscsi->lun ) ) != 0 ) { - DBGC ( iscsi, "iSCSI %p could not create SCSI device: %s\n", - iscsi, strerror ( rc ) ); - goto err_scsi_open; - } - - /* Mortalise self, and return */ - ref_put ( &iscsi->refcnt ); - return 0; - - err_scsi_open: - err_open_connection: - err_sanity_iqn: - err_sanity_address: - err_check_auth: - err_fetch_settings: - err_parse_root_path: - iscsi_close ( iscsi, rc ); - ref_put ( &iscsi->refcnt ); - err_zalloc: - err_sanity_uri: - return rc; -} - -/** iSCSI URI opener */ -struct uri_opener iscsi_uri_opener __uri_opener = { - .scheme = "iscsi", - .open = iscsi_open, -}; diff --git a/qemu/roms/ipxe/src/net/tcp/oncrpc.c b/qemu/roms/ipxe/src/net/tcp/oncrpc.c deleted file mode 100644 index 6469867e9..000000000 --- a/qemu/roms/ipxe/src/net/tcp/oncrpc.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. - * - * 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 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. - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/socket.h> -#include <ipxe/tcpip.h> -#include <ipxe/in.h> -#include <ipxe/iobuf.h> -#include <ipxe/dhcp.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/features.h> -#include <ipxe/oncrpc.h> -#include <ipxe/oncrpc_iob.h> -#include <ipxe/init.h> -#include <ipxe/settings.h> -#include <ipxe/version.h> - -/** @file - * - * SUN ONC RPC protocol - * - */ - -/** Set most significant bit to 1. */ -#define SET_LAST_FRAME( x ) ( (x) | 1 << 31 ) -#define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) ) - -#define ONCRPC_CALL 0 -#define ONCRPC_REPLY 1 - -/** AUTH NONE authentication flavor */ -struct oncrpc_cred oncrpc_auth_none = { - .flavor = ONCRPC_AUTH_NONE, - .length = 0 -}; - -const struct setting uid_setting __setting ( SETTING_AUTH, uid ) = { - .name = "uid", - .description = "User ID", - .tag = DHCP_EB_UID, - .type = &setting_type_uint32 -}; - -const struct setting gid_setting __setting ( SETTING_AUTH, gid ) = { - .name = "gid", - .description = "Group ID", - .tag = DHCP_EB_GID, - .type = &setting_type_uint32 -}; - -/** - * Initialize an ONC RPC AUTH SYS credential structure - * - * @v auth_sys The structure to initialize - * - * The hostname field is filled with the value of the hostname setting, if the - * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used - * instead. - */ -int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) { - if ( ! auth_sys ) - return -EINVAL; - - fetch_string_setting_copy ( NULL, &hostname_setting, - &auth_sys->hostname ); - if ( ! auth_sys->hostname ) - if ( ! ( auth_sys->hostname = strdup ( product_short_name ) ) ) - return -ENOMEM; - - auth_sys->uid = fetch_uintz_setting ( NULL, &uid_setting ); - auth_sys->gid = fetch_uintz_setting ( NULL, &uid_setting ); - auth_sys->aux_gid_len = 0; - auth_sys->stamp = 0; - - auth_sys->credential.flavor = ONCRPC_AUTH_SYS; - auth_sys->credential.length = 16 + - oncrpc_strlen ( auth_sys->hostname ); - - return 0; -} - -/** - * Prepare an ONC RPC session structure to be used by the ONC RPC layer - * - * @v session ONC RPC session - * @v credential Credential structure pointer - * @v verifier Verifier structure pointer - * @v prog_name ONC RPC program number - * @v prog_vers ONC RPC program version number - */ -void oncrpc_init_session ( struct oncrpc_session *session, - struct oncrpc_cred *credential, - struct oncrpc_cred *verifier, uint32_t prog_name, - uint32_t prog_vers ) { - if ( ! session ) - return; - - session->rpc_id = rand(); - session->credential = credential; - session->verifier = verifier; - session->prog_name = prog_name; - session->prog_vers = prog_vers; -} - -int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, - uint32_t proc_name, const struct oncrpc_field fields[] ) { - int rc; - size_t frame_size; - struct io_buffer *io_buf; - - if ( ! session ) - return -EINVAL; - - struct oncrpc_field header[] = { - ONCRPC_FIELD ( int32, 0 ), - ONCRPC_FIELD ( int32, ++session->rpc_id ), - ONCRPC_FIELD ( int32, ONCRPC_CALL ), - ONCRPC_FIELD ( int32, ONCRPC_VERS ), - ONCRPC_FIELD ( int32, session->prog_name ), - ONCRPC_FIELD ( int32, session->prog_vers ), - ONCRPC_FIELD ( int32, proc_name ), - ONCRPC_FIELD ( cred, session->credential ), - ONCRPC_FIELD ( cred, session->verifier ), - ONCRPC_FIELD_END, - }; - - frame_size = oncrpc_compute_size ( header ); - frame_size += oncrpc_compute_size ( fields ); - - io_buf = alloc_iob ( frame_size ); - if ( ! io_buf ) - return -ENOBUFS; - - header[0].value.int32 = SET_LAST_FRAME ( frame_size - - sizeof ( uint32_t ) ); - - oncrpc_iob_add_fields ( io_buf, header ); - oncrpc_iob_add_fields ( io_buf, fields ); - - rc = xfer_deliver_iob ( intf, io_buf ); - if ( rc != 0 ) - free_iob ( io_buf ); - - return rc; -} - -size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) { - - size_t i; - size_t size = 0; - - for ( i = 0; fields[i].type != oncrpc_none; i++ ) { - switch ( fields[i].type ) { - case oncrpc_int32: - size += sizeof ( uint32_t ); - break; - - case oncrpc_int64: - size += sizeof ( uint64_t ); - break; - - case oncrpc_str: - size += oncrpc_strlen ( fields[i].value.str ); - break; - - case oncrpc_array: - size += oncrpc_align ( fields[i].value.array.length ); - size += sizeof ( uint32_t ); - break; - - case oncrpc_intarray: - size += sizeof ( uint32_t ) * - fields[i].value.intarray.length; - size += sizeof ( uint32_t ); - break; - - case oncrpc_cred: - size += fields[i].value.cred->length; - size += 2 * sizeof ( uint32_t ); - break; - - default: - return size; - } - } - - return size; -} - -/** - * Parse an I/O buffer to extract a ONC RPC REPLY - * @v session ONC RPC session - * @v reply Reply structure where data will be saved - * @v io_buf I/O buffer - */ -int oncrpc_get_reply ( struct oncrpc_session *session __unused, - struct oncrpc_reply *reply, struct io_buffer *io_buf ) { - if ( ! reply || ! io_buf ) - return -EINVAL; - - reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) ); - reply->rpc_id = oncrpc_iob_get_int ( io_buf ); - - /* iPXE has no support for handling ONC RPC call */ - if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY ) - return -ENOSYS; - - reply->reply_state = oncrpc_iob_get_int ( io_buf ); - - if ( reply->reply_state == 0 ) - { - /* verifier.flavor */ - oncrpc_iob_get_int ( io_buf ); - /* verifier.length */ - iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf )); - - /* We don't use the verifier in iPXE, let it be an empty - verifier. */ - reply->verifier = &oncrpc_auth_none; - } - - reply->accept_state = oncrpc_iob_get_int ( io_buf ); - reply->data = io_buf; - - return 0; -} diff --git a/qemu/roms/ipxe/src/net/tcp/syslogs.c b/qemu/roms/ipxe/src/net/tcp/syslogs.c deleted file mode 100644 index 0c07f86d5..000000000 --- a/qemu/roms/ipxe/src/net/tcp/syslogs.c +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** @file - * - * Encrypted syslog protocol - * - */ - -#include <stdint.h> -#include <stdlib.h> -#include <byteswap.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/tcpip.h> -#include <ipxe/dhcp.h> -#include <ipxe/settings.h> -#include <ipxe/console.h> -#include <ipxe/lineconsole.h> -#include <ipxe/tls.h> -#include <ipxe/syslog.h> -#include <config/console.h> - -/* Set default console usage if applicable */ -#if ! ( defined ( CONSOLE_SYSLOGS ) && CONSOLE_EXPLICIT ( CONSOLE_SYSLOGS ) ) -#undef CONSOLE_SYSLOGS -#define CONSOLE_SYSLOGS ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_TUI ) -#endif - -struct console_driver syslogs_console __console_driver; - -/** The encrypted syslog server */ -static struct sockaddr_tcpip logserver = { - .st_port = htons ( SYSLOG_PORT ), -}; - -/** - * Handle encrypted syslog TLS interface close - * - * @v intf Interface - * @v rc Reason for close - */ -static void syslogs_close ( struct interface *intf __unused, int rc ) { - - DBG ( "SYSLOGS console disconnected: %s\n", strerror ( rc ) ); -} - -/** - * Handle encrypted syslog TLS interface window change - * - * @v intf Interface - */ -static void syslogs_window_changed ( struct interface *intf ) { - - /* Mark console as enabled when window first opens, indicating - * that TLS negotiation is complete. (Do not disable console - * when window closes again, since TCP will close the window - * whenever there is unACKed data.) - */ - if ( xfer_window ( intf ) ) { - if ( syslogs_console.disabled ) - DBG ( "SYSLOGS console connected\n" ); - syslogs_console.disabled = 0; - } -} - -/** Encrypted syslog TLS interface operations */ -static struct interface_operation syslogs_operations[] = { - INTF_OP ( xfer_window_changed, struct interface *, - syslogs_window_changed ), - INTF_OP ( intf_close, struct interface *, syslogs_close ), -}; - -/** Encrypted syslog TLS interface descriptor */ -static struct interface_descriptor syslogs_desc = - INTF_DESC_PURE ( syslogs_operations ); - -/** The encrypted syslog TLS interface */ -static struct interface syslogs = INTF_INIT ( syslogs_desc ); - -/****************************************************************************** - * - * Console driver - * - ****************************************************************************** - */ - -/** Encrypted syslog line buffer */ -static char syslogs_buffer[SYSLOG_BUFSIZE]; - -/** Encrypted syslog severity */ -static unsigned int syslogs_severity = SYSLOG_DEFAULT_SEVERITY; - -/** - * Handle ANSI set encrypted syslog priority (private sequence) - * - * @v ctx ANSI escape sequence context - * @v count Parameter count - * @v params List of graphic rendition aspects - */ -static void syslogs_handle_priority ( struct ansiesc_context *ctx __unused, - unsigned int count __unused, - int params[] ) { - if ( params[0] >= 0 ) { - syslogs_severity = params[0]; - } else { - syslogs_severity = SYSLOG_DEFAULT_SEVERITY; - } -} - -/** Encrypted syslog ANSI escape sequence handlers */ -static struct ansiesc_handler syslogs_handlers[] = { - { ANSIESC_LOG_PRIORITY, syslogs_handle_priority }, - { 0, NULL } -}; - -/** Encrypted syslog line console */ -static struct line_console syslogs_line = { - .buffer = syslogs_buffer, - .len = sizeof ( syslogs_buffer ), - .ctx = { - .handlers = syslogs_handlers, - }, -}; - -/** Encrypted syslog recursion marker */ -static int syslogs_entered; - -/** - * Print a character to encrypted syslog console - * - * @v character Character to be printed - */ -static void syslogs_putchar ( int character ) { - int rc; - - /* Ignore if we are already mid-logging */ - if ( syslogs_entered ) - return; - - /* Fill line buffer */ - if ( line_putchar ( &syslogs_line, character ) == 0 ) - return; - - /* Guard against re-entry */ - syslogs_entered = 1; - - /* Send log message */ - if ( ( rc = syslog_send ( &syslogs, syslogs_severity, - syslogs_buffer, "\n" ) ) != 0 ) { - DBG ( "SYSLOGS could not send log message: %s\n", - strerror ( rc ) ); - } - - /* Clear re-entry flag */ - syslogs_entered = 0; -} - -/** Encrypted syslog console driver */ -struct console_driver syslogs_console __console_driver = { - .putchar = syslogs_putchar, - .disabled = CONSOLE_DISABLED, - .usage = CONSOLE_SYSLOGS, -}; - -/****************************************************************************** - * - * Settings - * - ****************************************************************************** - */ - -/** Encrypted syslog server setting */ -const struct setting syslogs_setting __setting ( SETTING_MISC, syslogs ) = { - .name = "syslogs", - .description = "Encrypted syslog server", - .tag = DHCP_EB_SYSLOGS_SERVER, - .type = &setting_type_string, -}; - -/** - * Apply encrypted syslog settings - * - * @ret rc Return status code - */ -static int apply_syslogs_settings ( void ) { - static char *old_server; - char *server; - struct interface *socket; - int rc; - - /* Fetch log server */ - fetch_string_setting_copy ( NULL, &syslogs_setting, &server ); - - /* Do nothing unless log server has changed */ - if ( ( ( server == NULL ) && ( old_server == NULL ) ) || - ( ( server != NULL ) && ( old_server != NULL ) && - ( strcmp ( server, old_server ) == 0 ) ) ) { - rc = 0; - goto out_no_change; - } - free ( old_server ); - old_server = NULL; - - /* Reset encrypted syslog connection */ - syslogs_console.disabled = CONSOLE_DISABLED; - intf_restart ( &syslogs, 0 ); - - /* Do nothing unless we have a log server */ - if ( ! server ) { - DBG ( "SYSLOGS has no log server\n" ); - rc = 0; - goto out_no_server; - } - - /* Add TLS filter */ - if ( ( rc = add_tls ( &syslogs, server, &socket ) ) != 0 ) { - DBG ( "SYSLOGS cannot create TLS filter: %s\n", - strerror ( rc ) ); - goto err_add_tls; - } - - /* Connect to log server */ - if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, - (( struct sockaddr *) &logserver ), - server, NULL ) ) != 0 ) { - DBG ( "SYSLOGS cannot connect to log server: %s\n", - strerror ( rc ) ); - goto err_open_named_socket; - } - DBG ( "SYSLOGS using log server %s\n", server ); - - /* Record log server */ - old_server = server; - server = NULL; - - /* Success */ - rc = 0; - - err_open_named_socket: - err_add_tls: - out_no_server: - out_no_change: - free ( server ); - return rc; -} - -/** Encrypted syslog settings applicator */ -struct settings_applicator syslogs_applicator __settings_applicator = { - .apply = apply_syslogs_settings, -}; diff --git a/qemu/roms/ipxe/src/net/tcpip.c b/qemu/roms/ipxe/src/net/tcpip.c deleted file mode 100644 index 5ad982fd1..000000000 --- a/qemu/roms/ipxe/src/net/tcpip.c +++ /dev/null @@ -1,250 +0,0 @@ -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/iobuf.h> -#include <ipxe/tables.h> -#include <ipxe/ipstat.h> -#include <ipxe/netdevice.h> -#include <ipxe/tcpip.h> - -/** @file - * - * Transport-network layer interface - * - * This file contains functions and utilities for the - * TCP/IP transport-network layer interface - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * Process a received TCP/IP packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v tcpip_proto Transport-layer protocol number - * @v st_src Partially-filled source address - * @v st_dest Partially-filled destination address - * @v pshdr_csum Pseudo-header checksum - * @v stats IP statistics - * @ret rc Return status code - * - * This function expects a transport-layer segment from the network - * layer. The network layer should fill in as much as it can of the - * source and destination addresses (i.e. it should fill in the - * address family and the network-layer addresses, but leave the ports - * and the rest of the structures as zero). - */ -int tcpip_rx ( struct io_buffer *iobuf, struct net_device *netdev, - uint8_t tcpip_proto, struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum, - struct ip_statistics *stats ) { - struct tcpip_protocol *tcpip; - - /* Hand off packet to the appropriate transport-layer protocol */ - for_each_table_entry ( tcpip, TCPIP_PROTOCOLS ) { - if ( tcpip->tcpip_proto == tcpip_proto ) { - DBG ( "TCP/IP received %s packet\n", tcpip->name ); - stats->in_delivers++; - return tcpip->rx ( iobuf, netdev, st_src, st_dest, - pshdr_csum ); - } - } - - DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto ); - stats->in_unknown_protos++; - free_iob ( iobuf ); - return -EPROTONOSUPPORT; -} - -/** - * Find TCP/IP network-layer protocol - * - * @v st_dest Destination address - * @ret tcpip_net TCP/IP network-layer protocol, or NULL if not found - */ -static struct tcpip_net_protocol * -tcpip_net_protocol ( struct sockaddr_tcpip *st_dest ) { - struct tcpip_net_protocol *tcpip_net; - - for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) { - if ( tcpip_net->sa_family == st_dest->st_family ) - return tcpip_net; - } - - DBG ( "Unrecognised TCP/IP address family %d\n", st_dest->st_family ); - return NULL; -} - -/** - * Transmit a TCP/IP packet - * - * @v iobuf I/O buffer - * @v tcpip_protocol Transport-layer protocol - * @v st_src Source address, or NULL to use route default - * @v st_dest Destination 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 Return status code - */ -int tcpip_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 tcpip_net_protocol *tcpip_net; - - /* Hand off packet to the appropriate network-layer protocol */ - tcpip_net = tcpip_net_protocol ( st_dest ); - if ( tcpip_net ) { - DBG ( "TCP/IP sending %s packet\n", tcpip_net->name ); - return tcpip_net->tx ( iobuf, tcpip_protocol, st_src, st_dest, - netdev, trans_csum ); - } - - free_iob ( iobuf ); - return -EAFNOSUPPORT; -} - -/** - * Determine transmitting network device - * - * @v st_dest Destination address - * @ret netdev Network device, or NULL - */ -struct net_device * tcpip_netdev ( struct sockaddr_tcpip *st_dest ) { - struct tcpip_net_protocol *tcpip_net; - - /* Hand off to the appropriate network-layer protocol */ - tcpip_net = tcpip_net_protocol ( st_dest ); - if ( tcpip_net ) - return tcpip_net->netdev ( st_dest ); - - return NULL; -} - -/** - * Determine maximum transmission unit - * - * @v st_dest Destination address - * @ret mtu Maximum transmission unit - */ -size_t tcpip_mtu ( struct sockaddr_tcpip *st_dest ) { - struct tcpip_net_protocol *tcpip_net; - struct net_device *netdev; - size_t mtu; - - /* Find appropriate network-layer protocol */ - tcpip_net = tcpip_net_protocol ( st_dest ); - if ( ! tcpip_net ) - return 0; - - /* Find transmitting network device */ - netdev = tcpip_net->netdev ( st_dest ); - if ( ! netdev ) - return 0; - - /* Calculate MTU */ - mtu = ( netdev->max_pkt_len - netdev->ll_protocol->ll_header_len - - tcpip_net->header_len ); - - return mtu; -} - -/** - * Calculate continued TCP/IP checkum - * - * @v partial Checksum of already-summed data, in network byte order - * @v data Data buffer - * @v len Length of data buffer - * @ret cksum Updated checksum, in network byte order - * - * Calculates a TCP/IP-style 16-bit checksum over the data block. The - * checksum is returned in network byte order. - * - * This function may be used to add new data to an existing checksum. - * The function assumes that both the old data and the new data start - * on even byte offsets; if this is not the case then you will need to - * byte-swap either the input partial checksum, the output checksum, - * or both. Deciding which to swap is left as an exercise for the - * interested reader. - */ -uint16_t generic_tcpip_continue_chksum ( uint16_t partial, - const void *data, size_t len ) { - unsigned int cksum = ( ( ~partial ) & 0xffff ); - unsigned int value; - unsigned int i; - - for ( i = 0 ; i < len ; i++ ) { - value = * ( ( uint8_t * ) data + i ); - if ( i & 1 ) { - /* Odd bytes: swap on little-endian systems */ - value = be16_to_cpu ( value ); - } else { - /* Even bytes: swap on big-endian systems */ - value = le16_to_cpu ( value ); - } - cksum += value; - if ( cksum > 0xffff ) - cksum -= 0xffff; - } - - return ( ~cksum ); -} - -/** - * Calculate TCP/IP checkum - * - * @v data Data buffer - * @v len Length of data buffer - * @ret cksum Checksum, in network byte order - * - * Calculates a TCP/IP-style 16-bit checksum over the data block. The - * checksum is returned in network byte order. - */ -uint16_t tcpip_chksum ( const void *data, size_t len ) { - return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len ); -} - -/** - * Bind to local TCP/IP port - * - * @v st_local Local TCP/IP socket address, or NULL - * @v available Function to check port availability - * @ret port Local port number, or negative error - */ -int tcpip_bind ( struct sockaddr_tcpip *st_local, - int ( * available ) ( int port ) ) { - uint16_t flags = 0; - uint16_t try_port = 0; - uint16_t min_port; - uint16_t max_port; - unsigned int offset; - unsigned int i; - - /* Extract parameters from local socket address */ - if ( st_local ) { - flags = st_local->st_flags; - try_port = ntohs ( st_local->st_port ); - } - - /* If an explicit port is specified, check its availability */ - if ( try_port ) - return available ( try_port ); - - /* Otherwise, find an available port in the range [1,1023] or - * [1025,65535] as appropriate. - */ - min_port = ( ( ( ~flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); - max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 ); - offset = random(); - for ( i = 0 ; i <= max_port ; i++ ) { - try_port = ( ( i + offset ) & max_port ); - if ( try_port < min_port ) - continue; - if ( available ( try_port ) < 0 ) - continue; - return try_port; - } - return -EADDRINUSE; -} diff --git a/qemu/roms/ipxe/src/net/tls.c b/qemu/roms/ipxe/src/net/tls.c deleted file mode 100644 index db01fb291..000000000 --- a/qemu/roms/ipxe/src/net/tls.c +++ /dev/null @@ -1,2615 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/** - * @file - * - * Transport Layer Security Protocol - */ - -#include <stdint.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> -#include <time.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/pending.h> -#include <ipxe/hmac.h> -#include <ipxe/md5.h> -#include <ipxe/sha1.h> -#include <ipxe/sha256.h> -#include <ipxe/aes.h> -#include <ipxe/rsa.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/x509.h> -#include <ipxe/privkey.h> -#include <ipxe/certstore.h> -#include <ipxe/rbg.h> -#include <ipxe/validator.h> -#include <ipxe/tls.h> - -/* Disambiguate the various error causes */ -#define EINVAL_CHANGE_CIPHER __einfo_error ( EINFO_EINVAL_CHANGE_CIPHER ) -#define EINFO_EINVAL_CHANGE_CIPHER \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, \ - "Invalid Change Cipher record" ) -#define EINVAL_ALERT __einfo_error ( EINFO_EINVAL_ALERT ) -#define EINFO_EINVAL_ALERT \ - __einfo_uniqify ( EINFO_EINVAL, 0x02, \ - "Invalid Alert record" ) -#define EINVAL_HELLO __einfo_error ( EINFO_EINVAL_HELLO ) -#define EINFO_EINVAL_HELLO \ - __einfo_uniqify ( EINFO_EINVAL, 0x03, \ - "Invalid Server Hello record" ) -#define EINVAL_CERTIFICATE __einfo_error ( EINFO_EINVAL_CERTIFICATE ) -#define EINFO_EINVAL_CERTIFICATE \ - __einfo_uniqify ( EINFO_EINVAL, 0x04, \ - "Invalid Certificate" ) -#define EINVAL_CERTIFICATES __einfo_error ( EINFO_EINVAL_CERTIFICATES ) -#define EINFO_EINVAL_CERTIFICATES \ - __einfo_uniqify ( EINFO_EINVAL, 0x05, \ - "Invalid Server Certificate record" ) -#define EINVAL_HELLO_DONE __einfo_error ( EINFO_EINVAL_HELLO_DONE ) -#define EINFO_EINVAL_HELLO_DONE \ - __einfo_uniqify ( EINFO_EINVAL, 0x06, \ - "Invalid Server Hello Done record" ) -#define EINVAL_FINISHED __einfo_error ( EINFO_EINVAL_FINISHED ) -#define EINFO_EINVAL_FINISHED \ - __einfo_uniqify ( EINFO_EINVAL, 0x07, \ - "Invalid Server Finished record" ) -#define EINVAL_HANDSHAKE __einfo_error ( EINFO_EINVAL_HANDSHAKE ) -#define EINFO_EINVAL_HANDSHAKE \ - __einfo_uniqify ( EINFO_EINVAL, 0x08, \ - "Invalid Handshake record" ) -#define EINVAL_STREAM __einfo_error ( EINFO_EINVAL_STREAM ) -#define EINFO_EINVAL_STREAM \ - __einfo_uniqify ( EINFO_EINVAL, 0x09, \ - "Invalid stream-ciphered record" ) -#define EINVAL_BLOCK __einfo_error ( EINFO_EINVAL_BLOCK ) -#define EINFO_EINVAL_BLOCK \ - __einfo_uniqify ( EINFO_EINVAL, 0x0a, \ - "Invalid block-ciphered record" ) -#define EINVAL_PADDING __einfo_error ( EINFO_EINVAL_PADDING ) -#define EINFO_EINVAL_PADDING \ - __einfo_uniqify ( EINFO_EINVAL, 0x0b, \ - "Invalid block padding" ) -#define EINVAL_RX_STATE __einfo_error ( EINFO_EINVAL_RX_STATE ) -#define EINFO_EINVAL_RX_STATE \ - __einfo_uniqify ( EINFO_EINVAL, 0x0c, \ - "Invalid receive state" ) -#define EINVAL_MAC __einfo_error ( EINFO_EINVAL_MAC ) -#define EINFO_EINVAL_MAC \ - __einfo_uniqify ( EINFO_EINVAL, 0x0d, \ - "Invalid MAC" ) -#define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT ) -#define EINFO_EIO_ALERT \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, \ - "Unknown alert level" ) -#define ENOMEM_CONTEXT __einfo_error ( EINFO_ENOMEM_CONTEXT ) -#define EINFO_ENOMEM_CONTEXT \ - __einfo_uniqify ( EINFO_ENOMEM, 0x01, \ - "Not enough space for crypto context" ) -#define ENOMEM_CERTIFICATE __einfo_error ( EINFO_ENOMEM_CERTIFICATE ) -#define EINFO_ENOMEM_CERTIFICATE \ - __einfo_uniqify ( EINFO_ENOMEM, 0x02, \ - "Not enough space for certificate" ) -#define ENOMEM_CHAIN __einfo_error ( EINFO_ENOMEM_CHAIN ) -#define EINFO_ENOMEM_CHAIN \ - __einfo_uniqify ( EINFO_ENOMEM, 0x03, \ - "Not enough space for certificate chain" ) -#define ENOMEM_TX_PLAINTEXT __einfo_error ( EINFO_ENOMEM_TX_PLAINTEXT ) -#define EINFO_ENOMEM_TX_PLAINTEXT \ - __einfo_uniqify ( EINFO_ENOMEM, 0x04, \ - "Not enough space for transmitted plaintext" ) -#define ENOMEM_TX_CIPHERTEXT __einfo_error ( EINFO_ENOMEM_TX_CIPHERTEXT ) -#define EINFO_ENOMEM_TX_CIPHERTEXT \ - __einfo_uniqify ( EINFO_ENOMEM, 0x05, \ - "Not enough space for transmitted ciphertext" ) -#define ENOMEM_RX_DATA __einfo_error ( EINFO_ENOMEM_RX_DATA ) -#define EINFO_ENOMEM_RX_DATA \ - __einfo_uniqify ( EINFO_ENOMEM, 0x07, \ - "Not enough space for received data" ) -#define ENOMEM_RX_CONCAT __einfo_error ( EINFO_ENOMEM_RX_CONCAT ) -#define EINFO_ENOMEM_RX_CONCAT \ - __einfo_uniqify ( EINFO_ENOMEM, 0x08, \ - "Not enough space to concatenate received data" ) -#define ENOTSUP_CIPHER __einfo_error ( EINFO_ENOTSUP_CIPHER ) -#define EINFO_ENOTSUP_CIPHER \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ - "Unsupported cipher" ) -#define ENOTSUP_NULL __einfo_error ( EINFO_ENOTSUP_NULL ) -#define EINFO_ENOTSUP_NULL \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x02, \ - "Refusing to use null cipher" ) -#define ENOTSUP_SIG_HASH __einfo_error ( EINFO_ENOTSUP_SIG_HASH ) -#define EINFO_ENOTSUP_SIG_HASH \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x03, \ - "Unsupported signature and hash algorithm" ) -#define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION ) -#define EINFO_ENOTSUP_VERSION \ - __einfo_uniqify ( EINFO_ENOTSUP, 0x04, \ - "Unsupported protocol version" ) -#define EPERM_ALERT __einfo_error ( EINFO_EPERM_ALERT ) -#define EINFO_EPERM_ALERT \ - __einfo_uniqify ( EINFO_EPERM, 0x01, \ - "Received fatal alert" ) -#define EPERM_VERIFY __einfo_error ( EINFO_EPERM_VERIFY ) -#define EINFO_EPERM_VERIFY \ - __einfo_uniqify ( EINFO_EPERM, 0x02, \ - "Handshake verification failed" ) -#define EPERM_CLIENT_CERT __einfo_error ( EINFO_EPERM_CLIENT_CERT ) -#define EINFO_EPERM_CLIENT_CERT \ - __einfo_uniqify ( EINFO_EPERM, 0x03, \ - "No suitable client certificate available" ) -#define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION ) -#define EINFO_EPROTO_VERSION \ - __einfo_uniqify ( EINFO_EPROTO, 0x01, \ - "Illegal protocol version upgrade" ) - -static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, - const void *data, size_t len ); -static void tls_clear_cipher ( struct tls_session *tls, - struct tls_cipherspec *cipherspec ); - -/****************************************************************************** - * - * Utility functions - * - ****************************************************************************** - */ - -/** A TLS 24-bit integer - * - * TLS uses 24-bit integers in several places, which are awkward to - * parse in C. - */ -typedef struct { - /** High byte */ - uint8_t high; - /** Low word */ - uint16_t low; -} __attribute__ (( packed )) tls24_t; - -/** - * Extract 24-bit field value - * - * @v field24 24-bit field - * @ret value Field value - * - */ -static inline __attribute__ (( always_inline )) unsigned long -tls_uint24 ( const tls24_t *field24 ) { - - return ( ( field24->high << 16 ) | be16_to_cpu ( field24->low ) ); -} - -/** - * Set 24-bit field value - * - * @v field24 24-bit field - * @v value Field value - */ -static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) { - - field24->high = ( value >> 16 ); - field24->low = cpu_to_be16 ( value ); -} - -/** - * Determine if TLS session is ready for application data - * - * @v tls TLS session - * @ret is_ready TLS session is ready - */ -static int tls_ready ( struct tls_session *tls ) { - return ( ( ! is_pending ( &tls->client_negotiation ) ) && - ( ! is_pending ( &tls->server_negotiation ) ) ); -} - -/****************************************************************************** - * - * Hybrid MD5+SHA1 hash as used by TLSv1.1 and earlier - * - ****************************************************************************** - */ - -/** - * Initialise MD5+SHA1 algorithm - * - * @v ctx MD5+SHA1 context - */ -static void md5_sha1_init ( void *ctx ) { - struct md5_sha1_context *context = ctx; - - digest_init ( &md5_algorithm, context->md5 ); - digest_init ( &sha1_algorithm, context->sha1 ); -} - -/** - * Accumulate data with MD5+SHA1 algorithm - * - * @v ctx MD5+SHA1 context - * @v data Data - * @v len Length of data - */ -static void md5_sha1_update ( void *ctx, const void *data, size_t len ) { - struct md5_sha1_context *context = ctx; - - digest_update ( &md5_algorithm, context->md5, data, len ); - digest_update ( &sha1_algorithm, context->sha1, data, len ); -} - -/** - * Generate MD5+SHA1 digest - * - * @v ctx MD5+SHA1 context - * @v out Output buffer - */ -static void md5_sha1_final ( void *ctx, void *out ) { - struct md5_sha1_context *context = ctx; - struct md5_sha1_digest *digest = out; - - digest_final ( &md5_algorithm, context->md5, digest->md5 ); - digest_final ( &sha1_algorithm, context->sha1, digest->sha1 ); -} - -/** Hybrid MD5+SHA1 digest algorithm */ -static struct digest_algorithm md5_sha1_algorithm = { - .name = "md5+sha1", - .ctxsize = sizeof ( struct md5_sha1_context ), - .blocksize = 0, /* Not applicable */ - .digestsize = sizeof ( struct md5_sha1_digest ), - .init = md5_sha1_init, - .update = md5_sha1_update, - .final = md5_sha1_final, -}; - -/** RSA digestInfo prefix for MD5+SHA1 algorithm */ -struct rsa_digestinfo_prefix rsa_md5_sha1_prefix __rsa_digestinfo_prefix = { - .digest = &md5_sha1_algorithm, - .data = NULL, /* MD5+SHA1 signatures have no digestInfo */ - .len = 0, -}; - -/****************************************************************************** - * - * Cleanup functions - * - ****************************************************************************** - */ - -/** - * Free TLS session - * - * @v refcnt Reference counter - */ -static void free_tls ( struct refcnt *refcnt ) { - struct tls_session *tls = - container_of ( refcnt, struct tls_session, refcnt ); - struct io_buffer *iobuf; - struct io_buffer *tmp; - - /* Free dynamically-allocated resources */ - tls_clear_cipher ( tls, &tls->tx_cipherspec ); - tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); - tls_clear_cipher ( tls, &tls->rx_cipherspec ); - tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); - list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - } - x509_put ( tls->cert ); - x509_chain_put ( tls->chain ); - - /* Free TLS structure itself */ - free ( tls ); -} - -/** - * Finish with TLS session - * - * @v tls TLS session - * @v rc Status code - */ -static void tls_close ( struct tls_session *tls, int rc ) { - - /* Remove pending operations, if applicable */ - pending_put ( &tls->client_negotiation ); - pending_put ( &tls->server_negotiation ); - - /* Remove process */ - process_del ( &tls->process ); - - /* Close all interfaces */ - intf_shutdown ( &tls->cipherstream, rc ); - intf_shutdown ( &tls->plainstream, rc ); - intf_shutdown ( &tls->validator, rc ); -} - -/****************************************************************************** - * - * Random number generation - * - ****************************************************************************** - */ - -/** - * Generate random data - * - * @v tls TLS session - * @v data Buffer to fill - * @v len Length of buffer - * @ret rc Return status code - */ -static int tls_generate_random ( struct tls_session *tls, - void *data, size_t len ) { - int rc; - - /* Generate random bits with no additional input and without - * prediction resistance - */ - if ( ( rc = rbg_generate ( NULL, 0, 0, data, len ) ) != 0 ) { - DBGC ( tls, "TLS %p could not generate random data: %s\n", - tls, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Update HMAC with a list of ( data, len ) pairs - * - * @v digest Hash function to use - * @v digest_ctx Digest context - * @v args ( data, len ) pairs of data, terminated by NULL - */ -static void tls_hmac_update_va ( struct digest_algorithm *digest, - void *digest_ctx, va_list args ) { - void *data; - size_t len; - - while ( ( data = va_arg ( args, void * ) ) ) { - len = va_arg ( args, size_t ); - hmac_update ( digest, digest_ctx, data, len ); - } -} - -/** - * Generate secure pseudo-random data using a single hash function - * - * @v tls TLS session - * @v digest Hash function to use - * @v secret Secret - * @v secret_len Length of secret - * @v out Output buffer - * @v out_len Length of output buffer - * @v seeds ( data, len ) pairs of seed data, terminated by NULL - */ -static void tls_p_hash_va ( struct tls_session *tls, - struct digest_algorithm *digest, - void *secret, size_t secret_len, - void *out, size_t out_len, - va_list seeds ) { - uint8_t secret_copy[secret_len]; - uint8_t digest_ctx[digest->ctxsize]; - uint8_t digest_ctx_partial[digest->ctxsize]; - uint8_t a[digest->digestsize]; - uint8_t out_tmp[digest->digestsize]; - size_t frag_len = digest->digestsize; - va_list tmp; - - /* Copy the secret, in case HMAC modifies it */ - memcpy ( secret_copy, secret, secret_len ); - secret = secret_copy; - DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name ); - DBGC2_HD ( tls, secret, secret_len ); - - /* Calculate A(1) */ - hmac_init ( digest, digest_ctx, secret, &secret_len ); - va_copy ( tmp, seeds ); - tls_hmac_update_va ( digest, digest_ctx, tmp ); - va_end ( tmp ); - hmac_final ( digest, digest_ctx, secret, &secret_len, a ); - DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name ); - DBGC2_HD ( tls, &a, sizeof ( a ) ); - - /* Generate as much data as required */ - while ( out_len ) { - /* Calculate output portion */ - hmac_init ( digest, digest_ctx, secret, &secret_len ); - hmac_update ( digest, digest_ctx, a, sizeof ( a ) ); - memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize ); - va_copy ( tmp, seeds ); - tls_hmac_update_va ( digest, digest_ctx, tmp ); - va_end ( tmp ); - hmac_final ( digest, digest_ctx, - secret, &secret_len, out_tmp ); - - /* Copy output */ - if ( frag_len > out_len ) - frag_len = out_len; - memcpy ( out, out_tmp, frag_len ); - DBGC2 ( tls, "TLS %p %s output:\n", tls, digest->name ); - DBGC2_HD ( tls, out, frag_len ); - - /* Calculate A(i) */ - hmac_final ( digest, digest_ctx_partial, - secret, &secret_len, a ); - DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name ); - DBGC2_HD ( tls, &a, sizeof ( a ) ); - - out += frag_len; - out_len -= frag_len; - } -} - -/** - * Generate secure pseudo-random data - * - * @v tls TLS session - * @v secret Secret - * @v secret_len Length of secret - * @v out Output buffer - * @v out_len Length of output buffer - * @v ... ( data, len ) pairs of seed data, terminated by NULL - */ -static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len, - void *out, size_t out_len, ... ) { - va_list seeds; - va_list tmp; - size_t subsecret_len; - void *md5_secret; - void *sha1_secret; - uint8_t buf[out_len]; - unsigned int i; - - va_start ( seeds, out_len ); - - if ( tls->version >= TLS_VERSION_TLS_1_2 ) { - /* Use P_SHA256 for TLSv1.2 and later */ - tls_p_hash_va ( tls, &sha256_algorithm, secret, secret_len, - out, out_len, seeds ); - } else { - /* Use combination of P_MD5 and P_SHA-1 for TLSv1.1 - * and earlier - */ - - /* Split secret into two, with an overlap of up to one byte */ - subsecret_len = ( ( secret_len + 1 ) / 2 ); - md5_secret = secret; - sha1_secret = ( secret + secret_len - subsecret_len ); - - /* Calculate MD5 portion */ - va_copy ( tmp, seeds ); - tls_p_hash_va ( tls, &md5_algorithm, md5_secret, - subsecret_len, out, out_len, seeds ); - va_end ( tmp ); - - /* Calculate SHA1 portion */ - va_copy ( tmp, seeds ); - tls_p_hash_va ( tls, &sha1_algorithm, sha1_secret, - subsecret_len, buf, out_len, seeds ); - va_end ( tmp ); - - /* XOR the two portions together into the final output buffer */ - for ( i = 0 ; i < out_len ; i++ ) - *( ( uint8_t * ) out + i ) ^= buf[i]; - } - - va_end ( seeds ); -} - -/** - * Generate secure pseudo-random data - * - * @v secret Secret - * @v secret_len Length of secret - * @v out Output buffer - * @v out_len Length of output buffer - * @v label String literal label - * @v ... ( data, len ) pairs of seed data - */ -#define tls_prf_label( tls, secret, secret_len, out, out_len, label, ... ) \ - tls_prf ( (tls), (secret), (secret_len), (out), (out_len), \ - label, ( sizeof ( label ) - 1 ), __VA_ARGS__, NULL ) - -/****************************************************************************** - * - * Secret management - * - ****************************************************************************** - */ - -/** - * Generate master secret - * - * @v tls TLS session - * - * The pre-master secret and the client and server random values must - * already be known. - */ -static void tls_generate_master_secret ( struct tls_session *tls ) { - DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); - DBGC_HD ( tls, &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ) ); - DBGC ( tls, "TLS %p client random bytes:\n", tls ); - DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) ); - DBGC ( tls, "TLS %p server random bytes:\n", tls ); - DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) ); - - tls_prf_label ( tls, &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ), - &tls->master_secret, sizeof ( tls->master_secret ), - "master secret", - &tls->client_random, sizeof ( tls->client_random ), - &tls->server_random, sizeof ( tls->server_random ) ); - - DBGC ( tls, "TLS %p generated master secret:\n", tls ); - DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) ); -} - -/** - * Generate key material - * - * @v tls TLS session - * - * The master secret must already be known. - */ -static int tls_generate_keys ( struct tls_session *tls ) { - struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending; - struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; - size_t hash_size = tx_cipherspec->suite->digest->digestsize; - size_t key_size = tx_cipherspec->suite->key_len; - size_t iv_size = tx_cipherspec->suite->cipher->blocksize; - size_t total = ( 2 * ( hash_size + key_size + iv_size ) ); - uint8_t key_block[total]; - uint8_t *key; - int rc; - - /* Generate key block */ - tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - key_block, sizeof ( key_block ), "key expansion", - &tls->server_random, sizeof ( tls->server_random ), - &tls->client_random, sizeof ( tls->client_random ) ); - - /* Split key block into portions */ - key = key_block; - - /* TX MAC secret */ - memcpy ( tx_cipherspec->mac_secret, key, hash_size ); - DBGC ( tls, "TLS %p TX MAC secret:\n", tls ); - DBGC_HD ( tls, key, hash_size ); - key += hash_size; - - /* RX MAC secret */ - memcpy ( rx_cipherspec->mac_secret, key, hash_size ); - DBGC ( tls, "TLS %p RX MAC secret:\n", tls ); - DBGC_HD ( tls, key, hash_size ); - key += hash_size; - - /* TX key */ - if ( ( rc = cipher_setkey ( tx_cipherspec->suite->cipher, - tx_cipherspec->cipher_ctx, - key, key_size ) ) != 0 ) { - DBGC ( tls, "TLS %p could not set TX key: %s\n", - tls, strerror ( rc ) ); - return rc; - } - DBGC ( tls, "TLS %p TX key:\n", tls ); - DBGC_HD ( tls, key, key_size ); - key += key_size; - - /* RX key */ - if ( ( rc = cipher_setkey ( rx_cipherspec->suite->cipher, - rx_cipherspec->cipher_ctx, - key, key_size ) ) != 0 ) { - DBGC ( tls, "TLS %p could not set TX key: %s\n", - tls, strerror ( rc ) ); - return rc; - } - DBGC ( tls, "TLS %p RX key:\n", tls ); - DBGC_HD ( tls, key, key_size ); - key += key_size; - - /* TX initialisation vector */ - cipher_setiv ( tx_cipherspec->suite->cipher, - tx_cipherspec->cipher_ctx, key ); - DBGC ( tls, "TLS %p TX IV:\n", tls ); - DBGC_HD ( tls, key, iv_size ); - key += iv_size; - - /* RX initialisation vector */ - cipher_setiv ( rx_cipherspec->suite->cipher, - rx_cipherspec->cipher_ctx, key ); - DBGC ( tls, "TLS %p RX IV:\n", tls ); - DBGC_HD ( tls, key, iv_size ); - key += iv_size; - - assert ( ( key_block + total ) == key ); - - return 0; -} - -/****************************************************************************** - * - * Cipher suite management - * - ****************************************************************************** - */ - -/** Null cipher suite */ -struct tls_cipher_suite tls_cipher_suite_null = { - .pubkey = &pubkey_null, - .cipher = &cipher_null, - .digest = &digest_null, -}; - -/** Number of supported cipher suites */ -#define TLS_NUM_CIPHER_SUITES table_num_entries ( TLS_CIPHER_SUITES ) - -/** - * Identify cipher suite - * - * @v cipher_suite Cipher suite specification - * @ret suite Cipher suite, or NULL - */ -static struct tls_cipher_suite * -tls_find_cipher_suite ( unsigned int cipher_suite ) { - struct tls_cipher_suite *suite; - - /* Identify cipher suite */ - for_each_table_entry ( suite, TLS_CIPHER_SUITES ) { - if ( suite->code == cipher_suite ) - return suite; - } - - return NULL; -} - -/** - * Clear cipher suite - * - * @v cipherspec TLS cipher specification - */ -static void tls_clear_cipher ( struct tls_session *tls __unused, - struct tls_cipherspec *cipherspec ) { - - if ( cipherspec->suite ) { - pubkey_final ( cipherspec->suite->pubkey, - cipherspec->pubkey_ctx ); - } - free ( cipherspec->dynamic ); - memset ( cipherspec, 0, sizeof ( *cipherspec ) ); - cipherspec->suite = &tls_cipher_suite_null; -} - -/** - * Set cipher suite - * - * @v tls TLS session - * @v cipherspec TLS cipher specification - * @v suite Cipher suite - * @ret rc Return status code - */ -static int tls_set_cipher ( struct tls_session *tls, - struct tls_cipherspec *cipherspec, - struct tls_cipher_suite *suite ) { - struct pubkey_algorithm *pubkey = suite->pubkey; - struct cipher_algorithm *cipher = suite->cipher; - struct digest_algorithm *digest = suite->digest; - size_t total; - void *dynamic; - - /* Clear out old cipher contents, if any */ - tls_clear_cipher ( tls, cipherspec ); - - /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize ); - dynamic = zalloc ( total ); - if ( ! dynamic ) { - DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto " - "context\n", tls, total ); - return -ENOMEM_CONTEXT; - } - - /* Assign storage */ - cipherspec->dynamic = dynamic; - cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize; - cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize; - cipherspec->cipher_next_ctx = dynamic; dynamic += cipher->ctxsize; - cipherspec->mac_secret = dynamic; dynamic += digest->digestsize; - assert ( ( cipherspec->dynamic + total ) == dynamic ); - - /* Store parameters */ - cipherspec->suite = suite; - - return 0; -} - -/** - * Select next cipher suite - * - * @v tls TLS session - * @v cipher_suite Cipher suite specification - * @ret rc Return status code - */ -static int tls_select_cipher ( struct tls_session *tls, - unsigned int cipher_suite ) { - struct tls_cipher_suite *suite; - int rc; - - /* Identify cipher suite */ - suite = tls_find_cipher_suite ( cipher_suite ); - if ( ! suite ) { - DBGC ( tls, "TLS %p does not support cipher %04x\n", - tls, ntohs ( cipher_suite ) ); - return -ENOTSUP_CIPHER; - } - - /* Set ciphers */ - if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, - suite ) ) != 0 ) - return rc; - if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, - suite ) ) != 0 ) - return rc; - - DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls, suite->pubkey->name, - suite->cipher->name, ( suite->key_len * 8 ), - suite->digest->name ); - - return 0; -} - -/** - * Activate next cipher suite - * - * @v tls TLS session - * @v pending Pending cipher specification - * @v active Active cipher specification to replace - * @ret rc Return status code - */ -static int tls_change_cipher ( struct tls_session *tls, - struct tls_cipherspec *pending, - struct tls_cipherspec *active ) { - - /* Sanity check */ - if ( pending->suite == &tls_cipher_suite_null ) { - DBGC ( tls, "TLS %p refusing to use null cipher\n", tls ); - return -ENOTSUP_NULL; - } - - tls_clear_cipher ( tls, active ); - memswap ( active, pending, sizeof ( *active ) ); - return 0; -} - -/****************************************************************************** - * - * Signature and hash algorithms - * - ****************************************************************************** - */ - -/** Number of supported signature and hash algorithms */ -#define TLS_NUM_SIG_HASH_ALGORITHMS \ - table_num_entries ( TLS_SIG_HASH_ALGORITHMS ) - -/** - * Find TLS signature and hash algorithm - * - * @v pubkey Public-key algorithm - * @v digest Digest algorithm - * @ret sig_hash Signature and hash algorithm, or NULL - */ -static struct tls_signature_hash_algorithm * -tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, - struct digest_algorithm *digest ) { - struct tls_signature_hash_algorithm *sig_hash; - - /* Identify signature and hash algorithm */ - for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { - if ( ( sig_hash->pubkey == pubkey ) && - ( sig_hash->digest == digest ) ) { - return sig_hash; - } - } - - return NULL; -} - -/****************************************************************************** - * - * Handshake verification - * - ****************************************************************************** - */ - -/** - * Add handshake record to verification hash - * - * @v tls TLS session - * @v data Handshake record - * @v len Length of handshake record - */ -static void tls_add_handshake ( struct tls_session *tls, - const void *data, size_t len ) { - - digest_update ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx, - data, len ); - digest_update ( &sha256_algorithm, tls->handshake_sha256_ctx, - data, len ); -} - -/** - * Calculate handshake verification hash - * - * @v tls TLS session - * @v out Output buffer - * - * Calculates the MD5+SHA1 or SHA256 digest over all handshake - * messages seen so far. - */ -static void tls_verify_handshake ( struct tls_session *tls, void *out ) { - struct digest_algorithm *digest = tls->handshake_digest; - uint8_t ctx[ digest->ctxsize ]; - - memcpy ( ctx, tls->handshake_ctx, sizeof ( ctx ) ); - digest_final ( digest, ctx, out ); -} - -/****************************************************************************** - * - * Record handling - * - ****************************************************************************** - */ - -/** - * Resume TX state machine - * - * @v tls TLS session - */ -static void tls_tx_resume ( struct tls_session *tls ) { - process_add ( &tls->process ); -} - -/** - * Transmit Handshake record - * - * @v tls TLS session - * @v data Plaintext record - * @v len Length of plaintext record - * @ret rc Return status code - */ -static int tls_send_handshake ( struct tls_session *tls, - void *data, size_t len ) { - - /* Add to handshake digest */ - tls_add_handshake ( tls, data, len ); - - /* Send record */ - return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len ); -} - -/** - * Transmit Client Hello record - * - * @v tls TLS session - * @ret rc Return status code - */ -static int tls_send_client_hello ( struct tls_session *tls ) { - struct { - uint32_t type_length; - uint16_t version; - uint8_t random[32]; - uint8_t session_id_len; - uint16_t cipher_suite_len; - uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES]; - uint8_t compression_methods_len; - uint8_t compression_methods[1]; - uint16_t extensions_len; - struct { - uint16_t server_name_type; - uint16_t server_name_len; - struct { - uint16_t len; - struct { - uint8_t type; - uint16_t len; - uint8_t name[ strlen ( tls->name ) ]; - } __attribute__ (( packed )) list[1]; - } __attribute__ (( packed )) server_name; - uint16_t max_fragment_length_type; - uint16_t max_fragment_length_len; - struct { - uint8_t max; - } __attribute__ (( packed )) max_fragment_length; - uint16_t signature_algorithms_type; - uint16_t signature_algorithms_len; - struct { - uint16_t len; - struct tls_signature_hash_id - code[TLS_NUM_SIG_HASH_ALGORITHMS]; - } __attribute__ (( packed )) signature_algorithms; - } __attribute__ (( packed )) extensions; - } __attribute__ (( packed )) hello; - struct tls_cipher_suite *suite; - struct tls_signature_hash_algorithm *sighash; - unsigned int i; - - memset ( &hello, 0, sizeof ( hello ) ); - hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) | - htonl ( sizeof ( hello ) - - sizeof ( hello.type_length ) ) ); - hello.version = htons ( tls->version ); - memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); - hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) ); - i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES ) - hello.cipher_suites[i++] = suite->code; - hello.compression_methods_len = sizeof ( hello.compression_methods ); - hello.extensions_len = htons ( sizeof ( hello.extensions ) ); - hello.extensions.server_name_type = htons ( TLS_SERVER_NAME ); - hello.extensions.server_name_len - = htons ( sizeof ( hello.extensions.server_name ) ); - hello.extensions.server_name.len - = htons ( sizeof ( hello.extensions.server_name.list ) ); - hello.extensions.server_name.list[0].type = TLS_SERVER_NAME_HOST_NAME; - hello.extensions.server_name.list[0].len - = htons ( sizeof ( hello.extensions.server_name.list[0].name )); - memcpy ( hello.extensions.server_name.list[0].name, tls->name, - sizeof ( hello.extensions.server_name.list[0].name ) ); - hello.extensions.max_fragment_length_type - = htons ( TLS_MAX_FRAGMENT_LENGTH ); - hello.extensions.max_fragment_length_len - = htons ( sizeof ( hello.extensions.max_fragment_length ) ); - hello.extensions.max_fragment_length.max - = TLS_MAX_FRAGMENT_LENGTH_4096; - hello.extensions.signature_algorithms_type - = htons ( TLS_SIGNATURE_ALGORITHMS ); - hello.extensions.signature_algorithms_len - = htons ( sizeof ( hello.extensions.signature_algorithms ) ); - hello.extensions.signature_algorithms.len - = htons ( sizeof ( hello.extensions.signature_algorithms.code)); - i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS ) - hello.extensions.signature_algorithms.code[i++] = sighash->code; - - return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); -} - -/** - * Transmit Certificate record - * - * @v tls TLS session - * @ret rc Return status code - */ -static int tls_send_certificate ( struct tls_session *tls ) { - struct { - uint32_t type_length; - tls24_t length; - struct { - tls24_t length; - uint8_t data[ tls->cert->raw.len ]; - } __attribute__ (( packed )) certificates[1]; - } __attribute__ (( packed )) *certificate; - int rc; - - /* Allocate storage for Certificate record (which may be too - * large for the stack). - */ - certificate = zalloc ( sizeof ( *certificate ) ); - if ( ! certificate ) - return -ENOMEM_CERTIFICATE; - - /* Populate record */ - certificate->type_length = - ( cpu_to_le32 ( TLS_CERTIFICATE ) | - htonl ( sizeof ( *certificate ) - - sizeof ( certificate->type_length ) ) ); - tls_set_uint24 ( &certificate->length, - sizeof ( certificate->certificates ) ); - tls_set_uint24 ( &certificate->certificates[0].length, - sizeof ( certificate->certificates[0].data ) ); - memcpy ( certificate->certificates[0].data, - tls->cert->raw.data, - sizeof ( certificate->certificates[0].data ) ); - - /* Transmit record */ - rc = tls_send_handshake ( tls, certificate, sizeof ( *certificate ) ); - - /* Free record */ - free ( certificate ); - - return rc; -} - -/** - * Transmit Client Key Exchange record - * - * @v tls TLS session - * @ret rc Return status code - */ -static int tls_send_client_key_exchange ( struct tls_session *tls ) { - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; - struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; - size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx ); - struct { - uint32_t type_length; - uint16_t encrypted_pre_master_secret_len; - uint8_t encrypted_pre_master_secret[max_len]; - } __attribute__ (( packed )) key_xchg; - size_t unused; - int len; - int rc; - - /* Encrypt pre-master secret using server's public key */ - memset ( &key_xchg, 0, sizeof ( key_xchg ) ); - len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx, - &tls->pre_master_secret, - sizeof ( tls->pre_master_secret ), - key_xchg.encrypted_pre_master_secret ); - if ( len < 0 ) { - rc = len; - DBGC ( tls, "TLS %p could not encrypt pre-master secret: %s\n", - tls, strerror ( rc ) ); - return rc; - } - unused = ( max_len - len ); - key_xchg.type_length = - ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | - htonl ( sizeof ( key_xchg ) - - sizeof ( key_xchg.type_length ) - unused ) ); - key_xchg.encrypted_pre_master_secret_len = - htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) - - unused ); - - return tls_send_handshake ( tls, &key_xchg, - ( sizeof ( key_xchg ) - unused ) ); -} - -/** - * Transmit Certificate Verify record - * - * @v tls TLS session - * @ret rc Return status code - */ -static int tls_send_certificate_verify ( struct tls_session *tls ) { - struct digest_algorithm *digest = tls->handshake_digest; - struct x509_certificate *cert = tls->cert; - struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey; - uint8_t digest_out[ digest->digestsize ]; - uint8_t ctx[ pubkey->ctxsize ]; - struct tls_signature_hash_algorithm *sig_hash = NULL; - int rc; - - /* Generate digest to be signed */ - tls_verify_handshake ( tls, digest_out ); - - /* Initialise public-key algorithm */ - if ( ( rc = pubkey_init ( pubkey, ctx, private_key.data, - private_key.len ) ) != 0 ) { - DBGC ( tls, "TLS %p could not initialise %s client private " - "key: %s\n", tls, pubkey->name, strerror ( rc ) ); - goto err_pubkey_init; - } - - /* TLSv1.2 and later use explicit algorithm identifiers */ - if ( tls->version >= TLS_VERSION_TLS_1_2 ) { - sig_hash = tls_signature_hash_algorithm ( pubkey, digest ); - if ( ! sig_hash ) { - DBGC ( tls, "TLS %p could not identify (%s,%s) " - "signature and hash algorithm\n", tls, - pubkey->name, digest->name ); - rc = -ENOTSUP_SIG_HASH; - goto err_sig_hash; - } - } - - /* Generate and transmit record */ - { - size_t max_len = pubkey_max_len ( pubkey, ctx ); - int use_sig_hash = ( ( sig_hash == NULL ) ? 0 : 1 ); - struct { - uint32_t type_length; - struct tls_signature_hash_id sig_hash[use_sig_hash]; - uint16_t signature_len; - uint8_t signature[max_len]; - } __attribute__ (( packed )) certificate_verify; - size_t unused; - int len; - - /* Sign digest */ - len = pubkey_sign ( pubkey, ctx, digest, digest_out, - certificate_verify.signature ); - if ( len < 0 ) { - rc = len; - DBGC ( tls, "TLS %p could not sign %s digest using %s " - "client private key: %s\n", tls, digest->name, - pubkey->name, strerror ( rc ) ); - goto err_pubkey_sign; - } - unused = ( max_len - len ); - - /* Construct Certificate Verify record */ - certificate_verify.type_length = - ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) | - htonl ( sizeof ( certificate_verify ) - - sizeof ( certificate_verify.type_length ) - - unused ) ); - if ( use_sig_hash ) { - memcpy ( &certificate_verify.sig_hash[0], - &sig_hash->code, - sizeof ( certificate_verify.sig_hash[0] ) ); - } - certificate_verify.signature_len = - htons ( sizeof ( certificate_verify.signature ) - - unused ); - - /* Transmit record */ - rc = tls_send_handshake ( tls, &certificate_verify, - ( sizeof ( certificate_verify ) - unused ) ); - } - - err_pubkey_sign: - err_sig_hash: - pubkey_final ( pubkey, ctx ); - err_pubkey_init: - return rc; -} - -/** - * Transmit Change Cipher record - * - * @v tls TLS session - * @ret rc Return status code - */ -static int tls_send_change_cipher ( struct tls_session *tls ) { - static const uint8_t change_cipher[1] = { 1 }; - return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER, - change_cipher, sizeof ( change_cipher ) ); -} - -/** - * Transmit Finished record - * - * @v tls TLS session - * @ret rc Return status code - */ -static int tls_send_finished ( struct tls_session *tls ) { - struct digest_algorithm *digest = tls->handshake_digest; - struct { - uint32_t type_length; - uint8_t verify_data[12]; - } __attribute__ (( packed )) finished; - uint8_t digest_out[ digest->digestsize ]; - int rc; - - /* Construct record */ - memset ( &finished, 0, sizeof ( finished ) ); - finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) | - htonl ( sizeof ( finished ) - - sizeof ( finished.type_length ) ) ); - tls_verify_handshake ( tls, digest_out ); - tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - finished.verify_data, sizeof ( finished.verify_data ), - "client finished", digest_out, sizeof ( digest_out ) ); - - /* Transmit record */ - if ( ( rc = tls_send_handshake ( tls, &finished, - sizeof ( finished ) ) ) != 0 ) - return rc; - - /* Mark client as finished */ - pending_put ( &tls->client_negotiation ); - - return 0; -} - -/** - * Receive new Change Cipher record - * - * @v tls TLS session - * @v data Plaintext record - * @v len Length of plaintext record - * @ret rc Return status code - */ -static int tls_new_change_cipher ( struct tls_session *tls, - const void *data, size_t len ) { - int rc; - - if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) { - DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_CHANGE_CIPHER; - } - - if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending, - &tls->rx_cipherspec ) ) != 0 ) { - DBGC ( tls, "TLS %p could not activate RX cipher: %s\n", - tls, strerror ( rc ) ); - return rc; - } - tls->rx_seq = ~( ( uint64_t ) 0 ); - - return 0; -} - -/** - * Receive new Alert record - * - * @v tls TLS session - * @v data Plaintext record - * @v len Length of plaintext record - * @ret rc Return status code - */ -static int tls_new_alert ( struct tls_session *tls, const void *data, - size_t len ) { - const struct { - uint8_t level; - uint8_t description; - char next[0]; - } __attribute__ (( packed )) *alert = data; - const void *end = alert->next; - - /* Sanity check */ - if ( end != ( data + len ) ) { - DBGC ( tls, "TLS %p received overlength Alert\n", tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_ALERT; - } - - switch ( alert->level ) { - case TLS_ALERT_WARNING: - DBGC ( tls, "TLS %p received warning alert %d\n", - tls, alert->description ); - return 0; - case TLS_ALERT_FATAL: - DBGC ( tls, "TLS %p received fatal alert %d\n", - tls, alert->description ); - return -EPERM_ALERT; - default: - DBGC ( tls, "TLS %p received unknown alert level %d" - "(alert %d)\n", tls, alert->level, alert->description ); - return -EIO_ALERT; - } -} - -/** - * Receive new Server Hello handshake record - * - * @v tls TLS session - * @v data Plaintext handshake record - * @v len Length of plaintext handshake record - * @ret rc Return status code - */ -static int tls_new_server_hello ( struct tls_session *tls, - const void *data, size_t len ) { - const struct { - uint16_t version; - uint8_t random[32]; - uint8_t session_id_len; - char next[0]; - } __attribute__ (( packed )) *hello_a = data; - const struct { - uint8_t session_id[hello_a->session_id_len]; - uint16_t cipher_suite; - uint8_t compression_method; - char next[0]; - } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next; - const void *end = hello_b->next; - uint16_t version; - int rc; - - /* Sanity check */ - if ( end > ( data + len ) ) { - DBGC ( tls, "TLS %p received underlength Server Hello\n", tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_HELLO; - } - - /* Check and store protocol version */ - version = ntohs ( hello_a->version ); - if ( version < TLS_VERSION_TLS_1_0 ) { - DBGC ( tls, "TLS %p does not support protocol version %d.%d\n", - tls, ( version >> 8 ), ( version & 0xff ) ); - return -ENOTSUP_VERSION; - } - if ( version > tls->version ) { - DBGC ( tls, "TLS %p server attempted to illegally upgrade to " - "protocol version %d.%d\n", - tls, ( version >> 8 ), ( version & 0xff ) ); - return -EPROTO_VERSION; - } - tls->version = version; - DBGC ( tls, "TLS %p using protocol version %d.%d\n", - tls, ( version >> 8 ), ( version & 0xff ) ); - - /* Use MD5+SHA1 digest algorithm for handshake verification - * for versions earlier than TLSv1.2. - */ - if ( tls->version < TLS_VERSION_TLS_1_2 ) { - tls->handshake_digest = &md5_sha1_algorithm; - tls->handshake_ctx = tls->handshake_md5_sha1_ctx; - } - - /* Copy out server random bytes */ - memcpy ( &tls->server_random, &hello_a->random, - sizeof ( tls->server_random ) ); - - /* Select cipher suite */ - if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) - return rc; - - /* Generate secrets */ - tls_generate_master_secret ( tls ); - if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Parse certificate chain - * - * @v tls TLS session - * @v data Certificate chain - * @v len Length of certificate chain - * @ret rc Return status code - */ -static int tls_parse_chain ( struct tls_session *tls, - const void *data, size_t len ) { - const void *end = ( data + len ); - const struct { - tls24_t length; - uint8_t data[0]; - } __attribute__ (( packed )) *certificate; - size_t certificate_len; - struct x509_certificate *cert; - const void *next; - int rc; - - /* Free any existing certificate chain */ - x509_chain_put ( tls->chain ); - tls->chain = NULL; - - /* Create certificate chain */ - tls->chain = x509_alloc_chain(); - if ( ! tls->chain ) { - rc = -ENOMEM_CHAIN; - goto err_alloc_chain; - } - - /* Add certificates to chain */ - while ( data < end ) { - - /* Extract raw certificate data */ - certificate = data; - certificate_len = tls_uint24 ( &certificate->length ); - next = ( certificate->data + certificate_len ); - if ( next > end ) { - DBGC ( tls, "TLS %p overlength certificate:\n", tls ); - DBGC_HDA ( tls, 0, data, ( end - data ) ); - rc = -EINVAL_CERTIFICATE; - goto err_overlength; - } - - /* Add certificate to chain */ - if ( ( rc = x509_append_raw ( tls->chain, certificate->data, - certificate_len ) ) != 0 ) { - DBGC ( tls, "TLS %p could not append certificate: %s\n", - tls, strerror ( rc ) ); - DBGC_HDA ( tls, 0, data, ( end - data ) ); - goto err_parse; - } - cert = x509_last ( tls->chain ); - DBGC ( tls, "TLS %p found certificate %s\n", - tls, x509_name ( cert ) ); - - /* Move to next certificate in list */ - data = next; - } - - return 0; - - err_parse: - err_overlength: - x509_chain_put ( tls->chain ); - tls->chain = NULL; - err_alloc_chain: - return rc; -} - -/** - * Receive new Certificate handshake record - * - * @v tls TLS session - * @v data Plaintext handshake record - * @v len Length of plaintext handshake record - * @ret rc Return status code - */ -static int tls_new_certificate ( struct tls_session *tls, - const void *data, size_t len ) { - const struct { - tls24_t length; - uint8_t certificates[0]; - } __attribute__ (( packed )) *certificate = data; - size_t certificates_len = tls_uint24 ( &certificate->length ); - const void *end = ( certificate->certificates + certificates_len ); - int rc; - - /* Sanity check */ - if ( end != ( data + len ) ) { - DBGC ( tls, "TLS %p received overlength Server Certificate\n", - tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_CERTIFICATES; - } - - /* Parse certificate chain */ - if ( ( rc = tls_parse_chain ( tls, certificate->certificates, - certificates_len ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Receive new Certificate Request handshake record - * - * @v tls TLS session - * @v data Plaintext handshake record - * @v len Length of plaintext handshake record - * @ret rc Return status code - */ -static int tls_new_certificate_request ( struct tls_session *tls, - const void *data __unused, - size_t len __unused ) { - - /* We can only send a single certificate, so there is no point - * in parsing the Certificate Request. - */ - - /* Free any existing client certificate */ - x509_put ( tls->cert ); - - /* Determine client certificate to be sent */ - tls->cert = certstore_find_key ( &private_key ); - if ( ! tls->cert ) { - DBGC ( tls, "TLS %p could not find certificate corresponding " - "to private key\n", tls ); - return -EPERM_CLIENT_CERT; - } - x509_get ( tls->cert ); - DBGC ( tls, "TLS %p sending client certificate %s\n", - tls, x509_name ( tls->cert ) ); - - return 0; -} - -/** - * Receive new Server Hello Done handshake record - * - * @v tls TLS session - * @v data Plaintext handshake record - * @v len Length of plaintext handshake record - * @ret rc Return status code - */ -static int tls_new_server_hello_done ( struct tls_session *tls, - const void *data, size_t len ) { - const struct { - char next[0]; - } __attribute__ (( packed )) *hello_done = data; - const void *end = hello_done->next; - int rc; - - /* Sanity check */ - if ( end != ( data + len ) ) { - DBGC ( tls, "TLS %p received overlength Server Hello Done\n", - tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_HELLO_DONE; - } - - /* Begin certificate validation */ - if ( ( rc = create_validator ( &tls->validator, tls->chain ) ) != 0 ) { - DBGC ( tls, "TLS %p could not start certificate validation: " - "%s\n", tls, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Receive new Finished handshake record - * - * @v tls TLS session - * @v data Plaintext handshake record - * @v len Length of plaintext handshake record - * @ret rc Return status code - */ -static int tls_new_finished ( struct tls_session *tls, - const void *data, size_t len ) { - struct digest_algorithm *digest = tls->handshake_digest; - const struct { - uint8_t verify_data[12]; - char next[0]; - } __attribute__ (( packed )) *finished = data; - const void *end = finished->next; - uint8_t digest_out[ digest->digestsize ]; - uint8_t verify_data[ sizeof ( finished->verify_data ) ]; - - /* Sanity check */ - if ( end != ( data + len ) ) { - DBGC ( tls, "TLS %p received overlength Finished\n", tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_FINISHED; - } - - /* Verify data */ - tls_verify_handshake ( tls, digest_out ); - tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), - verify_data, sizeof ( verify_data ), "server finished", - digest_out, sizeof ( digest_out ) ); - if ( memcmp ( verify_data, finished->verify_data, - sizeof ( verify_data ) ) != 0 ) { - DBGC ( tls, "TLS %p verification failed\n", tls ); - return -EPERM_VERIFY; - } - - /* Mark server as finished */ - pending_put ( &tls->server_negotiation ); - - /* Send notification of a window change */ - xfer_window_changed ( &tls->plainstream ); - - return 0; -} - -/** - * Receive new Handshake record - * - * @v tls TLS session - * @v data Plaintext record - * @v len Length of plaintext record - * @ret rc Return status code - */ -static int tls_new_handshake ( struct tls_session *tls, - const void *data, size_t len ) { - const void *end = ( data + len ); - int rc; - - while ( data != end ) { - const struct { - uint8_t type; - tls24_t length; - uint8_t payload[0]; - } __attribute__ (( packed )) *handshake = data; - const void *payload = &handshake->payload; - size_t payload_len = tls_uint24 ( &handshake->length ); - const void *next = ( payload + payload_len ); - - /* Sanity check */ - if ( next > end ) { - DBGC ( tls, "TLS %p received overlength Handshake\n", - tls ); - DBGC_HD ( tls, data, len ); - return -EINVAL_HANDSHAKE; - } - - switch ( handshake->type ) { - case TLS_SERVER_HELLO: - rc = tls_new_server_hello ( tls, payload, payload_len ); - break; - case TLS_CERTIFICATE: - rc = tls_new_certificate ( tls, payload, payload_len ); - break; - case TLS_CERTIFICATE_REQUEST: - rc = tls_new_certificate_request ( tls, payload, - payload_len ); - break; - case TLS_SERVER_HELLO_DONE: - rc = tls_new_server_hello_done ( tls, payload, - payload_len ); - break; - case TLS_FINISHED: - rc = tls_new_finished ( tls, payload, payload_len ); - break; - default: - DBGC ( tls, "TLS %p ignoring handshake type %d\n", - tls, handshake->type ); - rc = 0; - break; - } - - /* Add to handshake digest (except for Hello Requests, - * which are explicitly excluded). - */ - if ( handshake->type != TLS_HELLO_REQUEST ) - tls_add_handshake ( tls, data, - sizeof ( *handshake ) + - payload_len ); - - /* Abort on failure */ - if ( rc != 0 ) - return rc; - - /* Move to next handshake record */ - data = next; - } - - return 0; -} - -/** - * Receive new record - * - * @v tls TLS session - * @v type Record type - * @v rx_data List of received data buffers - * @ret rc Return status code - */ -static int tls_new_record ( struct tls_session *tls, unsigned int type, - struct list_head *rx_data ) { - struct io_buffer *iobuf; - int ( * handler ) ( struct tls_session *tls, const void *data, - size_t len ); - int rc; - - /* Deliver data records to the plainstream interface */ - if ( type == TLS_TYPE_DATA ) { - - /* Fail unless we are ready to receive data */ - if ( ! tls_ready ( tls ) ) - return -ENOTCONN; - - /* Deliver each I/O buffer in turn */ - while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer, - list ) ) ) { - list_del ( &iobuf->list ); - if ( ( rc = xfer_deliver_iob ( &tls->plainstream, - iobuf ) ) != 0 ) { - DBGC ( tls, "TLS %p could not deliver data: " - "%s\n", tls, strerror ( rc ) ); - return rc; - } - } - return 0; - } - - /* For all other records, merge into a single I/O buffer */ - iobuf = iob_concatenate ( rx_data ); - if ( ! iobuf ) { - DBGC ( tls, "TLS %p could not concatenate non-data record " - "type %d\n", tls, type ); - return -ENOMEM_RX_CONCAT; - } - - /* Determine handler */ - switch ( type ) { - case TLS_TYPE_CHANGE_CIPHER: - handler = tls_new_change_cipher; - break; - case TLS_TYPE_ALERT: - handler = tls_new_alert; - break; - case TLS_TYPE_HANDSHAKE: - handler = tls_new_handshake; - break; - default: - /* RFC4346 says that we should just ignore unknown - * record types. - */ - handler = NULL; - DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type ); - break; - } - - /* Handle record and free I/O buffer */ - rc = ( handler ? handler ( tls, iobuf->data, iob_len ( iobuf ) ) : 0 ); - free_iob ( iobuf ); - return rc; -} - -/****************************************************************************** - * - * Record encryption/decryption - * - ****************************************************************************** - */ - -/** - * Initialise HMAC - * - * @v cipherspec Cipher specification - * @v ctx Context - * @v seq Sequence number - * @v tlshdr TLS header - */ -static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx, - uint64_t seq, struct tls_header *tlshdr ) { - struct digest_algorithm *digest = cipherspec->suite->digest; - - hmac_init ( digest, ctx, cipherspec->mac_secret, &digest->digestsize ); - seq = cpu_to_be64 ( seq ); - hmac_update ( digest, ctx, &seq, sizeof ( seq ) ); - hmac_update ( digest, ctx, tlshdr, sizeof ( *tlshdr ) ); -} - -/** - * Update HMAC - * - * @v cipherspec Cipher specification - * @v ctx Context - * @v data Data - * @v len Length of data - */ -static void tls_hmac_update ( struct tls_cipherspec *cipherspec, void *ctx, - const void *data, size_t len ) { - struct digest_algorithm *digest = cipherspec->suite->digest; - - hmac_update ( digest, ctx, data, len ); -} - -/** - * Finalise HMAC - * - * @v cipherspec Cipher specification - * @v ctx Context - * @v mac HMAC to fill in - */ -static void tls_hmac_final ( struct tls_cipherspec *cipherspec, void *ctx, - void *hmac ) { - struct digest_algorithm *digest = cipherspec->suite->digest; - - hmac_final ( digest, ctx, cipherspec->mac_secret, - &digest->digestsize, hmac ); -} - -/** - * Calculate HMAC - * - * @v cipherspec Cipher specification - * @v seq Sequence number - * @v tlshdr TLS header - * @v data Data - * @v len Length of data - * @v mac HMAC to fill in - */ -static void tls_hmac ( struct tls_cipherspec *cipherspec, - uint64_t seq, struct tls_header *tlshdr, - const void *data, size_t len, void *hmac ) { - struct digest_algorithm *digest = cipherspec->suite->digest; - uint8_t ctx[digest->ctxsize]; - - tls_hmac_init ( cipherspec, ctx, seq, tlshdr ); - tls_hmac_update ( cipherspec, ctx, data, len ); - tls_hmac_final ( cipherspec, ctx, hmac ); -} - -/** - * Allocate and assemble stream-ciphered record from data and MAC portions - * - * @v tls TLS session - * @ret data Data - * @ret len Length of data - * @ret digest MAC digest - * @ret plaintext_len Length of plaintext record - * @ret plaintext Allocated plaintext record - */ -static void * __malloc tls_assemble_stream ( struct tls_session *tls, - const void *data, size_t len, - void *digest, size_t *plaintext_len ) { - size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize; - void *plaintext; - void *content; - void *mac; - - /* Calculate stream-ciphered struct length */ - *plaintext_len = ( len + mac_len ); - - /* Allocate stream-ciphered struct */ - plaintext = malloc ( *plaintext_len ); - if ( ! plaintext ) - return NULL; - content = plaintext; - mac = ( content + len ); - - /* Fill in stream-ciphered struct */ - memcpy ( content, data, len ); - memcpy ( mac, digest, mac_len ); - - return plaintext; -} - -/** - * Allocate and assemble block-ciphered record from data and MAC portions - * - * @v tls TLS session - * @ret data Data - * @ret len Length of data - * @ret digest MAC digest - * @ret plaintext_len Length of plaintext record - * @ret plaintext Allocated plaintext record - */ -static void * tls_assemble_block ( struct tls_session *tls, - const void *data, size_t len, - void *digest, size_t *plaintext_len ) { - size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize; - size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize; - size_t iv_len; - size_t padding_len; - void *plaintext; - void *iv; - void *content; - void *mac; - void *padding; - - /* TLSv1.1 and later use an explicit IV */ - iv_len = ( ( tls->version >= TLS_VERSION_TLS_1_1 ) ? blocksize : 0 ); - - /* Calculate block-ciphered struct length */ - padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) ); - *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 ); - - /* Allocate block-ciphered struct */ - plaintext = malloc ( *plaintext_len ); - if ( ! plaintext ) - return NULL; - iv = plaintext; - content = ( iv + iv_len ); - mac = ( content + len ); - padding = ( mac + mac_len ); - - /* Fill in block-ciphered struct */ - tls_generate_random ( tls, iv, iv_len ); - memcpy ( content, data, len ); - memcpy ( mac, digest, mac_len ); - memset ( padding, padding_len, ( padding_len + 1 ) ); - - return plaintext; -} - -/** - * Send plaintext record - * - * @v tls TLS session - * @v type Record type - * @v data Plaintext record - * @v len Length of plaintext record - * @ret rc Return status code - */ -static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, - const void *data, size_t len ) { - struct tls_header plaintext_tlshdr; - struct tls_header *tlshdr; - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec; - struct cipher_algorithm *cipher = cipherspec->suite->cipher; - void *plaintext = NULL; - size_t plaintext_len; - struct io_buffer *ciphertext = NULL; - size_t ciphertext_len; - size_t mac_len = cipherspec->suite->digest->digestsize; - uint8_t mac[mac_len]; - int rc; - - /* Construct header */ - plaintext_tlshdr.type = type; - plaintext_tlshdr.version = htons ( tls->version ); - plaintext_tlshdr.length = htons ( len ); - - /* Calculate MAC */ - tls_hmac ( cipherspec, tls->tx_seq, &plaintext_tlshdr, data, len, mac ); - - /* Allocate and assemble plaintext struct */ - if ( is_stream_cipher ( cipher ) ) { - plaintext = tls_assemble_stream ( tls, data, len, mac, - &plaintext_len ); - } else { - plaintext = tls_assemble_block ( tls, data, len, mac, - &plaintext_len ); - } - if ( ! plaintext ) { - DBGC ( tls, "TLS %p could not allocate %zd bytes for " - "plaintext\n", tls, plaintext_len ); - rc = -ENOMEM_TX_PLAINTEXT; - goto done; - } - - DBGC2 ( tls, "Sending plaintext data:\n" ); - DBGC2_HD ( tls, plaintext, plaintext_len ); - - /* Allocate ciphertext */ - ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len ); - ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len ); - if ( ! ciphertext ) { - DBGC ( tls, "TLS %p could not allocate %zd bytes for " - "ciphertext\n", tls, ciphertext_len ); - rc = -ENOMEM_TX_CIPHERTEXT; - goto done; - } - - /* Assemble ciphertext */ - tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) ); - tlshdr->type = type; - tlshdr->version = htons ( tls->version ); - tlshdr->length = htons ( plaintext_len ); - memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx, - cipher->ctxsize ); - cipher_encrypt ( cipher, cipherspec->cipher_next_ctx, plaintext, - iob_put ( ciphertext, plaintext_len ), plaintext_len ); - - /* Free plaintext as soon as possible to conserve memory */ - free ( plaintext ); - plaintext = NULL; - - /* Send ciphertext */ - if ( ( rc = xfer_deliver_iob ( &tls->cipherstream, - iob_disown ( ciphertext ) ) ) != 0 ) { - DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n", - tls, strerror ( rc ) ); - goto done; - } - - /* Update TX state machine to next record */ - tls->tx_seq += 1; - memcpy ( tls->tx_cipherspec.cipher_ctx, - tls->tx_cipherspec.cipher_next_ctx, cipher->ctxsize ); - - done: - free ( plaintext ); - free_iob ( ciphertext ); - return rc; -} - -/** - * Split stream-ciphered record into data and MAC portions - * - * @v tls TLS session - * @v rx_data List of received data buffers - * @v mac MAC to fill in - * @ret rc Return status code - */ -static int tls_split_stream ( struct tls_session *tls, - struct list_head *rx_data, void **mac ) { - size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; - struct io_buffer *iobuf; - - /* Extract MAC */ - iobuf = list_last_entry ( rx_data, struct io_buffer, list ); - assert ( iobuf != NULL ); - if ( iob_len ( iobuf ) < mac_len ) { - DBGC ( tls, "TLS %p received underlength MAC\n", tls ); - DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) ); - return -EINVAL_STREAM; - } - iob_unput ( iobuf, mac_len ); - *mac = iobuf->tail; - - return 0; -} - -/** - * Split block-ciphered record into data and MAC portions - * - * @v tls TLS session - * @v rx_data List of received data buffers - * @v mac MAC to fill in - * @ret rc Return status code - */ -static int tls_split_block ( struct tls_session *tls, - struct list_head *rx_data, void **mac ) { - size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize; - struct io_buffer *iobuf; - size_t iv_len; - uint8_t *padding_final; - uint8_t *padding; - size_t padding_len; - - /* TLSv1.1 and later use an explicit IV */ - iobuf = list_first_entry ( rx_data, struct io_buffer, list ); - iv_len = ( ( tls->version >= TLS_VERSION_TLS_1_1 ) ? - tls->rx_cipherspec.suite->cipher->blocksize : 0 ); - if ( iob_len ( iobuf ) < iv_len ) { - DBGC ( tls, "TLS %p received underlength IV\n", tls ); - DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) ); - return -EINVAL_BLOCK; - } - iob_pull ( iobuf, iv_len ); - - /* Extract and verify padding */ - iobuf = list_last_entry ( rx_data, struct io_buffer, list ); - padding_final = ( iobuf->tail - 1 ); - padding_len = *padding_final; - if ( ( padding_len + 1 ) > iob_len ( iobuf ) ) { - DBGC ( tls, "TLS %p received underlength padding\n", tls ); - DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) ); - return -EINVAL_BLOCK; - } - iob_unput ( iobuf, ( padding_len + 1 ) ); - for ( padding = iobuf->tail ; padding < padding_final ; padding++ ) { - if ( *padding != padding_len ) { - DBGC ( tls, "TLS %p received bad padding\n", tls ); - DBGC_HD ( tls, padding, padding_len ); - return -EINVAL_PADDING; - } - } - - /* Extract MAC */ - if ( iob_len ( iobuf ) < mac_len ) { - DBGC ( tls, "TLS %p received underlength MAC\n", tls ); - DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) ); - return -EINVAL_BLOCK; - } - iob_unput ( iobuf, mac_len ); - *mac = iobuf->tail; - - return 0; -} - -/** - * Receive new ciphertext record - * - * @v tls TLS session - * @v tlshdr Record header - * @v rx_data List of received data buffers - * @ret rc Return status code - */ -static int tls_new_ciphertext ( struct tls_session *tls, - struct tls_header *tlshdr, - struct list_head *rx_data ) { - struct tls_header plaintext_tlshdr; - struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; - struct cipher_algorithm *cipher = cipherspec->suite->cipher; - struct digest_algorithm *digest = cipherspec->suite->digest; - uint8_t ctx[digest->ctxsize]; - uint8_t verify_mac[digest->digestsize]; - struct io_buffer *iobuf; - void *mac; - size_t len = 0; - int rc; - - /* Decrypt the received data */ - list_for_each_entry ( iobuf, &tls->rx_data, list ) { - cipher_decrypt ( cipher, cipherspec->cipher_ctx, - iobuf->data, iobuf->data, iob_len ( iobuf ) ); - } - - /* Split record into content and MAC */ - if ( is_stream_cipher ( cipher ) ) { - if ( ( rc = tls_split_stream ( tls, rx_data, &mac ) ) != 0 ) - return rc; - } else { - if ( ( rc = tls_split_block ( tls, rx_data, &mac ) ) != 0 ) - return rc; - } - - /* Calculate total length */ - DBGC2 ( tls, "Received plaintext data:\n" ); - list_for_each_entry ( iobuf, rx_data, list ) { - DBGC2_HD ( tls, iobuf->data, iob_len ( iobuf ) ); - len += iob_len ( iobuf ); - } - - /* Verify MAC */ - plaintext_tlshdr.type = tlshdr->type; - plaintext_tlshdr.version = tlshdr->version; - plaintext_tlshdr.length = htons ( len ); - tls_hmac_init ( cipherspec, ctx, tls->rx_seq, &plaintext_tlshdr ); - list_for_each_entry ( iobuf, rx_data, list ) { - tls_hmac_update ( cipherspec, ctx, iobuf->data, - iob_len ( iobuf ) ); - } - tls_hmac_final ( cipherspec, ctx, verify_mac ); - if ( memcmp ( mac, verify_mac, sizeof ( verify_mac ) ) != 0 ) { - DBGC ( tls, "TLS %p failed MAC verification\n", tls ); - return -EINVAL_MAC; - } - - /* Process plaintext record */ - if ( ( rc = tls_new_record ( tls, tlshdr->type, rx_data ) ) != 0 ) - return rc; - - return 0; -} - -/****************************************************************************** - * - * Plaintext stream operations - * - ****************************************************************************** - */ - -/** - * Check flow control window - * - * @v tls TLS session - * @ret len Length of window - */ -static size_t tls_plainstream_window ( struct tls_session *tls ) { - - /* Block window unless we are ready to accept data */ - if ( ! tls_ready ( tls ) ) - return 0; - - return xfer_window ( &tls->cipherstream ); -} - -/** - * Deliver datagram as raw data - * - * @v tls TLS session - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int tls_plainstream_deliver ( struct tls_session *tls, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - int rc; - - /* Refuse unless we are ready to accept data */ - if ( ! tls_ready ( tls ) ) { - rc = -ENOTCONN; - goto done; - } - - if ( ( rc = tls_send_plaintext ( tls, TLS_TYPE_DATA, iobuf->data, - iob_len ( iobuf ) ) ) != 0 ) - goto done; - - done: - free_iob ( iobuf ); - return rc; -} - -/** TLS plaintext stream interface operations */ -static struct interface_operation tls_plainstream_ops[] = { - INTF_OP ( xfer_deliver, struct tls_session *, tls_plainstream_deliver ), - INTF_OP ( xfer_window, struct tls_session *, tls_plainstream_window ), - INTF_OP ( intf_close, struct tls_session *, tls_close ), -}; - -/** TLS plaintext stream interface descriptor */ -static struct interface_descriptor tls_plainstream_desc = - INTF_DESC_PASSTHRU ( struct tls_session, plainstream, - tls_plainstream_ops, cipherstream ); - -/****************************************************************************** - * - * Ciphertext stream operations - * - ****************************************************************************** - */ - -/** - * Handle received TLS header - * - * @v tls TLS session - * @ret rc Returned status code - */ -static int tls_newdata_process_header ( struct tls_session *tls ) { - size_t data_len = ntohs ( tls->rx_header.length ); - size_t remaining = data_len; - size_t frag_len; - struct io_buffer *iobuf; - struct io_buffer *tmp; - int rc; - - /* Allocate data buffers now that we know the length */ - assert ( list_empty ( &tls->rx_data ) ); - while ( remaining ) { - - /* Calculate fragment length. Ensure that no block is - * smaller than TLS_RX_MIN_BUFSIZE (by increasing the - * allocation length if necessary). - */ - frag_len = remaining; - if ( frag_len > TLS_RX_BUFSIZE ) - frag_len = TLS_RX_BUFSIZE; - remaining -= frag_len; - if ( remaining < TLS_RX_MIN_BUFSIZE ) { - frag_len += remaining; - remaining = 0; - } - - /* Allocate buffer */ - iobuf = alloc_iob_raw ( frag_len, TLS_RX_ALIGN, 0 ); - if ( ! iobuf ) { - DBGC ( tls, "TLS %p could not allocate %zd of %zd " - "bytes for receive buffer\n", tls, - remaining, data_len ); - rc = -ENOMEM_RX_DATA; - goto err; - } - - /* Ensure tailroom is exactly what we asked for. This - * will result in unaligned I/O buffers when the - * fragment length is unaligned, which can happen only - * before we switch to using a block cipher. - */ - iob_reserve ( iobuf, ( iob_tailroom ( iobuf ) - frag_len ) ); - - /* Add I/O buffer to list */ - list_add_tail ( &iobuf->list, &tls->rx_data ); - } - - /* Move to data state */ - tls->rx_state = TLS_RX_DATA; - - return 0; - - err: - list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - } - return rc; -} - -/** - * Handle received TLS data payload - * - * @v tls TLS session - * @ret rc Returned status code - */ -static int tls_newdata_process_data ( struct tls_session *tls ) { - struct io_buffer *iobuf; - int rc; - - /* Move current buffer to end of list */ - iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list ); - list_del ( &iobuf->list ); - list_add_tail ( &iobuf->list, &tls->rx_data ); - - /* Continue receiving data if any space remains */ - iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list ); - if ( iob_tailroom ( iobuf ) ) - return 0; - - /* Process record */ - if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header, - &tls->rx_data ) ) != 0 ) - return rc; - - /* Increment RX sequence number */ - tls->rx_seq += 1; - - /* Return to header state */ - assert ( list_empty ( &tls->rx_data ) ); - tls->rx_state = TLS_RX_HEADER; - iob_unput ( &tls->rx_header_iobuf, sizeof ( tls->rx_header ) ); - - return 0; -} - -/** - * Receive new ciphertext - * - * @v tls TLS session - * @v iobuf I/O buffer - * @v meta Data transfer metadat - * @ret rc Return status code - */ -static int tls_cipherstream_deliver ( struct tls_session *tls, - struct io_buffer *iobuf, - struct xfer_metadata *xfer __unused ) { - size_t frag_len; - int ( * process ) ( struct tls_session *tls ); - struct io_buffer *dest; - int rc; - - while ( iob_len ( iobuf ) ) { - - /* Select buffer according to current state */ - switch ( tls->rx_state ) { - case TLS_RX_HEADER: - dest = &tls->rx_header_iobuf; - process = tls_newdata_process_header; - break; - case TLS_RX_DATA: - dest = list_first_entry ( &tls->rx_data, - struct io_buffer, list ); - assert ( dest != NULL ); - process = tls_newdata_process_data; - break; - default: - assert ( 0 ); - rc = -EINVAL_RX_STATE; - goto done; - } - - /* Copy data portion to buffer */ - frag_len = iob_len ( iobuf ); - if ( frag_len > iob_tailroom ( dest ) ) - frag_len = iob_tailroom ( dest ); - memcpy ( iob_put ( dest, frag_len ), iobuf->data, frag_len ); - iob_pull ( iobuf, frag_len ); - - /* Process data if buffer is now full */ - if ( iob_tailroom ( dest ) == 0 ) { - if ( ( rc = process ( tls ) ) != 0 ) { - tls_close ( tls, rc ); - goto done; - } - } - } - rc = 0; - - done: - free_iob ( iobuf ); - return rc; -} - -/** TLS ciphertext stream interface operations */ -static struct interface_operation tls_cipherstream_ops[] = { - INTF_OP ( xfer_deliver, struct tls_session *, - tls_cipherstream_deliver ), - INTF_OP ( xfer_window_changed, struct tls_session *, tls_tx_resume ), - INTF_OP ( intf_close, struct tls_session *, tls_close ), -}; - -/** TLS ciphertext stream interface descriptor */ -static struct interface_descriptor tls_cipherstream_desc = - INTF_DESC_PASSTHRU ( struct tls_session, cipherstream, - tls_cipherstream_ops, plainstream ); - -/****************************************************************************** - * - * Certificate validator - * - ****************************************************************************** - */ - -/** - * Handle certificate validation completion - * - * @v tls TLS session - * @v rc Reason for completion - */ -static void tls_validator_done ( struct tls_session *tls, int rc ) { - struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending; - struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; - struct x509_certificate *cert; - - /* Close validator interface */ - intf_restart ( &tls->validator, rc ); - - /* Check for validation failure */ - if ( rc != 0 ) { - DBGC ( tls, "TLS %p certificate validation failed: %s\n", - tls, strerror ( rc ) ); - goto err; - } - DBGC ( tls, "TLS %p certificate validation succeeded\n", tls ); - - /* Extract first certificate */ - cert = x509_first ( tls->chain ); - assert ( cert != NULL ); - - /* Verify server name */ - if ( ( rc = x509_check_name ( cert, tls->name ) ) != 0 ) { - DBGC ( tls, "TLS %p server certificate does not match %s: %s\n", - tls, tls->name, strerror ( rc ) ); - goto err; - } - - /* Initialise public key algorithm */ - if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx, - cert->subject.public_key.raw.data, - cert->subject.public_key.raw.len ) ) != 0 ) { - DBGC ( tls, "TLS %p cannot initialise public key: %s\n", - tls, strerror ( rc ) ); - goto err; - } - - /* Schedule Client Key Exchange, Change Cipher, and Finished */ - tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE | - TLS_TX_CHANGE_CIPHER | - TLS_TX_FINISHED ); - if ( tls->cert ) { - tls->tx_pending |= ( TLS_TX_CERTIFICATE | - TLS_TX_CERTIFICATE_VERIFY ); - } - tls_tx_resume ( tls ); - - return; - - err: - tls_close ( tls, rc ); - return; -} - -/** TLS certificate validator interface operations */ -static struct interface_operation tls_validator_ops[] = { - INTF_OP ( intf_close, struct tls_session *, tls_validator_done ), -}; - -/** TLS certificate validator interface descriptor */ -static struct interface_descriptor tls_validator_desc = - INTF_DESC ( struct tls_session, validator, tls_validator_ops ); - -/****************************************************************************** - * - * Controlling process - * - ****************************************************************************** - */ - -/** - * TLS TX state machine - * - * @v tls TLS session - */ -static void tls_tx_step ( struct tls_session *tls ) { - int rc; - - /* Wait for cipherstream to become ready */ - if ( ! xfer_window ( &tls->cipherstream ) ) - return; - - /* Send first pending transmission */ - if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) { - /* Send Client Hello */ - if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not send Client Hello: %s\n", - tls, strerror ( rc ) ); - goto err; - } - tls->tx_pending &= ~TLS_TX_CLIENT_HELLO; - } else if ( tls->tx_pending & TLS_TX_CERTIFICATE ) { - /* Send Certificate */ - if ( ( rc = tls_send_certificate ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p cold not send Certificate: %s\n", - tls, strerror ( rc ) ); - goto err; - } - tls->tx_pending &= ~TLS_TX_CERTIFICATE; - } else if ( tls->tx_pending & TLS_TX_CLIENT_KEY_EXCHANGE ) { - /* Send Client Key Exchange */ - if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not send Client Key " - "Exchange: %s\n", tls, strerror ( rc ) ); - goto err; - } - tls->tx_pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE; - } else if ( tls->tx_pending & TLS_TX_CERTIFICATE_VERIFY ) { - /* Send Certificate Verify */ - if ( ( rc = tls_send_certificate_verify ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not send Certificate " - "Verify: %s\n", tls, strerror ( rc ) ); - goto err; - } - tls->tx_pending &= ~TLS_TX_CERTIFICATE_VERIFY; - } else if ( tls->tx_pending & TLS_TX_CHANGE_CIPHER ) { - /* Send Change Cipher, and then change the cipher in use */ - if ( ( rc = tls_send_change_cipher ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not send Change Cipher: " - "%s\n", tls, strerror ( rc ) ); - goto err; - } - if ( ( rc = tls_change_cipher ( tls, - &tls->tx_cipherspec_pending, - &tls->tx_cipherspec )) != 0 ){ - DBGC ( tls, "TLS %p could not activate TX cipher: " - "%s\n", tls, strerror ( rc ) ); - goto err; - } - tls->tx_seq = 0; - tls->tx_pending &= ~TLS_TX_CHANGE_CIPHER; - } else if ( tls->tx_pending & TLS_TX_FINISHED ) { - /* Send Finished */ - if ( ( rc = tls_send_finished ( tls ) ) != 0 ) { - DBGC ( tls, "TLS %p could not send Finished: %s\n", - tls, strerror ( rc ) ); - goto err; - } - tls->tx_pending &= ~TLS_TX_FINISHED; - } - - /* Reschedule process if pending transmissions remain */ - if ( tls->tx_pending ) - tls_tx_resume ( tls ); - - return; - - err: - tls_close ( tls, rc ); -} - -/** TLS TX process descriptor */ -static struct process_descriptor tls_process_desc = - PROC_DESC_ONCE ( struct tls_session, process, tls_tx_step ); - -/****************************************************************************** - * - * Instantiator - * - ****************************************************************************** - */ - -int add_tls ( struct interface *xfer, const char *name, - struct interface **next ) { - struct tls_session *tls; - int rc; - - /* Allocate and initialise TLS structure */ - tls = malloc ( sizeof ( *tls ) ); - if ( ! tls ) { - rc = -ENOMEM; - goto err_alloc; - } - memset ( tls, 0, sizeof ( *tls ) ); - ref_init ( &tls->refcnt, free_tls ); - tls->name = name; - intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt ); - intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt ); - intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt ); - process_init ( &tls->process, &tls_process_desc, &tls->refcnt ); - tls->version = TLS_VERSION_TLS_1_2; - tls_clear_cipher ( tls, &tls->tx_cipherspec ); - tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); - tls_clear_cipher ( tls, &tls->rx_cipherspec ); - tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); - tls->client_random.gmt_unix_time = time ( NULL ); - if ( ( rc = tls_generate_random ( tls, &tls->client_random.random, - ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) { - goto err_random; - } - tls->pre_master_secret.version = htons ( tls->version ); - if ( ( rc = tls_generate_random ( tls, &tls->pre_master_secret.random, - ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) { - goto err_random; - } - digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx ); - digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx ); - tls->handshake_digest = &sha256_algorithm; - tls->handshake_ctx = tls->handshake_sha256_ctx; - tls->tx_pending = TLS_TX_CLIENT_HELLO; - iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0, - sizeof ( tls->rx_header ) ); - INIT_LIST_HEAD ( &tls->rx_data ); - - /* Add pending operations for server and client Finished messages */ - pending_get ( &tls->client_negotiation ); - pending_get ( &tls->server_negotiation ); - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &tls->plainstream, xfer ); - *next = &tls->cipherstream; - ref_put ( &tls->refcnt ); - return 0; - - err_random: - ref_put ( &tls->refcnt ); - err_alloc: - return rc; -} - -/* Drag in objects via add_tls() */ -REQUIRING_SYMBOL ( add_tls ); - -/* Drag in crypto configuration */ -REQUIRE_OBJECT ( config_crypto ); diff --git a/qemu/roms/ipxe/src/net/udp.c b/qemu/roms/ipxe/src/net/udp.c deleted file mode 100644 index 0f7dfb24a..000000000 --- a/qemu/roms/ipxe/src/net/udp.c +++ /dev/null @@ -1,440 +0,0 @@ -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <byteswap.h> -#include <errno.h> -#include <ipxe/tcpip.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/netdevice.h> -#include <ipxe/udp.h> - -/** @file - * - * UDP protocol - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** - * A UDP connection - * - */ -struct udp_connection { - /** Reference counter */ - struct refcnt refcnt; - /** List of UDP connections */ - struct list_head list; - - /** Data transfer interface */ - struct interface xfer; - - /** Local socket address */ - struct sockaddr_tcpip local; - /** Remote socket address */ - struct sockaddr_tcpip peer; -}; - -/** - * List of registered UDP connections - */ -static LIST_HEAD ( udp_conns ); - -/* Forward declatations */ -static struct interface_descriptor udp_xfer_desc; -struct tcpip_protocol udp_protocol __tcpip_protocol; - -/** - * Check if local UDP port is available - * - * @v port Local port number - * @ret port Local port number, or negative error - */ -static int udp_port_available ( int port ) { - struct udp_connection *udp; - - list_for_each_entry ( udp, &udp_conns, list ) { - if ( udp->local.st_port == htons ( port ) ) - return -EADDRINUSE; - } - return port; -} - -/** - * Open a UDP connection - * - * @v xfer Data transfer interface - * @v peer Peer socket address, or NULL - * @v local Local socket address, or NULL - * @v promisc Socket is promiscuous - * @ret rc Return status code - */ -static int udp_open_common ( struct interface *xfer, - struct sockaddr *peer, struct sockaddr *local, - int promisc ) { - struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; - struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; - struct udp_connection *udp; - int port; - int rc; - - /* Allocate and initialise structure */ - udp = zalloc ( sizeof ( *udp ) ); - if ( ! udp ) - return -ENOMEM; - DBGC ( udp, "UDP %p allocated\n", udp ); - ref_init ( &udp->refcnt, NULL ); - intf_init ( &udp->xfer, &udp_xfer_desc, &udp->refcnt ); - if ( st_peer ) - memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) ); - if ( st_local ) - memcpy ( &udp->local, st_local, sizeof ( udp->local ) ); - - /* Bind to local port */ - if ( ! promisc ) { - port = tcpip_bind ( st_local, udp_port_available ); - if ( port < 0 ) { - rc = port; - DBGC ( udp, "UDP %p could not bind: %s\n", - udp, strerror ( rc ) ); - goto err; - } - udp->local.st_port = htons ( port ); - DBGC ( udp, "UDP %p bound to port %d\n", - udp, ntohs ( udp->local.st_port ) ); - } - - /* Attach parent interface, transfer reference to connection - * list and return - */ - intf_plug_plug ( &udp->xfer, xfer ); - list_add ( &udp->list, &udp_conns ); - return 0; - - err: - ref_put ( &udp->refcnt ); - return rc; -} - -/** - * Open a UDP connection - * - * @v xfer Data transfer interface - * @v peer Peer socket address - * @v local Local socket address, or NULL - * @ret rc Return status code - */ -int udp_open ( struct interface *xfer, struct sockaddr *peer, - struct sockaddr *local ) { - return udp_open_common ( xfer, peer, local, 0 ); -} - -/** - * Open a promiscuous UDP connection - * - * @v xfer Data transfer interface - * @ret rc Return status code - * - * Promiscuous UDP connections are required in order to support the - * PXE API. - */ -int udp_open_promisc ( struct interface *xfer ) { - return udp_open_common ( xfer, NULL, NULL, 1 ); -} - -/** - * Close a UDP connection - * - * @v udp UDP connection - * @v rc Reason for close - */ -static void udp_close ( struct udp_connection *udp, int rc ) { - - /* Close data transfer interface */ - intf_shutdown ( &udp->xfer, rc ); - - /* Remove from list of connections and drop list's reference */ - list_del ( &udp->list ); - ref_put ( &udp->refcnt ); - - DBGC ( udp, "UDP %p closed\n", udp ); -} - -/** - * Transmit data via a UDP connection to a specified address - * - * @v udp UDP connection - * @v iobuf I/O buffer - * @v src Source address, or NULL to use default - * @v dest Destination address, or NULL to use default - * @v netdev Network device, or NULL to use default - * @ret rc Return status code - */ -static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, - struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest, - struct net_device *netdev ) { - struct udp_header *udphdr; - size_t len; - int rc; - - /* Check we can accommodate the header */ - if ( ( rc = iob_ensure_headroom ( iobuf, - MAX_LL_NET_HEADER_LEN ) ) != 0 ) { - free_iob ( iobuf ); - return rc; - } - - /* Fill in default values if not explicitly provided */ - if ( ! src ) - src = &udp->local; - if ( ! dest ) - dest = &udp->peer; - - /* Add the UDP header */ - udphdr = iob_push ( iobuf, sizeof ( *udphdr ) ); - len = iob_len ( iobuf ); - udphdr->dest = dest->st_port; - udphdr->src = src->st_port; - udphdr->len = htons ( len ); - udphdr->chksum = 0; - udphdr->chksum = tcpip_chksum ( udphdr, len ); - - /* Dump debugging information */ - DBGC2 ( udp, "UDP %p TX %d->%d len %d\n", udp, - ntohs ( udphdr->src ), ntohs ( udphdr->dest ), - ntohs ( udphdr->len ) ); - - /* Send it to the next layer for processing */ - if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev, - &udphdr->chksum ) ) != 0 ) { - DBGC ( udp, "UDP %p could not transmit packet: %s\n", - udp, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Identify UDP connection by local address - * - * @v local Local address - * @ret udp UDP connection, or NULL - */ -static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) { - static const struct sockaddr_tcpip empty_sockaddr = { .pad = { 0, } }; - struct udp_connection *udp; - - list_for_each_entry ( udp, &udp_conns, list ) { - if ( ( ( udp->local.st_family == local->st_family ) || - ( udp->local.st_family == 0 ) ) && - ( ( udp->local.st_port == local->st_port ) || - ( udp->local.st_port == 0 ) ) && - ( ( memcmp ( udp->local.pad, local->pad, - sizeof ( udp->local.pad ) ) == 0 ) || - ( memcmp ( udp->local.pad, empty_sockaddr.pad, - sizeof ( udp->local.pad ) ) == 0 ) ) ) { - return udp; - } - } - return NULL; -} - -/** - * Process a received packet - * - * @v iobuf I/O buffer - * @v netdev Network device - * @v st_src Partially-filled source address - * @v st_dest Partially-filled destination address - * @v pshdr_csum Pseudo-header checksum - * @ret rc Return status code - */ -static int udp_rx ( struct io_buffer *iobuf, - struct net_device *netdev __unused, - struct sockaddr_tcpip *st_src, - struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) { - struct udp_header *udphdr = iobuf->data; - struct udp_connection *udp; - struct xfer_metadata meta; - size_t ulen; - unsigned int csum; - int rc = 0; - - /* Sanity check packet */ - if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) { - DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n", - iob_len ( iobuf ), sizeof ( *udphdr ) ); - - rc = -EINVAL; - goto done; - } - ulen = ntohs ( udphdr->len ); - if ( ulen < sizeof ( *udphdr ) ) { - DBG ( "UDP length too short at %zd bytes " - "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) ); - rc = -EINVAL; - goto done; - } - if ( ulen > iob_len ( iobuf ) ) { - DBG ( "UDP length too long at %zd bytes (packet is %zd " - "bytes)\n", ulen, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - if ( udphdr->chksum ) { - csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen ); - if ( csum != 0 ) { - DBG ( "UDP checksum incorrect (is %04x including " - "checksum field, should be 0000)\n", csum ); - rc = -EINVAL; - goto done; - } - } - - /* Parse parameters from header and strip header */ - st_src->st_port = udphdr->src; - st_dest->st_port = udphdr->dest; - udp = udp_demux ( st_dest ); - iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) ); - iob_pull ( iobuf, sizeof ( *udphdr ) ); - - /* Dump debugging information */ - DBGC2 ( udp, "UDP %p RX %d<-%d len %zd\n", udp, - ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); - - /* Ignore if no matching connection found */ - if ( ! udp ) { - DBG ( "No UDP connection listening on port %d\n", - ntohs ( udphdr->dest ) ); - rc = -ENOTCONN; - goto done; - } - - /* Pass data to application */ - memset ( &meta, 0, sizeof ( meta ) ); - meta.src = ( struct sockaddr * ) st_src; - meta.dest = ( struct sockaddr * ) st_dest; - rc = xfer_deliver ( &udp->xfer, iob_disown ( iobuf ), &meta ); - - done: - free_iob ( iobuf ); - return rc; -} - -struct tcpip_protocol udp_protocol __tcpip_protocol = { - .name = "UDP", - .rx = udp_rx, - .tcpip_proto = IP_UDP, -}; - -/*************************************************************************** - * - * Data transfer interface - * - *************************************************************************** - */ - -/** - * Allocate I/O buffer for UDP - * - * @v udp UDP connection - * @v len Payload size - * @ret iobuf I/O buffer, or NULL - */ -static struct io_buffer * udp_xfer_alloc_iob ( struct udp_connection *udp, - size_t len ) { - struct io_buffer *iobuf; - - iobuf = alloc_iob ( MAX_LL_NET_HEADER_LEN + len ); - if ( ! iobuf ) { - DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n", - udp, len ); - return NULL; - } - iob_reserve ( iobuf, MAX_LL_NET_HEADER_LEN ); - return iobuf; -} - -/** - * Deliver datagram as I/O buffer - * - * @v udp UDP connection - * @v iobuf Datagram I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int udp_xfer_deliver ( struct udp_connection *udp, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - - /* Transmit data, if possible */ - return udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ), - ( ( struct sockaddr_tcpip * ) meta->dest ), - meta->netdev ); -} - -/** UDP data transfer interface operations */ -static struct interface_operation udp_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct udp_connection *, udp_xfer_deliver ), - INTF_OP ( xfer_alloc_iob, struct udp_connection *, udp_xfer_alloc_iob ), - INTF_OP ( intf_close, struct udp_connection *, udp_close ), -}; - -/** UDP data transfer interface descriptor */ -static struct interface_descriptor udp_xfer_desc = - INTF_DESC ( struct udp_connection, xfer, udp_xfer_operations ); - -/*************************************************************************** - * - * Openers - * - *************************************************************************** - */ - -/** UDP IPv4 socket opener */ -struct socket_opener udp_ipv4_socket_opener __socket_opener = { - .semantics = UDP_SOCK_DGRAM, - .family = AF_INET, - .open = udp_open, -}; - -/** UDP IPv6 socket opener */ -struct socket_opener udp_ipv6_socket_opener __socket_opener = { - .semantics = UDP_SOCK_DGRAM, - .family = AF_INET6, - .open = udp_open, -}; - -/** Linkage hack */ -int udp_sock_dgram = UDP_SOCK_DGRAM; - -/** - * Open UDP URI - * - * @v xfer Data transfer interface - * @v uri URI - * @ret rc Return status code - */ -static int udp_open_uri ( struct interface *xfer, struct uri *uri ) { - struct sockaddr_tcpip peer; - - /* Sanity check */ - if ( ! uri->host ) - return -EINVAL; - - memset ( &peer, 0, sizeof ( peer ) ); - peer.st_port = htons ( uri_port ( uri, 0 ) ); - return xfer_open_named_socket ( xfer, SOCK_DGRAM, - ( struct sockaddr * ) &peer, - uri->host, NULL ); -} - -/** UDP URI opener */ -struct uri_opener udp_uri_opener __uri_opener = { - .scheme = "udp", - .open = udp_open_uri, -}; diff --git a/qemu/roms/ipxe/src/net/udp/dhcp.c b/qemu/roms/ipxe/src/net/udp/dhcp.c deleted file mode 100644 index aed5ee360..000000000 --- a/qemu/roms/ipxe/src/net/udp/dhcp.c +++ /dev/null @@ -1,1497 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/if_ether.h> -#include <ipxe/iobuf.h> -#include <ipxe/netdevice.h> -#include <ipxe/device.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/job.h> -#include <ipxe/retry.h> -#include <ipxe/tcpip.h> -#include <ipxe/ip.h> -#include <ipxe/uuid.h> -#include <ipxe/timer.h> -#include <ipxe/settings.h> -#include <ipxe/dhcp.h> -#include <ipxe/dhcpopts.h> -#include <ipxe/dhcppkt.h> -#include <ipxe/dhcp_arch.h> -#include <ipxe/features.h> -#include <config/dhcp.h> - -/** @file - * - * Dynamic Host Configuration Protocol - * - */ - -struct dhcp_session; -static int dhcp_tx ( struct dhcp_session *dhcp ); - -/** - * DHCP operation types - * - * This table maps from DHCP message types (i.e. values of the @c - * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP - * packet. - */ -static const uint8_t dhcp_op[] = { - [DHCPDISCOVER] = BOOTP_REQUEST, - [DHCPOFFER] = BOOTP_REPLY, - [DHCPREQUEST] = BOOTP_REQUEST, - [DHCPDECLINE] = BOOTP_REQUEST, - [DHCPACK] = BOOTP_REPLY, - [DHCPNAK] = BOOTP_REPLY, - [DHCPRELEASE] = BOOTP_REQUEST, - [DHCPINFORM] = BOOTP_REQUEST, -}; - -/** Raw option data for options common to all DHCP requests */ -static uint8_t dhcp_request_options_data[] = { - DHCP_MESSAGE_TYPE, DHCP_BYTE ( 0 ), - DHCP_MAX_MESSAGE_SIZE, - DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ), - DHCP_CLIENT_ARCHITECTURE, DHCP_ARCH_CLIENT_ARCHITECTURE, - DHCP_CLIENT_NDI, DHCP_ARCH_CLIENT_NDI, - DHCP_VENDOR_CLASS_ID, DHCP_ARCH_VENDOR_CLASS_ID, - DHCP_USER_CLASS_ID, DHCP_STRING ( 'i', 'P', 'X', 'E' ), - DHCP_PARAMETER_REQUEST_LIST, - DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, - DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, - DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, - DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, - DHCP_DOMAIN_SEARCH, - 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */ - DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), - DHCP_END -}; - -/** DHCP server address setting */ -const struct setting dhcp_server_setting __setting ( SETTING_MISC, - dhcp-server ) = { - .name = "dhcp-server", - .description = "DHCP server", - .tag = DHCP_SERVER_IDENTIFIER, - .type = &setting_type_ipv4, -}; - -/** - * Most recent DHCP transaction ID - * - * This is exposed for use by the fakedhcp code when reconstructing - * DHCP packets for PXE NBPs. - */ -uint32_t dhcp_last_xid; - -/** - * Name a DHCP packet type - * - * @v msgtype DHCP message type - * @ret string DHCP mesasge type name - */ -static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) { - switch ( msgtype ) { - case DHCPNONE: return "BOOTP"; /* Non-DHCP packet */ - case DHCPDISCOVER: return "DHCPDISCOVER"; - case DHCPOFFER: return "DHCPOFFER"; - case DHCPREQUEST: return "DHCPREQUEST"; - case DHCPDECLINE: return "DHCPDECLINE"; - case DHCPACK: return "DHCPACK"; - case DHCPNAK: return "DHCPNAK"; - case DHCPRELEASE: return "DHCPRELEASE"; - case DHCPINFORM: return "DHCPINFORM"; - default: return "DHCP<invalid>"; - } -} - -/**************************************************************************** - * - * DHCP session - * - */ - -struct dhcp_session; - -/** DHCP session state operations */ -struct dhcp_session_state { - /** State name */ - const char *name; - /** - * Construct transmitted packet - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer Destination address - */ - int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer ); - /** - * Handle received packet - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer DHCP server address - * @v msgtype DHCP message type - * @v server_id DHCP server ID - * @v pseudo_id DHCP server pseudo-ID - */ - void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id, struct in_addr pseudo_id ); - /** - * Handle timer expiry - * - * @v dhcp DHCP session - */ - void ( * expired ) ( struct dhcp_session *dhcp ); - /** Transmitted message type */ - uint8_t tx_msgtype; - /** Timeout parameters */ - uint8_t min_timeout_sec; - uint8_t max_timeout_sec; -}; - -static struct dhcp_session_state dhcp_state_discover; -static struct dhcp_session_state dhcp_state_request; -static struct dhcp_session_state dhcp_state_proxy; -static struct dhcp_session_state dhcp_state_pxebs; - -/** A DHCP session */ -struct dhcp_session { - /** Reference counter */ - struct refcnt refcnt; - /** Job control interface */ - struct interface job; - /** Data transfer interface */ - struct interface xfer; - - /** Network device being configured */ - struct net_device *netdev; - /** Local socket address */ - struct sockaddr_in local; - /** State of the session */ - struct dhcp_session_state *state; - /** Transaction ID (in network-endian order) */ - uint32_t xid; - - /** Offered IP address */ - struct in_addr offer; - /** DHCP server */ - struct in_addr server; - /** DHCP offer priority */ - int priority; - - /** ProxyDHCP protocol extensions should be ignored */ - int no_pxedhcp; - /** ProxyDHCP server */ - struct in_addr proxy_server; - /** ProxyDHCP offer */ - struct dhcp_packet *proxy_offer; - /** ProxyDHCP offer priority */ - int proxy_priority; - - /** PXE Boot Server type */ - uint16_t pxe_type; - /** List of PXE Boot Servers to attempt */ - struct in_addr *pxe_attempt; - /** List of PXE Boot Servers to accept */ - struct in_addr *pxe_accept; - - /** Retransmission timer */ - struct retry_timer timer; - /** Transmission counter */ - unsigned int count; - /** Start time of the current state (in ticks) */ - unsigned long start; -}; - -/** - * Free DHCP session - * - * @v refcnt Reference counter - */ -static void dhcp_free ( struct refcnt *refcnt ) { - struct dhcp_session *dhcp = - container_of ( refcnt, struct dhcp_session, refcnt ); - - netdev_put ( dhcp->netdev ); - dhcppkt_put ( dhcp->proxy_offer ); - free ( dhcp ); -} - -/** - * Mark DHCP session as complete - * - * @v dhcp DHCP session - * @v rc Return status code - */ -static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) { - - /* Stop retry timer */ - stop_timer ( &dhcp->timer ); - - /* Shut down interfaces */ - intf_shutdown ( &dhcp->xfer, rc ); - intf_shutdown ( &dhcp->job, rc ); -} - -/** - * Transition to new DHCP session state - * - * @v dhcp DHCP session - * @v state New session state - */ -static void dhcp_set_state ( struct dhcp_session *dhcp, - struct dhcp_session_state *state ) { - - DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name ); - dhcp->state = state; - dhcp->start = currticks(); - stop_timer ( &dhcp->timer ); - set_timer_limits ( &dhcp->timer, - ( state->min_timeout_sec * TICKS_PER_SEC ), - ( state->max_timeout_sec * TICKS_PER_SEC ) ); - start_timer_nodelay ( &dhcp->timer ); -} - -/** - * Check if DHCP packet contains PXE options - * - * @v dhcppkt DHCP packet - * @ret has_pxeopts DHCP packet contains PXE options - * - * It is assumed that the packet is already known to contain option 60 - * set to "PXEClient". - */ -static int dhcp_has_pxeopts ( struct dhcp_packet *dhcppkt ) { - - /* Check for a boot filename */ - if ( dhcppkt_fetch ( dhcppkt, DHCP_BOOTFILE_NAME, NULL, 0 ) > 0 ) - return 1; - - /* Check for a PXE boot menu */ - if ( dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU, NULL, 0 ) > 0 ) - return 1; - - return 0; -} - -/**************************************************************************** - * - * DHCP state machine - * - */ - -/** - * Construct transmitted packet for DHCP discovery - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer Destination address - */ -static int dhcp_discovery_tx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt __unused, - struct sockaddr_in *peer ) { - - DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp ); - - /* Set server address */ - peer->sin_addr.s_addr = INADDR_BROADCAST; - peer->sin_port = htons ( BOOTPS_PORT ); - - return 0; -} - -/** - * Handle received packet during DHCP discovery - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer DHCP server address - * @v msgtype DHCP message type - * @v server_id DHCP server ID - * @v pseudo_id DHCP server pseudo-ID - */ -static void dhcp_discovery_rx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id, - struct in_addr pseudo_id ) { - struct in_addr ip; - char vci[9]; /* "PXEClient" */ - int vci_len; - int has_pxeclient; - int8_t priority = 0; - uint8_t no_pxedhcp = 0; - unsigned long elapsed; - - DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, - dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), - ntohs ( peer->sin_port ) ); - if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || - ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { - DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); - DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); - } - - /* Identify offered IP address */ - ip = dhcppkt->dhcphdr->yiaddr; - if ( ip.s_addr ) - DBGC ( dhcp, " for %s", inet_ntoa ( ip ) ); - - /* Identify "PXEClient" vendor class */ - vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID, - vci, sizeof ( vci ) ); - has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) && - ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 )); - if ( has_pxeclient ) { - DBGC ( dhcp, "%s", - ( dhcp_has_pxeopts ( dhcppkt ) ? " pxe" : " proxy" ) ); - } - - /* Identify priority */ - dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority, - sizeof ( priority ) ); - if ( priority ) - DBGC ( dhcp, " pri %d", priority ); - - /* Identify ignore-PXE flag */ - dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp, - sizeof ( no_pxedhcp ) ); - if ( no_pxedhcp ) - DBGC ( dhcp, " nopxe" ); - DBGC ( dhcp, "\n" ); - - /* Select as DHCP offer, if applicable */ - if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) && - ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) && - ( priority >= dhcp->priority ) ) { - dhcp->offer = ip; - dhcp->server = server_id; - dhcp->priority = priority; - dhcp->no_pxedhcp = no_pxedhcp; - } - - /* Select as ProxyDHCP offer, if applicable */ - if ( pseudo_id.s_addr && has_pxeclient && - ( priority >= dhcp->proxy_priority ) ) { - dhcppkt_put ( dhcp->proxy_offer ); - dhcp->proxy_server = pseudo_id; - dhcp->proxy_offer = dhcppkt_get ( dhcppkt ); - dhcp->proxy_priority = priority; - } - - /* We can exit the discovery state when we have a valid - * DHCPOFFER, and either: - * - * o The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or - * o We have a valid ProxyDHCPOFFER, or - * o We have allowed sufficient time for ProxyDHCPOFFERs. - */ - - /* If we don't yet have a DHCPOFFER, do nothing */ - if ( ! dhcp->offer.s_addr ) - return; - - /* If we can't yet transition to DHCPREQUEST, do nothing */ - elapsed = ( currticks() - dhcp->start ); - if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer || - ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) ) - return; - - /* Transition to DHCPREQUEST */ - dhcp_set_state ( dhcp, &dhcp_state_request ); -} - -/** - * Handle timer expiry during DHCP discovery - * - * @v dhcp DHCP session - */ -static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) { - unsigned long elapsed = ( currticks() - dhcp->start ); - - /* If link is blocked, defer DHCP discovery (and reset timeout) */ - if ( netdev_link_blocked ( dhcp->netdev ) ) { - DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp ); - start_timer_fixed ( &dhcp->timer, - ( DHCP_DISC_START_TIMEOUT_SEC * - TICKS_PER_SEC ) ); - return; - } - - /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( dhcp->offer.s_addr && - ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) { - dhcp_set_state ( dhcp, &dhcp_state_request ); - return; - } - - /* Otherwise, retransmit current packet */ - dhcp_tx ( dhcp ); -} - -/** DHCP discovery state operations */ -static struct dhcp_session_state dhcp_state_discover = { - .name = "discovery", - .tx = dhcp_discovery_tx, - .rx = dhcp_discovery_rx, - .expired = dhcp_discovery_expired, - .tx_msgtype = DHCPDISCOVER, - .min_timeout_sec = DHCP_DISC_START_TIMEOUT_SEC, - .max_timeout_sec = DHCP_DISC_END_TIMEOUT_SEC, -}; - -/** - * Construct transmitted packet for DHCP request - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer Destination address - */ -static int dhcp_request_tx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer ) { - int rc; - - DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d", - dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT ); - DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) ); - - /* Set server ID */ - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, - &dhcp->server, - sizeof ( dhcp->server ) ) ) != 0 ) - return rc; - - /* Set requested IP address */ - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, - &dhcp->offer, - sizeof ( dhcp->offer ) ) ) != 0 ) - return rc; - - /* Set server address */ - peer->sin_addr.s_addr = INADDR_BROADCAST; - peer->sin_port = htons ( BOOTPS_PORT ); - - return 0; -} - -/** - * Handle received packet during DHCP request - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer DHCP server address - * @v msgtype DHCP message type - * @v server_id DHCP server ID - * @v pseudo_id DHCP server pseudo-ID - */ -static void dhcp_request_rx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id, - struct in_addr pseudo_id ) { - struct in_addr ip; - struct settings *parent; - struct settings *settings; - int rc; - - DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, - dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), - ntohs ( peer->sin_port ) ); - if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || - ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { - DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); - DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); - } - - /* Identify leased IP address */ - ip = dhcppkt->dhcphdr->yiaddr; - if ( ip.s_addr ) - DBGC ( dhcp, " for %s", inet_ntoa ( ip ) ); - DBGC ( dhcp, "\n" ); - - /* Filter out unacceptable responses */ - if ( peer->sin_port != htons ( BOOTPS_PORT ) ) - return; - if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) ) - return; - if ( server_id.s_addr != dhcp->server.s_addr ) - return; - if ( ip.s_addr != dhcp->offer.s_addr ) - return; - - /* Record assigned address */ - dhcp->local.sin_addr = ip; - - /* Register settings */ - parent = netdev_settings ( dhcp->netdev ); - settings = &dhcppkt->settings; - if ( ( rc = register_settings ( settings, parent, - DHCP_SETTINGS_NAME ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not register settings: %s\n", - dhcp, strerror ( rc ) ); - dhcp_finished ( dhcp, rc ); - return; - } - - /* Perform ProxyDHCP if applicable */ - if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ && - ( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) { - if ( dhcp_has_pxeopts ( dhcp->proxy_offer ) ) { - /* PXE options already present; register settings - * without performing a ProxyDHCPREQUEST - */ - settings = &dhcp->proxy_offer->settings; - if ( ( rc = register_settings ( settings, NULL, - PROXYDHCP_SETTINGS_NAME ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not register " - "proxy settings: %s\n", - dhcp, strerror ( rc ) ); - dhcp_finished ( dhcp, rc ); - return; - } - } else { - /* PXE options not present; use a ProxyDHCPREQUEST */ - dhcp_set_state ( dhcp, &dhcp_state_proxy ); - return; - } - } - - /* Terminate DHCP */ - dhcp_finished ( dhcp, 0 ); -} - -/** - * Handle timer expiry during DHCP discovery - * - * @v dhcp DHCP session - */ -static void dhcp_request_expired ( struct dhcp_session *dhcp ) { - - /* Retransmit current packet */ - dhcp_tx ( dhcp ); -} - -/** DHCP request state operations */ -static struct dhcp_session_state dhcp_state_request = { - .name = "request", - .tx = dhcp_request_tx, - .rx = dhcp_request_rx, - .expired = dhcp_request_expired, - .tx_msgtype = DHCPREQUEST, - .min_timeout_sec = DHCP_REQ_START_TIMEOUT_SEC, - .max_timeout_sec = DHCP_REQ_END_TIMEOUT_SEC, -}; - -/** - * Construct transmitted packet for ProxyDHCP request - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer Destination address - */ -static int dhcp_proxy_tx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer ) { - int rc; - - DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s\n", dhcp, - inet_ntoa ( dhcp->proxy_server ) ); - - /* Set server ID */ - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, - &dhcp->proxy_server, - sizeof ( dhcp->proxy_server ) ) ) != 0 ) - return rc; - - /* Set server address */ - peer->sin_addr = dhcp->proxy_server; - peer->sin_port = htons ( PXE_PORT ); - - return 0; -} - -/** - * Handle received packet during ProxyDHCP request - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer DHCP server address - * @v msgtype DHCP message type - * @v server_id DHCP server ID - * @v pseudo_id DHCP server pseudo-ID - */ -static void dhcp_proxy_rx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id, - struct in_addr pseudo_id ) { - struct settings *settings = &dhcppkt->settings; - int rc; - - DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, - dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), - ntohs ( peer->sin_port ) ); - if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || - ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { - DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); - DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); - } - if ( dhcp_has_pxeopts ( dhcppkt ) ) - DBGC ( dhcp, " pxe" ); - DBGC ( dhcp, "\n" ); - - /* Filter out unacceptable responses */ - if ( peer->sin_port != ntohs ( PXE_PORT ) ) - return; - if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) ) - return; - if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) ) - return; - if ( ! dhcp_has_pxeopts ( dhcppkt ) ) - return; - - /* Register settings */ - if ( ( rc = register_settings ( settings, NULL, - PROXYDHCP_SETTINGS_NAME ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not register proxy settings: %s\n", - dhcp, strerror ( rc ) ); - dhcp_finished ( dhcp, rc ); - return; - } - - /* Terminate DHCP */ - dhcp_finished ( dhcp, 0 ); -} - -/** - * Handle timer expiry during ProxyDHCP request - * - * @v dhcp DHCP session - */ -static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) { - unsigned long elapsed = ( currticks() - dhcp->start ); - - /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) { - dhcp_finished ( dhcp, 0 ); - return; - } - - /* Retransmit current packet */ - dhcp_tx ( dhcp ); -} - -/** ProxyDHCP request state operations */ -static struct dhcp_session_state dhcp_state_proxy = { - .name = "ProxyDHCP", - .tx = dhcp_proxy_tx, - .rx = dhcp_proxy_rx, - .expired = dhcp_proxy_expired, - .tx_msgtype = DHCPREQUEST, - .min_timeout_sec = DHCP_PROXY_START_TIMEOUT_SEC, - .max_timeout_sec = DHCP_PROXY_END_TIMEOUT_SEC, -}; - -/** - * Construct transmitted packet for PXE Boot Server Discovery - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer Destination address - */ -static int dhcp_pxebs_tx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer ) { - struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; - int rc; - - /* Set server address */ - peer->sin_addr = *(dhcp->pxe_attempt); - peer->sin_port = ( ( peer->sin_addr.s_addr == INADDR_BROADCAST ) ? - htons ( BOOTPS_PORT ) : htons ( PXE_PORT ) ); - - DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n", - dhcp, inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ), - le16_to_cpu ( dhcp->pxe_type ) ); - - /* Set boot menu item */ - menu_item.type = dhcp->pxe_type; - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, - &menu_item, sizeof ( menu_item ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Check to see if PXE Boot Server address is acceptable - * - * @v dhcp DHCP session - * @v bs Boot Server address - * @ret accept Boot Server is acceptable - */ -static int dhcp_pxebs_accept ( struct dhcp_session *dhcp, - struct in_addr bs ) { - struct in_addr *accept; - - /* Accept if we have no acceptance filter */ - if ( ! dhcp->pxe_accept ) - return 1; - - /* Scan through acceptance list */ - for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) { - if ( accept->s_addr == bs.s_addr ) - return 1; - } - - DBGC ( dhcp, "DHCP %p rejecting server %s\n", - dhcp, inet_ntoa ( bs ) ); - return 0; -} - -/** - * Handle received packet during PXE Boot Server Discovery - * - * @v dhcp DHCP session - * @v dhcppkt DHCP packet - * @v peer DHCP server address - * @v msgtype DHCP message type - * @v server_id DHCP server ID - * @v pseudo_id DHCP server pseudo-ID - */ -static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id, - struct in_addr pseudo_id ) { - struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; - int rc; - - DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, - dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), - ntohs ( peer->sin_port ) ); - if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || - ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { - DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); - DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); - } - - /* Identify boot menu item */ - dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, - &menu_item, sizeof ( menu_item ) ); - if ( menu_item.type ) - DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) ); - DBGC ( dhcp, "\n" ); - - /* Filter out unacceptable responses */ - if ( ( peer->sin_port != htons ( BOOTPS_PORT ) ) && - ( peer->sin_port != htons ( PXE_PORT ) ) ) - return; - if ( msgtype != DHCPACK ) - return; - if ( menu_item.type != dhcp->pxe_type ) - return; - if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) ) - return; - - /* Register settings */ - if ( ( rc = register_settings ( &dhcppkt->settings, NULL, - PXEBS_SETTINGS_NAME ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not register settings: %s\n", - dhcp, strerror ( rc ) ); - dhcp_finished ( dhcp, rc ); - return; - } - - /* Terminate DHCP */ - dhcp_finished ( dhcp, 0 ); -} - -/** - * Handle timer expiry during PXE Boot Server Discovery - * - * @v dhcp DHCP session - */ -static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) { - unsigned long elapsed = ( currticks() - dhcp->start ); - - /* Give up waiting before we reach the failure point, and fail - * over to the next server in the attempt list - */ - if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) { - dhcp->pxe_attempt++; - if ( dhcp->pxe_attempt->s_addr ) { - dhcp_set_state ( dhcp, &dhcp_state_pxebs ); - return; - } else { - dhcp_finished ( dhcp, -ETIMEDOUT ); - return; - } - } - - /* Retransmit current packet */ - dhcp_tx ( dhcp ); -} - -/** PXE Boot Server Discovery state operations */ -static struct dhcp_session_state dhcp_state_pxebs = { - .name = "PXEBS", - .tx = dhcp_pxebs_tx, - .rx = dhcp_pxebs_rx, - .expired = dhcp_pxebs_expired, - .tx_msgtype = DHCPREQUEST, - .min_timeout_sec = PXEBS_START_TIMEOUT_SEC, - .max_timeout_sec = PXEBS_END_TIMEOUT_SEC, -}; - -/**************************************************************************** - * - * Packet construction - * - */ - -/** - * Create a DHCP packet - * - * @v dhcppkt DHCP packet structure to fill in - * @v netdev Network device - * @v msgtype DHCP message type - * @v xid Transaction ID (in network-endian order) - * @v options Initial options to include (or NULL) - * @v options_len Length of initial options - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Creates a DHCP packet in the specified buffer, and initialise a - * DHCP packet structure. - */ -int dhcp_create_packet ( struct dhcp_packet *dhcppkt, - struct net_device *netdev, uint8_t msgtype, - uint32_t xid, const void *options, size_t options_len, - void *data, size_t max_len ) { - struct dhcphdr *dhcphdr = data; - int rc; - - /* Sanity check */ - if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) ) - return -ENOSPC; - - /* Initialise DHCP packet content */ - memset ( dhcphdr, 0, max_len ); - dhcphdr->xid = xid; - dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE ); - dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto ); - dhcphdr->op = dhcp_op[msgtype]; - dhcphdr->hlen = netdev->ll_protocol->ll_addr_len; - memcpy ( dhcphdr->chaddr, netdev->ll_addr, - netdev->ll_protocol->ll_addr_len ); - memcpy ( dhcphdr->options, options, options_len ); - - /* If the local link-layer address functions only as a name - * (i.e. cannot be used as a destination address), then - * request broadcast responses. - */ - if ( netdev->ll_protocol->flags & LL_NAME_ONLY ) - dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST ); - - /* If the network device already has an IPv4 address then - * unicast responses from the DHCP server may be rejected, so - * request broadcast responses. - */ - if ( ipv4_has_any_addr ( netdev ) ) - dhcphdr->flags |= htons ( BOOTP_FL_BROADCAST ); - - /* Initialise DHCP packet structure */ - memset ( dhcppkt, 0, sizeof ( *dhcppkt ) ); - dhcppkt_init ( dhcppkt, data, max_len ); - - /* Set DHCP_MESSAGE_TYPE option */ - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE, - &msgtype, sizeof ( msgtype ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Create DHCP request packet - * - * @v dhcppkt DHCP packet structure to fill in - * @v netdev Network device - * @v msgtype DHCP message type - * @v xid Transaction ID (in network-endian order) - * @v ciaddr Client IP address - * @v data Buffer for DHCP packet - * @v max_len Size of DHCP packet buffer - * @ret rc Return status code - * - * Creates a DHCP request packet in the specified buffer, and - * initialise a DHCP packet structure. - */ -int dhcp_create_request ( struct dhcp_packet *dhcppkt, - struct net_device *netdev, unsigned int msgtype, - uint32_t xid, struct in_addr ciaddr, - void *data, size_t max_len ) { - struct dhcp_netdev_desc dhcp_desc; - struct dhcp_client_id client_id; - struct dhcp_client_uuid client_uuid; - uint8_t *dhcp_features; - size_t dhcp_features_len; - size_t ll_addr_len; - void *user_class; - ssize_t len; - int rc; - - /* Create DHCP packet */ - if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, xid, - dhcp_request_options_data, - sizeof ( dhcp_request_options_data ), - data, max_len ) ) != 0 ) { - DBG ( "DHCP could not create DHCP packet: %s\n", - strerror ( rc ) ); - goto err_create_packet; - } - - /* Set client IP address */ - dhcppkt->dhcphdr->ciaddr = ciaddr; - - /* Add options to identify the feature list */ - dhcp_features = table_start ( DHCP_FEATURES ); - dhcp_features_len = table_num_entries ( DHCP_FEATURES ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features, - dhcp_features_len ) ) != 0 ) { - DBG ( "DHCP could not set features list option: %s\n", - strerror ( rc ) ); - goto err_store_features; - } - - /* Add options to identify the network device */ - fetch_raw_setting ( netdev_settings ( netdev ), &busid_setting, - &dhcp_desc, sizeof ( dhcp_desc ) ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc, - sizeof ( dhcp_desc ) ) ) != 0 ) { - DBG ( "DHCP could not set bus ID option: %s\n", - strerror ( rc ) ); - goto err_store_busid; - } - - /* Add DHCP client identifier. Required for Infiniband, and - * doesn't hurt other link layers. - */ - client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto ); - ll_addr_len = netdev->ll_protocol->ll_addr_len; - assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) ); - memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id, - ( ll_addr_len + 1 ) ) ) != 0 ) { - DBG ( "DHCP could not set client ID: %s\n", - strerror ( rc ) ); - goto err_store_client_id; - } - - /* Add client UUID, if we have one. Required for PXE. The - * PXE spec does not specify a byte ordering for UUIDs, but - * RFC4578 suggests that it follows the EFI spec, in which the - * first three fields are little-endian. - */ - client_uuid.type = DHCP_CLIENT_UUID_TYPE; - if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, - &client_uuid.uuid ) ) >= 0 ) { - uuid_mangle ( &client_uuid.uuid ); - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID, - &client_uuid, - sizeof ( client_uuid ) ) ) != 0 ) { - DBG ( "DHCP could not set client UUID: %s\n", - strerror ( rc ) ); - goto err_store_client_uuid; - } - } - - /* Add user class, if we have one. */ - if ( ( len = fetch_raw_setting_copy ( NULL, &user_class_setting, - &user_class ) ) >= 0 ) { - if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID, - user_class, len ) ) != 0 ) { - DBG ( "DHCP could not set user class: %s\n", - strerror ( rc ) ); - goto err_store_user_class; - } - } - - err_store_user_class: - free ( user_class ); - err_store_client_uuid: - err_store_client_id: - err_store_busid: - err_store_features: - err_create_packet: - return rc; -} - -/**************************************************************************** - * - * Data transfer interface - * - */ - -/** - * Transmit DHCP request - * - * @v dhcp DHCP session - * @ret rc Return status code - */ -static int dhcp_tx ( struct dhcp_session *dhcp ) { - static struct sockaddr_in peer = { - .sin_family = AF_INET, - }; - struct xfer_metadata meta = { - .netdev = dhcp->netdev, - .src = ( struct sockaddr * ) &dhcp->local, - .dest = ( struct sockaddr * ) &peer, - }; - struct io_buffer *iobuf; - uint8_t msgtype = dhcp->state->tx_msgtype; - struct dhcp_packet dhcppkt; - int rc; - - /* Start retry timer. Do this first so that failures to - * transmit will be retried. - */ - start_timer ( &dhcp->timer ); - - /* Allocate buffer for packet */ - iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN ); - if ( ! iobuf ) - return -ENOMEM; - - /* Create basic DHCP packet in temporary buffer */ - if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype, - dhcp->xid, dhcp->local.sin_addr, - iobuf->data, - iob_tailroom ( iobuf ) ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n", - dhcp, strerror ( rc ) ); - goto done; - } - - /* (Ab)use the "secs" field to convey metadata about the DHCP - * session state into packet traces. Useful for extracting - * debug information from non-debug builds. - */ - dhcppkt.dhcphdr->secs = htons ( ( ++(dhcp->count) << 2 ) | - ( dhcp->offer.s_addr ? 0x02 : 0 ) | - ( dhcp->proxy_offer ? 0x01 : 0 ) ); - - /* Fill in packet based on current state */ - if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n", - dhcp, strerror ( rc ) ); - goto done; - } - - /* Transmit the packet */ - iob_put ( iobuf, dhcppkt_len ( &dhcppkt ) ); - if ( ( rc = xfer_deliver ( &dhcp->xfer, iob_disown ( iobuf ), - &meta ) ) != 0 ) { - DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n", - dhcp, strerror ( rc ) ); - goto done; - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Receive new data - * - * @v dhcp DHCP session - * @v iobuf I/O buffer - * @v meta Transfer metadata - * @ret rc Return status code - */ -static int dhcp_deliver ( struct dhcp_session *dhcp, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct net_device *netdev = dhcp->netdev; - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct sockaddr_in *peer; - size_t data_len; - struct dhcp_packet *dhcppkt; - struct dhcphdr *dhcphdr; - uint8_t msgtype = 0; - struct in_addr server_id = { 0 }; - struct in_addr pseudo_id; - int rc = 0; - - /* Sanity checks */ - if ( ! meta->src ) { - DBGC ( dhcp, "DHCP %p received packet without source port\n", - dhcp ); - rc = -EINVAL; - goto err_no_src; - } - peer = ( struct sockaddr_in * ) meta->src; - - /* Create a DHCP packet containing the I/O buffer contents. - * Whilst we could just use the original buffer in situ, that - * would waste the unused space in the packet buffer, and also - * waste a relatively scarce fully-aligned I/O buffer. - */ - data_len = iob_len ( iobuf ); - dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len ); - if ( ! dhcppkt ) { - rc = -ENOMEM; - goto err_alloc_dhcppkt; - } - dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) ); - memcpy ( dhcphdr, iobuf->data, data_len ); - dhcppkt_init ( dhcppkt, dhcphdr, data_len ); - - /* Identify message type */ - dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, - sizeof ( msgtype ) ); - - /* Identify server ID */ - dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER, - &server_id, sizeof ( server_id ) ); - - /* Identify server pseudo-ID */ - pseudo_id = server_id; - if ( ! pseudo_id.s_addr ) - pseudo_id = dhcppkt->dhcphdr->siaddr; - if ( ! pseudo_id.s_addr ) - pseudo_id = peer->sin_addr; - - /* Check for matching transaction ID */ - if ( dhcphdr->xid != dhcp->xid ) { - DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction " - "ID\n", dhcp, dhcp_msgtype_name ( msgtype ), - inet_ntoa ( peer->sin_addr ), - ntohs ( peer->sin_port ) ); - rc = -EINVAL; - goto err_xid; - }; - - /* Check for matching client hardware address */ - if ( memcmp ( dhcphdr->chaddr, netdev->ll_addr, - ll_protocol->ll_addr_len ) != 0 ) { - DBGC ( dhcp, "DHCP %p %s from %s:%d has bad chaddr %s\n", - dhcp, dhcp_msgtype_name ( msgtype ), - inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ), - ll_protocol->ntoa ( dhcphdr->chaddr ) ); - rc = -EINVAL; - goto err_chaddr; - } - - /* Handle packet based on current state */ - dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id ); - - err_chaddr: - err_xid: - dhcppkt_put ( dhcppkt ); - err_alloc_dhcppkt: - err_no_src: - free_iob ( iobuf ); - return rc; -} - -/** DHCP data transfer interface operations */ -static struct interface_operation dhcp_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct dhcp_session *, dhcp_deliver ), -}; - -/** DHCP data transfer interface descriptor */ -static struct interface_descriptor dhcp_xfer_desc = - INTF_DESC ( struct dhcp_session, xfer, dhcp_xfer_operations ); - -/** - * Handle DHCP retry timer expiry - * - * @v timer DHCP retry timer - * @v fail Failure indicator - */ -static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) { - struct dhcp_session *dhcp = - container_of ( timer, struct dhcp_session, timer ); - - /* If we have failed, terminate DHCP */ - if ( fail ) { - dhcp_finished ( dhcp, -ETIMEDOUT ); - return; - } - - /* Handle timer expiry based on current state */ - dhcp->state->expired ( dhcp ); -} - -/**************************************************************************** - * - * Job control interface - * - */ - -/** DHCP job control interface operations */ -static struct interface_operation dhcp_job_op[] = { - INTF_OP ( intf_close, struct dhcp_session *, dhcp_finished ), -}; - -/** DHCP job control interface descriptor */ -static struct interface_descriptor dhcp_job_desc = - INTF_DESC ( struct dhcp_session, job, dhcp_job_op ); - -/**************************************************************************** - * - * Instantiators - * - */ - -/** - * DHCP peer address for socket opening - * - * This is a dummy address; the only useful portion is the socket - * family (so that we get a UDP connection). The DHCP client will set - * the IP address and source port explicitly on each transmission. - */ -static struct sockaddr dhcp_peer = { - .sa_family = AF_INET, -}; - -/** - * Start DHCP state machine on a network device - * - * @v job Job control interface - * @v netdev Network device - * @ret rc Return status code - * - * Starts DHCP on the specified network device. If successful, the - * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as - * option sources. - */ -int start_dhcp ( struct interface *job, struct net_device *netdev ) { - struct dhcp_session *dhcp; - int rc; - - /* Allocate and initialise structure */ - dhcp = zalloc ( sizeof ( *dhcp ) ); - if ( ! dhcp ) - return -ENOMEM; - ref_init ( &dhcp->refcnt, dhcp_free ); - intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt ); - intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt ); - timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt ); - dhcp->netdev = netdev_get ( netdev ); - dhcp->local.sin_family = AF_INET; - dhcp->local.sin_port = htons ( BOOTPC_PORT ); - dhcp->xid = random(); - - /* Store DHCP transaction ID for fakedhcp code */ - dhcp_last_xid = dhcp->xid; - - /* Instantiate child objects and attach to our interfaces */ - if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer, - ( struct sockaddr * ) &dhcp->local ) ) != 0 ) - goto err; - - /* Enter DHCPDISCOVER state */ - dhcp_set_state ( dhcp, &dhcp_state_discover ); - - /* Attach parent interface, mortalise self, and return */ - intf_plug_plug ( &dhcp->job, job ); - ref_put ( &dhcp->refcnt ); - return 0; - - err: - dhcp_finished ( dhcp, rc ); - ref_put ( &dhcp->refcnt ); - return rc; -} - -/** - * Retrieve list of PXE boot servers for a given server type - * - * @v dhcp DHCP session - * @v raw DHCP PXE boot server list - * @v raw_len Length of DHCP PXE boot server list - * @v ip IP address list to fill in - * - * The caller must ensure that the IP address list has sufficient - * space. - */ -static void pxebs_list ( struct dhcp_session *dhcp, void *raw, - size_t raw_len, struct in_addr *ip ) { - struct dhcp_pxe_boot_server *server = raw; - size_t server_len; - unsigned int i; - - while ( raw_len ) { - if ( raw_len < sizeof ( *server ) ) { - DBGC ( dhcp, "DHCP %p malformed PXE server list\n", - dhcp ); - break; - } - server_len = offsetof ( typeof ( *server ), - ip[ server->num_ip ] ); - if ( raw_len < server_len ) { - DBGC ( dhcp, "DHCP %p malformed PXE server list\n", - dhcp ); - break; - } - if ( server->type == dhcp->pxe_type ) { - for ( i = 0 ; i < server->num_ip ; i++ ) - *(ip++) = server->ip[i]; - } - server = ( ( ( void * ) server ) + server_len ); - raw_len -= server_len; - } -} - -/** - * Start PXE Boot Server Discovery on a network device - * - * @v job Job control interface - * @v netdev Network device - * @v pxe_type PXE server type - * @ret rc Return status code - * - * Starts PXE Boot Server Discovery on the specified network device. - * If successful, the Boot Server ACK will be registered as an option - * source. - */ -int start_pxebs ( struct interface *job, struct net_device *netdev, - unsigned int pxe_type ) { - struct setting pxe_discovery_control_setting = - { .tag = DHCP_PXE_DISCOVERY_CONTROL }; - struct setting pxe_boot_servers_setting = - { .tag = DHCP_PXE_BOOT_SERVERS }; - struct setting pxe_boot_server_mcast_setting = - { .tag = DHCP_PXE_BOOT_SERVER_MCAST }; - ssize_t pxebs_list_len; - struct dhcp_session *dhcp; - struct in_addr *ip; - unsigned int pxe_discovery_control; - int rc; - - /* Get upper bound for PXE boot server IP address list */ - pxebs_list_len = fetch_raw_setting ( NULL, &pxe_boot_servers_setting, - NULL, 0 ); - if ( pxebs_list_len < 0 ) - pxebs_list_len = 0; - - /* Allocate and initialise structure */ - dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ + - sizeof ( *ip ) /* bcast */ + pxebs_list_len + - sizeof ( *ip ) /* terminator */ ); - if ( ! dhcp ) - return -ENOMEM; - ref_init ( &dhcp->refcnt, dhcp_free ); - intf_init ( &dhcp->job, &dhcp_job_desc, &dhcp->refcnt ); - intf_init ( &dhcp->xfer, &dhcp_xfer_desc, &dhcp->refcnt ); - timer_init ( &dhcp->timer, dhcp_timer_expired, &dhcp->refcnt ); - dhcp->netdev = netdev_get ( netdev ); - dhcp->local.sin_family = AF_INET; - fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting, - &dhcp->local.sin_addr ); - dhcp->local.sin_port = htons ( BOOTPC_PORT ); - dhcp->pxe_type = cpu_to_le16 ( pxe_type ); - - /* Construct PXE boot server IP address lists */ - pxe_discovery_control = - fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); - ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) ); - dhcp->pxe_attempt = ip; - if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) { - fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip); - if ( ip->s_addr ) - ip++; - } - if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) ) - (ip++)->s_addr = INADDR_BROADCAST; - if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS ) - dhcp->pxe_accept = ip; - if ( pxebs_list_len ) { - uint8_t buf[pxebs_list_len]; - - fetch_raw_setting ( NULL, &pxe_boot_servers_setting, - buf, sizeof ( buf ) ); - pxebs_list ( dhcp, buf, sizeof ( buf ), ip ); - } - if ( ! dhcp->pxe_attempt->s_addr ) { - DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n", - dhcp, pxe_type ); - rc = -EINVAL; - goto err; - } - - /* Dump out PXE server lists */ - DBGC ( dhcp, "DHCP %p attempting", dhcp ); - for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ ) - DBGC ( dhcp, " %s", inet_ntoa ( *ip ) ); - DBGC ( dhcp, "\n" ); - if ( dhcp->pxe_accept ) { - DBGC ( dhcp, "DHCP %p accepting", dhcp ); - for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ ) - DBGC ( dhcp, " %s", inet_ntoa ( *ip ) ); - DBGC ( dhcp, "\n" ); - } - - /* Instantiate child objects and attach to our interfaces */ - if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer, - ( struct sockaddr * ) &dhcp->local ) ) != 0 ) - goto err; - - /* Enter PXEBS state */ - dhcp_set_state ( dhcp, &dhcp_state_pxebs ); - - /* Attach parent interface, mortalise self, and return */ - intf_plug_plug ( &dhcp->job, job ); - ref_put ( &dhcp->refcnt ); - return 0; - - err: - dhcp_finished ( dhcp, rc ); - ref_put ( &dhcp->refcnt ); - return rc; -} - -/** DHCP network device configurator */ -struct net_device_configurator dhcp_configurator __net_device_configurator = { - .name = "dhcp", - .start = start_dhcp, -}; diff --git a/qemu/roms/ipxe/src/net/udp/dhcpv6.c b/qemu/roms/ipxe/src/net/udp/dhcpv6.c deleted file mode 100644 index a63543775..000000000 --- a/qemu/roms/ipxe/src/net/udp/dhcpv6.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/iobuf.h> -#include <ipxe/open.h> -#include <ipxe/netdevice.h> -#include <ipxe/settings.h> -#include <ipxe/retry.h> -#include <ipxe/timer.h> -#include <ipxe/in.h> -#include <ipxe/crc32.h> -#include <ipxe/errortab.h> -#include <ipxe/ipv6.h> -#include <ipxe/dhcpv6.h> - -/** @file - * - * Dynamic Host Configuration Protocol for IPv6 - * - */ - -/* Disambiguate the various error causes */ -#define EPROTO_UNSPECFAIL __einfo_error ( EINFO_EPROTO_UNSPECFAIL ) -#define EINFO_EPROTO_UNSPECFAIL \ - __einfo_uniqify ( EINFO_EPROTO, 1, "Unspecified server failure" ) -#define EPROTO_NOADDRSAVAIL __einfo_error ( EINFO_EPROTO_NOADDRSAVAIL ) -#define EINFO_EPROTO_NOADDRSAVAIL \ - __einfo_uniqify ( EINFO_EPROTO, 2, "No addresses available" ) -#define EPROTO_NOBINDING __einfo_error ( EINFO_EPROTO_NOBINDING ) -#define EINFO_EPROTO_NOBINDING \ - __einfo_uniqify ( EINFO_EPROTO, 3, "Client record unavailable" ) -#define EPROTO_NOTONLINK __einfo_error ( EINFO_EPROTO_NOTONLINK ) -#define EINFO_EPROTO_NOTONLINK \ - __einfo_uniqify ( EINFO_EPROTO, 4, "Prefix not on link" ) -#define EPROTO_USEMULTICAST __einfo_error ( EINFO_EPROTO_USEMULTICAST ) -#define EINFO_EPROTO_USEMULTICAST \ - __einfo_uniqify ( EINFO_EPROTO, 5, "Use multicast address" ) -#define EPROTO_STATUS( status ) \ - EUNIQ ( EINFO_EPROTO, ( (status) & 0x0f ), EPROTO_UNSPECFAIL, \ - EPROTO_NOADDRSAVAIL, EPROTO_NOBINDING, \ - EPROTO_NOTONLINK, EPROTO_USEMULTICAST ) - -/** Human-readable error messages */ -struct errortab dhcpv6_errors[] __errortab = { - __einfo_errortab ( EINFO_EPROTO_NOADDRSAVAIL ), -}; - -/**************************************************************************** - * - * DHCPv6 option lists - * - */ - -/** A DHCPv6 option list */ -struct dhcpv6_option_list { - /** Data buffer */ - const void *data; - /** Length of data buffer */ - size_t len; -}; - -/** - * Find DHCPv6 option - * - * @v options DHCPv6 option list - * @v code Option code - * @ret option DHCPv6 option, or NULL if not found - */ -static const union dhcpv6_any_option * -dhcpv6_option ( struct dhcpv6_option_list *options, unsigned int code ) { - const union dhcpv6_any_option *option = options->data; - size_t remaining = options->len; - size_t data_len; - - /* Scan through list of options */ - while ( remaining >= sizeof ( option->header ) ) { - - /* Calculate and validate option length */ - remaining -= sizeof ( option->header ); - data_len = ntohs ( option->header.len ); - if ( data_len > remaining ) { - /* Malformed option list */ - return NULL; - } - - /* Return if we have found the specified option */ - if ( option->header.code == htons ( code ) ) - return option; - - /* Otherwise, move to the next option */ - option = ( ( ( void * ) option->header.data ) + data_len ); - remaining -= data_len; - } - - return NULL; -} - -/** - * Check DHCPv6 client or server identifier - * - * @v options DHCPv6 option list - * @v code Option code - * @v expected Expected value - * @v len Length of expected value - * @ret rc Return status code - */ -static int dhcpv6_check_duid ( struct dhcpv6_option_list *options, - unsigned int code, const void *expected, - size_t len ) { - const union dhcpv6_any_option *option; - const struct dhcpv6_duid_option *duid; - - /* Find option */ - option = dhcpv6_option ( options, code ); - if ( ! option ) - return -ENOENT; - duid = &option->duid; - - /* Check option length */ - if ( ntohs ( duid->header.len ) != len ) - return -EINVAL; - - /* Compare option value */ - if ( memcmp ( duid->duid, expected, len ) != 0 ) - return -EINVAL; - - return 0; -} - -/** - * Get DHCPv6 status code - * - * @v options DHCPv6 option list - * @ret rc Return status code - */ -static int dhcpv6_status_code ( struct dhcpv6_option_list *options ) { - const union dhcpv6_any_option *option; - const struct dhcpv6_status_code_option *status_code; - unsigned int status; - - /* Find status code option, if present */ - option = dhcpv6_option ( options, DHCPV6_STATUS_CODE ); - if ( ! option ) { - /* Omitted status code should be treated as "success" */ - return 0; - } - status_code = &option->status_code; - - /* Sanity check */ - if ( ntohs ( status_code->header.len ) < - ( sizeof ( *status_code ) - sizeof ( status_code->header ) ) ) { - return -EINVAL; - } - - /* Calculate iPXE error code from DHCPv6 status code */ - status = ntohs ( status_code->status ); - return ( status ? -EPROTO_STATUS ( status ) : 0 ); -} - -/** - * Get DHCPv6 identity association address - * - * @v options DHCPv6 option list - * @v iaid Identity association ID - * @v address IPv6 address to fill in - * @ret rc Return status code - */ -static int dhcpv6_iaaddr ( struct dhcpv6_option_list *options, uint32_t iaid, - struct in6_addr *address ) { - const union dhcpv6_any_option *option; - const struct dhcpv6_ia_na_option *ia_na; - const struct dhcpv6_iaaddr_option *iaaddr; - struct dhcpv6_option_list suboptions; - size_t len; - int rc; - - /* Find identity association option, if present */ - option = dhcpv6_option ( options, DHCPV6_IA_NA ); - if ( ! option ) - return -ENOENT; - ia_na = &option->ia_na; - - /* Sanity check */ - len = ntohs ( ia_na->header.len ); - if ( len < ( sizeof ( *ia_na ) - sizeof ( ia_na->header ) ) ) - return -EINVAL; - - /* Check identity association ID */ - if ( ia_na->iaid != htonl ( iaid ) ) - return -EINVAL; - - /* Construct IA_NA sub-options list */ - suboptions.data = ia_na->options; - suboptions.len = ( len + sizeof ( ia_na->header ) - - offsetof ( typeof ( *ia_na ), options ) ); - - /* Check IA_NA status code */ - if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 ) - return rc; - - /* Find identity association address, if present */ - option = dhcpv6_option ( &suboptions, DHCPV6_IAADDR ); - if ( ! option ) - return -ENOENT; - iaaddr = &option->iaaddr; - - /* Sanity check */ - len = ntohs ( iaaddr->header.len ); - if ( len < ( sizeof ( *iaaddr ) - sizeof ( iaaddr->header ) ) ) - return -EINVAL; - - /* Construct IAADDR sub-options list */ - suboptions.data = iaaddr->options; - suboptions.len = ( len + sizeof ( iaaddr->header ) - - offsetof ( typeof ( *iaaddr ), options ) ); - - /* Check IAADDR status code */ - if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 ) - return rc; - - /* Extract IPv6 address */ - memcpy ( address, &iaaddr->address, sizeof ( *address ) ); - - return 0; -} - -/**************************************************************************** - * - * DHCPv6 settings blocks - * - */ - -/** A DHCPv6 settings block */ -struct dhcpv6_settings { - /** Reference count */ - struct refcnt refcnt; - /** Settings block */ - struct settings settings; - /** Option list */ - struct dhcpv6_option_list options; -}; - -/** - * Check applicability of DHCPv6 setting - * - * @v settings Settings block - * @v setting Setting - * @ret applies Setting applies within this settings block - */ -static int dhcpv6_applies ( struct settings *settings __unused, - const struct setting *setting ) { - - return ( setting->scope == &ipv6_scope ); -} - -/** - * Fetch value of DHCPv6 setting - * - * @v settings Settings block - * @v setting Setting to fetch - * @v data Buffer to fill with setting data - * @v len Length of buffer - * @ret len Length of setting data, or negative error - */ -static int dhcpv6_fetch ( struct settings *settings, - struct setting *setting, - void *data, size_t len ) { - struct dhcpv6_settings *dhcpv6set = - container_of ( settings, struct dhcpv6_settings, settings ); - const union dhcpv6_any_option *option; - size_t option_len; - - /* Find option */ - option = dhcpv6_option ( &dhcpv6set->options, setting->tag ); - if ( ! option ) - return -ENOENT; - - /* Copy option to data buffer */ - option_len = ntohs ( option->header.len ); - if ( len > option_len ) - len = option_len; - memcpy ( data, option->header.data, len ); - return option_len; -} - -/** DHCPv6 settings operations */ -static struct settings_operations dhcpv6_settings_operations = { - .applies = dhcpv6_applies, - .fetch = dhcpv6_fetch, -}; - -/** - * Register DHCPv6 options as network device settings - * - * @v options DHCPv6 option list - * @v parent Parent settings block - * @ret rc Return status code - */ -static int dhcpv6_register ( struct dhcpv6_option_list *options, - struct settings *parent ) { - struct dhcpv6_settings *dhcpv6set; - void *data; - size_t len; - int rc; - - /* Allocate and initialise structure */ - dhcpv6set = zalloc ( sizeof ( *dhcpv6set ) + options->len ); - if ( ! dhcpv6set ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &dhcpv6set->refcnt, NULL ); - settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations, - &dhcpv6set->refcnt, &ipv6_scope ); - data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) ); - len = options->len; - memcpy ( data, options->data, len ); - dhcpv6set->options.data = data; - dhcpv6set->options.len = len; - - /* Register settings */ - if ( ( rc = register_settings ( &dhcpv6set->settings, parent, - DHCPV6_SETTINGS_NAME ) ) != 0 ) - goto err_register; - - err_register: - ref_put ( &dhcpv6set->refcnt ); - err_alloc: - return rc; -} - -/**************************************************************************** - * - * DHCPv6 protocol - * - */ - -/** Options to be requested */ -static uint16_t dhcpv6_requested_options[] = { - htons ( DHCPV6_DNS_SERVERS ), htons ( DHCPV6_DOMAIN_LIST ), - htons ( DHCPV6_BOOTFILE_URL ), htons ( DHCPV6_BOOTFILE_PARAM ), -}; - -/** - * Name a DHCPv6 packet type - * - * @v type DHCPv6 packet type - * @ret name DHCPv6 packet type name - */ -static __attribute__ (( unused )) const char * -dhcpv6_type_name ( unsigned int type ) { - static char buf[ 12 /* "UNKNOWN-xxx" + NUL */ ]; - - switch ( type ) { - case DHCPV6_SOLICIT: return "SOLICIT"; - case DHCPV6_ADVERTISE: return "ADVERTISE"; - case DHCPV6_REQUEST: return "REQUEST"; - case DHCPV6_REPLY: return "REPLY"; - case DHCPV6_INFORMATION_REQUEST: return "INFORMATION-REQUEST"; - default: - snprintf ( buf, sizeof ( buf ), "UNKNOWN-%d", type ); - return buf; - } -} - -/** A DHCPv6 session state */ -struct dhcpv6_session_state { - /** Current transmitted packet type */ - uint8_t tx_type; - /** Current expected received packet type */ - uint8_t rx_type; - /** Flags */ - uint8_t flags; - /** Next state (or NULL to terminate) */ - struct dhcpv6_session_state *next; -}; - -/** DHCPv6 session state flags */ -enum dhcpv6_session_state_flags { - /** Include identity association within request */ - DHCPV6_TX_IA_NA = 0x01, - /** Include leased IPv6 address within request */ - DHCPV6_TX_IAADDR = 0x02, - /** Record received server ID */ - DHCPV6_RX_RECORD_SERVER_ID = 0x04, - /** Record received IPv6 address */ - DHCPV6_RX_RECORD_IAADDR = 0x08, - /** Apply received IPv6 address */ - DHCPV6_RX_APPLY_IAADDR = 0x10, -}; - -/** DHCPv6 request state */ -static struct dhcpv6_session_state dhcpv6_request = { - .tx_type = DHCPV6_REQUEST, - .rx_type = DHCPV6_REPLY, - .flags = ( DHCPV6_TX_IA_NA | DHCPV6_TX_IAADDR | - DHCPV6_RX_RECORD_IAADDR | DHCPV6_RX_APPLY_IAADDR ), - .next = NULL, -}; - -/** DHCPv6 solicitation state */ -static struct dhcpv6_session_state dhcpv6_solicit = { - .tx_type = DHCPV6_SOLICIT, - .rx_type = DHCPV6_ADVERTISE, - .flags = ( DHCPV6_TX_IA_NA | DHCPV6_RX_RECORD_SERVER_ID | - DHCPV6_RX_RECORD_IAADDR ), - .next = &dhcpv6_request, -}; - -/** DHCPv6 information request state */ -static struct dhcpv6_session_state dhcpv6_information_request = { - .tx_type = DHCPV6_INFORMATION_REQUEST, - .rx_type = DHCPV6_REPLY, - .flags = 0, - .next = NULL, -}; - -/** A DHCPv6 session */ -struct dhcpv6_session { - /** Reference counter */ - struct refcnt refcnt; - /** Job control interface */ - struct interface job; - /** Data transfer interface */ - struct interface xfer; - - /** Network device being configured */ - struct net_device *netdev; - /** Transaction ID */ - uint8_t xid[3]; - /** Identity association ID */ - uint32_t iaid; - /** Start time (in ticks) */ - unsigned long start; - /** Client DUID */ - struct dhcpv6_duid_uuid client_duid; - /** Server DUID, if known */ - void *server_duid; - /** Server DUID length */ - size_t server_duid_len; - /** Leased IPv6 address */ - struct in6_addr lease; - - /** Retransmission timer */ - struct retry_timer timer; - - /** Current session state */ - struct dhcpv6_session_state *state; - /** Current timeout status code */ - int rc; -}; - -/** - * Free DHCPv6 session - * - * @v refcnt Reference count - */ -static void dhcpv6_free ( struct refcnt *refcnt ) { - struct dhcpv6_session *dhcpv6 = - container_of ( refcnt, struct dhcpv6_session, refcnt ); - - netdev_put ( dhcpv6->netdev ); - free ( dhcpv6->server_duid ); - free ( dhcpv6 ); -} - -/** - * Terminate DHCPv6 session - * - * @v dhcpv6 DHCPv6 session - * @v rc Reason for close - */ -static void dhcpv6_finished ( struct dhcpv6_session *dhcpv6, int rc ) { - - /* Stop timer */ - stop_timer ( &dhcpv6->timer ); - - /* Shut down interfaces */ - intf_shutdown ( &dhcpv6->xfer, rc ); - intf_shutdown ( &dhcpv6->job, rc ); -} - -/** - * Transition to new DHCPv6 session state - * - * @v dhcpv6 DHCPv6 session - * @v state New session state - */ -static void dhcpv6_set_state ( struct dhcpv6_session *dhcpv6, - struct dhcpv6_session_state *state ) { - - DBGC ( dhcpv6, "DHCPv6 %s entering %s state\n", dhcpv6->netdev->name, - dhcpv6_type_name ( state->tx_type ) ); - - /* Record state */ - dhcpv6->state = state; - - /* Default to -ETIMEDOUT if no more specific error is recorded */ - dhcpv6->rc = -ETIMEDOUT; - - /* Start timer to trigger transmission */ - start_timer_nodelay ( &dhcpv6->timer ); -} - -/** - * Get DHCPv6 user class - * - * @v data Data buffer - * @v len Length of data buffer - * @ret len Length of user class - */ -static size_t dhcpv6_user_class ( void *data, size_t len ) { - static const char default_user_class[4] = { 'i', 'P', 'X', 'E' }; - int actual_len; - - /* Fetch user-class setting, if defined */ - actual_len = fetch_raw_setting ( NULL, &user_class_setting, data, len ); - if ( actual_len >= 0 ) - return actual_len; - - /* Otherwise, use the default user class ("iPXE") */ - if ( len > sizeof ( default_user_class ) ) - len = sizeof ( default_user_class ); - memcpy ( data, default_user_class, len ); - return sizeof ( default_user_class ); -} - -/** - * Transmit current request - * - * @v dhcpv6 DHCPv6 session - * @ret rc Return status code - */ -static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) { - struct dhcpv6_duid_option *client_id; - struct dhcpv6_duid_option *server_id; - struct dhcpv6_ia_na_option *ia_na; - struct dhcpv6_iaaddr_option *iaaddr; - struct dhcpv6_option_request_option *option_request; - struct dhcpv6_user_class_option *user_class; - struct dhcpv6_elapsed_time_option *elapsed; - struct dhcpv6_header *dhcphdr; - struct io_buffer *iobuf; - size_t client_id_len; - size_t server_id_len; - size_t ia_na_len; - size_t option_request_len; - size_t user_class_string_len; - size_t user_class_len; - size_t elapsed_len; - size_t total_len; - int rc; - - /* Calculate lengths */ - client_id_len = ( sizeof ( *client_id ) + - sizeof ( dhcpv6->client_duid ) ); - server_id_len = ( dhcpv6->server_duid ? ( sizeof ( *server_id ) + - dhcpv6->server_duid_len ) :0); - if ( dhcpv6->state->flags & DHCPV6_TX_IA_NA ) { - ia_na_len = sizeof ( *ia_na ); - if ( dhcpv6->state->flags & DHCPV6_TX_IAADDR ) - ia_na_len += sizeof ( *iaaddr ); - } else { - ia_na_len = 0; - } - option_request_len = ( sizeof ( *option_request ) + - sizeof ( dhcpv6_requested_options ) ); - user_class_string_len = dhcpv6_user_class ( NULL, 0 ); - user_class_len = ( sizeof ( *user_class ) + - sizeof ( user_class->user_class[0] ) + - user_class_string_len ); - elapsed_len = sizeof ( *elapsed ); - total_len = ( sizeof ( *dhcphdr ) + client_id_len + server_id_len + - ia_na_len + option_request_len + user_class_len + - elapsed_len ); - - /* Allocate packet */ - iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len ); - if ( ! iobuf ) - return -ENOMEM; - - /* Construct header */ - dhcphdr = iob_put ( iobuf, sizeof ( *dhcphdr ) ); - dhcphdr->type = dhcpv6->state->tx_type; - memcpy ( dhcphdr->xid, dhcpv6->xid, sizeof ( dhcphdr->xid ) ); - - /* Construct client identifier */ - client_id = iob_put ( iobuf, client_id_len ); - client_id->header.code = htons ( DHCPV6_CLIENT_ID ); - client_id->header.len = htons ( client_id_len - - sizeof ( client_id->header ) ); - memcpy ( client_id->duid, &dhcpv6->client_duid, - sizeof ( dhcpv6->client_duid ) ); - - /* Construct server identifier, if applicable */ - if ( server_id_len ) { - server_id = iob_put ( iobuf, server_id_len ); - server_id->header.code = htons ( DHCPV6_SERVER_ID ); - server_id->header.len = htons ( server_id_len - - sizeof ( server_id->header ) ); - memcpy ( server_id->duid, dhcpv6->server_duid, - dhcpv6->server_duid_len ); - } - - /* Construct identity association, if applicable */ - if ( ia_na_len ) { - ia_na = iob_put ( iobuf, ia_na_len ); - ia_na->header.code = htons ( DHCPV6_IA_NA ); - ia_na->header.len = htons ( ia_na_len - - sizeof ( ia_na->header ) ); - ia_na->iaid = htonl ( dhcpv6->iaid ); - ia_na->renew = htonl ( 0 ); - ia_na->rebind = htonl ( 0 ); - if ( dhcpv6->state->flags & DHCPV6_TX_IAADDR ) { - iaaddr = ( ( void * ) ia_na->options ); - iaaddr->header.code = htons ( DHCPV6_IAADDR ); - iaaddr->header.len = htons ( sizeof ( *iaaddr ) - - sizeof ( iaaddr->header )); - memcpy ( &iaaddr->address, &dhcpv6->lease, - sizeof ( iaaddr->address ) ); - iaaddr->preferred = htonl ( 0 ); - iaaddr->valid = htonl ( 0 ); - } - } - - /* Construct option request */ - option_request = iob_put ( iobuf, option_request_len ); - option_request->header.code = htons ( DHCPV6_OPTION_REQUEST ); - option_request->header.len = htons ( option_request_len - - sizeof ( option_request->header )); - memcpy ( option_request->requested, dhcpv6_requested_options, - sizeof ( dhcpv6_requested_options ) ); - - /* Construct user class */ - user_class = iob_put ( iobuf, user_class_len ); - user_class->header.code = htons ( DHCPV6_USER_CLASS ); - user_class->header.len = htons ( user_class_len - - sizeof ( user_class->header ) ); - user_class->user_class[0].len = htons ( user_class_string_len ); - dhcpv6_user_class ( user_class->user_class[0].string, - user_class_string_len ); - - /* Construct elapsed time */ - elapsed = iob_put ( iobuf, elapsed_len ); - elapsed->header.code = htons ( DHCPV6_ELAPSED_TIME ); - elapsed->header.len = htons ( elapsed_len - - sizeof ( elapsed->header ) ); - elapsed->elapsed = htons ( ( ( currticks() - dhcpv6->start ) * 100 ) / - TICKS_PER_SEC ); - - /* Sanity check */ - assert ( iob_len ( iobuf ) == total_len ); - - /* Transmit packet */ - if ( ( rc = xfer_deliver_iob ( &dhcpv6->xfer, iobuf ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not transmit: %s\n", - dhcpv6->netdev->name, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Handle timer expiry - * - * @v timer Retransmission timer - * @v fail Failure indicator - */ -static void dhcpv6_timer_expired ( struct retry_timer *timer, int fail ) { - struct dhcpv6_session *dhcpv6 = - container_of ( timer, struct dhcpv6_session, timer ); - - /* If we have failed, terminate DHCPv6 */ - if ( fail ) { - dhcpv6_finished ( dhcpv6, dhcpv6->rc ); - return; - } - - /* Restart timer */ - start_timer ( &dhcpv6->timer ); - - /* (Re)transmit current request */ - dhcpv6_tx ( dhcpv6 ); -} - -/** - * Receive new data - * - * @v dhcpv6 DHCPv6 session - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct settings *parent = netdev_settings ( dhcpv6->netdev ); - struct sockaddr_in6 *src = ( ( struct sockaddr_in6 * ) meta->src ); - struct dhcpv6_header *dhcphdr = iobuf->data; - struct dhcpv6_option_list options; - const union dhcpv6_any_option *option; - int rc; - - /* Sanity checks */ - if ( iob_len ( iobuf ) < sizeof ( *dhcphdr ) ) { - DBGC ( dhcpv6, "DHCPv6 %s received packet too short (%zd " - "bytes, min %zd bytes)\n", dhcpv6->netdev->name, - iob_len ( iobuf ), sizeof ( *dhcphdr ) ); - rc = -EINVAL; - goto done; - } - assert ( src != NULL ); - assert ( src->sin6_family == AF_INET6 ); - DBGC ( dhcpv6, "DHCPv6 %s received %s from %s\n", - dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ), - inet6_ntoa ( &src->sin6_addr ) ); - - /* Construct option list */ - options.data = dhcphdr->options; - options.len = ( iob_len ( iobuf ) - - offsetof ( typeof ( *dhcphdr ), options ) ); - - /* Verify client identifier */ - if ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_CLIENT_ID, - &dhcpv6->client_duid, - sizeof ( dhcpv6->client_duid ) ) ) !=0){ - DBGC ( dhcpv6, "DHCPv6 %s received %s without correct client " - "ID: %s\n", dhcpv6->netdev->name, - dhcpv6_type_name ( dhcphdr->type ), strerror ( rc ) ); - goto done; - } - - /* Verify server identifier, if applicable */ - if ( dhcpv6->server_duid && - ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_SERVER_ID, - dhcpv6->server_duid, - dhcpv6->server_duid_len ) ) != 0 ) ) { - DBGC ( dhcpv6, "DHCPv6 %s received %s without correct server " - "ID: %s\n", dhcpv6->netdev->name, - dhcpv6_type_name ( dhcphdr->type ), strerror ( rc ) ); - goto done; - } - - /* Check message type */ - if ( dhcphdr->type != dhcpv6->state->rx_type ) { - DBGC ( dhcpv6, "DHCPv6 %s received %s while expecting %s\n", - dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ), - dhcpv6_type_name ( dhcpv6->state->rx_type ) ); - rc = -ENOTTY; - goto done; - } - - /* Fetch status code, if present */ - if ( ( rc = dhcpv6_status_code ( &options ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s received %s with error status: %s\n", - dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ), - strerror ( rc ) ); - /* This is plausibly the error we want to return */ - dhcpv6->rc = rc; - goto done; - } - - /* Record identity association address, if applicable */ - if ( dhcpv6->state->flags & DHCPV6_RX_RECORD_IAADDR ) { - if ( ( rc = dhcpv6_iaaddr ( &options, dhcpv6->iaid, - &dhcpv6->lease ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s received %s with unusable " - "IAADDR: %s\n", dhcpv6->netdev->name, - dhcpv6_type_name ( dhcphdr->type ), - strerror ( rc ) ); - /* This is plausibly the error we want to return */ - dhcpv6->rc = rc; - goto done; - } - DBGC ( dhcpv6, "DHCPv6 %s received %s is for %s\n", - dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ), - inet6_ntoa ( &dhcpv6->lease ) ); - } - - /* Record server ID, if applicable */ - if ( dhcpv6->state->flags & DHCPV6_RX_RECORD_SERVER_ID ) { - assert ( dhcpv6->server_duid == NULL ); - option = dhcpv6_option ( &options, DHCPV6_SERVER_ID ); - if ( ! option ) { - DBGC ( dhcpv6, "DHCPv6 %s received %s missing server " - "ID\n", dhcpv6->netdev->name, - dhcpv6_type_name ( dhcphdr->type ) ); - rc = -EINVAL; - goto done; - } - dhcpv6->server_duid_len = ntohs ( option->duid.header.len ); - dhcpv6->server_duid = malloc ( dhcpv6->server_duid_len ); - if ( ! dhcpv6->server_duid ) { - rc = -ENOMEM; - goto done; - } - memcpy ( dhcpv6->server_duid, option->duid.duid, - dhcpv6->server_duid_len ); - } - - /* Apply identity association address, if applicable */ - if ( dhcpv6->state->flags & DHCPV6_RX_APPLY_IAADDR ) { - if ( ( rc = ipv6_set_address ( dhcpv6->netdev, - &dhcpv6->lease ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not apply %s: %s\n", - dhcpv6->netdev->name, - inet6_ntoa ( &dhcpv6->lease ), strerror ( rc ) ); - /* This is plausibly the error we want to return */ - dhcpv6->rc = rc; - goto done; - } - } - - /* Transition to next state or complete DHCPv6, as applicable */ - if ( dhcpv6->state->next ) { - - /* Transition to next state */ - dhcpv6_set_state ( dhcpv6, dhcpv6->state->next ); - rc = 0; - - } else { - - /* Register settings */ - if ( ( rc = dhcpv6_register ( &options, parent ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not register " - "settings: %s\n", dhcpv6->netdev->name, - strerror ( rc ) ); - goto done; - } - - /* Mark as complete */ - dhcpv6_finished ( dhcpv6, 0 ); - DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name ); - } - - done: - free_iob ( iobuf ); - return rc; -} - -/** DHCPv6 job control interface operations */ -static struct interface_operation dhcpv6_job_op[] = { - INTF_OP ( intf_close, struct dhcpv6_session *, dhcpv6_finished ), -}; - -/** DHCPv6 job control interface descriptor */ -static struct interface_descriptor dhcpv6_job_desc = - INTF_DESC ( struct dhcpv6_session, job, dhcpv6_job_op ); - -/** DHCPv6 data transfer interface operations */ -static struct interface_operation dhcpv6_xfer_op[] = { - INTF_OP ( xfer_deliver, struct dhcpv6_session *, dhcpv6_rx ), -}; - -/** DHCPv6 data transfer interface descriptor */ -static struct interface_descriptor dhcpv6_xfer_desc = - INTF_DESC ( struct dhcpv6_session, xfer, dhcpv6_xfer_op ); - -/** - * Start DHCPv6 - * - * @v job Job control interface - * @v netdev Network device - * @v stateful Perform stateful address autoconfiguration - * @ret rc Return status code - */ -int start_dhcpv6 ( struct interface *job, struct net_device *netdev, - int stateful ) { - struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct dhcpv6_session *dhcpv6; - struct { - union { - struct sockaddr_in6 sin6; - struct sockaddr sa; - } client; - union { - struct sockaddr_in6 sin6; - struct sockaddr sa; - } server; - } addresses; - uint32_t xid; - int len; - int rc; - - /* Allocate and initialise structure */ - dhcpv6 = zalloc ( sizeof ( *dhcpv6 ) ); - if ( ! dhcpv6 ) - return -ENOMEM; - ref_init ( &dhcpv6->refcnt, dhcpv6_free ); - intf_init ( &dhcpv6->job, &dhcpv6_job_desc, &dhcpv6->refcnt ); - intf_init ( &dhcpv6->xfer, &dhcpv6_xfer_desc, &dhcpv6->refcnt ); - dhcpv6->netdev = netdev_get ( netdev ); - xid = random(); - memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) ); - dhcpv6->start = currticks(); - timer_init ( &dhcpv6->timer, dhcpv6_timer_expired, &dhcpv6->refcnt ); - - /* Construct client and server addresses */ - memset ( &addresses, 0, sizeof ( addresses ) ); - addresses.client.sin6.sin6_family = AF_INET6; - addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT ); - addresses.server.sin6.sin6_family = AF_INET6; - ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr ); - addresses.server.sin6.sin6_scope_id = netdev->index; - addresses.server.sin6.sin6_port = htons ( DHCPV6_SERVER_PORT ); - - /* Construct client DUID from system UUID */ - dhcpv6->client_duid.type = htons ( DHCPV6_DUID_UUID ); - if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, - &dhcpv6->client_duid.uuid ) ) < 0 ) { - rc = len; - DBGC ( dhcpv6, "DHCPv6 %s could not create DUID-UUID: %s\n", - dhcpv6->netdev->name, strerror ( rc ) ); - goto err_client_duid; - } - - /* Construct IAID from link-layer address */ - dhcpv6->iaid = crc32_le ( 0, netdev->ll_addr, ll_protocol->ll_addr_len); - DBGC ( dhcpv6, "DHCPv6 %s has XID %02x%02x%02x\n", dhcpv6->netdev->name, - dhcpv6->xid[0], dhcpv6->xid[1], dhcpv6->xid[2] ); - - /* Enter initial state */ - dhcpv6_set_state ( dhcpv6, ( stateful ? &dhcpv6_solicit : - &dhcpv6_information_request ) ); - - /* Open socket */ - if ( ( rc = xfer_open_socket ( &dhcpv6->xfer, SOCK_DGRAM, - &addresses.server.sa, - &addresses.client.sa ) ) != 0 ) { - DBGC ( dhcpv6, "DHCPv6 %s could not open socket: %s\n", - dhcpv6->netdev->name, strerror ( rc ) ); - goto err_open_socket; - } - - /* Attach parent interface, mortalise self, and return */ - intf_plug_plug ( &dhcpv6->job, job ); - ref_put ( &dhcpv6->refcnt ); - return 0; - - err_open_socket: - dhcpv6_finished ( dhcpv6, rc ); - err_client_duid: - ref_put ( &dhcpv6->refcnt ); - return rc; -} - -/** Boot filename setting */ -const struct setting filename6_setting __setting ( SETTING_BOOT, filename ) = { - .name = "filename", - .description = "Boot filename", - .tag = DHCPV6_BOOTFILE_URL, - .type = &setting_type_string, - .scope = &ipv6_scope, -}; - -/** DNS search list setting */ -const struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { - .name = "dnssl", - .description = "DNS search list", - .tag = DHCPV6_DOMAIN_LIST, - .type = &setting_type_dnssl, - .scope = &ipv6_scope, -}; diff --git a/qemu/roms/ipxe/src/net/udp/dns.c b/qemu/roms/ipxe/src/net/udp/dns.c deleted file mode 100644 index 2d77477f6..000000000 --- a/qemu/roms/ipxe/src/net/udp/dns.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * Portions copyright (C) 2004 Anselm M. Hoffmeister - * <stockholm@users.sourceforge.net>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/refcnt.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/resolv.h> -#include <ipxe/retry.h> -#include <ipxe/tcpip.h> -#include <ipxe/settings.h> -#include <ipxe/features.h> -#include <ipxe/dhcp.h> -#include <ipxe/dhcpv6.h> -#include <ipxe/dns.h> - -/** @file - * - * DNS protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 ); - -/* Disambiguate the various error causes */ -#define ENXIO_NO_RECORD __einfo_error ( EINFO_ENXIO_NO_RECORD ) -#define EINFO_ENXIO_NO_RECORD \ - __einfo_uniqify ( EINFO_ENXIO, 0x01, "DNS name does not exist" ) -#define ENXIO_NO_NAMESERVER __einfo_error ( EINFO_ENXIO_NO_NAMESERVER ) -#define EINFO_ENXIO_NO_NAMESERVER \ - __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" ) - -/** The DNS server */ -static union { - struct sockaddr sa; - struct sockaddr_tcpip st; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; -} nameserver = { - .st = { - .st_port = htons ( DNS_PORT ), - }, -}; - -/** The DNS search list */ -static struct dns_name dns_search; - -/** - * Encode a DNS name using RFC1035 encoding - * - * @v string DNS name as a string - * @v name DNS name to fill in - * @ret len Length of DNS name, or negative error - */ -int dns_encode ( const char *string, struct dns_name *name ) { - uint8_t *start = ( name->data + name->offset ); - uint8_t *end = ( name->data + name->len ); - uint8_t *dst = start; - size_t len = 0; - char c; - - /* Encode name */ - while ( ( c = *(string++) ) ) { - - /* Handle '.' separators */ - if ( c == '.' ) { - - /* Reject consecutive '.' */ - if ( ( len == 0 ) && ( dst != start ) ) - return -EINVAL; - - /* Terminate if this is the trailing '.' */ - if ( *string == '\0' ) - break; - - /* Reject initial non-terminating '.' */ - if ( len == 0 ) - return -EINVAL; - - /* Reset length */ - len = 0; - - } else { - - /* Increment length */ - len++; - - /* Check for overflow */ - if ( len > DNS_MAX_LABEL_LEN ) - return -EINVAL; - } - - /* Copy byte, update length */ - if ( ++dst < end ) { - *dst = c; - dst[-len] = len; - } - } - - /* Add terminating root marker */ - if ( len ) - dst++; - if ( dst < end ) - *dst = '\0'; - dst++; - - return ( dst - start ); -} - -/** - * Find start of valid label within an RFC1035-encoded DNS name - * - * @v name DNS name - * @v offset Current offset - * @ret offset Offset of label, or negative error - */ -static int dns_label ( struct dns_name *name, size_t offset ) { - const uint8_t *byte; - const uint16_t *word; - size_t len; - size_t ptr; - - while ( 1 ) { - - /* Fail if we have overrun the DNS name */ - if ( ( offset + sizeof ( *byte) ) > name->len ) - return -EINVAL; - byte = ( name->data + offset ); - - /* Follow compression pointer, if applicable */ - if ( DNS_IS_COMPRESSED ( *byte ) ) { - - /* Fail if we have overrun the DNS name */ - if ( ( offset + sizeof ( *word ) ) > name->len ) - return -EINVAL; - word = ( name->data + offset ); - - /* Extract pointer to new offset */ - ptr = DNS_COMPRESSED_OFFSET ( ntohs ( *word ) ); - - /* Fail if pointer does not point backwards. - * (This guarantees termination of the - * function.) - */ - if ( ptr >= offset ) - return -EINVAL; - - /* Continue from new offset */ - offset = ptr; - continue; - } - - /* Fail if we have overrun the DNS name */ - len = *byte; - if ( ( offset + sizeof ( *byte ) + len ) > name->len ) - return -EINVAL; - - /* We have a valid label */ - return offset; - } -} - -/** - * Decode RFC1035-encoded DNS name - * - * @v name DNS name - * @v data Output buffer - * @v len Length of output buffer - * @ret len Length of decoded DNS name, or negative error - */ -int dns_decode ( struct dns_name *name, char *data, size_t len ) { - unsigned int recursion_limit = name->len; /* Generous upper bound */ - int offset = name->offset; - const uint8_t *label; - size_t decoded_len = 0; - size_t label_len; - size_t copy_len; - - while ( recursion_limit-- ) { - - /* Find valid DNS label */ - offset = dns_label ( name, offset ); - if ( offset < 0 ) - return offset; - - /* Terminate if we have reached the root */ - label = ( name->data + offset ); - label_len = *(label++); - if ( label_len == 0 ) { - if ( decoded_len < len ) - *data = '\0'; - return decoded_len; - } - - /* Prepend '.' if applicable */ - if ( decoded_len && ( decoded_len++ < len ) ) - *(data++) = '.'; - - /* Copy label to output buffer */ - copy_len = ( ( decoded_len < len ) ? ( len - decoded_len ) : 0); - if ( copy_len > label_len ) - copy_len = label_len; - memcpy ( data, label, copy_len ); - data += copy_len; - decoded_len += label_len; - - /* Move to next label */ - offset += ( sizeof ( *label ) + label_len ); - } - - /* Recursion limit exceeded */ - return -EINVAL; -} - -/** - * Compare DNS names for equality - * - * @v first First DNS name - * @v second Second DNS name - * @ret rc Return status code - */ -int dns_compare ( struct dns_name *first, struct dns_name *second ) { - unsigned int recursion_limit = first->len; /* Generous upper bound */ - int first_offset = first->offset; - int second_offset = second->offset; - const uint8_t *first_label; - const uint8_t *second_label; - size_t label_len; - size_t len; - - while ( recursion_limit-- ) { - - /* Find valid DNS labels */ - first_offset = dns_label ( first, first_offset ); - if ( first_offset < 0 ) - return first_offset; - second_offset = dns_label ( second, second_offset ); - if ( second_offset < 0 ) - return second_offset; - - /* Compare label lengths */ - first_label = ( first->data + first_offset ); - second_label = ( second->data + second_offset ); - label_len = *(first_label++); - if ( label_len != *(second_label++) ) - return -ENOENT; - len = ( sizeof ( *first_label ) + label_len ); - - /* Terminate if we have reached the root */ - if ( label_len == 0 ) - return 0; - - /* Compare label contents (case-insensitively) */ - while ( label_len-- ) { - if ( tolower ( *(first_label++) ) != - tolower ( *(second_label++) ) ) - return -ENOENT; - } - - /* Move to next labels */ - first_offset += len; - second_offset += len; - } - - /* Recursion limit exceeded */ - return -EINVAL; -} - -/** - * Copy a DNS name - * - * @v src Source DNS name - * @v dst Destination DNS name - * @ret len Length of copied DNS name, or negative error - */ -int dns_copy ( struct dns_name *src, struct dns_name *dst ) { - unsigned int recursion_limit = src->len; /* Generous upper bound */ - int src_offset = src->offset; - size_t dst_offset = dst->offset; - const uint8_t *label; - size_t label_len; - size_t copy_len; - size_t len; - - while ( recursion_limit-- ) { - - /* Find valid DNS label */ - src_offset = dns_label ( src, src_offset ); - if ( src_offset < 0 ) - return src_offset; - - /* Copy as an uncompressed label */ - label = ( src->data + src_offset ); - label_len = *label; - len = ( sizeof ( *label ) + label_len ); - copy_len = ( ( dst_offset < dst->len ) ? - ( dst->len - dst_offset ) : 0 ); - if ( copy_len > len ) - copy_len = len; - memcpy ( ( dst->data + dst_offset ), label, copy_len ); - src_offset += len; - dst_offset += len; - - /* Terminate if we have reached the root */ - if ( label_len == 0 ) - return ( dst_offset - dst->offset ); - } - - /* Recursion limit exceeded */ - return -EINVAL; -} - -/** - * Skip RFC1035-encoded DNS name - * - * @v name DNS name - * @ret offset Offset to next name, or negative error - */ -int dns_skip ( struct dns_name *name ) { - unsigned int recursion_limit = name->len; /* Generous upper bound */ - int offset = name->offset; - int prev_offset; - const uint8_t *label; - size_t label_len; - - while ( recursion_limit-- ) { - - /* Find valid DNS label */ - prev_offset = offset; - offset = dns_label ( name, prev_offset ); - if ( offset < 0 ) - return offset; - - /* Terminate if we have reached a compression pointer */ - if ( offset != prev_offset ) - return ( prev_offset + sizeof ( uint16_t ) ); - - /* Skip this label */ - label = ( name->data + offset ); - label_len = *label; - offset += ( sizeof ( *label ) + label_len ); - - /* Terminate if we have reached the root */ - if ( label_len == 0 ) - return offset; - } - - /* Recursion limit exceeded */ - return -EINVAL; -} - -/** - * Skip RFC1035-encoded DNS name in search list - * - * @v name DNS name - * @ret offset Offset to next non-empty name, or negative error - */ -static int dns_skip_search ( struct dns_name *name ) { - int offset; - - /* Find next name */ - offset = dns_skip ( name ); - if ( offset < 0 ) - return offset; - - /* Skip over any subsequent empty names (e.g. due to padding - * bytes used in the NDP DNSSL option). - */ - while ( ( offset < ( ( int ) name->len ) ) && - ( *( ( uint8_t * ) ( name->data + offset ) ) == 0 ) ) { - offset++; - } - - return offset; -} - -/** - * Transcribe DNS name (for debugging) - * - * @v name DNS name - * @ret string Transcribed DNS name - */ -static const char * dns_name ( struct dns_name *name ) { - static char buf[256]; - int len; - - len = dns_decode ( name, buf, sizeof ( buf ) ); - return ( ( len < 0 ) ? "<INVALID>" : buf ); -} - -/** - * Name a DNS query type (for debugging) - * - * @v type Query type (in network byte order) - * @ret name Type name - */ -static const char * dns_type ( uint16_t type ) { - switch ( type ) { - case htons ( DNS_TYPE_A ): return "A"; - case htons ( DNS_TYPE_AAAA ): return "AAAA"; - case htons ( DNS_TYPE_CNAME ): return "CNAME"; - default: return "<UNKNOWN>"; - } -} - -/** A DNS request */ -struct dns_request { - /** Reference counter */ - struct refcnt refcnt; - /** Name resolution interface */ - struct interface resolv; - /** Data transfer interface */ - struct interface socket; - /** Retry timer */ - struct retry_timer timer; - - /** Socket address to fill in with resolved address */ - union { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - } address; - /** Initial query type */ - uint16_t qtype; - /** Buffer for current query */ - struct { - /** Query header */ - struct dns_header query; - /** Name buffer */ - char name[DNS_MAX_NAME_LEN]; - /** Space for question */ - struct dns_question padding; - } __attribute__ (( packed )) buf; - /** Current query name */ - struct dns_name name; - /** Question within current query */ - struct dns_question *question; - /** Length of current query */ - size_t len; - /** Offset of search suffix within current query */ - size_t offset; - /** Search list */ - struct dns_name search; - /** Recursion counter */ - unsigned int recursion; -}; - -/** - * Mark DNS request as complete - * - * @v dns DNS request - * @v rc Return status code - */ -static void dns_done ( struct dns_request *dns, int rc ) { - - /* Stop the retry timer */ - stop_timer ( &dns->timer ); - - /* Shut down interfaces */ - intf_shutdown ( &dns->socket, rc ); - intf_shutdown ( &dns->resolv, rc ); -} - -/** - * Mark DNS request as resolved and complete - * - * @v dns DNS request - * @v rc Return status code - */ -static void dns_resolved ( struct dns_request *dns ) { - - DBGC ( dns, "DNS %p found address %s\n", - dns, sock_ntoa ( &dns->address.sa ) ); - - /* Return resolved address */ - resolv_done ( &dns->resolv, &dns->address.sa ); - - /* Mark operation as complete */ - dns_done ( dns, 0 ); -} - -/** - * Construct DNS question - * - * @v dns DNS request - * @ret rc Return status code - */ -static int dns_question ( struct dns_request *dns ) { - static struct dns_name search_root = { - .data = "", - .len = 1, - }; - struct dns_name *search = &dns->search; - int len; - size_t offset; - - /* Use root suffix if search list is empty */ - if ( search->offset == search->len ) - search = &search_root; - - /* Overwrite current suffix */ - dns->name.offset = dns->offset; - len = dns_copy ( search, &dns->name ); - if ( len < 0 ) - return len; - - /* Sanity check */ - offset = ( dns->name.offset + len ); - if ( offset > dns->name.len ) { - DBGC ( dns, "DNS %p name is too long\n", dns ); - return -EINVAL; - } - - /* Construct question */ - dns->question = ( ( ( void * ) &dns->buf ) + offset ); - dns->question->qtype = dns->qtype; - dns->question->qclass = htons ( DNS_CLASS_IN ); - - /* Store length */ - dns->len = ( offset + sizeof ( *(dns->question) ) ); - - /* Restore name */ - dns->name.offset = offsetof ( typeof ( dns->buf ), name ); - - DBGC2 ( dns, "DNS %p question is %s type %s\n", dns, - dns_name ( &dns->name ), dns_type ( dns->question->qtype ) ); - - return 0; -} - -/** - * Send DNS query - * - * @v dns DNS request - * @ret rc Return status code - */ -static int dns_send_packet ( struct dns_request *dns ) { - struct dns_header *query = &dns->buf.query; - - /* Start retransmission timer */ - start_timer ( &dns->timer ); - - /* Generate query identifier */ - query->id = random(); - - /* Send query */ - DBGC ( dns, "DNS %p sending query ID %#04x for %s type %s\n", dns, - ntohs ( query->id ), dns_name ( &dns->name ), - dns_type ( dns->question->qtype ) ); - - /* Send the data */ - return xfer_deliver_raw ( &dns->socket, query, dns->len ); -} - -/** - * Handle DNS retransmission timer expiry - * - * @v timer Retry timer - * @v fail Failure indicator - */ -static void dns_timer_expired ( struct retry_timer *timer, int fail ) { - struct dns_request *dns = - container_of ( timer, struct dns_request, timer ); - - if ( fail ) { - dns_done ( dns, -ETIMEDOUT ); - } else { - dns_send_packet ( dns ); - } -} - -/** - * Receive new data - * - * @v dns DNS request - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int dns_xfer_deliver ( struct dns_request *dns, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct dns_header *response = iobuf->data; - struct dns_header *query = &dns->buf.query; - unsigned int qtype = dns->question->qtype; - struct dns_name buf; - union dns_rr *rr; - int offset; - size_t answer_offset; - size_t next_offset; - size_t rdlength; - size_t name_len; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *response ) ) { - DBGC ( dns, "DNS %p received underlength packet length %zd\n", - dns, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - - /* Check response ID matches query ID */ - if ( response->id != query->id ) { - DBGC ( dns, "DNS %p received unexpected response ID %#04x " - "(wanted %d)\n", dns, ntohs ( response->id ), - ntohs ( query->id ) ); - rc = -EINVAL; - goto done; - } - DBGC ( dns, "DNS %p received response ID %#04x\n", - dns, ntohs ( response->id ) ); - - /* Check that we have exactly one question */ - if ( response->qdcount != htons ( 1 ) ) { - DBGC ( dns, "DNS %p received response with %d questions\n", - dns, ntohs ( response->qdcount ) ); - rc = -EINVAL; - goto done; - } - - /* Skip question section */ - buf.data = iobuf->data; - buf.offset = sizeof ( *response ); - buf.len = iob_len ( iobuf ); - offset = dns_skip ( &buf ); - if ( offset < 0 ) { - rc = offset; - DBGC ( dns, "DNS %p received response with malformed " - "question: %s\n", dns, strerror ( rc ) ); - goto done; - } - answer_offset = ( offset + sizeof ( struct dns_question ) ); - - /* Search through response for useful answers. Do this - * multiple times, to take advantage of useful nameservers - * which send us e.g. the CNAME *and* the A record for the - * pointed-to name. - */ - for ( buf.offset = answer_offset ; buf.offset != buf.len ; - buf.offset = next_offset ) { - - /* Check for valid name */ - offset = dns_skip ( &buf ); - if ( offset < 0 ) { - rc = offset; - DBGC ( dns, "DNS %p received response with malformed " - "answer: %s\n", dns, strerror ( rc ) ); - goto done; - } - - /* Check for sufficient space for resource record */ - rr = ( buf.data + offset ); - if ( ( offset + sizeof ( rr->common ) ) > buf.len ) { - DBGC ( dns, "DNS %p received response with underlength " - "RR\n", dns ); - rc = -EINVAL; - goto done; - } - rdlength = ntohs ( rr->common.rdlength ); - next_offset = ( offset + sizeof ( rr->common ) + rdlength ); - if ( next_offset > buf.len ) { - DBGC ( dns, "DNS %p received response with underlength " - "RR\n", dns ); - rc = -EINVAL; - goto done; - } - - /* Skip non-matching names */ - if ( dns_compare ( &buf, &dns->name ) != 0 ) { - DBGC2 ( dns, "DNS %p ignoring response for %s type " - "%s\n", dns, dns_name ( &buf ), - dns_type ( rr->common.type ) ); - continue; - } - - /* Handle answer */ - switch ( rr->common.type ) { - - case htons ( DNS_TYPE_AAAA ): - - /* Found the target AAAA record */ - if ( rdlength < sizeof ( dns->address.sin6.sin6_addr )){ - DBGC ( dns, "DNS %p received response with " - "underlength AAAA\n", dns ); - rc = -EINVAL; - goto done; - } - dns->address.sin6.sin6_family = AF_INET6; - memcpy ( &dns->address.sin6.sin6_addr, - &rr->aaaa.in6_addr, - sizeof ( dns->address.sin6.sin6_addr ) ); - dns_resolved ( dns ); - rc = 0; - goto done; - - case htons ( DNS_TYPE_A ): - - /* Found the target A record */ - if ( rdlength < sizeof ( dns->address.sin.sin_addr ) ) { - DBGC ( dns, "DNS %p received response with " - "underlength A\n", dns ); - rc = -EINVAL; - goto done; - } - dns->address.sin.sin_family = AF_INET; - dns->address.sin.sin_addr = rr->a.in_addr; - dns_resolved ( dns ); - rc = 0; - goto done; - - case htons ( DNS_TYPE_CNAME ): - - /* Terminate the operation if we recurse too far */ - if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) { - DBGC ( dns, "DNS %p recursion exceeded\n", - dns ); - rc = -ELOOP; - dns_done ( dns, rc ); - goto done; - } - - /* Found a CNAME record; update query and recurse */ - buf.offset = ( offset + sizeof ( rr->cname ) ); - DBGC ( dns, "DNS %p found CNAME %s\n", - dns, dns_name ( &buf ) ); - dns->search.offset = dns->search.len; - name_len = dns_copy ( &buf, &dns->name ); - dns->offset = ( offsetof ( typeof ( dns->buf ), name ) + - name_len - 1 /* Strip root label */ ); - if ( ( rc = dns_question ( dns ) ) != 0 ) { - dns_done ( dns, rc ); - goto done; - } - next_offset = answer_offset; - break; - - default: - DBGC ( dns, "DNS %p got unknown record type %d\n", - dns, ntohs ( rr->common.type ) ); - break; - } - } - - /* Stop the retry timer. After this point, each code path - * must either restart the timer by calling dns_send_packet(), - * or mark the DNS operation as complete by calling - * dns_done() - */ - stop_timer ( &dns->timer ); - - /* Determine what to do next based on the type of query we - * issued and the response we received - */ - switch ( qtype ) { - - case htons ( DNS_TYPE_AAAA ): - /* We asked for an AAAA record and got nothing; try - * the A. - */ - DBGC ( dns, "DNS %p found no AAAA record; trying A\n", dns ); - dns->question->qtype = htons ( DNS_TYPE_A ); - dns_send_packet ( dns ); - rc = 0; - goto done; - - case htons ( DNS_TYPE_A ): - /* We asked for an A record and got nothing; - * try the CNAME. - */ - DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns ); - dns->question->qtype = htons ( DNS_TYPE_CNAME ); - dns_send_packet ( dns ); - rc = 0; - goto done; - - case htons ( DNS_TYPE_CNAME ): - /* We asked for a CNAME record. If we got a response - * (i.e. if the next AAAA/A query is already set up), - * then issue it. - */ - if ( qtype == dns->qtype ) { - dns_send_packet ( dns ); - rc = 0; - goto done; - } - - /* If we have already reached the end of the search list, - * then terminate lookup. - */ - if ( dns->search.offset == dns->search.len ) { - DBGC ( dns, "DNS %p found no CNAME record\n", dns ); - rc = -ENXIO_NO_RECORD; - dns_done ( dns, rc ); - goto done; - } - - /* Move to next entry in search list. This can never fail, - * since we have already used this entry. - */ - DBGC ( dns, "DNS %p found no CNAME record; trying next " - "suffix\n", dns ); - dns->search.offset = dns_skip_search ( &dns->search ); - if ( ( rc = dns_question ( dns ) ) != 0 ) { - dns_done ( dns, rc ); - goto done; - } - dns_send_packet ( dns ); - goto done; - - default: - assert ( 0 ); - rc = -EINVAL; - dns_done ( dns, rc ); - goto done; - } - - done: - /* Free I/O buffer */ - free_iob ( iobuf ); - return rc; -} - -/** - * Receive new data - * - * @v dns DNS request - * @v rc Reason for close - */ -static void dns_xfer_close ( struct dns_request *dns, int rc ) { - - if ( ! rc ) - rc = -ECONNABORTED; - - dns_done ( dns, rc ); -} - -/** DNS socket interface operations */ -static struct interface_operation dns_socket_operations[] = { - INTF_OP ( xfer_deliver, struct dns_request *, dns_xfer_deliver ), - INTF_OP ( intf_close, struct dns_request *, dns_xfer_close ), -}; - -/** DNS socket interface descriptor */ -static struct interface_descriptor dns_socket_desc = - INTF_DESC ( struct dns_request, socket, dns_socket_operations ); - -/** DNS resolver interface operations */ -static struct interface_operation dns_resolv_op[] = { - INTF_OP ( intf_close, struct dns_request *, dns_done ), -}; - -/** DNS resolver interface descriptor */ -static struct interface_descriptor dns_resolv_desc = - INTF_DESC ( struct dns_request, resolv, dns_resolv_op ); - -/** - * Resolve name using DNS - * - * @v resolv Name resolution interface - * @v name Name to resolve - * @v sa Socket address to fill in - * @ret rc Return status code - */ -static int dns_resolv ( struct interface *resolv, - const char *name, struct sockaddr *sa ) { - struct dns_request *dns; - struct dns_header *query; - size_t search_len; - int name_len; - int rc; - - /* Fail immediately if no DNS servers */ - if ( ! nameserver.sa.sa_family ) { - DBG ( "DNS not attempting to resolve \"%s\": " - "no DNS servers\n", name ); - rc = -ENXIO_NO_NAMESERVER; - goto err_no_nameserver; - } - - /* Determine whether or not to use search list */ - search_len = ( strchr ( name, '.' ) ? 0 : dns_search.len ); - - /* Allocate DNS structure */ - dns = zalloc ( sizeof ( *dns ) + search_len ); - if ( ! dns ) { - rc = -ENOMEM; - goto err_alloc_dns; - } - ref_init ( &dns->refcnt, NULL ); - intf_init ( &dns->resolv, &dns_resolv_desc, &dns->refcnt ); - intf_init ( &dns->socket, &dns_socket_desc, &dns->refcnt ); - timer_init ( &dns->timer, dns_timer_expired, &dns->refcnt ); - memcpy ( &dns->address.sa, sa, sizeof ( dns->address.sa ) ); - dns->search.data = ( ( ( void * ) dns ) + sizeof ( *dns ) ); - dns->search.len = search_len; - memcpy ( dns->search.data, dns_search.data, search_len ); - - /* Determine initial query type */ - switch ( nameserver.sa.sa_family ) { - case AF_INET: - dns->qtype = htons ( DNS_TYPE_A ); - break; - case AF_INET6: - dns->qtype = htons ( DNS_TYPE_AAAA ); - break; - default: - rc = -ENOTSUP; - goto err_type; - } - - /* Construct query */ - query = &dns->buf.query; - query->flags = htons ( DNS_FLAG_RD ); - query->qdcount = htons ( 1 ); - dns->name.data = &dns->buf; - dns->name.offset = offsetof ( typeof ( dns->buf ), name ); - dns->name.len = offsetof ( typeof ( dns->buf ), padding ); - name_len = dns_encode ( name, &dns->name ); - if ( name_len < 0 ) { - rc = name_len; - goto err_encode; - } - dns->offset = ( offsetof ( typeof ( dns->buf ), name ) + - name_len - 1 /* Strip root label */ ); - if ( ( rc = dns_question ( dns ) ) != 0 ) - goto err_question; - - /* Open UDP connection */ - if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM, - &nameserver.sa, NULL ) ) != 0 ) { - DBGC ( dns, "DNS %p could not open socket: %s\n", - dns, strerror ( rc ) ); - goto err_open_socket; - } - - /* Start timer to trigger first packet */ - start_timer_nodelay ( &dns->timer ); - - /* Attach parent interface, mortalise self, and return */ - intf_plug_plug ( &dns->resolv, resolv ); - ref_put ( &dns->refcnt ); - return 0; - - err_open_socket: - err_question: - err_encode: - err_type: - ref_put ( &dns->refcnt ); - err_alloc_dns: - err_no_nameserver: - return rc; -} - -/** DNS name resolver */ -struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = { - .name = "DNS", - .resolv = dns_resolv, -}; - -/****************************************************************************** - * - * Settings - * - ****************************************************************************** - */ - -/** - * Format DNS search list setting - * - * @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 - */ -static int format_dnssl_setting ( const struct setting_type *type __unused, - const void *raw, size_t raw_len, - char *buf, size_t len ) { - struct dns_name name = { - .data = ( ( void * ) raw ), - .len = raw_len, - }; - size_t remaining = len; - size_t total = 0; - int name_len; - - while ( name.offset < raw_len ) { - - /* Decode name */ - remaining = ( ( total < len ) ? ( len - total ) : 0 ); - name_len = dns_decode ( &name, ( buf + total ), remaining ); - if ( name_len < 0 ) - return name_len; - total += name_len; - - /* Move to next name */ - name.offset = dns_skip_search ( &name ); - - /* Add separator if applicable */ - if ( name.offset != raw_len ) { - if ( total < len ) - buf[total] = ' '; - total++; - } - } - - return total; -} - -/** A DNS search list setting type */ -const struct setting_type setting_type_dnssl __setting_type = { - .name = "dnssl", - .format = format_dnssl_setting, -}; - -/** IPv4 DNS server setting */ -const struct setting dns_setting __setting ( SETTING_IP_EXTRA, dns ) = { - .name = "dns", - .description = "DNS server", - .tag = DHCP_DNS_SERVERS, - .type = &setting_type_ipv4, -}; - -/** IPv6 DNS server setting */ -const struct setting dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { - .name = "dns6", - .description = "DNS server", - .tag = DHCPV6_DNS_SERVERS, - .type = &setting_type_ipv6, - .scope = &ipv6_scope, -}; - -/** DNS search list */ -const struct setting dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { - .name = "dnssl", - .description = "DNS search list", - .tag = DHCP_DOMAIN_SEARCH, - .type = &setting_type_dnssl, -}; - -/** - * Apply DNS search list - * - */ -static void apply_dns_search ( void ) { - char *localdomain; - int len; - - /* Free existing search list */ - free ( dns_search.data ); - memset ( &dns_search, 0, sizeof ( dns_search ) ); - - /* Fetch DNS search list */ - len = fetch_setting_copy ( NULL, &dnssl_setting, NULL, NULL, - &dns_search.data ); - if ( len >= 0 ) { - dns_search.len = len; - return; - } - - /* If no DNS search list exists, try to fetch the local domain */ - fetch_string_setting_copy ( NULL, &domain_setting, &localdomain ); - if ( localdomain ) { - len = dns_encode ( localdomain, &dns_search ); - if ( len >= 0 ) { - dns_search.data = malloc ( len ); - if ( dns_search.data ) { - dns_search.len = len; - dns_encode ( localdomain, &dns_search ); - } - } - free ( localdomain ); - return; - } -} - -/** - * Apply DNS settings - * - * @ret rc Return status code - */ -static int apply_dns_settings ( void ) { - - /* Fetch DNS server address */ - nameserver.sa.sa_family = 0; - if ( fetch_ipv6_setting ( NULL, &dns6_setting, - &nameserver.sin6.sin6_addr ) >= 0 ) { - nameserver.sin6.sin6_family = AF_INET6; - } else if ( fetch_ipv4_setting ( NULL, &dns_setting, - &nameserver.sin.sin_addr ) >= 0 ) { - nameserver.sin.sin_family = AF_INET; - } - if ( nameserver.sa.sa_family ) { - DBG ( "DNS using nameserver %s\n", - sock_ntoa ( &nameserver.sa ) ); - } - - /* Fetch DNS search list */ - apply_dns_search(); - if ( DBG_LOG && ( dns_search.len != 0 ) ) { - struct dns_name name; - int offset; - - DBG ( "DNS search list:" ); - memcpy ( &name, &dns_search, sizeof ( name ) ); - while ( name.offset != name.len ) { - DBG ( " %s", dns_name ( &name ) ); - offset = dns_skip_search ( &name ); - if ( offset < 0 ) - break; - name.offset = offset; - } - DBG ( "\n" ); - } - - return 0; -} - -/** DNS settings applicator */ -struct settings_applicator dns_applicator __settings_applicator = { - .apply = apply_dns_settings, -}; diff --git a/qemu/roms/ipxe/src/net/udp/slam.c b/qemu/roms/ipxe/src/net/udp/slam.c deleted file mode 100644 index 8b26bfb3c..000000000 --- a/qemu/roms/ipxe/src/net/udp/slam.c +++ /dev/null @@ -1,761 +0,0 @@ -/* - * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/features.h> -#include <ipxe/iobuf.h> -#include <ipxe/bitmap.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/tcpip.h> -#include <ipxe/timer.h> -#include <ipxe/retry.h> - -/** @file - * - * Scalable Local Area Multicast protocol - * - * The SLAM protocol is supported only by Etherboot; it was designed - * and implemented by Eric Biederman. A server implementation is - * available in contrib/mini-slamd. There does not appear to be any - * documentation beyond a few sparse comments in Etherboot's - * proto_slam.c. - * - * SLAM packets use three types of data field: - * - * Nul : A single NUL (0) byte, used as a list terminator - * - * Raw : A block of raw data - * - * Int : A variable-length integer, in big-endian order. The length - * of the integer is encoded in the most significant three bits. - * - * Packets received by the client have the following layout: - * - * Int : Transaction identifier. This is an opaque value. - * - * Int : Total number of bytes in the transfer. - * - * Int : Block size, in bytes. - * - * Int : Packet sequence number within the transfer (if this packet - * contains data). - * - * Raw : Packet data (if this packet contains data). - * - * Packets transmitted by the client consist of a run-length-encoded - * representation of the received-blocks bitmap, looking something - * like: - * - * Int : Number of consecutive successfully-received packets - * Int : Number of consecutive missing packets - * Int : Number of consecutive successfully-received packets - * Int : Number of consecutive missing packets - * .... - * Nul - * - */ - -FEATURE ( FEATURE_PROTOCOL, "SLAM", DHCP_EB_FEATURE_SLAM, 1 ); - -/** Default SLAM server port */ -#define SLAM_DEFAULT_PORT 10000 - -/** Default SLAM multicast IP address */ -#define SLAM_DEFAULT_MULTICAST_IP \ - ( ( 239 << 24 ) | ( 255 << 16 ) | ( 1 << 8 ) | ( 1 << 0 ) ) - -/** Default SLAM multicast port */ -#define SLAM_DEFAULT_MULTICAST_PORT 10000 - -/** Maximum SLAM header length */ -#define SLAM_MAX_HEADER_LEN ( 7 /* transaction id */ + 7 /* total_bytes */ + \ - 7 /* block_size */ ) - -/** Maximum number of blocks to request per NACK - * - * This is a policy decision equivalent to selecting a TCP window - * size. - */ -#define SLAM_MAX_BLOCKS_PER_NACK 4 - -/** Maximum SLAM NACK length - * - * We only ever send a NACK for a single range of up to @c - * SLAM_MAX_BLOCKS_PER_NACK blocks. - */ -#define SLAM_MAX_NACK_LEN ( 7 /* block */ + 7 /* #blocks */ + 1 /* NUL */ ) - -/** SLAM slave timeout */ -#define SLAM_SLAVE_TIMEOUT ( 1 * TICKS_PER_SEC ) - -/** A SLAM request */ -struct slam_request { - /** Reference counter */ - struct refcnt refcnt; - - /** Data transfer interface */ - struct interface xfer; - /** Unicast socket */ - struct interface socket; - /** Multicast socket */ - struct interface mc_socket; - - /** Master client retry timer */ - struct retry_timer master_timer; - /** Slave client retry timer */ - struct retry_timer slave_timer; - - /** Cached header */ - uint8_t header[SLAM_MAX_HEADER_LEN]; - /** Size of cached header */ - size_t header_len; - /** Total number of bytes in transfer */ - unsigned long total_bytes; - /** Transfer block size */ - unsigned long block_size; - /** Number of blocks in transfer */ - unsigned long num_blocks; - /** Block bitmap */ - struct bitmap bitmap; - /** NACK sent flag */ - int nack_sent; -}; - -/** - * Free a SLAM request - * - * @v refcnt Reference counter - */ -static void slam_free ( struct refcnt *refcnt ) { - struct slam_request *slam = - container_of ( refcnt, struct slam_request, refcnt ); - - bitmap_free ( &slam->bitmap ); - free ( slam ); -} - -/** - * Mark SLAM request as complete - * - * @v slam SLAM request - * @v rc Return status code - */ -static void slam_finished ( struct slam_request *slam, int rc ) { - static const uint8_t slam_disconnect[] = { 0 }; - - DBGC ( slam, "SLAM %p finished with status code %d (%s)\n", - slam, rc, strerror ( rc ) ); - - /* Send a disconnect message if we ever sent anything to the - * server. - */ - if ( slam->nack_sent ) { - xfer_deliver_raw ( &slam->socket, slam_disconnect, - sizeof ( slam_disconnect ) ); - } - - /* Stop the retry timers */ - stop_timer ( &slam->master_timer ); - stop_timer ( &slam->slave_timer ); - - /* Close all data transfer interfaces */ - intf_shutdown ( &slam->socket, rc ); - intf_shutdown ( &slam->mc_socket, rc ); - intf_shutdown ( &slam->xfer, rc ); -} - -/**************************************************************************** - * - * TX datapath - * - */ - -/** - * Add a variable-length value to a SLAM packet - * - * @v slam SLAM request - * @v iobuf I/O buffer - * @v value Value to add - * @ret rc Return status code - * - * Adds a variable-length value to the end of an I/O buffer. Will - * always leave at least one byte of tailroom in the I/O buffer (to - * allow space for the terminating NUL). - */ -static int slam_put_value ( struct slam_request *slam, - struct io_buffer *iobuf, unsigned long value ) { - uint8_t *data; - size_t len; - unsigned int i; - - /* Calculate variable length required to store value. Always - * leave at least one byte in the I/O buffer. - */ - len = ( ( flsl ( value ) + 10 ) / 8 ); - if ( len >= iob_tailroom ( iobuf ) ) { - DBGC2 ( slam, "SLAM %p cannot add %zd-byte value\n", - slam, len ); - return -ENOBUFS; - } - /* There is no valid way within the protocol that we can end - * up trying to push a full-sized long (i.e. without space for - * the length encoding). - */ - assert ( len <= sizeof ( value ) ); - - /* Add value */ - data = iob_put ( iobuf, len ); - for ( i = len ; i-- ; ) { - data[i] = value; - value >>= 8; - } - *data |= ( len << 5 ); - assert ( value == 0 ); - - return 0; -} - -/** - * Send SLAM NACK packet - * - * @v slam SLAM request - * @ret rc Return status code - */ -static int slam_tx_nack ( struct slam_request *slam ) { - struct io_buffer *iobuf; - unsigned long first_block; - unsigned long num_blocks; - uint8_t *nul; - int rc; - - /* Mark NACK as sent, so that we know we have to disconnect later */ - slam->nack_sent = 1; - - /* Allocate I/O buffer */ - iobuf = xfer_alloc_iob ( &slam->socket, SLAM_MAX_NACK_LEN ); - if ( ! iobuf ) { - DBGC ( slam, "SLAM %p could not allocate I/O buffer\n", - slam ); - return -ENOMEM; - } - - /* Construct NACK. We always request only a single packet; - * this allows us to force multicast-TFTP-style flow control - * on the SLAM server, which will otherwise just blast the - * data out as fast as it can. On a gigabit network, without - * RX checksumming, this would inevitably cause packet drops. - */ - first_block = bitmap_first_gap ( &slam->bitmap ); - for ( num_blocks = 1 ; ; num_blocks++ ) { - if ( num_blocks >= SLAM_MAX_BLOCKS_PER_NACK ) - break; - if ( ( first_block + num_blocks ) >= slam->num_blocks ) - break; - if ( bitmap_test ( &slam->bitmap, - ( first_block + num_blocks ) ) ) - break; - } - if ( first_block ) { - DBGCP ( slam, "SLAM %p transmitting NACK for blocks " - "%ld-%ld\n", slam, first_block, - ( first_block + num_blocks - 1 ) ); - } else { - DBGC ( slam, "SLAM %p transmitting initial NACK for blocks " - "0-%ld\n", slam, ( num_blocks - 1 ) ); - } - if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 ) - return rc; - if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 ) - return rc; - nul = iob_put ( iobuf, 1 ); - *nul = 0; - - /* Transmit packet */ - return xfer_deliver_iob ( &slam->socket, iobuf ); -} - -/** - * Handle SLAM master client retry timer expiry - * - * @v timer Master retry timer - * @v fail Failure indicator - */ -static void slam_master_timer_expired ( struct retry_timer *timer, - int fail ) { - struct slam_request *slam = - container_of ( timer, struct slam_request, master_timer ); - - if ( fail ) { - /* Allow timer to stop running. We will terminate the - * connection only if the slave timer times out. - */ - DBGC ( slam, "SLAM %p giving up acting as master client\n", - slam ); - } else { - /* Retransmit NACK */ - start_timer ( timer ); - slam_tx_nack ( slam ); - } -} - -/** - * Handle SLAM slave client retry timer expiry - * - * @v timer Master retry timer - * @v fail Failure indicator - */ -static void slam_slave_timer_expired ( struct retry_timer *timer, - int fail ) { - struct slam_request *slam = - container_of ( timer, struct slam_request, slave_timer ); - - if ( fail ) { - /* Terminate connection */ - slam_finished ( slam, -ETIMEDOUT ); - } else { - /* Try sending a NACK */ - DBGC ( slam, "SLAM %p trying to become master client\n", - slam ); - start_timer ( timer ); - slam_tx_nack ( slam ); - } -} - -/**************************************************************************** - * - * RX datapath - * - */ - -/** - * Read and strip a variable-length value from a SLAM packet - * - * @v slam SLAM request - * @v iobuf I/O buffer - * @v value Value to fill in, or NULL to ignore value - * @ret rc Return status code - * - * Reads a variable-length value from the start of the I/O buffer. - */ -static int slam_pull_value ( struct slam_request *slam, - struct io_buffer *iobuf, - unsigned long *value ) { - uint8_t *data; - size_t len; - - /* Sanity check */ - if ( iob_len ( iobuf ) == 0 ) { - DBGC ( slam, "SLAM %p empty value\n", slam ); - return -EINVAL; - } - - /* Read and verify length of value */ - data = iobuf->data; - len = ( *data >> 5 ); - if ( ( len == 0 ) || - ( value && ( len > sizeof ( *value ) ) ) ) { - DBGC ( slam, "SLAM %p invalid value length %zd bytes\n", - slam, len ); - return -EINVAL; - } - if ( len > iob_len ( iobuf ) ) { - DBGC ( slam, "SLAM %p value extends beyond I/O buffer\n", - slam ); - return -EINVAL; - } - - /* Read value */ - iob_pull ( iobuf, len ); - *value = ( *data & 0x1f ); - while ( --len ) { - *value <<= 8; - *value |= *(++data); - } - - return 0; -} - -/** - * Read and strip SLAM header - * - * @v slam SLAM request - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int slam_pull_header ( struct slam_request *slam, - struct io_buffer *iobuf ) { - void *header = iobuf->data; - int rc; - - /* If header matches cached header, just pull it and return */ - if ( ( slam->header_len <= iob_len ( iobuf ) ) && - ( memcmp ( slam->header, iobuf->data, slam->header_len ) == 0 )){ - iob_pull ( iobuf, slam->header_len ); - return 0; - } - - DBGC ( slam, "SLAM %p detected changed header; resetting\n", slam ); - - /* Read and strip transaction ID, total number of bytes, and - * block size. - */ - if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 ) - return rc; - if ( ( rc = slam_pull_value ( slam, iobuf, - &slam->total_bytes ) ) != 0 ) - return rc; - if ( ( rc = slam_pull_value ( slam, iobuf, - &slam->block_size ) ) != 0 ) - return rc; - - /* Update the cached header */ - slam->header_len = ( iobuf->data - header ); - assert ( slam->header_len <= sizeof ( slam->header ) ); - memcpy ( slam->header, header, slam->header_len ); - - /* Calculate number of blocks */ - slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) / - slam->block_size ); - - DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num " - "blocks %ld\n", slam, slam->total_bytes, slam->block_size, - slam->num_blocks ); - - /* Discard and reset the bitmap */ - bitmap_free ( &slam->bitmap ); - memset ( &slam->bitmap, 0, sizeof ( slam->bitmap ) ); - - /* Allocate a new bitmap */ - if ( ( rc = bitmap_resize ( &slam->bitmap, - slam->num_blocks ) ) != 0 ) { - /* Failure to allocate a bitmap is fatal */ - DBGC ( slam, "SLAM %p could not allocate bitmap for %ld " - "blocks: %s\n", slam, slam->num_blocks, - strerror ( rc ) ); - slam_finished ( slam, rc ); - return rc; - } - - /* Notify recipient of file size */ - xfer_seek ( &slam->xfer, slam->total_bytes ); - - return 0; -} - -/** - * Receive SLAM data packet - * - * @v slam SLAM request - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int slam_mc_socket_deliver ( struct slam_request *slam, - struct io_buffer *iobuf, - struct xfer_metadata *rx_meta __unused ) { - struct xfer_metadata meta; - unsigned long packet; - size_t len; - int rc; - - /* Stop the master client timer. Restart the slave client timer. */ - stop_timer ( &slam->master_timer ); - stop_timer ( &slam->slave_timer ); - start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); - - /* Read and strip packet header */ - if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) - goto err_discard; - - /* Read and strip packet number */ - if ( ( rc = slam_pull_value ( slam, iobuf, &packet ) ) != 0 ) - goto err_discard; - - /* Sanity check packet number */ - if ( packet >= slam->num_blocks ) { - DBGC ( slam, "SLAM %p received out-of-range packet %ld " - "(num_blocks=%ld)\n", slam, packet, slam->num_blocks ); - rc = -EINVAL; - goto err_discard; - } - - /* Sanity check length */ - len = iob_len ( iobuf ); - if ( len > slam->block_size ) { - DBGC ( slam, "SLAM %p received oversize packet of %zd bytes " - "(block_size=%ld)\n", slam, len, slam->block_size ); - rc = -EINVAL; - goto err_discard; - } - if ( ( packet != ( slam->num_blocks - 1 ) ) && - ( len < slam->block_size ) ) { - DBGC ( slam, "SLAM %p received short packet of %zd bytes " - "(block_size=%ld)\n", slam, len, slam->block_size ); - rc = -EINVAL; - goto err_discard; - } - - /* If we have already seen this packet, discard it */ - if ( bitmap_test ( &slam->bitmap, packet ) ) { - goto discard; - } - - /* Pass to recipient */ - memset ( &meta, 0, sizeof ( meta ) ); - meta.flags = XFER_FL_ABS_OFFSET; - meta.offset = ( packet * slam->block_size ); - if ( ( rc = xfer_deliver ( &slam->xfer, iobuf, &meta ) ) != 0 ) - goto err; - - /* Mark block as received */ - bitmap_set ( &slam->bitmap, packet ); - - /* If we have received all blocks, terminate */ - if ( bitmap_full ( &slam->bitmap ) ) - slam_finished ( slam, 0 ); - - return 0; - - err_discard: - discard: - free_iob ( iobuf ); - err: - return rc; -} - -/** - * Receive SLAM non-data packet - * - * @v slam SLAM request - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int slam_socket_deliver ( struct slam_request *slam, - struct io_buffer *iobuf, - struct xfer_metadata *rx_meta __unused ) { - int rc; - - /* Restart the master client timer */ - stop_timer ( &slam->master_timer ); - start_timer ( &slam->master_timer ); - - /* Read and strip packet header */ - if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) - goto discard; - - /* Sanity check */ - if ( iob_len ( iobuf ) != 0 ) { - DBGC ( slam, "SLAM %p received trailing garbage:\n", slam ); - DBGC_HD ( slam, iobuf->data, iob_len ( iobuf ) ); - rc = -EINVAL; - goto discard; - } - - /* Discard packet */ - free_iob ( iobuf ); - - /* Send NACK in reply */ - slam_tx_nack ( slam ); - - return 0; - - discard: - free_iob ( iobuf ); - return rc; - -} - -/** SLAM unicast socket interface operations */ -static struct interface_operation slam_socket_operations[] = { - INTF_OP ( xfer_deliver, struct slam_request *, slam_socket_deliver ), - INTF_OP ( intf_close, struct slam_request *, slam_finished ), -}; - -/** SLAM unicast socket interface descriptor */ -static struct interface_descriptor slam_socket_desc = - INTF_DESC ( struct slam_request, socket, slam_socket_operations ); - -/** SLAM multicast socket interface operations */ -static struct interface_operation slam_mc_socket_operations[] = { - INTF_OP ( xfer_deliver, struct slam_request *, slam_mc_socket_deliver ), - INTF_OP ( intf_close, struct slam_request *, slam_finished ), -}; - -/** SLAM multicast socket interface descriptor */ -static struct interface_descriptor slam_mc_socket_desc = - INTF_DESC ( struct slam_request, mc_socket, slam_mc_socket_operations ); - -/**************************************************************************** - * - * Data transfer interface - * - */ - -/** SLAM data transfer interface operations */ -static struct interface_operation slam_xfer_operations[] = { - INTF_OP ( intf_close, struct slam_request *, slam_finished ), -}; - -/** SLAM data transfer interface descriptor */ -static struct interface_descriptor slam_xfer_desc = - INTF_DESC ( struct slam_request, xfer, slam_xfer_operations ); - -/** - * Parse SLAM URI multicast address - * - * @v slam SLAM request - * @v path Path portion of x-slam:// URI - * @v address Socket address to fill in - * @ret rc Return status code - */ -static int slam_parse_multicast_address ( struct slam_request *slam, - const char *path, - struct sockaddr_in *address ) { - char path_dup[ strlen ( path ) /* no +1 */ ]; - char *sep; - char *end; - - /* Create temporary copy of path, minus the leading '/' */ - assert ( *path == '/' ); - memcpy ( path_dup, ( path + 1 ) , sizeof ( path_dup ) ); - - /* Parse port, if present */ - sep = strchr ( path_dup, ':' ); - if ( sep ) { - *(sep++) = '\0'; - address->sin_port = htons ( strtoul ( sep, &end, 0 ) ); - if ( *end != '\0' ) { - DBGC ( slam, "SLAM %p invalid multicast port " - "\"%s\"\n", slam, sep ); - return -EINVAL; - } - } - - /* Parse address */ - if ( inet_aton ( path_dup, &address->sin_addr ) == 0 ) { - DBGC ( slam, "SLAM %p invalid multicast address \"%s\"\n", - slam, path_dup ); - return -EINVAL; - } - - return 0; -} - -/** - * Initiate a SLAM request - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int slam_open ( struct interface *xfer, struct uri *uri ) { - static const struct sockaddr_in default_multicast = { - .sin_family = AF_INET, - .sin_port = htons ( SLAM_DEFAULT_MULTICAST_PORT ), - .sin_addr = { htonl ( SLAM_DEFAULT_MULTICAST_IP ) }, - }; - struct slam_request *slam; - struct sockaddr_tcpip server; - struct sockaddr_in multicast; - int rc; - - /* Sanity checks */ - if ( ! uri->host ) - return -EINVAL; - - /* Allocate and populate structure */ - slam = zalloc ( sizeof ( *slam ) ); - if ( ! slam ) - return -ENOMEM; - ref_init ( &slam->refcnt, slam_free ); - intf_init ( &slam->xfer, &slam_xfer_desc, &slam->refcnt ); - intf_init ( &slam->socket, &slam_socket_desc, &slam->refcnt ); - intf_init ( &slam->mc_socket, &slam_mc_socket_desc, &slam->refcnt ); - timer_init ( &slam->master_timer, slam_master_timer_expired, - &slam->refcnt ); - timer_init ( &slam->slave_timer, slam_slave_timer_expired, - &slam->refcnt ); - /* Fake an invalid cached header of { 0x00, ... } */ - slam->header_len = 1; - /* Fake parameters for initial NACK */ - slam->num_blocks = 1; - if ( ( rc = bitmap_resize ( &slam->bitmap, 1 ) ) != 0 ) { - DBGC ( slam, "SLAM %p could not allocate initial bitmap: " - "%s\n", slam, strerror ( rc ) ); - goto err; - } - - /* Open unicast socket */ - memset ( &server, 0, sizeof ( server ) ); - server.st_port = htons ( uri_port ( uri, SLAM_DEFAULT_PORT ) ); - if ( ( rc = xfer_open_named_socket ( &slam->socket, SOCK_DGRAM, - ( struct sockaddr * ) &server, - uri->host, NULL ) ) != 0 ) { - DBGC ( slam, "SLAM %p could not open unicast socket: %s\n", - slam, strerror ( rc ) ); - goto err; - } - - /* Open multicast socket */ - memcpy ( &multicast, &default_multicast, sizeof ( multicast ) ); - if ( uri->path && - ( ( rc = slam_parse_multicast_address ( slam, uri->path, - &multicast ) ) != 0 ) ) { - goto err; - } - if ( ( rc = xfer_open_socket ( &slam->mc_socket, SOCK_DGRAM, - ( struct sockaddr * ) &multicast, - ( struct sockaddr * ) &multicast ) ) != 0 ) { - DBGC ( slam, "SLAM %p could not open multicast socket: %s\n", - slam, strerror ( rc ) ); - goto err; - } - - /* Start slave retry timer */ - start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &slam->xfer, xfer ); - ref_put ( &slam->refcnt ); - return 0; - - err: - slam_finished ( slam, rc ); - ref_put ( &slam->refcnt ); - return rc; -} - -/** SLAM URI opener */ -struct uri_opener slam_uri_opener __uri_opener = { - .scheme = "x-slam", - .open = slam_open, -}; diff --git a/qemu/roms/ipxe/src/net/udp/syslog.c b/qemu/roms/ipxe/src/net/udp/syslog.c deleted file mode 100644 index b6eee6036..000000000 --- a/qemu/roms/ipxe/src/net/udp/syslog.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -/** @file - * - * Syslog protocol - * - */ - -#include <stdint.h> -#include <stdlib.h> -#include <ctype.h> -#include <byteswap.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/tcpip.h> -#include <ipxe/dhcp.h> -#include <ipxe/dhcpv6.h> -#include <ipxe/settings.h> -#include <ipxe/console.h> -#include <ipxe/lineconsole.h> -#include <ipxe/syslog.h> -#include <config/console.h> - -/* Set default console usage if applicable */ -#if ! ( defined ( CONSOLE_SYSLOG ) && CONSOLE_EXPLICIT ( CONSOLE_SYSLOG ) ) -#undef CONSOLE_SYSLOG -#define CONSOLE_SYSLOG ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_TUI ) -#endif - -/** The syslog server */ -static union { - struct sockaddr sa; - struct sockaddr_tcpip st; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; -} logserver = { - .st = { - .st_port = htons ( SYSLOG_PORT ), - }, -}; - -/** Syslog UDP interface operations */ -static struct interface_operation syslogger_operations[] = {}; - -/** Syslog UDP interface descriptor */ -static struct interface_descriptor syslogger_desc = - INTF_DESC_PURE ( syslogger_operations ); - -/** The syslog UDP interface */ -static struct interface syslogger = INTF_INIT ( syslogger_desc ); - -/****************************************************************************** - * - * Console driver - * - ****************************************************************************** - */ - -/** Host name (for log messages) */ -static char *syslog_hostname; - -/** Domain name (for log messages) */ -static char *syslog_domain; - -/** - * Transmit formatted syslog message - * - * @v xfer Data transfer interface - * @v severity Severity - * @v message Message - * @v terminator Message terminator - * @ret rc Return status code - */ -int syslog_send ( struct interface *xfer, unsigned int severity, - const char *message, const char *terminator ) { - const char *hostname = ( syslog_hostname ? syslog_hostname : "" ); - const char *domain = ( ( hostname[0] && syslog_domain ) ? - syslog_domain : "" ); - - return xfer_printf ( xfer, "<%d>%s%s%s%sipxe: %s%s", - SYSLOG_PRIORITY ( SYSLOG_DEFAULT_FACILITY, - severity ), hostname, - ( domain[0] ? "." : "" ), domain, - ( hostname[0] ? " " : "" ), message, terminator ); -} - -/****************************************************************************** - * - * Console driver - * - ****************************************************************************** - */ - -/** Syslog line buffer */ -static char syslog_buffer[SYSLOG_BUFSIZE]; - -/** Syslog severity */ -static unsigned int syslog_severity = SYSLOG_DEFAULT_SEVERITY; - -/** - * Handle ANSI set syslog priority (private sequence) - * - * @v ctx ANSI escape sequence context - * @v count Parameter count - * @v params List of graphic rendition aspects - */ -static void syslog_handle_priority ( struct ansiesc_context *ctx __unused, - unsigned int count __unused, - int params[] ) { - if ( params[0] >= 0 ) { - syslog_severity = params[0]; - } else { - syslog_severity = SYSLOG_DEFAULT_SEVERITY; - } -} - -/** Syslog ANSI escape sequence handlers */ -static struct ansiesc_handler syslog_handlers[] = { - { ANSIESC_LOG_PRIORITY, syslog_handle_priority }, - { 0, NULL } -}; - -/** Syslog line console */ -static struct line_console syslog_line = { - .buffer = syslog_buffer, - .len = sizeof ( syslog_buffer ), - .ctx = { - .handlers = syslog_handlers, - }, -}; - -/** Syslog recursion marker */ -static int syslog_entered; - -/** - * Print a character to syslog console - * - * @v character Character to be printed - */ -static void syslog_putchar ( int character ) { - int rc; - - /* Ignore if we are already mid-logging */ - if ( syslog_entered ) - return; - - /* Fill line buffer */ - if ( line_putchar ( &syslog_line, character ) == 0 ) - return; - - /* Guard against re-entry */ - syslog_entered = 1; - - /* Send log message */ - if ( ( rc = syslog_send ( &syslogger, syslog_severity, - syslog_buffer, "" ) ) != 0 ) { - DBG ( "SYSLOG could not send log message: %s\n", - strerror ( rc ) ); - } - - /* Clear re-entry flag */ - syslog_entered = 0; -} - -/** Syslog console driver */ -struct console_driver syslog_console __console_driver = { - .putchar = syslog_putchar, - .disabled = CONSOLE_DISABLED, - .usage = CONSOLE_SYSLOG, -}; - -/****************************************************************************** - * - * Settings - * - ****************************************************************************** - */ - -/** IPv4 syslog server setting */ -const struct setting syslog_setting __setting ( SETTING_MISC, syslog ) = { - .name = "syslog", - .description = "Syslog server", - .tag = DHCP_LOG_SERVERS, - .type = &setting_type_ipv4, -}; - -/** IPv6 syslog server setting */ -const struct setting syslog6_setting __setting ( SETTING_MISC, syslog6 ) = { - .name = "syslog6", - .description = "Syslog server", - .tag = DHCPV6_LOG_SERVERS, - .type = &setting_type_ipv6, - .scope = &ipv6_scope, -}; - -/** - * Strip invalid characters from host/domain name - * - * @v name Name to strip - */ -static void syslog_fix_name ( char *name ) { - char *fixed = name; - int c; - - /* Do nothing if name does not exist */ - if ( ! name ) - return; - - /* Strip any non-printable or whitespace characters from the name */ - do { - c = *(name++); - *fixed = c; - if ( isprint ( c ) && ! isspace ( c ) ) - fixed++; - } while ( c ); -} - -/** - * Apply syslog settings - * - * @ret rc Return status code - */ -static int apply_syslog_settings ( void ) { - struct sockaddr old_logserver; - int rc; - - /* Fetch hostname and domain name */ - free ( syslog_hostname ); - fetch_string_setting_copy ( NULL, &hostname_setting, &syslog_hostname ); - syslog_fix_name ( syslog_hostname ); - free ( syslog_domain ); - fetch_string_setting_copy ( NULL, &domain_setting, &syslog_domain ); - syslog_fix_name ( syslog_domain ); - - /* Fetch log server */ - syslog_console.disabled = CONSOLE_DISABLED; - memcpy ( &old_logserver, &logserver, sizeof ( old_logserver ) ); - logserver.sa.sa_family = 0; - if ( fetch_ipv6_setting ( NULL, &syslog6_setting, - &logserver.sin6.sin6_addr ) >= 0 ) { - logserver.sin6.sin6_family = AF_INET6; - } else if ( fetch_ipv4_setting ( NULL, &syslog_setting, - &logserver.sin.sin_addr ) >= 0 ) { - logserver.sin.sin_family = AF_INET; - } - if ( logserver.sa.sa_family ) { - syslog_console.disabled = 0; - DBG ( "SYSLOG using log server %s\n", - sock_ntoa ( &logserver.sa ) ); - } - - /* Do nothing unless log server has changed */ - if ( memcmp ( &logserver, &old_logserver, sizeof ( logserver ) ) == 0 ) - return 0; - - /* Reset syslog connection */ - intf_restart ( &syslogger, 0 ); - - /* Do nothing unless we have a log server */ - if ( syslog_console.disabled ) { - DBG ( "SYSLOG has no log server\n" ); - return 0; - } - - /* Connect to log server */ - if ( ( rc = xfer_open_socket ( &syslogger, SOCK_DGRAM, - &logserver.sa, NULL ) ) != 0 ) { - DBG ( "SYSLOG cannot connect to log server: %s\n", - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** Syslog settings applicator */ -struct settings_applicator syslog_applicator __settings_applicator = { - .apply = apply_syslog_settings, -}; diff --git a/qemu/roms/ipxe/src/net/udp/tftp.c b/qemu/roms/ipxe/src/net/udp/tftp.c deleted file mode 100644 index 953bcb46a..000000000 --- a/qemu/roms/ipxe/src/net/udp/tftp.c +++ /dev/null @@ -1,1218 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <byteswap.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/refcnt.h> -#include <ipxe/iobuf.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/uri.h> -#include <ipxe/tcpip.h> -#include <ipxe/retry.h> -#include <ipxe/features.h> -#include <ipxe/bitmap.h> -#include <ipxe/settings.h> -#include <ipxe/dhcp.h> -#include <ipxe/uri.h> -#include <ipxe/tftp.h> - -/** @file - * - * TFTP protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "TFTP", DHCP_EB_FEATURE_TFTP, 1 ); - -/* TFTP-specific error codes */ -#define EINVAL_BLKSIZE __einfo_error ( EINFO_EINVAL_BLKSIZE ) -#define EINFO_EINVAL_BLKSIZE __einfo_uniqify \ - ( EINFO_EINVAL, 0x01, "Invalid blksize" ) -#define EINVAL_TSIZE __einfo_error ( EINFO_EINVAL_TSIZE ) -#define EINFO_EINVAL_TSIZE __einfo_uniqify \ - ( EINFO_EINVAL, 0x02, "Invalid tsize" ) -#define EINVAL_MC_NO_PORT __einfo_error ( EINFO_EINVAL_MC_NO_PORT ) -#define EINFO_EINVAL_MC_NO_PORT __einfo_uniqify \ - ( EINFO_EINVAL, 0x03, "Missing multicast port" ) -#define EINVAL_MC_NO_MC __einfo_error ( EINFO_EINVAL_MC_NO_MC ) -#define EINFO_EINVAL_MC_NO_MC __einfo_uniqify \ - ( EINFO_EINVAL, 0x04, "Missing multicast mc" ) -#define EINVAL_MC_INVALID_MC __einfo_error ( EINFO_EINVAL_MC_INVALID_MC ) -#define EINFO_EINVAL_MC_INVALID_MC __einfo_uniqify \ - ( EINFO_EINVAL, 0x05, "Missing multicast IP" ) -#define EINVAL_MC_INVALID_IP __einfo_error ( EINFO_EINVAL_MC_INVALID_IP ) -#define EINFO_EINVAL_MC_INVALID_IP __einfo_uniqify \ - ( EINFO_EINVAL, 0x06, "Invalid multicast IP" ) -#define EINVAL_MC_INVALID_PORT __einfo_error ( EINFO_EINVAL_MC_INVALID_PORT ) -#define EINFO_EINVAL_MC_INVALID_PORT __einfo_uniqify \ - ( EINFO_EINVAL, 0x07, "Invalid multicast port" ) - -/** - * A TFTP request - * - * This data structure holds the state for an ongoing TFTP transfer. - */ -struct tftp_request { - /** Reference count */ - struct refcnt refcnt; - /** Data transfer interface */ - struct interface xfer; - - /** URI being fetched */ - struct uri *uri; - /** Transport layer interface */ - struct interface socket; - /** Multicast transport layer interface */ - struct interface mc_socket; - - /** Data block size - * - * This is the "blksize" option negotiated with the TFTP - * server. (If the TFTP server does not support TFTP options, - * this will default to 512). - */ - unsigned int blksize; - /** File size - * - * This is the value returned in the "tsize" option from the - * TFTP server. If the TFTP server does not support the - * "tsize" option, this value will be zero. - */ - unsigned long tsize; - - /** Server port - * - * This is the port to which RRQ packets are sent. - */ - unsigned int port; - /** Peer address - * - * The peer address is determined by the first response - * received to the TFTP RRQ. - */ - struct sockaddr_tcpip peer; - /** Request flags */ - unsigned int flags; - /** MTFTP timeout count */ - unsigned int mtftp_timeouts; - - /** Block bitmap */ - struct bitmap bitmap; - /** Maximum known length - * - * We don't always know the file length in advance. In - * particular, if the TFTP server doesn't support the tsize - * option, or we are using MTFTP, then we don't know the file - * length until we see the end-of-file block (which, in the - * case of MTFTP, may not be the last block we see). - * - * This value is updated whenever we obtain information about - * the file length. - */ - size_t filesize; - /** Retransmission timer */ - struct retry_timer timer; -}; - -/** TFTP request flags */ -enum { - /** Send ACK packets */ - TFTP_FL_SEND_ACK = 0x0001, - /** Request blksize and tsize options */ - TFTP_FL_RRQ_SIZES = 0x0002, - /** Request multicast option */ - TFTP_FL_RRQ_MULTICAST = 0x0004, - /** Perform MTFTP recovery on timeout */ - TFTP_FL_MTFTP_RECOVERY = 0x0008, -}; - -/** Maximum number of MTFTP open requests before falling back to TFTP */ -#define MTFTP_MAX_TIMEOUTS 3 - -/** - * Free TFTP request - * - * @v refcnt Reference counter - */ -static void tftp_free ( struct refcnt *refcnt ) { - struct tftp_request *tftp = - container_of ( refcnt, struct tftp_request, refcnt ); - - uri_put ( tftp->uri ); - bitmap_free ( &tftp->bitmap ); - free ( tftp ); -} - -/** - * Mark TFTP request as complete - * - * @v tftp TFTP connection - * @v rc Return status code - */ -static void tftp_done ( struct tftp_request *tftp, int rc ) { - - DBGC ( tftp, "TFTP %p finished with status %d (%s)\n", - tftp, rc, strerror ( rc ) ); - - /* Stop the retry timer */ - stop_timer ( &tftp->timer ); - - /* Close all data transfer interfaces */ - intf_shutdown ( &tftp->socket, rc ); - intf_shutdown ( &tftp->mc_socket, rc ); - intf_shutdown ( &tftp->xfer, rc ); -} - -/** - * Reopen TFTP socket - * - * @v tftp TFTP connection - * @ret rc Return status code - */ -static int tftp_reopen ( struct tftp_request *tftp ) { - struct sockaddr_tcpip server; - int rc; - - /* Close socket */ - intf_restart ( &tftp->socket, 0 ); - - /* Disable ACK sending. */ - tftp->flags &= ~TFTP_FL_SEND_ACK; - - /* Reset peer address */ - memset ( &tftp->peer, 0, sizeof ( tftp->peer ) ); - - /* Open socket */ - memset ( &server, 0, sizeof ( server ) ); - server.st_port = htons ( tftp->port ); - if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM, - ( struct sockaddr * ) &server, - tftp->uri->host, NULL ) ) != 0 ) { - DBGC ( tftp, "TFTP %p could not open socket: %s\n", - tftp, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Reopen TFTP multicast socket - * - * @v tftp TFTP connection - * @v local Local socket address - * @ret rc Return status code - */ -static int tftp_reopen_mc ( struct tftp_request *tftp, - struct sockaddr *local ) { - int rc; - - /* Close multicast socket */ - intf_restart ( &tftp->mc_socket, 0 ); - - /* Open multicast socket. We never send via this socket, so - * use the local address as the peer address (since the peer - * address cannot be NULL). - */ - if ( ( rc = xfer_open_socket ( &tftp->mc_socket, SOCK_DGRAM, - local, local ) ) != 0 ) { - DBGC ( tftp, "TFTP %p could not open multicast " - "socket: %s\n", tftp, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Presize TFTP receive buffers and block bitmap - * - * @v tftp TFTP connection - * @v filesize Known minimum file size - * @ret rc Return status code - */ -static int tftp_presize ( struct tftp_request *tftp, size_t filesize ) { - unsigned int num_blocks; - int rc; - - /* Do nothing if we are already large enough */ - if ( filesize <= tftp->filesize ) - return 0; - - /* Record filesize */ - tftp->filesize = filesize; - - /* Notify recipient of file size */ - xfer_seek ( &tftp->xfer, filesize ); - xfer_seek ( &tftp->xfer, 0 ); - - /* Calculate expected number of blocks. Note that files whose - * length is an exact multiple of the blocksize will have a - * trailing zero-length block, which must be included. - */ - num_blocks = ( ( filesize / tftp->blksize ) + 1 ); - if ( ( rc = bitmap_resize ( &tftp->bitmap, num_blocks ) ) != 0 ) { - DBGC ( tftp, "TFTP %p could not resize bitmap to %d blocks: " - "%s\n", tftp, num_blocks, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * MTFTP multicast receive address - * - * This is treated as a global configuration parameter. - */ -static struct sockaddr_in tftp_mtftp_socket = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl ( 0xefff0101 ), - .sin_port = htons ( 3001 ), -}; - -/** - * Set MTFTP multicast address - * - * @v address Multicast IPv4 address - */ -void tftp_set_mtftp_address ( struct in_addr address ) { - tftp_mtftp_socket.sin_addr = address; -} - -/** - * Set MTFTP multicast port - * - * @v port Multicast port - */ -void tftp_set_mtftp_port ( unsigned int port ) { - tftp_mtftp_socket.sin_port = htons ( port ); -} - -/** - * Transmit RRQ - * - * @v tftp TFTP connection - * @ret rc Return status code - */ -static int tftp_send_rrq ( struct tftp_request *tftp ) { - const char *path = tftp->uri->path; - struct tftp_rrq *rrq; - size_t len; - struct io_buffer *iobuf; - size_t blksize; - - DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path ); - - /* Allocate buffer */ - len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */ - + 5 + 1 /* "octet" + NUL */ - + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */ - + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */ - + 9 + 1 + 1 /* "multicast" + NUL + NUL */ ); - iobuf = xfer_alloc_iob ( &tftp->socket, len ); - if ( ! iobuf ) - return -ENOMEM; - - /* Determine block size */ - blksize = xfer_window ( &tftp->xfer ); - if ( blksize > TFTP_MAX_BLKSIZE ) - blksize = TFTP_MAX_BLKSIZE; - - /* Build request */ - rrq = iob_put ( iobuf, sizeof ( *rrq ) ); - rrq->opcode = htons ( TFTP_RRQ ); - iob_put ( iobuf, snprintf ( iobuf->tail, iob_tailroom ( iobuf ), - "%s%coctet", path, 0 ) + 1 ); - if ( tftp->flags & TFTP_FL_RRQ_SIZES ) { - iob_put ( iobuf, snprintf ( iobuf->tail, - iob_tailroom ( iobuf ), - "blksize%c%zd%ctsize%c0", - 0, blksize, 0, 0 ) + 1 ); - } - if ( tftp->flags & TFTP_FL_RRQ_MULTICAST ) { - iob_put ( iobuf, snprintf ( iobuf->tail, - iob_tailroom ( iobuf ), - "multicast%c", 0 ) + 1 ); - } - - /* RRQ always goes to the address specified in the initial - * xfer_open() call - */ - return xfer_deliver_iob ( &tftp->socket, iobuf ); -} - -/** - * Transmit ACK - * - * @v tftp TFTP connection - * @ret rc Return status code - */ -static int tftp_send_ack ( struct tftp_request *tftp ) { - struct tftp_ack *ack; - struct io_buffer *iobuf; - struct xfer_metadata meta = { - .dest = ( struct sockaddr * ) &tftp->peer, - }; - unsigned int block; - - /* Determine next required block number */ - block = bitmap_first_gap ( &tftp->bitmap ); - DBGC2 ( tftp, "TFTP %p sending ACK for block %d\n", tftp, block ); - - /* Allocate buffer */ - iobuf = xfer_alloc_iob ( &tftp->socket, sizeof ( *ack ) ); - if ( ! iobuf ) - return -ENOMEM; - - /* Build ACK */ - ack = iob_put ( iobuf, sizeof ( *ack ) ); - ack->opcode = htons ( TFTP_ACK ); - ack->block = htons ( block ); - - /* ACK always goes to the peer recorded from the RRQ response */ - return xfer_deliver ( &tftp->socket, iobuf, &meta ); -} - -/** - * Transmit ERROR (Abort) - * - * @v tftp TFTP connection - * @v errcode TFTP error code - * @v errmsg Error message string - * @ret rc Return status code - */ -static int tftp_send_error ( struct tftp_request *tftp, int errcode, - const char *errmsg ) { - struct tftp_error *err; - struct io_buffer *iobuf; - struct xfer_metadata meta = { - .dest = ( struct sockaddr * ) &tftp->peer, - }; - size_t msglen; - - DBGC2 ( tftp, "TFTP %p sending ERROR %d: %s\n", tftp, errcode, - errmsg ); - - /* Allocate buffer */ - msglen = sizeof ( *err ) + strlen ( errmsg ) + 1 /* NUL */; - iobuf = xfer_alloc_iob ( &tftp->socket, msglen ); - if ( ! iobuf ) - return -ENOMEM; - - /* Build ERROR */ - err = iob_put ( iobuf, msglen ); - err->opcode = htons ( TFTP_ERROR ); - err->errcode = htons ( errcode ); - strcpy ( err->errmsg, errmsg ); - - /* ERR always goes to the peer recorded from the RRQ response */ - return xfer_deliver ( &tftp->socket, iobuf, &meta ); -} - -/** - * Transmit next relevant packet - * - * @v tftp TFTP connection - * @ret rc Return status code - */ -static int tftp_send_packet ( struct tftp_request *tftp ) { - - /* Update retransmission timer. While name resolution takes place the - * window is zero. Avoid unnecessary delay after name resolution - * completes by retrying immediately. - */ - stop_timer ( &tftp->timer ); - if ( xfer_window ( &tftp->socket ) ) { - start_timer ( &tftp->timer ); - } else { - start_timer_nodelay ( &tftp->timer ); - } - - /* Send RRQ or ACK as appropriate */ - if ( ! tftp->peer.st_family ) { - return tftp_send_rrq ( tftp ); - } else { - if ( tftp->flags & TFTP_FL_SEND_ACK ) { - return tftp_send_ack ( tftp ); - } else { - return 0; - } - } -} - -/** - * Handle TFTP retransmission timer expiry - * - * @v timer Retry timer - * @v fail Failure indicator - */ -static void tftp_timer_expired ( struct retry_timer *timer, int fail ) { - struct tftp_request *tftp = - container_of ( timer, struct tftp_request, timer ); - int rc; - - /* If we are doing MTFTP, attempt the various recovery strategies */ - if ( tftp->flags & TFTP_FL_MTFTP_RECOVERY ) { - if ( tftp->peer.st_family ) { - /* If we have received any response from the server, - * try resending the RRQ to restart the download. - */ - DBGC ( tftp, "TFTP %p attempting reopen\n", tftp ); - if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) - goto err; - } else { - /* Fall back to plain TFTP after several attempts */ - tftp->mtftp_timeouts++; - DBGC ( tftp, "TFTP %p timeout %d waiting for MTFTP " - "open\n", tftp, tftp->mtftp_timeouts ); - - if ( tftp->mtftp_timeouts > MTFTP_MAX_TIMEOUTS ) { - DBGC ( tftp, "TFTP %p falling back to plain " - "TFTP\n", tftp ); - tftp->flags = TFTP_FL_RRQ_SIZES; - - /* Close multicast socket */ - intf_restart ( &tftp->mc_socket, 0 ); - - /* Reset retry timer */ - start_timer_nodelay ( &tftp->timer ); - - /* The blocksize may change: discard - * the block bitmap - */ - bitmap_free ( &tftp->bitmap ); - memset ( &tftp->bitmap, 0, - sizeof ( tftp->bitmap ) ); - - /* Reopen on standard TFTP port */ - tftp->port = TFTP_PORT; - if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) - goto err; - } - } - } else { - /* Not doing MTFTP (or have fallen back to plain - * TFTP); fail as per normal. - */ - if ( fail ) { - rc = -ETIMEDOUT; - goto err; - } - } - tftp_send_packet ( tftp ); - return; - - err: - tftp_done ( tftp, rc ); -} - -/** - * Process TFTP "blksize" option - * - * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code - */ -static int tftp_process_blksize ( struct tftp_request *tftp, - const char *value ) { - char *end; - - tftp->blksize = strtoul ( value, &end, 10 ); - if ( *end ) { - DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n", - tftp, value ); - return -EINVAL_BLKSIZE; - } - DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize ); - - return 0; -} - -/** - * Process TFTP "tsize" option - * - * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code - */ -static int tftp_process_tsize ( struct tftp_request *tftp, - const char *value ) { - char *end; - - tftp->tsize = strtoul ( value, &end, 10 ); - if ( *end ) { - DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n", - tftp, value ); - return -EINVAL_TSIZE; - } - DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize ); - - return 0; -} - -/** - * Process TFTP "multicast" option - * - * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code - */ -static int tftp_process_multicast ( struct tftp_request *tftp, - const char *value ) { - union { - struct sockaddr sa; - struct sockaddr_in sin; - } socket; - char buf[ strlen ( value ) + 1 ]; - char *addr; - char *port; - char *port_end; - char *mc; - char *mc_end; - int rc; - - /* Split value into "addr,port,mc" fields */ - memcpy ( buf, value, sizeof ( buf ) ); - addr = buf; - port = strchr ( addr, ',' ); - if ( ! port ) { - DBGC ( tftp, "TFTP %p multicast missing port,mc\n", tftp ); - return -EINVAL_MC_NO_PORT; - } - *(port++) = '\0'; - mc = strchr ( port, ',' ); - if ( ! mc ) { - DBGC ( tftp, "TFTP %p multicast missing mc\n", tftp ); - return -EINVAL_MC_NO_MC; - } - *(mc++) = '\0'; - - /* Parse parameters */ - if ( strtoul ( mc, &mc_end, 0 ) == 0 ) - tftp->flags &= ~TFTP_FL_SEND_ACK; - if ( *mc_end ) { - DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc ); - return -EINVAL_MC_INVALID_MC; - } - DBGC ( tftp, "TFTP %p is%s the master client\n", - tftp, ( ( tftp->flags & TFTP_FL_SEND_ACK ) ? "" : " not" ) ); - if ( *addr && *port ) { - socket.sin.sin_family = AF_INET; - if ( inet_aton ( addr, &socket.sin.sin_addr ) == 0 ) { - DBGC ( tftp, "TFTP %p multicast invalid IP address " - "%s\n", tftp, addr ); - return -EINVAL_MC_INVALID_IP; - } - DBGC ( tftp, "TFTP %p multicast IP address %s\n", - tftp, inet_ntoa ( socket.sin.sin_addr ) ); - socket.sin.sin_port = htons ( strtoul ( port, &port_end, 0 ) ); - if ( *port_end ) { - DBGC ( tftp, "TFTP %p multicast invalid port %s\n", - tftp, port ); - return -EINVAL_MC_INVALID_PORT; - } - DBGC ( tftp, "TFTP %p multicast port %d\n", - tftp, ntohs ( socket.sin.sin_port ) ); - if ( ( rc = tftp_reopen_mc ( tftp, &socket.sa ) ) != 0 ) - return rc; - } - - return 0; -} - -/** A TFTP option */ -struct tftp_option { - /** Option name */ - const char *name; - /** Option processor - * - * @v tftp TFTP connection - * @v value Option value - * @ret rc Return status code - */ - int ( * process ) ( struct tftp_request *tftp, const char *value ); -}; - -/** Recognised TFTP options */ -static struct tftp_option tftp_options[] = { - { "blksize", tftp_process_blksize }, - { "tsize", tftp_process_tsize }, - { "multicast", tftp_process_multicast }, - { NULL, NULL } -}; - -/** - * Process TFTP option - * - * @v tftp TFTP connection - * @v name Option name - * @v value Option value - * @ret rc Return status code - */ -static int tftp_process_option ( struct tftp_request *tftp, - const char *name, const char *value ) { - struct tftp_option *option; - - for ( option = tftp_options ; option->name ; option++ ) { - if ( strcasecmp ( name, option->name ) == 0 ) - return option->process ( tftp, value ); - } - - DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n", - tftp, name, value ); - - /* Unknown options should be silently ignored */ - return 0; -} - -/** - * Receive OACK - * - * @v tftp TFTP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer - * @ret rc Return status code - */ -static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { - struct tftp_oack *oack = buf; - char *end = buf + len; - char *name; - char *value; - char *next; - int rc = 0; - - /* Sanity check */ - if ( len < sizeof ( *oack ) ) { - DBGC ( tftp, "TFTP %p received underlength OACK packet " - "length %zd\n", tftp, len ); - rc = -EINVAL; - goto done; - } - - /* Process each option in turn */ - for ( name = oack->data ; name < end ; name = next ) { - - /* Parse option name and value - * - * We treat parsing errors as non-fatal, because there - * exists at least one TFTP server (IBM Tivoli PXE - * Server 5.1.0.3) that has been observed to send - * malformed OACKs containing trailing garbage bytes. - */ - value = ( name + strnlen ( name, ( end - name ) ) + 1 ); - if ( value > end ) { - DBGC ( tftp, "TFTP %p received OACK with malformed " - "option name:\n", tftp ); - DBGC_HD ( tftp, oack, len ); - break; - } - if ( value == end ) { - DBGC ( tftp, "TFTP %p received OACK missing value " - "for option \"%s\"\n", tftp, name ); - DBGC_HD ( tftp, oack, len ); - break; - } - next = ( value + strnlen ( value, ( end - value ) ) + 1 ); - if ( next > end ) { - DBGC ( tftp, "TFTP %p received OACK with malformed " - "value for option \"%s\":\n", tftp, name ); - DBGC_HD ( tftp, oack, len ); - break; - } - - /* Process option */ - if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 ) - goto done; - } - - /* Process tsize information, if available */ - if ( tftp->tsize ) { - if ( ( rc = tftp_presize ( tftp, tftp->tsize ) ) != 0 ) - goto done; - } - - /* Request next data block */ - tftp_send_packet ( tftp ); - - done: - if ( rc ) - tftp_done ( tftp, rc ); - return rc; -} - -/** - * Receive DATA - * - * @v tftp TFTP connection - * @v iobuf I/O buffer - * @ret rc Return status code - * - * Takes ownership of I/O buffer. - */ -static int tftp_rx_data ( struct tftp_request *tftp, - struct io_buffer *iobuf ) { - struct tftp_data *data = iobuf->data; - struct xfer_metadata meta; - unsigned int block; - off_t offset; - size_t data_len; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *data ) ) { - DBGC ( tftp, "TFTP %p received underlength DATA packet " - "length %zd\n", tftp, iob_len ( iobuf ) ); - rc = -EINVAL; - goto done; - } - - /* Calculate block number */ - block = ( ( bitmap_first_gap ( &tftp->bitmap ) + 1 ) & ~0xffff ); - if ( data->block == 0 && block == 0 ) { - DBGC ( tftp, "TFTP %p received data block 0\n", tftp ); - rc = -EINVAL; - goto done; - } - block += ( ntohs ( data->block ) - 1 ); - - /* Extract data */ - offset = ( block * tftp->blksize ); - iob_pull ( iobuf, sizeof ( *data ) ); - data_len = iob_len ( iobuf ); - if ( data_len > tftp->blksize ) { - DBGC ( tftp, "TFTP %p received overlength DATA packet " - "length %zd\n", tftp, data_len ); - rc = -EINVAL; - goto done; - } - - /* Deliver data */ - memset ( &meta, 0, sizeof ( meta ) ); - meta.flags = XFER_FL_ABS_OFFSET; - meta.offset = offset; - if ( ( rc = xfer_deliver ( &tftp->xfer, iob_disown ( iobuf ), - &meta ) ) != 0 ) { - DBGC ( tftp, "TFTP %p could not deliver data: %s\n", - tftp, strerror ( rc ) ); - goto done; - } - - /* Ensure block bitmap is ready */ - if ( ( rc = tftp_presize ( tftp, ( offset + data_len ) ) ) != 0 ) - goto done; - - /* Mark block as received */ - bitmap_set ( &tftp->bitmap, block ); - - /* Acknowledge block */ - tftp_send_packet ( tftp ); - - /* If all blocks have been received, finish. */ - if ( bitmap_full ( &tftp->bitmap ) ) - tftp_done ( tftp, 0 ); - - done: - free_iob ( iobuf ); - if ( rc ) - tftp_done ( tftp, rc ); - return rc; -} - -/** - * Convert TFTP error code to return status code - * - * @v errcode TFTP error code - * @ret rc Return status code - */ -static int tftp_errcode_to_rc ( unsigned int errcode ) { - switch ( errcode ) { - case TFTP_ERR_FILE_NOT_FOUND: return -ENOENT; - case TFTP_ERR_ACCESS_DENIED: return -EACCES; - case TFTP_ERR_ILLEGAL_OP: return -ENOTTY; - default: return -ENOTSUP; - } -} - -/** - * Receive ERROR - * - * @v tftp TFTP connection - * @v buf Temporary data buffer - * @v len Length of temporary data buffer - * @ret rc Return status code - */ -static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) { - struct tftp_error *error = buf; - int rc; - - /* Sanity check */ - if ( len < sizeof ( *error ) ) { - DBGC ( tftp, "TFTP %p received underlength ERROR packet " - "length %zd\n", tftp, len ); - return -EINVAL; - } - - DBGC ( tftp, "TFTP %p received ERROR packet with code %d, message " - "\"%s\"\n", tftp, ntohs ( error->errcode ), error->errmsg ); - - /* Determine final operation result */ - rc = tftp_errcode_to_rc ( ntohs ( error->errcode ) ); - - /* Close TFTP request */ - tftp_done ( tftp, rc ); - - return 0; -} - -/** - * Receive new data - * - * @v tftp TFTP connection - * @v iobuf I/O buffer - * @v meta Transfer metadata - * @ret rc Return status code - */ -static int tftp_rx ( struct tftp_request *tftp, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - struct sockaddr_tcpip *st_src; - struct tftp_common *common = iobuf->data; - size_t len = iob_len ( iobuf ); - int rc = -EINVAL; - - /* Sanity checks */ - if ( len < sizeof ( *common ) ) { - DBGC ( tftp, "TFTP %p received underlength packet length " - "%zd\n", tftp, len ); - goto done; - } - if ( ! meta->src ) { - DBGC ( tftp, "TFTP %p received packet without source port\n", - tftp ); - goto done; - } - - /* Filter by TID. Set TID on first response received */ - st_src = ( struct sockaddr_tcpip * ) meta->src; - if ( ! tftp->peer.st_family ) { - memcpy ( &tftp->peer, st_src, sizeof ( tftp->peer ) ); - DBGC ( tftp, "TFTP %p using remote port %d\n", tftp, - ntohs ( tftp->peer.st_port ) ); - } else if ( memcmp ( &tftp->peer, st_src, - sizeof ( tftp->peer ) ) != 0 ) { - DBGC ( tftp, "TFTP %p received packet from wrong source (got " - "%d, wanted %d)\n", tftp, ntohs ( st_src->st_port ), - ntohs ( tftp->peer.st_port ) ); - goto done; - } - - switch ( common->opcode ) { - case htons ( TFTP_OACK ): - rc = tftp_rx_oack ( tftp, iobuf->data, len ); - break; - case htons ( TFTP_DATA ): - rc = tftp_rx_data ( tftp, iob_disown ( iobuf ) ); - break; - case htons ( TFTP_ERROR ): - rc = tftp_rx_error ( tftp, iobuf->data, len ); - break; - default: - DBGC ( tftp, "TFTP %p received strange packet type %d\n", - tftp, ntohs ( common->opcode ) ); - break; - }; - - done: - free_iob ( iobuf ); - return rc; -} - -/** - * Receive new data via socket - * - * @v tftp TFTP connection - * @v iobuf I/O buffer - * @v meta Transfer metadata - * @ret rc Return status code - */ -static int tftp_socket_deliver ( struct tftp_request *tftp, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - - /* Enable sending ACKs when we receive a unicast packet. This - * covers three cases: - * - * 1. Standard TFTP; we should always send ACKs, and will - * always receive a unicast packet before we need to send the - * first ACK. - * - * 2. RFC2090 multicast TFTP; the only unicast packets we will - * receive are the OACKs; enable sending ACKs here (before - * processing the OACK) and disable it when processing the - * multicast option if we are not the master client. - * - * 3. MTFTP; receiving a unicast datagram indicates that we - * are the "master client" and should send ACKs. - */ - tftp->flags |= TFTP_FL_SEND_ACK; - - return tftp_rx ( tftp, iobuf, meta ); -} - -/** TFTP socket operations */ -static struct interface_operation tftp_socket_operations[] = { - INTF_OP ( xfer_deliver, struct tftp_request *, tftp_socket_deliver ), -}; - -/** TFTP socket interface descriptor */ -static struct interface_descriptor tftp_socket_desc = - INTF_DESC ( struct tftp_request, socket, tftp_socket_operations ); - -/** TFTP multicast socket operations */ -static struct interface_operation tftp_mc_socket_operations[] = { - INTF_OP ( xfer_deliver, struct tftp_request *, tftp_rx ), -}; - -/** TFTP multicast socket interface descriptor */ -static struct interface_descriptor tftp_mc_socket_desc = - INTF_DESC ( struct tftp_request, mc_socket, tftp_mc_socket_operations ); - -/** - * Check flow control window - * - * @v tftp TFTP connection - * @ret len Length of window - */ -static size_t tftp_xfer_window ( struct tftp_request *tftp ) { - - /* We abuse this data-xfer method to convey the blocksize to - * the caller. This really should be done using some kind of - * stat() method, but we don't yet have the facility to do - * that. - */ - return tftp->blksize; -} - -/** - * Terminate download - * - * @v tftp TFTP connection - * @v rc Reason for close - */ -static void tftp_close ( struct tftp_request *tftp, int rc ) { - - /* Abort download */ - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - - /* Close TFTP request */ - tftp_done ( tftp, rc ); -} - -/** TFTP data transfer interface operations */ -static struct interface_operation tftp_xfer_operations[] = { - INTF_OP ( xfer_window, struct tftp_request *, tftp_xfer_window ), - INTF_OP ( intf_close, struct tftp_request *, tftp_close ), -}; - -/** TFTP data transfer interface descriptor */ -static struct interface_descriptor tftp_xfer_desc = - INTF_DESC ( struct tftp_request, xfer, tftp_xfer_operations ); - -/** - * Initiate TFTP/TFTM/MTFTP download - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int tftp_core_open ( struct interface *xfer, struct uri *uri, - unsigned int default_port, - struct sockaddr *multicast, - unsigned int flags ) { - struct tftp_request *tftp; - int rc; - - /* Sanity checks */ - if ( ! uri->host ) - return -EINVAL; - if ( ! uri->path ) - return -EINVAL; - - /* Allocate and populate TFTP structure */ - tftp = zalloc ( sizeof ( *tftp ) ); - if ( ! tftp ) - return -ENOMEM; - ref_init ( &tftp->refcnt, tftp_free ); - intf_init ( &tftp->xfer, &tftp_xfer_desc, &tftp->refcnt ); - intf_init ( &tftp->socket, &tftp_socket_desc, &tftp->refcnt ); - intf_init ( &tftp->mc_socket, &tftp_mc_socket_desc, &tftp->refcnt ); - timer_init ( &tftp->timer, tftp_timer_expired, &tftp->refcnt ); - tftp->uri = uri_get ( uri ); - tftp->blksize = TFTP_DEFAULT_BLKSIZE; - tftp->flags = flags; - - /* Open socket */ - tftp->port = uri_port ( tftp->uri, default_port ); - if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) - goto err; - - /* Open multicast socket */ - if ( multicast ) { - if ( ( rc = tftp_reopen_mc ( tftp, multicast ) ) != 0 ) - goto err; - } - - /* Start timer to initiate RRQ */ - start_timer_nodelay ( &tftp->timer ); - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &tftp->xfer, xfer ); - ref_put ( &tftp->refcnt ); - return 0; - - err: - DBGC ( tftp, "TFTP %p could not create request: %s\n", - tftp, strerror ( rc ) ); - tftp_done ( tftp, rc ); - ref_put ( &tftp->refcnt ); - return rc; -} - -/** - * Initiate TFTP download - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int tftp_open ( struct interface *xfer, struct uri *uri ) { - return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, - TFTP_FL_RRQ_SIZES ); - -} - -/** TFTP URI opener */ -struct uri_opener tftp_uri_opener __uri_opener = { - .scheme = "tftp", - .open = tftp_open, -}; - -/** - * Initiate TFTM download - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int tftm_open ( struct interface *xfer, struct uri *uri ) { - return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, - ( TFTP_FL_RRQ_SIZES | - TFTP_FL_RRQ_MULTICAST ) ); - -} - -/** TFTM URI opener */ -struct uri_opener tftm_uri_opener __uri_opener = { - .scheme = "tftm", - .open = tftm_open, -}; - -/** - * Initiate MTFTP download - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int mtftp_open ( struct interface *xfer, struct uri *uri ) { - return tftp_core_open ( xfer, uri, MTFTP_PORT, - ( struct sockaddr * ) &tftp_mtftp_socket, - TFTP_FL_MTFTP_RECOVERY ); -} - -/** MTFTP URI opener */ -struct uri_opener mtftp_uri_opener __uri_opener = { - .scheme = "mtftp", - .open = mtftp_open, -}; - -/****************************************************************************** - * - * Settings - * - ****************************************************************************** - */ - -/** - * Apply TFTP configuration settings - * - * @ret rc Return status code - */ -static int tftp_apply_settings ( void ) { - static struct in_addr tftp_server = { 0 }; - struct in_addr last_tftp_server; - char uri_string[32]; - struct uri *uri; - - /* Retrieve TFTP server setting */ - last_tftp_server = tftp_server; - fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server ); - - /* If TFTP server setting has changed, set the current working - * URI to match. Do it only when the TFTP server has changed - * to try to minimise surprises to the user, who probably - * won't expect the CWURI to change just because they updated - * an unrelated setting and triggered all the settings - * applicators. - */ - if ( tftp_server.s_addr != last_tftp_server.s_addr ) { - if ( tftp_server.s_addr ) { - snprintf ( uri_string, sizeof ( uri_string ), - "tftp://%s/", inet_ntoa ( tftp_server ) ); - uri = parse_uri ( uri_string ); - if ( ! uri ) - return -ENOMEM; - } else { - uri = NULL; - } - churi ( uri ); - uri_put ( uri ); - } - - return 0; -} - -/** TFTP settings applicator */ -struct settings_applicator tftp_settings_applicator __settings_applicator = { - .apply = tftp_apply_settings, -}; diff --git a/qemu/roms/ipxe/src/net/validator.c b/qemu/roms/ipxe/src/net/validator.c deleted file mode 100644 index db968398a..000000000 --- a/qemu/roms/ipxe/src/net/validator.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <ipxe/refcnt.h> -#include <ipxe/malloc.h> -#include <ipxe/interface.h> -#include <ipxe/xfer.h> -#include <ipxe/open.h> -#include <ipxe/iobuf.h> -#include <ipxe/xferbuf.h> -#include <ipxe/process.h> -#include <ipxe/x509.h> -#include <ipxe/settings.h> -#include <ipxe/dhcp.h> -#include <ipxe/base64.h> -#include <ipxe/crc32.h> -#include <ipxe/ocsp.h> -#include <ipxe/validator.h> - -/** @file - * - * Certificate validator - * - */ - -/** A certificate validator */ -struct validator { - /** Reference count */ - struct refcnt refcnt; - /** Job control interface */ - struct interface job; - /** Data transfer interface */ - struct interface xfer; - - /** Process */ - struct process process; - - /** X.509 certificate chain */ - struct x509_chain *chain; - /** OCSP check */ - struct ocsp_check *ocsp; - /** Data buffer */ - struct xfer_buffer buffer; - /** Action to take upon completed transfer */ - int ( * done ) ( struct validator *validator, const void *data, - size_t len ); -}; - -/** - * Free certificate validator - * - * @v refcnt Reference count - */ -static void validator_free ( struct refcnt *refcnt ) { - struct validator *validator = - container_of ( refcnt, struct validator, refcnt ); - - DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); - x509_chain_put ( validator->chain ); - ocsp_put ( validator->ocsp ); - xferbuf_free ( &validator->buffer ); - free ( validator ); -} - -/** - * Mark certificate validation as finished - * - * @v validator Certificate validator - * @v rc Reason for finishing - */ -static void validator_finished ( struct validator *validator, int rc ) { - - /* Remove process */ - process_del ( &validator->process ); - - /* Close all interfaces */ - intf_shutdown ( &validator->xfer, rc ); - intf_shutdown ( &validator->job, rc ); -} - -/**************************************************************************** - * - * Job control interface - * - */ - -/** Certificate validator job control interface operations */ -static struct interface_operation validator_job_operations[] = { - INTF_OP ( intf_close, struct validator *, validator_finished ), -}; - -/** Certificate validator job control interface descriptor */ -static struct interface_descriptor validator_job_desc = - INTF_DESC ( struct validator, job, validator_job_operations ); - -/**************************************************************************** - * - * Cross-signing certificates - * - */ - -/** Cross-signed certificate source setting */ -const struct setting crosscert_setting __setting ( SETTING_CRYPTO, crosscert )={ - .name = "crosscert", - .description = "Cross-signed certificate source", - .tag = DHCP_EB_CROSS_CERT, - .type = &setting_type_string, -}; - -/** Default cross-signed certificate source */ -static const char crosscert_default[] = "http://ca.ipxe.org/auto"; - -/** - * Append cross-signing certificates to certificate chain - * - * @v validator Certificate validator - * @v data Raw cross-signing certificate data - * @v len Length of raw data - * @ret rc Return status code - */ -static int validator_append ( struct validator *validator, - const void *data, size_t len ) { - struct asn1_cursor cursor; - struct x509_chain *certs; - struct x509_certificate *cert; - struct x509_certificate *last; - int rc; - - /* Allocate certificate list */ - certs = x509_alloc_chain(); - if ( ! certs ) { - rc = -ENOMEM; - goto err_alloc_certs; - } - - /* Initialise cursor */ - cursor.data = data; - cursor.len = len; - - /* Enter certificateSet */ - if ( ( rc = asn1_enter ( &cursor, ASN1_SET ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not enter " - "certificateSet: %s\n", validator, strerror ( rc ) ); - goto err_certificateset; - } - - /* Add each certificate to list */ - while ( cursor.len ) { - - /* Add certificate to chain */ - if ( ( rc = x509_append_raw ( certs, cursor.data, - cursor.len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificate: %s\n", - validator, strerror ( rc) ); - DBGC_HDA ( validator, 0, cursor.data, cursor.len ); - return rc; - } - cert = x509_last ( certs ); - DBGC ( validator, "VALIDATOR %p found certificate %s\n", - validator, x509_name ( cert ) ); - - /* Move to next certificate */ - asn1_skip_any ( &cursor ); - } - - /* Append certificates to chain */ - last = x509_last ( validator->chain ); - if ( ( rc = x509_auto_append ( validator->chain, certs ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not append " - "certificates: %s\n", validator, strerror ( rc ) ); - goto err_auto_append; - } - - /* Check that at least one certificate has been added */ - if ( last == x509_last ( validator->chain ) ) { - DBGC ( validator, "VALIDATOR %p failed to append any " - "applicable certificates\n", validator ); - rc = -EACCES; - goto err_no_progress; - } - - /* Drop reference to certificate list */ - x509_chain_put ( certs ); - - return 0; - - err_no_progress: - err_auto_append: - err_certificateset: - x509_chain_put ( certs ); - err_alloc_certs: - return rc; -} - -/** - * Start download of cross-signing certificate - * - * @v validator Certificate validator - * @v issuer Required issuer - * @ret rc Return status code - */ -static int validator_start_download ( struct validator *validator, - const struct asn1_cursor *issuer ) { - const char *crosscert; - char *crosscert_copy; - char *uri_string; - size_t uri_string_len; - uint32_t crc; - int len; - int rc; - - /* Determine cross-signed certificate source */ - fetch_string_setting_copy ( NULL, &crosscert_setting, &crosscert_copy ); - crosscert = ( crosscert_copy ? crosscert_copy : crosscert_default ); - - /* Allocate URI string */ - uri_string_len = ( strlen ( crosscert ) + 22 /* "/%08x.der?subject=" */ - + base64_encoded_len ( issuer->len ) + 1 /* NUL */ ); - uri_string = zalloc ( uri_string_len ); - if ( ! uri_string ) { - rc = -ENOMEM; - goto err_alloc_uri_string; - } - - /* Generate CRC32 */ - crc = crc32_le ( 0xffffffffUL, issuer->data, issuer->len ); - - /* Generate URI string */ - len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?subject=", - crosscert, crc ); - base64_encode ( issuer->data, issuer->len, ( uri_string + len ), - ( uri_string_len - len ) ); - DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate " - "from %s\n", validator, uri_string ); - - /* Set completion handler */ - validator->done = validator_append; - - /* Open URI */ - if ( ( rc = xfer_open_uri_string ( &validator->xfer, - uri_string ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", - validator, uri_string, strerror ( rc ) ); - goto err_open_uri_string; - } - - /* Success */ - rc = 0; - - err_open_uri_string: - free ( uri_string ); - err_alloc_uri_string: - free ( crosscert_copy ); - return rc; -} - -/**************************************************************************** - * - * OCSP checks - * - */ - -/** - * Validate OCSP response - * - * @v validator Certificate validator - * @v data Raw OCSP response - * @v len Length of raw data - * @ret rc Return status code - */ -static int validator_ocsp_validate ( struct validator *validator, - const void *data, size_t len ) { - time_t now; - int rc; - - /* Record OCSP response */ - if ( ( rc = ocsp_response ( validator->ocsp, data, len ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not record OCSP " - "response: %s\n", validator, strerror ( rc ) ); - return rc; - } - - /* Validate OCSP response */ - now = time ( NULL ); - if ( ( rc = ocsp_validate ( validator->ocsp, now ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not validate OCSP " - "response: %s\n", validator, strerror ( rc ) ); - return rc; - } - - /* Drop reference to OCSP check */ - ocsp_put ( validator->ocsp ); - validator->ocsp = NULL; - - return 0; -} - -/** - * Start OCSP check - * - * @v validator Certificate validator - * @v cert Certificate to check - * @v issuer Issuing certificate - * @ret rc Return status code - */ -static int validator_start_ocsp ( struct validator *validator, - struct x509_certificate *cert, - struct x509_certificate *issuer ) { - const char *uri_string; - int rc; - - /* Create OCSP check */ - assert ( validator->ocsp == NULL ); - if ( ( rc = ocsp_check ( cert, issuer, &validator->ocsp ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not create OCSP check: " - "%s\n", validator, strerror ( rc ) ); - return rc; - } - - /* Set completion handler */ - validator->done = validator_ocsp_validate; - - /* Open URI */ - uri_string = validator->ocsp->uri_string; - DBGC ( validator, "VALIDATOR %p performing OCSP check at %s\n", - validator, uri_string ); - if ( ( rc = xfer_open_uri_string ( &validator->xfer, - uri_string ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not open %s: %s\n", - validator, uri_string, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/**************************************************************************** - * - * Data transfer interface - * - */ - -/** - * Close data transfer interface - * - * @v validator Certificate validator - * @v rc Reason for close - */ -static void validator_xfer_close ( struct validator *validator, int rc ) { - - /* Close data transfer interface */ - intf_restart ( &validator->xfer, rc ); - - /* Check for errors */ - if ( rc != 0 ) { - DBGC ( validator, "VALIDATOR %p transfer failed: %s\n", - validator, strerror ( rc ) ); - goto err_transfer; - } - DBGC2 ( validator, "VALIDATOR %p transfer complete\n", validator ); - - /* Process completed download */ - assert ( validator->done != NULL ); - if ( ( rc = validator->done ( validator, validator->buffer.data, - validator->buffer.len ) ) != 0 ) - goto err_append; - - /* Free downloaded data */ - xferbuf_free ( &validator->buffer ); - - /* Resume validation process */ - process_add ( &validator->process ); - - return; - - err_append: - err_transfer: - validator_finished ( validator, rc ); -} - -/** - * Receive data - * - * @v validator Certificate validator - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int validator_xfer_deliver ( struct validator *validator, - struct io_buffer *iobuf, - struct xfer_metadata *meta ) { - int rc; - - /* Add data to buffer */ - if ( ( rc = xferbuf_deliver ( &validator->buffer, iob_disown ( iobuf ), - meta ) ) != 0 ) { - DBGC ( validator, "VALIDATOR %p could not receive data: %s\n", - validator, strerror ( rc ) ); - validator_finished ( validator, rc ); - return rc; - } - - return 0; -} - -/** Certificate validator data transfer interface operations */ -static struct interface_operation validator_xfer_operations[] = { - INTF_OP ( xfer_deliver, struct validator *, validator_xfer_deliver ), - INTF_OP ( intf_close, struct validator *, validator_xfer_close ), -}; - -/** Certificate validator data transfer interface descriptor */ -static struct interface_descriptor validator_xfer_desc = - INTF_DESC ( struct validator, xfer, validator_xfer_operations ); - -/**************************************************************************** - * - * Validation process - * - */ - -/** - * Certificate validation process - * - * @v validator Certificate validator - */ -static void validator_step ( struct validator *validator ) { - struct x509_link *link; - struct x509_certificate *cert; - struct x509_certificate *issuer = NULL; - struct x509_certificate *last; - time_t now; - int rc; - - /* Try validating chain. Try even if the chain is incomplete, - * since certificates may already have been validated - * previously. - */ - now = time ( NULL ); - if ( ( rc = x509_validate_chain ( validator->chain, now, NULL, - NULL ) ) == 0 ) { - validator_finished ( validator, 0 ); - return; - } - - /* If there is a certificate that could be validated using - * OCSP, try it. - */ - list_for_each_entry ( link, &validator->chain->links, list ) { - cert = issuer; - issuer = link->cert; - if ( ! cert ) - continue; - if ( ! issuer->valid ) - continue; - /* The issuer is valid, but this certificate is not - * yet valid. If OCSP is applicable, start it. - */ - if ( cert->extensions.auth_info.ocsp.uri.len && - ( ! cert->extensions.auth_info.ocsp.good ) ) { - /* Start OCSP */ - if ( ( rc = validator_start_ocsp ( validator, cert, - issuer ) ) != 0 ) { - validator_finished ( validator, rc ); - return; - } - return; - } - /* Otherwise, this is a permanent failure */ - validator_finished ( validator, rc ); - return; - } - - /* If chain ends with a self-issued certificate, then there is - * nothing more to do. - */ - last = x509_last ( validator->chain ); - if ( asn1_compare ( &last->issuer.raw, &last->subject.raw ) == 0 ) { - validator_finished ( validator, rc ); - return; - } - - /* Otherwise, try to download a suitable cross-signing - * certificate. - */ - if ( ( rc = validator_start_download ( validator, - &last->issuer.raw ) ) != 0 ) { - validator_finished ( validator, rc ); - return; - } -} - -/** Certificate validator process descriptor */ -static struct process_descriptor validator_process_desc = - PROC_DESC_ONCE ( struct validator, process, validator_step ); - -/**************************************************************************** - * - * Instantiator - * - */ - -/** - * Instantiate a certificate validator - * - * @v job Job control interface - * @v chain X.509 certificate chain - * @ret rc Return status code - */ -int create_validator ( struct interface *job, struct x509_chain *chain ) { - struct validator *validator; - int rc; - - /* Sanity check */ - if ( ! chain ) { - rc = -EINVAL; - goto err_sanity; - } - - /* Allocate and initialise structure */ - validator = zalloc ( sizeof ( *validator ) ); - if ( ! validator ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &validator->refcnt, validator_free ); - intf_init ( &validator->job, &validator_job_desc, - &validator->refcnt ); - intf_init ( &validator->xfer, &validator_xfer_desc, - &validator->refcnt ); - process_init ( &validator->process, &validator_process_desc, - &validator->refcnt ); - validator->chain = x509_chain_get ( chain ); - xferbuf_malloc_init ( &validator->buffer ); - - /* Attach parent interface, mortalise self, and return */ - intf_plug_plug ( &validator->job, job ); - ref_put ( &validator->refcnt ); - DBGC2 ( validator, "VALIDATOR %p validating X509 chain %p\n", - validator, validator->chain ); - return 0; - - validator_finished ( validator, rc ); - ref_put ( &validator->refcnt ); - err_alloc: - err_sanity: - return rc; -} diff --git a/qemu/roms/ipxe/src/net/vlan.c b/qemu/roms/ipxe/src/net/vlan.c deleted file mode 100644 index f515c2dc9..000000000 --- a/qemu/roms/ipxe/src/net/vlan.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdint.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/features.h> -#include <ipxe/if_ether.h> -#include <ipxe/ethernet.h> -#include <ipxe/netdevice.h> -#include <ipxe/iobuf.h> -#include <ipxe/vlan.h> - -/** @file - * - * Virtual LANs - * - */ - -FEATURE ( FEATURE_PROTOCOL, "VLAN", DHCP_EB_FEATURE_VLAN, 1 ); - -struct net_protocol vlan_protocol __net_protocol; - -/** VLAN device private data */ -struct vlan_device { - /** Trunk network device */ - struct net_device *trunk; - /** VLAN tag */ - unsigned int tag; - /** Default priority */ - unsigned int priority; -}; - -/** - * Open VLAN device - * - * @v netdev Network device - * @ret rc Return status code - */ -static int vlan_open ( struct net_device *netdev ) { - struct vlan_device *vlan = netdev->priv; - - return netdev_open ( vlan->trunk ); -} - -/** - * Close VLAN device - * - * @v netdev Network device - */ -static void vlan_close ( struct net_device *netdev ) { - struct vlan_device *vlan = netdev->priv; - - netdev_close ( vlan->trunk ); -} - -/** - * Transmit packet on VLAN device - * - * @v netdev Network device - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int vlan_transmit ( struct net_device *netdev, - struct io_buffer *iobuf ) { - struct vlan_device *vlan = netdev->priv; - struct net_device *trunk = vlan->trunk; - struct ll_protocol *ll_protocol; - struct vlan_header *vlanhdr; - uint8_t ll_dest_copy[ETH_ALEN]; - uint8_t ll_source_copy[ETH_ALEN]; - const void *ll_dest; - const void *ll_source; - uint16_t net_proto; - unsigned int flags; - int rc; - - /* Strip link-layer header and preserve link-layer header fields */ - ll_protocol = netdev->ll_protocol; - if ( ( rc = ll_protocol->pull ( netdev, iobuf, &ll_dest, &ll_source, - &net_proto, &flags ) ) != 0 ) { - DBGC ( netdev, "VLAN %s could not parse link-layer header: " - "%s\n", netdev->name, strerror ( rc ) ); - return rc; - } - memcpy ( ll_dest_copy, ll_dest, ETH_ALEN ); - memcpy ( ll_source_copy, ll_source, ETH_ALEN ); - - /* Construct VLAN header */ - vlanhdr = iob_push ( iobuf, sizeof ( *vlanhdr ) ); - vlanhdr->tci = htons ( VLAN_TCI ( vlan->tag, vlan->priority ) ); - vlanhdr->net_proto = net_proto; - - /* Reclaim I/O buffer from VLAN device's TX queue */ - list_del ( &iobuf->list ); - - /* Transmit packet on trunk device */ - if ( ( rc = net_tx ( iob_disown ( iobuf ), trunk, &vlan_protocol, - ll_dest_copy, ll_source_copy ) ) != 0 ) { - DBGC ( netdev, "VLAN %s could not transmit: %s\n", - netdev->name, strerror ( rc ) ); - /* Cannot return an error status, since that would - * cause the I/O buffer to be double-freed. - */ - return 0; - } - - return 0; -} - -/** - * Poll VLAN device - * - * @v netdev Network device - */ -static void vlan_poll ( struct net_device *netdev ) { - struct vlan_device *vlan = netdev->priv; - - /* Poll trunk device */ - netdev_poll ( vlan->trunk ); -} - -/** - * Enable/disable interrupts on VLAN device - * - * @v netdev Network device - * @v enable Interrupts should be enabled - */ -static void vlan_irq ( struct net_device *netdev, int enable ) { - struct vlan_device *vlan = netdev->priv; - - /* Enable/disable interrupts on trunk device. This is not at - * all robust, but there is no sensible course of action - * available. - */ - netdev_irq ( vlan->trunk, enable ); -} - -/** VLAN device operations */ -static struct net_device_operations vlan_operations = { - .open = vlan_open, - .close = vlan_close, - .transmit = vlan_transmit, - .poll = vlan_poll, - .irq = vlan_irq, -}; - -/** - * Synchronise VLAN device - * - * @v netdev Network device - */ -static void vlan_sync ( struct net_device *netdev ) { - struct vlan_device *vlan = netdev->priv; - struct net_device *trunk = vlan->trunk; - - /* Synchronise link status */ - if ( netdev->link_rc != trunk->link_rc ) - netdev_link_err ( netdev, trunk->link_rc ); - - /* Synchronise open/closed status */ - if ( netdev_is_open ( trunk ) ) { - if ( ! netdev_is_open ( netdev ) ) - netdev_open ( netdev ); - } else { - if ( netdev_is_open ( netdev ) ) - netdev_close ( netdev ); - } -} - -/** - * Identify VLAN device - * - * @v trunk Trunk network device - * @v tag VLAN tag - * @ret netdev VLAN device, if any - */ -struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) { - struct net_device *netdev; - struct vlan_device *vlan; - - for_each_netdev ( netdev ) { - if ( netdev->op != &vlan_operations ) - continue; - vlan = netdev->priv; - if ( ( vlan->trunk == trunk ) && ( vlan->tag == tag ) ) - return netdev; - } - return NULL; -} - -/** - * Process incoming VLAN packet - * - * @v iobuf I/O buffer - * @v trunk Trunk network device - * @v ll_dest Link-layer destination address - * @v ll_source Link-layer source address - * @v flags Packet flags - * @ret rc Return status code - */ -static int vlan_rx ( struct io_buffer *iobuf, struct net_device *trunk, - const void *ll_dest, const void *ll_source, - unsigned int flags __unused ) { - struct vlan_header *vlanhdr = iobuf->data; - struct net_device *netdev; - struct ll_protocol *ll_protocol; - uint8_t ll_dest_copy[ETH_ALEN]; - uint8_t ll_source_copy[ETH_ALEN]; - uint16_t tag; - int rc; - - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *vlanhdr ) ) { - DBGC ( trunk, "VLAN %s received underlength packet (%zd " - "bytes)\n", trunk->name, iob_len ( iobuf ) ); - rc = -EINVAL; - goto err_sanity; - } - - /* Identify VLAN device */ - tag = VLAN_TAG ( ntohs ( vlanhdr->tci ) ); - netdev = vlan_find ( trunk, tag ); - if ( ! netdev ) { - DBGC2 ( trunk, "VLAN %s received packet for unknown VLAN " - "%d\n", trunk->name, tag ); - rc = -EPIPE; - goto err_no_vlan; - } - - /* Strip VLAN header and preserve original link-layer header fields */ - iob_pull ( iobuf, sizeof ( *vlanhdr ) ); - ll_protocol = trunk->ll_protocol; - memcpy ( ll_dest_copy, ll_dest, ETH_ALEN ); - memcpy ( ll_source_copy, ll_source, ETH_ALEN ); - - /* Reconstruct link-layer header for VLAN device */ - ll_protocol = netdev->ll_protocol; - if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest_copy, - ll_source_copy, - vlanhdr->net_proto ) ) != 0 ) { - DBGC ( netdev, "VLAN %s could not reconstruct link-layer " - "header: %s\n", netdev->name, strerror ( rc ) ); - goto err_ll_push; - } - - /* Enqueue packet on VLAN device */ - netdev_rx ( netdev, iob_disown ( iobuf ) ); - return 0; - - err_ll_push: - err_no_vlan: - err_sanity: - free_iob ( iobuf ); - return rc; -} - -/** VLAN protocol */ -struct net_protocol vlan_protocol __net_protocol = { - .name = "VLAN", - .net_proto = htons ( ETH_P_8021Q ), - .rx = vlan_rx, -}; - -/** - * Get the VLAN tag - * - * @v netdev Network device - * @ret tag VLAN tag, or 0 if device is not a VLAN device - */ -unsigned int vlan_tag ( struct net_device *netdev ) { - struct vlan_device *vlan; - - if ( netdev->op == &vlan_operations ) { - vlan = netdev->priv; - return vlan->tag; - } else { - return 0; - } -} - -/** - * Check if network device can be used as a VLAN trunk device - * - * @v trunk Trunk network device - * @ret is_ok Trunk network device is usable - * - * VLAN devices will be created as Ethernet devices. (We cannot - * simply clone the link layer of the trunk network device, because - * this link layer may expect the network device structure to contain - * some link-layer-private data.) The trunk network device must - * therefore have a link layer that is in some sense 'compatible' with - * Ethernet; specifically, it must have link-layer addresses that are - * the same length as Ethernet link-layer addresses. - * - * As an additional check, and primarily to assist with the sanity of - * the FCoE code, we refuse to allow nested VLANs. - */ -int vlan_can_be_trunk ( struct net_device *trunk ) { - - return ( ( trunk->ll_protocol->ll_addr_len == ETH_ALEN ) && - ( trunk->op != &vlan_operations ) ); -} - -/** - * Create VLAN device - * - * @v trunk Trunk network device - * @v tag VLAN tag - * @v priority Default VLAN priority - * @ret rc Return status code - */ -int vlan_create ( struct net_device *trunk, unsigned int tag, - unsigned int priority ) { - struct net_device *netdev; - struct vlan_device *vlan; - int rc; - - /* If VLAN already exists, just update the priority */ - if ( ( netdev = vlan_find ( trunk, tag ) ) != NULL ) { - vlan = netdev->priv; - if ( priority != vlan->priority ) { - DBGC ( netdev, "VLAN %s priority changed from %d to " - "%d\n", netdev->name, vlan->priority, priority ); - } - vlan->priority = priority; - return 0; - } - - /* Sanity checks */ - if ( ! vlan_can_be_trunk ( trunk ) ) { - DBGC ( trunk, "VLAN %s cannot create VLAN on non-trunk " - "device\n", trunk->name ); - rc = -ENOTTY; - goto err_sanity; - } - if ( ! VLAN_TAG_IS_VALID ( tag ) ) { - DBGC ( trunk, "VLAN %s cannot create VLAN with invalid tag " - "%d\n", trunk->name, tag ); - rc = -EINVAL; - goto err_sanity; - } - if ( ! VLAN_PRIORITY_IS_VALID ( priority ) ) { - DBGC ( trunk, "VLAN %s cannot create VLAN with invalid " - "priority %d\n", trunk->name, priority ); - rc = -EINVAL; - goto err_sanity; - } - - /* Allocate and initialise structure */ - netdev = alloc_etherdev ( sizeof ( *vlan ) ); - if ( ! netdev ) { - rc = -ENOMEM; - goto err_alloc_etherdev; - } - netdev_init ( netdev, &vlan_operations ); - netdev->dev = trunk->dev; - memcpy ( netdev->hw_addr, trunk->ll_addr, ETH_ALEN ); - vlan = netdev->priv; - vlan->trunk = netdev_get ( trunk ); - vlan->tag = tag; - vlan->priority = priority; - - /* Construct VLAN device name */ - snprintf ( netdev->name, sizeof ( netdev->name ), "%s-%d", - trunk->name, vlan->tag ); - - /* Mark device as not supporting interrupts, if applicable */ - if ( ! netdev_irq_supported ( trunk ) ) - netdev->state |= NETDEV_IRQ_UNSUPPORTED; - - /* Register VLAN device */ - if ( ( rc = register_netdev ( netdev ) ) != 0 ) { - DBGC ( netdev, "VLAN %s could not register: %s\n", - netdev->name, strerror ( rc ) ); - goto err_register; - } - - /* Synchronise with trunk device */ - vlan_sync ( netdev ); - - DBGC ( netdev, "VLAN %s created with tag %d and priority %d\n", - netdev->name, vlan->tag, vlan->priority ); - - return 0; - - unregister_netdev ( netdev ); - err_register: - netdev_nullify ( netdev ); - netdev_put ( netdev ); - netdev_put ( trunk ); - err_alloc_etherdev: - err_sanity: - return rc; -} - -/** - * Destroy VLAN device - * - * @v netdev Network device - * @ret rc Return status code - */ -int vlan_destroy ( struct net_device *netdev ) { - struct vlan_device *vlan = netdev->priv; - struct net_device *trunk; - - /* Sanity check */ - if ( netdev->op != &vlan_operations ) { - DBGC ( netdev, "VLAN %s cannot destroy non-VLAN device\n", - netdev->name ); - return -ENOTTY; - } - - DBGC ( netdev, "VLAN %s destroyed\n", netdev->name ); - - /* Remove VLAN device */ - unregister_netdev ( netdev ); - trunk = vlan->trunk; - netdev_nullify ( netdev ); - netdev_put ( netdev ); - netdev_put ( trunk ); - - return 0; -} - -/** - * Handle trunk network device link state change - * - * @v trunk Trunk network device - */ -static void vlan_notify ( struct net_device *trunk ) { - struct net_device *netdev; - struct vlan_device *vlan; - - for_each_netdev ( netdev ) { - if ( netdev->op != &vlan_operations ) - continue; - vlan = netdev->priv; - if ( vlan->trunk == trunk ) - vlan_sync ( netdev ); - } -} - -/** - * Destroy first VLAN device for a given trunk - * - * @v trunk Trunk network device - * @ret found A VLAN device was found - */ -static int vlan_remove_first ( struct net_device *trunk ) { - struct net_device *netdev; - struct vlan_device *vlan; - - for_each_netdev ( netdev ) { - if ( netdev->op != &vlan_operations ) - continue; - vlan = netdev->priv; - if ( vlan->trunk == trunk ) { - vlan_destroy ( netdev ); - return 1; - } - } - return 0; -} - -/** - * Destroy all VLAN devices for a given trunk - * - * @v trunk Trunk network device - */ -static void vlan_remove ( struct net_device *trunk ) { - - /* Remove all VLAN devices attached to this trunk, safe - * against arbitrary net device removal. - */ - while ( vlan_remove_first ( trunk ) ) {} -} - -/** VLAN driver */ -struct net_driver vlan_driver __net_driver = { - .name = "VLAN", - .notify = vlan_notify, - .remove = vlan_remove, -}; |