For those people who enjoy pain here some code that allows bgpd to load ASPA records from a very current StayRTR cache.
This implements mostly what draft-ietf-sidrops-8210bis-10 specifies. Now the version negotiation is a hot mess and the result is not pretty. In rtr.c most change is because I renamed rtr_aspa_merge_set() to rtr_aspa_insert() and I moved the code up to rtr_roa_insert(). In rtr_proto.c most complexity comes from the version negotiation. The ASPA parser is reasonably streight forward. While there are no immediate fireworks with this on the latest StayRTR using any of the StayRTR / GoRTR releases with this triggers a bug on the RTR cache and with that version negotiation fails in most cases. So best run bleeding edge with bleeding edge. -- :wq Claudio Index: bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.461 diff -u -p -r1.461 bgpd.h --- bgpd.h 9 Feb 2023 13:43:23 -0000 1.461 +++ bgpd.h 24 Feb 2023 09:22:50 -0000 @@ -539,7 +539,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 { @@ -551,7 +551,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]; Index: rtr.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rtr.c,v retrieving revision 1.11 diff -u -p -r1.11 rtr.c --- rtr.c 20 Jan 2023 09:54:43 -0000 1.11 +++ rtr.c 24 Feb 2023 16:41:47 -0000 @@ -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 r 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 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 != @@ -418,67 +482,6 @@ rtr_imsg_compose(int type, uint32_t id, } /* - * 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. * At the same time tas_aid is overwritten with the bitmasks or cleared @@ -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); Index: rtr_proto.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rtr_proto.c,v retrieving revision 1.12 diff -u -p -r1.12 rtr_proto.c --- rtr_proto.c 2 Feb 2023 20:31:37 -0000 1.12 +++ rtr_proto.c 24 Feb 2023 16:52:09 -0000 @@ -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, uint3 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, e 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, 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, *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, 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, * 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, 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, 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; @@ -561,6 +601,98 @@ rtr_parse_ipv6_prefix(struct rtr_session } 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) { struct rtr_endofdata eod; @@ -675,22 +807,23 @@ rtr_parse_error(struct rtr_session *rs, 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 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 /* 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 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 /* 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 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, str 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, str 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 p 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; Index: session.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.h,v retrieving revision 1.159 diff -u -p -r1.159 session.h --- session.h 9 Feb 2023 13:43:23 -0000 1.159 +++ session.h 24 Feb 2023 16:38:08 -0000 @@ -328,11 +328,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);