summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--isisd/Makefile.am4
-rw-r--r--isisd/isis_adjacency.c20
-rw-r--r--isisd/isis_adjacency.h3
-rw-r--r--isisd/isis_bpf.c33
-rw-r--r--isisd/isis_circuit.c22
-rw-r--r--isisd/isis_circuit.h3
-rw-r--r--isisd/isis_constants.h10
-rw-r--r--isisd/isis_lsp.c424
-rw-r--r--isisd/isis_lsp.h2
-rw-r--r--isisd/isis_mt.c724
-rw-r--r--isisd/isis_mt.h146
-rw-r--r--isisd/isis_pdu.c48
-rw-r--r--isisd/isis_pfpacket.c5
-rw-r--r--isisd/isis_spf.c718
-rw-r--r--isisd/isis_spf.h10
-rw-r--r--isisd/isis_tlv.c687
-rw-r--r--isisd/isis_tlv.h24
-rw-r--r--isisd/isis_vty.c46
-rw-r--r--isisd/isisd.c91
-rw-r--r--isisd/isisd.h2
20 files changed, 2215 insertions, 807 deletions
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");
@@ -284,23 +279,6 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
}
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)
{
struct ether_header *eth;
@@ -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);
}
@@ -1383,6 +1389,22 @@ isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
}
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)
{
return 0;
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 <zebra.h>
+#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 \
+ "<ipv4-unicast" \
+ "|ipv4-mgmt" \
+ "|ipv6-unicast" \
+ "|ipv4-multicast" \
+ "|ipv6-multicast" \
+ "|ipv6-mgmt" \
+ ">"
+
+#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 <chris@opensourcerouting.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public Licenseas published by the Free
@@ -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:
+ }
+
+ if (VTYPE_IP(vertex->type))
+ {
prefix2str ((struct prefix *) &vertex->N.prefix, buff, size);
- break;
- default:
- return "UNKNOWN";
+ return buff;
}
- return (char *) 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);
- if (lsp == NULL)
- zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level);
+ memcpy(id, sysid, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(id) = 0;
- if (!spftree->area->oldmetric)
- vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS);
- else
- vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS);
+ lsp = isis_root_system_lsp (spftree->area, spftree->level, sysid);
+ if (lsp == NULL)
+ 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 (spftree->mtid != ISIS_MT_IPV4_UNICAST)
+ mt_router_info = tlvs_lookup_mt_router_info(&lsp->tlv_data, spftree->mtid);
- if (!speaks (lsp->tlv_data.nlpids, family))
+ 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)
- {
- 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);
- }
- }
- if (family == AF_INET && lsp->tlv_data.ipv4_ext_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_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);
- }
+ 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.te_ipv4_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.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;
-
- area = THREAD_ARG (thread);
- assert (area);
+ struct isis_spf_run *run = XMALLOC(MTYPE_ISIS_SPF_RUN, sizeof(*run));
- area->spf_timer[1] = NULL;
+ run->area = area;
+ run->level = level;
- 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 [<level-1|level-2>]",
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 <N, d(N), {Adj(N)}>
*/
@@ -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;
-
- if (!tlvs->te_is_neighs)
- tlvs->te_is_neighs = list_new ();
- listnode_add (tlvs->te_is_neighs, te_is_nei);
- }
- }
- else
- {
- pnt += length;
- }
+ retval = parse_mt_is_neighs(tlvs, false, length, pnt);
+ pnt += length;
+ break;
+
+ 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);
@@ -826,6 +1023,31 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
}
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)
{
struct listnode *node;
@@ -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++;
+ }
+
+ 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 add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
+ 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++;
+ }
+
+ 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 add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
+ 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;