5 * Copyright (C) 2008 Everton da Silva Marques
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include "pim_instance.h"
38 #include "pim_iface.h"
39 #include "pim_hello.h"
40 #include "pim_ifchannel.h"
43 #include "pim_jp_agg.h"
47 static void on_trace(const char *label
, struct interface
*ifp
, pim_addr src
)
49 if (PIM_DEBUG_PIM_TRACE
)
50 zlog_debug("%s: from %pPA on %s", label
, &src
, ifp
->name
);
53 static void recv_join(struct interface
*ifp
, struct pim_neighbor
*neigh
,
54 uint16_t holdtime
, pim_addr upstream
, pim_sgaddr
*sg
,
57 struct pim_interface
*pim_ifp
= NULL
;
59 if (PIM_DEBUG_PIM_TRACE
)
61 "%s: join (S,G)=%pSG rpt=%d wc=%d upstream=%pPAs holdtime=%d from %pPA on %s",
62 __func__
, sg
, !!(source_flags
& PIM_RPT_BIT_MASK
),
63 !!(source_flags
& PIM_WILDCARD_BIT_MASK
), &upstream
,
64 holdtime
, &neigh
->source_addr
, ifp
->name
);
69 ++pim_ifp
->pim_ifstat_join_recv
;
72 * If the RPT and WC are set it's a (*,G)
73 * and the source is the RP
75 if ((source_flags
& PIM_RPT_BIT_MASK
)
76 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
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 ((source_flags
& PIM_RPT_BIT_MASK
)
131 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
133 * RFC 4601 Section 4.5.2:
134 * Received Prune(*,G) messages are processed even if the
135 * RP in the message does not match RP(G).
137 if (PIM_DEBUG_PIM_TRACE
)
138 zlog_debug("%s: Prune received with RP(%pPAs) for %pSG",
139 __func__
, &sg
->src
, sg
);
141 sg
->src
= PIMADDR_ANY
;
144 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
147 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
148 pim_addr src_addr
, uint8_t *tlv_buf
, int tlv_buf_size
)
150 pim_addr msg_upstream_addr
;
151 bool wrong_af
= false;
152 struct pim_interface
*pim_ifp
;
153 uint8_t msg_num_groups
;
154 uint16_t msg_holdtime
;
160 struct pim_ifchannel
*child
= NULL
;
161 struct listnode
*ch_node
, *nch_node
;
164 pastend
= tlv_buf
+ tlv_buf_size
;
167 if (pim_ifp
->pim_passive_enable
) {
168 if (PIM_DEBUG_PIM_PACKETS
)
170 "skip receiving PIM message on passive interface %s",
178 addr_offset
= pim_parse_addr_ucast(&msg_upstream_addr
, buf
,
179 pastend
- buf
, &wrong_af
);
180 if (addr_offset
< 1) {
181 zlog_warn("%s: pim_parse_addr_ucast() failure: from %pPA on %s",
182 __func__
, &src_addr
, ifp
->name
);
188 Check upstream address family
192 "%s: ignoring join/prune directed to unexpected addr family from %pPA on %s",
193 __func__
, &src_addr
, ifp
->name
);
197 remain
= pastend
- buf
;
200 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %pPA on %s",
201 __func__
, remain
, 4, &src_addr
, ifp
->name
);
205 ++buf
; /* skip reserved byte */
206 msg_num_groups
= *(const uint8_t *)buf
;
208 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
212 if (PIM_DEBUG_PIM_J_P
)
214 "%s: join/prune upstream=%pPAs groups=%d holdtime=%d from %pPA on %s",
215 __func__
, &msg_upstream_addr
, msg_num_groups
,
216 msg_holdtime
, &src_addr
, ifp
->name
);
219 for (group
= 0; group
< msg_num_groups
; ++group
) {
221 uint8_t msg_source_flags
;
222 uint16_t msg_num_joined_sources
;
223 uint16_t msg_num_pruned_sources
;
225 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
226 bool filtered
= false;
228 memset(&sg
, 0, sizeof(sg
));
229 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
230 if (addr_offset
< 1) {
235 remain
= pastend
- buf
;
238 "%s: short join/prune buffer for source list: size=%d minimum=%d from %pPA on %s",
239 __func__
, remain
, 4, &src_addr
, ifp
->name
);
243 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
245 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
248 if (PIM_DEBUG_PIM_J_P
)
250 "%s: join/prune upstream=%pPAs group=%pPA/32 join_src=%d prune_src=%d from %pPA on %s",
251 __func__
, &msg_upstream_addr
, &sg
.grp
,
252 msg_num_joined_sources
, msg_num_pruned_sources
,
253 &src_addr
, ifp
->name
);
256 filtered
= pim_is_group_filtered(pim_ifp
, &sg
.grp
);
258 /* Scan joined sources */
259 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
260 addr_offset
= pim_parse_addr_source(
261 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
262 if (addr_offset
< 1) {
268 /* if we are filtering this group, skip the join */
272 recv_join(ifp
, neigh
, msg_holdtime
, msg_upstream_addr
,
273 &sg
, msg_source_flags
);
275 if (pim_addr_is_any(sg
.src
)) {
276 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
278 pim_ifchannel_set_star_g_join_state(
283 /* Scan pruned sources */
284 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
285 addr_offset
= pim_parse_addr_source(
286 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
287 if (addr_offset
< 1) {
293 /* if we are filtering this group, skip the prune */
297 recv_prune(ifp
, neigh
, msg_holdtime
, msg_upstream_addr
,
298 &sg
, msg_source_flags
);
300 * So if we are receiving a S,G,RPT prune
301 * before we have any data for that S,G
302 * We need to retrieve the sg_ch after
303 * we parse the prune.
305 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
310 /* (*,G) prune received */
311 for (ALL_LIST_ELEMENTS(sg_ch
->sources
, ch_node
,
313 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
314 if (child
->ifjoin_state
315 == PIM_IFJOIN_PRUNE_PENDING_TMP
)
317 child
->t_ifjoin_prune_pending_timer
);
319 child
->t_ifjoin_expiry_timer
);
320 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
321 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
322 delete_on_noinfo(child
);
326 /* Received SG-RPT Prune delete oif from specific S,G */
327 if (starg_ch
&& (msg_source_flags
& PIM_RPT_BIT_MASK
)
328 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
329 struct pim_upstream
*up
= sg_ch
->upstream
;
330 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
332 if (PIM_DEBUG_PIM_TRACE
)
334 "%s: SGRpt flag is set, del inherit oif from up %s",
335 __func__
, up
->sg_str
);
336 pim_channel_del_inherited_oif(
343 if (starg_ch
&& !filtered
)
344 pim_ifchannel_set_star_g_join_state(starg_ch
, 1, 0);
354 * While the RFC clearly states that this is 32 bits wide, it
355 * is cheating. These fields:
356 * Encoded-Unicast format (6 bytes MIN)
357 * Encoded-Group format (8 bytes MIN)
358 * Encoded-Source format (8 bytes MIN)
359 * are *not* 32 bits wide.
361 * Nor does the RFC explicitly call out the size for:
363 * Num Groups (1 byte)
365 * Number of Joined Sources (2 bytes)
366 * Number of Pruned Sources (2 bytes)
368 * This leads to a missleading representation from casual
369 * reading and making assumptions. Be careful!
372 * 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
373 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
374 * |PIM Ver| Type | Reserved | Checksum |
375 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376 * | Upstream Neighbor Address (Encoded-Unicast format) |
377 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378 * | Reserved | Num groups | Holdtime |
379 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 * | Multicast Group Address 1 (Encoded-Group format) |
381 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382 * | Number of Joined Sources | Number of Pruned Sources |
383 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 * | Joined Source Address 1 (Encoded-Source format) |
385 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 * | Joined Source Address n (Encoded-Source format) |
390 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391 * | Pruned Source Address 1 (Encoded-Source format) |
392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
396 * | Pruned Source Address n (Encoded-Source format) |
397 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
398 * | Multicast Group Address m (Encoded-Group format) |
399 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
400 * | Number of Joined Sources | Number of Pruned Sources |
401 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
402 * | Joined Source Address 1 (Encoded-Source format) |
403 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
407 * | Joined Source Address n (Encoded-Source format) |
408 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 * | Pruned Source Address 1 (Encoded-Source format) |
410 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
414 * | Pruned Source Address n (Encoded-Source format) |
415 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
417 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
419 struct pim_jp_agg_group
*group
;
420 struct pim_interface
*pim_ifp
= NULL
;
421 struct pim_jp_groups
*grp
= NULL
;
422 struct pim_jp
*msg
= NULL
;
423 struct listnode
*node
, *nnode
;
424 uint8_t pim_msg
[10000];
425 uint8_t *curr_ptr
= pim_msg
;
426 bool new_packet
= true;
427 size_t packet_left
= 0;
428 size_t packet_size
= 0;
429 size_t group_size
= 0;
431 if (rpf
->source_nexthop
.interface
)
432 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
434 zlog_warn("%s: RPF interface is not present", __func__
);
439 on_trace(__func__
, rpf
->source_nexthop
.interface
, rpf
->rpf_addr
);
442 zlog_warn("%s: multicast not enabled on interface %s", __func__
,
443 rpf
->source_nexthop
.interface
->name
);
447 if (pim_addr_is_any(rpf
->rpf_addr
)) {
448 if (PIM_DEBUG_PIM_J_P
)
450 "%s: upstream=%pPA is myself on interface %s",
451 __func__
, &rpf
->rpf_addr
,
452 rpf
->source_nexthop
.interface
->name
);
457 RFC 4601: 4.3.1. Sending Hello Messages
459 Thus, if a router needs to send a Join/Prune or Assert message on
460 an interface on which it has not yet sent a Hello message with the
461 currently configured IP address, then it MUST immediately send the
462 relevant Hello message without waiting for the Hello Timer to
463 expire, followed by the Join/Prune or Assert message.
465 pim_hello_require(rpf
->source_nexthop
.interface
);
467 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
469 msg
= (struct pim_jp
*)pim_msg
;
471 memset(msg
, 0, sizeof(*msg
));
473 pim_msg_addr_encode_ucast((uint8_t *)&msg
->addr
,
476 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
480 grp
= &msg
->groups
[0];
481 curr_ptr
= (uint8_t *)grp
;
482 packet_size
= sizeof(struct pim_msg_header
);
483 packet_size
+= sizeof(pim_encoded_unicast
);
485 4; // reserved (1) + groups (1) + holdtime (2)
487 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
488 packet_left
-= packet_size
;
490 if (PIM_DEBUG_PIM_J_P
)
492 "%s: sending (G)=%pPAs to upstream=%pPA on interface %s",
493 __func__
, &group
->group
, &rpf
->rpf_addr
,
494 rpf
->source_nexthop
.interface
->name
);
496 group_size
= pim_msg_get_jp_group_size(group
->sources
);
497 if (group_size
> packet_left
) {
498 pim_msg_build_header(pim_ifp
->primary_address
,
499 qpim_all_pim_routers_addr
, pim_msg
,
501 PIM_MSG_TYPE_JOIN_PRUNE
, false);
502 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
503 pim_ifp
->primary_address
,
504 qpim_all_pim_routers_addr
, pim_msg
,
506 rpf
->source_nexthop
.interface
)) {
508 "%s: could not send PIM message on interface %s",
510 rpf
->source_nexthop
.interface
->name
);
513 msg
= (struct pim_jp
*)pim_msg
;
514 memset(msg
, 0, sizeof(*msg
));
516 pim_msg_addr_encode_ucast((uint8_t *)&msg
->addr
,
519 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
523 grp
= &msg
->groups
[0];
524 curr_ptr
= (uint8_t *)grp
;
525 packet_size
= sizeof(struct pim_msg_header
);
526 packet_size
+= sizeof(pim_encoded_unicast
);
528 4; // reserved (1) + groups (1) + holdtime (2)
530 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
531 packet_left
-= packet_size
;
539 curr_ptr
+= group_size
;
540 packet_left
-= group_size
;
541 packet_size
+= group_size
;
542 pim_msg_build_jp_groups(grp
, group
, group_size
);
544 if (!pim_ifp
->pim_passive_enable
) {
545 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
546 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
549 if (PIM_DEBUG_PIM_TRACE
)
551 "%s: interface %s num_joins %u num_prunes %u",
552 __func__
, rpf
->source_nexthop
.interface
->name
,
553 ntohs(grp
->joins
), ntohs(grp
->prunes
));
555 grp
= (struct pim_jp_groups
*)curr_ptr
;
556 if (packet_left
< sizeof(struct pim_jp_groups
)
557 || msg
->num_groups
== 255) {
558 pim_msg_build_header(pim_ifp
->primary_address
,
559 qpim_all_pim_routers_addr
, pim_msg
,
561 PIM_MSG_TYPE_JOIN_PRUNE
, false);
562 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
563 pim_ifp
->primary_address
,
564 qpim_all_pim_routers_addr
, pim_msg
,
566 rpf
->source_nexthop
.interface
)) {
568 "%s: could not send PIM message on interface %s",
570 rpf
->source_nexthop
.interface
->name
);
579 // msg->num_groups = htons (msg->num_groups);
580 pim_msg_build_header(
581 pim_ifp
->primary_address
, qpim_all_pim_routers_addr
,
582 pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
, false);
583 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
584 qpim_all_pim_routers_addr
, pim_msg
,
585 packet_size
, rpf
->source_nexthop
.interface
)) {
587 "%s: could not send PIM message on interface %s",
588 __func__
, rpf
->source_nexthop
.interface
->name
);