diff options
-rw-r--r-- | lib/memtypes.c | 1 | ||||
-rw-r--r-- | pimd/Makefile.am | 4 | ||||
-rw-r--r-- | pimd/pim_cmd.c | 354 | ||||
-rw-r--r-- | pimd/pim_cmd.h | 1 | ||||
-rw-r--r-- | pimd/pim_static.c | 305 | ||||
-rw-r--r-- | pimd/pim_static.h | 47 | ||||
-rw-r--r-- | pimd/pimd.c | 13 | ||||
-rw-r--r-- | pimd/pimd.h | 13 |
8 files changed, 731 insertions, 7 deletions
diff --git a/lib/memtypes.c b/lib/memtypes.c index ab3c1f8e..57de5c4f 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 cb525f7b..bf8a158f 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_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_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 a5d11b9f..10d0c86a 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", @@ -4344,6 +4686,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); @@ -4437,6 +4785,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); @@ -4478,6 +4828,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 c5037400..25e24446 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/pim_static.c b/pimd/pim_static.c new file mode 100644 index 00000000..f2b8e856 --- /dev/null +++ b/pimd/pim_static.c @@ -0,0 +1,305 @@ +/* + 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; +} diff --git a/pimd/pim_static.h b/pimd/pim_static.h new file mode 100644 index 00000000..3a096932 --- /dev/null +++ b/pimd/pim_static.h @@ -0,0 +1,47 @@ +/* + 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_ */ diff --git a/pimd/pimd.c b/pimd/pimd.c index 3797d4fc..97fb2233 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -36,6 +36,7 @@ #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; @@ -68,6 +69,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() { @@ -78,6 +80,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() @@ -109,6 +114,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 22a29220..aed26bea 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); |