From: Christian Franke Date: Thu, 27 Apr 2017 11:56:45 +0000 (+0200) Subject: isisd: announce and parse MT IP reachabilities X-Git-Tag: reindent-master-before~209^2~1 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=c3ae3127028a92c09bcaed2eabfeaf5e11157438;p=matthieu%2Ffrr.git isisd: announce and parse MT IP reachabilities Signed-off-by: Christian Franke --- diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 5009c34b02..955a73ef61 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -856,6 +856,79 @@ lsp_print_mt_reach(struct list *list, struct vty *vty, } } +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) { @@ -865,12 +938,10 @@ 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]; @@ -985,25 +1056,14 @@ 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 */ lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty, @@ -1014,17 +1074,13 @@ lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) 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; @@ -1292,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) @@ -1301,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) @@ -1314,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); @@ -1327,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); } } @@ -1356,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; @@ -1591,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 = @@ -1619,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); } } @@ -1804,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) diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index e66c9d7d9c..baf72ad22c 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -34,6 +34,18 @@ 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) @@ -492,6 +504,106 @@ tlvs_get_mt_neighbors(struct tlvs *tlvs, 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) diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h index 313e992b4a..4c991dc5c9 100644 --- a/isisd/isis_mt.h +++ b/isisd/isis_mt.h @@ -71,6 +71,16 @@ struct tlv_mt_neighbors { 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); @@ -80,9 +90,17 @@ struct isis_circuit; 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, diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index 41c861bf58..b033e35a2e 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -86,10 +86,14 @@ 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)); @@ -97,33 +101,43 @@ free_tlvs (struct tlvs *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) @@ -173,6 +187,192 @@ parse_mt_is_neighs(struct tlvs *tlvs, bool read_mtid, 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. @@ -189,12 +389,9 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, 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)); @@ -629,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 @@ -724,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 @@ -1239,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 @@ -1297,36 +1418,49 @@ tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream) return add_tlv (IPV6_ADDR, pos - value, value, stream); } -int -tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream) +unsigned int +tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream, void *arg) { struct listnode *node; struct ipv6_reachability *ip6reach; u_char value[255]; u_char *pos = value; - int retval, prefix_octets; + uint16_t mtid = arg ? *(uint16_t*)arg : ISIS_MT_IPV4_UNICAST; + unsigned int consumed = 0; + size_t max_size = max_tlv_size(stream); + + if (mtid != ISIS_MT_IPV4_UNICAST) + { + uint16_t mtid_conversion = ntohs(mtid); + memcpy(pos, &mtid_conversion, sizeof(mtid_conversion)); + pos += sizeof(mtid_conversion); + } for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach)) { - if (pos - value + IPV6_MAX_BYTELEN + 6 > 255) - { - retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - *(uint32_t *) pos = ip6reach->metric; + if ((size_t)(pos - value) + 6 + PSIZE(ip6reach->prefix_len) > max_size) + break; + + *(uint32_t *)pos = ip6reach->metric; pos += 4; *pos = ip6reach->control_info; pos++; - prefix_octets = ((ip6reach->prefix_len + 7) / 8); *pos = ip6reach->prefix_len; pos++; - memcpy (pos, ip6reach->prefix, prefix_octets); - pos += prefix_octets; + memcpy (pos, ip6reach->prefix, PSIZE(ip6reach->prefix_len)); + pos += PSIZE(ip6reach->prefix_len); + consumed++; } - return add_tlv (IPV6_REACHABILITY, pos - value, value, stream); + if (consumed) + { + int rv = add_tlv ((mtid != ISIS_MT_IPV4_UNICAST) ? MT_IPV6_REACHABILITY + : IPV6_REACHABILITY, + pos - value, value, stream); + assert(rv == ISIS_OK); + } + + return consumed; } int diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h index f3c04baf54..2135f5071f 100644 --- a/isisd/isis_tlv.h +++ b/isisd/isis_tlv.h @@ -114,7 +114,9 @@ #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 @@ -282,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; }; @@ -339,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);