Index: sys/net/route.c =================================================================== --- sys/net/route.c +++ sys/net/route.c @@ -347,6 +347,9 @@ nhops_init_rib(rh); + /* Init subscription system */ + CK_STAILQ_INIT(&rh->rnh_subscribers); + /* Finally, set base callbacks */ rh->rnh_addaddr = rn_addroute; rh->rnh_deladdr = rn_delete; Index: sys/net/route/route_ctl.h =================================================================== --- sys/net/route/route_ctl.h +++ sys/net/route/route_ctl.h @@ -65,5 +65,19 @@ 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: sys/net/route/route_ctl.c =================================================================== --- sys/net/route/route_ctl.c +++ sys/net/route/route_ctl.c @@ -68,7 +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); /* * Returns rib_head poiner based on @fibnum and RTAX_DST family @@ -227,6 +239,8 @@ rc->rc_rt = RNTORT(rn); rc->rc_nh_new = nh; + + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); } else if ((info->rti_flags & RTF_PINNED) != 0) { /* @@ -256,10 +270,15 @@ } rc->rc_nh_old = rt_old->rt_nhop; rc->rc_nh_new = nh; + + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); } } 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); @@ -410,11 +429,13 @@ rnh->rnh_gen++; rc->rc_rt = rt; rc->rc_nh_old = rt->rt_nhop; + rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc); } RIB_WUNLOCK(rnh); if (error != 0) return (error); + rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); rt_notifydelete(rt, info); /* @@ -555,9 +576,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); @@ -608,6 +632,7 @@ struct rt_addrinfo info; struct rib_head *rnh; struct rtentry *head; + struct rib_cmd_info rc; }; /* @@ -637,7 +662,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; @@ -659,6 +690,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) @@ -668,20 +700,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); @@ -693,6 +729,95 @@ fibnum); 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); +} Index: sys/net/route/route_var.h =================================================================== --- sys/net/route/route_var.h +++ sys/net/route/route_var.h @@ -64,6 +64,7 @@ struct callout expire_callout; /* Callout for expiring dynamic routes */ time_t next_expire; /* Next expire run ts */ struct nh_control *nh_control; /* nexthop subsystem data */ + CK_STAILQ_HEAD(, rib_subscription) rnh_subscribers;/* notification subscribers */ }; #define RIB_RLOCK_TRACKER struct rm_priotracker _rib_tracker