-/* $OpenBSD: mrt.c,v 1.114 2023/04/19 09:03:00 claudio Exp $ */
+/* $OpenBSD: mrt.c,v 1.115 2023/07/12 14:45:42 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
return (-1);
/* communities */
- if (community_writebuf(buf, c) == -1)
+ if (community_writebuf(c, ATTR_COMMUNITIES, 0, buf) == -1 ||
+ community_writebuf(c, ATTR_EXT_COMMUNITIES, 0, buf) == -1 ||
+ community_writebuf(c, ATTR_LARGE_COMMUNITIES, 0, buf) == -1)
return (-1);
/* dump all other path attributes without modification */
goto fail;
}
- if (pt_writebuf(h2buf, p->pt) == -1) {
+ if (pt_writebuf(h2buf, p->pt, 0, 0, 0) == -1) {
log_warnx("%s: pt_writebuf error", __func__);
goto fail;
}
}
len = ibuf_size(tbuf);
DUMP_SHORT(buf, (uint16_t)len);
- if (ibuf_add(buf, tbuf->buf, len) == -1) {
- log_warn("%s: ibuf_add error", __func__);
+ if (ibuf_add_buf(buf, tbuf) == -1) {
+ log_warn("%s: ibuf_add_buf error", __func__);
ibuf_free(tbuf);
goto fail;
}
break;
}
- if (pt_writebuf(pbuf, re->prefix) == -1) {
+ if (pt_writebuf(pbuf, re->prefix, 0, 0, 0) == -1) {
log_warnx("%s: pt_writebuf error", __func__);
goto fail;
}
goto fail;
DUMP_LONG(hbuf, snum);
- if (ibuf_add(hbuf, pbuf->buf, ibuf_size(pbuf)) == -1) {
- log_warn("%s: ibuf_add error", __func__);
+ if (ibuf_add_buf(hbuf, pbuf) == -1) {
+ log_warn("%s: ibuf_add_buf error", __func__);
goto fail;
}
DUMP_SHORT(hbuf, nump);
goto fail;
DUMP_LONG(hbuf, snum);
- if (ibuf_add(hbuf, pbuf->buf, ibuf_size(pbuf)) == -1) {
- log_warn("%s: ibuf_add error", __func__);
+ if (ibuf_add_buf(hbuf, pbuf) == -1) {
+ log_warn("%s: ibuf_add_buf error", __func__);
goto fail;
}
DUMP_SHORT(hbuf, apnump);
}
off = ibuf_size(buf);
- if (ibuf_reserve(buf, sizeof(nump)) == NULL) {
- log_warn("%s: ibuf_reserve error", __func__);
+ if (ibuf_add_zero(buf, sizeof(nump)) == -1) {
+ log_warn("%s: ibuf_add_zero error", __func__);
goto fail;
}
arg.nump = 0;
if (arg.nump == -1)
goto fail;
- nump = htons(arg.nump);
- memcpy(ibuf_seek(buf, off, sizeof(nump)), &nump, sizeof(nump));
+ if (ibuf_set_n16(buf, off, arg.nump) == -1) {
+ log_warn("%s: ibuf_set_n16 error", __func__);
+ goto fail;
+ }
len = ibuf_size(buf);
if (mrt_dump_hdr_rde(&hbuf, MSG_TABLE_DUMP_V2,
void
mrt_clean(struct mrt *mrt)
{
- struct ibuf *b;
-
close(mrt->wbuf.fd);
- while ((b = TAILQ_FIRST(&mrt->wbuf.bufs))) {
- TAILQ_REMOVE(&mrt->wbuf.bufs, b, entry);
- ibuf_free(b);
- }
- mrt->wbuf.queued = 0;
+ msgbuf_clear(&mrt->wbuf);
}
static struct imsgbuf *mrt_imsgbuf[2];
-/* $OpenBSD: rde.c,v 1.607 2023/07/12 12:31:28 claudio Exp $ */
+/* $OpenBSD: rde.c,v 1.608 2023/07/12 14:45:42 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
static int rde_roa_reload(void);
static int rde_aspa_reload(void);
int rde_update_queue_pending(void);
-void rde_update_queue_runner(void);
-void rde_update6_queue_runner(uint8_t);
+void rde_update_queue_runner(uint8_t);
struct rde_prefixset *rde_find_prefixset(char *, struct rde_prefixset_head *);
void rde_mark_prefixsets_dirty(struct rde_prefixset_head *,
struct rde_prefixset_head *);
rib_dump_runner();
nexthop_runner();
if (ibuf_se && ibuf_se->w.queued < SESS_MSG_HIGH_MARK) {
- rde_update_queue_runner();
- for (aid = AID_INET6; aid < AID_MAX; aid++)
- rde_update6_queue_runner(aid);
+ for (aid = AID_MIN; aid < AID_MAX; aid++)
+ rde_update_queue_runner(aid);
}
/* commit pftable once per poll loop */
rde_commit_pftable();
}
void
-rde_update_queue_runner(void)
+rde_update_queue_runner(uint8_t aid)
{
struct rde_peer *peer;
- int r, sent, max = RDE_RUNNER_ROUNDS, eor;
- uint16_t len, wpos;
+ struct ibuf *buf;
+ int sent, max = RDE_RUNNER_ROUNDS;
- len = sizeof(queue_buf) - MSGSIZE_HEADER;
+ /* first withdraws ... */
do {
sent = 0;
RB_FOREACH(peer, peer_tree, &peertable) {
continue;
if (peer->throttled)
continue;
- eor = 0;
- wpos = 0;
- /* first withdraws, save 2 bytes for path attributes */
- if ((r = up_dump_withdraws(queue_buf, len - 2, peer,
- AID_INET)) == -1)
+ if (RB_EMPTY(&peer->withdraws[aid]))
continue;
- wpos += r;
-
- /* now bgp path attributes unless it is the EoR mark */
- if (up_is_eor(peer, AID_INET)) {
- eor = 1;
- memset(queue_buf + wpos, 0, 2);
- wpos += 2;
- } else {
- r = up_dump_attrnlri(queue_buf + wpos,
- len - wpos, peer);
- wpos += r;
- }
-
- /* finally send message to SE */
- if (wpos > 4) {
- if (imsg_compose(ibuf_se, IMSG_UPDATE,
- peer->conf.id, 0, -1, queue_buf,
- wpos) == -1)
- fatal("%s %d imsg_compose error",
- __func__, __LINE__);
- sent++;
- }
- if (eor) {
- int sent_eor = peer->sent_eor & (1 << AID_INET);
- if (peer->capa.grestart.restart && !sent_eor)
- rde_peer_send_eor(peer, AID_INET);
- if (peer->capa.enhanced_rr && sent_eor)
- rde_peer_send_rrefresh(peer, AID_INET,
- ROUTE_REFRESH_END_RR);
- }
- }
- max -= sent;
- } while (sent != 0 && max > 0);
-}
-
-void
-rde_update6_queue_runner(uint8_t aid)
-{
- struct rde_peer *peer;
- int r, sent, max = RDE_RUNNER_ROUNDS / 2;
- uint16_t len;
- /* first withdraws ... */
- do {
- sent = 0;
- RB_FOREACH(peer, peer_tree, &peertable) {
- if (peer->conf.id == 0)
- continue;
- if (peer->state != PEER_UP)
- continue;
- if (peer->throttled)
+ if ((buf = ibuf_dynamic(4, 4096 - MSGSIZE_HEADER)) ==
+ NULL)
+ fatal("%s", __func__);
+ if (up_dump_withdraws(buf, peer, aid) == -1) {
+ ibuf_free(buf);
continue;
- len = sizeof(queue_buf) - MSGSIZE_HEADER;
- r = up_dump_mp_unreach(queue_buf, len, peer, aid);
- if (r == -1)
- continue;
- /* finally send message to SE */
- if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id,
- 0, -1, queue_buf, r) == -1)
- fatal("%s %d imsg_compose error", __func__,
- __LINE__);
+ }
+ if (imsg_compose_ibuf(ibuf_se, IMSG_UPDATE,
+ peer->conf.id, 0, buf) == -1)
+ fatal("%s: imsg_create error", __func__);
sent++;
}
max -= sent;
} while (sent != 0 && max > 0);
/* ... then updates */
- max = RDE_RUNNER_ROUNDS / 2;
+ max = RDE_RUNNER_ROUNDS;
do {
sent = 0;
RB_FOREACH(peer, peer_tree, &peertable) {
continue;
if (peer->throttled)
continue;
- len = sizeof(queue_buf) - MSGSIZE_HEADER;
+ if (RB_EMPTY(&peer->updates[aid]))
+ continue;
+
if (up_is_eor(peer, aid)) {
int sent_eor = peer->sent_eor & (1 << aid);
if (peer->capa.grestart.restart && !sent_eor)
ROUTE_REFRESH_END_RR);
continue;
}
- r = up_dump_mp_reach(queue_buf, len, peer, aid);
- if (r == 0)
- continue;
- /* finally send message to SE */
- if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id,
- 0, -1, queue_buf, r) == -1)
- fatal("%s %d imsg_compose error", __func__,
- __LINE__);
+ if ((buf = ibuf_dynamic(4, 4096 - MSGSIZE_HEADER)) ==
+ NULL)
+ fatal("%s", __func__);
+ if (up_dump_update(buf, peer, aid) == -1) {
+ ibuf_free(buf);
+ continue;
+ }
+ if (imsg_compose_ibuf(ibuf_se, IMSG_UPDATE,
+ peer->conf.id, 0, buf) == -1)
+ fatal("%s: imsg_compose_ibuf error", __func__);
sent++;
}
max -= sent;
-/* $OpenBSD: rde.h,v 1.294 2023/06/12 12:48:07 claudio Exp $ */
+/* $OpenBSD: rde.h,v 1.295 2023/07/12 14:45:43 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and
int community_large_add(struct rde_community *, int, void *, size_t);
int community_ext_add(struct rde_community *, int, int, void *, size_t);
-int community_write(struct rde_community *, void *, uint16_t);
-int community_large_write(struct rde_community *, void *, uint16_t);
-int community_ext_write(struct rde_community *, int, void *, uint16_t);
-int community_writebuf(struct ibuf *, struct rde_community *);
+int community_writebuf(struct rde_community *, uint8_t, int, struct ibuf *);
void communities_shutdown(void);
struct rde_community *communities_lookup(struct rde_community *);
void pt_remove(struct pt_entry *);
struct pt_entry *pt_lookup(struct bgpd_addr *);
int pt_prefix_cmp(const struct pt_entry *, const struct pt_entry *);
-int pt_write(u_char *, int, struct pt_entry *, int);
-int pt_writebuf(struct ibuf *, struct pt_entry *);
+int pt_writebuf(struct ibuf *, struct pt_entry *, int, int, uint32_t);
static inline struct pt_entry *
pt_ref(struct pt_entry *pt)
struct prefix *, struct prefix *);
void up_generate_default(struct rde_peer *, uint8_t);
int up_is_eor(struct rde_peer *, uint8_t);
-int up_dump_withdraws(u_char *, int, struct rde_peer *, uint8_t);
-int up_dump_mp_unreach(u_char *, int, struct rde_peer *, uint8_t);
-int up_dump_attrnlri(u_char *, int, struct rde_peer *);
-int up_dump_mp_reach(u_char *, int, struct rde_peer *, uint8_t);
+int up_dump_withdraws(struct ibuf *, struct rde_peer *, uint8_t);
+int up_dump_update(struct ibuf *, struct rde_peer *, uint8_t);
/* rde_aspa.c */
void aspa_validation(struct rde_aspa *, struct aspath *,
-/* $OpenBSD: rde_attr.c,v 1.133 2023/06/12 12:10:17 claudio Exp $ */
+/* $OpenBSD: rde_attr.c,v 1.134 2023/07/12 14:45:43 claudio Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
#include "rde.h"
#include "log.h"
-int
-attr_write(void *p, uint16_t p_len, uint8_t flags, uint8_t type,
- void *data, uint16_t data_len)
-{
- u_char *b = p;
- uint16_t tmp, tot_len = 2; /* attribute header (without len) */
-
- flags &= ~ATTR_DEFMASK;
- if (data_len > 255) {
- tot_len += 2 + data_len;
- flags |= ATTR_EXTLEN;
- } else {
- tot_len += 1 + data_len;
- }
-
- if (tot_len > p_len)
- return (-1);
-
- *b++ = flags;
- *b++ = type;
- if (data_len > 255) {
- tmp = htons(data_len);
- memcpy(b, &tmp, sizeof(tmp));
- b += 2;
- } else
- *b++ = (u_char)data_len;
-
- if (data == NULL)
- return (tot_len - data_len);
-
- if (data_len != 0)
- memcpy(b, data, data_len);
-
- return (tot_len);
-}
-
int
attr_writebuf(struct ibuf *buf, uint8_t flags, uint8_t type, void *data,
uint16_t data_len)
-/* $OpenBSD: rde_community.c,v 1.12 2023/06/17 08:05:48 claudio Exp $ */
+/* $OpenBSD: rde_community.c,v 1.13 2023/07/12 14:45:43 claudio Exp $ */
/*
* Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
struct community *new;
int newsize = comm->size + 8;
- if ((new = reallocarray(comm->communities, newsize,
- sizeof(struct community))) == NULL)
+ if ((new = recallocarray(comm->communities, comm->size,
+ newsize, sizeof(struct community))) == NULL)
fatal(__func__);
- memset(new + comm->size, 0, 8 * sizeof(struct community));
comm->communities = new;
comm->size = newsize;
}
static int
non_transitive_ext_community(struct community *c)
{
+ if ((uint8_t)c->flags != COMMUNITY_TYPE_EXT)
+ return 0;
if ((c->data3 >> 8) & EXT_COMMUNITY_NON_TRANSITIVE)
return 1;
return 0;
/*
* Convert communities back to the wireformat.
- * This function needs to make sure that the attribute buffer is overflowed
- * while writing out the communities.
- * - community_write for ATTR_COMMUNITUES
- * - community_large_write for ATTR_LARGE_COMMUNITIES
- * - community_ext_write for ATTR_EXT_COMMUNITIES
- * When writing ATTR_EXT_COMMUNITIES non-transitive communities need to
- * be skipped if it is sent to an ebgp peer.
+ * When writing ATTR_EXT_COMMUNITIES non-transitive communities need to
+ * be skipped if they are sent to an ebgp peer.
*/
int
-community_write(struct rde_community *comm, void *buf, uint16_t len)
+community_writebuf(struct rde_community *comm, uint8_t type, int ebgp,
+ struct ibuf *buf)
{
- uint8_t *b = buf;
- uint16_t c;
- size_t n = 0;
- int l, r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
+ struct community *cp;
+ uint64_t ext;
+ int l, size, start, end, num;
+ int flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
+ uint8_t t;
- if (comm->flags & PARTIAL_COMMUNITIES)
- flags |= ATTR_PARTIAL;
+ switch (type) {
+ case ATTR_COMMUNITIES:
+ if (comm->flags & PARTIAL_COMMUNITIES)
+ flags |= ATTR_PARTIAL;
+ size = 4;
+ t = COMMUNITY_TYPE_BASIC;
+ break;
+ case ATTR_EXT_COMMUNITIES:
+ if (comm->flags & PARTIAL_EXT_COMMUNITIES)
+ flags |= ATTR_PARTIAL;
+ size = 8;
+ t = COMMUNITY_TYPE_EXT;
+ break;
+ case ATTR_LARGE_COMMUNITIES:
+ if (comm->flags & PARTIAL_LARGE_COMMUNITIES)
+ flags |= ATTR_PARTIAL;
+ size = 12;
+ t = COMMUNITY_TYPE_LARGE;
+ break;
+ default:
+ return -1;
+ }
/* first count how many communities will be written */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_BASIC)
- n++;
-
- if (n == 0)
- return 0;
-
- /* write attribute header */
- r = attr_write(b, len, flags, ATTR_COMMUNITIES, NULL, n * 4);
- if (r == -1)
- return -1;
- b += r;
+ num = 0;
+ start = -1;
+ for (l = 0; l < comm->nentries; l++) {
+ cp = &comm->communities[l];
- /* write out the communities */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_BASIC) {
- c = htons(comm->communities[l].data1);
- memcpy(b, &c, sizeof(c));
- b += sizeof(c);
- r += sizeof(c);
-
- c = htons(comm->communities[l].data2);
- memcpy(b, &c, sizeof(c));
- b += sizeof(c);
- r += sizeof(c);
+ if (ebgp && non_transitive_ext_community(cp))
+ continue;
+ if ((uint8_t)cp->flags == t) {
+ num++;
+ if (start == -1)
+ start = l;
}
+ if ((uint8_t)cp->flags > t)
+ break;
+ }
+ end = l;
- return r;
-}
-
-int
-community_large_write(struct rde_community *comm, void *buf, uint16_t len)
-{
- uint8_t *b = buf;
- uint32_t c;
- size_t n = 0;
- int l, r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
-
- if (comm->flags & PARTIAL_LARGE_COMMUNITIES)
- flags |= ATTR_PARTIAL;
-
- /* first count how many communities will be written */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_LARGE)
- n++;
-
- if (n == 0)
+ /* no communities for this type present */
+ if (num == 0)
return 0;
- /* write attribute header */
- r = attr_write(b, len, flags, ATTR_LARGE_COMMUNITIES, NULL, n * 12);
- if (r == -1)
+ if (num > INT16_MAX / size)
return -1;
- b += r;
-
- /* write out the communities */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_LARGE) {
- c = htonl(comm->communities[l].data1);
- memcpy(b, &c, sizeof(c));
- b += sizeof(c);
- r += sizeof(c);
-
- c = htonl(comm->communities[l].data2);
- memcpy(b, &c, sizeof(c));
- b += sizeof(c);
- r += sizeof(c);
-
- c = htonl(comm->communities[l].data3);
- memcpy(b, &c, sizeof(c));
- b += sizeof(c);
- r += sizeof(c);
- }
-
- return r;
-}
-
-int
-community_ext_write(struct rde_community *comm, int ebgp, void *buf,
- uint16_t len)
-{
- struct community *cp;
- uint8_t *b = buf;
- uint64_t ext;
- size_t n = 0;
- int l, r, flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
-
- if (comm->flags & PARTIAL_EXT_COMMUNITIES)
- flags |= ATTR_PARTIAL;
-
- /* first count how many communities will be written */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_EXT && !(ebgp &&
- non_transitive_ext_community(&comm->communities[l])))
- n++;
-
- if (n == 0)
- return 0;
/* write attribute header */
- r = attr_write(b, len, flags, ATTR_EXT_COMMUNITIES, NULL, n * 8);
- if (r == -1)
+ if (attr_writebuf(buf, flags, type, NULL, num * size) == -1)
return -1;
- b += r;
/* write out the communities */
- for (l = 0; l < comm->nentries; l++) {
- cp = comm->communities + l;
- if ((uint8_t)cp->flags == COMMUNITY_TYPE_EXT && !(ebgp &&
- non_transitive_ext_community(cp))) {
- ext = (uint64_t)cp->data3 << 48;
- switch ((cp->data3 >> 8) & EXT_COMMUNITY_VALUE) {
- case EXT_COMMUNITY_TRANS_TWO_AS:
- case EXT_COMMUNITY_TRANS_OPAQUE:
- case EXT_COMMUNITY_TRANS_EVPN:
- ext |= ((uint64_t)cp->data1 & 0xffff) << 32;
- ext |= (uint64_t)cp->data2;
- break;
- case EXT_COMMUNITY_TRANS_FOUR_AS:
- case EXT_COMMUNITY_TRANS_IPV4:
- ext |= (uint64_t)cp->data1 << 16;
- ext |= (uint64_t)cp->data2 & 0xffff;
- break;
- }
- ext = htobe64(ext);
- memcpy(b, &ext, sizeof(ext));
- b += sizeof(ext);
- r += sizeof(ext);
- }
- }
-
- return r;
-}
-
-/*
- * Convert communities back to the wireformat and dump them into the ibuf buf.
- * This function is used by the mrt dump code.
- */
-int
-community_writebuf(struct ibuf *buf, struct rde_community *comm)
-{
- size_t basic_n = 0, large_n = 0, ext_n = 0;
- int l, flags;
-
- /* first count how many communities will be written */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_BASIC)
- basic_n++;
- else if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_EXT)
- ext_n++;
- else if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_LARGE)
- large_n++;
-
-
- if (basic_n != 0) {
- /* write attribute header */
- flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
- if (comm->flags & PARTIAL_COMMUNITIES)
- flags |= ATTR_PARTIAL;
-
- if (attr_writebuf(buf, flags, ATTR_COMMUNITIES, NULL,
- basic_n * 4) == -1)
- return -1;
-
- /* write out the communities */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_BASIC) {
- uint16_t c;
- c = htons(comm->communities[l].data1);
- if (ibuf_add(buf, &c, sizeof(c)) == -1)
- return (-1);
- c = htons(comm->communities[l].data2);
- if (ibuf_add(buf, &c, sizeof(c)) == -1)
- return (-1);
- }
- }
- if (ext_n != 0) {
- /* write attribute header */
- flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
- if (comm->flags & PARTIAL_COMMUNITIES)
- flags |= ATTR_PARTIAL;
-
- if (attr_writebuf(buf, flags, ATTR_EXT_COMMUNITIES, NULL,
- ext_n * 8) == -1)
- return -1;
-
- /* write out the communities */
- for (l = 0; l < comm->nentries; l++) {
- struct community *cp;
- uint64_t ext;
+ for (l = start; l < end; l++) {
+ cp = &comm->communities[l];
- cp = comm->communities + l;
- if ((uint8_t)cp->flags != COMMUNITY_TYPE_EXT)
+ switch (type) {
+ case ATTR_COMMUNITIES:
+ if (ibuf_add_n16(buf, cp->data1) == -1)
+ return -1;
+ if (ibuf_add_n16(buf, cp->data2) == -1)
+ return -1;
+ break;
+ case ATTR_EXT_COMMUNITIES:
+ if (ebgp && non_transitive_ext_community(cp))
continue;
ext = (uint64_t)cp->data3 << 48;
ext |= (uint64_t)cp->data2 & 0xffff;
break;
}
- ext = htobe64(ext);
- if (ibuf_add(buf, &ext, sizeof(ext)) == -1)
- return (-1);
+ if (ibuf_add_n64(buf, ext) == -1)
+ return -1;
+ break;
+ case ATTR_LARGE_COMMUNITIES:
+ if (ibuf_add_n32(buf, cp->data1) == -1)
+ return -1;
+ if (ibuf_add_n32(buf, cp->data2) == -1)
+ return -1;
+ if (ibuf_add_n32(buf, cp->data3) == -1)
+ return -1;
+ break;
}
}
- if (large_n != 0) {
- /* write attribute header */
- flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
- if (comm->flags & PARTIAL_COMMUNITIES)
- flags |= ATTR_PARTIAL;
-
- if (attr_writebuf(buf, flags, ATTR_LARGE_COMMUNITIES, NULL,
- large_n * 12) == -1)
- return -1;
-
- /* write out the communities */
- for (l = 0; l < comm->nentries; l++)
- if ((uint8_t)comm->communities[l].flags ==
- COMMUNITY_TYPE_LARGE) {
- uint32_t c;
- c = htonl(comm->communities[l].data1);
- if (ibuf_add(buf, &c, sizeof(c)) == -1)
- return (-1);
- c = htonl(comm->communities[l].data2);
- if (ibuf_add(buf, &c, sizeof(c)) == -1)
- return (-1);
- c = htonl(comm->communities[l].data3);
- if (ibuf_add(buf, &c, sizeof(c)) == -1)
- return (-1);
- }
- }
-
return 0;
}
-/* $OpenBSD: rde_prefix.c,v 1.49 2023/04/19 07:09:47 claudio Exp $ */
+/* $OpenBSD: rde_prefix.c,v 1.50 2023/07/12 14:45:43 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org>
/* dump a prefix into specified buffer */
int
-pt_write(u_char *buf, int len, struct pt_entry *pte, int withdraw)
+pt_writebuf(struct ibuf *buf, struct pt_entry *pte, int withdraw,
+ int add_path, uint32_t pathid)
{
struct pt_entry_vpn4 *pvpn4 = (struct pt_entry_vpn4 *)pte;
struct pt_entry_vpn6 *pvpn6 = (struct pt_entry_vpn6 *)pte;
struct pt_entry_flow *pflow = (struct pt_entry_flow *)pte;
- int totlen, flowlen, psize;
+ struct ibuf *tmp;
+ int flowlen, psize;
uint8_t plen;
+ if ((tmp = ibuf_dynamic(32, UINT16_MAX)) == NULL)
+ goto fail;
+
+ if (add_path) {
+ if (ibuf_add_n32(tmp, pathid) == -1)
+ goto fail;
+ }
+
switch (pte->aid) {
case AID_INET:
case AID_INET6:
plen = pte->prefixlen;
- totlen = PREFIX_SIZE(plen);
-
- if (totlen > len)
- return (-1);
- *buf++ = plen;
- memcpy(buf, pte->data, totlen - 1);
- return (totlen);
+ if (ibuf_add_n8(tmp, plen) == -1)
+ goto fail;
+ if (ibuf_add(tmp, pte->data, PREFIX_SIZE(plen) - 1) == -1)
+ goto fail;
+ break;
case AID_VPN_IPv4:
plen = pvpn4->prefixlen;
- totlen = PREFIX_SIZE(plen) + sizeof(pvpn4->rd);
psize = PREFIX_SIZE(plen) - 1;
plen += sizeof(pvpn4->rd) * 8;
if (withdraw) {
/* withdraw have one compat label as placeholder */
- totlen += 3;
plen += 3 * 8;
} else {
- totlen += pvpn4->labellen;
plen += pvpn4->labellen * 8;
}
- if (totlen > len)
- return (-1);
- *buf++ = plen;
+ if (ibuf_add_n8(tmp, plen) == -1)
+ goto fail;
if (withdraw) {
/* magic compatibility label as per rfc8277 */
- *buf++ = 0x80;
- *buf++ = 0x0;
- *buf++ = 0x0;
+ if (ibuf_add_n8(tmp, 0x80) == -1 ||
+ ibuf_add_zero(tmp, 2) == -1)
+ goto fail;
} else {
- memcpy(buf, &pvpn4->labelstack, pvpn4->labellen);
- buf += pvpn4->labellen;
+ if (ibuf_add(tmp, &pvpn4->labelstack,
+ pvpn4->labellen) == -1)
+ goto fail;
}
- memcpy(buf, &pvpn4->rd, sizeof(pvpn4->rd));
- buf += sizeof(pvpn4->rd);
- memcpy(buf, &pvpn4->prefix4, psize);
- return (totlen);
+ if (ibuf_add(tmp, &pvpn4->rd, sizeof(pvpn4->rd)) == -1 ||
+ ibuf_add(tmp, &pvpn4->prefix4, psize) == -1)
+ goto fail;
+ break;
case AID_VPN_IPv6:
plen = pvpn6->prefixlen;
- totlen = PREFIX_SIZE(plen) + sizeof(pvpn6->rd);
psize = PREFIX_SIZE(plen) - 1;
plen += sizeof(pvpn6->rd) * 8;
if (withdraw) {
/* withdraw have one compat label as placeholder */
- totlen += 3;
plen += 3 * 8;
} else {
- totlen += pvpn6->labellen;
plen += pvpn6->labellen * 8;
}
- if (totlen > len)
- return (-1);
- *buf++ = plen;
+ if (ibuf_add_n8(tmp, plen) == -1)
+ goto fail;
if (withdraw) {
/* magic compatibility label as per rfc8277 */
- *buf++ = 0x80;
- *buf++ = 0x0;
- *buf++ = 0x0;
+ if (ibuf_add_n8(tmp, 0x80) == -1 ||
+ ibuf_add_zero(tmp, 2) == -1)
+ goto fail;
} else {
- memcpy(buf, &pvpn6->labelstack, pvpn6->labellen);
- buf += pvpn6->labellen;
+ if (ibuf_add(tmp, &pvpn6->labelstack,
+ pvpn6->labellen) == -1)
+ goto fail;
}
- memcpy(buf, &pvpn6->rd, sizeof(pvpn6->rd));
- buf += sizeof(pvpn6->rd);
- memcpy(buf, &pvpn6->prefix6, psize);
- return (totlen);
+ if (ibuf_add(tmp, &pvpn6->rd, sizeof(pvpn6->rd)) == -1 ||
+ ibuf_add(tmp, &pvpn6->prefix6, psize) == -1)
+ goto fail;
+ break;
case AID_FLOWSPECv4:
case AID_FLOWSPECv6:
flowlen = pflow->len - PT_FLOW_SIZE;
- totlen = flowlen < FLOWSPEC_LEN_LIMIT ? 1 : 2;
- totlen += flowlen;
-
- if (totlen > len)
- return (-1);
-
if (flowlen < FLOWSPEC_LEN_LIMIT) {
- *buf++ = flowlen;
+ if (ibuf_add_n8(tmp, flowlen) == -1)
+ goto fail;
} else {
- *buf++ = 0xf0 | (flowlen >> 8);
- *buf++ = flowlen & 0xff;
+ if (ibuf_add_n8(tmp, 0xf0 | (flowlen >> 8)) == -1 ||
+ ibuf_add_n8(tmp, flowlen) == -1)
+ goto fail;
}
- memcpy(buf, &pflow->flow, flowlen);
- return (totlen);
- default:
- return (-1);
- }
-}
-
-/* dump a prefix into specified ibuf, allocating space for it if needed */
-int
-pt_writebuf(struct ibuf *buf, struct pt_entry *pte)
-{
- struct pt_entry_vpn4 *pvpn4 = (struct pt_entry_vpn4 *)pte;
- struct pt_entry_vpn6 *pvpn6 = (struct pt_entry_vpn6 *)pte;
- struct pt_entry_flow *pflow = (struct pt_entry_flow *)pte;
- int totlen, flowlen;
- void *bptr;
-
- switch (pte->aid) {
- case AID_INET:
- case AID_INET6:
- totlen = PREFIX_SIZE(pte->prefixlen);
- break;
- case AID_VPN_IPv4:
- totlen = PREFIX_SIZE(pte->prefixlen) + sizeof(pvpn4->rd) +
- pvpn4->labellen;
- break;
- case AID_VPN_IPv6:
- totlen = PREFIX_SIZE(pte->prefixlen) + sizeof(pvpn6->rd) +
- pvpn6->labellen;
- break;
- case AID_FLOWSPECv4:
- case AID_FLOWSPECv6:
- flowlen = pflow->len - PT_FLOW_SIZE;
- totlen = flowlen < FLOWSPEC_LEN_LIMIT ? 1 : 2;
- totlen += flowlen;
+ if (ibuf_add(tmp, &pflow->flow, flowlen) == -1)
+ goto fail;
break;
default:
- return (-1);
+ goto fail;
}
- if ((bptr = ibuf_reserve(buf, totlen)) == NULL)
- return (-1);
- if (pt_write(bptr, totlen, pte, 0) == -1)
- return (-1);
- return (0);
+ if (ibuf_add_buf(buf, tmp) == -1)
+ goto fail;
+ ibuf_free(tmp);
+ return 0;
+
+ fail:
+ ibuf_free(tmp);
+ return -1;
}
-/* $OpenBSD: rde_update.c,v 1.162 2023/04/19 08:30:37 claudio Exp $ */
+/* $OpenBSD: rde_update.c,v 1.163 2023/07/12 14:45:43 claudio Exp $ */
/*
* Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org>
static int
-up_generate_attr(u_char *buf, int len, struct rde_peer *peer,
- struct filterstate *state, uint8_t aid)
+up_generate_attr(struct ibuf *buf, struct rde_peer *peer,
+ struct rde_aspath *asp, struct rde_community *comm, struct nexthop *nh,
+ uint8_t aid)
{
- struct rde_aspath *asp = &state->aspath;
- struct rde_community *comm = &state->communities;
struct attr *oa = NULL, *newaggr = NULL;
u_char *pdata;
uint32_t tmp32;
- struct bgpd_addr *nexthop;
- int flags, r, neednewpath = 0;
- uint16_t wlen = 0, plen;
+ int flags, neednewpath = 0, rv;
+ uint16_t plen;
uint8_t oalen = 0, type;
if (asp->others_len > 0)
/* dump attributes in ascending order */
for (type = ATTR_ORIGIN; type < 255; type++) {
- r = 0;
-
while (oa && oa->type < type) {
if (oalen < asp->others_len)
oa = asp->others[oalen++];
* Attributes stored in rde_aspath
*/
case ATTR_ORIGIN:
- if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN,
- ATTR_ORIGIN, &asp->origin, 1)) == -1)
- return (-1);
+ if (attr_writebuf(buf, ATTR_WELL_KNOWN,
+ ATTR_ORIGIN, &asp->origin, 1) == -1)
+ return -1;
break;
case ATTR_ASPATH:
plen = aspath_length(asp->aspath);
if (!peer_has_as4byte(peer))
pdata = aspath_deflate(pdata, &plen,
&neednewpath);
-
- if ((r = attr_write(buf + wlen, len, ATTR_WELL_KNOWN,
- ATTR_ASPATH, pdata, plen)) == -1)
- return (-1);
+ rv = attr_writebuf(buf, ATTR_WELL_KNOWN,
+ ATTR_ASPATH, pdata, plen);
if (!peer_has_as4byte(peer))
free(pdata);
+
+ if (rv == -1)
+ return -1;
break;
case ATTR_NEXTHOP:
switch (aid) {
case AID_INET:
- nexthop = &state->nexthop->exit_nexthop;
- if ((r = attr_write(buf + wlen, len,
- ATTR_WELL_KNOWN, ATTR_NEXTHOP,
- &nexthop->v4.s_addr, 4)) == -1)
- return (-1);
+ if (attr_writebuf(buf, ATTR_WELL_KNOWN,
+ ATTR_NEXTHOP, &nh->exit_nexthop.v4,
+ sizeof(nh->exit_nexthop.v4)) == -1)
+ return -1;
break;
default:
break;
asp->flags & F_ATTR_MED_ANNOUNCE ||
peer->flags & PEERFLAG_TRANS_AS)) {
tmp32 = htonl(asp->med);
- if ((r = attr_write(buf + wlen, len,
- ATTR_OPTIONAL, ATTR_MED, &tmp32, 4)) == -1)
- return (-1);
+ if (attr_writebuf(buf, ATTR_OPTIONAL,
+ ATTR_MED, &tmp32, 4) == -1)
+ return -1;
}
break;
case ATTR_LOCALPREF:
if (!peer->conf.ebgp) {
/* local preference, only valid for ibgp */
tmp32 = htonl(asp->lpref);
- if ((r = attr_write(buf + wlen, len,
- ATTR_WELL_KNOWN, ATTR_LOCALPREF, &tmp32,
- 4)) == -1)
- return (-1);
+ if (attr_writebuf(buf, ATTR_WELL_KNOWN,
+ ATTR_LOCALPREF, &tmp32, 4) == -1)
+ return -1;
}
break;
/*
* Communities are stored in struct rde_community
*/
case ATTR_COMMUNITIES:
- if ((r = community_write(comm, buf + wlen, len)) == -1)
- return (-1);
- break;
case ATTR_EXT_COMMUNITIES:
- if ((r = community_ext_write(comm, peer->conf.ebgp,
- buf + wlen, len)) == -1)
- return (-1);
- break;
case ATTR_LARGE_COMMUNITIES:
- if ((r = community_large_write(comm, buf + wlen,
- len)) == -1)
- return (-1);
+ if (community_writebuf(comm, type, peer->conf.ebgp,
+ buf) == -1)
+ return -1;
break;
/*
* NEW to OLD conversion when sending stuff to a 2byte AS peer
flags = ATTR_OPTIONAL|ATTR_TRANSITIVE;
if (!(asp->flags & F_PREFIX_ANNOUNCED))
flags |= ATTR_PARTIAL;
- if (plen == 0)
- r = 0;
- else if ((r = attr_write(buf + wlen, len, flags,
- ATTR_AS4_PATH, pdata, plen)) == -1)
- return (-1);
+ if (plen != 0)
+ if (attr_writebuf(buf, flags,
+ ATTR_AS4_PATH, pdata, plen) == -1)
+ return -1;
}
break;
case ATTR_AS4_AGGREGATOR:
flags = ATTR_OPTIONAL|ATTR_TRANSITIVE;
if (!(asp->flags & F_PREFIX_ANNOUNCED))
flags |= ATTR_PARTIAL;
- if ((r = attr_write(buf + wlen, len, flags,
+ if (attr_writebuf(buf, flags,
ATTR_AS4_AGGREGATOR, newaggr->data,
- newaggr->len)) == -1)
- return (-1);
+ newaggr->len) == -1)
+ return -1;
}
break;
/*
case ATTR_ATOMIC_AGGREGATE:
if (oa == NULL || oa->type != type)
break;
- if ((r = attr_write(buf + wlen, len,
- ATTR_WELL_KNOWN, ATTR_ATOMIC_AGGREGATE,
- NULL, 0)) == -1)
- return (-1);
+ if (attr_writebuf(buf, ATTR_WELL_KNOWN,
+ ATTR_ATOMIC_AGGREGATE, NULL, 0) == -1)
+ return -1;
break;
case ATTR_AGGREGATOR:
if (oa == NULL || oa->type != type)
break;
+ if ((!(oa->flags & ATTR_TRANSITIVE)) &&
+ peer->conf.ebgp)
+ break;
if (!peer_has_as4byte(peer)) {
/* need to deflate the aggregator */
- uint8_t t[6];
+ uint8_t t[6];
uint16_t tas;
if ((!(oa->flags & ATTR_TRANSITIVE)) &&
- peer->conf.ebgp) {
- r = 0;
+ peer->conf.ebgp)
break;
- }
memcpy(&tmp32, oa->data, sizeof(tmp32));
if (ntohl(tmp32) > USHRT_MAX) {
memcpy(t + sizeof(tas),
oa->data + sizeof(tmp32),
oa->len - sizeof(tmp32));
- if ((r = attr_write(buf + wlen, len,
- oa->flags, oa->type, &t, sizeof(t))) == -1)
- return (-1);
- break;
+ if (attr_writebuf(buf, oa->flags,
+ oa->type, &t, sizeof(t)) == -1)
+ return -1;
+ } else {
+ if (attr_writebuf(buf, oa->flags, oa->type,
+ oa->data, oa->len) == -1)
+ return -1;
}
- /* FALLTHROUGH */
+ break;
case ATTR_ORIGINATOR_ID:
case ATTR_CLUSTER_LIST:
case ATTR_OTC:
if (oa == NULL || oa->type != type)
break;
if ((!(oa->flags & ATTR_TRANSITIVE)) &&
- peer->conf.ebgp) {
- r = 0;
+ peer->conf.ebgp)
break;
- }
- if ((r = attr_write(buf + wlen, len,
- oa->flags, oa->type, oa->data, oa->len)) == -1)
- return (-1);
+ if (attr_writebuf(buf, oa->flags, oa->type,
+ oa->data, oa->len) == -1)
+ return -1;
break;
default:
if (oa == NULL && type >= ATTR_FIRST_UNKNOWN)
/* there is no attribute left to dump */
- goto done;
+ return (0);
if (oa == NULL || oa->type != type)
break;
*/
break;
}
- if ((r = attr_write(buf + wlen, len,
- oa->flags | ATTR_PARTIAL, oa->type,
- oa->data, oa->len)) == -1)
- return (-1);
+ if (attr_writebuf(buf, oa->flags | ATTR_PARTIAL,
+ oa->type, oa->data, oa->len) == -1)
+ return -1;
}
- wlen += r;
- len -= r;
}
-done:
- return (wlen);
+ return 0;
}
/*
/* minimal buffer size > withdraw len + attr len + attr hdr + afi/safi */
#define MIN_UPDATE_LEN 16
+static void
+up_prefix_free(struct prefix_tree *prefix_head, struct prefix *p,
+ struct rde_peer *peer, int withdraw)
+{
+ if (withdraw) {
+ /* prefix no longer needed, remove it */
+ prefix_adjout_destroy(p);
+ peer->stats.prefix_sent_withdraw++;
+ } else {
+ /* prefix still in Adj-RIB-Out, keep it */
+ RB_REMOVE(prefix_tree, prefix_head, p);
+ p->flags &= ~PREFIX_FLAG_UPDATE;
+ peer->stats.pending_update--;
+ peer->stats.prefix_sent_update++;
+ }
+}
+
/*
* Write prefixes to buffer until either there is no more space or
* the next prefix has no longer the same ASPATH attributes.
+ * Returns -1 if no prefix was written else 0.
*/
static int
-up_dump_prefix(u_char *buf, int len, struct prefix_tree *prefix_head,
+up_dump_prefix(struct ibuf *buf, struct prefix_tree *prefix_head,
struct rde_peer *peer, int withdraw)
{
struct prefix *p, *np;
- uint32_t pathid;
- int r, wpos = 0, done = 0;
+ int done = 0, has_ap = -1, rv = -1;
RB_FOREACH_SAFE(p, prefix_tree, prefix_head, np) {
- if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) {
- if (len <= wpos + (int)sizeof(pathid))
- break;
- pathid = htonl(p->path_id_tx);
- memcpy(buf + wpos, &pathid, sizeof(pathid));
- wpos += sizeof(pathid);
- }
- if ((r = pt_write(buf + wpos, len - wpos, p->pt,
- withdraw)) == -1) {
- if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND))
- wpos -= sizeof(pathid);
+ if (has_ap == -1)
+ has_ap = peer_has_add_path(peer, p->pt->aid,
+ CAPA_AP_SEND);
+ if (pt_writebuf(buf, p->pt, withdraw, has_ap, p->path_id_tx) ==
+ -1)
break;
- }
- wpos += r;
/* make sure we only dump prefixes which belong together */
if (np == NULL ||
(np->flags & PREFIX_FLAG_EOR))
done = 1;
- if (withdraw) {
- /* prefix no longer needed, remove it */
- prefix_adjout_destroy(p);
- peer->stats.prefix_sent_withdraw++;
- } else {
- /* prefix still in Adj-RIB-Out, keep it */
- RB_REMOVE(prefix_tree, prefix_head, p);
- p->flags &= ~PREFIX_FLAG_UPDATE;
- peer->stats.pending_update--;
- peer->stats.prefix_sent_update++;
- }
-
+ rv = 0;
+ up_prefix_free(prefix_head, p, peer, withdraw);
if (done)
break;
}
- return (wpos);
-}
-
-int
-up_dump_withdraws(u_char *buf, int len, struct rde_peer *peer, uint8_t aid)
-{
- uint16_t wpos, wd_len;
- int r;
-
- if (len < MIN_UPDATE_LEN)
- return (-1);
-
- /* reserve space for the length field */
- wpos = 2;
- r = up_dump_prefix(buf + wpos, len - wpos, &peer->withdraws[aid],
- peer, 1);
- wd_len = htons(r);
- memcpy(buf, &wd_len, 2);
-
- return (wpos + r);
-}
-
-int
-up_dump_mp_unreach(u_char *buf, int len, struct rde_peer *peer, uint8_t aid)
-{
- u_char *attrbuf;
- int wpos, r;
- uint16_t attr_len, tmp;
-
- if (len < MIN_UPDATE_LEN || RB_EMPTY(&peer->withdraws[aid]))
- return (-1);
-
- /* reserve space for withdraw len, attr len */
- wpos = 2 + 2;
- attrbuf = buf + wpos;
-
- /* attribute header, defaulting to extended length one */
- attrbuf[0] = ATTR_OPTIONAL | ATTR_EXTLEN;
- attrbuf[1] = ATTR_MP_UNREACH_NLRI;
- wpos += 4;
-
- /* afi & safi */
- if (aid2afi(aid, &tmp, buf + wpos + 2))
- fatalx("up_dump_mp_unreach: bad AID");
- tmp = htons(tmp);
- memcpy(buf + wpos, &tmp, sizeof(uint16_t));
- wpos += 3;
-
- r = up_dump_prefix(buf + wpos, len - wpos, &peer->withdraws[aid],
- peer, 1);
- if (r == 0)
- return (-1);
- wpos += r;
- attr_len = r + 3; /* prefixes + afi & safi */
-
- /* attribute length */
- attr_len = htons(attr_len);
- memcpy(attrbuf + 2, &attr_len, sizeof(attr_len));
-
- /* write length fields */
- memset(buf, 0, sizeof(uint16_t)); /* withdrawn routes len */
- attr_len = htons(wpos - 4);
- memcpy(buf + 2, &attr_len, sizeof(attr_len));
-
- return (wpos);
-}
-
-int
-up_dump_attrnlri(u_char *buf, int len, struct rde_peer *peer)
-{
- struct filterstate state;
- struct prefix *p;
- int r, wpos;
- uint16_t attr_len;
-
- if (len < 2)
- fatalx("up_dump_attrnlri: buffer way too small");
- if (len < MIN_UPDATE_LEN)
- goto done;
-
- p = RB_MIN(prefix_tree, &peer->updates[AID_INET]);
- if (p == NULL)
- goto done;
-
- rde_filterstate_prep(&state, p);
- r = up_generate_attr(buf + 2, len - 2, peer, &state, AID_INET);
- rde_filterstate_clean(&state);
- if (r == -1) {
- /*
- * either no packet or not enough space.
- * The length field needs to be set to zero else it would be
- * an invalid bgp update.
- */
-done:
- memset(buf, 0, 2);
- return (2);
- }
-
- /* first dump the 2-byte path attribute length */
- attr_len = htons(r);
- memcpy(buf, &attr_len, 2);
- wpos = 2;
- /* then skip over the already dumped path attributes themselves */
- wpos += r;
-
- /* last but not least dump the nlri */
- r = up_dump_prefix(buf + wpos, len - wpos, &peer->updates[AID_INET],
- peer, 0);
- wpos += r;
-
- return (wpos);
+ return rv;
}
static int
-up_generate_mp_reach(u_char *buf, int len, struct rde_peer *peer,
- struct filterstate *state, uint8_t aid)
+up_generate_mp_reach(struct ibuf *buf, struct rde_peer *peer,
+ struct nexthop *nh, uint8_t aid)
{
- struct bgpd_addr *nexthop;
- u_char *attrbuf;
- int r, wpos, attrlen;
- uint16_t tmp;
+ struct bgpd_addr *nexthop;
+ size_t off;
+ uint16_t len, afi;
+ uint8_t safi;
- if (len < 4)
- return (-1);
/* attribute header, defaulting to extended length one */
- buf[0] = ATTR_OPTIONAL | ATTR_EXTLEN;
- buf[1] = ATTR_MP_REACH_NLRI;
- wpos = 4;
- attrbuf = buf + wpos;
+ if (ibuf_add_n8(buf, ATTR_OPTIONAL | ATTR_EXTLEN) == -1)
+ return -1;
+ if (ibuf_add_n8(buf, ATTR_MP_REACH_NLRI) == -1)
+ return -1;
+ off = ibuf_size(buf);
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
+
+ if (aid2afi(aid, &afi, &safi))
+ fatalx("up_generate_mp_reach: bad AID");
+
+ /* AFI + SAFI + NH LEN + NH + Reserved */
+ if (ibuf_add_n16(buf, afi) == -1)
+ return -1;
+ if (ibuf_add_n8(buf, safi) == -1)
+ return -1;
switch (aid) {
case AID_INET6:
- attrlen = 21; /* AFI + SAFI + NH LEN + NH + Reserved */
- if (len < wpos + attrlen)
- return (-1);
- wpos += attrlen;
- if (aid2afi(aid, &tmp, &attrbuf[2]))
- fatalx("up_generate_mp_reach: bad AID");
- tmp = htons(tmp);
- memcpy(attrbuf, &tmp, sizeof(tmp));
- attrbuf[3] = sizeof(struct in6_addr);
- attrbuf[20] = 0; /* Reserved must be 0 */
-
+ /* NH LEN */
+ if (ibuf_add_n8(buf, sizeof(struct in6_addr)) == -1)
+ return -1;
/* write nexthop */
- attrbuf += 4;
- nexthop = &state->nexthop->exit_nexthop;
- memcpy(attrbuf, &nexthop->v6, sizeof(struct in6_addr));
+ nexthop = &nh->exit_nexthop;
+ if (ibuf_add(buf, &nexthop->v6, sizeof(struct in6_addr)) == -1)
+ return -1;
break;
case AID_VPN_IPv4:
- attrlen = 17; /* AFI + SAFI + NH LEN + NH + Reserved */
- if (len < wpos + attrlen)
- return (-1);
- wpos += attrlen;
- if (aid2afi(aid, &tmp, &attrbuf[2]))
- fatalx("up_generate_mp_reachi: bad AID");
- tmp = htons(tmp);
- memcpy(attrbuf, &tmp, sizeof(tmp));
- attrbuf[3] = sizeof(uint64_t) + sizeof(struct in_addr);
- memset(attrbuf + 4, 0, sizeof(uint64_t));
- attrbuf[16] = 0; /* Reserved must be 0 */
-
+ /* NH LEN */
+ if (ibuf_add_n8(buf,
+ sizeof(uint64_t) + sizeof(struct in_addr)) == -1)
+ return -1;
+ /* write zero rd */
+ if (ibuf_add_zero(buf, sizeof(uint64_t)) == -1)
+ return -1;
/* write nexthop */
- attrbuf += 12;
- nexthop = &state->nexthop->exit_nexthop;
- memcpy(attrbuf, &nexthop->v4, sizeof(struct in_addr));
+ nexthop = &nh->exit_nexthop;
+ if (ibuf_add(buf, &nexthop->v4, sizeof(struct in_addr)) == -1)
+ return -1;
break;
case AID_VPN_IPv6:
- attrlen = 29; /* AFI + SAFI + NH LEN + NH + Reserved */
- if (len < wpos + attrlen)
- return (-1);
- wpos += attrlen;
- if (aid2afi(aid, &tmp, &attrbuf[2]))
- fatalx("up_generate_mp_reachi: bad AID");
- tmp = htons(tmp);
- memcpy(attrbuf, &tmp, sizeof(tmp));
- attrbuf[3] = sizeof(uint64_t) + sizeof(struct in6_addr);
- memset(attrbuf + 4, 0, sizeof(uint64_t));
- attrbuf[28] = 0; /* Reserved must be 0 */
-
+ /* NH LEN */
+ if (ibuf_add_n8(buf,
+ sizeof(uint64_t) + sizeof(struct in6_addr)) == -1)
+ return -1;
+ /* write zero rd */
+ if (ibuf_add_zero(buf, sizeof(uint64_t)) == -1)
+ return -1;
/* write nexthop */
- attrbuf += 12;
- nexthop = &state->nexthop->exit_nexthop;
- memcpy(attrbuf, &nexthop->v6, sizeof(struct in6_addr));
+ nexthop = &nh->exit_nexthop;
+ if (ibuf_add(buf, &nexthop->v6, sizeof(struct in6_addr)) == -1)
+ return -1;
break;
case AID_FLOWSPECv4:
case AID_FLOWSPECv6:
- attrlen = 5; /* AFI + SAFI + NH LEN + NH + Reserved */
- if (len < wpos + attrlen)
- return (-1);
- wpos += attrlen;
- if (aid2afi(aid, &tmp, &attrbuf[2]))
- fatalx("up_generate_mp_reachi: bad AID");
- tmp = htons(tmp);
- memcpy(attrbuf, &tmp, sizeof(tmp));
- attrbuf[3] = 0; /* nh len MUST be 0 */
- attrbuf[4] = 0; /* Reserved must be 0 */
+ if (ibuf_add_zero(buf, 1) == -1) /* NH LEN MUST be 0 */
+ return -1;
+ /* no NH */
break;
default:
fatalx("up_generate_mp_reach: unknown AID");
}
- r = up_dump_prefix(buf + wpos, len - wpos, &peer->updates[aid],
- peer, 0);
- if (r == 0) {
- /* no prefixes written ... */
+ if (ibuf_add_zero(buf, 1) == -1) /* Reserved must be 0 */
+ return -1;
+
+ if (up_dump_prefix(buf, &peer->updates[aid], peer, 0) == -1)
+ /* no prefixes written, fail update */
return (-1);
- }
- attrlen += r;
- wpos += r;
- /* update attribute length field */
- tmp = htons(attrlen);
- memcpy(buf + 2, &tmp, sizeof(tmp));
- return (wpos);
+ /* update MP_REACH attribute length field */
+ len = ibuf_size(buf) - off - sizeof(len);
+ if (ibuf_set_n16(buf, off, len) == -1)
+ return -1;
+
+ return 0;
}
+/*
+ * Generate UPDATE message containing either just withdraws or updates.
+ * UPDATE messages are contructed like this:
+ *
+ * +-----------------------------------------------------+
+ * | Withdrawn Routes Length (2 octets) |
+ * +-----------------------------------------------------+
+ * | Withdrawn Routes (variable) |
+ * +-----------------------------------------------------+
+ * | Total Path Attribute Length (2 octets) |
+ * +-----------------------------------------------------+
+ * | Path Attributes (variable) |
+ * +-----------------------------------------------------+
+ * | Network Layer Reachability Information (variable) |
+ * +-----------------------------------------------------+
+ *
+ * Multiprotocol messages use MP_REACH_NLRI and MP_UNREACH_NLRI
+ * the latter will be the only path attribute in a message.
+ */
+
+/*
+ * Write UPDATE message for withdrawn routes. The size of buf limits
+ * how may routes can be added. Return 0 on success -1 on error which
+ * includes generating an empty withdraw message.
+ */
int
-up_dump_mp_reach(u_char *buf, int len, struct rde_peer *peer, uint8_t aid)
+up_dump_withdraws(struct ibuf *buf, struct rde_peer *peer, uint8_t aid)
{
- struct filterstate state;
- struct prefix *p;
- int r, wpos;
- uint16_t attr_len;
+ size_t off;
+ uint16_t afi, len;
+ uint8_t safi;
+
+ /* reserve space for the withdrawn routes length field */
+ off = ibuf_size(buf);
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
+
+ if (aid != AID_INET) {
+ /* reserve space for 2-byte path attribute length */
+ off = ibuf_size(buf);
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
+
+ /* attribute header, defaulting to extended length one */
+ if (ibuf_add_n8(buf, ATTR_OPTIONAL | ATTR_EXTLEN) == -1)
+ return -1;
+ if (ibuf_add_n8(buf, ATTR_MP_UNREACH_NLRI) == -1)
+ return -1;
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
+
+ /* afi & safi */
+ if (aid2afi(aid, &afi, &safi))
+ fatalx("up_dump_mp_unreach: bad AID");
+ if (ibuf_add_n16(buf, afi) == -1)
+ return -1;
+ if (ibuf_add_n8(buf, safi) == -1)
+ return -1;
+ }
- if (len < MIN_UPDATE_LEN)
- return 0;
+ if (up_dump_prefix(buf, &peer->withdraws[aid], peer, 1) == -1)
+ return -1;
+
+ /* update length field (either withdrawn routes or attribute length) */
+ len = ibuf_size(buf) - off - sizeof(len);
+ if (ibuf_set_n16(buf, off, len) == -1)
+ return -1;
+
+ if (aid != AID_INET) {
+ /* write MP_UNREACH_NLRI attribute length (always extended) */
+ len -= 4; /* skip attribute header */
+ if (ibuf_set_n16(buf, off + sizeof(len) + 2, len) == -1)
+ return -1;
+ } else {
+ /* no extra attributes so set attribute len to 0 */
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Write UPDATE message for changed and added routes. The size of buf limits
+ * how may routes can be added. The function first dumps the path attributes
+ * and then tries to add as many prefixes using these attributes.
+ * Return 0 on success -1 on error which includes producing an empty message.
+ */
+int
+up_dump_update(struct ibuf *buf, struct rde_peer *peer, uint8_t aid)
+{
+ struct bgpd_addr addr;
+ struct prefix *p;
+ size_t off;
+ uint16_t len;
- /* get starting point */
p = RB_MIN(prefix_tree, &peer->updates[aid]);
if (p == NULL)
- return 0;
+ return -1;
- wpos = 4; /* reserve space for length fields */
+ /* withdrawn routes length field is 0 */
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
- rde_filterstate_prep(&state, p);
+ /* reserve space for 2-byte path attribute length */
+ off = ibuf_size(buf);
+ if (ibuf_add_zero(buf, sizeof(len)) == -1)
+ return -1;
- /* write regular path attributes */
- r = up_generate_attr(buf + wpos, len - wpos, peer, &state, aid);
- if (r == -1) {
- rde_filterstate_clean(&state);
- return 0;
+ if (up_generate_attr(buf, peer, prefix_aspath(p),
+ prefix_communities(p), prefix_nexthop(p), aid) == -1)
+ goto fail;
+
+ if (aid != AID_INET) {
+ /* write mp attribute including nlri */
+
+ /*
+ * RFC 7606 wants this to be first but then we need
+ * to use multiple buffers with adjusted length to
+ * merge the attributes together in reverse order of
+ * creation.
+ */
+ if (up_generate_mp_reach(buf, peer, prefix_nexthop(p), aid) ==
+ -1)
+ goto fail;
}
- wpos += r;
- /* write mp attribute */
- r = up_generate_mp_reach(buf + wpos, len - wpos, peer, &state, aid);
- rde_filterstate_clean(&state);
- if (r == -1)
- return 0;
- wpos += r;
+ /* update attribute length field */
+ len = ibuf_size(buf) - off - sizeof(len);
+ if (ibuf_set_n16(buf, off, len) == -1)
+ return -1;
+
+ if (aid == AID_INET) {
+ /* last but not least dump the IPv4 nlri */
+ if (up_dump_prefix(buf, &peer->updates[aid], peer, 0) == -1)
+ goto fail;
+ }
+
+ return 0;
- /* write length fields */
- memset(buf, 0, sizeof(uint16_t)); /* withdrawn routes len */
- attr_len = htons(wpos - 4);
- memcpy(buf + 2, &attr_len, sizeof(attr_len));
+fail:
+ /* Not enough space. Drop prefix, it will never fit. */
+ pt_getaddr(p->pt, &addr);
+ log_peer_warnx(&peer->conf, "path attributes to large, "
+ "prefix %s/%d dropped", log_addr(&addr), p->pt->prefixlen);
- return (wpos);
+ up_prefix_free(&peer->updates[AID_INET], p, peer, 0);
+ /* XXX should probably send a withdraw for this prefix */
+ return -1;
}
-/* $OpenBSD: session.c,v 1.445 2023/05/25 14:20:25 claudio Exp $ */
+/* $OpenBSD: session.c,v 1.446 2023/07/12 14:45:43 claudio Exp $ */
/*
* Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org>
if ((mrt->peer_id == 0 && mrt->group_id == 0) ||
mrt->peer_id == p->conf.id || (mrt->group_id != 0 &&
mrt->group_id == p->conf.groupid))
- mrt_dump_bgp_msg(mrt, msg->buf->buf, msg->len, p,
+ mrt_dump_bgp_msg(mrt, ibuf_data(msg->buf), msg->len, p,
msg->type);
}
uint8_t op_len = optparamlen;
errs += ibuf_add(buf->buf, &op_len, 1);
}
- errs += ibuf_add(buf->buf, opb->buf, ibuf_size(opb));
+ errs += ibuf_add_buf(buf->buf, opb);
}
ibuf_free(opb);