]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements support for link-local addresses in BGP.
authorOndrej Zajicek <santiago@crfreenet.org>
Sun, 8 Jan 2012 14:28:27 +0000 (15:28 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 8 Jan 2012 14:31:34 +0000 (15:31 +0100)
Thanks Matthias Schiffer for the original patch.

lib/printf.c
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c

index c3f7074d38f6d2249079d238fa951fdd98ed8003..14af10629ffe44b4aac1e303050f0df56dfa0e42 100644 (file)
@@ -12,6 +12,8 @@
 
 #include <errno.h>
 
+#include "nest/iface.h"
+
 /* we use this so that we can do without the ctype library */
 #define is_digit(c)    ((c) >= '0' && (c) <= '9')
 
@@ -138,6 +140,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
        char *str, *start;
        const char *s;
        char ipbuf[STD_ADDRESS_P_LENGTH+1];
+       struct iface *iface;
 
        int flags;              /* flags to number() */
 
@@ -279,6 +282,21 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
                        s = ipbuf;
                        goto str;
 
+               /* Interface scope after link-local IP address */
+               case 'J':
+                       iface = va_arg(args, struct iface *);
+                       if (!iface)
+                               continue;
+                       if (!size)
+                               return -1;
+
+                       *str++ = '%';
+                       start++;
+                       size--;
+
+                       s = iface->name;
+                       goto str;
+
                /* Router/Network ID - essentially IPv4 address in u32 value */
                case 'R':
                        x = va_arg(args, u32);
index 93c1f6d6fe5fbc3b343018878d369f757aa1b70b..5a36878361cef1e22cbc3f6cb6774d516c0c64f1 100644 (file)
@@ -1001,10 +1001,13 @@ bgp_update_attrs(struct bgp_proto *p, rte *e, ea_list **attrs, struct linpool *p
     }
 
   /* iBGP -> keep next_hop, eBGP multi-hop -> use source_addr,
-     eBGP single-hop -> keep next_hop if on the same iface */
+   * eBGP single-hop -> keep next_hop if on the same iface.
+   * If the next_hop is zero (i.e. link-local), keep only if on the same iface.
+   */
   a = ea_find(e->attrs->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP));
   if (a && !p->cf->next_hop_self && 
-      (p->is_internal || (p->neigh && (e->attrs->iface == p->neigh->iface))))
+      ((p->is_internal && ipa_nonzero(*((ip_addr *) a->u.ptr->data))) ||
+       (p->neigh && (e->attrs->iface == p->neigh->iface))))
     {
       /* Leave the original next hop attribute, will check later where does it point */
     }
index 6bd18f0952d166d23db3a46f2a3f6ff601ea95b3..66fdc603ea4a983c2fb3661276dd6f7f14f76cff 100644 (file)
@@ -111,7 +111,7 @@ bgp_open(struct bgp_proto *p)
 
   if (p->cf->password)
     {
-      int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->password);
+      int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password);
       if (rv < 0)
        {
          bgp_close(p, 0);
@@ -178,7 +178,7 @@ bgp_close(struct bgp_proto *p, int apply_md5)
   bgp_counter--;
 
   if (p->cf->password && apply_md5)
-    sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, NULL);
+    sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL);
 
   if (!bgp_counter)
     {
@@ -578,6 +578,7 @@ bgp_connect(struct bgp_proto *p)    /* Enter Connect state and start establishing c
   s->type = SK_TCP_ACTIVE;
   s->saddr = p->source_addr;
   s->daddr = p->cf->remote_ip;
+  s->iface = p->neigh ? p->neigh->iface : NULL;
   s->dport = BGP_PORT;
   s->ttl = p->cf->ttl_security ? 255 : hops;
   s->rbsize = BGP_RX_BUFFER_SIZE;
@@ -585,7 +586,8 @@ bgp_connect(struct bgp_proto *p)    /* Enter Connect state and start establishing c
   s->tos = IP_PREC_INTERNET_CONTROL;
   s->password = p->cf->password;
   s->tx_hook = bgp_connected;
-  BGP_TRACE(D_EVENTS, "Connecting to %I from local address %I", s->daddr, s->saddr);
+  BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J", s->daddr, p->cf->iface,
+           s->saddr, ipa_has_link_scope(s->saddr) ? s->iface : NULL);
   bgp_setup_conn(p, conn);
   bgp_setup_sk(conn, s);
   bgp_conn_set_state(conn, BS_CONNECT);
@@ -634,14 +636,16 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
     if (pc->protocol == &proto_bgp && pc->proto)
       {
        struct bgp_proto *p = (struct bgp_proto *) pc->proto;
-       if (ipa_equal(p->cf->remote_ip, sk->daddr))
+       if (ipa_equal(p->cf->remote_ip, sk->daddr) &&
+           (!ipa_has_link_scope(sk->daddr) || (p->cf->iface == sk->iface)))
          {
            /* We are in proper state and there is no other incoming connection */
            int acc = (p->p.proto_state == PS_START || p->p.proto_state == PS_UP) &&
              (p->start_state >= BSS_CONNECT) && (!p->incoming_conn.sk);
 
-           BGP_TRACE(D_EVENTS, "Incoming connection from %I (port %d) %s",
-                     sk->daddr, sk->dport, acc ? "accepted" : "rejected");
+           BGP_TRACE(D_EVENTS, "Incoming connection from %I%J (port %d) %s",
+                     sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL,
+                     sk->dport, acc ? "accepted" : "rejected");
 
            if (!acc)
              goto err;
@@ -667,7 +671,8 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED)
          }
       }
 
-  log(L_WARN "BGP: Unexpected connect from unknown address %I (port %d)", sk->daddr, sk->dport);
+  log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)",
+      sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport);
  err:
   rfree(sk);
   return 0;
@@ -713,6 +718,7 @@ bgp_start_neighbor(struct bgp_proto *p)
 {
   /* Called only for single-hop BGP sessions */
 
+  /* Remove this ? */
   if (ipa_zero(p->source_addr))
     p->source_addr = p->neigh->iface->addr->ip; 
 
@@ -742,7 +748,7 @@ bgp_neigh_notify(neighbor *n)
 {
   struct bgp_proto *p = (struct bgp_proto *) n->proto;
 
-  if (n->iface)
+  if (n->scope > 0)
     {
       if ((p->p.proto_state == PS_START) && (p->start_state == BSS_PREPARE))
        {
@@ -793,10 +799,10 @@ bgp_start_locked(struct object_lock *lock)
       return;
     }
 
-  p->neigh = neigh_find(&p->p, &cf->remote_ip, NEF_STICKY);
+  p->neigh = neigh_find2(&p->p, &cf->remote_ip, cf->iface, NEF_STICKY);
   if (!p->neigh || (p->neigh->scope == SCOPE_HOST))
     {
-      log(L_ERR "%s: Invalid remote address %I", p->p.name, cf->remote_ip);
+      log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
       /* As we do not start yet, we can just disable protocol */
       p->p.disabled = 1;
       bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP);
@@ -807,7 +813,7 @@ bgp_start_locked(struct object_lock *lock)
   if (p->neigh->scope > 0)
     bgp_start_neighbor(p);
   else
-    BGP_TRACE(D_EVENTS, "Waiting for %I to become my neighbor", cf->remote_ip);
+    BGP_TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", cf->remote_ip, cf->iface);
 }
 
 static int
@@ -847,6 +853,7 @@ bgp_start(struct proto *P)
 
   lock = p->lock = olock_new(P->pool);
   lock->addr = p->cf->remote_ip;
+  lock->iface = p->cf->iface;
   lock->type = OBJLOCK_TCP;
   lock->port = BGP_PORT;
   lock->iface = NULL;
@@ -951,6 +958,10 @@ bgp_check_config(struct bgp_config *c)
   if (c->multihop && (c->gw_mode == GW_DIRECT))
     cf_error("Multihop BGP cannot use direct gateway mode");
 
+  if (c->multihop && (ipa_has_link_scope(c->remote_ip) || 
+                     ipa_has_link_scope(c->source_addr)))
+    cf_error("Multihop BGP cannot be used with link-local addresses");
+
   /* Different default based on rs_client */
   if (!c->missing_lladdr)
     c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
@@ -1115,7 +1126,7 @@ bgp_show_proto_info(struct proto *P)
   struct bgp_conn *c = p->conn;
 
   cli_msg(-1006, "  BGP state:          %s", bgp_state_dsc(p));
-  cli_msg(-1006, "    Neighbor address: %I", p->cf->remote_ip);
+  cli_msg(-1006, "    Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
   cli_msg(-1006, "    Neighbor AS:      %u", p->remote_as);
 
   if (P->proto_state == PS_START)
index 0c50583b9c66745be0a648d033eb22f2a1e8e741..a8c5818e517a07b88bb1c54770b2ba810d4a25ef 100644 (file)
@@ -19,9 +19,10 @@ struct bgp_config {
   struct proto_config c;
   u32 local_as, remote_as;
   ip_addr remote_ip;
+  ip_addr source_addr;                 /* Source address to use */
+  struct iface *iface;                 /* Interface for link-local addresses */
   int multihop;                                /* Number of hops if multihop */
   int ttl_security;                    /* Enable TTL security [RFC5082] */
-  ip_addr source_addr;                 /* Source address to use */
   int next_hop_self;                   /* Always set next hop to local IP address */
   int missing_lladdr;                  /* What we will do when we don' know link-local addr, see MLL_* */
   int gw_mode;                         /* How we compute route gateway from next_hop attr, see GW_* */
index 3ef9b29096dfb2b593592f931e0846e8882aebbc..5fb609409924aa87260d173b8a18620709d40d10 100644 (file)
@@ -57,11 +57,15 @@ bgp_proto:
  | bgp_proto proto_item ';'
  | bgp_proto LOCAL AS expr ';' { BGP_CFG->local_as = $4; }
  | bgp_proto LOCAL ipa AS expr ';' { BGP_CFG->source_addr = $3; BGP_CFG->local_as = $5; }
- | bgp_proto NEIGHBOR ipa AS expr ';' {
-     if (ipa_nonzero(BGP_CFG->remote_ip)) cf_error("Only one neighbor per BGP instance is allowed");
+ | bgp_proto NEIGHBOR ipa ipa_scope AS expr ';' {
+     if (ipa_nonzero(BGP_CFG->remote_ip))
+       cf_error("Only one neighbor per BGP instance is allowed");
+     if (!ipa_has_link_scope($3) != !$4)
+       cf_error("Link-local address and interface scope must be used together");
 
      BGP_CFG->remote_ip = $3;
-     BGP_CFG->remote_as = $5;
+     BGP_CFG->iface = $4;
+     BGP_CFG->remote_as = $6;
    }
  | bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; }
  | bgp_proto RR CLIENT ';' { BGP_CFG->rr_client = 1; }
index c3a867315316e230a97026ebee389ebcb8e0e0a7..bbb0865f75701b383fce491dc3072b59b6c85fd7 100644 (file)
@@ -318,6 +318,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
 
 #else          /* IPv6 version */
 
+static inline int
+same_iface(struct bgp_proto *p, ip_addr *ip)
+{
+  neighbor *n = neigh_find(&p->p, ip, 0);
+  return n && p->neigh && n->iface == p->neigh->iface;
+}
+
 static byte *
 bgp_create_update(struct bgp_conn *conn, byte *buf)
 {
@@ -329,7 +336,6 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
   ip_addr *ipp, ip, ip_ll;
   ea_list *ea;
   eattr *nh;
-  neighbor *n;
 
   put_u16(buf, 0);
   w = buf+4;
@@ -399,10 +405,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
               * link local address seems to be a natural way to solve that
               * problem, but it is contrary to RFC 2545 and Quagga does not
               * accept such routes.
+              *
+              * There are two cases, either we have global IP, or
+              * IPA_NONE if the neighbor is link-local. For IPA_NONE,
+              * we suppose it is on the same iface, see bgp_update_attrs().
               */
 
-             n = neigh_find(&p->p, &ip, 0);
-             if (n && p->neigh && n->iface == p->neigh->iface)
+             if (ipa_zero(ip) || same_iface(p, &ip))
                {
                  if (second && ipa_nonzero(ipp[1]))
                    ip_ll = ipp[1];
@@ -434,6 +443,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
          *tmp++ = BGP_AF_IPV6;
          *tmp++ = 1;
 
+         if (ipa_has_link_scope(ip))
+           ip = IPA_NONE;
+
          if (ipa_nonzero(ip_ll))
            {
              *tmp++ = 32;
@@ -809,13 +821,27 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
 
 #ifdef IPV6
   int second = (nh->u.ptr->length == NEXT_HOP_LENGTH);
+
+  /* First address should not be link-local, but may be zero in direct mode */
+  if (ipa_has_link_scope(*nexthop))
+    *nexthop = IPA_NONE;
 #else
   int second = 0;
 #endif
 
   if (p->cf->gw_mode == GW_DIRECT)
     {
-      neighbor *ng = neigh_find(&p->p, nexthop, 0) ? : p->neigh;
+      neighbor *ng;
+
+      if (ipa_nonzero(*nexthop))
+       ng = neigh_find(&p->p, nexthop, 0);
+      else if (second) /* GW_DIRECT -> single_hop -> p->neigh != NULL */
+       ng = neigh_find2(&p->p, nexthop + 1, p->neigh->iface, 0);
+
+      /* Fallback */
+      if (!ng)
+       ng = p->neigh;
+
       if (ng->scope == SCOPE_HOST)
        return 0;
 
@@ -826,7 +852,12 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
       a->igp_metric = 0;
     }
   else /* GW_RECURSIVE */
-    rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
+    {
+      if (ipa_zero(*nexthop))
+         return 0;
+
+      rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
+    }
 
   return 1;
 }