-/* $OpenBSD: session.c,v 1.459 2024/01/12 11:19:51 claudio Exp $ */
+/* $OpenBSD: session.c,v 1.460 2024/01/16 13:15:31 claudio Exp $ */
/*
* Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
void session_open(struct peer *);
void session_keepalive(struct peer *);
void session_update(uint32_t, void *, size_t);
-void session_notification(struct peer *, uint8_t, uint8_t, void *,
- ssize_t);
+void session_notification(struct peer *, uint8_t, uint8_t, struct ibuf *);
+void session_notification_data(struct peer *, uint8_t, uint8_t, void *,
+ size_t);
void session_rrefresh(struct peer *, uint8_t, uint8_t);
int session_graceful_restart(struct peer *);
int session_graceful_stop(struct peer *);
case EVNT_TIMER_HOLDTIME:
case EVNT_TIMER_SENDHOLD:
session_notification(peer, ERR_HOLDTIMEREXPIRED,
- 0, NULL, 0);
+ 0, NULL);
change_state(peer, STATE_IDLE, event);
break;
case EVNT_RCVD_OPEN:
break;
default:
session_notification(peer,
- ERR_FSM, ERR_FSM_UNEX_OPENSENT, NULL, 0);
+ ERR_FSM, ERR_FSM_UNEX_OPENSENT, NULL);
change_state(peer, STATE_IDLE, event);
break;
}
case EVNT_TIMER_HOLDTIME:
case EVNT_TIMER_SENDHOLD:
session_notification(peer, ERR_HOLDTIMEREXPIRED,
- 0, NULL, 0);
+ 0, NULL);
change_state(peer, STATE_IDLE, event);
break;
case EVNT_TIMER_KEEPALIVE:
break;
default:
session_notification(peer,
- ERR_FSM, ERR_FSM_UNEX_OPENCONFIRM, NULL, 0);
+ ERR_FSM, ERR_FSM_UNEX_OPENCONFIRM, NULL);
change_state(peer, STATE_IDLE, event);
break;
}
case EVNT_TIMER_HOLDTIME:
case EVNT_TIMER_SENDHOLD:
session_notification(peer, ERR_HOLDTIMEREXPIRED,
- 0, NULL, 0);
+ 0, NULL);
change_state(peer, STATE_IDLE, event);
break;
case EVNT_TIMER_KEEPALIVE:
break;
default:
session_notification(peer,
- ERR_FSM, ERR_FSM_UNEX_ESTABLISHED, NULL, 0);
+ ERR_FSM, ERR_FSM_UNEX_ESTABLISHED, NULL);
change_state(peer, STATE_IDLE, event);
break;
}
p->stats.msg_sent_update++;
}
+void
+session_notification_data(struct peer *p, uint8_t errcode, uint8_t subcode,
+ void *data, size_t datalen)
+{
+ struct ibuf ibuf;
+
+ ibuf_from_buffer(&ibuf, data, datalen);
+ session_notification(p, errcode, subcode, &ibuf);
+}
+
void
session_notification(struct peer *p, uint8_t errcode, uint8_t subcode,
- void *data, ssize_t datalen)
+ struct ibuf *ibuf)
{
struct bgp_msg *buf;
int errs = 0;
+ size_t datalen = 0;
if (p->stats.last_sent_errcode) /* some notification already sent */
return;
- log_notification(p, errcode, subcode, data, datalen, "sending");
+ log_notification(p, errcode, subcode, ibuf, "sending");
/* cap to maximum size */
- if (datalen > MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN) {
- log_peer_warnx(&p->conf,
- "oversized notification, data trunkated");
- datalen = MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN;
+ if (ibuf != NULL) {
+ if (ibuf_size(ibuf) >
+ MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN) {
+ log_peer_warnx(&p->conf,
+ "oversized notification, data trunkated");
+ ibuf_truncate(ibuf, MAX_PKTSIZE -
+ MSGSIZE_NOTIFICATION_MIN);
+ }
+ datalen = ibuf_size(ibuf);
}
if ((buf = session_newmsg(NOTIFICATION,
errs += ibuf_add_n8(buf->buf, errcode);
errs += ibuf_add_n8(buf->buf, subcode);
- if (datalen > 0)
- errs += ibuf_add(buf->buf, data, datalen);
+ if (ibuf != NULL)
+ errs += ibuf_add_buf(buf->buf, ibuf);
if (errs) {
ibuf_free(buf->buf);
p->stats.msg_rcvd_rrefresh++;
break;
default: /* cannot happen */
- session_notification(p, ERR_HEADER, ERR_HDR_TYPE,
+ session_notification_data(p, ERR_HEADER, ERR_HDR_TYPE,
&msgtype, 1);
log_warnx("received message with unknown type %u",
msgtype);
p = data;
if (memcmp(p, marker, sizeof(marker))) {
log_peer_warnx(&peer->conf, "sync error");
- session_notification(peer, ERR_HEADER, ERR_HDR_SYNC, NULL, 0);
+ session_notification(peer, ERR_HEADER, ERR_HDR_SYNC, NULL);
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
}
if (*len < MSGSIZE_HEADER || *len > MAX_PKTSIZE) {
log_peer_warnx(&peer->conf,
"received message: illegal length: %u byte", *len);
- session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_LEN,
&olen, sizeof(olen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
if (*len < MSGSIZE_OPEN_MIN) {
log_peer_warnx(&peer->conf,
"received OPEN: illegal len: %u byte", *len);
- session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_LEN,
&olen, sizeof(olen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
log_peer_warnx(&peer->conf,
"received NOTIFICATION: illegal len: %u byte",
*len);
- session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_LEN,
&olen, sizeof(olen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
if (*len < MSGSIZE_UPDATE_MIN) {
log_peer_warnx(&peer->conf,
"received UPDATE: illegal len: %u byte", *len);
- session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_LEN,
&olen, sizeof(olen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
if (*len != MSGSIZE_KEEPALIVE) {
log_peer_warnx(&peer->conf,
"received KEEPALIVE: illegal len: %u byte", *len);
- session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_LEN,
&olen, sizeof(olen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
if (*len < MSGSIZE_RREFRESH_MIN) {
log_peer_warnx(&peer->conf,
"received RREFRESH: illegal len: %u byte", *len);
- session_notification(peer, ERR_HEADER, ERR_HDR_LEN,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_LEN,
&olen, sizeof(olen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
default:
log_peer_warnx(&peer->conf,
"received msg with unknown type %u", *type);
- session_notification(peer, ERR_HEADER, ERR_HDR_TYPE,
+ session_notification_data(peer, ERR_HEADER, ERR_HDR_TYPE,
type, 1);
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
rversion = version - BGP_VERSION;
else
rversion = BGP_VERSION;
- session_notification(peer, ERR_OPEN, ERR_OPEN_VERSION,
+ session_notification_data(peer, ERR_OPEN, ERR_OPEN_VERSION,
&rversion, sizeof(rversion));
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
if (as == 0) {
log_peer_warnx(&peer->conf,
"peer requests unacceptable AS %u", as);
- session_notification(peer, ERR_OPEN, ERR_OPEN_AS,
- NULL, 0);
+ session_notification(peer, ERR_OPEN, ERR_OPEN_AS, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
if (holdtime && holdtime < peer->conf.min_holdtime) {
log_peer_warnx(&peer->conf,
"peer requests unacceptable holdtime %u", holdtime);
- session_notification(peer, ERR_OPEN, ERR_OPEN_HOLDTIME,
- NULL, 0);
+ session_notification(peer, ERR_OPEN, ERR_OPEN_HOLDTIME, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
if (ntohl(bgpid) == 0) {
log_peer_warnx(&peer->conf, "peer BGPID %u unacceptable",
ntohl(bgpid));
- session_notification(peer, ERR_OPEN, ERR_OPEN_BGPID,
- NULL, 0);
+ session_notification(peer, ERR_OPEN, ERR_OPEN_BGPID, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
bad_len:
log_peer_warnx(&peer->conf,
"corrupt OPEN message received: length mismatch");
- session_notification(peer, ERR_OPEN, 0, NULL, 0);
+ session_notification(peer, ERR_OPEN, 0, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
case OPT_PARAM_CAPABILITIES: /* RFC 3392 */
if (parse_capabilities(peer, op_val, op_len,
&as) == -1) {
- session_notification(peer, ERR_OPEN, 0,
- NULL, 0);
+ session_notification(peer, ERR_OPEN, 0, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
"received OPEN message with unsupported optional "
"parameter: type %u", op_type);
session_notification(peer, ERR_OPEN, ERR_OPEN_OPT,
- NULL, 0);
+ NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
/* no punish */
timer_set(&peer->timers, Timer_IdleHold, 0);
if (peer->conf.remote_as != as) {
log_peer_warnx(&peer->conf, "peer sent wrong AS %s",
log_as(as));
- session_notification(peer, ERR_OPEN, ERR_OPEN_AS, NULL, 0);
+ session_notification(peer, ERR_OPEN, ERR_OPEN_AS, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
if (!peer->conf.ebgp && peer->remote_bgpid == conf->bgpid) {
log_peer_warnx(&peer->conf, "peer BGPID %u conflicts with ours",
ntohl(bgpid));
- session_notification(peer, ERR_OPEN, ERR_OPEN_BGPID,
- NULL, 0);
+ session_notification(peer, ERR_OPEN, ERR_OPEN_BGPID, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
if (capa_neg_calc(peer, &suberr) == -1) {
- session_notification(peer, ERR_OPEN, suberr, NULL, 0);
+ session_notification(peer, ERR_OPEN, suberr, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
"received RREFRESH: illegal len: %u byte",
datalen);
datalen = htons(datalen);
- session_notification(peer, ERR_HEADER,
+ session_notification_data(peer, ERR_HEADER,
ERR_HDR_LEN, &datalen, sizeof(datalen));
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
p = peer->rbuf->rptr;
p += MSGSIZE_HEADER;
datalen -= MSGSIZE_HEADER;
- session_notification(peer, ERR_RREFRESH,
+ session_notification_data(peer, ERR_RREFRESH,
ERR_RR_INV_LEN, p, datalen);
bgp_fsm(peer, EVNT_CON_FATAL);
return (-1);
int
parse_notification(struct peer *peer)
{
+ struct ibuf ibuf;
u_char *p;
uint16_t datalen;
uint8_t errcode;
p += sizeof(subcode);
datalen -= sizeof(subcode);
- log_notification(peer, errcode, subcode, p, datalen, "received");
+ /* XXX */
+ ibuf_from_buffer(&ibuf, p, datalen);
+ log_notification(peer, errcode, subcode, &ibuf, "received");
+
peer->errcnt++;
peer->stats.last_rcvd_errcode = errcode;
peer->stats.last_rcvd_suberr = subcode;
log_peer_warnx(&peer->conf,
"peer requests unacceptable AS %u", *as);
session_notification(peer, ERR_OPEN,
- ERR_OPEN_AS, NULL, 0);
+ ERR_OPEN_AS, NULL);
change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN);
return (-1);
}
session_dispatch_imsg(struct imsgbuf *imsgbuf, int idx, u_int *listener_cnt)
{
struct imsg imsg;
+ struct ibuf ibuf;
struct mrt xmrt;
struct route_refresh rr;
struct mrt *mrt;
struct listen_addr *la, *next, nla;
struct session_dependon sdon;
struct bgpd_config tconf;
- u_char *data;
uint32_t peerid;
int n, fd, depend_ok, restricted;
uint16_t t;
case IMSG_UPDATE_ERR:
if (idx != PFD_PIPE_ROUTE)
fatalx("update request not from RDE");
- if (imsg.hdr.len < IMSG_HEADER_SIZE + 2) {
- log_warnx("RDE sent invalid notification");
+ if ((p = getpeerbyid(conf, peerid)) == NULL) {
+ log_warnx("no such peer: id=%u", peerid);
break;
}
- if ((p = getpeerbyid(conf, imsg.hdr.peerid)) == NULL) {
- log_warnx("no such peer: id=%u",
- imsg.hdr.peerid);
+ if (imsg_get_ibuf(&imsg, &ibuf) == -1 ||
+ ibuf_get_n8(&ibuf, &errcode) == -1 ||
+ ibuf_get_n8(&ibuf, &subcode) == -1) {
+ log_warnx("RDE sent invalid notification");
break;
}
- data = imsg.data;
- errcode = *data++;
- subcode = *data++;
- if (imsg.hdr.len == IMSG_HEADER_SIZE + 2)
- data = NULL;
-
- session_notification(p, errcode, subcode,
- data, imsg.hdr.len - IMSG_HEADER_SIZE - 2);
+ session_notification(p, errcode, subcode, &ibuf);
switch (errcode) {
case ERR_CEASE:
switch (subcode) {
void
session_stop(struct peer *peer, uint8_t subcode)
{
- char data[REASON_LEN];
- size_t datalen;
- size_t reason_len;
+ struct ibuf *ibuf;
char *communication;
- datalen = 0;
communication = peer->conf.reason;
+ ibuf = ibuf_dynamic(0, REASON_LEN);
+
if ((subcode == ERR_CEASE_ADMIN_DOWN ||
- subcode == ERR_CEASE_ADMIN_RESET)
- && communication && *communication) {
- reason_len = strlen(communication);
- if (reason_len > REASON_LEN - 1) {
- log_peer_warnx(&peer->conf,
- "trying to send overly long shutdown reason");
- } else {
- data[0] = reason_len;
- datalen = reason_len + sizeof(data[0]);
- memcpy(data + 1, communication, reason_len);
+ subcode == ERR_CEASE_ADMIN_RESET) &&
+ communication != NULL && *communication != '\0' &&
+ ibuf != NULL) {
+ if (ibuf_add_n8(ibuf, strlen(communication)) == -1 ||
+ ibuf_add(ibuf, communication, strlen(communication))) {
+ log_peer_warnx(&peer->conf,
+ "trying to send overly long shutdown reason");
+ ibuf_free(ibuf);
+ ibuf = NULL;
}
}
switch (peer->state) {
case STATE_OPENSENT:
case STATE_OPENCONFIRM:
case STATE_ESTABLISHED:
- session_notification(peer, ERR_CEASE, subcode, data, datalen);
+ session_notification(peer, ERR_CEASE, subcode, ibuf);
break;
default:
/* session not open, no need to send notification */
break;
}
+ ibuf_free(ibuf);
bgp_fsm(peer, EVNT_STOP);
}