summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS12
-rw-r--r--client/birdc.c3
-rw-r--r--client/commands.c27
-rw-r--r--conf/confbase.Y1
-rw-r--r--conf/gen_commands.m43
-rw-r--r--conf/gen_parser.m41
-rw-r--r--doc/bird.sgml36
-rw-r--r--filter/config.Y5
-rw-r--r--filter/data.h1
-rw-r--r--filter/f-inst.c65
-rw-r--r--filter/f-util.c3
-rw-r--r--filter/test.conf177
-rw-r--r--lib/ip.h5
-rw-r--r--lib/socket.h1
-rw-r--r--misc/bird.spec2
-rw-r--r--nest/config.Y34
-rw-r--r--nest/locks.c5
-rw-r--r--nest/locks.h3
-rw-r--r--nest/route.h7
-rw-r--r--nest/rt-attr.c48
-rw-r--r--nest/rt-show.c2
-rw-r--r--proto/aggregator/config.Y2
-rw-r--r--proto/babel/config.Y2
-rw-r--r--proto/bfd/bfd.c149
-rw-r--r--proto/bfd/bfd.h13
-rw-r--r--proto/bfd/config.Y30
-rw-r--r--proto/bfd/packets.c24
-rw-r--r--proto/bgp/attrs.c12
-rw-r--r--proto/bgp/bgp.c22
-rw-r--r--proto/bgp/config.Y28
-rw-r--r--proto/bgp/packets.c8
-rw-r--r--proto/mrt/config.Y5
-rw-r--r--proto/ospf/config.Y8
-rw-r--r--proto/ospf/lsalib.h4
-rw-r--r--proto/ospf/lsupd.c2
-rw-r--r--proto/rip/rip.c3
-rw-r--r--proto/rpki/config.Y3
-rw-r--r--proto/rpki/rpki.h1
-rw-r--r--proto/rpki/transport.c1
-rw-r--r--proto/static/config.Y5
-rw-r--r--sysdep/config.h2
-rw-r--r--sysdep/linux/krt-sys.h6
-rw-r--r--sysdep/linux/netlink.Y38
-rw-r--r--sysdep/linux/netlink.c94
-rw-r--r--sysdep/unix/config.Y26
-rw-r--r--sysdep/unix/io.c71
-rw-r--r--sysdep/unix/krt.c2
-rw-r--r--sysdep/unix/log.c64
-rw-r--r--sysdep/unix/unix.h8
49 files changed, 874 insertions, 200 deletions
diff --git a/NEWS b/NEWS
index d05bfe68..0637fc24 100644
--- a/NEWS
+++ b/NEWS
@@ -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 [&lt;name&gt;] {
roa6 { table &lt;tab&gt;; };
remote &lt;ip&gt; | "&lt;domain&gt;" [port &lt;num&gt;];
port &lt;num&gt;;
+ local address &lt;ip&gt;;
refresh [keep] &lt;num&gt;;
retry [keep] &lt;num&gt;;
expire [keep] &lt;num&gt;;
@@ -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");
diff --git a/lib/ip.h b/lib/ip.h
index f2650d3f..0a25d5bc 100644
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -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