1 // SPDX-License-Identifier: GPL-2.0-or-later
6 * Copyright (C) 2008 Everton da Silva Marques
18 #include "pim_instance.h"
25 #include "pim_iface.h"
26 #include "pim_hello.h"
27 #include "pim_ifchannel.h"
30 #include "pim_jp_agg.h"
34 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
36 if (PIM_DEBUG_PIM_TRACE
)
37 zlog_debug("%s: from %pPA on %s", label
, &src
, ifp
->name
);
40 static void recv_join(struct interface
*ifp
, struct pim_neighbor
*neigh
,
41 uint16_t holdtime
, pim_addr upstream
, pim_sgaddr
*sg
,
44 struct pim_interface
*pim_ifp
= NULL
;
46 if (PIM_DEBUG_PIM_TRACE
)
48 "%s: join (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
49 __func__
, sg
, !!(source_flags
& PIM_RPT_BIT_MASK
),
50 !!(source_flags
& PIM_WILDCARD_BIT_MASK
), &upstream
,
51 holdtime
, &neigh
->source_addr
, ifp
->name
);
56 ++pim_ifp
->pim_ifstat_join_recv
;
59 * If the RPT and WC are set it's a (*,G)
60 * and the source is the RP
62 if (CHECK_FLAG(source_flags
, PIM_WILDCARD_BIT_MASK
)) {
63 /* As per RFC 7761 Section 4.9.1:
64 * The RPT (or Rendezvous Point Tree) bit is a 1-bit value for
65 * use with PIM Join/Prune messages (see Section 4.9.5.1). If
66 * the WC bit is 1, the RPT bit MUST be 1.
68 if (!CHECK_FLAG(source_flags
, PIM_RPT_BIT_MASK
)) {
69 if (PIM_DEBUG_PIM_J_P
)
71 "Discarding (*,G)=%pSG join since WC bit is set but RPT bit is unset",
77 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
81 zlog_warn("%s: Lookup of RP failed for %pSG", __func__
,
86 * If the RP sent in the message is not
87 * our RP for the group, drop the message
89 rpf_addr
= rp
->rpf_addr
;
90 if (pim_addr_cmp(sg
->src
, rpf_addr
)) {
92 "%s: Specified RP(%pPAs) in join is different than our configured RP(%pPAs)",
93 __func__
, &sg
->src
, &rpf_addr
);
97 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
->grp
)) {
99 "%s: Specified Group(%pPA) in join is now in SSM, not allowed to create PIM state",
104 sg
->src
= PIMADDR_ANY
;
107 /* Restart join expiry timer */
108 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
, sg
,
109 source_flags
, holdtime
);
112 static void recv_prune(struct interface
*ifp
, struct pim_neighbor
*neigh
,
113 uint16_t holdtime
, pim_addr upstream
, pim_sgaddr
*sg
,
114 uint8_t source_flags
)
116 struct pim_interface
*pim_ifp
= NULL
;
118 if (PIM_DEBUG_PIM_TRACE
)
120 "%s: prune (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
121 __func__
, sg
, source_flags
& PIM_RPT_BIT_MASK
,
122 source_flags
& PIM_WILDCARD_BIT_MASK
, &upstream
,
123 holdtime
, &neigh
->source_addr
, ifp
->name
);
128 ++pim_ifp
->pim_ifstat_prune_recv
;
130 if (CHECK_FLAG(source_flags
, PIM_WILDCARD_BIT_MASK
)) {
131 /* As per RFC 7761 Section 4.9.1:
132 * The RPT (or Rendezvous Point Tree) bit is a 1-bit value for
133 * use with PIM Join/Prune messages (see Section 4.9.5.1). If
134 * the WC bit is 1, the RPT bit MUST be 1.
136 if (!CHECK_FLAG(source_flags
, PIM_RPT_BIT_MASK
)) {
137 if (PIM_DEBUG_PIM_J_P
)
139 "Discarding (*,G)=%pSG prune since WC bit is set but RPT bit is unset",
146 * RFC 4601 Section 4.5.2:
147 * Received Prune(*,G) messages are processed even if the
148 * RP in the message does not match RP(G).
150 if (PIM_DEBUG_PIM_TRACE
)
151 zlog_debug("%s: Prune received with RP(%pPAs) for %pSG",
152 __func__
, &sg
->src
, sg
);
154 sg
->src
= PIMADDR_ANY
;
157 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
160 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
161 pim_addr src_addr
, uint8_t *tlv_buf
, int tlv_buf_size
)
163 pim_addr msg_upstream_addr
;
164 bool wrong_af
= false;
165 struct pim_interface
*pim_ifp
;
166 uint8_t msg_num_groups
;
167 uint16_t msg_holdtime
;
173 struct pim_ifchannel
*child
= NULL
;
174 struct listnode
*ch_node
, *nch_node
;
177 pastend
= tlv_buf
+ tlv_buf_size
;
180 if (pim_ifp
->pim_passive_enable
) {
181 if (PIM_DEBUG_PIM_PACKETS
)
183 "skip receiving PIM message on passive interface %s",
191 addr_offset
= pim_parse_addr_ucast(&msg_upstream_addr
, buf
,
192 pastend
- buf
, &wrong_af
);
193 if (addr_offset
< 1) {
194 zlog_warn("%s: pim_parse_addr_ucast() failure: from %pPA on %s",
195 __func__
, &src_addr
, ifp
->name
);
201 Check upstream address family
205 "%s: ignoring join/prune directed to unexpected addr family from %pPA on %s",
206 __func__
, &src_addr
, ifp
->name
);
210 remain
= pastend
- buf
;
213 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %pPA on %s",
214 __func__
, remain
, 4, &src_addr
, ifp
->name
);
218 ++buf
; /* skip reserved byte */
219 msg_num_groups
= *(const uint8_t *)buf
;
221 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
225 if (PIM_DEBUG_PIM_J_P
)
227 "%s: join/prune upstream=%pPAs groups=%d holdtime=%d from %pPA on %s",
228 __func__
, &msg_upstream_addr
, msg_num_groups
,
229 msg_holdtime
, &src_addr
, ifp
->name
);
232 for (group
= 0; group
< msg_num_groups
; ++group
) {
234 uint8_t msg_source_flags
;
235 uint16_t msg_num_joined_sources
;
236 uint16_t msg_num_pruned_sources
;
238 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
239 bool filtered
= false;
241 memset(&sg
, 0, sizeof(sg
));
242 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
243 if (addr_offset
< 1) {
248 remain
= pastend
- buf
;
251 "%s: short join/prune buffer for source list: size=%d minimum=%d from %pPA on %s",
252 __func__
, remain
, 4, &src_addr
, ifp
->name
);
256 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
258 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
261 if (PIM_DEBUG_PIM_J_P
)
263 "%s: join/prune upstream=%pPAs group=%pPA/32 join_src=%d prune_src=%d from %pPA on %s",
264 __func__
, &msg_upstream_addr
, &sg
.grp
,
265 msg_num_joined_sources
, msg_num_pruned_sources
,
266 &src_addr
, ifp
->name
);
269 filtered
= pim_is_group_filtered(pim_ifp
, &sg
.grp
);
271 /* Scan joined sources */
272 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
273 addr_offset
= pim_parse_addr_source(
274 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
275 if (addr_offset
< 1) {
281 /* if we are filtering this group, skip the join */
285 recv_join(ifp
, neigh
, msg_holdtime
, msg_upstream_addr
,
286 &sg
, msg_source_flags
);
288 if (pim_addr_is_any(sg
.src
)) {
289 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
291 pim_ifchannel_set_star_g_join_state(
296 /* Scan pruned sources */
297 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
298 addr_offset
= pim_parse_addr_source(
299 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
300 if (addr_offset
< 1) {
306 /* if we are filtering this group, skip the prune */
310 recv_prune(ifp
, neigh
, msg_holdtime
, msg_upstream_addr
,
311 &sg
, msg_source_flags
);
313 * So if we are receiving a S,G,RPT prune
314 * before we have any data for that S,G
315 * We need to retrieve the sg_ch after
316 * we parse the prune.
318 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
323 /* (*,G) prune received */
324 for (ALL_LIST_ELEMENTS(sg_ch
->sources
, ch_node
,
326 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
327 if (child
->ifjoin_state
328 == PIM_IFJOIN_PRUNE_PENDING_TMP
)
330 child
->t_ifjoin_prune_pending_timer
);
332 child
->t_ifjoin_expiry_timer
);
333 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
334 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
335 delete_on_noinfo(child
);
339 /* Received SG-RPT Prune delete oif from specific S,G */
340 if (starg_ch
&& (msg_source_flags
& PIM_RPT_BIT_MASK
)
341 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
342 struct pim_upstream
*up
= sg_ch
->upstream
;
343 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
345 if (PIM_DEBUG_PIM_TRACE
)
347 "%s: SGRpt flag is set, del inherit oif from up %s",
348 __func__
, up
->sg_str
);
349 pim_channel_del_inherited_oif(
356 if (starg_ch
&& !filtered
)
357 pim_ifchannel_set_star_g_join_state(starg_ch
, 1, 0);
367 * While the RFC clearly states that this is 32 bits wide, it
368 * is cheating. These fields:
369 * Encoded-Unicast format (6 bytes MIN)
370 * Encoded-Group format (8 bytes MIN)
371 * Encoded-Source format (8 bytes MIN)
372 * are *not* 32 bits wide.
374 * Nor does the RFC explicitly call out the size for:
376 * Num Groups (1 byte)
378 * Number of Joined Sources (2 bytes)
379 * Number of Pruned Sources (2 bytes)
381 * This leads to a missleading representation from casual
382 * reading and making assumptions. Be careful!
385 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
386 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
387 * |PIM Ver| Type | Reserved | Checksum |
388 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 * | Upstream Neighbor Address (Encoded-Unicast format) |
390 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391 * | Reserved | Num groups | Holdtime |
392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 * | Multicast Group Address 1 (Encoded-Group format) |
394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * | Number of Joined Sources | Number of Pruned Sources |
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * | Joined Source Address 1 (Encoded-Source format) |
398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
402 * | Joined Source Address n (Encoded-Source format) |
403 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
404 * | Pruned Source Address 1 (Encoded-Source format) |
405 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 * | Pruned Source Address n (Encoded-Source format) |
410 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 * | Multicast Group Address m (Encoded-Group format) |
412 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413 * | Number of Joined Sources | Number of Pruned Sources |
414 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
415 * | Joined Source Address 1 (Encoded-Source format) |
416 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
419 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
420 * | Joined Source Address n (Encoded-Source format) |
421 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
422 * | Pruned Source Address 1 (Encoded-Source format) |
423 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
426 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427 * | Pruned Source Address n (Encoded-Source format) |
428 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
430 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
432 struct pim_jp_agg_group
*group
;
433 struct pim_interface
*pim_ifp
= NULL
;
434 struct pim_jp_groups
*grp
= NULL
;
435 struct pim_jp
*msg
= NULL
;
436 struct listnode
*node
, *nnode
;
437 uint8_t pim_msg
[10000];
438 uint8_t *curr_ptr
= pim_msg
;
439 bool new_packet
= true;
440 size_t packet_left
= 0;
441 size_t packet_size
= 0;
442 size_t group_size
= 0;
444 if (rpf
->source_nexthop
.interface
)
445 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
447 zlog_warn("%s: RPF interface is not present", __func__
);
452 on_trace(__func__
, rpf
->source_nexthop
.interface
, rpf
->rpf_addr
);
455 zlog_warn("%s: multicast not enabled on interface %s", __func__
,
456 rpf
->source_nexthop
.interface
->name
);
460 if (pim_addr_is_any(rpf
->rpf_addr
)) {
461 if (PIM_DEBUG_PIM_J_P
)
463 "%s: upstream=%pPA is myself on interface %s",
464 __func__
, &rpf
->rpf_addr
,
465 rpf
->source_nexthop
.interface
->name
);
470 RFC 4601: 4.3.1. Sending Hello Messages
472 Thus, if a router needs to send a Join/Prune or Assert message on
473 an interface on which it has not yet sent a Hello message with the
474 currently configured IP address, then it MUST immediately send the
475 relevant Hello message without waiting for the Hello Timer to
476 expire, followed by the Join/Prune or Assert message.
478 pim_hello_require(rpf
->source_nexthop
.interface
);
480 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
482 msg
= (struct pim_jp
*)pim_msg
;
484 memset(msg
, 0, sizeof(*msg
));
486 pim_msg_addr_encode_ucast((uint8_t *)&msg
->addr
,
489 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
493 grp
= &msg
->groups
[0];
494 curr_ptr
= (uint8_t *)grp
;
495 packet_size
= sizeof(struct pim_msg_header
);
496 packet_size
+= sizeof(pim_encoded_unicast
);
498 4; // reserved (1) + groups (1) + holdtime (2)
500 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
501 packet_left
-= packet_size
;
503 if (PIM_DEBUG_PIM_J_P
)
505 "%s: sending (G)=%pPAs to upstream=%pPA on interface %s",
506 __func__
, &group
->group
, &rpf
->rpf_addr
,
507 rpf
->source_nexthop
.interface
->name
);
509 group_size
= pim_msg_get_jp_group_size(group
->sources
);
510 if (group_size
> packet_left
) {
511 pim_msg_build_header(pim_ifp
->primary_address
,
512 qpim_all_pim_routers_addr
, pim_msg
,
514 PIM_MSG_TYPE_JOIN_PRUNE
, false);
515 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
516 pim_ifp
->primary_address
,
517 qpim_all_pim_routers_addr
, pim_msg
,
519 rpf
->source_nexthop
.interface
)) {
521 "%s: could not send PIM message on interface %s",
523 rpf
->source_nexthop
.interface
->name
);
526 msg
= (struct pim_jp
*)pim_msg
;
527 memset(msg
, 0, sizeof(*msg
));
529 pim_msg_addr_encode_ucast((uint8_t *)&msg
->addr
,
532 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
536 grp
= &msg
->groups
[0];
537 curr_ptr
= (uint8_t *)grp
;
538 packet_size
= sizeof(struct pim_msg_header
);
539 packet_size
+= sizeof(pim_encoded_unicast
);
541 4; // reserved (1) + groups (1) + holdtime (2)
543 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
544 packet_left
-= packet_size
;
552 curr_ptr
+= group_size
;
553 packet_left
-= group_size
;
554 packet_size
+= group_size
;
555 pim_msg_build_jp_groups(grp
, group
, group_size
);
557 if (!pim_ifp
->pim_passive_enable
) {
558 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
559 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
562 if (PIM_DEBUG_PIM_TRACE
)
564 "%s: interface %s num_joins %u num_prunes %u",
565 __func__
, rpf
->source_nexthop
.interface
->name
,
566 ntohs(grp
->joins
), ntohs(grp
->prunes
));
568 grp
= (struct pim_jp_groups
*)curr_ptr
;
569 if (packet_left
< sizeof(struct pim_jp_groups
)
570 || msg
->num_groups
== 255) {
571 pim_msg_build_header(pim_ifp
->primary_address
,
572 qpim_all_pim_routers_addr
, pim_msg
,
574 PIM_MSG_TYPE_JOIN_PRUNE
, false);
575 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
576 pim_ifp
->primary_address
,
577 qpim_all_pim_routers_addr
, pim_msg
,
579 rpf
->source_nexthop
.interface
)) {
581 "%s: could not send PIM message on interface %s",
583 rpf
->source_nexthop
.interface
->name
);
592 // msg->num_groups = htons (msg->num_groups);
593 pim_msg_build_header(
594 pim_ifp
->primary_address
, qpim_all_pim_routers_addr
,
595 pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
, false);
596 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
597 qpim_all_pim_routers_addr
, pim_msg
,
598 packet_size
, rpf
->source_nexthop
.interface
)) {
600 "%s: could not send PIM message on interface %s",
601 __func__
, rpf
->source_nexthop
.interface
->name
);