/*
 * This file is part of the Owl Positioning System (OwlPS) project.
 * It is subject to the copyright notice and license terms in the
 * COPYRIGHT.t2t file found in the top-level directory of this
 * distribution and at
 *   https://code.lm7.fr/mcy/owlps/src/master/COPYRIGHT.t2t
 * No part of the OwlPS Project, including this file, may be copied,
 * modified, propagated, or distributed except according to the terms
 * contained in the COPYRIGHT.t2t file; the COPYRIGHT.t2t file must be
 * distributed along with this file, either separately or by replacing
 * this notice by the COPYRIGHT.t2t file's contents.
 *
 ***********************************************************************
 *
 * This is the main source file of OwlPS Listener, the program that
 * captures the requests sent by the mobile terminals and forwards them
 * to the Aggregator.
 */


#include "owlps-listener.h"

#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
# include <confuse.h>
#endif // OWLPS_LISTENER_USES_CONFIG_FILE

#ifdef OWLPS_LISTENER_USES_PTHREAD
# include <pthread.h>
#endif // OWLPS_LISTENER_USES_PTHREAD

#ifdef OWLPS_LISTENER_KEEPS_MONITOR
# include <iwlib.h>
#endif // OWLPS_LISTENER_KEEPS_MONITOR

#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/if_ether.h>

#define __FAVOR_BSD
#include <netinet/udp.h>

#include <netinet/in_systm.h> // for n_time on BSD (used in ip.h)
#include <netinet/ip.h>
#include <arpa/inet.h>

#ifndef __GLIBC__
# include <sys/sysctl.h>
# include <net/if_dl.h>
#endif // __GLIBC__

#include <assert.h>


char *program_name = NULL ;
uint8_t my_mac_bytes[ETHER_ADDR_LEN] ; // CP MAC address
char my_ip[INET_ADDRSTRLEN] ; // CP IP address

/* Will we dump the configuration?
 * Note that we declare this option as a global variable because
 * it must not appear in Confuse's options (to avoid dumping the
 * dump_configuration option itself).
 */
bool dump_configuration = false ;

pcap_t *capture_handler = NULL ; // Packet capture descriptor
int aggregation_sockfd ;
struct sockaddr aggregation_server ;

#ifdef OWLPS_LISTENER_USES_PTHREAD
int autocalibration_send_sockfd ;
struct sockaddr autocalibration_send_server ;
// true if the coordinates of the listener were provided by the user:
bool coordinates_provided = false ;
#endif // OWLPS_LISTENER_USES_PTHREAD

#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
cfg_t *cfg = NULL ; // Configuration structure

#else // OWLPS_LISTENER_USES_CONFIG_FILE
/* If we do not use libconfuse, we declare a structure to store getopt
 * options.
 */
struct {
  bool daemon ;
  char mode ;
  char aggregation_host[MAXHOSTNAMELEN] ;
  uint_fast16_t aggregation_port ;
  uint_fast16_t listening_port ;
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
  bool keep_monitor ;
#endif // OWLPS_LISTENER_KEEPS_MONITOR
  char rtap_iface[IFNAMSIZ + 1] ;
  char *pcap_file ;
  char wifi_iface[IFNAMSIZ + 1] ;
#ifdef OWLPS_LISTENER_USES_PTHREAD
  bool autocalibration ;
  char autocalibration_host[MAXHOSTNAMELEN] ;
  uint_fast16_t autocalibration_request_port ;
  uint_fast16_t autocalibration_order_port ;
  uint_fast16_t autocalibration_hello_port ;
  uint_fast32_t autocalibration_hello_delay ;
  uint_fast32_t autocalibration_delay ;
  uint_fast16_t autocalibration_nb_packets ;
  owl_direction my_direction ;
  float my_position_x ;
  float my_position_y ;
  float my_position_z ;
#endif // OWLPS_LISTENER_USES_PTHREAD
  uint_fast8_t verbose ;
} options = { // Initalise default options:
  false, // daemon
  MODE_ACTIVE, // mode
  DEFAULT_AGGREGATION_HOST, // aggregation_host
  OWL_DEFAULT_LISTENER_PORT, // aggregation_port
  OWL_DEFAULT_REQUEST_PORT, // listening_port
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
  false, // keep_monitor
#endif // OWLPS_LISTENER_KEEPS_MONITOR
  "", // rtap_iface
  NULL, // pcap_file
  "", // wifi_iface
#ifdef OWLPS_LISTENER_USES_PTHREAD
  false, // autocalibration
  "", // autocalibration_host
  0, // autocalibration_request_port
  OWL_DEFAULT_AUTOCALIBRATION_ORDER_PORT, // autocalibration_order_port
  OWL_DEFAULT_AUTOCALIBRATION_HELLO_PORT, // autocalibration_hello_port
  DEFAULT_AUTOCALIBRATION_HELLO_DELAY, // autocalibration_hello_delay
  DEFAULT_AUTOCALIBRATION_DELAY, // autocalibration_delay
  DEFAULT_AUTOCALIBRATION_NBPKT, // autocalibration_nb_packets
  0, 0, 0, 0, // Calibration data
#endif // OWLPS_LISTENER_USES_PTHREAD
  0 // verbose
} ;
#endif // OWLPS_LISTENER_USES_CONFIG_FILE



int main(int argc, char *argv[])
{
  struct sigaction action ; // Signal handler structure
  int ret ; // Program return value
#ifdef OWLPS_LISTENER_USES_PTHREAD
  pthread_t
    autocalibration_thread,
    autocalibration_hello_thread ;
#endif // OWLPS_LISTENER_USES_PTHREAD
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
  pthread_t keep_monitor_thread ;
#endif // OWLPS_LISTENER_KEEPS_MONITOR

  owl_run = true ;

  program_name = argv[0] ;
  ret = initialise_configuration(argc, argv) ;
  if (! owl_run)
    goto exit ;

  if (GET_DAEMON())
    {
      if (VERBOSE_WARNING)
        fprintf(stderr, "Detaching to background...\n") ;
      if (daemon(0, 0))
        perror("Cannot daemonize") ;
    }

  /* Set up signal handlers */
  action.sa_flags = 0 ;
  sigemptyset(&action.sa_mask) ;
  action.sa_handler = sigint_handler ;
  sigaction(SIGINT, &action, NULL) ;
  action.sa_handler = sigterm_handler ;
  sigaction(SIGTERM, &action, NULL) ;

  if (LIVE_CAPTURING)
    {
      get_mac_addr(GET_WIFI_IFACE(), my_mac_bytes) ;
      get_ip_addr(GET_WIFI_IFACE(), my_ip) ;
      if (VERBOSE_INFO)
        printf("My MAC address is: %s\n"
               "My IP address is: %s\n",
               owl_mac_bytes_to_string(my_mac_bytes),
               my_ip) ;
    }

#ifdef OWLPS_LISTENER_USES_PTHREAD
  /* Set up threads */
# ifdef OWLPS_LISTENER_KEEPS_MONITOR
  if (GET_KEEP_MONITOR())
    {
      ret = pthread_create(&keep_monitor_thread, NULL,
                           &keep_mode_monitor, GET_WIFI_IFACE()) ;
      if (ret != 0)
        {
          perror("Cannot create keep monitor thread") ;
          ret = OWL_ERR_THREAD_CREATE ;
          goto exit ;
        }
    }
# endif // OWLPS_LISTENER_KEEPS_MONITOR

  if (GET_AUTOCALIBRATION())
    {
      ret = pthread_create(&autocalibration_thread, NULL,
                           &autocalibrate, NULL) ;
      if (ret != 0)
        {
          perror("Cannot create autocalibration thread") ;
          ret = OWL_ERR_THREAD_CREATE ;
          goto exit ;
        }

      ret = pthread_create(&autocalibration_hello_thread, NULL,
                           &autocalibrate_hello, NULL) ;
      if (ret != 0)
        {
          perror("Cannot create autocalibration hello thread") ;
          ret = OWL_ERR_THREAD_CREATE ;
          goto exit ;
        }
    }
#endif // OWLPS_LISTENER_USES_PTHREAD

  ret = capture() ; // Capture loop

  /* Wait for the threads to terminate */

#ifdef OWLPS_LISTENER_USES_PTHREAD
# ifdef OWLPS_LISTENER_KEEPS_MONITOR
  if (GET_KEEP_MONITOR())
    {
      if (VERBOSE_WARNING)
        fprintf(stderr,
                "Waiting for the keep mode monitor thread...\n") ;
      if (pthread_join(keep_monitor_thread, NULL) != 0)
        perror("Cannot join keep mode monitor thread") ;
      else if (VERBOSE_WARNING)
        fprintf(stderr, "Keep monitor thread done.\n") ;
    }
# endif // OWLPS_LISTENER_KEEPS_MONITOR

  if (GET_AUTOCALIBRATION())
    {
      // We must cancel this thread because it can be blocked on the
      // recvfrom() call:
      if (VERBOSE_WARNING)
        fprintf(stderr, "Cancelling the autocalibration thread...\n") ;
      if (pthread_cancel(autocalibration_thread) != 0)
        perror("Cannot cancel autocalibration thread") ;
      else if (VERBOSE_WARNING)
        fprintf(stderr, "Autocalibration thread cancelled.\n") ;

      if (VERBOSE_WARNING)
        fprintf(stderr, "Waiting for the autocalibration thread...\n") ;
      if (pthread_join(autocalibration_thread, NULL) != 0)
        perror("Cannot join autocalibration thread") ;
      else if (VERBOSE_WARNING)
        fprintf(stderr, "Autocalibration thread done.\n") ;

      // We must cancel this thread if we do not want to wait
      // autocalibration_hello_delay seconds (in the worst case):
      if (VERBOSE_WARNING)
        fprintf(stderr,
                "Cancelling the autocalibration hello thread...\n") ;
      if (pthread_cancel(autocalibration_hello_thread) != 0)
        perror("Cannot cancel autocalibration hello thread") ;
      else if (VERBOSE_WARNING)
        fprintf(stderr, "Autocalibration hello thread cancelled.\n") ;

      if (VERBOSE_WARNING)
        fprintf(stderr,
                "Waiting for the autocalibration hello thread...\n") ;
      if (pthread_join(autocalibration_hello_thread, NULL) != 0)
        perror("Cannot join autocalibration hello thread") ;
      else if (VERBOSE_WARNING)
        fprintf(stderr, "Autocalibration hello thread done.\n") ;
    }
#else // OWLPS_LISTENER_USES_PTHREAD
  // Just to avoid a warning when compiling without threads' support:
  goto exit ;
#endif // OWLPS_LISTENER_USES_PTHREAD

  /* Last cleaning tasks */
 exit:
#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
  /* If called with -h, cfg won't be initialised, so we must check if
   * cfg is NULL */
  if (cfg && VERBOSE_CHATTERBOX)
#else // OWLPS_LISTENER_USES_CONFIG_FILE
  if (VERBOSE_CHATTERBOX)
#endif // OWLPS_LISTENER_USES_CONFIG_FILE
    fprintf(stderr, "%s: exiting.\n", program_name) ;

#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
  cfg_free(cfg) ; // Clean configuration
#endif // OWLPS_LISTENER_USES_CONFIG_FILE

  return ret ;
}



/*
 * Read the configuration from both the command line and the
 * configuration file.
 * Returns an error code, or 0 in case of success. If the program should
 * stop (because of a special option or a configuration error), owl_run
 * is set to false.
 */
int initialise_configuration(const int argc, char *const *argv)
{
  int ret ;

  ret = parse_config_file(argc, argv) ;
  if (! owl_run)
    return ret ;

  ret = parse_command_line(argc, argv) ;
  if (! owl_run)
    return ret ;

  ret = check_configuration() ;
  if (! owl_run)
    return ret ;

  /* Configuration dumping */
  if (dump_configuration)
    {
      print_configuration(stdout) ;
      owl_run = false ;
      return 0 ;
    }

  /* Configuration printing */
  if (VERBOSE_INFO)
    {
      fprintf(stderr, "Configuration:\n") ;
      print_configuration(stderr) ;
    }

  return 0 ;
}



int parse_config_file(const int argc, char *const *argv)
{
#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
  // If we use libconfuse, we declare options:
  cfg_opt_t opts[] =
    {
      // Daemon mode:
      CFG_BOOL("daemon", cfg_false, CFGF_NONE),
      // Listening mode: a for active, p for passive, m for mixed
      // (default: a):
      CFG_INT("mode", MODE_ACTIVE, CFGF_NONE),
      // Aggregation server host (default: loopback):
      CFG_STR("aggregation_host", DEFAULT_AGGREGATION_HOST, CFGF_NONE),
      // Port on which the aggregator listens:
      CFG_INT("aggregation_port", OWL_DEFAULT_LISTENER_PORT, CFGF_NONE),
      // Port to which mobiles send active requests:
      CFG_INT("listening_port", OWL_DEFAULT_REQUEST_PORT, CFGF_NONE),
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
      // Activate the active monitor mode keeping-up (read the code if
      // you do not understand what I mean):
      CFG_BOOL("keep_monitor", cfg_false, CFGF_NONE),
#endif // OWLPS_LISTENER_KEEPS_MONITOR
      // Radiotap interface to capture from:
      CFG_STR("rtap_iface", "", CFGF_NONE),
      // Pcap file to read packets from:
      CFG_STR("pcap_file", "", CFGF_NONE),
      // Physical interface corresponding to the radiotap interface (used
      // to get the MAC address):
      CFG_STR("wifi_iface", "", CFGF_NONE),
#ifdef OWLPS_LISTENER_USES_PTHREAD
      // Autocalibration activated?
      CFG_BOOL("autocalibration", cfg_false, CFGF_NONE),
      // Destination host of the autocalibration requests (default:
      // none, but will be set to aggregation_ip in the config check):
      CFG_STR("autocalibration_host", "", CFGF_NONE),
      // Port to which autocalibration requests are sent (default: 0,
      // but will be set to listening_port in the config check):
      CFG_INT("autocalibration_request_port", 0, CFGF_NONE),
      // Port on which autocalibration orders are received:
      CFG_INT("autocalibration_order_port",
              OWL_DEFAULT_AUTOCALIBRATION_ORDER_PORT, CFGF_NONE),
      // Port to which autocalibration hello are sent:
      CFG_INT("autocalibration_hello_port",
              OWL_DEFAULT_AUTOCALIBRATION_HELLO_PORT, CFGF_NONE),
      // Delay between two hello messages:
      CFG_INT("autocalibration_hello_delay",
              DEFAULT_AUTOCALIBRATION_HELLO_DELAY,
              CFGF_NONE),
      // Delay between two calibration packet transmission:
      CFG_INT("autocalibration_delay", DEFAULT_AUTOCALIBRATION_DELAY,
              CFGF_NONE),
      // Number of packets for a calibration request:
      CFG_INT("autocalibration_nb_packets",
              DEFAULT_AUTOCALIBRATION_NBPKT, CFGF_NONE),
      // Direction
      CFG_INT("my_direction", 0, CFGF_NONE),
      // Position X
      CFG_FLOAT("my_position_x", 0, CFGF_NONE),
      // Position Y
      CFG_FLOAT("my_position_y", 0, CFGF_NONE),
      // Position Z
      CFG_FLOAT("my_position_z", 0, CFGF_NONE),
#endif // OWLPS_LISTENER_USES_PTHREAD
      // Verbose level:
      CFG_INT("verbose", 0, CFGF_NONE),
      CFG_END()
    } ;

  // Configuration file name
  char *config_file = NULL ;
  // True if we are using the default configuration file, false if the
  // user specified a different one with -f
  bool default_config_file = false ;
#endif // OWLPS_LISTENER_USES_CONFIG_FILE

  // Option -f specifies a config file, options -h and -V exit the
  // program, so we search for them first
  int opt ;
  while ((opt = getopt(argc, argv, OPTIONS)) != -1)
    {
      switch (opt)
        {
        case 'f' :
#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
          config_file = malloc((strlen(optarg) + 1) * sizeof(char)) ;
          if (! config_file)
            {
              perror("Cannot allocate memory") ;
              owl_run = false ;
              return errno ;
            }
          strcpy(config_file, optarg) ;
#else // OWLPS_LISTENER_USES_CONFIG_FILE
          fprintf(stderr, "Warning! Program was not compiled with"
                  " configuration file support, so -f is not available."
                  " You must specify all options on the command line,"
                  " or default value will be used.\n") ;
#endif // OWLPS_LISTENER_USES_CONFIG_FILE
          break ;
        case 'h' :
          print_usage() ;
          owl_run = false ;
          return EXIT_SUCCESS ;
        case 'V' :
          print_version() ;
          owl_run = false ;
          return EXIT_SUCCESS ;
        }
    }

#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
  // If -f isn't found, we use the default config file
  if (config_file == NULL)
    {
      default_config_file = true ;
      config_file =
        malloc((strlen(DEFAULT_CONFIG_FILE) + 1) * sizeof(char)) ;
      if (! config_file)
        {
          perror("Cannot allocate memory") ;
          owl_run = false ;
          return errno ;
        }
      strcpy(config_file, DEFAULT_CONFIG_FILE) ;
    }

  /* Parse config file */
  cfg = cfg_init(opts, CFGF_NONE) ; // Initialise options
  switch (cfg_parse(cfg, config_file))
    {
    case CFG_FILE_ERROR :
      /* If we can't open the file, we display a message only if
       * the user used -f. In verbose mode, it would be nice to
       * display the message even if the user didn't use -f, but
       * the command-line options are not parsed yet so the verbose
       * level is always zero. */
      if (! default_config_file)
        fprintf(stderr,
                "Warning! Cannot open configuration file \"%s\": %s.\n",
                config_file, strerror(errno)) ;
      break ;
    case CFG_PARSE_ERROR :
      fprintf(stderr,
              "Error! Parsing of configuration file \"%s\" failed!\n",
              config_file) ;
      free(config_file) ;
      owl_run = false ;
      return OWL_ERR_CONFIG_FILE ;
    }
  free(config_file) ;
#endif // OWLPS_LISTENER_USES_CONFIG_FILE

  return 0 ;
}



int parse_command_line(const int argc, char *const *argv)
{
  int ret ;

  ret = parse_main_options(argc, argv) ;
  if (! owl_run)
    return ret ;

#ifdef OWLPS_LISTENER_USES_PTHREAD
  ret = parse_calibration_data(argc, argv) ;
  if (! owl_run)
    return ret ;
#endif // OWLPS_LISTENER_USES_PTHREAD

  return 0 ;
}



int parse_main_options(const int argc, char *const *argv)
{
  int opt ;
  long arg_long ; // Integer value of optarg
  char *endptr ; // Return value of strtol()

  optind = 1 ; // Rewind argument parsing

  while ((opt = getopt(argc, argv, OPTIONS)) != -1)
    {
      switch (opt)
        {
        case 'A' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          SET_AUTOCALIBRATION() ;
#else // OWLPS_LISTENER_USES_PTHREAD
          fprintf(stderr, "Warning! The program was compiled without"
                  " support of POSIX threads, so -A (autocalibration)"
                  " is not available and will be ignored. All other"
                  " autocalibration-related options will also be"
                  " ignored.\n") ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'D' :
          SET_DAEMON() ;
          break ;
        case 'f' : // Config file
          break ; // (already parsed)
        case 'G' :
          dump_configuration = true ;
          /* We must not turn owl_run false here, to avoid the default
           * check on its value in initialise_configuration(). */
          break ;
        case 'H' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          SET_AUTOCALIBRATION_HELLO_PORT(strtol(optarg, NULL, 10)) ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'i' :
          SET_AGGREGATION_HOST(optarg) ;
          break ;
        case 'I' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          SET_AUTOCALIBRATION_HOST(optarg) ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'K' :
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
          SET_KEEP_MONITOR() ;
#else // OWLPS_LISTENER_KEEPS_MONITOR
          fprintf(stderr, "Warning! The program was compiled without"
                  " enabling the -K option (monitor mode keeping-up)."
                  "\n") ;
#endif // OWLPS_LISTENER_KEEPS_MONITOR
          break ;
        case 'l' :
          SET_LISTENING_PORT(strtol(optarg, NULL, 10)) ;
          break ;
        case 'm' :
          SET_MODE(optarg[0]) ;
          break ;
        case 'n' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          SET_AUTOCALIBRATION_NBPKT(strtol(optarg, NULL, 10)) ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'O' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          SET_AUTOCALIBRATION_ORDER_PORT(strtol(optarg, NULL, 10)) ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'p' :
          SET_AGGREGATION_PORT(strtol(optarg, NULL, 10)) ;
          break ;
        case 'P' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          SET_AUTOCALIBRATION_REQUEST_PORT(strtol(optarg, NULL, 10)) ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'q' :
          RESET_VERBOSE() ;
          break ;
        case 'r' :
          SET_RTAP_IFACE(optarg) ;
          break ;
        case 'R' :
          SET_PCAP_FILE(optarg) ;
          break ;
        case 't' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          arg_long = strtol(optarg, &endptr, 10) ;
          if (endptr != optarg)
            SET_AUTOCALIBRATION_DELAY(arg_long) ;
          else
            fprintf(stderr, "Warning! Bad autocalibration_delay:"
                    " failing back to the default value.\n") ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'T' :
#ifdef OWLPS_LISTENER_USES_PTHREAD
          arg_long = strtol(optarg, &endptr, 10) ;
          if (endptr != optarg)
            SET_AUTOCALIBRATION_HELLO_DELAY(arg_long) ;
          else
            fprintf(stderr, "Warning! Bad autocalibration_hello_delay:"
                    " failing back to the default value.\n") ;
#endif // OWLPS_LISTENER_USES_PTHREAD
          break ;
        case 'v' :
          INCREMENT_VERBOSE() ;
          break ;
        case 'w' :
          SET_WIFI_IFACE(optarg) ;
          break ;
        default :
          print_usage() ;
          owl_run = false ;
          return OWL_ERR_BAD_USAGE ;
        }
    }

  return 0 ;
}



#ifdef OWLPS_LISTENER_USES_PTHREAD
/* Parses remaining arguments (possible calibration data) */
int parse_calibration_data(const int argc, char *const *argv)
{
  /* No more arguments to parse */
  if (argc - optind == 0)
    return 0 ;

  /* Exactly 4 more arguments */
  if (argc - optind == 4)
    {
      char *endptr ;
      unsigned long arg_ulong ;
      double arg_double ;

      coordinates_provided = true ;

      arg_ulong = strtoul(argv[optind], &endptr, 10) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong direction!\n") ;
          goto error ;
        }
      SET_MY_DIRECTION(arg_ulong) ;
      optind++ ;

      arg_double = strtod(argv[optind], &endptr) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong X coordinate!\n") ;
          goto error ;
        }
      SET_MY_POSITION_X(arg_double) ;
      optind++ ;

      arg_double = strtod(argv[optind], &endptr) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong Y coordinate!\n") ;
          goto error ;
        }
      SET_MY_POSITION_Y(arg_double) ;
      optind++ ;

      arg_double = strtod(argv[optind], &endptr) ;
      if (endptr == argv[optind])
        {
          fprintf(stderr,
                  "Error in calibration data: wrong Z coordinate!\n") ;
          goto error ;
        }
      SET_MY_POSITION_Z(arg_double) ;

      return 0 ; // No error occurred
    }

  /* Bad number of arguments or parse error */
 error:
  print_usage() ;
  owl_run = false ;
  return OWL_ERR_BAD_USAGE ;
}
#endif // OWLPS_LISTENER_USES_PTHREAD



int check_configuration()
{
  // Capture mode //
  switch (GET_MODE())
    {
    case MODE_ACTIVE :
    case MODE_MIXED :
    case MODE_PASSIVE :
      break ;
    default :
      fprintf(stderr, "Error! Unknown mode \"%c\".\n", (char) GET_MODE()) ;
      print_usage() ;
      owl_run = false ;
      return OWL_ERR_BAD_USAGE ;
    }

  // Packet source //
  if (GET_PCAP_FILE() && GET_PCAP_FILE()[0] != '\0')
    {
      if (GET_RTAP_IFACE()[0] != '\0')
        {
          if (VERBOSE_WARNING)
            fprintf(stderr, "Warning! Cannot read both from a pcap file"
                    " and a capture interface: ignoring network-related"
                    " options.\n") ;
          GET_RTAP_IFACE()[0] = '\0' ;
        }

      // Ignoring the Wi-Fi interface too:
      GET_WIFI_IFACE()[0] = '\0' ;

#ifdef OWLPS_LISTENER_USES_PTHREAD
      if (GET_AUTOCALIBRATION())
        {
          if (VERBOSE_WARNING)
            fprintf(stderr, "Warning! Cannot send autocalibration"
                    " requests when reading from a pcap file: disabling"
                    " autocalibration.\n") ;
          UNSET_AUTOCALIBRATION() ;
        }

# ifdef OWLPS_LISTENER_KEEPS_MONITOR
      UNSET_KEEP_MONITOR() ;
# endif // OWLPS_LISTENER_KEEPS_MONITOR
#endif // OWLPS_LISTENER_USES_PTHREAD

      if (GET_DAEMON())
        {
          if (VERBOSE_WARNING)
            fprintf(stderr, "Warning! Reading from a pcap file: will"
                    " stay in foreground.\n") ;
          UNSET_DAEMON() ;
        }
    }

  // pcap_file is empty
  else if (GET_RTAP_IFACE()[0] == '\0')
    {
      fprintf(stderr, "Error! You must specify either a radiotap"
              " interface or a pcap file to read packets from.\n") ;
      print_usage() ;
      owl_run = false ;
      return OWL_ERR_BAD_USAGE ;
    }

  else if (GET_WIFI_IFACE()[0] == '\0')
    {
      if (VERBOSE_WARNING)
        fprintf(stderr, "No Wi-Fi interface was specified. Failing back"
                " to the radiotap interface (%s) instead.\n",
                GET_RTAP_IFACE()) ;
      SET_WIFI_IFACE(GET_RTAP_IFACE()) ;
    }

  // Port numbers //
  if (GET_AGGREGATION_PORT() < 1 || GET_AGGREGATION_PORT() > 65535)
    {
      if (VERBOSE_WARNING)
        fprintf(stderr, "Warning! Bad aggregation_port:"
                " failing back to the default value.\n") ;
      SET_AGGREGATION_PORT(OWL_DEFAULT_LISTENER_PORT) ;
    }
  if (GET_LISTENING_PORT() < 1 || GET_LISTENING_PORT() > 65535)
    {
      if (VERBOSE_WARNING)
        fprintf(stderr, "Warning! Bad listening_port:"
                " failing back to the default value.\n") ;
      SET_LISTENING_PORT(OWL_DEFAULT_REQUEST_PORT) ;
    }

  // Autocalibration stuff //
#ifdef OWLPS_LISTENER_USES_PTHREAD
  if (GET_AUTOCALIBRATION())
    {
      if (GET_AUTOCALIBRATION_HOST()[0] == '\0')
        {
          if (VERBOSE_WARNING)
            fprintf(stderr, "No autocalibration host specified, the"
                    " aggregation host will be used as the destination"
                    " of autocalibration requests.\n") ;
          SET_AUTOCALIBRATION_HOST(GET_AGGREGATION_HOST()) ;
        }

      if (GET_AUTOCALIBRATION_NBPKT() < 1)
        fprintf(stderr, "Warning! autocalibration_nb_packets is zero,"
                " no autocalibration request will be sent!\n") ;

      if (coordinates_provided)
        if (GET_MY_DIRECTION() < OWL_DIRECTION_MIN ||
            GET_MY_DIRECTION() > OWL_DIRECTION_MAX)
          fprintf(stderr, "Warning! \"%d\" is not a valid"
                  " direction.\n", (int) GET_MY_DIRECTION()) ;

      // Autocalibration port numbers
      if (GET_AUTOCALIBRATION_REQUEST_PORT() > 65535 && VERBOSE_WARNING)
        fprintf(stderr, "Warning! Bad autocalibration_request_port:"
                " failing back to the default value.\n") ;
      if (GET_AUTOCALIBRATION_REQUEST_PORT() < 1 ||
          GET_AUTOCALIBRATION_REQUEST_PORT() > 65535)
        SET_AUTOCALIBRATION_REQUEST_PORT(GET_LISTENING_PORT()) ;
      if (GET_AUTOCALIBRATION_ORDER_PORT() < 1 ||
          GET_AUTOCALIBRATION_ORDER_PORT() > 65535)
        {
          if (VERBOSE_WARNING)
            fprintf(stderr, "Warning! Bad autocalibration_order_port:"
                    " failing back to the default value.\n") ;
          SET_AUTOCALIBRATION_ORDER_PORT(OWL_DEFAULT_AUTOCALIBRATION_ORDER_PORT) ;
        }
      if (GET_AUTOCALIBRATION_HELLO_PORT() < 1 ||
          GET_AUTOCALIBRATION_HELLO_PORT() > 65535)
        {
          if (VERBOSE_WARNING)
            fprintf(stderr, "Warning! Bad autocalibration_hello_port:"
                    " failing back to the default value.\n") ;
          SET_AUTOCALIBRATION_HELLO_PORT(OWL_DEFAULT_AUTOCALIBRATION_HELLO_PORT) ;
        }
    }
#endif // OWLPS_LISTENER_USES_PTHREAD

  return 0 ;
}



void print_configuration(FILE *const stream)
{
#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
  cfg_print(cfg, stream) ;
#else // OWLPS_LISTENER_USES_CONFIG_FILE
  fprintf(stream,
          "mode = %c\n"
          "aggregation_host = \"%s\"\n"
          "aggregation_port = %"PRIuFAST16"\n"
          "listening_port = %"PRIuFAST16"\n"
          "rtap_iface = \"%s\"\n"
          "wifi_iface = \"%s\"\n"
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
          "keep_monitor = %s\n"
#endif // OWLPS_LISTENER_KEEPS_MONITOR
#ifdef OWLPS_LISTENER_USES_PTHREAD
          "autocalibration = %s\n"
          "autocalibration_host = \"%s\"\n"
          "autocalibration_request_port = %"PRIuFAST16"\n"
          "autocalibration_order_port = %"PRIuFAST16"\n"
          "autocalibration_hello_port = %"PRIuFAST16"\n"
          "autocalibration_hello_delay = %"PRIuFAST32"\n"
          "autocalibration_delay = %"PRIuFAST32"\n"
          "autocalibration_nb_packets = %"PRIuFAST16"\n"
          "my_direction = %"PRIu8"\n"
          "my_position_x = %f\n"
          "my_position_y = %f\n"
          "my_position_z = %f\n"
#endif // OWLPS_LISTENER_USES_PTHREAD
          "verbose = %"PRIuFAST8"\n"
          ,
          GET_MODE(),
          GET_AGGREGATION_HOST(),
          GET_AGGREGATION_PORT(),
          GET_LISTENING_PORT(),
          GET_RTAP_IFACE(),
          GET_WIFI_IFACE(),
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
          OWL_BOOL_TO_STRING(GET_KEEP_MONITOR()),
#endif // OWLPS_LISTENER_KEEPS_MONITOR
#ifdef OWLPS_LISTENER_USES_PTHREAD
          OWL_BOOL_TO_STRING(GET_AUTOCALIBRATION()),
          GET_AUTOCALIBRATION_HOST(),
          GET_AUTOCALIBRATION_REQUEST_PORT(),
          GET_AUTOCALIBRATION_ORDER_PORT(),
          GET_AUTOCALIBRATION_HELLO_PORT(),
          GET_AUTOCALIBRATION_HELLO_DELAY(),
          GET_AUTOCALIBRATION_DELAY(),
          GET_AUTOCALIBRATION_NBPKT(),
          GET_MY_DIRECTION(),
          GET_MY_POSITION_X(),
          GET_MY_POSITION_Y(),
          GET_MY_POSITION_Z(),
#endif // OWLPS_LISTENER_USES_PTHREAD
          GET_VERBOSE()
          ) ;
#endif // OWLPS_LISTENER_USES_CONFIG_FILE
}



#ifdef OWLPS_LISTENER_KEEPS_MONITOR
/*
 * Thread function. Switches interface 'iface' to monitor mode every
 * second. `iface` must be passed as a const char *const.
 */
void* keep_mode_monitor(void *const iface)
{
  if (VERBOSE_WARNING)
    fprintf(stderr, "Thread for keeping monitor mode launched.\n") ;

  while (owl_run)
    {
      // Switch the interface to monitor mode:
      iface_mode_monitor((char*) iface) ;
      sleep(1) ; // Wait for 1 second
    }

  pthread_exit(NULL) ;
}



/*
 * Switches the IEEE 802.11 interface 'iface' to Monitor mode.
 *
 * This function uses iwlib, which is Linux-specific. In case we want
 * this on BSD, there is some example code there:
 *   http://www.unixgarden.com/index.php/gnu-linux-magazine-hs/introduction-a-la-programmation-wifi-en-c-sous-netbsd#7-exemple-n°4-activer-et-utiliser-le-monitoring-aka-rf_mon
 * The thing is that we would have to switch the interface down and up
 * each time, so let's hope we will never need that. But even on Linux,
 * this function was only needed with the buggy ipw2200 driver (for Intel
 * BG2200 chips) and it hopefully won't be needed any more.
 */
int iface_mode_monitor(const char *const iface)
{
  struct iwreq wrq ;
  int sockfd = iw_sockets_open() ;

  strncpy((&wrq)->ifr_name, iface, IFNAMSIZ) ;

  if (ioctl(sockfd, SIOCGIWMODE, &wrq) == -1) // Get current mode
    {
      perror("Error reading interface mode") ;
      return OWL_ERR_IFACE_MODE_GET ;
    }

  // If interface is not yet in Monitor mode
  if (wrq.u.mode != IW_MODE_MONITOR)
    {
      wrq.u.mode = IW_MODE_MONITOR ;
      if (ioctl(sockfd, SIOCSIWMODE, &wrq) == -1) // Set up Monitor mode
        {
          perror("Error setting up Monitor mode") ;
          return OWL_ERR_IFACE_MODE_SET ;
        }
    }

  close(sockfd) ;

  return 0 ;
}
#endif // OWLPS_LISTENER_KEEPS_MONITOR



/*
 * Captures packets using the radiotap interface.
 * Captured data is transmitted to the aggregator.
 */
int capture()
{
  char errbuf[PCAP_ERRBUF_SIZE] ; // Error message
  char *source ;
  struct pcap_pkthdr *pkt_header ;
  const u_char *pkt_data ;

  // Open the capture handler:
  if (LIVE_CAPTURING)
    {
      source = GET_RTAP_IFACE() ;
      capture_handler = pcap_open_live(source, BUFSIZ, 1, 1000, errbuf) ;
    }
  else
    {
      source = GET_PCAP_FILE() ;
      capture_handler = pcap_open_offline(source, errbuf) ;
    }

  if (capture_handler == NULL) // Capture starting failed
    {
      fprintf(stderr, "Cannot create capture handler on '%s': %s\n",
              source, errbuf) ;
      return OWL_ERR_IFACE_PCAP_OPEN ;
    }

  /* Open UDP socket to the aggregator */
  aggregation_sockfd =
    owl_create_trx_socket(GET_AGGREGATION_HOST(), GET_AGGREGATION_PORT(),
                          &aggregation_server, NULL) ;

  while (owl_run)
    {
      // Capture one packet at time, and call read_packet() on it:
      int ret = pcap_next_ex(capture_handler, &pkt_header, &pkt_data) ;
      switch (ret)
        {
        case 1: // packet read successfuly
          read_packet(pkt_header, pkt_data) ;
          break ;
        case 0: // timeout reached
          break ;
        case -2: // no more packet to read from input file
          owl_run = false ;
          break ;
        case -1: // an error occured
          pcap_perror(capture_handler, "Error during capture") ;
        }
    }

  pcap_close(capture_handler) ; // Stop capture
  close(aggregation_sockfd) ; // Close socket

  return 0 ;
}



/*
 * Treats a packet and sends it to the aggregator.
 */
void read_packet(const struct pcap_pkthdr *const pkt_header,
                 const u_char *const pkt_data)
{
  owl_captured_request request ; // Message to send to the aggregator
  uint16_t rtap_bytes ; // Radiotap header size
  uint_fast16_t offset ; // Offset to read the packet
  uint8_t raw_packet_fc1 ; // First byte of the received frame's FC
  uint8_t raw_packet_fc2 ; // Second byte of the received frame's FC
  // Size of the IEEE 802.11 header:
  uint_fast8_t ieee80211_header_size = IEEE80211_HEADER_SIZE_DATA ;
  uint16_t llc_packet_type = 0 ;
  // Pointer to the (possible) IP header of the packet:
  struct ip *packet_ip_header = NULL ;
  // Pointer to the (possible) UDP header of the packet:
  struct udphdr *packet_udp_header = NULL ;
  // Localisation request type (request, calibration, autocalibration):
  // Is the packet an explicit request?
  bool is_explicit_packet = true ;
  // Is the packet an autocalibration positioning request?
  bool uses_autocalibration_request_port = false ;
  ssize_t nsent ; // sendto return value

  // Blank the request:
  memset(&request, 0, sizeof(request)) ;

  /* Common treatements */

  // Copy 2 bytes from the 3rd packet byte, that is the size of the rtap
  // header (changes with the flags):
  offset = 2 ;
  memcpy(&rtap_bytes, &pkt_data[offset], sizeof(rtap_bytes)) ;
  // Radiotap header is little-endian
  rtap_bytes = le16toh(rtap_bytes) ;
  // Check rtap_bytes for buggy values
  if (rtap_bytes > 100)
    return ;

  // After the rtap header, there is the 802.11 header; the first byte
  // is the first byte of the Frame Control (FC) field, which contains
  // the type of the packet (Management, Control or Data) and its subtype
  // (QoS, etc.):
  offset = rtap_bytes ;
  raw_packet_fc1 = pkt_data[offset] ;

  // The second byte of the FC field contains the frame flags. The two
  // first bits indicate the frame source and destination types: the
  // first bit is "To DS" and the second is "From DS", so if the second
  // bit is 0 the frame comes from a STA. That's what we want for an
  // explicit packet:
  offset = rtap_bytes + 1 ;
  raw_packet_fc2 = pkt_data[offset] ;

  if (! IS_DATA_FRAME(raw_packet_fc1)) // Data frame?
    goto not_explicit_packet ;

  if (DATA_FRAME_IS_QOS(raw_packet_fc1)) // QoS Data frame?
    ieee80211_header_size += 2 ; // 2 bytes of QoS information

  if (! IS_FRAME_FROM_STA(raw_packet_fc2))
    goto not_explicit_packet ;

  // Get the packet type (protocol, 2 bytes) from the LLC header:
  offset = rtap_bytes + ieee80211_header_size + 6 ;
  memcpy(&llc_packet_type, &pkt_data[offset], 2) ;
  llc_packet_type = ntohs(llc_packet_type) ;

  if (llc_packet_type != ETHERTYPE_IP) // IP packet?
    goto not_explicit_packet ;

  offset = rtap_bytes + ieee80211_header_size + LLC_HEADER_SIZE ;
  packet_ip_header = (struct ip *) &pkt_data[offset] ;
  // Get the source IP:
  memcpy(request.mobile_ip_addr_bytes, &packet_ip_header->ip_src, 4) ;

  if (GET_MODE() != MODE_PASSIVE) // If mode is active or mixed
    {
      uint_fast16_t dest_port ;

      // Protocol for an explicit request is UDP
      if (packet_ip_header->ip_p != IPPROTO_UDP)
        goto not_explicit_packet ;

      // Check destination port:
      offset =
        rtap_bytes + ieee80211_header_size +
        LLC_HEADER_SIZE + sizeof(struct ip) ;
      packet_udp_header = (struct udphdr *) &pkt_data[offset] ;
      dest_port = ntohs(packet_udp_header->uh_dport) ;

#ifdef OWLPS_LISTENER_USES_PTHREAD
      if (GET_AUTOCALIBRATION() && dest_port ==
          (uint_fast16_t) GET_AUTOCALIBRATION_REQUEST_PORT())
        uses_autocalibration_request_port = true ;
      else
#endif // OWLPS_LISTENER_USES_PTHREAD
        if (dest_port != (uint_fast16_t) GET_LISTENING_PORT())
          goto not_explicit_packet ;
    }

  goto process_packet ;

 not_explicit_packet :
  if (GET_MODE() == MODE_ACTIVE)
    return ;
  is_explicit_packet = false ;

 process_packet :

  if (IS_RETRY(raw_packet_fc2) && VERBOSE_CHATTERBOX)
    printf("This packet is a Retry.\n") ;

  // Source MAC address is 10 bytes after the 802.11 packet type:
  offset = rtap_bytes + 10 ;
  memcpy(request.mobile_mac_addr_bytes, &pkt_data[offset],
         ETHER_ADDR_LEN) ;

  // Drop the packet if it comes from the CP itself:
  if (owl_mac_equals(my_mac_bytes, request.mobile_mac_addr_bytes))
    return ;

  // Copy CP MAC :
  memcpy(request.cp_mac_addr_bytes, my_mac_bytes, ETHER_ADDR_LEN) ;
  // Capture time is in the pcap header (host-endian):
  owl_timeval_to_timestamp(&pkt_header->ts, &request.capture_time) ;
  /* Note on the line above: on OpenBSD, the ts field of struct
   * pcap_pkthdr (defined in <pcap.h>) is a struct bpf_timeval* instead
   * of a struct timeval*. struct bpf_timeval is defined in
   * /usr/include/net/bpf.h and is a clone of struct timeval with
   * fixed-size fields (u_int32_t). We *could* override the warning by
   * casting, but I prefer to leave it as a reminder in case something
   * went wrong at some point. */
  owl_hton_timestamp(&request.capture_time) ;

  /* Active mode */
  if (is_explicit_packet
      && (GET_MODE() == MODE_ACTIVE || GET_MODE() == MODE_MIXED)
      // FIXME: should we really ignore Retries?
      && ! IS_RETRY(raw_packet_fc2))
    {
      offset =
        rtap_bytes + ieee80211_header_size + LLC_HEADER_SIZE +
        sizeof(struct ip) + sizeof(struct udphdr) ;
      request.type = pkt_data[offset] ;
      extract_packet_numbers(&pkt_data[++offset], &request) ;
      offset += 2 * sizeof(uint16_t) ;
      // Copy the timestamp "as is" (i.e. without changing endianness)
      // because it will return to the network soon:
      memcpy(&request.request_time, &pkt_data[offset],
             sizeof(owl_timestamp)) ;
      offset += sizeof(owl_timestamp) ;

      switch(request.type)
        {
        case OWL_REQUEST_NORMAL :
          if (VERBOSE_INFO)
            printf("\nExplicit packet received.\n") ;
          break ;

        case OWL_REQUEST_CALIBRATION :
          if (VERBOSE_INFO)
            printf("\nExplicit calibration packet received.\n") ;
          extract_calibration_data(&pkt_data[offset], &request) ;
          break ;

        case OWL_REQUEST_AUTOCALIBRATION :
          if (VERBOSE_INFO)
            {
              printf("\nAutocalibration packet received.") ;
              if (! uses_autocalibration_request_port)
                printf(".. on the wrong port!") ;
              putchar('\n') ;
            }
          extract_calibration_data(&pkt_data[offset], &request) ;
          break ;

        default :
          if (VERBOSE_INFO)
            printf("\nStrange explicit packet received\n") ;
          fprintf(stderr,
                  "Error! Unknown request type (%d).\n", request.type) ;
          if (GET_MODE() == MODE_ACTIVE)
            return ;
          else
            {
              if (VERBOSE_INFO)
                printf("\nThis strange explicit packet will be handled"
                       " as an implicit one.\n") ;
              request.type = OWL_REQUEST_IMPLICIT ;
            }
        }
    }

  else if (GET_MODE() == MODE_PASSIVE || GET_MODE() == MODE_MIXED)
    {
      if (VERBOSE_CHATTERBOX)
        printf("\nImplicit packet received.\n") ;
      request.type = OWL_REQUEST_IMPLICIT ;
    }

  else // Active mode, packet was not an explicit request
    return ;

  /* Radiotap header handling */
  if (! extract_radiotap_ss(pkt_data, &request))
    fprintf(stderr, "Warning! The antenna signal field is absent from"
            " the radiotap header!\n") ;

  /* Display the packet details */
  if (VERBOSE_DISPLAY_CAPTURED)
    display_captured_request(&request, pkt_header) ;

  /* Send the request to the aggregator */
  nsent =
    sendto(aggregation_sockfd, &request, sizeof(request), 0,
           &aggregation_server, (socklen_t) sizeof(aggregation_server)) ;
  if (nsent != (ssize_t) sizeof(request))
    {
      perror("Error sending request to the aggregation server") ;
      return ;
    }
}



/*
 * Fills 'request' with the calibration data extracted from 'pkt_data'.
 * Note: 'pkt_data' is read from its first byte, therefore you must not
 * pass the whole received packet to this function.
 */
void extract_calibration_data(const u_char *const pkt_data,
                              owl_captured_request *const request)
{
  request->direction = pkt_data[0] ;
  assert(sizeof(float) == 4) ;
  memcpy(&request->x_position, &pkt_data[1], sizeof(float)) ;
  memcpy(&request->y_position, &pkt_data[5], sizeof(float)) ;
  memcpy(&request->z_position, &pkt_data[9], sizeof(float)) ;
}



/*
 * Fills 'request' with the number of packets and packet ID extracted
 * from 'pkt_data'.
 * Note: 'pkt_data' is read from its first byte, therefore you must not
 * pass the whole received packet to this function.
 */
void extract_packet_numbers(const u_char *const pkt_data,
                            owl_captured_request *const request)
{
  // Current packet's ID:
  memcpy(&request->packet_id, pkt_data,
         sizeof(uint16_t)) ;
  request->packet_id = request->packet_id ;
  // Number of packets:
  memcpy(&request->nb_packets, &pkt_data[sizeof(uint16_t)],
         sizeof(uint16_t)) ;
  request->nb_packets = request->nb_packets ;
}



/*
 * Fills 'request' with the signal strength extracted from the radiotap
 * header of 'pkt_data'.
 * Returns true if the antenna signal radiotap field was found, false
 * otherwhise.
 *
 * Ideas of improvement:
 *   http://www.unixgarden.com/index.php/gnu-linux-magazine-hs/introduction-a-la-programmation-wifi-en-c-sous-netbsd#8-exemple-n°5-radiotap
 */
bool extract_radiotap_ss(const u_char *const pkt_data,
                         owl_captured_request *const request)
{
  uint32_t rtap_presentflags ;
  uint_fast16_t rtap_position ;
  bool rtap_ext ;

  // Get the first Present Flags field from the Radiotap header:
  memcpy(&rtap_presentflags,
         &pkt_data[RTAP_P_PRESENTFLAGS], RTAP_L_PRESENTFLAGS) ;
  // The Radiotap header is little-endian
  rtap_presentflags = le32toh(rtap_presentflags) ;

  // The optional fields start right after the Present Flags field:
  rtap_position =
    RTAP_L_HREVISION + RTAP_L_HPAD +
    RTAP_L_HLENGTH + RTAP_L_PRESENTFLAGS ;

  // Skip the potential additional Present Flags fields:
  rtap_ext = FIELD_PRESENT(rtap_presentflags, RTAP_EXT) ;
  while (rtap_ext)
    {
      // Get the additional Present Flags field:
      uint32_t rtap_presentflags_ext ;
      memcpy(&rtap_presentflags_ext,
             &pkt_data[rtap_position], RTAP_L_PRESENTFLAGS) ;
      rtap_presentflags_ext = le32toh(rtap_presentflags_ext) ;
      // Check if there is another one after it:
      rtap_ext = FIELD_PRESENT(rtap_presentflags_ext, RTAP_EXT) ;
      // Skip the current field:
      rtap_position += SKIP_FIELD(rtap_position, RTAP_L_PRESENTFLAGS) ;
    }

  // Test the first bits of the flag field in order to check their
  // presence, up to the antenna signal field which is the only one
  // we need:

  if (FIELD_PRESENT(rtap_presentflags, RTAP_TSFT))
    rtap_position += SKIP_FIELD(rtap_position, RTAP_L_TSFT) ;

  if (FIELD_PRESENT(rtap_presentflags, RTAP_FLAGS))
    rtap_position += SKIP_FIELD(rtap_position, RTAP_L_FLAGS) ;

  if (FIELD_PRESENT(rtap_presentflags, RTAP_RATE))
    rtap_position += SKIP_FIELD(rtap_position, RTAP_L_RATE) ;

  if (FIELD_PRESENT(rtap_presentflags, RTAP_CHANNEL))
    {
      // The channel field is actually two fields that must be
      // aligned independently
      rtap_position += SKIP_FIELD(rtap_position, RTAP_L_CHANNEL) ;
      rtap_position += SKIP_FIELD(rtap_position, RTAP_L_CHANNELFLAGS) ;
    }

  if (FIELD_PRESENT(rtap_presentflags, RTAP_FHSS))
    {
      rtap_position += SKIP_FIELD(rtap_position, RTAP_L_FHSSHOPSET) ;
      rtap_position += SKIP_FIELD(rtap_position, RTAP_L_FHSSHOPPATTERN) ;
    }

  if (FIELD_PRESENT(rtap_presentflags, RTAP_ANTENNASIGNAL))
    {
      rtap_position += nat_align(rtap_position, RTAP_L_ANTENNASIGNAL) ;
      memcpy(&request->ss_dbm, &pkt_data[rtap_position],
             RTAP_L_ANTENNASIGNAL) ;
      if (VERBOSE_INFO)
        printf("Antenna signal: %"PRId8" dBm\n", request->ss_dbm) ;
      return true ;
    }

  // Antenna signal not extracted
  request->ss_dbm = 127 ; // maximum, improbable value
  return false ;
}



/*
 * Computes the number of padding bytes required to achieve natural
 * alignment of a field of length 'field_len', given the position
 * 'offset' it should normally be at in the buffer. The aligned
 * position is the sum of 'offset' and the returned value.
 *
 * To advance to the next available position, you can also use the macro
 * SKIP_FIELD(), which is a simple wrapper around this function.
 */
uint_fast16_t nat_align(const uint_fast16_t offset,
                        const uint_fast8_t field_len)
{
  uint_fast8_t alignment = offset % field_len ;
  if (alignment == 0)
    return 0 ;
  return field_len - alignment ;
}



void display_captured_request(const owl_captured_request *const request,
                              const struct pcap_pkthdr *const pkt_header)
{
  owl_timestamp tmp_time ;
  char
    request_time_str[OWL_TIMESTAMP_STRLEN],
    capture_time_str[OWL_TIMESTAMP_STRLEN],
    cp_mac_addr_str[OWL_ETHER_ADDR_STRLEN],
    mobile_mac_addr_str[OWL_ETHER_ADDR_STRLEN],
    mobile_ip_str[INET_ADDRSTRLEN] ;

  tmp_time = request->request_time ;
  owl_ntoh_timestamp(&tmp_time) ;
  owl_timestamp_to_string(&tmp_time, request_time_str) ;
  tmp_time = request->capture_time ;
  owl_ntoh_timestamp(&tmp_time) ;
  owl_timestamp_to_string(&tmp_time, capture_time_str) ;
  owl_mac_bytes_to_string_r(request->cp_mac_addr_bytes,
                            cp_mac_addr_str) ;
  owl_mac_bytes_to_string_r(request->mobile_mac_addr_bytes,
                            mobile_mac_addr_str) ;
  inet_ntop(AF_INET, &request->mobile_ip_addr_bytes,
            mobile_ip_str, INET_ADDRSTRLEN) ;

  printf("*** Request to send ***\n"
         "\tType: %"PRIu8"\n"
         "\tCP's MAC: %s\n"
         "\tMobile's MAC: %s\n"
         "\tMobile's IP: %s\n"
         "\tRequest timestamp: %s\n"
         "\tRequest arrival time on the CP: %s\n"
         "\tSignal: %"PRId8" dBm\n"
         "\tPosition X: %f\n"
         "\tPosition Y: %f\n"
         "\tPosition Z: %f\n"
         "\tDirection: %hhd\n"
         "\tPacket number: %"PRIu16"/%"PRIu16"\n"
         "\tPacket size: %"PRIu32"\n"
         ,
         request->type,
         cp_mac_addr_str,
         mobile_mac_addr_str,
         mobile_ip_str,
         request_time_str,
         capture_time_str,
         request->ss_dbm,
         owl_ntohf(request->x_position),
         owl_ntohf(request->y_position),
         owl_ntohf(request->z_position),
         request->direction,
         ntohs(request->packet_id),
         ntohs(request->nb_packets),
         pkt_header->len
         ) ;
}



/*
 * Gets the MAC address of the interface `iface` and copies it to
 * `mac_bytes`.
 */
void get_mac_addr(const char *const iface,
                  uint8_t mac_bytes[ETHER_ADDR_LEN])
#ifdef linux
{
  struct ifreq ifr;
  int sockfd ;

  // Empty mac_bytes:
  memset(mac_bytes, 0, sizeof(uint8_t) * ETHER_ADDR_LEN) ;

  sockfd = socket(AF_INET, SOCK_DGRAM, 0) ;
  if(sockfd < 0)
    {
      perror("Cannot open socket to read MAC address") ;
      return ;
    }

  strncpy(ifr.ifr_name, iface, IFNAMSIZ) ;

  // Note: the SIOCGIFHWADDR ioctl is Linux-specific
  if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0)
    {
      perror("ioctl(SIOCGIFHWADDR) error getting MAC address") ;
      return ;
    }

  memcpy(mac_bytes, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN) ;
}
#else // linux
{
  #define HWADDR_MIB_SIZE 6
  int mib[HWADDR_MIB_SIZE] ;
  int ifindex ; // iface's index
  char *buf = NULL ;
  size_t buflen = 0 ;

  // Empty mac_bytes:
  memset(mac_bytes, 0, sizeof(uint8_t) * ETHER_ADDR_LEN) ;

  ifindex = if_nametoindex(iface) ;
  if (ifindex == 0)
    {
      perror("Cannot get interface's index to get its MAC address") ;
      return ;
    }

  // See sysctl(7) for more information
  mib[0] = CTL_NET ; // First level = the net subtree
  mib[1] = PF_ROUTE ; // Second level = route
  mib[2] = 0 ; // Third level = protocol (always 0)
  mib[3] = AF_LINK ; // Fourth level = address family
  mib[4] = NET_RT_IFLIST ; // Fifth level = type of info
  mib[5] = ifindex ; /* sysctl(7) says there is no sixth level with
                      * NET_RT_IFLIST, but actually it has to be the
                      * interface's index */

  // Get the size of the available data
  if (sysctl(mib, HWADDR_MIB_SIZE, NULL, &buflen, NULL, 0))
    {
      perror("sysctl() error getting MAC address") ;
      return ;
    }

  buf = malloc(buflen) ;
  if (! buf)
    {
      perror("Cannot allocate memory") ;
      return ;
    }

  if (sysctl(mib, HWADDR_MIB_SIZE, buf, &buflen, NULL, 0))
    perror("sysctl() error getting MAC address") ;
  else
    {
      struct if_msghdr *ifm = (struct if_msghdr*)buf ;
      struct sockaddr_dl *sdl = (struct sockaddr_dl*)(ifm + 1) ;
      unsigned char *hwaddr = (unsigned char*)(LLADDR(sdl)) ;
      int i ;
      for (i = 0 ; i < 6 ; ++i)
        mac_bytes[i] = (uint8_t)hwaddr[i] ;
    }

  free(buf) ;
}
#endif // linux


/*
 * Gets the IP address of the interface `iface` and copies it to `ip`.
 */
void get_ip_addr(const char *const iface, char ip[INET_ADDRSTRLEN])
{
  struct ifreq ifr;
  int sockfd ;
  struct sockaddr_in sa ;
  struct in_addr ip_addr ;

  sockfd = socket(AF_INET, SOCK_DGRAM, 0) ;
  if(sockfd < 0)
    perror("Cannot open socket to read IP address") ;

  strncpy(ifr.ifr_name, iface, IFNAMSIZ) ;

  if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0)
    {
      perror("ioctl(SIOCGIFADDR) error getting IP address") ;
      return ;
    }

  memcpy(&ip_addr, &ifr.ifr_addr.sa_data[sizeof(sa.sin_port)],
         sizeof(ip_addr)) ;

  if (inet_ntop(AF_INET, &ip_addr, ip, INET_ADDRSTRLEN) == NULL)
    perror("inet_ntop() error getting IP address") ;
}



/* *** Autocalibration functions *** */
#ifdef OWLPS_LISTENER_USES_PTHREAD

void* autocalibrate_hello(void *const NULL_value)
{
  int send_sockfd ;
  struct sockaddr serv;
  owl_autocalibration_hello message ;

  if (VERBOSE_WARNING)
    fprintf(stderr, "Autocalibration Hello thread launched.\n") ;

  send_sockfd =
    owl_create_trx_socket(GET_AGGREGATION_HOST(),
                          GET_AUTOCALIBRATION_HELLO_PORT(),
                          &serv, NULL) ;
  pthread_cleanup_push(&owl_close_fd, &send_sockfd) ;

  memcpy(&message.cp_mac_addr_bytes, my_mac_bytes, ETHER_ADDR_LEN) ;

  while (owl_run)
    {
      owl_send_packet(send_sockfd, &serv,
                      &message, sizeof(message), VERBOSE_INFO) ;
      sleep(GET_AUTOCALIBRATION_HELLO_DELAY()) ;
    }

  /* Close the socket */
  pthread_cleanup_pop(1) ;

  pthread_exit(NULL_value) ;
}



void* autocalibrate(void *const NULL_value)
{
  int nread ; // recvfrom return value
  int listen_sockfd ;
  owl_autocalibration_order message ;

  if (VERBOSE_WARNING)
    fprintf(stderr, "Autocalibration thread launched.\n") ;

  // Socket to send autocalibration positioning requests
  autocalibration_send_sockfd =
    owl_create_trx_socket(GET_AUTOCALIBRATION_HOST(),
                          GET_AUTOCALIBRATION_REQUEST_PORT(),
                          &autocalibration_send_server,
                          GET_WIFI_IFACE()) ;

  // Socket to receive orders from the aggregator
  listen_sockfd =
    owl_create_udp_listening_socket(GET_AUTOCALIBRATION_ORDER_PORT()) ;
  if (listen_sockfd < 0)
    {
      perror("Error! Cannot create UDP listening socket from the"
             " aggregation server") ;
      exit(OWL_ERR_SOCKET_CREATE) ;
    }
  pthread_cleanup_push(&owl_close_fd, &listen_sockfd) ;

  while (owl_run)
    {
      nread = recvfrom(listen_sockfd, &message, sizeof(message), 0,
                       NULL, NULL) ;

      if (nread <= 0 && owl_run)
        {
          if (owl_run)
            perror("No message received from aggregator") ;
          continue ;
        }

      if (message.order == AUTOCALIBRATION_ORDER_SEND)
        {
          if (VERBOSE_INFO)
            fprintf(stderr, "I was just ordered to send an"
                    " autocalibration request...\n") ;
          send_autocalibration_request() ;
        }
      else
        fprintf(stderr,
                "Autocalibration order unknown: %d.\n", message.order) ;
    }

  /* Close the socket */
  pthread_cleanup_pop(1) ;

  pthread_exit(NULL_value) ;
}



void send_autocalibration_request()
{
  uint8_t *packet ;
  uint_fast16_t packet_size = make_packet(&packet) ;

  owl_send_request(autocalibration_send_sockfd,
                   &autocalibration_send_server,
                   packet, packet_size,
                   GET_AUTOCALIBRATION_NBPKT(),
                   GET_AUTOCALIBRATION_DELAY(),
                   VERBOSE_INFO) ;

  free(packet) ;
}



/*
 * Creates the calibration packet to send.
 * 'packet' must not be NULL. '*packet' must be freed by the calling
 * function.
 * Returns the size of the packet.
 * In case of error, 0 is returned and *packet is not updated.
 */
uint_fast16_t make_packet(uint8_t **const packet)
{
  uint8_t *pkt ;
  uint_fast16_t size ; // Packet size
  uint_fast16_t offset ; // Index used to create the packet
  owl_timestamp request_time ;
  float
    my_position_x = owl_htonf(GET_MY_POSITION_X()),
    my_position_y = owl_htonf(GET_MY_POSITION_Y()),
    my_position_z = owl_htonf(GET_MY_POSITION_Z()) ;
  uint16_t npkt ;

  assert(packet) ;

  owl_timestamp_now(&request_time) ;

  if (VERBOSE_CHATTERBOX)
    {
      char request_time_str[OWL_TIMESTAMP_STRLEN] ;
      owl_timestamp_to_string(&request_time, request_time_str) ;
      printf("Autocalibration time: %s\n", request_time_str) ;
    }

  owl_hton_timestamp(&request_time) ;

  offset = 0 ;
  size =
    sizeof(uint8_t) * 2 + sizeof(owl_timestamp) + sizeof(float) * 3 +
    sizeof(uint16_t) * 2 ;
  pkt = malloc(size) ;
  if (! pkt)
    {
      perror("Cannot allocate memory") ;
      return 0 ;
    }

  // Request type:
  memset(&pkt[offset++], OWL_REQUEST_AUTOCALIBRATION, 1) ;
  // Number of the current packet (1 for the first):
  npkt = htons(1u) ;
  memcpy(&pkt[offset], &npkt, sizeof(uint16_t)) ;
  offset += sizeof(uint16_t) ;
  // Number of packets:
  npkt = htons(GET_AUTOCALIBRATION_NBPKT()) ;
  memcpy(&pkt[offset], &npkt, sizeof(uint16_t)) ;
  offset += sizeof(uint16_t) ;
  // Timestamp:
  memcpy(&pkt[offset], &request_time, sizeof(request_time)) ;
  offset += sizeof(request_time) ;
  // Coordinates:
  pkt[offset++] = GET_MY_DIRECTION() ;
  memcpy(&pkt[offset], &my_position_x, sizeof(float)) ;
  offset += sizeof(float) ;
  memcpy(&pkt[offset], &my_position_y, sizeof(float)) ;
  offset += sizeof(float) ;
  memcpy(&pkt[offset], &my_position_z, sizeof(float)) ;
#ifndef NDEBUG
  offset += sizeof(float) ;
  assert(offset == size) ;
#endif // NDEBUG

  *packet = pkt ;
  return size ;
}

#endif // OWLPS_LISTENER_USES_PTHREAD
/* *** End of autocalibration functions *** */



void sigint_handler(const int num)
{
  owl_sigint_handler(num) ;
  pcap_breakloop(capture_handler) ;
}



void sigterm_handler(const int num)
{
  owl_sigterm_handler(num) ;
  pcap_breakloop(capture_handler) ;
}



void print_usage()
{
  printf("Usage :\n"
         "\t%s"
         " [-f config_file]"
         " [-G]"
         " [-D]"
         " [-v[v[v[v]]] | -q]"
         "\n\t"
         " <-r rtap_iface [-w wifi_iface] | -R pcap_file>"
         " [-K]"
         " [-m mode]"
         "\n\t"
         " [-l listening_port]"
         " [-i aggregation_host]"
         " [-p aggregation_port]"
         "\n\t"
         " [-A]"
         " [-I autocalibration_host]"
         " [-P autocalibration_request_port]"
         "\n\t"
         " [-O autocalibration_order_port]"
         " [-H hello_port]"
         " [-T hello_delay]"
         "\n\t"
         " [-t autocalibration_delay]"
         " [-n autocalibration_nb_packets]"
         "\n\t"
         " [direction x y z]\n"
         "\t%s -h\n"
         "\t%s -V\n"

         "Main options:\n"
         "\t-h\t\tPrint this help message and exit.\n"
         "\t-V\t\tShow version information and exit.\n"
         "\t-f config_file\tUse 'config_file' instead of the default"
         " configuration\n\t\t\tfile (%s).\n\t\t\tAvailable only if the"
         " program was linked against\n\t\t\tlibconfuse.\n"
         "\t-G\t\tDump the configuration on the standard output and exit"
         "\n\t\t\t(useful to generate a configuration file from the"
         "\n\t\t\tcurrent set of options).\n"
         "\t-D\t\tDaemon mode.\n"
         "\t-v\t\tBe verbose. You can use this option up to 4 times to"
         "\n\t\t\tincrease the level of verbosity (1 = warnings,"
         "\n\t\t\t2 = useful information, 3 = a lot of information,"
         "\n\t\t\t4 = display each captured packet).\n"
         "\t-q\t\tQuiet mode (default): sets the verbose level to 0.\n"

         "Capture options:\n"
         "\t-m mode\t\t\tCapture mode: a(ctive), p(assive), m(ixed)"
         "\n\t\t\t\t(default: a).\n"
         "\t-l listening_port\tPort to which explicit positioning"
         " requests are\n\t\t\t\tsent by the mobiles (default: %d).\n"
         "\t-i aggregation_host\tHost name or IP address of the"
         " aggregation\n\t\t\t\tserver (default: %s).\n"
         "\t-p aggregation_port\tRequests are transmitted to the"
         " aggregation\n\t\t\t\tserver on this port (default: %d).\n"
         "\t-r rtap_iface\t\tRadiotap-enabled capture interface.\n"
         "\t-w wifi_iface\t\tPhysical interface behind rtap_iface"
         " (default:\n\t\t\t\trtap_iface).\n"
         "\t-R pcap_file\t\tPcap file to read packets from.\n"

         "Autocalibration options:\n"
         "(These options are available only if the program was compiled"
         " with support of\nthe POSIX threads and the adequate"
         " compilation-time option.)\n"
         "\t-A\t\t\tEnable autocalibration (default: disabled).\n"
         "\t-I ac_host\t\tHost name or IP address of the destination"
         " of\n\t\t\t\tthe autocalibration requests"
         " (default:\n\t\t\t\taggregation_host).\n"
         "\t-P ac_request_port\tPort to which autocalibration"
         " requests are sent\n\t\t\t\t(default: listening_port).\n"
         "\t-O ac_order_port\tPort on which autocalibration orders are"
         "\n\t\t\t\treceived from the aggregation server (default:"
         "\n\t\t\t\t%d).\n"
         "\t-H hello_port\t\tPort to which hello messages are sent to"
         " the\n\t\t\t\taggregation server (default: %d).\n"
         "\t-T hello_delay\t\tTime between each hello message sent to"
         " the\n\t\t\t\taggregation server, in seconds (default:"
         " %d s).\n"
         "\t-t ac_delay\t\tTime between two autocalibration"
         " packets,\n\t\t\t\tin milliseconds (default: %d ms).\n"
         "\t-n ac_nb_packets\tNumber of packets transmitted"
         " for one\n\t\t\t\tautocalibration request (default: %d).\n"
         "\tdirection x y z\t\tThe coordinates of the listener"
         " (direction is an\n\t\t\t\tinteger; x, y, z are floats).\n"

         "Miscelanneous options:\n"
         "\t-K\tKeep the monitor mode up on wifi_iface. Use it with"
         " buggy\n\t\tdrivers that disable monitor mode periodically."
         " Available only\n\t\tif the program was compiled with the"
         " option\n\t\tOWLPS_LISTENER_KEEPS_MONITOR.\n"
         ,
         program_name,
         program_name,
         program_name,
         DEFAULT_CONFIG_FILE,
         OWL_DEFAULT_REQUEST_PORT,
         DEFAULT_AGGREGATION_HOST,
         OWL_DEFAULT_LISTENER_PORT,
         OWL_DEFAULT_AUTOCALIBRATION_ORDER_PORT,
         OWL_DEFAULT_AUTOCALIBRATION_HELLO_PORT,
         DEFAULT_AUTOCALIBRATION_HELLO_DELAY,
         DEFAULT_AUTOCALIBRATION_DELAY,
         DEFAULT_AUTOCALIBRATION_NBPKT
         ) ;
}


void print_version()
{
  printf("This is OwlPS Listener, part of the Owl Positioning System"
         " project.\n"
         "Version: %s.\n"
         "Compilation-time options:\n"
         "\tSupport for configuration file (libconfuse): %s.\n"
         "\tSupport for POSIX threads: %s.\n"
         "\tOption -K: %s.\n",
#ifdef OWLPS_VERSION
         OWLPS_VERSION
#else // OWLPS_VERSION
         "unknown version"
#endif // OWLPS_VERSION
         ,
#ifdef OWLPS_LISTENER_USES_CONFIG_FILE
         "YES"
#else // OWLPS_LISTENER_USES_CONFIG_FILE
         "NO"
#endif // OWLPS_LISTENER_USES_CONFIG_FILE
         ,
#ifdef OWLPS_LISTENER_USES_PTHREAD
         "YES"
#else // OWLPS_LISTENER_USES_PTHREAD
         "NO"
#endif // OWLPS_LISTENER_USES_PTHREAD
         ,
#ifdef OWLPS_LISTENER_KEEPS_MONITOR
         "YES"
#else // OWLPS_LISTENER_KEEPS_MONITOR
         "NO"
#endif // OWLPS_LISTENER_KEEPS_MONITOR
         ) ;
}
