]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_mroute.c
pim6d: Changing igmp_enable to gm_enable.
[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) {
638 if (PIM_DEBUG_IGMP_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->u.prefix4;
648 igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, ifaddr);
649
650 if (PIM_DEBUG_IGMP_PACKETS) {
651 zlog_debug(
652 "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4",
653 __func__, pim->vrf->name, ifp->name, igmp,
654 &ip_hdr->ip_src, &ip_hdr->ip_dst);
655 }
656 if (igmp)
657 pim_igmp_packet(igmp, (char *)buf, buf_size);
658 else if (PIM_DEBUG_IGMP_PACKETS) {
659 zlog_debug(
660 "No IGMP socket on interface: %s with connected source: %pFX",
661 ifp->name, connected_src);
662 }
663 return 0;
664 }
665 #endif
666
667 int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size,
668 ifindex_t ifindex)
669 {
670 struct interface *ifp;
671 const ipv_hdr *ip_hdr;
672 const kernmsg *msg;
673
674 if (buf_size < (int)sizeof(ipv_hdr))
675 return 0;
676
677 ip_hdr = (const ipv_hdr *)buf;
678
679 #if PIM_IPV == 4
680 if (ip_hdr->ip_p == IPPROTO_IGMP) {
681 process_igmp_packet(pim, buf, buf_size, ifindex);
682 } else if (ip_hdr->ip_p) {
683 if (PIM_DEBUG_MROUTE_DETAIL) {
684 zlog_debug(
685 "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%ld",
686 __func__, ip_hdr->ip_p, &ip_hdr->ip_src,
687 &ip_hdr->ip_dst, (long int)buf_size);
688 }
689
690 } else {
691 #else
692
693 if ((ip_hdr->ip6_vfc & 0xf) == 0) {
694 #endif
695 msg = (const kernmsg *)buf;
696
697 ifp = pim_if_find_by_vif_index(pim, msg->msg_im_vif);
698
699 if (!ifp)
700 return 0;
701 if (PIM_DEBUG_MROUTE) {
702 #if PIM_IPV == 4
703 zlog_debug(
704 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%ld",
705 __func__, gmmsgtype2str[msg->msg_im_msgtype],
706 msg->msg_im_msgtype, ip_hdr->ip_p,
707 pim->mroute_socket, &msg->msg_im_src,
708 &msg->msg_im_dst, ifp->name, msg->msg_im_vif,
709 (long int)buf_size);
710 #else
711 zlog_debug(
712 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI6,%pI6) on %s vifi=%d size=%ld",
713 __func__, gmmsgtype2str[msg->msg_im_msgtype],
714 msg->msg_im_msgtype, ip_hdr->ip6_nxt,
715 pim->mroute_socket, &msg->msg_im_src,
716 &msg->msg_im_dst, ifp->name, msg->msg_im_vif,
717 (long int)buf_size);
718 #endif
719 }
720
721 switch (msg->msg_im_msgtype) {
722 case GMMSG_WRONGVIF:
723 return pim_mroute_msg_wrongvif(pim->mroute_socket, ifp,
724 msg);
725 case GMMSG_NOCACHE:
726 return pim_mroute_msg_nocache(pim->mroute_socket, ifp,
727 msg);
728 case GMMSG_WHOLEPKT:
729 return pim_mroute_msg_wholepkt(pim->mroute_socket, ifp,
730 (const char *)msg,
731 buf_size);
732 case GMMSG_WRVIFWHOLE:
733 return pim_mroute_msg_wrvifwhole(pim->mroute_socket,
734 ifp, (const char *)msg,
735 buf_size);
736 default:
737 break;
738 }
739 }
740
741 return 0;
742 }
743
744 static void mroute_read(struct thread *t)
745 {
746 struct pim_instance *pim;
747 static long long count;
748 char buf[10000];
749 int cont = 1;
750 int rd;
751 ifindex_t ifindex;
752 pim = THREAD_ARG(t);
753
754 while (cont) {
755 rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf,
756 sizeof(buf), NULL, NULL, NULL, NULL,
757 &ifindex);
758 if (rd <= 0) {
759 if (errno == EINTR)
760 continue;
761 if (errno == EWOULDBLOCK || errno == EAGAIN)
762 break;
763
764 zlog_warn(
765 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
766 __func__, rd, pim->mroute_socket, errno,
767 safe_strerror(errno));
768 goto done;
769 }
770
771 pim_mroute_msg(pim, buf, rd, ifindex);
772
773 count++;
774 if (count % router->packet_process == 0)
775 cont = 0;
776 }
777 /* Keep reading */
778 done:
779 mroute_read_on(pim);
780
781 return;
782 }
783
784 static void mroute_read_on(struct pim_instance *pim)
785 {
786 thread_add_read(router->master, mroute_read, pim, pim->mroute_socket,
787 &pim->thread);
788 }
789
790 static void mroute_read_off(struct pim_instance *pim)
791 {
792 THREAD_OFF(pim->thread);
793 }
794
795 int pim_mroute_socket_enable(struct pim_instance *pim)
796 {
797 int fd;
798
799 frr_with_privs(&pimd_privs) {
800
801 #if PIM_IPV == 4
802 fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
803 #else
804 fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
805 #endif
806 if (fd < 0) {
807 zlog_warn("Could not create mroute socket: errno=%d: %s",
808 errno,
809 safe_strerror(errno));
810 return -2;
811 }
812
813 #if PIM_IPV == 6
814 struct icmp6_filter filter[1];
815 int ret;
816
817 /* Unlike IPv4, this socket is not used for MLD, so just drop
818 * everything with an empty ICMP6 filter. Otherwise we get
819 * all kinds of garbage here, possibly even non-multicast
820 * related ICMPv6 traffic (e.g. ping)
821 *
822 * (mroute kernel upcall "packets" are injected directly on the
823 * socket, this sockopt -or any other- has no effect on them)
824 */
825 ICMP6_FILTER_SETBLOCKALL(filter);
826 ret = setsockopt(fd, SOL_ICMPV6, ICMP6_FILTER, filter,
827 sizeof(filter));
828 if (ret)
829 zlog_err(
830 "(VRF %s) failed to set mroute control filter: %m",
831 pim->vrf->name);
832 #endif
833
834 #ifdef SO_BINDTODEVICE
835 if (pim->vrf->vrf_id != VRF_DEFAULT
836 && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
837 pim->vrf->name, strlen(pim->vrf->name))) {
838 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
839 safe_strerror(errno));
840 close(fd);
841 return -3;
842 }
843 #endif
844
845 }
846
847 pim->mroute_socket = fd;
848 if (pim_mroute_set(pim, 1)) {
849 zlog_warn(
850 "Could not enable mroute on socket fd=%d: errno=%d: %s",
851 fd, errno, safe_strerror(errno));
852 close(fd);
853 pim->mroute_socket = -1;
854 return -3;
855 }
856
857 pim->mroute_socket_creation = pim_time_monotonic_sec();
858
859 mroute_read_on(pim);
860
861 return 0;
862 }
863
864 int pim_mroute_socket_disable(struct pim_instance *pim)
865 {
866 if (pim_mroute_set(pim, 0)) {
867 zlog_warn(
868 "Could not disable mroute on socket fd=%d: errno=%d: %s",
869 pim->mroute_socket, errno, safe_strerror(errno));
870 return -2;
871 }
872
873 if (close(pim->mroute_socket)) {
874 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
875 pim->mroute_socket, errno, safe_strerror(errno));
876 return -3;
877 }
878
879 mroute_read_off(pim);
880 pim->mroute_socket = -1;
881
882 return 0;
883 }
884
885 /*
886 For each network interface (e.g., physical or a virtual tunnel) that
887 would be used for multicast forwarding, a corresponding multicast
888 interface must be added to the kernel.
889 */
890 int pim_mroute_add_vif(struct interface *ifp, pim_addr ifaddr,
891 unsigned char flags)
892 {
893 struct pim_interface *pim_ifp = ifp->info;
894 pim_vifctl vc;
895 int err;
896
897 if (PIM_DEBUG_MROUTE)
898 zlog_debug("%s: Add Vif %d (%s[%s])", __func__,
899 pim_ifp->mroute_vif_index, ifp->name,
900 pim_ifp->pim->vrf->name);
901
902 memset(&vc, 0, sizeof(vc));
903 vc.vc_vifi = pim_ifp->mroute_vif_index;
904 #if PIM_IPV == 4
905 #ifdef VIFF_USE_IFINDEX
906 vc.vc_lcl_ifindex = ifp->ifindex;
907 #else
908 if (ifaddr.s_addr == INADDR_ANY) {
909 zlog_warn(
910 "%s: unnumbered interfaces are not supported on this platform",
911 __func__);
912 return -1;
913 }
914 memcpy(&vc.vc_lcl_addr, &ifaddr, sizeof(vc.vc_lcl_addr));
915 #endif
916 #else
917 vc.vc_pifi = ifp->ifindex;
918 #endif
919 vc.vc_flags = flags;
920 vc.vc_threshold = PIM_MROUTE_MIN_TTL;
921 vc.vc_rate_limit = 0;
922
923 #if PIM_IPV == 4
924 #ifdef PIM_DVMRP_TUNNEL
925 if (vc.vc_flags & VIFF_TUNNEL) {
926 memcpy(&vc.vc_rmt_addr, &vif_remote_addr,
927 sizeof(vc.vc_rmt_addr));
928 }
929 #endif
930 #endif
931
932 err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_ADD_VIF,
933 (void *)&vc, sizeof(vc));
934 if (err) {
935 zlog_warn(
936 "%s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_VIF,vif_index=%d,ifaddr=%pPAs,flag=%d): errno=%d: %s",
937 __func__, pim_ifp->pim->mroute_socket, ifp->ifindex,
938 &ifaddr, flags, errno, safe_strerror(errno));
939 return -2;
940 }
941
942 return 0;
943 }
944
945 int pim_mroute_del_vif(struct interface *ifp)
946 {
947 struct pim_interface *pim_ifp = ifp->info;
948 pim_vifctl vc;
949 int err;
950
951 if (PIM_DEBUG_MROUTE)
952 zlog_debug("%s: Del Vif %d (%s[%s])", __func__,
953 pim_ifp->mroute_vif_index, ifp->name,
954 pim_ifp->pim->vrf->name);
955
956 memset(&vc, 0, sizeof(vc));
957 vc.vc_vifi = pim_ifp->mroute_vif_index;
958
959 err = setsockopt(pim_ifp->pim->mroute_socket, PIM_IPPROTO, MRT_DEL_VIF,
960 (void *)&vc, sizeof(vc));
961 if (err) {
962 zlog_warn(
963 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
964 __FILE__, __func__, pim_ifp->pim->mroute_socket,
965 pim_ifp->mroute_vif_index, errno, safe_strerror(errno));
966 return -2;
967 }
968
969 return 0;
970 }
971
972 /*
973 * Prevent creating MFC entry with OIF=IIF.
974 *
975 * This is a protection against implementation mistakes.
976 *
977 * PIM protocol implicitely ensures loopfree multicast topology.
978 *
979 * IGMP must be protected against adding looped MFC entries created
980 * by both source and receiver attached to the same interface. See
981 * TODO T22.
982 * We shall allow igmp to create upstream when it is DR for the intf.
983 * Assume RP reachable via non DR.
984 */
985 bool pim_mroute_allow_iif_in_oil(struct channel_oil *c_oil,
986 int oif_index)
987 {
988 #ifdef PIM_ENFORCE_LOOPFREE_MFC
989 struct interface *ifp_out;
990 struct pim_interface *pim_ifp;
991
992 if (c_oil->up &&
993 PIM_UPSTREAM_FLAG_TEST_ALLOW_IIF_IN_OIL(c_oil->up->flags))
994 return true;
995
996 ifp_out = pim_if_find_by_vif_index(c_oil->pim, oif_index);
997 if (!ifp_out)
998 return false;
999 pim_ifp = ifp_out->info;
1000 if (!pim_ifp)
1001 return false;
1002 if ((c_oil->oif_flags[oif_index] & PIM_OIF_FLAG_PROTO_GM) &&
1003 PIM_I_am_DR(pim_ifp))
1004 return true;
1005
1006 return false;
1007 #else
1008 return true;
1009 #endif
1010 }
1011
1012 static inline void pim_mroute_copy(struct channel_oil *out,
1013 struct channel_oil *in)
1014 {
1015 int i;
1016
1017 *oil_origin(out) = *oil_origin(in);
1018 *oil_mcastgrp(out) = *oil_mcastgrp(in);
1019 *oil_parent(out) = *oil_parent(in);
1020
1021 for (i = 0; i < MAXVIFS; ++i) {
1022 if (*oil_parent(out) == i &&
1023 !pim_mroute_allow_iif_in_oil(in, i)) {
1024 oil_if_set(out, i, 0);
1025 continue;
1026 }
1027
1028 if (in->oif_flags[i] & PIM_OIF_FLAG_MUTE)
1029 oil_if_set(out, i, 0);
1030 else
1031 oil_if_set(out, i, oil_if_has(in, i));
1032 }
1033 }
1034
1035 /* This function must not be called directly 0
1036 * use pim_upstream_mroute_add or pim_static_mroute_add instead
1037 */
1038 static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
1039 {
1040 struct pim_instance *pim = c_oil->pim;
1041 struct channel_oil tmp_oil[1] = { };
1042 int err;
1043
1044 pim->mroute_add_last = pim_time_monotonic_sec();
1045 ++pim->mroute_add_events;
1046
1047 /* Copy the oil to a temporary structure to fixup (without need to
1048 * later restore) before sending the mroute add to the dataplane
1049 */
1050 pim_mroute_copy(tmp_oil, c_oil);
1051
1052 /* The linux kernel *expects* the incoming
1053 * vif to be part of the outgoing list
1054 * in the case of a (*,G).
1055 */
1056 if (pim_addr_is_any(*oil_origin(c_oil))) {
1057 oil_if_set(tmp_oil, *oil_parent(c_oil), 1);
1058 }
1059
1060 /*
1061 * If we have an unresolved cache entry for the S,G
1062 * it is owned by the pimreg for the incoming IIF
1063 * So set pimreg as the IIF temporarily to cause
1064 * the packets to be forwarded. Then set it
1065 * to the correct IIF afterwords.
1066 */
1067 if (!c_oil->installed && !pim_addr_is_any(*oil_origin(c_oil))
1068 && *oil_parent(c_oil) != 0) {
1069 *oil_parent(tmp_oil) = 0;
1070 }
1071 /* For IPv6 MRT_ADD_MFC is defined to MRT6_ADD_MFC */
1072 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
1073 &tmp_oil->oil, sizeof(tmp_oil->oil));
1074
1075 if (!err && !c_oil->installed
1076 && !pim_addr_is_any(*oil_origin(c_oil))
1077 && *oil_parent(c_oil) != 0) {
1078 *oil_parent(tmp_oil) = *oil_parent(c_oil);
1079 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_ADD_MFC,
1080 &tmp_oil->oil, sizeof(tmp_oil->oil));
1081 }
1082
1083 if (err) {
1084 zlog_warn(
1085 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_ADD_MFC): errno=%d: %s",
1086 __FILE__, __func__, pim->mroute_socket, errno,
1087 safe_strerror(errno));
1088 return -2;
1089 }
1090
1091 if (PIM_DEBUG_MROUTE) {
1092 char buf[1000];
1093 zlog_debug("%s(%s), vrf %s Added Route: %s", __func__, name,
1094 pim->vrf->name,
1095 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1096 }
1097
1098 if (!c_oil->installed) {
1099 c_oil->installed = 1;
1100 c_oil->mroute_creation = pim_time_monotonic_sec();
1101 }
1102
1103 return 0;
1104 }
1105
1106 static int pim_upstream_get_mroute_iif(struct channel_oil *c_oil,
1107 const char *name)
1108 {
1109 vifi_t iif = MAXVIFS;
1110 struct interface *ifp = NULL;
1111 struct pim_interface *pim_ifp;
1112 struct pim_upstream *up = c_oil->up;
1113
1114 if (up) {
1115 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
1116 if (up->parent)
1117 ifp = up->parent->rpf.source_nexthop.interface;
1118 } else {
1119 ifp = up->rpf.source_nexthop.interface;
1120 }
1121 if (ifp) {
1122 pim_ifp = (struct pim_interface *)ifp->info;
1123 if (pim_ifp)
1124 iif = pim_ifp->mroute_vif_index;
1125 }
1126 }
1127 return iif;
1128 }
1129
1130 static int pim_upstream_mroute_update(struct channel_oil *c_oil,
1131 const char *name)
1132 {
1133 char buf[1000];
1134
1135 if (*oil_parent(c_oil) >= MAXVIFS) {
1136 /* the c_oil cannot be installed as a mroute yet */
1137 if (PIM_DEBUG_MROUTE)
1138 zlog_debug(
1139 "%s(%s) %s mroute not ready to be installed; %s",
1140 __func__, name,
1141 pim_channel_oil_dump(c_oil, buf,
1142 sizeof(buf)),
1143 c_oil->installed ?
1144 "uninstall" : "skip");
1145 /* if already installed flush it out as we are going to stop
1146 * updates to it leaving it in a stale state
1147 */
1148 if (c_oil->installed)
1149 pim_mroute_del(c_oil, name);
1150 /* return success (skipped) */
1151 return 0;
1152 }
1153
1154 return pim_mroute_add(c_oil, name);
1155 }
1156
1157 /* IIF associated with SGrpt entries are re-evaluated when the parent
1158 * (*,G) entries IIF changes
1159 */
1160 static void pim_upstream_all_sources_iif_update(struct pim_upstream *up)
1161 {
1162 struct listnode *listnode;
1163 struct pim_upstream *child;
1164
1165 for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
1166 child)) {
1167 if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(child->flags))
1168 pim_upstream_mroute_iif_update(child->channel_oil,
1169 __func__);
1170 }
1171 }
1172
1173 /* In the case of "PIM state machine" added mroutes an upstream entry
1174 * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
1175 */
1176 int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
1177 {
1178 vifi_t iif;
1179
1180 iif = pim_upstream_get_mroute_iif(c_oil, name);
1181
1182 if (*oil_parent(c_oil) != iif) {
1183 *oil_parent(c_oil) = iif;
1184 if (pim_addr_is_any(*oil_origin(c_oil)) &&
1185 c_oil->up)
1186 pim_upstream_all_sources_iif_update(c_oil->up);
1187 } else {
1188 *oil_parent(c_oil) = iif;
1189 }
1190
1191 return pim_upstream_mroute_update(c_oil, name);
1192 }
1193
1194 /* Look for IIF changes and update the dateplane entry only if the IIF
1195 * has changed.
1196 */
1197 int pim_upstream_mroute_iif_update(struct channel_oil *c_oil, const char *name)
1198 {
1199 vifi_t iif;
1200 char buf[1000];
1201
1202 iif = pim_upstream_get_mroute_iif(c_oil, name);
1203 if (*oil_parent(c_oil) == iif) {
1204 /* no change */
1205 return 0;
1206 }
1207 *oil_parent(c_oil) = iif;
1208
1209 if (pim_addr_is_any(*oil_origin(c_oil)) &&
1210 c_oil->up)
1211 pim_upstream_all_sources_iif_update(c_oil->up);
1212
1213 if (PIM_DEBUG_MROUTE_DETAIL)
1214 zlog_debug("%s(%s) %s mroute iif update %d",
1215 __func__, name,
1216 pim_channel_oil_dump(c_oil, buf,
1217 sizeof(buf)), iif);
1218 /* XXX: is this hack needed? */
1219 c_oil->oil_inherited_rescan = 1;
1220 return pim_upstream_mroute_update(c_oil, name);
1221 }
1222
1223 int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
1224 {
1225 return pim_mroute_add(c_oil, name);
1226 }
1227
1228 void pim_static_mroute_iif_update(struct channel_oil *c_oil,
1229 int input_vif_index,
1230 const char *name)
1231 {
1232 if (*oil_parent(c_oil) == input_vif_index)
1233 return;
1234
1235 *oil_parent(c_oil) = input_vif_index;
1236 if (input_vif_index == MAXVIFS)
1237 pim_mroute_del(c_oil, name);
1238 else
1239 pim_static_mroute_add(c_oil, name);
1240 }
1241
1242 int pim_mroute_del(struct channel_oil *c_oil, const char *name)
1243 {
1244 struct pim_instance *pim = c_oil->pim;
1245 int err;
1246
1247 pim->mroute_del_last = pim_time_monotonic_sec();
1248 ++pim->mroute_del_events;
1249
1250 if (!c_oil->installed) {
1251 if (PIM_DEBUG_MROUTE) {
1252 char buf[1000];
1253 zlog_debug(
1254 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
1255 __FILE__, __func__, *oil_parent(c_oil),
1256 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1257 }
1258 return -2;
1259 }
1260
1261 err = setsockopt(pim->mroute_socket, PIM_IPPROTO, MRT_DEL_MFC,
1262 &c_oil->oil, sizeof(c_oil->oil));
1263 if (err) {
1264 if (PIM_DEBUG_MROUTE)
1265 zlog_warn(
1266 "%s %s: failure: setsockopt(fd=%d,PIM_IPPROTO,MRT_DEL_MFC): errno=%d: %s",
1267 __FILE__, __func__, pim->mroute_socket, errno,
1268 safe_strerror(errno));
1269 return -2;
1270 }
1271
1272 if (PIM_DEBUG_MROUTE) {
1273 char buf[1000];
1274 zlog_debug("%s(%s), vrf %s Deleted Route: %s", __func__, name,
1275 pim->vrf->name,
1276 pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
1277 }
1278
1279 // Reset kernel installed flag
1280 c_oil->installed = 0;
1281
1282 return 0;
1283 }
1284
1285 void pim_mroute_update_counters(struct channel_oil *c_oil)
1286 {
1287 struct pim_instance *pim = c_oil->pim;
1288 pim_sioc_sg_req sgreq;
1289
1290 c_oil->cc.oldpktcnt = c_oil->cc.pktcnt;
1291 c_oil->cc.oldbytecnt = c_oil->cc.bytecnt;
1292 c_oil->cc.oldwrong_if = c_oil->cc.wrong_if;
1293
1294 if (!c_oil->installed) {
1295 c_oil->cc.lastused = 100 * pim->keep_alive_time;
1296 if (PIM_DEBUG_MROUTE) {
1297 pim_sgaddr sg;
1298
1299 sg.src = *oil_origin(c_oil);
1300 sg.grp = *oil_mcastgrp(c_oil);
1301 zlog_debug("Channel%pSG is not installed no need to collect data from kernel",
1302 &sg);
1303 }
1304 return;
1305 }
1306
1307
1308 memset(&sgreq, 0, sizeof(sgreq));
1309
1310 pim_zlookup_sg_statistics(c_oil);
1311
1312 #if PIM_IPV == 4
1313 sgreq.src = *oil_origin(c_oil);
1314 sgreq.grp = *oil_mcastgrp(c_oil);
1315 #else
1316 sgreq.src = c_oil->oil.mf6cc_origin;
1317 sgreq.grp = c_oil->oil.mf6cc_mcastgrp;
1318 #endif
1319 if (ioctl(pim->mroute_socket, PIM_SIOCGETSGCNT, &sgreq)) {
1320 pim_sgaddr sg;
1321
1322 sg.src = *oil_origin(c_oil);
1323 sg.grp = *oil_mcastgrp(c_oil);
1324
1325 zlog_warn(
1326 "ioctl(PIM_SIOCGETSGCNT=%lu) failure for (S,G)=%pSG: errno=%d: %s",
1327 (unsigned long)PIM_SIOCGETSGCNT, &sg, errno,
1328 safe_strerror(errno));
1329 return;
1330 }
1331
1332 c_oil->cc.pktcnt = sgreq.pktcnt;
1333 c_oil->cc.bytecnt = sgreq.bytecnt;
1334 c_oil->cc.wrong_if = sgreq.wrong_if;
1335 return;
1336 }