mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 11:45:06 +00:00 
			
		
		
		
	Update LDP to process received pw-status in received order. Update LDP to save pw-status regardless of whether the PW is configured. When the PW is configured, LDP checks for any saved PW pw-status. Signed-off-by: Karen Schoener <karen@voltanet.io>
		
			
				
	
	
		
			701 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			701 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*	$OpenBSD$ */
 | 
						|
 | 
						|
/*
 | 
						|
 * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
 | 
						|
 * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
 | 
						|
 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
 | 
						|
 * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
						|
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
						|
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "ldpd.h"
 | 
						|
#include "ldpe.h"
 | 
						|
#include "lde.h"
 | 
						|
#include "log.h"
 | 
						|
 | 
						|
static void		 l2vpn_pw_fec(struct l2vpn_pw *, struct fec *);
 | 
						|
static __inline int	 l2vpn_compare(const struct l2vpn *, const struct l2vpn *);
 | 
						|
static __inline int	 l2vpn_if_compare(const struct l2vpn_if *, const struct l2vpn_if *);
 | 
						|
static __inline int	 l2vpn_pw_compare(const struct l2vpn_pw *, const struct l2vpn_pw *);
 | 
						|
 | 
						|
RB_GENERATE(l2vpn_head, l2vpn, entry, l2vpn_compare)
 | 
						|
RB_GENERATE(l2vpn_if_head, l2vpn_if, entry, l2vpn_if_compare)
 | 
						|
RB_GENERATE(l2vpn_pw_head, l2vpn_pw, entry, l2vpn_pw_compare)
 | 
						|
 | 
						|
static __inline int
 | 
						|
l2vpn_compare(const struct l2vpn *a, const struct l2vpn *b)
 | 
						|
{
 | 
						|
	return (strcmp(a->name, b->name));
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn *
 | 
						|
l2vpn_new(const char *name)
 | 
						|
{
 | 
						|
	struct l2vpn	*l2vpn;
 | 
						|
 | 
						|
	if ((l2vpn = calloc(1, sizeof(*l2vpn))) == NULL)
 | 
						|
		fatal("l2vpn_new: calloc");
 | 
						|
 | 
						|
	strlcpy(l2vpn->name, name, sizeof(l2vpn->name));
 | 
						|
 | 
						|
	/* set default values */
 | 
						|
	l2vpn->mtu = DEFAULT_L2VPN_MTU;
 | 
						|
	l2vpn->pw_type = DEFAULT_PW_TYPE;
 | 
						|
 | 
						|
	RB_INIT(l2vpn_if_head, &l2vpn->if_tree);
 | 
						|
	RB_INIT(l2vpn_pw_head, &l2vpn->pw_tree);
 | 
						|
	RB_INIT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
 | 
						|
 | 
						|
	return (l2vpn);
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn *
 | 
						|
l2vpn_find(struct ldpd_conf *xconf, const char *name)
 | 
						|
{
 | 
						|
	struct l2vpn	 l2vpn;
 | 
						|
	strlcpy(l2vpn.name, name, sizeof(l2vpn.name));
 | 
						|
	return (RB_FIND(l2vpn_head, &xconf->l2vpn_tree, &l2vpn));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_del(struct l2vpn *l2vpn)
 | 
						|
{
 | 
						|
	struct l2vpn_if		*lif;
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
 | 
						|
	while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
 | 
						|
		lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
 | 
						|
 | 
						|
		RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
 | 
						|
		free(lif);
 | 
						|
	}
 | 
						|
	while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
 | 
						|
		pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
 | 
						|
 | 
						|
		RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
 | 
						|
		free(pw);
 | 
						|
	}
 | 
						|
	while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
 | 
						|
		pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
 | 
						|
 | 
						|
		RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
 | 
						|
		free(pw);
 | 
						|
	}
 | 
						|
 | 
						|
	free(l2vpn);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_init(struct l2vpn *l2vpn)
 | 
						|
{
 | 
						|
	struct l2vpn_pw	*pw;
 | 
						|
 | 
						|
	RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
 | 
						|
		l2vpn_pw_init(pw);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_exit(struct l2vpn *l2vpn)
 | 
						|
{
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
 | 
						|
	RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
 | 
						|
		l2vpn_pw_exit(pw);
 | 
						|
}
 | 
						|
 | 
						|
static __inline int
 | 
						|
l2vpn_if_compare(const struct l2vpn_if *a, const struct l2vpn_if *b)
 | 
						|
{
 | 
						|
	return if_cmp_name_func(a->ifname, b->ifname);
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn_if *
 | 
						|
l2vpn_if_new(struct l2vpn *l2vpn, const char *ifname)
 | 
						|
{
 | 
						|
	struct l2vpn_if	*lif;
 | 
						|
 | 
						|
	if ((lif = calloc(1, sizeof(*lif))) == NULL)
 | 
						|
		fatal("l2vpn_if_new: calloc");
 | 
						|
 | 
						|
	lif->l2vpn = l2vpn;
 | 
						|
	strlcpy(lif->ifname, ifname, sizeof(lif->ifname));
 | 
						|
 | 
						|
	return (lif);
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn_if *
 | 
						|
l2vpn_if_find(struct l2vpn *l2vpn, const char *ifname)
 | 
						|
{
 | 
						|
	struct l2vpn_if	 lif;
 | 
						|
	strlcpy(lif.ifname, ifname, sizeof(lif.ifname));
 | 
						|
	return (RB_FIND(l2vpn_if_head, &l2vpn->if_tree, &lif));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_if_update_info(struct l2vpn_if *lif, struct kif *kif)
 | 
						|
{
 | 
						|
	lif->ifindex = kif->ifindex;
 | 
						|
	lif->operative = kif->operative;
 | 
						|
	memcpy(lif->mac, kif->mac, sizeof(lif->mac));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_if_update(struct l2vpn_if *lif)
 | 
						|
{
 | 
						|
	struct l2vpn	*l2vpn = lif->l2vpn;
 | 
						|
	struct l2vpn_pw	*pw;
 | 
						|
	struct map	 fec;
 | 
						|
	struct nbr	*nbr;
 | 
						|
 | 
						|
	if (lif->operative)
 | 
						|
		return;
 | 
						|
 | 
						|
	RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
 | 
						|
		nbr = nbr_find_ldpid(pw->lsr_id.s_addr);
 | 
						|
		if (nbr == NULL)
 | 
						|
			continue;
 | 
						|
 | 
						|
		memset(&fec, 0, sizeof(fec));
 | 
						|
		fec.type = MAP_TYPE_PWID;
 | 
						|
		fec.fec.pwid.type = l2vpn->pw_type;
 | 
						|
		fec.fec.pwid.group_id = 0;
 | 
						|
		fec.flags |= F_MAP_PW_ID;
 | 
						|
		fec.fec.pwid.pwid = pw->pwid;
 | 
						|
 | 
						|
		send_mac_withdrawal(nbr, &fec, lif->mac);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static __inline int
 | 
						|
l2vpn_pw_compare(const struct l2vpn_pw *a, const struct l2vpn_pw *b)
 | 
						|
{
 | 
						|
	return if_cmp_name_func(a->ifname, b->ifname);
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn_pw *
 | 
						|
l2vpn_pw_new(struct l2vpn *l2vpn, const char *ifname)
 | 
						|
{
 | 
						|
	struct l2vpn_pw	*pw;
 | 
						|
 | 
						|
	if ((pw = calloc(1, sizeof(*pw))) == NULL)
 | 
						|
		fatal("l2vpn_pw_new: calloc");
 | 
						|
 | 
						|
	pw->l2vpn = l2vpn;
 | 
						|
	strlcpy(pw->ifname, ifname, sizeof(pw->ifname));
 | 
						|
 | 
						|
	return (pw);
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn_pw *
 | 
						|
l2vpn_pw_find(struct l2vpn *l2vpn, const char *ifname)
 | 
						|
{
 | 
						|
	struct l2vpn_pw	*pw;
 | 
						|
	struct l2vpn_pw	 s;
 | 
						|
 | 
						|
	strlcpy(s.ifname, ifname, sizeof(s.ifname));
 | 
						|
	pw = RB_FIND(l2vpn_pw_head, &l2vpn->pw_tree, &s);
 | 
						|
	if (pw)
 | 
						|
		return (pw);
 | 
						|
	return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_inactive_tree, &s));
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn_pw *
 | 
						|
l2vpn_pw_find_active(struct l2vpn *l2vpn, const char *ifname)
 | 
						|
{
 | 
						|
	struct l2vpn_pw	 s;
 | 
						|
 | 
						|
	strlcpy(s.ifname, ifname, sizeof(s.ifname));
 | 
						|
	return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_tree, &s));
 | 
						|
}
 | 
						|
 | 
						|
struct l2vpn_pw *
 | 
						|
l2vpn_pw_find_inactive(struct l2vpn *l2vpn, const char *ifname)
 | 
						|
{
 | 
						|
	struct l2vpn_pw	 s;
 | 
						|
 | 
						|
	strlcpy(s.ifname, ifname, sizeof(s.ifname));
 | 
						|
	return (RB_FIND(l2vpn_pw_head, &l2vpn->pw_inactive_tree, &s));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_pw_update_info(struct l2vpn_pw *pw, struct kif *kif)
 | 
						|
{
 | 
						|
	pw->ifindex = kif->ifindex;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_pw_init(struct l2vpn_pw *pw)
 | 
						|
{
 | 
						|
	struct fec	 fec;
 | 
						|
	struct zapi_pw	 zpw;
 | 
						|
 | 
						|
	l2vpn_pw_reset(pw);
 | 
						|
 | 
						|
	pw2zpw(pw, &zpw);
 | 
						|
	lde_imsg_compose_parent(IMSG_KPW_ADD, 0, &zpw, sizeof(zpw));
 | 
						|
 | 
						|
	l2vpn_pw_fec(pw, &fec);
 | 
						|
	lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0,
 | 
						|
	    0, 0, (void *)pw);
 | 
						|
	lde_kernel_update(&fec);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_pw_exit(struct l2vpn_pw *pw)
 | 
						|
{
 | 
						|
	struct fec	 fec;
 | 
						|
	struct zapi_pw	 zpw;
 | 
						|
 | 
						|
	l2vpn_pw_fec(pw, &fec);
 | 
						|
	lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0, 0);
 | 
						|
	lde_kernel_update(&fec);
 | 
						|
 | 
						|
	pw2zpw(pw, &zpw);
 | 
						|
	lde_imsg_compose_parent(IMSG_KPW_DELETE, 0, &zpw, sizeof(zpw));
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
l2vpn_pw_fec(struct l2vpn_pw *pw, struct fec *fec)
 | 
						|
{
 | 
						|
	memset(fec, 0, sizeof(*fec));
 | 
						|
	fec->type = FEC_TYPE_PWID;
 | 
						|
	fec->u.pwid.type = pw->l2vpn->pw_type;
 | 
						|
	fec->u.pwid.pwid = pw->pwid;
 | 
						|
	fec->u.pwid.lsr_id = pw->lsr_id;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_pw_reset(struct l2vpn_pw *pw)
 | 
						|
{
 | 
						|
	pw->remote_group = 0;
 | 
						|
	pw->remote_mtu = 0;
 | 
						|
	pw->local_status = PW_FORWARDING;
 | 
						|
	pw->remote_status = PW_NOT_FORWARDING;
 | 
						|
 | 
						|
	if (pw->flags & F_PW_CWORD_CONF)
 | 
						|
		pw->flags |= F_PW_CWORD;
 | 
						|
	else
 | 
						|
		pw->flags &= ~F_PW_CWORD;
 | 
						|
 | 
						|
	if (pw->flags & F_PW_STATUSTLV_CONF)
 | 
						|
		pw->flags |= F_PW_STATUSTLV;
 | 
						|
	else
 | 
						|
		pw->flags &= ~F_PW_STATUSTLV;
 | 
						|
 | 
						|
	if (pw->flags & F_PW_STATUSTLV_CONF) {
 | 
						|
		struct fec_node         *fn;
 | 
						|
		struct fec fec;
 | 
						|
		l2vpn_pw_fec(pw, &fec);
 | 
						|
		fn = (struct fec_node *)fec_find(&ft, &fec);
 | 
						|
		if (fn)
 | 
						|
			pw->remote_status = fn->pw_remote_status;
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh)
 | 
						|
{
 | 
						|
	/* check for a remote label */
 | 
						|
	if (fnh->remote_label == NO_LABEL) {
 | 
						|
		log_warnx("%s: pseudowire %s: no remote label", __func__,
 | 
						|
			  pw->ifname);
 | 
						|
		pw->reason = F_PW_NO_REMOTE_LABEL;
 | 
						|
		return (0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* MTUs must match */
 | 
						|
	if (pw->l2vpn->mtu != pw->remote_mtu) {
 | 
						|
		log_warnx("%s: pseudowire %s: MTU mismatch detected", __func__,
 | 
						|
			  pw->ifname);
 | 
						|
		pw->reason = F_PW_MTU_MISMATCH;
 | 
						|
		return (0);
 | 
						|
	}
 | 
						|
 | 
						|
	/* check pw status if applicable */
 | 
						|
	if ((pw->flags & F_PW_STATUSTLV) &&
 | 
						|
	    pw->remote_status != PW_FORWARDING) {
 | 
						|
		log_warnx("%s: pseudowire %s: remote end is down", __func__,
 | 
						|
			  pw->ifname);
 | 
						|
		pw->reason = F_PW_REMOTE_NOT_FWD;
 | 
						|
		return (0);
 | 
						|
	}
 | 
						|
 | 
						|
	pw->reason = F_PW_NO_ERR;
 | 
						|
	return (1);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map)
 | 
						|
{
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
	struct status_tlv	 st;
 | 
						|
 | 
						|
	/* NOTE: thanks martini & friends for all this mess */
 | 
						|
 | 
						|
	pw = (struct l2vpn_pw *) fn->data;
 | 
						|
	if (pw == NULL)
 | 
						|
		/*
 | 
						|
		 * pseudowire not configured, return and record
 | 
						|
		 * the mapping later
 | 
						|
		 */
 | 
						|
		return (0);
 | 
						|
 | 
						|
	/* RFC4447 - Section 6.2: control word negotiation */
 | 
						|
	if (fec_find(&ln->sent_map, &fn->fec)) {
 | 
						|
		if ((map->flags & F_MAP_PW_CWORD) &&
 | 
						|
		    !(pw->flags & F_PW_CWORD_CONF)) {
 | 
						|
			/* ignore the received label mapping */
 | 
						|
			return (1);
 | 
						|
		} else if (!(map->flags & F_MAP_PW_CWORD) &&
 | 
						|
		    (pw->flags & F_PW_CWORD_CONF)) {
 | 
						|
			/* append a "Wrong C-bit" status code */
 | 
						|
			st.status_code = S_WRONG_CBIT;
 | 
						|
			st.msg_id = map->msg_id;
 | 
						|
			st.msg_type = htons(MSG_TYPE_LABELMAPPING);
 | 
						|
			lde_send_labelwithdraw(ln, fn, NULL, &st);
 | 
						|
 | 
						|
			pw->flags &= ~F_PW_CWORD;
 | 
						|
			lde_send_labelmapping(ln, fn, 1);
 | 
						|
		}
 | 
						|
	} else if (map->flags & F_MAP_PW_CWORD) {
 | 
						|
		if (pw->flags & F_PW_CWORD_CONF)
 | 
						|
			pw->flags |= F_PW_CWORD;
 | 
						|
		else
 | 
						|
			/* act as if no label mapping had been received */
 | 
						|
			return (1);
 | 
						|
	} else
 | 
						|
		pw->flags &= ~F_PW_CWORD;
 | 
						|
 | 
						|
	/* RFC4447 - Section 5.4.3: pseudowire status negotiation */
 | 
						|
	if (fec_find(&ln->recv_map, &fn->fec) == NULL &&
 | 
						|
	    !(map->flags & F_MAP_PW_STATUS))
 | 
						|
		pw->flags &= ~F_PW_STATUSTLV;
 | 
						|
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_send_pw_status(struct lde_nbr *ln, uint32_t status, struct fec *fec)
 | 
						|
{
 | 
						|
	struct notify_msg	 nm;
 | 
						|
 | 
						|
	memset(&nm, 0, sizeof(nm));
 | 
						|
	nm.status_code = S_PW_STATUS;
 | 
						|
	nm.pw_status = status;
 | 
						|
	nm.flags |= F_NOTIF_PW_STATUS;
 | 
						|
	lde_fec2map(fec, &nm.fec);
 | 
						|
	nm.flags |= F_NOTIF_FEC;
 | 
						|
 | 
						|
	lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm,
 | 
						|
	    sizeof(nm));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_send_pw_status_wcard(struct lde_nbr *ln, uint32_t status,
 | 
						|
    uint16_t pw_type, uint32_t group_id)
 | 
						|
{
 | 
						|
	struct notify_msg	 nm;
 | 
						|
 | 
						|
	memset(&nm, 0, sizeof(nm));
 | 
						|
	nm.status_code = S_PW_STATUS;
 | 
						|
	nm.pw_status = status;
 | 
						|
	nm.flags |= F_NOTIF_PW_STATUS;
 | 
						|
	nm.fec.type = MAP_TYPE_PWID;
 | 
						|
	nm.fec.fec.pwid.type = pw_type;
 | 
						|
	nm.fec.fec.pwid.group_id = group_id;
 | 
						|
	nm.flags |= F_NOTIF_FEC;
 | 
						|
 | 
						|
	lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm,
 | 
						|
	    sizeof(nm));
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
 | 
						|
{
 | 
						|
	struct fec		 fec;
 | 
						|
	struct fec_node		*fn;
 | 
						|
	struct fec_nh		*fnh;
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
 | 
						|
	if (nm->fec.type == MAP_TYPE_TYPED_WCARD ||
 | 
						|
	    !(nm->fec.flags & F_MAP_PW_ID)) {
 | 
						|
		l2vpn_recv_pw_status_wcard(ln, nm);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	lde_map2fec(&nm->fec, ln->id, &fec);
 | 
						|
	fn = (struct fec_node *)fec_find(&ft, &fec);
 | 
						|
	if (fn == NULL)
 | 
						|
		/* unknown fec */
 | 
						|
		return;
 | 
						|
 | 
						|
	fn->pw_remote_status = nm->pw_status;
 | 
						|
 | 
						|
	pw = (struct l2vpn_pw *) fn->data;
 | 
						|
	if (pw == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id, 0, 0, 0);
 | 
						|
	if (fnh == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* remote status didn't change */
 | 
						|
	if (pw->remote_status == nm->pw_status)
 | 
						|
		return;
 | 
						|
	pw->remote_status = nm->pw_status;
 | 
						|
 | 
						|
	if (l2vpn_pw_ok(pw, fnh))
 | 
						|
		lde_send_change_klabel(fn, fnh);
 | 
						|
	else
 | 
						|
		lde_send_delete_klabel(fn, fnh);
 | 
						|
}
 | 
						|
 | 
						|
/* RFC4447 PWid group wildcard */
 | 
						|
void
 | 
						|
l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm)
 | 
						|
{
 | 
						|
	struct fec		*f;
 | 
						|
	struct fec_node		*fn;
 | 
						|
	struct fec_nh		*fnh;
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
	struct map		*wcard = &nm->fec;
 | 
						|
 | 
						|
	RB_FOREACH(f, fec_tree, &ft) {
 | 
						|
		fn = (struct fec_node *)f;
 | 
						|
		if (fn->fec.type != FEC_TYPE_PWID)
 | 
						|
			continue;
 | 
						|
 | 
						|
		pw = (struct l2vpn_pw *) fn->data;
 | 
						|
		if (pw == NULL)
 | 
						|
			continue;
 | 
						|
 | 
						|
		switch (wcard->type) {
 | 
						|
		case MAP_TYPE_TYPED_WCARD:
 | 
						|
			if (wcard->fec.twcard.u.pw_type != PW_TYPE_WILDCARD &&
 | 
						|
			    wcard->fec.twcard.u.pw_type != fn->fec.u.pwid.type)
 | 
						|
				continue;
 | 
						|
			break;
 | 
						|
		case MAP_TYPE_PWID:
 | 
						|
			if (wcard->fec.pwid.type != fn->fec.u.pwid.type)
 | 
						|
				continue;
 | 
						|
			if (wcard->fec.pwid.group_id != pw->remote_group)
 | 
						|
				continue;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		fnh = fec_nh_find(fn, AF_INET, (union ldpd_addr *)&ln->id,
 | 
						|
		    0, 0, 0);
 | 
						|
		if (fnh == NULL)
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* remote status didn't change */
 | 
						|
		if (pw->remote_status == nm->pw_status)
 | 
						|
			continue;
 | 
						|
		pw->remote_status = nm->pw_status;
 | 
						|
 | 
						|
		if (l2vpn_pw_ok(pw, fnh))
 | 
						|
			lde_send_change_klabel(fn, fnh);
 | 
						|
		else
 | 
						|
			lde_send_delete_klabel(fn, fnh);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
l2vpn_pw_status_update(struct zapi_pw_status *zpw)
 | 
						|
{
 | 
						|
	struct l2vpn		*l2vpn;
 | 
						|
	struct l2vpn_pw		*pw = NULL;
 | 
						|
	struct lde_nbr		*ln;
 | 
						|
	struct fec		 fec;
 | 
						|
	uint32_t		 local_status;
 | 
						|
 | 
						|
	RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree) {
 | 
						|
		pw = l2vpn_pw_find(l2vpn, zpw->ifname);
 | 
						|
		if (pw)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	if (!pw) {
 | 
						|
		log_warnx("%s: pseudowire %s not found", __func__, zpw->ifname);
 | 
						|
		return (1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (zpw->status == PW_FORWARDING) {
 | 
						|
		local_status = PW_FORWARDING;
 | 
						|
		pw->reason = F_PW_NO_ERR;
 | 
						|
	} else {
 | 
						|
		local_status = zpw->status;
 | 
						|
		pw->reason = F_PW_LOCAL_NOT_FWD;
 | 
						|
	}
 | 
						|
 | 
						|
	/* local status didn't change */
 | 
						|
	if (pw->local_status == local_status)
 | 
						|
		return (0);
 | 
						|
	pw->local_status = local_status;
 | 
						|
 | 
						|
	/* notify remote peer about the status update */
 | 
						|
	ln = lde_nbr_find_by_lsrid(pw->lsr_id);
 | 
						|
	if (ln == NULL)
 | 
						|
		return (0);
 | 
						|
	l2vpn_pw_fec(pw, &fec);
 | 
						|
	if (pw->flags & F_PW_STATUSTLV)
 | 
						|
		l2vpn_send_pw_status(ln, local_status, &fec);
 | 
						|
	else {
 | 
						|
		struct fec_node *fn;
 | 
						|
		fn = (struct fec_node *)fec_find(&ft, &fec);
 | 
						|
		if (fn) {
 | 
						|
			if (pw->local_status == PW_FORWARDING)
 | 
						|
				lde_send_labelmapping(ln, fn, 1);
 | 
						|
			else
 | 
						|
				lde_send_labelwithdraw(ln, fn, NULL, NULL);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_pw_ctl(pid_t pid)
 | 
						|
{
 | 
						|
	struct l2vpn		*l2vpn;
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
	static struct ctl_pw	 pwctl;
 | 
						|
 | 
						|
	RB_FOREACH(l2vpn, l2vpn_head, &ldeconf->l2vpn_tree)
 | 
						|
		RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree) {
 | 
						|
			memset(&pwctl, 0, sizeof(pwctl));
 | 
						|
			strlcpy(pwctl.l2vpn_name, pw->l2vpn->name,
 | 
						|
			    sizeof(pwctl.l2vpn_name));
 | 
						|
			strlcpy(pwctl.ifname, pw->ifname,
 | 
						|
			    sizeof(pwctl.ifname));
 | 
						|
			pwctl.pwid = pw->pwid;
 | 
						|
			pwctl.lsr_id = pw->lsr_id;
 | 
						|
			pwctl.status = PW_NOT_FORWARDING;
 | 
						|
			if (pw->enabled &&
 | 
						|
			    pw->local_status == PW_FORWARDING &&
 | 
						|
			    pw->remote_status == PW_FORWARDING)
 | 
						|
				pwctl.status = PW_FORWARDING;
 | 
						|
 | 
						|
			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0,
 | 
						|
			    pid, &pwctl, sizeof(pwctl));
 | 
						|
		}
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
l2vpn_binding_ctl(pid_t pid)
 | 
						|
{
 | 
						|
	struct fec		*f;
 | 
						|
	struct fec_node		*fn;
 | 
						|
	struct lde_map		*me;
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
	static struct ctl_pw	 pwctl;
 | 
						|
 | 
						|
	RB_FOREACH(f, fec_tree, &ft) {
 | 
						|
		if (f->type != FEC_TYPE_PWID)
 | 
						|
			continue;
 | 
						|
 | 
						|
		fn = (struct fec_node *)f;
 | 
						|
		if (fn->local_label == NO_LABEL &&
 | 
						|
		    RB_EMPTY(lde_map_head, &fn->downstream))
 | 
						|
			continue;
 | 
						|
 | 
						|
		memset(&pwctl, 0, sizeof(pwctl));
 | 
						|
		pwctl.type = f->u.pwid.type;
 | 
						|
		pwctl.pwid = f->u.pwid.pwid;
 | 
						|
		pwctl.lsr_id = f->u.pwid.lsr_id;
 | 
						|
 | 
						|
		pw = (struct l2vpn_pw *) fn->data;
 | 
						|
		if (pw) {
 | 
						|
			pwctl.local_label = fn->local_label;
 | 
						|
			pwctl.local_gid = 0;
 | 
						|
			pwctl.local_ifmtu = pw->l2vpn->mtu;
 | 
						|
			pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ?
 | 
						|
			    1 : 0;
 | 
						|
			pwctl.reason = pw->reason;
 | 
						|
		} else
 | 
						|
			pwctl.local_label = NO_LABEL;
 | 
						|
 | 
						|
		RB_FOREACH(me, lde_map_head, &fn->downstream)
 | 
						|
			if (f->u.pwid.lsr_id.s_addr == me->nexthop->id.s_addr)
 | 
						|
				break;
 | 
						|
 | 
						|
		if (me) {
 | 
						|
			pwctl.remote_label = me->map.label;
 | 
						|
			pwctl.remote_gid = me->map.fec.pwid.group_id;
 | 
						|
			if (me->map.flags & F_MAP_PW_IFMTU)
 | 
						|
				pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu;
 | 
						|
			if (pw)
 | 
						|
				pwctl.remote_cword = (pw->flags & F_PW_CWORD) ?
 | 
						|
				    1 : 0;
 | 
						|
 | 
						|
			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
 | 
						|
			    0, pid, &pwctl, sizeof(pwctl));
 | 
						|
		} else if (pw) {
 | 
						|
			pwctl.remote_label = NO_LABEL;
 | 
						|
 | 
						|
			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING,
 | 
						|
			    0, pid, &pwctl, sizeof(pwctl));
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* ldpe */
 | 
						|
 | 
						|
void
 | 
						|
ldpe_l2vpn_init(struct l2vpn *l2vpn)
 | 
						|
{
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
 | 
						|
	RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
 | 
						|
		ldpe_l2vpn_pw_init(pw);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ldpe_l2vpn_exit(struct l2vpn *l2vpn)
 | 
						|
{
 | 
						|
	struct l2vpn_pw		*pw;
 | 
						|
 | 
						|
	RB_FOREACH(pw, l2vpn_pw_head, &l2vpn->pw_tree)
 | 
						|
		ldpe_l2vpn_pw_exit(pw);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ldpe_l2vpn_pw_init(struct l2vpn_pw *pw)
 | 
						|
{
 | 
						|
	struct tnbr		*tnbr;
 | 
						|
 | 
						|
	tnbr = tnbr_find(leconf, pw->af, &pw->addr);
 | 
						|
	if (tnbr == NULL) {
 | 
						|
		tnbr = tnbr_new(pw->af, &pw->addr);
 | 
						|
		tnbr_update(tnbr);
 | 
						|
		RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
 | 
						|
	}
 | 
						|
 | 
						|
	tnbr->pw_count++;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ldpe_l2vpn_pw_exit(struct l2vpn_pw *pw)
 | 
						|
{
 | 
						|
	struct tnbr		*tnbr;
 | 
						|
 | 
						|
	tnbr = tnbr_find(leconf, pw->af, &pw->addr);
 | 
						|
	if (tnbr) {
 | 
						|
		tnbr->pw_count--;
 | 
						|
		tnbr_check(leconf, tnbr);
 | 
						|
	}
 | 
						|
}
 |