source: tools/traceanon/traceanon.cc @ bab946c

develop
Last change on this file since bab946c was bab946c, checked in by Shane Alcock <salcock@…>, 22 months ago

Fix clashes between global and local variable names in tools.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1/*
2 *
3 * Copyright (c) 2007-2016 The University of Waikato, Hamilton, New Zealand.
4 * All rights reserved.
5 *
6 * This file is part of libtrace.
7 *
8 * This code has been developed by the University of Waikato WAND
9 * research group. For further information please see http://www.wand.net.nz/
10 *
11 * libtrace is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * libtrace is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 *
24 *
25 */
26
27
28#include "config.h"
29#include "Anon.h"
30#include "libtrace_parallel.h"
31#include <stdio.h>
32#include <unistd.h>
33#include <stdlib.h>
34#include <getopt.h>
35#include <stdbool.h>
36#include <stddef.h>
37#include <string.h>
38#include <time.h>
39#include <assert.h>
40#include <signal.h>
41
42enum enc_type_t {
43        ENC_NONE,
44        ENC_CRYPTOPAN,
45        ENC_PREFIX_SUBSTITUTION
46};
47
48bool enc_source_opt = false;
49bool enc_dest_opt   = false;
50enum enc_type_t enc_type = ENC_NONE;
51char *key = NULL;
52
53int level = -1;
54trace_option_compresstype_t compress_type = TRACE_OPTION_COMPRESSTYPE_NONE;
55
56struct libtrace_t *inptrace = NULL;
57
58static void cleanup_signal(int signal)
59{
60        (void)signal;
61        // trace_pstop isn't really signal safe because its got lots of locks in it
62        trace_pstop(inptrace);
63}
64
65static void usage(char *argv0)
66{
67        fprintf(stderr,"Usage:\n"
68        "%s flags inputfile outputfile\n"
69        "-s --encrypt-source    Encrypt the source addresses\n"
70        "-d --encrypt-dest      Encrypt the destination addresses\n"
71        "-c --cryptopan=key     Encrypt the addresses with the cryptopan\n"
72        "                       prefix preserving\n"
73        "-F --keyfile=file      A file containing the cryptopan key\n"
74        "-p --prefix=C.I.D.R/bits Substitute the prefix of the address\n"
75        "-h --help              Print this usage information\n"
76        "-z --compress-level    Compress the output trace at the specified level\n"
77        "-Z --compress-type     Compress the output trace using the specified"
78        "                       compression algorithm\n"
79        "-t --threads=max       Use this number of threads for packet processing\n"
80        "-f --filter=expr       Discard all packets that do not match the\n"
81        "                       provided BPF expression\n"
82        ,argv0);
83        exit(1);
84}
85
86/* Incrementally update a checksum */
87static void update_in_cksum(uint16_t *csum, uint16_t old, uint16_t newval)
88{
89        uint32_t sum = (~htons(*csum) & 0xFFFF)
90                     + (~htons(old) & 0xFFFF)
91                     + htons(newval);
92        sum = (sum & 0xFFFF) + (sum >> 16);
93        *csum = htons(~(sum + (sum >> 16)));
94}
95
96UNUSED static void update_in_cksum32(uint16_t *csum, uint32_t old,
97                uint32_t newval)
98{
99        update_in_cksum(csum,(uint16_t)(old>>16),(uint16_t)(newval>>16));
100        update_in_cksum(csum,(uint16_t)(old&0xFFFF),(uint16_t)(newval&0xFFFF));
101}
102
103/* Ok this is remarkably complicated
104 *
105 * We want to change one, or the other IP address, while preserving
106 * the checksum.  TCP and UDP both include the faux header in their
107 * checksum calculations, so you have to update them too.  ICMP is
108 * even worse -- it can include the original IP packet that caused the
109 * error!  So anonymise that too, but remember that it's travelling in
110 * the opposite direction so we need to encrypt the destination and
111 * source instead of the source and destination!
112 */
113static void encrypt_ips(Anonymiser *anon, struct libtrace_ip *ip,
114                bool enc_source,bool enc_dest)
115{
116        libtrace_icmp_t *icmp=trace_get_icmp_from_ip(ip,NULL);
117
118        if (enc_source) {
119                uint32_t new_ip=htonl(anon->anonIPv4(ntohl(ip->ip_src.s_addr)));
120                ip->ip_src.s_addr = new_ip;
121        }
122
123        if (enc_dest) {
124                uint32_t new_ip=htonl(anon->anonIPv4(ntohl(ip->ip_dst.s_addr)));
125                ip->ip_dst.s_addr = new_ip;
126        }
127
128        if (icmp) {
129                /* These are error codes that return the IP packet
130                 * internally
131                 */
132               
133                if (icmp->type == 3 
134                                || icmp->type == 5 
135                                || icmp->type == 11) {
136                        char *ptr = (char *)icmp;
137                        encrypt_ips(anon,
138                                (struct libtrace_ip*)(ptr+
139                                        sizeof(struct libtrace_icmp)),
140                                enc_dest,
141                                enc_source);
142                }
143
144                if (enc_source || enc_dest)
145                        icmp->checksum = 0;
146        }
147}
148
149static void encrypt_ipv6(Anonymiser *anon, libtrace_ip6_t *ip6,
150                bool enc_source, bool enc_dest) {
151
152        uint8_t previp[16];
153
154        if (enc_source) {
155                memcpy(previp, &(ip6->ip_src.s6_addr), 16);
156                anon->anonIPv6(previp, (uint8_t *)&(ip6->ip_src.s6_addr));
157        }
158
159        if (enc_dest) {
160                memcpy(previp, &(ip6->ip_dst.s6_addr), 16);
161                anon->anonIPv6(previp, (uint8_t *)&(ip6->ip_dst.s6_addr));
162        }
163
164}
165
166
167static libtrace_packet_t *per_packet(libtrace_t *trace, libtrace_thread_t *t,
168        void *global, void *tls, libtrace_packet_t *packet) {
169
170        struct libtrace_ip *ipptr;
171        libtrace_ip6_t *ip6;
172        libtrace_udp_t *udp = NULL;
173        libtrace_tcp_t *tcp = NULL;
174        libtrace_icmp6_t *icmp6 = NULL;
175        Anonymiser *anon = (Anonymiser *)tls;
176        libtrace_generic_t result;
177
178        if (IS_LIBTRACE_META_PACKET(packet))
179                return packet;
180
181        ipptr = trace_get_ip(packet);
182        ip6 = trace_get_ip6(packet);
183
184        if (ipptr && (enc_source_opt || enc_dest_opt)) {
185                encrypt_ips(anon, ipptr,enc_source_opt,enc_dest_opt);
186                ipptr->ip_sum = 0;
187        } else if (ip6 && (enc_source_opt || enc_dest_opt)) {
188                encrypt_ipv6(anon, ip6, enc_source_opt, enc_dest_opt);
189        }
190
191
192        /* Replace checksums so that IP encryption cannot be
193         * reversed -- TODO allow checksums to be updated and remain valid
194         * for the new addresses */
195
196        /* XXX replace with nice use of trace_get_transport() */
197
198        udp = trace_get_udp(packet);
199        if (udp && (enc_source_opt || enc_dest_opt)) {
200                udp->check = 0;
201        }
202
203        tcp = trace_get_tcp(packet);
204        if (tcp && (enc_source_opt || enc_dest_opt)) {
205                tcp->check = 0;
206        }
207
208        icmp6 = trace_get_icmp6(packet);
209        if (icmp6 && (enc_source_opt || enc_dest_opt)) {
210                icmp6->checksum = 0;
211        }
212
213        /* TODO: Encrypt IP's in ARP packets */
214        result.pkt = packet;
215        trace_publish_result(trace, t, trace_packet_get_order(packet), result, RESULT_PACKET);
216
217        return NULL;
218}
219
220static void *start_anon(libtrace_t *trace, libtrace_thread_t *t, void *global)
221{
222        if (enc_type == ENC_PREFIX_SUBSTITUTION) {
223                PrefixSub *sub = new PrefixSub(key, NULL);
224                return sub;
225        }
226
227        if (enc_type == ENC_CRYPTOPAN) {
228                if (strlen(key) < 32) {
229                        fprintf(stderr, "ERROR: Key must be at least 32 "
230                        "characters long for CryptoPan anonymisation.\n");
231                        exit(1);
232                }
233#ifdef HAVE_LIBCRYPTO               
234                CryptoAnon *anon = new CryptoAnon((uint8_t *)key,
235                        (uint8_t)strlen(key), 20);
236                return anon;
237#else
238                /* TODO nicer way of exiting? */
239                fprintf(stderr, "Error: requested CryptoPan anonymisation but "
240                        "libtrace was built without libcrypto support!\n");
241                exit(1);
242#endif
243        }
244
245        return NULL;
246}
247
248static void end_anon(libtrace_t *trace, libtrace_thread_t *t, void *global,
249                void *tls) {
250        Anonymiser *anon = (Anonymiser *)tls;
251        delete(anon);
252
253}
254
255static void *init_output(libtrace_t *trace, libtrace_thread_t *t, void *global)
256{
257        libtrace_out_t *writer = NULL;
258        char *outputname = (char *)global;
259       
260        writer = trace_create_output(outputname);
261
262        if (trace_is_err_output(writer)) {
263                trace_perror_output(writer,"trace_create_output");
264                trace_destroy_output(writer);
265                return NULL;
266        }
267       
268        /* Hopefully this will deal nicely with people who want to crank the
269         * compression level up to 11 :) */
270        if (level > 9) {
271                fprintf(stderr, "WARNING: Compression level > 9 specified, setting to 9 instead\n");
272                level = 9;
273        }
274
275        if (level >= 0 && trace_config_output(writer, 
276                        TRACE_OPTION_OUTPUT_COMPRESS, &level) == -1) {
277                trace_perror_output(writer, "Configuring compression level");
278                trace_destroy_output(writer);
279                return NULL;
280        }
281
282        if (trace_config_output(writer, TRACE_OPTION_OUTPUT_COMPRESSTYPE,
283                                &compress_type) == -1) {
284                trace_perror_output(writer, "Configuring compression type");
285                trace_destroy_output(writer);
286                return NULL;
287        }
288
289        if (trace_start_output(writer)==-1) {
290                trace_perror_output(writer,"trace_start_output");
291                trace_destroy_output(writer);
292                return NULL;
293        }
294
295        return writer;
296
297}
298
299static void write_packet(libtrace_t *trace, libtrace_thread_t *sender,
300                      void *global, void *tls, libtrace_result_t *result) {
301        libtrace_packet_t *packet = (libtrace_packet_t*) result->value.pkt;
302        libtrace_out_t *writer = (libtrace_out_t *)tls;
303
304        if (writer != NULL && trace_write_packet(writer,packet)==-1) {
305                trace_perror_output(writer,"writer");
306                trace_interrupt();
307        }
308        trace_free_packet(trace, packet);
309}
310
311static void end_output(libtrace_t *trace, libtrace_thread_t *t, void *global,
312                void *tls) {
313        libtrace_out_t *writer = (libtrace_out_t *)tls;
314
315        trace_destroy_output(writer);
316}
317
318int main(int argc, char *argv[]) 
319{
320        //struct libtrace_t *trace = 0;
321        struct sigaction sigact;
322        char *output = 0;
323        char *compress_type_str=NULL;
324        int maxthreads = 4;
325        libtrace_callback_set_t *pktcbs = NULL;
326        libtrace_callback_set_t *repcbs = NULL;
327        int exitcode = 0;
328        char *filterstring = NULL;
329        libtrace_filter_t *filter = NULL;
330
331        if (argc<2)
332                usage(argv[0]);
333
334        while (1) {
335                int option_index;
336                struct option long_options[] = {
337                        { "encrypt-source",     0, 0, 's' },
338                        { "encrypt-dest",       0, 0, 'd' },
339                        { "cryptopan",          1, 0, 'c' },
340                        { "cryptopan-file",     1, 0, 'F' },
341                        { "prefix",             1, 0, 'p' },
342                        { "threads",            1, 0, 't' },
343                        { "filter",             1, 0, 'f' },
344                        { "compress-level",     1, 0, 'z' },
345                        { "compress-type",      1, 0, 'Z' },
346                        { "help",               0, 0, 'h' },
347                        { NULL,                 0, 0, 0   },
348                };
349
350                int c=getopt_long(argc, argv, "Z:z:sc:f:dp:ht:f:",
351                                long_options, &option_index);
352
353                if (c==-1)
354                        break;
355
356                switch (c) {
357                        case 'Z': compress_type_str=optarg; break;         
358                        case 'z': level = atoi(optarg); break;
359                        case 's': enc_source_opt=true; break;
360                        case 'd': enc_dest_opt  =true; break;
361                        case 'c': 
362                                  if (key!=NULL) {
363                                          fprintf(stderr,"You can only have one encryption type and one key\n");
364                                          usage(argv[0]);
365                                  }
366                                  key=strdup(optarg);
367                                  enc_type = ENC_CRYPTOPAN;
368                                  break;
369                        case 'F': {
370                                  if(key != NULL) {
371                                    fprintf(stderr,"You can only have one encryption type and one key\n");
372                                    usage(argv[0]);
373                                  }
374                                  FILE * infile = fopen(optarg,"rb");
375                                  if(infile == NULL) {
376                                    perror("Failed to open cryptopan keyfile");
377                                    return 1;
378                                  }
379                                  key = (char *) malloc(sizeof(char *) * 32);
380                                  if(fread(key,1,32,infile) != 32) {
381                                    if(ferror(infile)) {
382                                      perror("Failed while reading cryptopan keyfile");
383                                    }
384                                  }
385                                  fclose(infile);
386                                  enc_type = ENC_CRYPTOPAN;
387                                  break;
388                        }
389                        case 'f':
390                                  filterstring = optarg;
391                                  break;
392                        case 'p':
393                                  if (key!=NULL) {
394                                          fprintf(stderr,"You can only have one encryption type and one key\n");
395                                          usage(argv[0]);
396                                  }
397                                  key=strdup(optarg);
398                                  enc_type = ENC_PREFIX_SUBSTITUTION;
399                                  break;
400                        case 'h': 
401                                  usage(argv[0]);
402                        case 't':
403                                  maxthreads=atoi(optarg);
404                                  if (maxthreads <= 0)
405                                          maxthreads = 1;
406                                  break;
407                        default:
408                                fprintf(stderr,"unknown option: %c\n",c);
409                                usage(argv[0]);
410
411                }
412
413        }
414
415        if (compress_type_str == NULL && level >= 0) {
416                fprintf(stderr, "Compression level set, but no compression type was defined, setting to gzip\n");
417                compress_type = TRACE_OPTION_COMPRESSTYPE_ZLIB;
418        }
419
420        else if (compress_type_str == NULL) {
421                /* If a level or type is not specified, use the "none"
422                 * compression module */
423                compress_type = TRACE_OPTION_COMPRESSTYPE_NONE;
424        }
425
426        /* I decided to be fairly generous in what I accept for the
427         * compression type string */
428        else if (strncmp(compress_type_str, "gz", 2) == 0 ||
429                        strncmp(compress_type_str, "zlib", 4) == 0) {
430                compress_type = TRACE_OPTION_COMPRESSTYPE_ZLIB;
431        } else if (strncmp(compress_type_str, "bz", 2) == 0) {
432                compress_type = TRACE_OPTION_COMPRESSTYPE_BZ2;
433        } else if (strncmp(compress_type_str, "lzo", 3) == 0) {
434                compress_type = TRACE_OPTION_COMPRESSTYPE_LZO;
435        } else if (strncmp(compress_type_str, "no", 2) == 0) {
436                compress_type = TRACE_OPTION_COMPRESSTYPE_NONE;
437        } else {
438                fprintf(stderr, "Unknown compression type: %s\n",
439                        compress_type_str);
440                return 1;
441        }
442
443        /* open input uri */
444        inptrace = trace_create(argv[optind]);
445        if (trace_is_err(inptrace)) {
446                trace_perror(inptrace,"trace_create");
447                exitcode = 1;
448                goto exitanon;
449        }
450
451        if (optind +1>= argc) {
452                /* no output specified, output in same format to
453                 * stdout
454                 */
455                output = strdup("erf:-");
456        } else {
457                output = argv[optind +1];
458        }
459        // OK parallel changes start here
460
461        /* Set a special mode flag that means the output is timestamped
462         * and ordered before its read into reduce. Seems like a good
463         * special case to have.
464         */
465        trace_set_combiner(inptrace, &combiner_ordered, (libtrace_generic_t){0});
466
467        pktcbs = trace_create_callback_set();
468        trace_set_packet_cb(pktcbs, per_packet);
469        trace_set_stopping_cb(pktcbs, end_anon);
470        trace_set_starting_cb(pktcbs, start_anon);
471
472        repcbs = trace_create_callback_set();
473        trace_set_result_cb(repcbs, write_packet);
474        trace_set_stopping_cb(repcbs, end_output);
475        trace_set_starting_cb(repcbs, init_output);
476
477        trace_set_perpkt_threads(inptrace, maxthreads);
478
479        if (filterstring) {
480                filter = trace_create_filter(filterstring);
481        }
482
483        if (filter && trace_config(inptrace, TRACE_OPTION_FILTER, filter) == -1)
484        {
485                trace_perror(inptrace, "Configuring input filter");
486                exitcode = 1;
487                goto exitanon;
488        }
489
490        if (trace_pstart(inptrace, output, pktcbs, repcbs)==-1) {
491                trace_perror(inptrace,"trace_start");
492                exitcode = 1;
493                goto exitanon;
494        }
495
496        sigact.sa_handler = cleanup_signal;
497        sigemptyset(&sigact.sa_mask);
498        sigact.sa_flags = SA_RESTART;
499
500        sigaction(SIGINT, &sigact, NULL);
501        sigaction(SIGTERM, &sigact, NULL);
502
503        // Wait for the trace to finish
504        trace_join(inptrace);
505
506exitanon:
507        if (pktcbs)
508                trace_destroy_callback_set(pktcbs);
509        if (repcbs)
510                trace_destroy_callback_set(repcbs);
511        if (inptrace)
512                trace_destroy(inptrace);
513        return exitcode;
514}
Note: See TracBrowser for help on using the repository browser.