1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
13 #include "pim_instance.h"
18 #include "pim_hello.h"
19 #include "pim_iface.h"
20 #include "pim_neighbor.h"
21 #include "pim_upstream.h"
24 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
26 if (PIM_DEBUG_PIM_TRACE
)
27 zlog_debug("%s: from %pPAs on %s", label
, &src
, ifp
->name
);
30 static void tlv_trace_bool(const char *label
, const char *tlv_name
,
31 const char *ifname
, pim_addr src_addr
, int isset
,
36 "%s: PIM hello option from %pPAs on interface %s: %s=%d",
37 label
, &src_addr
, ifname
, tlv_name
, value
);
40 static void tlv_trace_uint16(const char *label
, const char *tlv_name
,
41 const char *ifname
, pim_addr src_addr
, int isset
,
46 "%s: PIM hello option from %pPAs on interface %s: %s=%u",
47 label
, &src_addr
, ifname
, tlv_name
, value
);
50 static void tlv_trace_uint32(const char *label
, const char *tlv_name
,
51 const char *ifname
, pim_addr src_addr
, int isset
,
56 "%s: PIM hello option from %pPAs on interface %s: %s=%u",
57 label
, &src_addr
, ifname
, tlv_name
, value
);
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
)
66 "%s: PIM hello option from %pPAs on interface %s: %s=%08x",
67 label
, &src_addr
, ifname
, tlv_name
, value
);
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
)
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,
82 #define FREE_ADDR_LIST \
83 if (hello_option_addr_list) { \
84 list_delete(&hello_option_addr_list); \
87 #define FREE_ADDR_LIST_THEN_RETURN(code) \
93 int pim_hello_recv(struct interface
*ifp
, pim_addr src_addr
, uint8_t *tlv_buf
,
96 struct pim_interface
*pim_ifp
;
97 struct pim_neighbor
*neigh
;
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;
109 if (PIM_DEBUG_PIM_HELLO
)
110 on_trace(__func__
, ifp
, src_addr
);
115 if (pim_ifp
->pim_passive_enable
) {
116 if (PIM_DEBUG_PIM_PACKETS
)
118 "skip receiving PIM message on passive interface %s",
123 ++pim_ifp
->pim_ifstat_hello_recv
;
128 assert(tlv_buf_size
>= 0);
130 tlv_pastend
= tlv_buf
+ tlv_buf_size
;
132 while (tlv_curr
< tlv_pastend
) {
133 uint16_t option_type
;
135 int remain
= tlv_pastend
- tlv_curr
;
137 if (remain
< PIM_TLV_MIN_SIZE
) {
138 if (PIM_DEBUG_PIM_HELLO
)
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);
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
;
151 if ((tlv_curr
+ option_len
) > tlv_pastend
) {
152 if (PIM_DEBUG_PIM_HELLO
)
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
,
158 FREE_ADDR_LIST_THEN_RETURN(-2);
161 if (PIM_DEBUG_PIM_HELLO
)
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
);
167 switch (option_type
) {
168 case PIM_MSG_OPTION_TYPE_HOLDTIME
:
169 if (pim_tlv_parse_holdtime(ifp
->name
, src_addr
,
171 &hello_option_holdtime
,
172 option_len
, tlv_curr
)) {
173 FREE_ADDR_LIST_THEN_RETURN(-3);
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
,
182 FREE_ADDR_LIST_THEN_RETURN(-4);
185 case PIM_MSG_OPTION_TYPE_DR_PRIORITY
:
186 if (pim_tlv_parse_dr_priority(ifp
->name
, src_addr
,
188 &hello_option_dr_priority
,
189 option_len
, tlv_curr
)) {
190 FREE_ADDR_LIST_THEN_RETURN(-5);
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
,
198 FREE_ADDR_LIST_THEN_RETURN(-6);
201 case PIM_MSG_OPTION_TYPE_ADDRESS_LIST
:
202 if (pim_tlv_parse_addr_list(ifp
->name
, src_addr
,
204 &hello_option_addr_list
,
205 option_len
, tlv_curr
)) {
209 case PIM_MSG_OPTION_TYPE_DM_STATE_REFRESH
:
210 if (PIM_DEBUG_PIM_HELLO
)
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
);
217 if (PIM_DEBUG_PIM_HELLO
)
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
);
224 tlv_curr
+= option_len
;
228 Check received PIM hello options
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
);
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
);
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
);
247 __func__
, "can_disable_join_suppression", ifp
->name
,
249 PIM_OPTION_IS_SET(hello_options
,
250 PIM_OPTION_MASK_LAN_PRUNE_DELAY
),
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
);
269 if (!PIM_OPTION_IS_SET(hello_options
, PIM_OPTION_MASK_HOLDTIME
)) {
270 if (PIM_DEBUG_PIM_HELLO
)
272 "%s: PIM hello missing holdtime from %pPAs on interface %s",
273 __func__
, &src_addr
, ifp
->name
);
280 neigh
= pim_neighbor_find(ifp
, src_addr
);
282 /* Add as new neighbor */
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
);
291 if (PIM_DEBUG_PIM_HELLO
)
293 "%s: failure creating PIM neighbor %pPAs on interface %s",
294 __func__
, &src_addr
, ifp
->name
);
295 FREE_ADDR_LIST_THEN_RETURN(-8);
297 /* Forward BSM if required */
298 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
299 if (PIM_DEBUG_PIM_HELLO
)
301 "%s: forwarding bsm to new nbr failed",
305 /* actual addr list has been saved under neighbor */
310 Received generation ID ?
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 */
320 if (PIM_DEBUG_PIM_HELLO
)
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
,
327 pim_upstream_rpf_genid_changed(pim_ifp
->pim
,
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
);
340 if (PIM_DEBUG_PIM_HELLO
)
342 "%s: failure re-creating PIM neighbor %pPAs on interface %s",
343 __func__
, &src_addr
, ifp
->name
);
344 FREE_ADDR_LIST_THEN_RETURN(-9);
346 /* Forward BSM if required */
347 if (!pim_bsm_new_nbr_fwd(neigh
, ifp
)) {
348 if (PIM_DEBUG_PIM_HELLO
)
350 "%s: forwarding bsm to new nbr failed",
353 /* actual addr list is saved under neighbor */
356 } /* GenId mismatch: replace neighbor */
358 } /* GenId received */
361 Update existing neighbor
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 */
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
)
376 uint8_t *curr
= tlv_buf
;
377 uint8_t *pastend
= tlv_buf
+ tlv_buf_size
;
380 struct pim_interface
*pim_ifp
= ifp
->info
;
381 struct pim_instance
*pim
= pim_ifp
->pim
;
389 curr
= pim_tlv_append_uint16(curr
, pastend
,
390 PIM_MSG_OPTION_TYPE_HOLDTIME
, holdtime
);
392 if (PIM_DEBUG_PIM_HELLO
) {
394 "%s: could not set PIM hello Holdtime option for interface %s",
395 __func__
, ifp
->name
);
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
);
405 if (PIM_DEBUG_PIM_HELLO
) {
407 "%s: could not set PIM LAN Prune Delay option for interface %s",
408 __func__
, ifp
->name
);
412 if (can_disable_join_suppression
) {
413 *(curr
+ 4) |= 0x80; /* enable T bit */
418 curr
= pim_tlv_append_uint32(
419 curr
, pastend
, PIM_MSG_OPTION_TYPE_DR_PRIORITY
, dr_priority
);
421 if (PIM_DEBUG_PIM_HELLO
) {
423 "%s: could not set PIM hello DR Priority option for interface %s",
424 __func__
, ifp
->name
);
430 curr
= pim_tlv_append_uint32(curr
, pastend
,
431 PIM_MSG_OPTION_TYPE_GENERATION_ID
,
434 if (PIM_DEBUG_PIM_HELLO
) {
436 "%s: could not set PIM hello Generation ID option for interface %s",
437 __func__
, ifp
->name
);
442 /* Secondary Address List */
443 if (ifp
->connected
->count
) {
444 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
, ifp
,
447 if (PIM_DEBUG_PIM_HELLO
) {
449 "%s: could not set PIM hello %s Secondary Address List option for interface %s",
450 __func__
, PIM_AF_NAME
, ifp
->name
);
455 if (pim
->send_v6_secondary
) {
456 curr
= pim_tlv_append_addrlist_ucast(curr
, pastend
, ifp
,
459 if (PIM_DEBUG_PIM_HELLO
) {
461 "%s: could not sent PIM hello v6 secondary Address List option for interface %s",
462 __func__
, ifp
->name
);
470 return curr
- tlv_buf
;
474 RFC 4601: 4.3.1. Sending Hello Messages
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.
482 void pim_hello_require(struct interface
*ifp
)
484 struct pim_interface
*pim_ifp
;
492 if (PIM_IF_FLAG_TEST_HELLO_SENT(pim_ifp
->flags
))
495 pim_hello_restart_now(ifp
); /* Send hello and restart timer */