mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-04 20:18:54 +00:00
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:
parent
f1aa5d8ac8
commit
c43ed2e48a
@ -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
624
bgpd/bgp_bfd.c
Normal 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
69
bgpd/bgp_bfd.h
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
21
bgpd/bgpd.c
21
bgpd/bgpd.c
@ -64,6 +64,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
#include "bgpd/bgp_snmp.h"
|
||||
#endif /* HAVE_SNMP */
|
||||
#include "bgpd/bgp_updgrp.h"
|
||||
#include "bgpd/bgp_bfd.h"
|
||||
|
||||
|
||||
/* BGP process wide configuration. */
|
||||
@ -940,6 +941,9 @@ peer_free (struct peer *peer)
|
||||
if (peer->conf_if)
|
||||
XFREE (MTYPE_PEER_CONF_IF, peer->conf_if);
|
||||
|
||||
if (peer->bfd_info)
|
||||
bgp_bfd_peer_free(peer);
|
||||
|
||||
memset (peer, 0, sizeof (struct peer));
|
||||
|
||||
XFREE (MTYPE_BGP_PEER, peer);
|
||||
@ -1242,6 +1246,8 @@ peer_create (union sockunion *su, const char *conf_if, struct bgp *bgp,
|
||||
}
|
||||
}
|
||||
|
||||
bgp_bfd_peer_init(peer);
|
||||
|
||||
/* Set up peer's events and timers. */
|
||||
if (! active && peer_active (peer))
|
||||
bgp_timer_set (peer);
|
||||
@ -1830,6 +1836,7 @@ peer_group_get (struct bgp *bgp, const char *name)
|
||||
group->conf->connect = 0;
|
||||
SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP);
|
||||
listnode_add_sort (bgp->group, group);
|
||||
bgp_bfd_peer_init(group->conf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2090,6 +2097,8 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
|
||||
pfilter->usmap.name = NULL;
|
||||
pfilter->usmap.map = NULL;
|
||||
}
|
||||
|
||||
bgp_bfd_peer_group2peer_copy(conf, peer);
|
||||
}
|
||||
|
||||
/* Peer group's remote AS configuration. */
|
||||
@ -2165,6 +2174,9 @@ peer_group_delete (struct peer_group *group)
|
||||
/* Delete from all peer_group list. */
|
||||
listnode_delete (bgp->group, group);
|
||||
|
||||
if (group->conf->bfd_info)
|
||||
bgp_bfd_peer_free(group->conf);
|
||||
|
||||
peer_group_free (group);
|
||||
|
||||
return 0;
|
||||
@ -5493,7 +5505,7 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_ebgp_multihop_configured (struct peer *peer)
|
||||
int is_ebgp_multihop_configured (struct peer *peer)
|
||||
{
|
||||
struct peer_group *group;
|
||||
struct listnode *node, *nnode;
|
||||
@ -6010,7 +6022,9 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
|
||||
if (CHECK_FLAG (peer->flags, PEER_FLAG_BFD))
|
||||
if (! peer_group_active (peer) ||
|
||||
! CHECK_FLAG (g_peer->flags, PEER_FLAG_BFD))
|
||||
vty_out (vty, " neighbor %s bfd%s", addr, VTY_NEWLINE);
|
||||
{
|
||||
bgp_bfd_peer_config_write(vty, peer, addr);
|
||||
}
|
||||
|
||||
/* Password. */
|
||||
if (peer->password)
|
||||
@ -6715,6 +6729,9 @@ bgp_init (void)
|
||||
#ifdef HAVE_SNMP
|
||||
bgp_snmp_init ();
|
||||
#endif /* HAVE_SNMP */
|
||||
|
||||
/* BFD init */
|
||||
bgp_bfd_init();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -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 *);
|
||||
|
@ -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
684
lib/csv.c
Normal 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
172
lib/csv.h
Normal 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
|
@ -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
|
||||
|
||||
|
@ -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
464
lib/ptm_lib.c
Normal 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
49
lib/ptm_lib.h
Normal 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 *);
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
29
zebra/zebra_ptm_null.c
Normal 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; }
|
122
zebra/zebra_ptm_redistribute.c
Normal file
122
zebra/zebra_ptm_redistribute.c
Normal 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);
|
||||
}
|
||||
}
|
28
zebra/zebra_ptm_redistribute.h
Normal file
28
zebra/zebra_ptm_redistribute.h
Normal 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 */
|
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user