Index: head/tests/sys/net/routing/Makefile =================================================================== --- head/tests/sys/net/routing/Makefile (revision 360044) +++ head/tests/sys/net/routing/Makefile (revision 360045) @@ -1,17 +1,19 @@ # $FreeBSD$ PACKAGE= tests TESTSDIR= ${TESTSBASE}/sys/net/routing ATF_TESTS_C += test_rtsock_l3 ATF_TESTS_C += test_rtsock_lladdr ${PACKAGE}FILES+= generic_cleanup.sh ${PACKAGE}FILESMODE_generic_cleanup.sh=0555 # 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 +CFLAGS+= -I${.CURDIR:H:H:H} + .include Index: head/tests/sys/net/routing/rtsock_common.h =================================================================== --- head/tests/sys/net/routing/rtsock_common.h (revision 360044) +++ head/tests/sys/net/routing/rtsock_common.h (revision 360045) @@ -1,916 +1,888 @@ /*- * 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 #include #include +#include "freebsd_test_suite/macros.h" #include "rtsock_print.h" #include "params.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); void file_append_line(char *fname, char *text); 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 char * iface_create(char *ifname_orig) { struct ifreq ifr; int s; char prefix[IFNAMSIZ], ifname[IFNAMSIZ], *result; char *src, *dst; for (src = ifname_orig, dst = prefix; *src && isalpha(*src); src++) *dst++ = *src; *dst = '\0'; - - if (_enforce_cloner_loaded(prefix) == 0) - return (NULL); memset(&ifr, 0, sizeof(struct ifreq)); s = socket(AF_LOCAL, SOCK_DGRAM, 0); strlcpy(ifr.ifr_name, ifname_orig, sizeof(ifr.ifr_name)); RLOG("creating iface %s %s", prefix, ifr.ifr_name); if (ioctl(s, SIOCIFCREATE2, &ifr) < 0) err(1, "SIOCIFCREATE2"); strlcpy(ifname, ifr.ifr_name, IFNAMSIZ); RLOG("created interface %s", ifname); result = strdup(ifname); file_append_line(IFACES_FNAME, ifname); if (strstr(ifname, "epair") == ifname) { /* call returned epairXXXa, need to add epairXXXb */ ifname[strlen(ifname) - 1] = 'b'; file_append_line(IFACES_FNAME, ifname); } return (result); } 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); } void file_append_line(char *fname, char *text) { FILE *f; f = fopen(fname, "a"); fputs(text, f); fputs("\n", f); fclose(f); } static int vnet_wait_interface(char *vnet_name, char *ifname) { char buf[512], cmd[512], *line, *token; FILE *fp; int i; snprintf(cmd, sizeof(cmd), "/usr/sbin/jexec %s /sbin/ifconfig -l", vnet_name); for (int i = 0; i < 50; i++) { fp = popen(cmd, "r"); line = fgets(buf, sizeof(buf), fp); /* cut last\n */ if (line[0]) line[strlen(line)-1] = '\0'; while ((token = strsep(&line, " ")) != NULL) { if (strcmp(token, ifname) == 0) return (1); } /* sleep 100ms */ usleep(1000 * 100); } return (0); } void vnet_switch(char *vnet_name, char **ifnames, int count) { char buf[512], cmd[512], *line; FILE *fp; int jid, len, ret; RLOG("switching to vnet %s with interface(s) %s", vnet_name, ifnames[0]); len = snprintf(cmd, sizeof(cmd), "/usr/sbin/jail -i -c name=%s persist vnet", vnet_name); for (int i = 0; i < count && len < sizeof(cmd); i++) { len += snprintf(&cmd[len], sizeof(cmd) - len, " vnet.interface=%s", ifnames[i]); } RLOG("jail cmd: \"%s\"\n", cmd); fp = popen(cmd, "r"); if (fp == NULL) atf_tc_fail("jail creation failed"); line = fgets(buf, sizeof(buf), fp); if (line == NULL) atf_tc_fail("empty output from jail(8)"); jid = strtol(line, NULL, 10); if (jid <= 0) { atf_tc_fail("invalid jail output: %s", line); } RLOG("created jail jid=%d", jid); file_append_line(JAILS_FNAME, vnet_name); /* Wait while interface appearsh inside vnet */ for (int i = 0; i < count; i++) { if (vnet_wait_interface(vnet_name, ifnames[i])) continue; atf_tc_fail("unable to move interface %s to jail %s", ifnames[i], vnet_name); } if (jail_attach(jid) == -1) { RLOG_ERRNO("jail %s attach failed: ret=%d", vnet_name, errno); atf_tc_fail("jail attach failed"); } RLOG("attached to the jail"); } void vnet_switch_one(char *vnet_name, char *ifname) { char *ifnames[1]; ifnames[0] = ifname; vnet_switch(vnet_name, ifnames, 1); } #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; struct pollfd pfd; int poll_delay = 5 * 1000; /* 5 seconds */ /* Check for the data available to read first */ memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; pfd.events = POLLIN; if (poll(&pfd, 1, poll_delay) == 0) ATF_REQUIRE_MSG(1 == 0, "rtsock read timed out (%d seconds passed)", poll_delay / 1000); 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; int found = 0; while (true) { rtm = rtsock_read_rtm(fd, buffer, buflen); if (rtm->rtm_pid == getpid() && rtm->rtm_seq == seq) found = 1; if (found) RLOG("--- MATCHED RTSOCK MESSAGE ---"); else RLOG("--- SKIPPED RTSOCK MESSAGE ---"); rtsock_print_rtm(rtm); if (found) 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_message_sockaddrs(char *buffer, int rtm_len, size_t offset, int rtm_addrs) { struct sockaddr *sa; size_t parsed_len = offset; /* Offset denotes initial header size */ sa = (struct sockaddr *)(buffer + offset); for (int i = 0; i < RTAX_MAX; i++) { if ((rtm_addrs & (1 << i)) == 0) continue; parsed_len += SA_SIZE(sa); RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len <= rtm_len, "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, rtm_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((struct rt_msghdr *)buffer, data_len <= rtm_len, "AF_LINK data size exceeds total len: %u vs %u, nlen=%d alen=%d", data_len, rtm_len, sdl->sdl_nlen, sdl->sdl_alen); } sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); } RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len == rtm_len, "message len != parsed len: expected %d parsed %d", rtm_len, (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_message_sockaddrs(buffer, rtm->rtm_msglen, sizeof(struct rt_msghdr), rtm->rtm_addrs); break; case RTM_DELADDR: case RTM_NEWADDR: _validate_message_sockaddrs(buffer, rtm->rtm_msglen, sizeof(struct ifa_msghdr), ((struct ifa_msghdr *)buffer)->ifam_addrs); break; } } void rtsock_validate_pid_ours(struct rt_msghdr *rtm) { RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == getpid(), "expected pid %d, got %d", getpid(), rtm->rtm_pid); } void rtsock_validate_pid_user(struct rt_msghdr *rtm) { RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid, got %d", rtm->rtm_pid); } void rtsock_validate_pid_kernel(struct rt_msghdr *rtm) { RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == 0, "expected zero pid, got %d", rtm->rtm_pid); } #endif Index: head/tests/sys/net/routing/rtsock_config.h =================================================================== --- head/tests/sys/net/routing/rtsock_config.h (revision 360044) +++ head/tests/sys/net/routing/rtsock_config.h (revision 360045) @@ -1,171 +1,174 @@ /*- * 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_ #include "params.h" struct rtsock_config_options { int num_interfaces; /* number of interfaces to create */ }; 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; char **ifnames; bool autocreated_interface; int rtsock_fd; int num_interfaces; }; struct rtsock_test_config * config_setup(const atf_tc_t *tc, struct rtsock_config_options *co) { struct rtsock_config_options default_co; struct rtsock_test_config *c; char buf[64], *s; const char *key; int mask; if (co == NULL) { bzero(&default_co, sizeof(default_co)); co = &default_co; co->num_interfaces = 1; } c = calloc(1, sizeof(struct rtsock_test_config)); c->rtsock_fd = -1; 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); if (co->num_interfaces > 0) { + kldload("if_epair"); + ATF_REQUIRE_KERNEL_MODULE("if_epair"); + c->ifnames = calloc(co->num_interfaces, sizeof(char *)); for (int i = 0; i < co->num_interfaces; i++) c->ifnames[i] = iface_create("epair"); c->ifname = c->ifnames[0]; c->ifindex = if_nametoindex(c->ifname); ATF_REQUIRE_MSG(c->ifindex != 0, "interface %s not found", c->ifname); } c->num_interfaces = co->num_interfaces; 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(const atf_tc_t *tc) { const char *srcdir = atf_tc_get_config_var(tc, "srcdir"); char cmd[512]; int ret; /* XXX: sleep 100ms to avoid epair qflush panic */ usleep(1000 * 100); snprintf(cmd, sizeof(cmd), "%s/generic_cleanup.sh", srcdir); ret = system(cmd); if (ret != 0) RLOG("'%s' failed, error %d", cmd, ret); } 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