From: Claudio Jeker Subject: bgpd: introduce min-version for rtr and default to version 1 To: tech@openbsd.org Date: Fri, 9 Aug 2024 22:28:20 +0200 This diff introduces a 'min-version' config option for rtr sessions. The session can only be established if the negotiated version with the cache is larger or equal to min-version. The default is 0 which is the old RFC6810 spec. Now on top of this introduce RTR_DEFAULT_VERSION which is not set to 1 for RFC8210. If people want to try ASPA and the draft-8210bis code you need to set min-version 2. Then the code will negotiate and require version 2 which includes the ASPA PDU. I lightly tested this and it seems to work fine. There is currently no support to clear a RTR session to pick up the new config. I will add something for that later. IMO defaulting to version 1 is currently the best option until the dust of RTRv2 settled. -- :wq Claudio Index: bgpctl/output.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v diff -u -p -r1.51 output.c --- bgpctl/output.c 22 May 2024 08:42:34 -0000 1.51 +++ bgpctl/output.c 28 May 2024 15:39:24 -0000 @@ -1128,8 +1128,9 @@ show_rtr(struct ctl_show_rtr *rtr) if (rtr->local_addr.aid != AID_UNSPEC) printf(" Local Address: %s\n", log_addr(&rtr->local_addr)); if (rtr->session_id != -1) - printf(" Version: %u Session ID: %d Serial #: %u\n", - rtr->version, rtr->session_id, rtr->serial); + printf(" Version: %u min %u Session ID: %d Serial #: %u\n", + rtr->version, rtr->min_version, rtr->session_id, + rtr->serial); printf(" Refresh: %u, Retry: %u, Expire: %u\n", rtr->refresh, rtr->retry, rtr->expire); Index: bgpctl/output_json.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v diff -u -p -r1.44 output_json.c --- bgpctl/output_json.c 22 May 2024 08:42:34 -0000 1.44 +++ bgpctl/output_json.c 28 May 2024 15:39:24 -0000 @@ -963,6 +963,7 @@ json_rtr(struct ctl_show_rtr *rtr) if (rtr->session_id != -1) { json_do_uint("version", rtr->version); + json_do_uint("minimal_version", rtr->min_version); json_do_uint("session_id", rtr->session_id); json_do_uint("serial", rtr->serial); } Index: bgpd/bgpd.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v diff -u -p -r1.264 bgpd.c --- bgpd/bgpd.c 15 May 2024 09:09:38 -0000 1.264 +++ bgpd/bgpd.c 9 Aug 2024 14:15:36 -0000 @@ -738,8 +738,12 @@ send_config(struct bgpd_config *conf) } free_aspatree(&conf->aspa); SIMPLEQ_FOREACH(rtr, &conf->rtrs, entry) { + struct rtr_config_msg rtrconf = { 0 }; + + strlcpy(rtrconf.descr, rtr->descr, sizeof(rtrconf.descr)); + rtrconf.min_version = rtr->min_version; if (imsg_compose(ibuf_rtr, IMSG_RECONF_RTR_CONFIG, rtr->id, - 0, -1, rtr->descr, sizeof(rtr->descr)) == -1) + 0, -1, &rtrconf, sizeof(rtrconf)) == -1) return (-1); } Index: bgpd/bgpd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v diff -u -p -r1.240 bgpd.conf.5 --- bgpd/bgpd.conf.5 24 Apr 2024 10:41:34 -0000 1.240 +++ bgpd/bgpd.conf.5 15 May 2024 08:50:55 -0000 @@ -579,6 +579,12 @@ Bind to the specific IP address before o .Em rtr server. .Pp +.Ic min-version Ar number +Require a minimal RTR version of +.Ar number . +To ensure that ASPA records are synchronised over RTR a minimal version +of 2 is required. +.Pp .It Ic port Ar number Specify the TCP destination port for the .Em rtr Index: bgpd/bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v diff -u -p -r1.493 bgpd.h --- bgpd/bgpd.h 18 May 2024 11:17:30 -0000 1.493 +++ bgpd/bgpd.h 9 Aug 2024 14:04:08 -0000 @@ -33,6 +33,8 @@ #include #define BGP_VERSION 4 +#define RTR_MAX_VERSION 2 +#define RTR_DEFAULT_VERSION 1 #define BGP_PORT 179 #define RTR_PORT 323 #define CONFFILE "/etc/bgpd.conf" @@ -570,11 +572,19 @@ struct rtr_config { struct bgpd_addr local_addr; uint32_t id; uint16_t remote_port; + uint8_t min_version; +}; + +struct rtr_config_msg { + char descr[PEER_DESCR_LEN]; + uint8_t min_version; }; struct ctl_show_rtr { char descr[PEER_DESCR_LEN]; char state[PEER_DESCR_LEN]; + char last_sent_msg[REASON_LEN]; + char last_recv_msg[REASON_LEN]; struct bgpd_addr remote_addr; struct bgpd_addr local_addr; uint32_t serial; @@ -582,12 +592,11 @@ struct ctl_show_rtr { uint32_t retry; uint32_t expire; int session_id; - uint16_t remote_port; - uint8_t version; enum rtr_error last_sent_error; enum rtr_error last_recv_error; - char last_sent_msg[REASON_LEN]; - char last_recv_msg[REASON_LEN]; + uint16_t remote_port; + uint8_t version; + uint8_t min_version; }; enum imsg_type { Index: bgpd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v diff -u -p -r1.463 parse.y --- bgpd/parse.y 22 May 2024 08:41:14 -0000 1.463 +++ bgpd/parse.y 28 May 2024 15:39:25 -0000 @@ -241,7 +241,7 @@ typedef struct { %token AS ROUTERID HOLDTIME YMIN LISTEN ON FIBUPDATE FIBPRIORITY RTABLE %token NONE UNICAST VPN RD EXPORT EXPORTTRGT IMPORTTRGT DEFAULTROUTE -%token RDE RIB EVALUATE IGNORE COMPARE RTR PORT +%token RDE RIB EVALUATE IGNORE COMPARE RTR PORT MINVERSION %token GROUP NEIGHBOR NETWORK %token EBGP IBGP %token FLOWSPEC PROTO FLAGS FRAGMENT TOS LENGTH ICMPTYPE CODE @@ -724,6 +724,14 @@ rtropt : DESCR STRING { | PORT port { currtr->remote_port = $2; } + | MINVERSION NUMBER { + if ($2 < 0 || $2 > RTR_MAX_VERSION) { + yyerror("min-version must be between %u and %u", + 0, RTR_MAX_VERSION); + YYERROR; + } + currtr->min_version = $2; + } ; conf_main : AS as4number { @@ -3578,6 +3586,7 @@ lookup(char *s) { "med", MED}, { "metric", METRIC}, { "min", YMIN}, + { "min-version", MINVERSION}, { "multihop", MULTIHOP}, { "neighbor", NEIGHBOR}, { "neighbor-as", NEIGHBORAS}, Index: bgpd/rtr.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rtr.c,v diff -u -p -r1.21 rtr.c --- bgpd/rtr.c 9 Apr 2024 12:05:07 -0000 1.21 +++ bgpd/rtr.c 9 Aug 2024 14:20:46 -0000 @@ -309,7 +309,7 @@ rtr_dispatch_imsg_parent(struct imsgbuf struct imsg imsg; struct bgpd_config tconf; struct roa roa; - char descr[PEER_DESCR_LEN]; + struct rtr_config_msg rtrconf; struct rtr_session *rs; uint32_t rtrid; int n, fd; @@ -395,13 +395,14 @@ rtr_dispatch_imsg_parent(struct imsgbuf aspa = NULL; break; case IMSG_RECONF_RTR_CONFIG: - if (imsg_get_data(&imsg, descr, sizeof(descr)) == -1) + if (imsg_get_data(&imsg, &rtrconf, + sizeof(rtrconf)) == -1) fatal("imsg_get_data"); rs = rtr_get(rtrid); if (rs == NULL) - rtr_new(rtrid, descr); + rtr_new(rtrid, &rtrconf); else - rtr_config_keep(rs); + rtr_config_keep(rs, &rtrconf); break; case IMSG_RECONF_DRAIN: imsg_compose(ibuf_main, IMSG_RECONF_DRAIN, 0, 0, Index: bgpd/rtr_proto.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rtr_proto.c,v diff -u -p -r1.37 rtr_proto.c --- bgpd/rtr_proto.c 9 Aug 2024 14:00:48 -0000 1.37 +++ bgpd/rtr_proto.c 9 Aug 2024 14:22:35 -0000 @@ -42,7 +42,6 @@ struct rtr_header { uint32_t length; } __packed; -#define RTR_MAX_VERSION 2 #define RTR_MAX_PDU_SIZE 49152 /* XXX < IBUF_READ_SIZE */ #define RTR_MAX_PDU_ERROR_SIZE 256 #define RTR_DEFAULT_REFRESH 3600 @@ -213,6 +212,9 @@ struct rtr_session { char last_recv_msg[REASON_LEN]; uint8_t version; uint8_t prev_version; + uint8_t min_version; + uint8_t errored; + }; TAILQ_HEAD(, rtr_session) rtrs = TAILQ_HEAD_INITIALIZER(rtrs); @@ -259,6 +261,14 @@ log_rtr_type(enum rtr_pdu_type type) } }; +static uint8_t +rtr_max_session_version(struct rtr_session *rs) +{ + if (rs->min_version > RTR_DEFAULT_VERSION) + return rs->min_version; + return RTR_DEFAULT_VERSION; +} + static void rtr_reset_cache(struct rtr_session *rs) { @@ -1084,13 +1094,14 @@ rtr_fsm(struct rtr_session *rs, enum rtr switch (event) { case RTR_EVNT_UNSUPP_PROTO_VERSION: - if (rs->prev_version == rs->version) { + if (rs->prev_version == rs->version || + rs->version < rs->min_version) { /* * Can't downgrade anymore, fail connection. * RFC requires sending the error with the * highest supported version number. */ - rs->version = RTR_MAX_VERSION; + rs->version = rtr_max_session_version(rs); rtr_send_error(rs, NULL, UNSUPP_PROTOCOL_VERS, "negotiation failed"); return; @@ -1114,8 +1125,13 @@ rtr_fsm(struct rtr_session *rs, enum rtr rs->fd = -1; } /* try to reopen session */ - timer_set(&rs->timers, Timer_Rtr_Retry, - arc4random_uniform(10)); + if (!rs->errored) + timer_set(&rs->timers, Timer_Rtr_Retry, + arc4random_uniform(10)); + else + timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry); + + rs->errored = 1; /* * A close event during version negotiation needs to remain * in the negotiation state else the same error will happen @@ -1190,6 +1206,7 @@ rtr_fsm(struct rtr_session *rs, enum rtr rtr_sem_release(rs->active_lock); rtr_recalc(); rs->active_lock = 0; + rs->errored = 0; /* clear the last errors */ rs->last_sent_error = NO_ERROR; rs->last_recv_error = NO_ERROR; @@ -1371,12 +1388,12 @@ rtr_poll_events(struct pollfd *pfds, siz } struct rtr_session * -rtr_new(uint32_t id, char *descr) +rtr_new(uint32_t id, struct rtr_config_msg *conf) { struct rtr_session *rs; if ((rs = calloc(1, sizeof(*rs))) == NULL) - fatal("RTR session %s", descr); + fatal("RTR session %s", conf->descr); RB_INIT(&rs->roa_set); RB_INIT(&rs->aspa); @@ -1384,11 +1401,12 @@ rtr_new(uint32_t id, char *descr) TAILQ_INIT(&rs->timers); msgbuf_init(&rs->w); - strlcpy(rs->descr, descr, sizeof(rs->descr)); + strlcpy(rs->descr, conf->descr, sizeof(rs->descr)); rs->id = id; rs->session_id = -1; - rs->version = RTR_MAX_VERSION; - rs->prev_version = RTR_MAX_VERSION; + rs->min_version = conf->min_version; /* must be set before version */ + rs->version = rtr_max_session_version(rs); + rs->prev_version = rtr_max_session_version(rs); rs->refresh = RTR_DEFAULT_REFRESH; rs->retry = RTR_DEFAULT_RETRY; rs->expire = RTR_DEFAULT_EXPIRE; @@ -1441,8 +1459,8 @@ rtr_open(struct rtr_session *rs, int fd) } if (rs->state == RTR_STATE_CLOSED) { - rs->version = RTR_MAX_VERSION; - rs->prev_version = RTR_MAX_VERSION; + rs->version = rtr_max_session_version(rs); + rs->prev_version = rtr_max_session_version(rs); } rs->fd = rs->w.fd = fd; @@ -1471,8 +1489,10 @@ rtr_config_merge(void) } void -rtr_config_keep(struct rtr_session *rs) +rtr_config_keep(struct rtr_session *rs, struct rtr_config_msg *conf) { + strlcpy(rs->descr, conf->descr, sizeof(rs->descr)); + rs->min_version = conf->min_version; rs->reconf_action = RECONF_KEEP; } @@ -1523,6 +1543,7 @@ rtr_show(struct rtr_session *rs, pid_t p /* descr, remote_addr, local_addr and remote_port set by parent */ msg.version = rs->version; + msg.min_version = rs->min_version; msg.serial = rs->serial; msg.refresh = rs->refresh; msg.retry = rs->retry; Index: bgpd/session.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.h,v diff -u -p -r1.170 session.h --- bgpd/session.h 18 May 2024 11:17:30 -0000 1.170 +++ bgpd/session.h 22 May 2024 08:37:14 -0000 @@ -296,13 +296,14 @@ struct rtr_session; size_t rtr_count(void); void rtr_check_events(struct pollfd *, size_t); size_t rtr_poll_events(struct pollfd *, size_t, time_t *); -struct rtr_session *rtr_new(uint32_t, char *); +struct rtr_session *rtr_new(uint32_t, struct rtr_config_msg *); struct rtr_session *rtr_get(uint32_t); void rtr_free(struct rtr_session *); void rtr_open(struct rtr_session *, int); void rtr_config_prep(void); void rtr_config_merge(void); -void rtr_config_keep(struct rtr_session *); +void rtr_config_keep(struct rtr_session *, + struct rtr_config_msg *); void rtr_roa_merge(struct roa_tree *); void rtr_aspa_merge(struct aspa_tree *); void rtr_shutdown(void);