]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_hello.c
Merge pull request #7669 from ranjanyash54/2370
[mirror_frr.git] / pimd / pim_hello.c
CommitLineData
12e41d03 1/*
896014f4
DL
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
12e41d03
DL
19
20#include <zebra.h>
21
22#include "log.h"
744d91b3 23#include "if.h"
12e41d03
DL
24
25#include "pimd.h"
26#include "pim_pim.h"
27#include "pim_str.h"
28#include "pim_tlv.h"
29#include "pim_util.h"
30#include "pim_hello.h"
31#include "pim_iface.h"
32#include "pim_neighbor.h"
33#include "pim_upstream.h"
6bb2ef35 34#include "pim_bsm.h"
12e41d03 35
d62a17ae 36static void on_trace(const char *label, struct interface *ifp,
37 struct in_addr src)
12e41d03 38{
d62a17ae 39 if (PIM_DEBUG_PIM_TRACE) {
40 char src_str[INET_ADDRSTRLEN];
41 pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
42 zlog_debug("%s: from %s on %s", label, src_str, ifp->name);
43 }
12e41d03
DL
44}
45
46static void tlv_trace_bool(const char *label, const char *tlv_name,
47 const char *ifname, struct in_addr src_addr,
48 int isset, int value)
49{
d62a17ae 50 if (isset) {
51 char src_str[INET_ADDRSTRLEN];
52 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
53 zlog_debug(
54 "%s: PIM hello option from %s on interface %s: %s=%d",
55 label, src_str, ifname, tlv_name, value);
56 }
12e41d03
DL
57}
58
59static void tlv_trace_uint16(const char *label, const char *tlv_name,
60 const char *ifname, struct in_addr src_addr,
61 int isset, uint16_t value)
62{
d62a17ae 63 if (isset) {
64 char src_str[INET_ADDRSTRLEN];
65 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
66 zlog_debug(
67 "%s: PIM hello option from %s on interface %s: %s=%u",
68 label, src_str, ifname, tlv_name, value);
69 }
12e41d03
DL
70}
71
72static void tlv_trace_uint32(const char *label, const char *tlv_name,
73 const char *ifname, struct in_addr src_addr,
74 int isset, uint32_t value)
75{
d62a17ae 76 if (isset) {
77 char src_str[INET_ADDRSTRLEN];
78 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
79 zlog_debug(
80 "%s: PIM hello option from %s on interface %s: %s=%u",
81 label, src_str, ifname, tlv_name, value);
82 }
12e41d03
DL
83}
84
85static void tlv_trace_uint32_hex(const char *label, const char *tlv_name,
86 const char *ifname, struct in_addr src_addr,
87 int isset, uint32_t value)
88{
d62a17ae 89 if (isset) {
90 char src_str[INET_ADDRSTRLEN];
91 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
92 zlog_debug(
93 "%s: PIM hello option from %s on interface %s: %s=%08x",
94 label, src_str, ifname, tlv_name, value);
95 }
12e41d03
DL
96}
97
98#if 0
99static void tlv_trace(const char *label, const char *tlv_name,
100 const char *ifname, struct in_addr src_addr,
101 int isset)
102{
103 if (isset) {
eaa54bdb 104 char src_str[INET_ADDRSTRLEN];
12e41d03
DL
105 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
106 zlog_debug("%s: PIM hello option from %s on interface %s: %s",
c258527b 107 label,
12e41d03
DL
108 src_str, ifname,
109 tlv_name);
110 }
111}
112#endif
113
114static void tlv_trace_list(const char *label, const char *tlv_name,
115 const char *ifname, struct in_addr src_addr,
116 int isset, struct list *addr_list)
117{
d62a17ae 118 if (isset) {
119 char src_str[INET_ADDRSTRLEN];
120 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
121 zlog_debug(
122 "%s: PIM hello option from %s on interface %s: %s size=%d list=%p",
123 label, src_str, ifname, tlv_name,
124 addr_list ? ((int)listcount(addr_list)) : -1,
125 (void *)addr_list);
126 }
12e41d03
DL
127}
128
996c9314
LB
129#define FREE_ADDR_LIST \
130 if (hello_option_addr_list) { \
6a154c88 131 list_delete(&hello_option_addr_list); \
d62a17ae 132 }
12e41d03 133
d62a17ae 134#define FREE_ADDR_LIST_THEN_RETURN(code) \
135 { \
136 FREE_ADDR_LIST \
137 return (code); \
138 }
12e41d03 139
d62a17ae 140int pim_hello_recv(struct interface *ifp, struct in_addr src_addr,
12e41d03
DL
141 uint8_t *tlv_buf, int tlv_buf_size)
142{
d62a17ae 143 struct pim_interface *pim_ifp;
144 struct pim_neighbor *neigh;
145 uint8_t *tlv_curr;
146 uint8_t *tlv_pastend;
147 pim_hello_options hello_options =
148 0; /* bit array recording options found */
149 uint16_t hello_option_holdtime = 0;
150 uint16_t hello_option_propagation_delay = 0;
151 uint16_t hello_option_override_interval = 0;
152 uint32_t hello_option_dr_priority = 0;
153 uint32_t hello_option_generation_id = 0;
154 struct list *hello_option_addr_list = 0;
155
156 if (PIM_DEBUG_PIM_HELLO)
15569c58 157 on_trace(__func__, ifp, src_addr);
d62a17ae 158
159 pim_ifp = ifp->info;
160 zassert(pim_ifp);
161
162 ++pim_ifp->pim_ifstat_hello_recv;
163
164 /*
165 Parse PIM hello TLVs
166 */
167 zassert(tlv_buf_size >= 0);
168 tlv_curr = tlv_buf;
169 tlv_pastend = tlv_buf + tlv_buf_size;
170
171 while (tlv_curr < tlv_pastend) {
172 uint16_t option_type;
173 uint16_t option_len;
174 int remain = tlv_pastend - tlv_curr;
175
176 if (remain < PIM_TLV_MIN_SIZE) {
177 if (PIM_DEBUG_PIM_HELLO) {
178 char src_str[INET_ADDRSTRLEN];
179 pim_inet4_dump("<src?>", src_addr, src_str,
180 sizeof(src_str));
181 zlog_debug(
182 "%s: short PIM hello TLV size=%d < min=%d from %s on interface %s",
15569c58
DA
183 __func__, remain, PIM_TLV_MIN_SIZE,
184 src_str, ifp->name);
d62a17ae 185 }
186 FREE_ADDR_LIST_THEN_RETURN(-1);
187 }
188
189 option_type = PIM_TLV_GET_TYPE(tlv_curr);
190 tlv_curr += PIM_TLV_TYPE_SIZE;
191 option_len = PIM_TLV_GET_LENGTH(tlv_curr);
192 tlv_curr += PIM_TLV_LENGTH_SIZE;
193
194 if ((tlv_curr + option_len) > tlv_pastend) {
195 if (PIM_DEBUG_PIM_HELLO) {
196 char src_str[INET_ADDRSTRLEN];
197 pim_inet4_dump("<src?>", src_addr, src_str,
198 sizeof(src_str));
199 zlog_debug(
200 "%s: long PIM hello TLV type=%d length=%d > left=%td from %s on interface %s",
15569c58
DA
201 __func__, option_type, option_len,
202 tlv_pastend - tlv_curr, src_str,
203 ifp->name);
d62a17ae 204 }
205 FREE_ADDR_LIST_THEN_RETURN(-2);
206 }
207
208 if (PIM_DEBUG_PIM_HELLO) {
209 char src_str[INET_ADDRSTRLEN];
210 pim_inet4_dump("<src?>", src_addr, src_str,
211 sizeof(src_str));
212 zlog_debug(
213 "%s: parse left_size=%d: PIM hello TLV type=%d length=%d from %s on %s",
15569c58
DA
214 __func__, remain, option_type, option_len,
215 src_str, ifp->name);
d62a17ae 216 }
217
218 switch (option_type) {
219 case PIM_MSG_OPTION_TYPE_HOLDTIME:
220 if (pim_tlv_parse_holdtime(ifp->name, src_addr,
221 &hello_options,
222 &hello_option_holdtime,
223 option_len, tlv_curr)) {
224 FREE_ADDR_LIST_THEN_RETURN(-3);
225 }
226 break;
227 case PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY:
228 if (pim_tlv_parse_lan_prune_delay(
229 ifp->name, src_addr, &hello_options,
230 &hello_option_propagation_delay,
231 &hello_option_override_interval, option_len,
12e41d03 232 tlv_curr)) {
d62a17ae 233 FREE_ADDR_LIST_THEN_RETURN(-4);
234 }
235 break;
236 case PIM_MSG_OPTION_TYPE_DR_PRIORITY:
237 if (pim_tlv_parse_dr_priority(ifp->name, src_addr,
238 &hello_options,
239 &hello_option_dr_priority,
240 option_len, tlv_curr)) {
241 FREE_ADDR_LIST_THEN_RETURN(-5);
242 }
243 break;
244 case PIM_MSG_OPTION_TYPE_GENERATION_ID:
245 if (pim_tlv_parse_generation_id(
246 ifp->name, src_addr, &hello_options,
247 &hello_option_generation_id, option_len,
248 tlv_curr)) {
249 FREE_ADDR_LIST_THEN_RETURN(-6);
250 }
251 break;
252 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST:
253 if (pim_tlv_parse_addr_list(ifp->name, src_addr,
254 &hello_options,
255 &hello_option_addr_list,
256 option_len, tlv_curr)) {
257 return -7;
258 }
259 break;
260 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH:
261 if (PIM_DEBUG_PIM_HELLO) {
262 char src_str[INET_ADDRSTRLEN];
263 pim_inet4_dump("<src?>", src_addr, src_str,
264 sizeof(src_str));
265 zlog_debug(
266 "%s: ignoring PIM hello dense-mode state refresh TLV option type=%d length=%d from %s on interface %s",
15569c58
DA
267 __func__, option_type, option_len,
268 src_str, ifp->name);
d62a17ae 269 }
270 break;
271 default:
272 if (PIM_DEBUG_PIM_HELLO) {
273 char src_str[INET_ADDRSTRLEN];
274 pim_inet4_dump("<src?>", src_addr, src_str,
275 sizeof(src_str));
276 zlog_debug(
277 "%s: ignoring unknown PIM hello TLV type=%d length=%d from %s on interface %s",
15569c58
DA
278 __func__, option_type, option_len,
279 src_str, ifp->name);
d62a17ae 280 }
281 }
282
283 tlv_curr += option_len;
284 }
12e41d03 285
d62a17ae 286 /*
287 Check received PIM hello options
288 */
12e41d03 289
d62a17ae 290 if (PIM_DEBUG_PIM_HELLO) {
15569c58 291 tlv_trace_uint16(__func__, "holdtime", ifp->name, src_addr,
d62a17ae 292 PIM_OPTION_IS_SET(hello_options,
293 PIM_OPTION_MASK_HOLDTIME),
294 hello_option_holdtime);
295 tlv_trace_uint16(
15569c58 296 __func__, "propagation_delay", ifp->name, src_addr,
d62a17ae 297 PIM_OPTION_IS_SET(hello_options,
298 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
299 hello_option_propagation_delay);
300 tlv_trace_uint16(
15569c58 301 __func__, "override_interval", ifp->name, src_addr,
d62a17ae 302 PIM_OPTION_IS_SET(hello_options,
303 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
304 hello_option_override_interval);
305 tlv_trace_bool(
15569c58
DA
306 __func__, "can_disable_join_suppression", ifp->name,
307 src_addr,
d62a17ae 308 PIM_OPTION_IS_SET(hello_options,
309 PIM_OPTION_MASK_LAN_PRUNE_DELAY),
310 PIM_OPTION_IS_SET(
311 hello_options,
312 PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION));
15569c58 313 tlv_trace_uint32(__func__, "dr_priority", ifp->name, src_addr,
d62a17ae 314 PIM_OPTION_IS_SET(hello_options,
315 PIM_OPTION_MASK_DR_PRIORITY),
316 hello_option_dr_priority);
317 tlv_trace_uint32_hex(
15569c58 318 __func__, "generation_id", ifp->name, src_addr,
d62a17ae 319 PIM_OPTION_IS_SET(hello_options,
320 PIM_OPTION_MASK_GENERATION_ID),
321 hello_option_generation_id);
15569c58 322 tlv_trace_list(__func__, "address_list", ifp->name, src_addr,
d62a17ae 323 PIM_OPTION_IS_SET(hello_options,
324 PIM_OPTION_MASK_ADDRESS_LIST),
325 hello_option_addr_list);
326 }
12e41d03 327
d62a17ae 328 if (!PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_HOLDTIME)) {
329 if (PIM_DEBUG_PIM_HELLO) {
330 char src_str[INET_ADDRSTRLEN];
331 pim_inet4_dump("<src?>", src_addr, src_str,
332 sizeof(src_str));
333 zlog_debug(
334 "%s: PIM hello missing holdtime from %s on interface %s",
15569c58 335 __func__, src_str, ifp->name);
d62a17ae 336 }
337 }
12e41d03 338
d62a17ae 339 /*
340 New neighbor?
341 */
342
343 neigh = pim_neighbor_find(ifp, src_addr);
344 if (!neigh) {
345 /* Add as new neighbor */
346
347 neigh = pim_neighbor_add(
348 ifp, src_addr, hello_options, hello_option_holdtime,
349 hello_option_propagation_delay,
350 hello_option_override_interval,
351 hello_option_dr_priority, hello_option_generation_id,
352 hello_option_addr_list, PIM_NEIGHBOR_SEND_DELAY);
353 if (!neigh) {
354 if (PIM_DEBUG_PIM_HELLO) {
355 char src_str[INET_ADDRSTRLEN];
356 pim_inet4_dump("<src?>", src_addr, src_str,
357 sizeof(src_str));
358 zlog_warn(
359 "%s: failure creating PIM neighbor %s on interface %s",
15569c58 360 __func__, src_str, ifp->name);
d62a17ae 361 }
362 FREE_ADDR_LIST_THEN_RETURN(-8);
363 }
6bb2ef35 364 /* Forward BSM if required */
365 if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
366 if (PIM_DEBUG_PIM_HELLO)
15569c58
DA
367 zlog_debug(
368 "%s: forwarding bsm to new nbr failed",
369 __func__);
6bb2ef35 370 }
d62a17ae 371
372 /* actual addr list has been saved under neighbor */
373 return 0;
aea6cb94 374 }
d62a17ae 375
376 /*
377 Received generation ID ?
378 */
379
380 if (PIM_OPTION_IS_SET(hello_options, PIM_OPTION_MASK_GENERATION_ID)) {
381 /* GenID mismatch ? */
382 if (!PIM_OPTION_IS_SET(neigh->hello_options,
383 PIM_OPTION_MASK_GENERATION_ID)
384 || (hello_option_generation_id != neigh->generation_id)) {
385 /* GenID mismatch, then replace neighbor */
386
387 if (PIM_DEBUG_PIM_HELLO) {
388 char src_str[INET_ADDRSTRLEN];
389 pim_inet4_dump("<src?>", src_addr, src_str,
390 sizeof(src_str));
391 zlog_debug(
392 "%s: GenId mismatch new=%08x old=%08x: replacing neighbor %s on %s",
15569c58 393 __func__, hello_option_generation_id,
d62a17ae 394 neigh->generation_id, src_str,
395 ifp->name);
396 }
397
9b29ea95
DS
398 pim_upstream_rpf_genid_changed(pim_ifp->pim,
399 neigh->source_addr);
d62a17ae 400
401 pim_neighbor_delete(ifp, neigh, "GenID mismatch");
402 neigh = pim_neighbor_add(ifp, src_addr, hello_options,
403 hello_option_holdtime,
404 hello_option_propagation_delay,
405 hello_option_override_interval,
406 hello_option_dr_priority,
407 hello_option_generation_id,
408 hello_option_addr_list,
409 PIM_NEIGHBOR_SEND_NOW);
410 if (!neigh) {
411 if (PIM_DEBUG_PIM_HELLO) {
412 char src_str[INET_ADDRSTRLEN];
413 pim_inet4_dump("<src?>", src_addr,
414 src_str,
415 sizeof(src_str));
416 zlog_debug(
417 "%s: failure re-creating PIM neighbor %s on interface %s",
15569c58 418 __func__, src_str, ifp->name);
d62a17ae 419 }
420 FREE_ADDR_LIST_THEN_RETURN(-9);
421 }
6bb2ef35 422 /* Forward BSM if required */
423 if (!pim_bsm_new_nbr_fwd(neigh, ifp)) {
424 if (PIM_DEBUG_PIM_HELLO)
15569c58
DA
425 zlog_debug(
426 "%s: forwarding bsm to new nbr failed",
427 __func__);
6bb2ef35 428 }
d62a17ae 429 /* actual addr list is saved under neighbor */
430 return 0;
431
432 } /* GenId mismatch: replace neighbor */
433
434 } /* GenId received */
435
436 /*
437 Update existing neighbor
438 */
439
440 pim_neighbor_update(neigh, hello_options, hello_option_holdtime,
441 hello_option_dr_priority, hello_option_addr_list);
442 /* actual addr list is saved under neighbor */
443 return 0;
12e41d03
DL
444}
445
d62a17ae 446int pim_hello_build_tlv(struct interface *ifp, uint8_t *tlv_buf,
447 int tlv_buf_size, uint16_t holdtime,
448 uint32_t dr_priority, uint32_t generation_id,
449 uint16_t propagation_delay, uint16_t override_interval,
dba78609 450 int can_disable_join_suppression)
12e41d03 451{
d62a17ae 452 uint8_t *curr = tlv_buf;
453 uint8_t *pastend = tlv_buf + tlv_buf_size;
454 uint8_t *tmp;
88a3f5e1
DS
455 struct pim_interface *pim_ifp = ifp->info;
456 struct pim_instance *pim = pim_ifp->pim;
d62a17ae 457
458 /*
459 * Append options
460 */
461
462 /* Holdtime */
463 curr = pim_tlv_append_uint16(curr, pastend,
464 PIM_MSG_OPTION_TYPE_HOLDTIME, holdtime);
465 if (!curr) {
466 if (PIM_DEBUG_PIM_HELLO) {
467 zlog_debug(
468 "%s: could not set PIM hello Holdtime option for interface %s",
15569c58 469 __func__, ifp->name);
d62a17ae 470 }
471 return -1;
472 }
12e41d03 473
d62a17ae 474 /* LAN Prune Delay */
475 tmp = pim_tlv_append_2uint16(curr, pastend,
476 PIM_MSG_OPTION_TYPE_LAN_PRUNE_DELAY,
477 propagation_delay, override_interval);
478 if (!tmp) {
479 if (PIM_DEBUG_PIM_HELLO) {
480 zlog_debug(
481 "%s: could not set PIM LAN Prune Delay option for interface %s",
15569c58 482 __func__, ifp->name);
d62a17ae 483 }
484 return -1;
485 }
486 if (can_disable_join_suppression) {
c4efd0f4 487 *(curr + 4) |= 0x80; /* enable T bit */
d62a17ae 488 }
489 curr = tmp;
490
491 /* DR Priority */
492 curr = pim_tlv_append_uint32(
493 curr, pastend, PIM_MSG_OPTION_TYPE_DR_PRIORITY, dr_priority);
494 if (!curr) {
495 if (PIM_DEBUG_PIM_HELLO) {
496 zlog_debug(
497 "%s: could not set PIM hello DR Priority option for interface %s",
15569c58 498 __func__, ifp->name);
d62a17ae 499 }
500 return -2;
501 }
12e41d03 502
d62a17ae 503 /* Generation ID */
504 curr = pim_tlv_append_uint32(curr, pastend,
505 PIM_MSG_OPTION_TYPE_GENERATION_ID,
506 generation_id);
507 if (!curr) {
508 if (PIM_DEBUG_PIM_HELLO) {
509 zlog_debug(
510 "%s: could not set PIM hello Generation ID option for interface %s",
15569c58 511 __func__, ifp->name);
d62a17ae 512 }
513 return -3;
514 }
12e41d03 515
d62a17ae 516 /* Secondary Address List */
517 if (ifp->connected->count) {
518 curr = pim_tlv_append_addrlist_ucast(curr, pastend,
519 ifp->connected, AF_INET);
520 if (!curr) {
521 if (PIM_DEBUG_PIM_HELLO) {
522 zlog_debug(
523 "%s: could not set PIM hello v4 Secondary Address List option for interface %s",
15569c58 524 __func__, ifp->name);
d62a17ae 525 }
526 return -4;
527 }
88a3f5e1 528 if (pim->send_v6_secondary) {
d62a17ae 529 curr = pim_tlv_append_addrlist_ucast(
530 curr, pastend, ifp->connected, AF_INET6);
531 if (!curr) {
532 if (PIM_DEBUG_PIM_HELLO) {
533 zlog_debug(
534 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
15569c58 535 __func__, ifp->name);
d62a17ae 536 }
537 return -4;
538 }
539 }
540 }
12e41d03 541
d62a17ae 542 return curr - tlv_buf;
12e41d03
DL
543}
544
545/*
546 RFC 4601: 4.3.1. Sending Hello Messages
547
548 Thus, if a router needs to send a Join/Prune or Assert message on an
549 interface on which it has not yet sent a Hello message with the
550 currently configured IP address, then it MUST immediately send the
551 relevant Hello message without waiting for the Hello Timer to
552 expire, followed by the Join/Prune or Assert message.
553*/
554void pim_hello_require(struct interface *ifp)
555{
d62a17ae 556 struct pim_interface *pim_ifp;
12e41d03 557
d62a17ae 558 zassert(ifp);
12e41d03 559
d62a17ae 560 pim_ifp = ifp->info;
12e41d03 561
d62a17ae 562 zassert(pim_ifp);
12e41d03 563
d62a17ae 564 if (pim_ifp->pim_ifstat_hello_sent)
565 return;
12e41d03 566
d62a17ae 567 pim_hello_restart_now(ifp); /* Send hello and restart timer */
12e41d03 568}