]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_join.c
Merge pull request #5381 from donaldsharp/zebra_100_is_no_good
[mirror_frr.git] / pimd / pim_join.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
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.
9 *
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.
14 *
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
18 */
19
20 #include <zebra.h>
21
22 #include "log.h"
23 #include "prefix.h"
24 #include "if.h"
25 #include "vty.h"
26 #include "plist.h"
27
28 #include "pimd.h"
29 #include "pim_str.h"
30 #include "pim_tlv.h"
31 #include "pim_msg.h"
32 #include "pim_pim.h"
33 #include "pim_join.h"
34 #include "pim_oil.h"
35 #include "pim_iface.h"
36 #include "pim_hello.h"
37 #include "pim_ifchannel.h"
38 #include "pim_rpf.h"
39 #include "pim_rp.h"
40 #include "pim_jp_agg.h"
41 #include "pim_util.h"
42
43 static void on_trace(const char *label, struct interface *ifp,
44 struct in_addr src)
45 {
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", label, src_str, ifp->name);
50 }
51 }
52
53 static void recv_join(struct interface *ifp, struct pim_neighbor *neigh,
54 uint16_t holdtime, struct in_addr upstream,
55 struct prefix_sg *sg, uint8_t source_flags)
56 {
57 struct pim_interface *pim_ifp = NULL;
58
59 if (PIM_DEBUG_PIM_TRACE) {
60 char up_str[INET_ADDRSTRLEN];
61 char neigh_str[INET_ADDRSTRLEN];
62 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
63 pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str,
64 sizeof(neigh_str));
65 zlog_debug(
66 "%s: join (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
67 __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
68 !!(source_flags & PIM_RPT_BIT_MASK),
69 !!(source_flags & PIM_WILDCARD_BIT_MASK), up_str,
70 holdtime, neigh_str, ifp->name);
71 }
72
73 pim_ifp = ifp->info;
74 zassert(pim_ifp);
75
76 ++pim_ifp->pim_ifstat_join_recv;
77
78 /*
79 * If the RPT and WC are set it's a (*,G)
80 * and the source is the RP
81 */
82 if ((source_flags & PIM_RPT_BIT_MASK)
83 && (source_flags & PIM_WILDCARD_BIT_MASK)) {
84 struct pim_rpf *rp = RP(pim_ifp->pim, sg->grp);
85
86 if (!rp) {
87 zlog_warn("%s: Lookup of RP failed for %pSG4",
88 __PRETTY_FUNCTION__, sg);
89 return;
90 }
91 /*
92 * If the RP sent in the message is not
93 * our RP for the group, drop the message
94 */
95 if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr) {
96 char received_rp[INET_ADDRSTRLEN];
97 char local_rp[INET_ADDRSTRLEN];
98 pim_inet4_dump("<received?>", sg->src, received_rp,
99 sizeof(received_rp));
100 pim_inet4_dump("<local?>", rp->rpf_addr.u.prefix4,
101 local_rp, sizeof(local_rp));
102 zlog_warn("%s: Specified RP(%s) in join is different than our configured RP(%s)",
103 __PRETTY_FUNCTION__, received_rp, local_rp);
104 return;
105 }
106
107 sg->src.s_addr = INADDR_ANY;
108 }
109
110 /* Restart join expiry timer */
111 pim_ifchannel_join_add(ifp, neigh->source_addr, upstream, sg,
112 source_flags, holdtime);
113 }
114
115 static void recv_prune(struct interface *ifp, struct pim_neighbor *neigh,
116 uint16_t holdtime, struct in_addr upstream,
117 struct prefix_sg *sg, uint8_t source_flags)
118 {
119 struct pim_interface *pim_ifp = NULL;
120
121 if (PIM_DEBUG_PIM_TRACE) {
122 char up_str[INET_ADDRSTRLEN];
123 char neigh_str[INET_ADDRSTRLEN];
124 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
125 pim_inet4_dump("<neigh?>", neigh->source_addr, neigh_str,
126 sizeof(neigh_str));
127 zlog_debug(
128 "%s: prune (S,G)=%s rpt=%d wc=%d upstream=%s holdtime=%d from %s on %s",
129 __PRETTY_FUNCTION__, pim_str_sg_dump(sg),
130 source_flags & PIM_RPT_BIT_MASK,
131 source_flags & PIM_WILDCARD_BIT_MASK, up_str, holdtime,
132 neigh_str, ifp->name);
133 }
134
135 pim_ifp = ifp->info;
136 zassert(pim_ifp);
137
138 ++pim_ifp->pim_ifstat_prune_recv;
139
140 if ((source_flags & PIM_RPT_BIT_MASK)
141 && (source_flags & PIM_WILDCARD_BIT_MASK)) {
142 struct pim_rpf *rp = RP(pim_ifp->pim, sg->grp);
143
144 if (!rp) {
145 if (PIM_DEBUG_PIM_TRACE)
146 zlog_debug("%s: RP for %pSG4 completely failed lookup",
147 __PRETTY_FUNCTION__, sg);
148 return;
149 }
150 // Ignoring Prune *,G's at the moment.
151 if (sg->src.s_addr != rp->rpf_addr.u.prefix4.s_addr)
152 return;
153
154 sg->src.s_addr = INADDR_ANY;
155 }
156
157 pim_ifchannel_prune(ifp, upstream, sg, source_flags, holdtime);
158 }
159
160 int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh,
161 struct in_addr src_addr, uint8_t *tlv_buf,
162 int tlv_buf_size)
163 {
164 struct prefix msg_upstream_addr;
165 struct pim_interface *pim_ifp;
166 uint8_t msg_num_groups;
167 uint16_t msg_holdtime;
168 int addr_offset;
169 uint8_t *buf;
170 uint8_t *pastend;
171 int remain;
172 int group;
173
174 buf = tlv_buf;
175 pastend = tlv_buf + tlv_buf_size;
176 pim_ifp = ifp->info;
177
178 /*
179 Parse ucast addr
180 */
181 addr_offset =
182 pim_parse_addr_ucast(&msg_upstream_addr, buf, pastend - buf);
183 if (addr_offset < 1) {
184 char src_str[INET_ADDRSTRLEN];
185 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
186 zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
187 __PRETTY_FUNCTION__, src_str, ifp->name);
188 return -1;
189 }
190 buf += addr_offset;
191
192 /*
193 Check upstream address family
194 */
195 if (msg_upstream_addr.family != AF_INET) {
196 char src_str[INET_ADDRSTRLEN];
197 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
198 zlog_warn("%s: ignoring join/prune directed to unexpected addr family=%d from %s on %s",
199 __PRETTY_FUNCTION__, msg_upstream_addr.family,
200 src_str, ifp->name);
201 return -2;
202 }
203
204 remain = pastend - buf;
205 if (remain < 4) {
206 char src_str[INET_ADDRSTRLEN];
207 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
208 zlog_warn(
209 "%s: short join/prune message buffer for group list: size=%d minimum=%d from %s on %s",
210 __PRETTY_FUNCTION__, remain, 4, src_str, ifp->name);
211 return -4;
212 }
213
214 ++buf; /* skip reserved byte */
215 msg_num_groups = *(const uint8_t *)buf;
216 ++buf;
217 msg_holdtime = ntohs(*(const uint16_t *)buf);
218 ++buf;
219 ++buf;
220
221 if (PIM_DEBUG_PIM_J_P) {
222 char src_str[INET_ADDRSTRLEN];
223 char upstream_str[INET_ADDRSTRLEN];
224 pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
225 pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
226 upstream_str, sizeof(upstream_str));
227 zlog_debug(
228 "%s: join/prune upstream=%s groups=%d holdtime=%d from %s on %s",
229 __PRETTY_FUNCTION__, upstream_str, msg_num_groups,
230 msg_holdtime, src_str, ifp->name);
231 }
232
233 /* Scan groups */
234 for (group = 0; group < msg_num_groups; ++group) {
235 struct prefix_sg sg;
236 uint8_t msg_source_flags;
237 uint16_t msg_num_joined_sources;
238 uint16_t msg_num_pruned_sources;
239 int source;
240 struct pim_ifchannel *starg_ch = NULL, *sg_ch = NULL;
241 bool filtered = false;
242
243 memset(&sg, 0, sizeof(struct prefix_sg));
244 addr_offset = pim_parse_addr_group(&sg, buf, pastend - buf);
245 if (addr_offset < 1) {
246 return -5;
247 }
248 buf += addr_offset;
249
250 remain = pastend - buf;
251 if (remain < 4) {
252 char src_str[INET_ADDRSTRLEN];
253 pim_inet4_dump("<src?>", src_addr, src_str,
254 sizeof(src_str));
255 zlog_warn(
256 "%s: short join/prune buffer for source list: size=%d minimum=%d from %s on %s",
257 __PRETTY_FUNCTION__, remain, 4, src_str,
258 ifp->name);
259 return -6;
260 }
261
262 msg_num_joined_sources = ntohs(*(const uint16_t *)buf);
263 buf += 2;
264 msg_num_pruned_sources = ntohs(*(const uint16_t *)buf);
265 buf += 2;
266
267 if (PIM_DEBUG_PIM_J_P) {
268 char src_str[INET_ADDRSTRLEN];
269 char upstream_str[INET_ADDRSTRLEN];
270 char group_str[INET_ADDRSTRLEN];
271 pim_inet4_dump("<src?>", src_addr, src_str,
272 sizeof(src_str));
273 pim_inet4_dump("<addr?>", msg_upstream_addr.u.prefix4,
274 upstream_str, sizeof(upstream_str));
275 pim_inet4_dump("<grp?>", sg.grp, group_str,
276 sizeof(group_str));
277 zlog_debug(
278 "%s: join/prune upstream=%s group=%s/32 join_src=%d prune_src=%d from %s on %s",
279 __PRETTY_FUNCTION__, upstream_str, group_str,
280 msg_num_joined_sources, msg_num_pruned_sources,
281 src_str, ifp->name);
282 }
283
284 /* boundary check */
285 filtered = pim_is_group_filtered(pim_ifp, &sg.grp);
286
287 /* Scan joined sources */
288 for (source = 0; source < msg_num_joined_sources; ++source) {
289 addr_offset = pim_parse_addr_source(
290 &sg, &msg_source_flags, buf, pastend - buf);
291 if (addr_offset < 1) {
292 return -7;
293 }
294
295 buf += addr_offset;
296
297 /* if we are filtering this group, skip the join */
298 if (filtered)
299 continue;
300
301 recv_join(ifp, neigh, msg_holdtime,
302 msg_upstream_addr.u.prefix4, &sg,
303 msg_source_flags);
304
305 if (sg.src.s_addr == INADDR_ANY) {
306 starg_ch = pim_ifchannel_find(ifp, &sg);
307 if (starg_ch)
308 pim_ifchannel_set_star_g_join_state(
309 starg_ch, 0, 1);
310 }
311 }
312
313 /* Scan pruned sources */
314 for (source = 0; source < msg_num_pruned_sources; ++source) {
315 addr_offset = pim_parse_addr_source(
316 &sg, &msg_source_flags, buf, pastend - buf);
317 if (addr_offset < 1) {
318 return -8;
319 }
320
321 buf += addr_offset;
322
323 /* if we are filtering this group, skip the prune */
324 if (filtered)
325 continue;
326
327 recv_prune(ifp, neigh, msg_holdtime,
328 msg_upstream_addr.u.prefix4, &sg,
329 msg_source_flags);
330
331 /*
332 * So if we are receiving a S,G,RPT prune
333 * before we have any data for that S,G
334 * We need to retrieve the sg_ch after
335 * we parse the prune.
336 */
337 sg_ch = pim_ifchannel_find(ifp, &sg);
338
339 /* Received SG-RPT Prune delete oif from specific S,G */
340 if (starg_ch && sg_ch
341 && (msg_source_flags & PIM_RPT_BIT_MASK)
342 && !(msg_source_flags & PIM_WILDCARD_BIT_MASK)) {
343 struct pim_upstream *up = sg_ch->upstream;
344 PIM_IF_FLAG_SET_S_G_RPT(sg_ch->flags);
345 if (up) {
346 if (PIM_DEBUG_PIM_TRACE)
347 zlog_debug(
348 "%s: SGRpt flag is set, del inherit oif from up %s",
349 __PRETTY_FUNCTION__,
350 up->sg_str);
351 pim_channel_del_oif(
352 up->channel_oil,
353 starg_ch->interface,
354 PIM_OIF_FLAG_PROTO_STAR);
355 }
356 }
357 }
358 if (starg_ch && !filtered)
359 pim_ifchannel_set_star_g_join_state(starg_ch, 1, 0);
360 starg_ch = NULL;
361 } /* scan groups */
362
363 return 0;
364 }
365
366 /*
367 * J/P Message Format
368 *
369 * While the RFC clearly states that this is 32 bits wide, it
370 * is cheating. These fields:
371 * Encoded-Unicast format (6 bytes MIN)
372 * Encoded-Group format (8 bytes MIN)
373 * Encoded-Source format (8 bytes MIN)
374 * are *not* 32 bits wide.
375 *
376 * Nor does the RFC explicitly call out the size for:
377 * Reserved (1 byte)
378 * Num Groups (1 byte)
379 * Holdtime (2 bytes)
380 * Number of Joined Sources (2 bytes)
381 * Number of Pruned Sources (2 bytes)
382 *
383 * This leads to a missleading representation from casual
384 * reading and making assumptions. Be careful!
385 *
386 * 0 1 2 3
387 * 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
388 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 * |PIM Ver| Type | Reserved | Checksum |
390 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
391 * | Upstream Neighbor Address (Encoded-Unicast format) |
392 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
393 * | Reserved | Num groups | Holdtime |
394 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
395 * | Multicast Group Address 1 (Encoded-Group format) |
396 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 * | Number of Joined Sources | Number of Pruned Sources |
398 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 * | Joined Source Address 1 (Encoded-Source format) |
400 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 * | . |
402 * | . |
403 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
404 * | Joined Source Address n (Encoded-Source format) |
405 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 * | Pruned Source Address 1 (Encoded-Source format) |
407 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 * | . |
409 * | . |
410 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 * | Pruned Source Address n (Encoded-Source format) |
412 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413 * | Multicast Group Address m (Encoded-Group format) |
414 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
415 * | Number of Joined Sources | Number of Pruned Sources |
416 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
417 * | Joined Source Address 1 (Encoded-Source format) |
418 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
419 * | . |
420 * | . |
421 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
422 * | Joined Source Address n (Encoded-Source format) |
423 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
424 * | Pruned Source Address 1 (Encoded-Source format) |
425 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
426 * | . |
427 * | . |
428 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 * | Pruned Source Address n (Encoded-Source format) |
430 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 */
432 int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
433 {
434 struct pim_jp_agg_group *group;
435 struct pim_interface *pim_ifp = NULL;
436 struct pim_jp_groups *grp = NULL;
437 struct pim_jp *msg = NULL;
438 struct listnode *node, *nnode;
439 uint8_t pim_msg[10000];
440 uint8_t *curr_ptr = pim_msg;
441 bool new_packet = true;
442 size_t packet_left = 0;
443 size_t packet_size = 0;
444 size_t group_size = 0;
445
446 if (rpf->source_nexthop.interface)
447 pim_ifp = rpf->source_nexthop.interface->info;
448 else {
449 zlog_warn("%s: RPF interface is not present",
450 __PRETTY_FUNCTION__);
451 return -1;
452 }
453
454 on_trace(__PRETTY_FUNCTION__, rpf->source_nexthop.interface,
455 rpf->rpf_addr.u.prefix4);
456
457 if (!pim_ifp) {
458 zlog_warn("%s: multicast not enabled on interface %s",
459 __PRETTY_FUNCTION__,
460 rpf->source_nexthop.interface->name);
461 return -1;
462 }
463
464 if (PIM_INADDR_IS_ANY(rpf->rpf_addr.u.prefix4)) {
465 if (PIM_DEBUG_PIM_J_P) {
466 char dst_str[INET_ADDRSTRLEN];
467 pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4,
468 dst_str, sizeof(dst_str));
469 zlog_debug("%s: upstream=%s is myself on interface %s",
470 __PRETTY_FUNCTION__, dst_str,
471 rpf->source_nexthop.interface->name);
472 }
473 return 0;
474 }
475
476 /*
477 RFC 4601: 4.3.1. Sending Hello Messages
478
479 Thus, if a router needs to send a Join/Prune or Assert message on
480 an interface on which it has not yet sent a Hello message with the
481 currently configured IP address, then it MUST immediately send the
482 relevant Hello message without waiting for the Hello Timer to
483 expire, followed by the Join/Prune or Assert message.
484 */
485 pim_hello_require(rpf->source_nexthop.interface);
486
487 for (ALL_LIST_ELEMENTS(groups, node, nnode, group)) {
488 if (new_packet) {
489 msg = (struct pim_jp *)pim_msg;
490
491 memset(msg, 0, sizeof(*msg));
492
493 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg->addr,
494 rpf->rpf_addr.u.prefix4);
495 msg->reserved = 0;
496 msg->holdtime = htons(PIM_JP_HOLDTIME);
497
498 new_packet = false;
499
500 grp = &msg->groups[0];
501 curr_ptr = (uint8_t *)grp;
502 packet_size = sizeof(struct pim_msg_header);
503 packet_size += sizeof(struct pim_encoded_ipv4_unicast);
504 packet_size +=
505 4; // reserved (1) + groups (1) + holdtime (2)
506
507 packet_left = rpf->source_nexthop.interface->mtu - 24;
508 packet_left -= packet_size;
509 }
510 if (PIM_DEBUG_PIM_J_P) {
511 char dst_str[INET_ADDRSTRLEN];
512 char grp_str[INET_ADDRSTRLEN];
513 pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4,
514 dst_str, sizeof(dst_str));
515 pim_inet4_dump("<grp?>", group->group, grp_str,
516 sizeof(grp_str));
517 zlog_debug(
518 "%s: sending (G)=%s to upstream=%s on interface %s",
519 __PRETTY_FUNCTION__, grp_str, dst_str,
520 rpf->source_nexthop.interface->name);
521 }
522
523 group_size = pim_msg_get_jp_group_size(group->sources);
524 if (group_size > packet_left) {
525 pim_msg_build_header(pim_msg, packet_size,
526 PIM_MSG_TYPE_JOIN_PRUNE, false);
527 if (pim_msg_send(pim_ifp->pim_sock_fd,
528 pim_ifp->primary_address,
529 qpim_all_pim_routers_addr, pim_msg,
530 packet_size,
531 rpf->source_nexthop.interface->name)) {
532 zlog_warn(
533 "%s: could not send PIM message on interface %s",
534 __PRETTY_FUNCTION__,
535 rpf->source_nexthop.interface->name);
536 }
537
538 msg = (struct pim_jp *)pim_msg;
539 memset(msg, 0, sizeof(*msg));
540
541 pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg->addr,
542 rpf->rpf_addr.u.prefix4);
543 msg->reserved = 0;
544 msg->holdtime = htons(PIM_JP_HOLDTIME);
545
546 new_packet = false;
547
548 grp = &msg->groups[0];
549 curr_ptr = (uint8_t *)grp;
550 packet_size = sizeof(struct pim_msg_header);
551 packet_size += sizeof(struct pim_encoded_ipv4_unicast);
552 packet_size +=
553 4; // reserved (1) + groups (1) + holdtime (2)
554
555 packet_left = rpf->source_nexthop.interface->mtu - 24;
556 packet_left -= packet_size;
557 }
558
559 msg->num_groups++;
560 /*
561 Build PIM message
562 */
563
564 curr_ptr += group_size;
565 packet_left -= group_size;
566 packet_size += group_size;
567 pim_msg_build_jp_groups(grp, group, group_size);
568
569 pim_ifp->pim_ifstat_join_send += ntohs(grp->joins);
570 pim_ifp->pim_ifstat_prune_send += ntohs(grp->prunes);
571
572 if (PIM_DEBUG_PIM_TRACE)
573 zlog_debug(
574 "%s: interface %s num_joins %u num_prunes %u",
575 __PRETTY_FUNCTION__,
576 rpf->source_nexthop.interface->name,
577 ntohs(grp->joins), ntohs(grp->prunes));
578
579 grp = (struct pim_jp_groups *)curr_ptr;
580 if (packet_left < sizeof(struct pim_jp_groups)
581 || msg->num_groups == 255) {
582 pim_msg_build_header(pim_msg, packet_size,
583 PIM_MSG_TYPE_JOIN_PRUNE, false);
584 if (pim_msg_send(pim_ifp->pim_sock_fd,
585 pim_ifp->primary_address,
586 qpim_all_pim_routers_addr, pim_msg,
587 packet_size,
588 rpf->source_nexthop.interface->name)) {
589 zlog_warn(
590 "%s: could not send PIM message on interface %s",
591 __PRETTY_FUNCTION__,
592 rpf->source_nexthop.interface->name);
593 }
594
595 new_packet = true;
596 }
597 }
598
599
600 if (!new_packet) {
601 // msg->num_groups = htons (msg->num_groups);
602 pim_msg_build_header(pim_msg, packet_size,
603 PIM_MSG_TYPE_JOIN_PRUNE, false);
604 if (pim_msg_send(pim_ifp->pim_sock_fd, pim_ifp->primary_address,
605 qpim_all_pim_routers_addr, pim_msg,
606 packet_size,
607 rpf->source_nexthop.interface->name)) {
608 zlog_warn(
609 "%s: could not send PIM message on interface %s",
610 __PRETTY_FUNCTION__,
611 rpf->source_nexthop.interface->name);
612 }
613 }
614 return 0;
615 }