isisd: learn and advertise IPv6 dst-src routes

Receive IPv6 dst-src routes from zebra and advertise them in our LSPs
if so configured.

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
This commit is contained in:
Christian Franke 2018-07-22 15:49:02 -04:00
parent c9b0e434bc
commit d43d2df5f3
10 changed files with 136 additions and 70 deletions

View 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);
}
}
}

View 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;
}

View 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,

View 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;

View 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);

View 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)
{

View 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,

View 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;
}

View 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. */