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