diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h index e65e6be4efda..e73a6dcf8a93 100644 --- a/sys/net/route/route_ctl.h +++ b/sys/net/route/route_ctl.h @@ -75,13 +75,9 @@ 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_add_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen, - struct route_nhop_data *rnd, int op_flags); -int rib_del_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen, - rib_filter_f_t *filter_func, void *filter_arg, int op_flags); - int rib_match_gw(const struct rtentry *rt, const struct nhop_object *nh, void *_data); +int rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info); int rib_add_default_route(uint32_t fibnum, int family, struct ifnet *ifp, struct sockaddr *gw, struct rib_cmd_info *rc); diff --git a/sys/net/route/route_ifaddrs.c b/sys/net/route/route_ifaddrs.c index d88284c2e008..a456ffa28696 100644 --- a/sys/net/route/route_ifaddrs.c +++ b/sys/net/route/route_ifaddrs.c @@ -64,103 +64,112 @@ VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0; SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(rt_add_addr_allfibs), 0, ""); -static void -report_operation(uint32_t fibnum, struct rib_cmd_info *rc) +/* + * 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 int +rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info) { + struct rib_cmd_info rc; struct nhop_object *nh; + int error; - if (rc->rc_cmd == RTM_DELETE) - nh = nhop_select(rc->rc_nh_old, 0); - else - nh = nhop_select(rc->rc_nh_new, 0); - rt_routemsg(rc->rc_cmd, rc->rc_rt, nh, fibnum); -} - -int -rib_add_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen, - struct route_nhop_data *rnd, int op_flags) -{ - struct rib_cmd_info rc = {}; - - NET_EPOCH_ASSERT(); - - int error = rib_add_route_px(fibnum, dst, plen, rnd, op_flags, &rc); - if (error != 0) - return (error); - report_operation(fibnum, &rc); - - if (V_rt_add_addr_allfibs != 0) { - for (int i = 0; i < V_rt_numfibs; i++) { - if (i == fibnum) - continue; - struct rib_head *rnh = rt_tables_get_rnh(fibnum, dst->sa_family); - /* Don't care much about the errors in non-primary fib */ - if (rnh != NULL) { - if (rib_copy_route(rc.rc_rt, rnd, rnh, &rc) == 0) - report_operation(i, &rc); - } - } + 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); } return (error); } +/* + * 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_del_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen, - rib_filter_f_t *filter_func, void *filter_arg, int op_flags) +rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info) { - struct rib_cmd_info rc = {}; + int error = 0, last_error = 0; + bool didwork = false; - NET_EPOCH_ASSERT(); - - int error = rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg, - op_flags, &rc); - if (error != 0) - return (error); - report_operation(fibnum, &rc); - - if (V_rt_add_addr_allfibs != 0) { - for (int i = 0; i < V_rt_numfibs; i++) { - if (i == fibnum) - continue; - /* Don't care much about the errors in non-primary fib */ - if (rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg, - op_flags, &rc) == 0) - report_operation(i, &rc); + 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; } } + if (cmd == RTM_DELETE) { + if (didwork) { + error = 0; + } else { + /* we only give an error if it wasn't in any table */ + error = ((info->rti_flags & RTF_HOST) ? + EHOSTUNREACH : ENETUNREACH); + } + } else { + if (last_error != 0) { + /* return an error if any of them failed */ + error = last_error; + } + } return (error); } static int -add_loopback_route_flags(struct ifaddr *ifa, struct sockaddr *ia, int op_flags) +ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa, + struct sockaddr *ia) { struct rib_cmd_info rc; + struct epoch_tracker et; int error; + struct rt_addrinfo info; + struct sockaddr_dl null_sdl; + struct ifnet *ifp; - NET_EPOCH_ASSERT(); + ifp = ifa->ifa_ifp; - struct ifnet *ifp = ifa->ifa_ifp; - struct nhop_object *nh = nhop_alloc(ifp->if_fib, ia->sa_family); - struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT }; - if (nh == NULL) - return (ENOMEM); + NET_EPOCH_ENTER(et); + bzero(&info, sizeof(info)); + if (cmd != RTM_DELETE) + info.rti_ifp = V_loif; + if (cmd == RTM_ADD) { + /* explicitly specify (loopback) ifa */ + if (info.rti_ifp != NULL) + info.rti_ifa = ifaof_ifpforaddr(ifa->ifa_addr, info.rti_ifp); + } + info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED; + info.rti_info[RTAX_DST] = ia; + info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl; + link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type); - nhop_set_direct_gw(nh, ifp); - nhop_set_transmit_ifp(nh, V_loif); - nhop_set_src(nh, ifaof_ifpforaddr(ifa->ifa_addr, ifp)); - nhop_set_pinned(nh, true); - nhop_set_rtflags(nh, RTF_STATIC); - nhop_set_pxtype_flag(nh, NHF_HOST); - rnd.rnd_nhop = nhop_get_nhop(nh, &error); - if (error != 0) + error = rib_action(ifp->if_fib, cmd, &info, &rc); + NET_EPOCH_EXIT(et); + + if (error == 0 || + (cmd == RTM_ADD && error == EEXIST) || + (cmd == RTM_DELETE && (error == ENOENT || error == ESRCH))) return (error); - error = rib_add_route_px(ifp->if_fib, ia, -1, &rnd, op_flags, &rc); - if (error != 0) - log(LOG_DEBUG, "%s: failed to update interface %s route: %u\n", - __func__, if_name(ifp), error); + log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n", + __func__, otype, if_name(ifp), error); return (error); } @@ -168,49 +177,22 @@ add_loopback_route_flags(struct ifaddr *ifa, struct sockaddr *ia, int op_flags) int ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) { - struct epoch_tracker et; - NET_EPOCH_ENTER(et); - int error = add_loopback_route_flags(ifa, ia, RTM_F_CREATE | RTM_F_FORCE); - NET_EPOCH_EXIT(et); - - return (error); -} - -int -ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) -{ - struct epoch_tracker et; - - NET_EPOCH_ENTER(et); - int error = add_loopback_route_flags(ifa, ia, RTM_F_REPLACE | RTM_F_FORCE); - NET_EPOCH_EXIT(et); - - return (error); + return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia)); } int ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) { - struct ifnet *ifp = ifa->ifa_ifp; - struct sockaddr_dl link_sdl; - struct sockaddr *gw = (struct sockaddr *)&link_sdl; - struct rib_cmd_info rc; - struct epoch_tracker et; - int error; - NET_EPOCH_ENTER(et); + return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia)); +} - link_init_sdl(ifp, gw, ifp->if_type); - error = rib_del_route_px_gw(ifp->if_fib, ia, -1, gw, RTM_F_FORCE, &rc); +int +ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) +{ - NET_EPOCH_EXIT(et); - - if (error != 0) - log(LOG_DEBUG, "%s: failed to delete interface %s route: %u\n", - __func__, if_name(ifp), error); - - return (error); + return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia)); } static bool diff --git a/sys/netinet/in.c b/sys/netinet/in.c index e097698c27dd..790740456160 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -821,34 +821,36 @@ in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *ar } static int -in_handle_prefix_route(uint32_t fibnum, int cmd, struct sockaddr *dst, - int plen, struct ifaddr *ifa, struct ifnet *ifp) +in_handle_prefix_route(uint32_t fibnum, int cmd, + struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa, + struct ifnet *ifp) { - int error = 0; NET_EPOCH_ASSERT(); - if (cmd == RTM_DELETE) { - error = rib_del_kernel_px(fibnum, dst, plen, in_match_ifaddr, ifa, - RTM_F_FORCE); - } else { - struct nhop_object *nh = nhop_alloc(fibnum, dst->sa_family); - struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT }; + /* 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, + }; - if (nh == NULL) - return (ENOMEM); - nhop_set_direct_gw(nh, ifa->ifa_ifp); - nhop_set_transmit_ifp(nh, ifp); - nhop_set_src(nh, ifa); - nhop_set_pinned(nh, true); - nhop_set_pxtype_flag(nh, plen == 32 ? NHF_HOST : 0); - rnd.rnd_nhop = nhop_get_nhop(nh, &error); - if (error != 0) - return (error); - int op_flags = RTM_F_CREATE | RTM_F_REPLACE | RTM_F_FORCE; - error = rib_add_kernel_px(fibnum, dst, plen, &rnd, op_flags); - } - return (error); + struct rt_addrinfo info = { + .rti_ifa = ifa, + .rti_ifp = ifp, + .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)); } /* @@ -957,12 +959,19 @@ 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; ia_getrtprefix(ia, &daddr, &maddr); - int plen = bitcount32(maddr.s_addr); + struct sockaddr_in mask = { + .sin_family = AF_INET, + .sin_len = sizeof(struct sockaddr_in), + .sin_addr = maddr, + }; + + pmask = (maddr.s_addr != INADDR_BROADCAST) ? &mask : NULL; struct sockaddr_in dst = { .sin_family = AF_INET, @@ -980,8 +989,7 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia) uint32_t fibnum = ifa->ifa_ifp->if_fib; NET_EPOCH_ENTER(et); - error = in_handle_prefix_route(fibnum, cmd, (struct sockaddr *)&dst, - plen, ifa, ifp); + error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa, ifp); NET_EPOCH_EXIT(et); return (error); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 4b3f30a7ee7c..ccdf71cfc01c 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1267,29 +1267,6 @@ in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra, return (error); } -static int -in6_handle_dstaddr_rtadd(uint32_t fibnum, struct sockaddr *dst, struct ifaddr *ifa) -{ - struct nhop_object *nh = nhop_alloc(fibnum, dst->sa_family); - struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT }; - int error = 0; - - if (nh == NULL) - return (ENOMEM); - nhop_set_direct_gw(nh, ifa->ifa_ifp); - nhop_set_transmit_ifp(nh, ifa->ifa_ifp); - nhop_set_src(nh, ifa); - nhop_set_pinned(nh, true); - nhop_set_pxtype_flag(nh, NHF_HOST); - rnd.rnd_nhop = nhop_get_nhop(nh, &error); - if (error == 0) { - int op_flags = RTM_F_CREATE | RTM_F_REPLACE | RTM_F_FORCE; - error = rib_add_kernel_px(fibnum, dst, -1, &rnd, op_flags); - } - - return (error); -} - /* * Adds or deletes interface route for p2p ifa. * Returns 0 on success or errno. @@ -1299,26 +1276,35 @@ in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia) { struct epoch_tracker et; struct ifaddr *ifa = &ia->ia_ifa; - uint32_t fibnum = ifa->ifa_ifp->if_fib; 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, }; - NET_EPOCH_ENTER(et); - if (cmd == RTM_DELETE) { - struct sockaddr_dl link_sdl; - struct sockaddr *gw = (struct sockaddr *)&link_sdl; - struct ifnet *ifp = ifa->ifa_ifp; + struct rt_addrinfo info = { + .rti_ifa = ifa, + .rti_ifp = ifa->ifa_ifp, + .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 */ - link_init_sdl(ifp, gw, ifp->if_type); - error = rib_del_kernel_px(fibnum, (struct sockaddr *)&dst, 128, - rib_match_gw, gw, RTM_F_FORCE); - } else - error = in6_handle_dstaddr_rtadd(fibnum, (struct sockaddr *)&dst, ifa); + NET_EPOCH_ENTER(et); + error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info); NET_EPOCH_EXIT(et); return (error); diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index a8431e23b815..793befc47aa5 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1999,55 +1999,41 @@ pfxlist_onlink_check(void) ND6_ONLINK_UNLOCK(); } -static int -nd6_prefix_rtadd(uint32_t fibnum, struct sockaddr *dst, int plen, - struct ifnet *ifp, struct ifaddr *ifa) -{ - struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT }; - int error = 0; - - struct nhop_object *nh = nhop_alloc(fibnum, dst->sa_family); - if (nh == NULL) - return (ENOMEM); - nhop_set_direct_gw(nh, ifp); - nhop_set_transmit_ifp(nh, ifp); - nhop_set_src(nh, ifa); - nhop_set_pinned(nh, true); - nhop_set_pxtype_flag(nh, plen == 128 ? NHF_HOST : 0); - rnd.rnd_nhop = nhop_get_nhop(nh, &error); - if (error == 0) { - int op_flags = RTM_F_CREATE | RTM_F_REPLACE | RTM_F_FORCE; - error = rib_add_kernel_px(fibnum, dst, plen, &rnd, op_flags); - } - - return (error); -} - /* * 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 *dst, - int plen, struct ifnet *ifp, struct ifaddr *ifa) +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_ifp = ifp, + .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); - if (cmd == RTM_DELETE) { - struct sockaddr_dl link_sdl; - struct sockaddr *gw = (struct sockaddr *)&link_sdl; - - link_init_sdl(ifp, gw, ifp->if_type); - error = rib_del_kernel_px(fibnum, dst, plen, - rib_match_gw, gw, RTM_F_FORCE); - } else - error = nd6_prefix_rtadd(fibnum, dst, plen, ifp, ifa); - + error = rib_handle_ifaddr_info(fibnum, cmd, &info); NET_EPOCH_EXIT(et); - return (error); } @@ -2056,8 +2042,15 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) { int error; + 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, - (struct sockaddr *)&pr->ndpr_prefix, pr->ndpr_plen, pr->ndpr_ifp, ifa); + &pr->ndpr_prefix, pmask6, pr->ndpr_ifp, ifa); if (error == 0) pr->ndpr_stateflags |= NDPRF_ONLINK; @@ -2168,9 +2161,10 @@ nd6_prefix_offlink(struct nd_prefix *pr) .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(ifp->if_fib, RTM_DELETE, - (struct sockaddr *)&pr->ndpr_prefix, pr->ndpr_plen, ifp, NULL); + &pr->ndpr_prefix, pmask6, ifp, NULL); a_failure = 1; if (error == 0) {