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 opt
= enable
? MRT_INIT
: MRT_DONE
;
78 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &opt
, opt_len
);
81 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
82 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
83 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
84 safe_strerror(errno
));
88 #if defined(HAVE_IP_PKTINFO)
90 /* Linux and Solaris IP_PKTINFO */
92 if (setsockopt(pim
->mroute_socket
, IPPROTO_IP
, IP_PKTINFO
, &opt
,
95 "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
96 pim
->mroute_socket
, errno
,
97 safe_strerror(errno
));
102 setsockopt_so_recvbuf(pim
->mroute_socket
, 1024 * 1024 * 8);
104 flags
= fcntl(pim
->mroute_socket
, F_GETFL
, 0);
106 zlog_warn("Could not get flags on socket fd:%d %d %s",
107 pim
->mroute_socket
, errno
, safe_strerror(errno
));
108 close(pim
->mroute_socket
);
111 if (fcntl(pim
->mroute_socket
, F_SETFL
, flags
| O_NONBLOCK
)) {
112 zlog_warn("Could not set O_NONBLOCK on socket fd:%d %d %s",
113 pim
->mroute_socket
, errno
, safe_strerror(errno
));
114 close(pim
->mroute_socket
);
120 int upcalls
= IGMPMSG_WRVIFWHOLE
;
123 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &upcalls
,
127 "Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
128 errno
, safe_strerror(errno
));
133 "PIM-SM will not work properly on this platform, until the ability to receive the WRVIFWHOLE upcall");
140 static const char *igmpmsgtype2str
[IGMPMSG_WRVIFWHOLE
+ 1] = {
141 "<unknown_upcall?>", "NOCACHE", "WRONGVIF", "WHOLEPKT", "WRVIFWHOLE"};
143 static int pim_mroute_msg_nocache(int fd
, struct interface
*ifp
,
144 const struct igmpmsg
*msg
)
146 struct pim_interface
*pim_ifp
= ifp
->info
;
147 struct pim_upstream
*up
;
151 rpg
= pim_ifp
? RP(pim_ifp
->pim
, msg
->im_dst
) : NULL
;
153 * If the incoming interface is unknown OR
154 * the Interface type is SSM we don't need to
157 if (!rpg
|| (pim_rpf_addr_is_inaddr_none(rpg
))
158 || (!(PIM_I_am_DR(pim_ifp
)))) {
159 if (PIM_DEBUG_MROUTE_DETAIL
)
161 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
162 __PRETTY_FUNCTION__
);
167 * If we've received a multicast packet that isn't connected to
170 if (!pim_if_connected_to_source(ifp
, msg
->im_src
)) {
171 if (PIM_DEBUG_MROUTE_DETAIL
)
173 "%s: Received incoming packet that doesn't originate on our seg",
174 __PRETTY_FUNCTION__
);
178 memset(&sg
, 0, sizeof(struct prefix_sg
));
179 sg
.src
= msg
->im_src
;
180 sg
.grp
= msg
->im_dst
;
182 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
183 __PRETTY_FUNCTION__
);
185 if (PIM_DEBUG_MROUTE
) {
187 "%s: Failure to add upstream information for %s",
188 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
194 * I moved this debug till after the actual add because
195 * I want to take advantage of the up->sg_str being filled in.
197 if (PIM_DEBUG_MROUTE
) {
198 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
199 __PRETTY_FUNCTION__
, up
->sg_str
);
202 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
203 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
205 up
->channel_oil
->cc
.pktcnt
++;
206 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
207 // resolve mfcc_parent prior to mroute_add in channel_add_oif
208 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
210 vif_index
= pim_if_find_vifindex_by_ifindex(
212 up
->rpf
.source_nexthop
.interface
->ifindex
);
213 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
215 pim_register_join(up
);
220 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
223 struct pim_interface
*pim_ifp
;
226 const struct ip
*ip_hdr
;
227 struct pim_upstream
*up
;
231 ip_hdr
= (const struct ip
*)buf
;
233 memset(&sg
, 0, sizeof(struct prefix_sg
));
234 sg
.src
= ip_hdr
->ip_src
;
235 sg
.grp
= ip_hdr
->ip_dst
;
237 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
239 struct prefix_sg star
= sg
;
240 star
.src
.s_addr
= INADDR_ANY
;
242 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
244 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
245 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
246 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
247 __PRETTY_FUNCTION__
, NULL
);
249 if (PIM_DEBUG_MROUTE
)
251 "%s: Unable to create upstream information for %s",
253 pim_str_sg_dump(&sg
));
256 pim_upstream_keep_alive_timer_start(
257 up
, pim_ifp
->pim
->keep_alive_time
);
258 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
259 pim_upstream_switch(pim_ifp
->pim
, up
,
260 PIM_UPSTREAM_JOINED
);
262 if (PIM_DEBUG_MROUTE
)
263 zlog_debug("%s: Creating %s upstream on LHR",
264 __PRETTY_FUNCTION__
, up
->sg_str
);
267 if (PIM_DEBUG_MROUTE_DETAIL
) {
269 "%s: Unable to find upstream channel WHOLEPKT%s",
270 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
275 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
277 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
279 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
280 || (!(PIM_I_am_DR(pim_ifp
)))) {
281 if (PIM_DEBUG_MROUTE
) {
282 zlog_debug("%s: Failed Check send packet",
283 __PRETTY_FUNCTION__
);
289 * If we've received a register suppress
291 if (!up
->t_rs_timer
) {
292 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
293 if (PIM_DEBUG_PIM_REG
)
295 "%s register forward skipped as group is SSM",
296 pim_str_sg_dump(&sg
));
299 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
300 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
301 pim_ifp
->primary_address
, rpg
, 0, up
);
306 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
307 const struct igmpmsg
*msg
)
309 struct pim_ifchannel
*ch
;
310 struct pim_interface
*pim_ifp
;
313 memset(&sg
, 0, sizeof(struct prefix_sg
));
314 sg
.src
= msg
->im_src
;
315 sg
.grp
= msg
->im_dst
;
318 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
320 RFC 4601 4.8.2. PIM-SSM-Only Routers
322 iif is the incoming interface of the packet.
323 if (iif is in inherited_olist(S,G)) {
324 send Assert(S,G) on iif
329 if (PIM_DEBUG_MROUTE
)
331 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
332 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
339 if (PIM_DEBUG_MROUTE
)
341 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
342 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
347 ch
= pim_ifchannel_find(ifp
, &sg
);
349 struct prefix_sg star_g
= sg
;
350 if (PIM_DEBUG_MROUTE
)
352 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
353 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
356 star_g
.src
.s_addr
= INADDR_ANY
;
357 ch
= pim_ifchannel_find(ifp
, &star_g
);
359 if (PIM_DEBUG_MROUTE
)
361 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
363 pim_str_sg_dump(&star_g
), ifp
->name
);
369 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
371 Transitions from NoInfo State
373 An (S,G) data packet arrives on interface I, AND
374 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
375 downstream interface that is in our (S,G) outgoing interface
376 list. We optimistically assume that we will be the assert
377 winner for this (S,G), and so we transition to the "I am Assert
378 Winner" state and perform Actions A1 (below), which will
379 initiate the assert negotiation for (S,G).
382 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
383 if (PIM_DEBUG_MROUTE
) {
385 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
386 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
391 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
392 if (PIM_DEBUG_MROUTE
) {
394 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
395 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
400 if (assert_action_a1(ch
)) {
401 if (PIM_DEBUG_MROUTE
) {
403 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
404 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
412 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
415 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
416 struct pim_interface
*pim_ifp
;
417 struct pim_ifchannel
*ch
;
418 struct pim_upstream
*up
;
419 struct prefix_sg star_g
;
421 struct channel_oil
*oil
;
425 memset(&sg
, 0, sizeof(struct prefix_sg
));
426 sg
.src
= ip_hdr
->ip_src
;
427 sg
.grp
= ip_hdr
->ip_dst
;
429 ch
= pim_ifchannel_find(ifp
, &sg
);
431 if (PIM_DEBUG_MROUTE
)
433 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
434 ch
->sg_str
, ifp
->name
);
439 star_g
.src
.s_addr
= INADDR_ANY
;
441 ch
= pim_ifchannel_find(ifp
, &star_g
);
444 if (PIM_DEBUG_MROUTE
)
445 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
446 pim_str_sg_dump (&star_g
), ifp
->name
);
451 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
453 struct pim_upstream
*parent
;
454 struct pim_nexthop source
;
455 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
456 if (!rpf
|| !rpf
->source_nexthop
.interface
)
460 * If we have received a WRVIFWHOLE and are at this
461 * point, we could be receiving the packet on the *,G
462 * tree, let's check and if so we can safely drop
465 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
466 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
469 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
471 memset(&source
, 0, sizeof(source
));
473 * If we are the fhr that means we are getting a callback during
474 * the pimreg period, so I believe we can ignore this packet
476 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
477 // No if channel, but upstream we are at the RP.
478 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
479 up
->upstream_register
, 0)
481 pim_register_stop_send(source
.interface
, &sg
,
482 pim_ifp
->primary_address
,
483 up
->upstream_register
);
484 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
486 if (!up
->channel_oil
)
487 up
->channel_oil
= pim_channel_oil_add(
489 pim_ifp
->mroute_vif_index
);
490 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
491 if (!up
->channel_oil
->installed
)
492 pim_mroute_add(up
->channel_oil
,
493 __PRETTY_FUNCTION__
);
495 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
496 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
497 up
->upstream_register
, 0)
499 pim_register_stop_send(
500 source
.interface
, &sg
,
501 pim_ifp
->primary_address
,
502 up
->upstream_register
);
503 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
505 pim_upstream_keep_alive_timer_start(
506 up
, pim_ifp
->pim
->keep_alive_time
);
507 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
508 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
514 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
516 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
517 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
518 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
519 PIM_UPSTREAM_FLAG_MASK_FHR
,
520 __PRETTY_FUNCTION__
, NULL
);
522 if (PIM_DEBUG_MROUTE
)
524 "%s: WRONGVIF%s unable to create upstream on interface",
525 pim_str_sg_dump(&sg
), ifp
->name
);
528 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
529 pim_upstream_keep_alive_timer_start(
530 up
, pim_ifp
->pim
->keep_alive_time
);
531 up
->channel_oil
= oil
;
532 up
->channel_oil
->cc
.pktcnt
++;
533 pim_register_join(up
);
534 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
536 // Send the packet to the RP
537 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
543 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
544 int buf_size
, ifindex_t ifindex
)
546 struct interface
*ifp
;
547 struct pim_interface
*pim_ifp
;
548 const struct ip
*ip_hdr
;
549 const struct igmpmsg
*msg
;
550 char ip_src_str
[INET_ADDRSTRLEN
] = "";
551 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
552 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
553 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
554 struct in_addr ifaddr
;
555 struct igmp_sock
*igmp
;
557 ip_hdr
= (const struct ip
*)buf
;
559 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
561 /* We have the IP packet but we do not know which interface this
563 * received on. Find the interface that is on the same subnet as
567 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
569 if (!ifp
|| !ifp
->info
)
573 ifaddr
= pim_find_primary_addr(ifp
);
574 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
577 if (PIM_DEBUG_MROUTE
) {
578 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
580 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
584 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
585 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
586 igmp
, ip_src_str
, ip_dst_str
);
589 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
591 } else if (ip_hdr
->ip_p
) {
592 if (PIM_DEBUG_MROUTE_DETAIL
) {
593 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
595 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
598 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
599 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
604 msg
= (const struct igmpmsg
*)buf
;
606 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
610 if (PIM_DEBUG_MROUTE
) {
611 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
613 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
616 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
618 igmpmsgtype2str
[msg
->im_msgtype
],
619 msg
->im_msgtype
, ip_hdr
->ip_p
,
620 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
621 msg
->im_vif
, buf_size
);
624 switch (msg
->im_msgtype
) {
625 case IGMPMSG_WRONGVIF
:
626 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
629 case IGMPMSG_NOCACHE
:
630 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
633 case IGMPMSG_WHOLEPKT
:
634 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
637 case IGMPMSG_WRVIFWHOLE
:
638 return pim_mroute_msg_wrvifwhole(
639 pim
->mroute_socket
, ifp
, (const char *)msg
);
649 static int mroute_read(struct thread
*t
)
651 struct pim_instance
*pim
;
652 static long long count
;
661 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
662 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
667 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
670 if (PIM_DEBUG_MROUTE
)
672 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
673 __PRETTY_FUNCTION__
, rd
,
674 pim
->mroute_socket
, errno
,
675 safe_strerror(errno
));
679 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
682 if (count
% qpim_packet_process
== 0)
692 static void mroute_read_on(struct pim_instance
*pim
)
694 thread_add_read(master
, mroute_read
, pim
, pim
->mroute_socket
,
698 static void mroute_read_off(struct pim_instance
*pim
)
700 THREAD_OFF(pim
->thread
);
703 int pim_mroute_socket_enable(struct pim_instance
*pim
)
707 frr_elevate_privs(&pimd_privs
) {
709 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
712 zlog_warn("Could not create mroute socket: errno=%d: %s",
714 safe_strerror(errno
));
718 #ifdef SO_BINDTODEVICE
719 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
720 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
721 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
722 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
723 safe_strerror(errno
));
731 pim
->mroute_socket
= fd
;
732 if (pim_mroute_set(pim
, 1)) {
734 "Could not enable mroute on socket fd=%d: errno=%d: %s",
735 fd
, errno
, safe_strerror(errno
));
737 pim
->mroute_socket
= -1;
741 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
748 int pim_mroute_socket_disable(struct pim_instance
*pim
)
750 if (pim_mroute_set(pim
, 0)) {
752 "Could not disable mroute on socket fd=%d: errno=%d: %s",
753 pim
->mroute_socket
, errno
, safe_strerror(errno
));
757 if (close(pim
->mroute_socket
)) {
758 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
759 pim
->mroute_socket
, errno
, safe_strerror(errno
));
763 mroute_read_off(pim
);
764 pim
->mroute_socket
= -1;
770 For each network interface (e.g., physical or a virtual tunnel) that
771 would be used for multicast forwarding, a corresponding multicast
772 interface must be added to the kernel.
774 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
777 struct pim_interface
*pim_ifp
= ifp
->info
;
781 if (PIM_DEBUG_MROUTE
)
782 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
783 pim_ifp
->mroute_vif_index
, ifp
->name
,
784 pim_ifp
->pim
->vrf
->name
);
786 memset(&vc
, 0, sizeof(vc
));
787 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
788 #ifdef VIFF_USE_IFINDEX
789 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
791 if (ifaddr
.s_addr
== INADDR_ANY
) {
793 "%s: unnumbered interfaces are not supported on this platform",
794 __PRETTY_FUNCTION__
);
797 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
799 vc
.vifc_flags
= flags
;
800 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
801 vc
.vifc_rate_limit
= 0;
803 #ifdef PIM_DVMRP_TUNNEL
804 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
805 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
806 sizeof(vc
.vifc_rmt_addr
));
810 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
811 (void *)&vc
, sizeof(vc
));
813 char ifaddr_str
[INET_ADDRSTRLEN
];
815 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
819 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
820 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
821 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
822 safe_strerror(errno
));
829 int pim_mroute_del_vif(struct interface
*ifp
)
831 struct pim_interface
*pim_ifp
= ifp
->info
;
835 if (PIM_DEBUG_MROUTE
)
836 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
837 pim_ifp
->mroute_vif_index
, ifp
->name
,
838 pim_ifp
->pim
->vrf
->name
);
840 memset(&vc
, 0, sizeof(vc
));
841 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
843 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
844 (void *)&vc
, sizeof(vc
));
847 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
848 __FILE__
, __PRETTY_FUNCTION__
,
849 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
850 errno
, safe_strerror(errno
));
857 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
859 struct pim_instance
*pim
= c_oil
->pim
;
862 int orig_iif_vif
= 0;
864 pim
->mroute_add_last
= pim_time_monotonic_sec();
865 ++pim
->mroute_add_events
;
867 /* Do not install route if incoming interface is undefined. */
868 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
869 if (PIM_DEBUG_MROUTE
) {
872 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
873 __PRETTY_FUNCTION__
, name
,
874 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
879 /* The linux kernel *expects* the incoming
880 * vif to be part of the outgoing list
881 * in the case of a (*,G).
883 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
884 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
885 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
889 * If we have an unresolved cache entry for the S,G
890 * it is owned by the pimreg for the incoming IIF
891 * So set pimreg as the IIF temporarily to cause
892 * the packets to be forwarded. Then set it
893 * to the correct IIF afterwords.
895 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
896 && c_oil
->oil
.mfcc_parent
!= 0) {
897 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
898 c_oil
->oil
.mfcc_parent
= 0;
900 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
901 &c_oil
->oil
, sizeof(c_oil
->oil
));
903 if (!err
&& !c_oil
->installed
904 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
905 && orig_iif_vif
!= 0) {
906 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
907 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
908 &c_oil
->oil
, sizeof(c_oil
->oil
));
911 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
912 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
916 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
917 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
918 errno
, safe_strerror(errno
));
922 if (PIM_DEBUG_MROUTE
) {
924 zlog_debug("%s(%s), vrf %s Added Route: %s",
925 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
926 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
929 c_oil
->installed
= 1;
933 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
935 struct pim_instance
*pim
= c_oil
->pim
;
938 pim
->mroute_del_last
= pim_time_monotonic_sec();
939 ++pim
->mroute_del_events
;
941 if (!c_oil
->installed
) {
942 if (PIM_DEBUG_MROUTE
) {
945 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
946 __FILE__
, __PRETTY_FUNCTION__
,
947 c_oil
->oil
.mfcc_parent
,
948 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
953 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
954 &c_oil
->oil
, sizeof(c_oil
->oil
));
956 if (PIM_DEBUG_MROUTE
)
958 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
959 __FILE__
, __PRETTY_FUNCTION__
,
960 pim
->mroute_socket
, errno
,
961 safe_strerror(errno
));
965 if (PIM_DEBUG_MROUTE
) {
967 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
968 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
969 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
972 // Reset kernel installed flag
973 c_oil
->installed
= 0;
978 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
980 struct pim_instance
*pim
= c_oil
->pim
;
981 struct sioc_sg_req sgreq
;
983 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
984 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
985 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
987 if (!c_oil
->installed
) {
988 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
989 if (PIM_DEBUG_MROUTE
) {
992 sg
.src
= c_oil
->oil
.mfcc_origin
;
993 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
994 if (PIM_DEBUG_MROUTE
)
996 "Channel(%s) is not installed no need to collect data from kernel",
997 pim_str_sg_dump(&sg
));
1002 memset(&sgreq
, 0, sizeof(sgreq
));
1003 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1004 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1006 pim_zlookup_sg_statistics(c_oil
);
1007 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1008 if (PIM_DEBUG_MROUTE
) {
1009 struct prefix_sg sg
;
1011 sg
.src
= c_oil
->oil
.mfcc_origin
;
1012 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1015 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1016 (unsigned long)SIOCGETSGCNT
,
1017 pim_str_sg_dump(&sg
), errno
,
1018 safe_strerror(errno
));
1023 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1024 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1025 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;