]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - proto/bgp/packets.c
BGP: Mandatory option for channels
[thirdparty/bird.git] / proto / bgp / packets.c
index ed1db04b229013549e4ad510e58f6502b95517e2..c09e9859e7b110841bebd5f4a868b7c8d686b8f2 100644 (file)
@@ -17,7 +17,7 @@
 #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"
@@ -90,91 +90,71 @@ get_af4(byte *buf)
   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->cf->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);
+
+  d.old_state = old;
+  d.new_state = new;
 
-  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);
+  mrt_dump_bgp_state_change(&d);
 }
 
 static byte *
@@ -237,6 +217,7 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
   struct bgp_af_caps *ac;
   uint any_ext_next_hop = 0;
   uint any_add_path = 0;
+  byte *buf_head = buf;
   byte *data;
 
   /* Prepare bgp_caps structure */
@@ -414,6 +395,8 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
     data[-1] = buf - data;
   }
 
+  caps->length = buf - buf_head;
+
   return buf;
 }
 
@@ -425,6 +408,8 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i
   int i, cl;
   u32 af;
 
+  caps->length += len;
+
   while (len > 0)
   {
     if (len < 2 || len < (2 + pos[1]))
@@ -588,6 +573,42 @@ 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)
 {
@@ -698,6 +719,10 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
   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)
@@ -871,22 +896,42 @@ bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
 }
 
 
+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;
@@ -900,15 +945,21 @@ static inline int
 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;
@@ -1179,7 +1230,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
   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;
   }
 
@@ -1200,7 +1251,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
   e->pflags = 0;
   e->u.bgp.suppressed = 0;
   e->u.bgp.stale = -1;
-  rte_update2(&s->channel->c, n, e, s->last_src);
+  rte_update3(&s->channel->c, n, e, s->last_src);
 }
 
 static void
@@ -2135,6 +2186,7 @@ again: ;
     .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,
@@ -2162,7 +2214,7 @@ again: ;
       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);
 
@@ -2389,6 +2441,67 @@ done:
   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
@@ -2890,7 +3003,7 @@ bgp_rx_packet(struct bgp_conn *conn, byte *pkt, uint len)
   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)
   {