diff options
| author | Donald Sharp <sharpd@cumulusnetworks.com> | 2017-08-08 07:54:32 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-08-08 07:54:32 -0400 | 
| commit | f66e92bc4856352dc4c1c81fa35b1dd570cd83e5 (patch) | |
| tree | d95a4b505cc238c09ea4d2512f2ad6af92f5cb9c | |
| parent | 78986c05cdb4adf6019d02dc328341943c573b35 (diff) | |
| parent | 1a99ca998dd05c41d4068ace4ca3ef8ac4ba309c (diff) | |
Merge pull request #783 from opensourcerouting/pw-manager-2
Add Pseudowire management in Zebra
| -rw-r--r-- | ldpd/l2vpn.c | 65 | ||||
| -rw-r--r-- | ldpd/lde.c | 60 | ||||
| -rw-r--r-- | ldpd/lde.h | 1 | ||||
| -rw-r--r-- | ldpd/lde_lib.c | 9 | ||||
| -rw-r--r-- | ldpd/ldp.h | 3 | ||||
| -rw-r--r-- | ldpd/ldp_zebra.c | 76 | ||||
| -rw-r--r-- | ldpd/ldpd.c | 41 | ||||
| -rw-r--r-- | ldpd/ldpd.h | 33 | ||||
| -rw-r--r-- | lib/Makefile.am | 1 | ||||
| -rw-r--r-- | lib/command.c | 2 | ||||
| -rw-r--r-- | lib/command.h | 1 | ||||
| -rw-r--r-- | lib/log.c | 5 | ||||
| -rw-r--r-- | lib/pw.h | 53 | ||||
| -rw-r--r-- | lib/vty.c | 2 | ||||
| -rw-r--r-- | lib/zclient.c | 70 | ||||
| -rw-r--r-- | lib/zclient.h | 38 | ||||
| -rw-r--r-- | vtysh/Makefile.am | 1 | ||||
| -rw-r--r-- | vtysh/vtysh.c | 25 | ||||
| -rw-r--r-- | vtysh/vtysh_config.c | 2 | ||||
| -rw-r--r-- | zebra/Makefile.am | 7 | ||||
| -rw-r--r-- | zebra/debug.c | 26 | ||||
| -rw-r--r-- | zebra/debug.h | 4 | ||||
| -rw-r--r-- | zebra/main.c | 1 | ||||
| -rw-r--r-- | zebra/rib.h | 1 | ||||
| -rw-r--r-- | zebra/zebra_mpls.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_mpls_openbsd.c | 100 | ||||
| -rw-r--r-- | zebra/zebra_pw.c | 532 | ||||
| -rw-r--r-- | zebra/zebra_pw.h | 75 | ||||
| -rw-r--r-- | zebra/zebra_pw_null.c | 29 | ||||
| -rw-r--r-- | zebra/zebra_rnh.c | 89 | ||||
| -rw-r--r-- | zebra/zebra_rnh.h | 3 | ||||
| -rw-r--r-- | zebra/zebra_vrf.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_vrf.h | 5 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 7 | ||||
| -rw-r--r-- | zebra/zserv.c | 138 | ||||
| -rw-r--r-- | zebra/zserv.h | 3 | 
36 files changed, 1419 insertions, 95 deletions
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 27948f5a1a..afb9528d80 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -234,6 +234,7 @@ void  l2vpn_pw_init(struct l2vpn_pw *pw)  {  	struct fec	 fec; +	struct zapi_pw	 zpw;  	l2vpn_pw_reset(pw); @@ -241,16 +242,23 @@ l2vpn_pw_init(struct l2vpn_pw *pw)  	lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0,  	    0, (void *)pw);  	lde_kernel_update(&fec); + +	pw2zpw(pw, &zpw); +	lde_imsg_compose_parent(IMSG_KPW_ADD, 0, &zpw, sizeof(zpw));  }  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);  	lde_kernel_update(&fec); + +	pw2zpw(pw, &zpw); +	lde_imsg_compose_parent(IMSG_KPW_DELETE, 0, &zpw, sizeof(zpw));  }  static void @@ -268,7 +276,8 @@ l2vpn_pw_reset(struct l2vpn_pw *pw)  {  	pw->remote_group = 0;  	pw->remote_mtu = 0; -	pw->remote_status = 0; +	pw->local_status = PW_FORWARDING; +	pw->remote_status = PW_NOT_FORWARDING;  	if (pw->flags & F_PW_CWORD_CONF)  		pw->flags |= F_PW_CWORD; @@ -474,6 +483,56 @@ l2vpn_recv_pw_status_wcard(struct lde_nbr *ln, struct notify_msg *nm)  	}  } +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_STATUS_UP) +		local_status = PW_FORWARDING; +	else +		local_status = PW_NOT_FORWARDING; + +	/* 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)  { @@ -490,7 +549,9 @@ l2vpn_pw_ctl(pid_t pid)  			    sizeof(pwctl.ifname));  			pwctl.pwid = pw->pwid;  			pwctl.lsr_id = pw->lsr_id; -			pwctl.status = pw->flags & F_PW_STATUS_UP; +			if (pw->local_status == PW_FORWARDING && +			    pw->remote_status == PW_FORWARDING) +				pwctl.status = 1;  			lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_PW, 0,  			    pid, &pwctl, sizeof(pwctl)); diff --git a/ldpd/lde.c b/ldpd/lde.c index 7540fc1cb4..37d64eaf50 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -491,6 +491,15 @@ lde_dispatch_parent(struct thread *thread)  				}  			}  			break; +		case IMSG_PW_UPDATE: +			if (imsg.hdr.len != IMSG_HEADER_SIZE + +			    sizeof(struct zapi_pw_status)) +				fatalx("PW_UPDATE imsg with wrong len"); + +			if (l2vpn_pw_status_update(imsg.data) != 0) +				log_warnx("%s: error updating PW status", +				    __func__); +			break;  		case IMSG_NETWORK_ADD:  		case IMSG_NETWORK_UPDATE:  			if (imsg.hdr.len != IMSG_HEADER_SIZE + @@ -730,8 +739,8 @@ lde_update_label(struct fec_node *fn)  void  lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh)  { -	struct kroute	kr; -	struct kpw	kpw; +	struct kroute	 kr; +	struct zapi_pw	 zpw;  	struct l2vpn_pw	*pw;  	switch (fn->fec.type) { @@ -769,19 +778,10 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh)  			return;  		pw = (struct l2vpn_pw *) fn->data; -		pw->flags |= F_PW_STATUS_UP; - -		memset(&kpw, 0, sizeof(kpw)); -		kpw.ifindex = pw->ifindex; -		kpw.pw_type = fn->fec.u.pwid.type; -		kpw.af = pw->af; -		kpw.nexthop = pw->addr; -		kpw.local_label = fn->local_label; -		kpw.remote_label = fnh->remote_label; -		kpw.flags = pw->flags; - -		lde_imsg_compose_parent(IMSG_KPWLABEL_CHANGE, 0, &kpw, -		    sizeof(kpw)); +		pw2zpw(pw, &zpw); +		zpw.local_label = fn->local_label; +		zpw.remote_label = fnh->remote_label; +		lde_imsg_compose_parent(IMSG_KPW_SET, 0, &zpw, sizeof(zpw));  		break;  	}  } @@ -790,7 +790,7 @@ void  lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)  {  	struct kroute	 kr; -	struct kpw	 kpw; +	struct zapi_pw	 zpw;  	struct l2vpn_pw	*pw;  	switch (fn->fec.type) { @@ -824,21 +824,10 @@ lde_send_delete_klabel(struct fec_node *fn, struct fec_nh *fnh)  		break;  	case FEC_TYPE_PWID:  		pw = (struct l2vpn_pw *) fn->data; -		if (!(pw->flags & F_PW_STATUS_UP)) -			return; -		pw->flags &= ~F_PW_STATUS_UP; - -		memset(&kpw, 0, sizeof(kpw)); -		kpw.ifindex = pw->ifindex; -		kpw.pw_type = fn->fec.u.pwid.type; -		kpw.af = pw->af; -		kpw.nexthop = pw->addr; -		kpw.local_label = fn->local_label; -		kpw.remote_label = fnh->remote_label; -		kpw.flags = pw->flags; - -		lde_imsg_compose_parent(IMSG_KPWLABEL_DELETE, 0, &kpw, -		    sizeof(kpw)); +		pw2zpw(pw, &zpw); +		zpw.local_label = fn->local_label; +		zpw.remote_label = fnh->remote_label; +		lde_imsg_compose_parent(IMSG_KPW_UNSET, 0, &zpw, sizeof(zpw));  		break;  	}  } @@ -919,8 +908,12 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single)  	 */  	lw = (struct lde_wdraw *)fec_find(&ln->sent_wdraw, &fn->fec);  	if (lw) { -		if (!fec_find(&ln->sent_map_pending, &fn->fec)) +		if (!fec_find(&ln->sent_map_pending, &fn->fec)) { +			debug_evt("%s: FEC %s: scheduling to send label " +			    "mapping later (waiting for pending label release)", +			    __func__, log_fec(&fn->fec));  			lde_map_pending_add(ln, fn); +		}  		return;  	} @@ -966,8 +959,7 @@ lde_send_labelmapping(struct lde_nbr *ln, struct fec_node *fn, int single)  			map.flags |= F_MAP_PW_CWORD;  		if (pw->flags & F_PW_STATUSTLV) {  			map.flags |= F_MAP_PW_STATUS; -			/* VPLS are always up */ -			map.pw_status = PW_FORWARDING; +			map.pw_status = pw->local_status;  		}  		break;  	} diff --git a/ldpd/lde.h b/ldpd/lde.h index 1cce483832..43f1d36481 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -238,6 +238,7 @@ void		 l2vpn_send_pw_status_wcard(struct lde_nbr *, uint32_t,  void		 l2vpn_recv_pw_status(struct lde_nbr *, struct notify_msg *);  void		 l2vpn_recv_pw_status_wcard(struct lde_nbr *,  		    struct notify_msg *); +int 		 l2vpn_pw_status_update(struct zapi_pw_status *);  void		 l2vpn_pw_ctl(pid_t);  void		 l2vpn_binding_ctl(pid_t); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 37a670bc8c..c24a57b56a 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -396,7 +396,7 @@ lde_kernel_update(struct fec *fec)  		lde_gc_start_timer();  	} else {  		fn->local_label = lde_update_label(fn); -		if (fn->local_label != NO_LABEL && RB_EMPTY(&fn->upstream)) +		if (fn->local_label != NO_LABEL)  			/* FEC.1: perform lsr label distribution procedure */  			RB_FOREACH(ln, nbr_tree, &lde_nbrs)  				lde_send_labelmapping(ln, fn, 1); @@ -531,6 +531,8 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln)  				pw->remote_mtu = map->fec.pwid.ifmtu;  			if (map->flags & F_MAP_PW_STATUS)  				pw->remote_status = map->pw_status; +			else +				pw->remote_status = PW_FORWARDING;  			fnh->remote_label = map->label;  			if (l2vpn_pw_ok(pw, fnh))  				lde_send_change_klabel(fn, fnh); @@ -780,6 +782,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln)  			pw = (struct l2vpn_pw *) fn->data;  			if (pw == NULL)  				continue; +			pw->remote_status = PW_NOT_FORWARDING;  			break;  		default:  			break; @@ -808,6 +811,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)  	struct fec_node	*fn;  	struct fec_nh	*fnh;  	struct lde_map	*me; +	struct l2vpn_pw	*pw;  	/* LWd.2: send label release */  	lde_send_labelrelease(ln, NULL, map, map->label); @@ -831,6 +835,9 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln)  			case FEC_TYPE_PWID:  				if (f->u.pwid.lsr_id.s_addr != ln->id.s_addr)  					continue; +				pw = (struct l2vpn_pw *) fn->data; +				if (pw) +					pw->remote_status = PW_NOT_FORWARDING;  				break;  			default:  				break; diff --git a/ldpd/ldp.h b/ldpd/ldp.h index c2b64d20c6..cac3da7c55 100644 --- a/ldpd/ldp.h +++ b/ldpd/ldp.h @@ -285,9 +285,6 @@ struct address_list_tlv {  #define	MAP_TYPE_GENPWID	0x81  #define CONTROL_WORD_FLAG	0x8000 -#define PW_TYPE_ETHERNET_TAGGED	0x0004 -#define PW_TYPE_ETHERNET	0x0005 -#define PW_TYPE_WILDCARD	0x7FFF  #define DEFAULT_PW_TYPE		PW_TYPE_ETHERNET  #define PW_TWCARD_RESERVED_BIT	0x8000 diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 3320238a05..f7d715e81c 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -54,6 +54,8 @@ static int	 ldp_interface_address_delete(int, struct zclient *,  		    zebra_size_t, vrf_id_t);  static int	 ldp_zebra_read_route(int, struct zclient *, zebra_size_t,  		    vrf_id_t); +static int	 ldp_zebra_read_pw_status_update(int, struct zclient *, +		    zebra_size_t, vrf_id_t);  static void	 ldp_zebra_connected(struct zclient *);  static struct zclient	*zclient; @@ -94,6 +96,25 @@ ifc2kaddr(struct interface *ifp, struct connected *ifc, struct kaddr *ka)  	}  } +void +pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) +{ +	memset(zpw, 0, sizeof(*zpw)); +	strlcpy(zpw->ifname, pw->ifname, sizeof(zpw->ifname)); +	zpw->ifindex = pw->ifindex; +	zpw->type = pw->l2vpn->pw_type; +	zpw->af = pw->af; +	zpw->nexthop.ipv6 = pw->addr.v6; +	zpw->local_label = NO_LABEL; +	zpw->remote_label = NO_LABEL; +	if (pw->flags & F_PW_CWORD) +		zpw->flags = F_PSEUDOWIRE_CWORD; +	zpw->data.ldp.lsr_id = pw->lsr_id; +	zpw->data.ldp.pwid = pw->pwid; +	strlcpy(zpw->data.ldp.vpn_name, pw->l2vpn->name, +	    sizeof(zpw->data.ldp.vpn_name)); +} +  static int  zebra_send_mpls_labels(int cmd, struct kroute *kr)  { @@ -154,17 +175,40 @@ kr_delete(struct kroute *kr)  }  int -kmpw_set(struct kpw *kpw) +kmpw_add(struct zapi_pw *zpw)  { -	/* TODO */ -	return (0); +	debug_zebra_out("pseudowire %s nexthop %s (add)", +	    zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + +	return (zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw));  }  int -kmpw_unset(struct kpw *kpw) +kmpw_del(struct zapi_pw *zpw)  { -	/* TODO */ -	return (0); +	debug_zebra_out("pseudowire %s nexthop %s (del)", +	    zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + +	return (zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw)); +} + +int +kmpw_set(struct zapi_pw *zpw) +{ +	debug_zebra_out("pseudowire %s nexthop %s labels %u/%u (set)", +	    zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop), +	    zpw->local_label, zpw->remote_label); + +	return (zebra_send_pw(zclient, ZEBRA_PW_SET, zpw)); +} + +int +kmpw_unset(struct zapi_pw *zpw) +{ +	debug_zebra_out("pseudowire %s nexthop %s (unset)", +	    zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); + +	return (zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw));  }  void @@ -466,6 +510,25 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,  	return (0);  } +/* + * Receive PW status update from Zebra and send it to LDE process. + */ +static int +ldp_zebra_read_pw_status_update(int command, struct zclient *zclient, +    zebra_size_t length, vrf_id_t vrf_id) +{ +	struct zapi_pw_status	 zpw; + +	zebra_read_pw_status_update(command, zclient, length, vrf_id, &zpw); + +	debug_zebra_in("pseudowire %s status %s", zpw.ifname, +	    (zpw.status == PW_STATUS_UP) ? "up" : "down"); + +	main_imsg_compose_lde(IMSG_PW_UPDATE, 0, &zpw, sizeof(zpw)); + +	return (0); +} +  static void  ldp_zebra_connected(struct zclient *zclient)  { @@ -496,6 +559,7 @@ ldp_zebra_init(struct thread_master *master)  	zclient->redistribute_route_ipv4_del = ldp_zebra_read_route;  	zclient->redistribute_route_ipv6_add = ldp_zebra_read_route;  	zclient->redistribute_route_ipv6_del = ldp_zebra_read_route; +	zclient->pw_status_update = ldp_zebra_read_pw_status_update;  }  void diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index f9e44012ef..303baf463b 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -591,21 +591,36 @@ main_dispatch_lde(struct thread *thread)  			if (kr_delete(imsg.data))  				log_warnx("%s: error deleting route", __func__);  			break; -		case IMSG_KPWLABEL_CHANGE: +		case IMSG_KPW_ADD: +		case IMSG_KPW_DELETE: +		case IMSG_KPW_SET: +		case IMSG_KPW_UNSET:  			if (imsg.hdr.len - IMSG_HEADER_SIZE != -			    sizeof(struct kpw)) +			    sizeof(struct zapi_pw))  				fatalx("invalid size of IMSG_KPWLABEL_CHANGE"); -			if (kmpw_set(imsg.data)) -				log_warnx("%s: error changing pseudowire", -				    __func__); -			break; -		case IMSG_KPWLABEL_DELETE: -			if (imsg.hdr.len - IMSG_HEADER_SIZE != -			    sizeof(struct kpw)) -				fatalx("invalid size of IMSG_KPWLABEL_DELETE"); -			if (kmpw_unset(imsg.data)) -				log_warnx("%s: error unsetting pseudowire", -				    __func__); + +			switch (imsg.hdr.type) { +			case IMSG_KPW_ADD: +				if (kmpw_add(imsg.data)) +					log_warnx("%s: error adding " +					    "pseudowire", __func__); +				break; +			case IMSG_KPW_DELETE: +				if (kmpw_del(imsg.data)) +					log_warnx("%s: error deleting " +					    "pseudowire", __func__); +				break; +			case IMSG_KPW_SET: +				if (kmpw_set(imsg.data)) +					log_warnx("%s: error setting " +					    "pseudowire", __func__); +				break; +			case IMSG_KPW_UNSET: +				if (kmpw_unset(imsg.data)) +					log_warnx("%s: error unsetting " +					    "pseudowire", __func__); +				break; +			}  			break;  		case IMSG_ACL_CHECK:  			if (imsg.hdr.len != IMSG_HEADER_SIZE + diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 97239ed08e..fd7d5c5729 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -29,6 +29,8 @@  #include "qobj.h"  #include "prefix.h"  #include "filter.h" +#include "pw.h" +#include "zclient.h"  #include "ldp.h" @@ -43,7 +45,6 @@  #define LDPD_OPT_NOACTION	0x00000004  #define TCP_MD5_KEY_LEN		80 -#define L2VPN_NAME_LEN		32  #define	RT_BUF_SIZE		16384  #define	MAX_RTSOCK_BUF		128 * 1024 @@ -101,8 +102,10 @@ enum imsg_type {  	IMSG_CTL_LOG_VERBOSE,  	IMSG_KLABEL_CHANGE,  	IMSG_KLABEL_DELETE, -	IMSG_KPWLABEL_CHANGE, -	IMSG_KPWLABEL_DELETE, +	IMSG_KPW_ADD, +	IMSG_KPW_DELETE, +	IMSG_KPW_SET, +	IMSG_KPW_UNSET,  	IMSG_IFSTATUS,  	IMSG_NEWADDR,  	IMSG_DELADDR, @@ -148,7 +151,8 @@ enum imsg_type {  	IMSG_ACL_CHECK,  	IMSG_GET_LABEL_CHUNK,  	IMSG_RELEASE_LABEL_CHUNK, -	IMSG_INIT +	IMSG_INIT, +	IMSG_PW_UPDATE  };  struct ldpd_init { @@ -408,6 +412,7 @@ struct l2vpn_pw {  	unsigned int		 ifindex;  	uint32_t		 remote_group;  	uint16_t		 remote_mtu; +	uint32_t		 local_status;  	uint32_t		 remote_status;  	uint8_t			 flags;  	QOBJ_FIELDS @@ -419,8 +424,7 @@ DECLARE_QOBJ_TYPE(l2vpn_pw)  #define F_PW_STATUSTLV		0x02	/* status tlv negotiated */  #define F_PW_CWORD_CONF		0x04	/* control word configured */  #define F_PW_CWORD		0x08	/* control word negotiated */ -#define F_PW_STATUS_UP		0x10	/* pseudowire is operational */ -#define F_PW_STATIC_NBR_ADDR	0x20	/* static neighbor address configured */ +#define F_PW_STATIC_NBR_ADDR	0x10	/* static neighbor address configured */  struct l2vpn {  	RB_ENTRY(l2vpn)		 entry; @@ -544,16 +548,6 @@ struct kroute {  	uint16_t		 flags;  }; -struct kpw { -	unsigned short		 ifindex; -	int			 pw_type; -	int			 af; -	union ldpd_addr		 nexthop; -	uint32_t		 local_label; -	uint32_t		 remote_label; -	uint8_t			 flags; -}; -  struct kaddr {  	char			 ifname[IF_NAMESIZE];  	unsigned short		 ifindex; @@ -670,11 +664,14 @@ struct ldpd_conf	*parse_config(char *);  int			 cmdline_symset(char *);  /* kroute.c */ +void		 pw2zpw(struct l2vpn_pw *, struct zapi_pw *);  void		 kif_redistribute(const char *);  int		 kr_change(struct kroute *);  int		 kr_delete(struct kroute *); -int		 kmpw_set(struct kpw *); -int		 kmpw_unset(struct kpw *); +int		 kmpw_add(struct zapi_pw *); +int		 kmpw_del(struct zapi_pw *); +int		 kmpw_set(struct zapi_pw *); +int		 kmpw_unset(struct zapi_pw *);  /* util.c */  uint8_t		 mask2prefixlen(in_addr_t); diff --git a/lib/Makefile.am b/lib/Makefile.am index 14b7130c8a..1f31806973 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -73,6 +73,7 @@ pkginclude_HEADERS = \  	module.h \  	hook.h \  	libfrr.h \ +	pw.h \  	# end  noinst_HEADERS = \ diff --git a/lib/command.c b/lib/command.c index 4912461adb..c2ee79035a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1392,6 +1392,7 @@ cmd_exit (struct vty *vty)        vty_config_unlock (vty);        break;      case INTERFACE_NODE: +    case PW_NODE:      case NS_NODE:      case VRF_NODE:      case ZEBRA_NODE: @@ -1471,6 +1472,7 @@ DEFUN (config_end,        break;      case CONFIG_NODE:      case INTERFACE_NODE: +    case PW_NODE:      case NS_NODE:      case VRF_NODE:      case ZEBRA_NODE: diff --git a/lib/command.h b/lib/command.h index 223f028144..313d73f7c8 100644 --- a/lib/command.h +++ b/lib/command.h @@ -130,6 +130,7 @@ enum node_type    FORWARDING_NODE,              /* IP forwarding node. */    PROTOCOL_NODE,                /* protocol filtering node */    MPLS_NODE,                    /* MPLS config node */ +  PW_NODE,                      /* Pseudowire config node */    VTY_NODE,                     /* Vty node. */    LINK_PARAMS_NODE,             /* Link-parameters node */  }; @@ -937,6 +937,11 @@ static const struct zebra_desc_table command_types[] = {    DESC_ENTRY    (ZEBRA_LABEL_MANAGER_CONNECT),    DESC_ENTRY    (ZEBRA_GET_LABEL_CHUNK),    DESC_ENTRY    (ZEBRA_RELEASE_LABEL_CHUNK), +  DESC_ENTRY    (ZEBRA_PW_ADD), +  DESC_ENTRY    (ZEBRA_PW_DELETE), +  DESC_ENTRY    (ZEBRA_PW_SET), +  DESC_ENTRY    (ZEBRA_PW_UNSET), +  DESC_ENTRY    (ZEBRA_PW_STATUS_UPDATE),  };  #undef DESC_ENTRY diff --git a/lib/pw.h b/lib/pw.h new file mode 100644 index 0000000000..63d8e52ddd --- /dev/null +++ b/lib/pw.h @@ -0,0 +1,53 @@ +/* Pseudowire definitions + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 + */ + +#ifndef _FRR_PW_H +#define _FRR_PW_H + +/* L2VPN name length. */ +#define L2VPN_NAME_LEN		32 + +/* Pseudowire type - LDP and BGP use the same values. */ +#define PW_TYPE_ETHERNET_TAGGED	0x0004	/* RFC 4446 */ +#define PW_TYPE_ETHERNET	0x0005	/* RFC 4446 */ +#define PW_TYPE_WILDCARD	0x7FFF	/* RFC 4863, RFC 6668 */ + +/* Pseudowire flags. */ +#define F_PSEUDOWIRE_CWORD	0x01 + +/* Pseudowire status. */ +#define PW_STATUS_DOWN		0 +#define PW_STATUS_UP		1 + +/* + * Protocol-specific information about the pseudowire. + */ +union pw_protocol_fields +{ +  struct { +      struct in_addr lsr_id; +      uint32_t pwid; +      char vpn_name[L2VPN_NAME_LEN]; +  } ldp; +  struct { +      /* TODO */ +  } bgp; +}; + +#endif /* _FRR_PW_H */ @@ -732,6 +732,7 @@ vty_end_config (struct vty *vty)        break;      case CONFIG_NODE:      case INTERFACE_NODE: +    case PW_NODE:      case ZEBRA_NODE:      case RIP_NODE:      case RIPNG_NODE: @@ -1157,6 +1158,7 @@ vty_stop_input (struct vty *vty)        break;      case CONFIG_NODE:      case INTERFACE_NODE: +    case PW_NODE:      case ZEBRA_NODE:      case RIP_NODE:      case RIPNG_NODE: diff --git a/lib/zclient.c b/lib/zclient.c index 6aea4bd0a3..9338f9c986 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1715,6 +1715,72 @@ lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end)    return 0;  } +int +zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw) +{ +  struct stream *s; + +  /* Reset stream. */ +  s = zclient->obuf; +  stream_reset(s); + +  zclient_create_header(s, command, VRF_DEFAULT); +  stream_write(s, pw->ifname, IF_NAMESIZE); +  stream_putl(s, pw->ifindex); + +  /* Put type */ +  stream_putl(s, pw->type); + +  /* Put nexthop */ +  stream_putl(s, pw->af); +  switch (pw->af) +    { +    case AF_INET: +      stream_put_in_addr(s, &pw->nexthop.ipv4); +      break; +    case AF_INET6: +      stream_write(s, (u_char *)&pw->nexthop.ipv6, 16); +      break; +    default: +      zlog_err ("%s: unknown af", __func__); +      return -1; +    } + +  /* Put labels */ +  stream_putl(s, pw->local_label); +  stream_putl(s, pw->remote_label); + +  /* Put flags */ +  stream_putc(s, pw->flags); + +  /* Protocol specific fields */ +  stream_write(s, &pw->data, sizeof(union pw_protocol_fields)); + +  /* Put length at the first point of the stream. */ +  stream_putw_at(s, 0, stream_get_endp(s)); + +  return zclient_send_message(zclient); +} + +/* + * Receive PW status update from Zebra and send it to LDE process. + */ +void +zebra_read_pw_status_update(int command, struct zclient *zclient, +                            zebra_size_t length, vrf_id_t vrf_id, +                            struct zapi_pw_status *pw) +{ +  struct stream	*s; + +  memset(pw, 0, sizeof(struct zapi_pw_status)); +  s = zclient->ibuf; + +  /* Get data. */ +  stream_get(pw->ifname, s, IF_NAMESIZE); +  pw->ifindex = stream_getl(s); +  pw->status = stream_getl(s); +} +  /* Zebra client message read function. */  static int  zclient_read (struct thread *thread) @@ -1899,6 +1965,10 @@ zclient_read (struct thread *thread)        if (zclient->interface_link_params)          (*zclient->interface_link_params) (command, zclient, length);        break; +    case ZEBRA_PW_STATUS_UPDATE: +      if (zclient->pw_status_update) +        (*zclient->pw_status_update) (command, zclient, length, vrf_id); +      break;      default:        break;      } diff --git a/lib/zclient.h b/lib/zclient.h index d3d0a202c5..489ed24c86 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -31,6 +31,12 @@  /* For vrf_bitmap_t. */  #include "vrf.h" +/* For union g_addr */ +#include "nexthop.h" + +/* For union pw_protocol_fields */ +#include "pw.h" +  /* For input/output buffer to zebra. */  #define ZEBRA_MAX_PACKET_SIZ          4096 @@ -94,6 +100,11 @@ typedef enum {    ZEBRA_LABEL_MANAGER_CONNECT,    ZEBRA_GET_LABEL_CHUNK,    ZEBRA_RELEASE_LABEL_CHUNK, +  ZEBRA_PW_ADD, +  ZEBRA_PW_DELETE, +  ZEBRA_PW_SET, +  ZEBRA_PW_UNSET, +  ZEBRA_PW_STATUS_UPDATE,  } zebra_message_types_t;  struct redist_proto @@ -164,6 +175,7 @@ struct zclient    int (*redistribute_route_ipv4_del) (int, struct zclient *, uint16_t, vrf_id_t);    int (*redistribute_route_ipv6_add) (int, struct zclient *, uint16_t, vrf_id_t);    int (*redistribute_route_ipv6_del) (int, struct zclient *, uint16_t, vrf_id_t); +  int (*pw_status_update) (int, struct zclient *, uint16_t, vrf_id_t);  };  /* Zebra API message flag. */ @@ -217,6 +229,27 @@ struct zapi_ipv4    vrf_id_t vrf_id;  }; +struct zapi_pw +{ +  char ifname[IF_NAMESIZE]; +  ifindex_t ifindex; +  int type; +  int af; +  union g_addr nexthop; +  uint32_t local_label; +  uint32_t remote_label; +  uint8_t flags; +  union pw_protocol_fields data; +  uint8_t protocol; +}; + +struct zapi_pw_status +{ +  char ifname[IF_NAMESIZE]; +  ifindex_t ifindex; +  uint32_t status; +}; +  /* Prototypes of zebra client service functions. */  extern struct zclient *zclient_new (struct thread_master *);  extern void zclient_init (struct zclient *, int, u_short); @@ -278,6 +311,11 @@ extern int lm_label_manager_connect (struct zclient *zclient);  extern int lm_get_label_chunk (struct zclient *zclient, u_char keep,                                 uint32_t chunk_size, uint32_t *start, uint32_t *end);  extern int lm_release_label_chunk (struct zclient *zclient, uint32_t start, uint32_t end); +extern int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw); +extern void zebra_read_pw_status_update(int command, struct zclient *zclient, +                                        zebra_size_t length, vrf_id_t vrf_id, +                                        struct zapi_pw_status *pw); +  /* IPv6 prefix add and delete function prototype. */  struct zapi_ipv6 diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 9a5008f9d7..2e928657f4 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -127,6 +127,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \  	          $(top_srcdir)/zebra/zebra_fpm.c \  		  $(top_srcdir)/zebra/zebra_ptm.c \  		  $(top_srcdir)/zebra/zebra_mpls_vty.c \ +		  $(top_srcdir)/zebra/zebra_pw.c \  		  $(top_srcdir)/watchfrr/watchfrr_vty.c \  	          $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 17b95707d6..84907fb63d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -890,6 +890,12 @@ static struct cmd_node interface_node =    "%s(config-if)# ",  }; +static struct cmd_node pw_node = +{ +  PW_NODE, +  "%s(config-pw)# ", +}; +  static struct cmd_node ns_node =  {    NS_NODE, @@ -1489,6 +1495,7 @@ vtysh_exit (struct vty *vty)        vty->node = ENABLE_NODE;        break;      case INTERFACE_NODE: +    case PW_NODE:      case NS_NODE:      case VRF_NODE:      case ZEBRA_NODE: @@ -1776,6 +1783,17 @@ DEFUNSH (VTYSH_INTERFACE,    return CMD_SUCCESS;  } +DEFUNSH (VTYSH_ZEBRA, +	 vtysh_pseudowire, +	 vtysh_pseudowire_cmd, +	 "pseudowire IFNAME", +	 "Static pseudowire configuration\n" +	 "Pseudowire name\n") +{ +  vty->node = PW_NODE; +  return CMD_SUCCESS; +} +  /* TODO Implement "no interface command in isisd. */  DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D,         vtysh_no_interface_cmd, @@ -3094,6 +3112,7 @@ vtysh_init_vty (void)    install_node (&bgp_node, NULL);    install_node (&rip_node, NULL);    install_node (&interface_node, NULL); +  install_node (&pw_node, NULL);    install_node (&link_params_node, NULL);    install_node (&ns_node, NULL);    install_node (&vrf_node, NULL); @@ -3130,6 +3149,7 @@ vtysh_init_vty (void)    vtysh_install_default (BGP_NODE);    vtysh_install_default (RIP_NODE);    vtysh_install_default (INTERFACE_NODE); +  vtysh_install_default (PW_NODE);    vtysh_install_default (LINK_PARAMS_NODE);    vtysh_install_default (NS_NODE);    vtysh_install_default (VRF_NODE); @@ -3273,6 +3293,10 @@ vtysh_init_vty (void)    install_element (LINK_PARAMS_NODE, &vtysh_exit_interface_cmd);    install_element (INTERFACE_NODE, &vtysh_quit_interface_cmd); +  install_element (PW_NODE, &vtysh_end_all_cmd); +  install_element (PW_NODE, &vtysh_exit_interface_cmd); +  install_element (PW_NODE, &vtysh_quit_interface_cmd); +    install_element (NS_NODE, &vtysh_end_all_cmd);    install_element (CONFIG_NODE, &vtysh_ns_cmd); @@ -3335,6 +3359,7 @@ vtysh_init_vty (void)    install_element (CONFIG_NODE, &vtysh_interface_cmd);    install_element (CONFIG_NODE, &vtysh_no_interface_cmd);    install_element (CONFIG_NODE, &vtysh_no_interface_vrf_cmd); +  install_element (CONFIG_NODE, &vtysh_pseudowire_cmd);    install_element (INTERFACE_NODE, &vtysh_link_params_cmd);    install_element (ENABLE_NODE, &vtysh_show_running_config_cmd);    install_element (ENABLE_NODE, &vtysh_copy_running_config_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 94c4042dd4..4e4c241123 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -196,6 +196,8 @@ vtysh_config_parse_line (const char *line)      default:        if (strncmp (line, "interface", strlen ("interface")) == 0)  	config = config_get (INTERFACE_NODE, line); +      else if (strncmp (line, "pseudowire", strlen ("pseudowire")) == 0) +	config = config_get (PW_NODE, line);        else if (strncmp (line, "logical-router", strlen ("ns")) == 0)  	config = config_get (NS_NODE, line);        else if (strncmp (line, "vrf", strlen ("vrf")) == 0) diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 3e0de3b463..9e9998ebbc 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -33,14 +33,14 @@ zebra_SOURCES = \  	zebra_ptm.c zebra_rnh.c zebra_ptm_redistribute.c \  	zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \  	zebra_mroute.c \ -	label_manager.c \ +	label_manager.c zebra_pw.c \  	# end  testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \  	zebra_vty.c zebra_ptm.c zebra_routemap.c zebra_ns.c zebra_vrf.c \  	kernel_null.c  redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c \  	zebra_ptm_null.c rtadv_null.c if_null.c zserv_null.c zebra_static.c \ -	zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c +	zebra_memory.c zebra_mpls.c zebra_mpls_vty.c zebra_mpls_null.c zebra_pw_null.c  noinst_HEADERS = \  	zebra_memory.h \ @@ -49,7 +49,8 @@ noinst_HEADERS = \  	rt_netlink.h zebra_fpm_private.h zebra_rnh.h \  	zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \  	zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \ -	kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h +	kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h zebra_pw.h \ +	# end  zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP) diff --git a/zebra/debug.c b/zebra/debug.c index a42d5aa3ef..6f59dc0ac2 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -32,6 +32,7 @@ unsigned long zebra_debug_rib;  unsigned long zebra_debug_fpm;  unsigned long zebra_debug_nht;  unsigned long zebra_debug_mpls; +unsigned long zebra_debug_pw;  DEFUN (show_debugging_zebra,         show_debugging_zebra_cmd, @@ -85,6 +86,8 @@ DEFUN (show_debugging_zebra,      vty_out (vty, "  Zebra next-hop tracking debugging is on%s", VTY_NEWLINE);    if (IS_ZEBRA_DEBUG_MPLS)      vty_out (vty, "  Zebra MPLS debugging is on%s", VTY_NEWLINE); +  if (IS_ZEBRA_DEBUG_PW) +    vty_out (vty, "  Zebra pseudowire debugging is on%s", VTY_NEWLINE);    return CMD_SUCCESS;  } @@ -122,6 +125,21 @@ DEFUN (debug_zebra_mpls,    return CMD_WARNING;  } +DEFUN (debug_zebra_pw, +       debug_zebra_pw_cmd, +       "[no] debug zebra pseudowires", +       "Negate a command or set its defaults\n" +       DEBUG_STR +       "Zebra configuration\n" +       "Debug option set for zebra pseudowires\n") +{ +  if (strmatch (argv[0]->text, "no")) +    UNSET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW); +  else +    SET_FLAG (zebra_debug_pw, ZEBRA_DEBUG_PW); +  return CMD_WARNING; +} +  DEFUN (debug_zebra_packet,         debug_zebra_packet_cmd,         "debug zebra packet [<recv|send>] [detail]", @@ -410,6 +428,11 @@ config_write_debug (struct vty *vty)        vty_out (vty, "debug zebra mpls%s", VTY_NEWLINE);        write++;      } +  if (IS_ZEBRA_DEBUG_PW) +    { +      vty_out (vty, "debug zebra pseudowires%s", VTY_NEWLINE); +      write++; +    }    return write;  } @@ -422,6 +445,7 @@ zebra_debug_init (void)    zebra_debug_rib = 0;    zebra_debug_fpm = 0;    zebra_debug_mpls = 0; +  zebra_debug_pw = 0;    install_node (&debug_node, config_write_debug); @@ -430,6 +454,7 @@ zebra_debug_init (void)    install_element (ENABLE_NODE, &debug_zebra_events_cmd);    install_element (ENABLE_NODE, &debug_zebra_nht_cmd);    install_element (ENABLE_NODE, &debug_zebra_mpls_cmd); +  install_element (ENABLE_NODE, &debug_zebra_pw_cmd);    install_element (ENABLE_NODE, &debug_zebra_packet_cmd);    install_element (ENABLE_NODE, &debug_zebra_kernel_cmd);    install_element (ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd); @@ -449,6 +474,7 @@ zebra_debug_init (void)    install_element (CONFIG_NODE, &debug_zebra_events_cmd);    install_element (CONFIG_NODE, &debug_zebra_nht_cmd);    install_element (CONFIG_NODE, &debug_zebra_mpls_cmd); +  install_element (CONFIG_NODE, &debug_zebra_pw_cmd);    install_element (CONFIG_NODE, &debug_zebra_packet_cmd);    install_element (CONFIG_NODE, &debug_zebra_kernel_cmd);    install_element (CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index f8ebf3d616..9a196d2f34 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -43,6 +43,8 @@  #define ZEBRA_DEBUG_MPLS    0x01 +#define ZEBRA_DEBUG_PW      0x01 +  /* Debug related macro. */  #define IS_ZEBRA_DEBUG_EVENT  (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -64,6 +66,7 @@  #define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM)  #define IS_ZEBRA_DEBUG_NHT  (zebra_debug_nht & ZEBRA_DEBUG_NHT)  #define IS_ZEBRA_DEBUG_MPLS  (zebra_debug_mpls & ZEBRA_DEBUG_MPLS) +#define IS_ZEBRA_DEBUG_PW  (zebra_debug_pw & ZEBRA_DEBUG_PW)  extern unsigned long zebra_debug_event;  extern unsigned long zebra_debug_packet; @@ -72,6 +75,7 @@ extern unsigned long zebra_debug_rib;  extern unsigned long zebra_debug_fpm;  extern unsigned long zebra_debug_nht;  extern unsigned long zebra_debug_mpls; +extern unsigned long zebra_debug_pw;  extern void zebra_debug_init (void); diff --git a/zebra/main.c b/zebra/main.c index 459e6148d8..84d71c33b9 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -314,6 +314,7 @@ main (int argc, char **argv)    zebra_mpls_init ();    zebra_mpls_vty_init (); +  zebra_pw_vty_init ();    /* For debug purpose. */    /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ diff --git a/zebra/rib.h b/zebra/rib.h index 5381d76b98..e910facb44 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -91,6 +91,7 @@ struct rib  #define RIB_ENTRY_NEXTHOPS_CHANGED 0x2  #define RIB_ENTRY_CHANGED          0x4  #define RIB_ENTRY_SELECTED_FIB     0x8 +#define RIB_ENTRY_LABELS_CHANGED   0x10    /* Nexthop information. */    u_char nexthop_num; diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 5a3ed7545d..ba500cac27 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1346,7 +1346,7 @@ mpls_ftn_update (int add, struct zebra_vrf *zvrf, enum lsp_types_t type,      return 0;    SET_FLAG (rib->status, RIB_ENTRY_CHANGED); -  SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); +  SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED);    rib_queue_add (rn);    return 0; @@ -1542,7 +1542,7 @@ mpls_ldp_ftn_uninstall_all (struct zebra_vrf *zvrf, int afi)  	    {  	      nexthop_del_labels (nexthop);  	      SET_FLAG (rib->status, RIB_ENTRY_CHANGED); -	      SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); +	      SET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED);  	      update = 1;  	    } diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 5dfe16caf5..0fcd28e0ee 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -35,6 +35,7 @@ extern struct zebra_privs_t zserv_privs;  struct {    u_int32_t rtseq;    int fd; +  int ioctl_fd;  } kr_state;  static int @@ -337,6 +338,90 @@ kernel_del_lsp (zebra_lsp_t *lsp)    return ret;  } +static int +kmpw_install (struct zebra_pw *pw) +{ +  struct ifreq ifr; +  struct ifmpwreq imr; +  struct sockaddr_storage ss; +  struct sockaddr_in *sa_in = (struct sockaddr_in *) &ss; +  struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) &ss; + +  memset (&imr, 0, sizeof (imr)); +  switch (pw->type) +    { +    case PW_TYPE_ETHERNET: +      imr.imr_type = IMR_TYPE_ETHERNET; +      break; +    case PW_TYPE_ETHERNET_TAGGED: +      imr.imr_type = IMR_TYPE_ETHERNET_TAGGED; +      break; +    default: +      zlog_err ("%s: unhandled pseudowire type (%#X)", __func__, +                pw->type); +      return -1; +    } + +  if (pw->flags & F_PSEUDOWIRE_CWORD) +    imr.imr_flags |= IMR_FLAG_CONTROLWORD; + +  /* pseudowire nexthop */ +  memset (&ss, 0, sizeof (ss)); +  switch (pw->af) { +    case AF_INET: +      sa_in->sin_family = AF_INET; +      sa_in->sin_len = sizeof (struct sockaddr_in); +      sa_in->sin_addr = pw->nexthop.ipv4; +      break; +    case AF_INET6: +      sa_in6->sin6_family = AF_INET6; +      sa_in6->sin6_len = sizeof (struct sockaddr_in6); +      sa_in6->sin6_addr = pw->nexthop.ipv6; +      break; +    default: +      zlog_err ("%s: unhandled pseudowire address-family (%u)", __func__, +		pw->af); +      return -1; +  } +  memcpy (&imr.imr_nexthop, (struct sockaddr *) &ss, +	  sizeof (imr.imr_nexthop)); + +  /* pseudowire local/remote labels */ +  imr.imr_lshim.shim_label = pw->local_label; +  imr.imr_rshim.shim_label = pw->remote_label; + +  /* ioctl */ +  memset (&ifr, 0, sizeof (ifr)); +  strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name)); +  ifr.ifr_data = (caddr_t) &imr; +  if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) +    { +      zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno)); +      return -1; +    } + +  return 0; +} + +static int +kmpw_uninstall (struct zebra_pw *pw) +{ +  struct ifreq ifr; +  struct ifmpwreq imr; + +  memset(&ifr, 0, sizeof (ifr)); +  memset(&imr, 0, sizeof (imr)); +  strlcpy (ifr.ifr_name, pw->ifname, sizeof (ifr.ifr_name)); +  ifr.ifr_data = (caddr_t) &imr; +  if (ioctl (kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) +    { +      zlog_err ("ioctl SIOCSETMPWCFG: %s", safe_strerror (errno)); +      return -1; +    } + +  return 0; +} +  #define MAX_RTSOCK_BUF	128 * 1024  int  mpls_kernel_init (void) @@ -344,10 +429,17 @@ mpls_kernel_init (void)    int		rcvbuf, default_rcvbuf;    socklen_t	optlen; -  if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { +  if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) +    {        zlog_warn("%s: socket", __func__);        return -1; -  } +    } + +  if ((kr_state.ioctl_fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) +    { +      zlog_warn("%s: ioctl socket", __func__); +      return -1; +    }    /* grow receive buffer, don't wanna miss messages */    optlen = sizeof (default_rcvbuf); @@ -364,5 +456,9 @@ mpls_kernel_init (void)    kr_state.rtseq = 1; +  /* register hook to install/uninstall pseudowires */ +  hook_register (pw_install, kmpw_install); +  hook_register (pw_uninstall, kmpw_uninstall); +    return 0;  } diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c new file mode 100644 index 0000000000..2164ddf6ee --- /dev/null +++ b/zebra/zebra_pw.c @@ -0,0 +1,532 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 <zebra.h> + +#include "log.h" +#include "memory.h" +#include "thread.h" +#include "command.h" +#include "vrf.h" + +#include "zebra/debug.h" +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_pw.h" + +DEFINE_MTYPE_STATIC(LIB, PW, "Pseudowire") + +DEFINE_QOBJ_TYPE(zebra_pw) + +DEFINE_HOOK(pw_install, (struct zebra_pw *pw), (pw)) +DEFINE_HOOK(pw_uninstall, (struct zebra_pw *pw), (pw)) + +extern struct zebra_t zebrad; + +static int zebra_pw_enabled(struct zebra_pw *); +static void zebra_pw_install(struct zebra_pw *); +static void zebra_pw_uninstall(struct zebra_pw *); +static int zebra_pw_install_retry(struct thread *); +static int zebra_pw_check_reachability(struct zebra_pw *); +static void zebra_pw_update_status(struct zebra_pw *, int); + +static inline int zebra_pw_compare(const struct zebra_pw *a, +				   const struct zebra_pw *b) +{ +	return (strcmp(a->ifname, b->ifname)); +} + +RB_GENERATE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare) +RB_GENERATE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *zvrf, const char *ifname, +			      uint8_t protocol, struct zserv *client) +{ +	struct zebra_pw *pw; + +	if (IS_ZEBRA_DEBUG_PW) +		zlog_debug("%u: adding pseudowire %s protocol %s", +			   zvrf_id(zvrf), ifname, zebra_route_string(protocol)); + +	pw = XCALLOC(MTYPE_PW, sizeof(*pw)); +	strlcpy(pw->ifname, ifname, sizeof(pw->ifname)); +	pw->protocol = protocol; +	pw->vrf_id = zvrf_id(zvrf); +	pw->client = client; +	pw->status = PW_STATUS_UP; +	pw->local_label = MPLS_NO_LABEL; +	pw->remote_label = MPLS_NO_LABEL; +	pw->flags = F_PSEUDOWIRE_CWORD; + +	RB_INSERT(zebra_pw_head, &zvrf->pseudowires, pw); +	if (pw->protocol == ZEBRA_ROUTE_STATIC) { +		RB_INSERT(zebra_static_pw_head, &zvrf->static_pseudowires, pw); +		QOBJ_REG(pw, zebra_pw); +	} + +	return pw; +} + +void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) +{ +	if (IS_ZEBRA_DEBUG_PW) +		zlog_debug("%u: deleting pseudowire %s protocol %s", pw->vrf_id, +			   pw->ifname, zebra_route_string(pw->protocol)); + +	/* remove nexthop tracking */ +	zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + +	/* uninstall */ +	if (pw->status == PW_STATUS_UP) +		hook_call(pw_uninstall, pw); +	else if (pw->install_retry_timer) +		THREAD_TIMER_OFF(pw->install_retry_timer); + +	/* unlink and release memory */ +	RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw); +	if (pw->protocol == ZEBRA_ROUTE_STATIC) +		RB_REMOVE(zebra_static_pw_head, &zvrf->static_pseudowires, pw); +	XFREE(MTYPE_PW, pw); +} + +void zebra_pw_change(struct zebra_pw *pw, ifindex_t ifindex, int type, int af, +		     union g_addr *nexthop, uint32_t local_label, +		     uint32_t remote_label, uint8_t flags, +		     union pw_protocol_fields *data) +{ +	zebra_deregister_rnh_pseudowire(pw->vrf_id, pw); + +	pw->ifindex = ifindex; +	pw->type = type; +	pw->af = af; +	pw->nexthop = *nexthop; +	pw->local_label = local_label; +	pw->remote_label = remote_label; +	pw->flags = flags; +	pw->data = *data; + +	if (zebra_pw_enabled(pw)) +		zebra_register_rnh_pseudowire(pw->vrf_id, pw); +	else +		zebra_pw_uninstall(pw); +} + +struct zebra_pw *zebra_pw_find(struct zebra_vrf *zvrf, const char *ifname) +{ +	struct zebra_pw pw; +	strlcpy(pw.ifname, ifname, sizeof(pw.ifname)); +	return (RB_FIND(zebra_pw_head, &zvrf->pseudowires, &pw)); +} + +static int zebra_pw_enabled(struct zebra_pw *pw) +{ +	if (pw->protocol == ZEBRA_ROUTE_STATIC) { +		if (pw->local_label == MPLS_NO_LABEL +		    || pw->remote_label == MPLS_NO_LABEL +		    || pw->af == AF_UNSPEC) +			return 0; +		return 1; +	} else +		return pw->enabled; +} + +void zebra_pw_update(struct zebra_pw *pw) +{ +	if (zebra_pw_check_reachability(pw) < 0) { +		zebra_pw_uninstall(pw); +		/* wait for NHT and try again later */ +	} else { +		/* +		 * Install or reinstall the pseudowire (e.g. to update +		 * parameters like the nexthop or the use of the control word). +		 */ +		zebra_pw_install(pw); +	} +} + +static void zebra_pw_install(struct zebra_pw *pw) +{ +	if (IS_ZEBRA_DEBUG_PW) +		zlog_debug("%u: installing pseudowire %s protocol %s", +			   pw->vrf_id, pw->ifname, +			   zebra_route_string(pw->protocol)); + +	if (hook_call(pw_install, pw)) { +		zebra_pw_install_failure(pw); +		return; +	} + +	if (pw->status == PW_STATUS_DOWN) +		zebra_pw_update_status(pw, PW_STATUS_UP); +} + +static void zebra_pw_uninstall(struct zebra_pw *pw) +{ +	if (pw->status == PW_STATUS_DOWN) +		return; + +	if (IS_ZEBRA_DEBUG_PW) +		zlog_debug("%u: uninstalling pseudowire %s protocol %s", +			   pw->vrf_id, pw->ifname, +			   zebra_route_string(pw->protocol)); + +	/* ignore any possible error */ +	hook_call(pw_uninstall, pw); + +	if (zebra_pw_enabled(pw)) +		zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +/* + * Installation of the pseudowire in the kernel or hardware has failed. This + * function will notify the pseudowire client about the failure and schedule + * to retry the installation later. This function can be called by an external + * agent that performs the pseudowire installation in an asynchronous way. + */ +void zebra_pw_install_failure(struct zebra_pw *pw) +{ +	if (IS_ZEBRA_DEBUG_PW) +		zlog_debug("%u: failed installing pseudowire %s, " +			   "scheduling retry in %u seconds", pw->vrf_id, +			   pw->ifname, PW_INSTALL_RETRY_INTERVAL); + +	/* schedule to retry later */ +	THREAD_TIMER_OFF(pw->install_retry_timer); +	pw->install_retry_timer = +		thread_add_timer(zebrad.master, zebra_pw_install_retry, +		pw, PW_INSTALL_RETRY_INTERVAL); + +	zebra_pw_update_status(pw, PW_STATUS_DOWN); +} + +static int zebra_pw_install_retry(struct thread *thread) +{ +	struct zebra_pw *pw = THREAD_ARG(thread); + +	pw->install_retry_timer = NULL; +	zebra_pw_install(pw); + +	return 0; +} + +static void zebra_pw_update_status(struct zebra_pw *pw, int status) +{ +	pw->status = status; +	if (pw->client) +		zsend_pw_update(pw->client, pw); +} + +static int zebra_pw_check_reachability(struct zebra_pw *pw) +{ +	struct rib *rib; +	struct nexthop *nexthop, *tnexthop; +	int recursing; + +	/* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */ + +	/* find route to the remote end of the pseudowire */ +	rib = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, +			&pw->nexthop, NULL); +	if (!rib) { +		if (IS_ZEBRA_DEBUG_PW) +			zlog_warn("%s: no route found for %s", __func__, +				  pw->ifname); +		return -1; +	} + +	/* +	 * Need to ensure that there's a label binding for all nexthops. +	 * Otherwise, ECMP for this route could render the pseudowire unusable. +	 */ +	for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) { +		if (!nexthop->nh_label) { +			if (IS_ZEBRA_DEBUG_PW) +				zlog_warn("%s: unlabeled route for %s", +					  __func__, pw->ifname); +			return -1; +		} +	} + +	return 0; +} + +void +zebra_pw_client_close(struct zserv *client) +{ +	struct vrf *vrf; +	struct zebra_vrf *zvrf; +	struct zebra_pw *pw, *tmp; + +	RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) { +		zvrf = vrf->info; +		RB_FOREACH_SAFE(pw, zebra_pw_head, &zvrf->pseudowires, tmp) { +			if (pw->client != client) +				continue; +			zebra_pw_del(zvrf, pw); +		} +	} +} + +void zebra_pw_init(struct zebra_vrf *zvrf) +{ +	RB_INIT(&zvrf->pseudowires); +	RB_INIT(&zvrf->static_pseudowires); +} + +void zebra_pw_exit(struct zebra_vrf *zvrf) +{ +	struct zebra_pw *pw; + +	while ((pw = RB_ROOT(&zvrf->pseudowires)) != NULL) +		zebra_pw_del(zvrf, pw); +} + +DEFUN_NOSH (pseudowire_if, +	    pseudowire_if_cmd, +	    "[no] pseudowire IFNAME", +	    NO_STR +	    "Static pseudowire configuration\n" +	    "Pseudowire name\n") +{ +	struct zebra_vrf *zvrf; +	struct zebra_pw *pw; +	int idx = 0; +	const char *ifname; + +	zvrf = vrf_info_lookup(VRF_DEFAULT); +	if (!zvrf) +		return CMD_WARNING; + +	argv_find(argv, argc, "IFNAME", &idx); +	ifname = argv[idx]->arg; +	pw = zebra_pw_find(zvrf, ifname); +	if (pw && pw->protocol != ZEBRA_ROUTE_STATIC) { +		vty_out(vty, "%% Pseudowire is not static%s", VTY_NEWLINE); +		return CMD_WARNING; +	} + +	if (argv_find(argv, argc, "no", &idx)) { +		if (!pw) +			return CMD_SUCCESS; +		zebra_pw_del(zvrf, pw); +	} + +	if (!pw) +		pw = zebra_pw_add(zvrf, ifname, ZEBRA_ROUTE_STATIC, NULL); +	VTY_PUSH_CONTEXT(PW_NODE, pw); + +	return CMD_SUCCESS; +} + +DEFUN (pseudowire_labels, +       pseudowire_labels_cmd, +       "[no] mpls label local (16-1048575) remote (16-1048575)", +       NO_STR +       "MPLS L2VPN PW command\n" +       "MPLS L2VPN static labels\n" +       "Local pseudowire label\n" +       "Local pseudowire label\n" +       "Remote pseudowire label\n" +       "Remote pseudowire label\n") +{ +	VTY_DECLVAR_CONTEXT(zebra_pw, pw); +	int idx = 0; +	mpls_label_t local_label, remote_label; + +	if (argv_find(argv, argc, "no", &idx)) { +		local_label = MPLS_NO_LABEL; +		remote_label = MPLS_NO_LABEL; +	} else { +		argv_find(argv, argc, "local", &idx); +		local_label = atoi(argv[idx + 1]->arg); +		argv_find(argv, argc, "remote", &idx); +		remote_label = atoi(argv[idx + 1]->arg); +	} + +	zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, +			local_label, remote_label, pw->flags, &pw->data); + +	return CMD_SUCCESS; +} + +DEFUN (pseudowire_neighbor, +       pseudowire_neighbor_cmd, +       "[no] neighbor <A.B.C.D|X:X::X:X>", +       NO_STR +       "Specify the IPv4 or IPv6 address of the remote endpoint\n" +       "IPv4 address\n" +       "IPv6 address\n") +{ +	VTY_DECLVAR_CONTEXT(zebra_pw, pw); +	int idx = 0; +	const char *address; +	int af; +	union g_addr nexthop; + +	af = AF_UNSPEC; +	memset(&nexthop, 0, sizeof(nexthop)); + +	if (!argv_find(argv, argc, "no", &idx)) { +		argv_find(argv, argc, "neighbor", &idx); +		address = argv[idx + 1]->arg; + +		if (inet_pton(AF_INET, address, &nexthop.ipv4) == 1) +			af = AF_INET; +		else if (inet_pton(AF_INET6, address, &nexthop.ipv6) == 1) +			af = AF_INET6; +		else { +			vty_out(vty, "%% Malformed address%s", VTY_NEWLINE); +			return CMD_WARNING; +		} +	} + +	zebra_pw_change(pw, pw->ifindex, pw->type, af, &nexthop, +			pw->local_label, pw->remote_label, pw->flags, +			&pw->data); + +	return CMD_SUCCESS; +} + +DEFUN (pseudowire_control_word, +       pseudowire_control_word_cmd, +       "[no] control-word <exclude|include>", +       NO_STR +       "Control-word options\n" +       "Exclude control-word in pseudowire packets\n" +       "Include control-word in pseudowire packets\n") +{ +	VTY_DECLVAR_CONTEXT(zebra_pw, pw); +	int idx = 0; +	uint8_t flags = 0; + +	if (argv_find(argv, argc, "no", &idx)) +		flags = F_PSEUDOWIRE_CWORD; +	else { +		argv_find(argv, argc, "control-word", &idx); +		if (argv[idx + 1]->text[0] == 'i') +			flags = F_PSEUDOWIRE_CWORD; +	} + +	zebra_pw_change(pw, pw->ifindex, pw->type, pw->af, &pw->nexthop, +			pw->local_label, pw->remote_label, flags, &pw->data); + +	return CMD_SUCCESS; +} + +DEFUN (show_pseudowires, +       show_pseudowires_cmd, +       "show pseudowires", +       SHOW_STR +       "Pseudowires") +{ +	struct zebra_vrf *zvrf; +	struct zebra_pw *pw; + +	zvrf = vrf_info_lookup(VRF_DEFAULT); +	if (!zvrf) +		return 0; + +	vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s", +		"Interface", "Neighbor", "Labels", "Protocol", "Status", +		VTY_NEWLINE); + +	RB_FOREACH(pw, zebra_pw_head, &zvrf->pseudowires) { +		char buf_nbr[INET6_ADDRSTRLEN]; +		char buf_labels[64]; + +		inet_ntop(pw->af, &pw->nexthop, buf_nbr, sizeof(buf_nbr)); + +		if (pw->local_label != MPLS_NO_LABEL +		    && pw->remote_label != MPLS_NO_LABEL) +			snprintf(buf_labels, sizeof(buf_labels), "%u/%u", +				 pw->local_label, pw->remote_label); +		else +			snprintf(buf_labels, sizeof(buf_labels), "-"); + +		vty_out(vty, "%-16s %-24s %-12s %-8s %-10s%s", +			pw->ifname, (pw->af != AF_UNSPEC) ? buf_nbr : "-", +			buf_labels, zebra_route_string(pw->protocol), +			(zebra_pw_enabled(pw) && pw->status == PW_STATUS_UP) ? +			"UP" : "DOWN", VTY_NEWLINE); +	} + +	return CMD_SUCCESS; +} + +/* Pseudowire configuration write function. */ +static int zebra_pw_config(struct vty *vty) +{ +	int write = 0; +	struct zebra_vrf *zvrf; +	struct zebra_pw *pw; + +	zvrf = vrf_info_lookup(VRF_DEFAULT); +	if (!zvrf) +		return 0; + +	RB_FOREACH(pw, zebra_static_pw_head, &zvrf->static_pseudowires) { +		vty_out(vty, "pseudowire %s%s", pw->ifname, VTY_NEWLINE); +		if (pw->local_label != MPLS_NO_LABEL +		    && pw->remote_label != MPLS_NO_LABEL) +			vty_out(vty, " mpls label local %u remote %u%s", +				pw->local_label, pw->remote_label, +				VTY_NEWLINE); +		else +			vty_out(vty, " ! Incomplete config, specify the static " +				"MPLS labels%s", VTY_NEWLINE); + +		if (pw->af != AF_UNSPEC) { +			char buf[INET6_ADDRSTRLEN]; +			inet_ntop(pw->af, &pw->nexthop, buf, sizeof(buf)); +			vty_out(vty, " neighbor %s%s", buf, VTY_NEWLINE); +		} else +			vty_out(vty, " ! Incomplete config, specify a neighbor " +				"address%s", VTY_NEWLINE); + +		if (!(pw->flags & F_PSEUDOWIRE_CWORD)) +			vty_out(vty, " control-word exclude%s", VTY_NEWLINE); + +		vty_out(vty, "!%s", VTY_NEWLINE); +		write = 1; +	} + +	return write; +} + +static struct cmd_node pw_node = +{ +	PW_NODE, +	"%s(config-pw)# ", +	1, +}; + +void zebra_pw_vty_init(void) +{ +	install_node(&pw_node, zebra_pw_config); +	install_default(PW_NODE); + +	install_element(CONFIG_NODE, &pseudowire_if_cmd); +	install_element(PW_NODE, &pseudowire_labels_cmd); +	install_element(PW_NODE, &pseudowire_neighbor_cmd); +	install_element(PW_NODE, &pseudowire_control_word_cmd); + +	install_element(VIEW_NODE, &show_pseudowires_cmd); +} diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h new file mode 100644 index 0000000000..b588bac0a2 --- /dev/null +++ b/zebra/zebra_pw.h @@ -0,0 +1,75 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 + */ + +#ifndef ZEBRA_PW_H_ +#define ZEBRA_PW_H_ + +#include <net/if.h> +#include <netinet/in.h> + +#include "hook.h" +#include "qobj.h" + +#define PW_INSTALL_RETRY_INTERVAL	30 + +struct zebra_pw { +	RB_ENTRY(zebra_pw) pw_entry, static_pw_entry; +	vrf_id_t vrf_id; +	char ifname[IF_NAMESIZE]; +	ifindex_t ifindex; +	int type; +	int af; +	union g_addr nexthop; +	uint32_t local_label; +	uint32_t remote_label; +	uint8_t flags; +	union pw_protocol_fields data; +	int enabled; +	int status; +	uint8_t protocol; +	struct zserv *client; +	struct rnh *rnh; +	struct thread *install_retry_timer; +	QOBJ_FIELDS +}; +DECLARE_QOBJ_TYPE(zebra_pw) + +RB_HEAD(zebra_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_pw_head, zebra_pw, pw_entry, zebra_pw_compare); + +RB_HEAD(zebra_static_pw_head, zebra_pw); +RB_PROTOTYPE(zebra_static_pw_head, zebra_pw, static_pw_entry, zebra_pw_compare); + +DECLARE_HOOK(pw_install, (struct zebra_pw * pw), (pw)) +DECLARE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw)) + +struct zebra_pw *zebra_pw_add(struct zebra_vrf *, const char *, +			      uint8_t, struct zserv *); +void zebra_pw_del(struct zebra_vrf *, struct zebra_pw *); +void zebra_pw_change(struct zebra_pw *, ifindex_t, int, int, union g_addr *, +		     uint32_t, uint32_t, uint8_t, union pw_protocol_fields *); +struct zebra_pw *zebra_pw_find(struct zebra_vrf *, const char *); +void zebra_pw_update(struct zebra_pw *); +void zebra_pw_install_failure(struct zebra_pw *); +void zebra_pw_client_close(struct zserv *); +void zebra_pw_init(struct zebra_vrf *); +void zebra_pw_exit(struct zebra_vrf *); +void zebra_pw_vty_init(void); + +#endif /* ZEBRA_PW_H_ */ diff --git a/zebra/zebra_pw_null.c b/zebra/zebra_pw_null.c new file mode 100644 index 0000000000..d19d80b8c9 --- /dev/null +++ b/zebra/zebra_pw_null.c @@ -0,0 +1,29 @@ +/* Zebra PW code + * Copyright (C) 2016 Volta Networks, Inc. + * + * This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 <zebra.h> + +#include "vrf.h" + +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_vrf.h" + +void zebra_pw_init(struct zebra_vrf *zvrf) {} +void zebra_pw_exit(struct zebra_vrf *zvrf) {} diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 5096e95395..d5ebbbc466 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -131,6 +131,7 @@ zebra_add_rnh (struct prefix *p, vrf_id_t vrfid, rnh_type_t type)        rnh->client_list = list_new();        rnh->vrf_id = vrfid;        rnh->zebra_static_route_list = list_new(); +      rnh->zebra_pseudowire_list = list_new();        route_lock_node (rn);        rn->info = rnh;        rnh->node = rn; @@ -168,6 +169,7 @@ zebra_free_rnh (struct rnh *rnh)    rnh->flags |= ZEBRA_NHT_DELETED;    list_free (rnh->client_list);    list_free (rnh->zebra_static_route_list); +  list_free (rnh->zebra_pseudowire_list);    free_state (rnh->vrf_id, rnh->state, rnh->node);    XFREE (MTYPE_RNH, rnh);  } @@ -222,7 +224,8 @@ zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client, rnh_type_t type)      }    listnode_delete(rnh->client_list, client);    if (list_isempty(rnh->client_list) && -      list_isempty(rnh->zebra_static_route_list)) +      list_isempty(rnh->zebra_static_route_list) && +      list_isempty(rnh->zebra_pseudowire_list))      zebra_delete_rnh(rnh, type);  } @@ -252,7 +255,8 @@ zebra_deregister_rnh_static_nh(vrf_id_t vrf_id, struct prefix *nh,    listnode_delete(rnh->zebra_static_route_list, static_rn);    if (list_isempty(rnh->client_list) && -      list_isempty(rnh->zebra_static_route_list)) +      list_isempty(rnh->zebra_static_route_list) && +      list_isempty(rnh->zebra_pseudowire_list))      zebra_delete_rnh(rnh, RNH_NEXTHOP_TYPE);  } @@ -301,6 +305,62 @@ zebra_deregister_rnh_static_nexthops (vrf_id_t vrf_id, struct nexthop *nexthop,      }  } +/* XXX move this utility function elsewhere? */ +static void +addr2hostprefix (int af, const union g_addr *addr, struct prefix *prefix) +{ +  switch (af) +    { +    case AF_INET: +      prefix->family = AF_INET; +      prefix->prefixlen = IPV4_MAX_BITLEN; +      prefix->u.prefix4 = addr->ipv4; +      break; +    case AF_INET6: +      prefix->family = AF_INET6; +      prefix->prefixlen = IPV6_MAX_BITLEN; +      prefix->u.prefix6 = addr->ipv6; +      break; +    default: +      zlog_warn ("%s: unknown address family %d", __func__, af); +      break; +    } +} + +void +zebra_register_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) +{ +  struct prefix nh; +  struct rnh *rnh; + +  addr2hostprefix (pw->af, &pw->nexthop, &nh); +  rnh = zebra_add_rnh (&nh, vrf_id, RNH_NEXTHOP_TYPE); +  if (rnh && !listnode_lookup (rnh->zebra_pseudowire_list, pw)) +    { +      listnode_add (rnh->zebra_pseudowire_list, pw); +      pw->rnh = rnh; +      zebra_evaluate_rnh (vrf_id, pw->af, 1, RNH_NEXTHOP_TYPE, &nh); +    } +} + +void +zebra_deregister_rnh_pseudowire (vrf_id_t vrf_id, struct zebra_pw *pw) +{ +  struct rnh *rnh; + +  rnh = pw->rnh; +  if (!rnh) +    return; + +  listnode_delete (rnh->zebra_pseudowire_list, pw); +  pw->rnh = NULL; + +  if (list_isempty (rnh->client_list) && +      list_isempty (rnh->zebra_static_route_list) && +      list_isempty (rnh->zebra_pseudowire_list)) +    zebra_delete_rnh (rnh, RNH_NEXTHOP_TYPE); +} +  /* Apply the NHT route-map for a client to the route (and nexthops)   * resolving a NH.   */ @@ -611,6 +671,16 @@ zebra_rnh_process_static_routes (vrf_id_t vrfid, int family,      }  } +static void +zebra_rnh_process_pseudowires (vrf_id_t vrfid, struct rnh *rnh) +{ +  struct zebra_pw *pw; +  struct listnode *node; + +  for (ALL_LIST_ELEMENTS_RO (rnh->zebra_pseudowire_list, node, pw)) +    zebra_pw_update (pw); +} +  /*   * See if a tracked nexthop entry has undergone any change, and if so,   * take appropriate action; this involves notifying any clients and/or @@ -655,6 +725,9 @@ zebra_rnh_eval_nexthop_entry (vrf_id_t vrfid, int family, int force,        /* Process static routes attached to this nexthop */        zebra_rnh_process_static_routes (vrfid, family, nrn, rnh,                                         prn, rnh->state); + +      /* Process pseudowires attached to this nexthop */ +      zebra_rnh_process_pseudowires (vrfid, rnh);      }  } @@ -717,7 +790,10 @@ zebra_rnh_clear_nhc_flag (vrf_id_t vrfid, int family, rnh_type_t type,    rib = zebra_rnh_resolve_entry (vrfid, family, type, nrn, rnh, &prn);    if (rib) -    UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); +    { +      UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); +      UNSET_FLAG (rib->status, RIB_ENTRY_LABELS_CHANGED); +    }  }  /* Evaluate all tracked entries (nexthops or routes for import into BGP) @@ -868,7 +944,8 @@ compare_state (struct rib *r1, struct rib *r2)    if (r1->nexthop_num != r2->nexthop_num)        return 1; -  if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED)) +  if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED) || +      CHECK_FLAG(r1->status, RIB_ENTRY_LABELS_CHANGED))      return 1;    return 0; @@ -1030,6 +1107,8 @@ print_rnh (struct route_node *rn, struct vty *vty)      vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto),  	    client->sock, rnh->filtered[client->proto] ? "(filtered)" : "");    if (!list_isempty(rnh->zebra_static_route_list)) -    vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); +    vty_out(vty, " zebra[static routes]%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : ""); +  if (!list_isempty(rnh->zebra_pseudowire_list)) +    vty_out(vty, " zebra[pseudowires]");    vty_out(vty, "%s", VTY_NEWLINE);  } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index 4394fde4f3..9da5138e93 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -42,6 +42,7 @@ struct rnh    struct prefix resolved_route;    struct list *client_list;    struct list *zebra_static_route_list; /* static routes dependent on this NH */ +  struct list *zebra_pseudowire_list; /* pseudowires dependent on this NH */    struct route_node *node;    int filtered[ZEBRA_ROUTE_MAX]; /* if this has been filtered for client */  }; @@ -67,6 +68,8 @@ extern void zebra_register_rnh_static_nh(vrf_id_t, struct prefix *, struct route  extern void zebra_deregister_rnh_static_nexthops (vrf_id_t, struct nexthop *nexthop,                                                    struct route_node *rn);  extern void zebra_deregister_rnh_static_nh(vrf_id_t, struct prefix *, struct route_node *); +extern void zebra_register_rnh_pseudowire (vrf_id_t, struct zebra_pw *); +extern void zebra_deregister_rnh_pseudowire (vrf_id_t, struct zebra_pw *);  extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,  				    rnh_type_t type);  extern void zebra_evaluate_rnh(vrf_id_t vrfid, int family, int force, rnh_type_t type, diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 6b36891056..1797ef080c 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -246,6 +246,7 @@ zebra_vrf_delete (struct vrf *vrf)  	}        zebra_mpls_close_tables (zvrf); +      zebra_pw_exit (zvrf);        for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp))  	if_nbr_ipv6ll_to_ipv4ll_neigh_del_all (ifp); @@ -423,6 +424,7 @@ zebra_vrf_alloc (void)      }    zebra_mpls_init_tables (zvrf); +  zebra_pw_init (zvrf);    return zvrf;  } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 96d631d646..56c98931f2 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -24,6 +24,7 @@  #define __ZEBRA_RIB_H__  #include <zebra/zebra_ns.h> +#include <zebra/zebra_pw.h>  /* Routing table instance.  */  struct zebra_vrf @@ -79,6 +80,10 @@ struct zebra_vrf    /* MPLS label forwarding table */    struct hash *lsp_table; +  /* Pseudowires. */ +  struct zebra_pw_head pseudowires; +  struct zebra_static_pw_head static_pseudowires; +    /* MPLS processing flags */    u_int16_t mpls_flags;  #define MPLS_FLAG_SCHEDULE_LSPS    (1 << 0) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index a8bee3cf50..180ecd64a5 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -932,6 +932,13 @@ vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib,                  break;              } +	  if (nexthop->nh_label && nexthop->nh_label->num_labels) +	    { +	      json_object_string_add(json_nexthop, "labels", +				     mpls_label2str (nexthop->nh_label->num_labels, +						     nexthop->nh_label->label, buf, BUFSIZ)); +	    } +            json_object_array_add(json_nexthops, json_nexthop);          } diff --git a/zebra/zserv.c b/zebra/zserv.c index 9beae9232e..fb02d60ba4 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -1013,6 +1013,28 @@ zsend_router_id_update (struct zserv *client, struct prefix *p,    return zebra_server_send_message(client);  } +/* + * Function used by Zebra to send a PW status update to LDP daemon + */ +int +zsend_pw_update (struct zserv *client, struct zebra_pw *pw) +{ +  struct stream *s; + +  s = client->obuf; +  stream_reset (s); + +  zserv_create_header (s, ZEBRA_PW_STATUS_UPDATE, pw->vrf_id); +  stream_write (s, pw->ifname, IF_NAMESIZE); +  stream_putl (s, pw->ifindex); +  stream_putl (s, pw->status); + +  /* Put length at the first point of the stream. */ +  stream_putw_at (s, 0, stream_get_endp (s)); + +  return zebra_server_send_message (client); +} +  /* Register zebra server interface information.  Send current all     interface and address information. */  static int @@ -1762,16 +1784,14 @@ zread_mpls_labels (int command, struct zserv *client, u_short length,      {        mpls_lsp_install (zvrf, type, in_label, out_label, gtype, &gate,  			NULL, ifindex); -      if (out_label != MPLS_IMP_NULL_LABEL) -	mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex, -			 distance, out_label); +      mpls_ftn_update (1, zvrf, type, &prefix, gtype, &gate, ifindex, +		       distance, out_label);      }    else if (command == ZEBRA_MPLS_LABELS_DELETE)      {        mpls_lsp_uninstall (zvrf, type, in_label, gtype, &gate, NULL, ifindex); -      if (out_label != MPLS_IMP_NULL_LABEL) -	mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, -			 distance, out_label); +      mpls_ftn_update (0, zvrf, type, &prefix, gtype, &gate, ifindex, +		       distance, out_label);      }  }  /* Send response to a label manager connect request to client */ @@ -1933,6 +1953,103 @@ zread_label_manager_request (int cmd, struct zserv *client, vrf_id_t vrf_id)      }  } +static int +zread_pseudowire (int command, struct zserv *client, u_short length, +		  vrf_id_t vrf_id) +{ +  struct stream *s; +  struct zebra_vrf *zvrf; +  char ifname[IF_NAMESIZE]; +  ifindex_t ifindex; +  int type; +  int af; +  union g_addr nexthop; +  uint32_t local_label; +  uint32_t remote_label; +  uint8_t flags; +  union pw_protocol_fields data; +  uint8_t protocol; +  struct zebra_pw *pw; + +  zvrf = vrf_info_lookup (vrf_id); +  if (!zvrf) +    return -1; + +  /* Get input stream.  */ +  s = client->ibuf; + +  /* Get data. */ +  stream_get (ifname, s, IF_NAMESIZE); +  ifindex = stream_getl (s); +  type = stream_getl (s); +  af = stream_getl (s); +  switch (af) +    { +    case AF_INET: +      nexthop.ipv4.s_addr = stream_get_ipv4 (s); +      break; +    case AF_INET6: +      stream_get (&nexthop.ipv6, s, 16); +      break; +    default: +      return -1; +    } +  local_label = stream_getl (s); +  remote_label = stream_getl (s); +  flags = stream_getc (s); +  stream_get (&data, s, sizeof(data)); +  protocol = client->proto; + +  pw = zebra_pw_find(zvrf, ifname); +  switch (command) +    { +    case ZEBRA_PW_ADD: +      if (pw) +	{ +	  zlog_warn ("%s: pseudowire %s already exists [%s]", __func__, ifname, +		     zserv_command_string (command)); +	  return -1; +	} + +      zebra_pw_add (zvrf, ifname, protocol, client); +      break; +    case ZEBRA_PW_DELETE: +      if (!pw) +	{ +	  zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname, +		     zserv_command_string (command)); +	  return -1; +	} + +      zebra_pw_del (zvrf, pw); +      break; +    case ZEBRA_PW_SET: +    case ZEBRA_PW_UNSET: +      if (!pw) +	{ +	  zlog_warn ("%s: pseudowire %s not found [%s]", __func__, ifname, +		     zserv_command_string (command)); +	  return -1; +	} + +      switch (command) +	{ +	case ZEBRA_PW_SET: +	  pw->enabled = 1; +	  break; +	case ZEBRA_PW_UNSET: +	  pw->enabled = 0; +	  break; +	} + +      zebra_pw_change (pw, ifindex, type, af, &nexthop, local_label, +		       remote_label, flags, &data); +      break; +    } + +  return 0; +} +  /* Cleanup registered nexthops (across VRFs) upon client disconnect. */  static void  zebra_client_close_cleanup_rnh (struct zserv *client) @@ -1972,6 +2089,9 @@ zebra_client_close (struct zserv *client)    /* Release Label Manager chunks */    release_daemon_chunks (client->proto, client->instance); +  /* Remove pseudowires associated with this client */ +  zebra_pw_client_close (client); +    /* Close file descriptor. */    if (client->sock)      { @@ -2269,6 +2389,12 @@ zebra_client_read (struct thread *thread)      case ZEBRA_RELEASE_LABEL_CHUNK:        zread_label_manager_request (command, client, vrf_id);        break; +    case ZEBRA_PW_ADD: +    case ZEBRA_PW_DELETE: +    case ZEBRA_PW_SET: +    case ZEBRA_PW_UNSET: +      zread_pseudowire (command, client, length, vrf_id); +      break;      default:        zlog_info ("Zebra received unknown command %d", command);        break; diff --git a/zebra/zserv.h b/zebra/zserv.h index cd1948373a..42e762caa3 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -31,6 +31,8 @@  #include "zclient.h"  #include "zebra/zebra_ns.h" +#include "zebra/zebra_pw.h" +  /* Default port information. */  #define ZEBRA_VTY_PORT                2601 @@ -170,6 +172,7 @@ extern int zsend_interface_vrf_update (struct zserv *, struct interface *,                                         vrf_id_t);  extern int zsend_interface_link_params (struct zserv *, struct interface *); +extern int zsend_pw_update (struct zserv *, struct zebra_pw *);  extern pid_t pid;  | 
