[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/ChangeLog b/bgpd/ChangeLog
index d6e0203..2150add 100644
--- a/bgpd/ChangeLog
+++ b/bgpd/ChangeLog
@@ -1,3 +1,39 @@
+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-02 MIYAJIMA Mitsuharu <miyajima.mitsuharu@anchor.jp>
* *.c: CMD_AS_RANGE was being used inside command strings, and thus
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);
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
index b455f57..618d8db 100644
--- a/bgpd/bgp_network.h
+++ b/bgpd/bgp_network.h
@@ -25,4 +25,6 @@
extern int bgp_connect (struct peer *);
extern void bgp_getsockname (struct peer *);
+extern int bgp_md5_set (struct peer *);
+
#endif /* _QUAGGA_BGP_NETWORK_H */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 8319a88..623fd9e 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1235,7 +1235,7 @@
zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4."
" Odd, but proceeding.", peer->host);
else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4))
- zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 fits "
+ zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits "
"in 2-bytes, very odd peer.", peer->host, as4);
if (as4)
remote_as = as4;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 7a7b3fd..54f1170 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -210,6 +210,9 @@
case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS:
str = "Cannot have local-as same as BGP AS number";
break;
+ case BGP_ERR_TCPSIG_FAILED:
+ str = "Error while applying TCP-Sig to session(s)";
+ break;
}
if (str)
{
@@ -1479,6 +1482,44 @@
"AS number used as local AS\n"
"Do not prepend local-as to updates from ebgp peers\n")
+DEFUN (neighbor_password,
+ neighbor_password_cmd,
+ NEIGHBOR_CMD2 "password LINE",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set a password\n"
+ "The password\n")
+{
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty (vty, argv[0]);
+ if (! peer)
+ return CMD_WARNING;
+
+ ret = peer_password_set (peer, argv[1]);
+ return bgp_vty_return (vty, ret);
+}
+
+DEFUN (no_neighbor_password,
+ no_neighbor_password_cmd,
+ NO_NEIGHBOR_CMD2 "password",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set a password\n")
+{
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty (vty, argv[0]);
+ if (! peer)
+ return CMD_WARNING;
+
+ ret = peer_password_unset (peer);
+ return bgp_vty_return (vty, ret);
+}
+
DEFUN (neighbor_activate,
neighbor_activate_cmd,
NEIGHBOR_CMD2 "activate",
@@ -8897,6 +8938,10 @@
install_element (BGP_NODE, &no_neighbor_local_as_val_cmd);
install_element (BGP_NODE, &no_neighbor_local_as_val2_cmd);
+ /* "neighbor password" commands. */
+ install_element (BGP_NODE, &neighbor_password_cmd);
+ install_element (BGP_NODE, &no_neighbor_password_cmd);
+
/* "neighbor activate" commands. */
install_element (BGP_NODE, &neighbor_activate_cmd);
install_element (BGP_IPV4_NODE, &neighbor_activate_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 4dc6621..8eb0d2e 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -788,6 +788,7 @@
peer->status = Idle;
peer->ostatus = Idle;
peer->weight = 0;
+ peer->password = NULL;
peer->bgp = bgp;
peer = peer_lock (peer); /* initial reference */
@@ -1202,6 +1203,17 @@
peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
bgp_stop (peer);
bgp_fsm_change_status (peer, Deleted);
+
+ /* Password configuration */
+ if (peer->password)
+ {
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = NULL;
+
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ bgp_md5_set (peer);
+ }
+
bgp_timer_set (peer); /* stops all timers for Deleted */
/* Delete from all peer list. */
@@ -1417,6 +1429,17 @@
else
peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+ /* password apply */
+ if (peer->password)
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+
+ if (conf->password)
+ peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, conf->password);
+ else
+ peer->password = NULL;
+
+ bgp_md5_set (peer);
+
/* maximum-prefix */
peer->pmax[afi][safi] = conf->pmax[afi][safi];
peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi];
@@ -3379,6 +3402,111 @@
return 0;
}
+/* Set password for authenticating with the peer. */
+int
+peer_password_set (struct peer *peer, const char *password)
+{
+ struct listnode *nn, *nnode;
+ int len = password ? strlen(password) : 0;
+ int ret = BGP_SUCCESS;
+
+ if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN))
+ return BGP_ERR_INVALID_VALUE;
+
+ if (peer->password && strcmp (peer->password, password) == 0
+ && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ if (peer->password)
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+
+ peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, password);
+
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ {
+ if (peer->status == Established)
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ BGP_EVENT_ADD (peer, BGP_Stop);
+
+ return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED;
+ }
+
+ for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
+ {
+ if (peer->password && strcmp (peer->password, password) == 0)
+ continue;
+
+ if (peer->password)
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+
+ peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
+
+ if (peer->status == Established)
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ BGP_EVENT_ADD (peer, BGP_Stop);
+
+ if (bgp_md5_set (peer) < 0)
+ ret = BGP_ERR_TCPSIG_FAILED;
+ }
+
+ return ret;
+}
+
+int
+peer_password_unset (struct peer *peer)
+{
+ struct listnode *nn, *nnode;
+
+ if (!peer->password
+ && !CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ if (!CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ {
+ if (peer_group_active (peer)
+ && peer->group->conf->password
+ && strcmp (peer->group->conf->password, peer->password) == 0)
+ return BGP_ERR_PEER_GROUP_HAS_THE_FLAG;
+
+ if (peer->status == Established)
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ BGP_EVENT_ADD (peer, BGP_Stop);
+
+ if (peer->password)
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+
+ peer->password = NULL;
+
+ bgp_md5_set (peer);
+
+ return 0;
+ }
+
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = NULL;
+
+ for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer))
+ {
+ if (!peer->password)
+ continue;
+
+ if (peer->status == Established)
+ bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ BGP_EVENT_ADD (peer, BGP_Stop);
+
+ XFREE (MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = NULL;
+
+ bgp_md5_set (peer);
+ }
+
+ return 0;
+}
+
/* Set distribute list to the peer. */
int
peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct,
@@ -4416,9 +4544,17 @@
! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN))
vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE);
+ /* Password. */
+ if (peer->password)
+ if (!peer_group_active (peer)
+ || ! g_peer->password
+ || strcmp (peer->password, g_peer->password) != 0)
+ vty_out (vty, " neighbor %s password %s%s", addr, peer->password,
+ VTY_NEWLINE);
+
/* BGP port. */
if (peer->port != BGP_PORT_DEFAULT)
- vty_out (vty, " neighbor %s port %d%s", addr, peer->port,
+ vty_out (vty, " neighbor %s port %d%s", addr, peer->port,
VTY_NEWLINE);
/* Local interface name. */
@@ -4948,6 +5084,7 @@
bm = &bgp_master;
bm->bgp = list_new ();
+ bm->listen_sockets = list_new ();
bm->port = BGP_PORT_DEFAULT;
bm->master = thread_master_create ();
bm->start_time = time (NULL);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index e9fc410..afe0663 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -42,6 +42,9 @@
struct work_queue *process_main_queue;
struct work_queue *process_rsclient_queue;
+ /* Listening sockets */
+ struct list *listen_sockets;
+
/* BGP port number. */
u_int16_t port;
@@ -377,7 +380,10 @@
#define PEER_FLAG_ORF_PREFIX_RM (1 << 13) /* orf capability receive-mode */
#define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */
#define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */
-#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */
+#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */
+
+ /* MD5 password */
+ char *password;
/* default-originate route-map. */
struct
@@ -534,6 +540,9 @@
#define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */
};
+#define PEER_PASSWORD_MINLEN (1)
+#define PEER_PASSWORD_MAXLEN (80)
+
/* This structure's member directly points incoming packet data
stream. */
struct bgp_nlri
@@ -787,7 +796,8 @@
#define BGP_ERR_INSTANCE_MISMATCH -26
#define BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP -27
#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS -28
-#define BGP_ERR_MAX -29
+#define BGP_ERR_TCPSIG_FAILED -29
+#define BGP_ERR_MAX -30
extern struct bgp_master *bm;
@@ -924,6 +934,10 @@
extern int peer_route_map_unset (struct peer *, afi_t, safi_t, int);
extern int peer_unsuppress_map_set (struct peer *, afi_t, safi_t, const char *);
+
+extern int peer_password_set (struct peer *, const char *);
+extern int peer_password_unset (struct peer *);
+
extern int peer_unsuppress_map_unset (struct peer *, afi_t, safi_t);
extern int peer_maximum_prefix_set (struct peer *, afi_t, safi_t, u_int32_t, u_char, int, u_int16_t);