pimd: add support for configuring multicast static routes

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

Acked-by: Donald Sharp <sharpd@cumulusnetworks.com>
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index a5d11b9..10d0c86 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 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 @@
 {
   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 @@
 	      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 @@
 {
   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 @@
 	    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 @@
   pim_mroute_socket_enable();
   pim_if_add_vif_all();
   mroute_add_all();
+  static_mroute_add_all();
   return CMD_SUCCESS;
 }
 
@@ -2377,6 +2497,7 @@
        "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 @@
   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 @@
        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",
@@ -4344,6 +4686,12 @@
   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);
@@ -4437,6 +4785,8 @@
   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);
@@ -4478,6 +4828,8 @@
   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);