#ifdef __linux__
#ifndef USE_LIBPCAP
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <netpacket/packet.h>

#include "pktlib.h"
#include "lib.h"

struct _pktif {
  struct _pktif_base base; /* 共通領域は先頭に置く必要がある */
  struct { /* インターフェースのRAWソケット共通部分 */
    int ifindex;
  } rawsock;
};

static int arptype2dlt(int arptype);

pktif_t pktif_open(char *ifname, unsigned long flags, int option_size)
{
  pktif_t pktif;
  int size, s, ifindex, linktype, buffer_size;
  struct ifreq ifr;
  struct sockaddr_ll sll;
  struct packet_mreq mreq;
  int optval;
  socklen_t optlen;

  size = sizeof(*pktif) + option_size + strlen(ifname) + 1;
  pktif = malloc(size);
  memset(pktif, 0, size);

  s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); /* RAWソケットを開く */
  if (s < 0)
    pktlib_error_exit("Cannot open raw socket.\n");

  /* 情報の取得 */
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
  if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) /* インターフェース番号を取得 */
    pktlib_error_exit("Fail to ioctl SIOCGIFINDEX.\n");
  ifindex = ifr.ifr_ifindex;
  optlen = sizeof(optval);
  if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &optval, &optlen) < 0)
    pktlib_error_exit("Fail to getsockopt SO_RCVBUF.\n");
  buffer_size = optval / 2; /* 受信バッファの必要サイズ */

  /* 受信関連の設定 */
  if (!(flags & PKTIF_OPEN_FLAG_RECV_NOTPROM)) {
    memset(&mreq, 0, sizeof(mreq));
    mreq.mr_type = PACKET_MR_PROMISC; /* 自宛でないパケットも受信する */
    mreq.mr_ifindex = ifindex;
    if (setsockopt(s, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
		   &mreq, sizeof(mreq)) < 0)
      pktlib_error_exit("Fail to setsockopt PACKET_ADD_MEMBERSHIP.\n");
  }

  memset(&sll, 0, sizeof(sll));
  sll.sll_family = AF_PACKET;
  sll.sll_protocol = htons(ETH_P_ALL);
  sll.sll_ifindex = ifindex; /* インターフェースを設定する */
  if (bind(s, (struct sockaddr *)&sll, sizeof(sll)) < 0)
    pktlib_error_exit("Cannot bind.\n");

  /* リンクタイプの取得 */
  memset(&sll, 0, sizeof(sll));
  optlen = sizeof(sll);
  if (getsockname(s, (struct sockaddr *)&sll, &optlen) < 0)
    pktlib_error_exit("Cannot getsockname.\n");
  linktype = arptype2dlt(sll.sll_hatype);

  pktif->base.next = NULL;
  pktif->base.name = (char *)(pktif + 1) + option_size;
  strcpy(pktif->base.name, ifname);
  pktif->base.fd = s;
  pktif->base.flags = flags;
  pktif->base.linktype = linktype;
  pktif->base.buffer_size = buffer_size;
  pktif->base.send_count = 0;
  pktif->base.recv_count = 0;
  pktif->base.option = (option_size > 0) ? (pktif + 1) : NULL;
  pktif->rawsock.ifindex = ifindex;

  pktlib_iflist_set(pktif); /* select()のためにリンクリストに接続する */

  return pktif;
}

int pktif_is_empty(pktif_t pktif)
{
  return 1; /* BPFとの整合性のための関数．常に１を返す */
}

int pktif_recv(pktif_t pktif, char *buffer, int size, struct timeval *tm)
{
  int r;
  socklen_t optlen;
  struct sockaddr_ll sll;

  while (1) {
    optlen = sizeof(sll);
    r = recvfrom(pktif->base.fd, buffer, size, 0,
		 (struct sockaddr *)&sll, &optlen); /* パケットの受信 */
    if (r <= 0)
      return r;
    if (!(pktif->base.flags & PKTIF_OPEN_FLAG_RECV_NOTSENT))
      break;
    if (sll.sll_pkttype != PACKET_OUTGOING) /* 自発のパケットは破棄する */
      break;
  }

  if (tm) { /* 受信時刻を取得 */
    if (ioctl(pktif->base.fd, SIOCGSTAMP, tm) < 0)
      pktlib_error_exit("Fail to ioctl SIOCGSTAMP.\n");
  }

  if (r > 0)
    pktif->base.recv_count++;

  return r;
}

int pktif_send(pktif_t pktif, char *buffer, int size)
{
  int r;
  r = send(pktif->base.fd, buffer, size, 0); /* パケットの送信 */
  if (r > 0)
    pktif->base.send_count++;
  return r;
}

static int arptype2dlt(int arptype)
{
  int dlt = DLT_UNKNOWN;

#ifndef ARPHRD_FDDI
#define ARPHRD_FDDI 774
#endif
#ifndef ARPHRD_ATM
#define ARPHRD_ATM 19
#endif
#ifndef ARPHRD_IEEE80211
#define ARPHRD_IEEE80211 801
#endif
#ifndef ARPHRD_IEEE80211_PRISM
#define ARPHRD_IEEE80211_PRISM 802
#endif
#ifndef ARPHRD_IEEE80211_RADIOTAP
#define ARPHRD_IEEE80211_RADIOTAP 803
#endif
#ifndef ARPHRD_NONE
#define ARPHRD_NONE 0xFFFE
#endif

  switch (arptype) {
  case ARPHRD_ETHER:    dlt = DLT_EN10MB;      break;
  case ARPHRD_LOOPBACK: dlt = DLT_NULL;        break; /* Need cook packet */
  case ARPHRD_EETHER:   dlt = DLT_EN3MB;       break;
  case ARPHRD_AX25:     dlt = DLT_AX25;        break; /* DLT_AX25_KISS? */
  case ARPHRD_PRONET:   dlt = DLT_PRONET;      break;
  case ARPHRD_CHAOS:    dlt = DLT_CHAOS;       break;
  case ARPHRD_IEEE802:  dlt = DLT_IEEE802;     break;
  case ARPHRD_ARCNET:   dlt = DLT_ARCNET;      break; /* DLT_ARCNET_LINUX? */
  case ARPHRD_FDDI:     dlt = DLT_FDDI;        break;
  case ARPHRD_ATM:      dlt = DLT_ATM_RFC1483; break; /* DLT_LINUX_SLL? */
  case ARPHRD_IEEE80211:          dlt = DLT_IEEE802_11;       break;
  case ARPHRD_IEEE80211_PRISM:    dlt = DLT_PRISM_HEADER;     break;
  case ARPHRD_IEEE80211_RADIOTAP: dlt = DLT_IEEE802_11_RADIO; break;
  case ARPHRD_SLIP:     dlt = DLT_SLIP;        break; /* DLT_RAW? */

  case ARPHRD_PPP: /* Need check */
  case ARPHRD_TUNNEL:
  case ARPHRD_NONE:
    dlt = DLT_RAW;
    break;

  default:
    break;
  }

  return dlt;
}
#endif
#endif
