/* Interface function for the kernel routing table updates.  Support
   for RTM_CHANGE will be needed. */
int
rtm_write (int message,
	   union sockunion *dest,
	   union sockunion *mask,
	   union sockunion *gate,
	   unsigned int index,
	   int zebra_flags,
	   int metric)
{
  int ret;
  caddr_t pnt;
  struct interface *ifp;
  struct sockaddr_in tmp_gate;
#ifdef HAVE_IPV6
  struct sockaddr_in6 tmp_gate6;
#endif /* HAVE_IPV6 */

  /* Sequencial number of routing message. */
  static int msg_seq = 0;

  /* Struct of rt_msghdr and buffer for storing socket's data. */
  struct 
  {
    struct rt_msghdr rtm;
    char buf[512];
  } msg;
  
  memset (&tmp_gate, 0, sizeof (struct sockaddr_in));
  tmp_gate.sin_family = AF_INET;
#ifdef HAVE_SIN_LEN
  tmp_gate.sin_len = sizeof (struct sockaddr_in);
#endif /* HAVE_SIN_LEN */

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

  if (routing_sock < 0)
    return ZEBRA_ERR_EPERM;

  /* Clear and set rt_msghdr values */
  memset (&msg, 0, sizeof (struct rt_msghdr));
  msg.rtm.rtm_version = RTM_VERSION;
  msg.rtm.rtm_type = message;
  msg.rtm.rtm_seq = msg_seq++;
  msg.rtm.rtm_addrs = RTA_DST;
  msg.rtm.rtm_addrs |= RTA_GATEWAY;
  msg.rtm.rtm_flags = RTF_UP;
  msg.rtm.rtm_index = index;

  if (metric != 0)
    {
      msg.rtm.rtm_rmx.rmx_hopcount = metric;
      msg.rtm.rtm_inits |= RTV_HOPCOUNT;
    }

  ifp = if_lookup_by_index (index);

  if (gate && message == RTM_ADD)
    msg.rtm.rtm_flags |= RTF_GATEWAY;

  if (! gate && message == RTM_ADD && ifp &&
      (ifp->flags & IFF_POINTOPOINT) == 0)
    msg.rtm.rtm_flags |= RTF_CLONING;

  /* If no protocol specific gateway is specified, use link
     address for gateway. */
  if (! gate)
    {
      if (!ifp)
        {
          zlog_warn ("no gateway found for interface index %d", index);
          return -1;
        }
      gate = (union sockunion *) & ifp->sdl;
    }

  if (mask)
    msg.rtm.rtm_addrs |= RTA_NETMASK;
  else if (message == RTM_ADD) 
    msg.rtm.rtm_flags |= RTF_HOST;

  /* Tagging route with flags */
  msg.rtm.rtm_flags |= (RTF_PROTO1);

  /* Additional flags. */
  if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
    msg.rtm.rtm_flags |= RTF_BLACKHOLE;

#ifdef HAVE_SIN_LEN
#define SOCKADDRSET(X,R) \
  if (msg.rtm.rtm_addrs & (R)) \
    { \
      int len = ROUNDUP ((X)->sa.sa_len); \
      memcpy (pnt, (caddr_t)(X), len); \
      pnt += len; \
    }
#else 
#define SOCKADDRSET(X,R) \
  if (msg.rtm.rtm_addrs & (R)) \
    { \
      int len = ROUNDUP (sizeof((X)->sa)); \
      memcpy (pnt, (caddr_t)(X), len); \
      pnt += len; \
    }
#endif /* HAVE_SIN_LEN */

  pnt = (caddr_t) msg.buf;

  /* Write each socket data into rtm message buffer */
  SOCKADDRSET (dest, RTA_DST);
  SOCKADDRSET (gate, RTA_GATEWAY);
  SOCKADDRSET (mask, RTA_NETMASK);

  msg.rtm.rtm_msglen = pnt - (caddr_t) &msg;

  ret = write (routing_sock, &msg, msg.rtm.rtm_msglen);

  if (ret != msg.rtm.rtm_msglen) 
    {
      if (errno == EEXIST) 
	return ZEBRA_ERR_RTEXIST;
      if (errno == ENETUNREACH)
	return ZEBRA_ERR_RTUNREACH;
      
      zlog_warn ("write : %s (%d)", strerror (errno), errno);
      return -1;
    }
  return 0;
}
/* Interface function for the kernel routing table updates.  Support
 * for RTM_CHANGE will be needed.
 * Exported only for rt_socket.c
 */
int
rtm_write (int message,
	   union sockunion *dest,
	   union sockunion *mask,
	   union sockunion *gate,
	   unsigned int index,
	   int zebra_flags,
	   int metric)
{
  int ret;
  caddr_t pnt;
  struct interface *ifp;

  /* Sequencial number of routing message. */
  static int msg_seq = 0;

  /* Struct of rt_msghdr and buffer for storing socket's data. */
  struct 
  {
    struct rt_msghdr rtm;
    char buf[512];
  } msg;
  
  if (routing_sock < 0)
    return ZEBRA_ERR_EPERM;

  /* Clear and set rt_msghdr values */
  memset (&msg, 0, sizeof (struct rt_msghdr));
  msg.rtm.rtm_version = RTM_VERSION;
  msg.rtm.rtm_type = message;
  msg.rtm.rtm_seq = msg_seq++;
  msg.rtm.rtm_addrs = RTA_DST;
  msg.rtm.rtm_addrs |= RTA_GATEWAY;
  msg.rtm.rtm_flags = RTF_UP;
  msg.rtm.rtm_index = index;

  if (metric != 0)
    {
      msg.rtm.rtm_rmx.rmx_hopcount = metric;
      msg.rtm.rtm_inits |= RTV_HOPCOUNT;
    }

  ifp = if_lookup_by_index (index);

  if (gate && message == RTM_ADD)
    msg.rtm.rtm_flags |= RTF_GATEWAY;

  /* When RTF_CLONING is unavailable on BSD, should we set some
   * other flag instead?
   */
#ifdef RTF_CLONING
  if (! gate && message == RTM_ADD && ifp &&
      (ifp->flags & IFF_POINTOPOINT) == 0)
    msg.rtm.rtm_flags |= RTF_CLONING;
#endif /* RTF_CLONING */

  /* If no protocol specific gateway is specified, use link
     address for gateway. */
  if (! gate)
    {
      if (!ifp)
        {
          char dest_buf[INET_ADDRSTRLEN] = "NULL", mask_buf[INET_ADDRSTRLEN] = "255.255.255.255";
          if (dest)
            inet_ntop (AF_INET, &dest->sin.sin_addr, dest_buf, INET_ADDRSTRLEN);
          if (mask)
            inet_ntop (AF_INET, &mask->sin.sin_addr, mask_buf, INET_ADDRSTRLEN);
          zlog_warn ("%s: %s/%s: gate == NULL and no gateway found for ifindex %d",
            __func__, dest_buf, mask_buf, index);
          return -1;
        }
      gate = (union sockunion *) & ifp->sdl;
    }

  if (mask)
    msg.rtm.rtm_addrs |= RTA_NETMASK;
  else if (message == RTM_ADD) 
    msg.rtm.rtm_flags |= RTF_HOST;

  /* Tagging route with flags */
  msg.rtm.rtm_flags |= (RTF_PROTO1);

  /* Additional flags. */
  if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
    msg.rtm.rtm_flags |= RTF_BLACKHOLE;
  if (zebra_flags & ZEBRA_FLAG_REJECT)
    msg.rtm.rtm_flags |= RTF_REJECT;


#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
#define SOCKADDRSET(X,R) \
  if (msg.rtm.rtm_addrs & (R)) \
    { \
      int len = ROUNDUP ((X)->sa.sa_len); \
      memcpy (pnt, (caddr_t)(X), len); \
      pnt += len; \
    }
#else 
#define SOCKADDRSET(X,R) \
  if (msg.rtm.rtm_addrs & (R)) \
    { \
      int len = SAROUNDUP (X); \
      memcpy (pnt, (caddr_t)(X), len); \
      pnt += len; \
    }
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */

  pnt = (caddr_t) msg.buf;

  /* Write each socket data into rtm message buffer */
  SOCKADDRSET (dest, RTA_DST);
  SOCKADDRSET (gate, RTA_GATEWAY);
  SOCKADDRSET (mask, RTA_NETMASK);

  msg.rtm.rtm_msglen = pnt - (caddr_t) &msg;

  ret = write (routing_sock, &msg, msg.rtm.rtm_msglen);

  if (ret != msg.rtm.rtm_msglen) 
    {
      if (errno == EEXIST) 
	return ZEBRA_ERR_RTEXIST;
      if (errno == ENETUNREACH)
	return ZEBRA_ERR_RTUNREACH;
      if (errno == ESRCH)
	return ZEBRA_ERR_RTNOEXIST;
      
      zlog_warn ("%s: write : %s (%d)", __func__, safe_strerror (errno), errno);
      return ZEBRA_ERR_KERNEL;
    }
  return ZEBRA_ERR_NOERROR;
}