--- embedaddon/quagga/isisd/isis_pdu.c 2012/02/21 17:26:11 1.1 +++ embedaddon/quagga/isisd/isis_pdu.c 2016/11/02 10:09:10 1.1.1.4 @@ -33,17 +33,18 @@ #include "prefix.h" #include "if.h" #include "checksum.h" +#include "md5.h" #include "isisd/dict.h" #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_circuit.h" #include "isisd/isis_network.h" #include "isisd/isis_misc.h" #include "isisd/isis_dr.h" -#include "isisd/isis_flags.h" #include "isisd/isis_tlv.h" #include "isisd/isisd.h" #include "isisd/isis_dynhn.h" @@ -53,9 +54,6 @@ #include "isisd/isis_csm.h" #include "isisd/isis_events.h" -extern struct thread_master *master; -extern struct isis *isis; - #define ISIS_MINIMUM_FIXED_HDR_LEN 15 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */ @@ -168,32 +166,152 @@ accept_level (int level, int circuit_t) return retval; } -int -authentication_check (struct isis_passwd *one, struct isis_passwd *theother) +/* + * 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) { - if (one->type != theother->type) + 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_warn ("Unsupported authentication type %d", theother->type); - return 1; /* Auth fail (different authentication types) */ + 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; } - switch (one->type) + + if (!(found & TLVFLAG_AUTH_INFO)) { - case ISIS_PASSWD_TYPE_CLEARTXT: - if (one->len != theother->len) - return 1; /* Auth fail () - passwd len mismatch */ - return memcmp (one->passwd, theother->passwd, one->len); - break; - default: - zlog_warn ("Unsupported authentication type"); - break; + zlog_err ("No authentication tlv in LSP"); + return ISIS_ERROR; } - return 0; /* Auth pass */ + + 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; @@ -203,6 +321,8 @@ tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adj { tlv_nlpids = tlvs->nlpids; + if (tlv_nlpids->count > array_size (adj->nlpids.nlpids)) + return 1; adj->nlpids.count = tlv_nlpids->count; @@ -211,15 +331,10 @@ tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adj adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i]; } } + return 0; } static void -del_ip_addr (void *val) -{ - XFREE (MTYPE_ISIS_TMP, val); -} - -static void tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) { struct listnode *node; @@ -227,7 +342,7 @@ tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis if (adj->ipv4_addrs) { - adj->ipv4_addrs->del = del_ip_addr; + adj->ipv4_addrs->del = del_addr; list_delete (adj->ipv4_addrs); } adj->ipv4_addrs = list_new (); @@ -251,7 +366,7 @@ tlvs_to_adj_ipv6_addrs (struct tlvs *tlvs, struct isis if (adj->ipv6_addrs) { - adj->ipv6_addrs->del = del_ip_addr; + adj->ipv6_addrs->del = del_addr; list_delete (adj->ipv6_addrs); } adj->ipv6_addrs = list_new (); @@ -284,9 +399,27 @@ 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; + u_int32_t expected = 0, found = 0, auth_tlv_offset = 0; + 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)); + } + + if (circuit->circ_type != CIRCUIT_T_P2P) + { + zlog_warn ("p2p hello on non p2p circuit"); + return ISIS_WARNING; + } + if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_P2PHELLO_HDRLEN) { @@ -311,43 +444,27 @@ process_p2p_hello (struct isis_circuit *circuit) * Get the header */ hdr = (struct isis_p2p_hello_hdr *) STREAM_PNT (circuit->rcv_stream); - circuit->rcv_stream->getp += ISIS_P2PHELLO_HDRLEN; + pdu_len = ntohs (hdr->pdu_len); - /* hdr.circuit_t = stream_getc (stream); - stream_get (hdr.source_id, stream, ISIS_SYS_ID_LEN); - hdr.hold_time = stream_getw (stream); - hdr.pdu_len = stream_getw (stream); - hdr.local_id = stream_getc (stream); */ + 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; + } /* - * My interpertation of the ISO, if no adj exists we will create one for - * the circuit + * 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_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->circuit_is_type), - circuit->circuit_id, ntohs (hdr->pdu_len)); - } + stream_forward_getp (circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN); - adj = circuit->u.p2p.neighbor; - if (!adj) - { - adj = isis_new_adj (hdr->source_id, NULL, 0, circuit); - if (adj == NULL) - return ISIS_ERROR; - circuit->u.p2p.neighbor = adj; - 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->last_upd = time (NULL); - /* * Lets get the TLVS now */ @@ -357,37 +474,165 @@ process_p2p_hello (struct isis_circuit *circuit) expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, STREAM_PNT (circuit->rcv_stream), - ntohs (hdr->pdu_len) - ISIS_P2PHELLO_HDRLEN - - ISIS_FIXED_HDR_LEN, &expected, &found, &tlvs); + 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 (&circuit->passwd, &tlvs.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 it's own interface ip match iih ip addrs + */ + if (found & TLVFLAG_IPV4_ADDR) + { + if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + v4_usable = 1; + else + zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap " + "in P2P IIH from %s\n", circuit->interface->name); + } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in P2P IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); + +#else + 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); +#endif + + 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; + } + + /* + * My interpertation of the ISO, if no adj exists we will create one for + * the circuit + */ + adj = 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)) { - isis_event_auth_failure (circuit->area->area_tag, - "P2P hello authentication failure", - hdr->source_id); - return ISIS_OK; - } + 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; + } } + if (!adj || adj->level != hdr->circuit_t) + { + if (!adj) + { + adj = isis_new_adj (hdr->source_id, NULL, hdr->circuit_t, circuit); + if (adj == NULL) + return ISIS_ERROR; + } + else + { + adj->level = hdr->circuit_t; + } + 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; + 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->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 */ - tlvs_to_adj_ipv4_addrs (&tlvs, adj); + if (found & TLVFLAG_IPV4_ADDR) + tlvs_to_adj_ipv4_addrs (&tlvs, adj); #ifdef HAVE_IPV6 - tlvs_to_adj_ipv6_addrs (&tlvs, adj); + if (found & TLVFLAG_IPV6_ADDR) + tlvs_to_adj_ipv6_addrs (&tlvs, adj); #endif /* HAVE_IPV6 */ /* lets take care of the expiry */ @@ -422,6 +667,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* (7) reject - wrong system type event */ zlog_warn ("wrongSystemType"); + free_tlvs (&tlvs); return ISIS_WARNING; /* Reject */ } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) @@ -508,6 +754,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* (5) reject - wrong system type event */ zlog_warn ("wrongSystemType"); + free_tlvs (&tlvs); return ISIS_WARNING; /* Reject */ } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || @@ -540,7 +787,7 @@ process_p2p_hello (struct isis_circuit *circuit) } } /* 8.2.5.2 b) if no match was detected */ - else + else if (listcount (circuit->area->area_addrs) > 0) { if (circuit->area->is_type == IS_LEVEL_1) { @@ -566,6 +813,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* (6) reject - Area Mismatch event */ zlog_warn ("AreaMismatch"); + free_tlvs (&tlvs); return ISIS_WARNING; /* Reject */ } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) @@ -618,6 +866,11 @@ process_p2p_hello (struct isis_circuit *circuit) } } } + else + { + /* down - area mismatch */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); + } /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ /* FIXME - Missing parts */ @@ -640,9 +893,16 @@ process_p2p_hello (struct isis_circuit *circuit) break; } - adj->circuit_t = hdr->circuit_t; - adj->level = hdr->circuit_t; + 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); + } + free_tlvs (&tlvs); return retval; @@ -652,16 +912,34 @@ process_p2p_hello (struct isis_circuit *circuit) * Process IS-IS LAN Level 1/2 Hello PDU */ static int -process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) { int retval = ISIS_OK; struct isis_lan_hello_hdr hdr; struct isis_adjacency *adj; - u_int32_t expected = 0, found; + 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) { @@ -676,7 +954,7 @@ process_lan_hello (int level, struct isis_circuit *cir return ISIS_WARNING; } - if (!accept_level (level, circuit->circuit_is_type)) + if (!accept_level (level, circuit->is_type)) { if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -708,13 +986,34 @@ process_lan_hello (int level, struct isis_circuit *cir hdr.prio = stream_getc (circuit->rcv_stream); stream_get (hdr.lan_id, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1); - if (hdr.circuit_t != IS_LEVEL_1 && hdr.circuit_t != IS_LEVEL_2 && - hdr.circuit_t != IS_LEVEL_1_AND_2) + 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 ("Level %d LAN Hello with Circuit Type %d", level, - hdr.circuit_t); + 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 */ @@ -725,10 +1024,12 @@ process_lan_hello (int level, struct isis_circuit *cir expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + 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); + 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) { @@ -744,24 +1045,43 @@ process_lan_hello (int level, struct isis_circuit *cir 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 (&circuit->passwd, &tlvs.auth_info)) - { - isis_event_auth_failure (circuit->area->area_tag, - "LAN hello authentication failure", - hdr.source_id); - retval = ISIS_WARNING; - goto out; - } + 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 (level == 1 && !area_match (circuit->area->area_addrs, tlvs.area_addrs)) + 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) { @@ -788,43 +1108,83 @@ process_lan_hello (int level, struct isis_circuit *cir /* * check if it's own interface ip match iih ip addrs */ - if (!(found & TLVFLAG_IPV4_ADDR) - || !ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + if (found & TLVFLAG_IPV4_ADDR) { - zlog_debug - ("ISIS-Adj: No usable IP interface addresses in LAN IIH from %s\n", - circuit->interface->name); - retval = ISIS_WARNING; - goto out; + if (ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + v4_usable = 1; + else + zlog_warn ("ISIS-Adj: IPv4 addresses present but no overlap " + "in LAN IIH from %s\n", circuit->interface->name); } +#ifndef HAVE_IPV6 + else /* !(found & TLVFLAG_IPV4_ADDR) */ + zlog_warn ("ISIS-Adj: no IPv4 in LAN IIH from %s " + "(this isisd has no IPv6)\n", circuit->interface->name); - adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]); - if (!adj) +#else + if (found & TLVFLAG_IPV6_ADDR) { - /* - * 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; - } + /* 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; + } - adj->level = level; + 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); +#endif + + 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)) + { + 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; + } + } + else + { + if (ssnpa) { + memcpy (adj->snpa, ssnpa, 6); + } else { + memset (adj->snpa, ' ', 6); + } + adj->level = level; + } isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - if (level == 1) - { - adj->sys_type = ISIS_SYSTYPE_L1_IS; - } + if (level == IS_LEVEL_1) + adj->sys_type = ISIS_SYSTYPE_L1_IS; else - { - adj->sys_type = ISIS_SYSTYPE_L2_IS; - } + 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]); + circuit->u.bc.lan_neighs[level - 1]); } if(adj->dis_record[level-1].dis==ISIS_IS_DIS) @@ -833,7 +1193,7 @@ process_lan_hello (int level, struct isis_circuit *cir 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); + thread_add_event (master, isis_event_dis_status_change, circuit, 0); memcpy (&circuit->u.bc.l1_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1); } @@ -841,7 +1201,7 @@ process_lan_hello (int level, struct isis_circuit *cir 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); + thread_add_event (master, isis_event_dis_status_change, circuit, 0); memcpy (&circuit->u.bc.l2_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1); } @@ -854,9 +1214,14 @@ process_lan_hello (int level, struct isis_circuit *cir memcpy (adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1); + tlvs_to_adj_area_addrs (&tlvs, adj); + /* which protocol are spoken ??? */ - if (found & TLVFLAG_NLPID) - tlvs_to_adj_nlpids (&tlvs, adj); + 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) @@ -872,7 +1237,7 @@ process_lan_hello (int level, struct isis_circuit *cir /* lets take care of the expiry */ THREAD_TIMER_OFF (adj->t_expire); THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj, - (long) adj->hold_time); + (long) adj->hold_time); /* * If the snpa for this circuit is found from LAN Neighbours TLV @@ -880,31 +1245,48 @@ process_lan_hello (int level, struct isis_circuit *cir */ if (found & TLVFLAG_LAN_NEIGHS) + { + if (adj->adj_state != ISIS_ADJ_UP) { - 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"); - } - } + 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"); + } + } + else if (adj->adj_state == ISIS_ADJ_UP) + { + isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, + "no LAN Neighbours TLV found"); + } out: - /* DEBUG_ADJ_PACKETS */ if (isis->debugs & DEBUG_ADJ_PACKETS) { - /* FIXME: is this place right? fix missing info */ zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, " - "cirID %u, length %ld", + "cirID %u, length %zd", circuit->area->area_tag, level, snpa_print (ssnpa), circuit->interface->name, - circuit_t2string (circuit->circuit_is_type), + circuit_t2string (circuit->is_type), circuit->circuit_id, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) stream_get_endp (circuit->rcv_stream)); + stream_get_endp (circuit->rcv_stream)); } free_tlvs (&tlvs); @@ -918,7 +1300,7 @@ out: * Section 7.3.15.1 - Action on receipt of a link state PDU */ static int -process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_lsp (int level, struct isis_circuit *circuit, const u_char *ssnpa) { struct isis_link_state_hdr *hdr; struct isis_adjacency *adj = NULL; @@ -926,8 +1308,19 @@ process_lsp (int level, struct isis_circuit *circuit, 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; - /* Sanity check - FIXME: move to correct place */ + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP 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_LSP_HDR_LEN) { @@ -937,28 +1330,56 @@ process_lsp (int level, struct isis_circuit *circuit, /* 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 %lu, on %s", + "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), - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) stream_get_endp (circuit->rcv_stream), + pdu_len, circuit->interface->name); } - assert (ntohs (hdr->pdu_len) > ISIS_LSP_HDR_LEN); + /* 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); + /* 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, - ntohs (hdr->pdu_len) - 12, &hdr->checksum)) + pdu_len - 12, &hdr->checksum)) { zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x", circuit->area->area_tag, @@ -979,13 +1400,13 @@ process_lsp (int level, struct isis_circuit *circuit, } /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ - if (!accept_level (level, circuit->circuit_is_type)) + if (!accept_level (level, circuit->is_type)) { 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), - level, circuit_t2string (circuit->circuit_is_type)); + level, circuit_t2string (circuit->is_type)); return ISIS_WARNING; } @@ -995,12 +1416,12 @@ process_lsp (int level, struct isis_circuit *circuit, /* 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 == ISIS_LEVEL1) ? (passwd = &circuit->area->area_passwd) : - (passwd = &circuit->area->domain_passwd); + (level == IS_LEVEL_1) ? (passwd = &circuit->area->area_passwd) : + (passwd = &circuit->area->domain_passwd); if (passwd->type) { - if (isis_lsp_authinfo_check (circuit->rcv_stream, circuit->area, - ntohs (hdr->pdu_len), passwd)) + 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); @@ -1021,7 +1442,6 @@ process_lsp (int level, struct isis_circuit *circuit, /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same level */ /* for broadcast circuits, snpa should be compared */ - /* FIXME : Point To Point */ if (circuit->circ_type == CIRCUIT_T_BROADCAST) { @@ -1038,7 +1458,6 @@ process_lsp (int level, struct isis_circuit *circuit, return ISIS_WARNING; /* Silently discard */ } } - /* for non broadcast, we just need to find same level adj */ else { @@ -1049,13 +1468,15 @@ process_lsp (int level, struct isis_circuit *circuit, } else { - if (((level == 1) && + if (((level == IS_LEVEL_1) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL2)) || - ((level == 2) && + ((level == IS_LEVEL_2) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1))) return ISIS_WARNING; /* Silently discard */ + adj = circuit->u.p2p.neighbor; } } + dontcheckadj: /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */ @@ -1063,6 +1484,21 @@ dontcheckadj: /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do it */ + /* 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; + } + else + lsp_confusion = 0; + /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */ if (hdr->rem_lifetime == 0) { @@ -1082,18 +1518,23 @@ dontcheckadj: /* 7.3.16.4 b) 1) */ if (comp == LSP_NEWER) { - lsp_update (lsp, hdr, circuit->rcv_stream, circuit->area, - level); + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); /* ii */ - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - /* iii */ - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + lsp_set_all_srmflags (lsp); /* v */ ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); /* FIXME: OTHER than c */ - /* iv */ - if (circuit->circ_type != CIRCUIT_T_BROADCAST) - ISIS_SET_FLAG (lsp->SSNflags, circuit); + /* For the case of lsp confusion, flood the purge back to its + * originator so that it can react. Otherwise, don't reflood + * through incoming circuit as usual */ + if (!lsp_confusion) + { + /* iii */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* iv */ + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + ISIS_SET_FLAG (lsp->SSNflags, circuit); + } } /* 7.3.16.4 b) 2) */ else if (comp == LSP_EQUAL) { @@ -1109,38 +1550,25 @@ dontcheckadj: ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } - else - { - /* our own LSP -> 7.3.16.4 c) */ - if (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id) != - circuit->circuit_id - || (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id) == - circuit->circuit_id - && circuit->u.bc.is_dr[level - 1] == 1)) - { - lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1); - if (isis->debugs & DEBUG_UPDATE_PACKETS) - zlog_debug ("LSP LEN: %d", - ntohs (lsp->lsp_header->pdu_len)); - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - if (isis->debugs & DEBUG_UPDATE_PACKETS) - zlog_debug ("ISIS-Upd (%s): (1) re-originating LSP %s new " - "seq 0x%08x", circuit->area->area_tag, - rawlspid_print (hdr->lsp_id), - ntohl (lsp->lsp_header->seq_num)); - lsp->lsp_header->rem_lifetime = - htons (isis_jitter - (circuit->area->max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); - } - else - { - /* Got purge for own pseudo-lsp, and we are not DR */ - lsp_purge_dr (lsp->lsp_header->lsp_id, circuit, level); - } - } + else if (lsp->lsp_header->rem_lifetime != 0) + { + /* our own LSP -> 7.3.16.4 c) */ + if (comp == LSP_NEWER) + { + lsp_inc_seqnum (lsp, ntohl (hdr->seq_num)); + lsp_set_all_srmflags (lsp); + } + else + { + ISIS_SET_FLAG (lsp->SRMflags, circuit); + ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); + } + if (isis->debugs & DEBUG_UPDATE_PACKETS) + zlog_debug ("ISIS-Upd (%s): (1) re-originating LSP %s new " + "seq 0x%08x", circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), + ntohl (lsp->lsp_header->seq_num)); + } } return retval; } @@ -1151,7 +1579,7 @@ dontcheckadj: if (!lsp) { /* 7.3.16.4: initiate a purge */ - lsp_purge_non_exist (hdr, circuit->area); + lsp_purge_non_exist(level, hdr, circuit->area); return ISIS_OK; } /* 7.3.15.1 d) - If this is our own lsp and we have it */ @@ -1160,25 +1588,19 @@ dontcheckadj: * has information that the current sequence number for source S is * "greater" than that held by S, ... */ - else if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num)) + if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num)) { /* 7.3.16.1 */ - lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1); - - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); - - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + lsp_inc_seqnum (lsp, ntohl (hdr->seq_num)); if (isis->debugs & DEBUG_UPDATE_PACKETS) zlog_debug ("ISIS-Upd (%s): (2) re-originating LSP %s new seq " "0x%08x", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), ntohl (lsp->lsp_header->seq_num)); - lsp->lsp_header->rem_lifetime = - htons (isis_jitter - (circuit->area->max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); } + /* If the received LSP is older or equal, + * resend the LSP which will act as ACK */ + lsp_set_all_srmflags (lsp); } else { @@ -1187,21 +1609,6 @@ dontcheckadj: /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */ if ((!lsp || comp == LSP_NEWER)) { - /* i */ - if (lsp) - { -#ifdef EXTREME_DEBUG - zlog_debug ("level %d number is - %ld", level, - circuit->area->lspdb[level - 1]->dict_nodecount); -#endif /* EXTREME DEBUG */ - lsp_search_and_destroy (hdr->lsp_id, - circuit->area->lspdb[level - 1]); - /* exists, so we overwrite */ -#ifdef EXTREME_DEBUG - zlog_debug ("level %d number is - %ld", level, - circuit->area->lspdb[level - 1]->dict_nodecount); -#endif /* EXTREME DEBUG */ - } /* * If this lsp is a frag, need to see if we have zero lsp present */ @@ -1212,19 +1619,24 @@ dontcheckadj: lsp0 = lsp_search (lspid, circuit->area->lspdb[level - 1]); if (!lsp0) { - zlog_debug ("Got lsp frag, while zero lsp not database"); + zlog_debug ("Got lsp frag, while zero lsp not in database"); return ISIS_OK; } } - lsp = - lsp_new_from_stream_ptr (circuit->rcv_stream, - ntohs (hdr->pdu_len), lsp0, - circuit->area); - lsp->level = level; - lsp->adj = adj; - lsp_insert (lsp, circuit->area->lspdb[level - 1]); + /* i */ + if (!lsp) + { + lsp = lsp_new_from_stream_ptr (circuit->rcv_stream, + pdu_len, lsp0, + circuit->area, level); + lsp_insert (lsp, circuit->area->lspdb[level - 1]); + } + else /* exists, so we overwrite */ + { + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); + } /* ii */ - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + lsp_set_all_srmflags (lsp); /* iii */ ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); @@ -1237,11 +1649,9 @@ dontcheckadj: else if (comp == LSP_EQUAL) { ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); - lsp_update (lsp, hdr, circuit->rcv_stream, circuit->area, level); + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); if (circuit->circ_type != CIRCUIT_T_BROADCAST) - { - ISIS_SET_FLAG (lsp->SSNflags, circuit); - } + ISIS_SET_FLAG (lsp->SSNflags, circuit); } /* 7.3.15.1 e) 3) LSP older than the one in db */ else @@ -1250,8 +1660,6 @@ dontcheckadj: ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } - if (lsp) - lsp->adj = adj; return retval; } @@ -1263,16 +1671,16 @@ dontcheckadj: static int process_snp (int snp_type, int level, struct isis_circuit *circuit, - u_char * ssnpa) + const u_char *ssnpa) { int retval = ISIS_OK; int cmp, own_lsp; char typechar = ' '; - int len; + 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; + uint32_t found = 0, expected = 0, auth_tlv_offset = 0; struct isis_lsp *lsp; struct lsp_entry *entry; struct listnode *node, *nnode; @@ -1287,12 +1695,14 @@ process_snp (int snp_type, int level, struct isis_circ typechar = 'C'; chdr = (struct isis_complete_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); - circuit->rcv_stream->getp += ISIS_CSNP_HDRLEN; - len = ntohs (chdr->pdu_len); - if (len < ISIS_CSNP_HDRLEN) + 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!"); - return ISIS_OK; + zlog_warn ("Received a CSNP with bogus length %d", pdu_len); + return ISIS_WARNING; } } else @@ -1300,15 +1710,24 @@ process_snp (int snp_type, int level, struct isis_circ typechar = 'P'; phdr = (struct isis_partial_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); - circuit->rcv_stream->getp += ISIS_PSNP_HDRLEN; - len = ntohs (phdr->pdu_len); - if (len < ISIS_PSNP_HDRLEN) + 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 CSNP with bogus length!"); - return ISIS_OK; + zlog_warn ("Received a PSNP with bogus length %d", 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); + /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */ if (circuit->ext_domain) { @@ -1322,7 +1741,7 @@ process_snp (int snp_type, int level, struct isis_circ } /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */ - if (!accept_level (level, circuit->circuit_is_type)) + if (!accept_level (level, circuit->is_type)) { zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " @@ -1331,26 +1750,23 @@ process_snp (int snp_type, int level, struct isis_circ level, typechar, circuit->interface->name, - circuit_t2string (circuit->circuit_is_type), level); + circuit_t2string (circuit->is_type), level); return ISIS_OK; } /* 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->circ_type == CIRCUIT_T_BROADCAST) && + (!circuit->u.bc.is_dr[level - 1])) { - if (!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", + circuit->area->area_tag, + level, + typechar, snpa_print (ssnpa), circuit->interface->name); - zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, " - "skipping: we are not the DIS", - circuit->area->area_tag, - level, - typechar, snpa_print (ssnpa), circuit->interface->name); - - return ISIS_OK; - } + return ISIS_OK; } /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked */ @@ -1380,7 +1796,10 @@ process_snp (int snp_type, int level, struct isis_circ else { if (!circuit->u.p2p.neighbor) - return ISIS_OK; /* Silently discard */ + { + zlog_warn ("no p2p neighbor on circuit %s", circuit->interface->name); + return ISIS_OK; /* Silently discard */ + } } /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */ @@ -1392,10 +1811,12 @@ process_snp (int snp_type, int level, struct isis_circ /* 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), - len - circuit->rcv_stream->getp, - &expected, &found, &tlvs); + pdu_len - stream_get_getp (circuit->rcv_stream), + &expected, &found, &tlvs, &auth_tlv_offset); if (retval > ISIS_WARNING) { @@ -1404,7 +1825,7 @@ process_snp (int snp_type, int level, struct isis_circ return retval; } - if (level == 1) + if (level == IS_LEVEL_1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; @@ -1412,16 +1833,19 @@ process_snp (int snp_type, int level, struct isis_circ if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) { if (passwd->type) - { - if (!(found & TLVFLAG_AUTH_INFO) || - authentication_check (passwd, &tlvs.auth_info)) - { - isis_event_auth_failure (circuit->area->area_tag, - "SNP authentication" " failure", - phdr ? phdr->source_id : chdr->source_id); - return ISIS_OK; - } - } + { + 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; + } + } } /* debug isis snp-packets */ @@ -1461,19 +1885,18 @@ process_snp (int snp_type, int level, struct isis_circ /* 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 */ + /* 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 { - /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM - * on p2p */ if (own_lsp) { lsp_inc_seqnum (lsp, ntohl (entry->seq_num)); @@ -1482,8 +1905,8 @@ process_snp (int snp_type, int level, struct isis_circ else { ISIS_SET_FLAG (lsp->SSNflags, circuit); - if (circuit->circ_type != CIRCUIT_T_BROADCAST) - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); } } } @@ -1494,9 +1917,11 @@ process_snp (int snp_type, int level, struct isis_circ if (entry->rem_lifetime && entry->checksum && entry->seq_num && memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { - lsp = lsp_new (entry->lsp_id, ntohs (entry->rem_lifetime), - 0, 0, entry->checksum, level); + 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); } } @@ -1507,7 +1932,8 @@ process_snp (int snp_type, int level, struct isis_circ if (snp_type == ISIS_SNP_CSNP_FLAG) { /* - * Build a list from our own LSP db bounded with start_ and stop_lsp_id + * 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, @@ -1530,11 +1956,10 @@ process_snp (int snp_type, int level, struct isis_circ } /* 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); - } /* lets free it */ - list_free (lsp_list); + list_delete (lsp_list); + } free_tlvs (&tlvs); @@ -1542,8 +1967,18 @@ process_snp (int snp_type, int level, struct isis_circ } static int -process_csnp (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_csnp (int level, struct isis_circuit *circuit, const u_char *ssnpa) { + 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)); + } + /* Sanity check - FIXME: move to correct place */ if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_CSNP_HDRLEN) @@ -1556,12 +1991,22 @@ process_csnp (int level, struct isis_circuit *circuit, } static int -process_psnp (int level, struct isis_circuit *circuit, u_char * ssnpa) +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"); + zlog_warn ("Packet too short ( < %d)", ISIS_PSNP_HDRLEN); return ISIS_WARNING; } @@ -1569,79 +2014,6 @@ process_psnp (int level, struct isis_circuit *circuit, } /* - * Process ISH - * ISO - 10589 - * Section 8.2.2 - Receiving ISH PDUs by an intermediate system - * FIXME: sample packet dump, need to figure 0x81 - looks like NLPid - * 0x82 0x15 0x01 0x00 0x04 0x01 0x2c 0x59 - * 0x38 0x08 0x47 0x00 0x01 0x00 0x02 0x00 - * 0x03 0x00 0x81 0x01 0xcc - */ -static int -process_is_hello (struct isis_circuit *circuit) -{ - struct isis_adjacency *adj; - int retval = ISIS_OK; - u_char neigh_len; - u_char *sysid; - - /* In this point in time we are not yet able to handle is_hellos - * on lan - Sorry juniper... - */ - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - return retval; - - neigh_len = stream_getc (circuit->rcv_stream); - sysid = STREAM_PNT (circuit->rcv_stream) + neigh_len - 1 - ISIS_SYS_ID_LEN; - adj = circuit->u.p2p.neighbor; - if (!adj) - { - /* 8.2.2 */ - adj = isis_new_adj (sysid, NULL, 0, circuit); - if (adj == NULL) - return ISIS_ERROR; - - isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - adj->sys_type = ISIS_SYSTYPE_UNKNOWN; - circuit->u.p2p.neighbor = adj; - } - /* 8.2.2 a) */ - if ((adj->adj_state == ISIS_ADJ_UP) && memcmp (adj->sysid, sysid, - ISIS_SYS_ID_LEN)) - { - /* 8.2.2 a) 1) FIXME: adjStateChange(down) event */ - /* 8.2.2 a) 2) delete the adj */ - XFREE (MTYPE_ISIS_ADJACENCY, adj); - /* 8.2.2 a) 3) create a new adj */ - adj = isis_new_adj (sysid, NULL, 0, circuit); - if (adj == NULL) - return ISIS_ERROR; - - /* 8.2.2 a) 3) i */ - isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - /* 8.2.2 a) 3) ii */ - adj->sys_type = ISIS_SYSTYPE_UNKNOWN; - /* 8.2.2 a) 4) quite meaningless */ - } - /* 8.2.2 b) ignore on condition */ - if ((adj->adj_state == ISIS_ADJ_INITIALIZING) && - (adj->sys_type == ISIS_SYSTYPE_IS)) - { - /* do nothing */ - } - else - { - /* 8.2.2 c) respond with a p2p IIH */ - send_hello (circuit, 1); - } - /* 8.2.2 d) type is IS */ - adj->sys_type = ISIS_SYSTYPE_IS; - /* 8.2.2 e) FIXME: Circuit type of? */ - - return retval; -} - -/* * PDU Dispatcher */ @@ -1649,7 +2021,6 @@ static int isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) { struct isis_fixed_hdr *hdr; - struct esis_fixed_hdr *esis_hdr; int retval = ISIS_OK; @@ -1660,7 +2031,7 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS)) { - zlog_warn ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp); + zlog_err ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp); return ISIS_ERROR; } @@ -1669,28 +2040,11 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char */ if (hdr->idrp == ISO9542_ESIS) { - esis_hdr = (struct esis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream); - stream_set_getp (circuit->rcv_stream, ESIS_FIXED_HDR_LEN); - /* FIXME: Need to do some acceptence tests */ - /* example length... */ - switch (esis_hdr->pdu_type) - { - case ESH_PDU: - /* FIXME */ - break; - case ISH_PDU: - zlog_debug ("AN ISH PDU!!"); - retval = process_is_hello (circuit); - break; - default: - return ISIS_ERROR; - } - return retval; + zlog_err ("No support for ES-IS packet IDRP=%02x", hdr->idrp); + return ISIS_ERROR; } - else - { - stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN); - } + stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN); + /* * and then process it */ @@ -1721,6 +2075,14 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char zlog_warn ("Unsupported ISIS version %u", hdr->version2); return ISIS_WARNING; } + + if (circuit->is_passive) + { + zlog_warn ("Received ISIS PDU on passive circuit %s", + circuit->interface->name); + return ISIS_WARNING; + } + /* either 3 or 0 */ if ((hdr->max_area_addrs != 0) && (hdr->max_area_addrs != isis->max_area_addrs)) @@ -1781,14 +2143,8 @@ isis_receive (struct thread *thread) circuit = THREAD_ARG (thread); assert (circuit); - if (!circuit->area) - return ISIS_OK; + isis_circuit_stream(circuit, &circuit->rcv_stream); - if (circuit->rcv_stream == NULL) - circuit->rcv_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->rcv_stream); - retval = circuit->rx (circuit, ssnpa); circuit->t_read = NULL; @@ -1798,8 +2154,11 @@ isis_receive (struct thread *thread) /* * prepare for next packet. */ - THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit, - circuit->fd); + if (!circuit->is_passive) + { + THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit, + circuit->fd); + } return retval; } @@ -1820,10 +2179,7 @@ isis_receive (struct thread *thread) circuit->t_read = NULL; - if (circuit->rcv_stream == NULL) - circuit->rcv_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->rcv_stream); + isis_circuit_stream(circuit, &circuit->rcv_stream); retval = circuit->rx (circuit, ssnpa); @@ -1833,10 +2189,13 @@ isis_receive (struct thread *thread) /* * prepare for next packet. */ - circuit->t_read = thread_add_timer_msec (master, isis_receive, circuit, - listcount - (circuit->area->circuit_list) * - 100); + if (!circuit->is_passive) + { + circuit->t_read = thread_add_timer_msec (master, isis_receive, circuit, + listcount + (circuit->area->circuit_list) * + 100); + } return retval; } @@ -1911,24 +2270,24 @@ 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; - unsigned long len_pointer, length; int retval; + if (circuit->is_passive) + return ISIS_OK; + if (circuit->interface->mtu == 0) { zlog_warn ("circuit has zero MTU"); return ISIS_WARNING; } - if (!circuit->snd_stream) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->snd_stream); + isis_circuit_stream(circuit, &circuit->snd_stream); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - if (level == 1) + if (level == IS_LEVEL_1) fill_fixed_hdr_andstream (&fixed_hdr, L1_LAN_HELLO, circuit->snd_stream); else @@ -1943,12 +2302,9 @@ send_hello (struct isis_circuit *circuit, int level) memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr)); interval = circuit->hello_multiplier[level - 1] * circuit->hello_interval[level - 1]; - /* If we are the DIS then hello interval is divided by three, as is the hold-timer */ - if (circuit->u.bc.is_dr[level - 1]) - interval=interval/3; if (interval > USHRT_MAX) interval = USHRT_MAX; - hello_hdr.circuit_t = circuit->circuit_is_type; + 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); @@ -1965,13 +2321,13 @@ send_hello (struct isis_circuit *circuit, int level) } else { - hello_hdr.prio = circuit->u.bc.priority[level - 1]; - if (level == 1 && circuit->u.bc.l1_desig_is) + 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 == 2 && circuit->u.bc.l2_desig_is) + else if (level == IS_LEVEL_2) { memcpy (hello_hdr.lan_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); @@ -1980,27 +2336,49 @@ send_hello (struct isis_circuit *circuit, int level) } /* - * Then the variable length part + * Then the variable length part. */ + /* add circuit password */ - if (circuit->passwd.type) - if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len, - circuit->passwd.passwd, circuit->snd_stream)) - return ISIS_WARNING; + 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 */ - assert (circuit->area); - if (circuit->area->area_addrs && circuit->area->area_addrs->count > 0) - if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream)) - return ISIS_WARNING; + 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 == 1 && circuit->u.bc.lan_neighs[0]->count > 0) + 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 == 2 && circuit->u.bc.lan_neighs[1]->count > 0) + 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; @@ -2011,19 +2389,20 @@ send_hello (struct isis_circuit *circuit, int level) if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream)) return ISIS_WARNING; /* IP interface Address TLV */ - if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0) + 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; #ifdef HAVE_IPV6 /* IPv6 Interface Address TLV */ if (circuit->ipv6_router && circuit->ipv6_link && - circuit->ipv6_link->count > 0) + listcount (circuit->ipv6_link) > 0) if (tlv_add_ipv6_addrs (circuit->ipv6_link, circuit->snd_stream)) return ISIS_WARNING; #endif /* HAVE_IPV6 */ - if (circuit->u.bc.pad_hellos) + if (circuit->pad_hellos) if (tlv_add_padding (circuit->snd_stream)) return ISIS_WARNING; @@ -2031,69 +2410,71 @@ send_hello (struct isis_circuit *circuit, int level) /* Update PDU length */ stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length); - retval = circuit->tx (circuit, level); - if (retval) - zlog_warn ("sending of LAN Level %d Hello failed", level); + /* 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); + } - /* DEBUG_ADJ_PACKETS */ if (isis->debugs & DEBUG_ADJ_PACKETS) { if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - zlog_debug ("ISIS-Adj (%s): Sent L%d LAN IIH on %s, length %ld", + zlog_debug ("ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd", circuit->area->area_tag, level, circuit->interface->name, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); + length); } else { - zlog_debug ("ISIS-Adj (%s): Sent P2P IIH on %s, length %ld", + zlog_debug ("ISIS-Adj (%s): Sending P2P IIH on %s, length %zd", circuit->area->area_tag, circuit->interface->name, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); + length); } + 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); + if (retval != ISIS_OK) + zlog_err ("ISIS-Adj (%s): Send L%d IIH on %s failed", + circuit->area->area_tag, level, circuit->interface->name); + return retval; } -static int -send_lan_hello (struct isis_circuit *circuit, int level) -{ - return send_hello (circuit, level); -} - int send_lan_l1_hello (struct thread *thread) { struct isis_circuit *circuit; int retval; - unsigned long next_hello; circuit = THREAD_ARG (thread); assert (circuit); - - if (!circuit->area) { - return ISIS_OK; - } - - /* Pseudonode sends hellos three times more than the other nodes */ - if (circuit->u.bc.is_dr[0]) - next_hello=circuit->hello_interval[0]/3+1; - else - next_hello=circuit->hello_interval[0]; - circuit->u.bc.t_send_lan_hello[0] = NULL; + if (!(circuit->area->is_type & IS_LEVEL_1)) + { + zlog_warn ("ISIS-Hello (%s): Trying to send L1 IIH in L2-only area", + circuit->area->area_tag); + return 1; + } + if (circuit->u.bc.run_dr_elect[0]) retval = isis_dr_elect (circuit, 1); - retval = send_lan_hello (circuit, 1); + retval = send_hello (circuit, 1); /* set next timer thread */ THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0], send_lan_l1_hello, circuit, - isis_jitter (next_hello, IIH_JITTER)); + isis_jitter (circuit->hello_interval[0], IIH_JITTER)); return retval; } @@ -2103,32 +2484,27 @@ send_lan_l2_hello (struct thread *thread) { struct isis_circuit *circuit; int retval; - unsigned long next_hello; circuit = THREAD_ARG (thread); assert (circuit); - - if (!circuit->area) { - return ISIS_OK; - } - - /* Pseudonode sends hellos three times more than the other nodes */ - if (circuit->u.bc.is_dr[1]) - next_hello=circuit->hello_interval[1]/3+1; - else - next_hello=circuit->hello_interval[1]; - circuit->u.bc.t_send_lan_hello[1] = NULL; + if (!(circuit->area->is_type & IS_LEVEL_2)) + { + zlog_warn ("ISIS-Hello (%s): Trying to send L2 IIH in L1 area", + circuit->area->area_tag); + return 1; + } + if (circuit->u.bc.run_dr_elect[1]) retval = isis_dr_elect (circuit, 2); - retval = send_lan_hello (circuit, 2); + retval = send_hello (circuit, 2); /* set next timer thread */ THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[1], send_lan_l2_hello, circuit, - isis_jitter (next_hello, IIH_JITTER)); + isis_jitter (circuit->hello_interval[1], IIH_JITTER)); return retval; } @@ -2158,11 +2534,15 @@ build_csnp (int level, u_char * start, u_char * stop, { struct isis_fixed_hdr fixed_hdr; struct isis_passwd *passwd; - int retval = ISIS_OK; 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; - if (level == 1) + 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 @@ -2186,81 +2566,244 @@ build_csnp (int level, u_char * start, u_char * stop, /* * And TLVs */ - if (level == 1) + 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)) - if (passwd->type) - retval = tlv_add_authinfo (passwd->type, passwd->len, - passwd->passwd, circuit->snd_stream); - - if (!retval && lsps) + { + switch (passwd->type) { - retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); + /* 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); - assert (length >= ISIS_CSNP_HDRLEN); /* 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. + */ +static uint16_t +get_max_lsp_count (uint16_t size) +{ + uint16_t tlv_count; + uint16_t lsp_count; + uint16_t remaining_size; + + /* First count the full size TLVs */ + tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE; + lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN); + + /* The last TLV, if any */ + remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE; + if (remaining_size - 2 >= LSP_ENTRIES_LEN) + lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN; + + 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) { - int retval = ISIS_OK; 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; + memset (start, 0x00, ISIS_SYS_ID_LEN + 2); memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); - if (circuit->area->lspdb[level - 1] && - dict_count (circuit->area->lspdb[level - 1]) > 0) + num_lsps = max_lsps_per_snp (ISIS_SNP_CSNP_FLAG, level, circuit); + + while (loop) { list = list_new (); - lsp_build_list (start, stop, list, circuit->area->lspdb[level - 1]); - - if (circuit->snd_stream == NULL) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); + lsp_build_list (start, stop, num_lsps, list, + circuit->area->lspdb[level - 1]); + /* + * Update the stop lsp_id before encoding this CSNP. + */ + if (listcount (list) < num_lsps) + { + memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); + } else - stream_reset (circuit->snd_stream); + { + node = listtail (list); + lsp = listgetdata (node); + memcpy (stop, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2); + } 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; + } if (isis->debugs & DEBUG_SNP_PACKETS) - { - zlog_debug ("ISIS-Snp (%s): Sent L%d CSNP on %s, length %ld", - circuit->area->area_tag, level, circuit->interface->name, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) STREAM_SIZE (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)); - } - } + { + zlog_debug ("ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd", + 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)); + } + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream)); + } - list_delete (list); + 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); + return retval; + } - if (retval == ISIS_OK) - retval = circuit->tx (circuit, level); + /* + * Start lsp_id of the next CSNP should be one plus the + * stop lsp_id in this current CSNP. + */ + memcpy (start, stop, ISIS_SYS_ID_LEN + 2); + loop = 0; + for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) + { + if (start[i] < (u_char)0xff) + { + start[i] += 1; + loop = 1; + break; + } + } + memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); + list_delete (list); } + return retval; } @@ -2314,12 +2857,16 @@ build_psnp (int level, struct isis_circuit *circuit, s struct isis_fixed_hdr fixed_hdr; unsigned long lenp; u_int16_t length; - int retval = 0; 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; - if (level == 1) + 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 @@ -2338,21 +2885,41 @@ build_psnp (int level, struct isis_circuit *circuit, s * And TLVs */ - if (level == 1) + 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)) - if (passwd->type) - retval = tlv_add_authinfo (passwd->type, passwd->len, - passwd->passwd, circuit->snd_stream); - - if (!retval && lsps) + { + switch (passwd->type) { - retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); + /* 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)) @@ -2368,10 +2935,22 @@ build_psnp (int level, struct isis_circuit *circuit, s } length = (u_int16_t) stream_get_endp (circuit->snd_stream); - assert (length >= ISIS_PSNP_HDRLEN); /* 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; } @@ -2382,54 +2961,74 @@ build_psnp (int level, struct isis_circuit *circuit, s static int send_psnp (int level, struct isis_circuit *circuit) { - int retval = ISIS_OK; 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]) || - circuit->circ_type != CIRCUIT_T_BROADCAST) - { + if (circuit->circ_type == CIRCUIT_T_BROADCAST && + circuit->u.bc.is_dr[level - 1]) + return ISIS_OK; - if (circuit->area->lspdb[level - 1] && - dict_count (circuit->area->lspdb[level - 1]) > 0) - { - list = list_new (); - lsp_build_list_ssn (circuit, list, circuit->area->lspdb[level - 1]); + if (circuit->area->lspdb[level - 1] == NULL || + dict_count (circuit->area->lspdb[level - 1]) == 0) + return ISIS_OK; - if (listcount (list) > 0) - { - if (circuit->snd_stream == NULL) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->snd_stream); + if (! circuit->snd_stream) + return ISIS_ERROR; + num_lsps = max_lsps_per_snp (ISIS_SNP_PSNP_FLAG, level, circuit); - if (isis->debugs & DEBUG_SNP_PACKETS) - zlog_debug ("ISIS-Snp (%s): Sent L%d PSNP on %s, length %ld", - circuit->area->area_tag, level, - circuit->interface->name, - /* FIXME: use %z when we stop supporting old - * compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); + while (1) + { + list = list_new (); + lsp_build_list_ssn (circuit, num_lsps, list, + circuit->area->lspdb[level - 1]); - retval = build_psnp (level, circuit, list); - if (retval == ISIS_OK) - retval = circuit->tx (circuit, level); + if (listcount (list) == 0) + { + list_delete (list); + return ISIS_OK; + } - if (retval == ISIS_OK) - { - /* - * 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); - } + 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; + } + + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug ("ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd", + circuit->area->area_tag, level, + 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)); + } + + 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); + return retval; + } + + /* + * 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); } return retval; @@ -2488,89 +3087,115 @@ send_lsp (struct thread *thread) struct isis_circuit *circuit; struct isis_lsp *lsp; struct listnode *node; - int retval = 0; + int clear_srm = 1; + int retval = ISIS_OK; circuit = THREAD_ARG (thread); assert (circuit); - if (circuit->state == C_STATE_UP) - { - lsp = listgetdata ((node = listhead (circuit->lsp_queue))); + if (!circuit->lsp_queue) + return ISIS_OK; - /* - * Do not send if levels do not match - */ - if (!(lsp->level & circuit->circuit_is_type)) - goto dontsend; + node = listhead (circuit->lsp_queue); - /* - * Do not send if we do not have adjacencies in state up on the circuit - */ - if (circuit->upadjcount[lsp->level - 1] == 0) - goto dontsend; - /* only send if it needs sending */ - if ((time (NULL) - lsp->last_sent) >= - circuit->area->lsp_gen_interval[lsp->level - 1]) - { + /* + * Handle case where there are no LSPs on the queue. This can + * happen, for instance, if an adjacency goes down before this + * thread gets a chance to run. + */ + if (!node) + return ISIS_OK; - if (isis->debugs & DEBUG_UPDATE_PACKETS) - { - zlog_debug - ("ISIS-Upd (%s): Sent 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); - } - /* copy our lsp to the send buffer */ - stream_copy (circuit->snd_stream, lsp->pdu); + /* + * Delete LSP from lsp_queue. If it's still in queue, it is assumed + * as 'transmit pending', but send_lsp may never be called again. + * Retry will happen because SRM flag will not be cleared. + */ + lsp = listgetdata(node); + list_delete_node (circuit->lsp_queue, node); - retval = circuit->tx (circuit, lsp->level); + /* Set the last-cleared time if the queue is empty. */ + /* TODO: Is is possible that new lsps keep being added to the queue + * that the queue is never empty? */ + if (list_isempty (circuit->lsp_queue)) + circuit->lsp_queue_last_cleared = time (NULL); - /* - * If the sending succeeded, we can del the lsp from circuits - * lsp_queue - */ - if (retval == ISIS_OK) - { - list_delete_node (circuit->lsp_queue, node); + if (circuit->state != C_STATE_UP || circuit->is_passive == 1) + goto out; - /* - * On broadcast circuits also the SRMflag can be cleared - */ - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* + * Do not send if levels do not match + */ + if (!(lsp->level & circuit->is_type)) + goto out; - if (flags_any_set (lsp->SRMflags) == 0) - { - /* - * need to remember when we were last sent - */ - lsp->last_sent = time (NULL); - } - } - else - { - zlog_debug ("sending of level %d link state failed", lsp->level); - } - } - else - { - /* my belief is that if it wasn't his time, the lsp can be removed - * from the queue - */ - dontsend: - list_delete_node (circuit->lsp_queue, node); - } -#if 0 - /* - * If there are still LSPs send next one after lsp-interval (33 msecs) + /* + * Do not send if we do not have adjacencies in state up on the circuit + */ + if (circuit->upadjcount[lsp->level - 1] == 0) + goto out; + + /* stream_copy will assert and stop program execution if LSP is larger than + * 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.", + 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, + stream_get_endp(lsp->pdu), + stream_get_size(circuit->snd_stream)); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data(STREAM_DATA(lsp->pdu), stream_get_endp(lsp->pdu)); + retval = ISIS_ERROR; + goto out; + } + + /* copy our lsp to the send buffer */ + 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); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream)); + } + + clear_srm = 0; + retval = circuit->tx (circuit, lsp->level); + if (retval != ISIS_OK) + { + zlog_err ("ISIS-Upd (%s): Send L%d LSP on %s failed %s", + circuit->area->area_tag, lsp->level, + circuit->interface->name, + (retval == ISIS_WARNING) ? "temporarily" : "permanently"); + } + +out: + if (clear_srm + || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST) + || (retval != ISIS_OK && retval != ISIS_WARNING)) + { + /* SRM flag will trigger retransmission. We will not retransmit if we + * encountered a fatal error. + * On success, they should only be cleared if it's a broadcast circuit. + * On a P2P circuit, we will wait for the ack from the neighbor to clear + * the fag. */ - if (listcount (circuit->lsp_queue) > 0) - thread_add_timer (master, send_lsp, circuit, 1); -#endif + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); } return retval; @@ -2585,13 +3210,10 @@ ack_lsp (struct isis_link_state_hdr *hdr, struct isis_ u_int16_t length; struct isis_fixed_hdr fixed_hdr; - if (!circuit->snd_stream) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->snd_stream); + isis_circuit_stream(circuit, &circuit->snd_stream); -// fill_llc_hdr (stream); - if (level == 1) + // fill_llc_hdr (stream); + if (level == IS_LEVEL_1) fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM, circuit->snd_stream); else @@ -2616,7 +3238,10 @@ ack_lsp (struct isis_link_state_hdr *hdr, struct isis_ 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; } -