}
}
+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)
{
struct is_neigh *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 mt_router_info *mt_router_info;
+ struct tlv_mt_ipv6_reachs *mt_ipv6_reachs;
struct tlv_mt_neighbors *mt_is_neigh;
- struct in6_addr in6;
- u_char buff[BUFSIZ];
+ struct tlv_mt_ipv4_reachs *mt_ipv4_reachs;
u_char LSPid[255];
u_char hostname[255];
u_char ipv4_reach_prefix[20];
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 */
lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty,
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;
}
}
+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)
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)
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);
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);
}
}
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;
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 =
memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr,
sizeof (ip6reach->prefix));
- listnode_add (tlv_data.ipv6_reachs, ip6reach);
+ listnode_add (ipv6_reachs, ip6reach);
}
}
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)
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)
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)
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 tlvs;
struct te_is_neigh;
+uint16_t isis_area_ipv6_topology(struct isis_area *area);
+
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,
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));
}
static int
-parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid,
- unsigned int length, u_char *pnt)
+parse_mtid(uint16_t *mtid, bool read_mtid,
+ unsigned int *length, u_char **pnt)
{
- struct list *neigh_list;
- uint16_t mtid;
-
- if (read_mtid)
+ if (!read_mtid)
{
- uint16_t mtid_buf;
-
- if (length < sizeof(mtid_buf))
- {
- zlog_warn("ISIS-TLV: mt tlv too short to contain MT id");
- return ISIS_WARNING;
- }
+ *mtid = ISIS_MT_IPV4_UNICAST;
+ return ISIS_OK;
+ }
- memcpy(&mtid_buf, pnt, sizeof(mtid_buf));
- pnt += sizeof(mtid_buf);
- length -= sizeof(mtid_buf);
+ uint16_t mtid_buf;
- mtid = ntohs(mtid_buf) & ISIS_MT_MASK;
- }
- else
+ if (*length < sizeof(mtid_buf))
{
- mtid = ISIS_MT_IPV4_UNICAST;
+ 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)
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.
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));
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
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
}
-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
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
#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
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;
};
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);