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 __PRETTY_FUNCTION__
, 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",
88 __PRETTY_FUNCTION__
, sg
);
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
));
102 zlog_warn("%s: Specified RP(%s) in join is different than our configured RP(%s)",
103 __PRETTY_FUNCTION__
, received_rp
, local_rp
);
107 sg
->src
.s_addr
= INADDR_ANY
;
110 /* Restart join expiry timer */
111 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
, sg
,
112 source_flags
, holdtime
);
115 static void recv_prune(struct interface
*ifp
, struct pim_neighbor
*neigh
,
116 uint16_t holdtime
, struct in_addr upstream
,
117 struct prefix_sg
*sg
, uint8_t source_flags
)
119 struct pim_interface
*pim_ifp
= NULL
;
121 if (PIM_DEBUG_PIM_TRACE
) {
122 char up_str
[INET_ADDRSTRLEN
];
123 char neigh_str
[INET_ADDRSTRLEN
];
124 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
125 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
128 "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
129 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
130 source_flags
& PIM_RPT_BIT_MASK
,
131 source_flags
& PIM_WILDCARD_BIT_MASK
, up_str
, holdtime
,
132 neigh_str
, ifp
->name
);
138 ++pim_ifp
->pim_ifstat_prune_recv
;
140 if ((source_flags
& PIM_RPT_BIT_MASK
)
141 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
142 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
145 if (PIM_DEBUG_PIM_TRACE
)
146 zlog_debug("%s: RP for %pSG4 completely failed lookup",
147 __PRETTY_FUNCTION__
, sg
);
150 // Ignoring Prune *,G's at the moment.
151 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
154 sg
->src
.s_addr
= INADDR_ANY
;
157 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
160 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
161 struct in_addr src_addr
, uint8_t *tlv_buf
,
164 struct prefix msg_upstream_addr
;
165 struct pim_interface
*pim_ifp
;
166 uint8_t msg_num_groups
;
167 uint16_t msg_holdtime
;
175 pastend
= tlv_buf
+ tlv_buf_size
;
182 pim_parse_addr_ucast(&msg_upstream_addr
, buf
, pastend
- buf
);
183 if (addr_offset
< 1) {
184 char src_str
[INET_ADDRSTRLEN
];
185 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
186 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
187 __PRETTY_FUNCTION__
, src_str
, ifp
->name
);
193 Check upstream address family
195 if (msg_upstream_addr
.family
!= AF_INET
) {
196 char src_str
[INET_ADDRSTRLEN
];
197 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
198 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
199 __PRETTY_FUNCTION__
, msg_upstream_addr
.family
,
204 remain
= pastend
- buf
;
206 char src_str
[INET_ADDRSTRLEN
];
207 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
209 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
210 __PRETTY_FUNCTION__
, remain
, 4, src_str
, ifp
->name
);
214 ++buf
; /* skip reserved byte */
215 msg_num_groups
= *(const uint8_t *)buf
;
217 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
221 if (PIM_DEBUG_PIM_J_P
) {
222 char src_str
[INET_ADDRSTRLEN
];
223 char upstream_str
[INET_ADDRSTRLEN
];
224 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
225 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
226 upstream_str
, sizeof(upstream_str
));
228 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
229 __PRETTY_FUNCTION__
, upstream_str
, msg_num_groups
,
230 msg_holdtime
, src_str
, ifp
->name
);
234 for (group
= 0; group
< msg_num_groups
; ++group
) {
236 uint8_t msg_source_flags
;
237 uint16_t msg_num_joined_sources
;
238 uint16_t msg_num_pruned_sources
;
240 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
241 bool filtered
= false;
243 memset(&sg
, 0, sizeof(struct prefix_sg
));
244 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
245 if (addr_offset
< 1) {
250 remain
= pastend
- buf
;
252 char src_str
[INET_ADDRSTRLEN
];
253 pim_inet4_dump("<src?>", src_addr
, src_str
,
256 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
257 __PRETTY_FUNCTION__
, remain
, 4, src_str
,
262 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
264 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
267 if (PIM_DEBUG_PIM_J_P
) {
268 char src_str
[INET_ADDRSTRLEN
];
269 char upstream_str
[INET_ADDRSTRLEN
];
270 char group_str
[INET_ADDRSTRLEN
];
271 pim_inet4_dump("<src?>", src_addr
, src_str
,
273 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
274 upstream_str
, sizeof(upstream_str
));
275 pim_inet4_dump("<grp?>", sg
.grp
, group_str
,
278 "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
279 __PRETTY_FUNCTION__
, upstream_str
, group_str
,
280 msg_num_joined_sources
, msg_num_pruned_sources
,
285 filtered
= pim_is_group_filtered(pim_ifp
, &sg
.grp
);
287 /* Scan joined sources */
288 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
289 addr_offset
= pim_parse_addr_source(
290 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
291 if (addr_offset
< 1) {
297 /* if we are filtering this group, skip the join */
301 recv_join(ifp
, neigh
, msg_holdtime
,
302 msg_upstream_addr
.u
.prefix4
, &sg
,
305 if (sg
.src
.s_addr
== INADDR_ANY
) {
306 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
308 pim_ifchannel_set_star_g_join_state(
313 /* Scan pruned sources */
314 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
315 addr_offset
= pim_parse_addr_source(
316 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
317 if (addr_offset
< 1) {
323 /* if we are filtering this group, skip the prune */
327 recv_prune(ifp
, neigh
, msg_holdtime
,
328 msg_upstream_addr
.u
.prefix4
, &sg
,
331 * So if we are receiving a S,G,RPT prune
332 * before we have any data for that S,G
333 * We need to retrieve the sg_ch after
334 * we parse the prune.
336 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
338 /* Received SG-RPT Prune delete oif from specific S,G */
339 if (starg_ch
&& sg_ch
340 && (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",
350 pim_channel_del_inherited_oif(
357 if (starg_ch
&& !filtered
)
358 pim_ifchannel_set_star_g_join_state(starg_ch
, 1, 0);
368 * While the RFC clearly states that this is 32 bits wide, it
369 * is cheating. These fields:
370 * Encoded-Unicast format (6 bytes MIN)
371 * Encoded-Group format (8 bytes MIN)
372 * Encoded-Source format (8 bytes MIN)
373 * are *not* 32 bits wide.
375 * Nor does the RFC explicitly call out the size for:
377 * Num Groups (1 byte)
379 * Number of Joined Sources (2 bytes)
380 * Number of Pruned Sources (2 bytes)
382 * This leads to a missleading representation from casual
383 * reading and making assumptions. Be careful!
386 * 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
387 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388 * |PIM Ver| Type | Reserved | Checksum |
389 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390 * | Upstream Neighbor Address (Encoded-Unicast format) |
391 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
392 * | Reserved | Num groups | Holdtime |
393 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394 * | Multicast Group Address 1 (Encoded-Group format) |
395 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
396 * | Number of Joined Sources | Number of Pruned Sources |
397 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
398 * | Joined Source Address 1 (Encoded-Source format) |
399 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
402 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
403 * | Joined Source Address n (Encoded-Source format) |
404 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405 * | Pruned Source Address 1 (Encoded-Source format) |
406 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
410 * | Pruned Source Address n (Encoded-Source format) |
411 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
412 * | Multicast Group Address m (Encoded-Group format) |
413 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
414 * | Number of Joined Sources | Number of Pruned Sources |
415 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
416 * | Joined Source Address 1 (Encoded-Source format) |
417 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
420 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421 * | Joined Source Address n (Encoded-Source format) |
422 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
423 * | Pruned Source Address 1 (Encoded-Source format) |
424 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428 * | Pruned Source Address n (Encoded-Source format) |
429 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
433 struct pim_jp_agg_group
*group
;
434 struct pim_interface
*pim_ifp
= NULL
;
435 struct pim_jp_groups
*grp
= NULL
;
436 struct pim_jp
*msg
= NULL
;
437 struct listnode
*node
, *nnode
;
438 uint8_t pim_msg
[10000];
439 uint8_t *curr_ptr
= pim_msg
;
440 bool new_packet
= true;
441 size_t packet_left
= 0;
442 size_t packet_size
= 0;
443 size_t group_size
= 0;
445 if (rpf
->source_nexthop
.interface
)
446 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
448 zlog_warn("%s: RPF interface is not present",
449 __PRETTY_FUNCTION__
);
453 on_trace(__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
,
454 rpf
->rpf_addr
.u
.prefix4
);
457 zlog_warn("%s: multicast not enabled on interface %s",
459 rpf
->source_nexthop
.interface
->name
);
463 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
)) {
464 if (PIM_DEBUG_PIM_J_P
) {
465 char dst_str
[INET_ADDRSTRLEN
];
466 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
467 dst_str
, sizeof(dst_str
));
468 zlog_debug("%s: upstream=%s is myself on interface %s",
469 __PRETTY_FUNCTION__
, dst_str
,
470 rpf
->source_nexthop
.interface
->name
);
476 RFC 4601: 4.3.1. Sending Hello Messages
478 Thus, if a router needs to send a Join/Prune or Assert message on
479 an interface on which it has not yet sent a Hello message with the
480 currently configured IP address, then it MUST immediately send the
481 relevant Hello message without waiting for the Hello Timer to
482 expire, followed by the Join/Prune or Assert message.
484 pim_hello_require(rpf
->source_nexthop
.interface
);
486 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
488 msg
= (struct pim_jp
*)pim_msg
;
490 memset(msg
, 0, sizeof(*msg
));
492 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
493 rpf
->rpf_addr
.u
.prefix4
);
495 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
499 grp
= &msg
->groups
[0];
500 curr_ptr
= (uint8_t *)grp
;
501 packet_size
= sizeof(struct pim_msg_header
);
502 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
504 4; // reserved (1) + groups (1) + holdtime (2)
506 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
507 packet_left
-= packet_size
;
509 if (PIM_DEBUG_PIM_J_P
) {
510 char dst_str
[INET_ADDRSTRLEN
];
511 char grp_str
[INET_ADDRSTRLEN
];
512 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
513 dst_str
, sizeof(dst_str
));
514 pim_inet4_dump("<grp?>", group
->group
, grp_str
,
517 "%s: sending (G)=%s to upstream=%s on interface %s",
518 __PRETTY_FUNCTION__
, grp_str
, dst_str
,
519 rpf
->source_nexthop
.interface
->name
);
522 group_size
= pim_msg_get_jp_group_size(group
->sources
);
523 if (group_size
> packet_left
) {
524 pim_msg_build_header(pim_msg
, packet_size
,
525 PIM_MSG_TYPE_JOIN_PRUNE
, false);
526 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
527 pim_ifp
->primary_address
,
528 qpim_all_pim_routers_addr
, pim_msg
,
530 rpf
->source_nexthop
.interface
->name
)) {
532 "%s: could not send PIM message on interface %s",
534 rpf
->source_nexthop
.interface
->name
);
537 msg
= (struct pim_jp
*)pim_msg
;
538 memset(msg
, 0, sizeof(*msg
));
540 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
541 rpf
->rpf_addr
.u
.prefix4
);
543 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
547 grp
= &msg
->groups
[0];
548 curr_ptr
= (uint8_t *)grp
;
549 packet_size
= sizeof(struct pim_msg_header
);
550 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
552 4; // reserved (1) + groups (1) + holdtime (2)
554 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
555 packet_left
-= packet_size
;
563 curr_ptr
+= group_size
;
564 packet_left
-= group_size
;
565 packet_size
+= group_size
;
566 pim_msg_build_jp_groups(grp
, group
, group_size
);
568 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
569 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
571 if (PIM_DEBUG_PIM_TRACE
)
573 "%s: interface %s num_joins %u num_prunes %u",
575 rpf
->source_nexthop
.interface
->name
,
576 ntohs(grp
->joins
), ntohs(grp
->prunes
));
578 grp
= (struct pim_jp_groups
*)curr_ptr
;
579 if (packet_left
< sizeof(struct pim_jp_groups
)
580 || msg
->num_groups
== 255) {
581 pim_msg_build_header(pim_msg
, packet_size
,
582 PIM_MSG_TYPE_JOIN_PRUNE
, false);
583 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
584 pim_ifp
->primary_address
,
585 qpim_all_pim_routers_addr
, pim_msg
,
587 rpf
->source_nexthop
.interface
->name
)) {
589 "%s: could not send PIM message on interface %s",
591 rpf
->source_nexthop
.interface
->name
);
600 // msg->num_groups = htons (msg->num_groups);
601 pim_msg_build_header(pim_msg
, packet_size
,
602 PIM_MSG_TYPE_JOIN_PRUNE
, false);
603 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
604 qpim_all_pim_routers_addr
, pim_msg
,
606 rpf
->source_nexthop
.interface
->name
)) {
608 "%s: could not send PIM message on interface %s",
610 rpf
->source_nexthop
.interface
->name
);