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 if (PIM_DEBUG_PIM_TRACE
) {
63 char up_str
[INET_ADDRSTRLEN
];
64 char neigh_str
[INET_ADDRSTRLEN
];
65 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
66 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
, sizeof(neigh_str
));
67 zlog_warn("%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
70 source_flags
& PIM_RPT_BIT_MASK
,
71 source_flags
& PIM_WILDCARD_BIT_MASK
,
72 up_str
, holdtime
, neigh_str
, ifp
->name
);
76 * If the RPT and WC are set it's a (*,G)
77 * and the source is the RP
79 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
80 (source_flags
& PIM_WILDCARD_BIT_MASK
))
82 struct pim_rpf
*rp
= RP (sg
->grp
);
85 * If the RP sent in the message is not
86 * our RP for the group, drop the message
88 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
91 sg
->src
.s_addr
= INADDR_ANY
;
94 /* Restart join expiry timer */
95 pim_ifchannel_join_add(ifp
, neigh
->source_addr
, upstream
,
96 sg
, source_flags
, holdtime
);
100 static void recv_prune(struct interface
*ifp
,
101 struct pim_neighbor
*neigh
,
103 struct in_addr upstream
,
104 struct prefix_sg
*sg
,
105 uint8_t source_flags
)
107 if (PIM_DEBUG_PIM_TRACE
) {
108 char up_str
[INET_ADDRSTRLEN
];
109 char neigh_str
[INET_ADDRSTRLEN
];
110 pim_inet4_dump("<upstream?>", upstream
, up_str
, sizeof(up_str
));
111 pim_inet4_dump("<neigh?>", neigh
->source_addr
, neigh_str
, sizeof(neigh_str
));
112 zlog_warn("%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
114 pim_str_sg_dump (sg
),
115 source_flags
& PIM_RPT_BIT_MASK
,
116 source_flags
& PIM_WILDCARD_BIT_MASK
,
117 up_str
, holdtime
, neigh_str
, ifp
->name
);
120 if ((source_flags
& PIM_RPT_BIT_MASK
) &&
121 (source_flags
& PIM_WILDCARD_BIT_MASK
))
123 struct pim_rpf
*rp
= RP (sg
->grp
);
125 // Ignoring Prune *,G's at the moment.
126 if (sg
->src
.s_addr
!= rp
->rpf_addr
.u
.prefix4
.s_addr
)
129 sg
->src
.s_addr
= INADDR_ANY
;
132 pim_ifchannel_prune(ifp
, upstream
, sg
, source_flags
, holdtime
);
136 int pim_joinprune_recv(struct interface
*ifp
,
137 struct pim_neighbor
*neigh
,
138 struct in_addr src_addr
,
139 uint8_t *tlv_buf
, int tlv_buf_size
)
141 struct prefix msg_upstream_addr
;
142 uint8_t msg_num_groups
;
143 uint16_t msg_holdtime
;
151 pastend
= tlv_buf
+ tlv_buf_size
;
156 addr_offset
= pim_parse_addr_ucast (&msg_upstream_addr
,
158 if (addr_offset
< 1) {
159 char src_str
[INET_ADDRSTRLEN
];
160 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
161 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
169 Check upstream address family
171 if (msg_upstream_addr
.family
!= AF_INET
) {
172 if (PIM_DEBUG_PIM_J_P
) {
173 char src_str
[INET_ADDRSTRLEN
];
174 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
175 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
177 msg_upstream_addr
.family
, src_str
, ifp
->name
);
182 remain
= pastend
- buf
;
184 char src_str
[INET_ADDRSTRLEN
];
185 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
186 zlog_warn("%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
188 remain
, 4, src_str
, ifp
->name
);
192 ++buf
; /* skip reserved byte */
193 msg_num_groups
= *(const uint8_t *) buf
;
195 msg_holdtime
= ntohs(*(const uint16_t *) buf
);
199 if (PIM_DEBUG_PIM_J_P
) {
200 char src_str
[INET_ADDRSTRLEN
];
201 char upstream_str
[INET_ADDRSTRLEN
];
202 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
203 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
204 upstream_str
, sizeof(upstream_str
));
205 zlog_debug ("%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
207 upstream_str
, msg_num_groups
, msg_holdtime
,
212 for (group
= 0; group
< msg_num_groups
; ++group
) {
214 uint8_t msg_source_flags
;
215 uint16_t msg_num_joined_sources
;
216 uint16_t msg_num_pruned_sources
;
218 struct pim_ifchannel
*ch
= NULL
;
220 memset (&sg
, 0, sizeof (struct prefix_sg
));
221 addr_offset
= pim_parse_addr_group (&sg
,
223 if (addr_offset
< 1) {
228 remain
= pastend
- buf
;
230 char src_str
[INET_ADDRSTRLEN
];
231 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
232 zlog_warn("%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
234 remain
, 4, src_str
, ifp
->name
);
238 msg_num_joined_sources
= ntohs(*(const uint16_t *) buf
);
240 msg_num_pruned_sources
= ntohs(*(const uint16_t *) buf
);
243 if (PIM_DEBUG_PIM_J_P
) {
244 char src_str
[INET_ADDRSTRLEN
];
245 char upstream_str
[INET_ADDRSTRLEN
];
246 char group_str
[INET_ADDRSTRLEN
];
247 pim_inet4_dump("<src?>", src_addr
, src_str
, sizeof(src_str
));
248 pim_inet4_dump("<addr?>", msg_upstream_addr
.u
.prefix4
,
249 upstream_str
, sizeof(upstream_str
));
250 pim_inet4_dump("<grp?>", sg
.grp
,
251 group_str
, sizeof(group_str
));
252 zlog_warn("%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
254 upstream_str
, group_str
,
255 msg_num_joined_sources
, msg_num_pruned_sources
,
259 /* Scan joined sources */
260 for (source
= 0; source
< msg_num_joined_sources
; ++source
) {
261 addr_offset
= pim_parse_addr_source (&sg
,
264 if (addr_offset
< 1) {
270 recv_join(ifp
, neigh
, msg_holdtime
,
271 msg_upstream_addr
.u
.prefix4
,
275 if (sg
.src
.s_addr
== INADDR_ANY
)
277 ch
= pim_ifchannel_find (ifp
, &sg
);
279 pim_ifchannel_set_star_g_join_state (ch
, 0);
283 /* Scan pruned sources */
284 for (source
= 0; source
< msg_num_pruned_sources
; ++source
) {
285 addr_offset
= pim_parse_addr_source (&sg
,
288 if (addr_offset
< 1) {
294 recv_prune(ifp
, neigh
, msg_holdtime
,
295 msg_upstream_addr
.u
.prefix4
,
300 pim_ifchannel_set_star_g_join_state (ch
, 1);
310 * While the RFC clearly states that this is 32 bits wide, it
311 * is cheating. These fields:
312 * Encoded-Unicast format (6 bytes MIN)
313 * Encoded-Group format (8 bytes MIN)
314 * Encoded-Source format (8 bytes MIN)
315 * are *not* 32 bits wide.
317 * Nor does the RFC explicitly call out the size for:
319 * Num Groups (1 byte)
321 * Number of Joined Sources (2 bytes)
322 * Number of Pruned Sources (2 bytes)
324 * This leads to a missleading representation from casual
325 * reading and making assumptions. Be careful!
328 * 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
329 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330 * |PIM Ver| Type | Reserved | Checksum |
331 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
332 * | Upstream Neighbor Address (Encoded-Unicast format) |
333 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334 * | Reserved | Num groups | Holdtime |
335 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
336 * | Multicast Group Address 1 (Encoded-Group format) |
337 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
338 * | Number of Joined Sources | Number of Pruned Sources |
339 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
340 * | Joined Source Address 1 (Encoded-Source format) |
341 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
344 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
345 * | Joined Source Address n (Encoded-Source format) |
346 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347 * | Pruned Source Address 1 (Encoded-Source format) |
348 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
351 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
352 * | Pruned Source Address n (Encoded-Source format) |
353 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
354 * | Multicast Group Address m (Encoded-Group format) |
355 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356 * | Number of Joined Sources | Number of Pruned Sources |
357 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358 * | Joined Source Address 1 (Encoded-Source format) |
359 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
362 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 * | Joined Source Address n (Encoded-Source format) |
364 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 * | Pruned Source Address 1 (Encoded-Source format) |
366 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
370 * | Pruned Source Address n (Encoded-Source format) |
371 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
373 int pim_joinprune_send(struct pim_rpf
*rpf
,
376 struct pim_jp_agg_group
*group
;
377 struct pim_interface
*pim_ifp
;
378 struct pim_jp_groups
*grp
= NULL
;
380 struct listnode
*node
, *nnode
;
381 uint8_t pim_msg
[10000];
382 uint8_t *curr_ptr
= pim_msg
;
383 bool new_packet
= true;
384 size_t packet_left
= 0;
385 size_t packet_size
= 0;
386 size_t group_size
= 0;
388 on_trace (__PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
, rpf
->rpf_addr
.u
.prefix4
);
390 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
393 zlog_warn("%s: multicast not enabled on interface %s",
395 rpf
->source_nexthop
.interface
->name
);
399 if (PIM_INADDR_IS_ANY(rpf
->rpf_addr
.u
.prefix4
))
401 if (PIM_DEBUG_PIM_J_P
) {
402 char dst_str
[INET_ADDRSTRLEN
];
403 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
404 zlog_debug("%s: upstream=%s is myself on interface %s",
406 dst_str
, rpf
->source_nexthop
.interface
->name
);
412 RFC 4601: 4.3.1. Sending Hello Messages
414 Thus, if a router needs to send a Join/Prune or Assert message on
415 an interface on which it has not yet sent a Hello message with the
416 currently configured IP address, then it MUST immediately send the
417 relevant Hello message without waiting for the Hello Timer to
418 expire, followed by the Join/Prune or Assert message.
420 pim_hello_require(rpf
->source_nexthop
.interface
);
422 for (ALL_LIST_ELEMENTS(groups
, node
, nnode
, group
))
426 msg
= (struct pim_jp
*)pim_msg
;
428 memset(msg
, 0, sizeof (*msg
));
430 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, rpf
->rpf_addr
.u
.prefix4
);
432 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
436 grp
= &msg
->groups
[0];
437 curr_ptr
= (uint8_t *)grp
;
438 packet_size
= sizeof (struct pim_msg_header
);
439 packet_size
+= sizeof (struct pim_encoded_ipv4_unicast
);
440 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
442 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
443 packet_left
-= packet_size
;
445 if (PIM_DEBUG_PIM_J_P
) {
446 char dst_str
[INET_ADDRSTRLEN
];
447 char grp_str
[INET_ADDRSTRLEN
];
448 pim_inet4_dump("<dst?>", rpf
->rpf_addr
.u
.prefix4
, dst_str
, sizeof(dst_str
));
449 pim_inet4_dump("<grp?>", group
->group
, grp_str
, sizeof(grp_str
));
450 zlog_debug("%s: sending (G)=%s to upstream=%s on interface %s",
452 grp_str
, dst_str
, rpf
->source_nexthop
.interface
->name
);
455 group_size
= pim_msg_get_jp_group_size (group
->sources
);
456 if (group_size
> packet_left
)
458 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
459 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
460 pim_ifp
->primary_address
,
461 qpim_all_pim_routers_addr
,
464 rpf
->source_nexthop
.interface
->name
)) {
465 zlog_warn("%s: could not send PIM message on interface %s",
466 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
469 msg
= (struct pim_jp
*)pim_msg
;
470 memset(msg
, 0, sizeof (*msg
));
472 pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg
->addr
, rpf
->rpf_addr
.u
.prefix4
);
474 msg
->holdtime
= htons(PIM_JP_HOLDTIME
);
478 grp
= &msg
->groups
[0];
479 curr_ptr
= (uint8_t *)grp
;
480 packet_size
= sizeof (struct pim_msg_header
);
481 packet_size
+= sizeof (struct pim_encoded_ipv4_unicast
);
482 packet_size
+= 4; // reserved (1) + groups (1) + holdtime (2)
484 packet_left
= rpf
->source_nexthop
.interface
->mtu
- 24;
485 packet_left
-= packet_size
;
493 curr_ptr
+= group_size
;
494 packet_left
-= group_size
;
495 packet_size
+= group_size
;
496 zlog_debug ("\tpl: %zd ps: %zd", packet_left
, packet_size
);
497 pim_msg_build_jp_groups (grp
, group
);
499 grp
= (struct pim_jp_groups
*)curr_ptr
;
500 if (packet_left
< sizeof (struct pim_jp_groups
) || msg
->num_groups
== 255)
502 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
503 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
504 pim_ifp
->primary_address
,
505 qpim_all_pim_routers_addr
,
508 rpf
->source_nexthop
.interface
->name
)) {
509 zlog_warn("%s: could not send PIM message on interface %s",
510 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);
520 //msg->num_groups = htons (msg->num_groups);
521 pim_msg_build_header (pim_msg
, packet_size
, PIM_MSG_TYPE_JOIN_PRUNE
);
522 if (pim_msg_send(pim_ifp
->pim_sock_fd
,
523 pim_ifp
->primary_address
,
524 qpim_all_pim_routers_addr
,
527 rpf
->source_nexthop
.interface
->name
)) {
528 zlog_warn("%s: could not send PIM message on interface %s",
529 __PRETTY_FUNCTION__
, rpf
->source_nexthop
.interface
->name
);