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