blob: 3ab5011bc76b9737fc748d78fcba7ae956f6eab8 [file] [log] [blame]
/*
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 <zebra.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;
}