3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
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
30 #include "pim_mroute.h"
34 #include "pim_iface.h"
35 #include "pim_macro.h"
38 #include "pim_register.h"
39 #include "pim_ifchannel.h"
40 #include "pim_zlookup.h"
44 static struct thread
*qpim_mroute_socket_reader
= NULL
;
46 static void mroute_read_on(void);
48 static int pim_mroute_set(int fd
, int enable
)
51 int opt
= enable
? MRT_INIT
: MRT_DONE
;
52 socklen_t opt_len
= sizeof(opt
);
53 int rcvbuf
= 1024 * 1024 * 8;
56 err
= setsockopt(fd
, IPPROTO_IP
, opt
, &opt
, opt_len
);
59 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
60 __FILE__
, __PRETTY_FUNCTION__
, fd
,
61 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
62 safe_strerror(errno
));
66 err
= setsockopt(fd
, SOL_SOCKET
, SO_RCVBUF
, &rcvbuf
, sizeof(rcvbuf
));
69 "%s: failure: setsockopt(fd=%d, SOL_SOCKET, %d): errno=%d: %s",
70 __PRETTY_FUNCTION__
, fd
, rcvbuf
, errno
,
71 safe_strerror(errno
));
74 flags
= fcntl(fd
, F_GETFL
, 0);
76 zlog_warn("Could not get flags on socket fd:%d %d %s", fd
,
77 errno
, safe_strerror(errno
));
81 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
)) {
82 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s", fd
,
83 errno
, safe_strerror(errno
));
90 int upcalls
= IGMPMSG_WRVIFWHOLE
;
93 err
= setsockopt(fd
, IPPROTO_IP
, opt
, &upcalls
,
97 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
98 errno
, safe_strerror(errno
));
103 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
110 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
111 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
113 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
114 const struct igmpmsg
*msg
)
116 struct pim_interface
*pim_ifp
= ifp
->info
;
117 struct pim_upstream
*up
;
121 rpg
= RP(msg
->im_dst
);
123 * If the incoming interface is unknown OR
124 * the Interface type is SSM we don't need to
127 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
128 || (!(PIM_I_am_DR(pim_ifp
)))) {
129 if (PIM_DEBUG_MROUTE_DETAIL
)
131 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
132 __PRETTY_FUNCTION__
);
137 * If we've received a multicast packet that isn't connected to
140 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
141 if (PIM_DEBUG_MROUTE_DETAIL
)
143 "%s: Received incoming packet that doesn't originate on our seg",
144 __PRETTY_FUNCTION__
);
148 memset(&sg
, 0, sizeof(struct prefix_sg
));
149 sg
.src
= msg
->im_src
;
150 sg
.grp
= msg
->im_dst
;
152 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
153 __PRETTY_FUNCTION__
);
155 if (PIM_DEBUG_MROUTE
) {
157 "%s: Failure to add upstream information for %s",
158 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
164 * I moved this debug till after the actual add because
165 * I want to take advantage of the up->sg_str being filled in.
167 if (PIM_DEBUG_MROUTE
) {
168 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
169 __PRETTY_FUNCTION__
, up
->sg_str
);
172 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
173 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
175 up
->channel_oil
->cc
.pktcnt
++;
176 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
177 // resolve mfcc_parent prior to mroute_add in channel_add_oif
178 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
180 vif_index
= pim_if_find_vifindex_by_ifindex(
181 up
->rpf
.source_nexthop
.interface
->ifindex
);
182 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
184 pim_register_join(up
);
189 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
192 struct pim_interface
*pim_ifp
;
195 const struct ip
*ip_hdr
;
196 struct pim_upstream
*up
;
198 ip_hdr
= (const struct ip
*)buf
;
200 memset(&sg
, 0, sizeof(struct prefix_sg
));
201 sg
.src
= ip_hdr
->ip_src
;
202 sg
.grp
= ip_hdr
->ip_dst
;
204 up
= pim_upstream_find(&sg
);
206 struct prefix_sg star
= sg
;
207 star
.src
.s_addr
= INADDR_ANY
;
209 up
= pim_upstream_find(&star
);
211 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
212 up
= pim_upstream_add(&sg
, ifp
,
213 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
214 __PRETTY_FUNCTION__
);
216 if (PIM_DEBUG_MROUTE
)
218 "%s: Unable to create upstream information for %s",
220 pim_str_sg_dump(&sg
));
223 pim_upstream_keep_alive_timer_start(
224 up
, qpim_keep_alive_time
);
225 pim_upstream_inherited_olist(up
);
226 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
228 if (PIM_DEBUG_MROUTE
)
229 zlog_debug("%s: Creating %s upstream on LHR",
230 __PRETTY_FUNCTION__
, up
->sg_str
);
233 if (PIM_DEBUG_MROUTE_DETAIL
) {
235 "%s: Unable to find upstream channel WHOLEPKT%s",
236 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
241 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
245 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
246 || (!(PIM_I_am_DR(pim_ifp
)))) {
247 if (PIM_DEBUG_MROUTE
) {
248 zlog_debug("%s: Failed Check send packet",
249 __PRETTY_FUNCTION__
);
255 * If we've received a register suppress
257 if (!up
->t_rs_timer
) {
258 if (pim_is_grp_ssm(sg
.grp
)) {
259 if (PIM_DEBUG_PIM_REG
)
261 "%s register forward skipped as group is SSM",
262 pim_str_sg_dump(&sg
));
265 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
266 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
267 pim_ifp
->primary_address
, rpg
, 0, up
);
272 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
273 const struct igmpmsg
*msg
)
275 struct pim_ifchannel
*ch
;
276 struct pim_interface
*pim_ifp
;
279 memset(&sg
, 0, sizeof(struct prefix_sg
));
280 sg
.src
= msg
->im_src
;
281 sg
.grp
= msg
->im_dst
;
284 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
286 RFC 4601 4.8.2. PIM-SSM-Only Routers
288 iif is the incoming interface of the packet.
289 if (iif is in inherited_olist(S,G)) {
290 send Assert(S,G) on iif
295 if (PIM_DEBUG_MROUTE
)
297 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
298 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
305 if (PIM_DEBUG_MROUTE
)
307 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
308 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
313 ch
= pim_ifchannel_find(ifp
, &sg
);
315 struct prefix_sg star_g
= sg
;
316 if (PIM_DEBUG_MROUTE
)
318 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
319 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
322 star_g
.src
.s_addr
= INADDR_ANY
;
323 ch
= pim_ifchannel_find(ifp
, &star_g
);
325 if (PIM_DEBUG_MROUTE
)
327 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
329 pim_str_sg_dump(&star_g
), ifp
->name
);
335 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
337 Transitions from NoInfo State
339 An (S,G) data packet arrives on interface I, AND
340 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
341 downstream interface that is in our (S,G) outgoing interface
342 list. We optimistically assume that we will be the assert
343 winner for this (S,G), and so we transition to the "I am Assert
344 Winner" state and perform Actions A1 (below), which will
345 initiate the assert negotiation for (S,G).
348 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
349 if (PIM_DEBUG_MROUTE
) {
351 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
352 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
357 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
358 if (PIM_DEBUG_MROUTE
) {
360 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
361 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
366 if (assert_action_a1(ch
)) {
367 if (PIM_DEBUG_MROUTE
) {
369 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
370 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
378 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
381 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
382 struct pim_interface
*pim_ifp
;
383 struct pim_ifchannel
*ch
;
384 struct pim_upstream
*up
;
385 struct prefix_sg star_g
;
387 struct channel_oil
*oil
;
389 memset(&sg
, 0, sizeof(struct prefix_sg
));
390 sg
.src
= ip_hdr
->ip_src
;
391 sg
.grp
= ip_hdr
->ip_dst
;
393 ch
= pim_ifchannel_find(ifp
, &sg
);
395 if (PIM_DEBUG_MROUTE
)
397 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
398 ch
->sg_str
, ifp
->name
);
403 star_g
.src
.s_addr
= INADDR_ANY
;
405 ch
= pim_ifchannel_find(ifp
, &star_g
);
408 if (PIM_DEBUG_MROUTE
)
409 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
410 pim_str_sg_dump (&star_g
), ifp
->name
);
415 up
= pim_upstream_find(&sg
);
417 struct pim_upstream
*parent
;
418 struct pim_nexthop source
;
419 struct pim_rpf
*rpf
= RP(sg
.grp
);
420 if (!rpf
|| !rpf
->source_nexthop
.interface
)
424 * If we have received a WRVIFWHOLE and are at this
425 * point, we could be receiving the packet on the *,G
426 * tree, let's check and if so we can safely drop
429 parent
= pim_upstream_find(&star_g
);
430 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
433 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
435 memset(&source
, 0, sizeof(source
));
437 * If we are the fhr that means we are getting a callback during
438 * the pimreg period, so I believe we can ignore this packet
440 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
441 // No if channel, but upstream we are at the RP.
442 if (pim_nexthop_lookup(&source
, up
->upstream_register
,
445 pim_register_stop_send(source
.interface
, &sg
,
446 pim_ifp
->primary_address
,
447 up
->upstream_register
);
448 if (!up
->channel_oil
)
449 up
->channel_oil
= pim_channel_oil_add(
450 &sg
, pim_ifp
->mroute_vif_index
);
451 pim_upstream_inherited_olist(up
);
452 if (!up
->channel_oil
->installed
)
453 pim_mroute_add(up
->channel_oil
,
454 __PRETTY_FUNCTION__
);
455 pim_upstream_set_sptbit(up
, ifp
);
457 if (I_am_RP(up
->sg
.grp
)) {
458 if (pim_nexthop_lookup(&source
,
459 up
->upstream_register
, 0)
461 pim_register_stop_send(
462 source
.interface
, &sg
,
463 pim_ifp
->primary_address
,
464 up
->upstream_register
);
465 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
467 pim_upstream_keep_alive_timer_start(
468 up
, qpim_keep_alive_time
);
469 pim_upstream_inherited_olist(up
);
470 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
476 oil
= pim_channel_oil_add(&sg
, pim_ifp
->mroute_vif_index
);
478 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
479 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
480 up
= pim_upstream_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
481 __PRETTY_FUNCTION__
);
483 if (PIM_DEBUG_MROUTE
)
485 "%s: WRONGVIF%s unable to create upstream on interface",
486 pim_str_sg_dump(&sg
), ifp
->name
);
489 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
490 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
491 up
->channel_oil
= oil
;
492 up
->channel_oil
->cc
.pktcnt
++;
493 pim_register_join(up
);
494 pim_upstream_inherited_olist(up
);
496 // Send the packet to the RP
497 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
503 int pim_mroute_msg(int fd
, const char *buf
, int buf_size
)
505 struct interface
*ifp
;
506 struct pim_interface
*pim_ifp
;
507 const struct ip
*ip_hdr
;
508 const struct igmpmsg
*msg
;
509 char ip_src_str
[INET_ADDRSTRLEN
] = "";
510 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
511 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
512 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
513 struct in_addr ifaddr
;
514 struct igmp_sock
*igmp
;
516 ip_hdr
= (const struct ip
*)buf
;
518 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
520 /* We have the IP packet but we do not know which interface this
522 * received on. Find the interface that is on the same subnet as
526 ifp
= pim_if_lookup_address_vrf(ip_hdr
->ip_src
, VRF_DEFAULT
);
529 if (PIM_DEBUG_MROUTE_DETAIL
) {
530 pim_inet4_dump("<src?>", ip_hdr
->ip_src
,
531 ip_src_str
, sizeof(ip_src_str
));
532 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
,
533 ip_dst_str
, sizeof(ip_dst_str
));
536 "%s: igmp kernel upcall could not find usable interface for %s -> %s",
537 __PRETTY_FUNCTION__
, ip_src_str
,
543 ifaddr
= pim_find_primary_addr(ifp
);
544 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
547 if (PIM_DEBUG_MROUTE
) {
548 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
550 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
554 "%s: igmp kernel upcall on %s(%p) for %s -> %s",
555 __PRETTY_FUNCTION__
, ifp
->name
, igmp
,
556 ip_src_str
, ip_dst_str
);
559 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
561 } else if (ip_hdr
->ip_p
) {
562 if (PIM_DEBUG_MROUTE_DETAIL
) {
563 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
565 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
568 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
569 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
574 msg
= (const struct igmpmsg
*)buf
;
576 ifp
= pim_if_find_by_vif_index(msg
->im_vif
);
580 if (PIM_DEBUG_MROUTE
) {
581 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
583 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
586 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
588 igmpmsgtype2str
[msg
->im_msgtype
],
589 msg
->im_msgtype
, ip_hdr
->ip_p
, fd
, src_str
,
590 grp_str
, ifp
->name
, msg
->im_vif
, buf_size
);
593 switch (msg
->im_msgtype
) {
594 case IGMPMSG_WRONGVIF
:
595 return pim_mroute_msg_wrongvif(fd
, ifp
, msg
);
597 case IGMPMSG_NOCACHE
:
598 return pim_mroute_msg_nocache(fd
, ifp
, msg
);
600 case IGMPMSG_WHOLEPKT
:
601 return pim_mroute_msg_wholepkt(fd
, ifp
,
604 case IGMPMSG_WRVIFWHOLE
:
605 return pim_mroute_msg_wrvifwhole(fd
, ifp
,
616 static int mroute_read(struct thread
*t
)
618 static long long count
;
628 rd
= read(fd
, buf
, sizeof(buf
));
632 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
635 if (PIM_DEBUG_MROUTE
)
637 "%s: failure reading fd=%d: errno=%d: %s",
638 __PRETTY_FUNCTION__
, fd
, errno
,
639 safe_strerror(errno
));
643 result
= pim_mroute_msg(fd
, buf
, rd
);
646 if (count
% qpim_packet_process
== 0)
656 static void mroute_read_on()
658 thread_add_read(master
, mroute_read
, 0, qpim_mroute_socket_fd
,
659 &qpim_mroute_socket_reader
);
662 static void mroute_read_off()
664 THREAD_OFF(qpim_mroute_socket_reader
);
667 int pim_mroute_socket_enable()
671 if (pimd_privs
.change(ZPRIVS_RAISE
))
672 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
673 safe_strerror(errno
));
675 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
677 if (pimd_privs
.change(ZPRIVS_LOWER
))
678 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
679 safe_strerror(errno
));
682 zlog_warn("Could not create mroute socket: errno=%d: %s", errno
,
683 safe_strerror(errno
));
687 if (pim_mroute_set(fd
, 1)) {
689 "Could not enable mroute on socket fd=%d: errno=%d: %s",
690 fd
, errno
, safe_strerror(errno
));
695 qpim_mroute_socket_fd
= fd
;
697 qpim_mroute_socket_creation
= pim_time_monotonic_sec();
703 int pim_mroute_socket_disable()
705 if (pim_mroute_set(qpim_mroute_socket_fd
, 0)) {
707 "Could not disable mroute on socket fd=%d: errno=%d: %s",
708 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
712 if (close(qpim_mroute_socket_fd
)) {
713 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
714 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
719 qpim_mroute_socket_fd
= -1;
725 For each network interface (e.g., physical or a virtual tunnel) that
726 would be used for multicast forwarding, a corresponding multicast
727 interface must be added to the kernel.
729 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
732 struct pim_interface
*pim_ifp
= ifp
->info
;
736 memset(&vc
, 0, sizeof(vc
));
737 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
738 #ifdef VIFF_USE_IFINDEX
739 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
741 if (ifaddr
.s_addr
== INADDR_ANY
) {
743 "%s: unnumbered interfaces are not supported on this platform",
744 __PRETTY_FUNCTION__
);
747 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
749 vc
.vifc_flags
= flags
;
750 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
751 vc
.vifc_rate_limit
= 0;
753 #ifdef PIM_DVMRP_TUNNEL
754 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
755 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
756 sizeof(vc
.vifc_rmt_addr
));
760 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_VIF
,
761 (void *)&vc
, sizeof(vc
));
763 char ifaddr_str
[INET_ADDRSTRLEN
];
765 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
769 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
770 __FILE__
, __PRETTY_FUNCTION__
, qpim_mroute_socket_fd
,
771 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
772 safe_strerror(errno
));
779 int pim_mroute_del_vif(int vif_index
)
784 if (PIM_DEBUG_MROUTE
) {
785 struct interface
*ifp
= pim_if_find_by_vif_index(vif_index
);
786 zlog_debug("%s %s: Del Vif %d (%s) ", __FILE__
,
787 __PRETTY_FUNCTION__
, vif_index
,
788 ifp
? ifp
->name
: "NULL");
791 memset(&vc
, 0, sizeof(vc
));
792 vc
.vifc_vifi
= vif_index
;
794 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_VIF
,
795 (void *)&vc
, sizeof(vc
));
798 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
799 __FILE__
, __PRETTY_FUNCTION__
, qpim_mroute_socket_fd
,
800 vif_index
, errno
, safe_strerror(errno
));
807 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
811 int orig_iif_vif
= 0;
813 qpim_mroute_add_last
= pim_time_monotonic_sec();
814 ++qpim_mroute_add_events
;
816 /* Do not install route if incoming interface is undefined. */
817 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
818 if (PIM_DEBUG_MROUTE
) {
821 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
822 __PRETTY_FUNCTION__
, name
,
823 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
828 /* The linux kernel *expects* the incoming
829 * vif to be part of the outgoing list
830 * in the case of a (*,G).
832 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
833 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
834 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
838 * If we have an unresolved cache entry for the S,G
839 * it is owned by the pimreg for the incoming IIF
840 * So set pimreg as the IIF temporarily to cause
841 * the packets to be forwarded. Then set it
842 * to the correct IIF afterwords.
844 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
845 && c_oil
->oil
.mfcc_parent
!= 0) {
846 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
847 c_oil
->oil
.mfcc_parent
= 0;
849 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
850 &c_oil
->oil
, sizeof(c_oil
->oil
));
852 if (!err
&& !c_oil
->installed
853 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
854 && orig_iif_vif
!= 0) {
855 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
856 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
857 &c_oil
->oil
, sizeof(c_oil
->oil
));
860 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
861 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
865 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
866 __FILE__
, __PRETTY_FUNCTION__
, qpim_mroute_socket_fd
,
867 errno
, safe_strerror(errno
));
871 if (PIM_DEBUG_MROUTE
) {
873 zlog_debug("%s(%s), Added Route: %s", __PRETTY_FUNCTION__
, name
,
874 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
877 c_oil
->installed
= 1;
881 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
885 qpim_mroute_del_last
= pim_time_monotonic_sec();
886 ++qpim_mroute_del_events
;
888 if (!c_oil
->installed
) {
889 if (PIM_DEBUG_MROUTE
) {
892 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
893 __FILE__
, __PRETTY_FUNCTION__
,
894 c_oil
->oil
.mfcc_parent
,
895 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
900 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_MFC
,
901 &c_oil
->oil
, sizeof(c_oil
->oil
));
903 if (PIM_DEBUG_MROUTE
)
905 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
906 __FILE__
, __PRETTY_FUNCTION__
,
907 qpim_mroute_socket_fd
, errno
,
908 safe_strerror(errno
));
912 if (PIM_DEBUG_MROUTE
) {
914 zlog_debug("%s(%s), Deleted Route: %s", __PRETTY_FUNCTION__
,
915 name
, pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
918 // Reset kernel installed flag
919 c_oil
->installed
= 0;
924 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
926 struct sioc_sg_req sgreq
;
928 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
929 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
930 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
932 if (!c_oil
->installed
) {
933 c_oil
->cc
.lastused
= 100 * qpim_keep_alive_time
;
934 if (PIM_DEBUG_MROUTE
) {
937 sg
.src
= c_oil
->oil
.mfcc_origin
;
938 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
939 if (PIM_DEBUG_MROUTE
)
941 "Channel(%s) is not installed no need to collect data from kernel",
942 pim_str_sg_dump(&sg
));
947 memset(&sgreq
, 0, sizeof(sgreq
));
948 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
949 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
951 pim_zlookup_sg_statistics(c_oil
);
952 if (ioctl(qpim_mroute_socket_fd
, SIOCGETSGCNT
, &sgreq
)) {
953 if (PIM_DEBUG_MROUTE
) {
956 sg
.src
= c_oil
->oil
.mfcc_origin
;
957 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
960 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
961 (unsigned long)SIOCGETSGCNT
,
962 pim_str_sg_dump(&sg
), errno
,
963 safe_strerror(errno
));
968 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
969 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
970 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;