From 8e478e648fb68ac6f07e4e5cd80a5c1fefcb1cf5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Oct 2022 16:52:24 +0100 Subject: [crypto] Allow initialisation vector length to vary from cipher blocksize Signed-off-by: Michael Brown --- src/net/peerblk.c | 3 ++- src/net/tls.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/net') 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/tls.c b/src/net/tls.c index 4aa4d9e29..3545f12db 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -717,14 +717,14 @@ static int tls_generate_keys ( struct tls_connection *tls ) { /* TX initialisation vector */ cipher_setiv ( tx_cipherspec->suite->cipher, - tx_cipherspec->cipher_ctx, key ); + tx_cipherspec->cipher_ctx, 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 ); + rx_cipherspec->cipher_ctx, key, iv_size ); DBGC ( tls, "TLS %p RX IV:\n", tls ); DBGC_HD ( tls, key, iv_size ); key += iv_size; -- cgit v1.2.3-55-g7522 From f8565a655eacc53319962c002a38cfd0340d0b81 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Oct 2022 11:06:26 +0100 Subject: [tls] Remove support for TLSv1.0 The TLSv1.0 protocol was deprecated by RFC 8996 (along with TLSv1.1), and has been disabled by default in iPXE since commit dc785b0fb ("[tls] Default to supporting only TLSv1.1 or above") in June 2020. While there is value in continuing to support older protocols for interoperability with older server appliances, the additional complexity of supporting the implicit initialisation vector for TLSv1.0 is not worth the cost. Remove support for the obsolete TLSv1.0 protocol, to reduce complexity of the implementation and simplify ongoing maintenance. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 5 ----- src/net/tls.c | 37 ++++++------------------------------- 2 files changed, 6 insertions(+), 36 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 879e1be98..0b34dee7c 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -41,9 +41,6 @@ struct tls_header { uint16_t length; } __attribute__ (( packed )); -/** TLS version 1.0 */ -#define TLS_VERSION_TLS_1_0 0x0301 - /** TLS version 1.1 */ #define TLS_VERSION_TLS_1_1 0x0302 @@ -196,8 +193,6 @@ struct tls_cipherspec { void *pubkey_ctx; /** Bulk encryption cipher context */ void *cipher_ctx; - /** Next bulk encryption cipher context (TX only) */ - void *cipher_next_ctx; /** MAC secret */ void *mac_secret; }; diff --git a/src/net/tls.c b/src/net/tls.c index 3545f12db..9ad8448cf 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -664,8 +664,7 @@ static int tls_generate_keys ( struct tls_connection *tls ) { struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; size_t hash_size = tx_cipherspec->suite->digest->digestsize; size_t key_size = tx_cipherspec->suite->key_len; - size_t iv_size = tx_cipherspec->suite->cipher->blocksize; - size_t total = ( 2 * ( hash_size + key_size + iv_size ) ); + size_t total = ( 2 * ( hash_size + key_size ) ); uint8_t key_block[total]; uint8_t *key; int rc; @@ -715,20 +714,6 @@ static int tls_generate_keys ( struct tls_connection *tls ) { DBGC_HD ( tls, key, key_size ); key += key_size; - /* TX initialisation vector */ - cipher_setiv ( tx_cipherspec->suite->cipher, - tx_cipherspec->cipher_ctx, 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, iv_size ); - DBGC ( tls, "TLS %p RX IV:\n", tls ); - DBGC_HD ( tls, key, iv_size ); - key += iv_size; - assert ( ( key_block + total ) == key ); return 0; @@ -809,7 +794,7 @@ static int tls_set_cipher ( struct tls_connection *tls, tls_clear_cipher ( tls, cipherspec ); /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize ); + total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize ); dynamic = zalloc ( total ); if ( ! dynamic ) { DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto " @@ -821,7 +806,6 @@ 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; assert ( ( cipherspec->dynamic + total ) == dynamic ); @@ -2635,7 +2619,7 @@ static void * tls_assemble_block ( struct tls_connection *tls, 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 iv_len = blocksize; size_t padding_len; void *plaintext; void *iv; @@ -2643,9 +2627,6 @@ static void * tls_assemble_block ( struct tls_connection *tls, 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 ); @@ -2732,9 +2713,7 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, 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, + cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext, iob_put ( ciphertext, plaintext_len ), plaintext_len ); /* Free plaintext as soon as possible to conserve memory */ @@ -2751,8 +2730,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 ); @@ -2798,16 +2775,14 @@ static int tls_split_stream ( struct tls_connection *tls, 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; + size_t iv_len = tls->rx_cipherspec.suite->cipher->blocksize; struct io_buffer *iobuf; - size_t iv_len; uint8_t *padding_final; uint8_t *padding; size_t padding_len; - /* TLSv1.1 and later use an explicit IV */ + /* Extract initialisation vector */ 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 ) ); -- cgit v1.2.3-55-g7522 From d1bc872a2e3b682169da4dd708dfe143d539eaaf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 28 Oct 2022 13:06:11 +0100 Subject: [tls] Formalise notions of fixed and record initialisation vectors TLS block ciphers always use CBC (as per RFC 5246 section 6.2.3.2) with a record initialisation vector length that is equal to the cipher block size, and no fixed initialisation vector. The initialisation vector for AEAD ciphers such as GCM is less straightforward, and requires both a fixed and per-record component. Extend the definition of a cipher suite to include fixed and record initialisation vector lengths, and generate the fixed portion (if any) as part of key expansion. Do not add explicit calls to cipher_setiv() in tls_assemble_block() and tls_split_block(), since the constraints imposed by RFC 5246 are specifically chosen to allow implementations to avoid doing so. (Instead, add a sanity check that the record initialisation vector length is equal to the cipher block size.) Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 8 ++++++++ src/crypto/mishmash/rsa_aes_cbc_sha256.c | 8 ++++++++ src/include/ipxe/tls.h | 10 ++++++++-- src/net/tls.c | 27 ++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 5 deletions(-) (limited to 'src/net') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index b054a01c7..765ed1138 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -34,6 +34,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -45,6 +47,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -56,6 +60,8 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -67,6 +73,8 @@ struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index b003523d5..1cc7dfe27 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -34,6 +34,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -45,6 +47,8 @@ struct tls_cipher_suite tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { .code = htons ( TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -56,6 +60,8 @@ struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), .key_len = ( 128 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -67,6 +73,8 @@ struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), .key_len = ( 256 / 8 ), + .fixed_iv_len = 0, + .record_iv_len = AES_BLOCKSIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 0b34dee7c..8bb1ccceb 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -169,10 +169,14 @@ struct tls_cipher_suite { struct cipher_algorithm *cipher; /** MAC digest algorithm */ struct digest_algorithm *digest; - /** Key length */ - uint16_t key_len; /** Numeric code (in network-endian order) */ uint16_t code; + /** Key length */ + uint8_t key_len; + /** Fixed initialisation vector length */ + uint8_t fixed_iv_len; + /** Record initialisation vector length */ + uint8_t record_iv_len; }; /** TLS cipher suite table */ @@ -195,6 +199,8 @@ struct tls_cipherspec { void *cipher_ctx; /** MAC secret */ void *mac_secret; + /** Fixed initialisation vector */ + void *fixed_iv; }; /** A TLS signature and hash algorithm identifier */ diff --git a/src/net/tls.c b/src/net/tls.c index 9ad8448cf..f4f8d930d 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -664,7 +664,8 @@ static int tls_generate_keys ( struct tls_connection *tls ) { struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; size_t hash_size = tx_cipherspec->suite->digest->digestsize; size_t key_size = tx_cipherspec->suite->key_len; - size_t total = ( 2 * ( hash_size + key_size ) ); + 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; int rc; @@ -714,6 +715,18 @@ static int tls_generate_keys ( struct tls_connection *tls ) { DBGC_HD ( tls, key, key_size ); key += key_size; + /* TX initialisation vector */ + 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 */ + 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; + assert ( ( key_block + total ) == key ); return 0; @@ -792,9 +805,10 @@ static int tls_set_cipher ( struct tls_connection *tls, /* Clear out old cipher contents, if any */ tls_clear_cipher ( tls, cipherspec ); - + /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize ); + total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize + + suite->fixed_iv_len ); dynamic = zalloc ( total ); if ( ! dynamic ) { DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto " @@ -807,6 +821,7 @@ static int tls_set_cipher ( struct tls_connection *tls, cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize; cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize; cipherspec->mac_secret = dynamic; dynamic += digest->digestsize; + cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len; assert ( ( cipherspec->dynamic + total ) == dynamic ); /* Store parameters */ @@ -2627,6 +2642,9 @@ static void * tls_assemble_block ( struct tls_connection *tls, void *mac; void *padding; + /* Sanity check */ + assert ( iv_len == tls->tx_cipherspec.suite->record_iv_len ); + /* 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 ); @@ -2781,6 +2799,9 @@ static int tls_split_block ( struct tls_connection *tls, uint8_t *padding; size_t padding_len; + /* Sanity check */ + assert ( iv_len == tls->rx_cipherspec.suite->record_iv_len ); + /* Extract initialisation vector */ iobuf = list_first_entry ( rx_data, struct io_buffer, list ); if ( iob_len ( iobuf ) < iv_len ) { -- cgit v1.2.3-55-g7522 From 6a360ebfde9921b9cacbee724fe25d646e4499d5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 30 Oct 2022 13:05:01 +0000 Subject: [tls] Ensure cipher alignment size is respected Adjust the length of the first received ciphertext data buffer to ensure that all decryption operations respect the cipher's alignment size. Signed-off-by: Michael Brown --- src/net/tls.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/net') diff --git a/src/net/tls.c b/src/net/tls.c index f4f8d930d..d2b8d6091 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -3004,13 +3004,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 ) { @@ -3045,6 +3056,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 ); } -- cgit v1.2.3-55-g7522 From b6eef1485808093f9dae4fe9d6b685e01a6d65a4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Nov 2022 18:34:37 +0000 Subject: [tls] Abstract out concept of a TLS authentication header All TLS cipher types use a common structure for the per-record data that is authenticated in addition to the plaintext itself. This data is used as a prefix in the HMAC calculation for stream and block ciphers, or as additional authenticated data for AEAD ciphers. Define a "TLS authentication header" structure to hold this data as a contiguous block, in order to meet the alignment requirement for AEAD ciphers such as GCM. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 8 ++++++++ src/net/tls.c | 40 +++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 21 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8bb1ccceb..be192b7ef 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -122,6 +122,14 @@ struct tls_header { /* TLS renegotiation information extension */ #define TLS_RENEGOTIATION_INFO 0xff01 +/** TLS authentication header */ +struct tls_auth_header { + /** Sequence number */ + uint64_t seq; + /** TLS header */ + struct tls_header header; +} __attribute__ (( packed )); + /** TLS verification data */ struct tls_verify_data { /** Client verification data */ diff --git a/src/net/tls.c b/src/net/tls.c index d2b8d6091..a613a08ed 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -2521,17 +2521,14 @@ 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 tls_auth_header *authhdr ) { struct digest_algorithm *digest = cipherspec->suite->digest; hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize ); - seq = cpu_to_be64 ( seq ); - hmac_update ( digest, ctx, &seq, sizeof ( seq ) ); - hmac_update ( digest, ctx, tlshdr, sizeof ( *tlshdr ) ); + hmac_update ( digest, ctx, authhdr, sizeof ( *authhdr ) ); } /** @@ -2567,19 +2564,18 @@ 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 ); } @@ -2678,10 +2674,10 @@ 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_auth_header authhdr; + struct tls_header *tlshdr; void *plaintext = NULL; size_t plaintext_len; struct io_buffer *ciphertext = NULL; @@ -2691,12 +2687,13 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, int rc; /* Construct header */ - plaintext_tlshdr.type = type; - plaintext_tlshdr.version = htons ( tls->version ); - plaintext_tlshdr.length = htons ( len ); + authhdr.seq = cpu_to_be64 ( tls->tx_seq ); + authhdr.header.type = type; + authhdr.header.version = htons ( tls->version ); + authhdr.header.length = htons ( len ); /* Calculate MAC */ - tls_hmac ( cipherspec, tls->tx_seq, &plaintext_tlshdr, data, len, mac ); + tls_hmac ( cipherspec, &authhdr, data, len, mac ); /* Allocate and assemble plaintext struct */ if ( is_stream_cipher ( cipher ) ) { @@ -2852,10 +2849,10 @@ 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; + struct tls_auth_header authhdr; uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t verify_mac[digest->digestsize]; struct io_buffer *iobuf; @@ -2886,10 +2883,11 @@ static int tls_new_ciphertext ( struct tls_connection *tls, } /* 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 ); + authhdr.seq = cpu_to_be64 ( tls->rx_seq ); + authhdr.header.type = tlshdr->type; + authhdr.header.version = tlshdr->version; + authhdr.header.length = htons ( len ); + tls_hmac_init ( cipherspec, ctx, &authhdr ); list_for_each_entry ( iobuf, rx_data, list ) { tls_hmac_update ( cipherspec, ctx, iobuf->data, iob_len ( iobuf ) ); -- cgit v1.2.3-55-g7522 From c453b4c284dbedb5de0663f6b30878b425a7a3e8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Nov 2022 18:11:36 +0000 Subject: [tls] Add MAC length as a cipher suite parameter TLS stream and block ciphers use a MAC with a length equal to the output length of the digest algorithm in use. For AEAD ciphers there is no MAC, with the equivalent functionality provided by the cipher algorithm's authentication tag. Allow for the existence of AEAD cipher suites by making the MAC length a parameter of the cipher suite. Assume that the MAC key length is equal to the MAC length, since this is true for all currently supported cipher suites. Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 4 ++++ src/crypto/mishmash/rsa_aes_cbc_sha256.c | 4 ++++ src/include/ipxe/tls.h | 2 ++ src/net/tls.c | 34 +++++++++++++++++--------------- 4 files changed, 28 insertions(+), 16 deletions(-) (limited to 'src/net') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 765ed1138..4f399a036 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -36,6 +36,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -49,6 +50,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -62,6 +64,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -75,6 +78,7 @@ tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA1_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index 1cc7dfe27..4b02a7743 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -36,6 +36,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -49,6 +50,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_dhe_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -62,6 +64,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .key_len = ( 128 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, @@ -75,6 +78,7 @@ tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .key_len = ( 256 / 8 ), .fixed_iv_len = 0, .record_iv_len = AES_BLOCKSIZE, + .mac_len = SHA256_DIGEST_SIZE, .exchange = &tls_pubkey_exchange_algorithm, .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index be192b7ef..8796fe931 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -185,6 +185,8 @@ struct tls_cipher_suite { uint8_t fixed_iv_len; /** Record initialisation vector length */ uint8_t record_iv_len; + /** MAC length */ + uint8_t mac_len; }; /** TLS cipher suite table */ diff --git a/src/net/tls.c b/src/net/tls.c index a613a08ed..06d01aeb3 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -662,7 +662,7 @@ 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->fixed_iv_len; size_t total = ( 2 * ( hash_size + key_size + iv_size ) ); @@ -799,7 +799,6 @@ 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; @@ -807,7 +806,7 @@ static int tls_set_cipher ( struct tls_connection *tls, tls_clear_cipher ( tls, cipherspec ); /* Allocate dynamic storage */ - total = ( pubkey->ctxsize + cipher->ctxsize + digest->digestsize + + total = ( pubkey->ctxsize + cipher->ctxsize + suite->mac_len + suite->fixed_iv_len ); dynamic = zalloc ( total ); if ( ! dynamic ) { @@ -820,7 +819,7 @@ 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->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 ); @@ -2525,9 +2524,10 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type, */ static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx, struct tls_auth_header *authhdr ) { - struct digest_algorithm *digest = cipherspec->suite->digest; + struct tls_cipher_suite *suite = cipherspec->suite; + struct digest_algorithm *digest = suite->digest; - hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize ); + hmac_init ( digest, ctx, cipherspec->mac_secret, suite->mac_len ); hmac_update ( digest, ctx, authhdr, sizeof ( *authhdr ) ); } @@ -2593,7 +2593,7 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, 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; + size_t mac_len = tls->tx_cipherspec.suite->mac_len; void *plaintext; void *content; void *mac; @@ -2629,7 +2629,7 @@ 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 mac_len = tls->tx_cipherspec.suite->mac_len; size_t iv_len = blocksize; size_t padding_len; void *plaintext; @@ -2675,15 +2675,16 @@ 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_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 tls_auth_header authhdr; struct tls_header *tlshdr; void *plaintext = NULL; size_t plaintext_len; struct io_buffer *ciphertext = NULL; size_t ciphertext_len; - size_t mac_len = cipherspec->suite->digest->digestsize; - uint8_t mac[mac_len]; + uint8_t mac[digest->digestsize]; int rc; /* Construct header */ @@ -2762,7 +2763,7 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, */ 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; + size_t mac_len = tls->rx_cipherspec.suite->mac_len; struct io_buffer *iobuf; /* Extract MAC */ @@ -2789,7 +2790,7 @@ static int tls_split_stream ( struct tls_connection *tls, */ 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; + size_t mac_len = tls->rx_cipherspec.suite->mac_len; size_t iv_len = tls->rx_cipherspec.suite->cipher->blocksize; struct io_buffer *iobuf; uint8_t *padding_final; @@ -2850,8 +2851,9 @@ static int tls_new_ciphertext ( struct tls_connection *tls, struct tls_header *tlshdr, struct list_head *rx_data ) { struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; - struct cipher_algorithm *cipher = cipherspec->suite->cipher; - struct digest_algorithm *digest = cipherspec->suite->digest; + struct tls_cipher_suite *suite = cipherspec->suite; + struct cipher_algorithm *cipher = suite->cipher; + struct digest_algorithm *digest = suite->digest; struct tls_auth_header authhdr; uint8_t ctx[ hmac_ctxsize ( digest ) ]; uint8_t verify_mac[digest->digestsize]; @@ -2893,7 +2895,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls, 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; } -- cgit v1.2.3-55-g7522 From 634a86093af9a6d134be8662f25616f4edfec683 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Nov 2022 23:42:02 +0000 Subject: [tls] Allow for arbitrary-length initialisation vectors Restructure the encryption and decryption operations to allow for the use of ciphers where the initialisation vector is constructed by concatenating the fixed IV (derived as part of key expansion) with a record IV (prepended to the ciphertext). Signed-off-by: Michael Brown --- src/include/ipxe/crypto.h | 4 + src/net/tls.c | 330 ++++++++++++++++++++-------------------------- 2 files changed, 148 insertions(+), 186 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index ba09c9468..a15d5eba0 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -249,6 +249,10 @@ static inline int is_stream_cipher ( struct cipher_algorithm *cipher ) { return ( cipher->blocksize == 1 ); } +static inline int is_block_cipher ( struct cipher_algorithm *cipher ) { + return ( cipher->blocksize > 1 ); +} + static inline int is_auth_cipher ( struct cipher_algorithm *cipher ) { return cipher->authsize; } diff --git a/src/net/tls.c b/src/net/tls.c index 06d01aeb3..fdaa2190d 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, \ @@ -2581,86 +2577,26 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec, } /** - * Allocate and assemble stream-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 * __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->mac_len; - 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 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->mac_len; - size_t iv_len = blocksize; - size_t padding_len; - void *plaintext; - void *iv; - void *content; - void *mac; - void *padding; - - /* Sanity check */ - assert ( iv_len == tls->tx_cipherspec.suite->record_iv_len ); - - /* 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 ); } /** @@ -2678,32 +2614,43 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, 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 padding_len; uint8_t mac[digest->digestsize]; + void *tmp; int rc; - /* Construct header */ + /* 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 MAC */ - tls_hmac ( cipherspec, &authhdr, data, len, mac ); - - /* Allocate and assemble plaintext struct */ - if ( is_stream_cipher ( cipher ) ) { - plaintext = tls_assemble_stream ( tls, data, len, mac, - &plaintext_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 ); @@ -2711,11 +2658,26 @@ 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 ) ); + /* Allocate ciphertext */ - ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len ); + ciphertext_len = ( sizeof ( *tlshdr ) + sizeof ( iv.record ) + + plaintext_len ); ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len ); if ( ! ciphertext ) { DBGC ( tls, "TLS %p could not allocate %zd bytes for " @@ -2728,9 +2690,12 @@ 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 ); + 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 ); + assert ( iob_len ( ciphertext ) == ciphertext_len ); /* Free plaintext as soon as possible to conserve memory */ free ( plaintext ); @@ -2754,89 +2719,38 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, } /** - * Split stream-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_stream ( struct tls_connection *tls, - struct list_head *rx_data, void **mac ) { - size_t mac_len = tls->rx_cipherspec.suite->mac_len; - 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 + * 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_block ( struct tls_connection *tls, - struct list_head *rx_data, void **mac ) { - size_t mac_len = tls->rx_cipherspec.suite->mac_len; - size_t iv_len = tls->rx_cipherspec.suite->cipher->blocksize; - struct io_buffer *iobuf; - uint8_t *padding_final; +static int tls_verify_padding ( struct tls_connection *tls, + struct io_buffer *iobuf ) { uint8_t *padding; - size_t padding_len; - - /* Sanity check */ - assert ( iv_len == tls->rx_cipherspec.suite->record_iv_len ); - - /* Extract initialisation vector */ - iobuf = list_first_entry ( rx_data, struct io_buffer, list ); - 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; } /** @@ -2854,47 +2768,91 @@ static int tls_new_ciphertext ( struct tls_connection *tls, 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 ctx[ hmac_ctxsize ( digest ) ]; uint8_t verify_mac[digest->digestsize]; + struct io_buffer *first; + struct io_buffer *last; struct io_buffer *iobuf; void *mac; - size_t len = 0; + 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 ); + + /* 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 ) ); + /* 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 ) + /* Strip block padding, if applicable */ + if ( is_block_cipher ( cipher ) ) { + pad_len = tls_verify_padding ( tls, last ); + if ( pad_len < 0 ) { + rc = pad_len; return rc; + } + iob_unput ( last, pad_len ); + len -= pad_len; + } + + /* 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; - /* Calculate total length */ + /* 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 ); - /* Verify MAC */ - authhdr.seq = cpu_to_be64 ( tls->rx_seq ); - authhdr.header.type = tlshdr->type; - authhdr.header.version = tlshdr->version; + /* Generate MAC */ authhdr.header.length = htons ( len ); - tls_hmac_init ( cipherspec, ctx, &authhdr ); - 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 ( suite->mac_len ) + tls_hmac_list ( cipherspec, &authhdr, rx_data, verify_mac ); + + /* Verify MAC */ if ( memcmp ( mac, verify_mac, suite->mac_len ) != 0 ) { DBGC ( tls, "TLS %p failed MAC verification\n", tls ); return -EINVAL_MAC; -- cgit v1.2.3-55-g7522 From 186306d6199096b7a7c4b4574d4be8cdb8426729 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Nov 2022 15:10:25 +0000 Subject: [tls] Treat invalid block padding as zero length padding Harden against padding oracle attacks by treating invalid block padding as zero length padding, thereby deferring the failure until after computing the (incorrect) MAC. Signed-off-by: Michael Brown --- src/net/tls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/net') diff --git a/src/net/tls.c b/src/net/tls.c index fdaa2190d..8a3ac3eed 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -2821,8 +2821,8 @@ static int tls_new_ciphertext ( struct tls_connection *tls, if ( is_block_cipher ( cipher ) ) { pad_len = tls_verify_padding ( tls, last ); if ( pad_len < 0 ) { - rc = pad_len; - return rc; + /* Assume zero padding length to avoid timing attacks */ + pad_len = 0; } iob_unput ( last, pad_len ); len -= pad_len; -- cgit v1.2.3-55-g7522 From 54d83e92f0989ca612c82e1a22d3be205a04ead9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 8 Nov 2022 14:29:08 +0000 Subject: [tls] Add support for AEAD ciphers Allow for AEAD cipher suites where the MAC length may be zero and the authentication is instead provided by an authenticating cipher, with the plaintext authentication tag appended to the ciphertext. Signed-off-by: Michael Brown --- src/net/tls.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'src/net') diff --git a/src/net/tls.c b/src/net/tls.c index 8a3ac3eed..0e3e68b6b 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -101,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, \ @@ -2675,9 +2675,15 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, /* 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 ) + sizeof ( iv.record ) + - plaintext_len ); + plaintext_len + cipher->authsize ); ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len ); if ( ! ciphertext ) { DBGC ( tls, "TLS %p could not allocate %zd bytes for " @@ -2695,6 +2701,8 @@ static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type, 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 */ @@ -2775,10 +2783,12 @@ static int tls_new_ciphertext ( struct tls_connection *tls, } __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; + void *auth; size_t check_len; int pad_len; int rc; @@ -2799,6 +2809,17 @@ static int tls_new_ciphertext ( struct tls_connection *tls, 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; @@ -2808,6 +2829,12 @@ static int tls_new_ciphertext ( struct tls_connection *tls, /* 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 ) { @@ -2852,12 +2879,22 @@ static int tls_new_ciphertext ( struct tls_connection *tls, 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 */ 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; -- cgit v1.2.3-55-g7522 From 51ecc054906eb0b1738c9d5541c7c4dfc15ec5fe Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 14:01:15 +0000 Subject: [tls] Always send maximum supported version in ClientHello Always send the maximum supported version in our ClientHello message, even when performing renegotiation (in which case the current version may already be lower than the maximum supported version). This is permitted by the specification, and allows the ClientHello to be reconstructed verbatim at the point of selecting the handshake digest algorithm in tls_new_server_hello(). Signed-off-by: Michael Brown --- src/net/tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/net') diff --git a/src/net/tls.c b/src/net/tls.c index 0e3e68b6b..af310a58f 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1134,7 +1134,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, -- cgit v1.2.3-55-g7522 From 7256a6eb24720adfd30c0307a415e51e9a402059 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Nov 2022 14:04:43 +0000 Subject: [tls] Allow handshake digest algorithm to be specified by cipher suite All existing cipher suites use SHA-256 as the TLSv1.2 and above handshake digest algorithm (even when using SHA-1 as the MAC digest algorithm). Some GCM cipher suites use SHA-384 as the handshake digest algorithm. Allow the cipher suite to specify the handshake (and PRF) digest algorithm to be used for TLSv1.2 and above. This requires some restructuring to allow for the fact that the ClientHello message must be included within the handshake digest, even though the relevant digest algorithm is not yet known at the point that the ClientHello is sent. Fortunately, the ClientHello may be reproduced verbatim at the point of receiving the ServerHello, so we rely on reconstructing (rather than storing) this message. Signed-off-by: Michael Brown --- src/crypto/mishmash/rsa_aes_cbc_sha1.c | 5 + src/crypto/mishmash/rsa_aes_cbc_sha256.c | 4 + src/include/ipxe/tls.h | 7 +- src/net/tls.c | 177 ++++++++++++++++++++----------- 4 files changed, 125 insertions(+), 68 deletions(-) (limited to 'src/net') diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c index 4f399a036..c2ecf1205 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suite */ @@ -41,6 +42,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 03 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_DHE_RSA_WITH_AES_256_CBC_SHA cipher suite */ @@ -55,6 +57,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 04 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */ @@ -69,6 +72,7 @@ tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite ( 13 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */ @@ -83,4 +87,5 @@ tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite ( 14 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha1_algorithm, + .handshake = &sha256_algorithm, }; diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c index 4b02a7743..7f114b1fd 100644 --- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c +++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -41,6 +41,7 @@ tls_dhe_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 01 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ @@ -55,6 +56,7 @@ tls_dhe_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 02 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ @@ -69,6 +71,7 @@ tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 11 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; /** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ @@ -83,4 +86,5 @@ tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite ( 12 ) = { .pubkey = &rsa_algorithm, .cipher = &aes_cbc_algorithm, .digest = &sha256_algorithm, + .handshake = &sha256_algorithm, }; diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 8796fe931..355814132 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -16,7 +16,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include -#include #include #include #include @@ -177,6 +176,8 @@ struct tls_cipher_suite { struct cipher_algorithm *cipher; /** MAC digest algorithm */ struct digest_algorithm *digest; + /** Handshake digest algorithm (for TLSv1.2 and above) */ + struct digest_algorithm *handshake; /** Numeric code (in network-endian order) */ uint16_t code; /** Key length */ @@ -346,10 +347,6 @@ struct tls_connection { void *server_key; /** Server Key Exchange record length */ size_t server_key_len; - /** MD5+SHA1 context for handshake verification */ - uint8_t handshake_md5_sha1_ctx[MD5_SHA1_CTX_SIZE]; - /** SHA256 context for handshake verification */ - uint8_t handshake_sha256_ctx[SHA256_CTX_SIZE]; /** Digest algorithm used for handshake verification */ struct digest_algorithm *handshake_digest; /** Digest algorithm context used for handshake verification */ diff --git a/src/net/tls.c b/src/net/tls.c index af310a58f..899629626 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -383,6 +383,7 @@ 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 ); @@ -564,8 +565,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 @@ -728,6 +729,83 @@ static int tls_generate_keys ( struct tls_connection *tls ) { return 0; } +/****************************************************************************** + * + * 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 @@ -835,6 +913,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 */ @@ -845,6 +924,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 ) @@ -956,46 +1041,6 @@ tls_signature_hash_digest ( struct tls_signature_hash_id code ) { return NULL; } -/****************************************************************************** - * - * 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 @@ -1037,12 +1082,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 ); @@ -1059,7 +1098,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 ); @@ -1069,12 +1108,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 { @@ -1182,7 +1225,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 ); } /** @@ -1892,22 +1946,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 ) && @@ -3504,6 +3554,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 ) ); -- cgit v1.2.3-55-g7522 From 5e62b4bc6c7bd7c6929b3fe540fb1ba8744fd16c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 9 Dec 2022 14:40:54 +0000 Subject: [vlan] Allow external code to identify VLAN priority as well as tag Signed-off-by: Michael Brown --- src/include/ipxe/vlan.h | 14 +++++++++++++- src/net/netdevice.c | 4 ++-- src/net/vlan.c | 8 ++++---- 3 files changed, 19 insertions(+), 7 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/vlan.h b/src/include/ipxe/vlan.h index 7f93439b3..e4baf4cf0 100644 --- a/src/include/ipxe/vlan.h +++ b/src/include/ipxe/vlan.h @@ -61,7 +61,19 @@ struct vlan_header { */ #define VLAN_PRIORITY_IS_VALID( priority ) ( (priority) <= 7 ) -extern unsigned int vlan_tag ( struct net_device *netdev ); +extern unsigned int vlan_tci ( struct net_device *netdev ); + +/** + * Get the VLAN tag + * + * @v netdev Network device + * @ret tag VLAN tag, or 0 if device is not a VLAN device + */ +static inline __attribute__ (( always_inline )) unsigned int +vlan_tag ( struct net_device *netdev ) { + return VLAN_TAG ( vlan_tci ( netdev ) ); +} + extern int vlan_can_be_trunk ( struct net_device *trunk ); extern int vlan_create ( struct net_device *trunk, unsigned int tag, unsigned int priority ); diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 5df306e8d..51d1831cc 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -1171,12 +1171,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/vlan.c b/src/net/vlan.c index 90f2934de..fe4488614 100644 --- a/src/net/vlan.c +++ b/src/net/vlan.c @@ -288,17 +288,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; } -- cgit v1.2.3-55-g7522 From d879c8e4d9f3b00c09a7d199a2681705fc5b55d0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 13 Dec 2022 14:45:44 +0000 Subject: [efi] Provide VLAN configuration protocol UEFI implements VLAN support within the Managed Network Protocol (MNP) driver, which may create child VLAN devices automatically based on stored UEFI variables. These child devices do not themselves provide a raw-packet interface via EFI_SIMPLE_NETWORK_PROTOCOL, and may be consumed only via the EFI_MANAGED_NETWORK_PROTOCOL interface. The device paths constructed for these child devices may conflict with those for the EFI_SIMPLE_NETWORK_PROTOCOL instances that iPXE attempts to install for its own VLAN devices. The upshot is that creating an iPXE VLAN device (e.g. via the "vcreate" command) will fail if the UEFI Managed Network Protocol has already created a device for the same VLAN tag. Fix by providing our own EFI_VLAN_CONFIG_PROTOCOL instance on the same device handle as EFI_SIMPLE_NETWORK_PROTOCOL. This causes the MNP driver to treat iPXE's device as supporting hardware VLAN offload, and it will therefore not attempt to install its own instance of the protocol. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_null.h | 2 + src/include/ipxe/efi/efi_snp.h | 3 + src/include/ipxe/vlan.h | 2 + src/interface/efi/efi_null.c | 42 ++++++++++ src/interface/efi/efi_snp.c | 179 +++++++++++++++++++++++++++++++++++++++- src/net/vlan.c | 3 +- 6 files changed, 227 insertions(+), 4 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/efi/efi_null.h b/src/include/ipxe/efi/efi_null.h index 297457081..d23d36349 100644 --- a/src/include/ipxe/efi/efi_null.h +++ b/src/include/ipxe/efi/efi_null.h @@ -19,9 +19,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include extern void efi_nullify_snp ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ); extern void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ); +extern void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg ); extern void efi_nullify_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *name2 ); extern void efi_nullify_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file ); extern void efi_nullify_hii ( EFI_HII_CONFIG_ACCESS_PROTOCOL *hii ); diff --git a/src/include/ipxe/efi/efi_snp.h b/src/include/ipxe/efi/efi_snp.h index c278b1d4c..96373b57d 100644 --- a/src/include/ipxe/efi/efi_snp.h +++ b/src/include/ipxe/efi/efi_snp.h @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** SNP transmit completion ring size */ #define EFI_SNP_NUM_TX 32 @@ -51,6 +52,8 @@ struct efi_snp_device { struct list_head rx; /** The network interface identifier */ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii; + /** VLAN configuration protocol */ + EFI_VLAN_CONFIG_PROTOCOL vcfg; /** Component name protocol */ EFI_COMPONENT_NAME2_PROTOCOL name2; /** Load file protocol handle */ diff --git a/src/include/ipxe/vlan.h b/src/include/ipxe/vlan.h index e4baf4cf0..8bf79234b 100644 --- a/src/include/ipxe/vlan.h +++ b/src/include/ipxe/vlan.h @@ -74,6 +74,8 @@ vlan_tag ( struct net_device *netdev ) { return VLAN_TAG ( vlan_tci ( netdev ) ); } +extern struct net_device * vlan_find ( struct net_device *trunk, + unsigned int tag ); extern int vlan_can_be_trunk ( struct net_device *trunk ); extern int vlan_create ( struct net_device *trunk, unsigned int tag, unsigned int priority ); diff --git a/src/interface/efi/efi_null.c b/src/interface/efi/efi_null.c index 29ca5b9b6..d0f0428cc 100644 --- a/src/interface/efi/efi_null.c +++ b/src/interface/efi/efi_null.c @@ -193,6 +193,48 @@ void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ) { nii->Id = ( ( intptr_t ) &efi_null_undi ); } +/****************************************************************************** + * + * VLAN configuration protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused, + UINT16 tag __unused, UINT8 priority __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused, + UINT16 *filter __unused, UINT16 *count __unused, + EFI_VLAN_FIND_DATA **entries __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused, + UINT16 tag __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_VLAN_CONFIG_PROTOCOL efi_null_vlan = { + .Set = efi_null_vlan_set, + .Find = efi_null_vlan_find, + .Remove = efi_null_vlan_remove, +}; + +/** + * Nullify VLAN configuration interface + * + * @v vcfg VLAN configuration protocol + */ +void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg ) { + + memcpy ( vcfg, &efi_null_vlan, sizeof ( *vcfg ) ); +} + /****************************************************************************** * * Component name protocol diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 6649eb1b0..088a3fdbd 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -1488,6 +1488,164 @@ static EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL efi_snp_device_nii = { .Ipv6Supported = TRUE, /* This is a raw packet interface, FFS! */ }; +/****************************************************************************** + * + * VLAN configuration protocol + * + ****************************************************************************** + */ + +/** + * Create or modify VLAN device + * + * @v vcfg VLAN configuration protocol + * @v tag VLAN tag + * @v priority Default VLAN priority + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg, + UINT16 tag, UINT8 priority ) { + struct efi_snp_device *snpdev = + container_of ( vcfg, struct efi_snp_device, vcfg ); + struct net_device *trunk = snpdev->netdev; + struct efi_saved_tpl tpl; + int rc; + + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + + /* Create or modify VLAN device */ + if ( ( rc = vlan_create ( trunk, tag, priority ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not create VLAN tag %d: %s\n", + snpdev, tag, strerror ( rc ) ); + goto err_create; + } + DBGC ( snpdev, "SNPDEV %p created VLAN tag %d priority %d\n", + snpdev, tag, priority ); + + err_create: + efi_restore_tpl ( &tpl ); + return EFIRC ( rc ); +} + +/** + * Find VLAN device(s) + * + * @v vcfg VLAN configuration protocol + * @v filter VLAN tag, or NULL to find all VLANs + * @v count Number of VLANs + * @v entries List of VLANs + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg, + UINT16 *filter, UINT16 *count, + EFI_VLAN_FIND_DATA **entries ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_snp_device *snpdev = + container_of ( vcfg, struct efi_snp_device, vcfg ); + struct net_device *trunk = snpdev->netdev; + struct net_device *vlan; + struct efi_saved_tpl tpl; + EFI_VLAN_FIND_DATA *entry; + VOID *buffer; + unsigned int tag; + unsigned int tci; + size_t len; + EFI_STATUS efirc; + int rc; + + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + + /* Count number of matching VLANs */ + *count = 0; + for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) { + if ( filter && ( tag != *filter ) ) + continue; + if ( ! ( vlan = vlan_find ( trunk, tag ) ) ) + continue; + (*count)++; + } + + /* Allocate buffer to hold results */ + len = ( (*count) * sizeof ( *entry ) ); + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, len, + &buffer ) ) != 0 ) { + rc = -EEFI ( efirc ); + goto err_alloc; + } + + /* Fill in buffer */ + *entries = buffer; + entry = *entries; + for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) { + if ( filter && ( tag != *filter ) ) + continue; + if ( ! ( vlan = vlan_find ( trunk, tag ) ) ) + continue; + tci = vlan_tci ( vlan ); + entry->VlanId = VLAN_TAG ( tci ); + entry->Priority = VLAN_PRIORITY ( tci ); + assert ( entry->VlanId == tag ); + entry++; + } + assert ( entry == &(*entries)[*count] ); + + /* Success */ + rc = 0; + + err_alloc: + efi_restore_tpl ( &tpl ); + return EFIRC ( rc ); +} + +/** + * Remove VLAN device + * + * @v vcfg VLAN configuration protocol + * @v tag VLAN tag + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg, + UINT16 tag ) { + struct efi_snp_device *snpdev = + container_of ( vcfg, struct efi_snp_device, vcfg ); + struct net_device *trunk = snpdev->netdev; + struct net_device *vlan; + struct efi_saved_tpl tpl; + int rc; + + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + + /* Identify VLAN device */ + vlan = vlan_find ( trunk, tag ); + if ( ! vlan ) { + DBGC ( snpdev, "SNPDEV %p could not find VLAN tag %d\n", + snpdev, tag ); + rc = -ENOENT; + goto err_find; + } + + /* Remove VLAN device */ + vlan_destroy ( vlan ); + DBGC ( snpdev, "SNPDEV %p removed VLAN tag %d\n", snpdev, tag ); + + /* Success */ + rc = 0; + + err_find: + efi_restore_tpl ( &tpl ); + return EFIRC ( rc ); +} + +/** VLAN configuration protocol */ +static EFI_VLAN_CONFIG_PROTOCOL efi_vlan = { + .Set = efi_vlan_set, + .Find = efi_vlan_find, + .Remove = efi_vlan_remove, +}; + /****************************************************************************** * * Component name protocol @@ -1627,6 +1785,8 @@ static int efi_snp_probe ( struct net_device *netdev ) { struct efi_snp_device *snpdev; unsigned int ifcnt; void *interface; + unsigned int tci; + char vlan_name[ 12 /* ", VLAN xxxx" + NUL */ ]; int leak = 0; EFI_STATUS efirc; int rc; @@ -1687,17 +1847,27 @@ static int efi_snp_probe ( struct net_device *netdev ) { efi_snp_undi.Fudge -= efi_undi_checksum ( &efi_snp_undi, sizeof ( efi_snp_undi ) ); + /* Populate the VLAN configuration protocol */ + memcpy ( &snpdev->vcfg, &efi_vlan, sizeof ( snpdev->vcfg ) ); + /* Populate the component name structure */ efi_snprintf ( snpdev->driver_name, ( sizeof ( snpdev->driver_name ) / sizeof ( snpdev->driver_name[0] ) ), "%s %s", product_short_name, netdev->dev->driver_name ); + tci = vlan_tci ( netdev ); + if ( tci ) { + snprintf ( vlan_name, sizeof ( vlan_name ), ", VLAN %d", + VLAN_TAG ( tci ) ); + } else { + vlan_name[0] = '\0'; + } efi_snprintf ( snpdev->controller_name, ( sizeof ( snpdev->controller_name ) / sizeof ( snpdev->controller_name[0] ) ), - "%s %s (%s, %s)", product_short_name, + "%s %s (%s, %s%s)", product_short_name, netdev->dev->driver_name, netdev->dev->name, - netdev_addr ( netdev ) ); + netdev_addr ( netdev ), vlan_name ); snpdev->name2.GetDriverName = efi_snp_get_driver_name; snpdev->name2.GetControllerName = efi_snp_get_controller_name; snpdev->name2.SupportedLanguages = "en"; @@ -1725,6 +1895,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, + &efi_vlan_config_protocol_guid, &snpdev->vcfg, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ) ) != 0 ) { @@ -1811,6 +1982,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, + &efi_vlan_config_protocol_guid, &snpdev->vcfg, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ) ) != 0 ) { @@ -1820,6 +1992,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { } efi_nullify_snp ( &snpdev->snp ); efi_nullify_nii ( &snpdev->nii ); + efi_nullify_vlan ( &snpdev->vcfg ); efi_nullify_name2 ( &snpdev->name2 ); efi_nullify_load_file ( &snpdev->load_file ); err_install_protocol_interface: @@ -1899,6 +2072,7 @@ static void efi_snp_remove ( struct net_device *netdev ) { &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, + &efi_vlan_config_protocol_guid, &snpdev->vcfg, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ) ) != 0 ) ) { @@ -1908,6 +2082,7 @@ static void efi_snp_remove ( struct net_device *netdev ) { } efi_nullify_snp ( &snpdev->snp ); efi_nullify_nii ( &snpdev->nii ); + efi_nullify_vlan ( &snpdev->vcfg ); efi_nullify_name2 ( &snpdev->name2 ); efi_nullify_load_file ( &snpdev->load_file ); if ( ! leak ) diff --git a/src/net/vlan.c b/src/net/vlan.c index fe4488614..81ec623f2 100644 --- a/src/net/vlan.c +++ b/src/net/vlan.c @@ -199,8 +199,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; -- cgit v1.2.3-55-g7522 From 47af48012e2afaaf56108466fb967009670660bb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 14 Jan 2023 00:09:20 +0000 Subject: [netdevice] Separate concept of scope ID from network device name index The network device index currently serves two purposes: acting as a sequential index for network device names ("net0", "net1", etc), and acting as an opaque unique integer identifier used in socket address scope IDs. There is no particular need for these usages to be linked, and it can lead to situations in which devices are named unexpectedly. For example: if a system has two network devices "net0" and "net1", a VLAN is created as "net1-42", and then a USB NIC is connected, then the USB NIC will be named "net3" rather than the expected "net2" since the VLAN device "net1-42" will have consumed an index. Separate the usages: rename the "index" field to "scope_id" (matching its one and only use case), and assign the name without reference to the scope ID by finding the first unused name. For consistency, assign the scope ID by similarly finding the first unused scope ID. Signed-off-by: Michael Brown --- src/include/ipxe/netdevice.h | 6 +++--- src/interface/efi/efi_pxe.c | 2 +- src/interface/efi/efi_snp.c | 4 ++-- src/net/ipv4.c | 2 +- src/net/ipv6.c | 12 ++++++------ src/net/ndp.c | 4 ++-- src/net/netdevice.c | 31 +++++++++++++++++-------------- src/net/peerdisc.c | 2 +- src/net/udp/dhcpv6.c | 2 +- src/tests/ipv6_test.c | 4 ++-- 10 files changed, 36 insertions(+), 33 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index 294f7b367..29358dba0 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -356,8 +356,8 @@ struct net_device { struct list_head list; /** List of open network devices */ struct list_head open_list; - /** Index of this network device */ - unsigned int index; + /** Scope ID */ + unsigned int scope_id; /** Name of this network device */ char name[NETDEV_NAME_LEN]; /** Underlying hardware device */ @@ -726,7 +726,7 @@ extern void netdev_close ( struct net_device *netdev ); extern void unregister_netdev ( struct net_device *netdev ); extern void netdev_irq ( struct net_device *netdev, int enable ); extern struct net_device * find_netdev ( const char *name ); -extern struct net_device * find_netdev_by_index ( unsigned int index ); +extern struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ); extern struct net_device * find_netdev_by_location ( unsigned int bus_type, unsigned int location ); extern struct net_device * diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index 15224a5e4..843ebb5e7 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -199,7 +199,7 @@ static void efi_pxe_ip_sockaddr ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip, memset ( sockaddr, 0, sizeof ( *sockaddr ) ); sockaddr->sa.sa_family = pxe->tcpip->sa_family; memcpy ( &sockaddr->se.se_addr, ip, pxe->net->net_addr_len ); - sockaddr->se.se_scope_id = pxe->netdev->index; + sockaddr->se.se_scope_id = pxe->netdev->scope_id; } /** diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 088a3fdbd..c4f7d4ea8 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -934,11 +934,11 @@ static uint8_t efi_undi_checksum ( void *data, size_t len ) { */ static unsigned int efi_undi_ifnum ( struct efi_snp_device *snpdev ) { - /* iPXE network device indexes are one-based (leaving zero + /* iPXE network device scope IDs are one-based (leaving zero * meaning "unspecified"). UNDI interface numbers are * zero-based. */ - return ( snpdev->netdev->index - 1 ); + return ( snpdev->netdev->scope_id - 1 ); } /** 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/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/netdevice.c b/src/net/netdevice.c index 51d1831cc..597c62285 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; @@ -757,12 +755,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 +923,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 +965,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; } 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/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index 253032e4e..28c6f7be4 100644 --- a/src/net/udp/dhcpv6.c +++ b/src/net/udp/dhcpv6.c @@ -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/tests/ipv6_test.c b/src/tests/ipv6_test.c index de8edc8ad..3b7d813a5 100644 --- a/src/tests/ipv6_test.c +++ b/src/tests/ipv6_test.c @@ -127,7 +127,7 @@ static const struct in6_addr sample_multicast = { /** Dummy network device used for routing tests */ static struct net_device ipv6_test_netdev = { .refcnt = REF_INIT ( ref_no_free ), - .index = 42, + .scope_id = 42, .state = NETDEV_OPEN, }; @@ -349,7 +349,7 @@ static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest, /* Perform routing */ actual = &in_dest; - miniroute = ipv6_route ( ipv6_test_netdev.index, &actual ); + miniroute = ipv6_route ( ipv6_test_netdev.scope_id, &actual ); /* Validate result */ if ( src ) { -- cgit v1.2.3-55-g7522 From c4c03e5be867a9b7be4dc48fe6576deca1dce8d8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 14 Jan 2023 00:31:54 +0000 Subject: [netdevice] Allow duplicate MAC addresses Many laptops now include the ability to specify a "system-specific MAC address" (also known as "pass-through MAC"), which is supposed to be used for both the onboard NIC and for any attached docking station or other USB NIC. This is intended to simplify interoperability with software or hardware that relies on a MAC address to recognise an individual machine: for example, a deployment server may associate the MAC address with a particular operating system image to be deployed. This therefore creates legitimate situations in which duplicate MAC addresses may exist within the same system. As described in commit 98d09a1 ("[netdevice] Avoid registering duplicate network devices"), the Xen netfront driver relies on the rejection of duplicate MAC addresses in order to inhibit registration of the emulated PCI devices that a Xen PV-HVM guest will create to shadow each of the paravirtual network devices. Move the code that rejects duplicate MAC addresses from the network device core to the Xen netfront driver, to allow for the existence of duplicate MAC addresses in non-Xen setups. Signed-off-by: Michael Brown --- src/drivers/net/ecm.c | 5 ++--- src/drivers/net/netfront.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/netfront.h | 5 +++++ src/include/ipxe/netdevice.h | 2 -- src/net/netdevice.c | 33 ----------------------------- 5 files changed, 56 insertions(+), 38 deletions(-) (limited to 'src/net') diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 68ac962ab..ab1f98370 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -121,10 +121,9 @@ int ecm_fetch_mac ( struct usb_function *func, } /* Apply system-specific MAC address as current link-layer - * address, if present and not already used. + * address, if present. */ - if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) && - ! find_netdev_by_ll_addr ( ðernet_protocol, amac ) ) { + if ( ( rc = acpi_mac ( amac ) ) == 0 ) { memcpy ( netdev->ll_addr, amac, ETH_ALEN ); DBGC ( usb, "USB %s using system-specific MAC %s\n", func->name, eth_ntoa ( netdev->ll_addr ) ); diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 1203e585c..90930a5a3 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -59,6 +59,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \ EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED ) +/** List of netfront devices */ +static LIST_HEAD ( netfront_devices ); + /****************************************************************************** * * XenStore interface @@ -952,6 +955,7 @@ static int netfront_probe ( struct xen_device *xendev ) { netdev->dev = &xendev->dev; netfront = netdev->priv; netfront->xendev = xendev; + netfront->netdev = netdev; INIT_LIST_HEAD ( &netfront->rx_partial ); DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); @@ -991,9 +995,13 @@ static int netfront_probe ( struct xen_device *xendev ) { /* Set initial link state */ netdev_link_down ( netdev ); + /* Add to list of netfront devices */ + list_add_tail ( &netfront->list, &netfront_devices ); + xen_set_drvdata ( xendev, netdev ); return 0; + list_del ( &netfront->list ); unregister_netdev ( netdev ); err_register_netdev: err_read_mac: @@ -1015,6 +1023,9 @@ static void netfront_remove ( struct xen_device *xendev ) { struct netfront_nic *netfront = netdev->priv; struct xen_hypervisor *xen = xendev->xen; + /* Remove from list of netfront devices */ + list_del ( &netfront->list ); + /* Unregister network device */ unregister_netdev ( netdev ); @@ -1033,3 +1044,41 @@ struct xen_driver netfront_driver __xen_driver = { .probe = netfront_probe, .remove = netfront_remove, }; + +/****************************************************************************** + * + * Emulated PCI device inhibitor + * + ****************************************************************************** + */ + +/** + * Inhibit emulated PCI devices + * + * @v netdev Network device + * @ret rc Return status code + */ +static int netfront_net_probe ( struct net_device *netdev ) { + struct netfront_nic *netfront; + + /* Inhibit emulated PCI devices matching an existing netfront device */ + list_for_each_entry ( netfront, &netfront_devices, list ) { + if ( ( netdev->dev != netfront->netdev->dev ) && + ( netdev->ll_protocol->ll_addr_len == ETH_ALEN ) && + ( memcmp ( netdev->hw_addr, netfront->netdev->hw_addr, + ETH_ALEN ) == 0 ) ) { + DBGC ( netfront, "NETFRONT %s inhibiting emulated %s " + "%s\n", netfront->xendev->key, + netdev->dev->driver_name, netdev->dev->name ); + return -EEXIST; + } + } + + return 0; +} + +/** Emulated PCI device inhibitor driver */ +struct net_driver netfront_net_driver __net_driver = { + .name = "netfront", + .probe = netfront_net_probe, +}; diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h index dca3ff1c5..de16d5291 100644 --- a/src/drivers/net/netfront.h +++ b/src/drivers/net/netfront.h @@ -159,6 +159,11 @@ struct netfront_nic { /** Grant references */ grant_ref_t refs[NETFRONT_REF_COUNT]; + /** Network device */ + struct net_device *netdev; + /** List of netfront NICs */ + struct list_head list; + /** Transmit ring */ struct netfront_ring tx; /** Transmit front ring */ diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index 29358dba0..af932c259 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -729,8 +729,6 @@ extern struct net_device * find_netdev ( const char *name ); extern struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ); extern struct net_device * find_netdev_by_location ( unsigned int bus_type, unsigned int location ); -extern struct net_device * -find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, const void *ll_addr ); extern struct net_device * last_opened_netdev ( void ); extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, struct net_protocol *net_protocol, const void *ll_dest, diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 597c62285..07961bf20 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -735,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", @@ -1002,27 +990,6 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type, return NULL; } -/** - * Get network device by link-layer address - * - * @v ll_protocol Link-layer protocol - * @v ll_addr Link-layer address - * @ret netdev Network device, or NULL - */ -struct net_device * find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, - const void *ll_addr ) { - struct net_device *netdev; - - list_for_each_entry ( netdev, &net_devices, list ) { - if ( ( netdev->ll_protocol == ll_protocol ) && - ( memcmp ( netdev->ll_addr, ll_addr, - ll_protocol->ll_addr_len ) == 0 ) ) - return netdev; - } - - return NULL; -} - /** * Get most recently opened network device * -- cgit v1.2.3-55-g7522 From f07630c74f3d67906baf820f080b5d0e5ad49ca4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 15 Jan 2023 22:35:44 +0000 Subject: [vlan] Support automatic VLAN device creation Add the ability to automatically create a VLAN device for a specified trunk device link-layer address and VLAN tag. Signed-off-by: Michael Brown --- src/include/ipxe/vlan.h | 1 + src/net/vlan.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) (limited to 'src/net') diff --git a/src/include/ipxe/vlan.h b/src/include/ipxe/vlan.h index 8bf79234b..20bbc891d 100644 --- a/src/include/ipxe/vlan.h +++ b/src/include/ipxe/vlan.h @@ -80,6 +80,7 @@ extern int vlan_can_be_trunk ( struct net_device *trunk ); extern int vlan_create ( struct net_device *trunk, unsigned int tag, unsigned int priority ); extern int vlan_destroy ( struct net_device *netdev ); +extern void vlan_auto ( const void *ll_addr, unsigned int tag ); extern void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag, struct io_buffer *iobuf ); extern void vlan_netdev_rx_err ( struct net_device *netdev, unsigned int tag, diff --git a/src/net/vlan.c b/src/net/vlan.c index 81ec623f2..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 * @@ -447,6 +453,47 @@ int vlan_destroy ( struct net_device *netdev ) { return 0; } +/** + * 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 * @@ -503,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, }; -- cgit v1.2.3-55-g7522 From 08740220baba87cbc6acb1c00cd5b492ac0c5a08 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 17 Jan 2023 12:42:46 +0000 Subject: [netdevice] Ensure consistent interpretation of "netX" device name Ensure that the "${netX/...}" settings mechanism always uses the same interpretation of the network device corresponding to "netX" as any other mechanism that performs a name-based lookup of a network device. Signed-off-by: Michael Brown --- src/net/netdev_settings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/net') 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 { -- cgit v1.2.3-55-g7522 From 2061d658b3d199ec84976e6a573f68424369be69 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 22 Jan 2023 16:54:20 +0000 Subject: [dhcp] Simplify platform-specific client architecture definitions Move the platform-specific DHCP client architecture definitions to header files of the form . This simplifies the directory structure and allows the otherwise unused arch/$(ARCH)/include/$(PLATFORM) to be removed from the include directory search path, which avoids the confusing situation in which a header file may potentially be accessed through more than one path. For Linux userspace binaries on any architecture, use the EFI values for that architecture by delegating to the EFI header file. This avoids the need to explicitly select values for Linux userspace binaries for each architecture. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 1 - src/arch/arm32/include/efi/ipxe/dhcp_arch.h | 40 ------------------------ src/arch/arm32/include/ipxe/efi/dhcparch.h | 20 ++++++++++++ src/arch/arm64/include/efi/ipxe/dhcp_arch.h | 40 ------------------------ src/arch/arm64/include/ipxe/efi/dhcparch.h | 20 ++++++++++++ src/arch/i386/include/efi/ipxe/dhcp_arch.h | 40 ------------------------ src/arch/i386/include/ipxe/efi/dhcparch.h | 20 ++++++++++++ src/arch/i386/include/pcbios/ipxe/dhcp_arch.h | 40 ------------------------ src/arch/x86/Makefile.linux | 4 --- src/arch/x86/include/ipxe/pcbios/dhcparch.h | 20 ++++++++++++ src/arch/x86/include/linux/ipxe/dhcp_arch.h | 41 ------------------------- src/arch/x86_64/include/efi/ipxe/dhcp_arch.h | 40 ------------------------ src/arch/x86_64/include/ipxe/efi/dhcparch.h | 20 ++++++++++++ src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h | 40 ------------------------ src/include/ipxe/dhcparch.h | 16 ++++++++++ src/include/ipxe/linux/dhcparch.h | 20 ++++++++++++ src/net/udp/dhcp.c | 2 +- src/net/udp/dhcpv6.c | 2 +- 18 files changed, 138 insertions(+), 288 deletions(-) delete mode 100644 src/arch/arm32/include/efi/ipxe/dhcp_arch.h create mode 100644 src/arch/arm32/include/ipxe/efi/dhcparch.h delete mode 100644 src/arch/arm64/include/efi/ipxe/dhcp_arch.h create mode 100644 src/arch/arm64/include/ipxe/efi/dhcparch.h delete mode 100644 src/arch/i386/include/efi/ipxe/dhcp_arch.h create mode 100644 src/arch/i386/include/ipxe/efi/dhcparch.h delete mode 100644 src/arch/i386/include/pcbios/ipxe/dhcp_arch.h create mode 100644 src/arch/x86/include/ipxe/pcbios/dhcparch.h delete mode 100644 src/arch/x86/include/linux/ipxe/dhcp_arch.h delete mode 100644 src/arch/x86_64/include/efi/ipxe/dhcp_arch.h create mode 100644 src/arch/x86_64/include/ipxe/efi/dhcparch.h delete mode 100644 src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h create mode 100644 src/include/ipxe/dhcparch.h create mode 100644 src/include/ipxe/linux/dhcparch.h (limited to 'src/net') diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index f3258025d..b32003ea5 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -369,7 +369,6 @@ endif # Include architecture-specific include path ifdef ARCH INCDIRS += arch/$(ARCH)/include -INCDIRS += arch/$(ARCH)/include/$(PLATFORM) endif ############################################################################### diff --git a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h b/src/arch/arm32/include/efi/ipxe/dhcp_arch.h deleted file mode 100644 index 29a235944..000000000 --- a/src/arch/arm32/include/efi/ipxe/dhcp_arch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _DHCP_ARCH_H -#define _DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM32 - -#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ - -#endif diff --git a/src/arch/arm32/include/ipxe/efi/dhcparch.h b/src/arch/arm32/include/ipxe/efi/dhcparch.h new file mode 100644 index 000000000..0b669992c --- /dev/null +++ b/src/arch/arm32/include/ipxe/efi/dhcparch.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_EFI_DHCPARCH_H +#define _IPXE_EFI_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** DHCP client architecture */ +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM32 + +/** DHCP client network device interface */ +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ + +#endif /* _IPXE_EFI_DHCPARCH_H */ diff --git a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h b/src/arch/arm64/include/efi/ipxe/dhcp_arch.h deleted file mode 100644 index bb26aae4d..000000000 --- a/src/arch/arm64/include/efi/ipxe/dhcp_arch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown . - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _DHCP_ARCH_H -#define _DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM64 - -#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ - -#endif diff --git a/src/arch/arm64/include/ipxe/efi/dhcparch.h b/src/arch/arm64/include/ipxe/efi/dhcparch.h new file mode 100644 index 000000000..21d5a881b --- /dev/null +++ b/src/arch/arm64/include/ipxe/efi/dhcparch.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_EFI_DHCPARCH_H +#define _IPXE_EFI_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** DHCP client architecture */ +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_ARM64 + +/** DHCP client network device interface */ +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ + +#endif /* _IPXE_EFI_DHCPARCH_H */ diff --git a/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/src/arch/i386/include/efi/ipxe/dhcp_arch.h deleted file mode 100644 index cf3dbe499..000000000 --- a/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 VMware, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _DHCP_ARCH_H -#define _DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_IA32 - -#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ - -#endif diff --git a/src/arch/i386/include/ipxe/efi/dhcparch.h b/src/arch/i386/include/ipxe/efi/dhcparch.h new file mode 100644 index 000000000..4746d4b18 --- /dev/null +++ b/src/arch/i386/include/ipxe/efi/dhcparch.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_EFI_DHCPARCH_H +#define _IPXE_EFI_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** DHCP client architecture */ +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_IA32 + +/** DHCP client network device interface */ +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ + +#endif /* _IPXE_EFI_DHCPARCH_H */ diff --git a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h deleted file mode 100644 index e22f50b37..000000000 --- a/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 VMware, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _DHCP_ARCH_H -#define _DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 - -#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ - -#endif diff --git a/src/arch/x86/Makefile.linux b/src/arch/x86/Makefile.linux index b60065567..42590441e 100644 --- a/src/arch/x86/Makefile.linux +++ b/src/arch/x86/Makefile.linux @@ -1,9 +1,5 @@ # -*- makefile -*- : Force emacs to use Makefile mode -# Include x86 Linux headers -# -INCDIRS += arch/x86/include/linux - # Include generic Linux Makefile # MAKEDEPS += Makefile.linux diff --git a/src/arch/x86/include/ipxe/pcbios/dhcparch.h b/src/arch/x86/include/ipxe/pcbios/dhcparch.h new file mode 100644 index 000000000..13138ea96 --- /dev/null +++ b/src/arch/x86/include/ipxe/pcbios/dhcparch.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_PCBIOS_DHCPARCH_H +#define _IPXE_PCBIOS_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** DHCP client architecture */ +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 + +/** DHCP client network device interface */ +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ + +#endif /* _IPXE_PCBIOS_DHCPARCH_H */ diff --git a/src/arch/x86/include/linux/ipxe/dhcp_arch.h b/src/arch/x86/include/linux/ipxe/dhcp_arch.h deleted file mode 100644 index d60905f22..000000000 --- a/src/arch/x86/include/linux/ipxe/dhcp_arch.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2010 Piotr JaroszyƄski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _LINUX_DHCP_ARCH_H -#define _LINUX_DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE(GPL2_OR_LATER_OR_UBDL); - -#include - -// Emulate one of the supported arch-platforms -#include -//#include -//#include - -#endif diff --git a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h deleted file mode 100644 index fb85b4405..000000000 --- a/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 VMware, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _DHCP_ARCH_H -#define _DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86_64 - -#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ - -#endif diff --git a/src/arch/x86_64/include/ipxe/efi/dhcparch.h b/src/arch/x86_64/include/ipxe/efi/dhcparch.h new file mode 100644 index 000000000..ccf0f46a0 --- /dev/null +++ b/src/arch/x86_64/include/ipxe/efi/dhcparch.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_EFI_DHCPARCH_H +#define _IPXE_EFI_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** DHCP client architecture */ +#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86_64 + +/** DHCP client network device interface */ +#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */ + +#endif /* _IPXE_EFI_DHCPARCH_H */ diff --git a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h b/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h deleted file mode 100644 index e22f50b37..000000000 --- a/src/arch/x86_64/include/pcbios/ipxe/dhcp_arch.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 VMware, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -#ifndef _DHCP_ARCH_H -#define _DHCP_ARCH_H - -/** @file - * - * Architecture-specific DHCP options - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include - -#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_X86 - -#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 2, 1 /* v2.1 */ - -#endif diff --git a/src/include/ipxe/dhcparch.h b/src/include/ipxe/dhcparch.h new file mode 100644 index 000000000..89ecfb31e --- /dev/null +++ b/src/include/ipxe/dhcparch.h @@ -0,0 +1,16 @@ +#ifndef _IPXE_DHCPARCH_H +#define _IPXE_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* Include platform-specific client architecture definitions */ +#define PLATFORM_DHCPARCH(_platform) +#include PLATFORM_DHCPARCH(PLATFORM) + +#endif /* _IPXE_DHCPARCH_H */ diff --git a/src/include/ipxe/linux/dhcparch.h b/src/include/ipxe/linux/dhcparch.h new file mode 100644 index 000000000..464aa5168 --- /dev/null +++ b/src/include/ipxe/linux/dhcparch.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_LINUX_DHCPARCH_H +#define _IPXE_LINUX_DHCPARCH_H + +/** @file + * + * DHCP client architecture definitions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/* + * There are no specification-defined values for DHCP architecture for + * PXE clients running as Linux userspace applications. Pretend to be + * the equivalent EFI client. + * + */ +#include + +#endif /* _IPXE_LINUX_DHCPARCH_H */ diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index a335a778a..b7b84e7db 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 #include #include -#include +#include #include #include diff --git a/src/net/udp/dhcpv6.c b/src/net/udp/dhcpv6.c index 28c6f7be4..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 #include #include -#include +#include #include /** @file -- cgit v1.2.3-55-g7522 From 8450fa4a7b9a8236a43b74639fc80bada994ce07 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 3 Feb 2023 19:36:57 +0000 Subject: [dhcp] Ignore DHCPNAK unless originating from the selected DHCP server RFC 2131 leaves undefined the behaviour of the client in response to a DHCPNAK that comes from a server other than the selected DHCP server. A substantial amount of online documentation suggests using multiple independent DHCP servers with non-overlapping ranges in the same subnet in order to provide some minimal redundancy. Experimentation shows that in this setup, at least ISC dhcpd will send a DHCPNAK in response to the client's DHCPREQUEST for an address that is not within the range defined on that server. (Since the requested address does lie within the subnet defined on that server, this will happen regardless of the "authoritative" parameter.) The client will therefore receive a DHCPACK from the selected DHCP server along with one or more DHCPNAKs from each of the non-selected DHCP servers. Filter out responses from non-selected DHCP servers before checking for a DHCPNAK, so that these arguably spurious DHCPNAKs will not cause iPXE to return to the discovery state. Continue to check for DHCPNAK before filtering out responses for non-selected lease addresses, since experimentation shows that the DHCPNAK will usually have an empty yiaddr field. Reported-by: Anders Blomdell Signed-off-by: Michael Brown --- src/net/udp/dhcp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/net') diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index b7b84e7db..a1a481e19 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -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; -- cgit v1.2.3-55-g7522 From dc16de3204d1956d4fd17808e6d34ac926bbe932 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 5 Feb 2023 13:07:30 +0000 Subject: [lldp] Add support for the Link Layer Discovery Protocol Add support for recording LLDP packets and exposing TLV values via the settings mechanism. LLDP settings are encoded as ${netX.lldp/....} where is the TLV type is the starting offset within the TLV value is the length (or zero to read the from to the end) , if it has a non-zero value, is the subtype byte string of length to match at the start of the TLV value, up to a maximum matched length of 4 bytes is the index of the entry matching and to be accessed, with zero indicating the first matching entry The is designed to accommodate both matching of the OUI within an organization-specific TLV (e.g. 0x0080c2 for IEEE 802.1 TLVs) and of a subtype byte as found within many TLVs. This encoding allows most LLDP values to be extracted easily. For example System name: ${netX.lldp/5.0.0.0:string} System description: ${netX.lldp/6.0.0.0:string} Port description: ${netX.lldp/4.0.0.0:string} Port interface name: ${netX.lldp/5.2.0.1.0:string} Chassis MAC address: ${netX.lldp/4.1.0.1.0:hex} Management IPv4 address: ${netX.lldp/5.1.8.0.2.4:ipv4} Port VLAN ID: ${netX.lldp/0x0080c2.1.127.0.4.2:int16} Port VLAN name: ${netX.lldp/0x0080c2.3.127.0.7.0:string} Maximum frame size: ${netX.lldp/0x00120f.4.127.0.4.2:uint16} Originally-implemented-by: Marin Hannache Signed-off-by: Michael Brown --- src/config/config_ethernet.c | 3 + src/config/general.h | 1 + src/include/ipxe/errfile.h | 1 + src/include/ipxe/if_ether.h | 1 + src/include/ipxe/lldp.h | 97 ++++++++++++ src/net/lldp.c | 340 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 443 insertions(+) create mode 100644 src/include/ipxe/lldp.h create mode 100644 src/net/lldp.c (limited to 'src/net') diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index 8a663c923..c1b35bfe6 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -49,3 +49,6 @@ REQUIRE_OBJECT ( eth_slow ); #ifdef NET_PROTO_EAPOL REQUIRE_OBJECT ( eapol ); #endif +#ifdef NET_PROTO_LLDP +REQUIRE_OBJECT ( lldp ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 2d15f500a..e9ceaff57 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -40,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define NET_PROTO_STP /* Spanning Tree protocol */ #define NET_PROTO_LACP /* Link Aggregation control protocol */ #define NET_PROTO_EAPOL /* EAP over LAN protocol */ +#undef NET_PROTO_LLDP /* Link Layer Discovery protocol */ /* * PXE support diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 7c3b0c43b..d7b6ea1bd 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -295,6 +295,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 ) #define ERRFILE_httpntlm ( ERRFILE_NET | 0x004a0000 ) #define ERRFILE_eap ( ERRFILE_NET | 0x004b0000 ) +#define ERRFILE_lldp ( ERRFILE_NET | 0x004c0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/if_ether.h b/src/include/ipxe/if_ether.h index 58d91b976..c1168b10e 100644 --- a/src/include/ipxe/if_ether.h +++ b/src/include/ipxe/if_ether.h @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ETH_P_SLOW 0x8809 /* Ethernet slow protocols */ #define ETH_P_EAPOL 0x888E /* 802.1X EAP over LANs */ #define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ +#define ETH_P_LLDP 0x88CC /* Link Layer Discovery Protocol */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ diff --git a/src/include/ipxe/lldp.h b/src/include/ipxe/lldp.h new file mode 100644 index 000000000..9951d3b8f --- /dev/null +++ b/src/include/ipxe/lldp.h @@ -0,0 +1,97 @@ +#ifndef _IPXE_LLDP_H +#define _IPXE_LLDP_H + +/** @file + * + * Link Layer Discovery Protocol + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** An LLDP TLV header */ +struct lldp_tlv { + /** Type and length */ + uint16_t type_len; + /** Data */ + uint8_t data[0]; +} __attribute__ (( packed )); + +/** + * Extract LLDP TLV type + * + * @v type_len Type and length + * @ret type Type + */ +#define LLDP_TLV_TYPE( type_len ) ( (type_len) >> 9 ) + +/** + * Extract LLDP TLV length + * + * @v type_len Type and length + * @ret len Length + */ +#define LLDP_TLV_LEN( type_len ) ( (type_len) & 0x01ff ) + +/** End of LLDP data unit */ +#define LLDP_TYPE_END 0x00 + +/** LLDP settings block name */ +#define LLDP_SETTINGS_NAME "lldp" + +/** + * Construct LLDP setting tag + * + * LLDP settings are encoded as + * + * ${netX.lldp/....} + * + * where + * + * is the TLV type + * + * is the starting offset within the TLV value + * + * is the length (or zero to read the from to the end) + * + * , if it has a non-zero value, is the subtype byte string + * of length to match at the start of the TLV value, up to + * a maximum matched length of 4 bytes + * + * is the index of the entry matching and to + * be accessed, with zero indicating the first matching entry + * + * The is designed to accommodate both matching of the OUI + * within an organization-specific TLV (e.g. 0x0080c2 for IEEE 802.1 + * TLVs) and of a subtype byte as found within many TLVs. + * + * This encoding allows most LLDP values to be extracted easily. For + * example + * + * System name: ${netX.lldp/5.0.0.0:string} + * + * System description: ${netX.lldp/6.0.0.0:string} + * + * Port description: ${netX.lldp/4.0.0.0:string} + * + * Port interface name: ${netX.lldp/5.2.0.1.0:string} + * + * Chassis MAC address: ${netX.lldp/4.1.0.1.0:hex} + * + * Management IPv4 address: ${netX.lldp/5.1.8.0.2.4:ipv4} + * + * Port VLAN ID: ${netX.lldp/0x0080c2.1.127.0.4.2:int16} + * + * Port VLAN name: ${netX.lldp/0x0080c2.3.127.0.7.0:string} + * + * Maximum frame size: ${netX.lldp/0x00120f.4.127.0.4.2:uint16} + * + */ +#define LLDP_TAG( prefix, type, index, offset, length ) \ + ( ( ( ( uint64_t ) (prefix) ) << 32 ) | \ + ( (type) << 24 ) | ( (index) << 16 ) | \ + ( (offset) << 8 ) | ( (length) << 0 ) ) + +#endif /* _IPXE_LLDP_H */ 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/** 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, +}; -- cgit v1.2.3-55-g7522 From 2733c4763a50b9eb0c206e7430d4d0638451e5e9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 16 Feb 2023 12:54:47 +0000 Subject: [iscsi] Limit maximum transfer size to MaxBurstLength We currently specify only the iSCSI default value for MaxBurstLength and ignore any negotiated value, since our internal block device API allows only for receiving directly into caller-allocated buffers and so we have no intrinsic limit on burst length. A conscientious target may however refuse to attempt a transfer that we request for a number of blocks that would exceed the negotiated maximum burst length. Fix by recording the negotiated maximum burst length and using it to limit the maximum number of blocks per transfer as reported by the SCSI layer. Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 4 +++ src/include/ipxe/iscsi.h | 12 +++++++++ src/net/tcp/iscsi.c | 65 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 4 deletions(-) (limited to 'src/net') diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index f765c9762..ff415f5c6 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -609,6 +609,7 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd, */ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, int rc ) { + struct scsi_device *scsidev = scsicmd->scsidev; struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd ); struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16; struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10; @@ -645,6 +646,9 @@ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, } capacity.max_count = -1U; + /* Allow transport layer to update capacity */ + block_capacity ( &scsidev->scsi, &capacity ); + /* Return capacity to caller */ block_capacity ( &scsicmd->block, &capacity ); diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h index 966cf52b6..a25eec257 100644 --- a/src/include/ipxe/iscsi.h +++ b/src/include/ipxe/iscsi.h @@ -22,6 +22,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Default iSCSI port */ #define ISCSI_PORT 3260 +/** Default iSCSI first burst length */ +#define ISCSI_FIRST_BURST_LEN 65536 + +/** Default iSCSI maximum burst length */ +#define ISCSI_MAX_BURST_LEN 262144 + +/** Default iSCSI maximum receive data segment length */ +#define ISCSI_MAX_RECV_DATA_SEG_LEN 8192 + /** * iSCSI segment lengths * @@ -577,6 +586,9 @@ struct iscsi_session { /** CHAP response (used for both initiator and target auth) */ struct chap_response chap; + /** Maximum burst length */ + size_t max_burst_len; + /** Initiator session ID (IANA format) qualifier * * This is part of the ISID. It is generated randomly 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 #include #include +#include #include #include @@ -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; @@ -908,6 +920,31 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, return 0; } +/** + * 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 * @@ -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 }, @@ -1847,6 +1885,24 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi, return iscsi->itt; } +/** + * 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 * @@ -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 ), -- cgit v1.2.3-55-g7522 From 60531ff6e25d363f36f843cae29a95031aac2147 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 1 Mar 2023 11:06:46 +0000 Subject: [http] Use POST method only if the form parameter list is non-empty An attempt to use an existent but empty form parameter list will currently result in an invalid POST request since the Content-Length header will be missing. Fix by using GET instead of POST if the form parameter list is empty. This is a non-breaking change (since the current behaviour produces an invalid request), and simplifies the imminent generalisation of the parameter list concept to handle both header and form parameters. Signed-off-by: Michael Brown --- src/net/tcp/httpcore.c | 76 +++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 44 deletions(-) (limited to 'src/net') diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index fd94b5f08..c970d54bf 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -1904,53 +1904,58 @@ 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 parameter list, if any */ + len = ( params ? http_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 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 parameter list */ + data = zalloc ( len + 1 /* NUL */ ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct temporary parameter list */ + check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + + } 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 +1964,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 ); -- cgit v1.2.3-55-g7522 From 96bb6ba441653a30729ade38dc6c23bc9e2d2339 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 28 Feb 2023 17:46:13 +0000 Subject: [params] Allow for arbitrary HTTP request headers to be specified Extend the request parameter mechanism to allow for arbitrary HTTP headers to be specified via e.g.: params param --header Referer http://www.example.com imgfetch http://192.168.0.1/script.ipxe##params Signed-off-by: Michael Brown --- src/core/params.c | 11 ++++++++--- src/hci/commands/param_cmd.c | 10 +++++++++- src/include/ipxe/params.h | 11 ++++++++++- src/net/tcp/httpcore.c | 43 ++++++++++++++++++++++++++++++++++--------- src/tests/uri_test.c | 13 ++++++++++++- 5 files changed, 73 insertions(+), 15 deletions(-) (limited to 'src/net') diff --git a/src/core/params.c b/src/core/params.c index 23206bef6..58c829f62 100644 --- a/src/core/params.c +++ b/src/core/params.c @@ -123,10 +123,12 @@ struct parameters * create_parameters ( const char *name ) { * @v params Parameter list * @v key Parameter key * @v value Parameter value + * @v flags Parameter flags * @ret param Parameter, or NULL on failure */ struct parameter * add_parameter ( struct parameters *params, - const char *key, const char *value ) { + const char *key, const char *value, + unsigned int flags ) { struct parameter *param; size_t key_len; size_t value_len; @@ -147,11 +149,14 @@ struct parameter * add_parameter ( struct parameters *params, param->key = key_copy; strcpy ( value_copy, value ); param->value = value_copy; + param->flags = flags; /* Add to list of parameters */ list_add_tail ( ¶m->list, ¶ms->entries ); - DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n", - params->name, param->key, param->value ); + DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"%s%s\n", + params->name, param->key, param->value, + ( ( param->flags & PARAMETER_FORM ) ? " (form)" : "" ), + ( ( param->flags & PARAMETER_HEADER ) ? " (header)" : "" ) ); return param; } diff --git a/src/hci/commands/param_cmd.c b/src/hci/commands/param_cmd.c index 9e3260de1..dad99f840 100644 --- a/src/hci/commands/param_cmd.c +++ b/src/hci/commands/param_cmd.c @@ -90,12 +90,16 @@ static int params_exec ( int argc, char **argv ) { struct param_options { /** Parameter list name */ char *params; + /** Parameter is a header */ + int header; }; /** "param" option list */ static struct option_descriptor param_opts[] = { OPTION_DESC ( "params", 'p', required_argument, struct param_options, params, parse_string ), + OPTION_DESC ( "header", 'H', no_argument, + struct param_options, header, parse_flag ), }; /** "param" command descriptor */ @@ -114,6 +118,7 @@ static int param_exec ( int argc, char **argv ) { struct param_options opts; char *key; char *value; + unsigned int flags; struct parameters *params; struct parameter *param; int rc; @@ -132,12 +137,15 @@ static int param_exec ( int argc, char **argv ) { goto err_parse_value; } + /* Construct flags */ + flags = ( opts.header ? PARAMETER_HEADER : PARAMETER_FORM ); + /* Identify parameter list */ if ( ( rc = parse_parameters ( opts.params, ¶ms ) ) != 0 ) goto err_parse_parameters; /* Add parameter */ - param = add_parameter ( params, key, value ); + param = add_parameter ( params, key, value, flags ); if ( ! param ) { rc = -ENOMEM; goto err_add_parameter; diff --git a/src/include/ipxe/params.h b/src/include/ipxe/params.h index 955f57acc..61e46e029 100644 --- a/src/include/ipxe/params.h +++ b/src/include/ipxe/params.h @@ -32,8 +32,16 @@ struct parameter { const char *key; /** Value */ const char *value; + /** Flags */ + unsigned int flags; }; +/** Request parameter is a form parameter */ +#define PARAMETER_FORM 0x0001 + +/** Request parameter is a header parameter */ +#define PARAMETER_HEADER 0x0002 + /** * Increment request parameter list reference count * @@ -78,6 +86,7 @@ claim_parameters ( struct parameters *params ) { extern struct parameters * find_parameters ( const char *name ); extern struct parameters * create_parameters ( const char *name ); extern struct parameter * add_parameter ( struct parameters *params, - const char *key, const char *value ); + const char *key, const char *value, + unsigned int flags ); #endif /* _IPXE_PARAMS_H */ diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index c970d54bf..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 ) @@ -1920,25 +1944,26 @@ int http_open_uri ( struct interface *xfer, struct uri *uri ) { size_t check_len; int rc; - /* Calculate length of parameter list, if any */ - len = ( params ? http_params ( params, NULL, 0 ) : 0 ); + /* Calculate length of form parameter list, if any */ + len = ( params ? http_form_params ( params, NULL, 0 ) : 0 ); - /* Use POST if and only if there are parameters */ + /* Use POST if and only if there are form parameters */ if ( len ) { /* Use POST */ method = &http_post; type = "application/x-www-form-urlencoded"; - /* Allocate temporary parameter list */ + /* Allocate temporary form parameter list */ data = zalloc ( len + 1 /* NUL */ ); if ( ! data ) { rc = -ENOMEM; goto err_alloc; } - /* Construct temporary parameter list */ - check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + /* Construct temporary form parameter list */ + check_len = http_form_params ( params, data, + ( len + 1 /* NUL */ ) ); assert ( check_len == len ); } else { diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 5e7060323..9d2f6dba5 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -98,6 +98,8 @@ struct uri_params_test_list { const char *key; /** Value */ const char *value; + /** Flags */ + unsigned int flags; }; /** A request parameter URI test */ @@ -428,6 +430,7 @@ static void uri_params_list_okx ( struct uri_params_test *test, file, line ); okx ( strcmp ( param->value, list->value ) == 0, file, line ); + okx ( param->flags == list->flags, file, line ); list++; } okx ( list->key == NULL, file, line ); @@ -456,7 +459,8 @@ static void uri_params_okx ( struct uri_params_test *test, const char *file, okx ( params != NULL, file, line ); if ( params ) { for ( list = test->list ; list->key ; list++ ) { - param = add_parameter ( params, list->key, list->value); + param = add_parameter ( params, list->key, list->value, + list->flags ); okx ( param != NULL, file, line ); } } @@ -884,18 +888,22 @@ static struct uri_params_test_list uri_params_list[] = { { "vendor", "10ec", + PARAMETER_FORM, }, { "device", "8139", + PARAMETER_FORM, }, { "uuid", "f59fac00-758f-498f-9fe5-87d790045d94", + PARAMETER_HEADER, }, { NULL, NULL, + 0, } }; @@ -917,14 +925,17 @@ static struct uri_params_test_list uri_named_params_list[] = { { "mac", "00:1e:65:80:d3:b6", + PARAMETER_FORM, }, { "serial", "LXTQ20Z1139322762F2000", + PARAMETER_FORM, }, { NULL, NULL, + 0, } }; -- cgit v1.2.3-55-g7522 From 8f1c1201199a924eeba31494be5aa6bf13eb3fa0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 8 Mar 2023 00:43:33 +0000 Subject: [dhcp] Unregister ProxyDHCP and PXEBS settings on a successful DHCPACK When a DHCP transaction does not result in the registration of a new "proxydhcp" or "pxebs" settings block, any existing settings blocks are currently left unaltered. This can cause surprising behaviour. For example: when chainloading iPXE, the "proxydhcp" and "pxebs" settings blocks may be prepopulated using cached values from the previous PXE bootloader. If iPXE performs a subsequent DHCP request, then the DHCP or ProxyDHCP servers may choose to respond differently to iPXE. The response may choose to omit the ProxyDHCP or PXEBS stages, in which case no new "proxydhcp" or "pxebs" settings blocks may be registered. This will result in iPXE using a combination of both old and new DHCP responses. Fix by assuming that a successful DHCPACK effectively acquires ownership of the "proxydhcp" and "pxebs" settings blocks, and that any existing settings blocks should therefore be unregistered. Reported-by: Henry Tung Signed-off-by: Michael Brown --- src/net/udp/dhcp.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/net') diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index a1a481e19..bd2c4a197 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -601,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 */ ) { -- cgit v1.2.3-55-g7522 From 2c6a15d2a350425c0f1f88e0e69cb5e9e2a651e5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 30 Mar 2023 16:57:12 +0100 Subject: [tls] Clean up change cipher spec record handling Define and use data structures and constants for the (single-byte) change cipher spec records. Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 3 +++ src/net/tls.c | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 6fcb69bef..99c7be019 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -52,6 +52,9 @@ struct tls_header { /** Change cipher content type */ #define TLS_TYPE_CHANGE_CIPHER 20 +/** Change cipher spec magic byte */ +#define TLS_CHANGE_CIPHER_SPEC 1 + /** Alert content type */ #define TLS_TYPE_ALERT 21 diff --git a/src/net/tls.c b/src/net/tls.c index 899629626..e0231b1c4 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1682,9 +1682,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 ) ); } /** @@ -1737,14 +1742,20 @@ static int tls_send_finished ( struct tls_connection *tls ) { */ static int tls_new_change_cipher ( struct tls_connection *tls, const void *data, size_t len ) { + const struct { + uint8_t spec; + } __attribute__ (( packed )) *change_cipher = data; 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; } + /* 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", -- cgit v1.2.3-55-g7522 From aa368ba529e13f07658e16507eb8686ef1c07423 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 30 Mar 2023 16:28:40 +0100 Subject: [tls] Pass I/O buffer to received record handlers Prepare for the possibility that a record handler may choose not to consume the entire record by passing the I/O buffer and requiring the handler to mark consumed data using iob_pull(). Signed-off-by: Michael Brown --- src/net/tls.c | 156 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 98 insertions(+), 58 deletions(-) (limited to 'src/net') diff --git a/src/net/tls.c b/src/net/tls.c index e0231b1c4..272ced24f 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -1736,15 +1736,15 @@ 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 = data; + } __attribute__ (( packed )) *change_cipher = iobuf->data; + size_t len = iob_len ( iobuf ); int rc; /* Sanity check */ @@ -1754,6 +1754,7 @@ static int tls_new_change_cipher ( struct tls_connection *tls, 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, @@ -1771,25 +1772,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", @@ -2403,21 +2406,20 @@ 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; @@ -2426,14 +2428,14 @@ static int tls_new_handshake ( struct tls_connection *tls, if ( sizeof ( *handshake ) > remaining ) { DBGC ( tls, "TLS %p received underlength Handshake\n", tls ); - DBGC_HD ( tls, data, remaining ); + DBGC_HD ( tls, handshake, remaining ); return -EINVAL_HANDSHAKE; } 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 ); + DBGC_HD ( tls, handshake, remaining ); return -EINVAL_HANDSHAKE; } payload = &handshake->payload; @@ -2481,15 +2483,60 @@ 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 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 rx_data List of received data buffers + * @ret rc Return status code + */ +static int tls_new_data ( struct tls_connection *tls, + struct list_head *rx_data ) { + struct io_buffer *iobuf; + int 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; @@ -2505,39 +2552,14 @@ static int tls_new_handshake ( struct tls_connection *tls, */ 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 *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; - } - } - 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; - } + /* Deliver data records as-is to the plainstream interface */ + if ( type == TLS_TYPE_DATA ) + return tls_new_data ( tls, rx_data ); /* Determine handler */ switch ( type ) { @@ -2551,17 +2573,35 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type, handler = tls_new_handshake; break; default: - /* RFC4346 says that we should just ignore unknown - * record types. - */ - handler = NULL; - DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type ); + 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 ); + /* 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 ); + rc = -ENOMEM_RX_CONCAT; + goto err_concatenate; + } + + /* Handle record */ + if ( ( rc = handler ( tls, iobuf ) ) != 0 ) + goto err_handle; + + /* Sanity check */ + assert ( iob_len ( iobuf ) == 0 ); + + /* Free I/O buffer */ + free_iob ( iobuf ); + + return 0; + + err_handle: free_iob ( iobuf ); + err_concatenate: return rc; } -- cgit v1.2.3-55-g7522 From 1d1cf74a5e58811822bee4b3da3cff7282fcdfca Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 30 Mar 2023 16:28:40 +0100 Subject: [tls] Handle fragmented handshake records Originally-implemented-by: Christopher Schenk Signed-off-by: Michael Brown --- src/include/ipxe/tls.h | 2 ++ src/net/tls.c | 42 ++++++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 18 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 99c7be019..30bb1c483 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -398,6 +398,8 @@ struct tls_connection { struct io_buffer rx_header_iobuf; /** List of received data buffers */ struct list_head rx_data; + /** Received handshake fragment */ + struct io_buffer *rx_handshake; }; /** RX I/O buffer size diff --git a/src/net/tls.c b/src/net/tls.c index 272ced24f..000a8a785 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -388,6 +388,7 @@ static void free_tls ( struct refcnt *refcnt ) { 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 ); @@ -2426,17 +2427,13 @@ static int tls_new_handshake ( struct tls_connection *tls, /* Parse header */ if ( sizeof ( *handshake ) > remaining ) { - DBGC ( tls, "TLS %p received underlength Handshake\n", - tls ); - DBGC_HD ( tls, handshake, 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, handshake, remaining ); - return -EINVAL_HANDSHAKE; + /* Leave remaining fragment unconsumed */ + break; } payload = &handshake->payload; record_len = ( sizeof ( *handshake ) + payload_len ); @@ -2554,14 +2551,16 @@ 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 *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; @@ -2571,6 +2570,7 @@ 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: DBGC ( tls, "TLS %p unknown record type %d\n", tls, type ); @@ -2579,8 +2579,10 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type, } /* Merge into a single I/O buffer */ - iobuf = iob_concatenate ( rx_data ); - if ( ! iobuf ) { + 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; @@ -2588,19 +2590,23 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type, } /* Handle record */ - if ( ( rc = handler ( tls, iobuf ) ) != 0 ) + if ( ( rc = handler ( tls, *iobuf ) ) != 0 ) goto err_handle; - /* Sanity check */ - assert ( iob_len ( iobuf ) == 0 ); + /* Discard I/O buffer if empty */ + if ( ! iob_len ( *iobuf ) ) { + free_iob ( *iobuf ); + *iobuf = NULL; + } - /* Free I/O buffer */ - free_iob ( iobuf ); + /* Sanity check */ + assert ( tmp == NULL ); return 0; err_handle: - free_iob ( iobuf ); + free_iob ( *iobuf ); + *iobuf = NULL; err_concatenate: return rc; } -- cgit v1.2.3-55-g7522