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
31 #include "pim_mroute.h"
35 #include "pim_iface.h"
36 #include "pim_macro.h"
39 #include "pim_register.h"
40 #include "pim_ifchannel.h"
41 #include "pim_zlookup.h"
45 static void mroute_read_on(struct pim_instance
*pim
);
47 static int pim_mroute_set(struct pim_instance
*pim
, int enable
)
51 socklen_t opt_len
= sizeof(opt
);
55 * We need to create the VRF table for the pim mroute_socket
57 if (pim
->vrf_id
!= VRF_DEFAULT
) {
58 if (pimd_privs
.change(ZPRIVS_RAISE
))
60 "pim_mroute_socket_enable: could not raise privs, %s",
61 safe_strerror(errno
));
63 opt
= pim
->vrf
->data
.l
.table_id
;
64 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, MRT_TABLE
,
68 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP, MRT_TABLE=%d): errno=%d: %s",
69 __FILE__
, __PRETTY_FUNCTION__
,
70 pim
->mroute_socket
, opt
, errno
,
71 safe_strerror(errno
));
75 if (pimd_privs
.change(ZPRIVS_LOWER
))
77 "pim_mroute_socket_enable: could not lower privs, %s",
78 safe_strerror(errno
));
81 opt
= enable
? MRT_INIT
: MRT_DONE
;
82 err
= setsockopt(pim
->mroute_socket
, IPPROTO_IP
, opt
, &opt
, opt_len
);
85 "%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
86 __FILE__
, __PRETTY_FUNCTION__
, pim
->mroute_socket
,
87 enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
,
88 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
= RP(pim_ifp
->pim
, msg
->im_dst
);
157 * If the incoming interface is unknown OR
158 * the Interface type is SSM we don't need to
161 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
162 || (!(PIM_I_am_DR(pim_ifp
)))) {
163 if (PIM_DEBUG_MROUTE_DETAIL
)
165 "%s: Interface is not configured correctly to handle incoming packet: Could be !DR, !pim_ifp, !SM, !RP",
166 __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 up
= pim_upstream_find_or_add(&sg
, ifp
, PIM_UPSTREAM_FLAG_MASK_FHR
,
187 __PRETTY_FUNCTION__
);
189 if (PIM_DEBUG_MROUTE
) {
191 "%s: Failure to add upstream information for %s",
192 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
198 * I moved this debug till after the actual add because
199 * I want to take advantage of the up->sg_str being filled in.
201 if (PIM_DEBUG_MROUTE
) {
202 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
203 __PRETTY_FUNCTION__
, up
->sg_str
);
206 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
207 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
209 up
->channel_oil
->cc
.pktcnt
++;
210 PIM_UPSTREAM_FLAG_SET_FHR(up
->flags
);
211 // resolve mfcc_parent prior to mroute_add in channel_add_oif
212 if (up
->channel_oil
->oil
.mfcc_parent
>= MAXVIFS
) {
214 vif_index
= pim_if_find_vifindex_by_ifindex(
216 up
->rpf
.source_nexthop
.interface
->ifindex
);
217 up
->channel_oil
->oil
.mfcc_parent
= vif_index
;
219 pim_register_join(up
);
224 static int pim_mroute_msg_wholepkt(int fd
, struct interface
*ifp
,
227 struct pim_interface
*pim_ifp
;
230 const struct ip
*ip_hdr
;
231 struct pim_upstream
*up
;
235 ip_hdr
= (const struct ip
*)buf
;
237 memset(&sg
, 0, sizeof(struct prefix_sg
));
238 sg
.src
= ip_hdr
->ip_src
;
239 sg
.grp
= ip_hdr
->ip_dst
;
241 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
243 struct prefix_sg star
= sg
;
244 star
.src
.s_addr
= INADDR_ANY
;
246 up
= pim_upstream_find(pim_ifp
->pim
, &star
);
248 if (up
&& PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(up
->flags
)) {
249 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
250 PIM_UPSTREAM_FLAG_MASK_SRC_LHR
,
251 __PRETTY_FUNCTION__
, NULL
);
253 if (PIM_DEBUG_MROUTE
)
255 "%s: Unable to create upstream information for %s",
257 pim_str_sg_dump(&sg
));
260 pim_upstream_keep_alive_timer_start(
261 up
, pim_ifp
->pim
->keep_alive_time
);
262 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
263 pim_upstream_switch(pim_ifp
->pim
, up
,
264 PIM_UPSTREAM_JOINED
);
266 if (PIM_DEBUG_MROUTE
)
267 zlog_debug("%s: Creating %s upstream on LHR",
268 __PRETTY_FUNCTION__
, up
->sg_str
);
271 if (PIM_DEBUG_MROUTE_DETAIL
) {
273 "%s: Unable to find upstream channel WHOLEPKT%s",
274 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
));
279 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
281 rpg
= RP(pim_ifp
->pim
, sg
.grp
);
283 if ((pim_rpf_addr_is_inaddr_none(rpg
)) || (!pim_ifp
)
284 || (!(PIM_I_am_DR(pim_ifp
)))) {
285 if (PIM_DEBUG_MROUTE
) {
286 zlog_debug("%s: Failed Check send packet",
287 __PRETTY_FUNCTION__
);
293 * If we've received a register suppress
295 if (!up
->t_rs_timer
) {
296 if (pim_is_grp_ssm(pim_ifp
->pim
, sg
.grp
)) {
297 if (PIM_DEBUG_PIM_REG
)
299 "%s register forward skipped as group is SSM",
300 pim_str_sg_dump(&sg
));
303 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
),
304 ntohs(ip_hdr
->ip_len
) - sizeof(struct ip
),
305 pim_ifp
->primary_address
, rpg
, 0, up
);
310 static int pim_mroute_msg_wrongvif(int fd
, struct interface
*ifp
,
311 const struct igmpmsg
*msg
)
313 struct pim_ifchannel
*ch
;
314 struct pim_interface
*pim_ifp
;
317 memset(&sg
, 0, sizeof(struct prefix_sg
));
318 sg
.src
= msg
->im_src
;
319 sg
.grp
= msg
->im_dst
;
322 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
324 RFC 4601 4.8.2. PIM-SSM-Only Routers
326 iif is the incoming interface of the packet.
327 if (iif is in inherited_olist(S,G)) {
328 send Assert(S,G) on iif
333 if (PIM_DEBUG_MROUTE
)
335 "%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
336 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
343 if (PIM_DEBUG_MROUTE
)
345 "%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
346 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
351 ch
= pim_ifchannel_find(ifp
, &sg
);
353 struct prefix_sg star_g
= sg
;
354 if (PIM_DEBUG_MROUTE
)
356 "%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
357 __PRETTY_FUNCTION__
, pim_str_sg_dump(&sg
),
360 star_g
.src
.s_addr
= INADDR_ANY
;
361 ch
= pim_ifchannel_find(ifp
, &star_g
);
363 if (PIM_DEBUG_MROUTE
)
365 "%s: WRONGVIF (*,G)=%s could not find channel on interface %s",
367 pim_str_sg_dump(&star_g
), ifp
->name
);
373 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
375 Transitions from NoInfo State
377 An (S,G) data packet arrives on interface I, AND
378 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
379 downstream interface that is in our (S,G) outgoing interface
380 list. We optimistically assume that we will be the assert
381 winner for this (S,G), and so we transition to the "I am Assert
382 Winner" state and perform Actions A1 (below), which will
383 initiate the assert negotiation for (S,G).
386 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
387 if (PIM_DEBUG_MROUTE
) {
389 "%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
390 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
395 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
396 if (PIM_DEBUG_MROUTE
) {
398 "%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
399 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
404 if (assert_action_a1(ch
)) {
405 if (PIM_DEBUG_MROUTE
) {
407 "%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
408 __PRETTY_FUNCTION__
, ch
->sg_str
, ifp
->name
);
416 static int pim_mroute_msg_wrvifwhole(int fd
, struct interface
*ifp
,
419 const struct ip
*ip_hdr
= (const struct ip
*)buf
;
420 struct pim_interface
*pim_ifp
;
421 struct pim_ifchannel
*ch
;
422 struct pim_upstream
*up
;
423 struct prefix_sg star_g
;
425 struct channel_oil
*oil
;
429 memset(&sg
, 0, sizeof(struct prefix_sg
));
430 sg
.src
= ip_hdr
->ip_src
;
431 sg
.grp
= ip_hdr
->ip_dst
;
433 ch
= pim_ifchannel_find(ifp
, &sg
);
435 if (PIM_DEBUG_MROUTE
)
437 "WRVIFWHOLE (S,G)=%s found ifchannel on interface %s",
438 ch
->sg_str
, ifp
->name
);
443 star_g
.src
.s_addr
= INADDR_ANY
;
445 ch
= pim_ifchannel_find(ifp
, &star_g
);
448 if (PIM_DEBUG_MROUTE
)
449 zlog_debug ("WRVIFWHOLE (*,G)=%s found ifchannel on interface %s",
450 pim_str_sg_dump (&star_g
), ifp
->name
);
455 up
= pim_upstream_find(pim_ifp
->pim
, &sg
);
457 struct pim_upstream
*parent
;
458 struct pim_nexthop source
;
459 struct pim_rpf
*rpf
= RP(pim_ifp
->pim
, sg
.grp
);
460 if (!rpf
|| !rpf
->source_nexthop
.interface
)
464 * If we have received a WRVIFWHOLE and are at this
465 * point, we could be receiving the packet on the *,G
466 * tree, let's check and if so we can safely drop
469 parent
= pim_upstream_find(pim_ifp
->pim
, &star_g
);
470 if (parent
&& parent
->rpf
.source_nexthop
.interface
== ifp
)
473 pim_ifp
= rpf
->source_nexthop
.interface
->info
;
475 memset(&source
, 0, sizeof(source
));
477 * If we are the fhr that means we are getting a callback during
478 * the pimreg period, so I believe we can ignore this packet
480 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up
->flags
)) {
481 // No if channel, but upstream we are at the RP.
482 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
483 up
->upstream_register
, 0)
485 pim_register_stop_send(source
.interface
, &sg
,
486 pim_ifp
->primary_address
,
487 up
->upstream_register
);
488 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
490 if (!up
->channel_oil
)
491 up
->channel_oil
= pim_channel_oil_add(
493 pim_ifp
->mroute_vif_index
);
494 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
495 if (!up
->channel_oil
->installed
)
496 pim_mroute_add(up
->channel_oil
,
497 __PRETTY_FUNCTION__
);
499 if (I_am_RP(pim_ifp
->pim
, up
->sg
.grp
)) {
500 if (pim_nexthop_lookup(pim_ifp
->pim
, &source
,
501 up
->upstream_register
, 0)
503 pim_register_stop_send(
504 source
.interface
, &sg
,
505 pim_ifp
->primary_address
,
506 up
->upstream_register
);
507 up
->sptbit
= PIM_UPSTREAM_SPTBIT_TRUE
;
509 pim_upstream_keep_alive_timer_start(
510 up
, pim_ifp
->pim
->keep_alive_time
);
511 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
512 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
518 oil
= pim_channel_oil_add(pim_ifp
->pim
, &sg
, pim_ifp
->mroute_vif_index
);
520 pim_mroute_add(oil
, __PRETTY_FUNCTION__
);
521 if (pim_if_connected_to_source(ifp
, sg
.src
)) {
522 up
= pim_upstream_add(pim_ifp
->pim
, &sg
, ifp
,
523 PIM_UPSTREAM_FLAG_MASK_FHR
,
524 __PRETTY_FUNCTION__
, NULL
);
526 if (PIM_DEBUG_MROUTE
)
528 "%s: WRONGVIF%s unable to create upstream on interface",
529 pim_str_sg_dump(&sg
), ifp
->name
);
532 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up
->flags
);
533 pim_upstream_keep_alive_timer_start(up
, pim_ifp
->pim
->keep_alive_time
);
534 up
->channel_oil
= oil
;
535 up
->channel_oil
->cc
.pktcnt
++;
536 pim_register_join(up
);
537 pim_upstream_inherited_olist(pim_ifp
->pim
, up
);
539 // Send the packet to the RP
540 pim_mroute_msg_wholepkt(fd
, ifp
, buf
);
546 static int pim_mroute_msg(struct pim_instance
*pim
, const char *buf
,
547 int buf_size
, ifindex_t ifindex
)
549 struct interface
*ifp
;
550 struct pim_interface
*pim_ifp
;
551 const struct ip
*ip_hdr
;
552 const struct igmpmsg
*msg
;
553 char ip_src_str
[INET_ADDRSTRLEN
] = "";
554 char ip_dst_str
[INET_ADDRSTRLEN
] = "";
555 char src_str
[INET_ADDRSTRLEN
] = "<src?>";
556 char grp_str
[INET_ADDRSTRLEN
] = "<grp?>";
557 struct in_addr ifaddr
;
558 struct igmp_sock
*igmp
;
560 ip_hdr
= (const struct ip
*)buf
;
562 if (ip_hdr
->ip_p
== IPPROTO_IGMP
) {
564 /* We have the IP packet but we do not know which interface this
566 * received on. Find the interface that is on the same subnet as
570 ifp
= if_lookup_by_index(ifindex
, pim
->vrf_id
);
572 if (!ifp
|| !ifp
->info
)
576 ifaddr
= pim_find_primary_addr(ifp
);
577 igmp
= pim_igmp_sock_lookup_ifaddr(pim_ifp
->igmp_socket_list
,
580 if (PIM_DEBUG_MROUTE
) {
581 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, ip_src_str
,
583 pim_inet4_dump("<dst?>", ip_hdr
->ip_dst
, ip_dst_str
,
587 "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s",
588 __PRETTY_FUNCTION__
, pim
->vrf
->name
, ifp
->name
,
589 igmp
, ip_src_str
, ip_dst_str
);
592 pim_igmp_packet(igmp
, (char *)buf
, buf_size
);
594 } else if (ip_hdr
->ip_p
) {
595 if (PIM_DEBUG_MROUTE_DETAIL
) {
596 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
,
598 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
,
601 "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d",
602 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
,
607 msg
= (const struct igmpmsg
*)buf
;
609 ifp
= pim_if_find_by_vif_index(pim
, msg
->im_vif
);
613 if (PIM_DEBUG_MROUTE
) {
614 pim_inet4_dump("<src?>", msg
->im_src
, src_str
,
616 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
,
619 "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d",
621 igmpmsgtype2str
[msg
->im_msgtype
],
622 msg
->im_msgtype
, ip_hdr
->ip_p
,
623 pim
->mroute_socket
, src_str
, grp_str
, ifp
->name
,
624 msg
->im_vif
, buf_size
);
627 switch (msg
->im_msgtype
) {
628 case IGMPMSG_WRONGVIF
:
629 return pim_mroute_msg_wrongvif(pim
->mroute_socket
, ifp
,
632 case IGMPMSG_NOCACHE
:
633 return pim_mroute_msg_nocache(pim
->mroute_socket
, ifp
,
636 case IGMPMSG_WHOLEPKT
:
637 return pim_mroute_msg_wholepkt(pim
->mroute_socket
, ifp
,
640 case IGMPMSG_WRVIFWHOLE
:
641 return pim_mroute_msg_wrvifwhole(
642 pim
->mroute_socket
, ifp
, (const char *)msg
);
652 static int mroute_read(struct thread
*t
)
654 struct pim_instance
*pim
;
655 static long long count
;
664 rd
= pim_socket_recvfromto(pim
->mroute_socket
, (uint8_t *)buf
,
665 sizeof(buf
), NULL
, NULL
, NULL
, NULL
,
670 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
673 if (PIM_DEBUG_MROUTE
)
675 "%s: failure reading rd=%d: fd=%d: errno=%d: %s",
676 __PRETTY_FUNCTION__
, rd
,
677 pim
->mroute_socket
, errno
,
678 safe_strerror(errno
));
682 result
= pim_mroute_msg(pim
, buf
, rd
, ifindex
);
685 if (count
% qpim_packet_process
== 0)
695 static void mroute_read_on(struct pim_instance
*pim
)
697 thread_add_read(master
, mroute_read
, pim
, pim
->mroute_socket
,
701 static void mroute_read_off(struct pim_instance
*pim
)
703 THREAD_OFF(pim
->thread
);
706 int pim_mroute_socket_enable(struct pim_instance
*pim
)
710 if (pimd_privs
.change(ZPRIVS_RAISE
))
711 zlog_err("pim_mroute_socket_enable: could not raise privs, %s",
712 safe_strerror(errno
));
714 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
716 #ifdef SO_BINDTODEVICE
717 setsockopt(fd
, SOL_SOCKET
, SO_BINDTODEVICE
, pim
->vrf
->name
,
718 strlen(pim
->vrf
->name
));
721 if (pimd_privs
.change(ZPRIVS_LOWER
))
722 zlog_err("pim_mroute_socket_enable: could not lower privs, %s",
723 safe_strerror(errno
));
726 zlog_warn("Could not create mroute socket: errno=%d: %s", errno
,
727 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
,
784 ifp
->name
, 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",
821 pim_ifp
->pim
->mroute_socket
, ifp
->ifindex
, ifaddr_str
,
822 flags
, errno
, 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
,
838 ifp
->name
, 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", __PRETTY_FUNCTION__
, 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", __PRETTY_FUNCTION__
,
968 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
;