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
, holdtime
,
69 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(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
)
92 sg
->src
.s_addr
= INADDR_ANY
;
95 /* Restart join expiry timer */
96 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
, sg
,
97 source_flags
, holdtime
);
100 static void recv_prune(struct interface
*ifp
, struct pim_neighbor
*neigh
,
101 uint16_t holdtime
, struct in_addr upstream
,
102 struct prefix_sg
*sg
, uint8_t source_flags
)
104 struct pim_interface
*pim_ifp
= NULL
;
106 if (PIM_DEBUG_PIM_TRACE
) {
107 char up_str
[INET_ADDRSTRLEN
];
108 char neigh_str
[INET_ADDRSTRLEN
];
109 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
110 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
,
113 "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
114 __PRETTY_FUNCTION__
, pim_str_sg_dump(sg
),
115 source_flags
& PIM_RPT_BIT_MASK
,
116 source_flags
& PIM_WILDCARD_BIT_MASK
, up_str
, holdtime
,
117 neigh_str
, ifp
->name
);
123 ++pim_ifp
->pim_ifstat_prune_recv
;
125 if ((source_flags
& PIM_RPT_BIT_MASK
)
126 && (source_flags
& PIM_WILDCARD_BIT_MASK
)) {
127 struct pim_rpf
*rp
= RP(sg
->grp
);
129 // Ignoring Prune *,G's at the moment.
130 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
133 sg
->src
.s_addr
= INADDR_ANY
;
136 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
139 int pim_joinprune_recv(struct interface
*ifp
, struct pim_neighbor
*neigh
,
140 struct in_addr src_addr
, uint8_t *tlv_buf
,
143 struct prefix msg_upstream_addr
;
144 uint8_t msg_num_groups
;
145 uint16_t msg_holdtime
;
153 pastend
= tlv_buf
+ tlv_buf_size
;
159 pim_parse_addr_ucast(&msg_upstream_addr
, buf
, pastend
- buf
);
160 if (addr_offset
< 1) {
161 char src_str
[INET_ADDRSTRLEN
];
162 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
163 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
164 __PRETTY_FUNCTION__
, src_str
, ifp
->name
);
170 Check upstream address family
172 if (msg_upstream_addr
.family
!= AF_INET
) {
173 if (PIM_DEBUG_PIM_J_P
) {
174 char src_str
[INET_ADDRSTRLEN
];
175 pim_inet4_dump("<src?>", src_addr
, src_str
,
178 "%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
179 __PRETTY_FUNCTION__
, msg_upstream_addr
.family
,
185 remain
= pastend
- buf
;
187 char src_str
[INET_ADDRSTRLEN
];
188 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
190 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
191 __PRETTY_FUNCTION__
, remain
, 4, src_str
, ifp
->name
);
195 ++buf
; /* skip reserved byte */
196 msg_num_groups
= *(const uint8_t *)buf
;
198 msg_holdtime
= ntohs(*(const uint16_t *)buf
);
202 if (PIM_DEBUG_PIM_J_P
) {
203 char src_str
[INET_ADDRSTRLEN
];
204 char upstream_str
[INET_ADDRSTRLEN
];
205 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
206 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
207 upstream_str
, sizeof(upstream_str
));
209 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
210 __PRETTY_FUNCTION__
, upstream_str
, msg_num_groups
,
211 msg_holdtime
, src_str
, ifp
->name
);
215 for (group
= 0; group
< msg_num_groups
; ++group
) {
217 uint8_t msg_source_flags
;
218 uint16_t msg_num_joined_sources
;
219 uint16_t msg_num_pruned_sources
;
221 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
222 uint8_t starg_alone
= 0;
224 memset(&sg
, 0, sizeof(struct prefix_sg
));
225 addr_offset
= pim_parse_addr_group(&sg
, buf
, pastend
- buf
);
226 if (addr_offset
< 1) {
231 remain
= pastend
- buf
;
233 char src_str
[INET_ADDRSTRLEN
];
234 pim_inet4_dump("<src?>", src_addr
, src_str
,
237 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
238 __PRETTY_FUNCTION__
, remain
, 4, src_str
,
243 msg_num_joined_sources
= ntohs(*(const uint16_t *)buf
);
245 msg_num_pruned_sources
= ntohs(*(const uint16_t *)buf
);
248 if (PIM_DEBUG_PIM_J_P
) {
249 char src_str
[INET_ADDRSTRLEN
];
250 char upstream_str
[INET_ADDRSTRLEN
];
251 char group_str
[INET_ADDRSTRLEN
];
252 pim_inet4_dump("<src?>", src_addr
, src_str
,
254 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
255 upstream_str
, sizeof(upstream_str
));
256 pim_inet4_dump("<grp?>", sg
.grp
, group_str
,
259 "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
260 __PRETTY_FUNCTION__
, upstream_str
, group_str
,
261 msg_num_joined_sources
, msg_num_pruned_sources
,
265 /* Scan joined sources */
266 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
267 addr_offset
= pim_parse_addr_source(
268 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
269 if (addr_offset
< 1) {
275 recv_join(ifp
, neigh
, msg_holdtime
,
276 msg_upstream_addr
.u
.prefix4
, &sg
,
279 if (sg
.src
.s_addr
== INADDR_ANY
) {
281 starg_ch
= pim_ifchannel_find(ifp
, &sg
);
283 pim_ifchannel_set_star_g_join_state(
284 starg_ch
, 0, msg_source_flags
,
289 /* Scan pruned sources */
290 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
291 addr_offset
= pim_parse_addr_source(
292 &sg
, &msg_source_flags
, buf
, pastend
- buf
);
293 if (addr_offset
< 1) {
297 sg_ch
= pim_ifchannel_find(ifp
, &sg
);
301 recv_prune(ifp
, neigh
, msg_holdtime
,
302 msg_upstream_addr
.u
.prefix4
, &sg
,
305 /* Received SG-RPT Prune delete oif from specific S,G */
306 if (starg_ch
&& sg_ch
307 && (msg_source_flags
& PIM_RPT_BIT_MASK
)
308 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
)) {
309 struct pim_upstream
*up
= sg_ch
->upstream
;
310 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
314 "%s: SGRpt flag is set, del inherit oif from up %s",
320 PIM_OIF_FLAG_PROTO_STAR
);
325 pim_ifchannel_set_star_g_join_state(
326 starg_ch
, 1, msg_source_flags
, 0, starg_alone
);
336 * While the RFC clearly states that this is 32 bits wide, it
337 * is cheating. These fields:
338 * Encoded-Unicast format (6 bytes MIN)
339 * Encoded-Group format (8 bytes MIN)
340 * Encoded-Source format (8 bytes MIN)
341 * are *not* 32 bits wide.
343 * Nor does the RFC explicitly call out the size for:
345 * Num Groups (1 byte)
347 * Number of Joined Sources (2 bytes)
348 * Number of Pruned Sources (2 bytes)
350 * This leads to a missleading representation from casual
351 * reading and making assumptions. Be careful!
354 * 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
355 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356 * |PIM Ver| Type | Reserved | Checksum |
357 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358 * | Upstream Neighbor Address (Encoded-Unicast format) |
359 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
360 * | Reserved | Num groups | Holdtime |
361 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
362 * | Multicast Group Address 1 (Encoded-Group format) |
363 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364 * | Number of Joined Sources | Number of Pruned Sources |
365 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
366 * | Joined Source Address 1 (Encoded-Source format) |
367 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
370 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371 * | Joined Source Address n (Encoded-Source format) |
372 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373 * | Pruned Source Address 1 (Encoded-Source format) |
374 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378 * | Pruned Source Address n (Encoded-Source format) |
379 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 * | Multicast Group Address m (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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 int pim_joinprune_send(struct pim_rpf
*rpf
, struct list
*groups
)
401 struct pim_jp_agg_group
*group
;
402 struct pim_interface
*pim_ifp
= NULL
;
403 struct pim_jp_groups
*grp
= NULL
;
405 struct listnode
*node
, *nnode
;
406 uint8_t pim_msg
[10000];
407 uint8_t *curr_ptr
= pim_msg
;
408 bool new_packet
= true;
409 size_t packet_left
= 0;
410 size_t packet_size
= 0;
411 size_t group_size
= 0;
413 on_trace(__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
,
414 rpf
->rpf_addr
.u
.prefix4
);
416 if (rpf
->source_nexthop
.interface
)
417 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
419 zlog_warn("%s: RPF interface is not present",
420 __PRETTY_FUNCTION__
);
425 zlog_warn("%s: multicast not enabled on interface %s",
427 rpf
->source_nexthop
.interface
->name
);
431 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
)) {
432 if (PIM_DEBUG_PIM_J_P
) {
433 char dst_str
[INET_ADDRSTRLEN
];
434 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
435 dst_str
, sizeof(dst_str
));
436 zlog_debug("%s: upstream=%s is myself on interface %s",
437 __PRETTY_FUNCTION__
, dst_str
,
438 rpf
->source_nexthop
.interface
->name
);
444 RFC 4601: 4.3.1. Sending Hello Messages
446 Thus, if a router needs to send a Join/Prune or Assert message on
447 an interface on which it has not yet sent a Hello message with the
448 currently configured IP address, then it MUST immediately send the
449 relevant Hello message without waiting for the Hello Timer to
450 expire, followed by the Join/Prune or Assert message.
452 pim_hello_require(rpf
->source_nexthop
.interface
);
454 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
)) {
456 msg
= (struct pim_jp
*)pim_msg
;
458 memset(msg
, 0, sizeof(*msg
));
460 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
461 rpf
->rpf_addr
.u
.prefix4
);
463 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
467 grp
= &msg
->groups
[0];
468 curr_ptr
= (uint8_t *)grp
;
469 packet_size
= sizeof(struct pim_msg_header
);
470 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
472 4; // reserved (1) + groups (1) + holdtime (2)
474 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
475 packet_left
-= packet_size
;
477 if (PIM_DEBUG_PIM_J_P
) {
478 char dst_str
[INET_ADDRSTRLEN
];
479 char grp_str
[INET_ADDRSTRLEN
];
480 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
,
481 dst_str
, sizeof(dst_str
));
482 pim_inet4_dump("<grp?>", group
->group
, grp_str
,
485 "%s: sending (G)=%s to upstream=%s on interface %s",
486 __PRETTY_FUNCTION__
, grp_str
, dst_str
,
487 rpf
->source_nexthop
.interface
->name
);
490 group_size
= pim_msg_get_jp_group_size(group
->sources
);
491 if (group_size
> packet_left
) {
492 pim_msg_build_header(pim_msg
, packet_size
,
493 PIM_MSG_TYPE_JOIN_PRUNE
);
494 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
495 pim_ifp
->primary_address
,
496 qpim_all_pim_routers_addr
, pim_msg
,
498 rpf
->source_nexthop
.interface
->name
)) {
500 "%s: could not send PIM message on interface %s",
502 rpf
->source_nexthop
.interface
->name
);
505 msg
= (struct pim_jp
*)pim_msg
;
506 memset(msg
, 0, sizeof(*msg
));
508 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg
->addr
,
509 rpf
->rpf_addr
.u
.prefix4
);
511 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
515 grp
= &msg
->groups
[0];
516 curr_ptr
= (uint8_t *)grp
;
517 packet_size
= sizeof(struct pim_msg_header
);
518 packet_size
+= sizeof(struct pim_encoded_ipv4_unicast
);
520 4; // reserved (1) + groups (1) + holdtime (2)
522 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
523 packet_left
-= packet_size
;
531 curr_ptr
+= group_size
;
532 packet_left
-= group_size
;
533 packet_size
+= group_size
;
534 pim_msg_build_jp_groups(grp
, group
, group_size
);
536 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
537 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
539 if (PIM_DEBUG_PIM_TRACE
)
541 "%s: interface %s num_joins %u num_prunes %u",
543 rpf
->source_nexthop
.interface
->name
,
544 ntohs(grp
->joins
), ntohs(grp
->prunes
));
546 grp
= (struct pim_jp_groups
*)curr_ptr
;
547 if (packet_left
< sizeof(struct pim_jp_groups
)
548 || msg
->num_groups
== 255) {
549 pim_msg_build_header(pim_msg
, packet_size
,
550 PIM_MSG_TYPE_JOIN_PRUNE
);
551 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
552 pim_ifp
->primary_address
,
553 qpim_all_pim_routers_addr
, pim_msg
,
555 rpf
->source_nexthop
.interface
->name
)) {
557 "%s: could not send PIM message on interface %s",
559 rpf
->source_nexthop
.interface
->name
);
568 // msg->num_groups = htons (msg->num_groups);
569 pim_msg_build_header(pim_msg
, packet_size
,
570 PIM_MSG_TYPE_JOIN_PRUNE
);
571 if (pim_msg_send(pim_ifp
->pim_sock_fd
, pim_ifp
->primary_address
,
572 qpim_all_pim_routers_addr
, pim_msg
,
574 rpf
->source_nexthop
.interface
->name
)) {
576 "%s: could not send PIM message on interface %s",
578 rpf
->source_nexthop
.interface
->name
);