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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
36 #include "pim_iface.h"
37 #include "pim_hello.h"
38 #include "pim_ifchannel.h"
41 #include "pim_jp_agg.h"
44 on_trace (const char *label
,
45 struct interface
*ifp
, struct in_addr src
)
47 if (PIM_DEBUG_PIM_TRACE
) {
48 char src_str
[INET_ADDRSTRLEN
];
49 pim_inet4_dump("<src?>", src
, src_str
, sizeof(src_str
));
50 zlog_debug("%s: from %s on %s",
51 label
, src_str
, ifp
->name
);
55 static void recv_join(struct interface
*ifp
,
56 struct pim_neighbor
*neigh
,
58 struct in_addr upstream
,
62 struct pim_interface
*pim_ifp
= NULL
;
64 if (PIM_DEBUG_PIM_TRACE
) {
65 char up_str
[INET_ADDRSTRLEN
];
66 char neigh_str
[INET_ADDRSTRLEN
];
67 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
68 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
, sizeof(neigh_str
));
69 zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
72 source_flags
& PIM_RPT_BIT_MASK
,
73 source_flags
& PIM_WILDCARD_BIT_MASK
,
74 up_str
, holdtime
, neigh_str
, ifp
->name
);
80 ++pim_ifp
->pim_ifstat_join_recv
;
83 * If the RPT and WC are set it's a (*,G)
84 * and the source is the RP
86 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
87 (source_flags
& PIM_WILDCARD_BIT_MASK
))
89 struct pim_rpf
*rp
= RP (sg
->grp
);
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
)
98 sg
->src
.s_addr
= INADDR_ANY
;
101 /* Restart join expiry timer */
102 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
,
103 sg
, source_flags
, holdtime
);
107 static void recv_prune(struct interface
*ifp
,
108 struct pim_neighbor
*neigh
,
110 struct in_addr upstream
,
111 struct prefix_sg
*sg
,
112 uint8_t source_flags
)
114 struct pim_interface
*pim_ifp
= NULL
;
116 if (PIM_DEBUG_PIM_TRACE
) {
117 char up_str
[INET_ADDRSTRLEN
];
118 char neigh_str
[INET_ADDRSTRLEN
];
119 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
120 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
, sizeof(neigh_str
));
121 zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
123 pim_str_sg_dump (sg
),
124 source_flags
& PIM_RPT_BIT_MASK
,
125 source_flags
& PIM_WILDCARD_BIT_MASK
,
126 up_str
, holdtime
, neigh_str
, ifp
->name
);
132 ++pim_ifp
->pim_ifstat_prune_recv
;
134 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
135 (source_flags
& PIM_WILDCARD_BIT_MASK
))
137 struct pim_rpf
*rp
= RP (sg
->grp
);
139 // Ignoring Prune *,G's at the moment.
140 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
143 sg
->src
.s_addr
= INADDR_ANY
;
146 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
150 int pim_joinprune_recv(struct interface
*ifp
,
151 struct pim_neighbor
*neigh
,
152 struct in_addr src_addr
,
153 uint8_t *tlv_buf
, int tlv_buf_size
)
155 struct prefix msg_upstream_addr
;
156 uint8_t msg_num_groups
;
157 uint16_t msg_holdtime
;
165 pastend
= tlv_buf
+ tlv_buf_size
;
170 addr_offset
= pim_parse_addr_ucast (&msg_upstream_addr
,
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",
183 Check upstream address family
185 if (msg_upstream_addr
.family
!= AF_INET
) {
186 if (PIM_DEBUG_PIM_J_P
) {
187 char src_str
[INET_ADDRSTRLEN
];
188 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
189 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
191 msg_upstream_addr
.family
, src_str
, ifp
->name
);
196 remain
= pastend
- buf
;
198 char src_str
[INET_ADDRSTRLEN
];
199 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
200 zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
202 remain
, 4, src_str
, ifp
->name
);
206 ++buf
; /* skip reserved byte */
207 msg_num_groups
= *(const uint8_t *) buf
;
209 msg_holdtime
= ntohs(*(const uint16_t *) buf
);
213 if (PIM_DEBUG_PIM_J_P
) {
214 char src_str
[INET_ADDRSTRLEN
];
215 char upstream_str
[INET_ADDRSTRLEN
];
216 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
217 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
218 upstream_str
, sizeof(upstream_str
));
219 zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
221 upstream_str
, msg_num_groups
, msg_holdtime
,
226 for (group
= 0; group
< msg_num_groups
; ++group
) {
228 uint8_t msg_source_flags
;
229 uint16_t msg_num_joined_sources
;
230 uint16_t msg_num_pruned_sources
;
232 struct pim_ifchannel
*ch
= NULL
;
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
)
291 ch
= pim_ifchannel_find (ifp
, &sg
);
293 pim_ifchannel_set_star_g_join_state (ch
, 0, msg_source_flags
, 1);
297 /* Scan pruned sources */
298 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
299 addr_offset
= pim_parse_addr_source (&sg
,
302 if (addr_offset
< 1) {
308 recv_prune(ifp
, neigh
, msg_holdtime
,
309 msg_upstream_addr
.u
.prefix4
,
314 pim_ifchannel_set_star_g_join_state (ch
, 1, msg_source_flags
, 0);
324 * While the RFC clearly states that this is 32 bits wide, it
325 * is cheating. These fields:
326 * Encoded-Unicast format (6 bytes MIN)
327 * Encoded-Group format (8 bytes MIN)
328 * Encoded-Source format (8 bytes MIN)
329 * are *not* 32 bits wide.
331 * Nor does the RFC explicitly call out the size for:
333 * Num Groups (1 byte)
335 * Number of Joined Sources (2 bytes)
336 * Number of Pruned Sources (2 bytes)
338 * This leads to a missleading representation from casual
339 * reading and making assumptions. Be careful!
342 * 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
343 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
344 * |PIM Ver| Type | Reserved | Checksum |
345 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
346 * | Upstream Neighbor Address (Encoded-Unicast format) |
347 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
348 * | Reserved | Num groups | Holdtime |
349 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
350 * | Multicast Group Address 1 (Encoded-Group format) |
351 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352 * | Number of Joined Sources | Number of Pruned Sources |
353 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
354 * | Joined Source Address 1 (Encoded-Source format) |
355 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359 * | Joined Source Address n (Encoded-Source format) |
360 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
361 * | Pruned Source Address 1 (Encoded-Source format) |
362 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
366 * | Pruned Source Address n (Encoded-Source format) |
367 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
368 * | Multicast Group Address m (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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
387 int pim_joinprune_send(struct pim_rpf
*rpf
,
390 struct pim_jp_agg_group
*group
;
391 struct pim_interface
*pim_ifp
= NULL
;
392 struct pim_jp_groups
*grp
= NULL
;
394 struct listnode
*node
, *nnode
;
395 uint8_t pim_msg
[10000];
396 uint8_t *curr_ptr
= pim_msg
;
397 bool new_packet
= true;
398 size_t packet_left
= 0;
399 size_t packet_size
= 0;
400 size_t group_size
= 0;
402 on_trace (__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
, rpf
->rpf_addr
.u
.prefix4
);
404 if (rpf
->source_nexthop
.interface
)
405 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
408 zlog_warn ("%s: RPF interface is not present", __PRETTY_FUNCTION__
);
414 zlog_warn ("%s: multicast not enabled on interface %s",
416 rpf
->source_nexthop
.interface
->name
);
420 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
))
422 if (PIM_DEBUG_PIM_J_P
) {
423 char dst_str
[INET_ADDRSTRLEN
];
424 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
425 zlog_debug("%s: upstream=%s is myself on interface %s",
427 dst_str
, rpf
->source_nexthop
.interface
->name
);
433 RFC 4601: 4.3.1. Sending Hello Messages
435 Thus, if a router needs to send a Join/Prune or Assert message on
436 an interface on which it has not yet sent a Hello message with the
437 currently configured IP address, then it MUST immediately send the
438 relevant Hello message without waiting for the Hello Timer to
439 expire, followed by the Join/Prune or Assert message.
441 pim_hello_require(rpf
->source_nexthop
.interface
);
443 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
))
447 msg
= (struct pim_jp
*)pim_msg
;
449 memset(msg
, 0, sizeof (*msg
));
451 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, rpf
->rpf_addr
.u
.prefix4
);
453 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
457 grp
= &msg
->groups
[0];
458 curr_ptr
= (uint8_t *)grp
;
459 packet_size
= sizeof (struct pim_msg_header
);
460 packet_size
+= sizeof (struct pim_encoded_ipv4_unicast
);
461 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
463 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
464 packet_left
-= packet_size
;
466 if (PIM_DEBUG_PIM_J_P
) {
467 char dst_str
[INET_ADDRSTRLEN
];
468 char grp_str
[INET_ADDRSTRLEN
];
469 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
470 pim_inet4_dump("<grp?>", group
->group
, grp_str
, sizeof(grp_str
));
471 zlog_debug("%s: sending (G)=%s to upstream=%s on interface %s",
473 grp_str
, dst_str
, rpf
->source_nexthop
.interface
->name
);
476 group_size
= pim_msg_get_jp_group_size (group
->sources
);
477 if (group_size
> packet_left
)
479 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
480 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
481 pim_ifp
->primary_address
,
482 qpim_all_pim_routers_addr
,
485 rpf
->source_nexthop
.interface
->name
)) {
486 zlog_warn("%s: could not send PIM message on interface %s",
487 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
490 msg
= (struct pim_jp
*)pim_msg
;
491 memset(msg
, 0, sizeof (*msg
));
493 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, 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
);
503 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
505 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
506 packet_left
-= packet_size
;
514 curr_ptr
+= group_size
;
515 packet_left
-= group_size
;
516 packet_size
+= group_size
;
517 pim_msg_build_jp_groups (grp
, group
, group_size
);
519 pim_ifp
->pim_ifstat_join_send
+= ntohs(grp
->joins
);
520 pim_ifp
->pim_ifstat_prune_send
+= ntohs(grp
->prunes
);
522 grp
= (struct pim_jp_groups
*)curr_ptr
;
523 if (packet_left
< sizeof (struct pim_jp_groups
) || msg
->num_groups
== 255)
525 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
526 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
527 pim_ifp
->primary_address
,
528 qpim_all_pim_routers_addr
,
531 rpf
->source_nexthop
.interface
->name
)) {
532 zlog_warn("%s: could not send PIM message on interface %s",
533 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
543 //msg->num_groups = htons (msg->num_groups);
544 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
545 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
546 pim_ifp
->primary_address
,
547 qpim_all_pim_routers_addr
,
550 rpf
->source_nexthop
.interface
->name
)) {
551 zlog_warn("%s: could not send PIM message on interface %s",
552 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);