
Imported n-dhcp4 code with command: git subtree add --prefix shared/n-dhcp4/ git@github.com:nettools/n-dhcp4.git master --squash To update the library use: git subtree pull --prefix shared/n-dhcp4/ git@github.com:nettools/n-dhcp4.git master --squash
389 lines
15 KiB
C
389 lines
15 KiB
C
/*
|
|
* Tests for DHCP4 Client Connections
|
|
*/
|
|
|
|
#undef NDEBUG
|
|
#include <assert.h>
|
|
#include <c-stdaux.h>
|
|
#include <endian.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <linux/if_packet.h>
|
|
#include <net/if_arp.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/epoll.h>
|
|
#include "n-dhcp4-private.h"
|
|
#include "test.h"
|
|
#include "util/link.h"
|
|
#include "util/netns.h"
|
|
#include "util/packet.h"
|
|
|
|
static void test_poll_client(int efd, unsigned int u32) {
|
|
struct epoll_event event = {};
|
|
int r;
|
|
|
|
r = epoll_wait(efd, &event, 1, -1);
|
|
c_assert(r == 1);
|
|
c_assert(event.events == EPOLLIN);
|
|
c_assert(event.data.u32 == u32);
|
|
}
|
|
|
|
static void test_poll_server(int fd) {
|
|
struct pollfd pfd = { .fd = fd, .events = POLLIN };
|
|
int r;
|
|
|
|
r = poll(&pfd, 1, -1);
|
|
c_assert(r == 1);
|
|
c_assert(pfd.revents == POLLIN);
|
|
}
|
|
|
|
static void test_s_connection_init(int netns, NDhcp4SConnection *connection, int ifindex) {
|
|
int r, oldns;
|
|
|
|
netns_get(&oldns);
|
|
netns_set(netns);
|
|
|
|
r = n_dhcp4_s_connection_init(connection, ifindex);
|
|
c_assert(!r);
|
|
|
|
netns_set(oldns);
|
|
}
|
|
|
|
static void test_c_connection_listen(int netns, NDhcp4CConnection *connection) {
|
|
int r, oldns;
|
|
|
|
netns_get(&oldns);
|
|
netns_set(netns);
|
|
|
|
r = n_dhcp4_c_connection_listen(connection);
|
|
c_assert(!r);
|
|
|
|
netns_set(oldns);
|
|
}
|
|
|
|
static void test_c_connection_connect(int netns,
|
|
NDhcp4CConnection *connection,
|
|
const struct in_addr *client,
|
|
const struct in_addr *server) {
|
|
int r, oldns;
|
|
|
|
netns_get(&oldns);
|
|
netns_set(netns);
|
|
|
|
r = n_dhcp4_c_connection_connect(connection, client, server);
|
|
c_assert(!r);
|
|
|
|
netns_set(oldns);
|
|
}
|
|
|
|
static void test_server_receive(NDhcp4SConnection *connection, uint8_t expected_type, NDhcp4Incoming **messagep) {
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
|
|
uint8_t received_type;
|
|
int r, fd;
|
|
|
|
n_dhcp4_s_connection_get_fd(connection, &fd);
|
|
test_poll_server(fd);
|
|
|
|
r = n_dhcp4_s_connection_dispatch_io(connection, &message);
|
|
c_assert(!r);
|
|
c_assert(message);
|
|
|
|
r = n_dhcp4_incoming_query_message_type(message, &received_type);
|
|
c_assert(!r);
|
|
c_assert(received_type == expected_type);
|
|
|
|
if (messagep) {
|
|
*messagep = message;
|
|
message = NULL;
|
|
}
|
|
}
|
|
|
|
static void test_client_receive(NDhcp4CConnection *connection, uint8_t expected_type, NDhcp4Incoming **messagep) {
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
|
|
uint8_t received_type;
|
|
int r;
|
|
|
|
test_poll_client(connection->fd_epoll, N_DHCP4_CLIENT_EPOLL_IO);
|
|
|
|
r = n_dhcp4_c_connection_dispatch_io(connection, &message);
|
|
c_assert(!r);
|
|
c_assert(message);
|
|
|
|
r = n_dhcp4_incoming_query_message_type(message, &received_type);
|
|
c_assert(!r);
|
|
c_assert(received_type == expected_type);
|
|
|
|
if (messagep) {
|
|
*messagep = message;
|
|
message = NULL;
|
|
}
|
|
}
|
|
|
|
static void test_discover(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
const struct in_addr *addr_server,
|
|
const struct in_addr *addr_client,
|
|
NDhcp4Incoming **offerp) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply_out = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_discover_new(connection_client, &request_out);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_DISCOVER, &request_in);
|
|
|
|
r = n_dhcp4_s_connection_offer_new(connection_server, &reply_out, request_in, addr_server, addr_client, 60);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply_out);
|
|
c_assert(!r);
|
|
|
|
test_client_receive(connection_client, N_DHCP4_MESSAGE_OFFER, offerp);
|
|
}
|
|
|
|
static void test_select(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
NDhcp4Incoming *offer,
|
|
const struct in_addr *addr_server,
|
|
const struct in_addr *addr_client) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_select_new(connection_client, &request_out, offer);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
|
|
|
|
r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
|
|
c_assert(!r);
|
|
|
|
test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, NULL);
|
|
}
|
|
|
|
static void test_reboot(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
const struct in_addr *addr_server,
|
|
const struct in_addr *addr_client,
|
|
NDhcp4Incoming **ackp) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_reboot_new(connection_client, &request_out, addr_server);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
|
|
|
|
r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
|
|
c_assert(!r);
|
|
|
|
test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, ackp);
|
|
}
|
|
|
|
static void test_decline(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
NDhcp4Incoming *ack) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_decline_new(connection_client, &request_out, ack, "No thanks.");
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_DECLINE, NULL);
|
|
}
|
|
|
|
|
|
static void test_renew(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
const struct in_addr *addr_server,
|
|
const struct in_addr *addr_client) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_renew_new(connection_client, &request_out);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
|
|
|
|
r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
|
|
c_assert(!r);
|
|
|
|
test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, NULL);
|
|
}
|
|
|
|
static void test_rebind(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
const struct in_addr *addr_server,
|
|
const struct in_addr *addr_client) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *request_in = NULL;
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *reply = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_rebind_new(connection_client, &request_out);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_REQUEST, &request_in);
|
|
|
|
r = n_dhcp4_s_connection_ack_new(connection_server, &reply, request_in, addr_server, addr_client, 60);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_s_connection_send_reply(connection_server, addr_server, reply);
|
|
c_assert(!r);
|
|
|
|
test_client_receive(connection_client, N_DHCP4_MESSAGE_ACK, NULL);
|
|
}
|
|
|
|
static void test_release(NDhcp4SConnection *connection_server,
|
|
NDhcp4CConnection *connection_client,
|
|
const struct in_addr *addr_server,
|
|
const struct in_addr *addr_client) {
|
|
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request_out = NULL;
|
|
int r;
|
|
|
|
r = n_dhcp4_c_connection_release_new(connection_client, &request_out, "Shutting down!");
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_start_request(connection_client, request_out, 0);
|
|
c_assert(!r);
|
|
request_out = NULL;
|
|
|
|
test_server_receive(connection_server, N_DHCP4_MESSAGE_RELEASE, NULL);
|
|
}
|
|
|
|
static void test_connection(void) {
|
|
const struct in_addr addr_server = (struct in_addr){ htonl(10 << 24 | 1) };
|
|
const struct in_addr addr_client = (struct in_addr){ htonl(10 << 24 | 2) };
|
|
_c_cleanup_(netns_closep) int ns_server = -1, ns_client = -1;
|
|
_c_cleanup_(link_deinit) Link link_server = LINK_NULL(link_server);
|
|
_c_cleanup_(link_deinit) Link link_client = LINK_NULL(link_client);
|
|
_c_cleanup_(c_closep) int efd_client = -1;
|
|
int r;
|
|
|
|
/* setup */
|
|
|
|
netns_new(&ns_server);
|
|
netns_new(&ns_client);
|
|
|
|
link_new_veth(&link_server, &link_client, ns_server, ns_client);
|
|
link_add_ip4(&link_server, &addr_server, 8);
|
|
|
|
efd_client = epoll_create1(EPOLL_CLOEXEC);
|
|
c_assert(efd_client >= 0);
|
|
|
|
/* test connections */
|
|
{
|
|
_c_cleanup_(n_dhcp4_client_config_freep) NDhcp4ClientConfig *client_config = NULL;
|
|
_c_cleanup_(n_dhcp4_client_probe_config_freep) NDhcp4ClientProbeConfig *probe_config = NULL;
|
|
NDhcp4SConnection connection_server = N_DHCP4_S_CONNECTION_NULL(connection_server);
|
|
NDhcp4SConnectionIp connection_server_ip = N_DHCP4_S_CONNECTION_IP_NULL(connection_server_ip);
|
|
NDhcp4CConnection connection_client = N_DHCP4_C_CONNECTION_NULL(connection_client);
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *offer = NULL;
|
|
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *ack = NULL;
|
|
|
|
test_s_connection_init(ns_server, &connection_server, link_server.ifindex);
|
|
n_dhcp4_s_connection_ip_init(&connection_server_ip, addr_server);
|
|
n_dhcp4_s_connection_ip_link(&connection_server_ip, &connection_server);
|
|
|
|
r = n_dhcp4_client_config_new(&client_config);
|
|
c_assert(!r);
|
|
|
|
n_dhcp4_client_config_set_ifindex(client_config, link_client.ifindex);
|
|
n_dhcp4_client_config_set_transport(client_config, N_DHCP4_TRANSPORT_ETHERNET);
|
|
n_dhcp4_client_config_set_request_broadcast(client_config, false);
|
|
n_dhcp4_client_config_set_mac(client_config, link_client.mac.ether_addr_octet, ETH_ALEN);
|
|
n_dhcp4_client_config_set_broadcast_mac(client_config,
|
|
(const uint8_t[]){
|
|
0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff,
|
|
},
|
|
ETH_ALEN);
|
|
r = n_dhcp4_client_config_set_client_id(client_config,
|
|
(void *)"client-id",
|
|
strlen("client-id"));
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_client_probe_config_new(&probe_config);
|
|
c_assert(!r);
|
|
|
|
r = n_dhcp4_c_connection_init(&connection_client,
|
|
client_config,
|
|
probe_config,
|
|
efd_client);
|
|
c_assert(!r);
|
|
test_c_connection_listen(ns_client, &connection_client);
|
|
|
|
test_discover(&connection_server, &connection_client, &addr_server, &addr_client, &offer);
|
|
test_select(&connection_server, &connection_client, offer, &addr_server, &addr_client);
|
|
test_reboot(&connection_server, &connection_client, &addr_server, &addr_client, &ack);
|
|
test_decline(&connection_server, &connection_client, ack);
|
|
|
|
link_add_ip4(&link_client, &addr_client, 8);
|
|
test_c_connection_connect(ns_client, &connection_client, &addr_client, &addr_server);
|
|
|
|
test_renew(&connection_server, &connection_client, &addr_server, &addr_client);
|
|
test_rebind(&connection_server, &connection_client, &addr_server, &addr_client);
|
|
test_release(&connection_server, &connection_client, &addr_server, &addr_client);
|
|
|
|
n_dhcp4_c_connection_deinit(&connection_client);
|
|
n_dhcp4_s_connection_ip_unlink(&connection_server_ip);
|
|
n_dhcp4_s_connection_ip_deinit(&connection_server_ip);
|
|
n_dhcp4_s_connection_deinit(&connection_server);
|
|
}
|
|
|
|
/* teardown */
|
|
|
|
link_del_ip4(&link_client, &addr_client, 8);
|
|
link_del_ip4(&link_server, &addr_server, 8);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
test_setup();
|
|
|
|
test_connection();
|
|
|
|
return 0;
|
|
}
|