Changeset View
Changeset View
Standalone View
Standalone View
sys/net/route/route_ifaddrs.c
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
* Control interface address fib propagation. | * Control interface address fib propagation. | ||||
* By default, interface address routes are added to the fib of the interface. | * By default, interface address routes are added to the fib of the interface. | ||||
* Once set to non-zero, adds interface address route to all fibs. | * Once set to non-zero, adds interface address route to all fibs. | ||||
*/ | */ | ||||
VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0; | VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0; | ||||
SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, | SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET, | ||||
&VNET_NAME(rt_add_addr_allfibs), 0, ""); | &VNET_NAME(rt_add_addr_allfibs), 0, ""); | ||||
/* | static void | ||||
* Executes routing tables change specified by @cmd and @info for the fib | report_operation(uint32_t fibnum, struct rib_cmd_info *rc) | ||||
* @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; | struct nhop_object *nh; | ||||
int error; | |||||
error = rib_action(fibnum, cmd, info, &rc); | if (rc->rc_cmd == RTM_DELETE) | ||||
if (error == 0) { | nh = nhop_select(rc->rc_nh_old, 0); | ||||
if (cmd == RTM_ADD) | |||||
nh = nhop_select(rc.rc_nh_new, 0); | |||||
else | else | ||||
nh = nhop_select(rc.rc_nh_old, 0); | nh = nhop_select(rc->rc_nh_new, 0); | ||||
rt_routemsg(cmd, rc.rc_rt, nh, fibnum); | 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); | 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); | |||||
} | } | ||||
} | |||||
} | |||||
/* | 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 | int | ||||
rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info) | 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 error = 0, last_error = 0; | struct rib_cmd_info rc = {}; | ||||
bool didwork = false; | |||||
if (V_rt_add_addr_allfibs == 0) { | NET_EPOCH_ASSERT(); | ||||
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) { | int error = rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg, | ||||
if (didwork) { | op_flags, &rc); | ||||
error = 0; | if (error != 0) | ||||
} else { | return (error); | ||||
/* we only give an error if it wasn't in any table */ | report_operation(fibnum, &rc); | ||||
error = ((info->rti_flags & RTF_HOST) ? | |||||
EHOSTUNREACH : ENETUNREACH); | 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); | |||||
} | } | ||||
} else { | |||||
if (last_error != 0) { | |||||
/* return an error if any of them failed */ | |||||
error = last_error; | |||||
} | } | ||||
} | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa, | add_loopback_route_flags(struct ifaddr *ifa, struct sockaddr *ia, int op_flags) | ||||
struct sockaddr *ia) | |||||
{ | { | ||||
struct rib_cmd_info rc; | struct rib_cmd_info rc; | ||||
struct epoch_tracker et; | |||||
int error; | int error; | ||||
struct rt_addrinfo info; | |||||
struct sockaddr_dl null_sdl; | |||||
struct ifnet *ifp; | |||||
ifp = ifa->ifa_ifp; | NET_EPOCH_ASSERT(); | ||||
NET_EPOCH_ENTER(et); | struct ifnet *ifp = ifa->ifa_ifp; | ||||
bzero(&info, sizeof(info)); | struct nhop_object *nh = nhop_alloc(ifp->if_fib, ia->sa_family); | ||||
if (cmd != RTM_DELETE) | struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT }; | ||||
info.rti_ifp = V_loif; | if (nh == NULL) | ||||
if (cmd == RTM_ADD) { | return (ENOMEM); | ||||
/* 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); | |||||
error = rib_action(ifp->if_fib, cmd, &info, &rc); | nhop_set_direct_gw(nh, ifp); | ||||
NET_EPOCH_EXIT(et); | nhop_set_transmit_ifp(nh, V_loif); | ||||
nhop_set_src(nh, ifaof_ifpforaddr(ifa->ifa_addr, ifp)); | |||||
if (error == 0 || | nhop_set_pinned(nh, true); | ||||
(cmd == RTM_ADD && error == EEXIST) || | nhop_set_rtflags(nh, RTF_STATIC); | ||||
(cmd == RTM_DELETE && (error == ENOENT || error == ESRCH))) | nhop_set_pxtype_flag(nh, NHF_HOST); | ||||
rnd.rnd_nhop = nhop_get_nhop(nh, &error); | |||||
if (error != 0) | |||||
return (error); | return (error); | ||||
error = rib_add_route_px(ifp->if_fib, ia, -1, &rnd, op_flags, &rc); | |||||
log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n", | if (error != 0) | ||||
__func__, otype, if_name(ifp), error); | log(LOG_DEBUG, "%s: failed to update interface %s route: %u\n", | ||||
__func__, if_name(ifp), error); | |||||
return (error); | return (error); | ||||
} | } | ||||
int | int | ||||
ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) | ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) | ||||
{ | { | ||||
struct epoch_tracker et; | |||||
return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia)); | 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 | int | ||||
ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) | ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) | ||||
{ | { | ||||
struct epoch_tracker et; | |||||
return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia)); | NET_EPOCH_ENTER(et); | ||||
int error = add_loopback_route_flags(ifa, ia, RTM_F_REPLACE | RTM_F_FORCE); | |||||
NET_EPOCH_EXIT(et); | |||||
return (error); | |||||
} | } | ||||
int | int | ||||
ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia) | 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; | |||||
return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia)); | NET_EPOCH_ENTER(et); | ||||
link_init_sdl(ifp, gw, ifp->if_type); | |||||
error = rib_del_route_px_gw(ifp->if_fib, ia, -1, gw, RTM_F_FORCE, &rc); | |||||
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); | |||||
} | } | ||||
static bool | static bool | ||||
match_kernel_route(const struct rtentry *rt, struct nhop_object *nh) | match_kernel_route(const struct rtentry *rt, struct nhop_object *nh) | ||||
{ | { | ||||
if (!NH_IS_NHGRP(nh) && (nhop_get_rtflags(nh) & RTF_PINNED) && | if (!NH_IS_NHGRP(nh) && (nhop_get_rtflags(nh) & RTF_PINNED) && | ||||
nh->nh_aifp->if_fib == nhop_get_fibnum(nh)) | nh->nh_aifp->if_fib == nhop_get_fibnum(nh)) | ||||
return (true); | return (true); | ||||
Show All 40 Lines |