Index: head/sys/net/if_llatbl.c =================================================================== --- head/sys/net/if_llatbl.c +++ head/sys/net/if_llatbl.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include Index: head/sys/net/route.h =================================================================== --- head/sys/net/route.h +++ head/sys/net/route.h @@ -399,12 +399,6 @@ void rtfree_func(struct rtentry *); void rt_updatemtu(struct ifnet *); -typedef int rt_walktree_f_t(struct rtentry *, void *); -typedef void rt_setwarg_t(struct rib_head *, uint32_t, int, void *); -void rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, - void *arg, bool report); -void rt_foreach_fib_walk(int af, rt_setwarg_t *, rt_walktree_f_t *, void *); -void rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg); void rt_flushifroutes_af(struct ifnet *, int); void rt_flushifroutes(struct ifnet *ifp); @@ -423,12 +417,8 @@ int rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t, struct rt_addrinfo *); void rib_free_info(struct rt_addrinfo *info); -int rib_add_redirect(u_int fibnum, struct sockaddr *dst, - struct sockaddr *gateway, struct sockaddr *author, struct ifnet *ifp, - int flags, int expire_sec); /* New API */ -void rib_walk(int af, u_int fibnum, rt_walktree_f_t *wa_f, void *arg); struct nhop_object *rib_lookup(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags, uint32_t flowid); #endif Index: head/sys/net/route.c =================================================================== --- head/sys/net/route.c +++ head/sys/net/route.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -346,6 +347,9 @@ nhops_init_rib(rh); + /* Init subscription system */ + CK_STAILQ_INIT(&rh->rnh_subscribers); + /* Finally, set base callbacks */ rh->rnh_addaddr = rn_addroute; rh->rnh_deladdr = rn_delete; @@ -1148,6 +1152,7 @@ { const struct sockaddr *dst; struct rib_head *rnh; + struct rib_cmd_info rc; int error; KASSERT((fibnum < rt_numfibs), ("rtrequest1_fib: bad fibnum")); @@ -1180,10 +1185,11 @@ if (info->rti_flags & RTF_HOST) info->rti_info[RTAX_NETMASK] = NULL; + bzero(&rc, sizeof(struct rib_cmd_info)); error = 0; switch (req) { case RTM_DELETE: - error = del_route(rnh, info, ret_nrt); + error = del_route(rnh, info, &rc); break; case RTM_RESOLVE: /* @@ -1192,14 +1198,17 @@ */ break; case RTM_ADD: - error = add_route(rnh, info, ret_nrt); + error = add_route(rnh, info, &rc); break; case RTM_CHANGE: - error = change_route(rnh, info, ret_nrt); + error = change_route(rnh, info, &rc); break; default: error = EOPNOTSUPP; } + + if (ret_nrt != NULL) + *ret_nrt = rc.rc_rt; return (error); } Index: head/sys/net/route/nhop_ctl.c =================================================================== --- head/sys/net/route/nhop_ctl.c +++ head/sys/net/route/nhop_ctl.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include Index: head/sys/net/route/route_ctl.h =================================================================== --- head/sys/net/route/route_ctl.h +++ head/sys/net/route/route_ctl.h @@ -0,0 +1,90 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * 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$ + */ + +/* + * This header file contains public functions and structures used for + * routing table manipulations. + */ + +#ifndef _NET_ROUTE_ROUTE_CTL_H_ +#define _NET_ROUTE_ROUTE_CTL_H_ + +struct rib_cmd_info { + uint8_t rc_cmd; /* RTM_ADD|RTM_DEL|RTM_CHANGE */ + uint8_t spare[3]; + uint32_t rc_nh_weight; /* new nhop weight */ + struct rtentry *rc_rt; /* Target entry */ + struct nhop_object *rc_nh_old; /* Target nhop OR mpath */ + struct nhop_object *rc_nh_new; /* Target nhop OR mpath */ +}; + + +int rib_add_route(uint32_t fibnum, struct rt_addrinfo *info, + struct rib_cmd_info *rc); +int rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, + struct rib_cmd_info *rc); +int rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, + struct rib_cmd_info *rc); + +int rib_add_redirect(u_int fibnum, struct sockaddr *dst, + struct sockaddr *gateway, struct sockaddr *author, struct ifnet *ifp, + int flags, int expire_sec); + +typedef int rt_walktree_f_t(struct rtentry *, void *); +void rib_walk(int af, u_int fibnum, rt_walktree_f_t *wa_f, void *arg); +void rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, + void *arg, bool report); + +typedef void rt_setwarg_t(struct rib_head *, uint32_t, int, void *); +void rt_foreach_fib_walk(int af, rt_setwarg_t *, rt_walktree_f_t *, void *); +void rt_foreach_fib_walk_del(int af, rt_filter_f_t *filter_f, void *arg); + +enum rib_subscription_type { + RIB_NOTIFY_IMMEDIATE, + RIB_NOTIFY_DELAYED +}; + +typedef void rib_subscription_cb_t(struct rib_head *rnh, struct rib_cmd_info *rc, + void *arg); + +struct rib_subscription { + CK_STAILQ_ENTRY(rib_subscription) next; + rib_subscription_cb_t *func; + void *arg; + enum rib_subscription_type type; +}; + +struct rib_subscription *rib_subscribe(uint32_t fibnum, int family, + rib_subscription_cb_t *f, void *arg, enum rib_subscription_type type, + int waitok); +int rib_unsibscribe(uint32_t fibnum, int family, struct rib_subscription *rs); + + +#endif + Index: head/sys/net/route/route_ctl.c =================================================================== --- head/sys/net/route/route_ctl.c +++ head/sys/net/route/route_ctl.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -67,11 +68,61 @@ * All functions assumes they are called in net epoch. */ +static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type, + struct rib_cmd_info *rc); + static void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info); +static struct rib_head * +get_rnh(uint32_t fibnum, const struct rt_addrinfo *info) +{ + struct rib_head *rnh; + struct sockaddr *dst; + + KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum")); + + dst = info->rti_info[RTAX_DST]; + rnh = rt_tables_get_rnh(fibnum, dst->sa_family); + + return (rnh); +} + +/* + * Adds route defined by @info into the kernel table specified by @fibnum and + * sa_family in @info->rti_info[RTAX_DST]. + * + * Returns 0 on success and fills in operation metadata into @rc. + */ int +rib_add_route(uint32_t fibnum, struct rt_addrinfo *info, + struct rib_cmd_info *rc) +{ + struct rib_head *rnh; + + NET_EPOCH_ASSERT(); + + rnh = get_rnh(fibnum, info); + if (rnh == NULL) + return (EAFNOSUPPORT); + + /* + * Check consistency between RTF_HOST flag and netmask + * existence. + */ + if (info->rti_flags & RTF_HOST) + info->rti_info[RTAX_NETMASK] = NULL; + else if (info->rti_info[RTAX_NETMASK] == NULL) + return (EINVAL); + + bzero(rc, sizeof(struct rib_cmd_info)); + rc->rc_cmd = RTM_ADD; + + return (add_route(rnh, info, rc)); +} + +int add_route(struct rib_head *rnh, struct rt_addrinfo *info, - struct rtentry **ret_nrt) + struct rib_cmd_info *rc) { struct sockaddr *dst, *ndst, *gateway, *netmask; struct rtentry *rt, *rt_old; @@ -146,6 +197,7 @@ rt->rt_weight = 1; rt_setmetrics(info, rt); + rt_old = NULL; RIB_WLOCK(rnh); RT_LOCK(rt); @@ -163,12 +215,20 @@ rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); - if (rn != NULL && rt->rt_expire > 0) - tmproutes_update(rnh, rt); + if (rn != NULL) { + /* Most common usecase */ + if (rt->rt_expire > 0) + tmproutes_update(rnh, rt); - rt_old = NULL; - if (rn == NULL && (info->rti_flags & RTF_PINNED) != 0) { + /* Finalize notification */ + rnh->rnh_gen++; + rc->rc_rt = RNTORT(rn); + rc->rc_nh_new = nh; + + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); + } else if ((info->rti_flags & RTF_PINNED) != 0) { + /* * Force removal and re-try addition * TODO: better multipath&pinned support @@ -180,9 +240,26 @@ rt_old = rt_unlinkrte(rnh, info, &error); info->rti_flags |= RTF_PINNED; info->rti_info[RTAX_DST] = info_dst; - if (rt_old != NULL) + if (rt_old != NULL) { rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes); + + /* Finalize notification */ + rnh->rnh_gen++; + + if (rn != NULL) { + rc->rc_cmd = RTM_CHANGE; + rc->rc_rt = RNTORT(rn); + rc->rc_nh_old = rt_old->rt_nhop; + rc->rc_nh_new = nh; + } else { + rc->rc_cmd = RTM_DELETE; + rc->rc_rt = RNTORT(rn); + rc->rc_nh_old = rt_old->rt_nhop; + rc->rc_nh_new = nh; + } + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); + } } RIB_WUNLOCK(rnh); @@ -208,12 +285,6 @@ if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_ADD, rt, rt->rt_nhop, info); - /* - * actually return a resultant rtentry - */ - if (ret_nrt) - *ret_nrt = rt; - rnh->rnh_gen++; /* Routing table updated */ RT_UNLOCK(rt); return (0); @@ -221,6 +292,29 @@ /* + * Removes route defined by @info from the kernel table specified by @fibnum and + * sa_family in @info->rti_info[RTAX_DST]. + * + * Returns 0 on success and fills in operation metadata into @rc. + */ +int +rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc) +{ + struct rib_head *rnh; + + NET_EPOCH_ASSERT(); + + rnh = get_rnh(fibnum, info); + if (rnh == NULL) + return (EAFNOSUPPORT); + + bzero(rc, sizeof(struct rib_cmd_info)); + rc->rc_cmd = RTM_DELETE; + + return (del_route(rnh, info, rc)); +} + +/* * Conditionally unlinks rtentry matching data inside @info from @rnh. * Returns unlinked, locked and referenced @rtentry on success, * Returns NULL and sets @perror to: @@ -295,7 +389,7 @@ int del_route(struct rib_head *rnh, struct rt_addrinfo *info, - struct rtentry **ret_nrt) + struct rib_cmd_info *rc) { struct sockaddr *dst, *netmask; struct sockaddr_storage mdst; @@ -314,6 +408,13 @@ RIB_WLOCK(rnh); rt = rt_unlinkrte(rnh, info, &error); + if (rt != NULL) { + /* Finalize notification */ + rnh->rnh_gen++; + rc->rc_rt = rt; + rc->rc_nh_old = rt->rt_nhop; + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); + } RIB_WUNLOCK(rnh); if (error != 0) return (error); @@ -324,17 +425,32 @@ * If the caller wants it, then it can have it, * the entry will be deleted after the end of the current epoch. */ - if (ret_nrt) - *ret_nrt = rt; - rtfree(rt); return (0); } +int +rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, + struct rib_cmd_info *rc) +{ + struct rib_head *rnh; + + NET_EPOCH_ASSERT(); + + rnh = get_rnh(fibnum, info); + if (rnh == NULL) + return (EAFNOSUPPORT); + + bzero(rc, sizeof(struct rib_cmd_info)); + rc->rc_cmd = RTM_CHANGE; + + return (change_route(rnh, info, rc)); +} + static int change_route_one(struct rib_head *rnh, struct rt_addrinfo *info, - struct rtentry **ret_nrt) + struct rib_cmd_info *rc) { RIB_RLOCK_TRACKER; struct rtentry *rt = NULL; @@ -434,14 +550,18 @@ if ((nh_orig->nh_ifa != nh->nh_ifa) && nh_orig->nh_ifa->ifa_rtrequest) nh_orig->nh_ifa->ifa_rtrequest(RTM_DELETE, rt, nh_orig, info); - if (ret_nrt != NULL) - *ret_nrt = rt; + /* Finalize notification */ + rc->rc_rt = rt; + rc->rc_nh_old = nh_orig; + rc->rc_nh_new = rt->rt_nhop; RT_UNLOCK(rt); /* Update generation id to reflect rtable change */ rnh->rnh_gen++; + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); + RIB_WUNLOCK(rnh); nhop_free(nh_orig); @@ -451,7 +571,7 @@ int change_route(struct rib_head *rnh, struct rt_addrinfo *info, - struct rtentry **ret_nrt) + struct rib_cmd_info *rc) { int error; @@ -468,7 +588,7 @@ * multiple times before failing. */ for (int i = 0; i < RIB_MAX_RETRIES; i++) { - error = change_route_one(rnh, info, ret_nrt); + error = change_route_one(rnh, info, rc); if (error != EAGAIN) break; } @@ -581,4 +701,62 @@ } } +static void +rib_notify(struct rib_head *rnh, enum rib_subscription_type type, + struct rib_cmd_info *rc) +{ + struct rib_subscription *rs; + + CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) { + if (rs->type == type) + rs->func(rnh, rc, rs->arg); + } +} + +struct rib_subscription * +rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg, + enum rib_subscription_type type, int waitok) +{ + struct rib_head *rnh; + struct rib_subscription *rs; + int flags = M_ZERO | (waitok ? M_WAITOK : 0); + + NET_EPOCH_ASSERT(); + KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__)); + rnh = rt_tables_get_rnh(fibnum, family); + + rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags); + if (rs == NULL) + return (NULL); + + rs->func = f; + rs->arg = arg; + rs->type = type; + + RIB_WLOCK(rnh); + CK_STAILQ_INSERT_TAIL(&rnh->rnh_subscribers, rs, next); + RIB_WUNLOCK(rnh); + + return (rs); +} + +int +rib_unsibscribe(uint32_t fibnum, int family, struct rib_subscription *rs) +{ + struct rib_head *rnh; + + NET_EPOCH_ASSERT(); + KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__)); + rnh = rt_tables_get_rnh(fibnum, family); + + if (rnh == NULL) + return (ENOENT); + + RIB_WLOCK(rnh); + CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next); + RIB_WUNLOCK(rnh); + + free(rs, M_RTABLE); + return (0); +} Index: head/sys/net/route/route_ddb.c =================================================================== --- head/sys/net/route/route_ddb.c +++ head/sys/net/route/route_ddb.c @@ -50,6 +50,7 @@ #include #include #include +#include #include /* Index: head/sys/net/route/route_helpers.c =================================================================== --- head/sys/net/route/route_helpers.c +++ head/sys/net/route/route_helpers.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include Index: head/sys/net/route/route_temporal.c =================================================================== --- head/sys/net/route/route_temporal.c +++ head/sys/net/route/route_temporal.c @@ -38,11 +38,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include Index: head/sys/net/route/route_var.h =================================================================== --- head/sys/net/route/route_var.h +++ head/sys/net/route/route_var.h @@ -35,6 +35,7 @@ #ifndef RNF_NORMAL #include #endif +#include #include #include /* struct sockaddr_in */ #include @@ -63,6 +64,7 @@ struct callout expire_callout; /* Callout for expiring dynamic routes */ time_t next_expire; /* Next expire run ts */ struct nh_control *nh_control; /* nexthop subsystem data */ + CK_STAILQ_HEAD(, rib_subscription) rnh_subscribers;/* notification subscribers */ }; #define RIB_RLOCK_TRACKER struct rm_priotracker _rib_tracker @@ -110,12 +112,13 @@ struct radix_node *rt_mpath_unlink(struct rib_head *rnh, struct rt_addrinfo *info, struct rtentry *rto, int *perror); #endif +struct rib_cmd_info; int add_route(struct rib_head *rnh, struct rt_addrinfo *info, - struct rtentry **ret_nrt); + struct rib_cmd_info *rc); int del_route(struct rib_head *rnh, struct rt_addrinfo *info, - struct rtentry **ret_nrt); + struct rib_cmd_info *rc); int change_route(struct rib_head *, struct rt_addrinfo *, - struct rtentry **); + struct rib_cmd_info *rc); VNET_PCPUSTAT_DECLARE(struct rtstat, rtstat); #define RTSTAT_ADD(name, val) \ Index: head/sys/netinet/in_rmx.c =================================================================== --- head/sys/netinet/in_rmx.c +++ head/sys/netinet/in_rmx.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include Index: head/sys/netinet/ip_icmp.c =================================================================== --- head/sys/netinet/ip_icmp.c +++ head/sys/netinet/ip_icmp.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include Index: head/sys/netinet6/icmp6.c =================================================================== --- head/sys/netinet6/icmp6.c +++ head/sys/netinet6/icmp6.c @@ -93,6 +93,7 @@ #include #include #include +#include #include #include Index: head/sys/netinet6/in6_rmx.c =================================================================== --- head/sys/netinet6/in6_rmx.c +++ head/sys/netinet6/in6_rmx.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include Index: head/sys/netinet6/nd6_rtr.c =================================================================== --- head/sys/netinet6/nd6_rtr.c +++ head/sys/netinet6/nd6_rtr.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include