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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
29 #include "pim_mroute.h"
33 #include "pim_iface.h"
34 #include "pim_macro.h"
37 #include "pim_register.h"
38 #include "pim_ifchannel.h"
41 extern struct zebra_privs_t pimd_privs
;
43 static void mroute_read_on(void);
45 static int pim_mroute_set(int fd
, int enable
)
48 int opt
= enable
? MRT_INIT
: MRT_DONE
;
49 socklen_t opt_len
= sizeof(opt
);
51 err
= setsockopt(fd
, IPPROTO_IP
, opt
, &opt
, opt_len
);
53 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
54 __FILE__
, __PRETTY_FUNCTION__
,
55 fd
, enable
? "MRT_INIT" : "MRT_DONE", opt
, errno
, safe_strerror(errno
));
61 int upcalls
= IGMPMSG_WRVIFWHOLE
;
64 err
= setsockopt (fd
, IPPROTO_IP
, opt
, &upcalls
, sizeof (upcalls
));
67 zlog_warn ("Failure to register for VIFWHOLE and WRONGVIF upcalls %d %s",
68 errno
, safe_strerror (errno
));
77 pim_mroute_connected_to_source (struct interface
*ifp
, struct in_addr src
)
79 struct listnode
*cnode
;
85 p
.prefixlen
= IPV4_MAX_BITLEN
;
87 for (ALL_LIST_ELEMENTS_RO (ifp
->connected
, cnode
, c
))
89 if ((c
->address
->family
== AF_INET
) &&
90 prefix_match (CONNECTED_PREFIX (c
), &p
))
99 static const char *igmpmsgtype2str
[IGMPMSG_WHOLEPKT
+ 1] = {
106 pim_mroute_msg_nocache (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
)
108 struct pim_interface
*pim_ifp
= ifp
->info
;
109 struct pim_upstream
*up
;
113 rpg
= RP(msg
->im_dst
);
115 * If the incoming interface is unknown OR
116 * the Interface type is SSM we don't need to
119 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
121 (!(PIM_I_am_DR(pim_ifp
))) ||
122 (pim_ifp
->itype
== PIM_INTERFACE_SSM
))
126 * If we've received a multicast packet that isn't connected to
129 if (!pim_mroute_connected_to_source (ifp
, msg
->im_src
))
131 if (PIM_DEBUG_MROUTE
)
132 zlog_debug ("%s: Received incoming packet that does originate on our seg",
133 __PRETTY_FUNCTION__
);
137 memset (&sg
, 0, sizeof (struct prefix_sg
));
138 sg
.src
= msg
->im_src
;
139 sg
.grp
= msg
->im_dst
;
141 if (PIM_DEBUG_MROUTE
) {
142 zlog_debug("%s: Adding a Route %s for WHOLEPKT consumption",
143 __PRETTY_FUNCTION__
, pim_str_sg_dump (&sg
));
146 up
= pim_upstream_add (&sg
, ifp
);
148 if (PIM_DEBUG_MROUTE
) {
149 zlog_debug("%s: Failure to add upstream information for %s",
151 pim_str_sg_dump (&sg
));
156 pim_upstream_keep_alive_timer_start (up
, PIM_KEEPALIVE_PERIOD
);
158 up
->channel_oil
= pim_channel_oil_add(&sg
,
159 pim_ifp
->mroute_vif_index
);
160 if (!up
->channel_oil
) {
161 if (PIM_DEBUG_MROUTE
) {
162 zlog_debug("%s: Failure to add channel oil for %s",
164 pim_str_sg_dump (&sg
));
168 up
->channel_oil
->cc
.pktcnt
++;
170 pim_channel_add_oif (up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_PIM
);
171 up
->join_state
= PIM_UPSTREAM_JOINED
;
177 pim_mroute_msg_wholepkt (int fd
, struct interface
*ifp
, const char *buf
)
179 struct pim_interface
*pim_ifp
;
182 const struct ip
*ip_hdr
;
183 struct pim_upstream
*up
;
185 ip_hdr
= (const struct ip
*)buf
;
187 memset (&sg
, 0, sizeof (struct prefix_sg
));
188 sg
.src
= ip_hdr
->ip_src
;
189 sg
.grp
= ip_hdr
->ip_dst
;
191 up
= pim_upstream_find(&sg
);
193 if (PIM_DEBUG_MROUTE
) {
194 zlog_debug("%s: Unable to find upstream channel WHOLEPKT%s",
195 __PRETTY_FUNCTION__
, pim_str_sg_dump (&sg
));
200 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
204 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
206 (!(PIM_I_am_DR(pim_ifp
))) ||
207 (pim_ifp
->itype
== PIM_INTERFACE_SSM
)) {
208 if (PIM_DEBUG_MROUTE
) {
209 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__
);
215 * If we've received a register suppress
218 pim_register_send((uint8_t *)buf
+ sizeof(struct ip
), ntohs (ip_hdr
->ip_len
), rpg
, 0);
224 pim_mroute_msg_wrongvif (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
)
226 struct pim_ifchannel
*ch
;
227 struct pim_interface
*pim_ifp
;
230 memset (&sg
, 0, sizeof (struct prefix_sg
));
231 sg
.src
= msg
->im_src
;
232 sg
.grp
= msg
->im_dst
;
235 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
237 RFC 4601 4.8.2. PIM-SSM-Only Routers
239 iif is the incoming interface of the packet.
240 if (iif is in inherited_olist(S,G)) {
241 send Assert(S,G) on iif
246 if (PIM_DEBUG_MROUTE
) {
247 zlog_debug("%s: WRONGVIF (S,G)=%s could not find input interface for input_vif_index=%d",
249 pim_str_sg_dump (&sg
), msg
->im_vif
);
256 if (PIM_DEBUG_MROUTE
) {
257 zlog_debug("%s: WRONGVIF (S,G)=%s multicast not enabled on interface %s",
259 pim_str_sg_dump (&sg
), ifp
->name
);
264 ch
= pim_ifchannel_find(ifp
, &sg
);
266 if (PIM_DEBUG_MROUTE
) {
267 zlog_debug("%s: WRONGVIF (S,G)=%s could not find channel on interface %s",
269 pim_str_sg_dump (&sg
), ifp
->name
);
275 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
277 Transitions from NoInfo State
279 An (S,G) data packet arrives on interface I, AND
280 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
281 downstream interface that is in our (S,G) outgoing interface
282 list. We optimistically assume that we will be the assert
283 winner for this (S,G), and so we transition to the "I am Assert
284 Winner" state and perform Actions A1 (below), which will
285 initiate the assert negotiation for (S,G).
288 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
289 if (PIM_DEBUG_MROUTE
) {
290 zlog_debug("%s: WRONGVIF (S,G)=%s channel is not on Assert NoInfo state for interface %s",
292 pim_str_sg_dump (&sg
), ifp
->name
);
297 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
298 if (PIM_DEBUG_MROUTE
) {
299 zlog_debug("%s: WRONGVIF (S,G)=%s interface %s is not downstream for channel",
301 pim_str_sg_dump (&sg
), ifp
->name
);
306 if (assert_action_a1(ch
)) {
307 if (PIM_DEBUG_MROUTE
) {
308 zlog_debug("%s: WRONGVIF (S,G)=%s assert_action_a1 failure on interface %s",
310 pim_str_sg_dump (&sg
), ifp
->name
);
318 int pim_mroute_msg(int fd
, const char *buf
, int buf_size
)
320 struct interface
*ifp
;
321 const struct ip
*ip_hdr
;
322 const struct igmpmsg
*msg
;
323 char src_str
[100] = "<src?>";
324 char grp_str
[100] = "<grp?>";
326 ip_hdr
= (const struct ip
*) buf
;
328 /* kernel upcall must have protocol=0 */
330 /* this is not a kernel upcall */
331 if (PIM_DEBUG_MROUTE
) {
332 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
, sizeof(src_str
));
333 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
, sizeof(grp_str
));
334 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
335 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
, grp_str
, buf_size
);
340 msg
= (const struct igmpmsg
*) buf
;
342 ifp
= pim_if_find_by_vif_index(msg
->im_vif
);
344 if (PIM_DEBUG_MROUTE
) {
345 pim_inet4_dump("<src?>", msg
->im_src
, src_str
, sizeof(src_str
));
346 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
, sizeof(grp_str
));
347 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
349 igmpmsgtype2str
[msg
->im_msgtype
],
359 switch (msg
->im_msgtype
) {
360 case IGMPMSG_WRONGVIF
:
361 return pim_mroute_msg_wrongvif(fd
, ifp
, msg
);
363 case IGMPMSG_NOCACHE
:
364 return pim_mroute_msg_nocache(fd
, ifp
, msg
);
366 case IGMPMSG_WHOLEPKT
:
367 return pim_mroute_msg_wholepkt(fd
, ifp
, (const char *)msg
);
376 static int mroute_read_msg(int fd
)
378 const int msg_min_size
= MAX(sizeof(struct ip
), sizeof(struct igmpmsg
));
382 if (((int) sizeof(buf
)) < msg_min_size
) {
383 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
384 __PRETTY_FUNCTION__
, fd
, sizeof(buf
), msg_min_size
);
388 rd
= read(fd
, buf
, sizeof(buf
));
390 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
391 __PRETTY_FUNCTION__
, fd
, errno
, safe_strerror(errno
));
395 if (rd
< msg_min_size
) {
396 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
397 __PRETTY_FUNCTION__
, fd
, rd
, msg_min_size
);
401 return pim_mroute_msg(fd
, buf
, rd
);
404 static int mroute_read(struct thread
*t
)
410 zassert(!THREAD_ARG(t
));
413 zassert(fd
== qpim_mroute_socket_fd
);
415 result
= mroute_read_msg(fd
);
418 qpim_mroute_socket_reader
= 0;
424 static void mroute_read_on()
426 zassert(!qpim_mroute_socket_reader
);
427 zassert(PIM_MROUTE_IS_ENABLED
);
429 THREAD_READ_ON(master
, qpim_mroute_socket_reader
,
430 mroute_read
, 0, qpim_mroute_socket_fd
);
433 static void mroute_read_off()
435 THREAD_OFF(qpim_mroute_socket_reader
);
438 int pim_mroute_socket_enable()
442 if (PIM_MROUTE_IS_ENABLED
)
445 if ( pimd_privs
.change (ZPRIVS_RAISE
) )
446 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
447 safe_strerror (errno
) );
449 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
451 if ( pimd_privs
.change (ZPRIVS_LOWER
) )
452 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
453 safe_strerror (errno
) );
456 zlog_warn("Could not create mroute socket: errno=%d: %s",
457 errno
, safe_strerror(errno
));
461 if (pim_mroute_set(fd
, 1)) {
462 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
463 fd
, errno
, safe_strerror(errno
));
468 qpim_mroute_socket_fd
= fd
;
470 qpim_mroute_socket_creation
= pim_time_monotonic_sec();
476 int pim_mroute_socket_disable()
478 if (PIM_MROUTE_IS_DISABLED
)
481 if (pim_mroute_set(qpim_mroute_socket_fd
, 0)) {
482 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
483 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
487 if (close(qpim_mroute_socket_fd
)) {
488 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
489 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
494 qpim_mroute_socket_fd
= -1;
500 For each network interface (e.g., physical or a virtual tunnel) that
501 would be used for multicast forwarding, a corresponding multicast
502 interface must be added to the kernel.
504 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
, unsigned char flags
)
506 struct pim_interface
*pim_ifp
= ifp
->info
;
510 if (PIM_MROUTE_IS_DISABLED
) {
511 zlog_warn("%s: global multicast is disabled",
512 __PRETTY_FUNCTION__
);
516 memset(&vc
, 0, sizeof(vc
));
517 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
518 #ifdef VIFF_USE_IFINDEX
519 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
521 if (ifaddr
.s_addr
== INADDR_ANY
) {
522 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
523 __PRETTY_FUNCTION__
);
526 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
528 vc
.vifc_flags
= flags
;
529 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
530 vc
.vifc_rate_limit
= 0;
532 #ifdef PIM_DVMRP_TUNNEL
533 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
534 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
, sizeof(vc
.vifc_rmt_addr
));
538 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_VIF
, (void*) &vc
, sizeof(vc
));
540 char ifaddr_str
[100];
542 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
544 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
545 __FILE__
, __PRETTY_FUNCTION__
,
546 qpim_mroute_socket_fd
, ifp
->ifindex
, ifaddr_str
, flags
,
547 errno
, safe_strerror(errno
));
554 int pim_mroute_del_vif(int vif_index
)
559 if (PIM_MROUTE_IS_DISABLED
) {
560 zlog_warn("%s: global multicast is disabled",
561 __PRETTY_FUNCTION__
);
565 memset(&vc
, 0, sizeof(vc
));
566 vc
.vifc_vifi
= vif_index
;
568 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_VIF
, (void*) &vc
, sizeof(vc
));
570 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
571 __FILE__
, __PRETTY_FUNCTION__
,
572 qpim_mroute_socket_fd
, vif_index
,
573 errno
, safe_strerror(errno
));
580 int pim_mroute_add(struct channel_oil
*c_oil
)
584 int orig_iif_vif
= 0;
586 qpim_mroute_add_last
= pim_time_monotonic_sec();
587 ++qpim_mroute_add_events
;
589 if (PIM_MROUTE_IS_DISABLED
) {
590 zlog_warn("%s: global multicast is disabled",
591 __PRETTY_FUNCTION__
);
595 /* The linux kernel *expects* the incoming
596 * vif to be part of the outgoing list
597 * in the case of a (*,G).
599 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
601 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
602 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
606 * If we have an unresolved cache entry for the S,G
607 * it is owned by the pimreg for the incoming IIF
608 * So set pimreg as the IIF temporarily to cause
609 * the packets to be forwarded. Then set it
610 * to the correct IIF afterwords.
612 if (!c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
&&
613 c_oil
->oil
.mfcc_parent
!= 0)
615 orig_iif_vif
= c_oil
->oil
.mfcc_parent
;
616 c_oil
->oil
.mfcc_parent
= 0;
618 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
619 &c_oil
->oil
, sizeof(c_oil
->oil
));
621 if (!err
&& !c_oil
->installed
&& c_oil
->oil
.mfcc_origin
.s_addr
!= INADDR_ANY
&&
624 c_oil
->oil
.mfcc_parent
= orig_iif_vif
;
625 err
= setsockopt (qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
626 &c_oil
->oil
, sizeof (c_oil
->oil
));
629 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
630 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
633 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
634 __FILE__
, __PRETTY_FUNCTION__
,
635 qpim_mroute_socket_fd
,
636 errno
, safe_strerror(errno
));
640 c_oil
->installed
= 1;
644 int pim_mroute_del (struct channel_oil
*c_oil
)
648 qpim_mroute_del_last
= pim_time_monotonic_sec();
649 ++qpim_mroute_del_events
;
651 if (PIM_MROUTE_IS_DISABLED
) {
652 zlog_warn("%s: global multicast is disabled",
653 __PRETTY_FUNCTION__
);
657 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_MFC
, &c_oil
->oil
, sizeof(c_oil
->oil
));
659 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
660 __FILE__
, __PRETTY_FUNCTION__
,
661 qpim_mroute_socket_fd
,
662 errno
, safe_strerror(errno
));
666 c_oil
->installed
= 0;
672 pim_mroute_update_counters (struct channel_oil
*c_oil
)
674 struct sioc_sg_req sgreq
;
676 memset (&sgreq
, 0, sizeof(sgreq
));
677 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
678 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
680 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
681 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
682 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
684 if (ioctl (qpim_mroute_socket_fd
, SIOCGETSGCNT
, &sgreq
))
687 char source_str
[100];
689 pim_inet4_dump("<group?>", c_oil
->oil
.mfcc_mcastgrp
, group_str
, sizeof(group_str
));
690 pim_inet4_dump("<source?>", c_oil
->oil
.mfcc_origin
, source_str
, sizeof(source_str
));
692 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
693 (unsigned long)SIOCGETSGCNT
,
697 safe_strerror(errno
));
701 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
702 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
703 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;