123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- /*
- PIM for Quagga
- Copyright (C) 2008 Everton da Silva Marques
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING; if not, write to the
- Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
- MA 02110-1301 USA
-
- $QuaggaId: $Format:%an, %ai, %h$ $
- */
- #include <zebra.h>
- #include "log.h"
- #include "privs.h"
- #include "pimd.h"
- #include "pim_mroute.h"
- #include "pim_str.h"
- #include "pim_time.h"
- #include "pim_iface.h"
- #include "pim_macro.h"
- /* GLOBAL VARS */
- extern struct zebra_privs_t pimd_privs;
- static void mroute_read_on(void);
- static int pim_mroute_set(int fd, int enable)
- {
- int err;
- int opt = enable ? MRT_INIT : MRT_DONE;
- socklen_t opt_len = sizeof(opt);
- err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len);
- if (err) {
- int e = errno;
- zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__,
- fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e));
- errno = e;
- return -1;
- }
- #if 0
- zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok",
- __FILE__, __PRETTY_FUNCTION__,
- fd, opt);
- #endif
- return 0;
- }
- int pim_mroute_msg(int fd, const char *buf, int buf_size)
- {
- struct interface *ifp;
- const struct ip *ip_hdr;
- const struct igmpmsg *msg;
- const char *upcall;
- char src_str[100];
- char grp_str[100];
- ip_hdr = (const struct ip *) buf;
- /* kernel upcall must have protocol=0 */
- if (ip_hdr->ip_p) {
- /* this is not a kernel upcall */
- #ifdef PIM_UNEXPECTED_KERNEL_UPCALL
- zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d",
- __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size);
- #endif
- return 0;
- }
- msg = (const struct igmpmsg *) buf;
- switch (msg->im_msgtype) {
- case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break;
- case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break;
- case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break;
- default: upcall = "<unknown_upcall?>";
- }
- ifp = pim_if_find_by_vif_index(msg->im_vif);
- pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str));
- pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str));
-
- if (msg->im_msgtype == IGMPMSG_WRONGVIF) {
- struct pim_ifchannel *ch;
- struct pim_interface *pim_ifp;
- /*
- Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
-
- RFC 4601 4.8.2. PIM-SSM-Only Routers
-
- iif is the incoming interface of the packet.
- if (iif is in inherited_olist(S,G)) {
- send Assert(S,G) on iif
- }
- */
- if (PIM_DEBUG_PIM_TRACE) {
- zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
- __PRETTY_FUNCTION__,
- fd,
- src_str,
- grp_str,
- ifp ? ifp->name : "<ifname?>",
- msg->im_vif);
- }
- if (!ifp) {
- zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d",
- __PRETTY_FUNCTION__,
- src_str, grp_str, msg->im_vif);
- return -1;
- }
- pim_ifp = ifp->info;
- if (!pim_ifp) {
- zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
- return -2;
- }
- ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst);
- if (!ch) {
- zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
- return -3;
- }
- /*
- RFC 4601: 4.6.1. (S,G) Assert Message State Machine
- Transitions from NoInfo State
- An (S,G) data packet arrives on interface I, AND
- CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an
- downstream interface that is in our (S,G) outgoing interface
- list. We optimistically assume that we will be the assert
- winner for this (S,G), and so we transition to the "I am Assert
- Winner" state and perform Actions A1 (below), which will
- initiate the assert negotiation for (S,G).
- */
- if (ch->ifassert_state != PIM_IFASSERT_NOINFO) {
- zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
- return -4;
- }
- if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
- zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
- return -5;
- }
- if (assert_action_a1(ch)) {
- zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
- __PRETTY_FUNCTION__,
- src_str, grp_str, ifp->name);
- return -6;
- }
- return 0;
- } /* IGMPMSG_WRONGVIF */
- zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d",
- __PRETTY_FUNCTION__,
- upcall,
- msg->im_msgtype,
- ip_hdr->ip_p,
- fd,
- src_str,
- grp_str,
- ifp ? ifp->name : "<ifname?>",
- msg->im_vif);
- return 0;
- }
- static int mroute_read_msg(int fd)
- {
- const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg));
- char buf[1000];
- int rd;
- if (((int) sizeof(buf)) < msg_min_size) {
- zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d",
- __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size);
- return -1;
- }
- rd = read(fd, buf, sizeof(buf));
- if (rd < 0) {
- zlog_warn("%s: failure reading fd=%d: errno=%d: %s",
- __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
- return -2;
- }
- if (rd < msg_min_size) {
- zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d",
- __PRETTY_FUNCTION__, fd, rd, msg_min_size);
- return -3;
- }
- return pim_mroute_msg(fd, buf, rd);
- }
- static int mroute_read(struct thread *t)
- {
- int fd;
- int result;
- zassert(t);
- zassert(!THREAD_ARG(t));
- fd = THREAD_FD(t);
- zassert(fd == qpim_mroute_socket_fd);
- result = mroute_read_msg(fd);
- /* Keep reading */
- qpim_mroute_socket_reader = 0;
- mroute_read_on();
- return result;
- }
- static void mroute_read_on()
- {
- zassert(!qpim_mroute_socket_reader);
- zassert(PIM_MROUTE_IS_ENABLED);
- THREAD_READ_ON(master, qpim_mroute_socket_reader,
- mroute_read, 0, qpim_mroute_socket_fd);
- }
- static void mroute_read_off()
- {
- THREAD_OFF(qpim_mroute_socket_reader);
- }
- int pim_mroute_socket_enable()
- {
- int fd;
- if (PIM_MROUTE_IS_ENABLED)
- return -1;
- if ( pimd_privs.change (ZPRIVS_RAISE) )
- zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
- safe_strerror (errno) );
- fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
- if ( pimd_privs.change (ZPRIVS_LOWER) )
- zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
- safe_strerror (errno) );
- if (fd < 0) {
- zlog_warn("Could not create mroute socket: errno=%d: %s",
- errno, safe_strerror(errno));
- return -2;
- }
- if (pim_mroute_set(fd, 1)) {
- zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
- fd, errno, safe_strerror(errno));
- close(fd);
- return -3;
- }
- qpim_mroute_socket_fd = fd;
- qpim_mroute_socket_creation = pim_time_monotonic_sec();
- mroute_read_on();
- zassert(PIM_MROUTE_IS_ENABLED);
- return 0;
- }
- int pim_mroute_socket_disable()
- {
- if (PIM_MROUTE_IS_DISABLED)
- return -1;
- if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
- zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
- qpim_mroute_socket_fd, errno, safe_strerror(errno));
- return -2;
- }
- if (close(qpim_mroute_socket_fd)) {
- zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
- qpim_mroute_socket_fd, errno, safe_strerror(errno));
- return -3;
- }
- mroute_read_off();
- qpim_mroute_socket_fd = -1;
- zassert(PIM_MROUTE_IS_DISABLED);
- return 0;
- }
- /*
- For each network interface (e.g., physical or a virtual tunnel) that
- would be used for multicast forwarding, a corresponding multicast
- interface must be added to the kernel.
- */
- int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
- {
- struct vifctl vc;
- int err;
- if (PIM_MROUTE_IS_DISABLED) {
- zlog_warn("%s: global multicast is disabled",
- __PRETTY_FUNCTION__);
- return -1;
- }
- memset(&vc, 0, sizeof(vc));
- vc.vifc_vifi = vif_index;
- vc.vifc_flags = 0;
- vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
- vc.vifc_rate_limit = 0;
- memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
- #ifdef PIM_DVMRP_TUNNEL
- if (vc.vifc_flags & VIFF_TUNNEL) {
- memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
- }
- #endif
- err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
- if (err) {
- char ifaddr_str[100];
- int e = errno;
- pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
- zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__,
- qpim_mroute_socket_fd, vif_index, ifaddr_str,
- e, safe_strerror(e));
- errno = e;
- return -2;
- }
- return 0;
- }
- int pim_mroute_del_vif(int vif_index)
- {
- struct vifctl vc;
- int err;
- if (PIM_MROUTE_IS_DISABLED) {
- zlog_warn("%s: global multicast is disabled",
- __PRETTY_FUNCTION__);
- return -1;
- }
- memset(&vc, 0, sizeof(vc));
- vc.vifc_vifi = vif_index;
- err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
- if (err) {
- int e = errno;
- zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__,
- qpim_mroute_socket_fd, vif_index,
- e, safe_strerror(e));
- errno = e;
- return -2;
- }
- return 0;
- }
- int pim_mroute_add(struct mfcctl *mc)
- {
- int err;
- qpim_mroute_add_last = pim_time_monotonic_sec();
- ++qpim_mroute_add_events;
- if (PIM_MROUTE_IS_DISABLED) {
- zlog_warn("%s: global multicast is disabled",
- __PRETTY_FUNCTION__);
- return -1;
- }
- err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
- mc, sizeof(*mc));
- if (err) {
- int e = errno;
- zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__,
- qpim_mroute_socket_fd,
- e, safe_strerror(e));
- errno = e;
- return -2;
- }
- return 0;
- }
- int pim_mroute_del(struct mfcctl *mc)
- {
- int err;
- qpim_mroute_del_last = pim_time_monotonic_sec();
- ++qpim_mroute_del_events;
- if (PIM_MROUTE_IS_DISABLED) {
- zlog_warn("%s: global multicast is disabled",
- __PRETTY_FUNCTION__);
- return -1;
- }
- err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
- if (err) {
- int e = errno;
- zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
- __FILE__, __PRETTY_FUNCTION__,
- qpim_mroute_socket_fd,
- e, safe_strerror(e));
- errno = e;
- return -2;
- }
- return 0;
- }
|