]> git.puffer.fish Git - matthieu/frr.git/commitdiff
BGP: Fix MD5 authentication for unnumbered neighbors
authorvivek <vivek@cumulusnetworks.com>
Sat, 29 Aug 2015 23:10:12 +0000 (16:10 -0700)
committervivek <vivek@cumulusnetworks.com>
Sat, 29 Aug 2015 23:10:12 +0000 (16:10 -0700)
Ticket: CM-6369
Reviewed By: CCR-3318
Testing Done: Manual testing of various password scenarios.

This is a port of patch bgpd-unnumbered-nbr-fix-password.patch from
2.5-br.

In the case of BGP unnumbered, the peer IP address is derived and not
explicitly configured. If there is a password configured for the peer,
it can be set on the listen socket only after the IP address has been
derived and needs to be cleared when the IP address goes away.

bgpd/bgp_network.c
bgpd/bgp_network.h
bgpd/bgpd.c

index a3ad3cc9dc09314f378abe6f904684e1643ea527..6b0b792f4a192e547603c73d06a0db7a27068403 100644 (file)
@@ -104,8 +104,8 @@ bgp_md5_set_connect (int socket, union sockunion *su, const char *password)
   return ret;
 }
 
-int
-bgp_md5_set (struct peer *peer)
+static int
+bgp_md5_set_password (struct peer *peer, const char *password)
 {
   struct listnode *node;
   int ret = 0;
@@ -117,13 +117,13 @@ bgp_md5_set (struct peer *peer)
       return -1;
     }
   
-  /* Just set the password on the listen socket(s). Outbound connections
+  /* Set or unset the password on the listen socket(s). Outbound connections
    * are taken care of in bgp_connect() below.
    */
   for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
     if (listener->su.sa.sa_family == peer->su.sa.sa_family)
       {
-       ret = bgp_md5_set_socket (listener->fd, &peer->su, peer->password);
+       ret = bgp_md5_set_socket (listener->fd, &peer->su, password);
        break;
       }
 
@@ -133,6 +133,20 @@ bgp_md5_set (struct peer *peer)
   return ret;
 }
 
+int
+bgp_md5_set (struct peer *peer)
+{
+  /* Set the password from listen socket. */
+  return bgp_md5_set_password (peer, peer->password);
+}
+
+int
+bgp_md5_unset (struct peer *peer)
+{
+  /* Unset the password from listen socket. */
+  return bgp_md5_set_password (peer, NULL);
+}
+
 /* Update BGP socket send buffer size */
 static void
 bgp_update_sock_send_buffer_size (int fd)
index 4216967fbf9a3a92a6ad95d62b186a74e087f602..1148d1978be54f97b164207ff74b5385588c2c60 100644 (file)
@@ -29,6 +29,7 @@ extern int bgp_connect (struct peer *);
 extern int bgp_getsockname (struct peer *);
 
 extern int bgp_md5_set (struct peer *);
+extern int bgp_md5_unset (struct peer *);
 extern int bgp_set_socket_ttl(struct peer *, int fd);
 
 #endif /* _QUAGGA_BGP_NETWORK_H */
index eb25fd9909283d32280f62dba9f90a10172115f9..14066462b84a6a7b4f49d91f3531d51dba189132 100644 (file)
@@ -1168,83 +1168,127 @@ peer_xfer_config (struct peer *peer_dst, struct peer *peer_src)
     }
 }
 
-/*
- * Set or reset the peer address socketunion structure based on the
- * learnt peer address. Currently via the source address of the
- * ipv6 ND router-advertisement.
- */
-void
-bgp_peer_conf_if_to_su_update (struct peer *peer)
+static int
+bgp_peer_conf_if_to_su_update_v4 (struct peer *peer, struct interface *ifp)
 {
-  struct interface *ifp;
-  struct nbr_connected *ifc_nbr;
   struct connected *ifc;
   struct prefix p;
   u_int32_t s_addr;
   struct listnode *node;
 
-  if (!peer->conf_if)
-    return;
-
-  if ((ifp = if_lookup_by_name(peer->conf_if)))
+  /* If our IPv4 address on the interface is /30 or /31, we can derive the
+   * IPv4 address of the other end.
+   */
+  for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
     {
-      /* if multiple IP addresses assigned to link, we pick the first */
-      if (!CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
-       for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc))
-         if (ifc->address && (ifc->address->family == AF_INET))
-           {
-             /* Try IPv4 connection first, if present */
-             PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc));
-             /* We can determine peer's IP address if prefixlen is 30/31 */
-             if (p.prefixlen == 30)
-               {
-                 peer->su.sa.sa_family = AF_INET;
-                 s_addr = ntohl(p.u.prefix4.s_addr);
-                 if (s_addr % 4 == 1)
-                   peer->su.sin.sin_addr.s_addr = htonl(s_addr+1);
-                 else if (s_addr % 4 == 2)
-                   peer->su.sin.sin_addr.s_addr = htonl(s_addr-1);
+      if (ifc->address && (ifc->address->family == AF_INET))
+        {
+          PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc));
+          if (p.prefixlen == 30)
+            {
+              peer->su.sa.sa_family = AF_INET;
+              s_addr = ntohl(p.u.prefix4.s_addr);
+              if (s_addr % 4 == 1)
+                peer->su.sin.sin_addr.s_addr = htonl(s_addr+1);
+              else if (s_addr % 4 == 2)
+                peer->su.sin.sin_addr.s_addr = htonl(s_addr-1);
 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
-                 peer->su->sin.sin_len = sizeof(struct sockaddr_in);
+              peer->su->sin.sin_len = sizeof(struct sockaddr_in);
 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-                 return;
-               }
-             else if (p.prefixlen == 31)
-               {
-                 peer->su.sa.sa_family = AF_INET;
-                 s_addr = ntohl(p.u.prefix4.s_addr);
-                 if (s_addr % 2 == 0)
-                   peer->su.sin.sin_addr.s_addr = htonl(s_addr+1);
-                 else
-                   peer->su.sin.sin_addr.s_addr = htonl(s_addr-1);
+              return 1;
+            }
+          else if (p.prefixlen == 31)
+            {
+              peer->su.sa.sa_family = AF_INET;
+              s_addr = ntohl(p.u.prefix4.s_addr);
+              if (s_addr % 2 == 0)
+                peer->su.sin.sin_addr.s_addr = htonl(s_addr+1);
+              else
+                peer->su.sin.sin_addr.s_addr = htonl(s_addr-1);
 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
-                 peer->su->sin.sin_len = sizeof(struct sockaddr_in);
+              peer->su->sin.sin_len = sizeof(struct sockaddr_in);
 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-                 return;
-               }
-             else
-               zlog_warn("%s: IPv4 interface address is not /30 or /31, v4 session not started",
-                         peer->conf_if);
-           }
+              return 1;
+            }
+          else
+            zlog_warn("%s: IPv4 interface address is not /30 or /31, v4 session not started",
+                      peer->conf_if);
+        }
+    }
 
-      if (ifp->nbr_connected &&
-         (ifc_nbr = listnode_head(ifp->nbr_connected)))
-       {
-         peer->su.sa.sa_family = AF_INET6;
-         memcpy(&peer->su.sin6.sin6_addr, &ifc_nbr->address->u.prefix,
-                sizeof (struct in6_addr));
+  return 0;
+}
+
+static int
+bgp_peer_conf_if_to_su_update_v6 (struct peer *peer, struct interface *ifp)
+{
+  struct nbr_connected *ifc_nbr;
+
+  /* Have we learnt the peer's IPv6 link-local address? */
+  if (ifp->nbr_connected &&
+      (ifc_nbr = listnode_head(ifp->nbr_connected)))
+    {
+      peer->su.sa.sa_family = AF_INET6;
+      memcpy(&peer->su.sin6.sin6_addr, &ifc_nbr->address->u.prefix,
+             sizeof (struct in6_addr));
 #ifdef SIN6_LEN
-         peer->su.sin6.sin6_len = sizeof (struct sockaddr_in6);
+      peer->su.sin6.sin6_len = sizeof (struct sockaddr_in6);
 #endif
-         peer->su.sin6.sin6_scope_id = ifp->ifindex;
+      peer->su.sin6.sin6_scope_id = ifp->ifindex;
+      return 1;
+    }
 
-         return;
-       }
+  return 0;
+}
+
+/*
+ * Set or reset the peer address socketunion structure based on the
+ * learnt/derived peer address. If the address has changed, update the
+ * password on the listen socket, if needed.
+ */
+void
+bgp_peer_conf_if_to_su_update (struct peer *peer)
+{
+  struct interface *ifp;
+  int prev_family;
+  int peer_addr_updated = 0;
+
+  if (!peer->conf_if)
+    return;
+
+  prev_family = peer->su.sa.sa_family;
+  if ((ifp = if_lookup_by_name(peer->conf_if)))
+    {
+      /* If BGP unnumbered is not "v6only", we first see if we can derive the
+       * peer's IPv4 address.
+       */
+      if (!CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+        peer_addr_updated = bgp_peer_conf_if_to_su_update_v4 (peer, ifp);
+
+      /* If "v6only" or we can't derive peer's IPv4 address, see if we've
+       * learnt the peer's IPv6 link-local address. This is from the source
+       * IPv6 address in router advertisement.
+       */
+      if (!peer_addr_updated)
+        peer_addr_updated = bgp_peer_conf_if_to_su_update_v6 (peer, ifp);
+    }
+  /* If we could derive the peer address, we may need to install the password
+   * configured for the peer, if any, on the listen socket. Otherwise, mark
+   * that peer's address is not available and uninstall the password, if
+   * needed.
+   */
+  if (peer_addr_updated)
+    {
+      if (peer->password && prev_family == AF_UNSPEC)
+        bgp_md5_set (peer);
+    }
+  else
+    {
+      if (peer->password && prev_family != AF_UNSPEC)
+        bgp_md5_unset (peer);
+      peer->su.sa.sa_family = AF_UNSPEC;
+      memset(&peer->su.sin6.sin6_addr, 0, sizeof (struct in6_addr));
     }
-  /* This works as an indication of unresolved peer address
-     on a BGP interface*/
-  peer->su.sa.sa_family = AF_UNSPEC;
-  memset(&peer->su.sin6.sin6_addr, 0, sizeof (struct in6_addr));
 }
 
 /* Create new BGP peer.  */
@@ -1745,8 +1789,9 @@ peer_delete (struct peer *peer)
       peer->password = NULL;
 
       if (!accept_peer &&
+          ! BGP_PEER_SU_UNSPEC(peer) &&
           ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
-       bgp_md5_set (peer);
+       bgp_md5_unset (peer);
     }
   
   bgp_timer_set (peer); /* stops all timers for Deleted */
@@ -1987,7 +2032,8 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
   if (conf->password && !peer->password)
     peer->password =  XSTRDUP (MTYPE_PEER_PASSWORD, conf->password);
 
-  bgp_md5_set (peer);
+  if (! BGP_PEER_SU_UNSPEC(peer))
+    bgp_md5_set (peer);
 
   /* maximum-prefix */
   peer->pmax[afi][safi] = conf->pmax[afi][safi];
@@ -4597,6 +4643,9 @@ peer_password_set (struct peer *peer, const char *password)
       else
         bgp_session_reset(peer);
         
+      if (BGP_PEER_SU_UNSPEC(peer))
+        return BGP_SUCCESS;
+
       return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED;
     }
 
@@ -4615,8 +4664,11 @@ peer_password_set (struct peer *peer, const char *password)
       else
         bgp_session_reset(peer);
       
-      if (bgp_md5_set (peer) < 0)
-        ret = BGP_ERR_TCPSIG_FAILED;
+      if (! BGP_PEER_SU_UNSPEC(peer))
+        {
+          if (bgp_md5_set (peer) < 0)
+            ret = BGP_ERR_TCPSIG_FAILED;
+        }
     }
 
   return ret;
@@ -4648,7 +4700,8 @@ peer_password_unset (struct peer *peer)
       
       peer->password = NULL;
       
-      bgp_md5_set (peer);
+      if (! BGP_PEER_SU_UNSPEC(peer))
+        bgp_md5_unset (peer);
 
       return 0;
     }
@@ -4669,7 +4722,8 @@ peer_password_unset (struct peer *peer)
       XFREE (MTYPE_PEER_PASSWORD, peer->password);
       peer->password = NULL;
 
-      bgp_md5_set (peer);
+      if (! BGP_PEER_SU_UNSPEC(peer))
+        bgp_md5_unset (peer);
     }
 
   return 0;