Add userland tests for route table/lltable rtsock operations.
MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D22860
This commit is contained in:
parent
863dc8aff0
commit
775dc8613d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=356146
@ -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.
|
||||
|
14
tests/sys/net/routing/Makefile
Normal file
14
tests/sys/net/routing/Makefile
Normal file
@ -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>
|
766
tests/sys/net/routing/rtsock_common.h
Normal file
766
tests/sys/net/routing/rtsock_common.h
Normal file
@ -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
|
164
tests/sys/net/routing/rtsock_config.h
Normal file
164
tests/sys/net/routing/rtsock_config.h
Normal file
@ -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
|
280
tests/sys/net/routing/rtsock_print.h
Normal file
280
tests/sys/net/routing/rtsock_print.h
Normal file
@ -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
|
521
tests/sys/net/routing/test_rtsock_l3.c
Normal file
521
tests/sys/net/routing/test_rtsock_l3.c
Normal file
@ -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());
|
||||
}
|
||||
|
459
tests/sys/net/routing/test_rtsock_lladdr.c
Normal file
459
tests/sys/net/routing/test_rtsock_lladdr.c
Normal file
@ -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());
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user