mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 08:15:19 +00:00 
			
		
		
		
	The FSF's address changed, and we had a mixture of comment styles for the GPL file header. (The style with * at the beginning won out with 580 to 141 in existing files.) Note: I've intentionally left intact other "variations" of the copyright header, e.g. whether it says "Zebra", "Quagga", "FRR", or nothing. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
		
			
				
	
	
		
			350 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "vty.h"
 | 
						|
#include "if.h"
 | 
						|
#include "log.h"
 | 
						|
#include "memory.h"
 | 
						|
#include "linklist.h"
 | 
						|
 | 
						|
#include "pimd.h"
 | 
						|
#include "pim_oil.h"
 | 
						|
#include "pim_static.h"
 | 
						|
#include "pim_time.h"
 | 
						|
#include "pim_str.h"
 | 
						|
#include "pim_iface.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(%zu) 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->c_oil.oil_ref_count         = 1;
 | 
						|
  s_route->c_oil.oil.mfcc_origin    = source;
 | 
						|
  s_route->c_oil.oil.mfcc_mcastgrp  = group;
 | 
						|
  s_route->c_oil.oil.mfcc_parent    = iif;
 | 
						|
  s_route->c_oil.oil.mfcc_ttls[oif] = 1;
 | 
						|
  s_route->c_oil.oif_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 = NULL;
 | 
						|
   struct static_route *s_route = NULL;
 | 
						|
   struct static_route *original_s_route = NULL;
 | 
						|
   struct pim_interface *pim_iif = iif ? iif->info : NULL;
 | 
						|
   struct pim_interface *pim_oif = oif ? oif->info : NULL;
 | 
						|
   ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
 | 
						|
   ifindex_t 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[INET_ADDRSTRLEN];
 | 
						|
            char sifaddr_str[INET_ADDRSTRLEN];
 | 
						|
            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->c_oil.oil.mfcc_ttls[oif_index] = 1;
 | 
						|
            s_route->c_oil.oif_creation[oif_index] = pim_time_monotonic_sec();
 | 
						|
            ++s_route->c_oil.oil_ref_count;
 | 
						|
         } else {
 | 
						|
            /* input interface changed */
 | 
						|
            s_route->iif = iif_index;
 | 
						|
            s_route->c_oil.oil.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->c_oil.oif_creation[iif_index] = 0;
 | 
						|
               s_route->c_oil.oil.mfcc_ttls[iif_index] = 0;
 | 
						|
               --s_route->c_oil.oil_ref_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->c_oil.oif_creation[oif_index] = pim_time_monotonic_sec();
 | 
						|
               s_route->c_oil.oil.mfcc_ttls[oif_index] = 1;
 | 
						|
               ++s_route->c_oil.oil_ref_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->c_oil, __PRETTY_FUNCTION__))
 | 
						|
   {
 | 
						|
      char gifaddr_str[INET_ADDRSTRLEN];
 | 
						|
      char sifaddr_str[INET_ADDRSTRLEN];
 | 
						|
      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);
 | 
						|
      }
 | 
						|
 | 
						|
      if (original_s_route) {
 | 
						|
         pim_static_route_free(original_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[INET_ADDRSTRLEN];
 | 
						|
     char sifaddr_str[INET_ADDRSTRLEN];
 | 
						|
     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 = NULL;
 | 
						|
   struct listnode *nextnode = NULL;
 | 
						|
   struct static_route *s_route = NULL;
 | 
						|
   struct pim_interface *pim_iif = iif ? iif->info : 0;
 | 
						|
   struct pim_interface *pim_oif = oif ? oif->info : 0;
 | 
						|
   ifindex_t iif_index = pim_iif ? pim_iif->mroute_vif_index : 0;
 | 
						|
   ifindex_t 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->c_oil.oil.mfcc_ttls[oif_index] = 0;
 | 
						|
         --s_route->c_oil.oil_ref_count;
 | 
						|
 | 
						|
         /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */
 | 
						|
         if (s_route->c_oil.oil_ref_count <= 0 ?
 | 
						|
	     pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__) : pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
 | 
						|
	   char gifaddr_str[INET_ADDRSTRLEN];
 | 
						|
	   char sifaddr_str[INET_ADDRSTRLEN];
 | 
						|
	   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->c_oil.oil.mfcc_ttls[oif_index] = 1;
 | 
						|
	   ++s_route->c_oil.oil_ref_count;
 | 
						|
 | 
						|
	   return -1;
 | 
						|
         }
 | 
						|
 | 
						|
         s_route->c_oil.oif_creation[oif_index] = 0;
 | 
						|
 | 
						|
         if (s_route->c_oil.oil_ref_count <= 0) {
 | 
						|
            listnode_delete(qpim_static_route_list, s_route);
 | 
						|
            pim_static_route_free(s_route);
 | 
						|
         }
 | 
						|
 | 
						|
         if (PIM_DEBUG_STATIC) {
 | 
						|
           char gifaddr_str[INET_ADDRSTRLEN];
 | 
						|
           char sifaddr_str[INET_ADDRSTRLEN];
 | 
						|
           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[INET_ADDRSTRLEN];
 | 
						|
      char sifaddr_str[INET_ADDRSTRLEN];
 | 
						|
      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;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
pim_static_write_mroute (struct vty *vty, struct interface *ifp)
 | 
						|
{
 | 
						|
  struct pim_interface *pim_ifp = ifp->info;
 | 
						|
  struct listnode *node;
 | 
						|
  struct static_route *sroute;
 | 
						|
  int count = 0;
 | 
						|
  char sbuf[INET_ADDRSTRLEN];
 | 
						|
  char gbuf[INET_ADDRSTRLEN];
 | 
						|
 | 
						|
  if (!pim_ifp)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  for (ALL_LIST_ELEMENTS_RO (qpim_static_route_list, node, sroute))
 | 
						|
    {
 | 
						|
      pim_inet4_dump ("<ifaddr?>", sroute->group, gbuf, sizeof (gbuf));
 | 
						|
      pim_inet4_dump ("<ifaddr?>", sroute->source, sbuf, sizeof (sbuf));
 | 
						|
      if (sroute->iif == pim_ifp->mroute_vif_index)
 | 
						|
        {
 | 
						|
          int i;
 | 
						|
          for (i = 0; i < MAXVIFS; i++)
 | 
						|
            if (sroute->oif_ttls[i])
 | 
						|
              {
 | 
						|
                struct interface *oifp = pim_if_find_by_vif_index (i);
 | 
						|
                if (sroute->source.s_addr == 0)
 | 
						|
                  vty_out (vty, " ip mroute %s %s%s", oifp->name, gbuf, VTY_NEWLINE);
 | 
						|
                else
 | 
						|
                  vty_out (vty, " ip mroute %s %s %s%s", oifp->name, gbuf, sbuf, VTY_NEWLINE);
 | 
						|
                count ++;
 | 
						|
              }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  return count;
 | 
						|
}
 |