Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108212467
D22860.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
63 KB
Referenced Files
None
Subscribers
None
D22860.diff
View Options
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 <bsd.test.mk>
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <arpa/inet.h>
+#include <net/ethernet.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <ifaddrs.h>
+
+#include <errno.h>
+#include <err.h>
+#include <sysexits.h>
+
+#include <atf-c.h>
+
+#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:<HOST,DONE,LLINFO,STATIC>
+locks: inits:
+sockaddrs: <DST,GATEWAY>
+*/
+
+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: <UP,DONE,PINNED>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * 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:<UP,DONE,PINNED>
+ * locks: inits:
+ * sockaddrs: <DST,GATEWAY,NETMASK,IFP,IFA>
+ * 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:<GATEWAY,DONE,STATIC>
+ * locks: inits:
+ * sockaddrs: <DST,GATEWAY,NETMASK>
+ * 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: <GATEWAY,DONE,STATIC>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * 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:<GATEWAY,DONE,STATIC>
+ * locks: inits:
+ * sockaddrs: <DST,GATEWAY,NETMASK>
+ * 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: <GATEWAY,DONE,STATIC>
+ * sockaddrs: 0x7 <DST,GATEWAY,NETMASK>
+ * 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: <UP,HOST,DONE,LLINFO>
+ * sockaddrs: 0x3 <DST,GATEWAY>
+ * 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: <UP,HOST,DONE,LLINFO>
+ * sockaddrs: 0x3 <DST,GATEWAY>
+ * 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: <HOST,DONE,LLINFO,STATIC>
+ * sockaddrs: 0x3 <DST,GATEWAY>
+ * 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());
+}
+
+
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 23, 5:21 PM (18 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16059465
Default Alt Text
D22860.diff (63 KB)
Attached To
Mode
D22860: Add userland tests for route table/lltable rtsock operations.
Attached
Detach File
Event Timeline
Log In to Comment