/* Kernel routing table updates using netlink over GNU/Linux system. * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro * * 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 this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include /* for sockaddr_un */ #include #include "bfd.h" #include "buffer.h" #include "command.h" #include "if.h" #include "network.h" #include "ptm_lib.h" #include "rib.h" #include "stream.h" #include "lib/version.h" #include "vrf.h" #include "vty.h" #include "lib_errors.h" #include "zebra/debug.h" #include "zebra/interface.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_ptm.h" #include "zebra/zebra_ptm_redistribute.h" #include "zebra/zebra_router.h" #include "zebra_vrf.h" /* * Choose the BFD implementation that we'll use. * * There are two implementations: * - PTM BFD: which uses an external daemon; * - bfdd: FRR's own BFD daemon; */ #if HAVE_BFDD == 0 #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_BFD_START_CMD[] = "start-bfd-sess"; const char ZEBRA_PTM_BFD_STOP_CMD[] = "stop-bfd-sess"; const char ZEBRA_PTM_BFD_CLIENT_REG_CMD[] = "reg-bfd-client"; const char ZEBRA_PTM_BFD_CLIENT_DEREG_CMD[] = "dereg-bfd-client"; const char ZEBRA_PTM_CMD_STR[] = "cmd"; const char ZEBRA_PTM_CMD_STATUS_STR[] = "cmd_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[] = "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_BFDVRF_STR[] = "vrf"; const char ZEBRA_PTM_INVALID_PORT_NAME[] = "N/A"; const char ZEBRA_PTM_INVALID_SRC_IP[] = "N/A"; const char ZEBRA_PTM_INVALID_VRF[] = "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"; const char ZEBRA_PTM_BFD_SEND_EVENT[] = "sendEvent"; const char ZEBRA_PTM_BFD_VRF_NAME_FIELD[] = "vrfName"; const char ZEBRA_PTM_BFD_CBIT_FIELD[] = "bfdcbit"; static ptm_lib_handle_t *ptm_hdl; struct zebra_ptm_cb ptm_cb; static int zebra_ptm_socket_init(void); void zebra_ptm_sock_read(struct thread *thread); static void zebra_ptm_install_commands(void); static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt); void zebra_bfd_peer_replay_req(void); void zebra_ptm_send_status_req(void); void zebra_ptm_reset_status(int ptm_disable); static int zebra_ptm_bfd_client_deregister(struct zserv *client); const char ZEBRA_PTM_SOCK_NAME[] = "\0/var/run/ptmd.socket"; void zebra_ptm_init(void) { char buf[64]; memset(&ptm_cb, 0, sizeof(ptm_cb)); ptm_cb.out_data = calloc(1, ZEBRA_PTM_SEND_MAX_SOCKBUF); if (!ptm_cb.out_data) { zlog_debug("%s: Allocation of send data failed", __func__); return; } ptm_cb.in_data = calloc(1, ZEBRA_PTM_MAX_SOCKBUF); if (!ptm_cb.in_data) { zlog_debug("%s: Allocation of recv data failed", __func__); free(ptm_cb.out_data); return; } ptm_cb.pid = getpid(); zebra_ptm_install_commands(); snprintf(buf, sizeof(buf), "%s", FRR_PTM_NAME); ptm_hdl = ptm_lib_register(buf, NULL, zebra_ptm_handle_msg_cb, zebra_ptm_handle_msg_cb); ptm_cb.wb = buffer_new(0); ptm_cb.reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL; ptm_cb.ptm_sock = -1; hook_register(zserv_client_close, zebra_ptm_bfd_client_deregister); } void zebra_ptm_finish(void) { buffer_flush_all(ptm_cb.wb, ptm_cb.ptm_sock); free(ptm_hdl); if (ptm_cb.out_data) free(ptm_cb.out_data); if (ptm_cb.in_data) free(ptm_cb.in_data); /* Cancel events. */ THREAD_OFF(ptm_cb.t_read); THREAD_OFF(ptm_cb.t_write); THREAD_OFF(ptm_cb.t_timer); if (ptm_cb.wb) buffer_free(ptm_cb.wb); if (ptm_cb.ptm_sock >= 0) close(ptm_cb.ptm_sock); } static void zebra_ptm_flush_messages(struct thread *thread) { ptm_cb.t_write = NULL; if (ptm_cb.ptm_sock == -1) return; errno = 0; switch (buffer_flush_available(ptm_cb.wb, ptm_cb.ptm_sock)) { case BUFFER_ERROR: flog_err_sys(EC_LIB_SOCKET, "%s ptm socket error: %s", __func__, safe_strerror(errno)); close(ptm_cb.ptm_sock); ptm_cb.ptm_sock = -1; zebra_ptm_reset_status(0); ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return; case BUFFER_PENDING: ptm_cb.t_write = NULL; thread_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, ptm_cb.ptm_sock, &ptm_cb.t_write); break; case BUFFER_EMPTY: break; } } static int zebra_ptm_send_message(char *data, int size) { errno = 0; switch (buffer_write(ptm_cb.wb, ptm_cb.ptm_sock, data, size)) { case BUFFER_ERROR: flog_err_sys(EC_LIB_SOCKET, "%s ptm socket error: %s", __func__, safe_strerror(errno)); close(ptm_cb.ptm_sock); ptm_cb.ptm_sock = -1; zebra_ptm_reset_status(0); ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return -1; case BUFFER_EMPTY: THREAD_OFF(ptm_cb.t_write); break; case BUFFER_PENDING: thread_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, ptm_cb.ptm_sock, &ptm_cb.t_write); break; } return 0; } void zebra_ptm_connect(struct thread *t) { int init = 0; if (ptm_cb.ptm_sock == -1) { zebra_ptm_socket_init(); init = 1; } if (ptm_cb.ptm_sock != -1) { if (init) { ptm_cb.t_read = NULL; thread_add_read(zrouter.master, zebra_ptm_sock_read, NULL, ptm_cb.ptm_sock, &ptm_cb.t_read); zebra_bfd_peer_replay_req(); } zebra_ptm_send_status_req(); ptm_cb.reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL; } else if (ptm_cb.reconnect_time < ZEBRA_PTM_RECONNECT_TIME_MAX) { ptm_cb.reconnect_time *= 2; if (ptm_cb.reconnect_time > ZEBRA_PTM_RECONNECT_TIME_MAX) ptm_cb.reconnect_time = ZEBRA_PTM_RECONNECT_TIME_MAX; ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); } else if (ptm_cb.reconnect_time >= ZEBRA_PTM_RECONNECT_TIME_MAX) { ptm_cb.reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL; } } DEFUN (zebra_ptm_enable, zebra_ptm_enable_cmd, "ptm-enable", "Enable neighbor check with specified topology\n") { struct vrf *vrf; struct interface *ifp; struct zebra_if *if_data; ptm_cb.ptm_enable = ZEBRA_IF_PTM_ENABLE_ON; RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) FOR_ALL_INTERFACES (vrf, ifp) if (!ifp->ptm_enable) { if_data = (struct zebra_if *)ifp->info; if (if_data && (if_data->ptm_enable == ZEBRA_IF_PTM_ENABLE_UNSPEC)) { ifp->ptm_enable = ZEBRA_IF_PTM_ENABLE_ON; } /* Assign a default unknown status */ ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; } zebra_ptm_connect(NULL); return CMD_SUCCESS; } DEFUN (no_zebra_ptm_enable, no_zebra_ptm_enable_cmd, "no ptm-enable", NO_STR "Enable neighbor check with specified topology\n") { ptm_cb.ptm_enable = ZEBRA_IF_PTM_ENABLE_OFF; zebra_ptm_reset_status(1); return CMD_SUCCESS; } DEFUN (zebra_ptm_enable_if, zebra_ptm_enable_if_cmd, "ptm-enable", "Enable neighbor check with specified topology\n") { VTY_DECLVAR_CONTEXT(interface, ifp); struct zebra_if *if_data; int old_ptm_enable; int send_linkdown = 0; if_data = ifp->info; if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC; if (ifp->ifindex == IFINDEX_INTERNAL) { return CMD_SUCCESS; } old_ptm_enable = ifp->ptm_enable; ifp->ptm_enable = ptm_cb.ptm_enable; if (if_is_no_ptm_operative(ifp)) send_linkdown = 1; if (!old_ptm_enable && ptm_cb.ptm_enable) { if (!if_is_operative(ifp) && send_linkdown) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Bringing down interface %s", __func__, ifp->name); if_down(ifp); } } return CMD_SUCCESS; } DEFUN (no_zebra_ptm_enable_if, no_zebra_ptm_enable_if_cmd, "no ptm-enable", NO_STR "Enable neighbor check with specified topology\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int send_linkup = 0; struct zebra_if *if_data; if ((ifp->ifindex != IFINDEX_INTERNAL) && (ifp->ptm_enable)) { if (!if_is_operative(ifp)) send_linkup = 1; ifp->ptm_enable = ZEBRA_IF_PTM_ENABLE_OFF; if (if_is_no_ptm_operative(ifp) && send_linkup) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Bringing up interface %s", __func__, ifp->name); if_up(ifp, true); } } if_data = ifp->info; if_data->ptm_enable = ZEBRA_IF_PTM_ENABLE_OFF; return CMD_SUCCESS; } void zebra_ptm_write(struct vty *vty) { if (ptm_cb.ptm_enable) vty_out(vty, "ptm-enable\n"); return; } static int zebra_ptm_socket_init(void) { int ret; int sock; struct sockaddr_un addr; ptm_cb.ptm_sock = -1; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) return -1; if (set_nonblocking(sock) < 0) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Unable to set socket non blocking[%s]", __func__, safe_strerror(errno)); close(sock); return -1; } /* Make server socket. */ memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; memcpy(&addr.sun_path, ZEBRA_PTM_SOCK_NAME, sizeof(ZEBRA_PTM_SOCK_NAME)); ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr.sun_family) + sizeof(ZEBRA_PTM_SOCK_NAME) - 1); if (ret < 0) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Unable to connect to socket %s [%s]", __func__, ZEBRA_PTM_SOCK_NAME, safe_strerror(errno)); close(sock); return -1; } ptm_cb.ptm_sock = sock; return sock; } static void zebra_ptm_install_commands(void) { install_element(CONFIG_NODE, &zebra_ptm_enable_cmd); install_element(CONFIG_NODE, &no_zebra_ptm_enable_cmd); install_element(INTERFACE_NODE, &zebra_ptm_enable_if_cmd); install_element(INTERFACE_NODE, &no_zebra_ptm_enable_if_cmd); } /* BFD session goes down, send message to the protocols. */ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp, struct prefix *sp, int status, vrf_id_t vrf_id) { if (IS_ZEBRA_DEBUG_EVENT) { char buf[2][INET6_ADDRSTRLEN]; if (ifp) { zlog_debug( "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s %s event", inet_ntop(dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN), dp->prefixlen, ifp->name, bfd_get_status_str(status)); } else { struct vrf *vrf = vrf_lookup_by_id(vrf_id); zlog_debug( "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d with src %s/%d and vrf %s(%u) %s event", 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, VRF_LOGNAME(vrf), vrf_id, bfd_get_status_str(status)); } } zebra_interface_bfd_update(ifp, dp, sp, status, vrf_id); } static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, struct interface *ifp) { char bfdst_str[32]; char dest_str[64]; char src_str[64]; char vrf_str[64]; struct prefix dest_prefix; struct prefix src_prefix; vrf_id_t vrf_id; ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDSTATUS_STR, bfdst_str); if (bfdst_str[0] == '\0') { return -1; } ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDDEST_STR, dest_str); if (dest_str[0] == '\0') { zlog_debug("%s: Key %s not found in PTM msg", __func__, ZEBRA_PTM_BFDDEST_STR); return -1; } ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDSRC_STR, src_str); if (src_str[0] == '\0') { zlog_debug("%s: Key %s not found in PTM msg", __func__, ZEBRA_PTM_BFDSRC_STR); return -1; } ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_BFDVRF_STR, vrf_str); if (vrf_str[0] == '\0') { zlog_debug("%s: Key %s not found in PTM msg", __func__, ZEBRA_PTM_BFDVRF_STR); return -1; } if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s: Recv Port [%s] bfd status [%s] vrf [%s] peer [%s] local [%s]", __func__, ifp ? ifp->name : "N/A", bfdst_str, vrf_str, dest_str, src_str); if (str2prefix(dest_str, &dest_prefix) == 0) { flog_err(EC_ZEBRA_PREFIX_PARSE_ERROR, "%s: Peer addr %s not found", __func__, dest_str); return -1; } memset(&src_prefix, 0, sizeof(src_prefix)); if (strcmp(ZEBRA_PTM_INVALID_SRC_IP, src_str)) { if (str2prefix(src_str, &src_prefix) == 0) { flog_err(EC_ZEBRA_PREFIX_PARSE_ERROR, "%s: Local addr %s not found", __func__, src_str); return -1; } } if (!strcmp(ZEBRA_PTM_INVALID_VRF, vrf_str) && ifp) { vrf_id = ifp->vrf->vrf_id; } else { struct vrf *pVrf; pVrf = vrf_lookup_by_name(vrf_str); if (pVrf) vrf_id = pVrf->vrf_id; else vrf_id = VRF_DEFAULT; } if (!strcmp(bfdst_str, ZEBRA_PTM_BFDSTATUS_DOWN_STR)) { if_bfd_session_update(ifp, &dest_prefix, &src_prefix, BFD_STATUS_DOWN, vrf_id); } else { if_bfd_session_update(ifp, &dest_prefix, &src_prefix, BFD_STATUS_UP, vrf_id); } return 0; } static int zebra_ptm_handle_cbl_msg(void *arg, void *in_ctxt, struct interface *ifp, char *cbl_str) { int send_linkup = 0; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Recv Port [%s] cbl status [%s]", __func__, ifp->name, cbl_str); if (!strcmp(cbl_str, ZEBRA_PTM_PASS_STR) && (ifp->ptm_status != ZEBRA_PTM_STATUS_UP)) { if (ifp->ptm_status == ZEBRA_PTM_STATUS_DOWN) send_linkup = 1; ifp->ptm_status = ZEBRA_PTM_STATUS_UP; if (ifp->ptm_enable && if_is_no_ptm_operative(ifp) && send_linkup) if_up(ifp, true); } else if (!strcmp(cbl_str, ZEBRA_PTM_FAIL_STR) && (ifp->ptm_status != ZEBRA_PTM_STATUS_DOWN)) { ifp->ptm_status = ZEBRA_PTM_STATUS_DOWN; if (ifp->ptm_enable && if_is_no_ptm_operative(ifp)) if_down(ifp); } return 0; } /* * zebra_ptm_handle_msg_cb - The purpose of this callback function is to handle * all the command responses and notifications received from PTM. * * Command responses: Upon establishing connection with PTM, Zebra requests * status of all interfaces using 'get-status' command if global ptm-enable * knob is enabled. As a response to the get-status command PTM sends status * of all the interfaces as command responses. All other type of command * responses with cmd_status key word are dropped. The sole purpose of * registering this function as callback for the command responses is to * handle the responses to get-status command. * * Notifications: Cable status and BFD session status changes are sent as * notifications by PTM. So, this function is also the callback function for * processing all the notifications from the PTM. * */ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt) { struct interface *ifp = NULL; char port_str[128]; char cbl_str[32]; char cmd_status_str[32]; ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_CMD_STATUS_STR, cmd_status_str); /* Drop command response messages */ if (cmd_status_str[0] != '\0') { return 0; } ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_PORT_STR, port_str); if (port_str[0] == '\0') { zlog_debug("%s: Key %s not found in PTM msg", __func__, ZEBRA_PTM_PORT_STR); return -1; } if (strcmp(ZEBRA_PTM_INVALID_PORT_NAME, port_str)) { struct vrf *vrf; int count = 0; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { ifp = if_lookup_by_name_vrf(port_str, vrf); if (ifp) { count++; if (!vrf_is_backend_netns()) break; } } if (!ifp) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "%s: %s not found in interface list", __func__, port_str); return -1; } if (count > 1) { flog_warn(EC_ZEBRA_UNKNOWN_INTERFACE, "%s: multiple interface with name %s", __func__, port_str); return -1; } } ptm_lib_find_key_in_msg(in_ctxt, ZEBRA_PTM_CBL_STR, cbl_str); if (cbl_str[0] == '\0') { return zebra_ptm_handle_bfd_msg(arg, in_ctxt, ifp); } else { if (ifp) { return zebra_ptm_handle_cbl_msg(arg, in_ctxt, ifp, cbl_str); } else { return -1; } } } void zebra_ptm_sock_read(struct thread *thread) { int sock; int rc; errno = 0; sock = THREAD_FD(thread); if (sock == -1) return; /* PTM communicates in CSV format */ do { rc = ptm_lib_process_msg(ptm_hdl, sock, ptm_cb.in_data, ZEBRA_PTM_MAX_SOCKBUF, NULL); } while (rc > 0); if (((rc == 0) && !errno) || (errno && (errno != EWOULDBLOCK) && (errno != EAGAIN))) { flog_err_sys(EC_LIB_SOCKET, "%s routing socket error: %s(%d) bytes %d", __func__, safe_strerror(errno), errno, rc); close(ptm_cb.ptm_sock); ptm_cb.ptm_sock = -1; zebra_ptm_reset_status(0); ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } ptm_cb.t_read = NULL; thread_add_read(zrouter.master, zebra_ptm_sock_read, NULL, ptm_cb.ptm_sock, &ptm_cb.t_read); } /* BFD peer/dst register/update */ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) { struct stream *s; struct prefix src_p; struct prefix dst_p; uint8_t multi_hop; uint8_t multi_hop_cnt; uint8_t detect_mul; unsigned int min_rx_timer; unsigned int min_tx_timer; char if_name[INTERFACE_NAMSIZ]; uint8_t len; void *out_ctxt; char buf[INET6_ADDRSTRLEN]; char tmp_buf[64]; int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF; unsigned int pid; uint8_t cbit_set; if (hdr->command == ZEBRA_BFD_DEST_UPDATE) client->bfd_peer_upd8_cnt++; else client->bfd_peer_add_cnt++; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_dst_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_START_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); snprintf(tmp_buf, sizeof(tmp_buf), "%s", zebra_route_string(client->proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); s = msg; STREAM_GETL(s, pid); snprintf(tmp_buf, sizeof(tmp_buf), "%d", pid); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD, tmp_buf); STREAM_GETW(s, dst_p.family); 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); } 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); } STREAM_GETL(s, min_rx_timer); snprintf(tmp_buf, sizeof(tmp_buf), "%d", min_rx_timer); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MIN_RX_FIELD, tmp_buf); STREAM_GETL(s, min_tx_timer); snprintf(tmp_buf, sizeof(tmp_buf), "%d", min_tx_timer); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MIN_TX_FIELD, tmp_buf); STREAM_GETC(s, detect_mul); snprintf(tmp_buf, sizeof(tmp_buf), "%d", detect_mul); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_DETECT_MULT_FIELD, tmp_buf); STREAM_GETC(s, multi_hop); if (multi_hop) { snprintf(tmp_buf, sizeof(tmp_buf), "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MULTI_HOP_FIELD, tmp_buf); STREAM_GETW(s, src_p.family); 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); } STREAM_GETC(s, multi_hop_cnt); snprintf(tmp_buf, sizeof(tmp_buf), "%d", multi_hop_cnt); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MAX_HOP_CNT_FIELD, tmp_buf); if (zvrf_id(zvrf) != VRF_DEFAULT) ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_VRF_NAME_FIELD, zvrf_name(zvrf)); } else { if (dst_p.family == AF_INET6) { STREAM_GETW(s, src_p.family); 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); } } STREAM_GETC(s, len); 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); } STREAM_GETC(s, cbit_set); snprintf(tmp_buf, sizeof(tmp_buf), "%d", cbit_set); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CBIT_FIELD, tmp_buf); snprintf(tmp_buf, sizeof(tmp_buf), "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEND_EVENT, tmp_buf); ptm_lib_complete_msg(ptm_hdl, out_ctxt, ptm_cb.out_data, &data_len); if (IS_ZEBRA_DEBUG_SEND) zlog_debug("%s: Sent message (%d) %s", __func__, data_len, ptm_cb.out_data); zebra_ptm_send_message(ptm_cb.out_data, data_len); return; stream_failure: ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); } /* BFD peer/dst deregister */ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) { struct stream *s; struct prefix src_p; struct prefix dst_p; uint8_t multi_hop; char if_name[INTERFACE_NAMSIZ]; uint8_t len; char buf[INET6_ADDRSTRLEN]; char tmp_buf[64]; int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF; void *out_ctxt; unsigned int pid; client->bfd_peer_del_cnt++; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_dst_deregister msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_STOP_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); snprintf(tmp_buf, sizeof(tmp_buf), "%s", zebra_route_string(client->proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); s = msg; STREAM_GETL(s, pid); snprintf(tmp_buf, sizeof(tmp_buf), "%d", pid); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD, tmp_buf); STREAM_GETW(s, dst_p.family); 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)); 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); STREAM_GETC(s, multi_hop); if (multi_hop) { snprintf(tmp_buf, sizeof(tmp_buf), "%d", 1); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_MULTI_HOP_FIELD, tmp_buf); STREAM_GETW(s, src_p.family); 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)); 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); if (zvrf_id(zvrf) != VRF_DEFAULT) ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_VRF_NAME_FIELD, zvrf_name(zvrf)); } else { if (dst_p.family == AF_INET6) { STREAM_GETW(s, src_p.family); 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); } } STREAM_GETC(s, len); 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, ptm_cb.out_data, &data_len); if (IS_ZEBRA_DEBUG_SEND) zlog_debug("%s: Sent message (%d) %s", __func__, data_len, ptm_cb.out_data); zebra_ptm_send_message(ptm_cb.out_data, data_len); return; stream_failure: ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); } /* BFD client register */ void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) { struct stream *s; unsigned int pid; void *out_ctxt = NULL; char tmp_buf[64]; int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF; client->bfd_client_reg_cnt++; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_client_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); s = msg; STREAM_GETL(s, pid); if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_CLIENT_REG_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); snprintf(tmp_buf, sizeof(tmp_buf), "%s", zebra_route_string(client->proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); snprintf(tmp_buf, sizeof(tmp_buf), "%d", pid); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_SEQID_FIELD, tmp_buf); ptm_lib_complete_msg(ptm_hdl, out_ctxt, ptm_cb.out_data, &data_len); if (IS_ZEBRA_DEBUG_SEND) zlog_debug("%s: Sent message (%d) %s", __func__, data_len, ptm_cb.out_data); zebra_ptm_send_message(ptm_cb.out_data, data_len); SET_FLAG(ptm_cb.client_flags[client->proto], ZEBRA_PTM_BFD_CLIENT_FLAG_REG); return; stream_failure: /* * IF we ever add more STREAM_GETXXX functions after the out_ctxt * is allocated then we need to add this code back in * * if (out_ctxt) * ptm_lib_cleanup_msg(ptm_hdl, out_ctxt); */ return; } /* BFD client deregister */ int zebra_ptm_bfd_client_deregister(struct zserv *client) { uint8_t proto = client->proto; void *out_ctxt; char tmp_buf[64]; int data_len = ZEBRA_PTM_SEND_MAX_SOCKBUF; if (!IS_BFD_ENABLED_PROTOCOL(proto)) return 0; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_client_deregister msg for client %s", zebra_route_string(proto)); if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, ptm_cb.reconnect_time, &ptm_cb.t_timer); return 0; } ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); snprintf(tmp_buf, sizeof(tmp_buf), "%s", ZEBRA_PTM_BFD_CLIENT_DEREG_CMD); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, tmp_buf); snprintf(tmp_buf, sizeof(tmp_buf), "%s", zebra_route_string(proto)); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_BFD_CLIENT_FIELD, tmp_buf); ptm_lib_complete_msg(ptm_hdl, out_ctxt, ptm_cb.out_data, &data_len); if (IS_ZEBRA_DEBUG_SEND) zlog_debug("%s: Sent message (%d) %s", __func__, data_len, ptm_cb.out_data); zebra_ptm_send_message(ptm_cb.out_data, data_len); UNSET_FLAG(ptm_cb.client_flags[proto], ZEBRA_PTM_BFD_CLIENT_FLAG_REG); return 0; } int zebra_ptm_get_enable_state(void) { return ptm_cb.ptm_enable; } /* * zebra_ptm_get_status_str - Convert status to a display string. */ static const char *zebra_ptm_get_status_str(int status) { switch (status) { case ZEBRA_PTM_STATUS_DOWN: return "fail"; case ZEBRA_PTM_STATUS_UP: return "pass"; case ZEBRA_PTM_STATUS_UNKNOWN: default: return "n/a"; } } void zebra_ptm_show_status(struct vty *vty, json_object *json, struct interface *ifp) { const char *status; if (ifp->ptm_enable) status = zebra_ptm_get_status_str(ifp->ptm_status); else status = "disabled"; if (json) json_object_string_add(json, "ptmStatus", status); else vty_out(vty, " PTM status: %s\n", status); } void zebra_ptm_send_status_req(void) { void *out_ctxt; int len = ZEBRA_PTM_SEND_MAX_SOCKBUF; if (ptm_cb.ptm_enable) { ptm_lib_init_msg(ptm_hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, &out_ctxt); ptm_lib_append_msg(ptm_hdl, out_ctxt, ZEBRA_PTM_CMD_STR, ZEBRA_PTM_GET_STATUS_CMD); ptm_lib_complete_msg(ptm_hdl, out_ctxt, ptm_cb.out_data, &len); zebra_ptm_send_message(ptm_cb.out_data, len); } } void zebra_ptm_reset_status(int ptm_disable) { struct vrf *vrf; struct interface *ifp; int send_linkup; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) FOR_ALL_INTERFACES (vrf, ifp) { send_linkup = 0; if (ifp->ptm_enable) { if (!if_is_operative(ifp)) send_linkup = 1; if (ptm_disable) ifp->ptm_enable = ZEBRA_IF_PTM_ENABLE_OFF; ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; if (if_is_operative(ifp) && send_linkup) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s: Bringing up interface %s", __func__, ifp->name); if_up(ifp, true); } } } } void zebra_ptm_if_init(struct zebra_if *zebra_ifp) { zebra_ifp->ptm_enable = ZEBRA_IF_PTM_ENABLE_UNSPEC; } void zebra_ptm_if_set_ptm_state(struct interface *ifp, struct zebra_if *zebra_ifp) { if (zebra_ifp && zebra_ifp->ptm_enable != ZEBRA_IF_PTM_ENABLE_UNSPEC) ifp->ptm_enable = zebra_ifp->ptm_enable; } void zebra_ptm_if_write(struct vty *vty, struct zebra_if *zebra_ifp) { if (zebra_ifp->ptm_enable == ZEBRA_IF_PTM_ENABLE_OFF) vty_out(vty, " no ptm-enable\n"); } #else /* HAVE_BFDD */ /* * Data structures. */ struct ptm_process { struct zserv *pp_zs; pid_t pp_pid; TAILQ_ENTRY(ptm_process) pp_entry; }; TAILQ_HEAD(ppqueue, ptm_process) ppqueue; DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_PTM_BFD_PROCESS, "PTM BFD process registration table."); /* * Prototypes. */ static struct ptm_process *pp_new(pid_t pid, struct zserv *zs); static struct ptm_process *pp_lookup_byzs(struct zserv *zs); static void pp_free(struct ptm_process *pp); static void pp_free_all(void); static void zebra_ptm_send_bfdd(struct stream *msg); static void zebra_ptm_send_clients(struct stream *msg); static int _zebra_ptm_bfd_client_deregister(struct zserv *zs); static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, struct stream *msg, uint32_t command); /* * Process PID registration. */ static struct ptm_process *pp_new(pid_t pid, struct zserv *zs) { struct ptm_process *pp; #ifdef PTM_DEBUG /* Sanity check: more than one client can't have the same PID. */ TAILQ_FOREACH(pp, &ppqueue, pp_entry) { if (pp->pp_pid == pid && pp->pp_zs != zs) zlog_err("%s:%d pid and client pointer doesn't match", __FILE__, __LINE__); } #endif /* PTM_DEBUG */ /* Lookup for duplicates. */ pp = pp_lookup_byzs(zs); if (pp != NULL) return pp; /* Allocate and register new process. */ pp = XCALLOC(MTYPE_ZEBRA_PTM_BFD_PROCESS, sizeof(*pp)); pp->pp_pid = pid; pp->pp_zs = zs; TAILQ_INSERT_HEAD(&ppqueue, pp, pp_entry); return pp; } static struct ptm_process *pp_lookup_byzs(struct zserv *zs) { struct ptm_process *pp; TAILQ_FOREACH(pp, &ppqueue, pp_entry) { if (pp->pp_zs != zs) continue; break; } return pp; } static void pp_free(struct ptm_process *pp) { if (pp == NULL) return; TAILQ_REMOVE(&ppqueue, pp, pp_entry); XFREE(MTYPE_ZEBRA_PTM_BFD_PROCESS, pp); } static void pp_free_all(void) { struct ptm_process *pp; while (!TAILQ_EMPTY(&ppqueue)) { pp = TAILQ_FIRST(&ppqueue); pp_free(pp); } } /* * Use the FRR's internal daemon implementation. */ static void zebra_ptm_send_bfdd(struct stream *msg) { struct listnode *node; struct zserv *client; struct stream *msgc; /* Create copy for replication. */ msgc = stream_dup(msg); /* Send message to all running BFDd daemons. */ for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { if (client->proto != ZEBRA_ROUTE_BFD) continue; zserv_send_message(client, msg); /* Allocate more messages. */ msg = stream_dup(msgc); } stream_free(msgc); stream_free(msg); } static void zebra_ptm_send_clients(struct stream *msg) { struct listnode *node; struct zserv *client; struct stream *msgc; /* Create copy for replication. */ msgc = stream_dup(msg); /* Send message to all running client daemons. */ for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) { if (!IS_BFD_ENABLED_PROTOCOL(client->proto)) continue; zserv_send_message(client, msg); /* Allocate more messages. */ msg = stream_dup(msgc); } stream_free(msgc); stream_free(msg); } static int _zebra_ptm_bfd_client_deregister(struct zserv *zs) { struct stream *msg; struct ptm_process *pp; if (!IS_BFD_ENABLED_PROTOCOL(zs->proto)) return 0; /* Find daemon pid by zebra connection pointer. */ pp = pp_lookup_byzs(zs); if (pp == NULL) { zlog_err("%s:%d failed to find process pid registration", __FILE__, __LINE__); return -1; } /* Generate, send message and free() daemon related data. */ msg = stream_new(ZEBRA_MAX_PACKET_SIZ); if (msg == NULL) { zlog_debug("%s: not enough memory", __func__); return 0; } /* * The message type will be ZEBRA_BFD_DEST_REPLAY so we can use only * one callback at the `bfdd` side, however the real command * number will be included right after the zebra header. */ zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, 0); stream_putl(msg, ZEBRA_BFD_CLIENT_DEREGISTER); /* Put process PID. */ stream_putl(msg, pp->pp_pid); /* Update the data pointers. */ stream_putw_at(msg, 0, stream_get_endp(msg)); zebra_ptm_send_bfdd(msg); pp_free(pp); return 0; } void zebra_ptm_init(void) { /* Initialize the ptm process information list. */ TAILQ_INIT(&ppqueue); /* * Send deregistration messages to BFD daemon when some other * daemon closes. This will help avoid sending daemons * unnecessary notification messages. */ hook_register(zserv_client_close, _zebra_ptm_bfd_client_deregister); } void zebra_ptm_finish(void) { /* Remove the client disconnect hook and free all memory. */ hook_unregister(zserv_client_close, _zebra_ptm_bfd_client_deregister); pp_free_all(); } /* * Message handling. */ static void _zebra_ptm_reroute(struct zserv *zs, struct zebra_vrf *zvrf, struct stream *msg, uint32_t command) { struct stream *msgc; char buf[ZEBRA_MAX_PACKET_SIZ]; pid_t ppid; /* Create BFD header */ msgc = stream_new(ZEBRA_MAX_PACKET_SIZ); zclient_create_header(msgc, ZEBRA_BFD_DEST_REPLAY, zvrf->vrf->vrf_id); stream_putl(msgc, command); if (STREAM_READABLE(msg) > STREAM_WRITEABLE(msgc)) { zlog_warn("Cannot fit extended BFD header plus original message contents into ZAPI packet; dropping message"); goto stream_failure; } /* Copy original message, excluding header, into new message */ stream_get_from(buf, msg, stream_get_getp(msg), STREAM_READABLE(msg)); stream_put(msgc, buf, STREAM_READABLE(msg)); /* Update length field */ stream_putw_at(msgc, 0, STREAM_READABLE(msgc)); zebra_ptm_send_bfdd(msgc); msgc = NULL; /* Registrate process PID for shutdown hook. */ STREAM_GETL(msg, ppid); pp_new(ppid, zs); return; stream_failure: if (msgc) stream_free(msgc); zlog_err("%s:%d failed to registrate client pid", __FILE__, __LINE__); } void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_dst_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REGISTER); } void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_dst_deregister msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_DEREGISTER); } void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_client_register msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_CLIENT_REGISTER); } void zebra_ptm_bfd_dst_replay(ZAPI_HANDLER_ARGS) { struct stream *msgc; size_t zmsglen, zhdrlen; uint32_t cmd; /* * NOTE: * Replay messages with HAVE_BFDD are meant to be replayed to * the client daemons. These messages are composed and * originated from the `bfdd` daemon. */ if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("bfd_dst_update msg from client %s: length=%d", zebra_route_string(client->proto), hdr->length); /* * Client messages must be re-routed, otherwise do the `bfdd` * special treatment. */ if (client->proto != ZEBRA_ROUTE_BFD) { _zebra_ptm_reroute(client, zvrf, msg, ZEBRA_BFD_DEST_REPLAY); return; } /* Figure out if this is an DEST_UPDATE or DEST_REPLAY. */ if (stream_getl2(msg, &cmd) == false) { zlog_err("%s: expected at least 4 bytes (command)", __func__); return; } /* * Don't modify message in the zebra API. In order to do that we * need to allocate a new message stream and copy the message * provided by zebra. */ msgc = stream_new(ZEBRA_MAX_PACKET_SIZ); if (msgc == NULL) { zlog_debug("%s: not enough memory", __func__); return; } /* Calculate our header size plus the message contents. */ if (cmd != ZEBRA_BFD_DEST_REPLAY) { zhdrlen = ZEBRA_HEADER_SIZE; zmsglen = msg->endp - msg->getp; memcpy(msgc->data + zhdrlen, msg->data + msg->getp, zmsglen); zclient_create_header(msgc, cmd, zvrf_id(zvrf)); msgc->getp = 0; msgc->endp = zhdrlen + zmsglen; } else zclient_create_header(msgc, cmd, zvrf_id(zvrf)); /* Update the data pointers. */ stream_putw_at(msgc, 0, stream_get_endp(msgc)); zebra_ptm_send_clients(msgc); } /* * Unused functions. */ void zebra_ptm_if_init(struct zebra_if *zifp __attribute__((__unused__))) { /* NOTHING */ } int zebra_ptm_get_enable_state(void) { return 0; } void zebra_ptm_show_status(struct vty *vty __attribute__((__unused__)), json_object *json __attribute__((__unused__)), struct interface *ifp __attribute__((__unused__))) { /* NOTHING */ } void zebra_ptm_write(struct vty *vty __attribute__((__unused__))) { /* NOTHING */ } void zebra_ptm_if_write(struct vty *vty __attribute__((__unused__)), struct zebra_if *zifp __attribute__((__unused__))) { /* NOTHING */ } void zebra_ptm_if_set_ptm_state(struct interface *i __attribute__((__unused__)), struct zebra_if *zi __attribute__((__unused__))) { /* NOTHING */ } #endif /* HAVE_BFDD */