summaryrefslogtreecommitdiff
path: root/gpxe/src/net
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2016-02-09 18:08:47 -0800
committerH. Peter Anvin <hpa@zytor.com>2016-02-09 18:08:47 -0800
commitf2f897a1762fab84d2905f32b1c15dd7b42abb56 (patch)
treea38f51d3f1fcbf44afddb4736d549c12eaf491be /gpxe/src/net
parent72d2959272b4616f17a97667e6dfa9d06bf109a3 (diff)
downloadsyslinux-f2f897a1762fab84d2905f32b1c15dd7b42abb56.tar.gz
gpxe: delete long since obsolete snapshot of gPXE
gPXE has been deprecated in favor of iPXE for many, many years now. It is much better than users get it directly from the iPXE project, since we should no longer need any special modifications for Syslinux use. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
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,
-};