Index: sys/net/radix.h =================================================================== --- sys/net/radix.h +++ sys/net/radix.h @@ -65,7 +65,8 @@ struct radix_node *rn_Dupedkey; } rn_leaf; struct { /* node only data: */ - int rn_Off; /* where to start compare */ + int16_t rn_Off; /* where to start compare */ + int16_t rn_Salen; /* total length of sockaddr */ struct radix_node *rn_L;/* progeny */ struct radix_node *rn_R;/* progeny */ } rn_node; @@ -81,6 +82,7 @@ #define rn_key rn_u.rn_leaf.rn_Key #define rn_mask rn_u.rn_leaf.rn_Mask #define rn_offset rn_u.rn_node.rn_Off +#define rn_salen rn_u.rn_node.rn_Salen #define rn_left rn_u.rn_node.rn_L #define rn_right rn_u.rn_node.rn_R @@ -106,7 +108,7 @@ struct radix_head; typedef int walktree_f_t(struct radix_node *, void *); -typedef struct radix_node *rn_matchaddr_f_t(void *v, +typedef struct radix_node *rn_matchaddr_f_t(const void *v, struct radix_head *head); typedef struct radix_node *rn_addaddr_f_t(void *v, void *mask, struct radix_head *head, struct radix_node nodes[]); @@ -175,13 +177,15 @@ int rn_inithead(void **, int); int rn_detachhead(void **); +void rn_setkeylen(struct radix_head *, unsigned int salen); int rn_refines(void *, void *); struct radix_node *rn_addroute(void *, void *, struct radix_head *, struct radix_node[2]); struct radix_node *rn_delete(void *, void *, struct radix_head *); struct radix_node *rn_lookup (void *v_arg, void *m_arg, struct radix_head *head); -struct radix_node *rn_match(void *, struct radix_head *); +struct radix_node *rn_match(const void *, struct radix_head *); +struct radix_node *rn_match_fixed(const void *, struct radix_head *); int rn_walktree_from(struct radix_head *h, void *a, void *m, walktree_f_t *f, void *w); int rn_walktree(struct radix_head *, walktree_f_t *, void *); Index: sys/net/radix.c =================================================================== --- sys/net/radix.c +++ sys/net/radix.c @@ -63,7 +63,7 @@ struct radix_node [2]), *rn_newpair(void *, int, struct radix_node[2]), *rn_search(void *, struct radix_node *), - *rn_search_m(void *, struct radix_node *, void *); + *rn_search_m(const void *, struct radix_node *, void *); static struct radix_node *rn_addmask(void *, struct radix_mask_head *, int,int); static void rn_detachhead_internal(struct radix_head *); @@ -83,8 +83,8 @@ static struct radix_mask * rn_new_radix_mask(struct radix_node *tt, struct radix_mask *next); -static int rn_satisfies_leaf(char *trial, struct radix_node *leaf, - int skip); +static int rn_satisfies_leaf(const char *trial, struct radix_node *leaf, + int skip, int salen); /* * The data structure for the keys is a radix tree with one way @@ -166,10 +166,10 @@ * XXX note this function is used only once. */ static struct radix_node * -rn_search_m(void *v_arg, struct radix_node *head, void *m_arg) +rn_search_m(const void *v_arg, struct radix_node *head, void *m_arg) { struct radix_node *x; - caddr_t v = v_arg, m = m_arg; + c_caddr_t v = v_arg, m = m_arg; for (x = head; x->rn_bit >= 0;) { if ((x->rn_bmask & m[x->rn_offset]) && @@ -255,11 +255,12 @@ } static int -rn_satisfies_leaf(char *trial, struct radix_node *leaf, int skip) +rn_satisfies_leaf(const char *trial, struct radix_node *leaf, int skip, + int salen) { - char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; - char *cplim; - int length = min(LEN(cp), LEN(cp2)); + const char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; + const char *cplim; + int length = min(salen, LEN(cp2) - skip); if (cp3 == NULL) cp3 = rn_ones; @@ -275,15 +276,15 @@ /* * Search for longest-prefix match in given @head */ -struct radix_node * -rn_match(void *v_arg, struct radix_head *head) +static inline struct radix_node * +rn_match_internal(const void *v_arg, struct radix_head *head, int salen) { - caddr_t v = v_arg; + c_caddr_t v = v_arg; struct radix_node *t = head->rnh_treetop, *x; - caddr_t cp = v, cp2; - caddr_t cplim; + c_caddr_t cp = v, cp2; + c_caddr_t cplim; struct radix_node *saved_t, *top = t; - int off = t->rn_offset, vlen = LEN(cp), matched_off; + int off = t->rn_offset, vlen = salen, matched_off; int test, b, rn_bit; /* @@ -344,7 +345,7 @@ if (t->rn_flags & RNF_NORMAL) { if (rn_bit <= t->rn_bit) return (t); - } else if (rn_satisfies_leaf(v, t, matched_off)) + } else if (rn_satisfies_leaf(v, t, matched_off, salen)) return (t); t = saved_t; /* start searching up the tree */ @@ -367,7 +368,7 @@ x = rn_search_m(v, t, m->rm_mask); while (x && x->rn_mask != m->rm_mask) x = x->rn_dupedkey; - if (x && rn_satisfies_leaf(v, x, off)) + if (x && rn_satisfies_leaf(v, x, off, salen)) return (x); } m = m->rm_mklist; @@ -376,6 +377,21 @@ return (0); } +struct radix_node * +rn_match(const void *v_arg, struct radix_head *head) +{ + + return (rn_match_internal(v_arg, head, LEN(v_arg))); +} + +struct radix_node * +rn_match_fixed(const void *v_arg, struct radix_head *head) +{ + + return (rn_match_internal(v_arg, head, head->rnh_treetop->rn_salen)); +} + + #ifdef RN_DEBUG int rn_nodenum; struct radix_node *rn_clist; @@ -1111,7 +1127,8 @@ * bits starting at 'off'. */ void -rn_inithead_internal(struct radix_head *rh, struct radix_node *base_nodes, int off) +rn_inithead_internal(struct radix_head *rh, struct radix_node *base_nodes, + int off) { struct radix_node *t, *tt, *ttt; @@ -1179,6 +1196,18 @@ return (1); } + + +/* Sets lookup _key_ length for the constant-length keys */ +void +rn_setkeylen(struct radix_head *rh, unsigned int salen) +{ + struct radix_node *t; + + t = rh->rnh_treetop; + t->rn_salen = t->rn_offset + salen; +} + static int rn_freeentry(struct radix_node *rn, void *arg) Index: sys/net/route.h =================================================================== --- sys/net/route.h +++ sys/net/route.h @@ -455,7 +455,7 @@ void rt_newmaddrmsg(int, struct ifmultiaddr *); int rt_setgate(struct rtentry *, struct sockaddr *, struct sockaddr *); void rt_maskedcopy(struct sockaddr *, struct sockaddr *, struct sockaddr *); -struct rib_head *rt_table_init(int, int, u_int); +struct rib_head *rt_table_init(int, int, int, u_int); void rt_table_destroy(struct rib_head *); u_int rt_tables_get_gen(int table, int fam); Index: sys/net/route.c =================================================================== --- sys/net/route.c +++ sys/net/route.c @@ -348,7 +348,7 @@ #endif struct rib_head * -rt_table_init(int offset, int family, u_int fibnum) +rt_table_init(int offset, int keylen, int family, u_int fibnum) { struct rib_head *rh; @@ -357,6 +357,7 @@ /* TODO: These details should be hidded inside radix.c */ /* Init masks tree */ rn_inithead_internal(&rh->head, rh->rnh_nodes, offset); + rn_setkeylen(&rh->head, keylen); rn_inithead_internal(&rh->rmhead.head, rh->rmhead.mask_nodes, 0); rh->head.rnh_masks = &rh->rmhead; @@ -375,7 +376,7 @@ /* Finally, set base callbacks */ rh->rnh_addaddr = rn_addroute; rh->rnh_deladdr = rn_delete; - rh->rnh_matchaddr = rn_match; + rh->rnh_matchaddr = rn_match_fixed; rh->rnh_lookup = rn_lookup; rh->rnh_walktree = rn_walktree; rh->rnh_walktree_from = rn_walktree_from; Index: sys/netinet/in_fib.c =================================================================== --- sys/netinet/in_fib.c +++ sys/netinet/in_fib.c @@ -66,6 +66,7 @@ uint32_t flags, struct nhop4_extended *pnh4); #define RNTORT(p) ((struct rtentry *)(p)) +#define FIB4_LOOKUP_KEY(a) (void *)(__containerof(a, struct sockaddr_in, sin_addr)) static void fib4_rte_to_nh_basic(struct rtentry *rte, struct in_addr dst, @@ -134,7 +135,7 @@ RIB_RLOCK_TRACKER; struct rib_head *rh; struct radix_node *rn; - struct sockaddr_in sin; + struct in_addr addr; struct rtentry *rte; KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_basic: bad fibnum")); @@ -143,12 +144,10 @@ return (ENOENT); /* Prepare lookup key */ - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(struct sockaddr_in); - sin.sin_addr = dst; + addr = dst; RIB_RLOCK(rh); - rn = rh->rnh_matchaddr((void *)&sin, &rh->head); + rn = rh->rnh_matchaddr(FIB4_LOOKUP_KEY(&addr), &rh->head); if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { rte = RNTORT(rn); /* Ensure route & ifp is UP */ @@ -183,7 +182,7 @@ RIB_RLOCK_TRACKER; struct rib_head *rh; struct radix_node *rn; - struct sockaddr_in sin; + struct in_addr addr; struct rtentry *rte; KASSERT((fibnum < rt_numfibs), ("fib4_lookup_nh_ext: bad fibnum")); @@ -192,12 +191,10 @@ return (ENOENT); /* Prepare lookup key */ - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(struct sockaddr_in); - sin.sin_addr = dst; + addr = dst; RIB_RLOCK(rh); - rn = rh->rnh_matchaddr((void *)&sin, &rh->head); + rn = rh->rnh_matchaddr(FIB4_LOOKUP_KEY(&addr), &rh->head); if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { rte = RNTORT(rn); #ifdef RADIX_MPATH Index: sys/netinet/in_rmx.c =================================================================== --- sys/netinet/in_rmx.c +++ sys/netinet/in_rmx.c @@ -120,7 +120,7 @@ { struct rib_head *rh; - rh = rt_table_init(32, AF_INET, fibnum); + rh = rt_table_init(32, sizeof(struct in_addr), AF_INET, fibnum); if (rh == NULL) return (0); Index: sys/netinet6/in6_fib.c =================================================================== --- sys/netinet6/in6_fib.c +++ sys/netinet6/in6_fib.c @@ -75,6 +75,8 @@ static struct ifnet *fib6_get_ifaifp(struct rtentry *rte); #define RNTORT(p) ((struct rtentry *)(p)) +#define FIB6_LOOKUP_KEY(a) (void *)(__containerof(a, struct sockaddr_in6, sin6_addr)) + #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) CHK_STRUCT_ROUTE_COMPAT(struct route_in6, ro_dst); @@ -179,7 +181,7 @@ RIB_RLOCK_TRACKER; struct rib_head *rh; struct radix_node *rn; - struct sockaddr_in6 sin6; + struct in6_addr addr6; struct rtentry *rte; KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_basic: bad fibnum")); @@ -188,20 +190,18 @@ return (ENOENT); /* Prepare lookup key */ - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_addr = *dst; - sin6.sin6_len = sizeof(struct sockaddr_in6); + addr6 = *dst; /* Assume scopeid is valid and embed it directly */ if (IN6_IS_SCOPE_LINKLOCAL(dst)) - sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); + addr6.s6_addr16[1] = htons(scopeid & 0xffff); RIB_RLOCK(rh); - rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); + rn = rh->rnh_matchaddr(FIB6_LOOKUP_KEY(&addr6), &rh->head); if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { rte = RNTORT(rn); /* Ensure route & ifp is UP */ if (RT_LINK_IS_UP(rte->rt_ifp)) { - fib6_rte_to_nh_basic(rte, &sin6.sin6_addr, flags, pnh6); + fib6_rte_to_nh_basic(rte, &addr6, flags, pnh6); RIB_RUNLOCK(rh); return (0); } @@ -229,7 +229,7 @@ RIB_RLOCK_TRACKER; struct rib_head *rh; struct radix_node *rn; - struct sockaddr_in6 sin6; + struct in6_addr addr6; struct rtentry *rte; KASSERT((fibnum < rt_numfibs), ("fib6_lookup_nh_ext: bad fibnum")); @@ -238,15 +238,13 @@ return (ENOENT); /* Prepare lookup key */ - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_len = sizeof(struct sockaddr_in6); - sin6.sin6_addr = *dst; + addr6 = *dst; /* Assume scopeid is valid and embed it directly */ if (IN6_IS_SCOPE_LINKLOCAL(dst)) - sin6.sin6_addr.s6_addr16[1] = htons(scopeid & 0xffff); + addr6.s6_addr16[1] = htons(scopeid & 0xffff); RIB_RLOCK(rh); - rn = rh->rnh_matchaddr((void *)&sin6, &rh->head); + rn = rh->rnh_matchaddr(FIB6_LOOKUP_KEY(&addr6), &rh->head); if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) { rte = RNTORT(rn); #ifdef RADIX_MPATH @@ -258,8 +256,7 @@ #endif /* Ensure route & ifp is UP */ if (RT_LINK_IS_UP(rte->rt_ifp)) { - fib6_rte_to_nh_extended(rte, &sin6.sin6_addr, flags, - pnh6); + fib6_rte_to_nh_extended(rte, &addr6, flags, pnh6); if ((flags & NHR_REF) != 0) { /* TODO: Do lwref on egress ifp's */ } Index: sys/netinet6/in6_rmx.c =================================================================== --- sys/netinet6/in6_rmx.c +++ sys/netinet6/in6_rmx.c @@ -162,7 +162,7 @@ struct rib_head *rh; rh = rt_table_init(offsetof(struct sockaddr_in6, sin6_addr) << 3, - AF_INET6, fibnum); + sizeof(struct in6_addr), AF_INET6, fibnum); if (rh == NULL) return (0); Index: tests/sys/netinet/Makefile =================================================================== --- tests/sys/netinet/Makefile +++ tests/sys/netinet/Makefile @@ -9,7 +9,7 @@ so_reuseport_lb_test \ socket_afinet -ATF_TESTS_SH= fibs_test redirect divert forward output +ATF_TESTS_SH= fibs_test redirect divert forward output lpm PROGS= udp_dontroute tcp_user_cookie Index: tests/sys/netinet/lpm.sh =================================================================== --- /dev/null +++ tests/sys/netinet/lpm.sh @@ -0,0 +1,179 @@ +#!/usr/bin/env atf-sh +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 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$ +# + +. $(atf_get_srcdir)/../common/vnet.subr + +setup_networking() +{ + jname="$1" + lo_dst="$2" + epair0="$3" + epair1="$4" + + vnet_mkjail ${jname}a ${epair0}a ${epair1}a + # Setup transit IPv4 networks + jexec ${jname}a ifconfig ${epair0}a up + jexec ${jname}a ifconfig ${epair0}a inet 203.0.113.1/30 + jexec ${jname}a ifconfig ${epair1}a up + jexec ${jname}a ifconfig ${epair1}a inet 203.0.113.5/30 + + vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst} + jexec ${jname}b ifconfig ${epair0}b up + jexec ${jname}b ifconfig ${epair0}b inet 203.0.113.2/30 + jexec ${jname}b ifconfig ${epair1}b up + jexec ${jname}b ifconfig ${epair1}b inet 203.0.113.6/30 + jexec ${jname}b ifconfig ${lo_dst} up + +} + +atf_test_case "lpm_test1_success" "cleanup" +lpm_test1_success_head() +{ + + atf_set descr 'Test IPv4 LPM for /30 and /31' + atf_set require.user root +} + +lpm_test1_success_body() +{ + + vnet_init + + jname="v4t-lpm_test1_success" + + lo_dst=$(vnet_mkloopback) + epair0=$(vnet_mkepair) + epair1=$(vnet_mkepair) + + setup_networking ${jname} ${lo_dst} ${epair0} ${epair1} + + jexec ${jname}b ifconfig ${lo_dst} inet 198.51.100.0/32 + jexec ${jname}b ifconfig ${lo_dst} alias 198.51.100.2/32 + + # Add routes + # A -> towards B via epair0a + jexec ${jname}a route add -4 -net 198.51.100.0/30 203.0.113.2 + # A -> towards B via epair1a + jexec ${jname}a route add -4 -net 198.51.100.0/31 203.0.113.6 + + count=20 + valid_message="${count} packets transmitted, ${count} packets received" + + # Check that 198.51.100.0 goes via epair1 + atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.0 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_1} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})" + exit 1 + fi + + # Check that 198.51.100.2 goes via epair0 + atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.2 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_0} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}" + exit 1 + fi + + echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}" +} + +lpm_test1_success_cleanup() +{ + vnet_cleanup +} + +atf_test_case "lpm_test2_success" "cleanup" +lpm_test2_success_head() +{ + + atf_set descr 'Test IPv4 LPM for the host routes' + atf_set require.user root +} + +lpm_test2_success_body() +{ + + vnet_init + + jname="v4t-lpm_test2_success" + + lo_dst=$(vnet_mkloopback) + epair0=$(vnet_mkepair) + epair1=$(vnet_mkepair) + + setup_networking ${jname} ${lo_dst} ${epair0} ${epair1} + + jexec ${jname}b ifconfig ${lo_dst} inet 198.51.100.0/32 + jexec ${jname}b ifconfig ${lo_dst} alias 198.51.100.1/32 + + # Add routes + # A -> towards B via epair0a + jexec ${jname}a route add -4 -host 198.51.100.0 203.0.113.2 + # A -> towards B via epair1a + jexec ${jname}a route add -4 -host 198.51.100.1 203.0.113.6 + + count=20 + valid_message="${count} packets transmitted, ${count} packets received" + + # Check that 198.51.100.0 goes via epair0 + atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.0 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_0} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}" + exit 1 + fi + + # Check that 198.51.100.1 goes via epair1 + atf_check -o match:"${valid_message}" jexec ${jname}a ping -f -nc${count} 198.51.100.1 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_1} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})" + exit 1 + fi + + echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}" +} + +lpm_test2_success_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "lpm_test1_success" + atf_add_test_case "lpm_test2_success" +} + Index: tests/sys/netinet6/Makefile =================================================================== --- tests/sys/netinet6/Makefile +++ tests/sys/netinet6/Makefile @@ -12,7 +12,8 @@ redirect \ divert \ forward6 \ - output6 + output6 \ + lpm6 ${PACKAGE}FILES+= exthdr.py ${PACKAGE}FILES+= mld.py Index: tests/sys/netinet6/lpm6.sh =================================================================== --- /dev/null +++ tests/sys/netinet6/lpm6.sh @@ -0,0 +1,199 @@ +#!/usr/bin/env atf-sh +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2020 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$ +# + +. $(atf_get_srcdir)/../common/vnet.subr + +setup_networking() +{ + jname="$1" + lo_dst="$2" + epair0="$3" + epair1="$4" + + vnet_mkjail ${jname}a ${epair0}a ${epair1}a ${lo_src} + # enable link-local IPv6 + jexec ${jname}a ndp -i ${epair0}a -- -disabled + jexec ${jname}a ifconfig ${epair0}a up + jexec ${jname}a ndp -i ${epair1}a -- -disabled + jexec ${jname}a ifconfig ${epair1}a up + jexec ${jname}a ifconfig ${lo_src} up + + vnet_mkjail ${jname}b ${epair0}b ${epair1}b ${lo_dst} + jexec ${jname}b ndp -i ${epair0}b -- -disabled + jexec ${jname}b ifconfig ${epair0}b up + jexec ${jname}b ndp -i ${epair1}b -- -disabled + jexec ${jname}b ifconfig ${epair1}b up + jexec ${jname}b ifconfig ${lo_dst} up + + # wait for DAD to complete + while [ `jexec ${jname}b ifconfig | grep inet6 | grep -c tentative` != "0" ]; do + sleep 0.1 + done + while [ `jexec ${jname}a ifconfig | grep inet6 | grep -c tentative` != "0" ]; do + sleep 0.1 + done +} + + +atf_test_case "lpm6_test1_success" "cleanup" +lpm6_test1_success_head() +{ + + atf_set descr 'Test IPv6 LPM for the host routes' + atf_set require.user root +} + +lpm6_test1_success_body() +{ + + vnet_init + + net_dst="2001:db8:" + + jname="v6t-lpm6_test1_success" + + epair0=$(vnet_mkepair) + epair1=$(vnet_mkepair) + lo_dst=$(vnet_mkloopback) + + setup_networking ${jname} ${lo_dst} ${epair0} ${epair1} + + jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:0/128 + jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:1/128 + + # Add routes + # A -> towards B via epair0a LL + ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'` + jexec ${jname}a route add -6 -host ${net_dst}:2:0 ${ll}%${epair0}a + # A -> towards B via epair1a LL + ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'` + jexec ${jname}a route add -6 -host ${net_dst}:2:1 ${ll}%${epair1}a + + count=20 + valid_message="${count} packets transmitted, ${count} packets received" + + # Check that ${net_dst}:2:0 goes via epair0 + atf_check -o match:"${valid_message}" jexec ${jname}a ping6 -f -nc${count} ${net_dst}:2:0 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_0} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}" + exit 1 + fi + + # Check that ${net_dst}:2:1 goes via epair1 + atf_check -o match:"${valid_message}" jexec ${jname}a ping6 -f -nc${count} ${net_dst}:2:1 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_1} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})" + exit 1 + fi + + echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}" +} + +lpm6_test1_success_cleanup() +{ + vnet_cleanup +} + +atf_test_case "lpm6_test2_success" "cleanup" +lpm6_test2_success_head() +{ + + atf_set descr 'Test IPv6 LPM for /126 and /127' + atf_set require.user root +} + +lpm6_test2_success_body() +{ + + vnet_init + + net_dst="2001:db8:" + + jname="v6t-lpm6_test2_success" + + epair0=$(vnet_mkepair) + epair1=$(vnet_mkepair) + lo_dst=$(vnet_mkloopback) + + setup_networking ${jname} ${lo_dst} ${epair0} ${epair1} + + jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:0/128 + jexec ${jname}b ifconfig ${lo_dst} inet6 ${net_dst}:2:2/128 + + # Add routes + # A -> towards B via epair0a LL + ll=`jexec ${jname}b ifconfig ${epair0}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'` + jexec ${jname}a route add -6 -net ${net_dst}:2:0/126 ${ll}%${epair0}a + # A -> towards B via epair1a LL + ll=`jexec ${jname}b ifconfig ${epair1}b inet6 | awk '$2~/^fe80:/{print$2}' | awk -F% '{print$1}'` + jexec ${jname}a route add -6 -net ${net_dst}:2:0/127 ${ll}%${epair1}a + + count=20 + valid_message="${count} packets transmitted, ${count} packets received" + + # Check that ${net_dst}:2:0 goes via epair1 + atf_check -o match:"${valid_message}" jexec ${jname}a ping6 -f -nc${count} ${net_dst}:2:0 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_1} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} 2: ${pkt_1} (should be ${count})" + exit 1 + fi + + # Check that ${net_dst}:2:2 goes via epair0 + atf_check -o match:"${valid_message}" jexec ${jname}a ping6 -f -nc${count} ${net_dst}:2:2 + pkt_0=`jexec ${jname}a netstat -Wf link -I ${epair0}a | head | awk '$1!~/^Name/{print$8}'` + pkt_1=`jexec ${jname}a netstat -Wf link -I ${epair1}a | head | awk '$1!~/^Name/{print$8}'` + if [ ${pkt_0} -le ${count} ]; then + echo "LPM failure: 1: ${pkt_0} (should be ${count}) 2: ${pkt_1}" + exit 1 + fi + + echo "RAW BALANCING: 1: ${pkt_0} 2: ${pkt_1}" +} + +lpm6_test2_success_cleanup() +{ + vnet_cleanup +} + +atf_init_test_cases() +{ + atf_add_test_case "lpm6_test1_success" + atf_add_test_case "lpm6_test2_success" +} + +# end + +