3 * Copyright (C) 2008 Everton da Silva Marques
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 #include "pim_iface.h"
36 #include "pim_hello.h"
37 #include "pim_ifchannel.h"
40 #include "pim_jp_agg.h"
43 static void on_trace(const char *label
, struct interface
*ifp
,
46 if (PIM_DEBUG_PIM_TRACE
) {
47 char src_str
[INET_ADDRSTRLEN
];
48 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
49 zlog_debug("%s: from %s on %s", label
, src_str
, ifp
->name
);
53 static void recv_join(struct interface
*ifp
, struct pim_neighbor
*neigh
,
54 uint16_t holdtime
, struct in_addr upstream
,
55 struct prefix_sg
*sg
, uint8_t source_flags
)
57 struct pim_interface
*pim_ifp
= NULL
;
59 if (PIM_DEBUG_PIM_TRACE
) {
60 char up_str
[INET_ADDRSTRLEN
];
61 char neigh_str
[INET_ADDRSTRLEN
];
62 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
63 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
66 "%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
67 __func__
, pim_str_sg_dump(sg
),
68 !!(source_flags
& PIM_RPT_BIT_MASK
),
69 !!(source_flags
& PIM_WILDCARD_BIT_MASK
), up_str
,
70 holdtime
, neigh_str
, ifp
->name
);
76 ++pim_ifp
->pim_ifstat_join_recv
;
79 * If the RPT and WC are set it's a (*,G)
80 * and the source is the RP
82 if ((source_flags
& PIM_RPT_BIT_MASK
)
83 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
84 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
87 zlog_warn("%s: Lookup of RP failed for %pSG4", __func__
,
92 * If the RP sent in the message is not
93 * our RP for the group, drop the message
95 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
) {
96 char received_rp
[INET_ADDRSTRLEN
];
97 char local_rp
[INET_ADDRSTRLEN
];
98 pim_inet4_dump("<received?>", sg
->src
, received_rp
,
100 pim_inet4_dump("<local?>", rp
->rpf_addr
.u
.prefix4
,
101 local_rp
, sizeof(local_rp
));
103 "%s: Specified RP(%s) in join is different than our configured RP(%s)",
104 __func__
, received_rp
, local_rp
);
108 sg
->src
.s_addr
= INADDR_ANY
;
111 /* Restart join expiry timer */
112 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
, sg
,
113 source_flags
, holdtime
);
116 static void recv_prune(struct interface
*ifp
, struct pim_neighbor
*neigh
,
117 uint16_t holdtime
, struct in_addr upstream
,
118 struct prefix_sg
*sg
, uint8_t source_flags
)
120 struct pim_interface
*pim_ifp
= NULL
;
122 if (PIM_DEBUG_PIM_TRACE
) {
123 char up_str
[INET_ADDRSTRLEN
];
124 char neigh_str
[INET_ADDRSTRLEN
];
125 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
126 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
129 "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
130 __func__
, pim_str_sg_dump(sg
),
131 source_flags
& PIM_RPT_BIT_MASK
,
132 source_flags
& PIM_WILDCARD_BIT_MASK
, up_str
, holdtime
,
133 neigh_str
, ifp
->name
);
139 ++pim_ifp
->pim_ifstat_prune_recv
;
141 if ((source_flags
& PIM_RPT_BIT_MASK
)
142 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
144 * RFC 4601 Section 4.5.2:
145 * Received Prune(*,G) messages are processed even if the
146 * RP in the message does not match RP(G).
148 if (PIM_DEBUG_PIM_TRACE
) {
149 char received_rp
[INET_ADDRSTRLEN
];
151 pim_inet4_dump("<received?>", sg
->src
, received_rp
,
152 sizeof(received_rp
));
153 zlog_debug("%s: Prune received with RP(%s) for %pSG4",
154 __func__
, received_rp
, sg
);
157 sg
->src
.s_addr
= INADDR_ANY
;
160 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
163 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
164 struct in_addr src_addr
, uint8_t *tlv_buf
,
167 struct prefix msg_upstream_addr
;
168 struct pim_interface
*pim_ifp
;
169 uint8_t msg_num_groups
;
170 uint16_t msg_holdtime
;
178 pastend
= tlv_buf
+ tlv_buf_size
;
185 pim_parse_addr_ucast(&msg_upstream_addr
, buf
, pastend
- buf
);
186 if (addr_offset
< 1) {
187 char src_str
[INET_ADDRSTRLEN
];
188 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
189 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
190 __func__
, src_str
, ifp
->name
);
196 Check upstream address family
198 if (msg_upstream_addr
.family
!= AF_INET
) {
199 char src_str
[INET_ADDRSTRLEN
];
200 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
202 "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
203 __func__
, msg_upstream_addr
.family
, src_str
, ifp
->name
);
207 remain
= pastend
- buf
;
209 char src_str
[INET_ADDRSTRLEN
];
210 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
212 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
213 __func__
, remain
, 4, src_str
, ifp
->name
);
217 ++buf
; /* skip reserved byte */
218 msg_num_groups
= *(const uint8_t *)buf
;
220 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
224 if (PIM_DEBUG_PIM_J_P
) {
225 char src_str
[INET_ADDRSTRLEN
];
226 char upstream_str
[INET_ADDRSTRLEN
];
227 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
228 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
229 upstream_str
, sizeof(upstream_str
));
231 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
232 __func__
, upstream_str
, msg_num_groups
, msg_holdtime
,
237 for (group
= 0; group
< msg_num_groups
; ++group
) {
239 uint8_t msg_source_flags
;
240 uint16_t msg_num_joined_sources
;
241 uint16_t msg_num_pruned_sources
;
243 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
244 bool filtered
= false;
246 memset(&sg
, 0, sizeof(struct prefix_sg
));
247 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
248 if (addr_offset
< 1) {
253 remain
= pastend
- buf
;
255 char src_str
[INET_ADDRSTRLEN
];
256 pim_inet4_dump("<src?>", src_addr
, src_str
,
259 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
260 __func__
, remain
, 4, src_str
, ifp
->name
);
264 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
266 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
269 if (PIM_DEBUG_PIM_J_P
) {
270 char src_str
[INET_ADDRSTRLEN
];
271 char upstream_str
[INET_ADDRSTRLEN
];
272 char group_str
[INET_ADDRSTRLEN
];
273 pim_inet4_dump("<src?>", src_addr
, src_str
,
275 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
276 upstream_str
, sizeof(upstream_str
));
277 pim_inet4_dump("<grp?>", sg
.grp
, group_str
,
280 "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
281 __func__
, upstream_str
, group_str
,
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 (sg
.src
.s_addr
== INADDR_ANY
) {
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
);
340 /* Received SG-RPT Prune delete oif from specific S,G */
341 if (starg_ch
&& sg_ch
342 && (msg_source_flags
& PIM_RPT_BIT_MASK
)
343 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
344 struct pim_upstream
*up
= sg_ch
->upstream
;
345 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
347 if (PIM_DEBUG_PIM_TRACE
)
349 "%s: SGRpt flag is set, del inherit oif from up %s",
350 __func__
, up
->sg_str
);
351 pim_channel_del_inherited_oif(
358 if (starg_ch
&& !filtered
)
359 pim_ifchannel_set_star_g_join_state(starg_ch
, 1, 0);
369 * While the RFC clearly states that this is 32 bits wide, it
370 * is cheating. These fields:
371 * Encoded-Unicast format (6 bytes MIN)
372 * Encoded-Group format (8 bytes MIN)
373 * Encoded-Source format (8 bytes MIN)
374 * are *not* 32 bits wide.
376 * Nor does the RFC explicitly call out the size for:
378 * Num Groups (1 byte)
380 * Number of Joined Sources (2 bytes)
381 * Number of Pruned Sources (2 bytes)
383 * This leads to a missleading representation from casual
384 * reading and making assumptions. Be careful!
387 * 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
388 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 * |PIM Ver| Type | Reserved | Checksum |
390 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391 * | Upstream Neighbor Address (Encoded-Unicast format) |
392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 * | Reserved | Num groups | Holdtime |
394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * | Multicast Group Address 1 (Encoded-Group format) |
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * | Number of Joined Sources | Number of Pruned Sources |
398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 * | Joined Source Address 1 (Encoded-Source format) |
400 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
404 * | Joined Source Address n (Encoded-Source format) |
405 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 * | Pruned Source Address 1 (Encoded-Source format) |
407 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 * | Pruned Source Address n (Encoded-Source format) |
412 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413 * | Multicast Group Address m (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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
432 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
434 struct pim_jp_agg_group
*group
;
435 struct pim_interface
*pim_ifp
= NULL
;
436 struct pim_jp_groups
*grp
= NULL
;
437 struct pim_jp
*msg
= NULL
;
438 struct listnode
*node
, *nnode
;
439 uint8_t pim_msg
[10000];
440 uint8_t *curr_ptr
= pim_msg
;
441 bool new_packet
= true;
442 size_t packet_left
= 0;
443 size_t packet_size
= 0;
444 size_t group_size
= 0;
446 if (rpf
->source_nexthop
.interface
)
447 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
449 zlog_warn("%s: RPF interface is not present", __func__
);
453 on_trace(__func__
, rpf
->source_nexthop
.interface
,
454 rpf
->rpf_addr
.u
.prefix4
);
457 zlog_warn("%s: multicast not enabled on interface %s", __func__
,
458 rpf
->source_nexthop
.interface
->name
);
462 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
)) {
463 if (PIM_DEBUG_PIM_J_P
) {
464 char dst_str
[INET_ADDRSTRLEN
];
465 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
466 dst_str
, sizeof(dst_str
));
467 zlog_debug("%s: upstream=%s is myself on interface %s",
469 rpf
->source_nexthop
.interface
->name
);
475 RFC 4601: 4.3.1. Sending Hello Messages
477 Thus, if a router needs to send a Join/Prune or Assert message on
478 an interface on which it has not yet sent a Hello message with the
479 currently configured IP address, then it MUST immediately send the
480 relevant Hello message without waiting for the Hello Timer to
481 expire, followed by the Join/Prune or Assert message.
483 pim_hello_require(rpf
->source_nexthop
.interface
);
485 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
487 msg
= (struct pim_jp
*)pim_msg
;
489 memset(msg
, 0, sizeof(*msg
));
491 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
492 rpf
->rpf_addr
.u
.prefix4
);
494 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
498 grp
= &msg
->groups
[0];
499 curr_ptr
= (uint8_t *)grp
;
500 packet_size
= sizeof(struct pim_msg_header
);
501 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
503 4; // reserved (1) + groups (1) + holdtime (2)
505 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
506 packet_left
-= packet_size
;
508 if (PIM_DEBUG_PIM_J_P
) {
509 char dst_str
[INET_ADDRSTRLEN
];
510 char grp_str
[INET_ADDRSTRLEN
];
511 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
512 dst_str
, sizeof(dst_str
));
513 pim_inet4_dump("<grp?>", group
->group
, grp_str
,
516 "%s: sending (G)=%s to upstream=%s on interface %s",
517 __func__
, grp_str
, dst_str
,
518 rpf
->source_nexthop
.interface
->name
);
521 group_size
= pim_msg_get_jp_group_size(group
->sources
);
522 if (group_size
> packet_left
) {
523 pim_msg_build_header(pim_msg
, packet_size
,
524 PIM_MSG_TYPE_JOIN_PRUNE
, false);
525 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
526 pim_ifp
->primary_address
,
527 qpim_all_pim_routers_addr
, pim_msg
,
529 rpf
->source_nexthop
.interface
->name
)) {
531 "%s: could not send PIM message on interface %s",
533 rpf
->source_nexthop
.interface
->name
);
536 msg
= (struct pim_jp
*)pim_msg
;
537 memset(msg
, 0, sizeof(*msg
));
539 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
540 rpf
->rpf_addr
.u
.prefix4
);
542 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
546 grp
= &msg
->groups
[0];
547 curr_ptr
= (uint8_t *)grp
;
548 packet_size
= sizeof(struct pim_msg_header
);
549 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
551 4; // reserved (1) + groups (1) + holdtime (2)
553 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
554 packet_left
-= packet_size
;
562 curr_ptr
+= group_size
;
563 packet_left
-= group_size
;
564 packet_size
+= group_size
;
565 pim_msg_build_jp_groups(grp
, group
, group_size
);
567 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
568 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
570 if (PIM_DEBUG_PIM_TRACE
)
572 "%s: interface %s num_joins %u num_prunes %u",
573 __func__
, rpf
->source_nexthop
.interface
->name
,
574 ntohs(grp
->joins
), ntohs(grp
->prunes
));
576 grp
= (struct pim_jp_groups
*)curr_ptr
;
577 if (packet_left
< sizeof(struct pim_jp_groups
)
578 || msg
->num_groups
== 255) {
579 pim_msg_build_header(pim_msg
, packet_size
,
580 PIM_MSG_TYPE_JOIN_PRUNE
, false);
581 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
582 pim_ifp
->primary_address
,
583 qpim_all_pim_routers_addr
, pim_msg
,
585 rpf
->source_nexthop
.interface
->name
)) {
587 "%s: could not send PIM message on interface %s",
589 rpf
->source_nexthop
.interface
->name
);
598 // msg->num_groups = htons (msg->num_groups);
599 pim_msg_build_header(pim_msg
, packet_size
,
600 PIM_MSG_TYPE_JOIN_PRUNE
, false);
601 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
602 qpim_all_pim_routers_addr
, pim_msg
,
604 rpf
->source_nexthop
.interface
->name
)) {
606 "%s: could not send PIM message on interface %s",
607 __func__
, rpf
->source_nexthop
.interface
->name
);