Changeset View
Changeset View
Standalone View
Standalone View
sys/net/route/route_ctl.c
Show First 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* This file contains control plane routing tables functions. | * This file contains control plane routing tables functions. | ||||
* | * | ||||
* All functions assumes they are called in net epoch. | * All functions assumes they are called in net epoch. | ||||
*/ | */ | ||||
struct rib_subscription { | |||||
CK_STAILQ_ENTRY(rib_subscription) next; | |||||
rib_subscription_cb_t *func; | |||||
void *arg; | |||||
enum rib_subscription_type type; | |||||
struct epoch_context epoch_ctx; | |||||
}; | |||||
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 void rt_notifydelete(struct rtentry *rt, struct rt_addrinfo *info); | ||||
static void destroy_subscription_epoch(epoch_context_t ctx); | |||||
/* | /* | ||||
* Returns rib_head poiner based on @fibnum and RTAX_DST family | * Returns rib_head poiner based on @fibnum and RTAX_DST family | ||||
* from @info. | * from @info. | ||||
*/ | */ | ||||
static struct rib_head * | static struct rib_head * | ||||
get_rnh(uint32_t fibnum, const struct rt_addrinfo *info) | get_rnh(uint32_t fibnum, const struct rt_addrinfo *info) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | if (rn != NULL) { | ||||
if (rt->rt_expire > 0) | if (rt->rt_expire > 0) | ||||
tmproutes_update(rnh, rt); | tmproutes_update(rnh, rt); | ||||
/* Finalize notification */ | /* Finalize notification */ | ||||
rnh->rnh_gen++; | rnh->rnh_gen++; | ||||
rc->rc_rt = RNTORT(rn); | rc->rc_rt = RNTORT(rn); | ||||
rc->rc_nh_new = nh; | rc->rc_nh_new = nh; | ||||
rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); | |||||
} else if ((info->rti_flags & RTF_PINNED) != 0) { | } else if ((info->rti_flags & RTF_PINNED) != 0) { | ||||
/* | /* | ||||
* Force removal and re-try addition | * Force removal and re-try addition | ||||
* TODO: better multipath&pinned support | * TODO: better multipath&pinned support | ||||
*/ | */ | ||||
struct sockaddr *info_dst = info->rti_info[RTAX_DST]; | struct sockaddr *info_dst = info->rti_info[RTAX_DST]; | ||||
info->rti_info[RTAX_DST] = ndst; | info->rti_info[RTAX_DST] = ndst; | ||||
Show All 13 Lines | if (rt_old != NULL) { | ||||
rc->rc_cmd = RTM_CHANGE; | rc->rc_cmd = RTM_CHANGE; | ||||
rc->rc_rt = RNTORT(rn); | rc->rc_rt = RNTORT(rn); | ||||
} else { | } else { | ||||
rc->rc_cmd = RTM_DELETE; | rc->rc_cmd = RTM_DELETE; | ||||
rc->rc_rt = rt_old; | rc->rc_rt = rt_old; | ||||
} | } | ||||
rc->rc_nh_old = rt_old->rt_nhop; | rc->rc_nh_old = rt_old->rt_nhop; | ||||
rc->rc_nh_new = nh; | rc->rc_nh_new = nh; | ||||
rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); | |||||
} | } | ||||
} | } | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
if ((rn != NULL) || (rt_old != NULL)) | |||||
rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); | |||||
if (rt_old != NULL) { | if (rt_old != NULL) { | ||||
rt_notifydelete(rt_old, info); | rt_notifydelete(rt_old, info); | ||||
rtfree(rt_old); | rtfree(rt_old); | ||||
} | } | ||||
/* | /* | ||||
* If it still failed to go into the tree, | * If it still failed to go into the tree, | ||||
* then un-make it (this should be a function) | * then un-make it (this should be a function) | ||||
▲ Show 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | del_route(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
RIB_WLOCK(rnh); | RIB_WLOCK(rnh); | ||||
rt = rt_unlinkrte(rnh, info, &error); | rt = rt_unlinkrte(rnh, info, &error); | ||||
if (rt != NULL) { | if (rt != NULL) { | ||||
/* Finalize notification */ | /* Finalize notification */ | ||||
rnh->rnh_gen++; | rnh->rnh_gen++; | ||||
rc->rc_rt = rt; | rc->rc_rt = rt; | ||||
rc->rc_nh_old = rt->rt_nhop; | rc->rc_nh_old = rt->rt_nhop; | ||||
rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); | |||||
} | } | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); | |||||
rt_notifydelete(rt, info); | rt_notifydelete(rt, info); | ||||
/* | /* | ||||
* If the caller wants it, then it can have it, | * If the caller wants it, then it can have it, | ||||
* the entry will be deleted after the end of the current epoch. | * the entry will be deleted after the end of the current epoch. | ||||
*/ | */ | ||||
rtfree(rt); | rtfree(rt); | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | #endif | ||||
rc->rc_rt = rt; | rc->rc_rt = rt; | ||||
rc->rc_nh_old = nh_orig; | rc->rc_nh_old = nh_orig; | ||||
rc->rc_nh_new = rt->rt_nhop; | rc->rc_nh_new = rt->rt_nhop; | ||||
RT_UNLOCK(rt); | RT_UNLOCK(rt); | ||||
/* Update generation id to reflect rtable change */ | /* Update generation id to reflect rtable change */ | ||||
rnh->rnh_gen++; | rnh->rnh_gen++; | ||||
rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); | |||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); | |||||
nhop_free(nh_orig); | nhop_free(nh_orig); | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
change_route(struct rib_head *rnh, struct rt_addrinfo *info, | change_route(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct rib_cmd_info *rc) | struct rib_cmd_info *rc) | ||||
Show All 34 Lines | if (ifa != NULL && ifa->ifa_rtrequest != NULL) | ||||
ifa->ifa_rtrequest(RTM_DELETE, rt, rt->rt_nhop, info); | ifa->ifa_rtrequest(RTM_DELETE, rt, rt->rt_nhop, info); | ||||
} | } | ||||
struct rt_delinfo | struct rt_delinfo | ||||
{ | { | ||||
struct rt_addrinfo info; | struct rt_addrinfo info; | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
struct rtentry *head; | struct rtentry *head; | ||||
struct rib_cmd_info rc; | |||||
}; | }; | ||||
/* | /* | ||||
* Conditionally unlinks @rn from radix tree based | * Conditionally unlinks @rn from radix tree based | ||||
* on info data passed in @arg. | * on info data passed in @arg. | ||||
*/ | */ | ||||
static int | static int | ||||
rt_checkdelroute(struct radix_node *rn, void *arg) | rt_checkdelroute(struct radix_node *rn, void *arg) | ||||
Show All 13 Lines | rt_checkdelroute(struct radix_node *rn, void *arg) | ||||
info->rti_info[RTAX_GATEWAY] = &rt->rt_nhop->gw_sa; | info->rti_info[RTAX_GATEWAY] = &rt->rt_nhop->gw_sa; | ||||
rt = rt_unlinkrte(di->rnh, info, &error); | rt = rt_unlinkrte(di->rnh, info, &error); | ||||
if (rt == NULL) { | if (rt == NULL) { | ||||
/* Either not allowed or not matched. Skip entry */ | /* Either not allowed or not matched. Skip entry */ | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Entry was unlinked. Add to the list and return */ | /* Entry was unlinked. Notify subscribers */ | ||||
di->rnh->rnh_gen++; | |||||
di->rc.rc_rt = rt; | |||||
di->rc.rc_nh_old = rt->rt_nhop; | |||||
rib_notify(di->rnh, RIB_NOTIFY_IMMEDIATE, &di->rc); | |||||
/* Add to the list and return */ | |||||
rt->rt_chain = di->head; | rt->rt_chain = di->head; | ||||
di->head = rt; | di->head = rt; | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Iterates over a routing table specified by @fibnum and @family and | * Iterates over a routing table specified by @fibnum and @family and | ||||
* deletes elements marked by @filter_f. | * deletes elements marked by @filter_f. | ||||
* @fibnum: rtable id | * @fibnum: rtable id | ||||
* @family: AF_ address family | * @family: AF_ address family | ||||
* @filter_f: function returning non-zero value for items to delete | * @filter_f: function returning non-zero value for items to delete | ||||
* @arg: data to pass to the @filter_f function | * @arg: data to pass to the @filter_f function | ||||
* @report: true if rtsock notification is needed. | * @report: true if rtsock notification is needed. | ||||
*/ | */ | ||||
void | void | ||||
rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report) | rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report) | ||||
{ | { | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
struct rt_delinfo di; | struct rt_delinfo di; | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
struct epoch_tracker et; | |||||
rnh = rt_tables_get_rnh(fibnum, family); | rnh = rt_tables_get_rnh(fibnum, family); | ||||
if (rnh == NULL) | if (rnh == NULL) | ||||
return; | return; | ||||
bzero(&di, sizeof(di)); | bzero(&di, sizeof(di)); | ||||
di.info.rti_filter = filter_f; | di.info.rti_filter = filter_f; | ||||
di.info.rti_filterdata = arg; | di.info.rti_filterdata = arg; | ||||
di.rnh = rnh; | di.rnh = rnh; | ||||
di.rc.rc_cmd = RTM_DELETE; | |||||
NET_EPOCH_ENTER(et); | |||||
RIB_WLOCK(rnh); | RIB_WLOCK(rnh); | ||||
rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di); | rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di); | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
if (di.head == NULL) | |||||
return; | |||||
/* We might have something to reclaim. */ | /* We might have something to reclaim. */ | ||||
while (di.head != NULL) { | while (di.head != NULL) { | ||||
rt = di.head; | rt = di.head; | ||||
di.head = rt->rt_chain; | di.head = rt->rt_chain; | ||||
rt->rt_chain = NULL; | rt->rt_chain = NULL; | ||||
di.rc.rc_rt = rt; | |||||
di.rc.rc_nh_old = rt->rt_nhop; | |||||
rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc); | |||||
/* TODO std rt -> rt_addrinfo export */ | /* TODO std rt -> rt_addrinfo export */ | ||||
di.info.rti_info[RTAX_DST] = rt_key(rt); | di.info.rti_info[RTAX_DST] = rt_key(rt); | ||||
di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); | di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); | ||||
rt_notifydelete(rt, &di.info); | rt_notifydelete(rt, &di.info); | ||||
if (report) | if (report) | ||||
rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0, | rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0, | ||||
fibnum); | fibnum); | ||||
rtfree(rt); | rtfree(rt); | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | |||||
} | } | ||||
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); | |||||
} | |||||
} | |||||
/* | |||||
* Subscribe for the changes in the routing table specified by @fibnum and | |||||
* @family. | |||||
* Needs to be run in network epoch. | |||||
* | |||||
* Returns pointer to the subscription structure on success. | |||||
*/ | |||||
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); | |||||
} | |||||
/* | |||||
* Remove rtable subscription @rs from the table specified by @fibnum | |||||
* and @family. | |||||
* Needs to be run in network epoch. | |||||
* | |||||
* Returns 0 on success. | |||||
*/ | |||||
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); | |||||
epoch_call(net_epoch_preempt, destroy_subscription_epoch, | |||||
&rs->epoch_ctx); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Epoch callback indicating subscription is safe to destroy | |||||
*/ | |||||
static void | |||||
destroy_subscription_epoch(epoch_context_t ctx) | |||||
{ | |||||
struct rib_subscription *rs; | |||||
rs = __containerof(ctx, struct rib_subscription, epoch_ctx); | |||||
free(rs, M_RTABLE); | |||||
} | |||||