mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 10:07:04 +00:00 
			
		
		
		
	Quagga sources have inherited a slew of Page Feed (^L, \xC) characters from ancient history. Among other things, these break patchwork's XML-RPC API because \xC is not a valid character in XML documents. Nuke them from high orbit. Patches can be adapted simply by: sed -e 's%^L%%' -i filename.patch (you can type page feeds in some environments with Ctrl-V Ctrl-L) Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
		
			
				
	
	
		
			529 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			529 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * kernel routing table update by ioctl().
 | 
						|
 * Copyright (C) 1997, 98 Kunihiro Ishiguro
 | 
						|
 *
 | 
						|
 * This file is part of GNU Zebra.
 | 
						|
 *
 | 
						|
 * GNU Zebra 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, or (at your option) any
 | 
						|
 * later version.
 | 
						|
 *
 | 
						|
 * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the Free
 | 
						|
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
						|
 * 02111-1307, USA.  
 | 
						|
 */
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include "prefix.h"
 | 
						|
#include "log.h"
 | 
						|
#include "if.h"
 | 
						|
 | 
						|
#include "zebra/zserv.h"
 | 
						|
#include "zebra/rib.h"
 | 
						|
#include "zebra/debug.h"
 | 
						|
#include "zebra/rt.h"
 | 
						|
 | 
						|
/* Initialize of kernel interface.  There is no kernel communication
 | 
						|
   support under ioctl().  So this is dummy stub function. */
 | 
						|
void
 | 
						|
kernel_init (void)
 | 
						|
{
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
/* Dummy function of routing socket. */
 | 
						|
static void
 | 
						|
kernel_read (int sock)
 | 
						|
{
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
/* Initialization prototype of struct sockaddr_in. */
 | 
						|
static struct sockaddr_in sin_proto =
 | 
						|
{
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
  sizeof (struct sockaddr_in), 
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
  AF_INET, 0, {0}, {0}
 | 
						|
};
 | 
						|
#endif /* 0 */
 | 
						|
 | 
						|
/* Solaris has ortentry. */
 | 
						|
#ifdef HAVE_OLD_RTENTRY
 | 
						|
#define rtentry ortentry
 | 
						|
#endif /* HAVE_OLD_RTENTRY */
 | 
						|
 | 
						|
/* Interface to ioctl route message. */
 | 
						|
int
 | 
						|
kernel_add_route (struct prefix_ipv4 *dest, struct in_addr *gate,
 | 
						|
		  int index, int flags)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  int sock;
 | 
						|
  struct rtentry rtentry;
 | 
						|
  struct sockaddr_in sin_dest, sin_mask, sin_gate;
 | 
						|
 | 
						|
  memset (&rtentry, 0, sizeof (struct rtentry));
 | 
						|
 | 
						|
  /* Make destination. */
 | 
						|
  memset (&sin_dest, 0, sizeof (struct sockaddr_in));
 | 
						|
  sin_dest.sin_family = AF_INET;
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
  sin_dest.sin_len = sizeof (struct sockaddr_in);
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
  sin_dest.sin_addr = dest->prefix;
 | 
						|
 | 
						|
  /* Make gateway. */
 | 
						|
  if (gate)
 | 
						|
    {
 | 
						|
      memset (&sin_gate, 0, sizeof (struct sockaddr_in));
 | 
						|
      sin_gate.sin_family = AF_INET;
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
      sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
      sin_gate.sin_addr = *gate;
 | 
						|
    }
 | 
						|
 | 
						|
  memset (&sin_mask, 0, sizeof (struct sockaddr_in));
 | 
						|
  sin_mask.sin_family = AF_INET;
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
      sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
  masklen2ip (dest->prefixlen, &sin_mask.sin_addr);
 | 
						|
 | 
						|
  /* Set destination address, mask and gateway.*/
 | 
						|
  memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in));
 | 
						|
  if (gate)
 | 
						|
    memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in));
 | 
						|
#ifndef SUNOS_5
 | 
						|
  memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in));
 | 
						|
#endif /* SUNOS_5 */
 | 
						|
 | 
						|
  /* Routing entry flag set. */
 | 
						|
  if (dest->prefixlen == 32)
 | 
						|
    rtentry.rt_flags |= RTF_HOST;
 | 
						|
 | 
						|
  if (gate && gate->s_addr != INADDR_ANY)
 | 
						|
    rtentry.rt_flags |= RTF_GATEWAY;
 | 
						|
 | 
						|
  rtentry.rt_flags |= RTF_UP;
 | 
						|
 | 
						|
  /* Additional flags */
 | 
						|
  rtentry.rt_flags |= flags;
 | 
						|
 | 
						|
 | 
						|
  /* For tagging route. */
 | 
						|
  /* rtentry.rt_flags |= RTF_DYNAMIC; */
 | 
						|
 | 
						|
  /* Open socket for ioctl. */
 | 
						|
  sock = socket (AF_INET, SOCK_DGRAM, 0);
 | 
						|
  if (sock < 0)
 | 
						|
    {
 | 
						|
      zlog_warn ("can't make socket\n");
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Send message by ioctl(). */
 | 
						|
  ret = ioctl (sock, SIOCADDRT, &rtentry);
 | 
						|
  if (ret < 0)
 | 
						|
    {
 | 
						|
      switch (errno) 
 | 
						|
	{
 | 
						|
	case EEXIST:
 | 
						|
	  close (sock);
 | 
						|
	  return ZEBRA_ERR_RTEXIST;
 | 
						|
	  break;
 | 
						|
	case ENETUNREACH:
 | 
						|
	  close (sock);
 | 
						|
	  return ZEBRA_ERR_RTUNREACH;
 | 
						|
	  break;
 | 
						|
	case EPERM:
 | 
						|
	  close (sock);
 | 
						|
	  return ZEBRA_ERR_EPERM;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
      close (sock);
 | 
						|
      zlog_warn ("write : %s (%d)", safe_strerror (errno), errno);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
  close (sock);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* Interface to ioctl route message. */
 | 
						|
static int
 | 
						|
kernel_ioctl_ipv4 (u_long cmd, struct prefix *p, struct rib *rib, int family)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  int sock;
 | 
						|
  struct rtentry rtentry;
 | 
						|
  struct sockaddr_in sin_dest, sin_mask, sin_gate;
 | 
						|
  struct nexthop *nexthop, *tnexthop;
 | 
						|
  int recursing;
 | 
						|
  int nexthop_num = 0;
 | 
						|
  struct interface *ifp;
 | 
						|
 | 
						|
  memset (&rtentry, 0, sizeof (struct rtentry));
 | 
						|
 | 
						|
  /* Make destination. */
 | 
						|
  memset (&sin_dest, 0, sizeof (struct sockaddr_in));
 | 
						|
  sin_dest.sin_family = AF_INET;
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
  sin_dest.sin_len = sizeof (struct sockaddr_in);
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
  sin_dest.sin_addr = p->u.prefix4;
 | 
						|
 | 
						|
  if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE))
 | 
						|
    {
 | 
						|
      SET_FLAG (rtentry.rt_flags, RTF_REJECT);
 | 
						|
 | 
						|
      if (cmd == SIOCADDRT)
 | 
						|
	for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
 | 
						|
	  {
 | 
						|
            /* We shouldn't encounter recursive nexthops on discard routes,
 | 
						|
             * but it is probably better to handle that case correctly anyway.
 | 
						|
             */
 | 
						|
	    if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
 | 
						|
	      continue;
 | 
						|
	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
 | 
						|
	  }
 | 
						|
      goto skip;
 | 
						|
    }
 | 
						|
 | 
						|
  memset (&sin_gate, 0, sizeof (struct sockaddr_in));
 | 
						|
 | 
						|
  /* Make gateway. */
 | 
						|
  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
 | 
						|
    {
 | 
						|
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
 | 
						|
        continue;
 | 
						|
 | 
						|
      if ((cmd == SIOCADDRT 
 | 
						|
	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
 | 
						|
	  || (cmd == SIOCDELRT
 | 
						|
	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
 | 
						|
	{
 | 
						|
	  if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
 | 
						|
	      nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
 | 
						|
	    {
 | 
						|
	      sin_gate.sin_family = AF_INET;
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
	      sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
	      sin_gate.sin_addr = nexthop->gate.ipv4;
 | 
						|
	      rtentry.rt_flags |= RTF_GATEWAY;
 | 
						|
	    }
 | 
						|
	  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
 | 
						|
	      || nexthop->type == NEXTHOP_TYPE_IFNAME)
 | 
						|
	    {
 | 
						|
	      ifp = if_lookup_by_index (nexthop->ifindex);
 | 
						|
	      if (ifp)
 | 
						|
	        rtentry.rt_dev = ifp->name;
 | 
						|
	      else
 | 
						|
	        return -1;
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (cmd == SIOCADDRT)
 | 
						|
	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
 | 
						|
 | 
						|
	  nexthop_num++;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* If there is no useful nexthop then return. */
 | 
						|
  if (nexthop_num == 0)
 | 
						|
    {
 | 
						|
      if (IS_ZEBRA_DEBUG_KERNEL)
 | 
						|
	zlog_debug ("netlink_route_multipath(): No useful nexthop.");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
 skip:
 | 
						|
 | 
						|
  memset (&sin_mask, 0, sizeof (struct sockaddr_in));
 | 
						|
  sin_mask.sin_family = AF_INET;
 | 
						|
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 | 
						|
  sin_mask.sin_len = sizeof (struct sockaddr_in);
 | 
						|
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 | 
						|
  masklen2ip (p->prefixlen, &sin_mask.sin_addr);
 | 
						|
 | 
						|
  /* Set destination address, mask and gateway.*/
 | 
						|
  memcpy (&rtentry.rt_dst, &sin_dest, sizeof (struct sockaddr_in));
 | 
						|
 | 
						|
  if (rtentry.rt_flags & RTF_GATEWAY)
 | 
						|
    memcpy (&rtentry.rt_gateway, &sin_gate, sizeof (struct sockaddr_in));
 | 
						|
 | 
						|
#ifndef SUNOS_5
 | 
						|
  memcpy (&rtentry.rt_genmask, &sin_mask, sizeof (struct sockaddr_in));
 | 
						|
#endif /* SUNOS_5 */
 | 
						|
 | 
						|
  /* Metric.  It seems metric minus one value is installed... */
 | 
						|
  rtentry.rt_metric = rib->metric;
 | 
						|
 | 
						|
  /* Routing entry flag set. */
 | 
						|
  if (p->prefixlen == 32)
 | 
						|
    rtentry.rt_flags |= RTF_HOST;
 | 
						|
 | 
						|
  rtentry.rt_flags |= RTF_UP;
 | 
						|
 | 
						|
  /* Additional flags */
 | 
						|
  /* rtentry.rt_flags |= flags; */
 | 
						|
 | 
						|
  /* For tagging route. */
 | 
						|
  /* rtentry.rt_flags |= RTF_DYNAMIC; */
 | 
						|
 | 
						|
  /* Open socket for ioctl. */
 | 
						|
  sock = socket (AF_INET, SOCK_DGRAM, 0);
 | 
						|
  if (sock < 0)
 | 
						|
    {
 | 
						|
      zlog_warn ("can't make socket\n");
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Send message by ioctl(). */
 | 
						|
  ret = ioctl (sock, cmd, &rtentry);
 | 
						|
  if (ret < 0)
 | 
						|
    {
 | 
						|
      switch (errno) 
 | 
						|
	{
 | 
						|
	case EEXIST:
 | 
						|
	  close (sock);
 | 
						|
	  return ZEBRA_ERR_RTEXIST;
 | 
						|
	  break;
 | 
						|
	case ENETUNREACH:
 | 
						|
	  close (sock);
 | 
						|
	  return ZEBRA_ERR_RTUNREACH;
 | 
						|
	  break;
 | 
						|
	case EPERM:
 | 
						|
	  close (sock);
 | 
						|
	  return ZEBRA_ERR_EPERM;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
 | 
						|
      close (sock);
 | 
						|
      zlog_warn ("write : %s (%d)", safe_strerror (errno), errno);
 | 
						|
      return ret;
 | 
						|
    }
 | 
						|
  close (sock);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
kernel_add_ipv4 (struct prefix *p, struct rib *rib)
 | 
						|
{
 | 
						|
  return kernel_ioctl_ipv4 (SIOCADDRT, p, rib, AF_INET);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
kernel_delete_ipv4 (struct prefix *p, struct rib *rib)
 | 
						|
{
 | 
						|
  return kernel_ioctl_ipv4 (SIOCDELRT, p, rib, AF_INET);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_IPV6
 | 
						|
 | 
						|
/* Below is hack for GNU libc definition and Linux 2.1.X header. */
 | 
						|
#undef RTF_DEFAULT
 | 
						|
#undef RTF_ADDRCONF
 | 
						|
 | 
						|
#include <asm/types.h>
 | 
						|
 | 
						|
#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
 | 
						|
/* struct in6_rtmsg will be declared in net/route.h. */
 | 
						|
#else
 | 
						|
#include <linux/ipv6_route.h>
 | 
						|
#endif
 | 
						|
 | 
						|
static int
 | 
						|
kernel_ioctl_ipv6 (u_long type, struct prefix_ipv6 *dest, struct in6_addr *gate,
 | 
						|
		   int index, int flags)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  int sock;
 | 
						|
  struct in6_rtmsg rtm;
 | 
						|
    
 | 
						|
  memset (&rtm, 0, sizeof (struct in6_rtmsg));
 | 
						|
 | 
						|
  rtm.rtmsg_flags |= RTF_UP;
 | 
						|
  rtm.rtmsg_metric = 1;
 | 
						|
  memcpy (&rtm.rtmsg_dst, &dest->prefix, sizeof (struct in6_addr));
 | 
						|
  rtm.rtmsg_dst_len = dest->prefixlen;
 | 
						|
 | 
						|
  /* We need link local index. But this should be done caller...
 | 
						|
  if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway))
 | 
						|
    {
 | 
						|
      index = if_index_address (&rtm.rtmsg_gateway);
 | 
						|
      rtm.rtmsg_ifindex = index;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    rtm.rtmsg_ifindex = 0;
 | 
						|
  */
 | 
						|
 | 
						|
  rtm.rtmsg_flags |= RTF_GATEWAY;
 | 
						|
 | 
						|
  /* For tagging route. */
 | 
						|
  /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
 | 
						|
 | 
						|
  memcpy (&rtm.rtmsg_gateway, gate, sizeof (struct in6_addr));
 | 
						|
 | 
						|
  if (index)
 | 
						|
    rtm.rtmsg_ifindex = index;
 | 
						|
  else
 | 
						|
    rtm.rtmsg_ifindex = 0;
 | 
						|
 | 
						|
  rtm.rtmsg_metric = 1;
 | 
						|
  
 | 
						|
  sock = socket (AF_INET6, SOCK_DGRAM, 0);
 | 
						|
  if (sock < 0)
 | 
						|
    {
 | 
						|
      zlog_warn ("can't make socket\n");
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Send message via ioctl. */
 | 
						|
  ret = ioctl (sock, type, &rtm);
 | 
						|
  if (ret < 0)
 | 
						|
    {
 | 
						|
      zlog_warn ("can't %s ipv6 route: %s\n", type == SIOCADDRT ? "add" : "delete", 
 | 
						|
	   safe_strerror(errno));
 | 
						|
      ret = errno;
 | 
						|
      close (sock);
 | 
						|
      return ret;
 | 
						|
    }
 | 
						|
  close (sock);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
kernel_ioctl_ipv6_multipath (u_long cmd, struct prefix *p, struct rib *rib,
 | 
						|
			     int family)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  int sock;
 | 
						|
  struct in6_rtmsg rtm;
 | 
						|
  struct nexthop *nexthop, *tnexthop;
 | 
						|
  int recursing;
 | 
						|
  int nexthop_num = 0;
 | 
						|
    
 | 
						|
  memset (&rtm, 0, sizeof (struct in6_rtmsg));
 | 
						|
 | 
						|
  rtm.rtmsg_flags |= RTF_UP;
 | 
						|
  rtm.rtmsg_metric = rib->metric;
 | 
						|
  memcpy (&rtm.rtmsg_dst, &p->u.prefix, sizeof (struct in6_addr));
 | 
						|
  rtm.rtmsg_dst_len = p->prefixlen;
 | 
						|
 | 
						|
  /* We need link local index. But this should be done caller...
 | 
						|
  if (IN6_IS_ADDR_LINKLOCAL(&rtm.rtmsg_gateway))
 | 
						|
    {
 | 
						|
      index = if_index_address (&rtm.rtmsg_gateway);
 | 
						|
      rtm.rtmsg_ifindex = index;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    rtm.rtmsg_ifindex = 0;
 | 
						|
  */
 | 
						|
 | 
						|
  rtm.rtmsg_flags |= RTF_GATEWAY;
 | 
						|
 | 
						|
  /* For tagging route. */
 | 
						|
  /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
 | 
						|
 | 
						|
  /* Make gateway. */
 | 
						|
  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
 | 
						|
    {
 | 
						|
      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
 | 
						|
        continue;
 | 
						|
 | 
						|
      if ((cmd == SIOCADDRT 
 | 
						|
	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
 | 
						|
	  || (cmd == SIOCDELRT
 | 
						|
	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
 | 
						|
	{
 | 
						|
	  if (nexthop->type == NEXTHOP_TYPE_IPV6
 | 
						|
	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
 | 
						|
	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
 | 
						|
	    {
 | 
						|
	      memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6,
 | 
						|
		      sizeof (struct in6_addr));
 | 
						|
	    }
 | 
						|
	  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
 | 
						|
	      || nexthop->type == NEXTHOP_TYPE_IFNAME
 | 
						|
	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
 | 
						|
	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
 | 
						|
	    rtm.rtmsg_ifindex = nexthop->ifindex;
 | 
						|
	  else
 | 
						|
	    rtm.rtmsg_ifindex = 0;
 | 
						|
 | 
						|
	  if (cmd == SIOCADDRT)
 | 
						|
	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
 | 
						|
 | 
						|
	  nexthop_num++;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* If there is no useful nexthop then return. */
 | 
						|
  if (nexthop_num == 0)
 | 
						|
    {
 | 
						|
      if (IS_ZEBRA_DEBUG_KERNEL)
 | 
						|
	zlog_debug ("netlink_route_multipath(): No useful nexthop.");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  sock = socket (AF_INET6, SOCK_DGRAM, 0);
 | 
						|
  if (sock < 0)
 | 
						|
    {
 | 
						|
      zlog_warn ("can't make socket\n");
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Send message via ioctl. */
 | 
						|
  ret = ioctl (sock, cmd, &rtm);
 | 
						|
  if (ret < 0)
 | 
						|
    {
 | 
						|
      zlog_warn ("can't %s ipv6 route: %s\n",
 | 
						|
		 cmd == SIOCADDRT ? "add" : "delete", 
 | 
						|
	   safe_strerror(errno));
 | 
						|
      ret = errno;
 | 
						|
      close (sock);
 | 
						|
      return ret;
 | 
						|
    }
 | 
						|
  close (sock);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
kernel_add_ipv6 (struct prefix *p, struct rib *rib)
 | 
						|
{
 | 
						|
  return kernel_ioctl_ipv6_multipath (SIOCADDRT, p, rib, AF_INET6);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
kernel_delete_ipv6 (struct prefix *p, struct rib *rib)
 | 
						|
{
 | 
						|
  return kernel_ioctl_ipv6_multipath (SIOCDELRT, p, rib, AF_INET6);
 | 
						|
}
 | 
						|
 | 
						|
/* Delete IPv6 route from the kernel. */
 | 
						|
int
 | 
						|
kernel_delete_ipv6_old (struct prefix_ipv6 *dest, struct in6_addr *gate,
 | 
						|
		    unsigned int index, int flags, int table)
 | 
						|
{
 | 
						|
  return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags);
 | 
						|
}
 | 
						|
#endif /* HAVE_IPV6 */
 |