summaryrefslogtreecommitdiffstats
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/a-path.c11
-rw-r--r--nest/attrs.h3
-rw-r--r--nest/config.Y18
-rw-r--r--nest/iface.c42
-rw-r--r--nest/iface.h1
-rw-r--r--nest/proto-hooks.c15
-rw-r--r--nest/proto.c51
-rw-r--r--nest/protocol.h29
-rw-r--r--nest/route.h9
-rw-r--r--nest/rt-dev.c4
-rw-r--r--nest/rt-table.c206
11 files changed, 299 insertions, 90 deletions
diff --git a/nest/a-path.c b/nest/a-path.c
index f549987..dba214d 100644
--- a/nest/a-path.c
+++ b/nest/a-path.c
@@ -401,6 +401,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
struct pm_pos pos[2048 + 1];
int plen = parse_path(path, pos);
int l, h, i, nh, nl;
+ u32 val;
/* l and h are bound of interval of positions where
are marked states */
@@ -424,14 +425,20 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
h = plen;
break;
- case PM_QUESTION:
case PM_ASN:
+ val = mask->val;
+ goto step;
+ case PM_ASN_EXPR:
+ val = f_eval_asn((struct f_inst *) mask->val);
+ goto step;
+ case PM_QUESTION:
+ step:
nh = -1;
for (i = h; i >= l; i--)
if (pos[i].mark)
{
pos[i].mark = 0;
- if ((mask->kind == PM_QUESTION) || pm_match(pos + i, mask->val))
+ if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val))
pm_mark(pos, i, plen, &nl, &nh);
}
diff --git a/nest/attrs.h b/nest/attrs.h
index 5542be6..b838ce9 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -35,11 +35,12 @@ int as_path_is_member(struct adata *path, u32 as);
#define PM_ASN 0
#define PM_QUESTION 1
#define PM_ASTERISK 2
+#define PM_ASN_EXPR 3
struct f_path_mask {
struct f_path_mask *next;
int kind;
- u32 val;
+ uintptr_t val;
};
int as_path_match(struct adata *path, struct f_path_mask *mask);
diff --git a/nest/config.Y b/nest/config.Y
index 3c6eb7b..dc31224 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -44,6 +44,7 @@ CF_KEYWORDS(ROUTER, ID, PROTOCOL, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE)
+CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@@ -82,6 +83,23 @@ idval:
}
;
+
+CF_ADDTO(conf, listen)
+
+listen: LISTEN BGP listen_opts ';' ;
+
+listen_opts:
+ /* Nothing */
+ | listen_opts listen_opt
+ ;
+
+listen_opt:
+ ADDRESS ipa { new_config->listen_bgp_addr = $2; }
+ | PORT expr { new_config->listen_bgp_port = $2; }
+ | V6ONLY { new_config->listen_bgp_flags |= SKF_V6ONLY; }
+ ;
+
+
/* Creation of routing tables */
CF_ADDTO(conf, newtab)
diff --git a/nest/iface.c b/nest/iface.c
index 01f2581..5e88b21 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -399,29 +399,43 @@ if_find_by_name(char *name)
return NULL;
}
+struct ifa *kif_choose_primary(struct iface *i);
+
static int
ifa_recalc_primary(struct iface *i)
{
- struct ifa *a, *b = NULL;
- int res;
+ struct ifa *a = kif_choose_primary(i);
- WALK_LIST(a, i->addrs)
+ if (a == i->addr)
+ return 0;
+
+ if (i->addr)
+ i->addr->flags &= ~IA_PRIMARY;
+
+ if (a)
{
- if (!(a->flags & IA_SECONDARY) && (!b || a->scope > b->scope))
- b = a;
- a->flags &= ~IA_PRIMARY;
+ a->flags |= IA_PRIMARY;
+ rem_node(&a->n);
+ add_head(&i->addrs, &a->n);
}
- res = (b != i->addr);
- i->addr = b;
- if (b)
+
+ i->addr = a;
+ return 1;
+}
+
+void
+ifa_recalc_all_primary_addresses(void)
+{
+ struct iface *i;
+
+ WALK_LIST(i, iface_list)
{
- b->flags |= IA_PRIMARY;
- rem_node(&b->n);
- add_head(&i->addrs, &b->n);
+ if (ifa_recalc_primary(i))
+ if_change_flags(i, i->flags | IF_TMP_DOWN);
}
- return res;
}
+
/**
* ifa_update - update interface address
* @a: new interface address
@@ -464,7 +478,7 @@ ifa_update(struct ifa *a)
memcpy(b, a, sizeof(struct ifa));
add_tail(&i->addrs, &b->n);
b->flags = (i->flags & ~IA_FLAGS) | (a->flags & IA_FLAGS);
- if ((!i->addr || i->addr->scope < b->scope) && ifa_recalc_primary(i))
+ if (ifa_recalc_primary(i))
if_change_flags(i, i->flags | IF_TMP_DOWN);
if (b->flags & IF_UP)
ifa_notify_change(IF_CHANGE_CREATE | IF_CHANGE_UP, b);
diff --git a/nest/iface.h b/nest/iface.h
index f884dd9..af98a76 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -80,6 +80,7 @@ void if_end_partial_update(struct iface *);
void if_feed_baby(struct proto *);
struct iface *if_find_by_index(unsigned);
struct iface *if_find_by_name(char *);
+void ifa_recalc_all_primary_addresses(void);
/* The Neighbor Cache */
diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c
index 82df5cb..c30b107 100644
--- a/nest/proto-hooks.c
+++ b/nest/proto-hooks.c
@@ -179,8 +179,8 @@ void ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
* rt_notify - notify instance about routing table change
* @p: protocol instance
* @net: a network entry
- * @new: new optimal route for the network
- * @old: old optimal route for the network
+ * @new: new route for the network
+ * @old: old route for the network
* @attrs: extended attributes associated with the @new entry
*
* The rt_notify() hook is called to inform the protocol instance about
@@ -188,6 +188,17 @@ void ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
* belonging to network @net being replaced by a new route @new with
* extended attributes @attrs. Either @new or @old or both can be %NULL
* if the corresponding route doesn't exist.
+ *
+ * If the type of route announcement is RA_OPTIMAL, it is an
+ * announcement of optimal route change, @new stores the new optimal
+ * route and @old stores the old optimal route.
+ *
+ * If the type of route announcement is RA_ANY, it is an announcement
+ * of any route change, @new stores the new route and @old stores the
+ * old route from the same protocol.
+ *
+ * @p->accept_ra_types specifies which kind of route announcements
+ * protocol wants to receive.
*/
void rt_notify(struct proto *p, net *net, rte *new, rte *old, ea_list *attrs)
{ DUMMY; }
diff --git a/nest/proto.c b/nest/proto.c
index 0ad7229..2af077b 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -269,6 +269,7 @@ proto_init(struct proto_config *c)
* @old: old configuration or %NULL if it's boot time config
* @force_reconfig: force restart of all protocols (used for example
* when the router ID changes)
+ * @type: type of reconfiguration (RECONFIG_SOFT or RECONFIG_HARD)
*
* Scan differences between @old and @new configuration and adjust all
* protocol instances to conform to the new configuration.
@@ -281,15 +282,17 @@ proto_init(struct proto_config *c)
* When a protocol exists in the old configuration, but it doesn't in the
* new one, it's shut down and deleted after the shutdown completes.
*
- * When a protocol exists in both configurations, the core decides whether
- * it's possible to reconfigure it dynamically (it checks all the core properties
- * of the protocol and if they match, it asks the reconfigure() hook of the
- * protocol to see if the protocol is able to switch to the new configuration).
- * If it isn't possible, the protocol is shut down and a new instance is started
- * with the new configuration after the shutdown is completed.
+ * When a protocol exists in both configurations, the core decides
+ * whether it's possible to reconfigure it dynamically - it checks all
+ * the core properties of the protocol (changes in filters are ignored
+ * if type is RECONFIG_SOFT) and if they match, it asks the
+ * reconfigure() hook of the protocol to see if the protocol is able
+ * to switch to the new configuration. If it isn't possible, the
+ * protocol is shut down and a new instance is started with the new
+ * configuration after the shutdown is completed.
*/
void
-protos_commit(struct config *new, struct config *old, int force_reconfig)
+protos_commit(struct config *new, struct config *old, int force_reconfig, int type)
{
struct proto_config *oc, *nc;
struct proto *p, *n;
@@ -310,8 +313,8 @@ protos_commit(struct config *new, struct config *old, int force_reconfig)
&& nc->preference == oc->preference
&& nc->disabled == oc->disabled
&& nc->table->table == oc->table->table
- && filter_same(nc->in_filter, oc->in_filter)
- && filter_same(nc->out_filter, oc->out_filter)
+ && ((type == RECONFIG_SOFT) || filter_same(nc->in_filter, oc->in_filter))
+ && ((type == RECONFIG_SOFT) || filter_same(nc->out_filter, oc->out_filter))
&& p->proto_state != PS_DOWN)
{
/* Generic attributes match, try converting them and then ask the protocol */
@@ -512,6 +515,9 @@ static void
proto_fell_down(struct proto *p)
{
DBG("Protocol %s down\n", p->name);
+ ASSERT(p->stats.imp_routes == 0);
+
+ bzero(&p->stats, sizeof(struct proto_stats));
rt_unlock_table(p->table);
proto_rethink_goal(p);
}
@@ -693,9 +699,30 @@ proto_do_show(struct proto *p, int verbose)
buf);
if (verbose)
{
- cli_msg(-1006, "\tPreference: %d", p->preference);
- cli_msg(-1006, "\tInput filter: %s", filter_name(p->in_filter));
- cli_msg(-1006, "\tOutput filter: %s", filter_name(p->out_filter));
+ cli_msg(-1006, " Preference: %d", p->preference);
+ cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter));
+ cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter));
+
+ if (p->proto_state != PS_DOWN)
+ {
+ cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
+ p->stats.imp_routes, p->stats.exp_routes, p->stats.pref_routes);
+ cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
+ cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
+ p->stats.imp_updates_received, p->stats.imp_updates_invalid,
+ p->stats.imp_updates_filtered, p->stats.imp_updates_ignored,
+ p->stats.imp_updates_accepted);
+ cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u",
+ p->stats.imp_withdraws_received, p->stats.imp_withdraws_invalid,
+ p->stats.imp_withdraws_ignored, p->stats.imp_withdraws_accepted);
+ cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u",
+ p->stats.exp_updates_received, p->stats.exp_updates_rejected,
+ p->stats.exp_updates_filtered, p->stats.exp_updates_accepted);
+ cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u",
+ p->stats.exp_withdraws_received, p->stats.exp_withdraws_accepted);
+ }
+
+ cli_msg(-1006, "");
}
}
diff --git a/nest/protocol.h b/nest/protocol.h
index d681ae6..0f9d59d 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -55,7 +55,7 @@ void protos_build(void);
void proto_build(struct protocol *);
void protos_preconfig(struct config *);
void protos_postconfig(struct config *);
-void protos_commit(struct config *new, struct config *old, int force_restart);
+void protos_commit(struct config *new, struct config *old, int force_restart, int type);
void protos_dump_all(void);
#define GA_UNKNOWN 0 /* Attribute not recognized */
@@ -87,6 +87,31 @@ struct proto_config {
/* Protocol-specific data follow... */
};
+ /* Protocol statistics */
+struct proto_stats {
+ /* Import - from protocol to core */
+ u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
+ u32 pref_routes; /* Number of routes that are preferred, sum over all routing table */
+ u32 imp_updates_received; /* Number of route updates received */
+ u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
+ u32 imp_updates_filtered; /* Number of route updates rejected by filters */
+ u32 imp_updates_ignored; /* Number of route updates rejected as already in route table */
+ u32 imp_updates_accepted; /* Number of route updates accepted and imported */
+ u32 imp_withdraws_received; /* Number of route withdraws received */
+ u32 imp_withdraws_invalid; /* Number of route withdraws rejected as invalid */
+ u32 imp_withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
+ u32 imp_withdraws_accepted; /* Number of route withdraws accepted and processed */
+
+ /* Export - from core to protocol */
+ u32 exp_routes; /* Number of routes successfully exported to the protocol */
+ u32 exp_updates_received; /* Number of route updates received */
+ u32 exp_updates_rejected; /* Number of route updates rejected by protocol */
+ u32 exp_updates_filtered; /* Number of route updates rejected by filters */
+ u32 exp_updates_accepted; /* Number of route updates accepted and exported */
+ u32 exp_withdraws_received; /* Number of route withdraws received */
+ u32 exp_withdraws_accepted; /* Number of route withdraws accepted and processed */
+};
+
struct proto {
node n; /* Node in *_proto_list */
node glob_node; /* Node in global proto_list */
@@ -100,6 +125,7 @@ struct proto {
unsigned debug; /* Debugging flags */
unsigned preference; /* Default route preference */
int min_scope; /* Minimal route scope accepted */
+ unsigned accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
unsigned disabled; /* Manually disabled */
unsigned proto_state; /* Protocol state machine (see below) */
unsigned core_state; /* Core state machine (see below) */
@@ -108,6 +134,7 @@ struct proto {
u32 hash_key; /* Random key used for hashing of neighbors */
bird_clock_t last_state_change; /* Time of last state transition */
char *last_state_name_announced; /* Last state name we've announced to the user */
+ struct proto_stats stats; /* Current protocol statistics */
/*
* General protocol hooks:
diff --git a/nest/route.h b/nest/route.h
index 43cfa9d..1bd23a6 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -146,6 +146,7 @@ typedef struct network {
typedef struct rte {
struct rte *next;
net *net; /* Network this RTE belongs to */
+ struct proto *sender; /* Protocol instance that sent the route to the routing table */
struct rta *attrs; /* Attributes of this route */
byte flags; /* Flags (REF_...) */
byte pflags; /* Protocol-specific flags */
@@ -178,6 +179,10 @@ typedef struct rte {
#define REF_COW 1 /* Copy this rte on write */
+/* Types of route announcement, also used as flags */
+#define RA_OPTIMAL 1 /* Announcement of optimal route change */
+#define RA_ANY 2 /* Announcement of any route change */
+
struct config;
void rt_init(void);
@@ -190,7 +195,7 @@ static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (n
static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); }
rte *rte_find(net *net, struct proto *p);
rte *rte_get_temp(struct rta *);
-void rte_update(rtable *tab, net *net, struct proto *p, rte *new);
+void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new);
void rte_discard(rtable *tab, rte *old);
void rte_dump(rte *);
void rte_free(rte *);
@@ -230,7 +235,7 @@ void rt_show(struct rt_show_data *);
typedef struct rta {
struct rta *next, **pprev; /* Hash chain */
- struct proto *proto; /* Protocol instance */
+ struct proto *proto; /* Protocol instance that originally created the route */
unsigned uc; /* Use count */
byte source; /* Route source (RTS_...) */
byte scope; /* Route scope (SCOPE_... -- see ip.h) */
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index 348bcc2..b86015d 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -44,7 +44,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
DBG("dev_if_notify: device shutdown: prefix not found\n");
return;
}
- rte_update(p->table, n, p, NULL);
+ rte_update(p->table, n, p, p, NULL);
}
else if (c & IF_CHANGE_UP)
{
@@ -66,7 +66,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
e = rte_get_temp(a);
e->net = n;
e->pflags = 0;
- rte_update(p->table, n, p, e);
+ rte_update(p->table, n, p, p, e);
}
}
diff --git a/nest/rt-table.c b/nest/rt-table.c
index b0781a3..fb2feac 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -158,7 +158,7 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
}
static inline void
-do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *tmpa, int class)
+do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class)
{
struct proto *p = a->proto;
rte *new0 = new;
@@ -167,16 +167,27 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *
if (new)
{
+ p->stats.exp_updates_received++;
+
char *drop_reason = NULL;
if ((class & IADDR_SCOPE_MASK) < p->min_scope)
- drop_reason = "out of scope";
+ {
+ p->stats.exp_updates_rejected++;
+ drop_reason = "out of scope";
+ }
else if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0)
- drop_reason = "rejected by protocol";
+ {
+ p->stats.exp_updates_rejected++;
+ drop_reason = "rejected by protocol";
+ }
else if (ok)
rte_trace_out(D_FILTERS, p, new, "forced accept by protocol");
else if (p->out_filter == FILTER_REJECT ||
p->out_filter && f_run(p->out_filter, &new, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)
- drop_reason = "filtered out";
+ {
+ p->stats.exp_updates_filtered++;
+ drop_reason = "filtered out";
+ }
if (drop_reason)
{
rte_trace_out(D_FILTERS, p, new, drop_reason);
@@ -185,7 +196,10 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *
new = NULL;
}
}
- if (old && p->out_filter)
+ else
+ p->stats.exp_withdraws_received++;
+
+ if (old)
{
if (p->out_filter == FILTER_REJECT)
old = NULL;
@@ -193,7 +207,7 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *
{
ea_list *tmpb = p->make_tmp_attrs ? p->make_tmp_attrs(old, rte_update_pool) : NULL;
ok = p->import_control ? p->import_control(p, &old, &tmpb, rte_update_pool) : 0;
- if (ok < 0 || (!ok && f_run(p->out_filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
+ if (ok < 0 || (!ok && p->out_filter && f_run(p->out_filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
{
if (old != old0)
rte_free(old);
@@ -201,6 +215,20 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *
}
}
}
+
+ if (!new && !old)
+ return;
+
+ if (new)
+ p->stats.exp_updates_accepted++;
+ else
+ p->stats.exp_withdraws_accepted++;
+
+ if (new)
+ p->stats.exp_routes++;
+ if (old)
+ p->stats.exp_routes--;
+
if (p->debug & D_ROUTES)
{
if (new && old)
@@ -210,8 +238,6 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *
else if (old)
rte_trace_out(D_ROUTES, p, old, "removed");
}
- if (!new && !old)
- return;
if (!new)
p->rt_notify(p, net, NULL, old, NULL);
else if (tmpa)
@@ -234,31 +260,51 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list *
/**
* rte_announce - announce a routing table change
* @tab: table the route has been added to
+ * @type: type of route announcement (RA_OPTIMAL or RA_ANY)
* @net: network in question
* @new: the new route to be announced
- * @old: previous optimal route for the same network
+ * @old: the previous route for the same network
* @tmpa: a list of temporary attributes belonging to the new route
*
* This function gets a routing table update and announces it
- * to all protocols connected to the same table by their announcement hooks.
+ * to all protocols that acccepts given type of route announcement
+ * and are connected to the same table by their announcement hooks.
*
- * For each such protocol, we first call its import_control() hook which
- * performs basic checks on the route (each protocol has a right to veto
- * or force accept of the route before any filter is asked) and adds default
- * values of attributes specific to the new protocol (metrics, tags etc.).
- * Then it consults the protocol's export filter and if it accepts the
- * route, the rt_notify() hook of the protocol gets called.
+ * Route announcement of type RA_OPTIMAL si generated when optimal
+ * route (in routing table @tab) changes. In that case @old stores the
+ * old optimal route.
+ *
+ * Route announcement of type RA_ANY si generated when any route (in
+ * routing table @tab) changes In that case @old stores the old route
+ * from the same protocol.
+ *
+ * For each appropriate protocol, we first call its import_control()
+ * hook which performs basic checks on the route (each protocol has a
+ * right to veto or force accept of the route before any filter is
+ * asked) and adds default values of attributes specific to the new
+ * protocol (metrics, tags etc.). Then it consults the protocol's
+ * export filter and if it accepts the route, the rt_notify() hook of
+ * the protocol gets called.
*/
static void
-rte_announce(rtable *tab, net *net, rte *new, rte *old, ea_list *tmpa)
+rte_announce(rtable *tab, int type, net *net, rte *new, rte *old, ea_list *tmpa)
{
struct announce_hook *a;
int class = ipa_classify(net->n.prefix);
+ if (type == RA_OPTIMAL)
+ {
+ if (new)
+ new->attrs->proto->stats.pref_routes++;
+ if (old)
+ old->attrs->proto->stats.pref_routes--;
+ }
+
WALK_LIST(a, tab->hooks)
{
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
- do_rte_announce(a, net, new, old, tmpa, class);
+ if (a->proto->accept_ra_types == type)
+ do_rte_announce(a, type, net, new, old, tmpa, class);
}
}
@@ -271,7 +317,7 @@ rte_validate(rte *e)
if (ipa_nonzero(ipa_and(n->n.prefix, ipa_not(ipa_mkmask(n->n.pxlen)))))
{
log(L_BUG "Ignoring bogus prefix %I/%d received via %s",
- n->n.prefix, n->n.pxlen, e->attrs->proto->name);
+ n->n.prefix, n->n.pxlen, e->sender->name);
return 0;
}
if (n->n.pxlen)
@@ -290,14 +336,14 @@ rte_validate(rte *e)
return 1;
}
log(L_WARN "Ignoring bogus route %I/%d received via %s",
- n->n.prefix, n->n.pxlen, e->attrs->proto->name);
+ n->n.prefix, n->n.pxlen, e->sender->name);
return 0;
}
- if ((c & IADDR_SCOPE_MASK) < e->attrs->proto->min_scope)
+ if ((c & IADDR_SCOPE_MASK) < e->sender->min_scope)
{
log(L_WARN "Ignoring %s scope route %I/%d received from %I via %s",
ip_scope_text(c & IADDR_SCOPE_MASK),
- n->n.prefix, n->n.pxlen, e->attrs->from, e->attrs->proto->name);
+ n->n.prefix, n->n.pxlen, e->attrs->from, e->sender->name);
return 0;
}
}
@@ -337,7 +383,7 @@ rte_same(rte *x, rte *y)
}
static void
-rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmpa)
+rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa)
{
rte *old_best = net->routes;
rte *old = NULL;
@@ -346,11 +392,12 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp
k = &net->routes; /* Find and remove original route from the same protocol */
while (old = *k)
{
- if (old->attrs->proto == p)
+ if (old->attrs->proto == src)
{
if (new && rte_same(old, new))
{
/* No changes, ignore the new route */
+ p->stats.imp_updates_ignored++;
rte_trace_in(D_ROUTES, p, new, "ignored");
rte_free_quick(new);
old->lastmod = now;
@@ -362,10 +409,28 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp
k = &old->next;
}
+ if (!old && !new)
+ {
+ p->stats.imp_withdraws_ignored++;
+ return;
+ }
+
+ if (new)
+ p->stats.imp_updates_accepted++;
+ else
+ p->stats.imp_withdraws_accepted++;
+
+ if (new)
+ p->stats.imp_routes++;
+ if (old)
+ p->stats.imp_routes--;
+
+ rte_announce(table, RA_ANY, net, new, old, tmpa);
+
if (new && rte_better(new, old_best)) /* It's a new optimal route => announce and relink it */
{
rte_trace_in(D_ROUTES, p, new, "added [best]");
- rte_announce(table, net, new, old_best, tmpa);
+ rte_announce(table, RA_OPTIMAL, net, new, old_best, tmpa);
new->next = net->routes;
net->routes = new;
}
@@ -377,7 +442,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp
for(s=net->routes; s; s=s->next)
if (rte_better(s, r))
r = s;
- rte_announce(table, net, r, old_best, tmpa);
+ rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa);
if (r) /* Re-link the new optimal route */
{
k = &net->routes;
@@ -447,6 +512,7 @@ rte_update_unlock(void)
* @table: table to be updated
* @net: network node
* @p: protocol submitting the update
+ * @src: protocol originating the update
* @new: a &rte representing the new route or %NULL for route removal.
*
* This function is called by the routing protocols whenever they discover
@@ -457,6 +523,12 @@ rte_update_unlock(void)
* rta_clone()), call rte_get_temp() to obtain a temporary &rte, fill in all
* the appropriate data and finally submit the new &rte by calling rte_update().
*
+ * @src specifies the protocol that originally created the route and the meaning
+ * of protocol-dependent data of @new. If @new is not %NULL, @src have to be the
+ * same value as @new->attrs->proto. @p specifies the protocol that called
+ * rte_update(). In most cases it is the same protocol as @src. rte_update()
+ * stores @p in @new->sender;
+ *
* When rte_update() gets any route, it automatically validates it (checks,
* whether the network and next hop address are valid IP addresses and also
* whether a normal routing protocol doesn't try to smuggle a host or link
@@ -466,7 +538,7 @@ rte_update_unlock(void)
* stores the temporary attributes back to the &rte.
*
* Now, having a "public" version of the route, we
- * automatically find any old route defined by the protocol @p
+ * automatically find any old route defined by the protocol @src
* for network @n, replace it by the new one (or removing it if @new is %NULL),
* recalculate the optimal route for this destination and finally broadcast
* the change (if any) to all routing protocols by calling rte_announce().
@@ -475,14 +547,16 @@ rte_update_unlock(void)
* from a special linear pool @rte_update_pool and freed when rte_update()
* finishes.
*/
+
void
-rte_update(rtable *table, net *net, struct proto *p, rte *new)
+rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new)
{
ea_list *tmpa = NULL;
rte_update_lock();
if (new)
{
+ new->sender = p;
struct filter *filter = p->in_filter;
/* Do not filter routes going to the secondary side of the pipe,
@@ -491,35 +565,42 @@ rte_update(rtable *table, net *net, struct proto *p, rte *new)
if (p->table != table)
filter = FILTER_ACCEPT;
+ p->stats.imp_updates_received++;
if (!rte_validate(new))
{
rte_trace_in(D_FILTERS, p, new, "invalid");
+ p->stats.imp_updates_invalid++;
goto drop;
}
if (filter == FILTER_REJECT)
{
+ p->stats.imp_updates_filtered++;
rte_trace_in(D_FILTERS, p, new, "filtered out");
goto drop;
}
- if (p->make_tmp_attrs)
- tmpa = p->make_tmp_attrs(new, rte_update_pool);
+ if (src->make_tmp_attrs)
+ tmpa = src->make_tmp_attrs(new, rte_update_pool);
if (filter)
{
ea_list *old_tmpa = tmpa;
int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
if (fr > F_ACCEPT)
{
+ p->stats.imp_updates_filtered++;
rte_trace_in(D_FILTERS, p, new, "filtered out");
goto drop;
}
- if (tmpa != old_tmpa && p->store_tmp_attrs)
- p->store_tmp_attrs(new, tmpa);
+ if (tmpa != old_tmpa && src->store_tmp_attrs)
+ src->store_tmp_attrs(new, tmpa);
}
if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
new->flags |= REF_COW;
}
- rte_recalculate(table, net, p, new, tmpa);
+ else
+ p->stats.imp_withdraws_received++;
+
+ rte_recalculate(table, net, p, src, new, tmpa);
rte_update_unlock();
return;
@@ -531,11 +612,8 @@ drop:
void
rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */
{
- net *n = old->net;
- struct proto *p = old->attrs->proto;
-
rte_update_lock();
- rte_recalculate(t, n, p, NULL, NULL);
+ rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL);
rte_update_unlock();
}
@@ -673,8 +751,8 @@ again:
ncnt++;
rescan:
for (e=n->routes; e; e=e->next, rcnt++)
- if (e->attrs->proto->core_state != FS_HAPPY &&
- e->attrs->proto->core_state != FS_FEEDING)
+ if (e->sender->core_state != FS_HAPPY &&
+ e->sender->core_state != FS_FEEDING)
{
rte_discard(tab, e);
rdel++;
@@ -827,6 +905,18 @@ rt_commit(struct config *new, struct config *old)
DBG("\tdone\n");
}
+static inline void
+do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e)
+{
+ struct proto *q = e->attrs->proto;
+ ea_list *tmpa;
+
+ rte_update_lock();
+ tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
+ do_rte_announce(h, type, n, e, NULL, tmpa, ipa_classify(n->n.prefix));
+ rte_update_unlock();
+}
+
/**
* rt_feed_baby - advertise routes to a new protocol
* @p: protocol to be fed
@@ -865,19 +955,24 @@ again:
FIB_ITERATE_PUT(fit, fn);
return 0;
}
- if (e)
- {
- struct proto *q = e->attrs->proto;
- ea_list *tmpa;
-
- if (p->core_state != FS_FEEDING)
- return 1; /* In the meantime, the protocol fell down. */
- rte_update_lock();
- tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL;
- do_rte_announce(h, n, e, NULL, tmpa, ipa_classify(n->n.prefix));
- rte_update_unlock();
- max_feed--;
- }
+
+ if (p->accept_ra_types == RA_OPTIMAL)
+ if (e)
+ {
+ if (p->core_state != FS_FEEDING)
+ return 1; /* In the meantime, the protocol fell down. */
+ do_feed_baby(p, RA_OPTIMAL, h, n, e);
+ max_feed--;
+ }
+
+ if (p->accept_ra_types == RA_ANY)
+ for(e = n->routes; e != NULL; e = e->next)
+ {
+ if (p->core_state != FS_FEEDING)
+ return 1; /* In the meantime, the protocol fell down. */
+ do_feed_baby(p, RA_ANY, h, n, e);
+ max_feed--;
+ }
}
FIB_ITERATE_END(fn);
p->feed_ahook = h->next;
@@ -988,8 +1083,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
if (p2 && p2 != p0) ok = 0;
if (ok && d->export_mode)
{
- int ic = (p1->import_control ? p1->import_control(p1, &e, &tmpa, rte_update_pool) : 0);
- if (ic < 0)
+ int class = ipa_classify(n->n.prefix);
+ int ic;
+ if ((class & IADDR_SCOPE_MASK) < p1->min_scope)
+ ok = 0;
+ else if ((ic = p1->import_control ? p1->import_control(p1, &e, &tmpa, rte_update_pool) : 0) < 0)
ok = 0;
else if (!ic && d->export_mode > 1)
{