/* * This file is part of the rtap localisation project. */ #include "owlps-listener.h" char *program_name = NULL ; unsigned char mac[6] ; // AP MAC address int aggregation_sockfd ; struct sockaddr_in aggregation_server ; #ifdef USE_CONFIG_FILE cfg_t *cfg ; // Configuration structure #else // USE_CONFIG_FILE /* If we do not use libconfuse, we declare a structure to store getopt * options. */ struct { char mode ; char aggregation_ip[16] ; long aggregation_port ; long listening_port ; #ifdef USE_PTHREAD BOOL keep_monitor ; #endif // USE_PTHREAD char rtap_iface[IFNAMSIZ + 1] ; char wifi_iface[IFNAMSIZ + 1] ; #ifdef USE_PTHREAD BOOL autocalibration ; long autocalibration_port ; long autocalibration_hello_delay ; #endif // USE_PTHREAD BOOL verbose ; } options = { // Initalise default options: MODE_ACTIVE, "127.0.0.1", AGGREGATE_DEFAULT_PORT, LOC_REQUEST_DEFAULT_PORT, #ifdef USE_PTHREAD FALSE, #endif // USE_PTHREAD "", "", #ifdef USE_PTHREAD FALSE, AUTOCALIBRATION_DEFAULT_PORT, AUTOCALIBRATION_DEFAULT_HELLO_DELAY, #endif // USE_PTHREAD FALSE } ; #endif // USE_CONFIG_FILE int main(int argc, char *argv[]) { struct sigaction action ; // Signal handler structure char *mac_string ; // AP MAC (string value) int ret ; // Program return value #ifdef USE_PTHREAD pthread_t keep_monitor_thread, autocalibration_hello_thread ; #endif // USE_PTHREAD program_name = argv[0] ; initialise_configuration(argc, argv) ; run = TRUE ; /* Set up signal handlers */ sigemptyset(&action.sa_mask) ; action.sa_handler = sigint_handler ; sigaction(SIGINT, &action, NULL) ; action.sa_handler = sigterm_handler ; sigaction(SIGTERM, &action, NULL) ; #ifdef USE_PTHREAD /* Set up threads */ if (GET_KEEP_MONITOR()) pthread_create(&keep_monitor_thread, NULL, (void *) &keep_mode_monitor, GET_WIFI_IFACE()) ; if (GET_AUTOCALIBRATION()) { pthread_create(&autocalibration_hello_thread, NULL, (void *) &autocalibrate_hello, NULL) ; } #endif // USE_PTHREAD get_mac_addr(GET_WIFI_IFACE(), mac) ; mac_string = mac_bytes_to_string(mac) ; printf("My MAC address is: %s\n", mac_string) ; free(mac_string) ; ret = capture() ; // Capture loop #ifdef USE_CONFIG_FILE cfg_free(cfg) ; // Clean configuration #endif // USE_CONFIG_FILE printf("%s: end.\n", program_name) ; return ret ; } void initialise_configuration(int argc, char **argv) { parse_config_file(argc, argv) ; parse_command_line(argc, argv) ; check_configuration() ; #ifdef DEBUG print_configuration() ; #endif // DEBUG } void parse_config_file(int argc, char **argv) { #ifdef USE_CONFIG_FILE // If we use libconfuse, we declare options: cfg_opt_t opts[] = { // Listening mode: a for active, p for passive, m for mixed // (default: a): CFG_INT("mode", MODE_ACTIVE, CFGF_NONE), // IP address of the aggregator (default: loopback): CFG_STR("aggregation_ip", "127.0.0.1", CFGF_NONE), // Port on which the aggregator listens: CFG_INT("aggregation_port", AGGREGATE_DEFAULT_PORT, CFGF_NONE), // Port on which mobiles send active requests: CFG_INT("listening_port", LOC_REQUEST_DEFAULT_PORT, CFGF_NONE), #ifdef USE_PTHREAD // 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 // USE_PTHREAD // Radiotap interface, used to capture: CFG_STR("rtap_iface", "", CFGF_NONE), // Physical interface corresponding to the radiotap interface (used // to get the MAC address): CFG_STR("wifi_iface", "", CFGF_NONE), #ifdef USE_PTHREAD // Autocalibration activated? CFG_BOOL("autocalibration", cfg_false, CFGF_NONE), // Port on which autocalibration data are exchanged: CFG_INT("autocalibration_port", AUTOCALIBRATION_DEFAULT_PORT, CFGF_NONE), // Delay between two hello messages: CFG_INT("autocalibration_hello_delay", AUTOCALIBRATION_DEFAULT_HELLO_DELAY, CFGF_NONE), #endif // USE_PTHREAD // Display capture packets, or not: CFG_BOOL("verbose", cfg_false, CFGF_NONE), CFG_END() } ; char *config_file ; // Configuration file name #endif // USE_CONFIG_FILE // Option -f specifies a config file, so we search for it first int opt ; do opt = getopt(argc, argv, OPTIONS) ; while (opt != 'f' && opt != -1) ; if (opt == 'f') { #ifdef USE_CONFIG_FILE config_file = malloc((strlen(optarg) + 1) * sizeof(char)) ; strcpy(config_file, optarg) ; #else // USE_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 // USE_CONFIG_FILE } #ifdef USE_CONFIG_FILE else // If -f isn't found, we use the default config file { config_file = malloc((strlen(DEFAULT_CONFIG_FILE) + 1) * sizeof(char)) ; 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 : fprintf(stderr, "Error! 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) ; exit(ERR_PARSING_CONFIG_FILE) ; } free(config_file) ; #endif // USE_CONFIG_FILE } void parse_command_line(int argc, char **argv) { int opt ; optind = 1 ; // Rewind argument parsing while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'A' : #ifdef USE_PTHREAD SET_AUTOCALIBRATION() ; #else // USE_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 // USE_PTHREAD break ; case 'a' : #ifdef USE_PTHREAD SET_AUTOCALIBRATION_PORT(strtol(optarg, NULL, 0)) ; #endif // USE_PTHREAD break ; case 'd' : SET_AGGREGATION_IP(optarg) ; break ; case 'f' : // Config file break ; // (already parsed) case 'h' : print_usage() ; exit(0) ; case 'H' : SET_AUTOCALIBRATION_HELLO_DELAY(strtol(optarg, NULL, 0)) ; break ; case 'k' : #ifdef USE_PTHREAD SET_KEEP_MONITOR() ; #else // USE_PTHREAD fprintf(stderr, "Warning! The program was compiled without" " support of POSIX threads, so -k (monitor mode" " keeping-up) is not available and will be ignored." "\n") ; #endif // USE_PTHREAD break ; case 'l' : SET_LISTENING_PORT(strtol(optarg, NULL, 0)) ; break ; case 'm' : SET_MODE(optarg[0]) ; break ; case 'p' : SET_AGGREGATION_PORT(strtol(optarg, NULL, 0)) ; break ; case 'q' : UNSET_VERBOSE() ; break ; case 'r' : SET_RTAP_IFACE(optarg) ; break ; case 'v' : SET_VERBOSE() ; break ; case 'w' : SET_WIFI_IFACE(optarg) ; break ; default : print_usage() ; exit(ERR_BAD_USAGE) ; } } } void check_configuration() { 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() ; exit(ERR_BAD_USAGE) ; } if (GET_RTAP_IFACE()[0] == '\0') { fprintf(stderr, "Error! You must specify a radiotap interface" " for the capture.\n") ; print_usage() ; exit(ERR_BAD_USAGE) ; } if (GET_WIFI_IFACE()[0] == '\0') { #ifdef DEBUG fprintf(stderr, "Warning! No Wi-Fi was specified. Failing back to" " the radiotap interface (%s) instead.\n", GET_RTAP_IFACE()) ; #endif // DEBUG SET_WIFI_IFACE(GET_RTAP_IFACE()) ; } } #ifdef DEBUG void print_configuration() { fprintf(stderr, "Configuration:\n") ; #ifdef USE_CONFIG_FILE cfg_print(cfg, stderr) ; #else // USE_CONFIG_FILE fprintf(stderr, "aggregation_ip = \"%s\"\n" "aggregation_port = %ld\n" "listening_port = %ld\n" "rtap_iface = \"%s\"\n" "wifi_iface = \"%s\"\n" , GET_AGGREGATION_IP(), GET_AGGREGATION_PORT(), GET_LISTENING_PORT(), GET_RTAP_IFACE(), GET_WIFI_IFACE() ); #ifdef USE_PTHREAD fprintf(stderr, "keep_monitor = %s\n", GET_KEEP_MONITOR() ? "true" : "false" ) ; #endif // USE_PTHREAD #endif // USE_CONFIG_FILE } #endif // DEBUG #ifdef USE_PTHREAD /* * Thread function. Switches interface 'iface' to monitor mode every * second. */ void keep_mode_monitor(char *iface) { #ifdef DEBUG fprintf(stderr, "Thread for keeping monitor mode launched.\n") ; #endif // DEBUG while (run) { iface_mode_monitor(iface) ; // Switch the interface to monitor mode sleep(1) ; // Wait for 1 second } } #endif // USE_PTHREAD /* * Captures packets using the radiotap interface. * Captured data is transmitted to the aggregator. */ int capture() { pcap_t *handle ; // Packet capture descriptor char errbuf[PCAP_ERRBUF_SIZE] ; // Error message // Start capture: handle = pcap_open_live(GET_RTAP_IFACE(), BUFSIZ, 1, 1000, errbuf) ; if (handle == NULL) // Capture starting failed { fprintf(stderr, "Cannot open capture interface %s\n", errbuf) ; return ERR_OPENING_IFACE ; } /* Open UDP socket to the aggregator */ aggregation_sockfd = owlps_create_socket_to_aggregator(GET_AGGREGATION_IP(), GET_AGGREGATION_PORT(), &aggregation_server, NULL) ; while(run) // Capture one packet at time, and call read_packet() on it: pcap_loop(handle, 1, read_packet, NULL) ; pcap_close(handle) ; // Stop capture (void) close(aggregation_sockfd) ; // Close socket return 0 ; } /* * Treats a packet and sends it to the aggregator. */ void read_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { // Copy packet address into data: unsigned char *data = (unsigned char *) packet ; unsigned short rtap_bytes ; // Received data size unsigned int rtap_presentflags, rtap_position ; couple_message couple ; // Message to send to the aggregator ssize_t nsent ; // sendto return value BOOL check[15] ; // Present flags unsigned char raw_packet_type ; // Received packet type (beacon, data…) unsigned char raw_packet_flags ; // IEEE 802.11 header flags unsigned short llc_packet_type = 0 ; // Pointer to the (possible) IP header of the packet: struct iphdr *packet_ip_header = NULL ; // Pointer to the (possible) UDP header of the packet: struct udphdr *packet_udp_header = NULL ; char packet_type ; // Localisation request type (request, calibration) BOOL is_explicit_packet = TRUE ; // Is the packet an explicit request? int i ; // Iterator /* Common treatements */ // Copy 2 bytes from the 3rd data byte, that is the size of the rtap // header (changes with the flags): memcpy(&rtap_bytes, &data[2], sizeof(unsigned short)) ; rtap_bytes = ntohs(rtap_bytes) ; // After the rtap header, there is the 802.11 header; the first byte // is the packet type (beacon or not): raw_packet_type = data[rtap_bytes] ; if (raw_packet_type == RAW_PACKET_TYPE_DATA) // Data packet { // Get the packet type (protocol, 2 bytes) from the LLC header: memcpy((unsigned char*) &llc_packet_type, &data[rtap_bytes + IEEE80211_HEADER_SIZE + 6], 2) ; llc_packet_type = ntohs(llc_packet_type) ; if (llc_packet_type == ETH_P_IP) // IP packet { packet_ip_header = (struct iphdr *) &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE] ; // Get the source IP: memcpy(couple.mobile_ip_addr_bytes, &packet_ip_header->saddr, 4) ; if (GET_MODE() != MODE_PASSIVE) // If mode is active or mixed { // Protocol for an explicit request is UDP if (packet_ip_header->protocol == IPPROTO_UDP) { // Check destination port: packet_udp_header = (struct udphdr *) &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr)] ; if (ntohs(packet_udp_header->dest) != GET_LISTENING_PORT()) { if (GET_MODE() == MODE_ACTIVE) return ; is_explicit_packet = FALSE ; } } } } else if (GET_MODE() != MODE_ACTIVE) // Passive or mixed mode { is_explicit_packet = FALSE ; bzero(couple.mobile_ip_addr_bytes, 4) ; // Blank the IP } else // Active mode and not an IP packet, so it is not a request return ; } else // Packet is not data, so it is not a localisation request { if (GET_MODE() == MODE_ACTIVE) return ; is_explicit_packet = FALSE ; } // Get 802.11 flags from the 802.11 header: raw_packet_flags = data[rtap_bytes+1] ; #ifdef DEBUG printf("raw_packet_flags: %02x\n", raw_packet_flags) ; if (IS_RETRY(raw_packet_flags)) printf("This packet is a Retry.\n") ; #endif // DEBUG memcpy(couple.ap_mac_addr_bytes, mac, 6) ; // Copy AP MAC // Source MAC address is 10 bytes after the 802.11 packet type: memcpy(couple.mobile_mac_addr_bytes, &data[rtap_bytes+10], 6) ; couple.start_time = header->ts ; // Capture time is in the pcap header // Transmission time on the mobile is unknown (unless the packet is // an explicit request): couple.request_time.tv_sec = 0 ; couple.request_time.tv_usec = 0 ; /* Active mode */ if (is_explicit_packet && (GET_MODE() == MODE_ACTIVE || GET_MODE() == MODE_MIXED) // FIXME: should we really ignore Retries? && ! IS_RETRY(raw_packet_flags)) { packet_type = data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr) + sizeof(struct udphdr)] ; switch(packet_type) { case PACKET_TYPE_NORMAL : if (GET_VERBOSE()) printf("\nExplicit packet received.\n") ; couple.direction = 0 ; couple.x_position = 0 ; couple.y_position = 0 ; couple.z_position = 0 ; break ; case PACKET_TYPE_CALIBRATION : if (GET_VERBOSE()) printf("\nExplicite calibration packet received.\n") ; couple.direction = data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr) + sizeof(struct udphdr) + 9]; memcpy(&couple.x_position, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr) + sizeof(struct udphdr) + 10], sizeof(float)) ; memcpy(&couple.y_position, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr) + sizeof(struct udphdr) + 14], sizeof(float)) ; memcpy(&couple.z_position, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr) + sizeof(struct udphdr) + 18], sizeof(float)) ; break ; default : if (GET_VERBOSE()) printf("\nStrange explicit packet received\n") ; fprintf(stderr, "Error! Unknown packet type (%d).\n", packet_type) ; is_explicit_packet = FALSE ; } if (! is_explicit_packet) { if (GET_MODE() == MODE_ACTIVE) return ; else if (GET_VERBOSE()) printf("\nThis strange explicit packet will be handled as" " an implicit one.\n") ; } else memcpy(&couple.request_time, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + sizeof(struct iphdr) + sizeof(struct udphdr) + 1], sizeof(struct timeval)) ; } else if (GET_MODE() == MODE_PASSIVE || GET_MODE() == MODE_MIXED) { if (GET_VERBOSE()) printf("\nImplicit packet received.\n") ; } else // Active mode, packet was not an explicit request return ; /* Radiotap header handling */ // Get rtap flags: memcpy(&rtap_presentflags, &data[RTAP_P_PRESENTFLAGS], RTAP_L_PRESENTFLAGS) ; /* We get the flags in big-endian (net-endianess), but we work on them * as if it was in little-endian. This allows to declare an array of 15 * bits (instead of 32), because we work only on the least significant * bits (and so we do not reserve space for most significant bits that * are useless). It's kind of cheat :-) * So, on big-endian architectures, we must inverse bits as if we had * got flags in little-endian: */ rtap_presentflags = le32toh(rtap_presentflags) ; for (i = 0 ; i < 15 ; i++) // Initialise present flags structure check[i] = FALSE ; rtap_position = 8 ; // Begining of the present flags determined fields // Test the first 15 bits of the flag field in order to check their // presence and to copy them: for (i = 0 ; i < 15 ; i++) { if ((rtap_presentflags % 2) == 1) { switch(i) { case RTAP_MACTS: check[RTAP_MACTS] = TRUE ; rtap_position += RTAP_L_MACTS ; break ; case RTAP_FLAGS: check[RTAP_FLAGS] = TRUE; rtap_position += RTAP_L_FLAGS ; break ; case RTAP_RATE: check[RTAP_RATE] = TRUE; rtap_position += RTAP_L_RATE ; break ; case RTAP_CHANNEL: rtap_position += RTAP_L_CHANNEL ; rtap_position += RTAP_L_CHANNELTYPE ; break ; case RTAP_FHSS: check[RTAP_FHSS] = TRUE; rtap_position += RTAP_L_FHSS ; break ; case RTAP_ANTENNASIGNALDBM: memcpy(&(couple.antenna_signal_dbm), &data[rtap_position], RTAP_L_ANTENNASIGNALDBM) ; check[RTAP_ANTENNASIGNALDBM] = TRUE; if (GET_VERBOSE()) printf("Antenna signal: %d dBm\n", couple.antenna_signal_dbm - 0x100); rtap_position += RTAP_L_ANTENNASIGNALDBM ; break ; case RTAP_ANTENNANOISEDBM: check[RTAP_ANTENNANOISEDBM] = TRUE; rtap_position += RTAP_L_ANTENNANOISEDBM ; break ; case RTAP_LOCKQUALITY: check[RTAP_LOCKQUALITY] = TRUE; rtap_position += RTAP_L_LOCKQUALITY ; break ; case RTAP_TXATTENUATION: check[RTAP_TXATTENUATION] = TRUE; rtap_position += RTAP_L_TXATTENUATION ; break ; case RTAP_TXATTENUATIONDB: check[RTAP_TXATTENUATIONDB] = TRUE; rtap_position += RTAP_L_TXATTENUATIONDB ; break ; case RTAP_TXATTENUATIONDBM: check[RTAP_TXATTENUATIONDBM] = TRUE; rtap_position += RTAP_L_TXATTENUATIONDBM ; break ; case RTAP_ANTENNA: check[RTAP_ANTENNA] = TRUE; rtap_position += RTAP_L_ANTENNA ; break ; case RTAP_ANTENNASIGNALDB: check[RTAP_ANTENNASIGNALDB] = TRUE; rtap_position += RTAP_L_ANTENNASIGNALDB ; break ; case RTAP_ANTENNANOISEDB: check[RTAP_ANTENNANOISEDB] = TRUE; rtap_position += RTAP_L_ANTENNANOISEDB ; break ; case RTAP_FCS: check[RTAP_FCS] = TRUE; rtap_position += RTAP_L_FCS ; break ; } } rtap_presentflags /= 2 ; } if (GET_VERBOSE()) { char *ap_mac_string = mac_bytes_to_string(couple.ap_mac_addr_bytes) ; char *mobile_mac_string = mac_bytes_to_string(couple.mobile_mac_addr_bytes) ; printf("*** Couple to send ***\n" "\tMAC AP: %s\n" "\tMobile MAC: %s\n" "\tSequence number (request time): %llu\n" "\tRequest arrival time on the AP: %llu\n" "\tSignal: %d dBm\n" "\tPosition X: %f\n" "\tPosition Y: %f\n" "\tPosition Z: %f\n" "\tDirection: %hhd\n" , ap_mac_string, mobile_mac_string, timeval_to_ms(couple.request_time), timeval_to_ms(couple.start_time), couple.antenna_signal_dbm - 0x100, couple.x_position, couple.y_position, couple.z_position, couple.direction ) ; free(ap_mac_string) ; free(mobile_mac_string) ; } /* Send couple to the aggregator */ nsent = sendto(aggregation_sockfd, (void *) &couple, sizeof(couple), 0, (struct sockaddr *) &aggregation_server, (socklen_t) sizeof(aggregation_server)) ; if (nsent != (ssize_t) sizeof(couple)) { perror("Error sending couple to the aggregation server") ; return ; } } /* * Get our own MAC address and copy it to 'mac_bytes'. */ void get_mac_addr(char *eth, unsigned char mac_bytes[6]) { struct ifreq ifr; int sockfd ; bzero(mac_bytes, sizeof(unsigned char) * 6) ; // Empty mac_bytes sockfd = socket(AF_INET, SOCK_DGRAM, 0) ; if(sockfd < 0) perror("Cannot open socket to read MAC address") ; strncpy(ifr.ifr_name, eth, IFNAMSIZ) ; if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) return ; if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) return ; memcpy(mac_bytes, ifr.ifr_hwaddr.sa_data, 6) ; } /* *** Autocalibration functions *** */ #ifdef USE_PTHREAD void autocalibrate_hello() { int send_sockfd ; struct sockaddr_in serv; struct sockaddr_in client ; socklen_t serv_len = sizeof(serv); autocalibration_hello message ; send_sockfd = create_udp_sending_socket(GET_AGGREGATION_IP(), GET_AUTOCALIBRATION_PORT(), &serv, &client) ; memcpy(&message.ap_mac_addr_bytes, mac, 6) ; while (run) { sendto(send_sockfd, (void *)&message, sizeof(message), 0, (struct sockaddr *)&serv, serv_len) ; sleep(GET_AUTOCALIBRATION_HELLO_DELAY()) ; } (void) close(send_sockfd) ; } #endif // USE_PTHREAD /* *** End of autocalibration functions *** */ void print_usage() { printf("Usage :\n" "\t%s [-f config_file] [-m mode] [-d aggregation_ip]" " [-l listening_port] [-p aggregation_port] -r rtap_iface" " [-w wifi_iface] [-k] [-v | -q] [-A] [-a autocalibration_port]" " [-H autocalibration_hello_delay]\n" "Main options:\n" "\t-h\t\tPrint this help.\n" "\t-f config_file\tUse 'config_file' instead of the default" " configuration file (%s). Available only if program was" " compiled with libconfuse.\n" "Capture options:\n" "\t-m mode\t\t\tCapture mode: a(ctive), p(assive), m(ixed)" " (default: a).\n" "\t-l listening_port\tPort on which explicit positioning" " requests are sent by mobiles (default: %d).\n" "\t-d aggregation_ip\tIP address of the aggregation server" " (default: 127.0.0.1)\n" "\t-p aggregation_port\tRequests are transmitted to the" " aggregation server on this port (default: %d).\n" "\t-r rtap_iface\t\tRadiotap capture interface.\n" "\t-w wifi_iface\t\tPhysical interface behind rtap_iface" " (default: rtap_iface).\n" "Autocalibration options:\n" "(These options are available only if the program was compiled" " with support of POSIX threads.)\n" "\t-A\t\tEnable autocalibration (default: disabled).\n" "\t-a port\t\tPort on which autocalibration data" " are exchanged with the aggregation server (default: %d).\n" "\t-H hello_delay\tTime between each hello" " message sent to the aggregation server (default: %d s).\n" "Other options:\n" "\t-k\tKeep the monitor mode up on wifi_iface. Use it with buggy" " drivers that disable monitor mode periodically. Available" " only if the program was compiled with support of POSIX" " threads.\n" "\t-v\tVerbose mode (display captured packets).\n" "\t-q\tQuiet mode (default).\n" , program_name, DEFAULT_CONFIG_FILE, LOC_REQUEST_DEFAULT_PORT, AGGREGATE_DEFAULT_PORT, AUTOCALIBRATION_DEFAULT_PORT, AUTOCALIBRATION_DEFAULT_HELLO_DELAY ) ; }