diff --git a/isisd/Makefile.am b/isisd/Makefile.am index 2973820eed..dc3d3683a1 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -8,24 +8,26 @@ LIBS = @LIBS@ AM_CFLAGS = $(WERROR) noinst_LIBRARIES = libisis.a -sbin_PROGRAMS = isisd +sbin_PROGRAMS = isisd libisis_a_SOURCES = \ isis_memory.c \ isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \ - isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.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_mt.c + isis_vty.c isis_mt.c \ + isis_tlvs.c noinst_HEADERS = \ isis_memory.h \ - isisd.h isis_pdu.h isis_tlv.h isis_adjacency.h isis_constants.h \ + isisd.h isis_pdu.h isis_adjacency.h isis_constants.h \ 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_mt.h + isis_route.h isis_routemap.h isis_te.h isis_mt.h \ + isis_tlvs.h isisd_SOURCES = \ isis_main.c $(libisis_a_SOURCES) \ diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index d8cb32375b..0afa65d726 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -43,7 +43,6 @@ #include "isisd/isis_dr.h" #include "isisd/isis_dynhn.h" #include "isisd/isis_pdu.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_spf.h" #include "isisd/isis_events.h" @@ -69,11 +68,6 @@ struct isis_adjacency *isis_new_adj(const u_char *id, const u_char *snpa, adj = adj_alloc(id); /* P2P kludge */ - if (adj == NULL) { - zlog_err("Out of memory!"); - return NULL; - } - if (snpa) { memcpy(adj->snpa, snpa, ETH_ALEN); } else { @@ -137,12 +131,12 @@ void isis_delete_adj(void *arg) /* remove from SPF trees */ spftree_area_adj_del(adj->circuit->area, adj); - if (adj->area_addrs) - list_delete(adj->area_addrs); - if (adj->ipv4_addrs) - list_delete(adj->ipv4_addrs); - if (adj->ipv6_addrs) - list_delete(adj->ipv6_addrs); + if (adj->area_addresses) + XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses); + if (adj->ipv4_addresses) + XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses); + if (adj->ipv6_addresses) + XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses); adj_mt_finish(adj); @@ -192,7 +186,7 @@ void isis_adj_state_change(struct isis_adjacency *adj, dyn = dynhn_find_by_id(adj->sysid); if (dyn) - adj_name = (const char *)dyn->name.name; + adj_name = dyn->hostname; else adj_name = sysid_print(adj->sysid); @@ -301,33 +295,29 @@ void isis_adj_state_change(struct isis_adjacency *adj, void isis_adj_print(struct isis_adjacency *adj) { struct isis_dynhn *dyn; - struct listnode *node; - struct in_addr *ipv4_addr; - struct in6_addr *ipv6_addr; - u_char ip6[INET6_ADDRSTRLEN]; if (!adj) return; dyn = dynhn_find_by_id(adj->sysid); if (dyn) - zlog_debug("%s", dyn->name.name); + zlog_debug("%s", dyn->hostname); zlog_debug("SystemId %20s SNPA %s, level %d\nHolding Time %d", sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level, adj->hold_time); - if (adj->ipv4_addrs && listcount(adj->ipv4_addrs) > 0) { + if (adj->ipv4_address_count) { zlog_debug("IPv4 Address(es):"); - - for (ALL_LIST_ELEMENTS_RO(adj->ipv4_addrs, node, ipv4_addr)) - zlog_debug("%s", inet_ntoa(*ipv4_addr)); + for (unsigned int i = 0; i < adj->ipv4_address_count; i++) + zlog_debug("%s", inet_ntoa(adj->ipv4_addresses[i])); } - if (adj->ipv6_addrs && listcount(adj->ipv6_addrs) > 0) { + if (adj->ipv6_address_count) { zlog_debug("IPv6 Address(es):"); - for (ALL_LIST_ELEMENTS_RO(adj->ipv6_addrs, node, ipv6_addr)) { - inet_ntop(AF_INET6, ipv6_addr, (char *)ip6, - INET6_ADDRSTRLEN); - zlog_debug("%s", ip6); + for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &adj->ipv6_addresses[i], buf, + sizeof(buf)); + zlog_debug("%s", buf); } } zlog_debug("Speaks: %s", nlpid2string(&adj->nlpids)); @@ -358,17 +348,13 @@ int isis_adj_expire(struct thread *thread) void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, char detail) { - struct in6_addr *ipv6_addr; - u_char ip6[INET6_ADDRSTRLEN]; - struct in_addr *ip_addr; time_t now; struct isis_dynhn *dyn; int level; - struct listnode *node; dyn = dynhn_find_by_id(adj->sysid); if (dyn) - vty_out(vty, " %-20s", dyn->name.name); + vty_out(vty, " %-20s", dyn->hostname); else vty_out(vty, " %-20s", sysid_print(adj->sysid)); @@ -429,8 +415,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->lanid); if (dyn) - vty_out(vty, ", LAN id: %s.%02x", - dyn->name.name, + vty_out(vty, ", LAN id: %s.%02x", dyn->hostname, adj->lanid[ISIS_SYS_ID_LEN]); else vty_out(vty, ", LAN id: %s.%02x", @@ -452,28 +437,32 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, } vty_out(vty, "\n"); - if (adj->area_addrs && listcount(adj->area_addrs) > 0) { - struct area_addr *area_addr; + if (adj->area_address_count) { vty_out(vty, " Area Address(es):\n"); - for (ALL_LIST_ELEMENTS_RO(adj->area_addrs, node, - area_addr)) + for (unsigned int i = 0; i < adj->area_address_count; + i++) { vty_out(vty, " %s\n", - isonet_print(area_addr->area_addr, - area_addr->addr_len)); + isonet_print(adj->area_addresses[i] + .area_addr, + adj->area_addresses[i] + .addr_len)); + } } - if (adj->ipv4_addrs && listcount(adj->ipv4_addrs) > 0) { + if (adj->ipv4_address_count) { vty_out(vty, " IPv4 Address(es):\n"); - for (ALL_LIST_ELEMENTS_RO(adj->ipv4_addrs, node, - ip_addr)) - vty_out(vty, " %s\n", inet_ntoa(*ip_addr)); + for (unsigned int i = 0; i < adj->ipv4_address_count; + i++) + vty_out(vty, " %s\n", + inet_ntoa(adj->ipv4_addresses[i])); } - if (adj->ipv6_addrs && listcount(adj->ipv6_addrs) > 0) { + if (adj->ipv6_address_count) { vty_out(vty, " IPv6 Address(es):\n"); - for (ALL_LIST_ELEMENTS_RO(adj->ipv6_addrs, node, - ipv6_addr)) { - inet_ntop(AF_INET6, ipv6_addr, (char *)ip6, - INET6_ADDRSTRLEN); - vty_out(vty, " %s\n", ip6); + for (unsigned int i = 0; i < adj->ipv6_address_count; + i++) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &adj->ipv6_addresses[i], + buf, sizeof(buf)); + vty_out(vty, " %s\n", buf); } } vty_out(vty, "\n"); diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 9f4af1b45d..98bb9838fa 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -73,13 +73,16 @@ struct isis_adjacency { int dischanges[ISIS_LEVELS]; /* how many DIS changes ? */ /* an array of N levels for M records */ struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS]; - enum isis_adj_state adj_state; /* adjacencyState */ - enum isis_adj_usage adj_usage; /* adjacencyUsage */ - struct list *area_addrs; /* areaAdressesOfNeighbour */ - struct nlpids nlpids; /* protocols spoken ... */ - struct list *ipv4_addrs; + enum isis_adj_state adj_state; /* adjacencyState */ + enum isis_adj_usage adj_usage; /* adjacencyUsage */ + struct area_addr *area_addresses; /* areaAdressesOfNeighbour */ + unsigned int area_address_count; + struct nlpids nlpids; /* protocols spoken ... */ + struct in_addr *ipv4_addresses; + unsigned int ipv4_address_count; struct in_addr router_address; - struct list *ipv6_addrs; + struct in6_addr *ipv6_addresses; + unsigned int ipv6_address_count; struct in6_addr router_address6; u_char prio[ISIS_LEVELS]; /* priorityOfNeighbour for DIS */ int circuit_t; /* from hello PDU hdr */ diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 72810532b0..c321677dee 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -48,7 +48,6 @@ #include "isisd/isis_common.h" #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_pdu.h" #include "isisd/isis_network.h" diff --git a/isisd/isis_common.h b/isisd/isis_common.h index ba6c5f876d..b157bb1836 100644 --- a/isisd/isis_common.h +++ b/isisd/isis_common.h @@ -46,16 +46,6 @@ struct isis_passwd { u_char passwd[255]; }; -/* - * (Dynamic) Hostname - * one struct for cache list - * one struct for LSP TLV - */ -struct hostname { - u_char namelen; - u_char name[255]; -}; - /* * Supported Protocol IDs */ diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index b0ccdee769..10870d5c50 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -37,7 +37,6 @@ #include "isisd/isis_common.h" #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_pdu.h" #include "isisd/isis_network.h" diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index 3f532ecf84..2db8271915 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -42,7 +42,6 @@ #include "isisd/isis_adjacency.h" #include "isisd/isis_constants.h" #include "isisd/isis_pdu.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_dr.h" #include "isisd/isis_events.h" diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 9249ad6290..6fa7988304 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -94,38 +94,26 @@ struct isis_dynhn *dynhn_find_by_name(const char *hostname) struct isis_dynhn *dyn = NULL; for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) - if (strncmp((char *)dyn->name.name, hostname, 255) == 0) + if (strncmp(dyn->hostname, hostname, 255) == 0) return dyn; return NULL; } -void isis_dynhn_insert(const u_char *id, struct hostname *hostname, int level) +void isis_dynhn_insert(const u_char *id, const char *hostname, int level) { struct isis_dynhn *dyn; dyn = dynhn_find_by_id(id); - if (dyn) { - memcpy(&dyn->name, hostname, hostname->namelen + 1); - memcpy(dyn->id, id, ISIS_SYS_ID_LEN); - dyn->refresh = time(NULL); - return; - } - dyn = XCALLOC(MTYPE_ISIS_DYNHN, sizeof(struct isis_dynhn)); if (!dyn) { - zlog_warn("isis_dynhn_insert(): out of memory!"); - return; + dyn = XCALLOC(MTYPE_ISIS_DYNHN, sizeof(struct isis_dynhn)); + memcpy(dyn->id, id, ISIS_SYS_ID_LEN); + dyn->level = level; + listnode_add(dyn_cache, dyn); } - /* we also copy the length */ - memcpy(&dyn->name, hostname, hostname->namelen + 1); - memcpy(dyn->id, id, ISIS_SYS_ID_LEN); + snprintf(dyn->hostname, sizeof(dyn->hostname), "%s", hostname); dyn->refresh = time(NULL); - dyn->level = level; - - listnode_add(dyn_cache, dyn); - - return; } void isis_dynhn_remove(const u_char *id) @@ -137,7 +125,6 @@ void isis_dynhn_remove(const u_char *id) return; listnode_delete(dyn_cache, dyn); XFREE(MTYPE_ISIS_DYNHN, dyn); - return; } /* @@ -155,7 +142,7 @@ void dynhn_print_all(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) { vty_out(vty, "%-7d", dyn->level); vty_out(vty, "%-15s%-15s\n", sysid_print(dyn->id), - dyn->name.name); + dyn->hostname); } vty_out(vty, " * %s %s\n", sysid_print(isis->sysid), diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h index f3ca94d40f..635d79f3f3 100644 --- a/isisd/isis_dynhn.h +++ b/isisd/isis_dynhn.h @@ -25,13 +25,13 @@ struct isis_dynhn { u_char id[ISIS_SYS_ID_LEN]; - struct hostname name; + char hostname[256]; time_t refresh; int level; }; void dyn_cache_init(void); -void isis_dynhn_insert(const u_char *id, struct hostname *hostname, int level); +void isis_dynhn_insert(const u_char *id, const char *hostname, int level); void isis_dynhn_remove(const u_char *id); struct isis_dynhn *dynhn_find_by_id(const u_char *id); struct isis_dynhn *dynhn_find_by_name(const char *hostname); diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 9af256ba38..1cc90d031c 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -37,7 +37,6 @@ #include "isisd/isis_common.h" #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_pdu.h" #include "isisd/isis_network.h" diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 40c6141ab8..51fe41a706 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -44,7 +44,6 @@ #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isisd.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_pdu.h" #include "isisd/isis_dynhn.h" @@ -54,6 +53,7 @@ #include "isisd/isis_spf.h" #include "isisd/isis_te.h" #include "isisd/isis_mt.h" +#include "isisd/isis_tlvs.h" /* staticly assigned vars for printing purposes */ char lsp_bits_string[200]; /* FIXME: enough ? */ @@ -105,19 +105,8 @@ static void lsp_clear_data(struct isis_lsp *lsp) if (!lsp) return; - if (lsp->tlv_data.hostname) - isis_dynhn_remove(lsp->lsp_header->lsp_id); - - if (lsp->own_lsp) { - if (lsp->tlv_data.nlpids) - XFREE(MTYPE_ISIS_TLV, lsp->tlv_data.nlpids); - if (lsp->tlv_data.hostname) - XFREE(MTYPE_ISIS_TLV, lsp->tlv_data.hostname); - if (lsp->tlv_data.router_id) - XFREE(MTYPE_ISIS_TLV, lsp->tlv_data.router_id); - } - - free_tlvs(&lsp->tlv_data); + isis_free_tlvs(lsp->tlvs); + lsp->tlvs = NULL; } static void lsp_destroy(struct isis_lsp *lsp) @@ -146,7 +135,7 @@ static void lsp_destroy(struct isis_lsp *lsp) lsp_clear_data(lsp); - if (LSP_FRAGMENT(lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags) { + if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0 && lsp->lspu.frags) { list_delete(lsp->lspu.frags); lsp->lspu.frags = NULL; } @@ -187,7 +176,7 @@ static void lsp_remove_frags(struct list *frags, dict_t *lspdb) struct isis_lsp *lsp; for (ALL_LIST_ELEMENTS(frags, lnode, lnnode, lsp)) { - dnode = dict_lookup(lspdb, lsp->lsp_header->lsp_id); + dnode = dict_lookup(lspdb, lsp->hdr.lsp_id); lsp_destroy(lsp); dnode_destroy(dict_delete(lspdb, dnode)); } @@ -209,7 +198,7 @@ void lsp_search_and_destroy(u_char *id, dict_t *lspdb) /* * If this is a zero lsp, remove all the frags now */ - if (LSP_FRAGMENT(lsp->lsp_header->lsp_id) == 0) { + if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0) { if (lsp->lspu.frags) lsp_remove_frags(lsp->lspu.frags, lspdb); } else { @@ -231,29 +220,25 @@ void lsp_search_and_destroy(u_char *id, dict_t *lspdb) * Compares a LSP to given values * Params are given in net order */ -int lsp_compare(char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, - u_int16_t checksum, u_int16_t rem_lifetime) +int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, + uint16_t checksum, uint16_t rem_lifetime) { - /* no point in double ntohl on seqnum */ - if (lsp->lsp_header->seq_num == seq_num - && lsp->lsp_header->checksum == checksum && - /*comparing with 0, no need to do ntohl */ - ((lsp->lsp_header->rem_lifetime == 0 && rem_lifetime == 0) - || (lsp->lsp_header->rem_lifetime != 0 && rem_lifetime != 0))) { + if (lsp->hdr.seqno == seqno && lsp->hdr.checksum == checksum + && ((lsp->hdr.rem_lifetime == 0 && rem_lifetime == 0) + || (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) { if (isis->debugs & DEBUG_SNP_PACKETS) { zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," - " lifetime %us", - areatag, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime)); + "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s", + areatag, rawlspid_print(lsp->hdr.lsp_id), + lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime); zlog_debug( - "ISIS-Snp (%s): is equal to ours seq 0x%08x," - " cksum 0x%04x, lifetime %us", - areatag, ntohl(seq_num), ntohs(checksum), - ntohs(rem_lifetime)); + "ISIS-Snp (%s): is equal to ours seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s", + areatag, seqno, checksum, rem_lifetime); } return LSP_EQUAL; } @@ -270,171 +255,136 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, * as given * in 7.3.16.2. */ - if (ntohl(seq_num) > ntohl(lsp->lsp_header->seq_num) - || (ntohl(seq_num) == ntohl(lsp->lsp_header->seq_num) - && ((lsp->lsp_header->rem_lifetime != 0 && rem_lifetime == 0) - || lsp->lsp_header->checksum != checksum))) { + if (seqno > lsp->hdr.seqno + || (seqno == lsp->hdr.seqno + && ((lsp->hdr.rem_lifetime != 0 && rem_lifetime == 0) + || lsp->hdr.checksum != checksum))) { if (isis->debugs & DEBUG_SNP_PACKETS) { zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," - " lifetime %us", - areatag, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(seq_num), ntohs(checksum), - ntohs(rem_lifetime)); + "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s", + areatag, rawlspid_print(lsp->hdr.lsp_id), seqno, + checksum, rem_lifetime); zlog_debug( - "ISIS-Snp (%s): is newer than ours seq 0x%08x, " - "cksum 0x%04x, lifetime %us", - areatag, ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime)); + "ISIS-Snp (%s): is newer than ours seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s", + areatag, lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime); } return LSP_NEWER; } if (isis->debugs & DEBUG_SNP_PACKETS) { + zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s", + areatag, rawlspid_print(lsp->hdr.lsp_id), seqno, + checksum, rem_lifetime); zlog_debug( - "ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us", - areatag, rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(seq_num), ntohs(checksum), ntohs(rem_lifetime)); - zlog_debug( - "ISIS-Snp (%s): is older than ours seq 0x%08x," - " cksum 0x%04x, lifetime %us", - areatag, ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime)); + "ISIS-Snp (%s): is older than ours seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s", + areatag, lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime); } return LSP_OLDER; } -static void lsp_auth_add(struct isis_lsp *lsp) +static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer) { - struct isis_passwd *passwd; - unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + uint8_t pdu_type = + (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE; + struct isis_lsp_hdr *hdr = &lsp->hdr; + struct stream *stream = lsp->pdu; - /* - * Add the authentication info if its present - */ - (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) - : (passwd = &lsp->area->domain_passwd); - switch (passwd->type) { - /* Cleartext */ - case ISIS_PASSWD_TYPE_CLEARTXT: - memcpy(&lsp->tlv_data.auth_info, passwd, - sizeof(struct isis_passwd)); - tlv_add_authinfo(passwd->type, passwd->len, passwd->passwd, - lsp->pdu); - break; + fill_fixed_hdr(pdu_type, stream); - /* HMAC MD5 */ - case ISIS_PASSWD_TYPE_HMAC_MD5: - /* Remember where TLV is written so we can later - * overwrite the MD5 hash */ - lsp->auth_tlv_offset = stream_get_endp(lsp->pdu); - memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); - lsp->tlv_data.auth_info.type = ISIS_PASSWD_TYPE_HMAC_MD5; - lsp->tlv_data.auth_info.len = ISIS_AUTH_MD5_SIZE; - memcpy(&lsp->tlv_data.auth_info.passwd, hmac_md5_hash, - ISIS_AUTH_MD5_SIZE); - tlv_add_authinfo(passwd->type, ISIS_AUTH_MD5_SIZE, - hmac_md5_hash, lsp->pdu); - break; - - default: - break; - } + if (len_pointer) + *len_pointer = stream_get_endp(stream); + stream_putw(stream, hdr->pdu_len); + stream_putw(stream, hdr->rem_lifetime); + stream_put(stream, hdr->lsp_id, sizeof(hdr->lsp_id)); + stream_putl(stream, hdr->seqno); + stream_putw(stream, hdr->checksum); + stream_putc(stream, hdr->lsp_bits); } -static void lsp_auth_update(struct isis_lsp *lsp) +static void lsp_add_auth(struct isis_lsp *lsp) { struct isis_passwd *passwd; - unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; - uint16_t checksum, rem_lifetime; - - /* For HMAC MD5 we need to recompute the md5 hash and store it */ - (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) - : (passwd = &lsp->area->domain_passwd); - if (passwd->type != ISIS_PASSWD_TYPE_HMAC_MD5) - return; - - /* - * In transient conditions (when net is configured where authentication - * config and lsp regenerate schedule is not yet run), there could be - * an own_lsp with auth_tlv_offset set to 0. In such a case, simply - * return, when lsp_regenerate is run, lsp will have auth tlv. - */ - if (lsp->auth_tlv_offset == 0) - return; - - /* - * RFC 5304 set auth value, checksum and remaining lifetime to zero - * before computation and reset to old values after computation. - */ - checksum = lsp->lsp_header->checksum; - rem_lifetime = lsp->lsp_header->rem_lifetime; - lsp->lsp_header->checksum = 0; - lsp->lsp_header->rem_lifetime = 0; - /* Set the authentication value as well to zero */ - memset(STREAM_DATA(lsp->pdu) + lsp->auth_tlv_offset + 3, 0, - ISIS_AUTH_MD5_SIZE); - /* Compute autentication value */ - hmac_md5(STREAM_DATA(lsp->pdu), stream_get_endp(lsp->pdu), - (unsigned char *)&passwd->passwd, passwd->len, - (unsigned char *)&hmac_md5_hash); - /* Copy the hash into the stream */ - memcpy(STREAM_DATA(lsp->pdu) + lsp->auth_tlv_offset + 3, hmac_md5_hash, - ISIS_AUTH_MD5_SIZE); - memcpy(&lsp->tlv_data.auth_info.passwd, hmac_md5_hash, - ISIS_AUTH_MD5_SIZE); - /* Copy back the checksum and remaining lifetime */ - lsp->lsp_header->checksum = checksum; - lsp->lsp_header->rem_lifetime = rem_lifetime; + passwd = (lsp->level == IS_LEVEL_1) ? &lsp->area->area_passwd + : &lsp->area->domain_passwd; + isis_tlvs_add_auth(lsp->tlvs, passwd); } -void lsp_inc_seqnum(struct isis_lsp *lsp, u_int32_t seq_num) +static void lsp_pack_pdu(struct isis_lsp *lsp) { - u_int32_t newseq; + if (!lsp->tlvs) + lsp->tlvs = isis_alloc_tlvs(); - if (seq_num == 0 || ntohl(lsp->lsp_header->seq_num) > seq_num) - newseq = ntohl(lsp->lsp_header->seq_num) + 1; + lsp_add_auth(lsp); + + size_t len_pointer; + stream_reset(lsp->pdu); + put_lsp_hdr(lsp, &len_pointer); + isis_pack_tlvs(lsp->tlvs, lsp->pdu, len_pointer, false, true); + + lsp->hdr.pdu_len = stream_get_endp(lsp->pdu); + lsp->hdr.checksum = + ntohs(fletcher_checksum(STREAM_DATA(lsp->pdu) + 12, + stream_get_endp(lsp->pdu) - 12, 12)); +} + +void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno) +{ + uint32_t newseq; + + if (seqno == 0 || lsp->hdr.seqno > seqno) + newseq = lsp->hdr.seqno + 1; else - newseq = seq_num + 1; + newseq = seqno + 1; - lsp->lsp_header->seq_num = htonl(newseq); - - /* Recompute authentication and checksum information */ - lsp_auth_update(lsp); - /* ISO 10589 - 7.3.11 Generation of the checksum - * The checksum shall be computed over all fields in the LSP which - * appear - * after the Remaining Lifetime field. This field (and those appearing - * before it) are excluded so that the LSP may be aged by systems - * without - * requiring recomputation. - */ - fletcher_checksum(STREAM_DATA(lsp->pdu) + 12, - ntohs(lsp->lsp_header->pdu_len) - 12, 12); + lsp->hdr.seqno = newseq; + lsp_pack_pdu(lsp); isis_spf_schedule(lsp->area, lsp->level); +} - return; +static void lsp_purge(struct isis_lsp *lsp, int level) +{ + /* reset stream */ + lsp_clear_data(lsp); + stream_reset(lsp->pdu); + + /* update header */ + lsp->hdr.checksum = 0; + lsp->hdr.rem_lifetime = 0; + lsp->level = level; + lsp->age_out = lsp->area->max_lsp_lifetime[level - 1]; + + lsp_pack_pdu(lsp); + lsp_set_all_srmflags(lsp); } /* - * Genetates checksum for LSP and its frags + * Generates checksum for LSP and its frags */ -static void lsp_seqnum_update(struct isis_lsp *lsp0) +static void lsp_seqno_update(struct isis_lsp *lsp0) { struct isis_lsp *lsp; struct listnode *node; - lsp_inc_seqnum(lsp0, 0); + lsp_inc_seqno(lsp0, 0); if (!lsp0->lspu.frags) return; - for (ALL_LIST_ELEMENTS_RO(lsp0->lspu.frags, node, lsp)) - lsp_inc_seqnum(lsp, 0); + for (ALL_LIST_ELEMENTS_RO(lsp0->lspu.frags, node, lsp)) { + if (lsp->tlvs) + lsp_inc_seqno(lsp, 0); + else + lsp_purge(lsp, lsp0->level); + } return; } @@ -453,12 +403,10 @@ static u_int8_t lsp_bits_generate(int level, int overload_bit, int attached_bit) return lsp_bits; } -static void lsp_update_data(struct isis_lsp *lsp, struct stream *stream, +static void lsp_update_data(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, + struct isis_tlvs *tlvs, struct stream *stream, struct isis_area *area, int level) { - uint32_t expected = 0, found; - int retval; - /* free the old lsp data */ lsp_clear_data(lsp); @@ -467,50 +415,17 @@ static void lsp_update_data(struct isis_lsp *lsp, struct stream *stream, stream_free(lsp->pdu); lsp->pdu = stream_dup(stream); - /* setting pointers to the correct place */ - lsp->isis_header = (struct isis_fixed_hdr *)(STREAM_DATA(lsp->pdu)); - lsp->lsp_header = (struct isis_link_state_hdr *)(STREAM_DATA(lsp->pdu) - + ISIS_FIXED_HDR_LEN); + memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr)); lsp->area = area; lsp->level = level; lsp->age_out = ZERO_AGE_LIFETIME; lsp->installed = time(NULL); - /* - * Get LSP data i.e. TLVs - */ - expected |= TLVFLAG_AUTH_INFO; - expected |= TLVFLAG_AREA_ADDRS; - expected |= TLVFLAG_IS_NEIGHS; - expected |= TLVFLAG_NLPID; - if (area->dynhostname) - expected |= TLVFLAG_DYN_HOSTNAME; - if (area->newmetric) { - expected |= TLVFLAG_TE_IS_NEIGHS; - 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; - expected |= TLVFLAG_IPV6_ADDR; - expected |= TLVFLAG_IPV6_REACHABILITY; - retval = parse_tlvs(area->area_tag, - STREAM_DATA(lsp->pdu) + ISIS_FIXED_HDR_LEN - + ISIS_LSP_HDR_LEN, - ntohs(lsp->lsp_header->pdu_len) - ISIS_FIXED_HDR_LEN - - ISIS_LSP_HDR_LEN, - &expected, &found, &lsp->tlv_data, NULL); - if (retval != ISIS_OK) { - zlog_warn("Could not parse LSP"); - return; - } + lsp->tlvs = tlvs; - if ((found & TLVFLAG_DYN_HOSTNAME) && (area->dynhostname)) { - isis_dynhn_insert(lsp->lsp_header->lsp_id, - lsp->tlv_data.hostname, - (lsp->lsp_header->lsp_bits & LSPBIT_IST) + if (area->dynhostname && lsp->tlvs->hostname) { + isis_dynhn_insert(lsp->hdr.lsp_id, lsp->tlvs->hostname, + (lsp->hdr.lsp_bits & LSPBIT_IST) == IS_LEVEL_1_AND_2 ? IS_LEVEL_2 : IS_LEVEL_1); @@ -519,44 +434,55 @@ static void lsp_update_data(struct isis_lsp *lsp, struct stream *stream, return; } -void lsp_update(struct isis_lsp *lsp, struct stream *stream, - struct isis_area *area, int level) +void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, + struct isis_tlvs *tlvs, struct stream *stream, + struct isis_area *area, int level, bool confusion) { dnode_t *dnode = NULL; /* Remove old LSP from database. This is required since the * lsp_update_data will free the lsp->pdu (which has the key, lsp_id) - * and will update it with the new data in the stream. */ - dnode = dict_lookup(area->lspdb[level - 1], lsp->lsp_header->lsp_id); + * and will update it with the new data in the stream. + * XXX: This doesn't hold true anymore since the header is now a copy. + * keeping the LSP in the dict if it is already present should be possible */ + dnode = dict_lookup(area->lspdb[level - 1], lsp->hdr.lsp_id); if (dnode) dnode_destroy(dict_delete(area->lspdb[level - 1], dnode)); if (lsp->own_lsp) { zlog_err( "ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP", - area->area_tag, - rawlspid_print(lsp->lsp_header->lsp_id)); + area->area_tag, rawlspid_print(lsp->hdr.lsp_id)); lsp_clear_data(lsp); lsp->own_lsp = 0; } - /* rebuild the lsp data */ - lsp_update_data(lsp, stream, area, level); + if (confusion) { + lsp_clear_data(lsp); + if (lsp->pdu != NULL) + stream_free(lsp->pdu); + lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); + lsp->age_out = ZERO_AGE_LIFETIME; + lsp->hdr.rem_lifetime = 0; + lsp_pack_pdu(lsp); + } else { + lsp_update_data(lsp, hdr, tlvs, stream, area, level); + } /* insert the lsp back into the database */ lsp_insert(lsp, area->lspdb[level - 1]); } /* creation of LSP directly from what we received */ -struct isis_lsp *lsp_new_from_stream_ptr(struct stream *stream, - u_int16_t pdu_len, - struct isis_lsp *lsp0, - struct isis_area *area, int level) +struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr, + struct isis_tlvs *tlvs, + struct stream *stream, struct isis_lsp *lsp0, + struct isis_area *area, int level) { struct isis_lsp *lsp; lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp)); - lsp_update_data(lsp, stream, area, level); + lsp_update_data(lsp, hdr, tlvs, stream, area, level); if (lsp0 == NULL) { /* @@ -576,8 +502,8 @@ struct isis_lsp *lsp_new_from_stream_ptr(struct stream *stream, } struct isis_lsp *lsp_new(struct isis_area *area, u_char *lsp_id, - u_int16_t rem_lifetime, u_int32_t seq_num, - u_int8_t lsp_bits, u_int16_t checksum, int level) + uint16_t rem_lifetime, uint32_t seqno, + uint8_t lsp_bits, uint16_t checksum, int level) { struct isis_lsp *lsp; @@ -587,44 +513,32 @@ struct isis_lsp *lsp_new(struct isis_area *area, u_char *lsp_id, lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); if (LSP_FRAGMENT(lsp_id) == 0) lsp->lspu.frags = list_new(); - lsp->isis_header = (struct isis_fixed_hdr *)(STREAM_DATA(lsp->pdu)); - lsp->lsp_header = (struct isis_link_state_hdr *)(STREAM_DATA(lsp->pdu) - + ISIS_FIXED_HDR_LEN); - /* at first we fill the FIXED HEADER */ - (level == IS_LEVEL_1) ? fill_fixed_hdr(lsp->isis_header, L1_LINK_STATE) - : fill_fixed_hdr(lsp->isis_header, L2_LINK_STATE); - - /* now for the LSP HEADER */ /* Minimal LSP PDU size */ - lsp->lsp_header->pdu_len = htons(ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); - memcpy(lsp->lsp_header->lsp_id, lsp_id, ISIS_SYS_ID_LEN + 2); - lsp->lsp_header->checksum = checksum; /* Provided in network order */ - lsp->lsp_header->seq_num = htonl(seq_num); - lsp->lsp_header->rem_lifetime = htons(rem_lifetime); - lsp->lsp_header->lsp_bits = lsp_bits; + lsp->hdr.pdu_len = ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN; + memcpy(lsp->hdr.lsp_id, lsp_id, sizeof(lsp->hdr.lsp_id)); + lsp->hdr.checksum = checksum; + lsp->hdr.seqno = seqno; + lsp->hdr.rem_lifetime = rem_lifetime; + lsp->hdr.lsp_bits = lsp_bits; lsp->level = level; lsp->age_out = ZERO_AGE_LIFETIME; - - stream_forward_endp(lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + put_lsp_hdr(lsp, NULL); if (isis->debugs & DEBUG_EVENTS) zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x", - sysid_print(lsp_id), - LSP_PSEUDO_ID(lsp->lsp_header->lsp_id), - LSP_FRAGMENT(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->pdu_len), - ntohl(lsp->lsp_header->seq_num)); + sysid_print(lsp_id), LSP_PSEUDO_ID(lsp->hdr.lsp_id), + LSP_FRAGMENT(lsp->hdr.lsp_id), lsp->hdr.pdu_len, + lsp->hdr.seqno); return lsp; } void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb) { - dict_alloc_insert(lspdb, lsp->lsp_header->lsp_id, lsp); - if (lsp->lsp_header->seq_num != 0) { + dict_alloc_insert(lspdb, lsp->hdr.lsp_id, lsp); + if (lsp->hdr.seqno) isis_spf_schedule(lsp->area, lsp->level); - } } /* @@ -643,14 +557,13 @@ void lsp_build_list_nonzero_ht(u_char *start_id, u_char *stop_id, curr = first; - if (((struct isis_lsp *)(curr->dict_data))->lsp_header->rem_lifetime) + if (((struct isis_lsp *)(curr->dict_data))->hdr.rem_lifetime) listnode_add(list, first->dict_data); while (curr) { curr = dict_next(lspdb, curr); if (curr - && ((struct isis_lsp *)(curr->dict_data)) - ->lsp_header->rem_lifetime) + && ((struct isis_lsp *)(curr->dict_data))->hdr.rem_lifetime) listnode_add(list, curr->dict_data); if (curr == last) break; @@ -659,77 +572,19 @@ void lsp_build_list_nonzero_ht(u_char *start_id, u_char *stop_id, return; } -/* - * Build a list of num_lsps LSPs bounded by start_id and stop_id. - */ -void lsp_build_list(u_char *start_id, u_char *stop_id, u_char num_lsps, - struct list *list, dict_t *lspdb) -{ - u_char count; - dnode_t *first, *last, *curr; - - first = dict_lower_bound(lspdb, start_id); - if (!first) - return; - - last = dict_upper_bound(lspdb, stop_id); - - curr = first; - - listnode_add(list, first->dict_data); - count = 1; - - while (curr) { - curr = dict_next(lspdb, curr); - if (curr) { - listnode_add(list, curr->dict_data); - count++; - } - if (count == num_lsps || curr == last) - break; - } - - return; -} - -/* - * Build a list of LSPs with SSN flag set for the given circuit - */ -void lsp_build_list_ssn(struct isis_circuit *circuit, u_char num_lsps, - struct list *list, dict_t *lspdb) -{ - dnode_t *dnode, *next; - struct isis_lsp *lsp; - u_char count = 0; - - dnode = dict_first(lspdb); - while (dnode != NULL) { - next = dict_next(lspdb, dnode); - lsp = dnode_get(dnode); - if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit)) { - listnode_add(list, lsp); - ++count; - } - if (count == num_lsps) - break; - dnode = next; - } - - return; -} - static void lsp_set_time(struct isis_lsp *lsp) { assert(lsp); - if (lsp->lsp_header->rem_lifetime == 0) { + if (lsp->hdr.rem_lifetime == 0) { if (lsp->age_out > 0) lsp->age_out--; return; } - lsp->lsp_header->rem_lifetime = - htons(ntohs(lsp->lsp_header->rem_lifetime) - 1); + lsp->hdr.rem_lifetime--; + if (lsp->pdu && stream_get_endp(lsp->pdu) >= 12) + stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime); } static void lspid_print(u_char *lsp_id, u_char *trg, char dynhost, char frag) @@ -743,7 +598,7 @@ static void lspid_print(u_char *lsp_id, u_char *trg, char dynhost, char frag) dyn = NULL; if (dyn) - sprintf((char *)id, "%.14s", dyn->name.name); + sprintf((char *)id, "%.14s", dyn->hostname); else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) sprintf((char *)id, "%.14s", unix_hostname()); else @@ -756,21 +611,21 @@ static void lspid_print(u_char *lsp_id, u_char *trg, char dynhost, char frag) } /* Convert the lsp attribute bits to attribute string */ -const char *lsp_bits2string(u_char *lsp_bits) +static const char *lsp_bits2string(uint8_t lsp_bits) { char *pos = lsp_bits_string; - if (!*lsp_bits) + if (!lsp_bits) return " none"; /* we only focus on the default metric */ pos += sprintf(pos, "%d/", - ISIS_MASK_LSP_ATT_DEFAULT_BIT(*lsp_bits) ? 1 : 0); + ISIS_MASK_LSP_ATT_DEFAULT_BIT(lsp_bits) ? 1 : 0); pos += sprintf(pos, "%d/", - ISIS_MASK_LSP_PARTITION_BIT(*lsp_bits) ? 1 : 0); + ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0); - pos += sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(*lsp_bits) ? 1 : 0); + pos += sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0); *(pos) = '\0'; @@ -783,276 +638,26 @@ void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost) u_char LSPid[255]; char age_out[8]; - lspid_print(lsp->lsp_header->lsp_id, LSPid, dynhost, 1); + lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1); vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); - vty_out(vty, "%5u ", ntohs(lsp->lsp_header->pdu_len)); - vty_out(vty, "0x%08x ", ntohl(lsp->lsp_header->seq_num)); - vty_out(vty, "0x%04x ", ntohs(lsp->lsp_header->checksum)); - if (ntohs(lsp->lsp_header->rem_lifetime) == 0) { - snprintf(age_out, 8, "(%u)", lsp->age_out); + vty_out(vty, "%5" PRIu16 " ", lsp->hdr.pdu_len); + vty_out(vty, "0x%08" PRIx32 " ", lsp->hdr.seqno); + vty_out(vty, "0x%04" PRIx16 " ", lsp->hdr.checksum); + if (lsp->hdr.rem_lifetime == 0) { + snprintf(age_out, 8, "(%d)", lsp->age_out); age_out[7] = '\0'; vty_out(vty, "%7s ", age_out); } else - vty_out(vty, " %5u ", ntohs(lsp->lsp_header->rem_lifetime)); - vty_out(vty, "%s\n", lsp_bits2string(&lsp->lsp_header->lsp_bits)); -} - -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 : %-8u IS-Extended : %s\n", - GET_TE_METRIC(neigh), lspid); - } else { - vty_out(vty, - " Metric : %-8u MT-Reach : %s %s\n", - GET_TE_METRIC(neigh), lspid, - isis_mtid2str(mtid)); - } - 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 : %-8" PRIu32 - " IPv6-Internal : %s/%d\n", - ntohl(ipv6_reach->metric), buff, - ipv6_reach->prefix_len); - else - vty_out(vty, " Metric : %-8" PRIu32 - " IPv6-External : %s/%d\n", - ntohl(ipv6_reach->metric), buff, - ipv6_reach->prefix_len); - } else { - if ((ipv6_reach->control_info & CTRL_INFO_DISTRIBUTION) - == DISTRIBUTION_INTERNAL) - vty_out(vty, " Metric : %-8" PRIu32 - " IPv6-MT-Int : %s/%d %s\n", - ntohl(ipv6_reach->metric), buff, - ipv6_reach->prefix_len, - isis_mtid2str(mtid)); - else - vty_out(vty, " Metric : %-8" PRIu32 - " IPv6-MT-Ext : %s/%d %s\n", - ntohl(ipv6_reach->metric), buff, - ipv6_reach->prefix_len, - isis_mtid2str(mtid)); - } - } -} - -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 : %-8" PRIu32 - " IPv4-Extended : %s/%d\n", - ntohl(te_ipv4_reach->te_metric), - inet_ntoa(newprefix2inaddr( - &te_ipv4_reach->prefix_start, - te_ipv4_reach->control)), - te_ipv4_reach->control & 0x3F); - } else { - /* FIXME: There should be better way to output this - * stuff. */ - vty_out(vty, " Metric : %-8" PRIu32 - " IPv4-MT : %s/%d %s\n", - 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_out(vty, " %5" PRIu16 " ", lsp->hdr.rem_lifetime); + vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits)); } void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) { - struct area_addr *area_addr; - int i; - struct listnode *lnode; - struct is_neigh *is_neigh; - struct ipv4_reachability *ipv4_reach; - struct in_addr *ipv4_addr; - 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]; - u_char ipv4_reach_mask[20]; - u_char ipv4_address[20]; - - lspid_print(lsp->lsp_header->lsp_id, LSPid, dynhost, 1); lsp_print(lsp, vty, dynhost); - - /* for all area address */ - if (lsp->tlv_data.area_addrs) - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.area_addrs, lnode, - area_addr)) { - vty_out(vty, " Area Address: %s\n", - isonet_print(area_addr->area_addr, - area_addr->addr_len)); - } - - /* for the nlpid tlv */ - if (lsp->tlv_data.nlpids) { - for (i = 0; i < lsp->tlv_data.nlpids->count; i++) { - switch (lsp->tlv_data.nlpids->nlpids[i]) { - case NLPID_IP: - case NLPID_IPV6: - vty_out(vty, " NLPID : 0x%X\n", - lsp->tlv_data.nlpids->nlpids[i]); - break; - default: - vty_out(vty, " NLPID : %s\n", "unknown"); - break; - } - } - } - - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.mt_router_info, lnode, - mt_router_info)) { - vty_out(vty, " MT : %s%s\n", - isis_mtid2str(mt_router_info->mtid), - mt_router_info->overload ? " (overload)" : ""); - } - - /* for the hostname tlv */ - if (lsp->tlv_data.hostname) { - bzero(hostname, sizeof(hostname)); - memcpy(hostname, lsp->tlv_data.hostname->name, - lsp->tlv_data.hostname->namelen); - vty_out(vty, " Hostname : %s\n", hostname); - } - - /* authentication tlv */ - if (lsp->tlv_data.auth_info.type != ISIS_PASSWD_TYPE_UNUSED) { - if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_HMAC_MD5) - vty_out(vty, " Auth type : md5\n"); - else if (lsp->tlv_data.auth_info.type - == ISIS_PASSWD_TYPE_CLEARTXT) - vty_out(vty, " Auth type : clear text\n"); - } - - /* TE router id */ - if (lsp->tlv_data.router_id) { - memcpy(ipv4_address, inet_ntoa(lsp->tlv_data.router_id->id), - sizeof(ipv4_address)); - vty_out(vty, " Router ID : %s\n", ipv4_address); - } - - if (lsp->tlv_data.ipv4_addrs) - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.ipv4_addrs, lnode, - ipv4_addr)) { - memcpy(ipv4_address, inet_ntoa(*ipv4_addr), - sizeof(ipv4_address)); - vty_out(vty, " IPv4 Address: %s\n", ipv4_address); - } - - /* for the IS neighbor tlv */ - if (lsp->tlv_data.is_neighs) - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.is_neighs, lnode, - is_neigh)) { - lspid_print(is_neigh->neigh_id, LSPid, dynhost, 0); - vty_out(vty, " Metric : %-8" PRIu8 - " IS : %s\n", - is_neigh->metrics.metric_default, LSPid); - } - - /* for the internal reachable tlv */ - if (lsp->tlv_data.ipv4_int_reachs) - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.ipv4_int_reachs, lnode, - ipv4_reach)) { - memcpy(ipv4_reach_prefix, inet_ntoa(ipv4_reach->prefix), - sizeof(ipv4_reach_prefix)); - memcpy(ipv4_reach_mask, inet_ntoa(ipv4_reach->mask), - sizeof(ipv4_reach_mask)); - vty_out(vty, " Metric : %-8" PRIu8 - " IPv4-Internal : %s %s\n", - ipv4_reach->metrics.metric_default, - ipv4_reach_prefix, ipv4_reach_mask); - } - - /* for the external reachable tlv */ - if (lsp->tlv_data.ipv4_ext_reachs) - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.ipv4_ext_reachs, lnode, - ipv4_reach)) { - memcpy(ipv4_reach_prefix, inet_ntoa(ipv4_reach->prefix), - sizeof(ipv4_reach_prefix)); - memcpy(ipv4_reach_mask, inet_ntoa(ipv4_reach->mask), - sizeof(ipv4_reach_mask)); - vty_out(vty, " Metric : %-8" PRIu8 - " IPv4-External : %s %s\n", - ipv4_reach->metrics.metric_default, - ipv4_reach_prefix, ipv4_reach_mask); - } - - /* IPv6 tlv */ - 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, 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 */ - 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); - + if (lsp->tlvs) + vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs)); vty_out(vty, "\n"); - - return; } /* print all the lsps info in the local lspdb */ @@ -1083,85 +688,6 @@ int 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) - -/* stream*, area->lsp_frag_threshold, increment */ -#define FRAG_NEEDED(S, T, I) \ - (STREAM_SIZE(S) - STREAM_REMAIN(S) + (I) > FRAG_THOLD(S, T)) - -/* FIXME: It shouldn't be necessary to pass tlvsize here, TLVs can have - * variable length (TE TLVs, sub TLVs). */ -static void lsp_tlv_fit(struct isis_lsp *lsp, struct list **from, - struct list **to, int tlvsize, int frag_thold, - int tlv_build_func(struct list *, struct stream *)) -{ - int count, i; - - /* can we fit all ? */ - if (!FRAG_NEEDED(lsp->pdu, frag_thold, - listcount(*from) * tlvsize + 2)) { - tlv_build_func(*from, lsp->pdu); - if (listcount(*to) != 0) { - struct listnode *node, *nextnode; - void *elem; - - 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 if (!FRAG_NEEDED(lsp->pdu, frag_thold, tlvsize + 2)) { - /* fit all we can */ - count = FRAG_THOLD(lsp->pdu, frag_thold) - 2 - - (STREAM_SIZE(lsp->pdu) - STREAM_REMAIN(lsp->pdu)); - count = count / tlvsize; - if (count > (int)listcount(*from)) - count = listcount(*from); - for (i = 0; i < count; i++) { - listnode_add(*to, listgetdata(listhead(*from))); - listnode_delete(*from, listgetdata(listhead(*from))); - } - tlv_build_func(*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) { u_int16_t rem_lifetime; @@ -1204,26 +730,85 @@ static u_int16_t lsp_refresh_time(struct isis_lsp *lsp, u_int16_t rem_lifetime) return refresh_time; } -static struct isis_lsp *lsp_next_frag(u_char frag_num, struct isis_lsp *lsp0, +static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, + struct isis_area *area) +{ + struct route_table *er_table = get_ext_reach(area, AF_INET, lsp->level); + if (!er_table) + return; + + for (struct route_node *rn = route_top(er_table); rn; + rn = route_next(rn)) { + if (!rn->info) + continue; + + struct prefix_ipv4 *ipv4 = (struct prefix_ipv4 *)&rn->p; + struct isis_ext_info *info = rn->info; + + uint32_t metric = info->metric; + if (metric > MAX_WIDE_PATH_METRIC) + metric = MAX_WIDE_PATH_METRIC; + if (area->oldmetric && metric > 0x3f) + metric = 0x3f; + + if (area->oldmetric) + isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, + metric); + if (area->newmetric) + isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, + metric); + } +} + +static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, + struct isis_area *area) +{ + struct route_table *er_table = + get_ext_reach(area, AF_INET6, lsp->level); + if (!er_table) + return; + + for (struct route_node *rn = route_top(er_table); rn; + rn = route_next(rn)) { + if (!rn->info) + continue; + + struct prefix_ipv6 *ipv6 = (struct prefix_ipv6 *)&rn->p; + struct isis_ext_info *info = rn->info; + + uint32_t metric = info->metric; + if (info->metric > MAX_WIDE_PATH_METRIC) + metric = MAX_WIDE_PATH_METRIC; + isis_tlvs_add_ipv6_reach( + lsp->tlvs, isis_area_ipv6_topology(area), ipv6, metric); + } +} + +static void lsp_build_ext_reach(struct isis_lsp *lsp, struct isis_area *area) +{ + lsp_build_ext_reach_ipv4(lsp, area); + lsp_build_ext_reach_ipv6(lsp, area); +} + +static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0, struct isis_area *area, int level) { struct isis_lsp *lsp; - u_char frag_id[ISIS_SYS_ID_LEN + 2]; + uint8_t frag_id[ISIS_SYS_ID_LEN + 2]; - memcpy(frag_id, lsp0->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 1); + memcpy(frag_id, lsp0->hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(frag_id) = frag_num; - /* FIXME add authentication TLV for fragment LSPs */ + lsp = lsp_search(frag_id, area->lspdb[level - 1]); if (lsp) { - /* Clear the TLVs */ lsp_clear_data(lsp); return lsp; } - lsp = lsp_new(area, frag_id, ntohs(lsp0->lsp_header->rem_lifetime), 0, + + lsp = lsp_new(area, frag_id, lsp0->hdr.rem_lifetime, 0, lsp_bits_generate(level, area->overload_bit, area->attached_bit), 0, level); - lsp->area = area; lsp->own_lsp = 1; lsp_insert(lsp, area->lspdb[level - 1]); listnode_add(lsp0->lspu.frags, lsp); @@ -1231,256 +816,75 @@ static struct isis_lsp *lsp_next_frag(u_char frag_num, struct isis_lsp *lsp0, return lsp; } -static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, - struct isis_area *area, - struct tlvs *tlv_data) -{ - struct route_table *er_table; - struct route_node *rn; - struct prefix_ipv4 *ipv4; - struct isis_ext_info *info; - struct ipv4_reachability *ipreach; - struct te_ipv4_reachability *te_ipreach; - - er_table = get_ext_reach(area, AF_INET, lsp->level); - if (!er_table) - return; - - for (rn = route_top(er_table); rn; rn = route_next(rn)) { - if (!rn->info) - continue; - - ipv4 = (struct prefix_ipv4 *)&rn->p; - info = rn->info; - if (area->oldmetric) { - if (tlv_data->ipv4_ext_reachs == NULL) { - tlv_data->ipv4_ext_reachs = list_new(); - tlv_data->ipv4_ext_reachs->del = free_tlv; - } - ipreach = XMALLOC(MTYPE_ISIS_TLV, sizeof(*ipreach)); - - ipreach->prefix.s_addr = ipv4->prefix.s_addr; - masklen2ip(ipv4->prefixlen, &ipreach->mask); - ipreach->prefix.s_addr &= ipreach->mask.s_addr; - - if ((info->metric & 0x3f) != info->metric) - ipreach->metrics.metric_default = 0x3f; - else - ipreach->metrics.metric_default = info->metric; - ipreach->metrics.metric_expense = METRICS_UNSUPPORTED; - ipreach->metrics.metric_error = METRICS_UNSUPPORTED; - ipreach->metrics.metric_delay = METRICS_UNSUPPORTED; - listnode_add(tlv_data->ipv4_ext_reachs, ipreach); - } - if (area->newmetric) { - if (tlv_data->te_ipv4_reachs == NULL) { - tlv_data->te_ipv4_reachs = list_new(); - tlv_data->te_ipv4_reachs->del = free_tlv; - } - te_ipreach = XCALLOC(MTYPE_ISIS_TLV, - sizeof(*te_ipreach) - 1 - + PSIZE(ipv4->prefixlen)); - if (info->metric > MAX_WIDE_PATH_METRIC) - te_ipreach->te_metric = - htonl(MAX_WIDE_PATH_METRIC); - else - te_ipreach->te_metric = htonl(info->metric); - te_ipreach->control = ipv4->prefixlen & 0x3f; - memcpy(&te_ipreach->prefix_start, &ipv4->prefix.s_addr, - PSIZE(ipv4->prefixlen)); - listnode_add(tlv_data->te_ipv4_reachs, te_ipreach); - } - } -} - -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 route_table *er_table; - struct route_node *rn; - 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) - return; - - for (rn = route_top(er_table); rn; rn = route_next(rn)) { - if (!rn->info) - continue; - - ipv6 = (struct prefix_ipv6 *)&rn->p; - info = rn->info; - - 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); - else - ip6reach->metric = htonl(info->metric); - ip6reach->control_info = DISTRIBUTION_EXTERNAL; - ip6reach->prefix_len = ipv6->prefixlen; - memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, - sizeof(ip6reach->prefix)); - listnode_add(reach_list, ip6reach); - } -} - -static void lsp_build_ext_reach(struct isis_lsp *lsp, struct isis_area *area, - struct tlvs *tlv_data) -{ - lsp_build_ext_reach_ipv4(lsp, area, tlv_data); - lsp_build_ext_reach_ipv6(lsp, area, tlv_data); -} - /* * Builds the LSP data part. This func creates a new frag whenever * area->lsp_frag_threshold is exceeded. */ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) { - struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; - struct listnode *node, *ipnode; int level = lsp->level; - struct isis_circuit *circuit; - struct prefix_ipv4 *ipv4; - struct ipv4_reachability *ipreach; - 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; - struct in_addr *routerid; - uint32_t expected = 0, found = 0; - uint32_t metric; - u_char zero_id[ISIS_SYS_ID_LEN + 1]; - int retval = ISIS_OK; - char buf[BUFSIZ]; + char buf[PREFIX2STR_BUFFER]; + struct listnode *node; + struct isis_lsp *frag; + lsp_clear_data(lsp); + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) + lsp_clear_data(frag); + + lsp->tlvs = isis_alloc_tlvs(); lsp_debug("ISIS (%s): Constructing local system LSP for level %d", area->area_tag, level); - /* - * Building the zero lsp - */ - memset(zero_id, 0, ISIS_SYS_ID_LEN + 1); + lsp->hdr.lsp_bits = lsp_bits_generate(level, area->overload_bit, + area->attached_bit); - /* Reset stream endp. Stream is always there and on every LSP refresh - * only - * TLV part of it is overwritten. So we must seek past header we will - * not - * touch. */ - stream_reset(lsp->pdu); - stream_forward_endp(lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + lsp_add_auth(lsp); - /* - * Add the authentication info if its present - */ - lsp_auth_add(lsp); - - /* - * First add the tlvs related to area - */ - - /* Area addresses */ - if (lsp->tlv_data.area_addrs == NULL) - lsp->tlv_data.area_addrs = list_new(); - list_add_list(lsp->tlv_data.area_addrs, area->area_addrs); - if (listcount(lsp->tlv_data.area_addrs) > 0) - tlv_add_area_addrs(lsp->tlv_data.area_addrs, lsp->pdu); + isis_tlvs_add_area_addresses(lsp->tlvs, area->area_addrs); /* Protocols Supported */ if (area->ip_circuits > 0 || area->ipv6_circuits > 0) { - lsp->tlv_data.nlpids = - XCALLOC(MTYPE_ISIS_TLV, sizeof(struct nlpids)); - lsp->tlv_data.nlpids->count = 0; + struct nlpids nlpids = {.count = 0}; if (area->ip_circuits > 0) { lsp_debug( "ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs", area->area_tag); - lsp->tlv_data.nlpids->count++; - lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP; + nlpids.nlpids[nlpids.count] = NLPID_IP; + nlpids.count++; } if (area->ipv6_circuits > 0) { lsp_debug( "ISIS (%s): Found IPv6 circuit, adding IPv6 to NLPIDs", area->area_tag); - lsp->tlv_data.nlpids->count++; - lsp->tlv_data.nlpids - ->nlpids[lsp->tlv_data.nlpids->count - 1] = - NLPID_IPV6; + nlpids.nlpids[nlpids.count] = NLPID_IPV6; + nlpids.count++; } - tlv_add_nlpid(lsp->tlv_data.nlpids, lsp->pdu); + isis_tlvs_set_protocols_supported(lsp->tlvs, &nlpids); } 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); + isis_tlvs_add_mt_router_info( + lsp->tlvs, mt_settings[i]->mtid, + mt_settings[i]->overload, false); lsp_debug("ISIS (%s): MT %s", area->area_tag, - isis_mtid2str(info->mtid)); + isis_mtid2str(mt_settings[i]->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) { - const char *hostname = unix_hostname(); - size_t hostname_len = strlen(hostname); - - lsp->tlv_data.hostname = - XMALLOC(MTYPE_ISIS_TLV, sizeof(struct hostname)); - - strncpy((char *)lsp->tlv_data.hostname->name, hostname, - sizeof(lsp->tlv_data.hostname->name)); - if (hostname_len <= MAX_TLV_LEN) - lsp->tlv_data.hostname->namelen = hostname_len; - else - lsp->tlv_data.hostname->namelen = MAX_TLV_LEN; - - lsp_debug("ISIS (%s): Adding dynamic hostname '%.*s'", - area->area_tag, lsp->tlv_data.hostname->namelen, - lsp->tlv_data.hostname->name); - tlv_add_dynamic_hostname(lsp->tlv_data.hostname, lsp->pdu); + isis_tlvs_set_dynamic_hostname(lsp->tlvs, unix_hostname()); + lsp_debug("ISIS (%s): Adding dynamic hostname '%s'", + area->area_tag, unix_hostname()); } else { lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag); @@ -1491,45 +895,31 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) * into * LSP and this address is same as router id. */ if (isis->router_id != 0) { - inet_ntop(AF_INET, &isis->router_id, buf, sizeof(buf)); + struct in_addr id = {.s_addr = isis->router_id}; + inet_ntop(AF_INET, &id, buf, sizeof(buf)); lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.", area->area_tag, buf); - if (lsp->tlv_data.ipv4_addrs == NULL) { - lsp->tlv_data.ipv4_addrs = list_new(); - lsp->tlv_data.ipv4_addrs->del = free_tlv; - } - - routerid = XMALLOC(MTYPE_ISIS_TLV, sizeof(struct in_addr)); - routerid->s_addr = isis->router_id; - listnode_add(lsp->tlv_data.ipv4_addrs, routerid); - tlv_add_in_addr(routerid, lsp->pdu, IPV4_ADDR); + isis_tlvs_add_ipv4_address(lsp->tlvs, &id); /* Exactly same data is put into TE router ID TLV, but only if * new style * TLV's are in use. */ if (area->newmetric) { + lsp_debug( "ISIS (%s): Adding router ID also as TE router ID tlv.", area->area_tag); - lsp->tlv_data.router_id = - XMALLOC(MTYPE_ISIS_TLV, sizeof(struct in_addr)); - lsp->tlv_data.router_id->id.s_addr = isis->router_id; - tlv_add_in_addr(&lsp->tlv_data.router_id->id, lsp->pdu, - TE_ROUTER_ID); + isis_tlvs_set_te_router_id(lsp->tlvs, &id); } } else { lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.", area->area_tag); } - memset(&tlv_data, 0, sizeof(struct tlvs)); - lsp_debug("ISIS (%s): Adding circuit specific information.", area->area_tag); - /* - * Then build lists of tlvs related to circuits - */ + struct isis_circuit *circuit; for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { if (!circuit->interface) lsp_debug( @@ -1549,245 +939,94 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) continue; } - /* - * Add IPv4 internal reachability of this circuit - */ + uint32_t metric = area->oldmetric + ? circuit->metric[level - 1] + : circuit->te_metric[level - 1]; + if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0) { lsp_debug( "ISIS (%s): Circuit has IPv4 active, adding respective TLVs.", area->area_tag); - if (area->oldmetric) { - if (tlv_data.ipv4_int_reachs == NULL) { - tlv_data.ipv4_int_reachs = list_new(); - tlv_data.ipv4_int_reachs->del = - free_tlv; - } - for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, - ipnode, ipv4)) { - ipreach = XMALLOC( - MTYPE_ISIS_TLV, - sizeof(struct - ipv4_reachability)); - ipreach->metrics.metric_default = - circuit->metric[level - 1]; - ipreach->metrics.metric_expense = - METRICS_UNSUPPORTED; - ipreach->metrics.metric_error = - METRICS_UNSUPPORTED; - ipreach->metrics.metric_delay = - METRICS_UNSUPPORTED; - masklen2ip(ipv4->prefixlen, - &ipreach->mask); - ipreach->prefix.s_addr = - ((ipreach->mask.s_addr) - & (ipv4->prefix.s_addr)); - inet_ntop(AF_INET, - &ipreach->prefix.s_addr, buf, - sizeof(buf)); + struct listnode *ipnode; + struct prefix_ipv4 *ipv4; + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode, + ipv4)) { + if (area->oldmetric) { lsp_debug( - "ISIS (%s): Adding old-style IP reachability for %s/%d", - area->area_tag, buf, - ipv4->prefixlen); - listnode_add(tlv_data.ipv4_int_reachs, - ipreach); + "ISIS (%s): Adding old-style IP reachability for %s", + area->area_tag, + prefix2str(ipv4, buf, + sizeof(buf))); + isis_tlvs_add_oldstyle_ip_reach( + lsp->tlvs, ipv4, metric); } - } - if (area->newmetric) { - if (tlv_data.te_ipv4_reachs == NULL) { - tlv_data.te_ipv4_reachs = list_new(); - tlv_data.te_ipv4_reachs->del = free_tlv; - } - for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, - ipnode, ipv4)) { - /* FIXME All this assumes that we have - * no sub TLVs. */ - te_ipreach = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct - te_ipv4_reachability) - + ((ipv4->prefixlen + 7) - / 8) - - 1); - if (area->oldmetric) - te_ipreach->te_metric = htonl( - circuit->metric[level - - 1]); - else - te_ipreach->te_metric = htonl( - circuit->te_metric - [level - 1]); - - te_ipreach->control = - (ipv4->prefixlen & 0x3F); - memcpy(&te_ipreach->prefix_start, - &ipv4->prefix.s_addr, - (ipv4->prefixlen + 7) / 8); - inet_ntop(AF_INET, &ipv4->prefix.s_addr, - buf, sizeof(buf)); + if (area->newmetric) { lsp_debug( - "ISIS (%s): Adding te-style IP reachability for %s/%d", - area->area_tag, buf, - ipv4->prefixlen); - listnode_add(tlv_data.te_ipv4_reachs, - te_ipreach); + "ISIS (%s): Adding te-style IP reachability for %s", + area->area_tag, + prefix2str(ipv4, buf, + sizeof(buf))); + isis_tlvs_add_extended_ip_reach( + lsp->tlvs, ipv4, metric); } } } - /* - * Add IPv6 reachability of this circuit - */ 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); - + struct listnode *ipnode; + struct prefix_ipv6 *ipv6; for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, ipnode, ipv6)) { - ip6reach = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct ipv6_reachability)); - - if (area->oldmetric) - ip6reach->metric = htonl( - circuit->metric[level - 1]); - else - ip6reach->metric = htonl( - circuit->te_metric[level - 1]); - - ip6reach->control_info = 0; - ip6reach->prefix_len = ipv6->prefixlen; - memcpy(&ip6prefix, ipv6, sizeof(ip6prefix)); - apply_mask_ipv6(&ip6prefix); - - inet_ntop(AF_INET6, &ip6prefix.prefix.s6_addr, - buf, sizeof(buf)); lsp_debug( - "ISIS (%s): Adding IPv6 reachability for %s/%d", - area->area_tag, buf, ipv6->prefixlen); - - memcpy(ip6reach->prefix, - ip6prefix.prefix.s6_addr, - sizeof(ip6reach->prefix)); - listnode_add(ipv6_reachs, ip6reach); + "ISIS (%s): Adding IPv6 reachability for %s", + area->area_tag, + prefix2str(ipv6, buf, sizeof(buf))); + isis_tlvs_add_ipv6_reach( + lsp->tlvs, + isis_area_ipv6_topology(area), ipv6, + metric); } } switch (circuit->circ_type) { case CIRCUIT_T_BROADCAST: if (level & circuit->is_type) { - if (area->oldmetric) { - if (tlv_data.is_neighs == NULL) { - tlv_data.is_neighs = list_new(); - tlv_data.is_neighs->del = - free_tlv; - } - is_neigh = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct is_neigh)); - if (level == IS_LEVEL_1) - memcpy(is_neigh->neigh_id, - circuit->u.bc - .l1_desig_is, - ISIS_SYS_ID_LEN + 1); - else - memcpy(is_neigh->neigh_id, - circuit->u.bc - .l2_desig_is, - ISIS_SYS_ID_LEN + 1); - is_neigh->metrics.metric_default = - circuit->metric[level - 1]; - is_neigh->metrics.metric_expense = - METRICS_UNSUPPORTED; - is_neigh->metrics.metric_error = - METRICS_UNSUPPORTED; - is_neigh->metrics.metric_delay = - METRICS_UNSUPPORTED; - if (!memcmp(is_neigh->neigh_id, zero_id, - ISIS_SYS_ID_LEN + 1)) { - XFREE(MTYPE_ISIS_TLV, is_neigh); - lsp_debug( - "ISIS (%s): No DIS for circuit, not adding old-style IS neighbor.", - area->area_tag); - } else { - listnode_add(tlv_data.is_neighs, - is_neigh); + uint8_t *ne_id = + (level == IS_LEVEL_1) + ? circuit->u.bc.l1_desig_is + : circuit->u.bc.l2_desig_is; + + if (LSP_PSEUDO_ID(ne_id)) { + if (area->oldmetric) { lsp_debug( "ISIS (%s): Adding DIS %s.%02x as old-style neighbor", area->area_tag, - sysid_print( - is_neigh->neigh_id), - LSP_PSEUDO_ID( - is_neigh->neigh_id)); + sysid_print(ne_id), + LSP_PSEUDO_ID(ne_id)); + isis_tlvs_add_oldstyle_reach( + lsp->tlvs, ne_id, + metric); } - } - if (area->newmetric) { - if (tlv_data.te_is_neighs == NULL) { - tlv_data.te_is_neighs = - list_new(); - tlv_data.te_is_neighs->del = - free_tlv; - } - te_is_neigh = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct te_is_neigh)); - if (level == IS_LEVEL_1) - memcpy(te_is_neigh->neigh_id, - circuit->u.bc - .l1_desig_is, - ISIS_SYS_ID_LEN + 1); - else - memcpy(te_is_neigh->neigh_id, - circuit->u.bc - .l2_desig_is, - ISIS_SYS_ID_LEN + 1); - if (area->oldmetric) - metric = circuit->metric[level - - 1]; - else - metric = - circuit->te_metric[level - - 1]; - SET_TE_METRIC(te_is_neigh, metric); - if (!memcmp(te_is_neigh->neigh_id, - zero_id, - ISIS_SYS_ID_LEN + 1)) { - XFREE(MTYPE_ISIS_TLV, - te_is_neigh); - lsp_debug( - "ISIS (%s): No DIS for circuit, not adding te-style IS neighbor.", - area->area_tag); - } else { - /* Check if MPLS_TE is activate - */ + if (area->newmetric) { + uint8_t subtlvs[256]; + uint8_t subtlv_len; + if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS( circuit->interface)) - /* Add SubTLVs & Adjust - * real size of SubTLVs - */ - te_is_neigh - ->sub_tlvs_length = add_te_subtlvs( - te_is_neigh - ->sub_tlvs, + subtlv_len = add_te_subtlvs( + subtlvs, circuit->mtc); else - /* Or keep only TE - * metric with no - * SubTLVs if MPLS_TE is - * off */ - te_is_neigh - ->sub_tlvs_length = - 0; + subtlv_len = 0; tlvs_add_mt_bcast( - &tlv_data, circuit, - level, te_is_neigh); - XFREE(MTYPE_ISIS_TLV, - te_is_neigh); + lsp->tlvs, circuit, + level, ne_id, metric, + subtlvs, subtlv_len); } } } else { @@ -1796,53 +1035,25 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) area->area_tag); } break; - case CIRCUIT_T_P2P: - nei = circuit->u.p2p.neighbor; + case CIRCUIT_T_P2P: { + struct isis_adjacency *nei = circuit->u.p2p.neighbor; if (nei && (level & nei->circuit_t)) { + uint8_t ne_id[7]; + memcpy(ne_id, nei->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(ne_id) = 0; + if (area->oldmetric) { - if (tlv_data.is_neighs == NULL) { - tlv_data.is_neighs = list_new(); - tlv_data.is_neighs->del = - free_tlv; - } - is_neigh = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct is_neigh)); - memcpy(is_neigh->neigh_id, nei->sysid, - ISIS_SYS_ID_LEN); - is_neigh->metrics.metric_default = - circuit->metric[level - 1]; - is_neigh->metrics.metric_expense = - METRICS_UNSUPPORTED; - is_neigh->metrics.metric_error = - METRICS_UNSUPPORTED; - is_neigh->metrics.metric_delay = - METRICS_UNSUPPORTED; - listnode_add(tlv_data.is_neighs, - is_neigh); lsp_debug( "ISIS (%s): Adding old-style is reach for %s", area->area_tag, - sysid_print( - is_neigh->neigh_id)); + sysid_print(ne_id)); + isis_tlvs_add_oldstyle_reach( + lsp->tlvs, ne_id, metric); } if (area->newmetric) { - uint32_t metric; + uint8_t subtlvs[256]; + uint8_t subtlv_len; - if (tlv_data.te_is_neighs == NULL) { - tlv_data.te_is_neighs = - list_new(); - tlv_data.te_is_neighs->del = - free_tlv; - } - te_is_neigh = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct te_is_neigh)); - memcpy(te_is_neigh->neigh_id, - nei->sysid, ISIS_SYS_ID_LEN); - metric = circuit->te_metric[level - 1]; - SET_TE_METRIC(te_is_neigh, metric); - /* Check if MPLS_TE is activate */ if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS( circuit->interface)) @@ -1861,28 +1072,24 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) /* Add SubTLVs & Adjust real * size of SubTLVs */ - te_is_neigh->sub_tlvs_length = - add_te_subtlvs( - te_is_neigh - ->sub_tlvs, - circuit->mtc); + subtlv_len = add_te_subtlvs( + subtlvs, circuit->mtc); else /* Or keep only TE metric with * no SubTLVs if MPLS_TE is off */ - te_is_neigh->sub_tlvs_length = - 0; + subtlv_len = 0; - tlvs_add_mt_p2p(&tlv_data, circuit, - te_is_neigh); - XFREE(MTYPE_ISIS_TLV, te_is_neigh); + tlvs_add_mt_p2p(lsp->tlvs, circuit, + ne_id, metric, subtlvs, + subtlv_len); } } else { lsp_debug( "ISIS (%s): No adjacency for given level on this circuit. Not adding IS neighbors", area->area_tag); } - break; + } break; case CIRCUIT_T_LOOPBACK: break; default: @@ -1890,169 +1097,36 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) } } - lsp_build_ext_reach(lsp, area, &tlv_data); + lsp_build_ext_reach(lsp, area); + struct isis_tlvs *tlvs = lsp->tlvs; + lsp->tlvs = NULL; + + lsp_pack_pdu(lsp); + size_t tlv_space = STREAM_WRITEABLE(lsp->pdu) - LLC_LEN; + lsp_clear_data(lsp); + + struct list *fragments = isis_fragment_tlvs(tlvs, tlv_space); + if (!fragments) { + zlog_warn("BUG: could not fragment own LSP:"); + log_multiline(LOG_WARNING, " ", "%s", isis_format_tlvs(tlvs)); + isis_free_tlvs(tlvs); + return; + } + isis_free_tlvs(tlvs); + + frag = lsp; + for (ALL_LIST_ELEMENTS_RO(fragments, node, tlvs)) { + if (node != listhead(fragments)) { + frag = lsp_next_frag(LSP_FRAGMENT(frag->hdr.lsp_id) + 1, + lsp, area, level); + } + frag->tlvs = tlvs; + } + + list_delete(fragments); lsp_debug("ISIS (%s): LSP construction is complete. Serializing...", area->area_tag); - - while (tlv_data.ipv4_int_reachs - && listcount(tlv_data.ipv4_int_reachs)) { - if (lsp->tlv_data.ipv4_int_reachs == NULL) - lsp->tlv_data.ipv4_int_reachs = list_new(); - lsp_tlv_fit(lsp, &tlv_data.ipv4_int_reachs, - &lsp->tlv_data.ipv4_int_reachs, IPV4_REACH_LEN, - area->lsp_frag_threshold, tlv_add_ipv4_int_reachs); - if (tlv_data.ipv4_int_reachs - && listcount(tlv_data.ipv4_int_reachs)) - lsp = lsp_next_frag( - LSP_FRAGMENT(lsp->lsp_header->lsp_id) + 1, lsp0, - area, level); - } - - while (tlv_data.ipv4_ext_reachs - && listcount(tlv_data.ipv4_ext_reachs)) { - if (lsp->tlv_data.ipv4_ext_reachs == NULL) - lsp->tlv_data.ipv4_ext_reachs = list_new(); - lsp_tlv_fit(lsp, &tlv_data.ipv4_ext_reachs, - &lsp->tlv_data.ipv4_ext_reachs, IPV4_REACH_LEN, - area->lsp_frag_threshold, tlv_add_ipv4_ext_reachs); - if (tlv_data.ipv4_ext_reachs - && listcount(tlv_data.ipv4_ext_reachs)) - lsp = lsp_next_frag( - LSP_FRAGMENT(lsp->lsp_header->lsp_id) + 1, lsp0, - area, level); - } - - 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, - 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, - 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) - lsp->tlv_data.is_neighs = list_new(); - lsp_tlv_fit(lsp, &tlv_data.is_neighs, &lsp->tlv_data.is_neighs, - IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, - tlv_add_is_neighs); - if (tlv_data.is_neighs && listcount(tlv_data.is_neighs)) - lsp = lsp_next_frag( - LSP_FRAGMENT(lsp->lsp_header->lsp_id) + 1, lsp0, - area, level); - } - - while (tlv_data.te_is_neighs && listcount(tlv_data.te_is_neighs)) { - 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, - 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); - - /* Validate the LSP */ - retval = parse_tlvs(area->area_tag, - STREAM_DATA(lsp->pdu) + ISIS_FIXED_HDR_LEN - + ISIS_LSP_HDR_LEN, - stream_get_endp(lsp->pdu) - ISIS_FIXED_HDR_LEN - - ISIS_LSP_HDR_LEN, - &expected, &found, &tlv_data, NULL); - assert(retval == ISIS_OK); - return; } @@ -2076,8 +1150,8 @@ int lsp_generate(struct isis_area *area, int level) oldlsp = lsp_search(lspid, area->lspdb[level - 1]); if (oldlsp) { /* FIXME: we should actually initiate a purge */ - seq_num = ntohl(oldlsp->lsp_header->seq_num); - lsp_search_and_destroy(oldlsp->lsp_header->lsp_id, + seq_num = oldlsp->hdr.seqno; + lsp_search_and_destroy(oldlsp->hdr.lsp_id, area->lspdb[level - 1]); } rem_lifetime = lsp_rem_lifetime(area, level); @@ -2092,7 +1166,7 @@ int lsp_generate(struct isis_area *area, int level) /* build_lsp_data (newlsp, area); */ lsp_build(newlsp, area); /* time to calculate our checksum */ - lsp_seqnum_update(newlsp); + lsp_seqno_update(newlsp); newlsp->last_generated = time(NULL); lsp_set_all_srmflags(newlsp); @@ -2108,15 +1182,14 @@ int lsp_generate(struct isis_area *area, int level) &area->t_lsp_refresh[level - 1]); if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug( - "ISIS-Upd (%s): Building L%d LSP %s, len %d, " - "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us", - area->area_tag, level, - rawlspid_print(newlsp->lsp_header->lsp_id), - ntohl(newlsp->lsp_header->pdu_len), - ntohl(newlsp->lsp_header->seq_num), - ntohs(newlsp->lsp_header->checksum), - ntohs(newlsp->lsp_header->rem_lifetime), refresh_time); + zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %" PRIu16 + ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 + ", lifetime %" PRIu16 "s refresh %" PRIu16 "s", + area->area_tag, level, + rawlspid_print(newlsp->hdr.lsp_id), + newlsp->hdr.pdu_len, newlsp->hdr.seqno, + newlsp->hdr.checksum, newlsp->hdr.rem_lifetime, + refresh_time); } sched_debug( "ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.", @@ -2154,24 +1227,21 @@ static int lsp_regenerate(struct isis_area *area, int level) lsp_clear_data(lsp); lsp_build(lsp, area); - lsp->lsp_header->lsp_bits = lsp_bits_generate(level, area->overload_bit, - area->attached_bit); rem_lifetime = lsp_rem_lifetime(area, level); - lsp->lsp_header->rem_lifetime = htons(rem_lifetime); - lsp_seqnum_update(lsp); - + lsp->hdr.rem_lifetime = rem_lifetime; lsp->last_generated = time(NULL); lsp_set_all_srmflags(lsp); for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { - frag->lsp_header->lsp_bits = lsp_bits_generate( + frag->hdr.lsp_bits = lsp_bits_generate( level, area->overload_bit, area->attached_bit); /* Set the lifetime values of all the fragments to the same * value, * so that no fragment expires before the lsp is refreshed. */ - frag->lsp_header->rem_lifetime = htons(rem_lifetime); + frag->hdr.rem_lifetime = rem_lifetime; lsp_set_all_srmflags(frag); } + lsp_seqno_update(lsp); refresh_time = lsp_refresh_time(lsp, rem_lifetime); if (level == IS_LEVEL_1) @@ -2184,14 +1254,12 @@ static int lsp_regenerate(struct isis_area *area, int level) if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Refreshing our L%d LSP %s, len %d, " - "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us", - area->area_tag, level, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->pdu_len), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime), refresh_time); + "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %" PRIu16 + ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 + ", lifetime %" PRIu16 "s refresh %" PRIu16 "s", + area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id), + lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime, refresh_time); } sched_debug( "ISIS (%s): Rebuilt L%d LSP. Set triggered regenerate to non-pending.", @@ -2351,164 +1419,89 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, int level) { struct isis_adjacency *adj; - struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; - struct es_neigh *es_neigh; struct list *adj_list; struct listnode *node; struct isis_area *area = circuit->area; + lsp_clear_data(lsp); + lsp->tlvs = isis_alloc_tlvs(); lsp_debug( "ISIS (%s): Constructing pseudo LSP %s for interface %s level %d", - area->area_tag, rawlspid_print(lsp->lsp_header->lsp_id), + area->area_tag, rawlspid_print(lsp->hdr.lsp_id), circuit->interface->name, level); lsp->level = level; /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ - lsp->lsp_header->lsp_bits = + lsp->hdr.lsp_bits = lsp_bits_generate(level, 0, circuit->area->attached_bit); /* * add self to IS neighbours */ - if (circuit->area->oldmetric) { - if (lsp->tlv_data.is_neighs == NULL) { - lsp->tlv_data.is_neighs = list_new(); - lsp->tlv_data.is_neighs->del = free_tlv; - } - is_neigh = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct is_neigh)); + uint8_t ne_id[ISIS_SYS_ID_LEN + 1]; - memcpy(&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); - listnode_add(lsp->tlv_data.is_neighs, is_neigh); + memcpy(ne_id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(ne_id) = 0; + + if (circuit->area->oldmetric) { + isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0); lsp_debug( "ISIS (%s): Adding %s.%02x as old-style neighbor (self)", - area->area_tag, sysid_print(is_neigh->neigh_id), - LSP_PSEUDO_ID(is_neigh->neigh_id)); + area->area_tag, sysid_print(ne_id), + LSP_PSEUDO_ID(ne_id)); } if (circuit->area->newmetric) { - if (lsp->tlv_data.te_is_neighs == NULL) { - lsp->tlv_data.te_is_neighs = list_new(); - lsp->tlv_data.te_is_neighs->del = free_tlv; - } - te_is_neigh = - XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh)); - - memcpy(&te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); - listnode_add(lsp->tlv_data.te_is_neighs, te_is_neigh); + isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, + ne_id, 0, NULL, 0); lsp_debug( "ISIS (%s): Adding %s.%02x as te-style neighbor (self)", - area->area_tag, sysid_print(te_is_neigh->neigh_id), - LSP_PSEUDO_ID(te_is_neigh->neigh_id)); + area->area_tag, sysid_print(ne_id), + LSP_PSEUDO_ID(ne_id)); } adj_list = list_new(); isis_adj_build_up_list(circuit->u.bc.adjdb[level - 1], adj_list); for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) { - if (adj->level & level) { - if ((level == IS_LEVEL_1 - && adj->sys_type == ISIS_SYSTYPE_L1_IS) - || (level == IS_LEVEL_1 - && adj->sys_type == ISIS_SYSTYPE_L2_IS - && adj->adj_usage == ISIS_ADJ_LEVEL1AND2) - || (level == IS_LEVEL_2 - && adj->sys_type == ISIS_SYSTYPE_L2_IS)) { - /* an IS neighbour -> add it */ - if (circuit->area->oldmetric) { - is_neigh = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct is_neigh)); - - memcpy(&is_neigh->neigh_id, adj->sysid, - ISIS_SYS_ID_LEN); - listnode_add(lsp->tlv_data.is_neighs, - is_neigh); - lsp_debug( - "ISIS (%s): Adding %s.%02x as old-style neighbor (peer)", - area->area_tag, - sysid_print(is_neigh->neigh_id), - LSP_PSEUDO_ID( - is_neigh->neigh_id)); - } - if (circuit->area->newmetric) { - te_is_neigh = XCALLOC( - MTYPE_ISIS_TLV, - sizeof(struct te_is_neigh)); - memcpy(&te_is_neigh->neigh_id, - adj->sysid, ISIS_SYS_ID_LEN); - listnode_add(lsp->tlv_data.te_is_neighs, - te_is_neigh); - lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", - area->area_tag, - sysid_print( - te_is_neigh->neigh_id), - LSP_PSEUDO_ID( - te_is_neigh->neigh_id)); - } - } else if (level == IS_LEVEL_1 - && adj->sys_type == ISIS_SYSTYPE_ES) { - /* an ES neigbour add it, if we are building - * level 1 LSP */ - /* FIXME: the tlv-format is hard to use here */ - if (lsp->tlv_data.es_neighs == NULL) { - lsp->tlv_data.es_neighs = list_new(); - lsp->tlv_data.es_neighs->del = free_tlv; - } - es_neigh = XCALLOC(MTYPE_ISIS_TLV, - sizeof(struct es_neigh)); - - memcpy(&es_neigh->first_es_neigh, adj->sysid, - ISIS_SYS_ID_LEN); - listnode_add(lsp->tlv_data.es_neighs, es_neigh); - lsp_debug( - "ISIS (%s): Adding %s as ES neighbor (peer)", - area->area_tag, - sysid_print(es_neigh->first_es_neigh)); - } else { - lsp_debug( - "ISIS (%s): Ignoring neighbor %s, level does not match", - area->area_tag, - sysid_print(adj->sysid)); - } - } else { + if (!(adj->level & level)) { lsp_debug( "ISIS (%s): Ignoring neighbor %s, level does not intersect", area->area_tag, sysid_print(adj->sysid)); + continue; + } + + if (!(level == IS_LEVEL_1 + && adj->sys_type == ISIS_SYSTYPE_L1_IS) + && !(level == IS_LEVEL_1 + && adj->sys_type == ISIS_SYSTYPE_L2_IS + && adj->adj_usage == ISIS_ADJ_LEVEL1AND2) + && !(level == IS_LEVEL_2 + && adj->sys_type == ISIS_SYSTYPE_L2_IS)) { + lsp_debug( + "ISIS (%s): Ignoring neighbor %s, level does not match", + area->area_tag, sysid_print(adj->sysid)); + continue; + } + + memcpy(ne_id, adj->sysid, ISIS_SYS_ID_LEN); + if (circuit->area->oldmetric) { + isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0); + lsp_debug( + "ISIS (%s): Adding %s.%02x as old-style neighbor (peer)", + area->area_tag, sysid_print(ne_id), + LSP_PSEUDO_ID(ne_id)); + } + if (circuit->area->newmetric) { + isis_tlvs_add_extended_reach(lsp->tlvs, + ISIS_MT_IPV4_UNICAST, + ne_id, 0, NULL, 0); + lsp_debug( + "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", + area->area_tag, sysid_print(ne_id), + LSP_PSEUDO_ID(ne_id)); } } list_delete(adj_list); - - lsp_debug("ISIS (%s): Pseudo LSP construction is complete.", - area->area_tag); - - /* Reset endp of stream to overwrite only TLV part of it. */ - stream_reset(lsp->pdu); - stream_forward_endp(lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); - - /* - * Add the authentication info if it's present - */ - lsp_auth_add(lsp); - - if (lsp->tlv_data.is_neighs && listcount(lsp->tlv_data.is_neighs) > 0) - 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, - 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); - - lsp->lsp_header->pdu_len = htons(stream_get_endp(lsp->pdu)); - - /* Recompute authentication and checksum information */ - lsp_auth_update(lsp); - fletcher_checksum(STREAM_DATA(lsp->pdu) + 12, - ntohs(lsp->lsp_header->pdu_len) - 12, 12); - return; } @@ -2543,7 +1536,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) lsp->area = circuit->area; lsp_build_pseudo(lsp, circuit, level); - + lsp_pack_pdu(lsp); lsp->own_lsp = 1; lsp_insert(lsp, lspdb); lsp_set_all_srmflags(lsp); @@ -2562,14 +1555,13 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Building L%d Pseudo LSP %s, len %d, " - "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us", + "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %" PRIu16 + ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 + ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s", circuit->area->area_tag, level, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->pdu_len), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime), refresh_time); + rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, + lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime, refresh_time); } return ISIS_OK; @@ -2599,16 +1591,11 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) rawlspid_print(lsp_id)); return ISIS_ERROR; } - lsp_clear_data(lsp); - lsp_build_pseudo(lsp, circuit, level); - - /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ - lsp->lsp_header->lsp_bits = - lsp_bits_generate(level, 0, circuit->area->attached_bit); rem_lifetime = lsp_rem_lifetime(circuit->area, level); - lsp->lsp_header->rem_lifetime = htons(rem_lifetime); - lsp_inc_seqnum(lsp, 0); + lsp->hdr.rem_lifetime = rem_lifetime; + lsp_build_pseudo(lsp, circuit, level); + lsp_inc_seqno(lsp, 0); lsp->last_generated = time(NULL); lsp_set_all_srmflags(lsp); @@ -2624,14 +1611,13 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): Refreshing L%d Pseudo LSP %s, len %d, " - "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us", + "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %" PRIu16 + ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16 + ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s", circuit->area->area_tag, level, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->pdu_len), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime), refresh_time); + rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len, + lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime, refresh_time); } return ISIS_OK; @@ -2828,8 +1814,7 @@ int lsp_tick(struct thread *thread) * when * the first time rem_lifetime becomes 0. */ - rem_lifetime = - ntohs(lsp->lsp_header->rem_lifetime); + rem_lifetime = lsp->hdr.rem_lifetime; lsp_set_time(lsp); /* @@ -2839,8 +1824,7 @@ int lsp_tick(struct thread *thread) * time. * ISO 10589 - 7.3.16.4 first paragraph. */ - if (rem_lifetime == 1 - && lsp->lsp_header->seq_num != 0) { + if (rem_lifetime == 1 && lsp->hdr.seqno != 0) { /* 7.3.16.4 a) set SRM flags on all */ lsp_set_all_srmflags(lsp); /* 7.3.16.4 b) retain only the header @@ -2857,13 +1841,11 @@ int lsp_tick(struct thread *thread) if (lsp->age_out == 0) { zlog_debug( - "ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", + "ISIS-Upd (%s): L%u LSP %s seq " + "0x%08" PRIx32 " aged out", area->area_tag, lsp->level, - rawlspid_print( - lsp->lsp_header - ->lsp_id), - ntohl(lsp->lsp_header - ->seq_num)); + rawlspid_print(lsp->hdr.lsp_id), + lsp->hdr.seqno); lsp_destroy(lsp); lsp = NULL; dict_delete_free(area->lspdb[level], @@ -2924,51 +1906,19 @@ int lsp_tick(struct thread *thread) void lsp_purge_pseudo(u_char *id, struct isis_circuit *circuit, int level) { struct isis_lsp *lsp; - u_int16_t seq_num; - u_int8_t lsp_bits; lsp = lsp_search(id, circuit->area->lspdb[level - 1]); if (!lsp) return; - /* store old values */ - seq_num = lsp->lsp_header->seq_num; - lsp_bits = lsp->lsp_header->lsp_bits; - - /* reset stream */ - lsp_clear_data(lsp); - stream_reset(lsp->pdu); - - /* update header */ - lsp->lsp_header->pdu_len = htons(ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); - memcpy(lsp->lsp_header->lsp_id, id, ISIS_SYS_ID_LEN + 2); - lsp->lsp_header->checksum = 0; - lsp->lsp_header->seq_num = seq_num; - lsp->lsp_header->rem_lifetime = 0; - lsp->lsp_header->lsp_bits = lsp_bits; - lsp->level = level; - lsp->age_out = lsp->area->max_lsp_lifetime[level - 1]; - stream_forward_endp(lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); - - /* - * Add and update the authentication info if its present - */ - lsp_auth_add(lsp); - lsp->lsp_header->pdu_len = htons(stream_get_endp(lsp->pdu)); - lsp_auth_update(lsp); - fletcher_checksum(STREAM_DATA(lsp->pdu) + 12, - ntohs(lsp->lsp_header->pdu_len) - 12, 12); - - lsp_set_all_srmflags(lsp); - - return; + lsp_purge(lsp, level); } /* * Purge own LSP that is received and we don't have. * -> Do as in 7.3.16.4 */ -void lsp_purge_non_exist(int level, struct isis_link_state_hdr *lsp_hdr, +void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, struct isis_area *area) { struct isis_lsp *lsp; @@ -2980,39 +1930,14 @@ void lsp_purge_non_exist(int level, struct isis_link_state_hdr *lsp_hdr, lsp->area = area; lsp->level = level; lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); - lsp->isis_header = (struct isis_fixed_hdr *)STREAM_DATA(lsp->pdu); - fill_fixed_hdr(lsp->isis_header, - (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE - : L2_LINK_STATE); - lsp->lsp_header = (struct isis_link_state_hdr *)(STREAM_DATA(lsp->pdu) - + ISIS_FIXED_HDR_LEN); - memcpy(lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN); - stream_forward_endp(lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + lsp->age_out = ZERO_AGE_LIFETIME; - /* - * Set the remaining lifetime to 0 - */ - lsp->lsp_header->rem_lifetime = 0; + memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr)); + lsp->hdr.rem_lifetime = 0; - /* - * Add and update the authentication info if its present - */ - lsp_auth_add(lsp); - lsp_auth_update(lsp); + lsp_pack_pdu(lsp); - /* - * Update the PDU length to header plus any authentication TLV. - */ - lsp->lsp_header->pdu_len = htons(stream_get_endp(lsp->pdu)); - - /* - * Put the lsp into LSPdb - */ lsp_insert(lsp, area->lspdb[lsp->level - 1]); - - /* - * Send in to whole area - */ lsp_set_all_srmflags(lsp); return; diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 7bec162719..0f9c749949 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -24,19 +24,19 @@ #ifndef _ZEBRA_ISIS_LSP_H #define _ZEBRA_ISIS_LSP_H +#include "isisd/isis_pdu.h" + /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability * sake it should better be avoided */ struct isis_lsp { - struct isis_fixed_hdr *isis_header; /* normally equals pdu */ - struct isis_link_state_hdr *lsp_header; /* pdu + isis_header_len */ - struct stream *pdu; /* full pdu lsp */ + struct isis_lsp_hdr hdr; + struct stream *pdu; /* full pdu lsp */ union { struct list *frags; struct isis_lsp *zero_lsp; } lspu; - u_int32_t auth_tlv_offset; /* authentication TLV position in the pdu */ u_int32_t SRMflags[ISIS_MAX_CIRCUITS]; u_int32_t SSNflags[ISIS_MAX_CIRCUITS]; int level; /* L1 or L2? */ @@ -47,7 +47,7 @@ struct isis_lsp { /* used for 60 second counting when rem_lifetime is zero */ int age_out; struct isis_area *area; - struct tlvs tlv_data; /* Simplifies TLV access */ + struct isis_tlvs *tlvs; }; dict_t *lsp_db_init(void); @@ -59,13 +59,13 @@ int lsp_regenerate_schedule(struct isis_area *area, int level, int all_pseudo); int lsp_generate_pseudo(struct isis_circuit *circuit, int level); int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level); -struct isis_lsp *lsp_new(struct isis_area *area, u_char *lsp_id, - u_int16_t rem_lifetime, u_int32_t seq_num, - u_int8_t lsp_bits, u_int16_t checksum, int level); -struct isis_lsp *lsp_new_from_stream_ptr(struct stream *stream, - u_int16_t pdu_len, - struct isis_lsp *lsp0, - struct isis_area *area, int level); +struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id, + uint16_t rem_lifetime, uint32_t seq_num, + uint8_t lsp_bits, uint16_t checksum, int level); +struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr, + struct isis_tlvs *tlvs, + struct stream *stream, struct isis_lsp *lsp0, + struct isis_area *area, int level); void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb); struct isis_lsp *lsp_search(u_char *id, dict_t *lspdb); @@ -73,12 +73,9 @@ void lsp_build_list(u_char *start_id, u_char *stop_id, u_char num_lsps, struct list *list, dict_t *lspdb); void lsp_build_list_nonzero_ht(u_char *start_id, u_char *stop_id, struct list *list, dict_t *lspdb); -void lsp_build_list_ssn(struct isis_circuit *circuit, u_char num_lsps, - struct list *list, dict_t *lspdb); - void lsp_search_and_destroy(u_char *id, dict_t *lspdb); void lsp_purge_pseudo(u_char *id, struct isis_circuit *circuit, int level); -void lsp_purge_non_exist(int level, struct isis_link_state_hdr *lsp_hdr, +void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr, struct isis_area *area); #define LSP_EQUAL 1 @@ -92,16 +89,15 @@ void lsp_purge_non_exist(int level, struct isis_link_state_hdr *lsp_hdr, (I)[ISIS_SYS_ID_LEN] = 0; \ (I)[ISIS_SYS_ID_LEN + 1] = 0 int lsp_id_cmp(u_char *id1, u_char *id2); -int lsp_compare(char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, - u_int16_t checksum, u_int16_t rem_lifetime); -void lsp_update(struct isis_lsp *lsp, struct stream *stream, - struct isis_area *area, int level); -void lsp_inc_seqnum(struct isis_lsp *lsp, u_int32_t seq_num); +int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, + uint16_t checksum, uint16_t rem_lifetime); +void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, + struct isis_tlvs *tlvs, struct stream *stream, + struct isis_area *area, int level, bool confusion); +void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost); 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 *); - /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 463e3abcf3..40ceb99fb2 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -52,7 +52,6 @@ #include "isisd/isis_route.h" #include "isisd/isis_routemap.h" #include "isisd/isis_zebra.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_te.h" /* Default configuration file name */ diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c index 4ad26cf91f..7d1ad6b049 100644 --- a/isisd/isis_memory.c +++ b/isisd/isis_memory.c @@ -31,9 +31,9 @@ DEFINE_MTYPE(ISISD, ISIS_TMP, "ISIS TMP") DEFINE_MTYPE(ISISD, ISIS_CIRCUIT, "ISIS circuit") DEFINE_MTYPE(ISISD, ISIS_LSP, "ISIS LSP") DEFINE_MTYPE(ISISD, ISIS_ADJACENCY, "ISIS adjacency") +DEFINE_MTYPE(ISISD, ISIS_ADJACENCY_INFO, "ISIS adjacency info") DEFINE_MTYPE(ISISD, ISIS_AREA, "ISIS area") DEFINE_MTYPE(ISISD, ISIS_AREA_ADDR, "ISIS area address") -DEFINE_MTYPE(ISISD, ISIS_TLV, "ISIS TLV") DEFINE_MTYPE(ISISD, ISIS_DYNHN, "ISIS dyn hostname") DEFINE_MTYPE(ISISD, ISIS_SPFTREE, "ISIS SPFtree") DEFINE_MTYPE(ISISD, ISIS_VERTEX, "ISIS vertex") diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h index 7729ebac33..4078c7a671 100644 --- a/isisd/isis_memory.h +++ b/isisd/isis_memory.h @@ -30,9 +30,9 @@ DECLARE_MTYPE(ISIS_TMP) DECLARE_MTYPE(ISIS_CIRCUIT) DECLARE_MTYPE(ISIS_LSP) DECLARE_MTYPE(ISIS_ADJACENCY) +DECLARE_MTYPE(ISIS_ADJACENCY_INFO) DECLARE_MTYPE(ISIS_AREA) DECLARE_MTYPE(ISIS_AREA_ADDR) -DECLARE_MTYPE(ISIS_TLV) DECLARE_MTYPE(ISIS_DYNHN) DECLARE_MTYPE(ISIS_SPFTREE) DECLARE_MTYPE(ISIS_VERTEX) diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 16c789ff59..4d7b4c381a 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -28,6 +28,7 @@ #include "hash.h" #include "if.h" #include "command.h" +#include "log_int.h" #include "isisd/dict.h" #include "isisd/isis_constants.h" @@ -38,7 +39,6 @@ #include "isisd/isisd.h" #include "isisd/isis_misc.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_constants.h" #include "isisd/isis_adjacency.h" @@ -46,15 +46,9 @@ /* staticly assigned vars for printing purposes */ struct in_addr new_prefix; -/* len of xxxx.xxxx.xxxx + place for #0 termination */ -char sysid[15]; -/* len of xxxx.xxxx.xxxx + place for #0 termination */ -char snpa[15]; /* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ -char isonet[51]; /* + place for #0 termination */ -/* len of xxxx.xxxx.xxxx.xx.xx + place for #0 termination */ -char lspid[21]; +char isonet[51]; /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ char datestring[20]; char nlpidstring[30]; @@ -179,6 +173,26 @@ int sysid2buff(u_char *buff, const char *dotted) return len; } +const char *nlpid2str(uint8_t nlpid) +{ + static char buf[4]; + switch (nlpid) { + case NLPID_IP: + return "IPv4"; + case NLPID_IPV6: + return "IPv6"; + case NLPID_SNAP: + return "SNAP"; + case NLPID_CLNP: + return "CLNP"; + case NLPID_ESIS: + return "ES-IS"; + default: + snprintf(buf, sizeof(buf), "%" PRIu8, nlpid); + return buf; + } +} + /* * converts the nlpids struct (filled by TLV #129) * into a string @@ -190,26 +204,7 @@ char *nlpid2string(struct nlpids *nlpids) int i; for (i = 0; i < nlpids->count; i++) { - switch (nlpids->nlpids[i]) { - case NLPID_IP: - pos += sprintf(pos, "IPv4"); - break; - case NLPID_IPV6: - pos += sprintf(pos, "IPv6"); - break; - case NLPID_SNAP: - pos += sprintf(pos, "SNAP"); - break; - case NLPID_CLNP: - pos += sprintf(pos, "CLNP"); - break; - case NLPID_ESIS: - pos += sprintf(pos, "ES-IS"); - break; - default: - pos += sprintf(pos, "unknown"); - break; - } + pos += sprintf(pos, "%s", nlpid2str(nlpids->nlpids[i])); if (nlpids->count - i > 1) pos += sprintf(pos, ", "); } @@ -219,25 +214,6 @@ char *nlpid2string(struct nlpids *nlpids) return nlpidstring; } -/* - * supports the given af ? - */ -int speaks(struct nlpids *nlpids, int family) -{ - int i, speaks = 0; - - if (nlpids == (struct nlpids *)NULL) - return speaks; - for (i = 0; i < nlpids->count; i++) { - if (family == AF_INET && nlpids->nlpids[i] == NLPID_IP) - speaks = 1; - if (family == AF_INET6 && nlpids->nlpids[i] == NLPID_IPV6) - speaks = 1; - } - - return speaks; -} - /* * Returns 0 on error, IS-IS Circuit Type on ok */ @@ -330,71 +306,53 @@ const char *syst2string(int type) */ const char *snpa_print(const u_char *from) { - int i = 0; - u_char *pos = (u_char *)snpa; - - if (!from) - return "unknown"; - - while (i < ETH_ALEN - 1) { - if (i & 1) { - sprintf((char *)pos, "%02x.", *(from + i)); - pos += 3; - } else { - sprintf((char *)pos, "%02x", *(from + i)); - pos += 2; - } - i++; - } - - sprintf((char *)pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1))); - pos += 2; - *(pos) = '\0'; - - return snpa; + return isis_format_id(from, ISIS_SYS_ID_LEN); } const char *sysid_print(const u_char *from) { - int i = 0; - char *pos = sysid; - - if (!from) - return "unknown"; - - while (i < ISIS_SYS_ID_LEN - 1) { - if (i & 1) { - sprintf(pos, "%02x.", *(from + i)); - pos += 3; - } else { - sprintf(pos, "%02x", *(from + i)); - pos += 2; - } - i++; - } - - sprintf(pos, "%02x", *(from + (ISIS_SYS_ID_LEN - 1))); - pos += 2; - *(pos) = '\0'; - - return sysid; + return isis_format_id(from, ISIS_SYS_ID_LEN); } const char *rawlspid_print(const u_char *from) { - char *pos = lspid; - if (!from) - return "unknown"; - memcpy(pos, sysid_print(from), 15); - pos += 14; - sprintf(pos, ".%02x", LSP_PSEUDO_ID(from)); - pos += 3; - sprintf(pos, "-%02x", LSP_FRAGMENT(from)); - pos += 3; + return isis_format_id(from, 8); +} - *(pos) = '\0'; +#define FORMAT_ID_SIZE sizeof("0000.0000.0000.00-00") +const char *isis_format_id(const uint8_t *id, size_t len) +{ +#define FORMAT_BUF_COUNT 4 + static char buf_ring[FORMAT_BUF_COUNT][FORMAT_ID_SIZE]; + static size_t cur_buf = 0; - return lspid; + char *rv; + + cur_buf++; + if (cur_buf >= FORMAT_BUF_COUNT) + cur_buf = 0; + + rv = buf_ring[cur_buf]; + + if (!id) { + snprintf(rv, FORMAT_ID_SIZE, "unknown"); + return rv; + } + + if (len < 6) { + snprintf(rv, FORMAT_ID_SIZE, "Short ID"); + return rv; + } + + snprintf(rv, FORMAT_ID_SIZE, "%02x%02x.%02x%02x.%02x%02x", id[0], id[1], + id[2], id[3], id[4], id[5]); + + if (len > 6) + snprintf(rv + 14, FORMAT_ID_SIZE - 14, ".%02x", id[6]); + if (len > 7) + snprintf(rv + 17, FORMAT_ID_SIZE - 17, "-%02x", id[7]); + + return rv; } const char *time2string(u_int32_t time) @@ -508,7 +466,7 @@ const char *print_sys_hostname(const u_char *sysid) dyn = dynhn_find_by_id(sysid); if (dyn) - return (const char *)dyn->name.name; + return dyn->hostname; return sysid_print(sysid); } @@ -572,3 +530,74 @@ void zlog_dump_data(void *data, int len) zlog_debug("[%8.8s] %-50.50s %s", addrstr, hexstr, charstr); return; } + +static char *qasprintf(const char *format, va_list ap) +{ + va_list aq; + va_copy(aq, ap); + + int size = 0; + char *p = NULL; + + size = vsnprintf(p, size, format, ap); + + if (size < 0) { + va_end(aq); + return NULL; + } + + size++; + p = XMALLOC(MTYPE_TMP, size); + + size = vsnprintf(p, size, format, aq); + va_end(aq); + + if (size < 0) { + XFREE(MTYPE_TMP, p); + return NULL; + } + + return p; +} + +void log_multiline(int priority, const char *prefix, const char *format, ...) +{ + va_list ap; + char *p; + + va_start(ap, format); + p = qasprintf(format, ap); + va_end(ap); + + if (!p) + return; + + char *saveptr = NULL; + for (char *line = strtok_r(p, "\n", &saveptr); line; + line = strtok_r(NULL, "\n", &saveptr)) { + zlog(priority, "%s%s", prefix, line); + } + + XFREE(MTYPE_TMP, p); +} + +void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) +{ + va_list ap; + char *p; + + va_start(ap, format); + p = qasprintf(format, ap); + va_end(ap); + + if (!p) + return; + + char *saveptr = NULL; + for (char *line = strtok_r(p, "\n", &saveptr); line; + line = strtok_r(NULL, "\n", &saveptr)) { + vty_out(vty, "%s%s\n", prefix, line); + } + + XFREE(MTYPE_TMP, p); +} diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index 12ab0fac1c..7de534ec7b 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -44,7 +44,9 @@ const char *isonet_print(const u_char *, int len); const char *sysid_print(const u_char *); const char *snpa_print(const u_char *); const char *rawlspid_print(const u_char *); +const char *isis_format_id(const uint8_t *id, size_t len); const char *time2string(u_int32_t); +const char *nlpid2str(uint8_t nlpid); /* typedef struct nlpids nlpids; */ char *nlpid2string(struct nlpids *); const char *print_sys_hostname(const u_char *sysid); @@ -53,7 +55,6 @@ void zlog_dump_data(void *data, int len); /* * misc functions */ -int speaks(struct nlpids *nlpids, int family); unsigned long isis_jitter(unsigned long timer, unsigned long jitter); const char *unix_hostname(void); @@ -77,4 +78,10 @@ enum { ISIS_UI_LEVEL_BRIEF, ISIS_UI_LEVEL_EXTENSIVE, }; +#include "lib/log.h" +void log_multiline(int priority, const char *prefix, const char *format, ...) + PRINTF_ATTRIBUTE(3, 4); +struct vty; +void vty_multiline(struct vty *vty, const char *prefix, const char *format, ...) + PRINTF_ATTRIBUTE(3, 4); #endif diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index 46b57510ac..52646c2624 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -24,19 +24,14 @@ #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" +#include "isisd/isis_tlvs.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) { @@ -367,7 +362,7 @@ static void adj_mt_set(struct isis_adjacency *adj, unsigned int index, adj->mt_set[index] = mtid; } -bool tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable, +bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable, struct isis_adjacency *adj) { struct isis_circuit_mt_setting **mt_settings; @@ -388,17 +383,20 @@ bool tlvs_to_adj_mt_set(struct tlvs *tlvs, bool v4_usable, bool v6_usable, 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) { + if (!tlvs->mt_router_info.count + && !tlvs->mt_router_info_empty) { /* Other end does not have MT enabled */ if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST && v4_usable) 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)) { + struct isis_mt_router_info *info_head; + + info_head = (struct isis_mt_router_info *) + tlvs->mt_router_info.head; + for (struct isis_mt_router_info *info = info_head; info; + info = info->next) { if (mt_settings[i]->mtid == info->mtid) { bool usable; switch (info->mtid) { @@ -456,153 +454,6 @@ void adj_mt_finish(struct isis_adjacency *adj) 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) { @@ -647,51 +498,46 @@ static uint16_t *circuit_bcast_mt_set(struct isis_circuit *circuit, int level, return rv; } -static void tlvs_add_mt_set(struct isis_area *area, struct tlvs *tlvs, +static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, unsigned int mt_count, uint16_t *mt_set, - struct te_is_neigh *neigh) + uint8_t *id, uint32_t metric, uint8_t *subtlvs, + uint8_t subtlv_len) { 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)); + area->area_tag, sysid_print(id), + LSP_PSEUDO_ID(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)); + area->area_tag, sysid_print(id), + LSP_PSEUDO_ID(id), isis_mtid2str(mtid)); } + isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, subtlvs, + subtlv_len); } } -void tlvs_add_mt_bcast(struct tlvs *tlvs, struct isis_circuit *circuit, - int level, struct te_is_neigh *neigh) +void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit, + int level, uint8_t *id, uint32_t metric, + uint8_t *subtlvs, uint8_t subtlv_len) { 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); + tlvs_add_mt_set(circuit->area, tlvs, mt_count, mt_set, id, metric, + subtlvs, subtlv_len); } -void tlvs_add_mt_p2p(struct tlvs *tlvs, struct isis_circuit *circuit, - struct te_is_neigh *neigh) +void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit, + uint8_t *id, uint32_t metric, uint8_t *subtlvs, + uint8_t subtlv_len) { struct isis_adjacency *adj = circuit->u.p2p.neighbor; - tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, neigh); + tlvs_add_mt_set(circuit->area, tlvs, adj->mt_count, adj->mt_set, id, + metric, subtlvs, subtlv_len); } diff --git a/isisd/isis_mt.h b/isisd/isis_mt.h index eec089228e..95aa99dba0 100644 --- a/isisd/isis_mt.h +++ b/isisd/isis_mt.h @@ -24,6 +24,7 @@ #define ISIS_MT_MASK 0x0fff #define ISIS_MT_OL_MASK 0x8000 +#define ISIS_MT_AT_MASK 0x4000 #define ISIS_MT_IPV4_UNICAST 0 #define ISIS_MT_IPV4_MGMT 1 @@ -64,21 +65,6 @@ struct isis_circuit_mt_setting { 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); @@ -87,27 +73,10 @@ struct isis_area; struct isis_circuit; struct tlvs; struct te_is_neigh; +struct isis_tlvs; 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, @@ -137,12 +106,14 @@ 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, +bool tlvs_to_adj_mt_set(struct isis_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); +void tlvs_add_mt_bcast(struct isis_tlvs *tlvs, struct isis_circuit *circuit, + int level, uint8_t *id, uint32_t metric, + uint8_t *subtlvs, uint8_t subtlv_len); +void tlvs_add_mt_p2p(struct isis_tlvs *tlvs, struct isis_circuit *circuit, + uint8_t *id, uint32_t metric, uint8_t *subtlvs, + uint8_t subtlv_len); #endif diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index a3706179a6..17ef8935d8 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -44,7 +44,6 @@ #include "isisd/isis_network.h" #include "isisd/isis_misc.h" #include "isisd/isis_dr.h" -#include "isisd/isis_tlv.h" #include "isisd/isisd.h" #include "isisd/isis_dynhn.h" #include "isisd/isis_lsp.h" @@ -54,497 +53,122 @@ #include "isisd/isis_events.h" #include "isisd/isis_te.h" #include "isisd/isis_mt.h" +#include "isisd/isis_tlvs.h" -#define ISIS_MINIMUM_FIXED_HDR_LEN 15 -#define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */ - -#ifndef PNBBY -#define PNBBY 8 -#endif /* PNBBY */ - -/* - * HELPER FUNCS - */ - -/* - * Compares two sets of area addresses - */ -static int area_match(struct list *left, struct list *right) +static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, + int level) { - struct area_addr *addr1, *addr2; - struct listnode *node1, *node2; + unsigned long lenp; + int retval; + u_int16_t length; + uint8_t pdu_type = + (level == IS_LEVEL_1) ? L1_PARTIAL_SEQ_NUM : L2_PARTIAL_SEQ_NUM; - for (ALL_LIST_ELEMENTS_RO(left, node1, addr1)) { - for (ALL_LIST_ELEMENTS_RO(right, node2, addr2)) { - if (addr1->addr_len == addr2->addr_len - && !memcmp(addr1->area_addr, addr2->area_addr, - (int)addr1->addr_len)) - return 1; /* match */ - } - } + isis_circuit_stream(circuit, &circuit->snd_stream); - return 0; /* mismatch */ -} + fill_fixed_hdr(pdu_type, circuit->snd_stream); -/* - * Checks whether we should accept a PDU of given level - */ -static int accept_level(int level, int circuit_t) -{ - int retval = ((circuit_t & level) == level); /* simple approach */ + lenp = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); /* PDU length */ + stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_putc(circuit->snd_stream, circuit->idx); + stream_putc(circuit->snd_stream, 9); /* code */ + stream_putc(circuit->snd_stream, 16); /* len */ + + stream_putw(circuit->snd_stream, hdr->rem_lifetime); + stream_put(circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2); + stream_putl(circuit->snd_stream, hdr->seqno); + stream_putw(circuit->snd_stream, hdr->checksum); + + length = (u_int16_t)stream_get_endp(circuit->snd_stream); + /* Update PDU length */ + stream_putw_at(circuit->snd_stream, lenp, length); + + retval = circuit->tx(circuit, level); + if (retval != ISIS_OK) + zlog_err("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed", + circuit->area->area_tag, level, + circuit->interface->name); return retval; } -/* - * Verify authentication information - * Support cleartext and HMAC MD5 authentication - */ -static int authentication_check(struct isis_passwd *remote, - struct isis_passwd *local, - struct stream *stream, uint32_t auth_tlv_offset) -{ - unsigned char digest[ISIS_AUTH_MD5_SIZE]; - - /* Auth fail () - passwd type mismatch */ - if (local->type != remote->type) - return ISIS_ERROR; - - switch (local->type) { - /* No authentication required */ - case ISIS_PASSWD_TYPE_UNUSED: - break; - - /* Cleartext (ISO 10589) */ - case ISIS_PASSWD_TYPE_CLEARTXT: - /* Auth fail () - passwd len mismatch */ - if (remote->len != local->len) - return ISIS_ERROR; - return memcmp(local->passwd, remote->passwd, local->len); - - /* HMAC MD5 (RFC 3567) */ - case ISIS_PASSWD_TYPE_HMAC_MD5: - /* Auth fail () - passwd len mismatch */ - if (remote->len != ISIS_AUTH_MD5_SIZE) - return ISIS_ERROR; - /* Set the authentication value to 0 before the check */ - memset(STREAM_DATA(stream) + auth_tlv_offset + 3, 0, - ISIS_AUTH_MD5_SIZE); - /* Compute the digest */ - hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), - (unsigned char *)&(local->passwd), local->len, - (unsigned char *)&digest); - /* Copy back the authentication value after the check */ - memcpy(STREAM_DATA(stream) + auth_tlv_offset + 3, - remote->passwd, ISIS_AUTH_MD5_SIZE); - return memcmp(digest, remote->passwd, ISIS_AUTH_MD5_SIZE); - - default: - zlog_err("Unsupported authentication type"); - return ISIS_ERROR; - } - - /* Authentication pass when no authentication is configured */ - return ISIS_OK; -} - -static int lsp_authentication_check(struct stream *stream, - struct isis_area *area, int level, - struct isis_passwd *passwd) -{ - struct isis_link_state_hdr *hdr; - uint32_t expected = 0, found = 0, auth_tlv_offset = 0; - uint16_t checksum, rem_lifetime, pdu_len; - struct tlvs tlvs; - int retval = ISIS_OK; - - hdr = (struct isis_link_state_hdr *)(STREAM_PNT(stream)); - pdu_len = ntohs(hdr->pdu_len); - expected |= TLVFLAG_AUTH_INFO; - auth_tlv_offset = stream_get_getp(stream) + ISIS_LSP_HDR_LEN; - retval = parse_tlvs(area->area_tag, - STREAM_PNT(stream) + ISIS_LSP_HDR_LEN, - pdu_len - ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, - &expected, &found, &tlvs, &auth_tlv_offset); - - if (retval != ISIS_OK) { - zlog_err( - "ISIS-Upd (%s): Parse failed L%d LSP %s, seq 0x%08x, " - "cksum 0x%04x, lifetime %us, len %u", - area->area_tag, level, rawlspid_print(hdr->lsp_id), - ntohl(hdr->seq_num), ntohs(hdr->checksum), - ntohs(hdr->rem_lifetime), pdu_len); - if ((isis->debugs & DEBUG_UPDATE_PACKETS) - && (isis->debugs & DEBUG_PACKET_DUMP)) - zlog_dump_data(STREAM_DATA(stream), - stream_get_endp(stream)); - return retval; - } - - if (!(found & TLVFLAG_AUTH_INFO)) { - zlog_err("No authentication tlv in LSP"); - return ISIS_ERROR; - } - - if (tlvs.auth_info.type != ISIS_PASSWD_TYPE_CLEARTXT - && tlvs.auth_info.type != ISIS_PASSWD_TYPE_HMAC_MD5) { - zlog_err("Unknown authentication type in LSP"); - return ISIS_ERROR; - } - - /* - * RFC 5304 set checksum and remaining lifetime to zero before - * verification and reset to old values after verification. - */ - checksum = hdr->checksum; - rem_lifetime = hdr->rem_lifetime; - hdr->checksum = 0; - hdr->rem_lifetime = 0; - retval = authentication_check(&tlvs.auth_info, passwd, stream, - auth_tlv_offset); - hdr->checksum = checksum; - hdr->rem_lifetime = rem_lifetime; - - return retval; -} - -/* - * Processing helper functions - */ -static void del_addr(void *val) -{ - XFREE(MTYPE_ISIS_TMP, val); -} - -static void tlvs_to_adj_area_addrs(struct tlvs *tlvs, - struct isis_adjacency *adj) -{ - struct listnode *node; - struct area_addr *area_addr, *malloced; - - if (adj->area_addrs) { - adj->area_addrs->del = del_addr; - list_delete(adj->area_addrs); - } - adj->area_addrs = list_new(); - if (tlvs->area_addrs) { - for (ALL_LIST_ELEMENTS_RO(tlvs->area_addrs, node, area_addr)) { - malloced = XMALLOC(MTYPE_ISIS_TMP, - sizeof(struct area_addr)); - memcpy(malloced, area_addr, sizeof(struct area_addr)); - listnode_add(adj->area_addrs, malloced); - } - } -} - -static int tlvs_to_adj_nlpids(struct tlvs *tlvs, struct isis_adjacency *adj) -{ - int i; - struct nlpids *tlv_nlpids; - - if (tlvs->nlpids) { - - tlv_nlpids = tlvs->nlpids; - if (tlv_nlpids->count > array_size(adj->nlpids.nlpids)) - return 1; - - adj->nlpids.count = tlv_nlpids->count; - - for (i = 0; i < tlv_nlpids->count; i++) { - adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i]; - } - } - return 0; -} - -static void tlvs_to_adj_ipv4_addrs(struct tlvs *tlvs, - struct isis_adjacency *adj) -{ - struct listnode *node; - struct in_addr *ipv4_addr, *malloced; - - if (adj->ipv4_addrs) { - adj->ipv4_addrs->del = del_addr; - list_delete(adj->ipv4_addrs); - } - adj->ipv4_addrs = list_new(); - if (tlvs->ipv4_addrs) { - for (ALL_LIST_ELEMENTS_RO(tlvs->ipv4_addrs, node, ipv4_addr)) { - malloced = - XMALLOC(MTYPE_ISIS_TMP, sizeof(struct in_addr)); - memcpy(malloced, ipv4_addr, sizeof(struct in_addr)); - listnode_add(adj->ipv4_addrs, malloced); - } - } -} - -static void tlvs_to_adj_ipv6_addrs(struct tlvs *tlvs, - struct isis_adjacency *adj) -{ - struct listnode *node; - struct in6_addr *ipv6_addr, *malloced; - - if (adj->ipv6_addrs) { - adj->ipv6_addrs->del = del_addr; - list_delete(adj->ipv6_addrs); - } - adj->ipv6_addrs = list_new(); - if (tlvs->ipv6_addrs) { - for (ALL_LIST_ELEMENTS_RO(tlvs->ipv6_addrs, node, ipv6_addr)) { - malloced = XMALLOC(MTYPE_ISIS_TMP, - sizeof(struct in6_addr)); - memcpy(malloced, ipv6_addr, sizeof(struct in6_addr)); - listnode_add(adj->ipv6_addrs, malloced); - } - } -} - /* * RECEIVE SIDE */ -/* - * Process P2P IIH - * ISO - 10589 - * Section 8.2.5 - Receiving point-to-point IIH PDUs - * - */ -static int process_p2p_hello(struct isis_circuit *circuit) -{ - int retval = ISIS_OK; - struct isis_p2p_hello_hdr *hdr; - struct isis_adjacency *adj; - u_int32_t expected = 0, found = 0, auth_tlv_offset = 0; +struct iih_info { + struct isis_circuit *circuit; + u_char *ssnpa; + int level; + + uint8_t circ_type; + uint8_t sys_id[ISIS_SYS_ID_LEN]; + uint16_t holdtime; uint16_t pdu_len; - struct tlvs tlvs; - int v4_usable = 0, v6_usable = 0; - if (isis->debugs & DEBUG_ADJ_PACKETS) { - zlog_debug( - "ISIS-Adj (%s): Rcvd P2P IIH on %s, cirType %s, cirID %u", - circuit->area->area_tag, circuit->interface->name, - circuit_t2string(circuit->is_type), - circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) - zlog_dump_data(STREAM_DATA(circuit->rcv_stream), - stream_get_endp(circuit->rcv_stream)); - } + uint8_t circuit_id; - if (circuit->circ_type != CIRCUIT_T_P2P) { - zlog_warn("p2p hello on non p2p circuit"); - return ISIS_WARNING; - } + uint8_t priority; + uint8_t dis[ISIS_SYS_ID_LEN + 1]; - if ((stream_get_endp(circuit->rcv_stream) - - stream_get_getp(circuit->rcv_stream)) - < ISIS_P2PHELLO_HDRLEN) { - zlog_warn("Packet too short"); - return ISIS_WARNING; - } + bool v4_usable; + bool v6_usable; - /* 8.2.5.1 PDU acceptance tests */ - - /* 8.2.5.1 a) external domain untrue */ - /* FIXME: not useful at all? */ - - /* 8.2.5.1 b) ID Length mismatch */ - /* checked at the handle_pdu */ - - /* 8.2.5.2 IIH PDU Processing */ - - /* 8.2.5.2 a) 1) Maximum Area Addresses */ - /* Already checked, and can also be ommited */ - - /* - * Get the header - */ - hdr = (struct isis_p2p_hello_hdr *)STREAM_PNT(circuit->rcv_stream); - pdu_len = ntohs(hdr->pdu_len); - - if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_P2PHELLO_HDRLEN) - || pdu_len > ISO_MTU(circuit) - || pdu_len > stream_get_endp(circuit->rcv_stream)) { - zlog_warn( - "ISIS-Adj (%s): Rcvd P2P IIH from (%s) with " - "invalid pdu length %d", - circuit->area->area_tag, circuit->interface->name, - pdu_len); - return ISIS_WARNING; - } - - /* - * Set the stream endp to PDU length, ignoring additional padding - * introduced by transport chips. - */ - if (pdu_len < stream_get_endp(circuit->rcv_stream)) - stream_set_endp(circuit->rcv_stream, pdu_len); - - stream_forward_getp(circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN); - - /* - * Lets get the TLVS now - */ - expected |= TLVFLAG_AREA_ADDRS; - expected |= TLVFLAG_AUTH_INFO; - 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, - STREAM_PNT(circuit->rcv_stream), - pdu_len - ISIS_P2PHELLO_HDRLEN - ISIS_FIXED_HDR_LEN, - &expected, &found, &tlvs, &auth_tlv_offset); - - if (retval > ISIS_WARNING) { - zlog_warn("parse_tlvs() failed"); - free_tlvs(&tlvs); - return retval; - }; - - if (!(found & TLVFLAG_AREA_ADDRS)) { - zlog_warn("No Area addresses TLV in P2P IS to IS hello"); - free_tlvs(&tlvs); - return ISIS_WARNING; - } - - if (!(found & TLVFLAG_NLPID)) { - zlog_warn("No supported protocols TLV in P2P IS to IS hello"); - free_tlvs(&tlvs); - return ISIS_WARNING; - } - - /* 8.2.5.1 c) Authentication */ - if (circuit->passwd.type) { - if (!(found & TLVFLAG_AUTH_INFO) - || authentication_check(&tlvs.auth_info, &circuit->passwd, - circuit->rcv_stream, - auth_tlv_offset)) { - isis_event_auth_failure( - circuit->area->area_tag, - "P2P hello authentication failure", - hdr->source_id); - free_tlvs(&tlvs); - return ISIS_OK; - } - } - - /* - * check if both ends have an IPv4 address - */ - if (circuit->ip_addrs && listcount(circuit->ip_addrs) && tlvs.ipv4_addrs - && listcount(tlvs.ipv4_addrs)) { - v4_usable = 1; - } - - if (found & TLVFLAG_IPV6_ADDR) { - /* TBA: check that we have a linklocal ourselves? */ - struct listnode *node; - struct in6_addr *ip; - for (ALL_LIST_ELEMENTS_RO(tlvs.ipv6_addrs, node, ip)) - if (IN6_IS_ADDR_LINKLOCAL(ip)) { - v6_usable = 1; - break; - } - - if (!v6_usable) - zlog_warn( - "ISIS-Adj: IPv6 addresses present but no link-local " - "in P2P IIH from %s\n", - circuit->interface->name); - } - - if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) - zlog_warn( - "ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n", - circuit->interface->name); - - if (!v6_usable && !v4_usable) { - free_tlvs(&tlvs); - return ISIS_WARNING; - } - - /* - * it's own p2p IIH PDU - discard - */ - if (!memcmp(hdr->source_id, isis->sysid, ISIS_SYS_ID_LEN)) { - zlog_warn("ISIS-Adj (%s): it's own IIH PDU - discarded", - circuit->area->area_tag); - free_tlvs(&tlvs); - return ISIS_WARNING; - } + struct isis_tlvs *tlvs; +}; +static int process_p2p_hello(struct iih_info *iih) +{ /* * My interpertation of the ISO, if no adj exists we will create one for * the circuit */ - adj = circuit->u.p2p.neighbor; + struct isis_adjacency *adj = iih->circuit->u.p2p.neighbor; /* If an adjacency exists, check it is with the source of the hello * packets */ if (adj) { - if (memcmp(hdr->source_id, adj->sysid, ISIS_SYS_ID_LEN)) { + if (memcmp(iih->sys_id, adj->sysid, ISIS_SYS_ID_LEN)) { zlog_debug( "hello source and adjacency do not match, set adj down\n"); isis_adj_state_change(adj, ISIS_ADJ_DOWN, "adj do not exist"); - return 0; + return ISIS_OK; } } - if (!adj || adj->level != hdr->circuit_t) { + if (!adj || adj->level != iih->circ_type) { if (!adj) { - adj = isis_new_adj(hdr->source_id, NULL, hdr->circuit_t, - circuit); - if (adj == NULL) - return ISIS_ERROR; + adj = isis_new_adj(iih->sys_id, NULL, iih->circ_type, + iih->circuit); } else { - adj->level = hdr->circuit_t; + adj->level = iih->circ_type; } - circuit->u.p2p.neighbor = adj; + iih->circuit->u.p2p.neighbor = adj; /* Build lsp with the new neighbor entry when a new * adjacency is formed. Set adjacency circuit type to * IIH PDU header circuit type before lsp is regenerated * when an adjacency is up. This will result in the new * adjacency entry getting added to the lsp tlv neighbor list. */ - adj->circuit_t = hdr->circuit_t; + adj->circuit_t = iih->circ_type; isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, NULL); adj->sys_type = ISIS_SYSTYPE_UNKNOWN; } /* 8.2.6 Monitoring point-to-point adjacencies */ - adj->hold_time = ntohs(hdr->hold_time); + adj->hold_time = iih->holdtime; adj->last_upd = time(NULL); - /* we do this now because the adj may not survive till the end... */ - tlvs_to_adj_area_addrs(&tlvs, adj); - - /* which protocol are spoken ??? */ - if (tlvs_to_adj_nlpids(&tlvs, adj)) { - free_tlvs(&tlvs); - return ISIS_WARNING; - } - - /* we need to copy addresses to the adj */ - if (found & TLVFLAG_IPV4_ADDR) - tlvs_to_adj_ipv4_addrs(&tlvs, adj); + bool changed; + isis_tlvs_to_adj(iih->tlvs, adj, &changed); + changed |= tlvs_to_adj_mt_set(iih->tlvs, iih->v4_usable, iih->v6_usable, + adj); /* Update MPLS TE Remote IP address parameter if possible */ - if (IS_MPLS_TE(isisMplsTE) && circuit->mtc - && IS_CIRCUIT_TE(circuit->mtc)) - if (adj->ipv4_addrs != NULL - && listcount(adj->ipv4_addrs) != 0) { - struct in_addr *ip_addr; - ip_addr = (struct in_addr *)listgetdata( - (struct listnode *)listhead(adj->ipv4_addrs)); - set_circuitparams_rmt_ipaddr(circuit->mtc, *ip_addr); - } - - 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); + if (IS_MPLS_TE(isisMplsTE) && iih->circuit->mtc + && IS_CIRCUIT_TE(iih->circuit->mtc) && adj->ipv4_address_count) + set_circuitparams_rmt_ipaddr(iih->circuit->mtc, + adj->ipv4_addresses[0]); /* lets take care of the expiry */ THREAD_TIMER_OFF(adj->t_expire); @@ -552,10 +176,11 @@ static int process_p2p_hello(struct isis_circuit *circuit) &adj->t_expire); /* 8.2.5.2 a) a match was detected */ - if (area_match(circuit->area->area_addrs, tlvs.area_addrs)) { + if (isis_tlvs_area_addresses_match(iih->tlvs, + iih->circuit->area->area_addrs)) { /* 8.2.5.2 a) 2) If the system is L1 - table 5 */ - if (circuit->area->is_type == IS_LEVEL_1) { - switch (hdr->circuit_t) { + if (iih->circuit->area->is_type == IS_LEVEL_1) { + switch (iih->circ_type) { case IS_LEVEL_1: case IS_LEVEL_1_AND_2: if (adj->adj_state != ISIS_ADJ_UP) { @@ -573,8 +198,7 @@ static int process_p2p_hello(struct isis_circuit *circuit) /* (7) reject - wrong system type event */ zlog_warn("wrongSystemType"); - free_tlvs(&tlvs); - return ISIS_WARNING; /* Reject */ + return ISIS_WARNING; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (6) down - wrong system */ isis_adj_state_change(adj, @@ -586,8 +210,8 @@ static int process_p2p_hello(struct isis_circuit *circuit) } /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */ - if (circuit->area->is_type == IS_LEVEL_1_AND_2) { - switch (hdr->circuit_t) { + if (iih->circuit->area->is_type == IS_LEVEL_1_AND_2) { + switch (iih->circ_type) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP) { /* (6) adj state up */ @@ -648,15 +272,14 @@ static int process_p2p_hello(struct isis_circuit *circuit) } /* 8.2.5.2 a) 4) If the system is L2 - table 7 */ - if (circuit->area->is_type == IS_LEVEL_2) { - switch (hdr->circuit_t) { + if (iih->circuit->area->is_type == IS_LEVEL_2) { + switch (iih->circ_type) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP) { /* (5) reject - wrong system type event */ zlog_warn("wrongSystemType"); - free_tlvs(&tlvs); - return ISIS_WARNING; /* Reject */ + return ISIS_WARNING; } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || (adj->adj_usage @@ -689,8 +312,8 @@ static int process_p2p_hello(struct isis_circuit *circuit) } } /* 8.2.5.2 b) if no match was detected */ - else if (listcount(circuit->area->area_addrs) > 0) { - if (circuit->area->is_type == IS_LEVEL_1) { + else if (listcount(iih->circuit->area->area_addrs) > 0) { + if (iih->circuit->area->is_type == IS_LEVEL_1) { /* 8.2.5.2 b) 1) is_type L1 and adj is not up */ if (adj->adj_state != ISIS_ADJ_UP) { isis_adj_state_change(adj, ISIS_ADJ_DOWN, @@ -703,13 +326,12 @@ static int process_p2p_hello(struct isis_circuit *circuit) } /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */ else { - switch (hdr->circuit_t) { + switch (iih->circ_type) { case IS_LEVEL_1: if (adj->adj_state != ISIS_ADJ_UP) { /* (6) reject - Area Mismatch event */ zlog_warn("AreaMismatch"); - free_tlvs(&tlvs); - return ISIS_WARNING; /* Reject */ + return ISIS_WARNING; } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) { /* (7) down - area mismatch */ isis_adj_state_change(adj, @@ -741,7 +363,7 @@ static int process_p2p_hello(struct isis_circuit *circuit) "Wrong System"); } else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2) { - if (hdr->circuit_t == IS_LEVEL_2) { + if (iih->circ_type == IS_LEVEL_2) { /* (7) down - wrong system */ isis_adj_state_change( adj, ISIS_ADJ_DOWN, @@ -763,7 +385,7 @@ static int process_p2p_hello(struct isis_circuit *circuit) isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Area Mismatch"); } - if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed) { + if (adj->adj_state == ISIS_ADJ_UP && changed) { lsp_regenerate_schedule(adj->circuit->area, isis_adj_usage2levels(adj->adj_usage), 0); @@ -790,332 +412,73 @@ static int process_p2p_hello(struct isis_circuit *circuit) break; } - if (isis->debugs & DEBUG_ADJ_PACKETS) { zlog_debug( "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s," " cir id %02d, length %d", - circuit->area->area_tag, circuit->interface->name, - circuit_t2string(circuit->is_type), circuit->circuit_id, - pdu_len); + iih->circuit->area->area_tag, + iih->circuit->interface->name, + circuit_t2string(iih->circuit->is_type), + iih->circuit->circuit_id, iih->pdu_len); } - free_tlvs(&tlvs); - - return retval; + return ISIS_OK; } -/* - * Process IS-IS LAN Level 1/2 Hello PDU - */ -static int process_lan_hello(int level, struct isis_circuit *circuit, - const u_char *ssnpa) +static int process_lan_hello(struct iih_info *iih) { - int retval = ISIS_OK; - struct isis_lan_hello_hdr hdr; struct isis_adjacency *adj; - u_int32_t expected = 0, found = 0, auth_tlv_offset = 0; - struct tlvs tlvs; - u_char *snpa; - struct listnode *node; - int v4_usable = 0, v6_usable = 0; - - if (isis->debugs & DEBUG_ADJ_PACKETS) { - zlog_debug( - "ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, " - "cirID %u", - circuit->area->area_tag, level, - circuit->interface->name, - circuit_t2string(circuit->is_type), - circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) - zlog_dump_data(STREAM_DATA(circuit->rcv_stream), - stream_get_endp(circuit->rcv_stream)); - } - - if (circuit->circ_type != CIRCUIT_T_BROADCAST) { - zlog_warn("lan hello on non broadcast circuit"); - return ISIS_WARNING; - } - - if ((stream_get_endp(circuit->rcv_stream) - - stream_get_getp(circuit->rcv_stream)) - < ISIS_LANHELLO_HDRLEN) { - zlog_warn("Packet too short"); - return ISIS_WARNING; - } - - if (circuit->ext_domain) { - zlog_debug( - "level %d LAN Hello received over circuit with " - "externalDomain = true", - level); - return ISIS_WARNING; - } - - if (!accept_level(level, circuit->is_type)) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { - zlog_debug( - "ISIS-Adj (%s): Interface level mismatch, %s", - circuit->area->area_tag, - circuit->interface->name); - } - return ISIS_WARNING; - } - -#if 0 - /* Cisco's debug message compatability */ - if (!accept_level (level, circuit->area->is_type)) - { - if (isis->debugs & DEBUG_ADJ_PACKETS) - { - zlog_debug ("ISIS-Adj (%s): is type mismatch", - circuit->area->area_tag); - } - return ISIS_WARNING; - } -#endif - /* - * Fill the header - */ - hdr.circuit_t = stream_getc(circuit->rcv_stream); - stream_get(hdr.source_id, circuit->rcv_stream, ISIS_SYS_ID_LEN); - hdr.hold_time = stream_getw(circuit->rcv_stream); - hdr.pdu_len = stream_getw(circuit->rcv_stream); - hdr.prio = stream_getc(circuit->rcv_stream); - stream_get(hdr.lan_id, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1); - - if (hdr.pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LANHELLO_HDRLEN) - || hdr.pdu_len > ISO_MTU(circuit) - || hdr.pdu_len > stream_get_endp(circuit->rcv_stream)) { - zlog_warn( - "ISIS-Adj (%s): Rcvd LAN IIH from (%s) with " - "invalid pdu length %d", - circuit->area->area_tag, circuit->interface->name, - hdr.pdu_len); - return ISIS_WARNING; - } - - /* - * Set the stream endp to PDU length, ignoring additional padding - * introduced by transport chips. - */ - if (hdr.pdu_len < stream_get_endp(circuit->rcv_stream)) - stream_set_endp(circuit->rcv_stream, hdr.pdu_len); - - if (hdr.circuit_t != IS_LEVEL_1 && hdr.circuit_t != IS_LEVEL_2 - && hdr.circuit_t != IS_LEVEL_1_AND_2 - && (level & hdr.circuit_t) == 0) { - zlog_err("Level %d LAN Hello with Circuit Type %d", level, - hdr.circuit_t); - return ISIS_ERROR; - } - - /* - * Then get the tlvs - */ - expected |= TLVFLAG_AUTH_INFO; - expected |= TLVFLAG_AREA_ADDRS; - expected |= TLVFLAG_LAN_NEIGHS; - 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, STREAM_PNT(circuit->rcv_stream), - hdr.pdu_len - ISIS_LANHELLO_HDRLEN - ISIS_FIXED_HDR_LEN, - &expected, &found, &tlvs, &auth_tlv_offset); - - if (retval > ISIS_WARNING) { - zlog_warn("parse_tlvs() failed"); - goto out; - } - - if (!(found & TLVFLAG_AREA_ADDRS)) { - zlog_warn( - "No Area addresses TLV in Level %d LAN IS to IS hello", - level); - retval = ISIS_WARNING; - goto out; - } - - if (!(found & TLVFLAG_NLPID)) { - zlog_warn( - "No supported protocols TLV in Level %d LAN IS to IS hello", - level); - retval = ISIS_WARNING; - goto out; - } - - /* Verify authentication, either cleartext of HMAC MD5 */ - if (circuit->passwd.type) { - if (!(found & TLVFLAG_AUTH_INFO) - || authentication_check(&tlvs.auth_info, &circuit->passwd, - circuit->rcv_stream, - auth_tlv_offset)) { - isis_event_auth_failure( - circuit->area->area_tag, - "LAN hello authentication failure", - hdr.source_id); - retval = ISIS_WARNING; - goto out; - } - } - - if (!memcmp(hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN)) { - zlog_warn("ISIS-Adj (%s): duplicate system ID on interface %s", - circuit->area->area_tag, circuit->interface->name); - return ISIS_WARNING; - } - - /* - * Accept the level 1 adjacency only if a match between local and - * remote area addresses is found - */ - if (listcount(circuit->area->area_addrs) == 0 - || (level == IS_LEVEL_1 - && area_match(circuit->area->area_addrs, tlvs.area_addrs) - == 0)) { - if (isis->debugs & DEBUG_ADJ_PACKETS) { - zlog_debug( - "ISIS-Adj (%s): Area mismatch, level %d IIH on %s", - circuit->area->area_tag, level, - circuit->interface->name); - } - retval = ISIS_OK; - goto out; - } - - /* - * it's own IIH PDU - discard silently - */ - if (!memcmp(circuit->u.bc.snpa, ssnpa, ETH_ALEN)) { - zlog_debug("ISIS-Adj (%s): it's own IIH PDU - discarded", - circuit->area->area_tag); - - retval = ISIS_OK; - goto out; - } - - /* - * check if both ends have an IPv4 address - */ - if (circuit->ip_addrs && listcount(circuit->ip_addrs) && tlvs.ipv4_addrs - && listcount(tlvs.ipv4_addrs)) { - v4_usable = 1; - } - - if (found & TLVFLAG_IPV6_ADDR) { - /* TBA: check that we have a linklocal ourselves? */ - struct listnode *node; - struct in6_addr *ip; - for (ALL_LIST_ELEMENTS_RO(tlvs.ipv6_addrs, node, ip)) - if (IN6_IS_ADDR_LINKLOCAL(ip)) { - v6_usable = 1; - break; - } - - if (!v6_usable) - zlog_warn( - "ISIS-Adj: IPv6 addresses present but no link-local " - "in LAN IIH from %s\n", - circuit->interface->name); - } - - if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR))) - zlog_warn( - "ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n", - circuit->interface->name); - - if (!v6_usable && !v4_usable) { - free_tlvs(&tlvs); - return ISIS_WARNING; - } - - - adj = isis_adj_lookup(hdr.source_id, circuit->u.bc.adjdb[level - 1]); - if ((adj == NULL) || (memcmp(adj->snpa, ssnpa, ETH_ALEN)) - || (adj->level != level)) { + adj = isis_adj_lookup(iih->sys_id, + iih->circuit->u.bc.adjdb[iih->level - 1]); + if ((adj == NULL) || (memcmp(adj->snpa, iih->ssnpa, ETH_ALEN)) + || (adj->level != iih->level)) { if (!adj) { - /* - * Do as in 8.4.2.5 - */ - adj = isis_new_adj(hdr.source_id, ssnpa, level, - circuit); - if (adj == NULL) { - retval = ISIS_ERROR; - goto out; - } + /* Do as in 8.4.2.5 */ + adj = isis_new_adj(iih->sys_id, iih->ssnpa, iih->level, + iih->circuit); } else { - if (ssnpa) { - memcpy(adj->snpa, ssnpa, 6); + if (iih->ssnpa) { + memcpy(adj->snpa, iih->ssnpa, 6); } else { memset(adj->snpa, ' ', 6); } - adj->level = level; + adj->level = iih->level; } isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, NULL); - if (level == IS_LEVEL_1) + if (iih->level == IS_LEVEL_1) adj->sys_type = ISIS_SYSTYPE_L1_IS; else adj->sys_type = ISIS_SYSTYPE_L2_IS; - list_delete_all_node(circuit->u.bc.lan_neighs[level - 1]); - isis_adj_build_neigh_list(circuit->u.bc.adjdb[level - 1], - circuit->u.bc.lan_neighs[level - 1]); + list_delete_all_node( + iih->circuit->u.bc.lan_neighs[iih->level - 1]); + isis_adj_build_neigh_list( + iih->circuit->u.bc.adjdb[iih->level - 1], + iih->circuit->u.bc.lan_neighs[iih->level - 1]); } - if (adj->dis_record[level - 1].dis == ISIS_IS_DIS) - switch (level) { - case 1: - if (memcmp(circuit->u.bc.l1_desig_is, hdr.lan_id, - ISIS_SYS_ID_LEN + 1)) { - thread_add_event(master, - isis_event_dis_status_change, - circuit, 0, NULL); - memcpy(&circuit->u.bc.l1_desig_is, hdr.lan_id, - ISIS_SYS_ID_LEN + 1); - } - break; - case 2: - if (memcmp(circuit->u.bc.l2_desig_is, hdr.lan_id, - ISIS_SYS_ID_LEN + 1)) { - thread_add_event(master, - isis_event_dis_status_change, - circuit, 0, NULL); - memcpy(&circuit->u.bc.l2_desig_is, hdr.lan_id, - ISIS_SYS_ID_LEN + 1); - } - break; + if (adj->dis_record[iih->level - 1].dis == ISIS_IS_DIS) { + u_char *dis = (iih->level == 1) + ? iih->circuit->u.bc.l1_desig_is + : iih->circuit->u.bc.l2_desig_is; + + if (memcmp(dis, iih->dis, ISIS_SYS_ID_LEN + 1)) { + thread_add_event(master, isis_event_dis_status_change, + iih->circuit, 0, NULL); + memcpy(dis, iih->dis, ISIS_SYS_ID_LEN + 1); } - - adj->hold_time = hdr.hold_time; - adj->last_upd = time(NULL); - adj->prio[level - 1] = hdr.prio; - - memcpy(adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1); - - tlvs_to_adj_area_addrs(&tlvs, adj); - - /* which protocol are spoken ??? */ - if (tlvs_to_adj_nlpids(&tlvs, adj)) { - retval = ISIS_WARNING; - goto out; } - /* we need to copy addresses to the adj */ - if (found & TLVFLAG_IPV4_ADDR) - tlvs_to_adj_ipv4_addrs(&tlvs, adj); + adj->circuit_t = iih->circ_type; + adj->hold_time = iih->holdtime; + adj->last_upd = time(NULL); + adj->prio[iih->level - 1] = iih->priority; + memcpy(adj->lanid, iih->dis, ISIS_SYS_ID_LEN + 1); - if (found & TLVFLAG_IPV6_ADDR) - tlvs_to_adj_ipv6_addrs(&tlvs, adj); - - adj->circuit_t = hdr.circuit_t; - - bool mt_set_changed = - tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj); + bool changed; + isis_tlvs_to_adj(iih->tlvs, adj, &changed); + changed |= tlvs_to_adj_mt_set(iih->tlvs, iih->v4_usable, iih->v6_usable, + adj); /* lets take care of the expiry */ THREAD_TIMER_OFF(adj->t_expire); @@ -1126,51 +489,192 @@ static int process_lan_hello(int level, struct isis_circuit *circuit, * If the snpa for this circuit is found from LAN Neighbours TLV * we have two-way communication -> adjacency can be put to state "up" */ + bool own_snpa_found = + isis_tlvs_own_snpa_found(iih->tlvs, iih->circuit->u.bc.snpa); - if (found & TLVFLAG_LAN_NEIGHS) { - if (adj->adj_state != ISIS_ADJ_UP) { - for (ALL_LIST_ELEMENTS_RO(tlvs.lan_neighs, node, - snpa)) { - if (!memcmp(snpa, circuit->u.bc.snpa, - ETH_ALEN)) { - isis_adj_state_change( - adj, ISIS_ADJ_UP, - "own SNPA found in LAN Neighbours TLV"); - } - } - } else { - int found = 0; - for (ALL_LIST_ELEMENTS_RO(tlvs.lan_neighs, node, snpa)) - if (!memcmp(snpa, circuit->u.bc.snpa, - ETH_ALEN)) { - found = 1; - break; - } - if (found == 0) - isis_adj_state_change( - adj, ISIS_ADJ_INITIALIZING, - "own SNPA not found in LAN Neighbours TLV"); + if (adj->adj_state != ISIS_ADJ_UP) { + if (own_snpa_found) { + isis_adj_state_change( + adj, ISIS_ADJ_UP, + "own SNPA found in LAN Neighbours TLV"); + } + } else { + if (!own_snpa_found) { + isis_adj_state_change( + adj, ISIS_ADJ_INITIALIZING, + "own SNPA not found in LAN Neighbours TLV"); } - } else if (adj->adj_state == ISIS_ADJ_UP) { - isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, - "no LAN Neighbours TLV found"); } - if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed) - lsp_regenerate_schedule(adj->circuit->area, level, 0); + if (adj->adj_state == ISIS_ADJ_UP && changed) + lsp_regenerate_schedule(adj->circuit->area, iih->level, 0); -out: if (isis->debugs & DEBUG_ADJ_PACKETS) { zlog_debug( - "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, " - "cirID %u, length %zd", - circuit->area->area_tag, level, snpa_print(ssnpa), - circuit->interface->name, - circuit_t2string(circuit->is_type), circuit->circuit_id, - stream_get_endp(circuit->rcv_stream)); + "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, cirID %u, length %zd", + iih->circuit->area->area_tag, iih->level, + snpa_print(iih->ssnpa), iih->circuit->interface->name, + circuit_t2string(iih->circuit->is_type), + iih->circuit->circuit_id, + stream_get_endp(iih->circuit->rcv_stream)); + } + return ISIS_OK; +} + +static int pdu_len_validate(uint16_t pdu_len, struct isis_circuit *circuit) +{ + if (pdu_len < stream_get_getp(circuit->rcv_stream) + || pdu_len > ISO_MTU(circuit) + || pdu_len > stream_get_endp(circuit->rcv_stream)) + return 1; + + if (pdu_len < stream_get_endp(circuit->rcv_stream)) + stream_set_endp(circuit->rcv_stream, pdu_len); + return 0; +} + +static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, + u_char *ssnpa) +{ + bool p2p_hello = (pdu_type == P2P_HELLO); + int level = p2p_hello ? 0 + : (pdu_type == L1_LAN_HELLO) ? ISIS_LEVEL1 + : ISIS_LEVEL2; + const char *pdu_name = + p2p_hello + ? "P2P IIH" + : (level == ISIS_LEVEL1) ? "L1 LAN IIH" : "L2 LAN IIH"; + + if (isis->debugs & DEBUG_ADJ_PACKETS) { + zlog_debug("ISIS-Adj (%s): Rcvd %s on %s, cirType %s, cirID %u", + circuit->area->area_tag, pdu_name, + circuit->interface->name, + circuit_t2string(circuit->is_type), + circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data(STREAM_DATA(circuit->rcv_stream), + stream_get_endp(circuit->rcv_stream)); } - free_tlvs(&tlvs); + if (p2p_hello) { + if (circuit->circ_type != CIRCUIT_T_P2P) { + zlog_warn("p2p hello on non p2p circuit"); + return ISIS_WARNING; + } + } else { + if (circuit->circ_type != CIRCUIT_T_BROADCAST) { + zlog_warn("lan hello on non broadcast circuit"); + return ISIS_WARNING; + } + + if (circuit->ext_domain) { + zlog_debug( + "level %d LAN Hello received over circuit with externalDomain = true", + level); + return ISIS_WARNING; + } + + if (!(circuit->is_type & level)) { + if (isis->debugs & DEBUG_ADJ_PACKETS) { + zlog_debug( + "ISIS-Adj (%s): Interface level mismatch, %s", + circuit->area->area_tag, + circuit->interface->name); + } + return ISIS_WARNING; + } + } + + struct iih_info iih = { + .circuit = circuit, .ssnpa = ssnpa, .level = level}; + + /* Generic IIH Header */ + iih.circ_type = stream_getc(circuit->rcv_stream) & 0x03; + stream_get(iih.sys_id, circuit->rcv_stream, ISIS_SYS_ID_LEN); + iih.holdtime = stream_getw(circuit->rcv_stream); + iih.pdu_len = stream_getw(circuit->rcv_stream); + + if (p2p_hello) { + iih.circuit_id = stream_getc(circuit->rcv_stream); + } else { + iih.priority = stream_getc(circuit->rcv_stream); + stream_get(iih.dis, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1); + } + + if (pdu_len_validate(iih.pdu_len, circuit)) { + zlog_warn( + "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %" PRIu16, + circuit->area->area_tag, pdu_name, + circuit->interface->name, iih.pdu_len); + return ISIS_WARNING; + } + + if (!p2p_hello && !(level & iih.circ_type)) { + zlog_err("Level %d LAN Hello with Circuit Type %d", level, + iih.circ_type); + return ISIS_ERROR; + } + + const char *error_log; + int retval = ISIS_WARNING; + + if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream), + circuit->rcv_stream, &iih.tlvs, &error_log)) { + zlog_warn("isis_unpack_tlvs() failed: %s", error_log); + goto out; + } + + if (!iih.tlvs->area_addresses.count) { + zlog_warn("No Area addresses TLV in %s", pdu_name); + goto out; + } + + if (!iih.tlvs->protocols_supported.count) { + zlog_warn("No supported protocols TLV in %s", pdu_name); + goto out; + } + + if (!isis_tlvs_auth_is_valid(iih.tlvs, &circuit->passwd, + circuit->rcv_stream, false)) { + isis_event_auth_failure(circuit->area->area_tag, + "IIH authentication failure", + iih.sys_id); + goto out; + } + + if (!memcmp(iih.sys_id, isis->sysid, ISIS_SYS_ID_LEN)) { + zlog_warn( + "ISIS-Adj (%s): Received IIH with own sysid - discard", + circuit->area->area_tag); + goto out; + } + + if (!p2p_hello + && (listcount(circuit->area->area_addrs) == 0 + || (level == ISIS_LEVEL1 + && !isis_tlvs_area_addresses_match( + iih.tlvs, circuit->area->area_addrs)))) { + if (isis->debugs & DEBUG_ADJ_PACKETS) { + zlog_debug( + "ISIS-Adj (%s): Area mismatch, level %d IIH on %s", + circuit->area->area_tag, level, + circuit->interface->name); + } + goto out; + } + + iih.v4_usable = (circuit->ip_addrs && listcount(circuit->ip_addrs) + && iih.tlvs->ipv4_address.count); + + iih.v6_usable = (circuit->ipv6_link && listcount(circuit->ipv6_link) + && iih.tlvs->ipv6_address.count); + + if (!iih.v4_usable && !iih.v6_usable) + goto out; + + retval = p2p_hello ? process_p2p_hello(&iih) : process_lan_hello(&iih); +out: + isis_free_tlvs(iih.tlvs); return retval; } @@ -1180,17 +684,10 @@ out: * ISO - 10589 * Section 7.3.15.1 - Action on receipt of a link state PDU */ -static int process_lsp(int level, struct isis_circuit *circuit, +static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, const u_char *ssnpa) { - struct isis_link_state_hdr *hdr; - struct isis_adjacency *adj = NULL; - struct isis_lsp *lsp, *lsp0 = NULL; - int retval = ISIS_OK, comp = 0; - u_char lspid[ISIS_SYS_ID_LEN + 2]; - struct isis_passwd *passwd; - uint16_t pdu_len; - int lsp_confusion; + int level = (pdu_type == L1_LINK_STATE) ? ISIS_LEVEL1 : ISIS_LEVEL2; if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug( @@ -1204,65 +701,50 @@ static int process_lsp(int level, struct isis_circuit *circuit, stream_get_endp(circuit->rcv_stream)); } - if ((stream_get_endp(circuit->rcv_stream) - - stream_get_getp(circuit->rcv_stream)) - < ISIS_LSP_HDR_LEN) { - zlog_warn("Packet too short"); + struct isis_lsp_hdr hdr = {}; + + hdr.pdu_len = stream_getw(circuit->rcv_stream); + hdr.rem_lifetime = stream_getw(circuit->rcv_stream); + stream_get(hdr.lsp_id, circuit->rcv_stream, sizeof(hdr.lsp_id)); + hdr.seqno = stream_getl(circuit->rcv_stream); + hdr.checksum = stream_getw(circuit->rcv_stream); + hdr.lsp_bits = stream_getc(circuit->rcv_stream); + + if (pdu_len_validate(hdr.pdu_len, circuit)) { + zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %" PRIu16, + circuit->area->area_tag, rawlspid_print(hdr.lsp_id), + hdr.pdu_len); return ISIS_WARNING; } - /* Reference the header */ - hdr = (struct isis_link_state_hdr *)STREAM_PNT(circuit->rcv_stream); - pdu_len = ntohs(hdr->pdu_len); - - /* lsp length check */ - if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN) - || pdu_len > ISO_MTU(circuit) - || pdu_len > stream_get_endp(circuit->rcv_stream)) { - zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %d", - circuit->area->area_tag, rawlspid_print(hdr->lsp_id), - pdu_len); - - return ISIS_WARNING; - } - - /* - * Set the stream endp to PDU length, ignoring additional padding - * introduced by transport chips. - */ - if (pdu_len < stream_get_endp(circuit->rcv_stream)) - stream_set_endp(circuit->rcv_stream, pdu_len); - if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug( - "ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04x, " - "lifetime %us, len %u, on %s", - circuit->area->area_tag, level, - rawlspid_print(hdr->lsp_id), ntohl(hdr->seq_num), - ntohs(hdr->checksum), ntohs(hdr->rem_lifetime), pdu_len, - circuit->interface->name); + zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s, len %" PRIu16 ", on %s", + circuit->area->area_tag, level, + rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum, + hdr.rem_lifetime, hdr.pdu_len, + circuit->interface->name); } /* lsp is_type check */ - if ((hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1 - && (hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1_AND_2) { - zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP is type %x", - circuit->area->area_tag, rawlspid_print(hdr->lsp_id), - hdr->lsp_bits); + if ((hdr.lsp_bits & IS_LEVEL_1) != IS_LEVEL_1) { + zlog_debug( + "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%" PRIx8, + circuit->area->area_tag, rawlspid_print(hdr.lsp_id), + hdr.lsp_bits & IS_LEVEL_1_AND_2); /* continue as per RFC1122 Be liberal in what you accept, and * conservative in what you send */ } /* Checksum sanity check - FIXME: move to correct place */ /* 12 = sysid+pdu+remtime */ - if (iso_csum_verify(STREAM_PNT(circuit->rcv_stream) + 4, pdu_len - 12, - hdr->checksum, - offsetof(struct isis_link_state_hdr, checksum) - - 4)) { - zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x", - circuit->area->area_tag, rawlspid_print(hdr->lsp_id), - ntohs(hdr->checksum)); - + if (iso_csum_verify(STREAM_DATA(circuit->rcv_stream) + 12, + hdr.pdu_len - 12, hdr.checksum, 12)) { + zlog_debug( + "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04" PRIx16, + circuit->area->area_tag, rawlspid_print(hdr.lsp_id), + hdr.checksum); return ISIS_WARNING; } @@ -1271,46 +753,56 @@ static int process_lsp(int level, struct isis_circuit *circuit, zlog_debug( "ISIS-Upd (%s): LSP %s received at level %d over circuit with " "externalDomain = true", - circuit->area->area_tag, rawlspid_print(hdr->lsp_id), + circuit->area->area_tag, rawlspid_print(hdr.lsp_id), level); - return ISIS_WARNING; } /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ - if (!accept_level(level, circuit->is_type)) { + if (!(circuit->is_type & level)) { zlog_debug( "ISIS-Upd (%s): LSP %s received at level %d over circuit of" " type %s", - circuit->area->area_tag, rawlspid_print(hdr->lsp_id), + circuit->area->area_tag, rawlspid_print(hdr.lsp_id), level, circuit_t2string(circuit->is_type)); - return ISIS_WARNING; } + struct isis_tlvs *tlvs = NULL; + int retval = ISIS_WARNING; + const char *error_log; + + if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream), + circuit->rcv_stream, &tlvs, &error_log)) { + zlog_warn("Something went wrong unpacking the LSP: %s", + error_log); + goto out; + } + /* 7.3.15.1 a) 4 - need to make sure IDLength matches */ /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use * 3 */ /* 7.3.15.1 a) 7 - password check */ - (level == IS_LEVEL_1) ? (passwd = &circuit->area->area_passwd) - : (passwd = &circuit->area->domain_passwd); - if (passwd->type) { - if (lsp_authentication_check(circuit->rcv_stream, circuit->area, - level, passwd)) { - isis_event_auth_failure(circuit->area->area_tag, - "LSP authentication failure", - hdr->lsp_id); - return ISIS_WARNING; - } + struct isis_passwd *passwd = (level == ISIS_LEVEL1) + ? &circuit->area->area_passwd + : &circuit->area->domain_passwd; + if (!isis_tlvs_auth_is_valid(tlvs, passwd, circuit->rcv_stream, true)) { + isis_event_auth_failure(circuit->area->area_tag, + "LSP authentication failure", + hdr.lsp_id); + goto out; } + /* Find the LSP in our database and compare it to this Link State header */ - lsp = lsp_search(hdr->lsp_id, circuit->area->lspdb[level - 1]); + struct isis_lsp *lsp = + lsp_search(hdr.lsp_id, circuit->area->lspdb[level - 1]); + int comp = 0; if (lsp) - comp = lsp_compare(circuit->area->area_tag, lsp, hdr->seq_num, - hdr->checksum, hdr->rem_lifetime); + comp = lsp_compare(circuit->area->area_tag, lsp, hdr.seqno, + hdr.checksum, hdr.rem_lifetime); if (lsp && (lsp->own_lsp)) goto dontcheckadj; @@ -1319,25 +811,24 @@ static int process_lsp(int level, struct isis_circuit *circuit, /* for broadcast circuits, snpa should be compared */ if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - adj = isis_adj_lookup_snpa(ssnpa, - circuit->u.bc.adjdb[level - 1]); - if (!adj) { - zlog_debug( - "(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04x, " - "lifetime %us on %s", - circuit->area->area_tag, - rawlspid_print(hdr->lsp_id), - ntohl(hdr->seq_num), ntohs(hdr->checksum), - ntohs(hdr->rem_lifetime), - circuit->interface->name); - return ISIS_WARNING; /* Silently discard */ + if (!isis_adj_lookup_snpa(ssnpa, + circuit->u.bc.adjdb[level - 1])) { + zlog_debug("(%s): DS ======= LSP %s, seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s on %s", + circuit->area->area_tag, + rawlspid_print(hdr.lsp_id), hdr.seqno, + hdr.checksum, hdr.rem_lifetime, + circuit->interface->name); + goto out; /* Silently discard */ } } /* for non broadcast, we just need to find same level adj */ else { /* If no adj, or no sharing of level */ if (!circuit->u.p2p.neighbor) { - return ISIS_OK; /* Silently discard */ + retval = ISIS_OK; + goto out; } else { if (((level == IS_LEVEL_1) && (circuit->u.p2p.neighbor->adj_usage @@ -1345,10 +836,12 @@ static int process_lsp(int level, struct isis_circuit *circuit, || ((level == IS_LEVEL_2) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1))) - return ISIS_WARNING; /* Silently discard */ + goto out; } } + bool lsp_confusion; + dontcheckadj: /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */ @@ -1360,33 +853,36 @@ dontcheckadj: /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num * but * wrong checksum, initiate a purge. */ - if (lsp && (lsp->lsp_header->seq_num == hdr->seq_num) - && (lsp->lsp_header->checksum != hdr->checksum)) { - zlog_warn( - "ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.", - circuit->area->area_tag, rawlspid_print(hdr->lsp_id), - ntohl(hdr->seq_num)); - hdr->rem_lifetime = 0; - lsp_confusion = 1; + if (lsp && (lsp->hdr.seqno == hdr.seqno) + && (lsp->hdr.checksum != hdr.checksum)) { + zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08" PRIx32 + " with confused checksum received.", + circuit->area->area_tag, rawlspid_print(hdr.lsp_id), + hdr.seqno); + hdr.rem_lifetime = 0; + lsp_confusion = true; } else - lsp_confusion = 0; + lsp_confusion = false; /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */ - if (hdr->rem_lifetime == 0) { + if (hdr.rem_lifetime == 0) { if (!lsp) { /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't * save */ /* only needed on explicit update, eg - p2p */ if (circuit->circ_type == CIRCUIT_T_P2P) - ack_lsp(hdr, circuit, level); - return retval; /* FIXME: do we need a purge? */ + ack_lsp(&hdr, circuit, level); + goto out; /* FIXME: do we need a purge? */ } else { - if (memcmp(hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { + if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { /* LSP by some other system -> do 7.3.16.4 b) */ /* 7.3.16.4 b) 1) */ if (comp == LSP_NEWER) { - lsp_update(lsp, circuit->rcv_stream, - circuit->area, level); + lsp_update(lsp, &hdr, tlvs, + circuit->rcv_stream, + circuit->area, level, + lsp_confusion); + tlvs = NULL; /* ii */ lsp_set_all_srmflags(lsp); /* v */ @@ -1427,11 +923,10 @@ dontcheckadj: ISIS_SET_FLAG(lsp->SRMflags, circuit); ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } - } else if (lsp->lsp_header->rem_lifetime != 0) { + } else if (lsp->hdr.rem_lifetime != 0) { /* our own LSP -> 7.3.16.4 c) */ if (comp == LSP_NEWER) { - lsp_inc_seqnum(lsp, - ntohl(hdr->seq_num)); + lsp_inc_seqno(lsp, hdr.seqno); lsp_set_all_srmflags(lsp); } else { ISIS_SET_FLAG(lsp->SRMflags, circuit); @@ -1439,23 +934,22 @@ dontcheckadj: } if (isis->debugs & DEBUG_UPDATE_PACKETS) zlog_debug( - "ISIS-Upd (%s): (1) re-originating LSP %s new " - "seq 0x%08x", + "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08" PRIx32, circuit->area->area_tag, - rawlspid_print(hdr->lsp_id), - ntohl(lsp->lsp_header - ->seq_num)); + rawlspid_print(hdr.lsp_id), + lsp->hdr.seqno); } } - return retval; + goto out; } /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a * purge */ - if (memcmp(hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) { + if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) { if (!lsp) { /* 7.3.16.4: initiate a purge */ - lsp_purge_non_exist(level, hdr, circuit->area); - return ISIS_OK; + lsp_purge_non_exist(level, &hdr, circuit->area); + retval = ISIS_OK; + goto out; } /* 7.3.15.1 d) - If this is our own lsp and we have it */ @@ -1465,16 +959,15 @@ dontcheckadj: * is * "greater" than that held by S, ... */ - if (ntohl(hdr->seq_num) > ntohl(lsp->lsp_header->seq_num)) { + if (hdr.seqno > lsp->hdr.seqno) { /* 7.3.16.1 */ - lsp_inc_seqnum(lsp, ntohl(hdr->seq_num)); + lsp_inc_seqno(lsp, hdr.seqno); if (isis->debugs & DEBUG_UPDATE_PACKETS) zlog_debug( - "ISIS-Upd (%s): (2) re-originating LSP %s new seq " - "0x%08x", + "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08" PRIx32, circuit->area->area_tag, - rawlspid_print(hdr->lsp_id), - ntohl(lsp->lsp_header->seq_num)); + rawlspid_print(hdr.lsp_id), + lsp->hdr.seqno); } /* If the received LSP is older or equal, * resend the LSP which will act as ACK */ @@ -1489,8 +982,10 @@ dontcheckadj: * If this lsp is a frag, need to see if we have zero * lsp present */ - if (LSP_FRAGMENT(hdr->lsp_id) != 0) { - memcpy(lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1); + struct isis_lsp *lsp0 = NULL; + if (LSP_FRAGMENT(hdr.lsp_id) != 0) { + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + memcpy(lspid, hdr.lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lspid) = 0; lsp0 = lsp_search( lspid, circuit->area->lspdb[level - 1]); @@ -1502,15 +997,17 @@ dontcheckadj: } /* i */ if (!lsp) { - lsp = lsp_new_from_stream_ptr( - circuit->rcv_stream, pdu_len, lsp0, + lsp = lsp_new_from_recv( + &hdr, tlvs, circuit->rcv_stream, lsp0, circuit->area, level); + tlvs = NULL; lsp_insert(lsp, circuit->area->lspdb[level - 1]); } else /* exists, so we overwrite */ { - lsp_update(lsp, circuit->rcv_stream, - circuit->area, level); + lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream, + circuit->area, level, false); + tlvs = NULL; } /* ii */ lsp_set_all_srmflags(lsp); @@ -1525,8 +1022,9 @@ dontcheckadj: /* 7.3.15.1 e) 2) LSP equal to the one in db */ else if (comp == LSP_EQUAL) { ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); - lsp_update(lsp, circuit->rcv_stream, circuit->area, - level); + lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream, + circuit->area, level, false); + tlvs = NULL; if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_SET_FLAG(lsp->SSNflags, circuit); } @@ -1536,6 +1034,11 @@ dontcheckadj: ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } } + + retval = ISIS_OK; + +out: + isis_free_tlvs(tlvs); return retval; } @@ -1545,60 +1048,48 @@ dontcheckadj: * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU */ -static int process_snp(int snp_type, int level, struct isis_circuit *circuit, +static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, const u_char *ssnpa) { - int retval = ISIS_OK; - int cmp, own_lsp; - char typechar = ' '; - uint16_t pdu_len; - struct isis_adjacency *adj; - struct isis_complete_seqnum_hdr *chdr = NULL; - struct isis_partial_seqnum_hdr *phdr = NULL; - uint32_t found = 0, expected = 0, auth_tlv_offset = 0; - struct isis_lsp *lsp; - struct lsp_entry *entry; - struct listnode *node, *nnode; - struct listnode *node2, *nnode2; - struct tlvs tlvs; - struct list *lsp_list = NULL; - struct isis_passwd *passwd; + bool is_csnp = (pdu_type == L1_COMPLETE_SEQ_NUM + || pdu_type == L2_COMPLETE_SEQ_NUM); + char typechar = is_csnp ? 'C' : 'P'; + int level = (pdu_type == L1_COMPLETE_SEQ_NUM + || pdu_type == L1_PARTIAL_SEQ_NUM) + ? ISIS_LEVEL1 + : ISIS_LEVEL2; - if (snp_type == ISIS_SNP_CSNP_FLAG) { - /* getting the header info */ - typechar = 'C'; - chdr = (struct isis_complete_seqnum_hdr *)STREAM_PNT( - circuit->rcv_stream); - stream_forward_getp(circuit->rcv_stream, ISIS_CSNP_HDRLEN); - pdu_len = ntohs(chdr->pdu_len); - if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_CSNP_HDRLEN) - || pdu_len > ISO_MTU(circuit) - || pdu_len > stream_get_endp(circuit->rcv_stream)) { - zlog_warn("Received a CSNP with bogus length %d", - pdu_len); - return ISIS_WARNING; - } - } else { - typechar = 'P'; - phdr = (struct isis_partial_seqnum_hdr *)STREAM_PNT( - circuit->rcv_stream); - stream_forward_getp(circuit->rcv_stream, ISIS_PSNP_HDRLEN); - pdu_len = ntohs(phdr->pdu_len); - if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_PSNP_HDRLEN) - || pdu_len > ISO_MTU(circuit) - || pdu_len > stream_get_endp(circuit->rcv_stream)) { - zlog_warn("Received a PSNP with bogus length %d", - pdu_len); - return ISIS_WARNING; - } + uint16_t pdu_len = stream_getw(circuit->rcv_stream); + uint8_t rem_sys_id[ISIS_SYS_ID_LEN]; + stream_get(rem_sys_id, circuit->rcv_stream, ISIS_SYS_ID_LEN); + stream_forward_getp(circuit->rcv_stream, 1); /* Circuit ID - unused */ + + uint8_t start_lsp_id[ISIS_SYS_ID_LEN + 2] = {}; + uint8_t stop_lsp_id[ISIS_SYS_ID_LEN + 2] = {}; + + if (is_csnp) { + stream_get(start_lsp_id, circuit->rcv_stream, + ISIS_SYS_ID_LEN + 2); + stream_get(stop_lsp_id, circuit->rcv_stream, + ISIS_SYS_ID_LEN + 2); } - /* - * Set the stream endp to PDU length, ignoring additional padding - * introduced by transport chips. - */ - if (pdu_len < stream_get_endp(circuit->rcv_stream)) - stream_set_endp(circuit->rcv_stream, pdu_len); + if (pdu_len_validate(pdu_len, circuit)) { + zlog_warn("Received a CSNP with bogus length %d", pdu_len); + return ISIS_WARNING; + } + + if (isis->debugs & DEBUG_SNP_PACKETS) { + zlog_debug( + "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, cirType %s, cirID %u", + circuit->area->area_tag, level, typechar, + circuit->interface->name, + circuit_t2string(circuit->is_type), + circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data(STREAM_DATA(circuit->rcv_stream), + stream_get_endp(circuit->rcv_stream)); + } /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */ if (circuit->ext_domain) { @@ -1613,8 +1104,7 @@ static int process_snp(int snp_type, int level, struct isis_circuit *circuit, } /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */ - if (!accept_level(level, circuit->is_type)) { - + if (!(circuit->is_type & level)) { zlog_debug( "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " "skipping: circuit type %s does not match level %d", @@ -1626,9 +1116,8 @@ static int process_snp(int snp_type, int level, struct isis_circuit *circuit, } /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */ - if ((snp_type == ISIS_SNP_PSNP_FLAG) - && (circuit->circ_type == CIRCUIT_T_BROADCAST) - && (!circuit->u.bc.is_dr[level - 1])) { + if (!is_csnp && (circuit->circ_type == CIRCUIT_T_BROADCAST) + && !circuit->u.bc.is_dr[level - 1]) { zlog_debug( "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, " "skipping: we are not the DIS", @@ -1650,15 +1139,8 @@ static int process_snp(int snp_type, int level, struct isis_circuit *circuit, /* for broadcast circuits, snpa should be compared */ /* FIXME : Do we need to check SNPA? */ if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - if (snp_type == ISIS_SNP_CSNP_FLAG) { - adj = isis_adj_lookup(chdr->source_id, - circuit->u.bc.adjdb[level - 1]); - } else { - /* a psnp on a broadcast, how lovely of Juniper :) */ - adj = isis_adj_lookup(phdr->source_id, - circuit->u.bc.adjdb[level - 1]); - } - if (!adj) + if (!isis_adj_lookup(rem_sys_id, + circuit->u.bc.adjdb[level - 1])) return ISIS_OK; /* Silently discard */ } else { if (!circuit->u.p2p.neighbor) { @@ -1668,164 +1150,128 @@ static int process_snp(int snp_type, int level, struct isis_circuit *circuit, } } - /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */ + struct isis_tlvs *tlvs; + int retval = ISIS_WARNING; + const char *error_log; - /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */ - - memset(&tlvs, 0, sizeof(struct tlvs)); - - /* parse the SNP */ - expected |= TLVFLAG_LSP_ENTRIES; - expected |= TLVFLAG_AUTH_INFO; - - auth_tlv_offset = stream_get_getp(circuit->rcv_stream); - retval = parse_tlvs(circuit->area->area_tag, - STREAM_PNT(circuit->rcv_stream), - pdu_len - stream_get_getp(circuit->rcv_stream), - &expected, &found, &tlvs, &auth_tlv_offset); - - if (retval > ISIS_WARNING) { - zlog_warn("something went very wrong processing SNP"); - free_tlvs(&tlvs); - return retval; + if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream), + circuit->rcv_stream, &tlvs, &error_log)) { + zlog_warn("Something went wrong unpacking the SNP: %s", + error_log); + goto out; } - if (level == IS_LEVEL_1) - passwd = &circuit->area->area_passwd; - else - passwd = &circuit->area->domain_passwd; - - if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) { - if (passwd->type) { - if (!(found & TLVFLAG_AUTH_INFO) - || authentication_check(&tlvs.auth_info, passwd, - circuit->rcv_stream, - auth_tlv_offset)) { - isis_event_auth_failure(circuit->area->area_tag, - "SNP authentication" - " failure", - phdr ? phdr->source_id - : chdr->source_id); - free_tlvs(&tlvs); - return ISIS_OK; - } - } + struct isis_passwd *passwd = (level == IS_LEVEL_1) + ? &circuit->area->area_passwd + : &circuit->area->domain_passwd; + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV) + && !isis_tlvs_auth_is_valid(tlvs, passwd, circuit->rcv_stream, + false)) { + isis_event_auth_failure(circuit->area->area_tag, + "SNP authentication failure", + rem_sys_id); + goto out; } + struct isis_lsp_entry *entry_head = + (struct isis_lsp_entry *)tlvs->lsp_entries.head; + /* debug isis snp-packets */ if (isis->debugs & DEBUG_SNP_PACKETS) { zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s", circuit->area->area_tag, level, typechar, snpa_print(ssnpa), circuit->interface->name); - if (tlvs.lsp_entries) { - for (ALL_LIST_ELEMENTS_RO(tlvs.lsp_entries, node, - entry)) { - zlog_debug( - "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x," - " cksum 0x%04x, lifetime %us", - circuit->area->area_tag, typechar, - rawlspid_print(entry->lsp_id), - ntohl(entry->seq_num), - ntohs(entry->checksum), - ntohs(entry->rem_lifetime)); - } + for (struct isis_lsp_entry *entry = entry_head; entry; + entry = entry->next) { + zlog_debug( + "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s", + circuit->area->area_tag, typechar, + rawlspid_print(entry->id), entry->seqno, + entry->checksum, entry->rem_lifetime); } } /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */ - if (tlvs.lsp_entries) { - for (ALL_LIST_ELEMENTS_RO(tlvs.lsp_entries, node, entry)) { - lsp = lsp_search(entry->lsp_id, - circuit->area->lspdb[level - 1]); - own_lsp = !memcmp(entry->lsp_id, isis->sysid, - ISIS_SYS_ID_LEN); - if (lsp) { - /* 7.3.15.2 b) 1) is this LSP newer */ - cmp = lsp_compare(circuit->area->area_tag, lsp, - entry->seq_num, - entry->checksum, - entry->rem_lifetime); - /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p - */ - if (cmp == LSP_EQUAL) { + for (struct isis_lsp_entry *entry = entry_head; entry; + entry = entry->next) { + struct isis_lsp *lsp = + lsp_search(entry->id, circuit->area->lspdb[level - 1]); + bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN); + if (lsp) { + /* 7.3.15.2 b) 1) is this LSP newer */ + int cmp = lsp_compare(circuit->area->area_tag, lsp, + entry->seqno, entry->checksum, + entry->rem_lifetime); + /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p */ + if (cmp == LSP_EQUAL) { + /* if (circuit->circ_type != + * CIRCUIT_T_BROADCAST) */ + ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); + } + /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM + */ + else if (cmp == LSP_OLDER) { + ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); + ISIS_SET_FLAG(lsp->SRMflags, circuit); + } + /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM + on p2p */ + else { + if (own_lsp) { + lsp_inc_seqno(lsp, entry->seqno); + ISIS_SET_FLAG(lsp->SRMflags, circuit); + } else { + ISIS_SET_FLAG(lsp->SSNflags, circuit); /* if (circuit->circ_type != * CIRCUIT_T_BROADCAST) */ ISIS_CLEAR_FLAG(lsp->SRMflags, circuit); } - /* 7.3.15.2 b) 3) if it is older, clear SSN and - set SRM */ - else if (cmp == LSP_OLDER) { - ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); - ISIS_SET_FLAG(lsp->SRMflags, circuit); - } - /* 7.3.15.2 b) 4) if it is newer, set SSN and - clear SRM on p2p */ - else { - if (own_lsp) { - lsp_inc_seqnum( - lsp, - ntohl(entry->seq_num)); - ISIS_SET_FLAG(lsp->SRMflags, - circuit); - } else { - ISIS_SET_FLAG(lsp->SSNflags, - circuit); - /* if (circuit->circ_type != - * CIRCUIT_T_BROADCAST) */ - ISIS_CLEAR_FLAG(lsp->SRMflags, - circuit); - } - } - } else { - /* 7.3.15.2 b) 5) if it was not found, and all - * of those are not 0, - * insert it and set SSN on it */ - if (entry->rem_lifetime && entry->checksum - && entry->seq_num - && memcmp(entry->lsp_id, isis->sysid, - ISIS_SYS_ID_LEN)) { - lsp = lsp_new( - circuit->area, entry->lsp_id, - ntohs(entry->rem_lifetime), 0, - 0, entry->checksum, level); - lsp_insert(lsp, - circuit->area - ->lspdb[level - 1]); - ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); - ISIS_SET_FLAG(lsp->SSNflags, circuit); - } + } + } else { + /* 7.3.15.2 b) 5) if it was not found, and all of those + * are not 0, + * insert it and set SSN on it */ + if (entry->rem_lifetime && entry->checksum + && entry->seqno && memcmp(entry->id, isis->sysid, + ISIS_SYS_ID_LEN)) { + struct isis_lsp *lsp = + lsp_new(circuit->area, entry->id, + entry->rem_lifetime, 0, 0, + entry->checksum, level); + lsp_insert(lsp, + circuit->area->lspdb[level - 1]); + ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); + ISIS_SET_FLAG(lsp->SSNflags, circuit); } } } /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported */ - if (snp_type == ISIS_SNP_CSNP_FLAG) { + if (is_csnp) { /* * Build a list from our own LSP db bounded with * start_lsp_id and stop_lsp_id */ - lsp_list = list_new(); - lsp_build_list_nonzero_ht(chdr->start_lsp_id, chdr->stop_lsp_id, - lsp_list, + struct list *lsp_list = list_new(); + lsp_build_list_nonzero_ht(start_lsp_id, stop_lsp_id, lsp_list, circuit->area->lspdb[level - 1]); /* Fixme: Find a better solution */ - if (tlvs.lsp_entries) { - for (ALL_LIST_ELEMENTS(tlvs.lsp_entries, node, nnode, - entry)) { - for (ALL_LIST_ELEMENTS(lsp_list, node2, nnode2, - lsp)) { - if (lsp_id_cmp(lsp->lsp_header->lsp_id, - entry->lsp_id) - == 0) { - list_delete_node(lsp_list, - node2); - break; - } + struct listnode *node, *nnode; + struct isis_lsp *lsp; + for (struct isis_lsp_entry *entry = entry_head; entry; + entry = entry->next) { + for (ALL_LIST_ELEMENTS(lsp_list, node, nnode, lsp)) { + if (lsp_id_cmp(lsp->hdr.lsp_id, entry->id) + == 0) { + list_delete_node(lsp_list, node); + break; } } } + /* on remaining LSPs we set SRM (neighbor knew not of) */ for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) ISIS_SET_FLAG(lsp->SRMflags, circuit); @@ -1833,59 +1279,39 @@ static int process_snp(int snp_type, int level, struct isis_circuit *circuit, list_delete(lsp_list); } - free_tlvs(&tlvs); + retval = ISIS_OK; +out: + isis_free_tlvs(tlvs); return retval; } -static int process_csnp(int level, struct isis_circuit *circuit, - const u_char *ssnpa) +static int pdu_size(uint8_t pdu_type, uint8_t *size) { - if (isis->debugs & DEBUG_SNP_PACKETS) { - zlog_debug( - "ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u", - circuit->area->area_tag, level, - circuit->interface->name, - circuit_t2string(circuit->is_type), - circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) - zlog_dump_data(STREAM_DATA(circuit->rcv_stream), - stream_get_endp(circuit->rcv_stream)); + switch (pdu_type) { + case L1_LAN_HELLO: + case L2_LAN_HELLO: + *size = ISIS_LANHELLO_HDRLEN; + break; + case P2P_HELLO: + *size = ISIS_P2PHELLO_HDRLEN; + break; + case L1_LINK_STATE: + case L2_LINK_STATE: + *size = ISIS_LSP_HDR_LEN; + break; + case L1_COMPLETE_SEQ_NUM: + case L2_COMPLETE_SEQ_NUM: + *size = ISIS_CSNP_HDRLEN; + break; + case L1_PARTIAL_SEQ_NUM: + case L2_PARTIAL_SEQ_NUM: + *size = ISIS_PSNP_HDRLEN; + break; + default: + return 1; } - - /* Sanity check - FIXME: move to correct place */ - if ((stream_get_endp(circuit->rcv_stream) - - stream_get_getp(circuit->rcv_stream)) - < ISIS_CSNP_HDRLEN) { - zlog_warn("Packet too short ( < %d)", ISIS_CSNP_HDRLEN); - return ISIS_WARNING; - } - - return process_snp(ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa); -} - -static int process_psnp(int level, struct isis_circuit *circuit, - const u_char *ssnpa) -{ - if (isis->debugs & DEBUG_SNP_PACKETS) { - zlog_debug( - "ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u", - circuit->area->area_tag, level, - circuit->interface->name, - circuit_t2string(circuit->is_type), - circuit->circuit_id); - if (isis->debugs & DEBUG_PACKET_DUMP) - zlog_dump_data(STREAM_DATA(circuit->rcv_stream), - stream_get_endp(circuit->rcv_stream)); - } - - if ((stream_get_endp(circuit->rcv_stream) - - stream_get_getp(circuit->rcv_stream)) - < ISIS_PSNP_HDRLEN) { - zlog_warn("Packet too short ( < %d)", ISIS_PSNP_HDRLEN); - return ISIS_WARNING; - } - - return process_snp(ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa); + *size += ISIS_FIXED_HDR_LEN; + return 0; } /* @@ -1894,53 +1320,68 @@ static int process_psnp(int level, struct isis_circuit *circuit, static int isis_handle_pdu(struct isis_circuit *circuit, u_char *ssnpa) { - struct isis_fixed_hdr *hdr; - int retval = ISIS_OK; - /* - * Let's first read data from stream to the header - */ - hdr = (struct isis_fixed_hdr *)STREAM_DATA(circuit->rcv_stream); - - if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS)) { - zlog_err("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp); + /* Verify that at least the 8 bytes fixed header have been received */ + if (stream_get_endp(circuit->rcv_stream) < ISIS_FIXED_HDR_LEN) { + zlog_err("PDU is too short to be IS-IS."); return ISIS_ERROR; } - /* now we need to know if this is an ISO 9542 packet and - * take real good care of it, waaa! - */ - if (hdr->idrp == ISO9542_ESIS) { - zlog_err("No support for ES-IS packet IDRP=%02x", hdr->idrp); - return ISIS_ERROR; - } - stream_set_getp(circuit->rcv_stream, ISIS_FIXED_HDR_LEN); + uint8_t idrp = stream_getc(circuit->rcv_stream); + uint8_t length = stream_getc(circuit->rcv_stream); + uint8_t version1 = stream_getc(circuit->rcv_stream); + uint8_t id_len = stream_getc(circuit->rcv_stream); + uint8_t pdu_type = stream_getc(circuit->rcv_stream) + & 0x1f; /* bits 6-8 are reserved */ + uint8_t version2 = stream_getc(circuit->rcv_stream); + stream_forward_getp(circuit->rcv_stream, 1); /* reserved */ + uint8_t max_area_addrs = stream_getc(circuit->rcv_stream); - /* - * and then process it - */ - - if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN) { - zlog_err("Fixed header length = %d", hdr->length); + if (idrp == ISO9542_ESIS) { + zlog_err("No support for ES-IS packet IDRP=%" PRIx8, idrp); return ISIS_ERROR; } - if (hdr->version1 != 1) { - zlog_warn("Unsupported ISIS version %u", hdr->version1); + if (idrp != ISO10589_ISIS) { + zlog_err("Not an IS-IS packet IDRP=%" PRIx8, idrp); + return ISIS_ERROR; + } + + if (version1 != 1) { + zlog_warn("Unsupported ISIS version %" PRIu8, version1); return ISIS_WARNING; } - /* either 6 or 0 */ - if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN)) { + + if (id_len != 0 && id_len != ISIS_SYS_ID_LEN) { zlog_err( - "IDFieldLengthMismatch: ID Length field in a received PDU %u, " - "while the parameter for this IS is %u", - hdr->id_len, ISIS_SYS_ID_LEN); + "IDFieldLengthMismatch: ID Length field in a received PDU %" PRIu8 + ", while the parameter for this IS is %u", + id_len, ISIS_SYS_ID_LEN); return ISIS_ERROR; } - if (hdr->version2 != 1) { - zlog_warn("Unsupported ISIS version %u", hdr->version2); + uint8_t expected_length; + if (pdu_size(pdu_type, &expected_length)) { + zlog_warn("Unsupported ISIS PDU %" PRIu8, pdu_type); + return ISIS_WARNING; + } + + if (length != expected_length) { + zlog_err("Exepected fixed header length = %" PRIu8 + " but got %" PRIu8, + expected_length, length); + return ISIS_ERROR; + } + + if (stream_get_endp(circuit->rcv_stream) < length) { + zlog_err( + "PDU is too short to contain fixed header of given PDU type."); + return ISIS_ERROR; + } + + if (version2 != 1) { + zlog_warn("Unsupported ISIS PDU version %" PRIu8, version2); return ISIS_WARNING; } @@ -1951,42 +1392,29 @@ static int isis_handle_pdu(struct isis_circuit *circuit, u_char *ssnpa) } /* either 3 or 0 */ - if ((hdr->max_area_addrs != 0) - && (hdr->max_area_addrs != isis->max_area_addrs)) { + if (max_area_addrs != 0 && max_area_addrs != isis->max_area_addrs) { zlog_err( - "maximumAreaAddressesMismatch: maximumAreaAdresses in a " - "received PDU %u while the parameter for this IS is %u", - hdr->max_area_addrs, isis->max_area_addrs); + "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %" PRIu8 + " while the parameter for this IS is %u", + max_area_addrs, isis->max_area_addrs); return ISIS_ERROR; } - switch (hdr->pdu_type) { + switch (pdu_type) { case L1_LAN_HELLO: - retval = process_lan_hello(ISIS_LEVEL1, circuit, ssnpa); - break; case L2_LAN_HELLO: - retval = process_lan_hello(ISIS_LEVEL2, circuit, ssnpa); - break; case P2P_HELLO: - retval = process_p2p_hello(circuit); + retval = process_hello(pdu_type, circuit, ssnpa); break; case L1_LINK_STATE: - retval = process_lsp(ISIS_LEVEL1, circuit, ssnpa); - break; case L2_LINK_STATE: - retval = process_lsp(ISIS_LEVEL2, circuit, ssnpa); + retval = process_lsp(pdu_type, circuit, ssnpa); break; case L1_COMPLETE_SEQ_NUM: - retval = process_csnp(ISIS_LEVEL1, circuit, ssnpa); - break; case L2_COMPLETE_SEQ_NUM: - retval = process_csnp(ISIS_LEVEL2, circuit, ssnpa); - break; case L1_PARTIAL_SEQ_NUM: - retval = process_psnp(ISIS_LEVEL1, circuit, ssnpa); - break; case L2_PARTIAL_SEQ_NUM: - retval = process_psnp(ISIS_LEVEL2, circuit, ssnpa); + retval = process_snp(pdu_type, circuit, ssnpa); break; default: return ISIS_ERROR; @@ -2025,73 +1453,67 @@ int isis_receive(struct thread *thread) return retval; } -/* filling of the fixed isis header */ -void fill_fixed_hdr(struct isis_fixed_hdr *hdr, u_char pdu_type) -{ - memset(hdr, 0, sizeof(struct isis_fixed_hdr)); - - hdr->idrp = ISO10589_ISIS; - - switch (pdu_type) { - case L1_LAN_HELLO: - case L2_LAN_HELLO: - hdr->length = ISIS_LANHELLO_HDRLEN; - break; - case P2P_HELLO: - hdr->length = ISIS_P2PHELLO_HDRLEN; - break; - case L1_LINK_STATE: - case L2_LINK_STATE: - hdr->length = ISIS_LSP_HDR_LEN; - break; - case L1_COMPLETE_SEQ_NUM: - case L2_COMPLETE_SEQ_NUM: - hdr->length = ISIS_CSNP_HDRLEN; - break; - case L1_PARTIAL_SEQ_NUM: - case L2_PARTIAL_SEQ_NUM: - hdr->length = ISIS_PSNP_HDRLEN; - break; - default: - zlog_warn("fill_fixed_hdr(): unknown pdu type %d", pdu_type); - return; - } - hdr->length += ISIS_FIXED_HDR_LEN; - hdr->pdu_type = pdu_type; - hdr->version1 = 1; - hdr->id_len = 0; /* ISIS_SYS_ID_LEN - 0==6 */ - hdr->version2 = 1; - hdr->max_area_addrs = 0; /* isis->max_area_addrs - 0==3 */ -} - /* * SEND SIDE */ -static void fill_fixed_hdr_andstream(struct isis_fixed_hdr *hdr, - u_char pdu_type, struct stream *stream) +void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream) { - fill_fixed_hdr(hdr, pdu_type); + uint8_t length; - stream_putc(stream, hdr->idrp); - stream_putc(stream, hdr->length); - stream_putc(stream, hdr->version1); - stream_putc(stream, hdr->id_len); - stream_putc(stream, hdr->pdu_type); - stream_putc(stream, hdr->version2); - stream_putc(stream, hdr->reserved); - stream_putc(stream, hdr->max_area_addrs); + if (pdu_size(pdu_type, &length)) + assert(!"Unknown PDU Type"); - return; + stream_putc(stream, ISO10589_ISIS); /* IDRP */ + stream_putc(stream, length); /* Length of fixed header */ + stream_putc(stream, 1); /* Version/Protocol ID Extension 1 */ + stream_putc(stream, 0); /* ID Length, 0 => 6 */ + stream_putc(stream, pdu_type); + stream_putc(stream, 1); /* Subversion */ + stream_putc(stream, 0); /* Reserved */ + stream_putc(stream, 0); /* Max Area Addresses 0 => 3 */ +} + +static void put_hello_hdr(struct isis_circuit *circuit, int level, + size_t *len_pointer) +{ + uint8_t pdu_type; + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + pdu_type = (level == IS_LEVEL_1) ? L1_LAN_HELLO : L2_LAN_HELLO; + else + pdu_type = P2P_HELLO; + + isis_circuit_stream(circuit, &circuit->snd_stream); + fill_fixed_hdr(pdu_type, circuit->snd_stream); + + stream_putc(circuit->snd_stream, circuit->is_type); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); + + uint32_t holdtime = circuit->hello_multiplier[level - 1] + * circuit->hello_interval[level - 1]; + + if (holdtime > 0xffff) + holdtime = 0xffff; + + stream_putw(circuit->snd_stream, holdtime); + *len_pointer = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); /* length is filled in later */ + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + u_char *desig_is = (level == IS_LEVEL_1) + ? circuit->u.bc.l1_desig_is + : circuit->u.bc.l2_desig_is; + stream_putc(circuit->snd_stream, circuit->priority[level - 1]); + stream_put(circuit->snd_stream, desig_is, ISIS_SYS_ID_LEN + 1); + } else { + stream_putc(circuit->snd_stream, circuit->circuit_id); + } } int send_hello(struct isis_circuit *circuit, int level) { - struct isis_fixed_hdr fixed_hdr; - struct isis_lan_hello_hdr hello_hdr; - struct isis_p2p_hello_hdr p2p_hello_hdr; - unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; - size_t len_pointer, length, auth_tlv_offset = 0; - u_int32_t interval; + size_t len_pointer; int retval; if (circuit->is_passive) @@ -2102,113 +1524,21 @@ int send_hello(struct isis_circuit *circuit, int level) return ISIS_WARNING; } - isis_circuit_stream(circuit, &circuit->snd_stream); + put_hello_hdr(circuit, level, &len_pointer); + + struct isis_tlvs *tlvs = isis_alloc_tlvs(); + + isis_tlvs_add_auth(tlvs, &circuit->passwd); + + if (!listcount(circuit->area->area_addrs)) + return ISIS_WARNING; + isis_tlvs_add_area_addresses(tlvs, circuit->area->area_addrs); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - if (level == IS_LEVEL_1) - fill_fixed_hdr_andstream(&fixed_hdr, L1_LAN_HELLO, - circuit->snd_stream); - else - fill_fixed_hdr_andstream(&fixed_hdr, L2_LAN_HELLO, - circuit->snd_stream); - else - fill_fixed_hdr_andstream(&fixed_hdr, P2P_HELLO, - circuit->snd_stream); + isis_tlvs_add_lan_neighbors( + tlvs, circuit->u.bc.lan_neighs[level - 1]); - /* - * Fill LAN Level 1 or 2 Hello PDU header - */ - memset(&hello_hdr, 0, sizeof(struct isis_lan_hello_hdr)); - interval = circuit->hello_multiplier[level - 1] - * circuit->hello_interval[level - 1]; - if (interval > USHRT_MAX) - interval = USHRT_MAX; - hello_hdr.circuit_t = circuit->is_type; - memcpy(hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN); - hello_hdr.hold_time = htons((u_int16_t)interval); - - hello_hdr.pdu_len = 0; /* Update the PDU Length later */ - len_pointer = - stream_get_endp(circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN; - - /* copy the shared part of the hello to the p2p hello if needed */ - if (circuit->circ_type == CIRCUIT_T_P2P) { - memcpy(&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN); - p2p_hello_hdr.local_id = circuit->circuit_id; - /* FIXME: need better understanding */ - stream_put(circuit->snd_stream, &p2p_hello_hdr, - ISIS_P2PHELLO_HDRLEN); - } else { - hello_hdr.prio = circuit->priority[level - 1]; - if (level == IS_LEVEL_1) { - memcpy(hello_hdr.lan_id, circuit->u.bc.l1_desig_is, - ISIS_SYS_ID_LEN + 1); - } else if (level == IS_LEVEL_2) { - memcpy(hello_hdr.lan_id, circuit->u.bc.l2_desig_is, - ISIS_SYS_ID_LEN + 1); - } - stream_put(circuit->snd_stream, &hello_hdr, - ISIS_LANHELLO_HDRLEN); - } - - /* - * Then the variable length part. - */ - - /* add circuit password */ - switch (circuit->passwd.type) { - /* Cleartext */ - case ISIS_PASSWD_TYPE_CLEARTXT: - if (tlv_add_authinfo(circuit->passwd.type, circuit->passwd.len, - circuit->passwd.passwd, - circuit->snd_stream)) - return ISIS_WARNING; - break; - - /* HMAC MD5 */ - case ISIS_PASSWD_TYPE_HMAC_MD5: - /* Remember where TLV is written so we can later overwrite the - * MD5 hash */ - auth_tlv_offset = stream_get_endp(circuit->snd_stream); - memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); - if (tlv_add_authinfo(circuit->passwd.type, ISIS_AUTH_MD5_SIZE, - hmac_md5_hash, circuit->snd_stream)) - return ISIS_WARNING; - break; - - default: - break; - } - - /* Area Addresses TLV */ - if (listcount(circuit->area->area_addrs) == 0) - return ISIS_WARNING; - if (tlv_add_area_addrs(circuit->area->area_addrs, circuit->snd_stream)) - return ISIS_WARNING; - - /* LAN Neighbors TLV */ - if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - if (level == IS_LEVEL_1 && circuit->u.bc.lan_neighs[0] - && listcount(circuit->u.bc.lan_neighs[0]) > 0) - if (tlv_add_lan_neighs(circuit->u.bc.lan_neighs[0], - circuit->snd_stream)) - return ISIS_WARNING; - if (level == IS_LEVEL_2 && circuit->u.bc.lan_neighs[1] - && listcount(circuit->u.bc.lan_neighs[1]) > 0) - if (tlv_add_lan_neighs(circuit->u.bc.lan_neighs[1], - circuit->snd_stream)) - return ISIS_WARNING; - } - - /* Protocols Supported TLV */ - if (circuit->nlpids.count > 0) - if (tlv_add_nlpid(&circuit->nlpids, circuit->snd_stream)) - return ISIS_WARNING; - /* IP interface Address TLV */ - if (circuit->ip_router && circuit->ip_addrs - && listcount(circuit->ip_addrs) > 0) - if (tlv_add_ip_addrs(circuit->ip_addrs, circuit->snd_stream)) - return ISIS_WARNING; + isis_tlvs_set_protocols_supported(tlvs, &circuit->nlpids); /* * MT Supported TLV @@ -2221,48 +1551,26 @@ int send_hello(struct isis_circuit *circuit, int level) 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); + if (mt_count == 0 && area_is_mt(circuit->area)) { + tlvs->mt_router_info_empty = true; + } else if ((mt_count == 1 + && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST) + || (mt_count > 1)) { + for (unsigned int i = 0; i < mt_count; i++) + isis_tlvs_add_mt_router_info(tlvs, mt_settings[i]->mtid, + false, false); } - /* IPv6 Interface Address TLV */ - if (circuit->ipv6_router && circuit->ipv6_link - && listcount(circuit->ipv6_link) > 0) - if (tlv_add_ipv6_addrs(circuit->ipv6_link, circuit->snd_stream)) - return ISIS_WARNING; + if (circuit->ip_router && circuit->ip_addrs) + isis_tlvs_add_ipv4_addresses(tlvs, circuit->ip_addrs); - if (circuit->pad_hellos) - if (tlv_add_padding(circuit->snd_stream)) - return ISIS_WARNING; + if (circuit->ipv6_router && circuit->ipv6_link) + isis_tlvs_add_ipv6_addresses(tlvs, circuit->ipv6_link); - length = stream_get_endp(circuit->snd_stream); - /* Update PDU length */ - stream_putw_at(circuit->snd_stream, len_pointer, (u_int16_t)length); - - /* For HMAC MD5 we need to compute the md5 hash and store it */ - if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) { - hmac_md5(STREAM_DATA(circuit->snd_stream), - stream_get_endp(circuit->snd_stream), - (unsigned char *)&circuit->passwd.passwd, - circuit->passwd.len, (unsigned char *)&hmac_md5_hash); - /* Copy the hash into the stream */ - memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3, - hmac_md5_hash, ISIS_AUTH_MD5_SIZE); + if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, + circuit->pad_hellos, false)) { + isis_free_tlvs(tlvs); + return ISIS_WARNING; /* XXX: Maybe Log TLV structure? */ } if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -2270,18 +1578,22 @@ int send_hello(struct isis_circuit *circuit, int level) zlog_debug( "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd", circuit->area->area_tag, level, - circuit->interface->name, length); + circuit->interface->name, + stream_get_endp(circuit->snd_stream)); } else { zlog_debug( "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd", circuit->area->area_tag, - circuit->interface->name, length); + circuit->interface->name, + stream_get_endp(circuit->snd_stream)); } if (isis->debugs & DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); } + isis_free_tlvs(tlvs); + retval = circuit->tx(circuit, level); if (retval != ISIS_OK) zlog_err("ISIS-Adj (%s): Send L%d IIH on %s failed", @@ -2366,101 +1678,10 @@ int send_p2p_hello(struct thread *thread) return ISIS_OK; } -static int build_csnp(int level, u_char *start, u_char *stop, struct list *lsps, - struct isis_circuit *circuit) -{ - struct isis_fixed_hdr fixed_hdr; - struct isis_passwd *passwd; - unsigned long lenp; - u_int16_t length; - unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; - unsigned long auth_tlv_offset = 0; - int retval = ISIS_OK; - - isis_circuit_stream(circuit, &circuit->snd_stream); - - if (level == IS_LEVEL_1) - fill_fixed_hdr_andstream(&fixed_hdr, L1_COMPLETE_SEQ_NUM, - circuit->snd_stream); - else - fill_fixed_hdr_andstream(&fixed_hdr, L2_COMPLETE_SEQ_NUM, - circuit->snd_stream); - - /* - * Fill Level 1 or 2 Complete Sequence Numbers header - */ - - lenp = stream_get_endp(circuit->snd_stream); - stream_putw(circuit->snd_stream, 0); /* PDU length - when we know it */ - /* no need to send the source here, it is always us if we csnp */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); - /* with zero circuit id - ref 9.10, 9.11 */ - stream_putc(circuit->snd_stream, 0x00); - - stream_put(circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2); - stream_put(circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2); - - /* - * And TLVs - */ - if (level == IS_LEVEL_1) - passwd = &circuit->area->area_passwd; - else - passwd = &circuit->area->domain_passwd; - - if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) { - switch (passwd->type) { - /* Cleartext */ - case ISIS_PASSWD_TYPE_CLEARTXT: - if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT, - passwd->len, passwd->passwd, - circuit->snd_stream)) - return ISIS_WARNING; - break; - - /* HMAC MD5 */ - case ISIS_PASSWD_TYPE_HMAC_MD5: - /* Remember where TLV is written so we can later - * overwrite the MD5 hash */ - auth_tlv_offset = stream_get_endp(circuit->snd_stream); - memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); - if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5, - ISIS_AUTH_MD5_SIZE, hmac_md5_hash, - circuit->snd_stream)) - return ISIS_WARNING; - break; - - default: - break; - } - } - - retval = tlv_add_lsp_entries(lsps, circuit->snd_stream); - if (retval != ISIS_OK) - return retval; - - length = (u_int16_t)stream_get_endp(circuit->snd_stream); - /* Update PU length */ - stream_putw_at(circuit->snd_stream, lenp, length); - - /* For HMAC MD5 we need to compute the md5 hash and store it */ - if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) - && passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) { - hmac_md5(STREAM_DATA(circuit->snd_stream), - stream_get_endp(circuit->snd_stream), - (unsigned char *)&passwd->passwd, passwd->len, - (unsigned char *)&hmac_md5_hash); - /* Copy the hash into the stream */ - memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3, - hmac_md5_hash, ISIS_AUTH_MD5_SIZE); - } - - return retval; -} - /* * Count the maximum number of lsps that can be accomodated by a given size. */ +#define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN) static uint16_t get_max_lsp_count(uint16_t size) { uint16_t tlv_count; @@ -2479,109 +1700,81 @@ static uint16_t get_max_lsp_count(uint16_t size) return lsp_count; } -/* - * Calculate the length of Authentication Info. TLV. - */ -static uint16_t auth_tlv_length(int level, struct isis_circuit *circuit) -{ - struct isis_passwd *passwd; - uint16_t length; - - if (level == IS_LEVEL_1) - passwd = &circuit->area->area_passwd; - else - passwd = &circuit->area->domain_passwd; - - /* Also include the length of TLV header */ - length = AUTH_INFO_HDRLEN; - if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) { - switch (passwd->type) { - /* Cleartext */ - case ISIS_PASSWD_TYPE_CLEARTXT: - length += passwd->len; - break; - - /* HMAC MD5 */ - case ISIS_PASSWD_TYPE_HMAC_MD5: - length += ISIS_AUTH_MD5_SIZE; - break; - - default: - break; - } - } - - return length; -} - -/* - * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP. - */ -static uint16_t max_lsps_per_snp(int snp_type, int level, - struct isis_circuit *circuit) -{ - int snp_hdr_len; - int auth_tlv_len; - uint16_t lsp_count; - - snp_hdr_len = ISIS_FIXED_HDR_LEN; - if (snp_type == ISIS_SNP_CSNP_FLAG) - snp_hdr_len += ISIS_CSNP_HDRLEN; - else - snp_hdr_len += ISIS_PSNP_HDRLEN; - - auth_tlv_len = auth_tlv_length(level, circuit); - lsp_count = get_max_lsp_count(stream_get_size(circuit->snd_stream) - - snp_hdr_len - auth_tlv_len); - return lsp_count; -} - -/* - * FIXME: support multiple CSNPs - */ - int send_csnp(struct isis_circuit *circuit, int level) { - u_char start[ISIS_SYS_ID_LEN + 2]; - u_char stop[ISIS_SYS_ID_LEN + 2]; - struct list *list = NULL; - struct listnode *node; - struct isis_lsp *lsp; - u_char num_lsps, loop = 1; - int i, retval = ISIS_OK; - if (circuit->area->lspdb[level - 1] == NULL || dict_count(circuit->area->lspdb[level - 1]) == 0) - return retval; + return ISIS_OK; + isis_circuit_stream(circuit, &circuit->snd_stream); + fill_fixed_hdr((level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM + : L2_COMPLETE_SEQ_NUM, + circuit->snd_stream); + + size_t len_pointer = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); + stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + /* with zero circuit id - ref 9.10, 9.11 */ + stream_putc(circuit->snd_stream, 0); + + size_t start_pointer = stream_get_endp(circuit->snd_stream); + stream_put(circuit->snd_stream, 0, ISIS_SYS_ID_LEN + 2); + size_t end_pointer = stream_get_endp(circuit->snd_stream); + stream_put(circuit->snd_stream, 0, ISIS_SYS_ID_LEN + 2); + + struct isis_passwd *passwd = (level == ISIS_LEVEL1) + ? &circuit->area->area_passwd + : &circuit->area->domain_passwd; + + struct isis_tlvs *tlvs = isis_alloc_tlvs(); + + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) + isis_tlvs_add_auth(tlvs, passwd); + + size_t tlv_start = stream_get_endp(circuit->snd_stream); + if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, false, + false)) { + isis_free_tlvs(tlvs); + return ISIS_WARNING; + } + isis_free_tlvs(tlvs); + + uint16_t num_lsps = + get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream)); + + uint8_t start[ISIS_SYS_ID_LEN + 2]; memset(start, 0x00, ISIS_SYS_ID_LEN + 2); + uint8_t stop[ISIS_SYS_ID_LEN + 2]; memset(stop, 0xff, ISIS_SYS_ID_LEN + 2); - num_lsps = max_lsps_per_snp(ISIS_SNP_CSNP_FLAG, level, circuit); - + bool loop = true; while (loop) { - list = list_new(); - lsp_build_list(start, stop, num_lsps, list, - circuit->area->lspdb[level - 1]); + tlvs = isis_alloc_tlvs(); + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) + isis_tlvs_add_auth(tlvs, passwd); + + struct isis_lsp *last_lsp; + isis_tlvs_add_csnp_entries(tlvs, start, stop, num_lsps, + circuit->area->lspdb[level - 1], + &last_lsp); /* * Update the stop lsp_id before encoding this CSNP. */ - if (listcount(list) < num_lsps) { + if (tlvs->lsp_entries.count < num_lsps) { memset(stop, 0xff, ISIS_SYS_ID_LEN + 2); } else { - node = listtail(list); - lsp = listgetdata(node); - memcpy(stop, lsp->lsp_header->lsp_id, - ISIS_SYS_ID_LEN + 2); + memcpy(stop, last_lsp->hdr.lsp_id, sizeof(stop)); } - retval = build_csnp(level, start, stop, list, circuit); - if (retval != ISIS_OK) { - zlog_err("ISIS-Snp (%s): Build L%d CSNP on %s failed", - circuit->area->area_tag, level, - circuit->interface->name); - list_delete(list); - return retval; + memcpy(STREAM_DATA(circuit->snd_stream) + start_pointer, start, + ISIS_SYS_ID_LEN + 2); + memcpy(STREAM_DATA(circuit->snd_stream) + end_pointer, stop, + ISIS_SYS_ID_LEN + 2); + stream_set_endp(circuit->snd_stream, tlv_start); + if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, + false, false)) { + isis_free_tlvs(tlvs); + return ISIS_WARNING; } if (isis->debugs & DEBUG_SNP_PACKETS) { @@ -2590,28 +1783,20 @@ int send_csnp(struct isis_circuit *circuit, int level) circuit->area->area_tag, level, circuit->interface->name, stream_get_endp(circuit->snd_stream)); - for (ALL_LIST_ELEMENTS_RO(list, node, lsp)) { - zlog_debug( - "ISIS-Snp (%s): CSNP entry %s, seq 0x%08x," - " cksum 0x%04x, lifetime %us", - circuit->area->area_tag, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime)); - } + log_multiline(LOG_DEBUG, " ", "%s", + isis_format_tlvs(tlvs)); if (isis->debugs & DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); } - retval = circuit->tx(circuit, level); + int retval = circuit->tx(circuit, level); if (retval != ISIS_OK) { zlog_err("ISIS-Snp (%s): Send L%d CSNP on %s failed", circuit->area->area_tag, level, circuit->interface->name); - list_delete(list); + isis_free_tlvs(tlvs); return retval; } @@ -2621,7 +1806,7 @@ int send_csnp(struct isis_circuit *circuit, int level) */ memcpy(start, stop, ISIS_SYS_ID_LEN + 2); loop = 0; - for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) { + for (int i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) { if (start[i] < (u_char)0xff) { start[i] += 1; loop = 1; @@ -2629,10 +1814,10 @@ int send_csnp(struct isis_circuit *circuit, int level) } } memset(stop, 0xff, ISIS_SYS_ID_LEN + 2); - list_delete(list); + isis_free_tlvs(tlvs); } - return retval; + return ISIS_OK; } int send_l1_csnp(struct thread *thread) @@ -2679,120 +1864,12 @@ int send_l2_csnp(struct thread *thread) return retval; } -static int build_psnp(int level, struct isis_circuit *circuit, - struct list *lsps) -{ - struct isis_fixed_hdr fixed_hdr; - unsigned long lenp; - u_int16_t length; - struct isis_lsp *lsp; - struct isis_passwd *passwd; - struct listnode *node; - unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; - unsigned long auth_tlv_offset = 0; - int retval = ISIS_OK; - - isis_circuit_stream(circuit, &circuit->snd_stream); - - if (level == IS_LEVEL_1) - fill_fixed_hdr_andstream(&fixed_hdr, L1_PARTIAL_SEQ_NUM, - circuit->snd_stream); - else - fill_fixed_hdr_andstream(&fixed_hdr, L2_PARTIAL_SEQ_NUM, - circuit->snd_stream); - - /* - * Fill Level 1 or 2 Partial Sequence Numbers header - */ - lenp = stream_get_endp(circuit->snd_stream); - stream_putw(circuit->snd_stream, 0); /* PDU length - when we know it */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); - stream_putc(circuit->snd_stream, circuit->idx); - - /* - * And TLVs - */ - - if (level == IS_LEVEL_1) - passwd = &circuit->area->area_passwd; - else - passwd = &circuit->area->domain_passwd; - - if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) { - switch (passwd->type) { - /* Cleartext */ - case ISIS_PASSWD_TYPE_CLEARTXT: - if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT, - passwd->len, passwd->passwd, - circuit->snd_stream)) - return ISIS_WARNING; - break; - - /* HMAC MD5 */ - case ISIS_PASSWD_TYPE_HMAC_MD5: - /* Remember where TLV is written so we can later - * overwrite the MD5 hash */ - auth_tlv_offset = stream_get_endp(circuit->snd_stream); - memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); - if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5, - ISIS_AUTH_MD5_SIZE, hmac_md5_hash, - circuit->snd_stream)) - return ISIS_WARNING; - break; - - default: - break; - } - } - - retval = tlv_add_lsp_entries(lsps, circuit->snd_stream); - if (retval != ISIS_OK) - return retval; - - if (isis->debugs & DEBUG_SNP_PACKETS) { - for (ALL_LIST_ELEMENTS_RO(lsps, node, lsp)) { - zlog_debug( - "ISIS-Snp (%s): PSNP entry %s, seq 0x%08x," - " cksum 0x%04x, lifetime %us", - circuit->area->area_tag, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime)); - } - } - - length = (u_int16_t)stream_get_endp(circuit->snd_stream); - /* Update PDU length */ - stream_putw_at(circuit->snd_stream, lenp, length); - - /* For HMAC MD5 we need to compute the md5 hash and store it */ - if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) - && passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) { - hmac_md5(STREAM_DATA(circuit->snd_stream), - stream_get_endp(circuit->snd_stream), - (unsigned char *)&passwd->passwd, passwd->len, - (unsigned char *)&hmac_md5_hash); - /* Copy the hash into the stream */ - memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3, - hmac_md5_hash, ISIS_AUTH_MD5_SIZE); - } - - return ISIS_OK; -} - /* * 7.3.15.4 action on expiration of partial SNP interval * level 1 */ static int send_psnp(int level, struct isis_circuit *circuit) { - struct isis_lsp *lsp; - struct list *list = NULL; - struct listnode *node; - u_char num_lsps; - int retval = ISIS_OK; - if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[level - 1]) return ISIS_OK; @@ -2804,25 +1881,64 @@ static int send_psnp(int level, struct isis_circuit *circuit) if (!circuit->snd_stream) return ISIS_ERROR; - num_lsps = max_lsps_per_snp(ISIS_SNP_PSNP_FLAG, level, circuit); + isis_circuit_stream(circuit, &circuit->snd_stream); + fill_fixed_hdr((level == ISIS_LEVEL1) ? L1_PARTIAL_SEQ_NUM + : L2_PARTIAL_SEQ_NUM, + circuit->snd_stream); + + size_t len_pointer = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); /* length is filled in later */ + stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_putc(circuit->snd_stream, circuit->idx); + + struct isis_passwd *passwd = (level == ISIS_LEVEL1) + ? &circuit->area->area_passwd + : &circuit->area->domain_passwd; + + struct isis_tlvs *tlvs = isis_alloc_tlvs(); + + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) + isis_tlvs_add_auth(tlvs, passwd); + + size_t tlv_start = stream_get_endp(circuit->snd_stream); + if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, false, + false)) { + isis_free_tlvs(tlvs); + return ISIS_WARNING; + } + isis_free_tlvs(tlvs); + + uint16_t num_lsps = + get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream)); while (1) { - list = list_new(); - lsp_build_list_ssn(circuit, num_lsps, list, - circuit->area->lspdb[level - 1]); + tlvs = isis_alloc_tlvs(); + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) + isis_tlvs_add_auth(tlvs, passwd); - if (listcount(list) == 0) { - list_delete(list); + for (dnode_t *dnode = + dict_first(circuit->area->lspdb[level - 1]); + dnode; dnode = dict_next(circuit->area->lspdb[level - 1], + dnode)) { + struct isis_lsp *lsp = dnode_get(dnode); + + if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit)) + isis_tlvs_add_lsp_entry(tlvs, lsp); + + if (tlvs->lsp_entries.count == num_lsps) + break; + } + + if (!tlvs->lsp_entries.count) { + isis_free_tlvs(tlvs); return ISIS_OK; } - retval = build_psnp(level, circuit, list); - if (retval != ISIS_OK) { - zlog_err("ISIS-Snp (%s): Build L%d PSNP on %s failed", - circuit->area->area_tag, level, - circuit->interface->name); - list_delete(list); - return retval; + stream_set_endp(circuit->snd_stream, tlv_start); + if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, + false, false)) { + isis_free_tlvs(tlvs); + return ISIS_WARNING; } if (isis->debugs & DEBUG_SNP_PACKETS) { @@ -2831,18 +1947,20 @@ static int send_psnp(int level, struct isis_circuit *circuit) circuit->area->area_tag, level, circuit->interface->name, stream_get_endp(circuit->snd_stream)); + log_multiline(LOG_DEBUG, " ", "%s", + isis_format_tlvs(tlvs)); if (isis->debugs & DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); } - retval = circuit->tx(circuit, level); + int retval = circuit->tx(circuit, level); if (retval != ISIS_OK) { zlog_err("ISIS-Snp (%s): Send L%d PSNP on %s failed", circuit->area->area_tag, level, circuit->interface->name); - list_delete(list); + isis_free_tlvs(tlvs); return retval; } @@ -2850,12 +1968,15 @@ static int send_psnp(int level, struct isis_circuit *circuit) * sending succeeded, we can clear SSN flags of this circuit * for the LSPs in list */ - for (ALL_LIST_ELEMENTS_RO(list, node, lsp)) - ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); - list_delete(list); + struct isis_lsp_entry *entry_head; + entry_head = (struct isis_lsp_entry *)tlvs->lsp_entries.head; + for (struct isis_lsp_entry *entry = entry_head; entry; + entry = entry->next) + ISIS_CLEAR_FLAG(entry->lsp->SSNflags, circuit); + isis_free_tlvs(tlvs); } - return retval; + return ISIS_OK; } int send_l1_psnp(struct thread *thread) @@ -2963,14 +2084,12 @@ int send_lsp(struct thread *thread) * the circuit's MTU. So handle and log this case here. */ if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) { zlog_err( - "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x," - " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu" - " while interface stream size is %zu.", + "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s on %s. LSP Size is %zu while interface stream size is %zu.", circuit->area->area_tag, lsp->level, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime), + rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, + lsp->hdr.checksum, lsp->hdr.rem_lifetime, circuit->interface->name, stream_get_endp(lsp->pdu), stream_get_size(circuit->snd_stream)); if (isis->debugs & DEBUG_PACKET_DUMP) @@ -2984,15 +2103,13 @@ int send_lsp(struct thread *thread) stream_copy(circuit->snd_stream, lsp->pdu); if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug( - "ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x," - " lifetime %us on %s", - circuit->area->area_tag, lsp->level, - rawlspid_print(lsp->lsp_header->lsp_id), - ntohl(lsp->lsp_header->seq_num), - ntohs(lsp->lsp_header->checksum), - ntohs(lsp->lsp_header->rem_lifetime), - circuit->interface->name); + zlog_debug("ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 + "s on %s", + circuit->area->area_tag, lsp->level, + rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, + lsp->hdr.checksum, lsp->hdr.rem_lifetime, + circuit->interface->name); if (isis->debugs & DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); @@ -3026,47 +2143,3 @@ out: return retval; } - -int ack_lsp(struct isis_link_state_hdr *hdr, struct isis_circuit *circuit, - int level) -{ - unsigned long lenp; - int retval; - u_int16_t length; - struct isis_fixed_hdr fixed_hdr; - - isis_circuit_stream(circuit, &circuit->snd_stream); - - // fill_llc_hdr (stream); - if (level == IS_LEVEL_1) - fill_fixed_hdr_andstream(&fixed_hdr, L1_PARTIAL_SEQ_NUM, - circuit->snd_stream); - else - fill_fixed_hdr_andstream(&fixed_hdr, L2_PARTIAL_SEQ_NUM, - circuit->snd_stream); - - - lenp = stream_get_endp(circuit->snd_stream); - stream_putw(circuit->snd_stream, 0); /* PDU length */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); - stream_putc(circuit->snd_stream, circuit->idx); - stream_putc(circuit->snd_stream, 9); /* code */ - stream_putc(circuit->snd_stream, 16); /* len */ - - stream_putw(circuit->snd_stream, ntohs(hdr->rem_lifetime)); - stream_put(circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2); - stream_putl(circuit->snd_stream, ntohl(hdr->seq_num)); - stream_putw(circuit->snd_stream, ntohs(hdr->checksum)); - - length = (u_int16_t)stream_get_endp(circuit->snd_stream); - /* Update PDU length */ - stream_putw_at(circuit->snd_stream, lenp, length); - - retval = circuit->tx(circuit, level); - if (retval != ISIS_OK) - zlog_err("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed", - circuit->area->area_tag, level, - circuit->interface->name); - - return retval; -} diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h index fa8006cda0..7096761879 100644 --- a/isisd/isis_pdu.h +++ b/isisd/isis_pdu.h @@ -65,36 +65,6 @@ struct esis_fixed_hdr { #define ISH_PDU 4 #define RD_PDU 5 -/* - * IS to IS Fixed Header - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Intradomain Routeing Protocol Discriminator | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Length Indicator | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Version/Protocol ID extension | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | R | R | R | PDU Type | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Version | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Reserved | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Maximum Area Addresses | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - */ - -struct isis_fixed_hdr { - u_char idrp; - u_char length; - u_char version1; - u_char id_len; - u_char pdu_type; - u_char version2; - u_char reserved; - u_char max_area_addrs; -} __attribute__((packed)); - #define ISIS_FIXED_HDR_LEN 8 /* @@ -155,30 +125,14 @@ struct isis_p2p_hello_hdr { #define L1_LINK_STATE 18 #define L2_LINK_STATE 20 -/* - * L1 and L2 IS to IS link state PDU header - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * + PDU Length + 2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * + Remaining Lifetime + 2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | LSP ID | id_len + 2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * + Sequence Number + 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * + Checksum + 2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | P | ATT |LSPDBOL| ISTYPE | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - */ -struct isis_link_state_hdr { - u_int16_t pdu_len; - u_int16_t rem_lifetime; - u_char lsp_id[ISIS_SYS_ID_LEN + 2]; - u_int32_t seq_num; - u_int16_t checksum; - u_int8_t lsp_bits; -} __attribute__((packed)); +struct isis_lsp_hdr { + uint16_t pdu_len; + uint16_t rem_lifetime; + uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; + uint32_t seqno; + uint16_t checksum; + uint8_t lsp_bits; +}; #define ISIS_LSP_HDR_LEN 19 /* @@ -259,9 +213,7 @@ int send_l2_csnp(struct thread *thread); int send_l1_psnp(struct thread *thread); int send_l2_psnp(struct thread *thread); int send_lsp(struct thread *thread); -int ack_lsp(struct isis_link_state_hdr *hdr, struct isis_circuit *circuit, - int level); -void fill_fixed_hdr(struct isis_fixed_hdr *hdr, u_char pdu_type); +void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream); int send_hello(struct isis_circuit *circuit, int level); #endif /* _ZEBRA_ISIS_PDU_H */ diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 8e329494dd..ea94b65805 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -37,7 +37,6 @@ #include "isisd/isis_flags.h" #include "isisd/isis_misc.h" #include "isisd/isis_circuit.h" -#include "isisd/isis_tlv.h" #include "isisd/isisd.h" #include "isisd/isis_lsp.h" #include "isisd/isis_route.h" diff --git a/isisd/isis_route.c b/isisd/isis_route.c index afc4f65128..267e72002f 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -42,7 +42,6 @@ #include "isis_misc.h" #include "isis_adjacency.h" #include "isis_circuit.h" -#include "isis_tlv.h" #include "isis_pdu.h" #include "isis_lsp.h" #include "isis_spf.h" @@ -208,13 +207,12 @@ static void nexthops6_print(struct list *nhs6) static void adjinfo2nexthop(struct list *nexthops, struct isis_adjacency *adj) { struct isis_nexthop *nh; - struct listnode *node; - struct in_addr *ipv4_addr; - if (adj->ipv4_addrs == NULL) + if (!adj->ipv4_address_count) return; - for (ALL_LIST_ELEMENTS_RO(adj->ipv4_addrs, node, ipv4_addr)) { + for (unsigned int i = 0; i < adj->ipv4_address_count; i++) { + struct in_addr *ipv4_addr = &adj->ipv4_addresses[i]; if (!nexthoplookup(nexthops, ipv4_addr, adj->circuit->interface->ifindex)) { nh = isis_nexthop_create( @@ -227,14 +225,13 @@ static void adjinfo2nexthop(struct list *nexthops, struct isis_adjacency *adj) static void adjinfo2nexthop6(struct list *nexthops6, struct isis_adjacency *adj) { - struct listnode *node; - struct in6_addr *ipv6_addr; struct isis_nexthop6 *nh6; - if (!adj->ipv6_addrs) + if (!adj->ipv6_address_count) return; - for (ALL_LIST_ELEMENTS_RO(adj->ipv6_addrs, node, ipv6_addr)) { + for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { + struct in6_addr *ipv6_addr = &adj->ipv6_addresses[i]; if (!nexthop6lookup(nexthops6, ipv6_addr, adj->circuit->interface->ifindex)) { nh6 = isis_nexthop6_create( diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 44d7fa0403..d92207d57c 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -42,7 +42,6 @@ #include "isis_misc.h" #include "isis_adjacency.h" #include "isis_circuit.h" -#include "isis_tlv.h" #include "isis_pdu.h" #include "isis_lsp.h" #include "isis_spf.h" diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 615c2eeaa2..740f087ee7 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -44,7 +44,6 @@ #include "isis_misc.h" #include "isis_adjacency.h" #include "isis_circuit.h" -#include "isis_tlv.h" #include "isis_pdu.h" #include "isis_lsp.h" #include "isis_dynhn.h" @@ -52,9 +51,24 @@ #include "isis_route.h" #include "isis_csm.h" #include "isis_mt.h" +#include "isis_tlvs.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); +/* + * supports the given af ? + */ +static bool speaks(uint8_t *protocols, uint8_t count, int family) +{ + for (uint8_t i = 0; i < count; i++) { + if (family == AF_INET && protocols[i] == NLPID_IP) + return true; + if (family == AF_INET6 && protocols[i] == NLPID_IPV6) + return true; + } + return false; +} + struct isis_spf_run { struct isis_area *area; int level; @@ -340,7 +354,7 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, LSP_PSEUDO_ID(lspid) = 0; LSP_FRAGMENT(lspid) = 0; lsp = lsp_search(lspid, area->lspdb[level - 1]); - if (lsp && lsp->lsp_header->rem_lifetime != 0) + if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; return NULL; } @@ -546,6 +560,13 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, assert(spftree && parent); + struct prefix p; + if (vtype >= VTYPE_IPREACH_INTERNAL) { + prefix_copy(&p, id); + apply_mask(&p); + id = &p; + } + /* RFC3787 section 5.1 */ if (spftree->area->newmetric == 1) { if (dist > MAX_WIDE_PATH_METRIC) @@ -632,30 +653,35 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, uint16_t depth, u_char *root_sysid, struct isis_vertex *parent) { - bool pseudo_lsp = LSP_PSEUDO_ID(lsp->lsp_header->lsp_id); - struct listnode *node, *fragnode = NULL; + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct listnode *fragnode = NULL; uint32_t dist; - struct is_neigh *is_neigh; - struct te_is_neigh *te_is_neigh; - struct ipv4_reachability *ipreach; - struct te_ipv4_reachability *te_ipv4_reach; enum vertextype vtype; - struct prefix prefix; - struct ipv6_reachability *ip6reach; static const u_char null_sysid[ISIS_SYS_ID_LEN]; - struct mt_router_info *mt_router_info = NULL; + struct isis_mt_router_info *mt_router_info = NULL; + + if (!lsp->tlvs) + return ISIS_OK; if (spftree->mtid != ISIS_MT_IPV4_UNICAST) - mt_router_info = tlvs_lookup_mt_router_info(&lsp->tlv_data, - spftree->mtid); + mt_router_info = isis_tlvs_lookup_mt_router_info(lsp->tlvs, + spftree->mtid); if (!pseudo_lsp && (spftree->mtid == ISIS_MT_IPV4_UNICAST - && !speaks(lsp->tlv_data.nlpids, spftree->family)) + && !speaks(lsp->tlvs->protocols_supported.protocols, + lsp->tlvs->protocols_supported.count, + spftree->family)) && !mt_router_info) return ISIS_OK; + /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ + bool no_overload = (pseudo_lsp + || (spftree->mtid == ISIS_MT_IPV4_UNICAST + && !ISIS_MASK_LSP_OL_BIT(lsp->hdr.lsp_bits)) + || (mt_router_info && !mt_router_info->overload)); + lspfragloop: - if (lsp->lsp_header->seq_num == 0) { + if (lsp->hdr.seqno == 0) { zlog_warn( "isis_spf_process_lsp(): lsp with 0 seq_num - ignore"); return ISIS_WARNING; @@ -663,142 +689,117 @@ lspfragloop: #ifdef EXTREME_DEBUG zlog_debug("ISIS-Spf: process_lsp %s", - print_sys_hostname(lsp->lsp_header->lsp_id)); + print_sys_hostname(lsp->hdr.lsp_id)); #endif /* EXTREME_DEBUG */ - /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ - if (pseudo_lsp || (spftree->mtid == ISIS_MT_IPV4_UNICAST - && !ISIS_MASK_LSP_OL_BIT(lsp->lsp_header->lsp_bits)) - || (mt_router_info && !mt_router_info->overload)) - - { + if (no_overload) { if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { - for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.is_neighs, node, - is_neigh)) { + struct isis_oldstyle_reach *r; + for (r = (struct isis_oldstyle_reach *) + lsp->tlvs->oldstyle_reach.head; + r; r = r->next) { /* C.2.6 a) */ /* Two way connectivity */ - if (!memcmp(is_neigh->neigh_id, root_sysid, - ISIS_SYS_ID_LEN)) + if (!memcmp(r->id, root_sysid, ISIS_SYS_ID_LEN)) continue; if (!pseudo_lsp - && !memcmp(is_neigh->neigh_id, null_sysid, + && !memcmp(r->id, null_sysid, ISIS_SYS_ID_LEN)) continue; - dist = cost + is_neigh->metrics.metric_default; + dist = cost + r->metric; process_N(spftree, - LSP_PSEUDO_ID(is_neigh->neigh_id) + LSP_PSEUDO_ID(r->id) ? VTYPE_PSEUDO_IS : VTYPE_NONPSEUDO_IS, - (void *)is_neigh->neigh_id, dist, - depth + 1, parent); + (void *)r->id, dist, depth + 1, + parent); } } - struct list *te_is_neighs = NULL; - if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { - te_is_neighs = lsp->tlv_data.te_is_neighs; - } else { - struct tlv_mt_neighbors *mt_neighbors; - mt_neighbors = tlvs_lookup_mt_neighbors(&lsp->tlv_data, - spftree->mtid); - if (mt_neighbors) - te_is_neighs = mt_neighbors->list; - } - for (ALL_LIST_ELEMENTS_RO(te_is_neighs, node, te_is_neigh)) { - if (!memcmp(te_is_neigh->neigh_id, root_sysid, - ISIS_SYS_ID_LEN)) + struct isis_item_list *te_neighs = NULL; + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) + te_neighs = &lsp->tlvs->extended_reach; + else + te_neighs = isis_lookup_mt_items(&lsp->tlvs->mt_reach, + spftree->mtid); + + struct isis_extended_reach *er; + for (er = te_neighs + ? (struct isis_extended_reach *) + te_neighs->head + : NULL; + er; er = er->next) { + if (!memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN)) continue; if (!pseudo_lsp - && !memcmp(te_is_neigh->neigh_id, null_sysid, - ISIS_SYS_ID_LEN)) + && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN)) continue; - dist = cost + GET_TE_METRIC(te_is_neigh); + dist = cost + er->metric; process_N(spftree, - LSP_PSEUDO_ID(te_is_neigh->neigh_id) - ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS, - (void *)te_is_neigh->neigh_id, dist, - depth + 1, parent); + LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS + : VTYPE_NONPSEUDO_TE_IS, + (void *)er->id, dist, depth + 1, parent); } } if (!pseudo_lsp && spftree->family == AF_INET && spftree->mtid == ISIS_MT_IPV4_UNICAST) { - struct list *reachs[] = {lsp->tlv_data.ipv4_int_reachs, - lsp->tlv_data.ipv4_ext_reachs}; + struct isis_item_list *reachs[] = { + &lsp->tlvs->oldstyle_ip_reach, + &lsp->tlvs->oldstyle_ip_reach_ext}; - prefix.family = AF_INET; for (unsigned int i = 0; i < array_size(reachs); i++) { - vtype = (reachs[i] == lsp->tlv_data.ipv4_int_reachs) - ? VTYPE_IPREACH_INTERNAL - : VTYPE_IPREACH_EXTERNAL; - for (ALL_LIST_ELEMENTS_RO(reachs[i], node, ipreach)) { - dist = cost + ipreach->metrics.metric_default; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen(ipreach->mask); - apply_mask(&prefix); - process_N(spftree, vtype, (void *)&prefix, dist, - depth + 1, parent); + vtype = i ? VTYPE_IPREACH_EXTERNAL + : VTYPE_IPREACH_INTERNAL; + + struct isis_oldstyle_ip_reach *r; + for (r = (struct isis_oldstyle_ip_reach *)reachs[i] + ->head; + r; r = r->next) { + dist = cost + r->metric; + process_N(spftree, vtype, (void *)&r->prefix, + dist, depth + 1, parent); } } } if (!pseudo_lsp && spftree->family == AF_INET) { - struct list *ipv4reachs = NULL; + struct isis_item_list *ipv4_reachs; + if (spftree->mtid == ISIS_MT_IPV4_UNICAST) + ipv4_reachs = &lsp->tlvs->extended_ip_reach; + else + ipv4_reachs = isis_lookup_mt_items( + &lsp->tlvs->mt_ip_reach, spftree->mtid); - if (spftree->mtid == ISIS_MT_IPV4_UNICAST) { - ipv4reachs = lsp->tlv_data.te_ipv4_reachs; - } else { - struct tlv_mt_ipv4_reachs *mt_reachs; - mt_reachs = tlvs_lookup_mt_ipv4_reachs(&lsp->tlv_data, - spftree->mtid); - if (mt_reachs) - ipv4reachs = mt_reachs->list; - } - - prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO(ipv4reachs, node, te_ipv4_reach)) { - assert((te_ipv4_reach->control & 0x3F) - <= IPV4_MAX_BITLEN); - - dist = cost + ntohl(te_ipv4_reach->te_metric); - prefix.u.prefix4 = - newprefix2inaddr(&te_ipv4_reach->prefix_start, - te_ipv4_reach->control); - prefix.prefixlen = (te_ipv4_reach->control & 0x3F); - apply_mask(&prefix); - process_N(spftree, VTYPE_IPREACH_TE, (void *)&prefix, + struct isis_extended_ip_reach *r; + for (r = ipv4_reachs + ? (struct isis_extended_ip_reach *) + ipv4_reachs->head + : NULL; + r; r = r->next) { + dist = cost + r->metric; + process_N(spftree, VTYPE_IPREACH_TE, (void *)&r->prefix, dist, depth + 1, parent); } } if (!pseudo_lsp && spftree->family == AF_INET6) { - struct list *ipv6reachs = NULL; + struct isis_item_list *ipv6_reachs; + if (spftree->mtid == ISIS_MT_IPV4_UNICAST) + ipv6_reachs = &lsp->tlvs->ipv6_reach; + else + ipv6_reachs = isis_lookup_mt_items( + &lsp->tlvs->mt_ipv6_reach, spftree->mtid); - if (spftree->mtid == ISIS_MT_IPV4_UNICAST) { - ipv6reachs = lsp->tlv_data.ipv6_reachs; - } else { - struct tlv_mt_ipv6_reachs *mt_reachs; - mt_reachs = tlvs_lookup_mt_ipv6_reachs(&lsp->tlv_data, - spftree->mtid); - if (mt_reachs) - ipv6reachs = mt_reachs->list; - } - - prefix.family = AF_INET6; - for (ALL_LIST_ELEMENTS_RO(ipv6reachs, node, ip6reach)) { - assert(ip6reach->prefix_len <= IPV6_MAX_BITLEN); - - dist = cost + ntohl(ip6reach->metric); - vtype = (ip6reach->control_info - & CTRL_INFO_DISTRIBUTION) - ? VTYPE_IP6REACH_EXTERNAL - : VTYPE_IP6REACH_INTERNAL; - prefix.prefixlen = ip6reach->prefix_len; - memcpy(&prefix.u.prefix6.s6_addr, ip6reach->prefix, - PSIZE(ip6reach->prefix_len)); - apply_mask(&prefix); - process_N(spftree, vtype, (void *)&prefix, dist, + struct isis_ipv6_reach *r; + for (r = ipv6_reachs + ? (struct isis_ipv6_reach *)ipv6_reachs->head + : NULL; + r; r = r->next) { + dist = cost + r->metric; + vtype = r->external ? VTYPE_IP6REACH_EXTERNAL + : VTYPE_IP6REACH_INTERNAL; + process_N(spftree, vtype, (void *)&r->prefix, dist, depth + 1, parent); } } @@ -893,7 +894,9 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, if (!adj_has_mt(adj, spftree->mtid)) continue; if (spftree->mtid == ISIS_MT_IPV4_UNICAST - && !speaks(&adj->nlpids, spftree->family)) + && !speaks(adj->nlpids.nlpids, + adj->nlpids.count, + spftree->family)) continue; switch (adj->sys_type) { case ISIS_SYSTYPE_ES: @@ -928,8 +931,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, ->lspdb[spftree->level - 1]); if (lsp == NULL - || lsp->lsp_header->rem_lifetime - == 0) + || lsp->hdr.rem_lifetime == 0) zlog_warn( "ISIS-Spf: No LSP %s found for IS adjacency " "L%d on %s (ID %u)", @@ -979,7 +981,7 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, lsp = lsp_search( lsp_id, spftree->area->lspdb[spftree->level - 1]); - if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) { + if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { zlog_warn( "ISIS-Spf: No lsp (%p) found from root " "to L%d DR %s on %s (ID %d)", @@ -1015,7 +1017,9 @@ static int isis_spf_preload_tent(struct isis_spftree *spftree, LSP_PSEUDO_ID(lsp_id) = 0; LSP_FRAGMENT(lsp_id) = 0; if (spftree->mtid != ISIS_MT_IPV4_UNICAST - || speaks(&adj->nlpids, spftree->family)) + || speaks(adj->nlpids.nlpids, + adj->nlpids.count, + spftree->family)) isis_spf_add_local( spftree, spftree->area->oldmetric @@ -1178,7 +1182,7 @@ static int isis_run_spf(struct isis_area *area, int level, int family, memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lsp_id) = 0; lsp = lsp_search(lsp_id, area->lspdb[level - 1]); - if (lsp && lsp->lsp_header->rem_lifetime != 0) { + if (lsp && lsp->hdr.rem_lifetime != 0) { isis_spf_process_lsp(spftree, lsp, vertex->d_N, vertex->depth, sysid, vertex); diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 5296d99480..70afef1a86 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -41,6 +41,7 @@ #include "md5.h" #include "sockunion.h" #include "network.h" +#include "sbuf.h" #include "isisd/dict.h" #include "isisd/isis_constants.h" @@ -48,7 +49,6 @@ #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isisd.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_pdu.h" #include "isisd/isis_dynhn.h" @@ -100,9 +100,9 @@ struct mpls_te_circuit *mpls_te_circuit_new() /* Copy SUB TLVs parameters into a buffer - No space verification are performed */ /* Caller must verify before that there is enough free space in the buffer */ -u_char add_te_subtlvs(u_char *buf, struct mpls_te_circuit *mtc) +uint8_t add_te_subtlvs(uint8_t *buf, struct mpls_te_circuit *mtc) { - u_char size, *tlvs = buf; + uint8_t size, *tlvs = buf; zlog_debug("ISIS MPLS-TE: Add TE Sub TLVs to buffer"); @@ -232,7 +232,7 @@ u_char add_te_subtlvs(u_char *buf, struct mpls_te_circuit *mtc) } /* Compute total Sub-TLVs size */ -u_char subtlvs_len(struct mpls_te_circuit *mtc) +uint8_t subtlvs_len(struct mpls_te_circuit *mtc) { int length = 0; @@ -306,7 +306,7 @@ u_char subtlvs_len(struct mpls_te_circuit *mtc) return 0; } - mtc->length = (u_char)length; + mtc->length = (uint8_t)length; return mtc->length; } @@ -546,13 +546,9 @@ void isis_link_params_update(struct isis_circuit *circuit, if ((SUBTLV_TYPE(mtc->rmt_ipaddr) == 0) && (circuit->circ_type == CIRCUIT_T_P2P)) { struct isis_adjacency *adj = circuit->u.p2p.neighbor; - if (adj->ipv4_addrs != NULL - && listcount(adj->ipv4_addrs) != 0) { - struct in_addr *ip_addr; - ip_addr = (struct in_addr *)listgetdata( - (struct listnode *)listhead( - adj->ipv4_addrs)); - set_circuitparams_rmt_ipaddr(mtc, *ip_addr); + if (adj->ipv4_address_count) { + set_circuitparams_rmt_ipaddr( + mtc, adj->ipv4_addresses[0]); } } @@ -670,163 +666,116 @@ void isis_mpls_te_update(struct interface *ifp) * Followings are vty session control functions. *------------------------------------------------------------------------*/ -static u_char show_vty_subtlv_admin_grp(struct vty *vty, - struct te_subtlv_admin_grp *tlv) +static u_char print_subtlv_admin_grp(struct sbuf *buf, int indent, + struct te_subtlv_admin_grp *tlv) { - - if (vty != NULL) - vty_out(vty, " Administrative Group: 0x%x\n", - (u_int32_t)ntohl(tlv->value)); - else - zlog_debug(" Administrative Group: 0x%x", - (u_int32_t)ntohl(tlv->value)); - + sbuf_push(buf, indent, "Administrative Group: 0x%" PRIx32 "\n", + ntohl(tlv->value)); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_llri(struct vty *vty, struct te_subtlv_llri *tlv) +static u_char print_subtlv_llri(struct sbuf *buf, int indent, + struct te_subtlv_llri *tlv) { - if (vty != NULL) { - vty_out(vty, " Link Local ID: %d\n", - (u_int32_t)ntohl(tlv->local)); - vty_out(vty, " Link Remote ID: %d\n", - (u_int32_t)ntohl(tlv->remote)); - } else { - zlog_debug(" Link Local ID: %d", - (u_int32_t)ntohl(tlv->local)); - zlog_debug(" Link Remote ID: %d", - (u_int32_t)ntohl(tlv->remote)); - } + sbuf_push(buf, indent, "Link Local ID: %" PRIu32 "\n", + ntohl(tlv->local)); + sbuf_push(buf, indent, "Link Remote ID: %" PRIu32 "\n", + ntohl(tlv->remote)); return (SUBTLV_HDR_SIZE + TE_SUBTLV_LLRI_SIZE); } -static u_char show_vty_subtlv_local_ipaddr(struct vty *vty, - struct te_subtlv_local_ipaddr *tlv) +static u_char print_subtlv_local_ipaddr(struct sbuf *buf, int indent, + struct te_subtlv_local_ipaddr *tlv) { - if (vty != NULL) - vty_out(vty, " Local Interface IP Address(es): %s\n", - inet_ntoa(tlv->value)); - else - zlog_debug(" Local Interface IP Address(es): %s", - inet_ntoa(tlv->value)); + sbuf_push(buf, indent, "Local Interface IP Address(es): %s\n", + inet_ntoa(tlv->value)); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_rmt_ipaddr(struct vty *vty, - struct te_subtlv_rmt_ipaddr *tlv) +static u_char print_subtlv_rmt_ipaddr(struct sbuf *buf, int indent, + struct te_subtlv_rmt_ipaddr *tlv) { - if (vty != NULL) - vty_out(vty, " Remote Interface IP Address(es): %s\n", - inet_ntoa(tlv->value)); - else - zlog_debug(" Remote Interface IP Address(es): %s", - inet_ntoa(tlv->value)); + sbuf_push(buf, indent, "Remote Interface IP Address(es): %s\n", + inet_ntoa(tlv->value)); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_max_bw(struct vty *vty, - struct te_subtlv_max_bw *tlv) +static u_char print_subtlv_max_bw(struct sbuf *buf, int indent, + struct te_subtlv_max_bw *tlv) { float fval; fval = ntohf(tlv->value); - if (vty != NULL) - vty_out(vty, " Maximum Bandwidth: %g (Bytes/sec)\n", fval); - else - zlog_debug(" Maximum Bandwidth: %g (Bytes/sec)", fval); + sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n", fval); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_max_rsv_bw(struct vty *vty, - struct te_subtlv_max_rsv_bw *tlv) +static u_char print_subtlv_max_rsv_bw(struct sbuf *buf, int indent, + struct te_subtlv_max_rsv_bw *tlv) { float fval; fval = ntohf(tlv->value); - if (vty != NULL) - vty_out(vty, - " Maximum Reservable Bandwidth: %g (Bytes/sec)\n", - fval); - else - zlog_debug(" Maximum Reservable Bandwidth: %g (Bytes/sec)", - fval); + sbuf_push(buf, indent, "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", fval); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_unrsv_bw(struct vty *vty, - struct te_subtlv_unrsv_bw *tlv) +static u_char print_subtlv_unrsv_bw(struct sbuf *buf, int indent, + struct te_subtlv_unrsv_bw *tlv) { float fval1, fval2; int i; - if (vty != NULL) - vty_out(vty, " Unreserved Bandwidth:\n"); - else - zlog_debug(" Unreserved Bandwidth:"); + sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); for (i = 0; i < MAX_CLASS_TYPE; i += 2) { fval1 = ntohf(tlv->value[i]); fval2 = ntohf(tlv->value[i + 1]); - if (vty != NULL) - vty_out(vty, - " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", - i, fval1, i + 1, fval2); - else - zlog_debug( - " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)", - i, fval1, i + 1, fval2); + sbuf_push(buf, indent + 2, "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + i, fval1, i + 1, fval2); } return (SUBTLV_HDR_SIZE + TE_SUBTLV_UNRSV_SIZE); } -static u_char show_vty_subtlv_te_metric(struct vty *vty, - struct te_subtlv_te_metric *tlv) +static u_char print_subtlv_te_metric(struct sbuf *buf, int indent, + struct te_subtlv_te_metric *tlv) { u_int32_t te_metric; te_metric = tlv->value[2] | tlv->value[1] << 8 | tlv->value[0] << 16; - if (vty != NULL) - vty_out(vty, " Traffic Engineering Metric: %u\n", te_metric); - else - zlog_debug(" Traffic Engineering Metric: %u", te_metric); + sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n", te_metric); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_ras(struct vty *vty, struct te_subtlv_ras *tlv) +static u_char print_subtlv_ras(struct sbuf *buf, int indent, + struct te_subtlv_ras *tlv) { - if (vty != NULL) - vty_out(vty, " Inter-AS TE Remote AS number: %u\n", - ntohl(tlv->value)); - else - zlog_debug(" Inter-AS TE Remote AS number: %u", - ntohl(tlv->value)); + sbuf_push(buf, indent, "Inter-AS TE Remote AS number: %" PRIu32 "\n", + ntohl(tlv->value)); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_rip(struct vty *vty, struct te_subtlv_rip *tlv) +static u_char print_subtlv_rip(struct sbuf *buf, int indent, + struct te_subtlv_rip *tlv) { - if (vty != NULL) - vty_out(vty, " Inter-AS TE Remote ASBR IP address: %s\n", - inet_ntoa(tlv->value)); - else - zlog_debug(" Inter-AS TE Remote ASBR IP address: %s", - inet_ntoa(tlv->value)); + sbuf_push(buf, indent, "Inter-AS TE Remote ASBR IP address: %s\n", + inet_ntoa(tlv->value)); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_av_delay(struct vty *vty, - struct te_subtlv_av_delay *tlv) +static u_char print_subtlv_av_delay(struct sbuf *buf, int indent, + struct te_subtlv_av_delay *tlv) { u_int32_t delay; u_int32_t A; @@ -834,18 +783,14 @@ static u_char show_vty_subtlv_av_delay(struct vty *vty, delay = (u_int32_t)ntohl(tlv->value) & TE_EXT_MASK; A = (u_int32_t)ntohl(tlv->value) & TE_EXT_ANORMAL; - if (vty != NULL) - vty_out(vty, " %s Average Link Delay: %d (micro-sec)\n", - A ? "Anomalous" : "Normal", delay); - else - zlog_debug(" %s Average Link Delay: %d (micro-sec)", - A ? "Anomalous" : "Normal", delay); + sbuf_push(buf, indent, "%s Average Link Delay: %" PRIu32 " (micro-sec)\n", + A ? "Anomalous" : "Normal", delay); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_mm_delay(struct vty *vty, - struct te_subtlv_mm_delay *tlv) +static u_char print_subtlv_mm_delay(struct sbuf *buf, int indent, + struct te_subtlv_mm_delay *tlv) { u_int32_t low, high; u_int32_t A; @@ -854,33 +799,26 @@ static u_char show_vty_subtlv_mm_delay(struct vty *vty, A = (u_int32_t)ntohl(tlv->low) & TE_EXT_ANORMAL; high = (u_int32_t)ntohl(tlv->high) & TE_EXT_MASK; - if (vty != NULL) - vty_out(vty, " %s Min/Max Link Delay: %d / %d (micro-sec)\n", - A ? "Anomalous" : "Normal", low, high); - else - zlog_debug(" %s Min/Max Link Delay: %d / %d (micro-sec)", - A ? "Anomalous" : "Normal", low, high); + sbuf_push(buf, indent, "%s Min/Max Link Delay: %" PRIu32 " / %" PRIu32 " (micro-sec)\n", + A ? "Anomalous" : "Normal", low, high); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_delay_var(struct vty *vty, - struct te_subtlv_delay_var *tlv) +static u_char print_subtlv_delay_var(struct sbuf *buf, int indent, + struct te_subtlv_delay_var *tlv) { u_int32_t jitter; jitter = (u_int32_t)ntohl(tlv->value) & TE_EXT_MASK; - if (vty != NULL) - vty_out(vty, " Delay Variation: %d (micro-sec)\n", jitter); - else - zlog_debug(" Delay Variation: %d (micro-sec)", jitter); + sbuf_push(buf, indent, "Delay Variation: %" PRIu32 " (micro-sec)\n", jitter); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_pkt_loss(struct vty *vty, - struct te_subtlv_pkt_loss *tlv) +static u_char print_subtlv_pkt_loss(struct sbuf *buf, int indent, + struct te_subtlv_pkt_loss *tlv) { u_int32_t loss; u_int32_t A; @@ -890,189 +828,162 @@ static u_char show_vty_subtlv_pkt_loss(struct vty *vty, fval = (float)(loss * LOSS_PRECISION); A = (u_int32_t)ntohl(tlv->value) & TE_EXT_ANORMAL; - if (vty != NULL) - vty_out(vty, " %s Link Packet Loss: %g (%%)\n", - A ? "Anomalous" : "Normal", fval); - else - zlog_debug(" %s Link Packet Loss: %g (%%)", - A ? "Anomalous" : "Normal", fval); + sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", + A ? "Anomalous" : "Normal", fval); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_res_bw(struct vty *vty, - struct te_subtlv_res_bw *tlv) +static u_char print_subtlv_res_bw(struct sbuf *buf, int indent, + struct te_subtlv_res_bw *tlv) { float fval; fval = ntohf(tlv->value); - if (vty != NULL) - vty_out(vty, - " Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", - fval); - else - zlog_debug( - " Unidirectional Residual Bandwidth: %g (Bytes/sec)", - fval); + sbuf_push(buf, indent, "Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", + fval); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_ava_bw(struct vty *vty, - struct te_subtlv_ava_bw *tlv) +static u_char print_subtlv_ava_bw(struct sbuf *buf, int indent, + struct te_subtlv_ava_bw *tlv) { float fval; fval = ntohf(tlv->value); - if (vty != NULL) - vty_out(vty, - " Unidirectional Available Bandwidth: %g (Bytes/sec)\n", - fval); - else - zlog_debug( - " Unidirectional Available Bandwidth: %g (Bytes/sec)", - fval); + sbuf_push(buf, indent, "Unidirectional Available Bandwidth: %g (Bytes/sec)\n", + fval); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_subtlv_use_bw(struct vty *vty, - struct te_subtlv_use_bw *tlv) +static u_char print_subtlv_use_bw(struct sbuf *buf, int indent, + struct te_subtlv_use_bw *tlv) { float fval; fval = ntohf(tlv->value); - if (vty != NULL) - vty_out(vty, - " Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", - fval); - else - zlog_debug( - " Unidirectional Utilized Bandwidth: %g (Bytes/sec)", - fval); + sbuf_push(buf, indent, "Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", + fval); return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); } -static u_char show_vty_unknown_tlv(struct vty *vty, struct subtlv_header *tlvh) +static u_char print_unknown_tlv(struct sbuf *buf, int indent, + struct subtlv_header *tlvh) { int i, rtn = 1; u_char *v = (u_char *)tlvh; - if (vty != NULL) { - if (tlvh->length != 0) { - vty_out(vty, - " Unknown TLV: [type(%#.2x), length(%#.2x)]\n", - tlvh->type, tlvh->length); - vty_out(vty, " Dump: [00]"); - rtn = 1; /* initialize end of line counter */ - for (i = 0; i < tlvh->length; i++) { - vty_out(vty, " %#.2x", v[i]); - if (rtn == 8) { - vty_out(vty, "\n [%.2x]", - i + 1); - rtn = 1; - } else - rtn++; - } - vty_out(vty, "\n"); - } else - vty_out(vty, - " Unknown TLV: [type(%#.2x), length(%#.2x)]\n", - tlvh->type, tlvh->length); + if (tlvh->length != 0) { + sbuf_push(buf, indent, + "Unknown TLV: [type(%#.2x), length(%#.2x)]\n", + tlvh->type, tlvh->length); + sbuf_push(buf, indent + 2, "Dump: [00]"); + rtn = 1; /* initialize end of line counter */ + for (i = 0; i < tlvh->length; i++) { + sbuf_push(buf, 0, " %#.2x", v[i]); + if (rtn == 8) { + sbuf_push(buf, 0, "\n"); + sbuf_push(buf, indent + 8, + "[%.2x]", i + 1); + rtn = 1; + } else + rtn++; + } + sbuf_push(buf, 0, "\n"); } else { - zlog_debug(" Unknown TLV: [type(%#.2x), length(%#.2x)]", - tlvh->type, tlvh->length); + sbuf_push(buf, indent, + "Unknown TLV: [type(%#.2x), length(%#.2x)]\n", + tlvh->type, tlvh->length); } return SUBTLV_SIZE(tlvh); } /* Main Show function */ -void mpls_te_print_detail(struct vty *vty, struct te_is_neigh *te) +void mpls_te_print_detail(struct sbuf *buf, int indent, + uint8_t *subtlvs, uint8_t subtlv_len) { - struct subtlv_header *tlvh; - u_int16_t sum = 0; + struct subtlv_header *tlvh = (struct subtlv_header *)subtlvs; + uint16_t sum = 0; - zlog_debug("ISIS MPLS-TE: Show database TE detail"); - - tlvh = (struct subtlv_header *)te->sub_tlvs; - - for (; sum < te->sub_tlvs_length; tlvh = SUBTLV_HDR_NEXT(tlvh)) { + for (; sum < subtlv_len; tlvh = SUBTLV_HDR_NEXT(tlvh)) { switch (tlvh->type) { case TE_SUBTLV_ADMIN_GRP: - sum += show_vty_subtlv_admin_grp( - vty, (struct te_subtlv_admin_grp *)tlvh); + sum += print_subtlv_admin_grp(buf, indent, + (struct te_subtlv_admin_grp *)tlvh); break; case TE_SUBTLV_LLRI: - sum += show_vty_subtlv_llri( - vty, (struct te_subtlv_llri *)tlvh); + sum += print_subtlv_llri(buf, indent, + (struct te_subtlv_llri *)tlvh); break; case TE_SUBTLV_LOCAL_IPADDR: - sum += show_vty_subtlv_local_ipaddr( - vty, (struct te_subtlv_local_ipaddr *)tlvh); + sum += print_subtlv_local_ipaddr(buf, indent, + (struct te_subtlv_local_ipaddr *)tlvh); break; case TE_SUBTLV_RMT_IPADDR: - sum += show_vty_subtlv_rmt_ipaddr( - vty, (struct te_subtlv_rmt_ipaddr *)tlvh); + sum += print_subtlv_rmt_ipaddr(buf, indent, + (struct te_subtlv_rmt_ipaddr *)tlvh); break; case TE_SUBTLV_MAX_BW: - sum += show_vty_subtlv_max_bw( - vty, (struct te_subtlv_max_bw *)tlvh); + sum += print_subtlv_max_bw(buf, indent, + (struct te_subtlv_max_bw *)tlvh); break; case TE_SUBTLV_MAX_RSV_BW: - sum += show_vty_subtlv_max_rsv_bw( - vty, (struct te_subtlv_max_rsv_bw *)tlvh); + sum += print_subtlv_max_rsv_bw(buf, indent, + (struct te_subtlv_max_rsv_bw *)tlvh); break; case TE_SUBTLV_UNRSV_BW: - sum += show_vty_subtlv_unrsv_bw( - vty, (struct te_subtlv_unrsv_bw *)tlvh); + sum += print_subtlv_unrsv_bw(buf, indent, + (struct te_subtlv_unrsv_bw *)tlvh); break; case TE_SUBTLV_TE_METRIC: - sum += show_vty_subtlv_te_metric( - vty, (struct te_subtlv_te_metric *)tlvh); + sum += print_subtlv_te_metric(buf, indent, + (struct te_subtlv_te_metric *)tlvh); break; case TE_SUBTLV_RAS: - sum += show_vty_subtlv_ras( - vty, (struct te_subtlv_ras *)tlvh); + sum += print_subtlv_ras(buf, indent, + (struct te_subtlv_ras *)tlvh); break; case TE_SUBTLV_RIP: - sum += show_vty_subtlv_rip( - vty, (struct te_subtlv_rip *)tlvh); + sum += print_subtlv_rip(buf, indent, + (struct te_subtlv_rip *)tlvh); break; case TE_SUBTLV_AV_DELAY: - sum += show_vty_subtlv_av_delay( - vty, (struct te_subtlv_av_delay *)tlvh); + sum += print_subtlv_av_delay(buf, indent, + (struct te_subtlv_av_delay *)tlvh); break; case TE_SUBTLV_MM_DELAY: - sum += show_vty_subtlv_mm_delay( - vty, (struct te_subtlv_mm_delay *)tlvh); + sum += print_subtlv_mm_delay(buf, indent, + (struct te_subtlv_mm_delay *)tlvh); break; case TE_SUBTLV_DELAY_VAR: - sum += show_vty_subtlv_delay_var( - vty, (struct te_subtlv_delay_var *)tlvh); + sum += print_subtlv_delay_var(buf, indent, + (struct te_subtlv_delay_var *)tlvh); break; case TE_SUBTLV_PKT_LOSS: - sum += show_vty_subtlv_pkt_loss( - vty, (struct te_subtlv_pkt_loss *)tlvh); + sum += print_subtlv_pkt_loss(buf, indent, + (struct te_subtlv_pkt_loss *)tlvh); break; case TE_SUBTLV_RES_BW: - sum += show_vty_subtlv_res_bw( - vty, (struct te_subtlv_res_bw *)tlvh); + sum += print_subtlv_res_bw(buf, indent, + (struct te_subtlv_res_bw *)tlvh); break; case TE_SUBTLV_AVA_BW: - sum += show_vty_subtlv_ava_bw( - vty, (struct te_subtlv_ava_bw *)tlvh); + sum += print_subtlv_ava_bw(buf, indent, + (struct te_subtlv_ava_bw *)tlvh); break; case TE_SUBTLV_USE_BW: - sum += show_vty_subtlv_use_bw( - vty, (struct te_subtlv_use_bw *)tlvh); + sum += print_subtlv_use_bw(buf, indent, + (struct te_subtlv_use_bw *)tlvh); break; default: - sum += show_vty_unknown_tlv(vty, tlvh); + sum += print_unknown_tlv(buf, indent, tlvh); break; } } @@ -1256,6 +1167,9 @@ DEFUN (show_isis_mpls_te_router, static void show_mpls_te_sub(struct vty *vty, struct interface *ifp) { struct mpls_te_circuit *mtc; + struct sbuf buf; + + sbuf_init(&buf, NULL, 0); if ((IS_MPLS_TE(isisMplsTE)) && ((mtc = lookup_mpls_params_by_ifp(ifp)) != NULL)) { @@ -1280,38 +1194,42 @@ static void show_mpls_te_sub(struct vty *vty, struct interface *ifp) ifp->name); } - show_vty_subtlv_admin_grp(vty, &mtc->admin_grp); + sbuf_reset(&buf); + print_subtlv_admin_grp(&buf, 4, &mtc->admin_grp); if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) - show_vty_subtlv_local_ipaddr(vty, &mtc->local_ipaddr); + print_subtlv_local_ipaddr(&buf, 4, &mtc->local_ipaddr); if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) - show_vty_subtlv_rmt_ipaddr(vty, &mtc->rmt_ipaddr); + print_subtlv_rmt_ipaddr(&buf, 4, &mtc->rmt_ipaddr); - show_vty_subtlv_max_bw(vty, &mtc->max_bw); - show_vty_subtlv_max_rsv_bw(vty, &mtc->max_rsv_bw); - show_vty_subtlv_unrsv_bw(vty, &mtc->unrsv_bw); - show_vty_subtlv_te_metric(vty, &mtc->te_metric); + print_subtlv_max_bw(&buf, 4, &mtc->max_bw); + print_subtlv_max_rsv_bw(&buf, 4, &mtc->max_rsv_bw); + print_subtlv_unrsv_bw(&buf, 4, &mtc->unrsv_bw); + print_subtlv_te_metric(&buf, 4, &mtc->te_metric); if (IS_INTER_AS(mtc->type)) { if (SUBTLV_TYPE(mtc->ras) != 0) - show_vty_subtlv_ras(vty, &mtc->ras); + print_subtlv_ras(&buf, 4, &mtc->ras); if (SUBTLV_TYPE(mtc->rip) != 0) - show_vty_subtlv_rip(vty, &mtc->rip); + print_subtlv_rip(&buf, 4, &mtc->rip); } - show_vty_subtlv_av_delay(vty, &mtc->av_delay); - show_vty_subtlv_mm_delay(vty, &mtc->mm_delay); - show_vty_subtlv_delay_var(vty, &mtc->delay_var); - show_vty_subtlv_pkt_loss(vty, &mtc->pkt_loss); - show_vty_subtlv_res_bw(vty, &mtc->res_bw); - show_vty_subtlv_ava_bw(vty, &mtc->ava_bw); - show_vty_subtlv_use_bw(vty, &mtc->use_bw); + print_subtlv_av_delay(&buf, 4, &mtc->av_delay); + print_subtlv_mm_delay(&buf, 4, &mtc->mm_delay); + print_subtlv_delay_var(&buf, 4, &mtc->delay_var); + print_subtlv_pkt_loss(&buf, 4, &mtc->pkt_loss); + print_subtlv_res_bw(&buf, 4, &mtc->res_bw); + print_subtlv_ava_bw(&buf, 4, &mtc->ava_bw); + print_subtlv_use_bw(&buf, 4, &mtc->use_bw); + + vty_multiline(vty, "", "%s", sbuf_buf(&buf)); vty_out(vty, "---------------\n\n"); } else { vty_out(vty, " %s: MPLS-TE is disabled on this interface\n", ifp->name); } + sbuf_free(&buf); return; } diff --git a/isisd/isis_te.h b/isisd/isis_te.h index 0bd076af19..9b29792e2b 100644 --- a/isisd/isis_te.h +++ b/isisd/isis_te.h @@ -81,6 +81,8 @@ struct subtlv_header { u_char length; /* Value portion only, in byte */ }; +#define MAX_SUBTLV_SIZE 256 + #define SUBTLV_HDR_SIZE 2 /* (sizeof (struct sub_tlv_header)) */ #define SUBTLV_SIZE(stlvh) (SUBTLV_HDR_SIZE + (stlvh)->length) @@ -306,12 +308,13 @@ struct mpls_te_circuit { /* Prototypes. */ void isis_mpls_te_init(void); struct mpls_te_circuit *mpls_te_circuit_new(void); -void mpls_te_print_detail(struct vty *, struct te_is_neigh *); +struct sbuf; +void mpls_te_print_detail(struct sbuf *buf, int indent, uint8_t *subtlvs, uint8_t subtlv_len); void set_circuitparams_local_ipaddr(struct mpls_te_circuit *, struct in_addr); void set_circuitparams_rmt_ipaddr(struct mpls_te_circuit *, struct in_addr); -u_char subtlvs_len(struct mpls_te_circuit *); -u_char add_te_subtlvs(u_char *, struct mpls_te_circuit *); -u_char build_te_subtlvs(u_char *, struct isis_circuit *); +uint8_t subtlvs_len(struct mpls_te_circuit *); +uint8_t add_te_subtlvs(uint8_t *, struct mpls_te_circuit *); +uint8_t build_te_subtlvs(uint8_t *, struct isis_circuit *); void isis_link_params_update(struct isis_circuit *, struct interface *); void isis_mpls_te_update(struct interface *); void isis_mpls_te_config_write_router(struct vty *); diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c deleted file mode 100644 index a295f4dd3f..0000000000 --- a/isisd/isis_tlv.c +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * IS-IS Rout(e)ing protocol - isis_tlv.c - * IS-IS TLV related routines - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program 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 this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "log.h" -#include "linklist.h" -#include "stream.h" -#include "memory.h" -#include "prefix.h" -#include "vty.h" -#include "if.h" - -#include "isisd/dict.h" -#include "isisd/isis_constants.h" -#include "isisd/isis_common.h" -#include "isisd/isis_flags.h" -#include "isisd/isis_circuit.h" -#include "isisd/isis_tlv.h" -#include "isisd/isisd.h" -#include "isisd/isis_dynhn.h" -#include "isisd/isis_misc.h" -#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) -{ - XFREE(MTYPE_ISIS_TLV, val); - - return; -} - -/* - * Called after parsing of a PDU. There shouldn't be any tlv's left, so this - * is only a caution to avoid memory leaks - */ -void 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) - list_delete(tlvs->lsp_entries); - if (tlvs->prefix_neighs) - list_delete(tlvs->prefix_neighs); - if (tlvs->lan_neighs) - list_delete(tlvs->lan_neighs); - if (tlvs->ipv4_addrs) - list_delete(tlvs->ipv4_addrs); - if (tlvs->ipv4_int_reachs) - list_delete(tlvs->ipv4_int_reachs); - if (tlvs->ipv4_ext_reachs) - 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. - */ -int parse_tlvs(char *areatag, u_char *stream, int size, u_int32_t *expected, - u_int32_t *found, struct tlvs *tlvs, u_int32_t *auth_tlv_offset) -{ - u_char type, length; - struct lan_neigh *lan_nei; - struct area_addr *area_addr; - struct is_neigh *is_nei; - struct es_neigh *es_nei; - struct lsp_entry *lsp_entry; - struct in_addr *ipv4_addr; - struct ipv4_reachability *ipv4_reach; - struct in6_addr *ipv6_addr; - int value_len, retval = ISIS_OK; - u_char *start = stream, *pnt = stream; - - *found = 0; - memset(tlvs, 0, sizeof(struct tlvs)); - - while (pnt < stream + size - 2) { - type = *pnt; - length = *(pnt + 1); - pnt += 2; - value_len = 0; - if (pnt + length > stream + size) { - zlog_warn( - "ISIS-TLV (%s): TLV (type %d, length %d) exceeds packet " - "boundaries", - areatag, type, length); - retval = ISIS_WARNING; - break; - } - switch (type) { - case AREA_ADDRESSES: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Address Length | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Area Address | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * : : - */ - *found |= TLVFLAG_AREA_ADDRS; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("TLV Area Adresses len %d", length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_AREA_ADDRS) { - while (length > value_len) { - area_addr = (struct area_addr *)pnt; - value_len += area_addr->addr_len + 1; - pnt += area_addr->addr_len + 1; - if (!tlvs->area_addrs) - tlvs->area_addrs = list_new(); - listnode_add(tlvs->area_addrs, - area_addr); - } - } else { - pnt += length; - } - break; - - case IS_NEIGHBOURS: - *found |= TLVFLAG_IS_NEIGHS; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): IS Neighbours length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (TLVFLAG_IS_NEIGHS & *expected) { - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Virtual Flag | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - */ - pnt++; - value_len++; - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 0 | I/E | Default - * Metric | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Delay Metric - * | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Expense - * Metric | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Error Metric - * | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Neighbour ID | - * +---------------------------------------------------------------+ - * : : - */ - while (length > value_len) { - is_nei = (struct is_neigh *)pnt; - value_len += 4 + ISIS_SYS_ID_LEN + 1; - pnt += 4 + ISIS_SYS_ID_LEN + 1; - if (!tlvs->is_neighs) - tlvs->is_neighs = list_new(); - listnode_add(tlvs->is_neighs, is_nei); - } - } else { - pnt += length; - } - break; - - case TE_IS_NEIGHBOURS: - *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) - retval = parse_mt_is_neighs(tlvs, false, length, - pnt); - pnt += length; - break; - - case MT_IS_NEIGHBOURS: - *found |= TLVFLAG_TE_IS_NEIGHS; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): MT IS Neighbours length %d", - areatag, length); -#endif - if (TLVFLAG_TE_IS_NEIGHS & *expected) - retval = parse_mt_is_neighs(tlvs, true, length, - pnt); - pnt += length; - break; - - case ES_NEIGHBOURS: -/* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 0 | I/E | Default Metric | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Delay Metric | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Expense Metric | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Error Metric | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Neighbour ID | - * +---------------------------------------------------------------+ - * | Neighbour ID | - * +---------------------------------------------------------------+ - * : : - */ -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): ES Neighbours length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - *found |= TLVFLAG_ES_NEIGHS; - if (*expected & TLVFLAG_ES_NEIGHS) { - es_nei = (struct es_neigh *)pnt; - value_len += 4; - pnt += 4; - while (length > value_len) { - /* FIXME FIXME FIXME - add to the list - */ - /* sys_id->id = pnt; */ - value_len += ISIS_SYS_ID_LEN; - pnt += ISIS_SYS_ID_LEN; - /* if (!es_nei->neigh_ids) - * es_nei->neigh_ids = sysid; */ - } - if (!tlvs->es_neighs) - tlvs->es_neighs = list_new(); - listnode_add(tlvs->es_neighs, es_nei); - } else { - pnt += length; - } - break; - - case LAN_NEIGHBOURS: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | LAN Address | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * : : - */ - *found |= TLVFLAG_LAN_NEIGHS; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): LAN Neigbours length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (TLVFLAG_LAN_NEIGHS & *expected) { - while (length > value_len) { - lan_nei = (struct lan_neigh *)pnt; - if (!tlvs->lan_neighs) - tlvs->lan_neighs = list_new(); - listnode_add(tlvs->lan_neighs, lan_nei); - value_len += ETH_ALEN; - pnt += ETH_ALEN; - } - } else { - pnt += length; - } - break; - - case PADDING: -#ifdef EXTREME_TLV_DEBUG - zlog_debug("TLV padding %d", length); -#endif /* EXTREME_TLV_DEBUG */ - pnt += length; - break; - - case LSP_ENTRIES: -/* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Remaining Lifetime | 2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | LSP ID | id+2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | LSP Sequence Number | 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Checksum | 2 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - */ -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): LSP Entries length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - *found |= TLVFLAG_LSP_ENTRIES; - if (TLVFLAG_LSP_ENTRIES & *expected) { - while (length > value_len) { - lsp_entry = (struct lsp_entry *)pnt; - value_len += 10 + ISIS_SYS_ID_LEN; - pnt += 10 + ISIS_SYS_ID_LEN; - if (!tlvs->lsp_entries) - tlvs->lsp_entries = list_new(); - listnode_add(tlvs->lsp_entries, - lsp_entry); - } - } else { - pnt += length; - } - break; - - case CHECKSUM: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 16 bit fletcher CHECKSUM | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * : : - */ - *found |= TLVFLAG_CHECKSUM; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): Checksum length %d", areatag, - length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_CHECKSUM) { - tlvs->checksum = (struct checksum *)pnt; - } - pnt += length; - break; - - case PROTOCOLS_SUPPORTED: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | NLPID | - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * : : - */ - *found |= TLVFLAG_NLPID; -#ifdef EXTREME_TLV_DEBUG - zlog_debug( - "ISIS-TLV (%s): Protocols Supported length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_NLPID) { - tlvs->nlpids = (struct nlpids *)(pnt - 1); - } - pnt += length; - break; - - case IPV4_ADDR: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * + IP version 4 address + 4 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * : : - */ - *found |= TLVFLAG_IPV4_ADDR; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): IPv4 Address length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_IPV4_ADDR) { - while (length > value_len) { - ipv4_addr = (struct in_addr *)pnt; -#ifdef EXTREME_TLV_DEBUG - zlog_debug( - "ISIS-TLV (%s) : IP ADDR %s, pnt %p", - areatag, inet_ntoa(*ipv4_addr), - pnt); -#endif /* EXTREME_TLV_DEBUG */ - if (!tlvs->ipv4_addrs) - tlvs->ipv4_addrs = list_new(); - listnode_add(tlvs->ipv4_addrs, - ipv4_addr); - value_len += 4; - pnt += 4; - } - } else { - pnt += length; - } - break; - - case AUTH_INFO: - *found |= TLVFLAG_AUTH_INFO; -#ifdef EXTREME_TLV_DEBUG - zlog_debug( - "ISIS-TLV (%s): IS-IS Authentication Information", - areatag); -#endif - if (*expected & TLVFLAG_AUTH_INFO) { - tlvs->auth_info.type = *pnt; - if (length == 0) { - zlog_warn( - "ISIS-TLV (%s): TLV (type %d, length %d) " - "incorrect.", - areatag, type, length); - return ISIS_WARNING; - } - --length; - tlvs->auth_info.len = length; - pnt++; - memcpy(tlvs->auth_info.passwd, pnt, length); - /* Return the authentication tlv pos for later - * computation - * of MD5 (RFC 5304, 2) - */ - if (auth_tlv_offset) - *auth_tlv_offset += (pnt - start - 3); - pnt += length; - } else { - pnt += length; - } - break; - - case DYNAMIC_HOSTNAME: - *found |= TLVFLAG_DYN_HOSTNAME; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): Dynamic Hostname length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_DYN_HOSTNAME) { - /* the length is also included in the pointed - * struct */ - tlvs->hostname = (struct hostname *)(pnt - 1); - } - pnt += length; - break; - - case TE_ROUTER_ID: - /* +---------------------------------------------------------------+ - * + Router ID + 4 - * +---------------------------------------------------------------+ - */ - *found |= TLVFLAG_TE_ROUTER_ID; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): TE Router ID %d", areatag, - length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_TE_ROUTER_ID) - tlvs->router_id = (struct te_router_id *)(pnt); - pnt += length; - break; - - case IPV4_INT_REACHABILITY: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 0 | I/E | Default Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Delay Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Expense Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Error Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | ip address | 4 - * +---------------------------------------------------------------+ - * | address mask | 4 - * +---------------------------------------------------------------+ - * : : - */ - *found |= TLVFLAG_IPV4_INT_REACHABILITY; -#ifdef EXTREME_TLV_DEBUG - zlog_debug( - "ISIS-TLV (%s): IPv4 internal Reachability length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_IPV4_INT_REACHABILITY) { - while (length > value_len) { - ipv4_reach = - (struct ipv4_reachability *)pnt; - if (!tlvs->ipv4_int_reachs) - tlvs->ipv4_int_reachs = - list_new(); - listnode_add(tlvs->ipv4_int_reachs, - ipv4_reach); - value_len += 12; - pnt += 12; - } - } else { - pnt += length; - } - break; - - case IPV4_EXT_REACHABILITY: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | 0 | I/E | Default Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Delay Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Expense Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | S | I/E | Error Metric | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | ip address | 4 - * +---------------------------------------------------------------+ - * | address mask | 4 - * +---------------------------------------------------------------+ - * : : - */ - *found |= TLVFLAG_IPV4_EXT_REACHABILITY; -#ifdef EXTREME_TLV_DEBUG - zlog_debug( - "ISIS-TLV (%s): IPv4 external Reachability length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_IPV4_EXT_REACHABILITY) { - while (length > value_len) { - ipv4_reach = - (struct ipv4_reachability *)pnt; - if (!tlvs->ipv4_ext_reachs) - tlvs->ipv4_ext_reachs = - list_new(); - listnode_add(tlvs->ipv4_ext_reachs, - ipv4_reach); - value_len += 12; - pnt += 12; - } - } else { - pnt += length; - } - break; - - case TE_IPV4_REACHABILITY: - *found |= TLVFLAG_TE_IPV4_REACHABILITY; -#ifdef EXTREME_TLV_DEBUG - zlog_debug( - "ISIS-TLV (%s): IPv4 extended Reachability length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) - 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 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * : : - */ - *found |= TLVFLAG_IPV6_ADDR; -#ifdef EXTREME_TLV_DEBUG - zlog_debug("ISIS-TLV (%s): IPv6 Address length %d", - areatag, length); -#endif /* EXTREME_TLV_DEBUG */ - if (*expected & TLVFLAG_IPV6_ADDR) { - while (length > value_len) { - ipv6_addr = (struct in6_addr *)pnt; - if (!tlvs->ipv6_addrs) - tlvs->ipv6_addrs = list_new(); - listnode_add(tlvs->ipv6_addrs, - ipv6_addr); - value_len += 16; - pnt += 16; - } - } else { - pnt += length; - } - break; - - case 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, 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 - * +---------------------------------------------------------------+ - * | Extended Local Circuit ID | 4 - * +---------------------------------------------------------------+ - * | Neighbor System ID (If known) - * | 0-8 - * (probably 6) - * +---------------------------------------------------------------+ - * | Neighbor Local Circuit ID (If - * known) | 4 - * +---------------------------------------------------------------+ - */ - *found |= TLVFLAG_3WAY_HELLO; - if (*expected & TLVFLAG_3WAY_HELLO) { - while (length > value_len) { - /* FIXME: make this work */ - /* Adjacency State (one - octet): - 0 = Up - 1 = Initializing - 2 = Down - Extended Local Circuit ID - (four octets) - Neighbor System ID if known - (zero to eight octets) - Neighbor Extended Local - Circuit ID (four octets, if Neighbor - System ID is present) */ - pnt += length; - value_len += length; - } - } else { - pnt += length; - } - - break; - case GRACEFUL_RESTART: - /* +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Reserved | SA | RA - * | RR | 1 - * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | Remaining Time | 2 - * +---------------------------------------------------------------+ - * | Restarting Neighbor ID (If known) - * | 0-8 - * +---------------------------------------------------------------+ - */ - *found |= TLVFLAG_GRACEFUL_RESTART; - if (*expected & TLVFLAG_GRACEFUL_RESTART) { - /* FIXME: make this work */ - } - 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); - - pnt += length; - break; - } - /* Abort Parsing if error occured */ - if (retval != ISIS_OK) - return retval; - } - - return retval; -} - -int add_tlv(u_char tag, u_char len, u_char *value, struct stream *stream) -{ - if ((stream_get_size(stream) - stream_get_endp(stream)) - < (((unsigned)len) + 2)) { - zlog_warn( - "No room for TLV of type %d " - "(total size %d available %d required %d)", - tag, (int)stream_get_size(stream), - (int)(stream_get_size(stream) - - stream_get_endp(stream)), - len + 2); - return ISIS_WARNING; - } - - stream_putc(stream, tag); /* TAG */ - stream_putc(stream, len); /* LENGTH */ - stream_put(stream, value, (int)len); /* VALUE */ - -#ifdef EXTREME_DEBUG - zlog_debug("Added TLV %d len %d", tag, len); -#endif /* EXTREME DEBUG */ - 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) -{ - struct listnode *node; - struct area_addr *area_addr; - - u_char value[255]; - u_char *pos = value; - - for (ALL_LIST_ELEMENTS_RO(area_addrs, node, area_addr)) { - if (pos - value + area_addr->addr_len > 255) - goto err; - *pos = area_addr->addr_len; - pos++; - memcpy(pos, area_addr->area_addr, (int)area_addr->addr_len); - pos += area_addr->addr_len; - } - - return add_tlv(AREA_ADDRESSES, pos - value, value, stream); - -err: - zlog_warn("tlv_add_area_addrs(): TLV longer than 255"); - return ISIS_WARNING; -} - -int tlv_add_is_neighs(struct list *is_neighs, struct stream *stream) -{ - struct listnode *node; - struct is_neigh *is_neigh; - u_char value[255]; - u_char *pos = value; - int retval; - - *pos = 0; /*is_neigh->virtual; */ - pos++; - - for (ALL_LIST_ELEMENTS_RO(is_neighs, node, is_neigh)) { - if (pos - value + IS_NEIGHBOURS_LEN > 255) { - retval = add_tlv(IS_NEIGHBOURS, pos - value, value, - stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - *pos = is_neigh->metrics.metric_default; - pos++; - *pos = is_neigh->metrics.metric_delay; - pos++; - *pos = is_neigh->metrics.metric_expense; - pos++; - *pos = is_neigh->metrics.metric_error; - pos++; - memcpy(pos, is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1); - pos += ISIS_SYS_ID_LEN + 1; - } - - return add_tlv(IS_NEIGHBOURS, pos - value, value, 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; - 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 ((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); - pos += 3; - /* Set the total size of Sub TLVs */ - *pos = te_is_neigh->sub_tlvs_length; - pos++; - /* Copy Sub TLVs if any */ - if (te_is_neigh->sub_tlvs_length > 0) { - memcpy(pos, te_is_neigh->sub_tlvs, - te_is_neigh->sub_tlvs_length); - pos += te_is_neigh->sub_tlvs_length; - } - consumed++; - } - - 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 tlv_add_lan_neighs(struct list *lan_neighs, struct stream *stream) -{ - struct listnode *node; - u_char *snpa; - u_char value[255]; - u_char *pos = value; - int retval; - - for (ALL_LIST_ELEMENTS_RO(lan_neighs, node, snpa)) { - if (pos - value + ETH_ALEN > 255) { - retval = add_tlv(LAN_NEIGHBOURS, pos - value, value, - stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - memcpy(pos, snpa, ETH_ALEN); - pos += ETH_ALEN; - } - - return add_tlv(LAN_NEIGHBOURS, pos - value, value, stream); -} - -int tlv_add_nlpid(struct nlpids *nlpids, struct stream *stream) -{ - return add_tlv(PROTOCOLS_SUPPORTED, nlpids->count, nlpids->nlpids, - stream); -} - -int tlv_add_authinfo(u_char auth_type, u_char auth_len, u_char *auth_value, - struct stream *stream) -{ - u_char value[255]; - u_char *pos = value; - *pos++ = auth_type; - memcpy(pos, auth_value, auth_len); - - return add_tlv(AUTH_INFO, auth_len + 1, value, stream); -} - -int tlv_add_checksum(struct checksum *checksum, struct stream *stream) -{ - u_char value[255]; - u_char *pos = value; - return add_tlv(CHECKSUM, pos - value, value, stream); -} - -int tlv_add_ip_addrs(struct list *ip_addrs, struct stream *stream) -{ - struct listnode *node; - struct prefix_ipv4 *ipv4; - u_char value[255]; - u_char *pos = value; - - for (ALL_LIST_ELEMENTS_RO(ip_addrs, node, ipv4)) { - if (pos - value + IPV4_MAX_BYTELEN > 255) { - /* RFC 1195 s4.2: only one tuple of 63 allowed. */ - zlog_warn( - "tlv_add_ip_addrs(): cutting off at 63 IP addresses"); - break; - } - *(u_int32_t *)pos = ipv4->prefix.s_addr; - pos += IPV4_MAX_BYTELEN; - } - - return add_tlv(IPV4_ADDR, pos - value, value, stream); -} - -/* Used to add TLV containing just one IPv4 address - either IPv4 address TLV - * (in case of LSP) or TE router ID TLV. */ -int tlv_add_in_addr(struct in_addr *addr, struct stream *stream, u_char tag) -{ - u_char value[255]; - u_char *pos = value; - - memcpy(pos, addr, IPV4_MAX_BYTELEN); - pos += IPV4_MAX_BYTELEN; - - return add_tlv(tag, pos - value, value, stream); -} - -int tlv_add_dynamic_hostname(struct hostname *hostname, struct stream *stream) -{ - return add_tlv(DYNAMIC_HOSTNAME, hostname->namelen, hostname->name, - stream); -} - -int tlv_add_lsp_entries(struct list *lsps, struct stream *stream) -{ - struct listnode *node; - struct isis_lsp *lsp; - u_char value[255]; - u_char *pos = value; - int retval; - - for (ALL_LIST_ELEMENTS_RO(lsps, node, lsp)) { - if (pos - value + LSP_ENTRIES_LEN > 255) { - retval = add_tlv(LSP_ENTRIES, pos - value, value, - stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - *((u_int16_t *)pos) = lsp->lsp_header->rem_lifetime; - pos += 2; - memcpy(pos, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2); - pos += ISIS_SYS_ID_LEN + 2; - *((u_int32_t *)pos) = lsp->lsp_header->seq_num; - pos += 4; - *((u_int16_t *)pos) = lsp->lsp_header->checksum; - pos += 2; - } - - return add_tlv(LSP_ENTRIES, pos - value, value, stream); -} - -static int tlv_add_ipv4_reachs(u_char tag, struct list *ipv4_reachs, - struct stream *stream) -{ - struct listnode *node; - struct ipv4_reachability *reach; - u_char value[255]; - u_char *pos = value; - int retval; - - for (ALL_LIST_ELEMENTS_RO(ipv4_reachs, node, reach)) { - if (pos - value + IPV4_REACH_LEN > 255) { - retval = add_tlv(tag, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - *pos = reach->metrics.metric_default; - pos++; - *pos = reach->metrics.metric_delay; - pos++; - *pos = reach->metrics.metric_expense; - pos++; - *pos = reach->metrics.metric_error; - pos++; - *(u_int32_t *)pos = reach->prefix.s_addr; - pos += IPV4_MAX_BYTELEN; - *(u_int32_t *)pos = reach->mask.s_addr; - pos += IPV4_MAX_BYTELEN; - } - - return add_tlv(tag, pos - value, value, stream); -} - -int tlv_add_ipv4_int_reachs(struct list *ipv4_reachs, struct stream *stream) -{ - return tlv_add_ipv4_reachs(IPV4_INT_REACHABILITY, ipv4_reachs, stream); -} - -int tlv_add_ipv4_ext_reachs(struct list *ipv4_reachs, struct stream *stream) -{ - return tlv_add_ipv4_reachs(IPV4_EXT_REACHABILITY, ipv4_reachs, 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; - 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)) { - unsigned char prefixlen = te_reach->control & 0x3F; - - if ((size_t)(pos - value) + 5 + PSIZE(prefixlen) > max_size) - break; - - *(u_int32_t *)pos = te_reach->te_metric; - pos += 4; - *pos = te_reach->control; - pos++; - memcpy(pos, &te_reach->prefix_start, PSIZE(prefixlen)); - pos += PSIZE(prefixlen); - consumed++; - } - - if (consumed) { - int rv = add_tlv((mtid != ISIS_MT_IPV4_UNICAST) - ? MT_IPV4_REACHABILITY - : TE_IPV4_REACHABILITY, - pos - value, value, stream); - assert(rv == ISIS_OK); - } - - return consumed; -} - -int tlv_add_ipv6_addrs(struct list *ipv6_addrs, struct stream *stream) -{ - struct listnode *node; - struct prefix_ipv6 *ipv6; - u_char value[255]; - u_char *pos = value; - int retval; - - for (ALL_LIST_ELEMENTS_RO(ipv6_addrs, node, ipv6)) { - if (pos - value + IPV6_MAX_BYTELEN > 255) { - retval = add_tlv(IPV6_ADDR, pos - value, value, stream); - if (retval != ISIS_OK) - return retval; - pos = value; - } - memcpy(pos, ipv6->prefix.s6_addr, IPV6_MAX_BYTELEN); - pos += IPV6_MAX_BYTELEN; - } - - return add_tlv(IPV6_ADDR, pos - value, value, 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; - 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 ((size_t)(pos - value) + 6 + PSIZE(ip6reach->prefix_len) - > max_size) - break; - - *(uint32_t *)pos = ip6reach->metric; - pos += 4; - *pos = ip6reach->control_info; - pos++; - *pos = ip6reach->prefix_len; - pos++; - memcpy(pos, ip6reach->prefix, PSIZE(ip6reach->prefix_len)); - pos += PSIZE(ip6reach->prefix_len); - consumed++; - } - - if (consumed) { - int rv = add_tlv((mtid != ISIS_MT_IPV4_UNICAST) - ? MT_IPV6_REACHABILITY - : IPV6_REACHABILITY, - pos - value, value, stream); - assert(rv == ISIS_OK); - } - - return consumed; -} - -int tlv_add_padding(struct stream *stream) -{ - int fullpads, i, left; - - /* - * How many times can we add full padding ? - */ - fullpads = (stream_get_size(stream) - stream_get_endp(stream)) / 257; - for (i = 0; i < fullpads; i++) { - if (!stream_putc(stream, (u_char)PADDING)) /* TAG */ - goto err; - if (!stream_putc(stream, (u_char)255)) /* LENGHT */ - goto err; - stream_put(stream, NULL, 255); /* zero padding */ - } - - left = stream_get_size(stream) - stream_get_endp(stream); - - if (left < 2) - return ISIS_OK; - - if (left == 2) { - stream_putc(stream, PADDING); - stream_putc(stream, 0); - return ISIS_OK; - } - - stream_putc(stream, PADDING); - stream_putc(stream, left - 2); - stream_put(stream, NULL, left - 2); - - return ISIS_OK; - -err: - zlog_warn("tlv_add_padding(): no room for tlv"); - return ISIS_WARNING; -} diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h deleted file mode 100644 index d06548519f..0000000000 --- a/isisd/isis_tlv.h +++ /dev/null @@ -1,340 +0,0 @@ -/* - * IS-IS Rout(e)ing protocol - isis_tlv.h - * IS-IS TLV related routines - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program 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 this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _ZEBRA_ISIS_TLV_H -#define _ZEBRA_ISIS_TLV_H - -#include "isisd/isis_mt.h" - -/* - * The list of TLVs we (should) support. - * ____________________________________________________________________________ - * Name Value IIH LSP SNP Status - * LAN - * ____________________________________________________________________________ - * - * Area Addresses 1 y y n ISO10589 - * IIS Neighbors 2 n y n ISO10589 - * ES Neighbors 3 n y n ISO10589 - * IIS Neighbors 6 y n n ISO10589 - * Padding 8 y n n ISO10589 - * LSP Entries 9 n n y ISO10589 - * Authentication 10 y y y ISO10589, RFC3567 - * Checksum 12 y n y RFC3358 - * Extended IS Reachability 22 n y n RFC5305 - * IS Alias 24 n y n RFC3786 - * IP Int. Reachability 128 n y n RFC1195 - * Protocols Supported 129 y y n RFC1195 - * IP Ext. Reachability 130 n y n RFC1195 - * IDRPI 131 n y y RFC1195 - * IP Interface Address 132 y y n RFC1195 - * TE Router ID 134 n y n RFC5305 - * Extended IP Reachability 135 n y n RFC5305 - * Dynamic Hostname 137 n y n RFC2763 - * Shared Risk Link Group 138 n y y RFC5307 - * Inter-AS Reachability 141 n y n RFC5316 - * Restart TLV 211 y n n RFC3847 - * MT IS Reachability 222 n y n RFC5120 - * MT Supported 229 y y n RFC5120 - * IPv6 Interface Address 232 y y n RFC5308 - * MT IP Reachability 235 n y n RFC5120 - * IPv6 IP Reachability 236 n y n RFC5308 - * MT IPv6 IP Reachability 237 n y n RFC5120 - * P2P Adjacency State 240 y n n RFC3373 - * IIH Sequence Number 241 y n n draft-shen-isis-iih-sequence - * Router Capability 242 n y n RFC4971 - * - * - * IS Reachability sub-TLVs we support (See isis_te.[c,h]) - * ____________________________________________________________________________ - * Name Value Status - * ____________________________________________________________________________ - * Administartive group (color) 3 RFC5305 - * Link Local/Remote Identifiers 4 RFC5307 - * IPv4 interface address 6 RFC5305 - * IPv4 neighbor address 8 RFC5305 - * Maximum link bandwidth 9 RFC5305 - * Reservable link bandwidth 10 RFC5305 - * Unreserved bandwidth 11 RFC5305 - * TE Default metric 18 RFC5305 - * Link Protection Type 20 RFC5307 - * Interface Switching Capability 21 RFC5307 - * Remote AS number 24 RFC5316 - * IPv4 Remote ASBR identifier 25 RFC5316 - * - * - * IP Reachability sub-TLVs we (should) support. - * ____________________________________________________________________________ - * Name Value Status - * ____________________________________________________________________________ - * 32bit administrative tag 1 RFC5130 - * 64bit administrative tag 2 RFC5130 - * Management prefix color 117 RFC5120 - */ - -#define AREA_ADDRESSES 1 -#define IS_NEIGHBOURS 2 -#define ES_NEIGHBOURS 3 -#define LAN_NEIGHBOURS 6 -#define PADDING 8 -#define LSP_ENTRIES 9 -#define AUTH_INFO 10 -#define CHECKSUM 12 -#define TE_IS_NEIGHBOURS 22 -#define IS_ALIAS 24 -#define IPV4_INT_REACHABILITY 128 -#define PROTOCOLS_SUPPORTED 129 -#define IPV4_EXT_REACHABILITY 130 -#define IDRP_INFO 131 -#define IPV4_ADDR 132 -#define TE_ROUTER_ID 134 -#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 - -#define AUTH_INFO_HDRLEN 3 - -#define MAX_TLV_LEN 255 - -#define IS_NEIGHBOURS_LEN (ISIS_SYS_ID_LEN + 5) -#define LAN_NEIGHBOURS_LEN 6 -#define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN) /* FIXME: should be entry */ -#define IPV4_REACH_LEN 12 -#define IPV6_REACH_LEN 22 -#define TE_IPV4_REACH_LEN 9 - -#define MAX_SUBTLV_SIZE 256 - -/* struct for neighbor */ -struct is_neigh { - struct metric metrics; - u_char neigh_id[ISIS_SYS_ID_LEN + 1]; -}; - -/* struct for te metric */ -struct te_is_neigh { - u_char neigh_id[ISIS_SYS_ID_LEN + 1]; - u_char te_metric[3]; - u_char sub_tlvs_length; - /* Theorical Maximum SubTLVs is 256 because the sub_tlvs_length is 8 - * bits */ - /* Practically, 118 bytes are necessary to store all supported TE - * parameters */ - /* FIXME: A pointer will use less memory, but need to be free */ - /* which is hard to fix, especially within free_tlvs() function */ - /* and malloc() / free() as a CPU cost compared to the memory usage */ - u_char sub_tlvs[MAX_SUBTLV_SIZE]; /* SUB TLVs storage */ -}; - -/* Decode and encode three-octet metric into host byte order integer */ -#define GET_TE_METRIC(t) \ - (((unsigned)(t)->te_metric[0] << 16) | ((t)->te_metric[1] << 8) \ - | (t)->te_metric[2]) -#define SET_TE_METRIC(t, m) \ - (((t)->te_metric[0] = (m) >> 16), ((t)->te_metric[1] = (m) >> 8), \ - ((t)->te_metric[2] = (m))) - -/* struct for es neighbors */ -struct es_neigh { - struct metric metrics; - /* approximate position of first, we use the - * length ((uchar*)metric-1) to know all */ - u_char first_es_neigh[ISIS_SYS_ID_LEN]; -}; - -struct partition_desig_level2_is { - struct list *isis_system_ids; -}; - -/* struct for lan neighbors */ -struct lan_neigh { - u_char LAN_addr[6]; -}; - -#ifdef __SUNPRO_C -#pragma pack(1) -#endif - -/* struct for LSP entry */ -struct lsp_entry { - u_int16_t rem_lifetime; - u_char lsp_id[ISIS_SYS_ID_LEN + 2]; - u_int32_t seq_num; - u_int16_t checksum; -} __attribute__((packed)); - -#ifdef __SUNPRO_C -#pragma pack() -#endif - -/* struct for checksum */ -struct checksum { - u_int16_t checksum; -}; - -/* ipv4 reachability */ -struct ipv4_reachability { - struct metric metrics; - struct in_addr prefix; - struct in_addr mask; -}; - -/* te router id */ -struct te_router_id { - struct in_addr id; -}; - -/* te ipv4 reachability */ -struct te_ipv4_reachability { - u_int32_t te_metric; - u_char control; - u_char prefix_start; /* since this is variable length by nature it only - */ -}; /* points to an approximate location */ - -#define TE_IPV4_HAS_SUBTLV (0x40) - -struct idrp_info { - u_char len; - u_char *value; -}; - -struct ipv6_reachability { - u_int32_t metric; - u_char control_info; - u_char prefix_len; - u_char prefix[16]; -}; - -/* bits in control_info */ -#define CTRL_INFO_DIRECTION 0x80 -#define DIRECTION_UP 0x00 -#define DIRECTION_DOWN 0x80 - -#define CTRL_INFO_DISTRIBUTION 0x40 -#define DISTRIBUTION_INTERNAL 0x00 -#define DISTRIBUTION_EXTERNAL 0x40 - -#define CTRL_INFO_SUBTLVS 0x20 - -struct mt_router_info { - ISIS_MT_INFO_FIELDS - bool overload; -}; - -/* - * Pointer to each tlv type, filled by parse_tlvs() - */ -struct tlvs { - struct checksum *checksum; - struct hostname *hostname; - 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; - struct list *lan_neighs; - struct list *ipv4_addrs; - 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; -}; - -/* - * Own definitions - used to bitmask found and expected - */ - -#define TLVFLAG_AREA_ADDRS (1<<0) -#define TLVFLAG_IS_NEIGHS (1<<1) -#define TLVFLAG_ES_NEIGHS (1<<2) -#define TLVFLAG_PARTITION_DESIG_LEVEL2_IS (1<<3) -#define TLVFLAG_PREFIX_NEIGHS (1<<4) -#define TLVFLAG_LAN_NEIGHS (1<<5) -#define TLVFLAG_LSP_ENTRIES (1<<6) -#define TLVFLAG_PADDING (1<<7) -#define TLVFLAG_AUTH_INFO (1<<8) -#define TLVFLAG_IPV4_INT_REACHABILITY (1<<9) -#define TLVFLAG_NLPID (1<<10) -#define TLVFLAG_IPV4_EXT_REACHABILITY (1<<11) -#define TLVFLAG_IPV4_ADDR (1<<12) -#define TLVFLAG_DYN_HOSTNAME (1<<13) -#define TLVFLAG_IPV6_ADDR (1<<14) -#define TLVFLAG_IPV6_REACHABILITY (1<<15) -#define TLVFLAG_TE_IS_NEIGHS (1<<16) -#define TLVFLAG_TE_IPV4_REACHABILITY (1<<17) -#define TLVFLAG_3WAY_HELLO (1<<18) -#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); -int parse_tlvs(char *areatag, u_char *stream, int size, u_int32_t *expected, - u_int32_t *found, struct tlvs *tlvs, u_int32_t *auth_tlv_offset); -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); -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); -int tlv_add_authinfo(u_char auth_type, u_char authlen, u_char *auth_value, - struct stream *stream); -int tlv_add_ip_addrs(struct list *ip_addrs, struct stream *stream); -int tlv_add_in_addr(struct in_addr *, struct stream *stream, u_char tag); -int tlv_add_dynamic_hostname(struct hostname *hostname, struct stream *stream); -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); -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); -unsigned int tlv_add_ipv6_reachs(struct list *ipv6_reachs, - struct stream *stream, void *arg); - -int tlv_add_padding(struct stream *stream); - -#endif /* _ZEBRA_ISIS_TLV_H */ diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c new file mode 100644 index 0000000000..8efbcd2811 --- /dev/null +++ b/isisd/isis_tlvs.c @@ -0,0 +1,3090 @@ +/* + * IS-IS TLV Serializer/Deserializer + * + * Copyright (C) 2015,2017 Christian Franke + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "md5.h" +#include "memory.h" +#include "stream.h" +#include "sbuf.h" + +#include "isisd/isisd.h" +#include "isisd/isis_memory.h" +#include "isisd/isis_tlvs.h" +#include "isisd/isis_common.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_te.h" + +DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs") +DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs") +DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists") + +typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type, + uint8_t tlv_len, struct stream *s, + struct sbuf *log, void *dest, int indent); +typedef int (*pack_item_func)(struct isis_item *item, struct stream *s); +typedef void (*free_item_func)(struct isis_item *i); +typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent); +typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent); +typedef struct isis_item *(*copy_item_func)(struct isis_item *i); + +struct tlv_ops { + const char *name; + unpack_tlv_func unpack; + + pack_item_func pack_item; + free_item_func free_item; + unpack_item_func unpack_item; + format_item_func format_item; + copy_item_func copy_item; +}; + +enum how_to_pack { + ISIS_ITEMS, + ISIS_MT_ITEMS, +}; + +struct pack_order_entry { + enum isis_tlv_context context; + enum isis_tlv_type type; + enum how_to_pack how_to_pack; + size_t what_to_pack; +}; +#define PACK_ENTRY(t, h, w) \ + { \ + .context = ISIS_CONTEXT_LSP, .type = ISIS_TLV_##t, \ + .how_to_pack = (h), \ + .what_to_pack = offsetof(struct isis_tlvs, w), \ + } + +static struct pack_order_entry pack_order[] = { + PACK_ENTRY(OLDSTYLE_REACH, ISIS_ITEMS, oldstyle_reach), + PACK_ENTRY(LAN_NEIGHBORS, ISIS_ITEMS, lan_neighbor), + PACK_ENTRY(LSP_ENTRY, ISIS_ITEMS, lsp_entries), + PACK_ENTRY(EXTENDED_REACH, ISIS_ITEMS, extended_reach), + PACK_ENTRY(MT_REACH, ISIS_MT_ITEMS, mt_reach), + PACK_ENTRY(OLDSTYLE_IP_REACH, ISIS_ITEMS, oldstyle_ip_reach), + PACK_ENTRY(OLDSTYLE_IP_REACH_EXT, ISIS_ITEMS, oldstyle_ip_reach_ext), + PACK_ENTRY(IPV4_ADDRESS, ISIS_ITEMS, ipv4_address), + PACK_ENTRY(IPV6_ADDRESS, ISIS_ITEMS, ipv6_address), + PACK_ENTRY(EXTENDED_IP_REACH, ISIS_ITEMS, extended_ip_reach), + PACK_ENTRY(MT_IP_REACH, ISIS_MT_ITEMS, mt_ip_reach), + PACK_ENTRY(IPV6_REACH, ISIS_ITEMS, ipv6_reach), + PACK_ENTRY(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach)}; + +/* This is a forward definition. The table is actually initialized + * in at the bottom. */ +static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX]; + +/* End of _ops forward definition. */ + +/* Prototypes */ +static void append_item(struct isis_item_list *dest, struct isis_item *item); + +/* Functions for Sub-TVL ??? IPv6 Source Prefix */ + +static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p) +{ + if (!p) + return NULL; + + struct prefix_ipv6 *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + rv->family = p->family; + rv->prefixlen = p->prefixlen; + memcpy(&rv->prefix, &p->prefix, sizeof(rv->prefix)); + return rv; +} + +static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p, + struct sbuf *buf, int indent) +{ + if (!p) + return; + + char prefixbuf[PREFIX2STR_BUFFER]; + sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n", + prefix2str(p, prefixbuf, sizeof(prefixbuf))); +} + +static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p, + struct stream *s) +{ + if (!p) + return 0; + + if (STREAM_WRITEABLE(s) < 3 + (unsigned)PSIZE(p->prefixlen)) + return 1; + + stream_putc(s, ISIS_SUBTLV_IPV6_SOURCE_PREFIX); + stream_putc(s, 1 + PSIZE(p->prefixlen)); + stream_putc(s, p->prefixlen); + stream_put(s, &p->prefix, PSIZE(p->prefixlen)); + return 0; +} + +static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_subtlvs *subtlvs = dest; + struct prefix_ipv6 p = { + .family = AF_INET6, + }; + + sbuf_push(log, indent, "Unpacking IPv6 Source Prefix Sub-TLV...\n"); + + if (tlv_len < 1) { + sbuf_push(log, indent, + "Not enough data left. (expected 1 or more bytes, got %" PRIu8 ")\n", + tlv_len); + return 1; + } + + p.prefixlen = stream_getc(s); + if (p.prefixlen > 128) { + sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n", + p.prefixlen); + return 1; + } + + if (tlv_len != 1 + PSIZE(p.prefixlen)) { + sbuf_push( + log, indent, + "TLV size differs from expected size for the prefixlen. " + "(expected %u but got %" PRIu8 ")\n", + 1 + PSIZE(p.prefixlen), tlv_len); + return 1; + } + + stream_get(&p.prefix, s, PSIZE(p.prefixlen)); + + if (subtlvs->source_prefix) { + sbuf_push( + log, indent, + "WARNING: source prefix Sub-TLV present multiple times.\n"); + /* Ignore all but first occurrence of the source prefix Sub-TLV + */ + return 0; + } + + subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(p)); + memcpy(subtlvs->source_prefix, &p, sizeof(p)); + return 0; +} + +/* Functions related to subtlvs */ + +static struct isis_subtlvs *isis_alloc_subtlvs(void) +{ + struct isis_subtlvs *result; + + result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result)); + + return result; +} + +static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) +{ + if (!subtlvs) + return NULL; + + struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + + rv->source_prefix = + copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix); + return rv; +} + +static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf, + int indent) +{ + format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent); +} + +static void isis_free_subtlvs(struct isis_subtlvs *subtlvs) +{ + if (!subtlvs) + return; + + XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix); + + XFREE(MTYPE_ISIS_SUBTLV, subtlvs); +} + +static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s) +{ + int rv; + size_t subtlv_len_pos = stream_get_endp(s); + + if (STREAM_WRITEABLE(s) < 1) + return 1; + + stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */ + + rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s); + if (rv) + return rv; + + size_t subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1; + if (subtlv_len > 255) + return 1; + + stream_putc_at(s, subtlv_len_pos, subtlv_len); + return 0; +} + +static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, + struct stream *stream, struct sbuf *log, void *dest, + int indent); + +/* Functions related to TLVs 1 Area Addresses */ + +static struct isis_item *copy_item_area_address(struct isis_item *i) +{ + struct isis_area_address *addr = (struct isis_area_address *)i; + struct isis_area_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->len = addr->len; + memcpy(rv->addr, addr->addr, addr->len); + return (struct isis_item *)rv; +} + +static void format_item_area_address(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_area_address *addr = (struct isis_area_address *)i; + + sbuf_push(buf, indent, "Area Address: %s\n", + isonet_print(addr->addr, addr->len)); +} + +static void free_item_area_address(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_area_address(struct isis_item *i, struct stream *s) +{ + struct isis_area_address *addr = (struct isis_area_address *)i; + + if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) + return 1; + stream_putc(s, addr->len); + stream_put(s, addr->addr, addr->len); + return 0; +} + +static int unpack_item_area_address(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_area_address *rv = NULL; + + sbuf_push(log, indent, "Unpack area address...\n"); + if (len < 1) { + sbuf_push( + log, indent, + "Not enough data left. (Expected 1 byte of address length, got %" PRIu8 + ")\n", + len); + goto out; + } + + rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->len = stream_getc(s); + + if (len < 1 + rv->len) { + sbuf_push(log, indent, "Not enough data left. (Expected %" PRIu8 + " bytes of address, got %" PRIu8 ")\n", + rv->len, len - 1); + goto out; + } + + if (rv->len < 1 || rv->len > 20) { + sbuf_push(log, indent, + "Implausible area address length %" PRIu8 "\n", + rv->len); + goto out; + } + + stream_get(rv->addr, s, rv->len); + + format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv, + log, indent + 2); + append_item(&tlvs->area_addresses, (struct isis_item *)rv); + return 0; +out: + XFREE(MTYPE_ISIS_TLV, rv); + return 1; +} + +/* Functions related to TLV 2 (Old-Style) IS Reach */ +static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i) +{ + struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; + struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + memcpy(rv->id, r->id, 7); + rv->metric = r->metric; + return (struct isis_item *)rv; +} + +static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; + + sbuf_push(buf, indent, "IS Reachability: %s (Metric: %" PRIu8 ")\n", + isis_format_id(r->id, 7), r->metric); +} + +static void free_item_oldstyle_reach(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s) +{ + struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; + + if (STREAM_WRITEABLE(s) < 11) + return 1; + + stream_putc(s, r->metric); + stream_putc(s, 0x80); /* delay metric - unsupported */ + stream_putc(s, 0x80); /* expense metric - unsupported */ + stream_putc(s, 0x80); /* error metric - unsupported */ + stream_put(s, r->id, 7); + + return 0; +} + +static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack oldstyle reach...\n"); + if (len < 11) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 11 bytes of reach information, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->metric = stream_getc(s); + if ((rv->metric & 0x3f) != rv->metric) { + sbuf_push(log, indent, "Metric has unplausible format\n"); + rv->metric &= 0x3f; + } + stream_forward_getp(s, 3); /* Skip other metrics */ + stream_get(rv->id, s, 7); + + format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log, + indent + 2); + append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv); + return 0; +} + +/* Functions related to TLV 6 LAN Neighbors */ +static struct isis_item *copy_item_lan_neighbor(struct isis_item *i) +{ + struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; + struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + memcpy(rv->mac, n->mac, 6); + return (struct isis_item *)rv; +} + +static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; + + sbuf_push(buf, indent, "LAN Neighbor: %s\n", isis_format_id(n->mac, 6)); +} + +static void free_item_lan_neighbor(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s) +{ + struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; + + if (STREAM_WRITEABLE(s) < 6) + return 1; + + stream_put(s, n->mac, 6); + + return 0; +} + +static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack LAN neighbor...\n"); + if (len < 6) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 6 bytes of mac, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + stream_get(rv->mac, s, 6); + + format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, indent + 2); + append_item(&tlvs->lan_neighbor, (struct isis_item *)rv); + return 0; +} + +/* Functions related to TLV 9 LSP Entry */ +static struct isis_item *copy_item_lsp_entry(struct isis_item *i) +{ + struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->rem_lifetime = e->rem_lifetime; + memcpy(rv->id, e->id, sizeof(rv->id)); + rv->seqno = e->seqno; + rv->checksum = e->checksum; + + return (struct isis_item *)rv; +} + +static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + + sbuf_push(buf, indent, "LSP Entry: %s, seq 0x%08" PRIx32 + ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s\n", + isis_format_id(e->id, 8), e->seqno, e->checksum, + e->rem_lifetime); +} + +static void free_item_lsp_entry(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_lsp_entry(struct isis_item *i, struct stream *s) +{ + struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + + if (STREAM_WRITEABLE(s) < 16) + return 1; + + stream_putw(s, e->rem_lifetime); + stream_put(s, e->id, 8); + stream_putl(s, e->seqno); + stream_putw(s, e->checksum); + + return 0; +} + +static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack LSP entry...\n"); + if (len < 16) { + sbuf_push( + log, indent, + "Not enough data left. (Expected 16 bytes of LSP info, got %" PRIu8, + len); + return 1; + } + + struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->rem_lifetime = stream_getw(s); + stream_get(rv->id, s, 8); + rv->seqno = stream_getl(s); + rv->checksum = stream_getw(s); + + format_item_lsp_entry(mtid, (struct isis_item *)rv, log, indent + 2); + append_item(&tlvs->lsp_entries, (struct isis_item *)rv); + return 0; +} + +/* Functions related to TLVs 22/222 Extended Reach/MT Reach */ + +static struct isis_item *copy_item_extended_reach(struct isis_item *i) +{ + struct isis_extended_reach *r = (struct isis_extended_reach *)i; + struct isis_extended_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + memcpy(rv->id, r->id, 7); + rv->metric = r->metric; + + if (r->subtlvs && r->subtlv_len) { + rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, r->subtlv_len); + memcpy(rv->subtlvs, r->subtlvs, r->subtlv_len); + rv->subtlv_len = r->subtlv_len; + } + + return (struct isis_item *)rv; +} + +static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_extended_reach *r = (struct isis_extended_reach *)i; + + sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", + isis_format_id(r->id, 7), r->metric); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); + + if (r->subtlv_len && r->subtlvs) + mpls_te_print_detail(buf, indent + 2, r->subtlvs, r->subtlv_len); +} + +static void free_item_extended_reach(struct isis_item *i) +{ + struct isis_extended_reach *item = (struct isis_extended_reach *)i; + XFREE(MTYPE_ISIS_TLV, item->subtlvs); + XFREE(MTYPE_ISIS_TLV, item); +} + +static int pack_item_extended_reach(struct isis_item *i, struct stream *s) +{ + struct isis_extended_reach *r = (struct isis_extended_reach *)i; + + if (STREAM_WRITEABLE(s) < 11 + (unsigned)r->subtlv_len) + return 1; + stream_put(s, r->id, sizeof(r->id)); + stream_put3(s, r->metric); + stream_putc(s, r->subtlv_len); + stream_put(s, r->subtlvs, r->subtlv_len); + return 0; +} + +static int unpack_item_extended_reach(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_extended_reach *rv = NULL; + uint8_t subtlv_len; + struct isis_item_list *items; + + if (mtid == ISIS_MT_IPV4_UNICAST) { + items = &tlvs->extended_reach; + } else { + items = isis_get_mt_items(&tlvs->mt_reach, mtid); + } + + sbuf_push(log, indent, "Unpacking %s reachability...\n", + (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt"); + + if (len < 11) { + sbuf_push(log, indent, + "Not enough data left. (expected 11 or more bytes, got %" + PRIu8 ")\n", + len); + goto out; + } + + rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + stream_get(rv->id, s, 7); + rv->metric = stream_get3(s); + subtlv_len = stream_getc(s); + + format_item_extended_reach(mtid, (struct isis_item *)rv, log, + indent + 2); + + if ((size_t)len < ((size_t)11) + subtlv_len) { + sbuf_push(log, indent, + "Not enough data left for subtlv size %" PRIu8 + ", there are only %" PRIu8 " bytes left.\n", + subtlv_len, len - 11); + goto out; + } + + sbuf_push(log, indent, "Storing %" PRIu8 " bytes of subtlvs\n", + subtlv_len); + + if (subtlv_len) { + size_t subtlv_start = stream_get_getp(s); + + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s, + log, NULL, indent + 4)) { + goto out; + } + + stream_set_getp(s, subtlv_start); + + rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len); + stream_get(rv->subtlvs, s, subtlv_len); + rv->subtlv_len = subtlv_len; + } + + append_item(items, (struct isis_item *)rv); + return 0; +out: + if (rv) + free_item_extended_reach((struct isis_item *)rv); + + return 1; +} + +/* Functions related to TLV 128 (Old-Style) IP Reach */ +static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i) +{ + struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; + struct isis_oldstyle_ip_reach *rv = + XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->metric = r->metric; + rv->prefix = r->prefix; + return (struct isis_item *)rv; +} + +static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; + char prefixbuf[PREFIX2STR_BUFFER]; + + sbuf_push(buf, indent, "IP Reachability: %s (Metric: %" PRIu8 ")\n", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric); +} + +static void free_item_oldstyle_ip_reach(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s) +{ + struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; + + if (STREAM_WRITEABLE(s) < 12) + return 1; + + stream_putc(s, r->metric); + stream_putc(s, 0x80); /* delay metric - unsupported */ + stream_putc(s, 0x80); /* expense metric - unsupported */ + stream_putc(s, 0x80); /* error metric - unsupported */ + stream_put(s, &r->prefix.prefix, 4); + + struct in_addr mask; + masklen2ip(r->prefix.prefixlen, &mask); + stream_put(s, &mask, sizeof(mask)); + + return 0; +} + +static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + sbuf_push(log, indent, "Unpack oldstyle ip reach...\n"); + if (len < 12) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 12 bytes of reach information, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_oldstyle_ip_reach *rv = + XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->metric = stream_getc(s); + if ((rv->metric & 0x7f) != rv->metric) { + sbuf_push(log, indent, "Metric has unplausible format\n"); + rv->metric &= 0x7f; + } + stream_forward_getp(s, 3); /* Skip other metrics */ + rv->prefix.family = AF_INET; + stream_get(&rv->prefix.prefix, s, 4); + + struct in_addr mask; + stream_get(&mask, s, 4); + rv->prefix.prefixlen = ip_masklen(mask); + + format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log, + indent + 2); + append_item(dest, (struct isis_item *)rv); + return 0; +} + + +/* Functions related to TLV 129 protocols supported */ + +static void copy_tlv_protocols_supported(struct isis_protocols_supported *src, + struct isis_protocols_supported *dest) +{ + if (!src->protocols || !src->count) + return; + dest->count = src->count; + dest->protocols = XCALLOC(MTYPE_ISIS_TLV, src->count); + memcpy(dest->protocols, src->protocols, src->count); +} + +static void format_tlv_protocols_supported(struct isis_protocols_supported *p, + struct sbuf *buf, int indent) +{ + if (!p || !p->count || !p->protocols) + return; + + sbuf_push(buf, indent, "Protocols Supported: "); + for (uint8_t i = 0; i < p->count; i++) { + sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]), + (i + 1 < p->count) ? ", " : ""); + } + sbuf_push(buf, 0, "\n"); +} + +static void free_tlv_protocols_supported(struct isis_protocols_supported *p) +{ + XFREE(MTYPE_ISIS_TLV, p->protocols); +} + +static int pack_tlv_protocols_supported(struct isis_protocols_supported *p, + struct stream *s) +{ + if (!p || !p->count || !p->protocols) + return 0; + + if (STREAM_WRITEABLE(s) < (unsigned)(p->count + 2)) + return 1; + + stream_putc(s, ISIS_TLV_PROTOCOLS_SUPPORTED); + stream_putc(s, p->count); + stream_put(s, p->protocols, p->count); + return 0; +} + +static int unpack_tlv_protocols_supported(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpacking Protocols Supported TLV...\n"); + if (!tlv_len) { + sbuf_push(log, indent, "WARNING: No protocols included\n"); + return 0; + } + if (tlvs->protocols_supported.protocols) { + sbuf_push( + log, indent, + "WARNING: protocols supported TLV present multiple times.\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + tlvs->protocols_supported.count = tlv_len; + tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len); + stream_get(tlvs->protocols_supported.protocols, s, tlv_len); + + format_tlv_protocols_supported(&tlvs->protocols_supported, log, + indent + 2); + return 0; +} + +/* Functions related to TLV 132 IPv4 Interface addresses */ +static struct isis_item *copy_item_ipv4_address(struct isis_item *i) +{ + struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; + struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->addr = a->addr; + return (struct isis_item *)rv; +} + +static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; + char addrbuf[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf)); + sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf); +} + +static void free_item_ipv4_address(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_ipv4_address(struct isis_item *i, struct stream *s) +{ + struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; + + if (STREAM_WRITEABLE(s) < 4) + return 1; + + stream_put(s, &a->addr, 4); + + return 0; +} + +static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack IPv4 Interface address...\n"); + if (len < 4) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 4 bytes of IPv4 address, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + stream_get(&rv->addr, s, 4); + + format_item_ipv4_address(mtid, (struct isis_item *)rv, log, indent + 2); + append_item(&tlvs->ipv4_address, (struct isis_item *)rv); + return 0; +} + + +/* Functions related to TLV 232 IPv6 Interface addresses */ +static struct isis_item *copy_item_ipv6_address(struct isis_item *i) +{ + struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; + struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->addr = a->addr; + return (struct isis_item *)rv; +} + +static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; + char addrbuf[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf)); + sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf); +} + +static void free_item_ipv6_address(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_ipv6_address(struct isis_item *i, struct stream *s) +{ + struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; + + if (STREAM_WRITEABLE(s) < 16) + return 1; + + stream_put(s, &a->addr, 16); + + return 0; +} + +static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack IPv6 Interface address...\n"); + if (len < 16) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 16 bytes of IPv6 address, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + stream_get(&rv->addr, s, 16); + + format_item_ipv6_address(mtid, (struct isis_item *)rv, log, indent + 2); + append_item(&tlvs->ipv6_address, (struct isis_item *)rv); + return 0; +} + + +/* Functions related to TLV 229 MT Router information */ +static struct isis_item *copy_item_mt_router_info(struct isis_item *i) +{ + struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; + struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->overload = info->overload; + rv->attached = info->attached; + rv->mtid = info->mtid; + return (struct isis_item *)rv; +} + +static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; + + sbuf_push(buf, indent, "MT Router Info: %s%s%s\n", + isis_mtid2str(info->mtid), + info->overload ? " Overload" : "", + info->attached ? " Attached" : ""); +} + +static void free_item_mt_router_info(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_mt_router_info(struct isis_item *i, struct stream *s) +{ + struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; + + if (STREAM_WRITEABLE(s) < 2) + return 1; + + uint16_t entry = info->mtid; + + if (info->overload) + entry |= ISIS_MT_OL_MASK; + if (info->attached) + entry |= ISIS_MT_AT_MASK; + + stream_putw(s, entry); + + return 0; +} + +static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack MT Router info...\n"); + if (len < 2) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 2 bytes of MT info, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + uint16_t entry = stream_getw(s); + rv->overload = entry & ISIS_MT_OL_MASK; + rv->attached = entry & ISIS_MT_AT_MASK; + rv->mtid = entry & ISIS_MT_MASK; + + format_item_mt_router_info(mtid, (struct isis_item *)rv, log, + indent + 2); + append_item(&tlvs->mt_router_info, (struct isis_item *)rv); + return 0; +} + +/* Functions related to TLV 134 TE Router ID */ + +static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id) +{ + if (!id) + return NULL; + + struct in_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + memcpy(rv, id, sizeof(*rv)); + return rv; +} + +static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf, + int indent) +{ + if (!id) + return; + + char addrbuf[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf)); + sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf); +} + +static void free_tlv_te_router_id(struct in_addr *id) +{ + XFREE(MTYPE_ISIS_TLV, id); +} + +static int pack_tlv_te_router_id(const struct in_addr *id, struct stream *s) +{ + if (!id) + return 0; + + if (STREAM_WRITEABLE(s) < (unsigned)(2 + sizeof(*id))) + return 1; + + stream_putc(s, ISIS_TLV_TE_ROUTER_ID); + stream_putc(s, 4); + stream_put(s, id, 4); + return 0; +} + +static int unpack_tlv_te_router_id(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpacking TE Router ID TLV...\n"); + if (tlv_len != 4) { + sbuf_push(log, indent, "WARNING: Length invalid\n"); + return 1; + } + + if (tlvs->te_router_id) { + sbuf_push(log, indent, + "WARNING: TE Router ID present multiple times.\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4); + stream_get(tlvs->te_router_id, s, 4); + format_tlv_te_router_id(tlvs->te_router_id, log, indent + 2); + return 0; +} + + +/* Functions related to TLVs 135/235 extended IP reach/MT IP Reach */ + +static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i) +{ + struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; + struct isis_extended_ip_reach *rv = + XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->metric = r->metric; + rv->down = r->down; + rv->prefix = r->prefix; + + return (struct isis_item *)rv; +} + +static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; + char prefixbuf[PREFIX2STR_BUFFER]; + + sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric, + r->down ? " Down" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); +} + +static void free_item_extended_ip_reach(struct isis_item *i) +{ + struct isis_extended_ip_reach *item = + (struct isis_extended_ip_reach *)i; + XFREE(MTYPE_ISIS_TLV, item); +} + +static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) +{ + struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; + uint8_t control; + + if (STREAM_WRITEABLE(s) < 5) + return 1; + stream_putl(s, r->metric); + + control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0; + control |= r->prefix.prefixlen; + stream_putc(s, control); + + if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) + return 1; + stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen)); + return 0; +} + +static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_extended_ip_reach *rv = NULL; + size_t consume; + uint8_t control, subtlv_len; + struct isis_item_list *items; + + if (mtid == ISIS_MT_IPV4_UNICAST) { + items = &tlvs->extended_ip_reach; + } else { + items = isis_get_mt_items(&tlvs->mt_ip_reach, mtid); + } + + sbuf_push(log, indent, "Unpacking %s IPv4 reachability...\n", + (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt"); + + consume = 5; + if (len < consume) { + sbuf_push(log, indent, + "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n", + len); + goto out; + } + + rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->metric = stream_getl(s); + control = stream_getc(s); + rv->down = (control & ISIS_EXTENDED_IP_REACH_DOWN); + rv->prefix.family = AF_INET; + rv->prefix.prefixlen = control & 0x3f; + if (rv->prefix.prefixlen > 32) { + sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv4\n", + rv->prefix.prefixlen); + goto out; + } + + consume += PSIZE(rv->prefix.prefixlen); + if (len < consume) { + sbuf_push(log, indent, + "Expected %u bytes of prefix, but only %u bytes available.\n", + PSIZE(rv->prefix.prefixlen), len - 5); + goto out; + } + stream_get(&rv->prefix.prefix.s_addr, s, PSIZE(rv->prefix.prefixlen)); + in_addr_t orig_prefix = rv->prefix.prefix.s_addr; + apply_mask_ipv4(&rv->prefix); + if (orig_prefix != rv->prefix.prefix.s_addr) + sbuf_push(log, indent + 2, + "WARNING: Prefix had hostbits set.\n"); + format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log, + indent + 2); + + if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) { + consume += 1; + if (len < consume) { + sbuf_push(log, indent, + "Expected 1 byte of subtlv len, but no more data present.\n"); + goto out; + } + subtlv_len = stream_getc(s); + + if (!subtlv_len) { + sbuf_push(log, indent + 2, + " WARNING: subtlv bit is set, but there are no subtlvs.\n"); + } + consume += subtlv_len; + if (len < consume) { + sbuf_push(log, indent, + "Expected %" PRIu8 + " bytes of subtlvs, but only %u bytes available.\n", + subtlv_len, + len - 6 - PSIZE(rv->prefix.prefixlen)); + goto out; + } + sbuf_push(log, indent, "Skipping %" PRIu8 " bytes of subvls", + subtlv_len); + stream_forward_getp(s, subtlv_len); + } + + append_item(items, (struct isis_item *)rv); + return 0; +out: + if (rv) + free_item_extended_ip_reach((struct isis_item *)rv); + return 1; +} + +/* Functions related to TLV 137 Dynamic Hostname */ + +static char *copy_tlv_dynamic_hostname(const char *hostname) +{ + if (!hostname) + return NULL; + + return XSTRDUP(MTYPE_ISIS_TLV, hostname); +} + +static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf, + int indent) +{ + if (!hostname) + return; + + sbuf_push(buf, indent, "Hostname: %s\n", hostname); +} + +static void free_tlv_dynamic_hostname(char *hostname) +{ + XFREE(MTYPE_ISIS_TLV, hostname); +} + +static int pack_tlv_dynamic_hostname(const char *hostname, struct stream *s) +{ + if (!hostname) + return 0; + + uint8_t name_len = strlen(hostname); + + if (STREAM_WRITEABLE(s) < (unsigned)(2 + name_len)) + return 1; + + stream_putc(s, ISIS_TLV_DYNAMIC_HOSTNAME); + stream_putc(s, name_len); + stream_put(s, hostname, name_len); + return 0; +} + +static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpacking Dynamic Hostname TLV...\n"); + if (!tlv_len) { + sbuf_push(log, indent, "WARNING: No hostname included\n"); + return 0; + } + + if (tlvs->hostname) { + sbuf_push(log, indent, + "WARNING: Hostname present multiple times.\n"); + stream_forward_getp(s, tlv_len); + return 0; + } + + tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1); + stream_get(tlvs->hostname, s, tlv_len); + tlvs->hostname[tlv_len] = '\0'; + + bool sane = true; + for (uint8_t i = 0; i < tlv_len; i++) { + if ((unsigned char)tlvs->hostname[i] > 127 + || !isprint(tlvs->hostname[i])) { + sane = false; + tlvs->hostname[i] = '?'; + } + } + if (!sane) { + sbuf_push( + log, indent, + "WARNING: Hostname contained non-printable/non-ascii characters.\n"); + } + + return 0; +} + +/* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */ + +static struct isis_item *copy_item_ipv6_reach(struct isis_item *i) +{ + struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; + struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + rv->metric = r->metric; + rv->down = r->down; + rv->external = r->external; + rv->prefix = r->prefix; + rv->subtlvs = copy_subtlvs(r->subtlvs); + + return (struct isis_item *)rv; +} + +static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; + char prefixbuf[PREFIX2STR_BUFFER]; + + sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s", + (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), + r->metric, + r->down ? " Down" : "", + r->external ? " External" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, indent + 4); + } +} + +static void free_item_ipv6_reach(struct isis_item *i) +{ + struct isis_ipv6_reach *item = (struct isis_ipv6_reach *)i; + + isis_free_subtlvs(item->subtlvs); + XFREE(MTYPE_ISIS_TLV, item); +} + +static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s) +{ + struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; + uint8_t control; + + if (STREAM_WRITEABLE(s) < 6) + return 1; + stream_putl(s, r->metric); + + control = r->down ? ISIS_IPV6_REACH_DOWN : 0; + control |= r->external ? ISIS_IPV6_REACH_EXTERNAL : 0; + control |= r->subtlvs ? ISIS_IPV6_REACH_SUBTLV : 0; + + stream_putc(s, control); + stream_putc(s, r->prefix.prefixlen); + + if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) + return 1; + stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen)); + + if (r->subtlvs) + return pack_subtlvs(r->subtlvs, s); + + return 0; +} + +static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_ipv6_reach *rv = NULL; + size_t consume; + uint8_t control, subtlv_len; + struct isis_item_list *items; + + if (mtid == ISIS_MT_IPV4_UNICAST) { + items = &tlvs->ipv6_reach; + } else { + items = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid); + } + + sbuf_push(log, indent, "Unpacking %sIPv6 reachability...\n", + (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "mt "); + consume = 6; + if (len < consume) { + sbuf_push(log, indent, + "Not enough data left. (expected 6 or more bytes, got %" + PRIu8 ")\n", + len); + goto out; + } + + rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->metric = stream_getl(s); + control = stream_getc(s); + rv->down = (control & ISIS_IPV6_REACH_DOWN); + rv->external = (control & ISIS_IPV6_REACH_EXTERNAL); + + rv->prefix.family = AF_INET6; + rv->prefix.prefixlen = stream_getc(s); + if (rv->prefix.prefixlen > 128) { + sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n", + rv->prefix.prefixlen); + goto out; + } + + consume += PSIZE(rv->prefix.prefixlen); + if (len < consume) { + sbuf_push(log, indent, + "Expected %u bytes of prefix, but only %u bytes available.\n", + PSIZE(rv->prefix.prefixlen), len - 6); + goto out; + } + stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen)); + struct in6_addr orig_prefix = rv->prefix.prefix; + apply_mask_ipv6(&rv->prefix); + if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix))) + sbuf_push(log, indent + 2, + "WARNING: Prefix had hostbits set.\n"); + format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2); + + if (control & ISIS_IPV6_REACH_SUBTLV) { + consume += 1; + if (len < consume) { + sbuf_push(log, indent, + "Expected 1 byte of subtlv len, but no more data persent.\n"); + goto out; + } + subtlv_len = stream_getc(s); + + if (!subtlv_len) { + sbuf_push(log, indent + 2, + " WARNING: subtlv bit set, but there are no subtlvs.\n"); + } + consume += subtlv_len; + if (len < consume) { + sbuf_push(log, indent, + "Expected %" PRIu8 + " bytes of subtlvs, but only %u bytes available.\n", + subtlv_len, + len - 6 - PSIZE(rv->prefix.prefixlen)); + goto out; + } + + rv->subtlvs = isis_alloc_subtlvs(); + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s, + log, rv->subtlvs, indent + 4)) { + goto out; + } + } + + append_item(items, (struct isis_item *)rv); + return 0; +out: + if (rv) + free_item_ipv6_reach((struct isis_item *)rv); + return 1; +} + +/* Functions related to TLV 10 Authentication */ +static struct isis_item *copy_item_auth(struct isis_item *i) +{ + struct isis_auth *auth = (struct isis_auth *)i; + struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->type = auth->type; + rv->length = auth->length; + memcpy(rv->value, auth->value, sizeof(rv->value)); + return (struct isis_item *)rv; +} + +static void format_item_auth(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_auth *auth = (struct isis_auth *)i; + char obuf[768]; + + sbuf_push(buf, indent, "Authentication:\n"); + switch (auth->type) { + case ISIS_PASSWD_TYPE_CLEARTXT: + zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length); + sbuf_push(buf, indent, " Password: %s\n", obuf); + break; + case ISIS_PASSWD_TYPE_HMAC_MD5: + for (unsigned int i = 0; i < 16; i++) { + snprintf(obuf + 2 * i, sizeof(obuf) - 2 * i, + "%02" PRIx8, auth->value[i]); + } + sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf); + break; + default: + sbuf_push(buf, indent, " Unknown (%" PRIu8 ")\n", auth->type); + break; + }; +} + +static void free_item_auth(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_TLV, i); +} + +static int pack_item_auth(struct isis_item *i, struct stream *s) +{ + struct isis_auth *auth = (struct isis_auth *)i; + + if (STREAM_WRITEABLE(s) < 1) + return 1; + stream_putc(s, auth->type); + + switch (auth->type) { + case ISIS_PASSWD_TYPE_CLEARTXT: + if (STREAM_WRITEABLE(s) < auth->length) + return 1; + stream_put(s, auth->passwd, auth->length); + break; + case ISIS_PASSWD_TYPE_HMAC_MD5: + if (STREAM_WRITEABLE(s) < 16) + return 1; + auth->offset = stream_get_endp(s); + stream_put(s, NULL, 16); + break; + default: + return 1; + } + + return 0; +} + +static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + + sbuf_push(log, indent, "Unpack Auth TLV...\n"); + if (len < 1) { + sbuf_push( + log, indent, + "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8 + ")\n", + len); + return 1; + } + + struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->type = stream_getc(s); + rv->length = len - 1; + + if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) { + sbuf_push( + log, indent, + "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8 + ")\n", + rv->length); + XFREE(MTYPE_ISIS_TLV, rv); + return 1; + } + + rv->offset = stream_get_getp(s); + stream_get(rv->value, s, rv->length); + format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2); + append_item(&tlvs->isis_auth, (struct isis_item *)rv); + return 0; +} + +/* Functions relating to item TLVs */ + +static void init_item_list(struct isis_item_list *items) +{ + items->head = NULL; + items->tail = &items->head; + items->count = 0; +} + +static struct isis_item *copy_item(enum isis_tlv_context context, + enum isis_tlv_type type, + struct isis_item *item) +{ + const struct tlv_ops *ops = tlv_table[context][type]; + + if (ops && ops->copy_item) + return ops->copy_item(item); + + assert(!"Unknown item tlv type!"); + return NULL; +} + +static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item_list *src, struct isis_item_list *dest) +{ + struct isis_item *item; + + init_item_list(dest); + + for (item = src->head; item; item = item->next) { + append_item(dest, copy_item(context, type, item)); + } +} + +static void format_item(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item *i, + struct sbuf *buf, int indent) +{ + const struct tlv_ops *ops = tlv_table[context][type]; + + if (ops && ops->format_item) { + ops->format_item(mtid, i, buf, indent); + return; + } + + assert(!"Unknown item tlv type!"); +} + +static void format_items_(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item_list *items, + struct sbuf *buf, int indent) +{ + struct isis_item *i; + + for (i = items->head; i; i = i->next) + format_item(mtid, context, type, i, buf, indent); +} +#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) + +static void free_item(enum isis_tlv_context tlv_context, + enum isis_tlv_type tlv_type, struct isis_item *item) +{ + const struct tlv_ops *ops = tlv_table[tlv_context][tlv_type]; + + if (ops && ops->free_item) { + ops->free_item(item); + return; + } + + assert(!"Unknown item tlv type!"); +} + +static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item_list *items) +{ + struct isis_item *item, *next_item; + + for (item = items->head; item; item = next_item) { + next_item = item->next; + free_item(context, type, item); + } +} + +static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item *i, struct stream *s, + struct isis_tlvs **fragment_tlvs, + struct pack_order_entry *pe, uint16_t mtid) +{ + const struct tlv_ops *ops = tlv_table[context][type]; + + if (ops && ops->pack_item) { + return ops->pack_item(i, s); + } + + assert(!"Unknown item tlv type!"); + return 1; +} + +static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *pe, + struct isis_tlvs *fragment_tlvs, uint16_t mtid) +{ + struct isis_item_list *l; + + if (pe->how_to_pack == ISIS_ITEMS) { + l = (struct isis_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack); + } else { + struct isis_mt_item_list *m; + m = (struct isis_mt_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack); + l = isis_get_mt_items(m, mtid); + } + + append_item(l, copy_item(pe->context, pe->type, i)); +} + +static int pack_items_(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item_list *items, + struct stream *s, struct isis_tlvs **fragment_tlvs, + struct pack_order_entry *pe, + struct isis_tlvs *(*new_fragment)(struct list *l), + struct list *new_fragment_arg) +{ + size_t len_pos, last_len, len; + struct isis_item *item = NULL; + int rv; + + if (!items->head) + return 0; + +top: + if (STREAM_WRITEABLE(s) < 2) + goto too_long; + + stream_putc(s, type); + len_pos = stream_get_endp(s); + stream_putc(s, 0); /* Put 0 as length for now */ + + if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(type) + && mtid != ISIS_MT_IPV4_UNICAST) { + if (STREAM_WRITEABLE(s) < 2) + goto too_long; + stream_putw(s, mtid); + } + + if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_OLDSTYLE_REACH) { + if (STREAM_WRITEABLE(s) < 1) + goto too_long; + stream_putc(s, 0); /* Virtual flag is set to 0 */ + } + + last_len = len = 0; + for (item = item ? item : items->head; item; item = item->next) { + rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid); + if (rv) + goto too_long; + + len = stream_get_endp(s) - len_pos - 1; + + /* Multiple auths don't go into one TLV, so always break */ + if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_AUTH) { + item = item->next; + break; + } + + if (len > 255) { + if (!last_len) /* strange, not a single item fit */ + return 1; + /* drop last tlv, otherwise, its too long */ + stream_set_endp(s, len_pos + 1 + last_len); + len = last_len; + break; + } + + if (fragment_tlvs) + add_item_to_fragment(item, pe, *fragment_tlvs, mtid); + + last_len = len; + } + + stream_putc_at(s, len_pos, len); + if (item) + goto top; + + return 0; +too_long: + if (!fragment_tlvs) + return 1; + stream_reset(s); + *fragment_tlvs = new_fragment(new_fragment_arg); + goto top; +} +#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) + +static void append_item(struct isis_item_list *dest, struct isis_item *item) +{ + *dest->tail = item; + dest->tail = &(*dest->tail)->next; + dest->count++; +} + +static int unpack_item(uint16_t mtid, enum isis_tlv_context context, + uint8_t tlv_type, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + const struct tlv_ops *ops = tlv_table[context][tlv_type]; + + if (ops && ops->unpack_item) + return ops->unpack_item(mtid, len, s, log, dest, indent); + + assert(!"Unknown item tlv type!"); + sbuf_push(log, indent, "Unknown item tlv type!\n"); + return 1; +} + +static int unpack_tlv_with_items(enum isis_tlv_context context, + uint8_t tlv_type, uint8_t tlv_len, + struct stream *s, struct sbuf *log, void *dest, + int indent) +{ + size_t tlv_start; + size_t tlv_pos; + int rv; + uint16_t mtid; + + tlv_start = stream_get_getp(s); + tlv_pos = 0; + + if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(tlv_type)) { + if (tlv_len < 2) { + sbuf_push(log, indent, + "TLV is too short to contain MTID\n"); + return 1; + } + mtid = stream_getw(s) & ISIS_MT_MASK; + tlv_pos += 2; + sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n", + isis_mtid2str(mtid)); + } else { + sbuf_push(log, indent, "Unpacking as item TLV...\n"); + mtid = ISIS_MT_IPV4_UNICAST; + } + + if (context == ISIS_CONTEXT_LSP + && tlv_type == ISIS_TLV_OLDSTYLE_REACH) { + if (tlv_len - tlv_pos < 1) { + sbuf_push(log, indent, + "TLV is too short for old style reach\n"); + return 1; + } + stream_forward_getp(s, 1); + tlv_pos += 1; + } + + if (context == ISIS_CONTEXT_LSP + && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH) { + struct isis_tlvs *tlvs = dest; + dest = &tlvs->oldstyle_ip_reach; + } else if (context == ISIS_CONTEXT_LSP + && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH_EXT) { + struct isis_tlvs *tlvs = dest; + dest = &tlvs->oldstyle_ip_reach_ext; + } + + if (context == ISIS_CONTEXT_LSP + && tlv_type == ISIS_TLV_MT_ROUTER_INFO) { + struct isis_tlvs *tlvs = dest; + tlvs->mt_router_info_empty = (tlv_pos >= (size_t)tlv_len); + } + + while (tlv_pos < (size_t)tlv_len) { + rv = unpack_item(mtid, context, tlv_type, tlv_len - tlv_pos, s, + log, dest, indent + 2); + if (rv) + return rv; + + tlv_pos = stream_get_getp(s) - tlv_start; + } + + return 0; +} + +/* Functions to manipulate mt_item_lists */ + +static int isis_mt_item_list_cmp(const struct isis_item_list *a, + const struct isis_item_list *b) +{ + if (a->mtid < b->mtid) + return -1; + if (a->mtid > b->mtid) + return 1; + return 0; +} + +RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp); +RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp); + +struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m, + uint16_t mtid) +{ + struct isis_item_list *rv; + + rv = isis_lookup_mt_items(m, mtid); + if (!rv) { + rv = XCALLOC(MTYPE_ISIS_MT_ITEM_LIST, sizeof(*rv)); + init_item_list(rv); + rv->mtid = mtid; + RB_INSERT(isis_mt_item_list, m, rv); + } + + return rv; +} + +struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m, + uint16_t mtid) +{ + struct isis_item_list key = {.mtid = mtid}; + + return RB_FIND(isis_mt_item_list, m, &key); +} + +static void free_mt_items(enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_mt_item_list *m) +{ + struct isis_item_list *n, *nnext; + + RB_FOREACH_SAFE(n, isis_mt_item_list, m, nnext) + { + free_items(context, type, n); + RB_REMOVE(isis_mt_item_list, m, n); + XFREE(MTYPE_ISIS_MT_ITEM_LIST, n); + } +} + +static void format_mt_items(enum isis_tlv_context context, + enum isis_tlv_type type, + struct isis_mt_item_list *m, struct sbuf *buf, + int indent) +{ + struct isis_item_list *n; + + RB_FOREACH(n, isis_mt_item_list, m) + { + format_items_(n->mtid, context, type, n, buf, indent); + } +} + +static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_mt_item_list *m, struct stream *s, + struct isis_tlvs **fragment_tlvs, + struct pack_order_entry *pe, + struct isis_tlvs *(*new_fragment)(struct list *l), + struct list *new_fragment_arg) +{ + struct isis_item_list *n; + + RB_FOREACH(n, isis_mt_item_list, m) + { + int rv; + + rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs, + pe, new_fragment, new_fragment_arg); + if (rv) + return rv; + } + + return 0; +} + +static void copy_mt_items(enum isis_tlv_context context, + enum isis_tlv_type type, + struct isis_mt_item_list *src, + struct isis_mt_item_list *dest) +{ + struct isis_item_list *n; + + RB_INIT(isis_mt_item_list, dest); + + RB_FOREACH(n, isis_mt_item_list, src) + { + copy_items(context, type, n, isis_get_mt_items(dest, n->mtid)); + } +} + +/* Functions related to tlvs in general */ + +struct isis_tlvs *isis_alloc_tlvs(void) +{ + struct isis_tlvs *result; + + result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result)); + + init_item_list(&result->isis_auth); + init_item_list(&result->area_addresses); + init_item_list(&result->mt_router_info); + init_item_list(&result->oldstyle_reach); + init_item_list(&result->lan_neighbor); + init_item_list(&result->lsp_entries); + init_item_list(&result->extended_reach); + RB_INIT(isis_mt_item_list, &result->mt_reach); + init_item_list(&result->oldstyle_ip_reach); + init_item_list(&result->oldstyle_ip_reach_ext); + init_item_list(&result->ipv4_address); + init_item_list(&result->ipv6_address); + init_item_list(&result->extended_ip_reach); + RB_INIT(isis_mt_item_list, &result->mt_ip_reach); + init_item_list(&result->ipv6_reach); + RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach); + + return result; +} + +struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs) +{ + struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, + &rv->isis_auth); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, + &tlvs->area_addresses, &rv->area_addresses); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, + &tlvs->mt_router_info, &rv->mt_router_info); + + tlvs->mt_router_info_empty = rv->mt_router_info_empty; + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH, + &tlvs->oldstyle_reach, &rv->oldstyle_reach); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS, + &tlvs->lan_neighbor, &rv->lan_neighbor); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries, + &rv->lsp_entries); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH, + &tlvs->extended_reach, &rv->extended_reach); + + copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach, + &rv->mt_reach); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH, + &tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach); + + copy_tlv_protocols_supported(&tlvs->protocols_supported, + &rv->protocols_supported); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT, + &tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address, + &rv->ipv4_address); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address, + &rv->ipv6_address); + + rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH, + &tlvs->extended_ip_reach, &rv->extended_ip_reach); + + copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH, + &tlvs->mt_ip_reach, &rv->mt_ip_reach); + + rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname); + + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach, + &rv->ipv6_reach); + + copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, + &tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach); + + return rv; +} + +static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent) +{ + format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf, + indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, + &tlvs->area_addresses, buf, indent); + + if (tlvs->mt_router_info_empty) { + sbuf_push(buf, indent, "MT Router Info: None\n"); + } else { + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, + &tlvs->mt_router_info, buf, indent); + } + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH, + &tlvs->oldstyle_reach, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS, + &tlvs->lan_neighbor, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries, + buf, indent); + + format_tlv_dynamic_hostname(tlvs->hostname, buf, indent); + format_tlv_te_router_id(tlvs->te_router_id, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH, + &tlvs->extended_reach, buf, indent); + + format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach, + buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH, + &tlvs->oldstyle_ip_reach, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT, + &tlvs->oldstyle_ip_reach_ext, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, + &tlvs->ipv4_address, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, + &tlvs->ipv6_address, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH, + &tlvs->extended_ip_reach, buf, indent); + + format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH, + &tlvs->mt_ip_reach, buf, indent); + + format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach, + buf, indent); + + format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, + &tlvs->mt_ipv6_reach, buf, indent); +} + +const char *isis_format_tlvs(struct isis_tlvs *tlvs) +{ + static struct sbuf buf; + + if (!sbuf_buf(&buf)) + sbuf_init(&buf, NULL, 0); + + sbuf_reset(&buf); + format_tlvs(tlvs, &buf, 0); + return sbuf_buf(&buf); +} + +void isis_free_tlvs(struct isis_tlvs *tlvs) +{ + if (!tlvs) + return; + + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, + &tlvs->area_addresses); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, + &tlvs->mt_router_info); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH, + &tlvs->oldstyle_reach); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS, + &tlvs->lan_neighbor); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH, + &tlvs->extended_reach); + free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH, + &tlvs->oldstyle_ip_reach); + free_tlv_protocols_supported(&tlvs->protocols_supported); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT, + &tlvs->oldstyle_ip_reach_ext); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, + &tlvs->ipv4_address); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, + &tlvs->ipv6_address); + free_tlv_te_router_id(tlvs->te_router_id); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH, + &tlvs->extended_ip_reach); + free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH, + &tlvs->mt_ip_reach); + free_tlv_dynamic_hostname(tlvs->hostname); + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach); + free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, + &tlvs->mt_ipv6_reach); + + XFREE(MTYPE_ISIS_TLV, tlvs); +} + +static void add_padding(struct stream *s) +{ + while (STREAM_WRITEABLE(s)) { + if (STREAM_WRITEABLE(s) == 1) + break; + uint32_t padding_len = STREAM_WRITEABLE(s) - 2; + + if (padding_len > 255) { + if (padding_len == 256) + padding_len = 254; + else + padding_len = 255; + } + + stream_putc(s, ISIS_TLV_PADDING); + stream_putc(s, padding_len); + stream_put(s, NULL, padding_len); + } +} + +#define LSP_REM_LIFETIME_OFF 10 +#define LSP_CHECKSUM_OFF 24 +static void safe_auth_md5(struct stream *s, uint16_t *checksum, + uint16_t *rem_lifetime) +{ + memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, + sizeof(*rem_lifetime)); + memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime)); + memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum)); + memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum)); +} + +static void restore_auth_md5(struct stream *s, uint16_t checksum, + uint16_t rem_lifetime) +{ + memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime, + sizeof(rem_lifetime)); + memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum)); +} + +static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s, + bool is_lsp) +{ + uint8_t digest[16]; + uint16_t checksum, rem_lifetime; + + if (is_lsp) + safe_auth_md5(s, &checksum, &rem_lifetime); + + memset(STREAM_DATA(s) + auth->offset, 0, 16); + hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd, + auth->plength, digest); + memcpy(auth->value, digest, 16); + memcpy(STREAM_DATA(s) + auth->offset, digest, 16); + + if (is_lsp) + restore_auth_md5(s, checksum, rem_lifetime); +} + +static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp) +{ + struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head; + + for (struct isis_auth *auth = auth_head; auth; auth = auth->next) { + if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5) + update_auth_hmac_md5(auth, s, is_lsp); + } +} + +static int handle_pack_entry(struct pack_order_entry *pe, + struct isis_tlvs *tlvs, struct stream *stream, + struct isis_tlvs **fragment_tlvs, + struct isis_tlvs *(*new_fragment)(struct list *l), + struct list *new_fragment_arg) +{ + int rv; + + if (pe->how_to_pack == ISIS_ITEMS) { + struct isis_item_list *l; + l = (struct isis_item_list *)(((char *)tlvs) + + pe->what_to_pack); + rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs, + pe, new_fragment, new_fragment_arg); + } else { + struct isis_mt_item_list *l; + l = (struct isis_mt_item_list *)(((char *)tlvs) + + pe->what_to_pack); + rv = pack_mt_items(pe->context, pe->type, l, stream, + fragment_tlvs, pe, new_fragment, + new_fragment_arg); + } + + return rv; +} + +static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream, + struct isis_tlvs *fragment_tlvs, + struct isis_tlvs *(*new_fragment)(struct list *l), + struct list *new_fragment_arg) +{ + int rv; + + /* When fragmenting, don't add auth as it's already accounted for in the + * size we are given. */ + if (!fragment_tlvs) { + rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, + stream, NULL, NULL, NULL, NULL); + if (rv) + return rv; + } + + rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream); + if (rv) + return rv; + if (fragment_tlvs) { + copy_tlv_protocols_supported( + &tlvs->protocols_supported, + &fragment_tlvs->protocols_supported); + } + + rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, + &tlvs->area_addresses, stream, NULL, NULL, NULL, NULL); + if (rv) + return rv; + if (fragment_tlvs) { + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, + &tlvs->area_addresses, + &fragment_tlvs->area_addresses); + } + + + if (tlvs->mt_router_info_empty) { + if (STREAM_WRITEABLE(stream) < 2) + return 1; + stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO); + stream_putc(stream, 0); + if (fragment_tlvs) + fragment_tlvs->mt_router_info_empty = true; + } else { + rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, + &tlvs->mt_router_info, stream, NULL, NULL, NULL, + NULL); + if (rv) + return rv; + if (fragment_tlvs) { + copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, + &tlvs->mt_router_info, + &fragment_tlvs->mt_router_info); + } + } + + rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream); + if (rv) + return rv; + if (fragment_tlvs) + fragment_tlvs->hostname = + copy_tlv_dynamic_hostname(tlvs->hostname); + + rv = pack_tlv_te_router_id(tlvs->te_router_id, stream); + if (rv) + return rv; + if (fragment_tlvs) { + fragment_tlvs->te_router_id = + copy_tlv_te_router_id(tlvs->te_router_id); + } + + for (size_t pack_idx = 0; pack_idx < array_size(pack_order); + pack_idx++) { + rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream, + fragment_tlvs ? &fragment_tlvs : NULL, + new_fragment, new_fragment_arg); + + if (rv) + return rv; + } + + return 0; +} + +int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream, + size_t len_pointer, bool pad, bool is_lsp) +{ + int rv; + + rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL); + if (rv) + return rv; + + if (pad) + add_padding(stream); + + if (len_pointer != (size_t)-1) { + stream_putw_at(stream, len_pointer, stream_get_endp(stream)); + } + + update_auth(tlvs, stream, is_lsp); + + return 0; +} + +static struct isis_tlvs *new_fragment(struct list *l) +{ + struct isis_tlvs *rv = isis_alloc_tlvs(); + + listnode_add(l, rv); + return rv; +} + +struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size) +{ + struct stream *dummy_stream = stream_new(size); + struct list *rv = list_new(); + struct isis_tlvs *fragment_tlvs = new_fragment(rv); + + if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) { + struct listnode *node; + for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs)) + isis_free_tlvs(fragment_tlvs); + list_delete(rv); + rv = NULL; + } + + stream_free(dummy_stream); + return rv; +} + +static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type, + uint8_t tlv_len, struct stream *s, + struct sbuf *log, int indent) +{ + stream_forward_getp(s, tlv_len); + sbuf_push(log, indent, + "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n", + tlv_type, tlv_len); + return 0; +} + +static int unpack_tlv(enum isis_tlv_context context, size_t avail_len, + struct stream *stream, struct sbuf *log, void *dest, + int indent) +{ + uint8_t tlv_type, tlv_len; + const struct tlv_ops *ops; + + sbuf_push(log, indent, "Unpacking TLV...\n"); + + if (avail_len < 2) { + sbuf_push( + log, indent + 2, + "Available data %zu too short to contain a TLV header.\n", + avail_len); + return 1; + } + + tlv_type = stream_getc(stream); + tlv_len = stream_getc(stream); + + sbuf_push(log, indent + 2, + "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n", + tlv_type, tlv_len); + + if (avail_len < ((size_t)tlv_len) + 2) { + sbuf_push(log, indent + 2, + "Available data %zu too short for claimed TLV len %" PRIu8 ".\n", + avail_len - 2, tlv_len); + return 1; + } + + ops = tlv_table[context][tlv_type]; + if (ops && ops->unpack) { + return ops->unpack(context, tlv_type, tlv_len, stream, log, + dest, indent + 2); + } + + return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log, + indent + 2); +} + +static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len, + struct stream *stream, struct sbuf *log, void *dest, + int indent) +{ + int rv; + size_t tlv_start, tlv_pos; + + tlv_start = stream_get_getp(stream); + tlv_pos = 0; + + sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len, + (context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs"); + + while (tlv_pos < avail_len) { + rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest, + indent + 2); + if (rv) + return rv; + + tlv_pos = stream_get_getp(stream) - tlv_start; + } + + return 0; +} + +int isis_unpack_tlvs(size_t avail_len, struct stream *stream, + struct isis_tlvs **dest, const char **log) +{ + static struct sbuf logbuf; + int indent = 0; + int rv; + struct isis_tlvs *result; + + if (!sbuf_buf(&logbuf)) + sbuf_init(&logbuf, NULL, 0); + + sbuf_reset(&logbuf); + if (avail_len > STREAM_READABLE(stream)) { + sbuf_push(&logbuf, indent, + "Stream doesn't contain sufficient data. " + "Claimed %zu, available %zu\n", + avail_len, STREAM_READABLE(stream)); + return 1; + } + + result = isis_alloc_tlvs(); + rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result, + indent); + + *log = sbuf_buf(&logbuf); + *dest = result; + + return rv; +} + +#define TLV_OPS(_name_, _desc_) \ + static const struct tlv_ops tlv_##_name_##_ops = { \ + .name = _desc_, .unpack = unpack_tlv_##_name_, \ + } + +#define ITEM_TLV_OPS(_name_, _desc_) \ + static const struct tlv_ops tlv_##_name_##_ops = { \ + .name = _desc_, \ + .unpack = unpack_tlv_with_items, \ + \ + .pack_item = pack_item_##_name_, \ + .free_item = free_item_##_name_, \ + .unpack_item = unpack_item_##_name_, \ + .format_item = format_item_##_name_, \ + .copy_item = copy_item_##_name_} + +#define SUBTLV_OPS(_name_, _desc_) \ + static const struct tlv_ops subtlv_##_name_##_ops = { \ + .name = _desc_, .unpack = unpack_subtlv_##_name_, \ + } + +ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses"); +ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability"); +ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors"); +ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries"); +ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth"); +ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability"); +ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability"); +TLV_OPS(protocols_supported, "TLV 129 Protocols Supported"); +ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address"); +TLV_OPS(te_router_id, "TLV 134 TE Router ID"); +ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability"); +TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname"); +ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information"); +ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address"); +ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability"); + +SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix"); + +static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { + [ISIS_CONTEXT_LSP] = { + [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops, + [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops, + [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops, + [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops, + [ISIS_TLV_AUTH] = &tlv_auth_ops, + [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops, + [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops, + [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops, + [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops, + [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops, + [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops, + [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops, + [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops, + [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops, + [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops, + [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops, + [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops, + [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops, + [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops, + }, + [ISIS_CONTEXT_SUBTLV_NE_REACH] = {}, + [ISIS_CONTEXT_SUBTLV_IP_REACH] = {}, + [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = { + [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops, + } +}; + +/* Accessor functions */ + +void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd) +{ + free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth); + init_item_list(&tlvs->isis_auth); + + if (passwd->type == ISIS_PASSWD_TYPE_UNUSED) + return; + + struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth)); + + auth->type = passwd->type; + + auth->plength = passwd->len; + memcpy(auth->passwd, passwd->passwd, + MIN(sizeof(auth->passwd), sizeof(passwd->passwd))); + + if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) { + auth->length = passwd->len; + memcpy(auth->value, passwd->passwd, + MIN(sizeof(auth->value), sizeof(passwd->passwd))); + } + + append_item(&tlvs->isis_auth, (struct isis_item *)auth); +} + +void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, + struct list *addresses) +{ + struct listnode *node; + struct area_addr *area_addr; + + for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) { + struct isis_area_address *a = + XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); + + a->len = area_addr->addr_len; + memcpy(a->addr, area_addr->area_addr, 20); + append_item(&tlvs->area_addresses, (struct isis_item *)a); + } +} + +void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors) +{ + struct listnode *node; + u_char *snpa; + + for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) { + struct isis_lan_neighbor *n = + XCALLOC(MTYPE_ISIS_TLV, sizeof(*n)); + + memcpy(n->mac, snpa, 6); + append_item(&tlvs->lan_neighbor, (struct isis_item *)n); + } +} + +void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs, + struct nlpids *nlpids) +{ + tlvs->protocols_supported.count = nlpids->count; + if (tlvs->protocols_supported.protocols) + XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols); + if (nlpids->count) { + tlvs->protocols_supported.protocols = + XCALLOC(MTYPE_ISIS_TLV, nlpids->count); + memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids, + nlpids->count); + } else { + tlvs->protocols_supported.protocols = NULL; + } +} + +void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid, + bool overload, bool attached) +{ + struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i)); + + i->overload = overload; + i->attached = attached; + i->mtid = mtid; + append_item(&tlvs->mt_router_info, (struct isis_item *)i); +} + +void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr) +{ + struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); + a->addr = *addr; + append_item(&tlvs->ipv4_address, (struct isis_item *)a); +} + + +void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs, + struct list *addresses) +{ + struct listnode *node; + struct prefix_ipv4 *ip_addr; + unsigned int addr_count = 0; + + for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) { + isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix); + addr_count++; + if (addr_count >= 63) + break; + } +} + +void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs, + struct list *addresses) +{ + struct listnode *node; + struct prefix_ipv6 *ip_addr; + + for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) { + struct isis_ipv6_address *a = + XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); + + a->addr = ip_addr->prefix; + append_item(&tlvs->ipv6_address, (struct isis_item *)a); + } +} + +typedef bool (*auth_validator_func)(struct isis_passwd *passwd, + struct stream *stream, + struct isis_auth *auth, bool is_lsp); + +static bool auth_validator_cleartxt(struct isis_passwd *passwd, + struct stream *stream, + struct isis_auth *auth, bool is_lsp) +{ + return (auth->length == passwd->len + && !memcmp(auth->value, passwd->passwd, passwd->len)); +} + +static bool auth_validator_hmac_md5(struct isis_passwd *passwd, + struct stream *stream, + struct isis_auth *auth, bool is_lsp) +{ + uint8_t digest[16]; + uint16_t checksum; + uint16_t rem_lifetime; + + if (is_lsp) + safe_auth_md5(stream, &checksum, &rem_lifetime); + + memset(STREAM_DATA(stream) + auth->offset, 0, 16); + hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd, + passwd->len, digest); + memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16); + + bool rv = !memcmp(digest, auth->value, 16); + + if (is_lsp) + restore_auth_md5(stream, checksum, rem_lifetime); + + return rv; +} + +static const auth_validator_func auth_validators[] = { + [ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt, + [ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5, +}; + +bool isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd, + struct stream *stream, bool is_lsp) +{ + /* If no auth is set, always pass authentication */ + if (!passwd->type) + return true; + + /* If we don't known how to validate the auth, return invalid */ + if (passwd->type >= array_size(auth_validators) + || !auth_validators[passwd->type]) + return false; + + struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head; + struct isis_auth *auth; + for (auth = auth_head; auth; auth = auth->next) { + if (auth->type == passwd->type) + break; + } + + /* If matching auth TLV could not be found, return invalid */ + if (!auth) + return false; + + /* Perform validation and return result */ + return auth_validators[passwd->type](passwd, stream, auth, is_lsp); +} + +bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs, + struct list *addresses) +{ + struct isis_area_address *addr_head; + + addr_head = (struct isis_area_address *)tlvs->area_addresses.head; + for (struct isis_area_address *addr = addr_head; addr; + addr = addr->next) { + struct listnode *node; + struct area_addr *a; + + for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) { + if (a->addr_len == addr->len + && !memcmp(a->area_addr, addr->addr, addr->len)) + return true; + } + } + + return false; +} + +static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs, + struct isis_adjacency *adj, + bool *changed) +{ + if (adj->area_address_count != tlvs->area_addresses.count) { + *changed = true; + adj->area_address_count = tlvs->area_addresses.count; + adj->area_addresses = XREALLOC( + MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses, + adj->area_address_count * sizeof(*adj->area_addresses)); + } + + struct isis_area_address *addr = NULL; + for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) { + if (!addr) + addr = (struct isis_area_address *) + tlvs->area_addresses.head; + else + addr = addr->next; + + if (adj->area_addresses[i].addr_len == addr->len + && !memcmp(adj->area_addresses[i].area_addr, addr->addr, + addr->len)) { + continue; + } + + *changed = true; + adj->area_addresses[i].addr_len = addr->len; + memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len); + } +} + +static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs, + struct isis_adjacency *adj, + bool *changed) +{ + bool ipv4_supported = false, ipv6_supported = false; + + for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) { + if (tlvs->protocols_supported.protocols[i] == NLPID_IP) + ipv4_supported = true; + if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6) + ipv6_supported = true; + } + + struct nlpids reduced = {}; + + if (ipv4_supported && ipv6_supported) { + reduced.count = 2; + reduced.nlpids[0] = NLPID_IP; + reduced.nlpids[1] = NLPID_IPV6; + } else if (ipv4_supported) { + reduced.count = 1; + reduced.nlpids[0] = NLPID_IP; + } else if (ipv6_supported) { + reduced.count = 1; + reduced.nlpids[1] = NLPID_IPV6; + } else { + reduced.count = 0; + } + + if (adj->nlpids.count == reduced.count + && !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count)) + return; + + *changed = true; + adj->nlpids.count = reduced.count; + memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count); +} + +static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs, + struct isis_adjacency *adj, + bool *changed) +{ + if (adj->ipv4_address_count != tlvs->ipv4_address.count) { + *changed = true; + adj->ipv4_address_count = tlvs->ipv4_address.count; + adj->ipv4_addresses = XREALLOC( + MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses, + adj->ipv4_address_count * sizeof(*adj->ipv4_addresses)); + } + + struct isis_ipv4_address *addr = NULL; + for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) { + if (!addr) + addr = (struct isis_ipv4_address *) + tlvs->ipv4_address.head; + else + addr = addr->next; + + if (!memcmp(&adj->ipv4_addresses[i], &addr->addr, + sizeof(addr->addr))) + continue; + + *changed = true; + adj->ipv4_addresses[i] = addr->addr; + } +} + +static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs, + struct isis_adjacency *adj, + bool *changed) +{ + if (adj->ipv6_address_count != tlvs->ipv6_address.count) { + *changed = true; + adj->ipv6_address_count = tlvs->ipv6_address.count; + adj->ipv6_addresses = XREALLOC( + MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses, + adj->ipv6_address_count * sizeof(*adj->ipv6_addresses)); + } + + struct isis_ipv6_address *addr = NULL; + for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) { + if (!addr) + addr = (struct isis_ipv6_address *) + tlvs->ipv6_address.head; + else + addr = addr->next; + + if (!memcmp(&adj->ipv6_addresses[i], &addr->addr, + sizeof(addr->addr))) + continue; + + *changed = true; + adj->ipv6_addresses[i] = addr->addr; + } +} + +void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj, + bool *changed) +{ + *changed = false; + + tlvs_area_addresses_to_adj(tlvs, adj, changed); + tlvs_protocols_supported_to_adj(tlvs, adj, changed); + tlvs_ipv4_addresses_to_adj(tlvs, adj, changed); + tlvs_ipv6_addresses_to_adj(tlvs, adj, changed); +} + +bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa) +{ + struct isis_lan_neighbor *ne_head; + + ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head; + for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) { + if (!memcmp(ne->mac, snpa, ETH_ALEN)) + return true; + } + + return false; +} + +void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp) +{ + struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry)); + + entry->rem_lifetime = lsp->hdr.rem_lifetime; + memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2); + entry->checksum = lsp->hdr.checksum; + entry->seqno = lsp->hdr.seqno; + entry->lsp = lsp; + + append_item(&tlvs->lsp_entries, (struct isis_item *)entry); +} + +void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, + uint8_t *stop_id, uint16_t num_lsps, + dict_t *lspdb, struct isis_lsp **last_lsp) +{ + dnode_t *first = dict_lower_bound(lspdb, start_id); + if (!first) + return; + + dnode_t *last = dict_upper_bound(lspdb, stop_id); + dnode_t *curr = first; + + isis_tlvs_add_lsp_entry(tlvs, first->dict_data); + *last_lsp = first->dict_data; + + while (curr) { + curr = dict_next(lspdb, curr); + if (curr) { + isis_tlvs_add_lsp_entry(tlvs, curr->dict_data); + *last_lsp = curr->dict_data; + } + if (curr == last || tlvs->lsp_entries.count == num_lsps) + break; + } +} + +void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, + const char *hostname) +{ + XFREE(MTYPE_ISIS_TLV, tlvs->hostname); + if (hostname) + tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname); +} + +void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, + const struct in_addr *id) +{ + XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id); + if (!id) + return; + tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id)); + memcpy(tlvs->te_router_id, id, sizeof(*id)); +} + +void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs, + struct prefix_ipv4 *dest, uint8_t metric) +{ + struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); + + r->metric = metric; + memcpy(&r->prefix, dest, sizeof(*dest)); + apply_mask_ipv4(&r->prefix); + append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r); +} + +void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, + struct prefix_ipv4 *dest, uint32_t metric) +{ + struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); + + r->metric = metric; + memcpy(&r->prefix, dest, sizeof(*dest)); + apply_mask_ipv4(&r->prefix); + append_item(&tlvs->extended_ip_reach, (struct isis_item *)r); +} + +void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, + struct prefix_ipv6 *dest, uint32_t metric) +{ + struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); + + r->metric = metric; + memcpy(&r->prefix, dest, sizeof(*dest)); + apply_mask_ipv6(&r->prefix); + + struct isis_item_list *l; + l = (mtid == ISIS_MT_IPV4_UNICAST) + ? &tlvs->ipv6_reach + : isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid); + append_item(l, (struct isis_item *)r); +} + +void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id, + uint8_t metric) +{ + struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); + + r->metric = metric; + memcpy(r->id, id, sizeof(r->id)); + append_item(&tlvs->oldstyle_reach, (struct isis_item *)r); +} + +void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid, + uint8_t *id, uint32_t metric, + uint8_t *subtlvs, uint8_t subtlv_len) +{ + struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r)); + + memcpy(r->id, id, sizeof(r->id)); + r->metric = metric; + if (subtlvs && subtlv_len) { + r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len); + memcpy(r->subtlvs, subtlvs, subtlv_len); + r->subtlv_len = subtlv_len; + } + + struct isis_item_list *l; + if (mtid == ISIS_MT_IPV4_UNICAST) + l = &tlvs->extended_reach; + else + l = isis_get_mt_items(&tlvs->mt_reach, mtid); + append_item(l, (struct isis_item *)r); +} + +struct isis_mt_router_info * +isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid) +{ + if (tlvs->mt_router_info_empty) + return NULL; + + struct isis_mt_router_info *rv; + for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv; + rv = rv->next) { + if (rv->mtid == mtid) + return rv; + } + + return NULL; +} diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h new file mode 100644 index 0000000000..6ade0af28c --- /dev/null +++ b/isisd/isis_tlvs.h @@ -0,0 +1,308 @@ +/* + * IS-IS TLV Serializer/Deserializer + * + * Copyright (C) 2015,2017 Christian Franke + * + * This file is part of 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_TLVS_H +#define ISIS_TLVS_H + +#include "openbsd-tree.h" +#include "prefix.h" +#include "isisd/dict.h" + +struct isis_subtlvs; + +struct isis_area_address; +struct isis_area_address { + struct isis_area_address *next; + + uint8_t addr[20]; + uint8_t len; +}; + +struct isis_oldstyle_reach; +struct isis_oldstyle_reach { + struct isis_oldstyle_reach *next; + + uint8_t id[7]; + uint8_t metric; +}; + +struct isis_oldstyle_ip_reach; +struct isis_oldstyle_ip_reach { + struct isis_oldstyle_ip_reach *next; + + uint8_t metric; + struct prefix_ipv4 prefix; +}; + +struct isis_lsp_entry; +struct isis_lsp_entry { + struct isis_lsp_entry *next; + + uint16_t rem_lifetime; + uint8_t id[8]; + uint16_t checksum; + uint32_t seqno; + + struct isis_lsp *lsp; +}; + +struct isis_extended_reach; +struct isis_extended_reach { + struct isis_extended_reach *next; + + uint8_t id[7]; + uint32_t metric; + + uint8_t *subtlvs; + uint8_t subtlv_len; +}; + +struct isis_extended_ip_reach; +struct isis_extended_ip_reach { + struct isis_extended_ip_reach *next; + + uint32_t metric; + bool down; + struct prefix_ipv4 prefix; +}; + +struct isis_ipv6_reach; +struct isis_ipv6_reach { + struct isis_ipv6_reach *next; + + uint32_t metric; + bool down; + bool external; + + struct prefix_ipv6 prefix; + + struct isis_subtlvs *subtlvs; +}; + +struct isis_protocols_supported { + uint8_t count; + uint8_t *protocols; +}; + +struct isis_item; +struct isis_item { + struct isis_item *next; +}; + +struct isis_lan_neighbor; +struct isis_lan_neighbor { + struct isis_lan_neighbor *next; + + uint8_t mac[6]; +}; + +struct isis_ipv4_address; +struct isis_ipv4_address { + struct isis_ipv4_address *next; + + struct in_addr addr; +}; + +struct isis_ipv6_address; +struct isis_ipv6_address { + struct isis_ipv6_address *next; + + struct in6_addr addr; +}; + +struct isis_mt_router_info; +struct isis_mt_router_info { + struct isis_mt_router_info *next; + + bool overload; + bool attached; + uint16_t mtid; +}; + +struct isis_auth; +struct isis_auth { + struct isis_auth *next; + + uint8_t type; + uint8_t length; + uint8_t value[256]; + + uint8_t plength; + uint8_t passwd[256]; + + size_t offset; /* Only valid after packing */ +}; + +struct isis_item_list; +struct isis_item_list { + struct isis_item *head; + struct isis_item **tail; + + RB_ENTRY(isis_item_list) mt_tree; + uint16_t mtid; + unsigned int count; +}; + +RB_HEAD(isis_mt_item_list, isis_item_list); + +struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m, + uint16_t mtid); +struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m, + uint16_t mtid); + +struct isis_tlvs { + struct isis_item_list isis_auth; + struct isis_item_list area_addresses; + struct isis_item_list oldstyle_reach; + struct isis_item_list lan_neighbor; + struct isis_item_list lsp_entries; + struct isis_item_list extended_reach; + struct isis_mt_item_list mt_reach; + struct isis_item_list oldstyle_ip_reach; + struct isis_protocols_supported protocols_supported; + struct isis_item_list oldstyle_ip_reach_ext; + struct isis_item_list ipv4_address; + struct isis_item_list ipv6_address; + struct isis_item_list mt_router_info; + bool mt_router_info_empty; + struct in_addr *te_router_id; + struct isis_item_list extended_ip_reach; + struct isis_mt_item_list mt_ip_reach; + char *hostname; + struct isis_item_list ipv6_reach; + struct isis_mt_item_list mt_ipv6_reach; +}; + +struct isis_subtlvs { + /* draft-baker-ipv6-isis-dst-src-routing-06 */ + struct prefix_ipv6 *source_prefix; +}; + +enum isis_tlv_context { + ISIS_CONTEXT_LSP, + ISIS_CONTEXT_SUBTLV_NE_REACH, + ISIS_CONTEXT_SUBTLV_IP_REACH, + ISIS_CONTEXT_SUBTLV_IPV6_REACH, + ISIS_CONTEXT_MAX +}; + +enum isis_tlv_type { + ISIS_TLV_AREA_ADDRESSES = 1, + ISIS_TLV_OLDSTYLE_REACH = 2, + ISIS_TLV_LAN_NEIGHBORS = 6, + ISIS_TLV_PADDING = 8, + ISIS_TLV_LSP_ENTRY = 9, + ISIS_TLV_AUTH = 10, + ISIS_TLV_EXTENDED_REACH = 22, + + ISIS_TLV_OLDSTYLE_IP_REACH = 128, + ISIS_TLV_PROTOCOLS_SUPPORTED = 129, + ISIS_TLV_OLDSTYLE_IP_REACH_EXT = 130, + ISIS_TLV_IPV4_ADDRESS = 132, + ISIS_TLV_TE_ROUTER_ID = 134, + ISIS_TLV_EXTENDED_IP_REACH = 135, + ISIS_TLV_DYNAMIC_HOSTNAME = 137, + ISIS_TLV_MT_REACH = 222, + ISIS_TLV_MT_ROUTER_INFO = 229, + ISIS_TLV_IPV6_ADDRESS = 232, + ISIS_TLV_MT_IP_REACH = 235, + ISIS_TLV_IPV6_REACH = 236, + ISIS_TLV_MT_IPV6_REACH = 237, + ISIS_TLV_MAX = 256, + + ISIS_SUBTLV_IPV6_SOURCE_PREFIX = 22 +}; + +#define IS_COMPAT_MT_TLV(tlv_type) \ + ((tlv_type == ISIS_TLV_MT_REACH) || (tlv_type == ISIS_TLV_MT_IP_REACH) \ + || (tlv_type == ISIS_TLV_MT_IPV6_REACH)) + +struct stream; +int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream, + size_t len_pointer, bool pad, bool is_lsp); +void isis_free_tlvs(struct isis_tlvs *tlvs); +struct isis_tlvs *isis_alloc_tlvs(void); +int isis_unpack_tlvs(size_t avail_len, struct stream *stream, + struct isis_tlvs **dest, const char **error_log); +const char *isis_format_tlvs(struct isis_tlvs *tlvs); +struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs); +struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); + +#define ISIS_EXTENDED_IP_REACH_DOWN 0x80 +#define ISIS_EXTENDED_IP_REACH_SUBTLV 0x40 + +#define ISIS_IPV6_REACH_DOWN 0x80 +#define ISIS_IPV6_REACH_EXTERNAL 0x40 +#define ISIS_IPV6_REACH_SUBTLV 0x20 + +#ifndef ISIS_MT_MASK +#define ISIS_MT_MASK 0x0fff +#define ISIS_MT_OL_MASK 0x8000 +#define ISIS_MT_AT_MASK 0x4000 +#endif + + +void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd); +void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, + struct list *addresses); +void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, + struct list *neighbors); +void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs, + struct nlpids *nlpids); +void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid, + bool overload, bool attached); +void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr); +void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs, + struct list *addresses); +void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs, + struct list *addresses); +bool isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd, + struct stream *stream, bool is_lsp); +bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs, + struct list *addresses); +struct isis_adjacency; +void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj, + bool *changed); +bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa); +void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp); +void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id, + uint8_t *stop_id, uint16_t num_lsps, + dict_t *lspdb, struct isis_lsp **last_lsp); +void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs, + const char *hostname); +void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, + const struct in_addr *id); +void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs, + struct prefix_ipv4 *dest, uint8_t metric); +void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, + struct prefix_ipv4 *dest, uint32_t metric); +void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, + struct prefix_ipv6 *dest, uint32_t metric); +void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id, + uint8_t metric); +void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid, + uint8_t *id, uint32_t metric, + uint8_t *subtlvs, uint8_t subtlv_len); + +struct isis_mt_router_info * +isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid); +#endif diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 18a59d1fc5..8c6968f8ec 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -42,7 +42,6 @@ #include "isisd/isis_flags.h" #include "isisd/isis_misc.h" #include "isisd/isis_circuit.h" -#include "isisd/isis_tlv.h" #include "isisd/isisd.h" #include "isisd/isis_circuit.h" #include "isisd/isis_csm.h" diff --git a/isisd/isisd.c b/isisd/isisd.c index 8bbb5cf949..05797fb73a 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -49,7 +49,6 @@ #include "isisd/isis_pdu.h" #include "isisd/isis_misc.h" #include "isisd/isis_constants.h" -#include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" #include "isisd/isis_spf.h" #include "isisd/isis_route.h" diff --git a/isisd/iso_checksum.c b/isisd/iso_checksum.c index e0a4ba3700..25a870991a 100644 --- a/isisd/iso_checksum.c +++ b/isisd/iso_checksum.c @@ -67,7 +67,7 @@ int iso_csum_verify(u_char *buffer, int len, uint16_t csum, int offset) return 1; checksum = fletcher_checksum(buffer, len, offset); - if (checksum == csum) + if (checksum == htons(csum)) return 0; return 1; } diff --git a/lib/sbuf.c b/lib/sbuf.c new file mode 100644 index 0000000000..37c1e5283d --- /dev/null +++ b/lib/sbuf.c @@ -0,0 +1,107 @@ +/* + * Simple string buffer + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "sbuf.h" +#include "memory.h" + +void sbuf_init(struct sbuf *dest, char *buf, size_t size) +{ + dest->fixed = (size > 0); + if (dest->fixed) { + dest->buf = buf; + dest->size = size; + } else { + dest->buf = XMALLOC(MTYPE_TMP, 4096); + dest->size = 4096; + } + + dest->pos = 0; + dest->buf[0] = '\0'; +} + +void sbuf_reset(struct sbuf *dest) +{ + dest->pos = 0; + dest->buf[0] = '\0'; +} + +const char *sbuf_buf(struct sbuf *buf) +{ + return buf->buf; +} + +void sbuf_free(struct sbuf *buf) +{ + if (!buf->fixed) + XFREE(MTYPE_TMP, buf->buf); +} + +void sbuf_push(struct sbuf *buf, int indent, const char *format, ...) +{ + va_list args; + int written; + + if (!buf->fixed) { + char dummy; + int written1, written2; + size_t new_size; + + written1 = snprintf(&dummy, 0, "%*s", indent, ""); + va_start(args, format); + written2 = vsnprintf(&dummy, 0, format, args); + va_end(args); + + new_size = buf->size; + if (written1 >= 0 && written2 >= 0) { + while (buf->pos + written1 + written2 >= new_size) + new_size *= 2; + if (new_size > buf->size) { + buf->buf = + XREALLOC(MTYPE_TMP, buf->buf, new_size); + buf->size = new_size; + } + } + } + + written = snprintf(buf->buf + buf->pos, buf->size - buf->pos, "%*s", + indent, ""); + + if (written >= 0) + buf->pos += written; + if (buf->pos > buf->size) + buf->pos = buf->size; + + va_start(args, format); + written = vsnprintf(buf->buf + buf->pos, buf->size - buf->pos, format, + args); + va_end(args); + + if (written >= 0) + buf->pos += written; + if (buf->pos > buf->size) + buf->pos = buf->size; + + if (buf->pos == buf->size) + assert(!"Buffer filled up!"); +} diff --git a/lib/sbuf.h b/lib/sbuf.h new file mode 100644 index 0000000000..3e49ada6c2 --- /dev/null +++ b/lib/sbuf.h @@ -0,0 +1,77 @@ +/* + * Simple string buffer + * + * Copyright (C) 2017 Christian Franke + * + * This file is part of 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 SBUF_H +#define SBUF_H + +/* + * sbuf provides a simple string buffer. One application where this comes + * in handy is the parsing of binary data: If there is an error in the parsing + * process due to invalid input data, printing an error message explaining what + * went wrong is definitely useful. However, just printing the actual error, + * without any information about the previous parsing steps, is usually not very + * helpful. + * Using sbuf, the parser can log the whole parsing process into a buffer using + * a printf like API. When an error ocurrs, all the information about previous + * parsing steps is there in the log, without any need for backtracking, and can + * be used to give a detailed and useful error description. + * When parsing completes successfully without any error, the log can just be + * discarded unless debugging is turned on, to not spam the log. + * + * For the described usecase, the code would look something like this: + * + * int sbuf_example(..., char **parser_log) + * { + * struct sbuf logbuf; + * + * sbuf_init(&logbuf, NULL, 0); + * sbuf_push(&logbuf, 0, "Starting parser\n"); + * + * int rv = do_parse(&logbuf, ...); + * + * *parser_log = sbuf_buf(&logbuf); + * + * return 1; + * } + * + * In this case, sbuf_example uses a string buffer with undefined size, which will + * be allocated on the heap by sbuf. The caller of sbuf_example is expected to free + * the string returned in parser_log. + */ + +struct sbuf { + bool fixed; + char *buf; + size_t size; + size_t pos; + int indent; +}; + +void sbuf_init(struct sbuf *dest, char *buf, size_t size); +void sbuf_reset(struct sbuf *buf); +const char *sbuf_buf(struct sbuf *buf); +void sbuf_free(struct sbuf *buf); +#include "lib/log.h" +void sbuf_push(struct sbuf *buf, int indent, const char *format, ...) + PRINTF_ATTRIBUTE(3, 4); + +#endif diff --git a/lib/subdir.am b/lib/subdir.am index 5a1971cba7..28a4ce5579 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -51,6 +51,7 @@ lib_libfrr_la_SOURCES = \ lib/ptm_lib.c \ lib/qobj.c \ lib/routemap.c \ + lib/sbuf.c \ lib/sha256.c \ lib/sigevent.c \ lib/skiplist.c \ @@ -125,6 +126,7 @@ pkginclude_HEADERS += \ lib/qobj.h \ lib/route_types.h \ lib/routemap.h \ + lib/sbuf.h \ lib/sha256.h \ lib/sigevent.h \ lib/skiplist.h \ diff --git a/tests/.gitignore b/tests/.gitignore index 6d54ae155b..113bdea098 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -25,6 +25,8 @@ __pycache__ /bgpd/test_ecommunity /bgpd/test_mp_attr /bgpd/test_mpath +/isisd/test_fuzz_isis_tlv +/isisd/test_fuzz_isis_tlv_tests.h /lib/cli/test_cli /lib/cli/test_commands /lib/cli/test_commands_defun.c diff --git a/tests/Makefile.am b/tests/Makefile.am index 43003e7075..0c31c0441a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,6 +24,13 @@ else TESTS_BGPD = endif +if ISISD +TESTS_ISISD = \ + isisd/test_fuzz_isis_tlv +else +TESTS_ISISD = +endif + if OSPF6D TESTS_OSPF6D = \ ospf6d/test_lsdb \ @@ -61,6 +68,7 @@ check_PROGRAMS = \ lib/cli/test_cli \ lib/cli/test_commands \ $(TESTS_BGPD) \ + $(TESTS_ISISD) \ $(TESTS_OSPF6D) \ # end @@ -75,7 +83,12 @@ lib/cli/test_commands_defun.c: ../vtysh/vtysh_cmd.c < ../vtysh/vtysh_cmd.c \ > "$@" -BUILT_SOURCES = lib/cli/test_commands_defun.c +isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz + gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@" + +BUILT_SOURCES = \ + lib/cli/test_commands_defun.c \ + isisd/test_fuzz_isis_tlv_tests.h noinst_HEADERS = \ ./helpers/c/prng.h \ @@ -110,11 +123,14 @@ bgpd_test_capability_SOURCES = bgpd/test_capability.c bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c bgpd_test_mpath_SOURCES = bgpd/test_mpath.c +isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv.c +isisd_test_fuzz_isis_tlv_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/tests/isisd ospf6d_test_lsdb_SOURCES = ospf6d/test_lsdb.c lib/cli/common_cli.c ALL_TESTS_LDADD = ../lib/libfrr.la @LIBCAP@ BGP_TEST_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) $(ALL_TESTS_LDADD) -lm +ISISD_TEST_LDADD = ../isisd/libisis.a $(ALL_TESTS_LDADD) OSPF6_TEST_LDADD = ../ospf6d/libospf6.a $(ALL_TESTS_LDADD) lib_test_buffer_LDADD = $(ALL_TESTS_LDADD) @@ -140,6 +156,7 @@ bgpd_test_capability_LDADD = $(BGP_TEST_LDADD) bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD) bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD) bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD) +isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD) ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD) EXTRA_DIST = \ @@ -151,6 +168,8 @@ EXTRA_DIST = \ bgpd/test_mpath.py \ helpers/python/frrsix.py \ helpers/python/frrtest.py \ + isisd/test_fuzz_isis_tlv.py \ + isisd/test_fuzz_isis_tlv_tests.h.gz \ lib/cli/test_commands.in \ lib/cli/test_commands.py \ lib/cli/test_commands.refout \ diff --git a/tests/isisd/.gitignore b/tests/isisd/.gitignore new file mode 100644 index 0000000000..e124221296 --- /dev/null +++ b/tests/isisd/.gitignore @@ -0,0 +1 @@ +/*_afl/* diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c new file mode 100644 index 0000000000..6727e663f5 --- /dev/null +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -0,0 +1,188 @@ +#include "test_fuzz_isis_tlv_tests.h" + +#include + +#include "memory.h" +#include "sbuf.h" +#include "stream.h" +#include "thread.h" + +#include "isisd/isis_circuit.h" +#include "isisd/isis_tlvs.h" + +#define TEST_STREAM_SIZE 1500 + +struct thread_master *master; +int isis_sock_init(struct isis_circuit *circuit); +int isis_sock_init(struct isis_circuit *circuit) +{ + return 0; +} + +static bool atexit_registered; + +static void show_meminfo_at_exit(void) +{ + log_memstats_stderr("isis fuzztest"); +} + +static int comp_line(const void *p1, const void *p2) +{ + return strcmp(*(char * const *)p1, *(char * const *)p2); +} + +static char *sortlines(char *in) +{ + size_t line_count = 1; + size_t rv_len = strlen(in) + 1; + size_t rv_pos = 0; + char *rv = XMALLOC(MTYPE_TMP, rv_len); + + for (char *c = in; *c; c++) { + if (*c == '\n') + line_count++; + } + + if (line_count == 1) { + strncpy(rv, in, rv_len); + return rv; + } + + char **lines = XCALLOC(MTYPE_TMP, sizeof(char *)*line_count); + char *saveptr = NULL; + size_t i = 0; + + for (char *line = strtok_r(in, "\n", &saveptr); line; + line = strtok_r(NULL, "\n", &saveptr)) { + lines[i++] = line; + assert(i <= line_count); + } + + line_count = i; + + qsort(lines, line_count, sizeof(char *), comp_line); + + for (i = 0; i < line_count; i++) { + int printf_rv = snprintf(rv + rv_pos, rv_len - rv_pos, "%s\n", lines[i]); + assert(printf_rv >= 0); + rv_pos += printf_rv; + } + + XFREE(MTYPE_TMP, lines); + return rv; +} + +static int test(FILE *input, FILE *output) +{ + struct stream *s = stream_new(TEST_STREAM_SIZE); + char buf[TEST_STREAM_SIZE]; + size_t bytes_read = 0; + + if (!atexit_registered) { + atexit(show_meminfo_at_exit); + atexit_registered = true; + } + + while (STREAM_WRITEABLE(s) && !feof(input)) { + bytes_read = fread(buf, 1, STREAM_WRITEABLE(s), input); + if (bytes_read == 0) + break; + stream_put(s, buf, bytes_read); + } + + if (bytes_read && !feof(input)) { + fprintf(output, "Too much input data.\n"); + stream_free(s); + return 1; + } + + stream_set_getp(s, 0); + struct isis_tlvs *tlvs; + const char *log; + int rv = isis_unpack_tlvs(STREAM_READABLE(s), s, &tlvs, &log); + + if (rv) { + fprintf(output, "Could not unpack TLVs:\n%s\n", log); + isis_free_tlvs(tlvs); + stream_free(s); + return 2; + } + + fprintf(output, "Unpack log:\n%s", log); + const char *s_tlvs = isis_format_tlvs(tlvs); + fprintf(output, "Unpacked TLVs:\n%s", s_tlvs); + + struct isis_tlvs *tlv_copy = isis_copy_tlvs(tlvs); + isis_free_tlvs(tlvs); + + struct stream *s2 = stream_new(TEST_STREAM_SIZE); + + if (isis_pack_tlvs(tlv_copy, s2, (size_t)-1, false, false)) { + fprintf(output, "Could not pack TLVs.\n"); + assert(0); + } + + stream_set_getp(s2, 0); + rv = isis_unpack_tlvs(STREAM_READABLE(s2), s2, &tlvs, &log); + if (rv) { + fprintf(output, "Could not unpack own TLVs:\n%s\n", log); + assert(0); + } + + char *orig_tlvs = XSTRDUP(MTYPE_TMP, s_tlvs); + s_tlvs = isis_format_tlvs(tlvs); + + if (strcmp(orig_tlvs, s_tlvs)) { + fprintf(output, + "Deserialized and Serialized LSP seem to differ.\n"); + fprintf(output, "Re-Unpacked TLVs:\n%s", s_tlvs); + assert(0); + } + + isis_free_tlvs(tlv_copy); + stream_free(s); + stream_free(s2); + + struct list *fragments = isis_fragment_tlvs(tlvs, 550); + isis_free_tlvs(tlvs); + if (!fragments) { + XFREE(MTYPE_TMP, orig_tlvs); + return 0; + } + + s = stream_new(550); + + struct sbuf fragment_format; + sbuf_init(&fragment_format, NULL, 0); + + struct listnode *node; + for (ALL_LIST_ELEMENTS_RO(fragments, node, tlvs)) { + stream_reset(s); + int rv = isis_pack_tlvs(tlvs, s, (size_t)-1, false, false); + if (rv) { + fprintf(output, "Could not pack fragment, too large.\n"); + assert(0); + } + sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs)); + isis_free_tlvs(tlvs); + } + list_delete(fragments); + stream_free(s); + + char *fragment_content = sortlines((char *)sbuf_buf(&fragment_format)); + sbuf_free(&fragment_format); + char *orig_tlv_content = sortlines(orig_tlvs); + XFREE(MTYPE_TMP, orig_tlvs); + + if (strcmp(fragment_content, orig_tlv_content)) { + fprintf(output, "Fragmented and unfragmented LSP seem to differ.\n"); + fprintf(output, "Original:\n%s\nFragmented:\n%s\n", + orig_tlv_content, fragment_content); + assert(0); + } + + XFREE(MTYPE_TMP, fragment_content); + XFREE(MTYPE_TMP, orig_tlv_content); + + return 0; +} diff --git a/tests/isisd/test_fuzz_isis_tlv.py b/tests/isisd/test_fuzz_isis_tlv.py new file mode 100644 index 0000000000..60938472b5 --- /dev/null +++ b/tests/isisd/test_fuzz_isis_tlv.py @@ -0,0 +1,6 @@ +import frrtest + +class TestFuzzIsisTLV(frrtest.TestMultiOut): + program = './test_fuzz_isis_tlv' + +TestFuzzIsisTLV.exit_cleanly() diff --git a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz new file mode 100644 index 0000000000..3eb0205a5d Binary files /dev/null and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ