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