2 * IS-IS Rout(e)ing protocol - isis_pdu.c
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public Licenseas published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
14 * This program is distributed in the hope that it will be useful,but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
38 #include "isisd/dict.h"
39 #include "isisd/isis_constants.h"
40 #include "isisd/isis_common.h"
41 #include "isisd/isis_flags.h"
42 #include "isisd/isis_adjacency.h"
43 #include "isisd/isis_circuit.h"
44 #include "isisd/isis_network.h"
45 #include "isisd/isis_misc.h"
46 #include "isisd/isis_dr.h"
47 #include "isisd/isis_tlv.h"
48 #include "isisd/isisd.h"
49 #include "isisd/isis_dynhn.h"
50 #include "isisd/isis_lsp.h"
51 #include "isisd/isis_pdu.h"
52 #include "isisd/iso_checksum.h"
53 #include "isisd/isis_csm.h"
54 #include "isisd/isis_events.h"
55 #include "isisd/isis_te.h"
57 #define ISIS_MINIMUM_FIXED_HDR_LEN 15
58 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */
64 /* Utility mask array. */
65 static const u_char maskbit
[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
66 0xf8, 0xfc, 0xfe, 0xff};
73 * Compares two sets of area addresses
75 static int area_match(struct list
*left
, struct list
*right
)
77 struct area_addr
*addr1
, *addr2
;
78 struct listnode
*node1
, *node2
;
80 for (ALL_LIST_ELEMENTS_RO(left
, node1
, addr1
)) {
81 for (ALL_LIST_ELEMENTS_RO(right
, node2
, addr2
)) {
82 if (addr1
->addr_len
== addr2
->addr_len
83 && !memcmp(addr1
->area_addr
, addr2
->area_addr
,
84 (int)addr1
->addr_len
))
89 return 0; /* mismatch */
93 * Check if ip2 is in the ip1's network (function like Prefix.h:prefix_match() )
94 * param ip1 the IS interface ip address structure
95 * param ip2 the IIH's ip address
96 * return 0 the IIH's IP is not in the IS's subnetwork
97 * 1 the IIH's IP is in the IS's subnetwork
99 static int ip_same_subnet(struct prefix_ipv4
*ip1
, struct in_addr
*ip2
)
101 u_char
*addr1
, *addr2
;
102 int shift
, offset
, offsetloop
;
105 addr1
= (u_char
*)&ip1
->prefix
.s_addr
;
106 addr2
= (u_char
*)&ip2
->s_addr
;
107 len
= ip1
->prefixlen
;
110 offsetloop
= offset
= len
/ PNBBY
;
113 if (addr1
[offsetloop
] != addr2
[offsetloop
])
117 if (maskbit
[shift
] & (addr1
[offset
] ^ addr2
[offset
]))
120 return 1; /* match */
124 * Compares two set of ip addresses
125 * param left the local interface's ip addresses
126 * param right the iih interface's ip address
130 static int ip_match(struct list
*left
, struct list
*right
)
132 struct prefix_ipv4
*ip1
;
134 struct listnode
*node1
, *node2
;
136 if ((left
== NULL
) || (right
== NULL
))
139 for (ALL_LIST_ELEMENTS_RO(left
, node1
, ip1
)) {
140 for (ALL_LIST_ELEMENTS_RO(right
, node2
, ip2
)) {
141 if (ip_same_subnet(ip1
, ip2
)) {
142 return 1; /* match */
150 * Checks whether we should accept a PDU of given level
152 static int accept_level(int level
, int circuit_t
)
154 int retval
= ((circuit_t
& level
) == level
); /* simple approach */
160 * Verify authentication information
161 * Support cleartext and HMAC MD5 authentication
163 static int authentication_check(struct isis_passwd
*remote
,
164 struct isis_passwd
*local
,
165 struct stream
*stream
, uint32_t auth_tlv_offset
)
167 unsigned char digest
[ISIS_AUTH_MD5_SIZE
];
169 /* Auth fail () - passwd type mismatch */
170 if (local
->type
!= remote
->type
)
173 switch (local
->type
) {
174 /* No authentication required */
175 case ISIS_PASSWD_TYPE_UNUSED
:
178 /* Cleartext (ISO 10589) */
179 case ISIS_PASSWD_TYPE_CLEARTXT
:
180 /* Auth fail () - passwd len mismatch */
181 if (remote
->len
!= local
->len
)
183 return memcmp(local
->passwd
, remote
->passwd
, local
->len
);
185 /* HMAC MD5 (RFC 3567) */
186 case ISIS_PASSWD_TYPE_HMAC_MD5
:
187 /* Auth fail () - passwd len mismatch */
188 if (remote
->len
!= ISIS_AUTH_MD5_SIZE
)
190 /* Set the authentication value to 0 before the check */
191 memset(STREAM_DATA(stream
) + auth_tlv_offset
+ 3, 0,
193 /* Compute the digest */
194 hmac_md5(STREAM_DATA(stream
), stream_get_endp(stream
),
195 (unsigned char *)&(local
->passwd
), local
->len
,
196 (unsigned char *)&digest
);
197 /* Copy back the authentication value after the check */
198 memcpy(STREAM_DATA(stream
) + auth_tlv_offset
+ 3,
199 remote
->passwd
, ISIS_AUTH_MD5_SIZE
);
200 return memcmp(digest
, remote
->passwd
, ISIS_AUTH_MD5_SIZE
);
203 zlog_err("Unsupported authentication type");
207 /* Authentication pass when no authentication is configured */
211 static int lsp_authentication_check(struct stream
*stream
,
212 struct isis_area
*area
, int level
,
213 struct isis_passwd
*passwd
)
215 struct isis_link_state_hdr
*hdr
;
216 uint32_t expected
= 0, found
= 0, auth_tlv_offset
= 0;
217 uint16_t checksum
, rem_lifetime
, pdu_len
;
219 int retval
= ISIS_OK
;
221 hdr
= (struct isis_link_state_hdr
*)(STREAM_PNT(stream
));
222 pdu_len
= ntohs(hdr
->pdu_len
);
223 expected
|= TLVFLAG_AUTH_INFO
;
224 auth_tlv_offset
= stream_get_getp(stream
) + ISIS_LSP_HDR_LEN
;
225 retval
= parse_tlvs(area
->area_tag
,
226 STREAM_PNT(stream
) + ISIS_LSP_HDR_LEN
,
227 pdu_len
- ISIS_FIXED_HDR_LEN
- ISIS_LSP_HDR_LEN
,
228 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
230 if (retval
!= ISIS_OK
) {
232 "ISIS-Upd (%s): Parse failed L%d LSP %s, seq 0x%08x, "
233 "cksum 0x%04x, lifetime %us, len %u",
234 area
->area_tag
, level
, rawlspid_print(hdr
->lsp_id
),
235 ntohl(hdr
->seq_num
), ntohs(hdr
->checksum
),
236 ntohs(hdr
->rem_lifetime
), pdu_len
);
237 if ((isis
->debugs
& DEBUG_UPDATE_PACKETS
)
238 && (isis
->debugs
& DEBUG_PACKET_DUMP
))
239 zlog_dump_data(STREAM_DATA(stream
),
240 stream_get_endp(stream
));
244 if (!(found
& TLVFLAG_AUTH_INFO
)) {
245 zlog_err("No authentication tlv in LSP");
249 if (tlvs
.auth_info
.type
!= ISIS_PASSWD_TYPE_CLEARTXT
250 && tlvs
.auth_info
.type
!= ISIS_PASSWD_TYPE_HMAC_MD5
) {
251 zlog_err("Unknown authentication type in LSP");
256 * RFC 5304 set checksum and remaining lifetime to zero before
257 * verification and reset to old values after verification.
259 checksum
= hdr
->checksum
;
260 rem_lifetime
= hdr
->rem_lifetime
;
262 hdr
->rem_lifetime
= 0;
263 retval
= authentication_check(&tlvs
.auth_info
, passwd
, stream
,
265 hdr
->checksum
= checksum
;
266 hdr
->rem_lifetime
= rem_lifetime
;
271 static void tlvs_to_adj_area_addrs(struct tlvs
*tlvs
,
272 struct isis_adjacency
*adj
)
274 struct listnode
*node
;
275 struct area_addr
*area_addr
, *malloced
;
277 if (adj
->area_addrs
) {
278 adj
->area_addrs
->del
= isis_area_adj_del_addr
;
279 list_delete(adj
->area_addrs
);
281 adj
->area_addrs
= list_new();
282 if (tlvs
->area_addrs
) {
283 for (ALL_LIST_ELEMENTS_RO(tlvs
->area_addrs
, node
, area_addr
)) {
284 malloced
= XMALLOC(MTYPE_ISIS_TMP
,
285 sizeof(struct area_addr
));
286 memcpy(malloced
, area_addr
, sizeof(struct area_addr
));
287 listnode_add(adj
->area_addrs
, malloced
);
292 static int tlvs_to_adj_nlpids(struct tlvs
*tlvs
, struct isis_adjacency
*adj
)
295 struct nlpids
*tlv_nlpids
;
299 tlv_nlpids
= tlvs
->nlpids
;
300 if (tlv_nlpids
->count
> array_size(adj
->nlpids
.nlpids
))
303 adj
->nlpids
.count
= tlv_nlpids
->count
;
305 for (i
= 0; i
< tlv_nlpids
->count
; i
++) {
306 adj
->nlpids
.nlpids
[i
] = tlv_nlpids
->nlpids
[i
];
312 static void tlvs_to_adj_ipv4_addrs(struct tlvs
*tlvs
,
313 struct isis_adjacency
*adj
)
315 struct listnode
*node
;
316 struct in_addr
*ipv4_addr
, *malloced
;
318 if (adj
->ipv4_addrs
) {
319 adj
->ipv4_addrs
->del
= isis_area_adj_del_addr
;
320 list_delete(adj
->ipv4_addrs
);
322 adj
->ipv4_addrs
= list_new();
323 if (tlvs
->ipv4_addrs
) {
324 for (ALL_LIST_ELEMENTS_RO(tlvs
->ipv4_addrs
, node
, ipv4_addr
)) {
326 XMALLOC(MTYPE_ISIS_TMP
, sizeof(struct in_addr
));
327 memcpy(malloced
, ipv4_addr
, sizeof(struct in_addr
));
328 listnode_add(adj
->ipv4_addrs
, malloced
);
333 static void tlvs_to_adj_ipv6_addrs(struct tlvs
*tlvs
,
334 struct isis_adjacency
*adj
)
336 struct listnode
*node
;
337 struct in6_addr
*ipv6_addr
, *malloced
;
339 if (adj
->ipv6_addrs
) {
340 adj
->ipv6_addrs
->del
= isis_area_adj_del_addr
;
341 list_delete(adj
->ipv6_addrs
);
343 adj
->ipv6_addrs
= list_new();
344 if (tlvs
->ipv6_addrs
) {
345 for (ALL_LIST_ELEMENTS_RO(tlvs
->ipv6_addrs
, node
, ipv6_addr
)) {
346 malloced
= XMALLOC(MTYPE_ISIS_TMP
,
347 sizeof(struct in6_addr
));
348 memcpy(malloced
, ipv6_addr
, sizeof(struct in6_addr
));
349 listnode_add(adj
->ipv6_addrs
, malloced
);
361 * Section 8.2.5 - Receiving point-to-point IIH PDUs
364 static int process_p2p_hello(struct isis_circuit
*circuit
)
366 int retval
= ISIS_OK
;
367 struct isis_p2p_hello_hdr
*hdr
;
368 struct isis_adjacency
*adj
;
369 u_int32_t expected
= 0, found
= 0, auth_tlv_offset
= 0;
372 int v4_usable
= 0, v6_usable
= 0;
374 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
376 "ISIS-Adj (%s): Rcvd P2P IIH on %s, cirType %s, cirID %u",
377 circuit
->area
->area_tag
, circuit
->interface
->name
,
378 circuit_t2string(circuit
->is_type
),
379 circuit
->circuit_id
);
380 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
381 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
382 stream_get_endp(circuit
->rcv_stream
));
385 if (circuit
->circ_type
!= CIRCUIT_T_P2P
) {
386 zlog_warn("p2p hello on non p2p circuit");
390 if ((stream_get_endp(circuit
->rcv_stream
)
391 - stream_get_getp(circuit
->rcv_stream
))
392 < ISIS_P2PHELLO_HDRLEN
) {
393 zlog_warn("Packet too short");
397 /* 8.2.5.1 PDU acceptance tests */
399 /* 8.2.5.1 a) external domain untrue */
400 /* FIXME: not useful at all? */
402 /* 8.2.5.1 b) ID Length mismatch */
403 /* checked at the handle_pdu */
405 /* 8.2.5.2 IIH PDU Processing */
407 /* 8.2.5.2 a) 1) Maximum Area Addresses */
408 /* Already checked, and can also be ommited */
413 hdr
= (struct isis_p2p_hello_hdr
*)STREAM_PNT(circuit
->rcv_stream
);
414 pdu_len
= ntohs(hdr
->pdu_len
);
416 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_P2PHELLO_HDRLEN
)
417 || pdu_len
> ISO_MTU(circuit
)
418 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
420 "ISIS-Adj (%s): Rcvd P2P IIH from (%s) with "
421 "invalid pdu length %d",
422 circuit
->area
->area_tag
, circuit
->interface
->name
,
428 * Set the stream endp to PDU length, ignoring additional padding
429 * introduced by transport chips.
431 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
432 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
434 stream_forward_getp(circuit
->rcv_stream
, ISIS_P2PHELLO_HDRLEN
);
437 * Lets get the TLVS now
439 expected
|= TLVFLAG_AREA_ADDRS
;
440 expected
|= TLVFLAG_AUTH_INFO
;
441 expected
|= TLVFLAG_NLPID
;
442 expected
|= TLVFLAG_IPV4_ADDR
;
443 expected
|= TLVFLAG_IPV6_ADDR
;
445 auth_tlv_offset
= stream_get_getp(circuit
->rcv_stream
);
446 retval
= parse_tlvs(circuit
->area
->area_tag
,
447 STREAM_PNT(circuit
->rcv_stream
),
448 pdu_len
- ISIS_P2PHELLO_HDRLEN
- ISIS_FIXED_HDR_LEN
,
449 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
451 if (retval
> ISIS_WARNING
) {
452 zlog_warn("parse_tlvs() failed");
457 if (!(found
& TLVFLAG_AREA_ADDRS
)) {
458 zlog_warn("No Area addresses TLV in P2P IS to IS hello");
463 if (!(found
& TLVFLAG_NLPID
)) {
464 zlog_warn("No supported protocols TLV in P2P IS to IS hello");
469 /* 8.2.5.1 c) Authentication */
470 if (circuit
->passwd
.type
) {
471 if (!(found
& TLVFLAG_AUTH_INFO
)
472 || authentication_check(&tlvs
.auth_info
, &circuit
->passwd
,
475 isis_event_auth_failure(
476 circuit
->area
->area_tag
,
477 "P2P hello authentication failure",
485 * check if it's own interface ip match iih ip addrs
487 if (found
& TLVFLAG_IPV4_ADDR
) {
488 if (ip_match(circuit
->ip_addrs
, tlvs
.ipv4_addrs
))
492 "ISIS-Adj: IPv4 addresses present but no overlap "
493 "in P2P IIH from %s\n",
494 circuit
->interface
->name
);
496 if (found
& TLVFLAG_IPV6_ADDR
) {
497 /* TBA: check that we have a linklocal ourselves? */
498 struct listnode
*node
;
500 for (ALL_LIST_ELEMENTS_RO(tlvs
.ipv6_addrs
, node
, ip
))
501 if (IN6_IS_ADDR_LINKLOCAL(ip
)) {
508 "ISIS-Adj: IPv6 addresses present but no link-local "
509 "in P2P IIH from %s\n",
510 circuit
->interface
->name
);
513 if (!(found
& (TLVFLAG_IPV4_ADDR
| TLVFLAG_IPV6_ADDR
)))
515 "ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n",
516 circuit
->interface
->name
);
518 if (!v6_usable
&& !v4_usable
) {
524 * it's own p2p IIH PDU - discard
526 if (!memcmp(hdr
->source_id
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
527 zlog_warn("ISIS-Adj (%s): it's own IIH PDU - discarded",
528 circuit
->area
->area_tag
);
534 * My interpertation of the ISO, if no adj exists we will create one for
537 adj
= circuit
->u
.p2p
.neighbor
;
538 /* If an adjacency exists, check it is with the source of the hello
541 if (memcmp(hdr
->source_id
, adj
->sysid
, ISIS_SYS_ID_LEN
)) {
543 "hello source and adjacency do not match, set adj down\n");
544 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
,
549 if (!adj
|| adj
->level
!= hdr
->circuit_t
) {
551 adj
= isis_new_adj(hdr
->source_id
, NULL
, hdr
->circuit_t
,
556 adj
->level
= hdr
->circuit_t
;
558 circuit
->u
.p2p
.neighbor
= adj
;
559 /* Build lsp with the new neighbor entry when a new
560 * adjacency is formed. Set adjacency circuit type to
561 * IIH PDU header circuit type before lsp is regenerated
562 * when an adjacency is up. This will result in the new
563 * adjacency entry getting added to the lsp tlv neighbor list.
565 adj
->circuit_t
= hdr
->circuit_t
;
566 isis_adj_state_change(adj
, ISIS_ADJ_INITIALIZING
, NULL
);
567 adj
->sys_type
= ISIS_SYSTYPE_UNKNOWN
;
570 /* 8.2.6 Monitoring point-to-point adjacencies */
571 adj
->hold_time
= ntohs(hdr
->hold_time
);
572 adj
->last_upd
= time(NULL
);
574 /* we do this now because the adj may not survive till the end... */
575 tlvs_to_adj_area_addrs(&tlvs
, adj
);
577 /* which protocol are spoken ??? */
578 if (tlvs_to_adj_nlpids(&tlvs
, adj
)) {
583 /* we need to copy addresses to the adj */
584 if (found
& TLVFLAG_IPV4_ADDR
)
585 tlvs_to_adj_ipv4_addrs(&tlvs
, adj
);
587 /* Update MPLS TE Remote IP address parameter if possible */
588 if (IS_MPLS_TE(isisMplsTE
) && circuit
->mtc
589 && IS_CIRCUIT_TE(circuit
->mtc
))
590 if (adj
->ipv4_addrs
!= NULL
591 && listcount(adj
->ipv4_addrs
) != 0) {
592 struct in_addr
*ip_addr
;
593 ip_addr
= (struct in_addr
*)listgetdata(
594 (struct listnode
*)listhead(adj
->ipv4_addrs
));
595 set_circuitparams_rmt_ipaddr(circuit
->mtc
, *ip_addr
);
598 if (found
& TLVFLAG_IPV6_ADDR
)
599 tlvs_to_adj_ipv6_addrs(&tlvs
, adj
);
601 /* lets take care of the expiry */
602 THREAD_TIMER_OFF(adj
->t_expire
);
603 THREAD_TIMER_ON(master
, adj
->t_expire
, isis_adj_expire
, adj
,
604 (long)adj
->hold_time
);
606 /* 8.2.5.2 a) a match was detected */
607 if (area_match(circuit
->area
->area_addrs
, tlvs
.area_addrs
)) {
608 /* 8.2.5.2 a) 2) If the system is L1 - table 5 */
609 if (circuit
->area
->is_type
== IS_LEVEL_1
) {
610 switch (hdr
->circuit_t
) {
612 case IS_LEVEL_1_AND_2
:
613 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
614 /* (4) adj state up */
615 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
617 /* (5) adj usage level 1 */
618 adj
->adj_usage
= ISIS_ADJ_LEVEL1
;
619 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
624 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
625 /* (7) reject - wrong system type event
627 zlog_warn("wrongSystemType");
629 return ISIS_WARNING
; /* Reject */
630 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
631 /* (6) down - wrong system */
632 isis_adj_state_change(adj
,
640 /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */
641 if (circuit
->area
->is_type
== IS_LEVEL_1_AND_2
) {
642 switch (hdr
->circuit_t
) {
644 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
645 /* (6) adj state up */
646 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
648 /* (7) adj usage level 1 */
649 adj
->adj_usage
= ISIS_ADJ_LEVEL1
;
650 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
652 } else if ((adj
->adj_usage
653 == ISIS_ADJ_LEVEL1AND2
)
655 == ISIS_ADJ_LEVEL2
)) {
656 /* (8) down - wrong system */
657 isis_adj_state_change(adj
,
663 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
664 /* (6) adj state up */
665 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
667 /* (9) adj usage level 2 */
668 adj
->adj_usage
= ISIS_ADJ_LEVEL2
;
669 } else if ((adj
->adj_usage
== ISIS_ADJ_LEVEL1
)
671 == ISIS_ADJ_LEVEL1AND2
)) {
672 /* (8) down - wrong system */
673 isis_adj_state_change(adj
,
676 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
680 case IS_LEVEL_1_AND_2
:
681 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
682 /* (6) adj state up */
683 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
685 /* (10) adj usage level 1 */
686 adj
->adj_usage
= ISIS_ADJ_LEVEL1AND2
;
687 } else if ((adj
->adj_usage
== ISIS_ADJ_LEVEL1
)
689 == ISIS_ADJ_LEVEL2
)) {
690 /* (8) down - wrong system */
691 isis_adj_state_change(adj
,
694 } else if (adj
->adj_usage
695 == ISIS_ADJ_LEVEL1AND2
) {
702 /* 8.2.5.2 a) 4) If the system is L2 - table 7 */
703 if (circuit
->area
->is_type
== IS_LEVEL_2
) {
704 switch (hdr
->circuit_t
) {
706 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
707 /* (5) reject - wrong system type event
709 zlog_warn("wrongSystemType");
711 return ISIS_WARNING
; /* Reject */
712 } else if ((adj
->adj_usage
713 == ISIS_ADJ_LEVEL1AND2
)
715 == ISIS_ADJ_LEVEL2
)) {
716 /* (6) down - wrong system */
717 isis_adj_state_change(adj
,
722 case IS_LEVEL_1_AND_2
:
724 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
725 /* (7) adj state up */
726 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
728 /* (8) adj usage level 2 */
729 adj
->adj_usage
= ISIS_ADJ_LEVEL2
;
730 } else if (adj
->adj_usage
731 == ISIS_ADJ_LEVEL1AND2
) {
732 /* (6) down - wrong system */
733 isis_adj_state_change(adj
,
736 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
743 /* 8.2.5.2 b) if no match was detected */
744 else if (listcount(circuit
->area
->area_addrs
) > 0) {
745 if (circuit
->area
->is_type
== IS_LEVEL_1
) {
746 /* 8.2.5.2 b) 1) is_type L1 and adj is not up */
747 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
748 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
,
750 /* 8.2.5.2 b) 2)is_type L1 and adj is up */
752 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
,
753 "Down - Area Mismatch");
756 /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
758 switch (hdr
->circuit_t
) {
760 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
761 /* (6) reject - Area Mismatch event */
762 zlog_warn("AreaMismatch");
764 return ISIS_WARNING
; /* Reject */
765 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
766 /* (7) down - area mismatch */
767 isis_adj_state_change(adj
,
771 } else if ((adj
->adj_usage
772 == ISIS_ADJ_LEVEL1AND2
)
774 == ISIS_ADJ_LEVEL2
)) {
775 /* (7) down - wrong system */
776 isis_adj_state_change(adj
,
781 case IS_LEVEL_1_AND_2
:
783 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
784 /* (8) adj state up */
785 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
787 /* (9) adj usage level 2 */
788 adj
->adj_usage
= ISIS_ADJ_LEVEL2
;
789 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
790 /* (7) down - wrong system */
791 isis_adj_state_change(adj
,
794 } else if (adj
->adj_usage
795 == ISIS_ADJ_LEVEL1AND2
) {
796 if (hdr
->circuit_t
== IS_LEVEL_2
) {
797 /* (7) down - wrong system */
798 isis_adj_state_change(
802 /* (7) down - area mismatch */
803 isis_adj_state_change(
807 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
814 /* down - area mismatch */
815 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
, "Area Mismatch");
817 /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
818 /* FIXME - Missing parts */
820 /* some of my own understanding of the ISO, why the heck does
821 * it not say what should I change the system_type to...
823 switch (adj
->adj_usage
) {
824 case ISIS_ADJ_LEVEL1
:
825 adj
->sys_type
= ISIS_SYSTYPE_L1_IS
;
827 case ISIS_ADJ_LEVEL2
:
828 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
830 case ISIS_ADJ_LEVEL1AND2
:
831 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
834 adj
->sys_type
= ISIS_SYSTYPE_UNKNOWN
;
839 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
841 "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s,"
842 " cir id %02d, length %d",
843 circuit
->area
->area_tag
, circuit
->interface
->name
,
844 circuit_t2string(circuit
->is_type
), circuit
->circuit_id
,
854 * Process IS-IS LAN Level 1/2 Hello PDU
856 static int process_lan_hello(int level
, struct isis_circuit
*circuit
,
859 int retval
= ISIS_OK
;
860 struct isis_lan_hello_hdr hdr
;
861 struct isis_adjacency
*adj
;
862 u_int32_t expected
= 0, found
= 0, auth_tlv_offset
= 0;
865 struct listnode
*node
;
866 int v4_usable
= 0, v6_usable
= 0;
868 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
870 "ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, "
872 circuit
->area
->area_tag
, level
,
873 circuit
->interface
->name
,
874 circuit_t2string(circuit
->is_type
),
875 circuit
->circuit_id
);
876 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
877 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
878 stream_get_endp(circuit
->rcv_stream
));
881 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
882 zlog_warn("lan hello on non broadcast circuit");
886 if ((stream_get_endp(circuit
->rcv_stream
)
887 - stream_get_getp(circuit
->rcv_stream
))
888 < ISIS_LANHELLO_HDRLEN
) {
889 zlog_warn("Packet too short");
893 if (circuit
->ext_domain
) {
895 "level %d LAN Hello received over circuit with "
896 "externalDomain = true",
901 if (!accept_level(level
, circuit
->is_type
)) {
902 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
904 "ISIS-Adj (%s): Interface level mismatch, %s",
905 circuit
->area
->area_tag
,
906 circuit
->interface
->name
);
912 /* Cisco's debug message compatability */
913 if (!accept_level (level
, circuit
->area
->is_type
))
915 if (isis
->debugs
& DEBUG_ADJ_PACKETS
)
917 zlog_debug ("ISIS-Adj (%s): is type mismatch",
918 circuit
->area
->area_tag
);
926 hdr
.circuit_t
= stream_getc(circuit
->rcv_stream
);
927 stream_get(hdr
.source_id
, circuit
->rcv_stream
, ISIS_SYS_ID_LEN
);
928 hdr
.hold_time
= stream_getw(circuit
->rcv_stream
);
929 hdr
.pdu_len
= stream_getw(circuit
->rcv_stream
);
930 hdr
.prio
= stream_getc(circuit
->rcv_stream
);
931 stream_get(hdr
.lan_id
, circuit
->rcv_stream
, ISIS_SYS_ID_LEN
+ 1);
933 if (hdr
.pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_LANHELLO_HDRLEN
)
934 || hdr
.pdu_len
> ISO_MTU(circuit
)
935 || hdr
.pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
937 "ISIS-Adj (%s): Rcvd LAN IIH from (%s) with "
938 "invalid pdu length %d",
939 circuit
->area
->area_tag
, circuit
->interface
->name
,
945 * Set the stream endp to PDU length, ignoring additional padding
946 * introduced by transport chips.
948 if (hdr
.pdu_len
< stream_get_endp(circuit
->rcv_stream
))
949 stream_set_endp(circuit
->rcv_stream
, hdr
.pdu_len
);
951 if (hdr
.circuit_t
!= IS_LEVEL_1
&& hdr
.circuit_t
!= IS_LEVEL_2
952 && hdr
.circuit_t
!= IS_LEVEL_1_AND_2
953 && (level
& hdr
.circuit_t
) == 0) {
954 zlog_err("Level %d LAN Hello with Circuit Type %d", level
,
962 expected
|= TLVFLAG_AUTH_INFO
;
963 expected
|= TLVFLAG_AREA_ADDRS
;
964 expected
|= TLVFLAG_LAN_NEIGHS
;
965 expected
|= TLVFLAG_NLPID
;
966 expected
|= TLVFLAG_IPV4_ADDR
;
967 expected
|= TLVFLAG_IPV6_ADDR
;
969 auth_tlv_offset
= stream_get_getp(circuit
->rcv_stream
);
971 circuit
->area
->area_tag
, STREAM_PNT(circuit
->rcv_stream
),
972 hdr
.pdu_len
- ISIS_LANHELLO_HDRLEN
- ISIS_FIXED_HDR_LEN
,
973 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
975 if (retval
> ISIS_WARNING
) {
976 zlog_warn("parse_tlvs() failed");
980 if (!(found
& TLVFLAG_AREA_ADDRS
)) {
982 "No Area addresses TLV in Level %d LAN IS to IS hello",
984 retval
= ISIS_WARNING
;
988 if (!(found
& TLVFLAG_NLPID
)) {
990 "No supported protocols TLV in Level %d LAN IS to IS hello",
992 retval
= ISIS_WARNING
;
996 /* Verify authentication, either cleartext of HMAC MD5 */
997 if (circuit
->passwd
.type
) {
998 if (!(found
& TLVFLAG_AUTH_INFO
)
999 || authentication_check(&tlvs
.auth_info
, &circuit
->passwd
,
1000 circuit
->rcv_stream
,
1002 isis_event_auth_failure(
1003 circuit
->area
->area_tag
,
1004 "LAN hello authentication failure",
1006 retval
= ISIS_WARNING
;
1011 if (!memcmp(hdr
.source_id
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
1012 zlog_warn("ISIS-Adj (%s): duplicate system ID on interface %s",
1013 circuit
->area
->area_tag
, circuit
->interface
->name
);
1014 return ISIS_WARNING
;
1018 * Accept the level 1 adjacency only if a match between local and
1019 * remote area addresses is found
1021 if (listcount(circuit
->area
->area_addrs
) == 0
1022 || (level
== IS_LEVEL_1
1023 && area_match(circuit
->area
->area_addrs
, tlvs
.area_addrs
)
1025 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
1027 "ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
1028 circuit
->area
->area_tag
, level
,
1029 circuit
->interface
->name
);
1036 * it's own IIH PDU - discard silently
1038 if (!memcmp(circuit
->u
.bc
.snpa
, ssnpa
, ETH_ALEN
)) {
1039 zlog_debug("ISIS-Adj (%s): it's own IIH PDU - discarded",
1040 circuit
->area
->area_tag
);
1047 * check if it's own interface ip match iih ip addrs
1049 if (found
& TLVFLAG_IPV4_ADDR
) {
1050 if (ip_match(circuit
->ip_addrs
, tlvs
.ipv4_addrs
))
1054 "ISIS-Adj: IPv4 addresses present but no overlap "
1055 "in LAN IIH from %s\n",
1056 circuit
->interface
->name
);
1058 if (found
& TLVFLAG_IPV6_ADDR
) {
1059 /* TBA: check that we have a linklocal ourselves? */
1060 struct listnode
*node
;
1061 struct in6_addr
*ip
;
1062 for (ALL_LIST_ELEMENTS_RO(tlvs
.ipv6_addrs
, node
, ip
))
1063 if (IN6_IS_ADDR_LINKLOCAL(ip
)) {
1070 "ISIS-Adj: IPv6 addresses present but no link-local "
1071 "in LAN IIH from %s\n",
1072 circuit
->interface
->name
);
1075 if (!(found
& (TLVFLAG_IPV4_ADDR
| TLVFLAG_IPV6_ADDR
)))
1077 "ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n",
1078 circuit
->interface
->name
);
1080 if (!v6_usable
&& !v4_usable
) {
1082 return ISIS_WARNING
;
1086 adj
= isis_adj_lookup(hdr
.source_id
, circuit
->u
.bc
.adjdb
[level
- 1]);
1087 if ((adj
== NULL
) || (memcmp(adj
->snpa
, ssnpa
, ETH_ALEN
))
1088 || (adj
->level
!= level
)) {
1093 adj
= isis_new_adj(hdr
.source_id
, ssnpa
, level
,
1096 retval
= ISIS_ERROR
;
1101 memcpy(adj
->snpa
, ssnpa
, 6);
1103 memset(adj
->snpa
, ' ', 6);
1107 isis_adj_state_change(adj
, ISIS_ADJ_INITIALIZING
, NULL
);
1109 if (level
== IS_LEVEL_1
)
1110 adj
->sys_type
= ISIS_SYSTYPE_L1_IS
;
1112 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
1113 list_delete_all_node(circuit
->u
.bc
.lan_neighs
[level
- 1]);
1114 isis_adj_build_neigh_list(circuit
->u
.bc
.adjdb
[level
- 1],
1115 circuit
->u
.bc
.lan_neighs
[level
- 1]);
1118 if (adj
->dis_record
[level
- 1].dis
== ISIS_IS_DIS
)
1121 if (memcmp(circuit
->u
.bc
.l1_desig_is
, hdr
.lan_id
,
1122 ISIS_SYS_ID_LEN
+ 1)) {
1123 thread_add_event(master
,
1124 isis_event_dis_status_change
,
1126 memcpy(&circuit
->u
.bc
.l1_desig_is
, hdr
.lan_id
,
1127 ISIS_SYS_ID_LEN
+ 1);
1131 if (memcmp(circuit
->u
.bc
.l2_desig_is
, hdr
.lan_id
,
1132 ISIS_SYS_ID_LEN
+ 1)) {
1133 thread_add_event(master
,
1134 isis_event_dis_status_change
,
1136 memcpy(&circuit
->u
.bc
.l2_desig_is
, hdr
.lan_id
,
1137 ISIS_SYS_ID_LEN
+ 1);
1142 adj
->hold_time
= hdr
.hold_time
;
1143 adj
->last_upd
= time(NULL
);
1144 adj
->prio
[level
- 1] = hdr
.prio
;
1146 memcpy(adj
->lanid
, hdr
.lan_id
, ISIS_SYS_ID_LEN
+ 1);
1148 tlvs_to_adj_area_addrs(&tlvs
, adj
);
1150 /* which protocol are spoken ??? */
1151 if (tlvs_to_adj_nlpids(&tlvs
, adj
)) {
1152 retval
= ISIS_WARNING
;
1156 /* we need to copy addresses to the adj */
1157 if (found
& TLVFLAG_IPV4_ADDR
)
1158 tlvs_to_adj_ipv4_addrs(&tlvs
, adj
);
1160 if (found
& TLVFLAG_IPV6_ADDR
)
1161 tlvs_to_adj_ipv6_addrs(&tlvs
, adj
);
1163 adj
->circuit_t
= hdr
.circuit_t
;
1165 /* lets take care of the expiry */
1166 THREAD_TIMER_OFF(adj
->t_expire
);
1167 THREAD_TIMER_ON(master
, adj
->t_expire
, isis_adj_expire
, adj
,
1168 (long)adj
->hold_time
);
1171 * If the snpa for this circuit is found from LAN Neighbours TLV
1172 * we have two-way communication -> adjacency can be put to state "up"
1175 if (found
& TLVFLAG_LAN_NEIGHS
) {
1176 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
1177 for (ALL_LIST_ELEMENTS_RO(tlvs
.lan_neighs
, node
,
1179 if (!memcmp(snpa
, circuit
->u
.bc
.snpa
,
1181 isis_adj_state_change(
1183 "own SNPA found in LAN Neighbours TLV");
1188 for (ALL_LIST_ELEMENTS_RO(tlvs
.lan_neighs
, node
, snpa
))
1189 if (!memcmp(snpa
, circuit
->u
.bc
.snpa
,
1195 isis_adj_state_change(
1196 adj
, ISIS_ADJ_INITIALIZING
,
1197 "own SNPA not found in LAN Neighbours TLV");
1199 } else if (adj
->adj_state
== ISIS_ADJ_UP
) {
1200 isis_adj_state_change(adj
, ISIS_ADJ_INITIALIZING
,
1201 "no LAN Neighbours TLV found");
1205 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
1207 "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, "
1208 "cirID %u, length %zd",
1209 circuit
->area
->area_tag
, level
, snpa_print(ssnpa
),
1210 circuit
->interface
->name
,
1211 circuit_t2string(circuit
->is_type
), circuit
->circuit_id
,
1212 stream_get_endp(circuit
->rcv_stream
));
1221 * Process Level 1/2 Link State
1223 * Section 7.3.15.1 - Action on receipt of a link state PDU
1225 static int process_lsp(int level
, struct isis_circuit
*circuit
,
1226 const u_char
*ssnpa
)
1228 struct isis_link_state_hdr
*hdr
;
1229 struct isis_adjacency
*adj
= NULL
;
1230 struct isis_lsp
*lsp
, *lsp0
= NULL
;
1231 int retval
= ISIS_OK
, comp
= 0;
1232 u_char lspid
[ISIS_SYS_ID_LEN
+ 2];
1233 struct isis_passwd
*passwd
;
1237 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
) {
1239 "ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u",
1240 circuit
->area
->area_tag
, level
,
1241 circuit
->interface
->name
,
1242 circuit_t2string(circuit
->is_type
),
1243 circuit
->circuit_id
);
1244 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
1245 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
1246 stream_get_endp(circuit
->rcv_stream
));
1249 if ((stream_get_endp(circuit
->rcv_stream
)
1250 - stream_get_getp(circuit
->rcv_stream
))
1251 < ISIS_LSP_HDR_LEN
) {
1252 zlog_warn("Packet too short");
1253 return ISIS_WARNING
;
1256 /* Reference the header */
1257 hdr
= (struct isis_link_state_hdr
*)STREAM_PNT(circuit
->rcv_stream
);
1258 pdu_len
= ntohs(hdr
->pdu_len
);
1260 /* lsp length check */
1261 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_LSP_HDR_LEN
)
1262 || pdu_len
> ISO_MTU(circuit
)
1263 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
1264 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %d",
1265 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1268 return ISIS_WARNING
;
1272 * Set the stream endp to PDU length, ignoring additional padding
1273 * introduced by transport chips.
1275 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
1276 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
1278 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
) {
1280 "ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04x, "
1281 "lifetime %us, len %u, on %s",
1282 circuit
->area
->area_tag
, level
,
1283 rawlspid_print(hdr
->lsp_id
), ntohl(hdr
->seq_num
),
1284 ntohs(hdr
->checksum
), ntohs(hdr
->rem_lifetime
), pdu_len
,
1285 circuit
->interface
->name
);
1288 /* lsp is_type check */
1289 if ((hdr
->lsp_bits
& IS_LEVEL_1_AND_2
) != IS_LEVEL_1
1290 && (hdr
->lsp_bits
& IS_LEVEL_1_AND_2
) != IS_LEVEL_1_AND_2
) {
1291 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP is type %x",
1292 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1294 /* continue as per RFC1122 Be liberal in what you accept, and
1295 * conservative in what you send */
1298 /* Checksum sanity check - FIXME: move to correct place */
1299 /* 12 = sysid+pdu+remtime */
1300 if (iso_csum_verify(STREAM_PNT(circuit
->rcv_stream
) + 4, pdu_len
- 12,
1302 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x",
1303 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1304 ntohs(hdr
->checksum
));
1306 return ISIS_WARNING
;
1309 /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
1310 if (circuit
->ext_domain
) {
1312 "ISIS-Upd (%s): LSP %s received at level %d over circuit with "
1313 "externalDomain = true",
1314 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1317 return ISIS_WARNING
;
1320 /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
1321 if (!accept_level(level
, circuit
->is_type
)) {
1323 "ISIS-Upd (%s): LSP %s received at level %d over circuit of"
1325 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1326 level
, circuit_t2string(circuit
->is_type
));
1328 return ISIS_WARNING
;
1331 /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
1333 /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use
1336 /* 7.3.15.1 a) 7 - password check */
1337 (level
== IS_LEVEL_1
) ? (passwd
= &circuit
->area
->area_passwd
)
1338 : (passwd
= &circuit
->area
->domain_passwd
);
1340 if (lsp_authentication_check(circuit
->rcv_stream
, circuit
->area
,
1342 isis_event_auth_failure(circuit
->area
->area_tag
,
1343 "LSP authentication failure",
1345 return ISIS_WARNING
;
1348 /* Find the LSP in our database and compare it to this Link State header
1350 lsp
= lsp_search(hdr
->lsp_id
, circuit
->area
->lspdb
[level
- 1]);
1352 comp
= lsp_compare(circuit
->area
->area_tag
, lsp
, hdr
->seq_num
,
1353 hdr
->checksum
, hdr
->rem_lifetime
);
1354 if (lsp
&& (lsp
->own_lsp
))
1357 /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same
1359 /* for broadcast circuits, snpa should be compared */
1361 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1362 adj
= isis_adj_lookup_snpa(ssnpa
,
1363 circuit
->u
.bc
.adjdb
[level
- 1]);
1366 "(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04x, "
1367 "lifetime %us on %s",
1368 circuit
->area
->area_tag
,
1369 rawlspid_print(hdr
->lsp_id
),
1370 ntohl(hdr
->seq_num
), ntohs(hdr
->checksum
),
1371 ntohs(hdr
->rem_lifetime
),
1372 circuit
->interface
->name
);
1373 return ISIS_WARNING
; /* Silently discard */
1376 /* for non broadcast, we just need to find same level adj */
1378 /* If no adj, or no sharing of level */
1379 if (!circuit
->u
.p2p
.neighbor
) {
1380 return ISIS_OK
; /* Silently discard */
1382 if (((level
== IS_LEVEL_1
)
1383 && (circuit
->u
.p2p
.neighbor
->adj_usage
1384 == ISIS_ADJ_LEVEL2
))
1385 || ((level
== IS_LEVEL_2
)
1386 && (circuit
->u
.p2p
.neighbor
->adj_usage
1387 == ISIS_ADJ_LEVEL1
)))
1388 return ISIS_WARNING
; /* Silently discard */
1393 /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */
1395 /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */
1397 /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do
1400 /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num
1402 * wrong checksum, initiate a purge. */
1403 if (lsp
&& (lsp
->lsp_header
->seq_num
== hdr
->seq_num
)
1404 && (lsp
->lsp_header
->checksum
!= hdr
->checksum
)) {
1406 "ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
1407 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1408 ntohl(hdr
->seq_num
));
1409 hdr
->rem_lifetime
= 0;
1414 /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */
1415 if (hdr
->rem_lifetime
== 0) {
1417 /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't
1419 /* only needed on explicit update, eg - p2p */
1420 if (circuit
->circ_type
== CIRCUIT_T_P2P
)
1421 ack_lsp(hdr
, circuit
, level
);
1422 return retval
; /* FIXME: do we need a purge? */
1424 if (memcmp(hdr
->lsp_id
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
1425 /* LSP by some other system -> do 7.3.16.4 b) */
1426 /* 7.3.16.4 b) 1) */
1427 if (comp
== LSP_NEWER
) {
1428 lsp_update(lsp
, circuit
->rcv_stream
,
1429 circuit
->area
, level
);
1431 lsp_set_all_srmflags(lsp
);
1433 ISIS_FLAGS_CLEAR_ALL(
1435 ->SSNflags
); /* FIXME:
1440 /* For the case of lsp confusion, flood
1441 * the purge back to its
1442 * originator so that it can react.
1443 * Otherwise, don't reflood
1444 * through incoming circuit as usual */
1445 if (!lsp_confusion
) {
1447 ISIS_CLEAR_FLAG(lsp
->SRMflags
,
1450 if (circuit
->circ_type
1451 != CIRCUIT_T_BROADCAST
)
1456 } /* 7.3.16.4 b) 2) */
1457 else if (comp
== LSP_EQUAL
) {
1459 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1461 if (circuit
->circ_type
1462 != CIRCUIT_T_BROADCAST
)
1463 ISIS_SET_FLAG(lsp
->SSNflags
,
1465 } /* 7.3.16.4 b) 3) */
1467 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1468 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1470 } else if (lsp
->lsp_header
->rem_lifetime
!= 0) {
1471 /* our own LSP -> 7.3.16.4 c) */
1472 if (comp
== LSP_NEWER
) {
1474 ntohl(hdr
->seq_num
));
1475 lsp_set_all_srmflags(lsp
);
1477 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1478 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1480 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
)
1482 "ISIS-Upd (%s): (1) re-originating LSP %s new "
1484 circuit
->area
->area_tag
,
1485 rawlspid_print(hdr
->lsp_id
),
1486 ntohl(lsp
->lsp_header
1492 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1494 if (memcmp(hdr
->lsp_id
, isis
->sysid
, ISIS_SYS_ID_LEN
) == 0) {
1496 /* 7.3.16.4: initiate a purge */
1497 lsp_purge_non_exist(level
, hdr
, circuit
->area
);
1500 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1502 /* In 7.3.16.1, If an Intermediate system R somewhere in the
1504 * has information that the current sequence number for source S
1506 * "greater" than that held by S, ... */
1508 if (ntohl(hdr
->seq_num
) > ntohl(lsp
->lsp_header
->seq_num
)) {
1510 lsp_inc_seqnum(lsp
, ntohl(hdr
->seq_num
));
1511 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
)
1513 "ISIS-Upd (%s): (2) re-originating LSP %s new seq "
1515 circuit
->area
->area_tag
,
1516 rawlspid_print(hdr
->lsp_id
),
1517 ntohl(lsp
->lsp_header
->seq_num
));
1519 /* If the received LSP is older or equal,
1520 * resend the LSP which will act as ACK */
1521 lsp_set_all_srmflags(lsp
);
1523 /* 7.3.15.1 e) - This lsp originated on another system */
1525 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db
1527 if ((!lsp
|| comp
== LSP_NEWER
)) {
1529 * If this lsp is a frag, need to see if we have zero
1532 if (LSP_FRAGMENT(hdr
->lsp_id
) != 0) {
1533 memcpy(lspid
, hdr
->lsp_id
, ISIS_SYS_ID_LEN
+ 1);
1534 LSP_FRAGMENT(lspid
) = 0;
1536 lspid
, circuit
->area
->lspdb
[level
- 1]);
1539 "Got lsp frag, while zero lsp not in database");
1545 lsp
= lsp_new_from_stream_ptr(
1546 circuit
->rcv_stream
, pdu_len
, lsp0
,
1547 circuit
->area
, level
);
1549 circuit
->area
->lspdb
[level
- 1]);
1550 } else /* exists, so we overwrite */
1552 lsp_update(lsp
, circuit
->rcv_stream
,
1553 circuit
->area
, level
);
1556 lsp_set_all_srmflags(lsp
);
1558 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1561 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
)
1562 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1565 /* 7.3.15.1 e) 2) LSP equal to the one in db */
1566 else if (comp
== LSP_EQUAL
) {
1567 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1568 lsp_update(lsp
, circuit
->rcv_stream
, circuit
->area
,
1570 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
)
1571 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1573 /* 7.3.15.1 e) 3) LSP older than the one in db */
1575 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1576 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1583 * Process Sequence Numbers
1585 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1588 static int process_snp(int snp_type
, int level
, struct isis_circuit
*circuit
,
1589 const u_char
*ssnpa
)
1591 int retval
= ISIS_OK
;
1593 char typechar
= ' ';
1595 struct isis_adjacency
*adj
;
1596 struct isis_complete_seqnum_hdr
*chdr
= NULL
;
1597 struct isis_partial_seqnum_hdr
*phdr
= NULL
;
1598 uint32_t found
= 0, expected
= 0, auth_tlv_offset
= 0;
1599 struct isis_lsp
*lsp
;
1600 struct lsp_entry
*entry
;
1601 struct listnode
*node
, *nnode
;
1602 struct listnode
*node2
, *nnode2
;
1604 struct list
*lsp_list
= NULL
;
1605 struct isis_passwd
*passwd
;
1607 if (snp_type
== ISIS_SNP_CSNP_FLAG
) {
1608 /* getting the header info */
1610 chdr
= (struct isis_complete_seqnum_hdr
*)STREAM_PNT(
1611 circuit
->rcv_stream
);
1612 stream_forward_getp(circuit
->rcv_stream
, ISIS_CSNP_HDRLEN
);
1613 pdu_len
= ntohs(chdr
->pdu_len
);
1614 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_CSNP_HDRLEN
)
1615 || pdu_len
> ISO_MTU(circuit
)
1616 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
1617 zlog_warn("Received a CSNP with bogus length %d",
1619 return ISIS_WARNING
;
1623 phdr
= (struct isis_partial_seqnum_hdr
*)STREAM_PNT(
1624 circuit
->rcv_stream
);
1625 stream_forward_getp(circuit
->rcv_stream
, ISIS_PSNP_HDRLEN
);
1626 pdu_len
= ntohs(phdr
->pdu_len
);
1627 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_PSNP_HDRLEN
)
1628 || pdu_len
> ISO_MTU(circuit
)
1629 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
1630 zlog_warn("Received a PSNP with bogus length %d",
1632 return ISIS_WARNING
;
1637 * Set the stream endp to PDU length, ignoring additional padding
1638 * introduced by transport chips.
1640 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
1641 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
1643 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
1644 if (circuit
->ext_domain
) {
1647 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1648 "skipping: circuit externalDomain = true",
1649 circuit
->area
->area_tag
, level
, typechar
,
1650 circuit
->interface
->name
);
1655 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
1656 if (!accept_level(level
, circuit
->is_type
)) {
1659 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1660 "skipping: circuit type %s does not match level %d",
1661 circuit
->area
->area_tag
, level
, typechar
,
1662 circuit
->interface
->name
,
1663 circuit_t2string(circuit
->is_type
), level
);
1668 /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */
1669 if ((snp_type
== ISIS_SNP_PSNP_FLAG
)
1670 && (circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
1671 && (!circuit
->u
.bc
.is_dr
[level
- 1])) {
1673 "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, "
1674 "skipping: we are not the DIS",
1675 circuit
->area
->area_tag
, level
, typechar
,
1676 snpa_print(ssnpa
), circuit
->interface
->name
);
1681 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked
1684 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use
1686 * - already checked */
1688 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same
1690 /* for broadcast circuits, snpa should be compared */
1691 /* FIXME : Do we need to check SNPA? */
1692 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1693 if (snp_type
== ISIS_SNP_CSNP_FLAG
) {
1694 adj
= isis_adj_lookup(chdr
->source_id
,
1695 circuit
->u
.bc
.adjdb
[level
- 1]);
1697 /* a psnp on a broadcast, how lovely of Juniper :) */
1698 adj
= isis_adj_lookup(phdr
->source_id
,
1699 circuit
->u
.bc
.adjdb
[level
- 1]);
1702 return ISIS_OK
; /* Silently discard */
1704 if (!circuit
->u
.p2p
.neighbor
) {
1705 zlog_warn("no p2p neighbor on circuit %s",
1706 circuit
->interface
->name
);
1707 return ISIS_OK
; /* Silently discard */
1711 /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */
1713 /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */
1715 memset(&tlvs
, 0, sizeof(struct tlvs
));
1718 expected
|= TLVFLAG_LSP_ENTRIES
;
1719 expected
|= TLVFLAG_AUTH_INFO
;
1721 auth_tlv_offset
= stream_get_getp(circuit
->rcv_stream
);
1722 retval
= parse_tlvs(circuit
->area
->area_tag
,
1723 STREAM_PNT(circuit
->rcv_stream
),
1724 pdu_len
- stream_get_getp(circuit
->rcv_stream
),
1725 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
1727 if (retval
> ISIS_WARNING
) {
1728 zlog_warn("something went very wrong processing SNP");
1733 if (level
== IS_LEVEL_1
)
1734 passwd
= &circuit
->area
->area_passwd
;
1736 passwd
= &circuit
->area
->domain_passwd
;
1738 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_RECV
)) {
1740 if (!(found
& TLVFLAG_AUTH_INFO
)
1741 || authentication_check(&tlvs
.auth_info
, passwd
,
1742 circuit
->rcv_stream
,
1744 isis_event_auth_failure(circuit
->area
->area_tag
,
1745 "SNP authentication"
1747 phdr
? phdr
->source_id
1755 /* debug isis snp-packets */
1756 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
1757 zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
1758 circuit
->area
->area_tag
, level
, typechar
,
1759 snpa_print(ssnpa
), circuit
->interface
->name
);
1760 if (tlvs
.lsp_entries
) {
1761 for (ALL_LIST_ELEMENTS_RO(tlvs
.lsp_entries
, node
,
1764 "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x,"
1765 " cksum 0x%04x, lifetime %us",
1766 circuit
->area
->area_tag
, typechar
,
1767 rawlspid_print(entry
->lsp_id
),
1768 ntohl(entry
->seq_num
),
1769 ntohs(entry
->checksum
),
1770 ntohs(entry
->rem_lifetime
));
1775 /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
1776 if (tlvs
.lsp_entries
) {
1777 for (ALL_LIST_ELEMENTS_RO(tlvs
.lsp_entries
, node
, entry
)) {
1778 lsp
= lsp_search(entry
->lsp_id
,
1779 circuit
->area
->lspdb
[level
- 1]);
1780 own_lsp
= !memcmp(entry
->lsp_id
, isis
->sysid
,
1783 /* 7.3.15.2 b) 1) is this LSP newer */
1784 cmp
= lsp_compare(circuit
->area
->area_tag
, lsp
,
1787 entry
->rem_lifetime
);
1788 /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p
1790 if (cmp
== LSP_EQUAL
) {
1791 /* if (circuit->circ_type !=
1792 * CIRCUIT_T_BROADCAST) */
1793 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1795 /* 7.3.15.2 b) 3) if it is older, clear SSN and
1797 else if (cmp
== LSP_OLDER
) {
1798 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1799 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1801 /* 7.3.15.2 b) 4) if it is newer, set SSN and
1807 ntohl(entry
->seq_num
));
1808 ISIS_SET_FLAG(lsp
->SRMflags
,
1811 ISIS_SET_FLAG(lsp
->SSNflags
,
1813 /* if (circuit->circ_type !=
1814 * CIRCUIT_T_BROADCAST) */
1815 ISIS_CLEAR_FLAG(lsp
->SRMflags
,
1820 /* 7.3.15.2 b) 5) if it was not found, and all
1821 * of those are not 0,
1822 * insert it and set SSN on it */
1823 if (entry
->rem_lifetime
&& entry
->checksum
1825 && memcmp(entry
->lsp_id
, isis
->sysid
,
1827 struct isis_lsp
*lsp0
= NULL
;
1829 if (LSP_FRAGMENT(entry
->lsp_id
)) {
1830 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
1832 memcpy(lspid
, entry
->lsp_id
,
1833 ISIS_SYS_ID_LEN
+ 1);
1834 LSP_FRAGMENT(lspid
) = 0;
1837 circuit
->area
->lspdb
[level
- 1]);
1839 zlog_debug("Got lsp frag in snp, while zero not in database");
1844 circuit
->area
, entry
->lsp_id
,
1845 ntohs(entry
->rem_lifetime
), 0,
1846 0, entry
->checksum
, lsp0
, level
);
1849 ->lspdb
[level
- 1]);
1850 ISIS_FLAGS_CLEAR_ALL(lsp
->SRMflags
);
1851 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1857 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported
1859 if (snp_type
== ISIS_SNP_CSNP_FLAG
) {
1861 * Build a list from our own LSP db bounded with
1862 * start_lsp_id and stop_lsp_id
1864 lsp_list
= list_new();
1865 lsp_build_list_nonzero_ht(chdr
->start_lsp_id
, chdr
->stop_lsp_id
,
1867 circuit
->area
->lspdb
[level
- 1]);
1869 /* Fixme: Find a better solution */
1870 if (tlvs
.lsp_entries
) {
1871 for (ALL_LIST_ELEMENTS(tlvs
.lsp_entries
, node
, nnode
,
1873 for (ALL_LIST_ELEMENTS(lsp_list
, node2
, nnode2
,
1875 if (lsp_id_cmp(lsp
->lsp_header
->lsp_id
,
1878 list_delete_node(lsp_list
,
1885 /* on remaining LSPs we set SRM (neighbor knew not of) */
1886 for (ALL_LIST_ELEMENTS_RO(lsp_list
, node
, lsp
))
1887 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1889 list_delete(lsp_list
);
1896 static int process_csnp(int level
, struct isis_circuit
*circuit
,
1897 const u_char
*ssnpa
)
1899 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
1901 "ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u",
1902 circuit
->area
->area_tag
, level
,
1903 circuit
->interface
->name
,
1904 circuit_t2string(circuit
->is_type
),
1905 circuit
->circuit_id
);
1906 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
1907 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
1908 stream_get_endp(circuit
->rcv_stream
));
1911 /* Sanity check - FIXME: move to correct place */
1912 if ((stream_get_endp(circuit
->rcv_stream
)
1913 - stream_get_getp(circuit
->rcv_stream
))
1914 < ISIS_CSNP_HDRLEN
) {
1915 zlog_warn("Packet too short ( < %d)", ISIS_CSNP_HDRLEN
);
1916 return ISIS_WARNING
;
1919 return process_snp(ISIS_SNP_CSNP_FLAG
, level
, circuit
, ssnpa
);
1922 static int process_psnp(int level
, struct isis_circuit
*circuit
,
1923 const u_char
*ssnpa
)
1925 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
1927 "ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u",
1928 circuit
->area
->area_tag
, level
,
1929 circuit
->interface
->name
,
1930 circuit_t2string(circuit
->is_type
),
1931 circuit
->circuit_id
);
1932 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
1933 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
1934 stream_get_endp(circuit
->rcv_stream
));
1937 if ((stream_get_endp(circuit
->rcv_stream
)
1938 - stream_get_getp(circuit
->rcv_stream
))
1939 < ISIS_PSNP_HDRLEN
) {
1940 zlog_warn("Packet too short ( < %d)", ISIS_PSNP_HDRLEN
);
1941 return ISIS_WARNING
;
1944 return process_snp(ISIS_SNP_PSNP_FLAG
, level
, circuit
, ssnpa
);
1951 static int isis_handle_pdu(struct isis_circuit
*circuit
, u_char
*ssnpa
)
1953 struct isis_fixed_hdr
*hdr
;
1955 int retval
= ISIS_OK
;
1958 * Let's first read data from stream to the header
1960 hdr
= (struct isis_fixed_hdr
*)STREAM_DATA(circuit
->rcv_stream
);
1962 if ((hdr
->idrp
!= ISO10589_ISIS
) && (hdr
->idrp
!= ISO9542_ESIS
)) {
1963 zlog_err("Not an IS-IS or ES-IS packet IDRP=%02x", hdr
->idrp
);
1967 /* now we need to know if this is an ISO 9542 packet and
1968 * take real good care of it, waaa!
1970 if (hdr
->idrp
== ISO9542_ESIS
) {
1971 zlog_err("No support for ES-IS packet IDRP=%02x", hdr
->idrp
);
1974 stream_set_getp(circuit
->rcv_stream
, ISIS_FIXED_HDR_LEN
);
1977 * and then process it
1980 if (hdr
->length
< ISIS_MINIMUM_FIXED_HDR_LEN
) {
1981 zlog_err("Fixed header length = %d", hdr
->length
);
1985 if (hdr
->version1
!= 1) {
1986 zlog_warn("Unsupported ISIS version %u", hdr
->version1
);
1987 return ISIS_WARNING
;
1990 if ((hdr
->id_len
!= 0) && (hdr
->id_len
!= ISIS_SYS_ID_LEN
)) {
1992 "IDFieldLengthMismatch: ID Length field in a received PDU %u, "
1993 "while the parameter for this IS is %u",
1994 hdr
->id_len
, ISIS_SYS_ID_LEN
);
1998 if (hdr
->version2
!= 1) {
1999 zlog_warn("Unsupported ISIS version %u", hdr
->version2
);
2000 return ISIS_WARNING
;
2003 if (circuit
->is_passive
) {
2004 zlog_warn("Received ISIS PDU on passive circuit %s",
2005 circuit
->interface
->name
);
2006 return ISIS_WARNING
;
2010 if ((hdr
->max_area_addrs
!= 0)
2011 && (hdr
->max_area_addrs
!= isis
->max_area_addrs
)) {
2013 "maximumAreaAddressesMismatch: maximumAreaAdresses in a "
2014 "received PDU %u while the parameter for this IS is %u",
2015 hdr
->max_area_addrs
, isis
->max_area_addrs
);
2019 switch (hdr
->pdu_type
) {
2021 retval
= process_lan_hello(ISIS_LEVEL1
, circuit
, ssnpa
);
2024 retval
= process_lan_hello(ISIS_LEVEL2
, circuit
, ssnpa
);
2027 retval
= process_p2p_hello(circuit
);
2030 retval
= process_lsp(ISIS_LEVEL1
, circuit
, ssnpa
);
2033 retval
= process_lsp(ISIS_LEVEL2
, circuit
, ssnpa
);
2035 case L1_COMPLETE_SEQ_NUM
:
2036 retval
= process_csnp(ISIS_LEVEL1
, circuit
, ssnpa
);
2038 case L2_COMPLETE_SEQ_NUM
:
2039 retval
= process_csnp(ISIS_LEVEL2
, circuit
, ssnpa
);
2041 case L1_PARTIAL_SEQ_NUM
:
2042 retval
= process_psnp(ISIS_LEVEL1
, circuit
, ssnpa
);
2044 case L2_PARTIAL_SEQ_NUM
:
2045 retval
= process_psnp(ISIS_LEVEL2
, circuit
, ssnpa
);
2054 int isis_receive(struct thread
*thread
)
2056 struct isis_circuit
*circuit
;
2057 u_char ssnpa
[ETH_ALEN
];
2063 circuit
= THREAD_ARG(thread
);
2066 circuit
->t_read
= NULL
;
2068 isis_circuit_stream(circuit
, &circuit
->rcv_stream
);
2070 retval
= circuit
->rx(circuit
, ssnpa
);
2072 if (retval
== ISIS_OK
)
2073 retval
= isis_handle_pdu(circuit
, ssnpa
);
2076 * prepare for next packet.
2078 if (!circuit
->is_passive
)
2079 isis_circuit_prepare(circuit
);
2084 /* filling of the fixed isis header */
2085 void fill_fixed_hdr(struct isis_fixed_hdr
*hdr
, u_char pdu_type
)
2087 memset(hdr
, 0, sizeof(struct isis_fixed_hdr
));
2089 hdr
->idrp
= ISO10589_ISIS
;
2094 hdr
->length
= ISIS_LANHELLO_HDRLEN
;
2097 hdr
->length
= ISIS_P2PHELLO_HDRLEN
;
2101 hdr
->length
= ISIS_LSP_HDR_LEN
;
2103 case L1_COMPLETE_SEQ_NUM
:
2104 case L2_COMPLETE_SEQ_NUM
:
2105 hdr
->length
= ISIS_CSNP_HDRLEN
;
2107 case L1_PARTIAL_SEQ_NUM
:
2108 case L2_PARTIAL_SEQ_NUM
:
2109 hdr
->length
= ISIS_PSNP_HDRLEN
;
2112 zlog_warn("fill_fixed_hdr(): unknown pdu type %d", pdu_type
);
2115 hdr
->length
+= ISIS_FIXED_HDR_LEN
;
2116 hdr
->pdu_type
= pdu_type
;
2118 hdr
->id_len
= 0; /* ISIS_SYS_ID_LEN - 0==6 */
2120 hdr
->max_area_addrs
= 0; /* isis->max_area_addrs - 0==3 */
2126 static void fill_fixed_hdr_andstream(struct isis_fixed_hdr
*hdr
,
2127 u_char pdu_type
, struct stream
*stream
)
2129 fill_fixed_hdr(hdr
, pdu_type
);
2131 stream_putc(stream
, hdr
->idrp
);
2132 stream_putc(stream
, hdr
->length
);
2133 stream_putc(stream
, hdr
->version1
);
2134 stream_putc(stream
, hdr
->id_len
);
2135 stream_putc(stream
, hdr
->pdu_type
);
2136 stream_putc(stream
, hdr
->version2
);
2137 stream_putc(stream
, hdr
->reserved
);
2138 stream_putc(stream
, hdr
->max_area_addrs
);
2143 int send_hello(struct isis_circuit
*circuit
, int level
)
2145 struct isis_fixed_hdr fixed_hdr
;
2146 struct isis_lan_hello_hdr hello_hdr
;
2147 struct isis_p2p_hello_hdr p2p_hello_hdr
;
2148 unsigned char hmac_md5_hash
[ISIS_AUTH_MD5_SIZE
];
2149 size_t len_pointer
, length
, auth_tlv_offset
= 0;
2153 if (circuit
->is_passive
)
2156 if (circuit
->interface
->mtu
== 0) {
2157 zlog_warn("circuit has zero MTU");
2158 return ISIS_WARNING
;
2161 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2163 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
2164 if (level
== IS_LEVEL_1
)
2165 fill_fixed_hdr_andstream(&fixed_hdr
, L1_LAN_HELLO
,
2166 circuit
->snd_stream
);
2168 fill_fixed_hdr_andstream(&fixed_hdr
, L2_LAN_HELLO
,
2169 circuit
->snd_stream
);
2171 fill_fixed_hdr_andstream(&fixed_hdr
, P2P_HELLO
,
2172 circuit
->snd_stream
);
2175 * Fill LAN Level 1 or 2 Hello PDU header
2177 memset(&hello_hdr
, 0, sizeof(struct isis_lan_hello_hdr
));
2178 interval
= circuit
->hello_multiplier
[level
- 1]
2179 * circuit
->hello_interval
[level
- 1];
2180 if (interval
> USHRT_MAX
)
2181 interval
= USHRT_MAX
;
2182 hello_hdr
.circuit_t
= circuit
->is_type
;
2183 memcpy(hello_hdr
.source_id
, isis
->sysid
, ISIS_SYS_ID_LEN
);
2184 hello_hdr
.hold_time
= htons((u_int16_t
)interval
);
2186 hello_hdr
.pdu_len
= 0; /* Update the PDU Length later */
2188 stream_get_endp(circuit
->snd_stream
) + 3 + ISIS_SYS_ID_LEN
;
2190 /* copy the shared part of the hello to the p2p hello if needed */
2191 if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
2192 memcpy(&p2p_hello_hdr
, &hello_hdr
, 5 + ISIS_SYS_ID_LEN
);
2193 p2p_hello_hdr
.local_id
= circuit
->circuit_id
;
2194 /* FIXME: need better understanding */
2195 stream_put(circuit
->snd_stream
, &p2p_hello_hdr
,
2196 ISIS_P2PHELLO_HDRLEN
);
2198 hello_hdr
.prio
= circuit
->priority
[level
- 1];
2199 if (level
== IS_LEVEL_1
) {
2200 memcpy(hello_hdr
.lan_id
, circuit
->u
.bc
.l1_desig_is
,
2201 ISIS_SYS_ID_LEN
+ 1);
2202 } else if (level
== IS_LEVEL_2
) {
2203 memcpy(hello_hdr
.lan_id
, circuit
->u
.bc
.l2_desig_is
,
2204 ISIS_SYS_ID_LEN
+ 1);
2206 stream_put(circuit
->snd_stream
, &hello_hdr
,
2207 ISIS_LANHELLO_HDRLEN
);
2211 * Then the variable length part.
2214 /* add circuit password */
2215 switch (circuit
->passwd
.type
) {
2217 case ISIS_PASSWD_TYPE_CLEARTXT
:
2218 if (tlv_add_authinfo(circuit
->passwd
.type
, circuit
->passwd
.len
,
2219 circuit
->passwd
.passwd
,
2220 circuit
->snd_stream
))
2221 return ISIS_WARNING
;
2225 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2226 /* Remember where TLV is written so we can later overwrite the
2228 auth_tlv_offset
= stream_get_endp(circuit
->snd_stream
);
2229 memset(&hmac_md5_hash
, 0, ISIS_AUTH_MD5_SIZE
);
2230 if (tlv_add_authinfo(circuit
->passwd
.type
, ISIS_AUTH_MD5_SIZE
,
2231 hmac_md5_hash
, circuit
->snd_stream
))
2232 return ISIS_WARNING
;
2239 /* Area Addresses TLV */
2240 if (listcount(circuit
->area
->area_addrs
) == 0)
2241 return ISIS_WARNING
;
2242 if (tlv_add_area_addrs(circuit
->area
->area_addrs
, circuit
->snd_stream
))
2243 return ISIS_WARNING
;
2245 /* LAN Neighbors TLV */
2246 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
2247 if (level
== IS_LEVEL_1
&& circuit
->u
.bc
.lan_neighs
[0]
2248 && listcount(circuit
->u
.bc
.lan_neighs
[0]) > 0)
2249 if (tlv_add_lan_neighs(circuit
->u
.bc
.lan_neighs
[0],
2250 circuit
->snd_stream
))
2251 return ISIS_WARNING
;
2252 if (level
== IS_LEVEL_2
&& circuit
->u
.bc
.lan_neighs
[1]
2253 && listcount(circuit
->u
.bc
.lan_neighs
[1]) > 0)
2254 if (tlv_add_lan_neighs(circuit
->u
.bc
.lan_neighs
[1],
2255 circuit
->snd_stream
))
2256 return ISIS_WARNING
;
2259 /* Protocols Supported TLV */
2260 if (circuit
->nlpids
.count
> 0)
2261 if (tlv_add_nlpid(&circuit
->nlpids
, circuit
->snd_stream
))
2262 return ISIS_WARNING
;
2263 /* IP interface Address TLV */
2264 if (circuit
->ip_router
&& circuit
->ip_addrs
2265 && listcount(circuit
->ip_addrs
) > 0)
2266 if (tlv_add_ip_addrs(circuit
->ip_addrs
, circuit
->snd_stream
))
2267 return ISIS_WARNING
;
2269 /* IPv6 Interface Address TLV */
2270 if (circuit
->ipv6_router
&& circuit
->ipv6_link
2271 && listcount(circuit
->ipv6_link
) > 0)
2272 if (tlv_add_ipv6_addrs(circuit
->ipv6_link
, circuit
->snd_stream
))
2273 return ISIS_WARNING
;
2275 if (circuit
->pad_hellos
)
2276 if (tlv_add_padding(circuit
->snd_stream
))
2277 return ISIS_WARNING
;
2279 length
= stream_get_endp(circuit
->snd_stream
);
2280 /* Update PDU length */
2281 stream_putw_at(circuit
->snd_stream
, len_pointer
, (u_int16_t
)length
);
2283 /* For HMAC MD5 we need to compute the md5 hash and store it */
2284 if (circuit
->passwd
.type
== ISIS_PASSWD_TYPE_HMAC_MD5
) {
2285 hmac_md5(STREAM_DATA(circuit
->snd_stream
),
2286 stream_get_endp(circuit
->snd_stream
),
2287 (unsigned char *)&circuit
->passwd
.passwd
,
2288 circuit
->passwd
.len
, (unsigned char *)&hmac_md5_hash
);
2289 /* Copy the hash into the stream */
2290 memcpy(STREAM_DATA(circuit
->snd_stream
) + auth_tlv_offset
+ 3,
2291 hmac_md5_hash
, ISIS_AUTH_MD5_SIZE
);
2294 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
2295 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
2297 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
2298 circuit
->area
->area_tag
, level
,
2299 circuit
->interface
->name
, length
);
2302 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
2303 circuit
->area
->area_tag
,
2304 circuit
->interface
->name
, length
);
2306 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2307 zlog_dump_data(STREAM_DATA(circuit
->snd_stream
),
2308 stream_get_endp(circuit
->snd_stream
));
2311 retval
= circuit
->tx(circuit
, level
);
2312 if (retval
!= ISIS_OK
)
2313 zlog_err("ISIS-Adj (%s): Send L%d IIH on %s failed",
2314 circuit
->area
->area_tag
, level
,
2315 circuit
->interface
->name
);
2320 int send_lan_l1_hello(struct thread
*thread
)
2322 struct isis_circuit
*circuit
;
2325 circuit
= THREAD_ARG(thread
);
2327 circuit
->u
.bc
.t_send_lan_hello
[0] = NULL
;
2329 if (!(circuit
->area
->is_type
& IS_LEVEL_1
)) {
2331 "ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
2332 circuit
->area
->area_tag
);
2336 if (circuit
->u
.bc
.run_dr_elect
[0])
2337 isis_dr_elect(circuit
, 1);
2339 retval
= send_hello(circuit
, 1);
2341 /* set next timer thread */
2342 THREAD_TIMER_ON(master
, circuit
->u
.bc
.t_send_lan_hello
[0],
2343 send_lan_l1_hello
, circuit
,
2344 isis_jitter(circuit
->hello_interval
[0], IIH_JITTER
));
2349 int send_lan_l2_hello(struct thread
*thread
)
2351 struct isis_circuit
*circuit
;
2354 circuit
= THREAD_ARG(thread
);
2356 circuit
->u
.bc
.t_send_lan_hello
[1] = NULL
;
2358 if (!(circuit
->area
->is_type
& IS_LEVEL_2
)) {
2359 zlog_warn("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
2360 circuit
->area
->area_tag
);
2364 if (circuit
->u
.bc
.run_dr_elect
[1])
2365 isis_dr_elect(circuit
, 2);
2367 retval
= send_hello(circuit
, 2);
2369 /* set next timer thread */
2370 THREAD_TIMER_ON(master
, circuit
->u
.bc
.t_send_lan_hello
[1],
2371 send_lan_l2_hello
, circuit
,
2372 isis_jitter(circuit
->hello_interval
[1], IIH_JITTER
));
2377 int send_p2p_hello(struct thread
*thread
)
2379 struct isis_circuit
*circuit
;
2381 circuit
= THREAD_ARG(thread
);
2383 circuit
->u
.p2p
.t_send_p2p_hello
= NULL
;
2385 send_hello(circuit
, 1);
2387 /* set next timer thread */
2388 THREAD_TIMER_ON(master
, circuit
->u
.p2p
.t_send_p2p_hello
, send_p2p_hello
,
2390 isis_jitter(circuit
->hello_interval
[1], IIH_JITTER
));
2395 static int build_csnp(int level
, u_char
*start
, u_char
*stop
, struct list
*lsps
,
2396 struct isis_circuit
*circuit
)
2398 struct isis_fixed_hdr fixed_hdr
;
2399 struct isis_passwd
*passwd
;
2402 unsigned char hmac_md5_hash
[ISIS_AUTH_MD5_SIZE
];
2403 unsigned long auth_tlv_offset
= 0;
2404 int retval
= ISIS_OK
;
2406 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2408 if (level
== IS_LEVEL_1
)
2409 fill_fixed_hdr_andstream(&fixed_hdr
, L1_COMPLETE_SEQ_NUM
,
2410 circuit
->snd_stream
);
2412 fill_fixed_hdr_andstream(&fixed_hdr
, L2_COMPLETE_SEQ_NUM
,
2413 circuit
->snd_stream
);
2416 * Fill Level 1 or 2 Complete Sequence Numbers header
2419 lenp
= stream_get_endp(circuit
->snd_stream
);
2420 stream_putw(circuit
->snd_stream
, 0); /* PDU length - when we know it */
2421 /* no need to send the source here, it is always us if we csnp */
2422 stream_put(circuit
->snd_stream
, isis
->sysid
, ISIS_SYS_ID_LEN
);
2423 /* with zero circuit id - ref 9.10, 9.11 */
2424 stream_putc(circuit
->snd_stream
, 0x00);
2426 stream_put(circuit
->snd_stream
, start
, ISIS_SYS_ID_LEN
+ 2);
2427 stream_put(circuit
->snd_stream
, stop
, ISIS_SYS_ID_LEN
+ 2);
2432 if (level
== IS_LEVEL_1
)
2433 passwd
= &circuit
->area
->area_passwd
;
2435 passwd
= &circuit
->area
->domain_passwd
;
2437 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)) {
2438 switch (passwd
->type
) {
2440 case ISIS_PASSWD_TYPE_CLEARTXT
:
2441 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT
,
2442 passwd
->len
, passwd
->passwd
,
2443 circuit
->snd_stream
))
2444 return ISIS_WARNING
;
2448 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2449 /* Remember where TLV is written so we can later
2450 * overwrite the MD5 hash */
2451 auth_tlv_offset
= stream_get_endp(circuit
->snd_stream
);
2452 memset(&hmac_md5_hash
, 0, ISIS_AUTH_MD5_SIZE
);
2453 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5
,
2454 ISIS_AUTH_MD5_SIZE
, hmac_md5_hash
,
2455 circuit
->snd_stream
))
2456 return ISIS_WARNING
;
2464 retval
= tlv_add_lsp_entries(lsps
, circuit
->snd_stream
);
2465 if (retval
!= ISIS_OK
)
2468 length
= (u_int16_t
)stream_get_endp(circuit
->snd_stream
);
2469 /* Update PU length */
2470 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
2472 /* For HMAC MD5 we need to compute the md5 hash and store it */
2473 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)
2474 && passwd
->type
== ISIS_PASSWD_TYPE_HMAC_MD5
) {
2475 hmac_md5(STREAM_DATA(circuit
->snd_stream
),
2476 stream_get_endp(circuit
->snd_stream
),
2477 (unsigned char *)&passwd
->passwd
, passwd
->len
,
2478 (unsigned char *)&hmac_md5_hash
);
2479 /* Copy the hash into the stream */
2480 memcpy(STREAM_DATA(circuit
->snd_stream
) + auth_tlv_offset
+ 3,
2481 hmac_md5_hash
, ISIS_AUTH_MD5_SIZE
);
2488 * Count the maximum number of lsps that can be accomodated by a given size.
2490 static uint16_t get_max_lsp_count(uint16_t size
)
2494 uint16_t remaining_size
;
2496 /* First count the full size TLVs */
2497 tlv_count
= size
/ MAX_LSP_ENTRIES_TLV_SIZE
;
2498 lsp_count
= tlv_count
* (MAX_LSP_ENTRIES_TLV_SIZE
/ LSP_ENTRIES_LEN
);
2500 /* The last TLV, if any */
2501 remaining_size
= size
% MAX_LSP_ENTRIES_TLV_SIZE
;
2502 if (remaining_size
- 2 >= LSP_ENTRIES_LEN
)
2503 lsp_count
+= (remaining_size
- 2) / LSP_ENTRIES_LEN
;
2509 * Calculate the length of Authentication Info. TLV.
2511 static uint16_t auth_tlv_length(int level
, struct isis_circuit
*circuit
)
2513 struct isis_passwd
*passwd
;
2516 if (level
== IS_LEVEL_1
)
2517 passwd
= &circuit
->area
->area_passwd
;
2519 passwd
= &circuit
->area
->domain_passwd
;
2521 /* Also include the length of TLV header */
2522 length
= AUTH_INFO_HDRLEN
;
2523 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)) {
2524 switch (passwd
->type
) {
2526 case ISIS_PASSWD_TYPE_CLEARTXT
:
2527 length
+= passwd
->len
;
2531 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2532 length
+= ISIS_AUTH_MD5_SIZE
;
2544 * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
2546 static uint16_t max_lsps_per_snp(int snp_type
, int level
,
2547 struct isis_circuit
*circuit
)
2553 snp_hdr_len
= ISIS_FIXED_HDR_LEN
;
2554 if (snp_type
== ISIS_SNP_CSNP_FLAG
)
2555 snp_hdr_len
+= ISIS_CSNP_HDRLEN
;
2557 snp_hdr_len
+= ISIS_PSNP_HDRLEN
;
2559 auth_tlv_len
= auth_tlv_length(level
, circuit
);
2560 lsp_count
= get_max_lsp_count(stream_get_size(circuit
->snd_stream
)
2561 - snp_hdr_len
- auth_tlv_len
);
2566 * FIXME: support multiple CSNPs
2569 int send_csnp(struct isis_circuit
*circuit
, int level
)
2571 u_char start
[ISIS_SYS_ID_LEN
+ 2];
2572 u_char stop
[ISIS_SYS_ID_LEN
+ 2];
2573 struct list
*list
= NULL
;
2574 struct listnode
*node
;
2575 struct isis_lsp
*lsp
;
2576 u_char num_lsps
, loop
= 1;
2577 int i
, retval
= ISIS_OK
;
2579 if (circuit
->area
->lspdb
[level
- 1] == NULL
2580 || dict_count(circuit
->area
->lspdb
[level
- 1]) == 0)
2583 memset(start
, 0x00, ISIS_SYS_ID_LEN
+ 2);
2584 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2586 num_lsps
= max_lsps_per_snp(ISIS_SNP_CSNP_FLAG
, level
, circuit
);
2590 lsp_build_list(start
, stop
, num_lsps
, list
,
2591 circuit
->area
->lspdb
[level
- 1]);
2593 * Update the stop lsp_id before encoding this CSNP.
2595 if (listcount(list
) < num_lsps
) {
2596 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2598 node
= listtail(list
);
2599 lsp
= listgetdata(node
);
2600 memcpy(stop
, lsp
->lsp_header
->lsp_id
,
2601 ISIS_SYS_ID_LEN
+ 2);
2604 retval
= build_csnp(level
, start
, stop
, list
, circuit
);
2605 if (retval
!= ISIS_OK
) {
2606 zlog_err("ISIS-Snp (%s): Build L%d CSNP on %s failed",
2607 circuit
->area
->area_tag
, level
,
2608 circuit
->interface
->name
);
2613 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
2615 "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
2616 circuit
->area
->area_tag
, level
,
2617 circuit
->interface
->name
,
2618 stream_get_endp(circuit
->snd_stream
));
2619 for (ALL_LIST_ELEMENTS_RO(list
, node
, lsp
)) {
2621 "ISIS-Snp (%s): CSNP entry %s, seq 0x%08x,"
2622 " cksum 0x%04x, lifetime %us",
2623 circuit
->area
->area_tag
,
2624 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2625 ntohl(lsp
->lsp_header
->seq_num
),
2626 ntohs(lsp
->lsp_header
->checksum
),
2627 ntohs(lsp
->lsp_header
->rem_lifetime
));
2629 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2631 STREAM_DATA(circuit
->snd_stream
),
2632 stream_get_endp(circuit
->snd_stream
));
2635 retval
= circuit
->tx(circuit
, level
);
2636 if (retval
!= ISIS_OK
) {
2637 zlog_err("ISIS-Snp (%s): Send L%d CSNP on %s failed",
2638 circuit
->area
->area_tag
, level
,
2639 circuit
->interface
->name
);
2645 * Start lsp_id of the next CSNP should be one plus the
2646 * stop lsp_id in this current CSNP.
2648 memcpy(start
, stop
, ISIS_SYS_ID_LEN
+ 2);
2650 for (i
= ISIS_SYS_ID_LEN
+ 1; i
>= 0; --i
) {
2651 if (start
[i
] < (u_char
)0xff) {
2657 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2664 int send_l1_csnp(struct thread
*thread
)
2666 struct isis_circuit
*circuit
;
2667 int retval
= ISIS_OK
;
2669 circuit
= THREAD_ARG(thread
);
2672 circuit
->t_send_csnp
[0] = NULL
;
2674 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2675 && circuit
->u
.bc
.is_dr
[0]) {
2676 send_csnp(circuit
, 1);
2678 /* set next timer thread */
2679 THREAD_TIMER_ON(master
, circuit
->t_send_csnp
[0], send_l1_csnp
, circuit
,
2680 isis_jitter(circuit
->csnp_interval
[0], CSNP_JITTER
));
2685 int send_l2_csnp(struct thread
*thread
)
2687 struct isis_circuit
*circuit
;
2688 int retval
= ISIS_OK
;
2690 circuit
= THREAD_ARG(thread
);
2693 circuit
->t_send_csnp
[1] = NULL
;
2695 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2696 && circuit
->u
.bc
.is_dr
[1]) {
2697 send_csnp(circuit
, 2);
2699 /* set next timer thread */
2700 THREAD_TIMER_ON(master
, circuit
->t_send_csnp
[1], send_l2_csnp
, circuit
,
2701 isis_jitter(circuit
->csnp_interval
[1], CSNP_JITTER
));
2706 static int build_psnp(int level
, struct isis_circuit
*circuit
,
2709 struct isis_fixed_hdr fixed_hdr
;
2712 struct isis_lsp
*lsp
;
2713 struct isis_passwd
*passwd
;
2714 struct listnode
*node
;
2715 unsigned char hmac_md5_hash
[ISIS_AUTH_MD5_SIZE
];
2716 unsigned long auth_tlv_offset
= 0;
2717 int retval
= ISIS_OK
;
2719 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2721 if (level
== IS_LEVEL_1
)
2722 fill_fixed_hdr_andstream(&fixed_hdr
, L1_PARTIAL_SEQ_NUM
,
2723 circuit
->snd_stream
);
2725 fill_fixed_hdr_andstream(&fixed_hdr
, L2_PARTIAL_SEQ_NUM
,
2726 circuit
->snd_stream
);
2729 * Fill Level 1 or 2 Partial Sequence Numbers header
2731 lenp
= stream_get_endp(circuit
->snd_stream
);
2732 stream_putw(circuit
->snd_stream
, 0); /* PDU length - when we know it */
2733 stream_put(circuit
->snd_stream
, isis
->sysid
, ISIS_SYS_ID_LEN
);
2734 stream_putc(circuit
->snd_stream
, circuit
->idx
);
2740 if (level
== IS_LEVEL_1
)
2741 passwd
= &circuit
->area
->area_passwd
;
2743 passwd
= &circuit
->area
->domain_passwd
;
2745 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)) {
2746 switch (passwd
->type
) {
2748 case ISIS_PASSWD_TYPE_CLEARTXT
:
2749 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT
,
2750 passwd
->len
, passwd
->passwd
,
2751 circuit
->snd_stream
))
2752 return ISIS_WARNING
;
2756 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2757 /* Remember where TLV is written so we can later
2758 * overwrite the MD5 hash */
2759 auth_tlv_offset
= stream_get_endp(circuit
->snd_stream
);
2760 memset(&hmac_md5_hash
, 0, ISIS_AUTH_MD5_SIZE
);
2761 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5
,
2762 ISIS_AUTH_MD5_SIZE
, hmac_md5_hash
,
2763 circuit
->snd_stream
))
2764 return ISIS_WARNING
;
2772 retval
= tlv_add_lsp_entries(lsps
, circuit
->snd_stream
);
2773 if (retval
!= ISIS_OK
)
2776 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
2777 for (ALL_LIST_ELEMENTS_RO(lsps
, node
, lsp
)) {
2779 "ISIS-Snp (%s): PSNP entry %s, seq 0x%08x,"
2780 " cksum 0x%04x, lifetime %us",
2781 circuit
->area
->area_tag
,
2782 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2783 ntohl(lsp
->lsp_header
->seq_num
),
2784 ntohs(lsp
->lsp_header
->checksum
),
2785 ntohs(lsp
->lsp_header
->rem_lifetime
));
2789 length
= (u_int16_t
)stream_get_endp(circuit
->snd_stream
);
2790 /* Update PDU length */
2791 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
2793 /* For HMAC MD5 we need to compute the md5 hash and store it */
2794 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)
2795 && passwd
->type
== ISIS_PASSWD_TYPE_HMAC_MD5
) {
2796 hmac_md5(STREAM_DATA(circuit
->snd_stream
),
2797 stream_get_endp(circuit
->snd_stream
),
2798 (unsigned char *)&passwd
->passwd
, passwd
->len
,
2799 (unsigned char *)&hmac_md5_hash
);
2800 /* Copy the hash into the stream */
2801 memcpy(STREAM_DATA(circuit
->snd_stream
) + auth_tlv_offset
+ 3,
2802 hmac_md5_hash
, ISIS_AUTH_MD5_SIZE
);
2809 * 7.3.15.4 action on expiration of partial SNP interval
2812 static int send_psnp(int level
, struct isis_circuit
*circuit
)
2814 struct isis_lsp
*lsp
;
2815 struct list
*list
= NULL
;
2816 struct listnode
*node
;
2818 int retval
= ISIS_OK
;
2820 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2821 && circuit
->u
.bc
.is_dr
[level
- 1])
2824 if (circuit
->area
->lspdb
[level
- 1] == NULL
2825 || dict_count(circuit
->area
->lspdb
[level
- 1]) == 0)
2828 if (!circuit
->snd_stream
)
2831 num_lsps
= max_lsps_per_snp(ISIS_SNP_PSNP_FLAG
, level
, circuit
);
2835 lsp_build_list_ssn(circuit
, num_lsps
, list
,
2836 circuit
->area
->lspdb
[level
- 1]);
2838 if (listcount(list
) == 0) {
2843 retval
= build_psnp(level
, circuit
, list
);
2844 if (retval
!= ISIS_OK
) {
2845 zlog_err("ISIS-Snp (%s): Build L%d PSNP on %s failed",
2846 circuit
->area
->area_tag
, level
,
2847 circuit
->interface
->name
);
2852 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
2854 "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
2855 circuit
->area
->area_tag
, level
,
2856 circuit
->interface
->name
,
2857 stream_get_endp(circuit
->snd_stream
));
2858 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2860 STREAM_DATA(circuit
->snd_stream
),
2861 stream_get_endp(circuit
->snd_stream
));
2864 retval
= circuit
->tx(circuit
, level
);
2865 if (retval
!= ISIS_OK
) {
2866 zlog_err("ISIS-Snp (%s): Send L%d PSNP on %s failed",
2867 circuit
->area
->area_tag
, level
,
2868 circuit
->interface
->name
);
2874 * sending succeeded, we can clear SSN flags of this circuit
2875 * for the LSPs in list
2877 for (ALL_LIST_ELEMENTS_RO(list
, node
, lsp
))
2878 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
2885 int send_l1_psnp(struct thread
*thread
)
2888 struct isis_circuit
*circuit
;
2889 int retval
= ISIS_OK
;
2891 circuit
= THREAD_ARG(thread
);
2894 circuit
->t_send_psnp
[0] = NULL
;
2896 send_psnp(1, circuit
);
2897 /* set next timer thread */
2898 THREAD_TIMER_ON(master
, circuit
->t_send_psnp
[0], send_l1_psnp
, circuit
,
2899 isis_jitter(circuit
->psnp_interval
[0], PSNP_JITTER
));
2905 * 7.3.15.4 action on expiration of partial SNP interval
2908 int send_l2_psnp(struct thread
*thread
)
2910 struct isis_circuit
*circuit
;
2911 int retval
= ISIS_OK
;
2913 circuit
= THREAD_ARG(thread
);
2916 circuit
->t_send_psnp
[1] = NULL
;
2918 send_psnp(2, circuit
);
2920 /* set next timer thread */
2921 THREAD_TIMER_ON(master
, circuit
->t_send_psnp
[1], send_l2_psnp
, circuit
,
2922 isis_jitter(circuit
->psnp_interval
[1], PSNP_JITTER
));
2928 * ISO 10589 - 7.3.14.3
2930 int send_lsp(struct thread
*thread
)
2932 struct isis_circuit
*circuit
;
2933 struct isis_lsp
*lsp
;
2934 struct listnode
*node
;
2936 int retval
= ISIS_OK
;
2938 circuit
= THREAD_ARG(thread
);
2941 if (!circuit
->lsp_queue
)
2944 node
= listhead(circuit
->lsp_queue
);
2947 * Handle case where there are no LSPs on the queue. This can
2948 * happen, for instance, if an adjacency goes down before this
2949 * thread gets a chance to run.
2955 * Delete LSP from lsp_queue. If it's still in queue, it is assumed
2956 * as 'transmit pending', but send_lsp may never be called again.
2957 * Retry will happen because SRM flag will not be cleared.
2959 lsp
= listgetdata(node
);
2960 list_delete_node(circuit
->lsp_queue
, node
);
2962 /* Set the last-cleared time if the queue is empty. */
2963 /* TODO: Is is possible that new lsps keep being added to the queue
2964 * that the queue is never empty? */
2965 if (list_isempty(circuit
->lsp_queue
))
2966 circuit
->lsp_queue_last_cleared
= time(NULL
);
2968 if (circuit
->state
!= C_STATE_UP
|| circuit
->is_passive
== 1)
2972 * Do not send if levels do not match
2974 if (!(lsp
->level
& circuit
->is_type
))
2978 * Do not send if we do not have adjacencies in state up on the circuit
2980 if (circuit
->upadjcount
[lsp
->level
- 1] == 0)
2983 /* stream_copy will assert and stop program execution if LSP is larger
2985 * the circuit's MTU. So handle and log this case here. */
2986 if (stream_get_endp(lsp
->pdu
) > stream_get_size(circuit
->snd_stream
)) {
2988 "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x,"
2989 " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu"
2990 " while interface stream size is %zu.",
2991 circuit
->area
->area_tag
, lsp
->level
,
2992 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2993 ntohl(lsp
->lsp_header
->seq_num
),
2994 ntohs(lsp
->lsp_header
->checksum
),
2995 ntohs(lsp
->lsp_header
->rem_lifetime
),
2996 circuit
->interface
->name
, stream_get_endp(lsp
->pdu
),
2997 stream_get_size(circuit
->snd_stream
));
2998 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2999 zlog_dump_data(STREAM_DATA(lsp
->pdu
),
3000 stream_get_endp(lsp
->pdu
));
3001 retval
= ISIS_ERROR
;
3005 /* copy our lsp to the send buffer */
3006 stream_copy(circuit
->snd_stream
, lsp
->pdu
);
3008 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
) {
3010 "ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
3011 " lifetime %us on %s",
3012 circuit
->area
->area_tag
, lsp
->level
,
3013 rawlspid_print(lsp
->lsp_header
->lsp_id
),
3014 ntohl(lsp
->lsp_header
->seq_num
),
3015 ntohs(lsp
->lsp_header
->checksum
),
3016 ntohs(lsp
->lsp_header
->rem_lifetime
),
3017 circuit
->interface
->name
);
3018 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
3019 zlog_dump_data(STREAM_DATA(circuit
->snd_stream
),
3020 stream_get_endp(circuit
->snd_stream
));
3024 retval
= circuit
->tx(circuit
, lsp
->level
);
3025 if (retval
!= ISIS_OK
) {
3026 zlog_err("ISIS-Upd (%s): Send L%d LSP on %s failed %s",
3027 circuit
->area
->area_tag
, lsp
->level
,
3028 circuit
->interface
->name
,
3029 (retval
== ISIS_WARNING
) ? "temporarily"
3035 || (retval
== ISIS_OK
&& circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
3036 || (retval
!= ISIS_OK
&& retval
!= ISIS_WARNING
)) {
3037 /* SRM flag will trigger retransmission. We will not retransmit
3039 * encountered a fatal error.
3040 * On success, they should only be cleared if it's a broadcast
3042 * On a P2P circuit, we will wait for the ack from the neighbor
3046 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
3052 int ack_lsp(struct isis_link_state_hdr
*hdr
, struct isis_circuit
*circuit
,
3058 struct isis_fixed_hdr fixed_hdr
;
3060 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
3062 // fill_llc_hdr (stream);
3063 if (level
== IS_LEVEL_1
)
3064 fill_fixed_hdr_andstream(&fixed_hdr
, L1_PARTIAL_SEQ_NUM
,
3065 circuit
->snd_stream
);
3067 fill_fixed_hdr_andstream(&fixed_hdr
, L2_PARTIAL_SEQ_NUM
,
3068 circuit
->snd_stream
);
3071 lenp
= stream_get_endp(circuit
->snd_stream
);
3072 stream_putw(circuit
->snd_stream
, 0); /* PDU length */
3073 stream_put(circuit
->snd_stream
, isis
->sysid
, ISIS_SYS_ID_LEN
);
3074 stream_putc(circuit
->snd_stream
, circuit
->idx
);
3075 stream_putc(circuit
->snd_stream
, 9); /* code */
3076 stream_putc(circuit
->snd_stream
, 16); /* len */
3078 stream_putw(circuit
->snd_stream
, ntohs(hdr
->rem_lifetime
));
3079 stream_put(circuit
->snd_stream
, hdr
->lsp_id
, ISIS_SYS_ID_LEN
+ 2);
3080 stream_putl(circuit
->snd_stream
, ntohl(hdr
->seq_num
));
3081 stream_putw(circuit
->snd_stream
, ntohs(hdr
->checksum
));
3083 length
= (u_int16_t
)stream_get_endp(circuit
->snd_stream
);
3084 /* Update PDU length */
3085 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
3087 retval
= circuit
->tx(circuit
, level
);
3088 if (retval
!= ISIS_OK
)
3089 zlog_err("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
3090 circuit
->area
->area_tag
, level
,
3091 circuit
->interface
->name
);