#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <stdbool.h>
#include <errno.h>

static void
usage (void) {
  fprintf(stderr,
	  "Usage: health_check"
	  " -d debug"
	  " -I interface"
	  "\n");
  exit(2);
}

int
main (int argc, char **argv)
{
  int sd, ch;
  uint8_t *opt, *pkt;
  struct ifreq ifr;
  char *interface = NULL;
  bool debug = false;

  while ((ch = getopt(argc, argv, "h?" "I:" "d")) != EOF) {
    switch(ch) {
    case 'I':
      interface = optarg;
      break;
    case 'd':
      debug = true;
      break;
    default:
      usage();
      break;
    }
  }

  argc -= optind;
  argv += optind;

  if (!interface)
    usage();

  /* Request a socket descriptor sd. */
  if ((sd = socket (AF_INET6, SOCK_RAW, IPPROTO_IPIP)) < 0) {
    perror ("Failed to get socket descriptor ");
    exit (EXIT_FAILURE);
  }

  memset(&ifr, 0, sizeof(ifr));
  snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface);

  /* Bind socket to interface of this node. */
  if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, (void *) &ifr, sizeof (ifr)) < 0) {
    perror ("SO_BINDTODEVICE failed");
    exit (EXIT_FAILURE);
  }
  if (debug) printf("Binding to interface %s\n", interface);

  while (1) {
    struct sockaddr_in6 src_addr;
    socklen_t addrlen = sizeof(src_addr);
    char source[INET6_ADDRSTRLEN+1];
    int len;
    uint8_t inpack[IP_MAXPACKET];

    if ((len = recvfrom(sd, inpack, sizeof(inpack), 0, (struct sockaddr *)&src_addr, &addrlen)) < 0) {
      perror("recvfrom failed ");
    }
    if (inet_ntop(AF_INET6, &src_addr.sin6_addr, source, INET6_ADDRSTRLEN) == NULL) {
      perror("inet_ntop() failed.");
      exit(EXIT_FAILURE);
    }

    /* Reply */
    struct iphdr *ip = (struct iphdr *)inpack;
    uint32_t saddr;
    struct icmphdr *icmp;

    saddr = ip->saddr;
    ip->saddr = ip->daddr;
    ip->daddr = saddr;

    switch (ip->protocol) {
    case 1:
      if (debug) printf ("ICMP Echo request from %s\n", source);
      icmp = (struct icmphdr *)&ip[1];
      icmp->type = ICMP_ECHOREPLY;
      break;
    default:
      fprintf(stderr, "Unsupported protocol %d", ip->protocol);
    }
    if (len = sendto(sd, inpack, len, 0, (struct sockaddr *)&src_addr, addrlen) < 0) {
      perror("sendto failed ");
    }
  }

  close (sd);

  return (EXIT_SUCCESS);
}
