From 0df7c91f048f2116610d6bdfce3ab6cad1981802 Mon Sep 17 00:00:00 2001 From: Paul Jakma Date: Mon, 21 Jul 2008 21:02:49 +0000 Subject: [bgpd] TCP-MD5: password vty configuration and initial Linux support 2008-07-21 Paul Jakma * 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 YOSHIFUJI Hideaki Tomohiko Kusuda Leigh Brown * 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 * 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 YOSHIFUJI Hideaki * sockopt.{c,h}: (sockopt_tcp_signature) Add TCP-MD5SIG support. --- bgpd/ChangeLog | 36 ++++++++++++++ bgpd/bgp_network.c | 82 ++++++++++++++++++++++++++++++- bgpd/bgp_network.h | 2 + bgpd/bgp_packet.c | 2 +- bgpd/bgp_vty.c | 45 +++++++++++++++++ bgpd/bgpd.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++- bgpd/bgpd.h | 18 ++++++- 7 files changed, 319 insertions(+), 5 deletions(-) (limited to 'bgpd') diff --git a/bgpd/ChangeLog b/bgpd/ChangeLog index d6e02036..2150add3 100644 --- a/bgpd/ChangeLog +++ b/bgpd/ChangeLog @@ -1,3 +1,39 @@ +2008-07-21 Paul Jakma + + + * 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 + YOSHIFUJI Hideaki + Tomohiko Kusuda + Leigh Brown + + * 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 * *.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 8452545d..71f3ec7d 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -22,12 +22,14 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #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 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 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 @@ bgp_connect (struct peer *peer) 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 @@ bgp_socket (struct bgp *bgp, unsigned short port, char *address) 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 b455f574..618d8dbb 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -25,4 +25,6 @@ extern int bgp_socket (struct bgp *, unsigned short, char *); 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 8319a885..623fd9ec 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1235,7 +1235,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) 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 7a7b3fdf..54f11701 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -210,6 +210,9 @@ bgp_vty_return (struct vty *vty, int ret) 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 @@ ALIAS (no_neighbor_local_as, "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 @@ bgp_vty_init (void) 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 4dc6621d..8eb0d2e4 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -788,6 +788,7 @@ peer_new (struct bgp *bgp) 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_delete (struct peer *peer) 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 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, 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 @@ peer_local_as_unset (struct peer *peer) 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 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, ! 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 @@ bgp_master_init (void) 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 e9fc410f..afe06635 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -42,6 +42,9 @@ struct bgp_master 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 @@ struct peer #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 @@ struct peer #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 @@ enum bgp_clear_type #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_set (struct peer *, afi_t, safi_t, int, const char *); 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); -- cgit v1.2.1