blob: 37e723da25de02b69dff6fd1b6100c04af380765 [file]
/*
* 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();
}