diff --git a/isisd/Makefile.am b/isisd/Makefile.am index c97385f87a..2973820eed 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -16,7 +16,7 @@ libisis_a_SOURCES = \ 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_redist.c isis_route.c isis_routemap.c isis_te.c \ - isis_vty.c + isis_vty.c isis_mt.c noinst_HEADERS = \ @@ -25,7 +25,7 @@ noinst_HEADERS = \ 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_redist.h \ - isis_route.h isis_routemap.h isis_te.h + isis_route.h isis_routemap.h isis_te.h isis_mt.h isisd_SOURCES = \ isis_main.c $(libisis_a_SOURCES) \ diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index f550924874..fea99ec907 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -47,6 +47,7 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_spf.h" #include "isisd/isis_events.h" +#include "isisd/isis_mt.h" extern struct isis *isis; @@ -148,6 +149,8 @@ isis_delete_adj (void *arg) if (adj->ipv6_addrs) list_delete (adj->ipv6_addrs); + adj_mt_finish(adj); + XFREE (MTYPE_ISIS_ADJACENCY, adj); return; } @@ -521,3 +524,20 @@ isis_adj_build_up_list (struct list *adjdb, struct list *list) return; } + +int +isis_adj_usage2levels(enum isis_adj_usage usage) +{ + switch (usage) + { + case ISIS_ADJ_LEVEL1: + return IS_LEVEL_1; + case ISIS_ADJ_LEVEL2: + return IS_LEVEL_2; + case ISIS_ADJ_LEVEL1AND2: + return IS_LEVEL_1 | IS_LEVEL_2; + default: + break; + } + return 0; +} diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 8539b03d6b..4f89e30960 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -97,6 +97,8 @@ struct isis_adjacency int flaps; /* number of adjacency flaps */ struct thread *t_expire; /* expire after hold_time */ struct isis_circuit *circuit; /* back pointer */ + uint16_t *mt_set; /* Topologies this adjacency is valid for */ + unsigned int mt_count; /* Number of entries in mt_set */ }; struct isis_adjacency *isis_adj_lookup (const u_char * sysid, struct list *adjdb); @@ -112,5 +114,6 @@ int isis_adj_expire (struct thread *thread); void isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail); void isis_adj_build_neigh_list (struct list *adjdb, struct list *list); void isis_adj_build_up_list (struct list *adjdb, struct list *list); +int isis_adj_usage2levels(enum isis_adj_usage usage); #endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 3a5eaf5585..0a1610b6f2 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -212,16 +212,11 @@ isis_sock_init (struct isis_circuit *circuit) goto end; } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) + if (if_is_broadcast(circuit->interface)) { circuit->tx = isis_send_pdu_bcast; circuit->rx = isis_recv_pdu_bcast; } - else if (circuit->circ_type == CIRCUIT_T_P2P) - { - circuit->tx = isis_send_pdu_p2p; - circuit->rx = isis_recv_pdu_p2p; - } else { zlog_warn ("isis_sock_init(): unknown circuit type"); @@ -283,23 +278,6 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) return ISIS_OK; } -int -isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa) -{ - int bytesread; - - bytesread = stream_read (circuit->rcv_stream, circuit->fd, - circuit->interface->mtu); - - if (bytesread < 0) - { - zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno)); - return ISIS_WARNING; - } - - return ISIS_OK; -} - int isis_send_pdu_bcast (struct isis_circuit *circuit, int level) { @@ -327,7 +305,8 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) else memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN); memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN); - eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); + size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN; + eth->ether_type = htons(isis_ethertype(frame_size)); /* * Then the LLC @@ -354,10 +333,4 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) return ISIS_OK; } -int -isis_send_pdu_p2p (struct isis_circuit *circuit, int level) -{ - return ISIS_OK; -} - #endif /* ISIS_METHOD == ISIS_METHOD_BPF */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 6207ae189a..6caf8200ee 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -60,6 +60,7 @@ #include "isisd/isis_csm.h" #include "isisd/isis_events.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" DEFINE_QOBJ_TYPE(isis_circuit) @@ -102,6 +103,8 @@ isis_circuit_new () circuit->mtc = mpls_te_circuit_new(); + circuit_mt_init(circuit); + QOBJ_REG (circuit, isis_circuit); return circuit; @@ -117,6 +120,8 @@ isis_circuit_del (struct isis_circuit *circuit) isis_circuit_if_unbind (circuit, circuit->interface); + circuit_mt_finish(circuit); + /* and lastly the circuit itself */ XFREE (MTYPE_ISIS_CIRCUIT, circuit); @@ -1215,6 +1220,7 @@ isis_interface_config_write (struct vty *vty) VTY_NEWLINE); write++; } + write += circuit_write_mt_settings(circuit, vty); } vty_out (vty, "!%s", VTY_NEWLINE); } @@ -1382,6 +1388,22 @@ isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) return 0; } +int +isis_circuit_mt_enabled_set (struct isis_circuit *circuit, uint16_t mtid, + bool enabled) +{ + struct isis_circuit_mt_setting *setting; + + setting = circuit_get_mt_setting(circuit, mtid); + if (setting->enabled != enabled) + { + setting->enabled = enabled; + lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } + + return CMD_SUCCESS; +} + int isis_if_new_hook (struct interface *ifp) { diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index bb0dc0f983..16dfa6304c 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -123,6 +123,7 @@ struct isis_circuit struct mpls_te_circuit *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */ int ip_router; /* Route IP ? */ int is_passive; /* Is Passive ? */ + struct list *mt_settings; /* IS-IS MT Settings */ struct list *ip_addrs; /* our IP addresses */ int ipv6_router; /* Route IPv6 ? */ struct list *ipv6_link; /* our link local IPv6 addresses */ @@ -187,4 +188,6 @@ int isis_circuit_passwd_unset (struct isis_circuit *circuit); int isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd); int isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd); +int isis_circuit_mt_enabled_set (struct isis_circuit *circuit, uint16_t mtid, bool enabled); + #endif /* _ZEBRA_ISIS_CIRCUIT_H */ diff --git a/isisd/isis_constants.h b/isisd/isis_constants.h index 17616d671b..ec0f6fb62c 100644 --- a/isisd/isis_constants.h +++ b/isisd/isis_constants.h @@ -171,4 +171,14 @@ #define ETH_ALEN 6 #endif +#define MAX_LLC_LEN 0x5ff +#define ETHERTYPE_EXT_LLC 0x8870 + +static inline uint16_t isis_ethertype(size_t len) +{ + if (len > MAX_LLC_LEN) + return ETHERTYPE_EXT_LLC; + return len; +} + #endif /* ISIS_CONSTANTS_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index f633a8fb78..955a73ef61 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -53,6 +53,7 @@ #include "isisd/isis_adjacency.h" #include "isisd/isis_spf.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" /* staticly assigned vars for printing purposes */ char lsp_bits_string[200]; /* FIXME: enough ? */ @@ -501,6 +502,7 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream, expected |= TLVFLAG_TE_IPV4_REACHABILITY; expected |= TLVFLAG_TE_ROUTER_ID; } + expected |= TLVFLAG_MT_ROUTER_INFORMATION; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV4_INT_REACHABILITY; expected |= TLVFLAG_IPV4_EXT_REACHABILITY; @@ -826,6 +828,107 @@ lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost) lsp_bits2string (&lsp->lsp_header->lsp_bits), VTY_NEWLINE); } +static void +lsp_print_mt_reach(struct list *list, struct vty *vty, + char dynhost, uint16_t mtid) +{ + struct listnode *node; + struct te_is_neigh *neigh; + + for (ALL_LIST_ELEMENTS_RO (list, node, neigh)) + { + u_char lspid[255]; + + lspid_print(neigh->neigh_id, lspid, dynhost, 0); + if (mtid == ISIS_MT_IPV4_UNICAST) + { + vty_out(vty, " Metric : %-8d IS-Extended : %s%s", + GET_TE_METRIC(neigh), lspid, VTY_NEWLINE); + } + else + { + vty_out(vty, " Metric : %-8d MT-Reach : %s %s%s", + GET_TE_METRIC(neigh), lspid, + isis_mtid2str(mtid), VTY_NEWLINE); + } + if (IS_MPLS_TE(isisMplsTE)) + mpls_te_print_detail(vty, neigh); + } +} + +static void +lsp_print_mt_ipv6_reach(struct list *list, struct vty *vty, uint16_t mtid) +{ + struct listnode *node; + struct ipv6_reachability *ipv6_reach; + struct in6_addr in6; + u_char buff[BUFSIZ]; + + for (ALL_LIST_ELEMENTS_RO (list, node, ipv6_reach)) + { + memset (&in6, 0, sizeof (in6)); + memcpy (in6.s6_addr, ipv6_reach->prefix, + PSIZE (ipv6_reach->prefix_len)); + inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ); + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if ((ipv6_reach->control_info & + CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) + vty_out (vty, " Metric : %-8d IPv6-Internal : %s/%d%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, VTY_NEWLINE); + else + vty_out (vty, " Metric : %-8d IPv6-External : %s/%d%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, VTY_NEWLINE); + } + else + { + if ((ipv6_reach->control_info & + CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) + vty_out (vty, " Metric : %-8d IPv6-MT-Int : %s/%d %s%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, + isis_mtid2str(mtid), VTY_NEWLINE); + else + vty_out (vty, " Metric : %-8d IPv6-MT-Ext : %s/%d %s%s", + ntohl (ipv6_reach->metric), + buff, ipv6_reach->prefix_len, + isis_mtid2str(mtid), VTY_NEWLINE); + } + } +} + +static void +lsp_print_mt_ipv4_reach(struct list *list, struct vty *vty, uint16_t mtid) +{ + struct listnode *node; + struct te_ipv4_reachability *te_ipv4_reach; + + for (ALL_LIST_ELEMENTS_RO (list, node, te_ipv4_reach)) + { + if (mtid == ISIS_MT_IPV4_UNICAST) + { + /* FIXME: There should be better way to output this stuff. */ + vty_out (vty, " Metric : %-8d IPv4-Extended : %s/%d%s", + ntohl (te_ipv4_reach->te_metric), + inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, + te_ipv4_reach->control)), + te_ipv4_reach->control & 0x3F, VTY_NEWLINE); + } + else + { + /* FIXME: There should be better way to output this stuff. */ + vty_out (vty, " Metric : %-8d IPv4-MT : %s/%d %s%s", + ntohl (te_ipv4_reach->te_metric), + inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, + te_ipv4_reach->control)), + te_ipv4_reach->control & 0x3F, + isis_mtid2str(mtid), VTY_NEWLINE); + } + } +} + void lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) { @@ -833,13 +936,12 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) int i; struct listnode *lnode; struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; struct ipv4_reachability *ipv4_reach; struct in_addr *ipv4_addr; - struct te_ipv4_reachability *te_ipv4_reach; - struct ipv6_reachability *ipv6_reach; - struct in6_addr in6; - u_char buff[BUFSIZ]; + struct mt_router_info *mt_router_info; + struct tlv_mt_ipv6_reachs *mt_ipv6_reachs; + struct tlv_mt_neighbors *mt_is_neigh; + struct tlv_mt_ipv4_reachs *mt_ipv4_reachs; u_char LSPid[255]; u_char hostname[255]; u_char ipv4_reach_prefix[20]; @@ -877,6 +979,14 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) } } + for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.mt_router_info, lnode, mt_router_info)) + { + vty_out (vty, " MT : %s%s%s", + isis_mtid2str(mt_router_info->mtid), + mt_router_info->overload ? " (overload)" : "", + VTY_NEWLINE); + } + /* for the hostname tlv */ if (lsp->tlv_data.hostname) { @@ -946,49 +1056,31 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) ipv4_reach->metrics.metric_default, ipv4_reach_prefix, ipv4_reach_mask, VTY_NEWLINE); } - + /* IPv6 tlv */ - if (lsp->tlv_data.ipv6_reachs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, lnode, ipv6_reach)) - { - memset (&in6, 0, sizeof (in6)); - memcpy (in6.s6_addr, ipv6_reach->prefix, - PSIZE (ipv6_reach->prefix_len)); - inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ); - if ((ipv6_reach->control_info & - CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) - vty_out (vty, " Metric : %-8d IPv6-Internal : %s/%d%s", - ntohl (ipv6_reach->metric), - buff, ipv6_reach->prefix_len, VTY_NEWLINE); - else - vty_out (vty, " Metric : %-8d IPv6-External : %s/%d%s", - ntohl (ipv6_reach->metric), - buff, ipv6_reach->prefix_len, VTY_NEWLINE); - } + lsp_print_mt_ipv6_reach(lsp->tlv_data.ipv6_reachs, vty, + ISIS_MT_IPV4_UNICAST); + + /* MT IPv6 reachability tlv */ + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv6_reachs, lnode, mt_ipv6_reachs)) + lsp_print_mt_ipv6_reach(mt_ipv6_reachs->list, vty, mt_ipv6_reachs->mtid); /* TE IS neighbor tlv */ - if (lsp->tlv_data.te_is_neighs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh)) - { - lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0); - vty_out (vty, " Metric : %-8d IS-Extended : %s%s", - GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE); - if (IS_MPLS_TE(isisMplsTE)) - mpls_te_print_detail(vty, te_is_neigh); - } + lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty, + dynhost, ISIS_MT_IPV4_UNICAST); + + /* MT IS neighbor tlv */ + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_is_neighs, lnode, mt_is_neigh)) + lsp_print_mt_reach(mt_is_neigh->list, vty, dynhost, mt_is_neigh->mtid); /* TE IPv4 tlv */ - if (lsp->tlv_data.te_ipv4_reachs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, lnode, - te_ipv4_reach)) - { - /* FIXME: There should be better way to output this stuff. */ - vty_out (vty, " Metric : %-8d IPv4-Extended : %s/%d%s", - ntohl (te_ipv4_reach->te_metric), - inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, - te_ipv4_reach->control)), - te_ipv4_reach->control & 0x3F, VTY_NEWLINE); - } + lsp_print_mt_ipv4_reach(lsp->tlv_data.te_ipv4_reachs, vty, + ISIS_MT_IPV4_UNICAST); + + /* MT IPv4 reachability tlv */ + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv4_reachs, lnode, mt_ipv4_reachs)) + lsp_print_mt_ipv4_reach(mt_ipv4_reachs->list, vty, mt_ipv4_reachs->mtid); + vty_out (vty, "%s", VTY_NEWLINE); return; @@ -1028,6 +1120,42 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) return lsp_count; } +static void +_lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, + int frag_thold, + unsigned int tlv_build_func (struct list *, struct stream *, + void *arg), + void *arg) +{ + while (*from && listcount(*from)) + { + unsigned int count; + + count = tlv_build_func(*from, lsp->pdu, arg); + + if (listcount(*to) != 0 || count != listcount(*from)) + { + struct listnode *node, *nnode; + void *elem; + + for (ALL_LIST_ELEMENTS(*from, node, nnode, elem)) + { + if (!count) + break; + listnode_add (*to, elem); + list_delete_node (*from, node); + --count; + } + } + else + { + list_free (*to); + *to = *from; + *from = NULL; + } + } +} + #define FRAG_THOLD(S,T) \ ((STREAM_SIZE(S)*T)/100) @@ -1085,64 +1213,6 @@ lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, return; } -/* Process IS_NEIGHBOURS TLV with TE subTLVs */ -void -lsp_te_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, int frag_thold) -{ - int count, size = 0; - struct listnode *node, *nextnode; - struct te_is_neigh *elem; - - /* Start computing real size of TLVs */ - for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) - size = size + elem->sub_tlvs_length + IS_NEIGHBOURS_LEN; - - /* can we fit all ? */ - if (!FRAG_NEEDED (lsp->pdu, frag_thold, size)) - { - tlv_add_te_is_neighs (*from, lsp->pdu); - if (listcount (*to) != 0) - { - for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) - { - listnode_add (*to, elem); - list_delete_node (*from, node); - } - } - else - { - list_free (*to); - *to = *from; - *from = NULL; - } - } - else - { - /* fit all we can */ - /* Compute remaining place in LSP PDU */ - count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 - - (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu)); - /* Determine size of TE SubTLVs */ - elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from)); - count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN; - if (count > 0) - { - while (count > 0) - { - listnode_add (*to, listgetdata ((struct listnode *)listhead (*from))); - listnode_delete (*from, listgetdata ((struct listnode *)listhead (*from))); - - elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from)); - count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN; - } - - tlv_add_te_is_neighs (*to, lsp->pdu); - } - } - lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); - return; -} - static u_int16_t lsp_rem_lifetime (struct isis_area *area, int level) { @@ -1278,6 +1348,24 @@ lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area, } } +static struct list * +tlv_get_ipv6_reach_list(struct isis_area *area, struct tlvs *tlv_data) +{ + uint16_t mtid = isis_area_ipv6_topology(area); + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlv_data->ipv6_reachs) + { + tlv_data->ipv6_reachs = list_new(); + tlv_data->ipv6_reachs->del = free_tlv; + } + return tlv_data->ipv6_reachs; + } + + struct tlv_mt_ipv6_reachs *reachs = tlvs_get_mt_ipv6_reachs(tlv_data, mtid); + return reachs->list; +} + static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, struct tlvs *tlv_data) @@ -1287,6 +1375,7 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, struct prefix_ipv6 *ipv6; struct isis_ext_info *info; struct ipv6_reachability *ip6reach; + struct list *reach_list = NULL; er_table = get_ext_reach(area, AF_INET6, lsp->level); if (!er_table) @@ -1300,11 +1389,9 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, 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; - } + if (!reach_list) + reach_list = tlv_get_ipv6_reach_list(area, tlv_data); + ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach)); if (info->metric > MAX_WIDE_PATH_METRIC) ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC); @@ -1313,7 +1400,7 @@ lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, 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); + listnode_add(reach_list, ip6reach); } } @@ -1342,6 +1429,7 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) struct te_ipv4_reachability *te_ipreach; struct isis_adjacency *nei; struct prefix_ipv6 *ipv6, ip6prefix; + struct list *ipv6_reachs = NULL; struct ipv6_reachability *ip6reach; struct tlvs tlv_data; struct isis_lsp *lsp0 = lsp; @@ -1402,6 +1490,32 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu); } + if (area_is_mt(area)) + { + lsp_debug("ISIS (%s): Adding MT router tlv...", area->area_tag); + lsp->tlv_data.mt_router_info = list_new(); + lsp->tlv_data.mt_router_info->del = free_tlv; + + struct isis_area_mt_setting **mt_settings; + unsigned int mt_count; + + mt_settings = area_mt_settings(area, &mt_count); + for (unsigned int i = 0; i < mt_count; i++) + { + struct mt_router_info *info; + + info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); + info->mtid = mt_settings[i]->mtid; + info->overload = mt_settings[i]->overload; + listnode_add(lsp->tlv_data.mt_router_info, info); + lsp_debug("ISIS (%s): MT %s", area->area_tag, isis_mtid2str(info->mtid)); + } + tlv_add_mt_router_info (lsp->tlv_data.mt_router_info, lsp->pdu); + } + else + { + lsp_debug("ISIS (%s): Not adding MT router tlv (disabled)", area->area_tag); + } /* Dynamic Hostname */ if (area->dynhostname) { @@ -1551,12 +1665,9 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) if (circuit->ipv6_router && circuit->ipv6_non_link && circuit->ipv6_non_link->count > 0) { + if (!ipv6_reachs) + ipv6_reachs = tlv_get_ipv6_reach_list(area, &tlv_data); - if (tlv_data.ipv6_reachs == NULL) - { - tlv_data.ipv6_reachs = list_new (); - tlv_data.ipv6_reachs->del = free_tlv; - } for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) { ip6reach = @@ -1579,7 +1690,7 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr, sizeof (ip6reach->prefix)); - listnode_add (tlv_data.ipv6_reachs, ip6reach); + listnode_add (ipv6_reachs, ip6reach); } } @@ -1658,10 +1769,8 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ te_is_neigh->sub_tlvs_length = 0; - listnode_add (tlv_data.te_is_neighs, te_is_neigh); - lsp_debug("ISIS (%s): Adding DIS %s.%02x as te-style neighbor", - area->area_tag, sysid_print(te_is_neigh->neigh_id), - LSP_PSEUDO_ID(te_is_neigh->neigh_id)); + tlvs_add_mt_bcast(&tlv_data, circuit, level, te_is_neigh); + XFREE(MTYPE_ISIS_TLV, te_is_neigh); } } } @@ -1718,9 +1827,9 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) else /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ te_is_neigh->sub_tlvs_length = 0; - listnode_add (tlv_data.te_is_neighs, te_is_neigh); - lsp_debug("ISIS (%s): Adding te-style is reach for %s", area->area_tag, - sysid_print(te_is_neigh->neigh_id)); + + tlvs_add_mt_p2p(&tlv_data, circuit, te_is_neigh); + XFREE(MTYPE_ISIS_TLV, te_is_neigh); } } else @@ -1766,35 +1875,62 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) 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!). */ while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) { if (lsp->tlv_data.te_ipv4_reachs == NULL) lsp->tlv_data.te_ipv4_reachs = list_new (); - lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, - &lsp->tlv_data.te_ipv4_reachs, - TE_IPV4_REACH_LEN, area->lsp_frag_threshold, - tlv_add_te_ipv4_reachs); + _lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, &lsp->tlv_data.te_ipv4_reachs, + area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, NULL); if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + struct tlv_mt_ipv4_reachs *mt_ipv4_reachs; + for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv4_reachs, node, mt_ipv4_reachs)) + { + while (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list)) + { + struct tlv_mt_ipv4_reachs *frag_mt_ipv4_reachs; + + frag_mt_ipv4_reachs = tlvs_get_mt_ipv4_reachs(&lsp->tlv_data, mt_ipv4_reachs->mtid); + _lsp_tlv_fit (lsp, &mt_ipv4_reachs->list, &frag_mt_ipv4_reachs->list, + area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, + &mt_ipv4_reachs->mtid); + if (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + } + while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) { if (lsp->tlv_data.ipv6_reachs == NULL) lsp->tlv_data.ipv6_reachs = list_new (); - lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, - &lsp->tlv_data.ipv6_reachs, - IPV6_REACH_LEN, area->lsp_frag_threshold, - tlv_add_ipv6_reachs); + _lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, &lsp->tlv_data.ipv6_reachs, + area->lsp_frag_threshold, tlv_add_ipv6_reachs, NULL); if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + struct tlv_mt_ipv6_reachs *mt_ipv6_reachs; + for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv6_reachs, node, mt_ipv6_reachs)) + { + while (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list)) + { + struct tlv_mt_ipv6_reachs *frag_mt_ipv6_reachs; + + frag_mt_ipv6_reachs = tlvs_get_mt_ipv6_reachs(&lsp->tlv_data, mt_ipv6_reachs->mtid); + _lsp_tlv_fit (lsp, &mt_ipv6_reachs->list, &frag_mt_ipv6_reachs->list, + area->lsp_frag_threshold, tlv_add_ipv6_reachs, + &mt_ipv6_reachs->mtid); + if (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + } + while (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) { if (lsp->tlv_data.is_neighs == NULL) @@ -1812,13 +1948,31 @@ lsp_build (struct isis_lsp *lsp, struct isis_area *area) { if (lsp->tlv_data.te_is_neighs == NULL) lsp->tlv_data.te_is_neighs = list_new (); - lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, - IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, - tlv_add_te_is_neighs); + _lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, + area->lsp_frag_threshold, tlv_add_te_is_neighs, NULL); if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + + struct tlv_mt_neighbors *mt_neighs; + for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_is_neighs, node, mt_neighs)) + { + while (mt_neighs->list && listcount(mt_neighs->list)) + { + struct tlv_mt_neighbors *frag_mt_neighs; + + frag_mt_neighs = tlvs_get_mt_neighbors(&lsp->tlv_data, mt_neighs->mtid); + _lsp_tlv_fit (lsp, &mt_neighs->list, &frag_mt_neighs->list, + area->lsp_frag_threshold, tlv_add_te_is_neighs, + &mt_neighs->mtid); + if (mt_neighs->list && listcount(mt_neighs->list)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + } + + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); free_tlvs (&tlv_data); @@ -2255,7 +2409,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu); if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0) - tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu); + tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu, NULL); if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0) tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu); diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 24fae57a7b..6f697df62c 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -108,8 +108,6 @@ void lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost); int lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost); const char *lsp_bits2string (u_char *); -void lsp_te_tlv_fit (struct isis_lsp *lsp, struct list **from, - struct list **to, int frag_thold); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags (struct isis_lsp *lsp); diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c new file mode 100644 index 0000000000..42e7b57aa4 --- /dev/null +++ b/isisd/isis_mt.c @@ -0,0 +1,724 @@ +/* + * IS-IS Rout(e)ing protocol - Multi Topology Support + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR 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, or (at your option) any + * later version. + * + * FRR 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include +#include "isisd/isisd.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_mt.h" + +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") +DEFINE_MTYPE_STATIC(ISISD, MT_NEIGHBORS, "ISIS MT Neighbors for TLV") +DEFINE_MTYPE_STATIC(ISISD, MT_IPV4_REACHS, "ISIS MT IPv4 Reachabilities for TLV") +DEFINE_MTYPE_STATIC(ISISD, MT_IPV6_REACHS, "ISIS MT IPv6 Reachabilities for TLV") + +uint16_t isis_area_ipv6_topology(struct isis_area *area) +{ + struct isis_area_mt_setting *area_mt_setting; + area_mt_setting = area_lookup_mt_setting(area, ISIS_MT_IPV6_UNICAST); + + if (area_mt_setting && area_mt_setting->enabled) + return ISIS_MT_IPV6_UNICAST; + return ISIS_MT_IPV4_UNICAST; +} + +/* MT naming api */ +const char *isis_mtid2str(uint16_t mtid) +{ + static char buf[sizeof("65535")]; + + switch(mtid) + { + case ISIS_MT_IPV4_UNICAST: + return "ipv4-unicast"; + case ISIS_MT_IPV4_MGMT: + return "ipv4-mgmt"; + case ISIS_MT_IPV6_UNICAST: + return "ipv6-unicast"; + case ISIS_MT_IPV4_MULTICAST: + return "ipv4-multicast"; + case ISIS_MT_IPV6_MULTICAST: + return "ipv6-multicast"; + case ISIS_MT_IPV6_MGMT: + return "ipv6-mgmt"; + default: + snprintf(buf, sizeof(buf), "%" PRIu16, mtid); + return buf; + } +} + +uint16_t isis_str2mtid(const char *name) +{ + if (!strcmp(name,"ipv4-unicast")) + return ISIS_MT_IPV4_UNICAST; + if (!strcmp(name,"ipv4-mgmt")) + return ISIS_MT_IPV4_MGMT; + if (!strcmp(name,"ipv6-unicast")) + return ISIS_MT_IPV6_UNICAST; + if (!strcmp(name,"ipv4-multicast")) + return ISIS_MT_IPV4_MULTICAST; + if (!strcmp(name,"ipv6-multicast")) + return ISIS_MT_IPV6_MULTICAST; + if (!strcmp(name,"ipv6-mgmt")) + return ISIS_MT_IPV6_MGMT; + return -1; +} + +/* General MT settings api */ + +struct mt_setting { + ISIS_MT_INFO_FIELDS; +}; + +static void * +lookup_mt_setting(struct list *mt_list, uint16_t mtid) +{ + struct listnode *node; + struct mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO(mt_list, node, setting)) + { + if (setting->mtid == mtid) + return setting; + } + return NULL; +} + +static void +add_mt_setting(struct list **mt_list, void *setting) +{ + if (!*mt_list) + *mt_list = list_new(); + listnode_add(*mt_list, setting); +} + +/* Area specific MT settings api */ + +struct isis_area_mt_setting* +area_lookup_mt_setting(struct isis_area *area, uint16_t mtid) +{ + return lookup_mt_setting(area->mt_settings, mtid); +} + +struct isis_area_mt_setting* +area_new_mt_setting(struct isis_area *area, uint16_t mtid) +{ + struct isis_area_mt_setting *setting; + + setting = XCALLOC(MTYPE_MT_AREA_SETTING, sizeof(*setting)); + setting->mtid = mtid; + return setting; +} + +static void +area_free_mt_setting(void *setting) +{ + XFREE(MTYPE_MT_AREA_SETTING, setting); +} + +void +area_add_mt_setting(struct isis_area *area, struct isis_area_mt_setting *setting) +{ + add_mt_setting(&area->mt_settings, setting); +} + +void +area_mt_init(struct isis_area *area) +{ + struct isis_area_mt_setting *v4_unicast_setting; + + /* MTID 0 is always enabled */ + v4_unicast_setting = area_new_mt_setting(area, ISIS_MT_IPV4_UNICAST); + v4_unicast_setting->enabled = true; + add_mt_setting(&area->mt_settings, v4_unicast_setting); + area->mt_settings->del = area_free_mt_setting; +} + +void +area_mt_finish(struct isis_area *area) +{ + list_delete(area->mt_settings); + area->mt_settings = NULL; +} + +struct isis_area_mt_setting * +area_get_mt_setting(struct isis_area *area, uint16_t mtid) +{ + struct isis_area_mt_setting *setting; + + setting = area_lookup_mt_setting(area, mtid); + if (!setting) + { + setting = area_new_mt_setting(area, mtid); + area_add_mt_setting(area, setting); + } + return setting; +} + +int +area_write_mt_settings(struct isis_area *area, struct vty *vty) +{ + int written = 0; + struct listnode *node; + struct isis_area_mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) + { + const char *name = isis_mtid2str(setting->mtid); + if (name && setting->enabled) + { + if (setting->mtid == ISIS_MT_IPV4_UNICAST) + continue; /* always enabled, no need to write out config */ + vty_out (vty, " topology %s%s%s", name, + setting->overload ? " overload" : "", + VTY_NEWLINE); + written++; + } + } + return written; +} + +bool area_is_mt(struct isis_area *area) +{ + struct listnode *node, *node2; + struct isis_area_mt_setting *setting; + struct isis_circuit *circuit; + struct isis_circuit_mt_setting *csetting; + + for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) + { + if (setting->enabled && setting->mtid != ISIS_MT_IPV4_UNICAST) + return true; + } + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + { + for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node2, csetting)) + { + if (!csetting->enabled && csetting->mtid == ISIS_MT_IPV4_UNICAST) + return true; + } + } + + return false; +} + +struct isis_area_mt_setting** +area_mt_settings(struct isis_area *area, unsigned int *mt_count) +{ + static unsigned int size = 0; + static struct isis_area_mt_setting **rv = NULL; + + unsigned int count = 0; + struct listnode *node; + struct isis_area_mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO(area->mt_settings, node, setting)) + { + if (!setting->enabled) + continue; + + count++; + if (count > size) + { + rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv)); + size = count; + } + rv[count-1] = setting; + } + + *mt_count = count; + return rv; +} + +/* Circuit specific MT settings api */ + +struct isis_circuit_mt_setting* +circuit_lookup_mt_setting(struct isis_circuit *circuit, uint16_t mtid) +{ + return lookup_mt_setting(circuit->mt_settings, mtid); +} + +struct isis_circuit_mt_setting* +circuit_new_mt_setting(struct isis_circuit *circuit, uint16_t mtid) +{ + struct isis_circuit_mt_setting *setting; + + setting = XCALLOC(MTYPE_MT_CIRCUIT_SETTING, sizeof(*setting)); + setting->mtid = mtid; + setting->enabled = true; /* Enabled is default for circuit */ + return setting; +} + +static void +circuit_free_mt_setting(void *setting) +{ + XFREE(MTYPE_MT_CIRCUIT_SETTING, setting); +} + +void +circuit_add_mt_setting(struct isis_circuit *circuit, + struct isis_circuit_mt_setting *setting) +{ + add_mt_setting(&circuit->mt_settings, setting); +} + +void +circuit_mt_init(struct isis_circuit *circuit) +{ + circuit->mt_settings = list_new(); + circuit->mt_settings->del = circuit_free_mt_setting; +} + +void +circuit_mt_finish(struct isis_circuit *circuit) +{ + list_delete(circuit->mt_settings); + circuit->mt_settings = NULL; +} + +struct isis_circuit_mt_setting* +circuit_get_mt_setting(struct isis_circuit *circuit, uint16_t mtid) +{ + struct isis_circuit_mt_setting *setting; + + setting = circuit_lookup_mt_setting(circuit, mtid); + if (!setting) + { + setting = circuit_new_mt_setting(circuit, mtid); + circuit_add_mt_setting(circuit, setting); + } + return setting; +} + +int +circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty) +{ + int written = 0; + struct listnode *node; + struct isis_circuit_mt_setting *setting; + + for (ALL_LIST_ELEMENTS_RO (circuit->mt_settings, node, setting)) + { + const char *name = isis_mtid2str(setting->mtid); + if (name && !setting->enabled) + { + vty_out (vty, " no isis topology %s%s", name, VTY_NEWLINE); + written++; + } + } + return written; +} + +struct isis_circuit_mt_setting** +circuit_mt_settings(struct isis_circuit *circuit, unsigned int *mt_count) +{ + static unsigned int size = 0; + static struct isis_circuit_mt_setting **rv = NULL; + + struct isis_area_mt_setting **area_settings; + unsigned int area_count; + + unsigned int count = 0; + + struct listnode *node; + struct isis_circuit_mt_setting *setting; + + area_settings = area_mt_settings(circuit->area, &area_count); + + for (unsigned int i = 0; i < area_count; i++) + { + for (ALL_LIST_ELEMENTS_RO(circuit->mt_settings, node, setting)) + { + if (setting->mtid != area_settings[i]->mtid) + continue; + break; + } + if (!setting) + setting = circuit_get_mt_setting(circuit, area_settings[i]->mtid); + + if (!setting->enabled) + continue; + + count++; + if (count > size) + { + rv = XREALLOC(MTYPE_TMP, rv, count * sizeof(*rv)); + size = count; + } + rv[count-1] = setting; + } + + *mt_count = count; + return rv; +} + +/* ADJ specific MT API */ +static void adj_mt_set(struct isis_adjacency *adj, unsigned int index, + uint16_t mtid) +{ + if (adj->mt_count < index + 1) + { + adj->mt_set = XREALLOC(MTYPE_MT_ADJ_INFO, adj->mt_set, + (index + 1) * sizeof(*adj->mt_set)); + adj->mt_count = index + 1; + } + adj->mt_set[index] = mtid; +} + +bool +tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable, + struct isis_adjacency *adj) +{ + struct isis_circuit_mt_setting **mt_settings; + unsigned int circuit_mt_count; + + unsigned int intersect_count = 0; + + uint16_t *old_mt_set; + unsigned int old_mt_count; + + old_mt_count = adj->mt_count; + if (old_mt_count) + { + old_mt_set = XCALLOC(MTYPE_TMP, old_mt_count * sizeof(*old_mt_set)); + memcpy(old_mt_set, adj->mt_set, old_mt_count * sizeof(*old_mt_set)); + } + + mt_settings = circuit_mt_settings(adj->circuit, &circuit_mt_count); + for (unsigned int i = 0; i < circuit_mt_count; i++) + { + if (!tlvs->mt_router_info) + { + /* Other end does not have MT enabled */ + if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST) + adj_mt_set(adj, intersect_count++, ISIS_MT_IPV4_UNICAST); + } + else + { + struct listnode *node; + struct mt_router_info *info; + for (ALL_LIST_ELEMENTS_RO(tlvs->mt_router_info, node, info)) + { + if (mt_settings[i]->mtid == info->mtid) + adj_mt_set(adj, intersect_count++, info->mtid); + } + } + } + adj->mt_count = intersect_count; + + bool changed = false; + + if (adj->mt_count != old_mt_count) + changed = true; + + if (!changed && old_mt_count + && memcmp(adj->mt_set, old_mt_set, + old_mt_count * sizeof(*old_mt_set))) + changed = true; + + if (old_mt_count) + XFREE(MTYPE_TMP, old_mt_set); + + return changed; +} + +bool +adj_has_mt(struct isis_adjacency *adj, uint16_t mtid) +{ + for (unsigned int i = 0; i < adj->mt_count; i++) + if (adj->mt_set[i] == mtid) + return true; + return false; +} + +void +adj_mt_finish(struct isis_adjacency *adj) +{ + XFREE(MTYPE_MT_ADJ_INFO, adj->mt_set); + adj->mt_count = 0; +} + +/* TLV Router info api */ +struct mt_router_info* +tlvs_lookup_mt_router_info(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_router_info, mtid); +} + +/* TLV MT Neighbors api */ +struct tlv_mt_neighbors* +tlvs_lookup_mt_neighbors(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_is_neighs, mtid); +} + +static struct tlv_mt_neighbors* +tlvs_new_mt_neighbors(uint16_t mtid) +{ + struct tlv_mt_neighbors *rv; + + rv = XCALLOC(MTYPE_MT_NEIGHBORS, sizeof(*rv)); + rv->mtid = mtid; + rv->list = list_new(); + + return rv; +}; + +static void +tlvs_free_mt_neighbors(void *arg) +{ + struct tlv_mt_neighbors *neighbors = arg; + + if (neighbors && neighbors->list) + list_delete(neighbors->list); + XFREE(MTYPE_MT_NEIGHBORS, neighbors); +} + +static void +tlvs_add_mt_neighbors(struct tlvs *tlvs, struct tlv_mt_neighbors *neighbors) +{ + add_mt_setting(&tlvs->mt_is_neighs, neighbors); + tlvs->mt_is_neighs->del = tlvs_free_mt_neighbors; +} + +struct tlv_mt_neighbors* +tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid) +{ + struct tlv_mt_neighbors *neighbors; + + neighbors = tlvs_lookup_mt_neighbors(tlvs, mtid); + if (!neighbors) + { + neighbors = tlvs_new_mt_neighbors(mtid); + tlvs_add_mt_neighbors(tlvs, neighbors); + } + return neighbors; +} + +/* TLV MT IPv4 reach api */ +struct tlv_mt_ipv4_reachs* +tlvs_lookup_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_ipv4_reachs, mtid); +} + +static struct tlv_mt_ipv4_reachs* +tlvs_new_mt_ipv4_reachs(uint16_t mtid) +{ + struct tlv_mt_ipv4_reachs *rv; + + rv = XCALLOC(MTYPE_MT_IPV4_REACHS, sizeof(*rv)); + rv->mtid = mtid; + rv->list = list_new(); + + return rv; +}; + +static void +tlvs_free_mt_ipv4_reachs(void *arg) +{ + struct tlv_mt_ipv4_reachs *reachs = arg; + + if (reachs && reachs->list) + list_delete(reachs->list); + XFREE(MTYPE_MT_IPV4_REACHS, reachs); +} + +static void +tlvs_add_mt_ipv4_reachs(struct tlvs *tlvs, struct tlv_mt_ipv4_reachs *reachs) +{ + add_mt_setting(&tlvs->mt_ipv4_reachs, reachs); + tlvs->mt_ipv4_reachs->del = tlvs_free_mt_ipv4_reachs; +} + +struct tlv_mt_ipv4_reachs* +tlvs_get_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + struct tlv_mt_ipv4_reachs *reachs; + + reachs = tlvs_lookup_mt_ipv4_reachs(tlvs, mtid); + if (!reachs) + { + reachs = tlvs_new_mt_ipv4_reachs(mtid); + tlvs_add_mt_ipv4_reachs(tlvs, reachs); + } + return reachs; +} + +/* TLV MT IPv6 reach api */ +struct tlv_mt_ipv6_reachs* +tlvs_lookup_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + return lookup_mt_setting(tlvs->mt_ipv6_reachs, mtid); +} + +static struct tlv_mt_ipv6_reachs* +tlvs_new_mt_ipv6_reachs(uint16_t mtid) +{ + struct tlv_mt_ipv6_reachs *rv; + + rv = XCALLOC(MTYPE_MT_IPV6_REACHS, sizeof(*rv)); + rv->mtid = mtid; + rv->list = list_new(); + + return rv; +}; + +static void +tlvs_free_mt_ipv6_reachs(void *arg) +{ + struct tlv_mt_ipv6_reachs *reachs = arg; + + if (reachs && reachs->list) + list_delete(reachs->list); + XFREE(MTYPE_MT_IPV6_REACHS, reachs); +} + +static void +tlvs_add_mt_ipv6_reachs(struct tlvs *tlvs, struct tlv_mt_ipv6_reachs *reachs) +{ + add_mt_setting(&tlvs->mt_ipv6_reachs, reachs); + tlvs->mt_ipv6_reachs->del = tlvs_free_mt_ipv6_reachs; +} + +struct tlv_mt_ipv6_reachs* +tlvs_get_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid) +{ + struct tlv_mt_ipv6_reachs *reachs; + + reachs = tlvs_lookup_mt_ipv6_reachs(tlvs, mtid); + if (!reachs) + { + reachs = tlvs_new_mt_ipv6_reachs(mtid); + tlvs_add_mt_ipv6_reachs(tlvs, reachs); + } + return reachs; +} + +static void +mt_set_add(uint16_t **mt_set, unsigned int *size, + unsigned int *index, uint16_t mtid) +{ + for (unsigned int i = 0; i < *index; i++) + { + if ((*mt_set)[i] == mtid) + return; + } + + if (*index >= *size) + { + *mt_set = XREALLOC(MTYPE_TMP, *mt_set, sizeof(**mt_set) * ((*index) + 1)); + *size = (*index) + 1; + } + + (*mt_set)[*index] = mtid; + *index = (*index) + 1; +} + +static uint16_t * +circuit_bcast_mt_set(struct isis_circuit *circuit, int level, + unsigned int *mt_count) +{ + static uint16_t *rv; + static unsigned int size; + struct listnode *node; + struct isis_adjacency *adj; + + unsigned int count = 0; + + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + { + *mt_count = 0; + return NULL; + } + + for (ALL_LIST_ELEMENTS_RO(circuit->u.bc.adjdb[level - 1], node, adj)) + { + if (adj->adj_state != ISIS_ADJ_UP) + continue; + for (unsigned int i = 0; i < adj->mt_count; i++) + mt_set_add(&rv, &size, &count, adj->mt_set[i]); + } + + *mt_count = count; + return rv; +} + +static void +tlvs_add_mt_set(struct isis_area *area, + struct tlvs *tlvs, unsigned int mt_count, + uint16_t *mt_set, struct te_is_neigh *neigh) +{ + for (unsigned int i = 0; i < mt_count; i++) + { + uint16_t mtid = mt_set[i]; + struct te_is_neigh *ne_copy; + + ne_copy = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ne_copy)); + memcpy(ne_copy, neigh, sizeof(*ne_copy)); + + if (mt_set[i] == ISIS_MT_IPV4_UNICAST) + { + listnode_add(tlvs->te_is_neighs, ne_copy); + lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor", + area->area_tag, sysid_print(ne_copy->neigh_id), + LSP_PSEUDO_ID(ne_copy->neigh_id)); + } + else + { + struct tlv_mt_neighbors *neighbors; + + neighbors = tlvs_get_mt_neighbors(tlvs, mtid); + neighbors->list->del = free_tlv; + listnode_add(neighbors->list, ne_copy); + lsp_debug("ISIS (%s): Adding %s.%02x as mt-style neighbor for %s", + area->area_tag, sysid_print(ne_copy->neigh_id), + LSP_PSEUDO_ID(ne_copy->neigh_id), isis_mtid2str(mtid)); + } + } +} + +void +tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit, + int level, struct te_is_neigh *neigh) +{ + unsigned int mt_count; + uint16_t *mt_set = circuit_bcast_mt_set(circuit, level, + &mt_count); + + tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, neigh); +} + +void +tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit, + struct te_is_neigh *neigh) +{ + struct isis_adjacency *adj = circuit->u.p2p.neighbor; + + tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, neigh); +} diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h new file mode 100644 index 0000000000..d4dc4c6f2a --- /dev/null +++ b/isisd/isis_mt.h @@ -0,0 +1,146 @@ +/* + * IS-IS Rout(e)ing protocol - Multi Topology Support + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR 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, or (at your option) any + * later version. + * + * FRR 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef ISIS_MT_H +#define ISIS_MT_H + +#define ISIS_MT_MASK 0x0fff +#define ISIS_MT_OL_MASK 0x8000 + +#define ISIS_MT_IPV4_UNICAST 0 +#define ISIS_MT_IPV4_MGMT 1 +#define ISIS_MT_IPV6_UNICAST 2 +#define ISIS_MT_IPV4_MULTICAST 3 +#define ISIS_MT_IPV6_MULTICAST 4 +#define ISIS_MT_IPV6_MGMT 5 + +#define ISIS_MT_NAMES \ + "" + +#define ISIS_MT_DESCRIPTIONS \ + "IPv4 unicast topology\n" \ + "IPv4 management topology\n" \ + "IPv6 unicast topology\n" \ + "IPv4 multicast topology\n" \ + "IPv6 multicast topology\n" \ + "IPv6 management topology\n" + +#define ISIS_MT_INFO_FIELDS \ + uint16_t mtid; + +struct list; + +struct isis_area_mt_setting { + ISIS_MT_INFO_FIELDS + bool enabled; + bool overload; +}; + +struct isis_circuit_mt_setting { + ISIS_MT_INFO_FIELDS + bool enabled; +}; + +struct tlv_mt_neighbors { + ISIS_MT_INFO_FIELDS + struct list *list; +}; + +struct tlv_mt_ipv4_reachs { + ISIS_MT_INFO_FIELDS + struct list *list; +}; + +struct tlv_mt_ipv6_reachs { + ISIS_MT_INFO_FIELDS + struct list *list; +}; + +const char *isis_mtid2str(uint16_t mtid); +uint16_t isis_str2mtid(const char *name); + +struct isis_adjacency; +struct isis_area; +struct isis_circuit; +struct tlvs; +struct te_is_neigh; + +uint16_t isis_area_ipv6_topology(struct isis_area *area); + +struct mt_router_info* tlvs_lookup_mt_router_info(struct tlvs *tlvs, uint16_t mtid); + +struct tlv_mt_neighbors* tlvs_lookup_mt_neighbors(struct tlvs *tlvs, uint16_t mtid); +struct tlv_mt_neighbors* tlvs_get_mt_neighbors(struct tlvs *tlvs, uint16_t mtid); + +struct tlv_mt_ipv4_reachs* tlvs_lookup_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid); +struct tlv_mt_ipv4_reachs* tlvs_get_mt_ipv4_reachs(struct tlvs *tlvs, uint16_t mtid); + +struct tlv_mt_ipv6_reachs* tlvs_lookup_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid); +struct tlv_mt_ipv6_reachs* tlvs_get_mt_ipv6_reachs(struct tlvs *tlvs, uint16_t mtid); + +struct isis_area_mt_setting* area_lookup_mt_setting(struct isis_area *area, + uint16_t mtid); +struct isis_area_mt_setting* area_new_mt_setting(struct isis_area *area, + uint16_t mtid); +void area_add_mt_setting(struct isis_area *area, + struct isis_area_mt_setting *setting); + +void area_mt_init(struct isis_area *area); +void area_mt_finish(struct isis_area *area); +struct isis_area_mt_setting* area_get_mt_setting(struct isis_area *area, + uint16_t mtid); +int area_write_mt_settings(struct isis_area *area, struct vty *vty); +bool area_is_mt(struct isis_area *area); +struct isis_area_mt_setting** area_mt_settings(struct isis_area *area, + unsigned int *mt_count); + +struct isis_circuit_mt_setting* circuit_lookup_mt_setting( + struct isis_circuit *circuit, + uint16_t mtid); +struct isis_circuit_mt_setting* circuit_new_mt_setting( + struct isis_circuit *circuit, + uint16_t mtid); +void circuit_add_mt_setting(struct isis_circuit *circuit, + struct isis_circuit_mt_setting *setting); +void circuit_mt_init(struct isis_circuit *circuit); +void circuit_mt_finish(struct isis_circuit *circuit); +struct isis_circuit_mt_setting* circuit_get_mt_setting( + struct isis_circuit *circuit, + uint16_t mtid); +int circuit_write_mt_settings(struct isis_circuit *circuit, struct vty *vty); +struct isis_circuit_mt_setting** circuit_mt_settings(struct isis_circuit *circuit, + unsigned int *mt_count); +bool tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable, + struct isis_adjacency *adj); +bool adj_has_mt(struct isis_adjacency *adj, uint16_t mtid); +void adj_mt_finish(struct isis_adjacency *adj); +void tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit, + int level, struct te_is_neigh *neigh); +void tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit, + struct te_is_neigh *neigh); +#endif diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 5fbf6c194e..9e90acf2e0 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -53,6 +53,7 @@ #include "isisd/isis_csm.h" #include "isisd/isis_events.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" #define ISIS_MINIMUM_FIXED_HDR_LEN 15 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */ @@ -471,6 +472,7 @@ process_p2p_hello (struct isis_circuit *circuit) expected |= TLVFLAG_NLPID; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + expected |= TLVFLAG_MT_ROUTER_INFORMATION; auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, @@ -633,6 +635,8 @@ process_p2p_hello (struct isis_circuit *circuit) if (found & TLVFLAG_IPV6_ADDR) tlvs_to_adj_ipv6_addrs (&tlvs, adj); + bool mt_set_changed = tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj); + /* lets take care of the expiry */ THREAD_TIMER_OFF (adj->t_expire); THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj, @@ -869,6 +873,13 @@ process_p2p_hello (struct isis_circuit *circuit) /* down - area mismatch */ isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); } + + if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed) + { + lsp_regenerate_schedule(adj->circuit->area, + isis_adj_usage2levels(adj->adj_usage), 0); + } + /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ /* FIXME - Missing parts */ @@ -1021,6 +1032,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) expected |= TLVFLAG_NLPID; expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + expected |= TLVFLAG_MT_ROUTER_INFORMATION; auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, @@ -1223,6 +1235,8 @@ process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) adj->circuit_t = hdr.circuit_t; + bool mt_set_changed = tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj); + /* lets take care of the expiry */ THREAD_TIMER_OFF (adj->t_expire); THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj, @@ -1266,6 +1280,9 @@ process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) "no LAN Neighbours TLV found"); } + if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed) + lsp_regenerate_schedule(adj->circuit->area, level, 0); + out: if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -2337,6 +2354,37 @@ send_hello (struct isis_circuit *circuit, int level) if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream)) return ISIS_WARNING; + /* + * MT Supported TLV + * + * TLV gets included if no topology is enabled on the interface, + * if one topology other than #0 is enabled, or if multiple topologies + * are enabled. + */ + struct isis_circuit_mt_setting **mt_settings; + unsigned int mt_count; + + mt_settings = circuit_mt_settings(circuit, &mt_count); + if ((mt_count == 0 && area_is_mt(circuit->area)) + || (mt_count == 1 && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST) + || (mt_count > 1)) + { + struct list *mt_info = list_new(); + mt_info->del = free_tlv; + + for (unsigned int i = 0; i < mt_count; i++) + { + struct mt_router_info *info; + + info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); + info->mtid = mt_settings[i]->mtid; + /* overload info is not valid in IIH, so it's not included here */ + listnode_add(mt_info, info); + } + tlv_add_mt_router_info (mt_info, circuit->snd_stream); + list_free(mt_info); + } + /* IPv6 Interface Address TLV */ if (circuit->ipv6_router && circuit->ipv6_link && listcount (circuit->ipv6_link) > 0) diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index dd07a9c6f5..5c434b90d1 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -371,7 +371,9 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) stream_set_getp (circuit->snd_stream, 0); memset (&sa, 0, sizeof (struct sockaddr_ll)); sa.sll_family = AF_PACKET; - sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); + + size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN; + sa.sll_protocol = htons(isis_ethertype(frame_size)); sa.sll_ifindex = circuit->interface->ifindex; sa.sll_halen = ETH_ALEN; /* RFC5309 section 4.1 recommends ALL_ISS */ @@ -418,7 +420,6 @@ isis_send_pdu_p2p (struct isis_circuit *circuit, int level) stream_set_getp (circuit->snd_stream, 0); memset (&sa, 0, sizeof (struct sockaddr_ll)); sa.sll_family = AF_PACKET; - sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); sa.sll_ifindex = circuit->interface->ifindex; sa.sll_halen = ETH_ALEN; if (level == 1) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 554fa563ad..d85f08f50b 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -5,6 +5,7 @@ * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering + * Copyright (C) 2017 Christian Franke * * 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 @@ -50,6 +51,14 @@ #include "isis_spf.h" #include "isis_route.h" #include "isis_csm.h" +#include "isis_mt.h" + +DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); + +struct isis_spf_run { + struct isis_area *area; + int level; +}; /* 7.2.7 */ static void @@ -142,35 +151,24 @@ vtype2string (enum vertextype vtype) default: return "UNKNOWN"; } - return NULL; /* Not reached */ + return NULL; /* Not reached */ } static const char * vid2string (struct isis_vertex *vertex, char * buff, int size) { - switch (vertex->type) + if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) { - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: return print_sys_hostname (vertex->N.id); - break; - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - case VTYPE_ES: - return print_sys_hostname (vertex->N.id); - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: - prefix2str ((struct prefix *) &vertex->N.prefix, buff, size); - break; - default: - return "UNKNOWN"; } - return (char *) buff; + if (VTYPE_IP(vertex->type)) + { + prefix2str ((struct prefix *) &vertex->N.prefix, buff, size); + return buff; + } + + return "UNKNOWN"; } static struct isis_vertex * @@ -181,26 +179,17 @@ isis_vertex_new (void *id, enum vertextype vtype) vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex)); vertex->type = vtype; - switch (vtype) + + if (VTYPE_IS(vtype) || VTYPE_ES(vtype)) { - case VTYPE_ES: - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN); - break; - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1); - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: - memcpy (&vertex->N.prefix, (struct prefix *) id, - sizeof (struct prefix)); - break; - default: + } + else if (VTYPE_IP(vtype)) + { + memcpy (&vertex->N.prefix, (struct prefix *) id, sizeof (struct prefix)); + } + else + { zlog_err ("WTF!"); } @@ -280,7 +269,7 @@ isis_spftree_del (struct isis_spftree *spftree) return; } -void +static void isis_spftree_adj_del (struct isis_spftree *spftree, struct isis_adjacency *adj) { struct listnode *node; @@ -394,23 +383,24 @@ isis_root_system_lsp (struct isis_area *area, int level, u_char *sysid) * Add this IS to the root of SPT */ static struct isis_vertex * -isis_spf_add_root (struct isis_spftree *spftree, int level, u_char *sysid) +isis_spf_add_root (struct isis_spftree *spftree, u_char *sysid) { struct isis_vertex *vertex; struct isis_lsp *lsp; #ifdef EXTREME_DEBUG char buff[PREFIX2STR_BUFFER]; #endif /* EXTREME_DEBUG */ + u_char id[ISIS_SYS_ID_LEN + 1]; - lsp = isis_root_system_lsp (spftree->area, level, sysid); + memcpy(id, sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(id) = 0; + + lsp = isis_root_system_lsp (spftree->area, spftree->level, sysid); if (lsp == NULL) - zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level); - - if (!spftree->area->oldmetric) - vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS); - else - vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS); + zlog_warn ("ISIS-Spf: could not find own l%d LSP!", spftree->level); + vertex = isis_vertex_new (id, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS); listnode_add (spftree->paths, vertex); #ifdef EXTREME_DEBUG @@ -432,44 +422,51 @@ isis_find_vertex (struct list *list, void *id, enum vertextype vtype) for (ALL_LIST_ELEMENTS_RO (list, node, vertex)) { if (vertex->type != vtype) - continue; - switch (vtype) - { - case VTYPE_ES: - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0) - return vertex; - break; - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: - if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0) - return vertex; - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: - p1 = (struct prefix *) id; - p2 = (struct prefix *) &vertex->N.id; - if (p1->family == p2->family && p1->prefixlen == p2->prefixlen && - memcmp (&p1->u.prefix, &p2->u.prefix, - PSIZE (p1->prefixlen)) == 0) - return vertex; - break; - } + continue; + if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) + { + if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0) + return vertex; + } + if (VTYPE_IP(vertex->type)) + { + p1 = (struct prefix *) id; + p2 = (struct prefix *) &vertex->N.id; + if (p1->family == p2->family + && p1->prefixlen == p2->prefixlen + && !memcmp(&p1->u.prefix, &p2->u.prefix, PSIZE (p1->prefixlen))) + { + return vertex; + } + } } return NULL; } +/* + * Compares vertizes for sorting in the TENT list. Returns true + * if candidate should be considered before current, false otherwise. + */ +static bool +tent_cmp (struct isis_vertex *current, struct isis_vertex *candidate) +{ + if (current->d_N > candidate->d_N) + return true; + + if (current->d_N == candidate->d_N + && current->type > candidate->type) + return true; + + return false; +} + /* * Add a vertex to TENT sorted by cost and by vertextype on tie break situation */ static struct isis_vertex * isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, - void *id, uint32_t cost, int depth, int family, + void *id, uint32_t cost, int depth, struct isis_adjacency *adj, struct isis_vertex *parent) { struct isis_vertex *vertex, *v; @@ -515,17 +512,11 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, for (node = listhead (spftree->tents); node; node = listnextnode (node)) { v = listgetdata (node); - if (v->d_N > vertex->d_N) - { - listnode_add_before (spftree->tents, node, vertex); - break; - } - else if (v->d_N == vertex->d_N && v->type > vertex->type) - { - /* Tie break, add according to type */ + if (tent_cmp(v, vertex)) + { listnode_add_before (spftree->tents, node, vertex); - break; - } + break; + } } if (node == NULL) @@ -537,7 +528,7 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, static void isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, void *id, struct isis_adjacency *adj, uint32_t cost, - int family, struct isis_vertex *parent) + struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -576,13 +567,13 @@ isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, } } - isis_spf_add2tent (spftree, vtype, id, cost, 1, family, adj, parent); + isis_spf_add2tent (spftree, vtype, id, cost, 1, adj, parent); return; } static void process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, - uint32_t dist, uint16_t depth, int family, + uint32_t dist, uint16_t depth, struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -670,7 +661,7 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, (parent ? print_sys_hostname (parent->N.id) : "null")); #endif /* EXTREME_DEBUG */ - isis_spf_add2tent (spftree, vtype, id, dist, depth, family, NULL, parent); + isis_spf_add2tent (spftree, vtype, id, dist, depth, NULL, parent); return; } @@ -679,9 +670,10 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, */ static int isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, - uint32_t cost, uint16_t depth, int family, + uint32_t cost, uint16_t depth, u_char *root_sysid, struct isis_vertex *parent) { + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->lsp_header->lsp_id); struct listnode *node, *fragnode = NULL; uint32_t dist; struct is_neigh *is_neigh; @@ -692,8 +684,14 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, struct prefix prefix; struct ipv6_reachability *ip6reach; static const u_char null_sysid[ISIS_SYS_ID_LEN]; + struct mt_router_info *mt_router_info = NULL; - if (!speaks (lsp->tlv_data.nlpids, family)) + if (spftree->mtid != ISIS_MT_IPV4_UNICAST) + mt_router_info = tlvs_lookup_mt_router_info(&lsp->tlv_data, spftree->mtid); + + if (!pseudo_lsp + && (spftree->mtid == ISIS_MT_IPV4_UNICAST && !speaks(lsp->tlv_data.nlpids, spftree->family)) + && !mt_router_info) return ISIS_OK; lspfragloop: @@ -707,9 +705,13 @@ lspfragloop: zlog_debug ("ISIS-Spf: process_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id)); #endif /* EXTREME_DEBUG */ - if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits)) + /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ + if (pseudo_lsp + || (spftree->mtid == ISIS_MT_IPV4_UNICAST && !ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits)) + || (mt_router_info && !mt_router_info->overload)) + { - if (lsp->tlv_data.is_neighs) + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) { @@ -717,95 +719,126 @@ lspfragloop: /* Two way connectivity */ if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) continue; - if (!memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) + if (!pseudo_lsp && !memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) continue; dist = cost + is_neigh->metrics.metric_default; - vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS - : VTYPE_NONPSEUDO_IS; - process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, - depth + 1, family, parent); + process_N (spftree, LSP_PSEUDO_ID(is_neigh->neigh_id) ? VTYPE_PSEUDO_IS + : VTYPE_NONPSEUDO_IS, + (void *) is_neigh->neigh_id, dist, depth + 1, parent); } } - if (lsp->tlv_data.te_is_neighs) - { - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, - te_is_neigh)) + + struct list *te_is_neighs = NULL; + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) + { + te_is_neighs = lsp->tlv_data.te_is_neighs; + } + else + { + struct tlv_mt_neighbors *mt_neighbors; + mt_neighbors = tlvs_lookup_mt_neighbors(&lsp->tlv_data, spftree->mtid); + if (mt_neighbors) + te_is_neighs = mt_neighbors->list; + } + for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) { if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) continue; - if (!memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) + if (!pseudo_lsp && !memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) continue; dist = cost + GET_TE_METRIC(te_is_neigh); - vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS; - process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, - depth + 1, family, parent); + process_N (spftree, LSP_PSEUDO_ID(te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS + : VTYPE_NONPSEUDO_TE_IS, + (void *) te_is_neigh->neigh_id, dist, depth + 1, parent); } - } } - if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs) + if (!pseudo_lsp + && spftree->family == AF_INET + && spftree->mtid == ISIS_MT_IPV4_UNICAST) { + struct list *reachs[] = {lsp->tlv_data.ipv4_int_reachs, + lsp->tlv_data.ipv4_ext_reachs}; + prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, node, ipreach)) - { - dist = cost + ipreach->metrics.metric_default; - vtype = VTYPE_IPREACH_INTERNAL; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen (ipreach->mask); - apply_mask (&prefix); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); - } + for (unsigned int i = 0; i < array_size(reachs); i++) + { + vtype = (reachs[i] == lsp->tlv_data.ipv4_int_reachs) ? VTYPE_IPREACH_INTERNAL + : VTYPE_IPREACH_EXTERNAL; + for (ALL_LIST_ELEMENTS_RO (reachs[i], node, ipreach)) + { + dist = cost + ipreach->metrics.metric_default; + prefix.u.prefix4 = ipreach->prefix; + prefix.prefixlen = ip_masklen (ipreach->mask); + apply_mask (&prefix); + process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, + parent); + } + } } - if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs) + + if (!pseudo_lsp && spftree->family == AF_INET) { + struct list *ipv4reachs = NULL; + + if (spftree->mtid == ISIS_MT_IPV4_UNICAST) + { + ipv4reachs = lsp->tlv_data.te_ipv4_reachs; + } + else + { + struct tlv_mt_ipv4_reachs *mt_reachs; + mt_reachs = tlvs_lookup_mt_ipv4_reachs(&lsp->tlv_data, spftree->mtid); + if (mt_reachs) + ipv4reachs = mt_reachs->list; + } + prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, node, ipreach)) - { - dist = cost + ipreach->metrics.metric_default; - vtype = VTYPE_IPREACH_EXTERNAL; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen (ipreach->mask); - apply_mask (&prefix); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); - } - } - if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs) - { - prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, - node, te_ipv4_reach)) + for (ALL_LIST_ELEMENTS_RO (ipv4reachs, node, te_ipv4_reach)) { assert ((te_ipv4_reach->control & 0x3F) <= IPV4_MAX_BITLEN); dist = cost + ntohl (te_ipv4_reach->te_metric); - vtype = VTYPE_IPREACH_TE; prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start, te_ipv4_reach->control); prefix.prefixlen = (te_ipv4_reach->control & 0x3F); apply_mask (&prefix); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); + process_N (spftree, VTYPE_IPREACH_TE, (void *) &prefix, dist, depth + 1, + parent); } } - if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs) + + if (!pseudo_lsp + && spftree->family == AF_INET6) { + struct list *ipv6reachs = NULL; + + if (spftree->mtid == ISIS_MT_IPV4_UNICAST) + { + ipv6reachs = lsp->tlv_data.ipv6_reachs; + } + else + { + struct tlv_mt_ipv6_reachs *mt_reachs; + mt_reachs = tlvs_lookup_mt_ipv6_reachs(&lsp->tlv_data, spftree->mtid); + if (mt_reachs) + ipv6reachs = mt_reachs->list; + } + prefix.family = AF_INET6; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, node, ip6reach)) + for (ALL_LIST_ELEMENTS_RO (ipv6reachs, node, ip6reach)) { assert (ip6reach->prefix_len <= IPV6_MAX_BITLEN); dist = cost + ntohl(ip6reach->metric); vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? - VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; + VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; prefix.prefixlen = ip6reach->prefix_len; memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix, PSIZE (ip6reach->prefix_len)); apply_mask (&prefix); process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - family, parent); + parent); } } @@ -824,76 +857,8 @@ lspfragloop: } static int -isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, - struct isis_lsp *lsp, uint32_t cost, - uint16_t depth, int family, - u_char *root_sysid, - struct isis_vertex *parent) -{ - struct listnode *node, *fragnode = NULL; - struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; - enum vertextype vtype; - uint32_t dist; - -pseudofragloop: - - if (lsp->lsp_header->seq_num == 0) - { - zlog_warn ("isis_spf_process_pseudo_lsp(): lsp with 0 seq_num" - " - do not process"); - return ISIS_WARNING; - } - -#ifdef EXTREME_DEBUG - zlog_debug ("ISIS-Spf: process_pseudo_lsp %s", - print_sys_hostname(lsp->lsp_header->lsp_id)); -#endif /* EXTREME_DEBUG */ - - /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ - - if (lsp->tlv_data.is_neighs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) - { - /* Two way connectivity */ - if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) - continue; - dist = cost + is_neigh->metrics.metric_default; - vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS - : VTYPE_NONPSEUDO_IS; - process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, - depth + 1, family, parent); - } - if (lsp->tlv_data.te_is_neighs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh)) - { - /* Two way connectivity */ - if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) - continue; - dist = cost + GET_TE_METRIC(te_is_neigh); - vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS; - process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, - depth + 1, family, parent); - } - - if (fragnode == NULL) - fragnode = listhead (lsp->lspu.frags); - else - fragnode = listnextnode (fragnode); - - if (fragnode) - { - lsp = listgetdata (fragnode); - goto pseudofragloop; - } - - return ISIS_OK; -} - -static int -isis_spf_preload_tent (struct isis_spftree *spftree, int level, - int family, u_char *root_sysid, +isis_spf_preload_tent (struct isis_spftree *spftree, + u_char *root_sysid, struct isis_vertex *parent) { struct isis_circuit *circuit; @@ -908,21 +873,25 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, u_char lsp_id[ISIS_SYS_ID_LEN + 2]; static u_char null_lsp_id[ISIS_SYS_ID_LEN + 2]; struct prefix_ipv6 *ipv6; + struct isis_circuit_mt_setting *circuit_mt; for (ALL_LIST_ELEMENTS_RO (spftree->area->circuit_list, cnode, circuit)) { + circuit_mt = circuit_lookup_mt_setting(circuit, spftree->mtid); + if (circuit_mt && !circuit_mt->enabled) + continue; if (circuit->state != C_STATE_UP) continue; - if (!(circuit->is_type & level)) + if (!(circuit->is_type & spftree->level)) continue; - if (family == AF_INET && !circuit->ip_router) + if (spftree->family == AF_INET && !circuit->ip_router) continue; - if (family == AF_INET6 && !circuit->ipv6_router) + if (spftree->family == AF_INET6 && !circuit->ipv6_router) continue; /* * Add IP(v6) addresses of this circuit */ - if (family == AF_INET) + if (spftree->family == AF_INET) { prefix.family = AF_INET; for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) @@ -931,10 +900,10 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, prefix.prefixlen = ipv4->prefixlen; apply_mask (&prefix); isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix, - NULL, 0, family, parent); + NULL, 0, parent); } } - if (family == AF_INET6) + if (spftree->family == AF_INET6) { prefix.family = AF_INET6; for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) @@ -943,7 +912,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, prefix.u.prefix6 = ipv6->prefix; apply_mask (&prefix); isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL, - &prefix, NULL, 0, family, parent); + &prefix, NULL, 0, parent); } } if (circuit->circ_type == CIRCUIT_T_BROADCAST) @@ -952,45 +921,46 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, * Add the adjacencies */ adj_list = list_new (); - adjdb = circuit->u.bc.adjdb[level - 1]; + adjdb = circuit->u.bc.adjdb[spftree->level - 1]; isis_adj_build_up_list (adjdb, adj_list); if (listcount (adj_list) == 0) { list_delete (adj_list); if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf: no L%d adjacencies on circuit %s", - level, circuit->interface->name); + spftree->level, circuit->interface->name); continue; } for (ALL_LIST_ELEMENTS_RO (adj_list, anode, adj)) { - if (!speaks (&adj->nlpids, family)) - continue; + if (!adj_has_mt(adj, spftree->mtid)) + continue; + if (spftree->mtid == ISIS_MT_IPV4_UNICAST && !speaks (&adj->nlpids, spftree->family)) + continue; switch (adj->sys_type) { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, - circuit->te_metric[level - 1], - family, parent); + circuit->te_metric[spftree->level - 1], + parent); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: - isis_spf_add_local (spftree, - spftree->area->oldmetric ? - VTYPE_NONPSEUDO_IS : - VTYPE_NONPSEUDO_TE_IS, - adj->sysid, adj, - circuit->te_metric[level - 1], - family, parent); memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (lsp_id) = 0; LSP_FRAGMENT (lsp_id) = 0; - lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]); + isis_spf_add_local (spftree, + spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS, + lsp_id, adj, + circuit->te_metric[spftree->level - 1], + parent); + lsp = lsp_search (lsp_id, spftree->area->lspdb[spftree->level - 1]); if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) zlog_warn ("ISIS-Spf: No LSP %s found for IS adjacency " "L%d on %s (ID %u)", - rawlspid_print (lsp_id), level, + rawlspid_print (lsp_id), spftree->level, circuit->interface->name, circuit->circuit_id); break; case ISIS_SYSTYPE_UNKNOWN: @@ -1002,7 +972,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, /* * Add the pseudonode */ - if (level == 1) + if (spftree->level == 1) memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); @@ -1011,55 +981,59 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, { if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf: No L%d DR on %s (ID %d)", - level, circuit->interface->name, circuit->circuit_id); + spftree->level, circuit->interface->name, circuit->circuit_id); continue; } adj = isis_adj_lookup (lsp_id, adjdb); /* if no adj, we are the dis or error */ - if (!adj && !circuit->u.bc.is_dr[level - 1]) + if (!adj && !circuit->u.bc.is_dr[spftree->level - 1]) { zlog_warn ("ISIS-Spf: No adjacency found from root " "to L%d DR %s on %s (ID %d)", - level, rawlspid_print (lsp_id), + spftree->level, rawlspid_print (lsp_id), circuit->interface->name, circuit->circuit_id); continue; } - lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]); + lsp = lsp_search (lsp_id, spftree->area->lspdb[spftree->level - 1]); if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) { zlog_warn ("ISIS-Spf: No lsp (%p) found from root " "to L%d DR %s on %s (ID %d)", - (void *)lsp, level, rawlspid_print (lsp_id), + (void *)lsp, spftree->level, rawlspid_print (lsp_id), circuit->interface->name, circuit->circuit_id); continue; } - isis_spf_process_pseudo_lsp (spftree, lsp, - circuit->te_metric[level - 1], 0, - family, root_sysid, parent); + isis_spf_process_lsp (spftree, lsp, + circuit->te_metric[spftree->level - 1], 0, + root_sysid, parent); } else if (circuit->circ_type == CIRCUIT_T_P2P) { adj = circuit->u.p2p.neighbor; if (!adj) continue; + if (!adj_has_mt(adj, spftree->mtid)) + continue; switch (adj->sys_type) { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, - circuit->te_metric[level - 1], family, + circuit->te_metric[spftree->level - 1], parent); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: - if (speaks (&adj->nlpids, family)) + memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (lsp_id) = 0; + LSP_FRAGMENT (lsp_id) = 0; + if (spftree->mtid != ISIS_MT_IPV4_UNICAST || speaks (&adj->nlpids, spftree->family)) isis_spf_add_local (spftree, - spftree->area->oldmetric ? - VTYPE_NONPSEUDO_IS : - VTYPE_NONPSEUDO_TE_IS, - adj->sysid, - adj, circuit->te_metric[level - 1], - family, parent); + spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS, + lsp_id, + adj, circuit->te_metric[spftree->level - 1], + parent); break; case ISIS_SYSTYPE_UNKNOWN: default: @@ -1086,8 +1060,7 @@ isis_spf_preload_tent (struct isis_spftree *spftree, int level, * now we just put the child pointer(s) in place */ static void -add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, - int level) +add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex) { char buff[PREFIX2STR_BUFFER]; @@ -1102,11 +1075,11 @@ add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ - if (vertex->type > VTYPE_ES) + if (VTYPE_IP(vertex->type)) { if (listcount (vertex->Adj_N) > 0) isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N, - vertex->depth, vertex->Adj_N, spftree->area, level); + vertex->depth, vertex->Adj_N, spftree->area, spftree->level); else if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf: no adjacencies do not install route for " "%s depth %d dist %d", vid2string (vertex, buff, sizeof (buff)), @@ -1117,12 +1090,16 @@ add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, } static void -init_spt (struct isis_spftree *spftree) +init_spt (struct isis_spftree *spftree, int mtid, int level, int family) { spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del; list_delete_all_node (spftree->tents); list_delete_all_node (spftree->paths); spftree->tents->del = spftree->paths->del = NULL; + + spftree->mtid = mtid; + spftree->level = level; + spftree->family = family; return; } @@ -1139,6 +1116,7 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) struct route_table *table = NULL; struct timeval time_now; unsigned long long start_time, end_time; + uint16_t mtid; /* Get time that can't roll backwards. */ monotime(&time_now); @@ -1160,14 +1138,20 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) isis_route_invalidate_table (area, table); + /* We only support ipv4-unicast and ipv6-unicast as topologies for now */ + if (family == AF_INET6) + mtid = isis_area_ipv6_topology(area); + else + mtid = ISIS_MT_IPV4_UNICAST; + /* * C.2.5 Step 0 */ - init_spt (spftree); + init_spt (spftree, mtid, level, family); /* a) */ - root_vertex = isis_spf_add_root (spftree, level, sysid); + root_vertex = isis_spf_add_root (spftree, sysid); /* b) */ - retval = isis_spf_preload_tent (spftree, level, family, sysid, root_vertex); + retval = isis_spf_preload_tent (spftree, sysid, root_vertex); if (retval != ISIS_OK) { zlog_warn ("ISIS-Spf: failed to load TENT SPF-root:%s", print_sys_hostname(sysid)); @@ -1196,37 +1180,22 @@ isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) /* Remove from tent list and add to paths list */ list_delete_node (spftree->tents, node); - add_to_paths (spftree, vertex, level); - switch (vertex->type) + add_to_paths (spftree, vertex); + if (VTYPE_IS(vertex->type)) { - case VTYPE_PSEUDO_IS: - case VTYPE_NONPSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: - case VTYPE_NONPSEUDO_TE_IS: memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (lsp_id) = 0; lsp = lsp_search (lsp_id, area->lspdb[level - 1]); if (lsp && lsp->lsp_header->rem_lifetime != 0) { - if (LSP_PSEUDO_ID (lsp_id)) - { - isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N, - vertex->depth, family, sysid, - vertex); - } - else - { - isis_spf_process_lsp (spftree, lsp, vertex->d_N, - vertex->depth, family, sysid, vertex); - } + isis_spf_process_lsp (spftree, lsp, vertex->d_N, + vertex->depth, sysid, vertex); } else { zlog_warn ("ISIS-Spf: No LSP found for %s", rawlspid_print (lsp_id)); } - break; - default:; } } @@ -1243,17 +1212,17 @@ out: } static int -isis_run_spf_l1 (struct thread *thread) +isis_run_spf_cb (struct thread *thread) { - struct isis_area *area; + struct isis_spf_run *run = THREAD_ARG (thread); + struct isis_area *area = run->area; + int level = run->level; int retval = ISIS_OK; - area = THREAD_ARG (thread); - assert (area); + XFREE(MTYPE_ISIS_SPF_RUN, run); + area->spf_timer[level - 1] = NULL; - area->spf_timer[0] = NULL; - - if (!(area->is_type & IS_LEVEL_1)) + if (!(area->is_type & level)) { if (isis->debugs & DEBUG_SPF_EVENTS) zlog_warn ("ISIS-SPF (%s) area does not share level", @@ -1262,43 +1231,26 @@ isis_run_spf_l1 (struct thread *thread) } if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag); + zlog_debug ("ISIS-Spf (%s) L%d SPF needed, periodic SPF", + area->area_tag, level); if (area->ip_circuits) - retval = isis_run_spf (area, 1, AF_INET, isis->sysid); + retval = isis_run_spf (area, level, AF_INET, isis->sysid); if (area->ipv6_circuits) - retval = isis_run_spf (area, 1, AF_INET6, isis->sysid); + retval = isis_run_spf (area, level, AF_INET6, isis->sysid); return retval; } -static int -isis_run_spf_l2 (struct thread *thread) +static struct isis_spf_run* +isis_run_spf_arg(struct isis_area *area, int level) { - struct isis_area *area; - int retval = ISIS_OK; + struct isis_spf_run *run = XMALLOC(MTYPE_ISIS_SPF_RUN, sizeof(*run)); - area = THREAD_ARG (thread); - assert (area); + run->area = area; + run->level = level; - area->spf_timer[1] = NULL; - - if (!(area->is_type & IS_LEVEL_2)) - { - if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag); - return ISIS_WARNING; - } - - if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag); - - if (area->ip_circuits) - retval = isis_run_spf (area, 2, AF_INET, isis->sysid); - if (area->ipv6_circuits) - retval = isis_run_spf (area, 2, AF_INET6, isis->sysid); - - return retval; + return run; } int @@ -1323,16 +1275,9 @@ isis_spf_schedule (struct isis_area *area, int level) if (area->spf_timer[level - 1]) return ISIS_OK; - if (level == 1) - { - THREAD_TIMER_MSEC_ON(master, area->spf_timer[0], - isis_run_spf_l1, area, delay); - } - else - { - THREAD_TIMER_MSEC_ON(master, area->spf_timer[1], - isis_run_spf_l2, area, delay); - } + THREAD_TIMER_MSEC_ON(master, area->spf_timer[level-1], + isis_run_spf_cb, isis_run_spf_arg(area, level), + delay); return ISIS_OK; } @@ -1352,12 +1297,9 @@ isis_spf_schedule (struct isis_area *area, int level) return retval; } - if (level == 1) - THREAD_TIMER_ON (master, area->spf_timer[0], isis_run_spf_l1, area, - area->min_spf_interval[0] - diff); - else - THREAD_TIMER_ON (master, area->spf_timer[1], isis_run_spf_l2, area, - area->min_spf_interval[1] - diff); + THREAD_TIMER_ON (master, area->spf_timer[level-1], + isis_run_spf_cb, isis_run_spf_arg(area, level), + area->min_spf_interval[level-1] - diff); if (isis->debugs & DEBUG_SPF_EVENTS) zlog_debug ("ISIS-Spf (%s) L%d SPF scheduled %d sec from now", @@ -1427,14 +1369,23 @@ isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid) DEFUN (show_isis_topology, show_isis_topology_cmd, - "show isis topology", + "show isis topology []", SHOW_STR "IS-IS information\n" - "IS-IS paths to Intermediate Systems\n") + "IS-IS paths to Intermediate Systems\n" + "Paths to all level-1 routers in the area\n" + "Paths to all level-2 routers in the domain\n") { + int levels; struct listnode *node; struct isis_area *area; - int level; + + if (argc < 4) + levels = ISIS_LEVEL1|ISIS_LEVEL2; + else if (!strcmp(argv[3]->arg, "level-1")) + levels = ISIS_LEVEL1; + else + levels = ISIS_LEVEL2; if (!isis->area_list || isis->area_list->count == 0) return CMD_SUCCESS; @@ -1444,23 +1395,26 @@ DEFUN (show_isis_topology, vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", VTY_NEWLINE); - for (level = 0; level < ISIS_LEVELS; level++) + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { - if (area->ip_circuits > 0 && area->spftree[level] - && area->spftree[level]->paths->count > 0) + if ((level & levels) == 0) + continue; + + if (area->ip_circuits > 0 && area->spftree[level-1] + && area->spftree[level-1]->paths->count > 0) { vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s", - level + 1, VTY_NEWLINE); - isis_print_paths (vty, area->spftree[level]->paths, isis->sysid); + level, VTY_NEWLINE); + isis_print_paths (vty, area->spftree[level-1]->paths, isis->sysid); vty_out (vty, "%s", VTY_NEWLINE); } - if (area->ipv6_circuits > 0 && area->spftree6[level] - && area->spftree6[level]->paths->count > 0) + if (area->ipv6_circuits > 0 && area->spftree6[level-1] + && area->spftree6[level-1]->paths->count > 0) { vty_out (vty, "IS-IS paths to level-%d routers that speak IPv6%s", - level + 1, VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[level]->paths, isis->sysid); + level, VTY_NEWLINE); + isis_print_paths (vty, area->spftree6[level-1]->paths, isis->sysid); vty_out (vty, "%s", VTY_NEWLINE); } } @@ -1471,92 +1425,8 @@ DEFUN (show_isis_topology, return CMD_SUCCESS; } -DEFUN (show_isis_topology_l1, - show_isis_topology_l1_cmd, - "show isis topology level-1", - SHOW_STR - "IS-IS information\n" - "IS-IS paths to Intermediate Systems\n" - "Paths to all level-1 routers in the area\n") -{ - struct listnode *node; - struct isis_area *area; - - if (!isis->area_list || isis->area_list->count == 0) - return CMD_SUCCESS; - - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) - { - vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", - VTY_NEWLINE); - - if (area->ip_circuits > 0 && area->spftree[0] - && area->spftree[0]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree[0]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - if (area->ipv6_circuits > 0 && area->spftree6[0] - && area->spftree6[0]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[0]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - vty_out (vty, "%s", VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN (show_isis_topology_l2, - show_isis_topology_l2_cmd, - "show isis topology level-2", - SHOW_STR - "IS-IS information\n" - "IS-IS paths to Intermediate Systems\n" - "Paths to all level-2 routers in the domain\n") -{ - struct listnode *node; - struct isis_area *area; - - if (!isis->area_list || isis->area_list->count == 0) - return CMD_SUCCESS; - - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) - { - vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", - VTY_NEWLINE); - - if (area->ip_circuits > 0 && area->spftree[1] - && area->spftree[1]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree[1]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - if (area->ipv6_circuits > 0 && area->spftree6[1] - && area->spftree6[1]->paths->count > 0) - { - vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s", - VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[1]->paths, isis->sysid); - vty_out (vty, "%s", VTY_NEWLINE); - } - vty_out (vty, "%s", VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - void isis_spf_cmds_init () { install_element (VIEW_NODE, &show_isis_topology_cmd); - install_element (VIEW_NODE, &show_isis_topology_l1_cmd); - install_element (VIEW_NODE, &show_isis_topology_l2_cmd); } diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index fb534542d0..9f06dbb602 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -38,6 +38,10 @@ enum vertextype VTYPE_IP6REACH_EXTERNAL }; +#define VTYPE_IS(t) ((t) >= VTYPE_PSEUDO_IS && (t) <= VTYPE_NONPSEUDO_TE_IS) +#define VTYPE_ES(t) ((t) == VTYPE_ES) +#define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL) + /* * Triple */ @@ -66,12 +70,14 @@ struct isis_spftree unsigned int runcount; /* number of runs since uptime */ time_t last_run_timestamp; /* last run timestamp for scheduling */ time_t last_run_duration; /* last run duration in msec */ + + uint16_t mtid; + int family; + int level; }; struct isis_spftree * isis_spftree_new (struct isis_area *area); void isis_spftree_del (struct isis_spftree *spftree); -void isis_spftree_adj_del (struct isis_spftree *spftree, - struct isis_adjacency *adj); void spftree_area_init (struct isis_area *area); void spftree_area_del (struct isis_area *area); void spftree_area_adj_del (struct isis_area *area, diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index 4192fff9a8..b033e35a2e 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -43,6 +43,7 @@ #include "isisd/isis_pdu.h" #include "isisd/isis_lsp.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" void free_tlv (void *val) @@ -61,10 +62,14 @@ free_tlvs (struct tlvs *tlvs) { if (tlvs->area_addrs) list_delete (tlvs->area_addrs); + if (tlvs->mt_router_info) + list_delete (tlvs->mt_router_info); if (tlvs->is_neighs) list_delete (tlvs->is_neighs); if (tlvs->te_is_neighs) list_delete (tlvs->te_is_neighs); + if (tlvs->mt_is_neighs) + list_delete (tlvs->mt_is_neighs); if (tlvs->es_neighs) list_delete (tlvs->es_neighs); if (tlvs->lsp_entries) @@ -81,16 +86,293 @@ free_tlvs (struct tlvs *tlvs) list_delete (tlvs->ipv4_ext_reachs); if (tlvs->te_ipv4_reachs) list_delete (tlvs->te_ipv4_reachs); + if (tlvs->mt_ipv4_reachs) + list_delete (tlvs->mt_ipv4_reachs); if (tlvs->ipv6_addrs) list_delete (tlvs->ipv6_addrs); if (tlvs->ipv6_reachs) list_delete (tlvs->ipv6_reachs); + if (tlvs->mt_ipv6_reachs) + list_delete (tlvs->mt_ipv6_reachs); memset (tlvs, 0, sizeof (struct tlvs)); return; } +static int +parse_mtid(uint16_t *mtid, bool read_mtid, + unsigned int *length, u_char **pnt) +{ + if (!read_mtid) + { + *mtid = ISIS_MT_IPV4_UNICAST; + return ISIS_OK; + } + + uint16_t mtid_buf; + + if (*length < sizeof(mtid_buf)) + { + zlog_warn("ISIS-TLV: mt tlv too short to contain MT id"); + return ISIS_WARNING; + } + + memcpy(&mtid_buf, *pnt, sizeof(mtid_buf)); + *pnt += sizeof(mtid_buf); + *length -= sizeof(mtid_buf); + + *mtid = ntohs(mtid_buf) & ISIS_MT_MASK; + return ISIS_OK; +} + +static int +parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid, + unsigned int length, u_char *pnt) +{ + struct list *neigh_list; + uint16_t mtid; + int rv; + + rv = parse_mtid(&mtid, read_mtid, &length, &pnt); + if (rv != ISIS_OK) + return rv; + + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlvs->te_is_neighs) + { + tlvs->te_is_neighs = list_new(); + tlvs->te_is_neighs->del = free_tlv; + } + neigh_list = tlvs->te_is_neighs; + } + else + { + struct tlv_mt_neighbors *neighbors; + + neighbors = tlvs_get_mt_neighbors(tlvs, mtid); + neighbors->list->del = free_tlv; + neigh_list = neighbors->list; + } + + while (length >= IS_NEIGHBOURS_LEN) + { + struct te_is_neigh *neigh = XCALLOC(MTYPE_ISIS_TLV, sizeof(*neigh)); + + memcpy(neigh, pnt, IS_NEIGHBOURS_LEN); + pnt += IS_NEIGHBOURS_LEN; + length -= IS_NEIGHBOURS_LEN; + + if (neigh->sub_tlvs_length > length) + { + zlog_warn("ISIS-TLV: neighbor subtlv length exceeds TLV size"); + XFREE(MTYPE_ISIS_TLV, neigh); + return ISIS_WARNING; + } + + memcpy(neigh->sub_tlvs, pnt, neigh->sub_tlvs_length); + pnt += neigh->sub_tlvs_length; + length -= neigh->sub_tlvs_length; + + listnode_add(neigh_list, neigh); + } + + if (length) + { + zlog_warn("ISIS-TLV: TE/MT neighor TLV has trailing data"); + return ISIS_WARNING; + } + + return ISIS_OK; +} + +static int +parse_mt_ipv4_reachs(struct tlvs *tlvs, bool read_mtid, + unsigned int length, u_char *pnt) +{ + struct list *reach_list; + uint16_t mtid; + int rv; + + rv = parse_mtid(&mtid, read_mtid, &length, &pnt); + if (rv != ISIS_OK) + return rv; + + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlvs->te_ipv4_reachs) + { + tlvs->te_ipv4_reachs = list_new(); + tlvs->te_ipv4_reachs->del = free_tlv; + } + reach_list = tlvs->te_ipv4_reachs; + } + else + { + struct tlv_mt_ipv4_reachs *reachs; + + reachs = tlvs_get_mt_ipv4_reachs(tlvs, mtid); + reachs->list->del = free_tlv; + reach_list = reachs->list; + } + + while (length >= 5) /* Metric + Control */ + { + struct te_ipv4_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, TE_IPV4_REACH_LEN); + + memcpy(reach, pnt, 5); /* Metric + Control */ + pnt += 5; + length -= 5; + + unsigned char prefixlen = reach->control & 0x3F; + + if (prefixlen > IPV4_MAX_BITLEN) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix length %d", prefixlen); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + if (length < (unsigned int)PSIZE(prefixlen)) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix too long for tlv"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + memcpy(&reach->prefix_start, pnt, PSIZE(prefixlen)); + pnt += PSIZE(prefixlen); + length -= PSIZE(prefixlen); + + if (reach->control & TE_IPV4_HAS_SUBTLV) + { + if (length < 1) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLV missing"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + u_char subtlv_len = *pnt; + pnt++; + length--; + + if (length < subtlv_len) + { + zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLVs have oversize"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + /* Skip Sub-TLVs for now */ + pnt += subtlv_len; + length -= subtlv_len; + } + listnode_add(reach_list, reach); + } + + if (length) + { + zlog_warn("ISIS-TLV: TE/MT ipv4 reachability TLV has trailing data"); + return ISIS_WARNING; + } + + return ISIS_OK; +} + +static int +parse_mt_ipv6_reachs(struct tlvs *tlvs, bool read_mtid, + unsigned int length, u_char *pnt) +{ + struct list *reach_list; + uint16_t mtid; + int rv; + + rv = parse_mtid(&mtid, read_mtid, &length, &pnt); + if (rv != ISIS_OK) + return rv; + + if (mtid == ISIS_MT_IPV4_UNICAST) + { + if (!tlvs->ipv6_reachs) + { + tlvs->ipv6_reachs = list_new(); + tlvs->ipv6_reachs->del = free_tlv; + } + reach_list = tlvs->ipv6_reachs; + } + else + { + struct tlv_mt_ipv6_reachs *reachs; + + reachs = tlvs_get_mt_ipv6_reachs(tlvs, mtid); + reachs->list->del = free_tlv; + reach_list = reachs->list; + } + + while (length >= 6) /* Metric + Control + Prefixlen */ + { + struct ipv6_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*reach)); + + memcpy(reach, pnt, 6); /* Metric + Control + Prefixlen */ + pnt += 6; + length -= 6; + + if (reach->prefix_len > IPV6_MAX_BITLEN) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix length %d", reach->prefix_len); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + if (length < (unsigned int)PSIZE(reach->prefix_len)) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix too long for tlv"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + memcpy(&reach->prefix, pnt, PSIZE(reach->prefix_len)); + pnt += PSIZE(reach->prefix_len); + length -= PSIZE(reach->prefix_len); + + if (reach->control_info & CTRL_INFO_SUBTLVS) + { + if (length < 1) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLV missing"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + u_char subtlv_len = *pnt; + pnt++; + length--; + + if (length < subtlv_len) + { + zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLVs have oversize"); + XFREE(MTYPE_ISIS_TLV, reach); + return ISIS_WARNING; + } + + /* Skip Sub-TLVs for now */ + pnt += subtlv_len; + length -= subtlv_len; + } + listnode_add(reach_list, reach); + } + + if (length) + { + zlog_warn("ISIS-TLV: (MT) IPv6 reachability TLV has trailing data"); + return ISIS_WARNING; + } + + return ISIS_OK; +} + /* * Parses the tlvs found in the variant length part of the PDU. * Caller tells with flags in "expected" which TLV's it is interested in. @@ -103,17 +385,13 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, struct lan_neigh *lan_nei; struct area_addr *area_addr; struct is_neigh *is_nei; - struct te_is_neigh *te_is_nei; struct es_neigh *es_nei; struct lsp_entry *lsp_entry; struct in_addr *ipv4_addr; struct ipv4_reachability *ipv4_reach; - struct te_ipv4_reachability *te_ipv4_reach; struct in6_addr *ipv6_addr; - struct ipv6_reachability *ipv6_reach; - int prefix_octets; int value_len, retval = ISIS_OK; - u_char *start = stream, *pnt = stream, *endpnt; + u_char *start = stream, *pnt = stream; *found = 0; memset (tlvs, 0, sizeof (struct tlvs)); @@ -207,54 +485,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, break; case TE_IS_NEIGHBOURS: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Neighbour ID | 7 - * +---------------------------------------------------------------+ - * | TE Metric | 3 - * +---------------------------------------------------------------+ - * | SubTLVs Length | 1 - * +---------------------------------------------------------------+ - * : : - */ *found |= TLVFLAG_TE_IS_NEIGHS; #ifdef EXTREME_TLV_DEBUG zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d", areatag, length); #endif /* EXTREME_TLV_DEBUG */ if (TLVFLAG_TE_IS_NEIGHS & *expected) - { - while (length > value_len) - { - te_is_nei = (struct te_is_neigh *) pnt; - value_len += IS_NEIGHBOURS_LEN; - pnt += IS_NEIGHBOURS_LEN; - /* FIXME - subtlvs are handled here, for now we skip */ - /* FIXME: All TE SubTLVs are not necessary present in LSP PDU. */ - /* So, it must be copied in a new te_is_neigh structure */ - /* rather than just initialize pointer to the original LSP PDU */ - /* to avoid consider the rest of lspdu as subTLVs or buffer overflow */ - if (IS_MPLS_TE(isisMplsTE)) - { - struct te_is_neigh *new = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh)); - memcpy(new->neigh_id, te_is_nei->neigh_id, ISIS_SYS_ID_LEN + 1); - memcpy(new->te_metric, te_is_nei->te_metric, 3); - new->sub_tlvs_length = te_is_nei->sub_tlvs_length; - memcpy(new->sub_tlvs, pnt, te_is_nei->sub_tlvs_length); - te_is_nei = new; - } - /* Skip SUB TLVs payload */ - value_len += te_is_nei->sub_tlvs_length; - pnt += te_is_nei->sub_tlvs_length; + retval = parse_mt_is_neighs(tlvs, false, length, pnt); + pnt += length; + break; - if (!tlvs->te_is_neighs) - tlvs->te_is_neighs = list_new (); - listnode_add (tlvs->te_is_neighs, te_is_nei); - } - } - else - { - pnt += length; - } + case MT_IS_NEIGHBOURS: + *found |= TLVFLAG_TE_IS_NEIGHS; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): MT IS Neighbours length %d", + areatag, length); +#endif + if (TLVFLAG_TE_IS_NEIGHS & *expected) + retval = parse_mt_is_neighs(tlvs, true, length, pnt); + pnt += length; break; case ES_NEIGHBOURS: @@ -577,71 +826,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, break; case TE_IPV4_REACHABILITY: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | TE Metric | 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | U/D | sTLV? | Prefix Mask Len | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Prefix | 0-4 - * +---------------------------------------------------------------+ - * | sub tlvs | - * +---------------------------------------------------------------+ - * : : - */ *found |= TLVFLAG_TE_IPV4_REACHABILITY; #ifdef EXTREME_TLV_DEBUG zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d", - areatag, length); + areatag, length); #endif /* EXTREME_TLV_DEBUG */ - endpnt = pnt + length; if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) - { - while (length > value_len) - { - te_ipv4_reach = (struct te_ipv4_reachability *) pnt; - if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN) - { - zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach" - "ability prefix length %d", areatag, - te_ipv4_reach->control & 0x3F); - retval = ISIS_WARNING; - break; - } - if (!tlvs->te_ipv4_reachs) - tlvs->te_ipv4_reachs = list_new (); - listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach); - - /* Metric + Control-Byte + Prefix */ - unsigned int entry_len = 5 + PSIZE(te_ipv4_reach->control & 0x3F); - value_len += entry_len; - pnt += entry_len; - - if (te_ipv4_reach->control & TE_IPV4_HAS_SUBTLV) - { - if (length <= value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLV missing", - areatag); - retval = ISIS_WARNING; - break; - } - u_char subtlv_len = *pnt; - value_len += subtlv_len + 1; - pnt += subtlv_len + 1; - if (length < value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLVs have oversize", - areatag); - retval = ISIS_WARNING; - break; - } - } - } - } - - pnt = endpnt; + retval = parse_mt_ipv4_reachs(tlvs, false, length, pnt); + pnt += length; + break; + case MT_IPV4_REACHABILITY: + *found |= TLVFLAG_TE_IPV4_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv4 MT Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) + retval = parse_mt_ipv4_reachs(tlvs, true, length, pnt); + pnt += length; break; - case IPV6_ADDR: /* +-------+-------+-------+-------+-------+-------+-------+-------+ * + IP version 6 address + 16 @@ -672,67 +875,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, break; case IPV6_REACHABILITY: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Default Metric | 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Control Informantion | - * +---------------------------------------------------------------+ - * | IPv6 Prefix Length |--+ - * +---------------------------------------------------------------+ | - * | IPv6 Prefix |<-+ - * +---------------------------------------------------------------+ - */ *found |= TLVFLAG_IPV6_REACHABILITY; - endpnt = pnt + length; - +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ if (*expected & TLVFLAG_IPV6_REACHABILITY) - { - while (length > value_len) - { - ipv6_reach = (struct ipv6_reachability *) pnt; - if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN) - { - zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach" - "ability prefix length %d", areatag, - ipv6_reach->prefix_len); - retval = ISIS_WARNING; - break; - } - - prefix_octets = ((ipv6_reach->prefix_len + 7) / 8); - value_len += prefix_octets + 6; - pnt += prefix_octets + 6; - - if (ipv6_reach->control_info & CTRL_INFO_SUBTLVS) - { - if (length <= value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLV missing", - areatag); - retval = ISIS_WARNING; - break; - } - u_char subtlv_len = *pnt; - value_len += subtlv_len + 1; - pnt += subtlv_len + 1; - if (length < value_len) - { - zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLVs have oversize", - areatag); - retval = ISIS_WARNING; - break; - } - } - /* FIXME: sub-tlvs */ - if (!tlvs->ipv6_reachs) - tlvs->ipv6_reachs = list_new (); - listnode_add (tlvs->ipv6_reachs, ipv6_reach); - } - } - - pnt = endpnt; + retval = parse_mt_ipv6_reachs(tlvs, false, length, pnt); + pnt += length; + break; + case MT_IPV6_REACHABILITY: + *found |= TLVFLAG_IPV6_REACHABILITY; +#ifdef EXTREME_TLV_DEBUG + zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d", + areatag, length); +#endif /* EXTREME_TLV_DEBUG */ + if (*expected & TLVFLAG_IPV6_REACHABILITY) + retval = parse_mt_ipv6_reachs(tlvs, true, length, pnt); + pnt += length; break; - case WAY3_HELLO: /* +---------------------------------------------------------------+ * | Adjacency state | 1 @@ -786,6 +947,42 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, pnt += length; break; + case MT_ROUTER_INFORMATION: + *found |= TLVFLAG_MT_ROUTER_INFORMATION; + if (*expected & TLVFLAG_MT_ROUTER_INFORMATION) + { + if (!tlvs->mt_router_info) + { + tlvs->mt_router_info = list_new(); + tlvs->mt_router_info->del = free_tlv; + } + while (length > value_len) + { + uint16_t mt_info; + struct mt_router_info *info; + + if (value_len + sizeof(mt_info) > length) { + zlog_warn("ISIS-TLV (%s): TLV 229 is truncated.", areatag); + pnt += length - value_len; + break; + } + + memcpy(&mt_info, pnt, sizeof(mt_info)); + pnt += sizeof(mt_info); + value_len += sizeof(mt_info); + + mt_info = ntohs(mt_info); + info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); + info->mtid = mt_info & ISIS_MT_MASK; + info->overload = mt_info & ISIS_MT_OL_MASK; + listnode_add(tlvs->mt_router_info, info); + } + } + else + { + pnt += length; + } + break; default: zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d", areatag, type, length); @@ -825,6 +1022,31 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream) return ISIS_OK; } +int +tlv_add_mt_router_info (struct list *mt_router_info, struct stream *stream) +{ + struct listnode *node; + struct mt_router_info *info; + + uint16_t value[127]; + uint16_t *pos = value; + + for (ALL_LIST_ELEMENTS_RO(mt_router_info, node, info)) + { + uint16_t mt_info; + + mt_info = info->mtid; + if (info->overload) + mt_info |= ISIS_MT_OL_MASK; + + *pos = htons(mt_info); + pos++; + } + + return add_tlv(MT_ROUTER_INFORMATION, (pos - value) * sizeof(*pos), + (u_char*)value, stream); +} + int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream) { @@ -887,26 +1109,44 @@ tlv_add_is_neighs (struct list *is_neighs, struct stream *stream) return add_tlv (IS_NEIGHBOURS, pos - value, value, stream); } -int -tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) +static size_t +max_tlv_size(struct stream *stream) +{ + size_t avail = stream_get_size (stream) - stream_get_endp(stream); + + if (avail < 2) + return 0; + + if (avail < 257) + return avail - 2; + + return 255; +} + +unsigned int +tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg) { struct listnode *node; struct te_is_neigh *te_is_neigh; u_char value[255]; u_char *pos = value; - int retval; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) { /* FIXME: Check if Total SubTLVs size doesn't exceed 255 */ - if (pos - value + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > 255) - { - retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - + if ((size_t)(pos - value) + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > max_size) + break; + memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); pos += ISIS_SYS_ID_LEN + 1; memcpy (pos, te_is_neigh->te_metric, 3); @@ -920,9 +1160,17 @@ tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) memcpy (pos, te_is_neigh->sub_tlvs, te_is_neigh->sub_tlvs_length); pos += te_is_neigh->sub_tlvs_length; } + consumed++; } - return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IS_NEIGHBOURS + : TE_IS_NEIGHBOURS, + pos - value, value, stream); + assert(rv == ISIS_OK); + } + return consumed; } int @@ -1100,37 +1348,49 @@ 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) +unsigned int +tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg) { struct listnode *node; struct te_ipv4_reachability *te_reach; u_char value[255]; u_char *pos = value; - u_char prefix_size; - int retval; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach)) { - prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1); + unsigned char prefixlen = te_reach->control & 0x3F; + + if ((size_t)(pos - value) + 5 + PSIZE(prefixlen) > max_size) + break; - if (pos - value + (5 + prefix_size) > 255) - { - retval = - add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } *(u_int32_t *) pos = te_reach->te_metric; pos += 4; *pos = te_reach->control; pos++; - memcpy (pos, &te_reach->prefix_start, prefix_size); - pos += prefix_size; + memcpy (pos, &te_reach->prefix_start, PSIZE(prefixlen)); + pos += PSIZE(prefixlen); + consumed++; } - return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV4_REACHABILITY + : TE_IPV4_REACHABILITY, + pos - value, value, stream); + assert(rv == ISIS_OK); + } + + return consumed; } int @@ -1158,36 +1418,49 @@ tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream) return add_tlv (IPV6_ADDR, pos - value, value, stream); } -int -tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream) +unsigned int +tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg) { struct listnode *node; struct ipv6_reachability *ip6reach; u_char value[255]; u_char *pos = value; - int retval, prefix_octets; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach)) { - if (pos - value + IPV6_MAX_BYTELEN + 6 > 255) - { - retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - *(uint32_t *) pos = ip6reach->metric; + if ((size_t)(pos - value) + 6 + PSIZE(ip6reach->prefix_len) > max_size) + break; + + *(uint32_t *)pos = ip6reach->metric; pos += 4; *pos = ip6reach->control_info; pos++; - prefix_octets = ((ip6reach->prefix_len + 7) / 8); *pos = ip6reach->prefix_len; pos++; - memcpy (pos, ip6reach->prefix, prefix_octets); - pos += prefix_octets; + memcpy (pos, ip6reach->prefix, PSIZE(ip6reach->prefix_len)); + pos += PSIZE(ip6reach->prefix_len); + consumed++; } - return add_tlv (IPV6_REACHABILITY, pos - value, value, stream); + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV6_REACHABILITY + : IPV6_REACHABILITY, + pos - value, value, stream); + assert(rv == ISIS_OK); + } + + return consumed; } int diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h index f899b9e9db..2135f5071f 100644 --- a/isisd/isis_tlv.h +++ b/isisd/isis_tlv.h @@ -24,6 +24,8 @@ #ifndef _ZEBRA_ISIS_TLV_H #define _ZEBRA_ISIS_TLV_H +#include "isisd/isis_mt.h" + /* * The list of TLVs we (should) support. * ____________________________________________________________________________ @@ -109,8 +111,12 @@ #define TE_IPV4_REACHABILITY 135 #define DYNAMIC_HOSTNAME 137 #define GRACEFUL_RESTART 211 +#define MT_IS_NEIGHBOURS 222 +#define MT_ROUTER_INFORMATION 229 #define IPV6_ADDR 232 +#define MT_IPV4_REACHABILITY 235 #define IPV6_REACHABILITY 236 +#define MT_IPV6_REACHABILITY 237 #define WAY3_HELLO 240 #define ROUTER_INFORMATION 242 @@ -250,6 +256,12 @@ struct ipv6_reachability #define CTRL_INFO_SUBTLVS 0x20 +struct mt_router_info +{ + ISIS_MT_INFO_FIELDS + bool overload; +}; + /* * Pointer to each tlv type, filled by parse_tlvs() */ @@ -260,8 +272,10 @@ struct tlvs struct nlpids *nlpids; struct te_router_id *router_id; struct list *area_addrs; + struct list *mt_router_info; struct list *is_neighs; struct list *te_is_neighs; + struct list *mt_is_neighs; struct list *es_neighs; struct list *lsp_entries; struct list *prefix_neighs; @@ -270,8 +284,10 @@ struct tlvs struct list *ipv4_int_reachs; struct list *ipv4_ext_reachs; struct list *te_ipv4_reachs; + struct list *mt_ipv4_reachs; struct list *ipv6_addrs; struct list *ipv6_reachs; + struct list *mt_ipv6_reachs; struct isis_passwd auth_info; }; @@ -301,6 +317,7 @@ struct tlvs #define TLVFLAG_TE_ROUTER_ID (1<<19) #define TLVFLAG_CHECKSUM (1<<20) #define TLVFLAG_GRACEFUL_RESTART (1<<21) +#define TLVFLAG_MT_ROUTER_INFORMATION (1<<22) void init_tlvs (struct tlvs *tlvs, uint32_t expected); void free_tlvs (struct tlvs *tlvs); @@ -310,9 +327,10 @@ int parse_tlvs (char *areatag, u_char * stream, int size, int add_tlv (u_char, u_char, u_char *, struct stream *); void free_tlv (void *val); +int tlv_add_mt_router_info (struct list *mt_router_info, struct stream *stream); int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream); int tlv_add_is_neighs (struct list *is_neighs, struct stream *stream); -int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream); +unsigned int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg); int tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream); int tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream); int tlv_add_checksum (struct checksum *checksum, struct stream *stream); @@ -325,9 +343,9 @@ int tlv_add_dynamic_hostname (struct hostname *hostname, int tlv_add_lsp_entries (struct list *lsps, 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); +unsigned int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg); int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream); -int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream); +unsigned int tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg); int tlv_add_padding (struct stream *stream); diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c index 721959859a..1658ca3733 100644 --- a/isisd/isis_vty.c +++ b/isisd/isis_vty.c @@ -29,6 +29,7 @@ #include "isis_circuit.h" #include "isis_csm.h" #include "isis_misc.h" +#include "isis_mt.h" #include "isisd.h" static struct isis_circuit * @@ -1271,6 +1272,48 @@ DEFUN (no_psnp_interval_l2, return CMD_SUCCESS; } +DEFUN (circuit_topology, + circuit_topology_cmd, + "isis topology " ISIS_MT_NAMES, + "IS-IS commands\n" + "Configure interface IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS) +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + const char *arg = argv[2]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return isis_circuit_mt_enabled_set(circuit, mtid, true); +} + +DEFUN (no_circuit_topology, + no_circuit_topology_cmd, + "no isis topology " ISIS_MT_NAMES, + NO_STR + "IS-IS commands\n" + "Configure interface IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS) +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + const char *arg = argv[3]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return isis_circuit_mt_enabled_set(circuit, mtid, false); +} static int validate_metric_style_narrow (struct vty *vty, struct isis_area *area) @@ -2116,6 +2159,9 @@ isis_vty_init (void) install_element (INTERFACE_NODE, &psnp_interval_l2_cmd); install_element (INTERFACE_NODE, &no_psnp_interval_l2_cmd); + install_element (INTERFACE_NODE, &circuit_topology_cmd); + install_element (INTERFACE_NODE, &no_circuit_topology_cmd); + install_element (ISIS_NODE, &metric_style_cmd); install_element (ISIS_NODE, &no_metric_style_cmd); diff --git a/isisd/isisd.c b/isisd/isisd.c index f226c4a1f3..179e430986 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -56,6 +56,7 @@ #include "isisd/isis_zebra.h" #include "isisd/isis_events.h" #include "isisd/isis_te.h" +#include "isisd/isis_mt.h" struct isis *isis = NULL; @@ -156,6 +157,8 @@ isis_area_create (const char *area_tag) area->lsp_frag_threshold = 90; area->lsp_mtu = DEFAULT_LSP_MTU; + area_mt_init(area); + area->area_tag = strdup (area_tag); listnode_add (isis->area_list, area); area->isis = isis; @@ -296,6 +299,8 @@ isis_area_destroy (struct vty *vty, const char *area_tag) free (area->area_tag); + area_mt_finish(area); + XFREE (MTYPE_ISIS_AREA, area); if (listcount (isis->area_list) == 0) @@ -307,6 +312,33 @@ isis_area_destroy (struct vty *vty, const char *area_tag) return CMD_SUCCESS; } +static void +area_set_mt_enabled(struct isis_area *area, uint16_t mtid, bool enabled) +{ + struct isis_area_mt_setting *setting; + + setting = area_get_mt_setting(area, mtid); + if (setting->enabled != enabled) + { + setting->enabled = enabled; + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } +} + +static void +area_set_mt_overload(struct isis_area *area, uint16_t mtid, bool overload) +{ + struct isis_area_mt_setting *setting; + + setting = area_get_mt_setting(area, mtid); + if (setting->overload != overload) + { + setting->overload = overload; + if (setting->enabled) + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0); + } +} + int area_net_title (struct vty *vty, const char *net_title) { @@ -1626,6 +1658,61 @@ DEFUN (no_net, return area_clear_net_title (vty, argv[idx_word]->arg); } +DEFUN (isis_topology, + isis_topology_cmd, + "topology " ISIS_MT_NAMES " [overload]", + "Configure IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS + "Set overload bit for topology\n") +{ + VTY_DECLVAR_CONTEXT (isis_area, area); + + const char *arg = argv[1]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + if (mtid == ISIS_MT_IPV4_UNICAST) + { + vty_out (vty, "Cannot configure IPv4 unicast topology%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + area_set_mt_enabled(area, mtid, true); + area_set_mt_overload(area, mtid, (argc == 3)); + return CMD_SUCCESS; +} + +DEFUN (no_isis_topology, + no_isis_topology_cmd, + "no topology " ISIS_MT_NAMES " [overload]", + NO_STR + "Configure IS-IS topologies\n" + ISIS_MT_DESCRIPTIONS + "Set overload bit for topology\n") +{ + VTY_DECLVAR_CONTEXT (isis_area, area); + + const char *arg = argv[2]->arg; + uint16_t mtid = isis_str2mtid(arg); + if (mtid == (uint16_t)-1) + { + vty_out (vty, "Don't know topology '%s'%s", arg, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + if (mtid == ISIS_MT_IPV4_UNICAST) + { + vty_out (vty, "Cannot configure IPv4 unicast topology%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + area_set_mt_enabled(area, mtid, false); + area_set_mt_overload(area, mtid, false); + return CMD_SUCCESS; +} + void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu) { area->lsp_mtu = lsp_mtu; @@ -2148,6 +2235,7 @@ isis_config_write (struct vty *vty) write++; } + write += area_write_mt_settings(area, vty); } isis_mpls_te_config_write_router(vty); } @@ -2254,6 +2342,9 @@ isis_init () install_element (ISIS_NODE, &net_cmd); install_element (ISIS_NODE, &no_net_cmd); + install_element (ISIS_NODE, &isis_topology_cmd); + install_element (ISIS_NODE, &no_isis_topology_cmd); + install_element (ISIS_NODE, &log_adj_changes_cmd); install_element (ISIS_NODE, &no_log_adj_changes_cmd); diff --git a/isisd/isisd.h b/isisd/isisd.h index e1d3a69f8d..a8cf3673fc 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -120,6 +120,8 @@ struct isis_area int ip_circuits; /* logging adjacency changes? */ u_char log_adj_changes; + /* multi topology settings */ + struct list *mt_settings; int ipv6_circuits; /* Counters */ u_int32_t circuit_state_changes;