From owner-dev-commits-src-main@freebsd.org Sat Jan 16 22:42:46 2021 Return-Path: Delivered-To: dev-commits-src-main@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 25A6C4ED4F2; Sat, 16 Jan 2021 22:42:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4DJCjy0Qtfz3CJY; Sat, 16 Jan 2021 22:42:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 0122117087; Sat, 16 Jan 2021 22:42:46 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 10GMgjOb039626; Sat, 16 Jan 2021 22:42:45 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 10GMgj08039625; Sat, 16 Jan 2021 22:42:45 GMT (envelope-from git) Date: Sat, 16 Jan 2021 22:42:45 GMT Message-Id: <202101162242.10GMgj08039625@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: "Alexander V. Chernikov" Subject: git: 81728a538d24 - main - Split rtinit() into multiple functions. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: melifaro X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 81728a538d24f483d0986850fa3f51d5d84d8f26 Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-main@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for the main branch of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 16 Jan 2021 22:42:46 -0000 The branch main has been updated by melifaro: URL: https://cgit.FreeBSD.org/src/commit/?id=81728a538d24f483d0986850fa3f51d5d84d8f26 commit 81728a538d24f483d0986850fa3f51d5d84d8f26 Author: Alexander V. Chernikov AuthorDate: 2021-01-09 00:19:25 +0000 Commit: Alexander V. Chernikov CommitDate: 2021-01-16 22:42:41 +0000 Split rtinit() into multiple functions. rtinit[1]() is a function used to add or remove interface address prefix routes, similar to ifa_maintain_loopback_route(). It was intended to be family-agnostic. There is a problem with this approach in reality. 1) IPv6 code does not use it for the ifa routes. There is a separate layer, nd6_prelist_(), providing interface for maintaining interface routes. Its part, responsible for the actual route table interaction, mimics rtenty() code. 2) rtinit tries to combine multiple actions in the same function: constructing proper route attributes and handling iterations over multiple fibs, for the non-zero net.add_addr_allfibs use case. It notably increases the code complexity. 3) dstaddr handling. flags parameter re-uses RTF_ flags. As there is no special flag for p2p connections, host routes and p2p routes are handled in the same way. Additionally, mapping IFA flags to RTF flags makes the interface pretty messy. It make rtinit() to clash with ifa_mainain_loopback_route() for IPV4 interface aliases. 4) rtinit() is the last customer passing non-masked prefixes to rib_action(), complicating rib_action() implementation. 5) rtinit() coupled ifa announce/withdrawal notifications, producing "false positive" ifa messages in certain corner cases. To address all these points, the following has been done: * rtinit() has been split into multiple functions: - Route attribute construction were moved to the per-address-family functions, dealing with (2), (3) and (4). - funnction providing net.add_addr_allfibs handling and route rtsock notificaions is the new routing table inteface. - rtsock ifa notificaion has been moved out as well. resulting set of funcion are only responsible for the actual route notifications. Side effects: * /32 alias does not result in interface routes (/32 route and "host" route) * RTF_PINNED is now set for IPv6 prefixes corresponding to the interface addresses Differential revision: https://reviews.freebsd.org/D28186 --- sys/net/if_spppsubr.c | 10 +- sys/net/route.c | 24 ----- sys/net/route.h | 5 - sys/net/route/route_ctl.h | 1 + sys/net/route/route_ifaddrs.c | 192 ++++++++------------------------- sys/netinet/in.c | 133 +++++++++++++++++++++-- sys/netinet/in_var.h | 1 + sys/netinet/raw_ip.c | 4 +- sys/netinet6/in6.c | 54 +++++++++- sys/netinet6/nd6_rtr.c | 165 ++++++++++------------------ tests/sys/net/routing/test_rtsock_l3.c | 5 +- 11 files changed, 294 insertions(+), 300 deletions(-) diff --git a/sys/net/if_spppsubr.c b/sys/net/if_spppsubr.c index 5a18147f14a7..fbf7b0ea8f4c 100644 --- a/sys/net/if_spppsubr.c +++ b/sys/net/if_spppsubr.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -4881,9 +4882,12 @@ sppp_set_ip_addr(struct sppp *sp, u_long src) if (ifa != NULL) { int error; + int fibnum = ifp->if_fib; + rt_addrmsg(RTM_DELETE, ifa, fibnum); /* delete old route */ - error = rtinit(ifa, (int)RTM_DELETE, RTF_HOST); + ia = ifatoia(ifa); + error = in_handle_ifaddr_route(RTM_DELETE, ia); if (debug && error) { log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit DEL failed, error=%d\n", SPP_ARGS(ifp), error); @@ -4891,14 +4895,14 @@ sppp_set_ip_addr(struct sppp *sp, u_long src) /* set new address */ si->sin_addr.s_addr = htonl(src); - ia = ifatoia(ifa); IN_IFADDR_WLOCK(); LIST_REMOVE(ia, ia_hash); LIST_INSERT_HEAD(INADDR_HASH(si->sin_addr.s_addr), ia, ia_hash); IN_IFADDR_WUNLOCK(); + rt_addrmsg(RTM_ADD, ifa, fibnum); /* add new route */ - error = rtinit(ifa, (int)RTM_ADD, RTF_HOST); + error = in_handle_ifaddr_route(RTM_ADD, ia); if (debug && error) { log(LOG_DEBUG, SPP_FMT "sppp_set_ip_addr: rtinit ADD failed, error=%d", SPP_ARGS(ifp), error); diff --git a/sys/net/route.c b/sys/net/route.c index b3383f90789b..7e087569d45f 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -767,27 +767,3 @@ rt_routemsg_info(int cmd, struct rt_addrinfo *info, int fibnum) return (rtsock_routemsg_info(cmd, info, fibnum)); } - -/* - * This is called to generate messages from the routing socket - * indicating a network interface has had addresses associated with it. - */ -void -rt_newaddrmsg_fib(int cmd, struct ifaddr *ifa, struct rtentry *rt, int fibnum) -{ - - KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE, - ("unexpected cmd %u", cmd)); - KASSERT((fibnum >= 0 && fibnum < rt_numfibs), - ("%s: fib out of range 0 <=%d<%d", __func__, fibnum, rt_numfibs)); - - if (cmd == RTM_ADD) { - rt_addrmsg(cmd, ifa, fibnum); - if (rt != NULL) - rt_routemsg(cmd, rt, nhop_select(rt->rt_nhop, 0), fibnum); - } else { - if (rt != NULL) - rt_routemsg(cmd, rt, nhop_select(rt->rt_nhop, 0), fibnum); - rt_addrmsg(cmd, ifa, fibnum); - } -} diff --git a/sys/net/route.h b/sys/net/route.h index 96a8e78ecb3a..f9928ab6a776 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -415,7 +415,6 @@ void rt_ifannouncemsg(struct ifnet *, int); void rt_ifmsg(struct ifnet *); void rt_missmsg(int, struct rt_addrinfo *, int, int); void rt_missmsg_fib(int, struct rt_addrinfo *, int, int, int); -void rt_newaddrmsg_fib(int, struct ifaddr *, struct rtentry *, int); int rt_addrmsg(int, struct ifaddr *, int); int rt_routemsg(int, struct rtentry *, struct nhop_object *, int); int rt_routemsg_info(int, struct rt_addrinfo *, int); @@ -433,10 +432,6 @@ void rt_updatemtu(struct ifnet *); void rt_flushifroutes_af(struct ifnet *, int); void rt_flushifroutes(struct ifnet *ifp); -/* XXX MRT COMPAT VERSIONS THAT SET UNIVERSE to 0 */ -/* Thes are used by old code not yet converted to use multiple FIBS */ -int rtinit(struct ifaddr *, int, int); - /* XXX MRT NEW VERSIONS THAT USE FIBs * For now the protocol indepedent versions are the same as the AF_INET ones * but this will change.. diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h index c52c6b96e126..ecbc9ee91dc0 100644 --- a/sys/net/route/route_ctl.h +++ b/sys/net/route/route_ctl.h @@ -52,6 +52,7 @@ int rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc); int rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info, struct rib_cmd_info *rc); +int rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info); typedef void route_notification_t(struct rib_cmd_info *rc, void *); void rib_decompose_notification(struct rib_cmd_info *rc, diff --git a/sys/net/route/route_ifaddrs.c b/sys/net/route/route_ifaddrs.c index 967aa5d75e68..6e264327d66d 100644 --- a/sys/net/route/route_ifaddrs.c +++ b/sys/net/route/route_ifaddrs.c @@ -65,176 +65,74 @@ SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(rt_add_addr_allfibs), 0, ""); /* - * Set up a routing table entry, normally - * for an interface. + * Executes routing tables change specified by @cmd and @info for the fib + * @fibnum. Generates routing message on success. + * Note: it assumes there is only single route (interface route) for the + * provided prefix. + * Returns 0 on success or errno. */ -static inline int -rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) +static int +rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info) { - RIB_RLOCK_TRACKER; - struct epoch_tracker et; - struct sockaddr *dst; - struct sockaddr *netmask; struct rib_cmd_info rc; - struct rt_addrinfo info; - int error = 0; - int startfib, endfib; - struct sockaddr_storage ss; - int didwork = 0; - int a_failure = 0; - struct sockaddr_dl_short sdl; - struct rib_head *rnh; + struct nhop_object *nh; + int error; - if (flags & RTF_HOST) { - dst = ifa->ifa_dstaddr; - netmask = NULL; - } else { - dst = ifa->ifa_addr; - netmask = ifa->ifa_netmask; - } - if (dst->sa_len == 0) - return(EINVAL); - switch (dst->sa_family) { - case AF_INET6: - case AF_INET: - /* We support multiple FIBs. */ - break; - default: - fibnum = RT_DEFAULT_FIB; - break; - } - if (fibnum == RT_ALL_FIBS) { - if (V_rt_add_addr_allfibs == 0 && cmd == (int)RTM_ADD) - startfib = endfib = ifa->ifa_ifp->if_fib; - else { - startfib = 0; - endfib = rt_numfibs - 1; - } - } else { - KASSERT((fibnum < rt_numfibs), ("rtinit1: bad fibnum")); - startfib = fibnum; - endfib = fibnum; + error = rib_action(fibnum, cmd, info, &rc); + if (error == 0) { + if (cmd == RTM_ADD) + nh = nhop_select(rc.rc_nh_new, 0); + else + nh = nhop_select(rc.rc_nh_old, 0); + rt_routemsg(cmd, rc.rc_rt, nh, fibnum); } - /* - * If it's a delete, check that if it exists, - * it's on the correct interface or we might scrub - * a route to another ifa which would - * be confusing at best and possibly worse. - */ - if (cmd == RTM_DELETE) { - /* - * It's a delete, so it should already exist.. - * If it's a net, mask off the host bits - * (Assuming we have a mask) - * XXX this is kinda inet specific.. - */ - if (netmask != NULL) { - rt_maskedcopy(dst, (struct sockaddr *)&ss, netmask); - dst = (struct sockaddr *)&ss; - } - } - bzero(&sdl, sizeof(struct sockaddr_dl_short)); - sdl.sdl_family = AF_LINK; - sdl.sdl_len = sizeof(struct sockaddr_dl_short); - sdl.sdl_type = ifa->ifa_ifp->if_type; - sdl.sdl_index = ifa->ifa_ifp->if_index; - /* - * Now go through all the requested tables (fibs) and do the - * requested action. Realistically, this will either be fib 0 - * for protocols that don't do multiple tables or all the - * tables for those that do. - */ - for ( fibnum = startfib; fibnum <= endfib; fibnum++) { - if (cmd == RTM_DELETE) { - struct radix_node *rn; - /* - * Look up an rtentry that is in the routing tree and - * contains the correct info. - */ - rnh = rt_tables_get_rnh(fibnum, dst->sa_family); - if (rnh == NULL) - /* this table doesn't exist but others might */ - continue; - RIB_RLOCK(rnh); - rn = rnh->rnh_lookup(dst, netmask, &rnh->head); - error = (rn == NULL || - (rn->rn_flags & RNF_ROOT) || - RNTORT(rn)->rt_nhop->nh_ifa != ifa); - RIB_RUNLOCK(rnh); - if (error) { - /* this is only an error if bad on ALL tables */ - continue; - } - } - /* - * Do the actual request - */ - bzero((caddr_t)&info, sizeof(info)); - info.rti_ifa = ifa; - info.rti_flags = flags | - (ifa->ifa_flags & ~IFA_RTSELF) | RTF_PINNED; - info.rti_info[RTAX_DST] = dst; - info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl; - info.rti_info[RTAX_NETMASK] = netmask; - NET_EPOCH_ENTER(et); - error = rib_action(fibnum, cmd, &info, &rc); - if (error == 0 && rc.rc_rt != NULL) { - /* - * notify any listening routing agents of the change - */ + return (error); +} - /* TODO: interface routes/aliases */ - rt_newaddrmsg_fib(cmd, ifa, rc.rc_rt, fibnum); - didwork = 1; +/* + * Adds/deletes interface prefix specified by @info to the routing table. + * If V_rt_add_addr_allfibs is set, iterates over all existing routing + * tables, otherwise uses fib in @fibnum. Generates routing message for + * each table. + * Returns 0 on success or errno. + */ +int +rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info) +{ + int error, last_error = 0; + bool didwork = false; + + if (V_rt_add_addr_allfibs == 0) { + error = rib_handle_ifaddr_one(fibnum, cmd, info); + didwork = (error == 0); + } else { + for (fibnum = 0; fibnum < V_rt_numfibs; fibnum++) { + error = rib_handle_ifaddr_one(fibnum, cmd, info); + if (error == 0) + didwork = true; + else + last_error = error; } - NET_EPOCH_EXIT(et); - if (error) - a_failure = error; } + if (cmd == RTM_DELETE) { if (didwork) { error = 0; } else { /* we only give an error if it wasn't in any table */ - error = ((flags & RTF_HOST) ? + error = ((info->rti_flags & RTF_HOST) ? EHOSTUNREACH : ENETUNREACH); } } else { - if (a_failure) { + if (last_error != 0) { /* return an error if any of them failed */ - error = a_failure; + error = last_error; } } return (error); } -/* - * Set up a routing table entry, normally - * for an interface. - */ -int -rtinit(struct ifaddr *ifa, int cmd, int flags) -{ - struct sockaddr *dst; - int fib = RT_DEFAULT_FIB; - - if (flags & RTF_HOST) { - dst = ifa->ifa_dstaddr; - } else { - dst = ifa->ifa_addr; - } - - switch (dst->sa_family) { - case AF_INET6: - case AF_INET: - /* We do support multiple FIBs. */ - fib = RT_ALL_FIBS; - break; - } - return (rtinit1(ifa, cmd, flags, fib)); -} - static int ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa, struct sockaddr *ia) diff --git a/sys/netinet/in.c b/sys/netinet/in.c index cf6541dca879..d6c0c350dec5 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -58,6 +58,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include @@ -709,6 +711,125 @@ in_gifaddr_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp, struct thread *td) return (0); } +static int +in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *arg) +{ + + if (nh->nh_ifa == (struct ifaddr *)arg) + return (1); + + return (0); +} + +static int +in_handle_prefix_route(uint32_t fibnum, int cmd, + struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa) +{ + + NET_EPOCH_ASSERT(); + + /* Prepare gateway */ + struct sockaddr_dl_short sdl = { + .sdl_family = AF_LINK, + .sdl_len = sizeof(struct sockaddr_dl_short), + .sdl_type = ifa->ifa_ifp->if_type, + .sdl_index = ifa->ifa_ifp->if_index, + }; + + struct rt_addrinfo info = { + .rti_ifa = ifa, + .rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST), + .rti_info = { + [RTAX_DST] = (struct sockaddr *)dst, + [RTAX_NETMASK] = (struct sockaddr *)netmask, + [RTAX_GATEWAY] = (struct sockaddr *)&sdl, + }, + /* Ensure we delete the prefix IFF prefix ifa matches */ + .rti_filter = in_match_ifaddr, + .rti_filterdata = ifa, + }; + + return (rib_handle_ifaddr_info(fibnum, cmd, &info)); +} + +/* + * Adds or delete interface route corresponding to @ifa. + * There can be multiple options: + * 1) Adding addr with prefix on non-p2p/non-lo interface. + * Example: 192.0.2.1/24. Action: add route towards + * 192.0.2.0/24 via this interface, using ifa as an address source. + * Note: route to 192.0.2.1 will be installed separately via + * ifa_maintain_loopback_route(). + * 2) Adding addr with "host" mask. + * Example: 192.0.2.2/32. In this case no action is performed, + * as the route should be installed by ifa_maintain_loopback_route(). + * Returns 0 to indicate success. + * 3) Adding address with or without prefix to p2p interface. + * Example: 10.0.0.1/24->10.0.0.2. In this case, all other addresses + * covered by prefix, does not make sense in the context of p2p link. + * Action: add route towards 10.0.0.2 via this interface, using ifa as an + * address source. + * Similar to (1), route to 10.0.0.1 will be installed by + * ifa_maintain_loopback_route(). + * 4) Adding address with or without prefix to loopback interface. + * Example: 192.0.2.1/24. In this case, trafic to non-host addresses cannot + * be forwarded, as it would introduce an infinite cycle. + * Similar to (2), perform no action and return 0. Loopback route + * will be installed by ifa_maintain_loopback_route(). + */ +int +in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia) +{ + struct ifaddr *ifa = &ia->ia_ifa; + struct in_addr daddr, maddr; + struct sockaddr_in *pmask; + struct epoch_tracker et; + int error; + + /* Case 4: ignore loopbacks */ + if (ifa->ifa_ifp->if_flags & IFF_LOOPBACK) + return (0); + + if (ifa->ifa_ifp->if_flags & IFF_POINTOPOINT) { + /* Case 3: install route towards dst addr */ + daddr = ia->ia_dstaddr.sin_addr; + pmask = NULL; + maddr.s_addr = INADDR_BROADCAST; + } else { + daddr = ia->ia_addr.sin_addr; + pmask = &ia->ia_sockmask; + maddr = pmask->sin_addr; + + if (maddr.s_addr == INADDR_BROADCAST) { + /* Case 2: ignore /32 routes */ + return (0); + } + } + + struct sockaddr_in mask = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + .sin_addr = maddr, + }; + + if (pmask != NULL) + pmask = &mask; + + struct sockaddr_in dst = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + .sin_addr.s_addr = daddr.s_addr & maddr.s_addr, + }; + + uint32_t fibnum = ifa->ifa_ifp->if_fib; + NET_EPOCH_ENTER(et); + error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa); + NET_EPOCH_EXIT(et); + + return (error); +} + + #define rtinitflags(x) \ ((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \ ? RTF_HOST : 0) @@ -785,7 +906,8 @@ in_addprefix(struct in_ifaddr *target, int flags) /* * No-one seem to have this prefix route, so we try to insert it. */ - error = rtinit(&target->ia_ifa, (int)RTM_ADD, flags); + rt_addrmsg(RTM_ADD, &target->ia_ifa, target->ia_ifp->if_fib); + error = in_handle_ifaddr_route(RTM_ADD, target); if (!error) target->ia_flags |= IFA_ROUTE; return (error); @@ -917,8 +1039,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) if ((ia->ia_flags & IFA_ROUTE) == 0) { ifa_ref(&ia->ia_ifa); IN_IFADDR_RUNLOCK(&in_ifa_tracker); - error = rtinit(&(target->ia_ifa), (int)RTM_DELETE, - rtinitflags(target)); + error = in_handle_ifaddr_route(RTM_DELETE, target); if (error == 0) target->ia_flags &= ~IFA_ROUTE; else @@ -927,8 +1048,7 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) /* Scrub all entries IFF interface is different */ in_scrubprefixlle(target, target->ia_ifp != ia->ia_ifp, flags); - error = rtinit(&ia->ia_ifa, (int)RTM_ADD, - rtinitflags(ia) | RTF_UP); + error = in_handle_ifaddr_route(RTM_ADD, ia); if (error == 0) ia->ia_flags |= IFA_ROUTE; else @@ -948,7 +1068,8 @@ in_scrubprefix(struct in_ifaddr *target, u_int flags) /* * As no-one seem to have this prefix, we can remove the route. */ - error = rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target)); + rt_addrmsg(RTM_DELETE, &target->ia_ifa, target->ia_ifp->if_fib); + error = in_handle_ifaddr_route(RTM_DELETE, target); if (error == 0) target->ia_flags &= ~IFA_ROUTE; else diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index fabd8e1ab50c..3a83c5e832ab 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -464,6 +464,7 @@ int in_control(struct socket *, u_long, caddr_t, struct ifnet *, int in_addprefix(struct in_ifaddr *, int); int in_scrubprefix(struct in_ifaddr *, u_int); void in_ifscrub_all(void); +int in_handle_ifaddr_route(int, struct in_ifaddr *); void ip_input(struct mbuf *); void ip_direct_input(struct mbuf *); void in_ifadown(struct ifaddr *ifa, int); diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index a63fc19587f9..c9def015343c 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -864,7 +865,8 @@ rip_ctlinput(int cmd, struct sockaddr *sa, void *vip) err = ifa_del_loopback_route((struct ifaddr *)ia, sa); - err = rtinit(&ia->ia_ifa, RTM_ADD, flags); + rt_addrmsg(RTM_ADD, &ia->ia_ifa, ia->ia_ifp->if_fib); + err = in_handle_ifaddr_route(RTM_ADD, ia); if (err == 0) ia->ia_flags |= IFA_ROUTE; diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 7c572e7b833b..b42cc16cdb6f 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -91,6 +91,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -1272,6 +1273,48 @@ in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, return (error); } +/* + * Adds or deletes interface route for p2p ifa. + * Returns 0 on success or errno. + */ +static int +in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia) +{ + struct epoch_tracker et; + struct ifaddr *ifa = &ia->ia_ifa; + int error; + + /* Prepare gateway */ + struct sockaddr_dl_short sdl = { + .sdl_family = AF_LINK, + .sdl_len = sizeof(struct sockaddr_dl_short), + .sdl_type = ifa->ifa_ifp->if_type, + .sdl_index = ifa->ifa_ifp->if_index, + }; + + struct sockaddr_in6 dst = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = ia->ia_dstaddr.sin6_addr, + }; + + struct rt_addrinfo info = { + .rti_ifa = ifa, + .rti_flags = RTF_PINNED | RTF_HOST, + .rti_info = { + [RTAX_DST] = (struct sockaddr *)&dst, + [RTAX_GATEWAY] = (struct sockaddr *)&sdl, + }, + }; + /* Don't set additional per-gw filters on removal */ + + NET_EPOCH_ENTER(et); + error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info); + NET_EPOCH_EXIT(et); + + return (error); +} + void in6_purgeaddr(struct ifaddr *ifa) { @@ -1305,10 +1348,12 @@ in6_purgeaddr(struct ifaddr *ifa) in6_leavegroup(imm->i6mm_maddr, NULL); free(imm, M_IP6MADDR); } + /* Check if we need to remove p2p route */ plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ + if (ia->ia_dstaddr.sin6_family != AF_INET6) + plen = 0; if ((ia->ia_flags & IFA_ROUTE) && plen == 128) { - error = rtinit(&(ia->ia_ifa), RTM_DELETE, ia->ia_flags | - (ia->ia_dstaddr.sin6_family == AF_INET6 ? RTF_HOST : 0)); + error = in6_handle_dstaddr_rtrequest(RTM_DELETE, ia); if (error != 0) log(LOG_INFO, "%s: err=%d, destination address delete " "failed\n", __func__, error); @@ -1416,7 +1461,7 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, if (pdst->sin6_family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&pdst->sin6_addr, &ia->ia_dstaddr.sin6_addr)) { if ((ia->ia_flags & IFA_ROUTE) != 0 && - (rtinit(&(ia->ia_ifa), (int)RTM_DELETE, RTF_HOST) != 0)) { + (in6_handle_dstaddr_rtrequest(RTM_DELETE, ia) != 0)) { nd6log((LOG_ERR, "in6_update_ifa_internal: failed to " "remove a route to the old destination: %s\n", ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); @@ -1436,13 +1481,12 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */ if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 && ia->ia_dstaddr.sin6_family == AF_INET6) { - int rtflags = RTF_UP | RTF_HOST; /* * Handle the case for ::1 . */ if (ifp->if_flags & IFF_LOOPBACK) ia->ia_flags |= IFA_RTSELF; - error = rtinit(&ia->ia_ifa, RTM_ADD, ia->ia_flags | rtflags); + error = in6_handle_dstaddr_rtrequest(RTM_ADD, ia); if (error) goto done; ia->ia_flags |= IFA_ROUTE; diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index c6317b1ea263..eca704dc2843 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -2020,73 +2020,61 @@ restart: ND6_ONLINK_UNLOCK(); } +/* + * Add or remove interface route specified by @dst, @netmask and @ifp. + * ifa can be NULL. + * Returns 0 on success + */ +static int +nd6_prefix_rtrequest(uint32_t fibnum, int cmd, struct sockaddr_in6 *dst, + struct sockaddr_in6 *netmask, struct ifnet *ifp, struct ifaddr *ifa) +{ + struct epoch_tracker et; + int error; + + /* Prepare gateway */ + struct sockaddr_dl_short sdl = { + .sdl_family = AF_LINK, + .sdl_len = sizeof(struct sockaddr_dl_short), + .sdl_type = ifp->if_type, + .sdl_index = ifp->if_index, + }; + + struct rt_addrinfo info = { + .rti_ifa = ifa, + .rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST), + .rti_info = { + [RTAX_DST] = (struct sockaddr *)dst, + [RTAX_NETMASK] = (struct sockaddr *)netmask, + [RTAX_GATEWAY] = (struct sockaddr *)&sdl, + }, + }; + /* Don't set additional per-gw filters on removal */ + + NET_EPOCH_ENTER(et); + error = rib_handle_ifaddr_info(fibnum, cmd, &info); + NET_EPOCH_EXIT(et); + return (error); +} + static int nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) { - struct sockaddr_dl_short sdl; - struct sockaddr_in6 mask6; - u_long rtflags; - int error, a_failure, fibnum, maxfib; - - bzero(&mask6, sizeof(mask6)); - mask6.sin6_len = sizeof(mask6); - mask6.sin6_addr = pr->ndpr_mask; - rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP; - - bzero(&sdl, sizeof(struct sockaddr_dl_short)); - sdl.sdl_len = sizeof(struct sockaddr_dl_short); - sdl.sdl_family = AF_LINK; - sdl.sdl_type = ifa->ifa_ifp->if_type; - sdl.sdl_index = ifa->ifa_ifp->if_index; - - if(V_rt_add_addr_allfibs) { - fibnum = 0; - maxfib = rt_numfibs; - } else { - fibnum = ifa->ifa_ifp->if_fib; - maxfib = fibnum + 1; - } - a_failure = 0; - for (; fibnum < maxfib; fibnum++) { - struct rt_addrinfo info; - struct rib_cmd_info rc; - - bzero((caddr_t)&info, sizeof(info)); - info.rti_flags = rtflags; - info.rti_info[RTAX_DST] = (struct sockaddr *)&pr->ndpr_prefix; - info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&sdl; - info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask6; - - NET_EPOCH_ASSERT(); - error = rib_action(fibnum, RTM_ADD, &info, &rc); - if (error != 0) { - char ip6buf[INET6_ADDRSTRLEN]; - char ip6bufg[INET6_ADDRSTRLEN]; - char ip6bufm[INET6_ADDRSTRLEN]; - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; - nd6log((LOG_ERR, "%s: failed to add " - "route for a prefix (%s/%d) on %s, gw=%s, mask=%s, " - "flags=%lx errno = %d\n", __func__, - ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), - pr->ndpr_plen, if_name(pr->ndpr_ifp), - ip6_sprintf(ip6bufg, &sin6->sin6_addr), - ip6_sprintf(ip6bufm, &mask6.sin6_addr), - rtflags, error)); + int error; - /* Save last error to return, see rtinit(). */ - a_failure = error; - continue; - } + struct sockaddr_in6 mask6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = pr->ndpr_mask, + }; + struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL; + error = nd6_prefix_rtrequest(pr->ndpr_ifp->if_fib, RTM_ADD, + &pr->ndpr_prefix, pmask6, pr->ndpr_ifp, ifa); + if (error == 0) pr->ndpr_stateflags |= NDPRF_ONLINK; - struct nhop_object *nh = nhop_select(rc.rc_nh_new, 0); - rt_routemsg(RTM_ADD, rc.rc_rt, nh, fibnum); - } - /* Return the last error we got. */ - return (a_failure); + return (error); } static int @@ -2178,11 +2166,10 @@ nd6_prefix_offlink(struct nd_prefix *pr) int error = 0; struct ifnet *ifp = pr->ndpr_ifp; struct nd_prefix *opr; - struct sockaddr_in6 sa6, mask6; + struct sockaddr_in6 sa6; char ip6buf[INET6_ADDRSTRLEN]; uint64_t genid; - int fibnum, maxfib, a_failure; - struct epoch_tracker et; + int a_failure; ND6_ONLINK_LOCK_ASSERT(); ND6_UNLOCK_ASSERT(); @@ -2190,50 +2177,16 @@ nd6_prefix_offlink(struct nd_prefix *pr) if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) return (EEXIST); - bzero(&sa6, sizeof(sa6)); - sa6.sin6_family = AF_INET6; - sa6.sin6_len = sizeof(sa6); - bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, - sizeof(struct in6_addr)); - bzero(&mask6, sizeof(mask6)); - mask6.sin6_family = AF_INET6; - mask6.sin6_len = sizeof(sa6); - bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); - - if (V_rt_add_addr_allfibs) { - fibnum = 0; - maxfib = rt_numfibs; - } else { - fibnum = ifp->if_fib; - maxfib = fibnum + 1; - } + struct sockaddr_in6 mask6 = { + .sin6_family = AF_INET6, + .sin6_len = sizeof(struct sockaddr_in6), + .sin6_addr = pr->ndpr_mask, + }; + struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL; - a_failure = 0; - NET_EPOCH_ENTER(et); - for (; fibnum < maxfib; fibnum++) { - struct rt_addrinfo info; - struct rib_cmd_info rc; - - bzero((caddr_t)&info, sizeof(info)); - info.rti_flags = RTF_GATEWAY; - info.rti_info[RTAX_DST] = (struct sockaddr *)&sa6; - info.rti_info[RTAX_GATEWAY] = NULL; - info.rti_info[RTAX_NETMASK] = (struct sockaddr *)&mask6; - - NET_EPOCH_ASSERT(); - error = rib_action(fibnum, RTM_DELETE, &info, &rc); - if (error != 0) { - /* Save last error to return, see rtinit(). */ - a_failure = error; - continue; - } + error = nd6_prefix_rtrequest(ifp->if_fib, RTM_DELETE, + &pr->ndpr_prefix, pmask6, ifp, NULL); - /* report route deletion to the routing socket. */ - struct nhop_object *nh = nhop_select(rc.rc_nh_old, 0); - rt_routemsg(RTM_DELETE, rc.rc_rt, nh, fibnum); - } - NET_EPOCH_EXIT(et); - error = a_failure; a_failure = 1; if (error == 0) { pr->ndpr_stateflags &= ~NDPRF_ONLINK; @@ -2283,7 +2236,7 @@ restart: /* XXX: can we still set the NDPRF_ONLINK flag? */ nd6log((LOG_ERR, "%s: failed to delete route: %s/%d on %s (errno=%d)\n", - __func__, ip6_sprintf(ip6buf, &sa6.sin6_addr), + __func__, ip6_sprintf(ip6buf, &pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(ifp), error)); } diff --git a/tests/sys/net/routing/test_rtsock_l3.c b/tests/sys/net/routing/test_rtsock_l3.c index daba6e5013e6..9486ac466965 100644 --- a/tests/sys/net/routing/test_rtsock_l3.c +++ b/tests/sys/net/routing/test_rtsock_l3.c @@ -1127,8 +1127,7 @@ ATF_TC_BODY(rtm_add_v6_gu_ifa_prefixroute_success, tc) /* 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; + int expected_rt_flags = RTF_UP | RTF_DONE | RTF_PINNED; verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); } @@ -1257,7 +1256,7 @@ ATF_TC_BODY(rtm_del_v6_gu_ifa_prefixroute_success, tc) /* gateway should be link sdl with ifindex of an address interface */ verify_link_gateway(rtm, c->ifindex); - int expected_rt_flags = RTF_DONE; + int expected_rt_flags = RTF_DONE | RTF_PINNED; verify_route_message_extra(rtm, c->ifindex, expected_rt_flags); }