]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_pdu.c
isisd: add new tlv parser
[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
eb5d44eb 1891/*
1892 * PDU Dispatcher
1893 */
1894
d62a17ae 1895static int isis_handle_pdu(struct isis_circuit *circuit, u_char *ssnpa)
eb5d44eb 1896{
d62a17ae 1897 struct isis_fixed_hdr *hdr;
eb5d44eb 1898
d62a17ae 1899 int retval = ISIS_OK;
eb5d44eb 1900
d62a17ae 1901 /*
1902 * Let's first read data from stream to the header
1903 */
1904 hdr = (struct isis_fixed_hdr *)STREAM_DATA(circuit->rcv_stream);
eb5d44eb 1905
d62a17ae 1906 if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS)) {
1907 zlog_err("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp);
1908 return ISIS_ERROR;
1909 }
eb5d44eb 1910
d62a17ae 1911 /* now we need to know if this is an ISO 9542 packet and
1912 * take real good care of it, waaa!
1913 */
1914 if (hdr->idrp == ISO9542_ESIS) {
1915 zlog_err("No support for ES-IS packet IDRP=%02x", hdr->idrp);
1916 return ISIS_ERROR;
1917 }
1918 stream_set_getp(circuit->rcv_stream, ISIS_FIXED_HDR_LEN);
3f045a08 1919
d62a17ae 1920 /*
1921 * and then process it
1922 */
eb5d44eb 1923
d62a17ae 1924 if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN) {
1925 zlog_err("Fixed header length = %d", hdr->length);
1926 return ISIS_ERROR;
1927 }
eb5d44eb 1928
d62a17ae 1929 if (hdr->version1 != 1) {
1930 zlog_warn("Unsupported ISIS version %u", hdr->version1);
1931 return ISIS_WARNING;
1932 }
1933 /* either 6 or 0 */
1934 if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN)) {
1935 zlog_err(
1936 "IDFieldLengthMismatch: ID Length field in a received PDU %u, "
1937 "while the parameter for this IS is %u",
1938 hdr->id_len, ISIS_SYS_ID_LEN);
1939 return ISIS_ERROR;
1940 }
eb5d44eb 1941
d62a17ae 1942 if (hdr->version2 != 1) {
1943 zlog_warn("Unsupported ISIS version %u", hdr->version2);
1944 return ISIS_WARNING;
1945 }
3f045a08 1946
d62a17ae 1947 if (circuit->is_passive) {
1948 zlog_warn("Received ISIS PDU on passive circuit %s",
1949 circuit->interface->name);
1950 return ISIS_WARNING;
1951 }
3f045a08 1952
d62a17ae 1953 /* either 3 or 0 */
1954 if ((hdr->max_area_addrs != 0)
1955 && (hdr->max_area_addrs != isis->max_area_addrs)) {
1956 zlog_err(
1957 "maximumAreaAddressesMismatch: maximumAreaAdresses in a "
1958 "received PDU %u while the parameter for this IS is %u",
1959 hdr->max_area_addrs, isis->max_area_addrs);
1960 return ISIS_ERROR;
1961 }
eb5d44eb 1962
d62a17ae 1963 switch (hdr->pdu_type) {
1964 case L1_LAN_HELLO:
1965 retval = process_lan_hello(ISIS_LEVEL1, circuit, ssnpa);
1966 break;
1967 case L2_LAN_HELLO:
1968 retval = process_lan_hello(ISIS_LEVEL2, circuit, ssnpa);
1969 break;
1970 case P2P_HELLO:
1971 retval = process_p2p_hello(circuit);
1972 break;
1973 case L1_LINK_STATE:
1974 retval = process_lsp(ISIS_LEVEL1, circuit, ssnpa);
1975 break;
1976 case L2_LINK_STATE:
1977 retval = process_lsp(ISIS_LEVEL2, circuit, ssnpa);
1978 break;
1979 case L1_COMPLETE_SEQ_NUM:
1980 retval = process_csnp(ISIS_LEVEL1, circuit, ssnpa);
1981 break;
1982 case L2_COMPLETE_SEQ_NUM:
1983 retval = process_csnp(ISIS_LEVEL2, circuit, ssnpa);
1984 break;
1985 case L1_PARTIAL_SEQ_NUM:
1986 retval = process_psnp(ISIS_LEVEL1, circuit, ssnpa);
1987 break;
1988 case L2_PARTIAL_SEQ_NUM:
1989 retval = process_psnp(ISIS_LEVEL2, circuit, ssnpa);
1990 break;
1991 default:
1992 return ISIS_ERROR;
1993 }
eb5d44eb 1994
d62a17ae 1995 return retval;
eb5d44eb 1996}
1997
d62a17ae 1998int isis_receive(struct thread *thread)
eb5d44eb 1999{
d62a17ae 2000 struct isis_circuit *circuit;
2001 u_char ssnpa[ETH_ALEN];
2002 int retval;
eb5d44eb 2003
d62a17ae 2004 /*
2005 * Get the circuit
2006 */
2007 circuit = THREAD_ARG(thread);
2008 assert(circuit);
eb5d44eb 2009
d62a17ae 2010 circuit->t_read = NULL;
eb5d44eb 2011
d62a17ae 2012 isis_circuit_stream(circuit, &circuit->rcv_stream);
eb5d44eb 2013
d62a17ae 2014 retval = circuit->rx(circuit, ssnpa);
eb5d44eb 2015
d62a17ae 2016 if (retval == ISIS_OK)
2017 retval = isis_handle_pdu(circuit, ssnpa);
eb5d44eb 2018
d62a17ae 2019 /*
2020 * prepare for next packet.
2021 */
2022 if (!circuit->is_passive)
2023 isis_circuit_prepare(circuit);
eb5d44eb 2024
d62a17ae 2025 return retval;
eb5d44eb 2026}
2027
d62a17ae 2028/* filling of the fixed isis header */
2029void fill_fixed_hdr(struct isis_fixed_hdr *hdr, u_char pdu_type)
eb5d44eb 2030{
d62a17ae 2031 memset(hdr, 0, sizeof(struct isis_fixed_hdr));
2032
2033 hdr->idrp = ISO10589_ISIS;
2034
2035 switch (pdu_type) {
2036 case L1_LAN_HELLO:
2037 case L2_LAN_HELLO:
2038 hdr->length = ISIS_LANHELLO_HDRLEN;
2039 break;
2040 case P2P_HELLO:
2041 hdr->length = ISIS_P2PHELLO_HDRLEN;
2042 break;
2043 case L1_LINK_STATE:
2044 case L2_LINK_STATE:
2045 hdr->length = ISIS_LSP_HDR_LEN;
2046 break;
2047 case L1_COMPLETE_SEQ_NUM:
2048 case L2_COMPLETE_SEQ_NUM:
2049 hdr->length = ISIS_CSNP_HDRLEN;
2050 break;
2051 case L1_PARTIAL_SEQ_NUM:
2052 case L2_PARTIAL_SEQ_NUM:
2053 hdr->length = ISIS_PSNP_HDRLEN;
2054 break;
2055 default:
2056 zlog_warn("fill_fixed_hdr(): unknown pdu type %d", pdu_type);
2057 return;
2058 }
2059 hdr->length += ISIS_FIXED_HDR_LEN;
2060 hdr->pdu_type = pdu_type;
2061 hdr->version1 = 1;
2062 hdr->id_len = 0; /* ISIS_SYS_ID_LEN - 0==6 */
2063 hdr->version2 = 1;
2064 hdr->max_area_addrs = 0; /* isis->max_area_addrs - 0==3 */
eb5d44eb 2065}
2066
eb5d44eb 2067/*
d62a17ae 2068 * SEND SIDE
eb5d44eb 2069 */
d62a17ae 2070static void fill_fixed_hdr_andstream(struct isis_fixed_hdr *hdr,
2071 u_char pdu_type, struct stream *stream)
eb5d44eb 2072{
d62a17ae 2073 fill_fixed_hdr(hdr, pdu_type);
2074
2075 stream_putc(stream, hdr->idrp);
2076 stream_putc(stream, hdr->length);
2077 stream_putc(stream, hdr->version1);
2078 stream_putc(stream, hdr->id_len);
2079 stream_putc(stream, hdr->pdu_type);
2080 stream_putc(stream, hdr->version2);
2081 stream_putc(stream, hdr->reserved);
2082 stream_putc(stream, hdr->max_area_addrs);
2083
2084 return;
eb5d44eb 2085}
2086
d62a17ae 2087int send_hello(struct isis_circuit *circuit, int level)
eb5d44eb 2088{
d62a17ae 2089 struct isis_fixed_hdr fixed_hdr;
2090 struct isis_lan_hello_hdr hello_hdr;
2091 struct isis_p2p_hello_hdr p2p_hello_hdr;
2092 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2093 size_t len_pointer, length, auth_tlv_offset = 0;
2094 u_int32_t interval;
2095 int retval;
2096
2097 if (circuit->is_passive)
2098 return ISIS_OK;
2099
2100 if (circuit->interface->mtu == 0) {
2101 zlog_warn("circuit has zero MTU");
2102 return ISIS_WARNING;
f390d2c7 2103 }
eb5d44eb 2104
d62a17ae 2105 isis_circuit_stream(circuit, &circuit->snd_stream);
99894f9a 2106
d62a17ae 2107 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
2108 if (level == IS_LEVEL_1)
2109 fill_fixed_hdr_andstream(&fixed_hdr, L1_LAN_HELLO,
2110 circuit->snd_stream);
2111 else
2112 fill_fixed_hdr_andstream(&fixed_hdr, L2_LAN_HELLO,
2113 circuit->snd_stream);
2114 else
2115 fill_fixed_hdr_andstream(&fixed_hdr, P2P_HELLO,
2116 circuit->snd_stream);
2117
2118 /*
2119 * Fill LAN Level 1 or 2 Hello PDU header
2120 */
2121 memset(&hello_hdr, 0, sizeof(struct isis_lan_hello_hdr));
2122 interval = circuit->hello_multiplier[level - 1]
2123 * circuit->hello_interval[level - 1];
2124 if (interval > USHRT_MAX)
2125 interval = USHRT_MAX;
2126 hello_hdr.circuit_t = circuit->is_type;
2127 memcpy(hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN);
2128 hello_hdr.hold_time = htons((u_int16_t)interval);
2129
2130 hello_hdr.pdu_len = 0; /* Update the PDU Length later */
2131 len_pointer =
2132 stream_get_endp(circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN;
2133
2134 /* copy the shared part of the hello to the p2p hello if needed */
2135 if (circuit->circ_type == CIRCUIT_T_P2P) {
2136 memcpy(&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN);
2137 p2p_hello_hdr.local_id = circuit->circuit_id;
2138 /* FIXME: need better understanding */
2139 stream_put(circuit->snd_stream, &p2p_hello_hdr,
2140 ISIS_P2PHELLO_HDRLEN);
2141 } else {
2142 hello_hdr.prio = circuit->priority[level - 1];
2143 if (level == IS_LEVEL_1) {
2144 memcpy(hello_hdr.lan_id, circuit->u.bc.l1_desig_is,
2145 ISIS_SYS_ID_LEN + 1);
2146 } else if (level == IS_LEVEL_2) {
2147 memcpy(hello_hdr.lan_id, circuit->u.bc.l2_desig_is,
2148 ISIS_SYS_ID_LEN + 1);
2149 }
2150 stream_put(circuit->snd_stream, &hello_hdr,
2151 ISIS_LANHELLO_HDRLEN);
2152 }
eb5d44eb 2153
d62a17ae 2154 /*
2155 * Then the variable length part.
2156 */
2157
2158 /* add circuit password */
2159 switch (circuit->passwd.type) {
2160 /* Cleartext */
2161 case ISIS_PASSWD_TYPE_CLEARTXT:
2162 if (tlv_add_authinfo(circuit->passwd.type, circuit->passwd.len,
2163 circuit->passwd.passwd,
2164 circuit->snd_stream))
2165 return ISIS_WARNING;
2166 break;
2167
2168 /* HMAC MD5 */
2169 case ISIS_PASSWD_TYPE_HMAC_MD5:
2170 /* Remember where TLV is written so we can later overwrite the
2171 * MD5 hash */
2172 auth_tlv_offset = stream_get_endp(circuit->snd_stream);
2173 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2174 if (tlv_add_authinfo(circuit->passwd.type, ISIS_AUTH_MD5_SIZE,
2175 hmac_md5_hash, circuit->snd_stream))
2176 return ISIS_WARNING;
2177 break;
2178
2179 default:
2180 break;
2181 }
eb5d44eb 2182
d62a17ae 2183 /* Area Addresses TLV */
2184 if (listcount(circuit->area->area_addrs) == 0)
2185 return ISIS_WARNING;
2186 if (tlv_add_area_addrs(circuit->area->area_addrs, circuit->snd_stream))
2187 return ISIS_WARNING;
2188
2189 /* LAN Neighbors TLV */
2190 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2191 if (level == IS_LEVEL_1 && circuit->u.bc.lan_neighs[0]
2192 && listcount(circuit->u.bc.lan_neighs[0]) > 0)
2193 if (tlv_add_lan_neighs(circuit->u.bc.lan_neighs[0],
2194 circuit->snd_stream))
2195 return ISIS_WARNING;
2196 if (level == IS_LEVEL_2 && circuit->u.bc.lan_neighs[1]
2197 && listcount(circuit->u.bc.lan_neighs[1]) > 0)
2198 if (tlv_add_lan_neighs(circuit->u.bc.lan_neighs[1],
2199 circuit->snd_stream))
2200 return ISIS_WARNING;
2201 }
eb5d44eb 2202
d62a17ae 2203 /* Protocols Supported TLV */
2204 if (circuit->nlpids.count > 0)
2205 if (tlv_add_nlpid(&circuit->nlpids, circuit->snd_stream))
2206 return ISIS_WARNING;
2207 /* IP interface Address TLV */
2208 if (circuit->ip_router && circuit->ip_addrs
2209 && listcount(circuit->ip_addrs) > 0)
2210 if (tlv_add_ip_addrs(circuit->ip_addrs, circuit->snd_stream))
2211 return ISIS_WARNING;
2212
2213 /*
2214 * MT Supported TLV
2215 *
2216 * TLV gets included if no topology is enabled on the interface,
2217 * if one topology other than #0 is enabled, or if multiple topologies
2218 * are enabled.
2219 */
2220 struct isis_circuit_mt_setting **mt_settings;
2221 unsigned int mt_count;
2222
2223 mt_settings = circuit_mt_settings(circuit, &mt_count);
2224 if ((mt_count == 0 && area_is_mt(circuit->area))
2225 || (mt_count == 1 && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST)
2226 || (mt_count > 1)) {
2227 struct list *mt_info = list_new();
2228 mt_info->del = free_tlv;
2229
2230 for (unsigned int i = 0; i < mt_count; i++) {
2231 struct mt_router_info *info;
2232
2233 info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info));
2234 info->mtid = mt_settings[i]->mtid;
2235 /* overload info is not valid in IIH, so it's not
2236 * included here */
2237 listnode_add(mt_info, info);
2238 }
2239 tlv_add_mt_router_info(mt_info, circuit->snd_stream);
2240 list_free(mt_info);
2241 }
eb5d44eb 2242
d62a17ae 2243 /* IPv6 Interface Address TLV */
2244 if (circuit->ipv6_router && circuit->ipv6_link
2245 && listcount(circuit->ipv6_link) > 0)
2246 if (tlv_add_ipv6_addrs(circuit->ipv6_link, circuit->snd_stream))
2247 return ISIS_WARNING;
2248
2249 if (circuit->pad_hellos)
2250 if (tlv_add_padding(circuit->snd_stream))
2251 return ISIS_WARNING;
2252
2253 length = stream_get_endp(circuit->snd_stream);
2254 /* Update PDU length */
2255 stream_putw_at(circuit->snd_stream, len_pointer, (u_int16_t)length);
2256
2257 /* For HMAC MD5 we need to compute the md5 hash and store it */
2258 if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) {
2259 hmac_md5(STREAM_DATA(circuit->snd_stream),
2260 stream_get_endp(circuit->snd_stream),
2261 (unsigned char *)&circuit->passwd.passwd,
2262 circuit->passwd.len, (unsigned char *)&hmac_md5_hash);
2263 /* Copy the hash into the stream */
2264 memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3,
2265 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
f390d2c7 2266 }
d62a17ae 2267
2268 if (isis->debugs & DEBUG_ADJ_PACKETS) {
2269 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2270 zlog_debug(
2271 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
2272 circuit->area->area_tag, level,
2273 circuit->interface->name, length);
2274 } else {
2275 zlog_debug(
2276 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
2277 circuit->area->area_tag,
2278 circuit->interface->name, length);
2279 }
2280 if (isis->debugs & DEBUG_PACKET_DUMP)
2281 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2282 stream_get_endp(circuit->snd_stream));
f390d2c7 2283 }
eb5d44eb 2284
d62a17ae 2285 retval = circuit->tx(circuit, level);
2286 if (retval != ISIS_OK)
2287 zlog_err("ISIS-Adj (%s): Send L%d IIH on %s failed",
2288 circuit->area->area_tag, level,
2289 circuit->interface->name);
eb5d44eb 2290
d62a17ae 2291 return retval;
eb5d44eb 2292}
2293
d62a17ae 2294int send_lan_l1_hello(struct thread *thread)
eb5d44eb 2295{
d62a17ae 2296 struct isis_circuit *circuit;
2297 int retval;
2298
2299 circuit = THREAD_ARG(thread);
2300 assert(circuit);
2301 circuit->u.bc.t_send_lan_hello[0] = NULL;
2302
2303 if (!(circuit->area->is_type & IS_LEVEL_1)) {
2304 zlog_warn(
2305 "ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
2306 circuit->area->area_tag);
2307 return 1;
2308 }
ddfdbd32 2309
d62a17ae 2310 if (circuit->u.bc.run_dr_elect[0])
2311 isis_dr_elect(circuit, 1);
eb5d44eb 2312
d62a17ae 2313 retval = send_hello(circuit, 1);
eb5d44eb 2314
d62a17ae 2315 /* set next timer thread */
2316 thread_add_timer(master, send_lan_l1_hello, circuit,
2317 isis_jitter(circuit->hello_interval[0], IIH_JITTER),
2318 &circuit->u.bc.t_send_lan_hello[0]);
eb5d44eb 2319
d62a17ae 2320 return retval;
eb5d44eb 2321}
2322
d62a17ae 2323int send_lan_l2_hello(struct thread *thread)
eb5d44eb 2324{
d62a17ae 2325 struct isis_circuit *circuit;
2326 int retval;
eb5d44eb 2327
d62a17ae 2328 circuit = THREAD_ARG(thread);
2329 assert(circuit);
2330 circuit->u.bc.t_send_lan_hello[1] = NULL;
eb5d44eb 2331
d62a17ae 2332 if (!(circuit->area->is_type & IS_LEVEL_2)) {
2333 zlog_warn("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
2334 circuit->area->area_tag);
2335 return 1;
2336 }
ddfdbd32 2337
d62a17ae 2338 if (circuit->u.bc.run_dr_elect[1])
2339 isis_dr_elect(circuit, 2);
eb5d44eb 2340
d62a17ae 2341 retval = send_hello(circuit, 2);
eb5d44eb 2342
d62a17ae 2343 /* set next timer thread */
2344 thread_add_timer(master, send_lan_l2_hello, circuit,
2345 isis_jitter(circuit->hello_interval[1], IIH_JITTER),
2346 &circuit->u.bc.t_send_lan_hello[1]);
eb5d44eb 2347
d62a17ae 2348 return retval;
eb5d44eb 2349}
2350
d62a17ae 2351int send_p2p_hello(struct thread *thread)
eb5d44eb 2352{
d62a17ae 2353 struct isis_circuit *circuit;
eb5d44eb 2354
d62a17ae 2355 circuit = THREAD_ARG(thread);
2356 assert(circuit);
2357 circuit->u.p2p.t_send_p2p_hello = NULL;
eb5d44eb 2358
d62a17ae 2359 send_hello(circuit, 1);
eb5d44eb 2360
d62a17ae 2361 /* set next timer thread */
2362 thread_add_timer(master, send_p2p_hello, circuit,
2363 isis_jitter(circuit->hello_interval[1], IIH_JITTER),
2364 &circuit->u.p2p.t_send_p2p_hello);
eb5d44eb 2365
d62a17ae 2366 return ISIS_OK;
eb5d44eb 2367}
2368
d62a17ae 2369static int build_csnp(int level, u_char *start, u_char *stop, struct list *lsps,
2370 struct isis_circuit *circuit)
eb5d44eb 2371{
d62a17ae 2372 struct isis_fixed_hdr fixed_hdr;
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;
2379
2380 isis_circuit_stream(circuit, &circuit->snd_stream);
2381
2382 if (level == IS_LEVEL_1)
2383 fill_fixed_hdr_andstream(&fixed_hdr, L1_COMPLETE_SEQ_NUM,
2384 circuit->snd_stream);
2385 else
2386 fill_fixed_hdr_andstream(&fixed_hdr, L2_COMPLETE_SEQ_NUM,
2387 circuit->snd_stream);
2388
2389 /*
2390 * Fill Level 1 or 2 Complete Sequence Numbers header
2391 */
2392
2393 lenp = stream_get_endp(circuit->snd_stream);
2394 stream_putw(circuit->snd_stream, 0); /* PDU length - when we know it */
2395 /* no need to send the source here, it is always us if we csnp */
2396 stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
2397 /* with zero circuit id - ref 9.10, 9.11 */
2398 stream_putc(circuit->snd_stream, 0x00);
2399
2400 stream_put(circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2);
2401 stream_put(circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2);
2402
2403 /*
2404 * And TLVs
2405 */
2406 if (level == IS_LEVEL_1)
2407 passwd = &circuit->area->area_passwd;
2408 else
2409 passwd = &circuit->area->domain_passwd;
2410
2411 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2412 switch (passwd->type) {
2413 /* Cleartext */
2414 case ISIS_PASSWD_TYPE_CLEARTXT:
2415 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT,
2416 passwd->len, passwd->passwd,
2417 circuit->snd_stream))
2418 return ISIS_WARNING;
2419 break;
2420
2421 /* HMAC MD5 */
2422 case ISIS_PASSWD_TYPE_HMAC_MD5:
2423 /* Remember where TLV is written so we can later
2424 * overwrite the MD5 hash */
2425 auth_tlv_offset = stream_get_endp(circuit->snd_stream);
2426 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2427 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5,
2428 ISIS_AUTH_MD5_SIZE, hmac_md5_hash,
2429 circuit->snd_stream))
2430 return ISIS_WARNING;
2431 break;
2432
2433 default:
2434 break;
2435 }
2436 }
eb5d44eb 2437
d62a17ae 2438 retval = tlv_add_lsp_entries(lsps, circuit->snd_stream);
2439 if (retval != ISIS_OK)
2440 return retval;
2441
2442 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
2443 /* Update PU length */
2444 stream_putw_at(circuit->snd_stream, lenp, length);
2445
2446 /* For HMAC MD5 we need to compute the md5 hash and store it */
2447 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)
2448 && passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) {
2449 hmac_md5(STREAM_DATA(circuit->snd_stream),
2450 stream_get_endp(circuit->snd_stream),
2451 (unsigned char *)&passwd->passwd, passwd->len,
2452 (unsigned char *)&hmac_md5_hash);
2453 /* Copy the hash into the stream */
2454 memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3,
2455 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2456 }
3f045a08 2457
d62a17ae 2458 return retval;
eb5d44eb 2459}
2460
3f045a08
JB
2461/*
2462 * Count the maximum number of lsps that can be accomodated by a given size.
2463 */
d62a17ae 2464static uint16_t get_max_lsp_count(uint16_t size)
3f045a08 2465{
d62a17ae 2466 uint16_t tlv_count;
2467 uint16_t lsp_count;
2468 uint16_t remaining_size;
3f045a08 2469
d62a17ae 2470 /* First count the full size TLVs */
2471 tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE;
2472 lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN);
3f045a08 2473
d62a17ae 2474 /* The last TLV, if any */
2475 remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE;
2476 if (remaining_size - 2 >= LSP_ENTRIES_LEN)
2477 lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN;
3f045a08 2478
d62a17ae 2479 return lsp_count;
3f045a08
JB
2480}
2481
2482/*
2483 * Calculate the length of Authentication Info. TLV.
2484 */
d62a17ae 2485static uint16_t auth_tlv_length(int level, struct isis_circuit *circuit)
3f045a08 2486{
d62a17ae 2487 struct isis_passwd *passwd;
2488 uint16_t length;
2489
2490 if (level == IS_LEVEL_1)
2491 passwd = &circuit->area->area_passwd;
2492 else
2493 passwd = &circuit->area->domain_passwd;
2494
2495 /* Also include the length of TLV header */
2496 length = AUTH_INFO_HDRLEN;
2497 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2498 switch (passwd->type) {
2499 /* Cleartext */
2500 case ISIS_PASSWD_TYPE_CLEARTXT:
2501 length += passwd->len;
2502 break;
2503
2504 /* HMAC MD5 */
2505 case ISIS_PASSWD_TYPE_HMAC_MD5:
2506 length += ISIS_AUTH_MD5_SIZE;
2507 break;
2508
2509 default:
2510 break;
2511 }
2512 }
3f045a08 2513
d62a17ae 2514 return length;
3f045a08
JB
2515}
2516
2517/*
2518 * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
2519 */
d62a17ae 2520static uint16_t max_lsps_per_snp(int snp_type, int level,
2521 struct isis_circuit *circuit)
3f045a08 2522{
d62a17ae 2523 int snp_hdr_len;
2524 int auth_tlv_len;
2525 uint16_t lsp_count;
2526
2527 snp_hdr_len = ISIS_FIXED_HDR_LEN;
2528 if (snp_type == ISIS_SNP_CSNP_FLAG)
2529 snp_hdr_len += ISIS_CSNP_HDRLEN;
2530 else
2531 snp_hdr_len += ISIS_PSNP_HDRLEN;
2532
2533 auth_tlv_len = auth_tlv_length(level, circuit);
2534 lsp_count = get_max_lsp_count(stream_get_size(circuit->snd_stream)
2535 - snp_hdr_len - auth_tlv_len);
2536 return lsp_count;
3f045a08
JB
2537}
2538
eb5d44eb 2539/*
2540 * FIXME: support multiple CSNPs
2541 */
2542
d62a17ae 2543int send_csnp(struct isis_circuit *circuit, int level)
eb5d44eb 2544{
d62a17ae 2545 u_char start[ISIS_SYS_ID_LEN + 2];
2546 u_char stop[ISIS_SYS_ID_LEN + 2];
2547 struct list *list = NULL;
2548 struct listnode *node;
2549 struct isis_lsp *lsp;
2550 u_char num_lsps, loop = 1;
2551 int i, retval = ISIS_OK;
2552
2553 if (circuit->area->lspdb[level - 1] == NULL
2554 || dict_count(circuit->area->lspdb[level - 1]) == 0)
2555 return retval;
2556
2557 memset(start, 0x00, ISIS_SYS_ID_LEN + 2);
2558 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2559
2560 num_lsps = max_lsps_per_snp(ISIS_SNP_CSNP_FLAG, level, circuit);
2561
2562 while (loop) {
2563 list = list_new();
2564 lsp_build_list(start, stop, num_lsps, list,
2565 circuit->area->lspdb[level - 1]);
2566 /*
2567 * Update the stop lsp_id before encoding this CSNP.
2568 */
2569 if (listcount(list) < num_lsps) {
2570 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2571 } else {
2572 node = listtail(list);
2573 lsp = listgetdata(node);
2574 memcpy(stop, lsp->lsp_header->lsp_id,
2575 ISIS_SYS_ID_LEN + 2);
2576 }
3f045a08 2577
d62a17ae 2578 retval = build_csnp(level, start, stop, list, circuit);
2579 if (retval != ISIS_OK) {
2580 zlog_err("ISIS-Snp (%s): Build L%d CSNP on %s failed",
2581 circuit->area->area_tag, level,
2582 circuit->interface->name);
2583 list_delete(list);
2584 return retval;
2585 }
eb5d44eb 2586
d62a17ae 2587 if (isis->debugs & DEBUG_SNP_PACKETS) {
2588 zlog_debug(
2589 "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
2590 circuit->area->area_tag, level,
2591 circuit->interface->name,
2592 stream_get_endp(circuit->snd_stream));
2593 for (ALL_LIST_ELEMENTS_RO(list, node, lsp)) {
2594 zlog_debug(
2595 "ISIS-Snp (%s): CSNP entry %s, seq 0x%08x,"
2596 " cksum 0x%04x, lifetime %us",
2597 circuit->area->area_tag,
2598 rawlspid_print(lsp->lsp_header->lsp_id),
2599 ntohl(lsp->lsp_header->seq_num),
2600 ntohs(lsp->lsp_header->checksum),
2601 ntohs(lsp->lsp_header->rem_lifetime));
2602 }
2603 if (isis->debugs & DEBUG_PACKET_DUMP)
2604 zlog_dump_data(
2605 STREAM_DATA(circuit->snd_stream),
2606 stream_get_endp(circuit->snd_stream));
2607 }
eb5d44eb 2608
d62a17ae 2609 retval = circuit->tx(circuit, level);
2610 if (retval != ISIS_OK) {
2611 zlog_err("ISIS-Snp (%s): Send L%d CSNP on %s failed",
2612 circuit->area->area_tag, level,
2613 circuit->interface->name);
2614 list_delete(list);
2615 return retval;
2616 }
3f045a08 2617
d62a17ae 2618 /*
2619 * Start lsp_id of the next CSNP should be one plus the
2620 * stop lsp_id in this current CSNP.
2621 */
2622 memcpy(start, stop, ISIS_SYS_ID_LEN + 2);
2623 loop = 0;
2624 for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) {
2625 if (start[i] < (u_char)0xff) {
2626 start[i] += 1;
2627 loop = 1;
2628 break;
2629 }
2630 }
2631 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2632 list_delete(list);
2633 }
3f045a08 2634
d62a17ae 2635 return retval;
eb5d44eb 2636}
2637
d62a17ae 2638int send_l1_csnp(struct thread *thread)
eb5d44eb 2639{
d62a17ae 2640 struct isis_circuit *circuit;
2641 int retval = ISIS_OK;
eb5d44eb 2642
d62a17ae 2643 circuit = THREAD_ARG(thread);
2644 assert(circuit);
eb5d44eb 2645
d62a17ae 2646 circuit->t_send_csnp[0] = NULL;
eb5d44eb 2647
d62a17ae 2648 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2649 && circuit->u.bc.is_dr[0]) {
2650 send_csnp(circuit, 1);
2651 }
2652 /* set next timer thread */
2653 thread_add_timer(master, send_l1_csnp, circuit,
2654 isis_jitter(circuit->csnp_interval[0], CSNP_JITTER),
2655 &circuit->t_send_csnp[0]);
eb5d44eb 2656
d62a17ae 2657 return retval;
eb5d44eb 2658}
2659
d62a17ae 2660int send_l2_csnp(struct thread *thread)
eb5d44eb 2661{
d62a17ae 2662 struct isis_circuit *circuit;
2663 int retval = ISIS_OK;
eb5d44eb 2664
d62a17ae 2665 circuit = THREAD_ARG(thread);
2666 assert(circuit);
eb5d44eb 2667
d62a17ae 2668 circuit->t_send_csnp[1] = NULL;
eb5d44eb 2669
d62a17ae 2670 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2671 && circuit->u.bc.is_dr[1]) {
2672 send_csnp(circuit, 2);
2673 }
2674 /* set next timer thread */
2675 thread_add_timer(master, send_l2_csnp, circuit,
2676 isis_jitter(circuit->csnp_interval[1], CSNP_JITTER),
2677 &circuit->t_send_csnp[1]);
d70f99e1 2678
d62a17ae 2679 return retval;
eb5d44eb 2680}
2681
d62a17ae 2682static int build_psnp(int level, struct isis_circuit *circuit,
2683 struct list *lsps)
eb5d44eb 2684{
d62a17ae 2685 struct isis_fixed_hdr fixed_hdr;
2686 unsigned long lenp;
2687 u_int16_t length;
2688 struct isis_lsp *lsp;
2689 struct isis_passwd *passwd;
2690 struct listnode *node;
2691 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2692 unsigned long auth_tlv_offset = 0;
2693 int retval = ISIS_OK;
2694
2695 isis_circuit_stream(circuit, &circuit->snd_stream);
2696
2697 if (level == IS_LEVEL_1)
2698 fill_fixed_hdr_andstream(&fixed_hdr, L1_PARTIAL_SEQ_NUM,
2699 circuit->snd_stream);
2700 else
2701 fill_fixed_hdr_andstream(&fixed_hdr, L2_PARTIAL_SEQ_NUM,
2702 circuit->snd_stream);
2703
2704 /*
2705 * Fill Level 1 or 2 Partial Sequence Numbers header
2706 */
2707 lenp = stream_get_endp(circuit->snd_stream);
2708 stream_putw(circuit->snd_stream, 0); /* PDU length - when we know it */
2709 stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
2710 stream_putc(circuit->snd_stream, circuit->idx);
2711
2712 /*
2713 * And TLVs
2714 */
2715
2716 if (level == IS_LEVEL_1)
2717 passwd = &circuit->area->area_passwd;
2718 else
2719 passwd = &circuit->area->domain_passwd;
2720
2721 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) {
2722 switch (passwd->type) {
2723 /* Cleartext */
2724 case ISIS_PASSWD_TYPE_CLEARTXT:
2725 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_CLEARTXT,
2726 passwd->len, passwd->passwd,
2727 circuit->snd_stream))
2728 return ISIS_WARNING;
2729 break;
2730
2731 /* HMAC MD5 */
2732 case ISIS_PASSWD_TYPE_HMAC_MD5:
2733 /* Remember where TLV is written so we can later
2734 * overwrite the MD5 hash */
2735 auth_tlv_offset = stream_get_endp(circuit->snd_stream);
2736 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2737 if (tlv_add_authinfo(ISIS_PASSWD_TYPE_HMAC_MD5,
2738 ISIS_AUTH_MD5_SIZE, hmac_md5_hash,
2739 circuit->snd_stream))
2740 return ISIS_WARNING;
2741 break;
2742
2743 default:
2744 break;
2745 }
2746 }
eb5d44eb 2747
d62a17ae 2748 retval = tlv_add_lsp_entries(lsps, circuit->snd_stream);
2749 if (retval != ISIS_OK)
2750 return retval;
2751
2752 if (isis->debugs & DEBUG_SNP_PACKETS) {
2753 for (ALL_LIST_ELEMENTS_RO(lsps, node, lsp)) {
2754 zlog_debug(
2755 "ISIS-Snp (%s): PSNP entry %s, seq 0x%08x,"
2756 " cksum 0x%04x, lifetime %us",
2757 circuit->area->area_tag,
2758 rawlspid_print(lsp->lsp_header->lsp_id),
2759 ntohl(lsp->lsp_header->seq_num),
2760 ntohs(lsp->lsp_header->checksum),
2761 ntohs(lsp->lsp_header->rem_lifetime));
2762 }
2763 }
eb5d44eb 2764
d62a17ae 2765 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
2766 /* Update PDU length */
2767 stream_putw_at(circuit->snd_stream, lenp, length);
2768
2769 /* For HMAC MD5 we need to compute the md5 hash and store it */
2770 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)
2771 && passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) {
2772 hmac_md5(STREAM_DATA(circuit->snd_stream),
2773 stream_get_endp(circuit->snd_stream),
2774 (unsigned char *)&passwd->passwd, passwd->len,
2775 (unsigned char *)&hmac_md5_hash);
2776 /* Copy the hash into the stream */
2777 memcpy(STREAM_DATA(circuit->snd_stream) + auth_tlv_offset + 3,
2778 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2779 }
3f045a08 2780
d62a17ae 2781 return ISIS_OK;
eb5d44eb 2782}
2783
2784/*
2785 * 7.3.15.4 action on expiration of partial SNP interval
2786 * level 1
2787 */
d62a17ae 2788static int send_psnp(int level, struct isis_circuit *circuit)
eb5d44eb 2789{
d62a17ae 2790 struct isis_lsp *lsp;
2791 struct list *list = NULL;
2792 struct listnode *node;
2793 u_char num_lsps;
2794 int retval = ISIS_OK;
eb5d44eb 2795
d62a17ae 2796 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2797 && circuit->u.bc.is_dr[level - 1])
2798 return ISIS_OK;
eb5d44eb 2799
d62a17ae 2800 if (circuit->area->lspdb[level - 1] == NULL
2801 || dict_count(circuit->area->lspdb[level - 1]) == 0)
2802 return ISIS_OK;
f390d2c7 2803
d62a17ae 2804 if (!circuit->snd_stream)
2805 return ISIS_ERROR;
e38e0df0 2806
d62a17ae 2807 num_lsps = max_lsps_per_snp(ISIS_SNP_PSNP_FLAG, level, circuit);
f390d2c7 2808
d62a17ae 2809 while (1) {
2810 list = list_new();
2811 lsp_build_list_ssn(circuit, num_lsps, list,
2812 circuit->area->lspdb[level - 1]);
eb5d44eb 2813
d62a17ae 2814 if (listcount(list) == 0) {
2815 list_delete(list);
2816 return ISIS_OK;
2817 }
2818
2819 retval = build_psnp(level, circuit, list);
2820 if (retval != ISIS_OK) {
2821 zlog_err("ISIS-Snp (%s): Build L%d PSNP on %s failed",
2822 circuit->area->area_tag, level,
2823 circuit->interface->name);
2824 list_delete(list);
2825 return retval;
2826 }
2827
2828 if (isis->debugs & DEBUG_SNP_PACKETS) {
2829 zlog_debug(
2830 "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
2831 circuit->area->area_tag, level,
2832 circuit->interface->name,
2833 stream_get_endp(circuit->snd_stream));
2834 if (isis->debugs & DEBUG_PACKET_DUMP)
2835 zlog_dump_data(
2836 STREAM_DATA(circuit->snd_stream),
2837 stream_get_endp(circuit->snd_stream));
2838 }
2839
2840 retval = circuit->tx(circuit, level);
2841 if (retval != ISIS_OK) {
2842 zlog_err("ISIS-Snp (%s): Send L%d PSNP on %s failed",
2843 circuit->area->area_tag, level,
2844 circuit->interface->name);
2845 list_delete(list);
2846 return retval;
2847 }
2848
2849 /*
2850 * sending succeeded, we can clear SSN flags of this circuit
2851 * for the LSPs in list
2852 */
2853 for (ALL_LIST_ELEMENTS_RO(list, node, lsp))
2854 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
2855 list_delete(list);
2856 }
2857
2858 return retval;
eb5d44eb 2859}
2860
d62a17ae 2861int send_l1_psnp(struct thread *thread)
eb5d44eb 2862{
2863
d62a17ae 2864 struct isis_circuit *circuit;
2865 int retval = ISIS_OK;
eb5d44eb 2866
d62a17ae 2867 circuit = THREAD_ARG(thread);
2868 assert(circuit);
eb5d44eb 2869
d62a17ae 2870 circuit->t_send_psnp[0] = NULL;
eb5d44eb 2871
d62a17ae 2872 send_psnp(1, circuit);
2873 /* set next timer thread */
2874 thread_add_timer(master, send_l1_psnp, circuit,
2875 isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
2876 &circuit->t_send_psnp[0]);
eb5d44eb 2877
d62a17ae 2878 return retval;
eb5d44eb 2879}
2880
2881/*
2882 * 7.3.15.4 action on expiration of partial SNP interval
2883 * level 2
2884 */
d62a17ae 2885int send_l2_psnp(struct thread *thread)
eb5d44eb 2886{
d62a17ae 2887 struct isis_circuit *circuit;
2888 int retval = ISIS_OK;
eb5d44eb 2889
d62a17ae 2890 circuit = THREAD_ARG(thread);
2891 assert(circuit);
eb5d44eb 2892
d62a17ae 2893 circuit->t_send_psnp[1] = NULL;
eb5d44eb 2894
d62a17ae 2895 send_psnp(2, circuit);
eb5d44eb 2896
d62a17ae 2897 /* set next timer thread */
2898 thread_add_timer(master, send_l2_psnp, circuit,
2899 isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
2900 &circuit->t_send_psnp[1]);
eb5d44eb 2901
d62a17ae 2902 return retval;
eb5d44eb 2903}
2904
eb5d44eb 2905/*
2906 * ISO 10589 - 7.3.14.3
2907 */
d62a17ae 2908int send_lsp(struct thread *thread)
eb5d44eb 2909{
d62a17ae 2910 struct isis_circuit *circuit;
2911 struct isis_lsp *lsp;
2912 struct listnode *node;
2913 int clear_srm = 1;
2914 int retval = ISIS_OK;
2915
2916 circuit = THREAD_ARG(thread);
2917 assert(circuit);
2918
2919 if (!circuit->lsp_queue)
2920 return ISIS_OK;
2921
2922 node = listhead(circuit->lsp_queue);
2923
2924 /*
2925 * Handle case where there are no LSPs on the queue. This can
2926 * happen, for instance, if an adjacency goes down before this
2927 * thread gets a chance to run.
2928 */
2929 if (!node)
2930 return ISIS_OK;
2931
2932 /*
2933 * Delete LSP from lsp_queue. If it's still in queue, it is assumed
2934 * as 'transmit pending', but send_lsp may never be called again.
2935 * Retry will happen because SRM flag will not be cleared.
2936 */
2937 lsp = listgetdata(node);
2938 list_delete_node(circuit->lsp_queue, node);
2939
2940 /* Set the last-cleared time if the queue is empty. */
2941 /* TODO: Is is possible that new lsps keep being added to the queue
2942 * that the queue is never empty? */
2943 if (list_isempty(circuit->lsp_queue))
2944 circuit->lsp_queue_last_cleared = time(NULL);
2945
2946 if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
2947 goto out;
2948
2949 /*
2950 * Do not send if levels do not match
2951 */
2952 if (!(lsp->level & circuit->is_type))
2953 goto out;
2954
2955 /*
2956 * Do not send if we do not have adjacencies in state up on the circuit
2957 */
2958 if (circuit->upadjcount[lsp->level - 1] == 0)
2959 goto out;
2960
2961 /* stream_copy will assert and stop program execution if LSP is larger
2962 * than
2963 * the circuit's MTU. So handle and log this case here. */
2964 if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) {
2965 zlog_err(
2966 "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x,"
2967 " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu"
2968 " while interface stream size is %zu.",
2969 circuit->area->area_tag, lsp->level,
2970 rawlspid_print(lsp->lsp_header->lsp_id),
2971 ntohl(lsp->lsp_header->seq_num),
2972 ntohs(lsp->lsp_header->checksum),
2973 ntohs(lsp->lsp_header->rem_lifetime),
2974 circuit->interface->name, stream_get_endp(lsp->pdu),
2975 stream_get_size(circuit->snd_stream));
2976 if (isis->debugs & DEBUG_PACKET_DUMP)
2977 zlog_dump_data(STREAM_DATA(lsp->pdu),
2978 stream_get_endp(lsp->pdu));
2979 retval = ISIS_ERROR;
2980 goto out;
2981 }
eb5d44eb 2982
d62a17ae 2983 /* copy our lsp to the send buffer */
2984 stream_copy(circuit->snd_stream, lsp->pdu);
2985
2986 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
2987 zlog_debug(
2988 "ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
2989 " lifetime %us on %s",
2990 circuit->area->area_tag, lsp->level,
2991 rawlspid_print(lsp->lsp_header->lsp_id),
2992 ntohl(lsp->lsp_header->seq_num),
2993 ntohs(lsp->lsp_header->checksum),
2994 ntohs(lsp->lsp_header->rem_lifetime),
2995 circuit->interface->name);
2996 if (isis->debugs & DEBUG_PACKET_DUMP)
2997 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2998 stream_get_endp(circuit->snd_stream));
2999 }
3f045a08 3000
d62a17ae 3001 clear_srm = 0;
3002 retval = circuit->tx(circuit, lsp->level);
3003 if (retval != ISIS_OK) {
3004 zlog_err("ISIS-Upd (%s): Send L%d LSP on %s failed %s",
3005 circuit->area->area_tag, lsp->level,
3006 circuit->interface->name,
3007 (retval == ISIS_WARNING) ? "temporarily"
3008 : "permanently");
3009 }
f390d2c7 3010
cfd1f27b 3011out:
d62a17ae 3012 if (clear_srm
3013 || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST)
3014 || (retval != ISIS_OK && retval != ISIS_WARNING)) {
3015 /* SRM flag will trigger retransmission. We will not retransmit
3016 * if we
3017 * encountered a fatal error.
3018 * On success, they should only be cleared if it's a broadcast
3019 * circuit.
3020 * On a P2P circuit, we will wait for the ack from the neighbor
3021 * to clear
3022 * the fag.
3023 */
3024 ISIS_CLEAR_FLAG(lsp->SRMflags, circuit);
3025 }
eb5d44eb 3026
d62a17ae 3027 return retval;
f390d2c7 3028}
eb5d44eb 3029
d62a17ae 3030int ack_lsp(struct isis_link_state_hdr *hdr, struct isis_circuit *circuit,
3031 int level)
eb5d44eb 3032{
d62a17ae 3033 unsigned long lenp;
3034 int retval;
3035 u_int16_t length;
3036 struct isis_fixed_hdr fixed_hdr;
3037
3038 isis_circuit_stream(circuit, &circuit->snd_stream);
3039
3040 // fill_llc_hdr (stream);
3041 if (level == IS_LEVEL_1)
3042 fill_fixed_hdr_andstream(&fixed_hdr, L1_PARTIAL_SEQ_NUM,
3043 circuit->snd_stream);
3044 else
3045 fill_fixed_hdr_andstream(&fixed_hdr, L2_PARTIAL_SEQ_NUM,
3046 circuit->snd_stream);
3047
3048
3049 lenp = stream_get_endp(circuit->snd_stream);
3050 stream_putw(circuit->snd_stream, 0); /* PDU length */
3051 stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
3052 stream_putc(circuit->snd_stream, circuit->idx);
3053 stream_putc(circuit->snd_stream, 9); /* code */
3054 stream_putc(circuit->snd_stream, 16); /* len */
3055
3056 stream_putw(circuit->snd_stream, ntohs(hdr->rem_lifetime));
3057 stream_put(circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2);
3058 stream_putl(circuit->snd_stream, ntohl(hdr->seq_num));
3059 stream_putw(circuit->snd_stream, ntohs(hdr->checksum));
3060
3061 length = (u_int16_t)stream_get_endp(circuit->snd_stream);
3062 /* Update PDU length */
3063 stream_putw_at(circuit->snd_stream, lenp, length);
3064
3065 retval = circuit->tx(circuit, level);
3066 if (retval != ISIS_OK)
3067 zlog_err("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
3068 circuit->area->area_tag, level,
3069 circuit->interface->name);
3070
3071 return retval;
eb5d44eb 3072}