Index: sys/netinet/in.c =================================================================== --- sys/netinet/in.c +++ sys/netinet/in.c @@ -78,6 +78,8 @@ static void in_socktrim(struct sockaddr_in *); static void in_purgemaddrs(struct ifnet *); +static void in_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info); + static VNET_DEFINE(int, nosameprefix); #define V_nosameprefix VNET(nosameprefix) SYSCTL_INT(_net_inet_ip, OID_AUTO, no_same_prefix, CTLFLAG_VNET | CTLFLAG_RW, @@ -401,6 +403,7 @@ ifa->ifa_netmask = (struct sockaddr *)&ia->ia_sockmask; callout_init_rw(&ia->ia_garp_timer, &ifp->if_addr_lock, CALLOUT_RETURNUNLOCKED); + ifa->ifa_rtrequest = in_rtrequest; ia->ia_ifp = ifp; ia->ia_addr = *addr; @@ -1497,3 +1500,56 @@ lltable_free(ii->ii_llt); free(ii, M_IFADDR); } + +/* + * Return true if the route described by info is the local subnet route for + * ifa, or false otherwise. + */ +static int +in_is_subnet_route(struct ifaddr *ifa, struct rt_addrinfo *info) +{ + struct sockaddr *tmp_sa; + struct sockaddr_storage tmp_storage; + + if (info->rti_info[RTAX_DST] == NULL || info->rti_info[RTAX_NETMASK] == NULL) + return (0); + + /* First test that the ifaddr falls into the subnet described by the route. */ + tmp_sa = (struct sockaddr*)&tmp_storage; + rt_maskedcopy(ifa->ifa_addr, tmp_sa, ifa->ifa_netmask); + + if (!sa_equal(tmp_sa, info->rti_info[RTAX_DST])) + return (0); + + /* + * Now test that the subnet mask is the same for the address and + * the route. If they are different then the route has a different + * prefix size and ifa just happens to fall within that route. + * + * For some reason ifa->ifa_netmask sa_len field is not + * sizeof(struct sockaddr_in). This causes attempts to directly + * compare against info->rti_info[RTAX_NETMASK] to incorrectly + * fail even if the netmasks are the same. Fix this by making a + * copy of ifa->ifa_netmask with the length field correct. + */ + bzero(tmp_sa, sizeof(struct sockaddr_in)); + memcpy(tmp_sa, ifa->ifa_netmask, ifa->ifa_netmask->sa_len); + tmp_sa->sa_len = sizeof(struct sockaddr_in); + + return (sa_equal(info->rti_info[RTAX_NETMASK], tmp_sa)); +} + +static void +in_rtrequest(int req, struct rtentry *rt, struct rt_addrinfo *info) +{ + + /* + * If we are modifying a route to a subnet, we need to migrate the IFA_ROUTE + * flag to the new source ifa for the route. + */ + if (req == RTM_DELETE && rt->rt_ifa->ifa_flags & IFA_ROUTE && + info->rti_ifa != NULL && in_is_subnet_route(rt->rt_ifa, info)) { + rt->rt_ifa->ifa_flags &= ~IFA_ROUTE; + info->rti_ifa->ifa_flags |= IFA_ROUTE; + } +} Index: tests/sys/netinet/Makefile =================================================================== --- tests/sys/netinet/Makefile +++ tests/sys/netinet/Makefile @@ -1,9 +1,16 @@ # $FreeBSD$ +PACKAGE= tests + TESTSDIR= ${TESTSBASE}/sys/netinet BINDIR= ${TESTSDIR} -ATF_TESTS_SH= fibs_test +ATF_TESTS_SH= \ + fibs_test \ + route_test \ + +${PACKAGE}FILES+= \ + route.subr PROGS= udp_dontroute tcp_user_cookie Index: tests/sys/netinet/fibs_test.sh =================================================================== --- tests/sys/netinet/fibs_test.sh +++ tests/sys/netinet/fibs_test.sh @@ -34,6 +34,8 @@ # All of the tests in this file requires the test-suite config variable "fibs" # to be defined to a space-delimited list of FIBs that may be used for testing. +. $(atf_get_srcdir)/route.subr + # arpresolve should check the interface fib for routes to a target when # creating an ARP table entry. This is a regression for kern/167947, where # arpresolve only checked the default route. @@ -720,7 +722,6 @@ cleanup_ifaces } - atf_init_test_cases() { atf_add_test_case arpresolve_checks_interface_fib @@ -737,109 +738,3 @@ atf_add_test_case udp_dontroute atf_add_test_case udp_dontroute6 } - -# Looks up one or more fibs from the configuration data and validates them. -# Returns the results in the env varilables FIB0, FIB1, etc. - -# parameter numfibs The number of fibs to lookup -get_fibs() -{ - NUMFIBS=$1 - net_fibs=`sysctl -n net.fibs` - i=0 - while [ $i -lt "$NUMFIBS" ]; do - fib=`atf_config_get "fibs" | \ - awk -v i=$(( i + 1 )) '{print $i}'` - echo "fib is ${fib}" - eval FIB${i}=${fib} - if [ "$fib" -ge "$net_fibs" ]; then - atf_skip "The ${i}th configured fib is ${fib}, which is not less than net.fibs, which is ${net_fibs}" - fi - i=$(( $i + 1 )) - done -} - -# Creates a new pair of connected epair(4) interface, registers them for -# cleanup, and returns their namen via the environment variables EPAIRA and -# EPAIRB -get_epair() -{ - local EPAIRD - - if (which pfctl && pfctl -s info | grep -q 'Status: Enabled') || - [ `sysctl -n net.inet.ip.fw.enable` = "1" ] || - (which ipf && ipf -V); then - atf_skip "firewalls interfere with this test" - fi - - if EPAIRD=`ifconfig epair create`; then - # Record the epair device so we can clean it up later - echo ${EPAIRD} >> "ifaces_to_cleanup" - EPAIRA=${EPAIRD} - EPAIRB=${EPAIRD%a}b - else - atf_skip "Could not create epair(4) interfaces" - fi -} - -# Creates a new tap(4) interface, registers it for cleanup, and returns the -# name via the environment variable TAP -get_tap() -{ - local TAPD - - if TAPD=`ifconfig tap create`; then - # Record the TAP device so we can clean it up later - echo ${TAPD} >> "ifaces_to_cleanup" - TAP=${TAPD} - else - atf_skip "Could not create a tap(4) interface" - fi -} - -# Configure an ethernet interface -# parameters: -# Interface name -# fib -# Protocol (inet or inet6) -# IP address -# Netmask in number of bits (eg 24 or 8) -# Extra flags -# Return: None -setup_iface() -{ - local IFACE=$1 - local FIB=$2 - local PROTO=$3 - local ADDR=$4 - local MASK=$5 - local FLAGS=$6 - echo setfib ${FIB} \ - ifconfig $IFACE ${PROTO} ${ADDR}/${MASK} fib $FIB $FLAGS - setfib ${FIB} ifconfig $IFACE ${PROTO} ${ADDR}/${MASK} fib $FIB $FLAGS -} - -# Create a tap(4) interface, configure it, and register it for cleanup. -# parameters: -# fib -# Protocol (inet or inet6) -# IP address -# Netmask in number of bits (eg 24 or 8) -# Extra flags -# Return: the tap interface name as the env variable TAP -setup_tap() -{ - get_tap - setup_iface "$TAP" "$@" -} - -cleanup_ifaces() -{ - if [ -f ifaces_to_cleanup ]; then - for iface in $(cat ifaces_to_cleanup); do - echo ifconfig "${iface}" destroy - ifconfig "${iface}" destroy 2>/dev/null || true - done - rm -f ifaces_to_cleanup - fi -} Index: tests/sys/netinet/route.subr =================================================================== --- /dev/null +++ tests/sys/netinet/route.subr @@ -0,0 +1,138 @@ +# +# 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$ + +# Looks up one or more fibs from the configuration data and validates them. +# Returns the results in the env varilables FIB0, FIB1, etc. + +# parameter numfibs The number of fibs to lookup +get_fibs() +{ + NUMFIBS=$1 + net_fibs=`sysctl -n net.fibs` + i=0 + while [ $i -lt "$NUMFIBS" ]; do + fib=`atf_config_get "fibs" | \ + awk -v i=$(( i + 1 )) '{print $i}'` + echo "fib is ${fib}" + eval FIB${i}=${fib} + if [ "$fib" -ge "$net_fibs" ]; then + atf_skip "The ${i}th configured fib is ${fib}, which is not less than net.fibs, which is ${net_fibs}" + fi + i=$(( $i + 1 )) + done +} + +# Creates a new pair of connected epair(4) interface, registers them for +# cleanup, and returns their namen via the environment variables EPAIRA and +# EPAIRB +get_epair() +{ + local EPAIRD + + if (which pfctl && pfctl -s info | grep -q 'Status: Enabled') || + [ `sysctl -n net.inet.ip.fw.enable` = "1" ] || + (which ipf && ipf -V); then + atf_skip "firewalls interfere with this test" + fi + + if EPAIRD=`ifconfig epair create`; then + # Record the epair device so we can clean it up later + echo ${EPAIRD} >> "ifaces_to_cleanup" + EPAIRA=${EPAIRD} + EPAIRB=${EPAIRD%a}b + else + atf_skip "Could not create epair(4) interfaces" + fi +} + +# Creates a new tap(4) interface, registers it for cleanup, and returns the +# name via the environment variable TAP +get_tap() +{ + local TAPD + + if TAPD=`ifconfig tap create`; then + # Record the TAP device so we can clean it up later + echo ${TAPD} >> "ifaces_to_cleanup" + TAP=${TAPD} + else + atf_skip "Could not create a tap(4) interface" + fi +} + +# Configure an ethernet interface +# parameters: +# Interface name +# fib +# Protocol (inet or inet6) +# IP address +# Netmask in number of bits (eg 24 or 8) +# Extra flags +# Return: None +setup_iface() +{ + local IFACE=$1 + local FIB=$2 + local PROTO=$3 + local ADDR=$4 + local MASK=$5 + local FLAGS=$6 + echo setfib ${FIB} \ + ifconfig $IFACE ${PROTO} ${ADDR}/${MASK} fib $FIB $FLAGS + setfib ${FIB} ifconfig $IFACE ${PROTO} ${ADDR}/${MASK} fib $FIB $FLAGS +} + +# Create a tap(4) interface, configure it, and register it for cleanup. +# parameters: +# fib +# Protocol (inet or inet6) +# IP address +# Netmask in number of bits (eg 24 or 8) +# Extra flags +# Return: the tap interface name as the env variable TAP +setup_tap() +{ + get_tap + setup_iface "$TAP" "$@" +} + +cleanup_ifaces() +{ + if [ -f ifaces_to_cleanup ]; then + for iface in $(cat ifaces_to_cleanup); do + echo ifconfig "${iface}" destroy + ifconfig "${iface}" destroy 2>/dev/null || true + done + rm -f ifaces_to_cleanup + fi +} Index: tests/sys/netinet/route_test.sh =================================================================== --- /dev/null +++ tests/sys/netinet/route_test.sh @@ -0,0 +1,117 @@ +# +# Copyright (c) 2014 Dell Inc +# 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. + +# All of the tests in this file requires the test-suite config variable "fibs" +# to be defined to a space-delimited list of FIBs that may be used for testing. + +. $(atf_get_srcdir)/route.subr + +atf_test_case ipv4_move_subnet_route cleanup +ipv4_move_subnet_route_head() +{ + atf_set "descr" "moving a subnet route to different ifa should be possible" + atf_set "require.user" "root" + atf_set "require.config" "fibs" +} + +ipv4_move_subnet_route_body() +{ + # Configure the TAP interfaces to use a RFC5737 nonrouteable addresses + # and a non-default fib + SUBNET_PREFIX="192.0.2" + SUBNET="${SUBNET_PREFIX}.0" + ADDR0="${SUBNET_PREFIX}.1" + ADDR1="${SUBNET_PREFIX}.2" + + MASK="24" + + # Check system configuration + if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then + atf_skip "This test requires net.add_addr_allfibs=0" + fi + get_fibs 1 + + get_epair + setup_iface "$EPAIRA" "$FIB0" inet ${ADDR0} $MASK + setup_iface "$EPAIRB" "$FIB0" inet ${ADDR1} $MASK + + setfib $FIB0 route change ${SUBNET}/${MASK} -ifp "$EPAIRB" + ifconfig "$EPAIRA" inet ${ADDR0}/${MASK} fib "$FIB0" -alias + atf_check -s exit:0 ifconfig "$EPAIRB" inet ${ADDR0}/$MASK alias fib "$FIB0" +} + +ipv4_move_subnet_route_cleanup() +{ + cleanup_ifaces +} + +atf_test_case ipv6_move_subnet_route cleanup +ipv6_move_subnet_route_head() +{ + atf_set "descr" "moving a subnet route to different ifa should be possible" + atf_set "require.user" "root" + atf_set "require.config" "fibs" +} + +ipv6_move_subnet_route_body() +{ + # Configure the TAP interfaces to use a RFC5737 nonrouteable addresses + # and a non-default fib + SUBNET_PREFIX="2001:db8:" + SUBNET="${SUBNET_PREFIX}:0" + ADDR0="${SUBNET_PREFIX}:1" + ADDR1="${SUBNET_PREFIX}:2" + + MASK="64" + + # Check system configuration + if [ 0 != `sysctl -n net.add_addr_allfibs` ]; then + atf_skip "This test requires net.add_addr_allfibs=0" + fi + get_fibs 1 + + get_epair + setup_iface "$EPAIRA" "$FIB0" inet6 ${ADDR0} $MASK + setup_iface "$EPAIRB" "$FIB0" inet6 ${ADDR1} $MASK + + setfib $FIB0 route -6 change ${SUBNET}/${MASK} -ifp "$EPAIRB" + ifconfig "$EPAIRA" inet6 ${ADDR0}/${MASK} fib "$FIB0" -alias + atf_check -s exit:0 setfib 1 ifconfig "$EPAIRB" inet6 ${ADDR0}/$MASK fib "$FIB0" alias +} + +ipv6_move_subnet_route_cleanup() +{ + cleanup_ifaces +} + +atf_init_test_cases() +{ + atf_add_test_case ipv4_move_subnet_route + atf_add_test_case ipv6_move_subnet_route +}