/* Basics */ #include #include #include /* Args parsing and basename() for usage */ #include #include /* UDP stuff */ #include #include #include #include #include /* Packet capture stuff */ #include #include #define DEFAULT_FILTER "not ( icmp[icmptype]=icmp-unreach or (udp and dst %s and port %s) )" #define DEFAULT_HOST "127.0.0.1" #define DEFAULT_PORT "37008" #define DEFAULT_SNAPLEN "70" /* 70 bytes captured packets will to sent in ~128 bytes frames when encapsulated */ #define UDP_SEND_BUFLEN 1500 /* MAX_TZSP_PAYLOAD is for not trying to send TZSP packets greater than the interface MTU MAX_TZSP_PAYLOAD = MTU - IP header - UDP header - TZSP header I am assuming the following : - Layer 2 overhead is the same for the captured packet and the transmitted packet - MTU is assumed to be 1500 for now */ #define MAX_TZSP_PAYLOAD (1500-20-8-16) #define MIN_BYTES_TO_CAPTURE 16 #define MAX_BYTES_TO_CAPTURE (1500-20-8-16) /* TODO List * Resolution DNS host -> IP (c'est l'ip qu'il faut dans le filtre et pas le host !!) * Stats nombre de packets loupés ? * Implémenter une bwlimit en sortie (ptks/s et/ou bytes/s) * Pkt timestamps sur 64bits (quel field prendre ?) * Comparer les headers envoyés par un mikrotik avec /tool sniffer * free() de tous les malloc(), calloc() et strdup() * TZSP over IPv6 * Graceful stop avec signal() * getopts -v et --help * Licence GPL * 64bits problem : pcap2tzsp.c:264:3: attention : format ‘%llu’ expects argument of type ‘long long unsigned int’, but argument 2 has type ‘uint64_t’ [-Wformat] */ /* Functions declaration */ void capture_loop(char pcap_filter[]); void process_packet(u_char *void_args, const struct pcap_pkthdr* pkthdr, const u_char * packet); /* Custom types definition */ typedef struct _process_packet_args { uint64_t pkt_count; int udp_socket; struct sockaddr_in udp_sockaddr; } process_packet_args_t; /* Constants */ const int sockaddr_len=sizeof(struct sockaddr_in); /* Flags from commandline parsing */ static int opt_verbose; /* Option arguments from commandline parsing */ static char *opt_iface=NULL; static char *opt_host=NULL; static char *opt_port=NULL; static char *opt_snaplen=NULL; void usage(char progname[]) { printf("Usage : %s [--verbose] [--brief] [-i ] [-h ] [-p ] [custom_pcap_filter]\n", progname); printf("\t : Interface name to capture from (Default : first available interface)\n"); printf("\t : Host (or IPv4 address) for sending captured packet headers (Default : '%s')\n", DEFAULT_HOST); printf("\t : Port for sending captured packet headers (Default '%s')\n", DEFAULT_PORT); printf("\t : libpcap capture filter (Default '%s')\n", DEFAULT_FILTER); exit(1); } int main(int argc, char *argv[]) { /* Command line args parsing */ int c,i,j,pcap_filter_len; char *pcap_filter=NULL; while (1) { static struct option long_options[] = { /* These options set a flag. */ {"verbose", no_argument, &opt_verbose, 1}, {"brief", no_argument, &opt_verbose, 0}, /* These options don't set a flag. We distinguish them by their indices. */ {"interface", required_argument, 0, 'i'}, {"host", required_argument, 0, 'h'}, {"port", required_argument, 0, 'p'}, {"snaplen", required_argument, 0, 's'}, {0, 0, 0, 0} }; /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long(argc, argv, "i:h:p:s:", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 0: /* If this option set a flag, do nothing else now. */ if (long_options[option_index].flag != 0) break; //printf ("option %s", long_options[option_index].name); //if (optarg) printf (" with arg %s", optarg); //printf ("\n"); break; case 'i': opt_iface= strdup(optarg); break; case 'h': opt_host= strdup(optarg); break; case 'p': opt_port= strdup(optarg); break; case 's': opt_snaplen= strdup(optarg); break; case '?': /* getopt_long already printed an error message. */ usage(basename(argv[0])); break; default: abort(); } } // Assign default value if no user supplied value if (opt_host==NULL) opt_host=strdup(DEFAULT_HOST); if (opt_port==NULL) opt_port=strdup(DEFAULT_PORT); if (opt_snaplen==NULL) opt_snaplen=strdup(DEFAULT_SNAPLEN); // No default for opt_iface because will be choosen at runtime //if (verbose_flag) puts ("verbose flag is set"); /* Construct the pcap_filter */ pcap_filter_len=0; if (optind < argc) { // Any remaining command line arguments is for pcap_filter for (i=optind; i MAX_BYTES_TO_CAPTURE ) { fprintf(stderr, "snaplen of %i is not in the allowed range of [%i..%i]\n", snaplen, MIN_BYTES_TO_CAPTURE, MAX_BYTES_TO_CAPTURE); exit(12); } //Already done : memset((char *) &(process_packet_args.udp_sockaddr), 0, sockaddr_len); process_packet_args.udp_sockaddr.sin_family = AF_INET; process_packet_args.udp_sockaddr.sin_port = htons(atoi(opt_port)); if (inet_aton(opt_host, &(process_packet_args.udp_sockaddr.sin_addr))==0) { fprintf(stderr, "inet_aton : Impossible de parser l'IP %s\n",opt_host); exit(11); } /* Initialisation pcap */ if (opt_verbose) printf("Opening device %s for capturing\n", pcap_device); /* Open device in promiscuous mode */ if ( (pcap_handle = pcap_open_live(pcap_device, snaplen, 1, 512, pcap_errbuf)) == NULL){ fprintf(stderr, "ERROR: %s\n", pcap_errbuf); exit(21); } if (opt_verbose) printf("Compiling the following capture filter : '%s'\n", pcap_filter); if ( pcap_compile(pcap_handle,&pcap_filter_prog,pcap_filter,1,PCAP_NETMASK_UNKNOWN) == -1 ) { fprintf(stderr,"ERROR : %s\n", pcap_geterr(pcap_handle) ); exit(22); } if ( pcap_setfilter(pcap_handle,&pcap_filter_prog) == -1 ) { fprintf(stderr,"ERROR : %s\n", pcap_geterr(pcap_handle) ); exit(23); } /* Initialisation socket UDP */ process_packet_args.udp_socket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (process_packet_args.udp_socket==-1) { perror("socket"); exit(10); } /* Loop forever & call process_packet() for every received packet*/ if ( pcap_loop(pcap_handle, -1, process_packet, (u_char *)&process_packet_args) == -1){ fprintf(stderr, "ERROR: %s\n", pcap_geterr(pcap_handle) ); exit(25); } close(process_packet_args.udp_socket); } /* process_packet(): Callback function called by pcap_loop() everytime a packet */ /* arrives to the network card. This function prints the captured raw data in */ /* hexadecimal. */ void process_packet(u_char *void_args, const struct pcap_pkthdr* pkthdr, const u_char * packet) { static time_t old_tv_sec=0; static uint64_t old_pkt_count=0; int res; double throughput; uint32_t ts; /* In network byte order */ uint16_t len; /* In network byte order */ char buf[UDP_SEND_BUFLEN]; process_packet_args_t *args=(process_packet_args_t *)void_args; /* Packet counting and displaying (max once by second) */ args->pkt_count++; if (old_tv_sec != pkthdr->ts.tv_sec) { //printf("DEBUG : throughput=((double) %llu - %llu ) / ( %u - %u )\n", args->pkt_count, old_pkt_count, pkthdr->ts.tv_sec, old_tv_sec); throughput=((double) args->pkt_count - old_pkt_count ) / (pkthdr->ts.tv_sec - old_tv_sec); printf("\rPacket Count: %20llu (%8.1f pkt/s)", args->pkt_count, throughput); fflush(stdout); old_tv_sec=pkthdr->ts.tv_sec; old_pkt_count= args->pkt_count; } /* Variable fields for TZSP packet */ ts=htonl((uint32_t) pkthdr->ts.tv_sec); /* TODO : this cast is bullshit ? */ len=htons((uint16_t) pkthdr->len); /* TaZmen Sniffing Protocol (TZSP) packet forging */ //memset((char *) &buf, 0, UDP_SEND_BUFLEN); /* Buffer reset not useful for now */ buf[0]=0x01; /* Version */ buf[1]=0x01; /* Type == Packet for transmit */ buf[2]=0x00; /* Encapsuled protocol (2 bytes) 0x0001 == Ethernet */ buf[3]=0x01; buf[4]=0x0D; /* Tag type == TAG_TIMESTAMP */ buf[5]=0x04; /* Tag length == 4 bytes */ /* buf[6,7,8,9] Timestamp on 4 bytes (network order) */ memcpy(buf+6, &ts, 4); buf[10]=0x29; /* Tag type TAG_RX_FRAME_LENGTH */ buf[11]=0x02; /* Tag length : 2 bytes */ memcpy(buf+12,&len, 2); buf[14]=0x00; /* Tag type TAG PADDING (for alignement) */ buf[15]=0x01; /* Tag type TAG END */ /* Raw packet copy */ //TODO : assert that pkthdr->caplen < MAX_TZSP_PAYLOAD memcpy(buf+16,packet, pkthdr->caplen); /* TZSP packet sending (over the UDP socket) */ res=sendto(args->udp_socket, buf, 16+pkthdr->caplen, 0, (struct sockaddr *)&(args->udp_sockaddr), sockaddr_len); if (res==-1) { fprintf(stderr, "sendto() error\n"); } }