]> git.puffer.fish Git - matthieu/frr.git/commitdiff
ldpd: update local labels when necessary
authorRenato Westphal <renato@opensourcerouting.org>
Fri, 3 Feb 2017 13:09:27 +0000 (11:09 -0200)
committerRenato Westphal <renato@opensourcerouting.org>
Mon, 6 Feb 2017 15:05:41 +0000 (13:05 -0200)
ldpd allocates null labels for directly connected routes. If a connected
route is removed (interface goes down) and an IGP learned route takes its
place in the RIB, ldpd must update the local label of the associated FEC
entry with a non-null label. The same applies for the other way around
(an interface goes up and a connected route is selected in favour of an
IGP route). Labels should be dynamic and change when necessary.

Additionally, this patch fixes the processing of route delete messages
from zebra. Route delete messages don't contain any nexthop, meaning that
whenever we receive such messages we must delete all nexthop previously
received.

Based on a patch from Bingen Eguzkitza <bingen@voltanet.io>.
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
ldpd/l2vpn.c
ldpd/lde.c
ldpd/lde.h
ldpd/lde_lib.c
ldpd/ldp_zebra.c
ldpd/ldpd.h

index c1d0437fbafadfb258f134bf009a78ac91f3beaa..792608d4259677565310c2b13cbe8276d8e3d3e5 100644 (file)
@@ -211,6 +211,7 @@ l2vpn_pw_init(struct l2vpn_pw *pw)
        l2vpn_pw_fec(pw, &fec);
        lde_kernel_insert(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0,
            0, (void *)pw);
+       lde_kernel_update(&fec);
 }
 
 void
@@ -220,6 +221,7 @@ l2vpn_pw_exit(struct l2vpn_pw *pw)
 
        l2vpn_pw_fec(pw, &fec);
        lde_kernel_remove(&fec, AF_INET, (union ldpd_addr*)&pw->lsr_id, 0, 0);
+       lde_kernel_update(&fec);
 }
 
 static void
index 05267a82c4558ffe6a8f540a9aa9e031e1fb4af9..1323ba3d0245344f740cf61bb82b5fbd66f95c15 100644 (file)
@@ -414,8 +414,7 @@ lde_dispatch_parent(struct thread *thread)
 
                switch (imsg.hdr.type) {
                case IMSG_NETWORK_ADD:
-               case IMSG_NETWORK_ADD_END:
-               case IMSG_NETWORK_DEL:
+               case IMSG_NETWORK_UPDATE:
                        if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
                                log_warnx("%s: wrong imsg len", __func__);
                                break;
@@ -443,12 +442,8 @@ lde_dispatch_parent(struct thread *thread)
                                    kr.ifindex, kr.priority,
                                    kr.flags & F_CONNECTED, NULL);
                                break;
-                       case IMSG_NETWORK_ADD_END:
-                               lde_kernel_reevaluate(&fec);
-                               break;
-                       case IMSG_NETWORK_DEL:
-                               lde_kernel_remove(&fec, kr.af, &kr.nexthop,
-                                   kr.ifindex, kr.priority);
+                       case IMSG_NETWORK_UPDATE:
+                               lde_kernel_update(&fec);
                                break;
                        }
                        break;
@@ -584,28 +579,37 @@ lde_acl_check(char *acl_name, int af, union ldpd_addr *addr, uint8_t prefixlen)
 }
 
 uint32_t
-lde_assign_label(struct fec *fec, int connected)
+lde_update_label(struct fec_node *fn)
 {
-       static uint32_t label = MPLS_LABEL_RESERVED_MAX;
+       static uint32_t  label = MPLS_LABEL_RESERVED_MAX;
+       struct fec_nh   *fnh;
+       int              connected = 0;
+
+       LIST_FOREACH(fnh, &fn->nexthops, entry) {
+               if (fnh->flags & F_FEC_NH_CONNECTED) {
+                       connected = 1;
+                       break;
+               }
+       }
 
        /* should we allocate a label for this fec? */
-       switch (fec->type) {
+       switch (fn->fec.type) {
        case FEC_TYPE_IPV4:
                if ((ldeconf->ipv4.flags & F_LDPD_AF_ALLOCHOSTONLY) &&
-                   fec->u.ipv4.prefixlen != 32)
+                   fn->fec.u.ipv4.prefixlen != 32)
                        return (NO_LABEL);
                if (lde_acl_check(ldeconf->ipv4.acl_label_allocate_for,
-                   AF_INET, (union ldpd_addr *)&fec->u.ipv4.prefix,
-                   fec->u.ipv4.prefixlen) != FILTER_PERMIT)
+                   AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+                   fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
                        return (NO_LABEL);
                break;
        case FEC_TYPE_IPV6:
                if ((ldeconf->ipv6.flags & F_LDPD_AF_ALLOCHOSTONLY) &&
-                   fec->u.ipv6.prefixlen != 128)
+                   fn->fec.u.ipv6.prefixlen != 128)
                        return (NO_LABEL);
                if (lde_acl_check(ldeconf->ipv6.acl_label_allocate_for,
-                   AF_INET6, (union ldpd_addr *)&fec->u.ipv6.prefix,
-                   fec->u.ipv6.prefixlen) != FILTER_PERMIT)
+                   AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+                   fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
                        return (NO_LABEL);
                break;
        default:
@@ -614,29 +618,34 @@ lde_assign_label(struct fec *fec, int connected)
 
        if (connected) {
                /* choose implicit or explicit-null depending on configuration */
-               switch (fec->type) {
+               switch (fn->fec.type) {
                case FEC_TYPE_IPV4:
                        if (!(ldeconf->ipv4.flags & F_LDPD_AF_EXPNULL))
                                return (MPLS_LABEL_IMPLNULL);
                        if (lde_acl_check(ldeconf->ipv4.acl_label_expnull_for,
-                           AF_INET, (union ldpd_addr *)&fec->u.ipv4.prefix,
-                           fec->u.ipv4.prefixlen) != FILTER_PERMIT)
+                           AF_INET, (union ldpd_addr *)&fn->fec.u.ipv4.prefix,
+                           fn->fec.u.ipv4.prefixlen) != FILTER_PERMIT)
                                return (MPLS_LABEL_IMPLNULL);
                        return (MPLS_LABEL_IPV4NULL);
                case FEC_TYPE_IPV6:
                        if (!(ldeconf->ipv6.flags & F_LDPD_AF_EXPNULL))
                                return (MPLS_LABEL_IMPLNULL);
                        if (lde_acl_check(ldeconf->ipv6.acl_label_expnull_for,
-                           AF_INET6, (union ldpd_addr *)&fec->u.ipv6.prefix,
-                           fec->u.ipv6.prefixlen) != FILTER_PERMIT)
+                           AF_INET6, (union ldpd_addr *)&fn->fec.u.ipv6.prefix,
+                           fn->fec.u.ipv6.prefixlen) != FILTER_PERMIT)
                                return (MPLS_LABEL_IMPLNULL);
                        return (MPLS_LABEL_IPV6NULL);
                default:
-                       fatalx("lde_assign_label: unexpected fec type");
+                       fatalx("lde_update_label: unexpected fec type");
                        break;
                }
        }
 
+       /* preserve current label if there's no need to update it */
+       if (fn->local_label != NO_LABEL &&
+           fn->local_label > MPLS_LABEL_RESERVED_MAX)
+               return (fn->local_label);
+
        /*
         * TODO: request label to zebra or define a range of labels for ldpd.
         */
@@ -1371,7 +1380,7 @@ lde_change_egress_label(int af)
                        fatalx("lde_change_egress_label: unknown af");
                }
 
-               fn->local_label = lde_assign_label(&fn->fec, 1);
+               fn->local_label = lde_update_label(fn);
                if (fn->local_label != NO_LABEL)
                        RB_FOREACH(ln, nbr_tree, &lde_nbrs)
                                lde_send_labelmapping(ln, fn, 0);
index 7fa5219b121cdf22ec95be741b8df2e378f785bb..e0e9873d5ca842bc83e4b260f4590e551724fd18 100644 (file)
@@ -110,6 +110,7 @@ struct fec_nh {
        uint8_t                  flags;
 };
 #define F_FEC_NH_NEW           0x01
+#define F_FEC_NH_CONNECTED     0x02
 
 struct fec_node {
        struct fec               fec;
@@ -134,7 +135,7 @@ void                 lde(const char *, const char *);
 int             lde_imsg_compose_parent(int, pid_t, void *, uint16_t);
 int             lde_imsg_compose_ldpe(int, uint32_t, pid_t, void *, uint16_t);
 int             lde_acl_check(char *, int, union ldpd_addr *, uint8_t);
-uint32_t        lde_assign_label(struct fec *, int);
+uint32_t        lde_update_label(struct fec_node *);
 void            lde_send_change_klabel(struct fec_node *, struct fec_nh *);
 void            lde_send_delete_klabel(struct fec_node *, struct fec_nh *);
 void            lde_fec2map(struct fec *, struct map *);
@@ -174,7 +175,7 @@ void                 lde_kernel_insert(struct fec *, int, union ldpd_addr *,
                    ifindex_t, uint8_t, int, void *);
 void            lde_kernel_remove(struct fec *, int, union ldpd_addr *,
                    ifindex_t, uint8_t);
-void            lde_kernel_reevaluate(struct fec *);
+void            lde_kernel_update(struct fec *);
 void            lde_check_mapping(struct map *, struct lde_nbr *);
 void            lde_check_request(struct map *, struct lde_nbr *);
 void            lde_check_release(struct map *, struct lde_nbr *);
index 7a4cb760f13f68b8c48c122309099a3f3bc4bb21..234d373fbb145998705022d2cd1ed6f4f484ed76 100644 (file)
@@ -311,55 +311,19 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop,
 {
        struct fec_node         *fn;
        struct fec_nh           *fnh;
-       struct lde_map          *me;
-       struct lde_nbr          *ln;
 
        fn = (struct fec_node *)fec_find(&ft, fec);
        if (fn == NULL)
                fn = fec_add(fec);
-       fnh = fec_nh_find(fn, af, nexthop, ifindex, priority);
-       if (fnh != NULL) {
-               lde_send_change_klabel(fn, fnh);
-               fnh->flags |= F_FEC_NH_NEW;
-               return;
-       }
-
-       if (fn->fec.type == FEC_TYPE_PWID)
+       if (data)
                fn->data = data;
 
-       if (fn->local_label == NO_LABEL) {
-               fn->local_label = lde_assign_label(&fn->fec, connected);
-
-               /* FEC.1: perform lsr label distribution procedure */
-               if (fn->local_label != NO_LABEL)
-                       RB_FOREACH(ln, nbr_tree, &lde_nbrs)
-                               lde_send_labelmapping(ln, fn, 1);
-       }
-
-       fnh = fec_nh_add(fn, af, nexthop, ifindex, priority);
+       fnh = fec_nh_find(fn, af, nexthop, ifindex, priority);
+       if (fnh == NULL)
+               fnh = fec_nh_add(fn, af, nexthop, ifindex, priority);
        fnh->flags |= F_FEC_NH_NEW;
-       lde_send_change_klabel(fn, fnh);
-
-       switch (fn->fec.type) {
-       case FEC_TYPE_IPV4:
-       case FEC_TYPE_IPV6:
-               ln = lde_nbr_find_by_addr(af, &fnh->nexthop);
-               break;
-       case FEC_TYPE_PWID:
-               ln = lde_nbr_find_by_lsrid(fn->fec.u.pwid.lsr_id);
-               break;
-       default:
-               ln = NULL;
-               break;
-       }
-
-       if (ln) {
-               /* FEC.2  */
-               me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
-               if (me)
-                       /* FEC.5 */
-                       lde_check_mapping(&me->map, ln);
-       }
+       if (connected)
+               fnh->flags |= F_FEC_NH_CONNECTED;
 }
 
 void
@@ -380,12 +344,6 @@ lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop,
 
        lde_send_delete_klabel(fn, fnh);
        fec_nh_del(fnh);
-       if (LIST_EMPTY(&fn->nexthops)) {
-               lde_send_labelwithdraw_all(fn, NO_LABEL);
-               fn->local_label = NO_LABEL;
-               if (fn->fec.type == FEC_TYPE_PWID)
-                       fn->data = NULL;
-       }
 }
 
 /*
@@ -395,10 +353,12 @@ lde_kernel_remove(struct fec *fec, int af, union ldpd_addr *nexthop,
  * them (if any), withdraw the associated labels from zebra.
  */
 void
-lde_kernel_reevaluate(struct fec *fec)
+lde_kernel_update(struct fec *fec)
 {
        struct fec_node         *fn;
        struct fec_nh           *fnh, *safe;
+       struct lde_nbr          *ln;
+       struct lde_map          *me;
 
        fn = (struct fec_node *)fec_find(&ft, fec);
        if (fn == NULL)
@@ -407,9 +367,53 @@ lde_kernel_reevaluate(struct fec *fec)
        LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) {
                if (fnh->flags & F_FEC_NH_NEW)
                        fnh->flags &= ~F_FEC_NH_NEW;
-               else
-                       lde_kernel_remove(fec, fnh->af, &fnh->nexthop,
-                           fnh->ifindex, fnh->priority);
+               else {
+                       lde_send_delete_klabel(fn, fnh);
+                       fec_nh_del(fnh);
+               }
+       }
+
+       if (LIST_EMPTY(&fn->nexthops)) {
+               lde_send_labelwithdraw_all(fn, NO_LABEL);
+               fn->local_label = NO_LABEL;
+               fn->data = NULL;
+       } else {
+               uint32_t         previous_label;
+
+               previous_label = fn->local_label;
+               fn->local_label = lde_update_label(fn);
+
+               if (fn->local_label != NO_LABEL &&
+                   fn->local_label != previous_label) {
+                       /* FEC.1: perform lsr label distribution procedure */
+                       RB_FOREACH(ln, nbr_tree, &lde_nbrs)
+                               lde_send_labelmapping(ln, fn, 1);
+               }
+       }
+
+       LIST_FOREACH(fnh, &fn->nexthops, entry) {
+               lde_send_change_klabel(fn, fnh);
+
+               switch (fn->fec.type) {
+               case FEC_TYPE_IPV4:
+               case FEC_TYPE_IPV6:
+                       ln = lde_nbr_find_by_addr(fnh->af, &fnh->nexthop);
+                       break;
+               case FEC_TYPE_PWID:
+                       ln = lde_nbr_find_by_lsrid(fn->fec.u.pwid.lsr_id);
+                       break;
+               default:
+                       ln = NULL;
+                       break;
+               }
+
+               if (ln) {
+                       /* FEC.2  */
+                       me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
+                       if (me)
+                               /* FEC.5 */
+                               lde_check_mapping(&me->map, ln);
+               }
        }
 }
 
index 79c4f5b3778f8007559d1fd9cebb9913a0f6941c..12954b91af6791ac862adf03e932f1a9d6fb2d7d 100644 (file)
@@ -353,7 +353,7 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
        u_char                   type;
        u_char                   message_flags;
        struct kroute            kr;
-       int                      nhnum, nhlen;
+       int                      nhnum = 0, nhlen;
        size_t                   nhmark;
 
        memset(&kr, 0, sizeof(kr));
@@ -374,8 +374,6 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
        stream_getl(s); /* flags, unused */
        stream_getw(s); /* instance, unused */
        message_flags = stream_getc(s);
-       if (!CHECK_FLAG(message_flags, ZAPI_MESSAGE_NEXTHOP))
-               return (0);
 
        switch (command) {
        case ZEBRA_REDISTRIBUTE_IPV4_ADD:
@@ -409,16 +407,35 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
                        return (0);
        }
 
-       nhnum = stream_getc(s);
-       nhmark = stream_get_getp(s);
-       stream_set_getp(s, nhmark + nhnum * (nhlen + 5));
+       if (CHECK_FLAG(message_flags, ZAPI_MESSAGE_NEXTHOP)) {
+               nhnum = stream_getc(s);
+               nhmark = stream_get_getp(s);
+               stream_set_getp(s, nhmark + nhnum * (nhlen + 5));
+       }
 
        if (CHECK_FLAG(message_flags, ZAPI_MESSAGE_DISTANCE))
                kr.priority = stream_getc(s);
        if (CHECK_FLAG(message_flags, ZAPI_MESSAGE_METRIC))
                stream_getl(s); /* metric, not used */
 
-       stream_set_getp(s, nhmark);
+       if (CHECK_FLAG(message_flags, ZAPI_MESSAGE_NEXTHOP))
+               stream_set_getp(s, nhmark);
+
+       if (nhnum == 0) {
+               switch (command) {
+               case ZEBRA_REDISTRIBUTE_IPV4_ADD:
+               case ZEBRA_REDISTRIBUTE_IPV6_ADD:
+                       return (0);
+               case ZEBRA_REDISTRIBUTE_IPV4_DEL:
+               case ZEBRA_REDISTRIBUTE_IPV6_DEL:
+                       debug_zebra_in("route delete %s/%d (%s)",
+                           log_addr(kr.af, &kr.prefix), kr.prefixlen,
+                           zebra_route_string(type));
+                       break;
+               default:
+                       fatalx("ldp_zebra_read_route: unknown command");
+               }
+       }
 
        /* loop through all the nexthops */
        for (; nhnum > 0; nhnum--) {
@@ -445,23 +462,12 @@ ldp_zebra_read_route(int command, struct zclient *zclient, zebra_size_t length,
                        main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr,
                            sizeof(kr));
                        break;
-               case ZEBRA_REDISTRIBUTE_IPV4_DEL:
-               case ZEBRA_REDISTRIBUTE_IPV6_DEL:
-                       debug_zebra_in("route delete %s/%d nexthop %s "
-                           "ifindex %u (%s)", log_addr(kr.af, &kr.prefix),
-                           kr.prefixlen, log_addr(kr.af, &kr.nexthop),
-                           kr.ifindex, zebra_route_string(type));
-                       main_imsg_compose_lde(IMSG_NETWORK_DEL, 0, &kr,
-                           sizeof(kr));
-                       break;
                default:
-                       fatalx("ldp_zebra_read_route: unknown command");
+                       break;
                }
        }
 
-       if (command == ZEBRA_REDISTRIBUTE_IPV4_ADD ||
-           command == ZEBRA_REDISTRIBUTE_IPV6_ADD)
-               main_imsg_compose_lde(IMSG_NETWORK_ADD_END, 0, &kr, sizeof(kr));
+       main_imsg_compose_lde(IMSG_NETWORK_UPDATE, 0, &kr, sizeof(kr));
 
        return (0);
 }
index fa3789a8394435fafd796d41ec0d924c24518b10..ff3af19db4729d5ce90b5b0ebd7408036b340e2d 100644 (file)
@@ -122,8 +122,7 @@ enum imsg_type {
        IMSG_NEIGHBOR_UP,
        IMSG_NEIGHBOR_DOWN,
        IMSG_NETWORK_ADD,
-       IMSG_NETWORK_ADD_END,
-       IMSG_NETWORK_DEL,
+       IMSG_NETWORK_UPDATE,
        IMSG_SOCKET_IPC,
        IMSG_SOCKET_NET,
        IMSG_CLOSE_SOCKETS,