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"
42 static void on_trace(const char *label
, struct interface
*ifp
,
45 if (PIM_DEBUG_PIM_TRACE
) {
46 char src_str
[INET_ADDRSTRLEN
];
47 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
48 zlog_debug("%s: from %s on %s", label
, src_str
, ifp
->name
);
52 static void recv_join(struct interface
*ifp
, struct pim_neighbor
*neigh
,
53 uint16_t holdtime
, struct in_addr upstream
,
54 struct prefix_sg
*sg
, uint8_t source_flags
)
56 struct pim_interface
*pim_ifp
= NULL
;
58 if (PIM_DEBUG_PIM_TRACE
) {
59 char up_str
[INET_ADDRSTRLEN
];
60 char neigh_str
[INET_ADDRSTRLEN
];
61 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
62 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
65 "%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
66 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
67 !!(source_flags
& PIM_RPT_BIT_MASK
),
68 !!(source_flags
& PIM_WILDCARD_BIT_MASK
), up_str
,
69 holdtime
, neigh_str
, ifp
->name
);
75 ++pim_ifp
->pim_ifstat_join_recv
;
78 * If the RPT and WC are set it's a (*,G)
79 * and the source is the RP
81 if ((source_flags
& PIM_RPT_BIT_MASK
)
82 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
83 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
86 * If the RP sent in the message is not
87 * our RP for the group, drop the message
89 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
) {
90 char received_rp
[INET_ADDRSTRLEN
];
91 char local_rp
[INET_ADDRSTRLEN
];
92 pim_inet4_dump("<received?>", sg
->src
, received_rp
,
94 pim_inet4_dump("<local?>", rp
->rpf_addr
.u
.prefix4
,
95 local_rp
, sizeof(local_rp
));
96 if (PIM_DEBUG_PIM_TRACE
)
98 "%s: Specified RP(%s) in join is different than our configured RP(%s)",
99 __PRETTY_FUNCTION__
, received_rp
,
104 sg
->src
.s_addr
= INADDR_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
, struct in_addr upstream
,
114 struct prefix_sg
*sg
, uint8_t source_flags
)
116 struct pim_interface
*pim_ifp
= NULL
;
118 if (PIM_DEBUG_PIM_TRACE
) {
119 char up_str
[INET_ADDRSTRLEN
];
120 char neigh_str
[INET_ADDRSTRLEN
];
121 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
122 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
125 "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
126 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
127 source_flags
& PIM_RPT_BIT_MASK
,
128 source_flags
& PIM_WILDCARD_BIT_MASK
, up_str
, holdtime
,
129 neigh_str
, ifp
->name
);
135 ++pim_ifp
->pim_ifstat_prune_recv
;
137 if ((source_flags
& PIM_RPT_BIT_MASK
)
138 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
139 struct pim_rpf
*rp
= RP(pim_ifp
->pim
, sg
->grp
);
141 // Ignoring Prune *,G's at the moment.
142 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
145 sg
->src
.s_addr
= INADDR_ANY
;
148 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
151 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
152 struct in_addr src_addr
, uint8_t *tlv_buf
,
155 struct prefix msg_upstream_addr
;
156 uint8_t msg_num_groups
;
157 uint16_t msg_holdtime
;
165 pastend
= tlv_buf
+ tlv_buf_size
;
171 pim_parse_addr_ucast(&msg_upstream_addr
, buf
, pastend
- buf
);
172 if (addr_offset
< 1) {
173 char src_str
[INET_ADDRSTRLEN
];
174 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
175 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
176 __PRETTY_FUNCTION__
, src_str
, ifp
->name
);
182 Check upstream address family
184 if (msg_upstream_addr
.family
!= AF_INET
) {
185 if (PIM_DEBUG_PIM_J_P
) {
186 char src_str
[INET_ADDRSTRLEN
];
187 pim_inet4_dump("<src?>", src_addr
, src_str
,
190 "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
191 __PRETTY_FUNCTION__
, msg_upstream_addr
.family
,
197 remain
= pastend
- buf
;
199 char src_str
[INET_ADDRSTRLEN
];
200 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
202 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
203 __PRETTY_FUNCTION__
, remain
, 4, src_str
, ifp
->name
);
207 ++buf
; /* skip reserved byte */
208 msg_num_groups
= *(const uint8_t *)buf
;
210 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
214 if (PIM_DEBUG_PIM_J_P
) {
215 char src_str
[INET_ADDRSTRLEN
];
216 char upstream_str
[INET_ADDRSTRLEN
];
217 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
218 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
219 upstream_str
, sizeof(upstream_str
));
221 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
222 __PRETTY_FUNCTION__
, upstream_str
, msg_num_groups
,
223 msg_holdtime
, src_str
, ifp
->name
);
227 for (group
= 0; group
< msg_num_groups
; ++group
) {
229 uint8_t msg_source_flags
;
230 uint16_t msg_num_joined_sources
;
231 uint16_t msg_num_pruned_sources
;
233 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
234 uint8_t starg_alone
= 0;
236 memset(&sg
, 0, sizeof(struct prefix_sg
));
237 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
238 if (addr_offset
< 1) {
243 remain
= pastend
- buf
;
245 char src_str
[INET_ADDRSTRLEN
];
246 pim_inet4_dump("<src?>", src_addr
, src_str
,
249 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
250 __PRETTY_FUNCTION__
, remain
, 4, src_str
,
255 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
257 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
260 if (PIM_DEBUG_PIM_J_P
) {
261 char src_str
[INET_ADDRSTRLEN
];
262 char upstream_str
[INET_ADDRSTRLEN
];
263 char group_str
[INET_ADDRSTRLEN
];
264 pim_inet4_dump("<src?>", src_addr
, src_str
,
266 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
267 upstream_str
, sizeof(upstream_str
));
268 pim_inet4_dump("<grp?>", sg
.grp
, group_str
,
271 "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
272 __PRETTY_FUNCTION__
, upstream_str
, group_str
,
273 msg_num_joined_sources
, msg_num_pruned_sources
,
277 /* Scan joined sources */
278 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
279 addr_offset
= pim_parse_addr_source(
280 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
281 if (addr_offset
< 1) {
287 recv_join(ifp
, neigh
, msg_holdtime
,
288 msg_upstream_addr
.u
.prefix4
, &sg
,
291 if (sg
.src
.s_addr
== INADDR_ANY
) {
293 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
295 pim_ifchannel_set_star_g_join_state(
296 starg_ch
, 0, msg_source_flags
,
301 /* Scan pruned sources */
302 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
303 addr_offset
= pim_parse_addr_source(
304 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
305 if (addr_offset
< 1) {
311 recv_prune(ifp
, neigh
, msg_holdtime
,
312 msg_upstream_addr
.u
.prefix4
, &sg
,
316 * So if we are receiving a S,G,RPT prune
317 * before we have any data for that S,G
318 * We need to retrieve the sg_ch after
319 * we parse the prune.
321 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
323 /* Received SG-RPT Prune delete oif from specific S,G */
324 if (starg_ch
&& sg_ch
325 && (msg_source_flags
& PIM_RPT_BIT_MASK
)
326 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
327 struct pim_upstream
*up
= sg_ch
->upstream
;
328 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
332 "%s: SGRpt flag is set, del inherit oif from up %s",
338 PIM_OIF_FLAG_PROTO_STAR
);
343 pim_ifchannel_set_star_g_join_state(
344 starg_ch
, 1, msg_source_flags
, 0, starg_alone
);
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
;
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 on_trace(__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
,
432 rpf
->rpf_addr
.u
.prefix4
);
434 if (rpf
->source_nexthop
.interface
)
435 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
437 zlog_warn("%s: RPF interface is not present",
438 __PRETTY_FUNCTION__
);
443 zlog_warn("%s: multicast not enabled on interface %s",
445 rpf
->source_nexthop
.interface
->name
);
449 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
)) {
450 if (PIM_DEBUG_PIM_J_P
) {
451 char dst_str
[INET_ADDRSTRLEN
];
452 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
453 dst_str
, sizeof(dst_str
));
454 zlog_debug("%s: upstream=%s is myself on interface %s",
455 __PRETTY_FUNCTION__
, dst_str
,
456 rpf
->source_nexthop
.interface
->name
);
462 RFC 4601: 4.3.1. Sending Hello Messages
464 Thus, if a router needs to send a Join/Prune or Assert message on
465 an interface on which it has not yet sent a Hello message with the
466 currently configured IP address, then it MUST immediately send the
467 relevant Hello message without waiting for the Hello Timer to
468 expire, followed by the Join/Prune or Assert message.
470 pim_hello_require(rpf
->source_nexthop
.interface
);
472 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
474 msg
= (struct pim_jp
*)pim_msg
;
476 memset(msg
, 0, sizeof(*msg
));
478 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
479 rpf
->rpf_addr
.u
.prefix4
);
481 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
485 grp
= &msg
->groups
[0];
486 curr_ptr
= (uint8_t *)grp
;
487 packet_size
= sizeof(struct pim_msg_header
);
488 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
490 4; // reserved (1) + groups (1) + holdtime (2)
492 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
493 packet_left
-= packet_size
;
495 if (PIM_DEBUG_PIM_J_P
) {
496 char dst_str
[INET_ADDRSTRLEN
];
497 char grp_str
[INET_ADDRSTRLEN
];
498 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
499 dst_str
, sizeof(dst_str
));
500 pim_inet4_dump("<grp?>", group
->group
, grp_str
,
503 "%s: sending (G)=%s to upstream=%s on interface %s",
504 __PRETTY_FUNCTION__
, grp_str
, dst_str
,
505 rpf
->source_nexthop
.interface
->name
);
508 group_size
= pim_msg_get_jp_group_size(group
->sources
);
509 if (group_size
> packet_left
) {
510 pim_msg_build_header(pim_msg
, packet_size
,
511 PIM_MSG_TYPE_JOIN_PRUNE
);
512 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
513 pim_ifp
->primary_address
,
514 qpim_all_pim_routers_addr
, pim_msg
,
516 rpf
->source_nexthop
.interface
->name
)) {
518 "%s: could not send PIM message on interface %s",
520 rpf
->source_nexthop
.interface
->name
);
523 msg
= (struct pim_jp
*)pim_msg
;
524 memset(msg
, 0, sizeof(*msg
));
526 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
527 rpf
->rpf_addr
.u
.prefix4
);
529 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
533 grp
= &msg
->groups
[0];
534 curr_ptr
= (uint8_t *)grp
;
535 packet_size
= sizeof(struct pim_msg_header
);
536 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
538 4; // reserved (1) + groups (1) + holdtime (2)
540 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
541 packet_left
-= packet_size
;
549 curr_ptr
+= group_size
;
550 packet_left
-= group_size
;
551 packet_size
+= group_size
;
552 pim_msg_build_jp_groups(grp
, group
, group_size
);
554 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
555 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
557 if (PIM_DEBUG_PIM_TRACE
)
559 "%s: interface %s num_joins %u num_prunes %u",
561 rpf
->source_nexthop
.interface
->name
,
562 ntohs(grp
->joins
), ntohs(grp
->prunes
));
564 grp
= (struct pim_jp_groups
*)curr_ptr
;
565 if (packet_left
< sizeof(struct pim_jp_groups
)
566 || msg
->num_groups
== 255) {
567 pim_msg_build_header(pim_msg
, packet_size
,
568 PIM_MSG_TYPE_JOIN_PRUNE
);
569 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
570 pim_ifp
->primary_address
,
571 qpim_all_pim_routers_addr
, pim_msg
,
573 rpf
->source_nexthop
.interface
->name
)) {
575 "%s: could not send PIM message on interface %s",
577 rpf
->source_nexthop
.interface
->name
);
586 // msg->num_groups = htons (msg->num_groups);
587 pim_msg_build_header(pim_msg
, packet_size
,
588 PIM_MSG_TYPE_JOIN_PRUNE
);
589 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
590 qpim_all_pim_routers_addr
, pim_msg
,
592 rpf
->source_nexthop
.interface
->name
)) {
594 "%s: could not send PIM message on interface %s",
596 rpf
->source_nexthop
.interface
->name
);