]> git.puffer.fish Git - matthieu/frr.git/commitdiff
ldpd: fix ACL rule modification
authorlynne <lynne@voltanet.io>
Wed, 15 Apr 2020 17:49:41 +0000 (13:49 -0400)
committerlynne <lynne@voltanet.io>
Wed, 29 Apr 2020 16:27:17 +0000 (12:27 -0400)
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>
ldpd/lde.c
ldpd/lde.h
ldpd/ldp_zebra.c
ldpd/ldpd.c
ldpd/ldpd.h
ldpd/ldpe.c

index ae883078dd0db06139b881f8da5e113114db33d3..1bc7ffeae77cf2333fb08dd75006b0f7be7c43d2 100644 (file)
@@ -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);
+}
index 36196a3d081a053025b39b42e495577585d45e36..2895e00ae55f33eccd73ed778da6958e15b0a7a0 100644 (file)
@@ -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 *);
 
index b3ccb7760245dcf935e51f031d84ad4ed5db43c2..8a09424105a246e35089d0163d90c0e09d7a9613 100644 (file)
@@ -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
index 741c8c4655a229dea5777befa3da38f5c85cb9f6..ab10d0fb595562ee1dc899e95fc5f9f925af6d83 100644 (file)
@@ -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)
index a736b4ca37a1c73250cf9fe790ae94fa0fa85138..606fb372bbd2b758b04764456f3585a6b1baf25a 100644 (file)
@@ -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;
index b34a1ecdd7bd9661aeace07bd75ba81c09bdb28d..bae8a6e5c314c7216f8a3b269a270916a13c078f 100644 (file)
@@ -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);
+}