From 83072fb646af9112a9c2b682065739e81210945b Mon Sep 17 00:00:00 2001 From: claudio Date: Thu, 9 Mar 2023 17:21:21 +0000 Subject: [PATCH] Implement ASPA support in RTR by following draft-ietf-sidrops-8210bis-10. In rtr.c renamed rtr_aspa_merge_set() to rtr_aspa_insert() and move it close to rtr_roa_insert(). In rtr_proto.c most complexity comes from the version negotiation. The ASPA parser is reasonably streight forward. The version negotiation is fragile but that is mostly because of the protocol specification and the fact that RTR cache daemons sometimes fail to send errors. OK tb@ --- usr.sbin/bgpd/bgpd.h | 7 +- usr.sbin/bgpd/rtr.c | 136 +++++++++--------- usr.sbin/bgpd/rtr_proto.c | 283 ++++++++++++++++++++++++++++++++------ usr.sbin/bgpd/session.h | 6 +- 4 files changed, 318 insertions(+), 114 deletions(-) diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 1cef0670cae..af2f67509e2 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.462 2023/03/09 13:12:19 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.463 2023/03/09 17:21:21 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -538,7 +538,7 @@ struct rtr_config { struct bgpd_addr remote_addr; struct bgpd_addr local_addr; uint32_t id; - in_addr_t remote_port; + uint16_t remote_port; }; struct ctl_show_rtr { @@ -550,7 +550,8 @@ struct ctl_show_rtr { uint32_t retry; uint32_t expire; int session_id; - in_addr_t remote_port; + 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]; diff --git a/usr.sbin/bgpd/rtr.c b/usr.sbin/bgpd/rtr.c index bd1c2ca6b46..178679ef918 100644 --- a/usr.sbin/bgpd/rtr.c +++ b/usr.sbin/bgpd/rtr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtr.c,v 1.11 2023/01/20 09:54:43 claudio Exp $ */ +/* $OpenBSD: rtr.c,v 1.12 2023/03/09 17:21:21 claudio Exp $ */ /* * Copyright (c) 2020 Claudio Jeker @@ -100,7 +100,7 @@ rtr_expire_aspa(time_t now) } void -roa_insert(struct roa_tree *rt, struct roa *in) +rtr_roa_insert(struct roa_tree *rt, struct roa *in) { struct roa *roa; @@ -112,6 +112,70 @@ roa_insert(struct roa_tree *rt, struct roa *in) free(roa); } +/* + * Add an asnum to the aspa_set. The aspa_set is sorted by asnum. + * The aid is altered to AID_UNSPEC (match for both v4 and v6) if + * the current aid and the one passed do not match. + */ +static void +aspa_set_entry(struct aspa_set *aspa, uint32_t asnum, uint8_t aid) +{ + uint32_t i, num, *newtas; + uint8_t *newtasaid; + + if (aid != AID_UNSPEC && aid != AID_INET && aid != AID_INET6) + fatalx("aspa set with invalid AFI %s", aid2str(aid)); + + for (i = 0; i < aspa->num; i++) { + if (asnum < aspa->tas[i]) + break; + if (asnum == aspa->tas[i]) { + if (aspa->tas_aid[i] != aid) + aspa->tas_aid[i] = AID_UNSPEC; + return; + } + } + + num = aspa->num + 1; + newtas = recallocarray(aspa->tas, aspa->num, num, sizeof(uint32_t)); + newtasaid = recallocarray(aspa->tas_aid, aspa->num, num, 1); + if (newtas == NULL || newtasaid == NULL) + fatal("aspa_set merge"); + + if (i < aspa->num) { + memmove(newtas + i + 1, newtas + i, + (aspa->num - i) * sizeof(uint32_t)); + memmove(newtasaid + i + 1, newtasaid + i, (aspa->num - i)); + } + newtas[i] = asnum; + newtasaid[i] = aid; + + aspa->num = num; + aspa->tas = newtas; + aspa->tas_aid = newtasaid; +} + +/* + * Insert and merge an aspa_set into the aspa_tree at. + */ +void +rtr_aspa_insert(struct aspa_tree *at, struct aspa_set *mergeset) +{ + struct aspa_set *aspa, needle = { .as = mergeset->as }; + uint32_t i; + + aspa = RB_FIND(aspa_tree, at, &needle); + if (aspa == NULL) { + if ((aspa = calloc(1, sizeof(*aspa))) == NULL) + fatal("aspa insert"); + aspa->as = mergeset->as; + RB_INSERT(aspa_tree, at, aspa); + } + + for (i = 0; i < mergeset->num; i++) + aspa_set_entry(aspa, mergeset->tas[i], mergeset->tas_aid[i]); +} + void rtr_main(int debug, int verbose) { @@ -294,7 +358,7 @@ rtr_dispatch_imsg_parent(struct imsgbuf *ibuf) if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(*roa)) fatalx("IMSG_RECONF_ROA_ITEM bad len"); - roa_insert(&nconf->roa, imsg.data); + rtr_roa_insert(&nconf->roa, imsg.data); break; case IMSG_RECONF_ASPA: if (imsg.hdr.len - IMSG_HEADER_SIZE != @@ -417,67 +481,6 @@ rtr_imsg_compose(int type, uint32_t id, pid_t pid, void *data, size_t datalen) imsg_compose(ibuf_main, type, id, pid, -1, data, datalen); } -/* - * Add an asnum to the aspa_set. The aspa_set is sorted by asnum. - * The aid is altered into a bitmask to simplify the merge of entries - * that just use a different aid. - */ -static void -aspa_set_entry(struct aspa_set *aspa, uint32_t asnum, uint8_t aid) -{ - uint32_t i, num, *newtas; - uint8_t *newtasaid; - - if (aid != AID_UNSPEC && aid != AID_INET && aid != AID_INET6) - fatalx("aspa set with invalid AFI %s", aid2str(aid)); - - for (i = 0; i < aspa->num; i++) { - if (asnum < aspa->tas[i] || aspa->tas[i] == 0) - break; - if (asnum == aspa->tas[i]) { - if (aspa->tas_aid[i] != aid) - aspa->tas_aid[i] = AID_UNSPEC; - return; - } - } - - num = aspa->num + 1; - newtas = recallocarray(aspa->tas, aspa->num, num, sizeof(uint32_t)); - newtasaid = recallocarray(aspa->tas_aid, aspa->num, num, 1); - if (newtas == NULL || newtasaid == NULL) - fatal("aspa_set merge"); - - if (i < aspa->num) { - memmove(newtas + i + 1, newtas + i, - (aspa->num - i) * sizeof(uint32_t)); - memmove(newtasaid + i + 1, newtasaid + i, (aspa->num - i)); - } - newtas[i] = asnum; - newtasaid[i] = aid; - - aspa->num = num; - aspa->tas = newtas; - aspa->tas_aid = newtasaid; -} - -static void -rtr_aspa_merge_set(struct aspa_tree *a, struct aspa_set *mergeset) -{ - struct aspa_set *aspa, needle = { .as = mergeset->as }; - uint32_t i; - - aspa = RB_FIND(aspa_tree, a, &needle); - if (aspa == NULL) { - if ((aspa = calloc(1, sizeof(*aspa))) == NULL) - fatal("aspa insert"); - aspa->as = mergeset->as; - RB_INSERT(aspa_tree, a, aspa); - } - - for (i = 0; i < mergeset->num; i++) - aspa_set_entry(aspa, mergeset->tas[i], mergeset->tas_aid[i]); -} - /* * Compress aspa_set tas_aid into the bitfield used by the RDE. * Returns the size of tas and tas_aid bitfield required for this aspa_set. @@ -543,7 +546,7 @@ rtr_recalc(void) RB_INIT(&at); RB_FOREACH(roa, roa_tree, &conf->roa) - roa_insert(&rt, roa); + rtr_roa_insert(&rt, roa); rtr_roa_merge(&rt); imsg_compose(ibuf_rde, IMSG_RECONF_ROA_SET, 0, 0, -1, NULL, 0); @@ -554,7 +557,8 @@ rtr_recalc(void) free_roatree(&rt); RB_FOREACH(aspa, aspa_tree, &conf->aspa) - rtr_aspa_merge_set(&at, aspa); + rtr_aspa_insert(&at, aspa); + rtr_aspa_merge(&at); RB_FOREACH(aspa, aspa_tree, &at) { ap.datasize += rtr_aspa_set_prep(aspa); diff --git a/usr.sbin/bgpd/rtr_proto.c b/usr.sbin/bgpd/rtr_proto.c index d7ff4da09b8..4f6c48adf47 100644 --- a/usr.sbin/bgpd/rtr_proto.c +++ b/usr.sbin/bgpd/rtr_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtr_proto.c,v 1.12 2023/02/02 20:31:37 job Exp $ */ +/* $OpenBSD: rtr_proto.c,v 1.13 2023/03/09 17:21:21 claudio Exp $ */ /* * Copyright (c) 2020 Claudio Jeker @@ -35,6 +35,7 @@ struct rtr_header { uint32_t length; }; +#define RTR_MAX_VERSION 2 #define RTR_MAX_LEN 2048 #define RTR_DEFAULT_REFRESH 3600 #define RTR_DEFAULT_RETRY 600 @@ -51,6 +52,7 @@ enum rtr_pdu_type { CACHE_RESET = 8, ROUTER_KEY = 9, ERROR_REPORT = 10, + ASPA = 11, }; #define FLAG_ANNOUNCE 0x1 @@ -73,6 +75,16 @@ struct rtr_ipv6 { uint32_t asnum; }; +#define FLAG_AFI_V6 0x1 +#define FLAG_AFI_MASK FLAG_AFI_V6 +struct rtr_aspa { + uint8_t flags; + uint8_t afi_flags; + uint16_t cnt; + uint32_t cas; + uint32_t spas[0]; +}; + struct rtr_endofdata { uint32_t serial; uint32_t refresh; @@ -94,6 +106,7 @@ enum rtr_event { RTR_EVNT_CACHE_RESET, RTR_EVNT_NO_DATA, RTR_EVNT_RESET_AND_CLOSE, + RTR_EVNT_UNSUPP_PROTO_VERSION, }; static const char *rtr_eventnames[] = { @@ -110,6 +123,7 @@ static const char *rtr_eventnames[] = { "cache reset received", "no data", "connection closed with reset", + "unsupported protocol version", }; enum rtr_state { @@ -117,19 +131,23 @@ enum rtr_state { RTR_STATE_ERROR, RTR_STATE_IDLE, RTR_STATE_ACTIVE, + RTR_STATE_NEGOTIATION, }; static const char *rtr_statenames[] = { "closed", "error", "idle", - "active" + "active", + "negotiation", }; struct rtr_session { TAILQ_ENTRY(rtr_session) entry; char descr[PEER_DESCR_LEN]; struct roa_tree roa_set; + struct aspa_tree aspa_v4; + struct aspa_tree aspa_v6; struct ibuf_read r; struct msgbuf w; struct timer_head timers; @@ -146,6 +164,7 @@ struct rtr_session { enum rtr_error last_recv_error; char last_sent_msg[REASON_LEN]; char last_recv_msg[REASON_LEN]; + uint8_t version; }; TAILQ_HEAD(, rtr_session) rtrs = TAILQ_HEAD_INITIALIZER(rtrs); @@ -184,6 +203,8 @@ log_rtr_type(enum rtr_pdu_type type) return "router key"; case ERROR_REPORT: return "error report"; + case ASPA: + return "aspa pdu"; default: snprintf(buf, sizeof(buf), "unknown %u", type); return buf; @@ -191,7 +212,8 @@ log_rtr_type(enum rtr_pdu_type type) }; static struct ibuf * -rtr_newmsg(enum rtr_pdu_type type, uint32_t len, uint16_t session_id) +rtr_newmsg(struct rtr_session *rs, enum rtr_pdu_type type, uint32_t len, + uint16_t session_id) { struct ibuf *buf; struct rtr_header rh; @@ -205,7 +227,7 @@ rtr_newmsg(enum rtr_pdu_type type, uint32_t len, uint16_t session_id) return NULL; memset(&rh, 0, sizeof(rh)); - rh.version = 1; + rh.version = rs->version; rh.type = type; rh.session_id = htons(session_id); rh.length = htonl(len); @@ -236,7 +258,8 @@ rtr_send_error(struct rtr_session *rs, enum rtr_error err, char *msg, rtr_fsm(rs, RTR_EVNT_SEND_ERROR); - buf = rtr_newmsg(ERROR_REPORT, 2 * sizeof(hdrlen) + len + mlen, err); + buf = rtr_newmsg(rs, ERROR_REPORT, 2 * sizeof(hdrlen) + len + mlen, + err); if (buf == NULL) { log_warn("rtr %s: send error report", log_rtr(rs)); return; @@ -260,7 +283,7 @@ rtr_reset_query(struct rtr_session *rs) { struct ibuf *buf; - buf = rtr_newmsg(RESET_QUERY, 0, 0); + buf = rtr_newmsg(rs, RESET_QUERY, 0, 0); if (buf == NULL) { log_warn("rtr %s: send reset query", log_rtr(rs)); rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0); @@ -275,7 +298,7 @@ rtr_serial_query(struct rtr_session *rs) struct ibuf *buf; uint32_t s; - buf = rtr_newmsg(SERIAL_QUERY, sizeof(s), rs->session_id); + buf = rtr_newmsg(rs, SERIAL_QUERY, sizeof(s), rs->session_id); if (buf == NULL) { log_warn("rtr %s: send serial query", log_rtr(rs)); rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0); @@ -304,9 +327,10 @@ rtr_parse_header(struct rtr_session *rs, void *buf, memcpy(&rh, buf, sizeof(rh)); - if (rh.version != 1) { - log_warnx("rtr %s: received message with unsupported version", - log_rtr(rs)); + if (rh.version != rs->version && rh.type != ERROR_REPORT) { + badversion: + log_warnx("rtr %s: received %s message: unexpected version %d", + log_rtr(rs), log_rtr_type(rh.type), rh.version); rtr_send_error(rs, UNEXP_PROTOCOL_VERS, NULL, &rh, sizeof(rh)); return -1; } @@ -314,7 +338,7 @@ rtr_parse_header(struct rtr_session *rs, void *buf, *msgtype = rh.type; *msglen = ntohl(rh.length); - switch (*msgtype) { + switch (rh.type) { case SERIAL_NOTIFY: session_id = rs->session_id; len = 12; @@ -343,19 +367,23 @@ rtr_parse_header(struct rtr_session *rs, void *buf, len = 8; break; case ROUTER_KEY: + if (rs->version < 1) + goto badversion; len = 36; /* XXX probably too small, but we ignore it */ /* FALLTHROUGH */ case ERROR_REPORT: if (*msglen > RTR_MAX_LEN) { + toobig: log_warnx("rtr %s: received %s: msg too big: %zu byte", - log_rtr(rs), log_rtr_type(*msgtype), *msglen); + log_rtr(rs), log_rtr_type(rh.type), *msglen); rtr_send_error(rs, CORRUPT_DATA, "too big", &rh, sizeof(rh)); return -1; } if (*msglen < len) { + toosmall: log_warnx("rtr %s: received %s: msg too small: " - "%zu byte", log_rtr(rs), log_rtr_type(*msgtype), + "%zu byte", log_rtr(rs), log_rtr_type(rh.type), *msglen); rtr_send_error(rs, CORRUPT_DATA, "too small", &rh, sizeof(rh)); @@ -366,16 +394,28 @@ rtr_parse_header(struct rtr_session *rs, void *buf, * use the field for different things. */ return 0; + case ASPA: + if (rs->version < 2) + goto badversion; + session_id = 0; + /* unlike all other messages ASPA is variable sized */ + if (*msglen > RTR_MAX_LEN) + goto toobig; + if (*msglen < sizeof(struct rtr_aspa)) + goto toosmall; + /* len must be a multiple of 4 */ + len = *msglen & ~0x3; + break; default: - log_warnx("rtr %s: received unknown message: type %u", - log_rtr(rs), *msgtype); + log_warnx("rtr %s: received unknown message: type %s", + log_rtr(rs), log_rtr_type(rh.type)); rtr_send_error(rs, UNSUPP_PDU_TYPE, NULL, &rh, sizeof(rh)); return -1; } if (len != *msglen) { log_warnx("rtr %s: received %s: illegal len: %zu byte not %u", - log_rtr(rs), log_rtr_type(*msgtype), *msglen, len); + log_rtr(rs), log_rtr_type(rh.type), *msglen, len); rtr_send_error(rs, CORRUPT_DATA, "bad length", &rh, sizeof(rh)); return -1; @@ -383,11 +423,11 @@ rtr_parse_header(struct rtr_session *rs, void *buf, if (session_id != ntohs(rh.session_id)) { /* ignore SERIAL_NOTIFY during startup */ - if (rs->session_id == -1 && *msgtype == SERIAL_NOTIFY) + if (rs->session_id == -1 && rh.type == SERIAL_NOTIFY) return 0; log_warnx("rtr %s: received %s: bad session_id: %d != %d", - log_rtr(rs), log_rtr_type(*msgtype), ntohs(rh.session_id), + log_rtr(rs), log_rtr_type(rh.type), ntohs(rh.session_id), session_id); rtr_send_error(rs, CORRUPT_DATA, "bad session_id", &rh, sizeof(rh)); @@ -413,7 +453,7 @@ rtr_parse_notify(struct rtr_session *rs, uint8_t *buf, size_t len) static int rtr_parse_cache_response(struct rtr_session *rs, uint8_t *buf, size_t len) { - if (rs->state != RTR_STATE_IDLE) { + if (rs->state != RTR_STATE_IDLE && rs->state != RTR_STATE_NEGOTIATION) { log_warnx("rtr %s: received %s: out of context", log_rtr(rs), log_rtr_type(CACHE_RESPONSE)); return -1; @@ -560,6 +600,98 @@ rtr_parse_ipv6_prefix(struct rtr_session *rs, uint8_t *buf, size_t len) return 0; } +static int +rtr_parse_aspa(struct rtr_session *rs, uint8_t *buf, size_t len) +{ + struct rtr_aspa rtr_aspa; + struct aspa_tree *aspatree; + struct aspa_set *aspa, *a; + size_t offset; + uint16_t cnt, i; + uint8_t aid; + + memcpy(&rtr_aspa, buf + sizeof(struct rtr_header), sizeof(rtr_aspa)); + offset = sizeof(struct rtr_header) + sizeof(rtr_aspa); + cnt = ntohs(rtr_aspa.cnt); + if (len != offset + cnt * sizeof(uint32_t)) { + log_warnx("rtr %s: received %s: bad pdu len", + log_rtr(rs), log_rtr_type(ASPA)); + rtr_send_error(rs, CORRUPT_DATA, "bad len", buf, len); + return -1; + } + + if (rs->state != RTR_STATE_ACTIVE) { + log_warnx("rtr %s: received %s: out of context", + log_rtr(rs), log_rtr_type(ASPA)); + rtr_send_error(rs, CORRUPT_DATA, NULL, buf, len); + return -1; + } + + if (rtr_aspa.afi_flags & FLAG_AFI_V6) { + aid = AID_INET6; + aspatree = &rs->aspa_v6; + } else { + aid = AID_INET; + aspatree = &rs->aspa_v4; + } + + /* create aspa_set entry from the rtr aspa pdu */ + if ((aspa = calloc(1, sizeof(*aspa))) == NULL) { + log_warn("rtr %s: received %s", + log_rtr(rs), log_rtr_type(ASPA)); + rtr_send_error(rs, INTERNAL_ERROR, "out of memory", NULL, 0); + return -1; + } + aspa->as = ntohl(rtr_aspa.cas); + aspa->num = cnt; + if (cnt > 0) { + if ((aspa->tas = calloc(cnt, sizeof(uint32_t))) == NULL || + (aspa->tas_aid = calloc(cnt, 1)) == NULL) { + free_aspa(aspa); + log_warn("rtr %s: received %s", + log_rtr(rs), log_rtr_type(ASPA)); + rtr_send_error(rs, INTERNAL_ERROR, "out of memory", + NULL, 0); + return -1; + } + for (i = 0; i < cnt; i++) { + aspa->tas[i] = ntohl(rtr_aspa.spas[i]); + aspa->tas_aid[i] = aid; + } + } + + if (rtr_aspa.flags & FLAG_ANNOUNCE) { + a = RB_INSERT(aspa_tree, aspatree, aspa); + if (a != NULL) { + RB_REMOVE(aspa_tree, aspatree, a); + free_aspa(a); + + if (RB_INSERT(aspa_tree, aspatree, aspa) != NULL) { + log_warnx("rtr %s: received %s: corrupt tree", + log_rtr(rs), log_rtr_type(ASPA)); + rtr_send_error(rs, INTERNAL_ERROR, + "corrupt aspa tree", NULL, 0); + free_aspa(aspa); + return -1; + } + } + } else { + a = RB_FIND(aspa_tree, aspatree, aspa); + if (a == NULL) { + log_warnx("rtr %s: received %s: unknown withdrawal", + log_rtr(rs), log_rtr_type(ASPA)); + rtr_send_error(rs, UNK_REC_WDRAWL, NULL, buf, len); + free_aspa(aspa); + return -1; + } + RB_REMOVE(aspa_tree, aspatree, a); + free_aspa(a); + free_aspa(aspa); + } + + return 0; +} + static int rtr_parse_end_of_data(struct rtr_session *rs, uint8_t *buf, size_t len) { @@ -675,22 +807,23 @@ rtr_parse_error(struct rtr_session *rs, uint8_t *buf, size_t len) if (errcode == NO_DATA_AVAILABLE) { rtr_fsm(rs, RTR_EVNT_NO_DATA); - } else { - rtr_fsm(rs, RTR_EVNT_RESET_AND_CLOSE); - rs->last_recv_error = errcode; - if (str) - strlcpy(rs->last_recv_msg, str, - sizeof(rs->last_recv_msg)); - else - memset(rs->last_recv_msg, 0, - sizeof(rs->last_recv_msg)); - free(str); - return -1; + return 0; } - free(str); + if (errcode == UNSUPP_PROTOCOL_VERS) + rtr_fsm(rs, RTR_EVNT_UNSUPP_PROTO_VERSION); + else + rtr_fsm(rs, RTR_EVNT_RESET_AND_CLOSE); + rs->last_recv_error = errcode; + if (str) + strlcpy(rs->last_recv_msg, str, + sizeof(rs->last_recv_msg)); + else + memset(rs->last_recv_msg, 0, + sizeof(rs->last_recv_msg)); - return 0; + free(str); + return -1; } /* @@ -767,6 +900,11 @@ rtr_process_msg(struct rtr_session *rs) /* no need to send back an error */ return; break; + case ASPA: + if (rtr_parse_aspa(rs, rptr, msglen) == -1) { + return; + } + break; default: log_warnx("rtr %s: received %s: unexpected pdu type", log_rtr(rs), log_rtr_type(msgtype)); @@ -790,6 +928,36 @@ rtr_fsm(struct rtr_session *rs, enum rtr_event event) enum rtr_state prev_state = rs->state; switch (event) { + case RTR_EVNT_UNSUPP_PROTO_VERSION: + if (rs->state == RTR_STATE_NEGOTIATION) { + if (rs->version > 0) + rs->version--; + else { + /* + * can't downgrade anymore, fail connection + * RFC requires to send the error with our + * highest version number. + */ + rs->version = RTR_MAX_VERSION; + log_warnx("rtr %s: version negotiation failed", + log_rtr(rs)); + rtr_send_error(rs, UNSUPP_PROTOCOL_VERS, + NULL, NULL, 0); + return; + } + + /* flush buffers */ + msgbuf_clear(&rs->w); + rs->r.wpos = 0; + close(rs->fd); + rs->fd = -1; + + /* retry connection with lower version */ + timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry); + rtr_imsg_compose(IMSG_SOCKET_CONN, rs->id, 0, NULL, 0); + break; + } + /* FALLTHROUGH */ case RTR_EVNT_RESET_AND_CLOSE: rs->state = RTR_STATE_ERROR; /* FALLTHROUGH */ @@ -798,6 +966,8 @@ rtr_fsm(struct rtr_session *rs, enum rtr_event event) /* reset session */ rs->session_id = -1; free_roatree(&rs->roa_set); + free_aspatree(&rs->aspa_v4); + free_aspatree(&rs->aspa_v6); rtr_recalc(); } if (rs->state != RTR_STATE_CLOSED) { @@ -844,12 +1014,15 @@ rtr_fsm(struct rtr_session *rs, enum rtr_event event) break; case RTR_EVNT_TIMER_EXPIRE: free_roatree(&rs->roa_set); + free_aspatree(&rs->aspa_v4); + free_aspatree(&rs->aspa_v6); rtr_recalc(); break; case RTR_EVNT_CACHE_RESPONSE: rs->state = RTR_STATE_ACTIVE; timer_stop(&rs->timers, Timer_Rtr_Refresh); timer_stop(&rs->timers, Timer_Rtr_Retry); + /* XXX start timer to limit active time */ break; case RTR_EVNT_END_OF_DATA: /* start refresh and expire timers */ @@ -862,6 +1035,8 @@ rtr_fsm(struct rtr_session *rs, enum rtr_event event) /* reset session and retry after a quick wait */ rs->session_id = -1; free_roatree(&rs->roa_set); + free_aspatree(&rs->aspa_v4); + free_aspatree(&rs->aspa_v6); rtr_recalc(); timer_set(&rs->timers, Timer_Rtr_Retry, arc4random_uniform(10)); @@ -871,6 +1046,7 @@ rtr_fsm(struct rtr_session *rs, enum rtr_event event) timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry); /* stop refresh timer just to be sure */ timer_stop(&rs->timers, Timer_Rtr_Refresh); + rs->state = RTR_STATE_IDLE; break; case RTR_EVNT_SEND_ERROR: rs->state = RTR_STATE_ERROR; @@ -904,14 +1080,14 @@ rtr_dispatch_msg(struct pollfd *pfd, struct rtr_session *rs) return; } if (pfd->revents & POLLOUT && rs->w.queued) { - if ((error = ibuf_write(&rs->w)) <= 0 && errno != EAGAIN) { - if (error == 0) - log_warnx("rtr %s: Connection closed", - log_rtr(rs)); - else if (error == -1) + if ((error = ibuf_write(&rs->w)) == -1) { + if (errno != EAGAIN) { log_warn("rtr %s: write error", log_rtr(rs)); + rtr_fsm(rs, RTR_EVNT_CON_CLOSE); + } + } + if (error == 0) { rtr_fsm(rs, RTR_EVNT_CON_CLOSE); - return; } if (rs->w.queued == 0 && rs->state == RTR_STATE_ERROR) rtr_fsm(rs, RTR_EVNT_CON_CLOSE); @@ -926,7 +1102,6 @@ rtr_dispatch_msg(struct pollfd *pfd, struct rtr_session *rs) return; } if (n == 0) { - log_warnx("rtr %s: Connection closed", log_rtr(rs)); rtr_fsm(rs, RTR_EVNT_CON_CLOSE); return; } @@ -1037,12 +1212,15 @@ rtr_new(uint32_t id, char *descr) fatal("RTR session %s", descr); RB_INIT(&rs->roa_set); + RB_INIT(&rs->aspa_v4); + RB_INIT(&rs->aspa_v6); TAILQ_INIT(&rs->timers); msgbuf_init(&rs->w); strlcpy(rs->descr, descr, sizeof(rs->descr)); rs->id = id; rs->session_id = -1; + rs->version = RTR_MAX_VERSION; rs->refresh = RTR_DEFAULT_REFRESH; rs->retry = RTR_DEFAULT_RETRY; rs->expire = RTR_DEFAULT_EXPIRE; @@ -1081,21 +1259,25 @@ rtr_free(struct rtr_session *rs) rtr_fsm(rs, RTR_EVNT_CON_CLOSE); timer_remove_all(&rs->timers); free_roatree(&rs->roa_set); + free_aspatree(&rs->aspa_v4); + free_aspatree(&rs->aspa_v6); free(rs); } void rtr_open(struct rtr_session *rs, int fd) { - if (rs->state != RTR_STATE_CLOSED) { + if (rs->state != RTR_STATE_CLOSED && + rs->state != RTR_STATE_NEGOTIATION) { log_warnx("rtr %s: bad session state", log_rtr(rs)); rtr_fsm(rs, RTR_EVNT_CON_CLOSE); } - log_debug("rtr %s: connection opened", log_rtr(rs)); + if (rs->state == RTR_STATE_CLOSED) + rs->version = RTR_MAX_VERSION; rs->fd = rs->w.fd = fd; - rs->state = RTR_STATE_IDLE; + rs->state = RTR_STATE_NEGOTIATION; rtr_fsm(rs, RTR_EVNT_CON_OPEN); } @@ -1134,7 +1316,21 @@ rtr_roa_merge(struct roa_tree *rt) TAILQ_FOREACH(rs, &rtrs, entry) { RB_FOREACH(roa, roa_tree, &rs->roa_set) - roa_insert(rt, roa); + rtr_roa_insert(rt, roa); + } +} + +void +rtr_aspa_merge(struct aspa_tree *at) +{ + struct rtr_session *rs; + struct aspa_set *aspa; + + TAILQ_FOREACH(rs, &rtrs, entry) { + RB_FOREACH(aspa, aspa_tree, &rs->aspa_v4) + rtr_aspa_insert(at, aspa); + RB_FOREACH(aspa, aspa_tree, &rs->aspa_v6) + rtr_aspa_insert(at, aspa); } } @@ -1158,6 +1354,7 @@ rtr_show(struct rtr_session *rs, pid_t pid) memset(&msg, 0, sizeof(msg)); /* descr, remote_addr, local_addr and remote_port set by parent */ + msg.version = rs->version; msg.serial = rs->serial; msg.refresh = rs->refresh; msg.retry = rs->retry; diff --git a/usr.sbin/bgpd/session.h b/usr.sbin/bgpd/session.h index 26fc2503e4e..c9e6b2164ba 100644 --- a/usr.sbin/bgpd/session.h +++ b/usr.sbin/bgpd/session.h @@ -1,4 +1,4 @@ -/* $OpenBSD: session.h,v 1.160 2023/03/09 13:12:19 claudio Exp $ */ +/* $OpenBSD: session.h,v 1.161 2023/03/09 17:21:21 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -329,11 +329,13 @@ void rtr_config_prep(void); void rtr_config_merge(void); void rtr_config_keep(struct rtr_session *); void rtr_roa_merge(struct roa_tree *); +void rtr_aspa_merge(struct aspa_tree *); void rtr_shutdown(void); void rtr_show(struct rtr_session *, pid_t); /* rtr.c */ -void roa_insert(struct roa_tree *, struct roa *); +void rtr_roa_insert(struct roa_tree *, struct roa *); +void rtr_aspa_insert(struct aspa_tree *, struct aspa_set *); void rtr_main(int, int); void rtr_imsg_compose(int, uint32_t, pid_t, void *, size_t); void rtr_recalc(void); -- 2.20.1