diff options
| author | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-09-10 10:39:55 -0400 | 
|---|---|---|
| committer | Luke Shumaker <lukeshu@sbcglobal.net> | 2016-09-10 10:39:55 -0400 | 
| commit | 39bccd020e2f986d4ff30ec27f5ecc293637ada1 (patch) | |
| tree | 4a703c3ff929865b70c7d39769b74762228b9a44 /src/libsystemd-network/test | |
| parent | d8b5a22abc8116f8d4c3c42ad01cfc5529822972 (diff) | |
./move.sh
Diffstat (limited to 'src/libsystemd-network/test')
| -rw-r--r-- | src/libsystemd-network/test/test-acd.c | 114 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-dhcp-client.c | 513 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-dhcp-option.c | 367 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-dhcp-server.c | 261 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-dhcp6-client.c | 763 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-ipv4ll-manual.c | 128 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-ipv4ll.c | 220 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-lldp.c | 257 | ||||
| -rw-r--r-- | src/libsystemd-network/test/test-ndisc-rs.c | 168 | 
9 files changed, 2791 insertions, 0 deletions
| diff --git a/src/libsystemd-network/test/test-acd.c b/src/libsystemd-network/test/test-acd.c new file mode 100644 index 0000000000..53ddfc3b62 --- /dev/null +++ b/src/libsystemd-network/test/test-acd.c @@ -0,0 +1,114 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <linux/veth.h> +#include <net/if.h> + +#include <systemd/sd-event.h> +#include <systemd/sd-ipv4acd.h> +#include <systemd/sd-netlink.h> + +#include "in-addr-util.h" +#include "netlink-util.h" +#include "util.h" + +static void acd_handler(sd_ipv4acd *acd, int event, void *userdata) { +        assert_se(acd); + +        switch (event) { +        case SD_IPV4ACD_EVENT_BIND: +                log_info("bound"); +                break; +        case SD_IPV4ACD_EVENT_CONFLICT: +                log_info("conflict"); +                break; +        case SD_IPV4ACD_EVENT_STOP: +                log_error("the client was stopped"); +                break; +        default: +                assert_not_reached("invalid ACD event"); +        } +} + +static int client_run(int ifindex, const struct in_addr *pa, const struct ether_addr *ha, sd_event *e) { +        sd_ipv4acd *acd; + +        assert_se(sd_ipv4acd_new(&acd) >= 0); +        assert_se(sd_ipv4acd_attach_event(acd, e, 0) >= 0); + +        assert_se(sd_ipv4acd_set_index(acd, ifindex) >= 0); +        assert_se(sd_ipv4acd_set_mac(acd, ha) >= 0); +        assert_se(sd_ipv4acd_set_address(acd, pa) >= 0); +        assert_se(sd_ipv4acd_set_callback(acd, acd_handler, NULL) >= 0); + +        log_info("starting IPv4ACD client"); + +        assert_se(sd_ipv4acd_start(acd) >= 0); + +        assert_se(sd_event_loop(e) >= 0); + +        assert_se(!sd_ipv4acd_unref(acd)); + +        return EXIT_SUCCESS; +} + +static int test_acd(const char *ifname, const char *address) { +        _cleanup_(sd_event_unrefp) sd_event *e = NULL; +        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; +        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; +        union in_addr_union pa; +        struct ether_addr ha; +        int ifindex; + +        assert_se(in_addr_from_string(AF_INET, address, &pa) >= 0); + +        assert_se(sd_event_new(&e) >= 0); + +        assert_se(sd_netlink_open(&rtnl) >= 0); +        assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0); + +        assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0); +        assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); +        assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + +        assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); +        assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + +        client_run(ifindex, &pa.in, &ha, e); + +        return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        if (argc == 3) +                return test_acd(argv[1], argv[2]); +        else { +                log_error("This program takes two arguments.\n" +                          "\t %s <ifname> <IPv4 address>", program_invocation_short_name); +                return EXIT_FAILURE; +        } +} diff --git a/src/libsystemd-network/test/test-dhcp-client.c b/src/libsystemd-network/test/test-dhcp-client.c new file mode 100644 index 0000000000..478b370c4c --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp-client.c @@ -0,0 +1,513 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2013 Intel Corporation. All rights reserved. + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <stdio.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <systemd/sd-dhcp-client.h> +#include <systemd/sd-event.h> + +#include "alloc-util.h" +#include "dhcp-identifier.h" +#include "dhcp-internal.h" +#include "dhcp-protocol.h" +#include "fd-util.h" +#include "util.h" + +static uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'}; + +typedef int (*test_callback_recv_t)(size_t size, DHCPMessage *dhcp); + +static bool verbose = true; +static int test_fd[2]; +static test_callback_recv_t callback_recv; +static be32_t xid; +static sd_event_source *test_hangcheck; + +static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { +        assert_not_reached("Test case should have completed in 2 seconds"); + +        return 0; +} + +static void test_request_basic(sd_event *e) { +        int r; + +        sd_dhcp_client *client; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        r = sd_dhcp_client_new(&client); + +        assert_se(r >= 0); +        assert_se(client); + +        r = sd_dhcp_client_attach_event(client, e, 0); +        assert_se(r >= 0); + +        assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL); +        assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL); +        assert_se(sd_dhcp_client_set_index(NULL, 0) == -EINVAL); + +        assert_se(sd_dhcp_client_set_index(client, 15) == 0); +        assert_se(sd_dhcp_client_set_index(client, -42) == -EINVAL); +        assert_se(sd_dhcp_client_set_index(client, -1) == -EINVAL); +        assert_se(sd_dhcp_client_set_index(client, 0) == -EINVAL); +        assert_se(sd_dhcp_client_set_index(client, 1) == 0); + +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_ROUTER) == -EEXIST); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_HOST_NAME) == -EEXIST); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_DOMAIN_NAME) == -EEXIST); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST); + +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_PAD) == -EINVAL); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_END) == -EINVAL); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_MESSAGE_TYPE) == -EINVAL); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_OVERLOAD) == -EINVAL); +        assert_se(sd_dhcp_client_set_request_option(client, +                                        SD_DHCP_OPTION_PARAMETER_REQUEST_LIST) +                        == -EINVAL); + +        assert_se(sd_dhcp_client_set_request_option(client, 33) == 0); +        assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST); +        assert_se(sd_dhcp_client_set_request_option(client, 44) == 0); +        assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST); + +        sd_dhcp_client_unref(client); +} + +static void test_checksum(void) { +        uint8_t buf[20] = { +                0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, +                0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                0xff, 0xff, 0xff, 0xff +        }; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae)); +} + +static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) { +        switch(code) { +        case SD_DHCP_OPTION_CLIENT_IDENTIFIER: +        { +                uint32_t iaid; +                struct duid duid; +                size_t duid_len; + +                assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0); +                assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, &iaid) >= 0); + +                assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); +                assert_se(len == 19); +                assert_se(((uint8_t*) option)[0] == 0xff); + +                assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0); +                assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0); +                break; +        } + +        default: +                break; +        } + +        return 0; +} + +int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) { +        size_t size; +        _cleanup_free_ DHCPPacket *discover; +        uint16_t ip_check, udp_check; + +        assert_se(s >= 0); +        assert_se(packet); + +        size = sizeof(DHCPPacket); +        assert_se(len > size); + +        discover = memdup(packet, len); + +        assert_se(discover->ip.ttl == IPDEFTTL); +        assert_se(discover->ip.protocol == IPPROTO_UDP); +        assert_se(discover->ip.saddr == INADDR_ANY); +        assert_se(discover->ip.daddr == INADDR_BROADCAST); +        assert_se(discover->udp.source == be16toh(DHCP_PORT_CLIENT)); +        assert_se(discover->udp.dest == be16toh(DHCP_PORT_SERVER)); + +        ip_check = discover->ip.check; + +        discover->ip.ttl = 0; +        discover->ip.check = discover->udp.len; + +        udp_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip.ttl, len - 8); +        assert_se(udp_check == 0xffff); + +        discover->ip.ttl = IPDEFTTL; +        discover->ip.check = ip_check; + +        ip_check = ~dhcp_packet_checksum((uint8_t*)&discover->ip, sizeof(discover->ip)); +        assert_se(ip_check == 0xffff); + +        assert_se(discover->dhcp.xid); +        assert_se(memcmp(discover->dhcp.chaddr, &mac_addr, ETH_ALEN) == 0); + +        size = len - sizeof(struct iphdr) - sizeof(struct udphdr); + +        assert_se(callback_recv); +        callback_recv(size, &discover->dhcp); + +        return 575; +} + +int dhcp_network_bind_raw_socket( +                int index, +                union sockaddr_union *link, +                uint32_t id, +                const uint8_t *addr, size_t addr_len, +                uint16_t arp_type) { + +        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0) +                return -errno; + +        return test_fd[0]; +} + +int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { +        int fd; + +        fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); +        if (fd < 0) +                return -errno; + +        return fd; +} + +int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) { +        return 0; +} + +static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) { +        int res; + +        res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL); +        assert_se(res == DHCP_DISCOVER); + +        if (verbose) +                printf("  recv DHCP Discover 0x%08x\n", be32toh(dhcp->xid)); + +        return 0; +} + +static void test_discover_message(sd_event *e) { +        sd_dhcp_client *client; +        int res, r; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        r = sd_dhcp_client_new(&client); +        assert_se(r >= 0); +        assert_se(client); + +        r = sd_dhcp_client_attach_event(client, e, 0); +        assert_se(r >= 0); + +        assert_se(sd_dhcp_client_set_index(client, 42) >= 0); +        assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + +        assert_se(sd_dhcp_client_set_request_option(client, 248) >= 0); + +        callback_recv = test_discover_message_verify; + +        res = sd_dhcp_client_start(client); + +        assert_se(res == 0 || res == -EINPROGRESS); + +        sd_event_run(e, (uint64_t) -1); + +        sd_dhcp_client_stop(client); +        sd_dhcp_client_unref(client); + +        test_fd[1] = safe_close(test_fd[1]); + +        callback_recv = NULL; +} + +static uint8_t test_addr_acq_offer[] = { +        0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, +        0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01, +        0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44, +        0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, +        0x6f, 0x95, 0x2f, 0x30, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf, +        0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        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, 0x02, 0x01, 0x33, 0x04, 0x00, +        0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff, +        0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f, +        0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74, +        0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01, +        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static uint8_t test_addr_acq_ack[] = { +        0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, +        0x80, 0x11, 0xb3, 0x84, 0xc0, 0xa8, 0x02, 0x01, +        0xc0, 0xa8, 0x02, 0xbf, 0x00, 0x43, 0x00, 0x44, +        0x01, 0x34, 0x00, 0x00, 0x02, 0x01, 0x06, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0xc0, 0xa8, 0x02, 0xbf, +        0xc0, 0xa8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        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, 0x02, 0x01, 0x33, 0x04, 0x00, +        0x00, 0x02, 0x58, 0x01, 0x04, 0xff, 0xff, 0xff, +        0x00, 0x2a, 0x04, 0xc0, 0xa8, 0x02, 0x01, 0x0f, +        0x09, 0x6c, 0x61, 0x62, 0x2e, 0x69, 0x6e, 0x74, +        0x72, 0x61, 0x03, 0x04, 0xc0, 0xa8, 0x02, 0x01, +        0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static void test_addr_acq_acquired(sd_dhcp_client *client, int event, +                                   void *userdata) { +        sd_event *e = userdata; +        sd_dhcp_lease *lease; +        struct in_addr addr; + +        assert_se(client); +        assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE); + +        assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0); +        assert_se(lease); + +        assert_se(sd_dhcp_lease_get_address(lease, &addr) >= 0); +        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[44], +                      sizeof(addr.s_addr)) == 0); + +        assert_se(sd_dhcp_lease_get_netmask(lease, &addr) >= 0); +        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285], +                      sizeof(addr.s_addr)) == 0); + +        assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0); +        assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308], +                      sizeof(addr.s_addr)) == 0); + +        if (verbose) +                printf("  DHCP address acquired\n"); + +        sd_event_exit(e, 0); +} + +static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { +        uint16_t udp_check = 0; +        uint8_t *msg_bytes = (uint8_t *)request; +        int res; + +        res = dhcp_option_parse(request, size, check_options, NULL, NULL); +        assert_se(res == DHCP_REQUEST); +        assert_se(xid == request->xid); + +        assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END); + +        if (verbose) +                printf("  recv DHCP Request  0x%08x\n", be32toh(xid)); + +        memcpy(&test_addr_acq_ack[26], &udp_check, sizeof(udp_check)); +        memcpy(&test_addr_acq_ack[32], &xid, sizeof(xid)); +        memcpy(&test_addr_acq_ack[56], &mac_addr, ETHER_ADDR_LEN); + +        callback_recv = NULL; + +        res = write(test_fd[1], test_addr_acq_ack, +                    sizeof(test_addr_acq_ack)); +        assert_se(res == sizeof(test_addr_acq_ack)); + +        if (verbose) +                printf("  send DHCP Ack\n"); + +        return 0; +}; + +static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) { +        uint16_t udp_check = 0; +        uint8_t *msg_bytes = (uint8_t *)discover; +        int res; + +        res = dhcp_option_parse(discover, size, check_options, NULL, NULL); +        assert_se(res == DHCP_DISCOVER); + +        assert_se(msg_bytes[size - 1] == SD_DHCP_OPTION_END); + +        xid = discover->xid; + +        if (verbose) +                printf("  recv DHCP Discover 0x%08x\n", be32toh(xid)); + +        memcpy(&test_addr_acq_offer[26], &udp_check, sizeof(udp_check)); +        memcpy(&test_addr_acq_offer[32], &xid, sizeof(xid)); +        memcpy(&test_addr_acq_offer[56], &mac_addr, ETHER_ADDR_LEN); + +        callback_recv = test_addr_acq_recv_request; + +        res = write(test_fd[1], test_addr_acq_offer, +                    sizeof(test_addr_acq_offer)); +        assert_se(res == sizeof(test_addr_acq_offer)); + +        if (verbose) +                printf("  sent DHCP Offer\n"); + +        return 0; +} + +static void test_addr_acq(sd_event *e) { +        usec_t time_now = now(clock_boottime_or_monotonic()); +        sd_dhcp_client *client; +        int res, r; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        r = sd_dhcp_client_new(&client); +        assert_se(r >= 0); +        assert_se(client); + +        r = sd_dhcp_client_attach_event(client, e, 0); +        assert_se(r >= 0); + +        assert_se(sd_dhcp_client_set_index(client, 42) >= 0); +        assert_se(sd_dhcp_client_set_mac(client, mac_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + +        assert_se(sd_dhcp_client_set_callback(client, test_addr_acq_acquired, e) >= 0); + +        callback_recv = test_addr_acq_recv_discover; + +        assert_se(sd_event_add_time(e, &test_hangcheck, +                                    clock_boottime_or_monotonic(), +                                    time_now + 2 * USEC_PER_SEC, 0, +                                    test_dhcp_hangcheck, NULL) >= 0); + +        res = sd_dhcp_client_start(client); +        assert_se(res == 0 || res == -EINPROGRESS); + +        assert_se(sd_event_loop(e) >= 0); + +        test_hangcheck = sd_event_source_unref(test_hangcheck); + +        assert_se(sd_dhcp_client_set_callback(client, NULL, NULL) >= 0); +        assert_se(sd_dhcp_client_stop(client) >= 0); +        sd_dhcp_client_unref(client); + +        test_fd[1] = safe_close(test_fd[1]); + +        callback_recv = NULL; +        xid = 0; +} + +int main(int argc, char *argv[]) { +        _cleanup_(sd_event_unrefp) sd_event *e; + +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        assert_se(sd_event_new(&e) >= 0); + +        test_request_basic(e); +        test_checksum(); + +        test_discover_message(e); +        test_addr_acq(e); + +#ifdef VALGRIND +        /* Make sure the async_close thread has finished. +         * valgrind would report some of the phread_* structures +         * as not cleaned up properly. */ +        sleep(1); +#endif + +        return 0; +} diff --git a/src/libsystemd-network/test/test-dhcp-option.c b/src/libsystemd-network/test/test-dhcp-option.c new file mode 100644 index 0000000000..d84859c053 --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp-option.c @@ -0,0 +1,367 @@ +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "alloc-util.h" +#include "dhcp-internal.h" +#include "dhcp-protocol.h" +#include "macro.h" +#include "util.h" + +struct option_desc { +        uint8_t sname[64]; +        int snamelen; +        uint8_t file[128]; +        int filelen; +        uint8_t options[128]; +        int len; +        bool success; +        int filepos; +        int snamepos; +        int pos; +}; + +static bool verbose = false; + +static struct option_desc option_tests[] = { +        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69 }, 7, false, }, +        { {}, 0, {}, 0, { 42, 5, 65, 66, 67, 68, 69, 0, 0, +                          SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 12, true, }, +        { {}, 0, {}, 0, { 8, 255, 70, 71, 72 }, 5, false, }, +        { {}, 0, {}, 0, { 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, }, +          40, true, }, +        { {}, 0, {}, 0, { SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_OFFER, +                          42, 3, 0, 0, 0 }, 8, true, }, +        { {}, 0, {}, 0, { 42, 2, 1, 2, 44 }, 5, false, }, + +        { {}, 0, +          { 222, 3, 1, 2, 3, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_NAK }, 8, +          { SD_DHCP_OPTION_OVERLOAD, 1, DHCP_OVERLOAD_FILE }, 3, true, }, + +        { { 1, 4, 1, 2, 3, 4, SD_DHCP_OPTION_MESSAGE_TYPE, 1, DHCP_ACK }, 9, +          { 222, 3, 1, 2, 3 }, 5, +          { SD_DHCP_OPTION_OVERLOAD, 1, +            DHCP_OVERLOAD_FILE|DHCP_OVERLOAD_SNAME }, 3, true, }, +}; + +static const char *dhcp_type(int type) { +        switch(type) { +        case DHCP_DISCOVER: +                return "DHCPDISCOVER"; +        case DHCP_OFFER: +                return "DHCPOFFER"; +        case DHCP_REQUEST: +                return "DHCPREQUEST"; +        case DHCP_DECLINE: +                return "DHCPDECLINE"; +        case DHCP_ACK: +                return "DHCPACK"; +        case DHCP_NAK: +                return "DHCPNAK"; +        case DHCP_RELEASE: +                return "DHCPRELEASE"; +        default: +                return "unknown"; +        } +} + +static void test_invalid_buffer_length(void) { +        DHCPMessage message; + +        assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL); +        assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL); +} + +static void test_message_init(void) { +        _cleanup_free_ DHCPMessage *message = NULL; +        size_t optlen = 4, optoffset; +        size_t len = sizeof(DHCPMessage) + optlen; +        uint8_t *magic; + +        message = malloc0(len); + +        assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678, +                  DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0); + +        assert_se(message->xid == htobe32(0x12345678)); +        assert_se(message->op == BOOTREQUEST); + +        magic = (uint8_t*)&message->magic; + +        assert_se(magic[0] == 99); +        assert_se(magic[1] == 130); +        assert_se(magic[2] == 83); +        assert_se(magic[3] == 99); + +        assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0); +} + +static DHCPMessage *create_message(uint8_t *options, uint16_t optlen, +                uint8_t *file, uint8_t filelen, +                uint8_t *sname, uint8_t snamelen) { +        DHCPMessage *message; +        size_t len = sizeof(DHCPMessage) + optlen; + +        message = malloc0(len); +        assert_se(message); + +        memcpy_safe(&message->options, options, optlen); +        memcpy_safe(&message->file, file, filelen); +        memcpy_safe(&message->sname, sname, snamelen); + +        return message; +} + +static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) { +        assert(*descpos >= 0); + +        while (*descpos < *desclen) { +                switch(descoption[*descpos]) { +                case SD_DHCP_OPTION_PAD: +                        *descpos += 1; +                        break; + +                case SD_DHCP_OPTION_MESSAGE_TYPE: +                case SD_DHCP_OPTION_OVERLOAD: +                        *descpos += 3; +                        break; + +                default: +                        return; +                } +        } +} + +static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) { +        struct option_desc *desc = userdata; +        uint8_t *descoption = NULL; +        int *desclen = NULL, *descpos = NULL; +        uint8_t optcode = 0; +        uint8_t optlen = 0; +        uint8_t i; + +        assert_se((!desc && !code && !len) || desc); + +        if (!desc) +                return -EINVAL; + +        assert_se(code != SD_DHCP_OPTION_PAD); +        assert_se(code != SD_DHCP_OPTION_END); +        assert_se(code != SD_DHCP_OPTION_MESSAGE_TYPE); +        assert_se(code != SD_DHCP_OPTION_OVERLOAD); + +        while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) { + +                if (desc->pos >= 0) { +                        descoption = &desc->options[0]; +                        desclen = &desc->len; +                        descpos = &desc->pos; +                } else if (desc->filepos >= 0) { +                        descoption = &desc->file[0]; +                        desclen = &desc->filelen; +                        descpos = &desc->filepos; +                } else if (desc->snamepos >= 0) { +                        descoption = &desc->sname[0]; +                        desclen = &desc->snamelen; +                        descpos = &desc->snamepos; +                } + +                assert_se(descoption && desclen && descpos); + +                if (*desclen) +                        test_ignore_opts(descoption, descpos, desclen); + +                if (*descpos < *desclen) +                        break; + +                if (*descpos == *desclen) +                        *descpos = -1; +        } + +        assert_se(descpos); +        assert_se(*descpos != -1); + +        optcode = descoption[*descpos]; +        optlen = descoption[*descpos + 1]; + +        if (verbose) +                printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode, +                                len, optlen); + +        assert_se(code == optcode); +        assert_se(len == optlen); + +        for (i = 0; i < len; i++) { + +                if (verbose) +                        printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i], +                                        descoption[*descpos + 2 + i]); + +                assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]); +        } + +        if (verbose) +                printf("\n"); + +        *descpos += optlen + 2; + +        test_ignore_opts(descoption, descpos, desclen); + +        if (desc->pos != -1 && desc->pos == desc->len) +                desc->pos = -1; + +        if (desc->filepos != -1 && desc->filepos == desc->filelen) +                desc->filepos = -1; + +        if (desc->snamepos != -1 && desc->snamepos == desc->snamelen) +                desc->snamepos = -1; + +        return 0; +} + +static void test_options(struct option_desc *desc) { +        uint8_t *options = NULL; +        uint8_t *file = NULL; +        uint8_t *sname = NULL; +        int optlen = 0; +        int filelen = 0; +        int snamelen = 0; +        int buflen = 0; +        _cleanup_free_ DHCPMessage *message = NULL; +        int res; + +        if (desc) { +                file = &desc->file[0]; +                filelen = desc->filelen; +                if (!filelen) +                        desc->filepos = -1; + +                sname = &desc->sname[0]; +                snamelen = desc->snamelen; +                if (!snamelen) +                        desc->snamepos = -1; + +                options = &desc->options[0]; +                optlen = desc->len; +                desc->pos = 0; +        } +        message = create_message(options, optlen, file, filelen, +                                 sname, snamelen); + +        buflen = sizeof(DHCPMessage) + optlen; + +        if (!desc) { +                assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG); +        } else if (desc->success) { +                assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0); +                assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1); +        } else +                assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0); + +        if (verbose) +                printf("DHCP type %s\n", dhcp_type(res)); +} + +static uint8_t options[64] = { +        'A', 'B', 'C', 'D', +        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 +}; + +static void test_option_set(void) { +        _cleanup_free_ DHCPMessage *result = NULL; +        size_t offset = 0, len, pos; +        unsigned i; + +        result = malloc0(sizeof(DHCPMessage) + 11); +        assert_se(result); + +        result->options[0] = 'A'; +        result->options[1] = 'B'; +        result->options[2] = 'C'; +        result->options[3] = 'D'; + +        assert_se(dhcp_option_append(result, 0, &offset, 0, SD_DHCP_OPTION_PAD, +                                     0, NULL) == -ENOBUFS); +        assert_se(offset == 0); + +        offset = 4; +        assert_se(dhcp_option_append(result, 5, &offset, 0, SD_DHCP_OPTION_PAD, +                                     0, NULL) == -ENOBUFS); +        assert_se(offset == 4); +        assert_se(dhcp_option_append(result, 6, &offset, 0, SD_DHCP_OPTION_PAD, +                                     0, NULL) >= 0); +        assert_se(offset == 5); + +        offset = pos = 4; +        len = 11; +        while (pos < len && options[pos] != SD_DHCP_OPTION_END) { +                assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME, +                                             options[pos], +                                             options[pos + 1], +                                             &options[pos + 2]) >= 0); + +                if (options[pos] == SD_DHCP_OPTION_PAD) +                        pos++; +                else +                        pos += 2 + options[pos + 1]; + +                if (pos < len) +                        assert_se(offset == pos); +        } + +        for (i = 0; i < 9; i++) { +                if (verbose) +                        printf("%2u: 0x%02x(0x%02x) (options)\n", i, result->options[i], +                               options[i]); +                assert_se(result->options[i] == options[i]); +        } + +        if (verbose) +                printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9], +                       SD_DHCP_OPTION_END); + +        assert_se(result->options[9] == SD_DHCP_OPTION_END); + +        if (verbose) +                printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10], +                       SD_DHCP_OPTION_PAD); + +        assert_se(result->options[10] == SD_DHCP_OPTION_PAD); + +        for (i = 0; i < pos - 8; i++) { +                if (verbose) +                        printf("%2u: 0x%02x(0x%02x) (sname)\n", i, result->sname[i], +                               options[i + 9]); +                assert_se(result->sname[i] == options[i + 9]); +        } + +        if (verbose) +                printf ("\n"); +} + +int main(int argc, char *argv[]) { +        unsigned i; + +        test_invalid_buffer_length(); +        test_message_init(); + +        test_options(NULL); + +        for (i = 0; i < ELEMENTSOF(option_tests); i++) +                test_options(&option_tests[i]); + +        test_option_set(); + +        return 0; +} diff --git a/src/libsystemd-network/test/test-dhcp-server.c b/src/libsystemd-network/test/test-dhcp-server.c new file mode 100644 index 0000000000..7dc315c07f --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp-server.c @@ -0,0 +1,261 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2013 Intel Corporation. All rights reserved. +  Copyright (C) 2014 Tom Gundersen + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> + +#include <systemd/sd-dhcp-server.h> +#include <systemd/sd-event.h> + +#include "dhcp-server-internal.h" + +static void test_pool(struct in_addr *address, unsigned size, int ret) { +        _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + +        assert_se(sd_dhcp_server_new(&server, 1) >= 0); + +        assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); +} + +static int test_basic(sd_event *event) { +        _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; +        struct in_addr address_lo = { +                .s_addr = htonl(INADDR_LOOPBACK), +        }; +        struct in_addr address_any = { +                .s_addr = htonl(INADDR_ANY), +        }; +        int r; + +        /* attach to loopback interface */ +        assert_se(sd_dhcp_server_new(&server, 1) >= 0); +        assert_se(server); + +        assert_se(sd_dhcp_server_attach_event(server, event, 0) >= 0); +        assert_se(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY); +        assert_se(sd_dhcp_server_get_event(server) == event); +        assert_se(sd_dhcp_server_detach_event(server) >= 0); +        assert_se(!sd_dhcp_server_get_event(server)); +        assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); +        assert_se(sd_dhcp_server_attach_event(server, NULL, 0) == -EBUSY); + +        assert_se(sd_dhcp_server_ref(server) == server); +        assert_se(!sd_dhcp_server_unref(server)); + +        assert_se(sd_dhcp_server_start(server) == -EUNATCH); + +        assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL); +        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE); +        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); +        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY); + +        test_pool(&address_any, 1, -EINVAL); +        test_pool(&address_lo, 1, 0); + +        r = sd_dhcp_server_start(server); + +        if (r == -EPERM) +                return EXIT_TEST_SKIP; +        assert_se(r >= 0); + +        assert_se(sd_dhcp_server_start(server) == -EBUSY); +        assert_se(sd_dhcp_server_stop(server) >= 0); +        assert_se(sd_dhcp_server_stop(server) >= 0); +        assert_se(sd_dhcp_server_start(server) >= 0); + +        return 0; +} + +static void test_message_handler(void) { +        _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; +        struct { +                DHCPMessage message; +                struct { +                        uint8_t code; +                        uint8_t length; +                        uint8_t type; +                } _packed_ option_type; +                struct { +                        uint8_t code; +                        uint8_t length; +                        be32_t address; +                } _packed_ option_requested_ip; +                struct { +                        uint8_t code; +                        uint8_t length; +                        be32_t address; +                } _packed_ option_server_id; +                struct { +                        uint8_t code; +                        uint8_t length; +                        uint8_t id[7]; +                } _packed_ option_client_id; +                uint8_t end; +        } _packed_ test = { +                .message.op = BOOTREQUEST, +                .message.htype = ARPHRD_ETHER, +                .message.hlen = ETHER_ADDR_LEN, +                .message.xid = htobe32(0x12345678), +                .message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' }, +                .option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE, +                .option_type.length = 1, +                .option_type.type = DHCP_DISCOVER, +                .end = SD_DHCP_OPTION_END, +        }; +        struct in_addr address_lo = { +                .s_addr = htonl(INADDR_LOOPBACK), +        }; + +        assert_se(sd_dhcp_server_new(&server, 1) >= 0); +        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); +        assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); +        assert_se(sd_dhcp_server_start(server) >= 0); + +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + +        test.end = 0; +        /* TODO, shouldn't this fail? */ +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); +        test.end = SD_DHCP_OPTION_END; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + +        test.option_type.code = 0; +        test.option_type.length = 0; +        test.option_type.type = 0; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.option_type.code = SD_DHCP_OPTION_MESSAGE_TYPE; +        test.option_type.length = 1; +        test.option_type.type = DHCP_DISCOVER; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + +        test.message.op = 0; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.message.op = BOOTREQUEST; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + +        test.message.htype = 0; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.message.htype = ARPHRD_ETHER; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + +        test.message.hlen = 0; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.message.hlen = ETHER_ADDR_LEN; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); + +        test.option_type.type = DHCP_REQUEST; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.option_requested_ip.code = SD_DHCP_OPTION_REQUESTED_IP_ADDRESS; +        test.option_requested_ip.length = 4; +        test.option_requested_ip.address = htobe32(0x12345678); +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_NAK); +        test.option_server_id.code = SD_DHCP_OPTION_SERVER_IDENTIFIER; +        test.option_server_id.length = 4; +        test.option_server_id.address = htobe32(INADDR_LOOPBACK); +        test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); + +        test.option_server_id.address = htobe32(0x12345678); +        test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.option_server_id.address = htobe32(INADDR_LOOPBACK); +        test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4); +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +        test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3); +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); + +        test.option_client_id.code = SD_DHCP_OPTION_CLIENT_IDENTIFIER; +        test.option_client_id.length = 7; +        test.option_client_id.id[0] = 0x01; +        test.option_client_id.id[1] = 'A'; +        test.option_client_id.id[2] = 'B'; +        test.option_client_id.id[3] = 'C'; +        test.option_client_id.id[4] = 'D'; +        test.option_client_id.id[5] = 'E'; +        test.option_client_id.id[6] = 'F'; +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); + +        test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); +        assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); +} + +static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) { +        struct siphash state; + +        siphash24_init(&state, key); +        client_id_hash_func(id, &state); + +        return htole64(siphash24_finalize(&state)); +} + +static void test_client_id_hash(void) { +        DHCPClientId a = { +                .length = 4, +        }, b = { +                .length = 4, +        }; +        uint8_t hash_key[HASH_KEY_SIZE] = { +                '0', '1', '2', '3', '4', '5', '6', '7', +                '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', +        }; + +        a.data = (uint8_t*)strdup("abcd"); +        b.data = (uint8_t*)strdup("abcd"); + +        assert_se(client_id_compare_func(&a, &b) == 0); +        assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); +        a.length = 3; +        assert_se(client_id_compare_func(&a, &b) != 0); +        a.length = 4; +        assert_se(client_id_compare_func(&a, &b) == 0); +        assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); + +        b.length = 3; +        assert_se(client_id_compare_func(&a, &b) != 0); +        b.length = 4; +        assert_se(client_id_compare_func(&a, &b) == 0); +        assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); + +        free(b.data); +        b.data = (uint8_t*)strdup("abce"); +        assert_se(client_id_compare_func(&a, &b) != 0); + +        free(a.data); +        free(b.data); +} + +int main(int argc, char *argv[]) { +        _cleanup_(sd_event_unrefp) sd_event *e; +        int r; + +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        assert_se(sd_event_new(&e) >= 0); + +        r = test_basic(e); +        if (r != 0) +                return r; + +        test_message_handler(); +        test_client_id_hash(); + +        return 0; +} diff --git a/src/libsystemd-network/test/test-dhcp6-client.c b/src/libsystemd-network/test/test-dhcp6-client.c new file mode 100644 index 0000000000..0548e8381e --- /dev/null +++ b/src/libsystemd-network/test/test-dhcp6-client.c @@ -0,0 +1,763 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2014 Intel Corporation. All rights reserved. + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <net/ethernet.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <systemd/sd-dhcp6-client.h> +#include <systemd/sd-event.h> + +#include "dhcp6-internal.h" +#include "dhcp6-lease-internal.h" +#include "dhcp6-protocol.h" +#include "fd-util.h" +#include "macro.h" +#include "socket-util.h" +#include "virt.h" + +static struct ether_addr mac_addr = { +        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} +}; + +static bool verbose = true; + +static sd_event_source *hangcheck; +static int test_dhcp_fd[2]; +static int test_index = 42; +static int test_client_message_num; +static be32_t test_iaid = 0; +static uint8_t test_duid[14] = { }; + +static int test_client_basic(sd_event *e) { +        sd_dhcp6_client *client; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(sd_dhcp6_client_new(&client) >= 0); +        assert_se(client); + +        assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); + +        assert_se(sd_dhcp6_client_set_index(client, 15) == 0); +        assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL); +        assert_se(sd_dhcp6_client_set_index(client, -1) == 0); +        assert_se(sd_dhcp6_client_set_index(client, 42) >= 0); + +        assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, +                                          sizeof (mac_addr), +                                          ARPHRD_ETHER) >= 0); + +        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL); +        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST); +        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST); +        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST); +        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); +        assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); + +        assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0); + +        assert_se(sd_dhcp6_client_detach_event(client) >= 0); +        assert_se(!sd_dhcp6_client_unref(client)); + +        return 0; +} + +static int test_option(sd_event *e) { +        uint8_t packet[] = { +                'F', 'O', 'O', +                0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, +                'A', 'B', 'C', 'D', 'E', 'F', 'G', +                0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, +                '1', '2', '3', '4', '5', '6', '7', '8', '9', +                'B', 'A', 'R', +        }; +        uint8_t result[] = { +                'F', 'O', 'O', +                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                'B', 'A', 'R', +        }; +        uint16_t optcode; +        size_t optlen; +        uint8_t *optval, *buf, *out; +        size_t zero = 0, pos = 3; +        size_t buflen = sizeof(packet), outlen = sizeof(result); + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(buflen == outlen); + +        assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen, +                                     &optval) == -ENOMSG); + +        buflen -= 3; +        buf = &packet[3]; +        outlen -= 3; +        out = &result[3]; + +        assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, +                                     &optval) >= 0); +        pos += 4 + optlen; +        assert_se(buf == &packet[pos]); +        assert_se(optcode == SD_DHCP6_OPTION_ORO); +        assert_se(optlen == 7); +        assert_se(buflen + pos == sizeof(packet)); + +        assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, +                                      optval) >= 0); +        assert_se(out == &result[pos]); +        assert_se(*out == 0x00); + +        assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, +                                     &optval) >= 0); +        pos += 4 + optlen; +        assert_se(buf == &packet[pos]); +        assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); +        assert_se(optlen == 9); +        assert_se(buflen + pos == sizeof(packet)); + +        assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, +                                      optval) >= 0); +        assert_se(out == &result[pos]); +        assert_se(*out == 'B'); + +        assert_se(memcmp(packet, result, sizeof(packet)) == 0); + +        return 0; +} + +static uint8_t msg_advertise[198] = { +        0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, +        0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, +        0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03, +        0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00, +        0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, +        0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, +        0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, +        0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, +        0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00, +        0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, +        0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, +        0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, +        0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, +        0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, +        0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, +        0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, +        0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, +        0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, +        0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, +        0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, +        0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19, +        0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, +        0x53, 0x00, 0x07, 0x00, 0x01, 0x00 +}; + +static uint8_t msg_reply[173] = { +        0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e, +        0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, +        0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01, +        0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, +        0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, +        0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d, +        0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, +        0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, +        0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, +        0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, +        0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e, +        0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, +        0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, +        0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, +        0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17, +        0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, +        0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +        0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c, +        0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, +        0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d, +        0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, +        0x00, 0x00, 0x00, 0x00, 0x01 +}; + +static int test_advertise_option(sd_event *e) { +        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; +        DHCP6Message *advertise = (DHCP6Message *)msg_advertise; +        uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message); +        uint16_t optcode; +        size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message); +        be32_t val; +        uint8_t preference = 255; +        struct in6_addr addr; +        uint32_t lt_pref, lt_valid; +        int r; +        bool opt_clientid = false; +        struct in6_addr *addrs; +        char **domains; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(dhcp6_lease_new(&lease) >= 0); + +        assert_se(advertise->type == DHCP6_ADVERTISE); +        assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == +                  0x0fb4e5); + +        while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen, +                                       &optval)) >= 0) { + +                switch(optcode) { +                case SD_DHCP6_OPTION_CLIENTID: +                        assert_se(optlen == 14); + +                        opt_clientid = true; +                        break; + +                case SD_DHCP6_OPTION_IA_NA: +                        assert_se(optlen == 94); +                        assert_se(!memcmp(optval, &msg_advertise[26], optlen)); + +                        val = htobe32(0x0ecfa37d); +                        assert_se(!memcmp(optval, &val, sizeof(val))); + +                        val = htobe32(80); +                        assert_se(!memcmp(optval + 4, &val, sizeof(val))); + +                        val = htobe32(120); +                        assert_se(!memcmp(optval + 8, &val, sizeof(val))); + +                        assert_se(dhcp6_option_parse_ia(&optval, &optlen, +                                                        optcode, +                                                        &lease->ia) >= 0); + +                        break; + +                case SD_DHCP6_OPTION_SERVERID: +                        assert_se(optlen == 14); +                        assert_se(!memcmp(optval, &msg_advertise[179], optlen)); + +                        assert_se(dhcp6_lease_set_serverid(lease, optval, +                                                           optlen) >= 0); +                        break; + +                case SD_DHCP6_OPTION_PREFERENCE: +                        assert_se(optlen == 1); +                        assert_se(!*optval); + +                        assert_se(dhcp6_lease_set_preference(lease, +                                                             *optval) >= 0); +                        break; + +                case SD_DHCP6_OPTION_ELAPSED_TIME: +                        assert_se(optlen == 2); + +                        break; + +                case SD_DHCP6_OPTION_DNS_SERVERS: +                        assert_se(optlen == 16); +                        assert_se(dhcp6_lease_set_dns(lease, optval, +                                                      optlen) >= 0); +                        break; + +                case SD_DHCP6_OPTION_DOMAIN_LIST: +                        assert_se(optlen == 11); +                        assert_se(dhcp6_lease_set_domains(lease, optval, +                                                          optlen) >= 0); +                        break; + +                case SD_DHCP6_OPTION_SNTP_SERVERS: +                        assert_se(optlen == 16); +                        assert_se(dhcp6_lease_set_sntp(lease, optval, +                                                       optlen) >= 0); +                        break; + +                default: +                        break; +                } +        } + + +        assert_se(r == -ENOMSG); + +        assert_se(opt_clientid); + +        sd_dhcp6_lease_reset_address_iter(lease); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) >= 0); +        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); +        assert_se(lt_pref == 150); +        assert_se(lt_valid == 180); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) == -ENOMSG); + +        sd_dhcp6_lease_reset_address_iter(lease); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) >= 0); +        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) == -ENOMSG); +        sd_dhcp6_lease_reset_address_iter(lease); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) >= 0); +        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) == -ENOMSG); + +        assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0); +        assert_se(len == 14); +        assert_se(!memcmp(opt, &msg_advertise[179], len)); + +        assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); +        assert_se(preference == 0); + +        r = sd_dhcp6_lease_get_dns(lease, &addrs); +        assert_se(r == 1); +        assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); + +        r = sd_dhcp6_lease_get_domains(lease, &domains); +        assert_se(r == 1); +        assert_se(!strcmp("lab.intra", domains[0])); +        assert_se(domains[1] == NULL); + +        r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); +        assert_se(r == 1); +        assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); + +        return 0; +} + +static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { +        assert_not_reached("Test case should have completed in 2 seconds"); + +        return 0; +} + +static void test_client_solicit_cb(sd_dhcp6_client *client, int event, +                                   void *userdata) { +        sd_event *e = userdata; +        sd_dhcp6_lease *lease; +        struct in6_addr *addrs; +        char **domains; + +        assert_se(e); +        assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + +        assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + +        assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); +        assert_se(!strcmp("lab.intra", domains[0])); +        assert_se(domains[1] == NULL); + +        assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); +        assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + +        assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); +        assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + +        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); + +        if (verbose) +                printf("  got DHCPv6 event %d\n", event); + +        sd_event_exit(e, 0); +} + +static int test_client_send_reply(DHCP6Message *request) { +        DHCP6Message reply; + +        reply.transaction_id = request->transaction_id; +        reply.type = DHCP6_REPLY; + +        memcpy(msg_reply, &reply.transaction_id, 4); + +        memcpy(&msg_reply[26], test_duid, sizeof(test_duid)); + +        memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid)); + +        assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply)) +                  == sizeof(msg_reply)); + +        return 0; +} + +static int test_client_verify_request(DHCP6Message *request, uint8_t *option, +                                      size_t len) { +        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; +        uint8_t *optval; +        uint16_t optcode; +        size_t optlen; +        bool found_clientid = false, found_iana = false, found_serverid = false, +                found_elapsed_time = false; +        int r; +        struct in6_addr addr; +        be32_t val; +        uint32_t lt_pref, lt_valid; + +        assert_se(request->type == DHCP6_REQUEST); + +        assert_se(dhcp6_lease_new(&lease) >= 0); + +        while ((r = dhcp6_option_parse(&option, &len, +                                       &optcode, &optlen, &optval)) >= 0) { +                switch(optcode) { +                case SD_DHCP6_OPTION_CLIENTID: +                        assert_se(!found_clientid); +                        found_clientid = true; + +                        assert_se(!memcmp(optval, &test_duid, +                                          sizeof(test_duid))); + +                        break; + +                case SD_DHCP6_OPTION_IA_NA: +                        assert_se(!found_iana); +                        found_iana = true; + + +                        assert_se(optlen == 40); +                        assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid))); + +                        val = htobe32(80); +                        assert_se(!memcmp(optval + 4, &val, sizeof(val))); + +                        val = htobe32(120); +                        assert_se(!memcmp(optval + 8, &val, sizeof(val))); + +                        assert_se(!dhcp6_option_parse_ia(&optval, &optlen, +                                                         optcode, &lease->ia)); + +                        break; + +                case SD_DHCP6_OPTION_SERVERID: +                        assert_se(!found_serverid); +                        found_serverid = true; + +                        assert_se(optlen == 14); +                        assert_se(!memcmp(&msg_advertise[179], optval, optlen)); + +                        break; + +                case SD_DHCP6_OPTION_ELAPSED_TIME: +                        assert_se(!found_elapsed_time); +                        found_elapsed_time = true; + +                        assert_se(optlen == 2); + +                        break; +                } +        } + +        assert_se(r == -ENOMSG); +        assert_se(found_clientid && found_iana && found_serverid && +                  found_elapsed_time); + +        sd_dhcp6_lease_reset_address_iter(lease); +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) >= 0); +        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); +        assert_se(lt_pref == 150); +        assert_se(lt_valid == 180); + +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) == -ENOMSG); + +        return 0; +} + +static int test_client_send_advertise(DHCP6Message *solicit) { +        DHCP6Message advertise; + +        advertise.transaction_id = solicit->transaction_id; +        advertise.type = DHCP6_ADVERTISE; + +        memcpy(msg_advertise, &advertise.transaction_id, 4); + +        memcpy(&msg_advertise[8], test_duid, sizeof(test_duid)); + +        memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid)); + +        assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise)) +                  == sizeof(msg_advertise)); + +        return 0; +} + +static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, +                                      size_t len) { +        uint8_t *optval; +        uint16_t optcode; +        size_t optlen; +        bool found_clientid = false, found_iana = false, +                found_elapsed_time = false; +        int r; + +        assert_se(solicit->type == DHCP6_SOLICIT); + +        while ((r = dhcp6_option_parse(&option, &len, +                                       &optcode, &optlen, &optval)) >= 0) { +                switch(optcode) { +                case SD_DHCP6_OPTION_CLIENTID: +                        assert_se(!found_clientid); +                        found_clientid = true; + +                        assert_se(optlen == sizeof(test_duid)); +                        memcpy(&test_duid, optval, sizeof(test_duid)); + +                        break; + +                case SD_DHCP6_OPTION_IA_NA: +                        assert_se(!found_iana); +                        found_iana = true; + +                        assert_se(optlen == 12); + +                        memcpy(&test_iaid, optval, sizeof(test_iaid)); + +                        break; + +                case SD_DHCP6_OPTION_ELAPSED_TIME: +                        assert_se(!found_elapsed_time); +                        found_elapsed_time = true; + +                        assert_se(optlen == 2); + +                        break; +                } +        } + +        assert_se(r == -ENOMSG); +        assert_se(found_clientid && found_iana && found_elapsed_time); + +        return 0; +} + +static void test_client_information_cb(sd_dhcp6_client *client, int event, +                                       void *userdata) { +        sd_event *e = userdata; +        sd_dhcp6_lease *lease; +        struct in6_addr *addrs; +        struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; +        char **domains; + +        assert_se(e); +        assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); + +        assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + +        assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); +        assert_se(!strcmp("lab.intra", domains[0])); +        assert_se(domains[1] == NULL); + +        assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); +        assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + +        assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); +        assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + +        if (verbose) +                printf("  got DHCPv6 event %d\n", event); + +        assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); +        assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); +        assert_se(sd_dhcp6_client_stop(client) >= 0); +        assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); + +        assert_se(sd_dhcp6_client_set_callback(client, +                                               test_client_solicit_cb, e) >= 0); + +        assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + +        assert_se(sd_dhcp6_client_start(client) >= 0); +} + +static int test_client_verify_information_request(DHCP6Message *information_request, +                                                  uint8_t *option, size_t len) { + +        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; +        uint8_t *optval; +        uint16_t optcode; +        size_t optlen; +        bool found_clientid = false, found_elapsed_time = false; +        int r; +        struct in6_addr addr; +        uint32_t lt_pref, lt_valid; + +        assert_se(information_request->type == DHCP6_INFORMATION_REQUEST); + +        assert_se(dhcp6_lease_new(&lease) >= 0); + +        while ((r = dhcp6_option_parse(&option, &len, +                                       &optcode, &optlen, &optval)) >= 0) { +                switch(optcode) { +                case SD_DHCP6_OPTION_CLIENTID: +                        assert_se(!found_clientid); +                        found_clientid = true; + +                        assert_se(optlen == sizeof(test_duid)); +                        memcpy(&test_duid, optval, sizeof(test_duid)); + +                        break; + +                case SD_DHCP6_OPTION_IA_NA: +                        assert_not_reached("IA TA option must not be present"); + +                        break; + +                case SD_DHCP6_OPTION_SERVERID: +                        assert_not_reached("Server ID option must not be present"); + +                        break; + +                case SD_DHCP6_OPTION_ELAPSED_TIME: +                        assert_se(!found_elapsed_time); +                        found_elapsed_time = true; + +                        assert_se(optlen == 2); + +                        break; +                } +        } + +        assert_se(r == -ENOMSG); +        assert_se(found_clientid && found_elapsed_time); + +        sd_dhcp6_lease_reset_address_iter(lease); + +        assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, +                                             <_valid) == -ENOMSG); + +        return 0; +} + +int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, +                                  const void *packet, size_t len) { +        struct in6_addr mcast = +                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; +        DHCP6Message *message; +        uint8_t *option; + +        assert_se(s == test_dhcp_fd[0]); +        assert_se(server_address); +        assert_se(packet); +        assert_se(len > sizeof(DHCP6Message) + 4); + +        assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); + +        message = (DHCP6Message *)packet; +        option = (uint8_t *)(message + 1); +        len -= sizeof(DHCP6Message); + +        assert_se(message->transaction_id & 0x00ffffff); + +        if (test_client_message_num == 0) { +                test_client_verify_information_request(message, option, len); +                test_client_send_reply(message); +                test_client_message_num++; +        } else if (test_client_message_num == 1) { +                test_client_verify_solicit(message, option, len); +                test_client_send_advertise(message); +                test_client_message_num++; +        } else if (test_client_message_num == 2) { +                test_client_verify_request(message, option, len); +                test_client_send_reply(message); +                test_client_message_num++; +        } + +        return len; +} + +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { +        assert_se(index == test_index); + +        if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0) +                return -errno; + +        return test_dhcp_fd[0]; +} + +static int test_client_solicit(sd_event *e) { +        sd_dhcp6_client *client; +        usec_t time_now = now(clock_boottime_or_monotonic()); +        struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; +        int val = true; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(sd_dhcp6_client_new(&client) >= 0); +        assert_se(client); + +        assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); + +        assert_se(sd_dhcp6_client_set_index(client, test_index) == 0); +        assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, +                                          sizeof (mac_addr), +                                          ARPHRD_ETHER) >= 0); + +        assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); +        assert_se(val == false); +        assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0); +        assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); +        assert_se(val == true); + +        assert_se(sd_dhcp6_client_set_callback(client, +                                               test_client_information_cb, e) >= 0); + +        assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(), +                                    time_now + 2 * USEC_PER_SEC, 0, +                                    test_hangcheck, NULL) >= 0); + +        assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + +        assert_se(sd_dhcp6_client_start(client) >= 0); + +        sd_event_loop(e); + +        hangcheck = sd_event_source_unref(hangcheck); + +        assert_se(!sd_dhcp6_client_unref(client)); + +        test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]); + +        return 0; +} + +int main(int argc, char *argv[]) { +        _cleanup_(sd_event_unrefp) sd_event *e; + +        assert_se(sd_event_new(&e) >= 0); + +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        test_client_basic(e); +        test_option(e); +        test_advertise_option(e); +        test_client_solicit(e); + +        return 0; +} diff --git a/src/libsystemd-network/test/test-ipv4ll-manual.c b/src/libsystemd-network/test/test-ipv4ll-manual.c new file mode 100644 index 0000000000..caa315b210 --- /dev/null +++ b/src/libsystemd-network/test/test-ipv4ll-manual.c @@ -0,0 +1,128 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <net/if.h> +#include <stdlib.h> +#include <unistd.h> +#include <linux/veth.h> + +#include <systemd/sd-event.h> +#include <systemd/sd-ipv4ll.h> +#include <systemd/sd-netlink.h> + +#include "alloc-util.h" +#include "in-addr-util.h" +#include "netlink-util.h" +#include "parse-util.h" +#include "string-util.h" +#include "util.h" + +static void ll_handler(sd_ipv4ll *ll, int event, void *userdata) { +        _cleanup_free_ char *address = NULL; +        struct in_addr addr = {}; + +        assert_se(ll); + +        if (sd_ipv4ll_get_address(ll, &addr) >= 0) +                assert_se(in_addr_to_string(AF_INET, (const union in_addr_union*) &addr, &address) >= 0); + +        switch (event) { +        case SD_IPV4LL_EVENT_BIND: +                log_info("bound %s", strna(address)); +                break; +        case SD_IPV4LL_EVENT_CONFLICT: +                log_info("conflict on %s", strna(address)); +                break; +        case SD_IPV4LL_EVENT_STOP: +                log_error("the client was stopped with address %s", strna(address)); +                break; +        default: +                assert_not_reached("invalid LL event"); +        } +} + +static int client_run(int ifindex, const char *seed_str, const struct ether_addr *ha, sd_event *e) { +        sd_ipv4ll *ll; + +        assert_se(sd_ipv4ll_new(&ll) >= 0); +        assert_se(sd_ipv4ll_attach_event(ll, e, 0) >= 0); + +        assert_se(sd_ipv4ll_set_index(ll, ifindex) >= 0); +        assert_se(sd_ipv4ll_set_mac(ll, ha) >= 0); +        assert_se(sd_ipv4ll_set_callback(ll, ll_handler, NULL) >= 0); + +        if (seed_str) { +                unsigned seed; + +                assert_se(safe_atou(seed_str, &seed) >= 0); + +                assert_se(sd_ipv4ll_set_address_seed(ll, seed) >= 0); +        } + +        log_info("starting IPv4LL client"); + +        assert_se(sd_ipv4ll_start(ll) >= 0); + +        assert_se(sd_event_loop(e) >= 0); + +        assert_se(!sd_ipv4ll_unref(ll)); + +        return EXIT_SUCCESS; +} + +static int test_ll(const char *ifname, const char *seed) { +        _cleanup_(sd_event_unrefp) sd_event *e = NULL; +        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; +        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; +        struct ether_addr ha; +        int ifindex; + +        assert_se(sd_event_new(&e) >= 0); + +        assert_se(sd_netlink_open(&rtnl) >= 0); +        assert_se(sd_netlink_attach_event(rtnl, e, 0) >= 0); + +        assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, 0) >= 0); +        assert_se(sd_netlink_message_append_string(m, IFLA_IFNAME, ifname) >= 0); +        assert_se(sd_netlink_call(rtnl, m, 0, &reply) >= 0); + +        assert_se(sd_rtnl_message_link_get_ifindex(reply, &ifindex) >= 0); +        assert_se(sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &ha) >= 0); + +        client_run(ifindex, seed, &ha, e); + +        return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        if (argc == 2) +                return test_ll(argv[1], NULL); +        else if (argc == 3) +                return test_ll(argv[1], argv[2]); +        else { +                log_error("This program takes one or two arguments.\n" +                          "\t %s <ifname> [<seed>]", program_invocation_short_name); +                return EXIT_FAILURE; +        } +} diff --git a/src/libsystemd-network/test/test-ipv4ll.c b/src/libsystemd-network/test/test-ipv4ll.c new file mode 100644 index 0000000000..b7278834f2 --- /dev/null +++ b/src/libsystemd-network/test/test-ipv4ll.c @@ -0,0 +1,220 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2014 Axis Communications AB. All rights reserved. + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <systemd/sd-ipv4ll.h> + +#include "arp-util.h" +#include "fd-util.h" +#include "socket-util.h" +#include "util.h" + +static bool verbose = false; +static bool extended = false; +static int test_fd[2]; + +static int basic_request_handler_bind = 0; +static int basic_request_handler_stop = 0; +static void* basic_request_handler_userdata = (void*)0xCABCAB; +static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { +        assert_se(userdata == basic_request_handler_userdata); + +        switch(event) { +                case SD_IPV4LL_EVENT_STOP: +                        basic_request_handler_stop = 1; +                        break; +                case SD_IPV4LL_EVENT_BIND: +                        basic_request_handler_bind = 1; +                        break; +                default: +                        assert_se(0); +                        break; +        } +} + +static int arp_network_send_raw_socket(int fd, int ifindex, +                                       const struct ether_arp *arp) { +        assert_se(arp); +        assert_se(ifindex > 0); +        assert_se(fd >= 0); + +        if (send(fd, arp, sizeof(struct ether_arp), 0) < 0) +                return -errno; + +        return 0; +} + +int arp_send_probe(int fd, int ifindex, +                    be32_t pa, const struct ether_addr *ha) { +        struct ether_arp ea = {}; + +        assert(fd >= 0); +        assert(ifindex > 0); +        assert(pa != 0); +        assert(ha); + +        return arp_network_send_raw_socket(fd, ifindex, &ea); +} + +int arp_send_announcement(int fd, int ifindex, +                          be32_t pa, const struct ether_addr *ha) { +        struct ether_arp ea = {}; + +        assert(fd >= 0); +        assert(ifindex > 0); +        assert(pa != 0); +        assert(ha); + +        return arp_network_send_raw_socket(fd, ifindex, &ea); +} + +int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) { +        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) +                return -errno; + +        return test_fd[0]; +} + +static void test_public_api_setters(sd_event *e) { +        struct in_addr address = {}; +        unsigned seed = 0; +        sd_ipv4ll *ll; +        struct ether_addr mac_addr = { +                .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(sd_ipv4ll_new(&ll) == 0); +        assert_se(ll); + +        assert_se(sd_ipv4ll_attach_event(NULL, NULL, 0) == -EINVAL); +        assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0); +        assert_se(sd_ipv4ll_attach_event(ll, e, 0) == -EBUSY); + +        assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL); +        assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0); + +        assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); +        address.s_addr |= htobe32(169U << 24 | 254U << 16); +        assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); +        address.s_addr |= htobe32(0x00FF); +        assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); +        address.s_addr |= htobe32(0xF000); +        assert_se(sd_ipv4ll_set_address(ll, &address) == 0); +        address.s_addr |= htobe32(0x0F00); +        assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL); + +        assert_se(sd_ipv4ll_set_address_seed(NULL, seed) == -EINVAL); +        assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0); + +        assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL); +        assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL); +        assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0); + +        assert_se(sd_ipv4ll_set_index(NULL, -1) == -EINVAL); +        assert_se(sd_ipv4ll_set_index(ll, -1) == -EINVAL); +        assert_se(sd_ipv4ll_set_index(ll, -99) == -EINVAL); +        assert_se(sd_ipv4ll_set_index(ll, 1) == 0); +        assert_se(sd_ipv4ll_set_index(ll, 99) == 0); + +        assert_se(sd_ipv4ll_ref(ll) == ll); +        assert_se(sd_ipv4ll_unref(ll) == NULL); + +        /* Cleanup */ +        assert_se(sd_ipv4ll_unref(ll) == NULL); +} + +static void test_basic_request(sd_event *e) { + +        sd_ipv4ll *ll; +        struct ether_arp arp; +        struct ether_addr mac_addr = { +                .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}}; + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        assert_se(sd_ipv4ll_new(&ll) == 0); +        assert_se(sd_ipv4ll_start(ll) == -EINVAL); + +        assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0); +        assert_se(sd_ipv4ll_start(ll) == -EINVAL); + +        assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0); +        assert_se(sd_ipv4ll_start(ll) == -EINVAL); + +        assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler, +                                         basic_request_handler_userdata) == 0); +        assert_se(sd_ipv4ll_start(ll) == -EINVAL); + +        assert_se(sd_ipv4ll_set_index(ll, 1) == 0); +        assert_se(sd_ipv4ll_start(ll) == 0); + +        sd_event_run(e, (uint64_t) -1); +        assert_se(sd_ipv4ll_start(ll) == -EBUSY); + +        assert_se(sd_ipv4ll_is_running(ll)); + +        /* PROBE */ +        sd_event_run(e, (uint64_t) -1); +        assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); + +        if (extended) { +                /* PROBE */ +                sd_event_run(e, (uint64_t) -1); +                assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); + +                /* PROBE */ +                sd_event_run(e, (uint64_t) -1); +                assert_se(read(test_fd[1], &arp, sizeof(struct ether_arp)) == sizeof(struct ether_arp)); + +                sd_event_run(e, (uint64_t) -1); +                assert_se(basic_request_handler_bind == 1); +        } + +        sd_ipv4ll_stop(ll); +        assert_se(basic_request_handler_stop == 1); + +        /* Cleanup */ +        assert_se(sd_ipv4ll_unref(ll) == NULL); +        safe_close(test_fd[1]); +} + +int main(int argc, char *argv[]) { +        _cleanup_(sd_event_unrefp) sd_event *e = NULL; + +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        assert_se(sd_event_new(&e) >= 0); + +        test_public_api_setters(e); +        test_basic_request(e); + +        return 0; +} diff --git a/src/libsystemd-network/test/test-lldp.c b/src/libsystemd-network/test/test-lldp.c new file mode 100644 index 0000000000..8c6d214d6f --- /dev/null +++ b/src/libsystemd-network/test/test-lldp.c @@ -0,0 +1,257 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2014 Tom Gundersen +  Copyright (C) 2014 Susant Sahani + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <arpa/inet.h> +#include <net/ethernet.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <systemd/sd-event.h> +#include <systemd/sd-lldp.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "lldp-network.h" +#include "macro.h" +#include "string-util.h" + +#define TEST_LLDP_PORT "em1" +#define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp" +#define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc" + +static int test_fd[2] = { -1, -1 }; +static int lldp_handler_calls; + +int lldp_network_bind_raw_socket(int ifindex) { +        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) +                return -errno; + +        return test_fd[0]; +} + +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { +        lldp_handler_calls++; +} + +static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { +        int r; + +        r = sd_lldp_new(lldp, 42); +        if (r < 0) +                return r; + +        r = sd_lldp_attach_event(*lldp, e, 0); +        if (r < 0) +                return r; + +        r = sd_lldp_set_callback(*lldp, cb, cb_data); +        if (r < 0) +                return r; + +        r = sd_lldp_start(*lldp); +        if (r < 0) +                return r; + +        return 0; +} + +static int stop_lldp(sd_lldp *lldp) { +        int r; + +        r = sd_lldp_stop(lldp); +        if (r < 0) +                return r; + +        r = sd_lldp_detach_event(lldp); +        if (r < 0) +                return r; + +        sd_lldp_unref(lldp); +        safe_close(test_fd[1]); + +        return 0; +} + +static void test_receive_basic_packet(sd_event *e) { + +        static const uint8_t frame[] = { +                /* Ethernet header */ +                0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC*/ +                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */ +                0x88, 0xcc,                             /* Ethertype */ +                /* LLDP mandatory TLVs */ +                0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */ +                0x03, 0x04, 0x05, +                0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */ +                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds*/ +                /* LLDP optional TLVs */ +                0x08, 0x04, 0x50, 0x6f, 0x72, 0x74,     /* Port Description: "Port" */ +                0x0a, 0x03, 0x53, 0x59, 0x53,           /* System Name: "SYS" */ +                0x0c, 0x04, 0x66, 0x6f, 0x6f, 0x00,     /* System Description: "foo" (NULL-terminated) */ +                0x00, 0x00                              /* End Of LLDPDU */ +        }; + +        sd_lldp *lldp; +        sd_lldp_neighbor **neighbors; +        uint8_t type; +        const void *data; +        uint16_t ttl; +        size_t length; +        const char *str; + +        lldp_handler_calls = 0; +        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + +        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); +        sd_event_run(e, 0); +        assert_se(lldp_handler_calls == 1); +        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); + +        assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0); +        assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS); +        assert_se(length == ETH_ALEN); +        assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN)); + +        assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0); +        assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME); +        assert_se(length == 3); +        assert_se(strneq((char *) data, "1/3", 3)); + +        assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0); +        assert_se(streq(str, "Port")); + +        assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0); +        assert_se(streq(str, "SYS")); + +        assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0); +        assert_se(streq(str, "foo")); + +        assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0); +        assert_se(ttl == 120); + +        sd_lldp_neighbor_unref(neighbors[0]); +        free(neighbors); + +        assert_se(stop_lldp(lldp) == 0); +} + +static void test_receive_incomplete_packet(sd_event *e) { +        sd_lldp *lldp; +        sd_lldp_neighbor **neighbors; +        uint8_t frame[] = { +                /* Ethernet header */ +                0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC*/ +                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */ +                0x88, 0xcc,                             /* Ethertype */ +                /* LLDP mandatory TLVs */ +                0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */ +                0x03, 0x04, 0x05, +                0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port: interface name, "1/3" */ +                                                        /* Missing TTL */ +                0x00, 0x00                              /* End Of LLDPDU */ +        }; + +        lldp_handler_calls = 0; +        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + +        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); +        sd_event_run(e, 0); +        assert_se(lldp_handler_calls == 0); +        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0); + +        assert_se(stop_lldp(lldp) == 0); +} + +static void test_receive_oui_packet(sd_event *e) { +        sd_lldp *lldp; +        sd_lldp_neighbor **neighbors; +        uint8_t frame[] = { +                /* Ethernet header */ +                0x01, 0x80, 0xc2, 0x00, 0x00, 0x03,     /* Destination MAC*/ +                0x01, 0x02, 0x03, 0x04, 0x05, 0x06,     /* Source MAC */ +                0x88, 0xcc,                             /* Ethertype */ +                /* LLDP mandatory TLVs */ +                0x02, 0x07, 0x04, 0x00, 0x01, 0x02,     /* Chassis: MAC, 00:01:02:03:04:05 */ +                0x03, 0x04, 0x05, +                0x04, 0x04, 0x05, 0x31, 0x2f, 0x33,     /* Port TLV: interface name, "1/3" */ +                0x06, 0x02, 0x00, 0x78,                 /* TTL: 120 seconds*/ +                /* LLDP optional TLVs */ +                0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01,     /* Port VLAN ID: 0x1234 */ +                0x12, 0x34, +                0xfe, 0x07, 0x00, 0x80, 0xc2, 0x02,     /* Port and protocol: flag 1, PPVID 0x7788 */ +                0x01, 0x77, 0x88, +                0xfe, 0x0d, 0x00, 0x80, 0xc2, 0x03,     /* VLAN Name: ID 0x1234, name "Vlan51" */ +                0x12, 0x34, 0x06, 0x56, 0x6c, 0x61, +                0x6e, 0x35, 0x31, +                0xfe, 0x06, 0x00, 0x80, 0xc2, 0x06,     /* Management VID: 0x0102 */ +                0x01, 0x02, +                0xfe, 0x09, 0x00, 0x80, 0xc2, 0x07,     /* Link aggregation: status 1, ID 0x00140012 */ +                0x01, 0x00, 0x14, 0x00, 0x12, +                0x00, 0x00                              /* End of LLDPDU */ +        }; + +        lldp_handler_calls = 0; +        assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); + +        assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); +        sd_event_run(e, 0); +        assert_se(lldp_handler_calls == 1); +        assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); + +        assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0); +        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); +        assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0); +        assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0); + +        sd_lldp_neighbor_unref(neighbors[0]); +        free(neighbors); + +        assert_se(stop_lldp(lldp) == 0); +} + +int main(int argc, char *argv[]) { +        _cleanup_(sd_event_unrefp) sd_event *e = NULL; + +        log_set_max_level(LOG_DEBUG); + +        /* LLDP reception tests */ +        assert_se(sd_event_new(&e) == 0); +        test_receive_basic_packet(e); +        test_receive_incomplete_packet(e); +        test_receive_oui_packet(e); + +        return 0; +} diff --git a/src/libsystemd-network/test/test-ndisc-rs.c b/src/libsystemd-network/test/test-ndisc-rs.c new file mode 100644 index 0000000000..863a76637c --- /dev/null +++ b/src/libsystemd-network/test/test-ndisc-rs.c @@ -0,0 +1,168 @@ +/*** +  This file is part of systemd. + +  Copyright (C) 2014 Intel Corporation. All rights reserved. + +  systemd 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. + +  systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <netinet/icmp6.h> + +#include <systemd/sd-ndisc.h> + +#include "icmp6-util.h" +#include "socket-util.h" + +static struct ether_addr mac_addr = { +        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} +}; + +static bool verbose = false; +static sd_event_source *test_hangcheck; +static int test_fd[2]; + +typedef int (*send_ra_t)(uint8_t flags); +static send_ra_t send_ra_function; + +static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, +                             void *userdata) { +        assert_se(false); + +        return 0; +} + +int icmp6_bind_router_solicitation(int index) { +        assert_se(index == 42); + +        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0) +                return -errno; + +        return test_fd[0]; +} + +static int send_ra(uint8_t flags) { +        uint8_t advertisement[] = { +                0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, +                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, +                0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, +                0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, +                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, +                0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, +                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +                0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, +                0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, +                0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +                0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, +        }; + +        advertisement[5] = flags; + +        assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == +               sizeof(advertisement)); + +        if (verbose) +                printf("  sent RA with flag 0x%02x\n", flags); + +        return 0; +} + +int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { +        return send_ra_function(0); +} + +static void test_rs_done(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { +        sd_event *e = userdata; +        static unsigned idx = 0; +        uint8_t flags_array[] = { +                0, +                0, +                0, +                ND_RA_FLAG_OTHER, +                ND_RA_FLAG_MANAGED +        }; +        uint32_t mtu; + +        assert_se(nd); + +        assert_se(flags == flags_array[idx]); +        idx++; + +        if (verbose) +                printf("  got event 0x%02x\n", flags); + +        if (idx < ELEMENTSOF(flags_array)) { +                send_ra(flags_array[idx]); +                return; +        } + +        assert_se(sd_ndisc_get_mtu(nd, &mtu) == -ENOMSG); + +        sd_event_exit(e, 0); +} + +static void test_rs(void) { +        sd_event *e; +        sd_ndisc *nd; +        usec_t time_now = now(clock_boottime_or_monotonic()); + +        if (verbose) +                printf("* %s\n", __FUNCTION__); + +        send_ra_function = send_ra; + +        assert_se(sd_event_new(&e) >= 0); + +        assert_se(sd_ndisc_new(&nd) >= 0); +        assert_se(nd); + +        assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); + +        assert_se(sd_ndisc_set_index(nd, 42) >= 0); +        assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); +        assert_se(sd_ndisc_set_callback(nd, test_rs_done, NULL, NULL, NULL, e) >= 0); + +        assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), +                                 time_now + 2 *USEC_PER_SEC, 0, +                                 test_rs_hangcheck, NULL) >= 0); + +        assert_se(sd_ndisc_stop(nd) >= 0); +        assert_se(sd_ndisc_router_discovery_start(nd) >= 0); +        assert_se(sd_ndisc_stop(nd) >= 0); + +        assert_se(sd_ndisc_router_discovery_start(nd) >= 0); + +        sd_event_loop(e); + +        test_hangcheck = sd_event_source_unref(test_hangcheck); + +        nd = sd_ndisc_unref(nd); +        assert_se(!nd); + +        close(test_fd[1]); + +        sd_event_unref(e); +} + +int main(int argc, char *argv[]) { + +        log_set_max_level(LOG_DEBUG); +        log_parse_environment(); +        log_open(); + +        test_rs(); + +        return 0; +} | 
