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