source: tools/traceanon/traceanon.cc @ 2d8a045

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

Remove mention of ipenc.h from traceanon.cc

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