david/ipxe
david
/
ipxe
Archived
1
0
Fork 0
This repository has been archived on 2020-12-06. You can view files and clone it, but cannot push or open issues or pull requests.
ipxe/src/tests/tcpip_test.c

261 lines
7.2 KiB
C
Raw Permalink Normal View History

/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** @file
*
* TCP/IP self-tests
*
*/
/* Forcibly enable assertions */
#undef NDEBUG
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ipxe/test.h>
#include <ipxe/profile.h>
#include <ipxe/tcpip.h>
/** Number of sample iterations for profiling */
#define PROFILE_COUNT 16
/** A TCP/IP fixed-data test */
struct tcpip_test {
/** Data */
const void *data;
/** Length of data */
size_t len;
};
/** A TCP/IP pseudorandom-data test */
struct tcpip_random_test {
/** Seed */
unsigned int seed;
/** Length of data */
size_t len;
/** Alignment offset */
size_t offset;
};
/** Define inline data */
#define DATA(...) { __VA_ARGS__ }
/** Define a TCP/IP fixed-data test */
#define TCPIP_TEST( name, DATA ) \
static const uint8_t __attribute__ (( aligned ( 16 ) )) \
name ## _data[] = DATA; \
static struct tcpip_test name = { \
.data = name ## _data, \
.len = sizeof ( name ## _data ), \
}
/** Define a TCP/IP pseudorandom-data test */
#define TCPIP_RANDOM_TEST( name, SEED, LEN, OFFSET ) \
static struct tcpip_random_test name = { \
.seed = SEED, \
.len = LEN, \
.offset = OFFSET, \
}
/** Buffer for pseudorandom-data tests */
static uint8_t __attribute__ (( aligned ( 16 ) ))
tcpip_data[ 4096 + 7 /* offset */ ];
/** Empty data */
TCPIP_TEST ( empty, DATA() );
/** Single byte */
TCPIP_TEST ( one_byte, DATA ( 0xeb ) );
/** Double byte */
TCPIP_TEST ( two_bytes, DATA ( 0xba, 0xbe ) );
[tcpip] Avoid generating positive zero for transmitted UDP checksums TCP/IP checksum fields are one's complement values and therefore have two possible representations of zero: positive zero (0x0000) and negative zero (0xffff). In RFC768, UDP over IPv4 exploits this redundancy to repurpose the positive representation of zero (0x0000) to mean "no checksum calculated"; checksums are optional for UDP over IPv4. In RFC2460, checksums are made mandatory for UDP over IPv4. The wording of the RFC is such that the UDP header is mandated to use only the negative representation of zero (0xffff), rather than simply requiring the checksum to be correct but allowing for either representation of zero to be used. In RFC1071, an example algorithm is given for calculating the TCP/IP checksum. This algorithm happens to produce only the positive representation of zero (0x0000); this is an artifact of the way that unsigned arithmetic is used to calculate a signed one's complement sum (and its final negation). A common misconception has developed (exemplified in RFC1624) that this artifact is part of the specification. Many people have assumed that the checksum field should never contain the negative representation of zero (0xffff). A sensible receiver will calculate the checksum over the whole packet and verify that the result is zero (in whichever representation of zero happens to be generated by the receiver's algorithm). Such a receiver will not care which representation of zero happens to be used in the checksum field. However, there are receivers in existence which will verify the received checksum the hard way: by calculating the checksum over the remainder of the packet and comparing the result against the checksum field. If the representation of zero used by the receiver's algorithm does not match the representation of zero used by the transmitter (and so placed in the checksum field), and if the receiver does not explicitly allow for both representations to compare as equal, then the receiver may reject packets with a valid checksum. For UDP, the combined RFCs effectively mandate that we should generate only the negative representation of zero in the checksum field. For IP, TCP and ICMP, the RFCs do not mandate which representation of zero should be used, but the misconceptions which have grown up around RFC1071 and RFC1624 suggest that it would be least surprising to generate only the positive representation of zero in the checksum field. Fix by ensuring that all of our checksum algorithms generate only the positive representation of zero, and explicitly inverting this in the case of transmitted UDP packets. Reported-by: Wissam Shoukair <wissams@mellanox.com> Tested-by: Wissam Shoukair <wissams@mellanox.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
2015-09-10 14:19:16 +02:00
/** Positive zero data */
TCPIP_TEST ( positive_zero, DATA ( 0x00, 0x00 ) );
/** Negative zero data */
TCPIP_TEST ( negative_zero, DATA ( 0xff, 0xff ) );
/** Final wrap-around carry (big-endian) */
TCPIP_TEST ( final_carry_big,
DATA ( 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ) );
/** Final wrap-around carry (little-endian) */
TCPIP_TEST ( final_carry_little,
DATA ( 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 ) );
/** Random data (aligned) */
TCPIP_RANDOM_TEST ( random_aligned, 0x12345678UL, 4096, 0 );
/** Random data (unaligned, +1) */
TCPIP_RANDOM_TEST ( random_unaligned_1, 0x12345678UL, 4096, 1 );
/** Random data (unaligned, +2) */
TCPIP_RANDOM_TEST ( random_unaligned_2, 0x12345678UL, 4096, 2 );
/** Random data (aligned, truncated) */
TCPIP_RANDOM_TEST ( random_aligned_truncated, 0x12345678UL, 4095, 0 );
/** Random data (unaligned start and finish) */
TCPIP_RANDOM_TEST ( partial, 0xcafebabe, 121, 5 );
/**
* Calculate TCP/IP checksum
*
* @v data Data to sum
* @v len Length of data
* @ret cksum Checksum
*
* This is a reference implementation taken from RFC1071 (and modified
* to fix compilation without warnings under gcc).
[tcpip] Avoid generating positive zero for transmitted UDP checksums TCP/IP checksum fields are one's complement values and therefore have two possible representations of zero: positive zero (0x0000) and negative zero (0xffff). In RFC768, UDP over IPv4 exploits this redundancy to repurpose the positive representation of zero (0x0000) to mean "no checksum calculated"; checksums are optional for UDP over IPv4. In RFC2460, checksums are made mandatory for UDP over IPv4. The wording of the RFC is such that the UDP header is mandated to use only the negative representation of zero (0xffff), rather than simply requiring the checksum to be correct but allowing for either representation of zero to be used. In RFC1071, an example algorithm is given for calculating the TCP/IP checksum. This algorithm happens to produce only the positive representation of zero (0x0000); this is an artifact of the way that unsigned arithmetic is used to calculate a signed one's complement sum (and its final negation). A common misconception has developed (exemplified in RFC1624) that this artifact is part of the specification. Many people have assumed that the checksum field should never contain the negative representation of zero (0xffff). A sensible receiver will calculate the checksum over the whole packet and verify that the result is zero (in whichever representation of zero happens to be generated by the receiver's algorithm). Such a receiver will not care which representation of zero happens to be used in the checksum field. However, there are receivers in existence which will verify the received checksum the hard way: by calculating the checksum over the remainder of the packet and comparing the result against the checksum field. If the representation of zero used by the receiver's algorithm does not match the representation of zero used by the transmitter (and so placed in the checksum field), and if the receiver does not explicitly allow for both representations to compare as equal, then the receiver may reject packets with a valid checksum. For UDP, the combined RFCs effectively mandate that we should generate only the negative representation of zero in the checksum field. For IP, TCP and ICMP, the RFCs do not mandate which representation of zero should be used, but the misconceptions which have grown up around RFC1071 and RFC1624 suggest that it would be least surprising to generate only the positive representation of zero in the checksum field. Fix by ensuring that all of our checksum algorithms generate only the positive representation of zero, and explicitly inverting this in the case of transmitted UDP packets. Reported-by: Wissam Shoukair <wissams@mellanox.com> Tested-by: Wissam Shoukair <wissams@mellanox.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
2015-09-10 14:19:16 +02:00
*
* The initial value of the one's complement @c sum is changed from
* positive zero (0x0000) to negative zero (0xffff). This ensures
* that the return value will always use the positive representation
* of zero (0x0000). Without this change, the return value would use
* negative zero (0xffff) if the input data is zero length (or all
* zeros) but positive zero (0x0000) for any other data which sums to
* zero.
*/
static uint16_t rfc_tcpip_chksum ( const void *data, size_t len ) {
[tcpip] Avoid generating positive zero for transmitted UDP checksums TCP/IP checksum fields are one's complement values and therefore have two possible representations of zero: positive zero (0x0000) and negative zero (0xffff). In RFC768, UDP over IPv4 exploits this redundancy to repurpose the positive representation of zero (0x0000) to mean "no checksum calculated"; checksums are optional for UDP over IPv4. In RFC2460, checksums are made mandatory for UDP over IPv4. The wording of the RFC is such that the UDP header is mandated to use only the negative representation of zero (0xffff), rather than simply requiring the checksum to be correct but allowing for either representation of zero to be used. In RFC1071, an example algorithm is given for calculating the TCP/IP checksum. This algorithm happens to produce only the positive representation of zero (0x0000); this is an artifact of the way that unsigned arithmetic is used to calculate a signed one's complement sum (and its final negation). A common misconception has developed (exemplified in RFC1624) that this artifact is part of the specification. Many people have assumed that the checksum field should never contain the negative representation of zero (0xffff). A sensible receiver will calculate the checksum over the whole packet and verify that the result is zero (in whichever representation of zero happens to be generated by the receiver's algorithm). Such a receiver will not care which representation of zero happens to be used in the checksum field. However, there are receivers in existence which will verify the received checksum the hard way: by calculating the checksum over the remainder of the packet and comparing the result against the checksum field. If the representation of zero used by the receiver's algorithm does not match the representation of zero used by the transmitter (and so placed in the checksum field), and if the receiver does not explicitly allow for both representations to compare as equal, then the receiver may reject packets with a valid checksum. For UDP, the combined RFCs effectively mandate that we should generate only the negative representation of zero in the checksum field. For IP, TCP and ICMP, the RFCs do not mandate which representation of zero should be used, but the misconceptions which have grown up around RFC1071 and RFC1624 suggest that it would be least surprising to generate only the positive representation of zero in the checksum field. Fix by ensuring that all of our checksum algorithms generate only the positive representation of zero, and explicitly inverting this in the case of transmitted UDP packets. Reported-by: Wissam Shoukair <wissams@mellanox.com> Tested-by: Wissam Shoukair <wissams@mellanox.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
2015-09-10 14:19:16 +02:00
unsigned long sum = 0xffff;
while ( len > 1 ) {
sum += *( ( uint16_t * ) data );
data += 2;
len -= 2;
}
if ( len > 0 )
sum += *( ( uint8_t * ) data );
while ( sum >> 16 )
sum = ( ( sum & 0xffff ) + ( sum >> 16 ) );
[tcpip] Avoid generating positive zero for transmitted UDP checksums TCP/IP checksum fields are one's complement values and therefore have two possible representations of zero: positive zero (0x0000) and negative zero (0xffff). In RFC768, UDP over IPv4 exploits this redundancy to repurpose the positive representation of zero (0x0000) to mean "no checksum calculated"; checksums are optional for UDP over IPv4. In RFC2460, checksums are made mandatory for UDP over IPv4. The wording of the RFC is such that the UDP header is mandated to use only the negative representation of zero (0xffff), rather than simply requiring the checksum to be correct but allowing for either representation of zero to be used. In RFC1071, an example algorithm is given for calculating the TCP/IP checksum. This algorithm happens to produce only the positive representation of zero (0x0000); this is an artifact of the way that unsigned arithmetic is used to calculate a signed one's complement sum (and its final negation). A common misconception has developed (exemplified in RFC1624) that this artifact is part of the specification. Many people have assumed that the checksum field should never contain the negative representation of zero (0xffff). A sensible receiver will calculate the checksum over the whole packet and verify that the result is zero (in whichever representation of zero happens to be generated by the receiver's algorithm). Such a receiver will not care which representation of zero happens to be used in the checksum field. However, there are receivers in existence which will verify the received checksum the hard way: by calculating the checksum over the remainder of the packet and comparing the result against the checksum field. If the representation of zero used by the receiver's algorithm does not match the representation of zero used by the transmitter (and so placed in the checksum field), and if the receiver does not explicitly allow for both representations to compare as equal, then the receiver may reject packets with a valid checksum. For UDP, the combined RFCs effectively mandate that we should generate only the negative representation of zero in the checksum field. For IP, TCP and ICMP, the RFCs do not mandate which representation of zero should be used, but the misconceptions which have grown up around RFC1071 and RFC1624 suggest that it would be least surprising to generate only the positive representation of zero in the checksum field. Fix by ensuring that all of our checksum algorithms generate only the positive representation of zero, and explicitly inverting this in the case of transmitted UDP packets. Reported-by: Wissam Shoukair <wissams@mellanox.com> Tested-by: Wissam Shoukair <wissams@mellanox.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
2015-09-10 14:19:16 +02:00
assert ( sum != 0x0000 );
return ~sum;
}
/**
* Report TCP/IP fixed-data test result
*
* @v test TCP/IP test
* @v file Test code file
* @v line Test code line
*/
static void tcpip_okx ( struct tcpip_test *test, const char *file,
unsigned int line ) {
uint16_t expected;
uint16_t generic_sum;
uint16_t sum;
/* Verify generic_tcpip_continue_chksum() result */
expected = rfc_tcpip_chksum ( test->data, test->len );
generic_sum = generic_tcpip_continue_chksum ( TCPIP_EMPTY_CSUM,
test->data, test->len );
okx ( generic_sum == expected, file, line );
/* Verify optimised tcpip_continue_chksum() result */
sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, test->data, test->len );
okx ( sum == expected, file, line );
}
#define tcpip_ok( test ) tcpip_okx ( test, __FILE__, __LINE__ )
/**
* Report TCP/IP pseudorandom-data test result
*
* @v test TCP/IP test
* @v file Test code file
* @v line Test code line
*/
static void tcpip_random_okx ( struct tcpip_random_test *test,
const char *file, unsigned int line ) {
uint8_t *data = ( tcpip_data + test->offset );
struct profiler profiler;
uint16_t expected;
uint16_t generic_sum;
uint16_t sum;
unsigned int i;
/* Sanity check */
assert ( ( test->len + test->offset ) <= sizeof ( tcpip_data ) );
/* Generate random data */
srandom ( test->seed );
for ( i = 0 ; i < test->len ; i++ )
data[i] = random();
/* Verify generic_tcpip_continue_chksum() result */
expected = rfc_tcpip_chksum ( data, test->len );
generic_sum = generic_tcpip_continue_chksum ( TCPIP_EMPTY_CSUM,
data, test->len );
okx ( generic_sum == expected, file, line );
/* Verify optimised tcpip_continue_chksum() result */
sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, test->len );
okx ( sum == expected, file, line );
/* Profile optimised calculation */
memset ( &profiler, 0, sizeof ( profiler ) );
for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
profile_start ( &profiler );
sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data,
test->len );
profile_stop ( &profiler );
}
DBG ( "TCPIP checksummed %zd bytes (+%zd) in %ld +/- %ld ticks\n",
test->len, test->offset, profile_mean ( &profiler ),
profile_stddev ( &profiler ) );
}
#define tcpip_random_ok( test ) tcpip_random_okx ( test, __FILE__, __LINE__ )
/**
* Perform TCP/IP self-tests
*
*/
static void tcpip_test_exec ( void ) {
tcpip_ok ( &empty );
tcpip_ok ( &one_byte );
tcpip_ok ( &two_bytes );
[tcpip] Avoid generating positive zero for transmitted UDP checksums TCP/IP checksum fields are one's complement values and therefore have two possible representations of zero: positive zero (0x0000) and negative zero (0xffff). In RFC768, UDP over IPv4 exploits this redundancy to repurpose the positive representation of zero (0x0000) to mean "no checksum calculated"; checksums are optional for UDP over IPv4. In RFC2460, checksums are made mandatory for UDP over IPv4. The wording of the RFC is such that the UDP header is mandated to use only the negative representation of zero (0xffff), rather than simply requiring the checksum to be correct but allowing for either representation of zero to be used. In RFC1071, an example algorithm is given for calculating the TCP/IP checksum. This algorithm happens to produce only the positive representation of zero (0x0000); this is an artifact of the way that unsigned arithmetic is used to calculate a signed one's complement sum (and its final negation). A common misconception has developed (exemplified in RFC1624) that this artifact is part of the specification. Many people have assumed that the checksum field should never contain the negative representation of zero (0xffff). A sensible receiver will calculate the checksum over the whole packet and verify that the result is zero (in whichever representation of zero happens to be generated by the receiver's algorithm). Such a receiver will not care which representation of zero happens to be used in the checksum field. However, there are receivers in existence which will verify the received checksum the hard way: by calculating the checksum over the remainder of the packet and comparing the result against the checksum field. If the representation of zero used by the receiver's algorithm does not match the representation of zero used by the transmitter (and so placed in the checksum field), and if the receiver does not explicitly allow for both representations to compare as equal, then the receiver may reject packets with a valid checksum. For UDP, the combined RFCs effectively mandate that we should generate only the negative representation of zero in the checksum field. For IP, TCP and ICMP, the RFCs do not mandate which representation of zero should be used, but the misconceptions which have grown up around RFC1071 and RFC1624 suggest that it would be least surprising to generate only the positive representation of zero in the checksum field. Fix by ensuring that all of our checksum algorithms generate only the positive representation of zero, and explicitly inverting this in the case of transmitted UDP packets. Reported-by: Wissam Shoukair <wissams@mellanox.com> Tested-by: Wissam Shoukair <wissams@mellanox.com> Signed-off-by: Michael Brown <mcb30@ipxe.org>
2015-09-10 14:19:16 +02:00
tcpip_ok ( &positive_zero );
tcpip_ok ( &negative_zero );
tcpip_ok ( &final_carry_big );
tcpip_ok ( &final_carry_little );
tcpip_random_ok ( &random_aligned );
tcpip_random_ok ( &random_unaligned_1 );
tcpip_random_ok ( &random_unaligned_2 );
tcpip_random_ok ( &random_aligned_truncated );
tcpip_random_ok ( &partial );
}
/** TCP/IP self-test */
struct self_test tcpip_test __self_test = {
.name = "tcpip",
.exec = tcpip_test_exec,
};