-/* $OpenBSD: session.c,v 1.476 2024/05/16 09:38:21 claudio Exp $ */
+/* $OpenBSD: session.c,v 1.477 2024/05/20 10:01:52 claudio Exp $ */
/*
* Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
int parse_update(struct peer *);
int parse_rrefresh(struct peer *);
void parse_notification(struct peer *);
-int parse_capabilities(struct peer *, u_char *, uint16_t, uint32_t *);
+int parse_capabilities(struct peer *, struct ibuf *, uint32_t *);
int capa_neg_calc(struct peer *);
void session_dispatch_imsg(struct imsgbuf *, int, u_int *);
void session_up(struct peer *);
struct mrt_head mrthead;
time_t pauseaccept;
+static const uint8_t marker[MSGSIZE_HEADER_MARKER] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
static inline int
peer_compare(const struct peer *a, const struct peer *b)
{
struct bgp_msg *
session_newmsg(enum msg_type msgtype, uint16_t len)
{
- u_char marker[MSGSIZE_HEADER_MARKER];
struct bgp_msg *msg;
struct ibuf *buf;
int errs = 0;
- memset(marker, 0xff, sizeof(marker));
-
if ((buf = ibuf_open(len)) == NULL)
return (NULL);
{
u_char *p;
uint16_t olen;
- static const uint8_t marker[MSGSIZE_HEADER_MARKER] = { 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* caller MUST make sure we are getting 19 bytes! */
p = data;
int
parse_open(struct peer *peer)
{
- u_char *p, *op_val;
+ struct ibuf ibuf;
+ u_char *p;
uint8_t version, rversion;
uint16_t short_as, msglen;
- uint16_t holdtime, oholdtime, myholdtime;
+ uint16_t holdtime, myholdtime;
uint32_t as, bgpid;
- uint16_t optparamlen, extlen, plen, op_len;
- uint8_t op_type;
+ uint8_t optparamlen;
p = peer->rbuf->rptr;
p += MSGSIZE_HEADER_MARKER;
p = peer->rbuf->rptr;
p += MSGSIZE_HEADER; /* header is already checked */
+ msglen -= MSGSIZE_HEADER;
- memcpy(&version, p, sizeof(version));
- p += sizeof(version);
+ /* XXX */
+ ibuf_from_buffer(&ibuf, p, msglen);
+
+ if (ibuf_get_n8(&ibuf, &version) == -1 ||
+ ibuf_get_n16(&ibuf, &short_as) == -1 ||
+ ibuf_get_n16(&ibuf, &holdtime) == -1 ||
+ ibuf_get_n32(&ibuf, &bgpid) == -1 ||
+ ibuf_get_n8(&ibuf, &optparamlen) == -1)
+ goto bad_len;
if (version != BGP_VERSION) {
log_peer_warnx(&peer->conf,
return (-1);
}
- memcpy(&short_as, p, sizeof(short_as));
- p += sizeof(short_as);
- as = peer->short_as = ntohs(short_as);
+ as = peer->short_as = short_as;
if (as == 0) {
log_peer_warnx(&peer->conf,
"peer requests unacceptable AS %u", as);
return (-1);
}
- memcpy(&oholdtime, p, sizeof(oholdtime));
- p += sizeof(oholdtime);
-
- holdtime = ntohs(oholdtime);
if (holdtime && holdtime < peer->conf.min_holdtime) {
log_peer_warnx(&peer->conf,
"peer requests unacceptable holdtime %u", holdtime);
else
peer->holdtime = myholdtime;
- memcpy(&bgpid, p, sizeof(bgpid));
- p += sizeof(bgpid);
-
/* check bgpid for validity - just disallow 0 */
- if (ntohl(bgpid) == 0) {
- log_peer_warnx(&peer->conf, "peer BGPID %u unacceptable",
- ntohl(bgpid));
+ if (bgpid == 0) {
+ log_peer_warnx(&peer->conf, "peer BGPID 0 unacceptable");
session_notification(peer, ERR_OPEN, ERR_OPEN_BGPID, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
- peer->remote_bgpid = bgpid;
+ /* strore remote_bgpid in network byte order */
+ peer->remote_bgpid = htonl(bgpid);
- extlen = 0;
- optparamlen = *p++;
+ if (optparamlen != 0) {
+ struct ibuf oparams, op;
+ uint8_t ext_type, op_type;
+ uint16_t ext_len, op_len;
- if (optparamlen == 0) {
- if (msglen != MSGSIZE_OPEN_MIN) {
-bad_len:
- log_peer_warnx(&peer->conf,
- "corrupt OPEN message received: length mismatch");
- session_notification(peer, ERR_OPEN, 0, NULL);
- change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
- return (-1);
- }
- } else {
- if (msglen < MSGSIZE_OPEN_MIN + 1)
- goto bad_len;
+ ibuf_from_ibuf(&oparams, &ibuf);
- op_type = *p;
- if (op_type == OPT_PARAM_EXT_LEN) {
- p++;
- memcpy(&optparamlen, p, sizeof(optparamlen));
- optparamlen = ntohs(optparamlen);
- p += sizeof(optparamlen);
- extlen = 1;
+ /* check for RFC9072 encoding */
+ if (ibuf_get_n8(&oparams, &ext_type) == -1)
+ goto bad_len;
+ if (ext_type == OPT_PARAM_EXT_LEN) {
+ if (ibuf_get_n16(&oparams, &ext_len) == -1)
+ goto bad_len;
+ /* skip RFC9072 header */
+ if (ibuf_skip(&ibuf, 3) == -1)
+ goto bad_len;
+ } else {
+ ext_len = optparamlen;
+ ibuf_rewind(&oparams);
}
- /* RFC9020 encoding has 3 extra bytes */
- if (optparamlen + 3 * extlen != msglen - MSGSIZE_OPEN_MIN)
+ if (ibuf_truncate(&oparams, ext_len) == -1 ||
+ ibuf_skip(&ibuf, ext_len) == -1)
goto bad_len;
- }
- plen = optparamlen;
- while (plen > 0) {
- if (plen < 2 + extlen)
- goto bad_len;
+ while (ibuf_size(&oparams) > 0) {
+ if (ibuf_get_n8(&oparams, &op_type) == -1)
+ goto bad_len;
- memcpy(&op_type, p, sizeof(op_type));
- p += sizeof(op_type);
- plen -= sizeof(op_type);
- if (!extlen) {
- op_len = *p++;
- plen--;
- } else {
- memcpy(&op_len, p, sizeof(op_len));
- op_len = ntohs(op_len);
- p += sizeof(op_len);
- plen -= sizeof(op_len);
- }
- if (op_len > 0) {
- if (plen < op_len)
+ if (ext_type == OPT_PARAM_EXT_LEN) {
+ if (ibuf_get_n16(&oparams, &op_len) == -1)
+ goto bad_len;
+ } else {
+ uint8_t tmp;
+ if (ibuf_get_n8(&oparams, &tmp) == -1)
+ goto bad_len;
+ op_len = tmp;
+ }
+
+ if (ibuf_get_ibuf(&oparams, op_len, &op) == -1)
goto bad_len;
- op_val = p;
- p += op_len;
- plen -= op_len;
- } else
- op_val = NULL;
- switch (op_type) {
- case OPT_PARAM_CAPABILITIES: /* RFC 3392 */
- if (parse_capabilities(peer, op_val, op_len,
- &as) == -1) {
- session_notification(peer, ERR_OPEN, 0, NULL);
+ switch (op_type) {
+ case OPT_PARAM_CAPABILITIES: /* RFC 3392 */
+ if (parse_capabilities(peer, &op, &as) == -1) {
+ session_notification(peer, ERR_OPEN, 0,
+ NULL);
+ change_state(peer, STATE_IDLE,
+ EVNT_RCVD_OPEN);
+ return (-1);
+ }
+ break;
+ case OPT_PARAM_AUTH: /* deprecated */
+ default:
+ /*
+ * unsupported type
+ * the RFCs tell us to leave the data section
+ * empty and notify the peer with ERR_OPEN,
+ * ERR_OPEN_OPT. How the peer should know
+ * _which_ optional parameter we don't support
+ * is beyond me.
+ */
+ log_peer_warnx(&peer->conf,
+ "received OPEN message with unsupported "
+ "optional parameter: type %u", op_type);
+ session_notification(peer, ERR_OPEN,
+ ERR_OPEN_OPT, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
- break;
- case OPT_PARAM_AUTH: /* deprecated */
- default:
- /*
- * unsupported type
- * the RFCs tell us to leave the data section empty
- * and notify the peer with ERR_OPEN, ERR_OPEN_OPT.
- * How the peer should know _which_ optional parameter
- * we don't support is beyond me.
- */
- log_peer_warnx(&peer->conf,
- "received OPEN message with unsupported optional "
- "parameter: type %u", op_type);
- session_notification(peer, ERR_OPEN, ERR_OPEN_OPT,
- NULL);
- change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
- return (-1);
}
}
+ if (ibuf_size(&ibuf) != 0) {
+ bad_len:
+ log_peer_warnx(&peer->conf,
+ "corrupt OPEN message received: length mismatch");
+ session_notification(peer, ERR_OPEN, 0, NULL);
+ change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
+ return (-1);
+ }
+
/* if remote-as is zero and it's a cloned neighbor, accept any */
if (peer->template && !peer->conf.remote_as && as != AS_TRANS) {
peer->conf.remote_as = as;
parse_rrefresh(struct peer *peer)
{
struct route_refresh rr;
+ struct ibuf ibuf;
uint16_t afi, datalen;
uint8_t aid, safi, subtype;
u_char *p;
p = peer->rbuf->rptr;
p += MSGSIZE_HEADER; /* header is already checked */
+ datalen -= MSGSIZE_HEADER;
- /*
- * We could check if we actually announced the capability but
- * as long as the message is correctly encoded we don't care.
- */
+ /* XXX */
+ ibuf_from_buffer(&ibuf, p, datalen);
- /* afi, 2 byte */
- memcpy(&afi, p, sizeof(afi));
- afi = ntohs(afi);
- p += 2;
- /* subtype, 1 byte */
- subtype = *p;
- p += 1;
- /* safi, 1 byte */
- safi = *p;
+ if (ibuf_get_n16(&ibuf, &afi) == -1 ||
+ ibuf_get_n8(&ibuf, &subtype) == -1 ||
+ ibuf_get_n8(&ibuf, &safi) == -1) {
+ /* minimum size checked in session_process_msg() */
+ fatalx("%s: message too small", __func__);
+ }
/* check subtype if peer announced enhanced route refresh */
if (peer->capa.neg.enhanced_rr) {
log_peer_warnx(&peer->conf,
"received RREFRESH: illegal len: %u byte",
datalen);
- p = peer->rbuf->rptr;
- p += MSGSIZE_HEADER;
- datalen -= MSGSIZE_HEADER;
- session_notification_data(peer, ERR_RREFRESH,
- ERR_RR_INV_LEN, p, datalen);
+ ibuf_rewind(&ibuf);
+ session_notification(peer, ERR_RREFRESH,
+ ERR_RR_INV_LEN, &ibuf);
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
}
/* XXX */
ibuf_from_buffer(&ibuf, p, datalen);
-
+
if (ibuf_get_n8(&ibuf, &errcode) == -1 ||
ibuf_get_n8(&ibuf, &subcode) == -1) {
log_peer_warnx(&peer->conf, "received bad notification");
}
int
-parse_capabilities(struct peer *peer, u_char *d, uint16_t dlen, uint32_t *as)
+parse_capabilities(struct peer *peer, struct ibuf *buf, uint32_t *as)
{
- u_char *capa_val;
- uint32_t remote_as;
- uint16_t len;
- uint16_t afi;
- uint16_t gr_header;
- uint8_t safi;
- uint8_t aid;
- uint8_t flags;
- uint8_t capa_code;
- uint8_t capa_len;
- uint8_t i;
-
- len = dlen;
- while (len > 0) {
- if (len < 2) {
+ struct ibuf capabuf;
+ uint16_t afi, gr_header;
+ uint8_t capa_code, capa_len;
+ uint8_t safi, aid, role, flags;
+
+ while (ibuf_size(buf) > 0) {
+ if (ibuf_get_n8(buf, &capa_code) == -1 ||
+ ibuf_get_n8(buf, &capa_len) == -1) {
log_peer_warnx(&peer->conf, "Bad capabilities attr "
- "length: %u, too short", len);
+ "length: too short");
+ return (-1);
+ }
+ if (ibuf_get_ibuf(buf, capa_len, &capabuf) == -1) {
+ log_peer_warnx(&peer->conf,
+ "Received bad capabilities attr length: "
+ "len %zu smaller than capa_len %u",
+ ibuf_size(buf), capa_len);
return (-1);
}
- memcpy(&capa_code, d, sizeof(capa_code));
- d += sizeof(capa_code);
- len -= sizeof(capa_code);
- memcpy(&capa_len, d, sizeof(capa_len));
- d += sizeof(capa_len);
- len -= sizeof(capa_len);
- if (capa_len > 0) {
- if (len < capa_len) {
- log_peer_warnx(&peer->conf,
- "Bad capabilities attr length: "
- "len %u smaller than capa_len %u",
- len, capa_len);
- return (-1);
- }
- capa_val = d;
- d += capa_len;
- len -= capa_len;
- } else
- capa_val = NULL;
switch (capa_code) {
case CAPA_MP: /* RFC 4760 */
- if (capa_len != 4) {
+ if (capa_len != 4 ||
+ ibuf_get_n16(&capabuf, &afi) == -1 ||
+ ibuf_skip(&capabuf, 1) == -1 ||
+ ibuf_get_n8(&capabuf, &safi) == -1) {
log_peer_warnx(&peer->conf,
- "Bad multi protocol capability length: "
- "%u", capa_len);
+ "Received bad multi protocol capability");
break;
}
- memcpy(&afi, capa_val, sizeof(afi));
- afi = ntohs(afi);
- memcpy(&safi, capa_val + 3, sizeof(safi));
if (afi2aid(afi, safi, &aid) == -1) {
log_peer_warnx(&peer->conf,
"Received multi protocol capability: "
peer->capa.peer.refresh = 1;
break;
case CAPA_ROLE:
- if (capa_len != 1) {
+ if (capa_len != 1 ||
+ ibuf_get_n8(&capabuf, &role) == -1) {
log_peer_warnx(&peer->conf,
- "Bad role capability length: %u", capa_len);
+ "Received bad role capability");
break;
}
if (!peer->conf.ebgp) {
break;
}
peer->capa.peer.policy = 1;
- peer->remote_role = capa2role(*capa_val);
+ peer->remote_role = capa2role(role);
break;
case CAPA_RESTART:
if (capa_len == 2) {
break;
} else if (capa_len % 4 != 2) {
log_peer_warnx(&peer->conf,
- "Bad graceful restart capability length: "
- "%u", capa_len);
+ "Bad graceful restart capability");
+ peer->capa.peer.grestart.restart = 0;
+ peer->capa.peer.grestart.timeout = 0;
+ break;
+ }
+
+ if (ibuf_get_n16(&capabuf, &gr_header) == -1) {
+ bad_gr_restart:
+ log_peer_warnx(&peer->conf,
+ "Bad graceful restart capability");
peer->capa.peer.grestart.restart = 0;
peer->capa.peer.grestart.timeout = 0;
break;
}
- memcpy(&gr_header, capa_val, sizeof(gr_header));
- gr_header = ntohs(gr_header);
peer->capa.peer.grestart.timeout =
gr_header & CAPA_GR_TIMEMASK;
if (peer->capa.peer.grestart.timeout == 0) {
log_peer_warnx(&peer->conf, "Received "
- "graceful restart timeout is zero");
+ "graceful restart with zero timeout");
peer->capa.peer.grestart.restart = 0;
break;
}
- for (i = 2; i <= capa_len - 4; i += 4) {
- memcpy(&afi, capa_val + i, sizeof(afi));
- afi = ntohs(afi);
- safi = capa_val[i + 2];
- flags = capa_val[i + 3];
+ while (ibuf_size(&capabuf) > 0) {
+ if (ibuf_get_n16(&capabuf, &afi) == -1 ||
+ ibuf_get_n8(&capabuf, &safi) == -1 ||
+ ibuf_get_n8(&capabuf, &flags) == -1)
+ goto bad_gr_restart;
if (afi2aid(afi, safi, &aid) == -1) {
log_peer_warnx(&peer->conf,
"Received graceful restart capa: "
}
break;
case CAPA_AS4BYTE:
- if (capa_len != 4) {
+ if (capa_len != 4 ||
+ ibuf_get_n32(&capabuf, as) == -1) {
log_peer_warnx(&peer->conf,
- "Bad AS4BYTE capability length: "
- "%u", capa_len);
+ "Received bad AS4BYTE capability");
peer->capa.peer.as4byte = 0;
break;
}
- memcpy(&remote_as, capa_val, sizeof(remote_as));
- *as = ntohl(remote_as);
if (*as == 0) {
log_peer_warnx(&peer->conf,
"peer requests unacceptable AS %u", *as);
break;
case CAPA_ADD_PATH:
if (capa_len % 4 != 0) {
+ bad_add_path:
log_peer_warnx(&peer->conf,
- "Bad ADD-PATH capability length: "
- "%u", capa_len);
+ "Received bad ADD-PATH capability");
memset(peer->capa.peer.add_path, 0,
sizeof(peer->capa.peer.add_path));
break;
}
- for (i = 0; i <= capa_len - 4; i += 4) {
- memcpy(&afi, capa_val + i, sizeof(afi));
- afi = ntohs(afi);
- safi = capa_val[i + 2];
- flags = capa_val[i + 3];
+ while (ibuf_size(&capabuf) > 0) {
+ if (ibuf_get_n16(&capabuf, &afi) == -1 ||
+ ibuf_get_n8(&capabuf, &safi) == -1 ||
+ ibuf_get_n8(&capabuf, &flags) == -1)
+ goto bad_add_path;
if (afi2aid(afi, safi, &aid) == -1) {
log_peer_warnx(&peer->conf,
"Received ADD-PATH capa: "