]> git.puffer.fish Git - matthieu/frr.git/commitdiff
This patch changes BGP from only listening mode for BFD status updates to interactive...
authorDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 12 Jun 2015 14:59:11 +0000 (07:59 -0700)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 12 Jun 2015 14:59:11 +0000 (07:59 -0700)
This patch also adds BFD multihop support for BGP. Whether a peer is multi-hop or single hop is determined internally. All IGP peers are considered as multi-hop peers. EBGP peers are considered as single hop unless configured as multi-hop.

BGP BFD command enhancement to configure BFD parameters (detect multiplier, min rx and min tx).

router bgp <as-number>
  neighbor <name/ip-address> bfd <detect mult> <min rx> <min tx>

Signed-off-by: Radhika Mahankali <radhika@cumulusnetworks.com>
Reviewed-by: Dinesh G Dutt <ddutt@cumulusnetworks.com>
Reviewed-by: Vipin Kumar <vipin@cumulusnetworks.com>
Reviewed-by: Kanna Rajagopal <kanna@cumulusnetworks.com>
33 files changed:
bgpd/Makefile.am
bgpd/bgp_bfd.c [new file with mode: 0644]
bgpd/bgp_bfd.h [new file with mode: 0644]
bgpd/bgp_fsm.c
bgpd/bgp_vty.c
bgpd/bgp_vty.h
bgpd/bgp_zebra.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/Makefile.am
lib/csv.c [new file with mode: 0644]
lib/csv.h [new file with mode: 0644]
lib/log.c
lib/memtypes.c
lib/ptm_lib.c [new file with mode: 0644]
lib/ptm_lib.h [new file with mode: 0644]
lib/zclient.c
lib/zclient.h
lib/zebra.h
ospfd/ospf_zebra.c
zebra/Makefile.am
zebra/interface.c
zebra/interface.h
zebra/redistribute.c
zebra/redistribute.h
zebra/redistribute_null.c
zebra/zebra_ptm.c
zebra/zebra_ptm.h
zebra/zebra_ptm_null.c [new file with mode: 0644]
zebra/zebra_ptm_redistribute.c [new file with mode: 0644]
zebra/zebra_ptm_redistribute.h [new file with mode: 0644]
zebra/zserv.c
zebra/zserv.h

index b005a0caf1d49d714e3b6f44f4c60ada046eed1d..e91e2aa31bbb5f730f7630aca87ea80d6a28d91b 100644 (file)
@@ -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 (file)
index 0000000..3005373
--- /dev/null
@@ -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 (file)
index 0000000..8c72c23
--- /dev/null
@@ -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 */
index 6089cbd94a88d9187e5b8155fa846a7133049300..33c5066cd335c4b56f821fe5289e8b9fca1416da 100644 (file)
@@ -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;
 }
 
index 71c9ba1b9419c6a45af1f4e6030bb6893c2ee192..c142573e8d4992cbae60e42a506368ed2b966085 100644 (file)
@@ -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);
index 5f8f8163f2081961f93c4940ed3e8fc9819447a7..2e54ccce0e85f0f60c0ec96d960736274e40fcfc 100644 (file)
@@ -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 */
index aade248f7c7ddbfd70ba55f106ae3ddd643c2786..23de75ae733916cf0c748eadda4ddd3cfc5ee9ba 100644 (file)
@@ -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;
@@ -313,47 +314,6 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length)
   return 0;
 }
 
-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;
index ba23f18dd1a7abfa151ab1fae8153cc999c33441..16040116ab473ff54c3da421bf0d785727bfb31b 100644 (file)
@@ -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
index e0deec7309cc5f81a742d41ab6ab12c79c8f0717..3b220520ba05b9585ac5e767e7e5f23cc73f6b2b 100644 (file)
@@ -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 *);
index d32ac1f7ab51dcbdc23fbeb08d52ba26e40aca90..1ed1d0a702f77bb16b4f6cd059bc7e28ad55b5ef 100644 (file)
@@ -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 (file)
index 0000000..bcee7bd
--- /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 (file)
index 0000000..078f390
--- /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
index e15f3d50df05f19e479357c32b5805bfdacd9ab5..a4ab9fdead668547d3ca54c71030fa94cf2b370f 100644 (file)
--- 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
 
index c32c08817fab520319b307c588a6ebf7bd4e49f6..57eb31ff34754761ebe38dc748c26ae1ae78f8bc 100644 (file)
@@ -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 (file)
index 0000000..175cf14
--- /dev/null
@@ -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 (file)
index 0000000..1aa2f35
--- /dev/null
@@ -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 *);
index 722075adab67ebf8fc5dc6a65ac1c49fdaf57848..1b03510ed555c3d1a36858f8d7bf15df18d0f70f 100644 (file)
@@ -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;
     }
index bd4d06647dcc1cd2452e6c615cde780787ecae0a..ab8c03e2d10d8b3ea3da7127d1310f333941f791 100644 (file)
@@ -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);
index c1231e4c8b8f623572de07e91b7fa4dee107b231..387cd6541502eadf8348e81dd066ab4ce37d7c15 100644 (file)
@@ -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
index 4a31c9a9a6719933874378a9e1910f4436b2ea0b..38a4228ce6ded48b4f2a619fafbdca925386e741 100644 (file)
@@ -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;
index 470e8a1a282b275ac5a657dfad458353d1643a26..a4e6f2c1861936cc406107d6c9426e46d66083ff 100644 (file)
@@ -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)
 
index 2f8de0b03da3762cfe42c480724f08a7d2bb5e51..cd9c21a61c44b4efbebc3c82a623bb69fd6f5bc6 100644 (file)
@@ -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
index 72e40044fc3dda7604abf0be8b5c0cf02773ea2c..e8456ce59a59149c88150ad89314965f363e75f0 100644 (file)
@@ -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 *);
index 843f5ad4b06429053f8df67c7b01ffd63646cfb5..6dfb455f41ad0c58eed18b9c995d54879687fbb2 100644 (file)
@@ -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)
 {
index aef944ca227044f7796961cdaf3e3f403243f797..a4f321979d30b998113117645937fc25efe92dce 100644 (file)
@@ -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,
index c40938c4d95cf98d22a7cd627f9d2356c1bd2910..04a36ab8b2d45c974b20cd33005d37709307ccc5 100644 (file)
@@ -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)
index 58f2514ad918f00a8da579a881007c6ea88152e5..4681649475a907c87f8bb82c99217007a88f66bc 100644 (file)
 #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;
 }
index 126c44811b1e667d7660746cb9434b2e75abe898..85a0bdced4db137d8266add3ad4d411869ce975f 100644 (file)
 
 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 (file)
index 0000000..a9e2333
--- /dev/null
@@ -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 (file)
index 0000000..5d231de
--- /dev/null
@@ -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 (file)
index 0000000..b79d122
--- /dev/null
@@ -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 */
index cd986daee91514d9bb3d3c0083031deec3ee5f35..51a69a961be978abc567dfa5079bd20b4b1f7880 100644 (file)
@@ -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,
index a344cbd0c63e472ece69e8d5e9b86cb06dc25aee..db20bad2630d16e0a2b4654bb36cfcdbfa35068f 100644 (file)
@@ -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);