]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mroute.c
Merge pull request #13522 from LabNConsulting/chopps/fix-bgp-test
[mirror_frr.git] / pimd / pim_mroute.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * PIM for Quagga
4 * Copyright (C) 2008 Everton da Silva Marques
5 */
6
7 #include <zebra.h>
8 #include "log.h"
9 #include "privs.h"
10 #include "if.h"
11 #include "prefix.h"
12 #include "vty.h"
13 #include "plist.h"
14 #include "sockopt.h"
15 #include "lib_errors.h"
16 #include "lib/network.h"
17
18 #include "pimd.h"
19 #include "pim_rpf.h"
20 #include "pim_mroute.h"
21 #include "pim_oil.h"
22 #include "pim_str.h"
23 #include "pim_time.h"
24 #include "pim_iface.h"
25 #include "pim_macro.h"
26 #include "pim_rp.h"
27 #include "pim_oil.h"
28 #include "pim_register.h"
29 #include "pim_ifchannel.h"
30 #include "pim_zlookup.h"
31 #include "pim_ssm.h"
32 #include "pim_sock.h"
33 #include "pim_vxlan.h"
34 #include "pim_msg.h"
35
36 static void mroute_read_on(struct pim_instance *pim);
37 static int pim_upstream_mroute_update(struct channel_oil *c_oil,
38 const char *name);
39
40 int pim_mroute_set(struct pim_instance *pim, int enable)
41 {
42 int err;
43 int opt, data;
44 socklen_t data_len = sizeof(data);
45
46 /*
47 * We need to create the VRF table for the pim mroute_socket
48 */
49 if (enable && pim->vrf->vrf_id != VRF_DEFAULT) {
50 frr_with_privs (&pimd_privs) {
51
52 data = pim->vrf->data.l.table_id;
53 err = setsockopt(pim->mroute_socket, PIM_IPPROTO,
54 MRT_TABLE, &data, data_len);
55 if (err) {
56 zlog_warn(
57 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO, MRT_TABLE=%d): errno=%d: %s",
58 __FILE__, __func__, pim->mroute_socket,
59 data, errno, safe_strerror(errno));
60 return -1;
61 }
62 }
63 }
64
65 frr_with_privs (&pimd_privs) {
66 opt = enable ? MRT_INIT : MRT_DONE;
67 /*
68 * *BSD *cares* about what value we pass down
69 * here
70 */
71 data = 1;
72 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &data,
73 data_len);
74 if (err) {
75 zlog_warn(
76 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,%s=%d): errno=%d: %s",
77 __FILE__, __func__, pim->mroute_socket,
78 enable ? "MRT_INIT" : "MRT_DONE", data, errno,
79 safe_strerror(errno));
80 return -1;
81 }
82 }
83
84 #if defined(HAVE_IP_PKTINFO)
85 if (enable) {
86 /* Linux and Solaris IP_PKTINFO */
87 data = 1;
88 if (setsockopt(pim->mroute_socket, PIM_IPPROTO, IP_PKTINFO,
89 &data, data_len)) {
90 zlog_warn(
91 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
92 pim->mroute_socket, errno,
93 safe_strerror(errno));
94 }
95 }
96 #endif
97
98 #if PIM_IPV == 6
99 if (enable) {
100 /* Linux and Solaris IPV6_PKTINFO */
101 data = 1;
102 if (setsockopt(pim->mroute_socket, PIM_IPPROTO,
103 IPV6_RECVPKTINFO, &data, data_len)) {
104 zlog_warn(
105 "Could not set IPV6_RECVPKTINFO on socket fd=%d: errno=%d: %s",
106 pim->mroute_socket, errno,
107 safe_strerror(errno));
108 }
109 }
110 #endif
111 setsockopt_so_recvbuf(pim->mroute_socket, 1024 * 1024 * 8);
112
113 if (set_nonblocking(pim->mroute_socket) < 0) {
114 zlog_warn(
115 "Could not set non blocking on socket fd=%d: errno=%d: %s",
116 pim->mroute_socket, errno, safe_strerror(errno));
117 return -1;
118 }
119
120 if (enable) {
121 #if defined linux
122 int upcalls = GMMSG_WRVIFWHOLE;
123 opt = MRT_PIM;
124
125 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, opt, &upcalls,
126 sizeof(upcalls));
127 if (err) {
128 zlog_warn(
129 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
130 errno, safe_strerror(errno));
131 return -1;
132 }
133 #else
134 zlog_warn(
135 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
136 #endif
137 }
138
139 return 0;
140 }
141
142 static const char *const gmmsgtype2str[GMMSG_WRVIFWHOLE + 1] = {
143 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
144
145
146 int pim_mroute_msg_nocache(int fd, struct interface *ifp, const kernmsg *msg)
147 {
148 struct pim_interface *pim_ifp = ifp->info;
149 struct pim_upstream *up;
150 pim_sgaddr sg;
151 bool desync = false;
152
153 memset(&sg, 0, sizeof(sg));
154 sg.src = msg->msg_im_src;
155 sg.grp = msg->msg_im_dst;
156
157
158 if (!pim_ifp || !pim_ifp->pim_enable) {
159 if (PIM_DEBUG_MROUTE)
160 zlog_debug(
161 "%s: %s on interface, dropping packet to %pSG",
162 ifp->name,
163 !pim_ifp ? "Multicast not enabled"
164 : "PIM not enabled",
165 &sg);
166 return 0;
167 }
168
169 if (!pim_is_grp_ssm(pim_ifp->pim, sg.grp)) {
170 /* for ASM, check that we have enough information (i.e. path
171 * to RP) to make a decision on what to do with this packet.
172 *
173 * for SSM, this is meaningless, everything is join-driven,
174 * and for NOCACHE we need to install an empty OIL MFC entry
175 * so the kernel doesn't keep nagging us.
176 */
177 struct pim_rpf *rpg;
178
179 rpg = RP(pim_ifp->pim, msg->msg_im_dst);
180 if (!rpg) {
181 if (PIM_DEBUG_MROUTE)
182 zlog_debug("%s: no RPF for packet to %pSG",
183 ifp->name, &sg);
184 return 0;
185 }
186 if (pim_rpf_addr_is_inaddr_any(rpg)) {
187 if (PIM_DEBUG_MROUTE)
188 zlog_debug("%s: null RPF for packet to %pSG",
189 ifp->name, &sg);
190 return 0;
191 }
192 }
193
194 /*
195 * If we've received a multicast packet that isn't connected to
196 * us
197 */
198 if (!pim_if_connected_to_source(ifp, msg->msg_im_src)) {
199 if (PIM_DEBUG_MROUTE)
200 zlog_debug(
201 "%s: incoming packet to %pSG from non-connected source",
202 ifp->name, &sg);
203 return 0;
204 }
205
206 if (!(PIM_I_am_DR(pim_ifp))) {
207 /* unlike the other debug messages, this one is further in the
208 * "normal operation" category and thus under _DETAIL
209 */
210 if (PIM_DEBUG_MROUTE_DETAIL)
211 zlog_debug(
212 "%s: not DR on interface, not forwarding traffic for %pSG",
213 ifp->name, &sg);
214
215 /*
216 * We are not the DR, but we are still receiving packets
217 * Let's blackhole those packets for the moment
218 * As that they will be coming up to the cpu
219 * and causing us to consider them.
220 *
221 * This *will* create a dangling channel_oil
222 * that I see no way to get rid of. Just noting
223 * this for future reference.
224 */
225 up = pim_upstream_find_or_add(
226 &sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE, __func__);
227 pim_upstream_mroute_add(up->channel_oil, __func__);
228
229 return 0;
230 }
231
232 up = pim_upstream_find_or_add(&sg, ifp, PIM_UPSTREAM_FLAG_MASK_FHR,
233 __func__);
234 if (up->channel_oil->installed) {
235 zlog_warn(
236 "%s: NOCACHE for %pSG, MFC entry disappeared - reinstalling",
237 ifp->name, &sg);
238 desync = true;
239 }
240
241 /*
242 * I moved this debug till after the actual add because
243 * I want to take advantage of the up->sg_str being filled in.
244 */
245 if (PIM_DEBUG_MROUTE) {
246 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
247 __func__, up->sg_str);
248 }
249
250 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
251 pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
252
253 up->channel_oil->cc.pktcnt++;
254 // resolve mfcc_parent prior to mroute_add in channel_add_oif
255 if (up->rpf.source_nexthop.interface &&
256 *oil_parent(up->channel_oil) >= MAXVIFS) {
257 pim_upstream_mroute_iif_update(up->channel_oil, __func__);
258 }
259 pim_register_join(up);
260 /* if we have receiver, inherit from parent */
261 pim_upstream_inherited_olist_decide(pim_ifp->pim, up);
262
263 /* we just got NOCACHE from the kernel, so... MFC is not in the
264 * kernel for some reason or another. Try installing again.
265 */
266 if (desync)
267 pim_upstream_mroute_update(up->channel_oil, __func__);
268 return 0;
269 }
270
271 int pim_mroute_msg_wholepkt(int fd, struct interface *ifp, const char *buf,
272 size_t len)
273 {
274 struct pim_interface *pim_ifp;
275 pim_sgaddr sg;
276 struct pim_rpf *rpg;
277 const ipv_hdr *ip_hdr;
278 struct pim_upstream *up;
279
280 pim_ifp = ifp->info;
281
282 ip_hdr = (const ipv_hdr *)buf;
283
284 memset(&sg, 0, sizeof(sg));
285 sg.src = IPV_SRC(ip_hdr);
286 sg.grp = IPV_DST(ip_hdr);
287
288 up = pim_upstream_find(pim_ifp->pim, &sg);
289 if (!up) {
290 pim_sgaddr star = sg;
291 star.src = PIMADDR_ANY;
292
293 up = pim_upstream_find(pim_ifp->pim, &star);
294
295 if (up && PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(up->flags)) {
296 up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
297 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
298 __func__, NULL);
299 if (!up) {
300 if (PIM_DEBUG_MROUTE)
301 zlog_debug(
302 "%s: Unable to create upstream information for %pSG",
303 __func__, &sg);
304 return 0;
305 }
306 pim_upstream_keep_alive_timer_start(
307 up, pim_ifp->pim->keep_alive_time);
308 pim_upstream_inherited_olist(pim_ifp->pim, up);
309 pim_upstream_update_join_desired(pim_ifp->pim, up);
310
311 if (PIM_DEBUG_MROUTE)
312 zlog_debug("%s: Creating %s upstream on LHR",
313 __func__, up->sg_str);
314 return 0;
315 }
316 if (PIM_DEBUG_MROUTE_DETAIL) {
317 zlog_debug(
318 "%s: Unable to find upstream channel WHOLEPKT%pSG",
319 __func__, &sg);
320 }
321 return 0;
322 }
323
324 if (!up->rpf.source_nexthop.interface) {
325 if (PIM_DEBUG_PIM_TRACE)
326 zlog_debug("%s: up %s RPF is not present", __func__,
327 up->sg_str);
328 return 0;
329 }
330
331 pim_ifp = up->rpf.source_nexthop.interface->info;
332
333 rpg = pim_ifp ? RP(pim_ifp->pim, sg.grp) : NULL;
334
335 if ((pim_rpf_addr_is_inaddr_any(rpg)) || (!pim_ifp) ||
336 (!(PIM_I_am_DR(pim_ifp)))) {
337 if (PIM_DEBUG_MROUTE) {
338 zlog_debug("%s: Failed Check send packet", __func__);
339 }
340 return 0;
341 }
342
343 /*
344 * If we've received a register suppress
345 */
346 if (!up->t_rs_timer) {
347 if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) {
348 if (PIM_DEBUG_PIM_REG)
349 zlog_debug(
350 "%pSG register forward skipped as group is SSM",
351 &sg);
352 return 0;
353 }
354
355 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
356 if (PIM_DEBUG_PIM_REG)
357 zlog_debug(
358 "%s register forward skipped, not FHR",
359 up->sg_str);
360 return 0;
361 }
362
363 pim_register_send((uint8_t *)buf + sizeof(ipv_hdr),
364 len - sizeof(ipv_hdr),
365 pim_ifp->primary_address, rpg, 0, up);
366 }
367 return 0;
368 }
369
370 int pim_mroute_msg_wrongvif(int fd, struct interface *ifp, const kernmsg *msg)
371 {
372 struct pim_ifchannel *ch;
373 struct pim_interface *pim_ifp;
374 pim_sgaddr sg;
375
376 memset(&sg, 0, sizeof(sg));
377 sg.src = msg->msg_im_src;
378 sg.grp = msg->msg_im_dst;
379
380 /*
381 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
382
383 RFC 4601 4.8.2. PIM-SSM-Only Routers
384
385 iif is the incoming interface of the packet.
386 if (iif is in inherited_olist(S,G)) {
387 send Assert(S,G) on iif
388 }
389 */
390
391 if (!ifp) {
392 if (PIM_DEBUG_MROUTE)
393 zlog_debug(
394 "%s: WRONGVIF (S,G)=%pSG could not find input interface for input_vif_index=%d",
395 __func__, &sg, msg->msg_im_vif);
396 return -1;
397 }
398
399 pim_ifp = ifp->info;
400 if (!pim_ifp) {
401 if (PIM_DEBUG_MROUTE)
402 zlog_debug(
403 "%s: WRONGVIF (S,G)=%pSG multicast not enabled on interface %s",
404 __func__, &sg, ifp->name);
405 return -2;
406 }
407
408 ch = pim_ifchannel_find(ifp, &sg);
409 if (!ch) {
410 pim_sgaddr star_g = sg;
411 if (PIM_DEBUG_MROUTE)
412 zlog_debug(
413 "%s: WRONGVIF (S,G)=%pSG could not find channel on interface %s",
414 __func__, &sg, ifp->name);
415
416 star_g.src = PIMADDR_ANY;
417 ch = pim_ifchannel_find(ifp, &star_g);
418 if (!ch) {
419 if (PIM_DEBUG_MROUTE)
420 zlog_debug(
421 "%s: WRONGVIF (*,G)=%pSG could not find channel on interface %s",
422 __func__, &star_g, ifp->name);
423 return -3;
424 }
425 }
426
427 /*
428 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
429
430 Transitions from NoInfo State
431
432 An (S,G) data packet arrives on interface I, AND
433 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
434 downstream interface that is in our (S,G) outgoing interface
435 list. We optimistically assume that we will be the assert
436 winner for this (S,G), and so we transition to the "I am Assert
437 Winner" state and perform Actions A1 (below), which will
438 initiate the assert negotiation for (S,G).
439 */
440
441 if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
442 if (PIM_DEBUG_MROUTE) {
443 zlog_debug(
444 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
445 __func__, ch->sg_str, ifp->name);
446 }
447 return -4;
448 }
449
450 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
451 if (PIM_DEBUG_MROUTE) {
452 zlog_debug(
453 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
454 __func__, ch->sg_str, ifp->name);
455 }
456 return -5;
457 }
458
459 if (assert_action_a1(ch)) {
460 if (PIM_DEBUG_MROUTE) {
461 zlog_debug(
462 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
463 __func__, ch->sg_str, ifp->name);
464 }
465 return -6;
466 }
467
468 return 0;
469 }
470
471 int pim_mroute_msg_wrvifwhole(int fd, struct interface *ifp, const char *buf,
472 size_t len)
473 {
474 const ipv_hdr *ip_hdr = (const ipv_hdr *)buf;
475 struct pim_interface *pim_ifp;
476 struct pim_instance *pim;
477 struct pim_ifchannel *ch;
478 struct pim_upstream *up;
479 pim_sgaddr star_g;
480 pim_sgaddr sg;
481
482 pim_ifp = ifp->info;
483
484 memset(&sg, 0, sizeof(sg));
485 sg.src = IPV_SRC(ip_hdr);
486 sg.grp = IPV_DST(ip_hdr);
487
488 ch = pim_ifchannel_find(ifp, &sg);
489 if (ch) {
490 if (PIM_DEBUG_MROUTE)
491 zlog_debug(
492 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
493 ch->sg_str, ifp->name);
494 return -1;
495 }
496
497 star_g = sg;
498 star_g.src = PIMADDR_ANY;
499
500 pim = pim_ifp->pim;
501 /*
502 * If the incoming interface is the pimreg, then
503 * we know the callback is associated with a pim register
504 * packet and there is nothing to do here as that
505 * normal pim processing will see the packet and allow
506 * us to do the right thing.
507 */
508 if (ifp == pim->regiface) {
509 return 0;
510 }
511
512 up = pim_upstream_find(pim_ifp->pim, &sg);
513 if (up) {
514 struct pim_upstream *parent;
515 struct pim_nexthop source;
516 struct pim_rpf *rpf = RP(pim_ifp->pim, sg.grp);
517
518 /* No RPF or No RPF interface or No mcast on RPF interface */
519 if (!rpf || !rpf->source_nexthop.interface ||
520 !rpf->source_nexthop.interface->info)
521 return 0;
522
523 /*
524 * If we have received a WRVIFWHOLE and are at this
525 * point, we could be receiving the packet on the *,G
526 * tree, let's check and if so we can safely drop
527 * it.
528 */
529 parent = pim_upstream_find(pim_ifp->pim, &star_g);
530 if (parent && parent->rpf.source_nexthop.interface == ifp)
531 return 0;
532
533 pim_ifp = rpf->source_nexthop.interface->info;
534
535 memset(&source, 0, sizeof(source));
536 /*
537 * If we are the fhr that means we are getting a callback during
538 * the pimreg period, so I believe we can ignore this packet
539 */
540 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags)) {
541 /*
542 * No if channel, but upstream we are at the RP.
543 *
544 * This could be a anycast RP too and we may
545 * not have received a register packet from
546 * the source here at all. So gracefully
547 * bow out of doing a nexthop lookup and
548 * setting the SPTBIT to true
549 */
550 if (!(pim_addr_is_any(up->upstream_register)) &&
551 pim_nexthop_lookup(pim_ifp->pim, &source,
552 up->upstream_register, 0)) {
553 pim_register_stop_send(source.interface, &sg,
554 pim_ifp->primary_address,
555 up->upstream_register);
556 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
557 }
558
559 pim_upstream_inherited_olist(pim_ifp->pim, up);
560 if (!up->channel_oil->installed)
561 pim_upstream_mroute_add(up->channel_oil,
562 __func__);
563 } else {
564 if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
565 if (pim_nexthop_lookup(pim_ifp->pim, &source,
566 up->upstream_register,
567 0))
568 pim_register_stop_send(
569 source.interface, &sg,
570 pim_ifp->primary_address,
571 up->upstream_register);
572 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
573 } else {
574 /*
575 * At this point pimd is connected to
576 * the source, it has a parent, we are not
577 * the RP and the SPTBIT should be set
578 * since we know *the* S,G is on the SPT.
579 * The first time this happens, let's cause
580 * an immediate join to go out so that
581 * the RP can trim this guy immediately
582 * if necessary, instead of waiting
583 * one join/prune send cycle
584 */
585 if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE &&
586 up->parent &&
587 up->rpf.source_nexthop.interface !=
588 up->parent->rpf.source_nexthop
589 .interface) {
590 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
591 pim_jp_agg_single_upstream_send(
592 &up->parent->rpf, up->parent,
593 true);
594 }
595 }
596 pim_upstream_keep_alive_timer_start(
597 up, pim_ifp->pim->keep_alive_time);
598 pim_upstream_inherited_olist(pim_ifp->pim, up);
599 pim_mroute_msg_wholepkt(fd, ifp, buf, len);
600 }
601 return 0;
602 }
603
604 pim_ifp = ifp->info;
605 if (pim_if_connected_to_source(ifp, sg.src)) {
606 up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
607 PIM_UPSTREAM_FLAG_MASK_FHR, __func__,
608 NULL);
609 if (!up) {
610 if (PIM_DEBUG_MROUTE)
611 zlog_debug(
612 "%pSG: WRONGVIF%s unable to create upstream on interface",
613 &sg, ifp->name);
614 return -2;
615 }
616 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
617 pim_upstream_keep_alive_timer_start(
618 up, pim_ifp->pim->keep_alive_time);
619 up->channel_oil->cc.pktcnt++;
620 pim_register_join(up);
621 pim_upstream_inherited_olist(pim_ifp->pim, up);
622 if (!up->channel_oil->installed)
623 pim_upstream_mroute_add(up->channel_oil, __func__);
624
625 // Send the packet to the RP
626 pim_mroute_msg_wholepkt(fd, ifp, buf, len);
627 } else {
628 up = pim_upstream_add(pim_ifp->pim, &sg, ifp,
629 PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
630 __func__, NULL);
631 if (!up->channel_oil->installed)
632 pim_upstream_mroute_add(up->channel_oil, __func__);
633 }
634
635 return 0;
636 }
637
638 #if PIM_IPV == 4
639 static int process_igmp_packet(struct pim_instance *pim, const char *buf,
640 size_t buf_size, ifindex_t ifindex)
641 {
642 struct interface *ifp;
643 struct pim_interface *pim_ifp;
644 struct in_addr ifaddr;
645 struct gm_sock *igmp;
646 const struct prefix *connected_src;
647 const struct ip *ip_hdr = (const struct ip *)buf;
648
649 /* We have the IP packet but we do not know which interface this
650 * packet was
651 * received on. Find the interface that is on the same subnet as
652 * the source
653 * of the IP packet.
654 */
655 ifp = if_lookup_by_index(ifindex, pim->vrf->vrf_id);
656
657 if (!ifp || !ifp->info)
658 return 0;
659
660 connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src);
661
662 if (!connected_src && !pim_addr_is_any(ip_hdr->ip_src)) {
663 if (PIM_DEBUG_GM_PACKETS) {
664 zlog_debug(
665 "Recv IGMP packet on interface: %s from a non-connected source: %pI4",
666 ifp->name, &ip_hdr->ip_src);
667 }
668 return 0;
669 }
670
671 pim_ifp = ifp->info;
672 ifaddr = connected_src ? connected_src->u.prefix4
673 : pim_ifp->primary_address;
674 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr);
675
676 if (PIM_DEBUG_GM_PACKETS) {
677 zlog_debug(
678 "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4",
679 __func__, pim->vrf->name, ifp->name, igmp,
680 &ip_hdr->ip_src, &ip_hdr->ip_dst);
681 }
682 if (igmp)
683 pim_igmp_packet(igmp, (char *)buf, buf_size);
684 else if (PIM_DEBUG_GM_PACKETS)
685 zlog_debug(
686 "No IGMP socket on interface: %s with connected source: %pI4",
687 ifp->name, &ifaddr);
688
689 return 0;
690 }
691 #endif
692
693 int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size,
694 ifindex_t ifindex)
695 {
696 struct interface *ifp;
697 const ipv_hdr *ip_hdr;
698 const kernmsg *msg;
699
700 if (buf_size < (int)sizeof(ipv_hdr))
701 return 0;
702
703 ip_hdr = (const ipv_hdr *)buf;
704
705 #if PIM_IPV == 4
706 if (ip_hdr->ip_p == IPPROTO_IGMP) {
707 process_igmp_packet(pim, buf, buf_size, ifindex);
708 } else if (ip_hdr->ip_p) {
709 if (PIM_DEBUG_MROUTE_DETAIL) {
710 zlog_debug(
711 "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%ld",
712 __func__, ip_hdr->ip_p, &ip_hdr->ip_src,
713 &ip_hdr->ip_dst, (long int)buf_size);
714 }
715
716 } else {
717 #else
718
719 if ((ip_hdr->ip6_vfc & 0xf) == 0) {
720 #endif
721 msg = (const kernmsg *)buf;
722
723 ifp = pim_if_find_by_vif_index(pim, msg->msg_im_vif);
724
725 if (!ifp)
726 return 0;
727 if (PIM_DEBUG_MROUTE) {
728 #if PIM_IPV == 4
729 zlog_debug(
730 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%ld",
731 __func__, gmmsgtype2str[msg->msg_im_msgtype],
732 msg->msg_im_msgtype, ip_hdr->ip_p,
733 pim->mroute_socket, &msg->msg_im_src,
734 &msg->msg_im_dst, ifp->name, msg->msg_im_vif,
735 (long int)buf_size);
736 #else
737 zlog_debug(
738 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI6,%pI6) on %s vifi=%d size=%ld",
739 __func__, gmmsgtype2str[msg->msg_im_msgtype],
740 msg->msg_im_msgtype, ip_hdr->ip6_nxt,
741 pim->mroute_socket, &msg->msg_im_src,
742 &msg->msg_im_dst, ifp->name, msg->msg_im_vif,
743 (long int)buf_size);
744 #endif
745 }
746
747 switch (msg->msg_im_msgtype) {
748 case GMMSG_WRONGVIF:
749 return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
750 msg);
751 case GMMSG_NOCACHE:
752 return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
753 msg);
754 case GMMSG_WHOLEPKT:
755 return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
756 (const char *)msg,
757 buf_size);
758 case GMMSG_WRVIFWHOLE:
759 return pim_mroute_msg_wrvifwhole(pim->mroute_socket,
760 ifp, (const char *)msg,
761 buf_size);
762 default:
763 break;
764 }
765 }
766
767 return 0;
768 }
769
770 static void mroute_read(struct event *t)
771 {
772 struct pim_instance *pim;
773 static long long count;
774 char buf[10000];
775 int cont = 1;
776 int rd;
777 ifindex_t ifindex;
778 pim = EVENT_ARG(t);
779
780 while (cont) {
781 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
782 sizeof(buf), NULL, NULL, NULL, NULL,
783 &ifindex);
784 if (rd <= 0) {
785 if (errno == EINTR)
786 continue;
787 if (errno == EWOULDBLOCK || errno == EAGAIN)
788 break;
789
790 zlog_warn(
791 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
792 __func__, rd, pim->mroute_socket, errno,
793 safe_strerror(errno));
794 goto done;
795 }
796
797 pim_mroute_msg(pim, buf, rd, ifindex);
798
799 count++;
800 if (count % router->packet_process == 0)
801 cont = 0;
802 }
803 /* Keep reading */
804 done:
805 mroute_read_on(pim);
806
807 return;
808 }
809
810 static void mroute_read_on(struct pim_instance *pim)
811 {
812 event_add_read(router->master, mroute_read, pim, pim->mroute_socket,
813 &pim->thread);
814 }
815
816 static void mroute_read_off(struct pim_instance *pim)
817 {
818 EVENT_OFF(pim->thread);
819 }
820
821 int pim_mroute_socket_enable(struct pim_instance *pim)
822 {
823 int fd;
824
825 frr_with_privs(&pimd_privs) {
826
827 #if PIM_IPV == 4
828 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
829 #else
830 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
831 #endif
832 if (fd < 0) {
833 zlog_warn("Could not create mroute socket: errno=%d: %s",
834 errno,
835 safe_strerror(errno));
836 return -2;
837 }
838
839 #if PIM_IPV == 6
840 struct icmp6_filter filter[1];
841 int ret;
842
843 /* Unlike IPv4, this socket is not used for MLD, so just drop
844 * everything with an empty ICMP6 filter. Otherwise we get
845 * all kinds of garbage here, possibly even non-multicast
846 * related ICMPv6 traffic (e.g. ping)
847 *
848 * (mroute kernel upcall "packets" are injected directly on the
849 * socket, this sockopt -or any other- has no effect on them)
850 */
851 ICMP6_FILTER_SETBLOCKALL(filter);
852 ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter,
853 sizeof(filter));
854 if (ret)
855 zlog_err(
856 "(VRF %s) failed to set mroute control filter: %m",
857 pim->vrf->name);
858 #endif
859
860 #ifdef SO_BINDTODEVICE
861 if (pim->vrf->vrf_id != VRF_DEFAULT
862 && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
863 pim->vrf->name, strlen(pim->vrf->name))) {
864 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
865 safe_strerror(errno));
866 close(fd);
867 return -3;
868 }
869 #endif
870
871 }
872
873 pim->mroute_socket = fd;
874 if (pim_mroute_set(pim, 1)) {
875 zlog_warn(
876 "Could not enable mroute on socket fd=%d: errno=%d: %s",
877 fd, errno, safe_strerror(errno));
878 close(fd);
879 pim->mroute_socket = -1;
880 return -3;
881 }
882
883 pim->mroute_socket_creation = pim_time_monotonic_sec();
884
885 mroute_read_on(pim);
886
887 return 0;
888 }
889
890 int pim_mroute_socket_disable(struct pim_instance *pim)
891 {
892 if (pim_mroute_set(pim, 0)) {
893 zlog_warn(
894 "Could not disable mroute on socket fd=%d: errno=%d: %s",
895 pim->mroute_socket, errno, safe_strerror(errno));
896 return -2;
897 }
898
899 if (close(pim->mroute_socket)) {
900 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
901 pim->mroute_socket, errno, safe_strerror(errno));
902 return -3;
903 }
904
905 mroute_read_off(pim);
906 pim->mroute_socket = -1;
907
908 return 0;
909 }
910
911 /*
912 For each network interface (e.g., physical or a virtual tunnel) that
913 would be used for multicast forwarding, a corresponding multicast
914 interface must be added to the kernel.
915 */
916 int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr,
917 unsigned char flags)
918 {
919 struct pim_interface *pim_ifp = ifp->info;
920 pim_vifctl vc;
921 int err;
922
923 if (PIM_DEBUG_MROUTE)
924 zlog_debug("%s: Add Vif %d (%s[%s])", __func__,
925 pim_ifp->mroute_vif_index, ifp->name,
926 pim_ifp->pim->vrf->name);
927
928 memset(&vc, 0, sizeof(vc));
929 vc.vc_vifi = pim_ifp->mroute_vif_index;
930 #if PIM_IPV == 4
931 #ifdef VIFF_USE_IFINDEX
932 vc.vc_lcl_ifindex = ifp->ifindex;
933 #else
934 if (ifaddr.s_addr == INADDR_ANY) {
935 zlog_warn(
936 "%s: unnumbered interfaces are not supported on this platform",
937 __func__);
938 return -1;
939 }
940 memcpy(&vc.vc_lcl_addr, &ifaddr, sizeof(vc.vc_lcl_addr));
941 #endif
942 #else
943 vc.vc_pifi = ifp->ifindex;
944 #endif
945 vc.vc_flags = flags;
946 vc.vc_threshold = PIM_MROUTE_MIN_TTL;
947 vc.vc_rate_limit = 0;
948
949 #if PIM_IPV == 4
950 #ifdef PIM_DVMRP_TUNNEL
951 if (vc.vc_flags & VIFF_TUNNEL) {
952 memcpy(&vc.vc_rmt_addr, &vif_remote_addr,
953 sizeof(vc.vc_rmt_addr));
954 }
955 #endif
956 #endif
957
958 err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF,
959 (void *)&vc, sizeof(vc));
960 if (err) {
961 zlog_warn(
962 "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s",
963 __func__, pim_ifp->pim->mroute_socket, ifp->ifindex,
964 &ifaddr, flags, errno, safe_strerror(errno));
965 return -2;
966 }
967
968 return 0;
969 }
970
971 int pim_mroute_del_vif(struct interface *ifp)
972 {
973 struct pim_interface *pim_ifp = ifp->info;
974 pim_vifctl vc;
975 int err;
976
977 if (PIM_DEBUG_MROUTE)
978 zlog_debug("%s: Del Vif %d (%s[%s])", __func__,
979 pim_ifp->mroute_vif_index, ifp->name,
980 pim_ifp->pim->vrf->name);
981
982 memset(&vc, 0, sizeof(vc));
983 vc.vc_vifi = pim_ifp->mroute_vif_index;
984
985 err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF,
986 (void *)&vc, sizeof(vc));
987 if (err) {
988 zlog_warn(
989 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
990 __FILE__, __func__, pim_ifp->pim->mroute_socket,
991 pim_ifp->mroute_vif_index, errno, safe_strerror(errno));
992 return -2;
993 }
994
995 return 0;
996 }
997
998 /*
999 * Prevent creating MFC entry with OIF=IIF.
1000 *
1001 * This is a protection against implementation mistakes.
1002 *
1003 * PIM protocol implicitely ensures loopfree multicast topology.
1004 *
1005 * IGMP must be protected against adding looped MFC entries created
1006 * by both source and receiver attached to the same interface. See
1007 * TODO T22.
1008 * We shall allow igmp to create upstream when it is DR for the intf.
1009 * Assume RP reachable via non DR.
1010 */
1011 bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
1012 int oif_index)
1013 {
1014 #ifdef PIM_ENFORCE_LOOPFREE_MFC
1015 struct interface *ifp_out;
1016 struct pim_interface *pim_ifp;
1017
1018 if (c_oil->up &&
1019 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
1020 return true;
1021
1022 ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
1023 if (!ifp_out)
1024 return false;
1025 pim_ifp = ifp_out->info;
1026 if (!pim_ifp)
1027 return false;
1028 if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_GM) &&
1029 PIM_I_am_DR(pim_ifp))
1030 return true;
1031
1032 return false;
1033 #else
1034 return true;
1035 #endif
1036 }
1037
1038 static inline void pim_mroute_copy(struct channel_oil *out,
1039 struct channel_oil *in)
1040 {
1041 int i;
1042
1043 *oil_origin(out) = *oil_origin(in);
1044 *oil_mcastgrp(out) = *oil_mcastgrp(in);
1045 *oil_parent(out) = *oil_parent(in);
1046
1047 for (i = 0; i < MAXVIFS; ++i) {
1048 if (*oil_parent(out) == i &&
1049 !pim_mroute_allow_iif_in_oil(in, i)) {
1050 oil_if_set(out, i, 0);
1051 continue;
1052 }
1053
1054 if (in->oif_flags[i] & PIM_OIF_FLAG_MUTE)
1055 oil_if_set(out, i, 0);
1056 else
1057 oil_if_set(out, i, oil_if_has(in, i));
1058 }
1059 }
1060
1061 /* This function must not be called directly 0
1062 * use pim_upstream_mroute_add or pim_static_mroute_add instead
1063 */
1064 static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
1065 {
1066 struct pim_instance *pim = c_oil->pim;
1067 struct channel_oil tmp_oil[1] = { };
1068 int err;
1069
1070 pim->mroute_add_last = pim_time_monotonic_sec();
1071 ++pim->mroute_add_events;
1072
1073 /* Copy the oil to a temporary structure to fixup (without need to
1074 * later restore) before sending the mroute add to the dataplane
1075 */
1076 pim_mroute_copy(tmp_oil, c_oil);
1077
1078 /* The linux kernel *expects* the incoming
1079 * vif to be part of the outgoing list
1080 * in the case of a (*,G).
1081 */
1082 if (pim_addr_is_any(*oil_origin(c_oil))) {
1083 oil_if_set(tmp_oil, *oil_parent(c_oil), 1);
1084 }
1085
1086 /*
1087 * If we have an unresolved cache entry for the S,G
1088 * it is owned by the pimreg for the incoming IIF
1089 * So set pimreg as the IIF temporarily to cause
1090 * the packets to be forwarded. Then set it
1091 * to the correct IIF afterwords.
1092 */
1093 if (!c_oil->installed && !pim_addr_is_any(*oil_origin(c_oil))
1094 && *oil_parent(c_oil) != 0) {
1095 *oil_parent(tmp_oil) = 0;
1096 }
1097 /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */
1098 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
1099 &tmp_oil->oil, sizeof(tmp_oil->oil));
1100
1101 if (!err && !c_oil->installed
1102 && !pim_addr_is_any(*oil_origin(c_oil))
1103 && *oil_parent(c_oil) != 0) {
1104 *oil_parent(tmp_oil) = *oil_parent(c_oil);
1105 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
1106 &tmp_oil->oil, sizeof(tmp_oil->oil));
1107 }
1108
1109 if (err) {
1110 zlog_warn(
1111 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_MFC): errno=%d: %s",
1112 __FILE__, __func__, pim->mroute_socket, errno,
1113 safe_strerror(errno));
1114 return -2;
1115 }
1116
1117 if (PIM_DEBUG_MROUTE) {
1118 char buf[1000];
1119 zlog_debug("%s(%s), vrf %s Added Route: %s", __func__, name,
1120 pim->vrf->name,
1121 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1122 }
1123
1124 if (!c_oil->installed) {
1125 c_oil->installed = 1;
1126 c_oil->mroute_creation = pim_time_monotonic_sec();
1127 }
1128
1129 return 0;
1130 }
1131
1132 static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
1133 const char *name)
1134 {
1135 vifi_t iif = MAXVIFS;
1136 struct interface *ifp = NULL;
1137 struct pim_interface *pim_ifp;
1138 struct pim_upstream *up = c_oil->up;
1139
1140 if (up) {
1141 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
1142 if (up->parent)
1143 ifp = up->parent->rpf.source_nexthop.interface;
1144 } else {
1145 ifp = up->rpf.source_nexthop.interface;
1146 }
1147 if (ifp) {
1148 pim_ifp = (struct pim_interface *)ifp->info;
1149 if (pim_ifp)
1150 iif = pim_ifp->mroute_vif_index;
1151 }
1152 }
1153 return iif;
1154 }
1155
1156 static int pim_upstream_mroute_update(struct channel_oil *c_oil,
1157 const char *name)
1158 {
1159 char buf[1000];
1160
1161 if (*oil_parent(c_oil) >= MAXVIFS) {
1162 /* the c_oil cannot be installed as a mroute yet */
1163 if (PIM_DEBUG_MROUTE)
1164 zlog_debug(
1165 "%s(%s) %s mroute not ready to be installed; %s",
1166 __func__, name,
1167 pim_channel_oil_dump(c_oil, buf,
1168 sizeof(buf)),
1169 c_oil->installed ?
1170 "uninstall" : "skip");
1171 /* if already installed flush it out as we are going to stop
1172 * updates to it leaving it in a stale state
1173 */
1174 if (c_oil->installed)
1175 pim_mroute_del(c_oil, name);
1176 /* return success (skipped) */
1177 return 0;
1178 }
1179
1180 return pim_mroute_add(c_oil, name);
1181 }
1182
1183 /* IIF associated with SGrpt entries are re-evaluated when the parent
1184 * (*,G) entries IIF changes
1185 */
1186 static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
1187 {
1188 struct listnode *listnode;
1189 struct pim_upstream *child;
1190
1191 for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
1192 child)) {
1193 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
1194 pim_upstream_mroute_iif_update(child->channel_oil,
1195 __func__);
1196 }
1197 }
1198
1199 /* In the case of "PIM state machine" added mroutes an upstream entry
1200 * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
1201 */
1202 int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
1203 {
1204 vifi_t iif;
1205
1206 iif = pim_upstream_get_mroute_iif(c_oil, name);
1207
1208 if (*oil_parent(c_oil) != iif) {
1209 *oil_parent(c_oil) = iif;
1210 if (pim_addr_is_any(*oil_origin(c_oil)) &&
1211 c_oil->up)
1212 pim_upstream_all_sources_iif_update(c_oil->up);
1213 } else {
1214 *oil_parent(c_oil) = iif;
1215 }
1216
1217 return pim_upstream_mroute_update(c_oil, name);
1218 }
1219
1220 /* Look for IIF changes and update the dateplane entry only if the IIF
1221 * has changed.
1222 */
1223 int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
1224 {
1225 vifi_t iif;
1226 char buf[1000];
1227
1228 iif = pim_upstream_get_mroute_iif(c_oil, name);
1229 if (*oil_parent(c_oil) == iif) {
1230 /* no change */
1231 return 0;
1232 }
1233 *oil_parent(c_oil) = iif;
1234
1235 if (pim_addr_is_any(*oil_origin(c_oil)) &&
1236 c_oil->up)
1237 pim_upstream_all_sources_iif_update(c_oil->up);
1238
1239 if (PIM_DEBUG_MROUTE_DETAIL)
1240 zlog_debug("%s(%s) %s mroute iif update %d",
1241 __func__, name,
1242 pim_channel_oil_dump(c_oil, buf,
1243 sizeof(buf)), iif);
1244 /* XXX: is this hack needed? */
1245 c_oil->oil_inherited_rescan = 1;
1246 return pim_upstream_mroute_update(c_oil, name);
1247 }
1248
1249 int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
1250 {
1251 return pim_mroute_add(c_oil, name);
1252 }
1253
1254 void pim_static_mroute_iif_update(struct channel_oil *c_oil,
1255 int input_vif_index,
1256 const char *name)
1257 {
1258 if (*oil_parent(c_oil) == input_vif_index)
1259 return;
1260
1261 *oil_parent(c_oil) = input_vif_index;
1262 if (input_vif_index == MAXVIFS)
1263 pim_mroute_del(c_oil, name);
1264 else
1265 pim_static_mroute_add(c_oil, name);
1266 }
1267
1268 int pim_mroute_del(struct channel_oil *c_oil, const char *name)
1269 {
1270 struct pim_instance *pim = c_oil->pim;
1271 int err;
1272
1273 pim->mroute_del_last = pim_time_monotonic_sec();
1274 ++pim->mroute_del_events;
1275
1276 if (!c_oil->installed) {
1277 if (PIM_DEBUG_MROUTE) {
1278 char buf[1000];
1279 zlog_debug(
1280 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1281 __FILE__, __func__, *oil_parent(c_oil),
1282 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1283 }
1284 return -2;
1285 }
1286
1287 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_DEL_MFC,
1288 &c_oil->oil, sizeof(c_oil->oil));
1289 if (err) {
1290 if (PIM_DEBUG_MROUTE)
1291 zlog_warn(
1292 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_MFC): errno=%d: %s",
1293 __FILE__, __func__, pim->mroute_socket, errno,
1294 safe_strerror(errno));
1295 return -2;
1296 }
1297
1298 if (PIM_DEBUG_MROUTE) {
1299 char buf[1000];
1300 zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__, name,
1301 pim->vrf->name,
1302 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1303 }
1304
1305 // Reset kernel installed flag
1306 c_oil->installed = 0;
1307
1308 return 0;
1309 }
1310
1311 void pim_mroute_update_counters(struct channel_oil *c_oil)
1312 {
1313 struct pim_instance *pim = c_oil->pim;
1314 pim_sioc_sg_req sgreq;
1315
1316 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
1317 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
1318 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
1319
1320 if (!c_oil->installed) {
1321 c_oil->cc.lastused = 100 * pim->keep_alive_time;
1322 if (PIM_DEBUG_MROUTE) {
1323 pim_sgaddr sg;
1324
1325 sg.src = *oil_origin(c_oil);
1326 sg.grp = *oil_mcastgrp(c_oil);
1327 zlog_debug("Channel%pSG is not installed no need to collect data from kernel",
1328 &sg);
1329 }
1330 return;
1331 }
1332
1333
1334 memset(&sgreq, 0, sizeof(sgreq));
1335
1336 pim_zlookup_sg_statistics(c_oil);
1337
1338 #if PIM_IPV == 4
1339 sgreq.src = *oil_origin(c_oil);
1340 sgreq.grp = *oil_mcastgrp(c_oil);
1341 #else
1342 sgreq.src = c_oil->oil.mf6cc_origin;
1343 sgreq.grp = c_oil->oil.mf6cc_mcastgrp;
1344 #endif
1345 if (ioctl(pim->mroute_socket, PIM_SIOCGETSGCNT, &sgreq)) {
1346 pim_sgaddr sg;
1347
1348 sg.src = *oil_origin(c_oil);
1349 sg.grp = *oil_mcastgrp(c_oil);
1350
1351 zlog_warn(
1352 "ioctl(PIM_SIOCGETSGCNT=%lu) failure for (S,G)=%pSG: errno=%d: %s",
1353 (unsigned long)PIM_SIOCGETSGCNT, &sg, errno,
1354 safe_strerror(errno));
1355 return;
1356 }
1357
1358 c_oil->cc.pktcnt = sgreq.pktcnt;
1359 c_oil->cc.bytecnt = sgreq.bytecnt;
1360 c_oil->cc.wrong_if = sgreq.wrong_if;
1361 return;
1362 }