svn commit: r356146 - in head/tests/sys/net: . routing

Alexander V. Chernikov melifaro at FreeBSD.org
Sat Dec 28 12:16:42 UTC 2019


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 ***


More information about the svn-src-all mailing list