]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_pdu.c
*: make consistent & update GPLv2 file headers
[mirror_frr.git] / isisd / isis_pdu.c
CommitLineData
eb5d44eb 1/*
2 * IS-IS Rout(e)ing protocol - isis_pdu.c
3 * PDU processing
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public Licenseas published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
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 */
f390d2c7 72static int
eb5d44eb 73area_match (struct list *left, struct list *right)
74{
75 struct area_addr *addr1, *addr2;
3fdb2dd9 76 struct listnode *node1, *node2;
eb5d44eb 77
3fdb2dd9 78 for (ALL_LIST_ELEMENTS_RO (left, node1, addr1))
f390d2c7 79 {
3fdb2dd9 80 for (ALL_LIST_ELEMENTS_RO (right, node2, addr2))
f390d2c7 81 {
82 if (addr1->addr_len == addr2->addr_len &&
83 !memcmp (addr1->area_addr, addr2->area_addr, (int) addr1->addr_len))
84 return 1; /* match */
eb5d44eb 85 }
86 }
87
f390d2c7 88 return 0; /* mismatch */
eb5d44eb 89}
90
eb5d44eb 91/*
92 * Checks whether we should accept a PDU of given level
93 */
94static int
95accept_level (int level, int circuit_t)
96{
f390d2c7 97 int retval = ((circuit_t & level) == level); /* simple approach */
eb5d44eb 98
99 return retval;
100}
101
3f045a08
JB
102/*
103 * Verify authentication information
104 * Support cleartext and HMAC MD5 authentication
105 */
106static int
107authentication_check (struct isis_passwd *remote, struct isis_passwd *local,
108 struct stream *stream, uint32_t auth_tlv_offset)
eb5d44eb 109{
3f045a08
JB
110 unsigned char digest[ISIS_AUTH_MD5_SIZE];
111
112 /* Auth fail () - passwd type mismatch */
113 if (local->type != remote->type)
114 return ISIS_ERROR;
115
116 switch (local->type)
117 {
118 /* No authentication required */
119 case ISIS_PASSWD_TYPE_UNUSED:
120 break;
121
122 /* Cleartext (ISO 10589) */
123 case ISIS_PASSWD_TYPE_CLEARTXT:
124 /* Auth fail () - passwd len mismatch */
125 if (remote->len != local->len)
126 return ISIS_ERROR;
127 return memcmp (local->passwd, remote->passwd, local->len);
128
129 /* HMAC MD5 (RFC 3567) */
130 case ISIS_PASSWD_TYPE_HMAC_MD5:
131 /* Auth fail () - passwd len mismatch */
132 if (remote->len != ISIS_AUTH_MD5_SIZE)
133 return ISIS_ERROR;
134 /* Set the authentication value to 0 before the check */
135 memset (STREAM_DATA (stream) + auth_tlv_offset + 3, 0,
136 ISIS_AUTH_MD5_SIZE);
137 /* Compute the digest */
138 hmac_md5 (STREAM_DATA (stream), stream_get_endp (stream),
139 (unsigned char *) &(local->passwd), local->len,
f2bce9a5 140 (unsigned char *) &digest);
3f045a08
JB
141 /* Copy back the authentication value after the check */
142 memcpy (STREAM_DATA (stream) + auth_tlv_offset + 3,
143 remote->passwd, ISIS_AUTH_MD5_SIZE);
144 return memcmp (digest, remote->passwd, ISIS_AUTH_MD5_SIZE);
145
146 default:
147 zlog_err ("Unsupported authentication type");
148 return ISIS_ERROR;
149 }
150
151 /* Authentication pass when no authentication is configured */
152 return ISIS_OK;
153}
154
155static int
156lsp_authentication_check (struct stream *stream, struct isis_area *area,
157 int level, struct isis_passwd *passwd)
158{
159 struct isis_link_state_hdr *hdr;
160 uint32_t expected = 0, found = 0, auth_tlv_offset = 0;
e38e0df0 161 uint16_t checksum, rem_lifetime, pdu_len;
3f045a08
JB
162 struct tlvs tlvs;
163 int retval = ISIS_OK;
164
165 hdr = (struct isis_link_state_hdr *) (STREAM_PNT (stream));
e38e0df0 166 pdu_len = ntohs (hdr->pdu_len);
3f045a08
JB
167 expected |= TLVFLAG_AUTH_INFO;
168 auth_tlv_offset = stream_get_getp (stream) + ISIS_LSP_HDR_LEN;
169 retval = parse_tlvs (area->area_tag, STREAM_PNT (stream) + ISIS_LSP_HDR_LEN,
e38e0df0 170 pdu_len - ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN,
3f045a08
JB
171 &expected, &found, &tlvs, &auth_tlv_offset);
172
173 if (retval != ISIS_OK)
174 {
175 zlog_err ("ISIS-Upd (%s): Parse failed L%d LSP %s, seq 0x%08x, "
176 "cksum 0x%04x, lifetime %us, len %u",
177 area->area_tag, level, rawlspid_print (hdr->lsp_id),
178 ntohl (hdr->seq_num), ntohs (hdr->checksum),
e38e0df0 179 ntohs (hdr->rem_lifetime), pdu_len);
3f045a08
JB
180 if ((isis->debugs & DEBUG_UPDATE_PACKETS) &&
181 (isis->debugs & DEBUG_PACKET_DUMP))
182 zlog_dump_data (STREAM_DATA (stream), stream_get_endp (stream));
183 return retval;
184 }
185
186 if (!(found & TLVFLAG_AUTH_INFO))
f390d2c7 187 {
3f045a08
JB
188 zlog_err ("No authentication tlv in LSP");
189 return ISIS_ERROR;
f390d2c7 190 }
3f045a08
JB
191
192 if (tlvs.auth_info.type != ISIS_PASSWD_TYPE_CLEARTXT &&
193 tlvs.auth_info.type != ISIS_PASSWD_TYPE_HMAC_MD5)
f390d2c7 194 {
3f045a08
JB
195 zlog_err ("Unknown authentication type in LSP");
196 return ISIS_ERROR;
f390d2c7 197 }
3f045a08
JB
198
199 /*
200 * RFC 5304 set checksum and remaining lifetime to zero before
201 * verification and reset to old values after verification.
202 */
203 checksum = hdr->checksum;
204 rem_lifetime = hdr->rem_lifetime;
205 hdr->checksum = 0;
206 hdr->rem_lifetime = 0;
207 retval = authentication_check (&tlvs.auth_info, passwd, stream,
208 auth_tlv_offset);
209 hdr->checksum = checksum;
210 hdr->rem_lifetime = rem_lifetime;
211
212 return retval;
eb5d44eb 213}
214
215/*
216 * Processing helper functions
217 */
3f045a08
JB
218static void
219del_addr (void *val)
220{
221 XFREE (MTYPE_ISIS_TMP, val);
222}
223
224static void
225tlvs_to_adj_area_addrs (struct tlvs *tlvs, struct isis_adjacency *adj)
226{
227 struct listnode *node;
228 struct area_addr *area_addr, *malloced;
229
230 if (adj->area_addrs)
231 {
232 adj->area_addrs->del = del_addr;
233 list_delete (adj->area_addrs);
234 }
235 adj->area_addrs = list_new ();
236 if (tlvs->area_addrs)
237 {
238 for (ALL_LIST_ELEMENTS_RO (tlvs->area_addrs, node, area_addr))
239 {
240 malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct area_addr));
241 memcpy (malloced, area_addr, sizeof (struct area_addr));
242 listnode_add (adj->area_addrs, malloced);
243 }
244 }
245}
246
655071f4 247static int
f390d2c7 248tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adjacency *adj)
eb5d44eb 249{
250 int i;
251 struct nlpids *tlv_nlpids;
252
f390d2c7 253 if (tlvs->nlpids)
254 {
eb5d44eb 255
f390d2c7 256 tlv_nlpids = tlvs->nlpids;
655071f4
DL
257 if (tlv_nlpids->count > array_size (adj->nlpids.nlpids))
258 return 1;
eb5d44eb 259
f390d2c7 260 adj->nlpids.count = tlv_nlpids->count;
eb5d44eb 261
f390d2c7 262 for (i = 0; i < tlv_nlpids->count; i++)
263 {
264 adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i];
265 }
eb5d44eb 266 }
655071f4 267 return 0;
eb5d44eb 268}
269
92365889 270static void
f390d2c7 271tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis_adjacency *adj)
eb5d44eb 272{
3fdb2dd9 273 struct listnode *node;
eb5d44eb 274 struct in_addr *ipv4_addr, *malloced;
275
f390d2c7 276 if (adj->ipv4_addrs)
277 {
3f045a08 278 adj->ipv4_addrs->del = del_addr;
f390d2c7 279 list_delete (adj->ipv4_addrs);
280 }
eb5d44eb 281 adj->ipv4_addrs = list_new ();
f390d2c7 282 if (tlvs->ipv4_addrs)
283 {
3fdb2dd9 284 for (ALL_LIST_ELEMENTS_RO (tlvs->ipv4_addrs, node, ipv4_addr))
f390d2c7 285 {
286 malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct in_addr));
287 memcpy (malloced, ipv4_addr, sizeof (struct in_addr));
288 listnode_add (adj->ipv4_addrs, malloced);
289 }
eb5d44eb 290 }
eb5d44eb 291}
292
92365889 293static void
f390d2c7 294tlvs_to_adj_ipv6_addrs (struct tlvs *tlvs, struct isis_adjacency *adj)
eb5d44eb 295{
3fdb2dd9 296 struct listnode *node;
eb5d44eb 297 struct in6_addr *ipv6_addr, *malloced;
298
f390d2c7 299 if (adj->ipv6_addrs)
300 {
3f045a08 301 adj->ipv6_addrs->del = del_addr;
f390d2c7 302 list_delete (adj->ipv6_addrs);
303 }
eb5d44eb 304 adj->ipv6_addrs = list_new ();
f390d2c7 305 if (tlvs->ipv6_addrs)
306 {
3fdb2dd9 307 for (ALL_LIST_ELEMENTS_RO (tlvs->ipv6_addrs, node, ipv6_addr))
f390d2c7 308 {
309 malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct in6_addr));
310 memcpy (malloced, ipv6_addr, sizeof (struct in6_addr));
311 listnode_add (adj->ipv6_addrs, malloced);
312 }
eb5d44eb 313 }
eb5d44eb 314
315}
eb5d44eb 316
eb5d44eb 317/*
318 * RECEIVE SIDE
319 */
320
321/*
322 * Process P2P IIH
323 * ISO - 10589
324 * Section 8.2.5 - Receiving point-to-point IIH PDUs
325 *
326 */
327static int
328process_p2p_hello (struct isis_circuit *circuit)
329{
330 int retval = ISIS_OK;
331 struct isis_p2p_hello_hdr *hdr;
332 struct isis_adjacency *adj;
3f045a08 333 u_int32_t expected = 0, found = 0, auth_tlv_offset = 0;
e38e0df0 334 uint16_t pdu_len;
eb5d44eb 335 struct tlvs tlvs;
28a8cfcb 336 int v4_usable = 0, v6_usable = 0;
eb5d44eb 337
3f045a08
JB
338 if (isis->debugs & DEBUG_ADJ_PACKETS)
339 {
340 zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH on %s, cirType %s, cirID %u",
341 circuit->area->area_tag, circuit->interface->name,
342 circuit_t2string (circuit->is_type), circuit->circuit_id);
343 if (isis->debugs & DEBUG_PACKET_DUMP)
344 zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
345 stream_get_endp (circuit->rcv_stream));
346 }
347
348 if (circuit->circ_type != CIRCUIT_T_P2P)
349 {
350 zlog_warn ("p2p hello on non p2p circuit");
351 return ISIS_WARNING;
352 }
353
f390d2c7 354 if ((stream_get_endp (circuit->rcv_stream) -
355 stream_get_getp (circuit->rcv_stream)) < ISIS_P2PHELLO_HDRLEN)
356 {
357 zlog_warn ("Packet too short");
358 return ISIS_WARNING;
359 }
eb5d44eb 360
361 /* 8.2.5.1 PDU acceptance tests */
362
363 /* 8.2.5.1 a) external domain untrue */
364 /* FIXME: not useful at all? */
365
366 /* 8.2.5.1 b) ID Length mismatch */
367 /* checked at the handle_pdu */
368
369 /* 8.2.5.2 IIH PDU Processing */
370
371 /* 8.2.5.2 a) 1) Maximum Area Addresses */
372 /* Already checked, and can also be ommited */
373
374 /*
375 * Get the header
376 */
f390d2c7 377 hdr = (struct isis_p2p_hello_hdr *) STREAM_PNT (circuit->rcv_stream);
e38e0df0 378 pdu_len = ntohs (hdr->pdu_len);
eb5d44eb 379
a22ab5a5
AS
380 if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_P2PHELLO_HDRLEN) ||
381 pdu_len > ISO_MTU(circuit) ||
e38e0df0 382 pdu_len > stream_get_endp (circuit->rcv_stream))
f390d2c7 383 {
3f045a08
JB
384 zlog_warn ("ISIS-Adj (%s): Rcvd P2P IIH from (%s) with "
385 "invalid pdu length %d",
e38e0df0 386 circuit->area->area_tag, circuit->interface->name, pdu_len);
3f045a08 387 return ISIS_WARNING;
f390d2c7 388 }
eb5d44eb 389
e38e0df0
SV
390 /*
391 * Set the stream endp to PDU length, ignoring additional padding
392 * introduced by transport chips.
393 */
394 if (pdu_len < stream_get_endp (circuit->rcv_stream))
395 stream_set_endp (circuit->rcv_stream, pdu_len);
396
397 stream_forward_getp (circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN);
398
eb5d44eb 399 /*
400 * Lets get the TLVS now
401 */
402 expected |= TLVFLAG_AREA_ADDRS;
403 expected |= TLVFLAG_AUTH_INFO;
404 expected |= TLVFLAG_NLPID;
405 expected |= TLVFLAG_IPV4_ADDR;
406 expected |= TLVFLAG_IPV6_ADDR;
99894f9a 407 expected |= TLVFLAG_MT_ROUTER_INFORMATION;
eb5d44eb 408
3f045a08 409 auth_tlv_offset = stream_get_getp (circuit->rcv_stream);
eb5d44eb 410 retval = parse_tlvs (circuit->area->area_tag,
411 STREAM_PNT (circuit->rcv_stream),
e38e0df0
SV
412 pdu_len - ISIS_P2PHELLO_HDRLEN - ISIS_FIXED_HDR_LEN,
413 &expected, &found, &tlvs, &auth_tlv_offset);
f390d2c7 414
415 if (retval > ISIS_WARNING)
416 {
3f045a08 417 zlog_warn ("parse_tlvs() failed");
f390d2c7 418 free_tlvs (&tlvs);
419 return retval;
420 };
eb5d44eb 421
3f045a08
JB
422 if (!(found & TLVFLAG_AREA_ADDRS))
423 {
424 zlog_warn ("No Area addresses TLV in P2P IS to IS hello");
425 free_tlvs (&tlvs);
426 return ISIS_WARNING;
427 }
428
b72f345d
DL
429 if (!(found & TLVFLAG_NLPID))
430 {
431 zlog_warn ("No supported protocols TLV in P2P IS to IS hello");
432 free_tlvs (&tlvs);
433 return ISIS_WARNING;
434 }
435
eb5d44eb 436 /* 8.2.5.1 c) Authentication */
f390d2c7 437 if (circuit->passwd.type)
438 {
439 if (!(found & TLVFLAG_AUTH_INFO) ||
3f045a08
JB
440 authentication_check (&tlvs.auth_info, &circuit->passwd,
441 circuit->rcv_stream, auth_tlv_offset))
442 {
443 isis_event_auth_failure (circuit->area->area_tag,
444 "P2P hello authentication failure",
445 hdr->source_id);
446 free_tlvs (&tlvs);
447 return ISIS_OK;
448 }
eb5d44eb 449 }
eb5d44eb 450
3f045a08 451 /*
481f1484 452 * check if both ends have an IPv4 address
3f045a08 453 */
481f1484
CF
454 if (circuit->ip_addrs && listcount(circuit->ip_addrs)
455 && tlvs.ipv4_addrs && listcount(tlvs.ipv4_addrs))
28a8cfcb 456 {
481f1484 457 v4_usable = 1;
28a8cfcb 458 }
481f1484 459
28a8cfcb
DL
460 if (found & TLVFLAG_IPV6_ADDR)
461 {
462 /* TBA: check that we have a linklocal ourselves? */
463 struct listnode *node;
ad2f92b6 464 struct in6_addr *ip;
28a8cfcb
DL
465 for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip))
466 if (IN6_IS_ADDR_LINKLOCAL (ip))
467 {
468 v6_usable = 1;
469 break;
470 }
471
472 if (!v6_usable)
473 zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local "
474 "in P2P IIH from %s\n", circuit->interface->name);
475 }
476
477 if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR)))
478 zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in P2P IIH from %s\n",
479 circuit->interface->name);
28a8cfcb
DL
480
481 if (!v6_usable && !v4_usable)
3f045a08 482 {
3f045a08
JB
483 free_tlvs (&tlvs);
484 return ISIS_WARNING;
485 }
486
0908a2fd
AN
487 /*
488 * it's own p2p IIH PDU - discard
489 */
490 if (!memcmp (hdr->source_id, isis->sysid, ISIS_SYS_ID_LEN))
491 {
492 zlog_warn ("ISIS-Adj (%s): it's own IIH PDU - discarded",
493 circuit->area->area_tag);
494 free_tlvs (&tlvs);
495 return ISIS_WARNING;
496 }
497
3f045a08
JB
498 /*
499 * My interpertation of the ISO, if no adj exists we will create one for
500 * the circuit
501 */
502 adj = circuit->u.p2p.neighbor;
793ec473
AN
503 /* If an adjacency exists, check it is with the source of the hello
504 * packets */
505 if (adj)
506 {
507 if (memcmp(hdr->source_id, adj->sysid, ISIS_SYS_ID_LEN))
508 {
509 zlog_debug("hello source and adjacency do not match, set adj down\n");
510 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "adj do not exist");
511 return 0;
512 }
513 }
3f045a08
JB
514 if (!adj || adj->level != hdr->circuit_t)
515 {
516 if (!adj)
517 {
518 adj = isis_new_adj (hdr->source_id, NULL, hdr->circuit_t, circuit);
519 if (adj == NULL)
520 return ISIS_ERROR;
521 }
522 else
523 {
524 adj->level = hdr->circuit_t;
525 }
526 circuit->u.p2p.neighbor = adj;
a2d2c0d2
AN
527 /* Build lsp with the new neighbor entry when a new
528 * adjacency is formed. Set adjacency circuit type to
529 * IIH PDU header circuit type before lsp is regenerated
530 * when an adjacency is up. This will result in the new
531 * adjacency entry getting added to the lsp tlv neighbor list.
532 */
533 adj->circuit_t = hdr->circuit_t;
3f045a08
JB
534 isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL);
535 adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
536 }
537
538 /* 8.2.6 Monitoring point-to-point adjacencies */
539 adj->hold_time = ntohs (hdr->hold_time);
540 adj->last_upd = time (NULL);
541
eb5d44eb 542 /* we do this now because the adj may not survive till the end... */
3f045a08
JB
543 tlvs_to_adj_area_addrs (&tlvs, adj);
544
545 /* which protocol are spoken ??? */
b72f345d
DL
546 if (tlvs_to_adj_nlpids (&tlvs, adj))
547 {
548 free_tlvs (&tlvs);
549 return ISIS_WARNING;
550 }
eb5d44eb 551
552 /* we need to copy addresses to the adj */
3f045a08
JB
553 if (found & TLVFLAG_IPV4_ADDR)
554 tlvs_to_adj_ipv4_addrs (&tlvs, adj);
eb5d44eb 555
f8c06e2c
OD
556 /* Update MPLS TE Remote IP address parameter if possible */
557 if (IS_MPLS_TE(isisMplsTE) && circuit->mtc && IS_CIRCUIT_TE(circuit->mtc))
558 if (adj->ipv4_addrs != NULL && listcount(adj->ipv4_addrs) != 0)
559 {
560 struct in_addr *ip_addr;
561 ip_addr = (struct in_addr *)listgetdata ((struct listnode *)listhead (adj->ipv4_addrs));
562 set_circuitparams_rmt_ipaddr (circuit->mtc, *ip_addr);
563 }
564
3f045a08
JB
565 if (found & TLVFLAG_IPV6_ADDR)
566 tlvs_to_adj_ipv6_addrs (&tlvs, adj);
eb5d44eb 567
d8fba7d9
CF
568 bool mt_set_changed = tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj);
569
eb5d44eb 570 /* lets take care of the expiry */
f390d2c7 571 THREAD_TIMER_OFF (adj->t_expire);
ffa2c898
QY
572 thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time,
573 &adj->t_expire);
eb5d44eb 574
575 /* 8.2.5.2 a) a match was detected */
f390d2c7 576 if (area_match (circuit->area->area_addrs, tlvs.area_addrs))
577 {
578 /* 8.2.5.2 a) 2) If the system is L1 - table 5 */
579 if (circuit->area->is_type == IS_LEVEL_1)
580 {
581 switch (hdr->circuit_t)
582 {
583 case IS_LEVEL_1:
584 case IS_LEVEL_1_AND_2:
585 if (adj->adj_state != ISIS_ADJ_UP)
586 {
587 /* (4) adj state up */
588 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
589 /* (5) adj usage level 1 */
590 adj->adj_usage = ISIS_ADJ_LEVEL1;
591 }
592 else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
593 {
594 ; /* accept */
595 }
596 break;
597 case IS_LEVEL_2:
598 if (adj->adj_state != ISIS_ADJ_UP)
599 {
600 /* (7) reject - wrong system type event */
601 zlog_warn ("wrongSystemType");
3f045a08 602 free_tlvs (&tlvs);
f390d2c7 603 return ISIS_WARNING; /* Reject */
604 }
605 else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
606 {
607 /* (6) down - wrong system */
608 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
609 }
610 break;
611 }
612 }
eb5d44eb 613
f390d2c7 614 /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */
615 if (circuit->area->is_type == IS_LEVEL_1_AND_2)
616 {
617 switch (hdr->circuit_t)
618 {
619 case IS_LEVEL_1:
620 if (adj->adj_state != ISIS_ADJ_UP)
621 {
622 /* (6) adj state up */
623 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
624 /* (7) adj usage level 1 */
625 adj->adj_usage = ISIS_ADJ_LEVEL1;
626 }
627 else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
628 {
629 ; /* accept */
630 }
631 else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
632 (adj->adj_usage == ISIS_ADJ_LEVEL2))
633 {
634 /* (8) down - wrong system */
635 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
636 }
637 break;
638 case IS_LEVEL_2:
639 if (adj->adj_state != ISIS_ADJ_UP)
640 {
641 /* (6) adj state up */
642 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
643 /* (9) adj usage level 2 */
644 adj->adj_usage = ISIS_ADJ_LEVEL2;
645 }
646 else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) ||
647 (adj->adj_usage == ISIS_ADJ_LEVEL1AND2))
648 {
649 /* (8) down - wrong system */
650 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
651 }
652 else if (adj->adj_usage == ISIS_ADJ_LEVEL2)
653 {
654 ; /* Accept */
655 }
656 break;
657 case IS_LEVEL_1_AND_2:
658 if (adj->adj_state != ISIS_ADJ_UP)
659 {
660 /* (6) adj state up */
661 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
662 /* (10) adj usage level 1 */
663 adj->adj_usage = ISIS_ADJ_LEVEL1AND2;
664 }
665 else if ((adj->adj_usage == ISIS_ADJ_LEVEL1) ||
666 (adj->adj_usage == ISIS_ADJ_LEVEL2))
667 {
668 /* (8) down - wrong system */
669 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
670 }
671 else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
672 {
673 ; /* Accept */
674 }
675 break;
676 }
677 }
eb5d44eb 678
f390d2c7 679 /* 8.2.5.2 a) 4) If the system is L2 - table 7 */
680 if (circuit->area->is_type == IS_LEVEL_2)
681 {
682 switch (hdr->circuit_t)
683 {
684 case IS_LEVEL_1:
685 if (adj->adj_state != ISIS_ADJ_UP)
686 {
687 /* (5) reject - wrong system type event */
688 zlog_warn ("wrongSystemType");
3f045a08 689 free_tlvs (&tlvs);
f390d2c7 690 return ISIS_WARNING; /* Reject */
691 }
692 else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
693 (adj->adj_usage == ISIS_ADJ_LEVEL2))
694 {
695 /* (6) down - wrong system */
696 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
697 }
698 break;
699 case IS_LEVEL_1_AND_2:
700 case IS_LEVEL_2:
701 if (adj->adj_state != ISIS_ADJ_UP)
702 {
703 /* (7) adj state up */
704 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
705 /* (8) adj usage level 2 */
706 adj->adj_usage = ISIS_ADJ_LEVEL2;
707 }
708 else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
709 {
710 /* (6) down - wrong system */
711 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
712 }
713 else if (adj->adj_usage == ISIS_ADJ_LEVEL2)
714 {
715 ; /* Accept */
716 }
717 break;
718 }
719 }
eb5d44eb 720 }
eb5d44eb 721 /* 8.2.5.2 b) if no match was detected */
3f045a08 722 else if (listcount (circuit->area->area_addrs) > 0)
eb5d44eb 723 {
f390d2c7 724 if (circuit->area->is_type == IS_LEVEL_1)
725 {
726 /* 8.2.5.2 b) 1) is_type L1 and adj is not up */
727 if (adj->adj_state != ISIS_ADJ_UP)
728 {
729 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch");
730 /* 8.2.5.2 b) 2)is_type L1 and adj is up */
731 }
732 else
733 {
734 isis_adj_state_change (adj, ISIS_ADJ_DOWN,
735 "Down - Area Mismatch");
736 }
737 }
738 /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
739 else
740 {
741 switch (hdr->circuit_t)
742 {
743 case IS_LEVEL_1:
744 if (adj->adj_state != ISIS_ADJ_UP)
745 {
746 /* (6) reject - Area Mismatch event */
747 zlog_warn ("AreaMismatch");
3f045a08 748 free_tlvs (&tlvs);
f390d2c7 749 return ISIS_WARNING; /* Reject */
750 }
751 else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
752 {
753 /* (7) down - area mismatch */
754 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch");
755
756 }
757 else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) ||
758 (adj->adj_usage == ISIS_ADJ_LEVEL2))
759 {
760 /* (7) down - wrong system */
761 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
762 }
763 break;
764 case IS_LEVEL_1_AND_2:
765 case IS_LEVEL_2:
766 if (adj->adj_state != ISIS_ADJ_UP)
767 {
768 /* (8) adj state up */
769 isis_adj_state_change (adj, ISIS_ADJ_UP, NULL);
770 /* (9) adj usage level 2 */
771 adj->adj_usage = ISIS_ADJ_LEVEL2;
772 }
773 else if (adj->adj_usage == ISIS_ADJ_LEVEL1)
774 {
775 /* (7) down - wrong system */
776 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Wrong System");
777 }
778 else if (adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
779 {
780 if (hdr->circuit_t == IS_LEVEL_2)
781 {
782 /* (7) down - wrong system */
783 isis_adj_state_change (adj, ISIS_ADJ_DOWN,
784 "Wrong System");
785 }
786 else
787 {
788 /* (7) down - area mismatch */
789 isis_adj_state_change (adj, ISIS_ADJ_DOWN,
790 "Area Mismatch");
791 }
792 }
793 else if (adj->adj_usage == ISIS_ADJ_LEVEL2)
794 {
795 ; /* Accept */
796 }
797 break;
798 }
799 }
eb5d44eb 800 }
3f045a08
JB
801 else
802 {
803 /* down - area mismatch */
804 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch");
805 }
d8fba7d9
CF
806
807 if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed)
808 {
809 lsp_regenerate_schedule(adj->circuit->area,
810 isis_adj_usage2levels(adj->adj_usage), 0);
811 }
812
eb5d44eb 813 /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
814 /* FIXME - Missing parts */
815
eb5d44eb 816 /* some of my own understanding of the ISO, why the heck does
817 * it not say what should I change the system_type to...
818 */
f390d2c7 819 switch (adj->adj_usage)
820 {
eb5d44eb 821 case ISIS_ADJ_LEVEL1:
822 adj->sys_type = ISIS_SYSTYPE_L1_IS;
823 break;
824 case ISIS_ADJ_LEVEL2:
825 adj->sys_type = ISIS_SYSTYPE_L2_IS;
826 break;
827 case ISIS_ADJ_LEVEL1AND2:
828 adj->sys_type = ISIS_SYSTYPE_L2_IS;
829 break;
830 case ISIS_ADJ_NONE:
831 adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
832 break;
f390d2c7 833 }
eb5d44eb 834
3f045a08
JB
835
836 if (isis->debugs & DEBUG_ADJ_PACKETS)
837 {
838 zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s,"
839 " cir id %02d, length %d",
840 circuit->area->area_tag, circuit->interface->name,
841 circuit_t2string (circuit->is_type),
e38e0df0 842 circuit->circuit_id, pdu_len);
3f045a08 843 }
eb5d44eb 844
845 free_tlvs (&tlvs);
846
847 return retval;
848}
849
eb5d44eb 850/*
851 * Process IS-IS LAN Level 1/2 Hello PDU
852 */
f390d2c7 853static int
02e33d3e 854process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa)
eb5d44eb 855{
856 int retval = ISIS_OK;
857 struct isis_lan_hello_hdr hdr;
858 struct isis_adjacency *adj;
3f045a08 859 u_int32_t expected = 0, found = 0, auth_tlv_offset = 0;
eb5d44eb 860 struct tlvs tlvs;
861 u_char *snpa;
3fdb2dd9 862 struct listnode *node;
28a8cfcb 863 int v4_usable = 0, v6_usable = 0;
eb5d44eb 864
3f045a08
JB
865 if (isis->debugs & DEBUG_ADJ_PACKETS)
866 {
867 zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, "
868 "cirID %u",
869 circuit->area->area_tag, level, circuit->interface->name,
870 circuit_t2string (circuit->is_type), circuit->circuit_id);
871 if (isis->debugs & DEBUG_PACKET_DUMP)
872 zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
873 stream_get_endp (circuit->rcv_stream));
874 }
875
876 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
877 {
878 zlog_warn ("lan hello on non broadcast circuit");
879 return ISIS_WARNING;
880 }
881
f390d2c7 882 if ((stream_get_endp (circuit->rcv_stream) -
883 stream_get_getp (circuit->rcv_stream)) < ISIS_LANHELLO_HDRLEN)
884 {
885 zlog_warn ("Packet too short");
886 return ISIS_WARNING;
887 }
eb5d44eb 888
f390d2c7 889 if (circuit->ext_domain)
890 {
529d65b3 891 zlog_debug ("level %d LAN Hello received over circuit with "
892 "externalDomain = true", level);
f390d2c7 893 return ISIS_WARNING;
894 }
eb5d44eb 895
3f045a08 896 if (!accept_level (level, circuit->is_type))
f390d2c7 897 {
898 if (isis->debugs & DEBUG_ADJ_PACKETS)
899 {
529d65b3 900 zlog_debug ("ISIS-Adj (%s): Interface level mismatch, %s",
901 circuit->area->area_tag, circuit->interface->name);
f390d2c7 902 }
903 return ISIS_WARNING;
eb5d44eb 904 }
eb5d44eb 905
906#if 0
907 /* Cisco's debug message compatability */
f390d2c7 908 if (!accept_level (level, circuit->area->is_type))
909 {
910 if (isis->debugs & DEBUG_ADJ_PACKETS)
911 {
529d65b3 912 zlog_debug ("ISIS-Adj (%s): is type mismatch",
913 circuit->area->area_tag);
f390d2c7 914 }
915 return ISIS_WARNING;
eb5d44eb 916 }
eb5d44eb 917#endif
918 /*
919 * Fill the header
920 */
921 hdr.circuit_t = stream_getc (circuit->rcv_stream);
922 stream_get (hdr.source_id, circuit->rcv_stream, ISIS_SYS_ID_LEN);
923 hdr.hold_time = stream_getw (circuit->rcv_stream);
f390d2c7 924 hdr.pdu_len = stream_getw (circuit->rcv_stream);
925 hdr.prio = stream_getc (circuit->rcv_stream);
eb5d44eb 926 stream_get (hdr.lan_id, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1);
927
a22ab5a5
AS
928 if (hdr.pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LANHELLO_HDRLEN) ||
929 hdr.pdu_len > ISO_MTU(circuit) ||
e38e0df0 930 hdr.pdu_len > stream_get_endp (circuit->rcv_stream))
f390d2c7 931 {
3f045a08
JB
932 zlog_warn ("ISIS-Adj (%s): Rcvd LAN IIH from (%s) with "
933 "invalid pdu length %d",
934 circuit->area->area_tag, circuit->interface->name,
935 hdr.pdu_len);
e38e0df0 936 return ISIS_WARNING;
3f045a08
JB
937 }
938
e38e0df0
SV
939 /*
940 * Set the stream endp to PDU length, ignoring additional padding
941 * introduced by transport chips.
942 */
943 if (hdr.pdu_len < stream_get_endp (circuit->rcv_stream))
944 stream_set_endp (circuit->rcv_stream, hdr.pdu_len);
945
3f045a08
JB
946 if (hdr.circuit_t != IS_LEVEL_1 &&
947 hdr.circuit_t != IS_LEVEL_2 &&
948 hdr.circuit_t != IS_LEVEL_1_AND_2 &&
949 (level & hdr.circuit_t) == 0)
950 {
951 zlog_err ("Level %d LAN Hello with Circuit Type %d", level,
952 hdr.circuit_t);
f390d2c7 953 return ISIS_ERROR;
954 }
3f045a08 955
eb5d44eb 956 /*
957 * Then get the tlvs
958 */
959 expected |= TLVFLAG_AUTH_INFO;
960 expected |= TLVFLAG_AREA_ADDRS;
961 expected |= TLVFLAG_LAN_NEIGHS;
962 expected |= TLVFLAG_NLPID;
963 expected |= TLVFLAG_IPV4_ADDR;
964 expected |= TLVFLAG_IPV6_ADDR;
99894f9a 965 expected |= TLVFLAG_MT_ROUTER_INFORMATION;
eb5d44eb 966
3f045a08 967 auth_tlv_offset = stream_get_getp (circuit->rcv_stream);
eb5d44eb 968 retval = parse_tlvs (circuit->area->area_tag,
3f045a08
JB
969 STREAM_PNT (circuit->rcv_stream),
970 hdr.pdu_len - ISIS_LANHELLO_HDRLEN - ISIS_FIXED_HDR_LEN,
971 &expected, &found, &tlvs,
972 &auth_tlv_offset);
eb5d44eb 973
f390d2c7 974 if (retval > ISIS_WARNING)
975 {
976 zlog_warn ("parse_tlvs() failed");
977 goto out;
978 }
eb5d44eb 979
f390d2c7 980 if (!(found & TLVFLAG_AREA_ADDRS))
981 {
982 zlog_warn ("No Area addresses TLV in Level %d LAN IS to IS hello",
983 level);
eb5d44eb 984 retval = ISIS_WARNING;
985 goto out;
986 }
f390d2c7 987
b72f345d
DL
988 if (!(found & TLVFLAG_NLPID))
989 {
990 zlog_warn ("No supported protocols TLV in Level %d LAN IS to IS hello",
991 level);
992 retval = ISIS_WARNING;
993 goto out;
994 }
995
3f045a08 996 /* Verify authentication, either cleartext of HMAC MD5 */
f390d2c7 997 if (circuit->passwd.type)
998 {
999 if (!(found & TLVFLAG_AUTH_INFO) ||
3f045a08
JB
1000 authentication_check (&tlvs.auth_info, &circuit->passwd,
1001 circuit->rcv_stream, auth_tlv_offset))
1002 {
1003 isis_event_auth_failure (circuit->area->area_tag,
1004 "LAN hello authentication failure",
1005 hdr.source_id);
1006 retval = ISIS_WARNING;
1007 goto out;
1008 }
f390d2c7 1009 }
eb5d44eb 1010
19f78ceb
DL
1011 if (!memcmp (hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN))
1012 {
1013 zlog_warn ("ISIS-Adj (%s): duplicate system ID on interface %s",
1014 circuit->area->area_tag, circuit->interface->name);
1015 return ISIS_WARNING;
1016 }
1017
eb5d44eb 1018 /*
1019 * Accept the level 1 adjacency only if a match between local and
1020 * remote area addresses is found
1021 */
3f045a08
JB
1022 if (listcount (circuit->area->area_addrs) == 0 ||
1023 (level == IS_LEVEL_1 &&
1024 area_match (circuit->area->area_addrs, tlvs.area_addrs) == 0))
f390d2c7 1025 {
1026 if (isis->debugs & DEBUG_ADJ_PACKETS)
1027 {
529d65b3 1028 zlog_debug ("ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
1029 circuit->area->area_tag, level,
1030 circuit->interface->name);
f390d2c7 1031 }
1032 retval = ISIS_OK;
1033 goto out;
eb5d44eb 1034 }
eb5d44eb 1035
1036 /*
1037 * it's own IIH PDU - discard silently
f390d2c7 1038 */
1039 if (!memcmp (circuit->u.bc.snpa, ssnpa, ETH_ALEN))
1040 {
529d65b3 1041 zlog_debug ("ISIS-Adj (%s): it's own IIH PDU - discarded",
1042 circuit->area->area_tag);
eb5d44eb 1043
f390d2c7 1044 retval = ISIS_OK;
1045 goto out;
1046 }
eb5d44eb 1047
1048 /*
481f1484 1049 * check if both ends have an IPv4 address
eb5d44eb 1050 */
481f1484
CF
1051 if (circuit->ip_addrs && listcount(circuit->ip_addrs)
1052 && tlvs.ipv4_addrs && listcount(tlvs.ipv4_addrs))
f390d2c7 1053 {
481f1484 1054 v4_usable = 1;
f390d2c7 1055 }
481f1484 1056
28a8cfcb
DL
1057 if (found & TLVFLAG_IPV6_ADDR)
1058 {
1059 /* TBA: check that we have a linklocal ourselves? */
1060 struct listnode *node;
ad2f92b6 1061 struct in6_addr *ip;
28a8cfcb
DL
1062 for (ALL_LIST_ELEMENTS_RO (tlvs.ipv6_addrs, node, ip))
1063 if (IN6_IS_ADDR_LINKLOCAL (ip))
1064 {
1065 v6_usable = 1;
1066 break;
1067 }
1068
1069 if (!v6_usable)
1070 zlog_warn ("ISIS-Adj: IPv6 addresses present but no link-local "
1071 "in LAN IIH from %s\n", circuit->interface->name);
1072 }
1073
1074 if (!(found & (TLVFLAG_IPV4_ADDR | TLVFLAG_IPV6_ADDR)))
1075 zlog_warn ("ISIS-Adj: neither IPv4 nor IPv6 addr in LAN IIH from %s\n",
1076 circuit->interface->name);
28a8cfcb
DL
1077
1078 if (!v6_usable && !v4_usable)
1079 {
1080 free_tlvs (&tlvs);
1081 return ISIS_WARNING;
1082 }
1083
eb5d44eb 1084
1085 adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]);
3f045a08
JB
1086 if ((adj == NULL) || (memcmp(adj->snpa, ssnpa, ETH_ALEN)) ||
1087 (adj->level != level))
f390d2c7 1088 {
3f045a08
JB
1089 if (!adj)
1090 {
1091 /*
1092 * Do as in 8.4.2.5
1093 */
1094 adj = isis_new_adj (hdr.source_id, ssnpa, level, circuit);
1095 if (adj == NULL)
1096 {
1097 retval = ISIS_ERROR;
1098 goto out;
1099 }
1100 }
1101 else
1102 {
1103 if (ssnpa) {
1104 memcpy (adj->snpa, ssnpa, 6);
1105 } else {
1106 memset (adj->snpa, ' ', 6);
1107 }
1108 adj->level = level;
1109 }
f390d2c7 1110 isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL);
eb5d44eb 1111
3f045a08
JB
1112 if (level == IS_LEVEL_1)
1113 adj->sys_type = ISIS_SYSTYPE_L1_IS;
f390d2c7 1114 else
3f045a08 1115 adj->sys_type = ISIS_SYSTYPE_L2_IS;
f390d2c7 1116 list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]);
1117 isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1],
3f045a08 1118 circuit->u.bc.lan_neighs[level - 1]);
eb5d44eb 1119 }
eb5d44eb 1120
a211d65d 1121 if(adj->dis_record[level-1].dis==ISIS_IS_DIS)
1122 switch (level)
1123 {
1124 case 1:
1125 if (memcmp (circuit->u.bc.l1_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1))
1126 {
ffa2c898
QY
1127 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
1128 NULL);
64a7afd6 1129 memcpy (&circuit->u.bc.l1_desig_is, hdr.lan_id,
1130 ISIS_SYS_ID_LEN + 1);
a211d65d 1131 }
1132 break;
1133 case 2:
1134 if (memcmp (circuit->u.bc.l2_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1))
1135 {
ffa2c898
QY
1136 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
1137 NULL);
64a7afd6 1138 memcpy (&circuit->u.bc.l2_desig_is, hdr.lan_id,
1139 ISIS_SYS_ID_LEN + 1);
a211d65d 1140 }
1141 break;
1142 }
eb5d44eb 1143
1144 adj->hold_time = hdr.hold_time;
f390d2c7 1145 adj->last_upd = time (NULL);
1146 adj->prio[level - 1] = hdr.prio;
eb5d44eb 1147
1148 memcpy (adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1);
1149
3f045a08
JB
1150 tlvs_to_adj_area_addrs (&tlvs, adj);
1151
eb5d44eb 1152 /* which protocol are spoken ??? */
b72f345d
DL
1153 if (tlvs_to_adj_nlpids (&tlvs, adj))
1154 {
1155 retval = ISIS_WARNING;
1156 goto out;
1157 }
eb5d44eb 1158
1159 /* we need to copy addresses to the adj */
f390d2c7 1160 if (found & TLVFLAG_IPV4_ADDR)
eb5d44eb 1161 tlvs_to_adj_ipv4_addrs (&tlvs, adj);
1162
f390d2c7 1163 if (found & TLVFLAG_IPV6_ADDR)
eb5d44eb 1164 tlvs_to_adj_ipv6_addrs (&tlvs, adj);
eb5d44eb 1165
1166 adj->circuit_t = hdr.circuit_t;
1167
d8fba7d9
CF
1168 bool mt_set_changed = tlvs_to_adj_mt_set(&tlvs, v4_usable, v6_usable, adj);
1169
eb5d44eb 1170 /* lets take care of the expiry */
f390d2c7 1171 THREAD_TIMER_OFF (adj->t_expire);
ffa2c898
QY
1172 thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time,
1173 &adj->t_expire);
eb5d44eb 1174
1175 /*
1176 * If the snpa for this circuit is found from LAN Neighbours TLV
1177 * we have two-way communication -> adjacency can be put to state "up"
1178 */
1179
f390d2c7 1180 if (found & TLVFLAG_LAN_NEIGHS)
3f045a08
JB
1181 {
1182 if (adj->adj_state != ISIS_ADJ_UP)
f390d2c7 1183 {
3f045a08
JB
1184 for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa))
1185 {
1186 if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
1187 {
1188 isis_adj_state_change (adj, ISIS_ADJ_UP,
1189 "own SNPA found in LAN Neighbours TLV");
1190 }
1191 }
1192 }
1193 else
1194 {
1195 int found = 0;
1196 for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa))
1197 if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN))
1198 {
1199 found = 1;
1200 break;
1201 }
1202 if (found == 0)
1203 isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING,
1204 "own SNPA not found in LAN Neighbours TLV");
eb5d44eb 1205 }
3f045a08
JB
1206 }
1207 else if (adj->adj_state == ISIS_ADJ_UP)
1208 {
1209 isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING,
1210 "no LAN Neighbours TLV found");
1211 }
eb5d44eb 1212
d8fba7d9
CF
1213 if (adj->adj_state == ISIS_ADJ_UP && mt_set_changed)
1214 lsp_regenerate_schedule(adj->circuit->area, level, 0);
1215
f390d2c7 1216out:
f390d2c7 1217 if (isis->debugs & DEBUG_ADJ_PACKETS)
1218 {
529d65b3 1219 zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, "
ee046671 1220 "cirID %u, length %zd",
529d65b3 1221 circuit->area->area_tag,
1222 level, snpa_print (ssnpa), circuit->interface->name,
3f045a08 1223 circuit_t2string (circuit->is_type),
29e50b23 1224 circuit->circuit_id,
3f045a08 1225 stream_get_endp (circuit->rcv_stream));
f390d2c7 1226 }
eb5d44eb 1227
1228 free_tlvs (&tlvs);
1229
1230 return retval;
1231}
1232
1233/*
1234 * Process Level 1/2 Link State
1235 * ISO - 10589
1236 * Section 7.3.15.1 - Action on receipt of a link state PDU
f390d2c7 1237 */
1238static int
02e33d3e 1239process_lsp (int level, struct isis_circuit *circuit, const u_char *ssnpa)
eb5d44eb 1240{
1241 struct isis_link_state_hdr *hdr;
1242 struct isis_adjacency *adj = NULL;
1243 struct isis_lsp *lsp, *lsp0 = NULL;
1244 int retval = ISIS_OK, comp = 0;
1245 u_char lspid[ISIS_SYS_ID_LEN + 2];
1246 struct isis_passwd *passwd;
e38e0df0 1247 uint16_t pdu_len;
0250758d 1248 int lsp_confusion;
eb5d44eb 1249
3f045a08
JB
1250 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1251 {
1252 zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u",
1253 circuit->area->area_tag, level, circuit->interface->name,
1254 circuit_t2string (circuit->is_type), circuit->circuit_id);
1255 if (isis->debugs & DEBUG_PACKET_DUMP)
1256 zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
1257 stream_get_endp (circuit->rcv_stream));
1258 }
1259
f390d2c7 1260 if ((stream_get_endp (circuit->rcv_stream) -
1261 stream_get_getp (circuit->rcv_stream)) < ISIS_LSP_HDR_LEN)
1262 {
1263 zlog_warn ("Packet too short");
1264 return ISIS_WARNING;
1265 }
eb5d44eb 1266
1267 /* Reference the header */
f390d2c7 1268 hdr = (struct isis_link_state_hdr *) STREAM_PNT (circuit->rcv_stream);
e38e0df0
SV
1269 pdu_len = ntohs (hdr->pdu_len);
1270
1271 /* lsp length check */
a22ab5a5 1272 if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN) ||
e38e0df0
SV
1273 pdu_len > ISO_MTU(circuit) ||
1274 pdu_len > stream_get_endp (circuit->rcv_stream))
1275 {
1276 zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP length %d",
1277 circuit->area->area_tag,
1278 rawlspid_print (hdr->lsp_id), pdu_len);
1279
1280 return ISIS_WARNING;
1281 }
1282
1283 /*
1284 * Set the stream endp to PDU length, ignoring additional padding
1285 * introduced by transport chips.
1286 */
1287 if (pdu_len < stream_get_endp (circuit->rcv_stream))
1288 stream_set_endp (circuit->rcv_stream, pdu_len);
f390d2c7 1289
1290 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1291 {
529d65b3 1292 zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04x, "
3f045a08 1293 "lifetime %us, len %u, on %s",
529d65b3 1294 circuit->area->area_tag,
1295 level,
1296 rawlspid_print (hdr->lsp_id),
1297 ntohl (hdr->seq_num),
1298 ntohs (hdr->checksum),
1299 ntohs (hdr->rem_lifetime),
e38e0df0 1300 pdu_len,
15935e9a 1301 circuit->interface->name);
f390d2c7 1302 }
eb5d44eb 1303
e38e0df0
SV
1304 /* lsp is_type check */
1305 if ((hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1 &&
1306 (hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1_AND_2)
3f045a08 1307 {
e38e0df0 1308 zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP is type %x",
3f045a08 1309 circuit->area->area_tag,
e38e0df0
SV
1310 rawlspid_print (hdr->lsp_id), hdr->lsp_bits);
1311 /* continue as per RFC1122 Be liberal in what you accept, and
1312 * conservative in what you send */
3f045a08 1313 }
eb5d44eb 1314
1315 /* Checksum sanity check - FIXME: move to correct place */
1316 /* 12 = sysid+pdu+remtime */
f390d2c7 1317 if (iso_csum_verify (STREAM_PNT (circuit->rcv_stream) + 4,
e38e0df0 1318 pdu_len - 12, &hdr->checksum))
f390d2c7 1319 {
529d65b3 1320 zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x",
1321 circuit->area->area_tag,
1322 rawlspid_print (hdr->lsp_id), ntohs (hdr->checksum));
f390d2c7 1323
1324 return ISIS_WARNING;
1325 }
eb5d44eb 1326
1327 /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
f390d2c7 1328 if (circuit->ext_domain)
1329 {
529d65b3 1330 zlog_debug
f390d2c7 1331 ("ISIS-Upd (%s): LSP %s received at level %d over circuit with "
1332 "externalDomain = true", circuit->area->area_tag,
1333 rawlspid_print (hdr->lsp_id), level);
1334
1335 return ISIS_WARNING;
1336 }
eb5d44eb 1337
1338 /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
3f045a08 1339 if (!accept_level (level, circuit->is_type))
f390d2c7 1340 {
529d65b3 1341 zlog_debug ("ISIS-Upd (%s): LSP %s received at level %d over circuit of"
1342 " type %s",
1343 circuit->area->area_tag,
1344 rawlspid_print (hdr->lsp_id),
3f045a08 1345 level, circuit_t2string (circuit->is_type));
f390d2c7 1346
1347 return ISIS_WARNING;
1348 }
eb5d44eb 1349
1350 /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
1351
1352 /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use 3 */
1353
1354 /* 7.3.15.1 a) 7 - password check */
3f045a08
JB
1355 (level == IS_LEVEL_1) ? (passwd = &circuit->area->area_passwd) :
1356 (passwd = &circuit->area->domain_passwd);
f390d2c7 1357 if (passwd->type)
1358 {
3f045a08
JB
1359 if (lsp_authentication_check (circuit->rcv_stream, circuit->area,
1360 level, passwd))
f390d2c7 1361 {
1362 isis_event_auth_failure (circuit->area->area_tag,
1363 "LSP authentication failure", hdr->lsp_id);
1364 return ISIS_WARNING;
1365 }
eb5d44eb 1366 }
eb5d44eb 1367 /* Find the LSP in our database and compare it to this Link State header */
1368 lsp = lsp_search (hdr->lsp_id, circuit->area->lspdb[level - 1]);
1369 if (lsp)
f390d2c7 1370 comp = lsp_compare (circuit->area->area_tag, lsp, hdr->seq_num,
1371 hdr->checksum, hdr->rem_lifetime);
095f8fae 1372 if (lsp && (lsp->own_lsp))
eb5d44eb 1373 goto dontcheckadj;
1374
1375 /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same level */
1376 /* for broadcast circuits, snpa should be compared */
eb5d44eb 1377
f390d2c7 1378 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
1379 {
1380 adj = isis_adj_lookup_snpa (ssnpa, circuit->u.bc.adjdb[level - 1]);
1381 if (!adj)
1382 {
529d65b3 1383 zlog_debug ("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04x, "
1384 "lifetime %us on %s",
1385 circuit->area->area_tag,
1386 rawlspid_print (hdr->lsp_id),
1387 ntohl (hdr->seq_num),
1388 ntohs (hdr->checksum),
1389 ntohs (hdr->rem_lifetime), circuit->interface->name);
f390d2c7 1390 return ISIS_WARNING; /* Silently discard */
1391 }
eb5d44eb 1392 }
eb5d44eb 1393 /* for non broadcast, we just need to find same level adj */
f390d2c7 1394 else
1395 {
1396 /* If no adj, or no sharing of level */
1397 if (!circuit->u.p2p.neighbor)
1398 {
1399 return ISIS_OK; /* Silently discard */
1400 }
1401 else
1402 {
3f045a08 1403 if (((level == IS_LEVEL_1) &&
f390d2c7 1404 (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL2)) ||
3f045a08 1405 ((level == IS_LEVEL_2) &&
f390d2c7 1406 (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1)))
1407 return ISIS_WARNING; /* Silently discard */
1408 }
eb5d44eb 1409 }
3f045a08 1410
f390d2c7 1411dontcheckadj:
eb5d44eb 1412 /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */
1413
1414 /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */
1415
f390d2c7 1416 /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do it */
1417
0250758d
CF
1418 /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num but
1419 * wrong checksum, initiate a purge. */
1420 if (lsp
1421 && (lsp->lsp_header->seq_num == hdr->seq_num)
1422 && (lsp->lsp_header->checksum != hdr->checksum))
1423 {
1424 zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
1425 circuit->area->area_tag, rawlspid_print(hdr->lsp_id),
1426 ntohl(hdr->seq_num));
1427 hdr->rem_lifetime = 0;
1428 lsp_confusion = 1;
1429 }
1430 else
1431 lsp_confusion = 0;
1432
f390d2c7 1433 /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */
1434 if (hdr->rem_lifetime == 0)
1435 {
1436 if (!lsp)
1437 {
1438 /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't save */
1439 /* only needed on explicit update, eg - p2p */
1440 if (circuit->circ_type == CIRCUIT_T_P2P)
1441 ack_lsp (hdr, circuit, level);
1442 return retval; /* FIXME: do we need a purge? */
1443 }
1444 else
1445 {
1446 if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN))
1447 {
1448 /* LSP by some other system -> do 7.3.16.4 b) */
1449 /* 7.3.16.4 b) 1) */
1450 if (comp == LSP_NEWER)
1451 {
3f045a08 1452 lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
f390d2c7 1453 /* ii */
3f045a08 1454 lsp_set_all_srmflags (lsp);
f390d2c7 1455 /* v */
1456 ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); /* FIXME: OTHER than c */
f390d2c7 1457
0250758d
CF
1458 /* For the case of lsp confusion, flood the purge back to its
1459 * originator so that it can react. Otherwise, don't reflood
1460 * through incoming circuit as usual */
1461 if (!lsp_confusion)
1462 {
1463 /* iii */
1464 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
1465 /* iv */
1466 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1467 ISIS_SET_FLAG (lsp->SSNflags, circuit);
1468 }
f390d2c7 1469 } /* 7.3.16.4 b) 2) */
1470 else if (comp == LSP_EQUAL)
1471 {
1472 /* i */
1473 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
1474 /* ii */
1475 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1476 ISIS_SET_FLAG (lsp->SSNflags, circuit);
1477 } /* 7.3.16.4 b) 3) */
1478 else
1479 {
1480 ISIS_SET_FLAG (lsp->SRMflags, circuit);
1481 ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
1482 }
1483 }
3f045a08
JB
1484 else if (lsp->lsp_header->rem_lifetime != 0)
1485 {
1486 /* our own LSP -> 7.3.16.4 c) */
1487 if (comp == LSP_NEWER)
1488 {
1489 lsp_inc_seqnum (lsp, ntohl (hdr->seq_num));
1490 lsp_set_all_srmflags (lsp);
1491 }
1492 else
1493 {
1494 ISIS_SET_FLAG (lsp->SRMflags, circuit);
1495 ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
1496 }
1497 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1498 zlog_debug ("ISIS-Upd (%s): (1) re-originating LSP %s new "
1499 "seq 0x%08x", circuit->area->area_tag,
1500 rawlspid_print (hdr->lsp_id),
1501 ntohl (lsp->lsp_header->seq_num));
1502 }
f390d2c7 1503 }
1504 return retval;
eb5d44eb 1505 }
eb5d44eb 1506 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1507 * purge */
f390d2c7 1508 if (memcmp (hdr->lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
1509 {
1510 if (!lsp)
1511 {
1512 /* 7.3.16.4: initiate a purge */
17baea9c 1513 lsp_purge_non_exist(level, hdr, circuit->area);
f390d2c7 1514 return ISIS_OK;
1515 }
1516 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1517
1518 /* In 7.3.16.1, If an Intermediate system R somewhere in the domain
1519 * has information that the current sequence number for source S is
1520 * "greater" than that held by S, ... */
1521
e38e0df0 1522 if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num))
f390d2c7 1523 {
1524 /* 7.3.16.1 */
3f045a08 1525 lsp_inc_seqnum (lsp, ntohl (hdr->seq_num));
c89c05dd 1526 if (isis->debugs & DEBUG_UPDATE_PACKETS)
1527 zlog_debug ("ISIS-Upd (%s): (2) re-originating LSP %s new seq "
1528 "0x%08x", circuit->area->area_tag,
1529 rawlspid_print (hdr->lsp_id),
1530 ntohl (lsp->lsp_header->seq_num));
f390d2c7 1531 }
e38e0df0
SV
1532 /* If the received LSP is older or equal,
1533 * resend the LSP which will act as ACK */
1534 lsp_set_all_srmflags (lsp);
eb5d44eb 1535 }
f390d2c7 1536 else
1537 {
1538 /* 7.3.15.1 e) - This lsp originated on another system */
1539
1540 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */
1541 if ((!lsp || comp == LSP_NEWER))
1542 {
f390d2c7 1543 /*
1544 * If this lsp is a frag, need to see if we have zero lsp present
1545 */
1546 if (LSP_FRAGMENT (hdr->lsp_id) != 0)
1547 {
1548 memcpy (lspid, hdr->lsp_id, ISIS_SYS_ID_LEN + 1);
1549 LSP_FRAGMENT (lspid) = 0;
1550 lsp0 = lsp_search (lspid, circuit->area->lspdb[level - 1]);
1551 if (!lsp0)
1552 {
3f045a08 1553 zlog_debug ("Got lsp frag, while zero lsp not in database");
f390d2c7 1554 return ISIS_OK;
1555 }
1556 }
3f045a08
JB
1557 /* i */
1558 if (!lsp)
1559 {
1560 lsp = lsp_new_from_stream_ptr (circuit->rcv_stream,
e38e0df0 1561 pdu_len, lsp0,
3f045a08
JB
1562 circuit->area, level);
1563 lsp_insert (lsp, circuit->area->lspdb[level - 1]);
1564 }
1565 else /* exists, so we overwrite */
1566 {
1567 lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
1568 }
f390d2c7 1569 /* ii */
3f045a08 1570 lsp_set_all_srmflags (lsp);
f390d2c7 1571 /* iii */
1572 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
1573
1574 /* iv */
1575 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1576 ISIS_SET_FLAG (lsp->SSNflags, circuit);
1577 /* FIXME: v) */
1578 }
1579 /* 7.3.15.1 e) 2) LSP equal to the one in db */
1580 else if (comp == LSP_EQUAL)
1581 {
1582 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
3f045a08 1583 lsp_update (lsp, circuit->rcv_stream, circuit->area, level);
f390d2c7 1584 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
3f045a08 1585 ISIS_SET_FLAG (lsp->SSNflags, circuit);
f390d2c7 1586 }
1587 /* 7.3.15.1 e) 3) LSP older than the one in db */
1588 else
1589 {
1590 ISIS_SET_FLAG (lsp->SRMflags, circuit);
1591 ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
1592 }
eb5d44eb 1593 }
eb5d44eb 1594 return retval;
1595}
1596
1597/*
1598 * Process Sequence Numbers
1599 * ISO - 10589
1600 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1601 */
1602
92365889 1603static int
f390d2c7 1604process_snp (int snp_type, int level, struct isis_circuit *circuit,
02e33d3e 1605 const u_char *ssnpa)
eb5d44eb 1606{
1607 int retval = ISIS_OK;
1608 int cmp, own_lsp;
1609 char typechar = ' ';
e38e0df0 1610 uint16_t pdu_len;
eb5d44eb 1611 struct isis_adjacency *adj;
1612 struct isis_complete_seqnum_hdr *chdr = NULL;
1613 struct isis_partial_seqnum_hdr *phdr = NULL;
3f045a08 1614 uint32_t found = 0, expected = 0, auth_tlv_offset = 0;
eb5d44eb 1615 struct isis_lsp *lsp;
1616 struct lsp_entry *entry;
1eb8ef25 1617 struct listnode *node, *nnode;
1618 struct listnode *node2, *nnode2;
eb5d44eb 1619 struct tlvs tlvs;
1620 struct list *lsp_list = NULL;
1621 struct isis_passwd *passwd;
1622
f390d2c7 1623 if (snp_type == ISIS_SNP_CSNP_FLAG)
1624 {
1625 /* getting the header info */
1626 typechar = 'C';
1627 chdr =
1628 (struct isis_complete_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream);
e38e0df0
SV
1629 stream_forward_getp (circuit->rcv_stream, ISIS_CSNP_HDRLEN);
1630 pdu_len = ntohs (chdr->pdu_len);
a22ab5a5 1631 if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_CSNP_HDRLEN) ||
e38e0df0
SV
1632 pdu_len > ISO_MTU(circuit) ||
1633 pdu_len > stream_get_endp (circuit->rcv_stream))
f390d2c7 1634 {
e38e0df0
SV
1635 zlog_warn ("Received a CSNP with bogus length %d", pdu_len);
1636 return ISIS_WARNING;
f390d2c7 1637 }
eb5d44eb 1638 }
f390d2c7 1639 else
1640 {
1641 typechar = 'P';
1642 phdr =
1643 (struct isis_partial_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream);
e38e0df0
SV
1644 stream_forward_getp (circuit->rcv_stream, ISIS_PSNP_HDRLEN);
1645 pdu_len = ntohs (phdr->pdu_len);
a22ab5a5 1646 if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_PSNP_HDRLEN) ||
e38e0df0
SV
1647 pdu_len > ISO_MTU(circuit) ||
1648 pdu_len > stream_get_endp (circuit->rcv_stream))
f390d2c7 1649 {
ac7d3169 1650 zlog_warn ("Received a PSNP with bogus length %d", pdu_len);
e38e0df0 1651 return ISIS_WARNING;
f390d2c7 1652 }
eb5d44eb 1653 }
eb5d44eb 1654
e38e0df0
SV
1655 /*
1656 * Set the stream endp to PDU length, ignoring additional padding
1657 * introduced by transport chips.
1658 */
1659 if (pdu_len < stream_get_endp (circuit->rcv_stream))
1660 stream_set_endp (circuit->rcv_stream, pdu_len);
1661
eb5d44eb 1662 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
f390d2c7 1663 if (circuit->ext_domain)
1664 {
eb5d44eb 1665
529d65b3 1666 zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1667 "skipping: circuit externalDomain = true",
1668 circuit->area->area_tag,
1669 level, typechar, circuit->interface->name);
eb5d44eb 1670
f390d2c7 1671 return ISIS_OK;
1672 }
eb5d44eb 1673
1674 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
3f045a08 1675 if (!accept_level (level, circuit->is_type))
f390d2c7 1676 {
eb5d44eb 1677
529d65b3 1678 zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, "
1679 "skipping: circuit type %s does not match level %d",
1680 circuit->area->area_tag,
1681 level,
1682 typechar,
1683 circuit->interface->name,
3f045a08 1684 circuit_t2string (circuit->is_type), level);
eb5d44eb 1685
1686 return ISIS_OK;
1687 }
f390d2c7 1688
1689 /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */
1690 if ((snp_type == ISIS_SNP_PSNP_FLAG) &&
3f045a08
JB
1691 (circuit->circ_type == CIRCUIT_T_BROADCAST) &&
1692 (!circuit->u.bc.is_dr[level - 1]))
f390d2c7 1693 {
3f045a08
JB
1694 zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, "
1695 "skipping: we are not the DIS",
1696 circuit->area->area_tag,
1697 level,
1698 typechar, snpa_print (ssnpa), circuit->interface->name);
f390d2c7 1699
3f045a08 1700 return ISIS_OK;
f390d2c7 1701 }
eb5d44eb 1702
1703 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked */
1704
1705 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use 3
1706 * - already checked */
1707
1708 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same level */
1709 /* for broadcast circuits, snpa should be compared */
1710 /* FIXME : Do we need to check SNPA? */
f390d2c7 1711 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
1712 {
1713 if (snp_type == ISIS_SNP_CSNP_FLAG)
1714 {
1715 adj =
1716 isis_adj_lookup (chdr->source_id, circuit->u.bc.adjdb[level - 1]);
1717 }
1718 else
1719 {
1720 /* a psnp on a broadcast, how lovely of Juniper :) */
1721 adj =
1722 isis_adj_lookup (phdr->source_id, circuit->u.bc.adjdb[level - 1]);
1723 }
1724 if (!adj)
1725 return ISIS_OK; /* Silently discard */
1726 }
1727 else
1728 {
1729 if (!circuit->u.p2p.neighbor)
3f045a08
JB
1730 {
1731 zlog_warn ("no p2p neighbor on circuit %s", circuit->interface->name);
1732 return ISIS_OK; /* Silently discard */
1733 }
f390d2c7 1734 }
eb5d44eb 1735
1736 /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */
1737
1738 /* 7.3.15.2 a) 9 - Passwords for level 2 - not implemented */
1739
1740 memset (&tlvs, 0, sizeof (struct tlvs));
1741
1742 /* parse the SNP */
1743 expected |= TLVFLAG_LSP_ENTRIES;
1744 expected |= TLVFLAG_AUTH_INFO;
3f045a08
JB
1745
1746 auth_tlv_offset = stream_get_getp (circuit->rcv_stream);
eb5d44eb 1747 retval = parse_tlvs (circuit->area->area_tag,
f390d2c7 1748 STREAM_PNT (circuit->rcv_stream),
e38e0df0 1749 pdu_len - stream_get_getp (circuit->rcv_stream),
3f045a08 1750 &expected, &found, &tlvs, &auth_tlv_offset);
eb5d44eb 1751
f390d2c7 1752 if (retval > ISIS_WARNING)
1753 {
1754 zlog_warn ("something went very wrong processing SNP");
1755 free_tlvs (&tlvs);
1756 return retval;
1757 }
eb5d44eb 1758
3f045a08 1759 if (level == IS_LEVEL_1)
1cbc562b 1760 passwd = &circuit->area->area_passwd;
1761 else
1762 passwd = &circuit->area->domain_passwd;
1763
1764 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV))
f390d2c7 1765 {
1cbc562b 1766 if (passwd->type)
3f045a08
JB
1767 {
1768 if (!(found & TLVFLAG_AUTH_INFO) ||
1769 authentication_check (&tlvs.auth_info, passwd,
1770 circuit->rcv_stream, auth_tlv_offset))
1771 {
1772 isis_event_auth_failure (circuit->area->area_tag,
1773 "SNP authentication" " failure",
1774 phdr ? phdr->source_id :
1775 chdr->source_id);
1776 free_tlvs (&tlvs);
1777 return ISIS_OK;
1778 }
1779 }
f390d2c7 1780 }
eb5d44eb 1781
1782 /* debug isis snp-packets */
f390d2c7 1783 if (isis->debugs & DEBUG_SNP_PACKETS)
1784 {
529d65b3 1785 zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
1786 circuit->area->area_tag,
1787 level,
1788 typechar, snpa_print (ssnpa), circuit->interface->name);
f390d2c7 1789 if (tlvs.lsp_entries)
1790 {
3fdb2dd9 1791 for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry))
f390d2c7 1792 {
529d65b3 1793 zlog_debug ("ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x,"
1794 " cksum 0x%04x, lifetime %us",
1795 circuit->area->area_tag,
1796 typechar,
1797 rawlspid_print (entry->lsp_id),
1798 ntohl (entry->seq_num),
1799 ntohs (entry->checksum), ntohs (entry->rem_lifetime));
f390d2c7 1800 }
1801 }
eb5d44eb 1802 }
eb5d44eb 1803
1804 /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
f390d2c7 1805 if (tlvs.lsp_entries)
1806 {
3fdb2dd9 1807 for (ALL_LIST_ELEMENTS_RO (tlvs.lsp_entries, node, entry))
f390d2c7 1808 {
1809 lsp = lsp_search (entry->lsp_id, circuit->area->lspdb[level - 1]);
1810 own_lsp = !memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
1811 if (lsp)
1812 {
1813 /* 7.3.15.2 b) 1) is this LSP newer */
1814 cmp = lsp_compare (circuit->area->area_tag, lsp, entry->seq_num,
1815 entry->checksum, entry->rem_lifetime);
1816 /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p */
1817 if (cmp == LSP_EQUAL)
1818 {
3f045a08
JB
1819 /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */
1820 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
f390d2c7 1821 }
3f045a08 1822 /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM */
f390d2c7 1823 else if (cmp == LSP_OLDER)
1824 {
1825 ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
1826 ISIS_SET_FLAG (lsp->SRMflags, circuit);
1827 }
3f045a08 1828 /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM on p2p */
f390d2c7 1829 else
1830 {
f390d2c7 1831 if (own_lsp)
1832 {
1833 lsp_inc_seqnum (lsp, ntohl (entry->seq_num));
1834 ISIS_SET_FLAG (lsp->SRMflags, circuit);
1835 }
1836 else
1837 {
1838 ISIS_SET_FLAG (lsp->SSNflags, circuit);
3f045a08
JB
1839 /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */
1840 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
f390d2c7 1841 }
1842 }
1843 }
1844 else
1845 {
1846 /* 7.3.15.2 b) 5) if it was not found, and all of those are not 0,
1847 * insert it and set SSN on it */
1848 if (entry->rem_lifetime && entry->checksum && entry->seq_num &&
1849 memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN))
1850 {
b20ccb3a
CF
1851 lsp = lsp_new(circuit->area, entry->lsp_id,
1852 ntohs(entry->rem_lifetime),
1853 0, 0, entry->checksum, level);
f390d2c7 1854 lsp_insert (lsp, circuit->area->lspdb[level - 1]);
3f045a08 1855 ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags);
f390d2c7 1856 ISIS_SET_FLAG (lsp->SSNflags, circuit);
1857 }
1858 }
eb5d44eb 1859 }
1860 }
eb5d44eb 1861
1862 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported */
f390d2c7 1863 if (snp_type == ISIS_SNP_CSNP_FLAG)
1864 {
1865 /*
3f045a08
JB
1866 * Build a list from our own LSP db bounded with
1867 * start_lsp_id and stop_lsp_id
f390d2c7 1868 */
1869 lsp_list = list_new ();
1870 lsp_build_list_nonzero_ht (chdr->start_lsp_id, chdr->stop_lsp_id,
1871 lsp_list, circuit->area->lspdb[level - 1]);
1872
1873 /* Fixme: Find a better solution */
1874 if (tlvs.lsp_entries)
1875 {
1eb8ef25 1876 for (ALL_LIST_ELEMENTS (tlvs.lsp_entries, node, nnode, entry))
f390d2c7 1877 {
1eb8ef25 1878 for (ALL_LIST_ELEMENTS (lsp_list, node2, nnode2, lsp))
f390d2c7 1879 {
1880 if (lsp_id_cmp (lsp->lsp_header->lsp_id, entry->lsp_id) == 0)
1881 {
1882 list_delete_node (lsp_list, node2);
1883 break;
1884 }
1885 }
1886 }
1887 }
1888 /* on remaining LSPs we set SRM (neighbor knew not of) */
3fdb2dd9 1889 for (ALL_LIST_ELEMENTS_RO (lsp_list, node, lsp))
f390d2c7 1890 ISIS_SET_FLAG (lsp->SRMflags, circuit);
f390d2c7 1891 /* lets free it */
3f045a08
JB
1892 list_delete (lsp_list);
1893
eb5d44eb 1894 }
eb5d44eb 1895
1896 free_tlvs (&tlvs);
1897 return retval;
1898}
1899
92365889 1900static int
02e33d3e 1901process_csnp (int level, struct isis_circuit *circuit, const u_char *ssnpa)
eb5d44eb 1902{
3f045a08
JB
1903 if (isis->debugs & DEBUG_SNP_PACKETS)
1904 {
1905 zlog_debug ("ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u",
1906 circuit->area->area_tag, level, circuit->interface->name,
1907 circuit_t2string (circuit->is_type), circuit->circuit_id);
1908 if (isis->debugs & DEBUG_PACKET_DUMP)
1909 zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
1910 stream_get_endp (circuit->rcv_stream));
1911 }
1912
eb5d44eb 1913 /* Sanity check - FIXME: move to correct place */
f390d2c7 1914 if ((stream_get_endp (circuit->rcv_stream) -
1915 stream_get_getp (circuit->rcv_stream)) < ISIS_CSNP_HDRLEN)
1916 {
1917 zlog_warn ("Packet too short ( < %d)", ISIS_CSNP_HDRLEN);
1918 return ISIS_WARNING;
1919 }
eb5d44eb 1920
1921 return process_snp (ISIS_SNP_CSNP_FLAG, level, circuit, ssnpa);
1922}
1923
92365889 1924static int
02e33d3e 1925process_psnp (int level, struct isis_circuit *circuit, const u_char *ssnpa)
eb5d44eb 1926{
3f045a08
JB
1927 if (isis->debugs & DEBUG_SNP_PACKETS)
1928 {
1929 zlog_debug ("ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u",
1930 circuit->area->area_tag, level, circuit->interface->name,
1931 circuit_t2string (circuit->is_type), circuit->circuit_id);
1932 if (isis->debugs & DEBUG_PACKET_DUMP)
1933 zlog_dump_data (STREAM_DATA (circuit->rcv_stream),
1934 stream_get_endp (circuit->rcv_stream));
1935 }
1936
f390d2c7 1937 if ((stream_get_endp (circuit->rcv_stream) -
1938 stream_get_getp (circuit->rcv_stream)) < ISIS_PSNP_HDRLEN)
1939 {
3f045a08 1940 zlog_warn ("Packet too short ( < %d)", ISIS_PSNP_HDRLEN);
f390d2c7 1941 return ISIS_WARNING;
1942 }
eb5d44eb 1943
1944 return process_snp (ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa);
1945}
1946
eb5d44eb 1947/*
1948 * PDU Dispatcher
1949 */
1950
92365889 1951static int
f390d2c7 1952isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa)
eb5d44eb 1953{
eb5d44eb 1954 struct isis_fixed_hdr *hdr;
eb5d44eb 1955
f390d2c7 1956 int retval = ISIS_OK;
eb5d44eb 1957
1958 /*
1959 * Let's first read data from stream to the header
1960 */
f390d2c7 1961 hdr = (struct isis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream);
eb5d44eb 1962
f390d2c7 1963 if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS))
1964 {
3f045a08 1965 zlog_err ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp);
f390d2c7 1966 return ISIS_ERROR;
1967 }
eb5d44eb 1968
1969 /* now we need to know if this is an ISO 9542 packet and
1970 * take real good care of it, waaa!
1971 */
f390d2c7 1972 if (hdr->idrp == ISO9542_ESIS)
1973 {
3f045a08
JB
1974 zlog_err ("No support for ES-IS packet IDRP=%02x", hdr->idrp);
1975 return ISIS_ERROR;
f390d2c7 1976 }
3f045a08
JB
1977 stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN);
1978
eb5d44eb 1979 /*
1980 * and then process it
1981 */
1982
f390d2c7 1983 if (hdr->length < ISIS_MINIMUM_FIXED_HDR_LEN)
1984 {
1985 zlog_err ("Fixed header length = %d", hdr->length);
1986 return ISIS_ERROR;
1987 }
eb5d44eb 1988
f390d2c7 1989 if (hdr->version1 != 1)
1990 {
1991 zlog_warn ("Unsupported ISIS version %u", hdr->version1);
1992 return ISIS_WARNING;
1993 }
eb5d44eb 1994 /* either 6 or 0 */
f390d2c7 1995 if ((hdr->id_len != 0) && (hdr->id_len != ISIS_SYS_ID_LEN))
1996 {
1997 zlog_err
1998 ("IDFieldLengthMismatch: ID Length field in a received PDU %u, "
1999 "while the parameter for this IS is %u", hdr->id_len,
2000 ISIS_SYS_ID_LEN);
2001 return ISIS_ERROR;
2002 }
eb5d44eb 2003
f390d2c7 2004 if (hdr->version2 != 1)
2005 {
2006 zlog_warn ("Unsupported ISIS version %u", hdr->version2);
2007 return ISIS_WARNING;
2008 }
3f045a08
JB
2009
2010 if (circuit->is_passive)
2011 {
2012 zlog_warn ("Received ISIS PDU on passive circuit %s",
2013 circuit->interface->name);
2014 return ISIS_WARNING;
2015 }
2016
eb5d44eb 2017 /* either 3 or 0 */
f390d2c7 2018 if ((hdr->max_area_addrs != 0)
2019 && (hdr->max_area_addrs != isis->max_area_addrs))
2020 {
2021 zlog_err ("maximumAreaAddressesMismatch: maximumAreaAdresses in a "
2022 "received PDU %u while the parameter for this IS is %u",
2023 hdr->max_area_addrs, isis->max_area_addrs);
2024 return ISIS_ERROR;
2025 }
eb5d44eb 2026
f390d2c7 2027 switch (hdr->pdu_type)
2028 {
2029 case L1_LAN_HELLO:
2030 retval = process_lan_hello (ISIS_LEVEL1, circuit, ssnpa);
2031 break;
2032 case L2_LAN_HELLO:
2033 retval = process_lan_hello (ISIS_LEVEL2, circuit, ssnpa);
2034 break;
2035 case P2P_HELLO:
2036 retval = process_p2p_hello (circuit);
2037 break;
2038 case L1_LINK_STATE:
2039 retval = process_lsp (ISIS_LEVEL1, circuit, ssnpa);
2040 break;
2041 case L2_LINK_STATE:
2042 retval = process_lsp (ISIS_LEVEL2, circuit, ssnpa);
2043 break;
2044 case L1_COMPLETE_SEQ_NUM:
2045 retval = process_csnp (ISIS_LEVEL1, circuit, ssnpa);
2046 break;
2047 case L2_COMPLETE_SEQ_NUM:
2048 retval = process_csnp (ISIS_LEVEL2, circuit, ssnpa);
2049 break;
2050 case L1_PARTIAL_SEQ_NUM:
2051 retval = process_psnp (ISIS_LEVEL1, circuit, ssnpa);
2052 break;
2053 case L2_PARTIAL_SEQ_NUM:
2054 retval = process_psnp (ISIS_LEVEL2, circuit, ssnpa);
2055 break;
2056 default:
2057 return ISIS_ERROR;
2058 }
eb5d44eb 2059
2060 return retval;
2061}
2062
eb5d44eb 2063int
2064isis_receive (struct thread *thread)
2065{
eb5d44eb 2066 struct isis_circuit *circuit;
2067 u_char ssnpa[ETH_ALEN];
2068 int retval;
2069
2070 /*
2071 * Get the circuit
2072 */
2073 circuit = THREAD_ARG (thread);
2074 assert (circuit);
2075
f390d2c7 2076 circuit->t_read = NULL;
eb5d44eb 2077
b20ccb3a 2078 isis_circuit_stream(circuit, &circuit->rcv_stream);
eb5d44eb 2079
2080 retval = circuit->rx (circuit, ssnpa);
2081
2082 if (retval == ISIS_OK)
2083 retval = isis_handle_pdu (circuit, ssnpa);
2084
2085 /*
2086 * prepare for next packet.
2087 */
3f045a08 2088 if (!circuit->is_passive)
5904f19f 2089 isis_circuit_prepare (circuit);
eb5d44eb 2090
2091 return retval;
2092}
2093
eb5d44eb 2094 /* filling of the fixed isis header */
2095void
2096fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type)
2097{
2098 memset (hdr, 0, sizeof (struct isis_fixed_hdr));
2099
2100 hdr->idrp = ISO10589_ISIS;
2101
f390d2c7 2102 switch (pdu_type)
2103 {
2104 case L1_LAN_HELLO:
2105 case L2_LAN_HELLO:
2106 hdr->length = ISIS_LANHELLO_HDRLEN;
2107 break;
2108 case P2P_HELLO:
2109 hdr->length = ISIS_P2PHELLO_HDRLEN;
2110 break;
2111 case L1_LINK_STATE:
2112 case L2_LINK_STATE:
2113 hdr->length = ISIS_LSP_HDR_LEN;
2114 break;
2115 case L1_COMPLETE_SEQ_NUM:
2116 case L2_COMPLETE_SEQ_NUM:
2117 hdr->length = ISIS_CSNP_HDRLEN;
2118 break;
2119 case L1_PARTIAL_SEQ_NUM:
2120 case L2_PARTIAL_SEQ_NUM:
2121 hdr->length = ISIS_PSNP_HDRLEN;
2122 break;
2123 default:
2124 zlog_warn ("fill_fixed_hdr(): unknown pdu type %d", pdu_type);
2125 return;
2126 }
eb5d44eb 2127 hdr->length += ISIS_FIXED_HDR_LEN;
2128 hdr->pdu_type = pdu_type;
2129 hdr->version1 = 1;
f390d2c7 2130 hdr->id_len = 0; /* ISIS_SYS_ID_LEN - 0==6 */
eb5d44eb 2131 hdr->version2 = 1;
f390d2c7 2132 hdr->max_area_addrs = 0; /* isis->max_area_addrs - 0==3 */
eb5d44eb 2133}
2134
eb5d44eb 2135/*
2136 * SEND SIDE
2137 */
92365889 2138static void
eb5d44eb 2139fill_fixed_hdr_andstream (struct isis_fixed_hdr *hdr, u_char pdu_type,
f390d2c7 2140 struct stream *stream)
eb5d44eb 2141{
f390d2c7 2142 fill_fixed_hdr (hdr, pdu_type);
eb5d44eb 2143
2144 stream_putc (stream, hdr->idrp);
2145 stream_putc (stream, hdr->length);
2146 stream_putc (stream, hdr->version1);
2147 stream_putc (stream, hdr->id_len);
2148 stream_putc (stream, hdr->pdu_type);
2149 stream_putc (stream, hdr->version2);
2150 stream_putc (stream, hdr->reserved);
2151 stream_putc (stream, hdr->max_area_addrs);
2152
2153 return;
2154}
2155
eb5d44eb 2156int
2157send_hello (struct isis_circuit *circuit, int level)
2158{
2159 struct isis_fixed_hdr fixed_hdr;
2160 struct isis_lan_hello_hdr hello_hdr;
2161 struct isis_p2p_hello_hdr p2p_hello_hdr;
3f045a08 2162 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
7b7a77a6 2163 size_t len_pointer, length, auth_tlv_offset = 0;
eb5d44eb 2164 u_int32_t interval;
eb5d44eb 2165 int retval;
2166
3f045a08
JB
2167 if (circuit->is_passive)
2168 return ISIS_OK;
2169
f390d2c7 2170 if (circuit->interface->mtu == 0)
2171 {
2172 zlog_warn ("circuit has zero MTU");
2173 return ISIS_WARNING;
2174 }
eb5d44eb 2175
b20ccb3a 2176 isis_circuit_stream(circuit, &circuit->snd_stream);
eb5d44eb 2177
2178 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
3f045a08 2179 if (level == IS_LEVEL_1)
f390d2c7 2180 fill_fixed_hdr_andstream (&fixed_hdr, L1_LAN_HELLO,
2181 circuit->snd_stream);
eb5d44eb 2182 else
f390d2c7 2183 fill_fixed_hdr_andstream (&fixed_hdr, L2_LAN_HELLO,
2184 circuit->snd_stream);
eb5d44eb 2185 else
f390d2c7 2186 fill_fixed_hdr_andstream (&fixed_hdr, P2P_HELLO, circuit->snd_stream);
eb5d44eb 2187
2188 /*
2189 * Fill LAN Level 1 or 2 Hello PDU header
2190 */
2191 memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr));
f390d2c7 2192 interval = circuit->hello_multiplier[level - 1] *
eb5d44eb 2193 circuit->hello_interval[level - 1];
2194 if (interval > USHRT_MAX)
2195 interval = USHRT_MAX;
3f045a08 2196 hello_hdr.circuit_t = circuit->is_type;
eb5d44eb 2197 memcpy (hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN);
f390d2c7 2198 hello_hdr.hold_time = htons ((u_int16_t) interval);
eb5d44eb 2199
f390d2c7 2200 hello_hdr.pdu_len = 0; /* Update the PDU Length later */
9985f83c 2201 len_pointer = stream_get_endp (circuit->snd_stream) + 3 + ISIS_SYS_ID_LEN;
eb5d44eb 2202
2203 /* copy the shared part of the hello to the p2p hello if needed */
f390d2c7 2204 if (circuit->circ_type == CIRCUIT_T_P2P)
2205 {
2206 memcpy (&p2p_hello_hdr, &hello_hdr, 5 + ISIS_SYS_ID_LEN);
2207 p2p_hello_hdr.local_id = circuit->circuit_id;
2208 /* FIXME: need better understanding */
2209 stream_put (circuit->snd_stream, &p2p_hello_hdr, ISIS_P2PHELLO_HDRLEN);
2210 }
2211 else
2212 {
3f045a08
JB
2213 hello_hdr.prio = circuit->priority[level - 1];
2214 if (level == IS_LEVEL_1)
f390d2c7 2215 {
2216 memcpy (hello_hdr.lan_id, circuit->u.bc.l1_desig_is,
2217 ISIS_SYS_ID_LEN + 1);
2218 }
3f045a08 2219 else if (level == IS_LEVEL_2)
f390d2c7 2220 {
2221 memcpy (hello_hdr.lan_id, circuit->u.bc.l2_desig_is,
2222 ISIS_SYS_ID_LEN + 1);
2223 }
2224 stream_put (circuit->snd_stream, &hello_hdr, ISIS_LANHELLO_HDRLEN);
2225 }
eb5d44eb 2226
2227 /*
3f045a08 2228 * Then the variable length part.
eb5d44eb 2229 */
3f045a08 2230
eb5d44eb 2231 /* add circuit password */
3f045a08
JB
2232 switch (circuit->passwd.type)
2233 {
2234 /* Cleartext */
2235 case ISIS_PASSWD_TYPE_CLEARTXT:
2236 if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len,
2237 circuit->passwd.passwd, circuit->snd_stream))
2238 return ISIS_WARNING;
2239 break;
2240
2241 /* HMAC MD5 */
2242 case ISIS_PASSWD_TYPE_HMAC_MD5:
2243 /* Remember where TLV is written so we can later overwrite the MD5 hash */
2244 auth_tlv_offset = stream_get_endp (circuit->snd_stream);
2245 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2246 if (tlv_add_authinfo (circuit->passwd.type, ISIS_AUTH_MD5_SIZE,
2247 hmac_md5_hash, circuit->snd_stream))
2248 return ISIS_WARNING;
2249 break;
2250
2251 default:
2252 break;
2253 }
2254
eb5d44eb 2255 /* Area Addresses TLV */
3f045a08
JB
2256 if (listcount (circuit->area->area_addrs) == 0)
2257 return ISIS_WARNING;
2258 if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream))
2259 return ISIS_WARNING;
eb5d44eb 2260
2261 /* LAN Neighbors TLV */
f390d2c7 2262 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
2263 {
3f045a08
JB
2264 if (level == IS_LEVEL_1 && circuit->u.bc.lan_neighs[0] &&
2265 listcount (circuit->u.bc.lan_neighs[0]) > 0)
f390d2c7 2266 if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[0],
2267 circuit->snd_stream))
2268 return ISIS_WARNING;
3f045a08
JB
2269 if (level == IS_LEVEL_2 && circuit->u.bc.lan_neighs[1] &&
2270 listcount (circuit->u.bc.lan_neighs[1]) > 0)
f390d2c7 2271 if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[1],
2272 circuit->snd_stream))
2273 return ISIS_WARNING;
2274 }
eb5d44eb 2275
2276 /* Protocols Supported TLV */
f390d2c7 2277 if (circuit->nlpids.count > 0)
eb5d44eb 2278 if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream))
2279 return ISIS_WARNING;
2280 /* IP interface Address TLV */
3f045a08
JB
2281 if (circuit->ip_router && circuit->ip_addrs &&
2282 listcount (circuit->ip_addrs) > 0)
eb5d44eb 2283 if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream))
2284 return ISIS_WARNING;
2285
99894f9a
CF
2286 /*
2287 * MT Supported TLV
2288 *
2289 * TLV gets included if no topology is enabled on the interface,
2290 * if one topology other than #0 is enabled, or if multiple topologies
2291 * are enabled.
2292 */
2293 struct isis_circuit_mt_setting **mt_settings;
2294 unsigned int mt_count;
2295
2296 mt_settings = circuit_mt_settings(circuit, &mt_count);
2297 if ((mt_count == 0 && area_is_mt(circuit->area))
2298 || (mt_count == 1 && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST)
2299 || (mt_count > 1))
2300 {
2301 struct list *mt_info = list_new();
2302 mt_info->del = free_tlv;
2303
2304 for (unsigned int i = 0; i < mt_count; i++)
2305 {
2306 struct mt_router_info *info;
2307
2308 info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info));
2309 info->mtid = mt_settings[i]->mtid;
2310 /* overload info is not valid in IIH, so it's not included here */
2311 listnode_add(mt_info, info);
2312 }
2313 tlv_add_mt_router_info (mt_info, circuit->snd_stream);
2314 list_free(mt_info);
2315 }
2316
eb5d44eb 2317 /* IPv6 Interface Address TLV */
f390d2c7 2318 if (circuit->ipv6_router && circuit->ipv6_link &&
3f045a08 2319 listcount (circuit->ipv6_link) > 0)
eb5d44eb 2320 if (tlv_add_ipv6_addrs (circuit->ipv6_link, circuit->snd_stream))
2321 return ISIS_WARNING;
eb5d44eb 2322
3f045a08 2323 if (circuit->pad_hellos)
eb5d44eb 2324 if (tlv_add_padding (circuit->snd_stream))
2325 return ISIS_WARNING;
2326
9985f83c 2327 length = stream_get_endp (circuit->snd_stream);
eb5d44eb 2328 /* Update PDU length */
f390d2c7 2329 stream_putw_at (circuit->snd_stream, len_pointer, (u_int16_t) length);
eb5d44eb 2330
3f045a08
JB
2331 /* For HMAC MD5 we need to compute the md5 hash and store it */
2332 if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5)
2333 {
2334 hmac_md5 (STREAM_DATA (circuit->snd_stream),
2335 stream_get_endp (circuit->snd_stream),
2336 (unsigned char *) &circuit->passwd.passwd, circuit->passwd.len,
f2bce9a5 2337 (unsigned char *) &hmac_md5_hash);
3f045a08
JB
2338 /* Copy the hash into the stream */
2339 memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3,
2340 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2341 }
eb5d44eb 2342
f390d2c7 2343 if (isis->debugs & DEBUG_ADJ_PACKETS)
2344 {
2345 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
2346 {
7b7a77a6 2347 zlog_debug ("ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
529d65b3 2348 circuit->area->area_tag, level, circuit->interface->name,
3f045a08 2349 length);
f390d2c7 2350 }
2351 else
2352 {
7b7a77a6 2353 zlog_debug ("ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
529d65b3 2354 circuit->area->area_tag, circuit->interface->name,
3f045a08 2355 length);
f390d2c7 2356 }
3f045a08
JB
2357 if (isis->debugs & DEBUG_PACKET_DUMP)
2358 zlog_dump_data (STREAM_DATA (circuit->snd_stream),
2359 stream_get_endp (circuit->snd_stream));
eb5d44eb 2360 }
eb5d44eb 2361
3f045a08
JB
2362 retval = circuit->tx (circuit, level);
2363 if (retval != ISIS_OK)
2364 zlog_err ("ISIS-Adj (%s): Send L%d IIH on %s failed",
2365 circuit->area->area_tag, level, circuit->interface->name);
eb5d44eb 2366
3f045a08 2367 return retval;
eb5d44eb 2368}
2369
2370int
2371send_lan_l1_hello (struct thread *thread)
2372{
eb5d44eb 2373 struct isis_circuit *circuit;
2374 int retval;
2375
2376 circuit = THREAD_ARG (thread);
2377 assert (circuit);
2378 circuit->u.bc.t_send_lan_hello[0] = NULL;
2379
ddfdbd32
CF
2380 if (!(circuit->area->is_type & IS_LEVEL_1))
2381 {
2382 zlog_warn ("ISIS-Hello (%s): Trying to send L1 IIH in L2-only area",
2383 circuit->area->area_tag);
2384 return 1;
2385 }
2386
eb5d44eb 2387 if (circuit->u.bc.run_dr_elect[0])
f7535236 2388 isis_dr_elect (circuit, 1);
eb5d44eb 2389
3f045a08 2390 retval = send_hello (circuit, 1);
eb5d44eb 2391
2392 /* set next timer thread */
ffa2c898
QY
2393 thread_add_timer(master, send_lan_l1_hello, circuit,
2394 isis_jitter(circuit->hello_interval[0], IIH_JITTER),
2395 &circuit->u.bc.t_send_lan_hello[0]);
eb5d44eb 2396
2397 return retval;
2398}
2399
2400int
2401send_lan_l2_hello (struct thread *thread)
2402{
2403 struct isis_circuit *circuit;
2404 int retval;
2405
2406 circuit = THREAD_ARG (thread);
2407 assert (circuit);
2408 circuit->u.bc.t_send_lan_hello[1] = NULL;
2409
ddfdbd32
CF
2410 if (!(circuit->area->is_type & IS_LEVEL_2))
2411 {
2412 zlog_warn ("ISIS-Hello (%s): Trying to send L2 IIH in L1 area",
2413 circuit->area->area_tag);
2414 return 1;
2415 }
2416
eb5d44eb 2417 if (circuit->u.bc.run_dr_elect[1])
f7535236 2418 isis_dr_elect (circuit, 2);
eb5d44eb 2419
3f045a08 2420 retval = send_hello (circuit, 2);
eb5d44eb 2421
f390d2c7 2422 /* set next timer thread */
ffa2c898
QY
2423 thread_add_timer(master, send_lan_l2_hello, circuit,
2424 isis_jitter(circuit->hello_interval[1], IIH_JITTER),
2425 &circuit->u.bc.t_send_lan_hello[1]);
eb5d44eb 2426
2427 return retval;
2428}
2429
2430int
2431send_p2p_hello (struct thread *thread)
2432{
2433 struct isis_circuit *circuit;
2434
2435 circuit = THREAD_ARG (thread);
2436 assert (circuit);
2437 circuit->u.p2p.t_send_p2p_hello = NULL;
2438
f390d2c7 2439 send_hello (circuit, 1);
eb5d44eb 2440
f390d2c7 2441 /* set next timer thread */
ffa2c898
QY
2442 thread_add_timer(master, send_p2p_hello, circuit,
2443 isis_jitter(circuit->hello_interval[1], IIH_JITTER),
2444 &circuit->u.p2p.t_send_p2p_hello);
eb5d44eb 2445
2446 return ISIS_OK;
2447}
2448
92365889 2449static int
f390d2c7 2450build_csnp (int level, u_char * start, u_char * stop, struct list *lsps,
2451 struct isis_circuit *circuit)
eb5d44eb 2452{
2453 struct isis_fixed_hdr fixed_hdr;
2454 struct isis_passwd *passwd;
eb5d44eb 2455 unsigned long lenp;
2456 u_int16_t length;
3f045a08
JB
2457 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2458 unsigned long auth_tlv_offset = 0;
2459 int retval = ISIS_OK;
2460
b20ccb3a 2461 isis_circuit_stream(circuit, &circuit->snd_stream);
eb5d44eb 2462
3f045a08 2463 if (level == IS_LEVEL_1)
f390d2c7 2464 fill_fixed_hdr_andstream (&fixed_hdr, L1_COMPLETE_SEQ_NUM,
2465 circuit->snd_stream);
eb5d44eb 2466 else
f390d2c7 2467 fill_fixed_hdr_andstream (&fixed_hdr, L2_COMPLETE_SEQ_NUM,
2468 circuit->snd_stream);
eb5d44eb 2469
2470 /*
2471 * Fill Level 1 or 2 Complete Sequence Numbers header
2472 */
2473
9985f83c 2474 lenp = stream_get_endp (circuit->snd_stream);
f390d2c7 2475 stream_putw (circuit->snd_stream, 0); /* PDU length - when we know it */
eb5d44eb 2476 /* no need to send the source here, it is always us if we csnp */
2477 stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
2478 /* with zero circuit id - ref 9.10, 9.11 */
2479 stream_putc (circuit->snd_stream, 0x00);
2480
2481 stream_put (circuit->snd_stream, start, ISIS_SYS_ID_LEN + 2);
2482 stream_put (circuit->snd_stream, stop, ISIS_SYS_ID_LEN + 2);
2483
2484 /*
2485 * And TLVs
2486 */
3f045a08 2487 if (level == IS_LEVEL_1)
eb5d44eb 2488 passwd = &circuit->area->area_passwd;
2489 else
2490 passwd = &circuit->area->domain_passwd;
2491
1cbc562b 2492 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
3f045a08
JB
2493 {
2494 switch (passwd->type)
2495 {
2496 /* Cleartext */
2497 case ISIS_PASSWD_TYPE_CLEARTXT:
2498 if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, passwd->len,
2499 passwd->passwd, circuit->snd_stream))
2500 return ISIS_WARNING;
2501 break;
2502
2503 /* HMAC MD5 */
2504 case ISIS_PASSWD_TYPE_HMAC_MD5:
2505 /* Remember where TLV is written so we can later overwrite the MD5 hash */
2506 auth_tlv_offset = stream_get_endp (circuit->snd_stream);
2507 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2508 if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE,
2509 hmac_md5_hash, circuit->snd_stream))
2510 return ISIS_WARNING;
2511 break;
2512
2513 default:
2514 break;
f390d2c7 2515 }
3f045a08
JB
2516 }
2517
2518 retval = tlv_add_lsp_entries (lsps, circuit->snd_stream);
2519 if (retval != ISIS_OK)
2520 return retval;
2521
9985f83c 2522 length = (u_int16_t) stream_get_endp (circuit->snd_stream);
eb5d44eb 2523 /* Update PU length */
2524 stream_putw_at (circuit->snd_stream, lenp, length);
2525
3f045a08
JB
2526 /* For HMAC MD5 we need to compute the md5 hash and store it */
2527 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) &&
2528 passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5)
2529 {
2530 hmac_md5 (STREAM_DATA (circuit->snd_stream),
2531 stream_get_endp(circuit->snd_stream),
2532 (unsigned char *) &passwd->passwd, passwd->len,
f2bce9a5 2533 (unsigned char *) &hmac_md5_hash);
3f045a08
JB
2534 /* Copy the hash into the stream */
2535 memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3,
2536 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2537 }
2538
eb5d44eb 2539 return retval;
2540}
2541
3f045a08
JB
2542/*
2543 * Count the maximum number of lsps that can be accomodated by a given size.
2544 */
2545static uint16_t
2546get_max_lsp_count (uint16_t size)
2547{
2548 uint16_t tlv_count;
2549 uint16_t lsp_count;
2550 uint16_t remaining_size;
2551
2552 /* First count the full size TLVs */
2553 tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE;
2554 lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN);
2555
2556 /* The last TLV, if any */
2557 remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE;
2558 if (remaining_size - 2 >= LSP_ENTRIES_LEN)
2559 lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN;
2560
2561 return lsp_count;
2562}
2563
2564/*
2565 * Calculate the length of Authentication Info. TLV.
2566 */
2567static uint16_t
2568auth_tlv_length (int level, struct isis_circuit *circuit)
2569{
2570 struct isis_passwd *passwd;
2571 uint16_t length;
2572
2573 if (level == IS_LEVEL_1)
2574 passwd = &circuit->area->area_passwd;
2575 else
2576 passwd = &circuit->area->domain_passwd;
2577
2578 /* Also include the length of TLV header */
2579 length = AUTH_INFO_HDRLEN;
2580 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
2581 {
2582 switch (passwd->type)
2583 {
2584 /* Cleartext */
2585 case ISIS_PASSWD_TYPE_CLEARTXT:
2586 length += passwd->len;
2587 break;
2588
2589 /* HMAC MD5 */
2590 case ISIS_PASSWD_TYPE_HMAC_MD5:
2591 length += ISIS_AUTH_MD5_SIZE;
2592 break;
2593
2594 default:
2595 break;
2596 }
2597 }
2598
2599 return length;
2600}
2601
2602/*
2603 * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP.
2604 */
2605static uint16_t
2606max_lsps_per_snp (int snp_type, int level, struct isis_circuit *circuit)
2607{
2608 int snp_hdr_len;
2609 int auth_tlv_len;
2610 uint16_t lsp_count;
2611
2612 snp_hdr_len = ISIS_FIXED_HDR_LEN;
2613 if (snp_type == ISIS_SNP_CSNP_FLAG)
2614 snp_hdr_len += ISIS_CSNP_HDRLEN;
2615 else
2616 snp_hdr_len += ISIS_PSNP_HDRLEN;
2617
2618 auth_tlv_len = auth_tlv_length (level, circuit);
2619 lsp_count = get_max_lsp_count (
2620 stream_get_size (circuit->snd_stream) - snp_hdr_len - auth_tlv_len);
e38e0df0 2621 return lsp_count;
3f045a08
JB
2622}
2623
eb5d44eb 2624/*
2625 * FIXME: support multiple CSNPs
2626 */
2627
2628int
2629send_csnp (struct isis_circuit *circuit, int level)
2630{
eb5d44eb 2631 u_char start[ISIS_SYS_ID_LEN + 2];
2632 u_char stop[ISIS_SYS_ID_LEN + 2];
2633 struct list *list = NULL;
3fdb2dd9 2634 struct listnode *node;
eb5d44eb 2635 struct isis_lsp *lsp;
3f045a08
JB
2636 u_char num_lsps, loop = 1;
2637 int i, retval = ISIS_OK;
2638
2639 if (circuit->area->lspdb[level - 1] == NULL ||
2640 dict_count (circuit->area->lspdb[level - 1]) == 0)
2641 return retval;
eb5d44eb 2642
f390d2c7 2643 memset (start, 0x00, ISIS_SYS_ID_LEN + 2);
eb5d44eb 2644 memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
2645
3f045a08
JB
2646 num_lsps = max_lsps_per_snp (ISIS_SNP_CSNP_FLAG, level, circuit);
2647
2648 while (loop)
f390d2c7 2649 {
2650 list = list_new ();
3f045a08
JB
2651 lsp_build_list (start, stop, num_lsps, list,
2652 circuit->area->lspdb[level - 1]);
2653 /*
2654 * Update the stop lsp_id before encoding this CSNP.
2655 */
2656 if (listcount (list) < num_lsps)
2657 {
2658 memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
2659 }
f390d2c7 2660 else
3f045a08
JB
2661 {
2662 node = listtail (list);
2663 lsp = listgetdata (node);
2664 memcpy (stop, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2);
2665 }
f390d2c7 2666
2667 retval = build_csnp (level, start, stop, list, circuit);
3f045a08
JB
2668 if (retval != ISIS_OK)
2669 {
2670 zlog_err ("ISIS-Snp (%s): Build L%d CSNP on %s failed",
2671 circuit->area->area_tag, level, circuit->interface->name);
2672 list_delete (list);
2673 return retval;
2674 }
f390d2c7 2675
2676 if (isis->debugs & DEBUG_SNP_PACKETS)
3f045a08 2677 {
0232bb72 2678 zlog_debug ("ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
3f045a08
JB
2679 circuit->area->area_tag, level, circuit->interface->name,
2680 stream_get_endp (circuit->snd_stream));
2681 for (ALL_LIST_ELEMENTS_RO (list, node, lsp))
2682 {
2683 zlog_debug ("ISIS-Snp (%s): CSNP entry %s, seq 0x%08x,"
2684 " cksum 0x%04x, lifetime %us",
2685 circuit->area->area_tag,
2686 rawlspid_print (lsp->lsp_header->lsp_id),
2687 ntohl (lsp->lsp_header->seq_num),
2688 ntohs (lsp->lsp_header->checksum),
2689 ntohs (lsp->lsp_header->rem_lifetime));
2690 }
2691 if (isis->debugs & DEBUG_PACKET_DUMP)
2692 zlog_dump_data (STREAM_DATA (circuit->snd_stream),
2693 stream_get_endp (circuit->snd_stream));
2694 }
2695
2696 retval = circuit->tx (circuit, level);
2697 if (retval != ISIS_OK)
2698 {
2699 zlog_err ("ISIS-Snp (%s): Send L%d CSNP on %s failed",
2700 circuit->area->area_tag, level,
2701 circuit->interface->name);
2702 list_delete (list);
2703 return retval;
2704 }
eb5d44eb 2705
3f045a08
JB
2706 /*
2707 * Start lsp_id of the next CSNP should be one plus the
2708 * stop lsp_id in this current CSNP.
2709 */
2710 memcpy (start, stop, ISIS_SYS_ID_LEN + 2);
2711 loop = 0;
2712 for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i)
2713 {
2714 if (start[i] < (u_char)0xff)
2715 {
2716 start[i] += 1;
2717 loop = 1;
2718 break;
2719 }
2720 }
2721 memset (stop, 0xff, ISIS_SYS_ID_LEN + 2);
f390d2c7 2722 list_delete (list);
f390d2c7 2723 }
3f045a08 2724
eb5d44eb 2725 return retval;
2726}
2727
2728int
2729send_l1_csnp (struct thread *thread)
2730{
2731 struct isis_circuit *circuit;
2732 int retval = ISIS_OK;
2733
2734 circuit = THREAD_ARG (thread);
2735 assert (circuit);
2736
2737 circuit->t_send_csnp[0] = NULL;
2738
f390d2c7 2739 if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[0])
2740 {
2741 send_csnp (circuit, 1);
2742 }
eb5d44eb 2743 /* set next timer thread */
ffa2c898
QY
2744 thread_add_timer(master, send_l1_csnp, circuit,
2745 isis_jitter(circuit->csnp_interval[0], CSNP_JITTER),
2746 &circuit->t_send_csnp[0]);
eb5d44eb 2747
2748 return retval;
2749}
2750
2751int
2752send_l2_csnp (struct thread *thread)
2753{
2754 struct isis_circuit *circuit;
2755 int retval = ISIS_OK;
2756
2757 circuit = THREAD_ARG (thread);
2758 assert (circuit);
2759
2760 circuit->t_send_csnp[1] = NULL;
2761
f390d2c7 2762 if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[1])
2763 {
2764 send_csnp (circuit, 2);
2765 }
eb5d44eb 2766 /* set next timer thread */
ffa2c898
QY
2767 thread_add_timer(master, send_l2_csnp, circuit,
2768 isis_jitter(circuit->csnp_interval[1], CSNP_JITTER),
2769 &circuit->t_send_csnp[1]);
d70f99e1 2770
eb5d44eb 2771 return retval;
2772}
2773
92365889 2774static int
eb5d44eb 2775build_psnp (int level, struct isis_circuit *circuit, struct list *lsps)
2776{
2777 struct isis_fixed_hdr fixed_hdr;
2778 unsigned long lenp;
2779 u_int16_t length;
eb5d44eb 2780 struct isis_lsp *lsp;
2781 struct isis_passwd *passwd;
3fdb2dd9 2782 struct listnode *node;
3f045a08
JB
2783 unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE];
2784 unsigned long auth_tlv_offset = 0;
2785 int retval = ISIS_OK;
eb5d44eb 2786
b20ccb3a 2787 isis_circuit_stream(circuit, &circuit->snd_stream);
3f045a08
JB
2788
2789 if (level == IS_LEVEL_1)
f390d2c7 2790 fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM,
2791 circuit->snd_stream);
eb5d44eb 2792 else
2793 fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM,
f390d2c7 2794 circuit->snd_stream);
eb5d44eb 2795
2796 /*
2797 * Fill Level 1 or 2 Partial Sequence Numbers header
2798 */
9985f83c 2799 lenp = stream_get_endp (circuit->snd_stream);
f390d2c7 2800 stream_putw (circuit->snd_stream, 0); /* PDU length - when we know it */
eb5d44eb 2801 stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
2802 stream_putc (circuit->snd_stream, circuit->idx);
2803
2804 /*
2805 * And TLVs
2806 */
2807
3f045a08 2808 if (level == IS_LEVEL_1)
eb5d44eb 2809 passwd = &circuit->area->area_passwd;
2810 else
2811 passwd = &circuit->area->domain_passwd;
2812
1cbc562b 2813 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
3f045a08
JB
2814 {
2815 switch (passwd->type)
2816 {
2817 /* Cleartext */
2818 case ISIS_PASSWD_TYPE_CLEARTXT:
2819 if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, passwd->len,
2820 passwd->passwd, circuit->snd_stream))
2821 return ISIS_WARNING;
2822 break;
2823
2824 /* HMAC MD5 */
2825 case ISIS_PASSWD_TYPE_HMAC_MD5:
2826 /* Remember where TLV is written so we can later overwrite the MD5 hash */
2827 auth_tlv_offset = stream_get_endp (circuit->snd_stream);
2828 memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE);
2829 if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE,
2830 hmac_md5_hash, circuit->snd_stream))
2831 return ISIS_WARNING;
2832 break;
2833
2834 default:
2835 break;
f390d2c7 2836 }
3f045a08
JB
2837 }
2838
2839 retval = tlv_add_lsp_entries (lsps, circuit->snd_stream);
2840 if (retval != ISIS_OK)
2841 return retval;
eb5d44eb 2842
f390d2c7 2843 if (isis->debugs & DEBUG_SNP_PACKETS)
2844 {
3fdb2dd9 2845 for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp))
f390d2c7 2846 {
529d65b3 2847 zlog_debug ("ISIS-Snp (%s): PSNP entry %s, seq 0x%08x,"
2848 " cksum 0x%04x, lifetime %us",
2849 circuit->area->area_tag,
2850 rawlspid_print (lsp->lsp_header->lsp_id),
2851 ntohl (lsp->lsp_header->seq_num),
2852 ntohs (lsp->lsp_header->checksum),
2853 ntohs (lsp->lsp_header->rem_lifetime));
f390d2c7 2854 }
eb5d44eb 2855 }
eb5d44eb 2856
9985f83c 2857 length = (u_int16_t) stream_get_endp (circuit->snd_stream);
eb5d44eb 2858 /* Update PDU length */
2859 stream_putw_at (circuit->snd_stream, lenp, length);
2860
3f045a08
JB
2861 /* For HMAC MD5 we need to compute the md5 hash and store it */
2862 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) &&
2863 passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5)
2864 {
2865 hmac_md5 (STREAM_DATA (circuit->snd_stream),
2866 stream_get_endp(circuit->snd_stream),
2867 (unsigned char *) &passwd->passwd, passwd->len,
f2bce9a5 2868 (unsigned char *) &hmac_md5_hash);
3f045a08
JB
2869 /* Copy the hash into the stream */
2870 memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3,
2871 hmac_md5_hash, ISIS_AUTH_MD5_SIZE);
2872 }
2873
eb5d44eb 2874 return ISIS_OK;
2875}
2876
2877/*
2878 * 7.3.15.4 action on expiration of partial SNP interval
2879 * level 1
2880 */
92365889 2881static int
eb5d44eb 2882send_psnp (int level, struct isis_circuit *circuit)
2883{
eb5d44eb 2884 struct isis_lsp *lsp;
2885 struct list *list = NULL;
3fdb2dd9 2886 struct listnode *node;
3f045a08
JB
2887 u_char num_lsps;
2888 int retval = ISIS_OK;
eb5d44eb 2889
3f045a08
JB
2890 if (circuit->circ_type == CIRCUIT_T_BROADCAST &&
2891 circuit->u.bc.is_dr[level - 1])
2892 return ISIS_OK;
eb5d44eb 2893
3f045a08
JB
2894 if (circuit->area->lspdb[level - 1] == NULL ||
2895 dict_count (circuit->area->lspdb[level - 1]) == 0)
2896 return ISIS_OK;
f390d2c7 2897
e38e0df0
SV
2898 if (! circuit->snd_stream)
2899 return ISIS_ERROR;
2900
3f045a08 2901 num_lsps = max_lsps_per_snp (ISIS_SNP_PSNP_FLAG, level, circuit);
f390d2c7 2902
3f045a08
JB
2903 while (1)
2904 {
2905 list = list_new ();
2906 lsp_build_list_ssn (circuit, num_lsps, list,
2907 circuit->area->lspdb[level - 1]);
2908
2909 if (listcount (list) == 0)
2910 {
2911 list_delete (list);
2912 return ISIS_OK;
2913 }
2914
2915 retval = build_psnp (level, circuit, list);
2916 if (retval != ISIS_OK)
2917 {
2918 zlog_err ("ISIS-Snp (%s): Build L%d PSNP on %s failed",
2919 circuit->area->area_tag, level, circuit->interface->name);
2920 list_delete (list);
2921 return retval;
2922 }
f390d2c7 2923
3f045a08
JB
2924 if (isis->debugs & DEBUG_SNP_PACKETS)
2925 {
0232bb72 2926 zlog_debug ("ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
3f045a08
JB
2927 circuit->area->area_tag, level,
2928 circuit->interface->name,
2929 stream_get_endp (circuit->snd_stream));
2930 if (isis->debugs & DEBUG_PACKET_DUMP)
2931 zlog_dump_data (STREAM_DATA (circuit->snd_stream),
2932 stream_get_endp (circuit->snd_stream));
2933 }
2934
2935 retval = circuit->tx (circuit, level);
2936 if (retval != ISIS_OK)
2937 {
2938 zlog_err ("ISIS-Snp (%s): Send L%d PSNP on %s failed",
2939 circuit->area->area_tag, level,
2940 circuit->interface->name);
2941 list_delete (list);
2942 return retval;
2943 }
f390d2c7 2944
3f045a08
JB
2945 /*
2946 * sending succeeded, we can clear SSN flags of this circuit
2947 * for the LSPs in list
2948 */
2949 for (ALL_LIST_ELEMENTS_RO (list, node, lsp))
2950 ISIS_CLEAR_FLAG (lsp->SSNflags, circuit);
2951 list_delete (list);
eb5d44eb 2952 }
eb5d44eb 2953
2954 return retval;
2955}
2956
2957int
2958send_l1_psnp (struct thread *thread)
2959{
2960
2961 struct isis_circuit *circuit;
2962 int retval = ISIS_OK;
2963
2964 circuit = THREAD_ARG (thread);
2965 assert (circuit);
2966
2967 circuit->t_send_psnp[0] = NULL;
2968
2969 send_psnp (1, circuit);
2970 /* set next timer thread */
ffa2c898
QY
2971 thread_add_timer(master, send_l1_psnp, circuit,
2972 isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
2973 &circuit->t_send_psnp[0]);
eb5d44eb 2974
2975 return retval;
2976}
2977
2978/*
2979 * 7.3.15.4 action on expiration of partial SNP interval
2980 * level 2
2981 */
2982int
2983send_l2_psnp (struct thread *thread)
2984{
eb5d44eb 2985 struct isis_circuit *circuit;
2986 int retval = ISIS_OK;
2987
2988 circuit = THREAD_ARG (thread);
2989 assert (circuit);
2990
2991 circuit->t_send_psnp[1] = NULL;
2992
2993 send_psnp (2, circuit);
2994
2995 /* set next timer thread */
ffa2c898
QY
2996 thread_add_timer(master, send_l2_psnp, circuit,
2997 isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
2998 &circuit->t_send_psnp[1]);
eb5d44eb 2999
3000 return retval;
3001}
3002
eb5d44eb 3003/*
3004 * ISO 10589 - 7.3.14.3
3005 */
3006int
3007send_lsp (struct thread *thread)
3008{
3009 struct isis_circuit *circuit;
3010 struct isis_lsp *lsp;
3011 struct listnode *node;
cfd1f27b 3012 int clear_srm = 1;
3f045a08 3013 int retval = ISIS_OK;
eb5d44eb 3014
3015 circuit = THREAD_ARG (thread);
3016 assert (circuit);
3017
cfd1f27b
CF
3018 if (!circuit->lsp_queue)
3019 return ISIS_OK;
eb5d44eb 3020
0fece074
AS
3021 node = listhead (circuit->lsp_queue);
3022
3023 /*
3024 * Handle case where there are no LSPs on the queue. This can
3025 * happen, for instance, if an adjacency goes down before this
3026 * thread gets a chance to run.
3027 */
3028 if (!node)
cfd1f27b 3029 return ISIS_OK;
0fece074 3030
cfd1f27b
CF
3031 /*
3032 * Delete LSP from lsp_queue. If it's still in queue, it is assumed
3033 * as 'transmit pending', but send_lsp may never be called again.
3034 * Retry will happen because SRM flag will not be cleared.
3035 */
0fece074 3036 lsp = listgetdata(node);
cfd1f27b
CF
3037 list_delete_node (circuit->lsp_queue, node);
3038
3039 /* Set the last-cleared time if the queue is empty. */
3040 /* TODO: Is is possible that new lsps keep being added to the queue
3041 * that the queue is never empty? */
3042 if (list_isempty (circuit->lsp_queue))
3043 circuit->lsp_queue_last_cleared = time (NULL);
3044
3045 if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
3046 goto out;
eb5d44eb 3047
3f045a08
JB
3048 /*
3049 * Do not send if levels do not match
3050 */
3051 if (!(lsp->level & circuit->is_type))
cfd1f27b 3052 goto out;
f390d2c7 3053
3f045a08
JB
3054 /*
3055 * Do not send if we do not have adjacencies in state up on the circuit
3056 */
3057 if (circuit->upadjcount[lsp->level - 1] == 0)
cfd1f27b
CF
3058 goto out;
3059
3060 /* stream_copy will assert and stop program execution if LSP is larger than
3061 * the circuit's MTU. So handle and log this case here. */
3062 if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream))
3063 {
3064 zlog_err("ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x,"
3065 " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu"
3066 " while interface stream size is %zu.",
3067 circuit->area->area_tag, lsp->level,
3068 rawlspid_print(lsp->lsp_header->lsp_id),
3069 ntohl(lsp->lsp_header->seq_num),
3070 ntohs(lsp->lsp_header->checksum),
3071 ntohs(lsp->lsp_header->rem_lifetime),
3072 circuit->interface->name,
3073 stream_get_endp(lsp->pdu),
3074 stream_get_size(circuit->snd_stream));
3075 if (isis->debugs & DEBUG_PACKET_DUMP)
3076 zlog_dump_data(STREAM_DATA(lsp->pdu), stream_get_endp(lsp->pdu));
3077 retval = ISIS_ERROR;
3078 goto out;
3f045a08 3079 }
f390d2c7 3080
3f045a08
JB
3081 /* copy our lsp to the send buffer */
3082 stream_copy (circuit->snd_stream, lsp->pdu);
eb5d44eb 3083
3f045a08
JB
3084 if (isis->debugs & DEBUG_UPDATE_PACKETS)
3085 {
3086 zlog_debug
0232bb72 3087 ("ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x,"
3f045a08
JB
3088 " lifetime %us on %s", circuit->area->area_tag, lsp->level,
3089 rawlspid_print (lsp->lsp_header->lsp_id),
3090 ntohl (lsp->lsp_header->seq_num),
3091 ntohs (lsp->lsp_header->checksum),
3092 ntohs (lsp->lsp_header->rem_lifetime),
3093 circuit->interface->name);
3094 if (isis->debugs & DEBUG_PACKET_DUMP)
3095 zlog_dump_data (STREAM_DATA (circuit->snd_stream),
3096 stream_get_endp (circuit->snd_stream));
3097 }
3098
cfd1f27b 3099 clear_srm = 0;
3f045a08
JB
3100 retval = circuit->tx (circuit, lsp->level);
3101 if (retval != ISIS_OK)
3102 {
cfd1f27b 3103 zlog_err ("ISIS-Upd (%s): Send L%d LSP on %s failed %s",
3f045a08 3104 circuit->area->area_tag, lsp->level,
cfd1f27b
CF
3105 circuit->interface->name,
3106 (retval == ISIS_WARNING) ? "temporarily" : "permanently");
3f045a08 3107 }
f390d2c7 3108
cfd1f27b
CF
3109out:
3110 if (clear_srm
3111 || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST)
3112 || (retval != ISIS_OK && retval != ISIS_WARNING))
3113 {
3114 /* SRM flag will trigger retransmission. We will not retransmit if we
3115 * encountered a fatal error.
3116 * On success, they should only be cleared if it's a broadcast circuit.
3117 * On a P2P circuit, we will wait for the ack from the neighbor to clear
3118 * the fag.
3119 */
3120 ISIS_CLEAR_FLAG (lsp->SRMflags, circuit);
3121 }
eb5d44eb 3122
3123 return retval;
f390d2c7 3124}
eb5d44eb 3125
3126int
f390d2c7 3127ack_lsp (struct isis_link_state_hdr *hdr, struct isis_circuit *circuit,
3128 int level)
eb5d44eb 3129{
3130 unsigned long lenp;
3131 int retval;
3132 u_int16_t length;
3133 struct isis_fixed_hdr fixed_hdr;
3134
b20ccb3a 3135 isis_circuit_stream(circuit, &circuit->snd_stream);
eb5d44eb 3136
3f045a08
JB
3137 // fill_llc_hdr (stream);
3138 if (level == IS_LEVEL_1)
f390d2c7 3139 fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM,
3140 circuit->snd_stream);
eb5d44eb 3141 else
f390d2c7 3142 fill_fixed_hdr_andstream (&fixed_hdr, L2_PARTIAL_SEQ_NUM,
3143 circuit->snd_stream);
eb5d44eb 3144
3145
9985f83c 3146 lenp = stream_get_endp (circuit->snd_stream);
f390d2c7 3147 stream_putw (circuit->snd_stream, 0); /* PDU length */
3148 stream_put (circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN);
eb5d44eb 3149 stream_putc (circuit->snd_stream, circuit->idx);
f390d2c7 3150 stream_putc (circuit->snd_stream, 9); /* code */
3151 stream_putc (circuit->snd_stream, 16); /* len */
eb5d44eb 3152
f390d2c7 3153 stream_putw (circuit->snd_stream, ntohs (hdr->rem_lifetime));
3154 stream_put (circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2);
3155 stream_putl (circuit->snd_stream, ntohl (hdr->seq_num));
3156 stream_putw (circuit->snd_stream, ntohs (hdr->checksum));
eb5d44eb 3157
9985f83c 3158 length = (u_int16_t) stream_get_endp (circuit->snd_stream);
eb5d44eb 3159 /* Update PDU length */
3160 stream_putw_at (circuit->snd_stream, lenp, length);
3161
3162 retval = circuit->tx (circuit, level);
3f045a08
JB
3163 if (retval != ISIS_OK)
3164 zlog_err ("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
3165 circuit->area->area_tag, level,
3166 circuit->interface->name);
eb5d44eb 3167
3168 return retval;
3169}