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
*ch
= NULL
;
233 memset (&sg
, 0, sizeof (struct prefix_sg
));
234 addr_offset
= pim_parse_addr_group (&sg
,
236 if (addr_offset
< 1) {
241 remain
= pastend
- buf
;
243 char src_str
[INET_ADDRSTRLEN
];
244 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
245 zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
247 remain
, 4, src_str
, ifp
->name
);
251 msg_num_joined_sources
= ntohs(*(const uint16_t *) buf
);
253 msg_num_pruned_sources
= ntohs(*(const uint16_t *) buf
);
256 if (PIM_DEBUG_PIM_J_P
) {
257 char src_str
[INET_ADDRSTRLEN
];
258 char upstream_str
[INET_ADDRSTRLEN
];
259 char group_str
[INET_ADDRSTRLEN
];
260 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
261 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
262 upstream_str
, sizeof(upstream_str
));
263 pim_inet4_dump("<grp?>", sg
.grp
,
264 group_str
, sizeof(group_str
));
265 zlog_warn("%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
267 upstream_str
, group_str
,
268 msg_num_joined_sources
, msg_num_pruned_sources
,
272 /* Scan joined sources */
273 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
274 addr_offset
= pim_parse_addr_source (&sg
,
277 if (addr_offset
< 1) {
283 recv_join(ifp
, neigh
, msg_holdtime
,
284 msg_upstream_addr
.u
.prefix4
,
288 if (sg
.src
.s_addr
== INADDR_ANY
)
290 ch
= pim_ifchannel_find (ifp
, &sg
);
292 pim_ifchannel_set_star_g_join_state (ch
, 0, msg_source_flags
, 1);
296 /* Scan pruned sources */
297 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
298 addr_offset
= pim_parse_addr_source (&sg
,
301 if (addr_offset
< 1) {
307 recv_prune(ifp
, neigh
, msg_holdtime
,
308 msg_upstream_addr
.u
.prefix4
,
313 pim_ifchannel_set_star_g_join_state (ch
, 1, msg_source_flags
, 0);
323 * While the RFC clearly states that this is 32 bits wide, it
324 * is cheating. These fields:
325 * Encoded-Unicast format (6 bytes MIN)
326 * Encoded-Group format (8 bytes MIN)
327 * Encoded-Source format (8 bytes MIN)
328 * are *not* 32 bits wide.
330 * Nor does the RFC explicitly call out the size for:
332 * Num Groups (1 byte)
334 * Number of Joined Sources (2 bytes)
335 * Number of Pruned Sources (2 bytes)
337 * This leads to a missleading representation from casual
338 * reading and making assumptions. Be careful!
341 * 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
342 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
343 * |PIM Ver| Type | Reserved | Checksum |
344 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
345 * | Upstream Neighbor Address (Encoded-Unicast format) |
346 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347 * | Reserved | Num groups | Holdtime |
348 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
349 * | Multicast Group Address 1 (Encoded-Group format) |
350 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
351 * | Number of Joined Sources | Number of Pruned Sources |
352 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
353 * | Joined Source Address 1 (Encoded-Source format) |
354 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
357 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358 * | Joined Source Address n (Encoded-Source format) |
359 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
360 * | Pruned Source Address 1 (Encoded-Source format) |
361 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 * | Pruned Source Address n (Encoded-Source format) |
366 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 * | Multicast Group Address m (Encoded-Group format) |
368 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 * | Number of Joined Sources | Number of Pruned Sources |
370 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371 * | Joined Source Address 1 (Encoded-Source format) |
372 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
375 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376 * | Joined Source Address n (Encoded-Source format) |
377 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
378 * | Pruned Source Address 1 (Encoded-Source format) |
379 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
383 * | Pruned Source Address n (Encoded-Source format) |
384 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386 int pim_joinprune_send(struct pim_rpf
*rpf
,
389 struct pim_jp_agg_group
*group
;
390 struct pim_interface
*pim_ifp
= NULL
;
391 struct pim_jp_groups
*grp
= NULL
;
393 struct listnode
*node
, *nnode
;
394 uint8_t pim_msg
[10000];
395 uint8_t *curr_ptr
= pim_msg
;
396 bool new_packet
= true;
397 size_t packet_left
= 0;
398 size_t packet_size
= 0;
399 size_t group_size
= 0;
401 on_trace (__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
, rpf
->rpf_addr
.u
.prefix4
);
403 if (rpf
->source_nexthop
.interface
)
404 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
407 zlog_warn ("%s: RPF interface is not present", __PRETTY_FUNCTION__
);
413 zlog_warn ("%s: multicast not enabled on interface %s",
415 rpf
->source_nexthop
.interface
->name
);
419 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
))
421 if (PIM_DEBUG_PIM_J_P
) {
422 char dst_str
[INET_ADDRSTRLEN
];
423 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
424 zlog_debug("%s: upstream=%s is myself on interface %s",
426 dst_str
, rpf
->source_nexthop
.interface
->name
);
432 RFC 4601: 4.3.1. Sending Hello Messages
434 Thus, if a router needs to send a Join/Prune or Assert message on
435 an interface on which it has not yet sent a Hello message with the
436 currently configured IP address, then it MUST immediately send the
437 relevant Hello message without waiting for the Hello Timer to
438 expire, followed by the Join/Prune or Assert message.
440 pim_hello_require(rpf
->source_nexthop
.interface
);
442 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
))
446 msg
= (struct pim_jp
*)pim_msg
;
448 memset(msg
, 0, sizeof (*msg
));
450 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, rpf
->rpf_addr
.u
.prefix4
);
452 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
456 grp
= &msg
->groups
[0];
457 curr_ptr
= (uint8_t *)grp
;
458 packet_size
= sizeof (struct pim_msg_header
);
459 packet_size
+= sizeof (struct pim_encoded_ipv4_unicast
);
460 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
462 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
463 packet_left
-= packet_size
;
465 if (PIM_DEBUG_PIM_J_P
) {
466 char dst_str
[INET_ADDRSTRLEN
];
467 char grp_str
[INET_ADDRSTRLEN
];
468 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
469 pim_inet4_dump("<grp?>", group
->group
, grp_str
, sizeof(grp_str
));
470 zlog_debug("%s: sending (G)=%s to upstream=%s on interface %s",
472 grp_str
, dst_str
, rpf
->source_nexthop
.interface
->name
);
475 group_size
= pim_msg_get_jp_group_size (group
->sources
);
476 if (group_size
> packet_left
)
478 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
479 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
480 pim_ifp
->primary_address
,
481 qpim_all_pim_routers_addr
,
484 rpf
->source_nexthop
.interface
->name
)) {
485 zlog_warn("%s: could not send PIM message on interface %s",
486 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
489 msg
= (struct pim_jp
*)pim_msg
;
490 memset(msg
, 0, sizeof (*msg
));
492 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, 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
);
502 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
504 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
505 packet_left
-= packet_size
;
513 curr_ptr
+= group_size
;
514 packet_left
-= group_size
;
515 packet_size
+= group_size
;
516 pim_msg_build_jp_groups (grp
, group
, group_size
);
518 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
519 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
521 grp
= (struct pim_jp_groups
*)curr_ptr
;
522 if (packet_left
< sizeof (struct pim_jp_groups
) || msg
->num_groups
== 255)
524 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
525 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
526 pim_ifp
->primary_address
,
527 qpim_all_pim_routers_addr
,
530 rpf
->source_nexthop
.interface
->name
)) {
531 zlog_warn("%s: could not send PIM message on interface %s",
532 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
542 //msg->num_groups = htons (msg->num_groups);
543 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
544 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
545 pim_ifp
->primary_address
,
546 qpim_all_pim_routers_addr
,
549 rpf
->source_nexthop
.interface
->name
)) {
550 zlog_warn("%s: could not send PIM message on interface %s",
551 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);