Merge pull request #423 from opensourcerouting/feature/isis-mt

IS-IS multi topology
This commit is contained in:
Donald Sharp 2017-04-28 10:18:33 -04:00 committed by GitHub
commit ed8ba2e920
20 changed files with 2215 additions and 807 deletions

View File

@ -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) \

View File

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

View File

@ -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 */

View File

@ -212,16 +212,11 @@ isis_sock_init (struct isis_circuit *circuit)
goto end;
}
if (circuit->circ_type == CIRCUIT_T_BROADCAST)
if (if_is_broadcast(circuit->interface))
{
circuit->tx = isis_send_pdu_bcast;
circuit->rx = isis_recv_pdu_bcast;
}
else if (circuit->circ_type == CIRCUIT_T_P2P)
{
circuit->tx = isis_send_pdu_p2p;
circuit->rx = isis_recv_pdu_p2p;
}
else
{
zlog_warn ("isis_sock_init(): unknown circuit type");
@ -283,23 +278,6 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa)
return ISIS_OK;
}
int
isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa)
{
int bytesread;
bytesread = stream_read (circuit->rcv_stream, circuit->fd,
circuit->interface->mtu);
if (bytesread < 0)
{
zlog_warn ("isis_recv_pdu_p2p(): read () failed: %s", safe_strerror (errno));
return ISIS_WARNING;
}
return ISIS_OK;
}
int
isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
{
@ -327,7 +305,8 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
else
memcpy (eth->ether_dhost, ALL_L2_ISS, ETHER_ADDR_LEN);
memcpy (eth->ether_shost, circuit->u.bc.snpa, ETHER_ADDR_LEN);
eth->ether_type = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN);
size_t frame_size = stream_get_endp(circuit->snd_stream) + LLC_LEN;
eth->ether_type = htons(isis_ethertype(frame_size));
/*
* Then the LLC
@ -354,10 +333,4 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level)
return ISIS_OK;
}
int
isis_send_pdu_p2p (struct isis_circuit *circuit, int level)
{
return ISIS_OK;
}
#endif /* ISIS_METHOD == ISIS_METHOD_BPF */

View File

@ -60,6 +60,7 @@
#include "isisd/isis_csm.h"
#include "isisd/isis_events.h"
#include "isisd/isis_te.h"
#include "isisd/isis_mt.h"
DEFINE_QOBJ_TYPE(isis_circuit)
@ -102,6 +103,8 @@ isis_circuit_new ()
circuit->mtc = mpls_te_circuit_new();
circuit_mt_init(circuit);
QOBJ_REG (circuit, isis_circuit);
return circuit;
@ -117,6 +120,8 @@ isis_circuit_del (struct isis_circuit *circuit)
isis_circuit_if_unbind (circuit, circuit->interface);
circuit_mt_finish(circuit);
/* and lastly the circuit itself */
XFREE (MTYPE_ISIS_CIRCUIT, circuit);
@ -1215,6 +1220,7 @@ isis_interface_config_write (struct vty *vty)
VTY_NEWLINE);
write++;
}
write += circuit_write_mt_settings(circuit, vty);
}
vty_out (vty, "!%s", VTY_NEWLINE);
}
@ -1382,6 +1388,22 @@ isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type)
return 0;
}
int
isis_circuit_mt_enabled_set (struct isis_circuit *circuit, uint16_t mtid,
bool enabled)
{
struct isis_circuit_mt_setting *setting;
setting = circuit_get_mt_setting(circuit, mtid);
if (setting->enabled != enabled)
{
setting->enabled = enabled;
lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0);
}
return CMD_SUCCESS;
}
int
isis_if_new_hook (struct interface *ifp)
{

View File

@ -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 */

View File

@ -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 */

View File

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

View File

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

724
isisd/isis_mt.c Normal file
View File

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

146
isisd/isis_mt.h Normal file
View File

@ -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

View File

@ -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)

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -43,6 +43,7 @@
#include "isisd/isis_pdu.h"
#include "isisd/isis_lsp.h"
#include "isisd/isis_te.h"
#include "isisd/isis_mt.h"
void
free_tlv (void *val)
@ -61,10 +62,14 @@ free_tlvs (struct tlvs *tlvs)
{
if (tlvs->area_addrs)
list_delete (tlvs->area_addrs);
if (tlvs->mt_router_info)
list_delete (tlvs->mt_router_info);
if (tlvs->is_neighs)
list_delete (tlvs->is_neighs);
if (tlvs->te_is_neighs)
list_delete (tlvs->te_is_neighs);
if (tlvs->mt_is_neighs)
list_delete (tlvs->mt_is_neighs);
if (tlvs->es_neighs)
list_delete (tlvs->es_neighs);
if (tlvs->lsp_entries)
@ -81,16 +86,293 @@ free_tlvs (struct tlvs *tlvs)
list_delete (tlvs->ipv4_ext_reachs);
if (tlvs->te_ipv4_reachs)
list_delete (tlvs->te_ipv4_reachs);
if (tlvs->mt_ipv4_reachs)
list_delete (tlvs->mt_ipv4_reachs);
if (tlvs->ipv6_addrs)
list_delete (tlvs->ipv6_addrs);
if (tlvs->ipv6_reachs)
list_delete (tlvs->ipv6_reachs);
if (tlvs->mt_ipv6_reachs)
list_delete (tlvs->mt_ipv6_reachs);
memset (tlvs, 0, sizeof (struct tlvs));
return;
}
static int
parse_mtid(uint16_t *mtid, bool read_mtid,
unsigned int *length, u_char **pnt)
{
if (!read_mtid)
{
*mtid = ISIS_MT_IPV4_UNICAST;
return ISIS_OK;
}
uint16_t mtid_buf;
if (*length < sizeof(mtid_buf))
{
zlog_warn("ISIS-TLV: mt tlv too short to contain MT id");
return ISIS_WARNING;
}
memcpy(&mtid_buf, *pnt, sizeof(mtid_buf));
*pnt += sizeof(mtid_buf);
*length -= sizeof(mtid_buf);
*mtid = ntohs(mtid_buf) & ISIS_MT_MASK;
return ISIS_OK;
}
static int
parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid,
unsigned int length, u_char *pnt)
{
struct list *neigh_list;
uint16_t mtid;
int rv;
rv = parse_mtid(&mtid, read_mtid, &length, &pnt);
if (rv != ISIS_OK)
return rv;
if (mtid == ISIS_MT_IPV4_UNICAST)
{
if (!tlvs->te_is_neighs)
{
tlvs->te_is_neighs = list_new();
tlvs->te_is_neighs->del = free_tlv;
}
neigh_list = tlvs->te_is_neighs;
}
else
{
struct tlv_mt_neighbors *neighbors;
neighbors = tlvs_get_mt_neighbors(tlvs, mtid);
neighbors->list->del = free_tlv;
neigh_list = neighbors->list;
}
while (length >= IS_NEIGHBOURS_LEN)
{
struct te_is_neigh *neigh = XCALLOC(MTYPE_ISIS_TLV, sizeof(*neigh));
memcpy(neigh, pnt, IS_NEIGHBOURS_LEN);
pnt += IS_NEIGHBOURS_LEN;
length -= IS_NEIGHBOURS_LEN;
if (neigh->sub_tlvs_length > length)
{
zlog_warn("ISIS-TLV: neighbor subtlv length exceeds TLV size");
XFREE(MTYPE_ISIS_TLV, neigh);
return ISIS_WARNING;
}
memcpy(neigh->sub_tlvs, pnt, neigh->sub_tlvs_length);
pnt += neigh->sub_tlvs_length;
length -= neigh->sub_tlvs_length;
listnode_add(neigh_list, neigh);
}
if (length)
{
zlog_warn("ISIS-TLV: TE/MT neighor TLV has trailing data");
return ISIS_WARNING;
}
return ISIS_OK;
}
static int
parse_mt_ipv4_reachs(struct tlvs *tlvs, bool read_mtid,
unsigned int length, u_char *pnt)
{
struct list *reach_list;
uint16_t mtid;
int rv;
rv = parse_mtid(&mtid, read_mtid, &length, &pnt);
if (rv != ISIS_OK)
return rv;
if (mtid == ISIS_MT_IPV4_UNICAST)
{
if (!tlvs->te_ipv4_reachs)
{
tlvs->te_ipv4_reachs = list_new();
tlvs->te_ipv4_reachs->del = free_tlv;
}
reach_list = tlvs->te_ipv4_reachs;
}
else
{
struct tlv_mt_ipv4_reachs *reachs;
reachs = tlvs_get_mt_ipv4_reachs(tlvs, mtid);
reachs->list->del = free_tlv;
reach_list = reachs->list;
}
while (length >= 5) /* Metric + Control */
{
struct te_ipv4_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, TE_IPV4_REACH_LEN);
memcpy(reach, pnt, 5); /* Metric + Control */
pnt += 5;
length -= 5;
unsigned char prefixlen = reach->control & 0x3F;
if (prefixlen > IPV4_MAX_BITLEN)
{
zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix length %d", prefixlen);
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
if (length < (unsigned int)PSIZE(prefixlen))
{
zlog_warn("ISIS-TLV: invalid IPv4 extended reachability prefix too long for tlv");
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
memcpy(&reach->prefix_start, pnt, PSIZE(prefixlen));
pnt += PSIZE(prefixlen);
length -= PSIZE(prefixlen);
if (reach->control & TE_IPV4_HAS_SUBTLV)
{
if (length < 1)
{
zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLV missing");
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
u_char subtlv_len = *pnt;
pnt++;
length--;
if (length < subtlv_len)
{
zlog_warn("ISIS-TLV: invalid IPv4 extended reachability SubTLVs have oversize");
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
/* Skip Sub-TLVs for now */
pnt += subtlv_len;
length -= subtlv_len;
}
listnode_add(reach_list, reach);
}
if (length)
{
zlog_warn("ISIS-TLV: TE/MT ipv4 reachability TLV has trailing data");
return ISIS_WARNING;
}
return ISIS_OK;
}
static int
parse_mt_ipv6_reachs(struct tlvs *tlvs, bool read_mtid,
unsigned int length, u_char *pnt)
{
struct list *reach_list;
uint16_t mtid;
int rv;
rv = parse_mtid(&mtid, read_mtid, &length, &pnt);
if (rv != ISIS_OK)
return rv;
if (mtid == ISIS_MT_IPV4_UNICAST)
{
if (!tlvs->ipv6_reachs)
{
tlvs->ipv6_reachs = list_new();
tlvs->ipv6_reachs->del = free_tlv;
}
reach_list = tlvs->ipv6_reachs;
}
else
{
struct tlv_mt_ipv6_reachs *reachs;
reachs = tlvs_get_mt_ipv6_reachs(tlvs, mtid);
reachs->list->del = free_tlv;
reach_list = reachs->list;
}
while (length >= 6) /* Metric + Control + Prefixlen */
{
struct ipv6_reachability *reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*reach));
memcpy(reach, pnt, 6); /* Metric + Control + Prefixlen */
pnt += 6;
length -= 6;
if (reach->prefix_len > IPV6_MAX_BITLEN)
{
zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix length %d", reach->prefix_len);
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
if (length < (unsigned int)PSIZE(reach->prefix_len))
{
zlog_warn("ISIS-TLV: invalid IPv6 reachability prefix too long for tlv");
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
memcpy(&reach->prefix, pnt, PSIZE(reach->prefix_len));
pnt += PSIZE(reach->prefix_len);
length -= PSIZE(reach->prefix_len);
if (reach->control_info & CTRL_INFO_SUBTLVS)
{
if (length < 1)
{
zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLV missing");
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
u_char subtlv_len = *pnt;
pnt++;
length--;
if (length < subtlv_len)
{
zlog_warn("ISIS-TLV: invalid IPv6 reachability SubTLVs have oversize");
XFREE(MTYPE_ISIS_TLV, reach);
return ISIS_WARNING;
}
/* Skip Sub-TLVs for now */
pnt += subtlv_len;
length -= subtlv_len;
}
listnode_add(reach_list, reach);
}
if (length)
{
zlog_warn("ISIS-TLV: (MT) IPv6 reachability TLV has trailing data");
return ISIS_WARNING;
}
return ISIS_OK;
}
/*
* Parses the tlvs found in the variant length part of the PDU.
* Caller tells with flags in "expected" which TLV's it is interested in.
@ -103,17 +385,13 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
struct lan_neigh *lan_nei;
struct area_addr *area_addr;
struct is_neigh *is_nei;
struct te_is_neigh *te_is_nei;
struct es_neigh *es_nei;
struct lsp_entry *lsp_entry;
struct in_addr *ipv4_addr;
struct ipv4_reachability *ipv4_reach;
struct te_ipv4_reachability *te_ipv4_reach;
struct in6_addr *ipv6_addr;
struct ipv6_reachability *ipv6_reach;
int prefix_octets;
int value_len, retval = ISIS_OK;
u_char *start = stream, *pnt = stream, *endpnt;
u_char *start = stream, *pnt = stream;
*found = 0;
memset (tlvs, 0, sizeof (struct tlvs));
@ -207,54 +485,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
break;
case TE_IS_NEIGHBOURS:
/* +-------+-------+-------+-------+-------+-------+-------+-------+
* | Neighbour ID | 7
* +---------------------------------------------------------------+
* | TE Metric | 3
* +---------------------------------------------------------------+
* | SubTLVs Length | 1
* +---------------------------------------------------------------+
* : :
*/
*found |= TLVFLAG_TE_IS_NEIGHS;
#ifdef EXTREME_TLV_DEBUG
zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d",
areatag, length);
#endif /* EXTREME_TLV_DEBUG */
if (TLVFLAG_TE_IS_NEIGHS & *expected)
{
while (length > value_len)
{
te_is_nei = (struct te_is_neigh *) pnt;
value_len += IS_NEIGHBOURS_LEN;
pnt += IS_NEIGHBOURS_LEN;
/* FIXME - subtlvs are handled here, for now we skip */
/* FIXME: All TE SubTLVs are not necessary present in LSP PDU. */
/* So, it must be copied in a new te_is_neigh structure */
/* rather than just initialize pointer to the original LSP PDU */
/* to avoid consider the rest of lspdu as subTLVs or buffer overflow */
if (IS_MPLS_TE(isisMplsTE))
{
struct te_is_neigh *new = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh));
memcpy(new->neigh_id, te_is_nei->neigh_id, ISIS_SYS_ID_LEN + 1);
memcpy(new->te_metric, te_is_nei->te_metric, 3);
new->sub_tlvs_length = te_is_nei->sub_tlvs_length;
memcpy(new->sub_tlvs, pnt, te_is_nei->sub_tlvs_length);
te_is_nei = new;
}
/* Skip SUB TLVs payload */
value_len += te_is_nei->sub_tlvs_length;
pnt += te_is_nei->sub_tlvs_length;
retval = parse_mt_is_neighs(tlvs, false, length, pnt);
pnt += length;
break;
if (!tlvs->te_is_neighs)
tlvs->te_is_neighs = list_new ();
listnode_add (tlvs->te_is_neighs, te_is_nei);
}
}
else
{
pnt += length;
}
case MT_IS_NEIGHBOURS:
*found |= TLVFLAG_TE_IS_NEIGHS;
#ifdef EXTREME_TLV_DEBUG
zlog_debug ("ISIS-TLV (%s): MT IS Neighbours length %d",
areatag, length);
#endif
if (TLVFLAG_TE_IS_NEIGHS & *expected)
retval = parse_mt_is_neighs(tlvs, true, length, pnt);
pnt += length;
break;
case ES_NEIGHBOURS:
@ -577,71 +826,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
break;
case TE_IPV4_REACHABILITY:
/* +-------+-------+-------+-------+-------+-------+-------+-------+
* | TE Metric | 4
* +-------+-------+-------+-------+-------+-------+-------+-------+
* | U/D | sTLV? | Prefix Mask Len | 1
* +-------+-------+-------+-------+-------+-------+-------+-------+
* | Prefix | 0-4
* +---------------------------------------------------------------+
* | sub tlvs |
* +---------------------------------------------------------------+
* : :
*/
*found |= TLVFLAG_TE_IPV4_REACHABILITY;
#ifdef EXTREME_TLV_DEBUG
zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d",
areatag, length);
areatag, length);
#endif /* EXTREME_TLV_DEBUG */
endpnt = pnt + length;
if (*expected & TLVFLAG_TE_IPV4_REACHABILITY)
{
while (length > value_len)
{
te_ipv4_reach = (struct te_ipv4_reachability *) pnt;
if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN)
{
zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach"
"ability prefix length %d", areatag,
te_ipv4_reach->control & 0x3F);
retval = ISIS_WARNING;
break;
}
if (!tlvs->te_ipv4_reachs)
tlvs->te_ipv4_reachs = list_new ();
listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach);
/* Metric + Control-Byte + Prefix */
unsigned int entry_len = 5 + PSIZE(te_ipv4_reach->control & 0x3F);
value_len += entry_len;
pnt += entry_len;
if (te_ipv4_reach->control & TE_IPV4_HAS_SUBTLV)
{
if (length <= value_len)
{
zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLV missing",
areatag);
retval = ISIS_WARNING;
break;
}
u_char subtlv_len = *pnt;
value_len += subtlv_len + 1;
pnt += subtlv_len + 1;
if (length < value_len)
{
zlog_warn("ISIS-TLV (%s): invalid IPv4 extended reachability SubTLVs have oversize",
areatag);
retval = ISIS_WARNING;
break;
}
}
}
}
pnt = endpnt;
retval = parse_mt_ipv4_reachs(tlvs, false, length, pnt);
pnt += length;
break;
case MT_IPV4_REACHABILITY:
*found |= TLVFLAG_TE_IPV4_REACHABILITY;
#ifdef EXTREME_TLV_DEBUG
zlog_debug ("ISIS-TLV (%s): IPv4 MT Reachability length %d",
areatag, length);
#endif /* EXTREME_TLV_DEBUG */
if (*expected & TLVFLAG_TE_IPV4_REACHABILITY)
retval = parse_mt_ipv4_reachs(tlvs, true, length, pnt);
pnt += length;
break;
case IPV6_ADDR:
/* +-------+-------+-------+-------+-------+-------+-------+-------+
* + IP version 6 address + 16
@ -672,67 +875,25 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
break;
case IPV6_REACHABILITY:
/* +-------+-------+-------+-------+-------+-------+-------+-------+
* | Default Metric | 4
* +-------+-------+-------+-------+-------+-------+-------+-------+
* | Control Informantion |
* +---------------------------------------------------------------+
* | IPv6 Prefix Length |--+
* +---------------------------------------------------------------+ |
* | IPv6 Prefix |<-+
* +---------------------------------------------------------------+
*/
*found |= TLVFLAG_IPV6_REACHABILITY;
endpnt = pnt + length;
#ifdef EXTREME_TLV_DEBUG
zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d",
areatag, length);
#endif /* EXTREME_TLV_DEBUG */
if (*expected & TLVFLAG_IPV6_REACHABILITY)
{
while (length > value_len)
{
ipv6_reach = (struct ipv6_reachability *) pnt;
if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN)
{
zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach"
"ability prefix length %d", areatag,
ipv6_reach->prefix_len);
retval = ISIS_WARNING;
break;
}
prefix_octets = ((ipv6_reach->prefix_len + 7) / 8);
value_len += prefix_octets + 6;
pnt += prefix_octets + 6;
if (ipv6_reach->control_info & CTRL_INFO_SUBTLVS)
{
if (length <= value_len)
{
zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLV missing",
areatag);
retval = ISIS_WARNING;
break;
}
u_char subtlv_len = *pnt;
value_len += subtlv_len + 1;
pnt += subtlv_len + 1;
if (length < value_len)
{
zlog_warn("ISIS-TLV (%s): invalid IPv6 extended reachability SubTLVs have oversize",
areatag);
retval = ISIS_WARNING;
break;
}
}
/* FIXME: sub-tlvs */
if (!tlvs->ipv6_reachs)
tlvs->ipv6_reachs = list_new ();
listnode_add (tlvs->ipv6_reachs, ipv6_reach);
}
}
pnt = endpnt;
retval = parse_mt_ipv6_reachs(tlvs, false, length, pnt);
pnt += length;
break;
case MT_IPV6_REACHABILITY:
*found |= TLVFLAG_IPV6_REACHABILITY;
#ifdef EXTREME_TLV_DEBUG
zlog_debug ("ISIS-TLV (%s): IPv6 Reachability length %d",
areatag, length);
#endif /* EXTREME_TLV_DEBUG */
if (*expected & TLVFLAG_IPV6_REACHABILITY)
retval = parse_mt_ipv6_reachs(tlvs, true, length, pnt);
pnt += length;
break;
case WAY3_HELLO:
/* +---------------------------------------------------------------+
* | Adjacency state | 1
@ -786,6 +947,42 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
pnt += length;
break;
case MT_ROUTER_INFORMATION:
*found |= TLVFLAG_MT_ROUTER_INFORMATION;
if (*expected & TLVFLAG_MT_ROUTER_INFORMATION)
{
if (!tlvs->mt_router_info)
{
tlvs->mt_router_info = list_new();
tlvs->mt_router_info->del = free_tlv;
}
while (length > value_len)
{
uint16_t mt_info;
struct mt_router_info *info;
if (value_len + sizeof(mt_info) > length) {
zlog_warn("ISIS-TLV (%s): TLV 229 is truncated.", areatag);
pnt += length - value_len;
break;
}
memcpy(&mt_info, pnt, sizeof(mt_info));
pnt += sizeof(mt_info);
value_len += sizeof(mt_info);
mt_info = ntohs(mt_info);
info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info));
info->mtid = mt_info & ISIS_MT_MASK;
info->overload = mt_info & ISIS_MT_OL_MASK;
listnode_add(tlvs->mt_router_info, info);
}
}
else
{
pnt += length;
}
break;
default:
zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d",
areatag, type, length);
@ -825,6 +1022,31 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
return ISIS_OK;
}
int
tlv_add_mt_router_info (struct list *mt_router_info, struct stream *stream)
{
struct listnode *node;
struct mt_router_info *info;
uint16_t value[127];
uint16_t *pos = value;
for (ALL_LIST_ELEMENTS_RO(mt_router_info, node, info))
{
uint16_t mt_info;
mt_info = info->mtid;
if (info->overload)
mt_info |= ISIS_MT_OL_MASK;
*pos = htons(mt_info);
pos++;
}
return add_tlv(MT_ROUTER_INFORMATION, (pos - value) * sizeof(*pos),
(u_char*)value, stream);
}
int
tlv_add_area_addrs (struct list *area_addrs, struct stream *stream)
{
@ -887,26 +1109,44 @@ tlv_add_is_neighs (struct list *is_neighs, struct stream *stream)
return add_tlv (IS_NEIGHBOURS, pos - value, value, stream);
}
int
tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream)
static size_t
max_tlv_size(struct stream *stream)
{
size_t avail = stream_get_size (stream) - stream_get_endp(stream);
if (avail < 2)
return 0;
if (avail < 257)
return avail - 2;
return 255;
}
unsigned int
tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream, void *arg)
{
struct listnode *node;
struct te_is_neigh *te_is_neigh;
u_char value[255];
u_char *pos = value;
int retval;
uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST;
unsigned int consumed = 0;
size_t max_size = max_tlv_size(stream);
if (mtid != ISIS_MT_IPV4_UNICAST)
{
uint16_t mtid_conversion = ntohs(mtid);
memcpy(pos, &mtid_conversion, sizeof(mtid_conversion));
pos += sizeof(mtid_conversion);
}
for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh))
{
/* FIXME: Check if Total SubTLVs size doesn't exceed 255 */
if (pos - value + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > 255)
{
retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
if (retval != ISIS_OK)
return retval;
pos = value;
}
if ((size_t)(pos - value) + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > max_size)
break;
memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1);
pos += ISIS_SYS_ID_LEN + 1;
memcpy (pos, te_is_neigh->te_metric, 3);
@ -920,9 +1160,17 @@ tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream)
memcpy (pos, te_is_neigh->sub_tlvs, te_is_neigh->sub_tlvs_length);
pos += te_is_neigh->sub_tlvs_length;
}
consumed++;
}
return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
if (consumed)
{
int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IS_NEIGHBOURS
: TE_IS_NEIGHBOURS,
pos - value, value, stream);
assert(rv == ISIS_OK);
}
return consumed;
}
int
@ -1100,37 +1348,49 @@ tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream)
}
int
tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream)
unsigned int
tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream, void *arg)
{
struct listnode *node;
struct te_ipv4_reachability *te_reach;
u_char value[255];
u_char *pos = value;
u_char prefix_size;
int retval;
uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST;
unsigned int consumed = 0;
size_t max_size = max_tlv_size(stream);
if (mtid != ISIS_MT_IPV4_UNICAST)
{
uint16_t mtid_conversion = ntohs(mtid);
memcpy(pos, &mtid_conversion, sizeof(mtid_conversion));
pos += sizeof(mtid_conversion);
}
for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach))
{
prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1);
unsigned char prefixlen = te_reach->control & 0x3F;
if ((size_t)(pos - value) + 5 + PSIZE(prefixlen) > max_size)
break;
if (pos - value + (5 + prefix_size) > 255)
{
retval =
add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
if (retval != ISIS_OK)
return retval;
pos = value;
}
*(u_int32_t *) pos = te_reach->te_metric;
pos += 4;
*pos = te_reach->control;
pos++;
memcpy (pos, &te_reach->prefix_start, prefix_size);
pos += prefix_size;
memcpy (pos, &te_reach->prefix_start, PSIZE(prefixlen));
pos += PSIZE(prefixlen);
consumed++;
}
return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
if (consumed)
{
int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV4_REACHABILITY
: TE_IPV4_REACHABILITY,
pos - value, value, stream);
assert(rv == ISIS_OK);
}
return consumed;
}
int
@ -1158,36 +1418,49 @@ tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream)
return add_tlv (IPV6_ADDR, pos - value, value, stream);
}
int
tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream)
unsigned int
tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg)
{
struct listnode *node;
struct ipv6_reachability *ip6reach;
u_char value[255];
u_char *pos = value;
int retval, prefix_octets;
uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST;
unsigned int consumed = 0;
size_t max_size = max_tlv_size(stream);
if (mtid != ISIS_MT_IPV4_UNICAST)
{
uint16_t mtid_conversion = ntohs(mtid);
memcpy(pos, &mtid_conversion, sizeof(mtid_conversion));
pos += sizeof(mtid_conversion);
}
for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach))
{
if (pos - value + IPV6_MAX_BYTELEN + 6 > 255)
{
retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
if (retval != ISIS_OK)
return retval;
pos = value;
}
*(uint32_t *) pos = ip6reach->metric;
if ((size_t)(pos - value) + 6 + PSIZE(ip6reach->prefix_len) > max_size)
break;
*(uint32_t *)pos = ip6reach->metric;
pos += 4;
*pos = ip6reach->control_info;
pos++;
prefix_octets = ((ip6reach->prefix_len + 7) / 8);
*pos = ip6reach->prefix_len;
pos++;
memcpy (pos, ip6reach->prefix, prefix_octets);
pos += prefix_octets;
memcpy (pos, ip6reach->prefix, PSIZE(ip6reach->prefix_len));
pos += PSIZE(ip6reach->prefix_len);
consumed++;
}
return add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
if (consumed)
{
int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV6_REACHABILITY
: IPV6_REACHABILITY,
pos - value, value, stream);
assert(rv == ISIS_OK);
}
return consumed;
}
int

View File

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

View File

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

View File

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

View File

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