{ 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 },
};
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_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 \
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_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)
#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,
}
}
+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",
{
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];
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,
{
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];
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);
}
}
pim_mroute_socket_enable();
pim_if_add_vif_all();
mroute_add_all();
+ static_mroute_add_all();
return CMD_SUCCESS;
}
"Enable IP multicast forwarding\n")
{
mroute_del_all();
+ static_mroute_del_all();
pim_if_del_vif_all();
pim_mroute_socket_disable();
return CMD_SUCCESS;
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",
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",
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);
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);
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);
#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"
--- /dev/null
+/*
+ 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;
+}
--- /dev/null
+/*
+ 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_ */
#include "pim_upstream.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;
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()
{
if (qpim_upstream_list)
list_free(qpim_upstream_list);
+
+ if (qpim_static_route_list)
+ list_free(qpim_static_route_list);
}
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;
#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;
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)
#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))
#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)
#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);