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 if (PIM_DEBUG_MROUTE_DETAIL
)
160 "%s: Interface is not configured correctly to handle incoming packet: Could be !pim_ifp, !SM, !RP",
161 __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 if (!(PIM_I_am_DR(pim_ifp
))) {
183 struct channel_oil
*c_oil
;
185 if (PIM_DEBUG_MROUTE_DETAIL
)
186 zlog_debug("%s: Interface is not the DR blackholing incoming traffic for %s",
187 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
190 * We are not the DR, but we are still receiving packets
191 * Let's blackhole those packets for the moment
192 * As that they will be coming up to the cpu
193 * and causing us to consider them.
195 c_oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
,
196 pim_ifp
->mroute_vif_index
);
197 pim_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
202 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
203 __PRETTY_FUNCTION__
);
205 if (PIM_DEBUG_MROUTE
) {
207 "%s: Failure to add upstream information for %s",
208 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
214 * I moved this debug till after the actual add because
215 * I want to take advantage of the up->sg_str being filled in.
217 if (PIM_DEBUG_MROUTE
) {
218 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
219 __PRETTY_FUNCTION__
, up
->sg_str
);
222 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
223 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
225 up
->channel_oil
->cc
.pktcnt
++;
226 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
227 // resolve mfcc_parent prior to mroute_add in channel_add_oif
228 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
230 vif_index
= pim_if_find_vifindex_by_ifindex(
232 up
->rpf
.source_nexthop
.interface
->ifindex
);
233 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
235 pim_register_join(up
);
240 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
243 struct pim_interface
*pim_ifp
;
246 const struct ip
*ip_hdr
;
247 struct pim_upstream
*up
;
251 ip_hdr
= (const struct ip
*)buf
;
253 memset(&sg
, 0, sizeof(struct prefix_sg
));
254 sg
.src
= ip_hdr
->ip_src
;
255 sg
.grp
= ip_hdr
->ip_dst
;
257 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
259 struct prefix_sg star
= sg
;
260 star
.src
.s_addr
= INADDR_ANY
;
262 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
264 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
265 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
266 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
267 __PRETTY_FUNCTION__
, NULL
);
269 if (PIM_DEBUG_MROUTE
)
271 "%s: Unable to create upstream information for %s",
273 pim_str_sg_dump(&sg
));
276 pim_upstream_keep_alive_timer_start(
277 up
, pim_ifp
->pim
->keep_alive_time
);
278 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
279 pim_upstream_switch(pim_ifp
->pim
, up
,
280 PIM_UPSTREAM_JOINED
);
282 if (PIM_DEBUG_MROUTE
)
283 zlog_debug("%s: Creating %s upstream on LHR",
284 __PRETTY_FUNCTION__
, up
->sg_str
);
287 if (PIM_DEBUG_MROUTE_DETAIL
) {
289 "%s: Unable to find upstream channel WHOLEPKT%s",
290 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
295 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
297 rpg
= pim_ifp
? RP(pim_ifp
->pim
, sg
.grp
) : NULL
;
299 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
300 || (!(PIM_I_am_DR(pim_ifp
)))) {
301 if (PIM_DEBUG_MROUTE
) {
302 zlog_debug("%s: Failed Check send packet",
303 __PRETTY_FUNCTION__
);
309 * If we've received a register suppress
311 if (!up
->t_rs_timer
) {
312 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
313 if (PIM_DEBUG_PIM_REG
)
315 "%s register forward skipped as group is SSM",
316 pim_str_sg_dump(&sg
));
319 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
320 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
321 pim_ifp
->primary_address
, rpg
, 0, up
);
326 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
327 const struct igmpmsg
*msg
)
329 struct pim_ifchannel
*ch
;
330 struct pim_interface
*pim_ifp
;
333 memset(&sg
, 0, sizeof(struct prefix_sg
));
334 sg
.src
= msg
->im_src
;
335 sg
.grp
= msg
->im_dst
;
338 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
340 RFC 4601 4.8.2. PIM-SSM-Only Routers
342 iif is the incoming interface of the packet.
343 if (iif is in inherited_olist(S,G)) {
344 send Assert(S,G) on iif
349 if (PIM_DEBUG_MROUTE
)
351 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
352 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
359 if (PIM_DEBUG_MROUTE
)
361 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
362 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
367 ch
= pim_ifchannel_find(ifp
, &sg
);
369 struct prefix_sg star_g
= sg
;
370 if (PIM_DEBUG_MROUTE
)
372 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
373 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
376 star_g
.src
.s_addr
= INADDR_ANY
;
377 ch
= pim_ifchannel_find(ifp
, &star_g
);
379 if (PIM_DEBUG_MROUTE
)
381 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
383 pim_str_sg_dump(&star_g
), ifp
->name
);
389 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
391 Transitions from NoInfo State
393 An (S,G) data packet arrives on interface I, AND
394 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
395 downstream interface that is in our (S,G) outgoing interface
396 list. We optimistically assume that we will be the assert
397 winner for this (S,G), and so we transition to the "I am Assert
398 Winner" state and perform Actions A1 (below), which will
399 initiate the assert negotiation for (S,G).
402 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
403 if (PIM_DEBUG_MROUTE
) {
405 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
406 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
411 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
412 if (PIM_DEBUG_MROUTE
) {
414 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
415 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
420 if (assert_action_a1(ch
)) {
421 if (PIM_DEBUG_MROUTE
) {
423 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
424 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
432 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
435 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
436 struct pim_interface
*pim_ifp
;
437 struct pim_ifchannel
*ch
;
438 struct pim_upstream
*up
;
439 struct prefix_sg star_g
;
441 struct channel_oil
*oil
;
445 memset(&sg
, 0, sizeof(struct prefix_sg
));
446 sg
.src
= ip_hdr
->ip_src
;
447 sg
.grp
= ip_hdr
->ip_dst
;
449 ch
= pim_ifchannel_find(ifp
, &sg
);
451 if (PIM_DEBUG_MROUTE
)
453 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
454 ch
->sg_str
, ifp
->name
);
459 star_g
.src
.s_addr
= INADDR_ANY
;
461 ch
= pim_ifchannel_find(ifp
, &star_g
);
464 if (PIM_DEBUG_MROUTE
)
465 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
466 pim_str_sg_dump (&star_g
), ifp
->name
);
471 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
473 struct pim_upstream
*parent
;
474 struct pim_nexthop source
;
475 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
476 if (!rpf
|| !rpf
->source_nexthop
.interface
)
480 * If we have received a WRVIFWHOLE and are at this
481 * point, we could be receiving the packet on the *,G
482 * tree, let's check and if so we can safely drop
485 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
486 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
489 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
491 memset(&source
, 0, sizeof(source
));
493 * If we are the fhr that means we are getting a callback during
494 * the pimreg period, so I believe we can ignore this packet
496 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
497 // No if channel, but upstream we are at the RP.
498 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
499 up
->upstream_register
, 0)
501 pim_register_stop_send(source
.interface
, &sg
,
502 pim_ifp
->primary_address
,
503 up
->upstream_register
);
504 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
506 if (!up
->channel_oil
)
507 up
->channel_oil
= pim_channel_oil_add(
509 pim_ifp
->mroute_vif_index
);
510 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
511 if (!up
->channel_oil
->installed
)
512 pim_mroute_add(up
->channel_oil
,
513 __PRETTY_FUNCTION__
);
515 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
516 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
517 up
->upstream_register
, 0)
519 pim_register_stop_send(
520 source
.interface
, &sg
,
521 pim_ifp
->primary_address
,
522 up
->upstream_register
);
523 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
525 pim_upstream_keep_alive_timer_start(
526 up
, pim_ifp
->pim
->keep_alive_time
);
527 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
528 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
534 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
536 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
537 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
538 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
539 PIM_UPSTREAM_FLAG_MASK_FHR
,
540 __PRETTY_FUNCTION__
, NULL
);
542 if (PIM_DEBUG_MROUTE
)
544 "%s: WRONGVIF%s unable to create upstream on interface",
545 pim_str_sg_dump(&sg
), ifp
->name
);
548 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
549 pim_upstream_keep_alive_timer_start(
550 up
, pim_ifp
->pim
->keep_alive_time
);
551 up
->channel_oil
= oil
;
552 up
->channel_oil
->cc
.pktcnt
++;
553 pim_register_join(up
);
554 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
556 // Send the packet to the RP
557 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
563 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
564 int buf_size
, ifindex_t ifindex
)
566 struct interface
*ifp
;
567 struct pim_interface
*pim_ifp
;
568 const struct ip
*ip_hdr
;
569 const struct igmpmsg
*msg
;
570 char ip_src_str
[INET_ADDRSTRLEN
] = "";
571 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
572 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
573 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
574 struct in_addr ifaddr
;
575 struct igmp_sock
*igmp
;
577 ip_hdr
= (const struct ip
*)buf
;
579 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
581 /* We have the IP packet but we do not know which interface this
583 * received on. Find the interface that is on the same subnet as
587 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
589 if (!ifp
|| !ifp
->info
)
593 ifaddr
= pim_find_primary_addr(ifp
);
594 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
597 if (PIM_DEBUG_MROUTE
) {
598 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
600 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
604 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
605 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
606 igmp
, ip_src_str
, ip_dst_str
);
609 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
611 } else if (ip_hdr
->ip_p
) {
612 if (PIM_DEBUG_MROUTE_DETAIL
) {
613 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
615 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
618 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
619 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
624 msg
= (const struct igmpmsg
*)buf
;
626 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
630 if (PIM_DEBUG_MROUTE
) {
631 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
633 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
636 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
638 igmpmsgtype2str
[msg
->im_msgtype
],
639 msg
->im_msgtype
, ip_hdr
->ip_p
,
640 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
641 msg
->im_vif
, buf_size
);
644 switch (msg
->im_msgtype
) {
645 case IGMPMSG_WRONGVIF
:
646 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
649 case IGMPMSG_NOCACHE
:
650 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
653 case IGMPMSG_WHOLEPKT
:
654 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
657 case IGMPMSG_WRVIFWHOLE
:
658 return pim_mroute_msg_wrvifwhole(
659 pim
->mroute_socket
, ifp
, (const char *)msg
);
669 static int mroute_read(struct thread
*t
)
671 struct pim_instance
*pim
;
672 static long long count
;
681 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
682 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
687 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
690 if (PIM_DEBUG_MROUTE
)
692 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
693 __PRETTY_FUNCTION__
, rd
,
694 pim
->mroute_socket
, errno
,
695 safe_strerror(errno
));
699 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
702 if (count
% qpim_packet_process
== 0)
712 static void mroute_read_on(struct pim_instance
*pim
)
714 thread_add_read(master
, mroute_read
, pim
, pim
->mroute_socket
,
718 static void mroute_read_off(struct pim_instance
*pim
)
720 THREAD_OFF(pim
->thread
);
723 int pim_mroute_socket_enable(struct pim_instance
*pim
)
727 frr_elevate_privs(&pimd_privs
) {
729 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
732 zlog_warn("Could not create mroute socket: errno=%d: %s",
734 safe_strerror(errno
));
738 #ifdef SO_BINDTODEVICE
739 if (pim
->vrf
->vrf_id
!= VRF_DEFAULT
740 && setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
,
741 pim
->vrf
->name
, strlen(pim
->vrf
->name
))) {
742 zlog_warn("Could not setsockopt SO_BINDTODEVICE: %s",
743 safe_strerror(errno
));
751 pim
->mroute_socket
= fd
;
752 if (pim_mroute_set(pim
, 1)) {
754 "Could not enable mroute on socket fd=%d: errno=%d: %s",
755 fd
, errno
, safe_strerror(errno
));
757 pim
->mroute_socket
= -1;
761 pim
->mroute_socket_creation
= pim_time_monotonic_sec();
768 int pim_mroute_socket_disable(struct pim_instance
*pim
)
770 if (pim_mroute_set(pim
, 0)) {
772 "Could not disable mroute on socket fd=%d: errno=%d: %s",
773 pim
->mroute_socket
, errno
, safe_strerror(errno
));
777 if (close(pim
->mroute_socket
)) {
778 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
779 pim
->mroute_socket
, errno
, safe_strerror(errno
));
783 mroute_read_off(pim
);
784 pim
->mroute_socket
= -1;
790 For each network interface (e.g., physical or a virtual tunnel) that
791 would be used for multicast forwarding, a corresponding multicast
792 interface must be added to the kernel.
794 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
,
797 struct pim_interface
*pim_ifp
= ifp
->info
;
801 if (PIM_DEBUG_MROUTE
)
802 zlog_debug("%s: Add Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
803 pim_ifp
->mroute_vif_index
, ifp
->name
,
804 pim_ifp
->pim
->vrf
->name
);
806 memset(&vc
, 0, sizeof(vc
));
807 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
808 #ifdef VIFF_USE_IFINDEX
809 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
811 if (ifaddr
.s_addr
== INADDR_ANY
) {
813 "%s: unnumbered interfaces are not supported on this platform",
814 __PRETTY_FUNCTION__
);
817 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
819 vc
.vifc_flags
= flags
;
820 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
821 vc
.vifc_rate_limit
= 0;
823 #ifdef PIM_DVMRP_TUNNEL
824 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
825 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
,
826 sizeof(vc
.vifc_rmt_addr
));
830 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_VIF
,
831 (void *)&vc
, sizeof(vc
));
833 char ifaddr_str
[INET_ADDRSTRLEN
];
835 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
,
839 "%s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
840 __PRETTY_FUNCTION__
, pim_ifp
->pim
->mroute_socket
,
841 ifp
->ifindex
, ifaddr_str
, flags
, errno
,
842 safe_strerror(errno
));
849 int pim_mroute_del_vif(struct interface
*ifp
)
851 struct pim_interface
*pim_ifp
= ifp
->info
;
855 if (PIM_DEBUG_MROUTE
)
856 zlog_debug("%s: Del Vif %d (%s[%s])", __PRETTY_FUNCTION__
,
857 pim_ifp
->mroute_vif_index
, ifp
->name
,
858 pim_ifp
->pim
->vrf
->name
);
860 memset(&vc
, 0, sizeof(vc
));
861 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
863 err
= setsockopt(pim_ifp
->pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_VIF
,
864 (void *)&vc
, sizeof(vc
));
867 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
868 __FILE__
, __PRETTY_FUNCTION__
,
869 pim_ifp
->pim
->mroute_socket
, pim_ifp
->mroute_vif_index
,
870 errno
, safe_strerror(errno
));
877 int pim_mroute_add(struct channel_oil
*c_oil
, const char *name
)
879 struct pim_instance
*pim
= c_oil
->pim
;
882 int orig_iif_vif
= 0;
884 pim
->mroute_add_last
= pim_time_monotonic_sec();
885 ++pim
->mroute_add_events
;
887 /* Do not install route if incoming interface is undefined. */
888 if (c_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
889 if (PIM_DEBUG_MROUTE
) {
892 "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
893 __PRETTY_FUNCTION__
, name
,
894 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
899 /* The linux kernel *expects* the incoming
900 * vif to be part of the outgoing list
901 * in the case of a (*,G).
903 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
) {
904 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
905 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
909 * If we have an unresolved cache entry for the S,G
910 * it is owned by the pimreg for the incoming IIF
911 * So set pimreg as the IIF temporarily to cause
912 * the packets to be forwarded. Then set it
913 * to the correct IIF afterwords.
915 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
916 && c_oil
->oil
.mfcc_parent
!= 0) {
917 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
918 c_oil
->oil
.mfcc_parent
= 0;
920 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
921 &c_oil
->oil
, sizeof(c_oil
->oil
));
923 if (!err
&& !c_oil
->installed
924 && c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
925 && orig_iif_vif
!= 0) {
926 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
927 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_ADD_MFC
,
928 &c_oil
->oil
, sizeof(c_oil
->oil
));
931 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
932 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
936 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
937 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
938 errno
, safe_strerror(errno
));
942 if (PIM_DEBUG_MROUTE
) {
944 zlog_debug("%s(%s), vrf %s Added Route: %s",
945 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
946 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
949 c_oil
->installed
= 1;
953 int pim_mroute_del(struct channel_oil
*c_oil
, const char *name
)
955 struct pim_instance
*pim
= c_oil
->pim
;
958 pim
->mroute_del_last
= pim_time_monotonic_sec();
959 ++pim
->mroute_del_events
;
961 if (!c_oil
->installed
) {
962 if (PIM_DEBUG_MROUTE
) {
965 "%s %s: vifi %d for route is %s not installed, do not need to send del req. ",
966 __FILE__
, __PRETTY_FUNCTION__
,
967 c_oil
->oil
.mfcc_parent
,
968 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
973 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_DEL_MFC
,
974 &c_oil
->oil
, sizeof(c_oil
->oil
));
976 if (PIM_DEBUG_MROUTE
)
978 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
979 __FILE__
, __PRETTY_FUNCTION__
,
980 pim
->mroute_socket
, errno
,
981 safe_strerror(errno
));
985 if (PIM_DEBUG_MROUTE
) {
987 zlog_debug("%s(%s), vrf %s Deleted Route: %s",
988 __PRETTY_FUNCTION__
, name
, pim
->vrf
->name
,
989 pim_channel_oil_dump(c_oil
, buf
, sizeof(buf
)));
992 // Reset kernel installed flag
993 c_oil
->installed
= 0;
998 void pim_mroute_update_counters(struct channel_oil
*c_oil
)
1000 struct pim_instance
*pim
= c_oil
->pim
;
1001 struct sioc_sg_req sgreq
;
1003 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
1004 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
1005 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
1007 if (!c_oil
->installed
) {
1008 c_oil
->cc
.lastused
= 100 * pim
->keep_alive_time
;
1009 if (PIM_DEBUG_MROUTE
) {
1010 struct prefix_sg sg
;
1012 sg
.src
= c_oil
->oil
.mfcc_origin
;
1013 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1014 if (PIM_DEBUG_MROUTE
)
1016 "Channel(%s) is not installed no need to collect data from kernel",
1017 pim_str_sg_dump(&sg
));
1022 memset(&sgreq
, 0, sizeof(sgreq
));
1023 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
1024 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1026 pim_zlookup_sg_statistics(c_oil
);
1027 if (ioctl(pim
->mroute_socket
, SIOCGETSGCNT
, &sgreq
)) {
1028 if (PIM_DEBUG_MROUTE
) {
1029 struct prefix_sg sg
;
1031 sg
.src
= c_oil
->oil
.mfcc_origin
;
1032 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
1035 "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s): errno=%d: %s",
1036 (unsigned long)SIOCGETSGCNT
,
1037 pim_str_sg_dump(&sg
), errno
,
1038 safe_strerror(errno
));
1043 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
1044 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
1045 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;