From 21effc03849d9015fc71f2d634659c1766526bed Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 9 Jun 2009 17:10:18 -0700 Subject: [PATCH] Add unit test for TCP/IP checksumming code. --- lib/csum.c | 16 +++- tests/automake.mk | 5 + tests/test-csum.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 tests/test-csum.c diff --git a/lib/csum.c b/lib/csum.c index 5266be6d..75cbb53a 100644 --- a/lib/csum.c +++ b/lib/csum.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Nicira Networks. + * Copyright (c) 2008, 2009 Nicira Networks. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,7 +17,12 @@ #include #include "csum.h" -/* Returns the IP checksum of the 'n' bytes in 'data'. */ +/* Returns the IP checksum of the 'n' bytes in 'data'. + * + * The return value has the same endianness as the data. That is, if 'data' + * consists of a packet in network byte order, then the return value is a value + * in network byte order, and if 'data' consists of a data structure in host + * byte order, then the return value is in host byte order. */ uint16_t csum(const void *data, size_t n) { @@ -62,7 +67,12 @@ csum_continue(uint32_t partial, const void *data_, size_t n) } /* Returns the IP checksum corresponding to 'partial', which is a value updated - * by some combination of csum_add16(), csum_add32(), and csum_continue(). */ + * by some combination of csum_add16(), csum_add32(), and csum_continue(). + * + * The return value has the same endianness as the checksummed data. That is, + * if the data consist of a packet in network byte order, then the return value + * is a value in network byte order, and if the data are a data structure in + * host byte order, then the return value is in host byte order. */ uint16_t csum_finish(uint32_t partial) { diff --git a/tests/automake.mk b/tests/automake.mk index 0508144f..ab6c1b14 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -3,6 +3,11 @@ noinst_PROGRAMS += tests/test-classifier tests_test_classifier_SOURCES = tests/test-classifier.c tests_test_classifier_LDADD = lib/libopenvswitch.a +TESTS += tests/test-csum +noinst_PROGRAMS += tests/test-csum +tests_test_csum_SOURCES = tests/test-csum.c +tests_test_csum_LDADD = lib/libopenvswitch.a + TESTS += tests/test-flows.sh noinst_PROGRAMS += tests/test-flows tests_test_flows_SOURCES = tests/test-flows.c diff --git a/tests/test-csum.c b/tests/test-csum.c new file mode 100644 index 00000000..a6db5c58 --- /dev/null +++ b/tests/test-csum.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2009 Nicira Networks. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "csum.h" +#include +#include +#include +#include +#include +#include "random.h" +#include "util.h" + +#undef NDEBUG +#include + +struct test_case { + char *data; + size_t size; + uint16_t csum; +}; + +#define TEST_CASE(DATA, CSUM) { DATA, (sizeof DATA) - 1, CSUM } + +static const struct test_case test_cases[] = { + /* RFC 1071 section 3. */ + TEST_CASE("\x00\x01\xf2\x03\xf4\xf5\xf6\xf7", (uint16_t) ~0xddf2), + + /* http://www.sbprojects.com/projects/tcpip/theory/theory14.htm */ + TEST_CASE("\x45\x00\x00\x28\x1F\xFD\x40\x00\x80\x06" + "\x00\x00\xC0\xA8\x3B\x0A\xC0\xA8\x3B\x32", + 0xe345), + + /* http://mathforum.org/library/drmath/view/54379.html */ + TEST_CASE("\x86\x5e\xac\x60\x71\x2a\x81\xb5", 0xda60), +}; + +static void +mark(char c) +{ + putchar(c); + fflush(stdout); +} + +#if 0 +/* This code is useful for generating new test cases for RFC 1624 section 4. */ +static void +generate_rfc1624_test_case(void) +{ + int i; + + for (i = 0; i < 10000000; i++) { + uint32_t data[8]; + int j; + + for (j = 0; j < 8; j++) { + data[j] = random_uint32(); + } + data[7] &= 0x0000ffff; + data[7] |= 0x55550000; + if (ntohs(~csum(data, sizeof data - 2)) == 0xcd7a) { + ovs_hex_dump(stdout, data, sizeof data, 0, false); + exit(0); + } + } +} +#endif + + + +/* Make sure we get the calculation in RFC 1624 section 4 correct. */ +static void +test_rfc1624(void) +{ + /* "...an IP packet header in which a 16-bit field m = 0x5555..." */ + uint8_t data[32] = + "\xfe\x8f\xc1\x14\x4b\x6f\x70\x2a\x80\x29\x78\xc0\x58\x81\x77\xaa" + "\x66\x64\xfc\x96\x63\x97\x64\xee\x12\x53\x1d\xa9\x2d\xa9\x55\x55"; + + /* "...the one's complement sum of all other header octets is 0xCD7A." */ + assert(ntohs(csum(data, sizeof data - 2)) == (uint16_t) ~0xcd7a); + + /* "...the header checksum would be: + + HC = ~(0xCD7A + 0x5555) + = ~0x22D0 + = 0xDD2F" + */ + assert(ntohs(csum(data, sizeof data)) == 0xdd2f); + + /* "a 16-bit field m = 0x5555 changes to m' = 0x3285..." */ + data[30] = 0x32; + data[31] = 0x85; + + /* "The new checksum via recomputation is: + + HC' = ~(0xCD7A + 0x3285) + = ~0xFFFF + = 0x0000" + */ + assert(ntohs(csum(data, sizeof data)) == 0x0000); + + /* "Applying [Eqn. 3] to the example above, we get the correct result: + + HC' = ~(C + (-m) + m') + = ~(0x22D0 + ~0x5555 + 0x3285) + = ~0xFFFF + = 0x0000" */ + assert(recalc_csum16(0xdd2f, 0x5555, 0x3285) == 0x0000); + + mark('#'); +} + +int +main(void) +{ + const struct test_case *tc; + int i; + + for (tc = test_cases; tc < &test_cases[ARRAY_SIZE(test_cases)]; tc++) { + const uint16_t *data16 = (const uint16_t *) tc->data; + const uint32_t *data32 = (const uint32_t *) tc->data; + uint32_t partial; + size_t i; + + /* Test csum(). */ + assert(ntohs(csum(tc->data, tc->size)) == tc->csum); + mark('.'); + + /* Test csum_add16(). */ + partial = 0; + for (i = 0; i < tc->size / 2; i++) { + partial = csum_add16(partial, data16[i]); + } + assert(ntohs(csum_finish(partial)) == tc->csum); + mark('.'); + + /* Test csum_add32(). */ + partial = 0; + for (i = 0; i < tc->size / 4; i++) { + partial = csum_add32(partial, data32[i]); + } + assert(ntohs(csum_finish(partial)) == tc->csum); + mark('.'); + + /* Test alternating csum_add16() and csum_add32(). */ + partial = 0; + for (i = 0; i < tc->size / 4; i++) { + if (i % 2) { + partial = csum_add32(partial, data32[i]); + } else { + partial = csum_add16(partial, data16[i * 2]); + partial = csum_add16(partial, data16[i * 2 + 1]); + } + } + assert(ntohs(csum_finish(partial)) == tc->csum); + mark('.'); + + /* Test csum_continue(). */ + partial = 0; + for (i = 0; i < tc->size / 4; i++) { + if (i) { + partial = csum_continue(partial, &data32[i], 4); + } else { + partial = csum_continue(partial, &data16[i * 2], 2); + partial = csum_continue(partial, &data16[i * 2 + 1], 2); + } + } + assert(ntohs(csum_finish(partial)) == tc->csum); + mark('#'); + } + + test_rfc1624(); + + /* Test recalc_csum16(). */ + for (i = 0; i < 32; i++) { + uint16_t old_u16, new_u16; + uint16_t old_csum; + uint16_t data[16]; + int j, index; + + for (j = 0; j < ARRAY_SIZE(data); j++) { + data[j] = random_uint32(); + } + old_csum = csum(data, sizeof data); + index = random_range(ARRAY_SIZE(data)); + old_u16 = data[index]; + new_u16 = data[index] = random_uint32(); + assert(csum(data, sizeof data) + == recalc_csum16(old_csum, old_u16, new_u16)); + mark('.'); + } + mark('#'); + + /* Test recalc_csum32(). */ + for (i = 0; i < 32; i++) { + uint32_t old_u32, new_u32; + uint16_t old_csum; + uint32_t data[16]; + int j, index; + + for (j = 0; j < ARRAY_SIZE(data); j++) { + data[j] = random_uint32(); + } + old_csum = csum(data, sizeof data); + index = random_range(ARRAY_SIZE(data)); + old_u32 = data[index]; + new_u32 = data[index] = random_uint32(); + assert(csum(data, sizeof data) + == recalc_csum32(old_csum, old_u32, new_u32)); + mark('.'); + } + mark('#'); + + putchar('\n'); + + return 0; +} -- 2.30.2