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