Date: Sat, 28 Dec 2019 12:16:41 +0000 (UTC) From: "Alexander V. Chernikov" <melifaro@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r356146 - in head/tests/sys/net: . routing Message-ID: <201912281216.xBSCGf1R017785@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: melifaro Date: Sat Dec 28 12:16:40 2019 New Revision: 356146 URL: https://svnweb.freebsd.org/changeset/base/356146 Log: Add userland tests for route table/lltable rtsock operations. MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D22860 Added: head/tests/sys/net/routing/ head/tests/sys/net/routing/Makefile (contents, props changed) head/tests/sys/net/routing/rtsock_common.h (contents, props changed) head/tests/sys/net/routing/rtsock_config.h (contents, props changed) head/tests/sys/net/routing/rtsock_print.h (contents, props changed) head/tests/sys/net/routing/test_rtsock_l3.c (contents, props changed) head/tests/sys/net/routing/test_rtsock_lladdr.c (contents, props changed) Modified: head/tests/sys/net/Makefile Modified: head/tests/sys/net/Makefile ============================================================================== --- head/tests/sys/net/Makefile Sat Dec 28 06:56:21 2019 (r356145) +++ head/tests/sys/net/Makefile Sat Dec 28 12:16:40 2019 (r356146) @@ -10,6 +10,8 @@ ATF_TESTS_SH+= if_clone_test 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. Added: head/tests/sys/net/routing/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tests/sys/net/routing/Makefile Sat Dec 28 12:16:40 2019 (r356146) @@ -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> Added: head/tests/sys/net/routing/rtsock_common.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tests/sys/net/routing/rtsock_common.h Sat Dec 28 12:16:40 2019 (r356146) @@ -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 Added: head/tests/sys/net/routing/rtsock_config.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tests/sys/net/routing/rtsock_config.h Sat Dec 28 12:16:40 2019 (r356146) @@ -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 Added: head/tests/sys/net/routing/rtsock_print.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tests/sys/net/routing/rtsock_print.h Sat Dec 28 12:16:40 2019 (r356146) @@ -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_ + + *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201912281216.xBSCGf1R017785>