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_static.c b/pimd/pim_static.c
new file mode 100644
index 0000000..f2b8e85
--- /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;
+}