| /* |
| * Embedded Linux library |
| * Copyright (C) 2020 Intel Corporation |
| * |
| * SPDX-License-Identifier: LGPL-2.1-or-later |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <assert.h> |
| #include <linux/types.h> |
| #include <netinet/ip.h> |
| #include <linux/if_arp.h> |
| |
| #include <ell/ell.h> |
| #include "ell/dhcp6-private.h" |
| |
| static uint8_t client_packet[1024]; |
| static size_t client_packet_len; |
| |
| static const uint8_t expected_iaid[] = { 0x03, 0x04, 0x05, 0x06 }; |
| |
| static const uint8_t advertise_no_address[] = { |
| 0x02, 0xfc, 0xd6, 0xaf, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, |
| 0x26, 0xa3, 0x58, 0x38, 0x08, 0x00, 0x27, 0x2c, 0x64, 0xc8, 0x00, 0x02, |
| 0x00, 0x12, 0x00, 0x02, 0x00, 0x00, 0x0d, 0xe9, 0x47, 0x32, 0x30, 0x41, |
| 0x33, 0x32, 0x38, 0x30, 0x30, 0x30, 0x30, 0x32, 0x00, 0x11, 0x00, 0x27, |
| 0x00, 0x00, 0x0d, 0xe9, 0x00, 0x01, 0x00, 0x1f, 0x68, 0x74, 0x74, 0x70, |
| 0x73, 0x3a, 0x2f, 0x2f, 0x61, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x64, |
| 0x2e, 0x67, 0x66, 0x73, 0x76, 0x63, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, |
| 0x77, 0x6d, 0x70, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x06, 0x00, 0x0d, 0x00, |
| 0x02, 0x00, 0x02, 0x00, 0x17, 0x00, 0x10, 0xfd, 0xaa, 0x88, 0x52, 0x00, |
| 0x88, 0x00, 0x01, 0xfa, 0x8f, 0xca, 0xff, 0xfe, 0x40, 0x87, 0x0c, 0x00, |
| 0x18, 0x00, 0x06, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x00 |
| }; |
| |
| static void test_option_parsing(const void *data) |
| { |
| struct dhcp6_message *message = |
| (struct dhcp6_message *) advertise_no_address; |
| size_t len = sizeof(advertise_no_address); |
| struct dhcp6_option_iter iter; |
| uint16_t t; |
| uint16_t l; |
| const void *v; |
| |
| assert(_dhcp6_option_iter_init(&iter, message, len)); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == DHCP6_OPTION_CLIENT_ID); |
| assert(l == 14); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == DHCP6_OPTION_SERVER_ID); |
| assert(l == 18); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == DHCP6_OPTION_VENDOR_OPTS); |
| assert(l == 39); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == DHCP6_OPTION_STATUS_CODE); |
| assert(l == 2); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == DHCP6_OPTION_STATUS_CODE); |
| assert(l == 2); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == L_DHCP6_OPTION_DNS_SERVERS); |
| assert(l == 16); |
| |
| assert(_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| assert(t == L_DHCP6_OPTION_DOMAIN_LIST); |
| assert(l == 6); |
| |
| assert(!_dhcp6_option_iter_next(&iter, &t, &l, &v)); |
| } |
| |
| static const uint8_t solicit_data_1[] = { |
| 0x01, 0x84, 0xec, 0xce, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x0c, 0x03, 0x04, |
| 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, |
| 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, 0x52, 0x00, 0x08, 0x00, 0x02, |
| 0x00, 0x00 |
| }; |
| |
| static const uint8_t advertise_data_1[] = { |
| 0x02, 0x08, 0xd9, 0xc2, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, |
| 0x00, 0x01, 0x10, 0xc3, 0x7b, 0x54, 0x74, 0xd0, 0x00, 0x03, 0x00, 0x28, |
| 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0xa8, 0xc0, 0x00, 0x01, 0x27, 0x50, |
| 0x00, 0x05, 0x00, 0x18, 0x26, 0x05, 0x60, 0x00, 0x10, 0x25, 0x60, 0xeb, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xfd, 0x00, 0x01, 0x51, 0x80, |
| 0x00, 0x01, 0x51, 0x80, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x00, 0x73, 0x75, |
| 0x63, 0x63, 0x65, 0x73, 0x73, 0x00, 0x07, 0x00, 0x01, 0xff, 0x00, 0x17, |
| 0x00, 0x10, 0x26, 0x05, 0x60, 0x00, 0x10, 0x25, 0x60, 0xeb, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 |
| }; |
| |
| static const uint8_t request_data_1[] = { |
| 0x03, 0x67, 0x8b, 0xed, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, |
| 0x00, 0x01, 0x10, 0xc3, 0x7b, 0x54, 0x74, 0xd0, 0x00, 0x03, 0x00, 0x28, |
| 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x05, 0x00, 0x18, 0x26, 0x05, 0x60, 0x00, 0x10, 0x25, 0x60, 0xeb, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xfd, 0x00, 0x01, 0x51, 0x80, |
| 0x00, 0x01, 0x51, 0x80, 0x00, 0x06, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, |
| 0x00, 0x52, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00 |
| }; |
| |
| static const uint8_t reply_data_1[] = { |
| 0x07, 0x67, 0x8b, 0xed, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, |
| 0x00, 0x01, 0x10, 0xc3, 0x7b, 0x54, 0x74, 0xd0, 0x00, 0x03, 0x00, 0x28, |
| 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0xa8, 0xc0, 0x00, 0x01, 0x27, 0x50, |
| 0x00, 0x05, 0x00, 0x18, 0x26, 0x05, 0x60, 0x00, 0x10, 0x25, 0x60, 0xeb, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xfd, 0x00, 0x01, 0x51, 0x80, |
| 0x00, 0x01, 0x51, 0x80, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x00, 0x73, 0x75, |
| 0x63, 0x63, 0x65, 0x73, 0x73, 0x00, 0x17, 0x00, 0x10, 0x26, 0x05, 0x60, |
| 0x00, 0x10, 0x25, 0x60, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x01 |
| }; |
| |
| static const uint8_t solicit_data_2[] = { |
| 0x01, 0xc4, 0x39, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x0c, 0x03, 0x04, |
| 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, |
| 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, 0x52, 0x00, 0x08, 0x00, 0x02, |
| 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00 |
| }; |
| |
| static const uint8_t reply_data_2[] = { |
| 0x07, 0xc4, 0x39, 0x06, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, |
| 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, |
| 0x00, 0x01, 0x10, 0xc3, 0x7b, 0x54, 0x74, 0xd0, 0x00, 0x0e, 0x00, 0x00, |
| 0x00, 0x03, 0x00, 0x28, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00, 0xa8, 0xc0, |
| 0x00, 0x01, 0x27, 0x50, 0x00, 0x05, 0x00, 0x18, 0x26, 0x05, 0x60, 0x00, |
| 0x10, 0x25, 0x4e, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xfd, |
| 0x00, 0x01, 0x51, 0x80, 0x00, 0x01, 0x51, 0x80, 0x00, 0x0d, 0x00, 0x09, |
| 0x00, 0x00, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x00, 0x07, 0x00, |
| 0x01, 0xff, 0x00, 0x18, 0x00, 0x0d, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, |
| 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x17, 0x00, 0x10, 0x26, |
| 0x05, 0x60, 0x00, 0x10, 0x25, 0x4e, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x01 |
| }; |
| |
| static void test_lease_parsing(const void *data) |
| { |
| struct dhcp6_message *message = |
| (struct dhcp6_message *) advertise_data_1; |
| size_t len = sizeof(advertise_data_1); |
| struct dhcp6_option_iter iter; |
| struct l_dhcp6_lease *lease; |
| char *address; |
| char **dns; |
| |
| assert(_dhcp6_option_iter_init(&iter, message, len)); |
| |
| lease = _dhcp6_lease_parse_options(&iter, expected_iaid); |
| assert(lease); |
| |
| address = l_dhcp6_lease_get_address(lease); |
| assert(address); |
| assert(!strcmp(address, "2605:6000:1025:60eb::1bfd")); |
| l_free(address); |
| |
| dns = l_dhcp6_lease_get_dns(lease); |
| assert(dns); |
| assert(dns[0]); |
| assert(!dns[1]); |
| assert(!strcmp(dns[0], "2605:6000:1025:60eb::1")); |
| l_strfreev(dns); |
| |
| _dhcp6_lease_free(lease); |
| } |
| |
| static bool dhcp6_message_compare(const uint8_t *expected, size_t expected_len, |
| const uint8_t *obtained, size_t obtained_len) |
| { |
| struct dhcp6_message *e = (struct dhcp6_message *) expected; |
| struct dhcp6_message *o = (struct dhcp6_message *) obtained; |
| |
| if (expected_len != obtained_len) |
| return false; |
| |
| if (e->msg_type != o->msg_type) |
| return false; |
| |
| return !memcmp(e->options, o->options, |
| expected_len - sizeof(struct dhcp6_message)); |
| } |
| |
| |
| static int fake_transport_send(struct dhcp6_transport *transport, |
| const struct in6_addr *dest, |
| const void *data, size_t len) |
| { |
| assert(len <= sizeof(client_packet)); |
| memcpy(client_packet, data, len); |
| client_packet_len = len; |
| |
| return len; |
| } |
| |
| static bool event_handler_called; |
| |
| static void event_handler_lease_obtained(struct l_dhcp6_client *client, |
| enum l_dhcp6_client_event event, |
| void *userdata) |
| { |
| assert(client); |
| assert(event == L_DHCP6_CLIENT_EVENT_LEASE_OBTAINED); |
| event_handler_called = true; |
| } |
| |
| #define FEED_RX_DATA(packet) \ |
| memcpy(transaction_id, client_packet + 1, sizeof(transaction_id)); \ |
| memcpy(client_packet, packet, sizeof(packet)); \ |
| memcpy(client_packet + 1, transaction_id, sizeof(transaction_id)); \ |
| transport->rx_cb(client_packet, sizeof(packet), 0, client) \ |
| |
| static void test_obtain_lease(const void *data) |
| { |
| static const uint8_t addr[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; |
| uint8_t transaction_id[3]; |
| struct dhcp6_transport *transport = l_new(struct dhcp6_transport, 1); |
| const struct l_dhcp6_lease *lease; |
| struct l_dhcp6_client *client; |
| |
| transport->send = fake_transport_send; |
| transport->ifindex = 42; |
| |
| client = l_dhcp6_client_new(42); |
| assert(l_dhcp6_client_set_address(client, ARPHRD_ETHER, addr, 6)); |
| assert(_dhcp6_client_set_transport(client, transport)); |
| assert(l_dhcp6_client_set_event_handler(client, |
| event_handler_lease_obtained, NULL, NULL)); |
| assert(l_dhcp6_client_set_nodelay(client, true)); |
| assert(l_dhcp6_client_set_nora(client, true)); |
| assert(l_dhcp6_client_set_lla_randomized(client, true)); |
| assert(l_dhcp6_client_set_no_rapid_commit(client, true)); |
| |
| assert(l_dhcp6_client_start(client)); |
| |
| assert(dhcp6_message_compare(solicit_data_1, sizeof(solicit_data_1), |
| client_packet, client_packet_len)); |
| |
| FEED_RX_DATA(advertise_data_1); |
| |
| assert(dhcp6_message_compare(request_data_1, sizeof(request_data_1), |
| client_packet, client_packet_len)); |
| |
| event_handler_called = false; |
| FEED_RX_DATA(reply_data_1); |
| assert(event_handler_called); |
| |
| lease = l_dhcp6_client_get_lease(client); |
| assert(lease); |
| |
| l_dhcp6_client_destroy(client); |
| } |
| |
| static void test_obtain_lease_rc(const void *data) |
| { |
| static const uint8_t addr[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; |
| uint8_t transaction_id[3]; |
| struct dhcp6_transport *transport = l_new(struct dhcp6_transport, 1); |
| const struct l_dhcp6_lease *lease; |
| struct l_dhcp6_client *client; |
| char **domains; |
| |
| transport->send = fake_transport_send; |
| transport->ifindex = 42; |
| |
| client = l_dhcp6_client_new(42); |
| assert(l_dhcp6_client_set_address(client, ARPHRD_ETHER, addr, 6)); |
| assert(_dhcp6_client_set_transport(client, transport)); |
| assert(l_dhcp6_client_set_event_handler(client, |
| event_handler_lease_obtained, NULL, NULL)); |
| assert(l_dhcp6_client_set_nodelay(client, true)); |
| assert(l_dhcp6_client_set_nora(client, true)); |
| assert(l_dhcp6_client_set_lla_randomized(client, true)); |
| |
| assert(l_dhcp6_client_start(client)); |
| |
| assert(dhcp6_message_compare(solicit_data_2, sizeof(solicit_data_2), |
| client_packet, client_packet_len)); |
| |
| event_handler_called = false; |
| FEED_RX_DATA(reply_data_2); |
| assert(event_handler_called); |
| |
| lease = l_dhcp6_client_get_lease(client); |
| assert(lease); |
| |
| domains = l_dhcp6_lease_get_domains(lease); |
| assert(domains); |
| assert(domains[0]); |
| assert(!domains[1]); |
| assert(!strcmp(domains[0], "example.com")); |
| l_strfreev(domains); |
| |
| l_dhcp6_client_destroy(client); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| l_test_init(&argc, &argv); |
| |
| l_test_add("option parsing", test_option_parsing, NULL); |
| l_test_add("lease parsing", test_lease_parsing, NULL); |
| l_test_add("obtain lease - no rapid commit", test_obtain_lease, NULL); |
| l_test_add("obtain lease - rapid commit", test_obtain_lease_rc, NULL); |
| |
| return l_test_run(); |
| } |