summaryrefslogtreecommitdiff
path: root/gpxe/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'gpxe/src/net')
-rw-r--r--gpxe/src/net/80211/net80211.c2829
-rw-r--r--gpxe/src/net/80211/rc80211.c371
-rw-r--r--gpxe/src/net/80211/sec80211.c503
-rw-r--r--gpxe/src/net/80211/wep.c303
-rw-r--r--gpxe/src/net/80211/wpa.c973
-rw-r--r--gpxe/src/net/80211/wpa_ccmp.c528
-rw-r--r--gpxe/src/net/80211/wpa_psk.c125
-rw-r--r--gpxe/src/net/80211/wpa_tkip.c586
-rw-r--r--gpxe/src/net/aoe.c471
-rw-r--r--gpxe/src/net/arp.c289
-rw-r--r--gpxe/src/net/cachedhcp.c76
-rw-r--r--gpxe/src/net/dhcpopts.c447
-rw-r--r--gpxe/src/net/dhcppkt.c283
-rw-r--r--gpxe/src/net/eapol.c85
-rw-r--r--gpxe/src/net/ethernet.c193
-rw-r--r--gpxe/src/net/fakedhcp.c217
-rw-r--r--gpxe/src/net/icmp.c103
-rw-r--r--gpxe/src/net/icmpv6.c128
-rw-r--r--gpxe/src/net/infiniband.c951
-rw-r--r--gpxe/src/net/infiniband/ib_cm.c413
-rw-r--r--gpxe/src/net/infiniband/ib_cmrc.c436
-rw-r--r--gpxe/src/net/infiniband/ib_mcast.c218
-rw-r--r--gpxe/src/net/infiniband/ib_mi.c406
-rw-r--r--gpxe/src/net/infiniband/ib_packet.c244
-rw-r--r--gpxe/src/net/infiniband/ib_pathrec.c296
-rw-r--r--gpxe/src/net/infiniband/ib_sma.c369
-rw-r--r--gpxe/src/net/infiniband/ib_smc.c179
-rw-r--r--gpxe/src/net/infiniband/ib_srp.c406
-rw-r--r--gpxe/src/net/iobpad.c68
-rw-r--r--gpxe/src/net/ipv4.c635
-rw-r--r--gpxe/src/net/ipv6.c381
-rw-r--r--gpxe/src/net/mii.c147
-rw-r--r--gpxe/src/net/ndp.c180
-rw-r--r--gpxe/src/net/netdev_settings.c120
-rw-r--r--gpxe/src/net/netdevice.c633
-rw-r--r--gpxe/src/net/nullnet.c60
-rw-r--r--gpxe/src/net/rarp.c70
-rw-r--r--gpxe/src/net/retry.c192
-rw-r--r--gpxe/src/net/tcp.c1156
-rw-r--r--gpxe/src/net/tcp/ftp.c529
-rw-r--r--gpxe/src/net/tcp/http.c597
-rw-r--r--gpxe/src/net/tcp/https.c51
-rw-r--r--gpxe/src/net/tcp/iscsi.c1934
-rw-r--r--gpxe/src/net/tcpip.c135
-rw-r--r--gpxe/src/net/tls.c1759
-rw-r--r--gpxe/src/net/udp.c463
-rw-r--r--gpxe/src/net/udp/dhcp.c1587
-rw-r--r--gpxe/src/net/udp/dns.c603
-rw-r--r--gpxe/src/net/udp/slam.c812
-rw-r--r--gpxe/src/net/udp/tftp.c1288
50 files changed, 0 insertions, 25828 deletions
diff --git a/gpxe/src/net/80211/net80211.c b/gpxe/src/net/80211/net80211.c
deleted file mode 100644
index 1c54597f..00000000
--- a/gpxe/src/net/80211/net80211.c
+++ /dev/null
@@ -1,2829 +0,0 @@
-/*
- * The gPXE 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <string.h>
-#include <byteswap.h>
-#include <stdlib.h>
-#include <gpxe/settings.h>
-#include <gpxe/if_arp.h>
-#include <gpxe/ethernet.h>
-#include <gpxe/ieee80211.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/net80211.h>
-#include <gpxe/sec80211.h>
-#include <gpxe/timer.h>
-#include <gpxe/nap.h>
-#include <unistd.h>
-#include <errno.h>
-
-/** @file
- *
- * 802.11 device management
- */
-
-/* Disambiguate the EINVAL's a bit */
-#define EINVAL_PKT_TOO_SHORT ( EINVAL | EUNIQ_01 )
-#define EINVAL_PKT_VERSION ( EINVAL | EUNIQ_02 )
-#define EINVAL_PKT_NOT_DATA ( EINVAL | EUNIQ_03 )
-#define EINVAL_PKT_NOT_FROMDS ( EINVAL | EUNIQ_04 )
-#define EINVAL_PKT_LLC_HEADER ( EINVAL | EUNIQ_05 )
-#define EINVAL_CRYPTO_REQUEST ( EINVAL | EUNIQ_06 )
-#define EINVAL_ACTIVE_SCAN ( EINVAL | EUNIQ_07 )
-
-/*
- * 802.11 error codes: The AP can give us a status code explaining why
- * authentication failed, or a reason code explaining why we were
- * deauthenticated/disassociated. These codes range from 0-63 (the
- * field is 16 bits wide, but only up to 45 or so are defined yet; we
- * allow up to 63 for extensibility). This is encoded into an error
- * code as such:
- *
- * status & 0x1f goes here --vv--
- * Status code 0-31: ECONNREFUSED | EUNIQ_(status & 0x1f) (0e1a6038)
- * Status code 32-63: EHOSTUNREACH | EUNIQ_(status & 0x1f) (171a6011)
- * Reason code 0-31: ECONNRESET | EUNIQ_(reason & 0x1f) (0f1a6039)
- * Reason code 32-63: ENETRESET | EUNIQ_(reason & 0x1f) (271a6001)
- *
- * The POSIX error codes more or less convey the appropriate message
- * (status codes occur when we can't associate at all, reason codes
- * when we lose association unexpectedly) and let us extract the
- * complete 802.11 error code from the rc value.
- */
-
-/** Make return status code from 802.11 status code */
-#define E80211_STATUS( stat ) ( ((stat & 0x20)? EHOSTUNREACH : ECONNREFUSED) \
- | ((stat & 0x1f) << 8) )
-
-/** Make return status code from 802.11 reason code */
-#define E80211_REASON( reas ) ( ((reas & 0x20)? ENETRESET : ECONNRESET) \
- | ((reas & 0x1f) << 8) )
-
-
-/** 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;
-};
-
-/**
- * @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 );
-/** @} */
-
-/**
- * @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 process *proc );
-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.
- */
-struct setting net80211_ssid_setting __setting = {
- .name = "ssid",
- .description = "802.11 SSID (network name)",
- .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.
- */
-struct setting net80211_active_setting __setting = {
- .name = "active-scan",
- .description = "Use an active scan during 802.11 association",
- .type = &setting_type_int8,
-};
-
-/** The cryptographic key to use
- *
- * For hex WEP keys, as is common, this must be entered using the
- * normal gPXE method for entering hex settings; an ASCII string of
- * hex characters will not behave as expected.
- */
-struct setting net80211_key_setting __setting = {
- .name = "key",
- .description = "Encryption key for protected 802.11 networks",
- .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 ---------- */
-
-/** 802.11 broadcast MAC address */
-static u8 net80211_ll_broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-/**
- * 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 gPXE.
- */
-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 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 )
-{
- 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;
- 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,
- .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 );
-
- list_for_each_entry ( iobuf, &dev->mgmt_queue, list ) {
- list_del ( &iobuf->list );
- return iobuf;
- }
- assert ( 0 );
- }
-
- 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 ---------- */
-
-/**
- * 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 = net80211_ll_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_step_associate,
- &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;
-
- 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 gPXE'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 ( dev->netdev->state & NETDEV_OPEN );
-
- 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,
- net80211_ll_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 proc Association process
- */
-static void net80211_step_associate ( struct process *proc )
-{
- struct net80211_device *dev =
- container_of ( proc, struct net80211_device, proc_assoc );
- 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 ( proc );
-
- 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 ( proc );
-
- 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 ( ! ( dev->netdev->state & NETDEV_OPEN ) )
- 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 ( dev->netdev->state & NETDEV_OPEN );
-
- 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 ( dev->netdev->state & NETDEV_OPEN );
-
- 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 ( dev->netdev->state & NETDEV_OPEN );
-
- 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 ( dev->netdev->state & NETDEV_OPEN );
-
- 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;
- }
-
- 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 );
-}
diff --git a/gpxe/src/net/80211/rc80211.c b/gpxe/src/net/80211/rc80211.c
deleted file mode 100644
index 5bd19143..00000000
--- a/gpxe/src/net/80211/rc80211.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Simple 802.11 rate-control algorithm for gPXE.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdlib.h>
-#include <gpxe/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 gPXE'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 gPXE 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/gpxe/src/net/80211/sec80211.c b/gpxe/src/net/80211/sec80211.c
deleted file mode 100644
index c5aa1183..00000000
--- a/gpxe/src/net/80211/sec80211.c
+++ /dev/null
@@ -1,503 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <gpxe/ieee80211.h>
-#include <gpxe/net80211.h>
-#include <gpxe/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.
- */
-
-/** 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 | EUNIQ_10 | ( crypt << 8 ) );
- }
-
- *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 gPXE support
- * @v tbl_end End of linker table to examine for gPXE 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 gPXE, 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/gpxe/src/net/80211/wep.c b/gpxe/src/net/80211/wep.c
deleted file mode 100644
index 1c37e0c3..00000000
--- a/gpxe/src/net/80211/wep.c
+++ /dev/null
@@ -1,303 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <gpxe/net80211.h>
-#include <gpxe/sec80211.h>
-#include <gpxe/crypto.h>
-#include <gpxe/arc4.h>
-#include <gpxe/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_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_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/gpxe/src/net/80211/wpa.c b/gpxe/src/net/80211/wpa.c
deleted file mode 100644
index 9bac8fe7..00000000
--- a/gpxe/src/net/80211/wpa.c
+++ /dev/null
@@ -1,973 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <gpxe/net80211.h>
-#include <gpxe/sec80211.h>
-#include <gpxe/wpa.h>
-#include <gpxe/eapol.h>
-#include <gpxe/crypto.h>
-#include <gpxe/arc4.h>
-#include <gpxe/crc32.h>
-#include <gpxe/sha1.h>
-#include <gpxe/hmac.h>
-#include <gpxe/list.h>
-#include <gpxe/ethernet.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.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 gPXE, 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 gPXE'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 );
- }
- }
-}
-
-
-/**
- * Check PMKID consistency
- *
- * @v ctx WPA common context
- * @v pmkid PMKID to check against (16 bytes long)
- * @ret rc Zero if they match, or a negative error code if not
- */
-int wpa_check_pmkid ( struct wpa_common_ctx *ctx, const u8 *pmkid )
-{
- u8 sha1_ctx[SHA1_CTX_SIZE];
- u8 my_pmkid[SHA1_SIZE];
- u8 pmk[ctx->pmk_len];
- size_t pmk_len;
- struct {
- char name[8];
- u8 aa[ETH_ALEN];
- u8 spa[ETH_ALEN];
- } __attribute__ (( packed )) pmkid_data;
-
- memcpy ( pmk, ctx->pmk, ctx->pmk_len );
- pmk_len = ctx->pmk_len;
-
- memcpy ( pmkid_data.name, "PMK Name", 8 );
- memcpy ( pmkid_data.aa, ctx->dev->bssid, ETH_ALEN );
- memcpy ( pmkid_data.spa, ctx->dev->netdev->ll_addr, ETH_ALEN );
-
- hmac_init ( &sha1_algorithm, sha1_ctx, pmk, &pmk_len );
- hmac_update ( &sha1_algorithm, sha1_ctx, &pmkid_data,
- sizeof ( pmkid_data ) );
- hmac_final ( &sha1_algorithm, sha1_ctx, pmk, &pmk_len, my_pmkid );
-
- if ( memcmp ( my_pmkid, pmkid, WPA_PMKID_LEN ) != 0 )
- return -EACCES;
-
- return 0;
-}
-
-
-/**
- * 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 %d)\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 );
-}
-
-
-/**
- * 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 )
-{
- int rc;
-
- if ( ctx->state == WPA_WAITING )
- return -EINVAL;
-
- ctx->state = WPA_WORKING;
- memcpy ( ctx->Anonce, pkt->nonce, sizeof ( ctx->Anonce ) );
- if ( ! ctx->have_Snonce ) {
- get_random_bytes ( ctx->Snonce, sizeof ( ctx->Snonce ) );
- ctx->have_Snonce = 1;
- }
-
- if ( is_rsn && pkt->datalen ) {
- union ieee80211_ie *ie = ( union ieee80211_ie * ) pkt->data;
- void *ie_end = pkt->data + pkt->datalen;
-
- if ( ! ieee80211_ie_bound ( ie, ie_end ) ) {
- DBGC ( ctx, "WPA %p: malformed PMKID KDE\n", ctx );
- return wpa_fail ( ctx, -EINVAL );
- }
-
- while ( ie ) {
- if ( ie->id == IEEE80211_IE_VENDOR &&
- ie->vendor.oui == WPA_KDE_PMKID ) {
- rc = wpa_check_pmkid ( ctx, ie->vendor.data );
- if ( rc < 0 ) {
- DBGC ( ctx, "WPA %p ALERT: PMKID "
- "mismatch in 1/4\n", ctx );
- return wpa_fail ( ctx, rc );
- }
- }
-
- ie = ieee80211_next_ie ( ie, ie_end );
- }
- }
-
- 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 gPXE 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 > %d)\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_source Source link-layer address
- */
-static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev,
- 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 %d 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 */
-REQUIRE_OBJECT ( eapol );
diff --git a/gpxe/src/net/80211/wpa_ccmp.c b/gpxe/src/net/80211/wpa_ccmp.c
deleted file mode 100644
index 08b1e17b..00000000
--- a/gpxe/src/net/80211/wpa_ccmp.c
+++ /dev/null
@@ -1,528 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <gpxe/net80211.h>
-#include <gpxe/crypto.h>
-#include <gpxe/hmac.h>
-#include <gpxe/sha1.h>
-#include <gpxe/aes.h>
-#include <gpxe/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_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/gpxe/src/net/80211/wpa_psk.c b/gpxe/src/net/80211/wpa_psk.c
deleted file mode 100644
index e7521682..00000000
--- a/gpxe/src/net/80211/wpa_psk.c
+++ /dev/null
@@ -1,125 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <gpxe/net80211.h>
-#include <gpxe/sha1.h>
-#include <gpxe/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/gpxe/src/net/80211/wpa_tkip.c b/gpxe/src/net/80211/wpa_tkip.c
deleted file mode 100644
index 0cb697fa..00000000
--- a/gpxe/src/net/80211/wpa_tkip.c
+++ /dev/null
@@ -1,586 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <gpxe/net80211.h>
-#include <gpxe/crypto.h>
-#include <gpxe/hmac.h>
-#include <gpxe/sha1.h>
-#include <gpxe/md5.h>
-#include <gpxe/crc32.h>
-#include <gpxe/arc4.h>
-#include <gpxe/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] ^ swap16 ( 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 )
-{
- struct md5_ctx md5;
- u8 kckb[16];
- size_t kck_len = 16;
-
- memcpy ( kckb, kck, kck_len );
-
- hmac_init ( &md5_algorithm, &md5, kckb, &kck_len );
- hmac_update ( &md5_algorithm, &md5, msg, len );
- hmac_final ( &md5_algorithm, &md5, 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/gpxe/src/net/aoe.c b/gpxe/src/net/aoe.c
deleted file mode 100644
index 839a875b..00000000
--- a/gpxe/src/net/aoe.c
+++ /dev/null
@@ -1,471 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stddef.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <assert.h>
-#include <byteswap.h>
-#include <gpxe/list.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/ethernet.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/uaccess.h>
-#include <gpxe/ata.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/process.h>
-#include <gpxe/features.h>
-#include <gpxe/aoe.h>
-
-/** @file
- *
- * AoE protocol
- *
- */
-
-FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 );
-
-struct net_protocol aoe_protocol;
-
-/** List of all AoE sessions */
-static LIST_HEAD ( aoe_sessions );
-
-static void aoe_free ( struct refcnt *refcnt ) {
- struct aoe_session *aoe =
- container_of ( refcnt, struct aoe_session, refcnt );
-
- netdev_put ( aoe->netdev );
- free ( aoe );
-}
-
-/**
- * Mark current AoE command complete
- *
- * @v aoe AoE session
- * @v rc Return status code
- */
-static void aoe_done ( struct aoe_session *aoe, int rc ) {
-
- /* Record overall command status */
- if ( aoe->command ) {
- aoe->command->cb.cmd_stat = aoe->status;
- aoe->command->rc = rc;
- aoe->command = NULL;
- }
-
- /* Stop retransmission timer */
- stop_timer ( &aoe->timer );
-
- /* Mark operation as complete */
- aoe->rc = rc;
-}
-
-/**
- * Send AoE command
- *
- * @v aoe AoE session
- * @ret rc Return status code
- *
- * This transmits an AoE command packet. It does not wait for a
- * response.
- */
-static int aoe_send_command ( struct aoe_session *aoe ) {
- struct ata_command *command = aoe->command;
- struct io_buffer *iobuf;
- struct aoehdr *aoehdr;
- union aoecmd *aoecmd;
- struct aoeata *aoeata;
- unsigned int count;
- unsigned int data_out_len;
- unsigned int aoecmdlen;
-
- /* Fail immediately if we have no netdev to send on */
- if ( ! aoe->netdev ) {
- aoe_done ( aoe, -ENETUNREACH );
- return -ENETUNREACH;
- }
-
- /* 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 ( &aoe->timer );
-
- /* Calculate count and data_out_len for this subcommand */
- switch ( aoe->aoe_cmd_type ) {
- case AOE_CMD_ATA:
- count = command->cb.count.native;
- if ( count > AOE_MAX_COUNT )
- count = AOE_MAX_COUNT;
- data_out_len = ( command->data_out ?
- ( count * ATA_SECTOR_SIZE ) : 0 );
- aoecmdlen = sizeof ( aoecmd->ata );
- break;
- case AOE_CMD_CONFIG:
- count = 0;
- data_out_len = 0;
- aoecmdlen = sizeof ( aoecmd->cfg );
- break;
- default:
- return -ENOTSUP;
- }
-
- /* Create outgoing I/O buffer */
- iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) +
- aoecmdlen + data_out_len );
-
- if ( ! iobuf )
- return -ENOMEM;
- iob_reserve ( iobuf, ETH_HLEN );
- aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) );
- aoecmd = iob_put ( iobuf, aoecmdlen );
- memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + aoecmdlen ) );
-
- /* Fill AoE header */
- aoehdr->ver_flags = AOE_VERSION;
- aoehdr->major = htons ( aoe->major );
- aoehdr->minor = aoe->minor;
- aoehdr->command = aoe->aoe_cmd_type;
- aoehdr->tag = htonl ( ++aoe->tag );
-
- /* Fill AoE payload */
- switch ( aoe->aoe_cmd_type ) {
- case AOE_CMD_ATA:
- /* Fill AoE command */
- aoeata = &aoecmd->ata;
- linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE,
- __fix_ata_h__ );
- aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 )|
- ( command->cb.device & ATA_DEV_SLAVE ) |
- ( data_out_len ? AOE_FL_WRITE : 0 ) );
- aoeata->err_feat = command->cb.err_feat.bytes.cur;
- aoeata->count = count;
- 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 );
-
- /* Fill data payload */
- copy_from_user ( iob_put ( iobuf, data_out_len ),
- command->data_out, aoe->command_offset,
- data_out_len );
- break;
- case AOE_CMD_CONFIG:
- /* Nothing to do */
- break;
- default:
- assert ( 0 );
- }
-
- /* Send packet */
- return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target );
-}
-
-/**
- * Handle AoE retry timer expiry
- *
- * @v timer AoE retry timer
- * @v fail Failure indicator
- */
-static void aoe_timer_expired ( struct retry_timer *timer, int fail ) {
- struct aoe_session *aoe =
- container_of ( timer, struct aoe_session, timer );
-
- if ( fail ) {
- aoe_done ( aoe, -ETIMEDOUT );
- } else {
- aoe_send_command ( aoe );
- }
-}
-
-/**
- * Handle AoE configuration command response
- *
- * @v aoe AoE session
- * @v ll_source Link-layer source address
- * @ret rc Return status code
- */
-static int aoe_rx_cfg ( struct aoe_session *aoe, const void *ll_source ) {
-
- /* Record target MAC address */
- memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) );
- DBGC ( aoe, "AoE %p target MAC address %s\n",
- aoe, eth_ntoa ( aoe->target ) );
-
- /* Mark config request as complete */
- aoe_done ( aoe, 0 );
-
- return 0;
-}
-
-/**
- * Handle AoE ATA command response
- *
- * @v aoe AoE session
- * @v aoeata AoE ATA command
- * @v len Length of AoE ATA command
- * @ret rc Return status code
- */
-static int aoe_rx_ata ( struct aoe_session *aoe, struct aoeata *aoeata,
- size_t len ) {
- struct ata_command *command = aoe->command;
- unsigned int rx_data_len;
- unsigned int count;
- unsigned int data_len;
-
- /* Sanity check */
- if ( len < sizeof ( *aoeata ) ) {
- /* Ignore packet; allow timer to trigger retransmit */
- return -EINVAL;
- }
- rx_data_len = ( len - sizeof ( *aoeata ) );
-
- /* Calculate count and data_len for this subcommand */
- count = command->cb.count.native;
- if ( count > AOE_MAX_COUNT )
- count = AOE_MAX_COUNT;
- data_len = count * ATA_SECTOR_SIZE;
-
- /* Merge into overall ATA status */
- aoe->status |= aoeata->cmd_stat;
-
- /* Copy data payload */
- if ( command->data_in ) {
- if ( rx_data_len > data_len )
- rx_data_len = data_len;
- copy_to_user ( command->data_in, aoe->command_offset,
- aoeata->data, rx_data_len );
- }
-
- /* Update ATA command and offset */
- aoe->command_offset += data_len;
- command->cb.lba.native += count;
- command->cb.count.native -= count;
-
- /* Check for operation complete */
- if ( ! command->cb.count.native ) {
- aoe_done ( aoe, 0 );
- return 0;
- }
-
- /* Transmit next portion of request */
- stop_timer ( &aoe->timer );
- aoe_send_command ( aoe );
-
- return 0;
-}
-
-/**
- * Process incoming AoE packets
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
- * @v ll_source Link-layer source address
- * @ret rc Return status code
- *
- */
-static int aoe_rx ( struct io_buffer *iobuf,
- struct net_device *netdev __unused,
- const void *ll_source ) {
- struct aoehdr *aoehdr = iobuf->data;
- struct aoe_session *aoe;
- int rc = 0;
-
- /* Sanity checks */
- if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) {
- rc = -EINVAL;
- goto done;
- }
- if ( ( aoehdr->ver_flags & AOE_VERSION_MASK ) != AOE_VERSION ) {
- rc = -EPROTONOSUPPORT;
- goto done;
- }
- if ( ! ( aoehdr->ver_flags & AOE_FL_RESPONSE ) ) {
- /* Ignore AoE requests that we happen to see */
- goto done;
- }
- iob_pull ( iobuf, sizeof ( *aoehdr ) );
-
- /* Demultiplex amongst active AoE sessions */
- list_for_each_entry ( aoe, &aoe_sessions, list ) {
- if ( ntohs ( aoehdr->major ) != aoe->major )
- continue;
- if ( aoehdr->minor != aoe->minor )
- continue;
- if ( ntohl ( aoehdr->tag ) != aoe->tag )
- continue;
- if ( aoehdr->ver_flags & AOE_FL_ERROR ) {
- aoe_done ( aoe, -EIO );
- break;
- }
- switch ( aoehdr->command ) {
- case AOE_CMD_ATA:
- rc = aoe_rx_ata ( aoe, iobuf->data, iob_len ( iobuf ));
- break;
- case AOE_CMD_CONFIG:
- rc = aoe_rx_cfg ( aoe, ll_source );
- break;
- default:
- DBGC ( aoe, "AoE %p ignoring command %02x\n",
- aoe, aoehdr->command );
- break;
- }
- break;
- }
-
- done:
- 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,
-};
-
-/**
- * Issue ATA command via an open AoE session
- *
- * @v ata ATA device
- * @v command ATA command
- * @ret rc Return status code
- */
-static int aoe_command ( struct ata_device *ata,
- struct ata_command *command ) {
- struct aoe_session *aoe =
- container_of ( ata->backend, struct aoe_session, refcnt );
-
- aoe->command = command;
- aoe->status = 0;
- aoe->command_offset = 0;
- aoe->aoe_cmd_type = AOE_CMD_ATA;
-
- aoe_send_command ( aoe );
-
- return 0;
-}
-
-/**
- * Issue AoE config query for AoE target discovery
- *
- * @v aoe AoE session
- * @ret rc Return status code
- */
-static int aoe_discover ( struct aoe_session *aoe ) {
- int rc;
-
- aoe->status = 0;
- aoe->aoe_cmd_type = AOE_CMD_CONFIG;
- aoe->command = NULL;
-
- aoe_send_command ( aoe );
-
- aoe->rc = -EINPROGRESS;
- while ( aoe->rc == -EINPROGRESS )
- step();
- rc = aoe->rc;
-
- return rc;
-}
-
-static int aoe_detached_command ( struct ata_device *ata __unused,
- struct ata_command *command __unused ) {
- return -ENODEV;
-}
-
-void aoe_detach ( struct ata_device *ata ) {
- struct aoe_session *aoe =
- container_of ( ata->backend, struct aoe_session, refcnt );
-
- stop_timer ( &aoe->timer );
- ata->command = aoe_detached_command;
- list_del ( &aoe->list );
- ref_put ( ata->backend );
- ata->backend = NULL;
-}
-
-static int aoe_parse_root_path ( struct aoe_session *aoe,
- const char *root_path ) {
- char *ptr;
-
- if ( strncmp ( root_path, "aoe:", 4 ) != 0 )
- return -EINVAL;
- ptr = ( ( char * ) root_path + 4 );
-
- if ( *ptr++ != 'e' )
- return -EINVAL;
-
- aoe->major = strtoul ( ptr, &ptr, 10 );
- if ( *ptr++ != '.' )
- return -EINVAL;
-
- aoe->minor = strtoul ( ptr, &ptr, 10 );
- if ( *ptr )
- return -EINVAL;
-
- return 0;
-}
-
-int aoe_attach ( struct ata_device *ata, struct net_device *netdev,
- const char *root_path ) {
- struct aoe_session *aoe;
- int rc;
-
- /* Allocate and initialise structure */
- aoe = zalloc ( sizeof ( *aoe ) );
- if ( ! aoe )
- return -ENOMEM;
- aoe->refcnt.free = aoe_free;
- aoe->netdev = netdev_get ( netdev );
- memcpy ( aoe->target, netdev->ll_broadcast, sizeof ( aoe->target ) );
- aoe->tag = AOE_TAG_MAGIC;
- aoe->timer.expired = aoe_timer_expired;
-
- /* Parse root path */
- if ( ( rc = aoe_parse_root_path ( aoe, root_path ) ) != 0 )
- goto err;
-
- /* Attach parent interface, transfer reference to connection
- * list, and return
- */
- ata->backend = ref_get ( &aoe->refcnt );
- ata->command = aoe_command;
- list_add ( &aoe->list, &aoe_sessions );
-
- /* Send discovery packet to find the target MAC address.
- * Ideally, this ought to be done asynchronously, but the
- * block device interface does not yet support asynchronous
- * operation.
- */
- if ( ( rc = aoe_discover( aoe ) ) != 0 )
- goto err;
-
- return 0;
-
- err:
- ref_put ( &aoe->refcnt );
- return rc;
-}
diff --git a/gpxe/src/net/arp.c b/gpxe/src/net/arp.c
deleted file mode 100644
index 124a856e..00000000
--- a/gpxe/src/net/arp.c
+++ /dev/null
@@ -1,289 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/if_arp.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/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.
- *
- */
-
-/** An ARP cache entry */
-struct arp_entry {
- /** Network-layer protocol */
- struct net_protocol *net_protocol;
- /** Link-layer protocol */
- struct ll_protocol *ll_protocol;
- /** Network-layer address */
- uint8_t net_addr[MAX_NET_ADDR_LEN];
- /** Link-layer address */
- uint8_t ll_addr[MAX_LL_ADDR_LEN];
-};
-
-/** Number of entries in the ARP cache
- *
- * This is a global cache, covering all network interfaces,
- * network-layer protocols and link-layer protocols.
- */
-#define NUM_ARP_ENTRIES 4
-
-/** The ARP cache */
-static struct arp_entry arp_table[NUM_ARP_ENTRIES];
-#define arp_table_end &arp_table[NUM_ARP_ENTRIES]
-
-static unsigned int next_new_arp_entry = 0;
-
-struct net_protocol arp_protocol;
-
-/**
- * Find entry in the ARP cache
- *
- * @v ll_protocol Link-layer protocol
- * @v net_protocol Network-layer protocol
- * @v net_addr Network-layer address
- * @ret arp ARP cache entry, or NULL if not found
- *
- */
-static struct arp_entry *
-arp_find_entry ( struct ll_protocol *ll_protocol,
- struct net_protocol *net_protocol,
- const void *net_addr ) {
- struct arp_entry *arp;
-
- for ( arp = arp_table ; arp < arp_table_end ; arp++ ) {
- if ( ( arp->ll_protocol == ll_protocol ) &&
- ( arp->net_protocol == net_protocol ) &&
- ( memcmp ( arp->net_addr, net_addr,
- net_protocol->net_addr_len ) == 0 ) )
- return arp;
- }
- return NULL;
-}
-
-/**
- * Look up media-specific link-layer address in the ARP cache
- *
- * @v netdev Network device
- * @v net_protocol Network-layer protocol
- * @v dest_net_addr Destination network-layer address
- * @v source_net_addr Source network-layer address
- * @ret dest_ll_addr Destination link layer address
- * @ret rc Return status code
- *
- * This function will use the ARP cache to look up the link-layer
- * address for the link-layer protocol associated with the network
- * device and the given network-layer protocol and addresses. If
- * found, the destination link-layer address will be filled in in @c
- * dest_ll_addr.
- *
- * If no address is found in the ARP cache, an ARP request will be
- * transmitted on the specified network device and -ENOENT will be
- * returned.
- */
-int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol,
- const void *dest_net_addr, const void *source_net_addr,
- void *dest_ll_addr ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- const struct arp_entry *arp;
- struct io_buffer *iobuf;
- struct arphdr *arphdr;
- int rc;
-
- /* Look for existing entry in ARP table */
- arp = arp_find_entry ( ll_protocol, net_protocol, dest_net_addr );
- if ( arp ) {
- DBG ( "ARP cache hit: %s %s => %s %s\n",
- net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
- ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
- memcpy ( dest_ll_addr, arp->ll_addr, ll_protocol->ll_addr_len);
- return 0;
- }
- DBG ( "ARP cache miss: %s %s\n", net_protocol->name,
- net_protocol->ntoa ( dest_net_addr ) );
-
- /* 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 ),
- source_net_addr, 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 ),
- dest_net_addr, net_protocol->net_addr_len );
-
- /* Transmit ARP request */
- if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol,
- netdev->ll_broadcast ) ) != 0 )
- return rc;
-
- return -ENOENT;
-}
-
-/**
- * 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
- * @ret rc Return status code
- *
- * This handles ARP requests and responses as detailed in RFC826. The
- * method detailed within the RFC is pretty optimised, handling
- * requests and responses with basically a single code path and
- * avoiding the need for extraneous ARP requests; read the RFC for
- * details.
- */
-static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
- const void *ll_source __unused ) {
- struct arphdr *arphdr = iobuf->data;
- struct arp_net_protocol *arp_net_protocol;
- struct net_protocol *net_protocol;
- struct ll_protocol *ll_protocol;
- struct arp_entry *arp;
- int merge = 0;
-
- /* Identify network-layer and link-layer protocols */
- arp_net_protocol = arp_find_protocol ( arphdr->ar_pro );
- if ( ! arp_net_protocol )
- 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 ) )
- goto done;
-
- /* See if we have an entry for this sender, and update it if so */
- arp = arp_find_entry ( ll_protocol, net_protocol,
- arp_sender_pa ( arphdr ) );
- if ( arp ) {
- memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
- arphdr->ar_hln );
- merge = 1;
- DBG ( "ARP cache update: %s %s => %s %s\n",
- net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
- ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
- }
-
- /* See if we own the target protocol address */
- if ( arp_net_protocol->check ( netdev, arp_target_pa ( arphdr ) ) != 0)
- goto done;
-
- /* Create new ARP table entry if necessary */
- if ( ! merge ) {
- arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES];
- arp->ll_protocol = ll_protocol;
- arp->net_protocol = net_protocol;
- memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ),
- arphdr->ar_hln );
- memcpy ( arp->net_addr, arp_sender_pa ( arphdr ),
- arphdr->ar_pln);
- DBG ( "ARP cache add: %s %s => %s %s\n",
- net_protocol->name, net_protocol->ntoa ( arp->net_addr ),
- ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) );
- }
-
- /* If it's not a request, there's nothing more to do */
- if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) )
- goto done;
-
- /* Change request to a reply */
- DBG ( "ARP reply: %s %s => %s %s\n", 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 */
- net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol,
- arp_target_ha ( arphdr ) );
-
- done:
- free_iob ( iobuf );
- return 0;
-}
-
-/**
- * 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 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/gpxe/src/net/cachedhcp.c b/gpxe/src/net/cachedhcp.c
deleted file mode 100644
index 37f344b6..00000000
--- a/gpxe/src/net/cachedhcp.c
+++ /dev/null
@@ -1,76 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/dhcppkt.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/uaccess.h>
-
-/** @file
- *
- * Cached DHCP packet handling
- *
- */
-
-/**
- * Store cached DHCPACK packet
- *
- * @v data User pointer to cached DHCP packet data
- * @v len Length of cached DHCP packet data
- * @ret rc Return status code
- *
- * This function should be called by the architecture-specific
- * get_cached_dhcpack() handler.
- */
-void store_cached_dhcpack ( userptr_t data, size_t len ) {
- struct dhcp_packet *dhcppkt;
- struct dhcphdr *dhcphdr;
- struct settings *parent;
- int rc;
-
- /* Create DHCP packet */
- dhcppkt = zalloc ( sizeof ( *dhcppkt ) + len );
- if ( ! dhcppkt )
- return;
-
- /* Fill in data for DHCP packet */
- dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( * dhcppkt ) );
- copy_from_user ( dhcphdr, data, 0, len );
- dhcppkt_init ( dhcppkt, dhcphdr, len );
- DBG_HD ( dhcppkt->options.data, dhcppkt->options.len );
-
- /* Register settings on the last opened network device.
- * This will have the effect of registering cached settings
- * with a network device when "dhcp netX" is performed for that
- * device, which is usually what we want.
- */
- parent = netdev_settings ( last_opened_netdev() );
- if ( ( rc = register_settings ( &dhcppkt->settings, parent ) ) != 0 )
- DBG ( "DHCP could not register cached settings: %s\n",
- strerror ( rc ) );
-
- dhcppkt_put ( dhcppkt );
-
- DBG ( "DHCP registered cached settings\n" );
-}
diff --git a/gpxe/src/net/dhcpopts.c b/gpxe/src/net/dhcpopts.c
deleted file mode 100644
index 6482c627..00000000
--- a/gpxe/src/net/dhcpopts.c
+++ /dev/null
@@ -1,447 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/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->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;
-}
-
-/**
- * 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)
- * @v can_realloc Can reallocate options data if necessary
- * @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,
- int can_realloc ) {
- struct dhcp_option *encapsulator;
- struct dhcp_option *option;
- ssize_t delta = ( new_len - old_len );
- size_t new_options_len;
- size_t new_encapsulator_len;
- void *new_data;
- void *source;
- void *dest;
- void *end;
-
- /* Check for sufficient space, and update length fields */
- if ( new_len > DHCP_MAX_LEN ) {
- DBGC ( options, "DHCPOPT %p overlength option\n", options );
- return -ENOSPC;
- }
- new_options_len = ( options->len + delta );
- if ( new_options_len > options->max_len ) {
- /* Reallocate options block if allowed to do so. */
- if ( can_realloc ) {
- new_data = realloc ( options->data, new_options_len );
- if ( ! new_data ) {
- DBGC ( options, "DHCPOPT %p could not "
- "reallocate to %zd bytes\n", options,
- new_options_len );
- return -ENOMEM;
- }
- options->data = new_data;
- options->max_len = new_options_len;
- } else {
- DBGC ( options, "DHCPOPT %p out of space\n", options );
- return -ENOMEM;
- }
- }
- 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;
- }
- options->len = new_options_len;
-
- /* Move remainder of option data */
- option = dhcp_option ( options, offset );
- source = ( ( ( void * ) option ) + old_len );
- dest = ( ( ( void * ) option ) + new_len );
- end = ( options->data + options->max_len );
- memmove ( dest, source, ( end - dest ) );
-
- 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
- * @v can_realloc Can reallocate options data if necessary
- * @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,
- int can_realloc ) {
- static const uint8_t empty_encapsulator[] = { 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->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_encapsulator, 1,
- can_realloc );
- 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,
- can_realloc ) ) != 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, 0 );
- }
-
- return offset;
-}
-
-/**
- * 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, 0 );
- if ( offset < 0 )
- return offset;
- return 0;
-}
-
-/**
- * Store value of DHCP option setting, extending options block if necessary
- *
- * @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_extensible_store ( struct dhcp_options *options, unsigned int tag,
- const void *data, size_t len ) {
- int offset;
-
- offset = set_dhcp_option ( options, tag, data, len, 1 );
- 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.
- */
-static void dhcpopt_update_len ( struct dhcp_options *options ) {
- struct dhcp_option *option;
- int offset = 0;
- ssize_t remaining = options->max_len;
- unsigned int option_len;
-
- /* Find last non-pad option */
- options->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->len = offset;
- }
-}
-
-/**
- * Initialise prepopulated block of DHCP options
- *
- * @v options Uninitialised DHCP option block
- * @v data Memory for DHCP option data
- * @v max_len Length of memory for DHCP option data
- *
- * 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 max_len ) {
-
- /* Fill in fields */
- options->data = data;
- options->max_len = max_len;
-
- /* Update length */
- dhcpopt_update_len ( options );
-
- DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n",
- options, options->data, options->len, options->max_len );
-}
diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c
deleted file mode 100644
index 20a0e666..00000000
--- a/gpxe/src/net/dhcppkt.c
+++ /dev/null
@@ -1,283 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/dhcpopts.h>
-#include <gpxe/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;
-}
-
-/**
- * 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;
- int rc;
-
- /* 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 */
- rc = dhcpopt_store ( &dhcppkt->options, tag, data, len );
-
- /* Update our used-length field */
- dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
- dhcppkt->options.len );
-
- return rc;
-}
-
-/**
- * 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
- *
- */
-
-/**
- * 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,
- 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 = {
- .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 ) {
- dhcppkt->dhcphdr = data;
- dhcppkt->max_len = len;
- dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options,
- ( len - offsetof ( struct dhcphdr, options ) ) );
- dhcppkt->len = ( offsetof ( struct dhcphdr, options ) +
- dhcppkt->options.len );
- settings_init ( &dhcppkt->settings,
- &dhcppkt_settings_operations, &dhcppkt->refcnt,
- DHCP_SETTINGS_NAME, 0 );
-}
diff --git a/gpxe/src/net/eapol.c b/gpxe/src/net/eapol.c
deleted file mode 100644
index 507c8ce2..00000000
--- a/gpxe/src/net/eapol.c
+++ /dev/null
@@ -1,85 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/** @file
- *
- * 802.1X Extensible Authentication Protocol over LANs demultiplexer
- *
- */
-
-#include <gpxe/netdevice.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/eapol.h>
-#include <errno.h>
-#include <byteswap.h>
-
-/**
- * Receive EAPOL network-layer packet
- *
- * @v iob I/O buffer
- * @v netdev Network device
- * @v ll_source Link-layer source address
- *
- * 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_source )
-{
- 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_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/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c
deleted file mode 100644
index 79ed1dc6..00000000
--- a/gpxe/src/net/ethernet.c
+++ /dev/null
@@ -1,193 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <assert.h>
-#include <gpxe/if_arp.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/in.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/ethernet.h>
-
-/** @file
- *
- * Ethernet protocol
- *
- */
-
-/** Ethernet broadcast MAC address */
-static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
-/**
- * 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
- */
-static 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 rc Return status code
- */
-static int eth_pull ( struct net_device *netdev __unused,
- struct io_buffer *iobuf, const void **ll_dest,
- const void **ll_source, uint16_t *net_proto ) {
- struct ethhdr *ethhdr = iobuf->data;
-
- /* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) {
- 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;
-
- 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 );
-}
-
-/**
- * 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;
- 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;
-}
-
-/** 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,
-};
-
-/**
- * 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 = &ethernet_protocol;
- netdev->ll_broadcast = eth_broadcast;
- netdev->max_pkt_len = ETH_FRAME_LEN;
- }
- return netdev;
-}
diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c
deleted file mode 100644
index ad3f046f..00000000
--- a/gpxe/src/net/fakedhcp.c
+++ /dev/null
@@ -1,217 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <gpxe/settings.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/dhcppkt.h>
-#include <gpxe/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;
- int len;
- int check_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_setting_len ( source, &setting );
- if ( len < 0 )
- break;
- {
- char buf[len];
-
- check_len = fetch_setting ( source, &setting,
- buf, sizeof (buf));
- assert ( check_len == len );
- if ( ( rc = dhcppkt_store ( dest, tag, buf,
- sizeof(buf) )) !=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,
- 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, 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, 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/gpxe/src/net/icmp.c b/gpxe/src/net/icmp.c
deleted file mode 100644
index 749c3454..00000000
--- a/gpxe/src/net/icmp.c
+++ /dev/null
@@ -1,103 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <string.h>
-#include <errno.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/in.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/icmp.h>
-
-/** @file
- *
- * ICMP protocol
- *
- */
-
-struct tcpip_protocol icmp_protocol __tcpip_protocol;
-
-/**
- * Process a received packet
- *
- * @v iobuf I/O buffer
- * @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 icmp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
- struct sockaddr_tcpip *st_dest,
- uint16_t pshdr_csum __unused ) {
- struct icmp_header *icmp = iobuf->data;
- size_t len = iob_len ( iobuf );
- unsigned int csum;
- 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 done;
- }
-
- /* 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 done;
- }
-
- /* We respond only to pings */
- if ( icmp->type != ICMP_ECHO_REQUEST ) {
- DBG ( "ICMP ignoring type %d\n", icmp->type );
- rc = 0;
- goto done;
- }
-
- DBG ( "ICMP responding to ping\n" );
-
- /* Change type to response and recalculate checksum */
- icmp->type = ICMP_ECHO_RESPONSE;
- icmp->chksum = 0;
- icmp->chksum = tcpip_chksum ( icmp, len );
-
- /* Transmit the response */
- if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmp_protocol, st_dest,
- st_src, NULL, NULL ) ) != 0 ) {
- DBG ( "ICMP could not transmit ping response: %s\n",
- strerror ( rc ) );
- goto done;
- }
-
- done:
- free_iob ( iobuf );
- return rc;
-}
-
-/** ICMP TCP/IP protocol */
-struct tcpip_protocol icmp_protocol __tcpip_protocol = {
- .name = "ICMP",
- .rx = icmp_rx,
- .tcpip_proto = IP_ICMP,
-};
diff --git a/gpxe/src/net/icmpv6.c b/gpxe/src/net/icmpv6.c
deleted file mode 100644
index 237fc4a6..00000000
--- a/gpxe/src/net/icmpv6.c
+++ /dev/null
@@ -1,128 +0,0 @@
-#include <stdint.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <gpxe/in.h>
-#include <gpxe/ip6.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/ndp.h>
-#include <gpxe/icmp6.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/netdevice.h>
-
-struct tcpip_protocol icmp6_protocol;
-
-/**
- * Send neighbour solicitation packet
- *
- * @v netdev Network device
- * @v src Source address
- * @v dest Destination address
- *
- * This function prepares a neighbour solicitation packet and sends it to the
- * network layer.
- */
-int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused,
- struct in6_addr *dest ) {
- union {
- struct sockaddr_in6 sin6;
- struct sockaddr_tcpip st;
- } st_dest;
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- struct neighbour_solicit *nsolicit;
- struct io_buffer *iobuf = alloc_iob ( sizeof ( *nsolicit ) + MIN_IOB_LEN );
- iob_reserve ( iobuf, MAX_HDR_LEN );
- nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) );
-
- /* Fill up the headers */
- memset ( nsolicit, 0, sizeof ( *nsolicit ) );
- nsolicit->type = ICMP6_NSOLICIT;
- nsolicit->code = 0;
- nsolicit->target = *dest;
- nsolicit->opt_type = 1;
- nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8;
- memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr,
- netdev->ll_protocol->ll_addr_len );
- /* Partial checksum */
- nsolicit->csum = 0;
- nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) );
-
- /* Solicited multicast address */
- st_dest.sin6.sin_family = AF_INET6;
- st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff;
- st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02;
- st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000;
- st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000;
- st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000;
- st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001;
- st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3];
- st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff;
-
- /* Send packet over IP6 */
- return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st,
- NULL, &nsolicit->csum );
-}
-
-/**
- * Process ICMP6 headers
- *
- * @v iobuf I/O buffer
- * @v st_src Source address
- * @v st_dest Destination address
- */
-static int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src,
- struct sockaddr_tcpip *st_dest, __unused uint16_t pshdr_csum ) {
- struct icmp6_header *icmp6hdr = iobuf->data;
-
- /* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) {
- DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
- free_iob ( iobuf );
- return -EINVAL;
- }
-
- /* TODO: Verify checksum */
-
- /* Process the ICMP header */
- switch ( icmp6hdr->type ) {
- case ICMP6_NADVERT:
- return ndp_process_advert ( iobuf, st_src, st_dest );
- }
- return -ENOSYS;
-}
-
-#if 0
-void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) {
-
- struct sockaddr_in6 server;
- memcpy ( &server, server_p, sizeof ( server ) );
- struct io_buffer *rxiobuf = alloc_iob ( 500 );
- iob_reserve ( rxiobuf, MAX_HDR_LEN );
- struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) );
- nadvert->type = 136;
- nadvert->code = 0;
- nadvert->flags = ICMP6_FLAGS_SOLICITED;
- nadvert->csum = 0xffff;
- nadvert->target = server.sin6_addr;
- nadvert->opt_type = 2;
- nadvert->opt_len = 1;
- memcpy ( nadvert->opt_ll_addr, ll_addr, 6 );
- struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) );
- ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );
- ip6hdr->hop_limit = 255;
- ip6hdr->nxt_hdr = 58;
- ip6hdr->payload_len = htons ( sizeof ( *nadvert ) );
- ip6hdr->src = server.sin6_addr;
- ip6hdr->dest = server.sin6_addr;
- hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) );
- net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr );
-}
-#endif
-
-/** ICMP6 protocol */
-struct tcpip_protocol icmp6_protocol __tcpip_protocol = {
- .name = "ICMP6",
- .rx = icmp6_rx,
- .tcpip_proto = IP_ICMP6, // 58
-};
diff --git a/gpxe/src/net/infiniband.c b/gpxe/src/net/infiniband.c
deleted file mode 100644
index d7813249..00000000
--- a/gpxe/src/net/infiniband.c
+++ /dev/null
@@ -1,951 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#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 <gpxe/list.h>
-#include <gpxe/errortab.h>
-#include <gpxe/if_arp.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/ipoib.h>
-#include <gpxe/process.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_mi.h>
-#include <gpxe/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 ( EINPROGRESS | EUNIQ_01 )
-#define EINPROGRESS_ARMED ( EINPROGRESS | EUNIQ_02 )
-
-/** Human-readable message for the link statuses */
-struct errortab infiniband_errors[] __errortab = {
- { EINPROGRESS_INIT, "Initialising" },
- { EINPROGRESS_ARMED, "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
- * @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 *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 );
-
- /* 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
- * @v av New address vector, if applicable
- * @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, 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,
- struct 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 av 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 *av,
- struct io_buffer *iobuf ) {
- struct ib_address_vector av_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 ( ! av )
- av = &qp->av;
-
- /* Make modifiable copy of address vector */
- memcpy ( &av_copy, av, sizeof ( av_copy ) );
- av = &av_copy;
-
- /* Fill in optional parameters in address vector */
- if ( ! av->qkey )
- av->qkey = qp->qkey;
- if ( ! av->rate )
- av->rate = IB_RATE_2_5;
-
- /* Post to hardware */
- if ( ( rc = ibdev->op->post_send ( ibdev, qp, av, 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 av Address vector
- * @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 *av,
- struct io_buffer *iobuf, int rc ) {
-
- if ( qp->recv.cq->op->complete_recv ) {
- qp->recv.cq->op->complete_recv ( ibdev, qp, av, 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 = 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
- *
- ***************************************************************************
- */
-
-/**
- * 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;
- }
-
- /* 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;
- }
-
- /* Open device */
- if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not open: %s\n",
- ibdev, strerror ( rc ) );
- goto err_open;
- }
-
- /* Add to head of open devices list */
- list_add ( &ibdev->open_list, &open_ib_devices );
-
- assert ( ibdev->open_count == 1 );
- return 0;
-
- ibdev->op->close ( ibdev );
- err_open:
- 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:
- 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 ) {
- 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 );
- }
-}
-
-/**
- * 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;
- }
-}
-
-/***************************************************************************
- *
- * 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,
- struct ib_gid *gid ) {
- struct ib_multicast_gid *mgid;
- int rc;
-
- /* 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,
- struct ib_gid *gid ) {
- struct ib_multicast_gid *mgid;
-
- /* 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
- *
- ***************************************************************************
- */
-
-/**
- * Get Infiniband HCA information
- *
- * @v ibdev Infiniband device
- * @ret hca_guid HCA GUID
- * @ret num_ports Number of ports
- */
-int ib_get_hca_info ( struct ib_device *ibdev,
- struct ib_gid_half *hca_guid ) {
- struct ib_device *tmp;
- int num_ports = 0;
-
- /* Search for IB devices with the same physical device to
- * identify port count and a suitable Node GUID.
- */
- for_each_ibdev ( tmp ) {
- if ( tmp->dev != ibdev->dev )
- continue;
- if ( num_ports == 0 ) {
- memcpy ( hca_guid, &tmp->gid.u.half[1],
- sizeof ( *hca_guid ) );
- }
- 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
- *
- ***************************************************************************
- */
-
-/**
- * Handle Infiniband link state change
- *
- * @v ibdev Infiniband device
- */
-void ib_link_state_changed ( struct ib_device *ibdev ) {
-
- /* Notify IPoIB of link state change */
- ipoib_link_state_changed ( ibdev );
-}
-
-/**
- * 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;
-
- for_each_ibdev ( ibdev )
- ib_poll_eq ( ibdev );
-}
-
-/** Infiniband event queue process */
-struct process ib_process __permanent_process = {
- .list = LIST_HEAD_INIT ( ib_process.list ),
- .step = 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->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 ) {
- int rc;
-
- /* Add to device list */
- ibdev_get ( ibdev );
- list_add_tail ( &ibdev->list, &ib_devices );
-
- /* Add IPoIB device */
- if ( ( rc = ipoib_probe ( ibdev ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not add IPoIB device: %s\n",
- ibdev, strerror ( rc ) );
- goto err_ipoib_probe;
- }
-
- DBGC ( ibdev, "IBDEV %p registered (phys %s)\n", ibdev,
- ibdev->dev->name );
- return 0;
-
- err_ipoib_probe:
- list_del ( &ibdev->list );
- ibdev_put ( ibdev );
- return rc;
-}
-
-/**
- * Unregister Infiniband device
- *
- * @v ibdev Infiniband device
- */
-void unregister_ibdev ( struct ib_device *ibdev ) {
-
- /* Close device */
- ipoib_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 ( struct 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;
-
- list_for_each_entry ( ibdev, &open_ib_devices, open_list ) {
- assert ( ibdev->open_count != 0 );
- return ibdev;
- }
-
- return NULL;
-}
diff --git a/gpxe/src/net/infiniband/ib_cm.c b/gpxe/src/net/infiniband/ib_cm.c
deleted file mode 100644
index ebe65b33..00000000
--- a/gpxe/src/net/infiniband/ib_cm.c
+++ /dev/null
@@ -1,413 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <assert.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_mi.h>
-#include <gpxe/ib_pathrec.h>
-#include <gpxe/ib_cm.h>
-
-/**
- * @file
- *
- * Infiniband communication management
- *
- */
-
-/** List of connections */
-static LIST_HEAD ( ib_cm_conns );
-
-/**
- * Send "ready to use" response
- *
- * @v ibdev Infiniband device
- * @v mi Management interface
- * @v conn Connection
- * @v av Address vector
- * @ret rc Return status code
- */
-static int ib_cm_send_rtu ( struct ib_device *ibdev,
- struct ib_mad_interface *mi,
- struct ib_connection *conn,
- struct ib_address_vector *av ) {
- union ib_mad mad;
- struct ib_cm_ready_to_use *ready =
- &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 );
- ready->local_id = htonl ( conn->local_id );
- ready->remote_id = htonl ( conn->remote_id );
- if ( ( rc = ib_mi_send ( ibdev, mi, &mad, av ) ) != 0 ){
- DBGC ( conn, "CM %p could not send RTU: %s\n",
- conn, 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_connect_rep ( struct ib_device *ibdev,
- struct ib_mad_interface *mi,
- union ib_mad *mad,
- struct ib_address_vector *av ) {
- struct ib_cm_connect_reply *connect_rep =
- &mad->cm.cm_data.connect_reply;
- struct ib_connection *conn;
- int rc;
-
- /* Identify connection */
- list_for_each_entry ( conn, &ib_cm_conns, list ) {
- if ( ntohl ( connect_rep->remote_id ) != conn->local_id )
- continue;
- /* Try to send "ready to use" reply */
- if ( ( rc = ib_cm_send_rtu ( ibdev, mi, conn, av ) ) != 0 ) {
- /* Ignore errors */
- return;
- }
- return;
- }
-
- DBG ( "CM unidentified connection %08x\n",
- ntohl ( connect_rep->remote_id ) );
-}
-
-/** 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_connect_rep,
- },
-};
-
-/**
- * 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 *connect_rep =
- &mad->cm.cm_data.connect_reply;
- struct ib_cm_connect_reject *connect_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 ( connect_rep->local_qpn ) >> 8 );
- qp->send.psn = ( ntohl ( connect_rep->starting_psn ) >> 8 );
- private_data = &connect_rep->private_data;
- private_data_len = sizeof ( connect_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, conn, av ) ) != 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 ( connect_rej->reason ) );
- /* Private data is valid only for a Consumer Reject */
- if ( connect_rej->reason == htons ( IB_CM_REJECT_CONSUMER ) ) {
- private_data = &connect_rej->private_data;
- private_data_len = sizeof (connect_rej->private_data);
- }
- rc = ib_cm_rejection_reason_to_rc ( connect_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 *connect_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 );
- connect_req->local_id = htonl ( conn->local_id );
- memcpy ( &connect_req->service_id, &conn->service_id,
- sizeof ( connect_req->service_id ) );
- ib_get_hca_info ( ibdev, &connect_req->local_ca );
- connect_req->local_qpn__responder_resources =
- htonl ( ( qp->qpn << 8 ) | 1 );
- connect_req->local_eecn__initiator_depth = htonl ( ( 0 << 8 ) | 1 );
- connect_req->remote_eecn__remote_timeout__service_type__ee_flow_ctrl =
- htonl ( ( 0x14 << 3 ) | ( IB_CM_TRANSPORT_RC << 1 ) |
- ( 0 << 0 ) );
- connect_req->starting_psn__local_timeout__retry_count =
- htonl ( ( qp->recv.psn << 8 ) | ( 0x14 << 3 ) |
- ( 0x07 << 0 ) );
- connect_req->pkey = htons ( ibdev->pkey );
- connect_req->payload_mtu__rdc_exists__rnr_retry =
- ( ( IB_MTU_2048 << 4 ) | ( 1 << 3 ) | ( 0x07 << 0 ) );
- connect_req->max_cm_retries__srq =
- ( ( 0x0f << 4 ) | ( 0 << 3 ) );
- connect_req->primary.local_lid = htons ( ibdev->lid );
- connect_req->primary.remote_lid = htons ( conn->qp->av.lid );
- memcpy ( &connect_req->primary.local_gid, &ibdev->gid,
- sizeof ( connect_req->primary.local_gid ) );
- memcpy ( &connect_req->primary.remote_gid, &conn->qp->av.gid,
- sizeof ( connect_req->primary.remote_gid ) );
- connect_req->primary.flow_label__rate =
- htonl ( ( 0 << 12 ) | ( conn->qp->av.rate << 0 ) );
- connect_req->primary.hop_limit = 0;
- connect_req->primary.sl__subnet_local =
- ( ( conn->qp->av.sl << 4 ) | ( 1 << 3 ) );
- connect_req->primary.local_ack_timeout = ( 0x13 << 3 );
- private_data_len = conn->private_data_len;
- if ( private_data_len > sizeof ( connect_req->private_data ) )
- private_data_len = sizeof ( connect_req->private_data );
- memcpy ( &connect_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,
- struct ib_gid *dgid, struct ib_gid_half *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 %08x:%08x:%08x:%08x %08x:%08x\n",
- conn, ntohl ( dgid->u.dwords[0] ), ntohl ( dgid->u.dwords[1] ),
- ntohl ( dgid->u.dwords[2] ), ntohl ( dgid->u.dwords[3] ),
- ntohl ( service_id->u.dwords[0] ),
- ntohl ( service_id->u.dwords[1] ) );
-
- 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/gpxe/src/net/infiniband/ib_cmrc.c b/gpxe/src/net/infiniband/ib_cmrc.c
deleted file mode 100644
index 2d648115..00000000
--- a/gpxe/src/net/infiniband/ib_cmrc.c
+++ /dev/null
@@ -1,436 +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 <gpxe/iobuf.h>
-#include <gpxe/xfer.h>
-#include <gpxe/process.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_cm.h>
-#include <gpxe/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 xfer_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 */
- struct ib_gid dgid;
- /** Service ID */
- struct ib_gid_half service_id;
- /** QP is connected */
- int connected;
- /** Shutdown process */
- struct process shutdown;
-};
-
-/**
- * Shut down CMRC connection gracefully
- *
- * @v process Process
- *
- * 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 process *process ) {
- struct ib_cmrc_connection *cmrc =
- container_of ( process, struct ib_cmrc_connection, shutdown );
-
- 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 );
-
- /* Remove process from run queue */
- process_del ( &cmrc->shutdown );
-
- /* 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 */
- xfer_nullify ( &cmrc->xfer );
- xfer_close ( &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;
- }
-
- /* 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 av 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 *av __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,
-};
-
-/**
- * Send data via CMRC
- *
- * @v xfer Data transfer interface
- * @v iobuf Datagram I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- */
-static int ib_cmrc_xfer_deliver_iob ( struct xfer_interface *xfer,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta __unused ) {
- struct ib_cmrc_connection *cmrc =
- container_of ( xfer, struct ib_cmrc_connection, xfer );
- 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 xfer Data transfer interface
- * @ret len Length of window
- */
-static size_t ib_cmrc_xfer_window ( struct xfer_interface *xfer ) {
- struct ib_cmrc_connection *cmrc =
- container_of ( xfer, struct ib_cmrc_connection, xfer );
-
- /* We indicate a window only when we are successfully
- * connected.
- */
- return ( cmrc->connected ? IB_MAX_PAYLOAD_SIZE : 0 );
-}
-
-/**
- * Close CMRC data-transfer interface
- *
- * @v xfer Data transfer interface
- * @v rc Reason for close
- */
-static void ib_cmrc_xfer_close ( struct xfer_interface *xfer, int rc ) {
- struct ib_cmrc_connection *cmrc =
- container_of ( xfer, struct ib_cmrc_connection, xfer );
-
- DBGC ( cmrc, "CMRC %p closed: %s\n", cmrc, strerror ( rc ) );
- ib_cmrc_close ( cmrc, rc );
-}
-
-/** CMRC data transfer interface operations */
-static struct xfer_interface_operations ib_cmrc_xfer_operations = {
- .close = ib_cmrc_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = ib_cmrc_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = ib_cmrc_xfer_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * 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 xfer_interface *xfer, struct ib_device *ibdev,
- struct ib_gid *dgid, struct ib_gid_half *service_id ) {
- struct ib_cmrc_connection *cmrc;
- int rc;
-
- /* Allocate and initialise structure */
- cmrc = zalloc ( sizeof ( *cmrc ) );
- if ( ! cmrc ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- xfer_init ( &cmrc->xfer, &ib_cmrc_xfer_operations, &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,
- &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 );
- 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.
- */
- xfer_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/gpxe/src/net/infiniband/ib_mcast.c b/gpxe/src/net/infiniband/ib_mcast.c
deleted file mode 100644
index 5cb395de..00000000
--- a/gpxe/src/net/infiniband/ib_mcast.c
+++ /dev/null
@@ -1,218 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <gpxe/list.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_mi.h>
-#include <gpxe/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, struct 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;
- struct 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 %08x:%08x:%08x:%08x qkey %lx\n",
- ibdev, qp->qpn, ( joined ? "joined" : "left" ),
- ntohl ( gid->u.dwords[0] ), ntohl ( gid->u.dwords[1] ),
- ntohl ( gid->u.dwords[2] ), ntohl ( gid->u.dwords[3] ),
- 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, struct 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 %08x:%08x:%08x:%08x\n",
- ibdev, qp->qpn, ntohl ( gid->u.dwords[0] ),
- ntohl ( gid->u.dwords[1] ), ntohl ( gid->u.dwords[2] ),
- ntohl ( gid->u.dwords[3] ) );
-
- /* 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 ) {
- struct ib_gid *gid = &membership->gid;
- union ib_mad mad;
- int rc;
-
- DBGC ( ibdev, "IBDEV %p QPN %lx leaving %08x:%08x:%08x:%08x\n",
- ibdev, qp->qpn, ntohl ( gid->u.dwords[0] ),
- ntohl ( gid->u.dwords[1] ), ntohl ( gid->u.dwords[2] ),
- ntohl ( gid->u.dwords[3] ) );
-
- /* 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/gpxe/src/net/infiniband/ib_mi.c b/gpxe/src/net/infiniband/ib_mi.c
deleted file mode 100644
index 7511fd87..00000000
--- a/gpxe/src/net/infiniband/ib_mi.c
+++ /dev/null
@@ -1,406 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/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 ( ( 'g' << 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 av 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 *av,
- 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, av ) ) != 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,
-};
-
-/**
- * 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;
- madx->mi = mi;
- madx->timer.expired = ib_mi_timer_expired;
- 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 );
- 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/gpxe/src/net/infiniband/ib_packet.c b/gpxe/src/net/infiniband/ib_packet.c
deleted file mode 100644
index 08820ef3..00000000
--- a/gpxe/src/net/infiniband/ib_packet.c
+++ /dev/null
@@ -1,244 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/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 av Address vector
- */
-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 *av ) {
- 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, av->lid, av->qpn, av->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 = ( av->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 = ( ( av->sl << 4 ) | lnh );
- lrh->dlid = htons ( av->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, &av->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 ( av->qpn );
- bth->ack__psn = htonl ( ( qp->send.psn++ ) & 0xffffffUL );
-
- /* Construct DETH */
- deth->qkey = htonl ( av->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 av Address vector to fill in
- */
-int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf,
- struct ib_queue_pair **qp, size_t *payload_len,
- struct ib_address_vector *av ) {
- 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;
- unsigned long qpn;
- unsigned int lid;
-
- /* Clear return values */
- if ( qp )
- *qp = NULL;
- if ( payload_len )
- *payload_len = 0;
- memset ( av, 0, sizeof ( *av ) );
-
- /* 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 ) );
- av->lid = ntohs ( lrh->slid );
- av->sl = ( lrh->sl__lnh >> 4 );
- lnh = ( lrh->sl__lnh & 0x3 );
- lid = ntohs ( lrh->dlid );
-
- /* 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 ) );
- av->gid_present = 1;
- memcpy ( &av->gid, &grh->sgid, sizeof ( av->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;
- }
- 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 ) );
- av->qpn = ntohl ( deth->src_qp );
- av->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 ( lid ) && grh ) {
- if ( ! ( *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ))){
- DBGC ( ibdev, "IBDEV %p RX for unknown MGID "
- "%08x:%08x:%08x:%08x\n", ibdev,
- ntohl ( grh->dgid.u.dwords[0] ),
- ntohl ( grh->dgid.u.dwords[1] ),
- ntohl ( grh->dgid.u.dwords[2] ),
- ntohl ( grh->dgid.u.dwords[3] ) );
- return -ENODEV;
- }
- } else {
- if ( ! ( *qp = ib_find_qp_qpn ( ibdev, qpn ) ) ) {
- DBGC ( ibdev, "IBDEV %p RX for nonexistent "
- "QPN %lx\n", ibdev, qpn );
- return -ENODEV;
- }
- }
- assert ( *qp );
- }
-
- DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n",
- ibdev, lid, ( IB_LID_MULTICAST( lid ) ?
- ( qp ? (*qp)->ext_qpn : -1UL ) : qpn ),
- av->lid, av->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/gpxe/src/net/infiniband/ib_pathrec.c b/gpxe/src/net/infiniband/ib_pathrec.c
deleted file mode 100644
index 136e628d..00000000
--- a/gpxe/src/net/infiniband/ib_pathrec.c
+++ /dev/null
@@ -1,296 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_mi.h>
-#include <gpxe/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 );
- struct 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 %08x:%08x:%08x:%08x "
- "failed: %s\n", ibdev, htonl ( dgid->u.dwords[0] ),
- htonl ( dgid->u.dwords[1] ),
- htonl ( dgid->u.dwords[2] ),
- htonl ( dgid->u.dwords[3] ), 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 %08x:%08x:%08x:%08x is %04x sl %d "
- "rate %d\n", ibdev, htonl ( dgid->u.dwords[0] ),
- htonl ( dgid->u.dwords[1] ), htonl ( dgid->u.dwords[2] ),
- htonl ( dgid->u.dwords[3] ), 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, struct 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 ) {
- struct 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 %08x:%08x:%08x:%08x\n",
- ibdev, htonl ( gid->u.dwords[0] ),
- htonl ( gid->u.dwords[1] ), htonl ( gid->u.dwords[2] ),
- htonl ( gid->u.dwords[3] ) );
- return 0;
- }
- DBGC ( ibdev, "IBDEV %p cache miss for %08x:%08x:%08x:%08x%s\n",
- ibdev, htonl ( gid->u.dwords[0] ), htonl ( gid->u.dwords[1] ),
- htonl ( gid->u.dwords[2] ), htonl ( gid->u.dwords[3] ),
- ( 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/gpxe/src/net/infiniband/ib_sma.c b/gpxe/src/net/infiniband/ib_sma.c
deleted file mode 100644
index ff4cbbf6..00000000
--- a/gpxe/src/net/infiniband/ib_sma.c
+++ /dev/null
@@ -1,369 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/settings.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/ib_mi.h>
-#include <gpxe/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_get_hca_info ( ibdev, &node_info->sys_guid );
- memcpy ( &node_info->node_guid, &node_info->sys_guid,
- sizeof ( node_info->node_guid ) );
- memcpy ( &node_info->port_guid, &ibdev->gid.u.half[1],
- 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;
- struct ib_gid_half guid;
- char hostname[ sizeof ( node_desc->node_string ) ];
- int hostname_len;
- int rc;
-
- /* Fill in information */
- memset ( node_desc, 0, sizeof ( *node_desc ) );
- ib_get_hca_info ( ibdev, &guid );
- hostname_len = fetch_string_setting ( NULL, &hostname_setting,
- hostname, sizeof ( hostname ) );
- snprintf ( node_desc->node_string, sizeof ( node_desc->node_string ),
- "gPXE %s%s%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)",
- hostname, ( ( hostname_len >= 0 ) ? " " : "" ),
- guid.u.bytes[0], guid.u.bytes[1], guid.u.bytes[2],
- guid.u.bytes[3], guid.u.bytes[4], guid.u.bytes[5],
- guid.u.bytes[6], guid.u.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.u.half[1],
- 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.u.half[0], port_info->gid_prefix,
- sizeof ( ibdev->gid.u.half[0] ) );
- 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.u.half[0],
- 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/gpxe/src/net/infiniband/ib_smc.c b/gpxe/src/net/infiniband/ib_smc.c
deleted file mode 100644
index d308dd9d..00000000
--- a/gpxe/src/net/infiniband/ib_smc.c
+++ /dev/null
@@ -1,179 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_smc.h>
-
-/**
- * @file
- *
- * Infiniband Subnet Management Client
- *
- */
-
-/**
- * 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;
-
- /* 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 = htons ( IB_SMP_ATTR_PORT_INFO );
- mad->hdr.attr_mod = htonl ( ibdev->port );
-
- if ( ( rc = local_mad ( ibdev, 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;
-
- /* 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 = htons ( IB_SMP_ATTR_GUID_INFO );
-
- if ( ( rc = local_mad ( ibdev, 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;
-
- /* 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 = htons ( IB_SMP_ATTR_PKEY_TABLE );
-
- if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) {
- DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n",
- ibdev, strerror ( rc ) );
- return rc;
- }
- return 0;
-}
-
-/**
- * Get MAD parameters
- *
- * @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 ) {
- union ib_mad mad;
- 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;
-
- /* 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.u.half[0], port_info->gid_prefix,
- sizeof ( ibdev->gid.u.half[0] ) );
- 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.u.half[1], guid_info->guid[0],
- sizeof ( ibdev->gid.u.half[1] ) );
-
- /* 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 %08x:%08x:%08x:%08x\n", ibdev,
- htonl ( ibdev->gid.u.dwords[0] ),
- htonl ( ibdev->gid.u.dwords[1] ),
- htonl ( ibdev->gid.u.dwords[2] ),
- htonl ( ibdev->gid.u.dwords[3] ) );
-
- return 0;
-}
diff --git a/gpxe/src/net/infiniband/ib_srp.c b/gpxe/src/net/infiniband/ib_srp.c
deleted file mode 100644
index c156d3ae..00000000
--- a/gpxe/src/net/infiniband/ib_srp.c
+++ /dev/null
@@ -1,406 +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 <gpxe/srp.h>
-#include <gpxe/infiniband.h>
-#include <gpxe/ib_cmrc.h>
-#include <gpxe/ib_srp.h>
-
-/**
- * @file
- *
- * SCSI RDMA Protocol over Infiniband
- *
- */
-
-/* Disambiguate the various possible EINVALs */
-#define EINVAL_BYTE_STRING_LEN ( EINVAL | EUNIQ_01 )
-#define EINVAL_BYTE_STRING ( EINVAL | EUNIQ_02 )
-#define EINVAL_INTEGER ( EINVAL | EUNIQ_03 )
-#define EINVAL_RP_TOO_SHORT ( EINVAL | EUNIQ_04 )
-
-/** 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 {
- /** SCSI LUN */
- struct scsi_lun *lun;
- /** SRP port IDs */
- struct srp_port_ids *port_ids;
- /** IB SRP parameters */
- struct ib_srp_parameters *ib;
-};
-
-/**
- * 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 );
- char buf[3];
- char *buf_end;
-
- /* 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 */
- for ( ; size ; size--, rp_comp += 2, bytes++ ) {
- memcpy ( buf, rp_comp, 2 );
- buf[2] = '\0';
- *bytes = strtoul ( buf, &buf_end, 16 );
- if ( buf_end != &buf[2] )
- return -EINVAL_BYTE_STRING;
- }
- 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 literal component
- *
- * @v rp_comp Root path component string
- * @v rp IB SRP root path
- * @ret rc Return status code
- */
-static int ib_srp_parse_literal ( const char *rp_comp __unused,
- struct ib_srp_root_path *rp __unused ) {
- /* Ignore */
- return 0;
-}
-
-/**
- * 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->ib->sgid, &ibdev->gid, sizeof ( rp->ib->sgid ) );
-
- return ib_srp_parse_byte_string ( rp_comp, rp->ib->sgid.u.bytes,
- ( sizeof ( rp->ib->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 ) {
- struct ib_srp_initiator_port_id *port_id =
- ib_srp_initiator_port_id ( rp->port_ids );
-
- return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
- ( sizeof ( port_id->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 ) {
- struct ib_srp_initiator_port_id *port_id =
- ib_srp_initiator_port_id ( rp->port_ids );
-
- /* Default to the GUID portion of the source GID */
- memcpy ( &port_id->hca_guid, &rp->ib->sgid.u.half[1],
- sizeof ( port_id->hca_guid ) );
-
- return ib_srp_parse_byte_string ( rp_comp, port_id->hca_guid.u.bytes,
- ( sizeof ( port_id->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->ib->dgid.u.bytes,
- ( sizeof ( rp->ib->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->ib->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->ib->service_id.u.bytes,
- ( sizeof ( rp->ib->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 ) {
- struct ib_srp_target_port_id *port_id =
- ib_srp_target_port_id ( rp->port_ids );
-
- return ib_srp_parse_byte_string ( rp_comp, port_id->id_ext.u.bytes,
- ( sizeof ( port_id->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 ) {
- struct ib_srp_target_port_id *port_id =
- ib_srp_target_port_id ( rp->port_ids );
-
- return ib_srp_parse_byte_string ( rp_comp, port_id->ioc_guid.u.bytes,
- ( sizeof ( port_id->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_literal },
- { 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 srp SRP device
- * @v rp_string Root path
- * @ret rc Return status code
- */
-static int ib_srp_parse_root_path ( struct srp_device *srp,
- const char *rp_string ) {
- struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
- struct ib_srp_root_path rp = {
- .lun = &srp->lun,
- .port_ids = &srp->port_ids,
- .ib = ib_params,
- };
- 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 ) {
- DBGC ( srp, "SRP %p root path \"%s\" too "
- "short\n", srp, 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++ ) {
- if ( ( rc = ib_srp_rp_parser[i].parse ( rp_comp[i],
- &rp ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not parse \"%s\" in root "
- "path \"%s\": %s\n", srp, rp_comp[i],
- rp_string, strerror ( rc ) );
- return rc;
- }
- }
-
- return 0;
-}
-
-/**
- * Connect IB SRP session
- *
- * @v srp SRP device
- * @ret rc Return status code
- */
-static int ib_srp_connect ( struct srp_device *srp ) {
- struct ib_srp_parameters *ib_params = ib_srp_params ( srp );
- struct ib_device *ibdev;
- int rc;
-
- /* Identify Infiniband device */
- ibdev = find_ibdev ( &ib_params->sgid );
- if ( ! ibdev ) {
- DBGC ( srp, "SRP %p could not identify Infiniband device\n",
- srp );
- return -ENODEV;
- }
-
- /* Configure remaining SRP parameters */
- srp->memory_handle = ibdev->rdma_key;
-
- /* Open CMRC socket */
- if ( ( rc = ib_cmrc_open ( &srp->socket, ibdev, &ib_params->dgid,
- &ib_params->service_id ) ) != 0 ) {
- DBGC ( srp, "SRP %p could not open CMRC socket: %s\n",
- srp, strerror ( rc ) );
- return rc;
- }
-
- return 0;
-}
-
-/** IB SRP transport type */
-struct srp_transport_type ib_srp_transport = {
- .priv_len = sizeof ( struct ib_srp_parameters ),
- .parse_root_path = ib_srp_parse_root_path,
- .connect = ib_srp_connect,
-};
diff --git a/gpxe/src/net/iobpad.c b/gpxe/src/net/iobpad.c
deleted file mode 100644
index cbae221a..00000000
--- a/gpxe/src/net/iobpad.c
+++ /dev/null
@@ -1,68 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/**
- * @file
- *
- * I/O buffer padding
- *
- */
-
-#include <string.h>
-#include <gpxe/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/gpxe/src/net/ipv4.c b/gpxe/src/net/ipv4.c
deleted file mode 100644
index 4c1393f2..00000000
--- a/gpxe/src/net/ipv4.c
+++ /dev/null
@@ -1,635 +0,0 @@
-#include <string.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/list.h>
-#include <gpxe/in.h>
-#include <gpxe/arp.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/ip.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/settings.h>
-
-/** @file
- *
- * IPv4 protocol
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/* Unique IP datagram identification number */
-static uint16_t next_ident = 0;
-
-struct net_protocol ipv4_protocol;
-
-/** List of IPv4 miniroutes */
-struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
-
-/** List of fragment reassembly buffers */
-static LIST_HEAD ( frag_buffers );
-
-/**
- * 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;
-
- DBG ( "IPv4 add %s", inet_ntoa ( address ) );
- DBG ( "/%s ", inet_ntoa ( netmask ) );
- if ( gateway.s_addr )
- DBG ( "gw %s ", inet_ntoa ( gateway ) );
- DBG ( "via %s\n", netdev->name );
-
- /* Allocate and populate miniroute structure */
- miniroute = malloc ( sizeof ( *miniroute ) );
- if ( ! miniroute ) {
- DBG ( "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 ) {
-
- DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) );
- DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) );
- if ( miniroute->gateway.s_addr )
- DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) );
- DBG ( "via %s\n", miniroute->netdev->name );
-
- netdev_put ( miniroute->netdev );
- list_del ( &miniroute->list );
- free ( miniroute );
-}
-
-/**
- * Perform IPv4 routing
- *
- * @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 ( struct in_addr *dest ) {
- struct ipv4_miniroute *miniroute;
- int local;
- int has_gw;
-
- /* Never attempt to route the broadcast address */
- if ( dest->s_addr == INADDR_BROADCAST )
- return NULL;
-
- /* Find first usable route in routing table */
- list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
- if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) )
- continue;
- local = ( ( ( dest->s_addr ^ miniroute->address.s_addr )
- & miniroute->netmask.s_addr ) == 0 );
- has_gw = ( miniroute->gateway.s_addr );
- if ( local || has_gw ) {
- if ( ! local )
- *dest = miniroute->gateway;
- return miniroute;
- }
- }
-
- return NULL;
-}
-
-/**
- * Fragment reassembly counter timeout
- *
- * @v timer Retry timer
- * @v over If asserted, the timer is greater than @c MAX_TIMEOUT
- */
-static void ipv4_frag_expired ( struct retry_timer *timer __unused,
- int over ) {
- if ( over ) {
- DBG ( "Fragment reassembly timeout" );
- /* Free the fragment buffer */
- }
-}
-
-/**
- * Free fragment buffer
- *
- * @v fragbug Fragment buffer
- */
-static void free_fragbuf ( struct frag_buffer *fragbuf ) {
- free ( fragbuf );
-}
-
-/**
- * Fragment reassembler
- *
- * @v iobuf I/O buffer, fragment of the datagram
- * @ret frag_iob Reassembled packet, or NULL
- */
-static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) {
- struct iphdr *iphdr = iobuf->data;
- struct frag_buffer *fragbuf;
-
- /**
- * Check if the fragment belongs to any fragment series
- */
- list_for_each_entry ( fragbuf, &frag_buffers, list ) {
- if ( fragbuf->ident == iphdr->ident &&
- fragbuf->src.s_addr == iphdr->src.s_addr ) {
- /**
- * Check if the packet is the expected fragment
- *
- * The offset of the new packet must be equal to the
- * length of the data accumulated so far (the length of
- * the reassembled I/O buffer
- */
- if ( iob_len ( fragbuf->frag_iob ) ==
- ( iphdr->frags & IP_MASK_OFFSET ) ) {
- /**
- * Append the contents of the fragment to the
- * reassembled I/O buffer
- */
- iob_pull ( iobuf, sizeof ( *iphdr ) );
- memcpy ( iob_put ( fragbuf->frag_iob,
- iob_len ( iobuf ) ),
- iobuf->data, iob_len ( iobuf ) );
- free_iob ( iobuf );
-
- /** Check if the fragment series is over */
- if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) {
- iobuf = fragbuf->frag_iob;
- free_fragbuf ( fragbuf );
- return iobuf;
- }
-
- } else {
- /* Discard the fragment series */
- free_fragbuf ( fragbuf );
- free_iob ( iobuf );
- }
- return NULL;
- }
- }
-
- /** Check if the fragment is the first in the fragment series */
- if ( iphdr->frags & IP_MASK_MOREFRAGS &&
- ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) {
-
- /** Create a new fragment buffer */
- fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) );
- fragbuf->ident = iphdr->ident;
- fragbuf->src = iphdr->src;
-
- /* Set up the reassembly I/O buffer */
- fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE );
- iob_pull ( iobuf, sizeof ( *iphdr ) );
- memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ),
- iobuf->data, iob_len ( iobuf ) );
- free_iob ( iobuf );
-
- /* Set the reassembly timer */
- fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT;
- fragbuf->frag_timer.expired = ipv4_frag_expired;
- start_timer ( &fragbuf->frag_timer );
-
- /* Add the fragment buffer to the list of fragment buffers */
- list_add ( &fragbuf->list, &frag_buffers );
- }
-
- return NULL;
-}
-
-/**
- * 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 ) );
-}
-
-/**
- * Determine link-layer address
- *
- * @v dest IPv4 destination address
- * @v src IPv4 source address
- * @v netdev Network device
- * @v ll_dest Link-layer destination address buffer
- * @ret rc Return status code
- */
-static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src,
- struct net_device *netdev, uint8_t *ll_dest ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
-
- if ( dest.s_addr == INADDR_BROADCAST ) {
- /* Broadcast address */
- memcpy ( ll_dest, netdev->ll_broadcast,
- ll_protocol->ll_addr_len );
- return 0;
- } else if ( IN_MULTICAST ( ntohl ( dest.s_addr ) ) ) {
- return ll_protocol->mc_hash ( AF_INET, &dest, ll_dest );
- } else {
- /* Unicast address: resolve via ARP */
- return arp_resolve ( netdev, &ipv4_protocol, &dest,
- &src, ll_dest );
- }
-}
-
-/**
- * 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;
- uint8_t ll_dest[MAX_LL_ADDR_LEN];
- int rc;
-
- /* 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->ident = htons ( ++next_ident );
- 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 ) &&
- ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) &&
- ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) {
- iphdr->src = miniroute->address;
- netdev = miniroute->netdev;
- }
- if ( ! netdev ) {
- DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) );
- rc = -ENETUNREACH;
- goto err;
- }
-
- /* Determine link-layer destination address */
- if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netdev,
- ll_dest ) ) != 0 ) {
- DBG ( "IPv4 has no link-layer address for %s: %s\n",
- inet_ntoa ( next_hop ), strerror ( rc ) );
- goto err;
- }
-
- /* 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 */
- DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) );
- DBG ( "%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 ) );
-
- /* Hand off to link layer */
- if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest ) ) != 0 ) {
- DBG ( "IPv4 could not transmit packet via %s: %s\n",
- netdev->name, strerror ( rc ) );
- return rc;
- }
-
- return 0;
-
- err:
- free_iob ( iobuf );
- return rc;
-}
-
-/**
- * Process incoming packets
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
- * @v ll_source Link-layer destination source
- *
- * 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 __unused,
- const void *ll_source __unused ) {
- 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;
-
- /* Sanity check the IPv4 header */
- if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) {
- DBG ( "IPv4 packet too short at %zd bytes (min %zd bytes)\n",
- iob_len ( iobuf ), sizeof ( *iphdr ) );
- goto err;
- }
- if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) {
- DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen );
- goto err;
- }
- hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
- if ( hdrlen < sizeof ( *iphdr ) ) {
- DBG ( "IPv4 header too short at %zd bytes (min %zd bytes)\n",
- hdrlen, sizeof ( *iphdr ) );
- goto err;
- }
- if ( hdrlen > iob_len ( iobuf ) ) {
- DBG ( "IPv4 header too long at %zd bytes "
- "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) );
- goto err;
- }
- if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) {
- DBG ( "IPv4 checksum incorrect (is %04x including checksum "
- "field, should be 0000)\n", csum );
- goto err;
- }
- len = ntohs ( iphdr->len );
- if ( len < hdrlen ) {
- DBG ( "IPv4 length too short at %zd bytes "
- "(header is %zd bytes)\n", len, hdrlen );
- goto err;
- }
- if ( len > iob_len ( iobuf ) ) {
- DBG ( "IPv4 length too long at %zd bytes "
- "(packet is %zd bytes)\n", len, iob_len ( iobuf ) );
- goto err;
- }
-
- /* Print IPv4 header for debugging */
- DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
- DBG ( "%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 ) );
-
- /* Truncate packet to correct length, calculate pseudo-header
- * checksum and then strip off the IPv4 header.
- */
- iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
- pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
- iob_pull ( iobuf, hdrlen );
-
- /* Fragment reassembly */
- if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) ||
- ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) {
- /* Pass the fragment to ipv4_reassemble() which either
- * returns a fully reassembled I/O buffer or NULL.
- */
- iobuf = ipv4_reassemble ( iobuf );
- if ( ! iobuf )
- return 0;
- }
-
- /* Construct socket addresses 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;
- if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st,
- &dest.st, pshdr_csum ) ) != 0 ) {
- DBG ( "IPv4 received packet rejected by stack: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- return 0;
-
- err:
- 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;
- struct ipv4_miniroute *miniroute;
-
- list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) {
- if ( ( miniroute->netdev == netdev ) &&
- ( miniroute->address.s_addr == address->s_addr ) ) {
- /* Found matching address */
- return 0;
- }
- }
- return -ENOENT;
-}
-
-/**
- * Convert IPv4 address to dotted-quad notation
- *
- * @v in IP address
- * @ret string IP 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 * ) &in;
-
- sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] );
- return buf;
-}
-
-/**
- * Transcribe IP address
- *
- * @v net_addr IP address
- * @ret string IP address in dotted-quad notation
- *
- */
-static const char * ipv4_ntoa ( const void *net_addr ) {
- return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) );
-}
-
-/** 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,
- .tx = ipv4_tx,
-};
-
-/** IPv4 ARP protocol */
-struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = {
- .net_protocol = &ipv4_protocol,
- .check = ipv4_arp_check,
-};
-
-/******************************************************************************
- *
- * Settings
- *
- ******************************************************************************
- */
-
-/** IPv4 address setting */
-struct setting ip_setting __setting = {
- .name = "ip",
- .description = "IPv4 address",
- .tag = DHCP_EB_YIADDR,
- .type = &setting_type_ipv4,
-};
-
-/** IPv4 subnet mask setting */
-struct setting netmask_setting __setting = {
- .name = "netmask",
- .description = "IPv4 subnet mask",
- .tag = DHCP_SUBNET_MASK,
- .type = &setting_type_ipv4,
-};
-
-/** Default gateway setting */
-struct setting gateway_setting __setting = {
- .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_CLASSA ( ntohl ( address.s_addr ) ) ) {
- netmask.s_addr = htonl ( IN_CLASSA_NET );
- } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) {
- netmask.s_addr = htonl ( IN_CLASSB_NET );
- } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) {
- netmask.s_addr = htonl ( IN_CLASSC_NET );
- }
- }
- /* 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 ICMP */
-REQUIRE_OBJECT ( icmp );
diff --git a/gpxe/src/net/ipv6.c b/gpxe/src/net/ipv6.c
deleted file mode 100644
index f7308bb4..00000000
--- a/gpxe/src/net/ipv6.c
+++ /dev/null
@@ -1,381 +0,0 @@
-#include <errno.h>
-#include <stdint.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <byteswap.h>
-#include <gpxe/in.h>
-#include <gpxe/ip6.h>
-#include <gpxe/ndp.h>
-#include <gpxe/list.h>
-#include <gpxe/icmp6.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/socket.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/if_ether.h>
-
-struct net_protocol ipv6_protocol;
-
-/* Unspecified IP6 address */
-static struct in6_addr ip6_none = {
- .in6_u.u6_addr32 = { 0,0,0,0 }
-};
-
-/** An IPv6 routing table entry */
-struct ipv6_miniroute {
- /* List of miniroutes */
- struct list_head list;
-
- /* Network device */
- struct net_device *netdev;
-
- /* Destination prefix */
- struct in6_addr prefix;
- /* Prefix length */
- int prefix_len;
- /* IPv6 address of interface */
- struct in6_addr address;
- /* Gateway address */
- struct in6_addr gateway;
-};
-
-/** List of IPv6 miniroutes */
-static LIST_HEAD ( miniroutes );
-
-/**
- * Add IPv6 minirouting table entry
- *
- * @v netdev Network device
- * @v prefix Destination prefix
- * @v address Address of the interface
- * @v gateway Gateway address (or ::0 for no gateway)
- * @ret miniroute Routing table entry, or NULL
- */
-static struct ipv6_miniroute * __malloc
-add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix,
- int prefix_len, struct in6_addr address,
- struct in6_addr gateway ) {
- struct ipv6_miniroute *miniroute;
-
- miniroute = malloc ( sizeof ( *miniroute ) );
- if ( miniroute ) {
- /* Record routing information */
- miniroute->netdev = netdev_get ( netdev );
- miniroute->prefix = prefix;
- miniroute->prefix_len = prefix_len;
- miniroute->address = address;
- miniroute->gateway = gateway;
-
- /* Add miniroute to list of miniroutes */
- if ( !IP6_EQUAL ( gateway, ip6_none ) ) {
- list_add_tail ( &miniroute->list, &miniroutes );
- } else {
- list_add ( &miniroute->list, &miniroutes );
- }
- }
-
- return miniroute;
-}
-
-/**
- * Delete IPv6 minirouting table entry
- *
- * @v miniroute Routing table entry
- */
-static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) {
- netdev_put ( miniroute->netdev );
- list_del ( &miniroute->list );
- free ( miniroute );
-}
-
-/**
- * Add IPv6 interface
- *
- * @v netdev Network device
- * @v prefix Destination prefix
- * @v address Address of the interface
- * @v gateway Gateway address (or ::0 for no gateway)
- */
-int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix,
- int prefix_len, struct in6_addr address,
- struct in6_addr gateway ) {
- struct ipv6_miniroute *miniroute;
-
- /* Clear any existing address for this net device */
- del_ipv6_address ( netdev );
-
- /* Add new miniroute */
- miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address,
- gateway );
- if ( ! miniroute )
- return -ENOMEM;
-
- return 0;
-}
-
-/**
- * Remove IPv6 interface
- *
- * @v netdev Network device
- */
-void del_ipv6_address ( struct net_device *netdev ) {
- struct ipv6_miniroute *miniroute;
-
- list_for_each_entry ( miniroute, &miniroutes, list ) {
- if ( miniroute->netdev == netdev ) {
- del_ipv6_miniroute ( miniroute );
- break;
- }
- }
-}
-
-/**
- * Calculate TCPIP checksum
- *
- * @v iobuf I/O buffer
- * @v tcpip TCP/IP protocol
- *
- * This function constructs the pseudo header and completes the checksum in the
- * upper layer header.
- */
-static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) {
- struct ip6_header *ip6hdr = iobuf->data;
- struct ipv6_pseudo_header pshdr;
-
- /* Calculate pseudo header */
- memset ( &pshdr, 0, sizeof ( pshdr ) );
- pshdr.src = ip6hdr->src;
- pshdr.dest = ip6hdr->dest;
- pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
- pshdr.nxt_hdr = ip6hdr->nxt_hdr;
-
- /* Update checksum value */
- return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) );
-}
-
-/**
- * Dump IP6 header for debugging
- *
- * ip6hdr IPv6 header
- */
-void ipv6_dump ( struct ip6_header *ip6hdr ) {
- DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr,
- inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ),
- ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) );
-}
-
-/**
- * Transmit IP6 packet
- *
- * iobuf I/O buffer
- * tcpip TCP/IP protocol
- * st_dest Destination socket address
- *
- * This function prepends the IPv6 headers to the payload an transmits it.
- */
-static int ipv6_tx ( struct io_buffer *iobuf,
- struct tcpip_protocol *tcpip,
- struct sockaddr_tcpip *st_src __unused,
- struct sockaddr_tcpip *st_dest,
- struct net_device *netdev,
- uint16_t *trans_csum ) {
- struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest;
- struct in6_addr next_hop;
- struct ipv6_miniroute *miniroute;
- uint8_t ll_dest_buf[MAX_LL_ADDR_LEN];
- const uint8_t *ll_dest = ll_dest_buf;
- int rc;
-
- /* Construct the IPv6 packet */
- struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) );
- memset ( ip6hdr, 0, sizeof ( *ip6hdr) );
- ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION;
- ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) );
- ip6hdr->nxt_hdr = tcpip->tcpip_proto;
- ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255
-
- /* Determine the next hop address and interface
- *
- * TODO: Implement the routing table.
- */
- next_hop = dest->sin6_addr;
- list_for_each_entry ( miniroute, &miniroutes, list ) {
- if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix,
- miniroute->prefix_len ) == 0 ) ||
- ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) {
- netdev = miniroute->netdev;
- ip6hdr->src = miniroute->address;
- if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) {
- next_hop = miniroute->gateway;
- }
- break;
- }
- }
- /* No network interface identified */
- if ( !netdev ) {
- DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) );
- rc = -ENETUNREACH;
- goto err;
- }
-
- /* Complete the transport layer checksum */
- if ( trans_csum )
- *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum );
-
- /* Print IPv6 header */
- ipv6_dump ( ip6hdr );
-
- /* Resolve link layer address */
- if ( next_hop.in6_u.u6_addr8[0] == 0xff ) {
- ll_dest_buf[0] = 0x33;
- ll_dest_buf[1] = 0x33;
- ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12];
- ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13];
- ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14];
- ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15];
- } else {
- /* Unicast address needs to be resolved by NDP */
- if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src,
- ll_dest_buf ) ) != 0 ) {
- DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) );
- goto err;
- }
- }
-
- /* Transmit packet */
- return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest );
-
- err:
- free_iob ( iobuf );
- return rc;
-}
-
-/**
- * Process next IP6 header
- *
- * @v iobuf I/O buffer
- * @v nxt_hdr Next header number
- * @v src Source socket address
- * @v dest Destination socket address
- *
- * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers
- */
-static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr,
- struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) {
- switch ( nxt_hdr ) {
- case IP6_HOPBYHOP:
- case IP6_ROUTING:
- case IP6_FRAGMENT:
- case IP6_AUTHENTICATION:
- case IP6_DEST_OPTS:
- case IP6_ESP:
- DBG ( "Function not implemented for header %d\n", nxt_hdr );
- return -ENOSYS;
- case IP6_ICMP6:
- break;
- case IP6_NO_HEADER:
- DBG ( "No next header\n" );
- return 0;
- }
- /* Next header is not a IPv6 extension header */
- return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ );
-}
-
-/**
- * Process incoming IP6 packets
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
- * @v ll_source Link-layer source address
- *
- * This function processes a IPv6 packet
- */
-static int ipv6_rx ( struct io_buffer *iobuf,
- __unused struct net_device *netdev,
- __unused const void *ll_source ) {
-
- struct ip6_header *ip6hdr = iobuf->data;
- union {
- struct sockaddr_in6 sin6;
- struct sockaddr_tcpip st;
- } src, dest;
-
- /* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) {
- DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
- goto drop;
- }
-
- /* TODO: Verify checksum */
-
- /* Print IP6 header for debugging */
- ipv6_dump ( ip6hdr );
-
- /* Check header version */
- if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) {
- DBG ( "Invalid protocol version\n" );
- goto drop;
- }
-
- /* Check the payload length */
- if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) {
- DBG ( "Inconsistent packet length (%d bytes)\n",
- ip6hdr->payload_len );
- goto drop;
- }
-
- /* Ignore the traffic class and flow control values */
-
- /* Construct socket address */
- memset ( &src, 0, sizeof ( src ) );
- src.sin6.sin_family = AF_INET6;
- src.sin6.sin6_addr = ip6hdr->src;
- memset ( &dest, 0, sizeof ( dest ) );
- dest.sin6.sin_family = AF_INET6;
- dest.sin6.sin6_addr = ip6hdr->dest;
-
- /* Strip header */
- iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) -
- sizeof ( *ip6hdr ) );
- iob_pull ( iobuf, sizeof ( *ip6hdr ) );
-
- /* Send it to the transport layer */
- return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st );
-
- drop:
- DBG ( "Packet dropped\n" );
- free_iob ( iobuf );
- return -1;
-}
-
-/**
- * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
- */
-char * inet6_ntoa ( struct in6_addr in6 ) {
- static char buf[40];
- uint16_t *bytes = ( uint16_t* ) &in6;
- sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2],
- bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] );
- return buf;
-}
-
-static const char * ipv6_ntoa ( const void *net_addr ) {
- return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) );
-}
-
-/** 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,
- .tx = ipv6_tx,
-};
diff --git a/gpxe/src/net/mii.c b/gpxe/src/net/mii.c
deleted file mode 100644
index 0de64428..00000000
--- a/gpxe/src/net/mii.c
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-
- mii.c: MII interface library
-
- Ported to gPXE by Daniel Verkamp <daniel@drv.nu>
- from Linux drivers/net/mii.c
-
- Maintained by Jeff Garzik <jgarzik@pobox.com>
- Copyright 2001,2002 Jeff Garzik
-
- Various code came from myson803.c and other files by
- Donald Becker. Copyright:
-
- Written 1998-2002 by Donald Becker.
-
- This software may be used and distributed according
- to the terms of the GNU General Public License (GPL),
- incorporated herein by reference. Drivers based on
- or derived from this code fall under the GPL and must
- retain the authorship, copyright and license notice.
- This file is not a complete program and may only be
- used when the entire operating system is licensed
- under the GPL.
-
- The author may be reached as becker@scyld.com, or C/O
- Scyld Computing Corporation
- 410 Severn Ave., Suite 210
- Annapolis MD 21403
-
-*/
-
-#include <mii.h>
-
-/**
- * mii_link_ok - is link status up/ok
- * @mii: the MII interface
- *
- * Returns 1 if the MII reports link status up/ok, 0 otherwise.
- */
-int
-mii_link_ok ( struct mii_if_info *mii )
-{
- /* first, a dummy read, needed to latch some MII phys */
- mii->mdio_read ( mii->dev, mii->phy_id, MII_BMSR );
- if ( mii->mdio_read ( mii->dev, mii->phy_id, MII_BMSR ) & BMSR_LSTATUS )
- return 1;
- return 0;
-}
-
-/**
- * mii_check_link - check MII link status
- * @mii: MII interface
- *
- * If the link status changed (previous != current), call
- * netif_carrier_on() if current link status is Up or call
- * netif_carrier_off() if current link status is Down.
- */
-void
-mii_check_link ( struct mii_if_info *mii )
-{
- int cur_link = mii_link_ok ( mii );
- int prev_link = netdev_link_ok ( mii->dev );
-
- if ( cur_link && !prev_link )
- netdev_link_up ( mii->dev );
- else if (prev_link && !cur_link)
- netdev_link_down ( mii->dev );
-}
-
-
-/**
- * mii_check_media - check the MII interface for a duplex change
- * @mii: the MII interface
- * @ok_to_print: OK to print link up/down messages
- * @init_media: OK to save duplex mode in @mii
- *
- * Returns 1 if the duplex mode changed, 0 if not.
- * If the media type is forced, always returns 0.
- */
-unsigned int
-mii_check_media ( struct mii_if_info *mii,
- unsigned int ok_to_print,
- unsigned int init_media )
-{
- unsigned int old_carrier, new_carrier;
- int advertise, lpa, media, duplex;
- int lpa2 = 0;
-
- /* if forced media, go no further */
- if (mii->force_media)
- return 0; /* duplex did not change */
-
- /* check current and old link status */
- old_carrier = netdev_link_ok ( mii->dev ) ? 1 : 0;
- new_carrier = (unsigned int) mii_link_ok ( mii );
-
- /* if carrier state did not change, this is a "bounce",
- * just exit as everything is already set correctly
- */
- if ( ( ! init_media ) && ( old_carrier == new_carrier ) )
- return 0; /* duplex did not change */
-
- /* no carrier, nothing much to do */
- if ( ! new_carrier ) {
- netdev_link_down ( mii->dev );
- if ( ok_to_print )
- DBG ( "%s: link down\n", mii->dev->name);
- return 0; /* duplex did not change */
- }
-
- /*
- * we have carrier, see who's on the other end
- */
- netdev_link_up ( mii->dev );
-
- /* get MII advertise and LPA values */
- if ( ( ! init_media ) && ( mii->advertising ) ) {
- advertise = mii->advertising;
- } else {
- advertise = mii->mdio_read ( mii->dev, mii->phy_id, MII_ADVERTISE );
- mii->advertising = advertise;
- }
- lpa = mii->mdio_read ( mii->dev, mii->phy_id, MII_LPA );
- if ( mii->supports_gmii )
- lpa2 = mii->mdio_read ( mii->dev, mii->phy_id, MII_STAT1000 );
-
- /* figure out media and duplex from advertise and LPA values */
- media = mii_nway_result ( lpa & advertise );
- duplex = ( media & ADVERTISE_FULL ) ? 1 : 0;
- if ( lpa2 & LPA_1000FULL )
- duplex = 1;
-
- if ( ok_to_print )
- DBG ( "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n",
- mii->dev->name,
- lpa2 & ( LPA_1000FULL | LPA_1000HALF ) ? "1000" :
- media & ( ADVERTISE_100FULL | ADVERTISE_100HALF ) ? "100" : "10",
- duplex ? "full" : "half",
- lpa);
-
- if ( ( init_media ) || ( mii->full_duplex != duplex ) ) {
- mii->full_duplex = duplex;
- return 1; /* duplex changed */
- }
-
- return 0; /* duplex did not change */
-}
diff --git a/gpxe/src/net/ndp.c b/gpxe/src/net/ndp.c
deleted file mode 100644
index 8bea8b32..00000000
--- a/gpxe/src/net/ndp.c
+++ /dev/null
@@ -1,180 +0,0 @@
-#include <stdint.h>
-#include <string.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/ndp.h>
-#include <gpxe/icmp6.h>
-#include <gpxe/ip6.h>
-#include <gpxe/netdevice.h>
-
-/** @file
- *
- * Neighbour Discovery Protocol
- *
- * This file implements address resolution as specified by the neighbour
- * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol
- * family.
- */
-
-/* A neighbour entry */
-struct ndp_entry {
- /** Target IP6 address */
- struct in6_addr in6;
- /** Link layer protocol */
- struct ll_protocol *ll_protocol;
- /** Link-layer address */
- uint8_t ll_addr[MAX_LL_ADDR_LEN];
- /** State of the neighbour entry */
- int state;
-};
-
-/** Number of entries in the neighbour cache table */
-#define NUM_NDP_ENTRIES 4
-
-/** The neighbour cache table */
-static struct ndp_entry ndp_table[NUM_NDP_ENTRIES];
-#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES]
-
-static unsigned int next_new_ndp_entry = 0;
-
-/**
- * Find entry in the neighbour cache
- *
- * @v in6 IP6 address
- */
-static struct ndp_entry *
-ndp_find_entry ( struct in6_addr *in6 ) {
- struct ndp_entry *ndp;
-
- for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) {
- if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) &&
- ( ndp->state != NDP_STATE_INVALID ) ) {
- return ndp;
- }
- }
- return NULL;
-}
-
-/**
- * Add NDP entry
- *
- * @v netdev Network device
- * @v in6 IP6 address
- * @v ll_addr Link-layer address
- * @v state State of the entry - one of the NDP_STATE_XXX values
- */
-static void
-add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6,
- void *ll_addr, int state ) {
- struct ndp_entry *ndp;
- ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES];
-
- /* Fill up entry */
- ndp->ll_protocol = netdev->ll_protocol;
- memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) );
- if ( ll_addr ) {
- memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len );
- } else {
- memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len );
- }
- ndp->state = state;
- DBG ( "New neighbour cache entry: IP6 %s => %s %s\n",
- inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name,
- netdev->ll_protocol->ntoa ( ndp->ll_addr ) );
-}
-
-/**
- * Resolve the link-layer address
- *
- * @v netdev Network device
- * @v dest Destination address
- * @v src Source address
- * @ret dest_ll_addr Destination link-layer address or NULL
- * @ret rc Status
- *
- * This function looks up the neighbour cache for an entry corresponding to the
- * destination address. If it finds a valid entry, it fills up dest_ll_addr and
- * returns 0. Otherwise it sends a neighbour solicitation to the solicited
- * multicast address.
- */
-int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest,
- struct in6_addr *src, void *dest_ll_addr ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- struct ndp_entry *ndp;
- int rc;
-
- ndp = ndp_find_entry ( dest );
- /* Check if the entry is valid */
- if ( ndp && ndp->state == NDP_STATE_REACHABLE ) {
- DBG ( "Neighbour cache hit: IP6 %s => %s %s\n",
- inet6_ntoa ( *dest ), ll_protocol->name,
- ll_protocol->ntoa ( ndp->ll_addr ) );
- memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len );
- return 0;
- }
-
- /* Check if the entry was already created */
- if ( ndp ) {
- DBG ( "Awaiting neighbour advertisement\n" );
- /* For test */
-// ndp->state = NDP_STATE_REACHABLE;
-// memcpy ( ndp->ll_addr, netdev->ll_addr, 6 );
-// assert ( ndp->ll_protocol->ll_addr_len == 6 );
-// icmp6_test_nadvert ( netdev, dest, ndp->ll_addr );
-// assert ( ndp->state == NDP_STATE_REACHABLE );
- /* Take it out till here */
- return -ENOENT;
- }
- DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) );
-
- /* Add entry in the neighbour cache */
- add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE );
-
- /* Send neighbour solicitation */
- if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) {
- return rc;
- }
- return -ENOENT;
-}
-
-/**
- * Process neighbour advertisement
- *
- * @v iobuf I/O buffer
- * @v st_src Source address
- * @v st_dest Destination address
- */
-int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src __unused,
- struct sockaddr_tcpip *st_dest __unused ) {
- struct neighbour_advert *nadvert = iobuf->data;
- struct ndp_entry *ndp;
-
- /* Sanity check */
- if ( iob_len ( iobuf ) < sizeof ( *nadvert ) ) {
- DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) );
- return -EINVAL;
- }
-
- assert ( nadvert->code == 0 );
- assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED );
- assert ( nadvert->opt_type == 2 );
-
- /* Update the neighbour cache, if entry is present */
- ndp = ndp_find_entry ( &nadvert->target );
- if ( ndp ) {
-
- assert ( nadvert->opt_len ==
- ( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) );
-
- if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) {
- memcpy ( ndp->ll_addr, nadvert->opt_ll_addr,
- ndp->ll_protocol->ll_addr_len );
- ndp->state = NDP_STATE_REACHABLE;
- return 0;
- }
- }
- DBG ( "Unsolicited advertisement (dropping packet)\n" );
- return 0;
-}
diff --git a/gpxe/src/net/netdev_settings.c b/gpxe/src/net/netdev_settings.c
deleted file mode 100644
index d814193b..00000000
--- a/gpxe/src/net/netdev_settings.c
+++ /dev/null
@@ -1,120 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <string.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/settings.h>
-#include <gpxe/device.h>
-#include <gpxe/netdevice.h>
-
-/** @file
- *
- * Network device configuration settings
- *
- */
-
-/** Network device named settings */
-struct setting mac_setting __setting = {
- .name = "mac",
- .description = "MAC address",
- .type = &setting_type_hex,
-};
-struct setting busid_setting __setting = {
- .name = "busid",
- .description = "Bus ID",
- .type = &setting_type_hex,
-};
-
-/**
- * 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, struct setting *setting,
- const void *data, size_t len ) {
- struct net_device *netdev = container_of ( settings, struct net_device,
- settings.settings );
-
- if ( setting_cmp ( setting, &mac_setting ) == 0 ) {
- if ( len != netdev->ll_protocol->ll_addr_len )
- return -EINVAL;
- memcpy ( netdev->ll_addr, data, len );
- return 0;
- }
-
- 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 Setting data, or NULL to clear setting
- * @v len Length of setting data
- * @ret rc Return status code
- */
-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 device_description *desc = &netdev->dev->desc;
- struct dhcp_netdev_desc dhcp_desc;
-
- if ( setting_cmp ( setting, &mac_setting ) == 0 ) {
- 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;
- }
- if ( setting_cmp ( setting, &busid_setting ) == 0 ) {
- 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 );
- }
-
- 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,
-};
diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c
deleted file mode 100644
index ee0d0b72..00000000
--- a/gpxe/src/net/netdevice.c
+++ /dev/null
@@ -1,633 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <byteswap.h>
-#include <string.h>
-#include <errno.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/tables.h>
-#include <gpxe/process.h>
-#include <gpxe/init.h>
-#include <gpxe/device.h>
-#include <gpxe/errortab.h>
-#include <gpxe/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 );
-
-/** Default link status code */
-#define EUNKNOWN_LINK_STATUS EINPROGRESS
-
-/** Human-readable message for the default link status */
-struct errortab netdev_errors[] __errortab = {
- { EUNKNOWN_LINK_STATUS, "Unknown" },
-};
-
-/**
- * Mark network device as having link down
- *
- * @v netdev Network device
- */
-void netdev_link_down ( struct net_device *netdev ) {
-
- switch ( netdev->link_rc ) {
- case 0:
- case -EUNKNOWN_LINK_STATUS:
- netdev->link_rc = -ENOTCONN;
- break;
- default:
- /* Avoid clobbering a more detailed link status code,
- * if one is already set.
- */
- break;
- }
-}
-
-/**
- * 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;
-
- DBGC ( netdev, "NETDEV %p transmitting %p (%p+%zx)\n",
- netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
-
- list_add_tail ( &iobuf->list, &netdev->tx_queue );
-
- if ( ! ( netdev->state & NETDEV_OPEN ) ) {
- rc = -ENETUNREACH;
- goto err;
- }
-
- if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
- goto err;
-
- return 0;
-
- err:
- netdev_tx_complete_err ( netdev, iobuf, rc );
- return rc;
-}
-
-/**
- * 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 ) {
-
- /* Update statistics counter */
- netdev_record_stat ( &netdev->tx_stats, rc );
- if ( rc == 0 ) {
- DBGC ( netdev, "NETDEV %p transmission %p complete\n",
- netdev, iobuf );
- } else {
- DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n",
- netdev, iobuf, strerror ( rc ) );
- }
-
- /* Catch data corruption as early as possible */
- assert ( iobuf->list.next != NULL );
- assert ( iobuf->list.prev != NULL );
-
- /* Dequeue and free I/O buffer */
- list_del ( &iobuf->list );
- free_iob ( 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;
-
- list_for_each_entry ( iobuf, &netdev->tx_queue, list ) {
- netdev_tx_complete_err ( netdev, iobuf, rc );
- return;
- }
-}
-
-/**
- * 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 */
- while ( ! list_empty ( &netdev->tx_queue ) ) {
- netdev_tx_complete_next_err ( netdev, -ECANCELED );
- }
-}
-
-/**
- * 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 ) {
-
- DBGC ( netdev, "NETDEV %p received %p (%p+%zx)\n",
- netdev, iobuf, iobuf->data, iob_len ( iobuf ) );
-
- /* 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 %p failed to receive %p: %s\n",
- netdev, 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->state & NETDEV_OPEN )
- 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;
-
- list_for_each_entry ( iobuf, &netdev->rx_queue, list ) {
- list_del ( &iobuf->list );
- return iobuf;
- }
- return NULL;
-}
-
-/**
- * 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 );
- }
-}
-
-/**
- * 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 );
-
- netdev_tx_flush ( netdev );
- netdev_rx_flush ( netdev );
- clear_settings ( netdev_settings ( netdev ) );
- free ( netdev );
-}
-
-/**
- * Allocate network device
- *
- * @v priv_size Size 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_size ) {
- struct net_device *netdev;
- size_t total_len;
-
- total_len = ( sizeof ( *netdev ) + priv_size );
- netdev = zalloc ( total_len );
- if ( netdev ) {
- netdev->refcnt.free = free_netdev;
- netdev->link_rc = -EUNKNOWN_LINK_STATUS;
- INIT_LIST_HEAD ( &netdev->tx_queue );
- INIT_LIST_HEAD ( &netdev->rx_queue );
- netdev_settings_init ( netdev );
- netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) );
- }
- 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 ) {
- static unsigned int ifindex = 0;
- int rc;
-
- /* Create device name */
- snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
- ifindex++ );
-
- /* Set initial link-layer address */
- netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
-
- /* Register per-netdev configuration settings */
- if ( ( rc = register_settings ( netdev_settings ( netdev ),
- NULL ) ) != 0 ) {
- DBGC ( netdev, "NETDEV %p could not register settings: %s\n",
- netdev, strerror ( rc ) );
- return rc;
- }
-
- /* Add to device list */
- netdev_get ( netdev );
- list_add_tail ( &netdev->list, &net_devices );
- DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
- netdev, netdev->name, netdev->dev->name,
- netdev_addr ( netdev ) );
-
- return 0;
-}
-
-/**
- * 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 %p opening\n", netdev );
-
- /* Open the device */
- if ( ( rc = netdev->op->open ( netdev ) ) != 0 )
- return rc;
-
- /* Mark as opened */
- netdev->state |= NETDEV_OPEN;
-
- /* Add to head of open devices list */
- list_add ( &netdev->open_list, &open_net_devices );
-
- return 0;
-}
-
-/**
- * Close network device
- *
- * @v netdev Network device
- */
-void netdev_close ( struct net_device *netdev ) {
-
- /* Do nothing if device is already closed */
- if ( ! ( netdev->state & NETDEV_OPEN ) )
- return;
-
- DBGC ( netdev, "NETDEV %p closing\n", netdev );
-
- /* Close the device */
- netdev->op->close ( netdev );
-
- /* Flush TX and RX queues */
- netdev_tx_flush ( netdev );
- netdev_rx_flush ( netdev );
-
- /* Mark as closed */
- netdev->state &= ~NETDEV_OPEN;
-
- /* Remove from open devices list */
- list_del ( &netdev->open_list );
-}
-
-/**
- * Unregister network device
- *
- * @v netdev Network device
- *
- * Removes the network device from the list of network devices.
- */
-void unregister_netdev ( struct net_device *netdev ) {
-
- /* Ensure device is closed */
- netdev_close ( netdev );
-
- /* Unregister per-netdev configuration settings */
- unregister_settings ( netdev_settings ( netdev ) );
-
- /* Remove from device list */
- list_del ( &netdev->list );
- netdev_put ( netdev );
- DBGC ( netdev, "NETDEV %p unregistered\n", netdev );
-}
-
-/** Enable or disable interrupts
- *
- * @v netdev Network device
- * @v enable Interrupts should be enabled
- */
-void netdev_irq ( struct net_device *netdev, int enable ) {
- netdev->op->irq ( netdev, enable );
-}
-
-/**
- * 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;
-
- list_for_each_entry ( netdev, &net_devices, list ) {
- if ( strcmp ( netdev->name, name ) == 0 )
- 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 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;
-
- list_for_each_entry ( netdev, &open_net_devices, open_list ) {
- assert ( netdev->state & NETDEV_OPEN );
- return netdev;
- }
-
- return NULL;
-}
-
-/**
- * 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
- * @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 ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- int rc;
-
- /* Force a poll on the netdevice to (potentially) clear any
- * backed-up TX completions. This is needed on some network
- * devices to avoid excessive losses due to small TX ring
- * sizes.
- */
- netdev_poll ( netdev );
-
- /* Add link-layer header */
- if ( ( rc = ll_protocol->push ( netdev, iobuf, ll_dest, netdev->ll_addr,
- net_protocol->net_proto ) ) != 0 ) {
- free_iob ( iobuf );
- 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_source Source link-layer address
- * @ret rc Return status code
- */
-int net_rx ( struct io_buffer *iobuf, struct net_device *netdev,
- uint16_t net_proto, const void *ll_source ) {
- 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_source );
- }
-
- DBGC ( netdev, "NETDEV %p unknown network protocol %04x\n",
- netdev, ntohs ( net_proto ) );
- free_iob ( iobuf );
- return 0;
-}
-
-/**
- * Single-step the network stack
- *
- * @v process Network stack process
- *
- * This polls all interfaces for received packets, and processes
- * packets from the RX queue.
- */
-static void net_step ( struct process *process __unused ) {
- 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;
- int rc;
-
- /* Poll and process each network device */
- list_for_each_entry ( netdev, &net_devices, list ) {
-
- /* Poll for new packets */
- netdev_poll ( netdev );
-
- /* Process at most one received packet. Give priority
- * to getting packets out of the NIC over processing
- * the received packets, because we advertise a window
- * that assumes that we can receive packets from the
- * NIC faster than they arrive.
- */
- if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) {
-
- DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n",
- netdev, iobuf, iobuf->data,
- iob_len ( iobuf ) );
-
- /* Remove link-layer header */
- ll_protocol = netdev->ll_protocol;
- if ( ( rc = ll_protocol->pull ( netdev, iobuf,
- &ll_dest, &ll_source,
- &net_proto ) ) != 0 ) {
- free_iob ( iobuf );
- continue;
- }
-
- net_rx ( iobuf, netdev, net_proto, ll_source );
- }
- }
-}
-
-/** Networking stack process */
-struct process net_process __permanent_process = {
- .list = LIST_HEAD_INIT ( net_process.list ),
- .step = net_step,
-};
diff --git a/gpxe/src/net/nullnet.c b/gpxe/src/net/nullnet.c
deleted file mode 100644
index 381f02a6..00000000
--- a/gpxe/src/net/nullnet.c
+++ /dev/null
@@ -1,60 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <errno.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/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/gpxe/src/net/rarp.c b/gpxe/src/net/rarp.c
deleted file mode 100644
index 1d0dd961..00000000
--- a/gpxe/src/net/rarp.c
+++ /dev/null
@@ -1,70 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <byteswap.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/rarp.h>
-
-/** @file
- *
- * Reverse Address Resolution Protocol
- *
- */
-
-/**
- * Process incoming ARP packets
- *
- * @v iobuf I/O buffer
- * @v netdev Network device
- * @v ll_source Link-layer source address
- * @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_source __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/gpxe/src/net/retry.c b/gpxe/src/net/retry.c
deleted file mode 100644
index 40f656f2..00000000
--- a/gpxe/src/net/retry.c
+++ /dev/null
@@ -1,192 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stddef.h>
-#include <gpxe/timer.h>
-#include <gpxe/list.h>
-#include <gpxe/process.h>
-#include <gpxe/init.h>
-#include <gpxe/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
- *
- * @v timer Retry timer
- *
- * This starts the timer running with the current 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 ) {
- if ( ! timer->running )
- list_add ( &timer->list, &timers );
- timer->start = currticks();
- timer->running = 1;
-
- /* 0 means "use default timeout" */
- if ( timer->min_timeout == 0 )
- timer->min_timeout = DEFAULT_MIN_TIMEOUT;
- /* We must never be less than MIN_TIMEOUT under any circumstances */
- if ( timer->min_timeout < MIN_TIMEOUT )
- timer->min_timeout = MIN_TIMEOUT;
- /* Honor user-specified minimum timeout */
- if ( timer->timeout < timer->min_timeout )
- timer->timeout = timer->min_timeout;
-
- DBG2 ( "Timer %p started at time %ld (expires at %ld)\n",
- timer, timer->start, ( timer->start + timer->timeout ) );
-}
-
-/**
- * Start timer with a specified fixed timeout
- *
- * @v timer Retry timer
- * @v timeout Timeout, in ticks
- */
-void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) {
- start_timer ( timer );
- timer->timeout = timeout;
- DBG2 ( "Timer %p expiry time changed to %ld\n",
- timer, ( timer->start + 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;
- DBG2 ( "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 ) {
- DBG ( "Timer %p timeout updated to %ld\n",
- timer, timer->timeout );
- }
- }
-}
-
-/**
- * Handle expired timer
- *
- * @v timer Retry timer
- */
-static void timer_expired ( struct retry_timer *timer ) {
- int fail;
-
- /* Stop timer without performing RTT calculations */
- DBG2 ( "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 ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */
- timer->max_timeout = DEFAULT_MAX_TIMEOUT;
- if ( ( fail = ( timer->timeout > timer->max_timeout ) ) )
- timer->timeout = timer->max_timeout;
- DBG ( "Timer %p timeout backed off to %ld\n",
- timer, timer->timeout );
-
- /* Call expiry callback */
- timer->expired ( timer, fail );
-}
-
-/**
- * Single-step the retry timer list
- *
- * @v process Retry timer process
- */
-static void retry_step ( struct process *process __unused ) {
- struct retry_timer *timer;
- struct retry_timer *tmp;
- unsigned long now = currticks();
- unsigned long used;
-
- list_for_each_entry_safe ( timer, tmp, &timers, list ) {
- used = ( now - timer->start );
- if ( used >= timer->timeout )
- timer_expired ( timer );
- }
-}
-
-/** Retry timer process */
-struct process retry_process __permanent_process = {
- .list = LIST_HEAD_INIT ( retry_process.list ),
- .step = retry_step,
-};
diff --git a/gpxe/src/net/tcp.c b/gpxe/src/net/tcp.c
deleted file mode 100644
index a0619622..00000000
--- a/gpxe/src/net/tcp.c
+++ /dev/null
@@ -1,1156 +0,0 @@
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/timer.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/malloc.h>
-#include <gpxe/retry.h>
-#include <gpxe/refcnt.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/uri.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/tcp.h>
-
-/** @file
- *
- * TCP protocol
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/** A TCP connection */
-struct tcp_connection {
- /** Reference counter */
- struct refcnt refcnt;
- /** List of TCP connections */
- struct list_head list;
-
- /** Data transfer interface */
- struct xfer_interface xfer;
- /** Data transfer interface closed flag */
- int xfer_closed;
-
- /** Remote socket address */
- struct sockaddr_tcpip peer;
- /** Local port, in network byte order */
- unsigned int local_port;
-
- /** 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;
- /** Most recent received timestamp
- *
- * Equivalent to TS.Recent in RFC 1323 terminology.
- */
- uint32_t ts_recent;
- /** Timestamps enabled */
- int timestamps;
-
- /** Transmit queue */
- struct list_head queue;
- /** Retransmission timer */
- struct retry_timer timer;
-};
-
-/**
- * List of registered TCP connections
- */
-static LIST_HEAD ( tcp_conns );
-
-/* Forward declarations */
-static struct xfer_interface_operations tcp_xfer_operations;
-static void tcp_expired ( struct retry_timer *timer, int over );
-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
- *
- ***************************************************************************
- */
-
-/**
- * Bind TCP connection to local port
- *
- * @v tcp TCP connection
- * @v port Local port number, in network-endian order
- * @ret rc Return status code
- *
- * If the port is 0, the connection is assigned an available port
- * between 1024 and 65535.
- */
-static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) {
- struct tcp_connection *existing;
- static uint16_t try_port = 1023;
-
- /* If no port specified, find the first available port */
- if ( ! port ) {
- while ( try_port ) {
- try_port++;
- if ( try_port < 1024 )
- continue;
- if ( tcp_bind ( tcp, htons ( try_port ) ) == 0 )
- return 0;
- }
- DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp );
- return -EADDRINUSE;
- }
-
- /* Attempt bind to local port */
- list_for_each_entry ( existing, &tcp_conns, list ) {
- if ( existing->local_port == port ) {
- DBGC ( tcp, "TCP %p could not bind: port %d in use\n",
- tcp, ntohs ( port ) );
- return -EADDRINUSE;
- }
- }
- tcp->local_port = port;
-
- DBGC ( tcp, "TCP %p bound to port %d\n", tcp, ntohs ( port ) );
- return 0;
-}
-
-/**
- * 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 xfer_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;
- unsigned int bind_port;
- int rc;
-
- /* Allocate and initialise structure */
- tcp = zalloc ( sizeof ( *tcp ) );
- if ( ! tcp )
- return -ENOMEM;
- DBGC ( tcp, "TCP %p allocated\n", tcp );
- xfer_init ( &tcp->xfer, &tcp_xfer_operations, &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->queue );
- tcp->timer.expired = tcp_expired;
- memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) );
-
- /* Bind to local port */
- bind_port = ( st_local ? st_local->st_port : 0 );
- if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 )
- goto err;
-
- /* Start timer to initiate SYN */
- start_timer_nodelay ( &tcp->timer );
-
- /* Attach parent interface, transfer reference to connection
- * list and return
- */
- xfer_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 */
- xfer_nullify ( &tcp->xfer );
- xfer_close ( &tcp->xfer, rc );
- tcp->xfer_closed = 1;
-
- /* 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 unsent I/O buffers */
- list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) {
- list_del ( &iobuf->list );
- free_iob ( iobuf );
- }
-
- /* Remove from list and drop reference */
- stop_timer ( &tcp->timer );
- 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->queue ) ) {
- tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
- tcp_dump_state ( tcp );
- }
-}
-
-/***************************************************************************
- *
- * 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;
-}
-
-/**
- * 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_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->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 );
- }
- }
- len += frag_len;
- max_len -= frag_len;
- }
- return len;
-}
-
-/**
- * Transmit any outstanding data
- *
- * @v tcp TCP connection
- * @v force_send Force sending of packet
- *
- * 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 int tcp_xmit ( struct tcp_connection *tcp, int force_send ) {
- struct io_buffer *iobuf;
- struct tcp_header *tcphdr;
- struct tcp_mss_option *mssopt;
- struct tcp_timestamp_padded_option *tsopt;
- void *payload;
- unsigned int flags;
- size_t len = 0;
- uint32_t seq_len;
- uint32_t app_win;
- uint32_t max_rcv_win;
- int rc;
-
- /* If retransmission timer is already running, do nothing */
- if ( timer_running ( &tcp->timer ) )
- return 0;
-
- /* 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_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 ) && ! force_send )
- return 0;
-
- /* 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 + MAX_HDR_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 -ENOMEM;
- }
- iob_reserve ( iobuf, MAX_HDR_LEN );
-
- /* Fill data payload from transmit queue */
- tcp_process_queue ( tcp, len, iobuf, 0 );
-
- /* Expand receive window if possible */
- max_rcv_win = ( ( freemem * 3 ) / 4 );
- if ( max_rcv_win > TCP_MAX_WINDOW_SIZE )
- max_rcv_win = TCP_MAX_WINDOW_SIZE;
- app_win = xfer_window ( &tcp->xfer );
- if ( max_rcv_win > app_win )
- max_rcv_win = app_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 );
- }
- if ( ( flags & TCP_SYN ) || tcp->timestamps ) {
- 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 = ntohl ( currticks() );
- tsopt->tsopt.tsecr = ntohl ( tcp->ts_recent );
- }
- if ( ! ( flags & TCP_SYN ) )
- flags |= TCP_PSH;
- tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) );
- memset ( tcphdr, 0, sizeof ( *tcphdr ) );
- tcphdr->src = 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 );
- 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 rc;
- }
-
- return 0;
-}
-
-/**
- * Retransmission timer expired
- *
- * @v timer Retry 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 );
- int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state );
-
- 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_TIME_WAIT ) ||
- ( tcp->tcp_state == TCP_CLOSE_WAIT ) ||
- ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) );
-
- if ( over || graceful_close ) {
- /* If we have finally timed out and given up, or if
- * this is the result of a graceful close, terminate
- * the connection
- */
- tcp->tcp_state = TCP_CLOSED;
- tcp_dump_state ( tcp );
- tcp_close ( tcp, -ETIMEDOUT );
- } else {
- /* Otherwise, retransmit the packet */
- tcp_xmit ( 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 ( MAX_HDR_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, MAX_HDR_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 ( TCP_MAX_WINDOW_SIZE );
- 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 (in network-endian order)
- * @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_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 ) {
- tcp->rcv_ack += seq_len;
- if ( tcp->rcv_win > seq_len ) {
- tcp->rcv_win -= seq_len;
- } else {
- tcp->rcv_win = 0;
- }
-}
-
-/**
- * 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->timestamps = 1;
- }
-
- /* Ignore duplicate SYN */
- if ( ( tcp->rcv_ack - seq ) > 0 )
- return 0;
-
- /* Mark SYN as received and start sending ACKs with each packet */
- tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) |
- TCP_STATE_RCVD ( TCP_SYN ) );
-
- /* Acknowledge SYN */
- tcp_rx_seq ( tcp, 1 );
-
- 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;
- }
- }
-
- /* 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--;
-
- /* Update SEQ and sent counters, and window size */
- tcp->snd_seq = ack;
- tcp->snd_sent = 0;
- tcp->snd_win = win;
-
- /* Remove any acknowledged data from transmit queue */
- tcp_process_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->queue ) && tcp->xfer_closed )
- tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN );
-
- 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;
-
- /* Deliver data to application */
- 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;
- }
-
- /* Acknowledge new data */
- tcp_rx_seq ( tcp, len );
-
- 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 ( ( tcp->rcv_ack - seq ) > 0 )
- return 0;
-
- /* Mark FIN as received and acknowledge it */
- tcp->tcp_state |= TCP_STATE_RCVD ( TCP_FIN );
- tcp_rx_seq ( tcp, 1 );
-
- /* 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 ( ( 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;
-}
-
-/**
- * Process received packet
- *
- * @v iobuf I/O buffer
- * @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 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 start_seq;
- uint32_t seq;
- uint32_t ack;
- uint32_t win;
- unsigned int flags;
- size_t len;
- int rc;
-
- /* 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 ( tcphdr->dest );
- start_seq = seq = ntohl ( tcphdr->seq );
- ack = ntohl ( tcphdr->ack );
- win = ntohs ( tcphdr->win );
- flags = tcphdr->flags;
- tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
- ( hlen - sizeof ( *tcphdr ) ), &options );
- iob_pull ( iobuf, hlen );
- len = iob_len ( iobuf );
-
- /* Dump header */
- DBGC2 ( tcp, "TCP %p RX %d<-%d %08x %08x..%08zx %4zd",
- tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ),
- ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ),
- ( ntohl ( tcphdr->seq ) + len +
- ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 )), len);
- tcp_dump_flags ( tcp, tcphdr->flags );
- DBGC2 ( tcp, "\n" );
-
- /* If no connection was found, send RST */
- if ( ! tcp ) {
- tcp_xmit_reset ( tcp, st_src, tcphdr );
- rc = -ENOTCONN;
- goto discard;
- }
-
- /* Handle ACK, if present */
- if ( flags & TCP_ACK ) {
- if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) {
- tcp_xmit_reset ( tcp, st_src, tcphdr );
- goto discard;
- }
- }
-
- /* 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;
- }
-
- /* Handle new data, if any */
- tcp_rx_data ( tcp, seq, iobuf );
- seq += len;
-
- /* Handle FIN, if present */
- if ( flags & TCP_FIN ) {
- tcp_rx_fin ( tcp, seq );
- seq++;
- }
-
- /* Update timestamp, if present and applicable */
- if ( ( seq == tcp->rcv_ack ) && options.tsopt )
- tcp->ts_recent = ntohl ( options.tsopt->tsval );
-
- /* Dump out any state change as a result of the received packet */
- tcp_dump_state ( tcp );
-
- /* Send out any pending data. We force sending a reply if either
- *
- * a) the peer is expecting an ACK (i.e. consumed sequence space), or
- * b) either end of the packet was outside the receive window
- *
- * Case (b) enables us to support TCP keepalives using
- * zero-length packets, which we would otherwise ignore. Note
- * that for case (b), we need *only* consider zero-length
- * packets, since non-zero-length packets will already be
- * caught by case (a).
- */
- tcp_xmit ( tcp, ( ( start_seq != seq ) ||
- ( ( seq - tcp->rcv_ack ) > tcp->rcv_win ) ) );
-
- /* 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 ) ) {
- tcp->timer.timeout = ( 2 * TCP_MSL );
- start_timer ( &tcp->timer );
- }
-
- 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,
-};
-
-/***************************************************************************
- *
- * Data transfer interface
- *
- ***************************************************************************
- */
-
-/**
- * Close interface
- *
- * @v xfer Data transfer interface
- * @v rc Reason for close
- */
-static void tcp_xfer_close ( struct xfer_interface *xfer, int rc ) {
- struct tcp_connection *tcp =
- container_of ( xfer, struct tcp_connection, xfer );
-
- /* Close data transfer interface */
- tcp_close ( tcp, rc );
-
- /* Transmit FIN, if possible */
- tcp_xmit ( tcp, 0 );
-}
-
-/**
- * Check flow control window
- *
- * @v xfer Data transfer interface
- * @ret len Length of window
- */
-static size_t tcp_xfer_window ( struct xfer_interface *xfer ) {
- struct tcp_connection *tcp =
- container_of ( xfer, struct tcp_connection, xfer );
-
- /* 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->queue ) )
- return 0;
-
- /* Return TCP window length */
- return tcp_xmit_win ( tcp );
-}
-
-/**
- * Deliver datagram as I/O buffer
- *
- * @v xfer Data transfer interface
- * @v iobuf Datagram I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- */
-static int tcp_xfer_deliver_iob ( struct xfer_interface *xfer,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta __unused ) {
- struct tcp_connection *tcp =
- container_of ( xfer, struct tcp_connection, xfer );
-
- /* Enqueue packet */
- list_add_tail ( &iobuf->list, &tcp->queue );
-
- /* Transmit data, if possible */
- tcp_xmit ( tcp, 0 );
-
- return 0;
-}
-
-/** TCP data transfer interface operations */
-static struct xfer_interface_operations tcp_xfer_operations = {
- .close = tcp_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = tcp_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = tcp_xfer_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/***************************************************************************
- *
- * Openers
- *
- ***************************************************************************
- */
-
-/** TCP socket opener */
-struct socket_opener tcp_socket_opener __socket_opener = {
- .semantics = TCP_SOCK_STREAM,
- .family = AF_INET,
- .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 xfer_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/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c
deleted file mode 100644
index 920e537a..00000000
--- a/gpxe/src/net/tcp/ftp.c
+++ /dev/null
@@ -1,529 +0,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/socket.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/in.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/uri.h>
-#include <gpxe/features.h>
-#include <gpxe/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_PASV,
- FTP_RETR,
- FTP_WAIT,
- FTP_QUIT,
- FTP_DONE,
-};
-
-/**
- * An FTP request
- *
- */
-struct ftp_request {
- /** Reference counter */
- struct refcnt refcnt;
- /** Data transfer interface */
- struct xfer_interface xfer;
-
- /** URI being fetched */
- struct uri *uri;
- /** FTP control channel interface */
- struct xfer_interface control;
- /** FTP data channel interface */
- struct xfer_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" */
-};
-
-/**
- * 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 */
- xfer_nullify ( &ftp->xfer );
- xfer_close ( &ftp->xfer, rc );
- xfer_nullify ( &ftp->control );
- xfer_close ( &ftp->control, rc );
- xfer_nullify ( &ftp->data );
- xfer_close ( &ftp->data, 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 = "etherboot@etherboot.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_PASV] = { "PASV", NULL },
- [FTP_RETR] = { "RETR ", ftp_uri_path },
- [FTP_WAIT] = { NULL, NULL },
- [FTP_QUIT] = { "QUIT", NULL },
- [FTP_DONE] = { NULL, NULL },
-};
-
-/**
- * Handle control channel being closed
- *
- * @v control FTP control channel interface
- * @v rc Reason for close
- *
- * When the control channel is closed, the data channel must also be
- * closed, if it is currently open.
- */
-static void ftp_control_close ( struct xfer_interface *control, int rc ) {
- struct ftp_request *ftp =
- container_of ( control, struct ftp_request, control );
-
- DBGC ( ftp, "FTP %p control connection closed: %s\n",
- ftp, strerror ( rc ) );
-
- /* Complete FTP operation */
- ftp_done ( ftp, rc );
-}
-
-/**
- * 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;
-
- /* 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;
- }
-
- /* 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 control FTP control channel interface
- * @v data New data
- * @v len Length of new data
- *
- * Data is collected until a complete line is received, at which point
- * its information is passed to ftp_reply().
- */
-static int ftp_control_deliver_raw ( struct xfer_interface *control,
- const void *data, size_t len ) {
- struct ftp_request *ftp =
- container_of ( control, struct ftp_request, control );
- char *recvbuf = ftp->recvbuf;
- size_t recvsize = ftp->recvsize;
- char c;
-
- while ( len-- ) {
- c = * ( ( char * ) data++ );
- switch ( c ) {
- case '\r' :
- case '\n' :
- /* End of line: call ftp_reply() to handle
- * completed reply. Avoid calling ftp_reply()
- * twice if we receive both \r and \n.
- */
- if ( recvsize == 0 )
- ftp_reply ( ftp );
- /* Start filling up the status code buffer */
- recvbuf = ftp->status_text;
- recvsize = sizeof ( ftp->status_text ) - 1;
- break;
- case '(' :
- /* Start filling up the passive parameter buffer */
- recvbuf = ftp->passive_text;
- recvsize = sizeof ( ftp->passive_text ) - 1;
- break;
- case ')' :
- /* Stop filling the passive parameter buffer */
- recvsize = 0;
- break;
- default :
- /* Fill up buffer if applicable */
- if ( recvsize > 0 ) {
- *(recvbuf++) = c;
- recvsize--;
- }
- break;
- }
- }
-
- /* Store for next invocation */
- ftp->recvbuf = recvbuf;
- ftp->recvsize = recvsize;
-
- return 0;
-}
-
-/** FTP control channel operations */
-static struct xfer_interface_operations ftp_control_operations = {
- .close = ftp_control_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = ftp_control_deliver_raw,
-};
-
-/*****************************************************************************
- *
- * FTP data channel
- *
- */
-
-/**
- * Handle FTP data channel being closed
- *
- * @v data FTP data channel interface
- * @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 xfer_interface *data, int rc ) {
- struct ftp_request *ftp =
- container_of ( data, struct ftp_request, data );
-
- 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 );
- }
-}
-
-/**
- * Handle data delivery via FTP data channel
- *
- * @v xfer FTP data channel interface
- * @v iobuf I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- */
-static int ftp_data_deliver_iob ( struct xfer_interface *data,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta __unused ) {
- struct ftp_request *ftp =
- container_of ( data, struct ftp_request, data );
- int rc;
-
- if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) {
- DBGC ( ftp, "FTP %p failed to deliver data: %s\n",
- ftp, strerror ( rc ) );
- return rc;
- }
-
- return 0;
-}
-
-/** FTP data channel operations */
-static struct xfer_interface_operations ftp_data_operations = {
- .close = ftp_data_closed,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = ftp_data_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/*****************************************************************************
- *
- * Data transfer interface
- *
- */
-
-/**
- * Close FTP data transfer interface
- *
- * @v xfer FTP data transfer interface
- * @v rc Reason for close
- */
-static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) {
- struct ftp_request *ftp =
- container_of ( xfer, struct ftp_request, xfer );
-
- DBGC ( ftp, "FTP %p data transfer interface closed: %s\n",
- ftp, strerror ( rc ) );
-
- ftp_done ( ftp, rc );
-}
-
-/** FTP data transfer interface operations */
-static struct xfer_interface_operations ftp_xfer_operations = {
- .close = ftp_xfer_closed,
- .vredirect = ignore_xfer_vredirect,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = ignore_xfer_deliver_raw,
-};
-
-/*****************************************************************************
- *
- * URI opener
- *
- */
-
-/**
- * Initiate an FTP connection
- *
- * @v xfer Data transfer interface
- * @v uri Uniform Resource Identifier
- * @ret rc Return status code
- */
-static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) {
- struct ftp_request *ftp;
- struct sockaddr_tcpip server;
- int rc;
-
- /* Sanity checks */
- if ( ! uri->path )
- return -EINVAL;
- if ( ! uri->host )
- return -EINVAL;
-
- /* Allocate and populate structure */
- ftp = zalloc ( sizeof ( *ftp ) );
- if ( ! ftp )
- return -ENOMEM;
- ftp->refcnt.free = ftp_free;
- xfer_init ( &ftp->xfer, &ftp_xfer_operations, &ftp->refcnt );
- ftp->uri = uri_get ( uri );
- xfer_init ( &ftp->control, &ftp_control_operations, &ftp->refcnt );
- xfer_init ( &ftp->data, &ftp_data_operations, &ftp->refcnt );
- 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 */
- xfer_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/gpxe/src/net/tcp/http.c b/gpxe/src/net/tcp/http.c
deleted file mode 100644
index a365b2a4..00000000
--- a/gpxe/src/net/tcp/http.c
+++ /dev/null
@@ -1,597 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/**
- * @file
- *
- * Hyper Text Transfer Protocol (HTTP)
- *
- */
-
-#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 <gpxe/uri.h>
-#include <gpxe/refcnt.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/socket.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/process.h>
-#include <gpxe/linebuf.h>
-#include <gpxe/features.h>
-#include <gpxe/base64.h>
-#include <gpxe/http.h>
-
-FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 );
-
-/** HTTP receive state */
-enum http_rx_state {
- HTTP_RX_RESPONSE = 0,
- HTTP_RX_HEADER,
- HTTP_RX_DATA,
- HTTP_RX_DEAD,
-};
-
-/**
- * An HTTP request
- *
- */
-struct http_request {
- /** Reference count */
- struct refcnt refcnt;
- /** Data transfer interface */
- struct xfer_interface xfer;
-
- /** URI being fetched */
- struct uri *uri;
- /** Transport layer interface */
- struct xfer_interface socket;
-
- /** TX process */
- struct process process;
-
- /** HTTP response code */
- unsigned int response;
- /** HTTP Content-Length */
- size_t content_length;
- /** Received length */
- size_t rx_len;
- /** RX state */
- enum http_rx_state rx_state;
- /** Line buffer for received header lines */
- struct line_buffer linebuf;
-};
-
-/**
- * Free HTTP request
- *
- * @v refcnt Reference counter
- */
-static void http_free ( struct refcnt *refcnt ) {
- struct http_request *http =
- container_of ( refcnt, struct http_request, refcnt );
-
- uri_put ( http->uri );
- empty_line_buffer ( &http->linebuf );
- free ( http );
-};
-
-/**
- * Mark HTTP request as complete
- *
- * @v http HTTP request
- * @v rc Return status code
- */
-static void http_done ( struct http_request *http, int rc ) {
-
- /* Prevent further processing of any current packet */
- http->rx_state = HTTP_RX_DEAD;
-
- /* If we had a Content-Length, and the received content length
- * isn't correct, flag an error
- */
- if ( http->content_length &&
- ( http->content_length != http->rx_len ) ) {
- DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n",
- http, http->rx_len, http->content_length );
- rc = -EIO;
- }
-
- /* Remove process */
- process_del ( &http->process );
-
- /* Close all data transfer interfaces */
- xfer_nullify ( &http->socket );
- xfer_close ( &http->socket, rc );
- xfer_nullify ( &http->xfer );
- xfer_close ( &http->xfer, rc );
-}
-
-/**
- * Convert HTTP response code to return status code
- *
- * @v response HTTP response code
- * @ret rc Return status code
- */
-static int http_response_to_rc ( unsigned int response ) {
- switch ( response ) {
- case 200:
- case 301:
- case 302:
- return 0;
- case 404:
- return -ENOENT;
- case 403:
- return -EPERM;
- case 401:
- return -EACCES;
- default:
- return -EIO;
- }
-}
-
-/**
- * Handle HTTP response
- *
- * @v http HTTP request
- * @v response HTTP response
- * @ret rc Return status code
- */
-static int http_rx_response ( struct http_request *http, char *response ) {
- char *spc;
- int rc;
-
- DBGC ( http, "HTTP %p response \"%s\"\n", http, response );
-
- /* Check response starts with "HTTP/" */
- if ( strncmp ( response, "HTTP/", 5 ) != 0 )
- return -EIO;
-
- /* Locate and check response code */
- spc = strchr ( response, ' ' );
- if ( ! spc )
- return -EIO;
- http->response = strtoul ( spc, NULL, 10 );
- if ( ( rc = http_response_to_rc ( http->response ) ) != 0 )
- return rc;
-
- /* Move to received headers */
- http->rx_state = HTTP_RX_HEADER;
- return 0;
-}
-
-/**
- * Handle HTTP Location header
- *
- * @v http HTTP request
- * @v value HTTP header value
- * @ret rc Return status code
- */
-static int http_rx_location ( struct http_request *http, const char *value ) {
- int rc;
-
- /* Redirect to new location */
- DBGC ( http, "HTTP %p redirecting to %s\n", http, value );
- if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING,
- value ) ) != 0 ) {
- DBGC ( http, "HTTP %p could not redirect: %s\n",
- http, strerror ( rc ) );
- return rc;
- }
-
- return 0;
-}
-
-/**
- * Handle HTTP Content-Length header
- *
- * @v http HTTP request
- * @v value HTTP header value
- * @ret rc Return status code
- */
-static int http_rx_content_length ( struct http_request *http,
- const char *value ) {
- char *endp;
-
- http->content_length = strtoul ( value, &endp, 10 );
- if ( *endp != '\0' ) {
- DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n",
- http, value );
- return -EIO;
- }
-
- /* Use seek() to notify recipient of filesize */
- xfer_seek ( &http->xfer, http->content_length, SEEK_SET );
- xfer_seek ( &http->xfer, 0, SEEK_SET );
-
- return 0;
-}
-
-/** An HTTP header handler */
-struct http_header_handler {
- /** Name (e.g. "Content-Length") */
- const char *header;
- /** Handle received header
- *
- * @v http HTTP request
- * @v value HTTP header value
- * @ret rc Return status code
- *
- * If an error is returned, the download will be aborted.
- */
- int ( * rx ) ( struct http_request *http, const char *value );
-};
-
-/** List of HTTP header handlers */
-static struct http_header_handler http_header_handlers[] = {
- {
- .header = "Location",
- .rx = http_rx_location,
- },
- {
- .header = "Content-Length",
- .rx = http_rx_content_length,
- },
- { NULL, NULL }
-};
-
-/**
- * Handle HTTP header
- *
- * @v http HTTP request
- * @v header HTTP header
- * @ret rc Return status code
- */
-static int http_rx_header ( struct http_request *http, char *header ) {
- struct http_header_handler *handler;
- char *separator;
- char *value;
- int rc;
-
- /* An empty header line marks the transition to the data phase */
- if ( ! header[0] ) {
- DBGC ( http, "HTTP %p start of data\n", http );
- empty_line_buffer ( &http->linebuf );
- http->rx_state = HTTP_RX_DATA;
- return 0;
- }
-
- DBGC ( http, "HTTP %p header \"%s\"\n", http, header );
-
- /* Split header at the ": " */
- separator = strstr ( header, ": " );
- if ( ! separator ) {
- DBGC ( http, "HTTP %p malformed header\n", http );
- return -EIO;
- }
- *separator = '\0';
- value = ( separator + 2 );
-
- /* Hand off to header handler, if one exists */
- for ( handler = http_header_handlers ; handler->header ; handler++ ) {
- if ( strcasecmp ( header, handler->header ) == 0 ) {
- if ( ( rc = handler->rx ( http, value ) ) != 0 )
- return rc;
- break;
- }
- }
- return 0;
-}
-
-/** An HTTP line-based data handler */
-struct http_line_handler {
- /** Handle line
- *
- * @v http HTTP request
- * @v line Line to handle
- * @ret rc Return status code
- */
- int ( * rx ) ( struct http_request *http, char *line );
-};
-
-/** List of HTTP line-based data handlers */
-static struct http_line_handler http_line_handlers[] = {
- [HTTP_RX_RESPONSE] = { .rx = http_rx_response },
- [HTTP_RX_HEADER] = { .rx = http_rx_header },
-};
-
-/**
- * Handle new data arriving via HTTP connection in the data phase
- *
- * @v http HTTP request
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
-static int http_rx_data ( struct http_request *http,
- struct io_buffer *iobuf ) {
- int rc;
-
- /* Update received length */
- http->rx_len += iob_len ( iobuf );
-
- /* Hand off data buffer */
- if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 )
- return rc;
-
- /* If we have reached the content-length, stop now */
- if ( http->content_length &&
- ( http->rx_len >= http->content_length ) ) {
- http_done ( http, 0 );
- }
-
- return 0;
-}
-
-/**
- * Handle new data arriving via HTTP connection
- *
- * @v socket Transport layer interface
- * @v iobuf I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- */
-static int http_socket_deliver_iob ( struct xfer_interface *socket,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta __unused ) {
- struct http_request *http =
- container_of ( socket, struct http_request, socket );
- struct http_line_handler *lh;
- char *line;
- ssize_t len;
- int rc = 0;
-
- while ( iob_len ( iobuf ) ) {
- switch ( http->rx_state ) {
- case HTTP_RX_DEAD:
- /* Do no further processing */
- goto done;
- case HTTP_RX_DATA:
- /* Once we're into the data phase, just fill
- * the data buffer
- */
- rc = http_rx_data ( http, iob_disown ( iobuf ) );
- goto done;
- case HTTP_RX_RESPONSE:
- case HTTP_RX_HEADER:
- /* In the other phases, buffer and process a
- * line at a time
- */
- len = line_buffer ( &http->linebuf, iobuf->data,
- iob_len ( iobuf ) );
- if ( len < 0 ) {
- rc = len;
- DBGC ( http, "HTTP %p could not buffer line: "
- "%s\n", http, strerror ( rc ) );
- goto done;
- }
- iob_pull ( iobuf, len );
- line = buffered_line ( &http->linebuf );
- if ( line ) {
- lh = &http_line_handlers[http->rx_state];
- if ( ( rc = lh->rx ( http, line ) ) != 0 )
- goto done;
- }
- break;
- default:
- assert ( 0 );
- break;
- }
- }
-
- done:
- if ( rc )
- http_done ( http, rc );
- free_iob ( iobuf );
- return rc;
-}
-
-/**
- * HTTP process
- *
- * @v process Process
- */
-static void http_step ( struct process *process ) {
- struct http_request *http =
- container_of ( process, struct http_request, process );
- const char *host = http->uri->host;
- const char *user = http->uri->user;
- const char *password =
- ( http->uri->password ? http->uri->password : "" );
- size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ +
- strlen ( password ) ) : 0 );
- size_t user_pw_base64_len = base64_encoded_len ( user_pw_len );
- char user_pw[ user_pw_len + 1 /* NUL */ ];
- char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ];
- int rc;
- int request_len = unparse_uri ( NULL, 0, http->uri,
- URI_PATH_BIT | URI_QUERY_BIT );
-
- if ( xfer_window ( &http->socket ) ) {
- char request[request_len + 1];
-
- /* Construct path?query request */
- unparse_uri ( request, sizeof ( request ), http->uri,
- URI_PATH_BIT | URI_QUERY_BIT );
-
- /* We want to execute only once */
- process_del ( &http->process );
-
- /* Construct authorisation, if applicable */
- if ( user ) {
- /* Make "user:password" string from decoded fields */
- snprintf ( user_pw, sizeof ( user_pw ), "%s:%s",
- user, password );
-
- /* Base64-encode the "user:password" string */
- base64_encode ( user_pw, user_pw_base64 );
- }
-
- /* Send GET request */
- if ( ( rc = xfer_printf ( &http->socket,
- "GET %s%s HTTP/1.0\r\n"
- "User-Agent: gPXE/" VERSION "\r\n"
- "%s%s%s"
- "Host: %s\r\n"
- "\r\n",
- http->uri->path ? "" : "/",
- request,
- ( user ?
- "Authorization: Basic " : "" ),
- ( user ? user_pw_base64 : "" ),
- ( user ? "\r\n" : "" ),
- host ) ) != 0 ) {
- http_done ( http, rc );
- }
- }
-}
-
-/**
- * HTTP connection closed by network stack
- *
- * @v socket Transport layer interface
- * @v rc Reason for close
- */
-static void http_socket_close ( struct xfer_interface *socket, int rc ) {
- struct http_request *http =
- container_of ( socket, struct http_request, socket );
-
- DBGC ( http, "HTTP %p socket closed: %s\n",
- http, strerror ( rc ) );
-
- http_done ( http, rc );
-}
-
-/** HTTP socket operations */
-static struct xfer_interface_operations http_socket_operations = {
- .close = http_socket_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = http_socket_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * Close HTTP data transfer interface
- *
- * @v xfer Data transfer interface
- * @v rc Reason for close
- */
-static void http_xfer_close ( struct xfer_interface *xfer, int rc ) {
- struct http_request *http =
- container_of ( xfer, struct http_request, xfer );
-
- DBGC ( http, "HTTP %p interface closed: %s\n",
- http, strerror ( rc ) );
-
- http_done ( http, rc );
-}
-
-/** HTTP data transfer interface operations */
-static struct xfer_interface_operations http_xfer_operations = {
- .close = http_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = ignore_xfer_deliver_raw,
-};
-
-/**
- * Initiate an HTTP connection, with optional filter
- *
- * @v xfer Data transfer interface
- * @v uri Uniform Resource Identifier
- * @v default_port Default port number
- * @v filter Filter to apply to socket, or NULL
- * @ret rc Return status code
- */
-int http_open_filter ( struct xfer_interface *xfer, struct uri *uri,
- unsigned int default_port,
- int ( * filter ) ( struct xfer_interface *xfer,
- struct xfer_interface **next ) ) {
- struct http_request *http;
- struct sockaddr_tcpip server;
- struct xfer_interface *socket;
- int rc;
-
- /* Sanity checks */
- if ( ! uri->host )
- return -EINVAL;
-
- /* Allocate and populate HTTP structure */
- http = zalloc ( sizeof ( *http ) );
- if ( ! http )
- return -ENOMEM;
- http->refcnt.free = http_free;
- xfer_init ( &http->xfer, &http_xfer_operations, &http->refcnt );
- http->uri = uri_get ( uri );
- xfer_init ( &http->socket, &http_socket_operations, &http->refcnt );
- process_init ( &http->process, http_step, &http->refcnt );
-
- /* Open socket */
- memset ( &server, 0, sizeof ( server ) );
- server.st_port = htons ( uri_port ( http->uri, default_port ) );
- socket = &http->socket;
- if ( filter ) {
- if ( ( rc = filter ( socket, &socket ) ) != 0 )
- goto err;
- }
- if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
- ( struct sockaddr * ) &server,
- uri->host, NULL ) ) != 0 )
- goto err;
-
- /* Attach to parent interface, mortalise self, and return */
- xfer_plug_plug ( &http->xfer, xfer );
- ref_put ( &http->refcnt );
- return 0;
-
- err:
- DBGC ( http, "HTTP %p could not create request: %s\n",
- http, strerror ( rc ) );
- http_done ( http, rc );
- ref_put ( &http->refcnt );
- return rc;
-}
-
-/**
- * Initiate an HTTP connection
- *
- * @v xfer Data transfer interface
- * @v uri Uniform Resource Identifier
- * @ret rc Return status code
- */
-static int http_open ( struct xfer_interface *xfer, struct uri *uri ) {
- return http_open_filter ( xfer, uri, HTTP_PORT, NULL );
-}
-
-/** HTTP URI opener */
-struct uri_opener http_uri_opener __uri_opener = {
- .scheme = "http",
- .open = http_open,
-};
diff --git a/gpxe/src/net/tcp/https.c b/gpxe/src/net/tcp/https.c
deleted file mode 100644
index 7a2961f2..00000000
--- a/gpxe/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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/**
- * @file
- *
- * Secure Hyper Text Transfer Protocol (HTTPS)
- *
- */
-
-#include <stddef.h>
-#include <gpxe/open.h>
-#include <gpxe/tls.h>
-#include <gpxe/http.h>
-#include <gpxe/features.h>
-
-FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 );
-
-/**
- * Initiate an HTTPS connection
- *
- * @v xfer Data transfer interface
- * @v uri Uniform Resource Identifier
- * @ret rc Return status code
- */
-static int https_open ( struct xfer_interface *xfer, struct uri *uri ) {
- return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls );
-}
-
-/** HTTPS URI opener */
-struct uri_opener https_uri_opener __uri_opener = {
- .scheme = "https",
- .open = https_open,
-};
diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c
deleted file mode 100644
index 771384b9..00000000
--- a/gpxe/src/net/tcp/iscsi.c
+++ /dev/null
@@ -1,1934 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stddef.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <assert.h>
-#include <byteswap.h>
-#include <gpxe/vsprintf.h>
-#include <gpxe/socket.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/scsi.h>
-#include <gpxe/process.h>
-#include <gpxe/uaccess.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/settings.h>
-#include <gpxe/features.h>
-#include <gpxe/iscsi.h>
-
-/** @file
- *
- * iSCSI protocol
- *
- */
-
-FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 );
-
-/** iSCSI initiator name (explicitly specified) */
-static char *iscsi_explicit_initiator_iqn;
-
-/** Default iSCSI initiator name (constructed from hostname) */
-static char *iscsi_default_initiator_iqn;
-
-/** iSCSI initiator username */
-static char *iscsi_initiator_username;
-
-/** iSCSI initiator password */
-static char *iscsi_initiator_password;
-
-/** iSCSI target username */
-static char *iscsi_target_username;
-
-/** iSCSI target password */
-static char *iscsi_target_password;
-
-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;
-}
-
-/**
- * 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->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 );
-}
-
-/**
- * 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 fresh initiator task tag */
- iscsi->itt++;
-
- /* 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 */
- xfer_close ( &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
- *
- * 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_issue(). 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 ) {
-
- assert ( iscsi->tx_state == ISCSI_TX_IDLE );
- assert ( iscsi->command != NULL );
-
- iscsi->command->rc = rc;
- iscsi->command = NULL;
-}
-
-/****************************************************************************
- *
- * 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 */
- command->lun = iscsi->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;
- int sense_offset;
-
- /* Capture the sense response code as it floats past, if present */
- sense_offset = ISCSI_SENSE_RESPONSE_CODE_OFFSET - iscsi->rx_offset;
- if ( ( sense_offset >= 0 ) && len ) {
- iscsi->command->sense_response =
- * ( ( char * ) data + sense_offset );
- }
-
- /* Wait for whole SCSI response to arrive */
- if ( remaining )
- return 0;
-
- /* Record SCSI status code */
- iscsi->command->status = response->status;
-
- /* Check for errors */
- if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
- return -EIO;
-
- /* Mark as completed */
- iscsi_scsi_done ( iscsi, 0 );
- 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->command->status = data_in->status;
- /* iSCSI cannot return an error status via a data-in */
- iscsi_scsi_done ( iscsi, 0 );
- }
-
- 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->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;
-
- offset = ntohl ( data_out->offset );
- len = ISCSI_DATA_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 );
- if ( ! iobuf )
- return -ENOMEM;
-
- copy_from_user ( iob_put ( iobuf, len ),
- iscsi->command->data_out, offset, len );
-
- return xfer_deliver_iob ( &iscsi->socket, iobuf );
-}
-
-/****************************************************************************
- *
- * 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
- * InitialR2T=Yes [1]
- * ImmediateData is irrelevant; we never send immediate data
- * 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.
- */
-static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
- void *data, size_t len ) {
- unsigned int used = 0;
- unsigned int i;
- 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 ) ) {
- assert ( iscsi->initiator_username != NULL );
- used += ssnprintf ( data + used, len - used,
- "CHAP_N=%s%cCHAP_R=0x",
- iscsi->initiator_username, 0 );
- for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) {
- used += ssnprintf ( data + used, len - used, "%02x",
- iscsi->chap.response[i] );
- }
- used += ssnprintf ( data + used, len - used, "%c", 0 );
- }
-
- if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) {
- used += ssnprintf ( data + used, len - used,
- "CHAP_I=%d%cCHAP_C=0x",
- iscsi->chap_challenge[0], 0 );
- for ( i = 1 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
- used += ssnprintf ( data + used, len - used, "%02x",
- iscsi->chap_challenge[i] );
- }
- used += ssnprintf ( data + used, len - used, "%c", 0 );
- }
-
- if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
- used += ssnprintf ( data + used, len - used,
- "HeaderDigest=None%c"
- "DataDigest=None%c"
- "InitialR2T=Yes%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 );
- }
-
- 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;
-
- /* 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 );
- /* isid_iana_qual left as zero */
- request->tsih = htons ( iscsi->tsih );
- 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;
-
- len = ISCSI_DATA_LEN ( request->lengths );
- iobuf = xfer_alloc_iob ( &iscsi->socket, len );
- if ( ! iobuf )
- return -ENOMEM;
- iob_put ( iobuf, len );
- iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
- return xfer_deliver_iob ( &iscsi->socket, iobuf );
-}
-
-/**
- * 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;
- }
-
- 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;
- }
-
- /* 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 ) {
- char buf[3];
- char *endp;
- uint8_t byte;
- unsigned int i;
-
- /* Check and strip leading "0x" */
- if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
- DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n",
- iscsi, value );
- return -EPROTO;
- }
- value += 2;
-
- /* Process challenge an octet at a time */
- for ( ; ( value[0] && value[1] ) ; value += 2 ) {
- memcpy ( buf, value, 2 );
- buf[2] = 0;
- byte = strtoul ( buf, &endp, 16 );
- if ( *endp != '\0' ) {
- DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge "
- "byte \"%s\"\n", iscsi, buf );
- return -EPROTO;
- }
- chap_update ( &iscsi->chap, &byte, sizeof ( byte ) );
- }
-
- /* 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;
- }
-
- 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 ) {
- char buf[3];
- char *endp;
- uint8_t byte;
- unsigned int i;
- 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 );
-
- /* Check and strip leading "0x" */
- if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) {
- DBGC ( iscsi, "iSCSI %p saw invalid CHAP response \"%s\"\n",
- iscsi, value );
- return -EPROTO;
- }
- value += 2;
-
- /* Check CHAP response length */
- if ( strlen ( value ) != ( 2 * iscsi->chap.response_len ) ) {
- DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n",
- iscsi );
- return -EPROTO;
- }
-
- /* Process response an octet at a time */
- for ( i = 0 ; ( value[0] && value[1] ) ; value += 2, i++ ) {
- memcpy ( buf, value, 2 );
- buf[2] = 0;
- byte = strtoul ( buf, &endp, 16 );
- if ( *endp != '\0' ) {
- DBGC ( iscsi, "iSCSI %p saw invalid CHAP response "
- "byte \"%s\"\n", iscsi, buf );
- return -EPROTO;
- }
- if ( byte != iscsi->chap.response[i] ) {
- DBGC ( iscsi, "iSCSI %p saw incorrect CHAP "
- "response\n", iscsi );
- return -EACCES;
- }
- }
- assert ( i == iscsi->chap.response_len );
-
- /* 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 up to and including 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;
- size_t key_len;
- int rc;
-
- for ( type = iscsi_string_types ; type->key ; type++ ) {
- key_len = strlen ( type->key );
- if ( strncmp ( string, type->key, key_len ) != 0 )
- continue;
- DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string );
- if ( ( rc = type->handle ( iscsi,
- ( string + key_len ) ) ) != 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;
-}
-
-/**
- * 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;
-}
-
-/**
- * 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 -EACCES;
- case ISCSI_STATUS_INITIATOR_ERROR_AUTHORISATION :
- return -EPERM;
- case ISCSI_STATUS_INITIATOR_ERROR_NOT_FOUND :
- case ISCSI_STATUS_INITIATOR_ERROR_REMOVED :
- return -ENODEV;
- default :
- return -ENOTSUP;
- }
- case ISCSI_STATUS_TARGET_ERROR :
- return -EIO;
- 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 );
- iscsi->instant_rc = rc;
- 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;
- }
-
- /* Reset retry count */
- iscsi->retry_count = 0;
-
- /* Record TSIH for future reference */
- iscsi->tsih = ntohl ( response->tsih );
-
- /* Send the actual SCSI command */
- iscsi_start_command ( iscsi );
-
- return 0;
-}
-
-/****************************************************************************
- *
- * iSCSI to socket interface
- *
- */
-
-/**
- * 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;
-}
-
-/**
- * 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;
- }
-}
-
-/**
- * Transmit data padding of an iSCSI PDU
- *
- * @v iscsi iSCSI session
- * @ret rc Return status code
- *
- * Handle transmission of any data padding in a PDU data segment.
- * iscsi::tx_bhs will be valid when this is called.
- */
-static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) {
- static const char pad[] = { '\0', '\0', '\0' };
- struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
- size_t pad_len;
-
- pad_len = ISCSI_DATA_PAD_LEN ( common->lengths );
- if ( ! pad_len )
- return 0;
-
- return xfer_deliver_raw ( &iscsi->socket, pad, pad_len );
-}
-
-/**
- * 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;
-
- switch ( common->opcode & ISCSI_OPCODE_MASK ) {
- case ISCSI_OPCODE_DATA_OUT:
- iscsi_data_out_done ( iscsi );
- case ISCSI_OPCODE_LOGIN_REQUEST:
- iscsi_login_request_done ( iscsi );
- 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 process *process ) {
- struct iscsi_session *iscsi =
- container_of ( process, struct iscsi_session, process );
- 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_IDLE:
- /* Stop processing */
- return;
- 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_DATA_PADDING;
- break;
- case ISCSI_TX_DATA_PADDING:
- tx = iscsi_tx_data_padding;
- tx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
- next_state = ISCSI_TX_IDLE;
- break;
- default:
- assert ( 0 );
- return;
- }
-
- /* Check for window availability, if needed */
- if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) {
- /* Cannot transmit at this point; stop processing */
- return;
- }
-
- /* Transmit data */
- if ( ( rc = tx ( iscsi ) ) != 0 ) {
- DBGC ( iscsi, "iSCSI %p could not transmit: %s\n",
- iscsi, strerror ( rc ) );
- return;
- }
-
- /* Move to next state */
- iscsi->tx_state = next_state;
- if ( next_state == ISCSI_TX_IDLE )
- iscsi_tx_done ( iscsi );
- }
-}
-
-/**
- * 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 );
- default:
- if ( remaining )
- return 0;
- DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi,
- response->opcode );
- return -ENOTSUP;
- }
-}
-
-/**
- * Receive new data
- *
- * @v socket Transport layer interface
- * @v data Received data
- * @v len Length of received data
- * @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_raw ( struct xfer_interface *socket,
- const void *data, size_t len ) {
- struct iscsi_session *iscsi =
- container_of ( socket, struct iscsi_session, socket );
- 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 );
- return -EINVAL;
- }
-
- frag_len = iscsi->rx_len - iscsi->rx_offset;
- if ( frag_len > len )
- frag_len = len;
- remaining = iscsi->rx_len - iscsi->rx_offset - frag_len;
- if ( ( rc = rx ( iscsi, data, frag_len, remaining ) ) != 0 ) {
- DBGC ( iscsi, "iSCSI %p could not process received "
- "data: %s\n", iscsi, strerror ( rc ) );
- iscsi_close_connection ( iscsi, rc );
- iscsi_scsi_done ( iscsi, rc );
- return rc;
- }
-
- iscsi->rx_offset += frag_len;
- data += frag_len;
- len -= 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 )
- return 0;
-
- iscsi->rx_state = next_state;
- iscsi->rx_offset = 0;
- }
-
- return 0;
-}
-
-/**
- * Handle stream connection closure
- *
- * @v socket Transport layer interface
- * @v rc Reason for close
- *
- */
-static void iscsi_socket_close ( struct xfer_interface *socket, int rc ) {
- struct iscsi_session *iscsi =
- container_of ( socket, struct iscsi_session, socket );
-
- /* Even a graceful close counts as an error for iSCSI */
- if ( ! rc )
- rc = -ECONNRESET;
-
- /* Close session cleanly */
- iscsi_close_connection ( iscsi, rc );
-
- /* Retry connection if within the retry limit, otherwise fail */
- if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) {
- DBGC ( iscsi, "iSCSI %p retrying connection (retry #%d)\n",
- iscsi, iscsi->retry_count );
- if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
- DBGC ( iscsi, "iSCSI %p could not reconnect: %s\n",
- iscsi, strerror ( rc ) );
- iscsi_scsi_done ( iscsi, rc );
- }
- } else {
- DBGC ( iscsi, "iSCSI %p retry count exceeded\n", iscsi );
- iscsi->instant_rc = rc;
- iscsi_scsi_done ( iscsi, rc );
- }
-}
-
-/**
- * Handle redirection event
- *
- * @v socket Transport layer interface
- * @v type Location type
- * @v args Remaining arguments depend upon location type
- * @ret rc Return status code
- */
-static int iscsi_vredirect ( struct xfer_interface *socket, int type,
- va_list args ) {
- struct iscsi_session *iscsi =
- container_of ( socket, struct iscsi_session, socket );
- 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 ( socket, type, args );
-}
-
-
-/** iSCSI socket operations */
-static struct xfer_interface_operations iscsi_socket_operations = {
- .close = iscsi_socket_close,
- .vredirect = iscsi_vredirect,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = iscsi_socket_deliver_raw,
-};
-
-
-/****************************************************************************
- *
- * iSCSI command issuing
- *
- */
-
-/**
- * Issue SCSI command
- *
- * @v scsi SCSI device
- * @v command SCSI command
- * @ret rc Return status code
- */
-static int iscsi_command ( struct scsi_device *scsi,
- struct scsi_command *command ) {
- struct iscsi_session *iscsi =
- container_of ( scsi->backend, struct iscsi_session, refcnt );
- int rc;
-
- /* Abort immediately if we have a recorded permanent failure */
- if ( iscsi->instant_rc )
- return iscsi->instant_rc;
-
- /* Record SCSI command */
- iscsi->command = command;
-
- /* Issue command or open connection as appropriate */
- if ( iscsi->status ) {
- iscsi_start_command ( iscsi );
- } else {
- if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
- iscsi->command = NULL;
- return rc;
- }
- }
-
- return 0;
-}
-
-/**
- * Shut down iSCSI interface
- *
- * @v scsi SCSI device
- */
-void iscsi_detach ( struct scsi_device *scsi ) {
- struct iscsi_session *iscsi =
- container_of ( scsi->backend, struct iscsi_session, refcnt );
-
- xfer_nullify ( &iscsi->socket );
- iscsi_close_connection ( iscsi, 0 );
- process_del ( &iscsi->process );
- scsi->command = scsi_detached_command;
- ref_put ( scsi->backend );
- scsi->backend = NULL;
-}
-
-/****************************************************************************
- *
- * Instantiator
- *
- */
-
-/** iSCSI root path components (as per RFC4173) */
-enum iscsi_root_path_component {
- RP_LITERAL = 0,
- RP_SERVERNAME,
- RP_PROTOCOL,
- RP_PORT,
- RP_LUN,
- RP_TARGETNAME,
- NUM_RP_COMPONENTS
-};
-
-/**
- * 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;
- }
- }
- *(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;
-}
-
-/**
- * Set iSCSI authentication details
- *
- * @v iscsi iSCSI session
- * @v initiator_username Initiator username, if any
- * @v initiator_password Initiator password, if any
- * @v target_username Target username, if any
- * @v target_password Target password, if any
- * @ret rc Return status code
- */
-static int iscsi_set_auth ( struct iscsi_session *iscsi,
- const char *initiator_username,
- const char *initiator_password,
- const char *target_username,
- const char *target_password ) {
-
- /* Check for initiator or target credentials */
- if ( initiator_username || initiator_password ||
- target_username || target_password ) {
-
- /* We must have at least an initiator username+password */
- if ( ! ( initiator_username && initiator_password ) )
- goto invalid_auth;
-
- /* Store initiator credentials */
- iscsi->initiator_username = strdup ( initiator_username );
- if ( ! iscsi->initiator_username )
- return -ENOMEM;
- iscsi->initiator_password = strdup ( initiator_password );
- if ( ! iscsi->initiator_password )
- return -ENOMEM;
-
- /* Check for target credentials */
- if ( target_username || target_password ) {
-
- /* We must have target username+password */
- if ( ! ( target_username && target_password ) )
- goto invalid_auth;
-
- /* Store target credentials */
- iscsi->target_username = strdup ( target_username );
- if ( ! iscsi->target_username )
- return -ENOMEM;
- iscsi->target_password = strdup ( target_password );
- if ( ! iscsi->target_password )
- return -ENOMEM;
- }
- }
-
- return 0;
-
- invalid_auth:
- DBGC ( iscsi, "iSCSI %p invalid credentials: initiator "
- "%sname,%spw, target %sname,%spw\n", iscsi,
- ( initiator_username ? "" : "no " ),
- ( initiator_password ? "" : "no " ),
- ( target_username ? "" : "no " ),
- ( target_password ? "" : "no " ) );
- return -EINVAL;
-}
-
-/**
- * Attach iSCSI interface
- *
- * @v scsi SCSI device
- * @v root_path iSCSI root path (as per RFC4173)
- * @ret rc Return status code
- */
-int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) {
- struct iscsi_session *iscsi;
- int rc;
-
- /* Allocate and initialise structure */
- iscsi = zalloc ( sizeof ( *iscsi ) );
- if ( ! iscsi )
- return -ENOMEM;
- iscsi->refcnt.free = iscsi_free;
- xfer_init ( &iscsi->socket, &iscsi_socket_operations, &iscsi->refcnt );
- process_init ( &iscsi->process, iscsi_tx_step, &iscsi->refcnt );
-
- /* Parse root path */
- if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 )
- goto err;
- /* Set fields not specified by root path */
- if ( ( rc = iscsi_set_auth ( iscsi,
- iscsi_initiator_username,
- iscsi_initiator_password,
- iscsi_target_username,
- iscsi_target_password ) ) != 0 )
- goto err;
-
- /* Sanity checks */
- if ( ! iscsi->target_address ) {
- DBGC ( iscsi, "iSCSI %p does not yet support discovery\n",
- iscsi );
- rc = -ENOTSUP;
- goto err;
- }
- if ( ! iscsi->target_iqn ) {
- DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n",
- iscsi, root_path );
- rc = -EINVAL;
- goto err;
- }
-
- /* Attach parent interface, mortalise self, and return */
- scsi->backend = ref_get ( &iscsi->refcnt );
- scsi->command = iscsi_command;
- ref_put ( &iscsi->refcnt );
- return 0;
-
- err:
- ref_put ( &iscsi->refcnt );
- return rc;
-}
-
-/****************************************************************************
- *
- * Settings
- *
- */
-
-/** iSCSI initiator IQN setting */
-struct setting initiator_iqn_setting __setting = {
- .name = "initiator-iqn",
- .description = "iSCSI initiator name",
- .tag = DHCP_ISCSI_INITIATOR_IQN,
- .type = &setting_type_string,
-};
-
-/** iSCSI reverse username setting */
-struct setting reverse_username_setting __setting = {
- .name = "reverse-username",
- .description = "Reverse user name",
- .tag = DHCP_EB_REVERSE_USERNAME,
- .type = &setting_type_string,
-};
-
-/** iSCSI reverse password setting */
-struct setting reverse_password_setting __setting = {
- .name = "reverse-password",
- .description = "Reverse password",
- .tag = DHCP_EB_REVERSE_PASSWORD,
- .type = &setting_type_string,
-};
-
-/** An iSCSI string setting */
-struct iscsi_string_setting {
- /** Setting */
- struct setting *setting;
- /** String to update */
- char **string;
- /** String prefix */
- const char *prefix;
-};
-
-/** iSCSI string settings */
-static struct iscsi_string_setting iscsi_string_settings[] = {
- {
- .setting = &initiator_iqn_setting,
- .string = &iscsi_explicit_initiator_iqn,
- .prefix = "",
- },
- {
- .setting = &username_setting,
- .string = &iscsi_initiator_username,
- .prefix = "",
- },
- {
- .setting = &password_setting,
- .string = &iscsi_initiator_password,
- .prefix = "",
- },
- {
- .setting = &reverse_username_setting,
- .string = &iscsi_target_username,
- .prefix = "",
- },
- {
- .setting = &reverse_password_setting,
- .string = &iscsi_target_password,
- .prefix = "",
- },
- {
- .setting = &hostname_setting,
- .string = &iscsi_default_initiator_iqn,
- .prefix = "iqn.2000-01.org.etherboot:",
- },
-};
-
-/**
- * Apply iSCSI setting
- *
- * @v setting iSCSI string setting
- * @ret rc Return status code
- */
-static int apply_iscsi_string_setting ( struct iscsi_string_setting *setting ){
- size_t prefix_len;
- int setting_len;
- size_t len;
- int check_len;
- char *p;
-
- /* Free old string */
- free ( *setting->string );
- *setting->string = NULL;
-
- /* Allocate new string */
- prefix_len = strlen ( setting->prefix );
- setting_len = fetch_setting_len ( NULL, setting->setting );
- if ( setting_len < 0 ) {
- /* Missing settings are not errors; leave strings as NULL */
- return 0;
- }
- len = ( prefix_len + setting_len + 1 );
- p = *setting->string = malloc ( len );
- if ( ! p )
- return -ENOMEM;
-
- /* Fill new string */
- strcpy ( p, setting->prefix );
- check_len = fetch_string_setting ( NULL, setting->setting,
- ( p + prefix_len ),
- ( len - prefix_len ) );
- assert ( check_len == setting_len );
-
- return 0;
-}
-
-/**
- * Apply iSCSI settings
- *
- * @ret rc Return status code
- */
-static int apply_iscsi_settings ( void ) {
- struct iscsi_string_setting *setting;
- unsigned int i;
- int rc;
-
- for ( i = 0 ; i < ( sizeof ( iscsi_string_settings ) /
- sizeof ( iscsi_string_settings[0] ) ) ; i++ ) {
- setting = &iscsi_string_settings[i];
- if ( ( rc = apply_iscsi_string_setting ( setting ) ) != 0 ) {
- DBG ( "iSCSI could not apply setting %s\n",
- setting->setting->name );
- return rc;
- }
- }
-
- return 0;
-}
-
-/** iSCSI settings applicator */
-struct settings_applicator iscsi_settings_applicator __settings_applicator = {
- .apply = apply_iscsi_settings,
-};
-
-/****************************************************************************
- *
- * Initiator name
- *
- */
-
-/**
- * Get iSCSI initiator IQN
- *
- * @v iscsi iSCSI session
- * @ret rc Return status code
- */
-const char * iscsi_initiator_iqn ( void ) {
-
- if ( iscsi_explicit_initiator_iqn )
- return iscsi_explicit_initiator_iqn;
- if ( iscsi_default_initiator_iqn )
- return iscsi_default_initiator_iqn;
- return "iqn.2000-09.org.etherboot:UNKNOWN";
-}
diff --git a/gpxe/src/net/tcpip.c b/gpxe/src/net/tcpip.c
deleted file mode 100644
index 932fd482..00000000
--- a/gpxe/src/net/tcpip.c
+++ /dev/null
@@ -1,135 +0,0 @@
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/tables.h>
-#include <gpxe/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 );
-
-/** Process a received TCP/IP packet
- *
- * @v iobuf I/O buffer
- * @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
- * @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, uint8_t tcpip_proto,
- struct sockaddr_tcpip *st_src,
- struct sockaddr_tcpip *st_dest,
- uint16_t pshdr_csum ) {
- 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 );
- return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum );
- }
- }
-
- DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto );
- free_iob ( iobuf );
- return -EPROTONOSUPPORT;
-}
-
-/** 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 */
- for_each_table_entry ( tcpip_net, TCPIP_NET_PROTOCOLS ) {
- if ( tcpip_net->sa_family == st_dest->st_family ) {
- DBG ( "TCP/IP sending %s packet\n", tcpip_net->name );
- return tcpip_net->tx ( iobuf, tcpip_protocol, st_src,
- st_dest, netdev, trans_csum );
- }
- }
-
- DBG ( "Unrecognised TCP/IP address family %d\n", st_dest->st_family );
- free_iob ( iobuf );
- return -EAFNOSUPPORT;
-}
-
-/**
- * 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 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 );
-}
diff --git a/gpxe/src/net/tls.c b/gpxe/src/net/tls.c
deleted file mode 100644
index a5b126ed..00000000
--- a/gpxe/src/net/tls.c
+++ /dev/null
@@ -1,1759 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/**
- * @file
- *
- * Transport Layer Security Protocol
- */
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/hmac.h>
-#include <gpxe/md5.h>
-#include <gpxe/sha1.h>
-#include <gpxe/aes.h>
-#include <gpxe/rsa.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/filter.h>
-#include <gpxe/asn1.h>
-#include <gpxe/x509.h>
-#include <gpxe/tls.h>
-
-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
- *
- ******************************************************************************
- */
-
-/**
- * Extract 24-bit field value
- *
- * @v field24 24-bit field
- * @ret value Field value
- *
- * TLS uses 24-bit integers in several places, which are awkward to
- * parse in C.
- */
-static unsigned long tls_uint24 ( uint8_t field24[3] ) {
- return ( ( field24[0] << 16 ) + ( field24[1] << 8 ) + field24[2] );
-}
-
-/******************************************************************************
- *
- * 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 );
-
- /* 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 );
- x509_free_rsa_public_key ( &tls->rsa );
- free ( tls->rx_data );
-
- /* 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 process */
- process_del ( &tls->process );
-
- /* Close ciphertext and plaintext streams */
- xfer_nullify ( &tls->cipherstream.xfer );
- xfer_close ( &tls->cipherstream.xfer, rc );
- xfer_nullify ( &tls->plainstream.xfer );
- xfer_close ( &tls->plainstream.xfer, rc );
-}
-
-/******************************************************************************
- *
- * Random number generation
- *
- ******************************************************************************
- */
-
-/**
- * Generate random data
- *
- * @v data Buffer to fill
- * @v len Length of buffer
- */
-static void tls_generate_random ( void *data, size_t len ) {
- /* FIXME: Some real random data source would be nice... */
- memset ( data, 0x01, len );
-}
-
-/**
- * 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 out_md5[out_len];
- uint8_t out_sha1[out_len];
- unsigned int i;
-
- va_start ( seeds, out_len );
-
- /* 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_md5, out_len, seeds );
- va_end ( tmp );
-
- /* Calculate SHA1 portion */
- va_copy ( tmp, seeds );
- tls_p_hash_va ( tls, &sha1_algorithm, sha1_secret, subsecret_len,
- out_sha1, 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 ) = ( out_md5[i] ^ out_sha1[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->digest->digestsize;
- size_t key_size = tx_cipherspec->key_len;
- size_t iv_size = tx_cipherspec->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->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->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->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->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
- *
- ******************************************************************************
- */
-
-/**
- * Clear cipher suite
- *
- * @v cipherspec TLS cipher specification
- */
-static void tls_clear_cipher ( struct tls_session *tls __unused,
- struct tls_cipherspec *cipherspec ) {
- free ( cipherspec->dynamic );
- memset ( cipherspec, 0, sizeof ( cipherspec ) );
- cipherspec->pubkey = &pubkey_null;
- cipherspec->cipher = &cipher_null;
- cipherspec->digest = &digest_null;
-}
-
-/**
- * Set cipher suite
- *
- * @v tls TLS session
- * @v cipherspec TLS cipher specification
- * @v pubkey Public-key encryption elgorithm
- * @v cipher Bulk encryption cipher algorithm
- * @v digest MAC digest algorithm
- * @v key_len Key length
- * @ret rc Return status code
- */
-static int tls_set_cipher ( struct tls_session *tls,
- struct tls_cipherspec *cipherspec,
- struct pubkey_algorithm *pubkey,
- struct cipher_algorithm *cipher,
- struct digest_algorithm *digest,
- size_t key_len ) {
- 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 = malloc ( total );
- if ( ! dynamic ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
- "context\n", tls, total );
- return -ENOMEM;
- }
- memset ( dynamic, 0, total );
-
- /* 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->pubkey = pubkey;
- cipherspec->cipher = cipher;
- cipherspec->digest = digest;
- cipherspec->key_len = key_len;
-
- 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 pubkey_algorithm *pubkey = &pubkey_null;
- struct cipher_algorithm *cipher = &cipher_null;
- struct digest_algorithm *digest = &digest_null;
- unsigned int key_len = 0;
- int rc;
-
- switch ( cipher_suite ) {
- case htons ( TLS_RSA_WITH_AES_128_CBC_SHA ):
- key_len = ( 128 / 8 );
- cipher = &aes_cbc_algorithm;
- digest = &sha1_algorithm;
- break;
- case htons ( TLS_RSA_WITH_AES_256_CBC_SHA ):
- key_len = ( 256 / 8 );
- cipher = &aes_cbc_algorithm;
- digest = &sha1_algorithm;
- break;
- default:
- DBGC ( tls, "TLS %p does not support cipher %04x\n",
- tls, ntohs ( cipher_suite ) );
- return -ENOTSUP;
- }
-
- /* Set ciphers */
- if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, pubkey,
- cipher, digest, key_len ) ) != 0 )
- return rc;
- if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, pubkey,
- cipher, digest, key_len ) ) != 0 )
- return rc;
-
- DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls,
- pubkey->name, cipher->name, ( key_len * 8 ), 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 ( /* FIXME (when pubkey is not hard-coded to RSA):
- * ( pending->pubkey == &pubkey_null ) || */
- ( pending->cipher == &cipher_null ) ||
- ( pending->digest == &digest_null ) ) {
- DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
- return -ENOTSUP;
- }
-
- tls_clear_cipher ( tls, active );
- memswap ( active, pending, sizeof ( *active ) );
- return 0;
-}
-
-/******************************************************************************
- *
- * 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_algorithm, tls->handshake_md5_ctx, data, len );
- digest_update ( &sha1_algorithm, tls->handshake_sha1_ctx, data, len );
-}
-
-/**
- * Calculate handshake verification hash
- *
- * @v tls TLS session
- * @v out Output buffer
- *
- * Calculates the MD5+SHA1 digest over all handshake messages seen so
- * far.
- */
-static void tls_verify_handshake ( struct tls_session *tls, void *out ) {
- struct digest_algorithm *md5 = &md5_algorithm;
- struct digest_algorithm *sha1 = &sha1_algorithm;
- uint8_t md5_ctx[md5->ctxsize];
- uint8_t sha1_ctx[sha1->ctxsize];
- void *md5_digest = out;
- void *sha1_digest = ( out + md5->digestsize );
-
- memcpy ( md5_ctx, tls->handshake_md5_ctx, sizeof ( md5_ctx ) );
- memcpy ( sha1_ctx, tls->handshake_sha1_ctx, sizeof ( sha1_ctx ) );
- digest_final ( md5, md5_ctx, md5_digest );
- digest_final ( sha1, sha1_ctx, sha1_digest );
-}
-
-/******************************************************************************
- *
- * Record handling
- *
- ******************************************************************************
- */
-
-/**
- * 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[2];
- uint8_t compression_methods_len;
- uint8_t compression_methods[1];
- } __attribute__ (( packed )) hello;
-
- 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_TLS_1_0 );
- memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) );
- hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
- hello.cipher_suites[0] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA );
- hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA );
- hello.compression_methods_len = sizeof ( hello.compression_methods );
-
- return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
-}
-
-/**
- * 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 ) {
- /* FIXME: Hack alert */
- RSA_CTX *rsa_ctx;
- RSA_pub_key_new ( &rsa_ctx, tls->rsa.modulus, tls->rsa.modulus_len,
- tls->rsa.exponent, tls->rsa.exponent_len );
- struct {
- uint32_t type_length;
- uint16_t encrypted_pre_master_secret_len;
- uint8_t encrypted_pre_master_secret[rsa_ctx->num_octets];
- } __attribute__ (( packed )) key_xchg;
-
- memset ( &key_xchg, 0, sizeof ( key_xchg ) );
- key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
- htonl ( sizeof ( key_xchg ) -
- sizeof ( key_xchg.type_length ) ) );
- key_xchg.encrypted_pre_master_secret_len
- = htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) );
-
- /* FIXME: Hack alert */
- DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" );
- DBGC_HD ( tls, &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ) );
- DBGC_HD ( tls, tls->rsa.modulus, tls->rsa.modulus_len );
- DBGC_HD ( tls, tls->rsa.exponent, tls->rsa.exponent_len );
- RSA_encrypt ( rsa_ctx, ( const uint8_t * ) &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ),
- key_xchg.encrypted_pre_master_secret, 0 );
- DBGC ( tls, "RSA encrypt done. Ciphertext:\n" );
- DBGC_HD ( tls, &key_xchg.encrypted_pre_master_secret,
- sizeof ( key_xchg.encrypted_pre_master_secret ) );
- RSA_free ( rsa_ctx );
-
-
- return tls_send_handshake ( tls, &key_xchg, sizeof ( key_xchg ) );
-}
-
-/**
- * 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 {
- uint32_t type_length;
- uint8_t verify_data[12];
- } __attribute__ (( packed )) finished;
- uint8_t digest[MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE];
-
- 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 );
- tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ),
- finished.verify_data, sizeof ( finished.verify_data ),
- "client finished", digest, sizeof ( digest ) );
-
- return tls_send_handshake ( tls, &finished, sizeof ( finished ) );
-}
-
-/**
- * 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,
- 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;
- }
-
- 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, void *data, size_t len ) {
- struct {
- uint8_t level;
- uint8_t description;
- char next[0];
- } __attribute__ (( packed )) *alert = data;
- 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;
- }
-
- 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;
- default:
- DBGC ( tls, "TLS %p received unknown alert level %d"
- "(alert %d)\n", tls, alert->level, alert->description );
- return -EIO;
- }
-}
-
-/**
- * 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,
- void *data, size_t len ) {
- struct {
- uint16_t version;
- uint8_t random[32];
- uint8_t session_id_len;
- char next[0];
- } __attribute__ (( packed )) *hello_a = data;
- 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;
- void *end = hello_b->next;
- int rc;
-
- /* Sanity check */
- if ( end != ( data + len ) ) {
- DBGC ( tls, "TLS %p received overlength Server Hello\n", tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- /* Check protocol version */
- if ( ntohs ( hello_a->version ) < TLS_VERSION_TLS_1_0 ) {
- DBGC ( tls, "TLS %p does not support protocol version %d.%d\n",
- tls, ( ntohs ( hello_a->version ) >> 8 ),
- ( ntohs ( hello_a->version ) & 0xff ) );
- return -ENOTSUP;
- }
-
- /* 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;
-}
-
-/**
- * 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,
- void *data, size_t len ) {
- struct {
- uint8_t length[3];
- uint8_t certificates[0];
- } __attribute__ (( packed )) *certificate = data;
- struct {
- uint8_t length[3];
- uint8_t certificate[0];
- } __attribute__ (( packed )) *element =
- ( ( void * ) certificate->certificates );
- size_t elements_len = tls_uint24 ( certificate->length );
- void *end = ( certificate->certificates + elements_len );
- struct asn1_cursor cursor;
- 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;
- }
-
- /* Traverse certificate chain */
- do {
- cursor.data = element->certificate;
- cursor.len = tls_uint24 ( element->length );
- if ( ( cursor.data + cursor.len ) > end ) {
- DBGC ( tls, "TLS %p received corrupt Server "
- "Certificate\n", tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- // HACK
- if ( ( rc = x509_rsa_public_key ( &cursor,
- &tls->rsa ) ) != 0 ) {
- DBGC ( tls, "TLS %p cannot determine RSA public key: "
- "%s\n", tls, strerror ( rc ) );
- return rc;
- }
- return 0;
-
- element = ( cursor.data + cursor.len );
- } while ( element != end );
-
- return -EINVAL;
-}
-
-/**
- * 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,
- void *data, size_t len ) {
- struct {
- char next[0];
- } __attribute__ (( packed )) *hello_done = data;
- void *end = hello_done->next;
-
- /* Sanity check */
- if ( end != ( data + len ) ) {
- DBGC ( tls, "TLS %p received overlength Server Hello Done\n",
- tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- /* Check that we are ready to send the Client Key Exchange */
- if ( tls->tx_state != TLS_TX_NONE ) {
- DBGC ( tls, "TLS %p received Server Hello Done while in "
- "TX state %d\n", tls, tls->tx_state );
- return -EIO;
- }
-
- /* Start sending the Client Key Exchange */
- tls->tx_state = TLS_TX_CLIENT_KEY_EXCHANGE;
-
- 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,
- void *data, size_t len ) {
-
- /* FIXME: Handle this properly */
- tls->tx_state = TLS_TX_DATA;
- ( void ) data;
- ( void ) len;
- 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,
- void *data, size_t len ) {
- struct {
- uint8_t type;
- uint8_t length[3];
- uint8_t payload[0];
- } __attribute__ (( packed )) *handshake = data;
- void *payload = &handshake->payload;
- size_t payload_len = tls_uint24 ( handshake->length );
- void *end = ( payload + payload_len );
- int rc;
-
- /* Sanity check */
- if ( end != ( data + len ) ) {
- DBGC ( tls, "TLS %p received overlength Handshake\n", tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL;
- }
-
- 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_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, len );
-
- return rc;
-}
-
-/**
- * Receive new 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_new_record ( struct tls_session *tls,
- unsigned int type, void *data, size_t len ) {
-
- switch ( type ) {
- case TLS_TYPE_CHANGE_CIPHER:
- return tls_new_change_cipher ( tls, data, len );
- case TLS_TYPE_ALERT:
- return tls_new_alert ( tls, data, len );
- case TLS_TYPE_HANDSHAKE:
- return tls_new_handshake ( tls, data, len );
- case TLS_TYPE_DATA:
- return xfer_deliver_raw ( &tls->plainstream.xfer, data, len );
- default:
- /* RFC4346 says that we should just ignore unknown
- * record types.
- */
- DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
- return 0;
- }
-}
-
-/******************************************************************************
- *
- * Record encryption/decryption
- *
- ******************************************************************************
- */
-
-/**
- * Calculate HMAC
- *
- * @v tls TLS session
- * @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_session *tls __unused,
- struct tls_cipherspec *cipherspec,
- uint64_t seq, struct tls_header *tlshdr,
- const void *data, size_t len, void *hmac ) {
- struct digest_algorithm *digest = cipherspec->digest;
- uint8_t digest_ctx[digest->ctxsize];
-
- hmac_init ( digest, digest_ctx, cipherspec->mac_secret,
- &digest->digestsize );
- seq = cpu_to_be64 ( seq );
- hmac_update ( digest, digest_ctx, &seq, sizeof ( seq ) );
- hmac_update ( digest, digest_ctx, tlshdr, sizeof ( *tlshdr ) );
- hmac_update ( digest, digest_ctx, data, len );
- hmac_final ( digest, digest_ctx, cipherspec->mac_secret,
- &digest->digestsize, 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.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.cipher->blocksize;
- size_t iv_len = blocksize;
- size_t mac_len = tls->tx_cipherspec.digest->digestsize;
- size_t padding_len;
- void *plaintext;
- void *iv;
- void *content;
- void *mac;
- void *padding;
-
- /* FIXME: TLSv1.1 has an explicit IV */
- iv_len = 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 */
- memset ( iv, 0, 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;
- void *plaintext = NULL;
- size_t plaintext_len;
- struct io_buffer *ciphertext = NULL;
- size_t ciphertext_len;
- size_t mac_len = cipherspec->digest->digestsize;
- uint8_t mac[mac_len];
- int rc;
-
- /* Construct header */
- plaintext_tlshdr.type = type;
- plaintext_tlshdr.version = htons ( TLS_VERSION_TLS_1_0 );
- plaintext_tlshdr.length = htons ( len );
-
- /* Calculate MAC */
- tls_hmac ( tls, cipherspec, tls->tx_seq, &plaintext_tlshdr,
- data, len, mac );
-
- /* Allocate and assemble plaintext struct */
- if ( is_stream_cipher ( cipherspec->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;
- 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.xfer,
- ciphertext_len );
- if ( ! ciphertext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "ciphertext\n", tls, ciphertext_len );
- rc = -ENOMEM;
- goto done;
- }
-
- /* Assemble ciphertext */
- tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
- tlshdr->type = type;
- tlshdr->version = htons ( TLS_VERSION_TLS_1_0 );
- tlshdr->length = htons ( plaintext_len );
- memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx,
- cipherspec->cipher->ctxsize );
- cipher_encrypt ( cipherspec->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 */
- rc = xfer_deliver_iob ( &tls->cipherstream.xfer, ciphertext );
- ciphertext = NULL;
- if ( rc != 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,
- tls->tx_cipherspec.cipher->ctxsize );
-
- done:
- free ( plaintext );
- free_iob ( ciphertext );
- return rc;
-}
-
-/**
- * Split stream-ciphered record into data and MAC portions
- *
- * @v tls TLS session
- * @v plaintext Plaintext record
- * @v plaintext_len Length of record
- * @ret data Data
- * @ret len Length of data
- * @ret digest MAC digest
- * @ret rc Return status code
- */
-static int tls_split_stream ( struct tls_session *tls,
- void *plaintext, size_t plaintext_len,
- void **data, size_t *len, void **digest ) {
- void *content;
- size_t content_len;
- void *mac;
- size_t mac_len;
-
- /* Decompose stream-ciphered data */
- mac_len = tls->rx_cipherspec.digest->digestsize;
- if ( plaintext_len < mac_len ) {
- DBGC ( tls, "TLS %p received underlength record\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- content_len = ( plaintext_len - mac_len );
- content = plaintext;
- mac = ( content + content_len );
-
- /* Fill in return values */
- *data = content;
- *len = content_len;
- *digest = mac;
-
- return 0;
-}
-
-/**
- * Split block-ciphered record into data and MAC portions
- *
- * @v tls TLS session
- * @v plaintext Plaintext record
- * @v plaintext_len Length of record
- * @ret data Data
- * @ret len Length of data
- * @ret digest MAC digest
- * @ret rc Return status code
- */
-static int tls_split_block ( struct tls_session *tls,
- void *plaintext, size_t plaintext_len,
- void **data, size_t *len,
- void **digest ) {
- void *iv;
- size_t iv_len;
- void *content;
- size_t content_len;
- void *mac;
- size_t mac_len;
- void *padding;
- size_t padding_len;
- unsigned int i;
-
- /* Decompose block-ciphered data */
- if ( plaintext_len < 1 ) {
- DBGC ( tls, "TLS %p received underlength record\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- iv_len = tls->rx_cipherspec.cipher->blocksize;
-
- /* FIXME: TLSv1.1 uses an explicit IV */
- iv_len = 0;
-
- mac_len = tls->rx_cipherspec.digest->digestsize;
- padding_len = *( ( uint8_t * ) ( plaintext + plaintext_len - 1 ) );
- if ( plaintext_len < ( iv_len + mac_len + padding_len + 1 ) ) {
- DBGC ( tls, "TLS %p received underlength record\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- content_len = ( plaintext_len - iv_len - mac_len - padding_len - 1 );
- iv = plaintext;
- content = ( iv + iv_len );
- mac = ( content + content_len );
- padding = ( mac + mac_len );
-
- /* Verify padding bytes */
- for ( i = 0 ; i < padding_len ; i++ ) {
- if ( *( ( uint8_t * ) ( padding + i ) ) != padding_len ) {
- DBGC ( tls, "TLS %p received bad padding\n", tls );
- DBGC_HD ( tls, plaintext, plaintext_len );
- return -EINVAL;
- }
- }
-
- /* Fill in return values */
- *data = content;
- *len = content_len;
- *digest = mac;
-
- return 0;
-}
-
-/**
- * Receive new ciphertext record
- *
- * @v tls TLS session
- * @v tlshdr Record header
- * @v ciphertext Ciphertext record
- * @ret rc Return status code
- */
-static int tls_new_ciphertext ( struct tls_session *tls,
- struct tls_header *tlshdr, void *ciphertext ) {
- struct tls_header plaintext_tlshdr;
- struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
- size_t record_len = ntohs ( tlshdr->length );
- void *plaintext = NULL;
- void *data;
- size_t len;
- void *mac;
- size_t mac_len = cipherspec->digest->digestsize;
- uint8_t verify_mac[mac_len];
- int rc;
-
- /* Allocate buffer for plaintext */
- plaintext = malloc ( record_len );
- if ( ! plaintext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "decryption buffer\n", tls, record_len );
- rc = -ENOMEM;
- goto done;
- }
-
- /* Decrypt the record */
- cipher_decrypt ( cipherspec->cipher, cipherspec->cipher_ctx,
- ciphertext, plaintext, record_len );
-
- /* Split record into content and MAC */
- if ( is_stream_cipher ( cipherspec->cipher ) ) {
- if ( ( rc = tls_split_stream ( tls, plaintext, record_len,
- &data, &len, &mac ) ) != 0 )
- goto done;
- } else {
- if ( ( rc = tls_split_block ( tls, plaintext, record_len,
- &data, &len, &mac ) ) != 0 )
- goto done;
- }
-
- /* Verify MAC */
- plaintext_tlshdr.type = tlshdr->type;
- plaintext_tlshdr.version = tlshdr->version;
- plaintext_tlshdr.length = htons ( len );
- tls_hmac ( tls, cipherspec, tls->rx_seq, &plaintext_tlshdr,
- data, len, verify_mac);
- if ( memcmp ( mac, verify_mac, mac_len ) != 0 ) {
- DBGC ( tls, "TLS %p failed MAC verification\n", tls );
- DBGC_HD ( tls, plaintext, record_len );
- goto done;
- }
-
- DBGC2 ( tls, "Received plaintext data:\n" );
- DBGC2_HD ( tls, data, len );
-
- /* Process plaintext record */
- if ( ( rc = tls_new_record ( tls, tlshdr->type, data, len ) ) != 0 )
- goto done;
-
- rc = 0;
- done:
- free ( plaintext );
- return rc;
-}
-
-/******************************************************************************
- *
- * Plaintext stream operations
- *
- ******************************************************************************
- */
-
-/**
- * Close interface
- *
- * @v xfer Plainstream data transfer interface
- * @v rc Reason for close
- */
-static void tls_plainstream_close ( struct xfer_interface *xfer, int rc ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, plainstream.xfer );
-
- tls_close ( tls, rc );
-}
-
-/**
- * Check flow control window
- *
- * @v xfer Plainstream data transfer interface
- * @ret len Length of window
- */
-static size_t tls_plainstream_window ( struct xfer_interface *xfer ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, plainstream.xfer );
-
- /* Block window unless we are ready to accept data */
- if ( tls->tx_state != TLS_TX_DATA )
- return 0;
-
- return filter_window ( xfer );
-}
-
-/**
- * Deliver datagram as raw data
- *
- * @v xfer Plainstream data transfer interface
- * @v data Data buffer
- * @v len Length of data buffer
- * @ret rc Return status code
- */
-static int tls_plainstream_deliver_raw ( struct xfer_interface *xfer,
- const void *data, size_t len ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, plainstream.xfer );
-
- /* Refuse unless we are ready to accept data */
- if ( tls->tx_state != TLS_TX_DATA )
- return -ENOTCONN;
-
- return tls_send_plaintext ( tls, TLS_TYPE_DATA, data, len );
-}
-
-/** TLS plaintext stream operations */
-static struct xfer_interface_operations tls_plainstream_operations = {
- .close = tls_plainstream_close,
- .vredirect = ignore_xfer_vredirect,
- .window = tls_plainstream_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = tls_plainstream_deliver_raw,
-};
-
-/******************************************************************************
- *
- * Ciphertext stream operations
- *
- ******************************************************************************
- */
-
-/**
- * Close interface
- *
- * @v xfer Plainstream data transfer interface
- * @v rc Reason for close
- */
-static void tls_cipherstream_close ( struct xfer_interface *xfer, int rc ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, cipherstream.xfer );
-
- tls_close ( tls, rc );
-}
-
-/**
- * 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 );
-
- /* Allocate data buffer now that we know the length */
- assert ( tls->rx_data == NULL );
- tls->rx_data = malloc ( data_len );
- if ( ! tls->rx_data ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes "
- "for receive buffer\n", tls, data_len );
- return -ENOMEM;
- }
-
- /* Move to data state */
- tls->rx_state = TLS_RX_DATA;
-
- return 0;
-}
-
-/**
- * Handle received TLS data payload
- *
- * @v tls TLS session
- * @ret rc Returned status code
- */
-static int tls_newdata_process_data ( struct tls_session *tls ) {
- int rc;
-
- /* 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;
-
- /* Free data buffer */
- free ( tls->rx_data );
- tls->rx_data = NULL;
-
- /* Return to header state */
- tls->rx_state = TLS_RX_HEADER;
-
- return 0;
-}
-
-/**
- * Receive new ciphertext
- *
- * @v app Stream application
- * @v data Data received
- * @v len Length of received data
- * @ret rc Return status code
- */
-static int tls_cipherstream_deliver_raw ( struct xfer_interface *xfer,
- const void *data, size_t len ) {
- struct tls_session *tls =
- container_of ( xfer, struct tls_session, cipherstream.xfer );
- size_t frag_len;
- void *buf;
- size_t buf_len;
- int ( * process ) ( struct tls_session *tls );
- int rc;
-
- while ( len ) {
- /* Select buffer according to current state */
- switch ( tls->rx_state ) {
- case TLS_RX_HEADER:
- buf = &tls->rx_header;
- buf_len = sizeof ( tls->rx_header );
- process = tls_newdata_process_header;
- break;
- case TLS_RX_DATA:
- buf = tls->rx_data;
- buf_len = ntohs ( tls->rx_header.length );
- process = tls_newdata_process_data;
- break;
- default:
- assert ( 0 );
- return -EINVAL;
- }
-
- /* Copy data portion to buffer */
- frag_len = ( buf_len - tls->rx_rcvd );
- if ( frag_len > len )
- frag_len = len;
- memcpy ( ( buf + tls->rx_rcvd ), data, frag_len );
- tls->rx_rcvd += frag_len;
- data += frag_len;
- len -= frag_len;
-
- /* Process data if buffer is now full */
- if ( tls->rx_rcvd == buf_len ) {
- if ( ( rc = process ( tls ) ) != 0 ) {
- tls_close ( tls, rc );
- return rc;
- }
- tls->rx_rcvd = 0;
- }
- }
-
- return 0;
-}
-
-/** TLS ciphertext stream operations */
-static struct xfer_interface_operations tls_cipherstream_operations = {
- .close = tls_cipherstream_close,
- .vredirect = xfer_vreopen,
- .window = filter_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = tls_cipherstream_deliver_raw,
-};
-
-/******************************************************************************
- *
- * Controlling process
- *
- ******************************************************************************
- */
-
-/**
- * TLS TX state machine
- *
- * @v process TLS process
- */
-static void tls_step ( struct process *process ) {
- struct tls_session *tls =
- container_of ( process, struct tls_session, process );
- int rc;
-
- /* Wait for cipherstream to become ready */
- if ( ! xfer_window ( &tls->cipherstream.xfer ) )
- return;
-
- switch ( tls->tx_state ) {
- case TLS_TX_NONE:
- /* Nothing to do */
- break;
- case 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_state = TLS_TX_NONE;
- break;
- case TLS_TX_CLIENT_KEY_EXCHANGE:
- /* Send Client Key Exchange */
- if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) {
- DBGC ( tls, "TLS %p could send Client Key Exchange: "
- "%s\n", tls, strerror ( rc ) );
- goto err;
- }
- tls->tx_state = TLS_TX_CHANGE_CIPHER;
- break;
- case 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_state = TLS_TX_FINISHED;
- break;
- case 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_state = TLS_TX_NONE;
- break;
- case TLS_TX_DATA:
- /* Nothing to do */
- break;
- default:
- assert ( 0 );
- }
-
- return;
-
- err:
- tls_close ( tls, rc );
-}
-
-/******************************************************************************
- *
- * Instantiator
- *
- ******************************************************************************
- */
-
-int add_tls ( struct xfer_interface *xfer, struct xfer_interface **next ) {
- struct tls_session *tls;
-
- /* Allocate and initialise TLS structure */
- tls = malloc ( sizeof ( *tls ) );
- if ( ! tls )
- return -ENOMEM;
- memset ( tls, 0, sizeof ( *tls ) );
- tls->refcnt.free = free_tls;
- filter_init ( &tls->plainstream, &tls_plainstream_operations,
- &tls->cipherstream, &tls_cipherstream_operations,
- &tls->refcnt );
- 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 = 0;
- tls_generate_random ( &tls->client_random.random,
- ( sizeof ( tls->client_random.random ) ) );
- tls->pre_master_secret.version = htons ( TLS_VERSION_TLS_1_0 );
- tls_generate_random ( &tls->pre_master_secret.random,
- ( sizeof ( tls->pre_master_secret.random ) ) );
- digest_init ( &md5_algorithm, tls->handshake_md5_ctx );
- digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx );
- tls->tx_state = TLS_TX_CLIENT_HELLO;
- process_init ( &tls->process, tls_step, &tls->refcnt );
-
- /* Attach to parent interface, mortalise self, and return */
- xfer_plug_plug ( &tls->plainstream.xfer, xfer );
- *next = &tls->cipherstream.xfer;
- ref_put ( &tls->refcnt );
- return 0;
-}
-
diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c
deleted file mode 100644
index 771655e0..00000000
--- a/gpxe/src/net/udp.c
+++ /dev/null
@@ -1,463 +0,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <byteswap.h>
-#include <errno.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/uri.h>
-#include <gpxe/udp.h>
-
-/** @file
- *
- * UDP protocol
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/**
- * A UDP connection
- *
- */
-struct udp_connection {
- /** Reference counter */
- struct refcnt refcnt;
- /** List of UDP connections */
- struct list_head list;
-
- /** Data transfer interface */
- struct xfer_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 xfer_interface_operations udp_xfer_operations;
-struct tcpip_protocol udp_protocol;
-
-/**
- * Bind UDP connection to local port
- *
- * @v udp UDP connection
- * @ret rc Return status code
- *
- * Opens the UDP connection and binds to the specified local port. If
- * no local port is specified, the first available port will be used.
- */
-static int udp_bind ( struct udp_connection *udp ) {
- struct udp_connection *existing;
- static uint16_t try_port = 1023;
-
- /* If no port specified, find the first available port */
- if ( ! udp->local.st_port ) {
- while ( try_port ) {
- try_port++;
- if ( try_port < 1024 )
- continue;
- udp->local.st_port = htons ( try_port );
- if ( udp_bind ( udp ) == 0 )
- return 0;
- }
- return -EADDRINUSE;
- }
-
- /* Attempt bind to local port */
- list_for_each_entry ( existing, &udp_conns, list ) {
- if ( existing->local.st_port == udp->local.st_port ) {
- DBGC ( udp, "UDP %p could not bind: port %d in use\n",
- udp, ntohs ( udp->local.st_port ) );
- return -EADDRINUSE;
- }
- }
-
- /* Add to UDP connection list */
- DBGC ( udp, "UDP %p bound to port %d\n",
- udp, ntohs ( udp->local.st_port ) );
-
- return 0;
-}
-
-/**
- * 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 xfer_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 rc;
-
- /* Allocate and initialise structure */
- udp = zalloc ( sizeof ( *udp ) );
- if ( ! udp )
- return -ENOMEM;
- DBGC ( udp, "UDP %p allocated\n", udp );
- xfer_init ( &udp->xfer, &udp_xfer_operations, &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 ) {
- if ( ( rc = udp_bind ( udp ) ) != 0 )
- goto err;
- }
-
- /* Attach parent interface, transfer reference to connection
- * list and return
- */
- xfer_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 xfer_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 xfer_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 */
- xfer_nullify ( &udp->xfer );
- xfer_close ( &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, UDP_MAX_HLEN ) ) != 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 */
- DBGC ( 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 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 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 */
- DBGC ( 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_iob_meta ( &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
- *
- ***************************************************************************
- */
-
-/**
- * Close interface
- *
- * @v xfer Data transfer interface
- * @v rc Reason for close
- */
-static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) {
- struct udp_connection *udp =
- container_of ( xfer, struct udp_connection, xfer );
-
- /* Close connection */
- udp_close ( udp, rc );
-}
-
-/**
- * Allocate I/O buffer for UDP
- *
- * @v xfer Data transfer interface
- * @v len Payload size
- * @ret iobuf I/O buffer, or NULL
- */
-static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer,
- size_t len ) {
- struct udp_connection *udp =
- container_of ( xfer, struct udp_connection, xfer );
- struct io_buffer *iobuf;
-
- iobuf = alloc_iob ( UDP_MAX_HLEN + len );
- if ( ! iobuf ) {
- DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n",
- udp, len );
- return NULL;
- }
- iob_reserve ( iobuf, UDP_MAX_HLEN );
- return iobuf;
-}
-
-/**
- * Deliver datagram as I/O buffer
- *
- * @v xfer Data transfer interface
- * @v iobuf Datagram I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- */
-static int udp_xfer_deliver_iob ( struct xfer_interface *xfer,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- struct udp_connection *udp =
- container_of ( xfer, struct udp_connection, xfer );
-
- /* Transmit data, if possible */
- udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ),
- ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev );
-
- return 0;
-}
-
-/** UDP data transfer interface operations */
-static struct xfer_interface_operations udp_xfer_operations = {
- .close = udp_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = unlimited_xfer_window,
- .alloc_iob = udp_alloc_iob,
- .deliver_iob = udp_xfer_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/***************************************************************************
- *
- * Openers
- *
- ***************************************************************************
- */
-
-/** UDP socket opener */
-struct socket_opener udp_socket_opener __socket_opener = {
- .semantics = UDP_SOCK_DGRAM,
- .family = AF_INET,
- .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 xfer_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/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c
deleted file mode 100644
index ce2c8207..00000000
--- a/gpxe/src/net/udp/dhcp.c
+++ /dev/null
@@ -1,1587 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <errno.h>
-#include <assert.h>
-#include <byteswap.h>
-#include <gpxe/if_ether.h>
-#include <gpxe/netdevice.h>
-#include <gpxe/device.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/job.h>
-#include <gpxe/retry.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/ip.h>
-#include <gpxe/uuid.h>
-#include <gpxe/timer.h>
-#include <gpxe/settings.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/dhcpopts.h>
-#include <gpxe/dhcppkt.h>
-#include <gpxe/features.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_WORD ( 0 ),
- DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ),
- DHCP_VENDOR_CLASS_ID,
- DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':',
- 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':',
- 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ),
- DHCP_USER_CLASS_ID,
- DHCP_STRING ( 'g', '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_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ),
- DHCP_END
-};
-
-/** Version number feature */
-FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH );
-
-/** DHCP server address setting */
-struct setting dhcp_server_setting __setting = {
- .name = "dhcp-server",
- .description = "DHCP server address",
- .tag = DHCP_SERVER_IDENTIFIER,
- .type = &setting_type_ipv4,
-};
-
-/** DHCP user class setting */
-struct setting user_class_setting __setting = {
- .name = "user-class",
- .description = "User class identifier",
- .tag = DHCP_USER_CLASS_ID,
- .type = &setting_type_string,
-};
-
-/** Use cached network settings */
-struct setting use_cached_setting __setting = {
- .name = "use-cached",
- .description = "Use cached network settings",
- .tag = DHCP_EB_USE_CACHED,
- .type = &setting_type_uint8,
-};
-
-/**
- * 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>";
- }
-}
-
-/**
- * Calculate DHCP transaction ID for a network device
- *
- * @v netdev Network device
- * @ret xid DHCP XID
- *
- * Extract the least significant bits of the hardware address for use
- * as the transaction ID.
- */
-static uint32_t dhcp_xid ( struct net_device *netdev ) {
- uint32_t xid;
-
- memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len
- - sizeof ( xid ) ), sizeof ( xid ) );
- return xid;
-}
-
-/****************************************************************************
- *
- * 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
- */
- void ( * rx ) ( struct dhcp_session *dhcp,
- struct dhcp_packet *dhcppkt,
- struct sockaddr_in *peer,
- uint8_t msgtype, struct in_addr server_id );
- /** Handle timer expiry
- *
- * @v dhcp DHCP session
- */
- void ( * expired ) ( struct dhcp_session *dhcp );
- /** Transmitted message type */
- uint8_t tx_msgtype;
- /** Apply minimum timeout */
- uint8_t apply_min_timeout;
-};
-
-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;
-
-/** DHCP offer is valid for IP lease */
-#define DHCP_OFFER_IP 1
-
-/** DHCP offer is valid for PXE options */
-#define DHCP_OFFER_PXE 2
-
-/** A DHCP offer */
-struct dhcp_offer {
- /** IP address of server granting offer */
- struct in_addr server;
-
- /** IP address being offered, or 0.0.0.0 for a pure proxy */
- struct in_addr ip;
-
- /** DHCP packet containing PXE options; NULL if missing or proxied */
- struct dhcp_packet *pxe;
-
- /** Valid uses for this offer, a combination of DHCP_OFFER bits */
- uint8_t valid;
-
- /** Priority of this offer */
- int8_t priority;
-
- /** Whether to ignore PXE DHCP extensions */
- uint8_t no_pxedhcp;
-};
-
-/** Maximum number of DHCP offers to queue */
-#define DHCP_MAX_OFFERS 6
-
-/** A DHCP session */
-struct dhcp_session {
- /** Reference counter */
- struct refcnt refcnt;
- /** Job control interface */
- struct job_interface job;
- /** Data transfer interface */
- struct xfer_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;
-
- /** 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;
- /** Start time of the current state (in ticks) */
- unsigned long start;
-
- /** DHCP offer just requested */
- struct dhcp_offer *current_offer;
- /** List of DHCP offers received */
- struct dhcp_offer offers[DHCP_MAX_OFFERS];
-};
-
-/**
- * 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 );
- int i;
-
- for ( i = 0 ; i < DHCP_MAX_OFFERS ; i++ ) {
- if ( dhcp->offers[i].pxe )
- dhcppkt_put ( dhcp->offers[i].pxe );
- }
-
- netdev_put ( dhcp->netdev );
- 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 ) {
-
- /* Block futher incoming messages */
- job_nullify ( &dhcp->job );
- xfer_nullify ( &dhcp->xfer );
-
- /* Stop retry timer */
- stop_timer ( &dhcp->timer );
-
- /* Free resources and close interfaces */
- xfer_close ( &dhcp->xfer, rc );
- job_done ( &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 );
- dhcp->timer.min_timeout =
- ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 );
- dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT;
- start_timer_nodelay ( &dhcp->timer );
-}
-
-/**
- * Determine next DHCP offer to try
- *
- * @v dhcp DHCP session
- * @v type DHCP offer type
- * @ret offer Next DHCP offer to try
- *
- * Offers are ranked by priority, then by completeness (combined
- * IP+PXE are tried before @a type alone), then by order of receipt.
- */
-static struct dhcp_offer * dhcp_next_offer ( struct dhcp_session *dhcp,
- uint8_t type ) {
-
- struct dhcp_offer *offer;
- struct dhcp_offer *best = NULL;
-
- for ( offer = dhcp->offers ; offer < dhcp->offers + DHCP_MAX_OFFERS ;
- offer++ ) {
- if ( ( offer->valid & type ) &&
- ( ( best == NULL ) ||
- ( offer->priority > best->priority ) ||
- ( ( offer->priority == best->priority ) &&
- ( offer->valid & ~best->valid ) ) ) )
- best = offer;
- }
-
- return best;
-}
-
-/****************************************************************************
- *
- * 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 DHCPOFFER during any state
- *
- * @v dhcp DHCP session
- * @v dhcppkt DHCP packet
- * @v peer DHCP server address
- * @v msgtype DHCP message type
- * @v server_id DHCP server ID
- */
-static void dhcp_rx_offer ( struct dhcp_session *dhcp,
- struct dhcp_packet *dhcppkt,
- struct sockaddr_in *peer, uint8_t msgtype,
- struct in_addr server_id ) {
- char vci[9]; /* "PXEClient" */
- int vci_len;
- int has_pxeclient;
- int pxeopts_len;
- int has_pxeopts;
- struct dhcp_offer *offer;
- int i;
-
- 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 )
- DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
-
- /* Identify offered IP address */
- if ( dhcppkt->dhcphdr->yiaddr.s_addr )
- DBGC ( dhcp, " for %s", inet_ntoa ( dhcppkt->dhcphdr->yiaddr ));
-
- /* Enqueue an offer to be filled in */
- for ( i = 0 ; i < DHCP_MAX_OFFERS ; i++ ) {
- if ( dhcp->offers[i].server.s_addr == server_id.s_addr ) {
- DBGC ( dhcp, " dup\n" );
- return;
- }
-
- if ( ! dhcp->offers[i].valid )
- break;
- }
- if ( i == DHCP_MAX_OFFERS ) {
- DBGC ( dhcp, " dropped\n" );
- return;
- }
-
- offer = &dhcp->offers[i];
- offer->server = server_id;
- offer->ip = dhcppkt->dhcphdr->yiaddr;
-
- /* 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 ));
-
- /* Identify presence of PXE-specific options */
- pxeopts_len = dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU, NULL, 0 );
- has_pxeopts = ( pxeopts_len >= 0 );
- if ( has_pxeclient )
- DBGC ( dhcp, "%s", ( has_pxeopts ? " pxe" : " proxy" ) );
-
- if ( has_pxeclient && has_pxeopts ) {
- /* Save reference to packet for future use */
- if ( offer->pxe )
- dhcppkt_put ( offer->pxe );
- offer->pxe = dhcppkt_get ( dhcppkt );
- }
-
- /* Identify priority */
- dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &offer->priority,
- sizeof ( offer->priority ) );
- if ( offer->priority )
- DBGC ( dhcp, " pri %d", offer->priority );
-
- /* Identify ignore-PXE flag */
- dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &offer->no_pxedhcp,
- sizeof ( offer->no_pxedhcp ) );
- if ( offer->no_pxedhcp )
- DBGC ( dhcp, " nopxe" );
- DBGC ( dhcp, "\n" );
-
- /* Determine roles this offer can fill */
- if ( offer->ip.s_addr &&
- ( peer->sin_port == htons ( BOOTPS_PORT ) ) &&
- ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) )
- offer->valid |= DHCP_OFFER_IP;
-
- if ( has_pxeclient && ( msgtype == DHCPOFFER ) )
- offer->valid |= DHCP_OFFER_PXE;
-}
-
-/**
- * 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
- */
-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 ) {
- unsigned long elapsed;
- struct dhcp_offer *ip_offer;
-
- dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
-
- /* 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 */
- ip_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
- if ( ! ip_offer )
- return;
-
- /* If we can't yet transition to DHCPREQUEST, do nothing */
- elapsed = ( currticks() - dhcp->start );
- if ( ! ( ip_offer->no_pxedhcp ||
- dhcp_next_offer ( dhcp, DHCP_OFFER_PXE ) ||
- ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) )
- 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 );
-
- /* Give up waiting for ProxyDHCP before we reach the failure point */
- if ( dhcp_next_offer ( dhcp, DHCP_OFFER_IP ) &&
- ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) {
- 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,
- .apply_min_timeout = 1,
-};
-
-/**
- * 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;
- struct dhcp_offer *offer;
-
- offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_IP );
-
- DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d",
- dhcp, inet_ntoa ( offer->server ), BOOTPS_PORT );
- DBGC ( dhcp, " for %s\n", inet_ntoa ( offer->ip ) );
-
- /* Set server ID */
- if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
- &offer->server,
- sizeof ( offer->server ) ) ) != 0 )
- return rc;
-
- /* Set requested IP address */
- if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS,
- &offer->ip, sizeof ( offer->ip ) ) ) != 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
- */
-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 ip;
- struct settings *parent;
- int rc;
- struct dhcp_offer *pxe_offer;
-
- if ( msgtype == DHCPOFFER ) {
- dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
- if ( dhcp_next_offer ( dhcp, DHCP_OFFER_IP ) !=
- dhcp->current_offer ) {
- /* Restart due to higher-priority offer received */
- DBGC ( dhcp, "DHCP %p re-requesting\n", dhcp );
- dhcp_set_state ( dhcp, &dhcp_state_request );
- }
- return;
- }
-
- 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 )
- DBGC ( dhcp, " (%s)", inet_ntoa ( server_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->current_offer->server.s_addr )
- return;
-
- /* Record assigned address */
- dhcp->local.sin_addr = ip;
-
- /* Register settings */
- parent = netdev_settings ( dhcp->netdev );
- if ( ( rc = register_settings ( &dhcppkt->settings, parent ) ) != 0 ){
- DBGC ( dhcp, "DHCP %p could not register settings: %s\n",
- dhcp, strerror ( rc ) );
- dhcp_finished ( dhcp, rc );
- return;
- }
-
- /* Locate best source of PXE settings */
- pxe_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
-
- if ( ( ! pxe_offer ) || /* No PXE available */
- /* IP offer instructs us to ignore PXE */
- dhcp->current_offer->no_pxedhcp ||
- /* PXE settings already registered with IP offer */
- ( ( dhcp->current_offer == pxe_offer ) && ( pxe_offer->pxe ) ) ) {
-
- /* Terminate DHCP */
- dhcp_finished ( dhcp, 0 );
-
- } else if ( pxe_offer->pxe ) {
- /* Register PXE settings and terminate DHCP */
- pxe_offer->pxe->settings.name = PROXYDHCP_SETTINGS_NAME;
- if ( ( rc = register_settings ( &pxe_offer->pxe->settings,
- NULL ) ) != 0 ) {
- DBGC ( dhcp, "DHCP %p could not register settings: "
- "%s\n", dhcp, strerror ( rc ) );
- }
- dhcp_finished ( dhcp, rc );
- } else {
- /* Start ProxyDHCP */
- dhcp_set_state ( dhcp, &dhcp_state_proxy );
- }
-}
-
-/**
- * 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,
- .apply_min_timeout = 0,
-};
-
-/**
- * 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;
- struct dhcp_offer *offer;
-
- offer = dhcp->current_offer = dhcp_next_offer ( dhcp, DHCP_OFFER_PXE );
-
- DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n", dhcp,
- inet_ntoa ( offer->server ), PXE_PORT );
-
- /* Set server ID */
- if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER,
- &offer->server,
- sizeof ( offer->server ) ) ) != 0 )
- return rc;
-
- /* Set server address */
- peer->sin_addr = offer->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
- */
-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 ) {
- int rc;
-
- /* Enqueue last-minute DHCPOFFERs for use in case of failure */
- if ( peer->sin_port == htons ( BOOTPS_PORT ) &&
- msgtype == DHCPOFFER ) {
- dhcp_rx_offer ( dhcp, dhcppkt, peer, msgtype, server_id );
- return;
- }
-
- 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 )
- DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) );
- DBGC ( dhcp, "\n" );
-
- /* Filter out unacceptable responses */
- if ( peer->sin_port != htons ( PXE_PORT ) )
- return;
- if ( msgtype != DHCPACK && msgtype != DHCPOFFER )
- return;
- if ( server_id.s_addr /* Linux PXE server omits server ID */ &&
- ( server_id.s_addr != dhcp->current_offer->server.s_addr ) )
- return;
-
- /* Register settings */
- dhcppkt->settings.name = PROXYDHCP_SETTINGS_NAME;
- if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 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 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 > PROXYDHCP_MAX_TIMEOUT ) {
-
- /* Mark failed offer as unsuitable for ProxyDHCP */
- dhcp->current_offer->valid &= ~DHCP_OFFER_PXE;
-
- /* Prefer not to use only half of a PXE+IP offer if we
- * have other offers available
- */
- dhcp->current_offer->priority = -1;
-
- /* If we have any other PXE offers we can try, go back
- * to DHCPREQUEST (since they might not be proxied
- * offers, or might be coupled to a new IP address).
- * We should probably DHCPRELEASE our old IP, but the
- * standard does not require it.
- */
- if ( dhcp_next_offer ( dhcp, DHCP_OFFER_PXE ) ) {
- dhcp->local.sin_addr.s_addr = 0;
- dhcp_set_state ( dhcp, &dhcp_state_request );
- return;
- }
-
- /* No possibilities left; finish without PXE options */
- 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,
- .apply_min_timeout = 0,
-};
-
-/**
- * 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
- */
-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 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 )
- DBGC ( dhcp, " (%s)", inet_ntoa ( server_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, ( server_id.s_addr ?
- server_id : peer->sin_addr ) ) )
- return;
-
- /* Register settings */
- dhcppkt->settings.name = PXEBS_SETTINGS_NAME;
- if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 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 ) {
- 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,
- .apply_min_timeout = 1,
-};
-
-/****************************************************************************
- *
- * Packet construction
- *
- */
-
-/**
- * Construct DHCP client hardware address field and broadcast flag
- *
- * @v netdev Network device
- * @v hlen DHCP hardware address length to fill in
- * @v flags DHCP flags to fill in
- * @ret chaddr DHCP client hardware address
- */
-void * dhcp_chaddr ( struct net_device *netdev, uint8_t *hlen,
- uint16_t *flags ) {
- struct ll_protocol *ll_protocol = netdev->ll_protocol;
- typeof ( ( ( struct dhcphdr * ) NULL )->chaddr ) chaddr;
-
- /* If the link-layer address cannot fit into the chaddr field
- * (as is the case for IPoIB) then try using the hardware
- * address instead. If we do this, set the broadcast flag,
- * since chaddr then does not represent a valid link-layer
- * address for the return path.
- *
- * If even the hardware address is too large, use an empty
- * chaddr field and set the broadcast flag.
- *
- * This goes against RFC4390, but RFC4390 mandates that we use
- * a DHCP client identifier that conforms with RFC4361, which
- * we cannot do without either persistent (NIC-independent)
- * storage, or by eliminating the hardware address completely
- * from the DHCP packet, which seems unfriendly to users.
- */
- if ( ( *hlen = ll_protocol->ll_addr_len ) <= sizeof ( chaddr ) ) {
- return netdev->ll_addr;
- }
- *flags = htons ( BOOTP_FL_BROADCAST );
- if ( ( *hlen = ll_protocol->hw_addr_len ) <= sizeof ( chaddr ) ) {
- return netdev->hw_addr;
- } else {
- *hlen = 0;
- return NULL;
- }
-}
-
-/**
- * Create a DHCP packet
- *
- * @v dhcppkt DHCP packet structure to fill in
- * @v netdev Network device
- * @v msgtype DHCP message type
- * @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,
- const void *options, size_t options_len,
- void *data, size_t max_len ) {
- struct dhcphdr *dhcphdr = data;
- void *chaddr;
- int rc;
-
- /* Sanity check */
- if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) )
- return -ENOSPC;
-
- /* Initialise DHCP packet content */
- memset ( dhcphdr, 0, max_len );
- dhcphdr->xid = dhcp_xid ( netdev );
- dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
- dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
- dhcphdr->op = dhcp_op[msgtype];
- chaddr = dhcp_chaddr ( netdev, &dhcphdr->hlen, &dhcphdr->flags );
- memcpy ( dhcphdr->chaddr, chaddr, dhcphdr->hlen );
- memcpy ( dhcphdr->options, options, options_len );
-
- /* 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 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,
- 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;
- ssize_t len;
- int rc;
-
- /* Create DHCP packet */
- if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype,
- dhcp_request_options_data,
- sizeof ( dhcp_request_options_data ),
- data, max_len ) ) != 0 ) {
- DBG ( "DHCP could not create DHCP packet: %s\n",
- strerror ( rc ) );
- return rc;
- }
-
- /* 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 ) );
- return rc;
- }
-
- /* Add options to identify the network device */
- fetch_setting ( &netdev->settings.settings, &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 ) );
- return rc;
- }
-
- /* 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 ) );
- return rc;
- }
-
- /* Add client UUID, if we have one. Required for PXE. */
- client_uuid.type = DHCP_CLIENT_UUID_TYPE;
- if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
- &client_uuid.uuid ) ) >= 0 ) {
- 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 ) );
- return rc;
- }
- }
-
- /* Add user class, if we have one. */
- if ( ( len = fetch_setting_len ( NULL, &user_class_setting ) ) >= 0 ) {
- char user_class[len];
- fetch_setting ( NULL, &user_class_setting, user_class,
- sizeof ( user_class ) );
- if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID,
- &user_class,
- sizeof ( user_class ) ) ) != 0 ) {
- DBG ( "DHCP could not set user class: %s\n",
- strerror ( rc ) );
- return rc;
- }
- }
-
- return 0;
-}
-
-/****************************************************************************
- *
- * 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->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;
- }
-
- /* 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 );
- if ( ( rc = xfer_deliver_iob_meta ( &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 xfer Data transfer interface
- * @v iobuf I/O buffer
- * @v meta Transfer metadata
- * @ret rc Return status code
- */
-static int dhcp_deliver_iob ( struct xfer_interface *xfer,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- struct dhcp_session *dhcp =
- container_of ( xfer, struct dhcp_session, xfer );
- 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 };
- 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 ) );
-
- /* Check for matching transaction ID */
- if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
- 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;
- };
-
- /* Handle packet based on current state */
- dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id );
-
- err_xid:
- dhcppkt_put ( dhcppkt );
- err_alloc_dhcppkt:
- err_no_src:
- free_iob ( iobuf );
- return rc;
-}
-
-/** DHCP data transfer interface operations */
-static struct xfer_interface_operations dhcp_xfer_operations = {
- .close = ignore_xfer_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = dhcp_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * 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
- *
- */
-
-/**
- * Handle kill() event received via job control interface
- *
- * @v job DHCP job control interface
- */
-static void dhcp_job_kill ( struct job_interface *job ) {
- struct dhcp_session *dhcp =
- container_of ( job, struct dhcp_session, job );
-
- /* Terminate DHCP session */
- dhcp_finished ( dhcp, -ECANCELED );
-}
-
-/** DHCP job control interface operations */
-static struct job_interface_operations dhcp_job_operations = {
- .done = ignore_job_done,
- .kill = dhcp_job_kill,
- .progress = ignore_job_progress,
-};
-
-/****************************************************************************
- *
- * 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, or positive if cached
- *
- * Starts DHCP on the specified network device. If successful, the
- * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as
- * option sources.
- *
- * On a return of 0, a background job has been started to perform the
- * DHCP request. Any nonzero return means the job has not been
- * started; a positive return value indicates the success condition of
- * having fetched the appropriate data from cached information.
- */
-int start_dhcp ( struct job_interface *job, struct net_device *netdev ) {
- struct dhcp_session *dhcp;
- int rc;
-
- /* Check for cached DHCP information */
- get_cached_dhcpack();
- if ( fetch_uintz_setting ( NULL, &use_cached_setting ) ) {
- DBG ( "DHCP using cached network settings\n" );
- return 1;
- }
-
- /* Allocate and initialise structure */
- dhcp = zalloc ( sizeof ( *dhcp ) );
- if ( ! dhcp )
- return -ENOMEM;
- dhcp->refcnt.free = dhcp_free;
- job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
- xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
- dhcp->netdev = netdev_get ( netdev );
- dhcp->local.sin_family = AF_INET;
- dhcp->local.sin_port = htons ( BOOTPC_PORT );
- dhcp->timer.expired = dhcp_timer_expired;
-
- /* 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 */
- job_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 job_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_setting_len ( NULL, &pxe_boot_servers_setting );
- 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;
- dhcp->refcnt.free = dhcp_free;
- job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
- xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &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 );
- dhcp->timer.expired = dhcp_timer_expired;
-
- /* 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_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 */
- job_plug_plug ( &dhcp->job, job );
- ref_put ( &dhcp->refcnt );
- return 0;
-
- err:
- dhcp_finished ( dhcp, rc );
- ref_put ( &dhcp->refcnt );
- return rc;
-}
diff --git a/gpxe/src/net/udp/dns.c b/gpxe/src/net/udp/dns.c
deleted file mode 100644
index f94094aa..00000000
--- a/gpxe/src/net/udp/dns.c
+++ /dev/null
@@ -1,603 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <byteswap.h>
-#include <gpxe/refcnt.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/resolv.h>
-#include <gpxe/retry.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/settings.h>
-#include <gpxe/features.h>
-#include <gpxe/dns.h>
-
-/** @file
- *
- * DNS protocol
- *
- */
-
-FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
-
-/** The DNS server */
-static struct sockaddr_tcpip nameserver = {
- .st_port = htons ( DNS_PORT ),
-};
-
-/** The local domain */
-static char *localdomain;
-
-/** A DNS request */
-struct dns_request {
- /** Reference counter */
- struct refcnt refcnt;
- /** Name resolution interface */
- struct resolv_interface resolv;
- /** Data transfer interface */
- struct xfer_interface socket;
- /** Retry timer */
- struct retry_timer timer;
-
- /** Socket address to fill in with resolved address */
- struct sockaddr sa;
- /** Current query packet */
- struct dns_query query;
- /** Location of query info structure within current packet
- *
- * The query info structure is located immediately after the
- * compressed name.
- */
- struct dns_query_info *qinfo;
- /** 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 );
-
- /* Close data transfer interface */
- xfer_nullify ( &dns->socket );
- xfer_close ( &dns->socket, rc );
-
- /* Mark name resolution as complete */
- resolv_done ( &dns->resolv, &dns->sa, rc );
-}
-
-/**
- * Compare DNS reply name against the query name from the original request
- *
- * @v dns DNS request
- * @v reply DNS reply
- * @v rname Reply name
- * @ret zero Names match
- * @ret non-zero Names do not match
- */
-static int dns_name_cmp ( struct dns_request *dns,
- const struct dns_header *reply,
- const char *rname ) {
- const char *qname = dns->query.payload;
- int i;
-
- while ( 1 ) {
- /* Obtain next section of rname */
- while ( ( *rname ) & 0xc0 ) {
- rname = ( ( ( char * ) reply ) +
- ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
- }
- /* Check that lengths match */
- if ( *rname != *qname )
- return -1;
- /* If length is zero, we have reached the end */
- if ( ! *qname )
- return 0;
- /* Check that data matches */
- for ( i = *qname + 1; i > 0 ; i-- ) {
- if ( *(rname++) != *(qname++) )
- return -1;
- }
- }
-}
-
-/**
- * Skip over a (possibly compressed) DNS name
- *
- * @v name DNS name
- * @ret name Next DNS name
- */
-static const char * dns_skip_name ( const char *name ) {
- while ( 1 ) {
- if ( ! *name ) {
- /* End of name */
- return ( name + 1);
- }
- if ( *name & 0xc0 ) {
- /* Start of a compressed name */
- return ( name + 2 );
- }
- /* Uncompressed name portion */
- name += *name + 1;
- }
-}
-
-/**
- * Find an RR in a reply packet corresponding to our query
- *
- * @v dns DNS request
- * @v reply DNS reply
- * @ret rr DNS RR, or NULL if not found
- */
-static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
- const struct dns_header *reply ) {
- int i, cmp;
- const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
- union dns_rr_info *rr_info;
-
- /* Skip over the questions section */
- for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
- p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
- }
-
- /* Process the answers section */
- for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
- cmp = dns_name_cmp ( dns, reply, p );
- p = dns_skip_name ( p );
- rr_info = ( ( union dns_rr_info * ) p );
- if ( cmp == 0 )
- return rr_info;
- p += ( sizeof ( rr_info->common ) +
- ntohs ( rr_info->common.rdlength ) );
- }
-
- return NULL;
-}
-
-/**
- * Append DHCP domain name if available and name is not fully qualified
- *
- * @v string Name as a NUL-terminated string
- * @ret fqdn Fully-qualified domain name, malloc'd copy
- *
- * The caller must free fqdn which is allocated even if the name is already
- * fully qualified.
- */
-static char * dns_qualify_name ( const char *string ) {
- char *fqdn;
-
- /* Leave unchanged if already fully-qualified or no local domain */
- if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) )
- return strdup ( string );
-
- /* Append local domain to name */
- asprintf ( &fqdn, "%s.%s", string, localdomain );
- return fqdn;
-}
-
-/**
- * Convert a standard NUL-terminated string to a DNS name
- *
- * @v string Name as a NUL-terminated string
- * @v buf Buffer in which to place DNS name
- * @ret next Byte following constructed DNS name
- *
- * DNS names consist of "<length>element" pairs.
- */
-static char * dns_make_name ( const char *string, char *buf ) {
- char *length_byte = buf++;
- char c;
-
- while ( ( c = *(string++) ) ) {
- if ( c == '.' ) {
- *length_byte = buf - length_byte - 1;
- length_byte = buf;
- }
- *(buf++) = c;
- }
- *length_byte = buf - length_byte - 1;
- *(buf++) = '\0';
- return buf;
-}
-
-/**
- * Convert an uncompressed DNS name to a NUL-terminated string
- *
- * @v name DNS name
- * @ret string NUL-terminated string
- *
- * Produce a printable version of a DNS name. Used only for debugging.
- */
-static inline char * dns_unmake_name ( char *name ) {
- char *p;
- unsigned int len;
-
- p = name;
- while ( ( len = *p ) ) {
- *(p++) = '.';
- p += len;
- }
-
- return name + 1;
-}
-
-/**
- * Decompress a DNS name
- *
- * @v reply DNS replay
- * @v name DNS name
- * @v buf Buffer into which to decompress DNS name
- * @ret next Byte following decompressed DNS name
- */
-static char * dns_decompress_name ( const struct dns_header *reply,
- const char *name, char *buf ) {
- int i, len;
-
- do {
- /* Obtain next section of name */
- while ( ( *name ) & 0xc0 ) {
- name = ( ( char * ) reply +
- ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
- }
- /* Copy data */
- len = *name;
- for ( i = len + 1 ; i > 0 ; i-- ) {
- *(buf++) = *(name++);
- }
- } while ( len );
- return buf;
-}
-
-/**
- * Send next packet in DNS request
- *
- * @v dns DNS request
- */
-static int dns_send_packet ( struct dns_request *dns ) {
- static unsigned int qid = 0;
- size_t qlen;
-
- /* Increment query ID */
- dns->query.dns.id = htons ( ++qid );
-
- DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
-
- /* Start retransmission timer */
- start_timer ( &dns->timer );
-
- /* Send the data */
- qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
- + sizeof ( dns->qinfo ) );
- return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
-}
-
-/**
- * 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 socket UDP socket
- * @v data DNS reply
- * @v len Length of DNS reply
- * @ret rc Return status code
- */
-static int dns_xfer_deliver_raw ( struct xfer_interface *socket,
- const void *data, size_t len ) {
- struct dns_request *dns =
- container_of ( socket, struct dns_request, socket );
- const struct dns_header *reply = data;
- union dns_rr_info *rr_info;
- struct sockaddr_in *sin;
- unsigned int qtype = dns->qinfo->qtype;
-
- /* Sanity check */
- if ( len < sizeof ( *reply ) ) {
- DBGC ( dns, "DNS %p received underlength packet length %zd\n",
- dns, len );
- return -EINVAL;
- }
-
- /* Check reply ID matches query ID */
- if ( reply->id != dns->query.dns.id ) {
- DBGC ( dns, "DNS %p received unexpected reply ID %d "
- "(wanted %d)\n", dns, ntohs ( reply->id ),
- ntohs ( dns->query.dns.id ) );
- return -EINVAL;
- }
-
- DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
-
- /* 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 );
-
- /* 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.
- */
- while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
- switch ( rr_info->common.type ) {
-
- case htons ( DNS_TYPE_A ):
-
- /* Found the target A record */
- DBGC ( dns, "DNS %p found address %s\n",
- dns, inet_ntoa ( rr_info->a.in_addr ) );
- sin = ( struct sockaddr_in * ) &dns->sa;
- sin->sin_family = AF_INET;
- sin->sin_addr = rr_info->a.in_addr;
-
- /* Mark operation as complete */
- dns_done ( dns, 0 );
- return 0;
-
- case htons ( DNS_TYPE_CNAME ):
-
- /* Found a CNAME record; update query and recurse */
- DBGC ( dns, "DNS %p found CNAME\n", dns );
- dns->qinfo = ( void * ) dns_decompress_name ( reply,
- rr_info->cname.cname,
- dns->query.payload );
- dns->qinfo->qtype = htons ( DNS_TYPE_A );
- dns->qinfo->qclass = htons ( DNS_CLASS_IN );
-
- /* Terminate the operation if we recurse too far */
- if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
- DBGC ( dns, "DNS %p recursion exceeded\n",
- dns );
- dns_done ( dns, -ELOOP );
- return 0;
- }
- break;
-
- default:
- DBGC ( dns, "DNS %p got unknown record type %d\n",
- dns, ntohs ( rr_info->common.type ) );
- break;
- }
- }
-
- /* Determine what to do next based on the type of query we
- * issued and the reponse we received
- */
- switch ( qtype ) {
-
- 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->qinfo->qtype = htons ( DNS_TYPE_CNAME );
- dns_send_packet ( dns );
- return 0;
-
- case htons ( DNS_TYPE_CNAME ):
- /* We asked for a CNAME record. If we got a response
- * (i.e. if the next A query is already set up), then
- * issue it, otherwise abort.
- */
- if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
- dns_send_packet ( dns );
- return 0;
- } else {
- DBGC ( dns, "DNS %p found no CNAME record\n", dns );
- dns_done ( dns, -ENXIO );
- return 0;
- }
-
- default:
- assert ( 0 );
- dns_done ( dns, -EINVAL );
- return 0;
- }
-}
-
-/**
- * Receive new data
- *
- * @v socket UDP socket
- * @v rc Reason for close
- */
-static void dns_xfer_close ( struct xfer_interface *socket, int rc ) {
- struct dns_request *dns =
- container_of ( socket, struct dns_request, socket );
-
- if ( ! rc )
- rc = -ECONNABORTED;
-
- dns_done ( dns, rc );
-}
-
-/** DNS socket operations */
-static struct xfer_interface_operations dns_socket_operations = {
- .close = dns_xfer_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = dns_xfer_deliver_raw,
-};
-
-/**
- * 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 resolv_interface *resolv,
- const char *name, struct sockaddr *sa ) {
- struct dns_request *dns;
- char *fqdn;
- int rc;
-
- /* Fail immediately if no DNS servers */
- if ( ! nameserver.st_family ) {
- DBG ( "DNS not attempting to resolve \"%s\": "
- "no DNS servers\n", name );
- rc = -ENXIO;
- goto err_no_nameserver;
- }
-
- /* Ensure fully-qualified domain name if DHCP option was given */
- fqdn = dns_qualify_name ( name );
- if ( ! fqdn ) {
- rc = -ENOMEM;
- goto err_qualify_name;
- }
-
- /* Allocate DNS structure */
- dns = zalloc ( sizeof ( *dns ) );
- if ( ! dns ) {
- rc = -ENOMEM;
- goto err_alloc_dns;
- }
- resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
- xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
- dns->timer.expired = dns_timer_expired;
- memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
-
- /* Create query */
- dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
- DNS_FLAG_RD );
- dns->query.dns.qdcount = htons ( 1 );
- dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
- dns->qinfo->qtype = htons ( DNS_TYPE_A );
- dns->qinfo->qclass = htons ( DNS_CLASS_IN );
-
- /* Open UDP connection */
- if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
- ( struct sockaddr * ) &nameserver,
- NULL ) ) != 0 ) {
- DBGC ( dns, "DNS %p could not open socket: %s\n",
- dns, strerror ( rc ) );
- goto err_open_socket;
- }
-
- /* Send first DNS packet */
- dns_send_packet ( dns );
-
- /* Attach parent interface, mortalise self, and return */
- resolv_plug_plug ( &dns->resolv, resolv );
- ref_put ( &dns->refcnt );
- free ( fqdn );
- return 0;
-
- err_open_socket:
- err_alloc_dns:
- ref_put ( &dns->refcnt );
- err_qualify_name:
- free ( fqdn );
- err_no_nameserver:
- return rc;
-}
-
-/** DNS name resolver */
-struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
- .name = "DNS",
- .resolv = dns_resolv,
-};
-
-/******************************************************************************
- *
- * Settings
- *
- ******************************************************************************
- */
-
-/** DNS server setting */
-struct setting dns_setting __setting = {
- .name = "dns",
- .description = "DNS server",
- .tag = DHCP_DNS_SERVERS,
- .type = &setting_type_ipv4,
-};
-
-/** Domain name setting */
-struct setting domain_setting __setting = {
- .name = "domain",
- .description = "Local domain",
- .tag = DHCP_DOMAIN_NAME,
- .type = &setting_type_string,
-};
-
-/**
- * Apply DNS settings
- *
- * @ret rc Return status code
- */
-static int apply_dns_settings ( void ) {
- struct sockaddr_in *sin_nameserver =
- ( struct sockaddr_in * ) &nameserver;
- int len;
-
- if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
- &sin_nameserver->sin_addr ) ) >= 0 ){
- sin_nameserver->sin_family = AF_INET;
- DBG ( "DNS using nameserver %s\n",
- inet_ntoa ( sin_nameserver->sin_addr ) );
- }
-
- /* Get local domain DHCP option */
- if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting,
- &localdomain ) ) >= 0 )
- DBG ( "DNS local domain %s\n", localdomain );
-
- return 0;
-}
-
-/** DNS settings applicator */
-struct settings_applicator dns_applicator __settings_applicator = {
- .apply = apply_dns_settings,
-};
diff --git a/gpxe/src/net/udp/slam.c b/gpxe/src/net/udp/slam.c
deleted file mode 100644
index 396f69b0..00000000
--- a/gpxe/src/net/udp/slam.c
+++ /dev/null
@@ -1,812 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <errno.h>
-#include <assert.h>
-#include <byteswap.h>
-#include <gpxe/features.h>
-#include <gpxe/iobuf.h>
-#include <gpxe/bitmap.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/uri.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/timer.h>
-#include <gpxe/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 xfer_interface xfer;
- /** Unicast socket */
- struct xfer_interface socket;
- /** Multicast socket */
- struct xfer_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 */
- xfer_nullify ( &slam->socket );
- xfer_close ( &slam->socket, rc );
- xfer_nullify ( &slam->mc_socket );
- xfer_close ( &slam->mc_socket, rc );
- xfer_nullify ( &slam->xfer );
- xfer_close ( &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, SEEK_SET );
-
- return 0;
-}
-
-/**
- * Receive SLAM data packet
- *
- * @v mc_socket SLAM multicast socket
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
-static int slam_mc_socket_deliver ( struct xfer_interface *mc_socket,
- struct io_buffer *iobuf,
- struct xfer_metadata *rx_meta __unused ) {
- struct slam_request *slam =
- container_of ( mc_socket, struct slam_request, mc_socket );
- 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.whence = SEEK_SET;
- meta.offset = ( packet * slam->block_size );
- if ( ( rc = xfer_deliver_iob_meta ( &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 socket SLAM unicast socket
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
-static int slam_socket_deliver ( struct xfer_interface *socket,
- struct io_buffer *iobuf,
- struct xfer_metadata *rx_meta __unused ) {
- struct slam_request *slam =
- container_of ( socket, struct slam_request, socket );
- 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;
-
-}
-
-/**
- * Close SLAM unicast socket
- *
- * @v socket SLAM unicast socket
- * @v rc Reason for close
- */
-static void slam_socket_close ( struct xfer_interface *socket, int rc ) {
- struct slam_request *slam =
- container_of ( socket, struct slam_request, socket );
-
- DBGC ( slam, "SLAM %p unicast socket closed: %s\n",
- slam, strerror ( rc ) );
-
- slam_finished ( slam, rc );
-}
-
-/** SLAM unicast socket data transfer operations */
-static struct xfer_interface_operations slam_socket_operations = {
- .close = slam_socket_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = slam_socket_deliver,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * Close SLAM multicast socket
- *
- * @v mc_socket SLAM multicast socket
- * @v rc Reason for close
- */
-static void slam_mc_socket_close ( struct xfer_interface *mc_socket, int rc ){
- struct slam_request *slam =
- container_of ( mc_socket, struct slam_request, mc_socket );
-
- DBGC ( slam, "SLAM %p multicast socket closed: %s\n",
- slam, strerror ( rc ) );
-
- slam_finished ( slam, rc );
-}
-
-/** SLAM multicast socket data transfer operations */
-static struct xfer_interface_operations slam_mc_socket_operations = {
- .close = slam_mc_socket_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = slam_mc_socket_deliver,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/****************************************************************************
- *
- * Data transfer interface
- *
- */
-
-/**
- * Close SLAM data transfer interface
- *
- * @v xfer SLAM data transfer interface
- * @v rc Reason for close
- */
-static void slam_xfer_close ( struct xfer_interface *xfer, int rc ) {
- struct slam_request *slam =
- container_of ( xfer, struct slam_request, xfer );
-
- DBGC ( slam, "SLAM %p data transfer interface closed: %s\n",
- slam, strerror ( rc ) );
-
- slam_finished ( slam, rc );
-}
-
-/** SLAM data transfer operations */
-static struct xfer_interface_operations slam_xfer_operations = {
- .close = slam_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = ignore_xfer_deliver_raw,
-};
-
-/**
- * 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 xfer_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;
- slam->refcnt.free = slam_free;
- xfer_init ( &slam->xfer, &slam_xfer_operations, &slam->refcnt );
- xfer_init ( &slam->socket, &slam_socket_operations, &slam->refcnt );
- xfer_init ( &slam->mc_socket, &slam_mc_socket_operations,
- &slam->refcnt );
- slam->master_timer.expired = slam_master_timer_expired;
- slam->slave_timer.expired = slam_slave_timer_expired;
- /* 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 */
- xfer_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/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c
deleted file mode 100644
index 3de2fb9b..00000000
--- a/gpxe/src/net/udp/tftp.c
+++ /dev/null
@@ -1,1288 +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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#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 <gpxe/refcnt.h>
-#include <gpxe/xfer.h>
-#include <gpxe/open.h>
-#include <gpxe/uri.h>
-#include <gpxe/tcpip.h>
-#include <gpxe/retry.h>
-#include <gpxe/features.h>
-#include <gpxe/bitmap.h>
-#include <gpxe/settings.h>
-#include <gpxe/dhcp.h>
-#include <gpxe/uri.h>
-#include <gpxe/tftp.h>
-
-/** @file
- *
- * TFTP protocol
- *
- */
-
-FEATURE ( FEATURE_PROTOCOL, "TFTP", DHCP_EB_FEATURE_TFTP, 1 );
-
-/* TFTP-specific error codes */
-#define ETFTP_INVALID_BLKSIZE EUNIQ_01
-#define ETFTP_INVALID_TSIZE EUNIQ_02
-#define ETFTP_MC_NO_PORT EUNIQ_03
-#define ETFTP_MC_NO_MC EUNIQ_04
-#define ETFTP_MC_INVALID_MC EUNIQ_05
-#define ETFTP_MC_INVALID_IP EUNIQ_06
-#define ETFTP_MC_INVALID_PORT EUNIQ_07
-
-/**
- * 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 xfer_interface xfer;
-
- /** URI being fetched */
- struct uri *uri;
- /** Transport layer interface */
- struct xfer_interface socket;
- /** Multicast transport layer interface */
- struct xfer_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,
- /** Only get filesize and then abort the transfer */
- TFTP_FL_SIZEONLY = 0x0010,
-};
-
-/** 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 */
- xfer_nullify ( &tftp->socket );
- xfer_close ( &tftp->socket, rc );
- xfer_nullify ( &tftp->mc_socket );
- xfer_close ( &tftp->mc_socket, rc );
- xfer_nullify ( &tftp->xfer );
- xfer_close ( &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 */
- xfer_close ( &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 */
- xfer_close ( &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, SEEK_SET );
- xfer_seek ( &tftp->xfer, 0, SEEK_SET );
-
- /* 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;
-}
-
-/**
- * TFTP requested blocksize
- *
- * This is treated as a global configuration parameter.
- */
-static unsigned int tftp_request_blksize = TFTP_MAX_BLKSIZE;
-
-/**
- * Set TFTP request blocksize
- *
- * @v blksize Requested block size
- */
-void tftp_set_request_blksize ( unsigned int blksize ) {
- if ( blksize < TFTP_DEFAULT_BLKSIZE )
- blksize = TFTP_DEFAULT_BLKSIZE;
- tftp_request_blksize = blksize;
-}
-
-/**
- * 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 ) {
- struct tftp_rrq *rrq;
- const char *path;
- size_t len;
- struct io_buffer *iobuf;
-
- /* Strip initial '/' if present. If we were opened via the
- * URI interface, then there will be an initial '/', since a
- * full tftp:// URI provides no way to specify a non-absolute
- * path. However, many TFTP servers (particularly Windows
- * TFTP servers) complain about having an initial '/', and it
- * violates user expectations to have a '/' silently added to
- * the DHCP-specified filename.
- */
- path = tftp->uri->path;
- if ( *path == '/' )
- path++;
-
- 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;
-
- /* 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%d%ctsize%c0", 0,
- tftp_request_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_iob_meta ( &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_iob_meta ( &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 */
- xfer_close ( &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 | ETFTP_INVALID_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 | ETFTP_INVALID_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 | ETFTP_MC_NO_PORT );
- }
- *(port++) = '\0';
- mc = strchr ( port, ',' );
- if ( ! mc ) {
- DBGC ( tftp, "TFTP %p multicast missing mc\n", tftp );
- return -( EINVAL | ETFTP_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 | ETFTP_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 | ETFTP_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 | ETFTP_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;
- }
-
- /* Abort request if only trying to determine file size */
- if ( tftp->flags & TFTP_FL_SIZEONLY ) {
- rc = 0;
- tftp_send_error ( tftp, 0, "TFTP Aborted" );
- tftp_done ( tftp, rc );
- return rc;
- }
-
- /* 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;
-
- if ( tftp->flags & TFTP_FL_SIZEONLY ) {
- /* If we get here then server doesn't support SIZE option */
- rc = -ENOTSUP;
- tftp_send_error ( tftp, 0, "TFTP Aborted" );
- goto done;
- }
-
- /* 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.whence = SEEK_SET;
- meta.offset = offset;
- if ( ( rc = xfer_deliver_iob_meta ( &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;
-}
-
-/** Translation between TFTP errors and internal error numbers */
-static const int tftp_errors[] = {
- [TFTP_ERR_FILE_NOT_FOUND] = ENOENT,
- [TFTP_ERR_ACCESS_DENIED] = EACCES,
- [TFTP_ERR_ILLEGAL_OP] = 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;
- unsigned int err;
- int rc = 0;
-
- /* 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 */
- err = ntohs ( error->errcode );
- if ( err < ( sizeof ( tftp_errors ) / sizeof ( tftp_errors[0] ) ) )
- rc = -tftp_errors[err];
- if ( ! rc )
- rc = -ENOTSUP;
-
- /* 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 socket Transport layer interface
- * @v iobuf I/O buffer
- * @v meta Transfer metadata
- * @ret rc Return status code
- */
-static int tftp_socket_deliver_iob ( struct xfer_interface *socket,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- struct tftp_request *tftp =
- container_of ( socket, struct tftp_request, socket );
-
- /* 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 xfer_interface_operations tftp_socket_operations = {
- .close = ignore_xfer_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = tftp_socket_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * Receive new data via multicast socket
- *
- * @v mc_socket Multicast transport layer interface
- * @v iobuf I/O buffer
- * @v meta Transfer metadata
- * @ret rc Return status code
- */
-static int tftp_mc_socket_deliver_iob ( struct xfer_interface *mc_socket,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- struct tftp_request *tftp =
- container_of ( mc_socket, struct tftp_request, mc_socket );
-
- return tftp_rx ( tftp, iobuf, meta );
-}
-
-/** TFTP multicast socket operations */
-static struct xfer_interface_operations tftp_mc_socket_operations = {
- .close = ignore_xfer_close,
- .vredirect = xfer_vreopen,
- .window = unlimited_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = tftp_mc_socket_deliver_iob,
- .deliver_raw = xfer_deliver_as_iob,
-};
-
-/**
- * Close TFTP data transfer interface
- *
- * @v xfer Data transfer interface
- * @v rc Reason for close
- */
-static void tftp_xfer_close ( struct xfer_interface *xfer, int rc ) {
- struct tftp_request *tftp =
- container_of ( xfer, struct tftp_request, xfer );
-
- DBGC ( tftp, "TFTP %p interface closed: %s\n",
- tftp, strerror ( rc ) );
-
- tftp_done ( tftp, rc );
-}
-
-/**
- * Check flow control window
- *
- * @v xfer Data transfer interface
- * @ret len Length of window
- */
-static size_t tftp_xfer_window ( struct xfer_interface *xfer ) {
- struct tftp_request *tftp =
- container_of ( xfer, struct tftp_request, xfer );
-
- /* 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;
-}
-
-/** TFTP data transfer interface operations */
-static struct xfer_interface_operations tftp_xfer_operations = {
- .close = tftp_xfer_close,
- .vredirect = ignore_xfer_vredirect,
- .window = tftp_xfer_window,
- .alloc_iob = default_xfer_alloc_iob,
- .deliver_iob = xfer_deliver_as_raw,
- .deliver_raw = ignore_xfer_deliver_raw,
-};
-
-/**
- * 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 xfer_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;
- tftp->refcnt.free = tftp_free;
- xfer_init ( &tftp->xfer, &tftp_xfer_operations, &tftp->refcnt );
- tftp->uri = uri_get ( uri );
- xfer_init ( &tftp->socket, &tftp_socket_operations, &tftp->refcnt );
- xfer_init ( &tftp->mc_socket, &tftp_mc_socket_operations,
- &tftp->refcnt );
- tftp->blksize = TFTP_DEFAULT_BLKSIZE;
- tftp->flags = flags;
- tftp->timer.expired = tftp_timer_expired;
-
- /* 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 */
- xfer_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 xfer_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 TFTP-size request
- *
- * @v xfer Data transfer interface
- * @v uri Uniform Resource Identifier
- * @ret rc Return status code
- */
-static int tftpsize_open ( struct xfer_interface *xfer, struct uri *uri ) {
- return tftp_core_open ( xfer, uri, TFTP_PORT, NULL,
- ( TFTP_FL_RRQ_SIZES |
- TFTP_FL_SIZEONLY ) );
-
-}
-
-/** TFTP URI opener */
-struct uri_opener tftpsize_uri_opener __uri_opener = {
- .scheme = "tftpsize",
- .open = tftpsize_open,
-};
-
-/**
- * Initiate TFTM download
- *
- * @v xfer Data transfer interface
- * @v uri Uniform Resource Identifier
- * @ret rc Return status code
- */
-static int tftm_open ( struct xfer_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 xfer_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
- *
- ******************************************************************************
- */
-
-/** TFTP server setting */
-struct setting next_server_setting __setting = {
- .name = "next-server",
- .description = "TFTP server",
- .tag = DHCP_EB_SIADDR,
- .type = &setting_type_ipv4,
-};
-
-/**
- * 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 ) {
- snprintf ( uri_string, sizeof ( uri_string ),
- "tftp://%s/", inet_ntoa ( tftp_server ) );
- uri = parse_uri ( uri_string );
- if ( ! uri )
- return -ENOMEM;
- churi ( uri );
- uri_put ( uri );
- }
-
- return 0;
-}
-
-/** TFTP settings applicator */
-struct settings_applicator tftp_settings_applicator __settings_applicator = {
- .apply = tftp_apply_settings,
-};