[bgpd] TCP-MD5: password vty configuration and initial Linux support

2008-07-21 Paul Jakma <paul.jakma@sun.com>

	* bgp_packet.c: (bgp_open_receive) fix warning in a zlog call
	* bgp_vty.c: (bgp_vty_return) add return code
	* bgpd.c: (bgp_master_init) setup the socket list.
	* bgp_network.c: Remove the dual IPv4/6 socket thing for now, which
	  was implemented by Michael, until such time as its clear its
	  required for Linux (see sockopt comments). IPv6 support, including
	  IPv4 sessions on AF_INET6 sockets, therefore is broken, and the
	  '-l 0.0.0.0' arguments would need to be given to bgpd to make
	  things work here.

2008-07-21 Michael H. Warfield <mhw@wittsend.com>
           YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
	   Tomohiko Kusuda <kusuda@inetcore.com>
           Leigh Brown <leigh@solinno.co.uk>

	* bgp_network.c: (bgp_md5_set_one) shim between libzebra tcp-md5
	  sockopt and bgpd.
	  (bgp_md5_set_socket) Helper for bgp_connect
	  (bgp_md5_set) setup TCP-MD5SIG for the given peer.
	  (bgp_connect) call out to bgp_md5_set_socket for the outgoing
	  connect socket.
	  (bgp_socket) save references to the listen sockets, needed if
	  TCP-MD5SIG is applied later or changed.
	* bgp_vty.c: (*neighbor_password_cmd) New 'neighbor ... password'
	  commands.
	* bgpd.c: (peer_{new,delete) manage TCP-MD5 password
	  (peer_group2peer_config_copy) inherit TCP-MD5 password
	  (peer_password_{un,}set) orchestrate the whole add/remove of TCP-MD5
	  passwords: applying checks, stopping peers, and trying to return
	  errors to UI, etc.
	  (bgp_config_write_peer) save password.
	  Fix missing newline in writeout of neighbor ... port.

2008-07-21 Paul Jakma <paul.jakma@sun.com>

	* sockunion.c: ifdef out various places that converted
	  v4mapped sockets to pure v4. Doesn't seem necessary at all,
	  presumably a workaround for now historical inet_ntop bugs (?)

2008-07-21 Michael H. Warfield <mhw@wittsend.com>
           YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>

	* sockopt.{c,h}: (sockopt_tcp_signature) Add TCP-MD5SIG support.
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 8452545..71f3ec7 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -22,12 +22,14 @@
 
 #include "thread.h"
 #include "sockunion.h"
+#include "sockopt.h"
 #include "memory.h"
 #include "log.h"
 #include "if.h"
 #include "prefix.h"
 #include "command.h"
 #include "privs.h"
+#include "linklist.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_fsm.h"
@@ -38,6 +40,80 @@
 extern struct zebra_privs_t bgpd_privs;
 
 
+/*
+ * Set MD5 key for the socket, for the given IPv4 peer address.
+ * If the password is NULL or zero-length, the option will be disabled.
+ */
+static int
+bgp_md5_set_socket (int socket, union sockunion *su, const char *password)
+{
+  int ret = -1;
+  int en = ENOSYS;
+  
+  assert (socket >= 0);
+  
+#if HAVE_DECL_TCP_MD5SIG  
+  ret = sockopt_tcp_signature (socket, su, password);
+  en  = errno;
+#endif /* HAVE_TCP_MD5SIG */
+  
+  if (ret < 0)
+    zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s",
+          socket, safe_strerror (en));
+
+  return ret;
+}
+
+/* Helper for bgp_connect */
+static int
+bgp_md5_set_connect (int socket, union sockunion *su, const char *password)
+{
+  int ret = -1;
+
+#if HAVE_DECL_TCP_MD5SIG  
+  if ( bgpd_privs.change (ZPRIVS_RAISE) )
+    {
+      zlog_err ("%s: could not raise privs", __func__);
+      return ret;
+    }
+  
+  ret = bgp_md5_set_socket (socket, su, password);
+
+  if (bgpd_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("%s: could not lower privs", __func__);
+#endif /* HAVE_TCP_MD5SIG */
+  
+  return ret;
+}
+
+int
+bgp_md5_set (struct peer *peer)
+{
+  struct listnode *node;
+  int fret = 0, ret;
+  int *socket;
+
+  if ( bgpd_privs.change (ZPRIVS_RAISE) )
+    {
+      zlog_err ("%s: could not raise privs", __func__);
+      return -1;
+    }
+  
+  /* Just set 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, socket))
+    {
+      ret = bgp_md5_set_socket ((int )socket, &peer->su, peer->password);
+      if (ret < 0)
+        fret = ret;
+    }
+  if (bgpd_privs.change (ZPRIVS_LOWER) )
+    zlog_err ("%s: could not lower privs", __func__);
+  
+  return fret;
+}
+
 /* Accept bgp connection. */
 static int
 bgp_accept (struct thread *thread)
@@ -237,6 +313,9 @@
 
   sockopt_reuseaddr (peer->fd);
   sockopt_reuseport (peer->fd);
+  
+  if (peer->password)
+    bgp_md5_set_connect (peer->fd, &peer->su, peer->password);
 
   /* Bind socket. */
   bgp_bind (peer);
@@ -345,7 +424,8 @@
 	  close (sock);
 	  continue;
 	}
-
+      
+      listnode_add (bm->listen_sockets, (void *)sock);
       thread_add_read (master, bgp_accept, bgp, sock);
     }
   while ((ainfo = ainfo->ai_next) != NULL);