/* $Id: if.c,v 1.1 1999/08/23 16:18:32 naamato Exp $ */
/*
 * Copyright(c) 1998 by Hitachi.Ltd All rights reserved.
 *
 */

#include "defs.h"
#include <sys/param.h>
#include <fcntl.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/route.h>
#ifdef HAVE_NIT
#include <stropts.h>
#include <net/nit.h>
#include <net/nit_if.h>
#include <net/nit_pf.h>
#include <net/packetfilt.h>
#endif /* HAVE_NIT */
#ifdef USE_BPF
#include <nlist.h>
#include <net/bpf.h>
#endif
#include <netinet/if_ether.h>
#include <netinet/in_var.h>

#if (defined(BSD) && (BSD >= 199103))
#include <sys/sysctl.h>
#include <net/if_dl.h>
#endif

#include "interface.h"

#define ETHERHL       sizeof(struct ether_header)  /* ethernet header length */
#define IPHL          sizeof(struct ip)            /* IP header length */
#define UDPHL         sizeof(struct udphdr)        /* UDP header length */
#define BOOTPLEN      300
#define WORD          4                            /* word alignment in bits */
#define QWOFF         2                 /* to make dhcp long to 4w alignment */

struct msg
{
  struct ether_header *ether;
  struct ip *ip;
};

#ifndef NBPFILTER
#define NBPFILTER 10
#endif

#ifdef USE_BPF

#define ETHER_MAP_IP_MULTICAST(ipaddr, enaddr) \
/* struct in_addr *ipaddr; */ \
/* u_char enaddr[ETHER_ADDR_LEN];          */ \
{ \
        (enaddr)[0] = 0x01; \
        (enaddr)[1] = 0x00; \
        (enaddr)[2] = 0x5e; \
        (enaddr)[3] = ((u_char *)ipaddr)[1] & 0x7f; \
        (enaddr)[4] = ((u_char *)ipaddr)[2]; \
        (enaddr)[5] = ((u_char *)ipaddr)[3]; \
}

#endif /* USE_BPF */

static struct msg snd;
static struct msg rcv;

struct if_info *flush;

struct if_info *if_list=NULL;

#ifdef USE_NIT
/*
 *    open the nit and setup 
 */
int open_if(ifinfo)
struct if_info *ifinfo;
{
    u_long i = 0;
    int n;
    struct ifreq ifreq;
    struct strioctl si;
    struct packetfilt pf;
    register u_short *fwp = &pf.Pf_Filter[0];

    /*
     *    open /dev/nit
     */
    if ((ifinfo->fd = open("/dev/nit", O_RDWR)) < 0) {   /* cannot open nit */
	log(LOG_ERR, "open error in open_if()");
	exit(1);
    }

    if (ioctl(ifinfo->fd, I_SRDOPT, (char *)RMSGD) < 0) {
	log(LOG_ERR, "ioctl(I_SRDOPT) error in open_if()");
	exit(1);
    }

    bzero(&si, sizeof(si));
    bzero(&ifreq, sizeof(ifreq));
    strncpy(ifreq.ifr_name, ifinfo->name, sizeof(ifreq.ifr_name));
    ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';

    si.ic_cmd = NIOCBIND;
    si.ic_len = sizeof(ifreq);
    si.ic_dp = (char *) &ifreq;
    if (ioctl(ifinfo->fd, I_STR, (char *) &si) < 0) {
	log(LOG_ERR, "ioctl(NIOCBIND) error in open_if()");
	exit(1);
    }

    bzero(&si, sizeof(si));
    si.ic_cmd = NIOCSSNAP;
    si.ic_len = sizeof(i);
    si.ic_dp = (char *)&i;
    if (ioctl(ifinfo->fd, I_STR, (char *) &si) < 0) {
	log(LOG_ERR, "ioctl(NIOCSSNAP) error in open_if()");
	exit(1);
    }

    /*
     * Initialize the interface information. subnet and IP address
     */
    if ((n = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	log(LOG_ERR, "socket() in open_if()");
	exit(1);
    }
#if 0
    if (ioctl(n, SIOCGIFNETMASK, &ifreq) < 0) {
	log(LOG_ERR, "ioctl(SIOCGIFNETMASK) in open_if()");
	exit(1);
    }
    if ((ifinfo->subnetmask =
	 (struct in_addr *) calloc(1, sizeof(struct in_addr))) == NULL) {
	log(LOG_ERR, "calloc error in open_if()");
	exit(1);
    }
    ifinfo->subnetmask->s_addr =
	((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;

    if (ioctl(n, SIOCGIFADDR, &ifreq) < 0) {
	log(LOG_ERR, "ioctl(SIOCGIFADDR) in open_if()");
	exit(1);
    }
    if ((ifinfo->ipaddr =
	 (struct in_addr *) calloc(1, sizeof(struct in_addr))) == NULL) {
	log(LOG_ERR, "calloc in open_if()");
	exit(1);
    }
    ifinfo->ipaddr->s_addr = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;
    close(n);


    ifinfo->htype = 1;
    ifinfo->hlen = 6;
    if (getmac(ifinfo->name, ifinfo->haddr) < 0) {
	exit(1);
    }
#endif

    if (ioctl(ifinfo->fd, I_PUSH, "pf") < 0) {
	log(LOG_ERR, "ioctl(I_PUSH) error in open_if()");
	exit(1);
    }

    *fwp++ = ENF_PUSHWORD + 6;
    *fwp++ = ENF_PUSHLIT | ENF_CAND;
    *fwp++ = htons(ETHERTYPE_IP);
    *fwp++ = ENF_PUSHWORD + 11;
    *fwp++ = ENF_PUSHLIT | ENF_AND;
    *fwp++ = htons(0x00ff);
    *fwp++ = ENF_PUSHLIT | ENF_CAND;
    *fwp++ = htons(IPPROTO_UDP);
#ifdef NOIPOPTION
    *fwp++ = ENF_PUSHWORD + 18;
    *fwp++ = ENF_PUSHLIT | ENF_CAND;
    *fwp++ = dhcps_port;
#endif

    pf.Pf_FilterLen = fwp - &pf.Pf_Filter[0];

    bzero(&si, sizeof(si));
    si.ic_cmd = NIOCSETF;
    si.ic_timout = INFTIM;
    si.ic_len = sizeof (pf);
    si.ic_dp = (char *)&pf;
    if (ioctl(ifinfo->fd, I_STR, (char *)&si) < 0){
	log(LOG_ERR, "ioctl(NIOCSETF) error in open_if()");
	exit(1);
    }

    if (ioctl(ifinfo->fd, I_FLUSH, (char *)FLUSHR) < 0) {
	log(LOG_ERR, "ioctl(I_FLUSH) error in open_if()");
	exit(1);
    }

    ifinfo->buf_size = 8192;  /* XXX */
    if ((ifinfo->buf = (char *)calloc(1, ifinfo->buf_size)) == NULL) {
	log(LOG_ERR, "calloc() error in open_if()");
	exit(1);
    }
    return(0);
}

#endif /* USE_NIT */

#ifdef USE_BPF

struct bpf_insn etherfilter[] = {
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),                  /* A <- ETHER_TYPE */
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 8), /* IP ? */
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20),                  /* A <- IP FRAGMENT */
    BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x1fff, 6, 0),      /* OFFSET == 0 ? */
    BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 23),                  /* A <- IP_PROTO */
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 89, 0, 4),           /* UDP ? */
/*  BPF_STMT(BPF_LDX+BPF_B+BPF_MSH, 14),*/               /* X <- IPHDR LEN */
/*  BPF_STMT(BPF_LD+BPF_H+BPF_IND, 16), */               /* A <- UDP DSTPORT */
/* BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 1),*/          /* check DSTPORT */
    BPF_STMT(BPF_RET+BPF_K, (u_int)-1),	                 /* return all*/
    BPF_STMT(BPF_RET+BPF_K, 0)			         /* ignore */
};
#define DSTPORTIS   8    /* UDP destination port variable */

/*
 *    open the bpf and setup 
 */
int if_init(ifinfo)
struct if_info *ifinfo;
{
    char dev[sizeof "/dev/bpfxx"];
    int n = 0;
    int immediate = 0;                /* immediately return from select() */
    struct ifreq ifreq;
    static struct bpf_program etherprogram = {
	sizeof(etherfilter) / sizeof(struct bpf_insn),
	etherfilter
    };

    /*
     *	open /dev/bpf*
     */
    n = 0;
    do {
	(void) sprintf(dev, "/dev/bpf%d", n++);
	ifinfo->fd = open(dev, O_RDWR);
    } while (ifinfo->fd < 0 && n < NBPFILTER);

    if (ifinfo->fd < 0) {           /* cannot open bpf */
	perror("can't open bpf");
	exit(1);
    }

    /*
     * Initialize the interface information. subnet and IP address
     */
    bzero(&ifreq, sizeof(ifreq));
    if ((n = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	perror("socket() in init_bpf()");
	exit(1);
    }
#if 0
    strcpy(ifreq.ifr_name, ifinfo->name);
    if (ioctl(n, SIOCGIFNETMASK, &ifreq) < 0) {
	perror("ioctl(SIOCGIFNETMASK) in init_bpf()");
	exit(1);
    }
    if ((ifinfo->subnetmask = 
	 (struct in_addr *) calloc(1, sizeof(struct in_addr))) == NULL) {
	perror("calloc error in open_if()");
	exit(1);
    }
    ifinfo->subnetmask->s_addr =
	((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;

    if (ioctl(n, SIOCGIFADDR, &ifreq) < 0) {
	perror("ioctl(SIOCGIFADDR) in init_bpf()");
	exit(1);
    }
    if ((ifinfo->ipaddr =
	 (struct in_addr *) calloc(1, sizeof(struct in_addr))) == NULL) {
	perror("calloc in open_if()");
	exit(1);
    }
    ifinfo->ipaddr->s_addr = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;
    close(n);
#endif 

    /*
     * set immediate mode
     */
    immediate = 1;
    if (ioctl(ifinfo->fd, BIOCIMMEDIATE, &immediate) < 0) {
	perror("ioctl(BIOCIMMEDIATE) in init_bpf()");
	exit(1);
    }

    /*
     * bind interface to bpf
     */
    strcpy(ifreq.ifr_name, ifinfo->name);
    if (ioctl(ifinfo->fd, BIOCSETIF, &ifreq) < 0) {
	perror("ioctl(BIOCSETIF) in init_bpf()");
	exit(1);
    }

    /*
     *	get buffer_length and allocate buffer
     */
    if (ioctl(ifinfo->fd, BIOCGBLEN, &ifinfo->buf_size) < 0) {
	perror("ioctl(BIOCGBLEN) in init_bpf()");
	exit(1);
    }

    if ((ifinfo->buf = calloc(1, ifinfo->buf_size)) == NULL) {
	perror("calloc in init_bpf()");
	exit(1);
    }
  

    /*
     * get the datalink type
     */
    if (ioctl(ifinfo->fd, BIOCGDLT, &ifinfo->htype) < 0) {
	perror("ioctl(BIOCGDLT) in open_if()");
	exit(1);
    }
#if 0
    ifinfo->hlen = 6;
    if (ioctl(ifinfo->fd, SIOCGIFADDR, &ifreq) < 0) {
	perror("ioctl(SIOCGIFADDR) error in init_bpf()"); 
	exit(1);
    }
    bcopy(ifreq.ifr_addr.sa_data, ifinfo->haddr, 6);

    if (getmac(ifinfo->name, ifinfo->haddr) < 0) {
	exit(1);
    }

    /* setup filter */
    etherfilter[DSTPORTIS].k = ntohs(dhcps_port);   /* host byte order */

    /* bind filter to bpf */
    if (ioctl(ifinfo->fd, BIOCSETF, &etherprogram) < 0) {
	perror("ioctl(BIOCSETF) to ether in open_if()");
	exit(1);
    }

#endif
    if (ioctl(ifinfo->fd, BIOCFLUSH) < 0) {
	perror("ioctl(BIOCFLUSH) in open_if()");
	exit(1);
    }

    return(0);
}
#endif /* USE_BPF */

#ifdef USE_BPF
/*
 * select and read from the interface
 */
struct if_info *read_interfaces(iflist, n)
struct if_info *iflist;
int *n;
{
    struct if_info *ifp;
    int nfound = 0;                  /* used in select */
    fd_set readfds;                  /* used in select */
    static int maxfd = 0;            /* maximum number of bpf descriptors */
    static int first = 0;
    static fd_set backup;
#ifdef sun 
    int offset;
#endif
    struct timeval t;

    t.tv_sec=0;
    t.tv_usec = 0;

    /* set up for select() */
    if (first == 0) {
	first = 1;
	FD_ZERO(&backup);
	ifp = iflist;
	while (ifp != NULL) {
	    FD_SET(ifp->fd, &backup);
	    if (maxfd < ifp->fd)
		maxfd = ifp->fd;
	    ifp = ifp->next;
	}
    }
    readfds = backup;

    /* block till some packet arrive */
    if ((nfound = select(maxfd + 1, &readfds, NULL, NULL, &t)) < 0) {
	log(LOG_WARNING, "select() fail in read_interfaces()");
	return(NULL);
    }

    /* determine the descriptor to be read */
    ifp = iflist;
    while (ifp != NULL) {
	if (FD_ISSET(ifp->fd, &readfds)) break;
	else ifp = ifp->next;
    }
    if (ifp == NULL) {
	log(LOG_WARNING, "select() fail in read_interfaces()");
	return(NULL);
    }

#ifdef sun
    if (ifp == iflist) offset = 0;  /* It's normal socket */
    else offset = QWOFF;

    if ((*n = read(ifp->fd, &ifp->buf[offset], ifp->buf_size - offset)) < 0) {
	log(LOG_WARNING, "read from nit");
	return(NULL);
    }
#else /* not sun */
    if ((*n = read(ifp->fd, ifp->buf, ifp->buf_size)) < 0) {
	log(LOG_WARNING, "read from bpf or socket");
	return(NULL);
    }
#endif

    return(ifp);
}  


/* Cast a struct sockaddr to a sockaddr_in */
#define SATOSIN(sa) ((struct sockaddr_in *)(sa))

/* Determine if "bits" is set in "flag" */
#define ALLSET(flag, bits) (((flag) & (bits)) == (bits))

static struct nlist nl[] = {
#define N_IFNET 0
	{ "_ifnet" },
	{ 0 }
};

#ifdef USE_NIT
int getmac(ifname, ha_addr)
char *ifname;
char *ha_addr;
{
    register struct ifnet *ifp = NULL;
    register struct arpcom *ac = NULL;
    register u_char *ep = NULL;
    struct arpcom arpcom;
    register struct ifaddr *ifa = NULL;
    register struct in_ifaddr *in = NULL;
    union {
	struct ifaddr ifa;
	struct in_ifaddr in;
    } ifaddr;
    u_long addr = 0;
    char name[16];
    char fullname[16];
    static kvm_t *kd = NULL;

    bzero(&arpcom, sizeof(arpcom));
    bzero(&ifaddr, sizeof(ifaddr));
    bzero(name, sizeof(name));
    bzero(fullname, sizeof(fullname));

    /* Open kernel memory for reading */
    kd = kvm_open(0, 0, 0, O_RDONLY, "kvm_open");
    if (kd == 0) {
	perror("kvm_open() error in getmac()");
	return(-1);
    }

    /* Fetch namelist */
    if (kvm_nlist(kd, nl) != 0) {
	perror("bad nlist in getmac()");
	kvm_close(kd);
	return(-1);
    }

    ac = &arpcom;
    ifp = &arpcom.ac_if;
    ep = arpcom.ac_enaddr.ether_addr_octet;

    ifa = &ifaddr.ifa;
    in = &ifaddr.in;

    if (kvm_read(kd, nl[N_IFNET].n_value, (char *)&addr, sizeof(addr)) !=
               sizeof(addr)) {
	perror("kvm_read() error in getmac()");
	kvm_close(kd);
	return(-1);
    }
    for ( ; addr; addr = (u_long)ifp->if_next) {
	if (kvm_read(kd, addr, (char *)ac, sizeof(*ac)) != sizeof(*ac)) {
	    perror("kvm_read() error in getmac()");
	    kvm_close(kd);
	    return(-1);
	}
	/* Only look at configured, broadcast interfaces */
	if (!ALLSET(ifp->if_flags, IFF_UP | IFF_BROADCAST))
	    continue;

	/* Only look at the specified interface */
	if (kvm_read(kd, (u_long)ifp->if_name, (char *)name, sizeof(name)) !=
                 sizeof(name)) {
	    perror("kvm_read() error in getmac()");
	    kvm_close(kd);
	    return(-1);
	}
	name[sizeof(name) - 1] = '\0';
	sprintf(fullname, "%s%d", name, ifp->if_unit);
	printf("%s%d", name, ifp->if_unit);
	if (strcmp(fullname, ifname) != 0)
	    continue;

	kvm_close(kd);
	bcopy(ep, ha_addr, 6);
	return(0);
    }

    kvm_close(kd);
    errno = 0;
    perror("getmac() is failed");
    return(-1);
}

#endif  /* USE_NIT */

void set_uni(list, dst)
struct if_info *list;
u_long dst;
{
    int mib[6];
    size_t needed;
    char *malloc(),*lim,*buf,*next;
    struct rt_msghdr *rtm;
    struct sockaddr_inarp *sin;
    struct sockaddr_dl *sdl;
    u_char *ha_addr;
    int i;
    struct if_info *ifp;

    ifp = list;
    mib[0] = CTL_NET;
    mib[1] = PF_ROUTE;
    mib[2] = 0;
    mib[3] = AF_INET;
    mib[4] = NET_RT_FLAGS;
    mib[5] = RTF_LLINFO;
    if(sysctl(mib, 6, NULL, &needed, NULL, 0) <0) {
	exit(0);
    }
    if((buf=malloc(needed)) == NULL) {
	exit(0);
    }
    if(sysctl(mib, 6, buf, &needed, NULL, 0) <0) {
	exit(0);
    }
    lim = buf+needed;
    for(next=buf; next < lim; next += rtm->rtm_msglen) {
	rtm = (struct rt_msghdr *)next;
	sin = (struct sockaddr_inarp *)(rtm + 1);
	sdl = (struct sockaddr_dl *)(sin + 1) ;
	if(htonl(dst) == sin->sin_addr.s_addr) break;
    }

    ha_addr = LLADDR(sdl);
    for (i = 0; i < 6; i++) {
#ifdef sun
	snd.ether->ether_dhost.ether_addr_octet[i] = rcv.dhcp->chaddr[i];
	snd.ether->ether_shost.ether_addr_octet[i] = ifp->haddr[i];
#else
	snd.ether->ether_dhost[i] = ha_addr[i];
	snd.ether->ether_shost[i] = (u_char)ifp->haddr[i];
#endif
    }
    free(buf);
}

void set_multi(list, dst) 
struct if_info *list;
u_long dst;
{
    struct in_addr ipaddr;
    u_char ha_addr[6];
    int i;
    struct if_info *ifp;

    ifp = list;
    ipaddr.s_addr = htonl(dst);
    ETHER_MAP_IP_MULTICAST(&ipaddr, ha_addr);

    for (i = 0; i < 6; i++) {
#ifdef sun
	snd.ether->ether_dhost.ether_addr_octet[i] = rcv.dhcp->chaddr[i];
	snd.ether->ether_shost.ether_addr_octet[i] = ifp->haddr[i];
#else
	snd.ether->ether_dhost[i] = ha_addr[i];
	snd.ether->ether_shost[i] = (u_char)ifp->haddr[i];
#endif
    }

}

/*
 * calculate the check sum for IP suit
 */
u_short cksum(buf, n)
u_short *buf;
int n;
{
    u_long sum;
    u_short result;

    for (sum = 0; n > 0; n--) {
	sum += *buf++;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    result = (u_short) (~sum);
    if (result == 0)
	result = 0xffff;

    return(result);
}

/*
 * return IP checksum in network byte order
 */
u_short get_ipsum(ipp)
struct ip *ipp;
{
    ipp->ip_sum = 0;

    return(cksum((u_short *)ipp, ipp->ip_hl * 2));
}

void send_interface(list, ifa,dst, pkt, len)
struct if_info *list;
struct if_addr *ifa;
u_long dst;
char *pkt;
int len;
{
    int i, total;
    struct if_info *ifp;
    int msgtype = 0;
    char *option = NULL;
    char *buf;
    int dummy;

    ifp = list;
/*
    while (ifp != NULL) {
	if (ifp->ipaddr != NULL)
	break;
	ifp = ifp->next;
    }
*/
    if (ifp == NULL) { /* there is no corresponding interfaces */
	perror("message from server has a wrong giaddr");
	return;
    }

    buf = malloc(len + IPHL + ETHERHL);
    bzero(buf,len+IPHL+ETHERHL);
    /* Already, there is space for ETHER, IP, UDP header */
/*  snd.dhcp = NULL;
    snd.udp = (struct udphdr *) (buf + ETHERHL + IPHL);*/
    bcopy(pkt, buf+IPHL+ETHERHL, len);
    snd.ip = (struct ip *)(buf + ETHERHL);
    snd.ether = (struct ether_header *)buf;
 
    if(IN_MULTICAST(dst)) {
	set_multi(ifp, dst);
    }
    else {
	set_uni(ifp,dst);
    }
    snd.ip->ip_dst.s_addr = htonl(dst);
    snd.ip->ip_src.s_addr = htonl(ifa->addr->s_addr);
 /* printf("src = %s\n", inet_ntoa(snd.ip->ip_src));
    printf("dst = %s\n", inet_ntoa(snd.ip->ip_dst));*/
    snd.ether->ether_type = htons(ETHERTYPE_IP);
/*  snd.udp->uh_sport = dhcps_port;
    snd.udp->uh_dport = dhcpc_port;
    snd.udp->uh_ulen = htons(dhcplen + UDPHL);
    snd.udp->uh_sum = get_udpsum(snd.ip, snd.udp);*/
    snd.ip->ip_v = IPVERSION;
    snd.ip->ip_hl = IPHL >> 2;
    snd.ip->ip_tos = 0;
    snd.ip->ip_len = htons(len + IPHL);
  /*snd.ip->ip_id = snd.udp->uh_sum;*/
    snd.ip->ip_ttl = 0x01;                       /* XXX */
    snd.ip->ip_p = 89;

    total = len + IPHL + ETHERHL;
    if (total <= ETHERMTU) {
	snd.ip->ip_off = 0;
	snd.ip->ip_sum = get_ipsum(snd.ip);
	dummy = write(ifp->fd, (char *)snd.ether, total);
    } else {
#define  MTU  (ETHERMTU - IPHL)
/*    char *n, *end, *begin;
      struct iovec sbufvec[2];

      sbufvec[0].iov_base = (char *) snd.ether;
      sbufvec[0].iov_len = ETHERHL + IPHL;
      begin = (char *) snd.udp;
      end = &begin[total];

      for (n = begin; n < end; n += MTU) {
      sbufvec[1].iov_base = n;
      if ((end - n) >= MTU) {
	sbufvec[1].iov_len = MTU;
	snd.ip->ip_off = htons(IP_MF | ((((n - begin) / 8)) & 0x1fff));
	snd.ip->ip_sum = get_ipsum(snd.ip);
      } else {
	sbufvec[1].iov_len = end - n;
	snd.ip->ip_off = htons(((n - begin) / 8) & 0x1fff);
	snd.ip->ip_sum = get_ipsum(snd.ip);
      }
      ether_writev(ifp->fd, sbufvec, 2);
      }
 */
    }

/*
   (void)read_interfaces(ifp,&dummy);
   printf("read %d \n",dummy);
*/
    free(buf);
/*  if (ioctl(ifp->fd, BIOCFLUSH) < 0) {
    perror("ioctl(BIOCFLUSH) in send_interface()");
    exit(1);
  }
*/
    return;
};

#endif /* USE_BPF */

#if (defined(BSD) && (BSD >= 199103))

#define ROUNDUP(a) \
       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))

void rt_xaddrs(cp, cplim, rtinfo)
char *cp, *cplim;
struct rt_addrinfo *rtinfo;
{
    struct sockaddr *sa;
    int i;

    memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
    for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
        if ((rtinfo->rti_addrs & (1 << i)) == 0)
	   continue;
	rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
	ADVANCE(cp, sa);
    }
}

int interface_init()
{
    int mib[6];
    int needed;
    char *buf,*lim, *next, *mynext;
    struct if_msghdr *ifm,*myifm;
    struct ifa_msghdr *ifam;
    struct sockaddr_dl *sdl;
    char name[32];
    struct sockaddr_in *sin,*sin_mask,*sin_brd;
    struct rt_addrinfo rt_info;
    int flags;
    struct if_info *ifp,*ifp1;
    struct if_addr *ifaddr,*ifap;

    mib[0] = CTL_NET;
    mib[1] = PF_ROUTE;
    mib[2] = 0;
    mib[3] = 0;
    mib[4] = NET_RT_IFLIST;
    mib[5] = 0;

    if(sysctl(mib, 6, NULL, (void *)&needed, NULL, 0) <0) {
	perror("sysctl:");
    }
    if((buf = malloc(needed)) == NULL) {
	perror("malloc");
    }
    if(sysctl(mib, 6, buf,(void *)&needed, NULL, 0) < 0) {
	perror("sysctl:");
    }

    lim = buf+needed;
    for(next = buf;next < lim; next += ifm->ifm_msglen) {
        ifm = (struct if_msghdr *)next;

	if(ifm->ifm_type == RTM_NEWADDR)
	    continue;

	if(ifm->ifm_type == RTM_IFINFO) {
	    sdl = (struct sockaddr_dl *)(ifm+1);
	    flags = ifm->ifm_flags;
	}
	else {
	    printf(" NET_RT_IFLIST\n");
	}
	strncpy(name, sdl->sdl_data, sdl->sdl_len);
	name[sdl->sdl_len] = '\n';
	
	if(!(flags & IFF_MULTICAST)) {
	  /*    printf(" %s don't support multicast \n",name);*/
	    continue;
	}

	myifm= ifm;
	ifp = (struct if_info *)malloc(sizeof(struct if_info));
	bzero(ifp,sizeof(struct if_info));
	strcpy(ifp->name,name);
	ifp->flag = flags;
	while(1) {
	    mynext = next + ifm->ifm_msglen;

	    if(mynext >= lim)
	        break;

	    myifm = (struct if_msghdr *)mynext;

	    if(myifm->ifm_type != RTM_NEWADDR)
	        break;

	    next = mynext;
	    ifm = (struct if_msghdr *)next;
	    ifam = (struct ifa_msghdr *)myifm;
	    rt_info.rti_addrs = ifam->ifam_addrs;
	    rt_xaddrs((char *)(ifam +1), ifam->ifam_msglen + (char *)ifam,&rt_info);
	    sin = (struct sockaddr_in *)rt_info.rti_info[RTAX_IFA];
	    sin_mask = (struct sockaddr_in *)rt_info.rti_info[RTAX_NETMASK];
	    sin_brd = (struct sockaddr_in *)rt_info.rti_info[RTAX_BRD];
	    if(sin && sin->sin_family == AF_INET) {
	        if(!strcmp(ifp->name,"lo0")) break;
		ifaddr = (struct if_addr *)malloc(sizeof(struct if_addr));
		ifaddr->addr = (struct in_addr *)malloc(sizeof(struct in_addr));
		ifaddr->net_mask = (struct in_addr *)malloc(sizeof(struct in_addr));
		ifaddr->broadcast = (struct in_addr *)malloc(sizeof(struct in_addr));
	        IF_ADDR(ifaddr) = ntohl(sin->sin_addr.s_addr);
	        IF_NETMASK(ifaddr) = ntohl(sin_mask->sin_addr.s_addr);
		IF_BRDCAST(ifaddr) = ntohl(sin_brd->sin_addr.s_addr);
		ifaddr->info = ifp;

	        if(!ifp->ipaddr) {
		    ifp->ipaddr = ifaddr;
		}
		else {
		    for(ifap=ifp->ipaddr;ifap->next;ifap=ifap->next) ;
		    ifap->next = ifaddr;
		}
	    }
	}
	if(!ifp->ipaddr) {
	    free(ifp);
	    continue;
	}

	if(if_list) {
	    ifp1->next = ifp;
	    ifp1 = ifp;
	}
	else {
	    if_list = ifp1 = ifp;
	}
    }
    free(buf);
    /*for(ifp=if_list;ifp;ifp=ifp->next) {
        printf("interface : %s\n",ifp->name);
	for(ifap=ifp->ipaddr;ifap;ifap=ifap->next) {
	    printf("\t inet %s\n",inetaddr_to_char(IF_ADDR(ifap)));
	}
    }*/
}

#else /* defined(BSD) && BSD >= 199103 */

int interface_init()
{
    static char buffer[32 * sizeof(struct ifreq) + sizeof(struct ifconf)];
    struct ifconf ifc;
    struct ifreq *p,*last,ifr;
    u_long addr;
    char name[32];
    struct sockaddr_in *sin,*sin_mask;
 /*   struct rt_addrinfo rt_info;*/
    int flags;
    struct if_info *ifp,*ifp1;
    struct if_addr *ifaddr,*ifap;
    int fd,n=0;

    fd = socket(AF_INET,SOCK_DGRAM,PF_UNSPEC);
    if(!fd) return;

    ifc.ifc_buf = buffer;
    ifc.ifc_len = sizeof(buffer);
    if(ioctl(fd,SIOCGIFCONF,(char *)&ifc)<0) {
        perror("ioctl(SIOCGIFCONF):");
        return;
    }
    last = ((struct ifreq *)ifc.ifc_req) + ifc.ifc_len;
    for(p = (struct ifreq *)ifc.ifc_req; p<last; p++) {
        if(strncmp(p->ifr_name,"",IFNAMSIZ) == 0)
          continue;

        if(p->ifr_addr.sa_family != AF_INET)
          continue;
 
	ifp = (struct if_info *)malloc(sizeof(struct if_info));       
        strcpy(ifp->name,p->ifr_name);
	if(!strcmp(ifp->name,"lo0")) 
	    continue;

	ifaddr = (struct if_addr *)malloc(sizeof(struct if_addr));
	ifaddr->addr = (struct in_addr *)malloc(sizeof(struct in_addr));
        sin = (struct sockaddr_in *)&(p->ifr_addr);
        IF_ADDR(ifaddr) = ntohl(sin->sin_addr.s_addr);
        /*printf("if: %s %s",p->ifr_name,inetaddr_to_char(IF_ADDR(ifaddr)));*/
        bcopy(p->ifr_name,ifr.ifr_name,sizeof(ifr.ifr_name));
        if(ioctl(fd,SIOCGIFNETMASK,(char *)&ifr)<0) {
            perror("ioctl(SIOCGIFNETMASK):");
            return;
        }       
	ifaddr->net_mask = (struct in_addr *)malloc(sizeof(struct in_addr));
        sin_mask = (struct sockaddr_in *)&(ifr.ifr_addr);
        IF_NETMASK(ifaddr) = ntohl(sin_mask->sin_addr.s_addr);
        /*printf("/ %s\n",inetaddr_to_char(IF_NETMASK(ifaddr)));*/
        ifp->ipaddr = ifaddr;
	ifaddr->info = ifp;
        
        if(if_list) {
            ifp1->next = ifp;
            ifp1 = ifp;
        }
        else {
            if_list = ifp1 = ifp;
        }
    }
    close(fd);
#if 0
    for(ifp=if_list;ifp!=NULL;ifp=ifp->next) {
      printf(" %s \n",ifp->name);
    }
#endif
}

#endif /* defined(BSD) && BSD >= 199103 */

struct if_addr *search_if_info_addr(addr)
u_long addr;
{
    struct if_info *ifp;
    struct if_addr *ifap;

    /*    printf(" search %s\n",inetaddr_to_char(addr));*/
    for(ifp=if_list;ifp;ifp=ifp->next) {
	for(ifap=ifp->ipaddr;ifap;ifap=ifap->next) {
	    if(addr == IF_ADDR(ifap))
	        return ifap;
	}
    }
    return NULL;
}

struct if_info *bind_interface(addr)
u_long addr;
{
    struct if_info *ifp;
    struct if_addr *ifaddr;

    for(ifp=if_list; ifp; ifp=ifp->next) {
        for(ifaddr=ifp->ipaddr;ifaddr;ifaddr=ifaddr->next) {
	    if(IF_ADDR(ifaddr) == addr) { 
	/*        printf("bind_interface %s -> %s\n",inetaddr_to_char(addr),ifp->name); */
		return ifp;
	    }

	}
    }

    return NULL;
}

struct if_addr *interface_addr(addr)
u_long addr;
{
    struct if_info *ifp;
    struct if_addr *ifaddr;

    for(ifp=if_list; ifp; ifp=ifp->next) {
        for(ifaddr=ifp->ipaddr;ifaddr;ifaddr=ifaddr->next) {
	    if(IF_ADDR(ifaddr) == addr) { 
	/*        printf("bind_interface %s -> %s\n",inetaddr_to_char(addr),ifp->name); */
		return ifaddr;
	    }

	}
    }

    return NULL;
}

#ifdef USE_BPF

struct if_addr *recv_packet_bpf(src, dst, pkt)
u_long *src;
u_long *dst;
byte *pkt;
{
    struct bpf_hdr *rbpf;
    struct if_info *ifp;
    int n;
    char *rbufp;
    int hlen,plen;
    struct if_addr *ifaddr;

  read:
    n=0;
    if((ifp = read_interfaces(if_list, &n)) == NULL) {
	return 0;
    }

    rbufp = ifp->buf;
    rbpf = (struct bpf_hdr *)rbufp;
    rcv.ether = (struct ether_header *)&rbufp[rbpf->bh_hdrlen];
    rcv.ip = (struct ip *)&rbufp[rbpf->bh_hdrlen + ETHERHL];

    if(rcv.ip->ip_p != 89)
	return NULL;

    *src = ntohl(rcv.ip->ip_src.s_addr);
    *dst = ntohl(rcv.ip->ip_dst.s_addr);
    hlen = rcv.ip->ip_hl << 2;
    plen = rbpf->bh_hdrlen + ETHERHL + rcv.ip->ip_len;

    printf(" packet receive from %s, %s",ifp->name,inetaddr_to_char(*src));
    printf(" -> %s (%d,%d,%d,%d,%d)\n",inetaddr_to_char(*dst),n,rbpf->bh_hdrlen,ETHERHL,IPHL,ntohs(rcv.ip->ip_len)-IPHL);

    if(IN_MULTICAST(*dst) && !(ifaddr=search_if_info_addr(*src))) {
        bcopy(&rbufp[rbpf->bh_hdrlen + ETHERHL + IPHL],pkt,n-ETHERHL-IPHL);
	if(plen < n) {
/*	    if (ioctl(ifp->fd, BIOCFLUSH) < 0) {
	      perror("ioctl(BIOCFLUSH) in send_interface()");
	      exit(1);
	    }*/
	    flush = ifp;
	  }
	return ifp->ipaddr ;
    }
    else if(ifaddr=search_if_info_addr(*dst)) {
        bcopy(&rbufp[rbpf->bh_hdrlen + ETHERHL + IPHL],pkt,n-ETHERHL-IPHL);
        return ifaddr;
    }
    else {
      printf(" drop packet \n");
/*      if (ioctl(ifp->fd, BIOCFLUSH) < 0) {
	perror("ioctl(BIOCFLUSH) in recv_paket_bpf()");
	exit(1);
      }*/
/*      return NULL;*/
        goto read;
    }
}

void bpf_flush()
{
    if(flush) {
       if (ioctl(flush->fd, BIOCFLUSH) < 0) {
	   perror("ioctl(BIOCFLUSH) in bpf_flush()");
	   exit(1);
	 }
    }
    flush = NULL;
}



struct if_addr *dummy_recv()
{
    struct bpf_hdr *rbpf;
    struct if_info *ifp;
    int n;
    char *rbufp;
    int hlen,plen;
    struct if_addr *ifaddr;
    u_long src,dst;

    n=0;
    if((ifp = read_interfaces(if_list, &n)) == NULL) {
      return 0;
    }

    rbufp = ifp->buf;
    rbpf = (struct bpf_hdr *)rbufp;
    rcv.ether = (struct ether_header *)&rbufp[rbpf->bh_hdrlen];
    rcv.ip = (struct ip *)&rbufp[rbpf->bh_hdrlen + ETHERHL];

    if(rcv.ip->ip_p != 89)
      return NULL;

    src = ntohl(rcv.ip->ip_src.s_addr);
    dst = ntohl(rcv.ip->ip_dst.s_addr);
    hlen = rcv.ip->ip_hl << 2;
    plen = rbpf->bh_hdrlen + ETHERHL + rcv.ip->ip_len;

    printf(" packet receive from %s, %s",ifp->name,inetaddr_to_char(src));
    printf(" -> %s (%d,%d,%d,%d,%d)\n",inetaddr_to_char(dst),n,rbpf->bh_hdrlen,ETHERHL,IPHL,ntohs(rcv.ip->ip_len)-IPHL);

    if(IN_MULTICAST(dst) && !(ifaddr=search_if_info_addr(src))) {
	if(plen < n) {
/*	    if (ioctl(ifp->fd, BIOCFLUSH) < 0) {
	      perror("ioctl(BIOCFLUSH) in send_interface()");
	      exit(1);
	    }*/
	    flush = ifp;
	  }
	return ifp->ipaddr ;
    }
    else if(ifaddr=search_if_info_addr(dst)) {
        return ifaddr;
    }
    else {
      printf(" drop packet \n");
/*      if (ioctl(ifp->fd, BIOCFLUSH) < 0) {
	perror("ioctl(BIOCFLUSH) in recv_paket_bpf()");
	exit(1);
      }*/
/*      return NULL;*/
    }
}

#endif /* USE_BPF */

/*
   recv_interface 1998.3.28

*/
struct if_addr *recv_interface(addr)
u_long addr;
{
    struct if_info *ifp;
    struct if_addr *ifap;

    for(ifp=if_list;ifp;ifp=ifp->next) {
	for(ifap=ifp->ipaddr;ifap;ifap=ifap->next) {
	    if( (IF_ADDR(ifap) & IF_NETMASK(ifap)) == (addr & IF_NETMASK(ifap))) return ifap;
	}
    }

    return NULL;
}

u_long get_ifaddr_byname(if_name)
char *if_name;
{
    struct if_info *ifp;
    struct if_addr *ifap;

    if(!if_name) {
        ifp = if_list;
	if(ifp)
	    return IF_ADDR(ifp->ipaddr);
    }

    for(ifp=if_list;ifp;ifp=ifp->next) {
	for(ifap=ifp->ipaddr;ifap;ifap=ifap->next) {
	    if(!strcmp(ifp->name,if_name))
	        return IF_ADDR(ifap);
	}
    }

    return 0;
}

struct if_info *if_withaddr(addr)
u_long addr;
{
    struct if_info *ifp;
    struct if_addr *ifap;
    u_long my_addr;
    u_long netmask;

    for(ifp=if_list;ifp;ifp=ifp->next) {
	for(ifap=ifp->ipaddr;ifap;ifap=ifap->next) {
	    my_addr = IF_ADDR(ifap);
	    netmask = IF_NETMASK(ifp->ipaddr);
	    if( (addr & netmask) == (my_addr & netmask))
		return ifp;
	}
    }

    return NULL;
}
