diff options
91 files changed, 4566 insertions, 4037 deletions
diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 5a2a4d6b..ccf5826a 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -123,27 +123,15 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*; } {DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+ { - ip4_addr a; - if (!ip4_pton(yytext, &a)) + if (!ip4_pton(yytext, &cf_lval.ip4)) cf_error("Invalid IPv4 address %s", yytext); - -#ifdef IPV6 - cf_lval.i32 = ip4_to_u32(a); - return RTRID; -#else - cf_lval.a = ipa_from_ip4(a); - return IPA; -#endif + return IP4; } ({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) { -#ifdef IPV6 - if (ipa_pton(yytext, &cf_lval.a)) - return IPA; - cf_error("Invalid IPv6 address %s", yytext); -#else - cf_error("This is an IPv4 router, therefore IPv6 addresses are not supported"); -#endif + if (!ip6_pton(yytext, &cf_lval.ip6)) + cf_error("Invalid IPv6 address %s", yytext); + return IP6; } 0x{XIGIT}+ { @@ -686,8 +674,6 @@ cf_symbol_class_name(struct symbol *sym) return "filter"; case SYM_TABLE: return "routing table"; - case SYM_ROA: - return "ROA table"; default: return "unknown type"; } diff --git a/conf/conf.c b/conf/conf.c index 825a8e9f..3fd10ad8 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -135,15 +135,16 @@ 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)) cf_error("No protocol is specified in the config file"); -#ifdef IPV6 + + /* if (!c->router_id) - cf_error("Router ID must be configured manually on IPv6 routers"); -#endif + cf_error("Router ID must be configured manually"); + */ + done = 1; cleanup: @@ -266,7 +267,6 @@ 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); @@ -504,7 +504,7 @@ order_shutdown(void) * error in the configuration. */ void -cf_error(char *msg, ...) +cf_error(const char *msg, ...) { char buf[1024]; va_list args; diff --git a/conf/conf.h b/conf/conf.h index 89a2c5b7..8e490c7b 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -20,12 +20,11 @@ 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 */ + struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */ struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */ u32 router_id; /* Our Router ID */ @@ -69,7 +68,7 @@ int config_commit(struct config *, int type, int timeout); int config_confirm(void); int config_undo(void); void config_init(void); -void cf_error(char *msg, ...) NORET; +void cf_error(const char *msg, ...) NORET; void config_add_obstacle(struct config *); void config_del_obstacle(struct config *); void order_shutdown(void); @@ -121,7 +120,6 @@ struct symbol { #define SYM_FUNCTION 3 #define SYM_FILTER 4 #define SYM_TABLE 5 -#define SYM_ROA 6 #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ #define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */ diff --git a/conf/confbase.Y b/conf/confbase.Y index 5f487c1d..22aee770 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -39,9 +39,14 @@ CF_DECLS int i; u32 i32; ip_addr a; + ip4_addr ip4; + ip6_addr ip6; + net_addr net; + net_addr *net_ptr; struct symbol *s; char *t; struct rtable_config *r; + struct channel_config *cc; struct f_inst *x; struct filter *f; struct f_tree *e; @@ -50,15 +55,14 @@ 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; + struct f_prefix px; struct proto_spec ps; + struct channel_limit cl; struct timeformat *tf; } @@ -66,19 +70,20 @@ CF_DECLS %token GEQ LEQ NEQ AND OR %token PO PC %token <i> NUM ENUM -%token <i32> RTRID -%token <a> IPA +%token <ip4> IP4 +%token <ip6> IP6 %token <s> SYM %token <t> TEXT %type <iface> ipa_scope -%type <i> expr bool pxlen +%type <i> expr bool pxlen4 %type <i32> expr_us %type <time> datetime %type <a> ipa -%type <px> prefix prefix_or_ipa -%type <t> text -%type <t> text_or_none +%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa +%type <net_ptr> net_ net_any net_roa4_ net_roa6_ net_roa_ + +%type <t> text opttext %nonassoc PREFIX_DUMMY %left AND OR @@ -146,13 +151,15 @@ bool: | /* Silence means agreement */ { $$ = 1; } ; -/* Addresses, prefixes and netmasks */ + +/* Addresses */ ipa: - IPA + IP4 { $$ = ipa_from_ip4($1); } + | IP6 { $$ = ipa_from_ip6($1); } | SYM { if ($1->class != (SYM_CONSTANT | T_IP)) cf_error("IP address expected"); - $$ = SYM_VAL($1).px.ip; + $$ = SYM_VAL($1).ip; } ; @@ -161,29 +168,110 @@ ipa_scope: | '%' SYM { $$ = if_get_by_name($2->name); } ; -prefix: - ipa pxlen { - if (!ip_is_prefix($1, $2)) cf_error("Invalid prefix"); - $$.addr = $1; $$.len = $2; + +/* Networks - internal */ + +pxlen4: + '/' NUM { + if ($2 < 0 || $2 > IP4_MAX_PREFIX_LENGTH) cf_error("Invalid prefix length %d", $2); + $$ = $2; + } + | ':' IP4 { + $$ = ip4_masklen($2); + if ($$ == 255) cf_error("Invalid netmask %I4", $2); } ; -prefix_or_ipa: - prefix - | ipa { $$.addr = $1; $$.len = BITS_PER_IP_ADDRESS; } +net_ip4_: IP4 pxlen4 +{ + net_fill_ip4(&($$), $1, $2); + if (!net_validate_ip4((net_addr_ip4 *) &($$))) + cf_error("Invalid IPv4 prefix"); +}; + +net_ip6_: IP6 '/' NUM +{ + net_fill_ip6(&($$), $1, $3); + if ($3 < 0 || $3 > IP6_MAX_PREFIX_LENGTH) + cf_error("Invalid prefix length %d", $3); + if (!net_validate_ip6((net_addr_ip6 *) &($$))) + cf_error("Invalid IPv6 prefix"); +}; + +net_roa4_: net_ip4_ MAX NUM AS NUM +{ + $$ = cfg_alloc(sizeof(net_addr_roa4)); + net_fill_roa4($$, ((net_addr_ip4 *)&$1)->prefix, $1.pxlen, $3, $5); + if ($3 < 0 || $3 > IP4_MAX_PREFIX_LENGTH) + cf_error("Invalid max prefix length %d", $3); + if (((net_addr_roa4 *) $$)->max_pxlen < ($$)->pxlen) + cf_error("Maximum prefix length %d must be >= prefix length %d", ((net_addr_roa4 *) $$)->max_pxlen, ($$)->pxlen); +}; + +net_roa6_: net_ip6_ MAX NUM AS NUM +{ + $$ = cfg_alloc(sizeof(net_addr_roa6)); + net_fill_roa6($$, ((net_addr_ip6 *)&$1)->prefix, $1.pxlen, $3, $5); + if ($3 < 0 || $3 > IP6_MAX_PREFIX_LENGTH) + cf_error("Invalid max prefix length %d", $3); + if (((net_addr_roa6 *) $$)->max_pxlen < ($$)->pxlen) + cf_error("Maximum prefix length %d must be >= prefix length %d", ((net_addr_roa6 *) $$)->max_pxlen, ($$)->pxlen); +}; + +net_ip_: net_ip4_ | net_ip6_ ; +net_roa_: net_roa4_ | net_roa6_ ; + +net_: + net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); } + | net_roa_ ; -pxlen: - '/' expr { - if ($2 < 0 || $2 > BITS_PER_IP_ADDRESS) cf_error("Invalid prefix length %d", $2); - $$ = $2; + +/* Networks - regular */ + +net_ip6: + net_ip6_ + | SYM { + if (($1->class != (SYM_CONSTANT | T_NET)) || (SYM_VAL($1).net->type != NET_IP6)) + cf_error("IPv6 network expected"); + $$ = * SYM_VAL($1).net; + } + ; + +net_ip: + net_ip_ + | SYM { + if (($1->class != (SYM_CONSTANT | T_NET)) || !net_is_ip(SYM_VAL($1).net)) + cf_error("IP network expected"); + $$ = * SYM_VAL($1).net; + } + ; + +net_any: + net_ + | SYM { + if ($1->class != (SYM_CONSTANT | T_NET)) + cf_error("Network expected"); + $$ = (net_addr *) SYM_VAL($1).net; /* Avoid const warning */ } - | ':' ipa { - $$ = ipa_masklen($2); - if ($$ < 0) cf_error("Invalid netmask %I", $2); + ; + +net_or_ipa: + net_ip4_ + | net_ip6_ + | IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); } + | IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); } + | SYM { + if ($1->class == (SYM_CONSTANT | T_IP)) + net_fill_ip_host(&($$), SYM_VAL($1).ip); + else if (($1->class == (SYM_CONSTANT | T_NET)) && net_is_ip(SYM_VAL($1).net)) + $$ = * SYM_VAL($1).net; + else + cf_error("IP address or network expected"); } ; + datetime: TEXT { $$ = tm_parse_datetime($1); @@ -200,11 +288,12 @@ text: } ; -text_or_none: - TEXT { $$ = $1; } - | { $$ = NULL; } +opttext: + TEXT + | /* empty */ { $$ = NULL; } ; + CF_CODE CF_END diff --git a/configure.in b/configure.in index b9220a1d..1c2c2fe1 100644 --- a/configure.in +++ b/configure.in @@ -9,9 +9,7 @@ AC_CONFIG_AUX_DIR(tools) AC_ARG_ENABLE(debug, [ --enable-debug enable internal debugging routines (default: disabled)],,enable_debug=no) AC_ARG_ENABLE(memcheck, [ --enable-memcheck check memory allocations when debugging (default: enabled)],,enable_memcheck=yes) AC_ARG_ENABLE(client, [ --enable-client enable building of BIRD client (default: enabled)],,enable_client=yes) -AC_ARG_ENABLE(ipv6, [ --enable-ipv6 enable building of IPv6 version (default: disabled)],,enable_ipv6=no) AC_ARG_ENABLE(pthreads, [ --enable-pthreads enable POSIX threads support (default: detect)],,enable_pthreads=try) -AC_ARG_WITH(suffix, [ --with-suffix=STRING use specified suffix for BIRD files (default: 6 for IPv6 version)],[given_suffix="yes"]) AC_ARG_WITH(sysconfig, [ --with-sysconfig=FILE use specified BIRD system configuration file]) AC_ARG_WITH(protocols, [ --with-protocols=LIST include specified routing protocols (default: all)],,[with_protocols="all"]) AC_ARG_WITH(sysinclude, [ --with-sysinclude=PATH search for system includes on specified place]) @@ -45,26 +43,13 @@ AC_SUBST(exedir) AC_SUBST(srcdir_rel_mf) AC_SUBST(runtimedir) -if test "$enable_ipv6" = yes ; then - ip=ipv6 - SUFFIX=6 - proto_radv=radv -else - ip=ipv4 - SUFFIX="" -fi - -if test "$given_suffix" = yes ; then - SUFFIX="$with_suffix" -fi -AC_SUBST(SUFFIX) if test "$enable_debug" = yes ; then - CONFIG_FILE="bird$SUFFIX.conf" - CONTROL_SOCKET="bird$SUFFIX.ctl" + CONFIG_FILE="bird.conf" + CONTROL_SOCKET="bird.ctl" else - CONFIG_FILE="\$(sysconfdir)/bird$SUFFIX.conf" - CONTROL_SOCKET="$runtimedir/bird$SUFFIX.ctl" + CONFIG_FILE="\$(sysconfdir)/bird.conf" + CONTROL_SOCKET="$runtimedir/bird.ctl" fi AC_SUBST(CONFIG_FILE) AC_SUBST(CONTROL_SOCKET) @@ -147,36 +132,21 @@ if test -n "$with_sysconfig" -a "$with_sysconfig" != no ; then elif test -f sysconfig.h ; then sysdesc=sysconfig else - case "$ip:$host_os" in - ipv6:linux*) sysdesc=linux-v6 + case "$host_os" in + linux*) sysdesc=linux default_iproutedir="/etc/iproute2" ;; - ipv4:linux*) sysdesc=linux - default_iproutedir="/etc/iproute2" + freebsd*) sysdesc=bsd ;; - ipv6:netbsd*) sysdesc=bsd-v6 - CPPFLAGS="$CPPFLAGS -I/usr/pkg/include" - LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib" + kfreebsd*) sysdesc=bsd ;; - ipv4:netbsd*) sysdesc=bsd + netbsd*) sysdesc=bsd CPPFLAGS="$CPPFLAGS -I/usr/pkg/include" LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib" ;; - ipv6:freebsd*) sysdesc=bsd-v6 + openbsd*) sysdesc=bsd ;; - ipv4:freebsd*) sysdesc=bsd - ;; - ipv6:dragonfly*) sysdesc=bsd-v6 - ;; - ipv4:dragonfly*) sysdesc=bsd - ;; - ipv6:kfreebsd*) sysdesc=bsd-v6 - ;; - ipv4:kfreebsd*) sysdesc=bsd - ;; - ipv6:openbsd*) sysdesc=bsd-v6 - ;; - ipv4:openbsd*) sysdesc=bsd + dragonfly*) sysdesc=bsd ;; *) AC_MSG_ERROR([Cannot determine correct system configuration. Please use --with-sysconfig to set it manually.]) ;; @@ -205,7 +175,9 @@ fi AC_SUBST(iproutedir) -all_protocols="$proto_bfd bgp ospf pipe $proto_radv rip static" +# all_protocols="$proto_bfd bgp ospf pipe radv rip static" +all_protocols="$proto_bfd ospf pipe radv rip static" + all_protocols=`echo $all_protocols | sed 's/ /,/g'` if test "$with_protocols" = all ; then @@ -226,7 +198,7 @@ AC_MSG_RESULT(ok) AC_SUBST(protocols) case $sysdesc in - */linux*|*/linux-v6*) + */linux*) AC_CHECK_HEADER(linux/rtnetlink.h,,[AC_MSG_ERROR([Appropriate version of Linux kernel headers not found.])],[ #include <asm/types.h> #include <sys/socket.h> diff --git a/filter/config.Y b/filter/config.Y index b94f5dff..3e70a63e 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -134,7 +134,7 @@ f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt) static inline struct f_inst * f_generate_empty(struct f_inst *dyn) -{ +{ struct f_inst *e = f_new_inst(); e->code = 'E'; @@ -205,7 +205,6 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor"); } -#ifndef IPV6 /* IP->Quad implicit conversion */ else if (tk->code == 'C') { c1 = 1; @@ -217,13 +216,12 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) else if (val->type == T_QUAD) { ipv4_used = 1; key = val->val.i; } - else if (val->type == T_IP) { - ipv4_used = 1; key = ipa_to_u32(val->val.px.ip); + else if ((val->type == T_IP) && ipa_is_ip4(val->val.ip)) { + ipv4_used = 1; key = ipa_to_u32(val->val.ip); } else cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor"); } -#endif if (tv->code == 'c') { if (tv->aux != T_INT) @@ -234,7 +232,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) if (c1 && c2) { u64 ec; - + if (kind == EC_GENERIC) { ec = ec_generic(key, val2); } @@ -253,7 +251,7 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv) NEW_F_VAL; rv = f_new_inst(); rv->code = 'C'; - rv->a1.p = val; + rv->a1.p = val; val->type = T_EC; val->val.ec = ec; } @@ -280,11 +278,11 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX, PREFERENCE, + ROA_CHECK, LEN, DEFINED, ADD, DELETE, CONTAINS, RESET, PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH, - ROA_CHECK, EMPTY, FILTER, WHERE, EVAL) @@ -297,8 +295,9 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %type <i32> pair_atom ec_expr %type <e> pair_item ec_item set_item switch_item set_items switch_items switch_body %type <trie> fprefix_set -%type <v> set_atom switch_atom fprefix fprefix_s fipa -%type <s> decls declsn one_decl function_params +%type <v> set_atom switch_atom fipa +%type <px> fprefix +%type <s> decls declsn one_decl function_params %type <h> bgp_path bgp_path_tail1 bgp_path_tail2 CF_GRAMMAR @@ -323,7 +322,7 @@ type: INT { $$ = T_INT; } | BOOL { $$ = T_BOOL; } | IP { $$ = T_IP; } - | PREFIX { $$ = T_PREFIX; } + | PREFIX { $$ = T_NET; } | PAIR { $$ = T_PAIR; } | QUAD { $$ = T_QUAD; } | EC { $$ = T_EC; } @@ -342,7 +341,7 @@ type: $$ = T_SET; break; - case T_PREFIX: + case T_NET: $$ = T_PREFIX_SET; break; @@ -477,7 +476,8 @@ block: * Complex types, their bison value is struct f_val */ fipa: - IPA %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.px.ip = $1; } + IP4 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); } + | IP6 %prec PREFIX_DUMMY { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); } ; @@ -491,7 +491,6 @@ fipa: set_atom: NUM { $$.type = T_INT; $$.val.i = $1; } - | RTRID { $$.type = T_QUAD; $$.val.i = $1; } | fipa { $$ = $1; } | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } | '(' term ')' { @@ -508,7 +507,6 @@ set_atom: switch_atom: NUM { $$.type = T_INT; $$.val.i = $1; } | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2); } - | RTRID { $$.type = T_QUAD; $$.val.i = $1; } | fipa { $$ = $1; } | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } ; @@ -574,26 +572,20 @@ switch_items: | switch_items ',' switch_item { $$ = f_merge_items($1, $3); } ; -fprefix_s: - IPA '/' NUM %prec '/' { - if (($3 < 0) || ($3 > MAX_PREFIX_LENGTH) || !ip_is_prefix($1, $3)) cf_error("Invalid network prefix: %I/%d.", $1, $3); - $$.type = T_PREFIX; $$.val.px.ip = $1; $$.val.px.len = $3; - } - ; - fprefix: - fprefix_s { $$ = $1; } - | fprefix_s '+' { $$ = $1; $$.val.px.len |= LEN_PLUS; } - | fprefix_s '-' { $$ = $1; $$.val.px.len |= LEN_MINUS; } - | fprefix_s '{' NUM ',' NUM '}' { - if (! ((0 <= $3) && ($3 <= $5) && ($5 <= MAX_PREFIX_LENGTH))) cf_error("Invalid prefix pattern range: {%d, %d}.", $3, $5); - $$ = $1; $$.val.px.len |= LEN_RANGE | ($3 << 16) | ($5 << 8); + net_ip_ { $$.net = $1; $$.lo = $1.pxlen; $$.hi = $1.pxlen; } + | net_ip_ '+' { $$.net = $1; $$.lo = $1.pxlen; $$.hi = net_max_prefix_length[$1.type]; } + | net_ip_ '-' { $$.net = $1; $$.lo = 0; $$.hi = $1.pxlen; } + | net_ip_ '{' NUM ',' NUM '}' { + $$.net = $1; $$.lo = $3; $$.hi = $5; + if ((0 > $3) || ($3 > $5) || ($5 > net_max_prefix_length[$1.type])) + cf_error("Invalid prefix pattern range: {%d, %d}", $3, $5); } ; fprefix_set: - fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_fprefix($$, &($1.val.px)); } - | fprefix_set ',' fprefix { $$ = $1; trie_add_fprefix($$, &($3.val.px)); } + fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); } + | fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.net), $3.lo, $3.hi); } ; switch_body: /* EMPTY */ { $$ = NULL; } @@ -604,7 +596,7 @@ switch_body: /* EMPTY */ { $$ = NULL; } t->data = $4; $$ = f_merge_items($1, $2); } - | switch_body ELSECOL cmds { + | switch_body ELSECOL cmds { struct f_tree *t = f_new_tree(); t->from.type = t->to.type = T_VOID; t->right = t; @@ -644,9 +636,8 @@ constant: | TRUE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 1; } | FALSE { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_BOOL; $$->a2.i = 0; } | TEXT { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_STRING; $$->a2.p = $1; } - | fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } - | fprefix_s {NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } - | RTRID { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_QUAD; $$->a2.i = $1; } + | fipa { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; $$->a1.p = val; *val = $1; } + | net_ { NEW_F_VAL; $$ = f_new_inst(); $$->code = 'C'; val->type = T_NET; val->val.net = $1; $$->a1.p = val; } | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } | '[' fprefix_set ']' { $$ = f_new_inst(); $$->code = 'c'; $$->aux = T_PREFIX_SET; $$->a2.p = $2; } | ENUM { $$ = f_new_inst(); $$->code = 'c'; $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } @@ -709,7 +700,7 @@ symbol: static_attr: FROM { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_FROM; $$->a1.i = 1; } | GW { $$ = f_new_inst(); $$->aux = T_IP; $$->a2.i = SA_GW; $$->a1.i = 1; } - | NET { $$ = f_new_inst(); $$->aux = T_PREFIX; $$->a2.i = SA_NET; } + | NET { $$ = f_new_inst(); $$->aux = T_NET; $$->a2.i = SA_NET; } | PROTO { $$ = f_new_inst(); $$->aux = T_STRING; $$->a2.i = SA_PROTO; } | SOURCE { $$ = f_new_inst(); $$->aux = T_ENUM_RTS; $$->a2.i = SA_SOURCE; } | SCOPE { $$ = f_new_inst(); $$->aux = T_ENUM_SCOPE; $$->a2.i = SA_SCOPE; $$->a1.i = 1; } @@ -770,8 +761,8 @@ 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); } + | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); } + | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); } /* | term '.' LEN { $$->code = P('P','l'); } */ diff --git a/filter/f-util.c b/filter/f-util.c index def2b248..661941ec 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -54,9 +54,8 @@ 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) +f_generate_roa_check(struct rtable_config *table, 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'); @@ -65,9 +64,9 @@ f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *a 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; + if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) + cf_error("%s is not a ROA table", table->name); + ret->rtc = table; return &ret->i; } diff --git a/filter/filter.c b/filter/filter.c index eddf4228..cc1bb3dc 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -39,6 +39,8 @@ #include "lib/socket.h" #include "lib/string.h" #include "lib/unaligned.h" +#include "lib/net.h" +#include "lib/ip.h" #include "nest/route.h" #include "nest/protocol.h" #include "nest/iface.h" @@ -90,17 +92,8 @@ pm_format(struct f_path_mask *p, buffer *buf) buffer_puts(buf, "=]"); } -static inline int -uint_cmp(uint i1, uint i2) -{ - return (int)(i1 > i2) - (int)(i1 < i2); -} - -static inline int -u64_cmp(u64 i1, u64 i2) -{ - return (int)(i1 > i2) - (int)(i1 < i2); -} +static inline int val_is_ip4(const struct f_val v) +{ return (v.type == T_IP) && ipa_is_ip4(v.val.ip); } /** * val_compare - compare two values @@ -114,21 +107,17 @@ u64_cmp(u64 i1, u64 i2) int val_compare(struct f_val v1, struct f_val v2) { - int rc; - if (v1.type != v2.type) { if (v1.type == T_VOID) /* Hack for else */ return -1; if (v2.type == T_VOID) return 1; -#ifndef IPV6 /* IP->Quad implicit conversion */ - if ((v1.type == T_QUAD) && (v2.type == T_IP)) - return uint_cmp(v1.val.i, ipa_to_u32(v2.val.px.ip)); - if ((v1.type == T_IP) && (v2.type == T_QUAD)) - return uint_cmp(ipa_to_u32(v1.val.px.ip), v2.val.i); -#endif + if ((v1.type == T_QUAD) && val_is_ip4(v2)) + return uint_cmp(v1.val.i, ipa_to_u32(v2.val.ip)); + if (val_is_ip4(v1) && (v2.type == T_QUAD)) + return uint_cmp(ipa_to_u32(v1.val.ip), v2.val.i); debug( "Types do not match in val_compare\n" ); return CMP_ERROR; @@ -146,11 +135,9 @@ val_compare(struct f_val v1, struct f_val v2) case T_EC: return u64_cmp(v1.val.ec, v2.val.ec); case T_IP: - return ipa_compare(v1.val.px.ip, v2.val.px.ip); - case T_PREFIX: - if (rc = ipa_compare(v1.val.px.ip, v2.val.px.ip)) - return rc; - return uint_cmp(v1.val.px.len, v2.val.px.len); + return ipa_compare(v1.val.ip, v2.val.ip); + case T_NET: + return net_compare(v1.val.net, v2.val.net); case T_STRING: return strcmp(v1.val.s, v2.val.s); default: @@ -209,38 +196,26 @@ val_same(struct f_val v1, struct f_val v2) } } -void -fprefix_get_bounds(struct f_prefix *px, int *l, int *h) -{ - *l = *h = px->len & LEN_MASK; - - if (px->len & LEN_MINUS) - *l = 0; - - else if (px->len & LEN_PLUS) - *h = MAX_PREFIX_LENGTH; - - else if (px->len & LEN_RANGE) - { - *l = 0xff & (px->len >> 16); - *h = 0xff & (px->len >> 8); - } -} - static int clist_set_type(struct f_tree *set, struct f_val *v) { - switch (set->from.type) { + switch (set->from.type) + { case T_PAIR: v->type = T_PAIR; return 1; + case T_QUAD: -#ifndef IPV6 - case T_IP: -#endif v->type = T_QUAD; return 1; - break; + + case T_IP: + if (val_is_ip4(set->from) && val_is_ip4(set->to)) + { + v->type = T_QUAD; + return 1; + } + /* Fall through */ default: v->type = T_VOID; return 0; @@ -383,11 +358,10 @@ val_in_range(struct f_val v1, struct f_val v2) 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 + /* IP->Quad implicit conversion */ - if ((v1.type == T_IP) && (v2.type == T_CLIST)) - return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.px.ip)); -#endif + if (val_is_ip4(v1) && (v2.type == T_CLIST)) + return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip)); if ((v1.type == T_EC) && (v2.type == T_ECLIST)) return ec_set_contains(v2.val.ad, v1.val.ec); @@ -395,21 +369,21 @@ val_in_range(struct f_val v1, struct f_val v2) if ((v1.type == T_STRING) && (v2.type == T_STRING)) return patmatch(v2.val.s, v1.val.s); - if ((v1.type == T_IP) && (v2.type == T_PREFIX)) - return ipa_in_net(v1.val.px.ip, v2.val.px.ip, v2.val.px.len); + if ((v1.type == T_IP) && (v2.type == T_NET)) + return ipa_in_netX(v1.val.ip, v2.val.net); - if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX)) - return net_in_net(v1.val.px.ip, v1.val.px.len, v2.val.px.ip, v2.val.px.len); + if ((v1.type == T_NET) && (v2.type == T_NET)) + return net_in_netX(v1.val.net, v2.val.net); - if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX_SET)) - return trie_match_fprefix(v2.val.ti, &v1.val.px); + if ((v1.type == T_NET) && (v2.type == T_PREFIX_SET)) + return trie_match_net(v2.val.ti, v1.val.net); if (v2.type != T_SET) return CMP_ERROR; /* With integrated Quad<->IP implicit conversion */ if ((v1.type == v2.val.t->from.type) || - ((IP_VERSION == 4) && (v1.type == T_QUAD) && (v2.val.t->from.type == T_IP))) + ((v1.type == T_QUAD) && val_is_ip4(v2.val.t->from) && val_is_ip4(v2.val.t->to))) return !!find_tree(v2.val.t, v1); if (v1.type == T_CLIST) @@ -437,8 +411,8 @@ val_format(struct f_val v, buffer *buf) case T_BOOL: buffer_puts(buf, v.val.i ? "TRUE" : "FALSE"); return; case T_INT: buffer_print(buf, "%u", v.val.i); return; case T_STRING: buffer_print(buf, "%s", v.val.s); return; - case T_IP: buffer_print(buf, "%I", v.val.px.ip); return; - case T_PREFIX: buffer_print(buf, "%I/%d", v.val.px.ip, v.val.px.len); return; + case T_IP: buffer_print(buf, "%I", v.val.ip); return; + case T_NET: buffer_print(buf, "%N", v.val.net); return; case T_PAIR: buffer_print(buf, "(%u,%u)", v.val.i >> 16, v.val.i & 0xffff); return; case T_QUAD: buffer_print(buf, "%R", v.val.i); return; case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return; @@ -628,12 +602,10 @@ interpret(struct f_inst *what) else if (v1.type == T_QUAD) { ipv4_used = 1; key = v1.val.i; } -#ifndef IPV6 /* IP->Quad implicit conversion */ - else if (v1.type == T_IP) { - ipv4_used = 1; key = ipa_to_u32(v1.val.px.ip); + else if (val_is_ip4(v1)) { + ipv4_used = 1; key = ipa_to_u32(v1.val.ip); } -#endif else runtime("Can't operate with key of non-integer/IPv4 type in EC constructor"); @@ -712,15 +684,15 @@ interpret(struct f_inst *what) ARG(v2, a2.p); sym = what->a1.p; vp = sym->def; - if ((sym->class != (SYM_VARIABLE | v2.type)) && (v2.type != T_VOID)) { -#ifndef IPV6 + if ((sym->class != (SYM_VARIABLE | v2.type)) && (v2.type != T_VOID)) + { /* IP->Quad implicit conversion */ - if ((sym->class == (SYM_VARIABLE | T_QUAD)) && (v2.type == T_IP)) { + if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(v2)) + { vp->type = T_QUAD; - vp->val.i = ipa_to_u32(v2.val.px.ip); + vp->val.i = ipa_to_u32(v2.val.ip); break; } -#endif runtime( "Assigning to variable of incompatible type" ); } *vp = v2; @@ -790,10 +762,9 @@ interpret(struct f_inst *what) switch (what->a2.i) { - case SA_FROM: res.val.px.ip = rta->from; break; - case SA_GW: res.val.px.ip = rta->gw; break; - case SA_NET: res.val.px.ip = (*f_rte)->net->n.prefix; - res.val.px.len = (*f_rte)->net->n.pxlen; break; + case SA_FROM: res.val.ip = rta->from; break; + case SA_GW: res.val.ip = rta->gw; break; + case SA_NET: res.val.net = (*f_rte)->net->n.addr; break; case SA_PROTO: res.val.s = rta->src->proto->name; break; case SA_SOURCE: res.val.i = rta->source; break; case SA_SCOPE: res.val.i = rta->scope; break; @@ -820,12 +791,12 @@ interpret(struct f_inst *what) switch (what->a2.i) { case SA_FROM: - rta->from = v1.val.px.ip; + rta->from = v1.val.ip; break; case SA_GW: { - ip_addr ip = v1.val.px.ip; + ip_addr ip = v1.val.ip; neighbor *n = neigh_find(rta->src->proto, &ip, 0); if (!n || (n->scope == SCOPE_HOST)) runtime( "Invalid gw address" ); @@ -908,7 +879,7 @@ interpret(struct f_inst *what) case EAF_TYPE_IP_ADDRESS: res.type = T_IP; struct adata * ad = e->u.ptr; - res.val.px.ip = * (ip_addr *) ad->data; + res.val.ip = * (ip_addr *) ad->data; break; case EAF_TYPE_AS_PATH: res.type = T_PATH; @@ -956,13 +927,11 @@ interpret(struct f_inst *what) break; case EAF_TYPE_ROUTER_ID: -#ifndef IPV6 /* IP->Quad implicit conversion */ - if (v1.type == T_IP) { - l->attrs[0].u.data = ipa_to_u32(v1.val.px.ip); + if (val_is_ip4(v1)) { + l->attrs[0].u.data = ipa_to_u32(v1.val.ip); break; } -#endif /* T_INT for backward compatibility */ if ((v1.type != T_QUAD) && (v1.type != T_INT)) runtime( "Setting quad attribute to non-quad value" ); @@ -978,7 +947,7 @@ interpret(struct f_inst *what) int len = sizeof(ip_addr); struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + len); ad->length = len; - (* (ip_addr *) ad->data) = v1.val.px.ip; + (* (ip_addr *) ad->data) = v1.val.ip; l->attrs[0].u.ptr = ad; break; case EAF_TYPE_AS_PATH: @@ -1053,7 +1022,7 @@ interpret(struct f_inst *what) ONEARG; res.type = T_INT; switch(v1.type) { - case T_PREFIX: res.val.i = v1.val.px.len; break; + case T_NET: res.val.i = net_pxlen(v1.val.net); break; case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break; case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break; @@ -1062,14 +1031,10 @@ interpret(struct f_inst *what) break; case P('c','p'): /* Convert prefix to ... */ ONEARG; - if (v1.type != T_PREFIX) + if (v1.type != T_NET) runtime( "Prefix expected" ); - res.type = what->aux; - switch(res.type) { - /* case T_INT: res.val.i = v1.val.px.len; break; Not needed any more */ - case T_IP: res.val.px.ip = v1.val.px.ip; break; - default: bug( "Unknown prefix to conversion" ); - } + res.type = T_IP; + res.val.ip = net_prefix(v1.val.net); break; case P('a','f'): /* Get first ASN from AS PATH */ ONEARG; @@ -1140,11 +1105,11 @@ interpret(struct f_inst *what) runtime( "Integer expected"); if (v1.type != T_IP) runtime( "You can mask only IP addresses" ); - { - ip_addr mask = ipa_mkmask(v2.val.i); - res.type = T_IP; - res.val.px.ip = ipa_and(mask, v1.val.px.ip); - } + + res.type = T_IP; + res.val.ip = ipa_is_ip4(v1.val.ip) ? + ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) : + ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))); break; case 'E': /* Create empty attribute */ @@ -1200,11 +1165,9 @@ interpret(struct f_inst *what) if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) n = v2.val.i; -#ifndef IPV6 /* IP->Quad implicit conversion */ - else if (v2.type == T_IP) - n = ipa_to_u32(v2.val.px.ip); -#endif + else if (val_is_ip4(v2)) + n = ipa_to_u32(v2.val.ip); else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) arg_set = 1; else if (v2.type == T_CLIST) @@ -1288,11 +1251,12 @@ 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)) + if ((v1.type != T_NET) || (v2.type != T_INT)) runtime("Invalid argument to roa_check()"); as = v2.val.i; @@ -1300,8 +1264,7 @@ interpret(struct f_inst *what) else { ACCESS_RTE; - v1.val.px.ip = (*f_rte)->net->n.prefix; - v1.val.px.len = (*f_rte)->net->n.pxlen; + v1.val.net = (*f_rte)->net->n.addr; /* 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 */ @@ -1313,14 +1276,16 @@ interpret(struct f_inst *what) as_path_get_last(e->u.ptr, &as); } - struct roa_table_config *rtc = ((struct f_inst_roa_check *) what)->rtc; - if (!rtc->table) + struct rtable *table = ((struct f_inst_roa_check *) what)->rtc->table; + if (!table || table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) 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); + res.val.i = net_roa_check(table, v1.val.net, as); + break; + default: bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); } @@ -1447,6 +1412,7 @@ 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; +#if 0 case P('R','C'): TWOARGS; /* Does not really make sense - ROA check resuls may change anyway */ @@ -1454,6 +1420,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) ((struct f_inst_roa_check *) f2)->rtc->name)) return 0; break; +#endif default: bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); } diff --git a/filter/filter.h b/filter/filter.h index e59c8226..af490121 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -35,17 +35,12 @@ struct f_inst { /* Instruction */ /* 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 rtable_config *rtc; }; struct f_prefix { - ip_addr ip; - int len; -#define LEN_MASK 0xff -#define LEN_PLUS 0x1000000 -#define LEN_MINUS 0x2000000 -#define LEN_RANGE 0x4000000 - /* If range then prefix must be in range (len >> 16 & 0xff, len >> 8 & 0xff) */ + net_addr net; + u8 lo, hi; }; struct f_val { @@ -53,8 +48,8 @@ struct f_val { union { uint i; u64 ec; - /* ip_addr ip; Folded into prefix */ - struct f_prefix px; + ip_addr ip; + const net_addr *net; char *s; struct f_tree *t; struct f_trie *ti; @@ -72,7 +67,7 @@ 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_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn); struct f_tree *build_tree(struct f_tree *); @@ -81,28 +76,11 @@ int same_tree(struct f_tree *t1, struct f_tree *t2); void tree_format(struct f_tree *t, buffer *buf); struct f_trie *f_new_trie(linpool *lp, uint node_size); -void *trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h); -int trie_match_prefix(struct f_trie *t, ip_addr px, int plen); +void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h); +int trie_match_net(struct f_trie *t, const net_addr *n); int trie_same(struct f_trie *t1, struct f_trie *t2); void trie_format(struct f_trie *t, buffer *buf); -void fprefix_get_bounds(struct f_prefix *px, int *l, int *h); - -static inline void -trie_add_fprefix(struct f_trie *t, struct f_prefix *px) -{ - int l, h; - fprefix_get_bounds(px, &l, &h); - trie_add_prefix(t, px->ip, px->len & LEN_MASK, l, h); -} - -static inline int -trie_match_fprefix(struct f_trie *t, struct f_prefix *px) -{ - return trie_match_prefix(t, px->ip, px->len & LEN_MASK); -} - - struct ea_list; struct rte; @@ -163,7 +141,7 @@ void val_format(struct f_val v, buffer *buf); /* Bigger ones */ #define T_IP 0x20 -#define T_PREFIX 0x21 +#define T_NET 0x21 #define T_STRING 0x22 #define T_PATH_MASK 0x23 /* mask for BGP path */ #define T_PATH 0x24 /* BGP path */ @@ -176,12 +154,12 @@ void val_format(struct f_val v, buffer *buf); #define T_PREFIX_SET 0x81 -#define SA_FROM 1 -#define SA_GW 2 -#define SA_NET 3 -#define SA_PROTO 4 -#define SA_SOURCE 5 -#define SA_SCOPE 6 +#define SA_FROM 1 +#define SA_GW 2 +#define SA_NET 3 +#define SA_PROTO 4 +#define SA_SOURCE 5 +#define SA_SCOPE 6 #define SA_CAST 7 #define SA_DEST 8 #define SA_IFNAME 9 diff --git a/filter/test.conf b/filter/test.conf index a99d0a51..c3f74d94 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -16,6 +16,10 @@ define ten = 10; define p23 = (2, 3); define ip1222 = 1.2.2.2; +define net10 = 10.0.0.0/8; +define netdoc = 2001:db8::/32; + + function onef(int a) { return 1; @@ -55,6 +59,7 @@ function fifteen() return 15; } +/* roa table rl { roa 10.110.0.0/16 max 16 as 1000; @@ -80,6 +85,7 @@ function test_roa() " ", roa_check(rl, 10.130.30.0/24, 3000) = ROA_INVALID, " ", roa_check(rl, 10.130.130.0/24, 3000) = ROA_VALID; } +*/ function path_test() bgpmask pm1; @@ -232,7 +238,7 @@ function __test2() function test_pxset(prefix set pxs) { print pxs; - print " must be true: ", 10.0.0.0/8 ~ pxs, ",", 10.0.0.0/10 ~ pxs, ",", 10.0.0.0/12 ~ pxs, ",", + print " must be true: ", net10 ~ pxs, ",", 10.0.0.0/10 ~ pxs, ",", 10.0.0.0/12 ~ pxs, ",", 20.0.0.0/24 ~ pxs, ",", 20.0.40.0/24 ~ pxs, ",", 20.0.0.0/26 ~ pxs, ",", 20.0.100.0/26 ~ pxs, ",", 20.0.0.0/28 ~ pxs, ",", 20.0.255.0/28 ~ pxs; print " must be false: ", 10.0.0.0/7 ~ pxs, ",", 10.0.0.0/13 ~ pxs, ",", 10.0.0.0/16 ~ pxs, ",", @@ -312,12 +318,12 @@ string st; px = 1.2.0.0/18; print "Testing prefixes: 1.2.0.0/18 = ", px; - print " must be true: ", 192.168.0.0/16 ~ 192.168.0.0/16, " ", 192.168.0.0/17 ~ 192.168.0.0/16, " ", 192.168.254.0/24 ~ 192.168.0.0/16; - print " must be false: ", 192.168.0.0/15 ~ 192.168.0.0/16, " ", 192.160.0.0/17 ~ 192.168.0.0/16; + print " must be true: ", 192.168.0.0/16 ~ 192.168.0.0/16, " ", 192.168.0.0/17 ~ 192.168.0.0/16, " ", 192.168.254.0/24 ~ 192.168.0.0/16, " ", netdoc ~ 2001::/16; + print " must be false: ", 192.168.0.0/15 ~ 192.168.0.0/16, " ", 192.160.0.0/17 ~ 192.168.0.0/16, " ", px ~ netdoc; p = 127.1.2.3; print "Testing mask : 127.0.0.0 = ", p.mask(8); - + pp = (1, 2); print "Testing pairs: (1,2) = ", (1,2), " = ", pp, " = ", (1,1+1), " = ", 'mkpair-a'(2); print " must be true: ", (1,2) = (1,1+1); @@ -397,7 +403,7 @@ string st; print "1.2.3.4 = ", onetwo; i = 4200000000; - print "4200000000 = ", i, " false: ", i = 4200000000, " ", i > 4100000000, " false: ", i > 4250000000; + print "4200000000 = ", i, " true: ", i = 4200000000, " ", i > 4100000000, " false: ", i > 4250000000; test_undef(2); test_undef(3); diff --git a/filter/trie.c b/filter/trie.c index 8af9015e..dad87339 100644 --- a/filter/trie.c +++ b/filter/trie.c @@ -74,6 +74,19 @@ #include "conf/conf.h" #include "filter/filter.h" + +/* + * In the trie code, the prefix length is internally treated as for the whole + * ip_addr, regardless whether it contains an IPv4 or IPv6 address. Therefore, + * remaining definitions make sense. + */ + +#define ipa_mkmask(x) ip6_mkmask(x) +#define ipa_masklen(x) ip6_masklen(&x) +#define ipa_pxlen(x,y) ip6_pxlen(x,y) +#define ipa_getbit(x,n) ip6_getbit(x,n) + + /** * f_new_trie - allocates and returns a new empty trie * @lp: linear pool to allocate items from @@ -109,12 +122,11 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child) /** * trie_add_prefix * @t: trie to add to - * @px: prefix address - * @plen: prefix length + * @net: IP network prefix * @l: prefix lower bound * @h: prefix upper bound * - * Adds prefix (prefix pattern) @px/@plen to trie @t. @l and @h are lower + * Adds prefix (prefix pattern) @n to trie @t. @l and @h are lower * and upper bounds on accepted prefix lengths, both inclusive. * 0 <= l, h <= 32 (128 for IPv6). * @@ -124,8 +136,19 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child) */ void * -trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) +trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h) { + ip_addr px = net_prefix(net); + uint plen = net_pxlen(net); + + if (net->type == NET_IP4) + { + const uint delta = IP6_MAX_PREFIX_LENGTH - IP4_MAX_PREFIX_LENGTH; + plen += delta; + l += delta; + h += delta; + } + if (l == 0) t->zero = 1; else @@ -140,7 +163,7 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) struct f_trie_node *o = NULL; struct f_trie_node *n = t->root; - while(n) + while (n) { ip_addr cmask = ipa_and(n->mask, pmask); @@ -196,17 +219,7 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h) return a; } -/** - * trie_match_prefix - * @t: trie - * @px: prefix address - * @plen: prefix length - * - * Tries to find a matching prefix pattern in the trie such that - * prefix @px/@plen matches that prefix pattern. Returns 1 if there - * is such prefix pattern in the trie. - */ -int +static int trie_match_prefix(struct f_trie *t, ip_addr px, int plen) { ip_addr pmask = ipa_mkmask(plen); @@ -241,6 +254,29 @@ trie_match_prefix(struct f_trie *t, ip_addr px, int plen) return 0; } +/** + * trie_match_net + * @t: trie + * @n: net address + * + * Tries to find a matching net in the trie such that + * prefix @n matches that prefix pattern. Returns 1 if there + * is such prefix pattern in the trie. + */ +int +trie_match_net(struct f_trie *t, const net_addr *n) +{ + int add = 0; + switch (n->type) { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + add = IP6_MAX_PREFIX_LENGTH - IP4_MAX_PREFIX_LENGTH; + } + + return trie_match_prefix(t, net_prefix(n), net_pxlen(n) + add); +} + static int trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2) { diff --git a/lib/Modules b/lib/Modules index 745306d9..6b9b4b0f 100644 --- a/lib/Modules +++ b/lib/Modules @@ -7,8 +7,10 @@ sha1.h birdlib.h bitops.c bitops.h -ip.h +idm.c +idm.h ip.c +ip.h lists.c lists.h md5.c @@ -31,3 +33,4 @@ event.h checksum.c checksum.h alloca.h +net.c diff --git a/lib/birdlib.h b/lib/birdlib.h index 16f437ef..ece50dc2 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -34,6 +34,13 @@ #define ABS(a) ((a)>=0 ? (a) : -(a)) #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a)) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) +#define CALL(fn, args...) ({ if (fn) fn(args); }) + +static inline int uint_cmp(uint i1, uint i2) +{ return (int)(i1 > i2) - (int)(i1 < i2); } + +static inline int u64_cmp(u64 i1, u64 i2) +{ return (int)(i1 > i2) - (int)(i1 < i2); } /* Bitfield macros */ @@ -49,12 +56,6 @@ #define NULL ((void *) 0) #endif -#ifndef IPV6 -#define IP_VERSION 4 -#else -#define IP_VERSION 6 -#endif - /* Macros for gcc attributes */ diff --git a/lib/bitops.c b/lib/bitops.c index 81586e87..efb8710e 100644 --- a/lib/bitops.c +++ b/lib/bitops.c @@ -28,15 +28,15 @@ u32_mkmask(uint n) * * This function checks whether the given integer @x represents * a valid bit mask (binary representation contains first ones, then - * zeroes) and returns the number of ones or -1 if the mask is invalid. + * zeroes) and returns the number of ones or 255 if the mask is invalid. */ -int +uint u32_masklen(u32 x) { int l = 0; u32 n = ~x; - if (n & (n+1)) return -1; + if (n & (n+1)) return 255; if (x & 0x0000ffff) { x &= 0x0000ffff; l += 16; } if (x & 0x00ff00ff) { x &= 0x00ff00ff; l += 8; } if (x & 0x0f0f0f0f) { x &= 0x0f0f0f0f; l += 4; } diff --git a/lib/bitops.h b/lib/bitops.h index c0ad1a70..82bef699 100644 --- a/lib/bitops.h +++ b/lib/bitops.h @@ -19,7 +19,7 @@ */ u32 u32_mkmask(uint n); -int u32_masklen(u32 x); +uint u32_masklen(u32 x); u32 u32_log2(u32 v); @@ -178,3 +178,16 @@ #define HASH_WALK_FILTER_END } while (0) +static inline uint +mem_hash(void *p, int s) +{ + const char *pp = p; + const u64 multiplier = 0xb38bc09a61202731ULL; + u64 value = 0x001047d54778bcafULL; + int i; + for (i=0;i<s;i++) + value = value*multiplier + pp[i]; + + return ((value >> 32) ^ (value & 0xffffffff)); +} + diff --git a/lib/idm.c b/lib/idm.c new file mode 100644 index 00000000..16d0e855 --- /dev/null +++ b/lib/idm.c @@ -0,0 +1,76 @@ +/* + * BIRD Library -- ID Map + * + * (c) 2013--2015 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2013--2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdlib.h> + +#include "nest/bird.h" +#include "lib/idm.h" +#include "lib/resource.h" +#include "lib/string.h" + + +void +idm_init(struct idm *m, pool *p, uint size) +{ + m->pos = 0; + m->used = 1; + m->size = size; + m->data = mb_allocz(p, m->size * sizeof(u32)); + + /* ID 0 is reserved */ + m->data[0] = 1; +} + +static inline int u32_cto(uint x) { return ffs(~x) - 1; } + +u32 +idm_alloc(struct idm *m) +{ + uint i, j; + + for (i = m->pos; i < m->size; i++) + if (m->data[i] != 0xffffffff) + goto found; + + /* If we are at least 7/8 full, expand */ + if (m->used > (m->size * 28)) + { + m->size *= 2; + m->data = mb_realloc(m->data, m->size * sizeof(u32)); + memset(m->data + i, 0, (m->size - i) * sizeof(u32)); + goto found; + } + + for (i = 0; i < m->pos; i++) + if (m->data[i] != 0xffffffff) + goto found; + + ASSERT(0); + + found: + ASSERT(i < 0x8000000); + + m->pos = i; + j = u32_cto(m->data[i]); + + m->data[i] |= (1 << j); + m->used++; + return 32 * i + j; +} + +void +idm_free(struct idm *m, u32 id) +{ + uint i = id / 32; + uint j = id % 32; + + ASSERT((i < m->size) && (m->data[i] & (1 << j))); + m->data[i] &= ~(1 << j); + m->used--; +} diff --git a/lib/idm.h b/lib/idm.h new file mode 100644 index 00000000..e3380cce --- /dev/null +++ b/lib/idm.h @@ -0,0 +1,25 @@ +/* + * BIRD Library -- ID Map + * + * (c) 2013--2015 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2013--2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_IDM_H_ +#define _BIRD_IDM_H_ + +struct idm +{ + u32 *data; + u32 pos; + u32 used; + u32 size; +}; + +void idm_init(struct idm *m, pool *p, uint size); +u32 idm_alloc(struct idm *m); +void idm_free(struct idm *m, u32 id); + +#endif @@ -58,7 +58,7 @@ ip6_mkmask(uint n) return a; } -int +uint ip6_masklen(ip6_addr *a) { int i, j, n; @@ -67,12 +67,12 @@ ip6_masklen(ip6_addr *a) if (a->addr[i] != ~0U) { j = u32_masklen(a->addr[i]); - if (j < 0) + if (j == 255) return j; n += j; while (++i < 4) if (a->addr[i]) - return -1; + return 255; break; } @@ -30,6 +30,13 @@ #define IP4_NONE _MI4(0) #define IP6_NONE _MI6(0,0,0,0) +#define IP4_MAX_PREFIX_LENGTH 32 +#define IP6_MAX_PREFIX_LENGTH 128 + +#define IP4_MAX_TEXT_LENGTH 15 /* "255.255.255.255" */ +#define IP6_MAX_TEXT_LENGTH 39 /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" */ +#define IPA_MAX_TEXT_LENGTH 39 + #define IP4_MIN_MTU 576 #define IP6_MIN_MTU 1280 @@ -40,19 +47,6 @@ #define UDP_HEADER_LENGTH 8 -#ifdef IPV6 -#define MAX_PREFIX_LENGTH 128 -#define BITS_PER_IP_ADDRESS 128 -#define STD_ADDRESS_P_LENGTH 39 -#define SIZE_OF_IP_HEADER 40 -#else -#define MAX_PREFIX_LENGTH 32 -#define BITS_PER_IP_ADDRESS 32 -#define STD_ADDRESS_P_LENGTH 15 -#define SIZE_OF_IP_HEADER 24 -#endif - - #ifdef DEBUGGING typedef struct ip4_addr { @@ -83,8 +77,6 @@ typedef struct ip6_addr { #define _I3(a) ((a).addr[3]) -#ifdef IPV6 - /* Structure ip_addr may contain both IPv4 and IPv6 addresses */ typedef ip6_addr ip_addr; #define IPA_NONE IP6_NONE @@ -99,23 +91,8 @@ typedef ip6_addr ip_addr; #define ipa_is_ip4(a) ip6_is_v4mapped(a) -#else - -/* Provisionary ip_addr definition same as ip4_addr */ -typedef ip4_addr ip_addr; -#define IPA_NONE IP4_NONE - -#define ipa_from_ip4(x) x -#define ipa_from_ip6(x) IPA_NONE -#define ipa_from_u32(x) ipa_from_ip4(ip4_from_u32(x)) - -#define ipa_to_ip4(x) x -#define ipa_to_ip6(x) IP6_NONE -#define ipa_to_u32(x) ip4_to_u32(ipa_to_ip4(x)) - -#define ipa_is_ip4(a) 1 - -#endif +#define IPA_NONE4 ipa_from_ip4(IP4_NONE) +#define IPA_NONE6 ipa_from_ip6(IP6_NONE) /* @@ -180,7 +157,6 @@ static inline ip6_addr ip6_not(ip6_addr a) { return _MI6(~_I0(a), ~_I1(a), ~_I2(a), ~_I3(a)); } -#ifdef IPV6 #define ipa_equal(x,y) ip6_equal(x,y) #define ipa_zero(x) ip6_zero(x) #define ipa_nonzero(x) ip6_nonzero(x) @@ -188,19 +164,8 @@ static inline ip6_addr ip6_not(ip6_addr a) #define ipa_or(x,y) ip6_or(x,y) #define ipa_xor(x,y) ip6_xor(x,y) #define ipa_not(x) ip6_not(x) -#else -#define ipa_equal(x,y) ip4_equal(x,y) -#define ipa_zero(x) ip4_zero(x) -#define ipa_nonzero(x) ip4_nonzero(x) -#define ipa_and(x,y) ip4_and(x,y) -#define ipa_or(x,y) ip4_or(x,y) -#define ipa_xor(x,y) ip4_xor(x,y) -#define ipa_not(x) ip4_not(x) -#endif - -#ifdef IPV6 /* * A zero address is either a token for invalid/unused, or the prefix of default * routes. These functions should be used in the second case, where both IPv4 @@ -213,26 +178,12 @@ static inline int ipa_zero2(ip_addr a) static inline int ipa_nonzero2(ip_addr a) { return _I0(a) || _I1(a) || ((_I2(a) != 0) && (_I2(a) != 0xffff)) || _I3(a); } -#else -#define ipa_zero2(x) ip4_zero(x) -#define ipa_nonzero2(x) ip4_nonzero(x) -#endif - /* * Hash and compare functions */ -static inline uint ip4_hash(ip4_addr a) -{ - /* Returns a 16-bit value */ - u32 x = _I(a); - x ^= x >> 16; - x ^= x << 10; - return x & 0xffff; -} - -static inline u32 ip4_hash32(ip4_addr a) +static inline u32 ip4_hash(ip4_addr a) { /* Returns a 32-bit value, although low-order bits are not mixed */ u32 x = _I(a); @@ -241,14 +192,7 @@ static inline u32 ip4_hash32(ip4_addr a) return x; } -static inline uint ip6_hash(ip6_addr a) -{ - /* Returns a 16-bit hash key */ - u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a); - return (x ^ (x >> 16) ^ (x >> 8)) & 0xffff; -} - -static inline u32 ip6_hash32(ip6_addr a) +static inline u32 ip6_hash(ip6_addr a) { /* Returns a 32-bit hash key, although low-order bits are not mixed */ u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a); @@ -260,16 +204,8 @@ static inline int ip4_compare(ip4_addr a, ip4_addr b) int ip6_compare(ip6_addr a, ip6_addr b); - -#ifdef IPV6 #define ipa_hash(x) ip6_hash(x) -#define ipa_hash32(x) ip6_hash32(x) #define ipa_compare(x,y) ip6_compare(x,y) -#else -#define ipa_hash(x) ip4_hash(x) -#define ipa_hash32(x) ip4_hash32(x) -#define ipa_compare(x,y) ip4_compare(x,y) -#endif /* @@ -300,14 +236,10 @@ static inline int ip6_is_link_local(ip6_addr a) static inline int ip6_is_v4mapped(ip6_addr a) { return _I0(a) == 0 && _I1(a) == 0 && _I2(a) == 0xffff; } -#ifdef IPV6 #define ipa_classify(x) ip6_classify(&(x)) #define ipa_is_link_local(x) ip6_is_link_local(x) -#else -#define ipa_classify(x) ip4_classify(x) -#define ipa_is_link_local(x) 0 -#endif +/* XXXX remove */ static inline int ipa_classify_net(ip_addr a) { return ipa_zero2(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); } @@ -319,11 +251,11 @@ static inline int ipa_classify_net(ip_addr a) static inline ip4_addr ip4_mkmask(uint n) { return _MI4(u32_mkmask(n)); } -static inline int ip4_masklen(ip4_addr a) +static inline uint ip4_masklen(ip4_addr a) { return u32_masklen(_I(a)); } ip6_addr ip6_mkmask(uint n); -int ip6_masklen(ip6_addr *a); +uint ip6_masklen(ip6_addr *a); /* ipX_pxlen() requires that x != y */ static inline uint ip4_pxlen(ip4_addr a, ip4_addr b) @@ -345,6 +277,18 @@ static inline u32 ip4_getbit(ip4_addr a, uint pos) static inline u32 ip6_getbit(ip6_addr a, uint pos) { return a.addr[pos / 32] & (0x80000000 >> (pos % 32)); } +static inline u32 ip4_setbit(ip4_addr *a, uint pos) +{ return _I(*a) |= (0x80000000 >> pos); } + +static inline u32 ip6_setbit(ip6_addr *a, uint pos) +{ return a->addr[pos / 32] |= (0x80000000 >> (pos % 32)); } + +static inline u32 ip4_clrbit(ip4_addr *a, uint pos) +{ return _I(*a) &= ~(0x80000000 >> pos); } + +static inline u32 ip6_clrbit(ip6_addr *a, uint pos) +{ return a->addr[pos / 32] &= ~(0x80000000 >> (pos % 32)); } + static inline ip4_addr ip4_opposite_m1(ip4_addr a) { return _MI4(_I(a) ^ 1); } @@ -359,21 +303,8 @@ static inline ip6_addr ip6_opposite_m2(ip6_addr a) ip4_addr ip4_class_mask(ip4_addr ad); -#ifdef IPV6 -#define ipa_mkmask(x) ip6_mkmask(x) -#define ipa_masklen(x) ip6_masklen(&x) -#define ipa_pxlen(x,y) ip6_pxlen(x,y) -#define ipa_getbit(x,n) ip6_getbit(x,n) #define ipa_opposite_m1(x) ip6_opposite_m1(x) #define ipa_opposite_m2(x) ip6_opposite_m2(x) -#else -#define ipa_mkmask(x) ip4_mkmask(x) -#define ipa_masklen(x) ip4_masklen(x) -#define ipa_pxlen(x,y) ip4_pxlen(x,y) -#define ipa_getbit(x,n) ip4_getbit(x,n) -#define ipa_opposite_m1(x) ip4_opposite_m1(x) -#define ipa_opposite_m2(x) ip4_opposite_m2(x) -#endif /* @@ -392,14 +323,6 @@ static inline ip6_addr ip6_hton(ip6_addr a) static inline ip6_addr ip6_ntoh(ip6_addr a) { return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); } -#ifdef IPV6 -#define ipa_hton(x) x = ip6_hton(x) -#define ipa_ntoh(x) x = ip6_ntoh(x) -#else -#define ipa_hton(x) x = ip4_hton(x) -#define ipa_ntoh(x) x = ip4_ntoh(x) -#endif - /* * Unaligned data access (in network order) @@ -430,15 +353,6 @@ static inline void * put_ip6(void *buf, ip6_addr a) return buf+16; } -// XXXX these functions must be redesigned or removed -#ifdef IPV6 -#define get_ipa(x) get_ip6(x) -#define put_ipa(x,y) put_ip6(x,y) -#else -#define get_ipa(x) get_ip4(x) -#define put_ipa(x,y) put_ip4(x,y) -#endif - /* * Binary/text form conversions @@ -456,34 +370,11 @@ static inline char * ip6_ntox(ip6_addr a, char *b) int ip4_pton(const char *a, ip4_addr *o); int ip6_pton(const char *a, ip6_addr *o); -// XXXX these functions must be redesigned or removed -#ifdef IPV6 -#define ipa_ntop(x,y) ip6_ntop(x,y) -#define ipa_ntox(x,y) ip6_ntox(x,y) -#define ipa_pton(x,y) ip6_pton(x,y) -#else -#define ipa_ntop(x,y) ip4_ntop(x,y) -#define ipa_ntox(x,y) ip4_ntox(x,y) -#define ipa_pton(x,y) ip4_pton(x,y) -#endif - /* * Miscellaneous */ -// XXXX review this - -#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))))) - char *ip_scope_text(uint); -struct prefix { - ip_addr addr; - uint len; -}; - - #endif diff --git a/lib/lists.h b/lib/lists.h index 51856b05..46b33446 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -52,7 +52,10 @@ typedef union list { /* In fact two overlayed nodes */ #define WALK_LIST2(n,nn,list,pos) \ for(nn=(list).head; NODE_VALID(nn) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nn->next) #define WALK_LIST_DELSAFE(n,nxt,list) \ - for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt) + for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt) +#define WALK_LIST2_DELSAFE(n,nn,nxt,list,pos) \ + for(nn=HEAD(list); (nxt=nn->next) && (n=SKIP_BACK(typeof(*n),pos,nn)); nn=nxt) + /* WALK_LIST_FIRST supposes that called code removes each processed node */ #define WALK_LIST_FIRST(n,list) \ while(n=HEAD(list), (NODE (n))->next) diff --git a/lib/net.c b/lib/net.c new file mode 100644 index 00000000..71fbe6ff --- /dev/null +++ b/lib/net.c @@ -0,0 +1,201 @@ + +#include "nest/bird.h" +#include "lib/ip.h" +#include "lib/net.h" + + +const char * const net_label[] = { + [NET_IP4] = "ipv4", + [NET_IP6] = "ipv6", + [NET_VPN4] = "vpn4", + [NET_VPN6] = "vpn6" +}; + +const u16 net_addr_length[] = { + [NET_IP4] = sizeof(net_addr_ip4), + [NET_IP6] = sizeof(net_addr_ip6), + [NET_VPN4] = sizeof(net_addr_vpn4), + [NET_VPN6] = sizeof(net_addr_vpn6), + [NET_ROA4] = sizeof(net_addr_roa4), + [NET_ROA6] = sizeof(net_addr_roa6) +}; + +const u8 net_max_prefix_length[] = { + [NET_IP4] = IP4_MAX_PREFIX_LENGTH, + [NET_IP6] = IP6_MAX_PREFIX_LENGTH, + [NET_VPN4] = IP4_MAX_PREFIX_LENGTH, + [NET_VPN6] = IP6_MAX_PREFIX_LENGTH, + [NET_ROA4] = IP4_MAX_PREFIX_LENGTH, + [NET_ROA6] = IP6_MAX_PREFIX_LENGTH +}; + +const u16 net_max_text_length[] = { + [NET_IP4] = 18, /* "255.255.255.255/32" */ + [NET_IP6] = 43, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */ + [NET_VPN4] = 40, /* "4294967296:4294967296 255.255.255.255/32" */ + [NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */ + [NET_ROA4] = 30, /* "255.255.255.255/32 AS4294967295" */ + [NET_ROA6] = 56, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 AS4294967295" */ +}; + + +int +net_format(const net_addr *N, char *buf, int buflen) +{ + net_addr_union *n = (void *) N; + + switch (n->n.type) + { + case NET_IP4: + return bsnprintf(buf, buflen, "%I4/%d", n->ip4.prefix, n->ip4.pxlen); + case NET_IP6: + return bsnprintf(buf, buflen, "%I6/%d", n->ip6.prefix, n->ip6.pxlen); + case NET_VPN4: + return bsnprintf(buf, buflen, "%u:%u %I4/%d", (u32) (n->vpn4.rd >> 32), (u32) n->vpn4.rd, n->vpn4.prefix, n->vpn4.pxlen); + case NET_VPN6: + return bsnprintf(buf, buflen, "%u:%u %I6/%d", (u32) (n->vpn6.rd >> 32), (u32) n->vpn6.rd, n->vpn6.prefix, n->vpn6.pxlen); + case NET_ROA4: + return bsnprintf(buf, buflen, "%I4/%u-%u AS%u", n->roa4.prefix, n->roa4.pxlen, n->roa4.max_pxlen, n->roa4.asn); + case NET_ROA6: + return bsnprintf(buf, buflen, "%I6/%u-%u AS%u", n->roa6.prefix, n->roa6.pxlen, n->roa6.max_pxlen, n->roa6.asn); + } + + return 0; +} + +ip_addr +net_pxmask(const net_addr *a) +{ + switch (a->type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return ipa_from_ip4(ip4_mkmask(net4_pxlen(a))); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return ipa_from_ip6(ip6_mkmask(net6_pxlen(a))); + + default: + return IPA_NONE; + } +} + +int +net_compare(const net_addr *a, const net_addr *b) +{ + if (a->type != b->type) + return uint_cmp(a->type, b->type); + + switch (a->type) + { + case NET_IP4: + return net_compare_ip4((const net_addr_ip4 *) a, (const net_addr_ip4 *) b); + case NET_IP6: + return net_compare_ip6((const net_addr_ip6 *) a, (const net_addr_ip6 *) b); + case NET_VPN4: + return net_compare_vpn4((const net_addr_vpn4 *) a, (const net_addr_vpn4 *) b); + case NET_VPN6: + return net_compare_vpn6((const net_addr_vpn6 *) a, (const net_addr_vpn6 *) b); + case NET_ROA4: + return net_compare_roa4((const net_addr_roa4 *) a, (const net_addr_roa4 *) b); + case NET_ROA6: + return net_compare_roa6((const net_addr_roa6 *) a, (const net_addr_roa6 *) b); + } + return 0; +} + +int +net_validate(const net_addr *N) +{ + switch (N->type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return net_validate_ip4((net_addr_ip4 *) N); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return net_validate_ip6((net_addr_ip6 *) N); + + default: + return 0; + } +} + +void +net_normalize(net_addr *N) +{ + net_addr_union *n = (void *) N; + + switch (n->n.type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return net_normalize_ip4(&n->ip4); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return net_normalize_ip6(&n->ip6); + } +} + +int +net_classify(const net_addr *N) +{ + net_addr_union *n = (void *) N; + + switch (n->n.type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return ip4_zero(n->ip4.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip4_classify(n->ip4.prefix); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix); + } + + return IADDR_INVALID; +} + +int +ipa_in_netX(const ip_addr a, const net_addr *n) +{ + switch (n->type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + if (!ipa_is_ip4(a)) return 0; + return ip4_zero(ip4_and(ip4_xor(ipa_to_ip4(a), net4_prefix(n)), + ip4_mkmask(net4_pxlen(n)))); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + if (ipa_is_ip4(a)) return 0; + return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)), + ip6_mkmask(net6_pxlen(n)))); + + default: + return 0; + } +} + +int +net_in_netX(const net_addr *a, const net_addr *n) +{ + if (a->type != n->type) + return 0; + + return (net_pxlen(n) <= net_pxlen(a)) && ipa_in_netX(net_prefix(a), n); +} diff --git a/lib/net.h b/lib/net.h new file mode 100644 index 00000000..fbce2811 --- /dev/null +++ b/lib/net.h @@ -0,0 +1,349 @@ +/* + * BIRD Internet Routing Daemon -- Network addresses + * + * (c) 2015 Ondrej Zajicek <santiago@crfreenet.org> + * (c) 2015 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_NET_H_ +#define _BIRD_NET_H_ + +#include "lib/ip.h" + + +#define NET_IP4 1 +#define NET_IP6 2 +#define NET_VPN4 3 +#define NET_VPN6 4 +#define NET_ROA4 5 +#define NET_ROA6 6 +#define NET_MAX 7 + +#define NB_IP4 (1 << NET_IP4) +#define NB_IP6 (1 << NET_IP6) +#define NB_VPN4 (1 << NET_VPN4) +#define NB_VPN6 (1 << NET_VPN6) + +#define NB_IP (NB_IP4 | NB_IP6) +#define NB_ANY 0xffffffff + + +typedef struct net_addr { + u8 type; + u8 pxlen; + u16 length; + u8 data[16]; + u64 align[0]; +} net_addr; + +typedef struct net_addr_ip4 { + u8 type; + u8 pxlen; + u16 length; + ip4_addr prefix; +} net_addr_ip4; + +typedef struct net_addr_ip6 { + u8 type; + u8 pxlen; + u16 length; + ip6_addr prefix; +} net_addr_ip6; + +typedef struct net_addr_vpn4 { + u8 type; + u8 pxlen; + u16 length; + ip4_addr prefix; + u64 rd; +} net_addr_vpn4; + +typedef struct net_addr_vpn6 { + u8 type; + u8 pxlen; + u16 length; + ip6_addr prefix; + u64 rd; +} net_addr_vpn6; + +typedef struct net_addr_roa4 { + u8 type; + u8 pxlen; + u16 length; + ip4_addr prefix; + u32 max_pxlen; + u32 asn; +} net_addr_roa4; + +typedef struct net_addr_roa6 { + u8 type; + u8 pxlen; + u16 length; + ip6_addr prefix; + u32 max_pxlen; + u32 asn; +} net_addr_roa6; + +typedef union net_addr_union { + net_addr n; + net_addr_ip4 ip4; + net_addr_ip6 ip6; + net_addr_vpn4 vpn4; + net_addr_vpn6 vpn6; + net_addr_roa4 roa4; + net_addr_roa6 roa6; +} net_addr_union; + + +extern const char * const net_label[]; +extern const u16 net_addr_length[]; +extern const u8 net_max_prefix_length[]; +extern const u16 net_max_text_length[]; + +#define NET_MAX_TEXT_LENGTH 65 + + +#define NET_ADDR_IP4(prefix,pxlen) \ + ((net_addr_ip4) { NET_IP4, pxlen, sizeof(net_addr_ip4), prefix }) + +#define NET_ADDR_IP6(prefix,pxlen) \ + ((net_addr_ip6) { NET_IP6, pxlen, sizeof(net_addr_ip6), prefix }) + +#define NET_ADDR_VPN4(prefix,pxlen,rd) \ + ((net_addr_vpn4) { NET_VPN4, pxlen, sizeof(net_addr_vpn4), prefix, rd }) + +#define NET_ADDR_VPN6(prefix,pxlen,rd) \ + ((net_addr_vpn6) { NET_VPN6, pxlen, sizeof(net_addr_vpn6), prefix, rd }) + +#define NET_ADDR_ROA4(prefix,pxlen,max_pxlen,asn) \ + ((net_addr_roa4) { NET_ROA4, pxlen, sizeof(net_addr_roa4), prefix, max_pxlen, asn }) + +#define NET_ADDR_ROA6(prefix,pxlen,max_pxlen,asn) \ + ((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn }) + + + +static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen) +{ *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); } + +static inline void net_fill_ip6(net_addr *a, ip6_addr prefix, uint pxlen) +{ *(net_addr_ip6 *)a = NET_ADDR_IP6(prefix, pxlen); } + +static inline void net_fill_vpn4(net_addr *a, ip4_addr prefix, uint pxlen, u64 rd) +{ *(net_addr_vpn4 *)a = NET_ADDR_VPN4(prefix, pxlen, rd); } + +static inline void net_fill_vpn6(net_addr *a, ip6_addr prefix, uint pxlen, u64 rd) +{ *(net_addr_vpn6 *)a = NET_ADDR_VPN6(prefix, pxlen, rd); } + +static inline void net_fill_roa4(net_addr *a, ip4_addr prefix, uint pxlen, uint max_pxlen, u32 asn) +{ *(net_addr_roa4 *)a = NET_ADDR_ROA4(prefix, pxlen, max_pxlen, asn); } + +static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint max_pxlen, u32 asn) +{ *(net_addr_roa6 *)a = NET_ADDR_ROA6(prefix, pxlen, max_pxlen, asn); } + +static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen) +{ + if (ipa_is_ip4(prefix)) + net_fill_ip4(a, ipa_to_ip4(prefix), pxlen); + else + net_fill_ip6(a, ipa_to_ip6(prefix), pxlen); +} + +static inline void net_fill_ip_host(net_addr *a, ip_addr prefix) +{ + if (ipa_is_ip4(prefix)) + net_fill_ip4(a, ipa_to_ip4(prefix), IP4_MAX_PREFIX_LENGTH); + else + net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH); +} + + +static inline int net_val_match(u8 type, u32 mask) +{ return !!((1 << type) & mask); } + +static inline int net_type_match(const net_addr *a, u32 mask) +{ return net_val_match(a->type, mask); } + +static inline int net_is_ip(const net_addr *a) +{ return (a->type == NET_IP4) || (a->type == NET_IP6); } + + +static inline ip4_addr net4_prefix(const net_addr *a) +{ return ((net_addr_ip4 *) a)->prefix; } + +static inline ip6_addr net6_prefix(const net_addr *a) +{ return ((net_addr_ip6 *) a)->prefix; } + +static inline ip_addr net_prefix(const net_addr *a) +{ + switch (a->type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return ipa_from_ip4(net4_prefix(a)); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return ipa_from_ip6(net6_prefix(a)); + + default: + return IPA_NONE; + } +} + +static inline uint net4_pxlen(const net_addr *a) +{ return a->pxlen; } + +static inline uint net6_pxlen(const net_addr *a) +{ return a->pxlen; } + +static inline uint net_pxlen(const net_addr *a) +{ return a->pxlen; } + +ip_addr net_pxmask(const net_addr *a); + + +static inline int net_equal(const net_addr *a, const net_addr *b) +{ return (a->length == b->length) && !memcmp(a, b, a->length); } + +static inline int net_equal_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b) +{ return !memcmp(a, b, sizeof(net_addr_ip4)); } + +static inline int net_equal_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b) +{ return !memcmp(a, b, sizeof(net_addr_ip6)); } + +static inline int net_equal_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b) +{ return !memcmp(a, b, sizeof(net_addr_vpn4)); } + +static inline int net_equal_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b) +{ return !memcmp(a, b, sizeof(net_addr_vpn6)); } + +static inline int net_equal_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b) +{ return !memcmp(a, b, sizeof(net_addr_roa4)); } + +static inline int net_equal_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b) +{ return !memcmp(a, b, sizeof(net_addr_roa6)); } + + +static inline int net_zero_ip4(const net_addr_ip4 *a) +{ return !a->pxlen && ip4_zero(a->prefix); } + +static inline int net_zero_ip6(const net_addr_ip6 *a) +{ return !a->pxlen && ip6_zero(a->prefix); } + +static inline int net_zero_vpn4(const net_addr_vpn4 *a) +{ return !a->pxlen && ip4_zero(a->prefix) && !a->rd; } + +static inline int net_zero_vpn6(const net_addr_vpn6 *a) +{ return !a->pxlen && ip6_zero(a->prefix) && !a->rd; } + +static inline int net_zero_roa4(const net_addr_roa4 *a) +{ return !a->pxlen && ip4_zero(a->prefix) && !a->max_pxlen && !a->asn; } + +static inline int net_zero_roa6(const net_addr_roa6 *a) +{ return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; } + + +static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b) +{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); } + +static inline int net_compare_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b) +{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); } + +static inline int net_compare_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b) +{ return u64_cmp(a->rd, b->rd) ?: ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); } + +static inline int net_compare_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b) +{ return u64_cmp(a->rd, b->rd) ?: ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); } + +static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b) +{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); } + +static inline int net_compare_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b) +{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); } + +int net_compare(const net_addr *a, const net_addr *b); + + +static inline void net_copy(net_addr *dst, const net_addr *src) +{ memcpy(dst, src, src->length); } + +static inline void net_copy_ip4(net_addr_ip4 *dst, const net_addr_ip4 *src) +{ memcpy(dst, src, sizeof(net_addr_ip4)); } + +static inline void net_copy_ip6(net_addr_ip6 *dst, const net_addr_ip6 *src) +{ memcpy(dst, src, sizeof(net_addr_ip6)); } + +static inline void net_copy_vpn4(net_addr_vpn4 *dst, const net_addr_vpn4 *src) +{ memcpy(dst, src, sizeof(net_addr_vpn4)); } + +static inline void net_copy_vpn6(net_addr_vpn6 *dst, const net_addr_vpn6 *src) +{ memcpy(dst, src, sizeof(net_addr_vpn6)); } + +static inline void net_copy_roa4(net_addr_roa4 *dst, const net_addr_roa4 *src) +{ memcpy(dst, src, sizeof(net_addr_roa4)); } + +static inline void net_copy_roa6(net_addr_roa6 *dst, const net_addr_roa6 *src) +{ memcpy(dst, src, sizeof(net_addr_roa6)); } + + +static inline u32 net_hash_ip4(const net_addr_ip4 *n) +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); } + +static inline u32 net_hash_ip6(const net_addr_ip6 *n) +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } + +/* XXXX */ +static inline u32 u64_hash(u64 a) +{ return u32_hash(a); } + +static inline u32 net_hash_vpn4(const net_addr_vpn4 *n) +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); } + +static inline u32 net_hash_vpn6(const net_addr_vpn6 *n) +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); } + +static inline u32 net_hash_roa4(const net_addr_roa4 *n) +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); } + +static inline u32 net_hash_roa6(const net_addr_roa6 *n) +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } + + +static inline int net_validate_ip4(const net_addr_ip4 *n) +{ + return (n->pxlen <= IP4_MAX_PREFIX_LENGTH) && + ip4_zero(ip4_and(n->prefix, ip4_not(ip4_mkmask(n->pxlen)))); +} + +static inline int net_validate_ip6(const net_addr_ip6 *n) +{ + return (n->pxlen <= IP6_MAX_PREFIX_LENGTH) && + ip6_zero(ip6_and(n->prefix, ip6_not(ip6_mkmask(n->pxlen)))); +} + +int net_validate(const net_addr *N); + + +static inline void net_normalize_ip4(net_addr_ip4 *n) +{ n->prefix = ip4_and(n->prefix, ip4_mkmask(n->pxlen)); } + +static inline void net_normalize_ip6(net_addr_ip6 *n) +{ n->prefix = ip6_and(n->prefix, ip6_mkmask(n->pxlen)); } + +void net_normalize(net_addr *N); + + +int net_classify(const net_addr *N); +int net_format(const net_addr *N, char *buf, int buflen); + + +int ipa_in_netX(const ip_addr A, const net_addr *N); +int net_in_netX(const net_addr *A, const net_addr *N); + + +#endif diff --git a/lib/printf.c b/lib/printf.c index e4cc3006..318cee2c 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -118,15 +118,15 @@ static char * number(char * str, long num, int base, int size, int precision, * @fmt: format string * @args: a list of arguments to be formatted * - * This functions acts like ordinary sprintf() except that it checks - * available space to avoid buffer overflows and it allows some more - * format specifiers: |%I| for formatting of IP addresses (any non-zero - * width is automatically replaced by standard IP address width which - * depends on whether we use IPv4 or IPv6; |%#I| gives hexadecimal format), - * |%R| for Router / Network ID (u32 value printed as IPv4 address) - * and |%m| resp. |%M| for error messages (uses strerror() to translate @errno code to - * message text). On the other hand, it doesn't support floating - * point numbers. + * This functions acts like ordinary sprintf() except that it checks available + * space to avoid buffer overflows and it allows some more format specifiers: + * |%I| for formatting of IP addresses (width of 1 is automatically replaced by + * standard IP address width which depends on whether we use IPv4 or IPv6; |%I4| + * or |%I6| can be used for explicit ip4_addr / ip6_addr arguments, |%N| for + * generic network addresses (net_addr *), |%R| for Router / Network ID (u32 + * value printed as IPv4 address) and |%m| resp. |%M| for error messages (uses + * strerror() to translate @errno code to message text). On the other hand, it + * doesn't support floating point numbers. * * Result: number of characters of the output string or -1 if * the buffer space was insufficient. @@ -139,7 +139,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) u32 x; char *str, *start; const char *s; - char ipbuf[STD_ADDRESS_P_LENGTH+1]; + char ipbuf[NET_MAX_TEXT_LENGTH+1]; struct iface *iface; int flags; /* flags to number() */ @@ -156,7 +156,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) *str++ = *fmt; continue; } - + /* process flags */ flags = 0; repeat: @@ -168,7 +168,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) case '#': flags |= SPECIAL; goto repeat; case '0': flags |= ZEROPAD; goto repeat; } - + /* get field width */ field_width = -1; if (is_digit(*fmt)) @@ -186,7 +186,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) /* get the precision */ precision = -1; if (*fmt == '.') { - ++fmt; + ++fmt; if (is_digit(*fmt)) precision = skip_atoi(&fmt); else if (*fmt == '*') { @@ -236,6 +236,14 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) case 'M': s = strerror(va_arg(args, int)); goto str; + case 'N': { + net_addr *n = va_arg(args, net_addr *); + if (field_width == 1) + field_width = net_max_text_length[n->type]; + net_format(n, ipbuf, sizeof(ipbuf)); + s = ipbuf; + goto str; + } case 's': s = va_arg(args, char *); if (!s) @@ -282,14 +290,35 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) /* IP address */ case 'I': - if (flags & SPECIAL) - ipa_ntox(va_arg(args, ip_addr), ipbuf); - else { - ipa_ntop(va_arg(args, ip_addr), ipbuf); - if (field_width == 1) - field_width = STD_ADDRESS_P_LENGTH; + if (fmt[1] == '4') { + /* Explicit IPv4 address */ + ip4_addr a = va_arg(args, ip4_addr); + ip4_ntop(a, ipbuf); + i = IP4_MAX_TEXT_LENGTH; + fmt++; + } else if (fmt[1] == '6') { + /* Explicit IPv6 address */ + ip6_addr a = va_arg(args, ip6_addr); + ip6_ntop(a, ipbuf); + i = IP6_MAX_TEXT_LENGTH; + fmt++; + } else { + /* Just IP address */ + ip_addr a = va_arg(args, ip_addr); + + if (ipa_is_ip4(a)) { + ip4_ntop(ipa_to_ip4(a), ipbuf); + i = IP4_MAX_TEXT_LENGTH; + } else { + ip6_ntop(ipa_to_ip6(a), ipbuf); + i = IP6_MAX_TEXT_LENGTH; + } } + s = ipbuf; + if (field_width == 1) + field_width = i; + goto str; /* Interface scope after link-local IP address */ @@ -310,11 +339,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) /* Router/Network ID - essentially IPv4 address in u32 value */ case 'R': x = va_arg(args, u32); - bsprintf(ipbuf, "%d.%d.%d.%d", - ((x >> 24) & 0xff), - ((x >> 16) & 0xff), - ((x >> 8) & 0xff), - (x & 0xff)); + ip4_ntop(ip4_from_u32(x), ipbuf); s = ipbuf; goto str; diff --git a/lib/socket.h b/lib/socket.h index 0327e9e5..91ae9db3 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -10,7 +10,6 @@ #define _BIRD_SOCKET_H_ #include <errno.h> -// #include <sys/socket.h> #include "lib/resource.h" @@ -45,7 +44,7 @@ typedef struct birdsock { uint lifindex; /* local interface that received the datagram */ /* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */ - int af; /* Address family (AF_INET, AF_INET6 or 0 for non-IP) of fd */ + int fam; /* Address family (SK_FAM_* or 0 for non-IP) of fd */ int fd; /* System-dependent data */ int index; /* Index in poll buffer */ int rcv_ttl; /* TTL of last received datagram */ @@ -68,19 +67,12 @@ void sk_set_tbsize(sock *s, uint val); /* Resize TX buffer, keeping content */ void sk_set_tbuf(sock *s, void *tbuf); /* Switch TX buffer, NULL-> return to internal */ void sk_dump_all(void); +int sk_is_ipv4(sock *s); /* True if socket is IPv4 */ +int sk_is_ipv6(sock *s); /* True if socket is IPv6 */ + static inline int sk_send_buffer_empty(sock *sk) { return sk->tbuf == sk->tpos; } - -#ifdef IPV6 -#define sk_is_ipv4(X) 0 -#define sk_is_ipv6(X) 1 -#else -#define sk_is_ipv4(X) 1 -#define sk_is_ipv6(X) 0 -#endif - - int sk_setup_multicast(sock *s); /* Prepare UDP or IP socket for multicasting */ int sk_join_group(sock *s, ip_addr maddr); /* Join multicast group on sk iface */ int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface */ @@ -124,6 +116,12 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou #define SK_UNIX_PASSIVE 8 #define SK_UNIX 9 +/* Socket families */ + +#define SK_FAM_NONE 0 +#define SK_FAM_IPV4 4 +#define SK_FAM_IPV6 6 + /* * For SK_UDP or SK_IP sockets setting DA/DP allows to use sk_send(), * otherwise sk_send_to() must be used. diff --git a/lib/unaligned.h b/lib/unaligned.h index dc777fbf..130b2479 100644 --- a/lib/unaligned.h +++ b/lib/unaligned.h @@ -17,6 +17,7 @@ * if possible. */ +#include "lib/endian.h" #include "lib/string.h" static inline u16 diff --git a/nest/Makefile b/nest/Makefile index e6928668..478a82b7 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -1,4 +1,4 @@ -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 \ +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 \ a-path.c a-set.c root-rel=../ dir-name=nest diff --git a/nest/bird.h b/nest/bird.h index 3c7d749b..55712abe 100644 --- a/nest/bird.h +++ b/nest/bird.h @@ -12,5 +12,6 @@ #include "sysdep/config.h" #include "lib/birdlib.h" #include "lib/ip.h" +#include "lib/net.h" #endif diff --git a/nest/cmds.c b/nest/cmds.c index 70fbdaf8..82fdca66 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -80,7 +80,6 @@ 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 @@ -89,7 +88,6 @@ 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/config.Y b/nest/config.Y index 87827c10..94a67670 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -17,9 +17,10 @@ CF_HDR CF_DEFINES static struct proto_config *this_proto; +static struct channel_config *this_channel; static struct iface_patt *this_ipatt; static struct iface_patt_node *this_ipn; -static struct roa_table_config *this_roa_table; +/* static struct roa_table_config *this_roa_table; */ static list *this_p_list; static struct password_item *this_p_item; static int password_id; @@ -30,7 +31,7 @@ iface_patt_check(void) struct iface_patt_node *pn; WALK_LIST(pn, this_ipatt->ipn_list) - if (!pn->pattern || pn->pxlen) + if (!pn->pattern || pn->prefix.type) cf_error("Interface name/mask expected, not IP prefix"); } @@ -49,15 +50,25 @@ get_passwords(void) return rv; } +static void +proto_postconfig(void) +{ + CALL(this_proto->protocol->postconfig, this_proto); + this_channel = NULL; + this_proto = NULL; +} + + #define DIRECT_CFG ((struct rt_dev_config *) this_proto) 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(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6) CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) -CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE, ROA) +CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE) /* ,ROA */ CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) @@ -74,12 +85,11 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) %type <r> rtable %type <s> optsym %type <ra> r_args -%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_mode roa_mode limit_action tab_sorted tos +%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type table_sorted tos %type <ps> proto_patt proto_patt2 -%type <g> limit_spec +%type <cc> channel_start proto_channel +%type <cl> limit_spec CF_GRAMMAR @@ -87,7 +97,7 @@ CF_GRAMMAR CF_ADDTO(conf, rtrid) -rtrid: +rtrid: ROUTER ID idval ';' { new_config->router_id = $3; } | ROUTER ID FROM iface_patt ';' { new_config->router_id_from = this_ipatt; } ; @@ -95,21 +105,12 @@ rtrid: idval: NUM { $$ = $1; } | '(' term ')' { $$ = f_eval_int($2); } - | RTRID - | IPA { -#ifndef IPV6 - $$ = ipa_to_u32($1); -#else - cf_error("Router IDs must be entered as hexadecimal numbers or IPv4 addresses in IPv6 version"); -#endif - } + | IP4 { $$ = ip4_to_u32($1); } | SYM { if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD)) $$ = SYM_VAL($1).i; -#ifndef IPV6 - else if ($1->class == (SYM_CONSTANT | T_IP)) - $$ = ipa_to_u32(SYM_VAL($1).px.ip); -#endif + else if (($1->class == (SYM_CONSTANT | T_IP)) && ipa_is_ip4(SYM_VAL($1).ip)) + $$ = ipa_to_u32(SYM_VAL($1).ip); else cf_error("Number or IPv4 address constant expected"); } @@ -125,7 +126,7 @@ listen_opts: | listen_opts listen_opt ; -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 = 0; } @@ -138,43 +139,38 @@ CF_ADDTO(conf, gr_opts) gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ; -/* Creation of routing tables */ +/* Network types (for tables, channels) */ -tab_sorted: - { $$ = 0; } - | SORTED { $$ = 1; } +net_type: + IPV4 { $$ = NET_IP4; } + | IPV6 { $$ = NET_IP6; } + | VPN4 { $$ = NET_VPN4; } + | VPN6 { $$ = NET_VPN6; } + | ROA4 { $$ = NET_ROA4; } + | ROA6 { $$ = NET_ROA6; } ; -CF_ADDTO(conf, newtab) -newtab: TABLE SYM tab_sorted { - struct rtable_config *cf; - cf = rt_new_table($2); - cf->sorted = $3; - } - ; +/* Creation of routing tables */ -CF_ADDTO(conf, roa_table) +CF_ADDTO(conf, table) -roa_table_start: ROA TABLE SYM { - this_roa_table = roa_new_table_config($3); -}; +table_sorted: + { $$ = 0; } + | SORTED { $$ = 1; } + ; -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); +table: net_type TABLE SYM table_sorted { + struct rtable_config *cf; + cf = rt_new_table($3, $1); + cf->sorted = $4; } ; -roa_table: - roa_table_start - | roa_table_start '{' roa_table_opts '}' - ; /* Definition of protocols */ -CF_ADDTO(conf, proto) +CF_ADDTO(conf, proto { proto_postconfig(); }) proto_start: PROTOCOL { $$ = SYM_PROTO; } @@ -212,24 +208,62 @@ proto_name: proto_item: /* EMPTY */ - | PREFERENCE expr { - if ($2 < 0 || $2 > 0xFFFF) cf_error("Invalid preference"); - this_proto->preference = $2; - } | DISABLED bool { this_proto->disabled = $2; } | DEBUG debug_mask { this_proto->debug = $2; } | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; } - | IMPORT imexport { this_proto->in_filter = $2; } - | EXPORT imexport { this_proto->out_filter = $2; } - | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; } - | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } - | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } - | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; } - | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION text { this_proto->dsc = $2; } ; + +channel_start: net_type +{ + $$ = this_channel = channel_config_new(NULL, $1, this_proto); +}; + +channel_item: + TABLE rtable { + if (this_channel->net_type && ($2->addr_type != this_channel->net_type)) + cf_error("Incompatible table type"); + this_channel->table = $2; + } + | IMPORT imexport { this_channel->in_filter = $2; } + | EXPORT imexport { this_channel->out_filter = $2; } + | RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; } + | IMPORT LIMIT limit_spec { this_channel->in_limit = $3; } + | EXPORT LIMIT limit_spec { this_channel->out_limit = $3; } + | PREFERENCE expr { this_channel->preference = $2; check_u16($2); } + | IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; } + ; + +channel_opts: + /* empty */ + | channel_opts channel_item ';' + ; + +channel_opt_list: + /* empty */ + | '{' channel_opts '}' + ; + +channel_end: +{ + if (!this_channel->table) + cf_error("Routing table not specified"); + + this_channel = NULL; +}; + +proto_channel: channel_start channel_opt_list channel_end; + + +rtable: + SYM { + if ($1->class != SYM_TABLE) cf_error("Table expected"); + $$ = $1->def; + } + ; + imexport: FILTER filter { $$ = $2; } | where_filter @@ -246,20 +280,8 @@ limit_action: ; limit_spec: - expr limit_action { - struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit)); - l->limit = $1; - l->action = $2; - $$ = l; - } - | OFF { $$ = NULL; } - ; - -rtable: - SYM { - if ($1->class != SYM_TABLE) cf_error("Table name expected"); - $$ = $1->def; - } + expr limit_action { $$ = (struct channel_limit){ .limit = $1, $$.action = $2 }; } + | OFF { $$ = (struct channel_limit){}; } ; CF_ADDTO(conf, debug_default) @@ -282,9 +304,8 @@ iface_patt_node_init: ; iface_patt_node_body: - TEXT { this_ipn->pattern = $1; this_ipn->prefix = IPA_NONE; this_ipn->pxlen = 0; } - | prefix_or_ipa { this_ipn->pattern = NULL; this_ipn->prefix = $1.addr; this_ipn->pxlen = $1.len; } - | TEXT prefix_or_ipa { this_ipn->pattern = $1; this_ipn->prefix = $2.addr; this_ipn->pxlen = $2.len; } + TEXT { this_ipn->pattern = $1; /* this_ipn->prefix stays zero */ } + | opttext net_or_ipa { this_ipn->pattern = $1; this_ipn->prefix = $2; } ; iface_negate: @@ -293,7 +314,7 @@ iface_negate: ; iface_patt_node: - iface_patt_node_init iface_negate iface_patt_node_body + iface_patt_node_init iface_negate iface_patt_node_body ; @@ -334,6 +355,7 @@ dev_proto_start: proto_start DIRECT { dev_proto: dev_proto_start proto_name '{' | dev_proto proto_item ';' + | dev_proto proto_channel ';' | dev_proto dev_iface_patt ';' ; @@ -428,8 +450,10 @@ password_item_params: /* empty */ { } | GENERATE FROM datetime ';' password_item_params { this_p_item->genfrom = $3; } | GENERATE TO datetime ';' password_item_params { this_p_item->gento = $3; } + | GENERATE FROM datetime TO datetime ';' password_item_params { this_p_item->genfrom = $3; this_p_item->gento = $5; } | ACCEPT FROM datetime ';' password_item_params { this_p_item->accfrom = $3; } | ACCEPT TO datetime ';' password_item_params { this_p_item->accto = $3; } + | ACCEPT FROM datetime TO datetime ';' password_item_params { this_p_item->accfrom = $3; this_p_item->accto = $5; } | ID expr ';' password_item_params { this_p_item->id = $2; if ($2 <= 0) cf_error("Password ID has to be greated than zero."); } ; @@ -468,21 +492,19 @@ CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filte r_args: /* empty */ { $$ = cfg_allocz(sizeof(struct rt_show_data)); - $$->pxlen = 256; $$->filter = FILTER_ACCEPT; } - | r_args prefix { + | r_args net_any { $$ = $1; - if ($$->pxlen != 256) cf_error("Only one prefix expected"); - $$->prefix = $2.addr; - $$->pxlen = $2.len; + if ($$->addr) cf_error("Only one prefix expected"); + $$->addr = $2; } - | r_args FOR prefix_or_ipa { + | r_args FOR net_or_ipa { $$ = $1; - if ($$->pxlen != 256) cf_error("Only one prefix expected"); - $$->prefix = $3.addr; - $$->pxlen = $3.len; + if ($$->addr) cf_error("Only one prefix expected"); $$->show_for = 1; + $$->addr = cfg_alloc($3.length); + net_copy($$->addr, &($3)); } | r_args TABLE SYM { $$ = $1; @@ -545,45 +567,8 @@ export_mode: ; -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]]) +CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]]) { cmd_show_symbols($3); } ; sym_args: @@ -595,46 +580,10 @@ sym_args: | 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/iface.c b/nest/iface.c index 4d73c2a4..00af5052 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -46,7 +46,7 @@ list iface_list; void ifa_dump(struct ifa *a) { - debug("\t%I, net %I/%-2d bc %I -> %I%s%s%s\n", a->ip, a->prefix, a->pxlen, a->brd, a->opposite, + debug("\t%I, net %N bc %I -> %I%s%s%s\n", a->ip, &a->prefix, a->brd, a->opposite, (a->flags & IF_UP) ? "" : " DOWN", (a->flags & IA_PRIMARY) ? "" : " SEC", (a->flags & IA_PEER) ? "PEER" : ""); @@ -138,13 +138,12 @@ if_copy(struct iface *to, struct iface *from) static inline void ifa_send_notify(struct proto *p, unsigned c, struct ifa *a) { - if (p->ifa_notify) + if (p->ifa_notify && (p->proto_state != PS_DOWN)) { if (p->debug & D_IFACES) - log(L_TRACE "%s < %s address %I/%d on interface %s %s", + log(L_TRACE "%s < %s address %N on interface %s %s", p->name, (a->flags & IA_PRIMARY) ? "primary" : "secondary", - a->prefix, a->pxlen, a->iface->name, - (c & IF_CHANGE_UP) ? "added" : "removed"); + &a->prefix, a->iface->name, (c & IF_CHANGE_UP) ? "added" : "removed"); p->ifa_notify(p, c, a); } } @@ -156,7 +155,7 @@ ifa_notify_change_(unsigned c, struct ifa *a) DBG("IFA change notification (%x) for %s:%I\n", c, a->iface->name, a->ip); - WALK_LIST(p, active_proto_list) + WALK_LIST(p, proto_list) ifa_send_notify(p, c, a); } @@ -175,7 +174,7 @@ ifa_notify_change(unsigned c, struct ifa *a) static inline void if_send_notify(struct proto *p, unsigned c, struct iface *i) { - if (p->if_notify) + if (p->if_notify && (p->proto_state != PS_DOWN)) { if (p->debug & D_IFACES) log(L_TRACE "%s < interface %s %s", p->name, i->name, @@ -216,7 +215,7 @@ if_notify_change(unsigned c, struct iface *i) ifa_notify_change_(IF_CHANGE_DOWN, a); } - WALK_LIST(p, active_proto_list) + WALK_LIST(p, proto_list) if_send_notify(p, c, i); if (c & IF_CHANGE_UP) @@ -500,8 +499,7 @@ ifa_recalc_all_primary_addresses(void) static inline int ifa_same(struct ifa *a, struct ifa *b) { - return ipa_equal(a->ip, b->ip) && ipa_equal(a->prefix, b->prefix) && - a->pxlen == b->pxlen; + return ipa_equal(a->ip, b->ip) && net_equal(&a->prefix, &b->prefix); } @@ -534,10 +532,8 @@ ifa_update(struct ifa *a) break; } -#ifndef IPV6 - if ((i->flags & IF_BROADCAST) && !ipa_nonzero(a->brd)) + if ((a->prefix.type == NET_IP4) && (i->flags & IF_BROADCAST) && ipa_zero(a->brd)) log(L_ERR "Missing broadcast address for interface %s", i->name); -#endif b = mb_alloc(if_pool, sizeof(struct ifa)); memcpy(b, a, sizeof(struct ifa)); @@ -586,7 +582,6 @@ ifa_delete(struct ifa *a) u32 if_choose_router_id(struct iface_patt *mask, u32 old_id) { -#ifndef IPV6 struct iface *i; struct ifa *a, *b; @@ -599,6 +594,9 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) WALK_LIST(a, i->addrs) { + if (a->prefix.type != NET_IP4) + continue; + if (a->flags & IA_SECONDARY) continue; @@ -623,10 +621,6 @@ if_choose_router_id(struct iface_patt *mask, u32 old_id) log(L_INFO "Chosen router ID %R according to interface %s", id, b->iface->name); return id; - -#else - return 0; -#endif } /** @@ -669,17 +663,17 @@ iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a) continue; } - if (p->pxlen == 0) + if (p->prefix.pxlen == 0) return pos; if (!a) continue; - if (ipa_in_net(a->ip, p->prefix, p->pxlen)) + if (ipa_in_netX(a->ip, &p->prefix)) return pos; if ((a->flags & IA_PEER) && - ipa_in_net(a->opposite, p->prefix, p->pxlen)) + ipa_in_netX(a->opposite, &p->prefix)) return pos; continue; @@ -713,8 +707,7 @@ iface_plists_equal(struct iface_patt *pa, struct iface_patt *pb) (!x->pattern && y->pattern) || /* This nasty lines where written by me... :-( Feela */ (!y->pattern && x->pattern) || ((x->pattern != y->pattern) && strcmp(x->pattern, y->pattern)) || - !ipa_equal(x->prefix, y->prefix) || - (x->pxlen != y->pxlen)) + !net_equal(&x->prefix, &y->prefix)) return 0; x = (void *) x->n.next; y = (void *) y->n.next; @@ -747,14 +740,14 @@ iface_patts_equal(list *a, list *b, int (*comp)(struct iface_patt *, struct ifac static void if_show_addr(struct ifa *a) { - byte opp[STD_ADDRESS_P_LENGTH + 16]; + byte opp[IPA_MAX_TEXT_LENGTH + 16]; if (ipa_nonzero(a->opposite)) bsprintf(opp, ", opposite %I", a->opposite); else opp[0] = 0; cli_msg(-1003, "\t%I/%d (%s%s, scope %s)", - a->ip, a->pxlen, + a->ip, a->prefix.pxlen, (a->flags & IA_PRIMARY) ? "Primary" : (a->flags & IA_SECONDARY) ? "Secondary" : "Unselected", opp, ip_scope_text(a->scope)); } @@ -798,13 +791,13 @@ void if_show_summary(void) { struct iface *i; - byte addr[STD_ADDRESS_P_LENGTH + 16]; + byte addr[IPA_MAX_TEXT_LENGTH + 16]; cli_msg(-2005, "interface state address"); WALK_LIST(i, iface_list) { if (i->addr) - bsprintf(addr, "%I/%d", i->addr->ip, i->addr->pxlen); + bsprintf(addr, "%I/%d", i->addr->ip, i->addr->prefix.pxlen); else addr[0] = 0; cli_msg(-1005, "%-9s %-5s %s", i->name, (i->flags & IF_UP) ? "up" : "DOWN", addr); diff --git a/nest/iface.h b/nest/iface.h index 56710e4a..c4f414ec 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -19,9 +19,8 @@ struct pool; struct ifa { /* Interface address */ node n; struct iface *iface; /* Interface this address belongs to */ + net_addr prefix; /* Network prefix */ ip_addr ip; /* IP address of this host */ - ip_addr prefix; /* Network prefix */ - unsigned pxlen; /* Prefix length */ ip_addr brd; /* Broadcast address */ ip_addr opposite; /* Opposite end of a point-to-point link */ unsigned scope; /* Interface address scope */ @@ -148,8 +147,7 @@ struct iface_patt_node { node n; int positive; byte *pattern; - ip_addr prefix; - int pxlen; + net_addr prefix; }; struct iface_patt { diff --git a/nest/neighbor.c b/nest/neighbor.c index 1685d67e..69f09423 100644 --- a/nest/neighbor.c +++ b/nest/neighbor.c @@ -45,6 +45,7 @@ #include "lib/resource.h" #define NEIGH_HASH_SIZE 256 +#define NEIGH_HASH_OFFSET 24 static slab *neigh_slab; static list sticky_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE]; @@ -52,7 +53,7 @@ static list sticky_neigh_list, neigh_hash_table[NEIGH_HASH_SIZE]; static inline uint neigh_hash(struct proto *p, ip_addr *a) { - return (p->hash_key ^ ipa_hash(*a)) & (NEIGH_HASH_SIZE-1); + return (p->hash_key ^ ipa_hash(*a)) >> NEIGH_HASH_OFFSET; } static int @@ -79,17 +80,17 @@ if_connected(ip_addr *a, struct iface *i, struct ifa **ap) } else { - if (ipa_in_net(*a, b->prefix, b->pxlen)) + if (ipa_in_netX(*a, &b->prefix)) { -#ifndef IPV6 - if ((b->pxlen < (BITS_PER_IP_ADDRESS - 1)) && - (ipa_equal(*a, b->prefix) || /* Network address */ + /* Do not allow IPv4 network and broadcast addresses */ + if (ipa_is_ip4(*a) && + (net_pxlen(&b->prefix) < (IP4_MAX_PREFIX_LENGTH - 1)) && + (ipa_equal(*a, net_prefix(&b->prefix)) || /* Network address */ ipa_equal(*a, b->brd))) /* Broadcast */ { *ap = NULL; return -1; } -#endif return b->scope; } @@ -238,7 +239,7 @@ neigh_up(neighbor *n, struct iface *i, int scope, struct ifa *a) rem_node(&n->n); add_tail(&neigh_hash_table[neigh_hash(n->proto, &n->addr)], &n->n); DBG("Waking up sticky neighbor %I\n", n->addr); - if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING) + if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) n->proto->neigh_notify(n); } @@ -251,7 +252,7 @@ neigh_down(neighbor *n) n->iface = NULL; n->ifa = NULL; n->scope = -1; - if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING) + if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) n->proto->neigh_notify(n); rem_node(&n->n); if (n->flags & NEF_STICKY) @@ -332,7 +333,7 @@ neigh_if_link(struct iface *i) WALK_LIST_DELSAFE(x, y, i->neighbors) { neighbor *n = SKIP_BACK(neighbor, if_n, x); - if (n->proto->neigh_notify && n->proto->core_state != FS_FLUSHING) + if (n->proto->neigh_notify && (n->proto->proto_state != PS_STOP)) n->proto->neigh_notify(n); } } diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c index e80f87ea..5923ff67 100644 --- a/nest/proto-hooks.c +++ b/nest/proto-hooks.c @@ -189,7 +189,7 @@ void ifa_notify(struct proto *p, unsigned flags, struct ifa *a) /** * rt_notify - notify instance about routing table change * @p: protocol instance - * @table: a routing table + * @channel: notifying channel * @net: a network entry * @new: new route for the network * @old: old route for the network diff --git a/nest/proto.c b/nest/proto.c index 436377f1..df4952b7 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -21,19 +21,12 @@ #include "filter/filter.h" pool *proto_pool; +list proto_list; static list protocol_list; -static list proto_list; #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0) -list active_proto_list; -static list inactive_proto_list; -static list initial_proto_list; -static list flush_proto_list; -static struct proto *initial_device_proto; - -static event *proto_flush_event; static timer *proto_shutdown_timer; static timer *gr_wait_timer; @@ -46,199 +39,641 @@ static int graceful_restart_state; static u32 graceful_restart_locks; static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; -static char *c_states[] = { "HUNGRY", "???", "HAPPY", "FLUSHING" }; +static char *cs_states[] = { + [CS_DOWN] = "DOWN", + [CS_START] = "START", + [CS_UP] = "UP", + [CS_FLUSHING] = "FLUSHING" +}; + +extern struct protocol proto_unix_iface; -static void proto_flush_loop(void *); static void proto_shutdown_loop(struct timer *); static void proto_rethink_goal(struct proto *p); -static void proto_want_export_up(struct proto *p); -static void proto_fell_down(struct proto *p); static char *proto_state_name(struct proto *p); +static void channel_verify_limits(struct channel *c); +static void channel_reset_limit(struct channel_limit *l); -static void -proto_relink(struct proto *p) -{ - list *l = NULL; - switch (p->core_state) - { - case FS_HUNGRY: - l = &inactive_proto_list; - break; - case FS_HAPPY: - l = &active_proto_list; - break; - case FS_FLUSHING: - l = &flush_proto_list; - break; - default: - ASSERT(0); - } +static inline int proto_is_done(struct proto *p) +{ return (p->proto_state == PS_DOWN) && (p->active_channels == 0); } - rem_node(&p->n); - add_tail(l, &p->n); -} +static inline int channel_is_active(struct channel *c) +{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); } static void proto_log_state_change(struct proto *p) { if (p->debug & D_STATES) + { + char *name = proto_state_name(p); + if (name != p->last_state_name_announced) { - char *name = proto_state_name(p); - if (name != p->last_state_name_announced) - { - p->last_state_name_announced = name; - PD(p, "State changed to %s", proto_state_name(p)); - } + p->last_state_name_announced = name; + PD(p, "State changed to %s", proto_state_name(p)); } + } else p->last_state_name_announced = NULL; } -/** - * proto_new - create a new protocol instance - * @c: protocol configuration - * @size: size of protocol data structure (each protocol instance is represented by - * a structure starting with generic part [struct &proto] and continued - * with data specific to the protocol) - * - * When a new configuration has been read in, the core code starts - * initializing all the protocol instances configured by calling their - * init() hooks with the corresponding instance configuration. The initialization - * code of the protocol is expected to create a new instance according to the - * configuration by calling this function and then modifying the default settings - * to values wanted by the protocol. - */ -void * -proto_new(struct proto_config *c, unsigned size) +struct channel_config * +proto_cf_find_channel(struct proto_config *pc, uint net_type) { - struct protocol *pr = c->protocol; - struct proto *p = mb_allocz(proto_pool, size); - - p->cf = c; - p->debug = c->debug; - p->mrtdump = c->mrtdump; - p->name = c->name; - p->preference = c->preference; - p->disabled = c->disabled; - p->proto = pr; - p->table = c->table->table; - p->hash_key = random_u32(); - c->proto = p; - return p; + struct channel_config *cc; + + WALK_LIST(cc, pc->channels) + if (cc->net_type == net_type) + return cc; + + return NULL; } -static void -proto_init_instance(struct proto *p) +/** + * proto_find_channel_by_table - find channel connected to a routing table + * @p: protocol instance + * @t: routing table + * + * Returns pointer to channel or NULL + */ +struct channel * +proto_find_channel_by_table(struct proto *p, struct rtable *t) { - /* Here we cannot use p->cf->name since it won't survive reconfiguration */ - p->pool = rp_new(proto_pool, p->proto->name); - p->attn = ev_new(p->pool); - p->attn->data = p; + struct channel *c; - if (graceful_restart_state == GRS_INIT) - p->gr_recovery = 1; + WALK_LIST(c, p->channels) + if (c->table == t) + return c; - if (! p->proto->multitable) - rt_lock_table(p->table); + return NULL; } -extern pool *rt_table_pool; /** - * proto_add_announce_hook - connect protocol to a routing table + * proto_add_channel - connect protocol to a routing table * @p: protocol instance - * @t: routing table to connect to - * @stats: per-table protocol statistics - * - * This function creates a connection between the protocol instance @p and the - * routing table @t, making the protocol hear all changes in the table. + * @cf: channel configuration * - * The announce hook is linked in the protocol ahook list. Announce hooks are - * allocated from the routing table resource pool and when protocol accepts - * routes also in the table ahook list. The are linked to the table ahook list - * and unlinked from it depending on export_state (in proto_want_export_up() and - * proto_want_export_down()) and they are automatically freed after the protocol - * is flushed (in proto_fell_down()). + * This function creates a channel between the protocol instance @p and the + * routing table specified in the configuration @cf, making the protocol hear + * all changes in the table and allowing the protocol to update routes in the + * table. * - * Unless you want to listen to multiple routing tables (as the Pipe protocol - * does), you needn't to worry about this function since the connection to the - * protocol's primary routing table is initialized automatically by the core - * code. + * The channel is linked in the protocol channel list and when active also in + * the table channel list. Channels are allocated from the global resource pool + * (@proto_pool) and they are automatically freed when the protocol is removed. */ -struct announce_hook * -proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats) + +struct channel * +proto_add_channel(struct proto *p, struct channel_config *cf) { - struct announce_hook *h; + struct channel *c = mb_allocz(proto_pool, cf->channel->channel_size); + + c->name = cf->name; + c->channel = cf->channel; + c->proto = p; + c->table = cf->table->table; + + c->in_filter = cf->in_filter; + c->out_filter = cf->out_filter; + c->rx_limit = cf->rx_limit; + c->in_limit = cf->in_limit; + c->out_limit = cf->out_limit; + + c->net_type = cf->net_type; + c->ra_mode = cf->ra_mode; + c->preference = cf->preference; + c->merge_limit = cf->merge_limit; + c->in_keep_filtered = cf->in_keep_filtered; + + c->channel_state = CS_DOWN; + c->export_state = ES_DOWN; + c->last_state_change = now; + c->reloadable = 1; + + CALL(c->channel->init, c, cf); + + add_tail(&p->channels, &c->n); + + PD(p, "Channel %s connected to table %s", c->name, c->table->name); + + return c; +} + +void +proto_remove_channel(struct proto *p, struct channel *c) +{ + ASSERT(c->channel_state == CS_DOWN); + + PD(p, "Channel %s removed", c->name); + + rem_node(&c->n); + mb_free(c); +} - DBG("Connecting protocol %s to table %s\n", p->name, t->name); - PD(p, "Connected to table %s", t->name); - h = mb_allocz(rt_table_pool, sizeof(struct announce_hook)); - h->table = t; - h->proto = p; - h->stats = stats; +static void +proto_start_channels(struct proto *p) +{ + struct channel *c; + WALK_LIST(c, p->channels) + if (!c->disabled) + channel_set_state(c, CS_UP); +} - h->next = p->ahooks; - p->ahooks = h; +static void +proto_pause_channels(struct proto *p) +{ + struct channel *c; + WALK_LIST(c, p->channels) + if (!c->disabled && channel_is_active(c)) + channel_set_state(c, CS_START); +} - if (p->rt_notify && (p->export_state != ES_DOWN)) - add_tail(&t->hooks, &h->n); - return h; +static void +proto_stop_channels(struct proto *p) +{ + struct channel *c; + WALK_LIST(c, p->channels) + if (!c->disabled && channel_is_active(c)) + channel_set_state(c, CS_FLUSHING); +} + +static void +proto_remove_channels(struct proto *p) +{ + struct channel *c; + WALK_LIST_FIRST(c, p->channels) + proto_remove_channel(p, c); +} + +static void +channel_schedule_feed(struct channel *c, int initial) +{ + // DBG("%s: Scheduling meal\n", p->name); + ASSERT(c->channel_state == CS_UP); + + c->export_state = ES_FEEDING; + c->refeeding = !initial; + + ev_schedule(c->feed_event); +} + +static void +channel_feed_loop(void *ptr) +{ + struct channel *c = ptr; + + if (c->export_state != ES_FEEDING) + return; + + if (!c->feed_active) + if (c->proto->feed_begin) + c->proto->feed_begin(c, !c->refeeding); + + // DBG("Feeding protocol %s continued\n", p->name); + if (!rt_feed_channel(c)) + { + ev_schedule(c->feed_event); + return; + } + + // DBG("Feeding protocol %s finished\n", p->name); + c->export_state = ES_READY; + // proto_log_state_change(p); + + if (c->proto->feed_end) + c->proto->feed_end(c); +} + + +static void +channel_start_export(struct channel *c) +{ + ASSERT(c->channel_state == CS_UP); + ASSERT(c->export_state == ES_DOWN); + + channel_schedule_feed(c, 1); /* Sets ES_FEEDING */ +} + +static void +channel_stop_export(struct channel *c) +{ + /* Need to abort feeding */ + if (c->export_state == ES_FEEDING) + rt_feed_channel_abort(c); + + c->export_state = ES_DOWN; + c->stats.exp_routes = 0; +} + +static void +channel_do_start(struct channel *c) +{ + rt_lock_table(c->table); + add_tail(&c->table->channels, &c->table_node); + c->proto->active_channels++; + + c->feed_event = ev_new(c->proto->pool); + c->feed_event->data = c; + c->feed_event->hook = channel_feed_loop; + + channel_reset_limit(&c->rx_limit); + channel_reset_limit(&c->in_limit); + channel_reset_limit(&c->out_limit); + + CALL(c->channel->start, c); +} + +static void +channel_do_flush(struct channel *c) +{ + rt_schedule_prune(c->table); + + c->gr_wait = 0; + if (c->gr_lock) + channel_graceful_restart_unlock(c); + + CALL(c->channel->shutdown, c); +} + +static void +channel_do_down(struct channel *c) +{ + rem_node(&c->table_node); + rt_unlock_table(c->table); + c->proto->active_channels--; + + if ((c->stats.imp_routes + c->stats.filt_routes) != 0) + log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name); + + memset(&c->stats, 0, sizeof(struct proto_stats)); + + /* Schedule protocol shutddown */ + if (proto_is_done(c->proto)) + ev_schedule(c->proto->event); +} + +void +channel_set_state(struct channel *c, uint state) +{ + uint cs = c->channel_state; + uint es = c->export_state; + + DBG("%s reporting channel %s state transition %s -> %s\n", c->proto->name, c->name, cs_states[cs], cs_states[state]); + if (state == cs) + return; + + c->channel_state = state; + c->last_state_change = now; + + switch (state) + { + case CS_START: + ASSERT(cs == CS_DOWN || cs == CS_UP); + + if (cs == CS_DOWN) + channel_do_start(c); + + if (es != ES_DOWN) + channel_stop_export(c); + + break; + + case CS_UP: + ASSERT(cs == CS_DOWN || cs == CS_START); + + if (cs == CS_DOWN) + channel_do_start(c); + + if (!c->gr_wait && c->proto->rt_notify) + channel_start_export(c); + + break; + + case CS_FLUSHING: + ASSERT(cs == CS_START || cs == CS_UP); + + if (es != ES_DOWN) + channel_stop_export(c); + + channel_do_flush(c); + break; + + case CS_DOWN: + ASSERT(cs == CS_FLUSHING); + + channel_do_down(c); + break; + + default: + ASSERT(0); + } + // XXXX proto_log_state_change(c); } /** - * proto_find_announce_hook - find announce hooks - * @p: protocol instance - * @t: routing table + * channel_request_feeding - request feeding routes to the channel + * @c: given channel * - * Returns pointer to announce hook or NULL + * Sometimes it is needed to send again all routes to the channel. This is + * called feeding and can be requested by this function. This would cause + * channel export state transition to ES_FEEDING (during feeding) and when + * completed, it will switch back to ES_READY. This function can be called + * even when feeding is already running, in that case it is restarted. */ -struct announce_hook * -proto_find_announce_hook(struct proto *p, struct rtable *t) +void +channel_request_feeding(struct channel *c) { - struct announce_hook *a; + ASSERT(c->channel_state == CS_UP); - for (a = p->ahooks; a; a = a->next) - if (a->table == t) - return a; + /* Do nothing if we are still waiting for feeding */ + if (c->export_state == ES_DOWN) + return; - return NULL; + /* If we are already feeding, we want to restart it */ + if (c->export_state == ES_FEEDING) + { + /* Unless feeding is in initial state */ + if (!c->feed_active) + return; + + rt_feed_channel_abort(c); + } + + channel_reset_limit(&c->out_limit); + + /* Hack: reset exp_routes during refeed, and do not decrease it later */ + c->stats.exp_routes = 0; + + channel_schedule_feed(c, 0); /* Sets ES_FEEDING */ + // proto_log_state_change(c); +} + +static inline int +channel_reloadable(struct channel *c) +{ + return c->proto->reload_routes && c->reloadable; } static void -proto_link_ahooks(struct proto *p) +channel_request_reload(struct channel *c) { - struct announce_hook *h; + ASSERT(c->channel_state == CS_UP); + // ASSERT(channel_reloadable(c)); + + c->proto->reload_routes(c); - if (p->rt_notify) - for(h=p->ahooks; h; h=h->next) - add_tail(&h->table->hooks, &h->n); + /* + * Should this be done before reload_routes() hook? + * Perhaps, but routes are updated asynchronously. + */ + channel_reset_limit(&c->rx_limit); + channel_reset_limit(&c->in_limit); } -static void -proto_unlink_ahooks(struct proto *p) +const struct channel_class channel_basic = { + .channel_size = sizeof(struct channel), + .config_size = sizeof(struct channel_config) +}; + +void * +channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto) +{ + struct channel_config *cf = NULL; + struct rtable_config *tab = NULL; + const char *name = NULL; + + if (net_type) + { + if (!net_val_match(net_type, proto->protocol->channel_mask)) + cf_error("Unsupported channel type"); + + if (proto->net_type && (net_type != proto->net_type)) + cf_error("Different channel type"); + + tab = new_config->def_tables[net_type]; + name = net_label[net_type]; + } + + if (!cc) + cc = &channel_basic; + + cf = cfg_allocz(cc->config_size); + cf->name = name; + cf->channel = cc; + cf->table = tab; + cf->out_filter = FILTER_REJECT; + + cf->net_type = net_type; + cf->ra_mode = RA_OPTIMAL; + cf->preference = proto->protocol->preference; + + add_tail(&proto->channels, &cf->n); + + return cf; +} + +struct channel_config * +channel_copy_config(struct channel_config *src, struct proto_config *proto) { - struct announce_hook *h; + struct channel_config *dst = cfg_alloc(src->channel->config_size); + + memcpy(dst, src, src->channel->config_size); + add_tail(&proto->channels, &dst->n); + CALL(src->channel->copy_config, dst, src); + + return dst; +} + + +static int reconfigure_type; /* Hack to propagate type info to channel_reconfigure() */ + +int +channel_reconfigure(struct channel *c, struct channel_config *cf) +{ + /* FIXME: better handle these changes, also handle in_keep_filtered */ + if ((c->table != cf->table->table) || (c->ra_mode != cf->ra_mode)) + return 0; - if (p->rt_notify) - for(h=p->ahooks; h; h=h->next) - rem_node(&h->n); + int import_changed = !filter_same(c->in_filter, cf->in_filter); + int export_changed = !filter_same(c->out_filter, cf->out_filter); + + if (c->preference != cf->preference) + import_changed = 1; + + if (c->merge_limit != cf->merge_limit) + export_changed = 1; + + /* Reconfigure channel fields */ + c->in_filter = cf->in_filter; + c->out_filter = cf->out_filter; + c->rx_limit = cf->rx_limit; + c->in_limit = cf->in_limit; + c->out_limit = cf->out_limit; + + // c->ra_mode = cf->ra_mode; + c->merge_limit = cf->merge_limit; + c->preference = cf->preference; + c->in_keep_filtered = cf->in_keep_filtered; + + channel_verify_limits(c); + + CALL(c->channel->reconfigure, c, cf); + + /* If the channel is not open, it has no routes and we cannot reload it anyways */ + if (c->channel_state != CS_UP) + return 1; + + if (reconfigure_type == RECONFIG_SOFT) + { + if (import_changed) + log(L_INFO "Channel %s.%s changed import", c->proto->name, c->name); + + if (export_changed) + log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name); + + return 1; + } + + /* Route reload may be not supported */ + if (import_changed && !channel_reloadable(c)) + return 0; + + if (import_changed || export_changed) + log(L_INFO "Reloading channel %s.%s", c->proto->name, c->name); + + if (import_changed) + channel_request_reload(c); + + if (export_changed) + channel_request_feeding(c); + + return 1; } + +int +proto_configure_channel(struct proto *p, struct channel **pc, struct channel_config *cf) +{ + struct channel *c = *pc; + + if (!c && cf) + { + *pc = proto_add_channel(p, cf); + } + else if (c && !cf) + { + if (c->channel_state != CS_DOWN) + { + log(L_INFO "Cannot remove channel %s.%s", c->proto->name, c->name); + return 0; + } + + proto_remove_channel(p, c); + *pc = NULL; + } + else if (c && cf) + { + if (!channel_reconfigure(c, cf)) + { + log(L_INFO "Cannot reconfigure channel %s.%s", c->proto->name, c->name); + return 0; + } + } + + return 1; +} + + static void -proto_free_ahooks(struct proto *p) +proto_event(void *ptr) { - struct announce_hook *h, *hn; + struct proto *p = ptr; - for(h = p->ahooks; h; h = hn) + if (p->do_start) { - hn = h->next; - mb_free(h); + if_feed_baby(p); + p->do_start = 0; } - p->ahooks = NULL; - p->main_ahook = NULL; + if (p->do_stop) + { + if (p->proto == &proto_unix_iface) + if_flush_ifaces(p); + p->do_stop = 0; + } + + if (proto_is_done(p)) + { + if (p->proto->cleanup) + p->proto->cleanup(p); + + p->active = 0; + proto_log_state_change(p); + proto_rethink_goal(p); + } +} + + +/** + * proto_new - create a new protocol instance + * @c: protocol configuration + * + * When a new configuration has been read in, the core code starts + * initializing all the protocol instances configured by calling their + * init() hooks with the corresponding instance configuration. The initialization + * code of the protocol is expected to create a new instance according to the + * configuration by calling this function and then modifying the default settings + * to values wanted by the protocol. + */ +void * +proto_new(struct proto_config *cf) +{ + struct proto *p = mb_allocz(proto_pool, cf->protocol->proto_size); + + p->cf = cf; + p->debug = cf->debug; + p->mrtdump = cf->mrtdump; + p->name = cf->name; + p->proto = cf->protocol; + p->net_type = cf->net_type; + p->disabled = cf->disabled; + p->hash_key = random_u32(); + cf->proto = p; + + init_list(&p->channels); + + return p; +} + +static struct proto * +proto_init(struct proto_config *c, node *n) +{ + struct protocol *pr = c->protocol; + struct proto *p = pr->init(c); + + p->proto_state = PS_DOWN; + p->last_state_change = now; + insert_node(&p->n, n); + + p->event = ev_new(proto_pool); + p->event->hook = proto_event; + p->event->data = p; + + PD(p, "Initializing%s", p->disabled ? " [disabled]" : ""); + + return p; +} + +static void +proto_start(struct proto *p) +{ + /* Here we cannot use p->cf->name since it won't survive reconfiguration */ + p->pool = rp_new(proto_pool, p->proto->name); + + if (graceful_restart_state == GRS_INIT) + p->gr_recovery = 1; } @@ -263,22 +698,24 @@ proto_free_ahooks(struct proto *p) void * proto_config_new(struct protocol *pr, int class) { - struct proto_config *c = cfg_allocz(pr->config_size); + struct proto_config *cf = cfg_allocz(pr->config_size); if (class == SYM_PROTO) - add_tail(&new_config->protos, &c->n); - c->global = new_config; - c->protocol = pr; - c->name = pr->name; - c->preference = pr->preference; - c->class = class; - c->out_filter = FILTER_REJECT; - c->table = c->global->master_rtc; - c->debug = new_config->proto_default_debug; - c->mrtdump = new_config->proto_default_mrtdump; - return c; + add_tail(&new_config->protos, &cf->n); + + cf->global = new_config; + cf->protocol = pr; + cf->name = pr->name; + cf->class = class; + cf->debug = new_config->proto_default_debug; + cf->mrtdump = new_config->proto_default_mrtdump; + + init_list(&cf->channels); + + return cf; } + /** * proto_copy_config - copy a protocol configuration * @dest: destination protocol configuration @@ -293,6 +730,7 @@ proto_config_new(struct protocol *pr, int class) void proto_copy_config(struct proto_config *dest, struct proto_config *src) { + struct channel_config *cc; node old_node; int old_class; char *old_name; @@ -305,7 +743,7 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src) DBG("Copying configuration from %s to %s\n", src->name, dest->name); - /* + /* * Copy struct proto_config here. Keep original node, class and name. * protocol-specific config copy is handled by protocol copy_config() hook */ @@ -314,12 +752,17 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src) old_class = dest->class; old_name = dest->name; - memcpy(dest, src, sizeof(struct proto_config)); + memcpy(dest, src, src->protocol->config_size); dest->n = old_node; dest->class = old_class; dest->name = old_name; + init_list(&dest->channels); + WALK_LIST(cc, src->channels) + channel_copy_config(cc, dest); + + /* FIXME: allow for undefined copy_config */ dest->protocol->copy_config(dest, src); } @@ -339,66 +782,15 @@ protos_preconfig(struct config *c) init_list(&c->protos); DBG("Protocol preconfig:"); WALK_LIST(p, protocol_list) - { - DBG(" %s", p->name); - p->name_counter = 0; - if (p->preconfig) - p->preconfig(p, c); - } - DBG("\n"); -} - -/** - * protos_postconfig - post-configuration processing - * @c: new configuration - * - * This function calls the postconfig() hooks of all protocol - * instances specified in configuration @c. The hooks are not - * called for protocol templates. - */ -void -protos_postconfig(struct config *c) -{ - struct proto_config *x; - struct protocol *p; - - DBG("Protocol postconfig:"); - WALK_LIST(x, c->protos) - { - DBG(" %s", x->name); - - p = x->protocol; - if (p->postconfig) - p->postconfig(x); - } + { + DBG(" %s", p->name); + p->name_counter = 0; + if (p->preconfig) + p->preconfig(p, c); + } DBG("\n"); } -extern struct protocol proto_unix_iface; - -static struct proto * -proto_init(struct proto_config *c) -{ - struct protocol *p = c->protocol; - struct proto *q = p->init(c); - - q->proto_state = PS_DOWN; - q->core_state = FS_HUNGRY; - q->export_state = ES_DOWN; - q->last_state_change = now; - - add_tail(&initial_proto_list, &q->n); - - if (p == &proto_unix_iface) - initial_device_proto = q; - - add_tail(&proto_list, &q->glob_node); - PD(q, "Initializing%s", q->disabled ? " [disabled]" : ""); - return q; -} - -int proto_reconfig_type; /* Hack to propagate type info to pipe reconfigure hook */ - static int proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type) { @@ -408,74 +800,23 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config /* If there is a too big change in core attributes, ... */ if ((nc->protocol != oc->protocol) || - (nc->disabled != p->disabled) || - (nc->table->table != oc->table->table)) + (nc->net_type != oc->net_type) || + (nc->disabled != p->disabled)) + return 0; + p->name = nc->name; p->debug = nc->debug; p->mrtdump = nc->mrtdump; - proto_reconfig_type = type; + reconfigure_type = type; /* Execute protocol specific reconfigure hook */ - if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc))) + if (!p->proto->reconfigure || !p->proto->reconfigure(p, nc)) return 0; DBG("\t%s: same\n", oc->name); PD(p, "Reconfigured"); p->cf = nc; - p->name = nc->name; - p->preference = nc->preference; - - - /* Multitable protocols handle rest in their reconfigure hooks */ - if (p->proto->multitable) - return 1; - - /* Update filters and limits in the main announce hook - Note that this also resets limit state */ - if (p->main_ahook) - { - struct announce_hook *ah = p->main_ahook; - ah->in_filter = nc->in_filter; - ah->out_filter = nc->out_filter; - ah->rx_limit = nc->rx_limit; - ah->in_limit = nc->in_limit; - ah->out_limit = nc->out_limit; - ah->in_keep_filtered = nc->in_keep_filtered; - proto_verify_limits(ah); - } - - /* Update routes when filters changed. If the protocol in not UP, - it has no routes and we can ignore such changes */ - if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT)) - return 1; - - int import_changed = ! filter_same(nc->in_filter, oc->in_filter); - int export_changed = ! filter_same(nc->out_filter, oc->out_filter); - - /* We treat a change in preferences by reimporting routes */ - if (nc->preference != oc->preference) - import_changed = 1; - - if (import_changed || export_changed) - log(L_INFO "Reloading protocol %s", p->name); - - /* If import filter changed, call reload hook */ - if (import_changed && ! (p->reload_routes && p->reload_routes(p))) - { - /* Now, the protocol is reconfigured. But route reload failed - and we have to do regular protocol restart. */ - log(L_INFO "Restarting protocol %s", p->name); - p->disabled = 1; - p->down_code = PDC_CF_RESTART; - proto_rethink_goal(p); - p->disabled = 0; - proto_rethink_goal(p); - return 1; - } - - if (export_changed) - proto_request_feeding(p); return 1; } @@ -512,85 +853,94 @@ void protos_commit(struct config *new, struct config *old, int force_reconfig, int type) { struct proto_config *oc, *nc; - struct proto *p, *n; struct symbol *sym; + struct proto *p; + node *n; + DBG("protos_commit:\n"); if (old) + { + WALK_LIST(oc, old->protos) { - WALK_LIST(oc, old->protos) - { - p = oc->proto; - sym = cf_find_symbol(new, oc->name); - if (sym && sym->class == SYM_PROTO && !new->shutdown) - { - /* Found match, let's check if we can smoothly switch to new configuration */ - /* No need to check description */ - nc = sym->def; - nc->proto = p; - - /* We will try to reconfigure protocol p */ - if (! force_reconfig && proto_reconfigure(p, oc, nc, type)) - continue; - - /* Unsuccessful, we will restart it */ - if (!p->disabled && !nc->disabled) - log(L_INFO "Restarting protocol %s", p->name); - else if (p->disabled && !nc->disabled) - log(L_INFO "Enabling protocol %s", p->name); - else if (!p->disabled && nc->disabled) - log(L_INFO "Disabling protocol %s", p->name); - - p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART; - p->cf_new = nc; - } - else if (!new->shutdown) - { - log(L_INFO "Removing protocol %s", p->name); - p->down_code = PDC_CF_REMOVE; - p->cf_new = NULL; - } - else /* global shutdown */ - { - p->down_code = PDC_CMD_SHUTDOWN; - p->cf_new = NULL; - } - - p->reconfiguring = 1; - config_add_obstacle(old); - proto_rethink_goal(p); - } + p = oc->proto; + sym = cf_find_symbol(new, oc->name); + if (sym && sym->class == SYM_PROTO && !new->shutdown) + { + /* Found match, let's check if we can smoothly switch to new configuration */ + /* No need to check description */ + nc = sym->def; + nc->proto = p; + + /* We will try to reconfigure protocol p */ + if (! force_reconfig && proto_reconfigure(p, oc, nc, type)) + continue; + + /* Unsuccessful, we will restart it */ + if (!p->disabled && !nc->disabled) + log(L_INFO "Restarting protocol %s", p->name); + else if (p->disabled && !nc->disabled) + log(L_INFO "Enabling protocol %s", p->name); + else if (!p->disabled && nc->disabled) + log(L_INFO "Disabling protocol %s", p->name); + + p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART; + p->cf_new = nc; + } + else if (!new->shutdown) + { + log(L_INFO "Removing protocol %s", p->name); + p->down_code = PDC_CF_REMOVE; + p->cf_new = NULL; + } + else /* global shutdown */ + { + p->down_code = PDC_CMD_SHUTDOWN; + p->cf_new = NULL; + } + + p->reconfiguring = 1; + config_add_obstacle(old); + proto_rethink_goal(p); } + } + struct proto *first_dev_proto = NULL; + + n = NODE &(proto_list.head); WALK_LIST(nc, new->protos) if (!nc->proto) - { - if (old) /* Not a first-time configuration */ - log(L_INFO "Adding protocol %s", nc->name); - proto_init(nc); - } - DBG("\tdone\n"); + { + /* Not a first-time configuration */ + if (old) + log(L_INFO "Adding protocol %s", nc->name); + + p = proto_init(nc, n); + n = NODE p; + + if (p->proto == &proto_unix_iface) + first_dev_proto = p; + } + else + n = NODE nc->proto; DBG("Protocol start\n"); /* Start device protocol first */ - if (initial_device_proto) - { - proto_rethink_goal(initial_device_proto); - initial_device_proto = NULL; - } + if (first_dev_proto) + proto_rethink_goal(first_dev_proto); /* Determine router ID for the first time - it has to be here and not in global_commit() because it is postponed after start of device protocol */ if (!config->router_id) - { - config->router_id = if_choose_router_id(config->router_id_from, 0); - if (!config->router_id) - die("Cannot determine router ID, please configure it manually"); - } + { + config->router_id = if_choose_router_id(config->router_id_from, 0); + if (!config->router_id) + die("Cannot determine router ID, please configure it manually"); + } - /* Start all other protocols */ - WALK_LIST_DELSAFE(p, n, initial_proto_list) + /* Start all new protocols */ + WALK_LIST_DELSAFE(p, n, proto_list) proto_rethink_goal(p); } @@ -600,19 +950,21 @@ proto_rethink_goal(struct proto *p) struct protocol *q; byte goal; - if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) - { - struct proto_config *nc = p->cf_new; - DBG("%s has shut down for reconfiguration\n", p->name); - p->cf->proto = NULL; - config_del_obstacle(p->cf->global); - rem_node(&p->n); - rem_node(&p->glob_node); - mb_free(p); - if (!nc) - return; - p = proto_init(nc); - } + if (p->reconfiguring && !p->active) + { + struct proto_config *nc = p->cf_new; + node *n = p->n.prev; + DBG("%s has shut down for reconfiguration\n", p->name); + p->cf->proto = NULL; + config_del_obstacle(p->cf->global); + proto_remove_channels(p); + rem_node(&p->n); + rfree(p->event); + mb_free(p); + if (!nc) + return; + p = proto_init(nc, n); + } /* Determine what state we want to reach */ if (p->disabled || p->reconfiguring) @@ -621,25 +973,27 @@ proto_rethink_goal(struct proto *p) goal = PS_UP; q = p->proto; - if (goal == PS_UP) /* Going up */ + if (goal == PS_UP) + { + if (!p->active) { - if (p->proto_state == PS_DOWN && p->core_state == FS_HUNGRY) - { - DBG("Kicking %s up\n", p->name); - PD(p, "Starting"); - proto_init_instance(p); - proto_notify_state(p, (q->start ? q->start(p) : PS_UP)); - } + /* Going up */ + DBG("Kicking %s up\n", p->name); + PD(p, "Starting"); + proto_start(p); + proto_notify_state(p, (q->start ? q->start(p) : PS_UP)); } - else /* Going down */ + } + else + { + if (p->proto_state == PS_START || p->proto_state == PS_UP) { - if (p->proto_state == PS_START || p->proto_state == PS_UP) - { - DBG("Kicking %s down\n", p->name); - PD(p, "Shutting down"); - proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN)); - } + /* Going down */ + DBG("Kicking %s down\n", p->name); + PD(p, "Shutting down"); + proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN)); } + } } @@ -661,13 +1015,14 @@ proto_rethink_goal(struct proto *p) * When graceful restart recovery need is detected during initialization, then * enabled protocols are marked with @gr_recovery flag before start. Such * protocols then decide how to proceed with graceful restart, participation is - * voluntary. Protocols could lock the recovery by proto_graceful_restart_lock() - * (stored in @gr_lock flag), which means that they want to postpone the end of - * the recovery until they converge and then unlock it. They also could set - * @gr_wait before advancing to %PS_UP, which means that the core should defer - * route export to that protocol until the end of the recovery. This should be - * done by protocols that expect their neigbors to keep the proper routes - * (kernel table, BGP sessions with BGP graceful restart capability). + * voluntary. Protocols could lock the recovery for each channel by function + * channel_graceful_restart_lock() (starte stored in @gr_lock flag), which means + * that they want to postpone the end of the recovery until they converge and + * then unlock it. They also could set @gr_wait before advancing to %PS_UP, + * which means that the core should defer route export to that channel until + * the end of the recovery. This should be done by protocols that expect their + * neigbors to keep the proper routes (kernel table, BGP sessions with BGP + * graceful restart capability). * * The graceful restart recovery is finished when either all graceful restart * locks are unlocked or when graceful restart wait timer fires. @@ -705,10 +1060,10 @@ graceful_restart_init(void) log(L_INFO "Graceful restart started"); if (!graceful_restart_locks) - { - graceful_restart_done(NULL); - return; - } + { + graceful_restart_done(NULL); + return; + } graceful_restart_state = GRS_ACTIVE; gr_wait_timer = tm_new(proto_pool); @@ -728,30 +1083,30 @@ graceful_restart_init(void) static void graceful_restart_done(struct timer *t UNUSED) { - struct proto *p; - node *n; - log(L_INFO "Graceful restart done"); graceful_restart_state = GRS_DONE; - WALK_LIST2(p, n, proto_list, glob_node) - { - if (!p->gr_recovery) - continue; + struct proto *p; + WALK_LIST(p, proto_list) + { + if (!p->gr_recovery) + continue; + struct channel *c; + WALK_LIST(c, p->channels) + { /* Resume postponed export of routes */ - if ((p->proto_state == PS_UP) && p->gr_wait) - { - proto_want_export_up(p); - proto_log_state_change(p); - } + if ((c->channel_state == CS_UP) && c->gr_wait && c->proto->rt_notify) + channel_start_export(c); /* Cleanup */ - p->gr_recovery = 0; - p->gr_wait = 0; - p->gr_lock = 0; + c->gr_wait = 0; + c->gr_lock = 0; } + p->gr_recovery = 0; + } + graceful_restart_locks = 0; } @@ -762,17 +1117,17 @@ graceful_restart_show_status(void) return; cli_msg(-24, "Graceful restart recovery in progress"); - cli_msg(-24, " Waiting for %d protocols to recover", graceful_restart_locks); + cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks); cli_msg(-24, " Wait timer is %d/%d", tm_remains(gr_wait_timer), config->gr_wait); } /** - * proto_graceful_restart_lock - lock graceful restart by protocol - * @p: protocol instance + * channel_graceful_restart_lock - lock graceful restart by channel + * @p: channel instance * * This function allows a protocol to postpone the end of graceful restart * recovery until it converges. The lock is removed when the protocol calls - * proto_graceful_restart_unlock() or when the protocol is stopped. + * channel_graceful_restart_unlock() or when the channel is closed. * * The function have to be called during the initial phase of graceful restart * recovery and only for protocols that are part of graceful restart (i.e. their @@ -780,32 +1135,32 @@ graceful_restart_show_status(void) * hooks. */ void -proto_graceful_restart_lock(struct proto *p) +channel_graceful_restart_lock(struct channel *c) { ASSERT(graceful_restart_state == GRS_INIT); - ASSERT(p->gr_recovery); + ASSERT(c->proto->gr_recovery); - if (p->gr_lock) + if (c->gr_lock) return; - p->gr_lock = 1; + c->gr_lock = 1; graceful_restart_locks++; } /** - * proto_graceful_restart_unlock - unlock graceful restart by protocol - * @p: protocol instance + * channel_graceful_restart_unlock - unlock graceful restart by channel + * @p: channel instance * - * This function unlocks a lock from proto_graceful_restart_lock(). It is also + * This function unlocks a lock from channel_graceful_restart_lock(). It is also * automatically called when the lock holding protocol went down. */ void -proto_graceful_restart_unlock(struct proto *p) +channel_graceful_restart_unlock(struct channel *c) { - if (!p->gr_lock) + if (!c->gr_lock) return; - p->gr_lock = 0; + c->gr_lock = 0; graceful_restart_locks--; if ((graceful_restart_state == GRS_ACTIVE) && !graceful_restart_locks) @@ -826,34 +1181,26 @@ proto_graceful_restart_unlock(struct proto *p) void protos_dump_all(void) { - struct proto *p; - struct announce_hook *a; - debug("Protocols:\n"); - WALK_LIST(p, active_proto_list) + struct proto *p; + WALK_LIST(p, proto_list) + { + debug(" protocol %s state %s\n", p->name, p_states[p->proto_state]); + + struct channel *c; + WALK_LIST(c, p->channels) { - debug(" protocol %s state %s/%s\n", p->name, - p_states[p->proto_state], c_states[p->core_state]); - for (a = p->ahooks; a; a = a->next) - { - debug("\tTABLE %s\n", a->table->name); - if (a->in_filter) - debug("\tInput filter: %s\n", filter_name(a->in_filter)); - if (a->out_filter != FILTER_REJECT) - debug("\tOutput filter: %s\n", filter_name(a->out_filter)); - } - if (p->disabled) - debug("\tDISABLED\n"); - else if (p->proto->dump) - p->proto->dump(p); + debug("\tTABLE %s\n", c->table->name); + if (c->in_filter) + debug("\tInput filter: %s\n", filter_name(c->in_filter)); + if (c->out_filter) + debug("\tOutput filter: %s\n", filter_name(c->out_filter)); } - WALK_LIST(p, inactive_proto_list) - debug(" inactive %s: state %s/%s\n", p->name, p_states[p->proto_state], c_states[p->core_state]); - WALK_LIST(p, initial_proto_list) - debug(" initial %s\n", p->name); - WALK_LIST(p, flush_proto_list) - debug(" flushing %s\n", p->name); + + if (p->proto->dump && (p->proto_state != PS_DOWN)) + p->proto->dump(p); + } } /** @@ -890,12 +1237,9 @@ extern void bfd_init_all(void); void protos_build(void) { - init_list(&protocol_list); init_list(&proto_list); - init_list(&active_proto_list); - init_list(&inactive_proto_list); - init_list(&initial_proto_list); - init_list(&flush_proto_list); + init_list(&protocol_list); + proto_build(&proto_device); #ifdef CONFIG_RADV proto_build(&proto_radv); @@ -921,136 +1265,10 @@ protos_build(void) #endif proto_pool = rp_new(&root_pool, "Protocols"); - proto_flush_event = ev_new(proto_pool); - proto_flush_event->hook = proto_flush_loop; proto_shutdown_timer = tm_new(proto_pool); proto_shutdown_timer->hook = proto_shutdown_loop; } -static void -proto_feed_more(void *P) -{ - struct proto *p = P; - - if (p->export_state != ES_FEEDING) - return; - - DBG("Feeding protocol %s continued\n", p->name); - if (rt_feed_baby(p)) - { - DBG("Feeding protocol %s finished\n", p->name); - p->export_state = ES_READY; - proto_log_state_change(p); - - if (p->feed_end) - p->feed_end(p); - } - else - { - p->attn->hook = proto_feed_more; - ev_schedule(p->attn); /* Will continue later... */ - } -} - -static void -proto_feed_initial(void *P) -{ - struct proto *p = P; - - if (p->export_state != ES_FEEDING) - return; - - DBG("Feeding protocol %s\n", p->name); - - if_feed_baby(p); - proto_feed_more(P); -} - -static void -proto_schedule_feed(struct proto *p, int initial) -{ - DBG("%s: Scheduling meal\n", p->name); - - p->export_state = ES_FEEDING; - p->refeeding = !initial; - - p->attn->hook = initial ? proto_feed_initial : proto_feed_more; - ev_schedule(p->attn); - - if (p->feed_begin) - p->feed_begin(p, initial); -} - -/* - * 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; - struct announce_hook *h; - - if (flush_loop_state) - return; - flush_loop_state = 1; - - WALK_LIST(p, flush_proto_list) - { - p->flushing = 1; - for (h=p->ahooks; h; h=h->next) - rt_mark_for_prune(h->table); - } - - 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; - } - - rt_prune_sources(); - - 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); - proto_log_state_change(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(); -} - /* Temporary hack to propagate restart to BGP */ int proto_restart; @@ -1060,19 +1278,19 @@ proto_shutdown_loop(struct timer *t UNUSED) { struct proto *p, *p_next; - WALK_LIST_DELSAFE(p, p_next, active_proto_list) + WALK_LIST_DELSAFE(p, p_next, proto_list) if (p->down_sched) - { - proto_restart = (p->down_sched == PDS_RESTART); + { + proto_restart = (p->down_sched == PDS_RESTART); - p->disabled = 1; + p->disabled = 1; + proto_rethink_goal(p); + if (proto_restart) + { + p->disabled = 0; proto_rethink_goal(p); - if (proto_restart) - { - p->disabled = 0; - proto_rethink_goal(p); - } } + } } static inline void @@ -1091,50 +1309,8 @@ proto_schedule_down(struct proto *p, byte restart, byte code) } -/** - * proto_request_feeding - request feeding routes to the protocol - * @p: given protocol - * - * Sometimes it is needed to send again all routes to the - * protocol. This is called feeding and can be requested by this - * function. This would cause protocol export state transition - * to ES_FEEDING (during feeding) and when completed, it will - * switch back to ES_READY. This function can be called even - * when feeding is already running, in that case it is restarted. - */ -void -proto_request_feeding(struct proto *p) -{ - ASSERT(p->proto_state == PS_UP); - - /* Do nothing if we are still waiting for feeding */ - if (p->export_state == ES_DOWN) - return; - - /* If we are already feeding, we want to restart it */ - if (p->export_state == ES_FEEDING) - { - /* Unless feeding is in initial state */ - if (p->attn->hook == proto_feed_initial) - return; - - rt_feed_baby_abort(p); - } - - /* FIXME: This should be changed for better support of multitable protos */ - struct announce_hook *ah; - for (ah = p->ahooks; ah; ah = ah->next) - proto_reset_limit(ah->out_limit); - - /* Hack: reset exp_routes during refeed, and do not decrease it later */ - p->stats.exp_routes = 0; - - proto_schedule_feed(p, 0); - proto_log_state_change(p); -} - static const char * -proto_limit_name(struct proto_limit *l) +channel_limit_name(struct channel_limit *l) { const char *actions[] = { [PLA_WARN] = "warn", @@ -1147,22 +1323,22 @@ proto_limit_name(struct proto_limit *l) } /** - * proto_notify_limit: notify about limit hit and take appropriate action - * @ah: announce hook + * channel_notify_limit: notify about limit hit and take appropriate action + * @c: channel * @l: limit being hit * @dir: limit direction (PLD_*) - * @rt_count: the number of routes + * @rt_count: the number of routes * * The function is called by the route processing core when limit @l * is breached. It activates the limit and tooks appropriate action * according to @l->action. */ void -proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count) +channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count) { const char *dir_name[PLD_MAX] = { "receive", "import" , "export" }; const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT }; - struct proto *p = ah->proto; + struct proto *p = c->proto; if (l->state == PLS_BLOCKED) return; @@ -1170,148 +1346,112 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 /* For warning action, we want the log message every time we hit the limit */ if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit))) log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", - p->name, dir_name[dir], l->limit, proto_limit_name(l)); + p->name, dir_name[dir], l->limit, channel_limit_name(l)); switch (l->action) - { - case PLA_WARN: - l->state = PLS_ACTIVE; - break; - - case PLA_BLOCK: - l->state = PLS_BLOCKED; - break; - - case PLA_RESTART: - case PLA_DISABLE: - l->state = PLS_BLOCKED; - if (p->proto_state == PS_UP) - proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); - break; - } + { + case PLA_WARN: + l->state = PLS_ACTIVE; + break; + + case PLA_BLOCK: + l->state = PLS_BLOCKED; + break; + + case PLA_RESTART: + case PLA_DISABLE: + l->state = PLS_BLOCKED; + if (p->proto_state == PS_UP) + proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]); + break; + } } -void -proto_verify_limits(struct announce_hook *ah) +static void +channel_verify_limits(struct channel *c) { - struct proto_limit *l; - struct proto_stats *stats = ah->stats; - u32 all_routes = stats->imp_routes + stats->filt_routes; + struct channel_limit *l; + u32 all_routes = c->stats.imp_routes + c->stats.filt_routes; - l = ah->rx_limit; - if (l && (all_routes > l->limit)) - proto_notify_limit(ah, l, PLD_RX, all_routes); + l = &c->rx_limit; + if (l->action && (all_routes > l->limit)) + channel_notify_limit(c, l, PLD_RX, all_routes); - l = ah->in_limit; - if (l && (stats->imp_routes > l->limit)) - proto_notify_limit(ah, l, PLD_IN, stats->imp_routes); + l = &c->in_limit; + if (l->action && (c->stats.imp_routes > l->limit)) + channel_notify_limit(c, l, PLD_IN, c->stats.imp_routes); - l = ah->out_limit; - if (l && (stats->exp_routes > l->limit)) - proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes); + l = &c->out_limit; + if (l->action && (c->stats.exp_routes > l->limit)) + channel_notify_limit(c, l, PLD_OUT, c->stats.exp_routes); } - -static void -proto_want_core_up(struct proto *p) +static inline void +channel_reset_limit(struct channel_limit *l) { - ASSERT(p->core_state == FS_HUNGRY); - - if (!p->proto->multitable) - { - p->main_source = rt_get_source(p, 0); - rt_lock_source(p->main_source); - - /* Connect protocol to routing table */ - p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats); - p->main_ahook->in_filter = p->cf->in_filter; - p->main_ahook->out_filter = p->cf->out_filter; - p->main_ahook->rx_limit = p->cf->rx_limit; - p->main_ahook->in_limit = p->cf->in_limit; - p->main_ahook->out_limit = p->cf->out_limit; - p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered; - - proto_reset_limit(p->main_ahook->rx_limit); - proto_reset_limit(p->main_ahook->in_limit); - proto_reset_limit(p->main_ahook->out_limit); - } - - p->core_state = FS_HAPPY; - proto_relink(p); + if (l->action) + l->state = PLS_INITIAL; } -static void -proto_want_export_up(struct proto *p) +static inline void +proto_do_start(struct proto *p) { - ASSERT(p->core_state == FS_HAPPY); - ASSERT(p->export_state == ES_DOWN); - - proto_link_ahooks(p); - proto_schedule_feed(p, 1); /* Sets ES_FEEDING */ + p->active = 1; + p->do_start = 1; + ev_schedule(p->event); } static void -proto_want_export_down(struct proto *p) +proto_do_up(struct proto *p) { - ASSERT(p->export_state != ES_DOWN); - - /* Need to abort feeding */ - if (p->export_state == ES_FEEDING) - rt_feed_baby_abort(p); + if (!p->main_source) + { + p->main_source = rt_get_source(p, 0); + rt_lock_source(p->main_source); + } - p->export_state = ES_DOWN; - p->stats.exp_routes = 0; - proto_unlink_ahooks(p); + proto_start_channels(p); } -static void -proto_want_core_down(struct proto *p) +static inline void +proto_do_pause(struct proto *p) { - ASSERT(p->core_state == FS_HAPPY); - ASSERT(p->export_state == ES_DOWN); - - p->core_state = FS_FLUSHING; - proto_relink(p); - proto_schedule_flush_loop(); - - if (!p->proto->multitable) - { - rt_unlock_source(p->main_source); - p->main_source = NULL; - } + proto_pause_channels(p); } static void -proto_falling_down(struct proto *p) +proto_do_stop(struct proto *p) { + p->down_sched = 0; p->gr_recovery = 0; - p->gr_wait = 0; - if (p->gr_lock) - proto_graceful_restart_unlock(p); -} -static void -proto_fell_down(struct proto *p) -{ - DBG("Protocol %s down\n", p->name); - - u32 all_routes = p->stats.imp_routes + p->stats.filt_routes; - if (all_routes != 0) - log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes); + p->do_stop = 1; + ev_schedule(p->event); - bzero(&p->stats, sizeof(struct proto_stats)); - proto_free_ahooks(p); - - if (! p->proto->multitable) - rt_unlock_table(p->table); + if (p->main_source) + { + rt_unlock_source(p->main_source); + p->main_source = NULL; + } - if (p->proto->cleanup) - p->proto->cleanup(p); + proto_stop_channels(p); +} - proto_rethink_goal(p); +static void +proto_do_down(struct proto *p) +{ + p->down_code = 0; + neigh_prune(); + rfree(p->pool); + p->pool = NULL; + + /* Shutdown is finished in the protocol event */ + if (proto_is_done(p)) + ev_schedule(p->event); } + /** * proto_notify_state - notify core about protocol state change * @p: protocol the state of which has changed @@ -1327,78 +1467,53 @@ proto_fell_down(struct proto *p) * it should be used at tail positions of protocol callbacks. */ void -proto_notify_state(struct proto *p, unsigned ps) +proto_notify_state(struct proto *p, uint state) { - unsigned ops = p->proto_state; - unsigned cs = p->core_state; - unsigned es = p->export_state; + uint ps = p->proto_state; - DBG("%s reporting state transition %s/%s -> */%s\n", p->name, c_states[cs], p_states[ops], p_states[ps]); - if (ops == ps) + DBG("%s reporting state transition %s -> %s\n", p->name, p_states[ps], p_states[state]); + if (state == ps) return; - p->proto_state = ps; + p->proto_state = state; p->last_state_change = now; - switch (ps) - { - case PS_START: - ASSERT(ops == PS_DOWN || ops == PS_UP); - ASSERT(cs == FS_HUNGRY || cs == FS_HAPPY); - - if (es != ES_DOWN) - proto_want_export_down(p); - break; - - case PS_UP: - ASSERT(ops == PS_DOWN || ops == PS_START); - ASSERT(cs == FS_HUNGRY || cs == FS_HAPPY); - ASSERT(es == ES_DOWN); - - if (cs == FS_HUNGRY) - proto_want_core_up(p); - if (!p->gr_wait) - proto_want_export_up(p); - break; - - case PS_STOP: - ASSERT(ops == PS_START || ops == PS_UP); - - p->down_sched = 0; - - if (es != ES_DOWN) - proto_want_export_down(p); - if (cs == FS_HAPPY) - proto_want_core_down(p); - proto_falling_down(p); - break; - - case PS_DOWN: - p->down_code = 0; - p->down_sched = 0; - - if (es != ES_DOWN) - proto_want_export_down(p); - if (cs == FS_HAPPY) - proto_want_core_down(p); - if (ops != PS_STOP) - proto_falling_down(p); - - neigh_prune(); // FIXME convert neighbors to resource? - rfree(p->pool); - p->pool = NULL; - - if (cs == FS_HUNGRY) /* Shutdown finished */ - { - proto_log_state_change(p); - proto_fell_down(p); - return; /* The protocol might have ceased to exist */ - } - break; - - default: - bug("%s: Invalid state %d", p->name, ps); - } + switch (state) + { + case PS_START: + ASSERT(ps == PS_DOWN || ps == PS_UP); + + if (ps == PS_DOWN) + proto_do_start(p); + else + proto_do_pause(p); + break; + + case PS_UP: + ASSERT(ps == PS_DOWN || ps == PS_START); + + if (ps == PS_DOWN) + proto_do_start(p); + + proto_do_up(p); + break; + + case PS_STOP: + ASSERT(ps == PS_START || ps == PS_UP); + + proto_do_stop(p); + break; + + case PS_DOWN: + if (ps != PS_STOP) + proto_do_stop(p); + + proto_do_down(p); + break; + + default: + bug("%s: Invalid state %d", p->name, ps); + } proto_log_state_change(p); } @@ -1410,82 +1525,73 @@ proto_notify_state(struct proto *p, unsigned ps) static char * proto_state_name(struct proto *p) { -#define P(x,y) ((x << 4) | y) - switch (P(p->proto_state, p->core_state)) - { - case P(PS_DOWN, FS_HUNGRY): return "down"; - case P(PS_START, FS_HUNGRY): - case P(PS_START, FS_HAPPY): return "start"; - case P(PS_UP, FS_HAPPY): - switch (p->export_state) - { - case ES_DOWN: return "wait"; - case ES_FEEDING: return "feed"; - case ES_READY: return "up"; - default: return "???"; - } - case P(PS_STOP, FS_HUNGRY): - case P(PS_STOP, FS_FLUSHING): return "stop"; - case P(PS_DOWN, FS_FLUSHING): return "flush"; - default: return "???"; - } -#undef P + switch (p->proto_state) + { + case PS_DOWN: return p->active ? "flush" : "down"; + case PS_START: return "start"; + case PS_UP: return "up"; + case PS_STOP: return "stop"; + default: return "???"; + } } static void -proto_show_stats(struct proto_stats *s, int in_keep_filtered) +channel_show_stats(struct channel *c) { - if (in_keep_filtered) - cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", - s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes); + struct proto_stats *s = &c->stats; + + if (c->in_keep_filtered) + cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported", + s->imp_routes, s->filt_routes, s->exp_routes); else - cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", - s->imp_routes, s->exp_routes, s->pref_routes); + cli_msg(-1006, " Routes: %u imported, %u exported", + s->imp_routes, s->exp_routes); - cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); - cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", + cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); + cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", s->imp_updates_received, s->imp_updates_invalid, s->imp_updates_filtered, s->imp_updates_ignored, s->imp_updates_accepted); - cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", + cli_msg(-1006, " Import withdraws: %10u %10u --- %10u %10u", s->imp_withdraws_received, s->imp_withdraws_invalid, s->imp_withdraws_ignored, s->imp_withdraws_accepted); - cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u", + cli_msg(-1006, " Export updates: %10u %10u %10u --- %10u", s->exp_updates_received, s->exp_updates_rejected, s->exp_updates_filtered, s->exp_updates_accepted); - cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u", + cli_msg(-1006, " Export withdraws: %10u --- --- --- %10u", s->exp_withdraws_received, s->exp_withdraws_accepted); } void -proto_show_limit(struct proto_limit *l, const char *dsc) +channel_show_limit(struct channel_limit *l, const char *dsc) { - if (!l) + if (!l->action) return; - cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : ""); - cli_msg(-1006, " Action: %s", proto_limit_name(l)); + cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : ""); + cli_msg(-1006, " Action: %s", channel_limit_name(l)); } void -proto_show_basic_info(struct proto *p) +channel_show_info(struct channel *c) { - // cli_msg(-1006, " Table: %s", p->table->name); - cli_msg(-1006, " Preference: %d", p->preference); - cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter)); - cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter)); + cli_msg(-1006, " Channel %s", c->name); + cli_msg(-1006, " Table: %s", c->table->name); + cli_msg(-1006, " Preference: %d", c->preference); + cli_msg(-1006, " Input filter: %s", filter_name(c->in_filter)); + cli_msg(-1006, " Output filter: %s", filter_name(c->out_filter)); if (graceful_restart_state == GRS_ACTIVE) - cli_msg(-1006, " GR recovery: %s%s", - p->gr_lock ? " pending" : "", - p->gr_wait ? " waiting" : ""); + cli_msg(-1006, " GR recovery: %s%s", + c->gr_lock ? " pending" : "", + c->gr_wait ? " waiting" : ""); - proto_show_limit(p->cf->rx_limit, "Receive limit:"); - proto_show_limit(p->cf->in_limit, "Import limit:"); - proto_show_limit(p->cf->out_limit, "Export limit:"); + channel_show_limit(&c->rx_limit, "Receive limit:"); + channel_show_limit(&c->in_limit, "Import limit:"); + channel_show_limit(&c->out_limit, "Export limit:"); - if (p->proto_state != PS_DOWN) - proto_show_stats(&p->stats, p->cf->in_keep_filtered); + if (c->channel_state != CS_DOWN) + channel_show_stats(c); } void @@ -1504,34 +1610,39 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt) cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s", p->name, p->proto->name, - p->table->name, + p->main_channel ? p->main_channel->table->name : "---", proto_state_name(p), tbuf, buf); + if (verbose) + { + if (p->cf->dsc) + cli_msg(-1006, " Description: %s", p->cf->dsc); + if (p->cf->router_id) + cli_msg(-1006, " Router ID: %R", p->cf->router_id); + + if (p->proto->show_proto_info) + p->proto->show_proto_info(p); + else { - if (p->cf->dsc) - cli_msg(-1006, " Description: %s", p->cf->dsc); - if (p->cf->router_id) - cli_msg(-1006, " Router ID: %R", p->cf->router_id); - - if (p->proto->show_proto_info) - p->proto->show_proto_info(p); - else - proto_show_basic_info(p); - - cli_msg(-1006, ""); + struct channel *c; + WALK_LIST(c, p->channels) + channel_show_info(c); } + + cli_msg(-1006, ""); + } } void proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED) { if (p->disabled) - { - cli_msg(-8, "%s: already disabled", p->name); - return; - } + { + cli_msg(-8, "%s: already disabled", p->name); + return; + } log(L_INFO "Disabling protocol %s", p->name); p->disabled = 1; @@ -1544,10 +1655,10 @@ void proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED) { if (!p->disabled) - { - cli_msg(-10, "%s: already enabled", p->name); - return; - } + { + cli_msg(-10, "%s: already enabled", p->name); + return; + } log(L_INFO "Enabling protocol %s", p->name); p->disabled = 0; @@ -1559,10 +1670,10 @@ void proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) { if (p->disabled) - { - cli_msg(-8, "%s: already disabled", p->name); - return; - } + { + cli_msg(-8, "%s: already disabled", p->name); + return; + } log(L_INFO "Restarting protocol %s", p->name); p->disabled = 1; @@ -1576,41 +1687,38 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) void proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED) { + struct channel *c; + if (p->disabled) - { - cli_msg(-8, "%s: already disabled", p->name); - return; - } + { + cli_msg(-8, "%s: already disabled", p->name); + return; + } /* If the protocol in not UP, it has no routes */ if (p->proto_state != PS_UP) return; + /* All channels must support reload */ + if (dir != CMD_RELOAD_OUT) + WALK_LIST(c, p->channels) + if (!channel_reloadable(c)) + { + cli_msg(-8006, "%s: reload failed", p->name); + return; + } + log(L_INFO "Reloading protocol %s", p->name); /* re-importing routes */ if (dir != CMD_RELOAD_OUT) - { - if (! (p->reload_routes && p->reload_routes(p))) - { - cli_msg(-8006, "%s: reload failed", p->name); - return; - } - - /* - * Should be done before reload_routes() hook? - * Perhaps, but these hooks work asynchronously. - */ - if (!p->proto->multitable) - { - proto_reset_limit(p->main_ahook->rx_limit); - proto_reset_limit(p->main_ahook->in_limit); - } - } + WALK_LIST(c, p->channels) + channel_request_reload(c); /* re-exporting routes */ if (dir != CMD_RELOAD_IN) - proto_request_feeding(p); + WALK_LIST(c, p->channels) + channel_request_feeding(c); cli_msg(-15, "%s: reloading", p->name); } @@ -1631,10 +1739,10 @@ static void proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg) { if (s->class != SYM_PROTO) - { - cli_msg(9002, "%s is not a protocol", s->name); - return; - } + { + cli_msg(9002, "%s is not a protocol", s->name); + return; + } cmd(((struct proto_config *)s->def)->proto, arg, 0); cli_msg(0, ""); @@ -1643,16 +1751,12 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int) static void proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg) { + struct proto *p; int cnt = 0; - node *nn; - WALK_LIST(nn, proto_list) - { - struct proto *p = SKIP_BACK(struct proto, glob_node, nn); - - if (!patt || patmatch(patt, p->name)) - cmd(p, arg, cnt++); - } + WALK_LIST(p, proto_list) + if (!patt || patmatch(patt, p->name)) + cmd(p, arg, cnt++); if (!cnt) cli_msg(8003, "No protocols match"); @@ -1679,25 +1783,27 @@ proto_get_named(struct symbol *sym, struct protocol *pr) struct proto *p, *q; if (sym) - { - if (sym->class != SYM_PROTO) - cf_error("%s: Not a protocol", sym->name); - p = ((struct proto_config *)sym->def)->proto; - if (!p || p->proto != pr) - cf_error("%s: Not a %s protocol", sym->name, pr->name); - } + { + if (sym->class != SYM_PROTO) + cf_error("%s: Not a protocol", sym->name); + + p = ((struct proto_config *) sym->def)->proto; + if (!p || p->proto != pr) + cf_error("%s: Not a %s protocol", sym->name, pr->name); + } else - { - p = NULL; - WALK_LIST(q, active_proto_list) - if (q->proto == pr) - { - if (p) - cf_error("There are multiple %s protocols running", pr->name); - p = q; - } - if (!p) - cf_error("There is no %s protocol running", pr->name); - } + { + p = NULL; + WALK_LIST(q, proto_list) + if ((q->proto == pr) && (q->proto_state != PS_DOWN)) + { + if (p) + cf_error("There are multiple %s protocols running", pr->name); + p = q; + } + if (!p) + cf_error("There is no %s protocol running", pr->name); + } + return p; } diff --git a/nest/proto.sgml b/nest/proto.sgml index 1d4c31a7..53da78b8 100644 --- a/nest/proto.sgml +++ b/nest/proto.sgml @@ -69,23 +69,6 @@ its state by calling the <func/proto_notify_state/ function. <p>At any time, the core code can ask the protocol to shut itself down by calling its stop() hook. -<p>The <em/core state machine/ takes care of the core view of protocol state. -The states are traversed according to changes of the protocol state machine, but -sometimes the transitions are delayed if the core needs to finish some actions -(for example sending of new routes to the protocol) before proceeding to the -new state. There are the following core states: - -<descrip> - <tag/FS_HUNGRY/ The protocol is down, it doesn't have any routes and - doesn't want them. - <tag/FS_FEEDING/ The protocol has reached the <tt/PS_UP/ state, but - we are still busy sending the initial set of routes to it. - <tag/FS_HAPPY/ The protocol is up and has complete routing information. - <tag/FS_FLUSHING/ The protocol is shutting down (it's in either <tt/PS_STOP/ - or <tt/PS_DOWN/ state) and we're flushing all of its routes from the - routing tables. -</descrip> - <sect1>Functions of the protocol module <p>The protocol module provides the following functions: diff --git a/nest/protocol.h b/nest/protocol.h index 8c49154f..19f5d070 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -11,7 +11,9 @@ #include "lib/lists.h" #include "lib/resource.h" +#include "lib/event.h" #include "lib/timer.h" +#include "nest/route.h" #include "conf/conf.h" struct iface; @@ -22,13 +24,16 @@ struct neighbor; struct rta; struct network; struct proto_config; +struct channel_limit; +struct channel_config; struct config; struct proto; -struct event; +struct channel; struct ea_list; struct eattr; struct symbol; + /* * Routing Protocol */ @@ -39,9 +44,10 @@ struct protocol { char *template; /* Template for automatic generation of names */ int name_counter; /* Counter for automatic name generation */ int attr_class; /* Attribute class known to this protocol */ - int multitable; /* Protocol handles all announce hooks itself */ uint preference; /* Default protocol preference */ - uint config_size; /* Size of protocol config */ + uint channel_mask; /* Mask of accepted channel types (NB_*) */ + uint proto_size; /* Size of protocol data structure */ + uint config_size; /* Size of protocol config data structure */ void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */ void (*postconfig)(struct proto_config *); /* After configuring each instance */ @@ -62,7 +68,6 @@ struct protocol { 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, int type); void protos_dump_all(void); @@ -90,16 +95,12 @@ struct proto_config { char *name; char *dsc; int class; /* SYM_PROTO or SYM_TEMPLATE */ + u8 net_type; /* Protocol network type (NET_*), 0 for undefined */ + u8 disabled; /* Protocol enabled/disabled by default */ u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */ - unsigned preference, disabled; /* Generic parameters */ - int in_keep_filtered; /* Routes rejected in import filter are kept */ u32 router_id; /* Protocol specific router ID */ - struct rtable_config *table; /* Table we're attached to */ - struct filter *in_filter, *out_filter; /* Attached filters */ - struct proto_limit *rx_limit; /* Limit for receiving routes from protocol - (relevant when in_keep_filtered is active) */ - struct proto_limit *in_limit; /* Limit for importing routes from protocol */ - struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ + + list channels; /* List of channel configs (struct channel_config) */ /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */ @@ -111,7 +112,6 @@ struct proto_stats { /* Import - from protocol to core */ u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */ u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */ - u32 pref_routes; /* Number of routes that are preferred, sum over all routing tables */ 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 */ @@ -133,36 +133,34 @@ struct proto_stats { }; struct proto { - node n; /* Node in *_proto_list */ - node glob_node; /* Node in global proto_list */ + node n; /* Node in global proto_list */ struct protocol *proto; /* Protocol */ struct proto_config *cf; /* Configuration data */ struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */ pool *pool; /* Pool containing local objects */ - struct event *attn; /* "Pay attention" event */ + event *event; /* Protocol event */ + + list channels; /* List of channels to rtables (struct channel) */ + struct channel *main_channel; /* Primary channel */ + struct rte_src *main_source; /* Primary route source */ char *name; /* Name of this instance (== cf->name) */ u32 debug; /* Debugging flags */ u32 mrtdump; /* MRTDump flags */ - unsigned preference; /* Default route preference */ - byte accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */ + uint active_channels; /* Number of active channels */ + byte net_type; /* Protocol network type (NET_*), 0 for undefined */ byte disabled; /* Manually disabled */ byte proto_state; /* Protocol state machine (PS_*, see below) */ - byte core_state; /* Core state machine (FS_*, see below) */ - byte export_state; /* Route export state (ES_*, see below) */ + byte active; /* From PS_START to cleanup after PS_STOP */ + byte do_start; /* Start actions are scheduled */ + byte do_stop; /* Stop actions are scheduled */ byte reconfiguring; /* We're shutting down due to reconfiguration */ - byte refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */ - byte flushing; /* Protocol is flushed in current flush loop round */ byte gr_recovery; /* Protocol should participate in graceful restart recovery */ - byte gr_lock; /* Graceful restart mechanism should wait for this proto */ - byte gr_wait; /* Route export to protocol is postponed until graceful restart */ byte down_sched; /* Shutdown is scheduled for later (PDS_*) */ byte down_code; /* Reason for shutdown (PDC_* codes) */ - byte merge_limit; /* Maximal number of nexthops for RA_MERGED */ 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: @@ -177,23 +175,23 @@ struct proto { * It can construct a new rte, add private attributes and * decide whether the route shall be imported: 1=yes, -1=no, * 0=process it through the import filter set by the user. - * reload_routes Request protocol to reload all its routes to the core + * reload_routes Request channel to reload all its routes to the core * (using rte_update()). Returns: 0=reload cannot be done, * 1= reload is scheduled and will happen (asynchronously). - * feed_begin Notify protocol about beginning of route feeding. - * feed_end Notify protocol about finish of route feeding. + * feed_begin Notify channel about beginning of route feeding. + * feed_end Notify channel about finish of route feeding. */ void (*if_notify)(struct proto *, unsigned flags, struct iface *i); void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); - void (*rt_notify)(struct proto *, struct rtable *table, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs); + void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs); void (*neigh_notify)(struct neighbor *neigh); struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs); int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool); - int (*reload_routes)(struct proto *); - void (*feed_begin)(struct proto *, int initial); - void (*feed_end)(struct proto *); + void (*reload_routes)(struct channel *); + void (*feed_begin)(struct channel *, int initial); + void (*feed_end)(struct channel *); /* * Routing entry hooks (called only for routes belonging to this protocol): @@ -213,14 +211,6 @@ struct proto { void (*rte_insert)(struct network *, struct rte *); void (*rte_remove)(struct network *, struct rte *); - struct rtable *table; /* Our primary routing table */ - struct rte_src *main_source; /* Primary route source */ - struct announce_hook *main_ahook; /* Primary announcement hook */ - struct announce_hook *ahooks; /* Announcement hooks for this protocol */ - - struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */ - struct announce_hook *feed_ahook; /* Announce hook we currently feed */ - /* Hic sunt protocol-specific data */ }; @@ -244,25 +234,20 @@ struct proto_spec { #define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */ -void *proto_new(struct proto_config *, unsigned size); +void *proto_new(struct proto_config *); void *proto_config_new(struct protocol *, int class); void proto_copy_config(struct proto_config *dest, struct proto_config *src); -void proto_request_feeding(struct proto *p); - -static inline void -proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size) -{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); } void graceful_restart_recovery(void); void graceful_restart_init(void); void graceful_restart_show_status(void); -void proto_graceful_restart_lock(struct proto *p); -void proto_graceful_restart_unlock(struct proto *p); +void channel_graceful_restart_lock(struct channel *c); +void channel_graceful_restart_unlock(struct channel *c); #define DEFAULT_GR_WAIT 240 -void proto_show_limit(struct proto_limit *l, const char *dsc); -void proto_show_basic_info(struct proto *p); +void channel_show_limit(struct channel_limit *l, const char *dsc); +void channel_show_info(struct channel *c); void proto_cmd_show(struct proto *, uint, int); void proto_cmd_disable(struct proto *, uint, int); @@ -285,7 +270,10 @@ proto_get_router_id(struct proto_config *pc) return pc->router_id ? pc->router_id : pc->global->router_id; } -extern list active_proto_list; +/* Moved from route.h to avoid dependency conflicts */ +static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_channel, net, new, p->main_source); } + +extern list proto_list; /* * Each protocol instance runs two different state machines: @@ -361,16 +349,6 @@ void proto_notify_state(struct proto *p, unsigned state); * as a result of received ROUTE-REFRESH request). */ -#define FS_HUNGRY 0 -#define FS_FEEDING 1 /* obsolete */ -#define FS_HAPPY 2 -#define FS_FLUSHING 3 - - -#define ES_DOWN 0 -#define ES_FEEDING 1 -#define ES_READY 2 - /* @@ -413,6 +391,7 @@ extern struct proto_config *cf_dev_proto; #define PLD_OUT 2 /* Export limit */ #define PLD_MAX 3 +#define PLA_NONE 0 /* No limit */ #define PLA_WARN 1 /* Issue log warning */ #define PLA_BLOCK 2 /* Block new routes */ #define PLA_RESTART 4 /* Force protocol restart */ @@ -422,42 +401,176 @@ extern struct proto_config *cf_dev_proto; #define PLS_ACTIVE 1 /* Limit was hit */ #define PLS_BLOCKED 2 /* Limit is active and blocking new routes */ -struct proto_limit { +struct channel_limit { u32 limit; /* Maximum number of prefixes */ - byte action; /* Action to take (PLA_*) */ - byte state; /* State of limit (PLS_*) */ + u8 action; /* Action to take (PLA_*) */ + u8 state; /* State of limit (PLS_*) */ }; -void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count); -void proto_verify_limits(struct announce_hook *ah); - -static inline void -proto_reset_limit(struct proto_limit *l) -{ - if (l) - l->state = PLS_INITIAL; -} +void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count); /* - * Route Announcement Hook + * Channels */ -struct announce_hook { +struct channel_class { + uint channel_size; /* Size of channel data structure */ + uint config_size; /* Size of channel config data structure */ + + struct channel * (*init)(struct channel *, struct channel_config *); /* Create new instance */ + int (*reconfigure)(struct channel *, struct channel_config *); /* Try to reconfigure instance, returns success */ + int (*start)(struct channel *); /* Start the instance */ + int (*shutdown)(struct channel *); /* Stop the instance */ + + void (*copy_config)(struct channel_config *, struct channel_config *); /* Copy config from given channel instance */ +#if 0 + void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */ + void (*postconfig)(struct proto_config *); /* After configuring each instance */ + + + void (*dump)(struct proto *); /* Debugging dump */ + void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */ + void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */ + void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */ + void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */ + int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */ + void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */ + +#endif +}; + +struct channel_config { node n; - struct rtable *table; + const char *name; + const struct channel_class *channel; + + struct rtable_config *table; /* Table we're attached to */ + struct filter *in_filter, *out_filter; /* Attached filters */ + struct channel_limit rx_limit; /* Limit for receiving routes from protocol + (relevant when in_keep_filtered is active) */ + struct channel_limit in_limit; /* Limit for importing routes from protocol */ + struct channel_limit out_limit; /* Limit for exporting routes to protocol */ + + u8 net_type; /* Routing table network type (NET_*), 0 for undefined */ + u8 ra_mode; /* Mode of received route advertisements (RA_*) */ + u16 preference; /* Default route preference */ + u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */ + u8 in_keep_filtered; /* Routes rejected in import filter are kept */ +}; + +struct channel { + node n; /* Node in proto->channels */ + node table_node; /* Node in table->channels */ + + const char *name; /* Channel name (may be NULL) */ + const struct channel_class *channel; struct proto *proto; + + struct rtable *table; struct filter *in_filter; /* Input filter */ struct filter *out_filter; /* Output filter */ - struct proto_limit *rx_limit; /* Receive limit (for in_keep_filtered) */ - struct proto_limit *in_limit; /* Input limit */ - struct proto_limit *out_limit; /* Output limit */ - struct proto_stats *stats; /* Per-table protocol statistics */ - struct announce_hook *next; /* Next hook for the same protocol */ - int in_keep_filtered; /* Routes rejected in import filter are kept */ + struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */ + struct channel_limit in_limit; /* Input limit */ + struct channel_limit out_limit; /* Output limit */ + + struct event *feed_event; /* Event responsible for feeding */ + struct fib_iterator feed_fit; /* Routing table iterator used during feeding */ + struct proto_stats stats; /* Per-channel protocol statistics */ + + u8 net_type; /* Routing table network type (NET_*), 0 for undefined */ + u8 ra_mode; /* Mode of received route advertisements (RA_*) */ + u16 preference; /* Default route preference */ + u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */ + u8 in_keep_filtered; /* Routes rejected in import filter are kept */ + u8 disabled; + + u8 channel_state; + u8 export_state; /* Route export state (ES_*, see below) */ + u8 feed_active; + u8 flush_active; + u8 refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */ + u8 reloadable; /* Hook reload_routes() is allowed on the channel */ + u8 gr_lock; /* Graceful restart mechanism should wait for this channel */ + u8 gr_wait; /* Route export to channel is postponed until graceful restart */ + + bird_clock_t last_state_change; /* Time of last state transition */ }; -struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats); -struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t); + +/* + * Channel states + * + * CS_DOWN - The initial and the final state of a channel. There is no route + * exchange between the protocol and the table. Channel is not counted as + * active. Channel keeps a ptr to the table, but do not lock the table and is + * not linked in the table. Generally, new closed channels are created in + * protocols' init() hooks. The protocol is expected to explicitly activate its + * channels (by calling channel_init() or channel_open()). + * + * CS_START - The channel as a connection between the protocol and the table is + * initialized (counted as active by the protocol, linked in the table and keeps + * the table locked), but there is no current route exchange. There still may be + * routes associated with the channel in the routing table if the channel falls + * to CS_START from CS_UP. Generally, channels are initialized in protocols' + * start() hooks when going to PS_START. + * + * CS_UP - The channel is initialized and the route exchange is allowed. Note + * that even in CS_UP state, route export may still be down (ES_DOWN) by the + * core decision (e.g. waiting for table convergence after graceful restart). + * I.e., the protocol decides to open the channel but the core decides to start + * route export. Route import (caused by rte_update() from the protocol) is not + * restricted by that and is on volition of the protocol. Generally, channels + * are opened in protocols' start() hooks when going to PS_UP. + * + * CS_FLUSHING - The transitional state between initialized channel and closed + * channel. The channel is still initialized, but no route exchange is allowed. + * Instead, the associated table is running flush loop to remove routes imported + * through the channel. After that, the channel changes state to CS_DOWN and + * is detached from the table (the table is unlocked and the channel is unlinked + * from it). Unlike other states, the CS_FLUSHING state is not explicitly + * entered or left by the protocol. A protocol may request to close a channel + * (by calling channel_close()), which causes the channel to change state to + * CS_FLUSHING and later to CS_DOWN. Also note that channels are closed + * automatically by the core when the protocol is going down. + * + * Allowed transitions: + * + * CS_DOWN -> CS_START / CS_UP + * CS_START -> CS_UP / CS_FLUSHING + * CS_UP -> CS_START / CS_FLUSHING + * CS_FLUSHING -> CS_DOWN (automatic) + */ + +#define CS_DOWN 0 +#define CS_START 1 +#define CS_UP 2 +#define CS_FLUSHING 3 + +#define ES_DOWN 0 +#define ES_FEEDING 1 +#define ES_READY 2 + + +struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type); +static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc) +{ struct channel_config *cc = HEAD(pc->channels); return NODE_VALID(cc) ? cc : NULL; } + +struct channel *proto_find_channel_by_table(struct proto *p, struct rtable *t); +struct channel *proto_add_channel(struct proto *p, struct channel_config *cf); +int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf); + +void channel_set_state(struct channel *c, uint state); + +/* +static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); } +static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); } +static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); } +*/ + +void channel_request_feeding(struct channel *c); +void *channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto); +int channel_reconfigure(struct channel *c, struct channel_config *cf); + #endif diff --git a/nest/route.h b/nest/route.h index 9368808f..11b08ce5 100644 --- a/nest/route.h +++ b/nest/route.h @@ -12,10 +12,12 @@ #include "lib/lists.h" #include "lib/resource.h" #include "lib/timer.h" -#include "nest/protocol.h" +//#include "nest/protocol.h" +struct ea_list; struct protocol; struct proto; +struct rte_src; struct symbol; struct filter; struct cli; @@ -35,11 +37,8 @@ struct cli; struct fib_node { struct fib_node *next; /* Next in hash chain */ struct fib_iterator *readers; /* List of readers of this node */ - byte pxlen; - byte flags; /* User-defined */ - byte x0, x1; /* User-defined */ - u32 uid; /* Unique ID based on hash */ - ip_addr prefix; /* In host order */ + byte flags; /* User-defined, will be removed */ + net_addr addr[0]; }; struct fib_iterator { /* See lib/slists.h for an explanation */ @@ -50,7 +49,7 @@ struct fib_iterator { /* See lib/slists.h for an explanation */ uint hash; }; -typedef void (*fib_init_func)(struct fib_node *); +typedef void (*fib_init_fn)(void *); struct fib { pool *fib_pool; /* Pool holding all our data */ @@ -58,16 +57,26 @@ struct fib { struct fib_node **hash_table; /* Node hash table */ uint hash_size; /* Number of hash table entries (a power of two) */ uint hash_order; /* Binary logarithm of hash_size */ - uint hash_shift; /* 16 - hash_log */ + uint hash_shift; /* 32 - hash_order */ + uint addr_type; /* Type of address data stored in fib (NET_*) */ + uint node_size; /* FIB node size, 0 for nonuniform */ + uint node_offset; /* Offset of fib_node struct inside of user data */ uint entries; /* Number of entries */ uint entries_min, entries_max; /* Entry count limits (else start rehashing) */ - fib_init_func init; /* Constructor */ + fib_init_fn init; /* Constructor */ }; -void fib_init(struct fib *, pool *, unsigned node_size, unsigned hash_order, fib_init_func init); -void *fib_find(struct fib *, ip_addr *, int); /* Find or return NULL if doesn't exist */ -void *fib_get(struct fib *, ip_addr *, int); /* Find or create new if nonexistent */ -void *fib_route(struct fib *, ip_addr, int); /* Longest-match routing lookup */ +static inline void * fib_node_to_user(struct fib *f, struct fib_node *e) +{ return e ? (void *) ((char *) e - f->node_offset) : NULL; } + +static inline struct fib_node * fib_user_to_node(struct fib *f, void *e) +{ return e ? (void *) ((char *) e + f->node_offset) : NULL; } + +void fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init); +void *fib_find(struct fib *, const net_addr *); /* Find or return NULL if doesn't exist */ +void *fib_get_chain(struct fib *f, const net_addr *a); /* Find first node in linked list from hash table */ +void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */ +void *fib_route(struct fib *, const net_addr *); /* Longest-match routing lookup */ void fib_delete(struct fib *, void *); /* Remove fib entry */ void fib_free(struct fib *); /* Destroy the fib */ void fib_check(struct fib *); /* Consistency check for debugging */ @@ -78,34 +87,37 @@ void fit_put(struct fib_iterator *, struct fib_node *); void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos); -#define FIB_WALK(fib, z) do { \ - struct fib_node *z, **ff = (fib)->hash_table; \ - uint count = (fib)->hash_size; \ - while (count--) \ - for(z = *ff++; z; z=z->next) +#define FIB_WALK(fib, type, z) do { \ + struct fib_node *fn_, **ff_ = (fib)->hash_table; \ + uint count_ = (fib)->hash_size; \ + type *z; \ + while (count_--) \ + for (fn_ = *ff_++; z = fib_node_to_user(fib, fn_); fn_=fn_->next) #define FIB_WALK_END } while (0) #define FIB_ITERATE_INIT(it, fib) fit_init(it, fib) -#define FIB_ITERATE_START(fib, it, z) do { \ - struct fib_node *z = fit_get(fib, it); \ - uint count = (fib)->hash_size; \ - uint hpos = (it)->hash; \ +#define FIB_ITERATE_START(fib, it, type, z) do { \ + struct fib_node *fn_ = fit_get(fib, it); \ + uint count_ = (fib)->hash_size; \ + uint hpos_ = (it)->hash; \ + type *z; \ for(;;) { \ - if (!z) \ + if (!fn_) \ { \ - if (++hpos >= count) \ + if (++hpos_ >= count_) \ break; \ - z = (fib)->hash_table[hpos]; \ + fn_ = (fib)->hash_table[hpos_]; \ continue; \ - } + } \ + z = fib_node_to_user(fib, fn_); -#define FIB_ITERATE_END(z) z = z->next; } } while(0) +#define FIB_ITERATE_END fn_ = fn_->next; } } while(0) -#define FIB_ITERATE_PUT(it, z) fit_put(it, z) +#define FIB_ITERATE_PUT(it) fit_put(it, fn_) -#define FIB_ITERATE_PUT_NEXT(it, fib, z) fit_put_next(fib, it, z, hpos) +#define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_) #define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it) @@ -126,6 +138,7 @@ struct rtable_config { char *name; struct rtable *table; struct proto_config *krt_attached; /* Kernel syncer attached to this table */ + uint addr_type; /* Type of address data stored in table (NET_*) */ int gc_max_ops; /* Maximum number of operations before GC is run */ int gc_min_time; /* Minimum time between two consecutive GC runs */ byte sorted; /* Routes of network are sorted according to rte_better() */ @@ -135,7 +148,8 @@ typedef struct rtable { node n; /* Node in list of all tables */ struct fib fib; char *name; /* Name of this table */ - list hooks; /* List of announcement hooks */ + list channels; /* List of attached channels (struct channel) */ + uint addr_type; /* Type of address data stored in table (NET_*) */ int pipe_busy; /* Pipe loop detection */ int use_count; /* Number of protocols using this table */ struct hostcache *hostcache; @@ -147,7 +161,6 @@ typedef struct rtable { struct event *rt_event; /* Routing table event */ 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 -> scheduled, 2-> running */ byte hcu_scheduled; /* Hostcache update is scheduled */ byte nhu_state; /* Next Hop Update state */ @@ -155,13 +168,9 @@ typedef struct rtable { struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */ } rtable; -#define RPS_NONE 0 -#define RPS_SCHEDULED 1 -#define RPS_RUNNING 2 - typedef struct network { - struct fib_node n; /* FIB flags reserved for kernel syncer */ struct rte *routes; /* Available routes for this network */ + struct fib_node n; /* FIB flags reserved for kernel syncer */ } net; struct hostcache { @@ -194,7 +203,7 @@ struct hostentry { typedef struct rte { struct rte *next; net *net; /* Network this RTE belongs to */ - struct announce_hook *sender; /* Announce hook used to send the route to the routing table */ + struct channel *sender; /* Channel used to send the route to the routing table */ struct rta *attrs; /* Attributes of this route */ byte flags; /* Flags (REF_...) */ byte pflags; /* Protocol-specific flags */ @@ -262,17 +271,19 @@ void rt_commit(struct config *new, struct config *old); void rt_lock_table(rtable *); void rt_unlock_table(rtable *); void rt_setup(pool *, rtable *, char *, struct rtable_config *); -static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_find(&tab->fib, &addr, len); } -static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); } +static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); } +static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); } + rte *rte_find(net *net, struct rte_src *src); rte *rte_get_temp(struct rta *); -void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src); -static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); } +void rte_update2(struct channel *c, net *net, rte *new, struct rte_src *src); +/* rte_update() moved to protocol.h to avoid dependency conflicts */ void rte_discard(rtable *tab, rte *old); -int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter); -rte *rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, struct ea_list **tmpa, int silent); -void rt_refresh_begin(rtable *t, struct announce_hook *ah); -void rt_refresh_end(rtable *t, struct announce_hook *ah); +int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter); +rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, struct ea_list **tmpa, int silent); +void rt_refresh_begin(rtable *t, struct channel *c); +void rt_refresh_end(rtable *t, struct channel *c); +void rt_schedule_prune(rtable *t); void rte_dump(rte *); void rte_free(rte *); rte *rte_do_cow(rte *); @@ -280,29 +291,20 @@ static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r rte *rte_cow_rta(rte *r, linpool *lp); void rt_dump(rtable *); void rt_dump_all(void); -int rt_feed_baby(struct proto *p); -void rt_feed_baby_abort(struct proto *p); -int rt_prune_loop(void); -struct rtable_config *rt_new_table(struct symbol *s); +int rt_feed_channel(struct channel *c); +void rt_feed_channel_abort(struct channel *c); +struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); -static inline void -rt_mark_for_prune(rtable *tab) -{ - if (tab->prune_state == RPS_RUNNING) - fit_get(&tab->fib, &tab->prune_fit); - - tab->prune_state = RPS_SCHEDULED; -} struct rt_show_data { - ip_addr prefix; - unsigned pxlen; + net_addr *addr; rtable *table; struct filter *filter; int verbose; struct fib_iterator fit; struct proto *show_protocol; struct proto *export_protocol; + struct channel *export_channel; int export_mode, primary_only, filtered; struct config *running_on_config; int net_counter, rt_counter, show_counter; @@ -343,22 +345,22 @@ struct rte_src { typedef struct rta { struct rta *next, **pprev; /* Hash chain */ + u32 uc; /* Use count */ + u32 hash_key; /* Hash over important fields */ + struct mpnh *nexthops; /* Next-hops for multipath routes */ + struct ea_list *eattrs; /* Extended Attribute chain */ struct rte_src *src; /* Route source that created the route */ - unsigned uc; /* Use count */ + struct hostentry *hostentry; /* Hostentry for recursive next-hops */ + struct iface *iface; /* Outgoing interface */ + ip_addr gw; /* Next hop */ + ip_addr from; /* Advertising router */ + u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */ byte source; /* Route source (RTS_...) */ byte scope; /* Route scope (SCOPE_... -- see ip.h) */ byte cast; /* Casting type (RTC_...) */ byte dest; /* Route destination type (RTD_...) */ byte flags; /* Route flags (RTF_...), now unused */ byte aflags; /* Attribute cache flags (RTAF_...) */ - u16 hash_key; /* Hash over important fields */ - u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */ - ip_addr gw; /* Next hop */ - ip_addr from; /* Advertising router */ - struct hostentry *hostentry; /* Hostentry for recursive next-hops */ - struct iface *iface; /* Outgoing interface */ - struct mpnh *nexthops; /* Next-hops for multipath routes */ - struct ea_list *eattrs; /* Extended Attribute chain */ } rta; #define RTS_DUMMY 0 /* Dummy route to be removed soon */ @@ -549,87 +551,16 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX]; #define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */ #define DEF_PREF_RIP 120 /* RIP */ #define DEF_PREF_BGP 100 /* BGP */ -#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); - +byte net_roa_check(rtable *tab, const net_addr *n, u32 asn); #endif diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 7fa05d6d..7d9605c2 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -52,18 +52,19 @@ #include "nest/attrs.h" #include "lib/alloca.h" #include "lib/hash.h" +#include "lib/idm.h" #include "lib/resource.h" #include "lib/string.h" +#include <stddef.h> + pool *rta_pool; static slab *rta_slab; static slab *mpnh_slab; static slab *rte_src_slab; -/* rte source ID bitmap */ -static u32 *src_ids; -static u32 src_id_size, src_id_used, src_id_pos; +static struct idm src_ids; #define SRC_ID_INIT_SIZE 4 /* rte source hash */ @@ -87,64 +88,11 @@ rte_src_init(void) { rte_src_slab = sl_new(rta_pool, sizeof(struct rte_src)); - src_id_pos = 0; - src_id_size = SRC_ID_INIT_SIZE; - src_ids = mb_allocz(rta_pool, src_id_size * sizeof(u32)); - - /* ID 0 is reserved */ - src_ids[0] = 1; - src_id_used = 1; + idm_init(&src_ids, rta_pool, SRC_ID_INIT_SIZE); HASH_INIT(src_hash, rta_pool, RSH_INIT_ORDER); } -static inline int u32_cto(uint x) { return ffs(~x) - 1; } - -static inline u32 -rte_src_alloc_id(void) -{ - int i, j; - for (i = src_id_pos; i < src_id_size; i++) - if (src_ids[i] != 0xffffffff) - goto found; - - /* If we are at least 7/8 full, expand */ - if (src_id_used > (src_id_size * 28)) - { - src_id_size *= 2; - src_ids = mb_realloc(src_ids, src_id_size * sizeof(u32)); - bzero(src_ids + i, (src_id_size - i) * sizeof(u32)); - goto found; - } - - for (i = 0; i < src_id_pos; i++) - if (src_ids[i] != 0xffffffff) - goto found; - - ASSERT(0); - - found: - ASSERT(i < 0x8000000); - - src_id_pos = i; - j = u32_cto(src_ids[i]); - - src_ids[i] |= (1 << j); - src_id_used++; - return 32 * i + j; -} - -static inline void -rte_src_free_id(u32 id) -{ - int i = id / 32; - int j = id % 32; - - ASSERT((i < src_id_size) && (src_ids[i] & (1 << j))); - src_ids[i] &= ~(1 << j); - src_id_used--; -} - HASH_DEFINE_REHASH_FN(RSH, struct rte_src) @@ -165,7 +113,7 @@ rt_get_source(struct proto *p, u32 id) src = sl_alloc(rte_src_slab); src->proto = p; src->private_id = id; - src->global_id = rte_src_alloc_id(); + src->global_id = idm_alloc(&src_ids); src->uc = 0; HASH_INSERT2(src_hash, RSH, rta_pool, src); @@ -181,7 +129,7 @@ rt_prune_sources(void) if (src->uc == 0) { HASH_DO_REMOVE(src_hash, RSH, sp); - rte_src_free_id(src->global_id); + idm_free(&src_ids, src->global_id); sl_free(rte_src_slab, src); } } @@ -195,10 +143,10 @@ rt_prune_sources(void) * Multipath Next Hop */ -static inline uint +static inline u32 mpnh_hash(struct mpnh *x) { - uint h = 0; + u32 h = 0; for (; x; x = x->next) h ^= ipa_hash(x->gw); @@ -929,7 +877,8 @@ ea_dump(ea_list *e) inline uint ea_hash(ea_list *e) { - u32 h = 0; + const u64 mul = 0x68576150f3d6847; + u64 h = 0xafcef24eda8b29; int i; if (e) /* Assuming chain of length 1 */ @@ -937,29 +886,18 @@ ea_hash(ea_list *e) for(i=0; i<e->count; i++) { struct eattr *a = &e->attrs[i]; - h ^= a->id; + h ^= a->id; h *= mul; if (a->type & EAF_EMBEDDED) h ^= a->u.data; else { struct adata *d = a->u.ptr; - int size = d->length; - byte *z = d->data; - while (size >= 4) - { - h ^= *(u32 *)z; - z += 4; - size -= 4; - } - while (size--) - h = (h >> 24) ^ (h << 8) ^ *z++; + h ^= mem_hash(d->data, d->length); } + h *= mul; } - h ^= h >> 16; - h ^= h >> 6; - h &= 0xffff; } - return h; + return (h >> 32) ^ (h & 0xffffffff); } /** @@ -1008,8 +946,8 @@ rta_alloc_hash(void) static inline uint rta_hash(rta *a) { - return (((uint) (uintptr_t) a->src) ^ ipa_hash(a->gw) ^ - mpnh_hash(a->nexthops) ^ ea_hash(a->eattrs)) & 0xffff; + return mem_hash(((void *)a) + offsetof(rta, src), sizeof(rta) - offsetof(rta, src)) ^ + mpnh_hash(a->nexthops) ^ ea_hash(a->eattrs); } static inline int diff --git a/nest/rt-dev.c b/nest/rt-dev.c index f6bc1432..a996f4fc 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -24,14 +24,17 @@ #include "lib/resource.h" #include "lib/string.h" + static void -dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) +dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) { - struct rt_dev_config *P = (void *) p->cf; + struct rt_dev_proto *p = (void *) P; + struct rt_dev_config *cf = (void *) P->cf; + struct channel *c; - if (!EMPTY_LIST(P->iface_list) && - !iface_patt_find(&P->iface_list, ad->iface, ad->iface->addr)) - /* Empty list is automagically treated as "*" */ + if (!EMPTY_LIST(cf->iface_list) && + !iface_patt_find(&cf->iface_list, ad->iface, ad->iface->addr)) + /* Empty list is automatically treated as "*" */ return; if (ad->flags & IA_SECONDARY) @@ -40,12 +43,22 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) if (ad->scope <= SCOPE_LINK) return; - if (c & IF_CHANGE_DOWN) + if (ad->prefix.type == NET_IP4) + c = p->ip4_channel; + else if (ad->prefix.type == NET_IP6) + c = p->ip6_channel; + else + return; + + if (!c) + return; + + if (flags & IF_CHANGE_DOWN) { net *n; DBG("dev_if_notify: %s:%I going down\n", ad->iface->name, ad->ip); - n = net_find(p->table, ad->prefix, ad->pxlen); + n = net_find(c->table, &ad->prefix); if (!n) { DBG("dev_if_notify: device shutdown: prefix not found\n"); @@ -53,10 +66,10 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) } /* Use iface ID as local source ID */ - struct rte_src *src = rt_get_source(p, ad->iface->index); - rte_update2(p->main_ahook, n, NULL, src); + struct rte_src *src = rt_get_source(P, ad->iface->index); + rte_update2(c, n, NULL, src); } - else if (c & IF_CHANGE_UP) + else if (flags & IF_CHANGE_UP) { rta *a; net *n; @@ -65,7 +78,7 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) DBG("dev_if_notify: %s:%I going up\n", ad->iface->name, ad->ip); /* Use iface ID as local source ID */ - struct rte_src *src = rt_get_source(p, ad->iface->index); + struct rte_src *src = rt_get_source(P, ad->iface->index); rta a0 = { .src = src, @@ -77,37 +90,51 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad) }; a = rta_lookup(&a0); - n = net_get(p->table, ad->prefix, ad->pxlen); + n = net_get(c->table, &ad->prefix); e = rte_get_temp(a); e->net = n; e->pflags = 0; - rte_update2(p->main_ahook, n, e, src); + rte_update2(c, n, e, src); } } static struct proto * -dev_init(struct proto_config *c) +dev_init(struct proto_config *CF) { - struct proto *p = proto_new(c, sizeof(struct proto)); + struct proto *P = proto_new(CF); + struct rt_dev_proto *p = (void *) P; + // struct rt_dev_config *cf = (void *) CF; - p->ifa_notify = dev_ifa_notify; - return p; + proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)); + proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); + + P->ifa_notify = dev_ifa_notify; + + return P; } static int -dev_reconfigure(struct proto *p, struct proto_config *new) +dev_reconfigure(struct proto *P, struct proto_config *CF) { - struct rt_dev_config *o = (struct rt_dev_config *) p->cf; - struct rt_dev_config *n = (struct rt_dev_config *) new; + struct rt_dev_proto *p = (void *) P; + struct rt_dev_config *o = (void *) P->cf; + struct rt_dev_config *n = (void *) CF; + + if (!iface_patts_equal(&o->iface_list, &n->iface_list, NULL)) + return 0; + + return + proto_configure_channel(P, &p->ip4_channel, proto_cf_find_channel(CF, NET_IP4)) && + proto_configure_channel(P, &p->ip6_channel, proto_cf_find_channel(CF, NET_IP6)); - return iface_patts_equal(&o->iface_list, &n->iface_list, NULL); + return 1; } static void dev_copy_config(struct proto_config *dest, struct proto_config *src) { - struct rt_dev_config *d = (struct rt_dev_config *) dest; - struct rt_dev_config *s = (struct rt_dev_config *) src; + struct rt_dev_config *d = (void *) dest; + struct rt_dev_config *s = (void *) src; /* * We copy iface_list as ifaces can be shared by more direct protocols. @@ -120,7 +147,9 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src) struct protocol proto_device = { .name = "Direct", .template = "direct%d", - .preference = DEF_PREF_DIRECT, + .preference = DEF_PREF_DIRECT, + .channel_mask = NB_IP, + .proto_size = sizeof(struct rt_dev_proto), .config_size = sizeof(struct rt_dev_config), .init = dev_init, .reconfigure = dev_reconfigure, diff --git a/nest/rt-dev.h b/nest/rt-dev.h index c36d0742..c9012336 100644 --- a/nest/rt-dev.h +++ b/nest/rt-dev.h @@ -14,4 +14,10 @@ struct rt_dev_config { list iface_list; /* list of struct iface_patt */ }; +struct rt_dev_proto { + struct proto p; + struct channel *ip4_channel; + struct channel *ip6_channel; +}; + #endif diff --git a/nest/rt-fib.c b/nest/rt-fib.c index a73de1fd..8021ea24 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -43,16 +43,17 @@ #define HASH_DEF_ORDER 10 #define HASH_HI_MARK *4 #define HASH_HI_STEP 2 -#define HASH_HI_MAX 16 /* Must be at most 16 */ +#define HASH_HI_MAX 16 #define HASH_LO_MARK /5 #define HASH_LO_STEP 2 #define HASH_LO_MIN 10 + static void fib_ht_alloc(struct fib *f) { f->hash_size = 1 << f->hash_order; - f->hash_shift = 16 - f->hash_order; + f->hash_shift = 32 - f->hash_order; if (f->hash_order > HASH_HI_MAX - HASH_HI_STEP) f->entries_max = ~0; else @@ -72,16 +73,9 @@ fib_ht_free(struct fib_node **h) mb_free(h); } -static inline unsigned -fib_hash(struct fib *f, ip_addr *a) -{ - return ipa_hash(*a) >> f->hash_shift; -} -static void -fib_dummy_init(struct fib_node *dummy UNUSED) -{ -} +static u32 +fib_hash(struct fib *f, const net_addr *a); /** * fib_init - initialize a new FIB @@ -96,18 +90,23 @@ fib_dummy_init(struct fib_node *dummy UNUSED) * This function initializes a newly allocated FIB and prepares it for use. */ void -fib_init(struct fib *f, pool *p, unsigned node_size, unsigned hash_order, fib_init_func init) +fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init) { + uint addr_length = net_addr_length[addr_type]; + if (!hash_order) hash_order = HASH_DEF_ORDER; f->fib_pool = p; - f->fib_slab = sl_new(p, node_size); + f->fib_slab = addr_length ? sl_new(p, node_size + addr_length) : NULL; + f->addr_type = addr_type; + f->node_size = node_size; + f->node_offset = node_offset; f->hash_order = hash_order; fib_ht_alloc(f); bzero(f->hash_table, f->hash_size * sizeof(struct fib_node *)); f->entries = 0; f->entries_min = 0; - f->init = init ? : fib_dummy_init; + f->init = init; } static void @@ -133,7 +132,7 @@ fib_rehash(struct fib *f, int step) while (e = x) { x = e->next; - nh = fib_hash(f, &e->prefix); + nh = fib_hash(f, e->addr); while (nh > ni) { *t = NULL; @@ -153,127 +152,201 @@ fib_rehash(struct fib *f, int step) fib_ht_free(m); } +#define CAST(t) (const net_addr_##t *) +#define CAST2(t) (net_addr_##t *) + +#define FIB_HASH(f,a,t) (net_hash_##t(CAST(t) a) >> f->hash_shift) + +#define FIB_FIND(f,a,t) \ + ({ \ + struct fib_node *e = f->hash_table[FIB_HASH(f, a, t)]; \ + while (e && !net_equal_##t(CAST(t) e->addr, CAST(t) a)) \ + e = e->next; \ + fib_node_to_user(f, e); \ + }) + +#define FIB_INSERT(f,a,e,t) \ + ({ \ + u32 h = net_hash_##t(CAST(t) a); \ + struct fib_node **ee = f->hash_table + (h >> f->hash_shift); \ + struct fib_node *g; \ + \ + while ((g = *ee) && (net_hash_##t(CAST(t) g->addr) < h)) \ + ee = &g->next; \ + \ + net_copy_##t(CAST2(t) e->addr, CAST(t) a); \ + e->next = *ee; \ + *ee = e; \ + }) + + +static u32 +fib_hash(struct fib *f, const net_addr *a) +{ + ASSERT(f->addr_type == a->type); + + switch (f->addr_type) + { + case NET_IP4: return FIB_HASH(f, a, ip4); + case NET_IP6: return FIB_HASH(f, a, ip6); + case NET_VPN4: return FIB_HASH(f, a, vpn4); + case NET_VPN6: return FIB_HASH(f, a, vpn6); + case NET_ROA4: return FIB_HASH(f, a, roa4); + case NET_ROA6: return FIB_HASH(f, a, roa6); + default: bug("invalid type"); + } +} + +void * +fib_get_chain(struct fib *f, const net_addr *a) +{ + ASSERT(f->addr_type == a->type); + + struct fib_node *e = f->hash_table[fib_hash(f, a)]; + return e; +} + /** * fib_find - search for FIB node by prefix * @f: FIB to search in - * @a: pointer to IP address of the prefix - * @len: prefix length + * @n: network address * * Search for a FIB node corresponding to the given prefix, return * a pointer to it or %NULL if no such node exists. */ void * -fib_find(struct fib *f, ip_addr *a, int len) +fib_find(struct fib *f, const net_addr *a) { - struct fib_node *e = f->hash_table[fib_hash(f, a)]; - - while (e && (e->pxlen != len || !ipa_equal(*a, e->prefix))) - e = e->next; - return e; + ASSERT(f->addr_type == a->type); + + switch (f->addr_type) + { + case NET_IP4: return FIB_FIND(f, a, ip4); + case NET_IP6: return FIB_FIND(f, a, ip6); + case NET_VPN4: return FIB_FIND(f, a, vpn4); + case NET_VPN6: return FIB_FIND(f, a, vpn6); + case NET_ROA4: return FIB_FIND(f, a, roa4); + case NET_ROA6: return FIB_FIND(f, a, roa6); + default: bug("invalid type"); + } } -/* -int -fib_histogram(struct fib *f) +static void +fib_insert(struct fib *f, const net_addr *a, struct fib_node *e) { - log(L_WARN "Histogram dump start %d %d", f->hash_size, f->entries); - - int i, j; - struct fib_node *e; - - for (i = 0; i < f->hash_size; i++) - { - j = 0; - for (e = f->hash_table[i]; e != NULL; e = e->next) - j++; - if (j > 0) - log(L_WARN "Histogram line %d: %d", i, j); - } - - log(L_WARN "Histogram dump end"); + ASSERT(f->addr_type == a->type); + + switch (f->addr_type) + { + case NET_IP4: FIB_INSERT(f, a, e, ip4); return; + case NET_IP6: FIB_INSERT(f, a, e, ip6); return; + case NET_VPN4: FIB_INSERT(f, a, e, vpn4); return; + case NET_VPN6: FIB_INSERT(f, a, e, vpn6); return; + case NET_ROA4: FIB_INSERT(f, a, e, roa4); return; + case NET_ROA6: FIB_INSERT(f, a, e, roa6); return; + default: bug("invalid type"); + } } -*/ + /** * fib_get - find or create a FIB node * @f: FIB to work with - * @a: pointer to IP address of the prefix - * @len: prefix length + * @n: network address * * Search for a FIB node corresponding to the given prefix and * return a pointer to it. If no such node exists, create it. */ void * -fib_get(struct fib *f, ip_addr *a, int len) +fib_get(struct fib *f, const net_addr *a) { - uint h = ipa_hash(*a); - struct fib_node **ee = f->hash_table + (h >> f->hash_shift); - struct fib_node *g, *e = *ee; - u32 uid = h << 16; - - while (e && (e->pxlen != len || !ipa_equal(*a, e->prefix))) - e = e->next; - if (e) - return e; -#ifdef DEBUGGING - if (len < 0 || len > BITS_PER_IP_ADDRESS || !ip_is_prefix(*a,len)) - bug("fib_get() called for invalid address"); -#endif + void *b = fib_find(f, a); + if (b) + return b; - while ((g = *ee) && g->uid < uid) - ee = &g->next; - while ((g = *ee) && g->uid == uid) - { - ee = &g->next; - uid++; - } + if (f->fib_slab) + b = sl_alloc(f->fib_slab); + else + b = mb_alloc(f->fib_pool, f->node_size + a->length); - if ((uid >> 16) != h) - log(L_ERR "FIB hash table chains are too long"); + struct fib_node *e = fib_user_to_node(f, b); + e->readers = NULL; + e->flags = 0; + fib_insert(f, a, e); - // log (L_WARN "FIB_GET %I %x %x", *a, h, uid); + memset(b, 0, f->node_offset); + if (f->init) + f->init(b); - e = sl_alloc(f->fib_slab); - e->prefix = *a; - e->pxlen = len; - e->next = *ee; - e->uid = uid; - *ee = e; - e->readers = NULL; - f->init(e); if (f->entries++ > f->entries_max) fib_rehash(f, HASH_HI_STEP); - return e; + return b; +} + +static inline void * +fib_route_ip4(struct fib *f, net_addr_ip4 *n) +{ + void *r; + + while (!(r = fib_find(f, (net_addr *) n)) && (n->pxlen > 0)) + { + n->pxlen--; + ip4_clrbit(&n->prefix, n->pxlen); + } + + return r; +} + +static inline void * +fib_route_ip6(struct fib *f, net_addr_ip6 *n) +{ + void *r; + + while (!(r = fib_find(f, (net_addr *) n)) && (n->pxlen > 0)) + { + n->pxlen--; + ip6_clrbit(&n->prefix, n->pxlen); + } + + return r; } /** * fib_route - CIDR routing lookup * @f: FIB to search in - * @a: pointer to IP address of the prefix - * @len: prefix length + * @n: network address * * Search for a FIB node with longest prefix matching the given * network, that is a node which a CIDR router would use for routing * that network. */ void * -fib_route(struct fib *f, ip_addr a, int len) +fib_route(struct fib *f, const net_addr *n) { - ip_addr a0; - void *t; - - while (len >= 0) - { - a0 = ipa_and(a, ipa_mkmask(len)); - t = fib_find(f, &a0, len); - if (t) - return t; - len--; - } - return NULL; + ASSERT(f->addr_type == n->type); + + net_addr *n0 = alloca(n->length); + net_copy(n0, n); + + switch (n->type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return fib_route_ip4(f, (net_addr_ip4 *) n0); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return fib_route_ip6(f, (net_addr_ip6 *) n0); + + default: + return NULL; + } } + static inline void fib_merge_readers(struct fib_iterator *i, struct fib_node *to) { @@ -320,8 +393,8 @@ fib_merge_readers(struct fib_iterator *i, struct fib_node *to) void fib_delete(struct fib *f, void *E) { - struct fib_node *e = E; - uint h = fib_hash(f, &e->prefix); + struct fib_node *e = fib_user_to_node(f, E); + uint h = fib_hash(f, e->addr); struct fib_node **ee = f->hash_table + h; struct fib_iterator *it; @@ -343,7 +416,12 @@ fib_delete(struct fib *f, void *E) } fib_merge_readers(it, l); } - sl_free(f->fib_slab, e); + + if (f->fib_slab) + sl_free(f->fib_slab, E); + else + mb_free(E); + if (f->entries-- < f->entries_min) fib_rehash(f, -HASH_LO_STEP); return; @@ -413,7 +491,7 @@ fit_get(struct fib *f, struct fib_iterator *i) if (k = i->next) k->prev = j; j->next = k; - i->hash = fib_hash(f, &n->prefix); + i->hash = fib_hash(f, n->addr); return n; } @@ -461,6 +539,7 @@ found: void fib_check(struct fib *f) { +#if 0 uint i, ec, lo, nulls; ec = 0; @@ -496,8 +575,32 @@ fib_check(struct fib *f) } if (ec != f->entries) bug("fib_check: invalid entry count (%d != %d)", ec, f->entries); +#endif + return; } +/* +int +fib_histogram(struct fib *f) +{ + log(L_WARN "Histogram dump start %d %d", f->hash_size, f->entries); + + int i, j; + struct fib_node *e; + + for (i = 0; i < f->hash_size; i++) + { + j = 0; + for (e = f->hash_table[i]; e != NULL; e = e->next) + j++; + if (j > 0) + log(L_WARN "Histogram line %d: %d", i, j); + } + + log(L_WARN "Histogram dump end"); +} +*/ + #endif #ifdef TEST @@ -517,7 +620,7 @@ void dump(char *m) struct fib_iterator *j; for(n=f.hash_table[i]; n; n=n->next) { - debug("%04x %04x %p %I/%2d", i, ipa_hash(n->prefix), n, n->prefix, n->pxlen); + debug("%04x %08x %p %N", i, ipa_hash(n->prefix), n, n->addr); for(j=n->readers; j; j=j->next) debug(" %p[%p]", j, j->node); debug("\n"); diff --git a/nest/rt-roa.c b/nest/rt-roa.c deleted file mode 100644 index 0fd89667..00000000 --- a/nest/rt-roa.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * 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(new, 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 57c8b8e0..03ab3b92 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -55,9 +55,7 @@ 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 inline int rt_prune_table(rtable *tab); -static inline void rt_schedule_gc(rtable *tab); -static inline void rt_schedule_prune(rtable *tab); +static inline void rt_prune_table(rtable *tab); static inline struct ea_list * @@ -68,31 +66,132 @@ make_tmp_attrs(struct rte *rt, struct linpool *pool) return mta ? mta(rt, rte_update_pool) : NULL; } + /* Like fib_route(), but skips empty net entries */ -static net * -net_route(rtable *tab, ip_addr a, int len) +static inline void * +net_route_ip4(struct fib *f, net_addr_ip4 *n) { - ip_addr a0; - net *n; + net *r; + + while (r = fib_find(f, (net_addr *) n), + !(r && rte_is_valid(r->routes)) && (n->pxlen > 0)) + { + n->pxlen--; + ip4_clrbit(&n->prefix, n->pxlen); + } + + return r; +} + +static inline void * +net_route_ip6(struct fib *f, net_addr_ip6 *n) +{ + net *r; + + while (r = fib_find(f, (net_addr *) n), + !(r && rte_is_valid(r->routes)) && (n->pxlen > 0)) + { + n->pxlen--; + ip6_clrbit(&n->prefix, n->pxlen); + } + + return r; +} - while (len >= 0) +static byte +net_roa4_check(rtable *tab, const net_addr_ip4 *px, u32 asn) +{ + struct net_addr_roa4 n = NET_ADDR_ROA4(px->prefix, px->pxlen, 0, 0); + byte anything = 0; + + struct fib_node *fn; + while (1) + { + for (fn = fib_get_chain(&tab->fib, (net_addr *) &n); fn; fn = fn->next) { - a0 = ipa_and(a, ipa_mkmask(len)); - n = fib_find(&tab->fib, &a0, len); - if (n && rte_is_valid(n->routes)) - return n; - len--; + net *r = fib_node_to_user(&tab->fib, fn); + if (rte_is_valid(r->routes) && ipa_in_netX(ipa_from_ip4(px->prefix), r->n.addr)) + { + net_addr_roa4 *roa = (void *) r->n.addr; + anything = 1; + if (asn && (roa->asn == asn) && (roa->max_pxlen >= px->pxlen)) + return ROA_VALID; + } } - return NULL; + + if (n.pxlen == 0) + break; + + n.pxlen--; + ip4_clrbit(&n.prefix, n.pxlen); + } + + return anything ? ROA_INVALID : ROA_UNKNOWN; } -static void -rte_init(struct fib_node *N) +static byte +net_roa6_check(rtable *tab, const net_addr_ip6 *px, u32 asn) +{ + struct net_addr_roa6 n = NET_ADDR_ROA6(px->prefix, px->pxlen, 0, 0); + byte anything = 0; + + struct fib_node *fn; + while (1) + { + for (fn = fib_get_chain(&tab->fib, (net_addr *) &n); fn; fn = fn->next) + { + net *r = fib_node_to_user(&tab->fib, fn); + if (rte_is_valid(r->routes) && ipa_in_netX(ipa_from_ip6(px->prefix), r->n.addr)) + { + net_addr_roa6 *roa = (void *) r->n.addr; + anything = 1; + if (asn && (roa->asn == asn) && (roa->max_pxlen >= px->pxlen)) + return ROA_VALID; + } + } + + if (n.pxlen == 0) + break; + + n.pxlen--; + ip6_clrbit(&n.prefix, n.pxlen); + } + + return anything ? ROA_INVALID : ROA_UNKNOWN; +} + +byte +net_roa_check(rtable *tab, const net_addr *n, u32 asn) +{ + if (tab->addr_type == NET_ROA4) + return net_roa4_check(tab, (const net_addr_ip4 *) n, asn); + else + return net_roa6_check(tab, (const net_addr_ip6 *) n, asn); +} + +void * +net_route(rtable *tab, const net_addr *n) { - net *n = (net *) N; + ASSERT(tab->addr_type == n->type); - N->flags = 0; - n->routes = NULL; + net_addr *n0 = alloca(n->length); + net_copy(n0, n); + + switch (n->type) + { + case NET_IP4: + case NET_VPN4: + case NET_ROA4: + return net_route_ip4(&tab->fib, (net_addr_ip4 *) n0); + + case NET_IP6: + case NET_VPN6: + case NET_ROA6: + return net_route_ip6(&tab->fib, (net_addr_ip6 *) n0); + + default: + return NULL; + } } /** @@ -129,7 +228,7 @@ rte_get_temp(rta *a) e->attrs = a; e->flags = 0; - e->pref = a->src->proto->preference; + e->pref = 0; return e; } @@ -227,10 +326,10 @@ rte_mergable(rte *pri, rte *sec) static void rte_trace(struct proto *p, rte *e, int dir, char *msg) { - byte via[STD_ADDRESS_P_LENGTH+32]; + byte via[IPA_MAX_TEXT_LENGTH+32]; rt_format_via(e, via); - log(L_TRACE "%s %c %s %I/%d %s", p->name, dir, msg, e->net->n.prefix, e->net->n.pxlen, via); + log(L_TRACE "%s %c %s %N %s", p->name, dir, msg, e->net->n.addr, via); } static inline void @@ -248,11 +347,11 @@ rte_trace_out(uint flag, struct proto *p, rte *e, char *msg) } static rte * -export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, int silent) +export_filter(struct channel *c, rte *rt0, rte **rt_free, ea_list **tmpa, int silent) { - struct proto *p = ah->proto; - struct filter *filter = ah->out_filter; - struct proto_stats *stats = ah->stats; + struct proto *p = c->proto; + struct filter *filter = c->out_filter; + struct proto_stats *stats = &c->stats; ea_list *tmpb = NULL; rte *rt; int v; @@ -308,10 +407,10 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa, } static void -do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) +do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) { - struct proto *p = ah->proto; - struct proto_stats *stats = ah->stats; + struct proto *p = c->proto; + struct proto_stats *stats = &c->stats; /* @@ -341,11 +440,11 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm * also non-new updates (contrary to import blocking). */ - struct proto_limit *l = ah->out_limit; - if (l && new) + struct channel_limit *l = &c->out_limit; + if (l->action && new) { if ((!old || refeed) && (stats->exp_routes >= l->limit)) - proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes); + channel_notify_limit(c, l, PLD_OUT, stats->exp_routes); if (l->state == PLS_BLOCKED) { @@ -382,25 +481,24 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm rte_trace_out(D_ROUTES, p, old, "removed"); } if (!new) - p->rt_notify(p, ah->table, net, NULL, old, NULL); + p->rt_notify(p, c, net, NULL, old, NULL); else if (tmpa) { ea_list *t = tmpa; while (t->next) t = t->next; t->next = new->attrs->eattrs; - p->rt_notify(p, ah->table, net, new, old, tmpa); + p->rt_notify(p, c, net, new, old, tmpa); t->next = NULL; } else - p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs); + p->rt_notify(p, c, net, new, old, new->attrs->eattrs); } static void -rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int refeed) +rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed) { - struct proto *p = ah->proto; - struct proto_stats *stats = ah->stats; + struct proto *p = c->proto; rte *new = new0; rte *old = old0; @@ -409,9 +507,9 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re ea_list *tmpa = NULL; if (new) - stats->exp_updates_received++; + c->stats.exp_updates_received++; else - stats->exp_withdraws_received++; + c->stats.exp_withdraws_received++; /* * This is a tricky part - we don't know whether route 'old' was @@ -434,10 +532,10 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re */ if (new) - new = export_filter(ah, new, &new_free, &tmpa, 0); + new = export_filter(c, new, &new_free, &tmpa, 0); if (old && !refeed) - old = export_filter(ah, old, &old_free, NULL, 1); + old = export_filter(c, old, &old_free, NULL, 1); if (!new && !old) { @@ -454,13 +552,13 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re #ifdef CONFIG_PIPE if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto)) - p->rt_notify(p, ah->table, net, NULL, old0, NULL); + p->rt_notify(p, c, net, NULL, old0, NULL); #endif return; } - do_rt_notify(ah, net, new, old, tmpa, refeed); + do_rt_notify(c, net, new, old, tmpa, refeed); /* Discard temporary rte's */ if (new_free) @@ -470,10 +568,9 @@ rt_notify_basic(struct announce_hook *ah, net *net, rte *new0, rte *old0, int re } static void -rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed) +rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_changed, rte *before_old, int feed) { - // struct proto *p = ah->proto; - struct proto_stats *stats = ah->stats; + // struct proto *p = c->proto; rte *r; rte *new_best = NULL; @@ -491,14 +588,14 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol was not valid, caller must use NULL for both old_changed and before_old. */ if (new_changed) - stats->exp_updates_received++; + c->stats.exp_updates_received++; else - stats->exp_withdraws_received++; + c->stats.exp_withdraws_received++; /* First, find the new_best route - first accepted by filters */ for (r=net->routes; rte_is_valid(r); r=r->next) { - if (new_best = export_filter(ah, r, &new_free, &tmpa, 0)) + if (new_best = export_filter(c, r, &new_free, &tmpa, 0)) break; /* Note if we walked around the position of old_changed route */ @@ -549,7 +646,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol /* First case */ if (old_meet) - if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1)) + if (old_best = export_filter(c, old_changed, &old_free, NULL, 1)) goto found; /* Second case */ @@ -567,18 +664,18 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol /* Fourth case */ for (r=r->next; rte_is_valid(r); r=r->next) { - if (old_best = export_filter(ah, r, &old_free, NULL, 1)) + if (old_best = export_filter(c, r, &old_free, NULL, 1)) goto found; if (r == before_old) - if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1)) + if (old_best = export_filter(c, old_changed, &old_free, NULL, 1)) goto found; } /* Implicitly, old_best is NULL and new_best is non-NULL */ found: - do_rt_notify(ah, net, new_best, old_best, tmpa, (feed == 2)); + do_rt_notify(c, net, new_best, old_best, tmpa, (feed == 2)); /* Discard temporary rte's */ if (new_free) @@ -597,9 +694,9 @@ mpnh_merge_rta(struct mpnh *nhs, rta *a, int max) } rte * -rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tmpa, int silent) +rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, int silent) { - // struct proto *p = ah->proto; + // struct proto *p = c->proto; struct mpnh *nhs = NULL; rte *best0, *best, *rt0, *rt, *tmp; @@ -609,7 +706,7 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm if (!rte_is_valid(best0)) return NULL; - best = export_filter(ah, best0, rt_free, tmpa, silent); + best = export_filter(c, best0, rt_free, tmpa, silent); if (!best || !rte_is_reachable(best)) return best; @@ -619,13 +716,13 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm if (!rte_mergable(best0, rt0)) continue; - rt = export_filter(ah, rt0, &tmp, NULL, 1); + rt = export_filter(c, rt0, &tmp, NULL, 1); if (!rt) continue; if (rte_is_reachable(rt)) - nhs = mpnh_merge_rta(nhs, rt->attrs, ah->proto->merge_limit); + nhs = mpnh_merge_rta(nhs, rt->attrs, c->merge_limit); if (tmp) rte_free(tmp); @@ -633,7 +730,7 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm if (nhs) { - nhs = mpnh_merge_rta(nhs, best->attrs, ah->proto->merge_limit); + nhs = mpnh_merge_rta(nhs, best->attrs, c->merge_limit); if (nhs->next) { @@ -651,10 +748,10 @@ rt_export_merged(struct announce_hook *ah, net *net, rte **rt_free, ea_list **tm static void -rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, +rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed, rte *new_best, rte*old_best, int refeed) { - // struct proto *p = ah->proto; + // struct proto *p = c->proto; rte *new_best_free = NULL; rte *old_best_free = NULL; @@ -672,31 +769,31 @@ rt_notify_merged(struct announce_hook *ah, net *net, rte *new_changed, rte *old_ if ((new_best == old_best) && !refeed) { new_changed = rte_mergable(new_best, new_changed) ? - export_filter(ah, new_changed, &new_changed_free, NULL, 1) : NULL; + export_filter(c, new_changed, &new_changed_free, NULL, 1) : NULL; old_changed = rte_mergable(old_best, old_changed) ? - export_filter(ah, old_changed, &old_changed_free, NULL, 1) : NULL; + export_filter(c, old_changed, &old_changed_free, NULL, 1) : NULL; if (!new_changed && !old_changed) return; } if (new_best) - ah->stats->exp_updates_received++; + c->stats.exp_updates_received++; else - ah->stats->exp_withdraws_received++; + c->stats.exp_withdraws_received++; /* Prepare new merged route */ if (new_best) - new_best = rt_export_merged(ah, net, &new_best_free, &tmpa, 0); + new_best = rt_export_merged(c, net, &new_best_free, &tmpa, 0); /* Prepare old merged route (without proper merged next hops) */ /* There are some issues with running filter on old route - see rt_notify_basic() */ if (old_best && !refeed) - old_best = export_filter(ah, old_best, &old_best_free, NULL, 1); + old_best = export_filter(c, old_best, &old_best_free, NULL, 1); if (new_best || old_best) - do_rt_notify(ah, net, new_best, old_best, tmpa, refeed); + do_rt_notify(c, net, new_best, old_best, tmpa, refeed); /* Discard temporary rte's */ if (new_best_free) @@ -757,28 +854,22 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, if (!old && !new) return; - if (type == RA_OPTIMAL) - { - if (new) - new->attrs->src->proto->stats.pref_routes++; - if (old) - old->attrs->src->proto->stats.pref_routes--; - - if (tab->hostcache) - rt_notify_hostcache(tab, net); - } + if ((type == RA_OPTIMAL) && tab->hostcache) + rt_notify_hostcache(tab, net); - struct announce_hook *a; - WALK_LIST(a, tab->hooks) + struct channel *c; node *n; + WALK_LIST2(c, n, tab->channels, table_node) { - ASSERT(a->proto->export_state != ES_DOWN); - if (a->proto->accept_ra_types == type) + if (c->export_state == ES_DOWN) + continue; + + if (c->ra_mode == type) if (type == RA_ACCEPTED) - rt_notify_accepted(a, net, new, old, before_old, 0); + rt_notify_accepted(c, net, new, old, before_old, 0); else if (type == RA_MERGED) - rt_notify_merged(a, net, new, old, new_best, old_best, 0); + rt_notify_merged(c, net, new, old, new_best, old_best, 0); else - rt_notify_basic(a, net, new, old, 0); + rt_notify_basic(c, net, new, old, 0); } } @@ -788,20 +879,21 @@ rte_validate(rte *e) int c; net *n = e->net; - if ((n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen)) - { - log(L_WARN "Ignoring bogus prefix %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->sender->proto->name); - return 0; - } + // (n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen)) + if (!net_validate(n->n.addr)) + { + log(L_WARN "Ignoring bogus prefix %N received via %s", + n->n.addr, e->sender->proto->name); + return 0; + } - c = ipa_classify_net(n->n.prefix); + c = net_classify(n->n.addr); if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) - { - log(L_WARN "Ignoring bogus route %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->sender->proto->name); - return 0; - } + { + log(L_WARN "Ignoring bogus route %N received via %s", + n->n.addr, e->sender->proto->name); + return 0; + } return 1; } @@ -841,11 +933,11 @@ rte_same(rte *x, rte *y) static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); } static void -rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) +rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src) { - struct proto *p = ah->proto; - struct rtable *table = ah->table; - struct proto_stats *stats = ah->stats; + struct proto *p = c->proto; + struct rtable *table = c->table; + struct proto_stats *stats = &c->stats; static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS; rte *before_old = NULL; rte *old_best = net->routes; @@ -870,8 +962,8 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr { if (new) { - log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %I/%d to table %s", - net->n.prefix, net->n.pxlen, table->name); + log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s", + net->n.addr, table->name); rte_free_quick(new); } return; @@ -909,13 +1001,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr int new_ok = rte_is_ok(new); int old_ok = rte_is_ok(old); - struct proto_limit *l = ah->rx_limit; - if (l && !old && new) + struct channel_limit *l = &c->rx_limit; + if (l->action && !old && new) { u32 all_routes = stats->imp_routes + stats->filt_routes; if (all_routes >= l->limit) - proto_notify_limit(ah, l, PLD_RX, all_routes); + channel_notify_limit(c, l, PLD_RX, all_routes); if (l->state == PLS_BLOCKED) { @@ -929,11 +1021,11 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr } } - l = ah->in_limit; - if (l && !old_ok && new_ok) + l = &c->in_limit; + if (l->action && !old_ok && new_ok) { if (stats->imp_routes >= l->limit) - proto_notify_limit(ah, l, PLD_IN, stats->imp_routes); + channel_notify_limit(c, l, PLD_IN, stats->imp_routes); if (l->state == PLS_BLOCKED) { @@ -947,13 +1039,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr stats->imp_updates_ignored++; rte_trace_in(D_FILTERS, p, new, "ignored [limit]"); - if (ah->in_keep_filtered) + if (c->in_keep_filtered) new->flags |= REF_FILTERED; else { rte_free_quick(new); new = NULL; } /* Note that old && !new could be possible when - ah->in_keep_filtered changed in the recent past. */ + c->in_keep_filtered changed in the recent past. */ if (!old && !new) return; @@ -1086,7 +1178,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, struct rte_src *sr if (!net->routes && (table->gc_counter++ >= table->config->gc_max_ops) && (table->gc_time + table->config->gc_min_time <= now)) - rt_schedule_gc(table); + rt_schedule_prune(table); if (old_ok && p->rte_remove) p->rte_remove(net, old); @@ -1135,7 +1227,7 @@ rte_unhide_dummy_routes(net *net, rte **dummy) /** * rte_update - enter a new update to a routing table * @table: table to be updated - * @ah: pointer to table announce hook + * @c: channel doing the update * @net: network node * @p: protocol submitting the update * @src: protocol originating the update @@ -1175,18 +1267,23 @@ rte_unhide_dummy_routes(net *net, rte **dummy) */ void -rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) +rte_update2(struct channel *c, net *net, rte *new, struct rte_src *src) { - struct proto *p = ah->proto; - struct proto_stats *stats = ah->stats; - struct filter *filter = ah->in_filter; + struct proto *p = c->proto; + struct proto_stats *stats = &c->stats; + struct filter *filter = c->in_filter; ea_list *tmpa = NULL; rte *dummy = NULL; + ASSERT(c->channel_state == CS_UP); + rte_update_lock(); if (new) { - new->sender = ah; + new->sender = c; + + if (!new->pref) + new->pref = c->preference; stats->imp_updates_received++; if (!rte_validate(new)) @@ -1201,7 +1298,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) stats->imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); - if (! ah->in_keep_filtered) + if (! c->in_keep_filtered) goto drop; /* new is a private copy, i could modify it */ @@ -1219,7 +1316,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) stats->imp_updates_filtered++; rte_trace_in(D_FILTERS, p, new, "filtered out"); - if (! ah->in_keep_filtered) + if (! c->in_keep_filtered) goto drop; new->flags |= REF_FILTERED; @@ -1246,7 +1343,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src) recalc: rte_hide_dummy_routes(net, &dummy); - rte_recalculate(ah, net, new, src); + rte_recalculate(c, net, new, src); rte_unhide_dummy_routes(net, &dummy); rte_update_unlock(); return; @@ -1278,9 +1375,9 @@ rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during gar /* Check rtable for best route to given net whether it would be exported do p */ int -rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter) +rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter) { - net *n = net_find(t, prefix, pxlen); + net *n = net_find(t, a); rte *rt = n ? n->routes : NULL; if (!rte_is_valid(rt)) @@ -1307,28 +1404,25 @@ rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter /** * rt_refresh_begin - start a refresh cycle * @t: related routing table - * @ah: related announce hook + * @c related channel * * This function starts a refresh cycle for given routing table and announce * hook. The refresh cycle is a sequence where the protocol sends all its valid * routes to the routing table (by rte_update()). After that, all protocol - * routes (more precisely routes with @ah as @sender) not sent during the + * routes (more precisely routes with @c as @sender) not sent during the * refresh cycle but still in the table from the past are pruned. This is * implemented by marking all related routes as stale by REF_STALE flag in * rt_refresh_begin(), then marking all related stale routes with REF_DISCARD * flag in rt_refresh_end() and then removing such routes in the prune loop. */ void -rt_refresh_begin(rtable *t, struct announce_hook *ah) +rt_refresh_begin(rtable *t, struct channel *c) { - net *n; - rte *e; - - FIB_WALK(&t->fib, fn) + FIB_WALK(&t->fib, net, n) { - n = (net *) fn; + rte *e; for (e = n->routes; e; e = e->next) - if (e->sender == ah) + if (e->sender == c) e->flags |= REF_STALE; } FIB_WALK_END; @@ -1337,23 +1431,21 @@ rt_refresh_begin(rtable *t, struct announce_hook *ah) /** * rt_refresh_end - end a refresh cycle * @t: related routing table - * @ah: related announce hook + * @c: related channel * - * This function starts a refresh cycle for given routing table and announce + * This function ends a refresh cycle for given routing table and announce * hook. See rt_refresh_begin() for description of refresh cycles. */ void -rt_refresh_end(rtable *t, struct announce_hook *ah) +rt_refresh_end(rtable *t, struct channel *c) { int prune = 0; - net *n; - rte *e; - FIB_WALK(&t->fib, fn) + FIB_WALK(&t->fib, net, n) { - n = (net *) fn; + rte *e; for (e = n->routes; e; e = e->next) - if ((e->sender == ah) && (e->flags & REF_STALE)) + if ((e->sender == c) && (e->flags & REF_STALE)) { e->flags |= REF_DISCARD; prune = 1; @@ -1376,7 +1468,7 @@ void rte_dump(rte *e) { net *n = e->net; - debug("%-1I/%2d ", n->n.prefix, n->n.pxlen); + debug("%-1N ", n->n.addr); debug("KF=%02x PF=%02x pref=%d lm=%d ", n->n.flags, e->pflags, e->pref, now-e->lastmod); rta_dump(e->attrs); if (e->attrs->src->proto->proto->dump_attrs) @@ -1393,23 +1485,17 @@ rte_dump(rte *e) void rt_dump(rtable *t) { - rte *e; - net *n; - struct announce_hook *a; - debug("Dump of routing table <%s>\n", t->name); #ifdef DEBUGGING fib_check(&t->fib); #endif - FIB_WALK(&t->fib, fn) + FIB_WALK(&t->fib, net, n) { - n = (net *) fn; + rte *e; for(e=n->routes; e; e=e->next) rte_dump(e); } FIB_WALK_END; - WALK_LIST(a, t->hooks) - debug("\tAnnounces routes to protocol %s\n", a->proto->name); debug("\n"); } @@ -1428,23 +1514,6 @@ rt_dump_all(void) } static inline void -rt_schedule_prune(rtable *tab) -{ - rt_mark_for_prune(tab); - ev_schedule(tab->rt_event); -} - -static inline void -rt_schedule_gc(rtable *tab) -{ - if (tab->gc_scheduled) - return; - - tab->gc_scheduled = 1; - ev_schedule(tab->rt_event); -} - -static inline void rt_schedule_hcu(rtable *tab) { if (tab->hcu_scheduled) @@ -1464,39 +1533,17 @@ rt_schedule_nhu(rtable *tab) tab->nhu_state |= 1; } - -static void -rt_prune_nets(rtable *tab) +void +rt_schedule_prune(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); + if (tab->prune_state == 0) + ev_schedule(tab->rt_event); - tab->gc_counter = 0; - tab->gc_time = now; - tab->gc_scheduled = 0; + /* state change 0->1, 2->3 */ + tab->prune_state |= 1; } + static void rt_event(void *ptr) { @@ -1509,28 +1556,19 @@ rt_event(void *ptr) rt_next_hop_update(tab); if (tab->prune_state) - if (!rt_prune_table(tab)) - { - /* Table prune unfinished */ - ev_schedule(tab->rt_event); - return; - } - - if (tab->gc_scheduled) - { - rt_prune_nets(tab); - rt_prune_sources(); // FIXME this should be moved to independent event - } + rt_prune_table(tab); } void rt_setup(pool *p, rtable *t, char *name, struct rtable_config *cf) { bzero(t, sizeof(*t)); - fib_init(&t->fib, p, sizeof(net), 0, rte_init); t->name = name; t->config = cf; - init_list(&t->hooks); + t->addr_type = cf ? cf->addr_type : NET_IP4; + fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL); + init_list(&t->channels); + if (cf) { t->rt_event = ev_new(p); @@ -1557,114 +1595,120 @@ rt_init(void) } -static int -rt_prune_step(rtable *tab, int *limit) +/** + * rt_prune_table - prune a routing table + * + * The prune loop scans routing tables and removes routes belonging to flushing + * protocols, discarded routes and also stale network entries. It is called from + * rt_event(). The event is rescheduled if the current iteration do not finish + * the table. The pruning is directed by the prune state (@prune_state), + * specifying whether the prune cycle is scheduled or running, and there + * is also a persistent pruning iterator (@prune_fit). + * + * The prune loop is used also for channel flushing. For this purpose, the + * channels to flush are marked before the iteration and notified after the + * iteration. + */ +static void +rt_prune_table(rtable *tab) { struct fib_iterator *fit = &tab->prune_fit; + int limit = 512; + + struct channel *c; + node *n, *x; DBG("Pruning route table %s\n", tab->name); #ifdef DEBUGGING fib_check(&tab->fib); #endif - if (tab->prune_state == RPS_NONE) - return 1; + if (tab->prune_state == 0) + return; - if (tab->prune_state == RPS_SCHEDULED) - { - FIB_ITERATE_INIT(fit, &tab->fib); - tab->prune_state = RPS_RUNNING; - } + if (tab->prune_state == 1) + { + /* Mark channels to flush */ + WALK_LIST2(c, n, tab->channels, table_node) + if (c->channel_state == CS_FLUSHING) + c->flush_active = 1; + + FIB_ITERATE_INIT(fit, &tab->fib); + tab->prune_state = 2; + } again: - FIB_ITERATE_START(&tab->fib, fit, fn) + FIB_ITERATE_START(&tab->fib, fit, net, n) { - net *n = (net *) fn; rte *e; rescan: for (e=n->routes; e; e=e->next) - if (e->sender->proto->flushing || (e->flags & REF_DISCARD)) + if (e->sender->flush_active || (e->flags & REF_DISCARD)) { - if (*limit <= 0) + if (limit <= 0) { - FIB_ITERATE_PUT(fit, fn); - return 0; + FIB_ITERATE_PUT(fit); + ev_schedule(tab->rt_event); + return; } rte_discard(tab, e); - (*limit)--; + limit--; goto rescan; } + if (!n->routes) /* Orphaned FIB entry */ { - FIB_ITERATE_PUT(fit, fn); - fib_delete(&tab->fib, fn); + FIB_ITERATE_PUT(fit); + fib_delete(&tab->fib, n); goto again; } } - FIB_ITERATE_END(fn); + FIB_ITERATE_END; #ifdef DEBUGGING fib_check(&tab->fib); #endif - tab->prune_state = RPS_NONE; - return 1; -} + tab->gc_counter = 0; + tab->gc_time = now; -/** - * rt_prune_table - prune a routing table - * - * This function scans the routing table @tab and removes routes belonging to - * flushing protocols, discarded routes and also stale network entries, in a - * similar fashion like rt_prune_loop(). Returns 1 when all such routes are - * pruned. Contrary to rt_prune_loop(), this function is not a part of the - * protocol flushing loop, but it is called from rt_event() for just one routing - * table. - * - * Note that rt_prune_table() and rt_prune_loop() share (for each table) the - * prune state (@prune_state) and also the pruning iterator (@prune_fit). - */ -static inline int -rt_prune_table(rtable *tab) -{ - int limit = 512; - return rt_prune_step(tab, &limit); -} + /* state change 2->0, 3->1 */ + tab->prune_state &= 1; -/** - * rt_prune_loop - prune routing tables - * - * The prune loop scans routing tables and removes routes belonging to flushing - * protocols, discarded routes and also stale network entries. Returns 1 when - * all such routes are pruned. It is a part of the protocol flushing loop. - */ -int -rt_prune_loop(void) -{ - int limit = 512; - rtable *t; + if (tab->prune_state > 0) + ev_schedule(tab->rt_event); - WALK_LIST(t, routing_tables) - if (! rt_prune_step(t, &limit)) - return 0; + /* FIXME: This should be handled in a better way */ + rt_prune_sources(); - return 1; + /* Close flushed channels */ + WALK_LIST2_DELSAFE(c, n, x, tab->channels, table_node) + if (c->flush_active) + { + c->flush_active = 0; + struct rtable_config *rtab_cf = c->table->config; + channel_set_state(c, CS_DOWN); /* Can free (struct rtable *) c->table */ + if (rtab_cf->table == NULL) + break; + } + + return; } void rt_preconfig(struct config *c) { - struct symbol *s = cf_get_symbol("master"); - init_list(&c->tables); - c->master_rtc = rt_new_table(s); + + rt_new_table(cf_get_symbol("master4"), NET_IP4); + rt_new_table(cf_get_symbol("master6"), NET_IP6); } -/* +/* * Some functions for handing internal next hop updates * triggered by rt_schedule_nhu(). */ @@ -1796,17 +1840,17 @@ rt_next_hop_update(rtable *tab) tab->nhu_state = 2; } - FIB_ITERATE_START(&tab->fib, fit, fn) + FIB_ITERATE_START(&tab->fib, fit, net, n) { if (max_feed <= 0) { - FIB_ITERATE_PUT(fit, fn); + FIB_ITERATE_PUT(fit); ev_schedule(tab->rt_event); return; } - max_feed -= rt_next_hop_update_net(tab, (net *) fn); + max_feed -= rt_next_hop_update_net(tab, n); } - FIB_ITERATE_END(fn); + FIB_ITERATE_END; /* state change 2->0, 3->1 */ tab->nhu_state &= 1; @@ -1817,19 +1861,28 @@ rt_next_hop_update(rtable *tab) struct rtable_config * -rt_new_table(struct symbol *s) +rt_new_table(struct symbol *s, uint addr_type) { /* Hack that allows to 'redefine' the master table */ - if ((s->class == SYM_TABLE) && (s->def == new_config->master_rtc)) + if ((s->class == SYM_TABLE) && + (s->def == new_config->def_tables[addr_type]) && + ((addr_type == NET_IP4) || (addr_type == NET_IP6))) return s->def; struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config)); cf_define_symbol(s, SYM_TABLE, c); c->name = s->name; - add_tail(&new_config->tables, &c->n); + c->addr_type = addr_type; c->gc_max_ops = 1000; c->gc_min_time = 5; + + add_tail(&new_config->tables, &c->n); + + /* First table of each type is kept as default */ + if (! new_config->def_tables[addr_type]) + new_config->def_tables[addr_type] = c; + return c; } @@ -1934,119 +1987,104 @@ rt_commit(struct config *new, struct config *old) } static inline void -do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) +do_feed_channel(struct channel *c, net *n, rte *e) { rte_update_lock(); - if (type == RA_ACCEPTED) - rt_notify_accepted(h, n, e, NULL, NULL, p->refeeding ? 2 : 1); - else if (type == RA_MERGED) - rt_notify_merged(h, n, NULL, NULL, e, p->refeeding ? e : NULL, p->refeeding); - else - rt_notify_basic(h, n, e, p->refeeding ? e : NULL, p->refeeding); + if (c->ra_mode == RA_ACCEPTED) + rt_notify_accepted(c, n, e, NULL, NULL, c->refeeding ? 2 : 1); + else if (c->ra_mode == RA_MERGED) + rt_notify_merged(c, n, NULL, NULL, e, c->refeeding ? e : NULL, c->refeeding); + else /* RA_BASIC */ + rt_notify_basic(c, n, e, c->refeeding ? e : NULL, c->refeeding); rte_update_unlock(); } /** - * rt_feed_baby - advertise routes to a new protocol - * @p: protocol to be fed + * rt_feed_channel - advertise all routes to a channel + * @c: channel to be fed * - * This function performs one pass of advertisement of routes to a newly - * initialized protocol. It's called by the protocol code as long as it - * has something to do. (We avoid transferring all the routes in single - * pass in order not to monopolize CPU time.) + * This function performs one pass of advertisement of routes to a channel that + * is in the ES_FEEDING state. It is called by the protocol code as long as it + * has something to do. (We avoid transferring all the routes in single pass in + * order not to monopolize CPU time.) */ int -rt_feed_baby(struct proto *p) +rt_feed_channel(struct channel *c) { - struct announce_hook *h; - struct fib_iterator *fit; + struct fib_iterator *fit = &c->feed_fit; int max_feed = 256; - if (!p->feed_ahook) /* Need to initialize first */ + ASSERT(c->export_state == ES_FEEDING); + + if (!c->feed_active) { - if (!p->ahooks) - return 1; - DBG("Announcing routes to new protocol %s\n", p->name); - p->feed_ahook = p->ahooks; - fit = p->feed_iterator = mb_alloc(p->pool, sizeof(struct fib_iterator)); - goto next_hook; + FIB_ITERATE_INIT(fit, &c->table->fib); + c->feed_active = 1; } - fit = p->feed_iterator; -again: - h = p->feed_ahook; - FIB_ITERATE_START(&h->table->fib, fit, fn) + FIB_ITERATE_START(&c->table->fib, fit, net, n) { - net *n = (net *) fn; rte *e = n->routes; if (max_feed <= 0) { - FIB_ITERATE_PUT(fit, fn); + FIB_ITERATE_PUT(fit); return 0; } - /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */ + /* FIXME: perhaps we should change feed for RA_ACCEPTED to not use 'new' */ - if ((p->accept_ra_types == RA_OPTIMAL) || - (p->accept_ra_types == RA_ACCEPTED) || - (p->accept_ra_types == RA_MERGED)) + if ((c->ra_mode == RA_OPTIMAL) || + (c->ra_mode == RA_ACCEPTED) || + (c->ra_mode == RA_MERGED)) if (rte_is_valid(e)) { - if (p->export_state != ES_FEEDING) - return 1; /* In the meantime, the protocol fell down. */ + /* In the meantime, the protocol may fell down */ + if (c->export_state != ES_FEEDING) + goto done; - do_feed_baby(p, p->accept_ra_types, h, n, e); + do_feed_channel(c, n, e); max_feed--; } - if (p->accept_ra_types == RA_ANY) + if (c->ra_mode == RA_ANY) for(e = n->routes; e; e = e->next) { - if (p->export_state != ES_FEEDING) - return 1; /* In the meantime, the protocol fell down. */ + /* In the meantime, the protocol may fell down */ + if (c->export_state != ES_FEEDING) + goto done; if (!rte_is_valid(e)) continue; - do_feed_baby(p, RA_ANY, h, n, e); + do_feed_channel(c, n, e); max_feed--; } } - FIB_ITERATE_END(fn); - p->feed_ahook = h->next; - if (!p->feed_ahook) - { - mb_free(p->feed_iterator); - p->feed_iterator = NULL; - return 1; - } + FIB_ITERATE_END; -next_hook: - h = p->feed_ahook; - FIB_ITERATE_INIT(fit, &h->table->fib); - goto again; +done: + c->feed_active = 0; + return 1; } /** * rt_feed_baby_abort - abort protocol feeding - * @p: protocol + * @c: channel * - * This function is called by the protocol code when the protocol - * stops or ceases to exist before the last iteration of rt_feed_baby() - * has finished. + * This function is called by the protocol code when the protocol stops or + * ceases to exist during the feeding. */ void -rt_feed_baby_abort(struct proto *p) +rt_feed_channel_abort(struct channel *c) { - if (p->feed_ahook) + if (c->feed_active) { - /* Unlink the iterator and exit */ - fit_get(&p->feed_ahook->table->fib, p->feed_iterator); - p->feed_ahook = NULL; + /* Unlink the iterator */ + fit_get(&c->table->fib, &c->feed_fit); + c->feed_active = 0; } } - static inline unsigned ptr_hash(void *ptr) { @@ -2054,10 +2092,10 @@ ptr_hash(void *ptr) return p ^ (p << 8) ^ (p >> 16); } -static inline unsigned +static inline u32 hc_hash(ip_addr a, rtable *dep) { - return (ipa_hash(a) ^ ptr_hash(dep)) & 0xffff; + return ipa_hash(a) ^ ptr_hash(dep); } static inline void @@ -2091,7 +2129,7 @@ hc_alloc_table(struct hostcache *hc, unsigned order) { unsigned hsize = 1 << order; hc->hash_order = order; - hc->hash_shift = 16 - order; + hc->hash_shift = 32 - order; hc->hash_max = (order >= HC_HI_ORDER) ? ~0 : (hsize HC_HI_MARK); hc->hash_min = (order <= HC_LO_ORDER) ? 0 : (hsize HC_LO_MARK); @@ -2192,12 +2230,10 @@ rt_free_hostcache(rtable *tab) static void rt_notify_hostcache(rtable *tab, net *net) { - struct hostcache *hc = tab->hostcache; - if (tab->hcu_scheduled) return; - if (trie_match_prefix(hc->trie, net->n.prefix, net->n.pxlen)) + if (trie_match_net(tab->hostcache->trie, net->n.addr)) rt_schedule_hcu(tab); } @@ -2248,24 +2284,26 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) rta *old_src = he->src; int pxlen = 0; - /* Reset the hostentry */ + /* Reset the hostentry */ he->src = NULL; he->gw = IPA_NONE; he->dest = RTD_UNREACHABLE; he->igp_metric = 0; - net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH); + net_addr he_addr; + net_fill_ip_host(&he_addr, he->addr); + net *n = net_route(tab, &he_addr); if (n) { rte *e = n->routes; rta *a = e->attrs; - pxlen = n->n.pxlen; + pxlen = n->n.addr->pxlen; if (a->hostentry) { /* Recursive route should not depend on another recursive route */ - log(L_WARN "Next hop address %I resolvable through recursive route for %I/%d", - he->addr, n->n.prefix, pxlen); + log(L_WARN "Next hop address %I resolvable through recursive route for %N", + he->addr, n->n.addr); goto done; } @@ -2296,7 +2334,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) done: /* Add a prefix range to the trie */ - trie_add_prefix(tab->hostcache->trie, he->addr, MAX_PREFIX_LENGTH, pxlen, MAX_PREFIX_LENGTH); + trie_add_prefix(tab->hostcache->trie, &he_addr, pxlen, he_addr.pxlen); rta_free(old_src); return old_src != he->src; @@ -2337,7 +2375,7 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) if (!tab->hostcache) rt_init_hostcache(tab); - uint k = hc_hash(a, dep); + u32 k = hc_hash(a, dep); struct hostcache *hc = tab->hostcache; for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next) if (ipa_equal(he->addr, a) && (he->tab == dep)) @@ -2379,7 +2417,8 @@ rt_format_via(rte *e, byte *via) static void rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa) { - byte via[STD_ADDRESS_P_LENGTH+32], from[STD_ADDRESS_P_LENGTH+8]; + byte via[IPA_MAX_TEXT_LENGTH+32]; + byte from[IPA_MAX_TEXT_LENGTH+8]; byte tm[TM_DATETIME_BUFFER_SIZE], info[256]; rta *a = e->attrs; int primary = (e->net->routes == e); @@ -2420,23 +2459,14 @@ static void rt_show_net(struct cli *c, net *n, struct rt_show_data *d) { rte *e, *ee; - byte ia[STD_ADDRESS_P_LENGTH+8]; + byte ia[NET_MAX_TEXT_LENGTH+1]; struct ea_list *tmpa; - struct announce_hook *a = NULL; + struct channel *ec = d->export_channel; int first = 1; int pass = 0; - bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen); + bsprintf(ia, "%N", n->n.addr); - if (d->export_mode) - { - if (! d->export_protocol->rt_notify) - return; - - a = proto_find_announce_hook(d->export_protocol, d->table); - if (!a) - return; - } for (e = n->routes; e; e = e->next) { @@ -2455,10 +2485,10 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) tmpa = make_tmp_attrs(e, rte_update_pool); /* Special case for merged export */ - if ((d->export_mode == RSEM_EXPORT) && (d->export_protocol->accept_ra_types == RA_MERGED)) + if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED)) { rte *rt_free; - e = rt_export_merged(a, n, &rt_free, &tmpa, 1); + e = rt_export_merged(ec, n, &rt_free, &tmpa, 1); pass = 1; if (!e) @@ -2469,7 +2499,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) struct proto *ep = d->export_protocol; int ic = ep->import_control ? ep->import_control(ep, &e, &tmpa, rte_update_pool) : 0; - if (ep->accept_ra_types == RA_OPTIMAL || ep->accept_ra_types == RA_MERGED) + if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED) pass = 1; if (ic < 0) @@ -2483,12 +2513,12 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) * command may change the export filter and do not update routes. */ int do_export = (ic > 0) || - (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); + (f_run(ec->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT); if (do_export != (d->export_mode == RSEM_EXPORT)) goto skip; - if ((d->export_mode == RSEM_EXPORT) && (ep->accept_ra_types == RA_ACCEPTED)) + if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_ACCEPTED)) pass = 1; } } @@ -2517,6 +2547,15 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) } } +static struct channel * +rt_show_export_channel(struct rt_show_data *d) +{ + if (! d->export_protocol->rt_notify) + return NULL; + + return proto_find_channel_by_table(d->export_protocol, d->table); +} + static void rt_show_cont(struct cli *c) { @@ -2529,27 +2568,27 @@ rt_show_cont(struct cli *c) struct fib *fib = &d->table->fib; struct fib_iterator *it = &d->fit; - FIB_ITERATE_START(fib, it, f) + if (d->export_mode) { - net *n = (net *) f; - if (d->running_on_config && d->running_on_config != config) - { - cli_printf(c, 8004, "Stopped due to reconfiguration"); - goto done; - } - if (d->export_protocol && (d->export_protocol->export_state == ES_DOWN)) - { - cli_printf(c, 8005, "Protocol is down"); + /* Ensure we have current export channel */ + d->export_channel = rt_show_export_channel(d); + if (!d->export_channel || (d->export_channel->export_state == ES_DOWN)) + { + cli_printf(c, 8005, "Channel is down"); goto done; } + } + + FIB_ITERATE_START(fib, it, net, n) + { if (!max--) { - FIB_ITERATE_PUT(it, f); + FIB_ITERATE_PUT(it); return; } rt_show_net(c, n, d); } - FIB_ITERATE_END(f); + FIB_ITERATE_END; if (d->stats) cli_printf(c, 14, "%d of %d routes for %d networks", d->show_counter, d->rt_counter, d->net_counter); else @@ -2567,21 +2606,35 @@ rt_show_cleanup(struct cli *c) fit_get(&d->table->fib, &d->fit); } +static inline rtable * +rt_show_get_table(struct proto *p) +{ + /* FIXME: Use a better way to handle multi-channel protocols */ + + if (p->main_channel) + return p->main_channel->table; + + if (!EMPTY_LIST(p->channels)) + return ((struct channel *) HEAD(p->channels))->table; + + return NULL; +} + void rt_show(struct rt_show_data *d) { net *n; /* Default is either a master table or a table related to a respective protocol */ - if (!d->table && d->export_protocol) d->table = d->export_protocol->table; - if (!d->table && d->show_protocol) d->table = d->show_protocol->table; - if (!d->table) d->table = config->master_rtc->table; + if (!d->table && d->export_protocol) d->table = rt_show_get_table(d->export_protocol); + if (!d->table && d->show_protocol) d->table = rt_show_get_table(d->show_protocol); + if (!d->table) d->table = config->def_tables[NET_IP4]->table; /* FIXME: iterate through all tables ? */ /* Filtered routes are neither exported nor have sensible ordering */ if (d->filtered && (d->export_mode || d->primary_only)) cli_msg(0, ""); - if (d->pxlen == 256) + if (!d->addr) { FIB_ITERATE_INIT(&d->fit, &d->table->fib); this_cli->cont = rt_show_cont; @@ -2590,10 +2643,21 @@ rt_show(struct rt_show_data *d) } else { + if (d->export_mode) + { + /* Find channel associated with the export protocol */ + d->export_channel = rt_show_export_channel(d); + if (!d->export_channel || (d->export_channel->export_state == ES_DOWN)) + { + cli_msg(8005, "Channel is down"); + return; + } + } + if (d->show_for) - n = net_route(d->table, d->prefix, d->pxlen); + n = net_route(d->table, d->addr); else - n = net_find(d->table, d->prefix, d->pxlen); + n = net_find(d->table, d->addr); if (n) rt_show_net(this_cli, n, d); @@ -2614,26 +2678,24 @@ rt_show(struct rt_show_data *d) * net_find - find a network entry * @tab: a routing table * @addr: address of the network - * @len: length of the network prefix * * net_find() looks up the given network in routing table @tab and * returns a pointer to its &net entry or %NULL if no such network * exists. */ -static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) +static inline net *net_find(rtable *tab, net_addr *addr) { DUMMY; } /** * net_get - obtain a network entry * @tab: a routing table * @addr: address of the network - * @len: length of the network prefix * * net_get() looks up the given network in routing table @tab and * returns a pointer to its &net entry. If no such entry exists, it's * created. */ -static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) +static inline net *net_get(rtable *tab, net_addr *addr) { DUMMY; } /** diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 5de2f556..f966161c 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -112,7 +112,7 @@ #define HASH_IP_KEY(n) n->addr #define HASH_IP_NEXT(n) n->next_ip #define HASH_IP_EQ(a,b) ipa_equal(a,b) -#define HASH_IP_FN(k) ipa_hash32(k) +#define HASH_IP_FN(k) ipa_hash(k) static list bfd_proto_list; static list bfd_wait_list; @@ -952,7 +952,7 @@ bfd_init_all(void) static struct proto * bfd_init(struct proto_config *c) { - struct proto *p = proto_new(c, sizeof(struct bfd_proto)); + struct proto *p = proto_new(c); p->neigh_notify = bfd_neigh_notify; @@ -981,8 +981,10 @@ bfd_start(struct proto *P) add_tail(&bfd_proto_list, &p->bfd_node); birdloop_enter(p->loop); - p->rx_1 = bfd_open_rx_sk(p, 0); - p->rx_m = bfd_open_rx_sk(p, 1); + p->rx4_1 = bfd_open_rx_sk(p, 0, 4); + p->rx4_m = bfd_open_rx_sk(p, 1, 4); + p->rx6_1 = bfd_open_rx_sk(p, 0, 6); + p->rx6_m = bfd_open_rx_sk(p, 1, 6); birdloop_leave(p->loop); bfd_take_requests(p); @@ -1116,6 +1118,7 @@ bfd_show_sessions(struct proto *P) struct protocol proto_bfd = { .name = "BFD", .template = "bfd%d", + .proto_size = sizeof(struct bfd_proto), .config_size = sizeof(struct bfd_config), .init = bfd_init, .start = bfd_start, diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 9b61be64..ce7d665b 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -84,8 +84,10 @@ struct bfd_proto sock *notify_ws; list notify_list; - sock *rx_1; - sock *rx_m; + sock *rx4_1; + sock *rx6_1; + sock *rx4_m; + sock *rx6_m; list iface_list; }; @@ -184,7 +186,7 @@ void bfd_show_sessions(struct proto *P); /* packets.c */ void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final); -sock * bfd_open_rx_sk(struct bfd_proto *p, int multihop); +sock * bfd_open_rx_sk(struct bfd_proto *p, int multihop, int inet_version); sock * bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa); diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index cb40bcda..b7a057f1 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -186,7 +186,7 @@ bfd_err_hook(sock *sk, int err) } sock * -bfd_open_rx_sk(struct bfd_proto *p, int multihop) +bfd_open_rx_sk(struct bfd_proto *p, int multihop, int inet_version) { sock *sk = sk_new(p->tpool); sk->type = SK_UDP; @@ -202,9 +202,18 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop) sk->priority = sk_priority_control; sk->flags = SKF_THREAD | SKF_LADDR_RX | (!multihop ? SKF_TTL_RX : 0); -#ifdef IPV6 - sk->flags |= SKF_V6ONLY; -#endif + switch (inet_version) { + case 4: + sk->fam = SK_FAM_IPV4; + sk->flags |= SKF_V4ONLY; + break; + case 6: + sk->fam = SK_FAM_IPV6; + sk->flags |= SKF_V6ONLY; + break; + default: + ASSERT(0); + } if (sk_open(sk) < 0) goto err; @@ -237,9 +246,13 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa) sk->ttl = ifa ? 255 : -1; sk->flags = SKF_THREAD | SKF_BIND | SKF_HIGH_PORT; -#ifdef IPV6 - sk->flags |= SKF_V6ONLY; -#endif + if (ipa_is_ip4(local)) { + sk->fam = SK_FAM_IPV4; + sk->flags |= SKF_V4ONLY; + } else { + sk->fam = SK_FAM_IPV6; + sk->flags |= SKF_V6ONLY; + } if (sk_open(sk) < 0) goto err; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 94c8e5c2..61b5cba2 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1389,7 +1389,6 @@ static void bgp_copy_config(struct proto_config *dest, struct proto_config *src) { /* Just a shallow copy */ - proto_copy_rest(dest, src, sizeof(struct bgp_config)); } diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 85b93a6b..614ef08c 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -112,12 +112,6 @@ bgp_proto: | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } | bgp_proto PASSWORD text ';' { BGP_CFG->password = $3; } - | bgp_proto ROUTE LIMIT expr ';' { - this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit)); - this_proto->in_limit->limit = $4; - this_proto->in_limit->action = PLA_RESTART; - log(L_WARN "%s: Route limit option is deprecated, use import limit", this_proto->name); - } | bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; } | bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; } | bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; } diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index c859960f..297774b5 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -68,6 +68,10 @@ ospf_proto_finish(void) if (EMPTY_LIST(cf->area_list)) cf_error( "No configured areas in OSPF"); + /* Define default channel */ + if (EMPTY_LIST(this_proto->channels)) + channel_config_new(NULL, this_proto->net_type, this_proto); + int areano = 0; int backbone = 0; int nssa = 0; @@ -84,7 +88,7 @@ ospf_proto_finish(void) cf->abr = areano > 1; /* Route export or NSSA translation (RFC 3101 3.1) */ - cf->asbr = (this_proto->out_filter != FILTER_REJECT) || (nssa && cf->abr); + cf->asbr = (proto_cf_main_channel(this_proto)->out_filter != FILTER_REJECT) || (nssa && cf->abr); if (cf->abr && !backbone) { @@ -130,25 +134,32 @@ CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY, BFD) 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, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH) -CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION) +CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION, OSPF2, OSPF3) -%type <t> opttext %type <ld> lsadb_args -%type <i> nbma_eligible +%type <i> ospf_variant nbma_eligible CF_GRAMMAR CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } ) -ospf_proto_start: proto_start OSPF { - this_proto = proto_config_new(&proto_ospf, $1); - init_list(&OSPF_CFG->area_list); - init_list(&OSPF_CFG->vlink_list); - OSPF_CFG->tick = OSPF_DEFAULT_TICK; - OSPF_CFG->ospf2 = OSPF_IS_V2; - } +ospf_variant: + OSPF { $$ = 1; } + | OSPF2 { $$ = 1; } + | OSPF3 { $$ = 0; } ; +ospf_proto_start: proto_start ospf_variant +{ + this_proto = proto_config_new(&proto_ospf, $1); + this_proto->net_type = $2 ? NET_IP4 : NET_IP6; + + init_list(&OSPF_CFG->area_list); + init_list(&OSPF_CFG->vlink_list); + OSPF_CFG->tick = OSPF_DEFAULT_TICK; + OSPF_CFG->ospf2 = $2; +}; + ospf_proto: ospf_proto_start proto_name '{' | ospf_proto ospf_proto_item ';' @@ -156,6 +167,7 @@ ospf_proto: ospf_proto_item: proto_item + | proto_channel | RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; } | STUB ROUTER bool { OSPF_CFG->stub_router = $3; } | ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; } @@ -212,10 +224,10 @@ ospf_stubnet: ; ospf_stubnet_start: - prefix { + net_ip { this_stubnet = cfg_allocz(sizeof(struct ospf_stubnet_config)); add_tail(&this_area->stubnet_list, NODE this_stubnet); - this_stubnet->px = $1; + this_stubnet->prefix = $1; this_stubnet->cost = COST_D; } ; @@ -311,7 +323,6 @@ ospf_iface_item: | TTL SECURITY bool { OSPF_PATT->ttl_security = $3; } | TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; } | BFD bool { OSPF_PATT->bfd = $2; cf_check_bfd($2); } - | SECONDARY bool { OSPF_PATT->bsd_secondary = $2; } | password_list { ospf_check_auth(); } ; @@ -322,12 +333,11 @@ pref_list: pref_item: pref_base pref_opt ';' ; -pref_base: prefix +pref_base: net_ip { this_pref = cfg_allocz(sizeof(struct area_net_config)); add_tail(this_nets, NODE this_pref); - this_pref->px.addr = $1.addr; - this_pref->px.len = $1.len; + this_pref->prefix = $1; } ; @@ -401,11 +411,6 @@ ospf_iface: ospf_iface_start ospf_iface_patt_list ospf_iface_opt_list { ospf_iface_finish(); } ; -opttext: - TEXT - | /* empty */ { $$ = NULL; } - ; - CF_ADDTO(dynamic_attr, OSPF_METRIC1 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_METRIC1); }) CF_ADDTO(dynamic_attr, OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_METRIC2); }) CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_TAG); }) diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c index 50cf1407..3fbb6167 100644 --- a/proto/ospf/hello.c +++ b/proto/ospf/hello.c @@ -74,7 +74,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask)) ps->netmask = 0; else - ps->netmask = htonl(u32_mkmask(ifa->addr->pxlen)); + ps->netmask = htonl(u32_mkmask(ifa->addr->prefix.pxlen)); ps->helloint = ntohs(ifa->helloint); ps->options = ifa->oa->options; @@ -105,7 +105,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn) } i = 0; - max = (ospf_pkt_maxsize(ifa) - length) / sizeof(u32); + max = (ospf_pkt_maxsize(p, ifa) - length) / sizeof(u32); /* Fill all neighbors */ if (kind != OHS_SHUTDOWN) @@ -198,7 +198,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, /* RFC 2328 10.5 */ /* - * We may not yet havethe associate neighbor, so we use Router ID from the + * We may not yet have the associate neighbor, so we use Router ID from the * packet instead of one from the neighbor structure for log messages. */ u32 rcv_rid = ntohl(pkt->routerid); @@ -224,7 +224,7 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, int pxlen = u32_masklen(ntohl(ps->netmask)); if ((ifa->type != OSPF_IT_VLINK) && (ifa->type != OSPF_IT_PTP) && - (pxlen != ifa->addr->pxlen)) + (pxlen != ifa->addr->prefix.pxlen)) DROP("prefix length mismatch", pxlen); neighbors = ps->neighbors; diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index 67ae094d..4548f6da 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -67,7 +67,9 @@ ifa_flood_queue_size(struct ospf_iface *ifa) int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen) { - plen += SIZE_OF_IP_HEADER; + struct ospf_proto *p = ifa->oa->po; + + plen += ospf_is_v2(p) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH; /* This is relevant just for OSPFv2 */ if (ifa->autype == OSPF_AUTH_CRYPT) @@ -109,6 +111,7 @@ ospf_sk_open(struct ospf_iface *ifa) sk->dport = OSPF_PROTO; sk->saddr = ifa->addr->ip; sk->iface = ifa->iface; + sk->fam = ospf_is_v2(p) ? SK_FAM_IPV4 : SK_FAM_IPV6; sk->tos = ifa->cf->tx_tos; sk->priority = ifa->cf->tx_priority; @@ -191,6 +194,7 @@ ospf_open_vlink_sk(struct ospf_proto *p) sock *sk = sk_new(p->p.pool); sk->type = SK_IP; sk->dport = OSPF_PROTO; + sk->fam = ospf_is_v2(p) ? SK_FAM_IPV4 : SK_FAM_IPV6; /* FIXME: configurable tos/priority ? */ sk->tos = IP_PREC_INTERNET_CONTROL; @@ -235,8 +239,8 @@ ospf_iface_down(struct ospf_iface *ifa) OSPF_TRACE(D_EVENTS, "Removing interface %s (peer %I) from area %R", ifa->ifname, ifa->addr->opposite, ifa->oa->areaid); else - OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R", - ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid); + OSPF_TRACE(D_EVENTS, "Removing interface %s (%N) from area %R", + ifa->ifname, &ifa->addr->prefix, ifa->oa->areaid); /* First of all kill all the related vlinks */ WALK_LIST(iff, p->iface_list) @@ -521,15 +525,6 @@ ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr) if (addr->iface->flags & IF_LOOPBACK) return 1; - /* - * For compatibility reasons on BSD systems, we force OSPF - * interfaces with non-primary IP prefixes to be stub. - */ -#if defined(OSPFv2) && !defined(CONFIG_MC_PROPER_SRC) - if (!ip->bsd_secondary && !(addr->flags & IA_PRIMARY)) - return 1; -#endif - return ip->stub; } @@ -548,8 +543,8 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i OSPF_TRACE(D_EVENTS, "Adding interface %s (peer %I) to area %R", iface->name, addr->opposite, oa->areaid); else - OSPF_TRACE(D_EVENTS, "Adding interface %s (%I/%d) to area %R", - iface->name, addr->prefix, addr->pxlen, oa->areaid); + OSPF_TRACE(D_EVENTS, "Adding interface %s (%N) to area %R", + iface->name, &addr->prefix, oa->areaid); pool = rp_new(p->p.pool, "OSPF Interface"); ifa = mb_allocz(pool, sizeof(struct ospf_iface)); @@ -586,6 +581,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i if (ip->ptp_netmask < 2) ifa->ptp_netmask = ip->ptp_netmask; + ifa->drip = ifa->bdrip = ospf_is_v2(p) ? IPA_NONE4 : IPA_NONE6; ifa->type = ospf_iface_classify(ip->type, addr); @@ -625,7 +621,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i should be used). Because OSPFv3 iface is not subnet-specific, there is no need for ipa_in_net() check */ - if (ospf_is_v2(p) && !ipa_in_net(nb->ip, addr->prefix, addr->pxlen)) + if (ospf_is_v2(p) && !ipa_in_netX(nb->ip, &addr->prefix)) continue; if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip)) @@ -638,7 +634,7 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i add_tail(&oa->po->iface_list, NODE ifa); struct object_lock *lock = olock_new(pool); - lock->addr = ospf_is_v2(p) ? ifa->addr->prefix : IPA_NONE; + lock->addr = ospf_is_v2(p) ? ipa_from_ip4(net4_prefix(&ifa->addr->prefix)) : IPA_NONE; lock->type = OBJLOCK_IP; lock->port = OSPF_PROTO; lock->inst = ifa->instance_id; @@ -884,7 +880,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new) WALK_LIST(nb, new->nbma_list) { /* See related note in ospf_iface_new() */ - if (ospf_is_v2(p) && !ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen)) + if (ospf_is_v2(p) && !ipa_in_netX(nb->ip, &ifa->addr->prefix)) continue; if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip)) @@ -1071,6 +1067,9 @@ ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a) { struct ospf_proto *p = (struct ospf_proto *) P; + if (a->prefix.type != NET_IP4) + return; + if (a->flags & IA_SECONDARY) return; @@ -1100,6 +1099,9 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a) { struct ospf_proto *p = (struct ospf_proto *) P; + if (a->prefix.type != NET_IP6) + return; + if (a->flags & IA_SECONDARY) return; @@ -1152,6 +1154,9 @@ ospf_reconfigure_ifaces2(struct ospf_proto *p) WALK_LIST(a, iface->addrs) { + if (a->prefix.type != NET_IP4) + continue; + if (a->flags & IA_SECONDARY) continue; @@ -1170,8 +1175,8 @@ ospf_reconfigure_ifaces2(struct ospf_proto *p) continue; /* Hard restart */ - log(L_INFO "%s: Restarting interface %s (%I/%d) in area %R", - p->p.name, ifa->ifname, a->prefix, a->pxlen, s.oa->areaid); + log(L_INFO "%s: Restarting interface %s (%N) in area %R", + p->p.name, ifa->ifname, &a->prefix, s.oa->areaid); ospf_iface_shutdown(ifa); ospf_iface_remove(ifa); } @@ -1195,6 +1200,9 @@ ospf_reconfigure_ifaces3(struct ospf_proto *p) WALK_LIST(a, iface->addrs) { + if (a->prefix.type != NET_IP6) + continue; + if (a->flags & IA_SECONDARY) continue; @@ -1326,7 +1334,7 @@ ospf_iface_info(struct ospf_iface *ifa) else if (ifa->addr->flags & IA_PEER) cli_msg(-1015, "Interface %s (peer %I)", ifa->ifname, ifa->addr->opposite); else - cli_msg(-1015, "Interface %s (%I/%d)", ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen); + cli_msg(-1015, "Interface %s (%N)", ifa->ifname, &ifa->addr->prefix); cli_msg(-1015, "\tType: %s%s", ospf_it[ifa->type], more); cli_msg(-1015, "\tArea: %R (%u)", ifa->oa->areaid, ifa->oa->areaid); diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index 66a3a23d..5564bee7 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -280,21 +280,19 @@ lsa_walk_rt(struct ospf_lsa_rt_walk *rt) void -lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric) +lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric) { if (ospf2) { struct ospf_lsa_sum2 *ls = en->lsa_body; - *ip = ipa_from_u32(en->lsa.id & ls->netmask); - *pxlen = u32_masklen(ls->netmask); + net_fill_ip4(net, ip4_from_u32(en->lsa.id & ls->netmask), u32_masklen(ls->netmask)); *pxopts = 0; *metric = ls->metric & LSA_METRIC_MASK; } else { struct ospf_lsa_sum3_net *ls = en->lsa_body; - u16 rest; - lsa_get_ipv6_prefix(ls->prefix, ip, pxlen, pxopts, &rest); + ospf_get_ipv6_prefix(ls->prefix, net, pxopts, NULL); *metric = ls->metric & LSA_METRIC_MASK; } } @@ -324,8 +322,9 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r if (ospf2) { struct ospf_lsa_ext2 *ext = en->lsa_body; - rt->ip = ipa_from_u32(en->lsa.id & ext->netmask); - rt->pxlen = u32_masklen(ext->netmask); + net_fill_ip4(&rt->net, + ip4_from_u32(en->lsa.id & ext->netmask), + u32_masklen(ext->netmask)); rt->pxopts = 0; rt->metric = ext->metric & LSA_METRIC_MASK; rt->ebit = ext->metric & LSA_EXT2_EBIT; @@ -339,14 +338,13 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r else { struct ospf_lsa_ext3 *ext = en->lsa_body; - u16 rest; - u32 *buf = lsa_get_ipv6_prefix(ext->rest, &rt->ip, &rt->pxlen, &rt->pxopts, &rest); + u32 *buf = ospf_get_ipv6_prefix(ext->rest, &rt->net, &rt->pxopts, NULL); rt->metric = ext->metric & LSA_METRIC_MASK; rt->ebit = ext->metric & LSA_EXT3_EBIT; rt->fbit = ext->metric & LSA_EXT3_FBIT; if (rt->fbit) - buf = lsa_get_ipv6_addr(buf, &rt->fwaddr); + buf = ospf_get_ipv6_addr(buf, &rt->fwaddr); else rt->fwaddr = IPA_NONE; @@ -452,7 +450,7 @@ lsa_validate_sum3_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_net *bod return 0; u8 pxl = pxlen(body->prefix); - if (pxl > MAX_PREFIX_LENGTH) + if (pxl > IP6_MAX_PREFIX_LENGTH) return 0; if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + @@ -491,11 +489,11 @@ lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body) return 0; u8 pxl = pxlen(body->rest); - if (pxl > MAX_PREFIX_LENGTH) + if (pxl > IP6_MAX_PREFIX_LENGTH) return 0; int len = IPV6_PREFIX_SPACE(pxl); - if (body->metric & LSA_EXT3_FBIT) // forwardinf address + if (body->metric & LSA_EXT3_FBIT) // forwarding address len += 16; if (body->metric & LSA_EXT3_TBIT) // route tag len += 4; @@ -520,7 +518,7 @@ lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *p return 0; u8 pxl = pxlen((u32 *) (pbuf + offset)); - if (pxl > MAX_PREFIX_LENGTH) + if (pxl > IP6_MAX_PREFIX_LENGTH) return 0; offset += IPV6_PREFIX_SPACE(pxl); diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index ae6af044..c93f0295 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -55,7 +55,7 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len); int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2); void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt); int lsa_walk_rt(struct ospf_lsa_rt_walk *rt); -void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric); +void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric); void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options); void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt); int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body); diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index c6a734ca..65ad8e3d 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -330,7 +330,7 @@ ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa, pkt = ospf_tx_buffer(ifa); hlen = ospf_lsupd_hdrlen(p); - maxsize = ospf_pkt_maxsize(ifa); + maxsize = ospf_pkt_maxsize(p, ifa); ospf_pkt_fill_hdr(ifa, pkt, LSUPD_P); pos = hlen; diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c index 0223ccdf..b68ba6f4 100644 --- a/proto/ospf/neighbor.c +++ b/proto/ospf/neighbor.c @@ -507,13 +507,14 @@ ospf_dr_election(struct ospf_iface *ifa) u32 old_drid = ifa->drid; u32 old_bdrid = ifa->bdrid; + ip_addr none = ospf_is_v2(p) ? IPA_NONE4 : IPA_NONE6; ifa->drid = ndr ? ndr->rid : 0; - ifa->drip = ndr ? ndr->ip : IPA_NONE; + ifa->drip = ndr ? ndr->ip : none; ifa->dr_iface_id = ndr ? ndr->iface_id : 0; ifa->bdrid = nbdr ? nbdr->rid : 0; - ifa->bdrip = nbdr ? nbdr->ip : IPA_NONE; + ifa->bdrip = nbdr ? nbdr->ip : none; DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index d5d5d354..d074600a 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -102,18 +102,11 @@ static int ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool); static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool); static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs); -static int ospf_reload_routes(struct proto *P); +static void ospf_reload_routes(struct channel *C); static int ospf_rte_better(struct rte *new, struct rte *old); static int ospf_rte_same(struct rte *new, struct rte *old); static void ospf_disp(timer *timer); -static void -ospf_area_initfib(struct fib_node *fn) -{ - struct area_net *an = (struct area_net *) fn; - an->hidden = 0; - an->active = 0; -} static void add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) @@ -122,18 +115,20 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) struct area_net_config *anc; struct area_net *an; - fib_init(&oa->net_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib); - fib_init(&oa->enet_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib); + fib_init(&oa->net_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6, + sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL); + fib_init(&oa->enet_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6, + sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL); WALK_LIST(anc, ac->net_list) { - an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len); + an = fib_get(&oa->net_fib, &anc->prefix); an->hidden = anc->hidden; } WALK_LIST(anc, ac->enet_list) { - an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len); + an = fib_get(&oa->enet_fib, &anc->prefix); an->hidden = anc->hidden; an->tag = anc->tag; } @@ -154,7 +149,7 @@ ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac) oa->areaid = ac->areaid; oa->rt = NULL; oa->po = p; - fib_init(&oa->rtr, p->p.pool, sizeof(ort), 0, ospf_rt_initort); + fib_init(&oa->rtr, p->p.pool, NET_IP4, sizeof(ort), OFFSETOF(ort, fn), 0, NULL); add_area_nets(oa, ac); if (oa->areaid == 0) @@ -243,7 +238,10 @@ ospf_start(struct proto *P) p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh)); init_list(&(p->iface_list)); init_list(&(p->area_list)); - fib_init(&p->rtf, P->pool, sizeof(ort), 0, ospf_rt_initort); + fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6, + sizeof(ort), OFFSETOF(ort, fn), 0, NULL); + if (ospf_is_v3(p)) + idm_init(&p->idm, P->pool, 16); p->areano = 0; p->gr = ospf_top_new(p, P->pool); s_init_list(&(p->lsal)); @@ -299,15 +297,16 @@ ospf_dump(struct proto *P) } static struct proto * -ospf_init(struct proto_config *c) +ospf_init(struct proto_config *CF) { - struct ospf_config *oc = (struct ospf_config *) c; - struct proto *P = proto_new(c, sizeof(struct ospf_proto)); + struct ospf_config *cf = (struct ospf_config *) CF; + struct proto *P = proto_new(CF); + + P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); - P->accept_ra_types = RA_OPTIMAL; P->rt_notify = ospf_rt_notify; P->if_notify = ospf_if_notify; - P->ifa_notify = oc->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3; + P->ifa_notify = cf->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3; P->import_control = ospf_import_control; P->reload_routes = ospf_reload_routes; P->make_tmp_attrs = ospf_make_tmp_attrs; @@ -391,17 +390,16 @@ ospf_schedule_rtcalc(struct ospf_proto *p) p->calcrt = 1; } -static int -ospf_reload_routes(struct proto *P) +static void +ospf_reload_routes(struct channel *C) { - struct ospf_proto *p = (struct ospf_proto *) P; + struct ospf_proto *p = (struct ospf_proto *) C->proto; - if (p->calcrt != 2) - OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload"); + if (p->calcrt == 2) + return; + OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload"); p->calcrt = 2; - - return 1; } @@ -506,9 +504,9 @@ ospf_shutdown(struct proto *P) ospf_iface_shutdown(ifa); /* Cleanup locked rta entries */ - FIB_WALK(&p->rtf, nftmp) + FIB_WALK(&p->rtf, ort, nf) { - rta_free(((ort *) nftmp)->old_rta); + rta_free(nf->old_rta); } FIB_WALK_END; @@ -639,17 +637,20 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) * nonbroadcast network, cost of interface, etc. */ static int -ospf_reconfigure(struct proto *P, struct proto_config *c) +ospf_reconfigure(struct proto *P, struct proto_config *CF) { struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_config *old = (struct ospf_config *) (P->cf); - struct ospf_config *new = (struct ospf_config *) c; + struct ospf_config *new = (struct ospf_config *) CF; struct ospf_area_config *nac; struct ospf_area *oa, *oax; struct ospf_iface *ifa, *ifx; struct ospf_iface_patt *ip; - if (proto_get_router_id(c) != p->router_id) + if (proto_get_router_id(CF) != p->router_id) + return 0; + + if (p->ospf2 != new->ospf2) return 0; if (p->rfc1583 != new->rfc1583) @@ -658,6 +659,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *c) if (old->abr != new->abr) return 0; + if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) + return 0; + p->stub_router = new->stub_router; p->merge_external = new->merge_external; p->asbr = new->asbr; @@ -746,7 +750,6 @@ ospf_sh(struct proto *P) struct ospf_iface *ifa; struct ospf_neighbor *n; int ifano, nno, adjno, firstfib; - struct area_net *anet; if (p->p.proto_state != PS_UP) { @@ -795,29 +798,27 @@ ospf_sh(struct proto *P) cli_msg(-1014, "\t\tNumber of adjacent neighbors:\t%u", adjno); firstfib = 1; - FIB_WALK(&oa->net_fib, nftmp) + FIB_WALK(&oa->net_fib, struct area_net, anet) { - anet = (struct area_net *) nftmp; if(firstfib) { cli_msg(-1014, "\t\tArea networks:"); firstfib = 0; } - cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen, + cli_msg(-1014, "\t\t\t%1N\t%s\t%s", anet->fn.addr, anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : ""); } FIB_WALK_END; firstfib = 1; - FIB_WALK(&oa->enet_fib, nftmp) + FIB_WALK(&oa->enet_fib, struct area_net, anet) { - anet = (struct area_net *) nftmp; if(firstfib) { cli_msg(-1014, "\t\tArea external networks:"); firstfib = 0; } - cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen, + cli_msg(-1014, "\t\t\t%1N\t%s\t%s", anet->fn.addr, anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : ""); } FIB_WALK_END; @@ -1074,13 +1075,12 @@ show_lsa_network(struct top_hash_entry *he, int ospf2) static inline void show_lsa_sum_net(struct top_hash_entry *he, int ospf2) { - ip_addr ip; - int pxlen; + net_addr net; u8 pxopts; u32 metric; - lsa_parse_sum_net(he, ospf2, &ip, &pxlen, &pxopts, &metric); - cli_msg(-1016, "\t\txnetwork %I/%d metric %u", ip, pxlen, metric); + lsa_parse_sum_net(he, ospf2, &net, &pxopts, &metric); + cli_msg(-1016, "\t\txnetwork %N metric %u", &net, metric); } static inline void @@ -1099,7 +1099,7 @@ static inline void show_lsa_external(struct top_hash_entry *he, int ospf2) { struct ospf_lsa_ext_local rt; - char str_via[STD_ADDRESS_P_LENGTH + 8] = ""; + char str_via[IPA_MAX_TEXT_LENGTH + 8] = ""; char str_tag[16] = ""; if (he->lsa_type == LSA_T_EXT) @@ -1113,19 +1113,15 @@ show_lsa_external(struct top_hash_entry *he, int ospf2) if (rt.tag) bsprintf(str_tag, " tag %08x", rt.tag); - cli_msg(-1016, "\t\t%s %I/%d metric%s %u%s%s", + cli_msg(-1016, "\t\t%s %N metric%s %u%s%s", (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external", - rt.ip, rt.pxlen, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); + &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); } static inline void show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode) { struct ospf_lsa_prefix *px = he->lsa_body; - ip_addr pxa; - int pxlen; - u8 pxopts; - u16 metric; u32 *buf; int i; @@ -1141,14 +1137,18 @@ show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode) buf = px->rest; for (i = 0; i < px->pxcount; i++) - { - buf = lsa_get_ipv6_prefix(buf, &pxa, &pxlen, &pxopts, &metric); + { + net_addr net; + u8 pxopts; + u16 metric; - if (px->ref_type == LSA_T_RT) - cli_msg(-1016, "\t\tstubnet %I/%d metric %u", pxa, pxlen, metric); - else - cli_msg(-1016, "\t\taddress %I/%d", pxa, pxlen); - } + buf = ospf_get_ipv6_prefix(buf, &net, &pxopts, &metric); + + if (px->ref_type == LSA_T_RT) + cli_msg(-1016, "\t\tstubnet %N metric %u", &net, metric); + else + cli_msg(-1016, "\t\taddress %N", &net); + } } void @@ -1468,6 +1468,8 @@ struct protocol proto_ospf = { .template = "ospf%d", .attr_class = EAP_OSPF, .preference = DEF_PREF_OSPF, + .channel_mask = NB_IP, + .proto_size = sizeof(struct ospf_proto), .config_size = sizeof(struct ospf_config), .init = ospf_init, .dump = ospf_dump, diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index a4e525ec..3d70df7b 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -14,7 +14,7 @@ #include "nest/bird.h" #include "lib/checksum.h" -#include "lib/ip.h" +#include "lib/idm.h" #include "lib/lists.h" #include "lib/slists.h" #include "lib/socket.h" @@ -37,14 +37,6 @@ #endif -#ifdef IPV6 -#define OSPF_IS_V2 0 -#else -#define OSPF_IS_V2 1 -#endif - -// FIXME: MAX_PREFIX_LENGTH - #define OSPF_TRACE(flags, msg, args...) \ do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \ log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0) @@ -87,7 +79,6 @@ #define OSPF_VLINK_ID_OFFSET 0x80000000 - struct ospf_config { struct proto_config c; @@ -125,24 +116,24 @@ struct ospf_area_config struct area_net_config { node n; - struct prefix px; + net_addr prefix; u32 tag; u8 hidden; }; struct area_net { - struct fib_node fn; u32 metric; /* With possible LSA_EXT3_EBIT for NSSA area nets */ u32 tag; u8 hidden; u8 active; + struct fib_node fn; }; struct ospf_stubnet_config { node n; - struct prefix px; + net_addr prefix; u32 cost; u8 hidden; u8 summary; @@ -193,7 +184,6 @@ struct ospf_iface_patt u8 ptp_netmask; /* bool + 2 for unspecified */ u8 ttl_security; /* bool + 2 for TX only */ u8 bfd; - u8 bsd_secondary; list *passwords; }; @@ -224,6 +214,7 @@ struct ospf_proto int areano; /* Number of area I belong to */ int padj; /* Number of neighbors in Exchange or Loading state */ struct fib rtf; /* Routing table */ + struct idm idm; /* OSPFv3 LSA ID map */ byte ospf2; /* OSPF v2 or v3 */ byte rfc1583; /* RFC1583 compatibility */ byte stub_router; /* Do not forward transit traffic */ @@ -681,8 +672,8 @@ struct ospf_lsa_ext3 struct ospf_lsa_ext_local { - ip_addr ip, fwaddr; - int pxlen; + net_addr net; + ip_addr fwaddr; u32 metric, ebit, fbit, tag, propagate; u8 pxopts; }; @@ -720,73 +711,80 @@ lsa_net_count(struct ospf_lsa_header *lsa) /* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address) as index, so we need to encapsulate RID to IP address */ -#define ipa_from_rid(x) ipa_from_u32(x) -#define ipa_to_rid(x) ipa_to_u32(x) +#define net_from_rid(x) NET_ADDR_IP4(ip4_from_u32(x), IP4_MAX_PREFIX_LENGTH) +#define rid_from_net(x) ip4_to_u32(((net_addr_ip4 *) x)->prefix) #define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4) #define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32) -/* FIXME: these four functions should be significantly redesigned w.r.t. integration, +/* FIXME: these functions should be significantly redesigned w.r.t. integration, also should be named as ospf3_* instead of *_ipv6_* */ +static inline int +ospf_valid_prefix(net_addr *n) +{ + /* In OSPFv2, prefix is stored as netmask; ip4_masklen() returns 255 for invalid one */ + return n->pxlen <= IP6_MAX_PREFIX_LENGTH; +} + static inline u32 * -lsa_get_ipv6_prefix(u32 *buf, ip_addr *addr, int *pxlen, u8 *pxopts, u16 *rest) +ospf_get_ipv6_prefix(u32 *buf, net_addr *N, u8 *pxopts, u16 *rest) { - u8 pxl = (*buf >> 24); - *pxopts = (*buf >> 16); - *rest = *buf; - *pxlen = pxl; + net_addr_ip6 *net = (void *) N; + u8 pxlen = (*buf >> 24); + *pxopts = (*buf >> 16) & 0xff; + if (rest) *rest = *buf & 0xffff; buf++; - *addr = IPA_NONE; + *net = NET_ADDR_IP6(IP6_NONE, pxlen); -#ifdef IPV6 - if (pxl > 0) - _I0(*addr) = *buf++; - if (pxl > 32) - _I1(*addr) = *buf++; - if (pxl > 64) - _I2(*addr) = *buf++; - if (pxl > 96) - _I3(*addr) = *buf++; + if (pxlen > 0) + _I0(net->prefix) = *buf++; + if (pxlen > 32) + _I1(net->prefix) = *buf++; + if (pxlen > 64) + _I2(net->prefix) = *buf++; + if (pxlen > 96) + _I3(net->prefix) = *buf++; /* Clean up remaining bits */ - if (pxl < 128) - addr->addr[pxl / 32] &= u32_mkmask(pxl % 32); -#endif + if (pxlen < 128) + net->prefix.addr[pxlen / 32] &= u32_mkmask(pxlen % 32); return buf; } static inline u32 * -lsa_get_ipv6_addr(u32 *buf, ip_addr *addr) +ospf_get_ipv6_addr(u32 *buf, ip_addr *addr) { - *addr = *(ip_addr *) buf; + *addr = ipa_from_ip6(*(ip6_addr *) buf); return buf + 4; } static inline u32 * -put_ipv6_prefix(u32 *buf, ip_addr addr, u8 pxlen, u8 pxopts, u16 lh) +ospf_put_ipv6_prefix(u32 *buf, net_addr *N, u8 pxopts, u16 rest) { -#ifdef IPV6 - *buf++ = ((pxlen << 24) | (pxopts << 16) | lh); + net_addr_ip6 *net = (void *) N; + u32 pxlen = net->pxlen; + + *buf++ = ((pxlen << 24) | (pxopts << 16) | rest); if (pxlen > 0) - *buf++ = _I0(addr); + *buf++ = _I0(net->prefix); if (pxlen > 32) - *buf++ = _I1(addr); + *buf++ = _I1(net->prefix); if (pxlen > 64) - *buf++ = _I2(addr); + *buf++ = _I2(net->prefix); if (pxlen > 96) - *buf++ = _I3(addr); -#endif + *buf++ = _I3(net->prefix); + return buf; } static inline u32 * -put_ipv6_addr(u32 *buf, ip_addr addr) +ospf_put_ipv6_addr(u32 *buf, ip_addr addr) { - *(ip_addr *) buf = addr; + *(ip6_addr *) buf = ipa_to_ip6(addr); return buf + 4; } @@ -830,16 +828,12 @@ static inline void ospf_notify_net_lsa(struct ospf_iface *ifa) static inline void ospf_notify_link_lsa(struct ospf_iface *ifa) { ifa->update_link_lsa = 1; } - -#define ospf_is_v2(X) OSPF_IS_V2 -#define ospf_is_v3(X) (!OSPF_IS_V2) -/* static inline int ospf_is_v2(struct ospf_proto *p) { return p->ospf2; } static inline int ospf_is_v3(struct ospf_proto *p) { return ! p->ospf2; } -*/ + static inline int ospf_get_version(struct ospf_proto *p) { return ospf_is_v2(p) ? 2 : 3; } @@ -896,7 +890,7 @@ void ospf_sh_neigh_info(struct ospf_neighbor *n); /* packet.c */ void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type); -uint ospf_pkt_maxsize(struct ospf_iface *ifa); +uint ospf_pkt_maxsize(struct ospf_proto *p, struct ospf_iface *ifa); int ospf_rx_hook(sock * sk, int size); // void ospf_tx_hook(sock * sk); void ospf_err_hook(sock * sk, int err); diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index faa33664..35ef7c6e 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -23,7 +23,7 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) pkt->version = ospf_get_version(p); pkt->type = h_type; - pkt->length = htons(ospf_pkt_maxsize(ifa)); + pkt->length = htons(ospf_pkt_maxsize(p, ifa)); pkt->routerid = htonl(p->router_id); pkt->areaid = htonl(ifa->oa->areaid); pkt->checksum = 0; @@ -32,9 +32,9 @@ ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type) } uint -ospf_pkt_maxsize(struct ospf_iface *ifa) +ospf_pkt_maxsize(struct ospf_proto *p, struct ospf_iface *ifa) { - uint headers = SIZE_OF_IP_HEADER; + uint headers = ospf_is_v2(p) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH; /* Relevant just for OSPFv2 */ if (ifa->autype == OSPF_AUTH_CRYPT) @@ -235,7 +235,7 @@ ospf_rx_hook(sock *sk, int len) return 1; int src_local, dst_local, dst_mcast; - src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen); + src_local = ipa_in_netX(sk->faddr, &ifa->addr->prefix); dst_local = ipa_equal(sk->laddr, ifa->addr->ip); dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers); @@ -500,8 +500,8 @@ ospf_send_to_agt(struct ospf_iface *ifa, u8 state) void ospf_send_to_bdr(struct ospf_iface *ifa) { - if (ipa_nonzero(ifa->drip)) + if (ipa_nonzero2(ifa->drip)) ospf_send_to(ifa, ifa->drip); - if (ipa_nonzero(ifa->bdrip)) + if (ipa_nonzero2(ifa->bdrip)) ospf_send_to(ifa, ifa->bdrip); } diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index cdf8012a..0855f21f 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -21,15 +21,6 @@ static inline void reset_ri(ort *ort) bzero(&ort->n, sizeof(orta)); } -void -ospf_rt_initort(struct fib_node *fn) -{ - ort *ri = (ort *) fn; - reset_ri(ri); - ri->old_rta = NULL; - ri->fn.flags = 0; -} - static inline int nh_is_vlink(struct mpnh *nhs) { @@ -334,9 +325,9 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new) static inline void -ri_install_net(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new) +ri_install_net(struct ospf_proto *p, net_addr *net, const orta *new) { - ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen); + ort *old = fib_get(&p->rtf, net); int cmp = orta_compare(p, new, &old->n); if (cmp > 0) @@ -348,8 +339,8 @@ ri_install_net(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new) static inline void ri_install_rt(struct ospf_area *oa, u32 rid, const orta *new) { - ip_addr addr = ipa_from_rid(rid); - ort *old = (ort *) fib_get(&oa->rtr, &addr, MAX_PREFIX_LENGTH); + net_addr_ip4 nrid = net_from_rid(rid); + ort *old = fib_get(&oa->rtr, (net_addr *) &nrid); int cmp = orta_compare(oa->po, new, &old->n); if (cmp > 0) @@ -359,17 +350,19 @@ ri_install_rt(struct ospf_area *oa, u32 rid, const orta *new) } static inline void -ri_install_asbr(struct ospf_proto *p, ip_addr *addr, const orta *new) +ri_install_asbr(struct ospf_proto *p, u32 rid, const orta *new) { - ort *old = (ort *) fib_get(&p->backbone->rtr, addr, MAX_PREFIX_LENGTH); + net_addr_ip4 nrid = net_from_rid(rid); + ort *old = fib_get(&p->backbone->rtr, (net_addr *) &nrid); + if (orta_compare_asbr(p, new, &old->n) > 0) ort_replace(old, new); } static inline void -ri_install_ext(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new) +ri_install_ext(struct ospf_proto *p, net_addr *net, const orta *new) { - ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen); + ort *old = fib_get(&p->rtf, net); int cmp = orta_compare_ext(p, new, &old->n); if (cmp > 0) @@ -404,7 +397,7 @@ px_pos_to_ifa(struct ospf_area *oa, int pos) static void -add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_hash_entry *en, int pos) +add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_entry *en, int pos) { struct ospf_proto *p = oa->po; @@ -419,7 +412,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_ .nhs = en->nhs }; - if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) + if (!ospf_valid_prefix(net)) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); @@ -441,7 +434,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_ nf.nhs = ifa ? new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL; } - ri_install_net(p, px, pxlen, &nf); + ri_install_net(p, net, &nf); } @@ -452,8 +445,7 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr struct ospf_lsa_rt *rt = act->lsa_body; struct ospf_lsa_rt_walk rtl; struct top_hash_entry *tmp; - ip_addr prefix; - int pxlen, i; + int i; if (rt->options & OPT_RT_V) oa->trcap = 1; @@ -503,9 +495,10 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr * the same result by handing them here because add_network() * will keep the best (not the first) found route. */ - prefix = ipa_from_u32(rtl.id & rtl.data); - pxlen = u32_masklen(rtl.data); - add_network(oa, prefix, pxlen, act->dist + rtl.metric, act, i); + net_addr_ip4 net = + NET_ADDR_IP4(ip4_from_u32(rtl.id & rtl.data), u32_masklen(rtl.data)); + + add_network(oa, (net_addr *) &net, act->dist + rtl.metric, act, i); break; case LSART_NET: @@ -527,14 +520,14 @@ spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_ent { struct ospf_lsa_net *ln = act->lsa_body; struct top_hash_entry *tmp; - ip_addr prefix; - int pxlen, i, cnt; + int i, cnt; if (ospf_is_v2(p)) { - prefix = ipa_from_u32(act->lsa.id & ln->optx); - pxlen = u32_masklen(ln->optx); - add_network(oa, prefix, pxlen, act->dist, act, -1); + net_addr_ip4 net = + NET_ADDR_IP4(ip4_from_u32(act->lsa.id & ln->optx), u32_masklen(ln->optx)); + + add_network(oa, (net_addr *) &net, act->dist, act, -1); } cnt = lsa_net_count(&act->lsa); @@ -550,10 +543,6 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa) { struct top_hash_entry *en, *src; struct ospf_lsa_prefix *px; - ip_addr pxa; - int pxlen; - u8 pxopts; - u16 metric; u32 *buf; int i; @@ -588,18 +577,22 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa) buf = px->rest; for (i = 0; i < px->pxcount; i++) - { - buf = lsa_get_ipv6_prefix(buf, &pxa, &pxlen, &pxopts, &metric); + { + net_addr_ip6 net; + u8 pxopts; + u16 metric; - if (pxopts & OPT_PX_NU) - continue; + buf = ospf_get_ipv6_prefix(buf, (net_addr *) &net, &pxopts, &metric); - /* Store the first global address to use it later as a vlink endpoint */ - if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb)) - src->lb = pxa; + if (pxopts & OPT_PX_NU) + continue; - add_network(oa, pxa, pxlen, src->dist + metric, src, i); - } + /* Store the first global address to use it later as a vlink endpoint */ + if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb)) + src->lb = ipa_from_ip6(net.prefix); + + add_network(oa, (net_addr *) &net, src->dist + metric, src, i); + } } } @@ -742,13 +735,12 @@ ospf_rt_sum(struct ospf_area *oa) { struct ospf_proto *p = oa->po; struct top_hash_entry *en; - ip_addr ip, abrip; + net_addr net; u32 dst_rid, metric, options; ort *abr; - int pxlen = -1, type = -1; + int type; u8 pxopts; - OSPF_TRACE(D_EVENTS, "Starting routing table calculation for inter-area (area %R)", oa->areaid); WALK_SLIST(en, p->lsal) @@ -771,18 +763,18 @@ ospf_rt_sum(struct ospf_area *oa) if (en->lsa_type == LSA_T_SUM_NET) { - lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric); + lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric); - if (pxopts & OPT_PX_NU) - continue; - - if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) + if (!ospf_valid_prefix(&net)) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); continue; } + if (pxopts & OPT_PX_NU) + continue; + options = 0; type = ORT_NET; } @@ -803,8 +795,8 @@ ospf_rt_sum(struct ospf_area *oa) continue; /* 16.2. (4) */ - abrip = ipa_from_rid(en->lsa.rt); - abr = (ort *) fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH); + net_addr_ip4 nrid = net_from_rid(en->lsa.rt); + abr = fib_find(&oa->rtr, (net_addr *) &nrid); if (!abr || !abr->n.type) continue; @@ -828,7 +820,7 @@ ospf_rt_sum(struct ospf_area *oa) }; if (type == ORT_NET) - ri_install_net(p, ip, pxlen, &nf); + ri_install_net(p, &net, &nf); else ri_install_rt(oa, dst_rid, &nf); } @@ -842,11 +834,7 @@ ospf_rt_sum_tr(struct ospf_area *oa) struct ospf_area *bb = p->backbone; struct top_hash_entry *en; ort *re, *abr; - ip_addr ip, abrip; - u32 dst_rid, metric, options; - int pxlen; - u8 pxopts; - + u32 metric; if (!bb) return; @@ -869,26 +857,31 @@ ospf_rt_sum_tr(struct ospf_area *oa) if (en->lsa_type == LSA_T_SUM_NET) { - lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric); + net_addr net; + u8 pxopts; - if (pxopts & OPT_PX_NU) - continue; + lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric); - if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH) + if (!ospf_valid_prefix(&net)) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); continue; } - re = fib_find(&p->rtf, &ip, pxlen); + if (pxopts & OPT_PX_NU) + continue; + + re = fib_find(&p->rtf, &net); } else // en->lsa_type == LSA_T_SUM_RT { + u32 dst_rid, options; + lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options); - ip = ipa_from_rid(dst_rid); - re = fib_find(&bb->rtr, &ip, MAX_PREFIX_LENGTH); + net_addr_ip4 nrid = net_from_rid(dst_rid); + re = fib_find(&bb->rtr, (net_addr *) &nrid); } /* 16.3 (1b) */ @@ -906,8 +899,8 @@ ospf_rt_sum_tr(struct ospf_area *oa) continue; /* 16.3. (4) */ - abrip = ipa_from_rid(en->lsa.rt); - abr = fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH); + net_addr_ip4 nrid = net_from_rid(en->lsa.rt); + abr = fib_find(&oa->rtr, (net_addr *) &nrid); if (!abr || !abr->n.type) continue; @@ -998,7 +991,7 @@ decide_sum_lsa(struct ospf_area *oa, ort *nf, int dest) return 1; struct area_net *anet = (struct area_net *) - fib_route(&nf->n.oa->net_fib, nf->fn.prefix, nf->fn.pxlen); + fib_route(&nf->n.oa->net_fib, nf->fn.addr); /* Condensed area network found */ if (anet) @@ -1017,13 +1010,13 @@ check_sum_net_lsa(struct ospf_proto *p, ort *nf) if (nf->area_net) { /* It is a default route for stub areas, handled entirely in ospf_rt_abr() */ - if (nf->fn.pxlen == 0) + if (nf->fn.addr->pxlen == 0) return; /* Find that area network */ WALK_LIST(anet_oa, p->area_list) { - anet = (struct area_net *) fib_find(&anet_oa->net_fib, &nf->fn.prefix, nf->fn.pxlen); + anet = fib_find(&anet_oa->net_fib, nf->fn.addr); if (anet) break; } @@ -1042,10 +1035,12 @@ check_sum_net_lsa(struct ospf_proto *p, ort *nf) static inline void check_sum_rt_lsa(struct ospf_proto *p, ort *nf) { + u32 rid = rid_from_net(nf->fn.addr); + struct ospf_area *oa; WALK_LIST(oa, p->area_list) if (decide_sum_lsa(oa, nf, ORT_ROUTER)) - ospf_originate_sum_rt_lsa(p, oa, nf, nf->n.metric1, nf->n.options); + ospf_originate_sum_rt_lsa(p, oa, rid, nf->n.metric1, nf->n.options); } static inline int @@ -1058,7 +1053,7 @@ decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt) return 0; /* Condensed area network found */ - if (fib_route(&oa->enet_fib, nf->fn.prefix, nf->fn.pxlen)) + if (fib_route(&oa->enet_fib, nf->fn.addr)) return 0; if (!en || (en->lsa_type != LSA_T_NSSA)) @@ -1093,7 +1088,7 @@ check_nssa_lsa(struct ospf_proto *p, ort *nf) /* Find that area network */ WALK_LIST(oa, p->area_list) { - anet = (struct area_net *) fib_find(&oa->enet_fib, &nf->fn.prefix, nf->fn.pxlen); + anet = fib_find(&oa->enet_fib, nf->fn.addr); if (anet) break; } @@ -1163,24 +1158,20 @@ static void ospf_rt_abr1(struct ospf_proto *p) { struct area_net *anet; - ort *nf, *default_nf; + ort *default_nf; + net_addr default_net; /* RFC 2328 G.3 - incomplete resolution of virtual next hops - routers */ - FIB_WALK(&p->backbone->rtr, nftmp) + FIB_WALK(&p->backbone->rtr, ort, nf) { - nf = (ort *) nftmp; - if (nf->n.type && unresolved_vlink(nf)) reset_ri(nf); } FIB_WALK_END; - FIB_WALK(&p->rtf, nftmp) + FIB_WALK(&p->rtf, ort, nf) { - nf = (ort *) nftmp; - - /* RFC 2328 G.3 - incomplete resolution of virtual next hops - networks */ if (nf->n.type && unresolved_vlink(nf)) reset_ri(nf); @@ -1189,7 +1180,7 @@ ospf_rt_abr1(struct ospf_proto *p) /* Compute condensed area networks */ if (nf->n.type == RTS_OSPF) { - anet = (struct area_net *) fib_route(&nf->n.oa->net_fib, nf->fn.prefix, nf->fn.pxlen); + anet = (struct area_net *) fib_route(&nf->n.oa->net_fib, nf->fn.addr); if (anet) { if (!anet->active) @@ -1197,7 +1188,7 @@ ospf_rt_abr1(struct ospf_proto *p) anet->active = 1; /* Get a RT entry and mark it to know that it is an area network */ - ort *nfi = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen); + ort *nfi = fib_get(&p->rtf, anet->fn.addr); nfi->area_net = 1; /* 16.2. (3) */ @@ -1212,8 +1203,13 @@ ospf_rt_abr1(struct ospf_proto *p) } FIB_WALK_END; - ip_addr addr = IPA_NONE; - default_nf = (ort *) fib_get(&p->rtf, &addr, 0); + + if (ospf_is_v2(p)) + net_fill_ip4(&default_net, IP4_NONE, 0); + else + net_fill_ip6(&default_net, IP6_NONE, 0); + + default_nf = fib_get(&p->rtf, &default_net); default_nf->area_net = 1; struct ospf_area *oa; @@ -1240,11 +1236,10 @@ ospf_rt_abr1(struct ospf_proto *p) /* RFC 2328 16.4. (3) - precompute preferred ASBR entries */ if (oa_is_ext(oa)) { - FIB_WALK(&oa->rtr, nftmp) + FIB_WALK(&oa->rtr, ort, nf) { - nf = (ort *) nftmp; if (nf->n.options & ORTA_ASBR) - ri_install_asbr(p, &nf->fn.prefix, &nf->n); + ri_install_asbr(p, rid_from_net(nf->fn.addr), &nf->n); } FIB_WALK_END; } @@ -1252,9 +1247,9 @@ ospf_rt_abr1(struct ospf_proto *p) /* Originate or flush ASBR summary LSAs */ - FIB_WALK(&p->backbone->rtr, nftmp) + FIB_WALK(&p->backbone->rtr, ort, nf) { - check_sum_rt_lsa(p, (ort *) nftmp); + check_sum_rt_lsa(p, nf); } FIB_WALK_END; @@ -1281,8 +1276,6 @@ ospf_rt_abr2(struct ospf_proto *p) { struct ospf_area *oa; struct top_hash_entry *en; - ort *nf, *nf2; - /* RFC 3103 3.1 - type-7 translator election */ struct ospf_area *bb = p->backbone; @@ -1294,13 +1287,12 @@ ospf_rt_abr2(struct ospf_proto *p) if (oa->ac->translator) goto decided; - FIB_WALK(&oa->rtr, nftmp) + FIB_WALK(&oa->rtr, ort, nf) { - nf = (ort *) nftmp; if (!nf->n.type || !(nf->n.options & ORTA_ABR)) continue; - nf2 = fib_find(&bb->rtr, &nf->fn.prefix, MAX_PREFIX_LENGTH); + ort *nf2 = fib_find(&bb->rtr, nf->fn.addr); if (!nf2 || !nf2->n.type || !(nf2->n.options & ORTA_ABR)) continue; @@ -1340,13 +1332,11 @@ ospf_rt_abr2(struct ospf_proto *p) /* Compute condensed external networks */ - FIB_WALK(&p->rtf, nftmp) + FIB_WALK(&p->rtf, ort, nf) { - nf = (ort *) nftmp; if (rt_is_nssa(nf) && (nf->n.options & ORTA_PROP)) { - struct area_net *anet = (struct area_net *) - fib_route(&nf->n.oa->enet_fib, nf->fn.prefix, nf->fn.pxlen); + struct area_net *anet = fib_route(&nf->n.oa->enet_fib, nf->fn.addr); if (anet) { @@ -1355,7 +1345,7 @@ ospf_rt_abr2(struct ospf_proto *p) anet->active = 1; /* Get a RT entry and mark it to know that it is an area network */ - nf2 = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen); + ort *nf2 = fib_get(&p->rtf, anet->fn.addr); nf2->area_net = 1; } @@ -1370,10 +1360,8 @@ ospf_rt_abr2(struct ospf_proto *p) FIB_WALK_END; - FIB_WALK(&p->rtf, nftmp) + FIB_WALK(&p->rtf, ort, nf) { - nf = (ort *) nftmp; - check_sum_net_lsa(p, nf); check_nssa_lsa(p, nf); } @@ -1383,22 +1371,57 @@ ospf_rt_abr2(struct ospf_proto *p) /* Like fib_route(), but ignores dummy rt entries */ static void * -ospf_fib_route(struct fib *f, ip_addr a, int len) +ospf_fib_route_ip4(struct fib *f, ip4_addr a, int len) +{ + net_addr_ip4 net = NET_ADDR_IP4(a, len); + ort *nf; + +loop: + nf = fib_find(f, (net_addr *) &net); + if (nf && nf->n.type) + return nf; + + if (net.pxlen > 0) + { + net.pxlen--; + ip4_clrbit(&net.prefix, net.pxlen); + goto loop; + } + + return NULL; +} + +static void * +ospf_fib_route_ip6(struct fib *f, ip6_addr a, int len) { - ip_addr a0; + net_addr_ip6 net = NET_ADDR_IP6(a, len); ort *nf; - while (len >= 0) +loop: + nf = fib_find(f, (net_addr *) &net); + if (nf && nf->n.type) + return nf; + + if (net.pxlen > 0) { - a0 = ipa_and(a, ipa_mkmask(len)); - nf = fib_find(f, &a0, len); - if (nf && nf->n.type) - return nf; - len--; + net.pxlen--; + ip6_clrbit(&net.prefix, net.pxlen); + goto loop; } + return NULL; } +static void * +ospf_fib_route(struct fib *f, ip_addr a) +{ + if (f->addr_type == NET_IP4) + return ospf_fib_route_ip4(f, ipa_to_ip4(a), IP4_MAX_PREFIX_LENGTH); + else + return ospf_fib_route_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH); +} + + /* RFC 2328 16.4. calculating external routes */ static void ospf_ext_spf(struct ospf_proto *p) @@ -1407,7 +1430,6 @@ ospf_ext_spf(struct ospf_proto *p) struct ospf_lsa_ext_local rt; ort *nf1, *nf2; orta nfa = {}; - ip_addr rtid; u32 br_metric; struct ospf_area *atmp; @@ -1431,19 +1453,18 @@ ospf_ext_spf(struct ospf_proto *p) lsa_parse_ext(en, ospf_is_v2(p), &rt); - if (rt.metric == LSINFINITY) - continue; - - if (rt.pxopts & OPT_PX_NU) - continue; - - if (rt.pxlen < 0 || rt.pxlen > MAX_PREFIX_LENGTH) + if (!ospf_valid_prefix(&rt.net)) { log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)", p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt); continue; } + if (rt.metric == LSINFINITY) + continue; + + if (rt.pxopts & OPT_PX_NU) + continue; /* 16.4. (3) */ /* If there are more areas, we already precomputed preferred ASBR @@ -1457,8 +1478,8 @@ ospf_ext_spf(struct ospf_proto *p) if (!atmp) continue; /* Should not happen */ - rtid = ipa_from_rid(en->lsa.rt); - nf1 = fib_find(&atmp->rtr, &rtid, MAX_PREFIX_LENGTH); + net_addr_ip4 nrid = net_from_rid(en->lsa.rt); + nf1 = fib_find(&atmp->rtr, (net_addr *) &nrid); if (!nf1 || !nf1->n.type) continue; /* No AS boundary router found */ @@ -1468,7 +1489,7 @@ ospf_ext_spf(struct ospf_proto *p) /* 16.4. (3) NSSA - special rule for default routes */ /* ABR should use default only if P-bit is set and summaries are active */ - if ((en->lsa_type == LSA_T_NSSA) && ipa_zero(rt.ip) && (rt.pxlen == 0) && + if ((en->lsa_type == LSA_T_NSSA) && (rt.net.pxlen == 0) && (p->areano > 1) && !(rt.propagate && atmp->ac->summary)) continue; @@ -1480,7 +1501,7 @@ ospf_ext_spf(struct ospf_proto *p) } else { - nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, MAX_PREFIX_LENGTH); + nf2 = ospf_fib_route(&p->rtf, rt.fwaddr); if (!nf2) continue; @@ -1542,7 +1563,7 @@ ospf_ext_spf(struct ospf_proto *p) nfa.oa = atmp; /* undefined in RFC 2328 */ nfa.en = en; /* store LSA for later (NSSA processing) */ - ri_install_ext(p, rt.ip, rt.pxlen, &nfa); + ri_install_ext(p, &rt.net, &nfa); } } @@ -1552,13 +1573,10 @@ ospf_rt_reset(struct ospf_proto *p) { struct ospf_area *oa; struct top_hash_entry *en; - struct area_net *anet; - ort *ri; /* Reset old routing table */ - FIB_WALK(&p->rtf, nftmp) + FIB_WALK(&p->rtf, ort, ri) { - ri = (ort *) nftmp; ri->area_net = 0; reset_ri(ri); } @@ -1579,9 +1597,8 @@ ospf_rt_reset(struct ospf_proto *p) WALK_LIST(oa, p->area_list) { /* Reset ASBR routing tables */ - FIB_WALK(&oa->rtr, nftmp) + FIB_WALK(&oa->rtr, ort, ri) { - ri = (ort *) nftmp; reset_ri(ri); } FIB_WALK_END; @@ -1589,17 +1606,15 @@ ospf_rt_reset(struct ospf_proto *p) /* Reset condensed area networks */ if (p->areano > 1) { - FIB_WALK(&oa->net_fib, nftmp) + FIB_WALK(&oa->net_fib, struct area_net, anet) { - anet = (struct area_net *) nftmp; anet->active = 0; anet->metric = 0; } FIB_WALK_END; - FIB_WALK(&oa->enet_fib, nftmp) + FIB_WALK(&oa->enet_fib, struct area_net, anet) { - anet = (struct area_net *) nftmp; anet->active = 0; anet->metric = 0; } @@ -1901,7 +1916,6 @@ rt_sync(struct ospf_proto *p) struct top_hash_entry *en; struct fib_iterator fit; struct fib *fib = &p->rtf; - ort *nf; struct ospf_area *oa; /* This is used for forced reload of routes */ @@ -1912,10 +1926,8 @@ rt_sync(struct ospf_proto *p) DBG("Now syncing my rt table with nest's\n"); FIB_ITERATE_INIT(&fit, fib); again1: - FIB_ITERATE_START(fib, &fit, nftmp) + FIB_ITERATE_START(fib, &fit, ort, nf) { - nf = (ort *) nftmp; - /* Sanity check of next-hop addresses, failure should not happen */ if (nf->n.type) { @@ -1961,7 +1973,7 @@ again1: if (reload || ort_changed(nf, &a0)) { - net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen); + net *ne = net_get(p->p.main_channel->table, nf->fn.addr); rta *a = rta_lookup(&a0); rte *e = rte_get_temp(a); @@ -1973,10 +1985,9 @@ again1: e->u.ospf.router_id = nf->old_rid = nf->n.rid; e->pflags = 0; e->net = ne; - e->pref = p->p.preference; - DBG("Mod rte type %d - %I/%d via %I on iface %s, met %d\n", - a0.source, nf->fn.prefix, nf->fn.pxlen, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1); + DBG("Mod rte type %d - %N via %I on iface %s, met %d\n", + a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1); rte_update(&p->p, ne, e); } } @@ -1986,19 +1997,22 @@ again1: rta_free(nf->old_rta); nf->old_rta = NULL; - net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen); + net *ne = net_get(p->p.main_channel->table, nf->fn.addr); rte_update(&p->p, ne, NULL); } /* Remove unused rt entry, some special entries are persistent */ if (!nf->n.type && !nf->external_rte && !nf->area_net) { - FIB_ITERATE_PUT(&fit, nftmp); - fib_delete(fib, nftmp); + if (nf->lsa_id) + idm_free(&p->idm, nf->lsa_id); + + FIB_ITERATE_PUT(&fit); + fib_delete(fib, nf); goto again1; } } - FIB_ITERATE_END(nftmp); + FIB_ITERATE_END; WALK_LIST(oa, p->area_list) @@ -2006,18 +2020,16 @@ again1: /* Cleanup ASBR hash tables */ FIB_ITERATE_INIT(&fit, &oa->rtr); again2: - FIB_ITERATE_START(&oa->rtr, &fit, nftmp) + FIB_ITERATE_START(&oa->rtr, &fit, ort, nf) { - nf = (ort *) nftmp; - if (!nf->n.type) { - FIB_ITERATE_PUT(&fit, nftmp); - fib_delete(&oa->rtr, nftmp); + FIB_ITERATE_PUT(&fit); + fib_delete(&oa->rtr, nf); goto again2; } } - FIB_ITERATE_END(nftmp); + FIB_ITERATE_END; } /* Cleanup stale LSAs */ diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h index 30332f3b..959d12e9 100644 --- a/proto/ospf/rt.h +++ b/proto/ospf/rt.h @@ -78,12 +78,14 @@ typedef struct ort * route was not in the last update, in that case other old_* values are not * valid. */ - struct fib_node fn; orta n; u32 old_metric1, old_metric2, old_tag, old_rid; rta *old_rta; + u32 lsa_id; u8 external_rte; u8 area_net; + + struct fib_node fn; } ort; diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 7558d4a0..86e39d75 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -283,8 +283,8 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa) if (en->nf != lsa->nf) { - log(L_ERR "%s: LSA ID collision for %I/%d", - p->p.name, lsa->nf->fn.prefix, lsa->nf->fn.pxlen); + log(L_ERR "%s: LSA ID collision for %N", + p->p.name, lsa->nf->fn.addr); en = NULL; goto drop; @@ -513,15 +513,14 @@ ospf_update_lsadb(struct ospf_proto *p) } } - -static inline u32 +static u32 ort_to_lsaid(struct ospf_proto *p, ort *nf) { /* * In OSPFv2, We have to map IP prefixes to u32 in such manner that resulting * u32 interpreted as IP address is a member of given prefix. Therefore, /32 - * prefix have to be mapped on itself. All received prefixes have to be - * mapped on different u32s. + * prefix has to be mapped on itself. All received prefixes have to be mapped + * on different u32s. * * We have an assumption that if there is nontrivial (non-/32) network prefix, * then there is not /32 prefix for the first and the last IP address of the @@ -542,17 +541,21 @@ ort_to_lsaid(struct ospf_proto *p, ort *nf) * network appeared, we choose a different way. * * In OSPFv3, it is simpler. There is not a requirement for membership of the - * result in the input network, so we just use a hash-based unique ID of a - * routing table entry for a route that originated given LSA. For ext-LSA, it - * is an imported route in the nest's routing table (p->table). For summary-LSA, - * it is a 'source' route in the protocol internal routing table (p->rtf). + * result in the input network, so we just allocate a unique ID from ID map + * and store it in nf->lsa_id for further reference. */ if (ospf_is_v3(p)) - return nf->fn.uid; + { + if (!nf->lsa_id) + nf->lsa_id = idm_alloc(&p->idm); - u32 id = ipa_to_u32(nf->fn.prefix); - int pxlen = nf->fn.pxlen; + return nf->lsa_id; + } + + net_addr_ip4 *net = (void *) nf->fn.addr; + u32 id = ip4_to_u32(net->prefix); + int pxlen = net->pxlen; if ((pxlen == 0) || (pxlen == 32)) return id; @@ -628,12 +631,12 @@ configured_stubnet(struct ospf_area *oa, struct ifa *a) { if (sn->summary) { - if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len)) + if (net_in_netX(&a->prefix, &sn->prefix)) return 1; } else { - if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len)) + if (net_equal(&a->prefix, &sn->prefix)) return 1; } } @@ -781,7 +784,8 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) (ifa->type == OSPF_IT_PTMP)) add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0); else - add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost); + add_rt2_lsa_link(p, LSART_STUB, ip4_to_u32(net4_prefix(&ifa->addr->prefix)), + u32_mkmask(net4_pxlen(&ifa->addr->prefix)), ifa->cost); i++; ifa->rt_pos_end = i; @@ -790,7 +794,8 @@ prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa) struct ospf_stubnet_config *sn; WALK_LIST(sn, oa->ac->stubnet_list) if (!sn->hidden) - add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++; + add_rt2_lsa_link(p, LSART_STUB, ip4_to_u32(net4_prefix(&sn->prefix)), + u32_mkmask(net4_pxlen(&sn->prefix)), sn->cost), i++; struct ospf_lsa_rt *rt = p->lsab; /* Store number of links in lower half of options */ @@ -907,7 +912,7 @@ prepare_net2_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) ASSERT(p->lsab_used == 0); net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes); - net->optx = u32_mkmask(ifa->addr->pxlen); + net->optx = u32_mkmask(ifa->addr->prefix.pxlen); net->routers[0] = p->router_id; WALK_LIST(n, ifa->neigh_list) @@ -999,9 +1004,10 @@ prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric) { struct ospf_lsa_sum3_net *sum; - sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(nf->fn.pxlen)); + sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + + IPV6_PREFIX_SPACE(nf->fn.addr->pxlen)); sum->metric = metric; - put_ipv6_prefix(sum->prefix, nf->fn.prefix, nf->fn.pxlen, 0, 0); + ospf_put_ipv6_prefix(sum->prefix, nf->fn.addr, 0, 0); } static inline void @@ -1028,7 +1034,7 @@ ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, }; if (ospf_is_v2(p)) - prepare_sum2_lsa_body(p, nf->fn.pxlen, metric); + prepare_sum2_lsa_body(p, nf->fn.addr->pxlen, metric); else prepare_sum3_net_lsa_body(p, nf, metric); @@ -1036,20 +1042,20 @@ ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, } void -ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options) +ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options) { struct ospf_new_lsa lsa = { .type = LSA_T_SUM_RT, .mode = LSA_M_RTCALC, .dom = oa->areaid, - .id = ipa_to_rid(nf->fn.prefix), /* Router ID of ASBR, irrelevant for OSPFv3 */ + .id = drid, /* Router ID of ASBR, irrelevant for OSPFv3 */ .opts = oa->options }; if (ospf_is_v2(p)) prepare_sum2_lsa_body(p, 0, metric); else - prepare_sum3_rt_lsa_body(p, lsa.id, metric, options & LSA_OPTIONS_MASK); + prepare_sum3_rt_lsa_body(p, drid, metric, options & LSA_OPTIONS_MASK); ospf_originate_lsa(p, &lsa); } @@ -1082,7 +1088,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, { struct ospf_lsa_ext3 *ext; int bsize = sizeof(struct ospf_lsa_ext3) - + IPV6_PREFIX_SPACE(nf->fn.pxlen) + + IPV6_PREFIX_SPACE(nf->fn.addr->pxlen) + (ipa_nonzero(fwaddr) ? 16 : 0) + (tag ? 4 : 0); @@ -1090,7 +1096,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, ext->metric = metric & LSA_METRIC_MASK; u32 *buf = ext->rest; - buf = put_ipv6_prefix(buf, nf->fn.prefix, nf->fn.pxlen, pbit ? OPT_PX_P : 0, 0); + buf = ospf_put_ipv6_prefix(buf, nf->fn.addr, pbit ? OPT_PX_P : 0, 0); if (ebit) ext->metric |= LSA_EXT3_EBIT; @@ -1098,7 +1104,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf, if (ipa_nonzero(fwaddr)) { ext->metric |= LSA_EXT3_FBIT; - buf = put_ipv6_addr(buf, fwaddr); + buf = ospf_put_ipv6_addr(buf, fwaddr); } if (tag) @@ -1140,7 +1146,7 @@ ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 m }; if (ospf_is_v2(p)) - prepare_ext2_lsa_body(p, nf->fn.pxlen, metric, ebit, fwaddr, tag); + prepare_ext2_lsa_body(p, nf->fn.addr->pxlen, metric, ebit, fwaddr, tag); else prepare_ext3_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit); @@ -1177,7 +1183,7 @@ use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface) WALK_LIST(ifa, p->iface_list) if ((ifa->iface == iface) && - (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen))) + (!ospf_is_v2(p) || ipa_in_netX(gw, &ifa->addr->prefix))) return 1; return 0; @@ -1215,7 +1221,8 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa) { WALK_LIST(a, ifa->iface->addrs) { - if ((a->flags & IA_SECONDARY) || + if ((a->prefix.type != NET_IP6) || + (a->flags & IA_SECONDARY) || (a->flags & IA_PEER) || (a->scope <= SCOPE_LINK)) continue; @@ -1234,7 +1241,7 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa) } void -ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea) +ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea) { struct ospf_proto *p = (struct ospf_proto *) P; struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */ @@ -1253,7 +1260,7 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U if (!new) { - nf = (ort *) fib_find(&p->rtf, &n->n.prefix, n->n.pxlen); + nf = fib_find(&p->rtf, n->n.addr); if (!nf || !nf->external_rte) return; @@ -1290,13 +1297,13 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U if (ipa_zero(fwd)) { - log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d", - p->p.name, n->n.prefix, n->n.pxlen); + log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %N", + p->p.name, n->n.addr); return; } } - nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen); + nf = fib_get(&p->rtf, n->n.addr); ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1); nf->external_rte = 1; } @@ -1308,11 +1315,11 @@ ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old U */ static inline void -lsab_put_prefix(struct ospf_proto *p, ip_addr prefix, u32 pxlen, u32 cost) +lsab_put_prefix(struct ospf_proto *p, net_addr *net, u32 cost) { - void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(pxlen)); - u8 flags = (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA; - put_ipv6_prefix(buf, prefix, pxlen, flags, cost); + void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(net6_pxlen(net))); + u8 flags = (net6_pxlen(net) < IP6_MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA; + ospf_put_ipv6_prefix(buf, net, flags, cost); } static void @@ -1330,11 +1337,12 @@ prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa) struct ifa *a; WALK_LIST(a, ifa->iface->addrs) { - if ((a->flags & IA_SECONDARY) || - (a->scope < SCOPE_SITE)) + if ((a->prefix.type != NET_IP6) || + (a->flags & IA_SECONDARY) || + (a->scope <= SCOPE_LINK)) continue; - lsab_put_prefix(p, a->prefix, a->pxlen, 0); + lsab_put_prefix(p, &a->prefix, 0); i++; } @@ -1401,12 +1409,13 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa) struct ifa *a; WALK_LIST(a, ifa->iface->addrs) { - if ((a->flags & IA_SECONDARY) || + if ((a->prefix.type != NET_IP6) || + (a->flags & IA_SECONDARY) || (a->flags & IA_PEER) || (a->scope <= SCOPE_LINK)) continue; - if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) || + if (((a->prefix.pxlen < IP6_MAX_PREFIX_LENGTH) && net_lsa) || configured_stubnet(oa, a)) continue; @@ -1414,11 +1423,12 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa) (ifa->state == OSPF_IS_LOOP) || (ifa->type == OSPF_IT_PTMP)) { - lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0); + net_addr_ip6 net = NET_ADDR_IP6(a->ip, IP6_MAX_PREFIX_LENGTH); + lsab_put_prefix(p, (net_addr *) &net, 0); host_addr = 1; } else - lsab_put_prefix(p, a->prefix, a->pxlen, ifa->cost); + lsab_put_prefix(p, &a->prefix, ifa->cost); i++; } @@ -1429,8 +1439,8 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa) WALK_LIST(sn, oa->ac->stubnet_list) if (!sn->hidden) { - lsab_put_prefix(p, sn->px.addr, sn->px.len, sn->cost); - if (sn->px.len == MAX_PREFIX_LENGTH) + lsab_put_prefix(p, &sn->prefix, sn->cost); + if (sn->prefix.pxlen == IP6_MAX_PREFIX_LENGTH) host_addr = 1; i++; } @@ -1447,11 +1457,14 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa) struct ifa *a; WALK_LIST(a, ifa->iface->addrs) { - if ((a->flags & IA_SECONDARY) || (a->scope <= SCOPE_LINK)) + if ((a->prefix.type != NET_IP6) || + (a->flags & IA_SECONDARY) || + (a->scope <= SCOPE_LINK)) continue; /* Found some IP */ - lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0); + net_addr_ip6 net = NET_ADDR_IP6(a->ip, IP6_MAX_PREFIX_LENGTH); + lsab_put_prefix(p, (net_addr *) &net, 0); i++; goto done; } diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index 5652ced0..38447fdf 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -185,10 +185,10 @@ static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry * { if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } } void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric); -void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options); +void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options); void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit); -void ospf_rt_notify(struct proto *P, rtable *tbl, net *n, rte *new, rte *old, ea_list *attrs); +void ospf_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old, ea_list *attrs); void ospf_update_topology(struct ospf_proto *p); struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type); diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y index 8daf2e7c..f51ee575 100644 --- a/proto/pipe/config.Y +++ b/proto/pipe/config.Y @@ -16,28 +16,25 @@ CF_DEFINES CF_DECLS -CF_KEYWORDS(PIPE, PEER, TABLE, MODE, OPAQUE, TRANSPARENT) +CF_KEYWORDS(PIPE, PEER, TABLE) CF_GRAMMAR -CF_ADDTO(proto, pipe_proto '}') +CF_ADDTO(proto, pipe_proto '}' { this_channel = NULL; } ) -pipe_proto_start: proto_start PIPE { - this_proto = proto_config_new(&proto_pipe, $1); - PIPE_CFG->mode = PIPE_TRANSPARENT; - } - ; +pipe_proto_start: proto_start PIPE +{ + this_proto = proto_config_new(&proto_pipe, $1); + this_channel = channel_config_new(NULL, 0, this_proto); + this_channel->in_filter = FILTER_ACCEPT; + this_channel->out_filter = FILTER_ACCEPT; +}; pipe_proto: pipe_proto_start proto_name '{' | pipe_proto proto_item ';' - | pipe_proto PEER TABLE SYM ';' { - if ($4->class != SYM_TABLE) - cf_error("Routing table name expected"); - PIPE_CFG->peer = $4->def; - } - | pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; } - | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; } + | pipe_proto channel_item ';' + | pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; } ; CF_CODE diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 6ef80322..f3df3e71 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -44,11 +44,10 @@ #include "pipe.h" static void -pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs) +pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old, ea_list *attrs) { - struct pipe_proto *p = (struct pipe_proto *) P; - struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook; - rtable *dst_table = ah->table; + struct pipe_proto *p = (void *) P; + struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri; struct rte_src *src; net *nn; @@ -58,24 +57,18 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e if (!new && !old) return; - if (dst_table->pipe_busy) + if (dst->table->pipe_busy) { - log(L_ERR "Pipe loop detected when sending %I/%d to table %s", - n->n.prefix, n->n.pxlen, dst_table->name); + log(L_ERR "Pipe loop detected when sending %N to table %s", + n->n.addr, dst->table->name); return; } - nn = net_get(dst_table, n->n.prefix, n->n.pxlen); + nn = net_get(dst->table, n->n.addr); if (new) { memcpy(&a, new->attrs, sizeof(rta)); - if (p->mode == PIPE_OPAQUE) - { - a.src = P->main_source; - a.source = RTS_PIPE; - } - a.aflags = 0; a.eattrs = attrs; a.hostentry = NULL; @@ -83,13 +76,10 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e e->net = nn; e->pflags = 0; - if (p->mode == PIPE_TRANSPARENT) - { - /* Copy protocol specific embedded attributes. */ - memcpy(&(e->u), &(new->u), sizeof(e->u)); - e->pref = new->pref; - e->pflags = new->pflags; - } + /* Copy protocol specific embedded attributes. */ + memcpy(&(e->u), &(new->u), sizeof(e->u)); + e->pref = new->pref; + e->pflags = new->pflags; src = a.src; } @@ -99,9 +89,9 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e src = old->attrs->src; } - src_table->pipe_busy = 1; - rte_update2(ah, nn, e, src); - src_table->pipe_busy = 0; + src_ch->table->pipe_busy = 1; + rte_update2(dst, nn, e, src); + src_ch->table->pipe_busy = 0; } static int @@ -111,171 +101,117 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo if (pp == P) return -1; /* Avoid local loops automatically */ + return 0; } -static int -pipe_reload_routes(struct proto *P) +static void +pipe_reload_routes(struct channel *C) { - struct pipe_proto *p = (struct pipe_proto *) P; - - /* - * Because the pipe protocol feeds routes from both routing tables - * together, both directions are reloaded during refeed and 'reload - * out' command works like 'reload' command. For symmetry, we also - * request refeed when 'reload in' command is used. - */ - proto_request_feeding(P); + struct pipe_proto *p = (void *) C->proto; - proto_reset_limit(P->main_ahook->in_limit); - proto_reset_limit(p->peer_ahook->in_limit); - - return 1; + /* Route reload on one channel is just refeed on the other */ + channel_request_feeding((C == p->pri) ? p->sec : p->pri); } -static struct proto * -pipe_init(struct proto_config *C) -{ - struct pipe_config *c = (struct pipe_config *) C; - struct proto *P = proto_new(C, sizeof(struct pipe_proto)); - struct pipe_proto *p = (struct pipe_proto *) P; - p->mode = c->mode; - p->peer_table = c->peer->table; - P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY; - P->rt_notify = pipe_rt_notify; - P->import_control = pipe_import_control; - P->reload_routes = pipe_reload_routes; - - return P; -} - -static int -pipe_start(struct proto *P) +static void +pipe_postconfig(struct proto_config *CF) { - struct pipe_config *cf = (struct pipe_config *) P->cf; - struct pipe_proto *p = (struct pipe_proto *) P; + struct pipe_config *cf = (void *) CF; + struct channel_config *cc = proto_cf_main_channel(CF); - /* Lock both tables, unlock is handled in pipe_cleanup() */ - rt_lock_table(P->table); - rt_lock_table(p->peer_table); + if (!cc->table) + cf_error("Primary routing table not specified"); - /* Going directly to PS_UP - prepare for feeding, - connect the protocol to both routing tables */ + if (!cf->peer) + cf_error("Secondary routing table not specified"); - P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats); - P->main_ahook->out_filter = cf->c.out_filter; - P->main_ahook->in_limit = cf->c.in_limit; - proto_reset_limit(P->main_ahook->in_limit); + if (cc->table == cf->peer) + cf_error("Primary table and peer table must be different"); - p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats); - p->peer_ahook->out_filter = cf->c.in_filter; - p->peer_ahook->in_limit = cf->c.out_limit; - proto_reset_limit(p->peer_ahook->in_limit); + if (cc->table->addr_type != cf->peer->addr_type) + cf_error("Primary table and peer table must have the same type"); - if (p->mode == PIPE_OPAQUE) - { - P->main_source = rt_get_source(P, 0); - rt_lock_source(P->main_source); - } + if (cc->rx_limit.action) + cf_error("Pipe protocol does not support receive limits"); - return PS_UP; + if (cc->in_keep_filtered) + cf_error("Pipe protocol prohibits keeping filtered routes"); } -static void -pipe_cleanup(struct proto *P) +static int +pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf) { - struct pipe_proto *p = (struct pipe_proto *) P; - - bzero(&P->stats, sizeof(struct proto_stats)); - bzero(&p->peer_stats, sizeof(struct proto_stats)); - - P->main_ahook = NULL; - p->peer_ahook = NULL; - - if (p->mode == PIPE_OPAQUE) - rt_unlock_source(P->main_source); - P->main_source = NULL; - - rt_unlock_table(P->table); - rt_unlock_table(p->peer_table); + struct channel_config *cc = proto_cf_main_channel(&cf->c); + + struct channel_config pri_cf = { + .name = "pri", + .channel = cc->channel, + .table = cc->table, + .out_filter = cc->out_filter, + .in_limit = cc->in_limit, + .ra_mode = RA_ANY + }; + + struct channel_config sec_cf = { + .name = "sec", + .channel = cc->channel, + .table = cf->peer, + .out_filter = cc->in_filter, + .in_limit = cc->out_limit, + .ra_mode = RA_ANY + }; + + return + proto_configure_channel(&p->p, &p->pri, &pri_cf) && + proto_configure_channel(&p->p, &p->sec, &sec_cf); } -static void -pipe_postconfig(struct proto_config *C) +static struct proto * +pipe_init(struct proto_config *CF) { - struct pipe_config *c = (struct pipe_config *) C; + struct proto *P = proto_new(CF); + struct pipe_proto *p = (void *) P; + struct pipe_config *cf = (void *) CF; - if (!c->peer) - cf_error("Name of peer routing table not specified"); - if (c->peer == C->table) - cf_error("Primary table and peer table must be different"); + P->rt_notify = pipe_rt_notify; + P->import_control = pipe_import_control; + P->reload_routes = pipe_reload_routes; - if (C->in_keep_filtered) - cf_error("Pipe protocol prohibits keeping filtered routes"); - if (C->rx_limit) - cf_error("Pipe protocol does not support receive limits"); -} + pipe_configure_channels(p, cf); -extern int proto_reconfig_type; + return P; +} static int -pipe_reconfigure(struct proto *P, struct proto_config *new) +pipe_reconfigure(struct proto *P, struct proto_config *CF) { - struct pipe_proto *p = (struct pipe_proto *)P; - struct proto_config *old = P->cf; - struct pipe_config *oc = (struct pipe_config *) old; - struct pipe_config *nc = (struct pipe_config *) new; - - if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode)) - return 0; - - /* Update output filters in ahooks */ - if (P->main_ahook) - { - P->main_ahook->out_filter = new->out_filter; - P->main_ahook->in_limit = new->in_limit; - proto_verify_limits(P->main_ahook); - } - - if (p->peer_ahook) - { - p->peer_ahook->out_filter = new->in_filter; - p->peer_ahook->in_limit = new->out_limit; - proto_verify_limits(p->peer_ahook); - } - - if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT)) - return 1; - - if ((new->preference != old->preference) - || ! filter_same(new->in_filter, old->in_filter) - || ! filter_same(new->out_filter, old->out_filter)) - proto_request_feeding(P); + struct pipe_proto *p = (void *) P; + struct pipe_config *cf = (void *) CF; - return 1; + return pipe_configure_channels(p, cf); } static void pipe_copy_config(struct proto_config *dest, struct proto_config *src) { /* Just a shallow copy, not many items here */ - proto_copy_rest(dest, src, sizeof(struct pipe_config)); } static void pipe_get_status(struct proto *P, byte *buf) { - struct pipe_proto *p = (struct pipe_proto *) P; + struct pipe_proto *p = (void *) P; - bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name); + bsprintf(buf, "%s <=> %s", p->pri->table->name, p->sec->table->name); } static void pipe_show_stats(struct pipe_proto *p) { - struct proto_stats *s1 = &p->p.stats; - struct proto_stats *s2 = &p->peer_stats; + struct proto_stats *s1 = &p->pri->stats; + struct proto_stats *s2 = &p->sec->stats; /* * Pipe stats (as anything related to pipes) are a bit tricky. There @@ -318,17 +254,16 @@ pipe_show_stats(struct pipe_proto *p) static void pipe_show_proto_info(struct proto *P) { - struct pipe_proto *p = (struct pipe_proto *) P; - struct pipe_config *cf = (struct pipe_config *) P->cf; + struct pipe_proto *p = (void *) P; - // cli_msg(-1006, " Table: %s", P->table->name); - // cli_msg(-1006, " Peer table: %s", p->peer_table->name); - cli_msg(-1006, " Preference: %d", P->preference); - cli_msg(-1006, " Input filter: %s", filter_name(cf->c.in_filter)); - cli_msg(-1006, " Output filter: %s", filter_name(cf->c.out_filter)); + cli_msg(-1006, " Channel %s", "main"); + cli_msg(-1006, " Table: %s", p->pri->table->name); + cli_msg(-1006, " Peer table: %s", p->sec->table->name); + cli_msg(-1006, " Import filter: %s", filter_name(p->sec->out_filter)); + cli_msg(-1006, " Export filter: %s", filter_name(p->pri->out_filter)); - proto_show_limit(cf->c.in_limit, "Import limit:"); - proto_show_limit(cf->c.out_limit, "Export limit:"); + channel_show_limit(&p->pri->in_limit, "Import limit:"); + channel_show_limit(&p->sec->in_limit, "Export limit:"); if (P->proto_state != PS_DOWN) pipe_show_stats(p); @@ -338,13 +273,10 @@ pipe_show_proto_info(struct proto *P) struct protocol proto_pipe = { .name = "Pipe", .template = "pipe%d", - .multitable = 1, - .preference = DEF_PREF_PIPE, + .proto_size = sizeof(struct pipe_proto), .config_size = sizeof(struct pipe_config), .postconfig = pipe_postconfig, .init = pipe_init, - .start = pipe_start, - .cleanup = pipe_cleanup, .reconfigure = pipe_reconfigure, .copy_config = pipe_copy_config, .get_status = pipe_get_status, diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h index 50b31698..038c6666 100644 --- a/proto/pipe/pipe.h +++ b/proto/pipe/pipe.h @@ -9,27 +9,15 @@ #ifndef _BIRD_PIPE_H_ #define _BIRD_PIPE_H_ -#define PIPE_OPAQUE 0 -#define PIPE_TRANSPARENT 1 - struct pipe_config { struct proto_config c; struct rtable_config *peer; /* Table we're connected to */ - int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */ }; struct pipe_proto { struct proto p; - struct rtable *peer_table; - struct announce_hook *peer_ahook; /* Announce hook for direction peer->primary */ - struct proto_stats peer_stats; /* Statistics for the direction peer->primary */ - int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */ + struct channel *pri; + struct channel *sec; }; - -extern struct protocol proto_pipe; - -static inline int proto_is_pipe(struct proto *p) -{ return p->proto == &proto_pipe; } - #endif diff --git a/proto/radv/config.Y b/proto/radv/config.Y index da300667..7ba23205 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -41,6 +41,7 @@ CF_ADDTO(proto, radv_proto) radv_proto_start: proto_start RADV { this_proto = proto_config_new(&proto_radv, $1); + init_list(&RADV_CFG->patt_list); init_list(&RADV_CFG->pref_list); init_list(&RADV_CFG->rdnss_list); @@ -49,15 +50,12 @@ radv_proto_start: proto_start RADV radv_proto_item: proto_item + | proto_channel | INTERFACE radv_iface | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); } - | TRIGGER prefix { - RADV_CFG->trigger_prefix = $2.addr; - RADV_CFG->trigger_pxlen = $2.len; - RADV_CFG->trigger_valid = 1; - } + | TRIGGER net_ip6 { RADV_CFG->trigger = $2; } ; radv_proto_opts: @@ -148,11 +146,10 @@ radv_iface: radv_iface_start iface_patt_list_nopx radv_iface_opt_list radv_iface_finish; -radv_prefix_start: prefix +radv_prefix_start: net_ip6 { this_radv_prefix = cfg_allocz(sizeof(struct radv_prefix_config)); - RADV_PREFIX->prefix = $1.addr; - RADV_PREFIX->pxlen = $1.len; + RADV_PREFIX->prefix = *(net_addr_ip6 *) &($1); RADV_PREFIX->onlink = 1; RADV_PREFIX->autonomous = 1; diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 3862af8d..8f6a1913 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -38,7 +38,7 @@ struct radv_opt_prefix u32 valid_lifetime; u32 preferred_lifetime; u32 reserved; - ip_addr prefix; + ip6_addr prefix; }; #define OPT_PX_ONLINK 0x80 @@ -58,7 +58,7 @@ struct radv_opt_rdnss u8 length; u16 reserved; u32 lifetime; - ip_addr servers[]; + ip6_addr servers[]; }; struct radv_opt_dnssl @@ -90,11 +90,11 @@ radv_prefix_match(struct radv_iface *ifa, struct ifa *a) return NULL; WALK_LIST(pc, ifa->cf->pref_list) - if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) + if (net_in_netX(&a->prefix, (net_addr *) &pc->prefix)) return pc; WALK_LIST(pc, cf->pref_list) - if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) + if (net_in_netX(&a->prefix, (net_addr *) &pc->prefix)) return pc; return &default_prefix; @@ -109,7 +109,7 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *b { struct radv_rdnss_config *rcf_base = rcf; struct radv_opt_rdnss *op = (void *) *buf; - int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr); + int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip6_addr); int i = 0; if (max_i < 1) @@ -123,20 +123,19 @@ radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *b else op->lifetime = htonl(rcf->lifetime); - while(NODE_VALID(rcf) && + while(NODE_VALID(rcf) && (rcf->lifetime == rcf_base->lifetime) && (rcf->lifetime_mult == rcf_base->lifetime_mult)) { if (i >= max_i) goto too_much; - op->servers[i] = rcf->server; - ipa_hton(op->servers[i]); + op->servers[i] = ip6_hton(rcf->server); i++; rcf = NODE_NEXT(rcf); } - + op->length = 1+2*i; *buf += 8 * op->length; } @@ -273,6 +272,9 @@ radv_prepare_ra(struct radv_iface *ifa) struct ifa *addr; WALK_LIST(addr, ifa->iface->addrs) { + if (addr->prefix.type != NET_IP6) + continue; + struct radv_prefix_config *pc; pc = radv_prefix_match(ifa, addr); @@ -288,7 +290,7 @@ radv_prepare_ra(struct radv_iface *ifa) struct radv_opt_prefix *op = (void *) buf; op->type = OPT_PREFIX; op->length = 4; - op->pxlen = addr->pxlen; + op->pxlen = net6_pxlen(&addr->prefix); op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ? @@ -296,8 +298,7 @@ radv_prepare_ra(struct radv_iface *ifa) op->preferred_lifetime = (ra->active || !pc->preferred_lifetime_sensitive) ? htonl(pc->preferred_lifetime) : 0; op->reserved = 0; - op->prefix = addr->prefix; - ipa_hton(op->prefix); + op->prefix = ip6_hton(net6_prefix(&addr->prefix)); buf += sizeof(*op); } @@ -411,6 +412,7 @@ radv_sk_open(struct radv_iface *ifa) sk->type = SK_IP; sk->dport = ICMPV6_PROTO; sk->saddr = ifa->addr->ip; + sk->fam = SK_FAM_IPV6; sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */ sk->rx_hook = radv_rx_hook; diff --git a/proto/radv/radv.c b/proto/radv/radv.c index 6370e006..4c845f7a 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -143,7 +143,7 @@ find_lladdr(struct iface *iface) { struct ifa *a; WALK_LIST(a, iface->addrs) - if (a->scope == SCOPE_LINK) + if ((a->prefix.type == NET_IP6) && (a->scope == SCOPE_LINK)) return a; return NULL; @@ -256,11 +256,16 @@ radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a) radv_iface_notify(ifa, RA_EV_CHANGE); } -static inline int radv_net_match_trigger(struct radv_config *cf, net *n) +static inline int +radv_trigger_valid(struct radv_config *cf) { - return cf->trigger_valid && - (n->n.pxlen == cf->trigger_pxlen) && - ipa_equal(n->n.prefix, cf->trigger_prefix); + return cf->trigger.type != 0; +} + +static inline int +radv_net_match_trigger(struct radv_config *cf, net *n) +{ + return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger); } int @@ -276,7 +281,7 @@ radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct l } static void -radv_rt_notify(struct proto *p, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) +radv_rt_notify(struct proto *p, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED) { struct proto_radv *ra = (struct proto_radv *) p; struct radv_config *cf = (struct radv_config *) (p->cf); @@ -303,23 +308,35 @@ radv_check_active(struct proto_radv *ra) { struct radv_config *cf = (struct radv_config *) (ra->p.cf); - if (! cf->trigger_valid) + if (!radv_trigger_valid(cf)) return 1; - return rt_examine(ra->p.table, cf->trigger_prefix, cf->trigger_pxlen, - &(ra->p), ra->p.cf->out_filter); + struct channel *c =ra->p.main_channel; + return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter); +} + +static void +radv_postconfig(struct proto_config *CF) +{ + // struct radv_config *cf = (void *) CF; + + /* Define default channel */ + if (EMPTY_LIST(CF->channels)) + channel_config_new(NULL, NET_IP6, CF); } static struct proto * -radv_init(struct proto_config *c) +radv_init(struct proto_config *CF) { - struct proto *p = proto_new(c, sizeof(struct proto_radv)); + struct proto *p = proto_new(CF); + + p->main_channel = proto_add_channel(p, proto_cf_main_channel(CF)); - p->accept_ra_types = RA_OPTIMAL; p->import_control = radv_import_control; p->rt_notify = radv_rt_notify; p->if_notify = radv_if_notify; p->ifa_notify = radv_ifa_notify; + return p; } @@ -330,7 +347,7 @@ radv_start(struct proto *p) struct radv_config *cf = (struct radv_config *) (p->cf); init_list(&(ra->iface_list)); - ra->active = !cf->trigger_valid; + ra->active = !radv_trigger_valid(cf); return PS_UP; } @@ -355,11 +372,11 @@ radv_shutdown(struct proto *p) } static int -radv_reconfigure(struct proto *p, struct proto_config *c) +radv_reconfigure(struct proto *p, struct proto_config *CF) { struct proto_radv *ra = (struct proto_radv *) p; // struct radv_config *old = (struct radv_config *) (p->cf); - struct radv_config *new = (struct radv_config *) c; + struct radv_config *new = (struct radv_config *) CF; /* * The question is why there is a reconfigure function for RAdv if @@ -369,7 +386,10 @@ radv_reconfigure(struct proto *p, struct proto_config *c) * causing nodes to temporary remove their default routes. */ - p->cf = c; /* radv_check_active() requires proper p->cf */ + if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF))) + return 0; + + p->cf = CF; /* radv_check_active() requires proper p->cf */ ra->active = radv_check_active(ra); struct iface *iface; @@ -426,7 +446,10 @@ radv_get_status(struct proto *p, byte *buf) struct protocol proto_radv = { .name = "RAdv", .template = "radv%d", + .channel_mask = NB_IP6, + .proto_size = sizeof(struct proto_radv), .config_size = sizeof(struct radv_config), + .postconfig = radv_postconfig, .init = radv_init, .start = radv_start, .shutdown = radv_shutdown, diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 48ba9c1a..f8aa421d 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -50,9 +50,7 @@ struct radv_config list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */ list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */ - ip_addr trigger_prefix; /* Prefix of a trigger route, if defined */ - u8 trigger_pxlen; /* Pxlen of a trigger route, if defined */ - u8 trigger_valid; /* Whether a trigger route is defined */ + net_addr trigger; /* Prefix of a trigger route, if defined */ }; struct radv_iface_config @@ -83,8 +81,7 @@ struct radv_iface_config struct radv_prefix_config { node n; - ip_addr prefix; - int pxlen; + net_addr_ip6 prefix; u8 skip; /* Do not include this prefix to RA */ u8 onlink; /* Standard options from RFC 4261 */ @@ -100,7 +97,7 @@ struct radv_rdnss_config node n; u32 lifetime; /* Valid if lifetime_mult is 0 */ u16 lifetime_mult; /* Lifetime specified as multiple of max_ra_int */ - ip_addr server; /* IP address of recursive DNS server */ + ip6_addr server; /* IP address of recursive DNS server */ }; struct radv_dnssl_config diff --git a/proto/rip/config.Y b/proto/rip/config.Y index 083d2e91..3c8cd0f2 100644 --- a/proto/rip/config.Y +++ b/proto/rip/config.Y @@ -32,32 +32,38 @@ rip_check_auth(void) CF_DECLS -CF_KEYWORDS(RIP, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT, +CF_KEYWORDS(RIP, RIPNG, ECMP, LIMIT, WEIGHT, INFINITY, METRIC, UPDATE, TIMEOUT, GARBAGE, PORT, ADDRESS, MODE, BROADCAST, MULTICAST, PASSIVE, VERSION, SPLIT, HORIZON, POISON, REVERSE, CHECK, ZERO, TIME, BFD, AUTHENTICATION, NONE, PLAINTEXT, CRYPTOGRAPHIC, MD5, TTL, SECURITY, RX, TX, BUFFER, LENGTH, PRIORITY, ONLY, LINK, RIP_METRIC, RIP_TAG) -%type <i> rip_auth +%type <i> rip_variant rip_auth CF_GRAMMAR CF_ADDTO(proto, rip_proto) -rip_proto_start: proto_start RIP +rip_variant: + RIP { $$ = 1; } + | RIPNG { $$ = 0; } + ; + +rip_proto_start: proto_start rip_variant { this_proto = proto_config_new(&proto_rip, $1); - init_list(&RIP_CFG->patt_list); + this_proto->net_type = $2 ? NET_IP4 : NET_IP6; - RIP_CFG->rip2 = RIP_IS_V2; + init_list(&RIP_CFG->patt_list); + RIP_CFG->rip2 = $2; RIP_CFG->infinity = RIP_DEFAULT_INFINITY; - RIP_CFG->min_timeout_time = 60; RIP_CFG->max_garbage_time = 60; }; rip_proto_item: proto_item + | proto_channel | ECMP bool { RIP_CFG->ecmp = $2 ? RIP_DEFAULT_ECMP_LIMIT : 0; } | ECMP bool LIMIT expr { RIP_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); } | INFINITY expr { RIP_CFG->infinity = $2; } diff --git a/proto/rip/packets.c b/proto/rip/packets.c index 9f10fd67..488ac9df 100644 --- a/proto/rip/packets.c +++ b/proto/rip/packets.c @@ -9,6 +9,8 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +#undef LOCAL_DEBUG + #include "rip.h" #include "lib/md5.h" @@ -78,8 +80,7 @@ struct rip_auth_tail /* Internal representation of RTE block data */ struct rip_block { - ip_addr prefix; - int pxlen; + net_addr net; u32 metric; u16 tag; u16 no_af; @@ -115,17 +116,17 @@ rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte) struct rip_block_v2 *block = (void *) pos; block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4); block->tag = htons(rte->tag); - block->network = ip4_hton(ipa_to_ip4(rte->prefix)); - block->netmask = ip4_hton(ip4_mkmask(rte->pxlen)); + block->network = ip4_hton(net4_prefix(&rte->net)); + block->netmask = ip4_hton(ip4_mkmask(net4_pxlen(&rte->net))); block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop)); block->metric = htonl(rte->metric); } else /* RIPng */ { struct rip_block_ng *block = (void *) pos; - block->prefix = ip6_hton(ipa_to_ip6(rte->prefix)); + block->prefix = ip6_hton(net6_prefix(&rte->net)); block->tag = htons(rte->tag); - block->pxlen = rte->pxlen; + block->pxlen = net6_pxlen(&rte->net); block->metric = rte->metric; } } @@ -151,8 +152,8 @@ rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte) if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4))) return 0; - rte->prefix = ipa_from_ip4(ip4_ntoh(block->network)); - rte->pxlen = ip4_masklen(ip4_ntoh(block->netmask)); + uint pxlen = ip4_masklen(ip4_ntoh(block->netmask)); + net_fill_ip4(&rte->net, ip4_ntoh(block->network), pxlen); rte->metric = ntohl(block->metric); rte->tag = ntohs(block->tag); rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop)); @@ -171,8 +172,8 @@ rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte) return 0; } - rte->prefix = ipa_from_ip6(ip6_ntoh(block->prefix)); - rte->pxlen = block->pxlen; + uint pxlen = (block->pxlen <= IP6_MAX_PREFIX_LENGTH) ? block->pxlen : 255; + net_fill_ip6(&rte->net, ip6_ntoh(block->prefix), pxlen); rte->metric = block->metric; rte->tag = ntohs(block->tag); /* rte->next_hop is deliberately kept unmodified */; @@ -383,8 +384,9 @@ rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packe if (!rip_get_block(p, pos, &b)) return; - /* Special case - zero prefix, infinity metric */ - if (ipa_nonzero(b.prefix) || b.pxlen || (b.metric != p->infinity)) + /* Special case - infinity metric, for RIPng also zero prefix */ + if ((b.metric != p->infinity) || + (rip_is_ng(p) && !net_zero_ip6((net_addr_ip6 *) &b.net))) return; /* We do nothing if TX is already active */ @@ -417,10 +419,8 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa) pkt->unused = 0; pos += rip_pkt_hdrlen(ifa); - FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, z) + FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, struct rip_entry, en) { - struct rip_entry *en = (struct rip_entry *) z; - /* Dummy entries */ if (!en->valid) goto next_entry; @@ -437,28 +437,28 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa) /* Not enough space for current entry */ if (pos > max) { - FIB_ITERATE_PUT(&ifa->tx_fit, z); + FIB_ITERATE_PUT(&ifa->tx_fit); goto break_loop; } struct rip_block rte = { - .prefix = en->n.prefix, - .pxlen = en->n.pxlen, .metric = en->metric, .tag = en->tag }; + net_copy(&rte.net, en->n.addr); + if (en->iface == ifa->iface) rte.next_hop = en->next_hop; if (rip_is_v2(p) && (ifa->cf->version == RIP_V1)) { /* Skipping subnets (i.e. not hosts, classful networks or default route) */ - if (ip4_masklen(ip4_class_mask(ipa_to_ip4(en->n.prefix))) != en->n.pxlen) + if (ip4_masklen(ip4_class_mask(net4_prefix(&rte.net))) != rte.net.pxlen) goto next_entry; rte.tag = 0; - rte.pxlen = 0; + rte.net.pxlen = 0; rte.next_hop = IPA_NONE; } @@ -474,7 +474,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa) goto next_entry; } - // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); + // TRACE(D_PACKETS, " %N -> %I metric %d", &rte.net, rte.next_hop, rte.metric); /* RIPng next hop entry */ if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop)) @@ -490,7 +490,7 @@ rip_send_response(struct rip_proto *p, struct rip_iface *ifa) next_entry: ; } - FIB_ITERATE_END(z); + FIB_ITERATE_END; ifa->tx_active = 0; /* Do not send empty packet */ @@ -519,7 +519,7 @@ break_loop: void rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed) { - DBG("RIP: Opening TX session to %I on %s\n", dst, ifa->iface->name); + DBG("RIP: Opening TX session to %I on %s\n", addr, ifa->iface->name); rip_reset_tx_session(p, ifa); @@ -575,23 +575,25 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack if (!rip_get_block(p, pos, &rte)) continue; - int c = ipa_classify_net(rte.prefix); - if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) - SKIP("invalid prefix"); - if (rip_is_v2(p) && (pkt->version == RIP_V1)) { - if (ifa->cf->check_zero && (rte.tag || rte.pxlen || ipa_nonzero(rte.next_hop))) + if (ifa->cf->check_zero && (rte.tag || rte.net.pxlen || ipa_nonzero(rte.next_hop))) SKIP("RIPv1 reserved field is nonzero"); rte.tag = 0; - rte.pxlen = ip4_masklen(ip4_class_mask(ipa_to_ip4(rte.prefix))); + rte.net.pxlen = ip4_masklen(ip4_class_mask(net4_prefix(&rte.net))); rte.next_hop = IPA_NONE; } - if ((rte.pxlen < 0) || (rte.pxlen > MAX_PREFIX_LENGTH)) + if (rte.net.pxlen == 255) SKIP("invalid prefix length"); + net_normalize(&rte.net); + + int c = net_classify(&rte.net); + if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) + SKIP("invalid prefix"); + if (rte.metric > p->infinity) SKIP("invalid metric"); @@ -602,7 +604,7 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack rte.next_hop = IPA_NONE; } - // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); + // TRACE(D_PACKETS, " %N -> %I metric %d", &rte.net.n, rte.next_hop, rte.metric); rte.metric += ifa->cf->metric; @@ -616,16 +618,16 @@ rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_pack .expires = now + ifa->cf->timeout_time }; - rip_update_rte(p, &rte.prefix, rte.pxlen, &new); + rip_update_rte(p, &rte.net, &new); } else - rip_withdraw_rte(p, &rte.prefix, rte.pxlen, from); + rip_withdraw_rte(p, &rte.net, from); continue; skip: - LOG_RTE("Ignoring route %I/%d received from %I - %s", - rte.prefix, rte.pxlen, from->nbr->addr, err_dsc); + LOG_RTE("Ignoring route %N received from %I - %s", + &rte.net, from->nbr->addr, err_dsc); } } @@ -713,6 +715,7 @@ rip_open_socket(struct rip_iface *ifa) sock *sk = sk_new(p->p.pool); sk->type = SK_UDP; + sk->fam = rip_is_v2(p) ? SK_FAM_IPV4 : SK_FAM_IPV6; sk->sport = ifa->cf->port; sk->dport = ifa->cf->port; sk->iface = ifa->iface; @@ -733,7 +736,8 @@ rip_open_socket(struct rip_iface *ifa) sk->tos = ifa->cf->tx_tos; sk->priority = ifa->cf->tx_priority; sk->ttl = ifa->cf->ttl_security ? 255 : 1; - sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0); + sk->flags = SKF_LADDR_RX | (rip_is_ng(p) ? SKF_V6ONLY : 0) | + ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0); /* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */ diff --git a/proto/rip/rip.c b/proto/rip/rip.c index c85fd69b..22023279 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -92,15 +92,6 @@ static void rip_trigger_update(struct rip_proto *p); * RIP routes */ -static void -rip_init_entry(struct fib_node *fn) -{ - // struct rip_entry *en = (void) *fn; - - const uint offset = OFFSETOF(struct rip_entry, routes); - memset((byte *)fn + offset, 0, sizeof(struct rip_entry) - offset); -} - static struct rip_rte * rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src) { @@ -152,7 +143,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en) if (rt) { /* Update */ - net *n = net_get(p->p.table, en->n.prefix, en->n.pxlen); + net *n = net_get(p->p.main_channel->table, en->n.addr); rta a0 = { .src = p->p.main_source, @@ -221,7 +212,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en) else { /* Withdraw */ - net *n = net_find(p->p.table, en->n.prefix, en->n.pxlen); + net *n = net_find(p->p.main_channel->table, en->n.addr); rte_update(&p->p, n, NULL); } } @@ -229,8 +220,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en) /** * rip_update_rte - enter a route update to RIP routing table * @p: RIP instance - * @prefix: network prefix - * @pxlen: network prefix length + * @addr: network address * @new: a &rip_rte representing the new route * * The function is called by the RIP packet processing code whenever it receives @@ -240,9 +230,9 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en) * rip_withdraw_rte() should be called instead of rip_update_rte(). */ void -rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new) +rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new) { - struct rip_entry *en = fib_get(&p->rtable, prefix, pxlen); + struct rip_entry *en = fib_get(&p->rtable, n); struct rip_rte *rt, **rp; int changed = 0; @@ -282,8 +272,7 @@ rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte * /** * rip_withdraw_rte - enter a route withdraw to RIP routing table * @p: RIP instance - * @prefix: network prefix - * @pxlen: network prefix length + * @addr: network address * @from: a &rip_neighbor propagating the withdraw * * The function is called by the RIP packet processing code whenever it receives @@ -291,9 +280,9 @@ rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte * * removed. Eventually, the change is also propagated by rip_announce_rte(). */ void -rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from) +rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from) { - struct rip_entry *en = fib_find(&p->rtable, prefix, pxlen); + struct rip_entry *en = fib_find(&p->rtable, n); struct rip_rte *rt, **rp; if (!en) @@ -320,7 +309,7 @@ rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_nei * it into our data structures. */ static void -rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, struct rte *new, +rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, struct rte *new, struct rte *old UNUSED, struct ea_list *attrs) { struct rip_proto *p = (struct rip_proto *) P; @@ -335,15 +324,15 @@ rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, if (rt_metric > p->infinity) { - log(L_WARN "%s: Invalid rip_metric value %u for route %I/%d", - p->p.name, rt_metric, net->n.prefix, net->n.pxlen); + log(L_WARN "%s: Invalid rip_metric value %u for route %N", + p->p.name, rt_metric, net->n.addr); rt_metric = p->infinity; } if (rt_tag > 0xffff) { - log(L_WARN "%s: Invalid rip_tag value %u for route %I/%d", - p->p.name, rt_tag, net->n.prefix, net->n.pxlen); + log(L_WARN "%s: Invalid rip_tag value %u for route %N", + p->p.name, rt_tag, net->n.addr); rt_metric = p->infinity; rt_tag = 0; } @@ -355,7 +344,7 @@ rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, * collection. */ - en = fib_get(&p->rtable, &net->n.prefix, net->n.pxlen); + en = fib_get(&p->rtable, net->n.addr); old_metric = en->valid ? en->metric : -1; @@ -369,7 +358,7 @@ rip_rt_notify(struct proto *P, struct rtable *table UNUSED, struct network *net, else { /* Withdraw */ - en = fib_find(&p->rtable, &net->n.prefix, net->n.pxlen); + en = fib_find(&p->rtable, net->n.addr); if (!en || en->valid != RIP_ENTRY_VALID) return; @@ -834,9 +823,8 @@ rip_timer(timer *t) FIB_ITERATE_INIT(&fit, &p->rtable); loop: - FIB_ITERATE_START(&p->rtable, &fit, node) + FIB_ITERATE_START(&p->rtable, &fit, struct rip_entry, en) { - struct rip_entry *en = (struct rip_entry *) node; struct rip_rte *rt, **rp; int changed = 0; @@ -863,7 +851,7 @@ rip_timer(timer *t) * rip_rt_notify() -> p->rtable change, invalidating hidden variables. */ - FIB_ITERATE_PUT_NEXT(&fit, &p->rtable, node); + FIB_ITERATE_PUT_NEXT(&fit, &p->rtable); rip_announce_rte(p, en); goto loop; } @@ -875,7 +863,7 @@ rip_timer(timer *t) if (expires <= now) { - // TRACE(D_EVENTS, "entry is too old: %I/%d", en->n.prefix, en->n.pxlen); + // TRACE(D_EVENTS, "entry is too old: %N", en->n.addr); en->valid = 0; } else @@ -885,12 +873,12 @@ rip_timer(timer *t) /* Remove empty nodes */ if (!en->valid && !en->routes) { - FIB_ITERATE_PUT(&fit, node); - fib_delete(&p->rtable, node); + FIB_ITERATE_PUT(&fit); + fib_delete(&p->rtable, en); goto loop; } } - FIB_ITERATE_END(node); + FIB_ITERATE_END; p->rt_reload = 0; @@ -1039,19 +1027,17 @@ rip_import_control(struct proto *P, struct rte **rt, struct ea_list **attrs, str return 0; } -static int -rip_reload_routes(struct proto *P) +static void +rip_reload_routes(struct channel *C) { - struct rip_proto *p = (struct rip_proto *) P; + struct rip_proto *p = (struct rip_proto *) C->proto; if (p->rt_reload) - return 1; + return; TRACE(D_EVENTS, "Scheduling route reload"); p->rt_reload = 1; rip_kick_timer(p); - - return 1; } static struct ea_list * @@ -1082,12 +1068,23 @@ rip_rte_same(struct rte *new, struct rte *old) } +static void +rip_postconfig(struct proto_config *CF) +{ + // struct rip_config *cf = (void *) CF; + + /* Define default channel */ + if (EMPTY_LIST(CF->channels)) + channel_config_new(NULL, CF->net_type, CF); +} + static struct proto * -rip_init(struct proto_config *cfg) +rip_init(struct proto_config *CF) { - struct proto *P = proto_new(cfg, sizeof(struct rip_proto)); + struct proto *P = proto_new(CF); + + P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); - P->accept_ra_types = RA_OPTIMAL; P->if_notify = rip_if_notify; P->rt_notify = rip_rt_notify; P->neigh_notify = rip_neigh_notify; @@ -1108,10 +1105,12 @@ rip_start(struct proto *P) struct rip_config *cf = (void *) (P->cf); init_list(&p->iface_list); - fib_init(&p->rtable, P->pool, sizeof(struct rip_entry), 0, rip_init_entry); + fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6, + sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL); p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte)); p->timer = tm_new_set(P->pool, rip_timer, p, 0, 0); + p->rip2 = cf->rip2; p->ecmp = cf->ecmp; p->infinity = cf->infinity; p->triggered = 0; @@ -1125,18 +1124,24 @@ rip_start(struct proto *P) } static int -rip_reconfigure(struct proto *P, struct proto_config *c) +rip_reconfigure(struct proto *P, struct proto_config *CF) { struct rip_proto *p = (void *) P; - struct rip_config *new = (void *) c; + struct rip_config *new = (void *) CF; // struct rip_config *old = (void *) (P->cf); + if (new->rip2 != p->rip2) + return 0; + if (new->infinity != p->infinity) return 0; + if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) + return 0; + TRACE(D_EVENTS, "Reconfiguring"); - p->p.cf = c; + p->p.cf = CF; p->ecmp = new->ecmp; rip_reconfigure_ifaces(p, new); @@ -1254,11 +1259,10 @@ rip_dump(struct proto *P) int i; i = 0; - FIB_WALK(&p->rtable, e) + FIB_WALK(&p->rtable, struct rip_entry, en) { - struct rip_entry *en = (struct rip_entry *) e; - debug("RIP: entry #%d: %I/%d via %I dev %s valid %d metric %d age %d s\n", - i++, en->n.prefix, en->n.pxlen, en->next_hop, en->iface->name, + debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %d s\n", + i++, en->n.addr, en->next_hop, en->iface->name, en->valid, en->metric, now - en->changed); } FIB_WALK_END; @@ -1278,7 +1282,10 @@ struct protocol proto_rip = { .template = "rip%d", .attr_class = EAP_RIP, .preference = DEF_PREF_RIP, + .channel_mask = NB_IP, + .proto_size = sizeof(struct rip_proto), .config_size = sizeof(struct rip_config), + .postconfig = rip_postconfig, .init = rip_init, .dump = rip_dump, .start = rip_start, diff --git a/proto/rip/rip.h b/proto/rip/rip.h index f245e612..d1c9933c 100644 --- a/proto/rip/rip.h +++ b/proto/rip/rip.h @@ -27,12 +27,6 @@ #include "lib/timer.h" -#ifdef IPV6 -#define RIP_IS_V2 0 -#else -#define RIP_IS_V2 1 -#endif - #define RIP_V1 1 #define RIP_V2 2 @@ -98,6 +92,7 @@ struct rip_proto slab *rte_slab; /* Slab for internal routes (struct rip_rte) */ timer *timer; /* Main protocol timer */ + u8 rip2; /* RIPv2 (IPv4) or RIPng (IPv6) */ u8 ecmp; /* Maximum number of nexthops in ECMP route, or 0 */ u8 infinity; /* Maximum metric value, representing infinity */ u8 triggered; /* Logical AND of interface want_triggered values */ @@ -149,7 +144,6 @@ struct rip_neighbor struct rip_entry { - struct fib_node n; struct rip_rte *routes; /* List of incoming routes */ u8 valid; /* Entry validity state (RIP_ENTRY_*) */ @@ -160,6 +154,8 @@ struct rip_entry ip_addr next_hop; /* Outgoing route next hop */ bird_clock_t changed; /* Last time when the outgoing route metric changed */ + + struct fib_node n; }; struct rip_rte @@ -189,16 +185,11 @@ struct rip_rte #define EA_RIP_METRIC EA_CODE(EAP_RIP, 0) #define EA_RIP_TAG EA_CODE(EAP_RIP, 1) -#define rip_is_v2(X) RIP_IS_V2 -#define rip_is_ng(X) (!RIP_IS_V2) - -/* static inline int rip_is_v2(struct rip_proto *p) { return p->rip2; } static inline int rip_is_ng(struct rip_proto *p) { return ! p->rip2; } -*/ static inline void rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa) @@ -211,8 +202,8 @@ rip_reset_tx_session(struct rip_proto *p, struct rip_iface *ifa) } /* rip.c */ -void rip_update_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_rte *new); -void rip_withdraw_rte(struct rip_proto *p, ip_addr *prefix, int pxlen, struct rip_neighbor *from); +void rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new); +void rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from); struct rip_neighbor * rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa); void rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n); void rip_show_interfaces(struct proto *P, char *iff); diff --git a/proto/static/config.Y b/proto/static/config.Y index 182721b3..86359f0b 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -38,25 +38,25 @@ CF_GRAMMAR CF_ADDTO(proto, static_proto '}') -static_proto_start: proto_start STATIC { - this_proto = proto_config_new(&proto_static, $1); - static_init_config((struct static_config *) this_proto); - } - ; +static_proto_start: proto_start STATIC +{ + this_proto = proto_config_new(&proto_static, $1); + static_init_config(STATIC_CFG); +}; static_proto: static_proto_start proto_name '{' | static_proto proto_item ';' + | static_proto proto_channel ';' { this_proto->net_type = $2->net_type; } | static_proto CHECK LINK bool ';' { STATIC_CFG->check_link = $4; } | static_proto IGP TABLE rtable ';' { STATIC_CFG->igp_table = $4; } | static_proto stat_route stat_route_opt_list ';' { static_route_finish(); } ; -stat_route0: ROUTE prefix { +stat_route0: ROUTE net_any { this_srt = cfg_allocz(sizeof(struct static_route)); add_tail(&STATIC_CFG->other_routes, &this_srt->n); - this_srt->net = $2.addr; - this_srt->masklen = $2.len; + this_srt->net = $2; this_srt_last_cmd = &(this_srt->cmds); } ; @@ -72,7 +72,7 @@ stat_multipath1: this_srt_nh->use_bfd = -1; /* undefined */ } | stat_multipath1 WEIGHT expr { - this_srt_nh->masklen = $3 - 1; /* really */ + this_srt_nh->weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256"); } | stat_multipath1 BFD bool { diff --git a/proto/static/static.c b/proto/static/static.c index be808593..6239fccb 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -26,7 +26,7 @@ * nodes (of dest RTD_NONE), which stores info about nexthops and are * connected to neighbor entries and neighbor notifications. Dummy * nodes are chained using mp_next, they aren't in other_routes list, - * and abuse some fields (masklen, if_name) for other purposes. + * and abuse if_name field for other purposes. * * The only other thing worth mentioning is that when asked for reconfiguration, * Static not only compares the two configurations, but it also calculates @@ -54,7 +54,7 @@ static inline rtable * p_igp_table(struct proto *p) { struct static_config *cf = (void *) p->cf; - return cf->igp_table ? cf->igp_table->table : p->table; + return cf->igp_table ? cf->igp_table->table : p->main_channel->table; } static void @@ -67,7 +67,7 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa) if (r->installed > 0) return; - DBG("Installing static route %I/%d, rtd=%d\n", r->net, r->masklen, r->dest); + DBG("Installing static route %N, rtd=%d\n", r->net, r->dest); bzero(&a, sizeof(a)); a.src = p->main_source; a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC; @@ -89,7 +89,7 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa) struct mpnh *nh = alloca(sizeof(struct mpnh)); nh->gw = r2->via; nh->iface = r2->neigh->iface; - nh->weight = r2->masklen; /* really */ + nh->weight = r2->weight; nh->next = NULL; *nhp = nh; nhp = &(nh->next); @@ -108,11 +108,11 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa) } if (r->dest == RTDX_RECURSIVE) - rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via); + rta_set_recursive_next_hop(p->main_channel->table, &a, p_igp_table(p), &r->via, &r->via); /* We skip rta_lookup() here */ - n = net_get(p->table, r->net, r->masklen); + n = net_get(p->main_channel->table, r->net); e = rte_get_temp(&a); e->net = n; e->pflags = 0; @@ -135,8 +135,8 @@ static_remove(struct proto *p, struct static_route *r) if (!r->installed) return; - DBG("Removing static route %I/%d via %I\n", r->net, r->masklen, r->via); - n = net_find(p->table, r->net, r->masklen); + DBG("Removing static route %N via %I\n", r->net, r->via); + n = net_find(p->main_channel->table, r->net); rte_update(p, n, NULL); r->installed = 0; } @@ -186,7 +186,7 @@ static_decide(struct static_config *cf, struct static_route *r) static void static_add(struct proto *p, struct static_config *cf, struct static_route *r) { - DBG("static_add(%I/%d,%d)\n", r->net, r->masklen, r->dest); + DBG("static_add(%N,%d)\n", r->net, r->dest); switch (r->dest) { case RTD_ROUTER: @@ -309,6 +309,17 @@ static_shutdown(struct proto *p) r->installed = 0; } + /* Handle failure during channel reconfigure */ + /* FIXME: This should be handled in a better way */ + cf = (void *) p->cf_new; + if (cf) + { + WALK_LIST(r, cf->iface_routes) + r->installed = 0; + WALK_LIST(r, cf->other_routes) + r->installed = 0; + } + return PS_DOWN; } @@ -388,7 +399,7 @@ static_bfd_notify(struct bfd_request *req) static void static_dump_rt(struct static_route *r) { - debug("%-1I/%2d: ", r->net, r->masklen); + debug("%-1N: ", r->net); switch (r->dest) { case RTD_ROUTER: @@ -450,22 +461,40 @@ static_init_config(struct static_config *c) init_list(&c->other_routes); } -static struct proto * -static_init(struct proto_config *c) +static void +static_postconfig(struct proto_config *CF) { - struct proto *p = proto_new(c, sizeof(struct proto)); + struct static_config *cf = (void *) CF; + struct static_route *r; + + if (EMPTY_LIST(CF->channels)) + cf_error("Channel not specified"); - p->neigh_notify = static_neigh_notify; - p->if_notify = static_if_notify; - p->rte_mergable = static_rte_mergable; - return p; + WALK_LIST(r, cf->iface_routes) + if (r->net->type != CF->net_type) + cf_error("Route %N incompatible with channel type", r->net); + + WALK_LIST(r, cf->other_routes) + if (r->net->type != CF->net_type) + cf_error("Route %N incompatible with channel type", r->net); } -static inline int -static_same_net(struct static_route *x, struct static_route *y) + +static struct proto * +static_init(struct proto_config *CF) { - return ipa_equal(x->net, y->net) && (x->masklen == y->masklen); + struct proto *P = proto_new(CF); + // struct static_proto *p = (void *) P; + // struct static_config *cf = (void *) CF; + + P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); + + P->neigh_notify = static_neigh_notify; + P->if_notify = static_if_notify; + P->rte_mergable = static_rte_mergable; + + return P; } static inline int @@ -486,7 +515,10 @@ static_same_dest(struct static_route *x, struct static_route *y) for (x = x->mp_next, y = y->mp_next; x && y; x = x->mp_next, y = y->mp_next) - if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd)) + if (!ipa_equal(x->via, y->via) || + (x->via_if != y->via_if) || + (x->use_bfd != y->use_bfd) || + (x->weight != y->weight)) return 0; return !x && !y; @@ -521,11 +553,11 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n) r->neigh->data = NULL; WALK_LIST(t, n->iface_routes) - if (static_same_net(r, t)) + if (net_equal(r->net, t->net)) goto found; WALK_LIST(t, n->other_routes) - if (static_same_net(r, t)) + if (net_equal(r->net, t->net)) goto found; static_remove(p, r); @@ -546,15 +578,18 @@ cf_igp_table(struct static_config *cf) } static int -static_reconfigure(struct proto *p, struct proto_config *new) +static_reconfigure(struct proto *p, struct proto_config *CF) { struct static_config *o = (void *) p->cf; - struct static_config *n = (void *) new; + struct static_config *n = (void *) CF; struct static_route *r; if (cf_igp_table(o) != cf_igp_table(n)) return 0; + if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF))) + return 0; + /* Delete all obsolete routes and reset neighbor entries */ WALK_LIST(r, o->iface_routes) static_match(p, r, n); @@ -620,20 +655,19 @@ static_copy_config(struct proto_config *dest, struct proto_config *src) struct static_config *d = (struct static_config *) dest; struct static_config *s = (struct static_config *) src; - /* Shallow copy of everything */ - proto_copy_rest(dest, src, sizeof(struct static_config)); - /* Copy route lists */ static_copy_routes(&d->iface_routes, &s->iface_routes); static_copy_routes(&d->other_routes, &s->other_routes); } - struct protocol proto_static = { .name = "Static", .template = "static%d", .preference = DEF_PREF_STATIC, + .channel_mask = NB_ANY, + .proto_size = sizeof(struct proto), .config_size = sizeof(struct static_config), + .postconfig = static_postconfig, .init = static_init, .dump = static_dump, .start = static_start, @@ -646,7 +680,7 @@ struct protocol proto_static = { static void static_show_rt(struct static_route *r) { - byte via[STD_ADDRESS_P_LENGTH + 16]; + byte via[IPA_MAX_TEXT_LENGTH + 25]; switch (r->dest) { @@ -659,13 +693,13 @@ static_show_rt(struct static_route *r) case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break; default: bsprintf(via, "???"); } - cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via, + cli_msg(-1009, "%N %s%s%s", r->net, via, r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)"); struct static_route *r2; if (r->dest == RTD_MULTIPATH) for (r2 = r->mp_next; r2; r2 = r2->mp_next) - cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */ + cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->weight + 1, r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)"); } diff --git a/proto/static/static.h b/proto/static/static.h index 6b047234..51486e83 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -26,8 +26,7 @@ void static_init_config(struct static_config *); struct static_route { node n; struct static_route *chain; /* Next for the same neighbor */ - ip_addr net; /* Network we route */ - int masklen; /* Mask length */ + net_addr *net; /* Network we route */ int dest; /* Destination type (RTD_*) */ ip_addr via; /* Destination router */ struct iface *via_if; /* Destination iface, for link-local vias */ @@ -37,6 +36,7 @@ struct static_route { struct f_inst *cmds; /* List of commands for setting attributes */ int installed; /* Installed in rt table, -1 for reinstall */ int use_bfd; /* Configured to use BFD */ + int weight; /* Multipath next hop weight */ struct bfd_request *bfd_req; /* BFD request, if BFD is used */ }; diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 6ff3b2b7..9f84b3f5 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -72,7 +72,6 @@ #endif - /* Dynamic max number of tables */ int krt_max_tables; @@ -136,7 +135,7 @@ extern int setfib(int fib); /* table_id -> krt_proto map */ #ifdef KRT_SHARED_SOCKET -static struct krt_proto *krt_table_map[KRT_MAX_TABLES]; +static struct krt_proto *krt_table_map[KRT_MAX_TABLES][2]; #endif @@ -207,7 +206,8 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e) msg.rtm.rtm_addrs = RTA_DST; msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1; - if (net->n.pxlen == MAX_PREFIX_LENGTH) + /* XXXX */ + if (net_pxlen(net->n.addr) == net_max_prefix_length[net->n.addr->type]) msg.rtm.rtm_flags |= RTF_HOST; else msg.rtm.rtm_addrs |= RTA_NETMASK; @@ -245,15 +245,28 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e) gw = a->gw; -#ifdef IPV6 /* Embed interface ID to link-local address */ if (ipa_is_link_local(gw)) _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff); -#endif - sockaddr_fill(&dst, BIRD_AF, net->n.prefix, NULL, 0); - sockaddr_fill(&mask, BIRD_AF, ipa_mkmask(net->n.pxlen), NULL, 0); - sockaddr_fill(&gate, BIRD_AF, gw, NULL, 0); + int af = AF_UNSPEC; + + switch (net->n.addr->type) { + case NET_IP4: + af = AF_INET; + break; + case NET_IP6: + af = AF_INET6; + break; + default: + log(L_ERR "KRT: Not sending VPN route %N to kernel", net->n.addr); + return -1; + } + + + sockaddr_fill(&dst, af, net_prefix(net->n.addr), NULL, 0); + sockaddr_fill(&mask, af, net_pxmask(net->n.addr), NULL, 0); + sockaddr_fill(&gate, af, gw, NULL, 0); switch (a->dest) { @@ -281,7 +294,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e) return -1; } - sockaddr_fill(&gate, BIRD_AF, i->addr->ip, NULL, 0); + sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0); msg.rtm.rtm_addrs |= RTA_GATEWAY; } break; @@ -299,7 +312,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e) msg.rtm.rtm_msglen = l; if ((l = write(p->sys.sk->fd, (char *)&msg, l)) < 0) { - log(L_ERR "KRT: Error sending route %I/%d to kernel: %m", net->n.prefix, net->n.pxlen); + log(L_ERR "KRT: Error sending route %N to kernel: %m", net->n.addr); return -1; } @@ -331,10 +344,12 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) { /* p is NULL iff KRT_SHARED_SOCKET and !scan */ + int ipv6; rte *e; net *net; sockaddr dst, gate, mask; ip_addr idst, igate, imask; + net_addr ndst; void *body = (char *)msg->buf; int new = (msg->rtm.rtm_type != RTM_DELETE); char *errmsg = "KRT: Invalid route received"; @@ -352,42 +367,64 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) if (flags & RTF_LLINFO) SKIP("link-local\n"); -#ifdef KRT_SHARED_SOCKET - if (!scan) - { - int table_id = msg->rtm.rtm_tableid; - p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id] : NULL; - - if (!p) - SKIP("unknown table id %d\n", table_id); - } -#endif - GETADDR(&dst, RTA_DST); GETADDR(&gate, RTA_GATEWAY); GETADDR(&mask, RTA_NETMASK); - if (dst.sa.sa_family != BIRD_AF) - SKIP("invalid DST"); + switch (dst.sa.sa_family) { + case AF_INET: + ipv6 = 0; + break; + case AF_INET6: + ipv6 = 1; + break; + default: + SKIP("invalid DST"); + } + + /* We do not test family for RTA_NETMASK, because BSD sends us + some strange values, but interpreting them as IPv4/IPv6 works */ + mask.sa.sa_family = dst.sa.sa_family; idst = ipa_from_sa(&dst); imask = ipa_from_sa(&mask); - igate = (gate.sa.sa_family == BIRD_AF) ? ipa_from_sa(&gate) : IPA_NONE; + igate = (gate.sa.sa_family == dst.sa.sa_family) ? ipa_from_sa(&gate) : IPA_NONE; - /* We do not test family for RTA_NETMASK, because BSD sends us - some strange values, but interpreting them as IPv4/IPv6 works */ +#ifdef KRT_SHARED_SOCKET + if (!scan) + { + int table_id = msg->rtm.rtm_tableid; + p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id][ipv6] : NULL; + if (!p) + SKIP("unknown table id %d\n", table_id); + } +#endif + if ((!ipv6) && (p->p.main_channel->table->addr_type != NET_IP4)) + SKIP("reading only IPv4 routes"); + if ( ipv6 && (p->p.main_channel->table->addr_type != NET_IP6)) + SKIP("reading only IPv6 routes"); int c = ipa_classify_net(idst); if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) SKIP("strange class/scope\n"); - int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_masklen(imask); + int pxlen; + if (ipv6) + pxlen = (flags & RTF_HOST) ? IP6_MAX_PREFIX_LENGTH : ip6_masklen(&ipa_to_ip6(imask)); + else + pxlen = (flags & RTF_HOST) ? IP4_MAX_PREFIX_LENGTH : ip4_masklen(ipa_to_ip4(imask)); + if (pxlen < 0) { log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; } + if (ipv6) + net_fill_ip6(&ndst, ipa_to_ip6(idst), pxlen); + else + net_fill_ip4(&ndst, ipa_to_ip4(idst), pxlen); + if ((flags & RTF_GATEWAY) && ipa_zero(igate)) - { log(L_ERR "%s (%I/%d) - missing gateway", errmsg, idst, pxlen); return; } + { log(L_ERR "%s (%N) - missing gateway", errmsg, ndst); return; } u32 self_mask = RTF_PROTO1; u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY; @@ -426,7 +463,7 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) else src = KRT_SRC_KERNEL; - net = net_get(p->p.table, idst, pxlen); + net = net_get(p->p.main_channel->table, &ndst); rta a = { .src = p->p.main_source, @@ -455,8 +492,8 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) a.iface = if_find_by_index(msg->rtm.rtm_index); if (!a.iface) { - log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u", - net->n.prefix, net->n.pxlen, msg->rtm.rtm_index); + log(L_ERR "KRT: Received route %N with unknown ifindex %u", + net->n.addr, msg->rtm.rtm_index); return; } @@ -466,11 +503,9 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) a.dest = RTD_ROUTER; a.gw = igate; -#ifdef IPV6 /* Clean up embedded interface ID returned in link-local address */ if (ipa_is_link_local(a.gw)) _I0(a.gw) = 0xfe800000; -#endif ng = neigh_find2(&p->p, &a.gw, a.iface, 0); if (!ng || (ng->scope == SCOPE_HOST)) @@ -480,8 +515,8 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST)) return; - log(L_ERR "KRT: Received route %I/%d with strange next-hop %I", - net->n.prefix, net->n.pxlen, a.gw); + log(L_ERR "KRT: Received route %N with strange next-hop %I", + net->n.addr, a.gw); return; } } @@ -643,22 +678,24 @@ krt_read_addr(struct ks_msg *msg, int scan) GETADDR (&null, RTA_AUTHOR); GETADDR (&brd, RTA_BRD); - /* Some other family address */ - if (addr.sa.sa_family != BIRD_AF) - return; + /* Is addr family IP4 or IP6? */ + int ipv6; + switch (addr.sa.sa_family) { + case AF_INET: ipv6 = 0; break; + case AF_INET6: ipv6 = 1; break; + default: return; + } iaddr = ipa_from_sa(&addr); imask = ipa_from_sa(&mask); ibrd = ipa_from_sa(&brd); - - if ((masklen = ipa_masklen(imask)) < 0) + if ((ipv6 ? (masklen = ip6_masklen(&ipa_to_ip6(imask))) : (masklen = ip4_masklen(ipa_to_ip4(imask)))) < 0) { - log(L_ERR "KIF: Invalid masklen %I for %s", imask, iface->name); + log(L_ERR "KIF: Invalid mask %I for %s", imask, iface->name); return; } -#ifdef IPV6 /* Clean up embedded interface ID returned in link-local address */ if (ipa_is_link_local(iaddr)) @@ -666,13 +703,11 @@ krt_read_addr(struct ks_msg *msg, int scan) if (ipa_is_link_local(ibrd)) _I0(ibrd) = 0xfe800000; -#endif bzero(&ifa, sizeof(ifa)); ifa.iface = iface; ifa.ip = iaddr; - ifa.pxlen = masklen; scope = ipa_classify(ifa.ip); if (scope < 0) @@ -682,17 +717,16 @@ krt_read_addr(struct ks_msg *msg, int scan) } ifa.scope = scope & IADDR_SCOPE_MASK; - if (masklen < BITS_PER_IP_ADDRESS) + if (masklen < (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH)) { - ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen)); + net_fill_ipa(&ifa.prefix, ifa.ip, masklen); + net_normalize(&ifa.prefix); - if (masklen == (BITS_PER_IP_ADDRESS - 1)) + if (masklen == ((ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH) - 1)) ifa.opposite = ipa_opposite_m1(ifa.ip); -#ifndef IPV6 - if (masklen == (BITS_PER_IP_ADDRESS - 2)) + if ((!ipv6) && (masklen == IP4_MAX_PREFIX_LENGTH - 2)) ifa.opposite = ipa_opposite_m2(ifa.ip); -#endif if (iface->flags & IF_BROADCAST) ifa.brd = ibrd; @@ -702,12 +736,13 @@ krt_read_addr(struct ks_msg *msg, int scan) } else if (!(iface->flags & IF_MULTIACCESS) && ipa_nonzero(ibrd)) { - ifa.prefix = ifa.opposite = ibrd; + net_fill_ipa(&ifa.prefix, ibrd, (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH)); + ifa.opposite = ibrd; ifa.flags |= IA_PEER; } else { - ifa.prefix = ifa.ip; + net_fill_ipa(&ifa.prefix, ifa.ip, (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH)); ifa.flags |= IA_HOST; } @@ -804,7 +839,7 @@ krt_sysctl_scan(struct proto *p, int cmd, int table_id) mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; - mib[3] = BIRD_AF; + mib[3] = 0; // Set AF to 0 for all available families mib[4] = cmd; mib[5] = 0; mcnt = 6; @@ -941,6 +976,7 @@ krt_sock_open(pool *pool, void *data, int table_id) return sk; } +static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32][2]; #ifdef KRT_SHARED_SOCKET @@ -972,7 +1008,17 @@ krt_sock_close_shared(void) int krt_sys_start(struct krt_proto *p) { - krt_table_map[KRT_CF->sys.table_id] = p; + int id = KRT_CF->sys.table_id; + + if (krt_table_cf[id/32][!!(p->af == AF_INET6)] & (1 << (id%32))) + { + log(L_ERR "%s: Multiple kernel syncers defined for table #%d", p->p.name, id); + return 0; + } + + krt_table_cf[id/32][!!(p->af == AF_INET6)] |= (1 << (id%32)); + + krt_table_map[KRT_CF->sys.table_id][!!(p->af == AF_INET6)] = p; krt_sock_open_shared(); p->sys.sk = krt_sock; @@ -983,10 +1029,12 @@ krt_sys_start(struct krt_proto *p) void krt_sys_shutdown(struct krt_proto *p) { + krt_table_cf[(KRT_CF->sys.table_id)/32][!!(p->af == AF_INET6)] &= ~(1 << ((KRT_CF->sys.table_id)%32)); + krt_sock_close_shared(); p->sys.sk = NULL; - krt_table_map[KRT_CF->sys.table_id] = NULL; + krt_table_map[KRT_CF->sys.table_id][!!(p->af == AF_INET6)] = NULL; krt_buffer_release(&p->p); } @@ -996,6 +1044,16 @@ krt_sys_shutdown(struct krt_proto *p) int krt_sys_start(struct krt_proto *p) { + int id = KRT_CF->sys.table_id; + + if (krt_table_cf[id/32][!!(p->af == AF_INET6)] & (1 << (id%32))) + { + log(L_ERR "%s: Multiple kernel syncers defined for table #%d", p->p.name, id); + return 0; + } + + krt_table_cf[id/32][!!(p->af == AF_INET6)] |= (1 << (id%32)); + p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id); return 1; } @@ -1003,6 +1061,8 @@ krt_sys_start(struct krt_proto *p) void krt_sys_shutdown(struct krt_proto *p) { + krt_table_cf[(KRT_CF->sys.table_id)/32][!!(p->af == AF_INET6)] &= ~(1 << ((KRT_CF->sys.table_id)%32)); + rfree(p->sys.sk); p->sys.sk = NULL; @@ -1014,8 +1074,6 @@ krt_sys_shutdown(struct krt_proto *p) /* KRT configuration callbacks */ -static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32]; - int krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o) { @@ -1029,18 +1087,6 @@ krt_sys_preconfig(struct config *c UNUSED) bzero(&krt_table_cf, sizeof(krt_table_cf)); } -void -krt_sys_postconfig(struct krt_config *x) -{ - u32 *tbl = krt_table_cf; - int id = x->sys.table_id; - - if (tbl[id/32] & (1 << (id%32))) - cf_error("Multiple kernel syncers defined for table #%d", id); - - tbl[id/32] |= (1 << (id%32)); -} - void krt_sys_init_config(struct krt_config *c) { c->sys.table_id = 0; /* Default table */ @@ -1069,7 +1115,7 @@ kif_sys_shutdown(struct kif_proto *p) struct ifa * kif_get_primary_ip(struct iface *i) { -#ifndef IPV6 +#if 0 static int fd = -1; if (fd < 0) diff --git a/sysdep/bsd/krt-sys.h b/sysdep/bsd/krt-sys.h index a63f8caf..870cdf2c 100644 --- a/sysdep/bsd/krt-sys.h +++ b/sysdep/bsd/krt-sys.h @@ -44,8 +44,8 @@ struct krt_state { static inline void krt_sys_io_init(void) { } static inline void krt_sys_init(struct krt_proto *p UNUSED) { } +static inline void krt_sys_postconfig(struct krt_config *x UNUSED) { } -static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { } - +static inline int krt_sys_get_attr(eattr *a UNUSED, byte *buf UNUSED, int buflen UNUSED) { return GA_UNKNOWN; } #endif diff --git a/sysdep/cf/bsd-v6.h b/sysdep/cf/bsd-v6.h deleted file mode 100644 index 745dfba3..00000000 --- a/sysdep/cf/bsd-v6.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Configuration for *BSD based systems (tested on FreeBSD and NetBSD) - * - * (c) 2004 Ondrej Filip <feela@network.cz> - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#define IPV6 - -#define CONFIG_AUTO_ROUTES -#define CONFIG_SELF_CONSCIOUS -#define CONFIG_MULTIPLE_TABLES -#define CONFIG_SINGLE_ROUTE - -#define CONFIG_SKIP_MC_BIND -#define CONFIG_NO_IFACE_BIND - -/* -Link: sysdep/unix -Link: sysdep/bsd - */ diff --git a/sysdep/cf/linux-v6.h b/sysdep/cf/linux-v6.h deleted file mode 100644 index 09f60377..00000000 --- a/sysdep/cf/linux-v6.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Configuration for Linux based systems running IPv6 - * - * (c) 1998--1999 Martin Mares <mj@ucw.cz> - * - * Can be freely distributed and used under the terms of the GNU GPL. - */ - -#define IPV6 - -#define CONFIG_AUTO_ROUTES -#define CONFIG_SELF_CONSCIOUS -#define CONFIG_MULTIPLE_TABLES -#define CONFIG_ALL_TABLES_AT_ONCE - -#define CONFIG_RESTRICTED_PRIVILEGES - -/* -Link: sysdep/linux -Link: sysdep/unix - */ diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 1ffdff07..c398a7f6 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -244,18 +244,16 @@ static struct nl_want_attrs ifla_attr_want[BIRD_IFLA_MAX] = { #define BIRD_IFA_MAX (IFA_ANYCAST+1) -#ifndef IPV6 static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = { [IFA_ADDRESS] = { 1, 1, sizeof(ip4_addr) }, [IFA_LOCAL] = { 1, 1, sizeof(ip4_addr) }, [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) }, }; -#else + static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = { [IFA_ADDRESS] = { 1, 1, sizeof(ip6_addr) }, [IFA_LOCAL] = { 1, 1, sizeof(ip6_addr) }, }; -#endif #define BIRD_RTA_MAX (RTA_TABLE+1) @@ -264,7 +262,6 @@ static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = { [RTA_GATEWAY] = { 1, 1, sizeof(ip4_addr) }, }; -#ifndef IPV6 static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = { [RTA_DST] = { 1, 1, sizeof(ip4_addr) }, [RTA_OIF] = { 1, 1, sizeof(u32) }, @@ -276,7 +273,7 @@ static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = { [RTA_FLOW] = { 1, 1, sizeof(u32) }, [RTA_TABLE] = { 1, 1, sizeof(u32) }, }; -#else + static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = { [RTA_DST] = { 1, 1, sizeof(ip6_addr) }, [RTA_IIF] = { 1, 1, sizeof(u32) }, @@ -288,7 +285,6 @@ static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = { [RTA_FLOW] = { 1, 1, sizeof(u32) }, [RTA_TABLE] = { 1, 1, sizeof(u32) }, }; -#endif static int @@ -304,7 +300,7 @@ nl_parse_attrs(struct rtattr *a, struct nl_want_attrs *want, struct rtattr **k, if (want[a->rta_type].checksize && (RTA_PAYLOAD(a) != want[a->rta_type].size)) { - log(L_ERR "nl_parse_attrs: Malformed message received"); + log(L_ERR "nl_parse_attrs: Malformed attribute received"); return 0; } @@ -329,6 +325,13 @@ static inline ip4_addr rta_get_ip4(struct rtattr *a) static inline ip6_addr rta_get_ip6(struct rtattr *a) { return ip6_ntoh(*(ip6_addr *) RTA_DATA(a)); } +static inline ip_addr rta_get_ipa(struct rtattr *a) +{ + if (RTA_PAYLOAD(a) == sizeof(ip4_addr)) + return ipa_from_ip4(rta_get_ip4(a)); + else + return ipa_from_ip6(rta_get_ip6(a)); +} struct rtattr * nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint dlen) @@ -351,16 +354,32 @@ nl_add_attr(struct nlmsghdr *h, uint bufsize, uint code, const void *data, uint } static inline void -nl_add_attr_u32(struct nlmsghdr *h, unsigned bufsize, int code, u32 data) +nl_add_attr_u32(struct nlmsghdr *h, uint bufsize, int code, u32 data) { nl_add_attr(h, bufsize, code, &data, 4); } static inline void -nl_add_attr_ipa(struct nlmsghdr *h, unsigned bufsize, int code, ip_addr ipa) +nl_add_attr_ip4(struct nlmsghdr *h, uint bufsize, int code, ip4_addr ip4) +{ + ip4 = ip4_hton(ip4); + nl_add_attr(h, bufsize, code, &ip4, sizeof(ip4)); +} + +static inline void +nl_add_attr_ip6(struct nlmsghdr *h, uint bufsize, int code, ip6_addr ip6) { - ipa_hton(ipa); - nl_add_attr(h, bufsize, code, &ipa, sizeof(ipa)); + ip6 = ip6_hton(ip6); + nl_add_attr(h, bufsize, code, &ip6, sizeof(ip6)); +} + +static inline void +nl_add_attr_ipa(struct nlmsghdr *h, uint bufsize, int code, ip_addr ipa) +{ + if (ipa_is_ip4(ipa)) + nl_add_attr_ip4(h, bufsize, code, ipa_to_ip4(ipa)); + else + nl_add_attr_ip6(h, bufsize, code, ipa_to_ip6(ipa)); } static inline struct rtattr * @@ -396,7 +415,7 @@ nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh) } static void -nl_add_multipath(struct nlmsghdr *h, unsigned bufsize, struct mpnh *nh) +nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh) { struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH); @@ -458,12 +477,12 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra) nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a)); if (a[RTA_GATEWAY]) { - memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ip_addr)); - ipa_ntoh(rv->gw); + rv->gw = rta_get_ipa(a[RTA_GATEWAY]); - neighbor *ng = neigh_find2(&p->p, &rv->gw, rv->iface, - (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); - if (!ng || (ng->scope == SCOPE_HOST)) + neighbor *nbr; + nbr = neigh_find2(&p->p, &rv->gw, rv->iface, + (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); + if (!nbr || (nbr->scope == SCOPE_HOST)) return NULL; } else @@ -598,40 +617,20 @@ nl_parse_link(struct nlmsghdr *h, int scan) } static void -nl_parse_addr(struct nlmsghdr *h, int scan) +nl_parse_addr4(struct ifaddrmsg *i, int scan, int new) { - struct ifaddrmsg *i; struct rtattr *a[BIRD_IFA_MAX]; - int new = h->nlmsg_type == RTM_NEWADDR; - struct ifa ifa; struct iface *ifi; int scope; - if (!(i = nl_checkin(h, sizeof(*i)))) + if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a))) return; - switch (i->ifa_family) + if (!a[IFA_LOCAL]) { -#ifndef IPV6 - case AF_INET: - if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want4, a, sizeof(a))) - return; - if (!a[IFA_LOCAL]) - { - log(L_ERR "KIF: Malformed message received (missing IFA_LOCAL)"); - return; - } - break; -#else - case AF_INET6: - if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a))) - return; - break; -#endif - default: - return; + log(L_ERR "KIF: Malformed message received (missing IFA_LOCAL)"); + return; } - if (!a[IFA_ADDRESS]) { log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)"); @@ -645,59 +644,57 @@ nl_parse_addr(struct nlmsghdr *h, int scan) return; } + struct ifa ifa; bzero(&ifa, sizeof(ifa)); ifa.iface = ifi; if (i->ifa_flags & IFA_F_SECONDARY) ifa.flags |= IA_SECONDARY; - /* IFA_LOCAL can be unset for IPv6 interfaces */ - memcpy(&ifa.ip, RTA_DATA(a[IFA_LOCAL] ? : a[IFA_ADDRESS]), sizeof(ifa.ip)); - ipa_ntoh(ifa.ip); - ifa.pxlen = i->ifa_prefixlen; - if (i->ifa_prefixlen > BITS_PER_IP_ADDRESS) + ifa.ip = rta_get_ipa(a[IFA_LOCAL]); + + if (i->ifa_prefixlen > IP4_MAX_PREFIX_LENGTH) { log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen); new = 0; } - if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS) + if (i->ifa_prefixlen == IP4_MAX_PREFIX_LENGTH) { - ip_addr addr; - memcpy(&addr, RTA_DATA(a[IFA_ADDRESS]), sizeof(addr)); - ipa_ntoh(addr); - ifa.prefix = ifa.brd = addr; + ifa.brd = rta_get_ipa(a[IFA_ADDRESS]); + net_fill_ip4(&ifa.prefix, rta_get_ip4(a[IFA_ADDRESS]), i->ifa_prefixlen); /* It is either a host address or a peer address */ - if (ipa_equal(ifa.ip, addr)) + if (ipa_equal(ifa.ip, ifa.brd)) ifa.flags |= IA_HOST; else { ifa.flags |= IA_PEER; - ifa.opposite = addr; + ifa.opposite = ifa.brd; } } else { - ip_addr netmask = ipa_mkmask(ifa.pxlen); - ifa.prefix = ipa_and(ifa.ip, netmask); - ifa.brd = ipa_or(ifa.ip, ipa_not(netmask)); - if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 1) + net_fill_ip4(&ifa.prefix, ipa_to_ip4(ifa.ip), i->ifa_prefixlen); + net_normalize(&ifa.prefix); + + if (i->ifa_prefixlen == IP4_MAX_PREFIX_LENGTH - 1) ifa.opposite = ipa_opposite_m1(ifa.ip); -#ifndef IPV6 - if (i->ifa_prefixlen == BITS_PER_IP_ADDRESS - 2) + if (i->ifa_prefixlen == IP4_MAX_PREFIX_LENGTH - 2) ifa.opposite = ipa_opposite_m2(ifa.ip); if ((ifi->flags & IF_BROADCAST) && a[IFA_BROADCAST]) { - ip_addr xbrd; - memcpy(&xbrd, RTA_DATA(a[IFA_BROADCAST]), sizeof(xbrd)); - ipa_ntoh(xbrd); - if (ipa_equal(xbrd, ifa.prefix) || ipa_equal(xbrd, ifa.brd)) - ifa.brd = xbrd; + ip4_addr xbrd = rta_get_ip4(a[IFA_BROADCAST]); + ip4_addr ybrd = ip4_or(ipa_to_ip4(ifa.ip), ip4_not(ip4_mkmask(i->ifa_prefixlen))); + + if (ip4_equal(xbrd, net4_prefix(&ifa.prefix)) || ip4_equal(xbrd, ybrd)) + ifa.brd = ipa_from_ip4(xbrd); else if (ifi->flags & IF_TMP_DOWN) /* Complain only during the first scan */ - log(L_ERR "KIF: Invalid broadcast address %I for %s", xbrd, ifi->name); + { + log(L_ERR "KIF: Invalid broadcast address %I4 for %s", xbrd, ifi->name); + ifa.brd = ipa_from_ip4(ybrd); + } } -#endif } scope = ipa_classify(ifa.ip); @@ -708,10 +705,93 @@ nl_parse_addr(struct nlmsghdr *h, int scan) } ifa.scope = scope & IADDR_SCOPE_MASK; - DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %I/%d, brd %I, opp %I\n", + DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %N, brd %I, opp %I\n", + ifi->index, ifi->name, + new ? "added" : "removed", + ifa.ip, ifa.flags, ifa.prefix, ifa.brd, ifa.opposite); + + if (new) + ifa_update(&ifa); + else + ifa_delete(&ifa); + + if (!scan) + if_end_partial_update(ifi); +} + +static void +nl_parse_addr6(struct ifaddrmsg *i, int scan, int new) +{ + struct rtattr *a[BIRD_IFA_MAX]; + struct iface *ifi; + int scope; + + if (!nl_parse_attrs(IFA_RTA(i), ifa_attr_want6, a, sizeof(a))) + return; + + if (!a[IFA_ADDRESS]) + { + log(L_ERR "KIF: Malformed message received (missing IFA_ADDRESS)"); + return; + } + + ifi = if_find_by_index(i->ifa_index); + if (!ifi) + { + log(L_ERR "KIF: Received address message for unknown interface %d", i->ifa_index); + return; + } + + struct ifa ifa; + bzero(&ifa, sizeof(ifa)); + ifa.iface = ifi; + if (i->ifa_flags & IFA_F_SECONDARY) + ifa.flags |= IA_SECONDARY; + + /* IFA_LOCAL can be unset for IPv6 interfaces */ + + ifa.ip = rta_get_ipa(a[IFA_LOCAL] ? : a[IFA_ADDRESS]); + + if (i->ifa_prefixlen > IP6_MAX_PREFIX_LENGTH) + { + log(L_ERR "KIF: Invalid prefix length for interface %s: %d", ifi->name, i->ifa_prefixlen); + new = 0; + } + if (i->ifa_prefixlen == IP6_MAX_PREFIX_LENGTH) + { + ifa.brd = rta_get_ipa(a[IFA_ADDRESS]); + net_fill_ip6(&ifa.prefix, rta_get_ip6(a[IFA_ADDRESS]), i->ifa_prefixlen); + + /* It is either a host address or a peer address */ + if (ipa_equal(ifa.ip, ifa.brd)) + ifa.flags |= IA_HOST; + else + { + ifa.flags |= IA_PEER; + ifa.opposite = ifa.brd; + } + } + else + { + net_fill_ip6(&ifa.prefix, ipa_to_ip6(ifa.ip), i->ifa_prefixlen); + net_normalize(&ifa.prefix); + + if (i->ifa_prefixlen == IP6_MAX_PREFIX_LENGTH - 1) + ifa.opposite = ipa_opposite_m1(ifa.ip); + } + + scope = ipa_classify(ifa.ip); + if (scope < 0) + { + log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, ifi->name); + return; + } + ifa.scope = scope & IADDR_SCOPE_MASK; + + DBG("KIF: IF%d(%s): %s IPA %I, flg %x, net %N, brd %I, opp %I\n", ifi->index, ifi->name, new ? "added" : "removed", - ifa.ip, ifa.flags, ifa.prefix, ifa.pxlen, ifa.brd, ifa.opposite); + ifa.ip, ifa.flags, ifa.prefix, ifa.brd, ifa.opposite); if (new) ifa_update(&ifa); @@ -722,6 +802,26 @@ nl_parse_addr(struct nlmsghdr *h, int scan) if_end_partial_update(ifi); } +static void +nl_parse_addr(struct nlmsghdr *h, int scan) +{ + struct ifaddrmsg *i; + + if (!(i = nl_checkin(h, sizeof(*i)))) + return; + + int new = (h->nlmsg_type == RTM_NEWADDR); + + switch (i->ifa_family) + { + case AF_INET: + return nl_parse_addr4(i, scan, new); + + case AF_INET6: + return nl_parse_addr6(i, scan, new); + } +} + void kif_do_scan(struct kif_proto *p UNUSED) { @@ -736,7 +836,14 @@ kif_do_scan(struct kif_proto *p UNUSED) else log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); - nl_request_dump(BIRD_AF, RTM_GETADDR); + nl_request_dump(AF_INET, RTM_GETADDR); + while (h = nl_get_scan()) + if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) + nl_parse_addr(h, 1); + else + log(L_DEBUG "nl_scan_ifaces: Unknown packet received (type=%d)", h->nlmsg_type); + + nl_request_dump(AF_INET6, RTM_GETADDR); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) nl_parse_addr(h, 1); @@ -758,10 +865,10 @@ krt_table_id(struct krt_proto *p) static HASH(struct krt_proto) nl_table_map; -#define RTH_FN(k) u32_hash(k) -#define RTH_EQ(k1,k2) k1 == k2 -#define RTH_KEY(p) krt_table_id(p) -#define RTH_NEXT(p) p->sys.hash_next +#define RTH_KEY(p) p->af, krt_table_id(p) +#define RTH_NEXT(p) p->sys.hash_next +#define RTH_EQ(a1,i1,a2,i2) a1 == a2 && i1 == i2 +#define RTH_FN(a,i) a ^ u32_hash(i) #define RTH_REHASH rth_rehash #define RTH_PARAMS /8, *2, 2, 2, 6, 20 @@ -814,7 +921,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) char buf[128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops)]; } r; - DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new); + DBG("nl_send_route(%N,new=%d)\n", net->n.addr, new); bzero(&r.h, sizeof(r.h)); bzero(&r.r, sizeof(r.r)); @@ -822,11 +929,11 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) r.h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); r.h.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (new ? NLM_F_CREATE|NLM_F_EXCL : 0); - r.r.rtm_family = BIRD_AF; - r.r.rtm_dst_len = net->n.pxlen; + r.r.rtm_family = p->af; + r.r.rtm_dst_len = net_pxlen(net->n.addr); r.r.rtm_protocol = RTPROT_BIRD; r.r.rtm_scope = RT_SCOPE_UNIVERSE; - nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); + nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net_prefix(net->n.addr)); if (krt_table_id(p) < 256) r.r.rtm_table = krt_table_id(p); @@ -931,9 +1038,9 @@ nl_parse_route(struct nlmsghdr *h, int scan) struct rtattr *a[BIRD_RTA_MAX]; int new = h->nlmsg_type == RTM_NEWROUTE; - ip_addr dst = IPA_NONE; + net_addr dst; u32 oif = ~0; - u32 table; + u32 table_id; int src; if (!(i = nl_checkin(h, sizeof(*i)))) @@ -941,54 +1048,54 @@ nl_parse_route(struct nlmsghdr *h, int scan) switch (i->rtm_family) { -#ifndef IPV6 - case AF_INET: - if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want4, a, sizeof(a))) - return; - break; -#else + case AF_INET: + if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want4, a, sizeof(a))) + return; + + if (a[RTA_DST]) + net_fill_ip4(&dst, rta_get_ip4(a[RTA_DST]), i->rtm_dst_len); + else + net_fill_ip4(&dst, IP4_NONE, 0); + break; + case AF_INET6: if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a))) return; - break; -#endif - default: - return; - } + if (a[RTA_DST]) + net_fill_ip6(&dst, rta_get_ip6(a[RTA_DST]), i->rtm_dst_len); + else + net_fill_ip6(&dst, IP6_NONE, 0); + break; - if (a[RTA_DST]) - { - memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); - ipa_ntoh(dst); + default: + return; } if (a[RTA_OIF]) oif = rta_get_u32(a[RTA_OIF]); if (a[RTA_TABLE]) - table = rta_get_u32(a[RTA_TABLE]); + table_id = rta_get_u32(a[RTA_TABLE]); else - table = i->rtm_table; + table_id = i->rtm_table; - p = HASH_FIND(nl_table_map, RTH, table); /* Do we know this table? */ - DBG("KRT: Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, table, i->rtm_protocol, p ? p->p.name : "(none)"); + /* Do we know this table? */ + p = HASH_FIND(nl_table_map, RTH, i->rtm_family, table_id); if (!p) SKIP("unknown table %d\n", table); -#ifdef IPV6 if (a[RTA_IIF]) SKIP("IIF set\n"); -#else + if (i->rtm_tos != 0) /* We don't support TOS */ SKIP("TOS %02x\n", i->rtm_tos); -#endif if (scan && !new) SKIP("RTM_DELROUTE in scan\n"); - int c = ipa_classify_net(dst); + int c = net_classify(&dst); if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) SKIP("strange class/scope\n"); @@ -1020,7 +1127,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) src = KRT_SRC_ALIEN; } - net *net = net_get(p->p.table, dst, i->rtm_dst_len); + net *net = net_get(p->p.main_channel->table, &dst); rta ra = { .src= p->p.main_source, @@ -1039,8 +1146,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) ra.nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]); if (!ra.nexthops) { - log(L_ERR "KRT: Received strange multipath route %I/%d", - net->n.prefix, net->n.pxlen); + log(L_ERR "KRT: Received strange multipath route %N", net->n.addr); return; } @@ -1050,30 +1156,26 @@ nl_parse_route(struct nlmsghdr *h, int scan) ra.iface = if_find_by_index(oif); if (!ra.iface) { - log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u", - net->n.prefix, net->n.pxlen, oif); + log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif); return; } if (a[RTA_GATEWAY]) { - neighbor *ng; ra.dest = RTD_ROUTER; - memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw)); - ipa_ntoh(ra.gw); + ra.gw = rta_get_ipa(a[RTA_GATEWAY]); -#ifdef IPV6 /* Silently skip strange 6to4 routes */ - if (ipa_in_net(ra.gw, IPA_NONE, 96)) + const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96); + if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra.gw, (net_addr *) &sit)) return; -#endif - ng = neigh_find2(&p->p, &ra.gw, ra.iface, - (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); - if (!ng || (ng->scope == SCOPE_HOST)) + neighbor *nbr; + nbr = neigh_find2(&p->p, &ra.gw, ra.iface, + (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0); + if (!nbr || (nbr->scope == SCOPE_HOST)) { - log(L_ERR "KRT: Received route %I/%d with strange next-hop %I", - net->n.prefix, net->n.pxlen, ra.gw); + log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr, ra.gw); return; } } @@ -1111,9 +1213,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) if (a[RTA_PREFSRC]) { - ip_addr ps; - memcpy(&ps, RTA_DATA(a[RTA_PREFSRC]), sizeof(ps)); - ipa_ntoh(ps); + ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]); ea_list *ea = alloca(sizeof(ea_list) + sizeof(eattr)); ea->next = ra.eattrs; @@ -1149,8 +1249,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0) { - log(L_ERR "KRT: Received route %I/%d with strange RTA_METRICS attribute", - net->n.prefix, net->n.pxlen); + log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net->n.addr); return; } @@ -1184,7 +1283,14 @@ krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NUL { struct nlmsghdr *h; - nl_request_dump(BIRD_AF, RTM_GETROUTE); + nl_request_dump(AF_INET, RTM_GETROUTE); + while (h = nl_get_scan()) + if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE) + nl_parse_route(h, 1); + else + log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type); + + nl_request_dump(AF_INET6, RTM_GETROUTE); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE) nl_parse_route(h, 1); @@ -1295,11 +1401,10 @@ nl_open_async(void) bzero(&sa, sizeof(sa)); sa.nl_family = AF_NETLINK; -#ifdef IPV6 - sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE; -#else - sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; -#endif + sa.nl_groups = RTMGRP_LINK | + RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE | + RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE; + if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { log(L_ERR "Unable to bind asynchronous rtnetlink socket: %m"); @@ -1331,7 +1436,7 @@ krt_sys_io_init(void) int krt_sys_start(struct krt_proto *p) { - struct krt_proto *old = HASH_FIND(nl_table_map, RTH, krt_table_id(p)); + struct krt_proto *old = HASH_FIND(nl_table_map, RTH, p->af, krt_table_id(p)); if (old) { diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index c1561cbf..6386940f 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -184,7 +184,7 @@ sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) struct tcp_md5sig md5; memset(&md5, 0, sizeof(md5)); - sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, a, ifa, 0); + sockaddr_fill((sockaddr *) &md5.tcpm_addr, fam_to_af[s->fam], a, ifa, 0); if (passwd) { diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index d6ab8cab..f9a92900 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -92,6 +92,7 @@ timeformat_which: | PROTOCOL { $$ = &new_config->tf_proto; } | BASE { $$ = &new_config->tf_base; } | LOG { $$ = &new_config->tf_log; } + ; timeformat_spec: timeformat_which TEXT { *$1 = (struct timeformat){$2, NULL, 0}; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 5955dbfe..37e26c9b 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -586,6 +586,7 @@ sockaddr_read(sockaddr *sa, int af, ip_addr *a, struct iface **ifa, uint *port) return -1; } +const int fam_to_af[] = { [SK_FAM_IPV4] = AF_INET, [SK_FAM_IPV6] = AF_INET6 }; /* * IPv6 multicast syscalls @@ -1183,7 +1184,7 @@ sk_setup(sock *s) if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) ERR("O_NONBLOCK"); - if (!s->af) + if (!s->fam) return 0; if (ipa_nonzero(s->saddr) && !(s->flags & SKF_BIND)) @@ -1202,7 +1203,7 @@ sk_setup(sock *s) if (s->iface) { #ifdef SO_BINDTODEVICE - struct ifreq ifr; + struct ifreq ifr = {}; strcpy(ifr.ifr_name, s->iface->name); if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) ERR("SO_BINDTODEVICE"); @@ -1284,7 +1285,7 @@ sk_tcp_connected(sock *s) int sa_len = sizeof(sa); if ((getsockname(s->fd, &sa.sa, &sa_len) < 0) || - (sockaddr_read(&sa, s->af, &s->saddr, &s->iface, &s->sport) < 0)) + (sockaddr_read(&sa, fam_to_af[s->fam], &s->saddr, &s->iface, &s->sport) < 0)) log(L_WARN "SOCK: Cannot get local IP address for TCP>"); s->type = SK_TCP; @@ -1309,8 +1310,8 @@ sk_passive_connected(sock *s, int type) sock *t = sk_new(s->pool); t->type = type; + t->fam = s->fam; t->fd = fd; - t->af = s->af; t->ttl = s->ttl; t->tos = s->tos; t->rbsize = s->rbsize; @@ -1319,10 +1320,10 @@ sk_passive_connected(sock *s, int type) if (type == SK_TCP) { if ((getsockname(fd, &loc_sa.sa, &loc_sa_len) < 0) || - (sockaddr_read(&loc_sa, s->af, &t->saddr, &t->iface, &t->sport) < 0)) + (sockaddr_read(&loc_sa, fam_to_af[s->fam], &t->saddr, &t->iface, &t->sport) < 0)) log(L_WARN "SOCK: Cannot get local IP address for TCP<"); - if (sockaddr_read(&rem_sa, s->af, &t->daddr, &t->iface, &t->dport) < 0) + if (sockaddr_read(&rem_sa, fam_to_af[s->fam], &t->daddr, &t->iface, &t->dport) < 0) log(L_WARN "SOCK: Cannot get remote IP address for TCP<"); } @@ -1357,7 +1358,6 @@ sk_passive_connected(sock *s, int type) int sk_open(sock *s) { - int af = BIRD_AF; int fd = -1; int do_bind = 0; int bind_port = 0; @@ -1370,28 +1370,28 @@ sk_open(sock *s) s->ttx = ""; /* Force s->ttx != s->tpos */ /* Fall thru */ case SK_TCP_PASSIVE: - fd = socket(af, SOCK_STREAM, IPPROTO_TCP); + fd = socket(fam_to_af[s->fam], SOCK_STREAM, IPPROTO_TCP); bind_port = s->sport; bind_addr = s->saddr; do_bind = bind_port || ipa_nonzero(bind_addr); break; case SK_UDP: - fd = socket(af, SOCK_DGRAM, IPPROTO_UDP); + fd = socket(fam_to_af[s->fam], SOCK_DGRAM, IPPROTO_UDP); bind_port = s->sport; bind_addr = (s->flags & SKF_BIND) ? s->saddr : IPA_NONE; do_bind = 1; break; case SK_IP: - fd = socket(af, SOCK_RAW, s->dport); + fd = socket(fam_to_af[s->fam], SOCK_RAW, s->dport); bind_port = 0; bind_addr = (s->flags & SKF_BIND) ? s->saddr : IPA_NONE; do_bind = ipa_nonzero(bind_addr); break; case SK_MAGIC: - af = 0; + s->fam = SK_FAM_NONE; fd = s->fd; break; @@ -1402,7 +1402,6 @@ sk_open(sock *s) if (fd < 0) ERR("socket"); - s->af = af; s->fd = fd; if (sk_setup(s) < 0) @@ -1431,7 +1430,7 @@ sk_open(sock *s) if (sk_set_high_port(s) < 0) log(L_WARN "Socket error: %s%#m", s->err); - sockaddr_fill(&sa, af, bind_addr, s->iface, bind_port); + sockaddr_fill(&sa, fam_to_af[s->fam], bind_addr, s->iface, bind_port); if (bind(fd, &sa.sa, SA_LEN(sa)) < 0) ERR2("bind"); } @@ -1443,7 +1442,7 @@ sk_open(sock *s) switch (s->type) { case SK_TCP_ACTIVE: - sockaddr_fill(&sa, af, s->daddr, s->iface, s->dport); + sockaddr_fill(&sa, fam_to_af[s->fam], s->daddr, s->iface, s->dport); if (connect(fd, &sa.sa, SA_LEN(sa)) >= 0) sk_tcp_connected(s); else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && @@ -1548,9 +1547,10 @@ sk_sendmsg(sock *s) { struct iovec iov = {s->tbuf, s->tpos - s->tbuf}; byte cmsg_buf[CMSG_TX_SPACE]; - sockaddr dst; + bzero(cmsg_buf, sizeof(cmsg_buf)); + sockaddr dst = {}; - sockaddr_fill(&dst, s->af, s->daddr, s->iface, s->dport); + sockaddr_fill(&dst, fam_to_af[s->fam], s->daddr, s->iface, s->dport); struct msghdr msg = { .msg_name = &dst.sa, @@ -1603,7 +1603,7 @@ sk_recvmsg(sock *s) // rv = ipv4_skip_header(pbuf, rv); //endif - sockaddr_read(&src, s->af, &s->faddr, NULL, &s->fport); + sockaddr_read(&src, fam_to_af[s->fam], &s->faddr, NULL, &s->fport); sk_process_cmsgs(s, &msg); if (msg.msg_flags & MSG_TRUNC) @@ -1823,7 +1823,7 @@ sk_write(sock *s) case SK_TCP_ACTIVE: { sockaddr sa; - sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); + sockaddr_fill(&sa, fam_to_af[s->fam], s->daddr, s->iface, s->dport); if (connect(s->fd, &sa.sa, SA_LEN(sa)) >= 0 || errno == EISCONN) sk_tcp_connected(s); @@ -1843,6 +1843,12 @@ sk_write(sock *s) } } +int sk_is_ipv4(sock *s) +{ return s->fam == SK_FAM_IPV4; } + +int sk_is_ipv6(sock *s) +{ return s->fam == SK_FAM_IPV6; } + void sk_dump_all(void) { diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index e036081d..1cd73502 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -15,6 +15,16 @@ CF_DEFINES #define THIS_KRT ((struct krt_config *) this_proto) #define THIS_KIF ((struct kif_config *) this_proto) +static void +krt_set_merge_paths(struct channel_config *cc, uint merge, uint limit) +{ + if ((limit <= 0) || (limit > 255)) + cf_error("Merge paths limit must be in range 1-255"); + + cc->ra_mode = merge ? RA_MERGED : RA_OPTIMAL; + cc->merge_limit = limit; +} + CF_DECLS CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, GRACEFUL, RESTART, KRT_SOURCE, KRT_METRIC, MERGE, PATHS) @@ -25,15 +35,18 @@ CF_GRAMMAR CF_ADDTO(proto, kern_proto '}') -kern_proto_start: proto_start KERNEL { this_proto = krt_init_config($1); } +kern_proto_start: proto_start KERNEL { + this_proto = krt_init_config($1); +} ; CF_ADDTO(kern_proto, kern_proto_start proto_name '{') -CF_ADDTO(kern_proto, kern_proto proto_item ';') CF_ADDTO(kern_proto, kern_proto kern_item ';') kern_item: - PERSIST bool { THIS_KRT->persist = $2; } + proto_item + | proto_channel { this_proto->net_type = $1->net_type; } + | PERSIST bool { THIS_KRT->persist = $2; } | SCAN TIME expr { /* Scan time of 0 means scan on startup only */ THIS_KRT->scan_time = $3; @@ -47,8 +60,8 @@ kern_item: } | DEVICE ROUTES bool { THIS_KRT->devroutes = $3; } | GRACEFUL RESTART bool { THIS_KRT->graceful_restart = $3; } - | MERGE PATHS bool { THIS_KRT->merge_paths = $3 ? KRT_DEFAULT_ECMP_LIMIT : 0; } - | MERGE PATHS bool LIMIT expr { THIS_KRT->merge_paths = $3 ? $5 : 0; if (($5 <= 0) || ($5 > 255)) cf_error("Merge paths limit must be in range 1-255"); } + | MERGE PATHS bool { krt_set_merge_paths(this_channel, $3, KRT_DEFAULT_ECMP_LIMIT); } + | MERGE PATHS bool LIMIT expr { krt_set_merge_paths(this_channel, $3, $5); } ; /* Kernel interface protocol */ @@ -59,19 +72,17 @@ kif_proto_start: proto_start DEVICE { this_proto = kif_init_config($1); } ; CF_ADDTO(kif_proto, kif_proto_start proto_name '{') -CF_ADDTO(kif_proto, kif_proto proto_item ';') CF_ADDTO(kif_proto, kif_proto kif_item ';') kif_item: - SCAN TIME expr { + proto_item + | SCAN TIME expr { /* Scan time of 0 means scan on startup only */ THIS_KIF->scan_time = $3; } - | PRIMARY text_or_none prefix_or_ipa { + | PRIMARY opttext net_or_ipa { struct kif_primary_item *kpi = cfg_alloc(sizeof (struct kif_primary_item)); - kpi->pattern = $2; - kpi->prefix = $3.addr; - kpi->pxlen = $3.len; + kpi->addr = $3; add_tail(&THIS_KIF->primary, &kpi->n); } ; diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index f5dee877..b0a96613 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -131,14 +131,14 @@ prefer_addr(struct ifa *a, struct ifa *b) } static inline struct ifa * -find_preferred_ifa(struct iface *i, ip_addr prefix, ip_addr mask) +find_preferred_ifa(struct iface *i, const net_addr *n) { struct ifa *a, *b = NULL; WALK_LIST(a, i->addrs) { if (!(a->flags & IA_SECONDARY) && - ipa_equal(ipa_and(a->ip, mask), prefix) && + (!n || ipa_in_netX(a->ip, n)) && (!b || prefer_addr(a, b))) b = a; } @@ -156,21 +156,21 @@ kif_choose_primary(struct iface *i) WALK_LIST(it, cf->primary) { if (!it->pattern || patmatch(it->pattern, i->name)) - if (a = find_preferred_ifa(i, it->prefix, ipa_mkmask(it->pxlen))) + if (a = find_preferred_ifa(i, &it->addr)) return a; } if (a = kif_get_primary_ip(i)) return a; - return find_preferred_ifa(i, IPA_NONE, IPA_NONE); + return find_preferred_ifa(i, NULL); } static struct proto * kif_init(struct proto_config *c) { - struct kif_proto *p = proto_new(c, sizeof(struct kif_proto)); + struct kif_proto *p = proto_new(c); kif_sys_init(p); return &p->p; @@ -266,9 +266,6 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src) struct kif_config *d = (struct kif_config *) dest; struct kif_config *s = (struct kif_config *) src; - /* Shallow copy of everything (just scan_time currently) */ - proto_copy_rest(dest, src, sizeof(struct kif_config)); - /* Copy primary addr list */ cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item)); @@ -280,7 +277,7 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src) struct protocol proto_unix_iface = { .name = "Device", .template = "device%d", - .preference = DEF_PREF_DIRECT, + .proto_size = sizeof(struct kif_proto), .config_size = sizeof(struct kif_config), .preconfig = kif_preconfig, .init = kif_init, @@ -298,14 +295,14 @@ static inline void krt_trace_in(struct krt_proto *p, rte *e, char *msg) { if (p->p.debug & D_PACKETS) - log(L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg); + log(L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg); } static inline void krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg) { if (p->p.debug & D_PACKETS) - log_rl(f, L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg); + log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg); } /* @@ -348,10 +345,9 @@ krt_learn_announce_update(struct krt_proto *p, rte *e) net *n = e->net; rta *aa = rta_clone(e->attrs); rte *ee = rte_get_temp(aa); - net *nn = net_get(p->p.table, n->n.prefix, n->n.pxlen); + net *nn = net_get(p->p.main_channel->table, n->n.addr); ee->net = nn; ee->pflags = 0; - ee->pref = p->p.preference; ee->u.krt = e->u.krt; rte_update(&p->p, nn, ee); } @@ -359,7 +355,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e) static void krt_learn_announce_delete(struct krt_proto *p, net *n) { - n = net_find(p->p.table, n->n.prefix, n->n.pxlen); + n = net_find(p->p.main_channel->table, n->n.addr); rte_update(&p->p, n, NULL); } @@ -368,7 +364,7 @@ static void krt_learn_scan(struct krt_proto *p, rte *e) { net *n0 = e->net; - net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); + net *n = net_get(&p->krt_table, n0->n.addr); rte *m, **mm; e->attrs = rta_lookup(e->attrs); @@ -412,9 +408,8 @@ krt_learn_prune(struct krt_proto *p) FIB_ITERATE_INIT(&fit, fib); again: - FIB_ITERATE_START(fib, &fit, f) + FIB_ITERATE_START(fib, &fit, net, n) { - net *n = (net *) f; rte *e, **ee, *best, **pbest, *old_best; /* @@ -455,8 +450,8 @@ again: if (old_best) krt_learn_announce_delete(p, n); - FIB_ITERATE_PUT(&fit, f); - fib_delete(fib, f); + FIB_ITERATE_PUT(&fit); + fib_delete(fib, n); goto again; } @@ -473,7 +468,7 @@ again: else DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); } - FIB_ITERATE_END(f); + FIB_ITERATE_END; p->reload = 0; } @@ -482,7 +477,7 @@ static void krt_learn_async(struct krt_proto *p, rte *e, int new) { net *n0 = e->net; - net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); + net *n = net_get(&p->krt_table, n0->n.addr); rte *g, **gg, *best, **bestp, *old_best; e->attrs = rta_lookup(e->attrs); @@ -588,12 +583,11 @@ krt_dump_attrs(rte *e) static void krt_flush_routes(struct krt_proto *p) { - struct rtable *t = p->p.table; + struct rtable *t = p->p.main_channel->table; KRT_TRACE(p, D_EVENTS, "Flushing kernel routes"); - FIB_WALK(&t->fib, f) + FIB_WALK(&t->fib, net, n) { - net *n = (net *) f; rte *e = n->routes; if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED)) { @@ -608,12 +602,12 @@ krt_flush_routes(struct krt_proto *p) static struct rte * krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa) { - struct announce_hook *ah = p->p.main_ahook; - struct filter *filter = ah->out_filter; + struct channel *c = p->p.main_channel; + struct filter *filter = c->out_filter; rte *rt; - if (p->p.accept_ra_types == RA_MERGED) - return rt_export_merged(ah, net, rt_free, tmpa, 1); + if (c->ra_mode == RA_MERGED) + return rt_export_merged(c, net, rt_free, tmpa, 1); rt = net->routes; *rt_free = NULL; @@ -760,13 +754,12 @@ krt_got_route(struct krt_proto *p, rte *e) static void krt_prune(struct krt_proto *p) { - struct rtable *t = p->p.table; + struct rtable *t = p->p.main_channel->table; KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name); - FIB_WALK(&t->fib, f) + FIB_WALK(&t->fib, net, n) { - net *n = (net *) f; - int verdict = f->flags & KRF_VERDICT_MASK; + int verdict = n->n.flags & KRF_VERDICT_MASK; rte *new, *old, *rt_free = NULL; ea_list *tmpa = NULL; @@ -795,7 +788,7 @@ krt_prune(struct krt_proto *p) switch (verdict) { case KRF_CREATE: - if (new && (f->flags & KRF_INSTALLED)) + if (new && (n->n.flags & KRF_INSTALLED)) { krt_trace_in(p, new, "reinstalling"); krt_replace_rte(p, n, new, NULL, tmpa); @@ -822,7 +815,7 @@ krt_prune(struct krt_proto *p) if (rt_free) rte_free(rt_free); lp_flush(krt_filter_lp); - f->flags &= ~KRF_VERDICT_MASK; + n->n.flags &= ~KRF_VERDICT_MASK; } FIB_WALK_END; @@ -1033,7 +1026,7 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * } static void -krt_rt_notify(struct proto *P, struct rtable *table UNUSED, net *net, +krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net, rte *new, rte *old, struct ea_list *eattrs) { struct krt_proto *p = (struct krt_proto *) P; @@ -1067,10 +1060,10 @@ krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED) krt_scan_timer_kick(p); } -static int -krt_reload_routes(struct proto *P) +static void +krt_reload_routes(struct channel *C) { - struct krt_proto *p = (struct krt_proto *) P; + struct krt_proto *p = (void *) C->proto; /* Although we keep learned routes in krt_table, we rather schedule a scan */ @@ -1079,14 +1072,12 @@ krt_reload_routes(struct proto *P) p->reload = 1; krt_scan_timer_kick(p); } - - return 1; } static void -krt_feed_end(struct proto *P) +krt_feed_end(struct channel *C) { - struct krt_proto *p = (struct krt_proto *) P; + struct krt_proto *p = (void *) C->proto; p->ready = 1; krt_scan_timer_kick(p); @@ -1107,14 +1098,42 @@ krt_rte_same(rte *a, rte *b) struct krt_config *krt_cf; +static void +krt_preconfig(struct protocol *P UNUSED, struct config *c) +{ + krt_cf = NULL; + krt_sys_preconfig(c); +} + +static void +krt_postconfig(struct proto_config *CF) +{ + struct krt_config *cf = (void *) CF; + + if (EMPTY_LIST(CF->channels)) + cf_error("Channel not specified"); + +#ifdef CONFIG_ALL_TABLES_AT_ONCE + if (krt_cf->scan_time != cf->scan_time) + cf_error("All kernel syncers must use the same table scan interval"); +#endif + + struct rtable_config *tab = proto_cf_main_channel(CF)->table; + if (tab->krt_attached) + cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name); + tab->krt_attached = CF; + + krt_sys_postconfig(cf); +} + static struct proto * -krt_init(struct proto_config *C) +krt_init(struct proto_config *CF) { - struct krt_proto *p = proto_new(C, sizeof(struct krt_proto)); - struct krt_config *c = (struct krt_config *) C; + struct krt_proto *p = proto_new(CF); + // struct krt_config *cf = (void *) CF; + + p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF)); - p->p.accept_ra_types = c->merge_paths ? RA_MERGED : RA_OPTIMAL; - p->p.merge_limit = c->merge_paths; p->p.import_control = krt_import_control; p->p.rt_notify = krt_rt_notify; p->p.if_notify = krt_if_notify; @@ -1133,6 +1152,13 @@ krt_start(struct proto *P) { struct krt_proto *p = (struct krt_proto *) P; + switch (p->p.net_type) + { + case NET_IP4: p->af = AF_INET; break; + case NET_IP6: p->af = AF_INET6; break; + default: ASSERT(0); + } + add_tail(&krt_proto_list, &p->krt_node); #ifdef KRT_ALLOW_LEARN @@ -1147,8 +1173,8 @@ krt_start(struct proto *P) krt_scan_timer_start(p); - if (P->gr_recovery && KRT_CF->graceful_restart) - P->gr_wait = 1; + if (p->p.gr_recovery && KRT_CF->graceful_restart) + p->p.main_channel->gr_wait = 1; return PS_UP; } @@ -1177,40 +1203,19 @@ krt_shutdown(struct proto *P) } static int -krt_reconfigure(struct proto *p, struct proto_config *new) +krt_reconfigure(struct proto *p, struct proto_config *CF) { - struct krt_config *o = (struct krt_config *) p->cf; - struct krt_config *n = (struct krt_config *) new; + struct krt_config *o = (void *) p->cf; + struct krt_config *n = (void *) CF; + + if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF))) + return 0; if (!krt_sys_reconfigure((struct krt_proto *) p, n, o)) return 0; /* persist, graceful restart need not be the same */ - return o->scan_time == n->scan_time && o->learn == n->learn && - o->devroutes == n->devroutes && o->merge_paths == n->merge_paths; -} - -static void -krt_preconfig(struct protocol *P UNUSED, struct config *c) -{ - krt_cf = NULL; - krt_sys_preconfig(c); -} - -static void -krt_postconfig(struct proto_config *C) -{ - struct krt_config *c = (struct krt_config *) C; - -#ifdef CONFIG_ALL_TABLES_AT_ONCE - if (krt_cf->scan_time != c->scan_time) - cf_error("All kernel syncers must use the same table scan interval"); -#endif - - if (C->table->krt_attached) - cf_error("Kernel syncer (%s) already attached to table %s", C->table->krt_attached->name, C->table->name); - C->table->krt_attached = C; - krt_sys_postconfig(c); + return o->scan_time == n->scan_time && o->learn == n->learn && o->devroutes == n->devroutes; } struct proto_config * @@ -1234,9 +1239,6 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src) struct krt_config *d = (struct krt_config *) dest; struct krt_config *s = (struct krt_config *) src; - /* Shallow copy of everything */ - proto_copy_rest(dest, src, sizeof(struct krt_config)); - /* Fix sysdep parts */ krt_sys_copy_config(d, s); } @@ -1265,6 +1267,8 @@ struct protocol proto_unix_kernel = { .template = "kernel%d", .attr_class = EAP_KRT, .preference = DEF_PREF_INHERITED, + .channel_mask = NB_IP, + .proto_size = sizeof(struct krt_proto), .config_size = sizeof(struct krt_config), .preconfig = krt_preconfig, .postconfig = krt_postconfig, diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index d4a8717e..e968ad57 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -49,7 +49,6 @@ struct krt_config { int learn; /* Learn routes from other sources */ int devroutes; /* Allow export of device routes */ int graceful_restart; /* Regard graceful restart recovery */ - int merge_paths; /* Exported routes are merged for ECMP */ }; struct krt_proto { @@ -65,6 +64,7 @@ struct krt_proto { #endif node krt_node; /* Node in krt_proto_list */ + byte af; /* Kernel address family (AF_*) */ byte ready; /* Initial feed has been finished */ byte initialized; /* First scan has been finished */ byte reload; /* Next scan is doing reload */ @@ -96,8 +96,7 @@ extern struct protocol proto_unix_iface; struct kif_primary_item { node n; byte *pattern; - ip_addr prefix; - int pxlen; + net_addr addr; }; struct kif_config { diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 5d5586a0..f95bd968 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -770,7 +770,7 @@ main(int argc, char **argv) io_init(); rt_init(); if_init(); - roa_init(); +// roa_init(); config_init(); uid_t use_uid = get_uid(use_user); diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 4e0ff841..414b6ca4 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -47,14 +47,6 @@ typedef struct sockaddr_bird { } sockaddr; -#ifdef IPV6 -#define BIRD_AF AF_INET6 -#define ipa_from_sa(x) ipa_from_sa6(x) -#else -#define BIRD_AF AF_INET -#define ipa_from_sa(x) ipa_from_sa4(x) -#endif - /* This is sloppy hack, it should be detected by configure script */ /* Linux systems have it defined so this is definition for BSD systems */ @@ -75,17 +67,21 @@ static inline ip_addr ipa_from_sa4(sockaddr *sa) static inline ip_addr ipa_from_sa6(sockaddr *sa) { return ipa_from_in6(((struct sockaddr_in6 *) sa)->sin6_addr); } +static inline ip_addr ipa_from_sa(sockaddr *sa) +{ + switch (sa->sa.sa_family) + { + case AF_INET: return ipa_from_sa4(sa); + case AF_INET6: return ipa_from_sa6(sa); + default: return IPA_NONE; + } +} + static inline struct in_addr ipa_to_in4(ip_addr a) { return (struct in_addr) { htonl(ipa_to_u32(a)) }; } -#ifdef IPV6 static inline struct in6_addr ipa_to_in6(ip_addr a) { return (struct in6_addr) { .s6_addr32 = { htonl(_I0(a)), htonl(_I1(a)), htonl(_I2(a)), htonl(_I3(a)) } }; } -#else -/* Temporary dummy */ -static inline struct in6_addr ipa_to_in6(ip_addr a) -{ return (struct in6_addr) { .s6_addr32 = { 0, 0, 0, 0 } }; } -#endif void sockaddr_fill(sockaddr *sa, int af, ip_addr a, struct iface *ifa, uint port); int sockaddr_read(sockaddr *sa, int af, ip_addr *a, struct iface **ifa, uint *port); @@ -106,6 +102,7 @@ int sk_open_unix(struct birdsock *s, char *name); void *tracked_fopen(struct pool *, char *name, char *mode); void test_old_bird(char *path); +extern const int fam_to_af[]; /* krt.c bits */ diff --git a/tools/Makefile.in b/tools/Makefile.in index 01bb7a7c..5de323ab 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -72,15 +72,15 @@ tags: install: all $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@ - $(INSTALL_PROGRAM) $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ - $(INSTALL_PROGRAM) $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ + $(INSTALL_PROGRAM) $(exedir)/bird $(DESTDIR)/$(sbindir)/bird + $(INSTALL_PROGRAM) $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl if test -n "@CLIENT@" ; then \ - $(INSTALL_PROGRAM) $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ + $(INSTALL_PROGRAM) $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc ; \ fi if ! test -f $(DESTDIR)/@CONFIG_FILE@ ; then \ $(INSTALL_DATA) $(srcdir)/doc/bird.conf.example $(DESTDIR)/@CONFIG_FILE@ ; \ else \ - echo "Not overwriting old bird@SUFFIX@.conf" ; \ + echo "Not overwriting old bird.conf" ; \ fi install-docs: |