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 on_trace (const char *label
,
44 struct interface
*ifp
, struct in_addr src
)
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",
50 label
, src_str
, ifp
->name
);
54 static void recv_join(struct interface
*ifp
,
55 struct pim_neighbor
*neigh
,
57 struct in_addr upstream
,
61 struct pim_interface
*pim_ifp
= NULL
;
63 if (PIM_DEBUG_PIM_TRACE
) {
64 char up_str
[INET_ADDRSTRLEN
];
65 char neigh_str
[INET_ADDRSTRLEN
];
66 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
67 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
, sizeof(neigh_str
));
68 zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
71 source_flags
& PIM_RPT_BIT_MASK
,
72 source_flags
& PIM_WILDCARD_BIT_MASK
,
73 up_str
, 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
))
88 struct pim_rpf
*rp
= RP (sg
->grp
);
91 * If the RP sent in the message is not
92 * our RP for the group, drop the message
94 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
97 sg
->src
.s_addr
= INADDR_ANY
;
100 /* Restart join expiry timer */
101 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
,
102 sg
, source_flags
, holdtime
);
106 static void recv_prune(struct interface
*ifp
,
107 struct pim_neighbor
*neigh
,
109 struct in_addr upstream
,
110 struct prefix_sg
*sg
,
111 uint8_t source_flags
)
113 struct pim_interface
*pim_ifp
= NULL
;
115 if (PIM_DEBUG_PIM_TRACE
) {
116 char up_str
[INET_ADDRSTRLEN
];
117 char neigh_str
[INET_ADDRSTRLEN
];
118 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
119 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
, sizeof(neigh_str
));
120 zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
122 pim_str_sg_dump (sg
),
123 source_flags
& PIM_RPT_BIT_MASK
,
124 source_flags
& PIM_WILDCARD_BIT_MASK
,
125 up_str
, holdtime
, neigh_str
, ifp
->name
);
131 ++pim_ifp
->pim_ifstat_prune_recv
;
133 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
134 (source_flags
& PIM_WILDCARD_BIT_MASK
))
136 struct pim_rpf
*rp
= RP (sg
->grp
);
138 // Ignoring Prune *,G's at the moment.
139 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
142 sg
->src
.s_addr
= INADDR_ANY
;
145 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
149 int pim_joinprune_recv(struct interface
*ifp
,
150 struct pim_neighbor
*neigh
,
151 struct in_addr src_addr
,
152 uint8_t *tlv_buf
, int tlv_buf_size
)
154 struct prefix msg_upstream_addr
;
155 uint8_t msg_num_groups
;
156 uint16_t msg_holdtime
;
164 pastend
= tlv_buf
+ tlv_buf_size
;
169 addr_offset
= pim_parse_addr_ucast (&msg_upstream_addr
,
171 if (addr_offset
< 1) {
172 char src_str
[INET_ADDRSTRLEN
];
173 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
174 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
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
, sizeof(src_str
));
188 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
190 msg_upstream_addr
.family
, src_str
, ifp
->name
);
195 remain
= pastend
- buf
;
197 char src_str
[INET_ADDRSTRLEN
];
198 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
199 zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
201 remain
, 4, src_str
, ifp
->name
);
205 ++buf
; /* skip reserved byte */
206 msg_num_groups
= *(const uint8_t *) buf
;
208 msg_holdtime
= ntohs(*(const uint16_t *) buf
);
212 if (PIM_DEBUG_PIM_J_P
) {
213 char src_str
[INET_ADDRSTRLEN
];
214 char upstream_str
[INET_ADDRSTRLEN
];
215 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
216 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
217 upstream_str
, sizeof(upstream_str
));
218 zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
220 upstream_str
, msg_num_groups
, msg_holdtime
,
225 for (group
= 0; group
< msg_num_groups
; ++group
) {
227 uint8_t msg_source_flags
;
228 uint16_t msg_num_joined_sources
;
229 uint16_t msg_num_pruned_sources
;
231 struct pim_ifchannel
*starg_ch
= NULL
, *sg_ch
= NULL
;
232 uint8_t starg_alone
= 0;
234 memset (&sg
, 0, sizeof (struct prefix_sg
));
235 addr_offset
= pim_parse_addr_group (&sg
,
237 if (addr_offset
< 1) {
242 remain
= pastend
- buf
;
244 char src_str
[INET_ADDRSTRLEN
];
245 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
246 zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
248 remain
, 4, src_str
, ifp
->name
);
252 msg_num_joined_sources
= ntohs(*(const uint16_t *) buf
);
254 msg_num_pruned_sources
= ntohs(*(const uint16_t *) buf
);
257 if (PIM_DEBUG_PIM_J_P
) {
258 char src_str
[INET_ADDRSTRLEN
];
259 char upstream_str
[INET_ADDRSTRLEN
];
260 char group_str
[INET_ADDRSTRLEN
];
261 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
262 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
263 upstream_str
, sizeof(upstream_str
));
264 pim_inet4_dump("<grp?>", sg
.grp
,
265 group_str
, sizeof(group_str
));
266 zlog_warn("%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
268 upstream_str
, group_str
,
269 msg_num_joined_sources
, msg_num_pruned_sources
,
273 /* Scan joined sources */
274 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
275 addr_offset
= pim_parse_addr_source (&sg
,
278 if (addr_offset
< 1) {
284 recv_join(ifp
, neigh
, msg_holdtime
,
285 msg_upstream_addr
.u
.prefix4
,
289 if (sg
.src
.s_addr
== INADDR_ANY
)
292 starg_ch
= pim_ifchannel_find (ifp
, &sg
);
294 pim_ifchannel_set_star_g_join_state (starg_ch
, 0, msg_source_flags
, 1, starg_alone
);
298 /* Scan pruned sources */
299 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
300 addr_offset
= pim_parse_addr_source (&sg
,
303 if (addr_offset
< 1) {
307 sg_ch
= pim_ifchannel_find (ifp
, &sg
);
311 recv_prune(ifp
, neigh
, msg_holdtime
,
312 msg_upstream_addr
.u
.prefix4
,
316 /* Received SG-RPT Prune delete oif from specific S,G */
317 if (starg_ch
&& sg_ch
&& (msg_source_flags
& PIM_RPT_BIT_MASK
)
318 && !(msg_source_flags
& PIM_WILDCARD_BIT_MASK
))
320 struct pim_upstream
*up
= sg_ch
->upstream
;
321 PIM_IF_FLAG_SET_S_G_RPT(sg_ch
->flags
);
325 zlog_debug ("%s: SGRpt flag is set, del inherit oif from up %s",
326 __PRETTY_FUNCTION__
, up
->sg_str
);
327 pim_channel_del_oif (up
->channel_oil
, starg_ch
->interface
, PIM_OIF_FLAG_PROTO_STAR
);
332 pim_ifchannel_set_star_g_join_state (starg_ch
, 1, msg_source_flags
, 0, starg_alone
);
342 * While the RFC clearly states that this is 32 bits wide, it
343 * is cheating. These fields:
344 * Encoded-Unicast format (6 bytes MIN)
345 * Encoded-Group format (8 bytes MIN)
346 * Encoded-Source format (8 bytes MIN)
347 * are *not* 32 bits wide.
349 * Nor does the RFC explicitly call out the size for:
351 * Num Groups (1 byte)
353 * Number of Joined Sources (2 bytes)
354 * Number of Pruned Sources (2 bytes)
356 * This leads to a missleading representation from casual
357 * reading and making assumptions. Be careful!
360 * 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
361 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
362 * |PIM Ver| Type | Reserved | Checksum |
363 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364 * | Upstream Neighbor Address (Encoded-Unicast format) |
365 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
366 * | Reserved | Num groups | Holdtime |
367 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
368 * | Multicast Group Address 1 (Encoded-Group format) |
369 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
370 * | Number of Joined Sources | Number of Pruned Sources |
371 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
372 * | Joined Source Address 1 (Encoded-Source format) |
373 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
377 * | Joined Source Address n (Encoded-Source format) |
378 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
379 * | Pruned Source Address 1 (Encoded-Source format) |
380 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 * | Pruned Source Address n (Encoded-Source format) |
385 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386 * | Multicast Group Address m (Encoded-Group format) |
387 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388 * | Number of Joined Sources | Number of Pruned Sources |
389 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390 * | Joined Source Address 1 (Encoded-Source format) |
391 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * | Joined Source Address n (Encoded-Source format) |
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * | Pruned Source Address 1 (Encoded-Source format) |
398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
402 * | Pruned Source Address n (Encoded-Source format) |
403 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
405 int pim_joinprune_send(struct pim_rpf
*rpf
,
408 struct pim_jp_agg_group
*group
;
409 struct pim_interface
*pim_ifp
= NULL
;
410 struct pim_jp_groups
*grp
= NULL
;
412 struct listnode
*node
, *nnode
;
413 uint8_t pim_msg
[10000];
414 uint8_t *curr_ptr
= pim_msg
;
415 bool new_packet
= true;
416 size_t packet_left
= 0;
417 size_t packet_size
= 0;
418 size_t group_size
= 0;
420 on_trace (__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
, rpf
->rpf_addr
.u
.prefix4
);
422 if (rpf
->source_nexthop
.interface
)
423 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
426 zlog_warn ("%s: RPF interface is not present", __PRETTY_FUNCTION__
);
432 zlog_warn ("%s: multicast not enabled on interface %s",
434 rpf
->source_nexthop
.interface
->name
);
438 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
))
440 if (PIM_DEBUG_PIM_J_P
) {
441 char dst_str
[INET_ADDRSTRLEN
];
442 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
443 zlog_debug("%s: upstream=%s is myself on interface %s",
445 dst_str
, rpf
->source_nexthop
.interface
->name
);
451 RFC 4601: 4.3.1. Sending Hello Messages
453 Thus, if a router needs to send a Join/Prune or Assert message on
454 an interface on which it has not yet sent a Hello message with the
455 currently configured IP address, then it MUST immediately send the
456 relevant Hello message without waiting for the Hello Timer to
457 expire, followed by the Join/Prune or Assert message.
459 pim_hello_require(rpf
->source_nexthop
.interface
);
461 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
))
465 msg
= (struct pim_jp
*)pim_msg
;
467 memset(msg
, 0, sizeof (*msg
));
469 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, rpf
->rpf_addr
.u
.prefix4
);
471 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
475 grp
= &msg
->groups
[0];
476 curr_ptr
= (uint8_t *)grp
;
477 packet_size
= sizeof (struct pim_msg_header
);
478 packet_size
+= sizeof (struct pim_encoded_ipv4_unicast
);
479 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
481 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
482 packet_left
-= packet_size
;
484 if (PIM_DEBUG_PIM_J_P
) {
485 char dst_str
[INET_ADDRSTRLEN
];
486 char grp_str
[INET_ADDRSTRLEN
];
487 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
488 pim_inet4_dump("<grp?>", group
->group
, grp_str
, sizeof(grp_str
));
489 zlog_debug("%s: sending (G)=%s to upstream=%s on interface %s",
491 grp_str
, dst_str
, rpf
->source_nexthop
.interface
->name
);
494 group_size
= pim_msg_get_jp_group_size (group
->sources
);
495 if (group_size
> packet_left
)
497 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
498 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
499 pim_ifp
->primary_address
,
500 qpim_all_pim_routers_addr
,
503 rpf
->source_nexthop
.interface
->name
)) {
504 zlog_warn("%s: could not send PIM message on interface %s",
505 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
508 msg
= (struct pim_jp
*)pim_msg
;
509 memset(msg
, 0, sizeof (*msg
));
511 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, rpf
->rpf_addr
.u
.prefix4
);
513 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
517 grp
= &msg
->groups
[0];
518 curr_ptr
= (uint8_t *)grp
;
519 packet_size
= sizeof (struct pim_msg_header
);
520 packet_size
+= sizeof (struct pim_encoded_ipv4_unicast
);
521 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
523 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
524 packet_left
-= packet_size
;
532 curr_ptr
+= group_size
;
533 packet_left
-= group_size
;
534 packet_size
+= group_size
;
535 pim_msg_build_jp_groups (grp
, group
, group_size
);
537 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
538 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
540 if (PIM_DEBUG_PIM_TRACE
)
541 zlog_debug ("%s: interface %s num_joins %u num_prunes %u", __PRETTY_FUNCTION__
,
542 rpf
->source_nexthop
.interface
->name
, ntohs(grp
->joins
), ntohs (grp
->prunes
));
544 grp
= (struct pim_jp_groups
*)curr_ptr
;
545 if (packet_left
< sizeof (struct pim_jp_groups
) || msg
->num_groups
== 255)
547 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
548 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
549 pim_ifp
->primary_address
,
550 qpim_all_pim_routers_addr
,
553 rpf
->source_nexthop
.interface
->name
)) {
554 zlog_warn("%s: could not send PIM message on interface %s",
555 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
565 //msg->num_groups = htons (msg->num_groups);
566 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
567 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
568 pim_ifp
->primary_address
,
569 qpim_all_pim_routers_addr
,
572 rpf
->source_nexthop
.interface
->name
)) {
573 zlog_warn("%s: could not send PIM message on interface %s",
574 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);