source: tools/traceanon/traceanon.cc @ ebb54a5

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

Improved parallel traceanon

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