iPXE
tcpip_test.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * TCP/IP self-tests
00029  *
00030  */
00031 
00032 /* Forcibly enable assertions */
00033 #undef NDEBUG
00034 
00035 #include <stdint.h>
00036 #include <stdlib.h>
00037 #include <string.h>
00038 #include <assert.h>
00039 #include <ipxe/test.h>
00040 #include <ipxe/profile.h>
00041 #include <ipxe/tcpip.h>
00042 
00043 /** Number of sample iterations for profiling */
00044 #define PROFILE_COUNT 16
00045 
00046 /** A TCP/IP fixed-data test */
00047 struct tcpip_test {
00048         /** Data */
00049         const void *data;
00050         /** Length of data */
00051         size_t len;
00052 };
00053 
00054 /** A TCP/IP pseudorandom-data test */
00055 struct tcpip_random_test {
00056         /** Seed */
00057         unsigned int seed;
00058         /** Length of data */
00059         size_t len;
00060         /** Alignment offset */
00061         size_t offset;
00062 };
00063 
00064 /** Define inline data */
00065 #define DATA(...) { __VA_ARGS__ }
00066 
00067 /** Define a TCP/IP fixed-data test */
00068 #define TCPIP_TEST( name, DATA )                                        \
00069         static const uint8_t __attribute__ (( aligned ( 16 ) ))         \
00070                 name ## _data[] = DATA;                                 \
00071         static struct tcpip_test name = {                               \
00072                 .data = name ## _data,                                  \
00073                 .len = sizeof ( name ## _data ),                        \
00074         }
00075 
00076 /** Define a TCP/IP pseudorandom-data test */
00077 #define TCPIP_RANDOM_TEST( name, SEED, LEN, OFFSET )                    \
00078         static struct tcpip_random_test name = {                        \
00079                 .seed = SEED,                                           \
00080                 .len = LEN,                                             \
00081                 .offset = OFFSET,                                       \
00082         }
00083 
00084 /** Buffer for pseudorandom-data tests */
00085 static uint8_t __attribute__ (( aligned ( 16 ) ))
00086         tcpip_data[ 4096 + 7 /* offset */ ];
00087 
00088 /** Empty data */
00089 TCPIP_TEST ( empty, DATA() );
00090 
00091 /** Single byte */
00092 TCPIP_TEST ( one_byte, DATA ( 0xeb ) );
00093 
00094 /** Double byte */
00095 TCPIP_TEST ( two_bytes, DATA ( 0xba, 0xbe ) );
00096 
00097 /** Positive zero data */
00098 TCPIP_TEST ( positive_zero, DATA ( 0x00, 0x00 ) );
00099 
00100 /** Negative zero data */
00101 TCPIP_TEST ( negative_zero, DATA ( 0xff, 0xff ) );
00102 
00103 /** Final wrap-around carry (big-endian) */
00104 TCPIP_TEST ( final_carry_big,
00105              DATA ( 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 ) );
00106 
00107 /** Final wrap-around carry (little-endian) */
00108 TCPIP_TEST ( final_carry_little,
00109              DATA ( 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 ) );
00110 
00111 /** Random data (aligned) */
00112 TCPIP_RANDOM_TEST ( random_aligned, 0x12345678UL, 4096, 0 );
00113 
00114 /** Random data (unaligned, +1) */
00115 TCPIP_RANDOM_TEST ( random_unaligned_1, 0x12345678UL, 4096, 1 );
00116 
00117 /** Random data (unaligned, +2) */
00118 TCPIP_RANDOM_TEST ( random_unaligned_2, 0x12345678UL, 4096, 2 );
00119 
00120 /** Random data (aligned, truncated) */
00121 TCPIP_RANDOM_TEST ( random_aligned_truncated, 0x12345678UL, 4095, 0 );
00122 
00123 /** Random data (unaligned start and finish) */
00124 TCPIP_RANDOM_TEST ( partial, 0xcafebabe, 121, 5 );
00125 
00126 /**
00127  * Calculate TCP/IP checksum
00128  *
00129  * @v data              Data to sum
00130  * @v len               Length of data
00131  * @ret cksum           Checksum
00132  *
00133  * This is a reference implementation taken from RFC1071 (and modified
00134  * to fix compilation without warnings under gcc).
00135  *
00136  * The initial value of the one's complement @c sum is changed from
00137  * positive zero (0x0000) to negative zero (0xffff).  This ensures
00138  * that the return value will always use the positive representation
00139  * of zero (0x0000).  Without this change, the return value would use
00140  * negative zero (0xffff) if the input data is zero length (or all
00141  * zeros) but positive zero (0x0000) for any other data which sums to
00142  * zero.
00143  */
00144 static uint16_t rfc_tcpip_chksum ( const void *data, size_t len ) {
00145         unsigned long sum = 0xffff;
00146 
00147         while ( len > 1 )  {
00148                 sum += *( ( uint16_t * ) data );
00149                 data += 2;
00150                 len -= 2;
00151         }
00152 
00153         if ( len > 0 )
00154                 sum += *( ( uint8_t * ) data );
00155 
00156         while ( sum >> 16 )
00157                 sum = ( ( sum & 0xffff ) + ( sum >> 16 ) );
00158 
00159         assert ( sum != 0x0000 );
00160         return ~sum;
00161 }
00162 
00163 /**
00164  * Report TCP/IP fixed-data test result
00165  *
00166  * @v test              TCP/IP test
00167  * @v file              Test code file
00168  * @v line              Test code line
00169  */
00170 static void tcpip_okx ( struct tcpip_test *test, const char *file,
00171                         unsigned int line ) {
00172         uint16_t expected;
00173         uint16_t generic_sum;
00174         uint16_t sum;
00175 
00176         /* Verify generic_tcpip_continue_chksum() result */
00177         expected = rfc_tcpip_chksum ( test->data, test->len );
00178         generic_sum = generic_tcpip_continue_chksum ( TCPIP_EMPTY_CSUM,
00179                                                       test->data, test->len );
00180         okx ( generic_sum == expected, file, line );
00181 
00182         /* Verify optimised tcpip_continue_chksum() result */
00183         sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, test->data, test->len );
00184         okx ( sum == expected, file, line );
00185 }
00186 #define tcpip_ok( test ) tcpip_okx ( test, __FILE__, __LINE__ )
00187 
00188 /**
00189  * Report TCP/IP pseudorandom-data test result
00190  *
00191  * @v test              TCP/IP test
00192  * @v file              Test code file
00193  * @v line              Test code line
00194  */
00195 static void tcpip_random_okx ( struct tcpip_random_test *test,
00196                                const char *file, unsigned int line ) {
00197         uint8_t *data = ( tcpip_data + test->offset );
00198         struct profiler profiler;
00199         uint16_t expected;
00200         uint16_t generic_sum;
00201         uint16_t sum;
00202         unsigned int i;
00203 
00204         /* Sanity check */
00205         assert ( ( test->len + test->offset ) <= sizeof ( tcpip_data ) );
00206 
00207         /* Generate random data */
00208         srandom ( test->seed );
00209         for ( i = 0 ; i < test->len ; i++ )
00210                 data[i] = random();
00211 
00212         /* Verify generic_tcpip_continue_chksum() result */
00213         expected = rfc_tcpip_chksum ( data, test->len );
00214         generic_sum = generic_tcpip_continue_chksum ( TCPIP_EMPTY_CSUM,
00215                                                       data, test->len );
00216         okx ( generic_sum == expected, file, line );
00217 
00218         /* Verify optimised tcpip_continue_chksum() result */
00219         sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, test->len );
00220         okx ( sum == expected, file, line );
00221 
00222         /* Profile optimised calculation */
00223         memset ( &profiler, 0, sizeof ( profiler ) );
00224         for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
00225                 profile_start ( &profiler );
00226                 sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data,
00227                                               test->len );
00228                 profile_stop ( &profiler );
00229         }
00230         DBG ( "TCPIP checksummed %zd bytes (+%zd) in %ld +/- %ld ticks\n",
00231               test->len, test->offset, profile_mean ( &profiler ),
00232               profile_stddev ( &profiler ) );
00233 }
00234 #define tcpip_random_ok( test ) tcpip_random_okx ( test, __FILE__, __LINE__ )
00235 
00236 /**
00237  * Perform TCP/IP self-tests
00238  *
00239  */
00240 static void tcpip_test_exec ( void ) {
00241 
00242         tcpip_ok ( &empty );
00243         tcpip_ok ( &one_byte );
00244         tcpip_ok ( &two_bytes );
00245         tcpip_ok ( &positive_zero );
00246         tcpip_ok ( &negative_zero );
00247         tcpip_ok ( &final_carry_big );
00248         tcpip_ok ( &final_carry_little );
00249         tcpip_random_ok ( &random_aligned );
00250         tcpip_random_ok ( &random_unaligned_1 );
00251         tcpip_random_ok ( &random_unaligned_2 );
00252         tcpip_random_ok ( &random_aligned_truncated );
00253         tcpip_random_ok ( &partial );
00254 }
00255 
00256 /** TCP/IP self-test */
00257 struct self_test tcpip_test __self_test = {
00258         .name = "tcpip",
00259         .exec = tcpip_test_exec,
00260 };