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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
30 #include "pim_mroute.h"
34 #include "pim_iface.h"
35 #include "pim_macro.h"
38 #include "pim_register.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
);
54 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
55 __FILE__
, __PRETTY_FUNCTION__
,
56 fd
, enable
? "MRT_INIT" : "MRT_DONE", opt
, e
, safe_strerror(e
));
62 zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
63 __FILE__
, __PRETTY_FUNCTION__
,
71 pim_mroute_connected_to_source (struct interface
*ifp
, struct in_addr src
)
73 struct listnode
*cnode
;
79 p
.prefixlen
= IPV4_MAX_BITLEN
;
81 for (ALL_LIST_ELEMENTS_RO (ifp
->connected
, cnode
, c
))
83 if ((c
->address
->family
== AF_INET
) &&
84 prefix_match (CONNECTED_PREFIX (c
), &p
))
93 static const char *igmpmsgtype2str
[IGMPMSG_WHOLEPKT
+ 1] = {
100 pim_mroute_msg_nocache (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
,
101 const char *src_str
, const char *grp_str
)
103 struct pim_interface
*pim_ifp
= ifp
->info
;
104 struct pim_upstream
*up
;
107 rpg
= RP(msg
->im_dst
);
109 * If the incoming interface is unknown OR
110 * the Interface type is SSM we don't need to
113 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
115 (!(PIM_I_am_DR(pim_ifp
))) ||
116 (pim_ifp
->itype
== PIM_INTERFACE_SSM
))
120 * If we've received a multicast packet that isn't connected to
123 if (!pim_mroute_connected_to_source (ifp
, msg
->im_src
))
125 if (PIM_DEBUG_PIM_TRACE
)
126 zlog_debug ("%s: Received incoming packet that does originate on our seg",
127 __PRETTY_FUNCTION__
);
131 if (PIM_DEBUG_PIM_TRACE
) {
132 zlog_debug("%s: Adding a Route for %s from %s for WHOLEPKT consumption",
133 __PRETTY_FUNCTION__
, grp_str
, src_str
);
136 up
= pim_upstream_add(msg
->im_src
, msg
->im_dst
, ifp
);
138 if (PIM_DEBUG_PIM_TRACE
) {
139 zlog_debug("%s: Failure to add upstream information for (%s,%s)",
146 pim_upstream_keep_alive_timer_start (up
, PIM_KEEPALIVE_PERIOD
);
148 up
->channel_oil
= pim_channel_oil_add(msg
->im_dst
,
150 pim_ifp
->mroute_vif_index
);
151 if (!up
->channel_oil
) {
152 if (PIM_DEBUG_PIM_TRACE
) {
153 zlog_debug("%s: Failure to add channel oil for (%s,%s)",
159 up
->channel_oil
->cc
.pktcnt
++;
161 pim_channel_add_oif(up
->channel_oil
, pim_regiface
, PIM_OIF_FLAG_PROTO_SOURCE
);
167 pim_mroute_msg_wholepkt (int fd
, struct interface
*ifp
, const char *buf
,
168 const char *src_str
, const char *grp_str
)
170 struct pim_interface
*pim_ifp
;
171 struct in_addr group
;
174 const struct ip
*ip_hdr
;
175 struct pim_upstream
*up
;
177 ip_hdr
= (const struct ip
*)buf
;
179 src
= ip_hdr
->ip_src
;
180 group
= ip_hdr
->ip_dst
;
182 up
= pim_upstream_find(src
, group
);
184 if (PIM_DEBUG_PIM_TRACE
) {
185 zlog_debug("%s: Unable to find upstream channel WHOLEPKT(%s,%s)",
186 __PRETTY_FUNCTION__
, src_str
, grp_str
);
191 pim_ifp
= up
->rpf
.source_nexthop
.interface
->info
;
195 if ((rpg
->rpf_addr
.s_addr
== INADDR_NONE
) ||
197 (!(PIM_I_am_DR(pim_ifp
))) ||
198 (pim_ifp
->itype
== PIM_INTERFACE_SSM
)) {
199 if (PIM_DEBUG_PIM_TRACE
) {
200 zlog_debug("%s: Failed Check send packet", __PRETTY_FUNCTION__
);
205 pim_register_send((const struct ip
*)(buf
+ sizeof(struct ip
)), rpg
);
210 pim_mroute_msg_wrongvif (int fd
, struct interface
*ifp
, const struct igmpmsg
*msg
,
211 const char *src_str
, const char *grp_str
)
213 struct pim_ifchannel
*ch
;
214 struct pim_interface
*pim_ifp
;
217 Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
219 RFC 4601 4.8.2. PIM-SSM-Only Routers
221 iif is the incoming interface of the packet.
222 if (iif is in inherited_olist(S,G)) {
223 send Assert(S,G) on iif
228 if (PIM_DEBUG_PIM_TRACE
) {
229 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
231 src_str
, grp_str
, msg
->im_vif
);
238 if (PIM_DEBUG_PIM_TRACE
) {
239 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
241 src_str
, grp_str
, ifp
->name
);
246 ch
= pim_ifchannel_find(ifp
, msg
->im_src
, msg
->im_dst
);
248 if (PIM_DEBUG_PIM_TRACE
) {
249 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
251 src_str
, grp_str
, ifp
->name
);
257 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
259 Transitions from NoInfo State
261 An (S,G) data packet arrives on interface I, AND
262 CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
263 downstream interface that is in our (S,G) outgoing interface
264 list. We optimistically assume that we will be the assert
265 winner for this (S,G), and so we transition to the "I am Assert
266 Winner" state and perform Actions A1 (below), which will
267 initiate the assert negotiation for (S,G).
270 if (ch
->ifassert_state
!= PIM_IFASSERT_NOINFO
) {
271 if (PIM_DEBUG_PIM_TRACE
) {
272 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
274 src_str
, grp_str
, ifp
->name
);
279 if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
280 if (PIM_DEBUG_PIM_TRACE
) {
281 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
283 src_str
, grp_str
, ifp
->name
);
288 if (assert_action_a1(ch
)) {
289 if (PIM_DEBUG_PIM_TRACE
) {
290 zlog_debug("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
292 src_str
, grp_str
, ifp
->name
);
300 int pim_mroute_msg(int fd
, const char *buf
, int buf_size
)
302 struct interface
*ifp
;
303 const struct ip
*ip_hdr
;
304 const struct igmpmsg
*msg
;
305 char src_str
[100] = "<src?>";
306 char grp_str
[100] = "<grp?>";
308 ip_hdr
= (const struct ip
*) buf
;
310 /* kernel upcall must have protocol=0 */
312 /* this is not a kernel upcall */
313 if (PIM_DEBUG_PIM_TRACE
) {
314 pim_inet4_dump("<src?>", ip_hdr
->ip_src
, src_str
, sizeof(src_str
));
315 pim_inet4_dump("<grp?>", ip_hdr
->ip_dst
, grp_str
, sizeof(grp_str
));
316 zlog_debug("%s: not a kernel upcall proto=%d src: %s dst: %s msg_size=%d",
317 __PRETTY_FUNCTION__
, ip_hdr
->ip_p
, src_str
, grp_str
, buf_size
);
322 msg
= (const struct igmpmsg
*) buf
;
324 ifp
= pim_if_find_by_vif_index(msg
->im_vif
);
326 if (PIM_DEBUG_PIM_TRACE
) {
327 pim_inet4_dump("<src?>", msg
->im_src
, src_str
, sizeof(src_str
));
328 pim_inet4_dump("<grp?>", msg
->im_dst
, grp_str
, sizeof(grp_str
));
329 zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
331 igmpmsgtype2str
[msg
->im_msgtype
],
337 ifp
? ifp
->name
: "<ifname?>",
341 switch (msg
->im_msgtype
) {
342 case IGMPMSG_WRONGVIF
:
343 return pim_mroute_msg_wrongvif(fd
, ifp
, msg
, src_str
, grp_str
);
345 case IGMPMSG_NOCACHE
:
346 return pim_mroute_msg_nocache(fd
, ifp
, msg
, src_str
, grp_str
);
348 case IGMPMSG_WHOLEPKT
:
349 return pim_mroute_msg_wholepkt(fd
, ifp
, (const char *)msg
, src_str
, grp_str
);
358 static int mroute_read_msg(int fd
)
360 const int msg_min_size
= MAX(sizeof(struct ip
), sizeof(struct igmpmsg
));
364 if (((int) sizeof(buf
)) < msg_min_size
) {
365 zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
366 __PRETTY_FUNCTION__
, fd
, sizeof(buf
), msg_min_size
);
370 rd
= read(fd
, buf
, sizeof(buf
));
372 zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
373 __PRETTY_FUNCTION__
, fd
, errno
, safe_strerror(errno
));
377 if (rd
< msg_min_size
) {
378 zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
379 __PRETTY_FUNCTION__
, fd
, rd
, msg_min_size
);
383 return pim_mroute_msg(fd
, buf
, rd
);
386 static int mroute_read(struct thread
*t
)
392 zassert(!THREAD_ARG(t
));
395 zassert(fd
== qpim_mroute_socket_fd
);
397 result
= mroute_read_msg(fd
);
400 qpim_mroute_socket_reader
= 0;
406 static void mroute_read_on()
408 zassert(!qpim_mroute_socket_reader
);
409 zassert(PIM_MROUTE_IS_ENABLED
);
411 THREAD_READ_ON(master
, qpim_mroute_socket_reader
,
412 mroute_read
, 0, qpim_mroute_socket_fd
);
415 static void mroute_read_off()
417 THREAD_OFF(qpim_mroute_socket_reader
);
420 int pim_mroute_socket_enable()
424 if (PIM_MROUTE_IS_ENABLED
)
427 if ( pimd_privs
.change (ZPRIVS_RAISE
) )
428 zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
429 safe_strerror (errno
) );
431 fd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_IGMP
);
433 if ( pimd_privs
.change (ZPRIVS_LOWER
) )
434 zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
435 safe_strerror (errno
) );
438 zlog_warn("Could not create mroute socket: errno=%d: %s",
439 errno
, safe_strerror(errno
));
443 if (pim_mroute_set(fd
, 1)) {
444 zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
445 fd
, errno
, safe_strerror(errno
));
450 qpim_mroute_socket_fd
= fd
;
452 qpim_mroute_socket_creation
= pim_time_monotonic_sec();
455 zassert(PIM_MROUTE_IS_ENABLED
);
460 int pim_mroute_socket_disable()
462 if (PIM_MROUTE_IS_DISABLED
)
465 if (pim_mroute_set(qpim_mroute_socket_fd
, 0)) {
466 zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
467 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
471 if (close(qpim_mroute_socket_fd
)) {
472 zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
473 qpim_mroute_socket_fd
, errno
, safe_strerror(errno
));
478 qpim_mroute_socket_fd
= -1;
480 zassert(PIM_MROUTE_IS_DISABLED
);
486 For each network interface (e.g., physical or a virtual tunnel) that
487 would be used for multicast forwarding, a corresponding multicast
488 interface must be added to the kernel.
490 int pim_mroute_add_vif(struct interface
*ifp
, struct in_addr ifaddr
, unsigned char flags
)
492 struct pim_interface
*pim_ifp
= ifp
->info
;
496 if (PIM_MROUTE_IS_DISABLED
) {
497 zlog_warn("%s: global multicast is disabled",
498 __PRETTY_FUNCTION__
);
502 memset(&vc
, 0, sizeof(vc
));
503 vc
.vifc_vifi
= pim_ifp
->mroute_vif_index
;
504 #ifdef VIFF_USE_IFINDEX
505 vc
.vifc_lcl_ifindex
= ifp
->ifindex
;
507 if (ifaddr
.s_addr
== INADDR_ANY
) {
508 zlog_warn("%s: unnumbered interfaces are not supported on this platform",
509 __PRETTY_FUNCTION__
);
512 memcpy(&vc
.vifc_lcl_addr
, &ifaddr
, sizeof(vc
.vifc_lcl_addr
));
514 vc
.vifc_flags
= flags
;
515 vc
.vifc_threshold
= PIM_MROUTE_MIN_TTL
;
516 vc
.vifc_rate_limit
= 0;
518 #ifdef PIM_DVMRP_TUNNEL
519 if (vc
.vifc_flags
& VIFF_TUNNEL
) {
520 memcpy(&vc
.vifc_rmt_addr
, &vif_remote_addr
, sizeof(vc
.vifc_rmt_addr
));
524 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_VIF
, (void*) &vc
, sizeof(vc
));
526 char ifaddr_str
[100];
529 pim_inet4_dump("<ifaddr?>", ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
531 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s,flag=%d): errno=%d: %s",
532 __FILE__
, __PRETTY_FUNCTION__
,
533 qpim_mroute_socket_fd
, ifp
->ifindex
, ifaddr_str
, flags
,
534 e
, safe_strerror(e
));
542 int pim_mroute_del_vif(int vif_index
)
547 if (PIM_MROUTE_IS_DISABLED
) {
548 zlog_warn("%s: global multicast is disabled",
549 __PRETTY_FUNCTION__
);
553 memset(&vc
, 0, sizeof(vc
));
554 vc
.vifc_vifi
= vif_index
;
556 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_VIF
, (void*) &vc
, sizeof(vc
));
559 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
560 __FILE__
, __PRETTY_FUNCTION__
,
561 qpim_mroute_socket_fd
, vif_index
,
562 e
, safe_strerror(e
));
570 int pim_mroute_add(struct channel_oil
*c_oil
)
575 qpim_mroute_add_last
= pim_time_monotonic_sec();
576 ++qpim_mroute_add_events
;
578 if (PIM_MROUTE_IS_DISABLED
) {
579 zlog_warn("%s: global multicast is disabled",
580 __PRETTY_FUNCTION__
);
584 /* The linux kernel *expects* the incoming
585 * vif to be part of the outgoing list
586 * in the case of a (*,G).
588 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
590 orig
= c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
];
591 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = 1;
594 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_ADD_MFC
,
595 &c_oil
->oil
, sizeof(c_oil
->oil
));
597 if (c_oil
->oil
.mfcc_origin
.s_addr
== INADDR_ANY
)
598 c_oil
->oil
.mfcc_ttls
[c_oil
->oil
.mfcc_parent
] = orig
;
602 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
603 __FILE__
, __PRETTY_FUNCTION__
,
604 qpim_mroute_socket_fd
,
605 e
, safe_strerror(e
));
610 c_oil
->installed
= 1;
614 int pim_mroute_del (struct channel_oil
*c_oil
)
618 qpim_mroute_del_last
= pim_time_monotonic_sec();
619 ++qpim_mroute_del_events
;
621 if (PIM_MROUTE_IS_DISABLED
) {
622 zlog_warn("%s: global multicast is disabled",
623 __PRETTY_FUNCTION__
);
627 err
= setsockopt(qpim_mroute_socket_fd
, IPPROTO_IP
, MRT_DEL_MFC
, &c_oil
->oil
, sizeof(c_oil
->oil
));
630 zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
631 __FILE__
, __PRETTY_FUNCTION__
,
632 qpim_mroute_socket_fd
,
633 e
, safe_strerror(e
));
638 c_oil
->installed
= 0;
644 pim_mroute_update_counters (struct channel_oil
*c_oil
)
646 struct sioc_sg_req sgreq
;
648 memset (&sgreq
, 0, sizeof(sgreq
));
649 sgreq
.src
= c_oil
->oil
.mfcc_origin
;
650 sgreq
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
652 c_oil
->cc
.oldpktcnt
= c_oil
->cc
.pktcnt
;
653 c_oil
->cc
.oldbytecnt
= c_oil
->cc
.bytecnt
;
654 c_oil
->cc
.oldwrong_if
= c_oil
->cc
.wrong_if
;
656 if (ioctl (qpim_mroute_socket_fd
, SIOCGETSGCNT
, &sgreq
))
659 char source_str
[100];
661 pim_inet4_dump("<group?>", c_oil
->oil
.mfcc_mcastgrp
, group_str
, sizeof(group_str
));
662 pim_inet4_dump("<source?>", c_oil
->oil
.mfcc_origin
, source_str
, sizeof(source_str
));
664 zlog_warn ("ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s",
665 (unsigned long)SIOCGETSGCNT
,
669 safe_strerror(errno
));
673 c_oil
->cc
.pktcnt
= sgreq
.pktcnt
;
674 c_oil
->cc
.bytecnt
= sgreq
.bytecnt
;
675 c_oil
->cc
.wrong_if
= sgreq
.wrong_if
;