/* kernel.c -- interface to kernel structures/ioctls */

/*
 *  srouted -- silent routing daemon
 *  Copyright (C) 1995 Kevin Buhr
 *
 *  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; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef lint
static char rcsid[] = "$Id: kernel.c,v 1.4 1995/02/20 17:44:16 buhr Exp $";
#endif /* not lint */

#include "defs.h"
#include "table.h"
#include "kernel.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <stdio.h>


static int s;  /* socket for route IOCTLs */

/*
 *	Get configuration of directly connected interfaces
 */

void kr_getifconf(void)
{
   struct ifconf ifc;
   struct ifreq *ife,ifr;
   struct tb_iface *iface;
   int ifi;
   short ifflags;

   if((s=socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
      warn1(ERCKR_NOIPSOCK,errno);
      goto getif_failed;
   }
   ifc.ifc_len=sizeof(page);
   ifc.ifc_buf=page;

      /* get configuration of the interfaces */
   if(ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
      warn2(ERCKR_IOCTL,SIOCGIFCONF,errno);
      goto getif_failed;
   }

      /* step through interfaces */
   for(ife=ifc.ifc_req; (char *) ife < (char *) ifc.ifc_req
                                       +ifc.ifc_len; ife++) {

      if(!ife->ifr_name[0])
	 continue;

      ifi=tb_newiface();
      if(ifi==-1) {
	 die0(ERCKR_IFFULL);
      }

      iface=&tb_iface[ifi];
      memcpy(iface->tbif_dev,ife->ifr_name,IFNAMSIZ);
      iface->tbif_myaddr=ife->ifr_addr;

      memcpy(ifr.ifr_name,iface->tbif_dev,IFNAMSIZ);

#define IFIOCTL(s,num,arg) \
      if(ioctl((s),(num),(arg)) < 0) {		\
         warn2(ERCKR_IOCTL,(num),errno);		\
         continue;				\
      }

      IFIOCTL(s, SIOCGIFFLAGS, (char *)&ifr);
      ifflags=ifr.ifr_flags;
      if((ifflags&IFF_UP)==0) {
	 continue;
      }
      if(ifflags&IFF_LOOPBACK) {
	 iface->tbif_flags|=TBIFF_LOOPBACK;
      }
      if(ifflags&IFF_POINTOPOINT) {
	 iface->tbif_flags|=TBIFF_POINTOPOINT;
	 IFIOCTL(s, SIOCGIFDSTADDR, (char *)&ifr);
	 iface->tbif_dstaddr=ifr.ifr_dstaddr;
      }
      if(ifflags&IFF_BROADCAST) {
	 iface->tbif_flags|=TBIFF_BROADCAST;
	 IFIOCTL(s, SIOCGIFBRDADDR, (char *)&ifr);
	 iface->tbif_broadaddr=ifr.ifr_broadaddr;
      }
      IFIOCTL(s, SIOCGIFNETMASK, (char *)&ifr);
      iface->tbif_netmask=ifr.ifr_netmask;
      IFIOCTL(s, SIOCGIFMETRIC, (char *)&ifr);
      iface->tbif_metric=ifr.ifr_metric+1;
      if(iface->tbif_metric<1 || iface->tbif_metric >= TBM_INFINITY)
	 iface->tbif_metric=1;  /* bad metric, so make one up */
      iface->tbif_flags|=TBIFF_USED|TBIFF_UP;
   }

 getif_failed:
   ;

}


/*
 *	Delete a route from the kernel tables
 */

void kr_delroute( int route )
{
   struct rtentry kr;

   kr.rt_dst = tb_route[route].tbrt_dst;
   kr.rt_dev = NULL;

   if( ioctl( s, SIOCDELRT, (char *) &kr ) < 0 ) {
      warn1(ERCKR_XDELRT, errno);
   }
   note1( ERCKR_DELRT, route );
}


/*
 *	Add a route to the kernel tables
 */

void kr_addroute( int route )
{
   struct rtentry kr;
   struct tb_route *rtp;

   rtp = &tb_route[route];
   kr.rt_dst = rtp->tbrt_dst;
   kr.rt_gateway = rtp->tbrt_gateway;
   kr.rt_genmask = rtp->tbrt_mask;
   kr.rt_metric = rtp->tbrt_metric;
   kr.rt_flags = RTF_UP;
   if( rtp->tbrt_flags & TBRTF_DIRECT ) {
      if( rtp->tbrt_iface != -1 )
	 kr.rt_dev = tb_iface[rtp->tbrt_iface].tbif_dev;
   } else {
      kr.rt_dev = NULL;
      kr.rt_flags |= RTF_GATEWAY;
   }
   if( rtp->tbrt_flags & TBRTF_HOST )
      kr.rt_flags |= RTF_HOST;

   if( ioctl( s, SIOCADDRT, (char *) &kr ) < 0 ) {
      warn1(ERCKR_XADDRT, errno);
   }
   note1( ERCKR_ADDRT, route );
}
