diff options
| author | lynne <lynne@voltanet.io> | 2020-04-15 13:49:41 -0400 | 
|---|---|---|
| committer | lynne <lynne@voltanet.io> | 2020-04-29 12:27:17 -0400 | 
| commit | 2d1aa1e8875ea38d6d2c2c79cca849399044261a (patch) | |
| tree | 03bbd5094f44f0cbf3c51dc483951c27442ea20f /ldpd | |
| parent | 5406061b2df1e7c57d514540007a53bb0ee74c00 (diff) | |
ldpd: fix ACL rule modification
Changes to ACL rules were not applied to LDP.  This fix allows
LDP to be notified when a rule in an ACL filter is modified by
the user. The filter is properly applied to the LDP session.
The filter may cause a LDP session to go down/up or to remove/add
labels being advertised/received from a neighbor.
Signed-off-by: Lynne Morrison <lynne@voltanet.io>
Signed-off-by: Karen Schoener <karen@voltanet.io>
Diffstat (limited to 'ldpd')
| -rw-r--r-- | ldpd/lde.c | 331 | ||||
| -rw-r--r-- | ldpd/lde.h | 8 | ||||
| -rw-r--r-- | ldpd/ldp_zebra.c | 21 | ||||
| -rw-r--r-- | ldpd/ldpd.c | 5 | ||||
| -rw-r--r-- | ldpd/ldpd.h | 9 | ||||
| -rw-r--r-- | ldpd/ldpe.c | 35 | 
6 files changed, 401 insertions, 8 deletions
diff --git a/ldpd/lde.c b/ldpd/lde.c index ae883078dd..1bc7ffeae7 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -63,6 +63,8 @@ static void		 on_get_label_chunk_response(uint32_t start, uint32_t end);  static uint32_t		 lde_get_next_label(void);  static bool		 lde_fec_connected(const struct fec_node *);  static bool		 lde_fec_outside_mpls_network(const struct fec_node *); +static void		 lde_check_filter_af(int, struct ldpd_af_conf *, +			     const char *);  RB_GENERATE(nbr_tree, lde_nbr, entry, lde_nbr_compare)  RB_GENERATE(lde_map_head, lde_map, entry, lde_map_compare) @@ -443,6 +445,7 @@ lde_dispatch_parent(struct thread *thread)  	ssize_t			 n;  	int			 shut = 0;  	struct fec		 fec; +	struct ldp_access	*laccess;  	iev->ev_read = NULL; @@ -635,6 +638,18 @@ lde_dispatch_parent(struct thread *thread)  			}  			memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug));  			break; +		case IMSG_FILTER_UPDATE: +			if (imsg.hdr.len != IMSG_HEADER_SIZE + +			    sizeof(struct ldp_access)) { +				log_warnx("%s: wrong imsg len", __func__); +				break; +			} +			laccess = imsg.data; +			lde_check_filter_af(AF_INET, &ldeconf->ipv4, +				laccess->name); +			lde_check_filter_af(AF_INET6, &ldeconf->ipv6, +				laccess->name); +			break;  		default:  			log_debug("%s: unexpected imsg %d", __func__,  			    imsg.hdr.type); @@ -1203,6 +1218,82 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn,  }  void +lde_send_labelrequest(struct lde_nbr *ln, struct fec_node *fn, +		      struct map *wcard, int single) +{ +	struct map		 map; +	struct fec		*f; +	struct lde_req		*lre; + +	if (fn) { +		lde_fec2map(&fn->fec, &map); +		switch (fn->fec.type) { +		case FEC_TYPE_IPV4: +			if (!ln->v4_enabled) +				return; +			break; +		case FEC_TYPE_IPV6: +			if (!ln->v6_enabled) +				return; +			break; +		default: +			fatalx("lde_send_labelrequest: unknown af"); +		} +	} else +		memcpy(&map, wcard, sizeof(map)); + +	map.label = NO_LABEL; + +	if (fn) { +		/* SLR1.1: has label request for FEC been previously sent +		 * and still outstanding just return, +		 */ +		lre = (struct  lde_req *)fec_find(&ln->sent_req, &fn->fec); +		if (lre == NULL) { +			/* SLRq.3: send label request */ +			lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, ln->peerid, 0, +			    &map, sizeof(map)); +			if (single) +				lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, +				    ln->peerid, 0, NULL, 0); + +			/* SLRq.4: record sent request */ +			lde_req_add(ln, &fn->fec, 1); +		} +	} else { +		/* if Wilcard just send label request */ +		/* SLRq.3: send label request */ +		lde_imsg_compose_ldpe(IMSG_REQUEST_ADD, +		    ln->peerid, 0, &map, sizeof(map)); +		if (single) +			lde_imsg_compose_ldpe(IMSG_REQUEST_ADD_END, +			    ln->peerid, 0, NULL, 0); + +		/* SLRq.4: record sent request */ +		RB_FOREACH(f, fec_tree, &ft) { +			fn = (struct fec_node *)f; +			lre = (struct lde_req *)fec_find(&ln->sent_req, &fn->fec); +			if (lde_wildcard_apply(wcard, &fn->fec, NULL) == 0) +				continue; +			if (lre == NULL) +				lde_req_add(ln, f, 1); +		} +	} +} + +void +lde_send_labelrequest_wcard(struct lde_nbr *ln, uint16_t af) +{ +	struct map	 wcard; + +	memset(&wcard, 0, sizeof(wcard)); +	wcard.type = MAP_TYPE_TYPED_WCARD; +	wcard.fec.twcard.type = MAP_TYPE_PREFIX; +	wcard.fec.twcard.u.prefix_af = af; +	lde_send_labelrequest(ln, NULL, &wcard, 1); +} + +void  lde_send_notification(struct lde_nbr *ln, uint32_t status_code, uint32_t msg_id,      uint16_t msg_type)  { @@ -1638,13 +1729,14 @@ lde_change_egress_label(int af)  }  void -lde_change_host_label(int af) +lde_change_allocate_filter(int af)  {  	struct lde_nbr  *ln;  	struct fec      *f;  	struct fec_node *fn;  	uint32_t         new_label; +	/* reallocate labels for fecs that match this filter */  	RB_FOREACH(f, fec_tree, &ft) {  		fn = (struct fec_node *)f; @@ -1658,7 +1750,7 @@ lde_change_host_label(int af)  				continue;  			break;  		default: -			fatalx("lde_change_host_label: unknown af"); +			fatalx("lde_change_allocate_filter: unknown af");  		}  		/* @@ -1687,6 +1779,225 @@ lde_change_host_label(int af)  		    NULL, 0);  } +void +lde_change_advertise_filter(int af) +{ +	struct lde_nbr  *ln; +	struct fec      *f; +	struct fec_node *fn; +	char            *acl_to_filter; +	char            *acl_for_filter; +	union ldpd_addr *prefix; +	uint8_t          plen; +	struct lde_map  *me; + +	/* advertise label for fecs to neighbors if matches advertise filters */ +	switch (af) { +	case AF_INET: +		acl_to_filter = ldeconf->ipv4.acl_label_advertise_to; +		acl_for_filter = ldeconf->ipv4.acl_label_advertise_for; +		break; +	case AF_INET6: +		acl_to_filter = ldeconf->ipv6.acl_label_advertise_to; +		acl_for_filter = ldeconf->ipv6.acl_label_advertise_for; +		break; +	default: +		fatalx("lde_change_advertise_filter: unknown af"); +	} + +	RB_FOREACH(ln, nbr_tree, &lde_nbrs) { +		if (lde_acl_check(acl_to_filter, af, (union ldpd_addr *)&ln->id, +		    IPV4_MAX_BITLEN) != FILTER_PERMIT) +			lde_send_labelwithdraw_wcard(ln, NO_LABEL); +		else { +			/* This neighbor is allowed in to_filter, so +			 * send labels if fec also matches for_filter +			 */ +			RB_FOREACH(f, fec_tree, &ft) { +				fn = (struct fec_node *)f; +				switch (af) { +				case AF_INET: +					if (fn->fec.type != FEC_TYPE_IPV4) +						continue; +					prefix = (union ldpd_addr *) +					    &fn->fec.u.ipv4.prefix; +					plen = fn->fec.u.ipv4.prefixlen; +					break; +				case FEC_TYPE_IPV6: +					if (fn->fec.type != FEC_TYPE_IPV6) +						continue; +					prefix = (union ldpd_addr *) +					    &fn->fec.u.ipv6.prefix; +					plen = fn->fec.u.ipv6.prefixlen; +					break; +				default: +					continue; +				} +				if (lde_acl_check(acl_for_filter, af, +				    prefix, plen) != FILTER_PERMIT) { +					me = (struct lde_map *)fec_find( +					    &ln->sent_map, &fn->fec); +					if (me) +						/* fec filtered withdraw */ +						lde_send_labelwithdraw(ln, fn, +						    NULL, NULL); +				} else +					/* fec allowed send map */ +					lde_send_labelmapping(ln, fn, 0); +			} +			lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, +			    ln->peerid, 0, NULL, 0); +		} +	} +} + + +void +lde_change_accept_filter(int af) +{ +	struct lde_nbr  *ln; +	struct fec      *f; +	struct fec_node *fn; +	char            *acl_for_filter; +	char            *acl_from_filter; +	union ldpd_addr *prefix; +	uint8_t          plen; +	struct lde_map  *me; +	enum fec_type    type; + +	/* accept labels from neighbors specified in the from_filter and for +	 * fecs defined in the for_filter +	 */ +	switch (af) { +	case AF_INET: +		acl_for_filter = ldeconf->ipv4.acl_label_accept_for; +		acl_from_filter = ldeconf->ipv4.acl_label_accept_from; +		type = FEC_TYPE_IPV4; +		break; +	case AF_INET6: +		acl_for_filter = ldeconf->ipv6.acl_label_accept_for; +		acl_from_filter = ldeconf->ipv6.acl_label_accept_from; +		type = FEC_TYPE_IPV6; +		break; +	default: +		fatalx("lde_change_accept_filter: unknown af"); +	} + +	RB_FOREACH(ln, nbr_tree, &lde_nbrs) { +		if (lde_acl_check(acl_from_filter, AF_INET, (union ldpd_addr *) +		    &ln->id, IPV4_MAX_BITLEN) != FILTER_PERMIT) { +			/* This neighbor is now filtered so remove fecs from +			 * recv list +			 */ +			RB_FOREACH(f, fec_tree, &ft) { +				fn = (struct fec_node *)f; +				if (fn->fec.type == type) { +					me = (struct lde_map *)fec_find( +					    &ln->recv_map, &fn->fec); +					if (me) +						lde_map_del(ln, me, 0); +				} +			} +		} else if (ln->flags & F_NBR_CAP_TWCARD) { +			/* This neighbor is allowed and supports type +			 * wildcard so send a labelrequest +			 * to get any new labels from neighbor +			 * and make sure any fecs we currently have +			 * match for_filter. +			 */ +			RB_FOREACH(f, fec_tree, &ft) { +				fn = (struct fec_node *)f; +				switch (af) { +				case AF_INET: +					if (fn->fec.type != FEC_TYPE_IPV4) +						continue; +					prefix = (union ldpd_addr *) +					    &fn->fec.u.ipv4.prefix; +					plen = fn->fec.u.ipv4.prefixlen; +					break; +				case AF_INET6: +					if (fn->fec.type != FEC_TYPE_IPV6) +						continue; +					prefix = (union ldpd_addr *) +					    &fn->fec.u.ipv6.prefix; +					plen = fn->fec.u.ipv6.prefixlen; +					break; +				default: +					continue; +				} +				if (lde_acl_check(acl_for_filter, af, +				    prefix, plen) != FILTER_PERMIT) { +					me = (struct lde_map *)fec_find( +					    &ln->recv_map, &fn->fec); +					if (me) +						lde_map_del(ln, me, 0); +				} +			} +			lde_send_labelrequest_wcard(ln, af); +		} else +			/* Type Wildcard is not supported so restart session */ +			lde_imsg_compose_ldpe(IMSG_NBR_SHUTDOWN, ln->peerid, 0, +			    NULL, 0); +	} +} + +void +lde_change_expnull_for_filter(int af) +{ +	struct lde_nbr  *ln; +	struct fec      *f; +	struct fec_node *fn; +	char            *acl_name; +	uint32_t         exp_label; +	union ldpd_addr *prefix; +	uint8_t          plen; + +	/* Configure explicit-null advertisement for all fecs in this filter */ +	RB_FOREACH(f, fec_tree, &ft) { +		fn = (struct fec_node *)f; + +		switch (af) { +		case AF_INET: +			if (fn->fec.type != FEC_TYPE_IPV4) +				continue; +			acl_name = ldeconf->ipv4.acl_label_expnull_for; +			prefix = (union ldpd_addr *)&fn->fec.u.ipv4.prefix; +			plen = fn->fec.u.ipv4.prefixlen; +			exp_label = MPLS_LABEL_IPV4_EXPLICIT_NULL; +			break; +		case AF_INET6: +			if (fn->fec.type != FEC_TYPE_IPV6) +				continue; +			acl_name = ldeconf->ipv6.acl_label_expnull_for; +			prefix = (union ldpd_addr *)&fn->fec.u.ipv6.prefix; +			plen = fn->fec.u.ipv6.prefixlen; +			exp_label = MPLS_LABEL_IPV6_EXPLICIT_NULL; +			break; +		default: +			fatalx("lde_change_expnull_for_filter: unknown af"); +		} + +		if (lde_acl_check(acl_name, af, prefix, plen) == FILTER_PERMIT) { +			/* for this fec change any imp-null to exp-null */ +			if (fn->local_label == MPLS_LABEL_IMPLICIT_NULL) { +				fn->local_label= lde_update_label(fn); +				RB_FOREACH(ln, nbr_tree, &lde_nbrs) +					lde_send_labelmapping(ln, fn, 0); +			} +		} else { +			/* for this fec change any exp-null back to imp-null */ +			if (fn->local_label == exp_label) { +				fn->local_label = lde_update_label(fn); +				RB_FOREACH(ln, nbr_tree, &lde_nbrs) +					lde_send_labelmapping(ln, fn, 0); +			} +		} +	} +	RB_FOREACH(ln, nbr_tree, &lde_nbrs) +		lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, 0, +		    NULL, 0); +} +  static int  lde_address_add(struct lde_nbr *ln, struct lde_addr *lde_addr)  { @@ -1917,3 +2228,19 @@ end:  	return (label);  } + +static void +lde_check_filter_af(int af, struct ldpd_af_conf *af_conf, +    const char *filter_name) +{ +	if (strcmp(af_conf->acl_label_allocate_for, filter_name) == 0) +		lde_change_allocate_filter(af); +	if ((strcmp(af_conf->acl_label_advertise_to, filter_name) == 0) +	    || (strcmp(af_conf->acl_label_advertise_for, filter_name) == 0)) +		lde_change_advertise_filter(af); +	if ((strcmp(af_conf->acl_label_accept_for, filter_name) == 0) +	    || (strcmp(af_conf->acl_label_accept_from, filter_name) == 0)) +		lde_change_accept_filter(af); +	if (strcmp(af_conf->acl_label_expnull_for, filter_name) == 0) +		lde_change_expnull_for_filter(af); +} diff --git a/ldpd/lde.h b/ldpd/lde.h index 36196a3d08..2895e00ae5 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -168,6 +168,9 @@ void		 lde_send_labelwithdraw_pwid_wcard(struct lde_nbr *, uint16_t,  		    uint32_t);  void		 lde_send_labelrelease(struct lde_nbr *, struct fec_node *,  		    struct map *, uint32_t); +void		 lde_send_labelrequest(struct lde_nbr *, struct fec_node *, +		     struct map *, int); +void		 lde_send_labelrequest_wcard(struct lde_nbr *, uint16_t af);  void		 lde_send_notification(struct lde_nbr *, uint32_t, uint32_t,  		    uint16_t);  void		 lde_send_notification_eol_prefix(struct lde_nbr *, int); @@ -183,7 +186,10 @@ void		 lde_req_del(struct lde_nbr *, struct lde_req *, int);  struct lde_wdraw *lde_wdraw_add(struct lde_nbr *, struct fec_node *);  void		 lde_wdraw_del(struct lde_nbr *, struct lde_wdraw *);  void		 lde_change_egress_label(int); -void		 lde_change_host_label(int); +void		 lde_change_allocate_filter(int); +void		 lde_change_advertise_filter(int); +void		 lde_change_accept_filter(int); +void		 lde_change_expnull_for_filter(int);  struct lde_addr	*lde_address_find(struct lde_nbr *, int,  		    union ldpd_addr *); diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index b3ccb77602..8a09424105 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -44,6 +44,7 @@ static int	 ldp_interface_address_delete(ZAPI_CALLBACK_ARGS);  static int	 ldp_zebra_read_route(ZAPI_CALLBACK_ARGS);  static int	 ldp_zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS);  static void	 ldp_zebra_connected(struct zclient *); +static void	 ldp_zebra_filter_update(struct access_list *access);  static struct zclient	*zclient; @@ -515,6 +516,22 @@ ldp_zebra_connected(struct zclient *zclient)  	    ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT);  } +static void +ldp_zebra_filter_update(struct access_list *access) +{ +	struct ldp_access laccess; + +	if (access && access->name[0] != '\0') { +		strlcpy(laccess.name, access->name, sizeof(laccess.name)); +		laccess.type = access->type; +		debug_evt("%s ACL update filter name %s type %d", __func__, +		    access->name, access->type); + +		main_imsg_compose_both(IMSG_FILTER_UPDATE, &laccess, +			sizeof(laccess)); +	} +} +  extern struct zebra_privs_t ldpd_privs;  void @@ -535,6 +552,10 @@ ldp_zebra_init(struct thread_master *master)  	zclient->redistribute_route_add = ldp_zebra_read_route;  	zclient->redistribute_route_del = ldp_zebra_read_route;  	zclient->pw_status_update = ldp_zebra_read_pw_status_update; + +	/* Access list initialize. */ +	access_list_add_hook(ldp_zebra_filter_update); +	access_list_delete_hook(ldp_zebra_filter_update);  }  void diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 741c8c4655..ab10d0fb59 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -1365,8 +1365,7 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa)  	}  	/* update ACLs */ -	if (strcmp(af_conf->acl_label_allocate_for, -	    xa->acl_label_allocate_for)) +	if (strcmp(af_conf->acl_label_allocate_for, xa->acl_label_allocate_for))  		change_host_label = 1;  	if (strcmp(af_conf->acl_label_advertise_to, @@ -1403,7 +1402,7 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa)  		if (change_egress_label)  			lde_change_egress_label(af);  		if (change_host_label) -			lde_change_host_label(af); +			lde_change_allocate_filter(af);  		break;  	case PROC_LDP_ENGINE:  		if (stop_init_backoff) diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index a736b4ca37..606fb372bb 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -151,7 +151,9 @@ enum imsg_type {  	IMSG_LOG,  	IMSG_ACL_CHECK,  	IMSG_INIT, -	IMSG_PW_UPDATE +	IMSG_PW_UPDATE, +	IMSG_FILTER_UPDATE, +	IMSG_NBR_SHUTDOWN  };  struct ldpd_init { @@ -162,6 +164,11 @@ struct ldpd_init {  	unsigned short instance;  }; +struct ldp_access { +	char			 name[ACL_NAMSIZ]; +	enum access_type	 type; +}; +  union ldpd_addr {  	struct in_addr	v4;  	struct in6_addr	v6; diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index b34a1ecdd7..bae8a6e5c3 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -42,6 +42,7 @@ static int	 ldpe_dispatch_pfkey(struct thread *);  static void	 ldpe_setup_sockets(int, int, int, int);  static void	 ldpe_close_sockets(int);  static void	 ldpe_iface_af_ctl(struct ctl_conn *c, int af, ifindex_t ifidx); +static void	 ldpe_check_filter_af(int, struct ldpd_af_conf *, const char *);  struct ldpd_conf	*leconf;  #ifdef __OpenBSD__ @@ -292,7 +293,8 @@ ldpe_dispatch_main(struct thread *thread)  	struct nbr_params	*nbrp;  #endif  	int			 n, shut = 0; - +	struct ldp_access       *laccess; +	  	iev->ev_read = NULL;  	if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) @@ -544,6 +546,18 @@ ldpe_dispatch_main(struct thread *thread)  			}  			memcpy(&ldp_debug, imsg.data, sizeof(ldp_debug));  			break; +		case IMSG_FILTER_UPDATE: +			if (imsg.hdr.len != IMSG_HEADER_SIZE + +			    sizeof(struct ldp_access)) { +				log_warnx("%s: wrong imsg len", __func__); +				break; +			} +			laccess = imsg.data; +			ldpe_check_filter_af(AF_INET, &leconf->ipv4, +				laccess->name); +			ldpe_check_filter_af(AF_INET6, &leconf->ipv6, +				laccess->name); +			break;  		default:  			log_debug("ldpe_dispatch_main: error handling imsg %d",  			    imsg.hdr.type); @@ -680,6 +694,17 @@ ldpe_dispatch_lde(struct thread *thread)  		case IMSG_CTL_SHOW_L2VPN_BINDING:  			control_imsg_relay(&imsg);  			break; +		case IMSG_NBR_SHUTDOWN: +			nbr = nbr_find_peerid(imsg.hdr.peerid); +			if (nbr == NULL) { +				log_debug("ldpe_dispatch_lde: cannot find " +				    "neighbor"); +				break; +			} +			if (nbr->state != NBR_STA_OPER) +				break; +			session_shutdown(nbr,S_SHUTDOWN,0,0); +			break;  		default:  			log_debug("ldpe_dispatch_lde: error handling imsg %d",  			    imsg.hdr.type); @@ -980,3 +1005,11 @@ mapping_list_clr(struct mapping_head *mh)  		free(me);  	}  } + +void +ldpe_check_filter_af(int af, struct ldpd_af_conf *af_conf, +    const char *filter_name) +{ +	if (strcmp(af_conf->acl_thello_accept_from, filter_name) == 0) +		ldpe_remove_dynamic_tnbrs(af); +}  | 
