diff --git a/filter/filter.c b/filter/filter.c index ec7e8997..4eaf6405 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -53,57 +53,36 @@ void (*bt_assert_hook)(int result, struct f_inst *assert); -struct filter_roa_notifier { - resource r; - struct listener L; +struct filter_roa_reloader { + node n; + struct listener *L; struct rtable *roa_table; struct filter_slot *slot; }; -static void filter_roa_notifier_hook(struct listener *L, void *data UNUSED) { - struct filter_roa_notifier *frn = SKIP_BACK(struct filter_roa_notifier, L, L); - frn->slot->reloader(frn->slot); +static void filter_roa_reloader_notify(void *self, const void *data UNUSED) { + struct filter_roa_reloader *frr = self; + frr->slot->reloader(frr->slot); } -static void filter_roa_notifier_unsubscribe(struct listener *L) { - struct filter_roa_notifier *frn = SKIP_BACK(struct filter_roa_notifier, L, L); - rfree(frn); +static void filter_roa_reloader_unsubscribe(void *self) { + struct filter_roa_reloader *frr = self; + rem_node(&(frr->n)); + mb_free(self); } -static void filter_roa_notifier_free(resource *r) { - struct filter_roa_notifier *frn = (void *) r; - unsubscribe(&(frn->L)); -} - -static struct resclass filter_roa_notifier_class = { - .name = "Filter ROA Notifier", - .size = sizeof(struct filter_roa_notifier), - .free = filter_roa_notifier_free, - .dump = NULL, - .lookup = NULL, - .memsize = NULL, -}; - -static void filter_roa_notifier_subscribe(struct rtable *roa_table, struct filter_slot *slot, const net_addr *n UNUSED, u32 as UNUSED) { - struct listener *oldL; +static void filter_roa_reloader_subscribe(struct rtable *roa_table, struct filter_slot *slot, const net_addr *n UNUSED, u32 as UNUSED) { + struct filter_roa_reloader *oldfrr; node *x; - WALK_LIST2(oldL, x, slot->notifiers, receiver_node) - if (oldL->hook == filter_roa_notifier_hook) - { - struct filter_roa_notifier *old = SKIP_BACK(struct filter_roa_notifier, L, oldL); - if ((old->roa_table == roa_table) && (old->slot == slot)) - return; /* Old notifier found for the same event. */ - } + WALK_LIST2(oldfrr, x, slot->notifiers, n) + if (oldfrr->roa_table == roa_table) + return; /* Old notifier found for the same event. */ - struct filter_roa_notifier *frn = ralloc(slot->p, &filter_roa_notifier_class); - frn->L = (struct listener) { - .hook = filter_roa_notifier_hook, - .unsub = filter_roa_notifier_unsubscribe, - }; - frn->roa_table = roa_table; - frn->slot = slot; - - subscribe(&(frn->L), &(roa_table->listeners), &(slot->notifiers)); + struct filter_roa_reloader *frr = mb_allocz(slot->p, sizeof(struct filter_roa_reloader)); + frr->roa_table = roa_table; + frr->slot = slot; + add_tail(&(slot->notifiers), &(frr->n)); + frr->L = subscribe(slot->p, &(roa_table->listeners), filter_roa_reloader_notify, filter_roa_reloader_unsubscribe, frr); } static struct adata undef_adata; /* adata of length 0 used for undefined */ @@ -1612,7 +1591,7 @@ interpret(struct f_inst *what) else { if (f_slot) - filter_roa_notifier_subscribe(table, f_slot, v1.val.net, as); + filter_roa_reloader_subscribe(table, f_slot, v1.val.net, as); res.val.i = net_roa_check(table, v1.val.net, as); } diff --git a/filter/filter.h b/filter/filter.h index 2f0fa735..a99f38cf 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -156,6 +156,21 @@ struct filter_slot { list notifiers; }; +static inline void filter_slot_init(struct filter_slot *fs, pool *pp, struct filter *filter, void (*reloader)(struct filter_slot *)) +{ + fs->filter = filter; + fs->reloader = reloader; + fs->p = rp_new(pp, "filter slot pool"); + init_list(&(fs->notifiers)); +} + +static inline void filter_slot_deactivate(struct filter_slot *fs) +{ + rfree(fs->p); + ASSERT(EMPTY_LIST(fs->notifiers)); + fs->p = NULL; +} + struct f_inst *f_new_inst(enum f_instruction_code fi_code); struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da); struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa); diff --git a/nest/Makefile b/nest/Makefile index 884d3950..dae45b02 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -1,4 +1,4 @@ -src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c +src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c notify.c password.c proto.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c obj := $(src-o-files) $(all-daemon) $(cf-local) diff --git a/nest/notify.c b/nest/notify.c new file mode 100644 index 00000000..fac1ac81 --- /dev/null +++ b/nest/notify.c @@ -0,0 +1,72 @@ +/* + * BIRD Internet Routing Daemon -- Notificators and Listeners + * + * (c) 2019 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "nest/bird.h" +#include "lib/resource.h" +#include "nest/notify.h" + +struct listener { + resource r; + node n; + + void (*notify)(void *self, const void *data); + void (*unsubscribe)(void *self); + + void *self; +}; + +static void +listener_unsubscribe(resource *r) +{ + struct listener *L = (struct listener *) r; + rem_node(&(L->n)); + CALL(L->unsubscribe, L->self); +} + +static struct resclass listener_class = { + .name = "Listener", + .size = sizeof(struct listener), + .free = listener_unsubscribe, + .dump = NULL, + .lookup = NULL, + .memsize = NULL, +}; + +struct listener * +subscribe(pool *p, list *sender, void (*notify)(void *, const void *), void (*unsubscribe)(void *), void *self) +{ + struct listener *L = ralloc(p, &listener_class); + L->notify = notify; + L->unsubscribe = unsubscribe; + L->self = self; + + add_tail(sender, &(L->n)); + return L; +} + +void unsubscribe(struct listener *L) +{ + L->unsubscribe = NULL; + rfree(L); +} + +void unsubscribe_all(list *sender) +{ + struct listener *L; + node *x, *y; + WALK_LIST2_DELSAFE(L, x, y, *sender, n) + rfree(L); +} + +void notify(list *sender, const void *data) +{ + struct listener *L; + node *x, *y; + WALK_LIST2_DELSAFE(L, x, y, *sender, n) + L->notify(L->self, data); +} diff --git a/nest/notify.h b/nest/notify.h index 9ae736d0..22b67cc5 100644 --- a/nest/notify.h +++ b/nest/notify.h @@ -9,80 +9,14 @@ #ifndef _BIRD_NOTIFY_H_ #define _BIRD_NOTIFY_H_ +#include "lib/resource.h" #include "lib/lists.h" -struct listener { - node sender_node; - node receiver_node; - - void (*hook)(struct listener *who, void *data); - void (*dump)(struct listener *who); - void (*unsub)(struct listener *who); -}; - -static inline void subscribe(struct listener *who, list *sender, list *receiver) -{ - ASSERT(!NODE_VALID(&(who->sender_node))); - ASSERT(!NODE_VALID(&(who->receiver_node))); - - add_tail(sender, &(who->sender_node)); - add_tail(receiver, &(who->receiver_node)); -} - -static inline void unsubscribe(struct listener *who) -{ - /* Allow multiple unsubscribe */ - if (!NODE_VALID(&(who->sender_node)) - && !NODE_VALID(&(who->receiver_node))) - return; - - ASSERT(NODE_VALID(&(who->sender_node)) - && NODE_VALID(&(who->receiver_node))); - - rem_node(&(who->sender_node)); - rem_node(&(who->receiver_node)); - - who->sender_node = who->receiver_node = (node) {}; - CALL(who->unsub, who); -} - -static inline void unsubscribe_all(list *receiver) -{ - struct listener *n; - node *x, *y; - WALK_LIST2_DELSAFE(n, x, y, *receiver, receiver_node) - unsubscribe(n); -} - -static inline void notify(list *sender, void *data) -{ - struct listener *n; - node *x, *y; - WALK_LIST2_DELSAFE(n, x, y, *sender, sender_node) - n->hook(n, data); -} - -static inline void listeners_dump(list *sender, list *receiver) -{ - ASSERT((!sender) || (!receiver)); - ASSERT(sender || receiver); - - struct listener *n; - node *x; - if (sender) - WALK_LIST2(n, x, *sender, sender_node) { - debug("\t\tNotifier: hook %p", n->hook); - CALL(n->dump, n); - debug("\n"); - } - - if (receiver) - WALK_LIST2(n, x, *receiver, receiver_node) { - debug("\t\tNotifier: hook %p", n->hook); - CALL(n->dump, n); - debug("\n"); - } -} +struct listener; +struct listener *subscribe(pool *p, list *sender, void (*notify)(void *self, const void *data), void (*unsubscribe)(void *self), void *self); +void unsubscribe(struct listener *L); +void unsubscribe_all(list *sender); +void notify(list *sender, const void *data); #endif diff --git a/nest/proto.c b/nest/proto.c index 123f3eed..2f03f515 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -160,15 +160,8 @@ proto_add_channel(struct proto *p, struct channel_config *cf) c->proto = p; c->table = cf->table->table; - c->in_filter.filter = cf->in_filter; - c->in_filter.reloader = channel_filter_slot_reimport; - c->in_filter.p = p->pool; - init_list(&c->in_filter.notifiers); - - c->out_filter.filter = cf->out_filter; - c->out_filter.reloader = channel_filter_slot_reexport; - c->out_filter.p = p->pool; - init_list(&c->out_filter.notifiers); + filter_slot_init(&(c->in_filter), p->pool, cf->in_filter, channel_filter_slot_reimport); + filter_slot_init(&(c->out_filter), p->pool, cf->out_filter, channel_filter_slot_reexport); c->rx_limit = cf->rx_limit; c->in_limit = cf->in_limit; @@ -418,8 +411,8 @@ channel_set_state(struct channel *c, uint state) /* No filter notifier shall remain after transitioning from CS_UP state. */ if (cs == CS_UP) { - unsubscribe_all(&(c->in_filter.notifiers)); - unsubscribe_all(&(c->out_filter.notifiers)); + filter_slot_deactivate(&(c->in_filter)); + filter_slot_deactivate(&(c->out_filter)); } switch (state) @@ -626,16 +619,20 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) if (c->merge_limit != cf->merge_limit) export_changed = 1; - /* Reconfigure channel fields */ - c->in_filter.filter = cf->in_filter; - c->out_filter.filter = cf->out_filter; + /* Reconfigure filter slots */ + if (import_changed) { + filter_slot_deactivate(&(c->in_filter)); + filter_slot_init(&(c->in_filter), c->proto->pool, cf->in_filter, channel_filter_slot_reimport); + } else + c->in_filter.filter = cf->in_filter; - if (import_changed) - unsubscribe_all(&(c->in_filter.notifiers)); - - if (export_changed) - unsubscribe_all(&(c->out_filter.notifiers)); + if (export_changed) { + filter_slot_deactivate(&(c->out_filter)); + filter_slot_init(&(c->out_filter), c->proto->pool, cf->out_filter, channel_filter_slot_reexport); + } else + c->out_filter.filter = cf->out_filter; + /* Reconfigure other channel fields */ c->rx_limit = cf->rx_limit; c->in_limit = cf->in_limit; c->out_limit = cf->out_limit; @@ -1335,18 +1332,8 @@ protos_dump_all(void) debug("\tTABLE %s\n", c->table->name); if (c->in_filter.filter) debug("\tInput filter: %s\n", filter_name(c->in_filter.filter)); - if (!EMPTY_LIST(c->in_filter.notifiers)) - { - debug("\tInput filter notifiers:\n"); - listeners_dump(NULL, &(c->in_filter.notifiers)); - } if (c->out_filter.filter) debug("\tOutput filter: %s\n", filter_name(c->out_filter.filter)); - if (!EMPTY_LIST(c->out_filter.notifiers)) - { - debug("\tOutput filter notifiers:\n"); - listeners_dump(NULL, &(c->out_filter.notifiers)); - } } if (p->proto->dump && (p->proto_state != PS_DOWN))