Index: head/tests/sys/net/if_clone_test.sh =================================================================== --- head/tests/sys/net/if_clone_test.sh (revision 357995) +++ head/tests/sys/net/if_clone_test.sh (revision 357996) @@ -1,560 +1,560 @@ # # Copyright (c) 2014 Spectra Logic Corporation # All rights reserved. # # 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, # without modification. # 2. Redistributions in binary form must reproduce at minimum a disclaimer # substantially similar to the "NO WARRANTY" disclaimer below # ("Disclaimer") and any redistribution must be conditioned upon # including a substantially similar Disclaimer requirement for further # binary redistribution. # # NO WARRANTY # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. # # Authors: Alan Somers (Spectra Logic Corporation) # # $FreeBSD$ # Outline: # For each cloned interface type, do three tests # 1) Create and destroy it # 2) Create, up, and destroy it # 3) Create, disable IPv6 auto address assignment, up, and destroy it TESTLEN=10 # seconds atf_test_case epair_stress cleanup epair_stress_head() { atf_set "descr" "Simultaneously create and destroy an epair(4)" atf_set "require.user" "root" } epair_stress_body() { do_stress "epair" } epair_stress_cleanup() { cleanup_ifaces } atf_test_case epair_up_stress cleanup epair_up_stress_head() { atf_set "descr" "Simultaneously up and detroy an epair(4)" atf_set "require.user" "root" } epair_up_stress_body() { do_up_stress "epair" "" "" } epair_up_stress_cleanup() { cleanup_ifaces } atf_test_case epair_ipv6_up_stress cleanup epair_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy an epair(4) with IPv6" atf_set "require.user" "root" } epair_ipv6_up_stress_body() { atf_skip "Quickly panics: page fault in in6_unlink_ifa (PR 225438)" do_up_stress "epair" "6" "" } epair_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case faith_stress cleanup faith_stress_head() { atf_set "descr" "Simultaneously create and destroy a faith(4)" atf_set "require.user" "root" } faith_stress_body() { do_stress "faith" } faith_stress_cleanup() { cleanup_ifaces } atf_test_case faith_up_stress cleanup faith_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a faith(4)" atf_set "require.user" "root" } faith_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "faith" "" "" } faith_up_stress_cleanup() { cleanup_ifaces } atf_test_case faith_ipv6_up_stress cleanup faith_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a faith(4) with IPv6" atf_set "require.user" "root" } faith_ipv6_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "faith" "6" "" } faith_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case gif_stress cleanup gif_stress_head() { atf_set "descr" "Simultaneously create and destroy a gif(4)" atf_set "require.user" "root" } gif_stress_body() { do_stress "gif" } gif_stress_cleanup() { cleanup_ifaces } atf_test_case gif_up_stress cleanup gif_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a gif(4)" atf_set "require.user" "root" } gif_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "gif" "" "p2p" } gif_up_stress_cleanup() { cleanup_ifaces } atf_test_case gif_ipv6_up_stress cleanup gif_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a gif(4) with IPv6" atf_set "require.user" "root" } gif_ipv6_up_stress_body() { atf_skip "Quickly panics: rt_tables_get_rnh_ptr: fam out of bounds." do_up_stress "gif" "6" "p2p" } gif_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case lo_stress cleanup lo_stress_head() { atf_set "descr" "Simultaneously create and destroy an lo(4)" atf_set "require.user" "root" } lo_stress_body() { do_stress "lo" } lo_stress_cleanup() { cleanup_ifaces } atf_test_case lo_up_stress cleanup lo_up_stress_head() { atf_set "descr" "Simultaneously up and destroy an lo(4)" atf_set "require.user" "root" } lo_up_stress_body() { atf_skip "Quickly panics: GPF in rtsock_routemsg" do_up_stress "lo" "" "" } lo_up_stress_cleanup() { cleanup_ifaces } atf_test_case lo_ipv6_up_stress cleanup lo_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy an lo(4) with IPv6" atf_set "require.user" "root" } lo_ipv6_up_stress_body() { atf_skip "Quickly panics: page fault in rtsock_addrmsg" do_up_stress "lo" "6" "" } lo_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case tap_stress cleanup tap_stress_head() { atf_set "descr" "Simultaneously create and destroy a tap(4)" atf_set "require.user" "root" } tap_stress_body() { do_stress "tap" } tap_stress_cleanup() { cleanup_ifaces } atf_test_case tap_up_stress cleanup tap_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a tap(4)" atf_set "require.user" "root" } tap_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "tap" "" "" } tap_up_stress_cleanup() { cleanup_ifaces } atf_test_case tap_ipv6_up_stress cleanup tap_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a tap(4) with IPv6" atf_set "require.user" "root" } tap_ipv6_up_stress_body() { atf_skip "Quickly panics: if_delmulti_locked: inconsistent ifp 0xfffff80150e44000" do_up_stress "tap" "6" "" } tap_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case tun_stress cleanup tun_stress_head() { atf_set "descr" "Simultaneously create and destroy a tun(4)" atf_set "require.user" "root" } tun_stress_body() { do_stress "tun" } tun_stress_cleanup() { cleanup_ifaces } atf_test_case tun_up_stress cleanup tun_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a tun(4)" atf_set "require.user" "root" } tun_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "tun" "" "p2p" } tun_up_stress_cleanup() { cleanup_ifaces } atf_test_case tun_ipv6_up_stress cleanup tun_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a tun(4) with IPv6" atf_set "require.user" "root" } tun_ipv6_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "tun" "6" "p2p" } tun_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case vlan_stress cleanup vlan_stress_head() { atf_set "descr" "Simultaneously create and destroy a vlan(4)" atf_set "require.user" "root" } vlan_stress_body() { do_stress "vlan" } vlan_stress_cleanup() { cleanup_ifaces } atf_test_case vlan_up_stress cleanup vlan_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a vlan(4)" atf_set "require.user" "root" } vlan_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "vlan" "" "" } vlan_up_stress_cleanup() { cleanup_ifaces } atf_test_case vlan_ipv6_up_stress cleanup vlan_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a vlan(4) with IPv6" atf_set "require.user" "root" } vlan_ipv6_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "vlan" "6" "" } vlan_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_test_case vmnet_stress cleanup vmnet_stress_head() { atf_set "descr" "Simultaneously create and destroy a vmnet(4)" atf_set "require.user" "root" } vmnet_stress_body() { do_stress "vmnet" } vmnet_stress_cleanup() { cleanup_ifaces } atf_test_case vmnet_up_stress cleanup vmnet_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a vmnet(4)" atf_set "require.user" "root" } vmnet_up_stress_body() { do_up_stress "vmnet" "" "" } vmnet_up_stress_cleanup() { cleanup_ifaces } atf_test_case vmnet_ipv6_up_stress cleanup vmnet_ipv6_up_stress_head() { atf_set "descr" "Simultaneously up and destroy a vmnet(4) with IPv6" atf_set "require.user" "root" } vmnet_ipv6_up_stress_body() { atf_skip "Quickly panics: if_freemulti: protospec not NULL" do_up_stress "vmnet" "6" "" } vmnet_ipv6_up_stress_cleanup() { cleanup_ifaces } atf_init_test_cases() { atf_add_test_case epair_ipv6_up_stress atf_add_test_case epair_stress atf_add_test_case epair_up_stress atf_add_test_case faith_ipv6_up_stress atf_add_test_case faith_stress atf_add_test_case faith_up_stress atf_add_test_case gif_ipv6_up_stress atf_add_test_case gif_stress atf_add_test_case gif_up_stress # Don't test lagg; it has its own test program atf_add_test_case lo_ipv6_up_stress atf_add_test_case lo_stress atf_add_test_case lo_up_stress atf_add_test_case tap_ipv6_up_stress atf_add_test_case tap_stress atf_add_test_case tap_up_stress atf_add_test_case tun_ipv6_up_stress atf_add_test_case tun_stress atf_add_test_case tun_up_stress atf_add_test_case vlan_ipv6_up_stress atf_add_test_case vlan_stress atf_add_test_case vlan_up_stress atf_add_test_case vmnet_ipv6_up_stress atf_add_test_case vmnet_stress atf_add_test_case vmnet_up_stress } do_stress() { local IFACE CREATOR_PID DESTROYER_PID IFACE=`get_iface $1` # First thread: create the interface while true; do ifconfig ${IFACE%a} create 2>/dev/null && \ echo -n . >> creator_count.txt done & CREATOR_PID=$! # Second thread: destroy the lagg - while true; do + while true; do ifconfig $IFACE destroy 2>/dev/null && \ echo -n . >> destroyer_count.txt done & DESTROYER_PID=$! sleep ${TESTLEN} kill $CREATOR_PID kill $DESTROYER_PID echo "Created ${IFACE%a} `stat -f %z creator_count.txt` times." echo "Destroyed it `stat -f %z destroyer_count.txt` times." } # Implement the up stress tests # Parameters # $1 Interface class, etc "lo" or "tap" # $2 "6" to enable IPv6 auto address assignment, anything else otherwise # $3 p2p for point to point interfaces, anything else for normal interfaces do_up_stress() { local ADDR DSTADDR MASK MEAN_SLEEP_SECONDS MAX_SLEEP_USECS \ IFACE IPV6 P2P SRCDIR LOOP_PID ipv6_cmd up_cmd # Configure the interface to use an RFC5737 nonrouteable addresses ADDR="192.0.2.2" DSTADDR="192.0.2.3" MASK="24" # ifconfig takes about 10ms to run. To increase race coverage, # randomly delay the two commands relative to each other by 5ms either # way. MEAN_SLEEP_SECONDS=.005 MAX_SLEEP_USECS=10000 IFACE=`get_iface $1` IPV6=$2 P2P=$3 SRCDIR=$( atf_get_srcdir ) if [ "$IPV6" = 6 ]; then ipv6_cmd="true" else ipv6_cmd="ifconfig $IFACE inet6 ifdisabled" fi if [ "$P2P" = "p2p" ]; then up_cmd="ifconfig $IFACE up ${ADDR}/${MASK} ${DSTADDR}" else up_cmd="ifconfig $IFACE up ${ADDR}/${MASK}" fi while true; do eval "$ipv6_cmd" { sleep ${MEAN_SLEEP_SECONDS} && \ eval "$up_cmd" 2> /dev/null && echo -n . >> up_count.txt ; } & { ${SRCDIR}/randsleep ${MAX_SLEEP_USECS} && \ ifconfig $IFACE destroy && echo -n . >> destroy_count.txt ; } & wait ifconfig ${IFACE%a} create done & LOOP_PID=$! sleep ${TESTLEN} kill $LOOP_PID echo "Upped ${IFACE} `stat -f %z up_count.txt` times." echo "Destroyed it `stat -f %z destroy_count.txt` times." } # Creates a new cloned interface, registers it for cleanup, and echoes it # params: $1 Interface class name (tap, gif, etc) get_iface() { local CLASS DEV N CLASS=$1 N=0 while ! ifconfig ${CLASS}${N} create > /dev/null 2>&1; do if [ "$N" -ge 8 ]; then atf_skip "Could not create a ${CLASS} interface" else N=$(($N + 1)) fi done if [ ${CLASS} = "epair" ]; then DEV=${CLASS}${N}a else DEV=${CLASS}${N} fi # Record the device so we can clean it up later echo ${DEV} >> "devices_to_cleanup" echo ${DEV} } cleanup_ifaces() { local DEV for DEV in `cat "devices_to_cleanup"`; do ifconfig ${DEV} destroy done true } Index: head/tests/sys/net/routing/rtsock_common.h =================================================================== --- head/tests/sys/net/routing/rtsock_common.h (revision 357995) +++ head/tests/sys/net/routing/rtsock_common.h (revision 357996) @@ -1,806 +1,806 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Alexander V. Chernikov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _NET_ROUTING_RTSOCK_COMMON_H_ #define _NET_ROUTING_RTSOCK_COMMON_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "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; struct pollfd pfd; int poll_delay = 5 * 1000; /* 5 seconds */ /* Check for the data available to read first */ memset(&pfd, 0, sizeof(pfd)); pfd.fd = fd; pfd.events = POLLIN; if (poll(&pfd, 1, poll_delay) == 0) ATF_REQUIRE_MSG(1 == 0, "rtsock read timed out (%d seconds passed)", poll_delay / 1000); len = read(fd, buffer, buflen); int my_errno = errno; ATF_REQUIRE_MSG(len > 0, "rtsock read failed: %s", strerror(my_errno)); rtsock_validate_message(buffer, len); return ((struct rt_msghdr *)buffer); } struct rt_msghdr * rtsock_read_rtm_reply(int fd, char *buffer, size_t buflen, int seq) { struct rt_msghdr *rtm; 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_message_sockaddrs(char *buffer, int rtm_len, size_t offset, int rtm_addrs) { struct sockaddr *sa; size_t parsed_len = offset; /* Offset denotes initial header size */ sa = (struct sockaddr *)(buffer + offset); for (int i = 0; i < RTAX_MAX; i++) { if ((rtm_addrs & (1 << i)) == 0) continue; parsed_len += SA_SIZE(sa); RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len <= rtm_len, "SA %d: len %d exceeds msg size %d", i, (int)sa->sa_len, rtm_len); if (sa->sa_family == AF_LINK) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa; int data_len = sdl->sdl_nlen + sdl->sdl_alen; data_len += offsetof(struct sockaddr_dl, sdl_data); RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, data_len <= rtm_len, "AF_LINK data size exceeds total len: %u vs %u, nlen=%d alen=%d", data_len, rtm_len, sdl->sdl_nlen, sdl->sdl_alen); } sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); } RTSOCK_ATF_REQUIRE_MSG((struct rt_msghdr *)buffer, parsed_len == rtm_len, "message len != parsed len: expected %d parsed %d", rtm_len, (int)parsed_len); } /* * Raises error if base syntax checks fails. */ void rtsock_validate_message(char *buffer, ssize_t len) { struct rt_msghdr *rtm; ATF_REQUIRE_MSG(len > 0, "read() return %zd, error: %s", len, strerror(errno)); rtm = (struct rt_msghdr *)buffer; ATF_REQUIRE_MSG(rtm->rtm_version == RTM_VERSION, "unknown RTM_VERSION: expected %d got %d", RTM_VERSION, rtm->rtm_version); ATF_REQUIRE_MSG(rtm->rtm_msglen <= len, "wrong message length: expected %d got %d", (int)len, (int)rtm->rtm_msglen); switch (rtm->rtm_type) { case RTM_GET: case RTM_ADD: case RTM_DELETE: case RTM_CHANGE: _validate_message_sockaddrs(buffer, rtm->rtm_msglen, sizeof(struct rt_msghdr), rtm->rtm_addrs); break; case RTM_DELADDR: case RTM_NEWADDR: _validate_message_sockaddrs(buffer, rtm->rtm_msglen, sizeof(struct ifa_msghdr), ((struct ifa_msghdr *)buffer)->ifam_addrs); break; } } void rtsock_validate_pid_ours(struct rt_msghdr *rtm) { RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == getpid(), "expected pid %d, got %d", getpid(), rtm->rtm_pid); } void rtsock_validate_pid_user(struct rt_msghdr *rtm) { RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid > 0, "expected non-zero pid, got %d", rtm->rtm_pid); } void rtsock_validate_pid_kernel(struct rt_msghdr *rtm) { RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_pid == 0, "expected zero pid, got %d", rtm->rtm_pid); } #endif Index: head/tests/sys/net/routing/rtsock_print.h =================================================================== --- head/tests/sys/net/routing/rtsock_print.h (revision 357995) +++ head/tests/sys/net/routing/rtsock_print.h (revision 357996) @@ -1,384 +1,384 @@ /*- * 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_message(_rtm); \ } \ ATF_REQUIRE_MSG(_cond, _fmt, ##__VA_ARGS__); \ } while (0); #define RTSOCKHD_ATF_REQUIRE_MSG(_rtm, _cond, _fmt, ...) do { \ if (!(_cond)) { \ printf("-- CONDITION FAILED, rtm hexdump--\n\n");\ rtsock_print_message_hd(_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]); } char * rtsock_print_rtm_flags(char *buf, int buflen, int rtm_flags) { _printb(buf, buflen, rtm_flags, routeflags); return (buf); } #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] && data[i] == 0x00) { 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("x%02X", v); else _PRINTX(", x%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: locks: inits: sockaddrs: */ 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); + 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]; rtsock_print_rtm_flags(flags_buf, sizeof(flags_buf), rtm->rtm_flags); 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); if (rtm->rtm_inits > 0) { _printb(flags_buf, sizeof(flags_buf), rtm->rtm_inits, metricnames); printf("metrics: %s\n", flags_buf); if (rtm->rtm_inits & RTV_MTU) printf("mtu: %lu\n", rtm->rtm_rmx.rmx_mtu); if (rtm->rtm_inits & RTV_EXPIRE) { struct timeval tv; gettimeofday(&tv, NULL); printf("expire: %d (%lu raw)\n", (int)(rtm->rtm_rmx.rmx_expire - tv.tv_sec), rtm->rtm_rmx.rmx_expire); } } _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"); } void rtsock_print_ifa(struct ifa_msghdr *ifam) { struct timeval tv; struct tm tm_res; char buf[64]; gettimeofday(&tv, NULL); - localtime_r(&tv.tv_sec, &tm_res); + localtime_r(&tv.tv_sec, &tm_res); strftime(buf, sizeof(buf), "%F %T", &tm_res); printf("Got message of size %hu on %s\n", ifam->ifam_msglen, buf); char flags_buf[256]; _printb(flags_buf, sizeof(flags_buf), ifam->ifam_flags, routeflags); printf("%s: len %hu, ifindex: %d, flags: %s\n", msgtypes[ifam->ifam_type], ifam->ifam_msglen, ifam->ifam_index, flags_buf); _printb(flags_buf, sizeof(flags_buf), ifam->ifam_addrs, addrnames); printf("sockaddrs: 0x%X %s\n", ifam->ifam_addrs, flags_buf); char *ptr = (char *)(ifam + 1); for (int i = 0; i < RTAX_MAX; i++) { if (ifam->ifam_addrs & (1 << i)) { struct sockaddr *sa = (struct sockaddr *)ptr; sa_print(sa, 1); /* add */ ptr += ALIGN(((struct sockaddr *)ptr)->sa_len); } } printf("\n"); } void rtsock_print_message_hd(struct rt_msghdr *rtm) { struct timeval tv; struct tm tm_res; char buf[64]; char dumpbuf[2048]; gettimeofday(&tv, NULL); - localtime_r(&tv.tv_sec, &tm_res); + localtime_r(&tv.tv_sec, &tm_res); strftime(buf, sizeof(buf), "%F %T", &tm_res); printf("Got message type %s of size %hu on %s\n", rtsock_print_cmdtype(rtm->rtm_type), rtm->rtm_msglen, buf); sa_print_hd(dumpbuf, sizeof(dumpbuf), (char *)rtm, rtm->rtm_msglen); printf(" %s\n", dumpbuf); } void rtsock_print_message(struct rt_msghdr *rtm) { switch (rtm->rtm_type) { case RTM_GET: case RTM_ADD: case RTM_DELETE: case RTM_CHANGE: rtsock_print_rtm(rtm); break; case RTM_DELADDR: case RTM_NEWADDR: rtsock_print_ifa((struct ifa_msghdr *)rtm); break; default: printf("unknown rt message type %X\n", rtm->rtm_type); } } #endif Index: head/tests/sys/net/routing/test_rtsock_l3.c =================================================================== --- head/tests/sys/net/routing/test_rtsock_l3.c (revision 357995) +++ head/tests/sys/net/routing/test_rtsock_l3.c (revision 357996) @@ -1,1017 +1,1017 @@ /*- * 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" #include "sys/types.h" #include #include #include "net/bpf.h" static inline struct rtsock_test_config * presetup_ipv6_iface(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); return (c); } static inline struct rtsock_test_config * presetup_ipv6(const atf_tc_t *tc) { struct rtsock_test_config *c; int ret; c = presetup_ipv6_iface(tc); 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_iface(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); /* 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); } return (c); } static inline struct rtsock_test_config * presetup_ipv4(const atf_tc_t *tc) { struct rtsock_test_config *c; int ret; c = presetup_ipv4_iface(tc); /* assumes ifconfig doing IFF_UP */ ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); ATF_REQUIRE_MSG(ret == 0, "ifconfig failed"); 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)); ret = 1; 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); } } 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); if (rtm->rtm_flags != rtm_flags) { char got_flags[64], expected_flags[64]; rtsock_print_rtm_flags(got_flags, sizeof(got_flags), rtm->rtm_flags); rtsock_print_rtm_flags(expected_flags, sizeof(expected_flags), rtm_flags); RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == rtm_flags, "expected flags: 0x%X %s, got 0x%X %s", rtm_flags, expected_flags, rtm->rtm_flags, got_flags); } } static void verify_link_gateway(struct rt_msghdr *rtm, int ifindex) { struct sockaddr *sa; struct sockaddr_dl *sdl; 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); sdl = (struct sockaddr_dl *)sa; RTSOCK_ATF_REQUIRE_MSG(rtm, sdl->sdl_index == ifindex, "GW ifindex is %d", sdl->sdl_index); } /* 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)) #define RTM_DECLARE_ROOT_TEST(_name, _descr) \ ATF_TC_WITH_CLEANUP(_name); \ ATF_TC_HEAD(_name, tc) \ { \ DESCRIBE_ROOT_TEST(_descr); \ } \ ATF_TC_CLEANUP(_name, tc) \ { \ CLEANUP_AFTER_TEST; \ } 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: * sockaddrs: 0x7 * 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 */ verify_link_gateway(rtm, c->ifindex); 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_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); /* * RTM_GET: Report Metrics: len 312, pid: 67074, seq 1, errno 0, flags: * locks: inits: * sockaddrs: * 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: * locks: inits: * sockaddrs: * 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: * sockaddrs: 0x7 * 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: * locks: inits: * sockaddrs: * 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: * sockaddrs: 0x7 * 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_TC_WITH_CLEANUP(rtm_add_v4_temporal1_success); ATF_TC_HEAD(rtm_add_v4_temporal1_success, tc) { DESCRIBE_ROOT_TEST("Tests IPv4 route expiration with expire time set"); } ATF_TC_BODY(rtm_add_v4_temporal1_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); /* Set expire time to now */ struct timeval tv; gettimeofday(&tv, NULL); rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1; rtm->rtm_inits |= RTV_EXPIRE; rtsock_send_rtm(c->rtsock_fd, rtm); rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD"); RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set"); /* The next should be route deletion */ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&net4, (struct sockaddr *)&mask4, (struct sockaddr *)&gw4); verify_route_message_extra(rtm, c->ifindex, RTF_GATEWAY | RTF_DONE | RTF_STATIC); } ATF_TC_CLEANUP(rtm_add_v4_temporal1_success, tc) { CLEANUP_AFTER_TEST; } ATF_TC_WITH_CLEANUP(rtm_add_v6_temporal1_success); ATF_TC_HEAD(rtm_add_v6_temporal1_success, tc) { DESCRIBE_ROOT_TEST("Tests IPv6 global unicast prefix addition with directly-reachable GU GW"); } ATF_TC_BODY(rtm_add_v6_temporal1_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); /* Set expire time to now */ struct timeval tv; gettimeofday(&tv, NULL); rtm->rtm_rmx.rmx_expire = tv.tv_sec - 1; rtm->rtm_inits |= RTV_EXPIRE; rtsock_send_rtm(c->rtsock_fd, rtm); rtm = rtsock_read_rtm_reply(c->rtsock_fd, buffer, sizeof(buffer), rtm->rtm_seq); ATF_REQUIRE_MSG(rtm != NULL, "unable to get rtsock reply for RTM_ADD"); RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_inits & RTV_EXPIRE, "RTV_EXPIRE not set"); /* The next should be route deletion */ rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); verify_route_message(rtm, RTM_DELETE, (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_GATEWAY | RTF_DONE | RTF_STATIC); } ATF_TC_CLEANUP(rtm_add_v6_temporal1_success, tc) { CLEANUP_AFTER_TEST; } /* Interface address messages tests */ RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_hostroute_success, "Tests validness for /128 host route announce after ifaddr assignment"); ATF_TC_BODY(rtm_add_v6_gu_ifa_hostroute_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv6_iface(tc); c->rtsock_fd = rtsock_setup_socket(); ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); /* * There will be multiple. * RTM_ADD without llinfo. */ while (true) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLINFO) == 0)) break; } /* This should be a message for the host route */ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->addr6, NULL, NULL); rtsock_validate_pid_kernel(rtm); /* No netmask should be set */ RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set"); /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); int expected_rt_flags = RTF_UP | RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED; verify_route_message_extra(rtm, if_nametoindex("lo0"), expected_rt_flags); } RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_prefixroute_success, "Tests validness for the prefix route announce after ifaddr assignment"); ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv6_iface(tc); c->rtsock_fd = rtsock_setup_socket(); ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); /* * Multiple RTM_ADD messages will be generated: * 1) lladdr mapping (RTF_LLDATA) * 2) host route (one w/o netmask) * 3) prefix route */ while (true) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) break; } /* This should be a message for the prefix route */ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net6, (struct sockaddr *)&c->mask6, NULL); /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); /* TODO: PINNED? */ int expected_rt_flags = RTF_UP | RTF_DONE; verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); } RTM_DECLARE_ROOT_TEST(rtm_add_v6_gu_ifa_ordered_success, "Tests ordering of the messages for IPv6 global unicast ifaddr assignment"); ATF_TC_BODY(rtm_add_v6_gu_ifa_ordered_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv6_iface(tc); c->rtsock_fd = rtsock_setup_socket(); ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); int count = 0, tries = 0; enum msgtype { MSG_IFADDR, MSG_HOSTROUTE, MSG_PREFIXROUTE, MSG_MAX, }; int msg_array[MSG_MAX]; bzero(msg_array, sizeof(msg_array)); while (count < 3 && tries < 20) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); tries++; /* Classify */ if (rtm->rtm_type == RTM_NEWADDR) { RLOG("MSG_IFADDR: %d", count); msg_array[MSG_IFADDR] = count++; continue; } /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) { RLOG("MSG_PREFIXROUTE: %d", count); msg_array[MSG_PREFIXROUTE] = count++; continue; } if ((rtm->rtm_type == RTM_ADD) && ((rtm->rtm_flags & RTF_LLDATA) == 0)) { RLOG("MSG_HOSTROUTE: %d", count); msg_array[MSG_HOSTROUTE] = count++; continue; } RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type), tries); } /* TODO: verify multicast */ ATF_REQUIRE_MSG(count == 3, "Received only %d/3 messages", count); ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first"); } RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_hostroute_success, "Tests validness for /128 host route removal after ifaddr removal"); ATF_TC_BODY(rtm_del_v6_gu_ifa_hostroute_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv6_iface(tc); ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); c->rtsock_fd = rtsock_setup_socket(); ret = iface_delete_addr(c->ifname, c->addr6_str); while (true) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); if ((rtm->rtm_type == RTM_DELETE) && ((rtm->rtm_flags & RTF_LLINFO) == 0) && rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL) break; } /* This should be a message for the host route */ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->addr6, NULL, NULL); rtsock_validate_pid_kernel(rtm); /* No netmask should be set */ RTSOCK_ATF_REQUIRE_MSG(rtm, rtsock_find_rtm_sa(rtm, RTA_NETMASK) == NULL, "netmask is set"); /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); /* XXX: consider passing ifindex in rtm_index as done in RTM_ADD. */ int expected_rt_flags = RTF_HOST | RTF_DONE | RTF_STATIC | RTF_PINNED; RTSOCK_ATF_REQUIRE_MSG(rtm, rtm->rtm_flags == expected_rt_flags, "expected rtm flags: 0x%X, got 0x%X", expected_rt_flags, rtm->rtm_flags); } RTM_DECLARE_ROOT_TEST(rtm_del_v6_gu_ifa_prefixroute_success, "Tests validness for the prefix route removal after ifaddr assignment"); ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv6_iface(tc); ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); c->rtsock_fd = rtsock_setup_socket(); ret = iface_delete_addr(c->ifname, c->addr6_str); while (true) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); /* Find RTM_DELETE with netmask - this should skip both host route and LLADDR */ if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) break; } /* This should be a message for the prefix route */ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net6, (struct sockaddr *)&c->mask6, NULL); /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); int expected_rt_flags = RTF_DONE; verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); } RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_prefixroute_success, "Tests validness for the prefix route announce after ifaddr assignment"); ATF_TC_BODY(rtm_add_v4_gu_ifa_prefixroute_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv4_iface(tc); c->rtsock_fd = rtsock_setup_socket(); ret = iface_setup_addr(c->ifname, c->addr6_str, c->plen6); /* * Multiple RTM_ADD messages will be generated: * 1) lladdr mapping (RTF_LLDATA) * 3) prefix route */ while (true) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) break; } /* This should be a message for the prefix route */ verify_route_message(rtm, RTM_ADD, (struct sockaddr *)&c->net4, (struct sockaddr *)&c->mask4, NULL); /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED; verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); } RTM_DECLARE_ROOT_TEST(rtm_add_v4_gu_ifa_ordered_success, "Tests ordering of the messages for IPv4 unicast ifaddr assignment"); ATF_TC_BODY(rtm_add_v4_gu_ifa_ordered_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv4_iface(tc); c->rtsock_fd = rtsock_setup_socket(); ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); int count = 0, tries = 0; enum msgtype { MSG_IFADDR, MSG_PREFIXROUTE, MSG_MAX, }; int msg_array[MSG_MAX]; bzero(msg_array, sizeof(msg_array)); while (count < 2 && tries < 20) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); tries++; /* Classify */ if (rtm->rtm_type == RTM_NEWADDR) { RLOG("MSG_IFADDR: %d", count); msg_array[MSG_IFADDR] = count++; continue; } /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ if ((rtm->rtm_type == RTM_ADD) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) { RLOG("MSG_PREFIXROUTE: %d", count); msg_array[MSG_PREFIXROUTE] = count++; continue; } RLOG("skipping msg type %s, try: %d", rtsock_print_cmdtype(rtm->rtm_type), tries); } /* TODO: verify multicast */ ATF_REQUIRE_MSG(count == 2, "Received only %d/2 messages", count); ATF_REQUIRE_MSG(msg_array[MSG_IFADDR] == 0, "ifaddr message is not the first"); } RTM_DECLARE_ROOT_TEST(rtm_del_v4_gu_ifa_prefixroute_success, "Tests validness for the prefix route removal after ifaddr assignment"); ATF_TC_BODY(rtm_del_v4_gu_ifa_prefixroute_success, tc) { DECLARE_TEST_VARS; c = presetup_ipv4_iface(tc); ret = iface_setup_addr(c->ifname, c->addr4_str, c->plen4); c->rtsock_fd = rtsock_setup_socket(); ret = iface_delete_addr(c->ifname, c->addr4_str); while (true) { rtm = rtsock_read_rtm(c->rtsock_fd, buffer, sizeof(buffer)); /* Find RTM_ADD with netmask - this should skip both host route and LLADDR */ if ((rtm->rtm_type == RTM_DELETE) && (rtsock_find_rtm_sa(rtm, RTA_NETMASK))) break; } /* This should be a message for the prefix route */ verify_route_message(rtm, RTM_DELETE, (struct sockaddr *)&c->net4, (struct sockaddr *)&c->mask4, NULL); /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); int expected_rt_flags = RTF_DONE | RTF_PINNED; verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); } 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); /* ifaddr tests */ ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_hostroute_success); ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_prefixroute_success); ATF_TP_ADD_TC(tp, rtm_add_v6_gu_ifa_ordered_success); ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_hostroute_success); ATF_TP_ADD_TC(tp, rtm_del_v6_gu_ifa_prefixroute_success); ATF_TP_ADD_TC(tp, rtm_add_v4_gu_ifa_ordered_success); ATF_TP_ADD_TC(tp, rtm_del_v4_gu_ifa_prefixroute_success); /* temporal routes */ ATF_TP_ADD_TC(tp, rtm_add_v4_temporal1_success); ATF_TP_ADD_TC(tp, rtm_add_v6_temporal1_success); return (atf_no_error()); }