summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/Makefile.am4
-rw-r--r--bgpd/bgp_bfd.c624
-rw-r--r--bgpd/bgp_bfd.h69
-rw-r--r--bgpd/bgp_fsm.c4
-rw-r--r--bgpd/bgp_vty.c33
-rw-r--r--bgpd/bgp_vty.h3
-rw-r--r--bgpd/bgp_zebra.c43
-rw-r--r--bgpd/bgpd.c21
-rw-r--r--bgpd/bgpd.h4
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/csv.c684
-rw-r--r--lib/csv.h172
-rw-r--r--lib/log.c4
-rw-r--r--lib/memtypes.c1
-rw-r--r--lib/ptm_lib.c464
-rw-r--r--lib/ptm_lib.h49
-rw-r--r--lib/zclient.c40
-rw-r--r--lib/zclient.h5
-rw-r--r--lib/zebra.h6
-rw-r--r--ospfd/ospf_zebra.c2
-rw-r--r--zebra/Makefile.am8
-rw-r--r--zebra/interface.c16
-rw-r--r--zebra/interface.h1
-rw-r--r--zebra/redistribute.c17
-rw-r--r--zebra/redistribute.h1
-rw-r--r--zebra/redistribute_null.c3
-rw-r--r--zebra/zebra_ptm.c678
-rw-r--r--zebra/zebra_ptm.h4
-rw-r--r--zebra/zebra_ptm_null.c29
-rw-r--r--zebra/zebra_ptm_redistribute.c122
-rw-r--r--zebra/zebra_ptm_redistribute.h28
-rw-r--r--zebra/zserv.c39
-rw-r--r--zebra/zserv.h6
33 files changed, 2870 insertions, 320 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index b005a0caf..e91e2aa31 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -16,7 +16,7 @@ libbgp_a_SOURCES = \
bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \
bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \
bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \
- bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c
+ bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c
noinst_HEADERS = \
bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \
@@ -24,7 +24,7 @@ noinst_HEADERS = \
bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \
bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_nht.h \
- bgp_updgrp.h
+ bgp_updgrp.h bgp_bfd.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
new file mode 100644
index 000000000..3005373d2
--- /dev/null
+++ b/bgpd/bgp_bfd.c
@@ -0,0 +1,624 @@
+/**
+ * bgp_bfd.c: BGP BFD handling routines
+ *
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+#include "prefix.h"
+#include "thread.h"
+#include "buffer.h"
+#include "stream.h"
+#include "zclient.h"
+#include "vty.h"
+#include "bgp_fsm.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_vty.h"
+
+extern struct zclient *zclient;
+
+/*
+ * bgp_bfd_peer_init - Allocate and initialize the peer BFD information
+ * with default values.
+ */
+void
+bgp_bfd_peer_init(struct peer *peer)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+
+ peer->bfd_info = XCALLOC (MTYPE_BGP_PEER_BFD_INFO,
+ sizeof (struct bgp_bfd_peer_info));
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ /* Set default BFD parameter values */
+ bfd_info->required_min_rx = BGP_BFD_DEF_MIN_RX;
+ bfd_info->desired_min_tx = BGP_BFD_DEF_MIN_TX;
+ bfd_info->detect_mult = BGP_BFD_DEF_DETECT_MULT;
+}
+
+/*
+ * bgp_bfd_peer_free - Free the peer BFD information.
+ */
+void
+bgp_bfd_peer_free(struct peer *peer)
+{
+ XFREE (MTYPE_BGP_PEER_BFD_INFO, peer->bfd_info);
+}
+
+/*
+ * bgp_bfd_peer_group2peer_copy - Copy the BFD information from peer group template
+ * to peer.
+ */
+void
+bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+ struct bgp_bfd_peer_info *conf_bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+ conf_bfd_info = (struct bgp_bfd_peer_info *)conf->bfd_info;
+
+ /* Copy BFD parameter values */
+ bfd_info->required_min_rx = conf_bfd_info->required_min_rx;
+ bfd_info->desired_min_tx = conf_bfd_info->desired_min_tx;
+ bfd_info->detect_mult = conf_bfd_info->detect_mult;
+}
+
+/*
+ * bgp_bfd_is_peer_multihop - returns whether BFD peer is multi-hop or single hop.
+ */
+static int
+bgp_bfd_is_peer_multihop(struct peer *peer)
+{
+ if((peer->sort == BGP_PEER_IBGP) || is_ebgp_multihop_configured(peer))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * sendmsg_bfd_peer - Format and send a Peer register/Unregister
+ * command to Zebra to be forwarded to BFD
+ */
+static void
+sendmsg_bfd_peer (struct peer *peer, int command)
+{
+ struct stream *s;
+ int ret;
+ int len;
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ {
+ zlog_debug("%s: Can't send BFD peer register, Zebra client not established",
+ __FUNCTION__);
+ return;
+ }
+
+ s = zclient->obuf;
+ stream_reset (s);
+ zclient_create_header (s, command);
+
+ stream_putw(s, peer->su.sa.sa_family);
+ switch (peer->su.sa.sa_family)
+ {
+ case AF_INET:
+ stream_put_in_addr (s, &peer->su.sin.sin_addr);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ stream_put(s, &(peer->su.sin6.sin6_addr), 16);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (command != ZEBRA_BFD_DEST_DEREGISTER)
+ {
+ stream_putl(s, bfd_info->required_min_rx);
+ stream_putl(s, bfd_info->desired_min_tx);
+ stream_putc(s, bfd_info->detect_mult);
+ }
+
+ if (bgp_bfd_is_peer_multihop(peer))
+ {
+ stream_putc(s, 1);
+ /* Multi-hop destination send the source IP address to BFD */
+ if (peer->su_local)
+ {
+ stream_putw(s, peer->su_local->sa.sa_family);
+ switch (peer->su_local->sa.sa_family)
+ {
+ case AF_INET:
+ stream_put_in_addr (s, &peer->su_local->sin.sin_addr);
+ break;
+ #ifdef HAVE_IPV6
+ case AF_INET6:
+ stream_put(s, &(peer->su_local->sin6.sin6_addr), 16);
+ break;
+ #endif
+ default:
+ break;
+ }
+ }
+ stream_putc(s, peer->ttl);
+ }
+ else
+ {
+ stream_putc(s, 0);
+#ifdef HAVE_IPV6
+ if ((peer->su.sa.sa_family == AF_INET6) && (peer->su_local))
+ {
+ stream_putw(s, peer->su_local->sa.sa_family);
+ stream_put(s, &(peer->su_local->sin6.sin6_addr), 16);
+ }
+#endif
+
+ if (peer->nexthop.ifp)
+ {
+ len = strlen(peer->nexthop.ifp->name);
+ stream_putc(s, len);
+ stream_put(s, peer->nexthop.ifp->name, len);
+ }
+ else
+ {
+ stream_putc(s, 0);
+ }
+ }
+
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ ret = zclient_send_message(zclient);
+
+ if (ret < 0)
+ zlog_warn("sendmsg_bfd_peer: zclient_send_message() failed");
+
+ if (command == ZEBRA_BFD_DEST_REGISTER)
+ SET_FLAG(bfd_info->flags, BGP_BFD_FLAG_BFD_REG);
+ else if (command == ZEBRA_BFD_DEST_DEREGISTER)
+ UNSET_FLAG(bfd_info->flags, BGP_BFD_FLAG_BFD_REG);
+ return;
+}
+
+/*
+ * bgp_bfd_register_peer - register a peer with BFD through zebra
+ * for monitoring the peer rechahability.
+ */
+void
+bgp_bfd_register_peer (struct peer *peer)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ /* Check if BFD is enabled and peer has already been registered with BFD */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_BFD) ||
+ CHECK_FLAG(bfd_info->flags, BGP_BFD_FLAG_BFD_REG))
+ return;
+
+ sendmsg_bfd_peer(peer, ZEBRA_BFD_DEST_REGISTER);
+}
+
+/**
+ * bgp_bfd_deregister_peer - deregister a peer with BFD through zebra
+ * for stopping the monitoring of the peer
+ * rechahability.
+ */
+void
+bgp_bfd_deregister_peer (struct peer *peer)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ /* Check if BFD is eanbled and peer has not been registered */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_BFD) ||
+ !CHECK_FLAG(bfd_info->flags, BGP_BFD_FLAG_BFD_REG))
+ return;
+
+ sendmsg_bfd_peer(peer, ZEBRA_BFD_DEST_DEREGISTER);
+}
+
+/*
+ * bgp_bfd_update_peer - update peer with BFD with new BFD paramters
+ * through zebra.
+ */
+void
+bgp_bfd_update_peer (struct peer *peer)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ /* Check if the peer has been registered with BFD*/
+ if (!CHECK_FLAG(bfd_info->flags, BGP_BFD_FLAG_BFD_REG))
+ return;
+
+ sendmsg_bfd_peer(peer, ZEBRA_BFD_DEST_UPDATE);
+}
+
+/*
+ * bgp_bfd_dest_replay - Replay all the peers that have BFD enabled
+ * to zebra
+ */
+int
+bgp_bfd_dest_replay (int command, struct zclient *client, zebra_size_t length)
+{
+ struct listnode *mnode, *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+
+ if (BGP_DEBUG (zebra, ZEBRA))
+ zlog_debug("Zebra: BFD Dest replay request");
+
+ /* Replay the peer, if BFD is enabled in BGP */
+
+ for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp))
+ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+ {
+ if (!CHECK_FLAG (peer->flags, PEER_FLAG_BFD))
+ continue;
+
+ bgp_bfd_update_peer(peer);
+ }
+
+ return 0;
+}
+
+/*
+ * bgp_interface_bfd_dest_down - Find the peer for which the BFD status
+ * has changed and bring down the peer
+ * connectivity.
+ */
+int
+bgp_interface_bfd_dest_down (int command, struct zclient *zclient,
+ zebra_size_t length)
+{
+ struct interface *ifp;
+ struct prefix dp;
+ struct prefix sp;
+
+ ifp = zebra_interface_bfd_read (zclient->ibuf, &dp, &sp);
+
+ if (BGP_DEBUG (zebra, ZEBRA))
+ {
+ char buf[2][128];
+ prefix2str(&dp, buf[0], sizeof(buf[0]));
+ if (ifp)
+ {
+ zlog_debug("Zebra: interface %s bfd destination %s down",
+ ifp->name, buf[0]);
+ }
+ else
+ {
+ prefix2str(&sp, buf[1], sizeof(buf[1]));
+ zlog_debug("Zebra: source %s bfd destination %s down",
+ buf[1], buf[0]);
+ }
+ }
+
+ /* Bring the peer down if BFD is enabled in BGP */
+ {
+ struct listnode *mnode, *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp))
+ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
+ {
+ if (!CHECK_FLAG (peer->flags, PEER_FLAG_BFD))
+ continue;
+
+ if (dp.family == AF_INET)
+ if (dp.u.prefix4.s_addr != peer->su.sin.sin_addr.s_addr)
+ continue;
+#ifdef HAVE_IPV6
+ else if (dp.family == AF_INET6)
+ if (!memcmp(&dp.u.prefix6, &peer->su.sin6.sin6_addr,
+ sizeof (struct in6_addr)))
+ continue;
+#endif
+ else
+ continue;
+
+ if (ifp && (ifp == peer->nexthop.ifp))
+ BGP_EVENT_ADD (peer, BGP_Stop);
+ else
+ {
+ if (!peer->su_local)
+ continue;
+
+ if (sp.family == AF_INET)
+ if (sp.u.prefix4.s_addr != peer->su_local->sin.sin_addr.s_addr)
+ continue;
+#ifdef HAVE_IPV6
+ else if (sp.family == AF_INET6)
+ if (!memcmp(&sp.u.prefix6, &peer->su_local->sin6.sin6_addr,
+ sizeof (struct in6_addr)))
+ continue;
+#endif
+ else
+ continue;
+ BGP_EVENT_ADD (peer, BGP_Stop);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * bgp_bfd_peer_param_set - Set the configured BFD paramter values for peer.
+ */
+int
+bgp_bfd_peer_param_set (struct peer *peer, u_int32_t min_rx, u_int32_t min_tx,
+ u_int8_t detect_mult, int reg_peer, int defaults)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ int change = 0;
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ if ((bfd_info->required_min_rx != min_rx) ||
+ (bfd_info->desired_min_tx != min_tx) ||
+ (bfd_info->detect_mult != detect_mult))
+ change = 1;
+
+ bfd_info->required_min_rx = min_rx;
+ bfd_info->desired_min_tx = min_tx;
+ bfd_info->detect_mult = detect_mult;
+
+ if (!defaults)
+ SET_FLAG (bfd_info->flags, BGP_BFD_FLAG_PARAM_CFG);
+ else
+ UNSET_FLAG (bfd_info->flags, BGP_BFD_FLAG_PARAM_CFG);
+
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+ {
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+ bfd_info->required_min_rx = min_rx;
+ bfd_info->desired_min_tx = min_tx;
+ bfd_info->detect_mult = detect_mult;
+ SET_FLAG (bfd_info->flags, BGP_BFD_FLAG_PARAM_CFG);
+
+ if (reg_peer && (peer->status == Established))
+ bgp_bfd_register_peer(peer);
+ else if (change)
+ bgp_bfd_update_peer(peer);
+ }
+ }
+ else
+ {
+ if (reg_peer && (peer->status == Established))
+ bgp_bfd_register_peer(peer);
+ else if (change)
+ bgp_bfd_update_peer(peer);
+ }
+ return 0;
+}
+
+/*
+ * bgp_bfd_peer_param_unset - Unset the configured BFD paramter values for peer.
+ */
+int
+bgp_bfd_peer_param_unset (struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ bfd_info->required_min_rx = BGP_BFD_DEF_MIN_RX;
+ bfd_info->desired_min_tx = BGP_BFD_DEF_MIN_TX;
+ bfd_info->detect_mult = BGP_BFD_DEF_DETECT_MULT;
+ UNSET_FLAG (bfd_info->flags, BGP_BFD_FLAG_PARAM_CFG);
+
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
+ {
+ bfd_info->required_min_rx = BGP_BFD_DEF_MIN_RX;
+ bfd_info->desired_min_tx = BGP_BFD_DEF_MIN_TX;
+ bfd_info->detect_mult = BGP_BFD_DEF_DETECT_MULT;
+ UNSET_FLAG (bfd_info->flags, BGP_BFD_FLAG_PARAM_CFG);
+
+ bgp_bfd_deregister_peer(peer);
+ }
+ }
+ else
+ bgp_bfd_deregister_peer(peer);
+ return 0;
+}
+
+/*
+ * bgp_bfd_peer_config_write - Write the peer BFD configuration.
+ */
+void
+bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ if (CHECK_FLAG (bfd_info->flags, BGP_BFD_FLAG_PARAM_CFG))
+ vty_out (vty, " neighbor %s bfd %d %d %d%s", addr,
+ bfd_info->detect_mult, bfd_info->required_min_rx,
+ bfd_info->desired_min_tx, VTY_NEWLINE);
+ else
+ vty_out (vty, " neighbor %s bfd%s", addr, VTY_NEWLINE);
+}
+
+/*
+ * bgp_bfd_show_info - Show the peer BFD information.
+ */
+void
+bgp_bfd_show_info(struct vty *vty, struct peer *peer)
+{
+ struct bgp_bfd_peer_info *bfd_info;
+
+ bfd_info = (struct bgp_bfd_peer_info *)peer->bfd_info;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_BFD))
+ {
+ vty_out (vty, " BFD: Multi-hop: %s%s",
+ (bgp_bfd_is_peer_multihop(peer)) ? "yes" : "no", VTY_NEWLINE);
+ vty_out (vty, " Detect Mul: %d, Min Rx interval: %d,"
+ " Min Tx interval: %d%s",
+ bfd_info->detect_mult, bfd_info->required_min_rx,
+ bfd_info->desired_min_tx, VTY_NEWLINE);
+ vty_out (vty, "%s", VTY_NEWLINE);
+ }
+}
+
+DEFUN (neighbor_bfd,
+ neighbor_bfd_cmd,
+ NEIGHBOR_CMD2 "bfd",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enables BFD support\n")
+{
+ struct peer *peer;
+ int ret;
+ int reg_peer = 0;
+
+ peer = peer_and_group_lookup_vty (vty, argv[0]);
+ if (! peer)
+ return CMD_WARNING;
+
+ if ( !CHECK_FLAG (peer->flags, PEER_FLAG_BFD) )
+ {
+ ret = peer_flag_set (peer, PEER_FLAG_BFD);
+ if (ret != 0)
+ return bgp_vty_return (vty, ret);
+
+ reg_peer = 1;
+ }
+
+ ret = bgp_bfd_peer_param_set (peer, BGP_BFD_DEF_MIN_RX, BGP_BFD_DEF_MIN_TX,
+ BGP_BFD_DEF_DETECT_MULT, reg_peer, 1);
+ if (ret != 0)
+ return bgp_vty_return (vty, ret);
+
+ return CMD_SUCCESS;
+
+}
+
+DEFUN (neighbor_bfd_param,
+ neighbor_bfd_param_cmd,
+ NEIGHBOR_CMD2 "bfd <2-255> <50-60000> <50-60000>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enables BFD support\n"
+ "Detect Multiplier\n"
+ "Required min receive interval\n"
+ "Desired min transmit interval\n")
+{
+ struct peer *peer;
+ u_int32_t rx_val;
+ u_int32_t tx_val;
+ u_int8_t dm_val;
+ int ret;
+ int reg_peer = 0;
+
+ peer = peer_and_group_lookup_vty (vty, argv[0]);
+ if (!peer)
+ return CMD_WARNING;
+
+ if (!CHECK_FLAG (peer->flags, PEER_FLAG_BFD))
+ {
+ ret = peer_flag_set (peer, PEER_FLAG_BFD);
+ if (ret != 0)
+ return bgp_vty_return (vty, ret);
+
+ reg_peer = 1;
+ }
+
+ VTY_GET_INTEGER_RANGE ("detect-mul", dm_val, argv[1], 2, 255);
+ VTY_GET_INTEGER_RANGE ("min-rx", rx_val, argv[2], 50, 60000);
+ VTY_GET_INTEGER_RANGE ("min-tx", tx_val, argv[3], 50, 60000);
+
+ ret = bgp_bfd_peer_param_set (peer, rx_val, tx_val, dm_val, reg_peer, 0);
+ if (ret != 0)
+ return bgp_vty_return (vty, ret);
+
+ return CMD_SUCCESS;
+
+}
+
+DEFUN (no_neighbor_bfd,
+ no_neighbor_bfd_cmd,
+ NO_NEIGHBOR_CMD2 "bfd",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Disables BFD support\n")
+{
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty (vty, argv[0]);
+ if (! peer)
+ return CMD_WARNING;
+
+ /* Do nothing if there is no change in the flag */
+ if ( !CHECK_FLAG (peer->flags, PEER_FLAG_BFD) )
+ return CMD_SUCCESS;
+
+ ret = bgp_bfd_peer_param_unset(peer);
+ if (ret != 0)
+ return bgp_vty_return (vty, ret);
+
+ ret = peer_flag_unset (peer, PEER_FLAG_BFD);
+ if (ret != 0)
+ return bgp_vty_return (vty, ret);
+
+ return CMD_SUCCESS;
+}
+
+void
+bgp_bfd_init(void)
+{
+ /* Initialize BFD client functions */
+ zclient->interface_bfd_dest_down = bgp_interface_bfd_dest_down;
+ zclient->bfd_dest_replay = bgp_bfd_dest_replay;
+
+ /* "neighbor bfd" commands. */
+ install_element (BGP_NODE, &neighbor_bfd_cmd);
+ install_element (BGP_NODE, &neighbor_bfd_param_cmd);
+ install_element (BGP_NODE, &no_neighbor_bfd_cmd);
+}
diff --git a/bgpd/bgp_bfd.h b/bgpd/bgp_bfd.h
new file mode 100644
index 000000000..8c72c231e
--- /dev/null
+++ b/bgpd/bgp_bfd.h
@@ -0,0 +1,69 @@
+/**
+ * bgp_bfd.h: BGP BFD definitions and structures
+ *
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_BFD_H
+#define _QUAGGA_BGP_BFD_H
+
+#define BGP_BFD_DEF_MIN_RX 300
+#define BGP_BFD_DEF_MIN_TX 300
+#define BGP_BFD_DEF_DETECT_MULT 3
+
+#define BGP_BFD_FLAG_PARAM_CFG (1 << 0) /* parameters have been configured */
+#define BGP_BFD_FLAG_BFD_REG (1 << 1) /* Peer registered with BFD */
+
+struct bgp_bfd_peer_info
+{
+ u_int16_t flags;
+ u_int8_t detect_mult;
+ u_int32_t desired_min_tx;
+ u_int32_t required_min_rx;
+};
+
+extern void
+bgp_bfd_init(void);
+
+extern void
+bgp_bfd_peer_init(struct peer *peer);
+
+extern void
+bgp_bfd_peer_free(struct peer *peer);
+
+extern void
+bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer);
+
+extern void
+bgp_bfd_register_peer (struct peer *peer);
+
+extern void
+bgp_bfd_deregister_peer (struct peer *peer);
+
+extern void
+bgp_bfd_update_peer (struct peer *peer);
+
+extern void
+bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr);
+
+extern void
+bgp_bfd_show_info(struct vty *vty, struct peer *peer);
+
+#endif /* _QUAGGA_BGP_BFD_H */
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 6089cbd94..33c5066cd 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -48,6 +48,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#endif /* HAVE_SNMP */
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_bfd.h"
/* Definition of display strings corresponding to FSM events. This should be
* kept consistent with the events defined in bgpd.h
@@ -1059,6 +1060,8 @@ bgp_stop (struct peer *peer)
/* Reset peer synctime */
peer->synctime = 0;
+
+ bgp_bfd_deregister_peer(peer);
}
/* Stop read and write threads when exists. */
@@ -1548,6 +1551,7 @@ bgp_establish (struct peer *peer)
peer_delete(peer->doppelganger);
}
+ bgp_bfd_register_peer(peer);
return ret;
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 71c9ba1b9..c142573e8 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -52,6 +52,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_mpath.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_bfd.h"
extern struct in_addr router_id_zebra;
@@ -181,7 +182,7 @@ peer_lookup_vty (struct vty *vty, const char *ip_str)
/* This is used only for configuration, so disallow if attempted on
* a dynamic neighbor.
*/
-static struct peer *
+struct peer *
peer_and_group_lookup_vty (struct vty *vty, const char *peer_str)
{
int ret;
@@ -227,7 +228,7 @@ peer_and_group_lookup_vty (struct vty *vty, const char *peer_str)
return NULL;
}
-static int
+int
bgp_vty_return (struct vty *vty, int ret)
{
const char *str = NULL;
@@ -3236,28 +3237,6 @@ DEFUN (no_neighbor_shutdown,
return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_SHUTDOWN);
}
-/* neighbor bfd. */
-DEFUN (neighbor_bfd,
- neighbor_bfd_cmd,
- NEIGHBOR_CMD2 "bfd",
- NEIGHBOR_STR
- NEIGHBOR_ADDR_STR2
- "Respond to BFD session event\n")
-{
- return peer_flag_set_vty (vty, argv[0], PEER_FLAG_BFD);
-}
-
-DEFUN (no_neighbor_bfd,
- no_neighbor_bfd_cmd,
- NO_NEIGHBOR_CMD2 "bfd",
- NO_STR
- NEIGHBOR_STR
- NEIGHBOR_ADDR_STR2
- "Respond to BFD session event\n")
-{
- return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_BFD);
-}
-
/* Deprecated neighbor capability route-refresh. */
DEFUN_DEPRECATED (neighbor_capability_route_refresh,
neighbor_capability_route_refresh_cmd,
@@ -9854,6 +9833,9 @@ bgp_show_peer (struct vty *vty, struct peer *p)
bgp_capability_vty_out (vty, p);
vty_out (vty, "%s", VTY_NEWLINE);
+
+ /* BFD information. */
+ bgp_bfd_show_info(vty, p);
}
static int
@@ -12245,9 +12227,6 @@ bgp_vty_init (void)
install_element (BGP_NODE, &neighbor_passive_cmd);
install_element (BGP_NODE, &no_neighbor_passive_cmd);
- /* "neighbor bfd" commands. */
- install_element (BGP_NODE, &neighbor_bfd_cmd);
- install_element (BGP_NODE, &no_neighbor_bfd_cmd);
/* "neighbor shutdown" commands. */
install_element (BGP_NODE, &neighbor_shutdown_cmd);
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index 5f8f8163f..2e54ccce0 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -32,5 +32,8 @@ extern int bgp_config_write_update_delay (struct vty *, struct bgp *);
extern int bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);
extern int bgp_config_write_listen(struct vty *vty, struct bgp *bgp);
extern int bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp);
+extern int bgp_vty_return (struct vty *vty, int ret);
+extern struct peer *
+peer_and_group_lookup_vty (struct vty *vty, const char *peer_str);
#endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index aade248f7..23de75ae7 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -41,6 +41,7 @@ Boston, MA 02111-1307, USA. */
#include "bgpd/bgp_mpath.h"
#include "bgpd/bgp_nexthop.h"
#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_bfd.h"
/* All information about zebra. */
struct zclient *zclient = NULL;
@@ -314,47 +315,6 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length)
}
static int
-bgp_interface_bfd_dest_down (int command, struct zclient *zclient,
- zebra_size_t length)
-{
- struct interface *ifp;
- struct prefix p;
-
- ifp = zebra_interface_bfd_read (zclient->ibuf, &p);
-
- if (ifp == NULL)
- return 0;
-
- if (BGP_DEBUG (zebra, ZEBRA))
- {
- char buf[128];
- prefix2str(&p, buf, sizeof(buf));
- zlog_debug("Zebra: interface %s bfd destination %s down", ifp->name, buf);
- }
-
- /* Bring the peer down if BFD is enabled in BGP */
- {
- struct listnode *mnode, *node, *nnode;
- struct bgp *bgp;
- struct peer *peer;
-
- for (ALL_LIST_ELEMENTS_RO (bm->bgp, mnode, bgp))
- {
- for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer))
- {
- if (!CHECK_FLAG (peer->flags, PEER_FLAG_BFD))
- continue;
-
- if (ifp == peer->nexthop.ifp)
- BGP_EVENT_ADD (peer, BGP_Stop);
- }
- }
- }
-
- return 0;
-}
-
-static int
bgp_interface_address_add (int command, struct zclient *zclient,
zebra_size_t length)
{
@@ -1718,7 +1678,6 @@ bgp_zebra_init (void)
zclient->ipv4_route_delete = zebra_read_ipv4;
zclient->interface_up = bgp_interface_up;
zclient->interface_down = bgp_interface_down;
- zclient->interface_bfd_dest_down = bgp_interface_bfd_dest_down;
#ifdef HAVE_IPV6
zclient->ipv6_route_add = zebra_read_ipv6;
zclient->ipv6_route_delete = zebra_read_ipv6;
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index ba23f18dd..16040116a 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -64,6 +64,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#include "bgpd/bgp_snmp.h"
#endif /* HAVE_SNMP */
#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_bfd.h"
/* BGP process wide configuration. */
@@ -940,6 +941,9 @@ peer_free (struct peer *peer)
if (peer->conf_if)
XFREE (MTYPE_PEER_CONF_IF, peer->conf_if);
+ if (peer->bfd_info)
+ bgp_bfd_peer_free(peer);
+
memset (peer, 0, sizeof (struct peer));
XFREE (MTYPE_BGP_PEER, peer);
@@ -1242,6 +1246,8 @@ peer_create (union sockunion *su, const char *conf_if, struct bgp *bgp,
}
}
+ bgp_bfd_peer_init(peer);
+
/* Set up peer's events and timers. */
if (! active && peer_active (peer))
bgp_timer_set (peer);
@@ -1830,6 +1836,7 @@ peer_group_get (struct bgp *bgp, const char *name)
group->conf->connect = 0;
SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP);
listnode_add_sort (bgp->group, group);
+ bgp_bfd_peer_init(group->conf);
return 0;
}
@@ -2090,6 +2097,8 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
pfilter->usmap.name = NULL;
pfilter->usmap.map = NULL;
}
+
+ bgp_bfd_peer_group2peer_copy(conf, peer);
}
/* Peer group's remote AS configuration. */
@@ -2165,6 +2174,9 @@ peer_group_delete (struct peer_group *group)
/* Delete from all peer_group list. */
listnode_delete (bgp->group, group);
+ if (group->conf->bfd_info)
+ bgp_bfd_peer_free(group->conf);
+
peer_group_free (group);
return 0;
@@ -5493,7 +5505,7 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
return 0;
}
-static int is_ebgp_multihop_configured (struct peer *peer)
+int is_ebgp_multihop_configured (struct peer *peer)
{
struct peer_group *group;
struct listnode *node, *nnode;
@@ -6010,7 +6022,9 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
if (CHECK_FLAG (peer->flags, PEER_FLAG_BFD))
if (! peer_group_active (peer) ||
! CHECK_FLAG (g_peer->flags, PEER_FLAG_BFD))
- vty_out (vty, " neighbor %s bfd%s", addr, VTY_NEWLINE);
+ {
+ bgp_bfd_peer_config_write(vty, peer, addr);
+ }
/* Password. */
if (peer->password)
@@ -6715,6 +6729,9 @@ bgp_init (void)
#ifdef HAVE_SNMP
bgp_snmp_init ();
#endif /* HAVE_SNMP */
+
+ /* BFD init */
+ bgp_bfd_init();
}
void
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index e0deec730..3b220520b 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -818,6 +818,9 @@ u_char last_reset_cause[BGP_MAX_PACKET_SIZE];
/* hostname and domainname advertised by host */
char *hostname;
char *domainname;
+
+ /* peer specific BFD information */
+ void *bfd_info;
};
#define PEER_PASSWORD_MINLEN (1)
@@ -1217,6 +1220,7 @@ extern int peer_af_flag_check (struct peer *, afi_t, safi_t, u_int32_t);
extern int peer_ebgp_multihop_set (struct peer *, int);
extern int peer_ebgp_multihop_unset (struct peer *);
+extern int is_ebgp_multihop_configured (struct peer *peer);
extern int peer_description_set (struct peer *, char *);
extern int peer_description_unset (struct peer *);
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d32ac1f7a..1ed1d0a70 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -12,7 +12,8 @@ libzebra_la_SOURCES = \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \
- sigevent.c pqueue.c jhash.c memtypes.c workqueue.c nexthop.c json.c
+ sigevent.c pqueue.c jhash.c memtypes.c workqueue.c nexthop.c json.c \
+ ptm_lib.c csv.c
BUILT_SOURCES = memtypes.h route_types.h gitversion.h
@@ -27,7 +28,8 @@ pkginclude_HEADERS = \
str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \
plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \
privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \
- workqueue.h route_types.h libospf.h nexthop.h json.h
+ workqueue.h route_types.h libospf.h nexthop.h json.h \
+ ptm_lib.h csv.h
EXTRA_DIST = \
regex.c regex-gnu.h \
diff --git a/lib/csv.c b/lib/csv.c
new file mode 100644
index 000000000..bcee7bddf
--- /dev/null
+++ b/lib/csv.c
@@ -0,0 +1,684 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/queue.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "csv.h"
+
+#define DEBUG_E 1
+#define DEBUG_V 1
+
+#define log_error(fmt, ...) \
+ do { if (DEBUG_E) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
+ __LINE__, __func__, ##__VA_ARGS__); } while (0)
+
+#define log_verbose(fmt, ...) \
+ do { if (DEBUG_V) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
+ __LINE__, __func__, __VA_ARGS__); } while (0)
+
+struct _csv_field_t_ {
+ TAILQ_ENTRY(_csv_field_t_) next_field;
+ char *field;
+ int field_len;
+};
+
+struct _csv_record_t_ {
+ TAILQ_HEAD(, _csv_field_t_) fields;
+ TAILQ_ENTRY(_csv_record_t_) next_record;
+ char *record;
+ int rec_len;
+};
+
+struct _csv_t_ {
+ TAILQ_HEAD(, _csv_record_t_) records;
+ char *buf;
+ int buflen;
+ int csv_len;
+ int pointer;
+ int num_recs;
+};
+
+
+int
+csvlen (csv_t *csv)
+{
+ return (csv->csv_len);
+}
+
+csv_t *
+csv_init (csv_t *csv,
+ char *buf,
+ int buflen)
+{
+ if (csv == NULL) {
+ csv = malloc(sizeof(csv_t));
+ if (csv == NULL) {
+ log_error("CSV Malloc failed\n");
+ return (NULL);
+ }
+ }
+ memset(csv, 0, sizeof(csv_t));
+
+ csv->buf = buf;
+ csv->buflen = buflen;
+ TAILQ_INIT(&(csv->records));
+ return (csv);
+}
+
+void
+csv_clean (csv_t *csv)
+{
+ csv_record_t *rec;
+ csv_record_t *rec_n;
+
+ rec = TAILQ_FIRST(&(csv->records));
+ while (rec != NULL) {
+ rec_n = TAILQ_NEXT(rec, next_record);
+ csv_remove_record(csv, rec);
+ rec = rec_n;
+ }
+}
+
+void
+csv_free (csv_t *csv)
+{
+ if (csv != NULL) {
+ free(csv);
+ }
+}
+
+static void
+csv_init_record (csv_record_t *record)
+{
+ TAILQ_INIT(&(record->fields));
+ record->rec_len = 0;
+}
+
+csv_record_t *
+csv_record_iter (csv_t *csv)
+{
+ return(TAILQ_FIRST(&(csv->records)));
+}
+
+csv_record_t *
+csv_record_iter_next (csv_record_t *rec)
+{
+ if(!rec) return NULL;
+ return(TAILQ_NEXT(rec, next_record));
+}
+
+char *
+csv_field_iter (csv_record_t *rec,
+ csv_field_t **fld)
+{
+ if(!rec) return NULL;
+ *fld = TAILQ_FIRST(&(rec->fields));
+ return ((*fld)->field);
+}
+
+char *
+csv_field_iter_next (csv_field_t **fld)
+{
+ *fld = TAILQ_NEXT(*fld, next_field);
+ if ((*fld) == NULL) {
+ return (NULL);
+ }
+ return ((*fld)->field);
+}
+
+int
+csv_field_len(csv_field_t *fld)
+{
+ if (fld) {
+ return fld->field_len;
+ }
+ return 0;
+}
+
+static void
+csv_decode_record(csv_record_t *rec)
+{
+ char *curr = rec->record;
+ char *field;
+ csv_field_t *fld;
+
+ field = strpbrk(curr, ",");
+ while (field != NULL) {
+ fld = malloc(sizeof(csv_field_t));
+ if (fld) {
+ TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
+ fld->field = curr;
+ fld->field_len = field-curr;
+ }
+ curr = field + 1;
+ field = strpbrk(curr, ",");
+ }
+ field = strstr(curr, "\n");
+ fld = malloc(sizeof(csv_field_t));
+ if (field && fld) {
+ fld->field = curr;
+ fld->field_len = field-curr;
+ TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
+ }
+}
+
+static csv_field_t *
+csv_add_field_to_record(csv_t *csv,
+ csv_record_t *rec,
+ char *col)
+{
+ csv_field_t *fld;
+ char *str = rec->record;
+ int rlen = rec->rec_len;
+ int blen = csv->buflen;
+
+ fld = malloc(sizeof(csv_field_t));
+ if (!fld) {
+ log_error("field malloc failed\n");
+ /* more cleanup needed */
+ return (NULL);
+ }
+ TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field);
+ fld->field = str+rlen;
+ fld->field_len = snprintf((str+rlen), (blen - rlen), "%s", col);
+ rlen += fld->field_len;
+ rec->rec_len = rlen;
+ return fld;
+}
+
+csv_record_t *
+csv_encode (csv_t *csv,
+ int count,
+ ...)
+{
+ int tempc;
+ va_list list;
+ char *buf = csv->buf;
+ int len = csv->buflen;
+ int pointer = csv->pointer;
+ char *str = NULL;
+ char *col;
+ csv_record_t *rec;
+ csv_field_t *fld;
+
+ if (buf) {
+ str = buf + pointer;
+ } else {
+ /* allocate sufficient buffer */
+ str = (char *)malloc(csv->buflen);
+ if (!str) {
+ log_error("field str malloc failed\n");
+ return (NULL);
+ }
+ }
+
+ va_start(list, count);
+ rec = malloc(sizeof(csv_record_t));
+ if (!rec) {
+ log_error("record malloc failed\n");
+ return (NULL);
+ }
+ csv_init_record(rec);
+ rec->record = str;
+ TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
+ csv->num_recs++;
+
+ /**
+ * Iterate through the fields passed as a variable list and add them
+ */
+ for (tempc = 0; tempc < count; tempc++) {
+ col = va_arg(list, char *);
+ fld = csv_add_field_to_record(csv, rec, col);
+ if (!fld) {
+ log_error("fld malloc failed\n");
+ csv_remove_record(csv, rec);
+ return (NULL);
+ }
+ if (tempc < (count - 1)) {
+ rec->rec_len += snprintf((str+rec->rec_len), (len - rec->rec_len), ",");
+ }
+ }
+ rec->rec_len += snprintf((str+rec->rec_len), (len - rec->rec_len), "\n");
+ va_end(list);
+ csv->csv_len += rec->rec_len;
+ csv->pointer += rec->rec_len;
+ return (rec);
+}
+
+int
+csv_num_records (csv_t *csv)
+{
+ if (csv) {
+ return csv->num_recs;
+ }
+ return 0;
+}
+
+csv_record_t *
+csv_encode_record (csv_t *csv,
+ csv_record_t *rec,
+ int count,
+ ...)
+{
+ int tempc;
+ va_list list;
+ char *str;
+ char *col;
+ csv_field_t *fld;
+ int i;
+
+ va_start(list, count);
+ str = csv_field_iter(rec, &fld);
+ for (tempc = 0; tempc < count; tempc++) {
+ col = va_arg(list, char *);
+ for (i = 0; i < fld->field_len; i++) {
+ str[i] = col[i];
+ }
+ str = csv_field_iter_next(&fld);
+ }
+ va_end(list);
+ return (rec);
+}
+
+csv_record_t *
+csv_append_record (csv_t *csv,
+ csv_record_t *rec,
+ int count,
+ ...)
+{
+ int tempc;
+ va_list list;
+ int len = csv->buflen, tlen;
+ char *str;
+ csv_field_t *fld;
+ char *col;
+
+ if (csv->buf) {
+ /* not only works with discrete bufs */
+ return NULL;
+ }
+
+ if (!rec) {
+ /* create a new rec */
+ rec = calloc(1, sizeof(csv_record_t));
+ if (!rec) {
+ log_error("record malloc failed\n");
+ return NULL;
+ }
+ csv_init_record(rec);
+ rec->record = calloc(1, csv->buflen);
+ if (!rec->record) {
+ log_error("field str malloc failed\n");
+ free(rec);
+ return NULL;
+ }
+ csv_insert_record(csv, rec);
+ }
+
+ str = rec->record;
+
+ va_start(list, count);
+
+ if (rec->rec_len && (str[rec->rec_len-1] == '\n'))
+ str[rec->rec_len-1] = ',';
+
+ /**
+ * Iterate through the fields passed as a variable list and add them
+ */
+ tlen = rec->rec_len;
+ for (tempc = 0; tempc < count; tempc++) {
+ col = va_arg(list, char *);
+ fld = csv_add_field_to_record(csv, rec, col);
+ if (!fld) {
+ log_error("fld malloc failed\n");
+ break;
+ }
+ if (tempc < (count - 1)) {
+ rec->rec_len += snprintf((str+rec->rec_len),
+ (len - rec->rec_len), ",");
+ }
+ }
+ rec->rec_len += snprintf((str+rec->rec_len),
+ (len - rec->rec_len), "\n");
+ va_end(list);
+ csv->csv_len += (rec->rec_len - tlen);
+ csv->pointer += (rec->rec_len - tlen);
+ return (rec);
+}
+
+int
+csv_serialize(csv_t *csv, char *msgbuf, int msglen)
+{
+ csv_record_t *rec;
+ int offset = 0;
+
+ if (!csv || !msgbuf) return -1;
+
+ rec = csv_record_iter(csv);
+ while (rec != NULL) {
+ if ((offset + rec->rec_len) >= msglen)
+ return -1;
+ offset += sprintf(&msgbuf[offset], "%s", rec->record);
+ rec = csv_record_iter_next(rec);
+ }
+
+ return 0;
+}
+
+void
+csv_clone_record (csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec)
+{
+ char *curr;
+ csv_record_t *rec;
+
+ /* first check if rec belongs to this csv */
+ if(!csv_is_record_valid(csv, in_rec)){
+ log_error("rec not in this csv\n");
+ return;
+ }
+
+ /* only works with csv with discrete bufs */
+ if (csv->buf) {
+ log_error("un-supported for this csv type - single buf detected\n");
+ return;
+ }
+
+ /* create a new rec */
+ rec = calloc(1, sizeof(csv_record_t));
+ if (!rec) {
+ log_error("record malloc failed\n");
+ return;
+ }
+ csv_init_record(rec);
+ curr = calloc(1, csv->buflen);
+ if (!curr) {
+ log_error("field str malloc failed\n");
+ return;
+ }
+ rec->record = curr;
+ rec->rec_len = in_rec->rec_len;
+ strcpy(rec->record, in_rec->record);
+
+ /* decode record into fields */
+ csv_decode_record(rec);
+
+ *out_rec = rec;
+}
+
+void
+csv_remove_record (csv_t *csv, csv_record_t *rec)
+{
+ csv_field_t *fld, *p_fld;
+
+ /* first check if rec belongs to this csv */
+ if(!csv_is_record_valid(csv, rec)){
+ log_error("rec not in this csv\n");
+ return;
+ }
+
+ /* remove fields */
+ csv_field_iter(rec, &fld);
+ while(fld) {
+ p_fld = fld;
+ csv_field_iter_next(&fld);
+ TAILQ_REMOVE(&(rec->fields), p_fld, next_field);
+ free(p_fld);
+ }
+
+ TAILQ_REMOVE(&(csv->records), rec, next_record);
+
+ csv->num_recs--;
+ csv->csv_len -= rec->rec_len;
+ csv->pointer -= rec->rec_len;
+ if (!csv->buf)
+ free(rec->record);
+ free(rec);
+}
+
+void
+csv_insert_record (csv_t *csv, csv_record_t *rec)
+{
+ /* first check if rec already in csv */
+ if(csv_is_record_valid(csv, rec)){
+ log_error("rec already in this csv\n");
+ return;
+ }
+
+ /* we can only insert records if no buf was supplied during csv init */
+ if (csv->buf) {
+ log_error("un-supported for this csv type - single buf detected\n");
+ return;
+ }
+
+ /* do we go beyond the max buf set for this csv ?*/
+ if ((csv->csv_len + rec->rec_len) > csv->buflen ) {
+ log_error("cannot insert - exceeded buf size\n");
+ return;
+ }
+
+ TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
+ csv->num_recs++;
+ csv->csv_len += rec->rec_len;
+ csv->pointer += rec->rec_len;
+}
+
+csv_record_t *
+csv_concat_record (csv_t *csv,
+ csv_record_t *rec1,
+ csv_record_t *rec2)
+{
+ char *curr;
+ char *ret;
+ csv_record_t *rec;
+
+ /* first check if rec1 and rec2 belong to this csv */
+ if(!csv_is_record_valid(csv, rec1) ||
+ !csv_is_record_valid(csv, rec2)) {
+ log_error("rec1 and/or rec2 invalid\n");
+ return (NULL);
+ }
+
+ /* we can only concat records if no buf was supplied during csv init */
+ if (csv->buf) {
+ log_error("un-supported for this csv type - single buf detected\n");
+ return (NULL);
+ }
+
+ /* create a new rec */
+ rec = calloc(1, sizeof(csv_record_t));
+ if (!rec) {
+ log_error("record malloc failed\n");
+ return (NULL);
+ }
+ csv_init_record(rec);
+
+ curr = (char *)calloc(1, csv->buflen);
+ if (!curr) {
+ log_error("field str malloc failed\n");
+ return (NULL);
+ }
+ rec->record = curr;
+
+ /* concat the record string */
+ ret = strstr(rec1->record, "\n");
+ if (!ret) {
+ log_error("rec1 str not properly formatted\n");
+ return (NULL);
+ }
+
+ snprintf(curr, (int)(ret - rec1->record + 1), "%s", rec1->record);
+ strcat(curr, ",");
+
+ ret = strstr(rec2->record, "\n");
+ if (!ret) {
+ log_error("rec2 str not properly formatted\n");
+ return (NULL);
+ }
+
+ snprintf((curr+strlen(curr)), (int)(ret - rec2->record + 1), "%s",
+ rec2->record);
+ strcat(curr, "\n");
+ rec->rec_len = strlen(curr);
+
+ /* paranoia */
+ assert(csv->buflen >
+ (csv->csv_len - rec1->rec_len - rec2->rec_len + rec->rec_len));
+
+ /* decode record into fields */
+ csv_decode_record(rec);
+
+ /* now remove rec1 and rec2 and insert rec into this csv */
+ csv_remove_record(csv, rec1);
+ csv_remove_record(csv, rec2);
+ csv_insert_record(csv, rec);
+
+ return rec;
+}
+
+void
+csv_decode (csv_t *csv, char *inbuf)
+{
+ char *buf;
+ char *pos;
+ csv_record_t *rec;
+
+ buf = (inbuf)? inbuf:csv->buf;
+ pos = strpbrk(buf, "\n");
+ while (pos != NULL) {
+ rec = calloc(1, sizeof(csv_record_t));
+ csv_init_record(rec);
+ TAILQ_INSERT_TAIL(&(csv->records), rec, next_record);
+ csv->num_recs++;
+ if (csv->buf)
+ rec->record = buf;
+ else {
+ rec->record = calloc(1, csv->buflen);
+ if (!rec->record) {
+ log_error("field str malloc failed\n");
+ return;
+ }
+ strncpy(rec->record, buf, pos-buf+1);
+ }
+ rec->rec_len = pos-buf+1;
+ /* decode record into fields */
+ csv_decode_record(rec);
+ buf = pos+1;
+ pos = strpbrk(buf, "\n");
+ }
+}
+
+int
+csv_is_record_valid(csv_t *csv, csv_record_t *in_rec)
+{
+ csv_record_t *rec;
+ int valid = 0;
+
+ rec = csv_record_iter(csv);
+ while (rec) {
+ if(rec == in_rec) {
+ valid = 1;
+ break;
+ }
+ rec = csv_record_iter_next(rec);
+ }
+
+ return valid;
+}
+
+void
+csv_dump (csv_t *csv)
+{
+ csv_record_t *rec;
+ csv_field_t *fld;
+ char *str;
+
+ rec = csv_record_iter(csv);
+ while (rec != NULL) {
+ str = csv_field_iter(rec, &fld);
+ while (str != NULL) {
+ fprintf(stderr, "%s\n", str);
+ str = csv_field_iter_next(&fld);
+ }
+ rec = csv_record_iter_next(rec);
+ }
+}
+
+#ifdef TEST_CSV
+
+static int
+get_memory_usage (pid_t pid)
+{
+ int fd, data, stack;
+ char buf[4096], status_child[BUFSIZ];
+ char *vm;
+
+ sprintf(status_child, "/proc/%d/status", pid);
+ if ((fd = open(status_child, O_RDONLY)) < 0)
+ return -1;
+
+ read(fd, buf, 4095);
+ buf[4095] = '\0';
+ close(fd);
+
+ data = stack = 0;
+
+ vm = strstr(buf, "VmData:");
+ if (vm) {
+ sscanf(vm, "%*s %d", &data);
+ }
+ vm = strstr(buf, "VmStk:");
+ if (vm) {
+ sscanf(vm, "%*s %d", &stack);
+ }
+
+ return data + stack;
+}
+
+int main ()
+{
+ char buf[10000];
+ csv_t csv;
+ int p;
+ int i, j;
+ csv_record_t *rec;
+ csv_field_t *fld;
+ char *str;
+ char hdr1[32], hdr2[32];
+
+ log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
+ csv_init(&csv, buf, 256);
+ sprintf(hdr1, "%4u", 0);
+ sprintf(hdr2, "%4u", 1);
+ log_verbose("(%d/%d/%d/%d)\n", strlen(hdr1),
+ strlen(hdr2), atoi(hdr1), atoi(hdr2));
+ rec = csv_encode(&csv, 2, hdr1, hdr2);
+ csv_encode(&csv, 4, "name", "age", "sex", "hei");
+ csv_encode(&csv, 3, NULL, "0", NULL);
+ csv_encode(&csv, 2, "p", "35");
+ for (i=0; i < 50; i++) {
+ csv_encode(&csv, 2, "p", "10");
+ }
+ csv_encode(&csv, 2, "pdfadfadfadsadsaddfdfdsfdsd",
+ "35444554545454545");
+ log_verbose("%s\n", buf);
+ sprintf(hdr1, "%4u", csv.csv_len);
+ sprintf(hdr2, "%4u", 1);
+ log_verbose("(%d/%d/%d/%d)\n", strlen(hdr1),
+ strlen(hdr2), atoi(hdr1), atoi(hdr2));
+ rec = csv_encode_record(&csv, rec, 2, hdr1, hdr2);
+ log_verbose("(%d/%d)\n%s\n", rec->rec_len, csv.csv_len, buf);
+
+ log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
+ csv_clean(&csv);
+ log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
+ csv_init(&csv, buf, 256);
+ csv_decode(&csv, NULL);
+ log_verbose("AFTER DECODE\n");
+ csv_dump(&csv);
+ csv_clean(&csv);
+ log_verbose("Mem: %ld\n", get_memory_usage(getpid()));
+}
+#endif
diff --git a/lib/csv.h b/lib/csv.h
new file mode 100644
index 000000000..078f39087
--- /dev/null
+++ b/lib/csv.h
@@ -0,0 +1,172 @@
+/* Copyright 2013 Cumulus Networks, LLC. All rights reserved. */
+
+#ifndef __CSV_H__
+#define __CSV_H__
+
+/*
+ * CSV encoding and decoding routines.
+ *
+ * Example:
+ * Encoding side:
+ *
+ * csv_t *csv;
+ * csv_record_t *fstrec;
+ * csv_record_t *rec;
+ * char buf[BUFSIZ];
+ *
+ * csv = csv_init(csv, buf, BUFSIZ);
+ * ...
+ * fstrec = csv_encode(csv, 2, "hello", "world");
+ * rec = csv_encode(csv, 2, "foo", "bar");
+ * ...
+ * fstrec = csv_encode_record(csv, fstrec, 2, "HELLO", "WORLD");
+ * ...
+ * csv_clean(csv);
+ *
+ * Decoding side:
+ *
+ * csv_t *csv;
+ * csv_record_t *rec;
+ * csv_field_t *fld;
+ * char *rcvdbuf;
+ *
+ * csv = csv_init(csv, rcvdbuf, BUFSIZ);
+ * ...
+ * csv_decode(csv);
+ * csv_dump(csv);
+ *
+ * for (rec = csv_record_iter(csv); rec;
+ * rec = csv_record_iter_next(rec)) {
+ * ...
+ * for (str = csv_field_iter(rec, &fld); str;
+ * str = csv_field_iter_next(&fld)) {
+ * ...
+ * }
+ * }
+ * ...
+ * csv_clean(csv);
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/queue.h>
+
+typedef struct _csv_field_t_ csv_field_t;
+typedef struct _csv_record_t_ csv_record_t;
+typedef struct _csv_t_ csv_t;
+
+/**
+ * Initialize the CSV structure (if necessary, allocate first). Point to
+ * the passed string buffer.
+ */
+csv_t * csv_init(csv_t *csv, char *buf, int buflen);
+
+/**
+ * Encode the variable list of arguments as CSV fields. The csv structure
+ * should have been initialized (with the string buffer). The fields get
+ * concatenated into the string.
+ */
+csv_record_t *csv_encode(csv_t *csv, int count, ...);
+
+/**
+ * Encode the variable list arguments into an existing record, essentially
+ * overwriting the record. No checking is done for consistency. The number
+ * of fields should be the same as what was encoded and the length of each
+ * field should also be the same as what was encoded before. The "rec"
+ * parameter should be the same as what was returned from a previous call
+ * to csv_encode().
+ *
+ * Useful for message encoding/decoding that get passed around between
+ * processes/nodes - e.g. the message header record can be rewritten AFTER
+ * encoding all other records, with new information such as total length.
+ */
+csv_record_t *csv_encode_record(csv_t *csv, csv_record_t *rec,
+ int count, ...);
+
+/**
+ * Decode a CSV formatted string. The csv structure should have been
+ * initialized (with the string). The function creates a LIST of records
+ * (csv_record_t structure) where each record is in turn a LIST of fields
+ * (csv_field_t structure). The record points to the string containing the
+ * list of fields. Similarly, the field points to the field string.
+ * NB: csv initialized for discrete buf , caller will pass inbuf
+ */
+void csv_decode(csv_t *csv, char *inbuf);
+
+/**
+ * Dump all fields of a decoded CSV to stderr
+ */
+void csv_dump(csv_t *csv);
+
+/**
+ * Total length of all characters encoded in the CSV.
+ */
+int csvlen(csv_t *csv);
+
+void csv_clean(csv_t *csv);
+void csv_free(csv_t *csv);
+
+/**
+ * Iterate through the records and fields of an encoded/decoded CSV.
+ */
+csv_record_t *csv_record_iter(csv_t *csv);
+csv_record_t *csv_record_iter_next(csv_record_t *rec);
+char *csv_field_iter(csv_record_t *rec, csv_field_t **fld);
+char *csv_field_iter_next(csv_field_t **fld);
+
+/**
+ * Return the length of field
+ */
+int csv_field_len(csv_field_t *fld);
+
+/**
+ * Checks to see if a record belongs to a csv
+ */
+int csv_is_record_valid(csv_t *csv, csv_record_t *in_rec);
+
+/**
+ * concat two records in a csv
+ * Returns the newly formed record which includes fields from rec1 and rec2
+ * rec1 and rec2 are removed
+ */
+csv_record_t *csv_concat_record(csv_t *csv, csv_record_t *rec1,
+ csv_record_t *rec2);
+
+/**
+ * Remove a record from csv
+ * Only works when csv has discrete record bufs
+ */
+void csv_remove_record(csv_t *csv, csv_record_t *rec);
+
+/**
+ * Insert a record into csv
+ * Only works when csv has discrete record bufs
+ */
+void csv_insert_record(csv_t *csv, csv_record_t *rec);
+
+/**
+ * append fields to a record
+ * Only works when csv has discrete record bufs
+ */
+csv_record_t *
+csv_append_record (csv_t *csv, csv_record_t *rec, int count, ...);
+
+/**
+ * Serialize contents of csv into string
+ * Only works when csv has discrete record bufs
+ */
+int csv_serialize(csv_t *csv, char *msgbuf, int msglen);
+
+/**
+ * Clone a record.
+ * Only works when csv has discrete record bufs
+ */
+void csv_clone_record (csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec);
+
+/**
+ * Return number of records
+ */
+int csv_num_records (csv_t *csv);
+
+#endif
diff --git a/lib/log.c b/lib/log.c
index e15f3d50d..a4ab9fdea 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -836,6 +836,10 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY (ZEBRA_INTERFACE_NBR_ADDRESS_ADD),
DESC_ENTRY (ZEBRA_INTERFACE_NBR_ADDRESS_DELETE),
DESC_ENTRY (ZEBRA_IMPORT_CHECK_UPDATE),
+ DESC_ENTRY (ZEBRA_INTERFACE_BFD_DEST_DOWN),
+ DESC_ENTRY (ZEBRA_BFD_DEST_REGISTER),
+ DESC_ENTRY (ZEBRA_BFD_DEST_DEREGISTER),
+ DESC_ENTRY (ZEBRA_BFD_DEST_UPDATE),
};
#undef DESC_ENTRY
diff --git a/lib/memtypes.c b/lib/memtypes.c
index c32c08817..57eb31ff3 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -162,6 +162,7 @@ struct memory_list memory_list_bgp[] =
{ MTYPE_BGP_REGEXP, "BGP regexp" },
{ MTYPE_BGP_AGGREGATE, "BGP aggregate" },
{ MTYPE_BGP_ADDR, "BGP own address" },
+ { MTYPE_BGP_PEER_BFD_INFO, "BGP peer BFD info" },
{ -1, NULL }
};
diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c
new file mode 100644
index 000000000..175cf1443
--- /dev/null
+++ b/lib/ptm_lib.c
@@ -0,0 +1,464 @@
+/*********************************************************************
+ * Copyright 2015 Cumulus Networks, LLC. All rights reserved.
+ *
+ * library file used by clients for sending commands and parsing response
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "csv.h"
+#include "ptm_lib.h"
+
+#define DEBUG_E 0
+#define DEBUG_V 0
+
+#define ERRLOG(fmt, ...) \
+ do { if (DEBUG_E) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
+ __LINE__, __func__, ##__VA_ARGS__); } while (0)
+
+#define DLOG(fmt, ...) \
+ do { if (DEBUG_V) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
+ __LINE__, __func__, ##__VA_ARGS__); } while (0)
+
+typedef struct ptm_lib_msg_ctxt_s {
+ int cmd_id;
+ csv_t *csv;
+ ptmlib_msg_type type;
+} ptm_lib_msg_ctxt_t;
+
+static csv_record_t *
+_ptm_lib_encode_header(csv_t *csv,
+ csv_record_t *rec,
+ int msglen,
+ int version,
+ int type,
+ int cmd_id,
+ char *client_name)
+{
+ char msglen_buf[16], vers_buf[16], type_buf[16], cmdid_buf[16];
+ char client_buf[32];
+ csv_record_t *rec1;
+
+ sprintf(msglen_buf, "%4u", msglen);
+ sprintf(vers_buf, "%4u", version);
+ sprintf(type_buf, "%4u", type);
+ sprintf(cmdid_buf, "%4u", cmd_id);
+ snprintf(client_buf, 17, "%16.16s", client_name);
+ if (rec) {
+ rec1 = csv_encode_record(csv, rec, 5, msglen_buf, vers_buf,
+ type_buf, cmdid_buf, client_buf);
+ } else {
+ rec1 = csv_encode(csv, 5, msglen_buf, vers_buf,
+ type_buf, cmdid_buf, client_buf);
+ }
+ return (rec1);
+}
+
+static int
+_ptm_lib_decode_header (csv_t *csv,
+ int *msglen,
+ int *version,
+ int *type,
+ int *cmd_id,
+ char *client_name)
+{
+ char *hdr;
+ csv_record_t *rec;
+ csv_field_t *fld;
+ int i, j;
+
+ csv_decode(csv, NULL);
+ rec = csv_record_iter(csv);
+ if (rec == NULL) {
+ DLOG("malformed CSV\n");
+ return (-1);
+ }
+ hdr = csv_field_iter(rec, &fld);
+ if (hdr == NULL) {
+ DLOG("malformed CSV\n");
+ return (-1);
+ }
+ *msglen = atoi(hdr);
+ hdr = csv_field_iter_next(&fld);
+ if (hdr == NULL) {
+ DLOG("malformed CSV\n");
+ return (-1);
+ }
+ *version = atoi(hdr);
+ hdr = csv_field_iter_next(&fld);
+ if (hdr == NULL) {
+ DLOG("malformed CSV\n");
+ return (-1);
+ }
+ *type = atoi(hdr);
+ hdr = csv_field_iter_next(&fld);
+ if (hdr == NULL) {
+ DLOG("malformed CSV\n");
+ return (-1);
+ }
+ *cmd_id = atoi(hdr);
+ hdr = csv_field_iter_next(&fld);
+ if (hdr == NULL) {
+ DLOG("malformed CSV\n");
+ return (-1);
+ }
+ /* remove leading spaces */
+ for (i = j = 0; i < csv_field_len(fld); i++) {
+ if (!isspace(hdr[i])) {
+ client_name[j] = hdr[i];
+ j++;
+ }
+ }
+ client_name[j] = '\0';
+
+ return (0);
+}
+
+int
+ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt,
+ const char *key, char *val)
+{
+ ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
+ csv_t *csv;
+ csv_record_t *mh_rec, *rec;
+
+ if (!p_ctxt) {
+ ERRLOG("%s: no context \n", __FUNCTION__);
+ return -1;
+ }
+
+ csv = p_ctxt->csv;
+ mh_rec = csv_record_iter(csv);
+ rec = csv_record_iter_next(mh_rec);
+
+ /* append to the hdr record */
+ rec = csv_append_record(csv, rec, 1, key);
+ if (!rec) {
+ ERRLOG("%s: Could not append key \n", __FUNCTION__);
+ return -1;
+ }
+
+ rec = csv_record_iter_next(rec);
+ /* append to the data record */
+ rec = csv_append_record(csv, rec, 1, val);
+ if (!rec) {
+ ERRLOG("%s: Could not append val \n", __FUNCTION__);
+ return -1;
+ }
+
+ /* update the msg hdr */
+ _ptm_lib_encode_header(csv, mh_rec,
+ (csvlen(csv) - PTMLIB_MSG_HDR_LEN),
+ PTMLIB_MSG_VERSION, p_ctxt->type,
+ p_ctxt->cmd_id, hdl->client_name);
+
+ return 0;
+}
+
+int
+ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type,
+ void *in_ctxt, void **out_ctxt)
+{
+ ptm_lib_msg_ctxt_t *p_ctxt;
+ ptm_lib_msg_ctxt_t *p_in_ctxt = in_ctxt;
+ csv_t *csv;
+ csv_record_t *rec, *d_rec;
+
+ /* Initialize csv for using discrete record buffers */
+ csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ);
+
+ if (!csv) {
+ ERRLOG("%s: Could not allocate csv \n", __FUNCTION__);
+ return -1;
+ }
+
+ rec = _ptm_lib_encode_header(csv, NULL, 0,
+ PTMLIB_MSG_VERSION, type,
+ cmd_id, hdl->client_name);
+
+ if (!rec) {
+ ERRLOG("%s: Could not allocate record \n", __FUNCTION__);
+ csv_clean(csv);
+ csv_free(csv);
+ return -1;
+ }
+
+ p_ctxt = calloc(1, sizeof(*p_ctxt));
+ if (!p_ctxt) {
+ ERRLOG("%s: Could not allocate context \n", __FUNCTION__);
+ csv_clean(csv);
+ csv_free(csv);
+ return -1;
+ }
+
+ p_ctxt->csv = csv;
+ p_ctxt->cmd_id = cmd_id;
+ p_ctxt->type = type;
+
+ *(ptm_lib_msg_ctxt_t **)out_ctxt = p_ctxt;
+
+ /* caller supplied a context to initialize with? */
+ if (p_in_ctxt) {
+ /* insert the hdr rec */
+ rec = csv_record_iter(p_in_ctxt->csv);
+ csv_clone_record (p_in_ctxt->csv, rec, &d_rec);
+ csv_insert_record (csv, d_rec);
+ /* insert the data rec */
+ rec = csv_record_iter_next(rec);
+ csv_clone_record (p_in_ctxt->csv, rec, &d_rec);
+ csv_insert_record (csv, d_rec);
+ }
+ return 0;
+}
+
+int
+ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt,
+ char *buf, int *len)
+{
+ ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
+ csv_t *csv;
+ csv_record_t *rec;
+
+ if (!p_ctxt) {
+ ERRLOG("%s: no context \n", __FUNCTION__);
+ return -1;
+ }
+
+ csv = p_ctxt->csv;
+ rec = csv_record_iter(csv);
+
+ _ptm_lib_encode_header(csv, rec,
+ (csvlen(csv) - PTMLIB_MSG_HDR_LEN),
+ PTMLIB_MSG_VERSION, p_ctxt->type,
+ p_ctxt->cmd_id, hdl->client_name);
+
+ /* parse csv contents into string */
+ if (buf && len) {
+ if (csv_serialize(csv, buf, *len)) {
+ ERRLOG("%s: cannot serialize\n", __FUNCTION__);
+ return -1;
+ }
+ *len = csvlen(csv);
+ }
+
+ csv_clean(csv);
+ csv_free(csv);
+ free(p_ctxt);
+
+ return 0;
+}
+
+int
+ptm_lib_find_key_in_msg(void *ctxt, const char *key, char *val)
+{
+ ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
+ csv_t *csv = p_ctxt->csv;
+ csv_record_t *hrec, *drec;
+ csv_field_t *hfld, *dfld;
+ char *hstr, *dstr;
+
+ /**
+ * skip over ptm hdr if present
+ * The next hdr is the keys (column name)
+ * The next hdr is the data
+ */
+ if (csv_num_records(csv) > 2) {
+ hrec = csv_record_iter(csv);
+ hrec = csv_record_iter_next(hrec);
+ } else {
+ hrec = csv_record_iter(csv);
+ }
+ drec = csv_record_iter_next(hrec);
+ val[0] = '\0';
+ for(hstr = csv_field_iter(hrec, &hfld),
+ dstr = csv_field_iter(drec, &dfld);
+ (hstr && dstr);
+ hstr = csv_field_iter_next(&hfld),
+ dstr = csv_field_iter_next(&dfld)) {
+ if (!strncmp(hstr, key, csv_field_len(hfld))) {
+ snprintf(val, csv_field_len(dfld)+1, "%s", dstr);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int
+_ptm_lib_read_ptm_socket(int fd, char *buf, int len)
+{
+ int retries = 0, rc;
+ int bytes_read = 0;
+
+ while (bytes_read != len) {
+ rc = recv(fd, (void *) (buf + bytes_read), (len - bytes_read),
+ MSG_DONTWAIT);
+ if (rc <= 0) {
+ if (errno && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+ ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
+ strerror(errno), rc);
+ return (rc);
+ } else {
+ if (retries++ < 2) {
+ usleep(10000);
+ continue;
+ }
+ DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
+ errno, strerror(errno), bytes_read, len);
+ return (bytes_read);
+ }
+ break;
+ } else {
+ bytes_read += rc;
+ }
+ }
+
+ return bytes_read;
+}
+
+int
+ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd,
+ char *inbuf, int inlen, void *arg)
+{
+ int rc, len;
+ char client_name[32];
+ int cmd_id, type, ver, msglen;
+ csv_t *csv;
+ ptm_lib_msg_ctxt_t *p_ctxt;
+
+ len = _ptm_lib_read_ptm_socket(fd, inbuf, PTMLIB_MSG_HDR_LEN);
+ if (len <= 0)
+ return (len);
+
+ csv = csv_init(NULL, inbuf, PTMLIB_MSG_HDR_LEN);
+
+ if (!csv) {
+ DLOG("Cannot allocate csv for hdr\n");
+ return (-1);
+ }
+
+ rc = _ptm_lib_decode_header(csv, &msglen, &ver, &type, &cmd_id, client_name);
+
+ csv_clean(csv);
+ csv_free(csv);
+
+ if (rc < 0) {
+ /* could not decode the CSV - maybe its legacy cmd?
+ * get the entire cmd from the socket and see if we can process it
+ */
+ if (len == PTMLIB_MSG_HDR_LEN) {
+ len += _ptm_lib_read_ptm_socket(fd, (inbuf+PTMLIB_MSG_HDR_LEN),
+ inlen - PTMLIB_MSG_HDR_LEN);
+ if (len <= 0)
+ return (len);
+ }
+
+ inbuf[len] = '\0';
+ /* we only support the get-status cmd */
+ if (strcmp(inbuf, PTMLIB_CMD_GET_STATUS)) {
+ DLOG("unsupported legacy cmd %s\n", inbuf);
+ return (-1);
+ }
+ /* internally create a csv-style cmd */
+ ptm_lib_init_msg(hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, (void *)&p_ctxt);
+ if (!p_ctxt) {
+ DLOG("couldnt allocate context\n");
+ return (-1);
+ }
+ ptm_lib_append_msg(hdl, p_ctxt, "cmd", PTMLIB_CMD_GET_STATUS);
+
+ } else {
+
+ if (msglen > inlen) {
+ DLOG("msglen [%d] > inlen [%d]\n", msglen, inlen);
+ return -1;
+ }
+
+ /* read the rest of the msg */
+ len = _ptm_lib_read_ptm_socket(fd, inbuf, msglen);
+ if (len <= 0) {
+ return (len);
+ }
+
+ inbuf[len] = '\0';
+
+ csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ);
+ if (!csv) {
+ ERRLOG("Cannot allocate csv for msg\n");
+ return -1;
+ }
+
+ csv_decode(csv, inbuf);
+ p_ctxt = calloc(1, sizeof(*p_ctxt));
+ if (!p_ctxt) {
+ ERRLOG("%s: Could not allocate context \n", __FUNCTION__);
+ csv_clean(csv);
+ csv_free(csv);
+ return -1;
+ }
+
+ p_ctxt->csv = csv;
+ p_ctxt->cmd_id = cmd_id;
+ p_ctxt->type = type;
+ }
+
+ switch(p_ctxt->type) {
+ case PTMLIB_MSG_TYPE_NOTIFICATION:
+ if (hdl->notify_cb)
+ hdl->notify_cb(arg, p_ctxt);
+ break;
+ case PTMLIB_MSG_TYPE_CMD:
+ if (hdl->cmd_cb)
+ hdl->cmd_cb(arg, p_ctxt);
+ break;
+ case PTMLIB_MSG_TYPE_RESPONSE:
+ if (hdl->response_cb)
+ hdl->response_cb(arg, p_ctxt);
+ break;
+ default:
+ return -1;
+ }
+
+ csv_clean(p_ctxt->csv);
+ csv_free(p_ctxt->csv);
+ free(p_ctxt);
+
+ return len;
+}
+
+ptm_lib_handle_t *
+ptm_lib_register(char *client_name,
+ ptm_cmd_cb cmd_cb,
+ ptm_notify_cb notify_cb,
+ ptm_response_cb response_cb)
+{
+ ptm_lib_handle_t *hdl;
+
+ hdl = calloc(1, sizeof(*hdl));
+
+ if (hdl) {
+ strcpy(hdl->client_name, client_name);
+ hdl->cmd_cb = cmd_cb;
+ hdl->notify_cb = notify_cb;
+ hdl->response_cb = response_cb;
+ }
+
+ return hdl;
+}
+
+void
+ptm_lib_deregister(ptm_lib_handle_t *hdl)
+{
+ if (hdl) {
+ memset(hdl, 0x00, sizeof(*hdl));
+ free(hdl);
+ }
+}
diff --git a/lib/ptm_lib.h b/lib/ptm_lib.h
new file mode 100644
index 000000000..1aa2f356f
--- /dev/null
+++ b/lib/ptm_lib.h
@@ -0,0 +1,49 @@
+/*********************************************************************
+ * Copyright 2015 Cumulus Networks, LLC. All rights reserved.
+ *
+ * library file used by clients for sending commands and parsing response
+ *
+ */
+
+#define PTMLIB_MSG_SZ 1024
+#define PTMLIB_MSG_HDR_LEN 37
+#define PTMLIB_MSG_VERSION 2
+#define PTMLIB_MAXNAMELEN 32
+
+#define PTMLIB_CMD_GET_STATUS "get-status"
+#define PTMLIB_CMD_GET_BFD_CLIENT "get-bfd-client"
+#define PTMLIB_CMD_START_BFD_SESS "start-bfd-sess"
+#define PTMLIB_CMD_STOP_BFD_SESS "stop-bfd-sess"
+
+typedef enum {
+ PTMLIB_MSG_TYPE_NOTIFICATION = 1,
+ PTMLIB_MSG_TYPE_CMD,
+ PTMLIB_MSG_TYPE_RESPONSE,
+} ptmlib_msg_type;
+
+typedef enum {
+ MODULE_BFD = 0,
+ MODULE_LLDP,
+ MODULE_MAX,
+} ptmlib_mod_type;
+
+typedef int (*ptm_cmd_cb) (void *data, void *arg);
+typedef int (*ptm_notify_cb) (void *data, void *arg);
+typedef int (*ptm_response_cb) (void *data, void *arg);
+typedef int (*ptm_log_cb) (void *data, void *arg,...);
+
+typedef struct ptm_lib_handle_s {
+ char client_name[PTMLIB_MAXNAMELEN];
+ ptm_cmd_cb cmd_cb;
+ ptm_notify_cb notify_cb;
+ ptm_response_cb response_cb;
+} ptm_lib_handle_t;
+
+/* Prototypes */
+int ptm_lib_process_msg(ptm_lib_handle_t *, int, char *, int, void *);
+ptm_lib_handle_t *ptm_lib_register(char *, ptm_cmd_cb, ptm_notify_cb, ptm_response_cb);
+void ptm_lib_deregister(ptm_lib_handle_t *);
+int ptm_lib_find_key_in_msg(void *, const char *, char *);
+int ptm_lib_init_msg(ptm_lib_handle_t *, int, int, void *, void **);
+int ptm_lib_append_msg(ptm_lib_handle_t *, void *, const char *, char *);
+int ptm_lib_complete_msg(ptm_lib_handle_t *, void *, char *, int *);
diff --git a/lib/zclient.c b/lib/zclient.c
index 722075ada..1b03510ed 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -943,31 +943,43 @@ memconstant(const void *s, int c, size_t n)
}
struct interface*
-zebra_interface_bfd_read (struct stream *s, struct prefix *p)
+zebra_interface_bfd_read (struct stream *s, struct prefix *dp,
+ struct prefix *sp)
{
unsigned int ifindex;
- struct interface *ifp;
+ struct interface *ifp = NULL;
int plen;
/* Get interface index. */
ifindex = stream_getl (s);
/* Lookup index. */
- ifp = if_lookup_by_index (ifindex);
- if (ifp == NULL)
+ if (ifindex != 0)
{
- zlog_warn ("zebra_interface_bfd_read: "
- "Can't find interface by ifindex: %d ", ifindex);
- return NULL;
+ ifp = if_lookup_by_index (ifindex);
+ if (ifp == NULL)
+ {
+ zlog_warn ("zebra_interface_bfd_read: "
+ "Can't find interface by ifindex: %d ", ifindex);
+ return NULL;
+ }
}
- /* Fetch interface address. */
- p->family = stream_getc (s);
+ /* Fetch destination address. */
+ dp->family = stream_getc (s);
+
+ plen = prefix_blen (dp);
+ stream_get (&dp->u.prefix, s, plen);
+ dp->prefixlen = stream_getc (s);
- plen = prefix_blen (p);
- stream_get (&p->u.prefix, s, plen);
- p->prefixlen = stream_getc (s);
+ if (sp)
+ {
+ sp->family = stream_getc (s);
+ plen = prefix_blen (sp);
+ stream_get (&sp->u.prefix, s, plen);
+ sp->prefixlen = stream_getc (s);
+ }
return ifp;
}
@@ -1284,6 +1296,10 @@ zclient_read (struct thread *thread)
if (zclient->import_check_update)
(*zclient->import_check_update) (command, zclient, length);
break;
+ case ZEBRA_BFD_DEST_REPLAY:
+ if (zclient->bfd_dest_replay)
+ (*zclient->bfd_dest_replay) (command, zclient, length);
+ break;
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index bd4d06647..ab8c03e2d 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -94,6 +94,7 @@ struct zclient
int (*ipv6_route_delete) (int, struct zclient *, uint16_t);
int (*nexthop_update) (int, struct zclient *, uint16_t);
int (*import_check_update) (int, struct zclient *, uint16_t);
+ int (*bfd_dest_replay) (int, struct zclient *, uint16_t);
};
/* Zebra API message flag. */
@@ -176,7 +177,9 @@ extern void zclient_create_header (struct stream *, uint16_t);
extern struct interface *zebra_interface_add_read (struct stream *);
extern struct interface *zebra_interface_state_read (struct stream *s);
extern struct connected *zebra_interface_address_read (int, struct stream *);
-extern struct interface *zebra_interface_bfd_read (struct stream *s, struct prefix *);
+extern struct interface *zebra_interface_bfd_read (struct stream *s,
+ struct prefix *,
+ struct prefix *);
extern struct nbr_connected *zebra_interface_nbr_address_read (int, struct stream *);
extern void zebra_interface_if_set_value (struct stream *, struct interface *);
extern void zebra_router_id_update_read (struct stream *s, struct prefix *rid);
diff --git a/lib/zebra.h b/lib/zebra.h
index c1231e4c8..387cd6541 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -434,7 +434,11 @@ struct in_pktinfo
#define ZEBRA_IMPORT_ROUTE_UNREGISTER 31
#define ZEBRA_IMPORT_CHECK_UPDATE 32
#define ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD 33
-#define ZEBRA_MESSAGE_MAX 34
+#define ZEBRA_BFD_DEST_REGISTER 34
+#define ZEBRA_BFD_DEST_DEREGISTER 35
+#define ZEBRA_BFD_DEST_UPDATE 36
+#define ZEBRA_BFD_DEST_REPLAY 37
+#define ZEBRA_MESSAGE_MAX 38
/* Marker value used in new Zserv, in the byte location corresponding
* the command value in the old zserv header. To allow old and new
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 4a31c9a9a..38a4228ce 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -338,7 +338,7 @@ ospf_interface_bfd_dest_down (int command, struct zclient *zclient,
struct route_node *node;
struct prefix p;
- ifp = zebra_interface_bfd_read (zclient->ibuf, &p);
+ ifp = zebra_interface_bfd_read (zclient->ibuf, &p, NULL);
if (ifp == NULL)
return 0;
diff --git a/zebra/Makefile.am b/zebra/Makefile.am
index 470e8a1a2..a4e6f2c18 100644
--- a/zebra/Makefile.am
+++ b/zebra/Makefile.am
@@ -34,16 +34,18 @@ zebra_SOURCES = \
zserv.c main.c interface.c connected.c zebra_rib.c zebra_routemap.c \
redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \
irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \
- $(othersrc) zebra_ptm.c zebra_rnh.c
+ $(othersrc) zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c
testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
zebra_vty.c zebra_ptm.c zebra_routemap.c \
- kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c
+ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \
+ zebra_ptm_null.c
noinst_HEADERS = \
connected.h ioctl.h rib.h rt.h zserv.h redistribute.h debug.h rtadv.h \
interface.h ipforward.h irdp.h router-id.h kernel_socket.h \
- rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h
+ rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h \
+ zebra_ptm_redistribute.h
zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6)
diff --git a/zebra/interface.c b/zebra/interface.c
index 2f8de0b03..cd9c21a61 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -684,22 +684,6 @@ if_refresh (struct interface *ifp)
if_get_flags (ifp);
}
-/* BFD session goes down, send message to the protocols. */
-void
-if_bfd_session_down (struct interface *ifp, struct prefix *p)
-{
- if (IS_ZEBRA_DEBUG_EVENT)
- {
- char buf[INET6_ADDRSTRLEN];
-
- zlog_debug ("MESSAGE: ZEBRA_INTERFACE_BFD_DEST_DOWN %s/%d on %s",
- inet_ntop (p->family, &p->u.prefix, buf, INET6_ADDRSTRLEN),
- p->prefixlen, ifp->name);
- }
-
- zebra_interface_bfd_update (ifp, p);
-}
-
/* Output prefix string to vty. */
static int
diff --git a/zebra/interface.h b/zebra/interface.h
index 72e40044f..e8456ce59 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -221,7 +221,6 @@ extern void if_add_update (struct interface *ifp);
extern void if_up (struct interface *);
extern void if_down (struct interface *);
extern void if_refresh (struct interface *);
-extern void if_bfd_session_down(struct interface *, struct prefix *);
extern void if_flags_update (struct interface *, uint64_t);
extern int if_subnet_add (struct interface *, struct connected *);
extern int if_subnet_delete (struct interface *, struct connected *);
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 843f5ad4b..6dfb455f4 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -482,23 +482,6 @@ zebra_interface_address_delete_update (struct interface *ifp,
}
}
-void
-zebra_interface_bfd_update (struct interface *ifp, struct prefix *p)
-{
- struct listnode *node, *nnode;
- struct zserv *client;
-
- for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
- {
- /* Supporting for OSPF and BGP */
- if (client->proto != ZEBRA_ROUTE_OSPF && client->proto != ZEBRA_ROUTE_BGP)
- continue;
-
- /* Notify to the protocol daemons. */
- zsend_interface_bfd_update (ZEBRA_INTERFACE_BFD_DEST_DOWN, client, ifp, p);
- }
-}
-
int
zebra_add_import_table_entry (struct route_node *rn, struct rib *rib)
{
diff --git a/zebra/redistribute.h b/zebra/redistribute.h
index aef944ca2..a4f321979 100644
--- a/zebra/redistribute.h
+++ b/zebra/redistribute.h
@@ -46,7 +46,6 @@ extern void zebra_interface_address_add_update (struct interface *,
struct connected *);
extern void zebra_interface_address_delete_update (struct interface *,
struct connected *c);
-extern void zebra_interface_bfd_update (struct interface *, struct prefix *);
extern int zebra_check_addr (struct prefix *);
extern int zebra_import_table (afi_t afi, u_int32_t table_id,
diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c
index c40938c4d..04a36ab8b 100644
--- a/zebra/redistribute_null.c
+++ b/zebra/redistribute_null.c
@@ -53,9 +53,6 @@ void zebra_interface_address_delete_update (struct interface *a,
struct connected *b)
{ return; }
#endif
-void zebra_interface_bfd_update (struct interface *a, struct prefix *b)
-{ return; }
-
int zebra_import_table (afi_t afi, u_int32_t table_id, u_int32_t metric,
int add)
diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c
index 58f2514ad..468164947 100644
--- a/zebra/zebra_ptm.c
+++ b/zebra/zebra_ptm.c
@@ -28,19 +28,42 @@
#include "zebra/zebra_ptm.h"
#include "if.h"
#include "command.h"
+#include "stream.h"
+#include "ptm_lib.h"
+#include "zebra/zebra_ptm_redistribute.h"
#define ZEBRA_PTM_RECONNECT_TIME_INITIAL 1 /* initial reconnect is 1s */
#define ZEBRA_PTM_RECONNECT_TIME_MAX 300
#define PTM_MSG_LEN 4
#define PTM_HEADER_LEN 37
-const char *ZEBRA_PTM_GET_STATUS_CMD = "get-status";
-const char *ZEBRA_PTM_PORT_STR = "port";
-const char *ZEBRA_PTM_CBL_STR = "cbl status";
-const char *ZEBRA_PTM_PASS_STR = "pass";
-const char *ZEBRA_PTM_FAIL_STR = "fail";
-const char *ZEBRA_PTM_BFDSTATUS_STR = "BFD status";
-const char *ZEBRA_PTM_BFDDEST_STR = "BFD peer";
+
+const char ZEBRA_PTM_GET_STATUS_CMD[] = "get-status";
+const char ZEBRA_PTM_BFD_START_CMD[] = "start-bfd-sess";
+const char ZEBRA_PTM_BFD_STOP_CMD[] = "stop-bfd-sess";
+
+const char ZEBRA_PTM_PORT_STR[] = "port";
+const char ZEBRA_PTM_CBL_STR[] = "cbl status";
+const char ZEBRA_PTM_PASS_STR[] = "pass";
+const char ZEBRA_PTM_FAIL_STR[] = "fail";
+const char ZEBRA_PTM_BFDSTATUS_STR[] = "state";
+const char ZEBRA_PTM_BFDSTATUS_UP_STR[] = "Up";
+const char ZEBRA_PTM_BFDSTATUS_DOWN_STR[] = "Down";
+const char ZEBRA_PTM_BFDDEST_STR[] = "peer";
+const char ZEBRA_PTM_BFDSRC_STR[] = "local";
+const char ZEBRA_PTM_INVALID_PORT_NAME[] = "N/A";
+const char ZEBRA_PTM_INVALID_SRC_IP[] = "N/A";
+
+const char ZEBRA_PTM_BFD_DST_IP_FIELD[] = "dstIPaddr";
+const char ZEBRA_PTM_BFD_SRC_IP_FIELD[] = "srcIPaddr";
+const char ZEBRA_PTM_BFD_MIN_RX_FIELD[] = "requiredMinRx";
+const char ZEBRA_PTM_BFD_MIN_TX_FIELD[] = "upMinTx";
+const char ZEBRA_PTM_BFD_DETECT_MULT_FIELD[] = "detectMult";
+const char ZEBRA_PTM_BFD_MULTI_HOP_FIELD[] = "multiHop";
+const char ZEBRA_PTM_BFD_CLIENT_FIELD[] = "client";
+const char ZEBRA_PTM_BFD_SEQID_FIELD[] = "seqid";
+const char ZEBRA_PTM_BFD_IFNAME_FIELD[] = "ifName";
+const char ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD[] = "maxHopCnt";
extern struct zebra_t zebrad;
int ptm_enable;
@@ -49,29 +72,63 @@ int zebra_ptm_sock = -1;
struct thread *zebra_ptm_thread = NULL;
static int zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL;
+int zebra_ptm_pid = 0;
+static ptm_lib_handle_t *ptm_hdl;
-static void zebra_ptm_finish(void);
static int zebra_ptm_socket_init(void);
int zebra_ptm_sock_read(struct thread *);
int zebra_ptm_sock_write(struct thread *);
static void zebra_ptm_install_commands (void);
+static int zebra_ptm_handle_cbl_msg(void *arg, void *in_ctxt);
+static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt);
+void zebra_bfd_peer_replay_req (void);
const char ZEBRA_PTM_SOCK_NAME[] = "\0/var/run/ptmd.socket";
void
zebra_ptm_init (void)
{
+ char buf[64];
+
+ zebra_ptm_pid = getpid();
zebra_ptm_install_commands();
+
+ sprintf(buf, "%s", "quagga");
+ ptm_hdl = ptm_lib_register(buf, NULL, zebra_ptm_handle_bfd_msg,
+ zebra_ptm_handle_cbl_msg);
}
int
zebra_ptm_connect (struct thread *t)
{
- zebra_ptm_socket_init();
+ int init = 0;
+ char *data;
+ void *out_ctxt;
+ int len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
+
+ if (zebra_ptm_sock == -1) {
+ zebra_ptm_socket_init();
+ init = 1;
+ }
if (zebra_ptm_sock != -1) {
- zebra_ptm_thread = thread_add_write (zebrad.master, zebra_ptm_sock_write,
- NULL, zebra_ptm_sock);
+ if (init) {
+ zebra_bfd_peer_replay_req();
+ }
+
+ if (ptm_enable) {
+ data = calloc(1, len);
+ if (!data) {
+ zlog_debug("%s: Allocation of send data failed", __func__);
+ return -1;
+ }
+ ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, "cmd", ZEBRA_PTM_GET_STATUS_CMD);
+ ptm_lib_complete_msg(ptm_hdl, out_ctxt, data, &len);
+
+ zebra_ptm_thread = thread_add_write (zebrad.master, zebra_ptm_sock_write,
+ data, zebra_ptm_sock);
+ }
zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL;
} else {
zebra_ptm_reconnect_time *= 2;
@@ -85,21 +142,6 @@ zebra_ptm_connect (struct thread *t)
return(errno);
}
-static void
-zebra_ptm_finish (void)
-{
- if (zebra_ptm_sock != -1)
- {
- if (zebra_ptm_thread != NULL)
- {
- thread_cancel(zebra_ptm_thread);
- zebra_ptm_thread = NULL;
- }
- close (zebra_ptm_sock);
- zebra_ptm_sock = -1;
- }
-}
-
DEFUN (zebra_ptm_enable,
zebra_ptm_enable_cmd,
"ptm-enable",
@@ -142,13 +184,12 @@ DEFUN (no_zebra_ptm_enable,
ifp->ptm_enable = 0;
if (if_is_operative (ifp) && send_linkup) {
- zlog_debug ("%s: Bringing up interface %s\n", __func__,
+ zlog_debug ("%s: Bringing up interface %s", __func__,
ifp->name);
if_up (ifp);
}
}
}
- zebra_ptm_finish();
return CMD_SUCCESS;
}
@@ -185,12 +226,12 @@ zebra_ptm_socket_init (void)
sizeof (addr.sun_family)+sizeof (ZEBRA_PTM_SOCK_NAME)-1);
if (ret < 0)
{
- zlog_debug("%s: Unable to connect to socket %s [%s]\n",
+ zlog_warn("%s: Unable to connect to socket %s [%s]",
__func__, ZEBRA_PTM_SOCK_NAME, safe_strerror(errno));
close (sock);
return -1;
}
- zlog_debug ("%s: connection to ptm socket %s succeeded\n",
+ zlog_debug ("%s: connection to ptm socket %s succeeded",
__func__, ZEBRA_PTM_SOCK_NAME);
zebra_ptm_sock = sock;
return sock;
@@ -203,134 +244,163 @@ zebra_ptm_install_commands (void)
install_element (CONFIG_NODE, &no_zebra_ptm_enable_cmd);
}
-static char *
-zebra_ptm_find_key(const char *key_arg, char *arg, int arglen)
+/* BFD session goes down, send message to the protocols. */
+void
+if_bfd_session_down (struct interface *ifp, struct prefix *dp, struct prefix *sp)
{
- char buf[ZEBRA_PTM_MAX_SOCKBUF];
- char *data, *hdr, *key, *val;
- char *currd, *currh;
- char *savd, *savh;
-
- snprintf(buf, sizeof(buf), "%s", arg);
- /* split up row header and data */
- hdr = buf;
- data = strstr(hdr, "\n");
- if (!data)
- return NULL;
- *data = '\0';
- data++;
-
- currh = strtok_r(hdr, ",\n\0", &savh);
- currd = strtok_r(data, ",\n\0", &savd);
- while(currh && currd) {
- key = currh;
- val = currd;
- if (!strcmp(key, key_arg)) {
- /* found the value */
- return val;
+ if (IS_ZEBRA_DEBUG_EVENT)
+ {
+ char buf[2][INET6_ADDRSTRLEN];
+
+ if (ifp)
+ {
+ zlog_debug ("MESSAGE: ZEBRA_INTERFACE_BFD_DEST_DOWN %s/%d on %s",
+ inet_ntop (dp->family, &dp->u.prefix, buf, INET6_ADDRSTRLEN),
+ dp->prefixlen, ifp->name);
+ }
+ else
+ {
+ zlog_debug ("MESSAGE: ZEBRA_INTERFACE_BFD_DEST_DOWN %s/%d "
+ "with src %s/%d",
+ inet_ntop (dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN),
+ dp->prefixlen,
+ inet_ntop (sp->family, &sp->u.prefix, buf[1], INET6_ADDRSTRLEN),
+ sp->prefixlen);
+ }
}
- currh = strtok_r(NULL, ",\n\0", &savh);
- currd = strtok_r(NULL, ",\n\0", &savd);
- }
- return NULL;
+ zebra_interface_bfd_update (ifp, dp, sp);
}
-static void
-zebra_ptm_handle_bfd_msg(char *buf, int buflen)
+static int
+zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt)
{
- struct interface *ifp;
- char *port_str, *bfdst_str, *dest_str;
- struct in_addr dest_addr;
+ struct interface *ifp = NULL;
+ char port_str[128];
+ char bfdst_str[32];
+ char dest_str[64];
+ char src_str[64];
struct prefix dest_prefix;
+ struct prefix src_prefix;
- port_str = zebra_ptm_find_key(ZEBRA_PTM_PORT_STR, buf, buflen);
+ ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_PORT_STR, port_str);
- if (!port_str) {
- zlog_debug("%s: Key %s not found in PTM msg\n", __func__,
+ if (port_str[0] == '\0') {
+ zlog_debug("%s: Key %s not found in PTM msg", __func__,
ZEBRA_PTM_PORT_STR);
- return;
+ return -1;
}
- ifp = if_lookup_by_name(port_str);
+ if (strcmp(ZEBRA_PTM_INVALID_PORT_NAME, port_str)) {
+ ifp = if_lookup_by_name(port_str);
- if (!ifp) {
- zlog_err("%s: %s not found in interface list\n", __func__, port_str);
- return;
+ if (!ifp) {
+ zlog_err("%s: %s not found in interface list", __func__, port_str);
+ return -1;
+ }
}
- bfdst_str = zebra_ptm_find_key(ZEBRA_PTM_BFDSTATUS_STR, buf, buflen);
+ ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDSTATUS_STR, bfdst_str);
- if (!bfdst_str) {
- zlog_debug("%s: Key %s not found in PTM msg\n", __func__,
+ if (bfdst_str[0] == '\0') {
+ zlog_debug("%s: Key %s not found in PTM msg", __func__,
ZEBRA_PTM_BFDSTATUS_STR);
- return;
+ return -1;
}
- dest_str = zebra_ptm_find_key(ZEBRA_PTM_BFDDEST_STR, buf, buflen);
+ ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDDEST_STR, dest_str);
- if (!dest_str) {
- zlog_debug("%s: Key %s not found in PTM msg\n", __func__,
+ if (dest_str[0] == '\0') {
+ zlog_debug("%s: Key %s not found in PTM msg", __func__,
ZEBRA_PTM_BFDDEST_STR);
- return;
+ return -1;
}
- zlog_debug("%s: Recv Port [%s] bfd status [%s] peer [%s]\n", __func__,
- port_str, bfdst_str, dest_str);
+ ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDSRC_STR, src_str);
- /* if ptm cbl checks fail then no more processing required */
- if (!ifp->ptm_status) {
- return;
+ if (src_str[0] == '\0') {
+ zlog_debug("%s: Key %s not found in PTM msg", __func__,
+ ZEBRA_PTM_BFDSRC_STR);
+ return -1;
}
+ zlog_debug("%s: Recv Port [%s] bfd status [%s] peer [%s] local [%s]",
+ __func__, port_str, bfdst_str, dest_str, src_str);
+
/* we only care if bfd session goes down */
- if (!strcmp (bfdst_str, ZEBRA_PTM_FAIL_STR)) {
- if (ifp->ptm_enable && if_is_no_ptm_operative (ifp)) {
- if (inet_pton(AF_INET, dest_str, &dest_addr) <= 0) {
- zlog_err("%s: Peer addr(%s) not found\n", __func__,
- dest_str);
- return;
- }
- dest_prefix.family = AF_INET;
- dest_prefix.u.prefix4 = dest_addr;
- dest_prefix.prefixlen = IPV4_MAX_PREFIXLEN;
+ if (!strcmp (bfdst_str, ZEBRA_PTM_BFDSTATUS_DOWN_STR)) {
+ if (inet_pton(AF_INET, dest_str, &dest_prefix.u.prefix4) > 0) {
+ dest_prefix.family = AF_INET;
+ dest_prefix.prefixlen = IPV4_MAX_PREFIXLEN;
+ }
+#ifdef HAVE_IPV6
+ else if (inet_pton(AF_INET6, dest_str, &dest_prefix.u.prefix6) > 0) {
+ dest_prefix.family = AF_INET6;
+ dest_prefix.prefixlen = IPV6_MAX_PREFIXLEN;
+ }
+#endif /* HAVE_IPV6 */
+ else {
+ zlog_err("%s: Peer addr %s not found", __func__,
+ dest_str);
+ return -1;
+ }
- zlog_debug("%s: bfd session down [%s]\n", __func__, dest_str);
- if_bfd_session_down(ifp, &dest_prefix);
+ memset(&src_prefix, 0, sizeof(struct prefix));
+ if (strcmp(ZEBRA_PTM_INVALID_SRC_IP, src_str)) {
+ if (inet_pton(AF_INET, src_str, &src_prefix.u.prefix4) > 0) {
+ src_prefix.family = AF_INET;
+ src_prefix.prefixlen = IPV4_MAX_PREFIXLEN;
+ }
+#ifdef HAVE_IPV6
+ else if (inet_pton(AF_INET6, src_str, &src_prefix.u.prefix6) > 0) {
+ src_prefix.family = AF_INET6;
+ src_prefix.prefixlen = IPV6_MAX_PREFIXLEN;
+ }
+#endif /* HAVE_IPV6 */
+ else {
+ zlog_err("%s: Local addr %s not found", __func__,
+ src_str);
+ return -1;
}
+ }
+
+ if_bfd_session_down(ifp, &dest_prefix, &src_prefix);
}
+
+ return 0;
}
-static void
-zebra_ptm_handle_cbl_msg(char *buf, int buflen)
+static int
+zebra_ptm_handle_cbl_msg(void *arg, void *in_ctxt)
{
struct interface *ifp;
- char *cbl_str, *port_str;
+ char cbl_str[32];
+ char port_str[128];
- port_str = zebra_ptm_find_key(ZEBRA_PTM_PORT_STR, buf, buflen);
+ ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_PORT_STR, port_str);
- if (!port_str) {
- zlog_debug("%s: Key %s not found in PTM msg\n", __func__,
+ if (port_str[0] == '\0') {
+ zlog_debug("%s: Key %s not found in PTM msg", __func__,
ZEBRA_PTM_PORT_STR);
- return;
+ return 0;
}
- cbl_str = zebra_ptm_find_key(ZEBRA_PTM_CBL_STR, buf, buflen);
+ ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_CBL_STR, cbl_str);
- if (!cbl_str) {
- zlog_debug("%s: Key %s not found in PTM msg\n", __func__,
+ if (cbl_str[0] == '\0') {
+ zlog_debug("%s: Key %s not found in PTM msg", __func__,
ZEBRA_PTM_CBL_STR);
- return;
+ return 0;
}
- zlog_debug("%s: Recv Port [%s] cbl status [%s]\n", __func__,
+ zlog_debug("%s: Recv Port [%s] cbl status [%s]", __func__,
port_str, cbl_str);
ifp = if_lookup_by_name(port_str);
if (!ifp) {
- zlog_err("%s: %s not found in interface list\n", __func__, port_str);
- return;
+ zlog_err("%s: %s not found in interface list", __func__, port_str);
+ return -1;
}
if (!strcmp(cbl_str, ZEBRA_PTM_PASS_STR) && (!ifp->ptm_status)) {
@@ -342,17 +412,8 @@ zebra_ptm_handle_cbl_msg(char *buf, int buflen)
if (ifp->ptm_enable && if_is_no_ptm_operative (ifp))
if_down (ifp);
}
-}
-
-static void
-zebra_ptm_process_csv (char *buf, int buflen)
-{
- /* handle any cbl messages */
- zebra_ptm_handle_cbl_msg(buf, buflen);
-
- /* handle any bfd messages */
- zebra_ptm_handle_bfd_msg(buf, buflen);
+ return 0;
}
int
@@ -360,29 +421,33 @@ zebra_ptm_sock_write (struct thread *thread)
{
int sock;
int nbytes;
+ char *data;
sock = THREAD_FD (thread);
+ data = THREAD_ARG (thread);
if (sock == -1)
return -1;
- nbytes = send(sock, ZEBRA_PTM_GET_STATUS_CMD,
- strlen(ZEBRA_PTM_GET_STATUS_CMD), 0);
+ errno = 0;
- if (nbytes <= 0)
- {
- if (nbytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN)
- zlog_warn ("routing socket error: %s", safe_strerror (errno));
+ nbytes = send(sock, data, strlen(data), 0);
- zebra_ptm_sock = -1;
- zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect, NULL,
- zebra_ptm_reconnect_time);
- return (-1);
+ if (nbytes <= 0) {
+ if (errno && errno != EWOULDBLOCK && errno != EAGAIN) {
+ zlog_warn ("%s routing socket error: %s", __func__,
+ safe_strerror (errno));
+ zebra_ptm_sock = -1;
+ zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect,
+ NULL, zebra_ptm_reconnect_time);
+ return (-1);
}
+ }
- zlog_debug ("%s: Sent message %s\n", __func__, ZEBRA_PTM_GET_STATUS_CMD);
- zebra_ptm_thread = thread_add_read (zebrad.master, zebra_ptm_sock_read, NULL, sock);
-
+ zlog_debug ("%s: Sent message (%d) %s", __func__, strlen(data), data);
+ zebra_ptm_thread = thread_add_read (zebrad.master, zebra_ptm_sock_read,
+ NULL, sock);
+ free (data);
return(0);
}
@@ -390,11 +455,10 @@ int
zebra_ptm_sock_read (struct thread *thread)
{
int sock, done = 0;
- char rcvbuf[ZEBRA_PTM_MAX_SOCKBUF];
- int nbytes, msglen;
+ int rc;
char *rcvptr;
- char msgbuf[ZEBRA_PTM_MAX_SOCKBUF];
+ errno = 0;
sock = THREAD_FD (thread);
if (sock == -1)
@@ -402,37 +466,337 @@ zebra_ptm_sock_read (struct thread *thread)
/* PTM communicates in CSV format */
while(!done) {
- rcvptr = rcvbuf;
- /* get PTM header */
- nbytes = recv(sock, rcvptr, PTM_HEADER_LEN, 0);
- if (nbytes <= 0)
- break;
- snprintf(msgbuf, PTM_MSG_LEN+1, "%s", rcvptr);
- msglen = strtol(msgbuf, NULL, 10);
-
- /* get the PTM message */
- rcvptr = calloc(1, msglen);
- nbytes = recv(sock, rcvptr, msglen, 0);
- if (nbytes <= 0)
- break;
- /* process one PTM message */
- zebra_ptm_process_csv(rcvptr, msglen);
- free(rcvptr);
+ rcvptr = calloc(1, ZEBRA_PTM_MAX_SOCKBUF);
+
+ rc = ptm_lib_process_msg(ptm_hdl, sock, rcvptr, ZEBRA_PTM_MAX_SOCKBUF,
+ NULL);
+ if (rc <= 0)
+ break;
}
- if (nbytes <= 0) {
- if (errno && errno != EWOULDBLOCK && errno != EAGAIN) {
- zlog_warn ("routing socket error: %s", safe_strerror (errno));
+ if (rc <= 0) {
+ if (((rc == 0) && !errno) || (errno && (errno != EWOULDBLOCK) && (errno != EAGAIN))) {
+ zlog_warn ("%s routing socket error: %s(%d) bytes %d", __func__,
+ safe_strerror (errno), errno, rc);
close (zebra_ptm_sock);
zebra_ptm_sock = -1;
zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect,
- NULL, zebra_ptm_reconnect_time);
+ NULL, zebra_ptm_reconnect_time);
return (-1);
}
}
- zebra_ptm_thread = thread_add_read (zebrad.master, zebra_ptm_sock_read, NULL, sock);
+ free(rcvptr);
+ zebra_ptm_thread = thread_add_read (zebrad.master, zebra_ptm_sock_read,
+ NULL, sock);
+
+ return 0;
+}
+
+/* BFD peer/dst register/update */
+int
+zebra_ptm_bfd_dst_register (struct zserv *client, int sock, u_short length,
+ int command)
+{
+ char *data;
+ struct stream *s;
+ struct prefix src_p;
+ struct prefix dst_p;
+ u_char multi_hop;
+ u_char multi_hop_cnt;
+ u_char detect_mul;
+ unsigned int min_rx_timer;
+ unsigned int min_tx_timer;
+ char if_name[INTERFACE_NAMSIZ];
+ u_char len;
+ void *out_ctxt;
+ char buf[INET6_ADDRSTRLEN];
+ char tmp_buf[64];
+ int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
+
+ if (command == ZEBRA_BFD_DEST_UPDATE)
+ client->bfd_peer_upd8_cnt++;
+ else
+ client->bfd_peer_add_cnt++;
+
+ zlog_debug("bfd_dst_register msg from client %s: length=%d",
+ zebra_route_string(client->proto), length);
+
+ if (zebra_ptm_sock == -1)
+ {
+ zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect,
+ NULL, zebra_ptm_reconnect_time);
+ return -1;
+ }
+
+ data = calloc(1, data_len);
+ if (!data)
+ {
+ zlog_debug("%s: Allocation of send data failed", __func__);
+ return -1;
+ }
+
+ ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt);
+ sprintf(tmp_buf, "%s", ZEBRA_PTM_BFD_START_CMD);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, "cmd", tmp_buf);
+ sprintf(tmp_buf, "quagga");
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD,
+ tmp_buf);
+ sprintf(tmp_buf, "%d", zebra_ptm_pid);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD,
+ tmp_buf);
+
+ s = client->ibuf;
+
+ dst_p.family = stream_getw(s);
+
+ if (dst_p.family == AF_INET)
+ dst_p.prefixlen = IPV4_MAX_BYTELEN;
+ else
+ dst_p.prefixlen = IPV6_MAX_BYTELEN;
+
+ stream_get(&dst_p.u.prefix, s, dst_p.prefixlen);
+ if (dst_p.family == AF_INET)
+ {
+ inet_ntop(AF_INET, &dst_p.u.prefix4, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DST_IP_FIELD, buf);
+ }
+#ifdef HAVE_IPV6
+ else
+ {
+ inet_ntop(AF_INET6, &dst_p.u.prefix6, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DST_IP_FIELD, buf);
+ }
+#endif /* HAVE_IPV6 */
+
+ min_rx_timer = stream_getl(s);
+ sprintf(tmp_buf, "%d", min_rx_timer);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MIN_RX_FIELD,
+ tmp_buf);
+ min_tx_timer = stream_getl(s);
+ sprintf(tmp_buf, "%d", min_tx_timer);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MIN_TX_FIELD,
+ tmp_buf);
+ detect_mul = stream_getc(s);
+ sprintf(tmp_buf, "%d", detect_mul);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DETECT_MULT_FIELD,
+ tmp_buf);
+
+ multi_hop = stream_getc(s);
+ if (multi_hop)
+ {
+ sprintf(tmp_buf, "%d", 1);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MULTI_HOP_FIELD,
+ tmp_buf);
+ src_p.family = stream_getw(s);
+
+ if (src_p.family == AF_INET)
+ src_p.prefixlen = IPV4_MAX_BYTELEN;
+ else
+ src_p.prefixlen = IPV6_MAX_BYTELEN;
+
+ stream_get(&src_p.u.prefix, s, src_p.prefixlen);
+ if (src_p.family == AF_INET)
+ {
+ inet_ntop(AF_INET, &src_p.u.prefix4, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+#ifdef HAVE_IPV6
+ else
+ {
+ inet_ntop(AF_INET6, &src_p.u.prefix6, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+#endif /* HAVE_IPV6 */
+
+ multi_hop_cnt = stream_getc(s);
+ sprintf(tmp_buf, "%d", multi_hop_cnt);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD,
+ tmp_buf);
+ }
+ else
+ {
+#ifdef HAVE_IPV6
+ if (dst_p.family == AF_INET6)
+ {
+ src_p.family = stream_getw(s);
+
+ if (src_p.family == AF_INET)
+ src_p.prefixlen = IPV4_MAX_BYTELEN;
+ else
+ src_p.prefixlen = IPV6_MAX_BYTELEN;
+
+ stream_get(&src_p.u.prefix, s, src_p.prefixlen);
+ if (src_p.family == AF_INET)
+ {
+ inet_ntop(AF_INET, &src_p.u.prefix4, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+ else
+ {
+ inet_ntop(AF_INET6, &src_p.u.prefix6, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+ }
+#endif /* HAVE_IPV6 */
+ len = stream_getc(s);
+ stream_get(if_name, s, len);
+ if_name[len] = '\0';
+
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_IFNAME_FIELD,
+ if_name);
+ }
+
+ ptm_lib_complete_msg(ptm_hdl, out_ctxt, data, &data_len);
+ zebra_ptm_thread = thread_add_write (zebrad.master, zebra_ptm_sock_write,
+ data, zebra_ptm_sock);
+ return 0;
+}
+
+/* BFD peer/dst deregister */
+int
+zebra_ptm_bfd_dst_deregister (struct zserv *client, int sock, u_short length)
+{
+ char *data;
+ struct stream *s;
+ struct prefix src_p;
+ struct prefix dst_p;
+ u_char multi_hop;
+ char if_name[INTERFACE_NAMSIZ];
+ u_char len;
+ char buf[INET6_ADDRSTRLEN];
+ char tmp_buf[64];
+ int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
+ void *out_ctxt;
+
+ client->bfd_peer_del_cnt++;
+
+ zlog_debug("bfd_dst_deregister msg from client %s: length=%d",
+ zebra_route_string(client->proto), length);
+
+ if (zebra_ptm_sock == -1)
+ {
+ zebra_ptm_thread = thread_add_timer (zebrad.master, zebra_ptm_connect,
+ NULL, zebra_ptm_reconnect_time);
+ return -1;
+ }
+
+ data = calloc(1, data_len);
+ if (!data)
+ {
+ zlog_debug("%s: Allocation of send data failed", __func__);
+ return -1;
+ }
+
+ ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt);
+
+ sprintf(tmp_buf, "%s", ZEBRA_PTM_BFD_STOP_CMD);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, "cmd", tmp_buf);
+
+ sprintf(tmp_buf, "%s", "quagga");
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD,
+ tmp_buf);
+
+ sprintf(tmp_buf, "%d", zebra_ptm_pid);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD,
+ tmp_buf);
+
+ s = client->ibuf;
+
+ dst_p.family = stream_getw(s);
+
+ if (dst_p.family == AF_INET)
+ dst_p.prefixlen = IPV4_MAX_BYTELEN;
+ else
+ dst_p.prefixlen = IPV6_MAX_BYTELEN;
+
+ stream_get(&dst_p.u.prefix, s, dst_p.prefixlen);
+ if (dst_p.family == AF_INET)
+ {
+ inet_ntop(AF_INET, &dst_p.u.prefix4, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DST_IP_FIELD, buf);
+ }
+#ifdef HAVE_IPV6
+ else
+ {
+ inet_ntop(AF_INET6, &dst_p.u.prefix6, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DST_IP_FIELD, buf);
+ }
+#endif /* HAVE_IPV6 */
+
+ multi_hop = stream_getc(s);
+ if (multi_hop)
+ {
+ sprintf(tmp_buf, "%d", 1);
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MULTI_HOP_FIELD,
+ tmp_buf);
+
+ src_p.family = stream_getw(s);
+
+ if (src_p.family == AF_INET)
+ src_p.prefixlen = IPV4_MAX_BYTELEN;
+ else
+ src_p.prefixlen = IPV6_MAX_BYTELEN;
+
+ stream_get(&src_p.u.prefix, s, src_p.prefixlen);
+ if (src_p.family == AF_INET)
+ {
+ inet_ntop(AF_INET, &src_p.u.prefix4, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+#ifdef HAVE_IPV6
+ else
+ {
+ inet_ntop(AF_INET6, &src_p.u.prefix6, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+#endif /* HAVE_IPV6 */
+ }
+ else
+ {
+#ifdef HAVE_IPV6
+ if (dst_p.family == AF_INET6)
+ {
+ src_p.family = stream_getw(s);
+
+ if (src_p.family == AF_INET)
+ src_p.prefixlen = IPV4_MAX_BYTELEN;
+ else
+ src_p.prefixlen = IPV6_MAX_BYTELEN;
+
+ stream_get(&src_p.u.prefix, s, src_p.prefixlen);
+ if (src_p.family == AF_INET)
+ {
+ inet_ntop(AF_INET, &src_p.u.prefix4, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+ else
+ {
+ inet_ntop(AF_INET6, &src_p.u.prefix6, buf, sizeof(buf));
+ ptm_lib_append_msg(ptm_hdl, out_ctxt,
+ ZEBRA_PTM_BFD_SRC_IP_FIELD, buf);
+ }
+ }
+#endif /* HAVE_IPV6 */
+
+ len = stream_getc(s);
+ stream_get(if_name, s, len);
+ if_name[len] = '\0';
+
+ ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_IFNAME_FIELD,
+ if_name);
+ }
+ ptm_lib_complete_msg(ptm_hdl, out_ctxt, data, &data_len);
+ zebra_ptm_thread = thread_add_write (zebrad.master, zebra_ptm_sock_write,
+ data, zebra_ptm_sock);
return 0;
}
diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h
index 126c44811..85a0bdced 100644
--- a/zebra/zebra_ptm.h
+++ b/zebra/zebra_ptm.h
@@ -25,10 +25,14 @@
extern const char ZEBRA_PTM_SOCK_NAME[];
#define ZEBRA_PTM_MAX_SOCKBUF 3200 /* 25B *128 ports */
+#define ZEBRA_PTM_SEND_MAX_SOCKBUF 512
extern int ptm_enable;
void zebra_ptm_init (void);
int zebra_ptm_connect (struct thread *t);
void zebra_ptm_write (struct vty *vty);
+int zebra_ptm_bfd_dst_register (struct zserv *client, int sock, u_short length,
+ int command);
+int zebra_ptm_bfd_dst_deregister (struct zserv *client, int sock, u_short length);
#endif
diff --git a/zebra/zebra_ptm_null.c b/zebra/zebra_ptm_null.c
new file mode 100644
index 000000000..a9e233376
--- /dev/null
+++ b/zebra/zebra_ptm_null.c
@@ -0,0 +1,29 @@
+/**
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include <zebra.h>
+#include "prefix.h"
+
+void zebra_interface_bfd_update (struct interface *a, struct prefix *dp,
+ struct prefix *sp)
+{ return; }
+
+void zebra_bfd_peer_replay_req (void)
+{ return; }
diff --git a/zebra/zebra_ptm_redistribute.c b/zebra/zebra_ptm_redistribute.c
new file mode 100644
index 000000000..5d231de85
--- /dev/null
+++ b/zebra/zebra_ptm_redistribute.c
@@ -0,0 +1,122 @@
+/**
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "prefix.h"
+#include "vty.h"
+#include "stream.h"
+#include "zebra/zserv.h"
+
+/* master zebra server structure */
+extern struct zebra_t zebrad;
+
+int
+zsend_interface_bfd_update (int cmd, struct zserv *client,
+ struct interface *ifp, struct prefix *dp,
+ struct prefix *sp)
+{
+ int blen;
+ struct stream *s;
+
+ /* Check this client need interface information. */
+ if (! client->ifinfo)
+ return 0;
+
+ s = client->obuf;
+ stream_reset (s);
+
+ zserv_create_header (s, cmd);
+ if (ifp)
+ stream_putl (s, ifp->ifindex);
+ else
+ stream_putl (s, 0);
+
+ /* BFD destination prefix information. */
+ stream_putc (s, dp->family);
+ blen = prefix_blen (dp);
+ stream_put (s, &dp->u.prefix, blen);
+ stream_putc (s, dp->prefixlen);
+
+ /* BFD source prefix information. */
+ stream_putc (s, sp->family);
+ blen = prefix_blen (sp);
+ stream_put (s, &sp->u.prefix, blen);
+ stream_putc (s, sp->prefixlen);
+
+ /* Write packet size. */
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ client->if_bfd_cnt++;
+ return zebra_server_send_message(client);
+}
+
+void
+zebra_interface_bfd_update (struct interface *ifp, struct prefix *dp,
+ struct prefix *sp)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+ {
+ /* Supporting for OSPF and BGP */
+ if (client->proto != ZEBRA_ROUTE_OSPF && client->proto != ZEBRA_ROUTE_BGP)
+ continue;
+
+ /* Notify to the protocol daemons. */
+ zsend_interface_bfd_update (ZEBRA_INTERFACE_BFD_DEST_DOWN, client, ifp,
+ dp, sp);
+ }
+}
+
+int
+zsend_bfd_peer_replay (int cmd, struct zserv *client)
+{
+ struct stream *s;
+
+ s = client->obuf;
+ stream_reset (s);
+
+ zserv_create_header (s, cmd);
+
+ /* Write packet size. */
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ client->bfd_peer_replay_cnt++;
+ return zebra_server_send_message(client);
+}
+
+void
+zebra_bfd_peer_replay_req (void)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client))
+ {
+ /* Supporting for BGP */
+ if (client->proto != ZEBRA_ROUTE_BGP)
+ continue;
+
+ /* Notify to the protocol daemons. */
+ zsend_bfd_peer_replay (ZEBRA_BFD_DEST_REPLAY, client);
+ }
+}
diff --git a/zebra/zebra_ptm_redistribute.h b/zebra/zebra_ptm_redistribute.h
new file mode 100644
index 000000000..b79d122af
--- /dev/null
+++ b/zebra/zebra_ptm_redistribute.h
@@ -0,0 +1,28 @@
+/**
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+
+#ifndef _ZEBRA_PTM_REDISTRIBUTE_H
+#define _ZEBRA_PTM_REDISTRIBUTE_H
+extern void zebra_interface_bfd_update (struct interface *, struct prefix *,
+ struct prefix *);
+extern void zebra_bfd_peer_replay_req (void);
+#endif /* _ZEBRA_PTM_REDISTRIBUTE_H */
diff --git a/zebra/zserv.c b/zebra/zserv.c
index cd986daee..51a69a961 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -477,36 +477,6 @@ zsend_interface_update (int cmd, struct zserv *client, struct interface *ifp)
return zebra_server_send_message(client);
}
-int
-zsend_interface_bfd_update (int cmd, struct zserv *client,
- struct interface *ifp, struct prefix *p)
-{
- int blen;
- struct stream *s;
-
- /* Check this client need interface information. */
- if (! client->ifinfo)
- return 0;
-
- s = client->obuf;
- stream_reset (s);
-
- zserv_create_header (s, cmd);
- stream_putl (s, ifp->ifindex);
-
- /* BFD destination prefix information. */
- stream_putc (s, p->family);
- blen = prefix_blen (p);
- stream_put (s, &p->u.prefix, blen);
- stream_putc (s, p->prefixlen);
-
- /* Write packet size. */
- stream_putw_at (s, 0, stream_get_endp (s));
-
- client->if_bfd_cnt++;
- return zebra_server_send_message(client);
-}
-
/*
* The zebra server sends the clients a ZEBRA_IPV4_ROUTE_ADD or a
* ZEBRA_IPV6_ROUTE_ADD via zsend_route_multipath in the following
@@ -1905,6 +1875,13 @@ zebra_client_read (struct thread *thread)
case ZEBRA_IMPORT_ROUTE_UNREGISTER:
zserv_rnh_unregister(client, sock, length, RNH_IMPORT_CHECK_TYPE);
break;
+ case ZEBRA_BFD_DEST_UPDATE:
+ case ZEBRA_BFD_DEST_REGISTER:
+ zebra_ptm_bfd_dst_register(client, sock, length, command);
+ break;
+ case ZEBRA_BFD_DEST_DEREGISTER:
+ zebra_ptm_bfd_dst_deregister(client, sock, length);
+ break;
default:
zlog_info ("Zebra received unknown command %d", command);
break;
@@ -2194,6 +2171,8 @@ zebra_show_client_detail (struct vty *vty, struct zserv *client)
client->redist_v6_del_cnt, VTY_NEWLINE);
vty_out (vty, "Connected %-12d%-12d%-12d%s", client->ifadd_cnt, 0,
client->ifdel_cnt, VTY_NEWLINE);
+ vty_out (vty, "BFD peer %-12d%-12d%-12d%s", client->bfd_peer_add_cnt,
+ client->bfd_peer_upd8_cnt, client->bfd_peer_del_cnt, VTY_NEWLINE);
vty_out (vty, "Interface Up Notifications: %d%s", client->ifup_cnt,
VTY_NEWLINE);
vty_out (vty, "Interface Down Notifications: %d%s", client->ifdown_cnt,
diff --git a/zebra/zserv.h b/zebra/zserv.h
index a344cbd0c..db20bad26 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -94,6 +94,10 @@ struct zserv
u_int32_t ifadd_cnt;
u_int32_t ifdel_cnt;
u_int32_t if_bfd_cnt;
+ u_int32_t bfd_peer_add_cnt;
+ u_int32_t bfd_peer_upd8_cnt;
+ u_int32_t bfd_peer_del_cnt;
+ u_int32_t bfd_peer_replay_cnt;
time_t connect_time;
time_t last_read_time;
@@ -148,8 +152,6 @@ extern int zsend_interface_update (int, struct zserv *, struct interface *);
extern int zsend_route_multipath (int, struct zserv *, struct prefix *,
struct rib *);
extern int zsend_router_id_update(struct zserv *, struct prefix *);
-extern int zsend_interface_bfd_update(int, struct zserv *, struct interface *,
- struct prefix *);
extern pid_t pid;
extern void zserv_create_header(struct stream *s, uint16_t cmd);