diff options
author | Christian Franke <chris@opensourcerouting.org> | 2015-11-12 14:24:22 +0100 |
---|---|---|
committer | Donald Sharp <sharpd@cumulusnetworks.com> | 2016-03-30 02:26:28 +0200 |
commit | f3ccedaa3e408eb0cb098579a4a3768255e82867 (patch) | |
tree | 1f23ae04af9682c51b45e24477408857a929b1b3 | |
parent | lib: add function to get precise remaining time of timer thread (diff) | |
download | frr-f3ccedaa3e408eb0cb098579a4a3768255e82867.tar.xz frr-f3ccedaa3e408eb0cb098579a4a3768255e82867.zip |
isisd: add support to import routes from other protocols
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
-rw-r--r-- | isisd/Makefile.am | 5 | ||||
-rw-r--r-- | isisd/isis_lsp.c | 134 | ||||
-rw-r--r-- | isisd/isis_main.c | 5 | ||||
-rw-r--r-- | isisd/isis_redist.c | 821 | ||||
-rw-r--r-- | isisd/isis_redist.h | 58 | ||||
-rw-r--r-- | isisd/isis_routemap.c | 551 | ||||
-rw-r--r-- | isisd/isis_routemap.h | 25 | ||||
-rw-r--r-- | isisd/isis_tlv.c | 21 | ||||
-rw-r--r-- | isisd/isis_tlv.h | 13 | ||||
-rw-r--r-- | isisd/isis_zebra.c | 101 | ||||
-rw-r--r-- | isisd/isis_zebra.h | 2 | ||||
-rw-r--r-- | isisd/isisd.c | 4 | ||||
-rw-r--r-- | isisd/isisd.h | 36 | ||||
-rw-r--r-- | lib/prefix.c | 11 | ||||
-rw-r--r-- | lib/prefix.h | 1 | ||||
-rw-r--r-- | lib/routemap.h | 3 |
16 files changed, 1681 insertions, 110 deletions
diff --git a/isisd/Makefile.am b/isisd/Makefile.am index 89f7cd4c1..5ec0811b6 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -17,14 +17,15 @@ libisis_a_SOURCES = \ isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \ isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \ isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \ - isis_spf.c isis_route.c isis_routemap.c + isis_spf.c isis_redist.c isis_route.c isis_routemap.c noinst_HEADERS = \ isisd.h isis_pdu.h isis_tlv.h isis_adjacency.h isis_constants.h \ isis_lsp.h dict.h isis_circuit.h isis_misc.h isis_network.h \ isis_zebra.h isis_dr.h isis_flags.h isis_dynhn.h isis_common.h \ - iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_route.h \ + iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_redist.h \ + isis_route.h isis_routemap.h \ include-netbsd/clnp.h include-netbsd/esis.h include-netbsd/iso.h isisd_SOURCES = \ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 17141477f..be1be0cac 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -5,9 +5,10 @@ * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering + * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free + * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * @@ -35,6 +36,7 @@ #include "if.h" #include "checksum.h" #include "md5.h" +#include "table.h" #include "isisd/dict.h" #include "isisd/isis_constants.h" @@ -1146,6 +1148,119 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area, return lsp; } +static void +lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area, + struct tlvs *tlv_data) +{ + struct route_table *er_table; + struct route_node *rn; + struct prefix_ipv4 *ipv4; + struct isis_ext_info *info; + struct ipv4_reachability *ipreach; + struct te_ipv4_reachability *te_ipreach; + + er_table = get_ext_reach(area, AF_INET, lsp->level); + if (!er_table) + return; + + for (rn = route_top(er_table); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + + ipv4 = (struct prefix_ipv4*)&rn->p; + info = rn->info; + if (area->oldmetric) + { + if (tlv_data->ipv4_ext_reachs == NULL) + { + tlv_data->ipv4_ext_reachs = list_new(); + tlv_data->ipv4_ext_reachs->del = free_tlv; + } + ipreach = XMALLOC(MTYPE_ISIS_TLV, sizeof(*ipreach)); + + ipreach->prefix.s_addr = ipv4->prefix.s_addr; + masklen2ip(ipv4->prefixlen, &ipreach->mask); + ipreach->prefix.s_addr &= ipreach->mask.s_addr; + + if ((info->metric & 0x3f) != info->metric) + ipreach->metrics.metric_default = 0x3f; + else + ipreach->metrics.metric_default = info->metric; + ipreach->metrics.metric_expense = METRICS_UNSUPPORTED; + ipreach->metrics.metric_error = METRICS_UNSUPPORTED; + ipreach->metrics.metric_delay = METRICS_UNSUPPORTED; + listnode_add(tlv_data->ipv4_ext_reachs, ipreach); + } + if (area->newmetric) + { + if (tlv_data->te_ipv4_reachs == NULL) + { + tlv_data->te_ipv4_reachs = list_new(); + tlv_data->te_ipv4_reachs->del = free_tlv; + } + te_ipreach = + XCALLOC(MTYPE_ISIS_TLV, + sizeof(*te_ipreach) - 1 + PSIZE(ipv4->prefixlen)); + if (info->metric > MAX_WIDE_PATH_METRIC) + te_ipreach->te_metric = htonl(MAX_WIDE_PATH_METRIC); + else + te_ipreach->te_metric = htonl(info->metric); + te_ipreach->control = ipv4->prefixlen & 0x3f; + memcpy(&te_ipreach->prefix_start, &ipv4->prefix.s_addr, + PSIZE(ipv4->prefixlen)); + listnode_add(tlv_data->te_ipv4_reachs, te_ipreach); + } + } +} + +static void +lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, + struct tlvs *tlv_data) +{ + struct route_table *er_table; + struct route_node *rn; + struct prefix_ipv6 *ipv6; + struct isis_ext_info *info; + struct ipv6_reachability *ip6reach; + + er_table = get_ext_reach(area, AF_INET6, lsp->level); + if (!er_table) + return; + + for (rn = route_top(er_table); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + + ipv6 = (struct prefix_ipv6*)&rn->p; + info = rn->info; + + if (tlv_data->ipv6_reachs == NULL) + { + tlv_data->ipv6_reachs = list_new(); + tlv_data->ipv6_reachs->del = free_tlv; + } + ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach)); + if (info->metric > MAX_WIDE_PATH_METRIC) + ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC); + else + ip6reach->metric = htonl(info->metric); + ip6reach->control_info = DISTRIBUTION_EXTERNAL; + ip6reach->prefix_len = ipv6->prefixlen; + memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, sizeof(ip6reach->prefix)); + listnode_add(tlv_data->ipv6_reachs, ip6reach); + } +} + +static void +lsp_build_ext_reach (struct isis_lsp *lsp, struct isis_area *area, + struct tlvs *tlv_data) +{ + lsp_build_ext_reach_ipv4(lsp, area, tlv_data); + lsp_build_ext_reach_ipv6(lsp, area, tlv_data); +} + /* * Builds the LSP data part. This func creates a new frag whenever * area->lsp_frag_threshold is exceeded. @@ -1561,6 +1676,8 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) } } + lsp_build_ext_reach(lsp, area, &tlv_data); + lsp_debug("ISIS (%s): LSP construction is complete. Serializing...", area->area_tag); while (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) @@ -1570,12 +1687,25 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) lsp_tlv_fit (lsp, &tlv_data.ipv4_int_reachs, &lsp->tlv_data.ipv4_int_reachs, IPV4_REACH_LEN, area->lsp_frag_threshold, - tlv_add_ipv4_reachs); + tlv_add_ipv4_int_reachs); if (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + while (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs)) + { + if (lsp->tlv_data.ipv4_ext_reachs == NULL) + lsp->tlv_data.ipv4_ext_reachs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.ipv4_ext_reachs, + &lsp->tlv_data.ipv4_ext_reachs, + IPV4_REACH_LEN, area->lsp_frag_threshold, + tlv_add_ipv4_ext_reachs); + if (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + /* FIXME: We pass maximum te_ipv4_reachability length to the lsp_tlv_fit() * for now. lsp_tlv_fit() needs to be fixed to deal with variable length * TLVs (sub TLVs!). */ diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 55bc2a734..0f721046c 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -34,6 +34,7 @@ #include "privs.h" #include "sigevent.h" #include "filter.h" +#include "plist.h" #include "zclient.h" #include "vrf.h" #include "systemd.h" @@ -48,6 +49,7 @@ #include "isisd/isis_dynhn.h" #include "isisd/isis_spf.h" #include "isisd/isis_route.h" +#include "isisd/isis_routemap.h" #include "isisd/isis_zebra.h" /* Default configuration file name */ @@ -339,9 +341,12 @@ main (int argc, char **argv, char **envp) memory_init (); access_list_init(); vrf_init (); + prefix_list_init(); isis_init (); isis_circuit_init (); isis_spf_cmds_init (); + isis_redist_init (); + isis_route_map_init(); /* create the global 'isis' instance */ isis_new (1); diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c new file mode 100644 index 000000000..abb9ecd40 --- /dev/null +++ b/isisd/isis_redist.c @@ -0,0 +1,821 @@ +/* + * IS-IS Rout(e)ing protocol - isis_redist.c + * + * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <zebra.h> + +#include "command.h" +#include "if.h" +#include "linklist.h" +#include "memory.h" +#include "memtypes.h" +#include "prefix.h" +#include "routemap.h" +#include "stream.h" +#include "table.h" +#include "vty.h" + +#include "isisd/dict.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_tlv.h" +#include "isisd/isisd.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_route.h" +#include "isisd/isis_zebra.h" + +static int +redist_protocol(int family) +{ + if (family == AF_INET) + return 0; + if (family == AF_INET6) + return 1; + + assert(!"Unsupported address family!"); +} + +static int +is_default(struct prefix *p) +{ + if (p->family == AF_INET) + if (p->u.prefix4.s_addr == 0 && p->prefixlen == 0) + return 1; + if (p->family == AF_INET6) + if (IN6_IS_ADDR_UNSPECIFIED(&p->u.prefix6) && p->prefixlen == 0) + return 1; + return 0; +} + +static struct route_table* +get_ext_info(struct isis *i, int family) +{ + int protocol = redist_protocol(family); + + return i->ext_info[protocol]; +} + +static struct isis_redist* +get_redist_settings(struct isis_area *area, int family, int type, int level) +{ + int protocol = redist_protocol(family); + + return &area->redist_settings[protocol][type][level-1]; +} + +struct route_table* +get_ext_reach(struct isis_area *area, int family, int level) +{ + int protocol = redist_protocol(family); + + 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_ROUTE_NODE, 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, node->info); + XFREE (MTYPE_ROUTE_NODE, 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) +{ + int family = p->family; + struct route_table *er_table = get_ext_reach(area, family, level); + struct route_node *er_node; + + if (!er_table) + { + zlog_warn("%s: External reachability table of area %s" + " is not initialized.", __func__, area->area_tag); + return; + } + + er_node = route_node_get(er_table, p); + if (er_node->info) + { + route_unlock_node(er_node); + + /* Don't update/reschedule lsp generation if nothing changed. */ + if (!memcmp(er_node->info, info, sizeof(*info))) + return; + } + else + { + er_node->info = XMALLOC(MTYPE_ISIS, sizeof(*info)); + } + + memcpy(er_node->info, info, sizeof(*info)); + lsp_regenerate_schedule(area, level, 0); +} + +/* Remove external reachability information from a + * 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) +{ + int family = p->family; + struct route_table *er_table = get_ext_reach(area, family, level); + struct route_node *er_node; + + if (!er_table) + { + zlog_warn("%s: External reachability table of area %s" + " is not initialized.", __func__, area->area_tag); + return; + } + + er_node = route_node_lookup(er_table, p); + if (!er_node) + return; + else + route_unlock_node(er_node); + + if (!er_node->info) + return; + + route_unlock_node(er_node); + lsp_regenerate_schedule(area, level, 0); +} + +/* Update external reachability info of area for a given 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, + struct isis_ext_info *info) +{ + struct isis_ext_info area_info; + route_map_result_t map_ret; + + memcpy(&area_info, info, sizeof(area_info)); + if (redist->metric != 0xffffffff) + area_info.metric = redist->metric; + + if (redist->map_name) + { + map_ret = route_map_apply(redist->map, p, RMAP_ISIS, &area_info); + if (map_ret == RMAP_DENYMATCH) + area_info.distance = 255; + } + + /* Allow synthesized default routes only on always orignate */ + if (area_info.origin == DEFAULT_ROUTE + && redist->redist != DEFAULT_ORIGINATE_ALWAYS) + area_info.distance = 255; + + if (area_info.distance < 255) + isis_redist_install(area, level, p, &area_info); + else + isis_redist_uninstall(area, level, p); +} + +static void +isis_redist_ensure_default(struct isis *isis, int family) +{ + struct prefix p; + struct route_table *ei_table = get_ext_info(isis, family); + struct route_node *ei_node; + struct isis_ext_info *info; + + if (family == AF_INET) + { + p.family = AF_INET; + p.prefixlen = 0; + memset(&p.u.prefix4, 0, sizeof(p.u.prefix4)); + } + else if (family == AF_INET6) + { + p.family = AF_INET6; + p.prefixlen = 0; + memset(&p.u.prefix6, 0, sizeof(p.u.prefix6)); + } + else + assert(!"Unknown family!"); + + ei_node = route_node_get(ei_table, &p); + if (ei_node->info) + { + route_unlock_node(ei_node); + return; + } + + ei_node->info = XCALLOC(MTYPE_ISIS, sizeof(struct isis_ext_info)); + + info = ei_node->info; + info->origin = DEFAULT_ROUTE; + info->distance = 254; + info->metric = MAX_WIDE_PATH_METRIC; +} + +/* Handle notification about route being added */ +void +isis_redist_add(int type, struct prefix *p, u_char distance, uint32_t metric) +{ + int family = p->family; + struct route_table *ei_table = get_ext_info(isis, family); + struct route_node *ei_node; + struct isis_ext_info *info; + struct listnode *node; + struct isis_area *area; + int level; + struct isis_redist *redist; + + char debug_buf[BUFSIZ]; + prefix2str(p, debug_buf, sizeof(debug_buf)); + + zlog_debug("%s: New route %s from %s.", __func__, debug_buf, + zebra_route_string(type)); + + if (!ei_table) + { + zlog_warn("%s: External information table not initialized.", + __func__); + return; + } + + ei_node = route_node_get(ei_table, p); + if (ei_node->info) + route_unlock_node(ei_node); + else + ei_node->info = XCALLOC(MTYPE_ISIS, sizeof(struct isis_ext_info)); + + info = ei_node->info; + info->origin = type; + info->distance = distance; + info->metric = metric; + + if (is_default(p)) + type = DEFAULT_ROUTE; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + for (level = 1; level <= ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, type, level); + if (!redist->redist) + continue; + + isis_redist_update_ext_reach(area, level, redist, p, info); + } +} + +void +isis_redist_delete(int type, struct prefix *p) +{ + int family = p->family; + struct route_table *ei_table = get_ext_info(isis, family); + struct route_node *ei_node; + struct listnode *node; + struct isis_area *area; + int level; + struct isis_redist *redist; + + char debug_buf[BUFSIZ]; + prefix2str(p, debug_buf, sizeof(debug_buf)); + + zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf, + zebra_route_string(type)); + + if (is_default(p)) + { + /* 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); + return; + } + + if (!ei_table) + { + zlog_warn("%s: External information table not initialized.", + __func__); + return; + } + + ei_node = route_node_lookup(ei_table, p); + if (!ei_node || !ei_node->info) + { + char buf[BUFSIZ]; + prefix2str(p, buf, sizeof(buf)); + zlog_warn("%s: Got a delete for %s route %s, but that route" + " was never added.", __func__, zebra_route_string(type), + buf); + if (ei_node) + route_unlock_node(ei_node); + return; + } + route_unlock_node(ei_node); + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + for (level = 1; level < ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, type, level); + if (!redist->redist) + continue; + + isis_redist_uninstall(area, level, p); + } + + route_unlock_node(ei_node); +} + +static void +isis_redist_routemap_set(struct isis_redist *redist, const char *routemap) +{ + if (redist->map_name) { + XFREE(MTYPE_ISIS, redist->map_name); + redist->map = NULL; + } + + if (routemap && strlen(routemap)) { + redist->map_name = XSTRDUP(MTYPE_ISIS, routemap); + redist->map = route_map_lookup_by_name(routemap); + } +} + +static void +isis_redist_update_zebra_subscriptions(struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + int type; + int level; + int protocol; + + char do_subscribe[ZEBRA_ROUTE_MAX + 1]; + + memset(do_subscribe, 0, sizeof(do_subscribe)); + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) + for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) + for (level = 0; level < ISIS_LEVELS; level++) + if (area->redist_settings[protocol][type][level].redist) + do_subscribe[type] = 1; + + for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) + { + /* This field is actually controlling transmission of the IS-IS + * routes to Zebra and has nothing to do with redistribution, + * so skip it. */ + if (type == ZEBRA_ROUTE_ISIS) + continue; + + if (do_subscribe[type]) + isis_zebra_redistribute_set(type); + else + isis_zebra_redistribute_unset(type); + } +} + +static void +isis_redist_set(struct isis_area *area, int level, + int family, int type, uint32_t metric, + const char *routemap, int originate_type) +{ + int protocol = redist_protocol(family); + struct isis_redist *redist = get_redist_settings(area, family, type, level); + int i; + struct route_table *ei_table; + struct route_node *rn; + struct isis_ext_info *info; + + redist->redist = (type == DEFAULT_ROUTE) ? originate_type : 1; + redist->metric = metric; + 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); + } + + 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); + } + + isis_redist_update_zebra_subscriptions(area->isis); + + if (type == DEFAULT_ROUTE && originate_type == DEFAULT_ORIGINATE_ALWAYS) + 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)) + { + if (!rn->info) + continue; + info = rn->info; + + if (type == DEFAULT_ROUTE) + { + if (!is_default(&rn->p)) + continue; + } + else + { + if (info->origin != type) + continue; + } + + isis_redist_update_ext_reach(area, level, redist, &rn->p, info); + } +} + +static void +isis_redist_unset(struct isis_area *area, int level, + int family, int type) +{ + struct isis_redist *redist = get_redist_settings(area, family, type, level); + struct route_table *er_table = get_ext_reach(area, family, level); + struct route_node *rn; + struct isis_ext_info *info; + + if (!redist->redist) + return; + + redist->redist = 0; + if (!er_table) + { + zlog_warn("%s: External reachability table uninitialized.", __func__); + return; + } + + for (rn = route_top(er_table); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + info = rn->info; + + if (type == DEFAULT_ROUTE) + { + if (!is_default(&rn->p)) + continue; + } + else + { + if (info->origin != type) + continue; + } + + route_unlock_node(rn); + } + + lsp_regenerate_schedule(area, level, 0); + isis_redist_update_zebra_subscriptions(area->isis); +} + +void +isis_redist_area_finish(struct isis_area *area) +{ + int protocol; + int level; + int type; + + for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) + for (level = 0; level < ISIS_LEVELS; level++) + { + for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) + { + struct isis_redist *redist; + + redist = &area->redist_settings[protocol][type][level]; + redist->redist = 0; + if (redist->map_name) + XFREE(MTYPE_ISIS, redist->map_name); + } + route_table_finish(area->ext_reach[protocol][level]); + } + + isis_redist_update_zebra_subscriptions(area->isis); +} + +DEFUN(isis_redistribute, + isis_redistribute_cmd, + "redistribute (ipv4|ipv6) " QUAGGA_REDIST_STR_ISISD + " (level-1|level-2) {metric <0-16777215>|route-map WORD}", + REDIST_STR + "Redistribute IPv4 routes\n" + "Redistribute IPv6 routes\n" + QUAGGA_REDIST_HELP_STR_ISISD + "Redistribute into level-1\n" + "Redistribute into level-2\n" + "Metric for redistributed routes\n" + "ISIS default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct isis_area *area = vty->index; + int family; + int afi; + int type; + int level; + unsigned long metric; + const char *routemap; + + if (argc < 5) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + afi = family2afi(family); + if (!afi) + return CMD_WARNING; + + type = proto_redistnum(afi, argv[1]); + if (type < 0 || type == ZEBRA_ROUTE_ISIS) + return CMD_WARNING; + + if (!strcmp("level-1", argv[2])) + level = 1; + else if (!strcmp("level-2", argv[2])) + level = 2; + else + return CMD_WARNING; + + if ((area->is_type & level) != level) + { + vty_out(vty, "Node is not a level-%d IS%s", level, VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv[3]) + { + char *endp; + metric = strtoul(argv[3], &endp, 10); + if (argv[3][0] == '\0' || *endp != '\0') + return CMD_WARNING; + } + else + { + metric = 0xffffffff; + } + + routemap = argv[4]; + + isis_redist_set(area, level, family, type, metric, routemap, 0); + return 0; +} + +DEFUN(no_isis_redistribute, + no_isis_redistribute_cmd, + "no redistribute (ipv4|ipv6) " QUAGGA_REDIST_STR_ISISD + " (level-1|level-2)", + NO_STR + REDIST_STR + "Redistribute IPv4 routes\n" + "Redistribute IPv6 routes\n" + QUAGGA_REDIST_HELP_STR_ISISD + "Redistribute into level-1\n" + "Redistribute into level-2\n") +{ + struct isis_area *area = vty->index; + int type; + int level; + int family; + int afi; + + if (argc < 3) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + afi = family2afi(family); + if (!afi) + return CMD_WARNING; + + type = proto_redistnum(afi, argv[1]); + if (type < 0 || type == ZEBRA_ROUTE_ISIS) + return CMD_WARNING; + + if (!strcmp("level-1", argv[2])) + level = 1; + else if (!strcmp("level-2", argv[2])) + level = 2; + else + return CMD_WARNING; + + isis_redist_unset(area, level, family, type); + return 0; +} + +DEFUN(isis_default_originate, + isis_default_originate_cmd, + "default-information originate (ipv4|ipv6) (level-1|level-2) " + "{always|metric <0-16777215>|route-map WORD}", + "Control distribution of default information\n" + "Distribute a default route\n" + "Distribute default route for IPv4\n" + "Distribute default route for IPv6\n" + "Distribute default route into level-1\n" + "Distribute default route into level-2\n" + "Always advertise default route\n" + "Metric for default route\n" + "ISIS default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct isis_area *area = vty->index; + int family; + int originate_type; + int level; + unsigned long metric; + const char *routemap; + + if (argc < 5) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + if (!strcmp("level-1", argv[1])) + level = 1; + else if (!strcmp("level-2", argv[1])) + level = 2; + else + return CMD_WARNING; + + if ((area->is_type & level) != level) + { + vty_out(vty, "Node is not a level-%d IS%s", level, VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv[2] && *argv[2] != '\0') + originate_type = DEFAULT_ORIGINATE_ALWAYS; + else + originate_type = DEFAULT_ORIGINATE; + + if (family == AF_INET6 && originate_type != DEFAULT_ORIGINATE_ALWAYS) + { + vty_out(vty, "Zebra doesn't implement default-originate for IPv6 yet%s", VTY_NEWLINE); + vty_out(vty, "so use with care or use default-originate always.%s", VTY_NEWLINE); + } + + if (argv[3]) + { + char *endp; + metric = strtoul(argv[3], &endp, 10); + if (argv[3][0] == '\0' || *endp != '\0') + return CMD_WARNING; + } + else + { + metric = 0xffffffff; + } + + routemap = argv[4]; + + isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, originate_type); + return 0; +} + +DEFUN(no_isis_default_originate, + no_isis_default_originate_cmd, + "no default-information originate (ipv4|ipv6) (level-1|level-2)", + NO_STR + "Control distribution of default information\n" + "Distribute a default route\n" + "Distribute default route for IPv4\n" + "Distribute default route for IPv6\n" + "Distribute default route into level-1\n" + "Distribute default route into level-2\n") +{ + struct isis_area *area = vty->index; + + int family; + int level; + + if (argc < 2) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + if (!strcmp("level-1", argv[1])) + level = 1; + else if (!strcmp("level-2", argv[1])) + level = 2; + else + return CMD_WARNING; + + isis_redist_unset(area, level, family, DEFAULT_ROUTE); + return 0; +} + +int +isis_redist_config_write(struct vty *vty, struct isis_area *area, + int family) +{ + int type; + int level; + int write = 0; + struct isis_redist *redist; + const char *family_str; + + if (family == AF_INET) + family_str = "ipv4"; + else if (family == AF_INET6) + family_str = "ipv6"; + else + return 0; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (type == ZEBRA_ROUTE_ISIS) + continue; + + for (level = 1; level <= ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, type, level); + if (!redist->redist) + continue; + vty_out(vty, " redistribute %s %s level-%d", + family_str, zebra_route_string(type), level); + if (redist->metric != 0xffffffff) + vty_out(vty, " metric %u", redist->metric); + if (redist->map_name) + vty_out(vty, " route-map %s", redist->map_name); + vty_out(vty, "%s", VTY_NEWLINE); + write++; + } + } + + for (level = 1; level <= ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, DEFAULT_ROUTE, level); + if (!redist->redist) + continue; + vty_out(vty, " default-information originate %s level-%d", + family_str, level); + if (redist->redist == DEFAULT_ORIGINATE_ALWAYS) + vty_out(vty, " always"); + if (redist->metric != 0xffffffff) + vty_out(vty, " metric %u", redist->metric); + if (redist->map_name) + vty_out(vty, " route-map %s", redist->map_name); + vty_out(vty, "%s", VTY_NEWLINE); + write++; + } + + return write; +} + +void +isis_redist_init(void) +{ + install_element(ISIS_NODE, &isis_redistribute_cmd); + install_element(ISIS_NODE, &no_isis_redistribute_cmd); + install_element(ISIS_NODE, &isis_default_originate_cmd); + install_element(ISIS_NODE, &no_isis_default_originate_cmd); +} diff --git a/isisd/isis_redist.h b/isisd/isis_redist.h new file mode 100644 index 000000000..218462005 --- /dev/null +++ b/isisd/isis_redist.h @@ -0,0 +1,58 @@ +/* + * IS-IS Rout(e)ing protocol - isis_redist.h + * + * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef ISIS_REDIST_H +#define ISIS_REDIST_H + +#define REDIST_PROTOCOL_COUNT 2 + +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX +#define DEFAULT_ORIGINATE 1 +#define DEFAULT_ORIGINATE_ALWAYS 2 + +struct isis_ext_info +{ + int origin; + uint32_t metric; + u_char distance; +}; + +struct isis_redist +{ + int redist; + uint32_t metric; + char *map_name; + struct route_map *map; +}; + +struct isis_area; +struct prefix; + +struct route_table *get_ext_reach(struct isis_area *area, + int family, int level); +void isis_redist_add(int type, struct prefix *p, + u_char distance, uint32_t metric); +void isis_redist_delete(int type, struct prefix *p); +int isis_redist_config_write(struct vty *vty, struct isis_area *area, + int family); +void isis_redist_init(void); +void isis_redist_area_finish(struct isis_area *area); + +#endif diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 84a14ac57..3e0ab047e 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -1,13 +1,10 @@ /* - * IS-IS Rout(e)ing protocol - isis_routemap.c - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering + * IS-IS Rout(e)ing protocol - isis_routemap.c * + * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free + * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * @@ -20,18 +17,22 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + #include <zebra.h> -#include "thread.h" +#include "command.h" +#include "filter.h" +#include "hash.h" +#include "if.h" #include "linklist.h" -#include "vty.h" #include "log.h" #include "memory.h" #include "prefix.h" -#include "hash.h" -#include "if.h" -#include "table.h" +#include "plist.h" #include "routemap.h" +#include "table.h" +#include "thread.h" +#include "vty.h" #include "isis_constants.h" #include "isis_common.h" @@ -47,60 +48,518 @@ #include "isis_spf.h" #include "isis_route.h" #include "isis_zebra.h" +#include "isis_routemap.h" -extern struct isis *isis; +static route_map_result_t +route_match_ip_address(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; -/* - * Prototypes. - */ -void isis_route_map_upd(const char *); -void isis_route_map_event(route_map_event_t, const char *); -void isis_route_map_init(void); + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + alist = access_list_lookup(AFI_IP, (char*)rule); + if (access_list_apply(alist, prefix) != FILTER_DENY) + return RMAP_MATCH; -void -isis_route_map_upd (const char *name) + return RMAP_NOMATCH; +} + +static void * +route_match_ip_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_ip_address_cmd = +{ + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_match_ip_address_prefix_list(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + + plist = prefix_list_lookup(AFI_IP, (char*)rule); + if (prefix_list_apply(plist, prefix) != PREFIX_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void * +route_match_ip_address_prefix_list_compile(const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_address_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = +{ + "ip address prefix-list", + route_match_ip_address_prefix_list, + route_match_ip_address_prefix_list_compile, + route_match_ip_address_prefix_list_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_match_ipv6_address(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { - int i = 0; + struct access_list *alist; - if (!isis) - return; + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + + alist = access_list_lookup(AFI_IP6, (char*)rule); + if (access_list_apply(alist, prefix) != FILTER_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void * +route_match_ipv6_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ipv6_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_ipv6_address_cmd = +{ + "ipv6 address", + route_match_ipv6_address, + route_match_ipv6_address_compile, + route_match_ipv6_address_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_match_ipv6_address_prefix_list(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; - for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + + plist = prefix_list_lookup(AFI_IP6, (char*)rule); + if (prefix_list_apply(plist, prefix) != PREFIX_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void * +route_match_ipv6_address_prefix_list_compile(const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ipv6_address_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = +{ + "ipv6 address prefix-list", + route_match_ipv6_address_prefix_list, + route_match_ipv6_address_prefix_list_compile, + route_match_ipv6_address_prefix_list_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_set_metric(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + uint32_t *metric; + struct isis_ext_info *info; + + if (type == RMAP_ISIS) { - if (isis->rmap[i].name) - isis->rmap[i].map = route_map_lookup_by_name (isis->rmap[i].name); - else - isis->rmap[i].map = NULL; + metric = rule; + info = object; + + info->metric = *metric; } - /* FIXME: do the address family sub-mode AF_INET6 here ? */ + return RMAP_OKAY; } -void -isis_route_map_event (route_map_event_t event, const char *name) +static void * +route_set_metric_compile(const char *arg) +{ + unsigned long metric; + char *endp; + uint32_t *ret; + + metric = strtoul(arg, &endp, 10); + if (arg[0] == '\0' || *endp != '\0' || metric > MAX_WIDE_PATH_METRIC) + return NULL; + + ret = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(ret)); + *ret = metric; + + return ret; +} + +static void +route_set_metric_free(void *rule) { - int type; + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_set_metric_cmd = +{ + "metric", + route_set_metric, + route_set_metric_compile, + route_set_metric_free +}; - if (!isis) - return; +/* ------------------------------------------------------------*/ + +static int +isis_route_match_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; - for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + ret = route_map_add_match (index, command, arg); + if (ret) { - if (isis->rmap[type].name && isis->rmap[type].map && - !strcmp (isis->rmap[type].name, name)) - { - isis_distribute_list_update (type); - } + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } } + return CMD_SUCCESS; +} + +static int +isis_route_match_delete(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_delete_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +static int +isis_route_set_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_set(index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +static int +isis_route_set_delete (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_delete_set (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +/* ------------------------------------------------------------*/ + +DEFUN(match_ip_address, + match_ip_address_cmd, + "match ip address (<1-199>|<1300-2699>|WORD)", + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ip address", argv[0]); +} + +DEFUN(no_match_ip_address, + no_match_ip_address_val_cmd, + "no match ip address (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + if (argc == 0) + return isis_route_match_delete(vty, vty->index, "ip address", NULL); + return isis_route_match_delete(vty, vty->index, "ip address", argv[0]); } +ALIAS(no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" +); + +/* ------------------------------------------------------------*/ + +DEFUN(match_ip_address_prefix_list, + match_ip_address_prefix_list_cmd, + "match ip address prefix-list WORD", + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ip address prefix-list", argv[0]); +} + +DEFUN(no_match_ip_address_prefix_list, + no_match_ip_address_prefix_list_cmd, + "no match ip address prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return isis_route_match_delete (vty, vty->index, "ip address prefix-list", NULL); + return isis_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]); +} + +ALIAS(no_match_ip_address_prefix_list, + no_match_ip_address_prefix_list_val_cmd, + "no match ip address prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n" +); + +/* ------------------------------------------------------------*/ + +DEFUN(match_ipv6_address, + match_ipv6_address_cmd, + "match ipv6 address WORD", + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ipv6 address", argv[0]); +} + +DEFUN(no_match_ipv6_address, + no_match_ipv6_address_val_cmd, + "no match ipv6 address WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + if (argc == 0) + return isis_route_match_delete(vty, vty->index, "ipv6 address", NULL); + return isis_route_match_delete(vty, vty->index, "ipv6 address", argv[0]); +} + +ALIAS(no_match_ipv6_address, + no_match_ipv6_address_cmd, + "no match ipv6 address", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" +); + +/* ------------------------------------------------------------*/ + +DEFUN(match_ipv6_address_prefix_list, + match_ipv6_address_prefix_list_cmd, + "match ipv6 address prefix-list WORD", + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ipv6 address prefix-list", argv[0]); +} + +DEFUN(no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_cmd, + "no match ipv6 address prefix-list", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", NULL); + return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]); +} + +ALIAS(no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_val_cmd, + "no match ipv6 address prefix-list WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n" +); + +/* ------------------------------------------------------------*/ + +/* set metric already exists e.g. in the ospf routemap. vtysh doesn't cope well with different + * commands at the same node, therefore add set metric with the same 32-bit range as ospf and + * verify that the input is a valid isis metric */ +DEFUN(set_metric, + set_metric_cmd, + "set metric <0-4294967295>", + SET_STR + "Metric vale for destination routing protocol\n" + "Metric value\n") +{ + return isis_route_set_add(vty, vty->index, "metric", argv[0]); +} + +DEFUN(no_set_metric, + no_set_metric_val_cmd, + "no set metric <0-4294967295>", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + if (argc == 0) + return isis_route_set_delete(vty, vty->index, "metric", NULL); + return isis_route_set_delete(vty, vty->index, "metric", argv[0]); +} + +ALIAS(no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric vale for destination routing protocol\n" +); + void -isis_route_map_init (void) +isis_route_map_init(void) { - route_map_init (); - route_map_init_vty (); + route_map_init(); + route_map_init_vty(); + + route_map_install_match(&route_match_ip_address_cmd); + install_element(RMAP_NODE, &match_ip_address_cmd); + install_element(RMAP_NODE, &no_match_ip_address_val_cmd); + install_element(RMAP_NODE, &no_match_ip_address_cmd); + + route_map_install_match(&route_match_ip_address_prefix_list_cmd); + install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + + route_map_install_match(&route_match_ipv6_address_cmd); + install_element(RMAP_NODE, &match_ipv6_address_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_val_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_cmd); + + route_map_install_match(&route_match_ipv6_address_prefix_list_cmd); + install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_val_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); - route_map_add_hook (isis_route_map_upd); - route_map_delete_hook (isis_route_map_upd); - route_map_event_hook (isis_route_map_event); + route_map_install_set(&route_set_metric_cmd); + install_element(RMAP_NODE, &set_metric_cmd); + install_element(RMAP_NODE, &no_set_metric_val_cmd); + install_element(RMAP_NODE, &no_set_metric_cmd); } diff --git a/isisd/isis_routemap.h b/isisd/isis_routemap.h new file mode 100644 index 000000000..1cb063fee --- /dev/null +++ b/isisd/isis_routemap.h @@ -0,0 +1,25 @@ +/* + * IS-IS Rout(e)ing protocol - isis_routemap.h + * + * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef ISIS_ROUTEMAP_H +#define ISIS_ROUTEMAP_H + +void isis_route_map_init(void); + +#endif diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index 848cc6ee3..b4017b5f4 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -992,8 +992,8 @@ tlv_add_lsp_entries (struct list *lsps, struct stream *stream) return add_tlv (LSP_ENTRIES, pos - value, value, stream); } -int -tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) +static int +tlv_add_ipv4_reachs (u_char tag, struct list *ipv4_reachs, struct stream *stream) { struct listnode *node; struct ipv4_reachability *reach; @@ -1006,7 +1006,7 @@ tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) if (pos - value + IPV4_REACH_LEN > 255) { retval = - add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); + add_tlv (tag, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; @@ -1025,10 +1025,23 @@ tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) pos += IPV4_MAX_BYTELEN; } - return add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); + return add_tlv (tag, pos - value, value, stream); +} + +int +tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream) +{ + return tlv_add_ipv4_reachs(IPV4_INT_REACHABILITY, ipv4_reachs, stream); } int +tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream) +{ + return tlv_add_ipv4_reachs(IPV4_EXT_REACHABILITY, ipv4_reachs, stream); +} + + +int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream) { struct listnode *node; diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h index f5b59a796..619003a0e 100644 --- a/isisd/isis_tlv.h +++ b/isisd/isis_tlv.h @@ -230,11 +230,13 @@ struct ipv6_reachability /* bits in control_info */ #define CTRL_INFO_DIRECTION 0x80 -#define DIRECTION_UP 0 -#define DIRECTION_DOWN 1 +#define DIRECTION_UP 0x00 +#define DIRECTION_DOWN 0x80 + #define CTRL_INFO_DISTRIBUTION 0x40 -#define DISTRIBUTION_INTERNAL 0 -#define DISTRIBUTION_EXTERNAL 1 +#define DISTRIBUTION_INTERNAL 0x00 +#define DISTRIBUTION_EXTERNAL 0x40 + #define CTRL_INFO_SUBTLVS 0x20 #endif /* HAVE_IPV6 */ @@ -313,7 +315,8 @@ int tlv_add_in_addr (struct in_addr *, struct stream *stream, u_char tag); int tlv_add_dynamic_hostname (struct hostname *hostname, struct stream *stream); int tlv_add_lsp_entries (struct list *lsps, struct stream *stream); -int tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream); +int tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream); +int tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream); int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream); #ifdef HAVE_IPV6 int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 00c0fd3c4..125a2f63d 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -4,6 +4,7 @@ * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering + * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas published by the Free @@ -32,6 +33,7 @@ #include "zclient.h" #include "stream.h" #include "linklist.h" +#include "nexthop.h" #include "vrf.h" #include "isisd/dict.h" @@ -531,8 +533,10 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, struct stream *stream; struct zapi_ipv4 api; struct prefix_ipv4 p; + struct prefix *p_generic = (struct prefix*)&p; stream = zclient->ibuf; + memset(&api, 0, sizeof(api)); memset (&p, 0, sizeof (struct prefix_ipv4)); api.type = stream_getc (stream); @@ -558,32 +562,80 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.distance = stream_getc (stream); if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) api.metric = stream_getl (stream); - else - api.metric = 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 (p.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS) + command = ZEBRA_IPV4_ROUTE_DELETE; if (command == ZEBRA_REDISTRIBUTE_IPV4_ADD) - { - if (isis->debugs & DEBUG_ZEBRA) - zlog_debug ("IPv4 Route add from Z"); - } + isis_redist_add(api.type, p_generic, api.distance, api.metric); + else + isis_redist_delete(api.type, p_generic); return 0; } -#ifdef HAVE_IPV6 static int isis_zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { + struct stream *stream; + struct zapi_ipv6 api; + struct prefix_ipv6 p; + struct prefix *p_generic = (struct prefix*)&p; + struct in6_addr nexthop; + unsigned long ifindex __attribute__((unused)); + + stream = zclient->ibuf; + memset(&api, 0, sizeof(api)); + memset(&p, 0, sizeof(struct prefix_ipv6)); + memset(&nexthop, 0, sizeof(nexthop)); + ifindex = 0; + + api.type = stream_getc(stream); + api.flags = stream_getc(stream); + api.message = stream_getc(stream); + + p.family = AF_INET6; + p.prefixlen = stream_getc(stream); + stream_get(&p.prefix, stream, PSIZE(p.prefixlen)); + + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) + { + api.nexthop_num = stream_getc(stream); /* this is always 1 */ + stream_get(&nexthop, stream, sizeof(nexthop)); + } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) + { + api.ifindex_num = stream_getc(stream); + ifindex = stream_getl(stream); + } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE)) + api.distance = stream_getc(stream); + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC)) + api.metric = stream_getl(stream); + + /* + * 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 (p.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS) + command = ZEBRA_IPV6_ROUTE_DELETE; + + if (command == ZEBRA_IPV6_ROUTE_ADD) + isis_redist_add(api.type, p_generic, api.distance, api.metric); + else + isis_redist_delete(api.type, p_generic); + return 0; } -#endif - -#define ISIS_TYPE_IS_REDISTRIBUTED(T) \ -T == ZEBRA_ROUTE_MAX ? \ - vrf_bitmap_check (zclient->default_information, VRF_DEFAULT) : \ - (vrf_bitmap_check (zclient->redist[AFI_IP][type], VRF_DEFAULT) || \ - vrf_bitmap_check (zclient->redist[AFI_IP6][type], VRF_DEFAULT) int isis_distribute_list_update (int routetype) @@ -591,14 +643,23 @@ isis_distribute_list_update (int routetype) return 0; } -#if 0 /* Not yet. */ -static int -isis_redistribute_default_set (int routetype, int metric_type, - int metric_value) +void +isis_zebra_redistribute_set(int type) { - return 0; + if (type == DEFAULT_ROUTE) + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, VRF_DEFAULT); + else + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, 0, VRF_DEFAULT); +} + +void +isis_zebra_redistribute_unset(int type) +{ + if (type == DEFAULT_ROUTE) + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, VRF_DEFAULT); + else + zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, type, 0, VRF_DEFAULT); } -#endif /* 0 */ static void isis_zebra_connected (struct zclient *zclient) diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index cf6a738a1..217e3d48c 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -28,5 +28,7 @@ void isis_zebra_init(struct thread_master *); void isis_zebra_route_update (struct prefix *prefix, struct isis_route_info *route_info); int isis_distribute_list_update (int routetype); +void isis_zebra_redistribute_set(int type); +void isis_zebra_redistribute_unset(int type); #endif /* _ZEBRA_ISIS_ZEBRA_H */ diff --git a/isisd/isisd.c b/isisd/isisd.c index 500d2b24c..9a9c77cc6 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -280,6 +280,8 @@ isis_area_destroy (struct vty *vty, const char *area_tag) } #endif /* HAVE_IPV6 */ + isis_redist_area_finish(area); + for (ALL_LIST_ELEMENTS (area->area_addrs, node, nnode, addr)) { list_delete_node (area->area_addrs, node); @@ -3050,6 +3052,8 @@ isis_config_write (struct vty *vty) vty_out (vty, " is-type level-2-only%s", VTY_NEWLINE); write++; } + write += isis_redist_config_write(vty, area, AF_INET); + write += isis_redist_config_write(vty, area, AF_INET6); /* ISIS - Lsp generation interval */ if (area->lsp_gen_interval[0] == area->lsp_gen_interval[1]) { diff --git a/isisd/isisd.h b/isisd/isisd.h index 35d27dbe8..6fe1eb814 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -27,16 +27,12 @@ #define ISISD_VERSION "0.0.7" +#include "isisd/isis_redist.h" + /* uncomment if you are a developer in bug hunt */ /* #define EXTREME_DEBUG */ /* #define EXTREME_TLV_DEBUG */ -struct rmap -{ - char *name; - struct route_map *map; -}; - struct isis { u_long process_id; @@ -55,30 +51,7 @@ struct isis time_t uptime; /* when did we start */ struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ - /* Redistributed external information. */ - struct route_table *external_info[ZEBRA_ROUTE_MAX + 1]; - /* Redistribute metric info. */ - struct - { - int type; /* Internal or External */ - int value; /* metric value */ - } dmetric[ZEBRA_ROUTE_MAX + 1]; - - struct - { - char *name; - struct route_map *map; - } rmap[ZEBRA_ROUTE_MAX + 1]; -#ifdef HAVE_IPV6 - struct - { - struct - { - char *name; - struct route_map *map; - } rmap[ZEBRA_ROUTE_MAX + 1]; - } inet6_afmode; -#endif + struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; }; extern struct isis *isis; @@ -147,6 +120,9 @@ struct isis_area #endif /* HAVE_IPV6 */ /* Counters */ u_int32_t circuit_state_changes; + struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT] + [ZEBRA_ROUTE_MAX + 1][ISIS_LEVELS]; + struct route_table *ext_reach[REDIST_PROTOCOL_COUNT][ISIS_LEVELS]; #ifdef TOPOLOGY_GENERATE struct list *topology; diff --git a/lib/prefix.c b/lib/prefix.c index 5f188f09f..1614cd560 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -187,6 +187,17 @@ prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen) return prefix_bit((const u_char *) &prefix->s6_addr, prefixlen); } +int +str2family(const char *string) +{ + if (!strcmp("ipv4", string)) + return AF_INET; + else if (!strcmp("ipv6", string)) + return AF_INET6; + else + return -1; +} + /* Address Famiy Identifier to Address Family converter. */ int afi2family (afi_t afi) diff --git a/lib/prefix.h b/lib/prefix.h index feacc2ae2..73ae0f25a 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -167,6 +167,7 @@ union prefix46constptr #endif /*s6_addr32*/ /* Prototypes. */ +extern int str2family(const char *); extern int afi2family (afi_t); extern afi_t family2afi (int); diff --git a/lib/routemap.h b/lib/routemap.h index a90966b38..f5981a87f 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -48,7 +48,8 @@ typedef enum RMAP_OSPF, RMAP_OSPF6, RMAP_BGP, - RMAP_ZEBRA + RMAP_ZEBRA, + RMAP_ISIS, } route_map_object_t; typedef enum |