Hi,

This patch adds the ability to configure multicast static routes directly into pimd. Two source files are introduced to implement the new feature in addition to changes to existing files.

Here is how it can be used the CLI:

interface <incoming interface>
ip mroute <outgoing interface> <group addr> # for asm
or ip mroute <outgoing interface> <group addr> <source>    # for ssm

Please let me know if you have any questions or concerns,

Regards,
Jafar


diff --git a/lib/memtypes.c b/lib/memtypes.c
index ab3c1f8..57de5c4 100644
--- a/lib/memtypes.c
+++ b/lib/memtypes.c
@@ -270,6 +270,7 @@ struct memory_list memory_list_pim[] =
   { MTYPE_PIM_IFCHANNEL,         "PIM interface (S,G) state"      },
   { MTYPE_PIM_UPSTREAM,          "PIM upstream (S,G) state"       },
   { MTYPE_PIM_SSMPINGD,          "PIM sspimgd socket"             },
+  { MTYPE_PIM_STATIC_ROUTE,      "PIM Static Route"               },
   { -1, NULL },
 };
 
diff --git a/pimd/Makefile.am b/pimd/Makefile.am
index b82613a..45a418a 100644
--- a/pimd/Makefile.am
+++ b/pimd/Makefile.am
@@ -53,7 +53,7 @@ libpim_a_SOURCES = \
        pim_oil.c pim_zlookup.c pim_pim.c pim_tlv.c pim_neighbor.c \
        pim_hello.c pim_ifchannel.c pim_join.c pim_assert.c \
        pim_msg.c pim_upstream.c pim_rpf.c pim_rand.c pim_macro.c \
-       pim_igmp_join.c pim_ssmpingd.c pim_int.c
+       pim_igmp_join.c pim_ssmpingd.c pim_int.c pim_static.c
 
 noinst_HEADERS = \
        pimd.h pim_version.h pim_cmd.h pim_signals.h pim_iface.h \
@@ -62,7 +62,7 @@ noinst_HEADERS = \
        pim_oil.h pim_zlookup.h pim_pim.h pim_tlv.h pim_neighbor.h \
        pim_hello.h pim_ifchannel.h pim_join.h pim_assert.h \
        pim_msg.h pim_upstream.h pim_rpf.h pim_rand.h pim_macro.h \
-       pim_igmp_join.h pim_ssmpingd.h pim_int.h
+       pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_static.h
 
 pimd_SOURCES = \
        pim_main.c $(libpim_a_SOURCES)
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 13812cd..99aed26 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -51,6 +51,7 @@
 #include "pim_macro.h"
 #include "pim_ssmpingd.h"
 #include "pim_zebra.h"
+#include "pim_static.h"
 
 static struct cmd_node pim_global_node = {
   PIM_NODE,
@@ -1626,6 +1627,44 @@ static void mroute_del_all()
   }
 }
 
+static void static_mroute_add_all()
+{
+  struct listnode     *node;
+  struct static_route *s_route;
+
+  for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+    if (pim_mroute_add(&s_route->mc)) {
+      /* just log warning */
+      char source_str[100];
+      char group_str[100];
+      pim_inet4_dump("<source?>", s_route->mc.mfcc_origin, source_str, 
sizeof(source_str));
+      pim_inet4_dump("<group?>", s_route->mc.mfcc_mcastgrp, group_str, 
sizeof(group_str));
+      zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC",
+      __FILE__, __PRETTY_FUNCTION__,
+      source_str, group_str);
+    }
+  }
+}
+
+static void static_mroute_del_all()
+{
+   struct listnode     *node;
+   struct static_route *s_route;
+
+   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+     if (pim_mroute_del(&s_route->mc)) {
+       /* just log warning */
+       char source_str[100];
+       char group_str[100];
+       pim_inet4_dump("<source?>", s_route->mc.mfcc_origin, source_str, 
sizeof(source_str));
+       pim_inet4_dump("<group?>", s_route->mc.mfcc_mcastgrp, group_str, 
sizeof(group_str));
+       zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC",
+       __FILE__, __PRETTY_FUNCTION__,
+       source_str, group_str);
+     }
+   }
+}
+
 DEFUN (clear_ip_mroute,
        clear_ip_mroute_cmd,
        "clear ip mroute",
@@ -2133,15 +2172,17 @@ static void show_mroute(struct vty *vty)
 {
   struct listnode    *node;
   struct channel_oil *c_oil;
+  struct static_route *s_route;
   time_t              now;
 
-  vty_out(vty, "Proto: I=IGMP P=PIM%s%s", VTY_NEWLINE, VTY_NEWLINE);
+  vty_out(vty, "Proto: I=IGMP P=PIM S=STATIC%s%s", VTY_NEWLINE, VTY_NEWLINE);
   
   vty_out(vty, "Source          Group           Proto Input iVifI Output oVifI 
TTL Uptime  %s",
          VTY_NEWLINE);
 
   now = pim_time_monotonic_sec();
 
+  /* print list of PIM and IGMP routes */
   for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
     char group_str[100]; 
     char source_str[100];
@@ -2187,6 +2228,48 @@ static void show_mroute(struct vty *vty)
              VTY_NEWLINE);
     }
   }
+
+  /* Print list of static routes */
+  for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+    char group_str[100];
+    char source_str[100];
+    int oif_vif_index;
+
+    pim_inet4_dump("<group?>", s_route->group, group_str, sizeof(group_str));
+    pim_inet4_dump("<source?>", s_route->source, source_str, 
sizeof(source_str));
+
+    for (oif_vif_index = 0; oif_vif_index < MAXVIFS; ++oif_vif_index) {
+      struct interface *ifp_in;
+      struct interface *ifp_out;
+      char oif_uptime[10];
+      int ttl;
+      char proto[5];
+
+      ttl = s_route->oif_ttls[oif_vif_index];
+      if (ttl < 1)
+         continue;
+
+      ifp_in  = pim_if_find_by_vif_index(s_route->iif);
+      ifp_out = pim_if_find_by_vif_index(oif_vif_index);
+
+      pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - 
s_route->creation[oif_vif_index]);
+
+      proto[0] = '\0';
+      strcat(proto, "S");
+
+      vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s",
+         source_str,
+         group_str,
+         proto,
+         ifp_in ? ifp_in->name : "<iif?>",
+         s_route->iif,
+         ifp_out ? ifp_out->name : "<oif?>",
+         oif_vif_index,
+         ttl,
+         oif_uptime,
+         VTY_NEWLINE);
+    }
+  }
 }
 
 DEFUN (show_ip_mroute,
@@ -2204,12 +2287,14 @@ static void show_mroute_count(struct vty *vty)
 {
   struct listnode    *node;
   struct channel_oil *c_oil;
+  struct static_route *s_route;
 
   vty_out(vty, "%s", VTY_NEWLINE);
   
   vty_out(vty, "Source          Group           Packets      Bytes WrongIf  
%s",
          VTY_NEWLINE);
 
+  /* Print PIM and IGMP route counts */
   for (ALL_LIST_ELEMENTS_RO(qpim_channel_oil_list, node, c_oil)) {
     char group_str[100]; 
     char source_str[100];
@@ -2242,7 +2327,41 @@ static void show_mroute_count(struct vty *vty)
            sgreq.bytecnt,
            sgreq.wrong_if,
            VTY_NEWLINE);
+  }
+
+   /* Print static route counts */
+  for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
+    char group_str[100];
+    char source_str[100];
+    struct sioc_sg_req sgreq;
+
+    memset(&sgreq, 0, sizeof(sgreq));
+    sgreq.src = s_route->mc.mfcc_origin;
+    sgreq.grp = s_route->mc.mfcc_mcastgrp;
+
+    pim_inet4_dump("<group?>", s_route->mc.mfcc_mcastgrp, group_str, 
sizeof(group_str));
+    pim_inet4_dump("<source?>", s_route->mc.mfcc_origin, source_str, 
sizeof(source_str));
+
+    if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) {
+      int e = errno;
+      vty_out(vty,
+         "ioctl(SIOCGETSGCNT=%d) failure for (S,G)=(%s,%s): errno=%d: %s%s",
+         SIOCGETSGCNT,
+         source_str,
+         group_str,
+         e,
+         safe_strerror(e),
+         VTY_NEWLINE);
+      continue;
+    }
 
+    vty_out(vty, "%-15s %-15s %7ld %10ld %7ld %s",
+       source_str,
+       group_str,
+       sgreq.pktcnt,
+       sgreq.bytecnt,
+       sgreq.wrong_if,
+       VTY_NEWLINE);
   }
 }
 
@@ -2365,6 +2484,7 @@ DEFUN (ip_multicast_routing,
   pim_mroute_socket_enable();
   pim_if_add_vif_all();
   mroute_add_all();
+  static_mroute_add_all();
   return CMD_SUCCESS;
 }
 
@@ -2377,6 +2497,7 @@ DEFUN (no_ip_multicast_routing,
        "Enable IP multicast forwarding\n")
 {
   mroute_del_all();
+  static_mroute_del_all();
   pim_if_del_vif_all();
   pim_mroute_socket_disable();
   return CMD_SUCCESS;
@@ -3071,6 +3192,200 @@ DEFUN (interface_no_ip_pim_ssm,
   return CMD_SUCCESS;
 }
 
+DEFUN (interface_ip_mroute,
+       interface_ip_mroute_cmd,
+       "ip mroute INTERFACE A.B.C.D",
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_addr.s_addr = INADDR_ANY;
+
+   if (pim_static_add(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to add route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_ip_mroute_source,
+       interface_ip_mroute_source_cmd,
+       "ip mroute INTERFACE A.B.C.D A.B.C.D",
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group address\n"
+       "Source address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   const char       *src_str;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_str = argv[2];
+   result = inet_pton(AF_INET, src_str, &src_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+        src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   if (pim_static_add(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to add route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_mroute,
+       interface_no_ip_mroute_cmd,
+       "no ip mroute INTERFACE A.B.C.D",
+       NO_STR
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group Address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_addr.s_addr = INADDR_ANY;
+
+   if (pim_static_del(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to remove route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
+DEFUN (interface_no_ip_mroute_source,
+       interface_no_ip_mroute_source_cmd,
+       "no ip mroute INTERFACE A.B.C.D A.B.C.D",
+       NO_STR
+       IP_STR
+       "Add multicast route\n"
+       "Outgoing interface name\n"
+       "Group Address\n"
+       "Source Address\n")
+{
+   struct interface *iif;
+   struct interface *oif;
+   const char       *oifname;
+   const char       *grp_str;
+   struct in_addr    grp_addr;
+   const char       *src_str;
+   struct in_addr    src_addr;
+   int               result;
+
+   iif = vty->index;
+
+   oifname = argv[0];
+   oif = if_lookup_by_name(oifname);
+   if (!oif) {
+     vty_out(vty, "No such interface name %s%s",
+        oifname, VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   grp_str = argv[1];
+   result = inet_pton(AF_INET, grp_str, &grp_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad group address %s: errno=%d: %s%s",
+        grp_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   src_str = argv[2];
+   result = inet_pton(AF_INET, src_str, &src_addr);
+   if (result <= 0) {
+     vty_out(vty, "Bad source address %s: errno=%d: %s%s",
+        src_str, errno, safe_strerror(errno), VTY_NEWLINE);
+     return CMD_WARNING;
+   }
+
+   if (pim_static_del(iif, oif, grp_addr, src_addr)) {
+      vty_out(vty, "Failed to remove route%s", VTY_NEWLINE);
+      return CMD_WARNING;
+   }
+
+   return CMD_SUCCESS;
+}
+
 DEFUN (debug_igmp,
        debug_igmp_cmd,
        "debug igmp",
@@ -3219,6 +3534,33 @@ ALIAS (no_debug_mroute,
        UNDEBUG_STR
        DEBUG_MROUTE_STR)
 
+DEFUN (debug_static,
+       debug_static_cmd,
+       "debug static",
+       DEBUG_STR
+       DEBUG_STATIC_STR)
+{
+  PIM_DO_DEBUG_STATIC;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_static,
+       no_debug_static_cmd,
+       "no debug static",
+       NO_STR
+       DEBUG_STR
+       DEBUG_STATIC_STR)
+{
+  PIM_DONT_DEBUG_STATIC;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_debug_static,
+       undebug_static_cmd,
+       "undebug static",
+       UNDEBUG_STR
+       DEBUG_STATIC_STR)
+
 DEFUN (debug_pim,
        debug_pim_cmd,
        "debug pim",
@@ -4343,6 +4685,12 @@ void pim_cmd_init()
   install_element (INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
   install_element (INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd); 
 
+  // Static mroutes NEB
+  install_element (INTERFACE_NODE, &interface_ip_mroute_cmd);
+  install_element (INTERFACE_NODE, &interface_ip_mroute_source_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_mroute_cmd);
+  install_element (INTERFACE_NODE, &interface_no_ip_mroute_source_cmd);
+
   install_element (VIEW_NODE, &show_ip_igmp_interface_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_join_cmd);
   install_element (VIEW_NODE, &show_ip_igmp_parameters_cmd);
@@ -4436,6 +4784,8 @@ void pim_cmd_init()
   install_element (ENABLE_NODE, &undebug_igmp_trace_cmd);
   install_element (ENABLE_NODE, &debug_mroute_cmd);
   install_element (ENABLE_NODE, &no_debug_mroute_cmd);
+  install_element (ENABLE_NODE, &debug_static_cmd);
+  install_element (ENABLE_NODE, &no_debug_static_cmd);
   install_element (ENABLE_NODE, &debug_pim_cmd);
   install_element (ENABLE_NODE, &no_debug_pim_cmd);
   install_element (ENABLE_NODE, &undebug_pim_cmd);
@@ -4477,6 +4827,8 @@ void pim_cmd_init()
   install_element (CONFIG_NODE, &undebug_igmp_trace_cmd);
   install_element (CONFIG_NODE, &debug_mroute_cmd);
   install_element (CONFIG_NODE, &no_debug_mroute_cmd);
+  install_element (CONFIG_NODE, &debug_static_cmd);
+  install_element (CONFIG_NODE, &no_debug_static_cmd);
   install_element (CONFIG_NODE, &debug_pim_cmd);
   install_element (CONFIG_NODE, &no_debug_pim_cmd);
   install_element (CONFIG_NODE, &undebug_pim_cmd);
diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h
index c503740..25e2444 100644
--- a/pimd/pim_cmd.h
+++ b/pimd/pim_cmd.h
@@ -39,6 +39,7 @@
 #define DEBUG_IGMP_PACKETS_STR                      "IGMP protocol packets\n"
 #define DEBUG_IGMP_TRACE_STR                        "IGMP internal daemon 
activity\n"
 #define DEBUG_MROUTE_STR                            "PIM interaction with 
kernel MFC cache\n"
+#define DEBUG_STATIC_STR                            "PIM Static Multicast 
Route activity\n"
 #define DEBUG_PIM_STR                               "PIM protocol activity\n"
 #define DEBUG_PIM_EVENTS_STR                        "PIM protocol events\n"
 #define DEBUG_PIM_PACKETS_STR                       "PIM protocol packets\n"
diff --git a/pimd/pimd.c b/pimd/pimd.c
index 78c3ff5..44012d0 100644
--- a/pimd/pimd.c
+++ b/pimd/pimd.c
@@ -37,6 +37,7 @@
 #include "pim_rand.h"
 #include "pim_rpf.h"
 #include "pim_ssmpingd.h"
+#include "pim_static.h"
 
 const char *const PIM_ALL_SYSTEMS      = MCAST_ALL_SYSTEMS;
 const char *const PIM_ALL_ROUTERS      = MCAST_ALL_ROUTERS;
@@ -69,6 +70,7 @@ int64_t                   qpim_mroute_add_events = 0;
 int64_t                   qpim_mroute_add_last = 0;
 int64_t                   qpim_mroute_del_events = 0;
 int64_t                   qpim_mroute_del_last = 0;
+struct list              *qpim_static_route_list = 0;
 
 static void pim_free()
 {
@@ -79,6 +81,9 @@ static void pim_free()
 
   if (qpim_upstream_list)
     list_free(qpim_upstream_list);
+
+  if (qpim_static_route_list)
+     list_free(qpim_static_route_list);
 }
 
 void pim_init()
@@ -110,6 +115,14 @@ void pim_init()
   }
   qpim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
 
+  qpim_static_route_list = list_new();
+  if (!qpim_static_route_list) {
+    zlog_err("%s %s: failure: static_route_list=list_new()",
+        __FILE__, __PRETTY_FUNCTION__);
+    return;
+  }
+  qpim_static_route_list->del = (void (*)(void *)) pim_static_route_free;
+
   qpim_mroute_socket_fd = -1; /* mark mroute as disabled */
   qpim_mroute_oif_highest_vif_index = -1;
 
diff --git a/pimd/pimd.h b/pimd/pimd.h
index 22a2922..aed26be 100644
--- a/pimd/pimd.h
+++ b/pimd/pimd.h
@@ -64,8 +64,9 @@
 #define PIM_MASK_ZEBRA               (1 << 8)
 #define PIM_MASK_SSMPINGD            (1 << 9)
 #define PIM_MASK_MROUTE              (1 << 10)
-#define PIM_MASK_PIM_HELLO          (1 << 11)
-#define PIM_MASK_PIM_J_P            (1 << 12)
+#define PIM_MASK_PIM_HELLO           (1 << 11)
+#define PIM_MASK_PIM_J_P             (1 << 12)
+#define PIM_MASK_STATIC              (1 << 13)
 
 const char *const PIM_ALL_SYSTEMS;
 const char *const PIM_ALL_ROUTERS;
@@ -99,6 +100,7 @@ int64_t                   qpim_mroute_add_events;
 int64_t                   qpim_mroute_add_last;
 int64_t                   qpim_mroute_del_events;
 int64_t                   qpim_mroute_del_last;
+struct list              *qpim_static_route_list; /* list of routes added 
statically */
 
 #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2)
 
@@ -116,8 +118,9 @@ int64_t                   qpim_mroute_del_last;
 #define PIM_DEBUG_ZEBRA               (qpim_debugs & PIM_MASK_ZEBRA)
 #define PIM_DEBUG_SSMPINGD            (qpim_debugs & PIM_MASK_SSMPINGD)
 #define PIM_DEBUG_MROUTE              (qpim_debugs & PIM_MASK_MROUTE)
-#define PIM_DEBUG_PIM_HELLO          (qpim_debugs & PIM_MASK_PIM_HELLO)
-#define PIM_DEBUG_PIM_J_P            (qpim_debugs & PIM_MASK_PIM_J_P)
+#define PIM_DEBUG_PIM_HELLO           (qpim_debugs & PIM_MASK_PIM_HELLO)
+#define PIM_DEBUG_PIM_J_P             (qpim_debugs & PIM_MASK_PIM_J_P)
+#define PIM_DEBUG_STATIC              (qpim_debugs & PIM_MASK_STATIC)
 
 #define PIM_DEBUG_EVENTS       (qpim_debugs & (PIM_MASK_PIM_EVENTS | 
PIM_MASK_IGMP_EVENTS))
 #define PIM_DEBUG_PACKETS      (qpim_debugs & (PIM_MASK_PIM_PACKETS | 
PIM_MASK_IGMP_PACKETS))
@@ -136,6 +139,7 @@ int64_t                   qpim_mroute_del_last;
 #define PIM_DO_DEBUG_MROUTE              (qpim_debugs |= PIM_MASK_MROUTE)
 #define PIM_DO_DEBUG_PIM_HELLO           (qpim_debugs |= PIM_MASK_PIM_HELLO)
 #define PIM_DO_DEBUG_PIM_J_P             (qpim_debugs |= PIM_MASK_PIM_J_P)
+#define PIM_DO_DEBUG_STATIC              (qpim_debugs |= PIM_MASK_STATIC)
 
 #define PIM_DONT_DEBUG_PIM_EVENTS          (qpim_debugs &= 
~PIM_MASK_PIM_EVENTS)
 #define PIM_DONT_DEBUG_PIM_PACKETS         (qpim_debugs &= 
~PIM_MASK_PIM_PACKETS)
@@ -150,6 +154,7 @@ int64_t                   qpim_mroute_del_last;
 #define PIM_DONT_DEBUG_MROUTE              (qpim_debugs &= ~PIM_MASK_MROUTE)
 #define PIM_DONT_DEBUG_PIM_HELLO           (qpim_debugs &= ~PIM_MASK_PIM_HELLO)
 #define PIM_DONT_DEBUG_PIM_J_P             (qpim_debugs &= ~PIM_MASK_PIM_J_P)
+#define PIM_DONT_DEBUG_STATIC              (qpim_debugs &= ~PIM_MASK_STATIC)
 
 void pim_init(void);
 void pim_terminate(void);
/*
  PIM for Quagga: add the ability to configure multicast static routes
  Copyright (C) 2014  Nathan Bahr, ATCorp

  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$ $
*/

#ifndef PIM_STATIC_H_
#define PIM_STATIC_H_

#include <zebra.h>
#include "pim_mroute.h"
#include "if.h"

struct static_route {
   /* Each static route is unique by these pair of addresses */
   struct in_addr group;
   struct in_addr source;

   unsigned int   iif;
   unsigned char  oif_ttls[MAXVIFS];
   int            oif_count;
   struct mfcctl  mc;
   time_t         creation[MAXVIFS];
};

void pim_static_route_free(struct static_route *s_route);

int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr 
group, struct in_addr source);
int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr 
group, struct in_addr source);

#endif /* PIM_STATIC_H_ */
/*
  PIM for Quagga: add the ability to configure multicast static routes
  Copyright (C) 2014  Nathan Bahr, ATCorp

  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 "pim_static.h"
#include "pim_time.h"
#include "pim_str.h"
#include "pimd.h"
#include "pim_iface.h"
#include "log.h"
#include "memory.h"
#include "linklist.h"

void pim_static_route_free(struct static_route *s_route)
{
  XFREE(MTYPE_PIM_STATIC_ROUTE, s_route);
}

static struct static_route * static_route_alloc()
{
   struct static_route *s_route;

   s_route = XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(*s_route));
   if (!s_route) {
     zlog_err("PIM XCALLOC(%lu) failure", sizeof(*s_route));
     return 0;
   }
   return s_route;
}

static struct static_route *static_route_new(unsigned int   iif,
                                             unsigned int   oif,
                                             struct in_addr group,
                                             struct in_addr source)
{
  struct static_route * s_route;
  s_route = static_route_alloc();
  if (!s_route) {
     return 0;
  }

  s_route->group             = group;
  s_route->source            = source;
  s_route->iif               = iif;
  s_route->oif_ttls[oif]     = 1;
  s_route->oif_count         = 1;
  s_route->mc.mfcc_origin    = source;
  s_route->mc.mfcc_mcastgrp  = group;
  s_route->mc.mfcc_parent    = iif;
  s_route->mc.mfcc_ttls[oif] = 1;
  s_route->creation[oif] = pim_time_monotonic_sec();

  return s_route;
}


int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
{
   struct listnode *node = 0;
   struct static_route *s_route = 0;
   struct static_route *original_s_route = 0;
   struct pim_interface *pim_iif = iif ? iif->info : 0;
   struct pim_interface *pim_oif = oif ? oif->info : 0;
   unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
   unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;

   if (!iif_index || !oif_index) {
      zlog_warn("%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)",
               __FILE__, __PRETTY_FUNCTION__,
               iif_index,
               oif_index);
      return -2;
   }

#ifdef PIM_ENFORCE_LOOPFREE_MFC
   if (iif_index == oif_index) {
      /* looped MFC entry */
      zlog_warn("%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)",
               __FILE__, __PRETTY_FUNCTION__,
               iif_index,
               oif_index);
      return -4;
   }
#endif

   for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) {
      if (s_route->group.s_addr == group.s_addr &&
          s_route->source.s_addr == source.s_addr) {
         if (s_route->iif == iif_index &&
             s_route->oif_ttls[oif_index]) {
            char gifaddr_str[100];
            char sifaddr_str[100];
            pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
            pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
            zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)",
                     __FILE__, __PRETTY_FUNCTION__,
                     iif_index,
                     oif_index,
                     gifaddr_str,
                     sifaddr_str);
            return -3;
         }

         /* Ok, from here on out we will be making changes to the s_route structure, but if
          * for some reason we fail to commit these changes to the kernel, we want to be able
          * restore the state of the list. So copy the node data and if need be, we can copy
          * back if it fails.
          */
         original_s_route = static_route_alloc();
         if (!original_s_route) {
            return -5;
         }
         memcpy(original_s_route, s_route, sizeof(struct static_route));

         /* Route exists and has the same input interface, but adding a new output interface */
         if (s_route->iif == iif_index) {
            s_route->oif_ttls[oif_index] = 1;
            s_route->mc.mfcc_ttls[oif_index] = 1;
            s_route->creation[oif_index] = pim_time_monotonic_sec();
            ++s_route->oif_count;
         } else {
            /* input interface changed */
            s_route->iif = iif_index;
            s_route->mc.mfcc_parent = iif_index;

#ifdef PIM_ENFORCE_LOOPFREE_MFC
            /* check to make sure the new input was not an old output */
            if (s_route->oif_ttls[iif_index]) {
               s_route->oif_ttls[iif_index] = 0;
               s_route->creation[iif_index] = 0;
               s_route->mc.mfcc_ttls[iif_index] = 0;
               --s_route->oif_count;
            }
#endif

            /* now add the new output, if it is new */
            if (!s_route->oif_ttls[oif_index]) {
               s_route->oif_ttls[oif_index] = 1;
               s_route->creation[oif_index] = pim_time_monotonic_sec();
               s_route->mc.mfcc_ttls[oif_index] = 1;
               ++s_route->oif_count;
            }
         }

         break;
      }
   }

   /* If node is null then we reached the end of the list without finding a match */
   if (!node) {
      s_route = static_route_new(iif_index, oif_index, group, source);
      listnode_add(qpim_static_route_list, s_route);
   }

   if (pim_mroute_add(&(s_route->mc)))
   {
      char gifaddr_str[100];
      char sifaddr_str[100];
      pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
      pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
      zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)",
               __FILE__, __PRETTY_FUNCTION__,
               iif_index,
               oif_index,
               gifaddr_str,
               sifaddr_str);

      /* Need to put s_route back to the way it was */
      if (original_s_route) {
         memcpy(s_route, original_s_route, sizeof(struct static_route));
      } else {
         /* we never stored off a copy, so it must have been a fresh new route */
         listnode_delete(qpim_static_route_list, s_route);
         pim_static_route_free(s_route);
      }

      return -1;
   }

   /* Make sure we free the memory for the route copy if used */
   if (original_s_route) {
      pim_static_route_free(original_s_route);
   }

   if (PIM_DEBUG_STATIC) {
     char gifaddr_str[100];
     char sifaddr_str[100];
     pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
     pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
     zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)",
           __PRETTY_FUNCTION__,
           iif_index,
           oif_index,
           gifaddr_str,
           sifaddr_str);
   }

   return 0;
}

int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source)
{
   struct listnode *node = 0;
   struct listnode *nextnode = 0;
   struct static_route *s_route = 0;
   struct pim_interface *pim_iif = iif ? iif->info : 0;
   struct pim_interface *pim_oif = oif ? oif->info : 0;
   unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
   unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0;

   if (!iif_index || !oif_index) {
      zlog_warn("%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)",
               __FILE__, __PRETTY_FUNCTION__,
               iif_index,
               oif_index);
      return -2;
   }

   for (ALL_LIST_ELEMENTS(qpim_static_route_list, node, nextnode, s_route)) {
      if (s_route->iif == iif_index &&
          s_route->group.s_addr == group.s_addr &&
          s_route->source.s_addr == source.s_addr &&
          s_route->oif_ttls[oif_index]) {
         s_route->oif_ttls[oif_index] = 0;
         s_route->mc.mfcc_ttls[oif_index] = 0;
         --s_route->oif_count;

         /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */
         if (s_route->oif_count <= 0 ? pim_mroute_del(&s_route->mc) : pim_mroute_add(&s_route->mc)) {
            char gifaddr_str[100];
            char sifaddr_str[100];
            pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
            pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
            zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)",
                     __FILE__, __PRETTY_FUNCTION__,
                     iif_index,
                     oif_index,
                     gifaddr_str,
                     sifaddr_str);

            s_route->oif_ttls[oif_index] = 1;
            s_route->mc.mfcc_ttls[oif_index] = 1;
            ++s_route->oif_count;

            return -1;
         }

         s_route->creation[oif_index] = 0;

         if (s_route->oif_count <= 0) {
            listnode_delete(qpim_static_route_list, s_route);
            pim_static_route_free(s_route);
         }

         if (PIM_DEBUG_STATIC) {
           char gifaddr_str[100];
           char sifaddr_str[100];
           pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
           pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
           zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)",
                 __PRETTY_FUNCTION__,
                 iif_index,
                 oif_index,
                 gifaddr_str,
                 sifaddr_str);
         }

         break;
      }
   }

   if (!node) {
      char gifaddr_str[100];
      char sifaddr_str[100];
      pim_inet4_dump("<ifaddr?>", group, gifaddr_str, sizeof(gifaddr_str));
      pim_inet4_dump("<ifaddr?>", source, sifaddr_str, sizeof(sifaddr_str));
      zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)",
               __FILE__, __PRETTY_FUNCTION__,
               iif_index,
               oif_index,
               gifaddr_str,
               sifaddr_str);
      return -3;
   }

   return 0;
}
_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to