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 opt_len
= sizeof(opt
);
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 opt
= 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
, opt
, errno
,
70 safe_strerror(errno
));
77 frr_elevate_privs(&pimd_privs
) {
78 opt
= enable
? MRT_INIT
: MRT_DONE
;
79 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
,
83 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
84 __FILE__
, __PRETTY_FUNCTION__
,
86 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
87 safe_strerror(errno
));
92 #if defined(HAVE_IP_PKTINFO)
94 /* Linux and Solaris IP_PKTINFO */
96 if (setsockopt(pim
->mroute_socket
, IPPROTO_IP
, IP_PKTINFO
, &opt
,
99 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
100 pim
->mroute_socket
, errno
,
101 safe_strerror(errno
));
106 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
108 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
110 zlog_warn("Could not get flags on socket fd:%d %d %s",
111 pim
->mroute_socket
, errno
, safe_strerror(errno
));
112 close(pim
->mroute_socket
);
115 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
116 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
117 pim
->mroute_socket
, errno
, safe_strerror(errno
));
118 close(pim
->mroute_socket
);
124 int upcalls
= IGMPMSG_WRVIFWHOLE
;
127 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
131 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
132 errno
, safe_strerror(errno
));
137 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
144 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
145 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
147 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
148 const struct igmpmsg
*msg
)
150 struct pim_interface
*pim_ifp
= ifp
->info
;
151 struct pim_upstream
*up
;
155 rpg
= pim_ifp
? RP(pim_ifp
->pim
, msg
->im_dst
) : NULL
;
157 * If the incoming interface is unknown OR
158 * the Interface type is SSM we don't need to
161 if (!rpg
|| pim_rpf_addr_is_inaddr_none(rpg
)) {
162 if (PIM_DEBUG_MROUTE_DETAIL
)
164 "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP",
165 __PRETTY_FUNCTION__
);
171 * If we've received a multicast packet that isn't connected to
174 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
175 if (PIM_DEBUG_MROUTE_DETAIL
)
177 "%s: Received incoming packet that doesn't originate on our seg",
178 __PRETTY_FUNCTION__
);
182 memset(&sg
, 0, sizeof(struct prefix_sg
));
183 sg
.src
= msg
->im_src
;
184 sg
.grp
= msg
->im_dst
;
186 if (!(PIM_I_am_DR(pim_ifp
))) {
187 struct channel_oil
*c_oil
;
189 if (PIM_DEBUG_MROUTE_DETAIL
)
190 zlog_debug("%s: Interface is not the DR blackholing incoming traffic for %s",
191 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
194 * We are not the DR, but we are still receiving packets
195 * Let's blackhole those packets for the moment
196 * As that they will be coming up to the cpu
197 * and causing us to consider them.
199 c_oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
,
200 pim_ifp
->mroute_vif_index
);
201 pim_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
206 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
207 __PRETTY_FUNCTION__
);
209 if (PIM_DEBUG_MROUTE
) {
211 "%s: Failure to add upstream information for %s",
212 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
218 * I moved this debug till after the actual add because
219 * I want to take advantage of the up->sg_str being filled in.
221 if (PIM_DEBUG_MROUTE
) {
222 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
223 __PRETTY_FUNCTION__
, up
->sg_str
);
226 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
227 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
229 up
->channel_oil
->cc
.pktcnt
++;
230 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
231 // resolve mfcc_parent prior to mroute_add in channel_add_oif
232 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
234 vif_index
= pim_if_find_vifindex_by_ifindex(
236 up
->rpf
.source_nexthop
.interface
->ifindex
);
237 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
239 pim_register_join(up
);
244 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
247 struct pim_interface
*pim_ifp
;
250 const struct ip
*ip_hdr
;
251 struct pim_upstream
*up
;
255 ip_hdr
= (const struct ip
*)buf
;
257 memset(&sg
, 0, sizeof(struct prefix_sg
));
258 sg
.src
= ip_hdr
->ip_src
;
259 sg
.grp
= ip_hdr
->ip_dst
;
261 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
263 struct prefix_sg star
= sg
;
264 star
.src
.s_addr
= INADDR_ANY
;
266 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
268 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
269 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
270 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
271 __PRETTY_FUNCTION__
, NULL
);
273 if (PIM_DEBUG_MROUTE
)
275 "%s: Unable to create upstream information for %s",
277 pim_str_sg_dump(&sg
));
280 pim_upstream_keep_alive_timer_start(
281 up
, pim_ifp
->pim
->keep_alive_time
);
282 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
283 pim_upstream_switch(pim_ifp
->pim
, up
,
284 PIM_UPSTREAM_JOINED
);
286 if (PIM_DEBUG_MROUTE
)
287 zlog_debug("%s: Creating %s upstream on LHR",
288 __PRETTY_FUNCTION__
, up
->sg_str
);
291 if (PIM_DEBUG_MROUTE_DETAIL
) {
293 "%s: Unable to find upstream channel WHOLEPKT%s",
294 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
299 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
301 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
303 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
304 || (!(PIM_I_am_DR(pim_ifp
)))) {
305 if (PIM_DEBUG_MROUTE
) {
306 zlog_debug("%s: Failed Check send packet",
307 __PRETTY_FUNCTION__
);
313 * If we've received a register suppress
315 if (!up
->t_rs_timer
) {
316 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
317 if (PIM_DEBUG_PIM_REG
)
319 "%s register forward skipped as group is SSM",
320 pim_str_sg_dump(&sg
));
323 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
324 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
325 pim_ifp
->primary_address
, rpg
, 0, up
);
330 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
331 const struct igmpmsg
*msg
)
333 struct pim_ifchannel
*ch
;
334 struct pim_interface
*pim_ifp
;
337 memset(&sg
, 0, sizeof(struct prefix_sg
));
338 sg
.src
= msg
->im_src
;
339 sg
.grp
= msg
->im_dst
;
342 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
344 RFC 4601 4.8.2. PIM-SSM-Only Routers
346 iif is the incoming interface of the packet.
347 if (iif is in inherited_olist(S,G)) {
348 send Assert(S,G) on iif
353 if (PIM_DEBUG_MROUTE
)
355 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
356 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
363 if (PIM_DEBUG_MROUTE
)
365 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
366 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
371 ch
= pim_ifchannel_find(ifp
, &sg
);
373 struct prefix_sg star_g
= sg
;
374 if (PIM_DEBUG_MROUTE
)
376 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
377 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
380 star_g
.src
.s_addr
= INADDR_ANY
;
381 ch
= pim_ifchannel_find(ifp
, &star_g
);
383 if (PIM_DEBUG_MROUTE
)
385 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
387 pim_str_sg_dump(&star_g
), ifp
->name
);
393 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
395 Transitions from NoInfo State
397 An (S,G) data packet arrives on interface I, AND
398 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
399 downstream interface that is in our (S,G) outgoing interface
400 list. We optimistically assume that we will be the assert
401 winner for this (S,G), and so we transition to the "I am Assert
402 Winner" state and perform Actions A1 (below), which will
403 initiate the assert negotiation for (S,G).
406 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
407 if (PIM_DEBUG_MROUTE
) {
409 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
410 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
415 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
416 if (PIM_DEBUG_MROUTE
) {
418 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
419 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
424 if (assert_action_a1(ch
)) {
425 if (PIM_DEBUG_MROUTE
) {
427 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
428 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
436 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
439 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
440 struct pim_interface
*pim_ifp
;
441 struct pim_ifchannel
*ch
;
442 struct pim_upstream
*up
;
443 struct prefix_sg star_g
;
445 struct channel_oil
*oil
;
449 memset(&sg
, 0, sizeof(struct prefix_sg
));
450 sg
.src
= ip_hdr
->ip_src
;
451 sg
.grp
= ip_hdr
->ip_dst
;
453 ch
= pim_ifchannel_find(ifp
, &sg
);
455 if (PIM_DEBUG_MROUTE
)
457 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
458 ch
->sg_str
, ifp
->name
);
463 star_g
.src
.s_addr
= INADDR_ANY
;
465 ch
= pim_ifchannel_find(ifp
, &star_g
);
468 if (PIM_DEBUG_MROUTE
)
469 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
470 pim_str_sg_dump (&star_g
), ifp
->name
);
475 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
477 struct pim_upstream
*parent
;
478 struct pim_nexthop source
;
479 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
480 if (!rpf
|| !rpf
->source_nexthop
.interface
)
484 * If we have received a WRVIFWHOLE and are at this
485 * point, we could be receiving the packet on the *,G
486 * tree, let's check and if so we can safely drop
489 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
490 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
493 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
495 memset(&source
, 0, sizeof(source
));
497 * If we are the fhr that means we are getting a callback during
498 * the pimreg period, so I believe we can ignore this packet
500 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
501 // No if channel, but upstream we are at the RP.
502 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
503 up
->upstream_register
, 0)
505 pim_register_stop_send(source
.interface
, &sg
,
506 pim_ifp
->primary_address
,
507 up
->upstream_register
);
508 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
510 if (!up
->channel_oil
)
511 up
->channel_oil
= pim_channel_oil_add(
513 pim_ifp
->mroute_vif_index
);
514 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
515 if (!up
->channel_oil
->installed
)
516 pim_mroute_add(up
->channel_oil
,
517 __PRETTY_FUNCTION__
);
519 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
520 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
521 up
->upstream_register
, 0)
523 pim_register_stop_send(
524 source
.interface
, &sg
,
525 pim_ifp
->primary_address
,
526 up
->upstream_register
);
527 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
529 pim_upstream_keep_alive_timer_start(
530 up
, pim_ifp
->pim
->keep_alive_time
);
531 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
532 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
538 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
540 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
541 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
542 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
543 PIM_UPSTREAM_FLAG_MASK_FHR
,
544 __PRETTY_FUNCTION__
, NULL
);
546 if (PIM_DEBUG_MROUTE
)
548 "%s: WRONGVIF%s unable to create upstream on interface",
549 pim_str_sg_dump(&sg
), ifp
->name
);
552 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
553 pim_upstream_keep_alive_timer_start(
554 up
, pim_ifp
->pim
->keep_alive_time
);
555 up
->channel_oil
= oil
;
556 up
->channel_oil
->cc
.pktcnt
++;
557 pim_register_join(up
);
558 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
560 // Send the packet to the RP
561 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
567 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
568 int buf_size
, ifindex_t ifindex
)
570 struct interface
*ifp
;
571 struct pim_interface
*pim_ifp
;
572 const struct ip
*ip_hdr
;
573 const struct igmpmsg
*msg
;
574 char ip_src_str
[INET_ADDRSTRLEN
] = "";
575 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
576 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
577 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
578 struct in_addr ifaddr
;
579 struct igmp_sock
*igmp
;
581 ip_hdr
= (const struct ip
*)buf
;
583 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
585 /* We have the IP packet but we do not know which interface this
587 * received on. Find the interface that is on the same subnet as
591 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
593 if (!ifp
|| !ifp
->info
)
597 ifaddr
= pim_find_primary_addr(ifp
);
598 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
601 if (PIM_DEBUG_MROUTE
) {
602 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
604 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
608 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
609 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
610 igmp
, ip_src_str
, ip_dst_str
);
613 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
615 } else if (ip_hdr
->ip_p
) {
616 if (PIM_DEBUG_MROUTE_DETAIL
) {
617 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
619 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
622 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
623 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
628 msg
= (const struct igmpmsg
*)buf
;
630 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
634 if (PIM_DEBUG_MROUTE
) {
635 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
637 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
640 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
642 igmpmsgtype2str
[msg
->im_msgtype
],
643 msg
->im_msgtype
, ip_hdr
->ip_p
,
644 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
645 msg
->im_vif
, buf_size
);
648 switch (msg
->im_msgtype
) {
649 case IGMPMSG_WRONGVIF
:
650 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
653 case IGMPMSG_NOCACHE
:
654 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
657 case IGMPMSG_WHOLEPKT
:
658 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
661 case IGMPMSG_WRVIFWHOLE
:
662 return pim_mroute_msg_wrvifwhole(
663 pim
->mroute_socket
, ifp
, (const char *)msg
);
673 static int mroute_read(struct thread
*t
)
675 struct pim_instance
*pim
;
676 static long long count
;
685 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
686 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
691 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
694 if (PIM_DEBUG_MROUTE
)
696 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
697 __PRETTY_FUNCTION__
, rd
,
698 pim
->mroute_socket
, errno
,
699 safe_strerror(errno
));
703 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
706 if (count
% qpim_packet_process
== 0)
716 static void mroute_read_on(struct pim_instance
*pim
)
718 thread_add_read(master
, mroute_read
, pim
, pim
->mroute_socket
,
722 static void mroute_read_off(struct pim_instance
*pim
)
724 THREAD_OFF(pim
->thread
);
727 int pim_mroute_socket_enable(struct pim_instance
*pim
)
731 frr_elevate_privs(&pimd_privs
) {
733 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
736 zlog_warn("Could not create mroute socket: errno=%d: %s",
738 safe_strerror(errno
));
742 #ifdef SO_BINDTODEVICE
743 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
744 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
745 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
746 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
747 safe_strerror(errno
));
755 pim
->mroute_socket
= fd
;
756 if (pim_mroute_set(pim
, 1)) {
758 "Could not enable mroute on socket fd=%d: errno=%d: %s",
759 fd
, errno
, safe_strerror(errno
));
761 pim
->mroute_socket
= -1;
765 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
772 int pim_mroute_socket_disable(struct pim_instance
*pim
)
774 if (pim_mroute_set(pim
, 0)) {
776 "Could not disable mroute on socket fd=%d: errno=%d: %s",
777 pim
->mroute_socket
, errno
, safe_strerror(errno
));
781 if (close(pim
->mroute_socket
)) {
782 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
783 pim
->mroute_socket
, errno
, safe_strerror(errno
));
787 mroute_read_off(pim
);
788 pim
->mroute_socket
= -1;
794 For each network interface (e.g., physical or a virtual tunnel) that
795 would be used for multicast forwarding, a corresponding multicast
796 interface must be added to the kernel.
798 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
801 struct pim_interface
*pim_ifp
= ifp
->info
;
805 if (PIM_DEBUG_MROUTE
)
806 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
807 pim_ifp
->mroute_vif_index
, ifp
->name
,
808 pim_ifp
->pim
->vrf
->name
);
810 memset(&vc
, 0, sizeof(vc
));
811 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
812 #ifdef VIFF_USE_IFINDEX
813 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
815 if (ifaddr
.s_addr
== INADDR_ANY
) {
817 "%s: unnumbered interfaces are not supported on this platform",
818 __PRETTY_FUNCTION__
);
821 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
823 vc
.vifc_flags
= flags
;
824 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
825 vc
.vifc_rate_limit
= 0;
827 #ifdef PIM_DVMRP_TUNNEL
828 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
829 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
830 sizeof(vc
.vifc_rmt_addr
));
834 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
835 (void *)&vc
, sizeof(vc
));
837 char ifaddr_str
[INET_ADDRSTRLEN
];
839 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
843 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
844 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
845 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
846 safe_strerror(errno
));
853 int pim_mroute_del_vif(struct interface
*ifp
)
855 struct pim_interface
*pim_ifp
= ifp
->info
;
859 if (PIM_DEBUG_MROUTE
)
860 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
861 pim_ifp
->mroute_vif_index
, ifp
->name
,
862 pim_ifp
->pim
->vrf
->name
);
864 memset(&vc
, 0, sizeof(vc
));
865 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
867 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
868 (void *)&vc
, sizeof(vc
));
871 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
872 __FILE__
, __PRETTY_FUNCTION__
,
873 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
874 errno
, safe_strerror(errno
));
881 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
883 struct pim_instance
*pim
= c_oil
->pim
;
886 int orig_iif_vif
= 0;
888 pim
->mroute_add_last
= pim_time_monotonic_sec();
889 ++pim
->mroute_add_events
;
891 /* Do not install route if incoming interface is undefined. */
892 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
893 if (PIM_DEBUG_MROUTE
) {
896 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
897 __PRETTY_FUNCTION__
, name
,
898 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
903 /* The linux kernel *expects* the incoming
904 * vif to be part of the outgoing list
905 * in the case of a (*,G).
907 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
908 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
909 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
913 * If we have an unresolved cache entry for the S,G
914 * it is owned by the pimreg for the incoming IIF
915 * So set pimreg as the IIF temporarily to cause
916 * the packets to be forwarded. Then set it
917 * to the correct IIF afterwords.
919 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
920 && c_oil
->oil
.mfcc_parent
!= 0) {
921 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
922 c_oil
->oil
.mfcc_parent
= 0;
924 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
925 &c_oil
->oil
, sizeof(c_oil
->oil
));
927 if (!err
&& !c_oil
->installed
928 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
929 && orig_iif_vif
!= 0) {
930 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
931 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
932 &c_oil
->oil
, sizeof(c_oil
->oil
));
935 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
936 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
940 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
941 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
942 errno
, safe_strerror(errno
));
946 if (PIM_DEBUG_MROUTE
) {
948 zlog_debug("%s(%s), vrf %s Added Route: %s",
949 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
950 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
953 c_oil
->installed
= 1;
957 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
959 struct pim_instance
*pim
= c_oil
->pim
;
962 pim
->mroute_del_last
= pim_time_monotonic_sec();
963 ++pim
->mroute_del_events
;
965 if (!c_oil
->installed
) {
966 if (PIM_DEBUG_MROUTE
) {
969 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
970 __FILE__
, __PRETTY_FUNCTION__
,
971 c_oil
->oil
.mfcc_parent
,
972 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
977 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
978 &c_oil
->oil
, sizeof(c_oil
->oil
));
980 if (PIM_DEBUG_MROUTE
)
982 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
983 __FILE__
, __PRETTY_FUNCTION__
,
984 pim
->mroute_socket
, errno
,
985 safe_strerror(errno
));
989 if (PIM_DEBUG_MROUTE
) {
991 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
992 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
993 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
996 // Reset kernel installed flag
997 c_oil
->installed
= 0;
1002 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1004 struct pim_instance
*pim
= c_oil
->pim
;
1005 struct sioc_sg_req sgreq
;
1007 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1008 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1009 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1011 if (!c_oil
->installed
) {
1012 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1013 if (PIM_DEBUG_MROUTE
) {
1014 struct prefix_sg sg
;
1016 sg
.src
= c_oil
->oil
.mfcc_origin
;
1017 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1018 if (PIM_DEBUG_MROUTE
)
1020 "Channel(%s) is not installed no need to collect data from kernel",
1021 pim_str_sg_dump(&sg
));
1026 memset(&sgreq
, 0, sizeof(sgreq
));
1027 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1028 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1030 pim_zlookup_sg_statistics(c_oil
);
1031 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1032 if (PIM_DEBUG_MROUTE
) {
1033 struct prefix_sg sg
;
1035 sg
.src
= c_oil
->oil
.mfcc_origin
;
1036 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1039 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1040 (unsigned long)SIOCGETSGCNT
,
1041 pim_str_sg_dump(&sg
), errno
,
1042 safe_strerror(errno
));
1047 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1048 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1049 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;