[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/lib/ChangeLog b/lib/ChangeLog
index 681bdeb..688c44f 100644
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,14 @@
+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.
+
 2008-06-07 Paul Jakma <paul@jakma.org>
 
 	* stream.{c,h}: (stream_{put,write}) add const qualifier to source
diff --git a/lib/memtypes.c b/lib/memtypes.c
index f1b10e7..dd365dd 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -95,6 +95,7 @@
   { MTYPE_BGP_PEER_HOST,	"BGP peer hostname"		},
   { MTYPE_PEER_GROUP,		"Peer group"			},
   { MTYPE_PEER_DESC,		"Peer description"		},
+  { MTYPE_PEER_PASSWORD,	"Peer password string"		},
   { MTYPE_ATTR,			"BGP attribute"			},
   { MTYPE_ATTR_EXTRA,		"BGP extra attributes"		},
   { MTYPE_AS_PATH,		"BGP aspath"			},
diff --git a/lib/sockopt.c b/lib/sockopt.c
index f8fa946..a2038a5 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -22,6 +22,7 @@
 #include <zebra.h>
 #include "log.h"
 #include "sockopt.h"
+#include "sockunion.h"
 
 int
 setsockopt_so_recvbuf (int sock, int size)
@@ -480,3 +481,70 @@
 
   iph->ip_id = ntohs(iph->ip_id);
 }
+
+int
+sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
+{
+#if HAVE_DECL_TCP_MD5SIG
+#ifndef GNU_LINUX
+  /*
+   * XXX Need to do PF_KEY operation here to add/remove an SA entry,
+   * and add/remove an SP entry for this peer's packet flows also.
+   */
+  int md5sig = password && *password ? 1 : 0;
+#else
+  int keylen = password ? strlen (password) : 0;
+  struct tcp_md5sig md5sig;
+  union sockunion *su2, *susock;
+  int ret;
+  
+  /* Figure out whether the socket and the sockunion are the same family..
+   * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
+   */
+  if (!(susock = sockunion_getsockname (sock)))
+    return -1;
+  
+  if (susock->sa.sa_family == su->sa.sa_family)
+    su2 = su;
+  else
+    {
+      /* oops.. */
+      su2 = susock;
+      
+      if (su2->sa.sa_family == AF_INET)
+        {
+          sockunion_free (susock);
+          return -1;
+        };
+      
+      /* If this does not work, then all users of this sockopt will need to
+       * differentiate between IPv4 and IPv6, and keep seperate sockets for
+       * each. 
+       *
+       * Sadly, it doesn't seem to work at present. It's unknown whether
+       * this is a bug or not.
+       */
+      if (su2->sa.sa_family == AF_INET6
+          && su->sa.sa_family == AF_INET)
+        {
+           su2->sin6.sin6_family = AF_INET6;
+           /* V4Map the address */
+           memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr));
+           su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
+           memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4);
+        }
+    }
+  
+  memset (&md5sig, 0, sizeof (md5sig));
+  memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2));
+  md5sig.tcpm_keylen = keylen;
+  if (keylen)
+    memcpy (md5sig.tcpm_key, password, keylen);
+#endif /* GNU_LINUX */
+  ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig);
+  sockunion_free (susock);
+  return ret;
+#else /* HAVE_TCP_MD5SIG */
+  return -2;
+#endif /* HAVE_TCP_MD5SIG */
+}
diff --git a/lib/sockopt.h b/lib/sockopt.h
index ebb7143..9538279 100644
--- a/lib/sockopt.h
+++ b/lib/sockopt.h
@@ -22,6 +22,8 @@
 #ifndef _ZEBRA_SOCKOPT_H
 #define _ZEBRA_SOCKOPT_H
 
+#include "sockunion.h"
+
 extern int setsockopt_so_recvbuf (int sock, int size);
 extern int setsockopt_so_sendbuf (const int sock, int size);
 extern int getsockopt_so_sendbuf (const int sock);
@@ -98,4 +100,6 @@
 extern void sockopt_iphdrincl_swab_htosys (struct ip *iph);
 extern void sockopt_iphdrincl_swab_systoh (struct ip *iph);
 
+extern int sockopt_tcp_signature(int sock, union sockunion *su,
+                                 const char *password);
 #endif /*_ZEBRA_SOCKOPT_H */
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 7721666..cfd3bf9 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -254,6 +254,7 @@
   client_sock = accept (sock, (struct sockaddr *) su, &len);
   
   /* Convert IPv4 compatible IPv6 address to IPv4 address. */
+#if 0
 #ifdef HAVE_IPV6
   if (su->sa.sa_family == AF_INET6)
     {
@@ -268,7 +269,7 @@
 	}
     }
 #endif /* HAVE_IPV6 */
-
+#endif
   return client_sock;
 }
 
@@ -592,6 +593,7 @@
       su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
       memcpy (su, &name, sizeof (struct sockaddr_in6));
 
+#if 0
       if (IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
 	{
 	  struct sockaddr_in sin;
@@ -601,6 +603,7 @@
 	  sin.sin_port = su->sin6.sin6_port;
 	  memcpy (su, &sin, sizeof (struct sockaddr_in));
 	}
+#endif
       return su;
     }
 #endif /* HAVE_IPV6 */
@@ -645,7 +648,7 @@
     {
       su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion));
       memcpy (su, &name, sizeof (struct sockaddr_in6));
-
+#if 0
       if (IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr))
 	{
 	  struct sockaddr_in sin;
@@ -655,6 +658,7 @@
 	  sin.sin_port = su->sin6.sin6_port;
 	  memcpy (su, &sin, sizeof (struct sockaddr_in));
 	}
+#endif
       return su;
     }
 #endif /* HAVE_IPV6 */