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 struct prefix_sg
*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
,
69 "%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
70 __func__
, pim_str_sg_dump(sg
),
71 !!(source_flags
& PIM_RPT_BIT_MASK
),
72 !!(source_flags
& PIM_WILDCARD_BIT_MASK
), up_str
,
73 holdtime
, neigh_str
, ifp
->name
);
79 ++pim_ifp
->pim_ifstat_join_recv
;
82 * If the RPT and WC are set it's a (*,G)
83 * and the source is the RP
85 if ((source_flags
& PIM_RPT_BIT_MASK
)
86 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
87 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
90 zlog_warn("%s: Lookup of RP failed for %pSG4", __func__
,
95 * If the RP sent in the message is not
96 * our RP for the group, drop the message
98 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
) {
99 char received_rp
[INET_ADDRSTRLEN
];
100 char local_rp
[INET_ADDRSTRLEN
];
101 pim_inet4_dump("<received?>", sg
->src
, received_rp
,
102 sizeof(received_rp
));
103 pim_inet4_dump("<local?>", rp
->rpf_addr
.u
.prefix4
,
104 local_rp
, sizeof(local_rp
));
106 "%s: Specified RP(%s) in join is different than our configured RP(%s)",
107 __func__
, received_rp
, local_rp
);
111 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
->grp
)) {
113 "%s: Specified Group(%pI4) in join is now in SSM, not allowed to create PIM state",
118 sg
->src
.s_addr
= INADDR_ANY
;
121 /* Restart join expiry timer */
122 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
, sg
,
123 source_flags
, holdtime
);
126 static void recv_prune(struct interface
*ifp
, struct pim_neighbor
*neigh
,
127 uint16_t holdtime
, struct in_addr upstream
,
128 struct prefix_sg
*sg
, uint8_t source_flags
)
130 struct pim_interface
*pim_ifp
= NULL
;
132 if (PIM_DEBUG_PIM_TRACE
) {
133 char up_str
[INET_ADDRSTRLEN
];
134 char neigh_str
[INET_ADDRSTRLEN
];
135 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
136 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
139 "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
140 __func__
, pim_str_sg_dump(sg
),
141 source_flags
& PIM_RPT_BIT_MASK
,
142 source_flags
& PIM_WILDCARD_BIT_MASK
, up_str
, holdtime
,
143 neigh_str
, ifp
->name
);
149 ++pim_ifp
->pim_ifstat_prune_recv
;
151 if ((source_flags
& PIM_RPT_BIT_MASK
)
152 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
154 * RFC 4601 Section 4.5.2:
155 * Received Prune(*,G) messages are processed even if the
156 * RP in the message does not match RP(G).
158 if (PIM_DEBUG_PIM_TRACE
) {
159 char received_rp
[INET_ADDRSTRLEN
];
161 pim_inet4_dump("<received?>", sg
->src
, received_rp
,
162 sizeof(received_rp
));
163 zlog_debug("%s: Prune received with RP(%s) for %pSG4",
164 __func__
, received_rp
, sg
);
167 sg
->src
.s_addr
= INADDR_ANY
;
170 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
173 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
174 struct in_addr src_addr
, uint8_t *tlv_buf
,
177 struct prefix msg_upstream_addr
;
178 struct pim_interface
*pim_ifp
;
179 uint8_t msg_num_groups
;
180 uint16_t msg_holdtime
;
186 struct pim_ifchannel
*child
= NULL
;
187 struct listnode
*ch_node
, *nch_node
;
190 pastend
= tlv_buf
+ tlv_buf_size
;
197 pim_parse_addr_ucast(&msg_upstream_addr
, buf
, pastend
- buf
);
198 if (addr_offset
< 1) {
199 char src_str
[INET_ADDRSTRLEN
];
200 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
201 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
202 __func__
, src_str
, ifp
->name
);
208 Check upstream address family
210 if (msg_upstream_addr
.family
!= AF_INET
) {
211 char src_str
[INET_ADDRSTRLEN
];
212 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
214 "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
215 __func__
, msg_upstream_addr
.family
, src_str
, ifp
->name
);
219 remain
= pastend
- buf
;
221 char src_str
[INET_ADDRSTRLEN
];
222 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
224 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
225 __func__
, remain
, 4, src_str
, ifp
->name
);
229 ++buf
; /* skip reserved byte */
230 msg_num_groups
= *(const uint8_t *)buf
;
232 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
236 if (PIM_DEBUG_PIM_J_P
) {
237 char src_str
[INET_ADDRSTRLEN
];
238 char upstream_str
[INET_ADDRSTRLEN
];
239 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
240 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
241 upstream_str
, sizeof(upstream_str
));
243 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
244 __func__
, upstream_str
, msg_num_groups
, msg_holdtime
,
249 for (group
= 0; group
< msg_num_groups
; ++group
) {
251 uint8_t msg_source_flags
;
252 uint16_t msg_num_joined_sources
;
253 uint16_t msg_num_pruned_sources
;
255 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
256 bool filtered
= false;
258 memset(&sg
, 0, sizeof(struct prefix_sg
));
259 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
260 if (addr_offset
< 1) {
265 remain
= pastend
- buf
;
267 char src_str
[INET_ADDRSTRLEN
];
268 pim_inet4_dump("<src?>", src_addr
, src_str
,
271 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
272 __func__
, remain
, 4, src_str
, ifp
->name
);
276 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
278 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
281 if (PIM_DEBUG_PIM_J_P
) {
282 char src_str
[INET_ADDRSTRLEN
];
283 char upstream_str
[INET_ADDRSTRLEN
];
284 char group_str
[INET_ADDRSTRLEN
];
285 pim_inet4_dump("<src?>", src_addr
, src_str
,
287 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
288 upstream_str
, sizeof(upstream_str
));
289 pim_inet4_dump("<grp?>", sg
.grp
, group_str
,
292 "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
293 __func__
, upstream_str
, group_str
,
294 msg_num_joined_sources
, msg_num_pruned_sources
,
299 filtered
= pim_is_group_filtered(pim_ifp
, &sg
.grp
);
301 /* Scan joined sources */
302 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
303 addr_offset
= pim_parse_addr_source(
304 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
305 if (addr_offset
< 1) {
311 /* if we are filtering this group, skip the join */
315 recv_join(ifp
, neigh
, msg_holdtime
,
316 msg_upstream_addr
.u
.prefix4
, &sg
,
319 if (sg
.src
.s_addr
== INADDR_ANY
) {
320 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
322 pim_ifchannel_set_star_g_join_state(
327 /* Scan pruned sources */
328 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
329 addr_offset
= pim_parse_addr_source(
330 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
331 if (addr_offset
< 1) {
337 /* if we are filtering this group, skip the prune */
341 recv_prune(ifp
, neigh
, msg_holdtime
,
342 msg_upstream_addr
.u
.prefix4
, &sg
,
345 * So if we are receiving a S,G,RPT prune
346 * before we have any data for that S,G
347 * We need to retrieve the sg_ch after
348 * we parse the prune.
350 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
355 /* (*,G) prune received */
356 for (ALL_LIST_ELEMENTS(sg_ch
->sources
, ch_node
,
358 if (PIM_IF_FLAG_TEST_S_G_RPT(child
->flags
)) {
359 if (child
->ifjoin_state
360 == PIM_IFJOIN_PRUNE_PENDING_TMP
)
362 child
->t_ifjoin_prune_pending_timer
);
364 child
->t_ifjoin_expiry_timer
);
365 PIM_IF_FLAG_UNSET_S_G_RPT(child
->flags
);
366 child
->ifjoin_state
= PIM_IFJOIN_NOINFO
;
367 delete_on_noinfo(child
);
371 /* Received SG-RPT Prune delete oif from specific S,G */
372 if (starg_ch
&& (msg_source_flags
& PIM_RPT_BIT_MASK
)
373 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
374 struct pim_upstream
*up
= sg_ch
->upstream
;
375 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
377 if (PIM_DEBUG_PIM_TRACE
)
379 "%s: SGRpt flag is set, del inherit oif from up %s",
380 __func__
, up
->sg_str
);
381 pim_channel_del_inherited_oif(
388 if (starg_ch
&& !filtered
)
389 pim_ifchannel_set_star_g_join_state(starg_ch
, 1, 0);
399 * While the RFC clearly states that this is 32 bits wide, it
400 * is cheating. These fields:
401 * Encoded-Unicast format (6 bytes MIN)
402 * Encoded-Group format (8 bytes MIN)
403 * Encoded-Source format (8 bytes MIN)
404 * are *not* 32 bits wide.
406 * Nor does the RFC explicitly call out the size for:
408 * Num Groups (1 byte)
410 * Number of Joined Sources (2 bytes)
411 * Number of Pruned Sources (2 bytes)
413 * This leads to a missleading representation from casual
414 * reading and making assumptions. Be careful!
417 * 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
418 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
419 * |PIM Ver| Type | Reserved | Checksum |
420 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421 * | Upstream Neighbor Address (Encoded-Unicast format) |
422 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
423 * | Reserved | Num groups | Holdtime |
424 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
425 * | Multicast Group Address 1 (Encoded-Group format) |
426 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427 * | Number of Joined Sources | Number of Pruned Sources |
428 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 * | Joined Source Address 1 (Encoded-Source format) |
430 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
434 * | Joined Source Address n (Encoded-Source format) |
435 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
436 * | Pruned Source Address 1 (Encoded-Source format) |
437 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
440 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
441 * | Pruned Source Address n (Encoded-Source format) |
442 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
443 * | Multicast Group Address m (Encoded-Group format) |
444 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
445 * | Number of Joined Sources | Number of Pruned Sources |
446 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
447 * | Joined Source Address 1 (Encoded-Source format) |
448 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
451 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
452 * | Joined Source Address n (Encoded-Source format) |
453 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454 * | Pruned Source Address 1 (Encoded-Source format) |
455 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
458 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
459 * | Pruned Source Address n (Encoded-Source format) |
460 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
462 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
464 struct pim_jp_agg_group
*group
;
465 struct pim_interface
*pim_ifp
= NULL
;
466 struct pim_jp_groups
*grp
= NULL
;
467 struct pim_jp
*msg
= NULL
;
468 struct listnode
*node
, *nnode
;
469 uint8_t pim_msg
[10000];
470 uint8_t *curr_ptr
= pim_msg
;
471 bool new_packet
= true;
472 size_t packet_left
= 0;
473 size_t packet_size
= 0;
474 size_t group_size
= 0;
476 if (rpf
->source_nexthop
.interface
)
477 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
479 zlog_warn("%s: RPF interface is not present", __func__
);
483 on_trace(__func__
, rpf
->source_nexthop
.interface
,
484 rpf
->rpf_addr
.u
.prefix4
);
487 zlog_warn("%s: multicast not enabled on interface %s", __func__
,
488 rpf
->source_nexthop
.interface
->name
);
492 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
)) {
493 if (PIM_DEBUG_PIM_J_P
) {
494 char dst_str
[INET_ADDRSTRLEN
];
495 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
496 dst_str
, sizeof(dst_str
));
497 zlog_debug("%s: upstream=%s is myself on interface %s",
499 rpf
->source_nexthop
.interface
->name
);
505 RFC 4601: 4.3.1. Sending Hello Messages
507 Thus, if a router needs to send a Join/Prune or Assert message on
508 an interface on which it has not yet sent a Hello message with the
509 currently configured IP address, then it MUST immediately send the
510 relevant Hello message without waiting for the Hello Timer to
511 expire, followed by the Join/Prune or Assert message.
513 pim_hello_require(rpf
->source_nexthop
.interface
);
515 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
517 msg
= (struct pim_jp
*)pim_msg
;
519 memset(msg
, 0, sizeof(*msg
));
521 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
522 rpf
->rpf_addr
.u
.prefix4
);
524 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
528 grp
= &msg
->groups
[0];
529 curr_ptr
= (uint8_t *)grp
;
530 packet_size
= sizeof(struct pim_msg_header
);
531 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
533 4; // reserved (1) + groups (1) + holdtime (2)
535 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
536 packet_left
-= packet_size
;
538 if (PIM_DEBUG_PIM_J_P
) {
539 char dst_str
[INET_ADDRSTRLEN
];
540 char grp_str
[INET_ADDRSTRLEN
];
541 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
542 dst_str
, sizeof(dst_str
));
543 pim_inet4_dump("<grp?>", group
->group
, grp_str
,
546 "%s: sending (G)=%s to upstream=%s on interface %s",
547 __func__
, grp_str
, dst_str
,
548 rpf
->source_nexthop
.interface
->name
);
551 group_size
= pim_msg_get_jp_group_size(group
->sources
);
552 if (group_size
> packet_left
) {
553 pim_msg_build_header(pim_msg
, packet_size
,
554 PIM_MSG_TYPE_JOIN_PRUNE
, false);
555 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
556 pim_ifp
->primary_address
,
557 qpim_all_pim_routers_addr
, pim_msg
,
559 rpf
->source_nexthop
.interface
->name
)) {
561 "%s: could not send PIM message on interface %s",
563 rpf
->source_nexthop
.interface
->name
);
566 msg
= (struct pim_jp
*)pim_msg
;
567 memset(msg
, 0, sizeof(*msg
));
569 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
570 rpf
->rpf_addr
.u
.prefix4
);
572 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
576 grp
= &msg
->groups
[0];
577 curr_ptr
= (uint8_t *)grp
;
578 packet_size
= sizeof(struct pim_msg_header
);
579 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
581 4; // reserved (1) + groups (1) + holdtime (2)
583 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
584 packet_left
-= packet_size
;
592 curr_ptr
+= group_size
;
593 packet_left
-= group_size
;
594 packet_size
+= group_size
;
595 pim_msg_build_jp_groups(grp
, group
, group_size
);
597 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
598 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
600 if (PIM_DEBUG_PIM_TRACE
)
602 "%s: interface %s num_joins %u num_prunes %u",
603 __func__
, rpf
->source_nexthop
.interface
->name
,
604 ntohs(grp
->joins
), ntohs(grp
->prunes
));
606 grp
= (struct pim_jp_groups
*)curr_ptr
;
607 if (packet_left
< sizeof(struct pim_jp_groups
)
608 || msg
->num_groups
== 255) {
609 pim_msg_build_header(pim_msg
, packet_size
,
610 PIM_MSG_TYPE_JOIN_PRUNE
, false);
611 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
612 pim_ifp
->primary_address
,
613 qpim_all_pim_routers_addr
, pim_msg
,
615 rpf
->source_nexthop
.interface
->name
)) {
617 "%s: could not send PIM message on interface %s",
619 rpf
->source_nexthop
.interface
->name
);
628 // msg->num_groups = htons (msg->num_groups);
629 pim_msg_build_header(pim_msg
, packet_size
,
630 PIM_MSG_TYPE_JOIN_PRUNE
, false);
631 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
632 qpim_all_pim_routers_addr
, pim_msg
,
634 rpf
->source_nexthop
.interface
->name
)) {
636 "%s: could not send PIM message on interface %s",
637 __func__
, rpf
->source_nexthop
.interface
->name
);