[pim] Initial pim 0.155
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
new file mode 100644
index 0000000..f021aba
--- /dev/null
+++ b/pimd/pim_mroute.c
@@ -0,0 +1,432 @@
+/*
+  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 "pimd.h"
+#include "pim_mroute.h"
+#include "pim_str.h"
+#include "pim_time.h"
+#include "pim_iface.h"
+#include "pim_macro.h"
+
+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, 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=%d 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, 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;
+
+  fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+  if (fd < 0) {
+    zlog_warn("Could not create mroute socket: errno=%d: %s",
+	      errno, 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, 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, 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, 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, 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, strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_mroute_add(struct mfcctl *mc)
+{
+  int err;
+
+  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, strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}
+
+int pim_mroute_del(struct mfcctl *mc)
+{
+  int err;
+
+  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, strerror(e));
+    errno = e;
+    return -2;
+  }
+
+  return 0;
+}