]> git.proxmox.com Git - mirror_frr.git/commitdiff
isisd: learn and advertise IPv6 dst-src routes
authorChristian Franke <chris@opensourcerouting.org>
Sun, 22 Jul 2018 19:49:02 +0000 (15:49 -0400)
committerChristian Franke <chris@opensourcerouting.org>
Fri, 3 Aug 2018 11:25:39 +0000 (13:25 +0200)
Receive IPv6 dst-src routes from zebra and advertise them in our LSPs
if so configured.

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
isisd/isis_lsp.c
isisd/isis_mt.c
isisd/isis_mt.h
isisd/isis_redist.c
isisd/isis_redist.h
isisd/isis_tlvs.c
isisd/isis_tlvs.h
isisd/isis_zebra.c
lib/zebra.h
tests/isisd/test_fuzz_isis_tlv_tests.h.gz

index c8ef829064b54bfa268ee21a284f49a8fec8f5c2..37651163f58b48cc1cfa20abeaca891fc507334d 100644 (file)
@@ -37,6 +37,7 @@
 #include "checksum.h"
 #include "md5.h"
 #include "table.h"
+#include "srcdest_table.h"
 
 #include "isisd/dict.h"
 #include "isisd/isis_constants.h"
@@ -765,18 +766,28 @@ static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp,
                return;
 
        for (struct route_node *rn = route_top(er_table); rn;
-            rn = route_next(rn)) {
+            rn = srcdest_route_next(rn)) {
                if (!rn->info)
                        continue;
-
-               struct prefix_ipv6 *ipv6 = (struct prefix_ipv6 *)&rn->p;
                struct isis_ext_info *info = rn->info;
 
+               struct prefix_ipv6 *p, *src_p;
+               srcdest_rnode_prefixes(rn, (const struct prefix **)&p,
+                                      (const struct prefix **)&src_p);
+
                uint32_t metric = info->metric;
                if (info->metric > MAX_WIDE_PATH_METRIC)
                        metric = MAX_WIDE_PATH_METRIC;
-               isis_tlvs_add_ipv6_reach(
-                       lsp->tlvs, isis_area_ipv6_topology(area), ipv6, metric);
+
+               if (!src_p || !src_p->prefixlen) {
+                       isis_tlvs_add_ipv6_reach(lsp->tlvs,
+                                                isis_area_ipv6_topology(area),
+                                                p, metric);
+               } else if (isis_area_ipv6_dstsrc_enabled(area)) {
+                       isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
+                                                       ISIS_MT_IPV6_DSTSRC,
+                                                       p, src_p, metric);
+               }
        }
 }
 
index d13f2a13f314ddb15a9a05566b9e88cb5c3192a9..2155bf584ec13d78aebd0d0d9401a365c3059093 100644 (file)
@@ -33,6 +33,14 @@ DEFINE_MTYPE_STATIC(ISISD, MT_AREA_SETTING, "ISIS MT Area Setting")
 DEFINE_MTYPE_STATIC(ISISD, MT_CIRCUIT_SETTING, "ISIS MT Circuit Setting")
 DEFINE_MTYPE_STATIC(ISISD, MT_ADJ_INFO, "ISIS MT Adjacency Info")
 
+bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area)
+{
+       struct isis_area_mt_setting *area_mt_setting;
+       area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_DSTSRC);
+
+       return (area_mt_setting && area_mt_setting->enabled);
+}
+
 uint16_t isis_area_ipv6_topology(struct isis_area *area)
 {
        struct isis_area_mt_setting *area_mt_setting;
@@ -61,6 +69,8 @@ const char *isis_mtid2str(uint16_t mtid)
                return "ipv6-multicast";
        case ISIS_MT_IPV6_MGMT:
                return "ipv6-mgmt";
+       case ISIS_MT_IPV6_DSTSRC:
+               return "ipv6-dstsrc";
        default:
                snprintf(buf, sizeof(buf), "%" PRIu16, mtid);
                return buf;
@@ -81,6 +91,8 @@ uint16_t isis_str2mtid(const char *name)
                return ISIS_MT_IPV6_MULTICAST;
        if (!strcmp(name, "ipv6-mgmt"))
                return ISIS_MT_IPV6_MGMT;
+       if (!strcmp(name, "ipv6-dstsrc"))
+               return ISIS_MT_IPV6_DSTSRC;
        return -1;
 }
 
index 95aa99dba0bd6be1b87be4bb9028020b24512e70..8c57d24afa4dae95fca6c9afaed6dae0869e677b 100644 (file)
@@ -32,6 +32,7 @@
 #define ISIS_MT_IPV4_MULTICAST 3
 #define ISIS_MT_IPV6_MULTICAST 4
 #define ISIS_MT_IPV6_MGMT      5
+#define ISIS_MT_IPV6_DSTSRC    3996 /* FIXME: IANA */
 
 #define ISIS_MT_NAMES                                                          \
        "<ipv4-unicast"                                                        \
@@ -40,6 +41,7 @@
        "|ipv4-multicast"                                                      \
        "|ipv6-multicast"                                                      \
        "|ipv6-mgmt"                                                           \
+       "|ipv6-dstsrc"                                                         \
        ">"
 
 #define ISIS_MT_DESCRIPTIONS                                                   \
@@ -48,7 +50,9 @@
        "IPv6 unicast topology\n"                                              \
        "IPv4 multicast topology\n"                                            \
        "IPv6 multicast topology\n"                                            \
-       "IPv6 management topology\n"
+       "IPv6 management topology\n"                                           \
+       "IPv6 dst-src topology\n"                                              \
+       ""
 
 #define ISIS_MT_INFO_FIELDS uint16_t mtid;
 
@@ -75,6 +79,8 @@ struct tlvs;
 struct te_is_neigh;
 struct isis_tlvs;
 
+bool isis_area_ipv6_dstsrc_enabled(struct isis_area *area);
+
 uint16_t isis_area_ipv6_topology(struct isis_area *area);
 
 struct isis_area_mt_setting *area_lookup_mt_setting(struct isis_area *area,
index e903dc8c7fea87d8dfd97c676b7c57e480f7f8d9..1c061fe1c0085d68b9f6dc5844079e123045b7cb 100644 (file)
@@ -30,6 +30,7 @@
 #include "stream.h"
 #include "table.h"
 #include "vty.h"
+#include "srcdest_table.h"
 
 #include "isisd/dict.h"
 #include "isisd/isis_constants.h"
@@ -86,33 +87,13 @@ struct route_table *get_ext_reach(struct isis_area *area, int family, int level)
        return area->ext_reach[protocol][level - 1];
 }
 
-static struct route_node *
-isis_redist_route_node_create(route_table_delegate_t *delegate,
-                             struct route_table *table)
-{
-       struct route_node *node;
-       node = XCALLOC(MTYPE_ISIS_EXT_ROUTE, sizeof(*node));
-       return node;
-}
-
-static void isis_redist_route_node_destroy(route_table_delegate_t *delegate,
-                                          struct route_table *table,
-                                          struct route_node *node)
-{
-       if (node->info)
-               XFREE(MTYPE_ISIS_EXT_INFO, node->info);
-       XFREE(MTYPE_ISIS_EXT_ROUTE, node);
-}
-
-static route_table_delegate_t isis_redist_rt_delegate = {
-       .create_node = isis_redist_route_node_create,
-       .destroy_node = isis_redist_route_node_destroy};
-
 /* Install external reachability information into a
  * specific area for a specific level.
  * Schedule an lsp regenerate if necessary */
 static void isis_redist_install(struct isis_area *area, int level,
-                               struct prefix *p, struct isis_ext_info *info)
+                               const struct prefix *p,
+                               const struct prefix_ipv6 *src_p,
+                               struct isis_ext_info *info)
 {
        int family = p->family;
        struct route_table *er_table = get_ext_reach(area, family, level);
@@ -126,7 +107,7 @@ static void isis_redist_install(struct isis_area *area, int level,
                return;
        }
 
-       er_node = route_node_get(er_table, p);
+       er_node = srcdest_rnode_get(er_table, p, src_p);
        if (er_node->info) {
                route_unlock_node(er_node);
 
@@ -145,7 +126,8 @@ static void isis_redist_install(struct isis_area *area, int level,
  * specific area for a specific level.
  * Schedule an lsp regenerate if necessary. */
 static void isis_redist_uninstall(struct isis_area *area, int level,
-                                 struct prefix *p)
+                                 const struct prefix *p,
+                                 const struct prefix_ipv6 *src_p)
 {
        int family = p->family;
        struct route_table *er_table = get_ext_reach(area, family, level);
@@ -159,7 +141,7 @@ static void isis_redist_uninstall(struct isis_area *area, int level,
                return;
        }
 
-       er_node = route_node_lookup(er_table, p);
+       er_node = srcdest_rnode_lookup(er_table, p, src_p);
        if (!er_node)
                return;
        else
@@ -177,7 +159,8 @@ static void isis_redist_uninstall(struct isis_area *area, int level,
  * and prefix, using the given redistribution settings. */
 static void isis_redist_update_ext_reach(struct isis_area *area, int level,
                                         struct isis_redist *redist,
-                                        struct prefix *p,
+                                        const struct prefix *p,
+                                        const struct prefix_ipv6 *src_p,
                                         struct isis_ext_info *info)
 {
        struct isis_ext_info area_info;
@@ -188,7 +171,8 @@ static void isis_redist_update_ext_reach(struct isis_area *area, int level,
 
        if (redist->map_name) {
                map_ret =
-                       route_map_apply(redist->map, p, RMAP_ISIS, &area_info);
+                       route_map_apply(redist->map, (struct prefix *)p,
+                                       RMAP_ISIS, &area_info);
                if (map_ret == RMAP_DENYMATCH)
                        area_info.distance = 255;
        }
@@ -199,9 +183,9 @@ static void isis_redist_update_ext_reach(struct isis_area *area, int level,
                area_info.distance = 255;
 
        if (area_info.distance < 255)
-               isis_redist_install(area, level, p, &area_info);
+               isis_redist_install(area, level, p, src_p, &area_info);
        else
-               isis_redist_uninstall(area, level, p);
+               isis_redist_uninstall(area, level, p, src_p);
 }
 
 static void isis_redist_ensure_default(struct isis *isis, int family)
@@ -222,7 +206,7 @@ static void isis_redist_ensure_default(struct isis *isis, int family)
        } else
                assert(!"Unknown family!");
 
-       ei_node = route_node_get(ei_table, &p);
+       ei_node = srcdest_rnode_get(ei_table, &p, NULL);
        if (ei_node->info) {
                route_unlock_node(ei_node);
                return;
@@ -238,8 +222,8 @@ static void isis_redist_ensure_default(struct isis *isis, int family)
 }
 
 /* Handle notification about route being added */
-void isis_redist_add(int type, struct prefix *p, uint8_t distance,
-                    uint32_t metric)
+void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
+                    uint8_t distance, uint32_t metric)
 {
        int family = p->family;
        struct route_table *ei_table = get_ext_info(isis, family);
@@ -262,7 +246,7 @@ void isis_redist_add(int type, struct prefix *p, uint8_t distance,
                return;
        }
 
-       ei_node = route_node_get(ei_table, p);
+       ei_node = srcdest_rnode_get(ei_table, p, src_p);
        if (ei_node->info)
                route_unlock_node(ei_node);
        else
@@ -274,8 +258,10 @@ void isis_redist_add(int type, struct prefix *p, uint8_t distance,
        info->distance = distance;
        info->metric = metric;
 
-       if (is_default_prefix(p))
+       if (is_default_prefix(p)
+           && (!src_p || !src_p->prefixlen)) {
                type = DEFAULT_ROUTE;
+       }
 
        for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area))
                for (level = 1; level <= ISIS_LEVELS; level++) {
@@ -284,11 +270,11 @@ void isis_redist_add(int type, struct prefix *p, uint8_t distance,
                                continue;
 
                        isis_redist_update_ext_reach(area, level, redist, p,
-                                                    info);
+                                                    src_p, info);
                }
 }
 
-void isis_redist_delete(int type, struct prefix *p)
+void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p)
 {
        int family = p->family;
        struct route_table *ei_table = get_ext_info(isis, family);
@@ -304,12 +290,14 @@ void isis_redist_delete(int type, struct prefix *p)
        zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf,
                   zebra_route_string(type));
 
-       if (is_default_prefix(p)) {
+       if (is_default_prefix(p)
+           && (!src_p || !src_p->prefixlen)) {
                /* Don't remove default route but add synthetic route for use
                 * by "default-information originate always". Areas without the
                 * "always" setting will ignore routes with origin
                 * DEFAULT_ROUTE. */
-               isis_redist_add(DEFAULT_ROUTE, p, 254, MAX_WIDE_PATH_METRIC);
+               isis_redist_add(DEFAULT_ROUTE, p, NULL,
+                               254, MAX_WIDE_PATH_METRIC);
                return;
        }
 
@@ -319,7 +307,7 @@ void isis_redist_delete(int type, struct prefix *p)
                return;
        }
 
-       ei_node = route_node_lookup(ei_table, p);
+       ei_node = srcdest_rnode_lookup(ei_table, p, src_p);
        if (!ei_node || !ei_node->info) {
                char buf[BUFSIZ];
                prefix2str(p, buf, sizeof(buf));
@@ -339,7 +327,7 @@ void isis_redist_delete(int type, struct prefix *p)
                        if (!redist->redist)
                                continue;
 
-                       isis_redist_uninstall(area, level, p);
+                       isis_redist_uninstall(area, level, p, src_p);
                }
 
        XFREE(MTYPE_ISIS_EXT_INFO, ei_node->info);
@@ -418,17 +406,14 @@ static void isis_redist_set(struct isis_area *area, int level, int family,
        isis_redist_routemap_set(redist, routemap);
 
        if (!area->ext_reach[protocol][level - 1]) {
-               area->ext_reach[protocol][level - 1] =
-                       route_table_init_with_delegate(
-                               &isis_redist_rt_delegate);
+               area->ext_reach[protocol][level - 1] = srcdest_table_init();
        }
 
-       for (i = 0; i < REDIST_PROTOCOL_COUNT; i++)
+       for (i = 0; i < REDIST_PROTOCOL_COUNT; i++) {
                if (!area->isis->ext_info[i]) {
-                       area->isis->ext_info[i] =
-                               route_table_init_with_delegate(
-                                       &isis_redist_rt_delegate);
+                       area->isis->ext_info[i] = srcdest_table_init();
                }
+       }
 
        isis_redist_update_zebra_subscriptions(area->isis);
 
@@ -436,20 +421,27 @@ static void isis_redist_set(struct isis_area *area, int level, int family,
                isis_redist_ensure_default(area->isis, family);
 
        ei_table = get_ext_info(area->isis, family);
-       for (rn = route_top(ei_table); rn; rn = route_next(rn)) {
+       for (rn = route_top(ei_table); rn; rn = srcdest_route_next(rn)) {
                if (!rn->info)
                        continue;
                info = rn->info;
 
+               const struct prefix *p, *src_p;
+
+               srcdest_rnode_prefixes(rn, &p, &src_p);
+
                if (type == DEFAULT_ROUTE) {
-                       if (!is_default_prefix(&rn->p))
+                       if (!is_default_prefix(p)
+                           || (src_p && src_p->prefixlen)) {
                                continue;
+                       }
                } else {
                        if (info->origin != type)
                                continue;
                }
 
-               isis_redist_update_ext_reach(area, level, redist, &rn->p, info);
+               isis_redist_update_ext_reach(area, level, redist, p,
+                                            (struct prefix_ipv6 *)src_p, info);
        }
 }
 
@@ -472,14 +464,19 @@ static void isis_redist_unset(struct isis_area *area, int level, int family,
                return;
        }
 
-       for (rn = route_top(er_table); rn; rn = route_next(rn)) {
+       for (rn = route_top(er_table); rn; rn = srcdest_route_next(rn)) {
                if (!rn->info)
                        continue;
                info = rn->info;
 
+               const struct prefix *p, *src_p;
+               srcdest_rnode_prefixes(rn, &p, &src_p);
+
                if (type == DEFAULT_ROUTE) {
-                       if (!is_default_prefix(&rn->p))
+                       if (!is_default_prefix(p)
+                           || (src_p && src_p->prefixlen)) {
                                continue;
+                       }
                } else {
                        if (info->origin != type)
                                continue;
index c12363d5089ae6454b26795f29ac5805254642b1..95f06f71ec9a343f88e71e4fa4f88867d77797f9 100644 (file)
@@ -42,13 +42,14 @@ struct isis_redist {
 
 struct isis_area;
 struct prefix;
+struct prefix_ipv6;
 struct vty;
 
 struct route_table *get_ext_reach(struct isis_area *area, int family,
                                  int level);
-void isis_redist_add(int type, struct prefix *p, uint8_t distance,
-                    uint32_t metric);
-void isis_redist_delete(int type, struct prefix *p);
+void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p,
+                    uint8_t distance, uint32_t metric);
+void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p);
 int isis_redist_config_write(struct vty *vty, struct isis_area *area,
                             int family);
 void isis_redist_init(void);
index 9b2aa7470b6f6cd418f4f357acf70efac19b2456..a433fcdb41a86f3da769b084d55d8c1dfb12ce56 100644 (file)
@@ -1912,6 +1912,11 @@ static void append_item(struct isis_item_list *dest, struct isis_item *item)
        dest->count++;
 }
 
+static struct isis_item *last_item(struct isis_item_list *list)
+{
+       return container_of(list->tail, struct isis_item, next);
+}
+
 static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
                       uint8_t tlv_type, uint8_t len, struct stream *s,
                       struct sbuf *log, void *dest, int indent)
@@ -3168,6 +3173,21 @@ void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
        append_item(l, (struct isis_item *)r);
 }
 
+void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
+                                    struct prefix_ipv6 *dest,
+                                    struct prefix_ipv6 *src,
+                                    uint32_t metric)
+{
+       isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric);
+       struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
+                                                    mtid);
+
+       struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
+       r->subtlvs = isis_alloc_subtlvs();
+       r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
+       memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
+}
+
 void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
                                  uint8_t metric)
 {
index 98edbf14e7dc0233c7511d1fd1c217f985665619..bd1fa3e6768d3cea582f6b3821393a0bc2b9765a 100644 (file)
@@ -313,6 +313,10 @@ void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
                                     struct prefix_ipv4 *dest, uint32_t metric);
 void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
                              struct prefix_ipv6 *dest, uint32_t metric);
+void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
+                                    struct prefix_ipv6 *dest,
+                                    struct prefix_ipv6 *src,
+                                    uint32_t metric);
 void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
                                  uint8_t metric);
 void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
index ac640c5e4909c4b0ce4f729d4c44adcb858ebc44..b225af106087e6b50c7e136ff4b9993947e65241 100644 (file)
@@ -359,24 +359,23 @@ static int isis_zebra_read(int command, struct zclient *zclient,
        if (zapi_route_decode(zclient->ibuf, &api) < 0)
                return -1;
 
-       /* we completely ignore srcdest routes for now. */
-       if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
-               return 0;
-
        /*
         * Avoid advertising a false default reachability. (A default
         * route installed by IS-IS gets redistributed from zebra back
         * into IS-IS causing us to start advertising default reachabity
         * without this check)
         */
-       if (api.prefix.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS)
+       if (api.prefix.prefixlen == 0
+           && api.src_prefix.prefixlen == 0
+           && api.type == ZEBRA_ROUTE_ISIS) {
                command = ZEBRA_REDISTRIBUTE_ROUTE_DEL;
+       }
 
        if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD)
-               isis_redist_add(api.type, &api.prefix, api.distance,
-                               api.metric);
+               isis_redist_add(api.type, &api.prefix, &api.src_prefix,
+                               api.distance, api.metric);
        else
-               isis_redist_delete(api.type, &api.prefix);
+               isis_redist_delete(api.type, &api.prefix, &api.src_prefix);
 
        return 0;
 }
index 98428eaab237685d5e6872e801705ef9123a308b..9ac96372ff70e6514062fe659654ed511d75963f 100644 (file)
@@ -365,6 +365,22 @@ struct in_pktinfo {
                _a < _b ? _a : _b;                                             \
        })
 
+#ifndef offsetof
+#ifdef __compiler_offsetof
+#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
+#else
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member)                                        \
+       ({                                                                     \
+               const typeof(((type *)0)->member) *__mptr = (ptr);             \
+               (type *)((char *)__mptr - offsetof(type, member));             \
+       })
+#endif
+
 #define ZEBRA_NUM_OF(x) (sizeof (x) / sizeof (x[0]))
 
 /* For old definition. */
index cca702b138f99f61f49760b0d403c8379f085109..4abbe81499be693f39a91cf6581bde569ebd3da8 100644 (file)
Binary files a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ