diff --git a/sys/net/radix.h b/sys/net/radix.h --- a/sys/net/radix.h +++ b/sys/net/radix.h @@ -120,6 +120,7 @@ void *a, void *m, walktree_f_t *f, void *w); typedef void rn_close_t(struct radix_node *rn, struct radix_head *head); struct radix_node *rn_nextprefix(struct radix_node *rn); +struct radix_node *rn_get_parent(const struct radix_head *head, struct radix_node *); struct radix_mask_head; diff --git a/sys/net/radix.c b/sys/net/radix.c --- a/sys/net/radix.c +++ b/sys/net/radix.c @@ -127,6 +127,9 @@ */ #define LEN(x) ( (int) (*(const u_char *)(x)) ) +#define RN_IS_ROOT(_rn) (((_rn)->rn_flags & RNF_ROOT) != 0) +#define RN_IS_LEAF(_rn) ((_rn)->rn_bit < 0) + /* * XXX THIS NEEDS TO BE FIXED * In the code, pointers to keys and masks are passed as either @@ -371,6 +374,50 @@ return (0); } +static int +get_mlen_bits(const struct radix_node *rn) +{ + if (rn->rn_mask == NULL) { + return ((LEN(rn->rn_key) * 8)); + } + return (-(1 + rn->rn_bit)); +} + +struct radix_node * +rn_get_parent(const struct radix_head *head, struct radix_node *rn) +{ + struct radix_node *rn_parent; + const char *rn_prefix = rn->rn_key; + int mlen = get_mlen_bits(rn); + int tree_off = head->rnh_treetop->rn_offset; + + rn_parent = rn_nextprefix(rn); + if (rn_parent != NULL) + return (rn_parent); + + while (!RN_IS_ROOT(rn)) { + /* + * Go up the tree to consider key candidates one-by-one. + * For each parent, go down to the leftmost leaf node & + * check if the masks are wide enough to match the original node. + */ + rn = rn->rn_parent; + /* Now dive left to find the leaf node */ + struct radix_node *rn_l = rn->rn_left; + while (rn_l != NULL && !RN_IS_LEAF(rn_l)) + rn_l = rn_l->rn_left; + /* now rn_left is either NULL or a leaf */ + for (; rn_l != NULL; rn_l = rn_nextprefix(rn_l)) { + if (RN_IS_ROOT(rn_l) || get_mlen_bits(rn_l) >= mlen) + continue; + if (rn_satisfies_leaf(rn_prefix, rn_l, tree_off)) + return (rn_l); + } + } + + return (NULL); +} + /* * Returns the next (wider) prefix for the key defined by @rn * if exists. diff --git a/sys/net/route/route_ctl.c b/sys/net/route/route_ctl.c --- a/sys/net/route/route_ctl.c +++ b/sys/net/route/route_ctl.c @@ -1526,6 +1526,12 @@ return (0); } +struct rtentry * +rt_get_parent(struct rib_head *rh, struct rtentry *rt) +{ + return (RNTORT(rn_get_parent(&rh->head, (struct radix_node *)rt))); +} + /* * Removes all routes from the routing table without executing notifications. * rtentres will be removed after the end of a current epoch. diff --git a/sys/net/route/route_var.h b/sys/net/route/route_var.h --- a/sys/net/route/route_var.h +++ b/sys/net/route/route_var.h @@ -229,6 +229,7 @@ int check_info_match_nhop(const struct rt_addrinfo *info, const struct rtentry *rt, const struct nhop_object *nh); bool rib_can_4o6_nhop(void); +struct rtentry *rt_get_parent(struct rib_head *rh, struct rtentry *rt); /* route_rtentry.c */ void vnet_rtzone_init(void); diff --git a/sys/tests/fib_lookup/fib_lookup.c b/sys/tests/fib_lookup/fib_lookup.c --- a/sys/tests/fib_lookup/fib_lookup.c +++ b/sys/tests/fib_lookup/fib_lookup.c @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -54,6 +55,7 @@ #include #include #include +#include #include #define CHUNK_SIZE 10000 @@ -428,7 +430,7 @@ return (0); } -SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_random, +SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_random1, CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, run_test_inet_random, "I", "Execute fib4_lookup random check tests"); @@ -749,6 +751,203 @@ CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 0, run_test_inet6_scan, "I", "Execute fib6_lookup scan tests"); +#define RADIX_MAX_KEY_LEN 32 + +static char rn_ones[RADIX_MAX_KEY_LEN] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +#define LEN(x) ( (int) (*(const u_char *)(x)) ) +static int +rn_satisfies_leaf(const char *trial, struct radix_node *leaf, int skip) +{ + const char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; + const char *cplim; + int length = min(LEN(cp), LEN(cp2)); + + if (cp3 == NULL) + cp3 = rn_ones; + else + length = min(length, LEN(cp3)); + cplim = cp + length; cp3 += skip; cp2 += skip; + for (cp += skip; cp < cplim; cp++, cp2++, cp3++) + if ((*cp ^ *cp2) & *cp3) + return (0); + return (1); +} + + +#define RN_IS_ROOT(_rn) (((_rn)->rn_flags & RNF_ROOT) != 0) +#define IS_LEAF(_rn) (((_rn)->rn_bit < 0)) +#define RN_IS_LEAF(_rn) ((_rn)->rn_bit < 0) + +static int +get_pxlen(struct radix_node *rn) +{ + const int offset = 32, host_plen = 32; + + if (rn->rn_mask == NULL) { + return ((LEN(rn->rn_key) * 8) - offset); + } + if (RN_IS_ROOT(rn)) + return (0); + return ((rn->rn_mask != NULL) ? -(1 + rn->rn_bit) - offset : host_plen); +} + +static int +get_masklen(const struct radix_node *rn) +{ + if (rn->rn_mask == NULL) { + return ((LEN(rn->rn_key) * 8)); + } + return (-(1 + rn->rn_bit)); +} + +static struct radix_node * +_rn_get_parent(struct radix_node *rn) +{ + struct radix_node *rn_parent; + const void *v = rn->rn_key; + int mlen = get_masklen(rn); + + rn_parent = rn_nextprefix(rn); + if (rn_parent != NULL) + return (rn_parent); + + while (!RN_IS_ROOT(rn)) { + rn = rn->rn_parent; + /* Now dive left to find the leaf node */ + struct radix_node *rn_l = rn->rn_left; + while (rn_l != NULL && !RN_IS_LEAF(rn_l)) + rn_l = rn_l->rn_left; + /* now rn_left is a leaft */ + for (; rn_l != NULL; rn_l = rn_nextprefix(rn_l)) { + if (RN_IS_ROOT(rn_l)) + continue; + if (rn_satisfies_leaf(v, rn_l, 32) && (get_masklen(rn_l) < mlen)) + return (rn_l); + } + } + + return (NULL); +} + + +static int +parent_route_chedk(struct rtentry *rt, void *vw) +{ + struct rib_head *rh = (struct rib_head *)vw; + + char buf[32]; + rt_print_buf(rt, buf, sizeof(buf)); + + struct rtentry *rt_parent = rt_get_parent(rh, rt); + if (rt_parent != NULL) { + char pbuf[32]; + + rt_print_buf(rt_parent, pbuf, sizeof(pbuf)); + + printf("X: %s -> %s\n", buf, pbuf); + } else + printf("X: %s -> NULL\n", buf); + + return (0); +} + +static void +print_rt(struct rtentry *rt, const char *name, char *prepend, int off) +{ + struct radix_node *rn = (struct radix_node *)rt; + + if (IS_LEAF(rn)) { + struct in_addr addr4 = {}; + uint32_t scopeid; + int plen = 0; + char abuf[16]; + int xplen = get_pxlen(rn); + + + if (!(rn->rn_flags & RNF_ROOT)) { + struct radix_node *parent = _rn_get_parent(rn); + rt_get_inet_prefix_plen(rt, &addr4, &plen, &scopeid); + inet_ntop(AF_INET, &addr4, abuf, sizeof(abuf)); + + prepend[off] = '\0'; + printf("%s%s a=%p p=%p f=%d [L] %s/%d{%d}", prepend, name, + rn, rn->rn_parent, rn->rn_flags, abuf, plen, xplen); + + if (parent != NULL) { + rt_get_inet_prefix_plen(RNTORT(parent), &addr4, &plen, &scopeid); + inet_ntop(AF_INET, &addr4, abuf, sizeof(abuf)); + printf(" P=%s/%d (%p)", abuf, plen, parent); + } + printf("\n"); + prepend[off] = ' '; + } else { + prepend[off] = '\0'; + printf("%s%s a=%p p=%p f=%d [RL]\n", prepend, name, + rn, rn->rn_parent, rn->rn_flags); + prepend[off] = ' '; + } + } else { + prepend[off] = '\0'; + printf("%s%s a=%p p=%p f=%d [N]\n", prepend, name, rn, rn->rn_parent, rn->rn_flags); + prepend[off] = ' '; + } + + if (!IS_LEAF(rn)) { + if (rn->rn_left != NULL) + print_rt(RNTORT(rn->rn_left), "L", prepend, off + 1); + if (rn->rn_right != NULL) + print_rt(RNTORT(rn->rn_right), "R", prepend, off + 1); + } else if (rn_nextprefix(rn) != NULL) { + struct radix_node *rn_next = rn_nextprefix(rn); + for (; rn_next != NULL; rn_next = rn_nextprefix(rn_next)) { + print_rt(RNTORT(rn_next), "D", prepend, off + 1); + } + } +} + + +static int +run_test_inet_parent(SYSCTL_HANDLER_ARGS) +{ + struct epoch_tracker et; + uint32_t fibnum = curthread->td_proc->p_fibnum; + + int count = 0; + int error = sysctl_handle_int(oidp, &count, 0, req); + if (error != 0) + return (error); + + if (count == 0) + return (0); + + printf("-------------------------------------------\n"); + NET_EPOCH_ENTER(et); + struct rib_head *rh; + rh = rt_tables_get_rnh(fibnum, AF_INET); + + char pbuf[64]; + + memset(pbuf, ' ', sizeof(pbuf)); + pbuf[sizeof(pbuf) - 1] = '\0'; + + print_rt(RNTORT(rh->head.rnh_treetop), "V", pbuf, 0); + + rib_walk(fibnum, AF_INET, false, parent_route_chedk, rh); + NET_EPOCH_EXIT(et); + + return (0); +} +SYSCTL_PROC(_net_route_test, OID_AUTO, run_inet_parent, + CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + 0, 0, run_test_inet_parent, "I", "Execute fib6_lookup scan tests"); + + #define LPS_SEQ 0x1 #define LPS_ANN 0x2 #define LPS_REP 0x4