diff options
Diffstat (limited to 'nest')
-rw-r--r-- | nest/a-path.c | 11 | ||||
-rw-r--r-- | nest/attrs.h | 3 | ||||
-rw-r--r-- | nest/config.Y | 18 | ||||
-rw-r--r-- | nest/iface.c | 42 | ||||
-rw-r--r-- | nest/iface.h | 1 | ||||
-rw-r--r-- | nest/proto-hooks.c | 15 | ||||
-rw-r--r-- | nest/proto.c | 51 | ||||
-rw-r--r-- | nest/protocol.h | 29 | ||||
-rw-r--r-- | nest/route.h | 9 | ||||
-rw-r--r-- | nest/rt-dev.c | 4 | ||||
-rw-r--r-- | nest/rt-table.c | 206 |
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) { |