summaryrefslogtreecommitdiffstats
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/ipv4.c2
-rw-r--r--src/net/ipv6.c12
-rw-r--r--src/net/lldp.c340
-rw-r--r--src/net/ndp.c4
-rw-r--r--src/net/netdev_settings.c4
-rw-r--r--src/net/netdevice.c68
-rw-r--r--src/net/peerblk.c3
-rw-r--r--src/net/peerdisc.c2
-rw-r--r--src/net/tcp/httpcore.c107
-rw-r--r--src/net/tcp/iscsi.c65
-rw-r--r--src/net/tls.c839
-rw-r--r--src/net/udp/dhcp.c14
-rw-r--r--src/net/udp/dhcpv6.c4
-rw-r--r--src/net/vlan.c59
14 files changed, 1038 insertions, 485 deletions
diff --git a/src/net/ipv4.c b/src/net/ipv4.c
index b9ce5e7f7..b91fa2ad0 100644
--- a/src/net/ipv4.c
+++ b/src/net/ipv4.c
@@ -163,7 +163,7 @@ static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
/* If destination is non-global, and the scope ID
* matches this network device, then use this route.
*/
- if ( miniroute->netdev->index == scope_id )
+ if ( miniroute->netdev->scope_id == scope_id )
return miniroute;
} else {
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index 901203c40..ef5e51daa 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -330,7 +330,7 @@ struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
/* Skip entries with a non-matching scope ID, if
* destination specifies a scope ID.
*/
- if ( scope_id && ( miniroute->netdev->index != scope_id ) )
+ if ( scope_id && ( miniroute->netdev->scope_id != scope_id ) )
continue;
/* Skip entries that are out of scope */
@@ -789,12 +789,12 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
src.sin6.sin6_family = AF_INET6;
memcpy ( &src.sin6.sin6_addr, &iphdr->src,
sizeof ( src.sin6.sin6_addr ) );
- src.sin6.sin6_scope_id = netdev->index;
+ src.sin6.sin6_scope_id = netdev->scope_id;
memset ( &dest, 0, sizeof ( dest ) );
dest.sin6.sin6_family = AF_INET6;
memcpy ( &dest.sin6.sin6_addr, &iphdr->dest,
sizeof ( dest.sin6.sin6_addr ) );
- dest.sin6.sin6_scope_id = netdev->index;
+ dest.sin6.sin6_scope_id = netdev->scope_id;
iob_pull ( iobuf, hdrlen );
pshdr_csum = ipv6_pshdr_chksum ( iphdr, iob_len ( iobuf ),
next_header, TCPIP_EMPTY_CSUM );
@@ -957,7 +957,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) {
/* Identify network device, if applicable */
if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) {
- netdev = find_netdev_by_index ( sin6->sin6_scope_id );
+ netdev = find_netdev_by_scope_id ( sin6->sin6_scope_id );
netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
} else {
netdev_name = NULL;
@@ -1020,7 +1020,7 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
rc = -ENODEV;
goto err_find_netdev;
}
- sin6->sin6_scope_id = netdev->index;
+ sin6->sin6_scope_id = netdev->scope_id;
} else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) ||
IN6_IS_ADDR_MULTICAST ( &in ) ) {
@@ -1031,7 +1031,7 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
*/
netdev = last_opened_netdev();
if ( netdev )
- sin6->sin6_scope_id = netdev->index;
+ sin6->sin6_scope_id = netdev->scope_id;
}
/* Copy IPv6 address portion to socket address */
diff --git a/src/net/lldp.c b/src/net/lldp.c
new file mode 100644
index 000000000..72e3ecdf6
--- /dev/null
+++ b/src/net/lldp.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Link Layer Discovery Protocol
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/settings.h>
+#include <ipxe/lldp.h>
+
+/** An LLDP settings block */
+struct lldp_settings {
+ /** Reference counter */
+ struct refcnt refcnt;
+ /** Settings interface */
+ struct settings settings;
+ /** List of LLDP settings blocks */
+ struct list_head list;
+ /** Name */
+ const char *name;
+ /** LLDP data */
+ void *data;
+ /** Length of LLDP data */
+ size_t len;
+};
+
+/** LLDP settings scope */
+static const struct settings_scope lldp_settings_scope;
+
+/** List of LLDP settings blocks */
+static LIST_HEAD ( lldp_settings );
+
+/**
+ * Free LLDP settings block
+ *
+ * @v refcnt Reference counter
+ */
+static void lldp_free ( struct refcnt *refcnt ) {
+ struct lldp_settings *lldpset =
+ container_of ( refcnt, struct lldp_settings, refcnt );
+
+ DBGC ( lldpset, "LLDP %s freed\n", lldpset->name );
+ list_del ( &lldpset->list );
+ free ( lldpset->data );
+ free ( lldpset );
+}
+
+/**
+ * Find LLDP settings block
+ *
+ * @v netdev Network device
+ * @ret lldpset LLDP settings block
+ */
+static struct lldp_settings * lldp_find ( struct net_device *netdev ) {
+ struct lldp_settings *lldpset;
+
+ /* Find matching LLDP settings block */
+ list_for_each_entry ( lldpset, &lldp_settings, list ) {
+ if ( netdev_settings ( netdev ) == lldpset->settings.parent )
+ return lldpset;
+ }
+
+ return NULL;
+}
+
+/**
+ * Check applicability of LLDP setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @ret applies Setting applies within this settings block
+ */
+static int lldp_applies ( struct settings *settings __unused,
+ const struct setting *setting ) {
+
+ return ( setting->scope == &lldp_settings_scope );
+}
+
+/**
+ * Fetch value of LLDP setting
+ *
+ * @v settings Settings block
+ * @v setting Setting to fetch
+ * @v buf Buffer to fill with setting data
+ * @v len Length of buffer
+ * @ret len Length of setting data, or negative error
+ */
+static int lldp_fetch ( struct settings *settings,
+ struct setting *setting,
+ void *buf, size_t len ) {
+ struct lldp_settings *lldpset =
+ container_of ( settings, struct lldp_settings, settings );
+ union {
+ uint32_t high;
+ uint8_t raw[4];
+ } tag_prefix;
+ uint32_t tag_low;
+ uint8_t tag_type;
+ uint8_t tag_index;
+ uint8_t tag_offset;
+ uint8_t tag_length;
+ const void *match;
+ const void *data;
+ size_t match_len;
+ size_t remaining;
+ const struct lldp_tlv *tlv;
+ unsigned int tlv_type_len;
+ unsigned int tlv_type;
+ unsigned int tlv_len;
+
+ /* Parse setting tag */
+ tag_prefix.high = htonl ( setting->tag >> 32 );
+ tag_low = setting->tag;
+ tag_type = ( tag_low >> 24 );
+ tag_index = ( tag_low >> 16 );
+ tag_offset = ( tag_low >> 8 );
+ tag_length = ( tag_low >> 0 );
+
+ /* Identify match prefix */
+ match_len = tag_offset;
+ if ( match_len > sizeof ( tag_prefix ) )
+ match_len = sizeof ( tag_prefix );
+ if ( ! tag_prefix.high )
+ match_len = 0;
+ match = &tag_prefix.raw[ sizeof ( tag_prefix ) - match_len ];
+
+ /* Locate matching TLV */
+ for ( data = lldpset->data, remaining = lldpset->len ; remaining ;
+ data += tlv_len, remaining -= tlv_len ) {
+
+ /* Parse TLV header */
+ if ( remaining < sizeof ( *tlv ) ) {
+ DBGC ( lldpset, "LLDP %s underlength TLV header\n",
+ lldpset->name );
+ DBGC_HDA ( lldpset, 0, data, remaining );
+ break;
+ }
+ tlv = data;
+ data += sizeof ( *tlv );
+ remaining -= sizeof ( *tlv );
+ tlv_type_len = ntohs ( tlv->type_len );
+ tlv_type = LLDP_TLV_TYPE ( tlv_type_len );
+ if ( tlv_type == LLDP_TYPE_END )
+ break;
+ tlv_len = LLDP_TLV_LEN ( tlv_type_len );
+ if ( remaining < tlv_len ) {
+ DBGC ( lldpset, "LLDP %s underlength TLV value\n",
+ lldpset->name );
+ DBGC_HDA ( lldpset, 0, data, remaining );
+ break;
+ }
+ DBGC2 ( lldpset, "LLDP %s found type %d:\n",
+ lldpset->name, tlv_type );
+ DBGC2_HDA ( lldpset, 0, data, tlv_len );
+
+ /* Check for matching tag type */
+ if ( tlv_type != tag_type )
+ continue;
+
+ /* Check for matching prefix */
+ if ( tlv_len < match_len )
+ continue;
+ if ( memcmp ( data, match, match_len ) != 0 )
+ continue;
+
+ /* Check for matching index */
+ if ( tag_index-- )
+ continue;
+
+ /* Skip offset */
+ if ( tlv_len < tag_offset )
+ return 0;
+ data += tag_offset;
+ tlv_len -= tag_offset;
+
+ /* Set type if not already specified */
+ if ( ! setting->type ) {
+ setting->type = ( tag_length ? &setting_type_hex :
+ &setting_type_string );
+ }
+
+ /* Extract value */
+ if ( tag_length && ( tlv_len > tag_length ) )
+ tlv_len = tag_length;
+ if ( len > tlv_len )
+ len = tlv_len;
+ memcpy ( buf, data, len );
+ return tlv_len;
+ }
+
+ return -ENOENT;
+}
+
+/** LLDP settings operations */
+static struct settings_operations lldp_settings_operations = {
+ .applies = lldp_applies,
+ .fetch = lldp_fetch,
+};
+
+/**
+ * Process LLDP packet
+ *
+ * @v iobuf I/O buffer
+ * @v netdev Network device
+ * @v ll_dest Link-layer destination address
+ * @v ll_source Link-layer source address
+ * @v flags Packet flags
+ * @ret rc Return status code
+ */
+static int lldp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
+ const void *ll_dest, const void *ll_source,
+ unsigned int flags __unused ) {
+ struct lldp_settings *lldpset;
+ size_t len;
+ void *data;
+ int rc;
+
+ /* Find matching LLDP settings block */
+ lldpset = lldp_find ( netdev );
+ if ( ! lldpset ) {
+ DBGC ( netdev, "LLDP %s has no \"%s\" settings block\n",
+ netdev->name, LLDP_SETTINGS_NAME );
+ rc = -ENOENT;
+ goto err_find;
+ }
+
+ /* Create trimmed copy of received LLDP data */
+ len = iob_len ( iobuf );
+ data = malloc ( len );
+ if ( ! data ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ memcpy ( data, iobuf->data, len );
+
+ /* Free any existing LLDP data */
+ free ( lldpset->data );
+
+ /* Transfer data to LLDP settings block */
+ lldpset->data = data;
+ lldpset->len = len;
+ data = NULL;
+ DBGC2 ( lldpset, "LLDP %s src %s ",
+ lldpset->name, netdev->ll_protocol->ntoa ( ll_source ) );
+ DBGC2 ( lldpset, "dst %s\n", netdev->ll_protocol->ntoa ( ll_dest ) );
+ DBGC2_HDA ( lldpset, 0, lldpset->data, lldpset->len );
+
+ /* Success */
+ rc = 0;
+
+ free ( data );
+ err_alloc:
+ err_find:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** LLDP protocol */
+struct net_protocol lldp_protocol __net_protocol = {
+ .name = "LLDP",
+ .net_proto = htons ( ETH_P_LLDP ),
+ .rx = lldp_rx,
+};
+
+/**
+ * Create LLDP settings block
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int lldp_probe ( struct net_device *netdev ) {
+ struct lldp_settings *lldpset;
+ int rc;
+
+ /* Allocate LLDP settings block */
+ lldpset = zalloc ( sizeof ( *lldpset ) );
+ if ( ! lldpset ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ ref_init ( &lldpset->refcnt, lldp_free );
+ settings_init ( &lldpset->settings, &lldp_settings_operations,
+ &lldpset->refcnt, &lldp_settings_scope );
+ list_add_tail ( &lldpset->list, &lldp_settings );
+ lldpset->name = netdev->name;
+
+ /* Register settings */
+ if ( ( rc = register_settings ( &lldpset->settings, netdev_settings ( netdev ),
+ LLDP_SETTINGS_NAME ) ) != 0 ) {
+ DBGC ( lldpset, "LLDP %s could not register settings: %s\n",
+ lldpset->name, strerror ( rc ) );
+ goto err_register;
+ }
+ DBGC ( lldpset, "LLDP %s registered\n", lldpset->name );
+
+ ref_put ( &lldpset->refcnt );
+ return 0;
+
+ unregister_settings ( &lldpset->settings );
+ err_register:
+ ref_put ( &lldpset->refcnt );
+ err_alloc:
+ return rc;
+}
+
+/** LLDP driver */
+struct net_driver lldp_driver __net_driver = {
+ .name = "LLDP",
+ .probe = lldp_probe,
+};
diff --git a/src/net/ndp.c b/src/net/ndp.c
index c8e8ebad3..373a9360b 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -140,7 +140,7 @@ static int ndp_tx_request ( struct net_device *netdev,
/* Construct multicast destination address */
memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
sin6_dest.sin6_family = AF_INET6;
- sin6_dest.sin6_scope_id = netdev->index;
+ sin6_dest.sin6_scope_id = netdev->scope_id;
ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest );
/* Construct neighbour header */
@@ -177,7 +177,7 @@ static int ndp_tx_router_solicitation ( struct net_device *netdev ) {
/* Construct multicast destination address */
memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
sin6_dest.sin6_family = AF_INET6;
- sin6_dest.sin6_scope_id = netdev->index;
+ sin6_dest.sin6_scope_id = netdev->scope_id;
ipv6_all_routers ( &sin6_dest.sin6_addr );
/* Construct router solicitation */
diff --git a/src/net/netdev_settings.c b/src/net/netdev_settings.c
index cc2e10354..fb98663ca 100644
--- a/src/net/netdev_settings.c
+++ b/src/net/netdev_settings.c
@@ -370,8 +370,8 @@ struct settings_operations netdev_settings_operations = {
static struct settings * netdev_redirect ( struct settings *settings ) {
struct net_device *netdev;
- /* Redirect to most recently opened network device */
- netdev = last_opened_netdev();
+ /* Redirect to "netX" network device */
+ netdev = find_netdev ( settings->name );
if ( netdev ) {
return netdev_settings ( netdev );
} else {
diff --git a/src/net/netdevice.c b/src/net/netdevice.c
index 5df306e8d..07961bf20 100644
--- a/src/net/netdevice.c
+++ b/src/net/netdevice.c
@@ -55,9 +55,6 @@ struct list_head net_devices = LIST_HEAD_INIT ( net_devices );
/** List of open network devices, in reverse order of opening */
static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
-/** Network device index */
-static unsigned int netdev_index = 0;
-
/** Network polling profiler */
static struct profiler net_poll_profiler __profiler = { .name = "net.poll" };
@@ -723,6 +720,7 @@ int register_netdev ( struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct net_driver *driver;
struct net_device *duplicate;
+ unsigned int i;
uint32_t seed;
int rc;
@@ -737,18 +735,6 @@ int register_netdev ( struct net_device *netdev ) {
ll_protocol->ll_header_len );
}
- /* Reject network devices that are already available via a
- * different hardware device.
- */
- duplicate = find_netdev_by_ll_addr ( ll_protocol, netdev->ll_addr );
- if ( duplicate && ( duplicate->dev != netdev->dev ) ) {
- DBGC ( netdev, "NETDEV rejecting duplicate (phys %s) of %s "
- "(phys %s)\n", netdev->dev->name, duplicate->name,
- duplicate->dev->name );
- rc = -EEXIST;
- goto err_duplicate;
- }
-
/* Reject named network devices that already exist */
if ( netdev->name[0] && ( duplicate = find_netdev ( netdev->name ) ) ) {
DBGC ( netdev, "NETDEV rejecting duplicate name %s\n",
@@ -757,12 +743,21 @@ int register_netdev ( struct net_device *netdev ) {
goto err_duplicate;
}
- /* Record device index and create device name */
+ /* Assign a unique device name, if not already set */
if ( netdev->name[0] == '\0' ) {
- snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
- netdev_index );
+ for ( i = 0 ; ; i++ ) {
+ snprintf ( netdev->name, sizeof ( netdev->name ),
+ "net%d", i );
+ if ( find_netdev ( netdev->name ) == NULL )
+ break;
+ }
+ }
+
+ /* Assign a unique non-zero scope ID */
+ for ( netdev->scope_id = 1 ; ; netdev->scope_id++ ) {
+ if ( find_netdev_by_scope_id ( netdev->scope_id ) == NULL )
+ break;
}
- netdev->index = ++netdev_index;
/* Use least significant bits of the link-layer address to
* improve the randomness of the (non-cryptographic) random
@@ -916,10 +911,6 @@ void unregister_netdev ( struct net_device *netdev ) {
DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name );
list_del ( &netdev->list );
netdev_put ( netdev );
-
- /* Reset network device index if no devices remain */
- if ( list_empty ( &net_devices ) )
- netdev_index = 0;
}
/** Enable or disable interrupts
@@ -962,17 +953,17 @@ struct net_device * find_netdev ( const char *name ) {
}
/**
- * Get network device by index
+ * Get network device by scope ID
*
* @v index Network device index
* @ret netdev Network device, or NULL
*/
-struct net_device * find_netdev_by_index ( unsigned int index ) {
+struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ) {
struct net_device *netdev;
/* Identify network device by index */
list_for_each_entry ( netdev, &net_devices, list ) {
- if ( netdev->index == index )
+ if ( netdev->scope_id == scope_id )
return netdev;
}
@@ -1000,27 +991,6 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type,
}
/**
- * Get network device by link-layer address
- *
- * @v ll_protocol Link-layer protocol
- * @v ll_addr Link-layer address
- * @ret netdev Network device, or NULL
- */
-struct net_device * find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol,
- const void *ll_addr ) {
- struct net_device *netdev;
-
- list_for_each_entry ( netdev, &net_devices, list ) {
- if ( ( netdev->ll_protocol == ll_protocol ) &&
- ( memcmp ( netdev->ll_addr, ll_addr,
- ll_protocol->ll_addr_len ) == 0 ) )
- return netdev;
- }
-
- return NULL;
-}
-
-/**
* Get most recently opened network device
*
* @ret netdev Most recently opened network device, or NULL
@@ -1171,12 +1141,12 @@ static void net_step ( struct process *process __unused ) {
}
/**
- * Get the VLAN tag (when VLAN support is not present)
+ * Get the VLAN tag control information (when VLAN support is not present)
*
* @v netdev Network device
* @ret tag 0, indicating that device is not a VLAN device
*/
-__weak unsigned int vlan_tag ( struct net_device *netdev __unused ) {
+__weak unsigned int vlan_tci ( struct net_device *netdev __unused ) {
return 0;
}
diff --git a/src/net/peerblk.c b/src/net/peerblk.c
index f8994f42c..bbd5f16ed 100644
--- a/src/net/peerblk.c
+++ b/src/net/peerblk.c
@@ -1033,7 +1033,8 @@ static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len,
}
/* Set initialisation vector */
- cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data );
+ cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data,
+ blksize );
return 0;
}
diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c
index d7e0d2989..86ff94a87 100644
--- a/src/net/peerdisc.c
+++ b/src/net/peerdisc.c
@@ -189,7 +189,7 @@ static void peerdisc_socket_tx ( const char *uuid, const char *id ) {
/* Skip unopened network devices */
if ( ! netdev_is_open ( netdev ) )
continue;
- address.st.st_scope_id = netdev->index;
+ address.st.st_scope_id = netdev->scope_id;
/* Discard request (for test purposes) if applicable */
if ( inject_fault ( PEERDISC_DISCARD_RATE ) )
diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c
index fd94b5f08..9ad39656d 100644
--- a/src/net/tcp/httpcore.c
+++ b/src/net/tcp/httpcore.c
@@ -830,7 +830,9 @@ static int http_transfer_complete ( struct http_transaction *http ) {
*/
static int http_format_headers ( struct http_transaction *http, char *buf,
size_t len ) {
+ struct parameters *params = http->uri->params;
struct http_request_header *header;
+ struct parameter *param;
size_t used;
size_t remaining;
char *line;
@@ -844,7 +846,7 @@ static int http_format_headers ( struct http_transaction *http, char *buf,
DBGC2 ( http, "HTTP %p TX %s\n", http, buf );
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
- /* Construct all headers */
+ /* Construct all fixed headers */
for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) {
/* Determine header value length */
@@ -869,6 +871,23 @@ static int http_format_headers ( struct http_transaction *http, char *buf,
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
}
+ /* Construct parameter headers, if any */
+ if ( params ) {
+
+ /* Construct all parameter headers */
+ for_each_param ( param, params ) {
+
+ /* Skip non-header parameters */
+ if ( ! ( param->flags & PARAMETER_HEADER ) )
+ continue;
+
+ /* Add parameter */
+ used += ssnprintf ( ( buf + used ), ( len - used ),
+ "%s: %s\r\n", param->key,
+ param->value );
+ }
+ }
+
/* Construct terminating newline */
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
@@ -1851,14 +1870,15 @@ static struct http_state http_trailers = {
*/
/**
- * Construct HTTP parameter list
+ * Construct HTTP form parameter list
*
* @v params Parameter list
* @v buf Buffer to contain HTTP POST parameters
* @v len Length of buffer
* @ret len Length of parameter list (excluding terminating NUL)
*/
-static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
+static size_t http_form_params ( struct parameters *params, char *buf,
+ size_t len ) {
struct parameter *param;
ssize_t remaining = len;
size_t frag_len;
@@ -1867,6 +1887,10 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
len = 0;
for_each_param ( param, params ) {
+ /* Skip non-form parameters */
+ if ( ! ( param->flags & PARAMETER_FORM ) )
+ continue;
+
/* Add the "&", if applicable */
if ( len ) {
if ( remaining > 0 )
@@ -1904,53 +1928,59 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
}
/**
- * Open HTTP transaction for simple GET URI
- *
- * @v xfer Data transfer interface
- * @v uri Request URI
- * @ret rc Return status code
- */
-static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) {
-
- return http_open ( xfer, &http_get, uri, NULL, NULL );
-}
-
-/**
- * Open HTTP transaction for simple POST URI
+ * Open HTTP transaction for simple URI
*
* @v xfer Data transfer interface
* @v uri Request URI
* @ret rc Return status code
*/
-static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) {
+int http_open_uri ( struct interface *xfer, struct uri *uri ) {
struct parameters *params = uri->params;
struct http_request_content content;
+ struct http_method *method;
+ const char *type;
void *data;
size_t len;
size_t check_len;
int rc;
- /* Calculate length of parameter list */
- len = http_params ( params, NULL, 0 );
+ /* Calculate length of form parameter list, if any */
+ len = ( params ? http_form_params ( params, NULL, 0 ) : 0 );
- /* Allocate temporary parameter list */
- data = zalloc ( len + 1 /* NUL */ );
- if ( ! data ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
+ /* Use POST if and only if there are form parameters */
+ if ( len ) {
- /* Construct temporary parameter list */
- check_len = http_params ( params, data, ( len + 1 /* NUL */ ) );
- assert ( check_len == len );
+ /* Use POST */
+ method = &http_post;
+ type = "application/x-www-form-urlencoded";
+
+ /* Allocate temporary form parameter list */
+ data = zalloc ( len + 1 /* NUL */ );
+ if ( ! data ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Construct temporary form parameter list */
+ check_len = http_form_params ( params, data,
+ ( len + 1 /* NUL */ ) );
+ assert ( check_len == len );
+
+ } else {
+
+ /* Use GET */
+ method = &http_get;
+ type = NULL;
+ data = NULL;
+ }
/* Construct request content */
- content.type = "application/x-www-form-urlencoded";
+ content.type = type;
content.data = data;
content.len = len;
/* Open HTTP transaction */
- if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 )
+ if ( ( rc = http_open ( xfer, method, uri, NULL, &content ) ) != 0 )
goto err_open;
err_open:
@@ -1959,23 +1989,6 @@ static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) {
return rc;
}
-/**
- * Open HTTP transaction for simple URI
- *
- * @v xfer Data transfer interface
- * @v uri Request URI
- * @ret rc Return status code
- */
-int http_open_uri ( struct interface *xfer, struct uri *uri ) {
-
- /* Open GET/POST URI as applicable */
- if ( uri->params ) {
- return http_open_post_uri ( xfer, uri );
- } else {
- return http_open_get_uri ( xfer, uri );
- }
-}
-
/* Drag in HTTP extensions */
REQUIRING_SYMBOL ( http_open );
REQUIRE_OBJECT ( config_http );
diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c
index e36d5619d..dd20849ce 100644
--- a/src/net/tcp/iscsi.c
+++ b/src/net/tcp/iscsi.c
@@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/base16.h>
#include <ipxe/base64.h>
#include <ipxe/ibft.h>
+#include <ipxe/blockdev.h>
#include <ipxe/efi/efi_path.h>
#include <ipxe/iscsi.h>
@@ -86,6 +87,10 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 );
__einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN )
#define EINFO_EINVAL_NO_INITIATOR_IQN \
__einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" )
+#define EINVAL_MAXBURSTLENGTH \
+ __einfo_error ( EINFO_EINVAL_MAXBURSTLENGTH )
+#define EINFO_EINVAL_MAXBURSTLENGTH \
+ __einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid MaxBurstLength" )
#define EIO_TARGET_UNAVAILABLE \
__einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE )
#define EINFO_EIO_TARGET_UNAVAILABLE \
@@ -281,6 +286,9 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
/* Assign fresh initiator task tag */
iscsi_new_itt ( iscsi );
+ /* Set default operational parameters */
+ iscsi->max_burst_len = ISCSI_MAX_BURST_LEN;
+
/* Initiate login */
iscsi_start_login ( iscsi );
@@ -736,16 +744,20 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
"MaxConnections=1%c"
"InitialR2T=Yes%c"
"ImmediateData=No%c"
- "MaxRecvDataSegmentLength=8192%c"
- "MaxBurstLength=262144%c"
- "FirstBurstLength=65536%c"
+ "MaxRecvDataSegmentLength=%d%c"
+ "MaxBurstLength=%d%c"
+ "FirstBurstLength=%d%c"
"DefaultTime2Wait=0%c"
"DefaultTime2Retain=0%c"
"MaxOutstandingR2T=1%c"
"DataPDUInOrder=Yes%c"
"DataSequenceInOrder=Yes%c"
"ErrorRecoveryLevel=0%c",
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+ 0, 0, 0, 0, 0,
+ ISCSI_MAX_RECV_DATA_SEG_LEN, 0,
+ ISCSI_MAX_BURST_LEN, 0,
+ ISCSI_FIRST_BURST_LEN, 0,
+ 0, 0, 0, 0, 0, 0 );
}
return used;
@@ -909,6 +921,31 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
}
/**
+ * Handle iSCSI MaxBurstLength text value
+ *
+ * @v iscsi iSCSI session
+ * @v value MaxBurstLength value
+ * @ret rc Return status code
+ */
+static int iscsi_handle_maxburstlength_value ( struct iscsi_session *iscsi,
+ const char *value ) {
+ unsigned long max_burst_len;
+ char *end;
+
+ /* Update maximum burst length */
+ max_burst_len = strtoul ( value, &end, 0 );
+ if ( *end ) {
+ DBGC ( iscsi, "iSCSI %p invalid MaxBurstLength \"%s\"\n",
+ iscsi, value );
+ return -EINVAL_MAXBURSTLENGTH;
+ }
+ if ( max_burst_len < iscsi->max_burst_len )
+ iscsi->max_burst_len = max_burst_len;
+
+ return 0;
+}
+
+/**
* Handle iSCSI CHAP_A text value
*
* @v iscsi iSCSI session
@@ -1148,6 +1185,7 @@ struct iscsi_string_type {
/** iSCSI text strings that we want to handle */
static struct iscsi_string_type iscsi_string_types[] = {
{ "TargetAddress", iscsi_handle_targetaddress_value },
+ { "MaxBurstLength", iscsi_handle_maxburstlength_value },
{ "AuthMethod", iscsi_handle_authmethod_value },
{ "CHAP_A", iscsi_handle_chap_a_value },
{ "CHAP_I", iscsi_handle_chap_i_value },
@@ -1848,6 +1886,24 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi,
}
/**
+ * Update SCSI block device capacity
+ *
+ * @v iscsi iSCSI session
+ * @v capacity Block device capacity
+ */
+static void iscsi_scsi_capacity ( struct iscsi_session *iscsi,
+ struct block_device_capacity *capacity ) {
+ unsigned int max_count;
+
+ /* Limit maximum number of blocks per transfer to fit MaxBurstLength */
+ if ( capacity->blksize ) {
+ max_count = ( iscsi->max_burst_len / capacity->blksize );
+ if ( max_count < capacity->max_count )
+ capacity->max_count = max_count;
+ }
+}
+
+/**
* Get iSCSI ACPI descriptor
*
* @v iscsi iSCSI session
@@ -1862,6 +1918,7 @@ static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) {
static struct interface_operation iscsi_control_op[] = {
INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ),
INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ),
+ INTF_OP ( block_capacity, struct iscsi_session *, iscsi_scsi_capacity ),
INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ),
EFI_INTF_OP ( efi_describe, struct iscsi_session *, efi_iscsi_path ),
diff --git a/src/net/tls.c b/src/net/tls.c
index 4aa4d9e29..000a8a785 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -86,14 +86,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EINVAL_HANDSHAKE \
__einfo_uniqify ( EINFO_EINVAL, 0x08, \
"Invalid Handshake record" )
-#define EINVAL_STREAM __einfo_error ( EINFO_EINVAL_STREAM )
-#define EINFO_EINVAL_STREAM \
- __einfo_uniqify ( EINFO_EINVAL, 0x09, \
- "Invalid stream-ciphered record" )
-#define EINVAL_BLOCK __einfo_error ( EINFO_EINVAL_BLOCK )
-#define EINFO_EINVAL_BLOCK \
+#define EINVAL_IV __einfo_error ( EINFO_EINVAL_IV )
+#define EINFO_EINVAL_IV \
__einfo_uniqify ( EINFO_EINVAL, 0x0a, \
- "Invalid block-ciphered record" )
+ "Invalid initialisation vector" )
#define EINVAL_PADDING __einfo_error ( EINFO_EINVAL_PADDING )
#define EINFO_EINVAL_PADDING \
__einfo_uniqify ( EINFO_EINVAL, 0x0b, \
@@ -105,7 +101,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINVAL_MAC __einfo_error ( EINFO_EINVAL_MAC )
#define EINFO_EINVAL_MAC \
__einfo_uniqify ( EINFO_EINVAL, 0x0d, \
- "Invalid MAC" )
+ "Invalid MAC or authentication tag" )
#define EINVAL_TICKET __einfo_error ( EINFO_EINVAL_TICKET )
#define EINFO_EINVAL_TICKET \
__einfo_uniqify ( EINFO_EINVAL, 0x0e, \
@@ -387,10 +383,12 @@ static void free_tls ( struct refcnt *refcnt ) {
tls_clear_cipher ( tls, &tls->rx_cipherspec );
tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
free ( tls->server_key );
+ free ( tls->handshake_ctx );
list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
}
+ free_iob ( tls->rx_handshake );
x509_chain_put ( tls->certs );
x509_chain_put ( tls->chain );
x509_root_put ( tls->root );
@@ -568,8 +566,8 @@ static void tls_prf ( struct tls_connection *tls, const void *secret,
va_start ( seeds, out_len );
if ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) {
- /* Use P_SHA256 for TLSv1.2 and later */
- tls_p_hash_va ( tls, &sha256_algorithm, secret, secret_len,
+ /* Use handshake digest PRF for TLSv1.2 and later */
+ tls_p_hash_va ( tls, tls->handshake_digest, secret, secret_len,
out, out_len, seeds );
} else {
/* Use combination of P_MD5 and P_SHA-1 for TLSv1.1
@@ -662,9 +660,9 @@ static void tls_generate_master_secret ( struct tls_connection *tls,
static int tls_generate_keys ( struct tls_connection *tls ) {
struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending;
struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending;
- size_t hash_size = tx_cipherspec->suite->digest->digestsize;
+ size_t hash_size = tx_cipherspec->suite->mac_len;
size_t key_size = tx_cipherspec->suite->key_len;
- size_t iv_size = tx_cipherspec->suite->cipher->blocksize;
+ size_t iv_size = tx_cipherspec->suite->fixed_iv_len;
size_t total = ( 2 * ( hash_size + key_size + iv_size ) );
uint8_t key_block[total];
uint8_t *key;
@@ -716,15 +714,13 @@ static int tls_generate_keys ( struct tls_connection *tls ) {
key += key_size;
/* TX initialisation vector */
- cipher_setiv ( tx_cipherspec->suite->cipher,
- tx_cipherspec->cipher_ctx, key );
+ memcpy ( tx_cipherspec->fixed_iv, key, iv_size );
DBGC ( tls, "TLS %p TX IV:\n", tls );
DBGC_HD ( tls, key, iv_size );
key += iv_size;
/* RX initialisation vector */
- cipher_setiv ( rx_cipherspec->suite->cipher,
- rx_cipherspec->cipher_ctx, key );
+ memcpy ( rx_cipherspec->fixed_iv, key, iv_size );
DBGC ( tls, "TLS %p RX IV:\n", tls );
DBGC_HD ( tls, key, iv_size );
key += iv_size;
@@ -736,6 +732,83 @@ static int tls_generate_keys ( struct tls_connection *tls ) {
/******************************************************************************
*
+ * Handshake verification
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Clear handshake digest algorithm
+ *
+ * @v tls TLS connection
+ */
+static void tls_clear_handshake ( struct tls_connection *tls ) {
+
+ /* Select null digest algorithm */
+ tls->handshake_digest = &digest_null;
+
+ /* Free any existing context */
+ free ( tls->handshake_ctx );
+ tls->handshake_ctx = NULL;
+}
+
+/**
+ * Select handshake digest algorithm
+ *
+ * @v tls TLS connection
+ * @v digest Handshake digest algorithm
+ * @ret rc Return status code
+ */
+static int tls_select_handshake ( struct tls_connection *tls,
+ struct digest_algorithm *digest ) {
+
+ /* Clear existing handshake digest */
+ tls_clear_handshake ( tls );
+
+ /* Allocate and initialise context */
+ tls->handshake_ctx = malloc ( digest->ctxsize );
+ if ( ! tls->handshake_ctx )
+ return -ENOMEM;
+ tls->handshake_digest = digest;
+ digest_init ( digest, tls->handshake_ctx );
+
+ return 0;
+}
+
+/**
+ * Add handshake record to verification hash
+ *
+ * @v tls TLS connection
+ * @v data Handshake record
+ * @v len Length of handshake record
+ * @ret rc Return status code
+ */
+static int tls_add_handshake ( struct tls_connection *tls,
+ const void *data, size_t len ) {
+ struct digest_algorithm *digest = tls->handshake_digest;
+
+ digest_update ( digest, tls->handshake_ctx, data, len );
+ return 0;
+}
+
+/**
+ * Calculate handshake verification hash
+ *
+ * @v tls TLS connection
+ * @v out Output buffer
+ *
+ * Calculates the digest over all handshake messages seen so far.
+ */
+static void tls_verify_handshake ( struct tls_connection *tls, void *out ) {
+ struct digest_algorithm *digest = tls->handshake_digest;
+ uint8_t ctx[ digest->ctxsize ];
+
+ memcpy ( ctx, tls->handshake_ctx, sizeof ( ctx ) );
+ digest_final ( digest, ctx, out );
+}
+
+/******************************************************************************
+ *
* Cipher suite management
*
******************************************************************************
@@ -801,15 +874,15 @@ static int tls_set_cipher ( struct tls_connection *tls,
struct tls_cipher_suite *suite ) {
struct pubkey_algorithm *pubkey = suite->pubkey;
struct cipher_algorithm *cipher = suite->cipher;
- struct digest_algorithm *digest = suite->digest;
size_t total;
void *dynamic;
/* Clear out old cipher contents, if any */
tls_clear_cipher ( tls, cipherspec );
-
+
/* Allocate dynamic storage */
- total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize );
+ total = ( pubkey->ctxsize + cipher->ctxsize + suite->mac_len +
+ suite->fixed_iv_len );
dynamic = zalloc ( total );
if ( ! dynamic ) {
DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
@@ -821,8 +894,8 @@ static int tls_set_cipher ( struct tls_connection *tls,
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;
+ cipherspec->mac_secret = dynamic; dynamic += suite->mac_len;
+ cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len;
assert ( ( cipherspec->dynamic + total ) == dynamic );
/* Store parameters */
@@ -841,6 +914,7 @@ static int tls_set_cipher ( struct tls_connection *tls,
static int tls_select_cipher ( struct tls_connection *tls,
unsigned int cipher_suite ) {
struct tls_cipher_suite *suite;
+ struct digest_algorithm *digest;
int rc;
/* Identify cipher suite */
@@ -851,6 +925,12 @@ static int tls_select_cipher ( struct tls_connection *tls,
return -ENOTSUP_CIPHER;
}
+ /* Set handshake digest algorithm */
+ digest = ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ?
+ suite->handshake : &md5_sha1_algorithm );
+ if ( ( rc = tls_select_handshake ( tls, digest ) ) != 0 )
+ return rc;
+
/* Set ciphers */
if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending,
suite ) ) != 0 )
@@ -964,46 +1044,6 @@ tls_signature_hash_digest ( struct tls_signature_hash_id code ) {
/******************************************************************************
*
- * Handshake verification
- *
- ******************************************************************************
- */
-
-/**
- * Add handshake record to verification hash
- *
- * @v tls TLS connection
- * @v data Handshake record
- * @v len Length of handshake record
- */
-static void tls_add_handshake ( struct tls_connection *tls,
- const void *data, size_t len ) {
-
- digest_update ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx,
- data, len );
- digest_update ( &sha256_algorithm, tls->handshake_sha256_ctx,
- data, len );
-}
-
-/**
- * Calculate handshake verification hash
- *
- * @v tls TLS connection
- * @v out Output buffer
- *
- * Calculates the MD5+SHA1 or SHA256 digest over all handshake
- * messages seen so far.
- */
-static void tls_verify_handshake ( struct tls_connection *tls, void *out ) {
- struct digest_algorithm *digest = tls->handshake_digest;
- uint8_t ctx[ digest->ctxsize ];
-
- memcpy ( ctx, tls->handshake_ctx, sizeof ( ctx ) );
- digest_final ( digest, ctx, out );
-}
-
-/******************************************************************************
- *
* Record handling
*
******************************************************************************
@@ -1043,12 +1083,6 @@ static void tls_restart ( struct tls_connection *tls ) {
assert ( ! is_pending ( &tls->server_negotiation ) );
assert ( ! is_pending ( &tls->validation ) );
- /* (Re)initialise handshake context */
- digest_init ( &md5_sha1_algorithm, tls->handshake_md5_sha1_ctx );
- digest_init ( &sha256_algorithm, tls->handshake_sha256_ctx );
- tls->handshake_digest = &sha256_algorithm;
- tls->handshake_ctx = tls->handshake_sha256_ctx;
-
/* (Re)start negotiation */
tls->tx_pending = TLS_TX_CLIENT_HELLO;
tls_tx_resume ( tls );
@@ -1065,7 +1099,7 @@ static void tls_restart ( struct tls_connection *tls ) {
* @ret rc Return status code
*/
static int tls_send_handshake ( struct tls_connection *tls,
- void *data, size_t len ) {
+ const void *data, size_t len ) {
/* Add to handshake digest */
tls_add_handshake ( tls, data, len );
@@ -1075,12 +1109,16 @@ static int tls_send_handshake ( struct tls_connection *tls,
}
/**
- * Transmit Client Hello record
+ * Digest or transmit Client Hello record
*
* @v tls TLS connection
+ * @v action Action to take on Client Hello record
* @ret rc Return status code
*/
-static int tls_send_client_hello ( struct tls_connection *tls ) {
+static int tls_client_hello ( struct tls_connection *tls,
+ int ( * action ) ( struct tls_connection *tls,
+ const void *data,
+ size_t len ) ) {
struct tls_session *session = tls->session;
size_t name_len = strlen ( session->name );
struct {
@@ -1140,7 +1178,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
htonl ( sizeof ( hello ) -
sizeof ( hello.type_length ) ) );
- hello.version = htons ( tls->version );
+ hello.version = htons ( TLS_VERSION_MAX );
memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) );
hello.session_id_len = tls->session_id_len;
memcpy ( hello.session_id, tls->session_id,
@@ -1188,7 +1226,18 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
memcpy ( hello.extensions.session_ticket.data, session->ticket,
sizeof ( hello.extensions.session_ticket.data ) );
- return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
+ return action ( tls, &hello, sizeof ( hello ) );
+}
+
+/**
+ * Transmit Client Hello record
+ *
+ * @v tls TLS connection
+ * @ret rc Return status code
+ */
+static int tls_send_client_hello ( struct tls_connection *tls ) {
+
+ return tls_client_hello ( tls, tls_send_handshake );
}
/**
@@ -1634,9 +1683,14 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) {
* @ret rc Return status code
*/
static int tls_send_change_cipher ( struct tls_connection *tls ) {
- static const uint8_t change_cipher[1] = { 1 };
+ static const struct {
+ uint8_t spec;
+ } __attribute__ (( packed )) change_cipher = {
+ .spec = TLS_CHANGE_CIPHER_SPEC,
+ };
+
return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER,
- change_cipher, sizeof ( change_cipher ) );
+ &change_cipher, sizeof ( change_cipher ) );
}
/**
@@ -1683,20 +1737,27 @@ static int tls_send_finished ( struct tls_connection *tls ) {
* Receive new Change Cipher record
*
* @v tls TLS connection
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
static int tls_new_change_cipher ( struct tls_connection *tls,
- const void *data, size_t len ) {
+ struct io_buffer *iobuf ) {
+ const struct {
+ uint8_t spec;
+ } __attribute__ (( packed )) *change_cipher = iobuf->data;
+ size_t len = iob_len ( iobuf );
int rc;
- if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) {
+ /* Sanity check */
+ if ( ( sizeof ( *change_cipher ) != len ) ||
+ ( change_cipher->spec != TLS_CHANGE_CIPHER_SPEC ) ) {
DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls );
- DBGC_HD ( tls, data, len );
+ DBGC_HD ( tls, change_cipher, len );
return -EINVAL_CHANGE_CIPHER;
}
+ iob_pull ( iobuf, sizeof ( *change_cipher ) );
+ /* Change receive cipher spec */
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",
@@ -1712,25 +1773,27 @@ static int tls_new_change_cipher ( struct tls_connection *tls,
* Receive new Alert record
*
* @v tls TLS connection
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int tls_new_alert ( struct tls_connection *tls, const void *data,
- size_t len ) {
+static int tls_new_alert ( struct tls_connection *tls,
+ struct io_buffer *iobuf ) {
const struct {
uint8_t level;
uint8_t description;
char next[0];
- } __attribute__ (( packed )) *alert = data;
+ } __attribute__ (( packed )) *alert = iobuf->data;
+ size_t len = iob_len ( iobuf );
/* Sanity check */
if ( sizeof ( *alert ) != len ) {
DBGC ( tls, "TLS %p received overlength Alert\n", tls );
- DBGC_HD ( tls, data, len );
+ DBGC_HD ( tls, alert, len );
return -EINVAL_ALERT;
}
+ iob_pull ( iobuf, sizeof ( *alert ) );
+ /* Handle alert */
switch ( alert->level ) {
case TLS_ALERT_WARNING:
DBGC ( tls, "TLS %p received warning alert %d\n",
@@ -1898,22 +1961,18 @@ static int tls_new_server_hello ( struct tls_connection *tls,
DBGC ( tls, "TLS %p using protocol version %d.%d\n",
tls, ( version >> 8 ), ( version & 0xff ) );
- /* Use MD5+SHA1 digest algorithm for handshake verification
- * for versions earlier than TLSv1.2.
- */
- if ( ! tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) {
- tls->handshake_digest = &md5_sha1_algorithm;
- tls->handshake_ctx = tls->handshake_md5_sha1_ctx;
- }
+ /* Select cipher suite */
+ if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 )
+ return rc;
+
+ /* Add preceding Client Hello to handshake digest */
+ if ( ( rc = tls_client_hello ( tls, tls_add_handshake ) ) != 0 )
+ return rc;
/* 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;
-
/* Check session ID */
if ( hello_a->session_id_len &&
( hello_a->session_id_len == tls->session_id_len ) &&
@@ -2348,38 +2407,33 @@ static int tls_new_finished ( struct tls_connection *tls,
* Receive new Handshake record
*
* @v tls TLS connection
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
static int tls_new_handshake ( struct tls_connection *tls,
- const void *data, size_t len ) {
- size_t remaining = len;
+ struct io_buffer *iobuf ) {
+ size_t remaining;
int rc;
- while ( remaining ) {
+ while ( ( remaining = iob_len ( iobuf ) ) ) {
const struct {
uint8_t type;
tls24_t length;
uint8_t payload[0];
- } __attribute__ (( packed )) *handshake = data;
+ } __attribute__ (( packed )) *handshake = iobuf->data;
const void *payload;
size_t payload_len;
size_t record_len;
/* Parse header */
if ( sizeof ( *handshake ) > remaining ) {
- DBGC ( tls, "TLS %p received underlength Handshake\n",
- tls );
- DBGC_HD ( tls, data, remaining );
- return -EINVAL_HANDSHAKE;
+ /* Leave remaining fragment unconsumed */
+ break;
}
payload_len = tls_uint24 ( &handshake->length );
if ( payload_len > ( remaining - sizeof ( *handshake ) ) ) {
- DBGC ( tls, "TLS %p received overlength Handshake\n",
- tls );
- DBGC_HD ( tls, data, len );
- return -EINVAL_HANDSHAKE;
+ /* Leave remaining fragment unconsumed */
+ break;
}
payload = &handshake->payload;
record_len = ( sizeof ( *handshake ) + payload_len );
@@ -2426,65 +2480,87 @@ static int tls_new_handshake ( struct tls_connection *tls,
* which are explicitly excluded).
*/
if ( handshake->type != TLS_HELLO_REQUEST )
- tls_add_handshake ( tls, data, record_len );
+ tls_add_handshake ( tls, handshake, record_len );
/* Abort on failure */
if ( rc != 0 )
return rc;
/* Move to next handshake record */
- data += record_len;
- remaining -= record_len;
+ iob_pull ( iobuf, record_len );
}
return 0;
}
/**
- * Receive new record
+ * Receive new unknown record
+ *
+ * @v tls TLS connection
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int tls_new_unknown ( struct tls_connection *tls __unused,
+ struct io_buffer *iobuf ) {
+
+ /* RFC4346 says that we should just ignore unknown record types */
+ iob_pull ( iobuf, iob_len ( iobuf ) );
+ return 0;
+}
+
+/**
+ * Receive new data record
*
* @v tls TLS connection
- * @v type Record type
* @v rx_data List of received data buffers
* @ret rc Return status code
*/
-static int tls_new_record ( struct tls_connection *tls, unsigned int type,
- struct list_head *rx_data ) {
+static int tls_new_data ( struct tls_connection *tls,
+ struct list_head *rx_data ) {
struct io_buffer *iobuf;
- int ( * handler ) ( struct tls_connection *tls, const void *data,
- size_t len );
int rc;
- /* Deliver data records to the plainstream interface */
- if ( type == TLS_TYPE_DATA ) {
-
- /* Fail unless we are ready to receive data */
- if ( ! tls_ready ( tls ) )
- return -ENOTCONN;
-
- /* Deliver each I/O buffer in turn */
- while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer,
- list ) ) ) {
- list_del ( &iobuf->list );
- if ( ( rc = xfer_deliver_iob ( &tls->plainstream,
- iobuf ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not deliver data: "
- "%s\n", tls, strerror ( rc ) );
- return rc;
- }
+ /* Fail unless we are ready to receive data */
+ if ( ! tls_ready ( tls ) )
+ return -ENOTCONN;
+
+ /* Deliver each I/O buffer in turn */
+ while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer,
+ list ) ) ) {
+ list_del ( &iobuf->list );
+ if ( ( rc = xfer_deliver_iob ( &tls->plainstream,
+ iobuf ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not deliver data: "
+ "%s\n", tls, strerror ( rc ) );
+ return rc;
}
- return 0;
}
- /* For all other records, merge into a single I/O buffer */
- iobuf = iob_concatenate ( rx_data );
- if ( ! iobuf ) {
- DBGC ( tls, "TLS %p could not concatenate non-data record "
- "type %d\n", tls, type );
- return -ENOMEM_RX_CONCAT;
- }
+ return 0;
+}
+
+/**
+ * Receive new record
+ *
+ * @v tls TLS connection
+ * @v type Record type
+ * @v rx_data List of received data buffers
+ * @ret rc Return status code
+ */
+static int tls_new_record ( struct tls_connection *tls, unsigned int type,
+ struct list_head *rx_data ) {
+ int ( * handler ) ( struct tls_connection *tls,
+ struct io_buffer *iobuf );
+ struct io_buffer *tmp = NULL;
+ struct io_buffer **iobuf;
+ int rc;
+
+ /* Deliver data records as-is to the plainstream interface */
+ if ( type == TLS_TYPE_DATA )
+ return tls_new_data ( tls, rx_data );
- /* Determine handler */
+ /* Determine handler and fragment buffer */
+ iobuf = &tmp;
switch ( type ) {
case TLS_TYPE_CHANGE_CIPHER:
handler = tls_new_change_cipher;
@@ -2494,19 +2570,44 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type,
break;
case TLS_TYPE_HANDSHAKE:
handler = tls_new_handshake;
+ iobuf = &tls->rx_handshake;
break;
default:
- /* RFC4346 says that we should just ignore unknown
- * record types.
- */
- handler = NULL;
- DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
+ DBGC ( tls, "TLS %p unknown record type %d\n", tls, type );
+ handler = tls_new_unknown;
break;
}
- /* Handle record and free I/O buffer */
- rc = ( handler ? handler ( tls, iobuf->data, iob_len ( iobuf ) ) : 0 );
- free_iob ( iobuf );
+ /* Merge into a single I/O buffer */
+ if ( *iobuf )
+ list_add ( &(*iobuf)->list, rx_data );
+ *iobuf = iob_concatenate ( rx_data );
+ if ( ! *iobuf ) {
+ DBGC ( tls, "TLS %p could not concatenate non-data record "
+ "type %d\n", tls, type );
+ rc = -ENOMEM_RX_CONCAT;
+ goto err_concatenate;
+ }
+
+ /* Handle record */
+ if ( ( rc = handler ( tls, *iobuf ) ) != 0 )
+ goto err_handle;
+
+ /* Discard I/O buffer if empty */
+ if ( ! iob_len ( *iobuf ) ) {
+ free_iob ( *iobuf );
+ *iobuf = NULL;
+ }
+
+ /* Sanity check */
+ assert ( tmp == NULL );
+
+ return 0;
+
+ err_handle:
+ free_iob ( *iobuf );
+ *iobuf = NULL;
+ err_concatenate:
return rc;
}
@@ -2522,17 +2623,15 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type,
*
* @v cipherspec Cipher specification
* @v ctx Context
- * @v seq Sequence number
- * @v tlshdr TLS header
+ * @v authhdr Authentication header
*/
static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx,
- uint64_t seq, struct tls_header *tlshdr ) {
- struct digest_algorithm *digest = cipherspec->suite->digest;
+ struct tls_auth_header *authhdr ) {
+ struct tls_cipher_suite *suite = cipherspec->suite;
+ struct digest_algorithm *digest = suite->digest;
- hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize );
- seq = cpu_to_be64 ( seq );
- hmac_update ( digest, ctx, &seq, sizeof ( seq ) );
- hmac_update ( digest, ctx, tlshdr, sizeof ( *tlshdr ) );
+ hmac_init ( digest, ctx, cipherspec->mac_secret, suite->mac_len );
+ hmac_update ( digest, ctx, authhdr, sizeof ( *authhdr ) );
}
/**
@@ -2568,104 +2667,43 @@ static void tls_hmac_final ( struct tls_cipherspec *cipherspec, void *ctx,
* Calculate HMAC
*
* @v cipherspec Cipher specification
- * @v seq Sequence number
- * @v tlshdr TLS header
+ * @v authhdr Authentication header
* @v data Data
* @v len Length of data
* @v mac HMAC to fill in
*/
static void tls_hmac ( struct tls_cipherspec *cipherspec,
- uint64_t seq, struct tls_header *tlshdr,
+ struct tls_auth_header *authhdr,
const void *data, size_t len, void *hmac ) {
struct digest_algorithm *digest = cipherspec->suite->digest;
uint8_t ctx[ hmac_ctxsize ( digest ) ];
- tls_hmac_init ( cipherspec, ctx, seq, tlshdr );
+ tls_hmac_init ( cipherspec, ctx, authhdr );
tls_hmac_update ( cipherspec, ctx, data, len );
tls_hmac_final ( cipherspec, ctx, hmac );
}
/**
- * Allocate and assemble stream-ciphered record from data and MAC portions
- *
- * @v tls TLS connection
- * @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_connection *tls, const void *data, size_t len,
- void *digest, size_t *plaintext_len ) {
- size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize;
- void *plaintext;
- void *content;
- void *mac;
-
- /* Calculate stream-ciphered struct length */
- *plaintext_len = ( len + mac_len );
-
- /* Allocate stream-ciphered struct */
- plaintext = malloc ( *plaintext_len );
- if ( ! plaintext )
- return NULL;
- content = plaintext;
- mac = ( content + len );
-
- /* Fill in stream-ciphered struct */
- memcpy ( content, data, len );
- memcpy ( mac, digest, mac_len );
-
- return plaintext;
-}
-
-/**
- * Allocate and assemble block-ciphered record from data and MAC portions
+ * Calculate HMAC over list of I/O buffers
*
- * @v tls TLS connection
- * @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_connection *tls,
- const void *data, size_t len,
- void *digest, size_t *plaintext_len ) {
- size_t blocksize = tls->tx_cipherspec.suite->cipher->blocksize;
- size_t mac_len = tls->tx_cipherspec.suite->digest->digestsize;
- size_t iv_len;
- size_t padding_len;
- void *plaintext;
- void *iv;
- void *content;
- void *mac;
- void *padding;
-
- /* TLSv1.1 and later use an explicit IV */
- iv_len = ( tls_version ( tls, TLS_VERSION_TLS_1_1 ) ? blocksize : 0 );
-
- /* Calculate block-ciphered struct length */
- padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) );
- *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 );
-
- /* Allocate block-ciphered struct */
- plaintext = malloc ( *plaintext_len );
- if ( ! plaintext )
- return NULL;
- iv = plaintext;
- content = ( iv + iv_len );
- mac = ( content + len );
- padding = ( mac + mac_len );
-
- /* Fill in block-ciphered struct */
- tls_generate_random ( tls, iv, iv_len );
- memcpy ( content, data, len );
- memcpy ( mac, digest, mac_len );
- memset ( padding, padding_len, ( padding_len + 1 ) );
+ * @v cipherspec Cipher specification
+ * @v authhdr Authentication header
+ * @v list List of I/O buffers
+ * @v mac HMAC to fill in
+ */
+static void tls_hmac_list ( struct tls_cipherspec *cipherspec,
+ struct tls_auth_header *authhdr,
+ struct list_head *list, void *hmac ) {
+ struct digest_algorithm *digest = cipherspec->suite->digest;
+ uint8_t ctx[ hmac_ctxsize ( digest ) ];
+ struct io_buffer *iobuf;
- return plaintext;
+ tls_hmac_init ( cipherspec, ctx, authhdr );
+ list_for_each_entry ( iobuf, list, list ) {
+ tls_hmac_update ( cipherspec, ctx, iobuf->data,
+ iob_len ( iobuf ) );
+ }
+ tls_hmac_final ( cipherspec, ctx, hmac );
}
/**
@@ -2679,34 +2717,47 @@ static void * tls_assemble_block ( struct tls_connection *tls,
*/
static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
const void *data, size_t len ) {
- struct tls_header plaintext_tlshdr;
- struct tls_header *tlshdr;
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
- struct cipher_algorithm *cipher = cipherspec->suite->cipher;
+ struct tls_cipher_suite *suite = cipherspec->suite;
+ struct cipher_algorithm *cipher = suite->cipher;
+ struct digest_algorithm *digest = suite->digest;
+ struct {
+ uint8_t fixed[suite->fixed_iv_len];
+ uint8_t record[suite->record_iv_len];
+ } __attribute__ (( packed )) iv;
+ struct tls_auth_header authhdr;
+ struct tls_header *tlshdr;
void *plaintext = NULL;
- size_t plaintext_len;
+ size_t plaintext_len = len;
struct io_buffer *ciphertext = NULL;
size_t ciphertext_len;
- size_t mac_len = cipherspec->suite->digest->digestsize;
- uint8_t mac[mac_len];
+ size_t padding_len;
+ uint8_t mac[digest->digestsize];
+ void *tmp;
int rc;
- /* Construct header */
- plaintext_tlshdr.type = type;
- plaintext_tlshdr.version = htons ( tls->version );
- plaintext_tlshdr.length = htons ( len );
-
- /* Calculate MAC */
- tls_hmac ( cipherspec, tls->tx_seq, &plaintext_tlshdr, data, len, mac );
-
- /* Allocate and assemble plaintext struct */
- if ( is_stream_cipher ( cipher ) ) {
- plaintext = tls_assemble_stream ( tls, data, len, mac,
- &plaintext_len );
+ /* Construct initialisation vector */
+ memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
+ tls_generate_random ( tls, iv.record, sizeof ( iv.record ) );
+
+ /* Construct authentication data */
+ authhdr.seq = cpu_to_be64 ( tls->tx_seq );
+ authhdr.header.type = type;
+ authhdr.header.version = htons ( tls->version );
+ authhdr.header.length = htons ( len );
+
+ /* Calculate padding length */
+ plaintext_len += suite->mac_len;
+ if ( is_block_cipher ( cipher ) ) {
+ padding_len = ( ( ( cipher->blocksize - 1 ) &
+ -( plaintext_len + 1 ) ) + 1 );
} else {
- plaintext = tls_assemble_block ( tls, data, len, mac,
- &plaintext_len );
+ padding_len = 0;
}
+ plaintext_len += padding_len;
+
+ /* Allocate plaintext */
+ plaintext = malloc ( plaintext_len );
if ( ! plaintext ) {
DBGC ( tls, "TLS %p could not allocate %zd bytes for "
"plaintext\n", tls, plaintext_len );
@@ -2714,11 +2765,32 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
goto done;
}
+ /* Assemble plaintext */
+ tmp = plaintext;
+ memcpy ( tmp, data, len );
+ tmp += len;
+ if ( suite->mac_len )
+ tls_hmac ( cipherspec, &authhdr, data, len, mac );
+ memcpy ( tmp, mac, suite->mac_len );
+ tmp += suite->mac_len;
+ memset ( tmp, ( padding_len - 1 ), padding_len );
+ tmp += padding_len;
+ assert ( tmp == ( plaintext + plaintext_len ) );
DBGC2 ( tls, "Sending plaintext data:\n" );
DBGC2_HD ( tls, plaintext, plaintext_len );
+ /* Set initialisation vector */
+ cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) );
+
+ /* Process authentication data, if applicable */
+ if ( is_auth_cipher ( cipher ) ) {
+ cipher_encrypt ( cipher, cipherspec->cipher_ctx, &authhdr,
+ NULL, sizeof ( authhdr ) );
+ }
+
/* Allocate ciphertext */
- ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len );
+ ciphertext_len = ( sizeof ( *tlshdr ) + sizeof ( iv.record ) +
+ plaintext_len + cipher->authsize );
ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len );
if ( ! ciphertext ) {
DBGC ( tls, "TLS %p could not allocate %zd bytes for "
@@ -2731,11 +2803,14 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
tlshdr->type = type;
tlshdr->version = htons ( tls->version );
- tlshdr->length = htons ( plaintext_len );
- memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx,
- cipher->ctxsize );
- cipher_encrypt ( cipher, cipherspec->cipher_next_ctx, plaintext,
+ tlshdr->length = htons ( ciphertext_len - sizeof ( *tlshdr ) );
+ memcpy ( iob_put ( ciphertext, sizeof ( iv.record ) ), iv.record,
+ sizeof ( iv.record ) );
+ cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext,
iob_put ( ciphertext, plaintext_len ), plaintext_len );
+ cipher_auth ( cipher, cipherspec->cipher_ctx,
+ iob_put ( ciphertext, cipher->authsize ) );
+ assert ( iob_len ( ciphertext ) == ciphertext_len );
/* Free plaintext as soon as possible to conserve memory */
free ( plaintext );
@@ -2751,8 +2826,6 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
/* Update TX state machine to next record */
tls->tx_seq += 1;
- memcpy ( tls->tx_cipherspec.cipher_ctx,
- tls->tx_cipherspec.cipher_next_ctx, cipher->ctxsize );
done:
free ( plaintext );
@@ -2761,88 +2834,38 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
}
/**
- * Split stream-ciphered record into data and MAC portions
+ * Verify block padding
*
* @v tls TLS connection
- * @v rx_data List of received data buffers
- * @v mac MAC to fill in
+ * @v iobuf Last received I/O buffer
+ * @ret len Padding length, or negative error
* @ret rc Return status code
*/
-static int tls_split_stream ( struct tls_connection *tls,
- struct list_head *rx_data, void **mac ) {
- size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize;
- struct io_buffer *iobuf;
-
- /* Extract MAC */
- iobuf = list_last_entry ( rx_data, struct io_buffer, list );
- assert ( iobuf != NULL );
- if ( iob_len ( iobuf ) < mac_len ) {
- DBGC ( tls, "TLS %p received underlength MAC\n", tls );
- DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
- return -EINVAL_STREAM;
- }
- iob_unput ( iobuf, mac_len );
- *mac = iobuf->tail;
-
- return 0;
-}
-
-/**
- * Split block-ciphered record into data and MAC portions
- *
- * @v tls TLS connection
- * @v rx_data List of received data buffers
- * @v mac MAC to fill in
- * @ret rc Return status code
- */
-static int tls_split_block ( struct tls_connection *tls,
- struct list_head *rx_data, void **mac ) {
- size_t mac_len = tls->rx_cipherspec.suite->digest->digestsize;
- struct io_buffer *iobuf;
- size_t iv_len;
- uint8_t *padding_final;
+static int tls_verify_padding ( struct tls_connection *tls,
+ struct io_buffer *iobuf ) {
uint8_t *padding;
- size_t padding_len;
-
- /* TLSv1.1 and later use an explicit IV */
- iobuf = list_first_entry ( rx_data, struct io_buffer, list );
- iv_len = ( tls_version ( tls, TLS_VERSION_TLS_1_1 ) ?
- tls->rx_cipherspec.suite->cipher->blocksize : 0 );
- if ( iob_len ( iobuf ) < iv_len ) {
- DBGC ( tls, "TLS %p received underlength IV\n", tls );
- DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
- return -EINVAL_BLOCK;
- }
- iob_pull ( iobuf, iv_len );
+ unsigned int pad;
+ unsigned int i;
+ size_t len;
/* Extract and verify padding */
- iobuf = list_last_entry ( rx_data, struct io_buffer, list );
- padding_final = ( iobuf->tail - 1 );
- padding_len = *padding_final;
- if ( ( padding_len + 1 ) > iob_len ( iobuf ) ) {
+ padding = ( iobuf->tail - 1 );
+ pad = *padding;
+ len = ( pad + 1 );
+ if ( len > iob_len ( iobuf ) ) {
DBGC ( tls, "TLS %p received underlength padding\n", tls );
DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
- return -EINVAL_BLOCK;
+ return -EINVAL_PADDING;
}
- iob_unput ( iobuf, ( padding_len + 1 ) );
- for ( padding = iobuf->tail ; padding < padding_final ; padding++ ) {
- if ( *padding != padding_len ) {
+ for ( i = 0 ; i < pad ; i++ ) {
+ if ( *(--padding) != pad ) {
DBGC ( tls, "TLS %p received bad padding\n", tls );
- DBGC_HD ( tls, padding, padding_len );
+ DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
return -EINVAL_PADDING;
}
}
- /* Extract MAC */
- if ( iob_len ( iobuf ) < mac_len ) {
- DBGC ( tls, "TLS %p received underlength MAC\n", tls );
- DBGC_HD ( tls, iobuf->data, iob_len ( iobuf ) );
- return -EINVAL_BLOCK;
- }
- iob_unput ( iobuf, mac_len );
- *mac = iobuf->tail;
-
- return 0;
+ return len;
}
/**
@@ -2856,54 +2879,129 @@ static int tls_split_block ( struct tls_connection *tls,
static int tls_new_ciphertext ( struct tls_connection *tls,
struct tls_header *tlshdr,
struct list_head *rx_data ) {
- struct tls_header plaintext_tlshdr;
struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
- struct cipher_algorithm *cipher = cipherspec->suite->cipher;
- struct digest_algorithm *digest = cipherspec->suite->digest;
- uint8_t ctx[ hmac_ctxsize ( digest ) ];
+ struct tls_cipher_suite *suite = cipherspec->suite;
+ struct cipher_algorithm *cipher = suite->cipher;
+ struct digest_algorithm *digest = suite->digest;
+ size_t len = ntohs ( tlshdr->length );
+ struct {
+ uint8_t fixed[suite->fixed_iv_len];
+ uint8_t record[suite->record_iv_len];
+ } __attribute__ (( packed )) iv;
+ struct tls_auth_header authhdr;
uint8_t verify_mac[digest->digestsize];
+ uint8_t verify_auth[cipher->authsize];
+ struct io_buffer *first;
+ struct io_buffer *last;
struct io_buffer *iobuf;
void *mac;
- size_t len = 0;
+ void *auth;
+ size_t check_len;
+ int pad_len;
int rc;
+ /* Locate first and last data buffers */
+ assert ( ! list_empty ( rx_data ) );
+ first = list_first_entry ( rx_data, struct io_buffer, list );
+ last = list_last_entry ( rx_data, struct io_buffer, list );
+
+ /* Extract initialisation vector */
+ if ( iob_len ( first ) < sizeof ( iv.record ) ) {
+ DBGC ( tls, "TLS %p received underlength IV\n", tls );
+ DBGC_HD ( tls, first->data, iob_len ( first ) );
+ return -EINVAL_IV;
+ }
+ memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
+ memcpy ( iv.record, first->data, sizeof ( iv.record ) );
+ iob_pull ( first, sizeof ( iv.record ) );
+ len -= sizeof ( iv.record );
+
+ /* Extract unencrypted authentication tag */
+ if ( iob_len ( last ) < cipher->authsize ) {
+ DBGC ( tls, "TLS %p received underlength authentication tag\n",
+ tls );
+ DBGC_HD ( tls, last->data, iob_len ( last ) );
+ return -EINVAL_MAC;
+ }
+ iob_unput ( last, cipher->authsize );
+ len -= cipher->authsize;
+ auth = last->tail;
+
+ /* Construct authentication data */
+ authhdr.seq = cpu_to_be64 ( tls->rx_seq );
+ authhdr.header.type = tlshdr->type;
+ authhdr.header.version = tlshdr->version;
+ authhdr.header.length = htons ( len );
+
+ /* Set initialisation vector */
+ cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) );
+
+ /* Process authentication data, if applicable */
+ if ( is_auth_cipher ( cipher ) ) {
+ cipher_decrypt ( cipher, cipherspec->cipher_ctx, &authhdr,
+ NULL, sizeof ( authhdr ) );
+ }
+
/* Decrypt the received data */
+ check_len = 0;
list_for_each_entry ( iobuf, &tls->rx_data, list ) {
cipher_decrypt ( cipher, cipherspec->cipher_ctx,
iobuf->data, iobuf->data, iob_len ( iobuf ) );
+ check_len += iob_len ( iobuf );
}
+ assert ( check_len == len );
- /* Split record into content and MAC */
- if ( is_stream_cipher ( cipher ) ) {
- if ( ( rc = tls_split_stream ( tls, rx_data, &mac ) ) != 0 )
- return rc;
- } else {
- if ( ( rc = tls_split_block ( tls, rx_data, &mac ) ) != 0 )
- return rc;
+ /* Strip block padding, if applicable */
+ if ( is_block_cipher ( cipher ) ) {
+ pad_len = tls_verify_padding ( tls, last );
+ if ( pad_len < 0 ) {
+ /* Assume zero padding length to avoid timing attacks */
+ pad_len = 0;
+ }
+ iob_unput ( last, pad_len );
+ len -= pad_len;
}
- /* Calculate total length */
+ /* Extract decrypted MAC */
+ if ( iob_len ( last ) < suite->mac_len ) {
+ DBGC ( tls, "TLS %p received underlength MAC\n", tls );
+ DBGC_HD ( tls, last->data, iob_len ( last ) );
+ return -EINVAL_MAC;
+ }
+ iob_unput ( last, suite->mac_len );
+ len -= suite->mac_len;
+ mac = last->tail;
+
+ /* Dump received data */
DBGC2 ( tls, "Received plaintext data:\n" );
+ check_len = 0;
list_for_each_entry ( iobuf, rx_data, list ) {
DBGC2_HD ( tls, iobuf->data, iob_len ( iobuf ) );
- len += iob_len ( iobuf );
+ check_len += iob_len ( iobuf );
}
+ assert ( check_len == len );
+
+ /* Generate MAC */
+ authhdr.header.length = htons ( len );
+ if ( suite->mac_len )
+ tls_hmac_list ( cipherspec, &authhdr, rx_data, verify_mac );
+
+ /* Generate authentication tag */
+ cipher_auth ( cipher, cipherspec->cipher_ctx, verify_auth );
/* Verify MAC */
- plaintext_tlshdr.type = tlshdr->type;
- plaintext_tlshdr.version = tlshdr->version;
- plaintext_tlshdr.length = htons ( len );
- tls_hmac_init ( cipherspec, ctx, tls->rx_seq, &plaintext_tlshdr );
- list_for_each_entry ( iobuf, rx_data, list ) {
- tls_hmac_update ( cipherspec, ctx, iobuf->data,
- iob_len ( iobuf ) );
- }
- tls_hmac_final ( cipherspec, ctx, verify_mac );
- if ( memcmp ( mac, verify_mac, sizeof ( verify_mac ) ) != 0 ) {
+ if ( memcmp ( mac, verify_mac, suite->mac_len ) != 0 ) {
DBGC ( tls, "TLS %p failed MAC verification\n", tls );
return -EINVAL_MAC;
}
+ /* Verify authentication tag */
+ if ( memcmp ( auth, verify_auth, cipher->authsize ) != 0 ) {
+ DBGC ( tls, "TLS %p failed authentication tag verification\n",
+ tls );
+ return -EINVAL_MAC;
+ }
+
/* Process plaintext record */
if ( ( rc = tls_new_record ( tls, tlshdr->type, rx_data ) ) != 0 )
return rc;
@@ -3008,13 +3106,24 @@ static struct interface_descriptor tls_plainstream_desc =
* @ret rc Returned status code
*/
static int tls_newdata_process_header ( struct tls_connection *tls ) {
+ struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ struct cipher_algorithm *cipher = cipherspec->suite->cipher;
+ size_t iv_len = cipherspec->suite->record_iv_len;
size_t data_len = ntohs ( tls->rx_header.length );
size_t remaining = data_len;
size_t frag_len;
+ size_t reserve;
struct io_buffer *iobuf;
struct io_buffer *tmp;
int rc;
+ /* Sanity check */
+ assert ( ( TLS_RX_BUFSIZE % cipher->alignsize ) == 0 );
+
+ /* Calculate alignment reservation at start of first data buffer */
+ reserve = ( ( -iv_len ) & ( cipher->alignsize - 1 ) );
+ remaining += reserve;
+
/* Allocate data buffers now that we know the length */
assert ( list_empty ( &tls->rx_data ) );
while ( remaining ) {
@@ -3049,6 +3158,13 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) {
*/
iob_reserve ( iobuf, ( iob_tailroom ( iobuf ) - frag_len ) );
+ /* Ensure first buffer length will be aligned to a
+ * multiple of the cipher alignment size after
+ * stripping the record IV.
+ */
+ iob_reserve ( iobuf, reserve );
+ reserve = 0;
+
/* Add I/O buffer to list */
list_add_tail ( &iobuf->list, &tls->rx_data );
}
@@ -3495,6 +3611,7 @@ int add_tls ( struct interface *xfer, const char *name,
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
tls_clear_cipher ( tls, &tls->rx_cipherspec );
tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
+ tls_clear_handshake ( tls );
tls->client_random.gmt_unix_time = time ( NULL );
iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0,
sizeof ( tls->rx_header ) );
diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c
index a335a778a..bd2c4a197 100644
--- a/src/net/udp/dhcp.c
+++ b/src/net/udp/dhcp.c
@@ -46,7 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/dhcp.h>
#include <ipxe/dhcpopts.h>
#include <ipxe/dhcppkt.h>
-#include <ipxe/dhcp_arch.h>
+#include <ipxe/dhcparch.h>
#include <ipxe/features.h>
#include <config/dhcp.h>
@@ -571,6 +571,10 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
if ( peer->sin_port != htons ( BOOTPS_PORT ) )
return;
+ /* Filter out non-selected servers */
+ if ( server_id.s_addr != dhcp->server.s_addr )
+ return;
+
/* Handle DHCPNAK */
if ( msgtype == DHCPNAK ) {
dhcp_defer ( dhcp );
@@ -580,8 +584,6 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
/* Filter out unacceptable responses */
if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) )
return;
- if ( server_id.s_addr != dhcp->server.s_addr )
- return;
if ( ip.s_addr != dhcp->offer.s_addr )
return;
@@ -599,6 +601,12 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
return;
}
+ /* Unregister any existing ProxyDHCP or PXEBS settings */
+ if ( ( settings = find_settings ( PROXYDHCP_SETTINGS_NAME ) ) != NULL )
+ unregister_settings ( settings );
+ if ( ( settings = find_settings ( PXEBS_SETTINGS_NAME ) ) != NULL )
+ unregister_settings ( settings );
+
/* Perform ProxyDHCP if applicable */
if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ &&
( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) {
diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c
index 253032e4e..9e27dec6f 100644
--- a/src/net/udp/dhcpv6.c
+++ b/src/net/udp/dhcpv6.c
@@ -40,7 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/crc32.h>
#include <ipxe/errortab.h>
#include <ipxe/ipv6.h>
-#include <ipxe/dhcp_arch.h>
+#include <ipxe/dhcparch.h>
#include <ipxe/dhcpv6.h>
/** @file
@@ -955,7 +955,7 @@ int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT );
addresses.server.sin6.sin6_family = AF_INET6;
ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr );
- addresses.server.sin6.sin6_scope_id = netdev->index;
+ addresses.server.sin6.sin6_scope_id = netdev->scope_id;
addresses.server.sin6.sin6_port = htons ( DHCPV6_SERVER_PORT );
/* Construct client DUID from system UUID */
diff --git a/src/net/vlan.c b/src/net/vlan.c
index 90f2934de..d73a95711 100644
--- a/src/net/vlan.c
+++ b/src/net/vlan.c
@@ -55,6 +55,12 @@ struct vlan_device {
unsigned int priority;
};
+/** Automatic VLAN device link-layer address */
+static uint8_t vlan_auto_ll_addr[ETH_ALEN];
+
+/** Automatic VLAN tag */
+static unsigned int vlan_auto_tag;
+
/**
* Open VLAN device
*
@@ -199,8 +205,7 @@ static void vlan_sync ( struct net_device *netdev ) {
* @v tag VLAN tag
* @ret netdev VLAN device, if any
*/
-static struct net_device * vlan_find ( struct net_device *trunk,
- unsigned int tag ) {
+struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) {
struct net_device *netdev;
struct vlan_device *vlan;
@@ -288,17 +293,17 @@ struct net_protocol vlan_protocol __net_protocol = {
};
/**
- * Get the VLAN tag
+ * Get the VLAN tag control information
*
* @v netdev Network device
- * @ret tag VLAN tag, or 0 if device is not a VLAN device
+ * @ret tci VLAN tag control information, or 0 if not a VLAN device
*/
-unsigned int vlan_tag ( struct net_device *netdev ) {
+unsigned int vlan_tci ( struct net_device *netdev ) {
struct vlan_device *vlan;
if ( netdev->op == &vlan_operations ) {
vlan = netdev->priv;
- return vlan->tag;
+ return ( VLAN_TCI ( vlan->tag, vlan->priority ) );
} else {
return 0;
}
@@ -449,6 +454,47 @@ int vlan_destroy ( struct net_device *netdev ) {
}
/**
+ * Configure automatic VLAN device
+ *
+ * @v ll_addr Link-layer address
+ * @v tag VLAN tag
+ */
+void vlan_auto ( const void *ll_addr, unsigned int tag ) {
+
+ /* Record link-layer address and VLAN tag */
+ memcpy ( vlan_auto_ll_addr, ll_addr, ETH_ALEN );
+ vlan_auto_tag = tag;
+}
+
+/**
+ * Create automatic VLAN device
+ *
+ * @v trunk Trunk network device
+ * @ret rc Return status code
+ */
+static int vlan_probe ( struct net_device *trunk ) {
+ int rc;
+
+ /* Do nothing unless an automatic VLAN exists */
+ if ( ! vlan_auto_tag )
+ return 0;
+
+ /* Ignore non-trunk devices */
+ if ( ! vlan_can_be_trunk ( trunk ) )
+ return 0;
+
+ /* Ignore non-matching link-layer addresses */
+ if ( memcmp ( trunk->ll_addr, vlan_auto_ll_addr, ETH_ALEN ) != 0 )
+ return 0;
+
+ /* Create automatic VLAN device */
+ if ( ( rc = vlan_create ( trunk, vlan_auto_tag, 0 ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
* Handle trunk network device link state change
*
* @v trunk Trunk network device
@@ -504,6 +550,7 @@ static void vlan_remove ( struct net_device *trunk ) {
/** VLAN driver */
struct net_driver vlan_driver __net_driver = {
.name = "VLAN",
+ .probe = vlan_probe,
.notify = vlan_notify,
.remove = vlan_remove,
};