From 993d07798e10f41c258c2a485650507c564e6990 Mon Sep 17 00:00:00 2001
From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Tue, 14 Dec 2010 03:26:51 +0100
Subject: [PATCH] BGP: Add support for peering with link-local IPv6 addresses

---
 nest/iface.c          |    2 +-
 proto/bgp/bgp.c       |   13 ++++++++++++-
 proto/bgp/bgp.h       |    1 +
 proto/bgp/config.Y    |    3 ++-
 proto/bgp/packets.c   |    3 +++
 sysdep/bsd/krt-sock.c |    2 +-
 6 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/nest/iface.c b/nest/iface.c
index c523678..2b0b9c6 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -483,7 +483,7 @@ ifa_update(struct ifa *a)
 	break;
       }
 
-  if (!(i->flags & IF_MULTIACCESS) && a->pxlen < BITS_PER_IP_ADDRESS - 2)
+  if (!(i->flags & IF_MULTIACCESS) && !ipa_has_link_scope(a->ip) && a->pxlen < BITS_PER_IP_ADDRESS - 2)
     log(L_WARN "Strange prefix length %d for point-to-point interface %s", a->pxlen, i->name);
 #ifndef IPV6
   if ((i->flags & IF_BROADCAST) && !ipa_nonzero(a->brd))
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 327292a..84c353e 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -574,6 +574,7 @@ bgp_connect(struct bgp_proto *p)	/* Enter Connect state and start establishing c
   s->saddr = p->source_addr;
   s->daddr = p->cf->remote_ip;
   s->dport = BGP_PORT;
+  s->iface = p->neigh->iface;
   s->ttl = p->cf->multihop ? : 1;
   s->rbsize = BGP_RX_BUFFER_SIZE;
   s->tbsize = BGP_TX_BUFFER_SIZE;
@@ -743,6 +744,7 @@ bgp_start_locked(struct object_lock *lock)
 {
   struct bgp_proto *p = lock->data;
   struct bgp_config *cf = p->cf;
+  struct iface *interface = NULL;
 
   if (p->p.proto_state != PS_START)
     {
@@ -759,7 +761,16 @@ bgp_start_locked(struct object_lock *lock)
       return;
     }
 
-  p->neigh = neigh_find(&p->p, &cf->remote_ip, NEF_STICKY);
+  if (*cf->interface) {
+    interface = if_find_by_name(cf->interface);
+
+    if (!interface) {
+      BGP_TRACE(D_EVENTS, "Waiting for interface %s", cf->interface);
+      return;
+    }
+  }
+
+  p->neigh = neigh_find2(&p->p, &cf->remote_ip, interface, NEF_STICKY);
   if (!p->neigh || (p->neigh->scope == SCOPE_HOST))
     {
       log(L_ERR "%s: Invalid remote address %I", p->p.name, cf->remote_ip);
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index b06f20a..c8acb25 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -47,6 +47,7 @@ struct bgp_config {
   unsigned error_delay_time_min;	/* Time to wait after an error is detected */
   unsigned error_delay_time_max;
   unsigned disable_after_error;		/* Disable the protocol when error is detected */
+  char interface[16];			/* Interface to use */
   char *password;			/* Password used for MD5 authentication */
   struct rtable_config *igp_table;	/* Table used for recursive next hop lookups */
 };
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index e932a7f..6ea94d6 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -25,7 +25,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
 	ADVERTISE, IPV4, CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER,
 	MISSING, LLADDR, DROP, IGNORE, ROUTE, REFRESH, INTERPRET,
 	COMMUNITIES, BGP_ORIGINATOR_ID, BGP_CLUSTER_LIST, IGP, TABLE,
-	GATEWAY, DIRECT, RECURSIVE)
+	GATEWAY, DIRECT, RECURSIVE, INTERFACE)
 
 CF_GRAMMAR
 
@@ -92,6 +92,7 @@ bgp_proto:
  | bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
  | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
  | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
+ | bgp_proto INTERFACE TEXT ';' { strncpy(BGP_CFG->interface, $3, sizeof(BGP_CFG->interface)-1); }
  | bgp_proto PASSWORD TEXT ';' { BGP_CFG->password = $3; }
  | bgp_proto ROUTE LIMIT expr ';' { BGP_CFG->route_limit = $4; }
  | bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index bf98640..7816c1d 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -434,6 +434,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;
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 53b30ca..fc73be8 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -541,7 +541,7 @@ krt_read_addr(struct ks_msg *msg)
     _I0(ifa.ip) = 0xfe800000;
 #endif
 
-  if (iface->flags & IF_MULTIACCESS)
+  if ((iface->flags & IF_MULTIACCESS) || ipa_has_link_scope(ifa.ip))
   {
     ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen));
 
-- 
1.7.3.3

