source: tools/traceanon/traceanon.cc @ 8e11beb

cachetimestampsdevelopdpdk-ndagetsilivendag_formatrc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformance
Last change on this file since 8e11beb was 8e11beb, checked in by Shane Alcock <salcock@…>, 3 years ago

Update tools to properly ignore meta records and missing timestamps

pcapng introduces a lot of meta records that we should preserve as
long as possible. Since we aren't automatically discarding them,
we need to make sure that our tools do not try to treat them as
"real" packets, i.e. try to get a timestamp or capture length from
them.

Similarly, simple pcapng packet records do not have a timestamp
so we need to make sure the tools do the right thing when
trace_get_seconds() returns a timestamp of zero on a packet. For
starters, we don't want to set our "first" packet time to zero
in that case!

  • Property mode set to 100644
File size: 15.1 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 = false;
49bool enc_dest   = 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 *trace = 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(trace);
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 || enc_dest)) {
185                encrypt_ips(anon, ipptr,enc_source,enc_dest);
186                ipptr->ip_sum = 0;
187        } else if (ip6 && (enc_source || enc_dest)) {
188                encrypt_ipv6(anon, ip6, enc_source, enc_dest);
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 || enc_dest)) {
200                udp->check = 0;
201        }
202
203        tcp = trace_get_tcp(packet);
204        if (tcp && (enc_source || enc_dest)) {
205                tcp->check = 0;
206        }
207
208        icmp6 = trace_get_icmp6(packet);
209        if (icmp6 && (enc_source || enc_dest)) {
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=true; break;
360                        case 'd': enc_dest  =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        trace = trace_create(argv[optind]);
445        if (trace_is_err(trace)) {
446                trace_perror(trace,"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(trace, &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(trace, maxthreads);
478
479        if (filterstring) {
480                filter = trace_create_filter(filterstring);
481        }
482
483        if (filter && trace_config(trace, TRACE_OPTION_FILTER, filter) == -1)
484        {
485                trace_perror(trace, "Configuring input filter");
486                exitcode = 1;
487                goto exitanon;
488        }
489
490        if (trace_pstart(trace, output, pktcbs, repcbs)==-1) {
491                trace_perror(trace,"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(trace);
505
506exitanon:
507        if (pktcbs)
508                trace_destroy_callback_set(pktcbs);
509        if (repcbs)
510                trace_destroy_callback_set(repcbs);
511        if (trace)
512                trace_destroy(trace);
513        return exitcode;
514}
Note: See TracBrowser for help on using the repository browser.