From: Jafar Al-Gharaibeh Date: Thu, 11 Jun 2015 23:29:02 +0000 (-0500) Subject: pimd: add support for configuring multicast static routes X-Git-Tag: frr-2.0-rc1~859 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=6250610a59397c2a54e4fa5f66a74a2e61aaa914;p=mirror%2Ffrr.git 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 ip mroute # for asm or ip mroute # for ssm Please let me know if you have any questions or concerns, Regards, Jafar Acked-by: Donald Sharp --- diff --git a/lib/memtypes.c b/lib/memtypes.c index 65f32a8495..d0db1bc996 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -283,6 +283,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 bb2f23d121..3a6fb34ea8 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 996afe2ca4..c5889d0b5f 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("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", 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("", s_route->group, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 : "", + s_route->iif, + ifp_out ? ifp_out->name : "", + 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("", s_route->mc.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", 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 c503740090..25e2444624 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 0000000000..f2b8e856b5 --- /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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", 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 0000000000..3a096932d2 --- /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 +#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 07dc179939..fa186eb716 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -35,6 +35,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; @@ -67,6 +68,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() { @@ -77,6 +79,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() @@ -108,6 +113,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 a34d951fa7..8b58a15526 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);