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
31 #include "pim_mroute.h"
35 #include "pim_iface.h"
36 #include "pim_macro.h"
39 #include "pim_register.h"
40 #include "pim_ifchannel.h"
41 #include "pim_zlookup.h"
45 static struct thread
*qpim_mroute_socket_reader
= NULL
;
47 static void mroute_read_on(void);
49 static int pim_mroute_set(struct pim_instance
*pim
, int enable
)
52 int opt
= enable
? MRT_INIT
: MRT_DONE
;
53 socklen_t opt_len
= sizeof(opt
);
56 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &opt
, opt_len
);
59 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
60 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
61 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
62 safe_strerror(errno
));
67 * We need to create the VRF table for the pim mroute_socket
69 if (pim
->vrf_id
!= VRF_DEFAULT
) {
71 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_TABLE
,
75 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
76 __FILE__
, __PRETTY_FUNCTION__
,
77 pim
->mroute_socket
, opt
, errno
,
78 safe_strerror(errno
));
83 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
85 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
87 zlog_warn("Could not get flags on socket fd:%d %d %s",
88 pim
->mroute_socket
, errno
, safe_strerror(errno
));
89 close(pim
->mroute_socket
);
92 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
93 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
94 pim
->mroute_socket
, errno
, safe_strerror(errno
));
95 close(pim
->mroute_socket
);
101 int upcalls
= IGMPMSG_WRVIFWHOLE
;
104 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
108 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
109 errno
, safe_strerror(errno
));
114 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
121 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
122 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
124 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
125 const struct igmpmsg
*msg
)
127 struct pim_interface
*pim_ifp
= ifp
->info
;
128 struct pim_upstream
*up
;
132 rpg
= RP(msg
->im_dst
);
134 * If the incoming interface is unknown OR
135 * the Interface type is SSM we don't need to
138 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
139 || (!(PIM_I_am_DR(pim_ifp
)))) {
140 if (PIM_DEBUG_MROUTE_DETAIL
)
142 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
143 __PRETTY_FUNCTION__
);
148 * If we've received a multicast packet that isn't connected to
151 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
152 if (PIM_DEBUG_MROUTE_DETAIL
)
154 "%s: Received incoming packet that doesn't originate on our seg",
155 __PRETTY_FUNCTION__
);
159 memset(&sg
, 0, sizeof(struct prefix_sg
));
160 sg
.src
= msg
->im_src
;
161 sg
.grp
= msg
->im_dst
;
163 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
164 __PRETTY_FUNCTION__
);
166 if (PIM_DEBUG_MROUTE
) {
168 "%s: Failure to add upstream information for %s",
169 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
175 * I moved this debug till after the actual add because
176 * I want to take advantage of the up->sg_str being filled in.
178 if (PIM_DEBUG_MROUTE
) {
179 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
180 __PRETTY_FUNCTION__
, up
->sg_str
);
183 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
184 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
186 up
->channel_oil
->cc
.pktcnt
++;
187 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
188 // resolve mfcc_parent prior to mroute_add in channel_add_oif
189 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
191 vif_index
= pim_if_find_vifindex_by_ifindex(
192 up
->rpf
.source_nexthop
.interface
->ifindex
);
193 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
195 pim_register_join(up
);
200 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
203 struct pim_interface
*pim_ifp
;
206 const struct ip
*ip_hdr
;
207 struct pim_upstream
*up
;
209 ip_hdr
= (const struct ip
*)buf
;
211 memset(&sg
, 0, sizeof(struct prefix_sg
));
212 sg
.src
= ip_hdr
->ip_src
;
213 sg
.grp
= ip_hdr
->ip_dst
;
215 up
= pim_upstream_find(&sg
);
217 struct prefix_sg star
= sg
;
218 star
.src
.s_addr
= INADDR_ANY
;
220 up
= pim_upstream_find(&star
);
222 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
223 up
= pim_upstream_add(&sg
, ifp
,
224 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
225 __PRETTY_FUNCTION__
);
227 if (PIM_DEBUG_MROUTE
)
229 "%s: Unable to create upstream information for %s",
231 pim_str_sg_dump(&sg
));
234 pim_upstream_keep_alive_timer_start(
235 up
, qpim_keep_alive_time
);
236 pim_upstream_inherited_olist(up
);
237 pim_upstream_switch(up
, PIM_UPSTREAM_JOINED
);
239 if (PIM_DEBUG_MROUTE
)
240 zlog_debug("%s: Creating %s upstream on LHR",
241 __PRETTY_FUNCTION__
, up
->sg_str
);
244 if (PIM_DEBUG_MROUTE_DETAIL
) {
246 "%s: Unable to find upstream channel WHOLEPKT%s",
247 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
252 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
256 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
257 || (!(PIM_I_am_DR(pim_ifp
)))) {
258 if (PIM_DEBUG_MROUTE
) {
259 zlog_debug("%s: Failed Check send packet",
260 __PRETTY_FUNCTION__
);
266 * If we've received a register suppress
268 if (!up
->t_rs_timer
) {
269 if (pim_is_grp_ssm(sg
.grp
)) {
270 if (PIM_DEBUG_PIM_REG
)
272 "%s register forward skipped as group is SSM",
273 pim_str_sg_dump(&sg
));
276 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
277 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
278 pim_ifp
->primary_address
, rpg
, 0, up
);
283 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
284 const struct igmpmsg
*msg
)
286 struct pim_ifchannel
*ch
;
287 struct pim_interface
*pim_ifp
;
290 memset(&sg
, 0, sizeof(struct prefix_sg
));
291 sg
.src
= msg
->im_src
;
292 sg
.grp
= msg
->im_dst
;
295 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
297 RFC 4601 4.8.2. PIM-SSM-Only Routers
299 iif is the incoming interface of the packet.
300 if (iif is in inherited_olist(S,G)) {
301 send Assert(S,G) on iif
306 if (PIM_DEBUG_MROUTE
)
308 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
309 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
316 if (PIM_DEBUG_MROUTE
)
318 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
319 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
324 ch
= pim_ifchannel_find(ifp
, &sg
);
326 struct prefix_sg star_g
= sg
;
327 if (PIM_DEBUG_MROUTE
)
329 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
330 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
333 star_g
.src
.s_addr
= INADDR_ANY
;
334 ch
= pim_ifchannel_find(ifp
, &star_g
);
336 if (PIM_DEBUG_MROUTE
)
338 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
340 pim_str_sg_dump(&star_g
), ifp
->name
);
346 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
348 Transitions from NoInfo State
350 An (S,G) data packet arrives on interface I, AND
351 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
352 downstream interface that is in our (S,G) outgoing interface
353 list. We optimistically assume that we will be the assert
354 winner for this (S,G), and so we transition to the "I am Assert
355 Winner" state and perform Actions A1 (below), which will
356 initiate the assert negotiation for (S,G).
359 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
360 if (PIM_DEBUG_MROUTE
) {
362 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
363 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
368 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
369 if (PIM_DEBUG_MROUTE
) {
371 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
372 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
377 if (assert_action_a1(ch
)) {
378 if (PIM_DEBUG_MROUTE
) {
380 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
381 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
389 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
392 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
393 struct pim_interface
*pim_ifp
;
394 struct pim_ifchannel
*ch
;
395 struct pim_upstream
*up
;
396 struct prefix_sg star_g
;
398 struct channel_oil
*oil
;
400 memset(&sg
, 0, sizeof(struct prefix_sg
));
401 sg
.src
= ip_hdr
->ip_src
;
402 sg
.grp
= ip_hdr
->ip_dst
;
404 ch
= pim_ifchannel_find(ifp
, &sg
);
406 if (PIM_DEBUG_MROUTE
)
408 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
409 ch
->sg_str
, ifp
->name
);
414 star_g
.src
.s_addr
= INADDR_ANY
;
416 ch
= pim_ifchannel_find(ifp
, &star_g
);
419 if (PIM_DEBUG_MROUTE
)
420 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
421 pim_str_sg_dump (&star_g
), ifp
->name
);
426 up
= pim_upstream_find(&sg
);
428 struct pim_upstream
*parent
;
429 struct pim_nexthop source
;
430 struct pim_rpf
*rpf
= RP(sg
.grp
);
431 if (!rpf
|| !rpf
->source_nexthop
.interface
)
435 * If we have received a WRVIFWHOLE and are at this
436 * point, we could be receiving the packet on the *,G
437 * tree, let's check and if so we can safely drop
440 parent
= pim_upstream_find(&star_g
);
441 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
444 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
446 memset(&source
, 0, sizeof(source
));
448 * If we are the fhr that means we are getting a callback during
449 * the pimreg period, so I believe we can ignore this packet
451 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
452 // No if channel, but upstream we are at the RP.
453 if (pim_nexthop_lookup(&source
, up
->upstream_register
,
456 pim_register_stop_send(source
.interface
, &sg
,
457 pim_ifp
->primary_address
,
458 up
->upstream_register
);
459 if (!up
->channel_oil
)
460 up
->channel_oil
= pim_channel_oil_add(
461 &sg
, pim_ifp
->mroute_vif_index
);
462 pim_upstream_inherited_olist(up
);
463 if (!up
->channel_oil
->installed
)
464 pim_mroute_add(up
->channel_oil
,
465 __PRETTY_FUNCTION__
);
466 pim_upstream_set_sptbit(up
, ifp
);
468 if (I_am_RP(up
->sg
.grp
)) {
469 if (pim_nexthop_lookup(&source
,
470 up
->upstream_register
, 0)
472 pim_register_stop_send(
473 source
.interface
, &sg
,
474 pim_ifp
->primary_address
,
475 up
->upstream_register
);
476 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
478 pim_upstream_keep_alive_timer_start(
479 up
, qpim_keep_alive_time
);
480 pim_upstream_inherited_olist(up
);
481 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
487 oil
= pim_channel_oil_add(&sg
, pim_ifp
->mroute_vif_index
);
489 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
490 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
491 up
= pim_upstream_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
492 __PRETTY_FUNCTION__
);
494 if (PIM_DEBUG_MROUTE
)
496 "%s: WRONGVIF%s unable to create upstream on interface",
497 pim_str_sg_dump(&sg
), ifp
->name
);
500 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
501 pim_upstream_keep_alive_timer_start(up
, qpim_keep_alive_time
);
502 up
->channel_oil
= oil
;
503 up
->channel_oil
->cc
.pktcnt
++;
504 pim_register_join(up
);
505 pim_upstream_inherited_olist(up
);
507 // Send the packet to the RP
508 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
514 int pim_mroute_msg(int fd
, const char *buf
, int buf_size
)
516 struct interface
*ifp
;
517 struct pim_interface
*pim_ifp
;
518 const struct ip
*ip_hdr
;
519 const struct igmpmsg
*msg
;
520 char ip_src_str
[INET_ADDRSTRLEN
] = "";
521 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
522 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
523 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
524 struct in_addr ifaddr
;
525 struct igmp_sock
*igmp
;
527 ip_hdr
= (const struct ip
*)buf
;
529 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
531 /* We have the IP packet but we do not know which interface this
533 * received on. Find the interface that is on the same subnet as
537 ifp
= pim_if_lookup_address_vrf(ip_hdr
->ip_src
, pimg
->vrf_id
);
540 if (PIM_DEBUG_MROUTE_DETAIL
) {
541 pim_inet4_dump("<src?>", ip_hdr
->ip_src
,
542 ip_src_str
, sizeof(ip_src_str
));
543 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
,
544 ip_dst_str
, sizeof(ip_dst_str
));
547 "%s: igmp kernel upcall could not find usable interface for %s -> %s",
548 __PRETTY_FUNCTION__
, ip_src_str
,
554 ifaddr
= pim_find_primary_addr(ifp
);
555 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
558 if (PIM_DEBUG_MROUTE
) {
559 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
561 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
565 "%s: igmp kernel upcall on %s(%p) for %s -> %s",
566 __PRETTY_FUNCTION__
, ifp
->name
, igmp
,
567 ip_src_str
, ip_dst_str
);
570 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
572 } else if (ip_hdr
->ip_p
) {
573 if (PIM_DEBUG_MROUTE_DETAIL
) {
574 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
576 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
579 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
580 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
585 msg
= (const struct igmpmsg
*)buf
;
587 ifp
= pim_if_find_by_vif_index(msg
->im_vif
);
591 if (PIM_DEBUG_MROUTE
) {
592 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
594 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
597 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
599 igmpmsgtype2str
[msg
->im_msgtype
],
600 msg
->im_msgtype
, ip_hdr
->ip_p
, fd
, src_str
,
601 grp_str
, ifp
->name
, msg
->im_vif
, buf_size
);
604 switch (msg
->im_msgtype
) {
605 case IGMPMSG_WRONGVIF
:
606 return pim_mroute_msg_wrongvif(fd
, ifp
, msg
);
608 case IGMPMSG_NOCACHE
:
609 return pim_mroute_msg_nocache(fd
, ifp
, msg
);
611 case IGMPMSG_WHOLEPKT
:
612 return pim_mroute_msg_wholepkt(fd
, ifp
,
615 case IGMPMSG_WRVIFWHOLE
:
616 return pim_mroute_msg_wrvifwhole(fd
, ifp
,
627 static int mroute_read(struct thread
*t
)
629 static long long count
;
639 rd
= read(fd
, buf
, sizeof(buf
));
643 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
646 if (PIM_DEBUG_MROUTE
)
648 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
649 __PRETTY_FUNCTION__
, rd
, fd
, errno
,
650 safe_strerror(errno
));
654 result
= pim_mroute_msg(fd
, buf
, rd
);
657 if (count
% qpim_packet_process
== 0)
667 static void mroute_read_on()
669 thread_add_read(master
, mroute_read
, 0, pimg
->mroute_socket
,
670 &qpim_mroute_socket_reader
);
673 static void mroute_read_off()
675 THREAD_OFF(qpim_mroute_socket_reader
);
678 int pim_mroute_socket_enable(struct pim_instance
*pim
)
682 if (pimd_privs
.change(ZPRIVS_RAISE
))
683 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
684 safe_strerror(errno
));
686 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
688 if (pimd_privs
.change(ZPRIVS_LOWER
))
689 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
690 safe_strerror(errno
));
693 zlog_warn("Could not create mroute socket: errno=%d: %s", errno
,
694 safe_strerror(errno
));
698 pim
->mroute_socket
= fd
;
699 if (pim_mroute_set(pim
, 1)) {
701 "Could not enable mroute on socket fd=%d: errno=%d: %s",
702 fd
, errno
, safe_strerror(errno
));
704 pim
->mroute_socket
= -1;
708 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
715 int pim_mroute_socket_disable(struct pim_instance
*pim
)
717 if (pim_mroute_set(pim
, 0)) {
719 "Could not disable mroute on socket fd=%d: errno=%d: %s",
720 pimg
->mroute_socket
, errno
, safe_strerror(errno
));
724 if (close(pim
->mroute_socket
)) {
725 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
726 pimg
->mroute_socket
, errno
, safe_strerror(errno
));
731 pim
->mroute_socket
= -1;
737 For each network interface (e.g., physical or a virtual tunnel) that
738 would be used for multicast forwarding, a corresponding multicast
739 interface must be added to the kernel.
741 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
744 struct pim_interface
*pim_ifp
= ifp
->info
;
748 memset(&vc
, 0, sizeof(vc
));
749 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
750 #ifdef VIFF_USE_IFINDEX
751 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
753 if (ifaddr
.s_addr
== INADDR_ANY
) {
755 "%s: unnumbered interfaces are not supported on this platform",
756 __PRETTY_FUNCTION__
);
759 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
761 vc
.vifc_flags
= flags
;
762 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
763 vc
.vifc_rate_limit
= 0;
765 #ifdef PIM_DVMRP_TUNNEL
766 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
767 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
768 sizeof(vc
.vifc_rmt_addr
));
772 err
= setsockopt(pimg
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
773 (void *)&vc
, sizeof(vc
));
775 char ifaddr_str
[INET_ADDRSTRLEN
];
777 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
781 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
782 __FILE__
, __PRETTY_FUNCTION__
, pimg
->mroute_socket
,
783 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
784 safe_strerror(errno
));
791 int pim_mroute_del_vif(int vif_index
)
796 if (PIM_DEBUG_MROUTE
) {
797 struct interface
*ifp
= pim_if_find_by_vif_index(vif_index
);
798 zlog_debug("%s %s: Del Vif %d (%s) ", __FILE__
,
799 __PRETTY_FUNCTION__
, vif_index
,
800 ifp
? ifp
->name
: "NULL");
803 memset(&vc
, 0, sizeof(vc
));
804 vc
.vifc_vifi
= vif_index
;
806 err
= setsockopt(pimg
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
807 (void *)&vc
, sizeof(vc
));
810 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
811 __FILE__
, __PRETTY_FUNCTION__
, pimg
->mroute_socket
,
812 vif_index
, errno
, safe_strerror(errno
));
819 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
823 int orig_iif_vif
= 0;
825 pimg
->mroute_add_last
= pim_time_monotonic_sec();
826 ++pimg
->mroute_add_events
;
828 /* Do not install route if incoming interface is undefined. */
829 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
830 if (PIM_DEBUG_MROUTE
) {
833 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
834 __PRETTY_FUNCTION__
, name
,
835 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
840 /* The linux kernel *expects* the incoming
841 * vif to be part of the outgoing list
842 * in the case of a (*,G).
844 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
845 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
846 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
850 * If we have an unresolved cache entry for the S,G
851 * it is owned by the pimreg for the incoming IIF
852 * So set pimreg as the IIF temporarily to cause
853 * the packets to be forwarded. Then set it
854 * to the correct IIF afterwords.
856 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
857 && c_oil
->oil
.mfcc_parent
!= 0) {
858 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
859 c_oil
->oil
.mfcc_parent
= 0;
861 err
= setsockopt(pimg
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
862 &c_oil
->oil
, sizeof(c_oil
->oil
));
864 if (!err
&& !c_oil
->installed
865 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
866 && orig_iif_vif
!= 0) {
867 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
868 err
= setsockopt(pimg
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
869 &c_oil
->oil
, sizeof(c_oil
->oil
));
872 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
873 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
877 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
878 __FILE__
, __PRETTY_FUNCTION__
, pimg
->mroute_socket
,
879 errno
, safe_strerror(errno
));
883 if (PIM_DEBUG_MROUTE
) {
885 zlog_debug("%s(%s), Added Route: %s", __PRETTY_FUNCTION__
, name
,
886 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
889 c_oil
->installed
= 1;
893 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
897 pimg
->mroute_del_last
= pim_time_monotonic_sec();
898 ++pimg
->mroute_del_events
;
900 if (!c_oil
->installed
) {
901 if (PIM_DEBUG_MROUTE
) {
904 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
905 __FILE__
, __PRETTY_FUNCTION__
,
906 c_oil
->oil
.mfcc_parent
,
907 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
912 err
= setsockopt(pimg
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
913 &c_oil
->oil
, sizeof(c_oil
->oil
));
915 if (PIM_DEBUG_MROUTE
)
917 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
918 __FILE__
, __PRETTY_FUNCTION__
,
919 pimg
->mroute_socket
, errno
,
920 safe_strerror(errno
));
924 if (PIM_DEBUG_MROUTE
) {
926 zlog_debug("%s(%s), Deleted Route: %s", __PRETTY_FUNCTION__
,
927 name
, pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
930 // Reset kernel installed flag
931 c_oil
->installed
= 0;
936 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
938 struct sioc_sg_req sgreq
;
940 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
941 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
942 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
944 if (!c_oil
->installed
) {
945 c_oil
->cc
.lastused
= 100 * qpim_keep_alive_time
;
946 if (PIM_DEBUG_MROUTE
) {
949 sg
.src
= c_oil
->oil
.mfcc_origin
;
950 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
951 if (PIM_DEBUG_MROUTE
)
953 "Channel(%s) is not installed no need to collect data from kernel",
954 pim_str_sg_dump(&sg
));
959 memset(&sgreq
, 0, sizeof(sgreq
));
960 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
961 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
963 pim_zlookup_sg_statistics(c_oil
);
964 if (ioctl(pimg
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
965 if (PIM_DEBUG_MROUTE
) {
968 sg
.src
= c_oil
->oil
.mfcc_origin
;
969 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
972 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
973 (unsigned long)SIOCGETSGCNT
,
974 pim_str_sg_dump(&sg
), errno
,
975 safe_strerror(errno
));
980 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
981 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
982 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;