/* * * Embedded Linux library * * Copyright (C) 2018 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "ell/dhcp-private.h" static bool verbose = false; static uint8_t client_packet[1024]; static size_t client_packet_len; static uint8_t server_packet[1024]; static size_t server_packet_len; static void test_request_option(const void *data) { struct l_dhcp_client *dhcp; dhcp = l_dhcp_client_new(0); assert(dhcp); assert(!l_dhcp_client_add_request_option(NULL, 0)); assert(l_dhcp_client_add_request_option(dhcp, L_DHCP_OPTION_SUBNET_MASK)); assert(l_dhcp_client_add_request_option(dhcp, L_DHCP_OPTION_ROUTER)); assert(l_dhcp_client_add_request_option(dhcp, L_DHCP_OPTION_HOST_NAME)); assert(l_dhcp_client_add_request_option(dhcp, L_DHCP_OPTION_DOMAIN_NAME)); assert(l_dhcp_client_add_request_option(dhcp, L_DHCP_OPTION_DOMAIN_NAME_SERVER)); assert(l_dhcp_client_add_request_option(dhcp, L_DHCP_OPTION_NTP_SERVERS)); assert(!l_dhcp_client_add_request_option(dhcp, 0)); assert(!l_dhcp_client_add_request_option(dhcp, 255)); assert(!l_dhcp_client_add_request_option(dhcp, 52)); assert(!l_dhcp_client_add_request_option(dhcp, 53)); assert(!l_dhcp_client_add_request_option(dhcp, 55)); assert(l_dhcp_client_add_request_option(dhcp, 33)); assert(l_dhcp_client_add_request_option(dhcp, 44)); l_dhcp_client_destroy(dhcp); } static void test_invalid_message_length(const void *data) { struct dhcp_message message; struct dhcp_message_iter iter; memset(&message, 0, sizeof(message)); assert(!_dhcp_message_iter_init(&iter, NULL, 0)); assert(!_dhcp_message_iter_init(&iter, &message, sizeof(message))); } static void test_cookie(const void *data) { struct dhcp_message *message; size_t len = sizeof(struct dhcp_message); uint8_t *opt; struct dhcp_message_iter iter; message = (struct dhcp_message *) l_new(uint8_t, len); opt = (uint8_t *)(&message->magic); opt[0] = 0xff; assert(!_dhcp_message_iter_init(&iter, message, len)); opt[0] = 99; opt[1] = 130; opt[2] = 83; opt[3] = 99; assert(_dhcp_message_iter_init(&iter, message, len)); assert(!_dhcp_message_iter_next(&iter, NULL, NULL, NULL)); l_free(message); } struct option_test { uint8_t sname[64]; int snamelen; uint8_t file[128]; int filelen; uint8_t options[128]; int len; }; static const struct option_test option_test_1 = { .options = { 42, 5, 65, 66, 67, 68, 69 }, .len = 7, }; static const struct option_test option_test_2 = { .options = { 42, 5, 65, 66, 67, 68, 69, 0, 0, 53, 1, 5 }, .len = 12, }; static const struct option_test option_test_3 = { .options = { 8, 255, 70, 71, 72 }, .len = 5, }; static const struct option_test option_test_4 = { .options = { 0x35, 0x01, 0x05, 0x36, 0x04, 0x01, 0x00, 0xa8, 0xc0, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0xc0, 0xa8, 0x00, 0x01, 0x06, 0x04, 0xc0, 0xa8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, .len = 40, }; static const struct option_test option_test_5 = { .options = { 53, 1, 2, 42, 3, 0, 0, 0 }, .len = 8, }; static const struct option_test option_test_6 = { .options = { 42, 2, 1, 2, 44 }, .len = 5, }; static const struct option_test option_test_7 = { .file = { 222, 3, 1, 2, 3, 53, 1, 6 }, .filelen = 8, .options = { 52, 0x1, 0x1 }, .len = 3, }; static const struct option_test option_test_8 = { .sname = { 1, 4, 1, 2, 3, 4, 53, 1, 5 }, .snamelen = 9, .file = { 222, 3, 1, 2, 3 }, .filelen = 5, .options = { 52, 0x1, 0x3 }, .len = 3, }; static struct dhcp_message *create_message(const struct option_test *test, size_t *out_len) { struct dhcp_message *message; size_t len = sizeof(struct dhcp_message) + test->len; uint8_t *opt; message = (struct dhcp_message *) l_new(uint8_t, len); opt = (uint8_t *)(&message->magic); opt[0] = 99; opt[1] = 130; opt[2] = 83; opt[3] = 99; if (test->len) memcpy(&opt[4], test->options, test->len); if (test->filelen <= 128) memcpy(&message->file, test->file, test->filelen); if (test->snamelen <= 64) memcpy(&message->sname, test->sname, test->snamelen); if (out_len) *out_len = len; return message; } static void test_option_1(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t,l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 42); assert(l == 5); assert(!_dhcp_message_iter_next(&iter, &t, &l, NULL)); l_free(message); } static void test_option_2(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t,l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 42); assert(l == 5); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 53); assert(l == 1); assert(!_dhcp_message_iter_next(&iter, &t, &l, NULL)); l_free(message); } static void test_option_3(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(!_dhcp_message_iter_next(&iter, NULL, NULL, NULL)); l_free(message); } static void test_option_4(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t,l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 0x35); assert(l == 1); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 0x36); assert(l == 4); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 0x33); assert(l == 4); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 0x1); assert(l == 4); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 0x3); assert(l == 4); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 0x6); assert(l == 4); assert(!_dhcp_message_iter_next(&iter, &t, &l, NULL)); l_free(message); } static void test_option_5(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t,l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 53); assert(l == 1); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 42); assert(l == 3); assert(!_dhcp_message_iter_next(&iter, &t, &l, NULL)); l_free(message); } static void test_option_6(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t,l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 42); assert(l == 2); assert(!_dhcp_message_iter_next(&iter, NULL, NULL, NULL)); l_free(message); } static void test_option_7(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t,l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 222); assert(l == 3); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 53); assert(l == 1); assert(!_dhcp_message_iter_next(&iter, NULL, NULL, NULL)); l_free(message); } static void test_option_8(const void *data) { const struct option_test *test = data; struct dhcp_message *message; struct dhcp_message_iter iter; size_t len; uint8_t t, l; message = create_message(test, &len); assert(_dhcp_message_iter_init(&iter, message, len)); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 222); assert(l == 3); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 1); assert(l == 4); assert(_dhcp_message_iter_next(&iter, &t, &l, NULL)); assert(t == 53); assert(l == 1); assert(!_dhcp_message_iter_next(&iter, NULL, NULL, NULL)); l_free(message); } static void test_option_set(const void *data) { struct dhcp_message_builder builder; struct dhcp_message *message; size_t outlen; uint8_t *msg_out; unsigned int i; static uint8_t result[sizeof(struct dhcp_message) + 64]; static uint8_t options[64] = { 160, 2, 0x11, 0x12, 0, 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0, 55, 3, 0x51, 0x52, 0x53, 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 255 }; message = (struct dhcp_message *)result; /* test a few failure conditions */ assert(!_dhcp_message_builder_init(NULL, NULL, 0, 0)); assert(!_dhcp_message_builder_init(&builder, message, 0, 0)); _dhcp_message_builder_init(&builder, message, sizeof(result), DHCP_MESSAGE_TYPE_DISCOVER); _dhcp_message_builder_append(&builder, 160, 2, options + 2); _dhcp_message_builder_append(&builder, 0, 0, NULL); _dhcp_message_builder_append(&builder, 31, 8, options + 7); _dhcp_message_builder_append(&builder, 0, 0, NULL); _dhcp_message_builder_append(&builder, 55, 3, options + 18); _dhcp_message_builder_append(&builder, 17, 7, options + 23); msg_out = _dhcp_message_builder_finalize(&builder, &outlen); /* * The builde APIs automatically append the type passed in during init * so we can skip over that in order to test the expected static data */ msg_out += sizeof(struct dhcp_message) + 3; for (i = 0; i < outlen - sizeof(struct dhcp_message); i++) { if (msg_out[i] != options[i]) { if (verbose) { l_info("byte[%d] did not match 0x%02x 0x%02x", i, msg_out[i], options[i]); } assert(false); } } } static void test_checksum(const void *data) { static const uint8_t buf[20] = { 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; struct iovec iov[2]; assert(_dhcp_checksum(&buf, 20) == L_BE16_TO_CPU(0x78ae)); iov[0].iov_base = (void *) buf; iov[0].iov_len = 8; iov[1].iov_base = (void *) buf + 8; iov[1].iov_len = 12; assert(_dhcp_checksumv(iov, 2) == L_BE16_TO_CPU(0x78ae)); } static const uint8_t discover_data_1[] = { 0x01, 0x01, 0x06, 0x00, 0x4d, 0x7c, 0x67, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x01, 0x39, 0x02, 0x02, 0x40, 0x37, 0x06, 0x03, 0x2a, 0x0f, 0x06, 0x01, 0x0c, 0x0c, 0x0a, 0x3c, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const uint8_t offer_data_1[] = { 0x02, 0x01, 0x06, 0x00, 0x4d, 0x7c, 0x67, 0xc6, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0xf9, 0xc0, 0xa8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x02, 0x36, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x3a, 0x04, 0x00, 0x00, 0xa8, 0xc0, 0x3b, 0x04, 0x00, 0x01, 0x27, 0x50, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x04, 0xc0, 0xa8, 0x01, 0xff, 0x06, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const uint8_t request_data_1[] = { 0x01, 0x01, 0x06, 0x00, 0x4d, 0x7c, 0x67, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x03, 0x32, 0x04, 0xc0, 0xa8, 0x01, 0xf9, 0x36, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x39, 0x02, 0x02, 0x40, 0x37, 0x06, 0x03, 0x2a, 0x0f, 0x06, 0x01, 0x0c, 0x0c, 0x0a, 0x3c, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const uint8_t ack_data_1[] = { 0x02, 0x01, 0x06, 0x00, 0x4d, 0x7c, 0x67, 0xc6, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0xf9, 0xc0, 0xa8, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63, 0x35, 0x01, 0x05, 0x36, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x33, 0x04, 0x00, 0x01, 0x51, 0x80, 0x3a, 0x04, 0x00, 0x00, 0xa8, 0xc0, 0x3b, 0x04, 0x00, 0x01, 0x27, 0x50, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x1c, 0x04, 0xc0, 0xa8, 0x01, 0xff, 0x06, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0x03, 0x04, 0xc0, 0xa8, 0x01, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; bool event_handler_called = false; static int dhcp_message_prl_equal(const uint8_t *haystack, uint8_t len, const uint8_t *prl) { unsigned int i, j; uint8_t needle; for (i = 0; i < len; i++) { needle = prl[i]; for (j = 0; j < len; j++) if (haystack[j] == needle) break; if (j == len) return false; } return true; } static int dhcp_message_has_option(struct dhcp_message_iter *iter, uint8_t type, uint8_t len, const uint8_t *data) { uint8_t t,l; const void *v; while (_dhcp_message_iter_next(iter, &t, &l, &v)) { if (t != type) continue; if (l != len) return -EMSGSIZE; if (type == 55) return dhcp_message_prl_equal(v, len, data); if (memcmp(data, v, len)) return -EINVAL; return 0; } return -ENOENT; } static bool dhcp_message_compare(const uint8_t *expected, size_t expected_len, const uint8_t *obtained, size_t obtained_len) { struct dhcp_message *e = (struct dhcp_message *) expected; struct dhcp_message *o = (struct dhcp_message *) obtained; struct dhcp_message_iter ei; bool r = true; uint8_t t, l; const void *v; assert(e->op == o->op); assert(e->htype == o->htype); assert(e->hlen == o->hlen); assert(e->hops == o->hops); /* Ignore xid & secs */ assert(e->flags == o->flags); assert(e->ciaddr == o->ciaddr); assert(e->yiaddr == o->yiaddr); assert(e->giaddr == o->giaddr); assert(!memcmp(e->chaddr, o->chaddr, sizeof(e->chaddr))); assert(_dhcp_message_iter_init(&ei, e, expected_len)); while (_dhcp_message_iter_next(&ei, &t, &l, &v)) { struct dhcp_message_iter oi; int err; assert(_dhcp_message_iter_init(&oi, o, obtained_len)); err = dhcp_message_has_option(&oi, t, l, v); if (err >= 0) continue; r = false; switch (err) { case -EINVAL: l_info("Option %s(%hhu) payload doesn't match", _dhcp_option_to_string(t), t); break; case -EMSGSIZE: l_info("Option %s(%hhu) length doesn't match", _dhcp_option_to_string(t), t); break; case -ENOENT: l_info("Option %s(%hhu) missing", _dhcp_option_to_string(t), t); break; default: assert(false); } } return r; } static int fake_transport_send(struct dhcp_transport *transport, const struct sockaddr_in *dest, const void *data, size_t len) { assert(len <= sizeof(client_packet)); memcpy(client_packet, data, len); client_packet_len = len; return len; } static int fake_transport_l2_send(struct dhcp_transport *transprot, uint32_t saddr, uint16_t sport, uint32_t daddr, uint16_t dport, const uint8_t *dest_mac, const void *data, size_t len) { assert(len <= sizeof(client_packet)); memcpy(client_packet, data, len); client_packet_len = len; return len; } static void event_handler_lease_obtained(struct l_dhcp_client *client, enum l_dhcp_client_event event, void *userdata) { assert(client); assert(event == L_DHCP_CLIENT_EVENT_LEASE_OBTAINED); event_handler_called = true; } static void test_discover(const void *data) { static const uint8_t addr[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; struct l_dhcp_client *client; struct dhcp_transport *transport = l_new(struct dhcp_transport, 1); const struct l_dhcp_lease *lease; transport->send = fake_transport_send; transport->l2_send = fake_transport_l2_send; transport->ifindex = 42; client = l_dhcp_client_new(42); assert(l_dhcp_client_set_address(client, ARPHRD_ETHER, addr, 6)); assert(l_dhcp_client_set_interface_name(client, "fake")); assert(_dhcp_client_set_transport(client, transport)); assert(l_dhcp_client_set_hostname(client, "")); _dhcp_client_override_xid(client, 0x4d7c67c6); assert(l_dhcp_client_set_event_handler(client, event_handler_lease_obtained, NULL, NULL)); assert(l_dhcp_client_start(client)); assert(dhcp_message_compare(discover_data_1, sizeof(discover_data_1), client_packet, client_packet_len)); transport->rx_cb(offer_data_1, sizeof(offer_data_1), client); assert(dhcp_message_compare(request_data_1, sizeof(request_data_1), client_packet, client_packet_len)); event_handler_called = false; transport->rx_cb(ack_data_1, sizeof(ack_data_1), client); assert(event_handler_called); lease = l_dhcp_client_get_lease(client); assert(lease); assert(lease->server_address == L_CPU_TO_BE32(0xc0a80101)); assert(lease->subnet_mask == L_CPU_TO_BE32(0xffffff00)); assert(lease->broadcast == L_CPU_TO_BE32(0xc0a801ff)); assert(lease->router == L_CPU_TO_BE32(0xc0a80101)); assert(lease->address == L_CPU_TO_BE32(0xc0a801f9)); assert(lease->lifetime == 0x00015180); assert(lease->t1 == 0x0000a8c0); assert(lease->t2 == 0x00012750); l_dhcp_client_destroy(client); } static bool l2_send_called = false; static int fake_transport_server_l2_send(struct dhcp_transport *s, uint32_t source_ip, uint16_t source_port, uint32_t dest_ip, uint16_t dest_port, const uint8_t *dest_arp, const void *data, size_t len) { assert(len <= sizeof(server_packet)); memcpy(server_packet, data, len); server_packet_len = len; l2_send_called = true; return 0; } static void do_debug(const char *str, void *user_data) { const char *prefix = user_data; l_info("%s%s", prefix, str); } static char *new_client; static char *expired_client; static void server_event(struct l_dhcp_server *server, enum l_dhcp_server_event event, void *user_data, const struct l_dhcp_lease *lease) { switch (event) { case L_DHCP_SERVER_EVENT_NEW_LEASE: new_client = l_dhcp_lease_get_address(lease); break; case L_DHCP_SERVER_EVENT_LEASE_EXPIRED: expired_client = l_dhcp_lease_get_address(lease); break; } } static struct l_dhcp_client *client_init(const uint8_t *mac) { struct l_dhcp_client *client = l_dhcp_client_new(42); struct dhcp_transport *transport = l_new(struct dhcp_transport, 1); assert(l_dhcp_client_set_address(client, ARPHRD_ETHER, mac, 6)); assert(l_dhcp_client_set_interface_name(client, "fake")); assert(l_dhcp_client_set_hostname(client, "")); _dhcp_client_override_xid(client, 0x4d7c67c6); assert(l_dhcp_client_set_event_handler(client, event_handler_lease_obtained, NULL, NULL)); if (verbose) l_dhcp_client_set_debug(client, do_debug, "[DHCP1] ", NULL); transport->send = fake_transport_send; transport->l2_send = fake_transport_l2_send; transport->ifindex = 42; assert(_dhcp_client_set_transport(client, transport)); return client; } static bool client_connect(struct l_dhcp_client *client, struct l_dhcp_server *server) { struct dhcp_transport *srv_transport = _dhcp_server_get_transport(server); struct dhcp_transport *cli_transport = _dhcp_client_get_transport(client); if (!l_dhcp_client_start(client)) return false; /* RX DISCOVER */ srv_transport->rx_cb(client_packet, client_packet_len, server); assert(l2_send_called); l2_send_called = false; /* RX OFFER */ cli_transport->rx_cb(server_packet, server_packet_len, client); /* RX REQUEST */ srv_transport->rx_cb(client_packet, client_packet_len, server); assert(l2_send_called); l2_send_called = false; /* RX ACK */ cli_transport->rx_cb(server_packet, server_packet_len, client); assert(event_handler_called); return true; } static struct l_dhcp_server *server_init() { char *dns[] = { "192.168.1.1", "192.168.1.254", NULL }; struct l_dhcp_server *server = l_dhcp_server_new(41); struct dhcp_transport *srv_transport = l_new(struct dhcp_transport, 1); assert(l_dhcp_server_set_interface_name(server, "fake")); assert(l_dhcp_server_set_ip_address(server, "192.168.1.1")); assert(l_dhcp_server_set_netmask(server, "255.255.255.0")); assert(l_dhcp_server_set_gateway(server, "192.168.1.1")); assert(l_dhcp_server_set_dns(server, dns)); assert(l_dhcp_server_set_event_handler(server, server_event, NULL, NULL)); if (verbose) l_dhcp_server_set_debug(server, do_debug, "[DHCP SERV] ", NULL); srv_transport->ifindex = 41; srv_transport->l2_send = fake_transport_server_l2_send; assert(_dhcp_server_set_transport(server, srv_transport)); assert(l_dhcp_server_start(server)); return server; } static void test_complete_run(const void *data) { static const uint8_t addr1[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; static const uint8_t addr2[6] = { 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c }; struct dhcp_transport *srv_transport; struct l_dhcp_client *client1; struct l_dhcp_client *client2; struct l_dhcp_server *server; const struct l_dhcp_lease *cli_lease; /* client IP address */ char *cli_addr; /* servers IP address */ char *srv_addr; char *tmp_addr; char **dns_list; server = server_init(); assert(l_dhcp_server_set_ip_range(server, "192.168.1.2", "192.168.1.100")); srv_transport = _dhcp_server_get_transport(server); client1 = client_init(addr1); client_connect(client1, server); cli_lease = l_dhcp_client_get_lease(client1); assert(cli_lease); cli_addr = l_dhcp_lease_get_address(cli_lease); assert(cli_addr); assert(!strcmp(cli_addr, "192.168.1.2")); assert(new_client); assert(!strcmp(new_client, cli_addr)); l_free(new_client); new_client = NULL; srv_addr = l_dhcp_lease_get_server_id(cli_lease); assert(!strcmp(srv_addr, "192.168.1.1")); l_free(srv_addr); l_free(cli_addr); tmp_addr = l_dhcp_lease_get_gateway(cli_lease); assert(!strcmp(tmp_addr, "192.168.1.1")); l_free(tmp_addr); tmp_addr = l_dhcp_lease_get_netmask(cli_lease); assert(!strcmp(tmp_addr, "255.255.255.0")); l_free(tmp_addr); dns_list = l_dhcp_lease_get_dns(cli_lease); assert(dns_list && dns_list[0] && dns_list[1]); assert(!strcmp(dns_list[0], "192.168.1.1")); assert(!strcmp(dns_list[1], "192.168.1.254")); l_strv_free(dns_list); client2 = client_init(addr2); client_connect(client2, server); cli_lease = l_dhcp_client_get_lease(client2); assert(cli_lease); cli_addr = l_dhcp_lease_get_address(cli_lease); assert(cli_addr); assert(!strcmp(cli_addr, "192.168.1.3")); assert(new_client); assert(!strcmp(new_client, cli_addr)); l_free(new_client); new_client = NULL; srv_addr = l_dhcp_lease_get_server_id(cli_lease); assert(!strcmp(srv_addr, "192.168.1.1")); l_free(srv_addr); l_free(cli_addr); l_dhcp_client_stop(client1); srv_transport->rx_cb(client_packet, client_packet_len, server); assert(expired_client); assert(!strcmp(expired_client, "192.168.1.2")); l_free(expired_client); expired_client = NULL; l_dhcp_client_stop(client2); srv_transport->rx_cb(client_packet, client_packet_len, server); assert(expired_client); assert(!strcmp(expired_client, "192.168.1.3")); l_free(expired_client); expired_client = NULL; l_dhcp_client_destroy(client1); l_dhcp_client_destroy(client2); l_dhcp_server_stop(server); l_dhcp_server_destroy(server); } static void test_expired_ip_reuse(const void *data) { uint8_t addr[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x00}; struct l_dhcp_server *server = server_init(); struct dhcp_transport *srv_transport = _dhcp_server_get_transport(server); struct l_dhcp_client *client_new; const struct l_dhcp_lease *lease; char *cli_addr; int i; l_dhcp_server_set_ip_range(server, "192.168.1.2", "192.168.1.11"); _dhcp_server_set_max_expired_clients(server, 10); /* * Connect and release 10 clients, this should max out the expired * queue (since we are setting it to 10) and force the first client that * expired to be removed allowing 192.168.1.2 to be reused for a new * client */ for (i = 0; i < 10; i++) { struct l_dhcp_client *client; addr[5] = i; client = client_init(addr); client_connect(client, server); l_dhcp_client_stop(client); srv_transport->rx_cb(client_packet, client_packet_len, server); } addr[5] = i + 1; client_new = client_init(addr); client_connect(client_new, server); lease = l_dhcp_client_get_lease(client_new); assert(lease); cli_addr = l_dhcp_lease_get_address(lease); assert(!strcmp(cli_addr, "192.168.1.2")); l_free(cli_addr); } int main(int argc, char *argv[]) { l_test_init(&argc, &argv); l_test_add("request-option", test_request_option, NULL); l_test_add("invalid-message-length", test_invalid_message_length, NULL); l_test_add("cookie", test_cookie, NULL); l_test_add("option test 1", test_option_1, &option_test_1); l_test_add("option test 2", test_option_2, &option_test_2); l_test_add("option test 3", test_option_3, &option_test_3); l_test_add("option test 4", test_option_4, &option_test_4); l_test_add("option test 5", test_option_5, &option_test_5); l_test_add("option test 6", test_option_6, &option_test_6); l_test_add("option test 7", test_option_7, &option_test_7); l_test_add("option test 8", test_option_8, &option_test_8); l_test_add("option set", test_option_set, NULL); l_test_add("checksum", test_checksum, NULL); l_test_add("discover", test_discover, NULL); l_test_add("complete run", test_complete_run, NULL); l_test_add("expired IP reuse", test_expired_ip_reuse, NULL); return l_test_run(); }