Index: head/sys/net/route/route_ctl.h =================================================================== --- head/sys/net/route/route_ctl.h +++ head/sys/net/route/route_ctl.h @@ -64,5 +64,20 @@ 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 +}; + +struct rib_subscription; +typedef void rib_subscription_cb_t(struct rib_head *rnh, struct rib_cmd_info *rc, + void *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); +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 @@ -68,10 +68,19 @@ * 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 destroy_subscription_epoch(epoch_context_t ctx); static struct rib_head * get_rnh(uint32_t fibnum, const struct rt_addrinfo *info) @@ -263,6 +272,9 @@ } RIB_WUNLOCK(rnh); + if ((rn != NULL) || (rt_old != NULL)) + rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); + if (rt_old != NULL) { rt_notifydelete(rt_old, info); rtfree(rt_old); @@ -419,6 +431,7 @@ if (error != 0) return (error); + rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); rt_notifydelete(rt, info); /* @@ -559,11 +572,12 @@ /* Update generation id to reflect rtable change */ rnh->rnh_gen++; - rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); RIB_WUNLOCK(rnh); + rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); + nhop_free(nh_orig); return (0); @@ -614,6 +628,7 @@ struct rt_addrinfo info; struct rib_head *rnh; struct rtentry *head; + struct rib_cmd_info rc; }; /* @@ -643,7 +658,13 @@ 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; di->head = rt; @@ -665,6 +686,7 @@ struct rib_head *rnh; struct rt_delinfo di; struct rtentry *rt; + struct epoch_tracker et; rnh = rt_tables_get_rnh(fibnum, family); if (rnh == NULL) @@ -674,20 +696,24 @@ di.info.rti_filter = filter_f; di.info.rti_filterdata = arg; di.rnh = rnh; + di.rc.rc_cmd = RTM_DELETE; + NET_EPOCH_ENTER(et); + RIB_WLOCK(rnh); rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di); RIB_WUNLOCK(rnh); - if (di.head == NULL) - return; - /* We might have something to reclaim. */ while (di.head != NULL) { rt = di.head; di.head = rt->rt_chain; 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 */ di.info.rti_info[RTAX_DST] = rt_key(rt); di.info.rti_info[RTAX_NETMASK] = rt_mask(rt); @@ -699,6 +725,8 @@ fibnum); rtfree(rt); } + + NET_EPOCH_EXIT(et); } static void @@ -713,6 +741,13 @@ } } +/* + * 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) @@ -740,6 +775,13 @@ 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) { @@ -756,7 +798,21 @@ CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next); RIB_WUNLOCK(rnh); - free(rs, M_RTABLE); + 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); +}