#! /bin/sh /usr/share/dpatch/dpatch-run @DPATCH@ diff -ruN quagga-0.99.9/bgpd/bgpd.c quagga-0.99.9-v9/bgpd/bgpd.c --- quagga-0.99.9/bgpd/bgpd.c 2007-07-25 12:49:06.000000000 -0400 +++ quagga-0.99.9-v9/bgpd/bgpd.c 2007-09-24 20:28:55.000000000 -0400 @@ -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,20 @@ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; bgp_stop (peer); bgp_fsm_change_status (peer, Deleted); + +#ifdef HAVE_TCP_MD5SIG + /* Password configuration */ + if (peer->password) + { + free (peer->password); + peer->password = NULL; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, NULL); + } +#endif /* HAVE_TCP_MD5SIG */ + bgp_timer_set (peer); /* stops all timers for Deleted */ /* Delete from all peer list. */ @@ -1417,6 +1432,27 @@ else peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; +#ifdef HAVE_TCP_MD5SIG + /* password apply */ + if (CHECK_FLAG (conf->flags, PEER_FLAG_PASSWORD)) + { + if (peer->password) + free (peer->password); + peer->password = strdup (conf->password); + + if (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, peer->password); + } + else if (peer->password) + { + free (peer->password); + peer->password = NULL; + + if (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, NULL); + } +#endif /* HAVE_TCP_MD5SIG */ + /* maximum-prefix */ peer->pmax[afi][safi] = conf->pmax[afi][safi]; peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi]; @@ -3379,6 +3415,125 @@ return 0; } +#ifdef HAVE_TCP_MD5SIG +/* Set password for authenticating with the peer. */ +int +peer_password_set (struct peer *peer, const char *password) +{ + struct peer_group *group; + struct listnode *nn, *nnode; + int len = password ? strlen(password) : 0; + + 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; + + SET_FLAG (peer->flags, PEER_FLAG_PASSWORD); + if (peer->password) + free (peer->password); + peer->password = strdup (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); + + if (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, peer->password); + return 0; + } + + group = peer->group; + /* #42# LIST_LOOP (group->peer, peer, nn) */ + for (ALL_LIST_ELEMENTS (group->peer, nn, nnode, peer)) + { + if (peer->password && strcmp (peer->password, password) == 0) + continue; + + SET_FLAG (peer->flags, PEER_FLAG_PASSWORD); + if (peer->password) + free (peer->password); + peer->password = strdup (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 (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, peer->password); + } + + return 0; +} + +int +peer_password_unset (struct peer *peer) +{ + struct peer_group *group; + struct listnode *nn, *nnode; + + if (! CHECK_FLAG (peer->flags, PEER_FLAG_PASSWORD) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer_group_active (peer) + && CHECK_FLAG (peer->group->conf->flags, PEER_FLAG_PASSWORD)) + 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 (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, NULL); + + UNSET_FLAG (peer->flags, PEER_FLAG_PASSWORD); + if (peer->password) + free (peer->password); + peer->password = NULL; + + return 0; + } + + UNSET_FLAG (peer->flags, PEER_FLAG_PASSWORD); + if (peer->password) + free (peer->password); + peer->password = NULL; + + group = peer->group; + /* #42# LIST_LOOP (group->peer, peer, nn) */ + for (ALL_LIST_ELEMENTS (group->peer, nn, nnode, peer)) + { + if (! CHECK_FLAG (peer->flags, PEER_FLAG_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); + + if (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (bm->sock, &peer->su.sin, NULL); + + UNSET_FLAG (peer->flags, PEER_FLAG_PASSWORD); + if (peer->password) + free (peer->password); + peer->password = NULL; + } + + return 0; +} +#endif /* HAVE_TCP_MD5SIG */ + /* Set distribute list to the peer. */ int peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, @@ -4416,6 +4571,16 @@ ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN)) vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE); +#ifdef HAVE_TCP_MD5SIG + /* Password. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSWORD)) + if (! peer_group_active (peer) + || ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSWORD) + || strcmp (peer->password, g_peer->password) != 0) + vty_out (vty, " neighbor %s password %s%s", addr, peer->password, + VTY_NEWLINE); +#endif /* HAVE_TCP_MD5SIG */ + /* BGP port. */ if (peer->port != BGP_PORT_DEFAULT) vty_out (vty, " neighbor %s port %d%s", addr, peer->port, @@ -4951,6 +5116,9 @@ bm->port = BGP_PORT_DEFAULT; bm->master = thread_master_create (); bm->start_time = time (NULL); +#ifdef HAVE_TCP_MD5SIG + bm->sock = -1; +#endif /* HAVE_TCP_MD5SIG */ } diff -ruN quagga-0.99.9/bgpd/bgpd.h quagga-0.99.9-v9/bgpd/bgpd.h --- quagga-0.99.9/bgpd/bgpd.h 2007-08-06 11:22:43.000000000 -0400 +++ quagga-0.99.9-v9/bgpd/bgpd.h 2007-09-24 20:28:55.000000000 -0400 @@ -52,6 +52,11 @@ #define BGP_OPT_NO_FIB (1 << 0) #define BGP_OPT_MULTIPLE_INSTANCE (1 << 1) #define BGP_OPT_CONFIG_CISCO (1 << 2) + +#ifdef HAVE_TCP_MD5SIG + /* bgp receive socket */ + int sock; +#endif /* HAVE_TCP_MD5SIG */ }; /* BGP instance structure. */ @@ -349,6 +354,7 @@ /* NSF mode (graceful restart) */ u_char nsf[AFI_MAX][SAFI_MAX]; +#define PEER_FLAG_PASSWORD (1 << 9) /* password */ /* Per AF configuration flags. */ u_int32_t af_flags[AFI_MAX][SAFI_MAX]; @@ -370,6 +376,9 @@ #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 */ + /* MD5 password */ + char *password; + /* default-originate route-map. */ struct { @@ -525,6 +534,13 @@ #define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */ }; +#if defined(HAVE_TCP_MD5SIG) + +#define PEER_PASSWORD_MINLEN (1) +#define PEER_PASSWORD_MAXLEN (80) + +#endif /* HAVE_TCP_MD5SIG */ + /* This structure's member directly points incoming packet data stream. */ struct bgp_nlri @@ -913,6 +929,11 @@ 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 *); +#ifdef HAVE_TCP_MD5SIG +extern int peer_password_set (struct peer *, const char *); +extern int peer_password_unset (struct peer *); +#endif /* HAVE_TCP_MD5SIG */ + 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); diff -ruN quagga-0.99.9/bgpd/bgp_network.c quagga-0.99.9-v9/bgpd/bgp_network.c --- quagga-0.99.9/bgpd/bgp_network.c 2007-05-09 16:59:33.000000000 -0400 +++ quagga-0.99.9-v9/bgpd/bgp_network.c 2007-09-27 18:50:49.000000000 -0400 @@ -22,6 +22,7 @@ #include "thread.h" #include "sockunion.h" +#include "sockopt.h" #include "memory.h" #include "log.h" #include "if.h" @@ -38,6 +39,34 @@ extern struct zebra_privs_t bgpd_privs; +#if defined(HAVE_TCP_MD5SIG) +/* + * 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. + */ +int +bgp_md5_set (int sock, struct sockaddr_in *sin, const char *password) +{ + int ret, en; + + if ( bgpd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("bgp_md5_set: could not raise privs"); + + ret = sockopt_tcp_signature (sock, sin, password); + en = errno; + + if (bgpd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("bgp_md5_set: could not lower privs"); + + if (ret < 0) + zlog (NULL, LOG_WARNING, "can't set TCP_MD5SIG option on socket %d: %s", + sock, safe_strerror (en)); + + return ret; +} + +#endif /* HAVE_TCP_MD5SIG */ + /* Accept bgp connection. */ static int bgp_accept (struct thread *thread) @@ -238,6 +267,12 @@ sockopt_reuseaddr (peer->fd); sockopt_reuseport (peer->fd); +#ifdef HAVE_TCP_MD5SIG + if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSWORD)) + if (sockunion_family (&peer->su) == AF_INET) + bgp_md5_set (peer->fd, &peer->su.sin, peer->password); +#endif /* HAVE_TCP_MD5SIG */ + /* Bind socket. */ bgp_bind (peer); @@ -288,6 +323,10 @@ struct addrinfo req; struct addrinfo *ainfo; struct addrinfo *ainfo_save; +#if defined(HAVE_TCP_MD5SIG) && defined(IPV6_V6ONLY) + struct sockaddr_in sin; + int socklen, on = 1; +#endif int sock = 0; char port_str[BUFSIZ]; @@ -323,6 +362,21 @@ sockopt_reuseaddr (sock); sockopt_reuseport (sock); +#if defined(HAVE_TCP_MD5SIG) && defined(IPV6_V6ONLY) +/* We can not apply MD5SIG to an IPv6 socket. If this is an AF_INET6 + socket, we'll have to create another socket for IPv4*/ + + if (ainfo->ai_family == AF_INET6) { +/* Mark this one for IPv6 only */ + ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, + (void *) &on, sizeof (on)); + if( ret < 0 ) { + en = errno; + zlog_err ("setsockopt V6ONLY: %s", safe_strerror (en)); + } + } +#endif + if (bgpd_privs.change (ZPRIVS_RAISE) ) zlog_err ("bgp_socket: could not raise privs"); @@ -346,7 +400,65 @@ continue; } +#if defined(HAVE_TCP_MD5SIG) && defined(IPV6_V6ONLY) + thread_add_read (master, bgp_accept, bgp, sock); + + if (ainfo->ai_family != AF_INET6) + continue; + + /* If first socket was an IPv6 socket, we need to create an IPv4 + socket for use by the TCP_MD5SIG logic. This code is blatently + copied and modified from the alternate IPv4 only code from below... */ + + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + zlog_err ("socket: %s", safe_strerror (errno)); + continue; + } + + sockopt_reuseaddr (sock); + sockopt_reuseport (sock); + + memset (&sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = htons (port); + socklen = sizeof (struct sockaddr_in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin.sin_len = socklen; +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + + if ( bgpd_privs.change (ZPRIVS_RAISE) ) + zlog_err ("bgp_socket: could not raise privs"); + + ret = bind (sock, (struct sockaddr *) &sin, socklen); + en = errno; + if (bgpd_privs.change (ZPRIVS_LOWER) ) + zlog_err ("bgp_bind_address: could not lower privs"); + + if (ret < 0) + { + zlog_err ("bind: %s", safe_strerror (en)); + close(sock); + continue; + } + + ret = listen (sock, 3); + if (ret < 0) + { + zlog_err ("listen: %s", safe_strerror (errno)); + close (sock); + continue; + } +#endif + +#ifdef HAVE_TCP_MD5SIG + bm->sock = sock; +#endif /* HAVE_TCP_MD5SIG */ + thread_add_read (master, bgp_accept, bgp, sock); + } while ((ainfo = ainfo->ai_next) != NULL); @@ -406,6 +518,9 @@ close (sock); return ret; } +#ifdef HAVE_TCP_MD5SIG + bm->sock = sock; +#endif /* HAVE_TCP_MD5SIG */ thread_add_read (bm->master, bgp_accept, bgp, sock); diff -ruN quagga-0.99.9/bgpd/bgp_network.h quagga-0.99.9-v9/bgpd/bgp_network.h --- quagga-0.99.9/bgpd/bgp_network.h 2007-06-29 09:41:43.000000000 -0400 +++ quagga-0.99.9-v9/bgpd/bgp_network.h 2007-09-24 20:28:55.000000000 -0400 @@ -21,6 +21,10 @@ #ifndef _QUAGGA_BGP_NETWORK_H #define _QUAGGA_BGP_NETWORK_H +#if defined(HAVE_TCP_MD5SIG) +extern int bgp_md5_set (int, struct sockaddr_in *, const char *); +#endif /* HAVE_TCP_MD5SIG */ + extern int bgp_socket (struct bgp *, unsigned short); extern int bgp_connect (struct peer *); extern void bgp_getsockname (struct peer *); diff -ruN quagga-0.99.9/bgpd/bgp_vty.c quagga-0.99.9-v9/bgpd/bgp_vty.c --- quagga-0.99.9/bgpd/bgp_vty.c 2007-08-06 11:17:46.000000000 -0400 +++ quagga-0.99.9-v9/bgpd/bgp_vty.c 2007-09-24 20:28:55.000000000 -0400 @@ -1479,6 +1479,46 @@ "AS number used as local AS\n" "Do not prepend local-as to updates from ebgp peers\n") +#ifdef HAVE_TCP_MD5SIG +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); +} +#endif /* HAVE_TCP_MD5SIG */ + DEFUN (neighbor_activate, neighbor_activate_cmd, NEIGHBOR_CMD2 "activate", @@ -8894,6 +8934,12 @@ install_element (BGP_NODE, &no_neighbor_local_as_val_cmd); install_element (BGP_NODE, &no_neighbor_local_as_val2_cmd); +#ifdef HAVE_TCP_MD5SIG + /* "neighbor password" commands. */ + install_element (BGP_NODE, &neighbor_password_cmd); + install_element (BGP_NODE, &no_neighbor_password_cmd); +#endif /* HAVE_TCP_MD5SIG */ + /* "neighbor activate" commands. */ install_element (BGP_NODE, &neighbor_activate_cmd); install_element (BGP_IPV4_NODE, &neighbor_activate_cmd); diff -ruN quagga-0.99.9/config.h.in quagga-0.99.9-v9/config.h.in --- quagga-0.99.9/config.h.in 2007-09-07 12:56:20.000000000 -0400 +++ quagga-0.99.9-v9/config.h.in 2008-01-28 13:47:20.000000000 -0500 @@ -487,6 +487,9 @@ /* Use TCP for zebra communication */ #undef HAVE_TCP_ZEBRA +/* Define to 1 for TCP MD5 Signatures */ +#undef HAVE_TCP_MD5SIG + /* Define to 1 if you have the header file. */ #undef HAVE_UCONTEXT_H diff -ruN quagga-0.99.9/configure.ac quagga-0.99.9-v9/configure.ac --- quagga-0.99.9/configure.ac 2007-09-07 12:54:01.000000000 -0400 +++ quagga-0.99.9-v9/configure.ac 2008-01-28 13:59:59.000000000 -0500 @@ -211,6 +211,8 @@ [ --with-libpam use libpam for PAM support in vtysh]) AC_ARG_ENABLE(tcp-zebra, [ --enable-tcp-zebra enable TCP/IP socket connection between zebra and protocol daemon]) +AC_ARG_ENABLE(tcp-md5, +[ --enable-tcp-md5 enable TCP/IP md5 in BGPd]) AC_ARG_ENABLE(opaque-lsa, [ --enable-opaque-lsa enable OSPF Opaque-LSA with OSPFAPI support (RFC2370)]) AC_ARG_ENABLE(ospfapi, @@ -281,6 +283,10 @@ AC_DEFINE(HAVE_TCP_ZEBRA,,Use TCP for zebra communication) fi +if test "${enable_tcp_md5}" = "yes"; then + AC_DEFINE(HAVE_TCP_MD5SIG,1,Enable TCP MD5 Signatures) +fi + if test "${enable_opaque_lsa}" = "yes"; then AC_DEFINE(HAVE_OPAQUE_LSA,,OSPF Opaque LSA) fi Binary files quagga-0.99.9/.configure.ac.swp and quagga-0.99.9-v9/.configure.ac.swp differ diff -ruN quagga-0.99.9/lib/sockopt.c quagga-0.99.9-v9/lib/sockopt.c --- quagga-0.99.9/lib/sockopt.c 2007-08-22 12:22:54.000000000 -0400 +++ quagga-0.99.9-v9/lib/sockopt.c 2007-09-24 20:28:55.000000000 -0400 @@ -480,3 +480,36 @@ iph->ip_id = ntohs(iph->ip_id); } + +#if defined(HAVE_TCP_MD5SIG) +int +sockopt_tcp_signature (int sock, struct sockaddr_in *sin, const char *password) +{ + int keylen = password ? strlen(password) : 0; + +#if defined(GNU_LINUX) + + struct tcp_md5sig md5sig; + + bzero ((char *)&md5sig, sizeof(md5sig)); + memcpy (&md5sig.tcpm_addr, sin, sizeof(*sin)); + md5sig.tcpm_keylen = keylen; + if (keylen) + memcpy (md5sig.tcpm_key, password, keylen); + + return setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig); + +#else /* !GNU_LINUX */ + + int enable = keylen ? (TCP_SIG_SPI_BASE + sin->sin_port) : 0; + + /* + * 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. + */ + return setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &enable, + sizeof(enable)); + +#endif /* !GNU_LINUX */ +} +#endif /* HAVE_TCP_MD5SIG */ diff -ruN quagga-0.99.9/lib/sockopt.h quagga-0.99.9-v9/lib/sockopt.h --- quagga-0.99.9/lib/sockopt.h 2007-08-22 12:22:54.000000000 -0400 +++ quagga-0.99.9-v9/lib/sockopt.h 2007-09-24 20:28:55.000000000 -0400 @@ -98,4 +98,32 @@ extern void sockopt_iphdrincl_swab_htosys (struct ip *iph); extern void sockopt_iphdrincl_swab_systoh (struct ip *iph); +#if defined(HAVE_TCP_MD5SIG) + +#if defined(GNU_LINUX) && !defined(TCP_MD5SIG) + +/* XXX these will come from eventually */ + +#define TCP_MD5SIG 14 +#define TCP_MD5SIG_MAXKEYLEN 80 + +struct tcp_md5sig { + struct sockaddr_storage tcpm_addr; /* address associated */ + __u16 __tcpm_pad1; /* zero */ + __u16 tcpm_keylen; /* key length */ + __u32 __tcpm_pad2; /* zero */ + __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ +}; + +#endif /* defined(GNU_LINUX) && !defined(TCP_MD5SIG) */ + +#if !defined(GNU_LINUX) && !defined(TCP_SIG_SPI_BASE) +#define TCP_SIG_SPI_BASE 1000 /* XXX this will go away */ +#endif + +extern int sockopt_tcp_signature(int sock, struct sockaddr_in *sin, + const char *password); + +#endif /* HAVE_TCP_MD5SIG */ + #endif /*_ZEBRA_SOCKOPT_H */