From 2d1aa1e8875ea38d6d2c2c79cca849399044261a Mon Sep 17 00:00:00 2001 From: lynne Date: Wed, 15 Apr 2020 13:49:41 -0400 Subject: [PATCH] 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 Signed-off-by: Karen Schoener --- ldpd/lde.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++- ldpd/lde.h | 8 +- ldpd/ldp_zebra.c | 21 +++ ldpd/ldpd.c | 5 +- ldpd/ldpd.h | 9 +- 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); @@ -1202,6 +1217,82 @@ lde_send_labelrelease(struct lde_nbr *ln, struct fec_node *fn, lde_imsg_compose_ldpe(IMSG_RELEASE_ADD_END, ln->peerid, 0, NULL, 0); } +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); +} -- 2.39.5