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
37 #include "pim_iface.h"
38 #include "pim_hello.h"
39 #include "pim_ifchannel.h"
42 #include "pim_jp_agg.h"
46 static void on_trace(const char *label
, struct interface
*ifp
,
49 if (PIM_DEBUG_PIM_TRACE
) {
50 char src_str
[INET_ADDRSTRLEN
];
51 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
52 zlog_debug("%s: from %s on %s", label
, src_str
, ifp
->name
);
56 static void recv_join(struct interface
*ifp
, struct pim_neighbor
*neigh
,
57 uint16_t holdtime
, struct in_addr upstream
,
58 pim_sgaddr
*sg
, uint8_t source_flags
)
60 struct pim_interface
*pim_ifp
= NULL
;
62 if (PIM_DEBUG_PIM_TRACE
) {
63 char up_str
[INET_ADDRSTRLEN
];
64 char neigh_str
[INET_ADDRSTRLEN
];
65 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
66 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
68 zlog_debug("%s: join (S,G)=%pSG rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
70 !!(source_flags
& PIM_RPT_BIT_MASK
),
71 !!(source_flags
& PIM_WILDCARD_BIT_MASK
), up_str
,
72 holdtime
, neigh_str
, ifp
->name
);
78 ++pim_ifp
->pim_ifstat_join_recv
;
81 * If the RPT and WC are set it's a (*,G)
82 * and the source is the RP
84 if ((source_flags
& PIM_RPT_BIT_MASK
)
85 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
86 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
89 zlog_warn("%s: Lookup of RP failed for %pSG", __func__
,
94 * If the RP sent in the message is not
95 * our RP for the group, drop the message
97 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
) {
98 char local_rp
[INET_ADDRSTRLEN
];
99 pim_inet4_dump("<local?>", rp
->rpf_addr
.u
.prefix4
,
100 local_rp
, sizeof(local_rp
));
102 "%s: Specified RP(%pPAs) in join is different than our configured RP(%s)",
103 __func__
, &sg
->src
, local_rp
);
107 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
->grp
)) {
109 "%s: Specified Group(%pPA) in join is now in SSM, not allowed to create PIM state",
114 sg
->src
= PIMADDR_ANY
;
117 /* Restart join expiry timer */
118 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
, sg
,
119 source_flags
, holdtime
);
122 static void recv_prune(struct interface
*ifp
, struct pim_neighbor
*neigh
,
123 uint16_t holdtime
, struct in_addr upstream
,
124 pim_sgaddr
*sg
, uint8_t source_flags
)
126 struct pim_interface
*pim_ifp
= NULL
;
128 if (PIM_DEBUG_PIM_TRACE
) {
129 char up_str
[INET_ADDRSTRLEN
];
130 char neigh_str
[INET_ADDRSTRLEN
];
131 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
132 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
134 zlog_debug("%s: prune (S,G)=%pSG rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
136 source_flags
& PIM_RPT_BIT_MASK
,
137 source_flags
& PIM_WILDCARD_BIT_MASK
, up_str
,
139 neigh_str
, ifp
->name
);
145 ++pim_ifp
->pim_ifstat_prune_recv
;
147 if ((source_flags
& PIM_RPT_BIT_MASK
)
148 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
150 * RFC 4601 Section 4.5.2:
151 * Received Prune(*,G) messages are processed even if the
152 * RP in the message does not match RP(G).
154 if (PIM_DEBUG_PIM_TRACE
)
155 zlog_debug("%s: Prune received with RP(%pPAs) for %pSG",
156 __func__
, &sg
->src
, sg
);
158 sg
->src
= PIMADDR_ANY
;
161 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
164 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
165 struct in_addr src_addr
, uint8_t *tlv_buf
,
168 struct prefix msg_upstream_addr
;
169 struct pim_interface
*pim_ifp
;
170 uint8_t msg_num_groups
;
171 uint16_t msg_holdtime
;
177 struct pim_ifchannel
*child
= NULL
;
178 struct listnode
*ch_node
, *nch_node
;
181 pastend
= tlv_buf
+ tlv_buf_size
;
188 pim_parse_addr_ucast(&msg_upstream_addr
, buf
, pastend
- buf
);
189 if (addr_offset
< 1) {
190 char src_str
[INET_ADDRSTRLEN
];
191 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
192 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
193 __func__
, src_str
, ifp
->name
);
199 Check upstream address family
201 if (msg_upstream_addr
.family
!= AF_INET
) {
202 char src_str
[INET_ADDRSTRLEN
];
203 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
205 "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
206 __func__
, msg_upstream_addr
.family
, src_str
, ifp
->name
);
210 remain
= pastend
- buf
;
212 char src_str
[INET_ADDRSTRLEN
];
213 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
215 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
216 __func__
, remain
, 4, src_str
, ifp
->name
);
220 ++buf
; /* skip reserved byte */
221 msg_num_groups
= *(const uint8_t *)buf
;
223 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
227 if (PIM_DEBUG_PIM_J_P
) {
228 char src_str
[INET_ADDRSTRLEN
];
229 char upstream_str
[INET_ADDRSTRLEN
];
230 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
231 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
232 upstream_str
, sizeof(upstream_str
));
234 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
235 __func__
, upstream_str
, msg_num_groups
, msg_holdtime
,
240 for (group
= 0; group
< msg_num_groups
; ++group
) {
242 uint8_t msg_source_flags
;
243 uint16_t msg_num_joined_sources
;
244 uint16_t msg_num_pruned_sources
;
246 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
247 bool filtered
= false;
249 memset(&sg
, 0, sizeof(sg
));
250 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
251 if (addr_offset
< 1) {
256 remain
= pastend
- buf
;
258 char src_str
[INET_ADDRSTRLEN
];
259 pim_inet4_dump("<src?>", src_addr
, src_str
,
262 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
263 __func__
, remain
, 4, src_str
, ifp
->name
);
267 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
269 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
272 if (PIM_DEBUG_PIM_J_P
) {
273 char src_str
[INET_ADDRSTRLEN
];
274 char upstream_str
[INET_ADDRSTRLEN
];
275 pim_inet4_dump("<src?>", src_addr
, src_str
,
277 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
278 upstream_str
, sizeof(upstream_str
));
280 "%s: join/prune upstream=%s group=%pPA/32 join_src=%d prune_src=%d from %s on %s",
281 __func__
, upstream_str
, &sg
.grp
,
282 msg_num_joined_sources
, msg_num_pruned_sources
,
287 filtered
= pim_is_group_filtered(pim_ifp
, &sg
.grp
);
289 /* Scan joined sources */
290 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
291 addr_offset
= pim_parse_addr_source(
292 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
293 if (addr_offset
< 1) {
299 /* if we are filtering this group, skip the join */
303 recv_join(ifp
, neigh
, msg_holdtime
,
304 msg_upstream_addr
.u
.prefix4
, &sg
,
307 if (pim_addr_is_any(sg
.src
)) {
308 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
310 pim_ifchannel_set_star_g_join_state(
315 /* Scan pruned sources */
316 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
317 addr_offset
= pim_parse_addr_source(
318 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
319 if (addr_offset
< 1) {
325 /* if we are filtering this group, skip the prune */
329 recv_prune(ifp
, neigh
, msg_holdtime
,
330 msg_upstream_addr
.u
.prefix4
, &sg
,
333 * So if we are receiving a S,G,RPT prune
334 * before we have any data for that S,G
335 * We need to retrieve the sg_ch after
336 * we parse the prune.
338 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
343 /* (*,G) prune received */
344 for (ALL_LIST_ELEMENTS(sg_ch
->sources
, ch_node
,
346 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
347 if (child
->ifjoin_state
348 == PIM_IFJOIN_PRUNE_PENDING_TMP
)
350 child
->t_ifjoin_prune_pending_timer
);
352 child
->t_ifjoin_expiry_timer
);
353 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
354 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
355 delete_on_noinfo(child
);
359 /* Received SG-RPT Prune delete oif from specific S,G */
360 if (starg_ch
&& (msg_source_flags
& PIM_RPT_BIT_MASK
)
361 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
362 struct pim_upstream
*up
= sg_ch
->upstream
;
363 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
365 if (PIM_DEBUG_PIM_TRACE
)
367 "%s: SGRpt flag is set, del inherit oif from up %s",
368 __func__
, up
->sg_str
);
369 pim_channel_del_inherited_oif(
376 if (starg_ch
&& !filtered
)
377 pim_ifchannel_set_star_g_join_state(starg_ch
, 1, 0);
387 * While the RFC clearly states that this is 32 bits wide, it
388 * is cheating. These fields:
389 * Encoded-Unicast format (6 bytes MIN)
390 * Encoded-Group format (8 bytes MIN)
391 * Encoded-Source format (8 bytes MIN)
392 * are *not* 32 bits wide.
394 * Nor does the RFC explicitly call out the size for:
396 * Num Groups (1 byte)
398 * Number of Joined Sources (2 bytes)
399 * Number of Pruned Sources (2 bytes)
401 * This leads to a missleading representation from casual
402 * reading and making assumptions. Be careful!
405 * 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
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
407 * |PIM Ver| Type | Reserved | Checksum |
408 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 * | Upstream Neighbor Address (Encoded-Unicast format) |
410 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 * | Reserved | Num groups | Holdtime |
412 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413 * | Multicast Group Address 1 (Encoded-Group format) |
414 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
415 * | Number of Joined Sources | Number of Pruned Sources |
416 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
417 * | Joined Source Address 1 (Encoded-Source format) |
418 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
422 * | Joined Source Address n (Encoded-Source format) |
423 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
424 * | Pruned Source Address 1 (Encoded-Source format) |
425 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 * | Pruned Source Address n (Encoded-Source format) |
430 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 * | Multicast Group Address m (Encoded-Group format) |
432 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433 * | Number of Joined Sources | Number of Pruned Sources |
434 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435 * | Joined Source Address 1 (Encoded-Source format) |
436 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
439 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440 * | Joined Source Address n (Encoded-Source format) |
441 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
442 * | Pruned Source Address 1 (Encoded-Source format) |
443 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
446 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
447 * | Pruned Source Address n (Encoded-Source format) |
448 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
450 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
452 struct pim_jp_agg_group
*group
;
453 struct pim_interface
*pim_ifp
= NULL
;
454 struct pim_jp_groups
*grp
= NULL
;
455 struct pim_jp
*msg
= NULL
;
456 struct listnode
*node
, *nnode
;
457 uint8_t pim_msg
[10000];
458 uint8_t *curr_ptr
= pim_msg
;
459 bool new_packet
= true;
460 size_t packet_left
= 0;
461 size_t packet_size
= 0;
462 size_t group_size
= 0;
464 if (rpf
->source_nexthop
.interface
)
465 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
467 zlog_warn("%s: RPF interface is not present", __func__
);
471 on_trace(__func__
, rpf
->source_nexthop
.interface
,
472 rpf
->rpf_addr
.u
.prefix4
);
475 zlog_warn("%s: multicast not enabled on interface %s", __func__
,
476 rpf
->source_nexthop
.interface
->name
);
480 if (rpf
->rpf_addr
.u
.prefix4
.s_addr
== INADDR_ANY
) {
481 if (PIM_DEBUG_PIM_J_P
) {
482 char dst_str
[INET_ADDRSTRLEN
];
483 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
484 dst_str
, sizeof(dst_str
));
485 zlog_debug("%s: upstream=%s is myself on interface %s",
487 rpf
->source_nexthop
.interface
->name
);
493 RFC 4601: 4.3.1. Sending Hello Messages
495 Thus, if a router needs to send a Join/Prune or Assert message on
496 an interface on which it has not yet sent a Hello message with the
497 currently configured IP address, then it MUST immediately send the
498 relevant Hello message without waiting for the Hello Timer to
499 expire, followed by the Join/Prune or Assert message.
501 pim_hello_require(rpf
->source_nexthop
.interface
);
503 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
505 msg
= (struct pim_jp
*)pim_msg
;
507 memset(msg
, 0, sizeof(*msg
));
509 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
510 rpf
->rpf_addr
.u
.prefix4
);
512 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
516 grp
= &msg
->groups
[0];
517 curr_ptr
= (uint8_t *)grp
;
518 packet_size
= sizeof(struct pim_msg_header
);
519 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
521 4; // reserved (1) + groups (1) + holdtime (2)
523 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
524 packet_left
-= packet_size
;
526 if (PIM_DEBUG_PIM_J_P
) {
527 char dst_str
[INET_ADDRSTRLEN
];
528 char grp_str
[INET_ADDRSTRLEN
];
529 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
530 dst_str
, sizeof(dst_str
));
531 pim_inet4_dump("<grp?>", group
->group
, grp_str
,
534 "%s: sending (G)=%s to upstream=%s on interface %s",
535 __func__
, grp_str
, dst_str
,
536 rpf
->source_nexthop
.interface
->name
);
539 group_size
= pim_msg_get_jp_group_size(group
->sources
);
540 if (group_size
> packet_left
) {
541 pim_msg_build_header(pim_msg
, packet_size
,
542 PIM_MSG_TYPE_JOIN_PRUNE
, false);
543 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
544 pim_ifp
->primary_address
,
545 qpim_all_pim_routers_addr
, pim_msg
,
547 rpf
->source_nexthop
.interface
->name
)) {
549 "%s: could not send PIM message on interface %s",
551 rpf
->source_nexthop
.interface
->name
);
554 msg
= (struct pim_jp
*)pim_msg
;
555 memset(msg
, 0, sizeof(*msg
));
557 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
558 rpf
->rpf_addr
.u
.prefix4
);
560 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
564 grp
= &msg
->groups
[0];
565 curr_ptr
= (uint8_t *)grp
;
566 packet_size
= sizeof(struct pim_msg_header
);
567 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
569 4; // reserved (1) + groups (1) + holdtime (2)
571 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
572 packet_left
-= packet_size
;
580 curr_ptr
+= group_size
;
581 packet_left
-= group_size
;
582 packet_size
+= group_size
;
583 pim_msg_build_jp_groups(grp
, group
, group_size
);
585 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
586 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
588 if (PIM_DEBUG_PIM_TRACE
)
590 "%s: interface %s num_joins %u num_prunes %u",
591 __func__
, rpf
->source_nexthop
.interface
->name
,
592 ntohs(grp
->joins
), ntohs(grp
->prunes
));
594 grp
= (struct pim_jp_groups
*)curr_ptr
;
595 if (packet_left
< sizeof(struct pim_jp_groups
)
596 || msg
->num_groups
== 255) {
597 pim_msg_build_header(pim_msg
, packet_size
,
598 PIM_MSG_TYPE_JOIN_PRUNE
, false);
599 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
600 pim_ifp
->primary_address
,
601 qpim_all_pim_routers_addr
, pim_msg
,
603 rpf
->source_nexthop
.interface
->name
)) {
605 "%s: could not send PIM message on interface %s",
607 rpf
->source_nexthop
.interface
->name
);
616 // msg->num_groups = htons (msg->num_groups);
617 pim_msg_build_header(pim_msg
, packet_size
,
618 PIM_MSG_TYPE_JOIN_PRUNE
, false);
619 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
620 qpim_all_pim_routers_addr
, pim_msg
,
622 rpf
->source_nexthop
.interface
->name
)) {
624 "%s: could not send PIM message on interface %s",
625 __func__
, rpf
->source_nexthop
.interface
->name
);