diff options
49 files changed, 874 insertions, 200 deletions
@@ -1,3 +1,15 @@ +Version 2.15 (2024-03-10) + o BGP: Send hold timer + o BGP: New options to specify required BGP capabilities + o BFD: Improvements to 'show bfd sessions' command + o RPKI: New 'local address' configuration option + o Linux: Support for more route attributes, including + TCP congestion control algorithm + o Support for UDP logging + o Static routes can have both nexthop and interface specified + o Completion of command options in BIRD client + o Many bugfixes and improvements + Version 2.14 (2023-10-06) o MPLS subsystem o L3VPN: BGP/MPLS VPNs (RFC 4364) diff --git a/client/birdc.c b/client/birdc.c index f1aea2fe..3e4a64f4 100644 --- a/client/birdc.c +++ b/client/birdc.c @@ -136,6 +136,9 @@ input_help(int arg, int key UNUSED) input_start_list(); cmd_help(rl_line_buffer, rl_point); rl_undo_command(1, 0); + /* <cmd> ? is "internal". Do not submit command in non interactive session */ + if (!interactive) + rl_replace_line("", 0); input_stop_list(); return 0; } diff --git a/client/commands.c b/client/commands.c index fdf2652a..318f0ecd 100644 --- a/client/commands.c +++ b/client/commands.c @@ -20,6 +20,7 @@ struct cmd_info { char *args; char *help; int is_real_cmd; + int is_option; }; static struct cmd_info command_table[] = { @@ -30,7 +31,8 @@ struct cmd_node { struct cmd_node *sibling, *son, **plastson; struct cmd_info *cmd, *help; int len; - signed char prio; + u8 final; + s8 prio; char token[1]; }; @@ -51,12 +53,13 @@ cmd_build_tree(void) struct cmd_node *old, *new; char *c = cmd->command; - old = &cmd_root; + new = &cmd_root; while (*c) { char *d = c; while (*c && !isspace_(*c)) c++; + old = new; for(new=old->son; new; new=new->sibling) if (new->len == c-d && !memcmp(new->token, d, c-d)) break; @@ -72,14 +75,17 @@ cmd_build_tree(void) memcpy(new->token, d, c-d); new->prio = (new->len == 3 && (!memcmp(new->token, "roa", 3) || !memcmp(new->token, "rip", 3))) ? 0 : 1; /* Hack */ } - old = new; while (isspace_(*c)) c++; } + if (cmd->is_real_cmd) - old->cmd = cmd; + new->cmd = cmd; else - old->help = cmd; + new->help = cmd; + + if (cmd->is_option) + old->final = 1; } } @@ -147,7 +153,7 @@ cmd_help(char *cmd, int len) int ambig; n = &cmd_root; - while (cmd < end) + while (cmd < end && !n->final) { if (isspace_(*cmd)) { @@ -168,6 +174,11 @@ cmd_help(char *cmd, int len) n = m; } cmd_display_help(n->cmd, NULL); + + /* Currently no help for options */ + if (n->final) + return; + for (m=n->son; m; m=m->sibling) cmd_display_help(m->help, m->cmd); } @@ -229,7 +240,7 @@ cmd_complete(char *cmd, int len, char *buf, int again) /* Find the context */ n = &cmd_root; - while (cmd < fin && n->son) + while (cmd < fin && n->son && !n->final) { if (isspace_(*cmd)) { @@ -290,7 +301,7 @@ cmd_expand(char *cmd) args = c = cmd; n = &cmd_root; - while (*c) + while (*c && !n->final) { if (isspace_(*c)) { diff --git a/conf/confbase.Y b/conf/confbase.Y index b2471198..ed3c1e6e 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -89,6 +89,7 @@ CF_DECLS struct lsadb_show_data *ld; struct mrt_dump_data *md; struct mpls_show_ranges_cmd *msrc; + struct bfd_show_sessions_cmd *bssc; struct iface *iface; void *g; btime time; diff --git a/conf/gen_commands.m4 b/conf/gen_commands.m4 index 3ed21f13..daf39da6 100644 --- a/conf/gen_commands.m4 +++ b/conf/gen_commands.m4 @@ -13,6 +13,9 @@ m4_divert(-1)') m4_define(CF_CLI_CMD, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1 }, m4_divert(-1)') +m4_define(CF_CLI_OPT, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 1, .is_option = 1 }, +m4_divert(-1)') + m4_define(CF_CLI_HELP, `m4_divert(0){ "m4_translit($1,A-Z,a-z)", "$2", "$3", 0 }, m4_divert(-1)') diff --git a/conf/gen_parser.m4 b/conf/gen_parser.m4 index 80071aef..48c2ca50 100644 --- a/conf/gen_parser.m4 +++ b/conf/gen_parser.m4 @@ -48,6 +48,7 @@ m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]])) m4_divert(3)cli_cmd: CF_cmd CF_cmd: $1 $2 END') m4_define(CF_CLI_CMD, `') +m4_define(CF_CLI_OPT, `') m4_define(CF_CLI_HELP, `') # ENUM declarations are ignored diff --git a/doc/bird.sgml b/doc/bird.sgml index 419a4537..ced927d4 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -543,11 +543,12 @@ ipv6 table include "tablename.conf";; </code> - <tag><label id="opt-log">log "<m/filename/" [<m/limit/ "<m/backup/"] | syslog [name <m/name/] | stderr all|{ <m/list of classes/ }</tag> + <tag><label id="opt-log">log "<m/filename/" [<m/limit/ "<m/backup/"] | syslog [name <m/name/] | stderr | udp <m/address/ [port <m/port/] all|{ <m/list of classes/ }</tag> Set logging of messages having the given class (either <cf/all/ or <cf>{ error|trace [, <m/.../] }</cf> etc.) into selected destination - a file specified as a filename string (with optional log rotation information), - syslog (with optional name argument), or the stderr output. + syslog (with optional name argument), the stderr output, or as a UDP + message (in <rfc id="3164"> syslog format). Classes are: <cf/info/, <cf/warning/, <cf/error/ and <cf/fatal/ for messages about local problems, @@ -1287,8 +1288,9 @@ This argument can be omitted if there exists only a single instance. <tag><label id="cli-show-static">show static [<m/name/]</tag> Show detailed information about static routes. - <tag><label id="cli-show-bfd-sessions">show bfd sessions [<m/name/]</tag> - Show information about BFD sessions. + <tag><label id="cli-show-bfd-sessions">show bfd sessions [<m/name/] [address (<m/IP/|<m/prefix/)] [(interface|dev) "<m/name/"] [ipv4|ipv6] [direct|multihop] [all]</tag> + Show information about BFD sessions. Options could be used to filter + entries, or in the case of the option <cf/all/ to give verbose output. <tag><label id="cli-show-symbols">show symbols [table|filter|function|protocol|template|roa|<m/symbol/]</tag> Show the list of symbols defined in the configuration (names of @@ -3961,15 +3963,17 @@ these attributes: <p>In Linux, there is also a plenty of obscure route attributes mostly focused on tuning TCP performance of local connections. BIRD supports most of these attributes, see Linux or iproute2 documentation for their meaning. Attributes -<cf/krt_lock_*/ and <cf/krt_feature_*/ have type bool, others have type int. -Supported attributes are: +<cf/krt_lock_*/ and <cf/krt_feature_*/ have type bool, <cf/krt_congctl/ has type +string, others have type int. Supported attributes are: <cf/krt_mtu/, <cf/krt_lock_mtu/, <cf/krt_window/, <cf/krt_lock_window/, <cf/krt_rtt/, <cf/krt_lock_rtt/, <cf/krt_rttvar/, <cf/krt_lock_rttvar/, -<cf/krt_sstresh/, <cf/krt_lock_sstresh/, <cf/krt_cwnd/, <cf/krt_lock_cwnd/, +<cf/krt_ssthresh/, <cf/krt_lock_ssthresh/, <cf/krt_cwnd/, <cf/krt_lock_cwnd/, <cf/krt_advmss/, <cf/krt_lock_advmss/, <cf/krt_reordering/, <cf/krt_lock_reordering/, <cf/krt_hoplimit/, <cf/krt_lock_hoplimit/, <cf/krt_rto_min/, <cf/krt_lock_rto_min/, -<cf/krt_initcwnd/, <cf/krt_initrwnd/, <cf/krt_quickack/, +<cf/krt_initcwnd/, <cf/krt_lock_initcwnd/, <cf/krt_initrwnd/, <cf/krt_lock_initrwnd/, +<cf/krt_quickack/, <cf/krt_lock_quickack/, <cf/krt_congctl/, <cf/krt_lock_congctl/, +<cf/krt_fastopen_no_cookie/, <cf/krt_lock_fastopen_no_cookie/, <cf/krt_feature_ecn/, <cf/krt_feature_allfrag/ <sect1>Example @@ -5694,6 +5698,7 @@ protocol rpki [<name>] { roa6 { table <tab>; }; remote <ip> | "<domain>" [port <num>]; port <num>; + local address <ip>; refresh [keep] <num>; retry [keep] <num>; expire [keep] <num>; @@ -5723,6 +5728,9 @@ specify both channels. number is 323 for transport without any encryption and 22 for transport with SSH encryption. + <tag>local address <m/ip/</tag> + Define local address we should use as a source address for the RTR session. + <tag>refresh [keep] <m/num/</tag> Time period in seconds. Tells how long to wait before next attempting to poll the cache using a Serial Query or a Reset Query packet. Must be lower than 86400 seconds (one @@ -5937,6 +5945,12 @@ options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/. that BFD protocol also has to be configured, see <ref id="bfd" name="BFD"> section for details. Default value is no. + <tag><label id="static-route-dev">dev <m/text/</tag> + The outgoing interface associated with the nexthop. Useful for + link-local nexthop addresses or when multiple interfaces use the same + network prefix. By default, the outgoing interface is resolved from the + nexthop address. + <tag><label id="static-route-mpls">mpls <m/num/[/<m/num/[/<m/num/[...]]]</tag> MPLS labels that should be pushed to packets forwarded by the route. The option could be used for both IP routes (on MPLS ingress routers) @@ -6117,7 +6131,8 @@ protocol static { via 198.51.100.20 bfd # BFD-controlled next hop via 192.0.2.1; route 203.0.113.0/24 blackhole; # Sink route - route 10.2.0.0/24 via "arc0"; # Secondary network + route 10.2.0.0/24 via "arc0"; # Direct route + route 10.2.2.0/24 via 192.0.2.1 dev "eth0" onlink; # Route with both nexthop and iface route 192.168.10.0/24 via 198.51.100.100 { ospf_metric1 = 20; # Set extended attribute }; @@ -6136,7 +6151,8 @@ protocol static { route 2001:db8:10::/48 via 2001:db8:1::1; # Route with global nexthop route 2001:db8:20::/48 via fe80::10%eth0; # Route with link-local nexthop route 2001:db8:30::/48 via fe80::20%'eth1.60'; # Iface with non-alphanumeric characters - route 2001:db8:40::/48 via "eth2"; # Direct route to eth2 + route 2001:db8:40::/48 via fe80::30 dev "eth1"; # Another link-local nexthop + route 2001:db8:50::/48 via "eth2"; # Direct route to eth2 route 2001:db8::/32 unreachable; # Unreachable route route ::/0 via 2001:db8:1::1 bfd; # BFD-controlled default route } diff --git a/filter/config.Y b/filter/config.Y index f3ed2dc5..09d4fd89 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -362,7 +362,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, IF, THEN, ELSE, CASE, FOR, DO, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, - FROM, GW, NET, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, ONLINK, + FROM, GW, NET, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, GW_MPLS_STACK, ONLINK, PREFERENCE, ROA_CHECK, DEFINED, @@ -879,6 +879,7 @@ static_attr: | WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); } | PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); } | GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); } + | GW_MPLS_STACK { $$ = f_new_static_attr(T_CLIST, SA_GW_MPLS_STACK, 0); } | ONLINK { $$ = f_new_static_attr(T_BOOL, SA_ONLINK, 0); } ; @@ -938,7 +939,7 @@ term: | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_ECLIST)); } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_LCLIST)); } -| PREPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("prepend", $3->type, $3, $5); } + | PREPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("prepend", $3->type, $3, $5); } | ADD '(' term ',' term ')' { $$ = f_dispatch_method_x("add", $3->type, $3, $5); } | DELETE '(' term ',' term ')' { $$ = f_dispatch_method_x("delete", $3->type, $3, $5); } | FILTER '(' term ',' term ')' { $$ = f_dispatch_method_x("filter", $3->type, $3, $5); } diff --git a/filter/data.h b/filter/data.h index 21a78bf6..df8d6a8f 100644 --- a/filter/data.h +++ b/filter/data.h @@ -120,6 +120,7 @@ enum f_sa_code { SA_WEIGHT, SA_PREF, SA_GW_MPLS, + SA_GW_MPLS_STACK, SA_ONLINK, } PACKED; diff --git a/filter/f-inst.c b/filter/f-inst.c index 9cc46aa0..6593a381 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -692,6 +692,16 @@ case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break; case SA_PREF: RESULT(sa.f_type, i, rta->pref); break; case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break; + case SA_GW_MPLS_STACK: + { + uint len = rta->nh.labels * sizeof(u32); + struct adata *list = falloc(sizeof(struct adata) + len); + list->length = len; + memcpy(list->data, rta->nh.label, len); + RESULT(sa.f_type, ad, list); + break; + } + case SA_ONLINK: RESULT(sa.f_type, i, rta->nh.flags & RNF_ONLINK ? 1 : 0); break; default: @@ -779,6 +789,36 @@ } else rta->nh.labels = 0; + + rta->nh.labels_orig = rta->hostentry ? rta->nh.labels : 0; + } + break; + + case SA_GW_MPLS_STACK: + { + int len = int_set_get_size(v1.val.ad); + u32 *l = int_set_get_data(v1.val.ad); + + if (len > MPLS_MAX_LABEL_STACK) + runtime("Too many MPLS labels in stack (%d)", len); + + int i; + for (i = 0; i < len; i++) + { + u32 label = l[i]; + + if (label >= 0x100000) + runtime("Invalid MPLS label (%u)", label); + + /* Ignore rest of label stack if implicit-NULL label (3) is set */ + if (label == MPLS_NULL) + break; + + rta->nh.label[i] = label; + } + + rta->nh.labels = i; + rta->nh.labels_orig = rta->hostentry ? i : 0; } break; @@ -861,6 +901,9 @@ case EAF_TYPE_LC_SET: RESULT_(T_LCLIST, ad, e->u.ptr); break; + case EAF_TYPE_STRING: + RESULT_(T_STRING, s, (const char *) e->u.ptr->data); + break; default: bug("Unknown dynamic attribute type"); } @@ -914,6 +957,12 @@ l->attrs[0].u.ptr = v1.val.ad; break; + case EAF_TYPE_STRING:; + struct adata *d = lp_alloc_adata(fs->pool, strlen(v1.val.s) + 1); + memcpy(d->data, v1.val.s, d->length); + l->attrs[0].u.ptr = d; + break; + case EAF_TYPE_BITFIELD: { /* First, we have to find the old value */ @@ -1253,6 +1302,14 @@ RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]); } + /* Hack for gw_mpls_list */ + INST(FI_CLIST_ADD_INT, 2, 1) { + ARG(1, T_CLIST); + ARG(2, T_INT); + METHOD_CONSTRUCTOR("add"); + RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]); + } + INST(FI_CLIST_ADD_IP, 2, 1) { ARG(1, T_CLIST); ARG(2, T_IP); @@ -1335,6 +1392,14 @@ RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]); } + /* Hack for gw_mpls_list */ + INST(FI_CLIST_DELETE_INT, 2, 1) { + ARG(1, T_CLIST); + ARG(2, T_INT); + METHOD_CONSTRUCTOR("delete"); + RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]); + } + INST(FI_CLIST_DELETE_IP, 2, 1) { ARG(1, T_CLIST); ARG(2, T_IP); diff --git a/filter/f-util.c b/filter/f-util.c index a47a8747..6fbdacba 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -294,6 +294,9 @@ ca_lookup(pool *p, const char *name, int f_type) case T_LCLIST: ea_type = EAF_TYPE_LC_SET; break; + case T_STRING: + ea_type = EAF_TYPE_STRING; + break; case T_BYTESTRING: ea_type = EAF_TYPE_OPAQUE; break; diff --git a/filter/test.conf b/filter/test.conf index f9289189..02857eac 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -496,6 +496,9 @@ prefix px; { px = 1.2.0.0/18; bt_assert(format(px) = "1.2.0.0/18"); + bt_assert(px.ip = 1.2.0.0); + bt_assert(px.len = 18); + bt_assert(192.168.0.0/16 ~ 192.168.0.0/16); bt_assert(192.168.0.0/17 ~ 192.168.0.0/16); bt_assert(192.168.254.0/24 ~ 192.168.0.0/16); @@ -633,9 +636,11 @@ bt_test_suite(t_prefix_set, "Testing prefix sets"); function t_prefix6() { - prefix px; - px = 1020::/18; + prefix px = 1020::/18; bt_assert(format(px) = "1020::/18"); + bt_assert(px.ip = 1020::); + bt_assert(px.len = 18); + bt_assert(1020:3040:5060:: ~ 1020:3040:5000::/40); bt_assert(1020:3040::/32 ~ 1020:3040::/32); bt_assert(1020:3040::/33 ~ 1020:3040::/32); @@ -717,33 +722,6 @@ bt_test_suite(t_prefix6_set, "Testing prefix IPv6 sets"); -function t_flowspec() -prefix p; -{ - p = flow4 { dst 10.0.0.0/8; }; - bt_assert(p !~ [ 10.0.0.0/8 ] ); - - bt_assert(format(flow4 { dst 10.0.0.0/8; proto = 23; }) = "flow4 { dst 10.0.0.0/8; proto 23; }"); - bt_assert(format(flow6 { dst ::1/128; src ::2/127; }) = "flow6 { dst ::1/128; src ::2/127; }"); - bt_assert(format(flow6 { next header false 42; }) = "flow6 { next header false 42; }"); - bt_assert(format(flow6 { port 80; }) = "flow6 { port 80; }"); - bt_assert(format(flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }) = "flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }"); - bt_assert(format(flow6 { sport 0..0x400; }) = "flow6 { sport 0..1024; }"); - bt_assert(format(flow6 { icmp type 80; }) = "flow6 { icmp type 80; }"); - bt_assert(format(flow6 { icmp code 90; }) = "flow6 { icmp code 90; }"); - bt_assert(format(flow6 { tcp flags 0x03/0x0f; }) = "flow6 { tcp flags 0x3/0x3 && 0x0/0xc; }"); - bt_assert(format(flow6 { length 0..65535; }) = "flow6 { length 0..65535; }"); - bt_assert(format(flow6 { dscp = 63; }) = "flow6 { dscp 63; }"); - bt_assert(format(flow6 { fragment is_fragment || !first_fragment; }) = "flow6 { fragment is_fragment || !first_fragment; }"); - bt_assert(format(flow6 { label 1000..2000; }) = "flow6 { label 1000..2000; }"); - bt_assert(format(flow6 { }) = "flow6 { }"); -} - -bt_test_suite(t_flowspec, "Testing flowspec routes"); - - - - /* * Testing Paths * ------------- @@ -1722,6 +1700,137 @@ bt_test_suite(t_rd_set, "Testing sets of route distinguishers"); /* + * Testing VPN nets + * ---------------- + */ + +function t_net_vpn() +{ + prefix p; + + p = 100:200 10.0.1.0/24; + bt_assert(format(p) = "100:200 10.0.1.0/24"); + bt_assert(p.type = NET_VPN4); + bt_assert(p.len = 24); + bt_assert(p.ip = 10.0.1.0); + bt_assert(p.rd = 100:200); + + p = 1012:2024 fe80:386c::/32; + bt_assert(format(p) = "1012:2024 fe80:386c::/32"); + bt_assert(p.type = NET_VPN6); + bt_assert(p.len = 32); + bt_assert(p.ip = fe80:386c::); + bt_assert(p.rd = 1012:2024); +} + +bt_test_suite(t_net_vpn, "Testing VPN nets"); + + + + +/* + * Testing ROA nets + * ---------------- + */ + + +function t_net_roa() +{ + prefix p; + + p = 12.13.0.0/16 max 24 as 1234; + bt_assert(format(p) = "12.13.0.0/16-24 AS1234"); + bt_assert(p.type = NET_ROA4); + bt_assert(p.ip = 12.13.0.0); + bt_assert(p.len = 16); + bt_assert(p.maxlen = 24); + bt_assert(p.asn = 1234); + + p = 1000::/8 max 32 as 1234; + bt_assert(format(p) = "1000::/8-32 AS1234"); + bt_assert(p.type = NET_ROA6); + bt_assert(p.ip = 1000::); + bt_assert(p.len = 8); + bt_assert(p.maxlen = 32); + bt_assert(p.asn = 1234); +} + +bt_test_suite(t_net_roa, "Testing ROA nets"); + + + + +/* + * Testing Flowspec nets + * --------------------- + */ + +function t_net_flowspec() +{ + prefix p; + + p = flow4 { dst 10.0.0.0/8; }; + bt_assert(p.type = NET_FLOW4); + bt_assert(p.ip = 10.0.0.0); + bt_assert(p.len = 8); + bt_assert(p.src = 0.0.0.0/0); + bt_assert(p.dst = 10.0.0.0/8); + + bt_assert(p !~ [ 10.0.0.0/8 ] ); + bt_assert(p.dst ~ [ 10.0.0.0/8 ] ); + + p = flow6 { dst ::1/128; }; + bt_assert(p.type = NET_FLOW6); + bt_assert(p.ip = ::1); + bt_assert(p.len = 128); + bt_assert(p.src = ::/0); + bt_assert(p.dst = ::1/128); + + bt_assert(format(flow4 { dst 10.0.0.0/8; proto = 23; }) = "flow4 { dst 10.0.0.0/8; proto 23; }"); + bt_assert(format(flow6 { dst ::1/128; src ::2/127; }) = "flow6 { dst ::1/128; src ::2/127; }"); + bt_assert(format(flow6 { next header false 42; }) = "flow6 { next header false 42; }"); + bt_assert(format(flow6 { port 80; }) = "flow6 { port 80; }"); + bt_assert(format(flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }) = "flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }"); + bt_assert(format(flow6 { sport 0..0x400; }) = "flow6 { sport 0..1024; }"); + bt_assert(format(flow6 { icmp type 80; }) = "flow6 { icmp type 80; }"); + bt_assert(format(flow6 { icmp code 90; }) = "flow6 { icmp code 90; }"); + bt_assert(format(flow6 { tcp flags 0x03/0x0f; }) = "flow6 { tcp flags 0x3/0x3 && 0x0/0xc; }"); + bt_assert(format(flow6 { length 0..65535; }) = "flow6 { length 0..65535; }"); + bt_assert(format(flow6 { dscp = 63; }) = "flow6 { dscp 63; }"); + bt_assert(format(flow6 { fragment is_fragment || !first_fragment; }) = "flow6 { fragment is_fragment || !first_fragment; }"); + bt_assert(format(flow6 { label 1000..2000; }) = "flow6 { label 1000..2000; }"); + bt_assert(format(flow6 { }) = "flow6 { }"); + +} + +bt_test_suite(t_net_flowspec, "Testing flowspec networks"); + + + + +/* + * Testing IPv6 SADR nets + * ---------------------- + */ + +function t_net_sadr() +{ + prefix p; + p = fe80:386c::/32 from 2001:db8:1:13::/64; + bt_assert(format(p) = "fe80:386c::/32 from 2001:db8:1:13::/64"); + bt_assert(p.type = NET_IP6_SADR); + bt_assert(p.ip = fe80:386c::); + bt_assert(p.len = 32); + bt_assert(p.src = 2001:db8:1:13::/64); + bt_assert(p.dst = fe80:386c::/32); +} + +bt_test_suite(t_net_sadr, "Testing IPv6 SADR nets"); + + + + +/* * Testing defined() function * -------------------------- */ @@ -2109,16 +2218,6 @@ prefix pfx; bt_assert(2001:0db8:85a3:8a2e::/64 ~ ::/0); bt_assert(10.130.130.0/24 !~ ::/0); bt_assert(2001:0db8:85a3:8a2e::/64 !~ 0.0.0.0/0); - - pfx = 12.13.0.0/16 max 24 as 1234; - bt_assert(pfx.len = 16); - bt_assert(pfx.maxlen = 24); - bt_assert(pfx.asn = 1234); - - pfx = 1000::/8 max 32 as 1234; - bt_assert(pfx.len = 8); - bt_assert(pfx.maxlen = 32); - bt_assert(pfx.asn = 1234); } bt_test_suite(t_roa_check, "Testing ROA"); @@ -176,6 +176,10 @@ static inline ip6_addr ip6_not(ip6_addr a) #define ipa_xor(x,y) ip6_xor(x,y) #define ipa_not(x) ip6_not(x) +/* Compare addresses when zero address works like a wildcard */ +static inline int ipa_equal_wildcard(ip_addr x, ip_addr y) +{ return ipa_zero(x) || ipa_zero(y) || ipa_equal(x, y); } + /* * A zero address is either a token for invalid/unused, or the prefix of default @@ -388,6 +392,7 @@ static inline ip6_addr ip6_ntoh(ip6_addr a) #define MPLS_MAX_LABEL 0x100000 #define MPLS_MAX_LABEL_STACK 8 +#define MPLS_MAX_LABEL_STRING MPLS_MAX_LABEL_STACK*12 + 5 typedef struct mpls_label_stack { uint len; u32 stack[MPLS_MAX_LABEL_STACK]; diff --git a/lib/socket.h b/lib/socket.h index 0b6ac589..231c10d8 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -124,6 +124,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou #define SKF_BIND 0x10 /* Bind datagram socket to given source address */ #define SKF_HIGH_PORT 0x20 /* Choose port from high range if possible */ #define SKF_FREEBIND 0x40 /* Allow socket to bind to a nonlocal address */ +#define SKF_CONNECT 0x80 /* Connect datagram socket to given dst address/port */ #define SKF_THREAD 0x100 /* Socked used in thread, Do not add to main loop */ #define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */ diff --git a/misc/bird.spec b/misc/bird.spec index 93c80193..14118ef2 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 2.14 +Version: 2.15 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/nest/config.Y b/nest/config.Y index 63888a04..20186ece 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -650,6 +650,23 @@ CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>|in <prefix>] [table <t>] [(import|export) table <p>.<c>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]]) { rt_show($3); } ; +CF_CLI_OPT(SHOW ROUTE FOR, <ip>|<prefix>) +CF_CLI_OPT(SHOW ROUTE IN, <prefix>) +CF_CLI_OPT(SHOW ROUTE TABLE, <t>) +CF_CLI_OPT(SHOW ROUTE FILTER, <f>) +CF_CLI_OPT(SHOW ROUTE WHERE, <cond>) +CF_CLI_OPT(SHOW ROUTE ALL) +CF_CLI_OPT(SHOW ROUTE PRIMARY) +CF_CLI_OPT(SHOW ROUTE FILTERED) +CF_CLI_OPT(SHOW ROUTE IMPORT, <p>[.<c>]) +CF_CLI_OPT(SHOW ROUTE EXPORT, <p>[.<c>]) +CF_CLI_OPT(SHOW ROUTE EXPORTED, <p>[.<c>]) +CF_CLI_OPT(SHOW ROUTE PREEXPORT, <p>[.<c>]) +CF_CLI_OPT(SHOW ROUTE NOEXPORT, <p>[.<c>]) +CF_CLI_OPT(SHOW ROUTE PROTOCOL, <p>) +CF_CLI_OPT(SHOW ROUTE STATS) +CF_CLI_OPT(SHOW ROUTE COUNT) + r_args: /* empty */ { $$ = cfg_allocz(sizeof(struct rt_show_data)); @@ -841,13 +858,19 @@ CF_CLI_HELP(SHOW SYMBOLS, ..., [[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); } ; +CF_CLI_OPT(SHOW SYMBOLS TABLE) +CF_CLI_OPT(SHOW SYMBOLS FILTER) +CF_CLI_OPT(SHOW SYMBOLS FUNCTION) +CF_CLI_OPT(SHOW SYMBOLS PROTOCOL) +CF_CLI_OPT(SHOW SYMBOLS TEMPLATE) + sym_args: /* empty */ { $$ = cfg_allocz(sizeof(struct sym_show_data)); } | sym_args TABLE { $$ = $1; $$->type = SYM_TABLE; } - | sym_args FUNCTION { $$ = $1; $$->type = SYM_FUNCTION; } | sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; } + | sym_args FUNCTION { $$ = $1; $$->type = SYM_FUNCTION; } | sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; } | sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; } | sym_args CF_SYM_KNOWN { $$ = $1; $$->sym = $2; } @@ -914,6 +937,15 @@ CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]]) CF_CLI(DEBUG, debug_args, (<protocol> | <channel> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]]) { /* Done in debug_args */ }; +CF_CLI_OPT(DEBUG ALL) +CF_CLI_OPT(DEBUG OFF) +CF_CLI_OPT(DEBUG STATES) +CF_CLI_OPT(DEBUG ROUTES) +CF_CLI_OPT(DEBUG FILTERS) +CF_CLI_OPT(DEBUG INTERFACES) +CF_CLI_OPT(DEBUG EVENTS) +CF_CLI_OPT(DEBUG PACKETS) + debug_args: proto_patt debug_mask { proto_apply_cmd($1, proto_cmd_debug, 1, $2); } | channel_arg debug_mask { channel_cmd_debug($1, $2); } diff --git a/nest/locks.c b/nest/locks.c index 812a6534..637f2c15 100644 --- a/nest/locks.c +++ b/nest/locks.c @@ -48,7 +48,8 @@ olock_same(struct object_lock *x, struct object_lock *y) x->vrf == y->vrf && x->port == y->port && x->inst == y->inst && - ipa_equal(x->addr, y->addr); + ipa_equal(x->addr, y->addr) && + ipa_equal_wildcard(x->addr_local, y->addr_local); } static void @@ -91,7 +92,7 @@ olock_dump(resource *r) struct object_lock *l = (struct object_lock *) r; static char *olock_states[] = { "free", "locked", "waiting", "event" }; - debug("(%d:%s:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->port, l->inst, olock_states[l->state]); + debug("(%d:%s:%I:%I:%d:%d) [%s]\n", l->type, (l->iface ? l->iface->name : "?"), l->addr, l->addr_local, l->port, l->inst, olock_states[l->state]); if (!EMPTY_LIST(l->waiters)) debug(" [wanted]\n"); } diff --git a/nest/locks.h b/nest/locks.h index 37026c68..1686e6ec 100644 --- a/nest/locks.h +++ b/nest/locks.h @@ -25,7 +25,8 @@ struct object_lock { resource r; - ip_addr addr; /* Identification of a object: IP address */ + ip_addr addr; /* Identification of a object: IP address (strict compare) */ + ip_addr addr_local; /* ... another IP address (allow zero IP wildcard) */ uint type; /* ... object type (OBJLOCK_xxx) */ uint port; /* ... port number */ uint inst; /* ... instance ID */ diff --git a/nest/route.h b/nest/route.h index 3e1340fa..12e85006 100644 --- a/nest/route.h +++ b/nest/route.h @@ -542,6 +542,7 @@ const char *ea_custom_name(uint ea); #define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */ #define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */ #define EA_BIT_GET(ea) ((ea) >> 24) +#define EA_DATA_ALIGN 4 /* Alignment of adata in attribute cache */ #define EAF_TYPE_MASK 0x1f /* Mask with this to get type */ #define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */ @@ -553,7 +554,8 @@ const char *ea_custom_name(uint ea); #define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */ #define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */ #define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */ -#define EAF_TYPE_IFACE 0x16 /* Interface pointer stored in adata */ +#define EAF_TYPE_IFACE 0x14 /* Interface pointer stored in adata */ +#define EAF_TYPE_STRING 0x16 /* Text string */ #define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */ #define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */ @@ -693,6 +695,9 @@ static inline size_t nexthop_size(const struct nexthop *nh) int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops */ static inline int nexthop_same(struct nexthop *x, struct nexthop *y) { return (x == y) || nexthop__same(x, y); } +int nexthop_equal_(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops, ignore labels_orig */ +static inline int nexthop_equal(struct nexthop *x, struct nexthop *y) +{ return (x == y) || nexthop_equal_(x, y); } struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp); struct nexthop *nexthop_sort(struct nexthop *x); static inline void nexthop_link(struct rta *a, const struct nexthop *from) diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 25936d81..c8ef8e08 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -185,20 +185,40 @@ nexthop_hash(struct nexthop *x) return h; } +static inline int +nexthop_equal_1(struct nexthop *x, struct nexthop *y) +{ + if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) || + (x->flags != y->flags) || (x->weight != y->weight) || + (x->labels != y->labels)) + return 0; + + for (int i = 0; i < x->labels; i++) + if (x->label[i] != y->label[i]) + return 0; + + return 1; +} + int -nexthop__same(struct nexthop *x, struct nexthop *y) +nexthop_equal_(struct nexthop *x, struct nexthop *y) { + /* Like nexthop_same(), but ignores difference between local labels and labels from hostentry */ + for (; x && y; x = x->next, y = y->next) - { - if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) || - (x->flags != y->flags) || (x->weight != y->weight) || - (x->labels_orig != y->labels_orig) || (x->labels != y->labels)) + if (!nexthop_equal_1(x, y)) return 0; - for (int i = 0; i < x->labels; i++) - if (x->label[i] != y->label[i]) - return 0; - } + return x == y; +} + +int +nexthop__same(struct nexthop *x, struct nexthop *y) +{ + for (; x && y; x = x->next, y = y->next) + if (!nexthop_equal_1(x, y) || + (x->labels_orig != y->labels_orig)) + return 0; return x == y; } @@ -766,7 +786,7 @@ ea_list_copy(ea_list *o) { eattr *a = &o->attrs[i]; if (!(a->type & EAF_EMBEDDED)) - elen += sizeof(struct adata) + a->u.ptr->length; + elen += BIRD_ALIGN(sizeof(struct adata) + a->u.ptr->length, EA_DATA_ALIGN); } n = mb_alloc(rta_pool, elen); @@ -777,11 +797,12 @@ ea_list_copy(ea_list *o) eattr *a = &n->attrs[i]; if (!(a->type & EAF_EMBEDDED)) { - unsigned size = sizeof(struct adata) + a->u.ptr->length; + uint size_u = sizeof(struct adata) + a->u.ptr->length; + uint size = BIRD_ALIGN(size_u, EA_DATA_ALIGN); ASSERT_DIE(adpos + size <= elen); struct adata *d = ((void *) n) + adpos; - memcpy(d, a->u.ptr, size); + memcpy(d, a->u.ptr, size_u); a->u.ptr = d; adpos += size; @@ -994,6 +1015,9 @@ ea_show(struct cli *c, const eattr *e) case EAF_TYPE_LC_SET: ea_show_lc_set(c, ad, pos, buf, end); return; + case EAF_TYPE_STRING: + bsnprintf(pos, end - pos, "%s", (const char *) ad->data); + break; default: bsprintf(pos, "<type %02x>", e->type); } diff --git a/nest/rt-show.c b/nest/rt-show.c index 265d5c44..fb71fba8 100644 --- a/nest/rt-show.c +++ b/nest/rt-show.c @@ -72,7 +72,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary if (a->dest == RTD_UNICAST) for (nh = &(a->nh); nh; nh = nh->next) { - char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls; + char mpls[MPLS_MAX_LABEL_STRING], *lsp = mpls; char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : ""; char weight[16] = ""; diff --git a/proto/aggregator/config.Y b/proto/aggregator/config.Y index 44b7752f..0eb160cb 100644 --- a/proto/aggregator/config.Y +++ b/proto/aggregator/config.Y @@ -20,7 +20,7 @@ CF_DEFINES CF_DECLS -CF_KEYWORDS(AGGREGATOR, AGGREGATE, ON, MERGE, BY) +CF_KEYWORDS(AGGREGATOR, PEER, AGGREGATE, ON, MERGE, BY) %type <ai> aggr_item aggr_list diff --git a/proto/babel/config.Y b/proto/babel/config.Y index b8af0267..d412a54b 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -26,7 +26,7 @@ CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS, ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE, - EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS) + EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS, COST, DELAY) CF_GRAMMAR diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index 97eb2d9b..41d8e210 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -118,6 +118,43 @@ static list STATIC_LIST_INIT(bfd_wait_list); const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" }; +const char *bfd_diag_names[] = { + [BFD_DIAG_NOTHING] = "None", + [BFD_DIAG_TIMEOUT] = "Time expired", + [BFD_DIAG_ECHO_FAILED] = "Echo failed", + [BFD_DIAG_NEIGHBOR_DOWN] = "Neighbor down", + [BFD_DIAG_FWD_RESET] = "Fwd plane reset", + [BFD_DIAG_PATH_DOWN] = "Path down", + [BFD_DIAG_C_PATH_DOWN] = "Concat path down", + [BFD_DIAG_ADMIN_DOWN] = "Admin down", + [BFD_DIAG_RC_PATH_DOWN] = "Rev concat path down", +}; + +const char *bfd_auth_names[] = { + [BFD_AUTH_NONE] = "None", + [BFD_AUTH_SIMPLE] = "Simple", + [BFD_AUTH_KEYED_MD5] = "Keyed MD5", + [BFD_AUTH_METICULOUS_KEYED_MD5] = "Meticulous keyed MD5", + [BFD_AUTH_KEYED_SHA1] = "Keyed SHA1", + [BFD_AUTH_METICULOUS_KEYED_SHA1] = "Meticulous keyed SHA1", +}; + +#define BFD_DIAG_BUFFER_SIZE 16 + +static inline const char * +bfd_diag_name(u8 id, char buf[BFD_DIAG_BUFFER_SIZE]) +{ + return (id < ARRAY_SIZE(bfd_diag_names)) ? + bfd_diag_names[id] : + (bsprintf(buf, "Error #%u", (uint) id), buf); +} + +static inline const char * +bfd_auth_name(u8 id) +{ + return (id < ARRAY_SIZE(bfd_auth_names)) ? bfd_auth_names[id] : "?"; +} + static void bfd_session_set_min_tx(struct bfd_session *s, u32 val); static struct bfd_iface *bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface); static void bfd_free_iface(struct bfd_iface *ifa); @@ -136,7 +173,7 @@ bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *o .min_tx_int = opts->min_tx_int ?: cf->min_tx_int, .idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int, .multiplier = opts->multiplier ?: cf->multiplier, - .passive = opts->passive_set ? opts->passive : cf->passive + .passive = opts->passive_set ? opts->passive : cf->passive, }; } @@ -231,7 +268,7 @@ bfd_session_control_tx_timer(struct bfd_session *s, int reset) return; - stop: +stop: tm_stop(s->tx_timer); s->last_tx = 0; } @@ -548,7 +585,7 @@ static struct bfd_iface_config bfd_default_iface = { .min_rx_int = BFD_DEFAULT_MIN_RX_INT, .min_tx_int = BFD_DEFAULT_MIN_TX_INT, .idle_tx_int = BFD_DEFAULT_IDLE_TX_INT, - .multiplier = BFD_DEFAULT_MULTIPLIER + .multiplier = BFD_DEFAULT_MULTIPLIER, }; static inline struct bfd_iface_config * @@ -800,7 +837,7 @@ static struct resclass bfd_request_class = { bfd_request_free, bfd_request_dump, NULL, - NULL + NULL, }; @@ -1144,13 +1181,84 @@ bfd_copy_config(struct proto_config *dest, struct proto_config *src UNUSED) } void -bfd_show_sessions(struct proto *P) +bfd_show_session(struct bfd_session *s, int details) { + /* FIXME: this is thread-unsafe, but perhaps harmless */ + + u8 loc_state = s->loc_state; + u8 rem_state = s->rem_state; + u8 loc_diag = s->loc_diag; + u8 rem_diag = s->rem_diag; + uint loc_id = s->loc_id; + uint rem_id = s->rem_id; + + const char *ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---"; + btime tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0; + btime timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult; + u8 auth_type = s->ifa->cf->auth_type; + + loc_state = (loc_state < 4) ? loc_state : 0; + rem_state = (rem_state < 4) ? rem_state : 0; + + byte dbuf[BFD_DIAG_BUFFER_SIZE]; byte tbuf[TM_DATETIME_BUFFER_SIZE]; + tm_format_time(tbuf, &config->tf_proto, s->last_state_change); + + if (!details) + { + cli_msg(-1020, "%-25I %-10s %-10s %-12s %7t %7t", + s->addr, ifname, bfd_state_names[loc_state], tbuf, tx_int, timeout); + + return; + } + + cli_msg(-1020, " %-21s %I", "Address:", s->addr); + cli_msg(-1020, " %-21s %s", "Interface:", ifname); + cli_msg(-1020, " %-21s %s", "Session type:", s->ifa->iface ? "Direct" : "Multihop"); + cli_msg(-1020, " %-21s %s", "Session state:", bfd_state_names[loc_state]); + cli_msg(-1020, " %-21s %s", "Remote state:", bfd_state_names[rem_state]); + cli_msg(-1020, " %-21s %s", "Last state change:", tbuf); + cli_msg(-1020, " %-21s %s", "Local diagnostic:", bfd_diag_name(loc_diag, dbuf)); + cli_msg(-1020, " %-21s %s", "Remote diagnostic:", bfd_diag_name(rem_diag, dbuf)); + cli_msg(-1020, " %-21s %u", "Local discriminator:", loc_id); + cli_msg(-1020, " %-21s %u", "Remote discriminator:", rem_id); + + if (tm_active(s->tx_timer)) + cli_msg(-1020, " %-21s %t / %t", "Transmit timer:", tm_remains(s->tx_timer), tx_int); + + if (tm_active(s->hold_timer)) + cli_msg(-1020, " %-21s %t / %t", "Detect timer:", tm_remains(s->hold_timer), timeout); + + cli_msg(-1020, " Local parameters:"); + cli_msg(-1020, " %-19s %t", "Min TX interval:", (btime) s->des_min_tx_int); + cli_msg(-1020, " %-19s %t", "Min RX interval:", (btime) s->req_min_rx_int); + cli_msg(-1020, " %-19s %s", "Demand mode:", s->demand_mode ? "Yes" : "No"); + cli_msg(-1020, " %-19s %i", "Multiplier:", s->detect_mult); + cli_msg(-1020, " Remote parameters:"); + cli_msg(-1020, " %-19s %t", "Min TX interval:", (btime) s->rem_min_tx_int); + cli_msg(-1020, " %-19s %t", "Min RX interval:", (btime) s->rem_min_rx_int); + cli_msg(-1020, " %-19s %s", "Demand mode:", s->rem_demand_mode ? "Yes" : "No"); + cli_msg(-1020, " %-19s %i", "Multiplier:", s->rem_detect_mult); + + if (auth_type) + { + cli_msg(-1020, " Authentication:"); + cli_msg(-1020, " %-19s %s", "Type:", bfd_auth_name(auth_type)); + + if (s->rx_csn_known) + cli_msg(-1020, " %-19s %u", "RX CSN:", s->rx_csn); + + if (auth_type > BFD_AUTH_SIMPLE) + cli_msg(-1020, " %-19s %u", "TX CSN:", s->tx_csn); + } + + cli_msg(-1020, ""); +} + +void +bfd_show_sessions(struct proto *P, struct bfd_show_sessions_cmd *args) +{ struct bfd_proto *p = (struct bfd_proto *) P; - uint state, diag UNUSED; - btime tx_int, timeout; - const char *ifname; if (p->p.proto_state != PS_UP) { @@ -1159,24 +1267,25 @@ bfd_show_sessions(struct proto *P) } cli_msg(-1020, "%s:", p->p.name); - cli_msg(-1020, "%-25s %-10s %-10s %-12s %8s %8s", + if (!args->verbose) + cli_msg(-1020, "%-25s %-10s %-10s %-12s %8s %8s", "IP address", "Interface", "State", "Since", "Interval", "Timeout"); - HASH_WALK(p->session_hash_id, next_id, s) { - /* FIXME: this is thread-unsafe, but perhaps harmless */ - state = s->loc_state; - diag = s->loc_diag; - ifname = (s->ifa && s->ifa->iface) ? s->ifa->iface->name : "---"; - tx_int = s->last_tx ? MAX(s->des_min_tx_int, s->rem_min_rx_int) : 0; - timeout = (btime) MAX(s->req_min_rx_int, s->rem_min_tx_int) * s->rem_detect_mult; + if (args->address.type && !ipa_in_netX(s->addr, &args->address)) + continue; - state = (state < 4) ? state : 0; - tm_format_time(tbuf, &config->tf_proto, s->last_state_change); + if (args->iface && (s->ifa->iface != args->iface)) + continue; - cli_msg(-1020, "%-25I %-10s %-10s %-12s %7t %7t", - s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout); + if (ipa_is_ip4(s->addr) ? args->ipv6 : args->ipv4) + continue; + + if (s->ifa->iface ? args->multihop : args->direct) + continue; + + bfd_show_session(s, args->verbose); } HASH_WALK_END; } diff --git a/proto/bfd/bfd.h b/proto/bfd/bfd.h index 7caf9f66..e711bc80 100644 --- a/proto/bfd/bfd.h +++ b/proto/bfd/bfd.h @@ -172,6 +172,17 @@ struct bfd_session u32 tx_csn_time; /* Timestamp of last tx_csn change */ }; +struct bfd_show_sessions_cmd { + net_addr address; + struct iface *iface; + struct symbol *name; + u8 verbose; + u8 ipv4; + u8 ipv6; + u8 direct; + u8 multihop; +}; + extern const char *bfd_state_names[]; @@ -218,7 +229,7 @@ static inline void bfd_unlock_sessions(struct bfd_proto *p) { pthread_spin_unloc struct bfd_session * bfd_find_session_by_id(struct bfd_proto *p, u32 id); struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex); void bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int); -void bfd_show_sessions(struct proto *P); +void bfd_show_sessions(struct proto *P, struct bfd_show_sessions_cmd *args); /* packets.c */ void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final); diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y index d9a154b2..1a7474b0 100644 --- a/proto/bfd/config.Y +++ b/proto/bfd/config.Y @@ -29,6 +29,7 @@ CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE, %type <iface> bfd_neigh_iface %type <a> bfd_neigh_local %type <i> bfd_neigh_multihop bfd_auth_type +%type <bssc> bfd_show_sessions_args CF_GRAMMAR @@ -183,8 +184,33 @@ bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop CF_CLI_HELP(SHOW BFD, ..., [[Show information about BFD protocol]]); -CF_CLI(SHOW BFD SESSIONS, optproto, [<name>], [[Show information about BFD sessions]]) -{ PROTO_WALK_CMD($4, &proto_bfd, p) bfd_show_sessions(p); }; + +CF_CLI_HELP(SHOW BFD SESSIONS, ..., [[Show information about BFD sessions]]); +CF_CLI(SHOW BFD SESSIONS, bfd_show_sessions_args, [<name>] [address <ip|prefix>] [(interface|dev) \"<name>\"] [ipv4|ipv6] [direct|multihop] [all], [[Show information about BFD sessions]]) +{ PROTO_WALK_CMD($4->name, &proto_bfd, p) bfd_show_sessions(p, $4); }; + +CF_CLI_OPT(SHOW BFD SESSIONS ADDRESS, <ip>|<prefix>) +CF_CLI_OPT(SHOW BFD SESSIONS INTERFACE, \"<name>\") +CF_CLI_OPT(SHOW BFD SESSIONS DEV, \"<name>\") +CF_CLI_OPT(SHOW BFD SESSIONS ALL) +CF_CLI_OPT(SHOW BFD SESSIONS IPV4) +CF_CLI_OPT(SHOW BFD SESSIONS IPV6) +CF_CLI_OPT(SHOW BFD SESSIONS DIRECT) +CF_CLI_OPT(SHOW BFD SESSIONS MULTIHOP) + +bfd_show_sessions_args: + /* empty */ { $$ = cfg_allocz(sizeof(struct bfd_show_sessions_cmd)); } + | bfd_show_sessions_args CF_SYM_KNOWN { cf_assert_symbol($2, SYM_PROTO); $$->name = $2; } + | bfd_show_sessions_args ADDRESS net_or_ipa { net_copy(&($$->address), &($3)); } + | bfd_show_sessions_args INTERFACE text { $$->iface = if_get_by_name($3); } + | bfd_show_sessions_args DEV text { $$->iface = if_get_by_name($3); } + | bfd_show_sessions_args ALL { $$->verbose = 1; } + | bfd_show_sessions_args IPV4 { $$->ipv4 = 1; if ($$->ipv6) cf_error("Options 'ipv4' and 'ipv6' are mutually exclusive"); } + | bfd_show_sessions_args IPV6 { $$->ipv6 = 1; if ($$->ipv4) cf_error("Options 'ipv4' and 'ipv6' are mutually exclusive"); } + | bfd_show_sessions_args DIRECT { $$->direct = 1; if ($$->multihop) cf_error("Options 'direct' and 'multihop' are mutually exclusive"); } + | bfd_show_sessions_args MULTIHOP { $$->multihop = 1; if ($$->direct) cf_error("Options 'direct' and 'multihop' are mutually exclusive"); } + ; + CF_CODE diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index cb5f0d89..e2ec5988 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -96,12 +96,12 @@ bfd_format_flags(u8 flags, char *buf) } const u8 bfd_auth_type_to_hash_alg[] = { - [BFD_AUTH_NONE] = ALG_UNDEFINED, - [BFD_AUTH_SIMPLE] = ALG_UNDEFINED, - [BFD_AUTH_KEYED_MD5] = ALG_MD5, - [BFD_AUTH_METICULOUS_KEYED_MD5] = ALG_MD5, - [BFD_AUTH_KEYED_SHA1] = ALG_SHA1, - [BFD_AUTH_METICULOUS_KEYED_SHA1] = ALG_SHA1, + [BFD_AUTH_NONE] = ALG_UNDEFINED, + [BFD_AUTH_SIMPLE] = ALG_UNDEFINED, + [BFD_AUTH_KEYED_MD5] = ALG_MD5, + [BFD_AUTH_METICULOUS_KEYED_MD5] = ALG_MD5, + [BFD_AUTH_KEYED_SHA1] = ALG_SHA1, + [BFD_AUTH_METICULOUS_KEYED_SHA1] = ALG_SHA1, }; @@ -151,7 +151,7 @@ bfd_fill_authentication(struct bfd_proto *p, struct bfd_session *s, struct bfd_c uint hash_len = mac_type_length(pass->alg); /* Increase CSN about one time per second */ - u32 new_time = (u64) current_time() >> 20; + u32 new_time = (u64) current_time() >> 20; if ((new_time != s->tx_csn_time) || meticulous) { s->tx_csn++; @@ -321,7 +321,7 @@ bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final) static int bfd_rx_hook(sock *sk, uint len) { - struct bfd_proto *p = sk->data; + struct bfd_proto *p = sk->data; struct bfd_ctl_packet *pkt = (struct bfd_ctl_packet *) sk->rbuf; const char *err_dsc = NULL; uint err_val = 0; @@ -387,7 +387,7 @@ bfd_rx_hook(sock *sk, uint len) u32 old_tx_int = s->des_min_tx_int; u32 old_rx_int = s->rem_min_rx_int; - s->rem_id= ntohl(pkt->snd_id); + s->rem_id = ntohl(pkt->snd_id); s->rem_state = bfd_pkt_get_state(pkt); s->rem_diag = bfd_pkt_get_diag(pkt); s->rem_demand_mode = pkt->flags & BFD_FLAG_DEMAND; @@ -438,7 +438,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af) sk_start(sk); return sk; - err: +err: sk_log_error(sk, p->p.name); rfree(sk); return NULL; @@ -470,7 +470,7 @@ bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa) sk_start(sk); return sk; - err: +err: sk_log_error(sk, p->p.name); rfree(sk); return NULL; @@ -502,7 +502,7 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa) sk_start(sk); return sk; - err: +err: sk_log_error(sk, p->p.name); rfree(sk); return NULL; diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 4346cd5d..85646647 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -89,7 +89,7 @@ bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintp attrs, pool, EA_CODE(PROTOCOL_BGP, code), - flags & ~BAF_EXT_LEN, + bgp_attr_table[code].flags | (flags & BAF_PARTIAL), bgp_attr_table[code].type, val ); @@ -1158,13 +1158,6 @@ bgp_attr_name(uint code) return (code < ARRAY_SIZE(bgp_attr_table)) ? bgp_attr_table[code].name : NULL; } -void bgp_fix_attr_flags(ea_list *attrs) -{ - for (u8 i = 0; i < attrs->count; i++) - { - attrs->attrs[i].flags = bgp_attr_table[EA_ID(attrs->attrs[i].id)].flags; - } -} /* * Attribute export @@ -1182,7 +1175,8 @@ bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to) { const struct bgp_attr_desc *desc = &bgp_attr_table[code]; - /* The flags might have been zero if the attr was added by filters */ + /* The flags should be correct, we reset them just to be sure */ + ASSERT(!((a->flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE))); a->flags = (a->flags & BAF_PARTIAL) | desc->flags; /* Set partial bit if new opt-trans attribute is attached to non-local route */ diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 9d4671af..bd6e90d6 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1059,12 +1059,13 @@ bgp_send_hold_timeout(timer *t) struct bgp_conn *conn = t->data; struct bgp_proto *p = conn->bgp; + DBG("BGP: Send hold timeout\n"); + if (conn->state == BS_CLOSE) return; - /* Error codes not yet assigned by IANA */ - uint code = 4; - uint subcode = 1; + uint code = 8; + uint subcode = 0; /* Like bgp_error() but without NOTIFICATION */ bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, NULL, 0); @@ -1126,7 +1127,7 @@ static void bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing connection */ { struct bgp_conn *conn = &p->outgoing_conn; - int hops = p->cf->multihop ? : 1; + int hops = p->cf->multihop ?: 1; DBG("BGP: Connecting\n"); sock *s = sk_new(p->p.pool); @@ -1263,7 +1264,7 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED) return 0; } - hops = p->cf->multihop ? : 1; + hops = p->cf->multihop ?: 1; if (sk_set_ttl(sk, p->cf->ttl_security ? 255 : hops) < 0) goto err; @@ -1616,6 +1617,7 @@ bgp_start(struct proto *P) struct object_lock *lock; lock = p->lock = olock_new(P->pool); lock->addr = p->remote_ip; + lock->addr_local = p->cf->local_ip; lock->port = p->cf->remote_port; lock->iface = p->cf->iface; lock->vrf = p->cf->iface ? NULL : p->p.vrf; @@ -2245,7 +2247,7 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF) return 1; } -#define TABLE(cf, NAME) ((cf)->NAME ? (cf)->NAME->table : NULL ) +#define TABLE(cf, NAME) ((cf)->NAME ? (cf)->NAME->table : NULL) static int bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed, int *export_changed) @@ -2377,10 +2379,10 @@ bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code) } static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" }; -static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""}; -static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket", "Link down", "BFD session down", "Graceful restart"}; -static char *bgp_auto_errors[] = { "", "Route limit exceeded"}; -static char *bgp_gr_states[] = { "None", "Regular", "Long-lived"}; +static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", "" }; +static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket", "Link down", "BFD session down", "Graceful restart" }; +static char *bgp_auto_errors[] = { "", "Route limit exceeded" }; +static char *bgp_gr_states[] = { "None", "Regular", "Long-lived" }; static const char * bgp_last_errmsg(struct bgp_proto *p) diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 1173ff06..4ce06488 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -349,33 +349,33 @@ bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end; dynamic_attr: BGP_ORIGIN - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); $$.flags = BAF_TRANSITIVE; } ; dynamic_attr: BGP_PATH - { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); $$.flags = BAF_TRANSITIVE; } ; dynamic_attr: BGP_NEXT_HOP - { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); $$.flags = BAF_TRANSITIVE; } ; dynamic_attr: BGP_MED - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); $$.flags = BAF_OPTIONAL; } ; dynamic_attr: BGP_LOCAL_PREF - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); $$.flags = BAF_TRANSITIVE; } ; dynamic_attr: BGP_ATOMIC_AGGR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); $$.flags = BAF_TRANSITIVE; } ; dynamic_attr: BGP_AGGREGATOR - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; dynamic_attr: BGP_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; dynamic_attr: BGP_ORIGINATOR_ID - { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); $$.flags = BAF_OPTIONAL; } ; dynamic_attr: BGP_CLUSTER_LIST - { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); $$.flags = BAF_OPTIONAL; } ; dynamic_attr: BGP_EXT_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; dynamic_attr: BGP_AIGP - { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); $$.flags = BAF_OPTIONAL; } ; dynamic_attr: BGP_LARGE_COMMUNITY - { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; dynamic_attr: BGP_OTC - { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); } ; + { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); $$.flags = BAF_OPTIONAL | BAF_TRANSITIVE; } ; custom_attr: ATTRIBUTE BGP expr type symbol ';' { if ($3 > 255 || $3 < 1) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index e8cc4718..f1e03621 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -3275,7 +3275,6 @@ static struct { { 3, 10, "Invalid network field" }, { 3, 11, "Malformed AS_PATH" }, { 4, 0, "Hold timer expired" }, - { 4, 1, "Send hold timer expired" }, /* Provisional [draft-ietf-idr-bgp-sendholdtimer] */ { 5, 0, "Finite state machine error" }, /* Subcodes are according to [RFC6608] */ { 5, 1, "Unexpected message in OpenSent state" }, { 5, 2, "Unexpected message in OpenConfirm state" }, @@ -3290,7 +3289,8 @@ static struct { { 6, 7, "Connection collision resolution" }, { 6, 8, "Out of Resources" }, { 7, 0, "Invalid ROUTE-REFRESH message" }, /* [RFC7313] */ - { 7, 1, "Invalid ROUTE-REFRESH message length" } /* [RFC7313] */ + { 7, 1, "Invalid ROUTE-REFRESH message length" }, /* [RFC7313] */ + { 8, 0, "Send hold timer expired" }, /* [draft-ietf-idr-bgp-sendholdtimer] */ }; /** @@ -3377,8 +3377,8 @@ bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode, *t++ = ':'; *t++ = ' '; - if (len > 16) - len = 16; + if (len > 128) + len = 128; for (i=0; i<len; i++) t += bsprintf(t, "%02x", data[i]); } diff --git a/proto/mrt/config.Y b/proto/mrt/config.Y index 4da6777a..d2136ed1 100644 --- a/proto/mrt/config.Y +++ b/proto/mrt/config.Y @@ -53,6 +53,11 @@ CF_CLI_HELP(MRT DUMP, [table <name>|\"<pattern>\"] [to \"<file>\"] [filter <filt CF_CLI(MRT DUMP, mrt_dump_args, [table <name>|\"<pattern>\"] [to \"<file>\"] [filter <filter>|where <where filter>], [[Save mrt table dump v2 of table name <t> right now]]) { mrt_dump_cmd($3); } ; +CF_CLI_OPT(MRT DUMP TABLE, <name>|\"<pattern>\") +CF_CLI_OPT(MRT DUMP TO, \"<file>\") +CF_CLI_OPT(MRT DUMP FILTER, <filter>) +CF_CLI_OPT(MRT DUMP WHERE, <where filter>) + mrt_dump_args: /* empty */ { $$ = cfg_allocz(sizeof(struct mrt_dump_data)); } | mrt_dump_args TABLE rtable { $$ = $1; $$->table_ptr = $3->table; } diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 71d9d05b..7d35304a 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -546,6 +546,14 @@ CF_CLI(SHOW OSPF LSADB, lsadb_args, [global | area <id> | link] [type <num>] [ls ospf_sh_lsadb($4); }; +CF_CLI_OPT(SHOW OSPF LSADB GLOBAL) +CF_CLI_OPT(SHOW OSPF LSADB AREA, <id>) +CF_CLI_OPT(SHOW OSPF LSADB LINK) +CF_CLI_OPT(SHOW OSPF LSADB TYPE, <num>) +CF_CLI_OPT(SHOW OSPF LSADB LSID, <id>) +CF_CLI_OPT(SHOW OSPF LSADB SELF) +CF_CLI_OPT(SHOW OSPF LSADB ROUTER, <id>) + lsadb_args: /* empty */ { $$ = cfg_allocz(sizeof(struct lsadb_show_data)); diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index eca138d7..77e52ce8 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -16,8 +16,8 @@ static inline void lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_heade static inline void lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { *h = *n; }; static inline void lsa_hton_body(void *h, void *n, u16 len) { ASSERT(h != n); memcpy(n, h, len); }; static inline void lsa_ntoh_body(void *n, void *h, u16 len) { ASSERT(n != h); memcpy(h, n, len); }; -static inline void lsa_hton_body1(void *h, u16 len) { }; -static inline void lsa_ntoh_body1(void *n, u16 len) { }; +static inline void lsa_hton_body1(void *h UNUSED, u16 len UNUSED) { }; +static inline void lsa_ntoh_body1(void *n UNUSED, u16 len UNUSED) { }; #else void lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n); void lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h); diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 54c4a069..12c9cfaa 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -172,7 +172,7 @@ ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n) WALK_SLIST(en, p->lsal) if ((en->lsa.age == LSA_MAXAGE) && (en->lsa_body != NULL) && lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa) && - lsa_is_acceptable(en->lsa_type, n, p)) + lsa_is_acceptable(en->lsa_type, n, p)) ospf_lsa_lsrt_up(en, n); /* If we found any flushed LSA, we send them ASAP */ diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 1c3509e4..4bfb5756 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -77,6 +77,7 @@ */ #include <stdlib.h> + #include "rip.h" @@ -1358,7 +1359,7 @@ struct protocol proto_rip = { .shutdown = rip_shutdown, .reconfigure = rip_reconfigure, .get_route_info = rip_get_route_info, - .get_attr = rip_get_attr + .get_attr = rip_get_attr, }; void diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y index c28cab7a..769ebb2c 100644 --- a/proto/rpki/config.Y +++ b/proto/rpki/config.Y @@ -32,7 +32,7 @@ rpki_check_unused_transport(void) CF_DECLS CF_KEYWORDS(RPKI, REMOTE, BIRD, PRIVATE, PUBLIC, KEY, TCP, SSH, TRANSPORT, USER, - RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH) + RETRY, REFRESH, EXPIRE, KEEP, IGNORE, MAX, LENGTH, LOCAL, ADDRESS) %type <i> rpki_keep_interval @@ -60,6 +60,7 @@ rpki_proto_item: | REMOTE rpki_cache_addr | REMOTE rpki_cache_addr rpki_proto_item_port | rpki_proto_item_port + | LOCAL ADDRESS ipa { RPKI_CFG->local_ip = $3; } | TRANSPORT rpki_transport | REFRESH rpki_keep_interval expr { if (rpki_check_refresh_interval($3)) diff --git a/proto/rpki/rpki.h b/proto/rpki/rpki.h index 8a5c38fd..e67eb0e3 100644 --- a/proto/rpki/rpki.h +++ b/proto/rpki/rpki.h @@ -116,6 +116,7 @@ struct rpki_proto { struct rpki_config { struct proto_config c; const char *hostname; /* Full domain name or stringified IP address of cache server */ + ip_addr local_ip; /* Source address to use */ ip_addr ip; /* IP address of cache server or IPA_NONE */ u16 port; /* Port number of cache server */ struct rpki_tr_config tr_config; /* Specific transport configuration structure */ diff --git a/proto/rpki/transport.c b/proto/rpki/transport.c index 81bd6dd8..26571977 100644 --- a/proto/rpki/transport.c +++ b/proto/rpki/transport.c @@ -82,6 +82,7 @@ rpki_tr_open(struct rpki_tr_sock *tr) sk->daddr = cf->ip; sk->dport = cf->port; sk->host = cf->hostname; + sk->saddr = cf->local_ip; sk->rbsize = RPKI_RX_BUFFER_SIZE; sk->tbsize = RPKI_TX_BUFFER_SIZE; sk->tos = IP_PREC_INTERNET_CONTROL; diff --git a/proto/static/config.Y b/proto/static/config.Y index 215681e8..7b282898 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -45,7 +45,7 @@ static_route_finish(void) CF_DECLS -CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK) +CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK, DEV) CF_KEYWORDS(ONLINK, WEIGHT, RECURSIVE, IGP, TABLE, BLACKHOLE, UNREACHABLE, BFD, MPLS) @@ -87,6 +87,9 @@ stat_nexthop: this_snh->via = IPA_NONE; this_snh->iface = if_get_by_name($2); } + | stat_nexthop DEV TEXT { + this_snh->iface = if_get_by_name($3); + } | stat_nexthop MPLS label_stack { this_snh->mls = $3; } diff --git a/sysdep/config.h b/sysdep/config.h index ffab1670..54aa4021 100644 --- a/sysdep/config.h +++ b/sysdep/config.h @@ -13,7 +13,7 @@ #ifdef GIT_LABEL #define BIRD_VERSION XSTR1(GIT_LABEL) #else -#define BIRD_VERSION "2.14" +#define BIRD_VERSION "2.15" #endif /* Include parameters determined by configure script */ diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h index 8897f889..2db32ddd 100644 --- a/sysdep/linux/krt-sys.h +++ b/sysdep/linux/krt-sys.h @@ -39,7 +39,7 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N #define EA_KRT_SCOPE EA_CODE(PROTOCOL_KERNEL, 0x12) -#define KRT_METRICS_MAX 0x10 /* RTAX_QUICKACK+1 */ +#define KRT_METRICS_MAX 0x12 /* RTAX_FASTOPEN_NO_COOKIE+1 */ #define KRT_METRICS_OFFSET 0x20 /* Offset of EA_KRT_* vs RTAX_* */ #define KRT_FEATURES_MAX 4 @@ -54,7 +54,7 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N #define EA_KRT_WINDOW EA_CODE(PROTOCOL_KERNEL, 0x23) #define EA_KRT_RTT EA_CODE(PROTOCOL_KERNEL, 0x24) #define EA_KRT_RTTVAR EA_CODE(PROTOCOL_KERNEL, 0x25) -#define EA_KRT_SSTRESH EA_CODE(PROTOCOL_KERNEL, 0x26) +#define EA_KRT_SSTHRESH EA_CODE(PROTOCOL_KERNEL, 0x26) #define EA_KRT_CWND EA_CODE(PROTOCOL_KERNEL, 0x27) #define EA_KRT_ADVMSS EA_CODE(PROTOCOL_KERNEL, 0x28) #define EA_KRT_REORDERING EA_CODE(PROTOCOL_KERNEL, 0x29) @@ -64,6 +64,8 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N #define EA_KRT_RTO_MIN EA_CODE(PROTOCOL_KERNEL, 0x2d) #define EA_KRT_INITRWND EA_CODE(PROTOCOL_KERNEL, 0x2e) #define EA_KRT_QUICKACK EA_CODE(PROTOCOL_KERNEL, 0x2f) +#define EA_KRT_CONGCTL EA_CODE(PROTOCOL_KERNEL, 0x30) +#define EA_KRT_FASTOPEN_NO_COOKIE EA_CODE(PROTOCOL_KERNEL, 0x31) struct krt_params { diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y index 487ad1d8..9c820da9 100644 --- a/sysdep/linux/netlink.Y +++ b/sysdep/linux/netlink.Y @@ -12,11 +12,19 @@ CF_DECLS CF_KEYWORDS(KERNEL, TABLE, METRIC, NETLINK, RX, BUFFER, KRT_PREFSRC, KRT_REALM, KRT_SCOPE, KRT_MTU, KRT_WINDOW, - KRT_RTT, KRT_RTTVAR, KRT_SSTRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING, + KRT_RTT, KRT_RTTVAR, KRT_SSTHRESH, KRT_CWND, KRT_ADVMSS, KRT_REORDERING, KRT_HOPLIMIT, KRT_INITCWND, KRT_RTO_MIN, KRT_INITRWND, KRT_QUICKACK, - KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, - KRT_LOCK_SSTRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING, - KRT_LOCK_HOPLIMIT, KRT_LOCK_RTO_MIN, KRT_FEATURE_ECN, KRT_FEATURE_ALLFRAG) + KRT_CONGCTL, KRT_FASTOPEN_NO_COOKIE) + +CF_KEYWORDS(KRT_LOCK_MTU, KRT_LOCK_WINDOW, KRT_LOCK_RTT, KRT_LOCK_RTTVAR, + KRT_LOCK_SSTHRESH, KRT_LOCK_CWND, KRT_LOCK_ADVMSS, KRT_LOCK_REORDERING, + KRT_LOCK_HOPLIMIT, KRT_LOCK_INITCWND, KRT_LOCK_RTO_MIN, KRT_LOCK_INITRWND, + KRT_LOCK_QUICKACK, KRT_LOCK_CONGCTL, KRT_LOCK_FASTOPEN_NO_COOKIE, + KRT_FEATURE_ECN, KRT_FEATURE_ALLFRAG) + +/* Deprecated names for backward compatiblity */ +CF_KEYWORDS(KRT_SSTRESH, KRT_LOCK_SSTRESH) + CF_GRAMMAR @@ -36,7 +44,9 @@ dynamic_attr: KRT_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_MTU dynamic_attr: KRT_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_WINDOW); } ; dynamic_attr: KRT_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTT); } ; dynamic_attr: KRT_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTTVAR); } ; -dynamic_attr: KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTRESH); } ; +dynamic_attr: KRT_SSTHRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTHRESH); } ; +dynamic_attr: KRT_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_SSTHRESH); + cf_warn("Attribute krt_sstresh is deprecated (typo), use krt_ssthresh"); } ; dynamic_attr: KRT_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_CWND); } ; dynamic_attr: KRT_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_ADVMSS); } ; dynamic_attr: KRT_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_REORDERING); } ; @@ -45,6 +55,8 @@ dynamic_attr: KRT_INITCWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT dynamic_attr: KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_RTO_MIN); } ; dynamic_attr: KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); } ; dynamic_attr: KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); } ; +dynamic_attr: KRT_CONGCTL { $$ = f_new_dynamic_attr(EAF_TYPE_STRING, T_STRING, EA_KRT_CONGCTL); } ; +dynamic_attr: KRT_FASTOPEN_NO_COOKIE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_FASTOPEN_NO_COOKIE); } ; /* Bits of EA_KRT_LOCK, based on RTAX_* constants */ @@ -52,15 +64,23 @@ dynamic_attr: KRT_LOCK_MTU { $$ = f_new_dynamic_attr_bit(2, T_BOOL, EA_KRT_LOCK) dynamic_attr: KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr_bit(3, T_BOOL, EA_KRT_LOCK); } ; dynamic_attr: KRT_LOCK_RTT { $$ = f_new_dynamic_attr_bit(4, T_BOOL, EA_KRT_LOCK); } ; dynamic_attr: KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr_bit(5, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr_bit(6, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_SSTHRESH { $$ = f_new_dynamic_attr_bit(6, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr_bit(6, T_BOOL, EA_KRT_LOCK); + cf_warn("Attribute krt_lock_sstresh is deprecated (typo), use krt_lock_ssthresh"); } ; dynamic_attr: KRT_LOCK_CWND { $$ = f_new_dynamic_attr_bit(7, T_BOOL, EA_KRT_LOCK); } ; dynamic_attr: KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr_bit(8, T_BOOL, EA_KRT_LOCK); } ; dynamic_attr: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr_bit(9, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr_bit(10, T_BOOL, EA_KRT_LOCK); } ; -dynamic_attr: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr_bit(13, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr_bit(10, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_INITCWND { $$ = f_new_dynamic_attr_bit(11, T_BOOL, EA_KRT_LOCK); } ; +/* No lock for FEATURES bitfield */ +dynamic_attr: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr_bit(13, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_INITRWND { $$ = f_new_dynamic_attr_bit(14, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_QUICKACK { $$ = f_new_dynamic_attr_bit(15, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_CONGCTL { $$ = f_new_dynamic_attr_bit(16, T_BOOL, EA_KRT_LOCK); } ; +dynamic_attr: KRT_LOCK_FASTOPEN_NO_COOKIE { $$ = f_new_dynamic_attr_bit(17, T_BOOL, EA_KRT_LOCK); } ; dynamic_attr: KRT_FEATURE_ECN { $$ = f_new_dynamic_attr_bit(0, T_BOOL, EA_KRT_FEATURES); } ; -dynamic_attr: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(3, T_BOOL, EA_KRT_FEATURES); } ; +dynamic_attr: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr_bit(3, T_BOOL, EA_KRT_FEATURES); } ; CF_CODE diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 29446cab..6b4fc80c 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -479,6 +479,9 @@ static inline u16 rta_get_u16(struct rtattr *a) static inline u32 rta_get_u32(struct rtattr *a) { return *(u32 *) RTA_DATA(a); } +static inline const char *rta_get_str(struct rtattr *a) +{ return RTA_DATA(a); } + static inline ip4_addr rta_get_ip4(struct rtattr *a) { return ip4_ntoh(*(ip4_addr *) RTA_DATA(a)); } @@ -570,6 +573,12 @@ nl_add_attr_u32(struct nlmsghdr *h, uint bufsize, int code, u32 data) } static inline void +nl_add_attr_str(struct nlmsghdr *h, unsigned bufsize, int code, const char *str) +{ + nl_add_attr(h, bufsize, code, str, strlen(str) + 1); +} + +static inline void nl_add_attr_ip4(struct nlmsghdr *h, uint bufsize, int code, ip4_addr ip4) { ip4 = ip4_hton(ip4); @@ -822,21 +831,26 @@ err: return NULL; } +STATIC_ASSERT(EA_KRT_METRICS + RTAX_CC_ALGO == EA_KRT_CONGCTL); + static void -nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, int max) +nl_add_metrics(struct nlmsghdr *h, uint bufsize, u32 *metrics, const char *cc_algo, int max) { struct rtattr *a = nl_open_attr(h, bufsize, RTA_METRICS); int t; for (t = 1; t < max; t++) if (metrics[0] & (1 << t)) - nl_add_attr_u32(h, bufsize, t, metrics[t]); + if (t == RTAX_CC_ALGO) + nl_add_attr_str(h, bufsize, t, cc_algo); + else + nl_add_attr_u32(h, bufsize, t, metrics[t]); nl_close_attr(h, a); } static int -nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) +nl_parse_metrics(struct rtattr *hdr, u32 *metrics, const char **cc_algo, int max) { struct rtattr *a = RTA_DATA(hdr); int len = RTA_PAYLOAD(hdr); @@ -844,17 +858,31 @@ nl_parse_metrics(struct rtattr *hdr, u32 *metrics, int max) metrics[0] = 0; for (; RTA_OK(a, len); a = RTA_NEXT(a, len)) { - if (a->rta_type == RTA_UNSPEC) + if (a->rta_type == RTAX_UNSPEC) continue; if (a->rta_type >= max) continue; - if (RTA_PAYLOAD(a) != 4) - return -1; + if (a->rta_type == RTAX_CC_ALGO) + { + *cc_algo = rta_get_str(a); + int slen = RTA_PAYLOAD(a); + + if (!slen || ((*cc_algo)[slen - 1] != 0)) + return -1; - metrics[0] |= 1 << a->rta_type; - metrics[a->rta_type] = rta_get_u32(a); + metrics[0] |= 1 << a->rta_type; + metrics[a->rta_type] = 0; + } + else + { + if (RTA_PAYLOAD(a) != 4) + return -1; + + metrics[0] |= 1 << a->rta_type; + metrics[a->rta_type] = rta_get_u32(a); + } } if (len > 0) @@ -1382,6 +1410,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op) u32 metrics[KRT_METRICS_MAX]; + const char *cc_algo = NULL; metrics[0] = 0; struct ea_walk_state ews = { .eattrs = eattrs }; @@ -1389,11 +1418,15 @@ nl_send_route(struct krt_proto *p, rte *e, int op) { int id = ea->id - EA_KRT_METRICS; metrics[0] |= 1 << id; - metrics[id] = ea->u.data; + + if (id == RTAX_CC_ALGO) + cc_algo = ea->u.ptr->data; + else + metrics[id] = ea->u.data; } if (metrics[0]) - nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX); + nl_add_metrics(&r->h, rsize, metrics, cc_algo, KRT_METRICS_MAX); switch (a->dest) { @@ -1795,10 +1828,11 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if (a[RTA_METRICS]) { u32 metrics[KRT_METRICS_MAX]; + const char *cc_algo = NULL; ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr)); int t, n = 0; - if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0) + if (nl_parse_metrics(a[RTA_METRICS], metrics, &cc_algo, ARRAY_SIZE(metrics)) < 0) { log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net->n.addr); return; @@ -1806,12 +1840,33 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) for (t = 1; t < KRT_METRICS_MAX; t++) if (metrics[0] & (1 << t)) - ea->attrs[n++] = (eattr) { - .id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t), - .flags = 0, - .type = EAF_TYPE_INT, /* FIXME: Some are EAF_TYPE_BITFIELD */ - .u.data = metrics[t], - }; + if ((t == RTAX_LOCK) || (t == RTAX_FEATURES)) + { + ea->attrs[n++] = (eattr) { + .id = EA_KRT_METRICS + t, + .type = EAF_TYPE_BITFIELD, + .u.data = metrics[t], + }; + } + else if (t == RTAX_CC_ALGO) + { + struct adata *ad = lp_alloc_adata(s->pool, strlen(cc_algo)); + memcpy(ad->data, cc_algo, ad->length); + + ea->attrs[n++] = (eattr) { + .id = EA_KRT_CONGCTL, + .type = EAF_TYPE_STRING, + .u.ptr = ad, + }; + } + else + { + ea->attrs[n++] = (eattr) { + .id = EA_KRT_METRICS + t, + .type = EAF_TYPE_INT, + .u.data = metrics[t], + }; + } if (n > 0) { @@ -2093,8 +2148,9 @@ krt_sys_copy_config(struct krt_config *d, struct krt_config *s) } static const char *krt_metrics_names[KRT_METRICS_MAX] = { - NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss", - "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack" + NULL, "lock", "mtu", "window", "rtt", "rttvar", "ssthresh", "cwnd", "advmss", + "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack", + "congctl", "fastopen_no_cookie" }; static const char *krt_features_names[KRT_FEATURES_MAX] = { diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y index fce64794..8de79600 100644 --- a/sysdep/unix/config.Y +++ b/sysdep/unix/config.Y @@ -17,9 +17,9 @@ static struct log_config *this_log; CF_DECLS -CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT) -CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS) -CF_KEYWORDS(GRACEFUL, RESTART, THREADS) +CF_KEYWORDS(LOG, SYSLOG, NAME, STDERR, UDP, PORT) +CF_KEYWORDS(ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG) +CF_KEYWORDS(DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, TIMEOUT, THREADS) %type <i> log_mask log_mask_list log_cat cfg_timeout %type <t> cfg_name @@ -64,8 +64,28 @@ log_file: } | SYSLOG syslog_name { this_log->fh = NULL; new_config->syslog_name = $2; } | STDERR { this_log->fh = stderr; } + | UDP log_udp_host log_udp_port { + this_log->udp_flag = 1; + + if (!parse_and_exit) + log_open_udp(this_log, new_config->pool); + } ; +log_udp_host: text_or_ipa +{ + if ($1.type == T_STRING) + this_log->host = $1.val.s; + else if ($1.type == T_IP) + this_log->ip = $1.val.ip; + else bug("Bad text_or_ipa"); +}; + +log_udp_port: + /* empty */ { this_log->port = 514; } + | PORT NUM { check_u16($2); this_log->port = $2; } + ; + log_mask: ALL { $$ = ~0; } | '{' log_mask_list '}' { $$ = $2; } diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 4b3eef48..9b499020 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -30,6 +30,7 @@ #include <netinet/tcp.h> #include <netinet/udp.h> #include <netinet/icmp6.h> +#include <netdb.h> #include "nest/bird.h" #include "lib/lists.h" @@ -94,12 +95,25 @@ struct rfile * rf_open(pool *p, const char *name, const char *mode) { FILE *f = fopen(name, mode); + if (!f) + return NULL; + + struct rfile *r = ralloc(p, &rf_class); + r->f = f; + return r; +} + +struct rfile * +rf_fdopen(pool *p, int fd, const char *mode) +{ + FILE *f = fdopen(fd, mode); if (!f) return NULL; struct rfile *r = ralloc(p, &rf_class); r->f = f; + return r; } @@ -1048,6 +1062,14 @@ sk_insert(sock *s) add_tail(&sock_list, &s->n); } +static int +sk_connect(sock *s) +{ + sockaddr sa; + sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); + return connect(s->fd, &sa.sa, SA_LEN(sa)); +} + static void sk_tcp_connected(sock *s) { @@ -1465,8 +1487,7 @@ sk_open(sock *s) switch (s->type) { case SK_TCP_ACTIVE: - sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); - if (connect(fd, &sa.sa, SA_LEN(sa)) >= 0) + if (sk_connect(s) >= 0) sk_tcp_connected(s); else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != ECONNREFUSED && errno != EHOSTUNREACH && errno != ENETUNREACH) @@ -1478,6 +1499,14 @@ sk_open(sock *s) ERR2("listen"); break; + case SK_UDP: + if (s->flags & SKF_CONNECT) + if (sk_connect(s) < 0) + ERR2("connect"); + + sk_alloc_bufs(s); + break; + case SK_SSH_ACTIVE: case SK_MAGIC: break; @@ -1945,10 +1974,7 @@ sk_write_noflush(sock *s) { case SK_TCP_ACTIVE: { - sockaddr sa; - sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); - - if (connect(s->fd, &sa.sa, SA_LEN(sa)) >= 0 || errno == EISCONN) + if (sk_connect(s) >= 0 || errno == EISCONN) sk_tcp_connected(s); else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) s->err_hook(s, errno); @@ -2418,3 +2444,36 @@ test_old_bird(char *path) die("I found another BIRD running."); close(fd); } + + +/* + * DNS resolver + */ + +ip_addr +resolve_hostname(const char *host, int type, const char **err_msg) +{ + struct addrinfo *res; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = (type == SK_UDP) ? SOCK_DGRAM : SOCK_STREAM, + .ai_flags = AI_ADDRCONFIG, + }; + + *err_msg = NULL; + + int err_code = getaddrinfo(host, NULL, &hints, &res); + if (err_code != 0) + { + *err_msg = gai_strerror(err_code); + return IPA_NONE; + } + + ip_addr addr = IPA_NONE; + uint unused; + + sockaddr_read((sockaddr *) res->ai_addr, res->ai_family, &addr, NULL, &unused); + freeaddrinfo(res); + + return addr; +} diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 3a4b24dc..7a078fb9 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -619,7 +619,7 @@ krt_same_dest(rte *k, rte *e) return 0; if (ka->dest == RTD_UNICAST) - return nexthop_same(&(ka->nh), &(ea->nh)); + return nexthop_equal(&(ka->nh), &(ea->nh)); return 1; } diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 53122aee..613a6aa5 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -29,6 +29,7 @@ #include "conf/conf.h" #include "lib/string.h" #include "lib/lists.h" +#include "lib/socket.h" #include "sysdep/unix/unix.h" static int dbg_fd = -1; @@ -138,6 +139,55 @@ log_rotate(struct log_config *l) return log_open(l); } +/* Expected to be called during config parsing */ +int +log_open_udp(struct log_config *l, pool *p) +{ + ASSERT(l->host || ipa_nonzero(l->ip)); + + if (l->host && ipa_zero(l->ip)) + { + const char *err_msg; + l->ip = resolve_hostname(l->host, SK_UDP, &err_msg); + + if (ipa_zero(l->ip)) + { + cf_warn("Cannot resolve hostname '%s': %s", l->host, err_msg); + goto err0; + } + } + + sock *sk = sk_new(p); + sk->type = SK_UDP; + sk->daddr = l->ip; + sk->dport = l->port; + sk->flags = SKF_CONNECT | SKF_THREAD; + + if (sk_open(sk) < 0) + { + cf_warn("Cannot open UDP log socket: %s%#m", sk->err); + goto err1; + } + + /* Move fd from sk resource to rf resource */ + l->rf = rf_fdopen(p, sk->fd, "a"); + if (!l->rf) + goto err1; + + l->fh = rf_file(l->rf); + + sk->fd = -1; + rfree(sk); + + return 0; + +err1: + rfree(sk); +err0: + l->mask = 0; + return -1; +} + /** * log_commit - commit a log message * @class: message class information (%L_DEBUG to %L_BUG, see |lib/birdlib.h|) @@ -168,6 +218,18 @@ log_commit(int class, buffer *buf) { if (l->terminal_flag) fputs("bird: ", l->fh); + else if (l->udp_flag) + { + int pri = LOG_DAEMON | syslog_priorities[class]; + char tbuf[TM_DATETIME_BUFFER_SIZE]; + const char *hostname = (config && config->hostname) ? config->hostname : "<none>"; + const char *fmt = "%b %d %T.%6f"; + if (!tm_format_real_time(tbuf, sizeof(tbuf), fmt, current_real_time())) + strcpy(tbuf, "<error>"); + + /* Legacy RFC 3164 format, but with us precision */ + fprintf(l->fh, "<%d>%s %s %s: ", pri, tbuf, hostname, bird_name); + } else { byte tbuf[TM_DATETIME_BUFFER_SIZE]; @@ -400,7 +462,7 @@ log_switch(int initial, list *logs, const char *new_syslog_name) /* Close the logs to avoid pinning them on disk when deleted */ if (current_log_list) WALK_LIST(l, *current_log_list) - if (l->rf) + if (l->filename && l->rf) log_close(l); /* Reopen the logs, needed for 'configure undo' */ diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index ad85d1ea..c1d966f9 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -109,9 +109,11 @@ void io_loop(void); void io_log_dump(void); int sk_open_unix(struct birdsock *s, char *name); struct rfile *rf_open(struct pool *, const char *name, const char *mode); +struct rfile *rf_fdopen(pool *p, int fd, const char *mode); void *rf_file(struct rfile *f); int rf_fileno(struct rfile *f); void test_old_bird(char *path); +ip_addr resolve_hostname(const char *host, int type, const char **err_msg); /* krt.c bits */ @@ -133,6 +135,12 @@ struct log_config { off_t pos; /* Position/size of current log */ off_t limit; /* Log size limit */ int terminal_flag; + int udp_flag; + const char *host; /* UDP log dst host name */ + ip_addr ip; /* UDP log dst IP address */ + uint port; /* UDP log dst port */ }; +int log_open_udp(struct log_config *l, pool *p); + #endif |