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
28 #include "lib_errors.h"
32 #include "pim_mroute.h"
36 #include "pim_iface.h"
37 #include "pim_macro.h"
40 #include "pim_register.h"
41 #include "pim_ifchannel.h"
42 #include "pim_zlookup.h"
46 static void mroute_read_on(struct pim_instance
*pim
);
48 static int pim_mroute_set(struct pim_instance
*pim
, int enable
)
52 socklen_t data_len
= sizeof(data
);
56 * We need to create the VRF table for the pim mroute_socket
58 if (pim
->vrf_id
!= VRF_DEFAULT
) {
59 frr_elevate_privs(&pimd_privs
) {
61 data
= pim
->vrf
->data
.l
.table_id
;
62 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
,
67 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
68 __FILE__
, __PRETTY_FUNCTION__
,
69 pim
->mroute_socket
, data
, errno
,
70 safe_strerror(errno
));
77 frr_elevate_privs(&pimd_privs
) {
78 opt
= enable
? MRT_INIT
: MRT_DONE
;
80 * *BSD *cares* about what value we pass down
84 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
,
85 opt
, &data
, data_len
);
88 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
89 __FILE__
, __PRETTY_FUNCTION__
,
91 enable
? "MRT_INIT" : "MRT_DONE", data
, errno
,
92 safe_strerror(errno
));
97 #if defined(HAVE_IP_PKTINFO)
99 /* Linux and Solaris IP_PKTINFO */
101 if (setsockopt(pim
->mroute_socket
, IPPROTO_IP
, IP_PKTINFO
,
104 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
105 pim
->mroute_socket
, errno
,
106 safe_strerror(errno
));
111 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
113 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
115 zlog_warn("Could not get flags on socket fd:%d %d %s",
116 pim
->mroute_socket
, errno
, safe_strerror(errno
));
117 close(pim
->mroute_socket
);
120 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
121 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
122 pim
->mroute_socket
, errno
, safe_strerror(errno
));
123 close(pim
->mroute_socket
);
129 int upcalls
= IGMPMSG_WRVIFWHOLE
;
132 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
136 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
137 errno
, safe_strerror(errno
));
142 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
149 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
150 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
152 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
153 const struct igmpmsg
*msg
)
155 struct pim_interface
*pim_ifp
= ifp
->info
;
156 struct pim_upstream
*up
;
160 rpg
= pim_ifp
? RP(pim_ifp
->pim
, msg
->im_dst
) : NULL
;
162 * If the incoming interface is unknown OR
163 * the Interface type is SSM we don't need to
166 if (!rpg
|| pim_rpf_addr_is_inaddr_none(rpg
)) {
167 if (PIM_DEBUG_MROUTE_DETAIL
)
169 "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP",
170 __PRETTY_FUNCTION__
);
176 * If we've received a multicast packet that isn't connected to
179 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
180 if (PIM_DEBUG_MROUTE_DETAIL
)
182 "%s: Received incoming packet that doesn't originate on our seg",
183 __PRETTY_FUNCTION__
);
187 memset(&sg
, 0, sizeof(struct prefix_sg
));
188 sg
.src
= msg
->im_src
;
189 sg
.grp
= msg
->im_dst
;
191 if (!(PIM_I_am_DR(pim_ifp
))) {
192 struct channel_oil
*c_oil
;
194 if (PIM_DEBUG_MROUTE_DETAIL
)
195 zlog_debug("%s: Interface is not the DR blackholing incoming traffic for %s",
196 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
199 * We are not the DR, but we are still receiving packets
200 * Let's blackhole those packets for the moment
201 * As that they will be coming up to the cpu
202 * and causing us to consider them.
204 c_oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
,
205 pim_ifp
->mroute_vif_index
);
206 pim_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
211 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
212 __PRETTY_FUNCTION__
);
214 if (PIM_DEBUG_MROUTE
) {
216 "%s: Failure to add upstream information for %s",
217 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
223 * I moved this debug till after the actual add because
224 * I want to take advantage of the up->sg_str being filled in.
226 if (PIM_DEBUG_MROUTE
) {
227 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
228 __PRETTY_FUNCTION__
, up
->sg_str
);
231 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
232 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
234 up
->channel_oil
->cc
.pktcnt
++;
235 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
236 // resolve mfcc_parent prior to mroute_add in channel_add_oif
237 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
239 vif_index
= pim_if_find_vifindex_by_ifindex(
241 up
->rpf
.source_nexthop
.interface
->ifindex
);
242 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
244 pim_register_join(up
);
249 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
252 struct pim_interface
*pim_ifp
;
255 const struct ip
*ip_hdr
;
256 struct pim_upstream
*up
;
260 ip_hdr
= (const struct ip
*)buf
;
262 memset(&sg
, 0, sizeof(struct prefix_sg
));
263 sg
.src
= ip_hdr
->ip_src
;
264 sg
.grp
= ip_hdr
->ip_dst
;
266 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
268 struct prefix_sg star
= sg
;
269 star
.src
.s_addr
= INADDR_ANY
;
271 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
273 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
274 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
275 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
276 __PRETTY_FUNCTION__
, NULL
);
278 if (PIM_DEBUG_MROUTE
)
280 "%s: Unable to create upstream information for %s",
282 pim_str_sg_dump(&sg
));
285 pim_upstream_keep_alive_timer_start(
286 up
, pim_ifp
->pim
->keep_alive_time
);
287 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
288 pim_upstream_switch(pim_ifp
->pim
, up
,
289 PIM_UPSTREAM_JOINED
);
291 if (PIM_DEBUG_MROUTE
)
292 zlog_debug("%s: Creating %s upstream on LHR",
293 __PRETTY_FUNCTION__
, up
->sg_str
);
296 if (PIM_DEBUG_MROUTE_DETAIL
) {
298 "%s: Unable to find upstream channel WHOLEPKT%s",
299 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
304 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
306 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
308 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
309 || (!(PIM_I_am_DR(pim_ifp
)))) {
310 if (PIM_DEBUG_MROUTE
) {
311 zlog_debug("%s: Failed Check send packet",
312 __PRETTY_FUNCTION__
);
318 * If we've received a register suppress
320 if (!up
->t_rs_timer
) {
321 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
322 if (PIM_DEBUG_PIM_REG
)
324 "%s register forward skipped as group is SSM",
325 pim_str_sg_dump(&sg
));
328 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
329 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
330 pim_ifp
->primary_address
, rpg
, 0, up
);
335 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
336 const struct igmpmsg
*msg
)
338 struct pim_ifchannel
*ch
;
339 struct pim_interface
*pim_ifp
;
342 memset(&sg
, 0, sizeof(struct prefix_sg
));
343 sg
.src
= msg
->im_src
;
344 sg
.grp
= msg
->im_dst
;
347 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
349 RFC 4601 4.8.2. PIM-SSM-Only Routers
351 iif is the incoming interface of the packet.
352 if (iif is in inherited_olist(S,G)) {
353 send Assert(S,G) on iif
358 if (PIM_DEBUG_MROUTE
)
360 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
361 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
368 if (PIM_DEBUG_MROUTE
)
370 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
371 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
376 ch
= pim_ifchannel_find(ifp
, &sg
);
378 struct prefix_sg star_g
= sg
;
379 if (PIM_DEBUG_MROUTE
)
381 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
382 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
385 star_g
.src
.s_addr
= INADDR_ANY
;
386 ch
= pim_ifchannel_find(ifp
, &star_g
);
388 if (PIM_DEBUG_MROUTE
)
390 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
392 pim_str_sg_dump(&star_g
), ifp
->name
);
398 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
400 Transitions from NoInfo State
402 An (S,G) data packet arrives on interface I, AND
403 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
404 downstream interface that is in our (S,G) outgoing interface
405 list. We optimistically assume that we will be the assert
406 winner for this (S,G), and so we transition to the "I am Assert
407 Winner" state and perform Actions A1 (below), which will
408 initiate the assert negotiation for (S,G).
411 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
412 if (PIM_DEBUG_MROUTE
) {
414 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
415 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
420 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
421 if (PIM_DEBUG_MROUTE
) {
423 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
424 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
429 if (assert_action_a1(ch
)) {
430 if (PIM_DEBUG_MROUTE
) {
432 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
433 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
441 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
444 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
445 struct pim_interface
*pim_ifp
;
446 struct pim_ifchannel
*ch
;
447 struct pim_upstream
*up
;
448 struct prefix_sg star_g
;
450 struct channel_oil
*oil
;
454 memset(&sg
, 0, sizeof(struct prefix_sg
));
455 sg
.src
= ip_hdr
->ip_src
;
456 sg
.grp
= ip_hdr
->ip_dst
;
458 ch
= pim_ifchannel_find(ifp
, &sg
);
460 if (PIM_DEBUG_MROUTE
)
462 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
463 ch
->sg_str
, ifp
->name
);
468 star_g
.src
.s_addr
= INADDR_ANY
;
470 ch
= pim_ifchannel_find(ifp
, &star_g
);
473 if (PIM_DEBUG_MROUTE
)
474 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
475 pim_str_sg_dump (&star_g
), ifp
->name
);
480 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
482 struct pim_upstream
*parent
;
483 struct pim_nexthop source
;
484 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
485 if (!rpf
|| !rpf
->source_nexthop
.interface
)
489 * If we have received a WRVIFWHOLE and are at this
490 * point, we could be receiving the packet on the *,G
491 * tree, let's check and if so we can safely drop
494 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
495 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
498 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
500 memset(&source
, 0, sizeof(source
));
502 * If we are the fhr that means we are getting a callback during
503 * the pimreg period, so I believe we can ignore this packet
505 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
506 // No if channel, but upstream we are at the RP.
507 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
508 up
->upstream_register
, 0)
510 pim_register_stop_send(source
.interface
, &sg
,
511 pim_ifp
->primary_address
,
512 up
->upstream_register
);
513 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
515 if (!up
->channel_oil
)
516 up
->channel_oil
= pim_channel_oil_add(
518 pim_ifp
->mroute_vif_index
);
519 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
520 if (!up
->channel_oil
->installed
)
521 pim_mroute_add(up
->channel_oil
,
522 __PRETTY_FUNCTION__
);
524 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
525 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
526 up
->upstream_register
, 0)
528 pim_register_stop_send(
529 source
.interface
, &sg
,
530 pim_ifp
->primary_address
,
531 up
->upstream_register
);
532 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
534 pim_upstream_keep_alive_timer_start(
535 up
, pim_ifp
->pim
->keep_alive_time
);
536 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
537 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
543 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
545 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
546 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
547 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
548 PIM_UPSTREAM_FLAG_MASK_FHR
,
549 __PRETTY_FUNCTION__
, NULL
);
551 if (PIM_DEBUG_MROUTE
)
553 "%s: WRONGVIF%s unable to create upstream on interface",
554 pim_str_sg_dump(&sg
), ifp
->name
);
557 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
558 pim_upstream_keep_alive_timer_start(
559 up
, pim_ifp
->pim
->keep_alive_time
);
560 up
->channel_oil
= oil
;
561 up
->channel_oil
->cc
.pktcnt
++;
562 pim_register_join(up
);
563 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
565 // Send the packet to the RP
566 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
572 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
573 int buf_size
, ifindex_t ifindex
)
575 struct interface
*ifp
;
576 struct pim_interface
*pim_ifp
;
577 const struct ip
*ip_hdr
;
578 const struct igmpmsg
*msg
;
579 char ip_src_str
[INET_ADDRSTRLEN
] = "";
580 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
581 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
582 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
583 struct in_addr ifaddr
;
584 struct igmp_sock
*igmp
;
586 ip_hdr
= (const struct ip
*)buf
;
588 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
590 /* We have the IP packet but we do not know which interface this
592 * received on. Find the interface that is on the same subnet as
596 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
598 if (!ifp
|| !ifp
->info
)
602 ifaddr
= pim_find_primary_addr(ifp
);
603 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
606 if (PIM_DEBUG_MROUTE
) {
607 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
609 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
613 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
614 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
615 igmp
, ip_src_str
, ip_dst_str
);
618 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
620 } else if (ip_hdr
->ip_p
) {
621 if (PIM_DEBUG_MROUTE_DETAIL
) {
622 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
624 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
627 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
628 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
633 msg
= (const struct igmpmsg
*)buf
;
635 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
639 if (PIM_DEBUG_MROUTE
) {
640 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
642 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
645 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
647 igmpmsgtype2str
[msg
->im_msgtype
],
648 msg
->im_msgtype
, ip_hdr
->ip_p
,
649 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
650 msg
->im_vif
, buf_size
);
653 switch (msg
->im_msgtype
) {
654 case IGMPMSG_WRONGVIF
:
655 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
658 case IGMPMSG_NOCACHE
:
659 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
662 case IGMPMSG_WHOLEPKT
:
663 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
666 case IGMPMSG_WRVIFWHOLE
:
667 return pim_mroute_msg_wrvifwhole(
668 pim
->mroute_socket
, ifp
, (const char *)msg
);
678 static int mroute_read(struct thread
*t
)
680 struct pim_instance
*pim
;
681 static long long count
;
690 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
691 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
696 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
699 if (PIM_DEBUG_MROUTE
)
701 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
702 __PRETTY_FUNCTION__
, rd
,
703 pim
->mroute_socket
, errno
,
704 safe_strerror(errno
));
708 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
711 if (count
% qpim_packet_process
== 0)
721 static void mroute_read_on(struct pim_instance
*pim
)
723 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
727 static void mroute_read_off(struct pim_instance
*pim
)
729 THREAD_OFF(pim
->thread
);
732 int pim_mroute_socket_enable(struct pim_instance
*pim
)
736 frr_elevate_privs(&pimd_privs
) {
738 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
741 zlog_warn("Could not create mroute socket: errno=%d: %s",
743 safe_strerror(errno
));
747 #ifdef SO_BINDTODEVICE
748 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
749 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
750 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
751 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
752 safe_strerror(errno
));
760 pim
->mroute_socket
= fd
;
761 if (pim_mroute_set(pim
, 1)) {
763 "Could not enable mroute on socket fd=%d: errno=%d: %s",
764 fd
, errno
, safe_strerror(errno
));
766 pim
->mroute_socket
= -1;
770 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
777 int pim_mroute_socket_disable(struct pim_instance
*pim
)
779 if (pim_mroute_set(pim
, 0)) {
781 "Could not disable mroute on socket fd=%d: errno=%d: %s",
782 pim
->mroute_socket
, errno
, safe_strerror(errno
));
786 if (close(pim
->mroute_socket
)) {
787 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
788 pim
->mroute_socket
, errno
, safe_strerror(errno
));
792 mroute_read_off(pim
);
793 pim
->mroute_socket
= -1;
799 For each network interface (e.g., physical or a virtual tunnel) that
800 would be used for multicast forwarding, a corresponding multicast
801 interface must be added to the kernel.
803 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
806 struct pim_interface
*pim_ifp
= ifp
->info
;
810 if (PIM_DEBUG_MROUTE
)
811 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
812 pim_ifp
->mroute_vif_index
, ifp
->name
,
813 pim_ifp
->pim
->vrf
->name
);
815 memset(&vc
, 0, sizeof(vc
));
816 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
817 #ifdef VIFF_USE_IFINDEX
818 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
820 if (ifaddr
.s_addr
== INADDR_ANY
) {
822 "%s: unnumbered interfaces are not supported on this platform",
823 __PRETTY_FUNCTION__
);
826 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
828 vc
.vifc_flags
= flags
;
829 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
830 vc
.vifc_rate_limit
= 0;
832 #ifdef PIM_DVMRP_TUNNEL
833 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
834 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
835 sizeof(vc
.vifc_rmt_addr
));
839 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
840 (void *)&vc
, sizeof(vc
));
842 char ifaddr_str
[INET_ADDRSTRLEN
];
844 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
848 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
849 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
850 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
851 safe_strerror(errno
));
858 int pim_mroute_del_vif(struct interface
*ifp
)
860 struct pim_interface
*pim_ifp
= ifp
->info
;
864 if (PIM_DEBUG_MROUTE
)
865 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
866 pim_ifp
->mroute_vif_index
, ifp
->name
,
867 pim_ifp
->pim
->vrf
->name
);
869 memset(&vc
, 0, sizeof(vc
));
870 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
872 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
873 (void *)&vc
, sizeof(vc
));
876 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
877 __FILE__
, __PRETTY_FUNCTION__
,
878 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
879 errno
, safe_strerror(errno
));
886 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
888 struct pim_instance
*pim
= c_oil
->pim
;
891 int orig_iif_vif
= 0;
893 pim
->mroute_add_last
= pim_time_monotonic_sec();
894 ++pim
->mroute_add_events
;
896 /* Do not install route if incoming interface is undefined. */
897 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
898 if (PIM_DEBUG_MROUTE
) {
901 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
902 __PRETTY_FUNCTION__
, name
,
903 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
908 /* The linux kernel *expects* the incoming
909 * vif to be part of the outgoing list
910 * in the case of a (*,G).
912 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
913 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
914 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
918 * If we have an unresolved cache entry for the S,G
919 * it is owned by the pimreg for the incoming IIF
920 * So set pimreg as the IIF temporarily to cause
921 * the packets to be forwarded. Then set it
922 * to the correct IIF afterwords.
924 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
925 && c_oil
->oil
.mfcc_parent
!= 0) {
926 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
927 c_oil
->oil
.mfcc_parent
= 0;
929 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
930 &c_oil
->oil
, sizeof(c_oil
->oil
));
932 if (!err
&& !c_oil
->installed
933 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
934 && orig_iif_vif
!= 0) {
935 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
936 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
937 &c_oil
->oil
, sizeof(c_oil
->oil
));
940 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
941 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
945 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
946 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
947 errno
, safe_strerror(errno
));
951 if (PIM_DEBUG_MROUTE
) {
953 zlog_debug("%s(%s), vrf %s Added Route: %s",
954 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
955 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
958 c_oil
->installed
= 1;
962 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
964 struct pim_instance
*pim
= c_oil
->pim
;
967 pim
->mroute_del_last
= pim_time_monotonic_sec();
968 ++pim
->mroute_del_events
;
970 if (!c_oil
->installed
) {
971 if (PIM_DEBUG_MROUTE
) {
974 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
975 __FILE__
, __PRETTY_FUNCTION__
,
976 c_oil
->oil
.mfcc_parent
,
977 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
982 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
983 &c_oil
->oil
, sizeof(c_oil
->oil
));
985 if (PIM_DEBUG_MROUTE
)
987 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
988 __FILE__
, __PRETTY_FUNCTION__
,
989 pim
->mroute_socket
, errno
,
990 safe_strerror(errno
));
994 if (PIM_DEBUG_MROUTE
) {
996 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
997 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
998 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1001 // Reset kernel installed flag
1002 c_oil
->installed
= 0;
1007 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1009 struct pim_instance
*pim
= c_oil
->pim
;
1010 struct sioc_sg_req sgreq
;
1012 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1013 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1014 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1016 if (!c_oil
->installed
) {
1017 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1018 if (PIM_DEBUG_MROUTE
) {
1019 struct prefix_sg sg
;
1021 sg
.src
= c_oil
->oil
.mfcc_origin
;
1022 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1023 if (PIM_DEBUG_MROUTE
)
1025 "Channel(%s) is not installed no need to collect data from kernel",
1026 pim_str_sg_dump(&sg
));
1031 memset(&sgreq
, 0, sizeof(sgreq
));
1032 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1033 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1035 pim_zlookup_sg_statistics(c_oil
);
1036 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1037 if (PIM_DEBUG_MROUTE
) {
1038 struct prefix_sg sg
;
1040 sg
.src
= c_oil
->oil
.mfcc_origin
;
1041 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1044 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1045 (unsigned long)SIOCGETSGCNT
,
1046 pim_str_sg_dump(&sg
), errno
,
1047 safe_strerror(errno
));
1052 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1053 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1054 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;