示例#1
0
/* Interface between zebra message and rtm message. */
static int
kernel_rtm_ipv6 (int message, struct prefix_ipv6 *dest,
		 struct in6_addr *gate, int index, int flags)
{
  struct sockaddr_in6 *mask;
  struct sockaddr_in6 sin_dest, sin_mask, sin_gate;

  memset (&sin_dest, 0, sizeof (struct sockaddr_in6));
  sin_dest.sin6_family = AF_INET6;
#ifdef SIN6_LEN
  sin_dest.sin6_len = sizeof (struct sockaddr_in6);
#endif /* SIN6_LEN */

  memset (&sin_mask, 0, sizeof (struct sockaddr_in6));

  memset (&sin_gate, 0, sizeof (struct sockaddr_in6));
  sin_gate.sin6_family = AF_INET6;
#ifdef SIN6_LEN
  sin_gate.sin6_len = sizeof (struct sockaddr_in6);
#endif /* SIN6_LEN */

  sin_dest.sin6_addr = dest->prefix;

  if (gate)
    memcpy (&sin_gate.sin6_addr, gate, sizeof (struct in6_addr));

  /* Under kame set interface index to link local address. */
#ifdef KAME

#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
  do { \
    (a).s6_addr[2] = ((i) >> 8) & 0xff; \
    (a).s6_addr[3] = (i) & 0xff; \
  } while (0)

  if (gate && IN6_IS_ADDR_LINKLOCAL(gate))
    SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, index);
#endif /* KAME */

  if (gate && dest->prefixlen == 128)
    mask = NULL;
  else
    {
      masklen2ip6 (dest->prefixlen, &sin_mask.sin6_addr);
      sin_mask.sin6_family = AF_INET6;
#ifdef SIN6_LEN
      sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr);
#endif /* SIN6_LEN */
      mask = &sin_mask;
    }

  return rtm_write (message, 
		    (union sockunion *) &sin_dest,
		    (union sockunion *) mask,
		    gate ? (union sockunion *)&sin_gate : NULL,
		    index,
		    flags,
		    0);
}
示例#2
0
/* Interface's address information get. */
int
ifam_read (struct ifa_msghdr *ifam)
{
  struct interface *ifp;
  union sockunion addr, mask, gate;

  /* Check does this interface exist or not. */
  ifp = if_lookup_by_index (ifam->ifam_index);
  if (ifp == NULL) 
    {
      zlog_warn ("no interface for index %d", ifam->ifam_index); 
      return -1;
    }

  /* Allocate and read address information. */
  ifam_read_mesg (ifam, &addr, &mask, &gate);

  /* Check interface flag for implicit up of the interface. */
  if_refresh (ifp);

  /* Add connected address. */
  switch (sockunion_family (&addr))
    {
    case AF_INET:
      if (ifam->ifam_type == RTM_NEWADDR)
	connected_add_ipv4 (ifp, 0, &addr.sin.sin_addr, 
			    ip_masklen (mask.sin.sin_addr),
			    &gate.sin.sin_addr, NULL);
      else
	connected_delete_ipv4 (ifp, 0, &addr.sin.sin_addr, 
			       ip_masklen (mask.sin.sin_addr),
			       &gate.sin.sin_addr, NULL);
      break;
#ifdef HAVE_IPV6
    case AF_INET6:
      /* Unset interface index from link-local address when IPv6 stack
	 is KAME. */
      if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr))
	SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0);

      if (ifam->ifam_type == RTM_NEWADDR)
	connected_add_ipv6 (ifp,
			    &addr.sin6.sin6_addr, 
			    ip6_masklen (mask.sin6.sin6_addr),
			    &gate.sin6.sin6_addr);
      else
	connected_delete_ipv6 (ifp,
			       &addr.sin6.sin6_addr, 
			       ip6_masklen (mask.sin6.sin6_addr),
			       &gate.sin6.sin6_addr);
      break;
#endif /* HAVE_IPV6 */
    default:
      /* Unsupported family silently ignore... */
      break;
    }
  return 0;
}
示例#3
0
static int
parse_kernel_route(const struct rt_msghdr *rtm, struct kernel_route *route)
{

    void *rta = (void*)rtm + sizeof(struct rt_msghdr);
    struct sockaddr_in6 *sin6;
    char addr[INET6_ADDRSTRLEN];

    memset(route, 0, sizeof(*route));
    route->metric = 0;
    route->ifindex = rtm->rtm_index;

    if(!(rtm->rtm_addrs & RTA_DST))
        return -1;
    sin6 = (struct sockaddr_in6 *)rta;
    if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) 
       || IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
        return -1;
    if((rtm->rtm_flags & RTF_PROTO2) != 0)
        return -1;
    memcpy(&route->prefix, &sin6->sin6_addr, 16);
    rta += ROUNDUP(sizeof(struct sockaddr_in6));
   
    if(!(rtm->rtm_addrs & RTA_GATEWAY))
        return -1;

    sin6 = (struct sockaddr_in6 *)rta;
    if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) {
        route->ifindex = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr);
        SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0);
    }
    memcpy(&route->gw, &sin6->sin6_addr, 16);
    rta += ROUNDUP(sizeof(struct sockaddr_in6));

    if(!(rtm->rtm_addrs & RTA_NETMASK)) {
        route->plen = 0;
    } else {
        sin6 = (struct sockaddr_in6 *)rta;        
        route->plen = mask2len(&sin6->sin6_addr);
        inet_ntop(AF_INET6, &sin6->sin6_addr, addr, sizeof(addr));
        rta += ROUNDUP(sizeof(struct sockaddr_in6));
    }
    if (rtm->rtm_flags & RTF_HOST)
        route->plen = 128;

    if(ifindex_lo < 0) {
        ifindex_lo = if_nametoindex("lo0");
        if(ifindex_lo <= 0)
            return -1;
    }

    if(route->ifindex == ifindex_lo)
        return -1;

    return 0;

}
示例#4
0
/* Performs a non-blocking connect().  */
enum connect_result sockunion_connect(int fd, const union sockunion *peersu,
				      unsigned short port, ifindex_t ifindex)
{
	int ret;
	union sockunion su;

	memcpy(&su, peersu, sizeof(union sockunion));

	switch (su.sa.sa_family) {
	case AF_INET:
		su.sin.sin_port = port;
		break;
	case AF_INET6:
		su.sin6.sin6_port = port;
#ifdef KAME
		if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) {
			su.sin6.sin6_scope_id = ifindex;
			SET_IN6_LINKLOCAL_IFINDEX(su.sin6.sin6_addr, ifindex);
		}
#endif /* KAME */
		break;
	}

	/* Call connect function. */
	ret = connect(fd, (struct sockaddr *)&su, sockunion_sizeof(&su));

	/* Immediate success */
	if (ret == 0)
		return connect_success;

	/* If connect is in progress then return 1 else it's real error. */
	if (ret < 0) {
		if (errno != EINPROGRESS) {
			char str[SU_ADDRSTRLEN];
			zlog_info("can't connect to %s fd %d : %s",
				  sockunion_log(&su, str, sizeof str), fd,
				  safe_strerror(errno));
			return connect_error;
		}
	}

	return connect_in_progress;
}
示例#5
0
void
rtm_read (struct rt_msghdr *rtm)
{
  int flags;
  u_char zebra_flags;
  union sockunion dest, mask, gate;

  zebra_flags = 0;

  /* Discard self send message. */
  if (rtm->rtm_type != RTM_GET 
      && (rtm->rtm_pid == pid || rtm->rtm_pid == old_pid))
    return;

  /* Read destination and netmask and gateway from rtm message
     structure. */
  flags = rtm_read_mesg (rtm, &dest, &mask, &gate);

#ifdef RTF_CLONED	/*bsdi, netbsd 1.6*/
  if (flags & RTF_CLONED)
    return;
#endif
#ifdef RTF_WASCLONED	/*freebsd*/
  if (flags & RTF_WASCLONED)
    return;
#endif

  if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP))
    return;

  /* Ignore route which has both HOST and GATEWAY attribute. */
  if ((flags & RTF_GATEWAY) && (flags & RTF_HOST))
    return;

  /* This is connected route. */
  if (! (flags & RTF_GATEWAY))
      return;

  if (flags & RTF_PROTO1)
    SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE);

  /* This is persistent route. */
  if (flags & RTF_STATIC)
    SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC);

  if (dest.sa.sa_family == AF_INET)
    {
      struct prefix_ipv4 p;

      p.family = AF_INET;
      p.prefix = dest.sin.sin_addr;
      p.prefixlen = ip_masklen (mask.sin.sin_addr);

      if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD)
	rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, 
		      &p, &gate.sin.sin_addr, 0, 0, 0, 0);
      else
	rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, 
		      &p, &gate.sin.sin_addr, 0, 0);
    }
#ifdef HAVE_IPV6
  if (dest.sa.sa_family == AF_INET6)
    {
      struct prefix_ipv6 p;
      unsigned int ifindex = 0;

      p.family = AF_INET6;
      p.prefix = dest.sin6.sin6_addr;
      p.prefixlen = ip6_masklen (mask.sin6.sin6_addr);

#ifdef KAME
      if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr))
	{
	  ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr);
	  SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0);
	}
#endif /* KAME */

      if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD)
	rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
		      &p, &gate.sin6.sin6_addr, ifindex, 0);
      else
	rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
			 &p, &gate.sin6.sin6_addr, ifindex, 0);
    }
#endif /* HAVE_IPV6 */
}
示例#6
0
/* sockunion_connect returns
   -1 : error occured
   0 : connect success
   1 : connect is in progress */
enum connect_result
sockunion_connect (int fd, union sockunion *peersu, unsigned short port,
		   unsigned int ifindex)
{
  int ret;
  int val;
  union sockunion su;

  memcpy (&su, peersu, sizeof (union sockunion));

  switch (su.sa.sa_family)
    {
    case AF_INET:
      su.sin.sin_port = port;
      break;
#ifdef HAVE_IPV6
    case AF_INET6:
      su.sin6.sin6_port  = port;
#ifdef KAME
      if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
	{
#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
	  /* su.sin6.sin6_scope_id = ifindex; */
#ifdef MUSICA
	  su.sin6.sin6_scope_id = ifindex; 
#endif
#endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
#ifndef MUSICA
	  SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
#endif
	}
#endif /* KAME */
      break;
#endif /* HAVE_IPV6 */
    }      

  /* Make socket non-block. */
  val = fcntl (fd, F_GETFL, 0);
  fcntl (fd, F_SETFL, val|O_NONBLOCK);

  /* Call connect function. */
  ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));

  /* Immediate success */
  if (ret == 0)
    {
      fcntl (fd, F_SETFL, val);
      return connect_success;
    }

  /* If connect is in progress then return 1 else it's real error. */
  if (ret < 0)
    {
      if (errno != EINPROGRESS)
	{
	  char str[SU_ADDRSTRLEN];
	  zlog_info ("can't connect to %s fd %d : %s",
		     sockunion_log (&su, str, sizeof str),
		     fd, safe_strerror (errno));
	  return connect_error;
	}
    }

  fcntl (fd, F_SETFL, val);

  return connect_in_progress;
}
示例#7
0
/* Tries to open TCP Connection to BGP Peer */
void
bpn_sock_cb_connect (struct bgp_peer *peer)
{
  pal_sock_handle_t sck_fd;
  union sockunion su;
  u_int32_t ifindex;
  s_int32_t ret;

  ifindex = 0;

  /* Obtain Socket FD */
  sck_fd = stream_sock_cb_get_fd (peer->sock_cb, &peer->su, &BLG);
  if (sck_fd < 0)
    {
      BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_FAIL);

      goto EXIT;
    }

  /* Set Socket options */
  ret = bpn_sock_set_opt (peer, sck_fd, PAL_TRUE);
  if (ret < 0)
    {
      BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_FAIL);

      goto EXIT;
    }

  /* Prepare the Sock-Union struct to connect */
  pal_mem_cpy (&su, &peer->su, sizeof (union sockunion));

#ifdef HAVE_IPV6
  IF_BGP_CAP_HAVE_IPV6
    {
      if (peer->ifname)
        ifindex = if_name2index (&BGP_VR.owning_ivr->ifm,
                                 peer->ifname);
    }
#endif /* HAVE_IPV6 */

  switch (su.sa.sa_family)
    {
    case AF_INET:
      if (peer->sock_remote_port != BGP_PORT_DEFAULT)
	su.sin.sin_port = pal_hton16 (peer->sock_remote_port);
      else
        su.sin.sin_port = pal_hton16 (BGP_PORT_DEFAULT);
      break;

#ifdef HAVE_IPV6
    case AF_INET6:
      if (peer->sock_remote_port != BGP_PORT_DEFAULT)
	su.sin.sin_port = pal_hton16 (peer->sock_remote_port);
      else
        su.sin6.sin6_port  = pal_hton16 (BGP_PORT_DEFAULT);
#ifdef KAME
      if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
        {
          SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
        }
#elif defined(HAVE_SIN6_SCOPE_ID)
      if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
        {
          su.sin6.sin6_scope_id = ifindex;
          SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
        }
#endif /* HAVE_SIN6_SCOPE_ID */
      break;
#endif /* HAVE_IPV6 */
    }

  /* Connect the Stream Socket */
  ret = stream_sock_cb_connect (peer->sock_cb, &su, &BLG);
  if (ret < 0)
    {
      BGP_PEER_FSM_EVENT_ADD (&BLG, peer, BPF_EVENT_TCP_CONN_FAIL);

      goto EXIT;
    }

 EXIT:

  return;
}
示例#8
0
void
rtm_read (struct rt_msghdr *rtm)
{
  int flags;
  u_char zebra_flags;
  union sockunion dest, mask, gate;
  char ifname[INTERFACE_NAMSIZ + 1];
  short ifnlen = 0;

  zebra_flags = 0;

  /* Read destination and netmask and gateway from rtm message
     structure. */
  flags = rtm_read_mesg (rtm, &dest, &mask, &gate, ifname, &ifnlen);
  if (!(flags & RTF_DONE))
    return;
  if (IS_ZEBRA_DEBUG_KERNEL)
    zlog_debug ("%s: got rtm of type %d (%s)", __func__, rtm->rtm_type,
      lookup (rtm_type_str, rtm->rtm_type));

#ifdef RTF_CLONED	/*bsdi, netbsd 1.6*/
  if (flags & RTF_CLONED)
    return;
#endif
#ifdef RTF_WASCLONED	/*freebsd*/
  if (flags & RTF_WASCLONED)
    return;
#endif

  if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP))
    return;

  /* This is connected route. */
  if (! (flags & RTF_GATEWAY))
      return;

  if (flags & RTF_PROTO1)
    SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE);

  /* This is persistent route. */
  if (flags & RTF_STATIC)
    SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC);

  /* This is a reject or blackhole route */
  if (flags & RTF_REJECT)
    SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT);
  if (flags & RTF_BLACKHOLE)
    SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE);

  if (dest.sa.sa_family == AF_INET)
    {
      struct prefix_ipv4 p;

      p.family = AF_INET;
      p.prefix = dest.sin.sin_addr;
      if (flags & RTF_HOST)
	p.prefixlen = IPV4_MAX_PREFIXLEN;
      else
	p.prefixlen = ip_masklen (mask.sin.sin_addr);
      
      /* Catch self originated messages and match them against our current RIB.
       * At the same time, ignore unconfirmed messages, they should be tracked
       * by rtm_write() and kernel_rtm_ipv4().
       */
      if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid)
      {
        char buf[INET_ADDRSTRLEN], gate_buf[INET_ADDRSTRLEN];
        int ret;
        if (! IS_ZEBRA_DEBUG_RIB)
          return;
        ret = rib_lookup_ipv4_route (&p, &gate); 
        inet_ntop (AF_INET, &p.prefix, buf, INET_ADDRSTRLEN);
        switch (rtm->rtm_type)
        {
          case RTM_ADD:
          case RTM_GET:
          case RTM_CHANGE:
            /* The kernel notifies us about a new route in FIB created by us.
               Do we have a correspondent entry in our RIB? */
            switch (ret)
            {
              case ZEBRA_RIB_NOTFOUND:
                zlog_debug ("%s: %s %s/%d: desync: RR isn't yet in RIB, while already in FIB",
                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen);
                break;
              case ZEBRA_RIB_FOUND_CONNECTED:
              case ZEBRA_RIB_FOUND_NOGATE:
                inet_ntop (AF_INET, &gate.sin.sin_addr, gate_buf, INET_ADDRSTRLEN);
                zlog_debug ("%s: %s %s/%d: desync: RR is in RIB, but gate differs (ours is %s)",
                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen, gate_buf);
                break;
              case ZEBRA_RIB_FOUND_EXACT: /* RIB RR == FIB RR */
                zlog_debug ("%s: %s %s/%d: done Ok",
                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen);
                rib_lookup_and_dump (&p);
                return;
                break;
            }
            break;
          case RTM_DELETE:
            /* The kernel notifies us about a route deleted by us. Do we still
               have it in the RIB? Do we have anything instead? */
            switch (ret)
            {
              case ZEBRA_RIB_FOUND_EXACT:
                zlog_debug ("%s: %s %s/%d: desync: RR is still in RIB, while already not in FIB",
                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen);
                rib_lookup_and_dump (&p);
                break;
              case ZEBRA_RIB_FOUND_CONNECTED:
              case ZEBRA_RIB_FOUND_NOGATE:
                zlog_debug ("%s: %s %s/%d: desync: RR is still in RIB, plus gate differs",
                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen);
                rib_lookup_and_dump (&p);
                break;
              case ZEBRA_RIB_NOTFOUND: /* RIB RR == FIB RR */
                zlog_debug ("%s: %s %s/%d: done Ok",
                  __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen);
                rib_lookup_and_dump (&p);
                return;
                break;
            }
            break;
          default:
            zlog_debug ("%s: %s/%d: warning: loopback RTM of type %s received",
              __func__, buf, p.prefixlen, lookup (rtm_type_str, rtm->rtm_type));
        }
        return;
      }

      /* Change, delete the old prefix, we have no further information
       * to specify the route really
       */
      if (rtm->rtm_type == RTM_CHANGE)
        rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
                         NULL, 0, 0);
      
      if (rtm->rtm_type == RTM_GET 
          || rtm->rtm_type == RTM_ADD
          || rtm->rtm_type == RTM_CHANGE)
	rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, 
		      &p, &gate.sin.sin_addr, NULL, 0, 0, 0, 0, rtm->rtm_protocol);
      else
	rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, 
		      &p, &gate.sin.sin_addr, 0, 0);
    }
#ifdef HAVE_IPV6
  if (dest.sa.sa_family == AF_INET6)
    {
      /* One day we might have a debug section here like one in the
       * IPv4 case above. Just ignore own messages at the moment.
       */
      if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid)
        return;
      struct prefix_ipv6 p;
      unsigned int ifindex = 0;

      p.family = AF_INET6;
      p.prefix = dest.sin6.sin6_addr;
      if (flags & RTF_HOST)
	p.prefixlen = IPV6_MAX_PREFIXLEN;
      else
	p.prefixlen = ip6_masklen (mask.sin6.sin6_addr);

#ifdef KAME
      if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr))
	{
	  ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr);
	  SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0);
	}
#endif /* KAME */

      /* CHANGE: delete the old prefix, we have no further information
       * to specify the route really
       */
      if (rtm->rtm_type == RTM_CHANGE)
        rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
                         NULL, 0, 0);
      
      if (rtm->rtm_type == RTM_GET 
          || rtm->rtm_type == RTM_ADD
          || rtm->rtm_type == RTM_CHANGE)
	rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
		      &p, &gate.sin6.sin6_addr, ifindex, 0, 0, 0);
      else
	rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
			 &p, &gate.sin6.sin6_addr, ifindex, 0);
    }
#endif /* HAVE_IPV6 */
}
示例#9
0
/* Interface's address information get. */
int
ifam_read (struct ifa_msghdr *ifam)
{
  struct interface *ifp = NULL;
  union sockunion addr, mask, brd;
  char ifname[INTERFACE_NAMSIZ];
  short ifnlen = 0;
  char isalias = 0;
  int flags = 0;
  
  ifname[0] = ifname[INTERFACE_NAMSIZ - 1] = '\0';
  
  /* Allocate and read address information. */
  ifam_read_mesg (ifam, &addr, &mask, &brd, ifname, &ifnlen);
  
  if ((ifp = if_lookup_by_index(ifam->ifam_index)) == NULL)
    {
      zlog_warn ("%s: no interface for ifname %s, index %d", 
                 __func__, ifname, ifam->ifam_index);
      return -1;
    }
  
  if (ifnlen && strncmp (ifp->name, ifname, INTERFACE_NAMSIZ))
    isalias = 1;
  
  /* N.B. The info in ifa_msghdr does not tell us whether the RTA_BRD
     field contains a broadcast address or a peer address, so we are forced to
     rely upon the interface type. */
  if (if_is_pointopoint(ifp))
    SET_FLAG(flags, ZEBRA_IFA_PEER);

#if 0
  /* it might seem cute to grab the interface metric here, however
   * we're processing an address update message, and so some systems
   * (e.g. FBSD) dont bother to fill in ifam_metric. Disabled, but left
   * in deliberately, as comment.
   */
  ifp->metric = ifam->ifam_metric;
#endif

  /* Add connected address. */
  switch (sockunion_family (&addr))
    {
    case AF_INET:
      if (ifam->ifam_type == RTM_NEWADDR)
	connected_add_ipv4 (ifp, flags, &addr.sin.sin_addr, 
			    ip_masklen (mask.sin.sin_addr),
			    &brd.sin.sin_addr,
			    (isalias ? ifname : NULL));
      else
	connected_delete_ipv4 (ifp, flags, &addr.sin.sin_addr, 
			       ip_masklen (mask.sin.sin_addr),
			       &brd.sin.sin_addr);
      break;
#ifdef HAVE_IPV6
    case AF_INET6:
      /* Unset interface index from link-local address when IPv6 stack
	 is KAME. */
      if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr))
	SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0);

      if (ifam->ifam_type == RTM_NEWADDR)
	connected_add_ipv6 (ifp, flags, &addr.sin6.sin6_addr, 
			    ip6_masklen (mask.sin6.sin6_addr),
			    &brd.sin6.sin6_addr,
			    (isalias ? ifname : NULL));
      else
	connected_delete_ipv6 (ifp,
			       &addr.sin6.sin6_addr, 
			       ip6_masklen (mask.sin6.sin6_addr),
			       &brd.sin6.sin6_addr);
      break;
#endif /* HAVE_IPV6 */
    default:
      /* Unsupported family silently ignore... */
      break;
    }
  
  /* Check interface flag for implicit up of the interface. */
  if_refresh (ifp);

#ifdef SUNOS_5
  /* In addition to lacking IFANNOUNCE, on SUNOS IFF_UP is strange. 
   * See comments for SUNOS_5 in interface.c::if_flags_mangle.
   * 
   * Here we take care of case where the real IFF_UP was previously
   * unset (as kept in struct zebra_if.primary_state) and the mangled
   * IFF_UP (ie IFF_UP set || listcount(connected) has now transitioned
   * to unset due to the lost non-primary address having DELADDR'd.
   *
   * we must delete the interface, because in between here and next
   * event for this interface-name the administrator could unplumb
   * and replumb the interface.
   */
  if (!if_is_up (ifp))
    if_delete_update (ifp);
#endif /* SUNOS_5 */
  
  return 0;
}
示例#10
0
int
bgp_nexthop_set (union sockunion *local, union sockunion *remote, 
		 struct bgp_nexthop *nexthop, struct peer *peer)
{
  int ret = 0;
  struct interface *ifp = NULL;

  memset (nexthop, 0, sizeof (struct bgp_nexthop));

  if (!local)
    return -1;
  if (!remote)
    return -1;

  if (local->sa.sa_family == AF_INET)
    {
      nexthop->v4 = local->sin.sin_addr;
      ifp = if_lookup_by_ipv4 (&local->sin.sin_addr);
    }
#ifdef HAVE_IPV6
  if (local->sa.sa_family == AF_INET6)
    {
      if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr))
	{
	  if (peer->ifname)
	    ifp = if_lookup_by_index (if_nametoindex (peer->ifname));
	}
      else
	ifp = if_lookup_by_ipv6 (&local->sin6.sin6_addr);
    }
#endif /* HAVE_IPV6 */

  if (!ifp)
    return -1;

  nexthop->ifp = ifp;

  /* IPv4 connection. */
  if (local->sa.sa_family == AF_INET)
    {
#ifdef HAVE_IPV6
      /* IPv6 nexthop*/
      ret = if_get_ipv6_global (ifp, &nexthop->v6_global);

      /* There is no global nexthop. */
      if (!ret)
	if_get_ipv6_local (ifp, &nexthop->v6_global);
      else
	if_get_ipv6_local (ifp, &nexthop->v6_local);
#endif /* HAVE_IPV6 */
    }

#ifdef HAVE_IPV6
  /* IPv6 connection. */
  if (local->sa.sa_family == AF_INET6)
    {
      struct interface *direct = NULL;

      /* IPv4 nexthop. */
      ret = if_get_ipv4_address(ifp, &nexthop->v4);
      if (!ret && peer->local_id.s_addr)
	nexthop->v4 = peer->local_id;

      /* Global address*/
      if (! IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr))
	{
	  memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, 
		  IPV6_MAX_BYTELEN);

	  /* If directory connected set link-local address. */
	  direct = if_lookup_by_ipv6 (&remote->sin6.sin6_addr);
	  if (direct)
	    if_get_ipv6_local (ifp, &nexthop->v6_local);
	}
      else
	/* Link-local address. */
	{
	  ret = if_get_ipv6_global (ifp, &nexthop->v6_global);

	  /* If there is no global address.  Set link-local address as
             global.  I know this break RFC specification... */
	  if (!ret)
	    memcpy (&nexthop->v6_global, &local->sin6.sin6_addr, 
		    IPV6_MAX_BYTELEN);
	  else
	    memcpy (&nexthop->v6_local, &local->sin6.sin6_addr, 
		    IPV6_MAX_BYTELEN);
	}
    }

  if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr) ||
      if_lookup_by_ipv6 (&remote->sin6.sin6_addr))
    peer->shared_network = 1;
  else
    peer->shared_network = 0;

  /* KAME stack specific treatment.  */
#ifdef KAME
  if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_global)
      && IN6_LINKLOCAL_IFINDEX (nexthop->v6_global))
    {
      SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_global, 0);
    }
  if (IN6_IS_ADDR_LINKLOCAL (&nexthop->v6_local)
      && IN6_LINKLOCAL_IFINDEX (nexthop->v6_local))
    {
      SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_local, 0);
    }
#endif /* KAME */
#endif /* HAVE_IPV6 */
  return ret;
}
示例#11
0
文件: rt_socket.c 项目: yubo/quagga
/* Interface between zebra message and rtm message. */
static int
kernel_rtm_ipv6_multipath(int cmd, struct prefix *p, struct rib *rib,
			  int family)
{
	struct sockaddr_in6 *mask;
	struct sockaddr_in6 sin_dest, sin_mask, sin_gate;
	struct nexthop *nexthop, *tnexthop;
	int recursing;
	int nexthop_num = 0;
	unsigned int ifindex = 0;
	int gate = 0;
	int error;

	memset(&sin_dest, 0, sizeof(struct sockaddr_in6));
	sin_dest.sin6_family = AF_INET6;
#ifdef SIN6_LEN
	sin_dest.sin6_len = sizeof(struct sockaddr_in6);
#endif /* SIN6_LEN */
	sin_dest.sin6_addr = p->u.prefix6;

	memset(&sin_mask, 0, sizeof(struct sockaddr_in6));

	memset(&sin_gate, 0, sizeof(struct sockaddr_in6));
	sin_gate.sin6_family = AF_INET6;
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
	sin_gate.sin6_len = sizeof(struct sockaddr_in6);
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */

	/* Make gateway. */
	for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) {
		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
			continue;

		gate = 0;

		if ((cmd == RTM_ADD
		     && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
		    || (cmd == RTM_DELETE
#if 0
			&& CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)
#endif
		    )) {
			if (nexthop->type == NEXTHOP_TYPE_IPV6
			    || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
			    || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
				sin_gate.sin6_addr = nexthop->gate.ipv6;
				gate = 1;
			}
			if (nexthop->type == NEXTHOP_TYPE_IFINDEX
			    || nexthop->type == NEXTHOP_TYPE_IFNAME
			    || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
			    || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
				ifindex = nexthop->ifindex;

			if (cmd == RTM_ADD)
				SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
		}

		/* Under kame set interface index to link local address. */
#ifdef KAME

#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \
      do { \
	(a).s6_addr[2] = ((i) >> 8) & 0xff; \
	(a).s6_addr[3] = (i) & 0xff; \
      } while (0)

		if (gate && IN6_IS_ADDR_LINKLOCAL(&sin_gate.sin6_addr))
			SET_IN6_LINKLOCAL_IFINDEX(sin_gate.sin6_addr, ifindex);
#endif /* KAME */

		if (gate && p->prefixlen == 128)
			mask = NULL;
		else {
			masklen2ip6(p->prefixlen, &sin_mask.sin6_addr);
			sin_mask.sin6_family = AF_INET6;
#ifdef SIN6_LEN
			sin_mask.sin6_len = sin6_masklen(sin_mask.sin6_addr);
#endif /* SIN6_LEN */
			mask = &sin_mask;
		}

		error = rtm_write(cmd,
				  (union sockunion *)&sin_dest,
				  (union sockunion *)mask,
				  gate ? (union sockunion *)&sin_gate : NULL,
				  ifindex, rib->flags, rib->metric);

#if 0
		if (error) {
			zlog_info
			    ("kernel_rtm_ipv6_multipath(): nexthop %d add error=%d.",
			     nexthop_num, error);
		}
#endif

		nexthop_num++;
	}

	/* If there is no useful nexthop then return. */
	if (nexthop_num == 0) {
		if (IS_ZEBRA_DEBUG_KERNEL)
			zlog_debug
			    ("kernel_rtm_ipv6_multipath(): No useful nexthop.");
		return 0;
	}

	return 0;
 /*XXX*/}
示例#12
0
/* sockunion_connect returns
   -1 : error occured
   0 : connect success
   1 : connect is in progress */
enum connect_result
sockunion_connect (int fd, union sockunion *peersu, unsigned short port,
		   unsigned int ifindex)
{
  int ret;
  int val;
  union sockunion su;
  int ret2=0;

  memcpy (&su, peersu, sizeof (union sockunion));

  switch (su.sa.sa_family)
    {
    case AF_INET:
      su.sin.sin_port = port;
      break;
#ifdef HAVE_IPV6
    case AF_INET6:
      su.sin6.sin6_port  = port;
#ifdef KAME
      if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex)
	{
#ifdef HAVE_SIN6_SCOPE_ID
	  /* su.sin6.sin6_scope_id = ifindex; */
#ifdef MUSICA
	  su.sin6.sin6_scope_id = ifindex; 
#endif
#endif /* HAVE_SIN6_SCOPE_ID */
#ifndef MUSICA
	  SET_IN6_LINKLOCAL_IFINDEX (su.sin6.sin6_addr, ifindex);
#endif
	}
#endif /* KAME */
      break;
#endif /* HAVE_IPV6 */
    }      

  /* Make socket non-block. */
  val = fcntl (fd, F_GETFL, 0);
  /*CID 10316 (#1 of 3): Unchecked return value from library (CHECKED_RETURN)
	3. check_return: Calling function "fcntl(fd, 4, val | 0x80)" without checking return value. This library function may fail and return an error code.
	Add check return value.*/
  /*fcntl (fd, F_SETFL, val|O_NONBLOCK);*/
  ret = fcntl (fd, F_SETFL, val|O_NONBLOCK);
  if(ret < 0)
	zlog_warn("%s: line %d, fcntl failed : %s .\n",__func__,__LINE__,safe_strerror(errno));

  /* Call connect function. */
  ret = connect (fd, (struct sockaddr *) &su, sockunion_sizeof (&su));

  /* Immediate success */
  if (ret == 0)
    {
    /*CID 10316 (#2 of 3): Unchecked return value from library (CHECKED_RETURN)
	4. check_return: Calling function "fcntl(fd, 4, val)" without checking return value. This library function may fail and return an error code.
	Change , add check return value.*/
      /*fcntl (fd, F_SETFL, val);*/
	  ret2 = fcntl (fd, F_SETFL, val);
	  if(ret < 0)
		zlog_warn("%s: line %d, fcntl failed : %s .\n",__func__,__LINE__,safe_strerror(errno));
      return connect_success;
    }

  /* If connect is in progress then return 1 else it's real error. */
  if (ret < 0)
    {
      if (errno != EINPROGRESS)
	{
		char *log_str = sockunion_log (&su);
		
	  zlog_info ("can't connect to %s fd %d : %s",
		     log_str, fd, safe_strerror (errno));
	  free(log_str);
	  return connect_error;
	}
    }
  /*CID 10316 (#3 of 3): Unchecked return value from library (CHECKED_RETURN)
	6. check_return: Calling function "fcntl(fd, 4, val)" without checking return value. This library function may fail and return an error code.
	Add check return value.*/
  /*fcntl (fd, F_SETFL, val);*/
  ret2 = fcntl (fd, F_SETFL, val);
  if(ret < 0)
  	zlog_warn("%s: line %d, fcntl failed : %s .\n",__func__,__LINE__,safe_strerror(errno));

  return connect_in_progress;
}
示例#13
0
void
rtm_read (struct rt_msghdr *rtm)
{
    int flags;
    u_char zebra_flags;
    union sockunion dest, mask, gate;
    char ifname[INTERFACE_NAMSIZ + 1];
    short ifnlen = 0;

    zebra_flags = 0;

    /* Discard self send message. */
    if (rtm->rtm_type != RTM_GET
            && (rtm->rtm_pid == pid || rtm->rtm_pid == old_pid))
        return;

    /* Read destination and netmask and gateway from rtm message
       structure. */
    flags = rtm_read_mesg (rtm, &dest, &mask, &gate, ifname, &ifnlen);

#ifdef RTF_CLONED	/*bsdi, netbsd 1.6*/
    if (flags & RTF_CLONED)
        return;
#endif
#ifdef RTF_WASCLONED	/*freebsd*/
    if (flags & RTF_WASCLONED)
        return;
#endif

    if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP))
        return;

    /* This is connected route. */
    if (! (flags & RTF_GATEWAY))
        return;

    if (flags & RTF_PROTO1)
        SET_FLAG (zebra_flags, ZEBRA_FLAG_SELFROUTE);

    /* This is persistent route. */
    if (flags & RTF_STATIC)
        SET_FLAG (zebra_flags, ZEBRA_FLAG_STATIC);

    /* This is a reject or blackhole route */
    if (flags & RTF_REJECT)
        SET_FLAG (zebra_flags, ZEBRA_FLAG_REJECT);
    if (flags & RTF_BLACKHOLE)
        SET_FLAG (zebra_flags, ZEBRA_FLAG_BLACKHOLE);

    if (dest.sa.sa_family == AF_INET)
    {
        struct prefix_ipv4 p;

        p.family = AF_INET;
        p.prefix = dest.sin.sin_addr;
        if (flags & RTF_HOST)
            p.prefixlen = IPV4_MAX_PREFIXLEN;
        else
            p.prefixlen = ip_masklen (mask.sin.sin_addr);

        /* Change, delete the old prefix, we have no further information
         * to specify the route really
         */
        if (rtm->rtm_type == RTM_CHANGE)
            rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
                             NULL, 0, 0);

        if (rtm->rtm_type == RTM_GET
                || rtm->rtm_type == RTM_ADD
                || rtm->rtm_type == RTM_CHANGE)
            rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags,
                          &p, &gate.sin.sin_addr, 0, 0, 0, 0);
        else
            rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags,
                             &p, &gate.sin.sin_addr, 0, 0);
    }
#ifdef HAVE_IPV6
    if (dest.sa.sa_family == AF_INET6)
    {
        struct prefix_ipv6 p;
        unsigned int ifindex = 0;

        p.family = AF_INET6;
        p.prefix = dest.sin6.sin6_addr;
        if (flags & RTF_HOST)
            p.prefixlen = IPV6_MAX_PREFIXLEN;
        else
            p.prefixlen = ip6_masklen (mask.sin6.sin6_addr);

#ifdef KAME
        if (IN6_IS_ADDR_LINKLOCAL (&gate.sin6.sin6_addr))
        {
            ifindex = IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr);
            SET_IN6_LINKLOCAL_IFINDEX (gate.sin6.sin6_addr, 0);
        }
#endif /* KAME */

        /* CHANGE: delete the old prefix, we have no further information
         * to specify the route really
         */
        if (rtm->rtm_type == RTM_CHANGE)
            rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p,
                             NULL, 0, 0);

        if (rtm->rtm_type == RTM_GET
                || rtm->rtm_type == RTM_ADD
                || rtm->rtm_type == RTM_CHANGE)
            rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
                          &p, &gate.sin6.sin6_addr, ifindex, 0, 0, 0);
        else
            rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags,
                             &p, &gate.sin6.sin6_addr, ifindex, 0);
    }
#endif /* HAVE_IPV6 */
}
示例#14
0
static int
parse_kernel_route(const struct rt_msghdr *rtm, struct kernel_route *route)
{
    struct sockaddr *sa;
    char *rta = (char*)rtm + sizeof(struct rt_msghdr);
    uint32_t excluded_flags = 0;

    if(ifindex_lo < 0) {
        ifindex_lo = if_nametoindex("lo0");
        if(ifindex_lo <= 0)
            return -1;
    }

    memset(route, 0, sizeof(*route));
    route->metric = 0;
    route->ifindex = rtm->rtm_index;

#if defined(RTF_IFSCOPE)
    /* Filter out kernel route on OS X */
    excluded_flags |= RTF_IFSCOPE;
#endif
#if defined(RTF_MULTICAST)
    /* Filter out multicast route on others BSD */
    excluded_flags |= RTF_MULTICAST;
#endif
    /* Filter out our own route */
    excluded_flags |= RTF_PROTO2;
    if((rtm->rtm_flags & excluded_flags) != 0)
        return -1;

    /* Prefix */
    if(!(rtm->rtm_addrs & RTA_DST))
        return -1;
    sa = (struct sockaddr *)rta;
    rta += ROUNDUP(sa->sa_len);
    if(sa->sa_family == AF_INET6) {
          struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        memcpy(route->prefix, &sin6->sin6_addr, 16);
        if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
             || IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
           return -1;
    } else if(sa->sa_family == AF_INET) {
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
#if defined(IN_LINKLOCAL)
        if(IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
            return -1;
#endif
        if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
            return -1;
        v4tov6(route->prefix, (unsigned char *)&sin->sin_addr);
    } else {
        return -1;
    }

    /* Gateway */
    if(!(rtm->rtm_addrs & RTA_GATEWAY))
        return -1;
    sa = (struct sockaddr *)rta;
    rta += ROUNDUP(sa->sa_len);
    if(sa->sa_family == AF_INET6) {
          struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        memcpy(route->gw, &sin6->sin6_addr, 16);
        if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) {
            route->ifindex = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr);
            SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0);
        }
    } else if(sa->sa_family == AF_INET) {
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
        v4tov6(route->gw, (unsigned char *)&sin->sin_addr);
    }
    if((int)route->ifindex == ifindex_lo)
        return -1;

    /* Netmask */
    if((rtm->rtm_addrs & RTA_NETMASK) != 0) {
        sa = (struct sockaddr *)rta;
        rta += ROUNDUP(sa->sa_len);
        if(!v4mapped(route->prefix)) {
            struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
            route->plen = mask2len((unsigned char*)&sin6->sin6_addr, 16);
        } else {
            struct sockaddr_in *sin = (struct sockaddr_in *)sa;
            route->plen = mask2len((unsigned char*)&sin->sin_addr, 4);
        }
    }
    if(v4mapped(route->prefix)) route->plen += 96;
    if(rtm->rtm_flags & RTF_HOST) route->plen = 128;

    return 0;
}
示例#15
0
int
kernel_route(int operation, const unsigned char *dest, unsigned short plen,
             const unsigned char *gate, int ifindex, unsigned int metric,
             const unsigned char *newgate, int newifindex,
             unsigned int newmetric)
{
    unsigned char msg[512];
    struct rt_msghdr *rtm;
    struct sockaddr_in6 *sin6;
    struct sockaddr_in *sin;
    int rc, len, ipv4;

    char local6[1][1][16] = IN6ADDR_LOOPBACK_INIT;
    char local4[1][1][16] =
        {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}};

    /* Check that the protocol family is consistent. */
    if(plen >= 96 && v4mapped(dest)) {
        if(!v4mapped(gate)) {
            errno = EINVAL;
            return -1;
        }
        ipv4 = 1;
    } else {
        if(v4mapped(gate)) {
            errno = EINVAL;
            return -1;
        }
        ipv4 = 0;
    }

    if(operation == ROUTE_MODIFY && newmetric == metric && 
       memcmp(newgate, gate, 16) == 0 && newifindex == ifindex)
      return 0;

    if(operation == ROUTE_MODIFY) {
        metric = newmetric;
        gate = newgate;
        ifindex = newifindex;
    }

    kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n",
            operation == ROUTE_ADD ? "add" :
            operation == ROUTE_FLUSH ? "flush" : "change",
            format_address(dest), plen, metric, ifindex,
            format_address(gate));

    if(kernel_socket < 0) kernel_setup_socket(1);

    memset(&msg, 0, sizeof(msg));
    rtm = (struct rt_msghdr *)msg;
    rtm->rtm_version = RTM_VERSION;
    switch(operation) {
    case ROUTE_FLUSH:
        rtm->rtm_type = RTM_DELETE; break;
    case ROUTE_ADD:
        rtm->rtm_type = RTM_ADD; break;
    case ROUTE_MODIFY: 
        rtm->rtm_type = RTM_CHANGE; break;
    default: 
        return -1;
    };
    rtm->rtm_index = ifindex;
    rtm->rtm_flags = RTF_UP | RTF_PROTO2;
    if(plen == 128) rtm->rtm_flags |= RTF_HOST;
    /*     if(memcmp(nexthop->id, dest, 16) == 0) { */
    /*         rtm -> rtm_flags |= RTF_LLINFO; */
    /*         rtm -> rtm_flags |= RTF_CLONING; */
    /*     } else { */
    rtm->rtm_flags |= RTF_GATEWAY;
    /*     } */
    if(metric == KERNEL_INFINITY) {
        rtm->rtm_flags |= RTF_BLACKHOLE;
        if(ifindex_lo < 0) {
            ifindex_lo = if_nametoindex("lo0");
            if(ifindex_lo <= 0)
                return -1;
        }
        rtm->rtm_index = ifindex_lo;      
    }
    rtm->rtm_seq = ++seq;
    rtm->rtm_addrs = RTA_DST | RTA_GATEWAY;
    if(!(operation == ROUTE_MODIFY && plen == 128)) {
        rtm->rtm_addrs |= RTA_NETMASK;
    }

#define push_sockaddr_in(ptr, offset) \
    do { (ptr) = (struct sockaddr_in *)((char *)(ptr) + (offset)); \
         (ptr)->sin_len = sizeof(struct sockaddr_in); \
         (ptr)->sin_family = AF_INET; } while (0)

#define get_sin_addr(dst,src) \
    do { memcpy((dst), (src) + 12, 4); } while (0)

#define push_sockaddr_in6(ptr, offset) \
    do { (ptr) = (struct sockaddr_in6 *)((char *)(ptr) + (offset)); \
         (ptr)->sin6_len = sizeof(struct sockaddr_in6); \
         (ptr)->sin6_family = AF_INET6; } while (0)

#define get_sin6_addr(dst,src) \
    do { memcpy((dst), (src), 16); } while (0)

    /* KAME ipv6 stack does not support IPv4 mapped IPv6, so we have to
     * duplicate the codepath */
    if(ipv4) {
        sin = (struct sockaddr_in *)msg;
        /* destination */
        push_sockaddr_in(sin, sizeof(*rtm));
        get_sin_addr(&(sin->sin_addr), dest);
        /* gateway */
        push_sockaddr_in(sin, ROUNDUP(sin->sin_len));
        if (metric == KERNEL_INFINITY)
            get_sin_addr(&(sin->sin_addr),**local4);
        else
            get_sin_addr(&(sin->sin_addr),gate);
        /* netmask */
        if((rtm->rtm_addrs | RTA_NETMASK) != 0) {
            struct in6_addr tmp_sin6_addr;
            push_sockaddr_in(sin, ROUNDUP(sin->sin_len));
            plen2mask(plen, &tmp_sin6_addr);
            get_sin_addr(&(sin->sin_addr), (char *)&tmp_sin6_addr);
        }
        len = (char *)sin + ROUNDUP(sin->sin_len) - (char *)msg;
    } else {
        sin6 = (struct sockaddr_in6 *)msg;
        /* destination */
        push_sockaddr_in6(sin6, sizeof(*rtm));
        get_sin6_addr(&(sin6->sin6_addr), dest);
        /* gateway */
        push_sockaddr_in6(sin6, ROUNDUP(sin6->sin6_len));
        if (metric == KERNEL_INFINITY)
            get_sin6_addr(&(sin6->sin6_addr),**local6);
        else
            get_sin6_addr(&(sin6->sin6_addr),gate);
        if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr))
            SET_IN6_LINKLOCAL_IFINDEX (sin6->sin6_addr, ifindex);
        /* netmask */
        if((rtm->rtm_addrs | RTA_NETMASK) != 0) {
            push_sockaddr_in6(sin6, ROUNDUP(sin6->sin6_len));
            plen2mask(plen, &sin6->sin6_addr);
        }
        len = (char *)sin6 + ROUNDUP(sin6->sin6_len) - (char *)msg;
    }
    rtm->rtm_msglen = len;

    rc = write(kernel_socket, msg, rtm->rtm_msglen);
    if (rc < rtm->rtm_msglen)
        return -1;

    return 1;
}