This patch changes BGP from only listening mode for BFD status updates to interactive mode of dynamically registering/deregistering BFD enabled peers with PTM/BFD through zebra. Peer is registered with BFD when it goes into established state and de-registers when it goes out of establish state.

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>
This commit is contained in:
Donald Sharp 2015-06-12 07:59:11 -07:00
parent f1aa5d8ac8
commit c43ed2e48a
33 changed files with 2868 additions and 318 deletions

View 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@

624
bgpd/bgp_bfd.c Normal file
View File

@ -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);
}

69
bgpd/bgp_bfd.h Normal file
View File

@ -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 */

View 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;
}

View 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);

View 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 */

View 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;

View 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

View 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 *);

View 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 \

684
lib/csv.c Normal file
View File

@ -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

172
lib/csv.h Normal file
View File

@ -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

View File

@ -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

View 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 }
};

464
lib/ptm_lib.c Normal file
View File

@ -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);
}
}

49
lib/ptm_lib.h Normal file
View File

@ -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 *);

View 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 (p);
stream_get (&p->u.prefix, s, plen);
p->prefixlen = stream_getc (s);
plen = prefix_blen (dp);
stream_get (&dp->u.prefix, s, plen);
dp->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;
}

View 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);

View 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

View 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;

View 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)

View 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

View 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 *);

View 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)
{

View 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,

View 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)

View File

@ -28,19 +28,42 @@
#include "zebra/zebra_ptm.h"
#include "if.h"
#include "command.h"
#include "stream.h"
#include "ptm_lib.h"
#include "zebra/zebra_ptm_redistribute.h"
#define ZEBRA_PTM_RECONNECT_TIME_INITIAL 1 /* initial reconnect is 1s */
#define ZEBRA_PTM_RECONNECT_TIME_MAX 300
#define PTM_MSG_LEN 4
#define PTM_HEADER_LEN 37
const char *ZEBRA_PTM_GET_STATUS_CMD = "get-status";
const char *ZEBRA_PTM_PORT_STR = "port";
const char *ZEBRA_PTM_CBL_STR = "cbl status";
const char *ZEBRA_PTM_PASS_STR = "pass";
const char *ZEBRA_PTM_FAIL_STR = "fail";
const char *ZEBRA_PTM_BFDSTATUS_STR = "BFD status";
const char *ZEBRA_PTM_BFDDEST_STR = "BFD peer";
const char ZEBRA_PTM_GET_STATUS_CMD[] = "get-status";
const char ZEBRA_PTM_BFD_START_CMD[] = "start-bfd-sess";
const char ZEBRA_PTM_BFD_STOP_CMD[] = "stop-bfd-sess";
const char ZEBRA_PTM_PORT_STR[] = "port";
const char ZEBRA_PTM_CBL_STR[] = "cbl status";
const char ZEBRA_PTM_PASS_STR[] = "pass";
const char ZEBRA_PTM_FAIL_STR[] = "fail";
const char ZEBRA_PTM_BFDSTATUS_STR[] = "state";
const char ZEBRA_PTM_BFDSTATUS_UP_STR[] = "Up";
const char ZEBRA_PTM_BFDSTATUS_DOWN_STR[] = "Down";
const char ZEBRA_PTM_BFDDEST_STR[] = "peer";
const char ZEBRA_PTM_BFDSRC_STR[] = "local";
const char ZEBRA_PTM_INVALID_PORT_NAME[] = "N/A";
const char ZEBRA_PTM_INVALID_SRC_IP[] = "N/A";
const char ZEBRA_PTM_BFD_DST_IP_FIELD[] = "dstIPaddr";
const char ZEBRA_PTM_BFD_SRC_IP_FIELD[] = "srcIPaddr";
const char ZEBRA_PTM_BFD_MIN_RX_FIELD[] = "requiredMinRx";
const char ZEBRA_PTM_BFD_MIN_TX_FIELD[] = "upMinTx";
const char ZEBRA_PTM_BFD_DETECT_MULT_FIELD[] = "detectMult";
const char ZEBRA_PTM_BFD_MULTI_HOP_FIELD[] = "multiHop";
const char ZEBRA_PTM_BFD_CLIENT_FIELD[] = "client";
const char ZEBRA_PTM_BFD_SEQID_FIELD[] = "seqid";
const char ZEBRA_PTM_BFD_IFNAME_FIELD[] = "ifName";
const char ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD[] = "maxHopCnt";
extern struct zebra_t zebrad;
int ptm_enable;
@ -49,29 +72,63 @@ int zebra_ptm_sock = -1;
struct thread *zebra_ptm_thread = NULL;
static int zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL;
int zebra_ptm_pid = 0;
static ptm_lib_handle_t *ptm_hdl;
static void zebra_ptm_finish(void);
static int zebra_ptm_socket_init(void);
int zebra_ptm_sock_read(struct thread *);
int zebra_ptm_sock_write(struct thread *);
static void zebra_ptm_install_commands (void);
static int zebra_ptm_handle_cbl_msg(void *arg, void *in_ctxt);
static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt);
void zebra_bfd_peer_replay_req (void);
const char ZEBRA_PTM_SOCK_NAME[] = "\0/var/run/ptmd.socket";
void
zebra_ptm_init (void)
{
char buf[64];
zebra_ptm_pid = getpid();
zebra_ptm_install_commands();
sprintf(buf, "%s", "quagga");
ptm_hdl = ptm_lib_register(buf, NULL, zebra_ptm_handle_bfd_msg,
zebra_ptm_handle_cbl_msg);
}
int
zebra_ptm_connect (struct thread *t)
{
zebra_ptm_socket_init();
int init = 0;
char *data;
void *out_ctxt;
int len = ZEBRA_PTM_SEND_MAX_SOCKBUF;
if (zebra_ptm_sock == -1) {
zebra_ptm_socket_init();
init = 1;
}
if (zebra_ptm_sock != -1) {
zebra_ptm_thread = thread_add_write (zebrad.master, zebra_ptm_sock_write,
NULL, zebra_ptm_sock);
if (init) {
zebra_bfd_peer_replay_req();
}
if (ptm_enable) {
data = calloc(1, len);
if (!data) {
zlog_debug("%s: Allocation of send data failed", __func__);
return -1;
}
ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt);
ptm_lib_append_msg(ptm_hdl, out_ctxt, "cmd", ZEBRA_PTM_GET_STATUS_CMD);
ptm_lib_complete_msg(ptm_hdl, out_ctxt, data, &len);
zebra_ptm_thread = thread_add_write (zebrad.master, zebra_ptm_sock_write,
data, zebra_ptm_sock);
}
zebra_ptm_reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL;
} else {
zebra_ptm_reconnect_time *= 2;
@ -85,21 +142,6 @@ zebra_ptm_connect (struct thread *t)
return(errno);
}
static void
zebra_ptm_finish (void)
{
if (zebra_ptm_sock != -1)
{
if (zebra_ptm_thread != NULL)
{
thread_cancel(zebra_ptm_thread);
zebra_ptm_thread = NULL;
}
close (zebra_ptm_sock);
zebra_ptm_sock = -1;
}
}
DEFUN (zebra_ptm_enable,
zebra_ptm_enable_cmd,
"ptm-enable",
@ -142,13 +184,12 @@ DEFUN (no_zebra_ptm_enable,
ifp->ptm_enable = 0;
if (if_is_operative (ifp) && send_linkup) {
zlog_debug ("%s: Bringing up interface %s\n", __func__,
zlog_debug ("%s: Bringing up interface %s", __func__,
ifp->name);
if_up (ifp);
}
}
}
zebra_ptm_finish();
return CMD_SUCCESS;
}
@ -185,12 +226,12 @@ zebra_ptm_socket_init (void)
sizeof (addr.sun_family)+sizeof (ZEBRA_PTM_SOCK_NAME)-1);
if (ret < 0)
{
zlog_debug("%s: Unable to connect to socket %s [%s]\n",
zlog_warn("%s: Unable to connect to socket %s [%s]",
__func__, ZEBRA_PTM_SOCK_NAME, safe_strerror(errno));
close (sock);
return -1;
}
zlog_debug ("%s: connection to ptm socket %s succeeded\n",
zlog_debug ("%s: connection to ptm socket %s succeeded",
__func__, ZEBRA_PTM_SOCK_NAME);
zebra_ptm_sock = sock;
return sock;
@ -203,134 +244,163 @@ zebra_ptm_install_commands (void)
install_element (CONFIG_NODE, &no_zebra_ptm_enable_cmd);
}
static char *
zebra_ptm_find_key(const char *key_arg, char *arg, int arglen)
/* BFD session goes down, send message to the protocols. */
void
if_bfd_session_down (struct interface *ifp, struct prefix *dp, struct prefix *sp)
{
char buf[ZEBRA_PTM_MAX_SOCKBUF];
char *data, *hdr, *key, *val;
char *currd, *currh;
char *savd, *savh;
if (IS_ZEBRA_DEBUG_EVENT)
{
char buf[2][INET6_ADDRSTRLEN];
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 (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);
rcvptr = calloc(1, ZEBRA_PTM_MAX_SOCKBUF);
/* 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);
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;
}

View File

@ -25,10 +25,14 @@
extern const char ZEBRA_PTM_SOCK_NAME[];
#define ZEBRA_PTM_MAX_SOCKBUF 3200 /* 25B *128 ports */
#define ZEBRA_PTM_SEND_MAX_SOCKBUF 512
extern int ptm_enable;
void zebra_ptm_init (void);
int zebra_ptm_connect (struct thread *t);
void zebra_ptm_write (struct vty *vty);
int zebra_ptm_bfd_dst_register (struct zserv *client, int sock, u_short length,
int command);
int zebra_ptm_bfd_dst_deregister (struct zserv *client, int sock, u_short length);
#endif

29
zebra/zebra_ptm_null.c Normal file
View File

@ -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; }

View File

@ -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);
}
}

View File

@ -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 */

View 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,

View 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);