Commit f827cf11 authored by Maria Matejka's avatar Maria Matejka
Browse files

ROA change notifications: simple variant

This implementation will just automatically request channel reload/refeed
when ROA change is detected. It may be sufficient for many uses;
performance impact for big IXPs is unclear.
parent b9deced2
Loading
Loading
Loading
Loading
+68 −5
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@
#include "nest/protocol.h"
#include "nest/iface.h"
#include "nest/attrs.h"
#include "nest/notify.h"
#include "conf/conf.h"
#include "filter/filter.h"

@@ -52,6 +53,59 @@

void (*bt_assert_hook)(int result, struct f_inst *assert);

struct filter_roa_notifier {
  resource r;
  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_notifier_unsubscribe(struct listener *L) {
  struct filter_roa_notifier *frn = SKIP_BACK(struct filter_roa_notifier, L, L);
  rfree(frn);
}

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;
  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. */
    }

  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));
}

static struct adata undef_adata;	/* adata of length 0 used for undefined */

/* Special undef value for paths and clists */
@@ -542,6 +596,7 @@ static struct ea_list **f_eattrs;
static struct linpool *f_pool;
static struct buffer f_buf;
static int f_flags;
static struct filter_slot *f_slot;

static inline void f_cache_eattrs(void)
{
@@ -1555,7 +1610,12 @@ interpret(struct f_inst *what)
    if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
      res.val.i = ROA_UNKNOWN; /* Prefix and table type mismatch */
    else
    {
      if (f_slot)
	filter_roa_notifier_subscribe(table, f_slot, v1.val.net, as);

      res.val.i = net_roa_check(table, v1.val.net, as);
    }

    break;

@@ -1754,12 +1814,12 @@ i_same(struct f_inst *f1, struct f_inst *f2)
 * modified in place, old cached rta is possibly freed.
 */
int
f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags)
f_run(struct filter_slot *filter_slot, struct rte **rte, struct linpool *tmp_pool, int flags)
{
  if (filter == FILTER_ACCEPT)
  if (filter_slot->filter == FILTER_ACCEPT)
    return F_ACCEPT;

  if (filter == FILTER_REJECT)
  if (filter_slot->filter == FILTER_REJECT)
    return F_REJECT;

  int rte_cow = ((*rte)->flags & REF_COW);
@@ -1770,10 +1830,11 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla
  f_old_rta = NULL;
  f_pool = tmp_pool;
  f_flags = flags;
  f_slot = filter_slot;

  LOG_BUFFER_INIT(f_buf);

  struct f_val res = interpret(filter->root);
  struct f_val res = interpret(filter_slot->filter->root);

  if (f_old_rta) {
    /*
@@ -1797,7 +1858,7 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla

  if (res.type != T_RETURN) {
    if (!(f_flags & FF_SILENT))
      log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
      log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter_slot->filter->name);
    return F_ERROR;
  }
  DBG( "done (%u)\n", res.val.i );
@@ -1815,6 +1876,7 @@ f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool)
  f_old_rta = NULL;
  f_pool = tmp_pool;
  f_flags = 0;
  f_slot = NULL;

  LOG_BUFFER_INIT(f_buf);

@@ -1831,6 +1893,7 @@ f_eval(struct f_inst *expr, struct linpool *tmp_pool)
  f_eattrs = NULL;
  f_rte = NULL;
  f_pool = tmp_pool;
  f_slot = NULL;

  LOG_BUFFER_INIT(f_buf);

+9 −1
Original line number Diff line number Diff line
@@ -149,6 +149,13 @@ struct filter {
  struct f_inst *root;
};

struct filter_slot {
  struct filter *filter;
  void (*reloader)(struct filter_slot *);
  pool *p;
  list notifiers;
};

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);
@@ -175,7 +182,7 @@ void trie_format(struct f_trie *t, buffer *buf);
struct ea_list;
struct rte;

int f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
int f_run(struct filter_slot *filter_slot, struct rte **rte, struct linpool *tmp_pool, int flags);
struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool);
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
uint f_eval_int(struct f_inst *expr);
@@ -286,6 +293,7 @@ struct f_trie
#define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val));

#define FF_SILENT 2			/* Silent filter execution */
#define FF_TEMP 4			/* Result of this filter is dropped */

/* Custom route attributes */
struct custom_attribute {

nest/notify.h

0 → 100644
+88 −0
Original line number Diff line number Diff line
/*
 *	BIRD Internet Routing Daemon -- Notificators and Listeners
 *
 *	(c) 2019 Maria Matejka <mq@jmq.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

#ifndef _BIRD_NOTIFY_H_
#define _BIRD_NOTIFY_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");
    }
}


#endif
+55 −13
Original line number Diff line number Diff line
@@ -125,6 +125,16 @@ proto_find_channel_by_name(struct proto *p, const char *n)
  return NULL;
}

static void channel_filter_slot_reimport(struct filter_slot *fs)
{
  return channel_request_reload(SKIP_BACK(struct channel, in_filter, fs));
}

static void channel_filter_slot_reexport(struct filter_slot *fs)
{
  return channel_request_feeding(SKIP_BACK(struct channel, out_filter, fs));
}

/**
 * proto_add_channel - connect protocol to a routing table
 * @p: protocol instance
@@ -150,8 +160,16 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
  c->proto = p;
  c->table = cf->table->table;

  c->in_filter = cf->in_filter;
  c->out_filter = cf->out_filter;
  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);

  c->rx_limit = cf->rx_limit;
  c->in_limit = cf->in_limit;
  c->out_limit = cf->out_limit;
@@ -397,6 +415,13 @@ channel_set_state(struct channel *c, uint state)
  c->channel_state = state;
  c->last_state_change = current_time();

  /* 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));
  }

  switch (state)
  {
  case CS_START:
@@ -492,7 +517,7 @@ channel_reloadable(struct channel *c)
  return c->proto->reload_routes && c->reloadable;
}

static void
void
channel_request_reload(struct channel *c)
{
  ASSERT(c->channel_state == CS_UP);
@@ -592,8 +617,8 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
    return 0;

  /* Note that filter_same() requires arguments in (new, old) order */
  int import_changed = !filter_same(cf->in_filter, c->in_filter);
  int export_changed = !filter_same(cf->out_filter, c->out_filter);
  int import_changed = !filter_same(cf->in_filter, c->in_filter.filter);
  int export_changed = !filter_same(cf->out_filter, c->out_filter.filter);

  if (c->preference != cf->preference)
    import_changed = 1;
@@ -602,8 +627,15 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
    export_changed = 1;

  /* Reconfigure channel fields */
  c->in_filter = cf->in_filter;
  c->out_filter = cf->out_filter;
  c->in_filter.filter = cf->in_filter;
  c->out_filter.filter = cf->out_filter;

  if (import_changed)
    unsubscribe_all(&(c->in_filter.notifiers));

  if (export_changed)
    unsubscribe_all(&(c->out_filter.notifiers));

  c->rx_limit = cf->rx_limit;
  c->in_limit = cf->in_limit;
  c->out_limit = cf->out_limit;
@@ -1301,10 +1333,20 @@ protos_dump_all(void)
    WALK_LIST(c, p->channels)
    {
      debug("\tTABLE %s\n", c->table->name);
      if (c->in_filter)
	debug("\tInput filter: %s\n", filter_name(c->in_filter));
      if (c->out_filter)
	debug("\tOutput filter: %s\n", filter_name(c->out_filter));
      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))
@@ -1731,8 +1773,8 @@ channel_show_info(struct channel *c)
  cli_msg(-1006, "    State:          %s", c_states[c->channel_state]);
  cli_msg(-1006, "    Table:          %s", c->table->name);
  cli_msg(-1006, "    Preference:     %d", c->preference);
  cli_msg(-1006, "    Input filter:   %s", filter_name(c->in_filter));
  cli_msg(-1006, "    Output filter:  %s", filter_name(c->out_filter));
  cli_msg(-1006, "    Input filter:   %s", filter_name(c->in_filter.filter));
  cli_msg(-1006, "    Output filter:  %s", filter_name(c->out_filter.filter));

  if (graceful_restart_state == GRS_ACTIVE)
    cli_msg(-1006, "    GR recovery:   %s%s",
+5 −2
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#include "lib/lists.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "filter/filter.h"
#include "nest/notify.h"
#include "nest/route.h"
#include "conf/conf.h"

@@ -511,8 +513,8 @@ struct channel {
  struct proto *proto;

  struct rtable *table;
  struct filter *in_filter;		/* Input filter */
  struct filter *out_filter;		/* Output filter */
  struct filter_slot in_filter;		/* Input filter */
  struct filter_slot out_filter;	/* Output filter */
  struct channel_limit rx_limit;	/* Receive limit (for in_keep_filtered) */
  struct channel_limit in_limit;	/* Input limit */
  struct channel_limit out_limit;	/* Output limit */
@@ -620,6 +622,7 @@ static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP)
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }

void channel_request_feeding(struct channel *c);
void channel_request_reload(struct channel *c);
void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
void *channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);
int channel_reconfigure(struct channel *c, struct channel_config *cf);
Loading