/* * 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 #include #include #include #include #include #ifdef OWLPS_LISTENER_USES_CONFIG_FILE # include #endif // OWLPS_LISTENER_USES_CONFIG_FILE #ifdef OWLPS_LISTENER_USES_PTHREAD # include #endif // OWLPS_LISTENER_USES_PTHREAD #ifdef OWLPS_LISTENER_KEEPS_MONITOR # include #endif // OWLPS_LISTENER_KEEPS_MONITOR #include #include #include #define __FAVOR_BSD #include #include // for n_time on BSD (used in ip.h) #include #include #ifndef __GLIBC__ # include # include #endif // __GLIBC__ #include 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. */ // TODO: the size of this structure could be reduced by reordering its fields. // Note: the alignment comments below are for the case where all the #ifdef // match. struct { bool daemon ; char mode ; char aggregation_host[MAXHOSTNAMELEN] ; // 6 bytes alignment 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] ; // 7 bytes alignment char *pcap_file ; char wifi_iface[IFNAMSIZ + 1] ; #ifdef OWLPS_LISTENER_USES_PTHREAD bool autocalibration ; char autocalibration_host[MAXHOSTNAMELEN] ; // 6 bytes alignment 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 ; // 3 bytes alignment float my_position_x ; float my_position_y ; float my_position_z ; #endif // OWLPS_LISTENER_USES_PTHREAD uint_fast8_t verbose ; // 7 bytes alignment } 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 ) 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 ) ; }