/* * This file is part of the rtap localisation project. */ #include "owlps-listener.h" #define DEBUG unsigned char mac[6] ; // Adresse MAC de l'AP #ifdef USE_CONFIG_FILE cfg_t *cfg ; // Structure contenant la configuration #else // USE_CONFIG_FILE // Si on n'utilise pas libconfuse, on déclare une struct { // structure qui servira à stocker les options getopt : 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] ; } options = { // et on initalise les options par défaut : "127.0.0.1", AGGREGATE_DEFAULT_PORT, #ifdef USE_PTHREAD FALSE, #endif // USE_PTHREAD "", "" } ; #endif // USE_CONFIG_FILE int main(int argc, char *argv[]) { struct sigaction action ; // Structure de mise en place des gestionnaires de signaux char *mac_string ; // MAC de l'AP sous forme de chaîne int ret ; // Code de retour du programme #ifdef USE_PTHREAD pthread_t thread ; // Thread pour le repassage en mode monitor #endif // USE_PTHREAD #ifdef USE_CONFIG_FILE // Si on utilise libconfuse, on déclare les options : cfg_opt_t opts[] = { // Options reconnues par confuse dans le fichier de config CFG_STR("aggregation_ip", "127.0.0.1", CFGF_NONE), // Adresse IP du serveur d'agrégation (défaut : boucle locale) CFG_INT("aggregation_port", AGGREGATE_DEFAULT_PORT, CFGF_NONE), // Port d'envoi sur le serveur d'agrégation CFG_INT("listening_port", LOC_REQUEST_DEFAULT_PORT, CFGF_NONE), // Port d'écoute des demandes des mobiles #ifdef USE_PTHREAD CFG_BOOL("keep_monitor", cfg_false, CFGF_NONE), // Active le maintien actif du mode monitor #endif // USE_PTHREAD CFG_STR("rtap_iface", "", CFGF_NONE), // Interface radiotap utilisée pour la capture CFG_STR("wifi_iface", "", CFGF_NONE), // Interface physique correspondante (utilisée pour récupérer l'adresse MAC) CFG_END() } ; char *config_file ; // Nom du fichier de configuration #endif // USE_CONFIG_FILE int opt ; // Retour de getopt /* L'option -f sert à spécifier un fichier de configuration alternatif, * on regarde en premier si elle est présente */ 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, "Attention ! Le programme a été compilé sans le support des fichiers de configuration, l'option -f n'est donc pas disponible. Vous devez passer toutes les options sur la ligne de commandes, sans quoi les valeurs par défaut seront utilisées.\n") ; #endif // USE_CONFIG_FILE } #ifdef USE_CONFIG_FILE else // Si -f n'est pas présente, on utilise le fichier de config par défaut { config_file = malloc((strlen(DEFAULT_CONFIG_FILE) + 1) * sizeof(char)) ; strcpy(config_file, DEFAULT_CONFIG_FILE) ; } /* Lecture du fichier de configuration */ cfg = cfg_init(opts, CFGF_NONE) ; // Initialisation des options if (cfg_parse(cfg, config_file) == CFG_PARSE_ERROR) { free(config_file) ; return ERR_PARSING_CONFIG_FILE ; } free(config_file) ; #endif // USE_CONFIG_FILE /* Lecture des arguments de la ligne de commandes */ optind = 1 ; // On reprend l'analyse des arguments au début while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'd' : SET_AGGREGATION_IP(optarg) ; break ; case 'f' : // Fichier de configuration // Déjà traité. break ; case 'k' : #ifdef USE_PTHREAD SET_KEEP_MONITOR() ; #else // USE_PTHREAD fprintf(stderr, "Attention ! Le programme a été compilé sans le support des threads POSIX, l'option -k (maintien du mode monitor) n'est donc pas disponible et sera ignorée.\n") ; #endif // USE_PTHREAD break ; case 'l' : SET_LISTENING_PORT(strtol(optarg, NULL, 0)) ; break ; case 'p' : SET_AGGREGATION_PORT(strtol(optarg, NULL, 0)) ; break ; case 'r' : SET_RTAP_IFACE(optarg) ; break ; case 'w' : SET_WIFI_IFACE(optarg) ; break ; default : print_usage(argv[0]) ; return ERR_BAD_USAGE ; } } /* Vérification des arguments */ if (GET_RTAP_IFACE()[0] == '\0') { fprintf(stderr, "Erreur ! Vous devez spécifier une interface radiotap pour la capture.\n") ; print_usage(argv[0]) ; return ERR_BAD_USAGE ; } if (GET_WIFI_IFACE()[0] == '\0') { #ifdef DEBUG fprintf(stderr, "Attention ! Aucune interface Wi-Fi spécifiée. Utilisation de l'interface radiotap (%s) à la place.\n", GET_RTAP_IFACE()) ; #endif // DEBUG SET_WIFI_IFACE(GET_RTAP_IFACE()) ; } #ifdef DEBUG /* Affichage de la 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 run = TRUE ; /* Mise en place des gestionnaires de signaux */ 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 /* Création du thread */ if (GET_KEEP_MONITOR()) pthread_create(&thread, NULL, (void *) &keep_mode_monitor, GET_WIFI_IFACE()) ; #endif // USE_PTHREAD get_mac_addr(GET_WIFI_IFACE(), mac) ; mac_string = mac_bytes_to_string(mac) ; printf("Ma mac est : %s\n", mac_string) ; free(mac_string) ; ret = capture(GET_RTAP_IFACE(), GET_AGGREGATION_IP(), GET_AGGREGATION_PORT(), TRUE) ; #ifdef USE_CONFIG_FILE cfg_free(cfg) ; // Nettoyage de la configuration #endif // USE_CONFIG_FILE printf("%s : fin.\n", argv[0]) ; return ret ; } #ifdef USE_PTHREAD /* Fonction du thread, qui surveille la liste et envoie les infos au serveur de localisation au bout du timeout */ void* keep_mode_monitor(char *iface) { while (run) { iface_mode_monitor(iface) ; // Passage de l'interface en mode Monitor. sleep(1) ; // Pause de 1 secondes. } pthread_exit(NULL) ; } #endif // USE_PTHREAD /* Capture des paquets en utilisant l'interface "capture_iface". * Les données capturées sont envoyées au serveur d'aggrégation dont l'IP est "aggregated_ip". */ int capture(char *capture_iface, char *aggregation_ip, unsigned int aggregation_port, BOOL print_values) { pcap_t *handle ; // Descripteur de capture de paquets char errbuf[PCAP_ERRBUF_SIZE] ; // Message d'erreur int sockfd ; // Descripteur de la socket vers le serveur d'aggrégation struct sockaddr_in server, client ; /* Sous-fonction de traitement des paquets capturés */ void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { read_packet(args, header, packet, sockfd, &server, print_values) ; // On appelle la fonction read_packet() avec les mêmes arguments, plus "print_values" qui indique si on doit afficher le paquet. } handle = pcap_open_live(capture_iface, BUFSIZ, 1, 1000, errbuf) ; // Début de la capture if (handle == NULL) // Le lancement de la capture a-t-il échoué ? { fprintf(stderr, "Impossible d'ouvrir l'interface « %s » : %s\n", capture_iface, errbuf) ; return ERR_OPENING_IFACE ; } /* Ouverture de la socket UDP vers le serveur d'aggrégation */ sockfd = create_udp_sending_socket(aggregation_ip, aggregation_port, &server, &client); if (sockfd < 0) { perror("Erreur ! Impossible de créer la socket vers le serveur d'aggrégation \n"); return ERR_CREATING_SOCKET ; } while(run) { pcap_loop(handle, 1, got_packet, NULL) ; // Collecte 1 paquet et appelle la fonction got_packet quand pcaploop a recupéré des paquets } pcap_close(handle) ; // Arrêt de la capture. (void) close(sockfd) ; // Fermeture de la socket return 0 ; } /* Traite un paquet et l'envoie au serveur d'agrégation sur la socket UDP "sockfd", au serveur "server". */ void read_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet, int sockfd, struct sockaddr_in *server, BOOL print_values) { unsigned char *data = (unsigned char *) packet ; // Recopie dans data l'adresse du paquet capturé unsigned short rtap_bytes ; // Taille des données reçues unsigned int rtap_presentflags, rtap_position ; couple_message couple ; // Message à envoyer à l'agrégateur ssize_t nsent ; // Retour de sendto BOOL check[15] ; // Champs présents unsigned char raw_packet_type ; // Type du packet reçu (beacon, data...) unsigned char raw_packet_flags ; // Flags de l'en-tête IEEE 802.11 char packet_type ; // Type de demande de localisation (demande, calibration) unsigned short dst_port ; // Port destination du paquet capturé int i ; // Compteur memcpy(&rtap_bytes, &data[2], sizeof(unsigned short)) ; // Recopie les deux octets à partir du troisième octet de donnée qui est la taille de l'en-tête rtap (change avec des flags) #ifdef PLATFORM_ATHEROS rtap_bytes = ((rtap_bytes & 0xff00) >> 8) + ((rtap_bytes & 0xff) << 8) ; #endif // PLATFORM_ATHEROS raw_packet_type = data[rtap_bytes] ; // Au bout de l'en-tête rtap il y a celle du 802.11 dont le premier determine le type (beacon ou pas) #ifdef PLATFORM_ATHEROS memcpy((unsigned char*) &dst_port, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + 2], 2) ; // On récupère le port de destination #else // PLATFORM_ATHEROS memcpy((unsigned char*) &dst_port, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + 3], 1) ; // On récupère le port de destination (mais il faut retourner les deux octets) memcpy(&((unsigned char*) &dst_port)[1], &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + 2], 1) ; // Port de destination = troisième et quatrième octets suivant l'en-tête IP. #endif // PLATFORM_ATHEROS raw_packet_flags = data[rtap_bytes+1] ; // On récupère les flags 802.11, qui sont dans l'en-tête 802.11. #ifdef DEBUG if (raw_packet_type == RAW_PACKET_TYPE_DATA && data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + 9] == RAW_PACKET_PROTO_UDP && dst_port == GET_LISTENING_PORT()) { printf("raw_packet_flags : %02x\n", raw_packet_flags) ; if (IS_RETRY(raw_packet_flags)) printf("Retry : pas à traiter\n") ; } #endif // DEBUG if (raw_packet_type == RAW_PACKET_TYPE_DATA // Si le paquet est de type data, && data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + 9] == RAW_PACKET_PROTO_UDP // et de protocole UDP (9 : position du champ "Protocol" de l'en-tête IP), && dst_port == LOC_REQUEST_DEFAULT_PORT // et le port de destination est le bon, && ! IS_RETRY(raw_packet_flags)) // et ce n'est pas un Retry. { memcpy(couple.ap_mac_addr_bytes, mac, 6); // On copie la MAC de l'AP memcpy(couple.mobile_mac_addr_bytes, &data[rtap_bytes+10], 6); // L'adresse MAC source est 10 octets plus loin que le type de paquet memcpy(couple.mobile_ip_addr_bytes, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + 12], 4); // L'adresse IP source est à la position 12 de l'en-tête IP gettimeofday(&couple.start_time, NULL) ; packet_type = data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE] ; memcpy(&couple.request_time, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE + 1], sizeof(struct timeval)); switch(packet_type) { case PACKET_TYPE_NORMAL : if (print_values) printf("\nPaquet normal reçu.\n") ; couple.direction = 0 ; couple.x_position = 0 ; couple.y_position = 0 ; couple.z_position = 0 ; break ; case PACKET_TYPE_CALIBRATION : if (print_values) printf("\nPaquet de calibration reçu.\n") ; couple.direction = data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE + 9]; memcpy(&couple.x_position, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE + 10], sizeof(float)); memcpy(&couple.y_position, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE + 14], sizeof(float)); memcpy(&couple.z_position, &data[rtap_bytes + IEEE80211_HEADER_SIZE + LLC_HEADER_SIZE + IP_HEADER_SIZE + UDP_HEADER_SIZE + 18], sizeof(float)); break ; default : if (print_values) printf("\nPaquet bizarre reçu.\n") ; fprintf(stderr, "Erreur ! Type de paquet inconnu (%d).\n", packet_type) ; return ; } memcpy(&rtap_presentflags, &data[RTAP_P_PRESENTFLAGS], RTAP_L_PRESENTFLAGS); // Récupère les flags de l'en-tête rtap #ifdef PLATFORM_ATHEROS rtap_presentflags = ((rtap_presentflags & 0xff000000) >> 24) + ((rtap_presentflags & 0xff0000) >> 8) + ((rtap_presentflags & 0xff00) << 8) + ((rtap_presentflags & 0xff) << 24) ; #endif // PLATFORM_ATHEROS for (i = 0 ; i < 15 ; i++) // Initialisation de la structure des champs présents check[i] = FALSE ; rtap_position = 8 ; // Début des champs déterminés par le present flag for(i=0 ; i < 15 ; i++) // on teste les 15 premiers bits du champ flag afin de valider la présence et de les copier { 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 (print_values) 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 (print_values) { 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 à envoyer ***\n\ \tMAC AP : %s\n\ \tMAC mobile : %s\n\ \tNuméro de séquence (heure de la demande) : %llu\n\ \tHeure d'arrivée de la demande de localisation sur l'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) ; } /* Envoi du couple au serveur d'aggrégation */ nsent = sendto(sockfd, (void *) &couple, sizeof(couple), 0, (struct sockaddr *) server, (socklen_t) sizeof(*server)) ; if (nsent != (ssize_t) sizeof(couple)) { perror("Erreur lors de l'envoi du couple au serveur ") ; return ; } } } /* Fonction permettant de récupérer sa propre adresse MAC (interface 'eth') dans le tableau '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) ; // RàZ sockfd = socket(AF_INET, SOCK_DGRAM, 0) ; if(sockfd < 0) perror("Impossible d'ouvrir la socket pour récupérer l'adresse MAC ") ; 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) ; } /* Affiche le mode d'emploi du programme */ void print_usage(char *prog) { printf("Usage :\n\ \t%s [-f config_file] [-d ip_agrégation] [-l listening_port] [-p port_agrégation] -r rtap_iface [-w wifi_iface] [-k]\n\ - L'option -f permet de spécifier un fichier de configuration alternatif (le fichier de configuration par défaut ne sera pas lu). Disponible uniquement si le programme a été compilé avec le support de libconfuse.\n\ - ip_agrégation est l'adresse IP du serveur d'agrégation (par défaut : 127.0.0.1), et port_agrégation le port sur lequel il écoute (par défaut : %d).\n\ - rtap_iface est l'interface de capture radiotap.\n\ - wifi_iface est l'interface physique correspondant à rtap_iface (par défaut : rtap_iface).\n\ - L'option -k active le maintien actif du mode monitor. À utiliser avec des pilotes bogués. Disponible uniquement si le programme a été compilé avec le support des threads POSIX.\n\ Note : tous les paramètres sont facultatifs si les options correspondantes sont renseignées dans un fichier de configuration. L'option -f permet de spécifier un fichier de configuration alternatif (par défaut : %s).\n\ ", prog, AGGREGATE_DEFAULT_PORT, DEFAULT_CONFIG_FILE) ; }