]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_pdu.c
isisd: Don't use structs to encode/decode PDU header
[mirror_frr.git] / isisd / isis_pdu.c
CommitLineData
eb5d44eb 1/*
d62a17ae 2 * IS-IS Rout(e)ing protocol - isis_pdu.c
eb5d44eb 3 * PDU processing
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
d62a17ae 6 * Tampere University of Technology
eb5d44eb 7 * Institute of Communications Engineering
8 *
d62a17ae 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)
eb5d44eb 12 * any later version.
13 *
d62a17ae 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
eb5d44eb 17 * more details.
896014f4
DL
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
eb5d44eb 22 */
23
eb5d44eb 24#include <zebra.h>
eb5d44eb 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"
3f045a08 32#include "hash.h"
eb5d44eb 33#include "prefix.h"
34#include "if.h"
6a270cd9 35#include "checksum.h"
3f045a08 36#include "md5.h"
eb5d44eb 37
38#include "isisd/dict.h"
eb5d44eb 39#include "isisd/isis_constants.h"
40#include "isisd/isis_common.h"
3f045a08 41#include "isisd/isis_flags.h"
eb5d44eb 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"
eb5d44eb 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"
f8c06e2c 55#include "isisd/isis_te.h"
99894f9a 56#include "isisd/isis_mt.h"
eb5d44eb 57
eb5d44eb 58#define ISIS_MINIMUM_FIXED_HDR_LEN 15
f390d2c7 59#define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */
eb5d44eb 60
61#ifndef PNBBY
62#define PNBBY 8
63#endif /* PNBBY */
64
eb5d44eb 65/*
66 * HELPER FUNCS
67 */
68
69/*
70 * Compares two sets of area addresses
71 */
d62a17ae 72static int area_match(struct list *left, struct list *right)
eb5d44eb 73{
d62a17ae 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 }
eb5d44eb 85
d62a17ae 86 return 0; /* mismatch */
eb5d44eb 87}
88
eb5d44eb 89/*
d62a17ae 90 * Checks whether we should accept a PDU of given level
eb5d44eb 91 */
d62a17ae 92static int accept_level(int level, int circuit_t)
eb5d44eb 93{
d62a17ae 94 int retval = ((circuit_t & level) == level); /* simple approach */
eb5d44eb 95
d62a17ae 96 return retval;
eb5d44eb 97}
98
3f045a08
JB
99/*
100 * Verify authentication information
101 * Support cleartext and HMAC MD5 authentication
102 */
d62a17ae 103static int authentication_check(struct isis_passwd *remote,
104 struct isis_passwd *local,
105 struct stream *stream, uint32_t auth_tlv_offset)
eb5d44eb 106{
d62a17ae 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;
3f045a08
JB
149}
150
d62a17ae 151static int lsp_authentication_check(struct stream *stream,
152 struct isis_area *area, int level,
153 struct isis_passwd *passwd)
3f045a08 154{
d62a17ae 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 }
3f045a08 183
d62a17ae 184 if (!(found & TLVFLAG_AUTH_INFO)) {
185 zlog_err("No authentication tlv in LSP");
186 return ISIS_ERROR;
187 }
3f045a08 188
d62a17ae 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 }
3f045a08 194
d62a17ae 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;
eb5d44eb 209}
210
211/*
212 * Processing helper functions
213 */
d62a17ae 214static void del_addr(void *val)
3f045a08 215{
d62a17ae 216 XFREE(MTYPE_ISIS_TMP, val);
3f045a08
JB
217}
218
d62a17ae 219static void tlvs_to_adj_area_addrs(struct tlvs *tlvs,
220 struct isis_adjacency *adj)
3f045a08 221{
d62a17ae 222 struct listnode *node;
223 struct area_addr *area_addr, *malloced;
3f045a08 224
d62a17ae 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 }
3f045a08
JB
238}
239
d62a17ae 240static int tlvs_to_adj_nlpids(struct tlvs *tlvs, struct isis_adjacency *adj)
eb5d44eb 241{
d62a17ae 242 int i;
243 struct nlpids *tlv_nlpids;
eb5d44eb 244
d62a17ae 245 if (tlvs->nlpids) {
eb5d44eb 246
d62a17ae 247 tlv_nlpids = tlvs->nlpids;
248 if (tlv_nlpids->count > array_size(adj->nlpids.nlpids))
249 return 1;
eb5d44eb 250
d62a17ae 251 adj->nlpids.count = tlv_nlpids->count;
eb5d44eb 252
d62a17ae 253 for (i = 0; i < tlv_nlpids->count; i++) {
254 adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i];
255 }
f390d2c7 256 }
d62a17ae 257 return 0;
eb5d44eb 258}
259
d62a17ae 260static void tlvs_to_adj_ipv4_addrs(struct tlvs *tlvs,
261 struct isis_adjacency *adj)
eb5d44eb 262{
d62a17ae 263 struct listnode *node;
264 struct in_addr *ipv4_addr, *malloced;
eb5d44eb 265
d62a17ae 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 }
eb5d44eb 279}
280
d62a17ae 281static void tlvs_to_adj_ipv6_addrs(struct tlvs *tlvs,
282 struct isis_adjacency *adj)
eb5d44eb 283{
d62a17ae 284 struct listnode *node;
285 struct in6_addr *ipv6_addr, *malloced;
eb5d44eb 286
d62a17ae 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 }
eb5d44eb 300}
eb5d44eb 301
eb5d44eb 302/*
d62a17ae 303 * RECEIVE SIDE
eb5d44eb 304 */
305
306/*
307 * Process P2P IIH
308 * ISO - 10589
309 * Section 8.2.5 - Receiving point-to-point IIH PDUs
310 *
311 */
d62a17ae 312static int process_p2p_hello(struct isis_circuit *circuit)
eb5d44eb 313{
d62a17ae 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 }
3f045a08 332
d62a17ae 333 if (circuit->circ_type != CIRCUIT_T_P2P) {
334 zlog_warn("p2p hello on non p2p circuit");
335 return ISIS_WARNING;
336 }
b72f345d 337
d62a17ae 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 }
eb5d44eb 344
d62a17ae 345 /* 8.2.5.1 PDU acceptance tests */
481f1484 346
d62a17ae 347 /* 8.2.5.1 a) external domain untrue */
348 /* FIXME: not useful at all? */
28a8cfcb 349
d62a17ae 350 /* 8.2.5.1 b) ID Length mismatch */
351 /* checked at the handle_pdu */
28a8cfcb 352
d62a17ae 353 /* 8.2.5.2 IIH PDU Processing */
3f045a08 354
d62a17ae 355 /* 8.2.5.2 a) 1) Maximum Area Addresses */
356 /* Already checked, and can also be ommited */
0908a2fd 357
d62a17ae 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);
3f045a08 363
d62a17ae 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 }
3f045a08 374
d62a17ae 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 }
3f045a08 411
d62a17ae 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 }
eb5d44eb 417
d62a17ae 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 }
eb5d44eb 432
d62a17ae 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 }
f8c06e2c 440
d62a17ae 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 }
eb5d44eb 457
d62a17ae 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);
d8fba7d9 462
d62a17ae 463 if (!v6_usable && !v4_usable) {
464 free_tlvs(&tlvs);
465 return ISIS_WARNING;
466 }
eb5d44eb 467
d62a17ae 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;
f390d2c7 476 }
eb5d44eb 477
d62a17ae 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;
f390d2c7 492 }
d62a17ae 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;
f390d2c7 502 }
d62a17ae 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;
f390d2c7 513 }
eb5d44eb 514
d62a17ae 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;
f390d2c7 526 }
f390d2c7 527
d62a17ae 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);
f390d2c7 541 }
d62a17ae 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 }
f390d2c7 586 }
d62a17ae 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 }
f390d2c7 648 }
d62a17ae 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 }
f390d2c7 689 }
d62a17ae 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 }
f390d2c7 703 }
d62a17ae 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 }
f390d2c7 760 }
d62a17ae 761 } else {
762 /* down - area mismatch */
763 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Area Mismatch");
f390d2c7 764 }
d8fba7d9 765
d62a17ae 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 }
eb5d44eb 771
d62a17ae 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 }
eb5d44eb 792
3f045a08 793
d62a17ae 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 }
eb5d44eb 802
d62a17ae 803 free_tlvs(&tlvs);
eb5d44eb 804
d62a17ae 805 return retval;
eb5d44eb 806}
807
eb5d44eb 808/*
809 * Process IS-IS LAN Level 1/2 Hello PDU
810 */
d62a17ae 811static int process_lan_hello(int level, struct isis_circuit *circuit,
812 const u_char *ssnpa)
eb5d44eb 813{
d62a17ae 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 }
3f045a08 835
d62a17ae 836 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
837 zlog_warn("lan hello on non broadcast circuit");
838 return ISIS_WARNING;
839 }
3f045a08 840
d62a17ae 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 }
eb5d44eb 847
d62a17ae 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 }
eb5d44eb 855
d62a17ae 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;
f390d2c7 864 }
eb5d44eb 865
866#if 0
867 /* Cisco's debug message compatability */
f390d2c7 868 if (!accept_level (level, circuit->area->is_type))
869 {
870 if (isis->debugs & DEBUG_ADJ_PACKETS)
871 {
529d65b3 872 zlog_debug ("ISIS-Adj (%s): is type mismatch",
873 circuit->area->area_tag);
f390d2c7 874 }
875 return ISIS_WARNING;
eb5d44eb 876 }
eb5d44eb 877#endif
d62a17ae 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 }
3f045a08 898
d62a17ae 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 }
3f045a08 913
d62a17ae 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 }
eb5d44eb 935
d62a17ae 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 }
f390d2c7 943
d62a17ae 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 }
b72f345d 951
d62a17ae 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 }
eb5d44eb 966
d62a17ae 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 }
19f78ceb 972
d62a17ae 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;
f390d2c7 989 }
eb5d44eb 990
d62a17ae 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);
eb5d44eb 997
d62a17ae 998 retval = ISIS_OK;
999 goto out;
1000 }
eb5d44eb 1001
d62a17ae 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 }
481f1484 1009
d62a17ae 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 }
28a8cfcb 1026
d62a17ae 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);
28a8cfcb 1031
d62a17ae 1032 if (!v6_usable && !v4_usable) {
1033 free_tlvs(&tlvs);
1034 return ISIS_WARNING;
1035 }
28a8cfcb 1036
eb5d44eb 1037
d62a17ae 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);
eb5d44eb 1060
d62a17ae 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 }
eb5d44eb 1069
d62a17ae 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 }
eb5d44eb 1093
d62a17ae 1094 adj->hold_time = hdr.hold_time;
1095 adj->last_upd = time(NULL);
1096 adj->prio[level - 1] = hdr.prio;
eb5d44eb 1097
d62a17ae 1098 memcpy(adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1);
d8fba7d9 1099
d62a17ae 1100 tlvs_to_adj_area_addrs(&tlvs, adj);
eb5d44eb 1101
d62a17ae 1102 /* which protocol are spoken ??? */
1103 if (tlvs_to_adj_nlpids(&tlvs, adj)) {
1104 retval = ISIS_WARNING;
1105 goto out;
1106 }
eb5d44eb 1107
d62a17ae 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 }
eb5d44eb 1158
d62a17ae 1159 if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed)
1160 lsp_regenerate_schedule(adj->circuit->area, level, 0);
d8fba7d9 1161
f390d2c7 1162out:
d62a17ae 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 }
eb5d44eb 1172
d62a17ae 1173 free_tlvs(&tlvs);
eb5d44eb 1174
d62a17ae 1175 return retval;
eb5d44eb 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
f390d2c7 1182 */
d62a17ae 1183static int process_lsp(int level, struct isis_circuit *circuit,
1184 const u_char *ssnpa)
eb5d44eb 1185{
d62a17ae 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 }
f390d2c7 1206
d62a17ae 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 }
eb5d44eb 1213
d62a17ae 1214 /* Reference the header */
1215 hdr = (struct isis_link_state_hdr *)STREAM_PNT(circuit->rcv_stream);
1216 pdu_len = ntohs(hdr->pdu_len);
eb5d44eb 1217
d62a17ae 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);
eb5d44eb 1225
d62a17ae 1226 return ISIS_WARNING;
f390d2c7 1227 }
d62a17ae 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);
f390d2c7 1244 }
d62a17ae 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 */
f390d2c7 1254 }
d62a17ae 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;
f390d2c7 1267 }
3f045a08 1268
d62a17ae 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);
eb5d44eb 1276
d62a17ae 1277 return ISIS_WARNING;
1278 }
eb5d44eb 1279
d62a17ae 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));
f390d2c7 1287
d62a17ae 1288 return ISIS_WARNING;
1289 }
0250758d 1290
d62a17ae 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 }
f390d2c7 1307 }
d62a17ae 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 */
f390d2c7 1349 }
f390d2c7 1350 }
f390d2c7 1351
d62a17ae 1352dontcheckadj:
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(
9d303b37
DL
1394 lsp
1395 ->SSNflags); /* FIXME:
1396 OTHER
1397 than c
1398 */
d62a17ae 1399
1400 /* For the case of lsp confusion, flood
1401 * the purge back to its
1402 * originator so that it can react.
1403 * Otherwise, don't reflood
1404 * through incoming circuit as usual */
1405 if (!lsp_confusion) {
1406 /* iii */
1407 ISIS_CLEAR_FLAG(lsp->SRMflags,
1408 circuit);
1409 /* iv */
1410 if (circuit->circ_type
1411 != CIRCUIT_T_BROADCAST)
1412 ISIS_SET_FLAG(
1413 lsp->SSNflags,
1414 circuit);
1415 }
1416 } /* 7.3.16.4 b) 2) */
1417 else if (comp == LSP_EQUAL) {
1418 /* i */
1419 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1420 /* ii */
1421 if (circuit->circ_type
1422 != CIRCUIT_T_BROADCAST)
1423 ISIS_SET_FLAG(lsp->SSNflags,
1424 circuit);
1425 } /* 7.3.16.4 b) 3) */
1426 else {
1427 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1428 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1429 }
1430 } else if (lsp->lsp_header->rem_lifetime != 0) {
1431 /* our own LSP -> 7.3.16.4 c) */
1432 if (comp == LSP_NEWER) {
1433 lsp_inc_seqnum(lsp,
1434 ntohl(hdr->seq_num));
1435 lsp_set_all_srmflags(lsp);
1436 } else {
1437 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1438 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1439 }
1440 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1441 zlog_debug(
1442 "ISIS-Upd (%s): (1) re-originating LSP %s new "
1443 "seq 0x%08x",
1444 circuit->area->area_tag,
1445 rawlspid_print(hdr->lsp_id),
1446 ntohl(lsp->lsp_header
1447 ->seq_num));
1448 }
f390d2c7 1449 }
d62a17ae 1450 return retval;
f390d2c7 1451 }
d62a17ae 1452 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1453 * purge */
1454 if (memcmp(hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) {
1455 if (!lsp) {
1456 /* 7.3.16.4: initiate a purge */
1457 lsp_purge_non_exist(level, hdr, circuit->area);
1458 return ISIS_OK;
1459 }
1460 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1461
1462 /* In 7.3.16.1, If an Intermediate system R somewhere in the
1463 * domain
1464 * has information that the current sequence number for source S
1465 * is
1466 * "greater" than that held by S, ... */
1467
1468 if (ntohl(hdr->seq_num) > ntohl(lsp->lsp_header->seq_num)) {
1469 /* 7.3.16.1 */
1470 lsp_inc_seqnum(lsp, ntohl(hdr->seq_num));
1471 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1472 zlog_debug(
1473 "ISIS-Upd (%s): (2) re-originating LSP %s new seq "
1474 "0x%08x",
1475 circuit->area->area_tag,
1476 rawlspid_print(hdr->lsp_id),
1477 ntohl(lsp->lsp_header->seq_num));
1478 }
1479 /* If the received LSP is older or equal,
1480 * resend the LSP which will act as ACK */
1481 lsp_set_all_srmflags(lsp);
1482 } else {
1483 /* 7.3.15.1 e) - This lsp originated on another system */
1484
1485 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db
1486 */
1487 if ((!lsp || comp == LSP_NEWER)) {
1488 /*
1489 * If this lsp is a frag, need to see if we have zero
1490 * lsp present
1491 */
1492 if (LSP_FRAGMENT(hdr->lsp_id) != 0) {
1493 memcpy(lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1);
1494 LSP_FRAGMENT(lspid) = 0;
1495 lsp0 = lsp_search(
1496 lspid, circuit->area->lspdb[level - 1]);
1497 if (!lsp0) {
1498 zlog_debug(
1499 "Got lsp frag, while zero lsp not in database");
1500 return ISIS_OK;
1501 }
1502 }
1503 /* i */
1504 if (!lsp) {
1505 lsp = lsp_new_from_stream_ptr(
1506 circuit->rcv_stream, pdu_len, lsp0,
1507 circuit->area, level);
1508 lsp_insert(lsp,
1509 circuit->area->lspdb[level - 1]);
1510 } else /* exists, so we overwrite */
1511 {
1512 lsp_update(lsp, circuit->rcv_stream,
1513 circuit->area, level);
1514 }
1515 /* ii */
1516 lsp_set_all_srmflags(lsp);
1517 /* iii */
1518 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1519
1520 /* iv */
1521 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1522 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1523 /* FIXME: v) */
1524 }
1525 /* 7.3.15.1 e) 2) LSP equal to the one in db */
1526 else if (comp == LSP_EQUAL) {
1527 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1528 lsp_update(lsp, circuit->rcv_stream, circuit->area,
1529 level);
1530 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1531 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1532 }
1533 /* 7.3.15.1 e) 3) LSP older than the one in db */
1534 else {
1535 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1536 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1537 }
f390d2c7 1538 }
d62a17ae 1539 return retval;
eb5d44eb 1540}
1541
1542/*
1543 * Process Sequence Numbers
1544 * ISO - 10589
1545 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1546 */
1547
d62a17ae 1548static int process_snp(int snp_type, int level, struct isis_circuit *circuit,
1549 const u_char *ssnpa)
eb5d44eb 1550{
d62a17ae 1551 int retval = ISIS_OK;
1552 int cmp, own_lsp;
1553 char typechar = ' ';
1554 uint16_t pdu_len;
1555 struct isis_adjacency *adj;
1556 struct isis_complete_seqnum_hdr *chdr = NULL;
1557 struct isis_partial_seqnum_hdr *phdr = NULL;
1558 uint32_t found = 0, expected = 0, auth_tlv_offset = 0;
1559 struct isis_lsp *lsp;
1560 struct lsp_entry *entry;
1561 struct listnode *node, *nnode;
1562 struct listnode *node2, *nnode2;
1563 struct tlvs tlvs;
1564 struct list *lsp_list = NULL;
1565 struct isis_passwd *passwd;
1566
1567 if (snp_type == ISIS_SNP_CSNP_FLAG) {
1568 /* getting the header info */
1569 typechar = 'C';
1570 chdr = (struct isis_complete_seqnum_hdr *)STREAM_PNT(
1571 circuit->rcv_stream);
1572 stream_forward_getp(circuit->rcv_stream, ISIS_CSNP_HDRLEN);
1573 pdu_len = ntohs(chdr->pdu_len);
1574 if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_CSNP_HDRLEN)
1575 || pdu_len > ISO_MTU(circuit)
1576 || pdu_len > stream_get_endp(circuit->rcv_stream)) {
1577 zlog_warn("Received a CSNP with bogus length %d",
1578 pdu_len);
1579 return ISIS_WARNING;
1580 }
1581 } else {
1582 typechar = 'P';
1583 phdr = (struct isis_partial_seqnum_hdr *)STREAM_PNT(
1584 circuit->rcv_stream);
1585 stream_forward_getp(circuit->rcv_stream, ISIS_PSNP_HDRLEN);
1586 pdu_len = ntohs(phdr->pdu_len);
1587 if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_PSNP_HDRLEN)
1588 || pdu_len > ISO_MTU(circuit)
1589 || pdu_len > stream_get_endp(circuit->rcv_stream)) {
1590 zlog_warn("Received a PSNP with bogus length %d",
1591 pdu_len);
1592 return ISIS_WARNING;
1593 }
f390d2c7 1594 }
eb5d44eb 1595
d62a17ae 1596 /*
1597 * Set the stream endp to PDU length, ignoring additional padding
1598 * introduced by transport chips.
1599 */
1600 if (pdu_len < stream_get_endp(circuit->rcv_stream))
1601 stream_set_endp(circuit->rcv_stream, pdu_len);
eb5d44eb 1602
d62a17ae 1603 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
1604 if (circuit->ext_domain) {
f390d2c7 1605
d62a17ae 1606 zlog_debug(
1607 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1608 "skipping: circuit externalDomain = true",
1609 circuit->area->area_tag, level, typechar,
1610 circuit->interface->name);
f390d2c7 1611
d62a17ae 1612 return ISIS_OK;
1613 }
eb5d44eb 1614
d62a17ae 1615 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
1616 if (!accept_level(level, circuit->is_type)) {
eb5d44eb 1617
d62a17ae 1618 zlog_debug(
1619 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1620 "skipping: circuit type %s does not match level %d",
1621 circuit->area->area_tag, level, typechar,
1622 circuit->interface->name,
1623 circuit_t2string(circuit->is_type), level);
eb5d44eb 1624
d62a17ae 1625 return ISIS_OK;
f390d2c7 1626 }
eb5d44eb 1627
d62a17ae 1628 /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */
1629 if ((snp_type == ISIS_SNP_PSNP_FLAG)
1630 && (circuit->circ_type == CIRCUIT_T_BROADCAST)
1631 && (!circuit->u.bc.is_dr[level - 1])) {
1632 zlog_debug(
1633 "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, "
1634 "skipping: we are not the DIS",
1635 circuit->area->area_tag, level, typechar,
1636 snpa_print(ssnpa), circuit->interface->name);
1637
1638 return ISIS_OK;
1639 }
eb5d44eb 1640
d62a17ae 1641 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked
1642 */
1643
1644 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use
1645 * 3
1646 * - already checked */
1647
1648 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same
1649 * level */
1650 /* for broadcast circuits, snpa should be compared */
1651 /* FIXME : Do we need to check SNPA? */
1652 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1653 if (snp_type == ISIS_SNP_CSNP_FLAG) {
1654 adj = isis_adj_lookup(chdr->source_id,
1655 circuit->u.bc.adjdb[level - 1]);
1656 } else {
1657 /* a psnp on a broadcast, how lovely of Juniper :) */
1658 adj = isis_adj_lookup(phdr->source_id,
1659 circuit->u.bc.adjdb[level - 1]);
1660 }
1661 if (!adj)
1662 return ISIS_OK; /* Silently discard */
1663 } else {
1664 if (!circuit->u.p2p.neighbor) {
1665 zlog_warn("no p2p neighbor on circuit %s",
1666 circuit->interface->name);
1667 return ISIS_OK; /* Silently discard */
1668 }
1669 }
eb5d44eb 1670
d62a17ae 1671 /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */
3f045a08 1672
d62a17ae 1673 /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */
eb5d44eb 1674
d62a17ae 1675 memset(&tlvs, 0, sizeof(struct tlvs));
eb5d44eb 1676
d62a17ae 1677 /* parse the SNP */
1678 expected |= TLVFLAG_LSP_ENTRIES;
1679 expected |= TLVFLAG_AUTH_INFO;
1cbc562b 1680
d62a17ae 1681 auth_tlv_offset = stream_get_getp(circuit->rcv_stream);
1682 retval = parse_tlvs(circuit->area->area_tag,
1683 STREAM_PNT(circuit->rcv_stream),
1684 pdu_len - stream_get_getp(circuit->rcv_stream),
1685 &expected, &found, &tlvs, &auth_tlv_offset);
eb5d44eb 1686
d62a17ae 1687 if (retval > ISIS_WARNING) {
1688 zlog_warn("something went very wrong processing SNP");
1689 free_tlvs(&tlvs);
1690 return retval;
f390d2c7 1691 }
eb5d44eb 1692
d62a17ae 1693 if (level == IS_LEVEL_1)
1694 passwd = &circuit->area->area_passwd;
f390d2c7 1695 else
d62a17ae 1696 passwd = &circuit->area->domain_passwd;
1697
1698 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) {
1699 if (passwd->type) {
1700 if (!(found & TLVFLAG_AUTH_INFO)
1701 || authentication_check(&tlvs.auth_info, passwd,
1702 circuit->rcv_stream,
1703 auth_tlv_offset)) {
1704 isis_event_auth_failure(circuit->area->area_tag,
1705 "SNP authentication"
1706 " failure",
1707 phdr ? phdr->source_id
1708 : chdr->source_id);
1709 free_tlvs(&tlvs);
1710 return ISIS_OK;
1711 }
1712 }
1713 }
eb5d44eb 1714
d62a17ae 1715 /* debug isis snp-packets */
1716 if (isis->debugs & DEBUG_SNP_PACKETS) {
1717 zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
1718 circuit->area->area_tag, level, typechar,
1719 snpa_print(ssnpa), circuit->interface->name);
1720 if (tlvs.lsp_entries) {
1721 for (ALL_LIST_ELEMENTS_RO(tlvs.lsp_entries, node,
1722 entry)) {
1723 zlog_debug(
1724 "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x,"
1725 " cksum 0x%04x, lifetime %us",
1726 circuit->area->area_tag, typechar,
1727 rawlspid_print(entry->lsp_id),
1728 ntohl(entry->seq_num),
1729 ntohs(entry->checksum),
1730 ntohs(entry->rem_lifetime));
1731 }
f390d2c7 1732 }
f390d2c7 1733 }
3f045a08 1734
d62a17ae 1735 /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
1736 if (tlvs.lsp_entries) {
1737 for (ALL_LIST_ELEMENTS_RO(tlvs.lsp_entries, node, entry)) {
1738 lsp = lsp_search(entry->lsp_id,
1739 circuit->area->lspdb[level - 1]);
1740 own_lsp = !memcmp(entry->lsp_id, isis->sysid,
1741 ISIS_SYS_ID_LEN);
1742 if (lsp) {
1743 /* 7.3.15.2 b) 1) is this LSP newer */
1744 cmp = lsp_compare(circuit->area->area_tag, lsp,
1745 entry->seq_num,
1746 entry->checksum,
1747 entry->rem_lifetime);
1748 /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p
1749 */
1750 if (cmp == LSP_EQUAL) {
1751 /* if (circuit->circ_type !=
1752 * CIRCUIT_T_BROADCAST) */
1753 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
1754 }
1755 /* 7.3.15.2 b) 3) if it is older, clear SSN and
1756 set SRM */
1757 else if (cmp == LSP_OLDER) {
1758 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1759 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1760 }
1761 /* 7.3.15.2 b) 4) if it is newer, set SSN and
1762 clear SRM on p2p */
1763 else {
1764 if (own_lsp) {
1765 lsp_inc_seqnum(
1766 lsp,
1767 ntohl(entry->seq_num));
1768 ISIS_SET_FLAG(lsp->SRMflags,
1769 circuit);
1770 } else {
1771 ISIS_SET_FLAG(lsp->SSNflags,
1772 circuit);
1773 /* if (circuit->circ_type !=
1774 * CIRCUIT_T_BROADCAST) */
1775 ISIS_CLEAR_FLAG(lsp->SRMflags,
1776 circuit);
1777 }
1778 }
1779 } else {
1780 /* 7.3.15.2 b) 5) if it was not found, and all
1781 * of those are not 0,
1782 * insert it and set SSN on it */
1783 if (entry->rem_lifetime && entry->checksum
1784 && entry->seq_num
1785 && memcmp(entry->lsp_id, isis->sysid,
1786 ISIS_SYS_ID_LEN)) {
1787 lsp = lsp_new(
1788 circuit->area, entry->lsp_id,
1789 ntohs(entry->rem_lifetime), 0,
1790 0, entry->checksum, level);
1791 lsp_insert(lsp,
1792 circuit->area
1793 ->lspdb[level - 1]);
1794 ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags);
1795 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1796 }
1797 }
1798 }
1799 }
eb5d44eb 1800
d62a17ae 1801 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported
1802 */
1803 if (snp_type == ISIS_SNP_CSNP_FLAG) {
1804 /*
1805 * Build a list from our own LSP db bounded with
1806 * start_lsp_id and stop_lsp_id
1807 */
1808 lsp_list = list_new();
1809 lsp_build_list_nonzero_ht(chdr->start_lsp_id, chdr->stop_lsp_id,
1810 lsp_list,
1811 circuit->area->lspdb[level - 1]);
1812
1813 /* Fixme: Find a better solution */
1814 if (tlvs.lsp_entries) {
1815 for (ALL_LIST_ELEMENTS(tlvs.lsp_entries, node, nnode,
1816 entry)) {
1817 for (ALL_LIST_ELEMENTS(lsp_list, node2, nnode2,
1818 lsp)) {
1819 if (lsp_id_cmp(lsp->lsp_header->lsp_id,
1820 entry->lsp_id)
1821 == 0) {
1822 list_delete_node(lsp_list,
1823 node2);
1824 break;
1825 }
1826 }
1827 }
1828 }
1829 /* on remaining LSPs we set SRM (neighbor knew not of) */
1830 for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp))
1831 ISIS_SET_FLAG(lsp->SRMflags, circuit);
1832 /* lets free it */
1833 list_delete(lsp_list);
1834 }
1835
1836 free_tlvs(&tlvs);
1837 return retval;
eb5d44eb 1838}
1839
d62a17ae 1840static int process_csnp(int level, struct isis_circuit *circuit,
1841 const u_char *ssnpa)
eb5d44eb 1842{
d62a17ae 1843 if (isis->debugs & DEBUG_SNP_PACKETS) {
1844 zlog_debug(
1845 "ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u",
1846 circuit->area->area_tag, level,
1847 circuit->interface->name,
1848 circuit_t2string(circuit->is_type),
1849 circuit->circuit_id);
1850 if (isis->debugs & DEBUG_PACKET_DUMP)
1851 zlog_dump_data(STREAM_DATA(circuit->rcv_stream),
1852 stream_get_endp(circuit->rcv_stream));
1853 }
3f045a08 1854
d62a17ae 1855 /* Sanity check - FIXME: move to correct place */
1856 if ((stream_get_endp(circuit->rcv_stream)
1857 - stream_get_getp(circuit->rcv_stream))
1858 < ISIS_CSNP_HDRLEN) {
1859 zlog_warn("Packet too short ( < %d)", ISIS_CSNP_HDRLEN);
1860 return ISIS_WARNING;
1861 }
eb5d44eb 1862
d62a17ae 1863 return process_snp(ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa);
eb5d44eb 1864}
1865
d62a17ae 1866static int process_psnp(int level, struct isis_circuit *circuit,
1867 const u_char *ssnpa)
eb5d44eb 1868{
d62a17ae 1869 if (isis->debugs & DEBUG_SNP_PACKETS) {
1870 zlog_debug(
1871 "ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u",
1872 circuit->area->area_tag, level,
1873 circuit->interface->name,
1874 circuit_t2string(circuit->is_type),
1875 circuit->circuit_id);
1876 if (isis->debugs & DEBUG_PACKET_DUMP)
1877 zlog_dump_data(STREAM_DATA(circuit->rcv_stream),
1878 stream_get_endp(circuit->rcv_stream));
1879 }
3f045a08 1880
d62a17ae 1881 if ((stream_get_endp(circuit->rcv_stream)
1882 - stream_get_getp(circuit->rcv_stream))
1883 < ISIS_PSNP_HDRLEN) {
1884 zlog_warn("Packet too short ( < %d)", ISIS_PSNP_HDRLEN);
1885 return ISIS_WARNING;
1886 }
eb5d44eb 1887
d62a17ae 1888 return process_snp(ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa);
eb5d44eb 1889}
1890
88f9d911
CF
1891static int pdu_size(uint8_t pdu_type, uint8_t *size)
1892{
1893 switch (pdu_type) {
1894 case L1_LAN_HELLO:
1895 case L2_LAN_HELLO:
1896 *size = ISIS_LANHELLO_HDRLEN;
1897 break;
1898 case P2P_HELLO:
1899 *size = ISIS_P2PHELLO_HDRLEN;
1900 break;
1901 case L1_LINK_STATE:
1902 case L2_LINK_STATE:
1903 *size = ISIS_LSP_HDR_LEN;
1904 break;
1905 case L1_COMPLETE_SEQ_NUM:
1906 case L2_COMPLETE_SEQ_NUM:
1907 *size = ISIS_CSNP_HDRLEN;
1908 break;
1909 case L1_PARTIAL_SEQ_NUM:
1910 case L2_PARTIAL_SEQ_NUM:
1911 *size = ISIS_PSNP_HDRLEN;
1912 break;
1913 default:
1914 return 1;
1915 }
1916 *size += ISIS_FIXED_HDR_LEN;
1917 return 0;
1918}
1919
eb5d44eb 1920/*
1921 * PDU Dispatcher
1922 */
1923
d62a17ae 1924static int isis_handle_pdu(struct isis_circuit *circuit, u_char *ssnpa)
eb5d44eb 1925{
d62a17ae 1926 int retval = ISIS_OK;
eb5d44eb 1927
88f9d911
CF
1928 /* Verify that at least the 8 bytes fixed header have been received */
1929 if (stream_get_endp(circuit->rcv_stream) < ISIS_FIXED_HDR_LEN) {
1930 zlog_err("PDU is too short to be IS-IS.");
1931 return ISIS_ERROR;
1932 }
eb5d44eb 1933
88f9d911
CF
1934 uint8_t idrp = stream_getc(circuit->rcv_stream);
1935 uint8_t length = stream_getc(circuit->rcv_stream);
1936 uint8_t version1 = stream_getc(circuit->rcv_stream);
1937 uint8_t id_len = stream_getc(circuit->rcv_stream);
1938 uint8_t pdu_type = stream_getc(circuit->rcv_stream)
1939 & 0x1f; /* bits 6-8 are reserved */
1940 uint8_t version2 = stream_getc(circuit->rcv_stream);
1941 stream_forward_getp(circuit->rcv_stream, 1); /* reserved */
1942 uint8_t max_area_addrs = stream_getc(circuit->rcv_stream);
1943
1944 if (idrp == ISO9542_ESIS) {
1945 zlog_err("No support for ES-IS packet IDRP=%" PRIx8, idrp);
d62a17ae 1946 return ISIS_ERROR;
1947 }
eb5d44eb 1948
88f9d911
CF
1949 if (idrp != ISO10589_ISIS) {
1950 zlog_err("Not an IS-IS packet IDRP=%" PRIx8, idrp);
d62a17ae 1951 return ISIS_ERROR;
1952 }
3f045a08 1953
88f9d911
CF
1954 if (version1 != 1) {
1955 zlog_warn("Unsupported ISIS version %" PRIu8, version1);
1956 return ISIS_WARNING;
1957 }
eb5d44eb 1958
88f9d911
CF
1959 if (id_len != 0 && id_len != ISIS_SYS_ID_LEN) {
1960 zlog_err(
1961 "IDFieldLengthMismatch: ID Length field in a received PDU %" PRIu8
1962 ", while the parameter for this IS is %u",
1963 id_len, ISIS_SYS_ID_LEN);
d62a17ae 1964 return ISIS_ERROR;
1965 }
eb5d44eb 1966
88f9d911
CF
1967 uint8_t expected_length;
1968 if (pdu_size(pdu_type, &expected_length)) {
1969 zlog_warn("Unsupported ISIS PDU %" PRIu8, pdu_type);
d62a17ae 1970 return ISIS_WARNING;
1971 }
88f9d911
CF
1972
1973 if (length != expected_length) {
1974 zlog_err("Exepected fixed header length = %" PRIu8
1975 " but got %" PRIu8,
1976 expected_length, length);
1977 return ISIS_ERROR;
1978 }
1979
1980 if (stream_get_endp(circuit->rcv_stream) < length) {
d62a17ae 1981 zlog_err(
88f9d911 1982 "PDU is too short to contain fixed header of given PDU type.");
d62a17ae 1983 return ISIS_ERROR;
1984 }
eb5d44eb 1985
88f9d911
CF
1986 if (version2 != 1) {
1987 zlog_warn("Unsupported ISIS PDU version %" PRIu8, version2);
d62a17ae 1988 return ISIS_WARNING;
1989 }
3f045a08 1990
d62a17ae 1991 if (circuit->is_passive) {
1992 zlog_warn("Received ISIS PDU on passive circuit %s",
1993 circuit->interface->name);
1994 return ISIS_WARNING;
1995 }
3f045a08 1996
d62a17ae 1997 /* either 3 or 0 */
88f9d911 1998 if (max_area_addrs != 0 && max_area_addrs != isis->max_area_addrs) {
d62a17ae 1999 zlog_err(
88f9d911
CF
2000 "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %" PRIu8
2001 " while the parameter for this IS is %u",
2002 max_area_addrs, isis->max_area_addrs);
d62a17ae 2003 return ISIS_ERROR;
2004 }
eb5d44eb 2005
88f9d911 2006 switch (pdu_type) {
d62a17ae 2007 case L1_LAN_HELLO:
2008 retval = process_lan_hello(ISIS_LEVEL1, circuit, ssnpa);
2009 break;
2010 case L2_LAN_HELLO:
2011 retval = process_lan_hello(ISIS_LEVEL2, circuit, ssnpa);
2012 break;
2013 case P2P_HELLO:
2014 retval = process_p2p_hello(circuit);
2015 break;
2016 case L1_LINK_STATE:
2017 retval = process_lsp(ISIS_LEVEL1, circuit, ssnpa);
2018 break;
2019 case L2_LINK_STATE:
2020 retval = process_lsp(ISIS_LEVEL2, circuit, ssnpa);
2021 break;
2022 case L1_COMPLETE_SEQ_NUM:
2023 retval = process_csnp(ISIS_LEVEL1, circuit, ssnpa);
2024 break;
2025 case L2_COMPLETE_SEQ_NUM:
2026 retval = process_csnp(ISIS_LEVEL2, circuit, ssnpa);
2027 break;
2028 case L1_PARTIAL_SEQ_NUM:
2029 retval = process_psnp(ISIS_LEVEL1, circuit, ssnpa);
2030 break;
2031 case L2_PARTIAL_SEQ_NUM:
2032 retval = process_psnp(ISIS_LEVEL2, circuit, ssnpa);
2033 break;
2034 default:
2035 return ISIS_ERROR;
2036 }
eb5d44eb 2037
d62a17ae 2038 return retval;
eb5d44eb 2039}
2040
d62a17ae 2041int isis_receive(struct thread *thread)
eb5d44eb 2042{
d62a17ae 2043 struct isis_circuit *circuit;
2044 u_char ssnpa[ETH_ALEN];
2045 int retval;
eb5d44eb 2046
d62a17ae 2047 /*
2048 * Get the circuit
2049 */
2050 circuit = THREAD_ARG(thread);
2051 assert(circuit);
eb5d44eb 2052
d62a17ae 2053 circuit->t_read = NULL;
eb5d44eb 2054
d62a17ae 2055 isis_circuit_stream(circuit, &circuit->rcv_stream);
eb5d44eb 2056
d62a17ae 2057 retval = circuit->rx(circuit, ssnpa);
eb5d44eb 2058
d62a17ae 2059 if (retval == ISIS_OK)
2060 retval = isis_handle_pdu(circuit, ssnpa);
eb5d44eb 2061
d62a17ae 2062 /*
2063 * prepare for next packet.
2064 */
2065 if (!circuit->is_passive)
2066 isis_circuit_prepare(circuit);
eb5d44eb 2067
d62a17ae 2068 return retval;
eb5d44eb 2069}
2070
eb5d44eb 2071/*
d62a17ae 2072 * SEND SIDE
eb5d44eb 2073 */
88f9d911 2074void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream)
eb5d44eb 2075{
88f9d911
CF
2076 uint8_t length;
2077
2078 if (pdu_size(pdu_type, &length))
2079 assert(!"Unknown PDU Type");
2080
2081 stream_putc(stream, ISO10589_ISIS); /* IDRP */
2082 stream_putc(stream, length); /* Length of fixed header */
2083 stream_putc(stream, 1); /* Version/Protocol ID Extension 1 */
2084 stream_putc(stream, 0); /* ID Length, 0 => 6 */
2085 stream_putc(stream, pdu_type);
2086 stream_putc(stream, 1); /* Subversion */
2087 stream_putc(stream, 0); /* Reserved */
2088 stream_putc(stream, 0); /* Max Area Addresses 0 => 3 */
eb5d44eb 2089}
2090
d62a17ae 2091int send_hello(struct isis_circuit *circuit, int level)
eb5d44eb 2092{
d62a17ae 2093 struct isis_lan_hello_hdr hello_hdr;
2094 struct isis_p2p_hello_hdr p2p_hello_hdr;
2095 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2096 size_t len_pointer, length, auth_tlv_offset = 0;
2097 u_int32_t interval;
2098 int retval;
2099
2100 if (circuit->is_passive)
2101 return ISIS_OK;
2102
2103 if (circuit->interface->mtu == 0) {
2104 zlog_warn("circuit has zero MTU");
2105 return ISIS_WARNING;
f390d2c7 2106 }
eb5d44eb 2107
d62a17ae 2108 isis_circuit_stream(circuit, &circuit->snd_stream);
99894f9a 2109
88f9d911
CF
2110 uint8_t pdu_type;
2111
d62a17ae 2112 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
88f9d911 2113 pdu_type = (level == IS_LEVEL_1) ? L1_LAN_HELLO : L2_LAN_HELLO;
d62a17ae 2114 else
88f9d911
CF
2115 pdu_type = P2P_HELLO;
2116
2117 fill_fixed_hdr(pdu_type, circuit->snd_stream);
d62a17ae 2118
2119 /*
2120 * Fill LAN Level 1 or 2 Hello PDU header
2121 */
2122 memset(&hello_hdr, 0, sizeof(struct isis_lan_hello_hdr));
2123 interval = circuit->hello_multiplier[level - 1]
2124 * circuit->hello_interval[level - 1];
2125 if (interval > USHRT_MAX)
2126 interval = USHRT_MAX;
2127 hello_hdr.circuit_t = circuit->is_type;
2128 memcpy(hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN);
2129 hello_hdr.hold_time = htons((u_int16_t)interval);
2130
2131 hello_hdr.pdu_len = 0; /* Update the PDU Length later */
2132 len_pointer =
2133 stream_get_endp(circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN;
2134
2135 /* copy the shared part of the hello to the p2p hello if needed */
2136 if (circuit->circ_type == CIRCUIT_T_P2P) {
2137 memcpy(&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN);
2138 p2p_hello_hdr.local_id = circuit->circuit_id;
2139 /* FIXME: need better understanding */
2140 stream_put(circuit->snd_stream, &p2p_hello_hdr,
2141 ISIS_P2PHELLO_HDRLEN);
2142 } else {
2143 hello_hdr.prio = circuit->priority[level - 1];
2144 if (level == IS_LEVEL_1) {
2145 memcpy(hello_hdr.lan_id, circuit->u.bc.l1_desig_is,
2146 ISIS_SYS_ID_LEN + 1);
2147 } else if (level == IS_LEVEL_2) {
2148 memcpy(hello_hdr.lan_id, circuit->u.bc.l2_desig_is,
2149 ISIS_SYS_ID_LEN + 1);
2150 }
2151 stream_put(circuit->snd_stream, &hello_hdr,
2152 ISIS_LANHELLO_HDRLEN);
2153 }
eb5d44eb 2154
d62a17ae 2155 /*
2156 * Then the variable length part.
2157 */
2158
2159 /* add circuit password */
2160 switch (circuit->passwd.type) {
2161 /* Cleartext */
2162 case ISIS_PASSWD_TYPE_CLEARTXT:
2163 if (tlv_add_authinfo(circuit->passwd.type, circuit->passwd.len,
2164 circuit->passwd.passwd,
2165 circuit->snd_stream))
2166 return ISIS_WARNING;
2167 break;
2168
2169 /* HMAC MD5 */
2170 case ISIS_PASSWD_TYPE_HMAC_MD5:
2171 /* Remember where TLV is written so we can later overwrite the
2172 * MD5 hash */
2173 auth_tlv_offset = stream_get_endp(circuit->snd_stream);
2174 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2175 if (tlv_add_authinfo(circuit->passwd.type, ISIS_AUTH_MD5_SIZE,
2176 hmac_md5_hash, circuit->snd_stream))
2177 return ISIS_WARNING;
2178 break;
2179
2180 default:
2181 break;
2182 }
eb5d44eb 2183
d62a17ae 2184 /* Area Addresses TLV */
2185 if (listcount(circuit->area->area_addrs) == 0)
2186 return ISIS_WARNING;
2187 if (tlv_add_area_addrs(circuit->area->area_addrs, circuit->snd_stream))
2188 return ISIS_WARNING;
2189
2190 /* LAN Neighbors TLV */
2191 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2192 if (level == IS_LEVEL_1 && circuit->u.bc.lan_neighs[0]
2193 && listcount(circuit->u.bc.lan_neighs[0]) > 0)
2194 if (tlv_add_lan_neighs(circuit->u.bc.lan_neighs[0],
2195 circuit->snd_stream))
2196 return ISIS_WARNING;
2197 if (level == IS_LEVEL_2 && circuit->u.bc.lan_neighs[1]
2198 && listcount(circuit->u.bc.lan_neighs[1]) > 0)
2199 if (tlv_add_lan_neighs(circuit->u.bc.lan_neighs[1],
2200 circuit->snd_stream))
2201 return ISIS_WARNING;
2202 }
eb5d44eb 2203
d62a17ae 2204 /* Protocols Supported TLV */
2205 if (circuit->nlpids.count > 0)
2206 if (tlv_add_nlpid(&circuit->nlpids, circuit->snd_stream))
2207 return ISIS_WARNING;
2208 /* IP interface Address TLV */
2209 if (circuit->ip_router && circuit->ip_addrs
2210 && listcount(circuit->ip_addrs) > 0)
2211 if (tlv_add_ip_addrs(circuit->ip_addrs, circuit->snd_stream))
2212 return ISIS_WARNING;
2213
2214 /*
2215 * MT Supported TLV
2216 *
2217 * TLV gets included if no topology is enabled on the interface,
2218 * if one topology other than #0 is enabled, or if multiple topologies
2219 * are enabled.
2220 */
2221 struct isis_circuit_mt_setting **mt_settings;
2222 unsigned int mt_count;
2223
2224 mt_settings = circuit_mt_settings(circuit, &mt_count);
2225 if ((mt_count == 0 && area_is_mt(circuit->area))
2226 || (mt_count == 1 && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST)
2227 || (mt_count > 1)) {
2228 struct list *mt_info = list_new();
2229 mt_info->del = free_tlv;
2230
2231 for (unsigned int i = 0; i < mt_count; i++) {
2232 struct mt_router_info *info;
2233
2234 info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info));
2235 info->mtid = mt_settings[i]->mtid;
2236 /* overload info is not valid in IIH, so it's not
2237 * included here */
2238 listnode_add(mt_info, info);
2239 }
2240 tlv_add_mt_router_info(mt_info, circuit->snd_stream);
2241 list_free(mt_info);
2242 }
eb5d44eb 2243
d62a17ae 2244 /* IPv6 Interface Address TLV */
2245 if (circuit->ipv6_router && circuit->ipv6_link
2246 && listcount(circuit->ipv6_link) > 0)
2247 if (tlv_add_ipv6_addrs(circuit->ipv6_link, circuit->snd_stream))
2248 return ISIS_WARNING;
2249
2250 if (circuit->pad_hellos)
2251 if (tlv_add_padding(circuit->snd_stream))
2252 return ISIS_WARNING;
2253
2254 length = stream_get_endp(circuit->snd_stream);
2255 /* Update PDU length */
2256 stream_putw_at(circuit->snd_stream, len_pointer, (u_int16_t)length);
2257
2258 /* For HMAC MD5 we need to compute the md5 hash and store it */
2259 if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) {
2260 hmac_md5(STREAM_DATA(circuit->snd_stream),
2261 stream_get_endp(circuit->snd_stream),
2262 (unsigned char *)&circuit->passwd.passwd,
2263 circuit->passwd.len, (unsigned char *)&hmac_md5_hash);
2264 /* Copy the hash into the stream */
2265 memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3,
2266 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
f390d2c7 2267 }
d62a17ae 2268
2269 if (isis->debugs & DEBUG_ADJ_PACKETS) {
2270 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2271 zlog_debug(
2272 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
2273 circuit->area->area_tag, level,
2274 circuit->interface->name, length);
2275 } else {
2276 zlog_debug(
2277 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
2278 circuit->area->area_tag,
2279 circuit->interface->name, length);
2280 }
2281 if (isis->debugs & DEBUG_PACKET_DUMP)
2282 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2283 stream_get_endp(circuit->snd_stream));
f390d2c7 2284 }
eb5d44eb 2285
d62a17ae 2286 retval = circuit->tx(circuit, level);
2287 if (retval != ISIS_OK)
2288 zlog_err("ISIS-Adj (%s): Send L%d IIH on %s failed",
2289 circuit->area->area_tag, level,
2290 circuit->interface->name);
eb5d44eb 2291
d62a17ae 2292 return retval;
eb5d44eb 2293}
2294
d62a17ae 2295int send_lan_l1_hello(struct thread *thread)
eb5d44eb 2296{
d62a17ae 2297 struct isis_circuit *circuit;
2298 int retval;
2299
2300 circuit = THREAD_ARG(thread);
2301 assert(circuit);
2302 circuit->u.bc.t_send_lan_hello[0] = NULL;
2303
2304 if (!(circuit->area->is_type & IS_LEVEL_1)) {
2305 zlog_warn(
2306 "ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
2307 circuit->area->area_tag);
2308 return 1;
2309 }
ddfdbd32 2310
d62a17ae 2311 if (circuit->u.bc.run_dr_elect[0])
2312 isis_dr_elect(circuit, 1);
eb5d44eb 2313
d62a17ae 2314 retval = send_hello(circuit, 1);
eb5d44eb 2315
d62a17ae 2316 /* set next timer thread */
2317 thread_add_timer(master, send_lan_l1_hello, circuit,
2318 isis_jitter(circuit->hello_interval[0], IIH_JITTER),
2319 &circuit->u.bc.t_send_lan_hello[0]);
eb5d44eb 2320
d62a17ae 2321 return retval;
eb5d44eb 2322}
2323
d62a17ae 2324int send_lan_l2_hello(struct thread *thread)
eb5d44eb 2325{
d62a17ae 2326 struct isis_circuit *circuit;
2327 int retval;
eb5d44eb 2328
d62a17ae 2329 circuit = THREAD_ARG(thread);
2330 assert(circuit);
2331 circuit->u.bc.t_send_lan_hello[1] = NULL;
eb5d44eb 2332
d62a17ae 2333 if (!(circuit->area->is_type & IS_LEVEL_2)) {
2334 zlog_warn("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
2335 circuit->area->area_tag);
2336 return 1;
2337 }
ddfdbd32 2338
d62a17ae 2339 if (circuit->u.bc.run_dr_elect[1])
2340 isis_dr_elect(circuit, 2);
eb5d44eb 2341
d62a17ae 2342 retval = send_hello(circuit, 2);
eb5d44eb 2343
d62a17ae 2344 /* set next timer thread */
2345 thread_add_timer(master, send_lan_l2_hello, circuit,
2346 isis_jitter(circuit->hello_interval[1], IIH_JITTER),
2347 &circuit->u.bc.t_send_lan_hello[1]);
eb5d44eb 2348
d62a17ae 2349 return retval;
eb5d44eb 2350}
2351
d62a17ae 2352int send_p2p_hello(struct thread *thread)
eb5d44eb 2353{
d62a17ae 2354 struct isis_circuit *circuit;
eb5d44eb 2355
d62a17ae 2356 circuit = THREAD_ARG(thread);
2357 assert(circuit);
2358 circuit->u.p2p.t_send_p2p_hello = NULL;
eb5d44eb 2359
d62a17ae 2360 send_hello(circuit, 1);
eb5d44eb 2361
d62a17ae 2362 /* set next timer thread */
2363 thread_add_timer(master, send_p2p_hello, circuit,
2364 isis_jitter(circuit->hello_interval[1], IIH_JITTER),
2365 &circuit->u.p2p.t_send_p2p_hello);
eb5d44eb 2366
d62a17ae 2367 return ISIS_OK;
eb5d44eb 2368}
2369
d62a17ae 2370static int build_csnp(int level, u_char *start, u_char *stop, struct list *lsps,
2371 struct isis_circuit *circuit)
eb5d44eb 2372{
d62a17ae 2373 struct isis_passwd *passwd;
2374 unsigned long lenp;
2375 u_int16_t length;
2376 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2377 unsigned long auth_tlv_offset = 0;
2378 int retval = ISIS_OK;
88f9d911
CF
2379 uint8_t pdu_type = (level == IS_LEVEL_1) ? L1_COMPLETE_SEQ_NUM
2380 : L2_COMPLETE_SEQ_NUM;
d62a17ae 2381
2382 isis_circuit_stream(circuit, &circuit->snd_stream);
2383
88f9d911 2384 fill_fixed_hdr(pdu_type, circuit->snd_stream);
d62a17ae 2385
2386 /*
2387 * Fill Level 1 or 2 Complete Sequence Numbers header
2388 */
2389
2390 lenp = stream_get_endp(circuit->snd_stream);
2391 stream_putw(circuit->snd_stream, 0); /* PDU length - when we know it */
2392 /* no need to send the source here, it is always us if we csnp */
2393 stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
2394 /* with zero circuit id - ref 9.10, 9.11 */
2395 stream_putc(circuit->snd_stream, 0x00);
2396
2397 stream_put(circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2);
2398 stream_put(circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2);
2399
2400 /*
2401 * And TLVs
2402 */
2403 if (level == IS_LEVEL_1)
2404 passwd = &circuit->area->area_passwd;
2405 else
2406 passwd = &circuit->area->domain_passwd;
2407
2408 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2409 switch (passwd->type) {
2410 /* Cleartext */
2411 case ISIS_PASSWD_TYPE_CLEARTXT:
2412 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT,
2413 passwd->len, passwd->passwd,
2414 circuit->snd_stream))
2415 return ISIS_WARNING;
2416 break;
2417
2418 /* HMAC MD5 */
2419 case ISIS_PASSWD_TYPE_HMAC_MD5:
2420 /* Remember where TLV is written so we can later
2421 * overwrite the MD5 hash */
2422 auth_tlv_offset = stream_get_endp(circuit->snd_stream);
2423 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2424 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5,
2425 ISIS_AUTH_MD5_SIZE, hmac_md5_hash,
2426 circuit->snd_stream))
2427 return ISIS_WARNING;
2428 break;
2429
2430 default:
2431 break;
2432 }
2433 }
eb5d44eb 2434
d62a17ae 2435 retval = tlv_add_lsp_entries(lsps, circuit->snd_stream);
2436 if (retval != ISIS_OK)
2437 return retval;
2438
2439 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
2440 /* Update PU length */
2441 stream_putw_at(circuit->snd_stream, lenp, length);
2442
2443 /* For HMAC MD5 we need to compute the md5 hash and store it */
2444 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)
2445 && passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) {
2446 hmac_md5(STREAM_DATA(circuit->snd_stream),
2447 stream_get_endp(circuit->snd_stream),
2448 (unsigned char *)&passwd->passwd, passwd->len,
2449 (unsigned char *)&hmac_md5_hash);
2450 /* Copy the hash into the stream */
2451 memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3,
2452 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2453 }
3f045a08 2454
d62a17ae 2455 return retval;
eb5d44eb 2456}
2457
3f045a08
JB
2458/*
2459 * Count the maximum number of lsps that can be accomodated by a given size.
2460 */
d62a17ae 2461static uint16_t get_max_lsp_count(uint16_t size)
3f045a08 2462{
d62a17ae 2463 uint16_t tlv_count;
2464 uint16_t lsp_count;
2465 uint16_t remaining_size;
3f045a08 2466
d62a17ae 2467 /* First count the full size TLVs */
2468 tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE;
2469 lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN);
3f045a08 2470
d62a17ae 2471 /* The last TLV, if any */
2472 remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE;
2473 if (remaining_size - 2 >= LSP_ENTRIES_LEN)
2474 lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN;
3f045a08 2475
d62a17ae 2476 return lsp_count;
3f045a08
JB
2477}
2478
2479/*
2480 * Calculate the length of Authentication Info. TLV.
2481 */
d62a17ae 2482static uint16_t auth_tlv_length(int level, struct isis_circuit *circuit)
3f045a08 2483{
d62a17ae 2484 struct isis_passwd *passwd;
2485 uint16_t length;
2486
2487 if (level == IS_LEVEL_1)
2488 passwd = &circuit->area->area_passwd;
2489 else
2490 passwd = &circuit->area->domain_passwd;
2491
2492 /* Also include the length of TLV header */
2493 length = AUTH_INFO_HDRLEN;
2494 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2495 switch (passwd->type) {
2496 /* Cleartext */
2497 case ISIS_PASSWD_TYPE_CLEARTXT:
2498 length += passwd->len;
2499 break;
2500
2501 /* HMAC MD5 */
2502 case ISIS_PASSWD_TYPE_HMAC_MD5:
2503 length += ISIS_AUTH_MD5_SIZE;
2504 break;
2505
2506 default:
2507 break;
2508 }
2509 }
3f045a08 2510
d62a17ae 2511 return length;
3f045a08
JB
2512}
2513
2514/*
2515 * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
2516 */
d62a17ae 2517static uint16_t max_lsps_per_snp(int snp_type, int level,
2518 struct isis_circuit *circuit)
3f045a08 2519{
d62a17ae 2520 int snp_hdr_len;
2521 int auth_tlv_len;
2522 uint16_t lsp_count;
2523
2524 snp_hdr_len = ISIS_FIXED_HDR_LEN;
2525 if (snp_type == ISIS_SNP_CSNP_FLAG)
2526 snp_hdr_len += ISIS_CSNP_HDRLEN;
2527 else
2528 snp_hdr_len += ISIS_PSNP_HDRLEN;
2529
2530 auth_tlv_len = auth_tlv_length(level, circuit);
2531 lsp_count = get_max_lsp_count(stream_get_size(circuit->snd_stream)
2532 - snp_hdr_len - auth_tlv_len);
2533 return lsp_count;
3f045a08
JB
2534}
2535
eb5d44eb 2536/*
2537 * FIXME: support multiple CSNPs
2538 */
2539
d62a17ae 2540int send_csnp(struct isis_circuit *circuit, int level)
eb5d44eb 2541{
d62a17ae 2542 u_char start[ISIS_SYS_ID_LEN + 2];
2543 u_char stop[ISIS_SYS_ID_LEN + 2];
2544 struct list *list = NULL;
2545 struct listnode *node;
2546 struct isis_lsp *lsp;
2547 u_char num_lsps, loop = 1;
2548 int i, retval = ISIS_OK;
2549
2550 if (circuit->area->lspdb[level - 1] == NULL
2551 || dict_count(circuit->area->lspdb[level - 1]) == 0)
2552 return retval;
2553
2554 memset(start, 0x00, ISIS_SYS_ID_LEN + 2);
2555 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2556
2557 num_lsps = max_lsps_per_snp(ISIS_SNP_CSNP_FLAG, level, circuit);
2558
2559 while (loop) {
2560 list = list_new();
2561 lsp_build_list(start, stop, num_lsps, list,
2562 circuit->area->lspdb[level - 1]);
2563 /*
2564 * Update the stop lsp_id before encoding this CSNP.
2565 */
2566 if (listcount(list) < num_lsps) {
2567 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2568 } else {
2569 node = listtail(list);
2570 lsp = listgetdata(node);
2571 memcpy(stop, lsp->lsp_header->lsp_id,
2572 ISIS_SYS_ID_LEN + 2);
2573 }
3f045a08 2574
d62a17ae 2575 retval = build_csnp(level, start, stop, list, circuit);
2576 if (retval != ISIS_OK) {
2577 zlog_err("ISIS-Snp (%s): Build L%d CSNP on %s failed",
2578 circuit->area->area_tag, level,
2579 circuit->interface->name);
2580 list_delete(list);
2581 return retval;
2582 }
eb5d44eb 2583
d62a17ae 2584 if (isis->debugs & DEBUG_SNP_PACKETS) {
2585 zlog_debug(
2586 "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
2587 circuit->area->area_tag, level,
2588 circuit->interface->name,
2589 stream_get_endp(circuit->snd_stream));
2590 for (ALL_LIST_ELEMENTS_RO(list, node, lsp)) {
2591 zlog_debug(
2592 "ISIS-Snp (%s): CSNP entry %s, seq 0x%08x,"
2593 " cksum 0x%04x, lifetime %us",
2594 circuit->area->area_tag,
2595 rawlspid_print(lsp->lsp_header->lsp_id),
2596 ntohl(lsp->lsp_header->seq_num),
2597 ntohs(lsp->lsp_header->checksum),
2598 ntohs(lsp->lsp_header->rem_lifetime));
2599 }
2600 if (isis->debugs & DEBUG_PACKET_DUMP)
2601 zlog_dump_data(
2602 STREAM_DATA(circuit->snd_stream),
2603 stream_get_endp(circuit->snd_stream));
2604 }
eb5d44eb 2605
d62a17ae 2606 retval = circuit->tx(circuit, level);
2607 if (retval != ISIS_OK) {
2608 zlog_err("ISIS-Snp (%s): Send L%d CSNP on %s failed",
2609 circuit->area->area_tag, level,
2610 circuit->interface->name);
2611 list_delete(list);
2612 return retval;
2613 }
3f045a08 2614
d62a17ae 2615 /*
2616 * Start lsp_id of the next CSNP should be one plus the
2617 * stop lsp_id in this current CSNP.
2618 */
2619 memcpy(start, stop, ISIS_SYS_ID_LEN + 2);
2620 loop = 0;
2621 for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) {
2622 if (start[i] < (u_char)0xff) {
2623 start[i] += 1;
2624 loop = 1;
2625 break;
2626 }
2627 }
2628 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2629 list_delete(list);
2630 }
3f045a08 2631
d62a17ae 2632 return retval;
eb5d44eb 2633}
2634
d62a17ae 2635int send_l1_csnp(struct thread *thread)
eb5d44eb 2636{
d62a17ae 2637 struct isis_circuit *circuit;
2638 int retval = ISIS_OK;
eb5d44eb 2639
d62a17ae 2640 circuit = THREAD_ARG(thread);
2641 assert(circuit);
eb5d44eb 2642
d62a17ae 2643 circuit->t_send_csnp[0] = NULL;
eb5d44eb 2644
d62a17ae 2645 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2646 && circuit->u.bc.is_dr[0]) {
2647 send_csnp(circuit, 1);
2648 }
2649 /* set next timer thread */
2650 thread_add_timer(master, send_l1_csnp, circuit,
2651 isis_jitter(circuit->csnp_interval[0], CSNP_JITTER),
2652 &circuit->t_send_csnp[0]);
eb5d44eb 2653
d62a17ae 2654 return retval;
eb5d44eb 2655}
2656
d62a17ae 2657int send_l2_csnp(struct thread *thread)
eb5d44eb 2658{
d62a17ae 2659 struct isis_circuit *circuit;
2660 int retval = ISIS_OK;
eb5d44eb 2661
d62a17ae 2662 circuit = THREAD_ARG(thread);
2663 assert(circuit);
eb5d44eb 2664
d62a17ae 2665 circuit->t_send_csnp[1] = NULL;
eb5d44eb 2666
d62a17ae 2667 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2668 && circuit->u.bc.is_dr[1]) {
2669 send_csnp(circuit, 2);
2670 }
2671 /* set next timer thread */
2672 thread_add_timer(master, send_l2_csnp, circuit,
2673 isis_jitter(circuit->csnp_interval[1], CSNP_JITTER),
2674 &circuit->t_send_csnp[1]);
d70f99e1 2675
d62a17ae 2676 return retval;
eb5d44eb 2677}
2678
d62a17ae 2679static int build_psnp(int level, struct isis_circuit *circuit,
2680 struct list *lsps)
eb5d44eb 2681{
d62a17ae 2682 unsigned long lenp;
2683 u_int16_t length;
2684 struct isis_lsp *lsp;
2685 struct isis_passwd *passwd;
2686 struct listnode *node;
2687 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2688 unsigned long auth_tlv_offset = 0;
2689 int retval = ISIS_OK;
88f9d911
CF
2690 uint8_t pdu_type =
2691 (level == IS_LEVEL_1) ? L1_PARTIAL_SEQ_NUM : L2_PARTIAL_SEQ_NUM;
d62a17ae 2692
2693 isis_circuit_stream(circuit, &circuit->snd_stream);
2694
88f9d911 2695 fill_fixed_hdr(pdu_type, circuit->snd_stream);
d62a17ae 2696
2697 /*
2698 * Fill Level 1 or 2 Partial Sequence Numbers header
2699 */
2700 lenp = stream_get_endp(circuit->snd_stream);
2701 stream_putw(circuit->snd_stream, 0); /* PDU length - when we know it */
2702 stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
2703 stream_putc(circuit->snd_stream, circuit->idx);
2704
2705 /*
2706 * And TLVs
2707 */
2708
2709 if (level == IS_LEVEL_1)
2710 passwd = &circuit->area->area_passwd;
2711 else
2712 passwd = &circuit->area->domain_passwd;
2713
2714 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2715 switch (passwd->type) {
2716 /* Cleartext */
2717 case ISIS_PASSWD_TYPE_CLEARTXT:
2718 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT,
2719 passwd->len, passwd->passwd,
2720 circuit->snd_stream))
2721 return ISIS_WARNING;
2722 break;
2723
2724 /* HMAC MD5 */
2725 case ISIS_PASSWD_TYPE_HMAC_MD5:
2726 /* Remember where TLV is written so we can later
2727 * overwrite the MD5 hash */
2728 auth_tlv_offset = stream_get_endp(circuit->snd_stream);
2729 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2730 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5,
2731 ISIS_AUTH_MD5_SIZE, hmac_md5_hash,
2732 circuit->snd_stream))
2733 return ISIS_WARNING;
2734 break;
2735
2736 default:
2737 break;
2738 }
2739 }
eb5d44eb 2740
d62a17ae 2741 retval = tlv_add_lsp_entries(lsps, circuit->snd_stream);
2742 if (retval != ISIS_OK)
2743 return retval;
2744
2745 if (isis->debugs & DEBUG_SNP_PACKETS) {
2746 for (ALL_LIST_ELEMENTS_RO(lsps, node, lsp)) {
2747 zlog_debug(
2748 "ISIS-Snp (%s): PSNP entry %s, seq 0x%08x,"
2749 " cksum 0x%04x, lifetime %us",
2750 circuit->area->area_tag,
2751 rawlspid_print(lsp->lsp_header->lsp_id),
2752 ntohl(lsp->lsp_header->seq_num),
2753 ntohs(lsp->lsp_header->checksum),
2754 ntohs(lsp->lsp_header->rem_lifetime));
2755 }
2756 }
eb5d44eb 2757
d62a17ae 2758 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
2759 /* Update PDU length */
2760 stream_putw_at(circuit->snd_stream, lenp, length);
2761
2762 /* For HMAC MD5 we need to compute the md5 hash and store it */
2763 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)
2764 && passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) {
2765 hmac_md5(STREAM_DATA(circuit->snd_stream),
2766 stream_get_endp(circuit->snd_stream),
2767 (unsigned char *)&passwd->passwd, passwd->len,
2768 (unsigned char *)&hmac_md5_hash);
2769 /* Copy the hash into the stream */
2770 memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3,
2771 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2772 }
3f045a08 2773
d62a17ae 2774 return ISIS_OK;
eb5d44eb 2775}
2776
2777/*
2778 * 7.3.15.4 action on expiration of partial SNP interval
2779 * level 1
2780 */
d62a17ae 2781static int send_psnp(int level, struct isis_circuit *circuit)
eb5d44eb 2782{
d62a17ae 2783 struct isis_lsp *lsp;
2784 struct list *list = NULL;
2785 struct listnode *node;
2786 u_char num_lsps;
2787 int retval = ISIS_OK;
eb5d44eb 2788
d62a17ae 2789 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2790 && circuit->u.bc.is_dr[level - 1])
2791 return ISIS_OK;
eb5d44eb 2792
d62a17ae 2793 if (circuit->area->lspdb[level - 1] == NULL
2794 || dict_count(circuit->area->lspdb[level - 1]) == 0)
2795 return ISIS_OK;
f390d2c7 2796
d62a17ae 2797 if (!circuit->snd_stream)
2798 return ISIS_ERROR;
e38e0df0 2799
d62a17ae 2800 num_lsps = max_lsps_per_snp(ISIS_SNP_PSNP_FLAG, level, circuit);
f390d2c7 2801
d62a17ae 2802 while (1) {
2803 list = list_new();
2804 lsp_build_list_ssn(circuit, num_lsps, list,
2805 circuit->area->lspdb[level - 1]);
eb5d44eb 2806
d62a17ae 2807 if (listcount(list) == 0) {
2808 list_delete(list);
2809 return ISIS_OK;
2810 }
2811
2812 retval = build_psnp(level, circuit, list);
2813 if (retval != ISIS_OK) {
2814 zlog_err("ISIS-Snp (%s): Build L%d PSNP on %s failed",
2815 circuit->area->area_tag, level,
2816 circuit->interface->name);
2817 list_delete(list);
2818 return retval;
2819 }
2820
2821 if (isis->debugs & DEBUG_SNP_PACKETS) {
2822 zlog_debug(
2823 "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
2824 circuit->area->area_tag, level,
2825 circuit->interface->name,
2826 stream_get_endp(circuit->snd_stream));
2827 if (isis->debugs & DEBUG_PACKET_DUMP)
2828 zlog_dump_data(
2829 STREAM_DATA(circuit->snd_stream),
2830 stream_get_endp(circuit->snd_stream));
2831 }
2832
2833 retval = circuit->tx(circuit, level);
2834 if (retval != ISIS_OK) {
2835 zlog_err("ISIS-Snp (%s): Send L%d PSNP on %s failed",
2836 circuit->area->area_tag, level,
2837 circuit->interface->name);
2838 list_delete(list);
2839 return retval;
2840 }
2841
2842 /*
2843 * sending succeeded, we can clear SSN flags of this circuit
2844 * for the LSPs in list
2845 */
2846 for (ALL_LIST_ELEMENTS_RO(list, node, lsp))
2847 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
2848 list_delete(list);
2849 }
2850
2851 return retval;
eb5d44eb 2852}
2853
d62a17ae 2854int send_l1_psnp(struct thread *thread)
eb5d44eb 2855{
2856
d62a17ae 2857 struct isis_circuit *circuit;
2858 int retval = ISIS_OK;
eb5d44eb 2859
d62a17ae 2860 circuit = THREAD_ARG(thread);
2861 assert(circuit);
eb5d44eb 2862
d62a17ae 2863 circuit->t_send_psnp[0] = NULL;
eb5d44eb 2864
d62a17ae 2865 send_psnp(1, circuit);
2866 /* set next timer thread */
2867 thread_add_timer(master, send_l1_psnp, circuit,
2868 isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
2869 &circuit->t_send_psnp[0]);
eb5d44eb 2870
d62a17ae 2871 return retval;
eb5d44eb 2872}
2873
2874/*
2875 * 7.3.15.4 action on expiration of partial SNP interval
2876 * level 2
2877 */
d62a17ae 2878int send_l2_psnp(struct thread *thread)
eb5d44eb 2879{
d62a17ae 2880 struct isis_circuit *circuit;
2881 int retval = ISIS_OK;
eb5d44eb 2882
d62a17ae 2883 circuit = THREAD_ARG(thread);
2884 assert(circuit);
eb5d44eb 2885
d62a17ae 2886 circuit->t_send_psnp[1] = NULL;
eb5d44eb 2887
d62a17ae 2888 send_psnp(2, circuit);
eb5d44eb 2889
d62a17ae 2890 /* set next timer thread */
2891 thread_add_timer(master, send_l2_psnp, circuit,
2892 isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
2893 &circuit->t_send_psnp[1]);
eb5d44eb 2894
d62a17ae 2895 return retval;
eb5d44eb 2896}
2897
eb5d44eb 2898/*
2899 * ISO 10589 - 7.3.14.3
2900 */
d62a17ae 2901int send_lsp(struct thread *thread)
eb5d44eb 2902{
d62a17ae 2903 struct isis_circuit *circuit;
2904 struct isis_lsp *lsp;
2905 struct listnode *node;
2906 int clear_srm = 1;
2907 int retval = ISIS_OK;
2908
2909 circuit = THREAD_ARG(thread);
2910 assert(circuit);
2911
2912 if (!circuit->lsp_queue)
2913 return ISIS_OK;
2914
2915 node = listhead(circuit->lsp_queue);
2916
2917 /*
2918 * Handle case where there are no LSPs on the queue. This can
2919 * happen, for instance, if an adjacency goes down before this
2920 * thread gets a chance to run.
2921 */
2922 if (!node)
2923 return ISIS_OK;
2924
2925 /*
2926 * Delete LSP from lsp_queue. If it's still in queue, it is assumed
2927 * as 'transmit pending', but send_lsp may never be called again.
2928 * Retry will happen because SRM flag will not be cleared.
2929 */
2930 lsp = listgetdata(node);
2931 list_delete_node(circuit->lsp_queue, node);
2932
2933 /* Set the last-cleared time if the queue is empty. */
2934 /* TODO: Is is possible that new lsps keep being added to the queue
2935 * that the queue is never empty? */
2936 if (list_isempty(circuit->lsp_queue))
2937 circuit->lsp_queue_last_cleared = time(NULL);
2938
2939 if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
2940 goto out;
2941
2942 /*
2943 * Do not send if levels do not match
2944 */
2945 if (!(lsp->level & circuit->is_type))
2946 goto out;
2947
2948 /*
2949 * Do not send if we do not have adjacencies in state up on the circuit
2950 */
2951 if (circuit->upadjcount[lsp->level - 1] == 0)
2952 goto out;
2953
2954 /* stream_copy will assert and stop program execution if LSP is larger
2955 * than
2956 * the circuit's MTU. So handle and log this case here. */
2957 if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) {
2958 zlog_err(
2959 "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x,"
2960 " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu"
2961 " while interface stream size is %zu.",
2962 circuit->area->area_tag, lsp->level,
2963 rawlspid_print(lsp->lsp_header->lsp_id),
2964 ntohl(lsp->lsp_header->seq_num),
2965 ntohs(lsp->lsp_header->checksum),
2966 ntohs(lsp->lsp_header->rem_lifetime),
2967 circuit->interface->name, stream_get_endp(lsp->pdu),
2968 stream_get_size(circuit->snd_stream));
2969 if (isis->debugs & DEBUG_PACKET_DUMP)
2970 zlog_dump_data(STREAM_DATA(lsp->pdu),
2971 stream_get_endp(lsp->pdu));
2972 retval = ISIS_ERROR;
2973 goto out;
2974 }
eb5d44eb 2975
d62a17ae 2976 /* copy our lsp to the send buffer */
2977 stream_copy(circuit->snd_stream, lsp->pdu);
2978
2979 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
2980 zlog_debug(
2981 "ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
2982 " lifetime %us on %s",
2983 circuit->area->area_tag, lsp->level,
2984 rawlspid_print(lsp->lsp_header->lsp_id),
2985 ntohl(lsp->lsp_header->seq_num),
2986 ntohs(lsp->lsp_header->checksum),
2987 ntohs(lsp->lsp_header->rem_lifetime),
2988 circuit->interface->name);
2989 if (isis->debugs & DEBUG_PACKET_DUMP)
2990 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2991 stream_get_endp(circuit->snd_stream));
2992 }
3f045a08 2993
d62a17ae 2994 clear_srm = 0;
2995 retval = circuit->tx(circuit, lsp->level);
2996 if (retval != ISIS_OK) {
2997 zlog_err("ISIS-Upd (%s): Send L%d LSP on %s failed %s",
2998 circuit->area->area_tag, lsp->level,
2999 circuit->interface->name,
3000 (retval == ISIS_WARNING) ? "temporarily"
3001 : "permanently");
3002 }
f390d2c7 3003
cfd1f27b 3004out:
d62a17ae 3005 if (clear_srm
3006 || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST)
3007 || (retval != ISIS_OK && retval != ISIS_WARNING)) {
3008 /* SRM flag will trigger retransmission. We will not retransmit
3009 * if we
3010 * encountered a fatal error.
3011 * On success, they should only be cleared if it's a broadcast
3012 * circuit.
3013 * On a P2P circuit, we will wait for the ack from the neighbor
3014 * to clear
3015 * the fag.
3016 */
3017 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
3018 }
eb5d44eb 3019
d62a17ae 3020 return retval;
f390d2c7 3021}
eb5d44eb 3022
d62a17ae 3023int ack_lsp(struct isis_link_state_hdr *hdr, struct isis_circuit *circuit,
3024 int level)
eb5d44eb 3025{
d62a17ae 3026 unsigned long lenp;
3027 int retval;
3028 u_int16_t length;
88f9d911
CF
3029 uint8_t pdu_type =
3030 (level == IS_LEVEL_1) ? L1_PARTIAL_SEQ_NUM : L2_PARTIAL_SEQ_NUM;
d62a17ae 3031
3032 isis_circuit_stream(circuit, &circuit->snd_stream);
3033
88f9d911 3034 fill_fixed_hdr(pdu_type, circuit->snd_stream);
d62a17ae 3035
3036 lenp = stream_get_endp(circuit->snd_stream);
3037 stream_putw(circuit->snd_stream, 0); /* PDU length */
3038 stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
3039 stream_putc(circuit->snd_stream, circuit->idx);
3040 stream_putc(circuit->snd_stream, 9); /* code */
3041 stream_putc(circuit->snd_stream, 16); /* len */
3042
3043 stream_putw(circuit->snd_stream, ntohs(hdr->rem_lifetime));
3044 stream_put(circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2);
3045 stream_putl(circuit->snd_stream, ntohl(hdr->seq_num));
3046 stream_putw(circuit->snd_stream, ntohs(hdr->checksum));
3047
3048 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
3049 /* Update PDU length */
3050 stream_putw_at(circuit->snd_stream, lenp, length);
3051
3052 retval = circuit->tx(circuit, level);
3053 if (retval != ISIS_OK)
3054 zlog_err("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
3055 circuit->area->area_tag, level,
3056 circuit->interface->name);
3057
3058 return retval;
eb5d44eb 3059}