5056c559c4
version: EXPORT <filter-spec> for outbound routes (i.e., those announced by BIRD to the rest of the world). IMPORT <filter-spec> for inbound routes (i.e., those imported by BIRD from the rest of the world). where <filter-spec> is one of: ALL pass all routes NONE drop all routes FILTER <name> use named filter FILTER { <filter> } use explicitly defined filter For all protocols, the default is IMPORT ALL, EXPORT NONE. This includes the kernel protocol, so that you need to add EXPORT ALL to get the previous configuration of kernel syncer (as usually, see doc/bird.conf.example for a bird.conf example :)).
379 lines
7.9 KiB
C
379 lines
7.9 KiB
C
/*
|
|
* BIRD -- Protocols
|
|
*
|
|
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
#define LOCAL_DEBUG
|
|
|
|
#include <string.h>
|
|
|
|
#include "nest/bird.h"
|
|
#include "nest/protocol.h"
|
|
#include "lib/resource.h"
|
|
#include "lib/lists.h"
|
|
#include "lib/event.h"
|
|
#include "conf/conf.h"
|
|
#include "nest/route.h"
|
|
#include "nest/iface.h"
|
|
#include "filter/filter.h"
|
|
|
|
static pool *proto_pool;
|
|
|
|
list protocol_list;
|
|
list proto_list;
|
|
|
|
static list inactive_proto_list;
|
|
static list initial_proto_list;
|
|
static list flush_proto_list;
|
|
|
|
static int proto_shutdown_counter;
|
|
|
|
static event *proto_flush_event;
|
|
|
|
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
|
|
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
|
|
|
|
static void proto_flush_all(void *);
|
|
|
|
static void
|
|
proto_enqueue(list *l, struct proto *p)
|
|
{
|
|
int pri = p->proto->priority;
|
|
|
|
if (!pri)
|
|
add_tail(l, &p->n);
|
|
else
|
|
{
|
|
struct proto *q = HEAD(*l);
|
|
while (q->n.next && q->proto->priority >= pri)
|
|
q = (struct proto *) q->n.next;
|
|
insert_node(&p->n, q->n.prev);
|
|
}
|
|
}
|
|
|
|
static void
|
|
proto_relink(struct proto *p)
|
|
{
|
|
list *l;
|
|
|
|
rem_node(&p->n);
|
|
switch (p->core_state)
|
|
{
|
|
case FS_HAPPY:
|
|
l = &proto_list;
|
|
break;
|
|
case FS_FLUSHING:
|
|
l = &flush_proto_list;
|
|
break;
|
|
default:
|
|
l = &inactive_proto_list;
|
|
}
|
|
proto_enqueue(l, p);
|
|
}
|
|
|
|
void *
|
|
proto_new(struct proto_config *c, unsigned size)
|
|
{
|
|
struct protocol *pr = c->proto;
|
|
struct proto *p = mb_allocz(proto_pool, size);
|
|
|
|
p->cf = c;
|
|
p->debug = c->debug;
|
|
p->name = c->name;
|
|
p->preference = c->preference;
|
|
p->disabled = c->disabled;
|
|
p->proto = pr;
|
|
p->table = &master_table;
|
|
p->in_filter = c->in_filter;
|
|
p->out_filter = c->out_filter;
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
proto_init_instance(struct proto *p)
|
|
{
|
|
/* Here we cannot use p->cf->name since it won't survive reconfiguration */
|
|
p->pool = rp_new(proto_pool, p->proto->name);
|
|
p->attn = ev_new(p->pool);
|
|
p->attn->data = p;
|
|
}
|
|
|
|
void *
|
|
proto_config_new(struct protocol *pr, unsigned size)
|
|
{
|
|
struct proto_config *c = cfg_allocz(size);
|
|
|
|
add_tail(&new_config->protos, &c->n);
|
|
c->global = new_config;
|
|
c->proto = pr;
|
|
c->debug = pr->debug;
|
|
c->name = pr->name;
|
|
c->out_filter = FILTER_REJECT;
|
|
return c;
|
|
}
|
|
|
|
void
|
|
protos_preconfig(struct config *c)
|
|
{
|
|
struct protocol *p;
|
|
|
|
init_list(&proto_list);
|
|
init_list(&inactive_proto_list);
|
|
init_list(&initial_proto_list);
|
|
init_list(&flush_proto_list);
|
|
debug("Protocol preconfig:");
|
|
WALK_LIST(p, protocol_list)
|
|
{
|
|
debug(" %s", p->name);
|
|
p->name_counter = 0;
|
|
if (p->preconfig)
|
|
p->preconfig(p, c);
|
|
}
|
|
debug("\n");
|
|
}
|
|
|
|
void
|
|
protos_postconfig(struct config *c)
|
|
{
|
|
struct proto_config *x;
|
|
struct protocol *p;
|
|
|
|
debug("Protocol postconfig:");
|
|
WALK_LIST(x, c->protos)
|
|
{
|
|
debug(" %s", x->name);
|
|
p = x->proto;
|
|
if (p->postconfig)
|
|
p->postconfig(x);
|
|
}
|
|
debug("\n");
|
|
}
|
|
|
|
void
|
|
protos_commit(struct config *c)
|
|
{
|
|
struct proto_config *x;
|
|
struct protocol *p;
|
|
struct proto *q;
|
|
|
|
debug("Protocol commit:");
|
|
WALK_LIST(x, c->protos)
|
|
{
|
|
debug(" %s", x->name);
|
|
p = x->proto;
|
|
q = p->init(x);
|
|
q->proto_state = PS_DOWN;
|
|
q->core_state = FS_HUNGRY;
|
|
proto_enqueue(&initial_proto_list, q);
|
|
}
|
|
debug("\n");
|
|
}
|
|
|
|
static void
|
|
proto_rethink_goal(struct proto *p)
|
|
{
|
|
struct protocol *q = p->proto;
|
|
|
|
if (p->core_state == p->core_goal)
|
|
return;
|
|
if (p->core_goal == FS_HAPPY) /* Going up */
|
|
{
|
|
if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN)
|
|
{
|
|
DBG("Kicking %s up\n", p->name);
|
|
proto_init_instance(p);
|
|
proto_notify_state(p, (q->start ? q->start(p) : PS_UP));
|
|
}
|
|
}
|
|
else /* Going down */
|
|
{
|
|
if (p->proto_state == PS_START || p->proto_state == PS_UP)
|
|
{
|
|
DBG("Kicking %s down\n", p->name);
|
|
proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN));
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
proto_set_goal(struct proto *p, unsigned goal)
|
|
{
|
|
if (p->disabled || shutting_down)
|
|
goal = FS_HUNGRY;
|
|
p->core_goal = goal;
|
|
proto_rethink_goal(p);
|
|
}
|
|
|
|
void
|
|
protos_start(void)
|
|
{
|
|
struct proto *p, *n;
|
|
|
|
debug("Protocol start\n");
|
|
WALK_LIST_DELSAFE(p, n, initial_proto_list)
|
|
proto_set_goal(p, FS_HAPPY);
|
|
}
|
|
|
|
void
|
|
protos_shutdown(void)
|
|
{
|
|
struct proto *p, *n;
|
|
|
|
debug("Protocol shutdown\n");
|
|
WALK_LIST_DELSAFE(p, n, inactive_proto_list)
|
|
if (p->core_state != FS_HUNGRY || p->proto_state != PS_DOWN)
|
|
{
|
|
proto_shutdown_counter++;
|
|
proto_set_goal(p, FS_HUNGRY);
|
|
}
|
|
WALK_LIST_DELSAFE(p, n, proto_list)
|
|
{
|
|
proto_shutdown_counter++;
|
|
proto_set_goal(p, FS_HUNGRY);
|
|
}
|
|
}
|
|
|
|
void
|
|
protos_dump_all(void)
|
|
{
|
|
struct proto *p;
|
|
|
|
debug("Protocols:\n");
|
|
|
|
WALK_LIST(p, proto_list)
|
|
{
|
|
debug(" protocol %s (pri=%d): state %s/%s\n", p->name, p->proto->priority,
|
|
p_states[p->proto_state], c_states[p->core_state]);
|
|
if (p->in_filter)
|
|
debug("\tInput filter: %s\n", filter_name(p->in_filter));
|
|
if (p->out_filter != FILTER_REJECT)
|
|
debug("\tOutput filter: %s\n", filter_name(p->out_filter));
|
|
if (p->disabled)
|
|
debug("\tDISABLED\n");
|
|
else if (p->proto->dump)
|
|
p->proto->dump(p);
|
|
}
|
|
WALK_LIST(p, inactive_proto_list)
|
|
debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]);
|
|
WALK_LIST(p, initial_proto_list)
|
|
debug(" initial %s\n", p->name);
|
|
}
|
|
|
|
void
|
|
protos_build(void)
|
|
{
|
|
init_list(&protocol_list);
|
|
add_tail(&protocol_list, &proto_device.n);
|
|
#ifdef CONFIG_RIP
|
|
add_tail(&protocol_list, &proto_rip.n);
|
|
#endif
|
|
#ifdef CONFIG_STATIC
|
|
add_tail(&protocol_list, &proto_static.n);
|
|
#endif
|
|
#ifdef CONFIG_OSPF
|
|
add_tail(&protocol_list, &proto_ospf.n);
|
|
#endif
|
|
proto_pool = rp_new(&root_pool, "Protocols");
|
|
proto_flush_event = ev_new(proto_pool);
|
|
proto_flush_event->hook = proto_flush_all;
|
|
}
|
|
|
|
static void
|
|
proto_fell_down(struct proto *p)
|
|
{
|
|
DBG("Protocol %s down\n", p->name);
|
|
if (!--proto_shutdown_counter)
|
|
protos_shutdown_notify();
|
|
proto_rethink_goal(p);
|
|
}
|
|
|
|
static void
|
|
proto_feed(void *P)
|
|
{
|
|
struct proto *p = P;
|
|
|
|
DBG("Feeding protocol %s\n", p->name);
|
|
if_feed_baby(p);
|
|
rt_feed_baby(p);
|
|
p->core_state = FS_HAPPY;
|
|
proto_relink(p);
|
|
DBG("Protocol %s up and running\n", p->name);
|
|
}
|
|
|
|
void
|
|
proto_notify_state(struct proto *p, unsigned ps)
|
|
{
|
|
unsigned ops = p->proto_state;
|
|
unsigned cs = p->core_state;
|
|
|
|
DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]);
|
|
if (ops == ps)
|
|
return;
|
|
|
|
switch (ps)
|
|
{
|
|
case PS_DOWN:
|
|
if (cs == FS_HUNGRY) /* Shutdown finished */
|
|
proto_fell_down(p);
|
|
else if (cs == FS_FLUSHING) /* Still flushing... */
|
|
;
|
|
else /* Need to start flushing */
|
|
goto schedule_flush;
|
|
break;
|
|
case PS_START:
|
|
ASSERT(ops == PS_DOWN);
|
|
ASSERT(cs == FS_HUNGRY);
|
|
break;
|
|
case PS_UP:
|
|
ASSERT(ops == PS_DOWN || ops == PS_START);
|
|
ASSERT(cs == FS_HUNGRY);
|
|
DBG("%s: Scheduling meal\n", p->name);
|
|
if (p->proto->priority) /* FIXME: Terrible hack to get synchronous device/kernel startup! */
|
|
{
|
|
p->proto_state = ps;
|
|
p->core_state = FS_FEEDING;
|
|
proto_feed(p);
|
|
return;
|
|
}
|
|
cs = FS_FEEDING;
|
|
p->attn->hook = proto_feed;
|
|
ev_schedule(p->attn);
|
|
break;
|
|
case PS_STOP:
|
|
if (cs == FS_FEEDING || cs == FS_HAPPY)
|
|
{
|
|
schedule_flush:
|
|
DBG("%s: Scheduling flush\n", p->name);
|
|
cs = FS_FLUSHING;
|
|
ev_schedule(proto_flush_event);
|
|
}
|
|
break;
|
|
default:
|
|
error:
|
|
bug("Invalid state transition for %s from %s/%s to */%s", p->name, c_states[cs], p_states[ops], p_states[ps]);
|
|
}
|
|
p->proto_state = ps;
|
|
p->core_state = cs;
|
|
proto_relink(p);
|
|
}
|
|
|
|
static void
|
|
proto_flush_all(void *unused)
|
|
{
|
|
struct proto *p;
|
|
|
|
rt_prune(&master_table);
|
|
neigh_prune();
|
|
while ((p = HEAD(flush_proto_list))->n.next)
|
|
{
|
|
DBG("Flushing protocol %s\n", p->name);
|
|
rfree(p->pool);
|
|
p->pool = NULL;
|
|
p->core_state = FS_HUNGRY;
|
|
proto_relink(p);
|
|
proto_fell_down(p);
|
|
}
|
|
}
|