source: tools/traceanon/traceanon.cc @ c968cfc

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

Re-add missing enc_types that used to be in ipenc.h

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