1*1cd81e65Sclaudio /* $OpenBSD: rtr_proto.c,v 1.49 2025/01/25 07:23:30 claudio Exp $ */ 2bd9df44eSclaudio 3bd9df44eSclaudio /* 4bd9df44eSclaudio * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org> 5bd9df44eSclaudio * 6bd9df44eSclaudio * Permission to use, copy, modify, and distribute this software for any 7bd9df44eSclaudio * purpose with or without fee is hereby granted, provided that the above 8bd9df44eSclaudio * copyright notice and this permission notice appear in all copies. 9bd9df44eSclaudio * 10bd9df44eSclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11bd9df44eSclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12bd9df44eSclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13bd9df44eSclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14bd9df44eSclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15bd9df44eSclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16bd9df44eSclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17bd9df44eSclaudio */ 18bd9df44eSclaudio #include <sys/tree.h> 19bd9df44eSclaudio #include <errno.h> 20bd9df44eSclaudio #include <stdint.h> 21bd9df44eSclaudio #include <poll.h> 22bd9df44eSclaudio #include <stdio.h> 23bd9df44eSclaudio #include <stdlib.h> 24bd9df44eSclaudio #include <string.h> 25bd9df44eSclaudio #include <unistd.h> 26bd9df44eSclaudio 27bd9df44eSclaudio #include "bgpd.h" 28bd9df44eSclaudio #include "session.h" 29bd9df44eSclaudio #include "log.h" 30bd9df44eSclaudio 31bd9df44eSclaudio struct rtr_header { 32bd9df44eSclaudio uint8_t version; 33bd9df44eSclaudio uint8_t type; 34228f6bd1Sclaudio union { 35228f6bd1Sclaudio uint16_t session_id; 36228f6bd1Sclaudio uint16_t errcode; 37228f6bd1Sclaudio struct { 38228f6bd1Sclaudio uint8_t flags; 39228f6bd1Sclaudio uint8_t zero; 40228f6bd1Sclaudio }; 41228f6bd1Sclaudio }; 42bd9df44eSclaudio uint32_t length; 4337149e4fSclaudio } __packed; 44bd9df44eSclaudio 4598aa40b8Sclaudio #define RTR_MAX_PDU_SIZE 65535 46623585daSclaudio #define RTR_MAX_PDU_ERROR_SIZE 256 47bd9df44eSclaudio #define RTR_DEFAULT_REFRESH 3600 48bd9df44eSclaudio #define RTR_DEFAULT_RETRY 600 49bd9df44eSclaudio #define RTR_DEFAULT_EXPIRE 7200 50dfd27b08Sclaudio #define RTR_DEFAULT_ACTIVE 60 51bd9df44eSclaudio 52bd9df44eSclaudio enum rtr_pdu_type { 53bd9df44eSclaudio SERIAL_NOTIFY = 0, 54bd9df44eSclaudio SERIAL_QUERY, 55bd9df44eSclaudio RESET_QUERY, 56bd9df44eSclaudio CACHE_RESPONSE, 57bd9df44eSclaudio IPV4_PREFIX, 58bd9df44eSclaudio IPV6_PREFIX = 6, 59bd9df44eSclaudio END_OF_DATA = 7, 60bd9df44eSclaudio CACHE_RESET = 8, 61bd9df44eSclaudio ROUTER_KEY = 9, 62bd9df44eSclaudio ERROR_REPORT = 10, 6383072fb6Sclaudio ASPA = 11, 64bd9df44eSclaudio }; 65bd9df44eSclaudio 6637149e4fSclaudio struct rtr_notify { 6737149e4fSclaudio struct rtr_header hdr; 6837149e4fSclaudio uint32_t serial; 6937149e4fSclaudio } __packed; 7037149e4fSclaudio 7137149e4fSclaudio struct rtr_query { 7237149e4fSclaudio struct rtr_header hdr; 7337149e4fSclaudio uint32_t serial; 7437149e4fSclaudio } __packed; 7537149e4fSclaudio 7637149e4fSclaudio struct rtr_reset { 7737149e4fSclaudio struct rtr_header hdr; 7837149e4fSclaudio } __packed; 7937149e4fSclaudio 8037149e4fSclaudio struct rtr_response { 8137149e4fSclaudio struct rtr_header hdr; 8237149e4fSclaudio } __packed; 8337149e4fSclaudio 84bd9df44eSclaudio #define FLAG_ANNOUNCE 0x1 85bd9df44eSclaudio #define FLAG_MASK FLAG_ANNOUNCE 86bd9df44eSclaudio struct rtr_ipv4 { 8737149e4fSclaudio struct rtr_header hdr; 88bd9df44eSclaudio uint8_t flags; 89bd9df44eSclaudio uint8_t prefixlen; 90bd9df44eSclaudio uint8_t maxlen; 91bd9df44eSclaudio uint8_t zero; 92bd9df44eSclaudio uint32_t prefix; 93bd9df44eSclaudio uint32_t asnum; 9437149e4fSclaudio } __packed; 95bd9df44eSclaudio 96bd9df44eSclaudio struct rtr_ipv6 { 9737149e4fSclaudio struct rtr_header hdr; 98bd9df44eSclaudio uint8_t flags; 99bd9df44eSclaudio uint8_t prefixlen; 100bd9df44eSclaudio uint8_t maxlen; 101bd9df44eSclaudio uint8_t zero; 102bd9df44eSclaudio uint32_t prefix[4]; 103bd9df44eSclaudio uint32_t asnum; 10437149e4fSclaudio } __packed; 10537149e4fSclaudio 10637149e4fSclaudio struct rtr_routerkey { 10737149e4fSclaudio struct rtr_header hdr; 10837149e4fSclaudio uint8_t ski[20]; 10937149e4fSclaudio uint32_t asnum; 11037149e4fSclaudio /* followed by Subject Public Key Info */ 11137149e4fSclaudio } __packed; 112bd9df44eSclaudio 11383072fb6Sclaudio struct rtr_aspa { 11437149e4fSclaudio struct rtr_header hdr; 11583072fb6Sclaudio uint32_t cas; 116228f6bd1Sclaudio /* array of spas filling the rest of the packet */ 11737149e4fSclaudio } __packed; 11883072fb6Sclaudio 119bd9df44eSclaudio struct rtr_endofdata { 12037149e4fSclaudio struct rtr_header hdr; 121bd9df44eSclaudio uint32_t serial; 122bd9df44eSclaudio uint32_t refresh; 123bd9df44eSclaudio uint32_t retry; 124bd9df44eSclaudio uint32_t expire; 12537149e4fSclaudio } __packed; 126bd9df44eSclaudio 1270d6339a2Sclaudio struct rtr_endofdata_v0 { 1280d6339a2Sclaudio struct rtr_header hdr; 1290d6339a2Sclaudio uint32_t serial; 1300d6339a2Sclaudio } __packed; 1310d6339a2Sclaudio 132bd9df44eSclaudio enum rtr_event { 133bd9df44eSclaudio RTR_EVNT_START, 134bd9df44eSclaudio RTR_EVNT_CON_OPEN, 1352ced3cdbSclaudio RTR_EVNT_CON_CLOSE, 136bd9df44eSclaudio RTR_EVNT_TIMER_REFRESH, 137bd9df44eSclaudio RTR_EVNT_TIMER_RETRY, 138bd9df44eSclaudio RTR_EVNT_TIMER_EXPIRE, 139dfd27b08Sclaudio RTR_EVNT_TIMER_ACTIVE, 140bd9df44eSclaudio RTR_EVNT_SEND_ERROR, 141bd9df44eSclaudio RTR_EVNT_SERIAL_NOTIFY, 142bd9df44eSclaudio RTR_EVNT_CACHE_RESPONSE, 143bd9df44eSclaudio RTR_EVNT_END_OF_DATA, 144bd9df44eSclaudio RTR_EVNT_CACHE_RESET, 145bd9df44eSclaudio RTR_EVNT_NO_DATA, 1462ced3cdbSclaudio RTR_EVNT_RESET_AND_CLOSE, 14783072fb6Sclaudio RTR_EVNT_UNSUPP_PROTO_VERSION, 14837149e4fSclaudio RTR_EVNT_NEGOTIATION_DONE, 149bd9df44eSclaudio }; 150bd9df44eSclaudio 151bd9df44eSclaudio static const char *rtr_eventnames[] = { 152bd9df44eSclaudio "start", 153bd9df44eSclaudio "connection open", 154bd9df44eSclaudio "connection closed", 155bd9df44eSclaudio "refresh timer expired", 156bd9df44eSclaudio "retry timer expired", 157bd9df44eSclaudio "expire timer expired", 158dfd27b08Sclaudio "activity timer expired", 159bd9df44eSclaudio "sent error", 160bd9df44eSclaudio "serial notify received", 161bd9df44eSclaudio "cache response received", 162bd9df44eSclaudio "end of data received", 163bd9df44eSclaudio "cache reset received", 1642ced3cdbSclaudio "no data", 1652ced3cdbSclaudio "connection closed with reset", 16683072fb6Sclaudio "unsupported protocol version", 16737149e4fSclaudio "negotiation done", 168bd9df44eSclaudio }; 169bd9df44eSclaudio 170bd9df44eSclaudio enum rtr_state { 171bd9df44eSclaudio RTR_STATE_CLOSED, 172bd9df44eSclaudio RTR_STATE_ERROR, 1735a04dc7fSclaudio /* sessions with a state below this line will poll for incoming data */ 1740314fe8bSclaudio RTR_STATE_ESTABLISHED, 1750314fe8bSclaudio RTR_STATE_EXCHANGE, 17683072fb6Sclaudio RTR_STATE_NEGOTIATION, 177bd9df44eSclaudio }; 178bd9df44eSclaudio 179bd9df44eSclaudio static const char *rtr_statenames[] = { 180bd9df44eSclaudio "closed", 181bd9df44eSclaudio "error", 1820314fe8bSclaudio "established", 1830314fe8bSclaudio "exchange", 18483072fb6Sclaudio "negotiation", 185bd9df44eSclaudio }; 186bd9df44eSclaudio 187bd9df44eSclaudio struct rtr_session { 188bd9df44eSclaudio TAILQ_ENTRY(rtr_session) entry; 189bd9df44eSclaudio char descr[PEER_DESCR_LEN]; 190bd9df44eSclaudio struct roa_tree roa_set; 191c0c9c169Sclaudio struct aspa_tree aspa; 192bd9df44eSclaudio struct timer_head timers; 19322b46a1fSclaudio struct msgbuf *w; 194bd9df44eSclaudio uint32_t id; /* rtr_config id */ 195bd9df44eSclaudio uint32_t serial; 196bd9df44eSclaudio uint32_t refresh; 197bd9df44eSclaudio uint32_t retry; 198bd9df44eSclaudio uint32_t expire; 199dfd27b08Sclaudio uint32_t active; 200bd9df44eSclaudio int session_id; 201bd9df44eSclaudio int fd; 202dfd27b08Sclaudio int active_lock; 203bd9df44eSclaudio enum rtr_state state; 204bd9df44eSclaudio enum reconf_action reconf_action; 205bd9df44eSclaudio enum rtr_error last_sent_error; 206bd9df44eSclaudio enum rtr_error last_recv_error; 207bd9df44eSclaudio char last_sent_msg[REASON_LEN]; 208bd9df44eSclaudio char last_recv_msg[REASON_LEN]; 20983072fb6Sclaudio uint8_t version; 210125ef3d5Sclaudio uint8_t prev_version; 211d87cfbccSclaudio uint8_t min_version; 212d87cfbccSclaudio uint8_t errored; 213d87cfbccSclaudio 214bd9df44eSclaudio }; 215bd9df44eSclaudio 216bd9df44eSclaudio TAILQ_HEAD(, rtr_session) rtrs = TAILQ_HEAD_INITIALIZER(rtrs); 217bd9df44eSclaudio 218bd9df44eSclaudio static void rtr_fsm(struct rtr_session *, enum rtr_event); 219bd9df44eSclaudio 220bd9df44eSclaudio static const char * 221bd9df44eSclaudio log_rtr(struct rtr_session *rs) 222bd9df44eSclaudio { 223bd9df44eSclaudio return rs->descr; 224bd9df44eSclaudio } 225bd9df44eSclaudio 226bd9df44eSclaudio static const char * 227bd9df44eSclaudio log_rtr_type(enum rtr_pdu_type type) 228bd9df44eSclaudio { 229bd9df44eSclaudio static char buf[20]; 230bd9df44eSclaudio 231bd9df44eSclaudio switch (type) { 232bd9df44eSclaudio case SERIAL_NOTIFY: 233bd9df44eSclaudio return "serial notify"; 234bd9df44eSclaudio case SERIAL_QUERY: 235bd9df44eSclaudio return "serial query"; 236bd9df44eSclaudio case RESET_QUERY: 237bd9df44eSclaudio return "reset query"; 238bd9df44eSclaudio case CACHE_RESPONSE: 239bd9df44eSclaudio return "cache response"; 240bd9df44eSclaudio case IPV4_PREFIX: 241bd9df44eSclaudio return "IPv4 prefix"; 242bd9df44eSclaudio case IPV6_PREFIX: 243bd9df44eSclaudio return "IPv6 prefix"; 244bd9df44eSclaudio case END_OF_DATA: 245bd9df44eSclaudio return "end of data"; 246bd9df44eSclaudio case CACHE_RESET: 247bd9df44eSclaudio return "cache reset"; 248bd9df44eSclaudio case ROUTER_KEY: 249bd9df44eSclaudio return "router key"; 250bd9df44eSclaudio case ERROR_REPORT: 251bd9df44eSclaudio return "error report"; 25283072fb6Sclaudio case ASPA: 2539dad7388Sclaudio return "aspa"; 254bd9df44eSclaudio default: 255bd9df44eSclaudio snprintf(buf, sizeof(buf), "unknown %u", type); 256bd9df44eSclaudio return buf; 257bd9df44eSclaudio } 258bd9df44eSclaudio }; 259bd9df44eSclaudio 260d87cfbccSclaudio static uint8_t 261d87cfbccSclaudio rtr_max_session_version(struct rtr_session *rs) 262d87cfbccSclaudio { 263d87cfbccSclaudio if (rs->min_version > RTR_DEFAULT_VERSION) 264d87cfbccSclaudio return rs->min_version; 265d87cfbccSclaudio return RTR_DEFAULT_VERSION; 266d87cfbccSclaudio } 267d87cfbccSclaudio 2685a04dc7fSclaudio static void 2695a04dc7fSclaudio rtr_reset_cache(struct rtr_session *rs) 2705a04dc7fSclaudio { 2715a04dc7fSclaudio /* reset session */ 2725a04dc7fSclaudio rs->session_id = -1; 2735a04dc7fSclaudio timer_stop(&rs->timers, Timer_Rtr_Expire); 2745a04dc7fSclaudio free_roatree(&rs->roa_set); 275c0c9c169Sclaudio free_aspatree(&rs->aspa); 2765a04dc7fSclaudio } 2775a04dc7fSclaudio 278bd9df44eSclaudio static struct ibuf * 27983072fb6Sclaudio rtr_newmsg(struct rtr_session *rs, enum rtr_pdu_type type, uint32_t len, 28083072fb6Sclaudio uint16_t session_id) 281bd9df44eSclaudio { 282bd9df44eSclaudio struct ibuf *buf; 283a2488d3fSclaudio int saved_errno; 284bd9df44eSclaudio 285623585daSclaudio if (len > RTR_MAX_PDU_SIZE) { 286bd9df44eSclaudio errno = ERANGE; 287bd9df44eSclaudio return NULL; 288bd9df44eSclaudio } 289de259131Sclaudio len += sizeof(struct rtr_header); 290bd9df44eSclaudio if ((buf = ibuf_open(len)) == NULL) 291a2488d3fSclaudio goto fail; 292a2488d3fSclaudio if (ibuf_add_n8(buf, rs->version) == -1) 293a2488d3fSclaudio goto fail; 294a2488d3fSclaudio if (ibuf_add_n8(buf, type) == -1) 295a2488d3fSclaudio goto fail; 296a2488d3fSclaudio if (ibuf_add_n16(buf, session_id) == -1) 297a2488d3fSclaudio goto fail; 298a2488d3fSclaudio if (ibuf_add_n32(buf, len) == -1) 299a2488d3fSclaudio goto fail; 300de259131Sclaudio 301bd9df44eSclaudio return buf; 302a2488d3fSclaudio 303a2488d3fSclaudio fail: 304a2488d3fSclaudio saved_errno = errno; 305a2488d3fSclaudio ibuf_free(buf); 306a2488d3fSclaudio errno = saved_errno; 307a2488d3fSclaudio return NULL; 308bd9df44eSclaudio } 309bd9df44eSclaudio 3109dad7388Sclaudio static void rtr_send_error(struct rtr_session *, struct ibuf *, enum rtr_error, 3119dad7388Sclaudio const char *, ...) __attribute__((__format__ (printf, 4, 5))); 3129dad7388Sclaudio 313bd9df44eSclaudio /* 314bd9df44eSclaudio * Try to send an error PDU to cache, put connection into error 315bd9df44eSclaudio * state. 316bd9df44eSclaudio */ 317bd9df44eSclaudio static void 3189dad7388Sclaudio rtr_send_error(struct rtr_session *rs, struct ibuf *pdu, enum rtr_error err, 3199dad7388Sclaudio const char *fmt, ...) 320bd9df44eSclaudio { 321bd9df44eSclaudio struct ibuf *buf; 3229dad7388Sclaudio va_list ap; 323b1793e8cSclaudio size_t len = 0, mlen = 0; 324bd9df44eSclaudio 325bd9df44eSclaudio rs->last_sent_error = err; 326bd9df44eSclaudio memset(rs->last_sent_msg, 0, sizeof(rs->last_sent_msg)); 3279dad7388Sclaudio if (fmt != NULL) { 3289dad7388Sclaudio va_start(ap, fmt); 3299dad7388Sclaudio vsnprintf(rs->last_sent_msg, sizeof(rs->last_sent_msg), 3309dad7388Sclaudio fmt, ap); 3319dad7388Sclaudio mlen = strlen(rs->last_sent_msg); 3329dad7388Sclaudio va_end(ap); 3339dad7388Sclaudio } 3349dad7388Sclaudio 3359dad7388Sclaudio log_warnx("rtr %s: sending error: %s%s%s", log_rtr(rs), 3369dad7388Sclaudio log_rtr_error(err), mlen > 0 ? ": " : "", rs->last_sent_msg); 337bd9df44eSclaudio 338b1793e8cSclaudio if (pdu != NULL) { 339b1793e8cSclaudio ibuf_rewind(pdu); 340b1793e8cSclaudio len = ibuf_size(pdu); 341623585daSclaudio if (len > RTR_MAX_PDU_ERROR_SIZE) { 342623585daSclaudio len = RTR_MAX_PDU_ERROR_SIZE; 343623585daSclaudio /* truncate down can not fail */ 344623585daSclaudio ibuf_truncate(pdu, RTR_MAX_PDU_ERROR_SIZE); 345623585daSclaudio } 346b1793e8cSclaudio } 347b1793e8cSclaudio 348de259131Sclaudio buf = rtr_newmsg(rs, ERROR_REPORT, 2 * sizeof(uint32_t) + len + mlen, 34983072fb6Sclaudio err); 350a2488d3fSclaudio if (buf == NULL) 351a2488d3fSclaudio goto fail; 352a2488d3fSclaudio if (ibuf_add_n32(buf, len) == -1) 353a2488d3fSclaudio goto fail; 354b1793e8cSclaudio if (pdu != NULL) { 355b1793e8cSclaudio if (ibuf_add_ibuf(buf, pdu) == -1) 356a2488d3fSclaudio goto fail; 357b1793e8cSclaudio } 358a2488d3fSclaudio if (ibuf_add_n32(buf, mlen) == -1) 359a2488d3fSclaudio goto fail; 3609dad7388Sclaudio if (ibuf_add(buf, rs->last_sent_msg, mlen) == -1) 361a2488d3fSclaudio goto fail; 36205453d67Sclaudio ibuf_close(rs->w, buf); 363bd9df44eSclaudio 3645a04dc7fSclaudio rtr_fsm(rs, RTR_EVNT_SEND_ERROR); 365a2488d3fSclaudio return; 366a2488d3fSclaudio 367a2488d3fSclaudio fail: 368a2488d3fSclaudio log_warn("rtr %s: send error report", log_rtr(rs)); 369a2488d3fSclaudio ibuf_free(buf); 370bd9df44eSclaudio } 371bd9df44eSclaudio 372bd9df44eSclaudio static void 3735a04dc7fSclaudio rtr_send_reset_query(struct rtr_session *rs) 374bd9df44eSclaudio { 375bd9df44eSclaudio struct ibuf *buf; 376bd9df44eSclaudio 37783072fb6Sclaudio buf = rtr_newmsg(rs, RESET_QUERY, 0, 0); 3789dad7388Sclaudio if (buf == NULL) 3799dad7388Sclaudio goto fail; 38005453d67Sclaudio ibuf_close(rs->w, buf); 3819dad7388Sclaudio return; 3829dad7388Sclaudio 3839dad7388Sclaudio fail: 3849dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, 3859dad7388Sclaudio "send %s: %s", log_rtr_type(RESET_QUERY), strerror(errno)); 3869dad7388Sclaudio ibuf_free(buf); 387bd9df44eSclaudio } 388bd9df44eSclaudio 389bd9df44eSclaudio static void 3905a04dc7fSclaudio rtr_send_serial_query(struct rtr_session *rs) 391bd9df44eSclaudio { 392bd9df44eSclaudio struct ibuf *buf; 393bd9df44eSclaudio 394de259131Sclaudio buf = rtr_newmsg(rs, SERIAL_QUERY, sizeof(uint32_t), rs->session_id); 395a2488d3fSclaudio if (buf == NULL) 396a2488d3fSclaudio goto fail; 397a2488d3fSclaudio if (ibuf_add_n32(buf, rs->serial) == -1) 398a2488d3fSclaudio goto fail; 39905453d67Sclaudio ibuf_close(rs->w, buf); 400a2488d3fSclaudio return; 401a2488d3fSclaudio 402a2488d3fSclaudio fail: 4039dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, 4049dad7388Sclaudio "send %s: %s", log_rtr_type(SERIAL_QUERY), strerror(errno)); 405a2488d3fSclaudio ibuf_free(buf); 406bd9df44eSclaudio } 407bd9df44eSclaudio 408bd9df44eSclaudio /* 40937149e4fSclaudio * Check the session_id of the rtr_header to match the expected value. 41037149e4fSclaudio * Returns -1 on failure and 0 on success. 41137149e4fSclaudio */ 41237149e4fSclaudio static int 41337149e4fSclaudio rtr_check_session_id(struct rtr_session *rs, uint16_t session_id, 41437149e4fSclaudio struct rtr_header *rh, struct ibuf *pdu) 41537149e4fSclaudio { 41637149e4fSclaudio if (session_id != ntohs(rh->session_id)) { 4179dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, 4189dad7388Sclaudio "%s: bad session_id %d (expected %d)", 4199dad7388Sclaudio log_rtr_type(rh->type), ntohs(rh->session_id), session_id); 42037149e4fSclaudio return -1; 42137149e4fSclaudio } 42237149e4fSclaudio return 0; 42337149e4fSclaudio } 42437149e4fSclaudio 42537149e4fSclaudio /* 42622b46a1fSclaudio * Callback for ibuf_read to get the size of a PDU. 427bd9df44eSclaudio */ 4289509a1e6Sclaudio static struct ibuf * 4299509a1e6Sclaudio rtr_reader_callback(struct ibuf *hdr, void *arg, int *fd) 430bd9df44eSclaudio { 43122b46a1fSclaudio struct rtr_session *rs = arg; 432bd9df44eSclaudio struct rtr_header rh; 4339509a1e6Sclaudio struct ibuf *b; 43422b46a1fSclaudio ssize_t len; 435bd9df44eSclaudio 436b1793e8cSclaudio if (ibuf_get(hdr, &rh, sizeof(rh)) == -1) 4379509a1e6Sclaudio return NULL; 438bd9df44eSclaudio 43937149e4fSclaudio len = ntohl(rh.length); 440bd9df44eSclaudio 441623585daSclaudio if (len > RTR_MAX_PDU_SIZE) { 4429dad7388Sclaudio rtr_send_error(rs, hdr, CORRUPT_DATA, "%s: too big: %zu bytes", 4439dad7388Sclaudio log_rtr_type(rh.type), len); 44422b46a1fSclaudio errno = ERANGE; 4459509a1e6Sclaudio return NULL; 446bd9df44eSclaudio } 44737149e4fSclaudio 4489509a1e6Sclaudio if ((b = ibuf_open(len)) == NULL) 4499509a1e6Sclaudio return NULL; 4509509a1e6Sclaudio return b; 45122b46a1fSclaudio } 45222b46a1fSclaudio 45322b46a1fSclaudio /* 45422b46a1fSclaudio * Parse the common rtr header (first 8 bytes) including the 45522b46a1fSclaudio * included length field. 45622b46a1fSclaudio * Returns -1 on failure. On success msgtype and msglen are set 45722b46a1fSclaudio * and the function return 0. 45822b46a1fSclaudio */ 45922b46a1fSclaudio static int 46022b46a1fSclaudio rtr_parse_header(struct rtr_session *rs, struct ibuf *msg, 46122b46a1fSclaudio enum rtr_pdu_type *msgtype) 46222b46a1fSclaudio { 46322b46a1fSclaudio struct rtr_header rh; 46422b46a1fSclaudio struct ibuf hdr; 46522b46a1fSclaudio size_t len; 46622b46a1fSclaudio uint16_t errcode; 46722b46a1fSclaudio 46822b46a1fSclaudio len = ibuf_size(msg); 46922b46a1fSclaudio 47022b46a1fSclaudio ibuf_from_ibuf(&hdr, msg); 47122b46a1fSclaudio if (ibuf_get(&hdr, &rh, sizeof(rh)) == -1) 47222b46a1fSclaudio fatal("%s: ibuf_get", __func__); 47322b46a1fSclaudio 47437149e4fSclaudio if (rs->state == RTR_STATE_NEGOTIATION) { 47537149e4fSclaudio switch (rh.type) { 47637149e4fSclaudio case CACHE_RESPONSE: 47737149e4fSclaudio case CACHE_RESET: 478125ef3d5Sclaudio /* implicit downgrade */ 479125ef3d5Sclaudio if (rh.version < rs->version) { 480125ef3d5Sclaudio rs->prev_version = rs->version; 48137149e4fSclaudio rs->version = rh.version; 482125ef3d5Sclaudio } 48337149e4fSclaudio rtr_fsm(rs, RTR_EVNT_NEGOTIATION_DONE); 48437149e4fSclaudio break; 485125ef3d5Sclaudio case ERROR_REPORT: 486a62144a2Sclaudio errcode = ntohs(rh.session_id); 487a62144a2Sclaudio if (errcode == UNSUPP_PROTOCOL_VERS || 488a62144a2Sclaudio errcode == NO_DATA_AVAILABLE) { 489a62144a2Sclaudio if (rh.version < rs->version) { 490a62144a2Sclaudio rs->prev_version = rs->version; 491a62144a2Sclaudio rs->version = rh.version; 492a62144a2Sclaudio } 493a62144a2Sclaudio } 494125ef3d5Sclaudio break; 49537149e4fSclaudio case SERIAL_NOTIFY: 49637149e4fSclaudio /* ignore SERIAL_NOTIFY */ 49737149e4fSclaudio break; 49837149e4fSclaudio default: 49922b46a1fSclaudio rtr_send_error(rs, msg, CORRUPT_DATA, 5009dad7388Sclaudio "%s: out of context", log_rtr_type(rh.type)); 501bd9df44eSclaudio return -1; 502bd9df44eSclaudio } 50337149e4fSclaudio } else if (rh.version != rs->version && rh.type != ERROR_REPORT) { 50437149e4fSclaudio goto badversion; 50537149e4fSclaudio } 50637149e4fSclaudio 50737149e4fSclaudio switch (rh.type) { 50837149e4fSclaudio case SERIAL_NOTIFY: 50937149e4fSclaudio if (len != sizeof(struct rtr_notify)) 51037149e4fSclaudio goto badlen; 51137149e4fSclaudio break; 51237149e4fSclaudio case CACHE_RESPONSE: 51337149e4fSclaudio if (len != sizeof(struct rtr_response)) 51437149e4fSclaudio goto badlen; 51537149e4fSclaudio break; 51637149e4fSclaudio case IPV4_PREFIX: 51737149e4fSclaudio if (len != sizeof(struct rtr_ipv4)) 51837149e4fSclaudio goto badlen; 51937149e4fSclaudio break; 52037149e4fSclaudio case IPV6_PREFIX: 52137149e4fSclaudio if (len != sizeof(struct rtr_ipv6)) 52237149e4fSclaudio goto badlen; 52337149e4fSclaudio break; 52437149e4fSclaudio case END_OF_DATA: 5250d6339a2Sclaudio if (rs->version == 0) { 5260d6339a2Sclaudio if (len != sizeof(struct rtr_endofdata_v0)) 5270d6339a2Sclaudio goto badlen; 5280d6339a2Sclaudio } else { 52937149e4fSclaudio if (len != sizeof(struct rtr_endofdata)) 53037149e4fSclaudio goto badlen; 5310d6339a2Sclaudio } 53237149e4fSclaudio break; 53337149e4fSclaudio case CACHE_RESET: 53437149e4fSclaudio if (len != sizeof(struct rtr_reset)) 53537149e4fSclaudio goto badlen; 53637149e4fSclaudio break; 53737149e4fSclaudio case ROUTER_KEY: 53837149e4fSclaudio if (rs->version < 1) 53937149e4fSclaudio goto badversion; 5404fa7a5b4Sclaudio if (len < sizeof(struct rtr_routerkey)) 5414fa7a5b4Sclaudio goto badlen; 54237149e4fSclaudio break; 54337149e4fSclaudio case ERROR_REPORT: 54437149e4fSclaudio if (len < 16) 54537149e4fSclaudio goto badlen; 54637149e4fSclaudio break; 54783072fb6Sclaudio case ASPA: 54883072fb6Sclaudio if (rs->version < 2) 54983072fb6Sclaudio goto badversion; 5504fa7a5b4Sclaudio if (len < sizeof(struct rtr_aspa) || (len % 4) != 0) 5514fa7a5b4Sclaudio goto badlen; 55283072fb6Sclaudio break; 553bd9df44eSclaudio default: 55422b46a1fSclaudio rtr_send_error(rs, msg, UNSUPP_PDU_TYPE, "type %s", 5559dad7388Sclaudio log_rtr_type(rh.type)); 556bd9df44eSclaudio return -1; 557bd9df44eSclaudio } 558bd9df44eSclaudio 55937149e4fSclaudio *msgtype = rh.type; 56037149e4fSclaudio 56137149e4fSclaudio return 0; 56237149e4fSclaudio 56337149e4fSclaudio badlen: 56422b46a1fSclaudio rtr_send_error(rs, msg, CORRUPT_DATA, "%s: bad length: %zu bytes", 5659dad7388Sclaudio log_rtr_type(rh.type), len); 566bd9df44eSclaudio return -1; 567bd9df44eSclaudio 56837149e4fSclaudio badversion: 56922b46a1fSclaudio rtr_send_error(rs, msg, UNEXP_PROTOCOL_VERS, "%s: version %d", 5709dad7388Sclaudio log_rtr_type(rh.type), rh.version); 571bd9df44eSclaudio return -1; 572bd9df44eSclaudio } 573bd9df44eSclaudio 574bd9df44eSclaudio static int 575b1793e8cSclaudio rtr_parse_notify(struct rtr_session *rs, struct ibuf *pdu) 576bd9df44eSclaudio { 57737149e4fSclaudio struct rtr_notify notify; 57837149e4fSclaudio 57937149e4fSclaudio /* ignore SERIAL_NOTIFY during startup */ 58037149e4fSclaudio if (rs->state == RTR_STATE_NEGOTIATION) 58137149e4fSclaudio return 0; 58237149e4fSclaudio 5839dad7388Sclaudio if (ibuf_get(pdu, ¬ify, sizeof(notify)) == -1) 5849dad7388Sclaudio goto badlen; 58537149e4fSclaudio 586a62144a2Sclaudio /* set session_id if not yet happened */ 587a62144a2Sclaudio if (rs->session_id == -1) 588a62144a2Sclaudio rs->session_id = ntohs(notify.hdr.session_id); 589a62144a2Sclaudio 59037149e4fSclaudio if (rtr_check_session_id(rs, rs->session_id, ¬ify.hdr, pdu) == -1) 59137149e4fSclaudio return -1; 59237149e4fSclaudio 593964d6687Sclaudio if (rs->state != RTR_STATE_ESTABLISHED) { 5945a04dc7fSclaudio log_warnx("rtr %s: received %s: while in state %s (ignored)", 5955a04dc7fSclaudio log_rtr(rs), log_rtr_type(SERIAL_NOTIFY), 5965a04dc7fSclaudio rtr_statenames[rs->state]); 597bd9df44eSclaudio return 0; 598bd9df44eSclaudio } 599bd9df44eSclaudio 600bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_SERIAL_NOTIFY); 601bd9df44eSclaudio return 0; 6029dad7388Sclaudio 6039dad7388Sclaudio badlen: 6049dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 6059dad7388Sclaudio log_rtr_type(SERIAL_NOTIFY)); 6069dad7388Sclaudio return -1; 607bd9df44eSclaudio } 608bd9df44eSclaudio 609bd9df44eSclaudio static int 610b1793e8cSclaudio rtr_parse_cache_response(struct rtr_session *rs, struct ibuf *pdu) 611bd9df44eSclaudio { 61237149e4fSclaudio struct rtr_response resp; 61337149e4fSclaudio 6149dad7388Sclaudio if (ibuf_get(pdu, &resp, sizeof(resp)) == -1) 6159dad7388Sclaudio goto badlen; 61637149e4fSclaudio 61737149e4fSclaudio /* set session_id if not yet happened */ 61837149e4fSclaudio if (rs->session_id == -1) 61937149e4fSclaudio rs->session_id = ntohs(resp.hdr.session_id); 62037149e4fSclaudio 62137149e4fSclaudio if (rtr_check_session_id(rs, rs->session_id, &resp.hdr, pdu) == -1) 62237149e4fSclaudio return -1; 62337149e4fSclaudio 62437149e4fSclaudio if (rs->state != RTR_STATE_ESTABLISHED) { 6259dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 6269dad7388Sclaudio log_rtr_type(CACHE_RESPONSE)); 627bd9df44eSclaudio return -1; 628bd9df44eSclaudio } 629bd9df44eSclaudio 630bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_CACHE_RESPONSE); 631bd9df44eSclaudio return 0; 6329dad7388Sclaudio 6339dad7388Sclaudio badlen: 6349dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 6359dad7388Sclaudio log_rtr_type(CACHE_RESPONSE)); 6369dad7388Sclaudio return -1; 637bd9df44eSclaudio } 638bd9df44eSclaudio 639bd9df44eSclaudio static int 640b1793e8cSclaudio rtr_parse_ipv4_prefix(struct rtr_session *rs, struct ibuf *pdu) 641bd9df44eSclaudio { 642bd9df44eSclaudio struct rtr_ipv4 ip4; 643bd9df44eSclaudio struct roa *roa; 644bd9df44eSclaudio 6459dad7388Sclaudio if (ibuf_get(pdu, &ip4, sizeof(ip4)) == -1) 6469dad7388Sclaudio goto badlen; 647bd9df44eSclaudio 64837149e4fSclaudio if (rtr_check_session_id(rs, 0, &ip4.hdr, pdu) == -1) 64937149e4fSclaudio return -1; 65037149e4fSclaudio 6510314fe8bSclaudio if (rs->state != RTR_STATE_EXCHANGE) { 6529dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 6539dad7388Sclaudio log_rtr_type(IPV4_PREFIX)); 654bd9df44eSclaudio return -1; 655bd9df44eSclaudio } 656bd9df44eSclaudio 65769fad272Sclaudio if (ip4.prefixlen > 32 || ip4.maxlen > 32 || 65869fad272Sclaudio ip4.prefixlen > ip4.maxlen) { 6599dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, 6609dad7388Sclaudio "%s: bad prefixlen / maxlen", log_rtr_type(IPV4_PREFIX)); 6611eccb425Sjob return -1; 6621eccb425Sjob } 663f3760350Stb 664f3760350Stb if ((roa = calloc(1, sizeof(*roa))) == NULL) { 6659dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory"); 666f3760350Stb return -1; 667f3760350Stb } 668bd9df44eSclaudio roa->aid = AID_INET; 669bd9df44eSclaudio roa->prefixlen = ip4.prefixlen; 670bd9df44eSclaudio roa->maxlen = ip4.maxlen; 671bd9df44eSclaudio roa->asnum = ntohl(ip4.asnum); 672bd9df44eSclaudio roa->prefix.inet.s_addr = ip4.prefix; 673bd9df44eSclaudio 674bd9df44eSclaudio if (ip4.flags & FLAG_ANNOUNCE) { 675bd9df44eSclaudio if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) { 6769dad7388Sclaudio rtr_send_error(rs, pdu, DUP_REC_RECV, "%s %s", 6779dad7388Sclaudio log_rtr_type(IPV4_PREFIX), log_roa(roa)); 678bd9df44eSclaudio free(roa); 679bd9df44eSclaudio return -1; 680bd9df44eSclaudio } 681bd9df44eSclaudio } else { 682bd9df44eSclaudio struct roa *r; 683bd9df44eSclaudio 684bd9df44eSclaudio r = RB_FIND(roa_tree, &rs->roa_set, roa); 685bd9df44eSclaudio if (r == NULL) { 6869dad7388Sclaudio rtr_send_error(rs, pdu, UNK_REC_WDRAWL, "%s %s", 6879dad7388Sclaudio log_rtr_type(IPV4_PREFIX), log_roa(roa)); 688bd9df44eSclaudio free(roa); 689bd9df44eSclaudio return -1; 690bd9df44eSclaudio } 691bd9df44eSclaudio RB_REMOVE(roa_tree, &rs->roa_set, r); 692bd9df44eSclaudio free(r); 693bd9df44eSclaudio free(roa); 694bd9df44eSclaudio } 695bd9df44eSclaudio 696bd9df44eSclaudio return 0; 6979dad7388Sclaudio 6989dad7388Sclaudio badlen: 6999dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 7009dad7388Sclaudio log_rtr_type(IPV4_PREFIX)); 7019dad7388Sclaudio return -1; 702bd9df44eSclaudio } 703bd9df44eSclaudio 704bd9df44eSclaudio static int 705b1793e8cSclaudio rtr_parse_ipv6_prefix(struct rtr_session *rs, struct ibuf *pdu) 706bd9df44eSclaudio { 707bd9df44eSclaudio struct rtr_ipv6 ip6; 708bd9df44eSclaudio struct roa *roa; 709bd9df44eSclaudio 7109dad7388Sclaudio if (ibuf_get(pdu, &ip6, sizeof(ip6)) == -1) 7119dad7388Sclaudio goto badlen; 712bd9df44eSclaudio 71337149e4fSclaudio if (rtr_check_session_id(rs, 0, &ip6.hdr, pdu) == -1) 71437149e4fSclaudio return -1; 71537149e4fSclaudio 7160314fe8bSclaudio if (rs->state != RTR_STATE_EXCHANGE) { 7179dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 7189dad7388Sclaudio log_rtr_type(IPV6_PREFIX)); 719bd9df44eSclaudio return -1; 720bd9df44eSclaudio } 721bd9df44eSclaudio 72269fad272Sclaudio if (ip6.prefixlen > 128 || ip6.maxlen > 128 || 72369fad272Sclaudio ip6.prefixlen > ip6.maxlen) { 7249dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, 7259dad7388Sclaudio "%s: bad prefixlen / maxlen", log_rtr_type(IPV6_PREFIX)); 7261eccb425Sjob return -1; 7271eccb425Sjob } 728f3760350Stb 729f3760350Stb if ((roa = calloc(1, sizeof(*roa))) == NULL) { 7309dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory"); 731f3760350Stb return -1; 732f3760350Stb } 733bd9df44eSclaudio roa->aid = AID_INET6; 734bd9df44eSclaudio roa->prefixlen = ip6.prefixlen; 735bd9df44eSclaudio roa->maxlen = ip6.maxlen; 736bd9df44eSclaudio roa->asnum = ntohl(ip6.asnum); 737bd9df44eSclaudio memcpy(&roa->prefix.inet6, ip6.prefix, sizeof(roa->prefix.inet6)); 738bd9df44eSclaudio 739bd9df44eSclaudio if (ip6.flags & FLAG_ANNOUNCE) { 740bd9df44eSclaudio if (RB_INSERT(roa_tree, &rs->roa_set, roa) != NULL) { 7419dad7388Sclaudio rtr_send_error(rs, pdu, DUP_REC_RECV, "%s %s", 7429dad7388Sclaudio log_rtr_type(IPV6_PREFIX), log_roa(roa)); 743bd9df44eSclaudio free(roa); 744bd9df44eSclaudio return -1; 745bd9df44eSclaudio } 746bd9df44eSclaudio } else { 747bd9df44eSclaudio struct roa *r; 748bd9df44eSclaudio 749bd9df44eSclaudio r = RB_FIND(roa_tree, &rs->roa_set, roa); 750bd9df44eSclaudio if (r == NULL) { 7519dad7388Sclaudio rtr_send_error(rs, pdu, UNK_REC_WDRAWL, "%s %s", 7529dad7388Sclaudio log_rtr_type(IPV6_PREFIX), log_roa(roa)); 753bd9df44eSclaudio free(roa); 754bd9df44eSclaudio return -1; 755bd9df44eSclaudio } 756bd9df44eSclaudio RB_REMOVE(roa_tree, &rs->roa_set, r); 757bd9df44eSclaudio free(r); 758bd9df44eSclaudio free(roa); 759bd9df44eSclaudio } 760bd9df44eSclaudio return 0; 7619dad7388Sclaudio 7629dad7388Sclaudio badlen: 7639dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 7649dad7388Sclaudio log_rtr_type(IPV6_PREFIX)); 7659dad7388Sclaudio return -1; 766bd9df44eSclaudio } 767bd9df44eSclaudio 768bd9df44eSclaudio static int 769b1793e8cSclaudio rtr_parse_aspa(struct rtr_session *rs, struct ibuf *pdu) 77083072fb6Sclaudio { 77183072fb6Sclaudio struct rtr_aspa rtr_aspa; 77283072fb6Sclaudio struct aspa_set *aspa, *a; 773228f6bd1Sclaudio uint32_t cnt, i; 774228f6bd1Sclaudio uint8_t flags; 77583072fb6Sclaudio 776b0ea642aSclaudio if (ibuf_get(pdu, &rtr_aspa, sizeof(rtr_aspa)) == -1) 7779dad7388Sclaudio goto badlen; 7789dad7388Sclaudio 779228f6bd1Sclaudio flags = rtr_aspa.hdr.flags; 780228f6bd1Sclaudio cnt = ibuf_size(pdu) / sizeof(uint32_t); 78183072fb6Sclaudio 782d9facda5Sclaudio if ((flags & FLAG_ANNOUNCE) && cnt == 0) { 783d9facda5Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: " 784d9facda5Sclaudio "announce with empty SPAS", log_rtr_type(ASPA)); 785d9facda5Sclaudio return -1; 786d9facda5Sclaudio } 787d9facda5Sclaudio if ((flags & FLAG_ANNOUNCE) == 0 && cnt != 0) { 788d9facda5Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: " 789d9facda5Sclaudio "withdraw with non-empty SPAS", log_rtr_type(ASPA)); 790d9facda5Sclaudio return -1; 791d9facda5Sclaudio } 792d9facda5Sclaudio 7930314fe8bSclaudio if (rs->state != RTR_STATE_EXCHANGE) { 7949dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 7959dad7388Sclaudio log_rtr_type(ASPA)); 79683072fb6Sclaudio return -1; 79783072fb6Sclaudio } 79883072fb6Sclaudio 799623585daSclaudio /* treat ASPA records with too many SPAS like a withdraw */ 800623585daSclaudio if (cnt > MAX_ASPA_SPAS_COUNT) { 801623585daSclaudio struct aspa_set needle = { 0 }; 802623585daSclaudio needle.as = ntohl(rtr_aspa.cas); 803623585daSclaudio 804623585daSclaudio log_warnx("rtr %s: oversized ASPA PDU: " 805623585daSclaudio "imlicit withdraw of customerAS %s", 806623585daSclaudio log_rtr(rs), log_as(needle.as)); 807228f6bd1Sclaudio a = RB_FIND(aspa_tree, &rs->aspa, &needle); 808623585daSclaudio if (a != NULL) { 809228f6bd1Sclaudio RB_REMOVE(aspa_tree, &rs->aspa, a); 810623585daSclaudio free_aspa(a); 811623585daSclaudio } 812623585daSclaudio return 0; 813623585daSclaudio } 814623585daSclaudio 81583072fb6Sclaudio /* create aspa_set entry from the rtr aspa pdu */ 81683072fb6Sclaudio if ((aspa = calloc(1, sizeof(*aspa))) == NULL) { 8179dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, "out of memory"); 81883072fb6Sclaudio return -1; 81983072fb6Sclaudio } 82083072fb6Sclaudio aspa->as = ntohl(rtr_aspa.cas); 82183072fb6Sclaudio aspa->num = cnt; 82283072fb6Sclaudio if (cnt > 0) { 823c0c9c169Sclaudio if ((aspa->tas = calloc(cnt, sizeof(uint32_t))) == NULL) { 82483072fb6Sclaudio free_aspa(aspa); 8259dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, 8269dad7388Sclaudio "out of memory"); 82783072fb6Sclaudio return -1; 82883072fb6Sclaudio } 82983072fb6Sclaudio for (i = 0; i < cnt; i++) { 830*1cd81e65Sclaudio if (ibuf_get_n32(pdu, &aspa->tas[i]) == -1) { 831*1cd81e65Sclaudio free_aspa(aspa); 8329dad7388Sclaudio goto badlen; 83383072fb6Sclaudio } 83483072fb6Sclaudio } 835*1cd81e65Sclaudio } 83683072fb6Sclaudio 837228f6bd1Sclaudio if (flags & FLAG_ANNOUNCE) { 838228f6bd1Sclaudio a = RB_INSERT(aspa_tree, &rs->aspa, aspa); 83983072fb6Sclaudio if (a != NULL) { 840228f6bd1Sclaudio RB_REMOVE(aspa_tree, &rs->aspa, a); 84183072fb6Sclaudio free_aspa(a); 84283072fb6Sclaudio 843228f6bd1Sclaudio if (RB_INSERT(aspa_tree, &rs->aspa, aspa) != NULL) { 8449dad7388Sclaudio rtr_send_error(rs, NULL, INTERNAL_ERROR, 8459dad7388Sclaudio "corrupt aspa tree"); 84683072fb6Sclaudio free_aspa(aspa); 84783072fb6Sclaudio return -1; 84883072fb6Sclaudio } 84983072fb6Sclaudio } 85083072fb6Sclaudio } else { 851228f6bd1Sclaudio a = RB_FIND(aspa_tree, &rs->aspa, aspa); 85283072fb6Sclaudio if (a == NULL) { 8539dad7388Sclaudio rtr_send_error(rs, pdu, UNK_REC_WDRAWL, "%s %s", 8549dad7388Sclaudio log_rtr_type(ASPA), log_aspa(aspa)); 85583072fb6Sclaudio free_aspa(aspa); 85683072fb6Sclaudio return -1; 85783072fb6Sclaudio } 858228f6bd1Sclaudio RB_REMOVE(aspa_tree, &rs->aspa, a); 85983072fb6Sclaudio free_aspa(a); 86083072fb6Sclaudio free_aspa(aspa); 86183072fb6Sclaudio } 86283072fb6Sclaudio 86383072fb6Sclaudio return 0; 8649dad7388Sclaudio 8659dad7388Sclaudio badlen: 8669dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 8679dad7388Sclaudio log_rtr_type(ASPA)); 8689dad7388Sclaudio return -1; 86983072fb6Sclaudio } 87083072fb6Sclaudio 87183072fb6Sclaudio static int 8720d6339a2Sclaudio rtr_parse_end_of_data_v0(struct rtr_session *rs, struct ibuf *pdu) 8730d6339a2Sclaudio { 8740d6339a2Sclaudio struct rtr_endofdata_v0 eod; 8750d6339a2Sclaudio 8769dad7388Sclaudio if (ibuf_get(pdu, &eod, sizeof(eod)) == -1) 8779dad7388Sclaudio goto badlen; 8780d6339a2Sclaudio 8790d6339a2Sclaudio if (rtr_check_session_id(rs, rs->session_id, &eod.hdr, pdu) == -1) 8800d6339a2Sclaudio return -1; 8810d6339a2Sclaudio 8820d6339a2Sclaudio if (rs->state != RTR_STATE_EXCHANGE) { 8839dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 8849dad7388Sclaudio log_rtr_type(END_OF_DATA)); 8850d6339a2Sclaudio return -1; 8860d6339a2Sclaudio } 8870d6339a2Sclaudio 8880d6339a2Sclaudio rs->serial = ntohl(eod.serial); 8890d6339a2Sclaudio 8900d6339a2Sclaudio rtr_fsm(rs, RTR_EVNT_END_OF_DATA); 8910d6339a2Sclaudio return 0; 8929dad7388Sclaudio 8939dad7388Sclaudio badlen: 8949dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 8959dad7388Sclaudio log_rtr_type(END_OF_DATA)); 8969dad7388Sclaudio return -1; 8970d6339a2Sclaudio } 8980d6339a2Sclaudio 8990d6339a2Sclaudio static int 900b1793e8cSclaudio rtr_parse_end_of_data(struct rtr_session *rs, struct ibuf *pdu) 901bd9df44eSclaudio { 902bd9df44eSclaudio struct rtr_endofdata eod; 903bd9df44eSclaudio uint32_t t; 904bd9df44eSclaudio 9050d6339a2Sclaudio /* version 0 does not have the timing values */ 9060d6339a2Sclaudio if (rs->version == 0) 9070d6339a2Sclaudio return rtr_parse_end_of_data_v0(rs, pdu); 9080d6339a2Sclaudio 9099dad7388Sclaudio if (ibuf_get(pdu, &eod, sizeof(eod)) == -1) 9109dad7388Sclaudio goto badlen; 911bd9df44eSclaudio 91237149e4fSclaudio if (rtr_check_session_id(rs, rs->session_id, &eod.hdr, pdu) == -1) 91337149e4fSclaudio return -1; 91437149e4fSclaudio 9150314fe8bSclaudio if (rs->state != RTR_STATE_EXCHANGE) { 9169dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 9179dad7388Sclaudio log_rtr_type(END_OF_DATA)); 918bd9df44eSclaudio return -1; 919bd9df44eSclaudio } 920bd9df44eSclaudio 921bd9df44eSclaudio rs->serial = ntohl(eod.serial); 922bd9df44eSclaudio /* validate timer values to be in the right range */ 923bd9df44eSclaudio t = ntohl(eod.refresh); 924bd9df44eSclaudio if (t < 1 || t > 86400) 925bd9df44eSclaudio goto bad; 926bd9df44eSclaudio rs->refresh = t; 927bd9df44eSclaudio t = ntohl(eod.retry); 928bd9df44eSclaudio if (t < 1 || t > 7200) 929bd9df44eSclaudio goto bad; 930bd9df44eSclaudio rs->retry = t; 931bd9df44eSclaudio t = ntohl(eod.expire); 932bd9df44eSclaudio if (t < 600 || t > 172800) 933bd9df44eSclaudio goto bad; 934bd9df44eSclaudio if (t <= rs->retry || t <= rs->refresh) 935bd9df44eSclaudio goto bad; 936bd9df44eSclaudio rs->expire = t; 937bd9df44eSclaudio 938bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_END_OF_DATA); 939bd9df44eSclaudio return 0; 940bd9df44eSclaudio 941bd9df44eSclaudio bad: 9429dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad timeout values", 9439dad7388Sclaudio log_rtr_type(END_OF_DATA)); 9449dad7388Sclaudio return -1; 9459dad7388Sclaudio 9469dad7388Sclaudio badlen: 9479dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 9489dad7388Sclaudio log_rtr_type(END_OF_DATA)); 949bd9df44eSclaudio return -1; 950bd9df44eSclaudio } 951bd9df44eSclaudio 952bd9df44eSclaudio static int 953b1793e8cSclaudio rtr_parse_cache_reset(struct rtr_session *rs, struct ibuf *pdu) 954bd9df44eSclaudio { 95537149e4fSclaudio struct rtr_reset reset; 95637149e4fSclaudio 9579dad7388Sclaudio if (ibuf_get(pdu, &reset, sizeof(reset)) == -1) 9589dad7388Sclaudio goto badlen; 95937149e4fSclaudio 96037149e4fSclaudio if (rtr_check_session_id(rs, 0, &reset.hdr, pdu) == -1) 96137149e4fSclaudio return -1; 96237149e4fSclaudio 9630314fe8bSclaudio if (rs->state != RTR_STATE_ESTABLISHED) { 9649dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: out of context", 9659dad7388Sclaudio log_rtr_type(CACHE_RESET)); 966bd9df44eSclaudio return -1; 967bd9df44eSclaudio } 968bd9df44eSclaudio 969bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_CACHE_RESET); 970bd9df44eSclaudio return 0; 9719dad7388Sclaudio 9729dad7388Sclaudio badlen: 9739dad7388Sclaudio rtr_send_error(rs, pdu, CORRUPT_DATA, "%s: bad length", 9749dad7388Sclaudio log_rtr_type(CACHE_RESET)); 9759dad7388Sclaudio return -1; 976bd9df44eSclaudio } 977bd9df44eSclaudio 978bd9df44eSclaudio /* 979bd9df44eSclaudio * Parse an Error Response message. This function behaves a bit different 980bd9df44eSclaudio * from other parse functions since on error the connection needs to be 981bd9df44eSclaudio * dropped without sending an error response back. 982bd9df44eSclaudio */ 983bd9df44eSclaudio static int 984b1793e8cSclaudio rtr_parse_error(struct rtr_session *rs, struct ibuf *pdu) 985bd9df44eSclaudio { 986bd9df44eSclaudio struct rtr_header rh; 987b1793e8cSclaudio struct ibuf err_pdu; 988bd9df44eSclaudio uint32_t pdu_len, msg_len; 989bd9df44eSclaudio char *str = NULL; 990bd9df44eSclaudio uint16_t errcode; 991929e486eSclaudio int rv = -1; 992bd9df44eSclaudio 993b1793e8cSclaudio if (ibuf_get(pdu, &rh, sizeof(rh)) == -1) 994b1793e8cSclaudio goto fail; 995228f6bd1Sclaudio errcode = ntohs(rh.errcode); 996bd9df44eSclaudio 997b1793e8cSclaudio if (ibuf_get_n32(pdu, &pdu_len) == -1) 998b1793e8cSclaudio goto fail; 999bd9df44eSclaudio 1000bd9df44eSclaudio /* for now just ignore the embedded pdu */ 1001b1793e8cSclaudio if (ibuf_get_ibuf(pdu, pdu_len, &err_pdu) == -1) 1002b1793e8cSclaudio goto fail; 1003bd9df44eSclaudio 1004b1793e8cSclaudio if (ibuf_get_n32(pdu, &msg_len) == -1) 1005b1793e8cSclaudio goto fail; 1006bd9df44eSclaudio 1007b1793e8cSclaudio /* optional error msg */ 1008bd9df44eSclaudio if (msg_len != 0) 1009b1793e8cSclaudio if ((str = ibuf_get_string(pdu, msg_len)) == NULL) 1010b1793e8cSclaudio goto fail; 1011bd9df44eSclaudio 1012bd9df44eSclaudio log_warnx("rtr %s: received error: %s%s%s", log_rtr(rs), 1013bd9df44eSclaudio log_rtr_error(errcode), str ? ": " : "", str ? str : ""); 1014bd9df44eSclaudio 1015bd9df44eSclaudio if (errcode == NO_DATA_AVAILABLE) { 1016bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_NO_DATA); 1017929e486eSclaudio rv = 0; 1018125ef3d5Sclaudio } else if (errcode == UNSUPP_PROTOCOL_VERS) { 101983072fb6Sclaudio rtr_fsm(rs, RTR_EVNT_UNSUPP_PROTO_VERSION); 1020125ef3d5Sclaudio rv = 0; 1021125ef3d5Sclaudio } else 10222ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_RESET_AND_CLOSE); 1023929e486eSclaudio 1024bd9df44eSclaudio rs->last_recv_error = errcode; 1025bd9df44eSclaudio if (str) 1026b1793e8cSclaudio strlcpy(rs->last_recv_msg, str, sizeof(rs->last_recv_msg)); 1027bd9df44eSclaudio else 1028b1793e8cSclaudio memset(rs->last_recv_msg, 0, sizeof(rs->last_recv_msg)); 1029bd9df44eSclaudio 1030bd9df44eSclaudio free(str); 1031929e486eSclaudio return rv; 1032b1793e8cSclaudio 1033b1793e8cSclaudio fail: 1034b1793e8cSclaudio log_warnx("rtr %s: received %s: bad encoding", log_rtr(rs), 1035b1793e8cSclaudio log_rtr_type(ERROR_REPORT)); 1036b1793e8cSclaudio rtr_fsm(rs, RTR_EVNT_RESET_AND_CLOSE); 1037b1793e8cSclaudio return -1; 1038bd9df44eSclaudio } 1039bd9df44eSclaudio 1040bd9df44eSclaudio /* 1041bd9df44eSclaudio * Try to process received rtr message, it is possible that not a full 1042bd9df44eSclaudio * message is in the buffer. In that case stop, once new data is available 1043bd9df44eSclaudio * a retry will be done. 1044bd9df44eSclaudio */ 1045bd9df44eSclaudio static void 104622b46a1fSclaudio rtr_process_msg(struct rtr_session *rs, struct ibuf *msg) 1047bd9df44eSclaudio { 1048bd9df44eSclaudio enum rtr_pdu_type msgtype; 1049bd9df44eSclaudio 105022b46a1fSclaudio /* parse and check header */ 105122b46a1fSclaudio if (rtr_parse_header(rs, msg, &msgtype) == -1) 1052bd9df44eSclaudio return; 1053bd9df44eSclaudio 1054bd9df44eSclaudio switch (msgtype) { 1055bd9df44eSclaudio case SERIAL_NOTIFY: 105622b46a1fSclaudio if (rtr_parse_notify(rs, msg) == -1) 1057bd9df44eSclaudio return; 1058bd9df44eSclaudio break; 1059bd9df44eSclaudio case CACHE_RESPONSE: 106022b46a1fSclaudio if (rtr_parse_cache_response(rs, msg) == -1) 1061bd9df44eSclaudio return; 1062bd9df44eSclaudio break; 1063bd9df44eSclaudio case IPV4_PREFIX: 106422b46a1fSclaudio if (rtr_parse_ipv4_prefix(rs, msg) == -1) 1065bd9df44eSclaudio return; 1066bd9df44eSclaudio break; 1067bd9df44eSclaudio case IPV6_PREFIX: 106822b46a1fSclaudio if (rtr_parse_ipv6_prefix(rs, msg) == -1) 1069bd9df44eSclaudio return; 1070bd9df44eSclaudio break; 1071bd9df44eSclaudio case END_OF_DATA: 107222b46a1fSclaudio if (rtr_parse_end_of_data(rs, msg) == -1) 1073bd9df44eSclaudio return; 1074bd9df44eSclaudio break; 1075bd9df44eSclaudio case CACHE_RESET: 107622b46a1fSclaudio if (rtr_parse_cache_reset(rs, msg) == -1) 1077bd9df44eSclaudio return; 1078bd9df44eSclaudio break; 1079bd9df44eSclaudio case ROUTER_KEY: 1080bd9df44eSclaudio /* silently ignore router key */ 1081bd9df44eSclaudio break; 1082bd9df44eSclaudio case ERROR_REPORT: 108322b46a1fSclaudio if (rtr_parse_error(rs, msg) == -1) { 1084bd9df44eSclaudio /* no need to send back an error */ 1085bd9df44eSclaudio return; 1086b1793e8cSclaudio } 1087bd9df44eSclaudio break; 108883072fb6Sclaudio case ASPA: 108922b46a1fSclaudio if (rtr_parse_aspa(rs, msg) == -1) 109083072fb6Sclaudio return; 109183072fb6Sclaudio break; 1092bd9df44eSclaudio default: 10939dad7388Sclaudio /* unreachable, checked in rtr_parse_header() */ 109422b46a1fSclaudio rtr_send_error(rs, msg, UNSUPP_PDU_TYPE, "type %s", 10959dad7388Sclaudio log_rtr_type(msgtype)); 1096bd9df44eSclaudio return; 1097bd9df44eSclaudio } 1098bd9df44eSclaudio } 1099bd9df44eSclaudio 1100bd9df44eSclaudio /* 1101bd9df44eSclaudio * Simple FSM for RTR sessions 1102bd9df44eSclaudio */ 1103bd9df44eSclaudio static void 1104bd9df44eSclaudio rtr_fsm(struct rtr_session *rs, enum rtr_event event) 1105bd9df44eSclaudio { 1106bd9df44eSclaudio enum rtr_state prev_state = rs->state; 1107bd9df44eSclaudio 1108bd9df44eSclaudio switch (event) { 110983072fb6Sclaudio case RTR_EVNT_UNSUPP_PROTO_VERSION: 1110d87cfbccSclaudio if (rs->prev_version == rs->version || 1111d87cfbccSclaudio rs->version < rs->min_version) { 111283072fb6Sclaudio /* 1113125ef3d5Sclaudio * Can't downgrade anymore, fail connection. 1114125ef3d5Sclaudio * RFC requires sending the error with the 1115125ef3d5Sclaudio * highest supported version number. 111683072fb6Sclaudio */ 1117d87cfbccSclaudio rs->version = rtr_max_session_version(rs); 11189dad7388Sclaudio rtr_send_error(rs, NULL, UNSUPP_PROTOCOL_VERS, 11199dad7388Sclaudio "negotiation failed"); 112083072fb6Sclaudio return; 112183072fb6Sclaudio } 1122125ef3d5Sclaudio /* try again with new version */ 1123125ef3d5Sclaudio if (rs->session_id == -1) 1124125ef3d5Sclaudio rtr_send_reset_query(rs); 1125125ef3d5Sclaudio else 1126125ef3d5Sclaudio rtr_send_serial_query(rs); 112783072fb6Sclaudio break; 11282ced3cdbSclaudio case RTR_EVNT_RESET_AND_CLOSE: 11295a04dc7fSclaudio rtr_reset_cache(rs); 11305a04dc7fSclaudio rtr_recalc(); 11312ced3cdbSclaudio /* FALLTHROUGH */ 11322ced3cdbSclaudio case RTR_EVNT_CON_CLOSE: 11335a04dc7fSclaudio if (rs->fd != -1) { 1134bd9df44eSclaudio /* flush buffers */ 113505453d67Sclaudio msgbuf_clear(rs->w); 1136bd9df44eSclaudio close(rs->fd); 1137bd9df44eSclaudio rs->fd = -1; 1138cd16358eSclaudio rtr_imsg_compose(IMSG_SOCKET_TEARDOWN, rs->id, 0, 1139cd16358eSclaudio NULL, 0); 1140bd9df44eSclaudio } 1141bd9df44eSclaudio /* try to reopen session */ 1142d87cfbccSclaudio if (!rs->errored) 1143bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Retry, 1144bd9df44eSclaudio arc4random_uniform(10)); 1145d87cfbccSclaudio else 1146d87cfbccSclaudio timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry); 1147d87cfbccSclaudio 1148d87cfbccSclaudio rs->errored = 1; 1149125ef3d5Sclaudio /* 1150125ef3d5Sclaudio * A close event during version negotiation needs to remain 1151125ef3d5Sclaudio * in the negotiation state else the same error will happen 1152125ef3d5Sclaudio * over and over again. The RFC is utterly underspecified 1153125ef3d5Sclaudio * and some RTR caches close the connection after sending 1154125ef3d5Sclaudio * the error PDU. 1155125ef3d5Sclaudio */ 1156125ef3d5Sclaudio if (rs->state != RTR_STATE_NEGOTIATION) 1157125ef3d5Sclaudio rs->state = RTR_STATE_CLOSED; 1158bd9df44eSclaudio break; 1159bd9df44eSclaudio case RTR_EVNT_START: 1160bd9df44eSclaudio case RTR_EVNT_TIMER_RETRY: 1161bd9df44eSclaudio switch (rs->state) { 1162bd9df44eSclaudio case RTR_STATE_ERROR: 11632ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1164125ef3d5Sclaudio break; 1165bd9df44eSclaudio case RTR_STATE_CLOSED: 1166125ef3d5Sclaudio case RTR_STATE_NEGOTIATION: 1167bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry); 1168cd16358eSclaudio rtr_imsg_compose(IMSG_SOCKET_SETUP, rs->id, 0, NULL, 0); 1169125ef3d5Sclaudio break; 1170a62144a2Sclaudio case RTR_STATE_ESTABLISHED: 1171a62144a2Sclaudio if (rs->session_id == -1) 1172a62144a2Sclaudio rtr_send_reset_query(rs); 1173a62144a2Sclaudio else 1174a62144a2Sclaudio rtr_send_serial_query(rs); 1175bd9df44eSclaudio default: 1176bd9df44eSclaudio break; 1177bd9df44eSclaudio } 1178125ef3d5Sclaudio break; 1179bd9df44eSclaudio case RTR_EVNT_CON_OPEN: 1180bd9df44eSclaudio timer_stop(&rs->timers, Timer_Rtr_Retry); 1181125ef3d5Sclaudio rs->state = RTR_STATE_NEGOTIATION; 1182bd9df44eSclaudio if (rs->session_id == -1) 11835a04dc7fSclaudio rtr_send_reset_query(rs); 1184bd9df44eSclaudio else 11855a04dc7fSclaudio rtr_send_serial_query(rs); 1186bd9df44eSclaudio break; 1187bd9df44eSclaudio case RTR_EVNT_SERIAL_NOTIFY: 1188bd9df44eSclaudio /* schedule a refresh after a quick wait */ 1189bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Refresh, 1190bd9df44eSclaudio arc4random_uniform(10)); 1191bd9df44eSclaudio break; 1192bd9df44eSclaudio case RTR_EVNT_TIMER_REFRESH: 11935a04dc7fSclaudio rtr_send_serial_query(rs); 1194bd9df44eSclaudio break; 1195bd9df44eSclaudio case RTR_EVNT_TIMER_EXPIRE: 11965a04dc7fSclaudio rtr_reset_cache(rs); 1197bd9df44eSclaudio rtr_recalc(); 1198bd9df44eSclaudio break; 1199dfd27b08Sclaudio case RTR_EVNT_TIMER_ACTIVE: 1200dfd27b08Sclaudio log_warnx("rtr %s: activity timer fired", log_rtr(rs)); 1201dfd27b08Sclaudio rtr_sem_release(rs->active_lock); 1202dfd27b08Sclaudio rtr_recalc(); 1203dfd27b08Sclaudio rs->active_lock = 0; 1204dfd27b08Sclaudio break; 1205bd9df44eSclaudio case RTR_EVNT_CACHE_RESPONSE: 12060314fe8bSclaudio rs->state = RTR_STATE_EXCHANGE; 1207bd9df44eSclaudio timer_stop(&rs->timers, Timer_Rtr_Refresh); 1208bd9df44eSclaudio timer_stop(&rs->timers, Timer_Rtr_Retry); 1209dfd27b08Sclaudio timer_set(&rs->timers, Timer_Rtr_Active, rs->active); 1210dfd27b08Sclaudio /* prevent rtr_recalc from running while active */ 1211dfd27b08Sclaudio rs->active_lock = 1; 1212dfd27b08Sclaudio rtr_sem_acquire(rs->active_lock); 1213bd9df44eSclaudio break; 1214bd9df44eSclaudio case RTR_EVNT_END_OF_DATA: 1215bd9df44eSclaudio /* start refresh and expire timers */ 1216bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Refresh, rs->refresh); 1217bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire); 1218dfd27b08Sclaudio timer_stop(&rs->timers, Timer_Rtr_Active); 12190314fe8bSclaudio rs->state = RTR_STATE_ESTABLISHED; 1220dfd27b08Sclaudio rtr_sem_release(rs->active_lock); 1221bd9df44eSclaudio rtr_recalc(); 1222dfd27b08Sclaudio rs->active_lock = 0; 1223d87cfbccSclaudio rs->errored = 0; 1224047cb73cSclaudio /* clear the last errors */ 1225047cb73cSclaudio rs->last_sent_error = NO_ERROR; 1226047cb73cSclaudio rs->last_recv_error = NO_ERROR; 1227047cb73cSclaudio rs->last_sent_msg[0] = '\0'; 1228047cb73cSclaudio rs->last_recv_msg[0] = '\0'; 1229bd9df44eSclaudio break; 1230bd9df44eSclaudio case RTR_EVNT_CACHE_RESET: 12315a04dc7fSclaudio rtr_reset_cache(rs); 12322ced3cdbSclaudio rtr_recalc(); 12335a04dc7fSclaudio /* retry after a quick wait */ 1234bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Retry, 1235bd9df44eSclaudio arc4random_uniform(10)); 1236bd9df44eSclaudio break; 1237bd9df44eSclaudio case RTR_EVNT_NO_DATA: 1238bd9df44eSclaudio /* start retry timer */ 1239bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Retry, rs->retry); 1240bd9df44eSclaudio /* stop refresh timer just to be sure */ 1241bd9df44eSclaudio timer_stop(&rs->timers, Timer_Rtr_Refresh); 12420314fe8bSclaudio rs->state = RTR_STATE_ESTABLISHED; 1243bd9df44eSclaudio break; 1244bd9df44eSclaudio case RTR_EVNT_SEND_ERROR: 12455a04dc7fSclaudio rtr_reset_cache(rs); 12465a04dc7fSclaudio rtr_recalc(); 1247bd9df44eSclaudio rs->state = RTR_STATE_ERROR; 1248bd9df44eSclaudio break; 124937149e4fSclaudio case RTR_EVNT_NEGOTIATION_DONE: 125037149e4fSclaudio rs->state = RTR_STATE_ESTABLISHED; 125137149e4fSclaudio break; 1252bd9df44eSclaudio } 1253bd9df44eSclaudio 1254fba72c10Sclaudio log_debug("rtr %s: state change %s -> %s, reason: %s", 1255bd9df44eSclaudio log_rtr(rs), rtr_statenames[prev_state], rtr_statenames[rs->state], 1256bd9df44eSclaudio rtr_eventnames[event]); 1257bd9df44eSclaudio } 1258bd9df44eSclaudio 1259bd9df44eSclaudio /* 1260bd9df44eSclaudio * IO handler for RTR sessions 1261bd9df44eSclaudio */ 1262bd9df44eSclaudio static void 1263bd9df44eSclaudio rtr_dispatch_msg(struct pollfd *pfd, struct rtr_session *rs) 1264bd9df44eSclaudio { 126522b46a1fSclaudio struct ibuf *b; 1266bd9df44eSclaudio 1267bd9df44eSclaudio if (pfd->revents & POLLHUP) { 1268bd9df44eSclaudio log_warnx("rtr %s: Connection closed, hangup", log_rtr(rs)); 12692ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1270bd9df44eSclaudio return; 1271bd9df44eSclaudio } 1272bd9df44eSclaudio if (pfd->revents & (POLLERR|POLLNVAL)) { 1273bd9df44eSclaudio log_warnx("rtr %s: Connection closed, error", log_rtr(rs)); 12742ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1275bd9df44eSclaudio return; 1276bd9df44eSclaudio } 127705453d67Sclaudio if (pfd->revents & POLLOUT && msgbuf_queuelen(rs->w) > 0) { 127805453d67Sclaudio if (ibuf_write(rs->fd, rs->w) == -1) { 1279bd9df44eSclaudio log_warn("rtr %s: write error", log_rtr(rs)); 12802ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 12814fb43511Sclaudio return; 128283072fb6Sclaudio } 1283db359c81Sclaudio if (rs->state == RTR_STATE_ERROR && 128405453d67Sclaudio msgbuf_queuelen(rs->w) == 0) 12852ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1286bd9df44eSclaudio } 1287bd9df44eSclaudio if (pfd->revents & POLLIN) { 128822b46a1fSclaudio switch (ibuf_read(rs->fd, rs->w)) { 128922b46a1fSclaudio case -1: 129022b46a1fSclaudio /* if already in error state, ignore */ 129122b46a1fSclaudio if (rs->state == RTR_STATE_ERROR) 129222b46a1fSclaudio return; 1293bd9df44eSclaudio log_warn("rtr %s: read error", log_rtr(rs)); 12942ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1295bd9df44eSclaudio return; 129622b46a1fSclaudio case 0: 12972ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1298bd9df44eSclaudio return; 1299bd9df44eSclaudio } 1300bd9df44eSclaudio /* new data arrived, try to process it */ 130122b46a1fSclaudio while ((b = msgbuf_get(rs->w)) != NULL) { 130222b46a1fSclaudio rtr_process_msg(rs, b); 130322b46a1fSclaudio ibuf_free(b); 1304bd9df44eSclaudio } 130522b46a1fSclaudio } 1306bd9df44eSclaudio } 1307bd9df44eSclaudio 1308bd9df44eSclaudio void 1309bd9df44eSclaudio rtr_check_events(struct pollfd *pfds, size_t npfds) 1310bd9df44eSclaudio { 1311bd9df44eSclaudio struct rtr_session *rs; 1312bd9df44eSclaudio struct timer *t; 1313bd9df44eSclaudio time_t now; 1314bd9df44eSclaudio size_t i = 0; 1315bd9df44eSclaudio 1316bd9df44eSclaudio for (i = 0; i < npfds; i++) { 1317bd9df44eSclaudio if (pfds[i].revents == 0) 1318bd9df44eSclaudio continue; 1319bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) 1320bd9df44eSclaudio if (rs->fd == pfds[i].fd) { 1321bd9df44eSclaudio rtr_dispatch_msg(&pfds[i], rs); 1322bd9df44eSclaudio break; 1323bd9df44eSclaudio } 1324bd9df44eSclaudio if (rs == NULL) 1325bd9df44eSclaudio log_warnx("%s: unknown fd in pollfds", __func__); 1326bd9df44eSclaudio } 1327bd9df44eSclaudio 1328bd9df44eSclaudio /* run all timers */ 1329bd9df44eSclaudio now = getmonotime(); 1330bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) 1331bd9df44eSclaudio if ((t = timer_nextisdue(&rs->timers, now)) != NULL) { 1332bd9df44eSclaudio /* stop timer so it does not trigger again */ 1333bd9df44eSclaudio timer_stop(&rs->timers, t->type); 1334bd9df44eSclaudio switch (t->type) { 1335bd9df44eSclaudio case Timer_Rtr_Refresh: 1336bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_TIMER_REFRESH); 1337bd9df44eSclaudio break; 1338bd9df44eSclaudio case Timer_Rtr_Retry: 1339bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_TIMER_RETRY); 1340bd9df44eSclaudio break; 1341bd9df44eSclaudio case Timer_Rtr_Expire: 1342bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_TIMER_EXPIRE); 1343bd9df44eSclaudio break; 1344dfd27b08Sclaudio case Timer_Rtr_Active: 1345dfd27b08Sclaudio rtr_fsm(rs, RTR_EVNT_TIMER_ACTIVE); 1346dfd27b08Sclaudio break; 1347bd9df44eSclaudio default: 1348bd9df44eSclaudio fatalx("King Bula lost in time"); 1349bd9df44eSclaudio } 1350bd9df44eSclaudio } 1351bd9df44eSclaudio } 1352bd9df44eSclaudio 1353bd9df44eSclaudio size_t 1354bd9df44eSclaudio rtr_count(void) 1355bd9df44eSclaudio { 1356bd9df44eSclaudio struct rtr_session *rs; 1357bd9df44eSclaudio size_t count = 0; 1358bd9df44eSclaudio 1359bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) 1360bd9df44eSclaudio count++; 1361bd9df44eSclaudio return count; 1362bd9df44eSclaudio } 1363bd9df44eSclaudio 1364bd9df44eSclaudio size_t 1365bd9df44eSclaudio rtr_poll_events(struct pollfd *pfds, size_t npfds, time_t *timeout) 1366bd9df44eSclaudio { 1367bd9df44eSclaudio struct rtr_session *rs; 1368bd9df44eSclaudio time_t now = getmonotime(); 1369bd9df44eSclaudio size_t i = 0; 1370bd9df44eSclaudio 1371bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) { 1372bd9df44eSclaudio time_t nextaction; 1373bd9df44eSclaudio struct pollfd *pfd = pfds + i++; 1374bd9df44eSclaudio 1375bd9df44eSclaudio if (i > npfds) 1376bd9df44eSclaudio fatalx("%s: too many sessions for pollfd", __func__); 1377bd9df44eSclaudio 1378bd9df44eSclaudio if ((nextaction = timer_nextduein(&rs->timers, now)) != -1 && 1379bd9df44eSclaudio nextaction < *timeout) 1380bd9df44eSclaudio *timeout = nextaction; 1381bd9df44eSclaudio 1382bd9df44eSclaudio if (rs->state == RTR_STATE_CLOSED) { 1383bd9df44eSclaudio pfd->fd = -1; 1384bd9df44eSclaudio continue; 1385bd9df44eSclaudio } 1386bd9df44eSclaudio 1387bd9df44eSclaudio pfd->fd = rs->fd; 1388bd9df44eSclaudio pfd->events = 0; 1389bd9df44eSclaudio 139005453d67Sclaudio if (msgbuf_queuelen(rs->w) > 0) 1391bd9df44eSclaudio pfd->events |= POLLOUT; 13920314fe8bSclaudio if (rs->state >= RTR_STATE_ESTABLISHED) 1393bd9df44eSclaudio pfd->events |= POLLIN; 1394bd9df44eSclaudio } 1395bd9df44eSclaudio 1396bd9df44eSclaudio return i; 1397bd9df44eSclaudio } 1398bd9df44eSclaudio 1399bd9df44eSclaudio struct rtr_session * 1400d87cfbccSclaudio rtr_new(uint32_t id, struct rtr_config_msg *conf) 1401bd9df44eSclaudio { 1402bd9df44eSclaudio struct rtr_session *rs; 1403bd9df44eSclaudio 1404bd9df44eSclaudio if ((rs = calloc(1, sizeof(*rs))) == NULL) 1405d87cfbccSclaudio fatal("RTR session %s", conf->descr); 140622b46a1fSclaudio if ((rs->w = msgbuf_new_reader(sizeof(struct rtr_header), 14079509a1e6Sclaudio rtr_reader_callback, rs)) == NULL) 140805453d67Sclaudio fatal("RTR session %s", conf->descr); 1409bd9df44eSclaudio 1410bd9df44eSclaudio RB_INIT(&rs->roa_set); 1411c0c9c169Sclaudio RB_INIT(&rs->aspa); 1412bd9df44eSclaudio TAILQ_INIT(&rs->timers); 1413bd9df44eSclaudio 1414d87cfbccSclaudio strlcpy(rs->descr, conf->descr, sizeof(rs->descr)); 1415bd9df44eSclaudio rs->id = id; 1416bd9df44eSclaudio rs->session_id = -1; 1417d87cfbccSclaudio rs->min_version = conf->min_version; /* must be set before version */ 1418d87cfbccSclaudio rs->version = rtr_max_session_version(rs); 1419d87cfbccSclaudio rs->prev_version = rtr_max_session_version(rs); 1420bd9df44eSclaudio rs->refresh = RTR_DEFAULT_REFRESH; 1421bd9df44eSclaudio rs->retry = RTR_DEFAULT_RETRY; 1422bd9df44eSclaudio rs->expire = RTR_DEFAULT_EXPIRE; 1423dfd27b08Sclaudio rs->active = RTR_DEFAULT_ACTIVE; 1424bd9df44eSclaudio rs->state = RTR_STATE_CLOSED; 1425bd9df44eSclaudio rs->reconf_action = RECONF_REINIT; 1426bd9df44eSclaudio rs->last_recv_error = NO_ERROR; 1427bd9df44eSclaudio rs->last_sent_error = NO_ERROR; 1428bd9df44eSclaudio 1429bd9df44eSclaudio /* make sure that some timer is running to abort bad sessions */ 1430bd9df44eSclaudio timer_set(&rs->timers, Timer_Rtr_Expire, rs->expire); 1431bd9df44eSclaudio 1432bd9df44eSclaudio log_debug("rtr %s: new session, start", log_rtr(rs)); 1433bd9df44eSclaudio TAILQ_INSERT_TAIL(&rtrs, rs, entry); 1434bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_START); 1435bd9df44eSclaudio 1436bd9df44eSclaudio return rs; 1437bd9df44eSclaudio } 1438bd9df44eSclaudio 1439bd9df44eSclaudio struct rtr_session * 1440bd9df44eSclaudio rtr_get(uint32_t id) 1441bd9df44eSclaudio { 1442bd9df44eSclaudio struct rtr_session *rs; 1443bd9df44eSclaudio 1444bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) 1445bd9df44eSclaudio if (rs->id == id) 1446bd9df44eSclaudio return rs; 1447bd9df44eSclaudio return NULL; 1448bd9df44eSclaudio } 1449bd9df44eSclaudio 1450bd9df44eSclaudio void 1451bd9df44eSclaudio rtr_free(struct rtr_session *rs) 1452bd9df44eSclaudio { 1453bd9df44eSclaudio if (rs == NULL) 1454bd9df44eSclaudio return; 1455bd9df44eSclaudio 14565a04dc7fSclaudio rtr_reset_cache(rs); 14572ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1458bd9df44eSclaudio timer_remove_all(&rs->timers); 145905453d67Sclaudio msgbuf_free(rs->w); 1460bd9df44eSclaudio free(rs); 1461bd9df44eSclaudio } 1462bd9df44eSclaudio 1463bd9df44eSclaudio void 1464bd9df44eSclaudio rtr_open(struct rtr_session *rs, int fd) 1465bd9df44eSclaudio { 146683072fb6Sclaudio if (rs->state != RTR_STATE_CLOSED && 146783072fb6Sclaudio rs->state != RTR_STATE_NEGOTIATION) { 1468bd9df44eSclaudio log_warnx("rtr %s: bad session state", log_rtr(rs)); 14692ced3cdbSclaudio rtr_fsm(rs, RTR_EVNT_CON_CLOSE); 1470bd9df44eSclaudio } 1471bd9df44eSclaudio 1472125ef3d5Sclaudio if (rs->state == RTR_STATE_CLOSED) { 1473d87cfbccSclaudio rs->version = rtr_max_session_version(rs); 1474d87cfbccSclaudio rs->prev_version = rtr_max_session_version(rs); 1475125ef3d5Sclaudio } 1476bd9df44eSclaudio 1477bb561412Sclaudio rs->fd = fd; 1478bd9df44eSclaudio rtr_fsm(rs, RTR_EVNT_CON_OPEN); 1479bd9df44eSclaudio } 1480bd9df44eSclaudio 1481bd9df44eSclaudio void 1482bd9df44eSclaudio rtr_config_prep(void) 1483bd9df44eSclaudio { 1484bd9df44eSclaudio struct rtr_session *rs; 1485bd9df44eSclaudio 1486bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) 1487bd9df44eSclaudio rs->reconf_action = RECONF_DELETE; 1488bd9df44eSclaudio } 1489bd9df44eSclaudio 1490bd9df44eSclaudio void 1491bd9df44eSclaudio rtr_config_merge(void) 1492bd9df44eSclaudio { 1493bd9df44eSclaudio struct rtr_session *rs, *nrs; 1494bd9df44eSclaudio 1495bd9df44eSclaudio TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs) 1496bd9df44eSclaudio if (rs->reconf_action == RECONF_DELETE) { 1497bd9df44eSclaudio TAILQ_REMOVE(&rtrs, rs, entry); 1498bd9df44eSclaudio rtr_free(rs); 1499bd9df44eSclaudio } 1500bd9df44eSclaudio } 1501bd9df44eSclaudio 1502bd9df44eSclaudio void 1503d87cfbccSclaudio rtr_config_keep(struct rtr_session *rs, struct rtr_config_msg *conf) 1504bd9df44eSclaudio { 1505d87cfbccSclaudio strlcpy(rs->descr, conf->descr, sizeof(rs->descr)); 1506d87cfbccSclaudio rs->min_version = conf->min_version; 1507bd9df44eSclaudio rs->reconf_action = RECONF_KEEP; 1508bd9df44eSclaudio } 1509bd9df44eSclaudio 1510bd9df44eSclaudio void 1511bd9df44eSclaudio rtr_roa_merge(struct roa_tree *rt) 1512bd9df44eSclaudio { 1513bd9df44eSclaudio struct rtr_session *rs; 1514bd9df44eSclaudio struct roa *roa; 1515bd9df44eSclaudio 1516bd9df44eSclaudio TAILQ_FOREACH(rs, &rtrs, entry) { 1517bd9df44eSclaudio RB_FOREACH(roa, roa_tree, &rs->roa_set) 151883072fb6Sclaudio rtr_roa_insert(rt, roa); 151983072fb6Sclaudio } 152083072fb6Sclaudio } 152183072fb6Sclaudio 152283072fb6Sclaudio void 152383072fb6Sclaudio rtr_aspa_merge(struct aspa_tree *at) 152483072fb6Sclaudio { 152583072fb6Sclaudio struct rtr_session *rs; 152683072fb6Sclaudio struct aspa_set *aspa; 152783072fb6Sclaudio 152883072fb6Sclaudio TAILQ_FOREACH(rs, &rtrs, entry) { 1529c0c9c169Sclaudio RB_FOREACH(aspa, aspa_tree, &rs->aspa) 153083072fb6Sclaudio rtr_aspa_insert(at, aspa); 1531bd9df44eSclaudio } 1532bd9df44eSclaudio } 1533bd9df44eSclaudio 1534bd9df44eSclaudio void 1535bd9df44eSclaudio rtr_shutdown(void) 1536bd9df44eSclaudio { 1537bd9df44eSclaudio struct rtr_session *rs, *nrs; 1538bd9df44eSclaudio 1539bd9df44eSclaudio TAILQ_FOREACH_SAFE(rs, &rtrs, entry, nrs) 1540bd9df44eSclaudio rtr_free(rs); 1541bd9df44eSclaudio } 1542bd9df44eSclaudio 1543bd9df44eSclaudio void 1544bd9df44eSclaudio rtr_show(struct rtr_session *rs, pid_t pid) 1545bd9df44eSclaudio { 1546bd9df44eSclaudio struct ctl_show_rtr msg; 1547bd9df44eSclaudio struct ctl_timer ct; 1548bd9df44eSclaudio u_int i; 1549bd9df44eSclaudio time_t d; 1550bd9df44eSclaudio 1551bd9df44eSclaudio memset(&msg, 0, sizeof(msg)); 1552bd9df44eSclaudio 1553bd9df44eSclaudio /* descr, remote_addr, local_addr and remote_port set by parent */ 155483072fb6Sclaudio msg.version = rs->version; 1555d87cfbccSclaudio msg.min_version = rs->min_version; 1556bd9df44eSclaudio msg.serial = rs->serial; 1557bd9df44eSclaudio msg.refresh = rs->refresh; 1558bd9df44eSclaudio msg.retry = rs->retry; 1559bd9df44eSclaudio msg.expire = rs->expire; 1560bd9df44eSclaudio msg.session_id = rs->session_id; 1561bd9df44eSclaudio msg.last_sent_error = rs->last_sent_error; 1562bd9df44eSclaudio msg.last_recv_error = rs->last_recv_error; 156333c73471Sclaudio strlcpy(msg.state, rtr_statenames[rs->state], sizeof(msg.state)); 1564bd9df44eSclaudio strlcpy(msg.last_sent_msg, rs->last_sent_msg, 1565bd9df44eSclaudio sizeof(msg.last_sent_msg)); 1566bd9df44eSclaudio strlcpy(msg.last_recv_msg, rs->last_recv_msg, 1567bd9df44eSclaudio sizeof(msg.last_recv_msg)); 1568bd9df44eSclaudio 1569bd9df44eSclaudio /* send back imsg */ 1570bd9df44eSclaudio rtr_imsg_compose(IMSG_CTL_SHOW_RTR, rs->id, pid, &msg, sizeof(msg)); 1571bd9df44eSclaudio 1572bd9df44eSclaudio /* send back timer imsgs */ 1573bd9df44eSclaudio for (i = 1; i < Timer_Max; i++) { 1574bd9df44eSclaudio if (!timer_running(&rs->timers, i, &d)) 1575bd9df44eSclaudio continue; 1576bd9df44eSclaudio ct.type = i; 1577bd9df44eSclaudio ct.val = d; 1578bd9df44eSclaudio rtr_imsg_compose(IMSG_CTL_SHOW_TIMER, rs->id, pid, 1579bd9df44eSclaudio &ct, sizeof(ct)); 1580bd9df44eSclaudio } 1581bd9df44eSclaudio } 1582