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
->rpf
.source_nexthop
.interface
&&
238 up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
240 vif_index
= pim_if_find_vifindex_by_ifindex(
242 up
->rpf
.source_nexthop
.interface
->ifindex
);
243 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
245 pim_register_join(up
);
250 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
253 struct pim_interface
*pim_ifp
;
256 const struct ip
*ip_hdr
;
257 struct pim_upstream
*up
;
261 ip_hdr
= (const struct ip
*)buf
;
263 memset(&sg
, 0, sizeof(struct prefix_sg
));
264 sg
.src
= ip_hdr
->ip_src
;
265 sg
.grp
= ip_hdr
->ip_dst
;
267 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
269 struct prefix_sg star
= sg
;
270 star
.src
.s_addr
= INADDR_ANY
;
272 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
274 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
275 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
276 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
277 __PRETTY_FUNCTION__
, NULL
);
279 if (PIM_DEBUG_MROUTE
)
281 "%s: Unable to create upstream information for %s",
283 pim_str_sg_dump(&sg
));
286 pim_upstream_keep_alive_timer_start(
287 up
, pim_ifp
->pim
->keep_alive_time
);
288 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
289 pim_upstream_switch(pim_ifp
->pim
, up
,
290 PIM_UPSTREAM_JOINED
);
292 if (PIM_DEBUG_MROUTE
)
293 zlog_debug("%s: Creating %s upstream on LHR",
294 __PRETTY_FUNCTION__
, up
->sg_str
);
297 if (PIM_DEBUG_MROUTE_DETAIL
) {
299 "%s: Unable to find upstream channel WHOLEPKT%s",
300 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
305 if (!up
->rpf
.source_nexthop
.interface
) {
307 zlog_debug("%s: up %s RPF is not present",
308 __PRETTY_FUNCTION__
, up
->sg_str
);
312 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
314 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
316 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
317 || (!(PIM_I_am_DR(pim_ifp
)))) {
318 if (PIM_DEBUG_MROUTE
) {
319 zlog_debug("%s: Failed Check send packet",
320 __PRETTY_FUNCTION__
);
326 * If we've received a register suppress
328 if (!up
->t_rs_timer
) {
329 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
330 if (PIM_DEBUG_PIM_REG
)
332 "%s register forward skipped as group is SSM",
333 pim_str_sg_dump(&sg
));
336 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
337 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
338 pim_ifp
->primary_address
, rpg
, 0, up
);
343 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
344 const struct igmpmsg
*msg
)
346 struct pim_ifchannel
*ch
;
347 struct pim_interface
*pim_ifp
;
350 memset(&sg
, 0, sizeof(struct prefix_sg
));
351 sg
.src
= msg
->im_src
;
352 sg
.grp
= msg
->im_dst
;
355 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
357 RFC 4601 4.8.2. PIM-SSM-Only Routers
359 iif is the incoming interface of the packet.
360 if (iif is in inherited_olist(S,G)) {
361 send Assert(S,G) on iif
366 if (PIM_DEBUG_MROUTE
)
368 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
369 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
376 if (PIM_DEBUG_MROUTE
)
378 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
379 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
384 ch
= pim_ifchannel_find(ifp
, &sg
);
386 struct prefix_sg star_g
= sg
;
387 if (PIM_DEBUG_MROUTE
)
389 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
390 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
393 star_g
.src
.s_addr
= INADDR_ANY
;
394 ch
= pim_ifchannel_find(ifp
, &star_g
);
396 if (PIM_DEBUG_MROUTE
)
398 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
400 pim_str_sg_dump(&star_g
), ifp
->name
);
406 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
408 Transitions from NoInfo State
410 An (S,G) data packet arrives on interface I, AND
411 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
412 downstream interface that is in our (S,G) outgoing interface
413 list. We optimistically assume that we will be the assert
414 winner for this (S,G), and so we transition to the "I am Assert
415 Winner" state and perform Actions A1 (below), which will
416 initiate the assert negotiation for (S,G).
419 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
420 if (PIM_DEBUG_MROUTE
) {
422 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
423 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
428 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
429 if (PIM_DEBUG_MROUTE
) {
431 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
432 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
437 if (assert_action_a1(ch
)) {
438 if (PIM_DEBUG_MROUTE
) {
440 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
441 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
449 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
452 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
453 struct pim_interface
*pim_ifp
;
454 struct pim_ifchannel
*ch
;
455 struct pim_upstream
*up
;
456 struct prefix_sg star_g
;
458 struct channel_oil
*oil
;
462 memset(&sg
, 0, sizeof(struct prefix_sg
));
463 sg
.src
= ip_hdr
->ip_src
;
464 sg
.grp
= ip_hdr
->ip_dst
;
466 ch
= pim_ifchannel_find(ifp
, &sg
);
468 if (PIM_DEBUG_MROUTE
)
470 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
471 ch
->sg_str
, ifp
->name
);
476 star_g
.src
.s_addr
= INADDR_ANY
;
478 ch
= pim_ifchannel_find(ifp
, &star_g
);
481 if (PIM_DEBUG_MROUTE
)
482 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
483 pim_str_sg_dump (&star_g
), ifp
->name
);
488 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
490 struct pim_upstream
*parent
;
491 struct pim_nexthop source
;
492 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
493 if (!rpf
|| !rpf
->source_nexthop
.interface
)
497 * If we have received a WRVIFWHOLE and are at this
498 * point, we could be receiving the packet on the *,G
499 * tree, let's check and if so we can safely drop
502 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
503 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
506 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
508 memset(&source
, 0, sizeof(source
));
510 * If we are the fhr that means we are getting a callback during
511 * the pimreg period, so I believe we can ignore this packet
513 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
514 // No if channel, but upstream we are at the RP.
515 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
516 up
->upstream_register
, 0)
518 pim_register_stop_send(source
.interface
, &sg
,
519 pim_ifp
->primary_address
,
520 up
->upstream_register
);
521 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
523 if (!up
->channel_oil
)
524 up
->channel_oil
= pim_channel_oil_add(
526 pim_ifp
->mroute_vif_index
);
527 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
528 if (!up
->channel_oil
->installed
)
529 pim_mroute_add(up
->channel_oil
,
530 __PRETTY_FUNCTION__
);
532 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
533 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
534 up
->upstream_register
, 0)
536 pim_register_stop_send(
537 source
.interface
, &sg
,
538 pim_ifp
->primary_address
,
539 up
->upstream_register
);
540 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
542 pim_upstream_keep_alive_timer_start(
543 up
, pim_ifp
->pim
->keep_alive_time
);
544 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
545 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
551 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
553 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
554 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
555 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
556 PIM_UPSTREAM_FLAG_MASK_FHR
,
557 __PRETTY_FUNCTION__
, NULL
);
559 if (PIM_DEBUG_MROUTE
)
561 "%s: WRONGVIF%s unable to create upstream on interface",
562 pim_str_sg_dump(&sg
), ifp
->name
);
565 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
566 pim_upstream_keep_alive_timer_start(
567 up
, pim_ifp
->pim
->keep_alive_time
);
568 up
->channel_oil
= oil
;
569 up
->channel_oil
->cc
.pktcnt
++;
570 pim_register_join(up
);
571 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
573 // Send the packet to the RP
574 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
580 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
581 int buf_size
, ifindex_t ifindex
)
583 struct interface
*ifp
;
584 struct pim_interface
*pim_ifp
;
585 const struct ip
*ip_hdr
;
586 const struct igmpmsg
*msg
;
587 char ip_src_str
[INET_ADDRSTRLEN
] = "";
588 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
589 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
590 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
591 struct in_addr ifaddr
;
592 struct igmp_sock
*igmp
;
594 ip_hdr
= (const struct ip
*)buf
;
596 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
598 /* We have the IP packet but we do not know which interface this
600 * received on. Find the interface that is on the same subnet as
604 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
606 if (!ifp
|| !ifp
->info
)
610 ifaddr
= pim_find_primary_addr(ifp
);
611 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
614 if (PIM_DEBUG_MROUTE
) {
615 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
617 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
621 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
622 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
623 igmp
, ip_src_str
, ip_dst_str
);
626 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
628 } else if (ip_hdr
->ip_p
) {
629 if (PIM_DEBUG_MROUTE_DETAIL
) {
630 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
632 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
635 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
636 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
641 msg
= (const struct igmpmsg
*)buf
;
643 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
647 if (PIM_DEBUG_MROUTE
) {
648 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
650 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
653 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
655 igmpmsgtype2str
[msg
->im_msgtype
],
656 msg
->im_msgtype
, ip_hdr
->ip_p
,
657 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
658 msg
->im_vif
, buf_size
);
661 switch (msg
->im_msgtype
) {
662 case IGMPMSG_WRONGVIF
:
663 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
666 case IGMPMSG_NOCACHE
:
667 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
670 case IGMPMSG_WHOLEPKT
:
671 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
674 case IGMPMSG_WRVIFWHOLE
:
675 return pim_mroute_msg_wrvifwhole(
676 pim
->mroute_socket
, ifp
, (const char *)msg
);
686 static int mroute_read(struct thread
*t
)
688 struct pim_instance
*pim
;
689 static long long count
;
698 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
699 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
704 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
707 if (PIM_DEBUG_MROUTE
)
709 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
710 __PRETTY_FUNCTION__
, rd
,
711 pim
->mroute_socket
, errno
,
712 safe_strerror(errno
));
716 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
719 if (count
% router
->packet_process
== 0)
729 static void mroute_read_on(struct pim_instance
*pim
)
731 thread_add_read(router
->master
, mroute_read
, pim
, pim
->mroute_socket
,
735 static void mroute_read_off(struct pim_instance
*pim
)
737 THREAD_OFF(pim
->thread
);
740 int pim_mroute_socket_enable(struct pim_instance
*pim
)
744 frr_elevate_privs(&pimd_privs
) {
746 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
749 zlog_warn("Could not create mroute socket: errno=%d: %s",
751 safe_strerror(errno
));
755 #ifdef SO_BINDTODEVICE
756 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
757 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
758 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
759 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
760 safe_strerror(errno
));
768 pim
->mroute_socket
= fd
;
769 if (pim_mroute_set(pim
, 1)) {
771 "Could not enable mroute on socket fd=%d: errno=%d: %s",
772 fd
, errno
, safe_strerror(errno
));
774 pim
->mroute_socket
= -1;
778 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
785 int pim_mroute_socket_disable(struct pim_instance
*pim
)
787 if (pim_mroute_set(pim
, 0)) {
789 "Could not disable mroute on socket fd=%d: errno=%d: %s",
790 pim
->mroute_socket
, errno
, safe_strerror(errno
));
794 if (close(pim
->mroute_socket
)) {
795 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
796 pim
->mroute_socket
, errno
, safe_strerror(errno
));
800 mroute_read_off(pim
);
801 pim
->mroute_socket
= -1;
807 For each network interface (e.g., physical or a virtual tunnel) that
808 would be used for multicast forwarding, a corresponding multicast
809 interface must be added to the kernel.
811 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
814 struct pim_interface
*pim_ifp
= ifp
->info
;
818 if (PIM_DEBUG_MROUTE
)
819 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
820 pim_ifp
->mroute_vif_index
, ifp
->name
,
821 pim_ifp
->pim
->vrf
->name
);
823 memset(&vc
, 0, sizeof(vc
));
824 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
825 #ifdef VIFF_USE_IFINDEX
826 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
828 if (ifaddr
.s_addr
== INADDR_ANY
) {
830 "%s: unnumbered interfaces are not supported on this platform",
831 __PRETTY_FUNCTION__
);
834 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
836 vc
.vifc_flags
= flags
;
837 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
838 vc
.vifc_rate_limit
= 0;
840 #ifdef PIM_DVMRP_TUNNEL
841 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
842 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
843 sizeof(vc
.vifc_rmt_addr
));
847 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
848 (void *)&vc
, sizeof(vc
));
850 char ifaddr_str
[INET_ADDRSTRLEN
];
852 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
856 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
857 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
858 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
859 safe_strerror(errno
));
866 int pim_mroute_del_vif(struct interface
*ifp
)
868 struct pim_interface
*pim_ifp
= ifp
->info
;
872 if (PIM_DEBUG_MROUTE
)
873 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
874 pim_ifp
->mroute_vif_index
, ifp
->name
,
875 pim_ifp
->pim
->vrf
->name
);
877 memset(&vc
, 0, sizeof(vc
));
878 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
880 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
881 (void *)&vc
, sizeof(vc
));
884 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
885 __FILE__
, __PRETTY_FUNCTION__
,
886 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
887 errno
, safe_strerror(errno
));
894 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
896 struct pim_instance
*pim
= c_oil
->pim
;
899 int orig_iif_vif
= 0;
901 pim
->mroute_add_last
= pim_time_monotonic_sec();
902 ++pim
->mroute_add_events
;
904 /* Do not install route if incoming interface is undefined. */
905 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
906 if (PIM_DEBUG_MROUTE
) {
909 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
910 __PRETTY_FUNCTION__
, name
,
911 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
916 /* The linux kernel *expects* the incoming
917 * vif to be part of the outgoing list
918 * in the case of a (*,G).
920 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
921 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
922 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
926 * If we have an unresolved cache entry for the S,G
927 * it is owned by the pimreg for the incoming IIF
928 * So set pimreg as the IIF temporarily to cause
929 * the packets to be forwarded. Then set it
930 * to the correct IIF afterwords.
932 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
933 && c_oil
->oil
.mfcc_parent
!= 0) {
934 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
935 c_oil
->oil
.mfcc_parent
= 0;
937 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
938 &c_oil
->oil
, sizeof(c_oil
->oil
));
940 if (!err
&& !c_oil
->installed
941 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
942 && orig_iif_vif
!= 0) {
943 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
944 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
945 &c_oil
->oil
, sizeof(c_oil
->oil
));
948 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
949 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
953 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
954 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
955 errno
, safe_strerror(errno
));
959 if (PIM_DEBUG_MROUTE
) {
961 zlog_debug("%s(%s), vrf %s Added Route: %s",
962 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
963 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
966 c_oil
->installed
= 1;
970 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
972 struct pim_instance
*pim
= c_oil
->pim
;
975 pim
->mroute_del_last
= pim_time_monotonic_sec();
976 ++pim
->mroute_del_events
;
978 if (!c_oil
->installed
) {
979 if (PIM_DEBUG_MROUTE
) {
982 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
983 __FILE__
, __PRETTY_FUNCTION__
,
984 c_oil
->oil
.mfcc_parent
,
985 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
990 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
991 &c_oil
->oil
, sizeof(c_oil
->oil
));
993 if (PIM_DEBUG_MROUTE
)
995 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
996 __FILE__
, __PRETTY_FUNCTION__
,
997 pim
->mroute_socket
, errno
,
998 safe_strerror(errno
));
1002 if (PIM_DEBUG_MROUTE
) {
1004 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
1005 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
1006 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
1009 // Reset kernel installed flag
1010 c_oil
->installed
= 0;
1015 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1017 struct pim_instance
*pim
= c_oil
->pim
;
1018 struct sioc_sg_req sgreq
;
1020 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1021 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1022 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1024 if (!c_oil
->installed
) {
1025 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1026 if (PIM_DEBUG_MROUTE
) {
1027 struct prefix_sg sg
;
1029 sg
.src
= c_oil
->oil
.mfcc_origin
;
1030 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1031 if (PIM_DEBUG_MROUTE
)
1033 "Channel(%s) is not installed no need to collect data from kernel",
1034 pim_str_sg_dump(&sg
));
1039 memset(&sgreq
, 0, sizeof(sgreq
));
1040 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1041 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1043 pim_zlookup_sg_statistics(c_oil
);
1044 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1045 if (PIM_DEBUG_MROUTE
) {
1046 struct prefix_sg sg
;
1048 sg
.src
= c_oil
->oil
.mfcc_origin
;
1049 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1052 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1053 (unsigned long)SIOCGETSGCNT
,
1054 pim_str_sg_dump(&sg
), errno
,
1055 safe_strerror(errno
));
1060 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1061 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1062 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;