summaryrefslogtreecommitdiff
path: root/ldpd
diff options
context:
space:
mode:
authorlynne <lynne@voltanet.io>2020-04-15 13:49:41 -0400
committerlynne <lynne@voltanet.io>2020-04-29 12:27:17 -0400
commit2d1aa1e8875ea38d6d2c2c79cca849399044261a (patch)
tree03bbd5094f44f0cbf3c51dc483951c27442ea20f /ldpd
parent5406061b2df1e7c57d514540007a53bb0ee74c00 (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.c331
-rw-r--r--ldpd/lde.h8
-rw-r--r--ldpd/ldp_zebra.c21
-rw-r--r--ldpd/ldpd.c5
-rw-r--r--ldpd/ldpd.h9
-rw-r--r--ldpd/ldpe.c35
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);
+}