]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_pdu.c
Correct build TLV functions
[mirror_frr.git] / isisd / isis_pdu.c
1 /*
2 * IS-IS Rout(e)ing protocol - isis_pdu.c
3 * PDU processing
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 *
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)
12 * any later version.
13 *
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
17 * more details.
18 *
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
22 */
23
24 #include <zebra.h>
25
26 #include "memory.h"
27 #include "thread.h"
28 #include "linklist.h"
29 #include "log.h"
30 #include "stream.h"
31 #include "vty.h"
32 #include "hash.h"
33 #include "prefix.h"
34 #include "if.h"
35 #include "checksum.h"
36 #include "md5.h"
37
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"
57
58 #define ISIS_MINIMUM_FIXED_HDR_LEN 15
59 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */
60
61 #ifndef PNBBY
62 #define PNBBY 8
63 #endif /* PNBBY */
64
65 /*
66 * HELPER FUNCS
67 */
68
69 /*
70 * Compares two sets of area addresses
71 */
72 static int area_match(struct list *left, struct list *right)
73 {
74 struct area_addr *addr1, *addr2;
75 struct listnode *node1, *node2;
76
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))
82 return 1; /* match */
83 }
84 }
85
86 return 0; /* mismatch */
87 }
88
89 /*
90 * Checks whether we should accept a PDU of given level
91 */
92 static int accept_level(int level, int circuit_t)
93 {
94 int retval = ((circuit_t & level) == level); /* simple approach */
95
96 return retval;
97 }
98
99 /*
100 * Verify authentication information
101 * Support cleartext and HMAC MD5 authentication
102 */
103 static int authentication_check(struct isis_passwd *remote,
104 struct isis_passwd *local,
105 struct stream *stream, uint32_t auth_tlv_offset)
106 {
107 unsigned char digest[ISIS_AUTH_MD5_SIZE];
108
109 /* Auth fail () - passwd type mismatch */
110 if (local->type != remote->type)
111 return ISIS_ERROR;
112
113 switch (local->type) {
114 /* No authentication required */
115 case ISIS_PASSWD_TYPE_UNUSED:
116 break;
117
118 /* Cleartext (ISO 10589) */
119 case ISIS_PASSWD_TYPE_CLEARTXT:
120 /* Auth fail () - passwd len mismatch */
121 if (remote->len != local->len)
122 return ISIS_ERROR;
123 return memcmp(local->passwd, remote->passwd, local->len);
124
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)
129 return ISIS_ERROR;
130 /* Set the authentication value to 0 before the check */
131 memset(STREAM_DATA(stream) + auth_tlv_offset + 3, 0,
132 ISIS_AUTH_MD5_SIZE);
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);
141
142 default:
143 zlog_err("Unsupported authentication type");
144 return ISIS_ERROR;
145 }
146
147 /* Authentication pass when no authentication is configured */
148 return ISIS_OK;
149 }
150
151 static int lsp_authentication_check(struct stream *stream,
152 struct isis_area *area, int level,
153 struct isis_passwd *passwd)
154 {
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;
158 struct tlvs tlvs;
159 int retval = ISIS_OK;
160
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);
169
170 if (retval != ISIS_OK) {
171 zlog_err(
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));
181 return retval;
182 }
183
184 if (!(found & TLVFLAG_AUTH_INFO)) {
185 zlog_err("No authentication tlv in LSP");
186 return ISIS_ERROR;
187 }
188
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");
192 return ISIS_ERROR;
193 }
194
195 /*
196 * RFC 5304 set checksum and remaining lifetime to zero before
197 * verification and reset to old values after verification.
198 */
199 checksum = hdr->checksum;
200 rem_lifetime = hdr->rem_lifetime;
201 hdr->checksum = 0;
202 hdr->rem_lifetime = 0;
203 retval = authentication_check(&tlvs.auth_info, passwd, stream,
204 auth_tlv_offset);
205 hdr->checksum = checksum;
206 hdr->rem_lifetime = rem_lifetime;
207
208 return retval;
209 }
210
211 /*
212 * Processing helper functions
213 */
214 static void del_addr(void *val)
215 {
216 XFREE(MTYPE_ISIS_TMP, val);
217 }
218
219 static void tlvs_to_adj_area_addrs(struct tlvs *tlvs,
220 struct isis_adjacency *adj)
221 {
222 struct listnode *node;
223 struct area_addr *area_addr, *malloced;
224
225 if (adj->area_addrs) {
226 adj->area_addrs->del = del_addr;
227 list_delete(adj->area_addrs);
228 }
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);
236 }
237 }
238 }
239
240 static int tlvs_to_adj_nlpids(struct tlvs *tlvs, struct isis_adjacency *adj)
241 {
242 int i;
243 struct nlpids *tlv_nlpids;
244
245 if (tlvs->nlpids) {
246
247 tlv_nlpids = tlvs->nlpids;
248 if (tlv_nlpids->count > array_size(adj->nlpids.nlpids))
249 return 1;
250
251 adj->nlpids.count = tlv_nlpids->count;
252
253 for (i = 0; i < tlv_nlpids->count; i++) {
254 adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i];
255 }
256 }
257 return 0;
258 }
259
260 static void tlvs_to_adj_ipv4_addrs(struct tlvs *tlvs,
261 struct isis_adjacency *adj)
262 {
263 struct listnode *node;
264 struct in_addr *ipv4_addr, *malloced;
265
266 if (adj->ipv4_addrs) {
267 adj->ipv4_addrs->del = del_addr;
268 list_delete(adj->ipv4_addrs);
269 }
270 adj->ipv4_addrs = list_new();
271 if (tlvs->ipv4_addrs) {
272 for (ALL_LIST_ELEMENTS_RO(tlvs->ipv4_addrs, node, ipv4_addr)) {
273 malloced =
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);
277 }
278 }
279 }
280
281 static void tlvs_to_adj_ipv6_addrs(struct tlvs *tlvs,
282 struct isis_adjacency *adj)
283 {
284 struct listnode *node;
285 struct in6_addr *ipv6_addr, *malloced;
286
287 if (adj->ipv6_addrs) {
288 adj->ipv6_addrs->del = del_addr;
289 list_delete(adj->ipv6_addrs);
290 }
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);
298 }
299 }
300 }
301
302 /*
303 * RECEIVE SIDE
304 */
305
306 /*
307 * Process P2P IIH
308 * ISO - 10589
309 * Section 8.2.5 - Receiving point-to-point IIH PDUs
310 *
311 */
312 static int process_p2p_hello(struct isis_circuit *circuit)
313 {
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;
318 uint16_t pdu_len;
319 struct tlvs tlvs;
320 int v4_usable = 0, v6_usable = 0;
321
322 if (isis->debugs & DEBUG_ADJ_PACKETS) {
323 zlog_debug(
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));
331 }
332
333 if (circuit->circ_type != CIRCUIT_T_P2P) {
334 zlog_warn("p2p hello on non p2p circuit");
335 return ISIS_WARNING;
336 }
337
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");
342 return ISIS_WARNING;
343 }
344
345 /* 8.2.5.1 PDU acceptance tests */
346
347 /* 8.2.5.1 a) external domain untrue */
348 /* FIXME: not useful at all? */
349
350 /* 8.2.5.1 b) ID Length mismatch */
351 /* checked at the handle_pdu */
352
353 /* 8.2.5.2 IIH PDU Processing */
354
355 /* 8.2.5.2 a) 1) Maximum Area Addresses */
356 /* Already checked, and can also be ommited */
357
358 /*
359 * Get the header
360 */
361 hdr = (struct isis_p2p_hello_hdr *)STREAM_PNT(circuit->rcv_stream);
362 pdu_len = ntohs(hdr->pdu_len);
363
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)) {
367 zlog_warn(
368 "ISIS-Adj (%s): Rcvd P2P IIH from (%s) with "
369 "invalid pdu length %d",
370 circuit->area->area_tag, circuit->interface->name,
371 pdu_len);
372 return ISIS_WARNING;
373 }
374
375 /*
376 * Set the stream endp to PDU length, ignoring additional padding
377 * introduced by transport chips.
378 */
379 if (pdu_len < stream_get_endp(circuit->rcv_stream))
380 stream_set_endp(circuit->rcv_stream, pdu_len);
381
382 stream_forward_getp(circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN);
383
384 /*
385 * Lets get the TLVS now
386 */
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;
393
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);
399
400 if (retval > ISIS_WARNING) {
401 zlog_warn("parse_tlvs() failed");
402 free_tlvs(&tlvs);
403 return retval;
404 };
405
406 if (!(found & TLVFLAG_AREA_ADDRS)) {
407 zlog_warn("No Area addresses TLV in P2P IS to IS hello");
408 free_tlvs(&tlvs);
409 return ISIS_WARNING;
410 }
411
412 if (!(found & TLVFLAG_NLPID)) {
413 zlog_warn("No supported protocols TLV in P2P IS to IS hello");
414 free_tlvs(&tlvs);
415 return ISIS_WARNING;
416 }
417
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,
422 circuit->rcv_stream,
423 auth_tlv_offset)) {
424 isis_event_auth_failure(
425 circuit->area->area_tag,
426 "P2P hello authentication failure",
427 hdr->source_id);
428 free_tlvs(&tlvs);
429 return ISIS_OK;
430 }
431 }
432
433 /*
434 * check if both ends have an IPv4 address
435 */
436 if (circuit->ip_addrs && listcount(circuit->ip_addrs) && tlvs.ipv4_addrs
437 && listcount(tlvs.ipv4_addrs)) {
438 v4_usable = 1;
439 }
440
441 if (found & TLVFLAG_IPV6_ADDR) {
442 /* TBA: check that we have a linklocal ourselves? */
443 struct listnode *node;
444 struct in6_addr *ip;
445 for (ALL_LIST_ELEMENTS_RO(tlvs.ipv6_addrs, node, ip))
446 if (IN6_IS_ADDR_LINKLOCAL(ip)) {
447 v6_usable = 1;
448 break;
449 }
450
451 if (!v6_usable)
452 zlog_warn(
453 "ISIS-Adj: IPv6 addresses present but no link-local "
454 "in P2P IIH from %s\n",
455 circuit->interface->name);
456 }
457
458 if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR)))
459 zlog_warn(
460 "ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n",
461 circuit->interface->name);
462
463 if (!v6_usable && !v4_usable) {
464 free_tlvs(&tlvs);
465 return ISIS_WARNING;
466 }
467
468 /*
469 * it's own p2p IIH PDU - discard
470 */
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);
474 free_tlvs(&tlvs);
475 return ISIS_WARNING;
476 }
477
478 /*
479 * My interpertation of the ISO, if no adj exists we will create one for
480 * the circuit
481 */
482 adj = circuit->u.p2p.neighbor;
483 /* If an adjacency exists, check it is with the source of the hello
484 * packets */
485 if (adj) {
486 if (memcmp(hdr->source_id, adj->sysid, ISIS_SYS_ID_LEN)) {
487 zlog_debug(
488 "hello source and adjacency do not match, set adj down\n");
489 isis_adj_state_change(adj, ISIS_ADJ_DOWN,
490 "adj do not exist");
491 return 0;
492 }
493 }
494 if (!adj || adj->level != hdr->circuit_t) {
495 if (!adj) {
496 adj = isis_new_adj(hdr->source_id, NULL, hdr->circuit_t,
497 circuit);
498 if (adj == NULL)
499 return ISIS_ERROR;
500 } else {
501 adj->level = hdr->circuit_t;
502 }
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.
509 */
510 adj->circuit_t = hdr->circuit_t;
511 isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, NULL);
512 adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
513 }
514
515 /* 8.2.6 Monitoring point-to-point adjacencies */
516 adj->hold_time = ntohs(hdr->hold_time);
517 adj->last_upd = time(NULL);
518
519 /* we do this now because the adj may not survive till the end... */
520 tlvs_to_adj_area_addrs(&tlvs, adj);
521
522 /* which protocol are spoken ??? */
523 if (tlvs_to_adj_nlpids(&tlvs, adj)) {
524 free_tlvs(&tlvs);
525 return ISIS_WARNING;
526 }
527
528 /* we need to copy addresses to the adj */
529 if (found & TLVFLAG_IPV4_ADDR)
530 tlvs_to_adj_ipv4_addrs(&tlvs, adj);
531
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);
541 }
542
543 if (found & TLVFLAG_IPV6_ADDR)
544 tlvs_to_adj_ipv6_addrs(&tlvs, adj);
545
546 bool mt_set_changed =
547 tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj);
548
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,
552 &adj->t_expire);
553
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) {
559 case IS_LEVEL_1:
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,
564 NULL);
565 /* (5) adj usage level 1 */
566 adj->adj_usage = ISIS_ADJ_LEVEL1;
567 } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) {
568 ; /* accept */
569 }
570 break;
571 case IS_LEVEL_2:
572 if (adj->adj_state != ISIS_ADJ_UP) {
573 /* (7) reject - wrong system type event
574 */
575 zlog_warn("wrongSystemType");
576 free_tlvs(&tlvs);
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,
581 ISIS_ADJ_DOWN,
582 "Wrong System");
583 }
584 break;
585 }
586 }
587
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) {
591 case IS_LEVEL_1:
592 if (adj->adj_state != ISIS_ADJ_UP) {
593 /* (6) adj state up */
594 isis_adj_state_change(adj, ISIS_ADJ_UP,
595 NULL);
596 /* (7) adj usage level 1 */
597 adj->adj_usage = ISIS_ADJ_LEVEL1;
598 } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) {
599 ; /* accept */
600 } else if ((adj->adj_usage
601 == ISIS_ADJ_LEVEL1AND2)
602 || (adj->adj_usage
603 == ISIS_ADJ_LEVEL2)) {
604 /* (8) down - wrong system */
605 isis_adj_state_change(adj,
606 ISIS_ADJ_DOWN,
607 "Wrong System");
608 }
609 break;
610 case IS_LEVEL_2:
611 if (adj->adj_state != ISIS_ADJ_UP) {
612 /* (6) adj state up */
613 isis_adj_state_change(adj, ISIS_ADJ_UP,
614 NULL);
615 /* (9) adj usage level 2 */
616 adj->adj_usage = ISIS_ADJ_LEVEL2;
617 } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1)
618 || (adj->adj_usage
619 == ISIS_ADJ_LEVEL1AND2)) {
620 /* (8) down - wrong system */
621 isis_adj_state_change(adj,
622 ISIS_ADJ_DOWN,
623 "Wrong System");
624 } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) {
625 ; /* Accept */
626 }
627 break;
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,
632 NULL);
633 /* (10) adj usage level 1 */
634 adj->adj_usage = ISIS_ADJ_LEVEL1AND2;
635 } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1)
636 || (adj->adj_usage
637 == ISIS_ADJ_LEVEL2)) {
638 /* (8) down - wrong system */
639 isis_adj_state_change(adj,
640 ISIS_ADJ_DOWN,
641 "Wrong System");
642 } else if (adj->adj_usage
643 == ISIS_ADJ_LEVEL1AND2) {
644 ; /* Accept */
645 }
646 break;
647 }
648 }
649
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) {
653 case IS_LEVEL_1:
654 if (adj->adj_state != ISIS_ADJ_UP) {
655 /* (5) reject - wrong system type event
656 */
657 zlog_warn("wrongSystemType");
658 free_tlvs(&tlvs);
659 return ISIS_WARNING; /* Reject */
660 } else if ((adj->adj_usage
661 == ISIS_ADJ_LEVEL1AND2)
662 || (adj->adj_usage
663 == ISIS_ADJ_LEVEL2)) {
664 /* (6) down - wrong system */
665 isis_adj_state_change(adj,
666 ISIS_ADJ_DOWN,
667 "Wrong System");
668 }
669 break;
670 case IS_LEVEL_1_AND_2:
671 case IS_LEVEL_2:
672 if (adj->adj_state != ISIS_ADJ_UP) {
673 /* (7) adj state up */
674 isis_adj_state_change(adj, ISIS_ADJ_UP,
675 NULL);
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,
682 ISIS_ADJ_DOWN,
683 "Wrong System");
684 } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) {
685 ; /* Accept */
686 }
687 break;
688 }
689 }
690 }
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,
697 "Area Mismatch");
698 /* 8.2.5.2 b) 2)is_type L1 and adj is up */
699 } else {
700 isis_adj_state_change(adj, ISIS_ADJ_DOWN,
701 "Down - Area Mismatch");
702 }
703 }
704 /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
705 else {
706 switch (hdr->circuit_t) {
707 case IS_LEVEL_1:
708 if (adj->adj_state != ISIS_ADJ_UP) {
709 /* (6) reject - Area Mismatch event */
710 zlog_warn("AreaMismatch");
711 free_tlvs(&tlvs);
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,
716 ISIS_ADJ_DOWN,
717 "Area Mismatch");
718
719 } else if ((adj->adj_usage
720 == ISIS_ADJ_LEVEL1AND2)
721 || (adj->adj_usage
722 == ISIS_ADJ_LEVEL2)) {
723 /* (7) down - wrong system */
724 isis_adj_state_change(adj,
725 ISIS_ADJ_DOWN,
726 "Wrong System");
727 }
728 break;
729 case IS_LEVEL_1_AND_2:
730 case IS_LEVEL_2:
731 if (adj->adj_state != ISIS_ADJ_UP) {
732 /* (8) adj state up */
733 isis_adj_state_change(adj, ISIS_ADJ_UP,
734 NULL);
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,
740 ISIS_ADJ_DOWN,
741 "Wrong System");
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(
747 adj, ISIS_ADJ_DOWN,
748 "Wrong System");
749 } else {
750 /* (7) down - area mismatch */
751 isis_adj_state_change(
752 adj, ISIS_ADJ_DOWN,
753 "Area Mismatch");
754 }
755 } else if (adj->adj_usage == ISIS_ADJ_LEVEL2) {
756 ; /* Accept */
757 }
758 break;
759 }
760 }
761 } else {
762 /* down - area mismatch */
763 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Area Mismatch");
764 }
765
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),
769 0);
770 }
771
772 /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
773 /* FIXME - Missing parts */
774
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...
777 */
778 switch (adj->adj_usage) {
779 case ISIS_ADJ_LEVEL1:
780 adj->sys_type = ISIS_SYSTYPE_L1_IS;
781 break;
782 case ISIS_ADJ_LEVEL2:
783 adj->sys_type = ISIS_SYSTYPE_L2_IS;
784 break;
785 case ISIS_ADJ_LEVEL1AND2:
786 adj->sys_type = ISIS_SYSTYPE_L2_IS;
787 break;
788 case ISIS_ADJ_NONE:
789 adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
790 break;
791 }
792
793
794 if (isis->debugs & DEBUG_ADJ_PACKETS) {
795 zlog_debug(
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,
800 pdu_len);
801 }
802
803 free_tlvs(&tlvs);
804
805 return retval;
806 }
807
808 /*
809 * Process IS-IS LAN Level 1/2 Hello PDU
810 */
811 static int process_lan_hello(int level, struct isis_circuit *circuit,
812 const u_char *ssnpa)
813 {
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;
818 struct tlvs tlvs;
819 u_char *snpa;
820 struct listnode *node;
821 int v4_usable = 0, v6_usable = 0;
822
823 if (isis->debugs & DEBUG_ADJ_PACKETS) {
824 zlog_debug(
825 "ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, "
826 "cirID %u",
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));
834 }
835
836 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
837 zlog_warn("lan hello on non broadcast circuit");
838 return ISIS_WARNING;
839 }
840
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");
845 return ISIS_WARNING;
846 }
847
848 if (circuit->ext_domain) {
849 zlog_debug(
850 "level %d LAN Hello received over circuit with "
851 "externalDomain = true",
852 level);
853 return ISIS_WARNING;
854 }
855
856 if (!accept_level(level, circuit->is_type)) {
857 if (isis->debugs & DEBUG_ADJ_PACKETS) {
858 zlog_debug(
859 "ISIS-Adj (%s): Interface level mismatch, %s",
860 circuit->area->area_tag,
861 circuit->interface->name);
862 }
863 return ISIS_WARNING;
864 }
865
866 #if 0
867 /* Cisco's debug message compatability */
868 if (!accept_level (level, circuit->area->is_type))
869 {
870 if (isis->debugs & DEBUG_ADJ_PACKETS)
871 {
872 zlog_debug ("ISIS-Adj (%s): is type mismatch",
873 circuit->area->area_tag);
874 }
875 return ISIS_WARNING;
876 }
877 #endif
878 /*
879 * Fill the header
880 */
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);
887
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)) {
891 zlog_warn(
892 "ISIS-Adj (%s): Rcvd LAN IIH from (%s) with "
893 "invalid pdu length %d",
894 circuit->area->area_tag, circuit->interface->name,
895 hdr.pdu_len);
896 return ISIS_WARNING;
897 }
898
899 /*
900 * Set the stream endp to PDU length, ignoring additional padding
901 * introduced by transport chips.
902 */
903 if (hdr.pdu_len < stream_get_endp(circuit->rcv_stream))
904 stream_set_endp(circuit->rcv_stream, hdr.pdu_len);
905
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,
910 hdr.circuit_t);
911 return ISIS_ERROR;
912 }
913
914 /*
915 * Then get the tlvs
916 */
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;
924
925 auth_tlv_offset = stream_get_getp(circuit->rcv_stream);
926 retval = parse_tlvs(
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);
930
931 if (retval > ISIS_WARNING) {
932 zlog_warn("parse_tlvs() failed");
933 goto out;
934 }
935
936 if (!(found & TLVFLAG_AREA_ADDRS)) {
937 zlog_warn(
938 "No Area addresses TLV in Level %d LAN IS to IS hello",
939 level);
940 retval = ISIS_WARNING;
941 goto out;
942 }
943
944 if (!(found & TLVFLAG_NLPID)) {
945 zlog_warn(
946 "No supported protocols TLV in Level %d LAN IS to IS hello",
947 level);
948 retval = ISIS_WARNING;
949 goto out;
950 }
951
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,
956 circuit->rcv_stream,
957 auth_tlv_offset)) {
958 isis_event_auth_failure(
959 circuit->area->area_tag,
960 "LAN hello authentication failure",
961 hdr.source_id);
962 retval = ISIS_WARNING;
963 goto out;
964 }
965 }
966
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);
970 return ISIS_WARNING;
971 }
972
973 /*
974 * Accept the level 1 adjacency only if a match between local and
975 * remote area addresses is found
976 */
977 if (listcount(circuit->area->area_addrs) == 0
978 || (level == IS_LEVEL_1
979 && area_match(circuit->area->area_addrs, tlvs.area_addrs)
980 == 0)) {
981 if (isis->debugs & DEBUG_ADJ_PACKETS) {
982 zlog_debug(
983 "ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
984 circuit->area->area_tag, level,
985 circuit->interface->name);
986 }
987 retval = ISIS_OK;
988 goto out;
989 }
990
991 /*
992 * it's own IIH PDU - discard silently
993 */
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);
997
998 retval = ISIS_OK;
999 goto out;
1000 }
1001
1002 /*
1003 * check if both ends have an IPv4 address
1004 */
1005 if (circuit->ip_addrs && listcount(circuit->ip_addrs) && tlvs.ipv4_addrs
1006 && listcount(tlvs.ipv4_addrs)) {
1007 v4_usable = 1;
1008 }
1009
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)) {
1016 v6_usable = 1;
1017 break;
1018 }
1019
1020 if (!v6_usable)
1021 zlog_warn(
1022 "ISIS-Adj: IPv6 addresses present but no link-local "
1023 "in LAN IIH from %s\n",
1024 circuit->interface->name);
1025 }
1026
1027 if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR)))
1028 zlog_warn(
1029 "ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n",
1030 circuit->interface->name);
1031
1032 if (!v6_usable && !v4_usable) {
1033 free_tlvs(&tlvs);
1034 return ISIS_WARNING;
1035 }
1036
1037
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)) {
1041 if (!adj) {
1042 /*
1043 * Do as in 8.4.2.5
1044 */
1045 adj = isis_new_adj(hdr.source_id, ssnpa, level,
1046 circuit);
1047 if (adj == NULL) {
1048 retval = ISIS_ERROR;
1049 goto out;
1050 }
1051 } else {
1052 if (ssnpa) {
1053 memcpy(adj->snpa, ssnpa, 6);
1054 } else {
1055 memset(adj->snpa, ' ', 6);
1056 }
1057 adj->level = level;
1058 }
1059 isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING, NULL);
1060
1061 if (level == IS_LEVEL_1)
1062 adj->sys_type = ISIS_SYSTYPE_L1_IS;
1063 else
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]);
1068 }
1069
1070 if (adj->dis_record[level - 1].dis == ISIS_IS_DIS)
1071 switch (level) {
1072 case 1:
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,
1077 circuit, 0, NULL);
1078 memcpy(&circuit->u.bc.l1_desig_is, hdr.lan_id,
1079 ISIS_SYS_ID_LEN + 1);
1080 }
1081 break;
1082 case 2:
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,
1087 circuit, 0, NULL);
1088 memcpy(&circuit->u.bc.l2_desig_is, hdr.lan_id,
1089 ISIS_SYS_ID_LEN + 1);
1090 }
1091 break;
1092 }
1093
1094 adj->hold_time = hdr.hold_time;
1095 adj->last_upd = time(NULL);
1096 adj->prio[level - 1] = hdr.prio;
1097
1098 memcpy(adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1);
1099
1100 tlvs_to_adj_area_addrs(&tlvs, adj);
1101
1102 /* which protocol are spoken ??? */
1103 if (tlvs_to_adj_nlpids(&tlvs, adj)) {
1104 retval = ISIS_WARNING;
1105 goto out;
1106 }
1107
1108 /* we need to copy addresses to the adj */
1109 if (found & TLVFLAG_IPV4_ADDR)
1110 tlvs_to_adj_ipv4_addrs(&tlvs, adj);
1111
1112 if (found & TLVFLAG_IPV6_ADDR)
1113 tlvs_to_adj_ipv6_addrs(&tlvs, adj);
1114
1115 adj->circuit_t = hdr.circuit_t;
1116
1117 bool mt_set_changed =
1118 tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj);
1119
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,
1123 &adj->t_expire);
1124
1125 /*
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"
1128 */
1129
1130 if (found & TLVFLAG_LAN_NEIGHS) {
1131 if (adj->adj_state != ISIS_ADJ_UP) {
1132 for (ALL_LIST_ELEMENTS_RO(tlvs.lan_neighs, node,
1133 snpa)) {
1134 if (!memcmp(snpa, circuit->u.bc.snpa,
1135 ETH_ALEN)) {
1136 isis_adj_state_change(
1137 adj, ISIS_ADJ_UP,
1138 "own SNPA found in LAN Neighbours TLV");
1139 }
1140 }
1141 } else {
1142 int found = 0;
1143 for (ALL_LIST_ELEMENTS_RO(tlvs.lan_neighs, node, snpa))
1144 if (!memcmp(snpa, circuit->u.bc.snpa,
1145 ETH_ALEN)) {
1146 found = 1;
1147 break;
1148 }
1149 if (found == 0)
1150 isis_adj_state_change(
1151 adj, ISIS_ADJ_INITIALIZING,
1152 "own SNPA not found in LAN Neighbours TLV");
1153 }
1154 } else if (adj->adj_state == ISIS_ADJ_UP) {
1155 isis_adj_state_change(adj, ISIS_ADJ_INITIALIZING,
1156 "no LAN Neighbours TLV found");
1157 }
1158
1159 if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed)
1160 lsp_regenerate_schedule(adj->circuit->area, level, 0);
1161
1162 out:
1163 if (isis->debugs & DEBUG_ADJ_PACKETS) {
1164 zlog_debug(
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));
1171 }
1172
1173 free_tlvs(&tlvs);
1174
1175 return retval;
1176 }
1177
1178 /*
1179 * Process Level 1/2 Link State
1180 * ISO - 10589
1181 * Section 7.3.15.1 - Action on receipt of a link state PDU
1182 */
1183 static int process_lsp(int level, struct isis_circuit *circuit,
1184 const u_char *ssnpa)
1185 {
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;
1192 uint16_t pdu_len;
1193 int lsp_confusion;
1194
1195 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
1196 zlog_debug(
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));
1205 }
1206
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;
1212 }
1213
1214 /* Reference the header */
1215 hdr = (struct isis_link_state_hdr *)STREAM_PNT(circuit->rcv_stream);
1216 pdu_len = ntohs(hdr->pdu_len);
1217
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),
1224 pdu_len);
1225
1226 return ISIS_WARNING;
1227 }
1228
1229 /*
1230 * Set the stream endp to PDU length, ignoring additional padding
1231 * introduced by transport chips.
1232 */
1233 if (pdu_len < stream_get_endp(circuit->rcv_stream))
1234 stream_set_endp(circuit->rcv_stream, pdu_len);
1235
1236 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
1237 zlog_debug(
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);
1244 }
1245
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),
1251 hdr->lsp_bits);
1252 /* continue as per RFC1122 Be liberal in what you accept, and
1253 * conservative in what you send */
1254 }
1255
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,
1259 hdr->checksum,
1260 offsetof(struct isis_link_state_hdr, checksum)
1261 - 4)) {
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));
1265
1266 return ISIS_WARNING;
1267 }
1268
1269 /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
1270 if (circuit->ext_domain) {
1271 zlog_debug(
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),
1275 level);
1276
1277 return ISIS_WARNING;
1278 }
1279
1280 /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
1281 if (!accept_level(level, circuit->is_type)) {
1282 zlog_debug(
1283 "ISIS-Upd (%s): LSP %s received at level %d over circuit of"
1284 " type %s",
1285 circuit->area->area_tag, rawlspid_print(hdr->lsp_id),
1286 level, circuit_t2string(circuit->is_type));
1287
1288 return ISIS_WARNING;
1289 }
1290
1291 /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
1292
1293 /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use
1294 * 3 */
1295
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);
1299 if (passwd->type) {
1300 if (lsp_authentication_check(circuit->rcv_stream, circuit->area,
1301 level, passwd)) {
1302 isis_event_auth_failure(circuit->area->area_tag,
1303 "LSP authentication failure",
1304 hdr->lsp_id);
1305 return ISIS_WARNING;
1306 }
1307 }
1308 /* Find the LSP in our database and compare it to this Link State header
1309 */
1310 lsp = lsp_search(hdr->lsp_id, circuit->area->lspdb[level - 1]);
1311 if (lsp)
1312 comp = lsp_compare(circuit->area->area_tag, lsp, hdr->seq_num,
1313 hdr->checksum, hdr->rem_lifetime);
1314 if (lsp && (lsp->own_lsp))
1315 goto dontcheckadj;
1316
1317 /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same
1318 * level */
1319 /* for broadcast circuits, snpa should be compared */
1320
1321 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1322 adj = isis_adj_lookup_snpa(ssnpa,
1323 circuit->u.bc.adjdb[level - 1]);
1324 if (!adj) {
1325 zlog_debug(
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 */
1334 }
1335 }
1336 /* for non broadcast, we just need to find same level adj */
1337 else {
1338 /* If no adj, or no sharing of level */
1339 if (!circuit->u.p2p.neighbor) {
1340 return ISIS_OK; /* Silently discard */
1341 } else {
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 */
1349 }
1350 }
1351
1352 dontcheckadj:
1353 /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */
1354
1355 /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */
1356
1357 /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do
1358 * it */
1359
1360 /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num
1361 * but
1362 * wrong checksum, initiate a purge. */
1363 if (lsp && (lsp->lsp_header->seq_num == hdr->seq_num)
1364 && (lsp->lsp_header->checksum != hdr->checksum)) {
1365 zlog_warn(
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;
1370 lsp_confusion = 1;
1371 } else
1372 lsp_confusion = 0;
1373
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) {
1376 if (!lsp) {
1377 /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't
1378 * save */
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? */
1383 } else {
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);
1390 /* ii */
1391 lsp_set_all_srmflags(lsp);
1392 /* v */
1393 ISIS_FLAGS_CLEAR_ALL(
1394 lsp->SSNflags); /* FIXME:
1395 OTHER
1396 than c
1397 */
1398
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) {
1405 /* iii */
1406 ISIS_CLEAR_FLAG(lsp->SRMflags,
1407 circuit);
1408 /* iv */
1409 if (circuit->circ_type
1410 != CIRCUIT_T_BROADCAST)
1411 ISIS_SET_FLAG(
1412 lsp->SSNflags,
1413 circuit);
1414 }
1415 } /* 7.3.16.4 b) 2) */
1416 else if (comp == LSP_EQUAL) {
1417 /* i */
1418 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1419 /* ii */
1420 if (circuit->circ_type
1421 != CIRCUIT_T_BROADCAST)
1422 ISIS_SET_FLAG(lsp->SSNflags,
1423 circuit);
1424 } /* 7.3.16.4 b) 3) */
1425 else {
1426 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1427 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1428 }
1429 } else if (lsp->lsp_header->rem_lifetime != 0) {
1430 /* our own LSP -> 7.3.16.4 c) */
1431 if (comp == LSP_NEWER) {
1432 lsp_inc_seqnum(lsp,
1433 ntohl(hdr->seq_num));
1434 lsp_set_all_srmflags(lsp);
1435 } else {
1436 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1437 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1438 }
1439 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1440 zlog_debug(
1441 "ISIS-Upd (%s): (1) re-originating LSP %s new "
1442 "seq 0x%08x",
1443 circuit->area->area_tag,
1444 rawlspid_print(hdr->lsp_id),
1445 ntohl(lsp->lsp_header
1446 ->seq_num));
1447 }
1448 }
1449 return retval;
1450 }
1451 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1452 * purge */
1453 if (memcmp(hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) {
1454 if (!lsp) {
1455 /* 7.3.16.4: initiate a purge */
1456 lsp_purge_non_exist(level, hdr, circuit->area);
1457 return ISIS_OK;
1458 }
1459 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1460
1461 /* In 7.3.16.1, If an Intermediate system R somewhere in the
1462 * domain
1463 * has information that the current sequence number for source S
1464 * is
1465 * "greater" than that held by S, ... */
1466
1467 if (ntohl(hdr->seq_num) > ntohl(lsp->lsp_header->seq_num)) {
1468 /* 7.3.16.1 */
1469 lsp_inc_seqnum(lsp, ntohl(hdr->seq_num));
1470 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1471 zlog_debug(
1472 "ISIS-Upd (%s): (2) re-originating LSP %s new seq "
1473 "0x%08x",
1474 circuit->area->area_tag,
1475 rawlspid_print(hdr->lsp_id),
1476 ntohl(lsp->lsp_header->seq_num));
1477 }
1478 /* If the received LSP is older or equal,
1479 * resend the LSP which will act as ACK */
1480 lsp_set_all_srmflags(lsp);
1481 } else {
1482 /* 7.3.15.1 e) - This lsp originated on another system */
1483
1484 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db
1485 */
1486 if ((!lsp || comp == LSP_NEWER)) {
1487 /*
1488 * If this lsp is a frag, need to see if we have zero
1489 * lsp present
1490 */
1491 if (LSP_FRAGMENT(hdr->lsp_id) != 0) {
1492 memcpy(lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1);
1493 LSP_FRAGMENT(lspid) = 0;
1494 lsp0 = lsp_search(
1495 lspid, circuit->area->lspdb[level - 1]);
1496 if (!lsp0) {
1497 zlog_debug(
1498 "Got lsp frag, while zero lsp not in database");
1499 return ISIS_OK;
1500 }
1501 }
1502 /* i */
1503 if (!lsp) {
1504 lsp = lsp_new_from_stream_ptr(
1505 circuit->rcv_stream, pdu_len, lsp0,
1506 circuit->area, level);
1507 lsp_insert(lsp,
1508 circuit->area->lspdb[level - 1]);
1509 } else /* exists, so we overwrite */
1510 {
1511 lsp_update(lsp, circuit->rcv_stream,
1512 circuit->area, level);
1513 }
1514 /* ii */
1515 lsp_set_all_srmflags(lsp);
1516 /* iii */
1517 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1518
1519 /* iv */
1520 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1521 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1522 /* FIXME: v) */
1523 }
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,
1528 level);
1529 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1530 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1531 }
1532 /* 7.3.15.1 e) 3) LSP older than the one in db */
1533 else {
1534 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1535 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1536 }
1537 }
1538 return retval;
1539 }
1540
1541 /*
1542 * Process Sequence Numbers
1543 * ISO - 10589
1544 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1545 */
1546
1547 static int process_snp(int snp_type, int level, struct isis_circuit *circuit,
1548 const u_char *ssnpa)
1549 {
1550 int retval = ISIS_OK;
1551 int cmp, own_lsp;
1552 char typechar = ' ';
1553 uint16_t pdu_len;
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;
1562 struct tlvs tlvs;
1563 struct list *lsp_list = NULL;
1564 struct isis_passwd *passwd;
1565
1566 if (snp_type == ISIS_SNP_CSNP_FLAG) {
1567 /* getting the header info */
1568 typechar = 'C';
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",
1577 pdu_len);
1578 return ISIS_WARNING;
1579 }
1580 } else {
1581 typechar = 'P';
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",
1590 pdu_len);
1591 return ISIS_WARNING;
1592 }
1593 }
1594
1595 /*
1596 * Set the stream endp to PDU length, ignoring additional padding
1597 * introduced by transport chips.
1598 */
1599 if (pdu_len < stream_get_endp(circuit->rcv_stream))
1600 stream_set_endp(circuit->rcv_stream, pdu_len);
1601
1602 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
1603 if (circuit->ext_domain) {
1604
1605 zlog_debug(
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);
1610
1611 return ISIS_OK;
1612 }
1613
1614 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
1615 if (!accept_level(level, circuit->is_type)) {
1616
1617 zlog_debug(
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);
1623
1624 return ISIS_OK;
1625 }
1626
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])) {
1631 zlog_debug(
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);
1636
1637 return ISIS_OK;
1638 }
1639
1640 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked
1641 */
1642
1643 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use
1644 * 3
1645 * - already checked */
1646
1647 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same
1648 * level */
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]);
1655 } else {
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]);
1659 }
1660 if (!adj)
1661 return ISIS_OK; /* Silently discard */
1662 } else {
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 */
1667 }
1668 }
1669
1670 /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */
1671
1672 /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */
1673
1674 memset(&tlvs, 0, sizeof(struct tlvs));
1675
1676 /* parse the SNP */
1677 expected |= TLVFLAG_LSP_ENTRIES;
1678 expected |= TLVFLAG_AUTH_INFO;
1679
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);
1685
1686 if (retval > ISIS_WARNING) {
1687 zlog_warn("something went very wrong processing SNP");
1688 free_tlvs(&tlvs);
1689 return retval;
1690 }
1691
1692 if (level == IS_LEVEL_1)
1693 passwd = &circuit->area->area_passwd;
1694 else
1695 passwd = &circuit->area->domain_passwd;
1696
1697 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) {
1698 if (passwd->type) {
1699 if (!(found & TLVFLAG_AUTH_INFO)
1700 || authentication_check(&tlvs.auth_info, passwd,
1701 circuit->rcv_stream,
1702 auth_tlv_offset)) {
1703 isis_event_auth_failure(circuit->area->area_tag,
1704 "SNP authentication"
1705 " failure",
1706 phdr ? phdr->source_id
1707 : chdr->source_id);
1708 free_tlvs(&tlvs);
1709 return ISIS_OK;
1710 }
1711 }
1712 }
1713
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,
1721 entry)) {
1722 zlog_debug(
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));
1730 }
1731 }
1732 }
1733
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,
1740 ISIS_SYS_ID_LEN);
1741 if (lsp) {
1742 /* 7.3.15.2 b) 1) is this LSP newer */
1743 cmp = lsp_compare(circuit->area->area_tag, lsp,
1744 entry->seq_num,
1745 entry->checksum,
1746 entry->rem_lifetime);
1747 /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p
1748 */
1749 if (cmp == LSP_EQUAL) {
1750 /* if (circuit->circ_type !=
1751 * CIRCUIT_T_BROADCAST) */
1752 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1753 }
1754 /* 7.3.15.2 b) 3) if it is older, clear SSN and
1755 set SRM */
1756 else if (cmp == LSP_OLDER) {
1757 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1758 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1759 }
1760 /* 7.3.15.2 b) 4) if it is newer, set SSN and
1761 clear SRM on p2p */
1762 else {
1763 if (own_lsp) {
1764 lsp_inc_seqnum(
1765 lsp,
1766 ntohl(entry->seq_num));
1767 ISIS_SET_FLAG(lsp->SRMflags,
1768 circuit);
1769 } else {
1770 ISIS_SET_FLAG(lsp->SSNflags,
1771 circuit);
1772 /* if (circuit->circ_type !=
1773 * CIRCUIT_T_BROADCAST) */
1774 ISIS_CLEAR_FLAG(lsp->SRMflags,
1775 circuit);
1776 }
1777 }
1778 } else {
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
1783 && entry->seq_num
1784 && memcmp(entry->lsp_id, isis->sysid,
1785 ISIS_SYS_ID_LEN)) {
1786 lsp = lsp_new(
1787 circuit->area, entry->lsp_id,
1788 ntohs(entry->rem_lifetime), 0,
1789 0, entry->checksum, level);
1790 lsp_insert(lsp,
1791 circuit->area
1792 ->lspdb[level - 1]);
1793 ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags);
1794 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1795 }
1796 }
1797 }
1798 }
1799
1800 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported
1801 */
1802 if (snp_type == ISIS_SNP_CSNP_FLAG) {
1803 /*
1804 * Build a list from our own LSP db bounded with
1805 * start_lsp_id and stop_lsp_id
1806 */
1807 lsp_list = list_new();
1808 lsp_build_list_nonzero_ht(chdr->start_lsp_id, chdr->stop_lsp_id,
1809 lsp_list,
1810 circuit->area->lspdb[level - 1]);
1811
1812 /* Fixme: Find a better solution */
1813 if (tlvs.lsp_entries) {
1814 for (ALL_LIST_ELEMENTS(tlvs.lsp_entries, node, nnode,
1815 entry)) {
1816 for (ALL_LIST_ELEMENTS(lsp_list, node2, nnode2,
1817 lsp)) {
1818 if (lsp_id_cmp(lsp->lsp_header->lsp_id,
1819 entry->lsp_id)
1820 == 0) {
1821 list_delete_node(lsp_list,
1822 node2);
1823 break;
1824 }
1825 }
1826 }
1827 }
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);
1831 /* lets free it */
1832 list_delete(lsp_list);
1833 }
1834
1835 free_tlvs(&tlvs);
1836 return retval;
1837 }
1838
1839 static int process_csnp(int level, struct isis_circuit *circuit,
1840 const u_char *ssnpa)
1841 {
1842 if (isis->debugs & DEBUG_SNP_PACKETS) {
1843 zlog_debug(
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));
1852 }
1853
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;
1860 }
1861
1862 return process_snp(ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa);
1863 }
1864
1865 static int process_psnp(int level, struct isis_circuit *circuit,
1866 const u_char *ssnpa)
1867 {
1868 if (isis->debugs & DEBUG_SNP_PACKETS) {
1869 zlog_debug(
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));
1878 }
1879
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;
1885 }
1886
1887 return process_snp(ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa);
1888 }
1889
1890 /*
1891 * PDU Dispatcher
1892 */
1893
1894 static int isis_handle_pdu(struct isis_circuit *circuit, u_char *ssnpa)
1895 {
1896 struct isis_fixed_hdr *hdr;
1897
1898 int retval = ISIS_OK;
1899
1900 /*
1901 * Let's first read data from stream to the header
1902 */
1903 hdr = (struct isis_fixed_hdr *)STREAM_DATA(circuit->rcv_stream);
1904
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);
1907 return ISIS_ERROR;
1908 }
1909
1910 /* now we need to know if this is an ISO 9542 packet and
1911 * take real good care of it, waaa!
1912 */
1913 if (hdr->idrp == ISO9542_ESIS) {
1914 zlog_err("No support for ES-IS packet IDRP=%02x", hdr->idrp);
1915 return ISIS_ERROR;
1916 }
1917 stream_set_getp(circuit->rcv_stream, ISIS_FIXED_HDR_LEN);
1918
1919 /*
1920 * and then process it
1921 */
1922
1923 if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN) {
1924 zlog_err("Fixed header length = %d", hdr->length);
1925 return ISIS_ERROR;
1926 }
1927
1928 if (hdr->version1 != 1) {
1929 zlog_warn("Unsupported ISIS version %u", hdr->version1);
1930 return ISIS_WARNING;
1931 }
1932 /* either 6 or 0 */
1933 if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN)) {
1934 zlog_err(
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);
1938 return ISIS_ERROR;
1939 }
1940
1941 if (hdr->version2 != 1) {
1942 zlog_warn("Unsupported ISIS version %u", hdr->version2);
1943 return ISIS_WARNING;
1944 }
1945
1946 if (circuit->is_passive) {
1947 zlog_warn("Received ISIS PDU on passive circuit %s",
1948 circuit->interface->name);
1949 return ISIS_WARNING;
1950 }
1951
1952 /* either 3 or 0 */
1953 if ((hdr->max_area_addrs != 0)
1954 && (hdr->max_area_addrs != isis->max_area_addrs)) {
1955 zlog_err(
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);
1959 return ISIS_ERROR;
1960 }
1961
1962 switch (hdr->pdu_type) {
1963 case L1_LAN_HELLO:
1964 retval = process_lan_hello(ISIS_LEVEL1, circuit, ssnpa);
1965 break;
1966 case L2_LAN_HELLO:
1967 retval = process_lan_hello(ISIS_LEVEL2, circuit, ssnpa);
1968 break;
1969 case P2P_HELLO:
1970 retval = process_p2p_hello(circuit);
1971 break;
1972 case L1_LINK_STATE:
1973 retval = process_lsp(ISIS_LEVEL1, circuit, ssnpa);
1974 break;
1975 case L2_LINK_STATE:
1976 retval = process_lsp(ISIS_LEVEL2, circuit, ssnpa);
1977 break;
1978 case L1_COMPLETE_SEQ_NUM:
1979 retval = process_csnp(ISIS_LEVEL1, circuit, ssnpa);
1980 break;
1981 case L2_COMPLETE_SEQ_NUM:
1982 retval = process_csnp(ISIS_LEVEL2, circuit, ssnpa);
1983 break;
1984 case L1_PARTIAL_SEQ_NUM:
1985 retval = process_psnp(ISIS_LEVEL1, circuit, ssnpa);
1986 break;
1987 case L2_PARTIAL_SEQ_NUM:
1988 retval = process_psnp(ISIS_LEVEL2, circuit, ssnpa);
1989 break;
1990 default:
1991 return ISIS_ERROR;
1992 }
1993
1994 return retval;
1995 }
1996
1997 int isis_receive(struct thread *thread)
1998 {
1999 struct isis_circuit *circuit;
2000 u_char ssnpa[ETH_ALEN];
2001 int retval;
2002
2003 /*
2004 * Get the circuit
2005 */
2006 circuit = THREAD_ARG(thread);
2007 assert(circuit);
2008
2009 circuit->t_read = NULL;
2010
2011 isis_circuit_stream(circuit, &circuit->rcv_stream);
2012
2013 retval = circuit->rx(circuit, ssnpa);
2014
2015 if (retval == ISIS_OK)
2016 retval = isis_handle_pdu(circuit, ssnpa);
2017
2018 /*
2019 * prepare for next packet.
2020 */
2021 if (!circuit->is_passive)
2022 isis_circuit_prepare(circuit);
2023
2024 return retval;
2025 }
2026
2027 /* filling of the fixed isis header */
2028 void fill_fixed_hdr(struct isis_fixed_hdr *hdr, u_char pdu_type)
2029 {
2030 memset(hdr, 0, sizeof(struct isis_fixed_hdr));
2031
2032 hdr->idrp = ISO10589_ISIS;
2033
2034 switch (pdu_type) {
2035 case L1_LAN_HELLO:
2036 case L2_LAN_HELLO:
2037 hdr->length = ISIS_LANHELLO_HDRLEN;
2038 break;
2039 case P2P_HELLO:
2040 hdr->length = ISIS_P2PHELLO_HDRLEN;
2041 break;
2042 case L1_LINK_STATE:
2043 case L2_LINK_STATE:
2044 hdr->length = ISIS_LSP_HDR_LEN;
2045 break;
2046 case L1_COMPLETE_SEQ_NUM:
2047 case L2_COMPLETE_SEQ_NUM:
2048 hdr->length = ISIS_CSNP_HDRLEN;
2049 break;
2050 case L1_PARTIAL_SEQ_NUM:
2051 case L2_PARTIAL_SEQ_NUM:
2052 hdr->length = ISIS_PSNP_HDRLEN;
2053 break;
2054 default:
2055 zlog_warn("fill_fixed_hdr(): unknown pdu type %d", pdu_type);
2056 return;
2057 }
2058 hdr->length += ISIS_FIXED_HDR_LEN;
2059 hdr->pdu_type = pdu_type;
2060 hdr->version1 = 1;
2061 hdr->id_len = 0; /* ISIS_SYS_ID_LEN - 0==6 */
2062 hdr->version2 = 1;
2063 hdr->max_area_addrs = 0; /* isis->max_area_addrs - 0==3 */
2064 }
2065
2066 /*
2067 * SEND SIDE
2068 */
2069 static void fill_fixed_hdr_andstream(struct isis_fixed_hdr *hdr,
2070 u_char pdu_type, struct stream *stream)
2071 {
2072 fill_fixed_hdr(hdr, pdu_type);
2073
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);
2082
2083 return;
2084 }
2085
2086 int send_hello(struct isis_circuit *circuit, int level)
2087 {
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;
2093 u_int32_t interval;
2094 int retval;
2095
2096 if (circuit->is_passive)
2097 return ISIS_OK;
2098
2099 if (circuit->interface->mtu == 0) {
2100 zlog_warn("circuit has zero MTU");
2101 return ISIS_WARNING;
2102 }
2103
2104 isis_circuit_stream(circuit, &circuit->snd_stream);
2105
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);
2110 else
2111 fill_fixed_hdr_andstream(&fixed_hdr, L2_LAN_HELLO,
2112 circuit->snd_stream);
2113 else
2114 fill_fixed_hdr_andstream(&fixed_hdr, P2P_HELLO,
2115 circuit->snd_stream);
2116
2117 /*
2118 * Fill LAN Level 1 or 2 Hello PDU header
2119 */
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);
2128
2129 hello_hdr.pdu_len = 0; /* Update the PDU Length later */
2130 len_pointer =
2131 stream_get_endp(circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN;
2132
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);
2140 } else {
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);
2148 }
2149 stream_put(circuit->snd_stream, &hello_hdr,
2150 ISIS_LANHELLO_HDRLEN);
2151 }
2152
2153 /*
2154 * Then the variable length part.
2155 */
2156
2157 /* add circuit password */
2158 switch (circuit->passwd.type) {
2159 /* Cleartext */
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;
2165 break;
2166
2167 /* HMAC MD5 */
2168 case ISIS_PASSWD_TYPE_HMAC_MD5:
2169 /* Remember where TLV is written so we can later overwrite the
2170 * MD5 hash */
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;
2176 break;
2177
2178 default:
2179 break;
2180 }
2181
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;
2187
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;
2200 }
2201
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;
2211
2212 /*
2213 * MT Supported TLV
2214 *
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
2217 * are enabled.
2218 */
2219 struct isis_circuit_mt_setting **mt_settings;
2220 unsigned int mt_count;
2221
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;
2228
2229 for (unsigned int i = 0; i < mt_count; i++) {
2230 struct mt_router_info *info;
2231
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
2235 * included here */
2236 listnode_add(mt_info, info);
2237 }
2238 tlv_add_mt_router_info(mt_info, circuit->snd_stream);
2239 list_free(mt_info);
2240 }
2241
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;
2247
2248 if (circuit->pad_hellos)
2249 if (tlv_add_padding(circuit->snd_stream))
2250 return ISIS_WARNING;
2251
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);
2255
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);
2265 }
2266
2267 if (isis->debugs & DEBUG_ADJ_PACKETS) {
2268 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2269 zlog_debug(
2270 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
2271 circuit->area->area_tag, level,
2272 circuit->interface->name, length);
2273 } else {
2274 zlog_debug(
2275 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
2276 circuit->area->area_tag,
2277 circuit->interface->name, length);
2278 }
2279 if (isis->debugs & DEBUG_PACKET_DUMP)
2280 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2281 stream_get_endp(circuit->snd_stream));
2282 }
2283
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);
2289
2290 return retval;
2291 }
2292
2293 int send_lan_l1_hello(struct thread *thread)
2294 {
2295 struct isis_circuit *circuit;
2296 int retval;
2297
2298 circuit = THREAD_ARG(thread);
2299 assert(circuit);
2300 circuit->u.bc.t_send_lan_hello[0] = NULL;
2301
2302 if (!(circuit->area->is_type & IS_LEVEL_1)) {
2303 zlog_warn(
2304 "ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
2305 circuit->area->area_tag);
2306 return 1;
2307 }
2308
2309 if (circuit->u.bc.run_dr_elect[0])
2310 isis_dr_elect(circuit, 1);
2311
2312 retval = send_hello(circuit, 1);
2313
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]);
2318
2319 return retval;
2320 }
2321
2322 int send_lan_l2_hello(struct thread *thread)
2323 {
2324 struct isis_circuit *circuit;
2325 int retval;
2326
2327 circuit = THREAD_ARG(thread);
2328 assert(circuit);
2329 circuit->u.bc.t_send_lan_hello[1] = NULL;
2330
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);
2334 return 1;
2335 }
2336
2337 if (circuit->u.bc.run_dr_elect[1])
2338 isis_dr_elect(circuit, 2);
2339
2340 retval = send_hello(circuit, 2);
2341
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]);
2346
2347 return retval;
2348 }
2349
2350 int send_p2p_hello(struct thread *thread)
2351 {
2352 struct isis_circuit *circuit;
2353
2354 circuit = THREAD_ARG(thread);
2355 assert(circuit);
2356 circuit->u.p2p.t_send_p2p_hello = NULL;
2357
2358 send_hello(circuit, 1);
2359
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);
2364
2365 return ISIS_OK;
2366 }
2367
2368 static int build_csnp(int level, u_char *start, u_char *stop, struct list *lsps,
2369 struct isis_circuit *circuit)
2370 {
2371 struct isis_fixed_hdr fixed_hdr;
2372 struct isis_passwd *passwd;
2373 unsigned long lenp;
2374 u_int16_t length;
2375 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2376 unsigned long auth_tlv_offset = 0;
2377 int retval = ISIS_OK;
2378
2379 isis_circuit_stream(circuit, &circuit->snd_stream);
2380
2381 if (level == IS_LEVEL_1)
2382 fill_fixed_hdr_andstream(&fixed_hdr, L1_COMPLETE_SEQ_NUM,
2383 circuit->snd_stream);
2384 else
2385 fill_fixed_hdr_andstream(&fixed_hdr, L2_COMPLETE_SEQ_NUM,
2386 circuit->snd_stream);
2387
2388 /*
2389 * Fill Level 1 or 2 Complete Sequence Numbers header
2390 */
2391
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);
2398
2399 stream_put(circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2);
2400 stream_put(circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2);
2401
2402 /*
2403 * And TLVs
2404 */
2405 if (level == IS_LEVEL_1)
2406 passwd = &circuit->area->area_passwd;
2407 else
2408 passwd = &circuit->area->domain_passwd;
2409
2410 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2411 switch (passwd->type) {
2412 /* Cleartext */
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;
2418 break;
2419
2420 /* HMAC MD5 */
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;
2430 break;
2431
2432 default:
2433 break;
2434 }
2435 }
2436
2437 retval = tlv_add_lsp_entries(lsps, circuit->snd_stream);
2438 if (retval != ISIS_OK)
2439 return retval;
2440
2441 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
2442 /* Update PU length */
2443 stream_putw_at(circuit->snd_stream, lenp, length);
2444
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);
2455 }
2456
2457 return retval;
2458 }
2459
2460 /*
2461 * Count the maximum number of lsps that can be accomodated by a given size.
2462 */
2463 static uint16_t get_max_lsp_count(uint16_t size)
2464 {
2465 uint16_t tlv_count;
2466 uint16_t lsp_count;
2467 uint16_t remaining_size;
2468
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);
2472
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;
2477
2478 return lsp_count;
2479 }
2480
2481 /*
2482 * Calculate the length of Authentication Info. TLV.
2483 */
2484 static uint16_t auth_tlv_length(int level, struct isis_circuit *circuit)
2485 {
2486 struct isis_passwd *passwd;
2487 uint16_t length;
2488
2489 if (level == IS_LEVEL_1)
2490 passwd = &circuit->area->area_passwd;
2491 else
2492 passwd = &circuit->area->domain_passwd;
2493
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) {
2498 /* Cleartext */
2499 case ISIS_PASSWD_TYPE_CLEARTXT:
2500 length += passwd->len;
2501 break;
2502
2503 /* HMAC MD5 */
2504 case ISIS_PASSWD_TYPE_HMAC_MD5:
2505 length += ISIS_AUTH_MD5_SIZE;
2506 break;
2507
2508 default:
2509 break;
2510 }
2511 }
2512
2513 return length;
2514 }
2515
2516 /*
2517 * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
2518 */
2519 static uint16_t max_lsps_per_snp(int snp_type, int level,
2520 struct isis_circuit *circuit)
2521 {
2522 int snp_hdr_len;
2523 int auth_tlv_len;
2524 uint16_t lsp_count;
2525
2526 snp_hdr_len = ISIS_FIXED_HDR_LEN;
2527 if (snp_type == ISIS_SNP_CSNP_FLAG)
2528 snp_hdr_len += ISIS_CSNP_HDRLEN;
2529 else
2530 snp_hdr_len += ISIS_PSNP_HDRLEN;
2531
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);
2535 return lsp_count;
2536 }
2537
2538 /*
2539 * FIXME: support multiple CSNPs
2540 */
2541
2542 int send_csnp(struct isis_circuit *circuit, int level)
2543 {
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;
2551
2552 if (circuit->area->lspdb[level - 1] == NULL
2553 || dict_count(circuit->area->lspdb[level - 1]) == 0)
2554 return retval;
2555
2556 memset(start, 0x00, ISIS_SYS_ID_LEN + 2);
2557 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2558
2559 num_lsps = max_lsps_per_snp(ISIS_SNP_CSNP_FLAG, level, circuit);
2560
2561 while (loop) {
2562 list = list_new();
2563 lsp_build_list(start, stop, num_lsps, list,
2564 circuit->area->lspdb[level - 1]);
2565 /*
2566 * Update the stop lsp_id before encoding this CSNP.
2567 */
2568 if (listcount(list) < num_lsps) {
2569 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2570 } else {
2571 node = listtail(list);
2572 lsp = listgetdata(node);
2573 memcpy(stop, lsp->lsp_header->lsp_id,
2574 ISIS_SYS_ID_LEN + 2);
2575 }
2576
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);
2582 list_delete(list);
2583 return retval;
2584 }
2585
2586 if (isis->debugs & DEBUG_SNP_PACKETS) {
2587 zlog_debug(
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)) {
2593 zlog_debug(
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));
2601 }
2602 if (isis->debugs & DEBUG_PACKET_DUMP)
2603 zlog_dump_data(
2604 STREAM_DATA(circuit->snd_stream),
2605 stream_get_endp(circuit->snd_stream));
2606 }
2607
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);
2613 list_delete(list);
2614 return retval;
2615 }
2616
2617 /*
2618 * Start lsp_id of the next CSNP should be one plus the
2619 * stop lsp_id in this current CSNP.
2620 */
2621 memcpy(start, stop, ISIS_SYS_ID_LEN + 2);
2622 loop = 0;
2623 for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) {
2624 if (start[i] < (u_char)0xff) {
2625 start[i] += 1;
2626 loop = 1;
2627 break;
2628 }
2629 }
2630 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2631 list_delete(list);
2632 }
2633
2634 return retval;
2635 }
2636
2637 int send_l1_csnp(struct thread *thread)
2638 {
2639 struct isis_circuit *circuit;
2640 int retval = ISIS_OK;
2641
2642 circuit = THREAD_ARG(thread);
2643 assert(circuit);
2644
2645 circuit->t_send_csnp[0] = NULL;
2646
2647 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2648 && circuit->u.bc.is_dr[0]) {
2649 send_csnp(circuit, 1);
2650 }
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]);
2655
2656 return retval;
2657 }
2658
2659 int send_l2_csnp(struct thread *thread)
2660 {
2661 struct isis_circuit *circuit;
2662 int retval = ISIS_OK;
2663
2664 circuit = THREAD_ARG(thread);
2665 assert(circuit);
2666
2667 circuit->t_send_csnp[1] = NULL;
2668
2669 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2670 && circuit->u.bc.is_dr[1]) {
2671 send_csnp(circuit, 2);
2672 }
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]);
2677
2678 return retval;
2679 }
2680
2681 static int build_psnp(int level, struct isis_circuit *circuit,
2682 struct list *lsps)
2683 {
2684 struct isis_fixed_hdr fixed_hdr;
2685 unsigned long lenp;
2686 u_int16_t length;
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;
2693
2694 isis_circuit_stream(circuit, &circuit->snd_stream);
2695
2696 if (level == IS_LEVEL_1)
2697 fill_fixed_hdr_andstream(&fixed_hdr, L1_PARTIAL_SEQ_NUM,
2698 circuit->snd_stream);
2699 else
2700 fill_fixed_hdr_andstream(&fixed_hdr, L2_PARTIAL_SEQ_NUM,
2701 circuit->snd_stream);
2702
2703 /*
2704 * Fill Level 1 or 2 Partial Sequence Numbers header
2705 */
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);
2710
2711 /*
2712 * And TLVs
2713 */
2714
2715 if (level == IS_LEVEL_1)
2716 passwd = &circuit->area->area_passwd;
2717 else
2718 passwd = &circuit->area->domain_passwd;
2719
2720 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2721 switch (passwd->type) {
2722 /* Cleartext */
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;
2728 break;
2729
2730 /* HMAC MD5 */
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;
2740 break;
2741
2742 default:
2743 break;
2744 }
2745 }
2746
2747 retval = tlv_add_lsp_entries(lsps, circuit->snd_stream);
2748 if (retval != ISIS_OK)
2749 return retval;
2750
2751 if (isis->debugs & DEBUG_SNP_PACKETS) {
2752 for (ALL_LIST_ELEMENTS_RO(lsps, node, lsp)) {
2753 zlog_debug(
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));
2761 }
2762 }
2763
2764 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
2765 /* Update PDU length */
2766 stream_putw_at(circuit->snd_stream, lenp, length);
2767
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);
2778 }
2779
2780 return ISIS_OK;
2781 }
2782
2783 /*
2784 * 7.3.15.4 action on expiration of partial SNP interval
2785 * level 1
2786 */
2787 static int send_psnp(int level, struct isis_circuit *circuit)
2788 {
2789 struct isis_lsp *lsp;
2790 struct list *list = NULL;
2791 struct listnode *node;
2792 u_char num_lsps;
2793 int retval = ISIS_OK;
2794
2795 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2796 && circuit->u.bc.is_dr[level - 1])
2797 return ISIS_OK;
2798
2799 if (circuit->area->lspdb[level - 1] == NULL
2800 || dict_count(circuit->area->lspdb[level - 1]) == 0)
2801 return ISIS_OK;
2802
2803 if (!circuit->snd_stream)
2804 return ISIS_ERROR;
2805
2806 num_lsps = max_lsps_per_snp(ISIS_SNP_PSNP_FLAG, level, circuit);
2807
2808 while (1) {
2809 list = list_new();
2810 lsp_build_list_ssn(circuit, num_lsps, list,
2811 circuit->area->lspdb[level - 1]);
2812
2813 if (listcount(list) == 0) {
2814 list_delete(list);
2815 return ISIS_OK;
2816 }
2817
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);
2823 list_delete(list);
2824 return retval;
2825 }
2826
2827 if (isis->debugs & DEBUG_SNP_PACKETS) {
2828 zlog_debug(
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)
2834 zlog_dump_data(
2835 STREAM_DATA(circuit->snd_stream),
2836 stream_get_endp(circuit->snd_stream));
2837 }
2838
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);
2844 list_delete(list);
2845 return retval;
2846 }
2847
2848 /*
2849 * sending succeeded, we can clear SSN flags of this circuit
2850 * for the LSPs in list
2851 */
2852 for (ALL_LIST_ELEMENTS_RO(list, node, lsp))
2853 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
2854 list_delete(list);
2855 }
2856
2857 return retval;
2858 }
2859
2860 int send_l1_psnp(struct thread *thread)
2861 {
2862
2863 struct isis_circuit *circuit;
2864 int retval = ISIS_OK;
2865
2866 circuit = THREAD_ARG(thread);
2867 assert(circuit);
2868
2869 circuit->t_send_psnp[0] = NULL;
2870
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]);
2876
2877 return retval;
2878 }
2879
2880 /*
2881 * 7.3.15.4 action on expiration of partial SNP interval
2882 * level 2
2883 */
2884 int send_l2_psnp(struct thread *thread)
2885 {
2886 struct isis_circuit *circuit;
2887 int retval = ISIS_OK;
2888
2889 circuit = THREAD_ARG(thread);
2890 assert(circuit);
2891
2892 circuit->t_send_psnp[1] = NULL;
2893
2894 send_psnp(2, circuit);
2895
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]);
2900
2901 return retval;
2902 }
2903
2904 /*
2905 * ISO 10589 - 7.3.14.3
2906 */
2907 int send_lsp(struct thread *thread)
2908 {
2909 struct isis_circuit *circuit;
2910 struct isis_lsp *lsp;
2911 struct listnode *node;
2912 int clear_srm = 1;
2913 int retval = ISIS_OK;
2914
2915 circuit = THREAD_ARG(thread);
2916 assert(circuit);
2917
2918 if (!circuit->lsp_queue)
2919 return ISIS_OK;
2920
2921 node = listhead(circuit->lsp_queue);
2922
2923 /*
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.
2927 */
2928 if (!node)
2929 return ISIS_OK;
2930
2931 /*
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.
2935 */
2936 lsp = listgetdata(node);
2937 list_delete_node(circuit->lsp_queue, node);
2938
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);
2944
2945 if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
2946 goto out;
2947
2948 /*
2949 * Do not send if levels do not match
2950 */
2951 if (!(lsp->level & circuit->is_type))
2952 goto out;
2953
2954 /*
2955 * Do not send if we do not have adjacencies in state up on the circuit
2956 */
2957 if (circuit->upadjcount[lsp->level - 1] == 0)
2958 goto out;
2959
2960 /* stream_copy will assert and stop program execution if LSP is larger
2961 * than
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)) {
2964 zlog_err(
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;
2979 goto out;
2980 }
2981
2982 /* copy our lsp to the send buffer */
2983 stream_copy(circuit->snd_stream, lsp->pdu);
2984
2985 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
2986 zlog_debug(
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));
2998 }
2999
3000 clear_srm = 0;
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"
3007 : "permanently");
3008 }
3009
3010 out:
3011 if (clear_srm
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
3015 * if we
3016 * encountered a fatal error.
3017 * On success, they should only be cleared if it's a broadcast
3018 * circuit.
3019 * On a P2P circuit, we will wait for the ack from the neighbor
3020 * to clear
3021 * the fag.
3022 */
3023 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
3024 }
3025
3026 return retval;
3027 }
3028
3029 int ack_lsp(struct isis_link_state_hdr *hdr, struct isis_circuit *circuit,
3030 int level)
3031 {
3032 unsigned long lenp;
3033 int retval;
3034 u_int16_t length;
3035 struct isis_fixed_hdr fixed_hdr;
3036
3037 isis_circuit_stream(circuit, &circuit->snd_stream);
3038
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);
3043 else
3044 fill_fixed_hdr_andstream(&fixed_hdr, L2_PARTIAL_SEQ_NUM,
3045 circuit->snd_stream);
3046
3047
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 */
3054
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));
3059
3060 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
3061 /* Update PDU length */
3062 stream_putw_at(circuit->snd_stream, lenp, length);
3063
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);
3069
3070 return retval;
3071 }