#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/attrs.h"
-#include "nest/mrtdump.h"
+#include "proto/mrt/mrt.h"
#include "conf/conf.h"
#include "lib/unaligned.h"
#include "lib/flowspec.h"
return (get_u16(buf) << 16) | buf[3];
}
-/*
- * MRT Dump format is not semantically specified.
- * We will use these values in appropriate fields:
- *
- * Local AS, Remote AS - configured AS numbers for given BGP instance.
- * Local IP, Remote IP - IP addresses of the TCP connection (0 if no connection)
- *
- * We dump two kinds of MRT messages: STATE_CHANGE (for BGP state
- * changes) and MESSAGE (for received BGP messages).
- *
- * STATE_CHANGE uses always AS4 variant, but MESSAGE uses AS4 variant
- * only when AS4 session is established and even in that case MESSAGE
- * does not use AS4 variant for initial OPEN message. This strange
- * behavior is here for compatibility with Quagga and Bgpdump,
- */
-
-static byte *
-mrt_put_bgp4_hdr(byte *buf, struct bgp_conn *conn, int as4)
+static void
+init_mrt_bgp_data(struct bgp_conn *conn, struct mrt_bgp_data *d)
{
struct bgp_proto *p = conn->bgp;
- uint v4 = ipa_is_ip4(p->cf->remote_ip);
+ int p_ok = conn->state >= BS_OPENCONFIRM;
- if (as4)
- {
- put_u32(buf+0, p->remote_as);
- put_u32(buf+4, p->public_as);
- buf+=8;
- }
- else
- {
- put_u16(buf+0, (p->remote_as <= 0xFFFF) ? p->remote_as : AS_TRANS);
- put_u16(buf+2, (p->public_as <= 0xFFFF) ? p->public_as : AS_TRANS);
- buf+=4;
- }
+ memset(d, 0, sizeof(struct mrt_bgp_data));
+ d->peer_as = p->remote_as;
+ d->local_as = p->local_as;
+ d->index = (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0;
+ d->af = ipa_is_ip4(p->remote_ip) ? BGP_AFI_IPV4 : BGP_AFI_IPV6;
+ d->peer_ip = conn->sk ? conn->sk->daddr : IPA_NONE;
+ d->local_ip = conn->sk ? conn->sk->saddr : IPA_NONE;
+ d->as4 = p_ok ? p->as4_session : 0;
+}
- put_u16(buf+0, (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0);
- put_u16(buf+2, v4 ? BGP_AFI_IPV4 : BGP_AFI_IPV6);
- buf+=4;
+static uint bgp_find_update_afi(byte *pos, uint len);
- if (v4)
- {
- buf = put_ip4(buf, conn->sk ? ipa_to_ip4(conn->sk->daddr) : IP4_NONE);
- buf = put_ip4(buf, conn->sk ? ipa_to_ip4(conn->sk->saddr) : IP4_NONE);
- }
- else
+static int
+bgp_estimate_add_path(struct bgp_proto *p, byte *pkt, uint len)
+{
+ /* No need to estimate it for other messages than UPDATE */
+ if (pkt[18] != PKT_UPDATE)
+ return 0;
+
+ /* 1 -> no channel, 2 -> all channels, 3 -> some channels */
+ if (p->summary_add_path_rx < 3)
+ return p->summary_add_path_rx == 2;
+
+ uint afi = bgp_find_update_afi(pkt, len);
+ struct bgp_channel *c = bgp_get_channel(p, afi);
+ if (!c)
{
- buf = put_ip6(buf, conn->sk ? ipa_to_ip6(conn->sk->daddr) : IP6_NONE);
- buf = put_ip6(buf, conn->sk ? ipa_to_ip6(conn->sk->saddr) : IP6_NONE);
+ /* Either frame error (if !afi) or unknown AFI/SAFI,
+ will be reported later in regular parsing */
+ BGP_TRACE(D_PACKETS, "MRT processing noticed invalid packet");
+ return 0;
}
- return buf;
+ return c->add_path_rx;
}
static void
-mrt_dump_bgp_packet(struct bgp_conn *conn, byte *pkt, uint len)
+bgp_dump_message(struct bgp_conn *conn, byte *pkt, uint len)
{
- byte *buf = alloca(128+len); /* 128 is enough for MRT headers */
- byte *bp = buf + MRTDUMP_HDR_LENGTH;
- int as4 = conn->bgp->as4_session;
+ struct mrt_bgp_data d;
+ init_mrt_bgp_data(conn, &d);
- bp = mrt_put_bgp4_hdr(bp, conn, as4);
- memcpy(bp, pkt, len);
- bp += len;
- mrt_dump_message(&conn->bgp->p, BGP4MP, as4 ? BGP4MP_MESSAGE_AS4 : BGP4MP_MESSAGE,
- buf, bp-buf);
-}
+ d.message = pkt;
+ d.msg_len = len;
+ d.add_path = bgp_estimate_add_path(conn->bgp, pkt, len);
-static inline u16
-convert_state(uint state)
-{
- /* Convert state from our BS_* values to values used in MRTDump */
- return (state == BS_CLOSE) ? 1 : state + 1;
+ mrt_dump_bgp_message(&d);
}
void
-mrt_dump_bgp_state_change(struct bgp_conn *conn, uint old, uint new)
+bgp_dump_state_change(struct bgp_conn *conn, uint old, uint new)
{
- byte buf[128];
- byte *bp = buf + MRTDUMP_HDR_LENGTH;
+ struct mrt_bgp_data d;
+ init_mrt_bgp_data(conn, &d);
- bp = mrt_put_bgp4_hdr(bp, conn, 1);
- put_u16(bp+0, convert_state(old));
- put_u16(bp+2, convert_state(new));
- bp += 4;
- mrt_dump_message(&conn->bgp->p, BGP4MP, BGP4MP_STATE_CHANGE_AS4, buf, bp-buf);
+ d.old_state = old;
+ d.new_state = new;
+
+ mrt_dump_bgp_state_change(&d);
}
static byte *
}
-static byte *
-bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
+void
+bgp_prepare_capabilities(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
struct bgp_channel *c;
struct bgp_caps *caps;
struct bgp_af_caps *ac;
- uint any_ext_next_hop = 0;
- uint any_add_path = 0;
- byte *data;
- /* Prepare bgp_caps structure */
+ if (!p->cf->capabilities)
+ {
+ /* Just prepare empty local_caps */
+ conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
+ return;
+ }
+ /* Prepare bgp_caps structure */
int n = list_length(&p->p.channels);
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
conn->local_caps = caps;
caps->gr_flags = p->p.gr_recovery ? BGP_GRF_RESTART : 0;
}
+ if (p->cf->llgr_mode)
+ caps->llgr_aware = 1;
+
/* Allocate and fill per-AF fields */
WALK_LIST(c, p->p.channels)
{
ac->ready = 1;
ac->ext_next_hop = bgp_channel_is_ipv4(c) && c->cf->ext_next_hop;
- any_ext_next_hop |= ac->ext_next_hop;
+ caps->any_ext_next_hop |= ac->ext_next_hop;
ac->add_path = c->cf->add_path;
- any_add_path |= ac->add_path;
+ caps->any_add_path |= ac->add_path;
if (c->cf->gr_able)
{
if (p->p.gr_recovery)
ac->gr_af_flags |= BGP_GRF_FORWARDING;
}
+
+ if (c->cf->llgr_able)
+ {
+ ac->llgr_able = 1;
+ ac->llgr_time = c->cf->llgr_time;
+
+ if (p->p.gr_recovery)
+ ac->llgr_flags |= BGP_LLGRF_FORWARDING;
+ }
}
/* Sort capability fields by AFI/SAFI */
qsort(caps->af_data, caps->af_count, sizeof(struct bgp_af_caps), bgp_af_caps_cmp);
+}
+static byte *
+bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
+{
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_caps *caps = conn->local_caps;
+ struct bgp_af_caps *ac;
+ byte *buf_head = buf;
+ byte *data;
/* Create capability list in buffer */
/*
- * Note that max length is ~ 20+14*af_count. With max 12 channels that is
- * 188. Option limit is 253 and buffer size is 4096, so we cannot overflow
- * unless we add new capabilities or more AFs.
+ * Note that max length is ~ 22+21*af_count. With max 12 channels that is
+ * 274. Option limit is 253 and buffer size is 4096, so we cannot overflow
+ * unless we add new capabilities or more AFs. XXXXX
*/
WALK_AF_CAPS(caps, ac)
*buf++ = 0; /* Capability data length */
}
- if (any_ext_next_hop)
+ if (caps->any_ext_next_hop)
{
*buf++ = 5; /* Capability 5: Support for extended next hop */
*buf++ = 0; /* Capability data length, will be fixed later */
buf += 4;
}
- if (any_add_path)
+ if (caps->any_add_path)
{
*buf++ = 69; /* Capability 69: Support for ADD-PATH */
*buf++ = 0; /* Capability data length, will be fixed later */
*buf++ = 0; /* Capability data length */
}
+ if (caps->llgr_aware)
+ {
+ *buf++ = 71; /* Capability 71: Support for long-lived graceful restart */
+ *buf++ = 0; /* Capability data length, will be fixed later */
+ data = buf;
+
+ WALK_AF_CAPS(caps, ac)
+ if (ac->llgr_able)
+ {
+ put_af3(buf, ac->afi);
+ buf[3] = ac->llgr_flags;
+ put_u24(buf+4, ac->llgr_time);
+ buf += 7;
+ }
+
+ data[-1] = buf - data;
+ }
+
+ caps->length = buf - buf_head;
+
return buf;
}
int i, cl;
u32 af;
+ caps->length += len;
+
while (len > 0)
{
if (len < 2 || len < (2 + pos[1]))
caps->enhanced_refresh = 1;
break;
+ case 71: /* Long lived graceful restart capability, RFC draft */
+ if (cl % 7)
+ goto err;
+
+ /* Presumably, only the last instance is valid */
+ WALK_AF_CAPS(caps, ac)
+ {
+ ac->llgr_able = 0;
+ ac->llgr_flags = 0;
+ ac->llgr_time = 0;
+ }
+
+ caps->llgr_aware = 1;
+
+ for (i = 0; i < cl; i += 7)
+ {
+ af = get_af3(pos+2+i);
+ ac = bgp_get_af_caps(caps, af);
+ ac->llgr_able = 1;
+ ac->llgr_flags = pos[2+i+3];
+ ac->llgr_time = get_u24(pos + 2+i+4);
+ }
+ break;
+
/* We can safely ignore all other capabilities */
}
ADVANCE(pos, len, 2 + cl);
}
+
+ /* The LLGR capability must be advertised together with the GR capability,
+ otherwise it must be disregarded */
+ if (!caps->gr_aware && caps->llgr_aware)
+ {
+ caps->llgr_aware = 0;
+ WALK_AF_CAPS(caps, ac)
+ {
+ ac->llgr_able = 0;
+ ac->llgr_flags = 0;
+ ac->llgr_time = 0;
+ }
+ }
+
return;
err:
return;
}
+static int
+bgp_check_capabilities(struct bgp_conn *conn)
+{
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_caps *local = conn->local_caps;
+ struct bgp_caps *remote = conn->remote_caps;
+ struct bgp_channel *c;
+ int count = 0;
+
+ /* This is partially overlapping with bgp_conn_enter_established_state(),
+ but we need to run this just after we receive OPEN message */
+
+ WALK_LIST(c, p->p.channels)
+ {
+ const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
+ const struct bgp_af_caps *rem = bgp_find_af_caps(remote, c->afi);
+
+ /* Find out whether this channel will be active */
+ int active = loc && loc->ready &&
+ ((rem && rem->ready) || (!remote->length && (c->afi == BGP_AF_IPV4)));
+
+ /* Mandatory must be active */
+ if (c->cf->mandatory && !active)
+ return 0;
+
+ if (active)
+ count++;
+ }
+
+ /* We need at least one channel active */
+ if (!count)
+ return 0;
+
+ return 1;
+}
+
static int
bgp_read_options(struct bgp_conn *conn, byte *pos, int len)
{
}
else
{
- /* Prepare empty local_caps */
- conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
-
buf[9] = 0; /* No optional parameters */
return buf + 10;
}
if (!id || (p->is_internal && id == p->local_id))
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
+ /* RFC 5492 4 - check for required capabilities */
+ if (p->cf->capabilities && !bgp_check_capabilities(conn))
+ { bgp_error(conn, 2, 7, NULL, 0); return; }
+
struct bgp_caps *caps = conn->remote_caps;
if (caps->as4_support)
if ((as4 != asn) && (asn != AS_TRANS))
log(L_WARN "%s: Peer advertised inconsistent AS numbers", p->p.name);
- if (as4 != p->remote_as)
+ /* When remote ASN is unspecified, it must be external one */
+ if (p->remote_as ? (as4 != p->remote_as) : (as4 == p->local_as))
{ as4 = htonl(as4); bgp_error(conn, 2, 2, (byte *) &as4, 4); return; }
+
+ conn->received_as = as4;
}
else
{
- if (asn != p->remote_as)
+ if (p->remote_as ? (asn != p->remote_as) : (asn == p->local_as))
{ bgp_error(conn, 2, 2, pkt+20, 2); return; }
+
+ conn->received_as = asn;
}
/* Check the other connection */
/* GW_DIRECT -> single_hop -> p->neigh != NULL */
if (ipa_nonzero(gw))
- nbr = neigh_find2(&p->p, &gw, NULL, 0);
+ nbr = neigh_find(&p->p, gw, NULL, 0);
else if (ipa_nonzero(ll))
- nbr = neigh_find2(&p->p, &ll, p->neigh->iface, 0);
+ nbr = neigh_find(&p->p, ll, p->neigh->iface, 0);
if (!nbr || (nbr->scope == SCOPE_HOST))
WITHDRAW(BAD_NEXT_HOP);
}
+static int
+bgp_match_src(struct bgp_export_state *s, int mode)
+{
+ switch (mode)
+ {
+ case NH_NO: return 0;
+ case NH_ALL: return 1;
+ case NH_IBGP: return s->src && s->src->is_internal;
+ case NH_EBGP: return s->src && !s->src->is_internal;
+ default: return 0;
+ }
+}
+
static inline int
bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
{
struct bgp_proto *p = s->proto;
+ struct bgp_channel *c = s->channel;
ip_addr *nh = (void *) a->u.ptr->data;
- if (s->channel->cf->next_hop_self)
+ /* Handle next hop self option */
+ if (c->cf->next_hop_self && bgp_match_src(s, c->cf->next_hop_self))
return 0;
- if (s->channel->cf->next_hop_keep)
+ /* Handle next hop keep option */
+ if (c->cf->next_hop_keep && bgp_match_src(s, c->cf->next_hop_keep))
return 1;
/* Keep it when explicitly set in export filter */
if (a->type & EAF_FRESH)
return 1;
+ /* Check for non-matching AF */
+ if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
+ return 0;
+
/* Keep it when exported to internal peers */
if (p->is_interior && ipa_nonzero(*nh))
return 1;
bgp_use_gateway(struct bgp_export_state *s)
{
struct bgp_proto *p = s->proto;
+ struct bgp_channel *c = s->channel;
rta *ra = s->route->attrs;
- if (s->channel->cf->next_hop_self)
+ /* Handle next hop self option - also applies to gateway */
+ if (c->cf->next_hop_self && bgp_match_src(s, c->cf->next_hop_self))
return 0;
/* We need one valid global gateway */
if ((ra->dest != RTD_UNICAST) || ra->nh.next || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
return 0;
+ /* Check for non-matching AF */
+ if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
+ return 0;
+
/* Use it when exported to internal peers */
if (p->is_interior)
return 1;
/* TODO: Use local MPLS assigned label */
if (s->mpls)
- bgp_unset_attr(to, s->pool, BA_MPLS_LABEL_STACK);
+ {
+ u32 implicit_null = BGP_MPLS_NULL;
+ bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4);
+ }
}
}
WITHDRAW(NO_NEXT_HOP);
ip_addr *nh = (void *) a->u.ptr->data;
- ip_addr peer = s->proto->cf->remote_ip;
+ ip_addr peer = s->proto->remote_ip;
uint len = a->u.ptr->length;
/* Forbid zero next hop */
if (!a0)
{
/* Route withdraw */
- rte_update2(&s->channel->c, n, NULL, s->last_src);
+ rte_update3(&s->channel->c, n, NULL, s->last_src);
return;
}
e->pflags = 0;
e->u.bgp.suppressed = 0;
- rte_update2(&s->channel->c, n, e, s->last_src);
+ e->u.bgp.stale = -1;
+ rte_update3(&s->channel->c, n, e, s->last_src);
}
static void
-bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, adata *mpls, byte **pos, uint *size, byte *pxlen)
+bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, const adata *mpls, byte **pos, uint *size, byte *pxlen)
{
- u32 dummy = 0;
- u32 *labels = mpls ? (u32 *) mpls->data : &dummy;
+ const u32 dummy = 0;
+ const u32 *labels = mpls ? (const u32 *) mpls->data : &dummy;
uint lnum = mpls ? (mpls->length / 4) : 1;
for (uint i = 0; i < lnum; i++)
ADVANCE(*pos, *len, 3);
*pxlen -= 24;
- /* Withdraw: Magic label stack value 0x800000 according to RFC 3107, section 3, last paragraph */
- if (!a && !s->err_withdraw && (lnum == 1) && (label == BGP_MPLS_MAGIC))
- break;
+ /* RFC 8277 2.4 - withdraw does not have variable-size MPLS stack but
+ fixed-size 24-bit Compatibility field, which MUST be ignored */
+ if (!a && !s->err_withdraw)
+ return;
}
while (!(label & BGP_MPLS_BOS));
ADVANCE(pos, size, 1);
/* Encode MPLS labels */
- bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
+ if (s->mpls)
+ bgp_encode_mpls_labels(s, s->mpls_labels, &pos, &size, pos - 1);
/* Encode route distinguisher */
put_u64(pos, net->rd);
uint pxlen = data[1];
// FIXME: Use some generic function
- memcpy(&px, data, BYTES(pxlen));
- px = ip4_and(px, ip4_mkmask(pxlen));
+ memcpy(&px, data+2, BYTES(pxlen));
+ px = ip4_and(ip4_ntoh(px), ip4_mkmask(pxlen));
/* Prepare the flow */
net_addr *n = alloca(sizeof(struct net_addr_flow4) + flen);
uint pxlen = data[1];
// FIXME: Use some generic function
- memcpy(&px, data, BYTES(pxlen));
- px = ip6_and(px, ip6_mkmask(pxlen));
+ memcpy(&px, data+2, BYTES(pxlen));
+ px = ip6_and(ip6_ntoh(px), ip6_mkmask(pxlen));
/* Prepare the flow */
net_addr *n = alloca(sizeof(struct net_addr_flow6) + flen);
.proto = p,
.channel = c,
.pool = bgp_linpool,
+ .mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
.as4_session = p->as4_session,
.add_path = c->add_path_tx,
.mpls = c->desc->mpls,
goto again;
}
- res = (c->afi == BGP_AF_IPV4) && !c->ext_next_hop ?
+ res = !s.mp_reach ?
bgp_create_ip_reach(&s, buck, buf, end):
bgp_create_mp_reach(&s, buck, buf, end);
a->source = RTS_BGP;
a->scope = SCOPE_UNIVERSE;
- a->from = s->proto->cf->remote_ip;
+ a->from = s->proto->remote_ip;
a->eattrs = ea;
c->desc->decode_next_hop(s, nh, nh_len, a);
if (s.attr_len)
ea = bgp_decode_attrs(&s, s.attrs, s.attr_len);
+ else
+ ea = NULL;
/* Check for End-of-RIB marker */
if (!s.attr_len && !s.ip_unreach_len && !s.ip_reach_len)
return;
}
+static uint
+bgp_find_update_afi(byte *pos, uint len)
+{
+ /*
+ * This is stripped-down version of bgp_rx_update(), bgp_decode_attrs() and
+ * bgp_decode_mp_[un]reach_nlri() used by MRT code in order to find out which
+ * AFI/SAFI is associated with incoming UPDATE. Returns 0 for framing errors.
+ */
+ if (len < 23)
+ return 0;
+
+ /* Assume there is no withrawn NLRI, read lengths and move to attribute list */
+ uint wlen = get_u16(pos + 19);
+ uint alen = get_u16(pos + 21);
+ ADVANCE(pos, len, 23);
+
+ /* Either non-zero withdrawn NLRI, non-zero reachable NLRI, or IPv4 End-of-RIB */
+ if ((wlen != 0) || (alen < len) || !alen)
+ return BGP_AF_IPV4;
+
+ if (alen > len)
+ return 0;
+
+ /* Process attribute list (alen == len) */
+ while (len)
+ {
+ if (len < 2)
+ return 0;
+
+ uint flags = pos[0];
+ uint code = pos[1];
+ ADVANCE(pos, len, 2);
+
+ uint ll = !(flags & BAF_EXT_LEN) ? 1 : 2;
+ if (len < ll)
+ return 0;
+
+ /* Read attribute length and move to attribute body */
+ alen = (ll == 1) ? get_u8(pos) : get_u16(pos);
+ ADVANCE(pos, len, ll);
+
+ if (len < alen)
+ return 0;
+
+ /* Found MP NLRI */
+ if ((code == BA_MP_REACH_NLRI) || (code == BA_MP_UNREACH_NLRI))
+ {
+ if (alen < 3)
+ return 0;
+
+ return BGP_AF(get_u16(pos), pos[2]);
+ }
+
+ /* Move to the next attribute */
+ ADVANCE(pos, len, alen);
+ }
+
+ /* No basic or MP NLRI, but there are some attributes -> error */
+ return 0;
+}
+
/*
* ROUTE-REFRESH
end = bgp_create_notification(conn, pkt);
return bgp_send(conn, PKT_NOTIFICATION, end - buf);
}
+ else if (s & (1 << PKT_OPEN))
+ {
+ conn->packets_to_send &= ~(1 << PKT_OPEN);
+ end = bgp_create_open(conn, pkt);
+ return bgp_send(conn, PKT_OPEN, end - buf);
+ }
else if (s & (1 << PKT_KEEPALIVE))
{
conn->packets_to_send &= ~(1 << PKT_KEEPALIVE);
bgp_start_timer(conn->keepalive_timer, conn->keepalive_time);
return bgp_send(conn, PKT_KEEPALIVE, BGP_HEADER_LENGTH);
}
- else if (s & (1 << PKT_OPEN))
- {
- conn->packets_to_send &= ~(1 << PKT_OPEN);
- end = bgp_create_open(conn, pkt);
- return bgp_send(conn, PKT_OPEN, end - buf);
- }
else while (conn->channels_to_send)
{
c = bgp_get_channel_to_send(p, conn);
if ((conn->sk->tpos == conn->sk->tbuf) && !ev_active(conn->tx_ev))
ev_schedule(conn->tx_ev);
}
-
void
bgp_kick_tx(void *vconn)
{
struct bgp_conn *conn = vconn;
DBG("BGP: kicking TX\n");
- while (bgp_fire_tx(conn) > 0)
+ uint max = 1024;
+ while (--max && (bgp_fire_tx(conn) > 0))
;
+
+ if (!max && !ev_active(conn->tx_ev))
+ ev_schedule(conn->tx_ev);
}
void
struct bgp_conn *conn = sk->data;
DBG("BGP: TX hook\n");
- while (bgp_fire_tx(conn) > 0)
+ uint max = 1024;
+ while (--max && (bgp_fire_tx(conn) > 0))
;
+
+ if (!max && !ev_active(conn->tx_ev))
+ ev_schedule(conn->tx_ev);
}
return buff;
}
+/* RFC 8203 - shutdown communication message */
+static int
+bgp_handle_message(struct bgp_proto *p, byte *data, uint len, byte **bp)
+{
+ byte *msg = data + 1;
+ uint msg_len = data[0];
+ uint i;
+
+ /* Handle zero length message */
+ if (msg_len == 0)
+ return 1;
+
+ /* Handle proper message */
+ if ((msg_len > 255) && (msg_len + 1 > len))
+ return 0;
+
+ /* Some elementary cleanup */
+ for (i = 0; i < msg_len; i++)
+ if (msg[i] < ' ')
+ msg[i] = ' ';
+
+ proto_set_message(&p->p, msg, msg_len);
+ *bp += bsprintf(*bp, ": \"%s\"", p->p.message);
+ return 1;
+}
+
void
bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode, byte *data, uint len)
{
- const byte *name;
- byte *t, argbuf[36];
+ byte argbuf[256], *t = argbuf;
uint i;
/* Don't report Cease messages generated by myself */
if (code == 6 && class == BE_BGP_TX)
return;
- name = bgp_error_dsc(code, subcode);
- t = argbuf;
+ /* Reset shutdown message */
+ if ((code == 6) && ((subcode == 2) || (subcode == 4)))
+ proto_set_message(&p->p, NULL, 0);
+
if (len)
{
- *t++ = ':';
- *t++ = ' ';
-
+ /* Bad peer AS - we would like to print the AS */
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
{
- /* Bad peer AS - we would like to print the AS */
- t += bsprintf(t, "%u", (len == 2) ? get_u16(data) : get_u32(data));
+ t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
goto done;
}
+
+ /* RFC 8203 - shutdown communication */
+ if (((code == 6) && ((subcode == 2) || (subcode == 4))))
+ if (bgp_handle_message(p, data, len, &t))
+ goto done;
+
+ *t++ = ':';
+ *t++ = ' ';
if (len > 16)
len = 16;
for (i=0; i<len; i++)
t += bsprintf(t, "%02x", data[i]);
}
- done:
+
+done:
*t = 0;
- log(L_REMOTE "%s: %s: %s%s", p->p.name, msg, name, argbuf);
+ const byte *dsc = bgp_error_dsc(code, subcode);
+ log(L_REMOTE "%s: %s: %s%s", p->p.name, msg, dsc, argbuf);
}
static void
if (err)
{
bgp_update_startup_delay(p);
- bgp_stop(p, 0);
+ bgp_stop(p, 0, NULL, 0);
+ }
+ else
+ {
+ uint subcode_bit = 1 << ((subcode <= 8) ? subcode : 0);
+ if (p->cf->disable_after_cease & subcode_bit)
+ {
+ log(L_INFO "%s: Disabled after Cease notification", p->p.name);
+ p->startup_delay = 0;
+ p->p.disabled = 1;
+ }
}
}
DBG("BGP: Got packet %02x (%d bytes)\n", type, len);
if (conn->bgp->p.mrtdump & MD_MESSAGES)
- mrt_dump_bgp_packet(conn, pkt, len);
+ bgp_dump_message(conn, pkt, len);
switch (type)
{