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