mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 11:45:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			559 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			559 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/rib.h"
 | 
						||
#include "zebra/debug.h"
 | 
						||
 | 
						||
/* Initialize of kernel interface.  There is no kernel communication
 | 
						||
   support under ioctl().  So this is dummy stub function. */
 | 
						||
void
 | 
						||
kernel_init ()
 | 
						||
{
 | 
						||
  return;
 | 
						||
}
 | 
						||
 | 
						||
/* Dummy function of routing socket. */
 | 
						||
void
 | 
						||
kernel_read (int sock)
 | 
						||
{
 | 
						||
  return;
 | 
						||
}
 | 
						||
 | 
						||
#if 0
 | 
						||
/* Initialization prototype of struct sockaddr_in. */
 | 
						||
static struct sockaddr_in sin_proto =
 | 
						||
{
 | 
						||
#ifdef HAVE_SIN_LEN
 | 
						||
  sizeof (struct sockaddr_in), 
 | 
						||
#endif /* HAVE_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_SIN_LEN
 | 
						||
  sin_dest.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_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_SIN_LEN
 | 
						||
      sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_SIN_LEN */
 | 
						||
      sin_gate.sin_addr = *gate;
 | 
						||
    }
 | 
						||
 | 
						||
  memset (&sin_mask, 0, sizeof (struct sockaddr_in));
 | 
						||
  sin_mask.sin_family = AF_INET;
 | 
						||
#ifdef HAVE_SIN_LEN
 | 
						||
      sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_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)", strerror (errno), errno);
 | 
						||
      return 1;
 | 
						||
    }
 | 
						||
  close (sock);
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
/* Interface to ioctl route message. */
 | 
						||
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;
 | 
						||
  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_SIN_LEN
 | 
						||
  sin_dest.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_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 (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
 | 
						||
	  SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
 | 
						||
 | 
						||
      goto skip;
 | 
						||
    }
 | 
						||
 | 
						||
  memset (&sin_gate, 0, sizeof (struct sockaddr_in));
 | 
						||
 | 
						||
  /* Make gateway. */
 | 
						||
  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
 | 
						||
    {
 | 
						||
      if ((cmd == SIOCADDRT 
 | 
						||
	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
 | 
						||
	  || (cmd == SIOCDELRT
 | 
						||
	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
 | 
						||
	{
 | 
						||
	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
 | 
						||
	    {
 | 
						||
	      if (nexthop->rtype == NEXTHOP_TYPE_IPV4 ||
 | 
						||
		  nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
 | 
						||
		{
 | 
						||
		  sin_gate.sin_family = AF_INET;
 | 
						||
#ifdef HAVE_SIN_LEN
 | 
						||
		  sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_SIN_LEN */
 | 
						||
		  sin_gate.sin_addr = nexthop->rgate.ipv4;
 | 
						||
		  rtentry.rt_flags |= RTF_GATEWAY;
 | 
						||
		}
 | 
						||
	      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
 | 
						||
		  || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
 | 
						||
		{
 | 
						||
		  ifp = if_lookup_by_index (nexthop->rifindex);
 | 
						||
		  if (ifp)
 | 
						||
		    rtentry.rt_dev = ifp->name;
 | 
						||
		  else
 | 
						||
		    return -1;
 | 
						||
		}
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    {
 | 
						||
	      if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
 | 
						||
		  nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
 | 
						||
		{
 | 
						||
		  sin_gate.sin_family = AF_INET;
 | 
						||
#ifdef HAVE_SIN_LEN
 | 
						||
		  sin_gate.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_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_info ("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_SIN_LEN
 | 
						||
  sin_mask.sin_len = sizeof (struct sockaddr_in);
 | 
						||
#endif /* HAVE_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)", 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
 | 
						||
 | 
						||
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", 
 | 
						||
	   strerror(errno));
 | 
						||
      ret = errno;
 | 
						||
      close (sock);
 | 
						||
      return ret;
 | 
						||
    }
 | 
						||
  close (sock);
 | 
						||
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
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;
 | 
						||
  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 (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
 | 
						||
    {
 | 
						||
      if ((cmd == SIOCADDRT 
 | 
						||
	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
 | 
						||
	  || (cmd == SIOCDELRT
 | 
						||
	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
 | 
						||
	{
 | 
						||
	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
 | 
						||
	    {
 | 
						||
	      if (nexthop->rtype == NEXTHOP_TYPE_IPV6
 | 
						||
		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
 | 
						||
		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
 | 
						||
		{
 | 
						||
		  memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6,
 | 
						||
			  sizeof (struct in6_addr));
 | 
						||
		}
 | 
						||
	      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
 | 
						||
		  || nexthop->rtype == NEXTHOP_TYPE_IFNAME
 | 
						||
		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
 | 
						||
		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
 | 
						||
		rtm.rtmsg_ifindex = nexthop->rifindex;
 | 
						||
	      else
 | 
						||
		rtm.rtmsg_ifindex = 0;
 | 
						||
	      
 | 
						||
	    }
 | 
						||
	  else
 | 
						||
	    {
 | 
						||
	      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_info ("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", 
 | 
						||
	   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,
 | 
						||
		    int index, int flags, int table)
 | 
						||
{
 | 
						||
  return kernel_ioctl_ipv6 (SIOCDELRT, dest, gate, index, flags);
 | 
						||
}
 | 
						||
#endif /* HAVE_IPV6 */
 |