mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 18:40:52 +00:00 
			
		
		
		
	 ced291deb7
			
		
	
	
		ced291deb7
		
	
	
	
	
		
			
			Don't be selective about what to observe, always observe all possible aspects of the session that may change on run-time (i.e. bind address, interface and VRF existence). Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
		
			
				
	
	
		
			895 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			895 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * BFD PTM adapter code
 | |
|  * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
 | |
|  *
 | |
|  * FRR 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.
 | |
|  *
 | |
|  * FRR 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 FRR; 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 "lib/libfrr.h"
 | |
| #include "lib/queue.h"
 | |
| #include "lib/stream.h"
 | |
| #include "lib/zclient.h"
 | |
| 
 | |
| #include "lib/bfd.h"
 | |
| 
 | |
| #include "bfd.h"
 | |
| 
 | |
| /*
 | |
|  * Data structures
 | |
|  */
 | |
| struct ptm_client_notification {
 | |
| 	struct bfd_session *pcn_bs;
 | |
| 	struct ptm_client *pcn_pc;
 | |
| 
 | |
| 	TAILQ_ENTRY(ptm_client_notification) pcn_entry;
 | |
| };
 | |
| TAILQ_HEAD(pcnqueue, ptm_client_notification);
 | |
| 
 | |
| struct ptm_client {
 | |
| 	uint32_t pc_pid;
 | |
| 	struct pcnqueue pc_pcnqueue;
 | |
| 
 | |
| 	TAILQ_ENTRY(ptm_client) pc_entry;
 | |
| };
 | |
| TAILQ_HEAD(pcqueue, ptm_client);
 | |
| 
 | |
| static struct pcqueue pcqueue;
 | |
| static struct zclient *zclient;
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Prototypes
 | |
|  */
 | |
| static int _ptm_msg_address(struct stream *msg, int family, const void *addr);
 | |
| 
 | |
| static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
 | |
| static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
 | |
| 			 struct bfd_peer_cfg *bpc, struct ptm_client **pc);
 | |
| 
 | |
| static struct ptm_client *pc_lookup(uint32_t pid);
 | |
| static struct ptm_client *pc_new(uint32_t pid);
 | |
| static void pc_free(struct ptm_client *pc);
 | |
| static void pc_free_all(void);
 | |
| static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
 | |
| 					       struct bfd_session *bs);
 | |
| static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
 | |
| 						  struct bfd_session *bs);
 | |
| static void pcn_free(struct ptm_client_notification *pcn);
 | |
| 
 | |
| 
 | |
| static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id);
 | |
| static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id);
 | |
| static void bfdd_client_register(struct stream *msg);
 | |
| static void bfdd_client_deregister(struct stream *msg);
 | |
| 
 | |
| /*
 | |
|  * Functions
 | |
|  */
 | |
| #ifdef BFD_DEBUG
 | |
| static void debug_printbpc(const char *func, unsigned int line,
 | |
| 			   struct bfd_peer_cfg *bpc);
 | |
| 
 | |
| static void debug_printbpc(const char *func, unsigned int line,
 | |
| 			   struct bfd_peer_cfg *bpc)
 | |
| {
 | |
| 	char addr[3][128];
 | |
| 	char timers[3][128];
 | |
| 	char cbit_str[10];
 | |
| 
 | |
| 	addr[0][0] = addr[1][0] = addr[2][0] = timers[0][0] = timers[1][0] =
 | |
| 		timers[2][0] = 0;
 | |
| 
 | |
| 	snprintf(addr[0], sizeof(addr[0]), "peer:%s", satostr(&bpc->bpc_peer));
 | |
| 	if (bpc->bpc_local.sa_sin.sin_family)
 | |
| 		snprintf(addr[1], sizeof(addr[1]), " local:%s",
 | |
| 			 satostr(&bpc->bpc_local));
 | |
| 
 | |
| 	if (bpc->bpc_has_localif)
 | |
| 		snprintf(addr[2], sizeof(addr[2]), " ifname:%s",
 | |
| 			 bpc->bpc_localif);
 | |
| 
 | |
| 	if (bpc->bpc_has_vrfname)
 | |
| 		snprintf(addr[2], sizeof(addr[2]), " vrf:%s", bpc->bpc_vrfname);
 | |
| 
 | |
| 	if (bpc->bpc_has_recvinterval)
 | |
| 		snprintf(timers[0], sizeof(timers[0]), " rx:%lu",
 | |
| 			 bpc->bpc_recvinterval);
 | |
| 
 | |
| 	if (bpc->bpc_has_txinterval)
 | |
| 		snprintf(timers[1], sizeof(timers[1]), " tx:%lu",
 | |
| 			 bpc->bpc_recvinterval);
 | |
| 
 | |
| 	if (bpc->bpc_has_detectmultiplier)
 | |
| 		snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d",
 | |
| 			 bpc->bpc_detectmultiplier);
 | |
| 
 | |
| 	sprintf(cbit_str, "CB %x", bpc->bpc_cbit);
 | |
| 
 | |
| 	log_debug("%s:%d: %s %s%s%s%s%s%s %s", func, line,
 | |
| 		  bpc->bpc_mhop ? "multi-hop" : "single-hop", addr[0], addr[1],
 | |
| 		  addr[2], timers[0], timers[1], timers[2], cbit_str);
 | |
| }
 | |
| 
 | |
| #define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc))
 | |
| #else
 | |
| #define DEBUG_PRINTBPC(bpc)
 | |
| #endif /* BFD_DEBUG */
 | |
| 
 | |
| static int _ptm_msg_address(struct stream *msg, int family, const void *addr)
 | |
| {
 | |
| 	stream_putc(msg, family);
 | |
| 
 | |
| 	switch (family) {
 | |
| 	case AF_INET:
 | |
| 		stream_put(msg, addr, sizeof(struct in_addr));
 | |
| 		stream_putc(msg, 32);
 | |
| 		break;
 | |
| 
 | |
| 	case AF_INET6:
 | |
| 		stream_put(msg, addr, sizeof(struct in6_addr));
 | |
| 		stream_putc(msg, 128);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		assert(0);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int ptm_bfd_notify(struct bfd_session *bs)
 | |
| {
 | |
| 	struct stream *msg;
 | |
| 
 | |
| 	bs->stats.znotification++;
 | |
| 
 | |
| 	/*
 | |
| 	 * Message format:
 | |
| 	 * - header: command, vrf
 | |
| 	 * - l: interface index
 | |
| 	 * - c: family
 | |
| 	 *   - AF_INET:
 | |
| 	 *     - 4 bytes: ipv4
 | |
| 	 *   - AF_INET6:
 | |
| 	 *     - 16 bytes: ipv6
 | |
| 	 *   - c: prefix length
 | |
| 	 * - l: bfd status
 | |
| 	 * - c: family
 | |
| 	 *   - AF_INET:
 | |
| 	 *     - 4 bytes: ipv4
 | |
| 	 *   - AF_INET6:
 | |
| 	 *     - 16 bytes: ipv6
 | |
| 	 *   - c: prefix length
 | |
| 	 * - c: cbit
 | |
| 	 *
 | |
| 	 * Commands: ZEBRA_BFD_DEST_REPLAY
 | |
| 	 *
 | |
| 	 * q(64), l(32), w(16), c(8)
 | |
| 	 */
 | |
| 	msg = zclient->obuf;
 | |
| 	stream_reset(msg);
 | |
| 
 | |
| 	/* TODO: VRF handling */
 | |
| 	if (bs->vrf)
 | |
| 		zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, bs->vrf->vrf_id);
 | |
| 	else
 | |
| 		zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
 | |
| 
 | |
| 	/* This header will be handled by `zebra_ptm.c`. */
 | |
| 	stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE);
 | |
| 
 | |
| 	/* NOTE: Interface is a shortcut to avoid comparing source address. */
 | |
| 	if (bs->ifp != NULL)
 | |
| 		stream_putl(msg, bs->ifp->ifindex);
 | |
| 	else
 | |
| 		stream_putl(msg, IFINDEX_INTERNAL);
 | |
| 
 | |
| 	/* BFD destination prefix information. */
 | |
| 	_ptm_msg_address(msg, bs->key.family, &bs->key.peer);
 | |
| 
 | |
| 	/* BFD status */
 | |
| 	switch (bs->ses_state) {
 | |
| 	case PTM_BFD_UP:
 | |
| 		stream_putl(msg, BFD_STATUS_UP);
 | |
| 		break;
 | |
| 
 | |
| 	case PTM_BFD_ADM_DOWN:
 | |
| 	case PTM_BFD_DOWN:
 | |
| 	case PTM_BFD_INIT:
 | |
| 		stream_putl(msg, BFD_STATUS_DOWN);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		stream_putl(msg, BFD_STATUS_UNKNOWN);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* BFD source prefix information. */
 | |
| 	_ptm_msg_address(msg, bs->key.family, &bs->key.local);
 | |
| 
 | |
| 	stream_putc(msg, bs->remote_cbit);
 | |
| 
 | |
| 	/* Write packet size. */
 | |
| 	stream_putw_at(msg, 0, stream_get_endp(msg));
 | |
| 
 | |
| 	return zclient_send_message(zclient);
 | |
| }
 | |
| 
 | |
| static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa)
 | |
| {
 | |
| 	uint16_t family;
 | |
| 
 | |
| 	STREAM_GETW(msg, family);
 | |
| 
 | |
| 	switch (family) {
 | |
| 	case AF_INET:
 | |
| 		sa->sa_sin.sin_family = family;
 | |
| 		STREAM_GET(&sa->sa_sin.sin_addr, msg,
 | |
| 			   sizeof(sa->sa_sin.sin_addr));
 | |
| #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
 | |
| 		sa->sa_sin.sin_len = sizeof(sa->sa_sin);
 | |
| #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
 | |
| 		return;
 | |
| 
 | |
| 	case AF_INET6:
 | |
| 		sa->sa_sin6.sin6_family = family;
 | |
| 		STREAM_GET(&sa->sa_sin6.sin6_addr, msg,
 | |
| 			   sizeof(sa->sa_sin6.sin6_addr));
 | |
| #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
 | |
| 		sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
 | |
| #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
 | |
| 		return;
 | |
| 
 | |
| 	default:
 | |
| 		log_warning("ptm-read-address: invalid family: %d", family);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| stream_failure:
 | |
| 	memset(sa, 0, sizeof(*sa));
 | |
| }
 | |
| 
 | |
| static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
 | |
| 			 struct bfd_peer_cfg *bpc, struct ptm_client **pc)
 | |
| {
 | |
| 	uint32_t pid;
 | |
| 	uint8_t ttl __attribute__((unused));
 | |
| 	size_t ifnamelen;
 | |
| 
 | |
| 	/*
 | |
| 	 * Register/Deregister/Update Message format:
 | |
| 	 * - header: Command, VRF
 | |
| 	 * - l: pid
 | |
| 	 * - w: family
 | |
| 	 *   - AF_INET:
 | |
| 	 *     - l: destination ipv4
 | |
| 	 *   - AF_INET6:
 | |
| 	 *     - 16 bytes: destination IPv6
 | |
| 	 * - command != ZEBRA_BFD_DEST_DEREGISTER
 | |
| 	 *   - l: min_rx
 | |
| 	 *   - l: min_tx
 | |
| 	 *   - c: detect multiplier
 | |
| 	 * - c: is_multihop?
 | |
| 	 *   - multihop:
 | |
| 	 *     - w: family
 | |
| 	 *       - AF_INET:
 | |
| 	 *         - l: destination ipv4
 | |
| 	 *       - AF_INET6:
 | |
| 	 *         - 16 bytes: destination IPv6
 | |
| 	 *     - c: ttl
 | |
| 	 *   - no multihop
 | |
| 	 *     - AF_INET6:
 | |
| 	 *       - w: family
 | |
| 	 *       - 16 bytes: ipv6 address
 | |
| 	 *     - c: ifname length
 | |
| 	 *     - X bytes: interface name
 | |
| 	 * - c: bfd_cbit
 | |
| 	 *
 | |
| 	 * q(64), l(32), w(16), c(8)
 | |
| 	 */
 | |
| 
 | |
| 	/* Initialize parameters return values. */
 | |
| 	memset(bpc, 0, sizeof(*bpc));
 | |
| 	*pc = NULL;
 | |
| 
 | |
| 	/* Find or allocate process context data. */
 | |
| 	STREAM_GETL(msg, pid);
 | |
| 
 | |
| 	*pc = pc_new(pid);
 | |
| 	if (*pc == NULL) {
 | |
| 		log_debug("ptm-read: failed to allocate memory");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Register/update peer information. */
 | |
| 	_ptm_msg_read_address(msg, &bpc->bpc_peer);
 | |
| 
 | |
| 	/* Determine IP type from peer destination. */
 | |
| 	bpc->bpc_ipv4 = (bpc->bpc_peer.sa_sin.sin_family == AF_INET);
 | |
| 
 | |
| 	/* Get peer configuration. */
 | |
| 	if (command != ZEBRA_BFD_DEST_DEREGISTER) {
 | |
| 		STREAM_GETL(msg, bpc->bpc_recvinterval);
 | |
| 		bpc->bpc_has_recvinterval =
 | |
| 			(bpc->bpc_recvinterval != BPC_DEF_RECEIVEINTERVAL);
 | |
| 
 | |
| 		STREAM_GETL(msg, bpc->bpc_txinterval);
 | |
| 		bpc->bpc_has_txinterval =
 | |
| 			(bpc->bpc_txinterval != BPC_DEF_TRANSMITINTERVAL);
 | |
| 
 | |
| 		STREAM_GETC(msg, bpc->bpc_detectmultiplier);
 | |
| 		bpc->bpc_has_detectmultiplier =
 | |
| 			(bpc->bpc_detectmultiplier != BPC_DEF_DETECTMULTIPLIER);
 | |
| 	}
 | |
| 
 | |
| 	/* Read (single|multi)hop and its options. */
 | |
| 	STREAM_GETC(msg, bpc->bpc_mhop);
 | |
| 	if (bpc->bpc_mhop) {
 | |
| 		/* Read multihop source address and TTL. */
 | |
| 		_ptm_msg_read_address(msg, &bpc->bpc_local);
 | |
| 		STREAM_GETC(msg, ttl);
 | |
| 	} else {
 | |
| 		/* If target is IPv6, then we must obtain local address. */
 | |
| 		if (bpc->bpc_ipv4 == false)
 | |
| 			_ptm_msg_read_address(msg, &bpc->bpc_local);
 | |
| 
 | |
| 		/*
 | |
| 		 * Read interface name and make sure it fits our data
 | |
| 		 * structure, otherwise fail.
 | |
| 		 */
 | |
| 		STREAM_GETC(msg, ifnamelen);
 | |
| 		if (ifnamelen >= sizeof(bpc->bpc_localif)) {
 | |
| 			log_error("ptm-read: interface name is too big");
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		bpc->bpc_has_localif = ifnamelen > 0;
 | |
| 		if (bpc->bpc_has_localif) {
 | |
| 			STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
 | |
| 			bpc->bpc_localif[ifnamelen] = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	if (vrf_id != VRF_DEFAULT) {
 | |
| 		struct vrf *vrf;
 | |
| 
 | |
| 		vrf = vrf_lookup_by_id(vrf_id);
 | |
| 		if (vrf) {
 | |
| 			bpc->bpc_has_vrfname = true;
 | |
| 			strlcpy(bpc->bpc_vrfname, vrf->name, sizeof(bpc->bpc_vrfname));
 | |
| 		} else {
 | |
| 			log_error("ptm-read: vrf id %u could not be identified", vrf_id);
 | |
| 			return -1;
 | |
| 		}
 | |
| 	} else {
 | |
| 		bpc->bpc_has_vrfname = true;
 | |
| 		strlcpy(bpc->bpc_vrfname, VRF_DEFAULT_NAME, sizeof(bpc->bpc_vrfname));
 | |
| 	}
 | |
| 
 | |
| 	STREAM_GETC(msg, bpc->bpc_cbit);
 | |
| 
 | |
| 	/* Sanity check: peer and local address must match IP types. */
 | |
| 	if (bpc->bpc_local.sa_sin.sin_family != 0
 | |
| 	    && (bpc->bpc_local.sa_sin.sin_family
 | |
| 		!= bpc->bpc_peer.sa_sin.sin_family)) {
 | |
| 		log_warning("ptm-read: peer family doesn't match local type");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| stream_failure:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 	struct ptm_client_notification *pcn;
 | |
| 	struct bfd_session *bs;
 | |
| 	struct bfd_peer_cfg bpc;
 | |
| 
 | |
| 	/* Read the client context and peer data. */
 | |
| 	if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, vrf_id, &bpc, &pc) == -1)
 | |
| 		return;
 | |
| 
 | |
| 	DEBUG_PRINTBPC(&bpc);
 | |
| 
 | |
| 	/* Find or start new BFD session. */
 | |
| 	bs = bs_peer_find(&bpc);
 | |
| 	if (bs == NULL) {
 | |
| 		bs = ptm_bfd_sess_new(&bpc);
 | |
| 		if (bs == NULL) {
 | |
| 			log_debug("ptm-add-dest: failed to create BFD session");
 | |
| 			return;
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* Don't try to change echo/shutdown state. */
 | |
| 		bpc.bpc_echo = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
 | |
| 		bpc.bpc_shutdown =
 | |
| 			BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
 | |
| 	}
 | |
| 
 | |
| 	/* Create client peer notification register. */
 | |
| 	pcn = pcn_new(pc, bs);
 | |
| 	if (pcn == NULL) {
 | |
| 		log_error("ptm-add-dest: failed to registrate notifications");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ptm_bfd_notify(bs);
 | |
| }
 | |
| 
 | |
| static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 	struct ptm_client_notification *pcn;
 | |
| 	struct bfd_session *bs;
 | |
| 	struct bfd_peer_cfg bpc;
 | |
| 
 | |
| 	/* Read the client context and peer data. */
 | |
| 	if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, vrf_id, &bpc, &pc) == -1)
 | |
| 		return;
 | |
| 
 | |
| 	DEBUG_PRINTBPC(&bpc);
 | |
| 
 | |
| 	/* Find or start new BFD session. */
 | |
| 	bs = bs_peer_find(&bpc);
 | |
| 	if (bs == NULL) {
 | |
| 		log_debug("ptm-del-dest: failed to find BFD session");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Unregister client peer notification. */
 | |
| 	pcn = pcn_lookup(pc, bs);
 | |
| 	pcn_free(pcn);
 | |
| 	if (bs->refcount ||
 | |
| 	    BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG))
 | |
| 		return;
 | |
| 	ptm_bfd_sess_del(&bpc);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * header: command, VRF
 | |
|  * l: pid
 | |
|  */
 | |
| static void bfdd_client_register(struct stream *msg)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 	uint32_t pid;
 | |
| 
 | |
| 	/* Find or allocate process context data. */
 | |
| 	STREAM_GETL(msg, pid);
 | |
| 
 | |
| 	pc = pc_new(pid);
 | |
| 	if (pc == NULL) {
 | |
| 		log_error("ptm-add-client: failed to register client: %u", pid);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	return;
 | |
| 
 | |
| stream_failure:
 | |
| 	log_error("ptm-add-client: failed to register client");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * header: command, VRF
 | |
|  * l: pid
 | |
|  */
 | |
| static void bfdd_client_deregister(struct stream *msg)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 	uint32_t pid;
 | |
| 
 | |
| 	/* Find or allocate process context data. */
 | |
| 	STREAM_GETL(msg, pid);
 | |
| 
 | |
| 	pc = pc_lookup(pid);
 | |
| 	if (pc == NULL) {
 | |
| 		log_debug("ptm-del-client: failed to find client: %u", pid);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	pc_free(pc);
 | |
| 
 | |
| 	return;
 | |
| 
 | |
| stream_failure:
 | |
| 	log_error("ptm-del-client: failed to deregister client");
 | |
| }
 | |
| 
 | |
| static int bfdd_replay(ZAPI_CALLBACK_ARGS)
 | |
| {
 | |
| 	struct stream *msg = zclient->ibuf;
 | |
| 	uint32_t rcmd;
 | |
| 
 | |
| 	STREAM_GETL(msg, rcmd);
 | |
| 
 | |
| 	switch (rcmd) {
 | |
| 	case ZEBRA_BFD_DEST_REGISTER:
 | |
| 	case ZEBRA_BFD_DEST_UPDATE:
 | |
| 		bfdd_dest_register(msg, vrf_id);
 | |
| 		break;
 | |
| 	case ZEBRA_BFD_DEST_DEREGISTER:
 | |
| 		bfdd_dest_deregister(msg, vrf_id);
 | |
| 		break;
 | |
| 	case ZEBRA_BFD_CLIENT_REGISTER:
 | |
| 		bfdd_client_register(msg);
 | |
| 		break;
 | |
| 	case ZEBRA_BFD_CLIENT_DEREGISTER:
 | |
| 		bfdd_client_deregister(msg);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		log_debug("ptm-replay: invalid message type %u", rcmd);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| stream_failure:
 | |
| 	log_error("ptm-replay: failed to find command");
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static void bfdd_zebra_connected(struct zclient *zc)
 | |
| {
 | |
| 	struct stream *msg = zc->obuf;
 | |
| 
 | |
| 	/* Clean-up and free ptm clients data memory. */
 | |
| 	pc_free_all();
 | |
| 
 | |
| 	/*
 | |
| 	 * The replay is an empty message just to trigger client daemons
 | |
| 	 * configuration replay.
 | |
| 	 */
 | |
| 	stream_reset(msg);
 | |
| 	zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
 | |
| 	stream_putl(msg, ZEBRA_BFD_DEST_REPLAY);
 | |
| 	stream_putw_at(msg, 0, stream_get_endp(msg));
 | |
| 
 | |
| 	/* Ask for interfaces information. */
 | |
| 	zclient_create_header(msg, ZEBRA_INTERFACE_ADD, VRF_DEFAULT);
 | |
| 
 | |
| 	/* Send requests. */
 | |
| 	zclient_send_message(zclient);
 | |
| }
 | |
| 
 | |
| static void bfdd_sessions_enable_interface(struct interface *ifp)
 | |
| {
 | |
| 	struct bfd_session_observer *bso;
 | |
| 	struct bfd_session *bs;
 | |
| 	struct vrf *vrf;
 | |
| 
 | |
| 	TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
 | |
| 		bs = bso->bso_bs;
 | |
| 		/* Interface name mismatch. */
 | |
| 		if (strcmp(ifp->name, bs->key.ifname))
 | |
| 			continue;
 | |
| 		vrf = vrf_lookup_by_id(ifp->vrf_id);
 | |
| 		if (!vrf)
 | |
| 			continue;
 | |
| 		if (bs->key.vrfname[0] &&
 | |
| 		    strcmp(vrf->name, bs->key.vrfname))
 | |
| 			continue;
 | |
| 		/* Skip enabled sessions. */
 | |
| 		if (bs->sock != -1)
 | |
| 			continue;
 | |
| 
 | |
| 		/* Try to enable it. */
 | |
| 		bfd_session_enable(bs);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void bfdd_sessions_disable_interface(struct interface *ifp)
 | |
| {
 | |
| 	struct bfd_session_observer *bso;
 | |
| 	struct bfd_session *bs;
 | |
| 
 | |
| 	TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
 | |
| 		bs = bso->bso_bs;
 | |
| 		if (strcmp(ifp->name, bs->key.ifname))
 | |
| 			continue;
 | |
| 		/* Skip disabled sessions. */
 | |
| 		if (bs->sock == -1)
 | |
| 			continue;
 | |
| 
 | |
| 		bfd_session_disable(bs);
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void bfdd_sessions_enable_vrf(struct vrf *vrf)
 | |
| {
 | |
| 	struct bfd_session_observer *bso;
 | |
| 	struct bfd_session *bs;
 | |
| 
 | |
| 	/* it may affect configs without interfaces */
 | |
| 	TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
 | |
| 		bs = bso->bso_bs;
 | |
| 		/* update name */
 | |
| 		if (bs->vrf && bs->vrf == vrf) {
 | |
| 			if (!strmatch(bs->key.vrfname, vrf->name))
 | |
| 				bfd_session_update_vrf_name(bs, vrf);
 | |
| 		}
 | |
| 		if (bs->vrf)
 | |
| 			continue;
 | |
| 		if (bs->key.vrfname[0] &&
 | |
| 		    strcmp(vrf->name, bs->key.vrfname))
 | |
| 			continue;
 | |
| 		/* need to update the vrf information on
 | |
| 		 * bs so that callbacks are handled
 | |
| 		 */
 | |
| 		bs->vrf = vrf;
 | |
| 		/* Skip enabled sessions. */
 | |
| 		if (bs->sock != -1)
 | |
| 			continue;
 | |
| 		/* Try to enable it. */
 | |
| 		bfd_session_enable(bs);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void bfdd_sessions_disable_vrf(struct vrf *vrf)
 | |
| {
 | |
| 	struct bfd_session_observer *bso;
 | |
| 	struct bfd_session *bs;
 | |
| 
 | |
| 	TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
 | |
| 		bs = bso->bso_bs;
 | |
| 		if (bs->key.vrfname[0] &&
 | |
| 		    strcmp(vrf->name, bs->key.vrfname))
 | |
| 			continue;
 | |
| 		/* Skip disabled sessions. */
 | |
| 		if (bs->sock == -1)
 | |
| 			continue;
 | |
| 
 | |
| 		bfd_session_disable(bs);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int bfd_ifp_destroy(struct interface *ifp)
 | |
| {
 | |
| 	bfdd_sessions_disable_interface(ifp);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS)
 | |
| {
 | |
| 	struct interface *ifp;
 | |
| 	vrf_id_t nvrfid;
 | |
| 
 | |
| 	ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid);
 | |
| 	if (ifp == NULL)
 | |
| 		return 0;
 | |
| 
 | |
| 	if_update_to_new_vrf(ifp, nvrfid);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void bfdd_sessions_enable_address(struct connected *ifc)
 | |
| {
 | |
| 	struct bfd_session_observer *bso;
 | |
| 	struct bfd_session *bs;
 | |
| 	struct prefix prefix;
 | |
| 
 | |
| 	TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
 | |
| 		/* Skip enabled sessions. */
 | |
| 		bs = bso->bso_bs;
 | |
| 		if (bs->sock != -1)
 | |
| 			continue;
 | |
| 
 | |
| 		/* Check address. */
 | |
| 		prefix = bso->bso_addr;
 | |
| 		prefix.prefixlen = ifc->address->prefixlen;
 | |
| 		if (prefix_cmp(&prefix, ifc->address))
 | |
| 			continue;
 | |
| 
 | |
| 		/* Try to enable it. */
 | |
| 		bfd_session_enable(bs);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
 | |
| {
 | |
| 	struct connected *ifc;
 | |
| 
 | |
| 	ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
 | |
| 	if (ifc == NULL)
 | |
| 		return 0;
 | |
| 
 | |
| 	bfdd_sessions_enable_address(ifc);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int bfd_ifp_create(struct interface *ifp)
 | |
| {
 | |
| 	bfdd_sessions_enable_interface(ifp);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
 | |
| {
 | |
| 	if_zapi_callbacks(bfd_ifp_create, NULL, NULL, bfd_ifp_destroy);
 | |
| 	zclient = zclient_new(master, &zclient_options_default);
 | |
| 	assert(zclient != NULL);
 | |
| 	zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
 | |
| 
 | |
| 	/*
 | |
| 	 * We'll receive all messages through replay, however it will
 | |
| 	 * contain a special field with the real command inside so we
 | |
| 	 * avoid having to create too many handlers.
 | |
| 	 */
 | |
| 	zclient->bfd_dest_replay = bfdd_replay;
 | |
| 
 | |
| 	/* Send replay request on zebra connect. */
 | |
| 	zclient->zebra_connected = bfdd_zebra_connected;
 | |
| 
 | |
| 	/* Learn about interface VRF. */
 | |
| 	zclient->interface_vrf_update = bfdd_interface_vrf_update;
 | |
| 
 | |
| 	/* Learn about new addresses being registered. */
 | |
| 	zclient->interface_address_add = bfdd_interface_address_update;
 | |
| 	zclient->interface_address_delete = bfdd_interface_address_update;
 | |
| }
 | |
| 
 | |
| void bfdd_zclient_register(vrf_id_t vrf_id)
 | |
| {
 | |
| 	if (!zclient || zclient->sock < 0)
 | |
| 		return;
 | |
| 	zclient_send_reg_requests(zclient, vrf_id);
 | |
| }
 | |
| 
 | |
| void bfdd_zclient_unregister(vrf_id_t vrf_id)
 | |
| {
 | |
| 	if (!zclient || zclient->sock < 0)
 | |
| 		return;
 | |
| 	zclient_send_dereg_requests(zclient, vrf_id);
 | |
| }
 | |
| 
 | |
| void bfdd_zclient_stop(void)
 | |
| {
 | |
| 	zclient_stop(zclient);
 | |
| 
 | |
| 	/* Clean-up and free ptm clients data memory. */
 | |
| 	pc_free_all();
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Client handling.
 | |
|  */
 | |
| static struct ptm_client *pc_lookup(uint32_t pid)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 
 | |
| 	TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
 | |
| 		if (pc->pc_pid != pid)
 | |
| 			continue;
 | |
| 
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return pc;
 | |
| }
 | |
| 
 | |
| static struct ptm_client *pc_new(uint32_t pid)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 
 | |
| 	/* Look up first, if not found create the client. */
 | |
| 	pc = pc_lookup(pid);
 | |
| 	if (pc != NULL)
 | |
| 		return pc;
 | |
| 
 | |
| 	/* Allocate the client data and save it. */
 | |
| 	pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
 | |
| 
 | |
| 	pc->pc_pid = pid;
 | |
| 	TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
 | |
| 	return pc;
 | |
| }
 | |
| 
 | |
| static void pc_free(struct ptm_client *pc)
 | |
| {
 | |
| 	struct ptm_client_notification *pcn;
 | |
| 
 | |
| 	if (pc == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	TAILQ_REMOVE(&pcqueue, pc, pc_entry);
 | |
| 
 | |
| 	while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) {
 | |
| 		pcn = TAILQ_FIRST(&pc->pc_pcnqueue);
 | |
| 		pcn_free(pcn);
 | |
| 	}
 | |
| 
 | |
| 	XFREE(MTYPE_BFDD_CONTROL, pc);
 | |
| }
 | |
| 
 | |
| static void pc_free_all(void)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 
 | |
| 	while (!TAILQ_EMPTY(&pcqueue)) {
 | |
| 		pc = TAILQ_FIRST(&pcqueue);
 | |
| 		pc_free(pc);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
 | |
| 					       struct bfd_session *bs)
 | |
| {
 | |
| 	struct ptm_client_notification *pcn;
 | |
| 
 | |
| 	/* Try to find an existing pcn fist. */
 | |
| 	pcn = pcn_lookup(pc, bs);
 | |
| 	if (pcn != NULL)
 | |
| 		return pcn;
 | |
| 
 | |
| 	/* Save the client notification data. */
 | |
| 	pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
 | |
| 
 | |
| 	TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
 | |
| 	pcn->pcn_pc = pc;
 | |
| 	pcn->pcn_bs = bs;
 | |
| 	bs->refcount++;
 | |
| 
 | |
| 	return pcn;
 | |
| }
 | |
| 
 | |
| static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
 | |
| 						  struct bfd_session *bs)
 | |
| {
 | |
| 	struct ptm_client_notification *pcn;
 | |
| 
 | |
| 	TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
 | |
| 		if (pcn->pcn_bs != bs)
 | |
| 			continue;
 | |
| 
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return pcn;
 | |
| }
 | |
| 
 | |
| static void pcn_free(struct ptm_client_notification *pcn)
 | |
| {
 | |
| 	struct ptm_client *pc;
 | |
| 	struct bfd_session *bs;
 | |
| 
 | |
| 	if (pcn == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	/* Handle session de-registration. */
 | |
| 	bs = pcn->pcn_bs;
 | |
| 	pcn->pcn_bs = NULL;
 | |
| 	bs->refcount--;
 | |
| 
 | |
| 	/* Handle ptm_client deregistration. */
 | |
| 	pc = pcn->pcn_pc;
 | |
| 	pcn->pcn_pc = NULL;
 | |
| 	TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
 | |
| 
 | |
| 	XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
 | |
| }
 |