source: tools/traceanon/traceanon.cc @ aafdc55

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivelibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since aafdc55 was aafdc55, checked in by Shane Alcock <salcock@…>, 5 years ago

Add BPF filtering back into traceanon

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