Index: head/tests/sys/net/Makefile =================================================================== --- head/tests/sys/net/Makefile +++ head/tests/sys/net/Makefile @@ -10,6 +10,8 @@ ATF_TESTS_SH+= if_tun_test ATF_TESTS_SH+= if_vlan +TESTS_SUBDIRS+= routing + # The tests are written to be run in parallel, but doing so leads to random # panics. I think it's because the kernel's list of interfaces isn't properly # locked. Index: head/tests/sys/net/routing/Makefile =================================================================== --- head/tests/sys/net/routing/Makefile +++ head/tests/sys/net/routing/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/sys/net + +ATF_TESTS_C += test_rtsock_l3 +ATF_TESTS_C += test_rtsock_lladdr + +# Most of the tests operates on a common IPv4/IPv6 prefix, +# so running them in parallel will lead to weird results. +TEST_METADATA+= is_exclusive=true + +.include Index: head/tests/sys/net/routing/rtsock_common.h =================================================================== --- head/tests/sys/net/routing/rtsock_common.h +++ head/tests/sys/net/routing/rtsock_common.h @@ -0,0 +1,766 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NET_ROUTING_RTSOCK_COMMON_H_ +#define _NET_ROUTING_RTSOCK_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "rtsock_print.h" + +void rtsock_update_rtm_len(struct rt_msghdr *rtm); +void rtsock_validate_message(char *buffer, ssize_t len); +void rtsock_add_rtm_sa(struct rt_msghdr *rtm, int addr_type, struct sockaddr *sa); + +static int _rtm_seq = 42; + + +/* + * Checks if the interface cloner module is present for @name. + */ +static int +_check_cloner(char *name) +{ + struct if_clonereq ifcr; + char *cp, *buf; + int idx; + int s; + int found = 0; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s == -1) + err(1, "socket(AF_LOCAL,SOCK_DGRAM)"); + + memset(&ifcr, 0, sizeof(ifcr)); + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for count"); + + buf = malloc(ifcr.ifcr_total * IFNAMSIZ); + if (buf == NULL) + err(1, "unable to allocate cloner name buffer"); + + ifcr.ifcr_count = ifcr.ifcr_total; + ifcr.ifcr_buffer = buf; + + if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0) + err(1, "SIOCIFGCLONERS for names"); + + /* + * In case some disappeared in the mean time, clamp it down. + */ + if (ifcr.ifcr_count > ifcr.ifcr_total) + ifcr.ifcr_count = ifcr.ifcr_total; + + for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) { + if (!strcmp(cp, name)) { + found = 1; + break; + } + } + + free(buf); + close(s); + + return (found); +} + +/* + * Tries to ensure if_tap is loaded. + * Checks list of interface cloners first, then tries + * to load the module. + * + * return nonzero on success. + */ +static int +_enforce_cloner_loaded(char *cloner_name) +{ + if (_check_cloner(cloner_name)) + return (1); + /* need to load */ + RLOG("trying to load %s driver", cloner_name); + + char cmd[64]; + + snprintf(cmd, sizeof(cmd), "/sbin/kldload if_%s", cloner_name); + int ret = system(cmd); + if (ret != 0) { + RLOG("'%s' failed, error %d", cmd, ret); + return (0); + } + + return (1); +} + +static int +iface_create_cloned(char *ifname_ptr) +{ + struct ifreq ifr; + int s; + char prefix[IFNAMSIZ]; + + char *src, *dst; + for (src = ifname_ptr, dst = prefix; *src && isalpha(*src); src++) + *dst++ = *src; + *dst = '\0'; + + if (_enforce_cloner_loaded(prefix) == 0) + return (0); + + memset(&ifr, 0, sizeof(struct ifreq)); + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + strlcpy(ifr.ifr_name, ifname_ptr, sizeof(ifr.ifr_name)); + + RLOG("creating iface %s %s", prefix, ifr.ifr_name); + if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) + err(1, "SIOCIFCREATE2"); + + strlcpy(ifname_ptr, ifr.ifr_name, IFNAMSIZ); + RLOG("created interface %s", ifname_ptr); + + return (1); +} + +static int +iface_destroy(char *ifname) +{ + struct ifreq ifr; + int s; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + RLOG("destroying interface %s", ifname); + if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) + return (0); + + return (1); +} + +/* + * Open tunneling device such as tuntap and returns fd. + */ +int +iface_open(char *ifname) +{ + char path[256]; + + snprintf(path, sizeof(path), "/dev/%s", ifname); + + RLOG("opening interface %s", ifname); + int fd = open(path, O_RDWR|O_EXCL); + if (fd == -1) { + RLOG_ERRNO("unable to open interface %s", ifname); + return (-1); + } + + return (fd); +} + +/* + * Sets primary IPv4 addr. + * Returns 0 on success. + */ +inline int +iface_setup_addr(char *ifname, char *addr, int plen) +{ + char cmd[512]; + char *af; + + if (strchr(addr, ':')) + af = "inet6"; + else + af = "inet"; + RLOG("setting af_%s %s/%d on %s", af, addr, plen, ifname); + snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s %s %s/%d", ifname, + af, addr, plen); + + return system(cmd); +} + +/* + * Removes primary IPv4 prefix. + * Returns 0 on success. + */ +inline int +iface_delete_addr(char *ifname, char *addr) +{ + char cmd[512]; + + if (strchr(addr, ':')) { + RLOG("removing IPv6 %s from %s", addr, ifname); + snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s inet6 %s delete", ifname, addr); + } else { + RLOG("removing IPv4 %s from %s", addr, ifname); + snprintf(cmd, sizeof(cmd), "/sbin/ifconfig %s -alias %s", ifname, addr); + } + + return system(cmd); +} + +int +iface_turn_up(char *ifname) +{ + struct ifreq ifr; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + RLOG_ERRNO("socket"); + return (-1); + } + memset(&ifr, 0, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + RLOG_ERRNO("ioctl(SIOCGIFFLAGS)"); + return (-1); + } + /* Update flags */ + if ((ifr.ifr_flags & IFF_UP) == 0) { + ifr.ifr_flags |= IFF_UP; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { + RLOG_ERRNO("ioctl(SIOSGIFFLAGS)"); + return (-1); + } + RLOG("turned interface %s up", ifname); + } + + return (0); +} + +/* + * Removes ND6_IFF_IFDISABLED from IPv6 interface flags. + * Returns 0 on success. + */ +int +iface_enable_ipv6(char *ifname) +{ + struct in6_ndireq nd; + int s; + + if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + err(1, "socket"); + } + memset(&nd, 0, sizeof(nd)); + strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); + if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) { + RLOG_ERRNO("ioctl(SIOCGIFINFO_IN6)"); + return (-1); + } + /* Update flags */ + if ((nd.ndi.flags & ND6_IFF_IFDISABLED) != 0) { + nd.ndi.flags &= ~ND6_IFF_IFDISABLED; + if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) { + RLOG_ERRNO("ioctl(SIOCSIFINFO_IN6)"); + return (-1); + } + RLOG("enabled IPv6 for %s", ifname); + } + + return (0); +} + +#define SA_F_IGNORE_IFNAME 0x01 +#define SA_F_IGNORE_IFTYPE 0x02 +#define SA_F_IGNORE_MEMCMP 0x04 +int +sa_equal_msg_flags(const struct sockaddr *a, const struct sockaddr *b, char *msg, size_t sz, int flags) +{ + char a_s[64], b_s[64]; + const struct sockaddr_in *a4, *b4; + const struct sockaddr_in6 *a6, *b6; + const struct sockaddr_dl *al, *bl; + + if (a == NULL) { + snprintf(msg, sz, "first sa is NULL"); + return 0; + } + if (b == NULL) { + snprintf(msg, sz, "second sa is NULL"); + return 0; + } + + if (a->sa_family != b->sa_family) { + snprintf(msg, sz, "family: %d vs %d", a->sa_family, b->sa_family); + return 0; + } + if (a->sa_len != b->sa_len) { + snprintf(msg, sz, "len: %d vs %d", a->sa_len, b->sa_len); + return 0; + } + + switch (a->sa_family) { + case AF_INET: + a4 = (const struct sockaddr_in *)a; + b4 = (const struct sockaddr_in *)b; + if (a4->sin_addr.s_addr != b4->sin_addr.s_addr) { + inet_ntop(AF_INET, &a4->sin_addr, a_s, sizeof(a_s)); + inet_ntop(AF_INET, &b4->sin_addr, b_s, sizeof(b_s)); + snprintf(msg, sz, "addr diff: %s vs %s", a_s, b_s); + return 0; + } + if (a4->sin_port != b4->sin_port) { + snprintf(msg, sz, "port diff: %d vs %d", + ntohs(a4->sin_port), ntohs(b4->sin_port)); + //return 0; + } + const uint32_t *a32, *b32; + a32 = (const uint32_t *)a4->sin_zero; + b32 = (const uint32_t *)b4->sin_zero; + if ((*a32 != *b32) || (*(a32 + 1) != *(b32 + 1))) { + snprintf(msg, sz, "zero diff: 0x%08X%08X vs 0x%08X%08X", + ntohl(*a32), ntohl(*(a32 + 1)), + ntohl(*b32), ntohl(*(b32 + 1))); + return 0; + } + return 1; + case AF_INET6: + a6 = (const struct sockaddr_in6 *)a; + b6 = (const struct sockaddr_in6 *)b; + if (!IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr)) { + inet_ntop(AF_INET6, &a6->sin6_addr, a_s, sizeof(a_s)); + inet_ntop(AF_INET6, &b6->sin6_addr, a_s, sizeof(a_s)); + snprintf(msg, sz, "addr diff: %s vs %s", a_s, b_s); + return 0; + } + if (a6->sin6_scope_id != b6->sin6_scope_id) { + snprintf(msg, sz, "scope diff: %u vs %u", a6->sin6_scope_id, b6->sin6_scope_id); + return 0; + } + break; + case AF_LINK: + al = (const struct sockaddr_dl *)a; + bl = (const struct sockaddr_dl *)b; + + if (al->sdl_index != bl->sdl_index) { + snprintf(msg, sz, "sdl_index diff: %u vs %u", al->sdl_index, bl->sdl_index); + return 0; + } + + if ((al->sdl_alen != bl->sdl_alen) || (memcmp(LLADDR(al), LLADDR(bl), al->sdl_alen) != 0)) { + char abuf[64], bbuf[64]; + sa_print_hd(abuf, sizeof(abuf), LLADDR(al), al->sdl_alen); + sa_print_hd(bbuf, sizeof(bbuf), LLADDR(bl), bl->sdl_alen); + snprintf(msg, sz, "sdl_alen diff: {%s} (%d) vs {%s} (%d)", + abuf, al->sdl_alen, bbuf, bl->sdl_alen); + return 0; + } + + if (((flags & SA_F_IGNORE_IFTYPE) == 0) && (al->sdl_type != bl->sdl_type)) { + snprintf(msg, sz, "sdl_type diff: %u vs %u", al->sdl_type, bl->sdl_type); + return 0; + } + + if (((flags & SA_F_IGNORE_IFNAME) == 0) && ((al->sdl_nlen != bl->sdl_nlen) || + (memcmp(al->sdl_data, bl->sdl_data, al->sdl_nlen) != 0))) { + char abuf[64], bbuf[64]; + memcpy(abuf, al->sdl_data, al->sdl_nlen); + abuf[al->sdl_nlen] = '\0'; + memcpy(bbuf, bl->sdl_data, bl->sdl_nlen); + abuf[bl->sdl_nlen] = '\0'; + snprintf(msg, sz, "sdl_nlen diff: {%s} (%d) vs {%s} (%d)", + abuf, al->sdl_nlen, bbuf, bl->sdl_nlen); + return 0; + } + + if (flags & SA_F_IGNORE_MEMCMP) + return 1; + break; + } + + if (memcmp(a, b, a->sa_len)) { + int i; + for (i = 0; i < a->sa_len; i++) + if (((const char *)a)[i] != ((const char *)b)[i]) + break; + + sa_print(a, 1); + sa_print(b, 1); + + snprintf(msg, sz, "overall memcmp() reports diff for af %d offset %d", + a->sa_family, i); + return 0; + } + return 1; +} + +int +sa_equal_msg(const struct sockaddr *a, const struct sockaddr *b, char *msg, size_t sz) +{ + + return sa_equal_msg_flags(a, b, msg, sz, 0); +} + +void +sa_fill_mask4(struct sockaddr_in *sin, int plen) +{ + + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0); +} + +void +sa_fill_mask6(struct sockaddr_in6 *sin6, uint8_t mask) +{ + uint32_t *cp; + + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + + for (cp = (uint32_t *)&sin6->sin6_addr; mask >= 32; mask -= 32) + *cp++ = 0xFFFFFFFF; + if (mask > 0) + *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); +} + +/* 52:54:00:14:e3:10 */ +#define ETHER_MAC_MAX_LENGTH 17 + +int +sa_convert_str_to_sa(const char *_addr, struct sockaddr *sa) +{ + int error; + + int af = AF_UNSPEC; + + char *addr = strdup(_addr); + int retcode = 0; + + /* classify AF by str */ + if (strchr(addr, ':')) { + /* inet6 or ether */ + char *k; + int delim_cnt = 0; + for (k = addr; *k; k++) + if (*k == ':') + delim_cnt++; + af = AF_INET6; + + if (delim_cnt == 5) { + k = strchr(addr, '%'); + if (k != NULL && (k - addr) <= ETHER_MAC_MAX_LENGTH) + af = AF_LINK; + } + } else if (strchr(addr, '.')) + af = AF_INET; + + /* */ + char *delimiter; + int ifindex = 0; + char *ifname = NULL; + if ((delimiter = strchr(addr, '%')) != NULL) { + *delimiter = '\0'; + ifname = delimiter + 1; + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + RLOG("unable to find ifindex for '%s'", ifname); + else + RLOG("if %s mapped to %d", ifname, ifindex); + } + + if (af == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + memset(sin6, 0, sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_scope_id = ifindex; + error = inet_pton(AF_INET6, addr, &sin6->sin6_addr); + if (error != 1) + RLOG_ERRNO("inet_ntop() failed: ret=%d", error); + else + retcode = 1; + } else if (af == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + memset(sin, 0, sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + error = inet_pton(AF_INET, addr, &sin->sin_addr); + if (error != 1) + RLOG("inet_ntop() failed: ret=%d", error); + else + retcode = 1; + } else if (af == AF_LINK) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + memset(sdl, 0, sizeof(struct sockaddr_dl)); + sdl->sdl_family = AF_LINK; + sdl->sdl_len = sizeof(struct sockaddr_dl); + sdl->sdl_index = ifindex; + sdl->sdl_alen = 6; + struct ether_addr *ea = (struct ether_addr *)LLADDR(sdl); + if (ether_aton_r(addr, ea) == NULL) + RLOG("ether_aton() failed"); + else + retcode = 1; + } + + return (retcode); +} + + +int +rtsock_setup_socket() +{ + int fd; + int af = AF_UNSPEC; /* 0 to capture messages from all AFs */ + fd = socket(PF_ROUTE, SOCK_RAW, af); + + ATF_REQUIRE_MSG(fd != -1, "rtsock open failed: %s", strerror(errno)); + + /* Listen for our messages */ + int on = 1; + if (setsockopt(fd, SOL_SOCKET,SO_USELOOPBACK, &on, sizeof(on)) < 0) + RLOG_ERRNO("setsockopt failed"); + + return (fd); +} + +ssize_t +rtsock_send_rtm(int fd, struct rt_msghdr *rtm) +{ + int my_errno; + ssize_t len; + + rtsock_update_rtm_len(rtm); + + len = write(fd, rtm, rtm->rtm_msglen); + my_errno = errno; + RTSOCK_ATF_REQUIRE_MSG(rtm, len == rtm->rtm_msglen, + "rtsock write failed: want %d got %zd (%s)", + rtm->rtm_msglen, len, strerror(my_errno)); + + return (len); +} + +struct rt_msghdr * +rtsock_read_rtm(int fd, char *buffer, size_t buflen) +{ + ssize_t len; + + len = read(fd, buffer, buflen); + int my_errno = errno; + ATF_REQUIRE_MSG(len > 0, "rtsock read failed: %s", strerror(my_errno)); + + rtsock_validate_message(buffer, len); + return ((struct rt_msghdr *)buffer); +} + +struct rt_msghdr * +rtsock_read_rtm_reply(int fd, char *buffer, size_t buflen, int seq) +{ + struct rt_msghdr *rtm; + + while (true) { + rtm = rtsock_read_rtm(fd, buffer, buflen); + if (rtm->rtm_pid != getpid()) + continue; + if (rtm->rtm_seq != seq) + continue; + + return (rtm); + } + + /* NOTREACHED */ +} + +void +rtsock_prepare_route_message_base(struct rt_msghdr *rtm, int cmd) +{ + + memset(rtm, 0, sizeof(struct rt_msghdr)); + rtm->rtm_type = cmd; + rtm->rtm_version = RTM_VERSION; + rtm->rtm_seq = _rtm_seq++; +} + +void +rtsock_prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *mask, struct sockaddr *gw) +{ + + rtsock_prepare_route_message_base(rtm, cmd); + if (dst != NULL) + rtsock_add_rtm_sa(rtm, RTA_DST, dst); + + if (gw != NULL) { + rtsock_add_rtm_sa(rtm, RTA_GATEWAY, gw); + rtm->rtm_flags |= RTF_GATEWAY; + } + + if (mask != NULL) + rtsock_add_rtm_sa(rtm, RTA_NETMASK, mask); +} + +void +rtsock_add_rtm_sa(struct rt_msghdr *rtm, int addr_type, struct sockaddr *sa) +{ + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + rtm->rtm_addrs |= addr_type; + memcpy(ptr, sa, sa->sa_len); +} + +struct sockaddr * +rtsock_find_rtm_sa(struct rt_msghdr *rtm, int addr_type) +{ + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + if (addr_type == (1 << i)) + return ((struct sockaddr *)ptr); + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + return (NULL); +} + +size_t +rtsock_calc_rtm_len(struct rt_msghdr *rtm) +{ + size_t len = sizeof(struct rt_msghdr); + + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + /* add */ + int sa_len = ALIGN(((struct sockaddr *)ptr)->sa_len); + len += sa_len; + ptr += sa_len; + } + } + + return len; +} + +void +rtsock_update_rtm_len(struct rt_msghdr *rtm) +{ + + rtm->rtm_msglen = rtsock_calc_rtm_len(rtm); +} + +static void +_validate_route_message(struct rt_msghdr *rtm) +{ + struct sockaddr *sa; + size_t parsed_len = sizeof(struct rt_msghdr); + int len = rtm->rtm_msglen; + + sa = (struct sockaddr *)(rtm + 1); + + for (int i = 0; i < RTAX_MAX; i++) { + if ((rtm->rtm_addrs & (1 << i)) == 0) + continue; + parsed_len += SA_SIZE(sa); + RTSOCK_ATF_REQUIRE_MSG(rtm, parsed_len <= len, + "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, len); + if (sa->sa_family == AF_LINK) { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + int data_len = sdl->sdl_nlen + sdl->sdl_alen; + data_len += offsetof(struct sockaddr_dl, sdl_data); + + RTSOCK_ATF_REQUIRE_MSG(rtm, data_len <= len, + "AF_LINK data size exceeds total len: %u vs %u", + data_len, len); + } + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + } + + RTSOCK_ATF_REQUIRE_MSG(rtm, parsed_len == rtm->rtm_msglen, + "message len != parsed len: expected %d parsed %d", + rtm->rtm_msglen, (int)parsed_len); +} + +/* + * Raises error if base syntax checks fails. + */ +void +rtsock_validate_message(char *buffer, ssize_t len) +{ + struct rt_msghdr *rtm; + + ATF_REQUIRE_MSG(len > 0, "read() return %zd, error: %s", len, strerror(errno)); + + rtm = (struct rt_msghdr *)buffer; + ATF_REQUIRE_MSG(rtm->rtm_version == RTM_VERSION, "unknown RTM_VERSION: expected %d got %d", + RTM_VERSION, rtm->rtm_version); + ATF_REQUIRE_MSG(rtm->rtm_msglen <= len, "wrong message length: expected %d got %d", + (int)len, (int)rtm->rtm_msglen); + + switch (rtm->rtm_type) { + case RTM_GET: + case RTM_ADD: + case RTM_DELETE: + case RTM_CHANGE: + _validate_route_message(rtm); + break; + } +} + +#endif Index: head/tests/sys/net/routing/rtsock_config.h =================================================================== --- head/tests/sys/net/routing/rtsock_config.h +++ head/tests/sys/net/routing/rtsock_config.h @@ -0,0 +1,164 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NET_ROUTING_RTSOCK_CONFIG_H_ +#define _NET_ROUTING_RTSOCK_CONFIG_H_ + +struct rtsock_test_config { + int ifindex; + char net4_str[INET_ADDRSTRLEN]; + char addr4_str[INET_ADDRSTRLEN]; + char net6_str[INET6_ADDRSTRLEN]; + char addr6_str[INET6_ADDRSTRLEN]; + struct sockaddr_in net4; + struct sockaddr_in mask4; + struct sockaddr_in addr4; + struct sockaddr_in6 net6; + struct sockaddr_in6 mask6; + struct sockaddr_in6 addr6; + int plen4; + int plen6; + char *remote_lladdr; + char *ifname; + bool autocreated_interface; + int rtsock_fd; +}; + +struct rtsock_test_config * +config_setup_base(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + + c = calloc(1, sizeof(struct rtsock_test_config)); + c->rtsock_fd = -1; + + return c; +} + +struct rtsock_test_config * +config_setup(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + char buf[64], *s; + const char *key; + int mask; + + c = config_setup_base(tc); + + key = atf_tc_get_config_var_wd(tc, "rtsock.v4prefix", "192.0.2.0/24"); + strlcpy(buf, key, sizeof(buf)); + if ((s = strchr(buf, '/')) == NULL) + return (NULL); + *s++ = '\0'; + mask = strtol(s, NULL, 10); + if (mask < 0 || mask > 32) + return (NULL); + c->plen4 = mask; + inet_pton(AF_INET, buf, &c->net4.sin_addr); + + c->net4.sin_len = sizeof(struct sockaddr_in); + c->net4.sin_family = AF_INET; + c->addr4.sin_len = sizeof(struct sockaddr_in); + c->addr4.sin_family = AF_INET; + + sa_fill_mask4(&c->mask4, c->plen4); + + /* Fill in interface IPv4 address. Assume the first address in net */ + c->addr4.sin_addr.s_addr = htonl(ntohl(c->net4.sin_addr.s_addr) + 1); + inet_ntop(AF_INET, &c->net4.sin_addr, c->net4_str, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &c->addr4.sin_addr, c->addr4_str, INET_ADDRSTRLEN); + + key = atf_tc_get_config_var_wd(tc, "rtsock.v6prefix", "2001:DB8::/32"); + strlcpy(buf, key, sizeof(buf)); + if ((s = strchr(buf, '/')) == NULL) + return (NULL); + *s++ = '\0'; + mask = strtol(s, NULL, 10); + if (mask < 0 || mask > 128) + return (NULL); + c->plen6 = mask; + + inet_pton(AF_INET6, buf, &c->net6.sin6_addr); + + c->net6.sin6_len = sizeof(struct sockaddr_in6); + c->net6.sin6_family = AF_INET6; + c->addr6.sin6_len = sizeof(struct sockaddr_in6); + c->addr6.sin6_family = AF_INET6; + + sa_fill_mask6(&c->mask6, c->plen6); + + /* Fill in interface IPv6 address. Assume the first address in net */ + memcpy(&c->addr6.sin6_addr, &c->net6.sin6_addr, sizeof(struct in6_addr)); +#define _s6_addr32 __u6_addr.__u6_addr32 + c->addr6.sin6_addr._s6_addr32[3] = htonl(ntohl(c->net6.sin6_addr._s6_addr32[3]) + 1); +#undef _s6_addr32 + inet_ntop(AF_INET6, &c->net6.sin6_addr, c->net6_str, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &c->addr6.sin6_addr, c->addr6_str, INET6_ADDRSTRLEN); + + c->ifname = strdup(atf_tc_get_config_var_wd(tc, "rtsock.ifname", "tap4242")); + c->autocreated_interface = atf_tc_get_config_var_as_bool_wd(tc, "rtsock.create_interface", true); + + if (c->autocreated_interface && (if_nametoindex(c->ifname) == 0)) + { + /* create our own interface */ + char new_ifname[IFNAMSIZ]; + strlcpy(new_ifname, c->ifname, sizeof(new_ifname)); + int ret = iface_create_cloned(new_ifname); + ATF_REQUIRE_MSG(ret != 0, "tap interface creation failed: %s", strerror(errno)); + c->ifname = strdup(new_ifname); + } + c->ifindex = if_nametoindex(c->ifname); + ATF_REQUIRE_MSG(c->ifindex != 0, "inteface %s not found", c->ifname); + + c->remote_lladdr = strdup(atf_tc_get_config_var_wd(tc, + "rtsock.remote_lladdr", "00:00:5E:00:53:42")); + + return (c); +} + +void +config_generic_cleanup(struct rtsock_test_config *c) +{ + if (c->ifname != NULL && c->autocreated_interface) { + iface_destroy(c->ifname); + free(c->ifname); + c->ifname = NULL; + } +} + +void +config_describe_root_test(atf_tc_t *tc, char *test_descr) +{ + + atf_tc_set_md_var(tc, "descr", test_descr); + // Adding/deleting prefix requires root privileges + atf_tc_set_md_var(tc, "require.user", "root"); +} + +#endif Index: head/tests/sys/net/routing/rtsock_print.h =================================================================== --- head/tests/sys/net/routing/rtsock_print.h +++ head/tests/sys/net/routing/rtsock_print.h @@ -0,0 +1,280 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _NET_ROUTING_RTSOCK_PRINT_H_ +#define _NET_ROUTING_RTSOCK_PRINT_H_ + + +#define RLOG(_fmt, ...) printf("%s: " _fmt "\n", __func__, ##__VA_ARGS__) +#define RLOG_ERRNO(_fmt, ...) do { \ + printf("%s: " _fmt, __func__, ##__VA_ARGS__); \ + printf(": %s\n", strerror(errno)); \ +} while(0) + +#define RTSOCK_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \ + if (!(_cond)) { \ + printf("-- CONDITION FAILED, rtm dump --\n\n");\ + rtsock_print_rtm(_rtm); \ + } \ + ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \ +} while (0); + + +/* from route.c */ +static const char *const msgtypes[] = { + "", + "RTM_ADD", + "RTM_DELETE", + "RTM_CHANGE", + "RTM_GET", + "RTM_LOSING", + "RTM_REDIRECT", + "RTM_MISS", + "RTM_LOCK", + "RTM_OLDADD", + "RTM_OLDDEL", + "RTM_RESOLVE", + "RTM_NEWADDR", + "RTM_DELADDR", + "RTM_IFINFO", + "RTM_NEWMADDR", + "RTM_DELMADDR", + "RTM_IFANNOUNCE", + "RTM_IEEE80211", +}; + +static const char metricnames[] = + "\011weight\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire" + "\1mtu"; +static const char routeflags[] = + "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE" + "\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" + "\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3" + "\024FIXEDMTU\025PINNED\026LOCAL\027BROADCAST\030MULTICAST\035STICKY"; +static const char ifnetflags[] = + "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP" + "\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1" + "\017LINK2\020MULTICAST"; +static const char addrnames[] = + "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +static int +_printb(char *buf, size_t bufsize, int b, const char *str) +{ + int i; + int gotsome = 0; + + char *pbuf = buf; + + if (b == 0) { + *pbuf = '\0'; + return (0); + } + while ((i = *str++) != 0) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + *pbuf++ = i; + gotsome = 1; + for (; (i = *str) > 32; str++) + *pbuf++ = i; + } else + while (*str > 32) + str++; + } + if (gotsome) + *pbuf++ = '>'; + *pbuf = '\0'; + + return (int)(pbuf - buf); +} + +const char * +rtsock_print_cmdtype(int cmd) +{ + + return (msgtypes[cmd]); +} + + +#define _PRINTX(fmt, ...) do { \ + one_len = snprintf(ptr, rem_len, fmt, __VA_ARGS__); \ + ptr += one_len; \ + rem_len -= one_len; \ +} while(0) + + +void +sa_print_hd(char *buf, int buflen, const char *data, int len) +{ + char *ptr; + int one_len, rem_len; + + ptr = buf; + rem_len = buflen; + + const char *last_char = NULL; + unsigned char v; + int repeat_count = 0; + for (int i = 0; i < len; i++) { + if (last_char && *last_char == data[i]) { + repeat_count++; + continue; + } + + if (repeat_count > 1) { + _PRINTX("{%d}", repeat_count); + repeat_count = 0; + } + + v = ((const unsigned char *)data)[i]; + if (last_char == NULL) + _PRINTX("%02X", v); + else + _PRINTX(", %02X", v); + + last_char = &data[i]; + repeat_count = 1; + } + + if (repeat_count > 1) + snprintf(ptr, rem_len, "{%d}", repeat_count); +} + +#undef _PRINTX + +void +sa_print(const struct sockaddr *sa, int include_hexdump) +{ + char hdbuf[512], abuf[64]; + char ifbuf[128]; + const struct sockaddr_dl *sdl; + const struct sockaddr_in6 *sin6; + const struct sockaddr_in *sin; + int i; + + switch (sa->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)sa; + inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf)); + printf(" af=inet len=%d addr=%s", sa->sa_len, abuf); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sa; + inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf)); + int scope_id = sin6->sin6_scope_id; + printf(" af=inet6 len=%d addr=%s", sa->sa_len, abuf); + if (scope_id != 0) { + memset(ifbuf, 0, sizeof(ifbuf)); + if_indextoname(scope_id, ifbuf); + printf(" scope_id=%d if_name=%s", scope_id, ifbuf); + } + break; + case AF_LINK: + sdl = (const struct sockaddr_dl *)sa; + int sdl_index = sdl->sdl_index; + if (sdl_index != 0) { + memset(ifbuf, 0, sizeof(ifbuf)); + if_indextoname(sdl_index, ifbuf); + printf(" af=link len=%d sdl_index=%d if_name=%s", sdl->sdl_len, sdl_index, ifbuf); + } + if (sdl->sdl_nlen) { + char _ifname[IFNAMSIZ]; + memcpy(_ifname, sdl->sdl_data, sdl->sdl_nlen); + _ifname[sdl->sdl_nlen] = '\0'; + printf(" name=%s", _ifname); + } + if (sdl->sdl_alen) { + printf(" addr="); + const char *lladdr = LLADDR(sdl); + for (int i = 0; i < sdl->sdl_alen; i++) { + if (i + 1 < sdl->sdl_alen) + printf("%02X:", ((const unsigned char *)lladdr)[i]); + else + printf("%02X", ((const unsigned char *)lladdr)[i]); + } + } + break; + default: + printf(" af=%d len=%d", sa->sa_family, sa->sa_len); + } + + if (include_hexdump) { + sa_print_hd(hdbuf, sizeof(hdbuf), ((char *)sa), sa->sa_len); + printf(" hd={%s}", hdbuf); + } + printf("\n"); +} + +/* +got message of size 240 on Mon Dec 16 09:23:31 2019 +RTM_ADD: Add Route: len 240, pid: 25534, seq 2, errno 0, flags: +locks: inits: +sockaddrs: +*/ + +void +rtsock_print_rtm(struct rt_msghdr *rtm) +{ + struct timeval tv; + struct tm tm_res; + char buf[64]; + + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm_res); + strftime(buf, sizeof(buf), "%F %T", &tm_res); + printf("Got message of size %hu on %s\n", rtm->rtm_msglen, buf); + + char flags_buf[256]; + _printb(flags_buf, sizeof(flags_buf), rtm->rtm_flags, routeflags); + + printf("%s: len %hu, pid: %d, seq %d, errno %d, flags: %s\n", msgtypes[rtm->rtm_type], + rtm->rtm_msglen, rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno, flags_buf); + + _printb(flags_buf, sizeof(flags_buf), rtm->rtm_addrs, addrnames); + printf("sockaddrs: 0x%X %s\n", rtm->rtm_addrs, flags_buf); + + char *ptr = (char *)(rtm + 1); + for (int i = 0; i < RTAX_MAX; i++) { + if (rtm->rtm_addrs & (1 << i)) { + struct sockaddr *sa = (struct sockaddr *)ptr; + sa_print(sa, 1); + + /* add */ + ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); + } + } + + printf("\n"); + +} + +#endif Index: head/tests/sys/net/routing/test_rtsock_l3.c =================================================================== --- head/tests/sys/net/routing/test_rtsock_l3.c +++ head/tests/sys/net/routing/test_rtsock_l3.c @@ -0,0 +1,521 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "rtsock_common.h" +#include "rtsock_config.h" + +static inline struct rtsock_test_config * +presetup_ipv6(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc); + + ret = iface_turn_up(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname); + + ret = iface_enable_ipv6(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname); + + ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); + + c->rtsock_fd = rtsock_setup_socket(); + + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv4(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc); + + /* assumes ifconfig doing IFF_UP */ + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + /* Actually open interface, so kernel writes won't fail */ + if (c->autocreated_interface) { + ret = iface_open(c->ifname); + ATF_REQUIRE_MSG(ret >= 0, "unable to open interface %s", c->ifname); + } + + c->rtsock_fd = rtsock_setup_socket(); + + return (c); +} + + +static void +prepare_v4_network(struct rtsock_test_config *c, struct sockaddr_in *dst, + struct sockaddr_in *mask, struct sockaddr_in *gw) +{ + /* Create IPv4 subnetwork with smaller prefix */ + sa_fill_mask4(mask, c->plen4 + 1); + *dst = c->net4; + /* Calculate GW as last-net-address - 1 */ + *gw = c->net4; + gw->sin_addr.s_addr = htonl((ntohl(c->net4.sin_addr.s_addr) | ~ntohl(c->mask4.sin_addr.s_addr)) - 1); + sa_print((struct sockaddr *)dst, 0); + sa_print((struct sockaddr *)mask, 0); + sa_print((struct sockaddr *)gw, 0); +} + +static void +prepare_v6_network(struct rtsock_test_config *c, struct sockaddr_in6 *dst, + struct sockaddr_in6 *mask, struct sockaddr_in6 *gw) +{ + /* Create IPv6 subnetwork with smaller prefix */ + sa_fill_mask6(mask, c->plen6 + 1); + *dst = c->net6; + /* Calculate GW as last-net-address - 1 */ + *gw = c->net6; +#define _s6_addr32 __u6_addr.__u6_addr32 + gw->sin6_addr._s6_addr32[0] = htonl((ntohl(gw->sin6_addr._s6_addr32[0]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[0]))); + gw->sin6_addr._s6_addr32[1] = htonl((ntohl(gw->sin6_addr._s6_addr32[1]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[1]))); + gw->sin6_addr._s6_addr32[2] = htonl((ntohl(gw->sin6_addr._s6_addr32[2]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[2]))); + gw->sin6_addr._s6_addr32[3] = htonl((ntohl(gw->sin6_addr._s6_addr32[3]) | ~ntohl(c->mask6.sin6_addr._s6_addr32[3])) - 1); +#undef _s6_addr32 + sa_print((struct sockaddr *)dst, 0); + sa_print((struct sockaddr *)mask, 0); + sa_print((struct sockaddr *)gw, 0); +} + +static void +prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *mask, struct sockaddr *gw) +{ + + rtsock_prepare_route_message(rtm, cmd, dst, mask, gw); + + if (cmd == RTM_ADD || cmd == RTM_CHANGE) + rtm->rtm_flags |= RTF_STATIC; +} + +static void +verify_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *mask, struct sockaddr *gw) +{ + char msg[512]; + struct sockaddr *sa; + int ret; + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == cmd, + "expected %s message, got %d (%s)", rtsock_print_cmdtype(cmd), + rtm->rtm_type, rtsock_print_cmdtype(rtm->rtm_type)); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_errno == 0, + "got got errno %d as message reply", rtm->rtm_errno); + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->_rtm_spare1 == 0, + "expected rtm_spare==0, got %d", rtm->_rtm_spare1); + + /* kernel MAY return more sockaddrs, including RTA_IFP / RTA_IFA, so verify the needed ones */ + if (dst != NULL) { + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "DST is not set"); + ret = sa_equal_msg(sa, dst, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + } + + if (mask != NULL) { + sa = rtsock_find_rtm_sa(rtm, RTA_NETMASK); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "NETMASK is not set"); + ret = sa_equal_msg(sa, mask, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "NETMASK sa diff: %s", msg); + } + + if (gw != NULL) { + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); + ret = sa_equal_msg(sa, gw, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + } + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid"); +} + +static void +verify_route_message_extra(struct rt_msghdr *rtm, int ifindex, int rtm_flags) +{ + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_index == ifindex, + "expected ifindex %d, got %d", ifindex, rtm->rtm_index); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == rtm_flags, + "expected flags: %X, got %X", rtm_flags, rtm->rtm_flags); +} + +/* TESTS */ + +#define DECLARE_TEST_VARS \ + char buffer[2048]; \ + struct rtsock_test_config *c; \ + struct rt_msghdr *rtm = (struct rt_msghdr *)buffer; \ + struct sockaddr *sa; \ + int ret; \ + \ + +#define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg) +#define CLEANUP_AFTER_TEST config_generic_cleanup(config_setup(tc)) + + +ATF_TC_WITH_CLEANUP(rtm_get_v4_exact_success); +ATF_TC_HEAD(rtm_get_v4_exact_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests RTM_GET with exact prefix lookup on an interface prefix"); +} + +ATF_TC_BODY(rtm_get_v4_exact_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_GET: Report Metrics: len 240, pid: 45072, seq 42, errno 0, flags: + * sockaddrs: 0x7 + * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}} + * af=link len=54 sdl_index=3 if_name=tap4242 hd={36, 12, 03, 00, 06, 00{49}} + * af=inet len=16 addr=255.255.255.0 hd={10, 02, FF{5}, 00{9}} + */ + + verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED); + + /* Explicitly verify gateway for the interface route */ + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa != NULL, "GATEWAY is not set"); + RTSOCK_ATF_REQUIRE_MSG(rtm, sa->sa_family == AF_LINK, "GW sa family is %d", sa->sa_family); + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == c->ifindex, "GW ifindex is %d", sdl->sdl_index); +} + +ATF_TC_CLEANUP(rtm_get_v4_exact_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_get_v4_lpm_success); +ATF_TC_HEAD(rtm_get_v4_lpm_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests RTM_GET with address lookup on an existing prefix"); +} + +ATF_TC_BODY(rtm_get_v4_lpm_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, NULL, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + /* + * RTM_GET: Report Metrics: len 312, pid: 67074, seq 1, errno 0, flags: + * locks: inits: + * sockaddrs: + * 10.0.0.0 link#1 255.255.255.0 vtnet0:52.54.0.42.f.ef 10.0.0.157 + */ + + verify_route_message(rtm, RTM_GET, (struct sockaddr *)&c->net4, + (struct sockaddr *)&c->mask4, NULL); + + verify_route_message_extra(rtm, c->ifindex, RTF_UP | RTF_DONE | RTF_PINNED); +} + +ATF_TC_CLEANUP(rtm_get_v4_lpm_success, tc) +{ + CLEANUP_AFTER_TEST; +} + + +ATF_TC_WITH_CLEANUP(rtm_get_v4_empty_dst_failure); +ATF_TC_HEAD(rtm_get_v4_empty_dst_failure, tc) +{ + + DESCRIBE_ROOT_TEST("Tests RTM_GET with empty DST addr"); +} + +ATF_TC_BODY(rtm_get_v4_empty_dst_failure, tc) +{ + DECLARE_TEST_VARS; + + c = config_setup_base(tc); + c->rtsock_fd = rtsock_setup_socket(); + + rtsock_prepare_route_message(rtm, RTM_GET, NULL, + (struct sockaddr *)&c->mask4, NULL); + rtsock_update_rtm_len(rtm); + + write(c->rtsock_fd, rtm, rtm->rtm_msglen); + ATF_CHECK_ERRNO(EINVAL, write(c->rtsock_fd, rtm, rtm->rtm_msglen)); +} + +ATF_TC_CLEANUP(rtm_get_v4_empty_dst_failure, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_get_v4_hostbits_failure); +ATF_TC_HEAD(rtm_get_v4_hostbits_failure, tc) +{ + DESCRIBE_ROOT_TEST("Tests RTM_GET with prefix with some hosts-bits set"); +} + +ATF_TC_BODY(rtm_get_v4_hostbits_failure, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Q the same prefix */ + rtsock_prepare_route_message(rtm, RTM_GET, (struct sockaddr *)&c->addr4, + (struct sockaddr *)&c->mask4, NULL); + rtsock_update_rtm_len(rtm); + + ATF_CHECK_ERRNO(ESRCH, write(c->rtsock_fd, rtm, rtm->rtm_msglen)); +} + +ATF_TC_CLEANUP(rtm_get_v4_hostbits_failure, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v4_gw_direct_success); +ATF_TC_HEAD(rtm_add_v4_gw_direct_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv4 route addition with directly-reachable GW specified by IP"); +} + +ATF_TC_BODY(rtm_add_v4_gw_direct_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags: + * locks: inits: + * sockaddrs: + * 192.0.2.0 192.0.2.254 255.255.255.128 + */ + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + /* XXX: Currently kernel sets RTF_UP automatically but does NOT report it in the reply */ + verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v4_gw_direct_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_del_v4_prefix_nogw_success); +ATF_TC_HEAD(rtm_del_v4_prefix_nogw_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv4 route removal without specifying gateway"); +} + +ATF_TC_BODY(rtm_del_v4_prefix_nogw_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + /* Create IPv4 subnetwork with smaller prefix */ + struct sockaddr_in mask4; + struct sockaddr_in net4; + struct sockaddr_in gw4; + prepare_v4_network(c, &net4, &mask4, &gw4); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Route has been added successfully, try to delete it */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: + * sockaddrs: 0x7 + * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}} + * af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}} + * af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}} + */ + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, + (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); + + verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_del_v4_prefix_nogw_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success); +ATF_TC_HEAD(rtm_add_v6_gu_gw_gu_direct_success, tc) +{ + DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW"); +} + +ATF_TC_BODY(rtm_add_v6_gu_gw_gu_direct_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_ADD: Add Route: len 200, pid: 46068, seq 42, errno 0, flags: + * locks: inits: + * sockaddrs: + * 192.0.2.0 192.0.2.254 255.255.255.128 + */ + + verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + /* XXX: Currently kernel sets RTF_UP automatically but does NOT report it in the reply */ + verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_add_v6_gu_gw_gu_direct_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_del_v6_gu_prefix_nogw_success); +ATF_TC_HEAD(rtm_del_v6_gu_prefix_nogw_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix removal without specifying gateway"); +} + +ATF_TC_BODY(rtm_del_v6_gu_prefix_nogw_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + /* Create IPv6 subnetwork with smaller prefix */ + struct sockaddr_in6 mask6; + struct sockaddr_in6 net6; + struct sockaddr_in6 gw6; + prepare_v6_network(c, &net6, &mask6, &gw6); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Route has been added successfully, try to delete it */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, NULL); + + rtsock_send_rtm(c->rtsock_fd, rtm); + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + /* + * RTM_DELETE: Delete Route: len 200, pid: 46417, seq 43, errno 0, flags: + * sockaddrs: 0x7 + * af=inet len=16 addr=192.0.2.0 hd={10, 02, 00{2}, C0, 00, 02, 00{9}} + * af=inet len=16 addr=192.0.2.254 hd={10, 02, 00{2}, C0, 00, 02, FE, 00{8}} + * af=inet len=16 addr=255.255.255.128 hd={10, 02, FF{5}, 80, 00{8}} + */ + + verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net6, + (struct sockaddr *)&mask6, (struct sockaddr *)&gw6); + verify_route_message_extra(rtm, c->ifindex, RTF_DONE | RTF_GATEWAY | RTF_STATIC); +} + +ATF_TC_CLEANUP(rtm_del_v6_gu_prefix_nogw_success, tc) +{ + CLEANUP_AFTER_TEST; +} + + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, rtm_get_v4_exact_success); + ATF_TP_ADD_TC(tp, rtm_get_v4_lpm_success); + ATF_TP_ADD_TC(tp, rtm_get_v4_hostbits_failure); + ATF_TP_ADD_TC(tp, rtm_get_v4_empty_dst_failure); + ATF_TP_ADD_TC(tp, rtm_add_v4_gw_direct_success); + ATF_TP_ADD_TC(tp, rtm_del_v4_prefix_nogw_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_gw_gu_direct_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_prefix_nogw_success); + + return (atf_no_error()); +} + Index: head/tests/sys/net/routing/test_rtsock_lladdr.c =================================================================== --- head/tests/sys/net/routing/test_rtsock_lladdr.c +++ head/tests/sys/net/routing/test_rtsock_lladdr.c @@ -0,0 +1,459 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Alexander V. Chernikov + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "rtsock_common.h" +#include "rtsock_config.h" + +static inline struct rtsock_test_config * +presetup_ipv6(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc); + + ret = iface_turn_up(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to turn up %s", c->ifname); + ret = iface_enable_ipv6(c->ifname); + ATF_REQUIRE_MSG(ret == 0, "Unable to enable IPv6 on %s", c->ifname); + + c->rtsock_fd = rtsock_setup_socket(); + + return (c); +} + +static inline struct rtsock_test_config * +presetup_ipv4(const atf_tc_t *tc) +{ + struct rtsock_test_config *c; + int ret; + + c = config_setup(tc); + + /* assumes ifconfig doing IFF_UP */ + ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); + ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); + + /* Actually open interface, so kernel writes won't fail */ + if (c->autocreated_interface) { + ret = iface_open(c->ifname); + ATF_REQUIRE_MSG(ret >= 0, "unable to open interface %s", c->ifname); + } + + c->rtsock_fd = rtsock_setup_socket(); + + return (c); +} + +static void +prepare_route_message(struct rt_msghdr *rtm, int cmd, struct sockaddr *dst, + struct sockaddr *gw) +{ + + rtsock_prepare_route_message(rtm, cmd, dst, NULL, gw); + + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA); +} + +/* TESTS */ +#define DECLARE_TEST_VARS \ + char buffer[2048], msg[512]; \ + ssize_t len; \ + int ret; \ + struct rtsock_test_config *c; \ + struct rt_msghdr *rtm = (struct rt_msghdr *)buffer; \ + struct sockaddr *sa; \ + \ + +#define DECLARE_CLEANUP_VARS \ + struct rtsock_test_config *c = config_setup(tc); \ + \ + +#define DESCRIBE_ROOT_TEST(_msg) config_describe_root_test(tc, _msg) +#define CLEANUP_AFTER_TEST config_generic_cleanup(config_setup(tc)) + + +ATF_TC_WITH_CLEANUP(rtm_add_v6_ll_lle_success); +ATF_TC_HEAD(rtm_add_v6_ll_lle_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests addition of link-local IPv6 ND entry"); +} + +ATF_TC_BODY(rtm_add_v6_ll_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + struct sockaddr_in6 sin6; + /* Interface here is optional. XXX: verify kernel side. */ + char *v6addr = "fe80::4242:4242"; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", v6addr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)&sin6); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * Got message of size 240 on 2019-12-17 15:06:51 + * RTM_ADD: Add Route: len 240, pid: 0, seq 0, errno 0, flags: + * sockaddrs: 0x3 + * af=inet6 len=28 addr=fe80::4242:4242 scope_id=3 if_name=tap4242 + * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10 + */ + + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* Some additional checks to verify kernel has filled in interface data */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_type > 0, "sdl_type not set"); +} + +ATF_TC_CLEANUP(rtm_add_v6_ll_lle_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v6_gu_lle_success); +ATF_TC_HEAD(rtm_add_v6_gu_lle_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests addition of global IPv6 ND entry"); +} + +ATF_TC_BODY(rtm_add_v6_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + + struct sockaddr_in6 sin6; + sin6 = c->net6; +#define _s6_addr32 __u6_addr.__u6_addr32 + sin6.sin6_addr._s6_addr32[3] = htonl(0x42424242); +#undef _s6_addr32 + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * Got message of size 240 on 2019-12-17 14:56:43 + * RTM_ADD: Add Route: len 240, pid: 0, seq 0, errno 0, flags: + * sockaddrs: 0x3 + * af=inet6 len=28 addr=2001:db8::4242:4242 + * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10 + */ + + /* XXX: where is uRPF?! this should fail */ + + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* Some additional checks to verify kernel has filled in interface data */ + struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; + RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_type > 0, "sdl_type not set"); +} + +ATF_TC_CLEANUP(rtm_add_v6_gu_lle_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_add_v4_gu_lle_success); +ATF_TC_HEAD(rtm_add_v4_gu_lle_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests addition of IPv4 ARP entry"); +} + +ATF_TC_BODY(rtm_add_v4_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + char str_buf[128]; + + struct sockaddr_in sin; + sin = c->addr4; + /* Use the next IPv4 address after self */ + sin.sin_addr.s_addr = htonl(ntohl(sin.sin_addr.s_addr) + 1); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)ðer); + + len = rtsock_send_rtm(c->rtsock_fd, rtm); + + /* + * RTM_ADD: Add Route: len 224, pid: 43131, seq 42, errno 0, flags: + * sockaddrs: 0x3 + * af=inet len=16 addr=192.0.2.2 + * af=link len=54 sdl_index=3 if_name=tap4242 addr=52:54:00:14:E3:10 + */ + + rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type, contrary to IPv6. + */ +} + +ATF_TC_CLEANUP(rtm_add_v4_gu_lle_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_del_v6_ll_lle_success); +ATF_TC_HEAD(rtm_del_v6_ll_lle_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests removal of link-local IPv6 ND entry"); +} + +ATF_TC_BODY(rtm_del_v6_ll_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + + struct sockaddr_in6 sin6; + /* Interface here is optional. XXX: verify kernel side. */ + char *v6addr = "fe80::4242:4242"; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", v6addr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)&sin6); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Successfully added an entry, let's try to remove it. */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete"); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type on delete. + */ +} + +ATF_TC_CLEANUP(rtm_del_v6_ll_lle_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_del_v6_gu_lle_success); +ATF_TC_HEAD(rtm_del_v6_gu_lle_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests removal of global IPv6 ND entry"); +} + +ATF_TC_BODY(rtm_del_v6_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv6(tc); + + char str_buf[128]; + + struct sockaddr_in6 sin6; + sin6 = c->net6; +#define _s6_addr32 __u6_addr.__u6_addr32 + sin6.sin6_addr._s6_addr32[3] = htonl(0x42424242); +#undef _s6_addr32 + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + len = rtsock_send_rtm(c->rtsock_fd, rtm); + + /* Successfully added an entry, let's try to remove it. */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin6, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete"); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin6, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type on delete. + */ +} + +ATF_TC_CLEANUP(rtm_del_v6_gu_lle_success, tc) +{ + CLEANUP_AFTER_TEST; +} + +ATF_TC_WITH_CLEANUP(rtm_del_v4_gu_lle_success); +ATF_TC_HEAD(rtm_del_v4_gu_lle_success, tc) +{ + + DESCRIBE_ROOT_TEST("Tests removal of IPv4 ARP entry"); +} + +ATF_TC_BODY(rtm_del_v4_gu_lle_success, tc) +{ + DECLARE_TEST_VARS; + + c = presetup_ipv4(tc); + + char str_buf[128]; + + struct sockaddr_in sin; + sin = c->addr4; + /* Use the next IPv4 address after self */ + sin.sin_addr.s_addr = htonl(ntohl(sin.sin_addr.s_addr) + 1); + + struct sockaddr_dl ether; + snprintf(str_buf, sizeof(str_buf), "%s%%%s", c->remote_lladdr, c->ifname); + sa_convert_str_to_sa(str_buf, (struct sockaddr *)ðer); + + prepare_route_message(rtm, RTM_ADD, (struct sockaddr *)&sin, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); + + /* We successfully added an entry, let's try to remove it. */ + prepare_route_message(rtm, RTM_DELETE, (struct sockaddr *)&sin, (struct sockaddr *)ðer); + + rtsock_send_rtm(c->rtsock_fd, rtm); + + rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); + + RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_type == RTM_DELETE, "rtm_type is not delete"); + + sa = rtsock_find_rtm_sa(rtm, RTA_DST); + ret = sa_equal_msg(sa, (struct sockaddr *)&sin, msg, sizeof(msg)); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "DST sa diff: %s", msg); + + sa = rtsock_find_rtm_sa(rtm, RTA_GATEWAY); + int sa_flags = SA_F_IGNORE_IFNAME | SA_F_IGNORE_IFTYPE | SA_F_IGNORE_MEMCMP; + ret = sa_equal_msg_flags(sa, (struct sockaddr *)ðer, msg, sizeof(msg), sa_flags); + RTSOCK_ATF_REQUIRE_MSG(rtm, ret != 0, "GATEWAY sa diff: %s", msg); + + /* + * TODO: Currently kernel code does not set sdl_type, contrary to IPv6. + */ +} + +ATF_TC_CLEANUP(rtm_del_v4_gu_lle_success, tc) +{ + CLEANUP_AFTER_TEST; +} + + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, rtm_add_v6_ll_lle_success); + ATF_TP_ADD_TC(tp, rtm_add_v6_gu_lle_success); + ATF_TP_ADD_TC(tp, rtm_add_v4_gu_lle_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_ll_lle_success); + ATF_TP_ADD_TC(tp, rtm_del_v6_gu_lle_success); + ATF_TP_ADD_TC(tp, rtm_del_v4_gu_lle_success); + + return (atf_no_error()); +} + +