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; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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"
56 #include "isisd/isis_mt.h"
58 #define ISIS_MINIMUM_FIXED_HDR_LEN 15
59 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */
70 * Compares two sets of area addresses
72 static int area_match(struct list
*left
, struct list
*right
)
74 struct area_addr
*addr1
, *addr2
;
75 struct listnode
*node1
, *node2
;
77 for (ALL_LIST_ELEMENTS_RO(left
, node1
, addr1
)) {
78 for (ALL_LIST_ELEMENTS_RO(right
, node2
, addr2
)) {
79 if (addr1
->addr_len
== addr2
->addr_len
80 && !memcmp(addr1
->area_addr
, addr2
->area_addr
,
81 (int)addr1
->addr_len
))
86 return 0; /* mismatch */
90 * Checks whether we should accept a PDU of given level
92 static int accept_level(int level
, int circuit_t
)
94 int retval
= ((circuit_t
& level
) == level
); /* simple approach */
100 * Verify authentication information
101 * Support cleartext and HMAC MD5 authentication
103 static int authentication_check(struct isis_passwd
*remote
,
104 struct isis_passwd
*local
,
105 struct stream
*stream
, uint32_t auth_tlv_offset
)
107 unsigned char digest
[ISIS_AUTH_MD5_SIZE
];
109 /* Auth fail () - passwd type mismatch */
110 if (local
->type
!= remote
->type
)
113 switch (local
->type
) {
114 /* No authentication required */
115 case ISIS_PASSWD_TYPE_UNUSED
:
118 /* Cleartext (ISO 10589) */
119 case ISIS_PASSWD_TYPE_CLEARTXT
:
120 /* Auth fail () - passwd len mismatch */
121 if (remote
->len
!= local
->len
)
123 return memcmp(local
->passwd
, remote
->passwd
, local
->len
);
125 /* HMAC MD5 (RFC 3567) */
126 case ISIS_PASSWD_TYPE_HMAC_MD5
:
127 /* Auth fail () - passwd len mismatch */
128 if (remote
->len
!= ISIS_AUTH_MD5_SIZE
)
130 /* Set the authentication value to 0 before the check */
131 memset(STREAM_DATA(stream
) + auth_tlv_offset
+ 3, 0,
133 /* Compute the digest */
134 hmac_md5(STREAM_DATA(stream
), stream_get_endp(stream
),
135 (unsigned char *)&(local
->passwd
), local
->len
,
136 (unsigned char *)&digest
);
137 /* Copy back the authentication value after the check */
138 memcpy(STREAM_DATA(stream
) + auth_tlv_offset
+ 3,
139 remote
->passwd
, ISIS_AUTH_MD5_SIZE
);
140 return memcmp(digest
, remote
->passwd
, ISIS_AUTH_MD5_SIZE
);
143 zlog_err("Unsupported authentication type");
147 /* Authentication pass when no authentication is configured */
151 static int lsp_authentication_check(struct stream
*stream
,
152 struct isis_area
*area
, int level
,
153 struct isis_passwd
*passwd
)
155 struct isis_link_state_hdr
*hdr
;
156 uint32_t expected
= 0, found
= 0, auth_tlv_offset
= 0;
157 uint16_t checksum
, rem_lifetime
, pdu_len
;
159 int retval
= ISIS_OK
;
161 hdr
= (struct isis_link_state_hdr
*)(STREAM_PNT(stream
));
162 pdu_len
= ntohs(hdr
->pdu_len
);
163 expected
|= TLVFLAG_AUTH_INFO
;
164 auth_tlv_offset
= stream_get_getp(stream
) + ISIS_LSP_HDR_LEN
;
165 retval
= parse_tlvs(area
->area_tag
,
166 STREAM_PNT(stream
) + ISIS_LSP_HDR_LEN
,
167 pdu_len
- ISIS_FIXED_HDR_LEN
- ISIS_LSP_HDR_LEN
,
168 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
170 if (retval
!= ISIS_OK
) {
172 "ISIS-Upd (%s): Parse failed L%d LSP %s, seq 0x%08x, "
173 "cksum 0x%04x, lifetime %us, len %u",
174 area
->area_tag
, level
, rawlspid_print(hdr
->lsp_id
),
175 ntohl(hdr
->seq_num
), ntohs(hdr
->checksum
),
176 ntohs(hdr
->rem_lifetime
), pdu_len
);
177 if ((isis
->debugs
& DEBUG_UPDATE_PACKETS
)
178 && (isis
->debugs
& DEBUG_PACKET_DUMP
))
179 zlog_dump_data(STREAM_DATA(stream
),
180 stream_get_endp(stream
));
184 if (!(found
& TLVFLAG_AUTH_INFO
)) {
185 zlog_err("No authentication tlv in LSP");
189 if (tlvs
.auth_info
.type
!= ISIS_PASSWD_TYPE_CLEARTXT
190 && tlvs
.auth_info
.type
!= ISIS_PASSWD_TYPE_HMAC_MD5
) {
191 zlog_err("Unknown authentication type in LSP");
196 * RFC 5304 set checksum and remaining lifetime to zero before
197 * verification and reset to old values after verification.
199 checksum
= hdr
->checksum
;
200 rem_lifetime
= hdr
->rem_lifetime
;
202 hdr
->rem_lifetime
= 0;
203 retval
= authentication_check(&tlvs
.auth_info
, passwd
, stream
,
205 hdr
->checksum
= checksum
;
206 hdr
->rem_lifetime
= rem_lifetime
;
212 * Processing helper functions
214 static void del_addr(void *val
)
216 XFREE(MTYPE_ISIS_TMP
, val
);
219 static void tlvs_to_adj_area_addrs(struct tlvs
*tlvs
,
220 struct isis_adjacency
*adj
)
222 struct listnode
*node
;
223 struct area_addr
*area_addr
, *malloced
;
225 if (adj
->area_addrs
) {
226 adj
->area_addrs
->del
= del_addr
;
227 list_delete(adj
->area_addrs
);
229 adj
->area_addrs
= list_new();
230 if (tlvs
->area_addrs
) {
231 for (ALL_LIST_ELEMENTS_RO(tlvs
->area_addrs
, node
, area_addr
)) {
232 malloced
= XMALLOC(MTYPE_ISIS_TMP
,
233 sizeof(struct area_addr
));
234 memcpy(malloced
, area_addr
, sizeof(struct area_addr
));
235 listnode_add(adj
->area_addrs
, malloced
);
240 static int tlvs_to_adj_nlpids(struct tlvs
*tlvs
, struct isis_adjacency
*adj
)
243 struct nlpids
*tlv_nlpids
;
247 tlv_nlpids
= tlvs
->nlpids
;
248 if (tlv_nlpids
->count
> array_size(adj
->nlpids
.nlpids
))
251 adj
->nlpids
.count
= tlv_nlpids
->count
;
253 for (i
= 0; i
< tlv_nlpids
->count
; i
++) {
254 adj
->nlpids
.nlpids
[i
] = tlv_nlpids
->nlpids
[i
];
260 static void tlvs_to_adj_ipv4_addrs(struct tlvs
*tlvs
,
261 struct isis_adjacency
*adj
)
263 struct listnode
*node
;
264 struct in_addr
*ipv4_addr
, *malloced
;
266 if (adj
->ipv4_addrs
) {
267 adj
->ipv4_addrs
->del
= del_addr
;
268 list_delete(adj
->ipv4_addrs
);
270 adj
->ipv4_addrs
= list_new();
271 if (tlvs
->ipv4_addrs
) {
272 for (ALL_LIST_ELEMENTS_RO(tlvs
->ipv4_addrs
, node
, ipv4_addr
)) {
274 XMALLOC(MTYPE_ISIS_TMP
, sizeof(struct in_addr
));
275 memcpy(malloced
, ipv4_addr
, sizeof(struct in_addr
));
276 listnode_add(adj
->ipv4_addrs
, malloced
);
281 static void tlvs_to_adj_ipv6_addrs(struct tlvs
*tlvs
,
282 struct isis_adjacency
*adj
)
284 struct listnode
*node
;
285 struct in6_addr
*ipv6_addr
, *malloced
;
287 if (adj
->ipv6_addrs
) {
288 adj
->ipv6_addrs
->del
= del_addr
;
289 list_delete(adj
->ipv6_addrs
);
291 adj
->ipv6_addrs
= list_new();
292 if (tlvs
->ipv6_addrs
) {
293 for (ALL_LIST_ELEMENTS_RO(tlvs
->ipv6_addrs
, node
, ipv6_addr
)) {
294 malloced
= XMALLOC(MTYPE_ISIS_TMP
,
295 sizeof(struct in6_addr
));
296 memcpy(malloced
, ipv6_addr
, sizeof(struct in6_addr
));
297 listnode_add(adj
->ipv6_addrs
, malloced
);
309 * Section 8.2.5 - Receiving point-to-point IIH PDUs
312 static int process_p2p_hello(struct isis_circuit
*circuit
)
314 int retval
= ISIS_OK
;
315 struct isis_p2p_hello_hdr
*hdr
;
316 struct isis_adjacency
*adj
;
317 u_int32_t expected
= 0, found
= 0, auth_tlv_offset
= 0;
320 int v4_usable
= 0, v6_usable
= 0;
322 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
324 "ISIS-Adj (%s): Rcvd P2P IIH on %s, cirType %s, cirID %u",
325 circuit
->area
->area_tag
, circuit
->interface
->name
,
326 circuit_t2string(circuit
->is_type
),
327 circuit
->circuit_id
);
328 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
329 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
330 stream_get_endp(circuit
->rcv_stream
));
333 if (circuit
->circ_type
!= CIRCUIT_T_P2P
) {
334 zlog_warn("p2p hello on non p2p circuit");
338 if ((stream_get_endp(circuit
->rcv_stream
)
339 - stream_get_getp(circuit
->rcv_stream
))
340 < ISIS_P2PHELLO_HDRLEN
) {
341 zlog_warn("Packet too short");
345 /* 8.2.5.1 PDU acceptance tests */
347 /* 8.2.5.1 a) external domain untrue */
348 /* FIXME: not useful at all? */
350 /* 8.2.5.1 b) ID Length mismatch */
351 /* checked at the handle_pdu */
353 /* 8.2.5.2 IIH PDU Processing */
355 /* 8.2.5.2 a) 1) Maximum Area Addresses */
356 /* Already checked, and can also be ommited */
361 hdr
= (struct isis_p2p_hello_hdr
*)STREAM_PNT(circuit
->rcv_stream
);
362 pdu_len
= ntohs(hdr
->pdu_len
);
364 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_P2PHELLO_HDRLEN
)
365 || pdu_len
> ISO_MTU(circuit
)
366 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
368 "ISIS-Adj (%s): Rcvd P2P IIH from (%s) with "
369 "invalid pdu length %d",
370 circuit
->area
->area_tag
, circuit
->interface
->name
,
376 * Set the stream endp to PDU length, ignoring additional padding
377 * introduced by transport chips.
379 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
380 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
382 stream_forward_getp(circuit
->rcv_stream
, ISIS_P2PHELLO_HDRLEN
);
385 * Lets get the TLVS now
387 expected
|= TLVFLAG_AREA_ADDRS
;
388 expected
|= TLVFLAG_AUTH_INFO
;
389 expected
|= TLVFLAG_NLPID
;
390 expected
|= TLVFLAG_IPV4_ADDR
;
391 expected
|= TLVFLAG_IPV6_ADDR
;
392 expected
|= TLVFLAG_MT_ROUTER_INFORMATION
;
394 auth_tlv_offset
= stream_get_getp(circuit
->rcv_stream
);
395 retval
= parse_tlvs(circuit
->area
->area_tag
,
396 STREAM_PNT(circuit
->rcv_stream
),
397 pdu_len
- ISIS_P2PHELLO_HDRLEN
- ISIS_FIXED_HDR_LEN
,
398 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
400 if (retval
> ISIS_WARNING
) {
401 zlog_warn("parse_tlvs() failed");
406 if (!(found
& TLVFLAG_AREA_ADDRS
)) {
407 zlog_warn("No Area addresses TLV in P2P IS to IS hello");
412 if (!(found
& TLVFLAG_NLPID
)) {
413 zlog_warn("No supported protocols TLV in P2P IS to IS hello");
418 /* 8.2.5.1 c) Authentication */
419 if (circuit
->passwd
.type
) {
420 if (!(found
& TLVFLAG_AUTH_INFO
)
421 || authentication_check(&tlvs
.auth_info
, &circuit
->passwd
,
424 isis_event_auth_failure(
425 circuit
->area
->area_tag
,
426 "P2P hello authentication failure",
434 * check if both ends have an IPv4 address
436 if (circuit
->ip_addrs
&& listcount(circuit
->ip_addrs
) && tlvs
.ipv4_addrs
437 && listcount(tlvs
.ipv4_addrs
)) {
441 if (found
& TLVFLAG_IPV6_ADDR
) {
442 /* TBA: check that we have a linklocal ourselves? */
443 struct listnode
*node
;
445 for (ALL_LIST_ELEMENTS_RO(tlvs
.ipv6_addrs
, node
, ip
))
446 if (IN6_IS_ADDR_LINKLOCAL(ip
)) {
453 "ISIS-Adj: IPv6 addresses present but no link-local "
454 "in P2P IIH from %s\n",
455 circuit
->interface
->name
);
458 if (!(found
& (TLVFLAG_IPV4_ADDR
| TLVFLAG_IPV6_ADDR
)))
460 "ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n",
461 circuit
->interface
->name
);
463 if (!v6_usable
&& !v4_usable
) {
469 * it's own p2p IIH PDU - discard
471 if (!memcmp(hdr
->source_id
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
472 zlog_warn("ISIS-Adj (%s): it's own IIH PDU - discarded",
473 circuit
->area
->area_tag
);
479 * My interpertation of the ISO, if no adj exists we will create one for
482 adj
= circuit
->u
.p2p
.neighbor
;
483 /* If an adjacency exists, check it is with the source of the hello
486 if (memcmp(hdr
->source_id
, adj
->sysid
, ISIS_SYS_ID_LEN
)) {
488 "hello source and adjacency do not match, set adj down\n");
489 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
,
494 if (!adj
|| adj
->level
!= hdr
->circuit_t
) {
496 adj
= isis_new_adj(hdr
->source_id
, NULL
, hdr
->circuit_t
,
501 adj
->level
= hdr
->circuit_t
;
503 circuit
->u
.p2p
.neighbor
= adj
;
504 /* Build lsp with the new neighbor entry when a new
505 * adjacency is formed. Set adjacency circuit type to
506 * IIH PDU header circuit type before lsp is regenerated
507 * when an adjacency is up. This will result in the new
508 * adjacency entry getting added to the lsp tlv neighbor list.
510 adj
->circuit_t
= hdr
->circuit_t
;
511 isis_adj_state_change(adj
, ISIS_ADJ_INITIALIZING
, NULL
);
512 adj
->sys_type
= ISIS_SYSTYPE_UNKNOWN
;
515 /* 8.2.6 Monitoring point-to-point adjacencies */
516 adj
->hold_time
= ntohs(hdr
->hold_time
);
517 adj
->last_upd
= time(NULL
);
519 /* we do this now because the adj may not survive till the end... */
520 tlvs_to_adj_area_addrs(&tlvs
, adj
);
522 /* which protocol are spoken ??? */
523 if (tlvs_to_adj_nlpids(&tlvs
, adj
)) {
528 /* we need to copy addresses to the adj */
529 if (found
& TLVFLAG_IPV4_ADDR
)
530 tlvs_to_adj_ipv4_addrs(&tlvs
, adj
);
532 /* Update MPLS TE Remote IP address parameter if possible */
533 if (IS_MPLS_TE(isisMplsTE
) && circuit
->mtc
534 && IS_CIRCUIT_TE(circuit
->mtc
))
535 if (adj
->ipv4_addrs
!= NULL
536 && listcount(adj
->ipv4_addrs
) != 0) {
537 struct in_addr
*ip_addr
;
538 ip_addr
= (struct in_addr
*)listgetdata(
539 (struct listnode
*)listhead(adj
->ipv4_addrs
));
540 set_circuitparams_rmt_ipaddr(circuit
->mtc
, *ip_addr
);
543 if (found
& TLVFLAG_IPV6_ADDR
)
544 tlvs_to_adj_ipv6_addrs(&tlvs
, adj
);
546 bool mt_set_changed
=
547 tlvs_to_adj_mt_set(&tlvs
, v4_usable
, v6_usable
, adj
);
549 /* lets take care of the expiry */
550 THREAD_TIMER_OFF(adj
->t_expire
);
551 thread_add_timer(master
, isis_adj_expire
, adj
, (long)adj
->hold_time
,
554 /* 8.2.5.2 a) a match was detected */
555 if (area_match(circuit
->area
->area_addrs
, tlvs
.area_addrs
)) {
556 /* 8.2.5.2 a) 2) If the system is L1 - table 5 */
557 if (circuit
->area
->is_type
== IS_LEVEL_1
) {
558 switch (hdr
->circuit_t
) {
560 case IS_LEVEL_1_AND_2
:
561 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
562 /* (4) adj state up */
563 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
565 /* (5) adj usage level 1 */
566 adj
->adj_usage
= ISIS_ADJ_LEVEL1
;
567 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
572 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
573 /* (7) reject - wrong system type event
575 zlog_warn("wrongSystemType");
577 return ISIS_WARNING
; /* Reject */
578 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
579 /* (6) down - wrong system */
580 isis_adj_state_change(adj
,
588 /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */
589 if (circuit
->area
->is_type
== IS_LEVEL_1_AND_2
) {
590 switch (hdr
->circuit_t
) {
592 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
593 /* (6) adj state up */
594 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
596 /* (7) adj usage level 1 */
597 adj
->adj_usage
= ISIS_ADJ_LEVEL1
;
598 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
600 } else if ((adj
->adj_usage
601 == ISIS_ADJ_LEVEL1AND2
)
603 == ISIS_ADJ_LEVEL2
)) {
604 /* (8) down - wrong system */
605 isis_adj_state_change(adj
,
611 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
612 /* (6) adj state up */
613 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
615 /* (9) adj usage level 2 */
616 adj
->adj_usage
= ISIS_ADJ_LEVEL2
;
617 } else if ((adj
->adj_usage
== ISIS_ADJ_LEVEL1
)
619 == ISIS_ADJ_LEVEL1AND2
)) {
620 /* (8) down - wrong system */
621 isis_adj_state_change(adj
,
624 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
628 case IS_LEVEL_1_AND_2
:
629 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
630 /* (6) adj state up */
631 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
633 /* (10) adj usage level 1 */
634 adj
->adj_usage
= ISIS_ADJ_LEVEL1AND2
;
635 } else if ((adj
->adj_usage
== ISIS_ADJ_LEVEL1
)
637 == ISIS_ADJ_LEVEL2
)) {
638 /* (8) down - wrong system */
639 isis_adj_state_change(adj
,
642 } else if (adj
->adj_usage
643 == ISIS_ADJ_LEVEL1AND2
) {
650 /* 8.2.5.2 a) 4) If the system is L2 - table 7 */
651 if (circuit
->area
->is_type
== IS_LEVEL_2
) {
652 switch (hdr
->circuit_t
) {
654 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
655 /* (5) reject - wrong system type event
657 zlog_warn("wrongSystemType");
659 return ISIS_WARNING
; /* Reject */
660 } else if ((adj
->adj_usage
661 == ISIS_ADJ_LEVEL1AND2
)
663 == ISIS_ADJ_LEVEL2
)) {
664 /* (6) down - wrong system */
665 isis_adj_state_change(adj
,
670 case IS_LEVEL_1_AND_2
:
672 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
673 /* (7) adj state up */
674 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
676 /* (8) adj usage level 2 */
677 adj
->adj_usage
= ISIS_ADJ_LEVEL2
;
678 } else if (adj
->adj_usage
679 == ISIS_ADJ_LEVEL1AND2
) {
680 /* (6) down - wrong system */
681 isis_adj_state_change(adj
,
684 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
691 /* 8.2.5.2 b) if no match was detected */
692 else if (listcount(circuit
->area
->area_addrs
) > 0) {
693 if (circuit
->area
->is_type
== IS_LEVEL_1
) {
694 /* 8.2.5.2 b) 1) is_type L1 and adj is not up */
695 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
696 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
,
698 /* 8.2.5.2 b) 2)is_type L1 and adj is up */
700 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
,
701 "Down - Area Mismatch");
704 /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
706 switch (hdr
->circuit_t
) {
708 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
709 /* (6) reject - Area Mismatch event */
710 zlog_warn("AreaMismatch");
712 return ISIS_WARNING
; /* Reject */
713 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
714 /* (7) down - area mismatch */
715 isis_adj_state_change(adj
,
719 } else if ((adj
->adj_usage
720 == ISIS_ADJ_LEVEL1AND2
)
722 == ISIS_ADJ_LEVEL2
)) {
723 /* (7) down - wrong system */
724 isis_adj_state_change(adj
,
729 case IS_LEVEL_1_AND_2
:
731 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
732 /* (8) adj state up */
733 isis_adj_state_change(adj
, ISIS_ADJ_UP
,
735 /* (9) adj usage level 2 */
736 adj
->adj_usage
= ISIS_ADJ_LEVEL2
;
737 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
738 /* (7) down - wrong system */
739 isis_adj_state_change(adj
,
742 } else if (adj
->adj_usage
743 == ISIS_ADJ_LEVEL1AND2
) {
744 if (hdr
->circuit_t
== IS_LEVEL_2
) {
745 /* (7) down - wrong system */
746 isis_adj_state_change(
750 /* (7) down - area mismatch */
751 isis_adj_state_change(
755 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
762 /* down - area mismatch */
763 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
, "Area Mismatch");
766 if (adj
->adj_state
== ISIS_ADJ_UP
&& mt_set_changed
) {
767 lsp_regenerate_schedule(adj
->circuit
->area
,
768 isis_adj_usage2levels(adj
->adj_usage
),
772 /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
773 /* FIXME - Missing parts */
775 /* some of my own understanding of the ISO, why the heck does
776 * it not say what should I change the system_type to...
778 switch (adj
->adj_usage
) {
779 case ISIS_ADJ_LEVEL1
:
780 adj
->sys_type
= ISIS_SYSTYPE_L1_IS
;
782 case ISIS_ADJ_LEVEL2
:
783 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
785 case ISIS_ADJ_LEVEL1AND2
:
786 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
789 adj
->sys_type
= ISIS_SYSTYPE_UNKNOWN
;
794 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
796 "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s,"
797 " cir id %02d, length %d",
798 circuit
->area
->area_tag
, circuit
->interface
->name
,
799 circuit_t2string(circuit
->is_type
), circuit
->circuit_id
,
809 * Process IS-IS LAN Level 1/2 Hello PDU
811 static int process_lan_hello(int level
, struct isis_circuit
*circuit
,
814 int retval
= ISIS_OK
;
815 struct isis_lan_hello_hdr hdr
;
816 struct isis_adjacency
*adj
;
817 u_int32_t expected
= 0, found
= 0, auth_tlv_offset
= 0;
820 struct listnode
*node
;
821 int v4_usable
= 0, v6_usable
= 0;
823 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
825 "ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, "
827 circuit
->area
->area_tag
, level
,
828 circuit
->interface
->name
,
829 circuit_t2string(circuit
->is_type
),
830 circuit
->circuit_id
);
831 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
832 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
833 stream_get_endp(circuit
->rcv_stream
));
836 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
837 zlog_warn("lan hello on non broadcast circuit");
841 if ((stream_get_endp(circuit
->rcv_stream
)
842 - stream_get_getp(circuit
->rcv_stream
))
843 < ISIS_LANHELLO_HDRLEN
) {
844 zlog_warn("Packet too short");
848 if (circuit
->ext_domain
) {
850 "level %d LAN Hello received over circuit with "
851 "externalDomain = true",
856 if (!accept_level(level
, circuit
->is_type
)) {
857 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
859 "ISIS-Adj (%s): Interface level mismatch, %s",
860 circuit
->area
->area_tag
,
861 circuit
->interface
->name
);
867 /* Cisco's debug message compatability */
868 if (!accept_level (level
, circuit
->area
->is_type
))
870 if (isis
->debugs
& DEBUG_ADJ_PACKETS
)
872 zlog_debug ("ISIS-Adj (%s): is type mismatch",
873 circuit
->area
->area_tag
);
881 hdr
.circuit_t
= stream_getc(circuit
->rcv_stream
);
882 stream_get(hdr
.source_id
, circuit
->rcv_stream
, ISIS_SYS_ID_LEN
);
883 hdr
.hold_time
= stream_getw(circuit
->rcv_stream
);
884 hdr
.pdu_len
= stream_getw(circuit
->rcv_stream
);
885 hdr
.prio
= stream_getc(circuit
->rcv_stream
);
886 stream_get(hdr
.lan_id
, circuit
->rcv_stream
, ISIS_SYS_ID_LEN
+ 1);
888 if (hdr
.pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_LANHELLO_HDRLEN
)
889 || hdr
.pdu_len
> ISO_MTU(circuit
)
890 || hdr
.pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
892 "ISIS-Adj (%s): Rcvd LAN IIH from (%s) with "
893 "invalid pdu length %d",
894 circuit
->area
->area_tag
, circuit
->interface
->name
,
900 * Set the stream endp to PDU length, ignoring additional padding
901 * introduced by transport chips.
903 if (hdr
.pdu_len
< stream_get_endp(circuit
->rcv_stream
))
904 stream_set_endp(circuit
->rcv_stream
, hdr
.pdu_len
);
906 if (hdr
.circuit_t
!= IS_LEVEL_1
&& hdr
.circuit_t
!= IS_LEVEL_2
907 && hdr
.circuit_t
!= IS_LEVEL_1_AND_2
908 && (level
& hdr
.circuit_t
) == 0) {
909 zlog_err("Level %d LAN Hello with Circuit Type %d", level
,
917 expected
|= TLVFLAG_AUTH_INFO
;
918 expected
|= TLVFLAG_AREA_ADDRS
;
919 expected
|= TLVFLAG_LAN_NEIGHS
;
920 expected
|= TLVFLAG_NLPID
;
921 expected
|= TLVFLAG_IPV4_ADDR
;
922 expected
|= TLVFLAG_IPV6_ADDR
;
923 expected
|= TLVFLAG_MT_ROUTER_INFORMATION
;
925 auth_tlv_offset
= stream_get_getp(circuit
->rcv_stream
);
927 circuit
->area
->area_tag
, STREAM_PNT(circuit
->rcv_stream
),
928 hdr
.pdu_len
- ISIS_LANHELLO_HDRLEN
- ISIS_FIXED_HDR_LEN
,
929 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
931 if (retval
> ISIS_WARNING
) {
932 zlog_warn("parse_tlvs() failed");
936 if (!(found
& TLVFLAG_AREA_ADDRS
)) {
938 "No Area addresses TLV in Level %d LAN IS to IS hello",
940 retval
= ISIS_WARNING
;
944 if (!(found
& TLVFLAG_NLPID
)) {
946 "No supported protocols TLV in Level %d LAN IS to IS hello",
948 retval
= ISIS_WARNING
;
952 /* Verify authentication, either cleartext of HMAC MD5 */
953 if (circuit
->passwd
.type
) {
954 if (!(found
& TLVFLAG_AUTH_INFO
)
955 || authentication_check(&tlvs
.auth_info
, &circuit
->passwd
,
958 isis_event_auth_failure(
959 circuit
->area
->area_tag
,
960 "LAN hello authentication failure",
962 retval
= ISIS_WARNING
;
967 if (!memcmp(hdr
.source_id
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
968 zlog_warn("ISIS-Adj (%s): duplicate system ID on interface %s",
969 circuit
->area
->area_tag
, circuit
->interface
->name
);
974 * Accept the level 1 adjacency only if a match between local and
975 * remote area addresses is found
977 if (listcount(circuit
->area
->area_addrs
) == 0
978 || (level
== IS_LEVEL_1
979 && area_match(circuit
->area
->area_addrs
, tlvs
.area_addrs
)
981 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
983 "ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
984 circuit
->area
->area_tag
, level
,
985 circuit
->interface
->name
);
992 * it's own IIH PDU - discard silently
994 if (!memcmp(circuit
->u
.bc
.snpa
, ssnpa
, ETH_ALEN
)) {
995 zlog_debug("ISIS-Adj (%s): it's own IIH PDU - discarded",
996 circuit
->area
->area_tag
);
1003 * check if both ends have an IPv4 address
1005 if (circuit
->ip_addrs
&& listcount(circuit
->ip_addrs
) && tlvs
.ipv4_addrs
1006 && listcount(tlvs
.ipv4_addrs
)) {
1010 if (found
& TLVFLAG_IPV6_ADDR
) {
1011 /* TBA: check that we have a linklocal ourselves? */
1012 struct listnode
*node
;
1013 struct in6_addr
*ip
;
1014 for (ALL_LIST_ELEMENTS_RO(tlvs
.ipv6_addrs
, node
, ip
))
1015 if (IN6_IS_ADDR_LINKLOCAL(ip
)) {
1022 "ISIS-Adj: IPv6 addresses present but no link-local "
1023 "in LAN IIH from %s\n",
1024 circuit
->interface
->name
);
1027 if (!(found
& (TLVFLAG_IPV4_ADDR
| TLVFLAG_IPV6_ADDR
)))
1029 "ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n",
1030 circuit
->interface
->name
);
1032 if (!v6_usable
&& !v4_usable
) {
1034 return ISIS_WARNING
;
1038 adj
= isis_adj_lookup(hdr
.source_id
, circuit
->u
.bc
.adjdb
[level
- 1]);
1039 if ((adj
== NULL
) || (memcmp(adj
->snpa
, ssnpa
, ETH_ALEN
))
1040 || (adj
->level
!= level
)) {
1045 adj
= isis_new_adj(hdr
.source_id
, ssnpa
, level
,
1048 retval
= ISIS_ERROR
;
1053 memcpy(adj
->snpa
, ssnpa
, 6);
1055 memset(adj
->snpa
, ' ', 6);
1059 isis_adj_state_change(adj
, ISIS_ADJ_INITIALIZING
, NULL
);
1061 if (level
== IS_LEVEL_1
)
1062 adj
->sys_type
= ISIS_SYSTYPE_L1_IS
;
1064 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
1065 list_delete_all_node(circuit
->u
.bc
.lan_neighs
[level
- 1]);
1066 isis_adj_build_neigh_list(circuit
->u
.bc
.adjdb
[level
- 1],
1067 circuit
->u
.bc
.lan_neighs
[level
- 1]);
1070 if (adj
->dis_record
[level
- 1].dis
== ISIS_IS_DIS
)
1073 if (memcmp(circuit
->u
.bc
.l1_desig_is
, hdr
.lan_id
,
1074 ISIS_SYS_ID_LEN
+ 1)) {
1075 thread_add_event(master
,
1076 isis_event_dis_status_change
,
1078 memcpy(&circuit
->u
.bc
.l1_desig_is
, hdr
.lan_id
,
1079 ISIS_SYS_ID_LEN
+ 1);
1083 if (memcmp(circuit
->u
.bc
.l2_desig_is
, hdr
.lan_id
,
1084 ISIS_SYS_ID_LEN
+ 1)) {
1085 thread_add_event(master
,
1086 isis_event_dis_status_change
,
1088 memcpy(&circuit
->u
.bc
.l2_desig_is
, hdr
.lan_id
,
1089 ISIS_SYS_ID_LEN
+ 1);
1094 adj
->hold_time
= hdr
.hold_time
;
1095 adj
->last_upd
= time(NULL
);
1096 adj
->prio
[level
- 1] = hdr
.prio
;
1098 memcpy(adj
->lanid
, hdr
.lan_id
, ISIS_SYS_ID_LEN
+ 1);
1100 tlvs_to_adj_area_addrs(&tlvs
, adj
);
1102 /* which protocol are spoken ??? */
1103 if (tlvs_to_adj_nlpids(&tlvs
, adj
)) {
1104 retval
= ISIS_WARNING
;
1108 /* we need to copy addresses to the adj */
1109 if (found
& TLVFLAG_IPV4_ADDR
)
1110 tlvs_to_adj_ipv4_addrs(&tlvs
, adj
);
1112 if (found
& TLVFLAG_IPV6_ADDR
)
1113 tlvs_to_adj_ipv6_addrs(&tlvs
, adj
);
1115 adj
->circuit_t
= hdr
.circuit_t
;
1117 bool mt_set_changed
=
1118 tlvs_to_adj_mt_set(&tlvs
, v4_usable
, v6_usable
, adj
);
1120 /* lets take care of the expiry */
1121 THREAD_TIMER_OFF(adj
->t_expire
);
1122 thread_add_timer(master
, isis_adj_expire
, adj
, (long)adj
->hold_time
,
1126 * If the snpa for this circuit is found from LAN Neighbours TLV
1127 * we have two-way communication -> adjacency can be put to state "up"
1130 if (found
& TLVFLAG_LAN_NEIGHS
) {
1131 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
1132 for (ALL_LIST_ELEMENTS_RO(tlvs
.lan_neighs
, node
,
1134 if (!memcmp(snpa
, circuit
->u
.bc
.snpa
,
1136 isis_adj_state_change(
1138 "own SNPA found in LAN Neighbours TLV");
1143 for (ALL_LIST_ELEMENTS_RO(tlvs
.lan_neighs
, node
, snpa
))
1144 if (!memcmp(snpa
, circuit
->u
.bc
.snpa
,
1150 isis_adj_state_change(
1151 adj
, ISIS_ADJ_INITIALIZING
,
1152 "own SNPA not found in LAN Neighbours TLV");
1154 } else if (adj
->adj_state
== ISIS_ADJ_UP
) {
1155 isis_adj_state_change(adj
, ISIS_ADJ_INITIALIZING
,
1156 "no LAN Neighbours TLV found");
1159 if (adj
->adj_state
== ISIS_ADJ_UP
&& mt_set_changed
)
1160 lsp_regenerate_schedule(adj
->circuit
->area
, level
, 0);
1163 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
1165 "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, "
1166 "cirID %u, length %zd",
1167 circuit
->area
->area_tag
, level
, snpa_print(ssnpa
),
1168 circuit
->interface
->name
,
1169 circuit_t2string(circuit
->is_type
), circuit
->circuit_id
,
1170 stream_get_endp(circuit
->rcv_stream
));
1179 * Process Level 1/2 Link State
1181 * Section 7.3.15.1 - Action on receipt of a link state PDU
1183 static int process_lsp(int level
, struct isis_circuit
*circuit
,
1184 const u_char
*ssnpa
)
1186 struct isis_link_state_hdr
*hdr
;
1187 struct isis_adjacency
*adj
= NULL
;
1188 struct isis_lsp
*lsp
, *lsp0
= NULL
;
1189 int retval
= ISIS_OK
, comp
= 0;
1190 u_char lspid
[ISIS_SYS_ID_LEN
+ 2];
1191 struct isis_passwd
*passwd
;
1195 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
) {
1197 "ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u",
1198 circuit
->area
->area_tag
, level
,
1199 circuit
->interface
->name
,
1200 circuit_t2string(circuit
->is_type
),
1201 circuit
->circuit_id
);
1202 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
1203 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
1204 stream_get_endp(circuit
->rcv_stream
));
1207 if ((stream_get_endp(circuit
->rcv_stream
)
1208 - stream_get_getp(circuit
->rcv_stream
))
1209 < ISIS_LSP_HDR_LEN
) {
1210 zlog_warn("Packet too short");
1211 return ISIS_WARNING
;
1214 /* Reference the header */
1215 hdr
= (struct isis_link_state_hdr
*)STREAM_PNT(circuit
->rcv_stream
);
1216 pdu_len
= ntohs(hdr
->pdu_len
);
1218 /* lsp length check */
1219 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_LSP_HDR_LEN
)
1220 || pdu_len
> ISO_MTU(circuit
)
1221 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
1222 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %d",
1223 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1226 return ISIS_WARNING
;
1230 * Set the stream endp to PDU length, ignoring additional padding
1231 * introduced by transport chips.
1233 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
1234 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
1236 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
) {
1238 "ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04x, "
1239 "lifetime %us, len %u, on %s",
1240 circuit
->area
->area_tag
, level
,
1241 rawlspid_print(hdr
->lsp_id
), ntohl(hdr
->seq_num
),
1242 ntohs(hdr
->checksum
), ntohs(hdr
->rem_lifetime
), pdu_len
,
1243 circuit
->interface
->name
);
1246 /* lsp is_type check */
1247 if ((hdr
->lsp_bits
& IS_LEVEL_1_AND_2
) != IS_LEVEL_1
1248 && (hdr
->lsp_bits
& IS_LEVEL_1_AND_2
) != IS_LEVEL_1_AND_2
) {
1249 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP is type %x",
1250 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1252 /* continue as per RFC1122 Be liberal in what you accept, and
1253 * conservative in what you send */
1256 /* Checksum sanity check - FIXME: move to correct place */
1257 /* 12 = sysid+pdu+remtime */
1258 if (iso_csum_verify(STREAM_PNT(circuit
->rcv_stream
) + 4, pdu_len
- 12,
1260 offsetof(struct isis_link_state_hdr
, checksum
)
1262 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x",
1263 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1264 ntohs(hdr
->checksum
));
1266 return ISIS_WARNING
;
1269 /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
1270 if (circuit
->ext_domain
) {
1272 "ISIS-Upd (%s): LSP %s received at level %d over circuit with "
1273 "externalDomain = true",
1274 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1277 return ISIS_WARNING
;
1280 /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
1281 if (!accept_level(level
, circuit
->is_type
)) {
1283 "ISIS-Upd (%s): LSP %s received at level %d over circuit of"
1285 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1286 level
, circuit_t2string(circuit
->is_type
));
1288 return ISIS_WARNING
;
1291 /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
1293 /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use
1296 /* 7.3.15.1 a) 7 - password check */
1297 (level
== IS_LEVEL_1
) ? (passwd
= &circuit
->area
->area_passwd
)
1298 : (passwd
= &circuit
->area
->domain_passwd
);
1300 if (lsp_authentication_check(circuit
->rcv_stream
, circuit
->area
,
1302 isis_event_auth_failure(circuit
->area
->area_tag
,
1303 "LSP authentication failure",
1305 return ISIS_WARNING
;
1308 /* Find the LSP in our database and compare it to this Link State header
1310 lsp
= lsp_search(hdr
->lsp_id
, circuit
->area
->lspdb
[level
- 1]);
1312 comp
= lsp_compare(circuit
->area
->area_tag
, lsp
, hdr
->seq_num
,
1313 hdr
->checksum
, hdr
->rem_lifetime
);
1314 if (lsp
&& (lsp
->own_lsp
))
1317 /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same
1319 /* for broadcast circuits, snpa should be compared */
1321 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1322 adj
= isis_adj_lookup_snpa(ssnpa
,
1323 circuit
->u
.bc
.adjdb
[level
- 1]);
1326 "(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04x, "
1327 "lifetime %us on %s",
1328 circuit
->area
->area_tag
,
1329 rawlspid_print(hdr
->lsp_id
),
1330 ntohl(hdr
->seq_num
), ntohs(hdr
->checksum
),
1331 ntohs(hdr
->rem_lifetime
),
1332 circuit
->interface
->name
);
1333 return ISIS_WARNING
; /* Silently discard */
1336 /* for non broadcast, we just need to find same level adj */
1338 /* If no adj, or no sharing of level */
1339 if (!circuit
->u
.p2p
.neighbor
) {
1340 return ISIS_OK
; /* Silently discard */
1342 if (((level
== IS_LEVEL_1
)
1343 && (circuit
->u
.p2p
.neighbor
->adj_usage
1344 == ISIS_ADJ_LEVEL2
))
1345 || ((level
== IS_LEVEL_2
)
1346 && (circuit
->u
.p2p
.neighbor
->adj_usage
1347 == ISIS_ADJ_LEVEL1
)))
1348 return ISIS_WARNING
; /* Silently discard */
1353 /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */
1355 /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */
1357 /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do
1360 /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num
1362 * wrong checksum, initiate a purge. */
1363 if (lsp
&& (lsp
->lsp_header
->seq_num
== hdr
->seq_num
)
1364 && (lsp
->lsp_header
->checksum
!= hdr
->checksum
)) {
1366 "ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
1367 circuit
->area
->area_tag
, rawlspid_print(hdr
->lsp_id
),
1368 ntohl(hdr
->seq_num
));
1369 hdr
->rem_lifetime
= 0;
1374 /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */
1375 if (hdr
->rem_lifetime
== 0) {
1377 /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't
1379 /* only needed on explicit update, eg - p2p */
1380 if (circuit
->circ_type
== CIRCUIT_T_P2P
)
1381 ack_lsp(hdr
, circuit
, level
);
1382 return retval
; /* FIXME: do we need a purge? */
1384 if (memcmp(hdr
->lsp_id
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
1385 /* LSP by some other system -> do 7.3.16.4 b) */
1386 /* 7.3.16.4 b) 1) */
1387 if (comp
== LSP_NEWER
) {
1388 lsp_update(lsp
, circuit
->rcv_stream
,
1389 circuit
->area
, level
);
1391 lsp_set_all_srmflags(lsp
);
1393 ISIS_FLAGS_CLEAR_ALL(
1394 lsp
->SSNflags
); /* FIXME:
1399 /* For the case of lsp confusion, flood
1400 * the purge back to its
1401 * originator so that it can react.
1402 * Otherwise, don't reflood
1403 * through incoming circuit as usual */
1404 if (!lsp_confusion
) {
1406 ISIS_CLEAR_FLAG(lsp
->SRMflags
,
1409 if (circuit
->circ_type
1410 != CIRCUIT_T_BROADCAST
)
1415 } /* 7.3.16.4 b) 2) */
1416 else if (comp
== LSP_EQUAL
) {
1418 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1420 if (circuit
->circ_type
1421 != CIRCUIT_T_BROADCAST
)
1422 ISIS_SET_FLAG(lsp
->SSNflags
,
1424 } /* 7.3.16.4 b) 3) */
1426 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1427 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1429 } else if (lsp
->lsp_header
->rem_lifetime
!= 0) {
1430 /* our own LSP -> 7.3.16.4 c) */
1431 if (comp
== LSP_NEWER
) {
1433 ntohl(hdr
->seq_num
));
1434 lsp_set_all_srmflags(lsp
);
1436 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1437 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1439 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
)
1441 "ISIS-Upd (%s): (1) re-originating LSP %s new "
1443 circuit
->area
->area_tag
,
1444 rawlspid_print(hdr
->lsp_id
),
1445 ntohl(lsp
->lsp_header
1451 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1453 if (memcmp(hdr
->lsp_id
, isis
->sysid
, ISIS_SYS_ID_LEN
) == 0) {
1455 /* 7.3.16.4: initiate a purge */
1456 lsp_purge_non_exist(level
, hdr
, circuit
->area
);
1459 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1461 /* In 7.3.16.1, If an Intermediate system R somewhere in the
1463 * has information that the current sequence number for source S
1465 * "greater" than that held by S, ... */
1467 if (ntohl(hdr
->seq_num
) > ntohl(lsp
->lsp_header
->seq_num
)) {
1469 lsp_inc_seqnum(lsp
, ntohl(hdr
->seq_num
));
1470 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
)
1472 "ISIS-Upd (%s): (2) re-originating LSP %s new seq "
1474 circuit
->area
->area_tag
,
1475 rawlspid_print(hdr
->lsp_id
),
1476 ntohl(lsp
->lsp_header
->seq_num
));
1478 /* If the received LSP is older or equal,
1479 * resend the LSP which will act as ACK */
1480 lsp_set_all_srmflags(lsp
);
1482 /* 7.3.15.1 e) - This lsp originated on another system */
1484 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db
1486 if ((!lsp
|| comp
== LSP_NEWER
)) {
1488 * If this lsp is a frag, need to see if we have zero
1491 if (LSP_FRAGMENT(hdr
->lsp_id
) != 0) {
1492 memcpy(lspid
, hdr
->lsp_id
, ISIS_SYS_ID_LEN
+ 1);
1493 LSP_FRAGMENT(lspid
) = 0;
1495 lspid
, circuit
->area
->lspdb
[level
- 1]);
1498 "Got lsp frag, while zero lsp not in database");
1504 lsp
= lsp_new_from_stream_ptr(
1505 circuit
->rcv_stream
, pdu_len
, lsp0
,
1506 circuit
->area
, level
);
1508 circuit
->area
->lspdb
[level
- 1]);
1509 } else /* exists, so we overwrite */
1511 lsp_update(lsp
, circuit
->rcv_stream
,
1512 circuit
->area
, level
);
1515 lsp_set_all_srmflags(lsp
);
1517 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1520 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
)
1521 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1524 /* 7.3.15.1 e) 2) LSP equal to the one in db */
1525 else if (comp
== LSP_EQUAL
) {
1526 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1527 lsp_update(lsp
, circuit
->rcv_stream
, circuit
->area
,
1529 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
)
1530 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1532 /* 7.3.15.1 e) 3) LSP older than the one in db */
1534 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1535 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1542 * Process Sequence Numbers
1544 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1547 static int process_snp(int snp_type
, int level
, struct isis_circuit
*circuit
,
1548 const u_char
*ssnpa
)
1550 int retval
= ISIS_OK
;
1552 char typechar
= ' ';
1554 struct isis_adjacency
*adj
;
1555 struct isis_complete_seqnum_hdr
*chdr
= NULL
;
1556 struct isis_partial_seqnum_hdr
*phdr
= NULL
;
1557 uint32_t found
= 0, expected
= 0, auth_tlv_offset
= 0;
1558 struct isis_lsp
*lsp
;
1559 struct lsp_entry
*entry
;
1560 struct listnode
*node
, *nnode
;
1561 struct listnode
*node2
, *nnode2
;
1563 struct list
*lsp_list
= NULL
;
1564 struct isis_passwd
*passwd
;
1566 if (snp_type
== ISIS_SNP_CSNP_FLAG
) {
1567 /* getting the header info */
1569 chdr
= (struct isis_complete_seqnum_hdr
*)STREAM_PNT(
1570 circuit
->rcv_stream
);
1571 stream_forward_getp(circuit
->rcv_stream
, ISIS_CSNP_HDRLEN
);
1572 pdu_len
= ntohs(chdr
->pdu_len
);
1573 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_CSNP_HDRLEN
)
1574 || pdu_len
> ISO_MTU(circuit
)
1575 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
1576 zlog_warn("Received a CSNP with bogus length %d",
1578 return ISIS_WARNING
;
1582 phdr
= (struct isis_partial_seqnum_hdr
*)STREAM_PNT(
1583 circuit
->rcv_stream
);
1584 stream_forward_getp(circuit
->rcv_stream
, ISIS_PSNP_HDRLEN
);
1585 pdu_len
= ntohs(phdr
->pdu_len
);
1586 if (pdu_len
< (ISIS_FIXED_HDR_LEN
+ ISIS_PSNP_HDRLEN
)
1587 || pdu_len
> ISO_MTU(circuit
)
1588 || pdu_len
> stream_get_endp(circuit
->rcv_stream
)) {
1589 zlog_warn("Received a PSNP with bogus length %d",
1591 return ISIS_WARNING
;
1596 * Set the stream endp to PDU length, ignoring additional padding
1597 * introduced by transport chips.
1599 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
1600 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
1602 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
1603 if (circuit
->ext_domain
) {
1606 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1607 "skipping: circuit externalDomain = true",
1608 circuit
->area
->area_tag
, level
, typechar
,
1609 circuit
->interface
->name
);
1614 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
1615 if (!accept_level(level
, circuit
->is_type
)) {
1618 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1619 "skipping: circuit type %s does not match level %d",
1620 circuit
->area
->area_tag
, level
, typechar
,
1621 circuit
->interface
->name
,
1622 circuit_t2string(circuit
->is_type
), level
);
1627 /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */
1628 if ((snp_type
== ISIS_SNP_PSNP_FLAG
)
1629 && (circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
1630 && (!circuit
->u
.bc
.is_dr
[level
- 1])) {
1632 "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, "
1633 "skipping: we are not the DIS",
1634 circuit
->area
->area_tag
, level
, typechar
,
1635 snpa_print(ssnpa
), circuit
->interface
->name
);
1640 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked
1643 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use
1645 * - already checked */
1647 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same
1649 /* for broadcast circuits, snpa should be compared */
1650 /* FIXME : Do we need to check SNPA? */
1651 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1652 if (snp_type
== ISIS_SNP_CSNP_FLAG
) {
1653 adj
= isis_adj_lookup(chdr
->source_id
,
1654 circuit
->u
.bc
.adjdb
[level
- 1]);
1656 /* a psnp on a broadcast, how lovely of Juniper :) */
1657 adj
= isis_adj_lookup(phdr
->source_id
,
1658 circuit
->u
.bc
.adjdb
[level
- 1]);
1661 return ISIS_OK
; /* Silently discard */
1663 if (!circuit
->u
.p2p
.neighbor
) {
1664 zlog_warn("no p2p neighbor on circuit %s",
1665 circuit
->interface
->name
);
1666 return ISIS_OK
; /* Silently discard */
1670 /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */
1672 /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */
1674 memset(&tlvs
, 0, sizeof(struct tlvs
));
1677 expected
|= TLVFLAG_LSP_ENTRIES
;
1678 expected
|= TLVFLAG_AUTH_INFO
;
1680 auth_tlv_offset
= stream_get_getp(circuit
->rcv_stream
);
1681 retval
= parse_tlvs(circuit
->area
->area_tag
,
1682 STREAM_PNT(circuit
->rcv_stream
),
1683 pdu_len
- stream_get_getp(circuit
->rcv_stream
),
1684 &expected
, &found
, &tlvs
, &auth_tlv_offset
);
1686 if (retval
> ISIS_WARNING
) {
1687 zlog_warn("something went very wrong processing SNP");
1692 if (level
== IS_LEVEL_1
)
1693 passwd
= &circuit
->area
->area_passwd
;
1695 passwd
= &circuit
->area
->domain_passwd
;
1697 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_RECV
)) {
1699 if (!(found
& TLVFLAG_AUTH_INFO
)
1700 || authentication_check(&tlvs
.auth_info
, passwd
,
1701 circuit
->rcv_stream
,
1703 isis_event_auth_failure(circuit
->area
->area_tag
,
1704 "SNP authentication"
1706 phdr
? phdr
->source_id
1714 /* debug isis snp-packets */
1715 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
1716 zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
1717 circuit
->area
->area_tag
, level
, typechar
,
1718 snpa_print(ssnpa
), circuit
->interface
->name
);
1719 if (tlvs
.lsp_entries
) {
1720 for (ALL_LIST_ELEMENTS_RO(tlvs
.lsp_entries
, node
,
1723 "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x,"
1724 " cksum 0x%04x, lifetime %us",
1725 circuit
->area
->area_tag
, typechar
,
1726 rawlspid_print(entry
->lsp_id
),
1727 ntohl(entry
->seq_num
),
1728 ntohs(entry
->checksum
),
1729 ntohs(entry
->rem_lifetime
));
1734 /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
1735 if (tlvs
.lsp_entries
) {
1736 for (ALL_LIST_ELEMENTS_RO(tlvs
.lsp_entries
, node
, entry
)) {
1737 lsp
= lsp_search(entry
->lsp_id
,
1738 circuit
->area
->lspdb
[level
- 1]);
1739 own_lsp
= !memcmp(entry
->lsp_id
, isis
->sysid
,
1742 /* 7.3.15.2 b) 1) is this LSP newer */
1743 cmp
= lsp_compare(circuit
->area
->area_tag
, lsp
,
1746 entry
->rem_lifetime
);
1747 /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p
1749 if (cmp
== LSP_EQUAL
) {
1750 /* if (circuit->circ_type !=
1751 * CIRCUIT_T_BROADCAST) */
1752 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
1754 /* 7.3.15.2 b) 3) if it is older, clear SSN and
1756 else if (cmp
== LSP_OLDER
) {
1757 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1758 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1760 /* 7.3.15.2 b) 4) if it is newer, set SSN and
1766 ntohl(entry
->seq_num
));
1767 ISIS_SET_FLAG(lsp
->SRMflags
,
1770 ISIS_SET_FLAG(lsp
->SSNflags
,
1772 /* if (circuit->circ_type !=
1773 * CIRCUIT_T_BROADCAST) */
1774 ISIS_CLEAR_FLAG(lsp
->SRMflags
,
1779 /* 7.3.15.2 b) 5) if it was not found, and all
1780 * of those are not 0,
1781 * insert it and set SSN on it */
1782 if (entry
->rem_lifetime
&& entry
->checksum
1784 && memcmp(entry
->lsp_id
, isis
->sysid
,
1787 circuit
->area
, entry
->lsp_id
,
1788 ntohs(entry
->rem_lifetime
), 0,
1789 0, entry
->checksum
, level
);
1792 ->lspdb
[level
- 1]);
1793 ISIS_FLAGS_CLEAR_ALL(lsp
->SRMflags
);
1794 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1800 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported
1802 if (snp_type
== ISIS_SNP_CSNP_FLAG
) {
1804 * Build a list from our own LSP db bounded with
1805 * start_lsp_id and stop_lsp_id
1807 lsp_list
= list_new();
1808 lsp_build_list_nonzero_ht(chdr
->start_lsp_id
, chdr
->stop_lsp_id
,
1810 circuit
->area
->lspdb
[level
- 1]);
1812 /* Fixme: Find a better solution */
1813 if (tlvs
.lsp_entries
) {
1814 for (ALL_LIST_ELEMENTS(tlvs
.lsp_entries
, node
, nnode
,
1816 for (ALL_LIST_ELEMENTS(lsp_list
, node2
, nnode2
,
1818 if (lsp_id_cmp(lsp
->lsp_header
->lsp_id
,
1821 list_delete_node(lsp_list
,
1828 /* on remaining LSPs we set SRM (neighbor knew not of) */
1829 for (ALL_LIST_ELEMENTS_RO(lsp_list
, node
, lsp
))
1830 ISIS_SET_FLAG(lsp
->SRMflags
, circuit
);
1832 list_delete(lsp_list
);
1839 static int process_csnp(int level
, struct isis_circuit
*circuit
,
1840 const u_char
*ssnpa
)
1842 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
1844 "ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u",
1845 circuit
->area
->area_tag
, level
,
1846 circuit
->interface
->name
,
1847 circuit_t2string(circuit
->is_type
),
1848 circuit
->circuit_id
);
1849 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
1850 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
1851 stream_get_endp(circuit
->rcv_stream
));
1854 /* Sanity check - FIXME: move to correct place */
1855 if ((stream_get_endp(circuit
->rcv_stream
)
1856 - stream_get_getp(circuit
->rcv_stream
))
1857 < ISIS_CSNP_HDRLEN
) {
1858 zlog_warn("Packet too short ( < %d)", ISIS_CSNP_HDRLEN
);
1859 return ISIS_WARNING
;
1862 return process_snp(ISIS_SNP_CSNP_FLAG
, level
, circuit
, ssnpa
);
1865 static int process_psnp(int level
, struct isis_circuit
*circuit
,
1866 const u_char
*ssnpa
)
1868 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
1870 "ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u",
1871 circuit
->area
->area_tag
, level
,
1872 circuit
->interface
->name
,
1873 circuit_t2string(circuit
->is_type
),
1874 circuit
->circuit_id
);
1875 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
1876 zlog_dump_data(STREAM_DATA(circuit
->rcv_stream
),
1877 stream_get_endp(circuit
->rcv_stream
));
1880 if ((stream_get_endp(circuit
->rcv_stream
)
1881 - stream_get_getp(circuit
->rcv_stream
))
1882 < ISIS_PSNP_HDRLEN
) {
1883 zlog_warn("Packet too short ( < %d)", ISIS_PSNP_HDRLEN
);
1884 return ISIS_WARNING
;
1887 return process_snp(ISIS_SNP_PSNP_FLAG
, level
, circuit
, ssnpa
);
1894 static int isis_handle_pdu(struct isis_circuit
*circuit
, u_char
*ssnpa
)
1896 struct isis_fixed_hdr
*hdr
;
1898 int retval
= ISIS_OK
;
1901 * Let's first read data from stream to the header
1903 hdr
= (struct isis_fixed_hdr
*)STREAM_DATA(circuit
->rcv_stream
);
1905 if ((hdr
->idrp
!= ISO10589_ISIS
) && (hdr
->idrp
!= ISO9542_ESIS
)) {
1906 zlog_err("Not an IS-IS or ES-IS packet IDRP=%02x", hdr
->idrp
);
1910 /* now we need to know if this is an ISO 9542 packet and
1911 * take real good care of it, waaa!
1913 if (hdr
->idrp
== ISO9542_ESIS
) {
1914 zlog_err("No support for ES-IS packet IDRP=%02x", hdr
->idrp
);
1917 stream_set_getp(circuit
->rcv_stream
, ISIS_FIXED_HDR_LEN
);
1920 * and then process it
1923 if (hdr
->length
< ISIS_MINIMUM_FIXED_HDR_LEN
) {
1924 zlog_err("Fixed header length = %d", hdr
->length
);
1928 if (hdr
->version1
!= 1) {
1929 zlog_warn("Unsupported ISIS version %u", hdr
->version1
);
1930 return ISIS_WARNING
;
1933 if ((hdr
->id_len
!= 0) && (hdr
->id_len
!= ISIS_SYS_ID_LEN
)) {
1935 "IDFieldLengthMismatch: ID Length field in a received PDU %u, "
1936 "while the parameter for this IS is %u",
1937 hdr
->id_len
, ISIS_SYS_ID_LEN
);
1941 if (hdr
->version2
!= 1) {
1942 zlog_warn("Unsupported ISIS version %u", hdr
->version2
);
1943 return ISIS_WARNING
;
1946 if (circuit
->is_passive
) {
1947 zlog_warn("Received ISIS PDU on passive circuit %s",
1948 circuit
->interface
->name
);
1949 return ISIS_WARNING
;
1953 if ((hdr
->max_area_addrs
!= 0)
1954 && (hdr
->max_area_addrs
!= isis
->max_area_addrs
)) {
1956 "maximumAreaAddressesMismatch: maximumAreaAdresses in a "
1957 "received PDU %u while the parameter for this IS is %u",
1958 hdr
->max_area_addrs
, isis
->max_area_addrs
);
1962 switch (hdr
->pdu_type
) {
1964 retval
= process_lan_hello(ISIS_LEVEL1
, circuit
, ssnpa
);
1967 retval
= process_lan_hello(ISIS_LEVEL2
, circuit
, ssnpa
);
1970 retval
= process_p2p_hello(circuit
);
1973 retval
= process_lsp(ISIS_LEVEL1
, circuit
, ssnpa
);
1976 retval
= process_lsp(ISIS_LEVEL2
, circuit
, ssnpa
);
1978 case L1_COMPLETE_SEQ_NUM
:
1979 retval
= process_csnp(ISIS_LEVEL1
, circuit
, ssnpa
);
1981 case L2_COMPLETE_SEQ_NUM
:
1982 retval
= process_csnp(ISIS_LEVEL2
, circuit
, ssnpa
);
1984 case L1_PARTIAL_SEQ_NUM
:
1985 retval
= process_psnp(ISIS_LEVEL1
, circuit
, ssnpa
);
1987 case L2_PARTIAL_SEQ_NUM
:
1988 retval
= process_psnp(ISIS_LEVEL2
, circuit
, ssnpa
);
1997 int isis_receive(struct thread
*thread
)
1999 struct isis_circuit
*circuit
;
2000 u_char ssnpa
[ETH_ALEN
];
2006 circuit
= THREAD_ARG(thread
);
2009 circuit
->t_read
= NULL
;
2011 isis_circuit_stream(circuit
, &circuit
->rcv_stream
);
2013 retval
= circuit
->rx(circuit
, ssnpa
);
2015 if (retval
== ISIS_OK
)
2016 retval
= isis_handle_pdu(circuit
, ssnpa
);
2019 * prepare for next packet.
2021 if (!circuit
->is_passive
)
2022 isis_circuit_prepare(circuit
);
2027 /* filling of the fixed isis header */
2028 void fill_fixed_hdr(struct isis_fixed_hdr
*hdr
, u_char pdu_type
)
2030 memset(hdr
, 0, sizeof(struct isis_fixed_hdr
));
2032 hdr
->idrp
= ISO10589_ISIS
;
2037 hdr
->length
= ISIS_LANHELLO_HDRLEN
;
2040 hdr
->length
= ISIS_P2PHELLO_HDRLEN
;
2044 hdr
->length
= ISIS_LSP_HDR_LEN
;
2046 case L1_COMPLETE_SEQ_NUM
:
2047 case L2_COMPLETE_SEQ_NUM
:
2048 hdr
->length
= ISIS_CSNP_HDRLEN
;
2050 case L1_PARTIAL_SEQ_NUM
:
2051 case L2_PARTIAL_SEQ_NUM
:
2052 hdr
->length
= ISIS_PSNP_HDRLEN
;
2055 zlog_warn("fill_fixed_hdr(): unknown pdu type %d", pdu_type
);
2058 hdr
->length
+= ISIS_FIXED_HDR_LEN
;
2059 hdr
->pdu_type
= pdu_type
;
2061 hdr
->id_len
= 0; /* ISIS_SYS_ID_LEN - 0==6 */
2063 hdr
->max_area_addrs
= 0; /* isis->max_area_addrs - 0==3 */
2069 static void fill_fixed_hdr_andstream(struct isis_fixed_hdr
*hdr
,
2070 u_char pdu_type
, struct stream
*stream
)
2072 fill_fixed_hdr(hdr
, pdu_type
);
2074 stream_putc(stream
, hdr
->idrp
);
2075 stream_putc(stream
, hdr
->length
);
2076 stream_putc(stream
, hdr
->version1
);
2077 stream_putc(stream
, hdr
->id_len
);
2078 stream_putc(stream
, hdr
->pdu_type
);
2079 stream_putc(stream
, hdr
->version2
);
2080 stream_putc(stream
, hdr
->reserved
);
2081 stream_putc(stream
, hdr
->max_area_addrs
);
2086 int send_hello(struct isis_circuit
*circuit
, int level
)
2088 struct isis_fixed_hdr fixed_hdr
;
2089 struct isis_lan_hello_hdr hello_hdr
;
2090 struct isis_p2p_hello_hdr p2p_hello_hdr
;
2091 unsigned char hmac_md5_hash
[ISIS_AUTH_MD5_SIZE
];
2092 size_t len_pointer
, length
, auth_tlv_offset
= 0;
2096 if (circuit
->is_passive
)
2099 if (circuit
->interface
->mtu
== 0) {
2100 zlog_warn("circuit has zero MTU");
2101 return ISIS_WARNING
;
2104 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2106 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
2107 if (level
== IS_LEVEL_1
)
2108 fill_fixed_hdr_andstream(&fixed_hdr
, L1_LAN_HELLO
,
2109 circuit
->snd_stream
);
2111 fill_fixed_hdr_andstream(&fixed_hdr
, L2_LAN_HELLO
,
2112 circuit
->snd_stream
);
2114 fill_fixed_hdr_andstream(&fixed_hdr
, P2P_HELLO
,
2115 circuit
->snd_stream
);
2118 * Fill LAN Level 1 or 2 Hello PDU header
2120 memset(&hello_hdr
, 0, sizeof(struct isis_lan_hello_hdr
));
2121 interval
= circuit
->hello_multiplier
[level
- 1]
2122 * circuit
->hello_interval
[level
- 1];
2123 if (interval
> USHRT_MAX
)
2124 interval
= USHRT_MAX
;
2125 hello_hdr
.circuit_t
= circuit
->is_type
;
2126 memcpy(hello_hdr
.source_id
, isis
->sysid
, ISIS_SYS_ID_LEN
);
2127 hello_hdr
.hold_time
= htons((u_int16_t
)interval
);
2129 hello_hdr
.pdu_len
= 0; /* Update the PDU Length later */
2131 stream_get_endp(circuit
->snd_stream
) + 3 + ISIS_SYS_ID_LEN
;
2133 /* copy the shared part of the hello to the p2p hello if needed */
2134 if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
2135 memcpy(&p2p_hello_hdr
, &hello_hdr
, 5 + ISIS_SYS_ID_LEN
);
2136 p2p_hello_hdr
.local_id
= circuit
->circuit_id
;
2137 /* FIXME: need better understanding */
2138 stream_put(circuit
->snd_stream
, &p2p_hello_hdr
,
2139 ISIS_P2PHELLO_HDRLEN
);
2141 hello_hdr
.prio
= circuit
->priority
[level
- 1];
2142 if (level
== IS_LEVEL_1
) {
2143 memcpy(hello_hdr
.lan_id
, circuit
->u
.bc
.l1_desig_is
,
2144 ISIS_SYS_ID_LEN
+ 1);
2145 } else if (level
== IS_LEVEL_2
) {
2146 memcpy(hello_hdr
.lan_id
, circuit
->u
.bc
.l2_desig_is
,
2147 ISIS_SYS_ID_LEN
+ 1);
2149 stream_put(circuit
->snd_stream
, &hello_hdr
,
2150 ISIS_LANHELLO_HDRLEN
);
2154 * Then the variable length part.
2157 /* add circuit password */
2158 switch (circuit
->passwd
.type
) {
2160 case ISIS_PASSWD_TYPE_CLEARTXT
:
2161 if (tlv_add_authinfo(circuit
->passwd
.type
, circuit
->passwd
.len
,
2162 circuit
->passwd
.passwd
,
2163 circuit
->snd_stream
))
2164 return ISIS_WARNING
;
2168 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2169 /* Remember where TLV is written so we can later overwrite the
2171 auth_tlv_offset
= stream_get_endp(circuit
->snd_stream
);
2172 memset(&hmac_md5_hash
, 0, ISIS_AUTH_MD5_SIZE
);
2173 if (tlv_add_authinfo(circuit
->passwd
.type
, ISIS_AUTH_MD5_SIZE
,
2174 hmac_md5_hash
, circuit
->snd_stream
))
2175 return ISIS_WARNING
;
2182 /* Area Addresses TLV */
2183 if (listcount(circuit
->area
->area_addrs
) == 0)
2184 return ISIS_WARNING
;
2185 if (tlv_add_area_addrs(circuit
->area
->area_addrs
, circuit
->snd_stream
))
2186 return ISIS_WARNING
;
2188 /* LAN Neighbors TLV */
2189 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
2190 if (level
== IS_LEVEL_1
&& circuit
->u
.bc
.lan_neighs
[0]
2191 && listcount(circuit
->u
.bc
.lan_neighs
[0]) > 0)
2192 if (tlv_add_lan_neighs(circuit
->u
.bc
.lan_neighs
[0],
2193 circuit
->snd_stream
))
2194 return ISIS_WARNING
;
2195 if (level
== IS_LEVEL_2
&& circuit
->u
.bc
.lan_neighs
[1]
2196 && listcount(circuit
->u
.bc
.lan_neighs
[1]) > 0)
2197 if (tlv_add_lan_neighs(circuit
->u
.bc
.lan_neighs
[1],
2198 circuit
->snd_stream
))
2199 return ISIS_WARNING
;
2202 /* Protocols Supported TLV */
2203 if (circuit
->nlpids
.count
> 0)
2204 if (tlv_add_nlpid(&circuit
->nlpids
, circuit
->snd_stream
))
2205 return ISIS_WARNING
;
2206 /* IP interface Address TLV */
2207 if (circuit
->ip_router
&& circuit
->ip_addrs
2208 && listcount(circuit
->ip_addrs
) > 0)
2209 if (tlv_add_ip_addrs(circuit
->ip_addrs
, circuit
->snd_stream
))
2210 return ISIS_WARNING
;
2215 * TLV gets included if no topology is enabled on the interface,
2216 * if one topology other than #0 is enabled, or if multiple topologies
2219 struct isis_circuit_mt_setting
**mt_settings
;
2220 unsigned int mt_count
;
2222 mt_settings
= circuit_mt_settings(circuit
, &mt_count
);
2223 if ((mt_count
== 0 && area_is_mt(circuit
->area
))
2224 || (mt_count
== 1 && mt_settings
[0]->mtid
!= ISIS_MT_IPV4_UNICAST
)
2225 || (mt_count
> 1)) {
2226 struct list
*mt_info
= list_new();
2227 mt_info
->del
= free_tlv
;
2229 for (unsigned int i
= 0; i
< mt_count
; i
++) {
2230 struct mt_router_info
*info
;
2232 info
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*info
));
2233 info
->mtid
= mt_settings
[i
]->mtid
;
2234 /* overload info is not valid in IIH, so it's not
2236 listnode_add(mt_info
, info
);
2238 tlv_add_mt_router_info(mt_info
, circuit
->snd_stream
);
2242 /* IPv6 Interface Address TLV */
2243 if (circuit
->ipv6_router
&& circuit
->ipv6_link
2244 && listcount(circuit
->ipv6_link
) > 0)
2245 if (tlv_add_ipv6_addrs(circuit
->ipv6_link
, circuit
->snd_stream
))
2246 return ISIS_WARNING
;
2248 if (circuit
->pad_hellos
)
2249 if (tlv_add_padding(circuit
->snd_stream
))
2250 return ISIS_WARNING
;
2252 length
= stream_get_endp(circuit
->snd_stream
);
2253 /* Update PDU length */
2254 stream_putw_at(circuit
->snd_stream
, len_pointer
, (u_int16_t
)length
);
2256 /* For HMAC MD5 we need to compute the md5 hash and store it */
2257 if (circuit
->passwd
.type
== ISIS_PASSWD_TYPE_HMAC_MD5
) {
2258 hmac_md5(STREAM_DATA(circuit
->snd_stream
),
2259 stream_get_endp(circuit
->snd_stream
),
2260 (unsigned char *)&circuit
->passwd
.passwd
,
2261 circuit
->passwd
.len
, (unsigned char *)&hmac_md5_hash
);
2262 /* Copy the hash into the stream */
2263 memcpy(STREAM_DATA(circuit
->snd_stream
) + auth_tlv_offset
+ 3,
2264 hmac_md5_hash
, ISIS_AUTH_MD5_SIZE
);
2267 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
2268 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
2270 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
2271 circuit
->area
->area_tag
, level
,
2272 circuit
->interface
->name
, length
);
2275 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
2276 circuit
->area
->area_tag
,
2277 circuit
->interface
->name
, length
);
2279 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2280 zlog_dump_data(STREAM_DATA(circuit
->snd_stream
),
2281 stream_get_endp(circuit
->snd_stream
));
2284 retval
= circuit
->tx(circuit
, level
);
2285 if (retval
!= ISIS_OK
)
2286 zlog_err("ISIS-Adj (%s): Send L%d IIH on %s failed",
2287 circuit
->area
->area_tag
, level
,
2288 circuit
->interface
->name
);
2293 int send_lan_l1_hello(struct thread
*thread
)
2295 struct isis_circuit
*circuit
;
2298 circuit
= THREAD_ARG(thread
);
2300 circuit
->u
.bc
.t_send_lan_hello
[0] = NULL
;
2302 if (!(circuit
->area
->is_type
& IS_LEVEL_1
)) {
2304 "ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
2305 circuit
->area
->area_tag
);
2309 if (circuit
->u
.bc
.run_dr_elect
[0])
2310 isis_dr_elect(circuit
, 1);
2312 retval
= send_hello(circuit
, 1);
2314 /* set next timer thread */
2315 thread_add_timer(master
, send_lan_l1_hello
, circuit
,
2316 isis_jitter(circuit
->hello_interval
[0], IIH_JITTER
),
2317 &circuit
->u
.bc
.t_send_lan_hello
[0]);
2322 int send_lan_l2_hello(struct thread
*thread
)
2324 struct isis_circuit
*circuit
;
2327 circuit
= THREAD_ARG(thread
);
2329 circuit
->u
.bc
.t_send_lan_hello
[1] = NULL
;
2331 if (!(circuit
->area
->is_type
& IS_LEVEL_2
)) {
2332 zlog_warn("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
2333 circuit
->area
->area_tag
);
2337 if (circuit
->u
.bc
.run_dr_elect
[1])
2338 isis_dr_elect(circuit
, 2);
2340 retval
= send_hello(circuit
, 2);
2342 /* set next timer thread */
2343 thread_add_timer(master
, send_lan_l2_hello
, circuit
,
2344 isis_jitter(circuit
->hello_interval
[1], IIH_JITTER
),
2345 &circuit
->u
.bc
.t_send_lan_hello
[1]);
2350 int send_p2p_hello(struct thread
*thread
)
2352 struct isis_circuit
*circuit
;
2354 circuit
= THREAD_ARG(thread
);
2356 circuit
->u
.p2p
.t_send_p2p_hello
= NULL
;
2358 send_hello(circuit
, 1);
2360 /* set next timer thread */
2361 thread_add_timer(master
, send_p2p_hello
, circuit
,
2362 isis_jitter(circuit
->hello_interval
[1], IIH_JITTER
),
2363 &circuit
->u
.p2p
.t_send_p2p_hello
);
2368 static int build_csnp(int level
, u_char
*start
, u_char
*stop
, struct list
*lsps
,
2369 struct isis_circuit
*circuit
)
2371 struct isis_fixed_hdr fixed_hdr
;
2372 struct isis_passwd
*passwd
;
2375 unsigned char hmac_md5_hash
[ISIS_AUTH_MD5_SIZE
];
2376 unsigned long auth_tlv_offset
= 0;
2377 int retval
= ISIS_OK
;
2379 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2381 if (level
== IS_LEVEL_1
)
2382 fill_fixed_hdr_andstream(&fixed_hdr
, L1_COMPLETE_SEQ_NUM
,
2383 circuit
->snd_stream
);
2385 fill_fixed_hdr_andstream(&fixed_hdr
, L2_COMPLETE_SEQ_NUM
,
2386 circuit
->snd_stream
);
2389 * Fill Level 1 or 2 Complete Sequence Numbers header
2392 lenp
= stream_get_endp(circuit
->snd_stream
);
2393 stream_putw(circuit
->snd_stream
, 0); /* PDU length - when we know it */
2394 /* no need to send the source here, it is always us if we csnp */
2395 stream_put(circuit
->snd_stream
, isis
->sysid
, ISIS_SYS_ID_LEN
);
2396 /* with zero circuit id - ref 9.10, 9.11 */
2397 stream_putc(circuit
->snd_stream
, 0x00);
2399 stream_put(circuit
->snd_stream
, start
, ISIS_SYS_ID_LEN
+ 2);
2400 stream_put(circuit
->snd_stream
, stop
, ISIS_SYS_ID_LEN
+ 2);
2405 if (level
== IS_LEVEL_1
)
2406 passwd
= &circuit
->area
->area_passwd
;
2408 passwd
= &circuit
->area
->domain_passwd
;
2410 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)) {
2411 switch (passwd
->type
) {
2413 case ISIS_PASSWD_TYPE_CLEARTXT
:
2414 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT
,
2415 passwd
->len
, passwd
->passwd
,
2416 circuit
->snd_stream
))
2417 return ISIS_WARNING
;
2421 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2422 /* Remember where TLV is written so we can later
2423 * overwrite the MD5 hash */
2424 auth_tlv_offset
= stream_get_endp(circuit
->snd_stream
);
2425 memset(&hmac_md5_hash
, 0, ISIS_AUTH_MD5_SIZE
);
2426 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5
,
2427 ISIS_AUTH_MD5_SIZE
, hmac_md5_hash
,
2428 circuit
->snd_stream
))
2429 return ISIS_WARNING
;
2437 retval
= tlv_add_lsp_entries(lsps
, circuit
->snd_stream
);
2438 if (retval
!= ISIS_OK
)
2441 length
= (u_int16_t
)stream_get_endp(circuit
->snd_stream
);
2442 /* Update PU length */
2443 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
2445 /* For HMAC MD5 we need to compute the md5 hash and store it */
2446 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)
2447 && passwd
->type
== ISIS_PASSWD_TYPE_HMAC_MD5
) {
2448 hmac_md5(STREAM_DATA(circuit
->snd_stream
),
2449 stream_get_endp(circuit
->snd_stream
),
2450 (unsigned char *)&passwd
->passwd
, passwd
->len
,
2451 (unsigned char *)&hmac_md5_hash
);
2452 /* Copy the hash into the stream */
2453 memcpy(STREAM_DATA(circuit
->snd_stream
) + auth_tlv_offset
+ 3,
2454 hmac_md5_hash
, ISIS_AUTH_MD5_SIZE
);
2461 * Count the maximum number of lsps that can be accomodated by a given size.
2463 static uint16_t get_max_lsp_count(uint16_t size
)
2467 uint16_t remaining_size
;
2469 /* First count the full size TLVs */
2470 tlv_count
= size
/ MAX_LSP_ENTRIES_TLV_SIZE
;
2471 lsp_count
= tlv_count
* (MAX_LSP_ENTRIES_TLV_SIZE
/ LSP_ENTRIES_LEN
);
2473 /* The last TLV, if any */
2474 remaining_size
= size
% MAX_LSP_ENTRIES_TLV_SIZE
;
2475 if (remaining_size
- 2 >= LSP_ENTRIES_LEN
)
2476 lsp_count
+= (remaining_size
- 2) / LSP_ENTRIES_LEN
;
2482 * Calculate the length of Authentication Info. TLV.
2484 static uint16_t auth_tlv_length(int level
, struct isis_circuit
*circuit
)
2486 struct isis_passwd
*passwd
;
2489 if (level
== IS_LEVEL_1
)
2490 passwd
= &circuit
->area
->area_passwd
;
2492 passwd
= &circuit
->area
->domain_passwd
;
2494 /* Also include the length of TLV header */
2495 length
= AUTH_INFO_HDRLEN
;
2496 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)) {
2497 switch (passwd
->type
) {
2499 case ISIS_PASSWD_TYPE_CLEARTXT
:
2500 length
+= passwd
->len
;
2504 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2505 length
+= ISIS_AUTH_MD5_SIZE
;
2517 * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
2519 static uint16_t max_lsps_per_snp(int snp_type
, int level
,
2520 struct isis_circuit
*circuit
)
2526 snp_hdr_len
= ISIS_FIXED_HDR_LEN
;
2527 if (snp_type
== ISIS_SNP_CSNP_FLAG
)
2528 snp_hdr_len
+= ISIS_CSNP_HDRLEN
;
2530 snp_hdr_len
+= ISIS_PSNP_HDRLEN
;
2532 auth_tlv_len
= auth_tlv_length(level
, circuit
);
2533 lsp_count
= get_max_lsp_count(stream_get_size(circuit
->snd_stream
)
2534 - snp_hdr_len
- auth_tlv_len
);
2539 * FIXME: support multiple CSNPs
2542 int send_csnp(struct isis_circuit
*circuit
, int level
)
2544 u_char start
[ISIS_SYS_ID_LEN
+ 2];
2545 u_char stop
[ISIS_SYS_ID_LEN
+ 2];
2546 struct list
*list
= NULL
;
2547 struct listnode
*node
;
2548 struct isis_lsp
*lsp
;
2549 u_char num_lsps
, loop
= 1;
2550 int i
, retval
= ISIS_OK
;
2552 if (circuit
->area
->lspdb
[level
- 1] == NULL
2553 || dict_count(circuit
->area
->lspdb
[level
- 1]) == 0)
2556 memset(start
, 0x00, ISIS_SYS_ID_LEN
+ 2);
2557 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2559 num_lsps
= max_lsps_per_snp(ISIS_SNP_CSNP_FLAG
, level
, circuit
);
2563 lsp_build_list(start
, stop
, num_lsps
, list
,
2564 circuit
->area
->lspdb
[level
- 1]);
2566 * Update the stop lsp_id before encoding this CSNP.
2568 if (listcount(list
) < num_lsps
) {
2569 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2571 node
= listtail(list
);
2572 lsp
= listgetdata(node
);
2573 memcpy(stop
, lsp
->lsp_header
->lsp_id
,
2574 ISIS_SYS_ID_LEN
+ 2);
2577 retval
= build_csnp(level
, start
, stop
, list
, circuit
);
2578 if (retval
!= ISIS_OK
) {
2579 zlog_err("ISIS-Snp (%s): Build L%d CSNP on %s failed",
2580 circuit
->area
->area_tag
, level
,
2581 circuit
->interface
->name
);
2586 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
2588 "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
2589 circuit
->area
->area_tag
, level
,
2590 circuit
->interface
->name
,
2591 stream_get_endp(circuit
->snd_stream
));
2592 for (ALL_LIST_ELEMENTS_RO(list
, node
, lsp
)) {
2594 "ISIS-Snp (%s): CSNP entry %s, seq 0x%08x,"
2595 " cksum 0x%04x, lifetime %us",
2596 circuit
->area
->area_tag
,
2597 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2598 ntohl(lsp
->lsp_header
->seq_num
),
2599 ntohs(lsp
->lsp_header
->checksum
),
2600 ntohs(lsp
->lsp_header
->rem_lifetime
));
2602 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2604 STREAM_DATA(circuit
->snd_stream
),
2605 stream_get_endp(circuit
->snd_stream
));
2608 retval
= circuit
->tx(circuit
, level
);
2609 if (retval
!= ISIS_OK
) {
2610 zlog_err("ISIS-Snp (%s): Send L%d CSNP on %s failed",
2611 circuit
->area
->area_tag
, level
,
2612 circuit
->interface
->name
);
2618 * Start lsp_id of the next CSNP should be one plus the
2619 * stop lsp_id in this current CSNP.
2621 memcpy(start
, stop
, ISIS_SYS_ID_LEN
+ 2);
2623 for (i
= ISIS_SYS_ID_LEN
+ 1; i
>= 0; --i
) {
2624 if (start
[i
] < (u_char
)0xff) {
2630 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2637 int send_l1_csnp(struct thread
*thread
)
2639 struct isis_circuit
*circuit
;
2640 int retval
= ISIS_OK
;
2642 circuit
= THREAD_ARG(thread
);
2645 circuit
->t_send_csnp
[0] = NULL
;
2647 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2648 && circuit
->u
.bc
.is_dr
[0]) {
2649 send_csnp(circuit
, 1);
2651 /* set next timer thread */
2652 thread_add_timer(master
, send_l1_csnp
, circuit
,
2653 isis_jitter(circuit
->csnp_interval
[0], CSNP_JITTER
),
2654 &circuit
->t_send_csnp
[0]);
2659 int send_l2_csnp(struct thread
*thread
)
2661 struct isis_circuit
*circuit
;
2662 int retval
= ISIS_OK
;
2664 circuit
= THREAD_ARG(thread
);
2667 circuit
->t_send_csnp
[1] = NULL
;
2669 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2670 && circuit
->u
.bc
.is_dr
[1]) {
2671 send_csnp(circuit
, 2);
2673 /* set next timer thread */
2674 thread_add_timer(master
, send_l2_csnp
, circuit
,
2675 isis_jitter(circuit
->csnp_interval
[1], CSNP_JITTER
),
2676 &circuit
->t_send_csnp
[1]);
2681 static int build_psnp(int level
, struct isis_circuit
*circuit
,
2684 struct isis_fixed_hdr fixed_hdr
;
2687 struct isis_lsp
*lsp
;
2688 struct isis_passwd
*passwd
;
2689 struct listnode
*node
;
2690 unsigned char hmac_md5_hash
[ISIS_AUTH_MD5_SIZE
];
2691 unsigned long auth_tlv_offset
= 0;
2692 int retval
= ISIS_OK
;
2694 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2696 if (level
== IS_LEVEL_1
)
2697 fill_fixed_hdr_andstream(&fixed_hdr
, L1_PARTIAL_SEQ_NUM
,
2698 circuit
->snd_stream
);
2700 fill_fixed_hdr_andstream(&fixed_hdr
, L2_PARTIAL_SEQ_NUM
,
2701 circuit
->snd_stream
);
2704 * Fill Level 1 or 2 Partial Sequence Numbers header
2706 lenp
= stream_get_endp(circuit
->snd_stream
);
2707 stream_putw(circuit
->snd_stream
, 0); /* PDU length - when we know it */
2708 stream_put(circuit
->snd_stream
, isis
->sysid
, ISIS_SYS_ID_LEN
);
2709 stream_putc(circuit
->snd_stream
, circuit
->idx
);
2715 if (level
== IS_LEVEL_1
)
2716 passwd
= &circuit
->area
->area_passwd
;
2718 passwd
= &circuit
->area
->domain_passwd
;
2720 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)) {
2721 switch (passwd
->type
) {
2723 case ISIS_PASSWD_TYPE_CLEARTXT
:
2724 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT
,
2725 passwd
->len
, passwd
->passwd
,
2726 circuit
->snd_stream
))
2727 return ISIS_WARNING
;
2731 case ISIS_PASSWD_TYPE_HMAC_MD5
:
2732 /* Remember where TLV is written so we can later
2733 * overwrite the MD5 hash */
2734 auth_tlv_offset
= stream_get_endp(circuit
->snd_stream
);
2735 memset(&hmac_md5_hash
, 0, ISIS_AUTH_MD5_SIZE
);
2736 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5
,
2737 ISIS_AUTH_MD5_SIZE
, hmac_md5_hash
,
2738 circuit
->snd_stream
))
2739 return ISIS_WARNING
;
2747 retval
= tlv_add_lsp_entries(lsps
, circuit
->snd_stream
);
2748 if (retval
!= ISIS_OK
)
2751 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
2752 for (ALL_LIST_ELEMENTS_RO(lsps
, node
, lsp
)) {
2754 "ISIS-Snp (%s): PSNP entry %s, seq 0x%08x,"
2755 " cksum 0x%04x, lifetime %us",
2756 circuit
->area
->area_tag
,
2757 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2758 ntohl(lsp
->lsp_header
->seq_num
),
2759 ntohs(lsp
->lsp_header
->checksum
),
2760 ntohs(lsp
->lsp_header
->rem_lifetime
));
2764 length
= (u_int16_t
)stream_get_endp(circuit
->snd_stream
);
2765 /* Update PDU length */
2766 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
2768 /* For HMAC MD5 we need to compute the md5 hash and store it */
2769 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
)
2770 && passwd
->type
== ISIS_PASSWD_TYPE_HMAC_MD5
) {
2771 hmac_md5(STREAM_DATA(circuit
->snd_stream
),
2772 stream_get_endp(circuit
->snd_stream
),
2773 (unsigned char *)&passwd
->passwd
, passwd
->len
,
2774 (unsigned char *)&hmac_md5_hash
);
2775 /* Copy the hash into the stream */
2776 memcpy(STREAM_DATA(circuit
->snd_stream
) + auth_tlv_offset
+ 3,
2777 hmac_md5_hash
, ISIS_AUTH_MD5_SIZE
);
2784 * 7.3.15.4 action on expiration of partial SNP interval
2787 static int send_psnp(int level
, struct isis_circuit
*circuit
)
2789 struct isis_lsp
*lsp
;
2790 struct list
*list
= NULL
;
2791 struct listnode
*node
;
2793 int retval
= ISIS_OK
;
2795 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2796 && circuit
->u
.bc
.is_dr
[level
- 1])
2799 if (circuit
->area
->lspdb
[level
- 1] == NULL
2800 || dict_count(circuit
->area
->lspdb
[level
- 1]) == 0)
2803 if (!circuit
->snd_stream
)
2806 num_lsps
= max_lsps_per_snp(ISIS_SNP_PSNP_FLAG
, level
, circuit
);
2810 lsp_build_list_ssn(circuit
, num_lsps
, list
,
2811 circuit
->area
->lspdb
[level
- 1]);
2813 if (listcount(list
) == 0) {
2818 retval
= build_psnp(level
, circuit
, list
);
2819 if (retval
!= ISIS_OK
) {
2820 zlog_err("ISIS-Snp (%s): Build L%d PSNP on %s failed",
2821 circuit
->area
->area_tag
, level
,
2822 circuit
->interface
->name
);
2827 if (isis
->debugs
& DEBUG_SNP_PACKETS
) {
2829 "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
2830 circuit
->area
->area_tag
, level
,
2831 circuit
->interface
->name
,
2832 stream_get_endp(circuit
->snd_stream
));
2833 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2835 STREAM_DATA(circuit
->snd_stream
),
2836 stream_get_endp(circuit
->snd_stream
));
2839 retval
= circuit
->tx(circuit
, level
);
2840 if (retval
!= ISIS_OK
) {
2841 zlog_err("ISIS-Snp (%s): Send L%d PSNP on %s failed",
2842 circuit
->area
->area_tag
, level
,
2843 circuit
->interface
->name
);
2849 * sending succeeded, we can clear SSN flags of this circuit
2850 * for the LSPs in list
2852 for (ALL_LIST_ELEMENTS_RO(list
, node
, lsp
))
2853 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
2860 int send_l1_psnp(struct thread
*thread
)
2863 struct isis_circuit
*circuit
;
2864 int retval
= ISIS_OK
;
2866 circuit
= THREAD_ARG(thread
);
2869 circuit
->t_send_psnp
[0] = NULL
;
2871 send_psnp(1, circuit
);
2872 /* set next timer thread */
2873 thread_add_timer(master
, send_l1_psnp
, circuit
,
2874 isis_jitter(circuit
->psnp_interval
[0], PSNP_JITTER
),
2875 &circuit
->t_send_psnp
[0]);
2881 * 7.3.15.4 action on expiration of partial SNP interval
2884 int send_l2_psnp(struct thread
*thread
)
2886 struct isis_circuit
*circuit
;
2887 int retval
= ISIS_OK
;
2889 circuit
= THREAD_ARG(thread
);
2892 circuit
->t_send_psnp
[1] = NULL
;
2894 send_psnp(2, circuit
);
2896 /* set next timer thread */
2897 thread_add_timer(master
, send_l2_psnp
, circuit
,
2898 isis_jitter(circuit
->psnp_interval
[1], PSNP_JITTER
),
2899 &circuit
->t_send_psnp
[1]);
2905 * ISO 10589 - 7.3.14.3
2907 int send_lsp(struct thread
*thread
)
2909 struct isis_circuit
*circuit
;
2910 struct isis_lsp
*lsp
;
2911 struct listnode
*node
;
2913 int retval
= ISIS_OK
;
2915 circuit
= THREAD_ARG(thread
);
2918 if (!circuit
->lsp_queue
)
2921 node
= listhead(circuit
->lsp_queue
);
2924 * Handle case where there are no LSPs on the queue. This can
2925 * happen, for instance, if an adjacency goes down before this
2926 * thread gets a chance to run.
2932 * Delete LSP from lsp_queue. If it's still in queue, it is assumed
2933 * as 'transmit pending', but send_lsp may never be called again.
2934 * Retry will happen because SRM flag will not be cleared.
2936 lsp
= listgetdata(node
);
2937 list_delete_node(circuit
->lsp_queue
, node
);
2939 /* Set the last-cleared time if the queue is empty. */
2940 /* TODO: Is is possible that new lsps keep being added to the queue
2941 * that the queue is never empty? */
2942 if (list_isempty(circuit
->lsp_queue
))
2943 circuit
->lsp_queue_last_cleared
= time(NULL
);
2945 if (circuit
->state
!= C_STATE_UP
|| circuit
->is_passive
== 1)
2949 * Do not send if levels do not match
2951 if (!(lsp
->level
& circuit
->is_type
))
2955 * Do not send if we do not have adjacencies in state up on the circuit
2957 if (circuit
->upadjcount
[lsp
->level
- 1] == 0)
2960 /* stream_copy will assert and stop program execution if LSP is larger
2962 * the circuit's MTU. So handle and log this case here. */
2963 if (stream_get_endp(lsp
->pdu
) > stream_get_size(circuit
->snd_stream
)) {
2965 "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x,"
2966 " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu"
2967 " while interface stream size is %zu.",
2968 circuit
->area
->area_tag
, lsp
->level
,
2969 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2970 ntohl(lsp
->lsp_header
->seq_num
),
2971 ntohs(lsp
->lsp_header
->checksum
),
2972 ntohs(lsp
->lsp_header
->rem_lifetime
),
2973 circuit
->interface
->name
, stream_get_endp(lsp
->pdu
),
2974 stream_get_size(circuit
->snd_stream
));
2975 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2976 zlog_dump_data(STREAM_DATA(lsp
->pdu
),
2977 stream_get_endp(lsp
->pdu
));
2978 retval
= ISIS_ERROR
;
2982 /* copy our lsp to the send buffer */
2983 stream_copy(circuit
->snd_stream
, lsp
->pdu
);
2985 if (isis
->debugs
& DEBUG_UPDATE_PACKETS
) {
2987 "ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
2988 " lifetime %us on %s",
2989 circuit
->area
->area_tag
, lsp
->level
,
2990 rawlspid_print(lsp
->lsp_header
->lsp_id
),
2991 ntohl(lsp
->lsp_header
->seq_num
),
2992 ntohs(lsp
->lsp_header
->checksum
),
2993 ntohs(lsp
->lsp_header
->rem_lifetime
),
2994 circuit
->interface
->name
);
2995 if (isis
->debugs
& DEBUG_PACKET_DUMP
)
2996 zlog_dump_data(STREAM_DATA(circuit
->snd_stream
),
2997 stream_get_endp(circuit
->snd_stream
));
3001 retval
= circuit
->tx(circuit
, lsp
->level
);
3002 if (retval
!= ISIS_OK
) {
3003 zlog_err("ISIS-Upd (%s): Send L%d LSP on %s failed %s",
3004 circuit
->area
->area_tag
, lsp
->level
,
3005 circuit
->interface
->name
,
3006 (retval
== ISIS_WARNING
) ? "temporarily"
3012 || (retval
== ISIS_OK
&& circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
3013 || (retval
!= ISIS_OK
&& retval
!= ISIS_WARNING
)) {
3014 /* SRM flag will trigger retransmission. We will not retransmit
3016 * encountered a fatal error.
3017 * On success, they should only be cleared if it's a broadcast
3019 * On a P2P circuit, we will wait for the ack from the neighbor
3023 ISIS_CLEAR_FLAG(lsp
->SRMflags
, circuit
);
3029 int ack_lsp(struct isis_link_state_hdr
*hdr
, struct isis_circuit
*circuit
,
3035 struct isis_fixed_hdr fixed_hdr
;
3037 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
3039 // fill_llc_hdr (stream);
3040 if (level
== IS_LEVEL_1
)
3041 fill_fixed_hdr_andstream(&fixed_hdr
, L1_PARTIAL_SEQ_NUM
,
3042 circuit
->snd_stream
);
3044 fill_fixed_hdr_andstream(&fixed_hdr
, L2_PARTIAL_SEQ_NUM
,
3045 circuit
->snd_stream
);
3048 lenp
= stream_get_endp(circuit
->snd_stream
);
3049 stream_putw(circuit
->snd_stream
, 0); /* PDU length */
3050 stream_put(circuit
->snd_stream
, isis
->sysid
, ISIS_SYS_ID_LEN
);
3051 stream_putc(circuit
->snd_stream
, circuit
->idx
);
3052 stream_putc(circuit
->snd_stream
, 9); /* code */
3053 stream_putc(circuit
->snd_stream
, 16); /* len */
3055 stream_putw(circuit
->snd_stream
, ntohs(hdr
->rem_lifetime
));
3056 stream_put(circuit
->snd_stream
, hdr
->lsp_id
, ISIS_SYS_ID_LEN
+ 2);
3057 stream_putl(circuit
->snd_stream
, ntohl(hdr
->seq_num
));
3058 stream_putw(circuit
->snd_stream
, ntohs(hdr
->checksum
));
3060 length
= (u_int16_t
)stream_get_endp(circuit
->snd_stream
);
3061 /* Update PDU length */
3062 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
3064 retval
= circuit
->tx(circuit
, level
);
3065 if (retval
!= ISIS_OK
)
3066 zlog_err("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
3067 circuit
->area
->area_tag
, level
,
3068 circuit
->interface
->name
);