diff options
42 files changed, 1473 insertions, 202 deletions
@@ -1,4 +1,11 @@ +Version 1.3.7 (2012-03-22) + o Route Origin Authorization basics. + o RIPng working again. + o Extended clist operations in filters. + o Fixes several bugs in BSD iface handling. + o Several minor bugfixes and enhancements. + Version 1.3.6 (2012-01-20) o Important bugfix in BGP. @@ -2,7 +2,6 @@ Core ~~~~ - socket open failure should not be fatal - &&,||: priorities -- filters: function for BGP path length - static: allow specifying a per-route filter program for setting route attributes? Globals @@ -23,14 +22,10 @@ Various ideas ~~~~~~~~~~~~~ - client: Ctrl-R eats one more enter - bgp: timing of updates? -- netlink: realms - netlink: import Linux route attributes to our rta's, so that they can be filtered? - config: executable config files - filters: user defined attributes? -- client: access control - io: use poll if available -- real multipath (doesn't seem to be simple at all :() -- fake multipath (even less simple) - route recalculation timing and flap dampening [see RFC2439 for algorithms] - aggregate engine: standard route aggregation and summarization [RFC2519] - aggregate engine: injection of manually configured pseudo-static routes @@ -44,7 +39,6 @@ OSPF - check incoming packets using neighbor cache - RFC2328 appendix E: Use a better algorithm - automatic generation of external route tags (RFC1403) - - RFC3101 NSSA areas - RFC2370 opaque LSA's - Limit export rate of external LSAs (like Gated does) - Bugfix in link state retransmission list (aging) diff --git a/client/commands.c b/client/commands.c index 7612b64c..08e7949a 100644 --- a/client/commands.c +++ b/client/commands.c @@ -29,6 +29,7 @@ struct cmd_node { struct cmd_node *sibling, *son, **plastson; struct cmd_info *cmd, *help; int len; + signed char prio; char token[1]; }; @@ -66,6 +67,7 @@ cmd_build_tree(void) new->plastson = &new->son; new->len = c-d; memcpy(new->token, d, c-d); + new->prio = (new->len == 3 && !memcmp(new->token, "roa", 3)) ? 0 : 1; /* Hack */ } old = new; while (isspace(*c)) @@ -108,7 +110,10 @@ cmd_find_abbrev(struct cmd_node *root, char *cmd, int len, int *pambiguous) return m; if (m->len > len && !memcmp(m->token, cmd, len)) { - best2 = best; + if (best && best->prio > m->prio) + continue; + if (best && best->prio == m->prio) + best2 = best; best = m; } } @@ -168,19 +173,31 @@ static int cmd_find_common_match(struct cmd_node *root, char *cmd, int len, int *pcount, char *buf) { struct cmd_node *m; - int best, i; + int best, best_prio, i; *pcount = 0; best = -1; + best_prio = -1; for(m=root->son; m; m=m->sibling) { if (m->len < len || memcmp(m->token, cmd, len)) continue; + + if (best_prio > m->prio) + continue; + + if (best_prio < m->prio) + { + *pcount = 0; + best = -1; + } + (*pcount)++; if (best < 0) { strcpy(buf, m->token + len); best = m->len - len; + best_prio = m->prio; } else { diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 408fa93a..8cd52c42 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -536,6 +536,8 @@ cf_symbol_class_name(struct symbol *sym) return "network address"; case SYM_TEMPLATE: return "protocol template"; + case SYM_ROA: + return "ROA table"; default: return "unknown type"; } diff --git a/conf/conf.c b/conf/conf.c index 4b605b36..13049be4 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -112,6 +112,7 @@ config_parse(struct config *c) sysdep_preconfig(c); protos_preconfig(c); rt_preconfig(c); + roa_preconfig(c); cf_parse(); protos_postconfig(c); if (EMPTY_LIST(c->protos)) @@ -210,6 +211,7 @@ config_do_commit(struct config *c, int type) force_restart |= global_commit(c, old_config); DBG("rt_commit\n"); rt_commit(c, old_config); + roa_commit(c, old_config); DBG("protos_commit\n"); protos_commit(c, old_config, force_restart, type); new_config = NULL; /* Just to be sure nobody uses that now */ diff --git a/conf/conf.h b/conf/conf.h index 8753bafe..b4ec3157 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -21,7 +21,9 @@ struct config { linpool *mem; /* Linear pool containing configuration data */ list protos; /* Configured protocol instances (struct proto_config) */ list tables; /* Configured routing tables (struct rtable_config) */ + list roa_tables; /* Configured ROA tables (struct roa_table_config) */ list logfiles; /* Configured log fils (sysdep) */ + int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ struct rtable_config *master_rtc; /* Configuration of master routing table */ @@ -110,6 +112,7 @@ struct symbol { #define SYM_TABLE 5 #define SYM_IPA 6 #define SYM_TEMPLATE 7 +#define SYM_ROA 8 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ diff --git a/conf/confbase.Y b/conf/confbase.Y index 14893f47..dcb0719f 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -50,7 +50,11 @@ CF_DECLS struct f_path_mask *h; struct password_item *p; struct rt_show_data *ra; + struct roa_show_data *ro; + struct sym_show_data *sd; + struct lsadb_show_data *ld; struct iface *iface; + struct roa_table *rot; void *g; bird_clock_t time; struct prefix px; diff --git a/doc/bird.sgml b/doc/bird.sgml index b8dabf47..20738be3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -358,6 +358,22 @@ protocol rip { routing table is created implicitly, other routing tables have to be added by this command. + <tag>roa table [ { roa table options ... } ] <m/name/</tag> + Create a new ROA (Route Origin Authorization) table. ROA + tables can be used to validate route origination of BGP + routes. A ROA table contains ROA entries, each consist of a + network prefix, a max prefix length and an AS number. A ROA + entry specifies prefixes which could be originated by that AS + number. ROA tables could be filled with data from RPKI (RFC + 6480) or from public databases like Whois. ROA tables are + examined by <cf/roa_check()/ operator in filters. + + Currently, there is just one option, + <cf>roa <m/prefix/ max <m/num/ as <m/num/</cf>, which + can be used to populate the ROA table with static ROA + entries. The option may be used multiple times. Other entries + can be added dynamically by <cf/add roa/ command. + <tag>eval <m/expr/</tag> Evaluates given filter expression. It is used by us for testing of filters. </descrip> @@ -561,13 +577,16 @@ This argument can be omitted if there exists only a single instance. link-state database. It is just a stripped-down version of 'show ospf state'. + <tag>show ospf lsadb [global | area <m/id/ | link] [type <m/num/] [lsid <m/id/] [self | router <m/id/] [<m/name/] </tag> + Show contents of an OSPF LSA database. Options could be used to filter entries. + <tag>show static [<m/name/]</tag> Show detailed information about static routes. <tag>show interfaces [summary]</tag> Show the list of interfaces. For each interface, print its type, state, MTU and addresses assigned. - <tag>show symbols</tag> + <tag>show symbols [table|filter|function|protocol|template|roa|<m/symbol/]</tag> Show the list of symbols defined in the configuration (names of protocols, routing tables etc.). <tag>show route [[for] <m/prefix/|<m/IP/] [table <m/sym/] [filter <m/f/|where <m/c/] [(export|preexport) <m/p/] [protocol <m/p/] [<m/options/]</tag> @@ -596,6 +615,29 @@ This argument can be omitted if there exists only a single instance. number of networks, number of routes before and after filtering). If you use <cf/count/ instead, only the statistics will be printed. + <tag>show roa [<m/prefix/ | in <m/prefix/ | for <m/prefix/] [as <m/num/] [table <m/t/>]</tag> + Show contents of a ROA table (by default of the first one). + You can specify a <m/prefix/ to print ROA entries for a + specific network. If you use <cf>for <m/prefix/</cf>, you'll + get all entries relevant for route validation of the network + prefix; i.e., ROA entries whose prefixes cover the network + prefix. Or you can use <cf>in <m/prefix/</cf> to get ROA entries + covered by the network prefix. You could also use <cf/as/ option + to show just entries for given AS. + + <tag>add roa <m/prefix/ max <m/num/] as <m/num/ [table <m/t/>]</tag> + Add a new ROA entry to a ROA table. Such entry is called + <it/dynamic/ compared to <it/static/ entries specified in the + config file. These dynamic entries survive reconfiguration. + + <tag>delete roa <m/prefix/ max <m/num/] as <m/num/ [table <m/t/>]</tag> + Delete the specified ROA entry from a ROA table. Only dynamic + ROA entries (i.e., the ones added by <cf/add roa/ command) can + be deleted. + + <tag>flush roa [table <m/t/>]</tag> + Remove all dynamic ROA entries from a ROA table. + <tag>configure [soft] ["<m/config file/"]</tag> Reload configuration from a given file. BIRD will smoothly switch itself to the new configuration, protocols are @@ -875,19 +917,22 @@ incompatible with each other (that is to prevent you from shooting in the foot). <cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and returns the result. If item <m/P/ is already in - clist <m/C/, it does nothing. + clist <m/C/, it does nothing. <m/P/ may also be a clist, + in that case all its members are added; i.e., it works as clist union. <cf>delete(<m/C/,<m/P/)</cf> deletes pair (or quad) <m/P/ from clist <m/C/ and returns the result. If clist <m/C/ does not contain item <m/P/, it does nothing. <m/P/ may also be a pair (or quad) set, in that case the operator deletes all items from clist <m/C/ that are also - members of set <m/P/. + members of set <m/P/. Moreover, <m/P/ may also be a clist, + which works analogously; i.e., it works as clist difference. <cf>filter(<m/C/,<m/P/)</cf> deletes all items from clist <m/C/ that are not members of pair (or quad) set <m/P/. I.e., <cf/filter/ do the same as <cf/delete/ with inverted - set <m/P/. + set <m/P/. <m/P/ may also be a clist, which works analogously; + i.e., it works as clist intersection. Statement <cf><m/C/ = add(<m/C/, <m/P/);</cf> can be shortened to <cf><m/C/.add(<m/P/);</cf> if <m/C/ is appropriate route @@ -910,7 +955,18 @@ incompatible with each other (that is to prevent you from shooting in the foot). Special operators include <cf/˜/ for "is element of a set" operation - it can be used on element and set of elements of the same type (returning true if element is contained in the given set), or on two strings (returning true if first string matches a shell-like pattern stored in second string) or on IP and prefix (returning true if IP is within the range defined by that prefix), or on -prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on pair/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set). +prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on number and bgppath (returning true if the number is in the path) or on pair/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set). + +<p>There is one operator related to ROA infrastructure - +<cf/roa_check()/. It examines a ROA table and does RFC 6483 route +origin validation for a given network prefix. The basic usage +is <cf>roa_check(<m/table/)</cf>, which checks current route (which +should be from BGP to have AS_PATH argument) in the specified ROA +table and returns ROA_UNKNOWN if there is no relevant ROA, ROA_VALID +if there is a matching ROA, or ROA_INVALID if there are some relevant +ROAs but none of them match. There is also an extended variant +<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to +specify a prefix and an ASN as arguments. <sect>Control structures @@ -1562,7 +1618,7 @@ kernel table. <p>Because the kernel protocol is partially integrated with the connected routing table, there are two limitations - it is not possible to connect more kernel protocols to the same routing table -and changing route attributes (even the kernel ones) in an export +and changing route destination/gateway in an export filter of a kernel protocol does not work. Both limitations can be overcome using another routing table and the pipe protocol. @@ -1597,6 +1653,16 @@ are translated to appropriate system (and OS-specific) route attributes. We support these attributes: <descrip> + <tag>int <cf/krt_source/</tag> The original source of the imported + kernel route. The value is system-dependent. On Linux, it is + a value of the protocol field of the route. See + /etc/iproute2/rt_protos for common values. On BSD, it is + based on STATIC and PROTOx flags. The attribute is read-only. + + <tag>int <cf/krt_metric/</tag> The kernel metric of + the route. When multiple same routes are in a kernel routing + table, the Linux kernel chooses one with lower metric. + <tag>ip <cf/krt_prefsrc/</tag> (Linux) The preferred source address. Used in source address selection for outgoing packets. Have to be one of IP addresses of the router. @@ -2085,17 +2151,18 @@ and vice versa, depending on what's allowed by the filters. Export filters contr of routes from the primary table to the secondary one, import filters control the opposite direction. -<p>The Pipe protocol may work in the opaque mode or in the transparent -mode. In the opaque mode, the Pipe protocol retransmits optimal route +<p>The Pipe protocol may work in the transparent mode mode or in the opaque mode. +In the transparent mode, the Pipe protocol retransmits all routes from +one table to the other table, retaining their original source and +attributes. If import and export filters are set to accept, then both +tables would have the same content. The transparent mode is the default mode. + +<p>In the opaque mode, the Pipe protocol retransmits optimal route from one table to the other table in a similar way like other protocols send and receive routes. Retransmitted route will have the source set to the Pipe protocol, which may limit access to protocol -specific route attributes. The opaque mode is a default mode. - -<p>In transparent mode, the Pipe protocol retransmits all routes from -one table to the other table, retaining their original source and -attributes. If import and export filters are set to accept, then both -tables would have the same content. The mode can be set by +specific route attributes. This mode is mainly for compatibility, it +is not suggested for new configs. The mode can be changed by <tt/mode/ option. <p>The primary use of multiple routing tables and the Pipe protocol is for policy routing, diff --git a/doc/reply_codes b/doc/reply_codes index a0db2cad..7ec2e27d 100644 --- a/doc/reply_codes +++ b/doc/reply_codes @@ -45,6 +45,7 @@ Reply codes of BIRD command-line interface 1016 Show ospf state/topology 1017 Show ospf lsadb 1018 Show memory +1019 Show ROA list 8000 Reply too long 8001 Route not found diff --git a/filter/config.Y b/filter/config.Y index 0166d27b..2e8b522e 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -259,6 +259,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, DEFINED, ADD, DELETE, CONTAINS, RESET, PREPEND, FIRST, LAST, MATCH, + ROA_CHECK, EMPTY, FILTER, WHERE, EVAL) @@ -495,7 +496,7 @@ pair_item: /* Hack: $2 and $4 should be pair_expr, but that would cause shift/reduce conflict */ if ((pair_a($2) != pair_b($2)) || (pair_a($4) != pair_b($4))) cf_error("syntax error"); - $$ = f_new_pair_item(pair_b($2), pair_b($4), $8, $10); + $$ = f_new_pair_item(pair_b($2), $8, pair_b($4), $10); } ; @@ -755,6 +756,9 @@ term: | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; } + | ROA_CHECK '(' SYM ')' { $$ = f_generate_roa_check($3, NULL, NULL); } + | ROA_CHECK '(' SYM ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); } + /* | term '.' LEN { $$->code = P('P','l'); } */ /* function_call is inlined here */ @@ -801,7 +805,6 @@ print_list: /* EMPTY */ { $$ = NULL; } $$ = $1; } else $$ = $3; } - ; var_listn: term { diff --git a/filter/f-util.c b/filter/f-util.c index 9f2eb6b3..5908ac64 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -54,6 +54,24 @@ f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct return set_dyn; } + +struct f_inst * +f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn) +{ + struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check)); + ret->i.code = P('R','C'); + ret->i.lineno = ifs->conf_lino; + ret->i.arg1 = prefix; + ret->i.arg2 = asn; + /* prefix == NULL <-> asn == NULL */ + + if ((sym->class != SYM_ROA) || ! sym->def) + cf_error("%s is not a ROA table", sym->name); + ret->rtc = sym->def; + + return &ret->i; +} + char * filter_name(struct filter *filter) { @@ -61,6 +79,8 @@ filter_name(struct filter *filter) return "ACCEPT"; else if (filter == FILTER_REJECT) return "REJECT"; + else if (!filter->name) + return "(unnamed)"; else return filter->name; } diff --git a/filter/filter.c b/filter/filter.c index d6d338bf..acdcfd2b 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -228,6 +228,9 @@ val_simple_in_range(struct f_val v1, struct f_val v2) { if ((v1.type == T_PATH) && (v2.type == T_PATH_MASK)) return as_path_match(v1.val.ad, v2.val.path_mask); + if ((v1.type == T_INT) && (v2.type == T_PATH)) + return as_path_is_member(v2.val.ad, v1.val.i); + if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST)) return int_set_contains(v2.val.ad, v1.val.i); #ifndef IPV6 @@ -245,7 +248,7 @@ val_simple_in_range(struct f_val v1, struct f_val v2) return ipa_in_net(v1.val.px.ip, v2.val.px.ip, v2.val.px.len); if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX)) - return ipa_in_net(v1.val.px.ip, v2.val.px.ip, v2.val.px.len) && (v1.val.px.len >= v2.val.px.len); + return net_in_net(v1.val.px.ip, v1.val.px.len, v2.val.px.ip, v2.val.px.len); return CMP_ERROR; } @@ -320,28 +323,34 @@ eclist_match_set(struct adata *list, struct f_tree *set) } static struct adata * -clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int pos) +clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) { - if (!clist) + if (!list) return NULL; + int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ struct f_val v; - clist_set_type(set, &v); + if (tree) + clist_set_type(set.val.t, &v); + else + v.type = T_PAIR; - u32 tmp[clist->length/4]; - u32 *l = (u32 *) clist->data; + int len = int_set_get_size(list); + u32 *l = int_set_get_data(list); + u32 tmp[len]; u32 *k = tmp; - u32 *end = l + clist->length/4; + u32 *end = l + len; while (l < end) { v.val.i = *l++; - if (pos == !!find_tree(set, v)) /* pos && find_tree || !pos && !find_tree */ + /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ + if ((tree ? !!find_tree(set.val.t, v) : int_set_contains(set.val.ad, v.val.i)) == pos) *k++ = v.val.i; } int nl = (k - tmp) * 4; - if (nl == clist->length) - return clist; + if (nl == list->length) + return list; struct adata *res = adata_empty(pool, nl); memcpy(res->data, tmp, nl); @@ -349,11 +358,12 @@ clist_filter(struct linpool *pool, struct adata *clist, struct f_tree *set, int } static struct adata * -eclist_filter(struct linpool *pool, struct adata *list, struct f_tree *set, int pos) +eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) { if (!list) return NULL; + int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ struct f_val v; int len = int_set_get_size(list); @@ -365,7 +375,8 @@ eclist_filter(struct linpool *pool, struct adata *list, struct f_tree *set, int v.type = T_EC; for (i = 0; i < len; i += 2) { v.val.ec = ec_get(l, i); - if (pos == !!find_tree(set, v)) { /* pos && find_tree || !pos && !find_tree */ + /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ + if ((tree ? !!find_tree(set.val.t, v) : ec_set_contains(set.val.ad, v.val.ec)) == pos) { *k++ = l[i]; *k++ = l[i+1]; } @@ -1116,6 +1127,8 @@ interpret(struct f_inst *what) #endif else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) arg_set = 1; + else if (v2.type == T_CLIST) + arg_set = 2; else runtime("Can't add/delete non-pair"); @@ -1123,22 +1136,25 @@ interpret(struct f_inst *what) switch (what->aux) { case 'a': - if (arg_set) + if (arg_set == 1) runtime("Can't add set"); - res.val.ad = int_set_add(f_pool, v1.val.ad, i); + else if (!arg_set) + res.val.ad = int_set_add(f_pool, v1.val.ad, i); + else + res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad); break; case 'd': if (!arg_set) res.val.ad = int_set_del(f_pool, v1.val.ad, i); else - res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 0); + res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 0); break; case 'f': if (!arg_set) runtime("Can't filter pair"); - res.val.ad = clist_filter(f_pool, v1.val.ad, v2.val.t, 1); + res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 1); break; default: @@ -1153,6 +1169,8 @@ interpret(struct f_inst *what) /* v2.val is either EC or EC-set */ if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) arg_set = 1; + else if (v2.type == T_ECLIST) + arg_set = 2; else if (v2.type != T_EC) runtime("Can't add/delete non-pair"); @@ -1160,22 +1178,25 @@ interpret(struct f_inst *what) switch (what->aux) { case 'a': - if (arg_set) + if (arg_set == 1) runtime("Can't add set"); - res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec); + else if (!arg_set) + res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec); + else + res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad); break; case 'd': if (!arg_set) res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec); else - res.val.ad = eclist_filter(f_pool, v1.val.ad, v2.val.t, 0); + res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 0); break; case 'f': if (!arg_set) runtime("Can't filter ec"); - res.val.ad = eclist_filter(f_pool, v1.val.ad, v2.val.t, 1); + res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 1); break; default: @@ -1187,6 +1208,38 @@ interpret(struct f_inst *what) break; + case P('R','C'): /* ROA Check */ + if (what->arg1) + { + TWOARGS; + if ((v1.type != T_PREFIX) || (v2.type != T_INT)) + runtime("Invalid argument to roa_check()"); + + as = v2.val.i; + } + else + { + v1.val.px.ip = (*f_rte)->net->n.prefix; + v1.val.px.len = (*f_rte)->net->n.pxlen; + + /* We ignore temporary attributes, probably not a problem here */ + /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */ + eattr *e = ea_find((*f_rte)->attrs->eattrs, EA_CODE(EAP_BGP, 0x02)); + + if (!e || e->type != EAF_TYPE_AS_PATH) + runtime("Missing AS_PATH attribute"); + + as_path_get_last(e->u.ptr, &as); + } + + struct roa_table_config *rtc = ((struct f_inst_roa_check *) what)->rtc; + if (!rtc->table) + runtime("Missing ROA table"); + + res.type = T_ENUM_ROA; + res.val.i = roa_check(rtc->table, v1.val.px.ip, v1.val.px.len, as); + break; + default: bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); } @@ -1311,6 +1364,13 @@ i_same(struct f_inst *f1, struct f_inst *f2) case P('C','a'): TWOARGS; break; case P('a','f'): case P('a','l'): ONEARG; break; + case P('R','C'): + TWOARGS; + /* Does not really make sense - ROA check resuls may change anyway */ + if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name, + ((struct f_inst_roa_check *) f2)->rtc->name)) + return 0; + break; default: bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); } diff --git a/filter/filter.h b/filter/filter.h index 2cf4652d..2386fc95 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -32,6 +32,12 @@ struct f_inst { /* Instruction */ #define arg1 a1.p #define arg2 a2.p +/* Not enough fields in f_inst for three args used by roa_check() */ +struct f_inst_roa_check { + struct f_inst i; + struct roa_table_config *rtc; +}; + struct f_prefix { ip_addr ip; int len; @@ -66,6 +72,8 @@ struct f_inst *f_new_inst(void); struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */ struct f_tree *f_new_tree(void); struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument); +struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn); + struct f_tree *build_tree(struct f_tree *); struct f_tree *find_tree(struct f_tree *t, struct f_val val); @@ -141,6 +149,7 @@ int tree_compare(const void *p1, const void *p2); #define T_ENUM_SCOPE 0x32 #define T_ENUM_RTC 0x33 #define T_ENUM_RTD 0x34 +#define T_ENUM_ROA 0x35 /* new enums go here */ #define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ diff --git a/filter/test.conf b/filter/test.conf index 4f09637c..64e6d91b 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -52,12 +52,40 @@ function fifteen() return 15; } +roa table rl +{ + roa 10.110.0.0/16 max 16 as 1000; + roa 10.120.0.0/16 max 24 as 1000; + roa 10.130.0.0/16 max 24 as 2000; + roa 10.130.128.0/18 max 24 as 3000; +} + +function test_roa() +{ + # cannot be tested in __startup(), sorry + print "Testing ROA"; + print "Should be true: ", roa_check(rl, 10.10.0.0/16, 1000) = ROA_UNKNOWN, + " ", roa_check(rl, 10.0.0.0/8, 1000) = ROA_UNKNOWN, + " ", roa_check(rl, 10.110.0.0/16, 1000) = ROA_VALID, + " ", roa_check(rl, 10.110.0.0/16, 2000) = ROA_INVALID, + " ", roa_check(rl, 10.110.32.0/20, 1000) = ROA_INVALID, + " ", roa_check(rl, 10.120.32.0/20, 1000) = ROA_VALID; + print "Should be true: ", roa_check(rl, 10.120.32.0/20, 2000) = ROA_INVALID, + " ", roa_check(rl, 10.120.32.32/28, 1000) = ROA_INVALID, + " ", roa_check(rl, 10.130.130.0/24, 1000) = ROA_INVALID, + " ", roa_check(rl, 10.130.130.0/24, 2000) = ROA_VALID, + " ", roa_check(rl, 10.130.30.0/24, 3000) = ROA_INVALID, + " ", roa_check(rl, 10.130.130.0/24, 3000) = ROA_VALID; +} + function paths() bgpmask pm1; bgpmask pm2; bgppath p2; clist l; +clist l2; eclist el; +eclist el2; { pm1 = / 4 3 2 1 /; pm2 = [= 4 3 2 1 =]; @@ -67,10 +95,10 @@ eclist el; p2 = prepend( p2, 3 ); p2 = prepend( p2, 4 ); print "Testing paths: ", p2; - print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2; + print "Should be true: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 3 ~ p2; print "4 = ", p2.len; p2 = prepend( p2, 5 ); - print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2; + print "Should be false: ", p2 ~ pm1, " ", p2 ~ pm2, " ", 10 ~ p2; print "Should be true: ", p2 ~ / ? 4 3 2 1 /, " ", p2, " ", / ? 4 3 2 1 /; print "Should be true: ", p2 ~ [= * 4 3 * 1 =], " ", p2, " ", [= * 4 3 * 1 =]; print "Should be true: ", p2 ~ [= (3+2) (2*2) 3 2 1 =], " ", p2 ~ mkpath(5, 4); @@ -108,6 +136,7 @@ eclist el; l = add( l, (3,3) ); l = add( l, (3,4) ); l = add( l, (3,5) ); + l2 = filter( l, [(3,*)] ); l = delete( l, [(3,2..4)] ); print "Community list (1,2) (3,1) (3,5) ", l; l = add( l, (3,2) ); @@ -120,6 +149,14 @@ eclist el; print "Community list (3,1) ", l; l = delete( l, [(*,(onef(5)))] ); print "Community list empty ", l; + l2 = add( l2, (3,6) ); + l = filter( l2, [(3,1..4)] ); + l2 = filter( l2, [(3,3..6)] ); + print "clist A (1..4): ", l; + print "clist B (3..6): ", l2; + print "clist A union B: ", add( l2, l ); + print "clist A isect B: ", filter( l, l2 ); + print "clist A \ B: ", delete( l, l2 ); el = -- empty --; el = add(el, (rt, 10, 20)); @@ -143,6 +180,16 @@ eclist el; print "EC list (rt, 10, 1) (rt, 10, 30): ", el; print "Testing EC list, true: ", (rt, 10, 1) ~ el, " ", el ~ [(rt, 10, ten..40)]; print "Testing EC list, false: ", (rt, 10, 20) ~ el, " ", (ro, 10.20.30.40, 100) ~ el, " ", el ~ [(rt, 10, 35..40)], " ", el ~ [(ro, 10, *)]; + el = add(el, (rt, 10, 40)); + el2 = filter(el, [(rt, 10, 20..40)] ); + el2 = add(el2, (rt, 10, 50)); + print "eclist A (1,30,40): ", el; + print "eclist B (30,40,50): ", el2; + print "eclist A union B: ", add( el2, el ); + print "eclist A isect B: ", filter( el, el2 ); + print "eclist A \ B: ", delete( el, el2 ); + +# test_roa(); } function bla() @@ -15,7 +15,10 @@ #include "ipv6.h" #endif -#define ipa_in_net(x,n,p) (!ipa_nonzero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p)))) +#define ipa_zero(x) (!ipa_nonzero(x)) +#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l))))) +#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p)))) +#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2))))) /* * ip_classify() returns either a negative number for invalid addresses @@ -50,9 +53,6 @@ struct prefix { unsigned int len; }; -#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l))))) -#define ipa_zero(x) (!ipa_nonzero(x)) - static inline int ipa_classify_net(ip_addr a) { return ipa_zero(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); } diff --git a/misc/bird.spec b/misc/bird.spec index c08cb5e1..60002a5d 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 1.3.6 +Version: 1.3.7 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/nest/Makefile b/nest/Makefile index 478a82b7..e6928668 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -1,4 +1,4 @@ -source=rt-table.c rt-fib.c rt-attr.c proto.c iface.c rt-dev.c password.c cli.c locks.c cmds.c neighbor.c \ +source=rt-table.c rt-fib.c rt-attr.c rt-roa.c proto.c iface.c rt-dev.c password.c cli.c locks.c cmds.c neighbor.c \ a-path.c a-set.c root-rel=../ dir-name=nest diff --git a/nest/a-set.c b/nest/a-set.c index 020d0978..42ef9b06 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -264,3 +264,67 @@ ec_set_del(struct linpool *pool, struct adata *list, u64 val) return res; } + + +struct adata * +int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) +{ + if (!l1) + return l2; + if (!l2) + return l1; + + struct adata *res; + int len = int_set_get_size(l2); + u32 *l = int_set_get_data(l2); + u32 tmp[len]; + u32 *k = tmp; + int i; + + for (i = 0; i < len; i++) + if (!int_set_contains(l1, l[i])) + *k++ = l[i]; + + if (k == tmp) + return l1; + + len = (k - tmp) * 4; + res = lp_alloc(pool, sizeof(struct adata) + l1->length + len); + res->length = l1->length + len; + memcpy(res->data, l1->data, l1->length); + memcpy(res->data + l1->length, tmp, len); + return res; +} + +struct adata * +ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) +{ + if (!l1) + return l2; + if (!l2) + return l1; + + struct adata *res; + int len = int_set_get_size(l2); + u32 *l = int_set_get_data(l2); + u32 tmp[len]; + u32 *k = tmp; + int i; + + for (i = 0; i < len; i += 2) + if (!ec_set_contains(l1, ec_get(l, i))) + { + *k++ = l[i]; + *k++ = l[i+1]; + } + + if (k == tmp) + return l1; + + len = (k - tmp) * 4; + res = lp_alloc(pool, sizeof(struct adata) + l1->length + len); + res->length = l1->length + len; + memcpy(res->data, l1->data, l1->length); + memcpy(res->data + l1->length, tmp, len); + return res; +} diff --git a/nest/attrs.h b/nest/attrs.h index 85e4e59a..42f81a10 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -96,6 +96,8 @@ struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val); struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val); struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val); struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val); +struct adata *int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); +struct adata *ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); #endif diff --git a/nest/cmds.c b/nest/cmds.c index 8ac32096..2a803930 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -7,6 +7,7 @@ */ #include "nest/bird.h" +#include "nest/route.h" #include "nest/cli.h" #include "conf/conf.h" #include "nest/cmds.h" @@ -35,16 +36,22 @@ cmd_show_status(void) } void -cmd_show_symbols(struct symbol *sym) +cmd_show_symbols(struct sym_show_data *sd) { int pos = 0; + struct symbol *sym = sd->sym; if (sym) - cli_msg(1010, "%s\t%s", sym->name, cf_symbol_class_name(sym)); + cli_msg(1010, "%-8s\t%s", sym->name, cf_symbol_class_name(sym)); else { while (sym = cf_walk_symbols(config, sym, &pos)) - cli_msg(-1010, "%s\t%s", sym->name, cf_symbol_class_name(sym)); + { + if (sd->type && (sym->class != sd->type)) + continue; + + cli_msg(-1010, "%-8s\t%s", sym->name, cf_symbol_class_name(sym)); + } cli_msg(0, ""); } } @@ -65,6 +72,7 @@ print_size(char *dsc, size_t val) extern pool *rt_table_pool; extern pool *rta_pool; +extern pool *roa_pool; extern pool *proto_pool; void @@ -73,6 +81,7 @@ cmd_show_memory(void) cli_msg(-1018, "BIRD memory usage"); print_size("Routing tables:", rmemsize(rt_table_pool)); print_size("Route attributes:", rmemsize(rta_pool)); + print_size("ROA tables:", rmemsize(roa_pool)); print_size("Protocols:", rmemsize(proto_pool)); print_size("Total:", rmemsize(&root_pool)); cli_msg(0, ""); diff --git a/nest/cmds.h b/nest/cmds.h index 3b86a924..8b0bff7e 100644 --- a/nest/cmds.h +++ b/nest/cmds.h @@ -6,6 +6,11 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +struct sym_show_data { + int type; /* Symbols type to show */ + struct symbol *sym; +}; + void cmd_show_status(void); -void cmd_show_symbols(struct symbol *sym); +void cmd_show_symbols(struct sym_show_data *sym); void cmd_show_memory(void); diff --git a/nest/config.Y b/nest/config.Y index 3fcfa528..f889828a 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -19,6 +19,7 @@ CF_DEFINES static struct proto_config *this_proto; static struct iface_patt *this_ipatt; static struct iface_patt_node *this_ipn; +static struct roa_table_config *this_roa_table; static list *this_p_list; static struct password_item *this_p_item; static int password_id; @@ -44,7 +45,7 @@ CF_DECLS CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, 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(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC) @@ -53,13 +54,17 @@ CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIREC CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST) CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH) +CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) %type <i32> idval %type <f> imexport %type <r> rtable %type <s> optsym %type <ra> r_args -%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport +%type <ro> roa_args +%type <rot> roa_table_arg +%type <sd> sym_args +%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode %type <ps> proto_patt proto_patt2 CF_GRAMMAR @@ -112,6 +117,24 @@ newtab: TABLE SYM { } ; +CF_ADDTO(conf, roa_table) + +roa_table_start: ROA TABLE SYM { + this_roa_table = roa_new_table_config($3); +}; + +roa_table_opts: + /* empty */ + | roa_table_opts ROA prefix MAX NUM AS NUM ';' { + roa_add_item_config(this_roa_table, $3.addr, $3.len, $5, $7); + } + ; + +roa_table: + roa_table_start + | roa_table_start '{' roa_table_opts '}' + ; + /* Definition of protocols */ CF_ADDTO(conf, proto) @@ -353,6 +376,7 @@ CF_CLI(SHOW INTERFACES,,, [[Show network interfaces]]) CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]]) { if_show_summary(); } ; +CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]]) { rt_show($3); } ; @@ -432,9 +456,97 @@ export_or_preexport: | EXPORT { $$ = 2; } ; -CF_CLI(SHOW SYMBOLS, optsym, [<symbol>], [[Show all known symbolic names]]) + +CF_CLI_HELP(SHOW ROA, ..., [[Show ROA table]]) +CF_CLI(SHOW ROA, roa_args, [<prefix> | in <prefix> | for <prefix>] [as <num>] [table <t>], [[Show ROA table]]) +{ roa_show($3); } ; + +roa_args: + /* empty */ { + $$ = cfg_allocz(sizeof(struct roa_show_data)); + $$->mode = ROA_SHOW_ALL; + $$->table = roa_table_default; + if (roa_table_default == NULL) + cf_error("No ROA table defined"); + } + | roa_args roa_mode prefix { + $$ = $1; + if ($$->mode != ROA_SHOW_ALL) cf_error("Only one prefix expected"); + $$->prefix = $3.addr; + $$->pxlen = $3.len; + $$->mode = $2; + } + | roa_args AS NUM { + $$ = $1; + $$->asn = $3; + } + | roa_args TABLE SYM { + $$ = $1; + if ($3->class != SYM_ROA) cf_error("%s is not a ROA table", $3->name); + $$->table = ((struct roa_table_config *)$3->def)->table; + } + ; + +roa_mode: + { $$ = ROA_SHOW_PX; } + | IN { $$ = ROA_SHOW_IN; } + | FOR { $$ = ROA_SHOW_FOR; } + ; + + +CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]]) +CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|roa|<symbol>], [[Show all known symbolic names]]) { cmd_show_symbols($3); } ; +sym_args: + /* empty */ { + $$ = cfg_allocz(sizeof(struct sym_show_data)); + } + | sym_args TABLE { $$ = $1; $$->type = SYM_TABLE; } + | sym_args FUNCTION { $$ = $1; $$->type = SYM_FUNCTION; } + | sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; } + | sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; } + | sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; } + | sym_args ROA { $$ = $1; $$->type = SYM_ROA; } + | sym_args SYM { $$ = $1; $$->sym = $2; } + ; + + +roa_table_arg: + /* empty */ { + if (roa_table_default == NULL) + cf_error("No ROA table defined"); + $$ = roa_table_default; + } + | TABLE SYM { + if ($2->class != SYM_ROA) + cf_error("%s is not a ROA table", $2->name); + $$ = ((struct roa_table_config *)$2->def)->table; + } + ; + +CF_CLI_HELP(ADD, roa ..., [[Add ROA record]]) +CF_CLI(ADD ROA, prefix MAX NUM AS NUM roa_table_arg, <prefix> max <num> as <num> [table <name>], [[Add ROA record]]) +{ + if (! cli_access_restricted()) + { roa_add_item($8, $3.addr, $3.len, $5, $7, ROA_SRC_DYNAMIC); cli_msg(0, ""); } +}; + +CF_CLI_HELP(DELETE, roa ..., [[Delete ROA record]]) +CF_CLI(DELETE ROA, prefix MAX NUM AS NUM roa_table_arg, <prefix> max <num> as <num> [table <name>], [[Delete ROA record]]) +{ + if (! cli_access_restricted()) + { roa_delete_item($8, $3.addr, $3.len, $5, $7, ROA_SRC_DYNAMIC); cli_msg(0, ""); } +}; + +CF_CLI_HELP(FLUSH, roa [table <name>], [[Removes all dynamic ROA records]]) +CF_CLI(FLUSH ROA, roa_table_arg, [table <name>], [[Removes all dynamic ROA records]]) +{ + if (! cli_access_restricted()) + { roa_flush($3, ROA_SRC_DYNAMIC); cli_msg(0, ""); } +}; + + CF_CLI_HELP(DUMP, ..., [[Dump debugging information]]) CF_CLI(DUMP RESOURCES,,, [[Dump all allocated resource]]) { rdump(&root_pool); cli_msg(0, ""); } ; diff --git a/nest/proto.c b/nest/proto.c index 9a7f123e..802d5238 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -38,7 +38,7 @@ 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_flush_loop(void *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); @@ -703,7 +703,7 @@ protos_build(void) #endif proto_pool = rp_new(&root_pool, "Protocols"); proto_flush_event = ev_new(proto_pool); - proto_flush_event->hook = proto_flush_all; + proto_flush_event->hook = proto_flush_loop; } static void @@ -763,20 +763,6 @@ proto_feed_initial(void *P) } static void -proto_schedule_flush(struct proto *p) -{ - /* Need to abort feeding */ - if (p->core_state == FS_FEEDING) - rt_feed_baby_abort(p); - - DBG("%s: Scheduling flush\n", p->name); - p->core_state = FS_FLUSHING; - proto_relink(p); - proto_unlink_ahooks(p); - ev_schedule(proto_flush_event); -} - -static void proto_schedule_feed(struct proto *p, int initial) { DBG("%s: Scheduling meal\n", p->name); @@ -797,6 +783,84 @@ proto_schedule_feed(struct proto *p, int initial) ev_schedule(p->attn); } +/* + * Flushing loop is responsible for flushing routes and protocols + * after they went down. It runs in proto_flush_event. At the start of + * one round, protocols waiting to flush are marked in + * proto_schedule_flush_loop(). At the end of the round (when routing + * table flush is complete), marked protocols are flushed and a next + * round may start. + */ + +static int flush_loop_state; /* 1 -> running */ + +static void +proto_schedule_flush_loop(void) +{ + struct proto *p; + + if (flush_loop_state) + return; + flush_loop_state = 1; + + rt_schedule_prune_all(); + WALK_LIST(p, flush_proto_list) + p->flushing = 1; + + ev_schedule(proto_flush_event); +} + +static void +proto_flush_loop(void *unused UNUSED) +{ + struct proto *p; + + if (! rt_prune_loop()) + { + /* Rtable pruning is not finished */ + ev_schedule(proto_flush_event); + return; + } + + again: + WALK_LIST(p, flush_proto_list) + if (p->flushing) + { + /* This will flush interfaces in the same manner + like rt_prune_all() flushes routes */ + if (p->proto == &proto_unix_iface) + if_flush_ifaces(p); + + DBG("Flushing protocol %s\n", p->name); + p->flushing = 0; + p->core_state = FS_HUNGRY; + proto_relink(p); + if (p->proto_state == PS_DOWN) + proto_fell_down(p); + goto again; + } + + /* This round finished, perhaps there will be another one */ + flush_loop_state = 0; + if (!EMPTY_LIST(flush_proto_list)) + proto_schedule_flush_loop(); +} + +static void +proto_schedule_flush(struct proto *p) +{ + /* Need to abort feeding */ + if (p->core_state == FS_FEEDING) + rt_feed_baby_abort(p); + + DBG("%s: Scheduling flush\n", p->name); + p->core_state = FS_FLUSHING; + proto_relink(p); + proto_unlink_ahooks(p); + proto_schedule_flush_loop(); +} + + /** * proto_request_feeding - request feeding routes to the protocol * @p: given protocol @@ -886,27 +950,6 @@ proto_notify_state(struct proto *p, unsigned ps) } } -static void -proto_flush_all(void *unused UNUSED) -{ - struct proto *p; - - rt_prune_all(); - while ((p = HEAD(flush_proto_list))->n.next) - { - /* This will flush interfaces in the same manner - like rt_prune_all() flushes routes */ - if (p->proto == &proto_unix_iface) - if_flush_ifaces(p); - - DBG("Flushing protocol %s\n", p->name); - p->core_state = FS_HUNGRY; - proto_relink(p); - if (p->proto_state == PS_DOWN) - proto_fell_down(p); - } -} - /* * CLI Commands */ diff --git a/nest/protocol.h b/nest/protocol.h index 187b8712..983ce75a 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -145,6 +145,7 @@ struct proto { unsigned core_goal; /* State we want to reach (see below) */ unsigned reconfiguring; /* We're shutting down due to reconfiguration */ unsigned refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */ + unsigned flushing; /* Protocol is flushed in current flush loop round */ 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 */ diff --git a/nest/route.h b/nest/route.h index fcb4f283..34536609 100644 --- a/nest/route.h +++ b/nest/route.h @@ -140,8 +140,10 @@ typedef struct rtable { int gc_counter; /* Number of operations since last GC */ bird_clock_t gc_time; /* Time of last GC */ byte gc_scheduled; /* GC is scheduled */ + byte prune_state; /* Table prune state, 1 -> prune is running */ byte hcu_scheduled; /* Hostcache update is scheduled */ byte nhu_state; /* Next Hop Update state */ + struct fib_iterator prune_fit; /* Rtable prune FIB iterator */ struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */ } rtable; @@ -246,7 +248,8 @@ void rt_dump(rtable *); void rt_dump_all(void); int rt_feed_baby(struct proto *p); void rt_feed_baby_abort(struct proto *p); -void rt_prune_all(void); +void rt_schedule_prune_all(void); +int rt_prune_loop(void); struct rtable_config *rt_new_table(struct symbol *s); struct rt_show_data { @@ -456,4 +459,84 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX]; #define DEF_PREF_PIPE 70 /* Routes piped from other tables */ #define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */ + +/* + * Route Origin Authorization + */ + +struct roa_item { + u32 asn; + byte maxlen; + byte src; + struct roa_item *next; +}; + +struct roa_node { + struct fib_node n; + struct roa_item *items; + // u32 cached_asn; +}; + +struct roa_table { + node n; /* Node in roa_table_list */ + struct fib fib; + char *name; /* Name of this ROA table */ + struct roa_table_config *cf; /* Configuration of this ROA table */ +}; + +struct roa_item_config { + ip_addr prefix; + byte pxlen, maxlen; + u32 asn; + struct roa_item_config *next; +}; + +struct roa_table_config { + node n; /* Node in config->rpa_tables */ + char *name; /* Name of this ROA table */ + struct roa_table *table; + + struct roa_item_config *roa_items; /* Preconfigured ROA items */ + + // char *filename; + // int gc_max_ops; /* Maximum number of operations before GC is run */ + // int gc_min_time; /* Minimum time between two consecutive GC runs */ +}; + +struct roa_show_data { + struct fib_iterator fit; + struct roa_table *table; + ip_addr prefix; + byte pxlen; + byte mode; /* ROA_SHOW_* values */ + u32 asn; /* Filter ASN, 0 -> all */ +}; + +#define ROA_UNKNOWN 0 +#define ROA_VALID 1 +#define ROA_INVALID 2 + +#define ROA_SRC_ANY 0 +#define ROA_SRC_CONFIG 1 +#define ROA_SRC_DYNAMIC 2 + +#define ROA_SHOW_ALL 0 +#define ROA_SHOW_PX 1 +#define ROA_SHOW_IN 2 +#define ROA_SHOW_FOR 3 + +extern struct roa_table *roa_table_default; + +void roa_add_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src); +void roa_delete_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src); +void roa_flush(struct roa_table *t, byte src); +byte roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn); +struct roa_table_config * roa_new_table_config(struct symbol *s); +void roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn); +void roa_init(void); +void roa_preconfig(struct config *c); +void roa_commit(struct config *new, struct config *old); +void roa_show(struct roa_show_data *d); + + #endif diff --git a/nest/rt-roa.c b/nest/rt-roa.c new file mode 100644 index 00000000..aa453f16 --- /dev/null +++ b/nest/rt-roa.c @@ -0,0 +1,440 @@ +/* + * BIRD -- Route Origin Authorization + * + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#undef LOCAL_DEBUG + +#include "nest/bird.h" +#include "nest/route.h" +#include "nest/cli.h" +#include "lib/lists.h" +#include "lib/resource.h" +#include "lib/event.h" +#include "lib/string.h" +#include "conf/conf.h" + + +pool *roa_pool; +static slab *roa_slab; /* Slab of struct roa_item */ +static list roa_table_list; /* List of struct roa_table */ +struct roa_table *roa_table_default; /* The first ROA table in the config */ + +static inline int +src_match(struct roa_item *it, byte src) +{ return !src || it->src == src; } + +/** + * roa_add_item - add a ROA entry + * @t: ROA table + * @prefix: prefix of the ROA entry + * @pxlen: prefix length of the ROA entry + * @maxlen: max length field of the ROA entry + * @asn: AS number field of the ROA entry + * @src: source of the ROA entry (ROA_SRC_*) + * + * The function adds a new ROA entry to the ROA table. If the same ROA + * is already in the table, nothing is added. @src field is used to + * distinguish different sources of ROAs. + */ +void +roa_add_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src) +{ + struct roa_node *n = fib_get(&t->fib, &prefix, pxlen); + + // if ((n->items == NULL) && (n->n.x0 != ROA_INVALID)) + // t->cached_items--; + + struct roa_item *it; + for (it = n->items; it; it = it->next) + if ((it->maxlen == maxlen) && (it->asn == asn) && src_match(it, src)) + return; + + it = sl_alloc(roa_slab); + it->asn = asn; + it->maxlen = maxlen; + it->src = src; + it->next = n->items; + n->items = it; +} + +/** + * roa_delete_item - delete a ROA entry + * @t: ROA table + * @prefix: prefix of the ROA entry + * @pxlen: prefix length of the ROA entry + * @maxlen: max length field of the ROA entry + * @asn: AS number field of the ROA entry + * @src: source of the ROA entry (ROA_SRC_*) + * + * The function removes a specified ROA entry from the ROA table and + * frees it. If @src field is not ROA_SRC_ANY, only entries from + * that source are considered. + */ +void +roa_delete_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src) +{ + struct roa_node *n = fib_find(&t->fib, &prefix, pxlen); + + if (!n) + return; + + struct roa_item *it, **itp; + for (itp = &n->items; it = *itp; itp = &it->next) + if ((it->maxlen == maxlen) && (it->asn == asn) && src_match(it, src)) + break; + + if (!it) + return; + + *itp = it->next; + sl_free(roa_slab, it); + + // if ((n->items == NULL) && (n->n.x0 != ROA_INVALID)) + // t->cached_items++; +} + + +/** + * roa_flush - flush a ROA table + * @t: ROA table + * @src: source of ROA entries (ROA_SRC_*) + * + * The function removes and frees ROA entries from the ROA table. If + * @src is ROA_SRC_ANY, all entries in the table are removed, + * otherwise only all entries from that source are removed. + */ +void +roa_flush(struct roa_table *t, byte src) +{ + struct roa_item *it, **itp; + struct roa_node *n; + + FIB_WALK(&t->fib, fn) + { + n = (struct roa_node *) fn; + + itp = &n->items; + while (it = *itp) + if (src_match(it, src)) + { + *itp = it->next; + sl_free(roa_slab, it); + } + else + itp = &it->next; + } + FIB_WALK_END; + + // TODO add cleanup of roa_nodes +} + + + +/* +byte +roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn) +{ + struct roa_node *n = fib_find(&t->fib, &prefix, pxlen); + + if (n && n->n.x0 == ROA_UNKNOWN) + return ROA_UNKNOWN; + + if (n && n->n.x0 == ROA_VALID && asn == n->cached_asn) + return ROA_VALID; + + byte rv = roa_match(t, n, prefix, pxlen, asn); + + if (rv != ROA_INVALID) + { + if (!n) + { + if (t->cached_items >= t->cached_items_max) + n = fib_get(&t->fib, &prefix, pxlen); + t->cached_items++; + } + + n->cached_asn = asn; + n->n.x0 = rv; + } + + return rv; +} +*/ + +/** + * roa_check - check validity of route origination in a ROA table + * @t: ROA table + * @prefix: network prefix to check + * @pxlen: length of network prefix + * @asn: AS number of network prefix + * + * Implements RFC 6483 route validation for the given network + * prefix. The procedure is to find all candidate ROAs - ROAs whose + * prefixes cover the give network prefix. If there is no candidate + * ROA, return ROA_UNKNOWN. If there is a candidate ROA with matching + * ASN and maxlen field greater than or equal to the given prefix + * length, return ROA_VALID. Otherwise return ROA_INVALID. If caller + * cannot determine origin AS, 0 could be used (in that case ROA_VALID + * cannot happen). + */ +byte +roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn) +{ + struct roa_node *n; + ip_addr px; + byte anything = 0; + + int len; + for (len = pxlen; len >= 0; len--) + { + px = ipa_and(prefix, ipa_mkmask(len)); + n = fib_find(&t->fib, &px, len); + + if (!n) + continue; + + struct roa_item *it; + for (it = n->items; it; it = it->next) + { + anything = 1; + if ((it->maxlen >= pxlen) && (it->asn == asn) && asn) + return ROA_VALID; + } + } + + return anything ? ROA_INVALID : ROA_UNKNOWN; +} + +static void +roa_node_init(struct fib_node *fn) +{ + struct roa_node *n = (struct roa_node *) fn; + n->items = NULL; +} + +static inline void +roa_populate(struct roa_table *t) +{ + struct roa_item_config *ric; + for (ric = t->cf->roa_items; ric; ric = ric->next) + roa_add_item(t, ric->prefix, ric->pxlen, ric->maxlen, ric->asn, ROA_SRC_CONFIG); +} + +static void +roa_new_table(struct roa_table_config *cf) +{ + struct roa_table *t; + + t = mb_allocz(roa_pool, sizeof(struct roa_table)); + fib_init(&t->fib, roa_pool, sizeof(struct roa_node), 0, roa_node_init); + t->name = cf->name; + t->cf = cf; + + cf->table = t; + add_tail(&roa_table_list, &t->n); + + roa_populate(t); +} + +struct roa_table_config * +roa_new_table_config(struct symbol *s) +{ + struct roa_table_config *rtc = cfg_allocz(sizeof(struct roa_table_config)); + + cf_define_symbol(s, SYM_ROA, rtc); + rtc->name = s->name; + add_tail(&new_config->roa_tables, &rtc->n); + return rtc; +} + +/** + * roa_add_item_config - add a static ROA entry to a ROA table configuration + * + * Arguments are self-explanatory. The first is the ROA table config, rest + * are specifying the ROA entry. + */ +void +roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn) +{ + struct roa_item_config *ric = cfg_allocz(sizeof(struct roa_item_config)); + + ric->prefix = prefix; + ric->pxlen = pxlen; + ric->maxlen = maxlen; + ric->asn = asn; + ric->next = rtc->roa_items; + rtc->roa_items = ric; +} + +/** + * roa_init - initialize ROA tables + * + * This function is called during BIRD startup. It initializes + * the ROA table module. + */ +void +roa_init(void) +{ + roa_pool = rp_new(&root_pool, "ROA tables"); + roa_slab = sl_new(roa_pool, sizeof(struct roa_item)); + init_list(&roa_table_list); +} + +void +roa_preconfig(struct config *c) +{ + init_list(&c->roa_tables); +} + + +/** + * roa_commit - commit new ROA table configuration + * @new: new configuration + * @old: original configuration or %NULL if it's boot time config + * + * Scan differences between @old and @new configuration and modify the + * ROA tables according to these changes. If @new defines a previously + * unknown table, create it, if it omits a table existing in @old, + * delete it (there are no references, only indirect through struct + * roa_table_config). If it exists in both configurations, update the + * configured ROA entries. + */ +void +roa_commit(struct config *new, struct config *old) +{ + struct roa_table_config *cf; + struct roa_table *t; + + if (old) + WALK_LIST(t, roa_table_list) + { + struct symbol *sym = cf_find_symbol(t->name); + if (sym && sym->class == SYM_ROA) + { + /* Found old table in new config */ + cf = sym->def; + cf->table = t; + t->name = cf->name; + t->cf = cf; + + /* Reconfigure it */ + roa_flush(t, ROA_SRC_CONFIG); + roa_populate(t); + } + else + { + t->cf->table = NULL; + + /* Free it now */ + roa_flush(t, ROA_SRC_ANY); + rem_node(&t->n); + fib_free(&t->fib); + mb_free(t); + } + } + + /* Add new tables */ + WALK_LIST(cf, new->roa_tables) + if (! cf->table) + roa_new_table(cf); + + roa_table_default = EMPTY_LIST(new->roa_tables) ? NULL : + ((struct roa_table_config *) HEAD(new->roa_tables))->table; +} + + + +static void +roa_show_node(struct cli *c, struct roa_node *rn, int len, u32 asn) +{ + struct roa_item *ri; + + for (ri = rn->items; ri; ri = ri->next) + if ((ri->maxlen >= len) && (!asn || (ri->asn == asn))) + cli_printf(c, -1019, "%I/%d max %d as %u", rn->n.prefix, rn->n.pxlen, ri->maxlen, ri->asn); +} + +static void +roa_show_cont(struct cli *c) +{ + struct roa_show_data *d = c->rover; + struct fib *fib = &d->table->fib; + struct fib_iterator *it = &d->fit; + struct roa_node *rn; + unsigned max = 32; + + FIB_ITERATE_START(fib, it, f) + { + rn = (struct roa_node *) f; + + if (!max--) + { + FIB_ITERATE_PUT(it, f); + return; + } + + if ((d->mode == ROA_SHOW_ALL) || + net_in_net(rn->n.prefix, rn->n.pxlen, d->prefix, d->pxlen)) + roa_show_node(c, rn, 0, d->asn); + } + FIB_ITERATE_END(f); + + cli_printf(c, 0, ""); + c->cont = c->cleanup = NULL; +} + +static void +roa_show_cleanup(struct cli *c) +{ + struct roa_show_data *d = c->rover; + + /* Unlink the iterator */ + fit_get(&d->table->fib, &d->fit); +} + +void +roa_show(struct roa_show_data *d) +{ + struct roa_node *rn; + ip_addr px; + int len; + + switch (d->mode) + { + case ROA_SHOW_ALL: + case ROA_SHOW_IN: + FIB_ITERATE_INIT(&d->fit, &d->table->fib); + this_cli->cont = roa_show_cont; + this_cli->cleanup = roa_show_cleanup; + this_cli->rover = d; + break; + + case ROA_SHOW_PX: + rn = fib_find(&d->table->fib, &d->prefix, d->pxlen); + if (rn) + { + roa_show_node(this_cli, rn, 0, d->asn); + cli_msg(0, ""); + } + else + cli_msg(-8001, "Network not in table"); + break; + + case ROA_SHOW_FOR: + for (len = d->pxlen; len >= 0; len--) + { + px = ipa_and(d->prefix, ipa_mkmask(len)); + rn = fib_find(&d->table->fib, &px, len); + + if (!rn) + continue; + + roa_show_node(this_cli, rn, 0, d->asn); + } + cli_msg(0, ""); + break; + } +} diff --git a/nest/rt-table.c b/nest/rt-table.c index 3807ef8d..310c1afd 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -55,7 +55,6 @@ static void rt_free_hostcache(rtable *tab); static void rt_notify_hostcache(rtable *tab, net *net); static void rt_update_hostcache(rtable *tab); static void rt_next_hop_update(rtable *tab); -static void rt_prune(rtable *tab); static inline void rt_schedule_gc(rtable *tab); @@ -817,6 +816,38 @@ rt_schedule_nhu(rtable *tab) } static void +rt_prune_nets(rtable *tab) +{ + struct fib_iterator fit; + int ncnt = 0, ndel = 0; + +#ifdef DEBUGGING + fib_check(&tab->fib); +#endif + + FIB_ITERATE_INIT(&fit, &tab->fib); +again: + FIB_ITERATE_START(&tab->fib, &fit, f) + { + net *n = (net *) f; + ncnt++; + if (!n->routes) /* Orphaned FIB entry */ + { + FIB_ITERATE_PUT(&fit, f); + fib_delete(&tab->fib, f); + ndel++; + goto again; + } + } + FIB_ITERATE_END(f); + DBG("Pruned %d of %d networks\n", ndel, ncnt); + + tab->gc_counter = 0; + tab->gc_time = now; + tab->gc_scheduled = 0; +} + +static void rt_event(void *ptr) { rtable *tab = ptr; @@ -828,7 +859,7 @@ rt_event(void *ptr) rt_next_hop_update(tab); if (tab->gc_scheduled) - rt_prune(tab); + rt_prune_nets(tab); } void @@ -864,70 +895,96 @@ rt_init(void) init_list(&routing_tables); } -/** - * rt_prune - prune a routing table - * @tab: routing table to be pruned - * - * This function is called whenever a protocol shuts down. It scans - * the routing table and removes all routes belonging to inactive - * protocols and also stale network entries. - */ -static void -rt_prune(rtable *tab) + +/* Called from proto_schedule_flush_loop() only, + ensuring that all prune states are zero */ +void +rt_schedule_prune_all(void) { - struct fib_iterator fit; - int rcnt = 0, rdel = 0, ncnt = 0, ndel = 0; + rtable *t; + + WALK_LIST(t, routing_tables) + t->prune_state = 1; +} + +static inline int +rt_prune_step(rtable *tab, int *max_feed) +{ + struct fib_iterator *fit = &tab->prune_fit; DBG("Pruning route table %s\n", tab->name); #ifdef DEBUGGING fib_check(&tab->fib); #endif - FIB_ITERATE_INIT(&fit, &tab->fib); + + if (tab->prune_state == 0) + return 1; + + if (tab->prune_state == 1) + { + FIB_ITERATE_INIT(fit, &tab->fib); + tab->prune_state = 2; + } + again: - FIB_ITERATE_START(&tab->fib, &fit, f) + FIB_ITERATE_START(&tab->fib, fit, fn) { - net *n = (net *) f; + net *n = (net *) fn; rte *e; - ncnt++; + rescan: - for (e=n->routes; e; e=e->next, rcnt++) + for (e=n->routes; e; e=e->next) if (e->sender->proto->core_state != FS_HAPPY && e->sender->proto->core_state != FS_FEEDING) { + if (*max_feed <= 0) + { + FIB_ITERATE_PUT(fit, fn); + return 0; + } + rte_discard(tab, e); - rdel++; + (*max_feed)--; + goto rescan; } - if (!n->routes) /* Orphaned FIB entry? */ + if (!n->routes) /* Orphaned FIB entry */ { - FIB_ITERATE_PUT(&fit, f); - fib_delete(&tab->fib, f); - ndel++; + FIB_ITERATE_PUT(fit, fn); + fib_delete(&tab->fib, fn); goto again; } } - FIB_ITERATE_END(f); - DBG("Pruned %d of %d routes and %d of %d networks\n", rdel, rcnt, ndel, ncnt); + FIB_ITERATE_END(fn); + #ifdef DEBUGGING fib_check(&tab->fib); #endif - tab->gc_counter = 0; - tab->gc_time = now; - tab->gc_scheduled = 0; + + tab->prune_state = 0; + return 1; } /** - * rt_prune_all - prune all routing tables + * rt_prune_loop - prune routing tables + * @tab: routing table to be pruned * - * This function calls rt_prune() for all known routing tables. + * The prune loop scans routing tables and removes routes belonging to + * inactive protocols and also stale network entries. Returns 1 when + * all such routes are pruned. It is a part of the protocol flushing + * loop. */ -void -rt_prune_all(void) +int +rt_prune_loop(void) { rtable *t; + int max_feed = 512; WALK_LIST(t, routing_tables) - rt_prune(t); + if (! rt_prune_step(t, &max_feed)) + return 0; + + return 1; } void diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 24e125a7..38e59886 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -120,8 +120,10 @@ CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC) CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK) CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL) CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY) +CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF) %type <t> opttext +%type <ld> lsadb_args CF_GRAMMAR @@ -217,7 +219,7 @@ ospf_stubnet_item: ospf_vlink: ospf_vlink_start '{' ospf_vlink_opts '}' { ospf_iface_finish(); } - | ospf_vlink_start + | ospf_vlink_start { ospf_iface_finish(); } ; ospf_vlink_opts: @@ -386,7 +388,8 @@ CF_ADDTO(dynamic_attr, OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_TAG); }) CF_ADDTO(dynamic_attr, OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID | EAF_TEMP, T_QUAD, EA_OSPF_ROUTER_ID); }) -CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol]]) +CF_CLI_HELP(SHOW OSPF, ..., [[Show information about OSPF protocol]]); +CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol XXX]]) { ospf_sh(proto_get_named($3, &proto_ospf)); }; CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]]) @@ -411,8 +414,23 @@ CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about reach CF_CLI(SHOW OSPF STATE ALL, optsym opttext, [<name>], [[Show information about all OSPF network state]]) { ospf_sh_state(proto_get_named($5, &proto_ospf), 1, 0); }; -CF_CLI(SHOW OSPF LSADB, optsym opttext, [<name>], [[Show content of OSPF LSA database]]) -{ ospf_sh_lsadb(proto_get_named($4, &proto_ospf)); }; +CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]]); +CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [lsid <id>] [self | router <id>] [<proto>], [[Show content of OSPF LSA database]]) +{ ospf_sh_lsadb($4); }; + +lsadb_args: + /* empty */ { + $$ = cfg_allocz(sizeof(struct lsadb_show_data)); + } + | lsadb_args GLOBAL { $$ = $1; $$->scope = LSA_SCOPE_AS; } + | lsadb_args AREA idval { $$ = $1; $$->scope = LSA_SCOPE_AREA; $$->area = $3 } + | lsadb_args LINK { $$ = $1; $$->scope = 1; /* hack, 0 is no filter */ } + | lsadb_args TYPE NUM { $$ = $1; $$->type = $3; } + | lsadb_args LSID idval { $$ = $1; $$->lsid = $3; } + | lsadb_args SELF { $$ = $1; $$->router = SH_ROUTER_SELF; } + | lsadb_args ROUTER idval { $$ = $1; $$->router = $3; } + | lsadb_args SYM { $$ = $1; $$->name = $2; } + ; CF_CODE diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c index 8cd7c8b2..75ecf24c 100644 --- a/proto/ospf/dbdes.c +++ b/proto/ospf/dbdes.c @@ -96,7 +96,7 @@ ospf_dbdes_send(struct ospf_neighbor *n, int next) pkt = ospf_tx_buffer(ifa); op = &pkt->ospf_packet; ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); - pkt->iface_mtu = htons(ifa->iface->mtu); + pkt->iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : htons(ifa->iface->mtu); pkt->options = hton_opt(oa->options); pkt->imms = n->myimms; pkt->ddseq = htonl(n->dds); @@ -119,7 +119,7 @@ ospf_dbdes_send(struct ospf_neighbor *n, int next) op = (struct ospf_packet *) pkt; ospf_pkt_fill_hdr(ifa, pkt, DBDES_P); - pkt->iface_mtu = htons(ifa->iface->mtu); + pkt->iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : htons(ifa->iface->mtu); pkt->ddseq = htonl(n->dds); pkt->options = hton_opt(oa->options); @@ -260,6 +260,7 @@ ospf_dbdes_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, struct ospf_dbdes_packet *ps = (void *) ps_i; u32 ps_ddseq = ntohl(ps->ddseq); u32 ps_options = ntoh_opt(ps->options); + u16 ps_iface_mtu = ntohs(ps->iface_mtu); OSPF_PACKET(ospf_dump_dbdes, ps, "DBDES packet received from %I via %s", n->ip, ifa->iface->name); @@ -277,6 +278,12 @@ ospf_dbdes_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa, if (n->state != NEIGHBOR_EXSTART) return; case NEIGHBOR_EXSTART: + + if ((ps_iface_mtu != ifa->iface->mtu) && (ifa->type != OSPF_IT_VLINK) + && (ps_iface_mtu != 0) && (ifa->iface->mtu != 0)) + log(L_WARN "OSPF: MTU mismatch with neighbour %I on interface %s (remote %d, local %d)", + n->ip, ifa->iface->name, ps_iface_mtu, ifa->iface->mtu); + if ((ps->imms.bit.m && ps->imms.bit.ms && ps->imms.bit.i) && (n->rid > po->router_id) && (size == sizeof(struct ospf_dbdes_packet))) { diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index cced7105..405e49df 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -120,6 +120,8 @@ ospf_sk_open(struct ospf_iface *ifa) sk->saddr = ifa->addr->ip; if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_PTP)) { + sk->ttl = 1; /* Hack, this will affect just multicast packets */ + if (sk_setup_multicast(sk) < 0) goto err; @@ -568,6 +570,9 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i { ifa->voa = ospf_find_area(oa->po, ip->voa); ifa->vid = ip->vid; + + ifa->hello_timer = tm_new_set(ifa->pool, hello_timer_hook, ifa, 0, ifa->helloint); + return; /* Don't lock, don't add sockets */ } diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index 538a7303..bcf7bcdd 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -512,7 +512,6 @@ lsa_validate(struct ospf_lsa_header *lsa, void *body) * @lsa: LSA header * @domain: domain of LSA * @body: pointer to LSA body - * * This function ensures installing new LSA into LSA database. Old instance is * replaced. Several actions are taken to detect if new routing table diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 73c06c27..9872faf2 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -1471,8 +1471,9 @@ lsa_compare_for_lsadb(const void *p1, const void *p2) } void -ospf_sh_lsadb(struct proto *p) +ospf_sh_lsadb(struct lsadb_show_data *ld) { + struct proto *p = proto_get_named(ld->name, &proto_ospf); struct proto_ospf *po = (struct proto_ospf *) p; int num = po->gr->hash_entries; unsigned int i, j; @@ -1486,6 +1487,9 @@ ospf_sh_lsadb(struct proto *p) return; } + if (ld->router == SH_ROUTER_SELF) + ld->router = po->router_id; + struct top_hash_entry *hea[num]; struct top_hash_entry *he; @@ -1502,6 +1506,22 @@ ospf_sh_lsadb(struct proto *p) { struct ospf_lsa_header *lsa = &(hea[i]->lsa); int dscope = LSA_SCOPE(lsa); + + if (ld->scope && (dscope != (ld->scope & 0xf000))) + continue; + + if ((ld->scope == LSA_SCOPE_AREA) && (hea[i]->domain != ld->area)) + continue; + + /* Ignore high nibble */ + if (ld->type && ((lsa->type & 0x0fff) != (ld->type & 0x0fff))) + continue; + + if (ld->lsid && (lsa->id != ld->lsid)) + continue; + + if (ld->router && (lsa->rt != ld->router)) + continue; if ((dscope != last_dscope) || (hea[i]->domain != last_domain)) { diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index d6961519..96da9aa7 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -846,7 +846,19 @@ void ospf_sh_neigh(struct proto *p, char *iff); void ospf_sh(struct proto *p); void ospf_sh_iface(struct proto *p, char *iff); void ospf_sh_state(struct proto *p, int verbose, int reachable); -void ospf_sh_lsadb(struct proto *p); + +#define SH_ROUTER_SELF 0xffffffff + +struct lsadb_show_data { + struct symbol *name; /* Protocol to request data from */ + u16 type; /* LSA Type, 0 -> all */ + u16 scope; /* Scope, 0 -> all, hack to handle link scope as 1 */ + u32 area; /* Specified for area scope */ + u32 lsid; /* LSA ID, 0 -> all */ + u32 router; /* Advertising router, 0 -> all */ +}; + +void ospf_sh_lsadb(struct lsadb_show_data *ld); #define EA_OSPF_METRIC1 EA_CODE(EAP_OSPF, 0) diff --git a/proto/rip/rip.c b/proto/rip/rip.c index f0a41347..b41c3f8d 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -146,7 +146,11 @@ rip_tx( sock *s ) DBG( "Preparing packet to send: " ); packet->heading.command = RIPCMD_RESPONSE; +#ifndef IPV6 packet->heading.version = RIP_V2; +#else + packet->heading.version = RIP_NG; +#endif packet->heading.unused = 0; i = !!P_CF->authtype; @@ -281,7 +285,7 @@ rip_rte_update_if_better(rtable *tab, net *net, struct proto *p, rte *new) * bird core with this route. */ static void -advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme ) +advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme, struct iface *iface ) { rta *a, A; rte *r; @@ -309,7 +313,7 @@ advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme ) /* No need to look if destination looks valid - ie not net 0 or 127 -- core will do for us. */ - neighbor = neigh_find( p, &A.gw, 0 ); + neighbor = neigh_find2( p, &A.gw, iface, 0 ); if (!neighbor) { log( L_REMOTE "%s: %I asked me to route %I/%d using not-neighbor %I.", p->name, A.from, b->network, pxlen, A.gw ); return; @@ -353,7 +357,7 @@ advertise_entry( struct proto *p, struct rip_block *b, ip_addr whotoldme ) * process_block - do some basic check and pass block to advertise_entry */ static void -process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme ) +process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme, struct iface *iface ) { #ifndef IPV6 int metric = ntohl( block->metric ); @@ -380,7 +384,7 @@ process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme ) return; } - advertise_entry( p, block, whotoldme ); + advertise_entry( p, block, whotoldme, iface ); } #define BAD( x ) { log( L_REMOTE "%s: " x, p->name ); return 1; } @@ -389,7 +393,7 @@ process_block( struct proto *p, struct rip_block *block, ip_addr whotoldme ) * rip_process_packet - this is main routine for incoming packets. */ static int -rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port ) +rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr whotoldme, int port, struct iface *iface ) { int i; int authenticated = 0; @@ -406,7 +410,7 @@ rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr if (P_CF->honor == HO_NEVER) BAD( "They asked me to send routing table, but I was told not to do it" ); - if ((P_CF->honor == HO_NEIGHBOR) && (!neigh_find( p, &whotoldme, 0 ))) + if ((P_CF->honor == HO_NEIGHBOR) && (!neigh_find2( p, &whotoldme, iface, 0 ))) BAD( "They asked me to send routing table, but he is not my neighbor" ); rip_sendto( p, whotoldme, port, HEAD(P->interfaces) ); /* no broadcast */ break; @@ -416,7 +420,7 @@ rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr return 1; } - if (!(neighbor = neigh_find( p, &whotoldme, 0 )) || neighbor->scope == SCOPE_HOST) { + if (!(neighbor = neigh_find2( p, &whotoldme, iface, 0 )) || neighbor->scope == SCOPE_HOST) { log( L_REMOTE "%s: %I send me routing info but he is not my neighbor", p->name, whotoldme ); return 0; } @@ -443,7 +447,7 @@ rip_process_packet( struct proto *p, struct rip_packet *packet, int num, ip_addr if (packet->heading.version == RIP_V1) /* FIXME (nonurgent): switch to disable this? */ block->netmask = ipa_class_mask(block->network); #endif - process_block( p, block, whotoldme ); + process_block( p, block, whotoldme, iface ); } break; case RIPCMD_TRACEON: @@ -463,12 +467,20 @@ rip_rx(sock *s, int size) { struct rip_interface *i = s->data; struct proto *p = i->proto; + struct iface *iface = NULL; int num; /* In non-listening mode, just ignore packet */ if (i->mode & IM_NOLISTEN) return 1; +#ifdef IPV6 + if (! i->iface || s->lifindex != i->iface->index) + return 1; + + iface = i->iface; +#endif + CHK_MAGIC; DBG( "RIP: message came: %d bytes from %I via %s\n", size, s->faddr, i->iface ? i->iface->name : "(dummy)" ); size -= sizeof( struct rip_packet_heading ); @@ -477,17 +489,12 @@ rip_rx(sock *s, int size) num = size / sizeof( struct rip_block ); if (num>PACKET_MAX) BAD( "Too many blocks" ); -#ifdef IPV6 - /* Try to absolutize link scope addresses */ - ipa_absolutize(&s->faddr, &i->iface->addr->ip); -#endif - if (ipa_equal(i->iface->addr->ip, s->faddr)) { DBG("My own packet\n"); return 1; } - rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport ); + rip_process_packet( p, (struct rip_packet *) s->rbuf, num, s->faddr, s->fport, iface ); return 1; } @@ -701,6 +708,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ { rif->sock->ttl = 1; rif->sock->tos = IP_PREC_INTERNET_CONTROL; + rif->sock->flags = SKF_LADDR_RX; } if (new) { @@ -712,7 +720,6 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ rif->sock->daddr = ipa_from_u32(0xe0000009); #else rif->sock->daddr = ipa_build(0xff020000, 0, 0, 9); - rif->sock->saddr = new->addr->ip; /* Does not really work on Linux */ #endif } else { rif->sock->daddr = new->addr->brd; @@ -976,7 +983,7 @@ rip_init_config(struct rip_proto_config *c) { init_list(&c->iface_list); c->infinity = 16; - c->port = 520; + c->port = RIP_PORT; c->period = 30; c->garbage_time = 120+180; c->timeout_time = 120; diff --git a/proto/rip/rip.h b/proto/rip/rip.h index 5a6e36d8..896fab64 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -11,8 +11,19 @@ #define EA_RIP_TAG EA_CODE(EAP_RIP, 0) #define EA_RIP_METRIC EA_CODE(EAP_RIP, 1) -#define PACKET_MAX 25 -#define PACKET_MD5_MAX 18 /* FIXME */ +#define PACKET_MAX 25 +#define PACKET_MD5_MAX 18 /* FIXME */ + + +#define RIP_V1 1 +#define RIP_V2 2 +#define RIP_NG 1 /* A new version numbering */ + +#ifndef IPV6 +#define RIP_PORT 520 /* RIP for IPv4 */ +#else +#define RIP_PORT 521 /* RIPng */ +#endif struct rip_connection { node n; @@ -37,8 +48,9 @@ struct rip_packet_heading { /* 4 bytes */ #define RIPCMD_TRACEOFF 4 /* turn it off */ #define RIPCMD_MAX 5 u8 version; -#define RIP_V1 1 -#define RIP_V2 2 +#define RIP_V1 1 +#define RIP_V2 2 +#define RIP_NG 1 /* this is verion 1 of RIPng */ u16 unused; }; diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 4ee5495f..9ca36d83 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -189,7 +189,8 @@ krt_sock_send(int cmd, rte *e) } void -krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old) +krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old, + struct ea_list *eattrs UNUSED) { int err = 0; @@ -255,10 +256,11 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) ip_addr idst, igate, imask; void *body = (char *)msg->buf; int new = (msg->rtm.rtm_type == RTM_ADD); - int src; char *errmsg = "KRT: Invalid route received"; int flags = msg->rtm.rtm_flags; int addrs = msg->rtm.rtm_addrs; + int src; + byte src2; if (!(flags & RTF_UP) && scan) SKIP("not up in scan\n"); @@ -302,12 +304,17 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) u32 self_mask = RTF_PROTO1; u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY; + src2 = (flags & RTF_STATIC) ? 1 : 0; + src2 |= (flags & RTF_PROTO1) ? 2 : 0; + #ifdef RTF_PROTO2 alien_mask |= RTF_PROTO2; + src2 |= (flags & RTF_PROTO2) ? 4 : 0; #endif #ifdef RTF_PROTO3 alien_mask |= RTF_PROTO3; + src2 |= (flags & RTF_PROTO3) ? 8 : 0; #endif #ifdef RTF_REJECT @@ -397,9 +404,9 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) e = rte_get_temp(&a); e->net = net; e->u.krt.src = src; + e->u.krt.proto = src2; /* These are probably too Linux-specific */ - e->u.krt.proto = 0; e->u.krt.type = 0; e->u.krt.metric = 0; diff --git a/sysdep/config.h b/sysdep/config.h index 8e8f6d37..7e6fad8b 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -7,7 +7,7 @@ #define _BIRD_CONFIG_H_ /* BIRD version */ -#define BIRD_VERSION "1.3.6" +#define BIRD_VERSION "1.3.7" /* Include parameters determined by configure script */ #include "sysdep/autoconf.h" diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c index 17c369ea..c8eed0f3 100644 --- a/sysdep/linux/netlink/netlink.c +++ b/sysdep/linux/netlink/netlink.c @@ -612,7 +612,7 @@ nh_bufsize(struct mpnh *nh) } static int -nl_send_route(struct krt_proto *p, rte *e, int new) +nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) { eattr *ea; net *net = e->net; @@ -639,10 +639,18 @@ nl_send_route(struct krt_proto *p, rte *e, int new) r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); - if (ea = ea_find(a->eattrs, EA_KRT_PREFSRC)) + u32 metric = 0; + if (new && e->attrs->source == RTS_INHERIT) + metric = e->u.krt.metric; + if (ea = ea_find(eattrs, EA_KRT_METRIC)) + metric = ea->u.data; + if (metric != 0) + nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric); + + if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); - if (ea = ea_find(a->eattrs, EA_KRT_REALM)) + if (ea = ea_find(eattrs, EA_KRT_REALM)) nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data); switch (a->dest) @@ -683,15 +691,22 @@ nl_send_route(struct krt_proto *p, rte *e, int new) } void -krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old) +krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs) { int err = 0; + /* + * NULL for eattr of the old route is a little hack, but we don't + * get proper eattrs for old in rt_notify() anyway. NULL means no + * extended route attributes and therefore matches if the kernel + * route has any of them. + */ + if (old) - nl_send_route(p, old, 0); + nl_send_route(p, old, NULL, 0); if (new) - err = nl_send_route(p, new, 1); + err = nl_send_route(p, new, eattrs, 1); if (err < 0) n->n.flags |= KRF_SYNC_ERROR; @@ -886,7 +901,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->u.krt.type = i->rtm_type; if (a[RTA_PRIORITY]) - memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); + memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); else e->u.krt.metric = 0; diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index 18e1e52d..c0141f57 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -17,7 +17,7 @@ CF_DEFINES CF_DECLS -CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES) +CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, KRT_SOURCE, KRT_METRIC) CF_GRAMMAR @@ -90,6 +90,9 @@ kif_item: } ; +CF_ADDTO(dynamic_attr, KRT_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_SOURCE); }) +CF_ADDTO(dynamic_attr, KRT_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_METRIC); }) + CF_CODE CF_END diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 0fb8c4f9..de97a092 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -46,6 +46,7 @@ #include "nest/iface.h" #include "nest/route.h" #include "nest/protocol.h" +#include "filter/filter.h" #include "lib/timer.h" #include "conf/conf.h" #include "lib/string.h" @@ -53,18 +54,18 @@ #include "unix.h" #include "krt.h" -static int krt_uptodate(rte *k, rte *e); - /* * Global resources */ pool *krt_pool; +static linpool *krt_filter_lp; void krt_io_init(void) { krt_pool = rp_new(&root_pool, "Kernel Syncer"); + krt_filter_lp = lp_new(krt_pool, 4080); krt_if_io_init(); } @@ -278,12 +279,30 @@ krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg) static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored; +/* + * krt_same_key() specifies what (aside from the net) is the key in + * kernel routing tables. It should be OS-dependent, this is for + * Linux. It is important for asynchronous alien updates, because a + * positive update is implicitly a negative one for any old route with + * the same key. + */ + static inline int krt_same_key(rte *a, rte *b) { - return a->u.krt.proto == b->u.krt.proto && - a->u.krt.metric == b->u.krt.metric && - a->u.krt.type == b->u.krt.type; + return a->u.krt.metric == b->u.krt.metric; +} + +static inline int +krt_uptodate(rte *a, rte *b) +{ + if (a->attrs != b->attrs) + return 0; + + if (a->u.krt.proto != b->u.krt.proto) + return 0; + + return 1; } static void @@ -308,6 +327,7 @@ krt_learn_announce_delete(struct krt_proto *p, net *n) rte_update(p->p.table, n, &p->p, &p->p, NULL); } +/* Called when alien route is discovered during scan */ static void krt_learn_scan(struct krt_proto *p, rte *e) { @@ -315,7 +335,7 @@ krt_learn_scan(struct krt_proto *p, rte *e) net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); rte *m, **mm; - e->attrs->source = RTS_INHERIT; + e->attrs = rta_lookup(e->attrs); for(mm=&n->routes; m = *mm; mm=&m->next) if (krt_same_key(m, e)) @@ -340,7 +360,6 @@ krt_learn_scan(struct krt_proto *p, rte *e) krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created"); if (!m) { - e->attrs = rta_lookup(e->attrs); e->next = n->routes; n->routes = e; e->u.krt.seen = 1; @@ -416,7 +435,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new) net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); rte *g, **gg, *best, **bestp, *old_best; - e->attrs->source = RTS_INHERIT; + e->attrs = rta_lookup(e->attrs); old_best = n->routes; for(gg=&n->routes; g = *gg; gg = &g->next) @@ -438,7 +457,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new) } else krt_trace_in(p, e, "[alien async] created"); - e->attrs = rta_lookup(e->attrs); + e->next = n->routes; n->routes = e; } @@ -538,7 +557,8 @@ krt_flush_routes(struct krt_proto *p) if ((n->n.flags & KRF_INSTALLED) && a->source != RTS_DEVICE && a->source != RTS_INHERIT) { - krt_set_notify(p, e->net, NULL, e); + /* FIXME: this does not work if gw is changed in export filter */ + krt_set_notify(p, e->net, NULL, e, NULL); n->n.flags &= ~KRF_INSTALLED; } } @@ -547,7 +567,7 @@ krt_flush_routes(struct krt_proto *p) } static int -krt_uptodate(rte *k, rte *e) +krt_same_dest(rte *k, rte *e) { rta *ka = k->attrs, *ea = e->attrs; @@ -559,6 +579,8 @@ krt_uptodate(rte *k, rte *e) return ipa_equal(ka->gw, ea->gw); case RTD_DEVICE: return !strcmp(ka->iface->name, ea->iface->name); + case RTD_MULTIPATH: + return mpnh_same(ka->nexthops, ea->nexthops); default: return 1; } @@ -611,10 +633,12 @@ krt_got_route(struct krt_proto *p, rte *e) old = net->routes; if ((net->n.flags & KRF_INSTALLED) && old) { - if (krt_uptodate(e, old)) - verdict = KRF_SEEN; - else + /* There may be changes in route attributes, we ignore that. + Also, this does not work well if gw is changed in export filter */ + if ((net->n.flags & KRF_SYNC_ERROR) || ! krt_same_dest(e, old)) verdict = KRF_UPDATE; + else + verdict = KRF_SEEN; } else verdict = KRF_DELETE; @@ -624,7 +648,7 @@ krt_got_route(struct krt_proto *p, rte *e) net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict; if (verdict == KRF_UPDATE || verdict == KRF_DELETE) { - /* Get a cached copy of attributes and link the route */ + /* Get a cached copy of attributes and temporarily link the route */ rta *a = e->attrs; a->source = RTS_DUMMY; e->attrs = rta_lookup(a); @@ -635,6 +659,25 @@ krt_got_route(struct krt_proto *p, rte *e) rte_free(e); } +static inline int +krt_export_rte(struct krt_proto *p, rte **new, ea_list **tmpa) +{ + struct filter *filter = p->p.main_ahook->out_filter; + + if (! *new) + return 0; + + if (filter == FILTER_REJECT) + return 0; + + if (filter == FILTER_ACCEPT) + return 1; + + struct proto *src = (*new)->attrs->proto; + *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(*new, krt_filter_lp) : NULL; + return f_run(filter, new, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) <= F_ACCEPT; +} + static void krt_prune(struct krt_proto *p) { @@ -645,16 +688,28 @@ krt_prune(struct krt_proto *p) { net *n = (net *) f; int verdict = f->flags & KRF_VERDICT_MASK; - rte *new, *old; + rte *new, *new0, *old; + ea_list *tmpa = NULL; - if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE) + if (verdict == KRF_UPDATE || verdict == KRF_DELETE) { + /* Get a dummy route from krt_got_route() */ old = n->routes; n->routes = old->next; } else old = NULL; - new = n->routes; + + new = new0 = n->routes; + if (verdict == KRF_CREATE || verdict == KRF_UPDATE) + { + /* We have to run export filter to get proper 'new' route */ + if (! krt_export_rte(p, &new, &tmpa)) + { + /* Route rejected, should not happen (KRF_INSTALLED) but to be sure .. */ + verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; + } + } switch (verdict) { @@ -662,7 +717,7 @@ krt_prune(struct krt_proto *p) if (new && (f->flags & KRF_INSTALLED)) { krt_trace_in(p, new, "reinstalling"); - krt_set_notify(p, n, new, NULL); + krt_set_notify(p, n, new, NULL, tmpa); } break; case KRF_SEEN: @@ -671,17 +726,21 @@ krt_prune(struct krt_proto *p) break; case KRF_UPDATE: krt_trace_in(p, new, "updating"); - krt_set_notify(p, n, new, old); + krt_set_notify(p, n, new, old, tmpa); break; case KRF_DELETE: krt_trace_in(p, old, "deleting"); - krt_set_notify(p, n, NULL, old); + krt_set_notify(p, n, NULL, old, NULL); break; default: bug("krt_prune: invalid route status"); } + if (old) rte_free(old); + if (new != new0) + rte_free(new); + lp_flush(krt_filter_lp); f->flags &= ~KRF_VERDICT_MASK; } FIB_WALK_END; @@ -707,7 +766,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new) if (new) { krt_trace_in(p, e, "[redirect] deleting"); - krt_set_notify(p, net, NULL, e); + krt_set_notify(p, net, NULL, e, NULL); } /* If !new, it is probably echo of our deletion */ break; @@ -781,7 +840,7 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * static void krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, - rte *new, rte *old, struct ea_list *attrs UNUSED) + rte *new, rte *old, struct ea_list *eattrs) { struct krt_proto *p = (struct krt_proto *) P; @@ -793,8 +852,8 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, net->n.flags |= KRF_INSTALLED; else net->n.flags &= ~KRF_INSTALLED; - if (p->initialized) /* Before first scan we don't touch the routes */ - krt_set_notify(p, net, new, old); + if (p->initialized) /* Before first scan we don't touch the routes */ + krt_set_notify(p, net, new, old, eattrs); } /* @@ -907,14 +966,53 @@ krt_shutdown(struct proto *P) return PS_DOWN; } +static struct ea_list * +krt_make_tmp_attrs(rte *rt, struct linpool *pool) +{ + struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr)); + + l->next = NULL; + l->flags = EALF_SORTED; + l->count = 2; + + l->attrs[0].id = EA_KRT_SOURCE; + l->attrs[0].flags = 0; + l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP; + l->attrs[0].u.data = rt->u.krt.proto; + + l->attrs[1].id = EA_KRT_METRIC; + l->attrs[1].flags = 0; + l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP; + l->attrs[1].u.data = rt->u.krt.metric; + + return l; +} + +static void +krt_store_tmp_attrs(rte *rt, struct ea_list *attrs) +{ + /* EA_KRT_SOURCE is read-only */ + rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0); +} + +static int +krt_rte_same(rte *a, rte *b) +{ + /* src is always KRT_SRC_ALIEN and type is irrelevant */ + return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric); +} + static struct proto * krt_init(struct proto_config *c) { struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); p->p.accept_ra_types = RA_OPTIMAL; + p->p.make_tmp_attrs = krt_make_tmp_attrs; + p->p.store_tmp_attrs = krt_store_tmp_attrs; p->p.import_control = krt_import_control; p->p.rt_notify = krt_notify; + p->p.rte_same = krt_rte_same; return &p->p; } @@ -952,12 +1050,22 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) { switch (a->id) { + case EA_KRT_SOURCE: + bsprintf(buf, "source"); + return GA_NAME; + + case EA_KRT_METRIC: + bsprintf(buf, "metric"); + return GA_NAME; + case EA_KRT_PREFSRC: bsprintf(buf, "prefsrc"); return GA_NAME; + case EA_KRT_REALM: bsprintf(buf, "realm"); return GA_NAME; + default: return GA_UNKNOWN; } diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index b0c4dc5e..19b69e49 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -28,8 +28,10 @@ struct kif_proto; #define KRF_DELETE 3 /* Should be deleted */ #define KRF_IGNORE 4 /* To be ignored */ -#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0) -#define EA_KRT_REALM EA_CODE(EAP_KRT, 1) +#define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0) +#define EA_KRT_METRIC EA_CODE(EAP_KRT, 1) +#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 2) +#define EA_KRT_REALM EA_CODE(EAP_KRT, 3) /* Whenever we recognize our own routes, we allow learing of foreign routes */ @@ -130,7 +132,7 @@ void krt_set_start(struct krt_proto *, int); void krt_set_shutdown(struct krt_proto *, int); int krt_capable(rte *e); -void krt_set_notify(struct krt_proto *x, net *net, rte *new, rte *old); +void krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs); /* krt-iface.c */ diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index d6176841..9219da9b 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -170,7 +170,10 @@ cf_open(char *filename) int ret; if (*filename != '/') { - snprintf(full_name, sizeof(full_name), "%s/%s", dirname(config_name), filename); + char dir[BIRD_FNAME_MAX]; + strncpy(dir, config_name, sizeof(dir)); + dir[sizeof(dir)-1] = 0; + snprintf(full_name, sizeof(full_name), "%s/%s", dirname(dir), filename); full_name[sizeof(full_name)-1] = 0; cur = full_name; } @@ -188,7 +191,7 @@ sysdep_preconfig(struct config *c) init_list(&c->logfiles); #ifdef PATH_IPROUTE_DIR - // read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256); + read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256); read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256); read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256); read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256); @@ -643,6 +646,7 @@ main(int argc, char **argv) io_init(); rt_init(); if_init(); + roa_init(); uid_t use_uid = get_uid(use_user); gid_t use_gid = get_gid(use_group); |