├── .clang-format ├── .github ├── FUNDING.yml └── workflows │ ├── main.yml │ └── trigger-copr-build.yml ├── .gitignore ├── CHANGELOG ├── LICENSE ├── Makefile.am ├── Makefile.in ├── README.md ├── aclocal.m4 ├── autogen.sh ├── compile ├── configure ├── configure.ac ├── depcomp ├── icon ├── lpa.png └── lpa.webp ├── install-sh ├── m4 ├── ax_cxx_compile_stdcxx.m4 └── ax_cxx_compile_stdcxx_11.m4 ├── missing ├── renovate.json ├── snap └── snapcraft.yaml ├── spec └── large-pcap-analyzer.spec ├── src ├── config.h.in ├── config.h.in~ ├── filter.cpp ├── filter.h ├── hash_algo.cpp ├── hash_algo.h ├── ipaddress.h ├── large-pcap-analyzer.cpp ├── large-pcap-analyzer.h ├── packet.h ├── parse.cpp ├── parse.h ├── pcap_helpers.cpp ├── pcap_helpers.h ├── printf_helpers.cpp ├── printf_helpers.h ├── process_file.cpp ├── process_file.h ├── processor.h ├── timestamp_pkt_processor.cpp ├── timestamp_pkt_processor.h ├── trafficstats_pkt_processor.cpp └── trafficstats_pkt_processor.h ├── test-pcaps ├── invalid_timestamp1_negative_ts.pcap ├── invalid_timestamp2_zero_ts.pcap ├── ipv4_ftp.pcap ├── ipv4_gtpc_single_pkt.pcap ├── ipv4_gtpu_fragmented.pcap ├── ipv4_gtpu_https.pcap ├── ipv4_sctp_iua.pcap ├── ipv4_tcp_flags.pcap ├── run_tests.sh ├── timestamps-30pkts.txt ├── timestamps-40470pkts.txt ├── timestamps-invalid.txt ├── timing-test.pcap ├── traffic_ipv4_ftp.csv └── traffic_ipv4_gtpu_https.csv └── update_version.sh /.clang-format: -------------------------------------------------------------------------------- 1 | # large-pcap-analyzer style 2 | 3 | BasedOnStyle: WebKit 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: f18m 4 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | # add a cron job to run every month -- this project is not very active, at least ensure there's a valid CI build every month 9 | # this is also useful to check if something breaks e.g. due to infrastructure changes (e.g. Ubuntu OS) 10 | schedule: 11 | - cron: '0 0 1 * *' 12 | 13 | jobs: 14 | build_and_test: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | # install deps 19 | - uses: actions/checkout@v4 20 | - name: install debian-packaged dependencies 21 | run: sudo apt install -y automake libpcap-dev diffutils tcpdump tshark 22 | 23 | # build & test 24 | #- name: check C++ formatting -- TODO 25 | # run: make format_check 26 | - name: autogen 27 | run: ./autogen.sh 28 | - name: configure 29 | run: ./configure 30 | - name: build large-pcap-analyzer 31 | run: make 32 | - name: run unit tests 33 | run: make tests 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/trigger-copr-build.yml: -------------------------------------------------------------------------------- 1 | name: trigger COPR 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | # add a cron job to run every month -- this project is not very active, at least ensure there's a valid CI build every month 9 | # this is also useful to check if something breaks e.g. due to infrastructure changes (e.g. Ubuntu OS) 10 | schedule: 11 | - cron: '0 0 1 * *' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | copr_build_lpa: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | # install deps 20 | - name: install COPR CLI 21 | run: pip3 install copr-cli 22 | 23 | - name: Setup Copr config file 24 | env: 25 | # You need to have those secrets in your repo. 26 | # See also: https://copr.fedorainfracloud.org/api/. 27 | COPR_CONFIG: ${{ secrets.COPR_CONFIG }} 28 | run: | 29 | mkdir -p ~/.config 30 | echo "$COPR_CONFIG" > ~/.config/copr 31 | 32 | - name: Trigger COPR build [collector] 33 | run: | 34 | copr-cli buildscm \ 35 | --type git --clone-url https://github.com/f18m/large-pcap-analyzer.git \ 36 | --spec spec/large-pcap-analyzer.spec --method rpkg --commit ${{ github.head_ref || github.ref_name }} \ 37 | large-pcap-analyzer 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/*.o 2 | src/config.h 3 | src/stamp-h1 4 | .cproject 5 | .project 6 | autom4te.cache/ 7 | config.log 8 | config.status 9 | large_pcap_analyzer 10 | src/.deps/ 11 | src/.dirstamp 12 | Makefile 13 | *.snap 14 | *.tar.bz2 15 | latest 16 | parts/ 17 | prime/ 18 | snap/.snapcraft/ 19 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | ***************************************************************************************************** 2 | large-pcap-analyzer v3.8.0 3 | released on June 6, 2023 4 | 5 | * Implement new --report and --report-write CLI options to produce "traffic reports" (#18) 6 | * Build fails on Ubuntu 23.04 because pcap_compile_nopcap is deprecated (#17) 7 | 8 | 9 | ***************************************************************************************************** 10 | large-pcap-analyzer v3.7.1 11 | released on Dec 19, 2022 12 | 13 | * bugfix: declare zero as duration for PCAPs with just 1 packet 14 | 15 | Others: 16 | * added Github actions CI 17 | 18 | 19 | ***************************************************************************************************** 20 | large-pcap-analyzer v3.7.0 21 | released on Nov 17, 2019 22 | 23 | New features 24 | * Add --set-duration-preserve-ifg commandline option. 25 | * rename "--set-timestamps" to "--set-timestamps-from" 26 | 27 | ***************************************************************************************************** 28 | large-pcap-analyzer v3.6.0 29 | released on Feb 10, 2019 30 | 31 | New features 32 | * add --set-timestamps options 33 | 34 | Others 35 | * added TravisCI integration for continuous testing 36 | 37 | 38 | ***************************************************************************************************** 39 | large-pcap-analyzer v3.5.1 40 | released on Jun 13, 2018 41 | 42 | New features 43 | * add --quiet mode option to allow this utility to be used easily from external scripts 44 | * fix some casts that blocked i386/armhf building 45 | 46 | 47 | ***************************************************************************************************** 48 | large-pcap-analyzer v3.5.0 49 | released on Jun 10, 2018 50 | 51 | New features 52 | * add --set-duration option that allows altering PCAP timestamps 53 | * add support for long options also for other existing commands 54 | * add SNAP distribution/deployment option (with snapcraft integration) 55 | * add RPM distribution/deployment option (with copr integration) 56 | 57 | 58 | ***************************************************************************************************** 59 | large-pcap-analyzer v3.4.3 60 | released on Mar 14, 2018 61 | 62 | Fixes: 63 | * allow building with GCC 7.x 64 | * allow configure script to run on systems with automake <15 like Centos 7 65 | 66 | 67 | ***************************************************************************************************** 68 | large-pcap-analyzer v3.4.2 69 | released on Feb 28, 2017 70 | 71 | Major new features: 72 | * added -C option to extract a TCP/UDP/SCTP connection (possibly GTPu-encapsulated) by specifying 73 | in a compact form the 4tuple (IP:port IP:port) 74 | 75 | 76 | ***************************************************************************************************** 77 | large-pcap-analyzer v3.4.0 78 | released on Feb 15, 2017 79 | 80 | New features: 81 | * added 3 modes to the -T option: -T syn, -T full3way and -T full3way-data, see help for more info 82 | * added testing to avoid regression, see "make check" 83 | * added C++11 optional support for faster -T filtering 84 | 85 | Fixed some bugs with TCP parsing (when optional headers are present) and with GTPu padding 86 | 87 | 88 | ***************************************************************************************************** 89 | large-pcap-analyzer v3.2 up to 3.3.1 90 | released on Feb 7, 2017 91 | 92 | New features: 93 | * added -T filtering mode to extract "valid" TCP connections: this adds a stateful filter to this utility 94 | * reworked command-line arguments to match those of "tshark" command line utility; 95 | * added support for GTPu-filtering (-G option) 96 | * added several command-line options (-v, -a) 97 | 98 | Fixes: 99 | * fix GTPu parsing in compute_hash() for GTPu packets 100 | * fix compatibility issues with pcap.h inclusion 101 | * fix packaging issues 102 | * fix C99 build issue 103 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | 3 | # avoid warnings about this packet not being in GNU style: 4 | AUTOMAKE_OPTIONS = foreign subdir-objects 5 | ACLOCAL_AMFLAGS = -I m4 6 | 7 | # makes it easier to debug integration tests: 8 | BASH_DEBUG_OPT = -x 9 | 10 | 11 | bin_PROGRAMS = large_pcap_analyzer 12 | large_pcap_analyzer_SOURCES = \ 13 | src/filter.cpp \ 14 | src/hash_algo.cpp \ 15 | src/large-pcap-analyzer.cpp \ 16 | src/parse.cpp \ 17 | src/pcap_helpers.cpp \ 18 | src/printf_helpers.cpp \ 19 | src/process_file.cpp \ 20 | src/timestamp_pkt_processor.cpp \ 21 | src/trafficstats_pkt_processor.cpp 22 | large_pcap_analyzer_HDRS = \ 23 | src/config.h \ 24 | src/filter.h \ 25 | src/hash_algo.h \ 26 | src/large-pcap-analyzer.h \ 27 | src/packet.h \ 28 | src/parse.h \ 29 | src/pcap_helpers.h \ 30 | src/printf_helpers.h \ 31 | src/process_file.h \ 32 | src/processor.h \ 33 | src/timestamp_pkt_processor.h \ 34 | src/trafficstats_pkt_processor.h 35 | 36 | check: $(bin_PROGRAMS) 37 | cd test-pcaps && chmod a+x ./run_tests.sh && bash $(BASH_DEBUG_OPT) run_tests.sh 38 | 39 | # "test" or "tests" are just synonyms of "check" 40 | test: check 41 | tests: check 42 | 43 | distclean: 44 | rm -f Makefile config.log config.status src/.dirstamp src/config.h src/stamp-h1 45 | rm -rf autom4te.cache snap/.snapcraft src/.deps 46 | 47 | # Note that currently there is no enforcement (for build to be successful) of the code formatting but 48 | # the project adheres to the formatting defined in the .clang-format file 49 | reformat: 50 | for s in $(large_pcap_analyzer_SOURCES) $(large_pcap_analyzer_HDRS); do \ 51 | clang-format -i $$s ; \ 52 | done 53 | 54 | 55 | # this is the MANUAL way to upload a new SNAP but it will be uploaded only for amd64 architecture! 56 | release_new_version: 57 | snap remove large-pcap-analyzer # in case it was already installed 58 | snapcraft # this will rebuild the snap using snap/snapcraft.yaml 59 | snap install large-pcap-analyzer_*_amd64.snap --devmode # test new snap installation 60 | snap list # confirm new version is now installed 61 | snapcraft login # this is interactive and will ask email & password 62 | snapcraft push --release=stable large-pcap-analyzer_*_amd64.snap # release upstream 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://github.com/f18m/large-pcap-analyzer/actions/workflows/main.yml/badge.svg)](https://github.com/f18m/large-pcap-analyzer/actions) 2 | 3 | # Large PCAP file analyzer 4 | Large PCAP file analyzer is a command-line utility program that performs some simple operations 5 | on .PCAP files very quickly. This allows you to manipulate also very large PCAP files 6 | that cannot be easily handled with other software like Wireshark. 7 | 8 | Currently it builds and works on Linux but actually nothing prevents it from running on Windows. 9 | It is based over the well-known libpcap. 10 | 11 | Some features of this utility: 12 | 13 | 1. Extract packets matching a simple BPF filter (tcpdump syntax). 14 | 2. Extract packets matching plain text. 15 | 3. Computes the tcpreplay speed required to respect packet timestamps. 16 | 4. Understands GTPu tunnelling and allows filtering via BPF filters (tcpdump syntax) the encapsulated (inner) GTPu frames. 17 | 5. Changes PCAP duration, changing the timestamp inside each packet. 18 | 6. Provides "traffic reports" e.g. the connections that transport the most bytes or packets. 19 | 20 | 21 | # Table of Contents 22 | 23 | * [How to install](#how-to-install) 24 | * [Command line help](#command-line-help) 25 | * [Example run 1: time analysis](#example-run-1-time-analysis) 26 | * [Example run 2: raw search](#example-run-2-raw-search) 27 | * [Example run 3: tcpdump-like](#example-run-3-tcpdump-like) 28 | * [Example run 4: GTPu filtering](#example-run-4-gtpu-filtering) 29 | * [Example run 5: valid TCP stream filtering](#example-run-5-valid-tcp-stream-filtering) 30 | * [Example run 6: set PCAP duration resetting IFG](#example-run-6-set-pcap-duration-resetting-ifg) 31 | * [Example run 7: set PCAP duration preserving IFG](#example-run-7-set-pcap-duration-preserving-ifg) 32 | * [Example run 8: change PCAP timestamps](#example-run-8-change-pcap-timestamps) 33 | * [Example run 9: generate traffic reports](#example-run-9-generate-traffic-reports) 34 | 35 | 36 | 37 | # How to install 38 | 39 | You can use one of the following installation options: 40 | 41 | | Link to Install Info | Build Status | Applies to | 42 | |:--------------------:|:-------------:|:----------:| 43 | | [RPM repository](https://copr.fedorainfracloud.org/coprs/f18m/large-pcap-analyzer/) | ![Build status](https://copr.fedorainfracloud.org/coprs/f18m/large-pcap-analyzer/package/large-pcap-analyzer/status_image/last_build.png) | CentOS, RHEL, Fedora, RockyLinux, AlmaLinux, openSUSE Tumbleweed | 44 | | [Snap](https://snapcraft.io/large-pcap-analyzer) | [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/large-pcap-analyzer) | Arch Linux, Debian, Fedora, Gentoo, Linux Mint, openSUSE, Raspbian, Ubuntu, etc. If you have [snapd](https://docs.snapcraft.io/core/install) installed, just run ```snap install large-pcap-analyzer``` | 45 | 46 | For developers: link to [Snapcraft page for large PCAP analyzer](https://build.snapcraft.io/user/f18m/large-pcap-analyzer) 47 | 48 | As for most Linux software, you can also install the software by building it from sources: 49 | 50 | ``` 51 | $ wget https://github.com/f18m/large-pcap-analyzer/archive/3.8.2.tar.gz 52 | $ tar xvzf 3.8.2.tar.gz 53 | $ cd large-pcap-analyzer-3.8.2/ 54 | $ apt install -y automake libpcap-dev diffutils tcpdump tshark # or similar command to fetch dependencies 55 | $ ./configure && make 56 | $ sudo make install 57 | ``` 58 | 59 | 60 | # Command line help 61 | 62 | ``` 63 | large-pcap-analyzer version 3.8.2, built with libpcap libpcap version 1.9.1 (with TPACKET_V3) 64 | by Francesco Montorsi, (c) 2014-2023 65 | Usage: 66 | large-pcap-analyzer [options] somefile.pcap ... 67 | Miscellaneous options: 68 | -h,--help this help 69 | -v,--verbose be verbose 70 | -V,--version print version and exit 71 | -q,--quiet suppress all normal output, be script-friendly 72 | -w , --write 73 | where to save the PCAP containing the results of filtering/processing 74 | -a,--append open output file in APPEND mode instead of TRUNCATE 75 | Filtering options (i.e., options to select the packets to save in ): 76 | -Y , --display-filter 77 | the PCAP filter to apply on packets (will be applied on outer IP frames for GTPu pkts) 78 | -G , --inner-filter 79 | the PCAP filter to apply on inner/encapsulated GTPu frames (or outer IP frames for non-GTPu pkts) 80 | -C , --connection-filter 81 | 4-tuple identifying a connection to filter; syntax is 'IP1:port1 IP2:port2' 82 | -S , --string-filter 83 | a string filter that will be searched inside loaded packets 84 | -T , --tcp-filter 85 | filter for entire TCP connections having 86 | -T syn: at least 1 SYN packet 87 | -T full3way: the full 3way handshake 88 | -T full3way-data: the full 3way handshake and data packets 89 | Timestamp processing options (i.e., options that might change packets saved in ): 90 | -t,--timing provide timestamp analysis on loaded packets 91 | --set-duration 92 | alters packet timestamps so that the time difference between first and last packet 93 | matches the given amount of time. All packets in the middle will be equally spaced in time. 94 | --set-duration-preserve-ifg 95 | alters packet timestamps so that the time difference between first and last packet 96 | matches the given amount of time. Interframe gaps (IFG) are scaled accordingly. 97 | --set-timestamps-from 98 | alters all packet timestamps using the list of Unix timestamps contained in the given text file; 99 | the file format is: one line per packet, a single Unix timestamp in seconds (floating point supported) 100 | per line; the number of lines must match exactly the number of packets of the filtered input PCAP. 101 | Reporting options: 102 | -p,--stats provide basic parsing statistics on loaded packets 103 | --report 104 | provide a report on loaded packets; list of supported reports is: 105 | allflows_by_pkts: print in CSV format all the flows sorted by number of packets 106 | top10flows_by_pkts: print in CSV format the top 10 flows sorted by number of packets 107 | allflows_by_pkts_outer: same as but stop at GTPu outer tunnel, don't parse the tunneled packet 108 | top10flows_by_pkts_outer: same as but stop at GTPu outer tunnel, don't parse the tunneled packet 109 | --report-write 110 | save the report specified by --report in CSV format into 111 | Inputs: 112 | somefile.pcap the large PCAP trace to analyze; more than 1 file can be specified. 113 | 114 | Note that: 115 | -Y and -G options accept filters expressed in tcpdump/pcap_filters syntax. See http://www.manpagez.com/man/7/pcap-filter/ for more info. 116 | A 'flow' is defined as a unique tuple of (srcIP, srcPort, dstIP, dstPort) for UDP,TCP,SCTP protocols. 117 | Other PCAP utilities you may be looking for are: 118 | * mergecap: to merge PCAP files 119 | * tcpdump: can be used to split PCAP files (and more) 120 | * editcap: can be used to manipulate timestamps in PCAP files (and more) 121 | * tcprewrite: can be used to rewrite some packet fields in PCAP files (and more) 122 | ``` 123 | 124 | # Example run 1: time analysis 125 | 126 | In this example we are interested in understanding how many seconds of traffic are contained in a PCAP file: 127 | 128 | ``` 129 | $ large_pcap_analyzer -t large.pcap 130 | 131 | No PCAP filter set: all packets inside the PCAP will be loaded. 132 | 8M packets (8751268 packets) were loaded from PCAP. 133 | Tcpreplay should replay this PCAP at an average of 73.34Mbps / 14580.72pps to respect PCAP timings. 134 | ``` 135 | 136 | Note that to load a 5.6GB PCAP only 1.9secs were required (on a 3GHz Intel Xeon CPU). 137 | This translates to a processing throughput of about 3GB/sec (in this mode). 138 | RAM memory consumption was about 4MB. 139 | 140 | 141 | # Example run 2: raw search 142 | 143 | In this example we are interested in selecting any packet that may contain inside it the string "youtube": 144 | 145 | ``` 146 | $ large_pcap_analyzer -v -S "youtube" -w out.pcap bigcapture.pcap 147 | 148 | Analyzing PCAP file 'bigcapture.pcap'... 149 | The PCAP file has size 5.50GiB = 5636MiB. 150 | No PCAP filter set: all packets inside the PCAP will be loaded. 151 | Successfully opened output PCAP 'out.pcap' 152 | 1M packets loaded from PCAP... 153 | 2M packets loaded from PCAP... 154 | 3M packets loaded from PCAP... 155 | 4M packets loaded from PCAP... 156 | 5M packets loaded from PCAP... 157 | 6M packets loaded from PCAP... 158 | 7M packets loaded from PCAP... 159 | 8M packets loaded from PCAP... 160 | Processing took 5 seconds. 161 | 8M packets (8751268 packets) were loaded from PCAP. 162 | 0M packets (9825 packets) matched the filtering criteria (search string / PCAP filters / valid TCP streams filter) and were saved into output PCAP. 163 | ``` 164 | 165 | Note that to load, search and extract packets from a 5.6GB PCAP only 5secs were required (on a 3GHz Intel Xeon CPU). 166 | This translates to a processing throughput of about 1GB/sec (in this mode). 167 | RAM memory consumption was about 4MB. 168 | 169 | 170 | # Example run 3: tcpdump-like 171 | 172 | In this example we are interested in selecting packets having a VLAN tag and directed or coming from an HTTP server: 173 | 174 | ``` 175 | $ large_pcap_analyzer -v -Y 'vlan and tcp port 80' -w out.pcap bigcapture.pcap 176 | 177 | Successfully compiled PCAP filter: vlan and tcp port 80 178 | Analyzing PCAP file 'bigcapture.pcap'... 179 | The PCAP file has size 5.50GiB = 5636MiB. 180 | Successfully opened output PCAP 'out.pcap' 181 | 1M packets loaded from PCAP (matching PCAP filter)... 182 | 2M packets loaded from PCAP (matching PCAP filter)... 183 | 3M packets loaded from PCAP (matching PCAP filter)... 184 | 4M packets loaded from PCAP (matching PCAP filter)... 185 | 5M packets loaded from PCAP (matching PCAP filter)... 186 | 6M packets loaded from PCAP (matching PCAP filter)... 187 | 7M packets loaded from PCAP (matching PCAP filter)... 188 | 8M packets loaded from PCAP (matching PCAP filter)... 189 | Processing took 3 seconds. 190 | 8M packets (8751268 packets) were loaded from PCAP (matching PCAP filter). 191 | 0M packets (1147 packets) matched the filtering criteria (search string / PCAP filters / valid TCP streams filter) and were saved into output PCAP. 192 | ``` 193 | 194 | Note that to load, search and extract packets from a 2GB PCAP only 1sec was required (on a 3GHz Intel Xeon CPU). 195 | RAM memory consumption was about 4MB. 196 | 197 | 198 | # Example run 4: GTPu filtering 199 | 200 | In this example we are interested in selecting packets GTPu-encapsulated for a specific TCP flow between the 201 | IP address 1.1.1.1 <-> 1.1.1.2, on TCP ports 80 <-> 10000: 202 | 203 | 204 | ``` 205 | $ large_pcap_analyzer -v -G '(host 1.1.1.1 or host 1.1.1.2) and (port 80 or port 10000)' -w out.pcap bigcapture.pcap 206 | 207 | Successfully compiled GTPu PCAP filter: (host 1.1.1.1 or host 1.1.1.2) and (port 80 or port 10000) 208 | Analyzing PCAP file 'bigcapture.pcap'... 209 | The PCAP file has size 5.50GiB = 5636MiB. 210 | Successfully opened output PCAP 'out.pcap' 211 | 1M packets loaded from PCAP... 212 | 2M packets loaded from PCAP... 213 | 3M packets loaded from PCAP... 214 | 4M packets loaded from PCAP... 215 | 5M packets loaded from PCAP... 216 | 6M packets loaded from PCAP... 217 | 7M packets loaded from PCAP... 218 | 8M packets loaded from PCAP... 219 | Processing took 3 seconds. 220 | 8M packets (8751268 packets) were loaded from PCAP. 221 | 8M packets (8501213 packets) loaded from PCAP are GTPu packets (97.1%). 222 | 0M packets (0 packets) matched the filtering criteria (search string / PCAP filters / valid TCP streams filter) and were saved into output PCAP. 223 | ``` 224 | 225 | 226 | # Example run 5: valid TCP stream filtering 227 | 228 | In this example we are interested in selecting packets of TCP connections that have at least 1 SYN and 1 SYN-ACK packet 229 | (if GTPu packets are found this analysis is done for the encapsulated TCP connections): 230 | 231 | ``` 232 | $ large_pcap_analyzer -v -T -w out.pcap bigcapture.pcap 233 | 234 | Analyzing PCAP file 'bigcapture.pcap'... 235 | The PCAP file has size 5.50GiB = 5636MiB. 236 | Successfully opened output PCAP 'out.pcap' 237 | Valid TCP filtering enabled: performing first pass 238 | 1M packets loaded from PCAP... 239 | 2M packets loaded from PCAP... 240 | 3M packets loaded from PCAP... 241 | 4M packets loaded from PCAP... 242 | 5M packets loaded from PCAP... 243 | 6M packets loaded from PCAP... 244 | 7M packets loaded from PCAP... 245 | 8M packets loaded from PCAP... 246 | Processing took 2 seconds. 247 | Detected 1 invalid packets, 721214 non-TCP packets and 37436 valid TCP flows (on a total of 85878 flows). 248 | Valid TCP filtering enabled: performing second pass 249 | Analyzing PCAP file 'bigcapture.pcap'... 250 | The PCAP file has size 5.50GiB = 5636MiB. 251 | 1M packets loaded from PCAP... 252 | 2M packets loaded from PCAP... 253 | 3M packets loaded from PCAP... 254 | 4M packets loaded from PCAP... 255 | 5M packets loaded from PCAP... 256 | 6M packets loaded from PCAP... 257 | 7M packets loaded from PCAP... 258 | 8M packets loaded from PCAP... 259 | Processing took 2 seconds. 260 | 8M packets (8751268 packets) were loaded from PCAP. 261 | 0M packets (4498 packets) matched the filtering criteria (search string / PCAP filters / valid TCP streams filter) and were saved into output PCAP. 262 | ``` 263 | 264 | Note that to load, search and extract packets from a 5.6GB PCAP only 4.5secs were required (on a 3GHz Intel Xeon CPU). 265 | This translates to a processing throughput of about 1GB/sec (in this mode). 266 | 267 | 268 | # Example run 6: set PCAP duration resetting IFG 269 | 270 | In this example a PCAP that would take 8 minutes to be replayed (without top speed option) will be 271 | modified to take just 1.2 seconds to replay. 272 | To better explain the result of the processing consider the following table where the original PCAP duration 273 | is reset from 20secs down to 10secs using `--set-duration` option: 274 | 275 | | Frame index | Frame relative time in original PCAP | Frame relative time in output PCAP | 276 | |-------------|--------------------------------------|------------------------------------| 277 | | 1 | +0.0 | +0.0 | 278 | | 2 | +1.0 | +2.5 | 279 | | 3 | +15.0 | +5.0 | 280 | | 4 | +18.0 | +7.5 | 281 | | 5 | +20.0 | +10.0 | 282 | 283 | See the following example session: 284 | 285 | ``` 286 | $ large_pcap_analyzer --timing test-pcaps/ipv4_gtpu_https.pcap 287 | 0M packets (18201 packets) were loaded from PCAP. 288 | Last packet has a timestamp offset = 473.48sec = 7.89min = 0.13hours 289 | Tcpreplay should replay this PCAP at an average of 0.27Mbps / 38.44pps to respect PCAP timings. 290 | 291 | $ large_pcap_analyzer --set-duration 1.2 --write /tmp/test.pcap test-pcaps/ipv4_gtpu_https.pcap 292 | PCAP duration will be set to: 1.200000 secs 293 | Successfully opened output PCAP '/tmp/test.pcap' 294 | Packet processing operations require 2 passes: performing first pass 295 | 0M packets (18201 packets) were loaded from PCAP. 296 | Packet processing operations require 2 passes: performing second pass 297 | 0M packets (18201 packets) were loaded from PCAP. 298 | 0M packets (18201 packets) were processed and saved into output PCAP. 299 | 300 | $ large_pcap_analyzer --timing /tmp/test.pcap 301 | 0M packets (18201 packets) were loaded from PCAP. 302 | Last packet has a timestamp offset = 1.20sec = 0.02min = 0.00hours 303 | Tcpreplay should replay this PCAP at an average of 105.00Mbps / 15167.50pps to respect PCAP timings. 304 | ``` 305 | 306 | Note that using `--set-duration` all timestamps in the resulting PCAP will have an equal inter-frame-gap (IFG). 307 | In other words the original IFGs will be lost. 308 | 309 | 310 | # Example run 7: set PCAP duration preserving IFG 311 | 312 | Repeating example #6 using `--set-duration-preserve-ifg` instead of `--set-duration` will give the same 313 | result as far as the total PCAP duration is concerned, but the ratio between the new PCAP IFGs and the original 314 | PCAP IFGs will be preserved. 315 | To better explain the result of the processing consider the following table where the original PCAP duration 316 | is scaled down by a factor of 10 using `--set-duration-preserve-ifg`: 317 | 318 | | Frame index | Frame relative time in original PCAP | Frame relative time in output PCAP | 319 | |-------------|--------------------------------------|------------------------------------| 320 | | 1 | +0.0 | +0.0 | 321 | | 2 | +1.0 | +0.1 | 322 | | 3 | +15.0 | +1.5 | 323 | | 4 | +16.0 | +1.6 | 324 | 325 | As you can see the inter-frame-gaps (IFGs) among the packets are preserved: the packet #4 in the original PCAP 326 | has a timestamp difference from packet #1 equal to 16secs that become 1.6secs in the rescaled PCAP. 327 | The same ratio is found considering the timestamp difference between packet #4 and packet #3: it is 1sec in 328 | the original PCAP and 0.1sec in the rescaled output PCAP. 329 | 330 | ``` 331 | $ large_pcap_analyzer --set-duration-preserve-ifg 1.2 --write /tmp/test.pcap test-pcaps/ipv4_gtpu_https.pcap 332 | PCAP duration will be set to: 1.200000 secs 333 | Successfully opened output PCAP '/tmp/test.pcap' 334 | Packet processing operations require 2 passes: performing first pass 335 | 0M packets (18201 packets) were loaded from PCAP. 336 | Packet processing operations require 2 passes: performing second pass 337 | 0M packets (18201 packets) were loaded from PCAP. 338 | 0M packets (18201 packets) were processed and saved into output PCAP. 339 | 340 | $ large_pcap_analyzer --timing /tmp/test.pcap 341 | 0M packets (18201 packets) were loaded from PCAP. 342 | Last packet has a timestamp offset = 1.20sec = 0.02min = 0.00hours 343 | Tcpreplay should replay this PCAP at an average of 105.00Mbps / 15167.50pps to respect PCAP timings. 344 | ``` 345 | 346 | 347 | # Example run 8: change PCAP timestamps 348 | 349 | In this example the timestamps of 2 packets are manually tweaked. 350 | First of all current timestamps are extracted using a tool like [tshark](https://www.wireshark.org/docs/man-pages/tshark.html), 351 | in Epoch format: 352 | 353 | ``` 354 | $ tshark -F pcap -r test-pcaps/timing-test.pcap -Tfields -e frame.time_epoch >pkts_timings.txt 355 | ``` 356 | 357 | Then the timestamps of the 10-th packet and 11-th packet are replaced with the absolute time "Saturday 9 February 2019 19:20:00", 358 | corresponding to the Unix timestamp value 1549740000 (you can use an online tool like https://www.epochconverter.com/), 359 | in the dump of packet timestamps: 360 | 361 | ``` 362 | $ sed -i '10s/.*/1549740000.000000000/' pkts_timings.txt 363 | $ sed -i '11s/.*/1549740000.100000000/' pkts_timings.txt 364 | ``` 365 | 366 | Finally using the Large PCAP file analyzer tool, the capture trace is actually modified and the result is saved into the 367 | "out.pcap" file: 368 | 369 | ``` 370 | $ large_pcap_analyzer --write out.pcap --set-timestamps-from pkts_timings.txt test-pcaps/timing-test.pcap 371 | ``` 372 | 373 | 374 | # Example run 9: generate traffic reports 375 | 376 | In this example we are interested in having a quick overview of the top 10 connections contained in the PCAP file 377 | that carry the highest number of packets. 378 | To achieve that, from the --help overview, the "top10flows_by_pkts" traffic report is selected: 379 | 380 | ``` 381 | $ ./large_pcap_analyzer --report top10flows_by_pkts test-pcaps/ipv4_gtpu_https.pcap 382 | 0M packets (18201 packets) were loaded from PCAP. 383 | Packet parsing failed for 0/18201 pkts. Total number of packets/flows detected: 18201/213. 384 | Traffic report in CSV format: 385 | flow_num,num_pkts,%pkts,num_bytes,%bytes,flow_hash,ip_src,ip_dst,ip_proto,port_src,port_dst 386 | 0,5562,30.56,5779325,34.98,5346E247DCBCA579,10.85.73.237,202.122.145.141,6,49789,443 387 | 1,5043,27.71,5262864,31.86,359E06A6A0671690,10.85.73.237,202.122.145.141,6,40461,443 388 | 2,4328,23.78,4470177,27.06,D0D80A5F6A8B3F4F,10.85.73.237,202.122.145.141,6,50059,443 389 | 3,849,4.66,214889,1.30,13111CC415F92E54,10.85.73.237,58.71.141.53,6,49473,10000 390 | 4,209,1.15,46214,0.28,5255AFFF70F6BFB5,10.85.73.237,58.71.141.53,6,49407,10000 391 | 5,144,0.79,33478,0.20,DE86E0ED622568E9,10.85.73.237,208.67.76.95,6,49461,443 392 | 6,112,0.62,74928,0.45,EC297958A1CA3946,10.85.73.237,216.58.196.74,6,54805,443 393 | 7,101,0.55,28010,0.17,E26097004F952A54,10.85.73.237,208.67.76.95,6,49460,443 394 | 8,59,0.32,10551,0.06,D3CD1D4C2E38C2C2,10.85.73.237,121.123.204.19,6,49458,80 395 | 9,56,0.31,17508,0.11,54FD688F6BAAAF89,10.85.73.237,65.52.108.154,6,49453,443 396 | Completed generation of 10 lines of traffic report. 397 | ``` 398 | 399 | From such output it's clear that the TCP connection (ip_proto=6) between IPs 10.85.73.237 and 202.122.145.141 400 | on TCP ports 49789 and 443 (default HTTPs ports) is the connection which transported the highest number of 401 | packets (30.56%) and bytes (34.98%). 402 | 403 | Note that, even if pkt/byte counts do not matter, the traffic reports are also an handy way to 404 | count and dump all connections found inside a PCAP file. 405 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -i 4 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Wrapper for compilers which do not understand '-c -o'. 3 | 4 | scriptversion=2012-10-14.11; # UTC 5 | 6 | # Copyright (C) 1999-2014 Free Software Foundation, Inc. 7 | # Written by Tom Tromey . 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # This file is maintained in Automake, please report 28 | # bugs to or send patches to 29 | # . 30 | 31 | nl=' 32 | ' 33 | 34 | # We need space, tab and new line, in precisely that order. Quoting is 35 | # there to prevent tools from complaining about whitespace usage. 36 | IFS=" "" $nl" 37 | 38 | file_conv= 39 | 40 | # func_file_conv build_file lazy 41 | # Convert a $build file to $host form and store it in $file 42 | # Currently only supports Windows hosts. If the determined conversion 43 | # type is listed in (the comma separated) LAZY, no conversion will 44 | # take place. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv/,$2, in 65 | *,$file_conv,*) 66 | ;; 67 | mingw/*) 68 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 69 | ;; 70 | cygwin/*) 71 | file=`cygpath -m "$file" || echo "$file"` 72 | ;; 73 | wine/*) 74 | file=`winepath -w "$file" || echo "$file"` 75 | ;; 76 | esac 77 | ;; 78 | esac 79 | } 80 | 81 | # func_cl_dashL linkdir 82 | # Make cl look for libraries in LINKDIR 83 | func_cl_dashL () 84 | { 85 | func_file_conv "$1" 86 | if test -z "$lib_path"; then 87 | lib_path=$file 88 | else 89 | lib_path="$lib_path;$file" 90 | fi 91 | linker_opts="$linker_opts -LIBPATH:$file" 92 | } 93 | 94 | # func_cl_dashl library 95 | # Do a library search-path lookup for cl 96 | func_cl_dashl () 97 | { 98 | lib=$1 99 | found=no 100 | save_IFS=$IFS 101 | IFS=';' 102 | for dir in $lib_path $LIB 103 | do 104 | IFS=$save_IFS 105 | if $shared && test -f "$dir/$lib.dll.lib"; then 106 | found=yes 107 | lib=$dir/$lib.dll.lib 108 | break 109 | fi 110 | if test -f "$dir/$lib.lib"; then 111 | found=yes 112 | lib=$dir/$lib.lib 113 | break 114 | fi 115 | if test -f "$dir/lib$lib.a"; then 116 | found=yes 117 | lib=$dir/lib$lib.a 118 | break 119 | fi 120 | done 121 | IFS=$save_IFS 122 | 123 | if test "$found" != yes; then 124 | lib=$lib.lib 125 | fi 126 | } 127 | 128 | # func_cl_wrapper cl arg... 129 | # Adjust compile command to suit cl 130 | func_cl_wrapper () 131 | { 132 | # Assume a capable shell 133 | lib_path= 134 | shared=: 135 | linker_opts= 136 | for arg 137 | do 138 | if test -n "$eat"; then 139 | eat= 140 | else 141 | case $1 in 142 | -o) 143 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 144 | eat=1 145 | case $2 in 146 | *.o | *.[oO][bB][jJ]) 147 | func_file_conv "$2" 148 | set x "$@" -Fo"$file" 149 | shift 150 | ;; 151 | *) 152 | func_file_conv "$2" 153 | set x "$@" -Fe"$file" 154 | shift 155 | ;; 156 | esac 157 | ;; 158 | -I) 159 | eat=1 160 | func_file_conv "$2" mingw 161 | set x "$@" -I"$file" 162 | shift 163 | ;; 164 | -I*) 165 | func_file_conv "${1#-I}" mingw 166 | set x "$@" -I"$file" 167 | shift 168 | ;; 169 | -l) 170 | eat=1 171 | func_cl_dashl "$2" 172 | set x "$@" "$lib" 173 | shift 174 | ;; 175 | -l*) 176 | func_cl_dashl "${1#-l}" 177 | set x "$@" "$lib" 178 | shift 179 | ;; 180 | -L) 181 | eat=1 182 | func_cl_dashL "$2" 183 | ;; 184 | -L*) 185 | func_cl_dashL "${1#-L}" 186 | ;; 187 | -static) 188 | shared=false 189 | ;; 190 | -Wl,*) 191 | arg=${1#-Wl,} 192 | save_ifs="$IFS"; IFS=',' 193 | for flag in $arg; do 194 | IFS="$save_ifs" 195 | linker_opts="$linker_opts $flag" 196 | done 197 | IFS="$save_ifs" 198 | ;; 199 | -Xlinker) 200 | eat=1 201 | linker_opts="$linker_opts $2" 202 | ;; 203 | -*) 204 | set x "$@" "$1" 205 | shift 206 | ;; 207 | *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) 208 | func_file_conv "$1" 209 | set x "$@" -Tp"$file" 210 | shift 211 | ;; 212 | *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) 213 | func_file_conv "$1" mingw 214 | set x "$@" "$file" 215 | shift 216 | ;; 217 | *) 218 | set x "$@" "$1" 219 | shift 220 | ;; 221 | esac 222 | fi 223 | shift 224 | done 225 | if test -n "$linker_opts"; then 226 | linker_opts="-link$linker_opts" 227 | fi 228 | exec "$@" $linker_opts 229 | exit 1 230 | } 231 | 232 | eat= 233 | 234 | case $1 in 235 | '') 236 | echo "$0: No command. Try '$0 --help' for more information." 1>&2 237 | exit 1; 238 | ;; 239 | -h | --h*) 240 | cat <<\EOF 241 | Usage: compile [--help] [--version] PROGRAM [ARGS] 242 | 243 | Wrapper for compilers which do not understand '-c -o'. 244 | Remove '-o dest.o' from ARGS, run PROGRAM with the remaining 245 | arguments, and rename the output as expected. 246 | 247 | If you are trying to build a whole package this is not the 248 | right script to run: please start by reading the file 'INSTALL'. 249 | 250 | Report bugs to . 251 | EOF 252 | exit $? 253 | ;; 254 | -v | --v*) 255 | echo "compile $scriptversion" 256 | exit $? 257 | ;; 258 | cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) 259 | func_cl_wrapper "$@" # Doesn't return... 260 | ;; 261 | esac 262 | 263 | ofile= 264 | cfile= 265 | 266 | for arg 267 | do 268 | if test -n "$eat"; then 269 | eat= 270 | else 271 | case $1 in 272 | -o) 273 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 274 | # So we strip '-o arg' only if arg is an object. 275 | eat=1 276 | case $2 in 277 | *.o | *.obj) 278 | ofile=$2 279 | ;; 280 | *) 281 | set x "$@" -o "$2" 282 | shift 283 | ;; 284 | esac 285 | ;; 286 | *.c) 287 | cfile=$1 288 | set x "$@" "$1" 289 | shift 290 | ;; 291 | *) 292 | set x "$@" "$1" 293 | shift 294 | ;; 295 | esac 296 | fi 297 | shift 298 | done 299 | 300 | if test -z "$ofile" || test -z "$cfile"; then 301 | # If no '-o' option was seen then we might have been invoked from a 302 | # pattern rule where we don't need one. That is ok -- this is a 303 | # normal compilation that the losing compiler can handle. If no 304 | # '.c' file was seen then we are probably linking. That is also 305 | # ok. 306 | exec "$@" 307 | fi 308 | 309 | # Name of file we expect compiler to create. 310 | cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` 311 | 312 | # Create the lock directory. 313 | # Note: use '[/\\:.-]' here to ensure that we don't use the same name 314 | # that we are using for the .o file. Also, base the name on the expected 315 | # object file name, since that is what matters with a parallel build. 316 | lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d 317 | while true; do 318 | if mkdir "$lockdir" >/dev/null 2>&1; then 319 | break 320 | fi 321 | sleep 1 322 | done 323 | # FIXME: race condition here if user kills between mkdir and trap. 324 | trap "rmdir '$lockdir'; exit 1" 1 2 15 325 | 326 | # Run the compile. 327 | "$@" 328 | ret=$? 329 | 330 | if test -f "$cofile"; then 331 | test "$cofile" = "$ofile" || mv "$cofile" "$ofile" 332 | elif test -f "${cofile}bj"; then 333 | test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" 334 | fi 335 | 336 | rmdir "$lockdir" 337 | exit $ret 338 | 339 | # Local Variables: 340 | # mode: shell-script 341 | # sh-indentation: 2 342 | # eval: (add-hook 'write-file-hooks 'time-stamp) 343 | # time-stamp-start: "scriptversion=" 344 | # time-stamp-format: "%:y-%02m-%02d.%02H" 345 | # time-stamp-time-zone: "UTC" 346 | # time-stamp-end: "; # UTC" 347 | # End: 348 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Process this file with autoreconf to produce a configure script. 2 | 3 | AC_PREREQ([2.69]) 4 | AC_INIT([large-pcap-analyzer], [3.8.2], [francesco.montorsi@gmail.com]) 5 | AC_CONFIG_SRCDIR([src]) 6 | AC_CONFIG_HEADERS([src/config.h]) 7 | AC_CONFIG_MACRO_DIR([m4]) 8 | AM_INIT_AUTOMAKE 9 | 10 | AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [Enable debug build; default is release]),[enable_debug=$enableval],[enable_debug=no]) 11 | 12 | # do not get the -g -O2 default CFLAGS: 13 | : ${CFLAGS=""} 14 | : ${CXXFLAGS=""} 15 | 16 | 17 | 18 | # handle debug/release build 19 | common_flags="-Wall -Wextra -Werror" 20 | 21 | # useful for debug builds: 22 | debug_flags="$common_flags -g -O0 -DDEBUG" 23 | release_flags="$common_flags -O3" 24 | 25 | AC_MSG_CHECKING([for debug build mode]) 26 | AS_IF([test "x$enable_debug" = "xyes"], [ 27 | CXXFLAGS="$CXXFLAGS $debug_flags" 28 | AC_MSG_RESULT([debug mode enabled]) 29 | ], [ 30 | CXXFLAGS="$CXXFLAGS $release_flags" 31 | AC_MSG_RESULT([release mode enabled]) 32 | ]) 33 | 34 | # Checks for programs. 35 | AC_PROG_CC 36 | AC_PROG_CXX 37 | AX_CXX_COMPILE_STDCXX_11([noext], [optional]) 38 | 39 | AC_PROG_INSTALL 40 | AC_PROG_MAKE_SET 41 | 42 | # Checks for libraries. 43 | AC_CHECK_LIB([pcap], [pcap_open_live], [], [echo "PCAP library not found. Aborting." ; exit 1]) 44 | 45 | # Checks for header files. 46 | AC_CHECK_HEADER([pcap.h]) 47 | 48 | # Checks for typedefs, structures, and compiler characteristics. 49 | 50 | # Checks for library functions. 51 | AC_CHECK_FUNCS([gettimeofday memset]) 52 | 53 | 54 | AC_CONFIG_FILES([Makefile]) 55 | AC_OUTPUT 56 | -------------------------------------------------------------------------------- /icon/lpa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/icon/lpa.png -------------------------------------------------------------------------------- /icon/lpa.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/icon/lpa.webp -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # install - install a program, script, or datafile 3 | 4 | scriptversion=2014-09-12.12; # UTC 5 | 6 | # This originates from X11R5 (mit/util/scripts/install.sh), which was 7 | # later released in X11R6 (xc/config/util/install.sh) with the 8 | # following copyright and license. 9 | # 10 | # Copyright (C) 1994 X Consortium 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documentation files (the "Software"), to 14 | # deal in the Software without restriction, including without limitation the 15 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | # sell copies of the Software, and to permit persons to whom the Software is 17 | # furnished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 26 | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- 27 | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # Except as contained in this notice, the name of the X Consortium shall not 30 | # be used in advertising or otherwise to promote the sale, use or other deal- 31 | # ings in this Software without prior written authorization from the X Consor- 32 | # tium. 33 | # 34 | # 35 | # FSF changes to this file are in the public domain. 36 | # 37 | # Calling this script install-sh is preferred over install.sh, to prevent 38 | # 'make' implicit rules from creating a file called install from it 39 | # when there is no Makefile. 40 | # 41 | # This script is compatible with the BSD install script, but was written 42 | # from scratch. 43 | 44 | tab=' ' 45 | nl=' 46 | ' 47 | IFS=" $tab$nl" 48 | 49 | # Set DOITPROG to "echo" to test this script. 50 | 51 | doit=${DOITPROG-} 52 | doit_exec=${doit:-exec} 53 | 54 | # Put in absolute file names if you don't have them in your path; 55 | # or use environment vars. 56 | 57 | chgrpprog=${CHGRPPROG-chgrp} 58 | chmodprog=${CHMODPROG-chmod} 59 | chownprog=${CHOWNPROG-chown} 60 | cmpprog=${CMPPROG-cmp} 61 | cpprog=${CPPROG-cp} 62 | mkdirprog=${MKDIRPROG-mkdir} 63 | mvprog=${MVPROG-mv} 64 | rmprog=${RMPROG-rm} 65 | stripprog=${STRIPPROG-strip} 66 | 67 | posix_mkdir= 68 | 69 | # Desired mode of installed file. 70 | mode=0755 71 | 72 | chgrpcmd= 73 | chmodcmd=$chmodprog 74 | chowncmd= 75 | mvcmd=$mvprog 76 | rmcmd="$rmprog -f" 77 | stripcmd= 78 | 79 | src= 80 | dst= 81 | dir_arg= 82 | dst_arg= 83 | 84 | copy_on_change=false 85 | is_target_a_directory=possibly 86 | 87 | usage="\ 88 | Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE 89 | or: $0 [OPTION]... SRCFILES... DIRECTORY 90 | or: $0 [OPTION]... -t DIRECTORY SRCFILES... 91 | or: $0 [OPTION]... -d DIRECTORIES... 92 | 93 | In the 1st form, copy SRCFILE to DSTFILE. 94 | In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. 95 | In the 4th, create DIRECTORIES. 96 | 97 | Options: 98 | --help display this help and exit. 99 | --version display version info and exit. 100 | 101 | -c (ignored) 102 | -C install only if different (preserve the last data modification time) 103 | -d create directories instead of installing files. 104 | -g GROUP $chgrpprog installed files to GROUP. 105 | -m MODE $chmodprog installed files to MODE. 106 | -o USER $chownprog installed files to USER. 107 | -s $stripprog installed files. 108 | -t DIRECTORY install into DIRECTORY. 109 | -T report an error if DSTFILE is a directory. 110 | 111 | Environment variables override the default commands: 112 | CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG 113 | RMPROG STRIPPROG 114 | " 115 | 116 | while test $# -ne 0; do 117 | case $1 in 118 | -c) ;; 119 | 120 | -C) copy_on_change=true;; 121 | 122 | -d) dir_arg=true;; 123 | 124 | -g) chgrpcmd="$chgrpprog $2" 125 | shift;; 126 | 127 | --help) echo "$usage"; exit $?;; 128 | 129 | -m) mode=$2 130 | case $mode in 131 | *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) 132 | echo "$0: invalid mode: $mode" >&2 133 | exit 1;; 134 | esac 135 | shift;; 136 | 137 | -o) chowncmd="$chownprog $2" 138 | shift;; 139 | 140 | -s) stripcmd=$stripprog;; 141 | 142 | -t) 143 | is_target_a_directory=always 144 | dst_arg=$2 145 | # Protect names problematic for 'test' and other utilities. 146 | case $dst_arg in 147 | -* | [=\(\)!]) dst_arg=./$dst_arg;; 148 | esac 149 | shift;; 150 | 151 | -T) is_target_a_directory=never;; 152 | 153 | --version) echo "$0 $scriptversion"; exit $?;; 154 | 155 | --) shift 156 | break;; 157 | 158 | -*) echo "$0: invalid option: $1" >&2 159 | exit 1;; 160 | 161 | *) break;; 162 | esac 163 | shift 164 | done 165 | 166 | # We allow the use of options -d and -T together, by making -d 167 | # take the precedence; this is for compatibility with GNU install. 168 | 169 | if test -n "$dir_arg"; then 170 | if test -n "$dst_arg"; then 171 | echo "$0: target directory not allowed when installing a directory." >&2 172 | exit 1 173 | fi 174 | fi 175 | 176 | if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then 177 | # When -d is used, all remaining arguments are directories to create. 178 | # When -t is used, the destination is already specified. 179 | # Otherwise, the last argument is the destination. Remove it from $@. 180 | for arg 181 | do 182 | if test -n "$dst_arg"; then 183 | # $@ is not empty: it contains at least $arg. 184 | set fnord "$@" "$dst_arg" 185 | shift # fnord 186 | fi 187 | shift # arg 188 | dst_arg=$arg 189 | # Protect names problematic for 'test' and other utilities. 190 | case $dst_arg in 191 | -* | [=\(\)!]) dst_arg=./$dst_arg;; 192 | esac 193 | done 194 | fi 195 | 196 | if test $# -eq 0; then 197 | if test -z "$dir_arg"; then 198 | echo "$0: no input file specified." >&2 199 | exit 1 200 | fi 201 | # It's OK to call 'install-sh -d' without argument. 202 | # This can happen when creating conditional directories. 203 | exit 0 204 | fi 205 | 206 | if test -z "$dir_arg"; then 207 | if test $# -gt 1 || test "$is_target_a_directory" = always; then 208 | if test ! -d "$dst_arg"; then 209 | echo "$0: $dst_arg: Is not a directory." >&2 210 | exit 1 211 | fi 212 | fi 213 | fi 214 | 215 | if test -z "$dir_arg"; then 216 | do_exit='(exit $ret); exit $ret' 217 | trap "ret=129; $do_exit" 1 218 | trap "ret=130; $do_exit" 2 219 | trap "ret=141; $do_exit" 13 220 | trap "ret=143; $do_exit" 15 221 | 222 | # Set umask so as not to create temps with too-generous modes. 223 | # However, 'strip' requires both read and write access to temps. 224 | case $mode in 225 | # Optimize common cases. 226 | *644) cp_umask=133;; 227 | *755) cp_umask=22;; 228 | 229 | *[0-7]) 230 | if test -z "$stripcmd"; then 231 | u_plus_rw= 232 | else 233 | u_plus_rw='% 200' 234 | fi 235 | cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; 236 | *) 237 | if test -z "$stripcmd"; then 238 | u_plus_rw= 239 | else 240 | u_plus_rw=,u+rw 241 | fi 242 | cp_umask=$mode$u_plus_rw;; 243 | esac 244 | fi 245 | 246 | for src 247 | do 248 | # Protect names problematic for 'test' and other utilities. 249 | case $src in 250 | -* | [=\(\)!]) src=./$src;; 251 | esac 252 | 253 | if test -n "$dir_arg"; then 254 | dst=$src 255 | dstdir=$dst 256 | test -d "$dstdir" 257 | dstdir_status=$? 258 | else 259 | 260 | # Waiting for this to be detected by the "$cpprog $src $dsttmp" command 261 | # might cause directories to be created, which would be especially bad 262 | # if $src (and thus $dsttmp) contains '*'. 263 | if test ! -f "$src" && test ! -d "$src"; then 264 | echo "$0: $src does not exist." >&2 265 | exit 1 266 | fi 267 | 268 | if test -z "$dst_arg"; then 269 | echo "$0: no destination specified." >&2 270 | exit 1 271 | fi 272 | dst=$dst_arg 273 | 274 | # If destination is a directory, append the input filename; won't work 275 | # if double slashes aren't ignored. 276 | if test -d "$dst"; then 277 | if test "$is_target_a_directory" = never; then 278 | echo "$0: $dst_arg: Is a directory" >&2 279 | exit 1 280 | fi 281 | dstdir=$dst 282 | dst=$dstdir/`basename "$src"` 283 | dstdir_status=0 284 | else 285 | dstdir=`dirname "$dst"` 286 | test -d "$dstdir" 287 | dstdir_status=$? 288 | fi 289 | fi 290 | 291 | obsolete_mkdir_used=false 292 | 293 | if test $dstdir_status != 0; then 294 | case $posix_mkdir in 295 | '') 296 | # Create intermediate dirs using mode 755 as modified by the umask. 297 | # This is like FreeBSD 'install' as of 1997-10-28. 298 | umask=`umask` 299 | case $stripcmd.$umask in 300 | # Optimize common cases. 301 | *[2367][2367]) mkdir_umask=$umask;; 302 | .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; 303 | 304 | *[0-7]) 305 | mkdir_umask=`expr $umask + 22 \ 306 | - $umask % 100 % 40 + $umask % 20 \ 307 | - $umask % 10 % 4 + $umask % 2 308 | `;; 309 | *) mkdir_umask=$umask,go-w;; 310 | esac 311 | 312 | # With -d, create the new directory with the user-specified mode. 313 | # Otherwise, rely on $mkdir_umask. 314 | if test -n "$dir_arg"; then 315 | mkdir_mode=-m$mode 316 | else 317 | mkdir_mode= 318 | fi 319 | 320 | posix_mkdir=false 321 | case $umask in 322 | *[123567][0-7][0-7]) 323 | # POSIX mkdir -p sets u+wx bits regardless of umask, which 324 | # is incompatible with FreeBSD 'install' when (umask & 300) != 0. 325 | ;; 326 | *) 327 | # $RANDOM is not portable (e.g. dash); use it when possible to 328 | # lower collision chance 329 | tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ 330 | trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 331 | 332 | # As "mkdir -p" follows symlinks and we work in /tmp possibly; so 333 | # create the $tmpdir first (and fail if unsuccessful) to make sure 334 | # that nobody tries to guess the $tmpdir name. 335 | if (umask $mkdir_umask && 336 | $mkdirprog $mkdir_mode "$tmpdir" && 337 | exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 338 | then 339 | if test -z "$dir_arg" || { 340 | # Check for POSIX incompatibilities with -m. 341 | # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or 342 | # other-writable bit of parent directory when it shouldn't. 343 | # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. 344 | test_tmpdir="$tmpdir/a" 345 | ls_ld_tmpdir=`ls -ld "$test_tmpdir"` 346 | case $ls_ld_tmpdir in 347 | d????-?r-*) different_mode=700;; 348 | d????-?--*) different_mode=755;; 349 | *) false;; 350 | esac && 351 | $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { 352 | ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` 353 | test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" 354 | } 355 | } 356 | then posix_mkdir=: 357 | fi 358 | rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 359 | else 360 | # Remove any dirs left behind by ancient mkdir implementations. 361 | rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null 362 | fi 363 | trap '' 0;; 364 | esac;; 365 | esac 366 | 367 | if 368 | $posix_mkdir && ( 369 | umask $mkdir_umask && 370 | $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" 371 | ) 372 | then : 373 | else 374 | 375 | # The umask is ridiculous, or mkdir does not conform to POSIX, 376 | # or it failed possibly due to a race condition. Create the 377 | # directory the slow way, step by step, checking for races as we go. 378 | 379 | case $dstdir in 380 | /*) prefix='/';; 381 | [-=\(\)!]*) prefix='./';; 382 | *) prefix='';; 383 | esac 384 | 385 | oIFS=$IFS 386 | IFS=/ 387 | set -f 388 | set fnord $dstdir 389 | shift 390 | set +f 391 | IFS=$oIFS 392 | 393 | prefixes= 394 | 395 | for d 396 | do 397 | test X"$d" = X && continue 398 | 399 | prefix=$prefix$d 400 | if test -d "$prefix"; then 401 | prefixes= 402 | else 403 | if $posix_mkdir; then 404 | (umask=$mkdir_umask && 405 | $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break 406 | # Don't fail if two instances are running concurrently. 407 | test -d "$prefix" || exit 1 408 | else 409 | case $prefix in 410 | *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; 411 | *) qprefix=$prefix;; 412 | esac 413 | prefixes="$prefixes '$qprefix'" 414 | fi 415 | fi 416 | prefix=$prefix/ 417 | done 418 | 419 | if test -n "$prefixes"; then 420 | # Don't fail if two instances are running concurrently. 421 | (umask $mkdir_umask && 422 | eval "\$doit_exec \$mkdirprog $prefixes") || 423 | test -d "$dstdir" || exit 1 424 | obsolete_mkdir_used=true 425 | fi 426 | fi 427 | fi 428 | 429 | if test -n "$dir_arg"; then 430 | { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && 431 | { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && 432 | { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || 433 | test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 434 | else 435 | 436 | # Make a couple of temp file names in the proper directory. 437 | dsttmp=$dstdir/_inst.$$_ 438 | rmtmp=$dstdir/_rm.$$_ 439 | 440 | # Trap to clean up those temp files at exit. 441 | trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 442 | 443 | # Copy the file name to the temp name. 444 | (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && 445 | 446 | # and set any options; do chmod last to preserve setuid bits. 447 | # 448 | # If any of these fail, we abort the whole thing. If we want to 449 | # ignore errors from any of these, just make sure not to ignore 450 | # errors from the above "$doit $cpprog $src $dsttmp" command. 451 | # 452 | { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && 453 | { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && 454 | { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && 455 | { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && 456 | 457 | # If -C, don't bother to copy if it wouldn't change the file. 458 | if $copy_on_change && 459 | old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && 460 | new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && 461 | set -f && 462 | set X $old && old=:$2:$4:$5:$6 && 463 | set X $new && new=:$2:$4:$5:$6 && 464 | set +f && 465 | test "$old" = "$new" && 466 | $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 467 | then 468 | rm -f "$dsttmp" 469 | else 470 | # Rename the file to the real destination. 471 | $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || 472 | 473 | # The rename failed, perhaps because mv can't rename something else 474 | # to itself, or perhaps because mv is so ancient that it does not 475 | # support -f. 476 | { 477 | # Now remove or move aside any old file at destination location. 478 | # We try this two ways since rm can't unlink itself on some 479 | # systems and the destination file might be busy for other 480 | # reasons. In this case, the final cleanup might fail but the new 481 | # file should still install successfully. 482 | { 483 | test ! -f "$dst" || 484 | $doit $rmcmd -f "$dst" 2>/dev/null || 485 | { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && 486 | { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } 487 | } || 488 | { echo "$0: cannot unlink or rename $dst" >&2 489 | (exit 1); exit 1 490 | } 491 | } && 492 | 493 | # Now rename the file to the real destination. 494 | $doit $mvcmd "$dsttmp" "$dst" 495 | } 496 | fi || exit 1 497 | 498 | trap '' 0 499 | fi 500 | done 501 | 502 | # Local variables: 503 | # eval: (add-hook 'write-file-hooks 'time-stamp) 504 | # time-stamp-start: "scriptversion=" 505 | # time-stamp-format: "%:y-%02m-%02d.%02H" 506 | # time-stamp-time-zone: "UTC" 507 | # time-stamp-end: "; # UTC" 508 | # End: 509 | -------------------------------------------------------------------------------- /m4/ax_cxx_compile_stdcxx_11.m4: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html 3 | # ============================================================================= 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # Check for baseline language coverage in the compiler for the C++11 12 | # standard; if necessary, add switches to CXX and CXXCPP to enable 13 | # support. 14 | # 15 | # This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX 16 | # macro with the version set to C++11. The two optional arguments are 17 | # forwarded literally as the second and third argument respectively. 18 | # Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for 19 | # more information. If you want to use this macro, you also need to 20 | # download the ax_cxx_compile_stdcxx.m4 file. 21 | # 22 | # LICENSE 23 | # 24 | # Copyright (c) 2008 Benjamin Kosnik 25 | # Copyright (c) 2012 Zack Weinberg 26 | # Copyright (c) 2013 Roy Stogner 27 | # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov 28 | # Copyright (c) 2015 Paul Norman 29 | # Copyright (c) 2015 Moritz Klammler 30 | # 31 | # Copying and distribution of this file, with or without modification, are 32 | # permitted in any medium without royalty provided the copyright notice 33 | # and this notice are preserved. This file is offered as-is, without any 34 | # warranty. 35 | 36 | #serial 18 37 | 38 | AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX]) 39 | AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])]) 40 | -------------------------------------------------------------------------------- /missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common wrapper for a few potentially missing GNU programs. 3 | 4 | scriptversion=2013-10-28.13; # UTC 5 | 6 | # Copyright (C) 1996-2014 Free Software Foundation, Inc. 7 | # Originally written by Fran,cois Pinard , 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=http://www.perl.org/ 105 | flex_URL=http://flex.sourceforge.net/ 106 | gnu_software_URL=http://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'autom4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'write-file-hooks 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: large-pcap-analyzer 2 | version: 3.8.2 3 | summary: Command-line utility to process .PCAP files very quickly. 4 | description: | 5 | Large PCAP file analyzer is a command-line utility program that performs some simple operations 6 | on .PCAP files very quickly. This allows you to manipulate also very large PCAP files that cannot 7 | be easily handled with other software like Wireshark. 8 | 9 | grade: stable 10 | 11 | confinement: strict 12 | 13 | apps: 14 | # NOTE: even though the software binary is using underscores (large_pcap_analyzer), the snapcraft 15 | # tool REQUIRES the use of dashes in place of underscores 16 | large-pcap-analyzer: 17 | command: large_pcap_analyzer 18 | plugs: [ home, removable-media ] 19 | 20 | parts: 21 | large-pcap-analyzer: 22 | plugin: autotools 23 | source: https://github.com/f18m/large-pcap-analyzer/archive/3.8.2.tar.gz 24 | build-packages: 25 | - g++ 26 | - make 27 | - libpcap-dev 28 | -------------------------------------------------------------------------------- /spec/large-pcap-analyzer.spec: -------------------------------------------------------------------------------- 1 | Name: large-pcap-analyzer 2 | Version: 3.8.2 3 | Release: 1%{?dist} 4 | Summary: A command-line utility program to process PCAP captures 5 | 6 | License: GPL 7 | URL: https://github.com/f18m/large-pcap-analyzer 8 | Source0: https://github.com/f18m/large-pcap-analyzer/archive/3.8.2.tar.gz 9 | 10 | BuildRequires: gcc-c++, libpcap-devel, make 11 | 12 | %description 13 | A command-line utility program that performs some simple operations on .PCAP files very quickly. 14 | This allows you to manipulate also very large PCAP files that cannot be easily handled with other 15 | software like Wireshark (or tshark). Supports filtering encapsulated GTPu frames. 16 | Supports simple per-TCP-stream filtering. Easily extendible. 17 | 18 | %prep 19 | %autosetup 20 | 21 | %build 22 | %configure 23 | %make_build 24 | 25 | %install 26 | rm -rf %{buildroot} 27 | %make_install 28 | 29 | %files 30 | %{_bindir}/large_pcap_analyzer 31 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | /* src/config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* define if the compiler supports basic C++11 syntax */ 4 | #undef HAVE_CXX11 5 | 6 | /* Define to 1 if you have the `gettimeofday' function. */ 7 | #undef HAVE_GETTIMEOFDAY 8 | 9 | /* Define to 1 if you have the header file. */ 10 | #undef HAVE_INTTYPES_H 11 | 12 | /* Define to 1 if you have the `pcap' library (-lpcap). */ 13 | #undef HAVE_LIBPCAP 14 | 15 | /* Define to 1 if you have the header file. */ 16 | #undef HAVE_MEMORY_H 17 | 18 | /* Define to 1 if you have the `memset' function. */ 19 | #undef HAVE_MEMSET 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #undef HAVE_STDINT_H 23 | 24 | /* Define to 1 if you have the header file. */ 25 | #undef HAVE_STDLIB_H 26 | 27 | /* Define to 1 if you have the header file. */ 28 | #undef HAVE_STRINGS_H 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #undef HAVE_STRING_H 32 | 33 | /* Define to 1 if you have the header file. */ 34 | #undef HAVE_SYS_STAT_H 35 | 36 | /* Define to 1 if you have the header file. */ 37 | #undef HAVE_SYS_TYPES_H 38 | 39 | /* Define to 1 if you have the header file. */ 40 | #undef HAVE_UNISTD_H 41 | 42 | /* Name of package */ 43 | #undef PACKAGE 44 | 45 | /* Define to the address where bug reports for this package should be sent. */ 46 | #undef PACKAGE_BUGREPORT 47 | 48 | /* Define to the full name of this package. */ 49 | #undef PACKAGE_NAME 50 | 51 | /* Define to the full name and version of this package. */ 52 | #undef PACKAGE_STRING 53 | 54 | /* Define to the one symbol short name of this package. */ 55 | #undef PACKAGE_TARNAME 56 | 57 | /* Define to the home page for this package. */ 58 | #undef PACKAGE_URL 59 | 60 | /* Define to the version of this package. */ 61 | #undef PACKAGE_VERSION 62 | 63 | /* Define to 1 if you have the ANSI C header files. */ 64 | #undef STDC_HEADERS 65 | 66 | /* Version number of package */ 67 | #undef VERSION 68 | -------------------------------------------------------------------------------- /src/config.h.in~: -------------------------------------------------------------------------------- 1 | /* src/config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* Define to 1 if you have the `gettimeofday' function. */ 4 | #undef HAVE_GETTIMEOFDAY 5 | 6 | /* Define to 1 if you have the header file. */ 7 | #undef HAVE_INTTYPES_H 8 | 9 | /* Define to 1 if you have the `pcap' library (-lpcap). */ 10 | #undef HAVE_LIBPCAP 11 | 12 | /* Define to 1 if you have the header file. */ 13 | #undef HAVE_MEMORY_H 14 | 15 | /* Define to 1 if you have the `memset' function. */ 16 | #undef HAVE_MEMSET 17 | 18 | /* Define to 1 if you have the header file. */ 19 | #undef HAVE_STDINT_H 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #undef HAVE_STDLIB_H 23 | 24 | /* Define to 1 if you have the header file. */ 25 | #undef HAVE_STRINGS_H 26 | 27 | /* Define to 1 if you have the header file. */ 28 | #undef HAVE_STRING_H 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #undef HAVE_SYS_STAT_H 32 | 33 | /* Define to 1 if you have the header file. */ 34 | #undef HAVE_SYS_TYPES_H 35 | 36 | /* Define to 1 if you have the header file. */ 37 | #undef HAVE_UNISTD_H 38 | 39 | /* Name of package */ 40 | #undef PACKAGE 41 | 42 | /* Define to the address where bug reports for this package should be sent. */ 43 | #undef PACKAGE_BUGREPORT 44 | 45 | /* Define to the full name of this package. */ 46 | #undef PACKAGE_NAME 47 | 48 | /* Define to the full name and version of this package. */ 49 | #undef PACKAGE_STRING 50 | 51 | /* Define to the one symbol short name of this package. */ 52 | #undef PACKAGE_TARNAME 53 | 54 | /* Define to the home page for this package. */ 55 | #undef PACKAGE_URL 56 | 57 | /* Define to the version of this package. */ 58 | #undef PACKAGE_VERSION 59 | 60 | /* Define to 1 if you have the ANSI C header files. */ 61 | #undef STDC_HEADERS 62 | 63 | /* Version number of package */ 64 | #undef VERSION 65 | -------------------------------------------------------------------------------- /src/filter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * filter.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | //------------------------------------------------------------------------------ 26 | // Includes 27 | //------------------------------------------------------------------------------ 28 | 29 | #include "filter.h" 30 | #include "pcap_helpers.h" 31 | #include "printf_helpers.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include /* includes net/ethernet.h */ 37 | #include 38 | #include /* superset of previous */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | //------------------------------------------------------------------------------ 50 | // Globals 51 | //------------------------------------------------------------------------------ 52 | 53 | u_char g_buffer[MAX_SNAPLEN]; 54 | 55 | //------------------------------------------------------------------------------ 56 | // Static Functions 57 | //------------------------------------------------------------------------------ 58 | 59 | /* static */ 60 | bool FilterCriteria::convert_extract_filter(const std::string& extract_filter, 61 | std::string& output_pcap_filter) 62 | { 63 | std::istringstream iss(extract_filter); 64 | std::string token; 65 | std::vector tokens; 66 | while (std::getline(iss, token, ' ')) 67 | tokens.push_back(token); 68 | std::vector ip_port; 69 | switch (tokens.size()) { 70 | case 2: 71 | for (int i = 0; i < 2; i++) { 72 | // assume the 2 tokens are IP:port strings 73 | std::istringstream iss(tokens[i]); 74 | std::string token; 75 | while (std::getline(iss, token, ':')) 76 | ip_port.push_back(token); 77 | } 78 | if (ip_port.size() != 4) { 79 | printf_error("Expected an IP address and a port number separated by a " 80 | "colon; found: %s and %s invalid IP:port strings.\n", 81 | tokens[0].c_str(), tokens[1].c_str()); 82 | return false; 83 | } 84 | break; 85 | case 4: 86 | // ok as is 87 | ip_port = tokens; 88 | break; 89 | default: 90 | printf_error("Expected space-separated IP:port strings instead of %s\n", 91 | extract_filter.c_str()); 92 | return false; 93 | } 94 | // very basic IPv4 validation check; todo: ipv6 95 | for (int i = 0; i < 4; i += 2) { 96 | if (std::count(ip_port[i].begin(), ip_port[i].end(), '.') != 3) { 97 | printf_error("Expected a valid IPv4 address, found instead %s\n", 98 | ip_port[i].c_str()); 99 | return false; 100 | } 101 | } 102 | output_pcap_filter = "host " + ip_port[0] + " && port " + ip_port[1] + " && host " + ip_port[2] + " && port " + ip_port[3]; 103 | return true; 104 | } 105 | 106 | static bool 107 | apply_filter_on_inner_ip_frame(const Packet& pkt, unsigned int inner_ip_offset, 108 | unsigned int ipver, 109 | unsigned int len_after_inner_ip_start, 110 | const struct bpf_program* gtpu_filter) 111 | { 112 | bool tosave = false; 113 | // memset(g_buffer, 0, sizeof(g_buffer)); // not actually needed 114 | 115 | // rebuild the ethernet frame, copying the original one possibly 116 | const struct ether_header* orig_ehdr = (struct ether_header*)pkt.data(); 117 | struct ether_header* fake_ehdr = (struct ether_header*)g_buffer; 118 | memcpy(fake_ehdr, orig_ehdr, sizeof(*orig_ehdr)); 119 | 120 | switch (ipver) { 121 | case 4: 122 | fake_ehdr->ether_type = htons(ETH_P_IP); // erase any layer (like VLAN) 123 | // possibly present in orig packet 124 | break; 125 | 126 | case 6: 127 | fake_ehdr->ether_type = htons(ETH_P_IPV6); // erase any layer (like VLAN) possibly present in 128 | // orig packet 129 | break; 130 | 131 | default: 132 | assert(0); 133 | } 134 | 135 | // copy from IPv4/v6 onward: 136 | const u_char* inner_ip = pkt.data() + inner_ip_offset; 137 | u_char* fake_ip = g_buffer + sizeof(struct ether_header); 138 | memcpy(fake_ip, inner_ip, len_after_inner_ip_start); 139 | fake_ip[len_after_inner_ip_start] = 0; // put a NULL byte after last copied byte just in case 140 | 141 | // create also a fake PCAP header 142 | struct pcap_pkthdr fakehdr; 143 | memcpy(&fakehdr.ts, &pkt.header()->ts, sizeof(pkt.header()->ts)); 144 | fakehdr.caplen = fakehdr.len = sizeof(struct ether_header) + len_after_inner_ip_start; 145 | 146 | // pcap_offline_filter returns 147 | // zero if the packet doesn't match the filter and non-zero 148 | // if the packet matches the filter. 149 | int ret = pcap_offline_filter(gtpu_filter, &fakehdr, g_buffer); 150 | if (ret != 0) { 151 | tosave = true; 152 | } 153 | 154 | return tosave; 155 | } 156 | 157 | //------------------------------------------------------------------------------ 158 | // FilterCriteria 159 | //------------------------------------------------------------------------------ 160 | 161 | bool FilterCriteria::prepare_filter(const std::string& pcap_filter_str, 162 | const std::string& gtpu_filter_str, 163 | const std::string& str_filter, 164 | TcpFilterMode valid_tcp_filter) 165 | { 166 | // PCAP filter 167 | if (!pcap_filter_str.empty()) { 168 | if (pcap_compile_bpf(&m_capture_filter, pcap_filter_str.c_str()) != 0) { 169 | printf_error("Cannot parse PCAP filter: %s\n", pcap_filter_str.c_str()); 170 | return false; 171 | } 172 | 173 | m_capture_filter_set = true; 174 | printf_verbose("Successfully compiled PCAP filter: %s\n", pcap_filter_str.c_str()); 175 | } 176 | // GTPu PCAP filter 177 | if (!gtpu_filter_str.empty()) { 178 | 179 | if (pcap_compile_bpf(&m_gtpu_filter, gtpu_filter_str.c_str()) != 0) { 180 | printf_error("Cannot parse GTPu filter: %s\n", gtpu_filter_str.c_str()); 181 | return false; 182 | } 183 | 184 | m_gtpu_filter_set = true; 185 | printf_verbose("Successfully compiled GTPu PCAP filter: %s\n", gtpu_filter_str.c_str()); 186 | } 187 | // other filters: 188 | m_string_filter = str_filter; 189 | m_valid_tcp_filter_mode = valid_tcp_filter; 190 | return true; 191 | } 192 | 193 | bool FilterCriteria::is_matching(const Packet& pkt, bool* is_gtpu) // will do a logical OR of all filters set 194 | { 195 | // string-search filter: 196 | 197 | if (UNLIKELY(!m_string_filter.empty())) { 198 | unsigned int len = MIN(pkt.len(), MAX_SNAPLEN); 199 | 200 | memcpy(g_buffer, pkt.data(), len); 201 | g_buffer[len] = '\0'; 202 | 203 | void* result = memmem(g_buffer, len, m_string_filter.c_str(), m_string_filter.size()); 204 | if (result != NULL) 205 | // string was found inside the packet! 206 | return true; // useless to proceed! 207 | } 208 | 209 | // PCAP capture filter: 210 | 211 | if (UNLIKELY(m_capture_filter_set)) { 212 | int ret = pcap_offline_filter(&m_capture_filter, pkt.header(), pkt.data()); 213 | if (ret != 0) { 214 | // pcap_offline_filter returns 215 | // zero if the packet doesn't match the filter and non-zero 216 | // if the packet matches the filter. 217 | return true; // useless to proceed! 218 | } 219 | } 220 | 221 | // GTPu capture filter: 222 | 223 | if (UNLIKELY(m_gtpu_filter_set)) { 224 | // is this a GTPu packet? 225 | int offset = 0, ipver = 0, len_after_inner_ip_start = 0; 226 | ParserRetCode_t errcode = get_gtpu_inner_ip_start_offset(pkt, &offset, &ipver, &len_after_inner_ip_start, NULL); 227 | if (errcode == GPRC_VALID_PKT) { 228 | if (is_gtpu) 229 | *is_gtpu = true; 230 | 231 | m_num_gtpu_pkts++; 232 | 233 | if (offset > 0 && len_after_inner_ip_start > 0) { 234 | // run the filter only on inner/encapsulated frame: 235 | if (apply_filter_on_inner_ip_frame( 236 | pkt, offset, ipver, len_after_inner_ip_start, &m_gtpu_filter)) 237 | return true; // useless to proceed! 238 | } 239 | } 240 | } 241 | 242 | // valid-TCP-stream filter: 243 | 244 | if (UNLIKELY(m_valid_tcp_filter_mode != TCP_FILTER_NOT_ACTIVE)) { 245 | flow_hash_t hash = compute_flow_hash(pkt); 246 | if (hash != INVALID_FLOW_HASH) { 247 | flow_map_t::const_iterator entry = m_valid_tcp_firstpass_flows.find(hash); 248 | if (entry != m_valid_tcp_firstpass_flows.end()) { 249 | switch (m_valid_tcp_filter_mode) { 250 | case TCP_FILTER_CONN_HAVING_SYN: 251 | if (entry->second >= FLOW_FOUND_SYN_AND_SYNACK) 252 | return true; // this TCP packet belongs to a connection that in the 253 | // 1st pass was detected as having at least one SYN 254 | break; 255 | case TCP_FILTER_CONN_HAVING_FULL_3WAY_HANDSHAKE: 256 | if (entry->second >= FLOW_FOUND_SYN_AND_SYNACK_AND_ACK) 257 | return true; // this TCP packet belongs to a connection that in the 258 | // 1st pass was detected as having the full 3way 259 | // handshake 260 | break; 261 | case TCP_FILTER_CONN_HAVING_FULL_3WAY_HANDSHAKE_AND_DATA: 262 | if (entry->second == FLOW_FOUND_SYN_AND_SYNACK_AND_ACK_AND_DATA) 263 | return true; // this TCP packet belongs to a connection that in the 264 | // 1st pass was detected as having the full 3way 265 | // handshake 266 | break; 267 | default: 268 | assert(0); 269 | } 270 | } 271 | } 272 | } 273 | 274 | return false; 275 | } 276 | 277 | bool FilterCriteria::post_filtering(unsigned long nloaded) 278 | { 279 | if (m_gtpu_filter_set) { 280 | // in this case, the GTPu parser was run and we have a stat about how many 281 | // packets are GTPu 282 | printf_verbose( 283 | "%luM packets (%lu packets) loaded from the input PCAP are GTPu " 284 | "packets (%.1f%%).\n", 285 | m_num_gtpu_pkts / MILLION, m_num_gtpu_pkts, 286 | (double)(100.0 * (double)(m_num_gtpu_pkts) / (double)(nloaded))); 287 | } 288 | 289 | return true; // no error 290 | } 291 | -------------------------------------------------------------------------------- /src/filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * filter.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef FILTER_H_ 26 | #define FILTER_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include "large-pcap-analyzer.h" 33 | #include "parse.h" 34 | 35 | #include 36 | 37 | //------------------------------------------------------------------------------ 38 | // Constants 39 | //------------------------------------------------------------------------------ 40 | 41 | //------------------------------------------------------------------------------ 42 | // Types 43 | //------------------------------------------------------------------------------ 44 | 45 | typedef enum { 46 | TCP_FILTER_NOT_ACTIVE, 47 | TCP_FILTER_CONN_HAVING_SYN, 48 | TCP_FILTER_CONN_HAVING_FULL_3WAY_HANDSHAKE, 49 | TCP_FILTER_CONN_HAVING_FULL_3WAY_HANDSHAKE_AND_DATA, 50 | } TcpFilterMode; 51 | 52 | //------------------------------------------------------------------------------ 53 | // FilterCriteria 54 | // Implements all possible filtering mechanisms supported by LPA to decide 55 | // which packets need to be processed/analyzed 56 | //------------------------------------------------------------------------------ 57 | 58 | class FilterCriteria { 59 | public: 60 | FilterCriteria() 61 | { 62 | memset(&m_capture_filter, 0, sizeof(m_capture_filter)); 63 | memset(&m_gtpu_filter, 0, sizeof(m_gtpu_filter)); 64 | m_capture_filter_set = false; 65 | m_gtpu_filter_set = false; 66 | m_valid_tcp_filter_mode = TCP_FILTER_NOT_ACTIVE; 67 | m_num_gtpu_pkts = 0; 68 | } 69 | 70 | ~FilterCriteria() 71 | { 72 | if (m_capture_filter_set) 73 | pcap_freecode(&m_capture_filter); 74 | if (m_gtpu_filter_set) 75 | pcap_freecode(&m_gtpu_filter); 76 | } 77 | 78 | bool prepare_filter(const std::string& pcap_filter_str, 79 | const std::string& gtpu_filter_str, 80 | const std::string& str_filter, 81 | TcpFilterMode valid_tcp_filter); 82 | 83 | static bool convert_extract_filter(const std::string& extract_filter, 84 | std::string& output_pcap_filter); 85 | 86 | // getters 87 | 88 | bool is_some_filter_active() const 89 | { 90 | return (m_capture_filter_set || m_gtpu_filter_set || !m_string_filter.empty() || m_valid_tcp_filter_mode != TCP_FILTER_NOT_ACTIVE); 91 | } 92 | 93 | bool is_capture_filter_set() const { return m_capture_filter_set; } 94 | bool is_gtpu_filter_set() const { return m_gtpu_filter_set; } 95 | 96 | // some filtering criteria will be able to decide correctly if a packet is matching/not-matching the filter 97 | // only by running 2 passes on each input PCAP file 98 | bool needs_2passes() const 99 | { 100 | return m_valid_tcp_filter_mode != TCP_FILTER_NOT_ACTIVE; 101 | } 102 | 103 | flow_map_t& flow_map() { return m_valid_tcp_firstpass_flows; } 104 | 105 | // main API 106 | bool is_matching(const Packet& pkt, bool* is_gtpu); 107 | 108 | bool post_filtering(unsigned long nloaded); 109 | 110 | private: // filter configuration 111 | // BPF filtering on the outer frame: 112 | struct bpf_program m_capture_filter; 113 | bool m_capture_filter_set; 114 | 115 | // BPF filtering on the inner frame of GTPu packets: 116 | struct bpf_program m_gtpu_filter; 117 | bool m_gtpu_filter_set; 118 | 119 | // text/binary search in packet: 120 | std::string m_string_filter; 121 | 122 | // TCP filtering: 123 | TcpFilterMode m_valid_tcp_filter_mode; 124 | 125 | private: // filter status 126 | unsigned long m_num_gtpu_pkts; 127 | flow_map_t m_valid_tcp_firstpass_flows; // contains the result of the 1st pass 128 | }; 129 | 130 | #endif // FILTER_H_ 131 | -------------------------------------------------------------------------------- /src/hash_algo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * hash_algo.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | //------------------------------------------------------------------------------ 26 | // Includes 27 | //------------------------------------------------------------------------------ 28 | 29 | #include "hash_algo.h" 30 | 31 | //------------------------------------------------------------------------------ 32 | // Constants 33 | //------------------------------------------------------------------------------ 34 | 35 | #if defined(__GNUC__) && __GNUC__ >= 7 36 | #define GCC_ALLOW_FALLTHROUGH __attribute__((fallthrough)) 37 | #else 38 | #define GCC_ALLOW_FALLTHROUGH /* empty macro */ 39 | #endif 40 | 41 | //------------------------------------------------------------------------------ 42 | // Hash Functions 43 | //------------------------------------------------------------------------------ 44 | 45 | // Compression function for Merkle-Damgard construction. 46 | #define fasthash64_mix(h) \ 47 | ({ \ 48 | (h) ^= (h) >> 23; \ 49 | (h) *= 0x2127599bf4325c37ULL; \ 50 | (h) ^= (h) >> 47; \ 51 | }) 52 | 53 | uint64_t FastHash64(const char* buf, uint32_t len, uint64_t seed) 54 | { 55 | const uint64_t m = 0x880355f21e6d1965ULL; 56 | const uint64_t* pos = (const uint64_t*)buf; 57 | const uint64_t* end = pos + (len / 8); 58 | const unsigned char* pos2; 59 | uint64_t h = seed ^ (len * m); 60 | uint64_t v; 61 | 62 | while (pos != end) { 63 | v = *pos++; 64 | h ^= fasthash64_mix(v); 65 | h *= m; 66 | } 67 | 68 | pos2 = (const unsigned char*)pos; 69 | v = 0; 70 | 71 | switch (len & 7) { 72 | case 7: 73 | v ^= (uint64_t)pos2[6] << 48; 74 | GCC_ALLOW_FALLTHROUGH; 75 | case 6: 76 | v ^= (uint64_t)pos2[5] << 40; 77 | GCC_ALLOW_FALLTHROUGH; 78 | case 5: 79 | v ^= (uint64_t)pos2[4] << 32; 80 | GCC_ALLOW_FALLTHROUGH; 81 | case 4: 82 | v ^= (uint64_t)pos2[3] << 24; 83 | GCC_ALLOW_FALLTHROUGH; 84 | case 3: 85 | v ^= (uint64_t)pos2[2] << 16; 86 | GCC_ALLOW_FALLTHROUGH; 87 | case 2: 88 | v ^= (uint64_t)pos2[1] << 8; 89 | GCC_ALLOW_FALLTHROUGH; 90 | case 1: 91 | v ^= (uint64_t)pos2[0]; 92 | h ^= fasthash64_mix(v); 93 | h *= m; 94 | break; 95 | } 96 | 97 | return fasthash64_mix(h); 98 | } 99 | -------------------------------------------------------------------------------- /src/hash_algo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * hash_algo.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef HASH_ALGO_H_ 26 | #define HASH_ALGO_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include 33 | 34 | //------------------------------------------------------------------------------ 35 | // Constants 36 | //------------------------------------------------------------------------------ 37 | 38 | //------------------------------------------------------------------------------ 39 | // Types 40 | //------------------------------------------------------------------------------ 41 | 42 | //------------------------------------------------------------------------------ 43 | // Hash algorithm functions 44 | //------------------------------------------------------------------------------ 45 | 46 | uint64_t FastHash64(const char* buf, uint32_t len, uint64_t seed); 47 | 48 | #endif // HASH_ALGO_H_ 49 | -------------------------------------------------------------------------------- /src/ipaddress.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ipaddress.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #pragma once 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #include "hash_algo.h" 33 | 34 | #define IPV6_LEN (16) 35 | 36 | //--------------------------------------------------------------------------- 37 | // IpAddress 38 | // A simple class that represents either an IPv4 or IPv6 address 39 | //--------------------------------------------------------------------------- 40 | class IpAddress { 41 | public: 42 | IpAddress() 43 | { 44 | addrFamily = 0; 45 | } 46 | 47 | IpAddress(const struct in_addr& ipv4) 48 | { 49 | addrFamily = AF_INET; 50 | addr.ipv4 = ipv4; 51 | } 52 | IpAddress(const struct in6_addr& ipv6) 53 | { 54 | addrFamily = AF_INET6; 55 | addr.ipv6 = ipv6; 56 | } 57 | 58 | // ---------------------------------------------------------------------- 59 | // IPv4 or IPv6 hashing 60 | // ---------------------------------------------------------------------- 61 | 62 | uint64_t get_hash() const 63 | { 64 | switch (addrFamily) { 65 | case AF_INET: 66 | return FastHash64((const char*)&addr.ipv4.s_addr, sizeof(addr.ipv4.s_addr), 0); 67 | case AF_INET6: 68 | return FastHash64((const char*)&addr.ipv4.s_addr, IPV6_LEN, 0); 69 | 70 | default: 71 | return 0; 72 | } 73 | } 74 | 75 | // ---------------------------------------------------------------------- 76 | // String conversion 77 | // ---------------------------------------------------------------------- 78 | 79 | std::string toString() const 80 | { 81 | if (addrFamily == 0) 82 | return ""; 83 | 84 | const size_t bufOutLen = 128; 85 | char bufOut[bufOutLen + 1]; 86 | 87 | switch (addrFamily) { 88 | case AF_INET: 89 | return ipv4ul_to_string(ntohl(addr.ipv4.s_addr), bufOut, bufOutLen); 90 | 91 | case AF_INET6: 92 | if (inet_ntop(addrFamily, (const void*)&addr, bufOut, bufOutLen) == NULL) 93 | return ""; 94 | return std::string(bufOut); 95 | 96 | default: 97 | bufOut[0] = 0; 98 | return bufOut; 99 | } 100 | } 101 | 102 | private: 103 | // Convert IPv4 stored as 32bits in HOST order to a string. 104 | static inline char* ipv4ul_to_string(uint32_t addr, char* buf, size_t bufLen) 105 | { 106 | char *cp, *retStr; 107 | uint32_t byte; 108 | int n; 109 | cp = &buf[bufLen]; 110 | *--cp = '\0'; 111 | n = 4; 112 | do { 113 | byte = addr & 0xff; 114 | *--cp = byte % 10 + '0'; 115 | byte /= 10; 116 | if (byte > 0) { 117 | *--cp = byte % 10 + '0'; 118 | byte /= 10; 119 | if (byte > 0) 120 | *--cp = byte + '0'; 121 | } 122 | *--cp = '.'; 123 | addr >>= 8; 124 | } while (--n > 0); 125 | /* Convert the string to lowercase */ 126 | retStr = (char*)((cp + 1)); 127 | return (retStr); 128 | } 129 | 130 | private: 131 | uint8_t addrFamily; // AF_INET or AF_INET6 132 | union { 133 | // NOTE: both these structures and the POSIX APIs that manipulate them will store the IP 134 | // addresses in NETWORK order: 135 | struct in6_addr ipv6; // this is 16 bytes long 136 | struct in_addr ipv4; // this is 4 bytes long 137 | } addr; 138 | }; 139 | -------------------------------------------------------------------------------- /src/large-pcap-analyzer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * large-pcap-analyzer.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef LPA_H_ 26 | #define LPA_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Constants 30 | //------------------------------------------------------------------------------ 31 | 32 | #define KB (1024) 33 | #define MB (1024 * 1024) 34 | #define GB (1024 * 1024 * 1024) 35 | #define MILLION (1000000) 36 | #define SMALL_NUM (0.000001) // 1us 37 | #define MAX_SNAPLEN (65535) 38 | #define INVALID_FLOW_HASH (0) 39 | 40 | #if !defined(PCAP_NETMASK_UNKNOWN) 41 | /* 42 | * Value to pass to pcap_compile() as the netmask if you don't know what 43 | * the netmask is. 44 | */ 45 | #define PCAP_NETMASK_UNKNOWN 0xffffffff 46 | #endif 47 | 48 | #ifndef MIN 49 | #define MIN(x, y) ((x) > (y) ? (y) : (x)) 50 | #endif /*MIN*/ 51 | 52 | #define LIKELY(x) __builtin_expect((x), 1) 53 | #define UNLIKELY(x) __builtin_expect((x), 0) 54 | 55 | //------------------------------------------------------------------------------ 56 | // App configuration 57 | //------------------------------------------------------------------------------ 58 | 59 | class LPAConfig { 60 | public: 61 | bool m_verbose = false; 62 | bool m_quiet = false; 63 | bool m_parsing_stats = false; 64 | 65 | // technically this is not a configuration but the status of the application... 66 | // but I'm lazy and didn't create a separate global class just for this: 67 | bool m_termination_requested = false; 68 | }; 69 | 70 | extern LPAConfig g_config; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * packet.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef LPA_PACKET_H_ 26 | #define LPA_PACKET_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #ifndef _GNU_SOURCE 33 | #define _GNU_SOURCE // to have memmem 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | // libpcap dependency: 42 | // NOTE: in version 1.7.x there is no pcap/pcap.h, just a pcap.h apparently: 43 | //#include 44 | #include 45 | 46 | #include "config.h" 47 | 48 | //------------------------------------------------------------------------------ 49 | // Time macros 50 | //------------------------------------------------------------------------------ 51 | 52 | #define SEC_TO_NSEC(x) (x * (1E9)) 53 | #define SEC_TO_USEC(x) (x * (1E6)) 54 | 55 | #define NSEC_TO_SEC(x) (x / (1E9)) 56 | #define NSEC_TO_USEC(x) (x / (1E6)) 57 | 58 | #define USEC_TO_SEC(x) (x / (1E6)) 59 | 60 | //------------------------------------------------------------------------------ 61 | // Protocol Constants 62 | //------------------------------------------------------------------------------ 63 | 64 | #define VLAN_VID_MASK (0x0FFF) 65 | #define ETHERTYPE_IS_VLAN(x) \ 66 | ((x) == ETH_P_8021Q || (x) == 0x9100 /*qinq*/ || (x) == 0x88A8 /*802.1 ad*/) 67 | 68 | // stuff coming from http://lxr.free-electrons.com/source/include/net/gtp.h 69 | 70 | /* General GTP protocol related definitions. */ 71 | #define GTP1U_PORT 2152 72 | #define GTP_TPDU 255 73 | #define GTP1_F_NPDU 0x01 74 | #define GTP1_F_SEQ 0x02 75 | #define GTP1_F_EXTHDR 0x04 76 | #define GTP1_F_MASK 0x07 77 | 78 | // VLAN frame definition: 79 | 80 | typedef struct { 81 | uint16_t vlanId; // in NETWORK order 82 | uint16_t protoType; 83 | } __attribute__((packed)) ether80211q_t; 84 | 85 | // GTPv1 frame definition: 86 | // stuff coming from http://lxr.free-electrons.com/source/include/net/gtp.h 87 | 88 | struct gtp1_header { /* According to 3GPP TS 29.060. */ 89 | __u8 flags; 90 | __u8 type; 91 | __be16 length; 92 | __be32 tid; 93 | } __attribute__((packed)); 94 | 95 | /* 96 | * Header repeated only once just after IP frame 97 | */ 98 | typedef struct sctphdr { 99 | uint16_t source; 100 | uint16_t dest; 101 | uint32_t vtag; 102 | uint32_t checksum; 103 | } __attribute__((packed)) sctp_sctphdr_t; // 12B 104 | 105 | //------------------------------------------------------------------------------ 106 | // Packet 107 | //------------------------------------------------------------------------------ 108 | 109 | class Packet { 110 | public: 111 | Packet(struct pcap_pkthdr* hdr = NULL, const u_char* data = NULL) 112 | { 113 | m_pcap_header = hdr; 114 | m_pcap_packet = data; 115 | m_pcaplib_owns_data = true; 116 | } 117 | 118 | ~Packet() { destroy(); } 119 | 120 | void destroy() 121 | { 122 | if (m_pcaplib_owns_data) { 123 | // libpcap owns this packet, just reset pointers and leave 124 | // memory deallocation to libpcap 125 | m_pcap_header = NULL; 126 | m_pcap_packet = NULL; 127 | } else { 128 | if (m_pcap_header) 129 | free(m_pcap_header); 130 | 131 | if (m_pcap_packet) 132 | free(const_cast(m_pcap_packet)); 133 | 134 | m_pcap_header = NULL; 135 | m_pcap_packet = NULL; 136 | } 137 | } 138 | 139 | void copy(const struct pcap_pkthdr* hdr, const u_char* data) 140 | { 141 | destroy(); // just in case 142 | 143 | m_pcaplib_owns_data = false; 144 | m_pcap_header = (struct pcap_pkthdr*)malloc(sizeof(struct pcap_pkthdr) + 1); 145 | m_pcap_packet = (u_char*)malloc(hdr->caplen + 1); 146 | 147 | memcpy(m_pcap_header, hdr, sizeof(struct pcap_pkthdr)); 148 | memcpy(const_cast(m_pcap_packet), data, hdr->caplen); 149 | } 150 | 151 | size_t len() const 152 | { 153 | if (m_pcap_header) 154 | return m_pcap_header->caplen; 155 | else 156 | return 0; 157 | } 158 | 159 | const struct pcap_pkthdr* header() const { return m_pcap_header; } 160 | const u_char* data() const { return m_pcap_packet; } 161 | 162 | static double pcap_timestamp_to_seconds(struct timeval* ts) 163 | { 164 | // for some reason the 'struct timeval' is using SIGNED integers, at least on Linux x86_64: 165 | if (ts->tv_sec < 0 || ts->tv_usec < 0) 166 | return 0; 167 | 168 | return (double)ts->tv_sec + USEC_TO_SEC((double)ts->tv_usec); 169 | } 170 | static double pcap_timestamp_to_seconds(struct pcap_pkthdr* pcap_header) 171 | { 172 | return pcap_timestamp_to_seconds(&pcap_header->ts); 173 | } 174 | static bool pcap_timestamp_is_valid(struct pcap_pkthdr* pcap_header) 175 | { 176 | return pcap_header->ts.tv_sec + pcap_header->ts.tv_usec > 0; 177 | } 178 | 179 | double pcap_timestamp_to_seconds() const 180 | { 181 | return pcap_timestamp_to_seconds(&m_pcap_header->ts); 182 | } 183 | 184 | void set_timestamp_from_seconds(double ts) 185 | { 186 | m_pcap_header->ts.tv_sec = (time_t)ts; 187 | m_pcap_header->ts.tv_usec = SEC_TO_USEC(ts) - SEC_TO_USEC(m_pcap_header->ts.tv_sec); 188 | 189 | /* 190 | Beware: ts_usec value shouldn't reach 1 second (in regular pcap files 1 000 000; in nanosecond-resolution files, 1 000 000 000); 191 | in this case ts_sec must be increased instead! 192 | See https://wiki.wireshark.org/Development/LibpcapFileFormat 193 | 194 | Also note that large_pcap_analyzer is using libpcap to write PCAP files and libpcap provides no way to produce 195 | a nanosecond-resolution file... so we always store MICROSECONDs inside the timeval "ts": 196 | */ 197 | while (m_pcap_header->ts.tv_usec > 1000000) { 198 | m_pcap_header->ts.tv_sec++; 199 | m_pcap_header->ts.tv_usec -= 1000000; 200 | } 201 | } 202 | 203 | private: 204 | struct pcap_pkthdr* m_pcap_header; 205 | const u_char* m_pcap_packet; 206 | bool m_pcaplib_owns_data; 207 | }; 208 | 209 | #endif // LPA_PACKET_H_ 210 | -------------------------------------------------------------------------------- /src/parse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * parse.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | //------------------------------------------------------------------------------ 26 | // Includes 27 | //------------------------------------------------------------------------------ 28 | 29 | #include "parse.h" 30 | #include "hash_algo.h" // ???? needed 31 | 32 | #include /* includes net/ethernet.h */ 33 | #include 34 | #include /* superset of previous */ 35 | #include 36 | 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | //------------------------------------------------------------------------------ 44 | // Static Functions 45 | //------------------------------------------------------------------------------ 46 | 47 | static ParserRetCode_t do_ip_layer_parse(const Packet& pkt, int offset, 48 | int* offsetOut, int* ipver, 49 | int* len_after_ip_start, 50 | FlowInfo* info) 51 | { 52 | // parse IPv4/v6 layer 53 | 54 | const struct ip* ip = (const struct ip*)(pkt.data() + offset); 55 | const struct ip6_hdr* ip6 = (const struct ip6_hdr*)(pkt.data() + offset); 56 | 57 | uint16_t ip_total_len = 0; 58 | uint16_t ip_hdr_len = 0; 59 | switch (ip->ip_v) { 60 | case 4: 61 | if (UNLIKELY(pkt.len() < (offset + sizeof(struct ip)))) 62 | return GPRC_TOO_SHORT_PKT; // Packet too short 63 | 64 | ip_total_len = ntohs(ip->ip_len); // IMPORTANT: in IPv4 the "ip_len" does include the size of the IPv4 header! 65 | ip_hdr_len = (uint16_t)(ip->ip_hl) * 4; 66 | if (info) { 67 | // store IP addresses 68 | info->m_ip_src = ip->ip_src; 69 | info->m_ip_dst = ip->ip_dst; 70 | } 71 | break; 72 | 73 | case 6: 74 | if (UNLIKELY(pkt.len() < (offset + sizeof(struct ip6_hdr)))) 75 | return GPRC_TOO_SHORT_PKT; // Packet too short 76 | 77 | ip_total_len = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); // IMPORTANT: in IPv6 the "ip6_un1_plen" field 78 | // does not include the size of the IPv6 header! 79 | ip_hdr_len = sizeof(struct ip6_hdr); 80 | if (info) { 81 | // store IP addresses 82 | info->m_ip_src = ip6->ip6_src; 83 | info->m_ip_dst = ip6->ip6_dst; 84 | } 85 | break; 86 | 87 | default: 88 | return GPRC_INVALID_PKT; // wrong packet 89 | } 90 | 91 | if (UNLIKELY(ip_hdr_len >= ip_total_len)) 92 | return GPRC_INVALID_PKT; // wrong packet 93 | 94 | // ok, found the offset of IPv4/IPv6 layer 95 | 96 | if (offsetOut) 97 | *offsetOut = offset; 98 | if (ipver) 99 | *ipver = ip->ip_v; 100 | if (len_after_ip_start) 101 | *len_after_ip_start = ip_total_len; 102 | 103 | return GPRC_VALID_PKT; 104 | } 105 | 106 | static ParserRetCode_t 107 | do_transport_layer_parse(const Packet& pkt, int ipStartOffset, int ipver, 108 | int len_after_ip_start, int* offsetTransportOut, 109 | int* ipprotOut, int* len_after_transport_start, 110 | FlowInfo* info) 111 | { 112 | const struct ip* ip = NULL; 113 | struct ip6_hdr* ipv6 = NULL; 114 | int ip_proto = 0; 115 | uint16_t ip_hdr_len = 0; 116 | 117 | if (ipver == 4) { 118 | ip = (const struct ip*)(pkt.data() + ipStartOffset); 119 | ip_proto = ip->ip_p; 120 | ip_hdr_len = (uint16_t)(ip->ip_hl) * 4; 121 | } else { 122 | ipv6 = (struct ip6_hdr*)(pkt.data() + ipStartOffset); 123 | ip_proto = ipv6->ip6_ctlun.ip6_un1.ip6_un1_nxt; 124 | ip_hdr_len = sizeof(struct ip6_hdr); // fixed size 125 | } 126 | unsigned int transportStartOffset = ipStartOffset + ip_hdr_len; 127 | 128 | switch (ip_proto) { 129 | case IPPROTO_TCP: 130 | // check there are enough bytes remaining 131 | if (UNLIKELY(pkt.len() < (transportStartOffset + sizeof(struct tcphdr)))) 132 | return GPRC_TOO_SHORT_PKT; // Packet too short 133 | if (info) { 134 | const struct tcphdr* tcp = (const struct tcphdr*)(pkt.data() + transportStartOffset); 135 | // store port numbers 136 | info->m_port_src = ntohs(tcp->source); 137 | info->m_port_dst = ntohs(tcp->dest); 138 | } 139 | break; 140 | case IPPROTO_UDP: 141 | // check there are enough bytes remaining 142 | if (UNLIKELY(pkt.len() < (transportStartOffset + sizeof(struct udphdr)))) 143 | return GPRC_TOO_SHORT_PKT; // Packet too short 144 | if (info) { 145 | const struct udphdr* udp = (const struct udphdr*)(pkt.data() + transportStartOffset); 146 | // store port numbers 147 | info->m_port_src = ntohs(udp->source); 148 | info->m_port_dst = ntohs(udp->dest); 149 | } 150 | break; 151 | case IPPROTO_SCTP: 152 | // check there are enough bytes remaining 153 | if (UNLIKELY(pkt.len() < (transportStartOffset + sizeof(struct sctphdr)))) 154 | return GPRC_TOO_SHORT_PKT; // Packet too short 155 | if (info) { 156 | const struct sctphdr* sctp = (const struct sctphdr*)(pkt.data() + transportStartOffset); 157 | // store port numbers 158 | info->m_port_src = ntohs(sctp->source); 159 | info->m_port_dst = ntohs(sctp->dest); 160 | } 161 | break; 162 | default: 163 | break; 164 | } 165 | 166 | // ok, found the ipStartOffset for a valid UDP/TCP layer 167 | 168 | if (offsetTransportOut) 169 | *offsetTransportOut = transportStartOffset; 170 | if (ipprotOut) 171 | *ipprotOut = ip_proto; 172 | if (len_after_transport_start) 173 | *len_after_transport_start = len_after_ip_start - ip_hdr_len; 174 | if (info) 175 | info->m_ip_proto = ip_proto; 176 | return GPRC_VALID_PKT; 177 | } 178 | 179 | //------------------------------------------------------------------------------ 180 | // Global Functions 181 | //------------------------------------------------------------------------------ 182 | 183 | ParserRetCode_t get_ip_start_offset(const Packet& pkt, int* offsetOut, 184 | int* ipver, int* len_after_ip_start, 185 | FlowInfo* info) 186 | { 187 | unsigned int offset = 0; 188 | 189 | // parse Ethernet layer 190 | 191 | if (UNLIKELY(pkt.len() < sizeof(struct ether_header))) 192 | return GPRC_TOO_SHORT_PKT; // Packet too short 193 | 194 | const struct ether_header* ehdr = (const struct ether_header*)pkt.data(); 195 | uint16_t eth_type = ntohs(ehdr->ether_type); 196 | offset = sizeof(struct ether_header); 197 | 198 | // parse VLAN tags 199 | 200 | while (ETHERTYPE_IS_VLAN(eth_type) && offset < pkt.len()) { 201 | const ether80211q_t* qType = (const ether80211q_t*)(pkt.data() + offset); 202 | eth_type = ntohs(qType->protoType); 203 | offset += sizeof(ether80211q_t); 204 | } 205 | 206 | if (UNLIKELY(eth_type != ETH_P_IP && eth_type != ETH_P_IPV6)) 207 | return GPRC_UNKNOWN_ETHERTYPE; // not supported at this time 208 | 209 | // parse IPv4/v6 layer 210 | 211 | return do_ip_layer_parse(pkt, offset, offsetOut, ipver, len_after_ip_start, info); 212 | } 213 | 214 | ParserRetCode_t get_transport_start_offset(const Packet& pkt, 215 | int* offsetTransportOut, 216 | int* ipprotOut, 217 | int* len_after_transport_start, 218 | FlowInfo* info) 219 | { 220 | int ipStartOffset = 0, ipver = 0, len_after_ip_start = 0; 221 | ParserRetCode_t ret = get_ip_start_offset(pkt, &ipStartOffset, &ipver, &len_after_ip_start, info); 222 | if (UNLIKELY(ret != GPRC_VALID_PKT)) 223 | return ret; 224 | 225 | return do_transport_layer_parse(pkt, ipStartOffset, ipver, len_after_ip_start, offsetTransportOut, 226 | ipprotOut, len_after_transport_start, info); 227 | } 228 | 229 | //------------------------------------------------------------------------------ 230 | // Global Functions - GTPu parsing 231 | //------------------------------------------------------------------------------ 232 | 233 | ParserRetCode_t get_gtpu_inner_ip_start_offset(const Packet& pkt, 234 | int* offsetIpInner, int* ipver, 235 | int* remainingLen, 236 | FlowInfo* info) 237 | { 238 | int offset = 0, ip_prot = 0; 239 | ParserRetCode_t ret = get_transport_start_offset(pkt, &offset, &ip_prot, NULL, NULL); 240 | if (UNLIKELY(ret != GPRC_VALID_PKT)) 241 | return ret; 242 | if (UNLIKELY(ip_prot != IPPROTO_UDP)) 243 | return GPRC_NOT_GTPU_PKT; // not a GTPu packet, all GTPu packets go over UDP 244 | 245 | // parse UDP layer 246 | 247 | const struct udphdr* udp = (const struct udphdr*)(pkt.data() + offset); 248 | if (udp->source != htons(GTP1U_PORT) && udp->dest != htons(GTP1U_PORT)) 249 | return GPRC_NOT_GTPU_PKT; // not a GTPu packet 250 | 251 | offset += sizeof(struct udphdr); 252 | 253 | // parse GTPu layer 254 | 255 | if (UNLIKELY(pkt.len() < (offset + sizeof(struct gtp1_header)))) 256 | return GPRC_TOO_SHORT_PKT; // Packet too short 257 | 258 | const struct gtp1_header* gtpu = (const struct gtp1_header*)(pkt.data() + offset); 259 | 260 | // check for gtp-u message (type = 0xff) and if it's a gtp release 1 261 | if (UNLIKELY((gtpu->flags & 0xf0) != 0x30)) 262 | return GPRC_NOT_GTPU_PKT; // not a GTPu packet 263 | if (UNLIKELY(gtpu->type != GTP_TPDU)) 264 | return GPRC_NOT_GTPU_PKT; // not a GTPu packet 265 | 266 | offset += sizeof(struct gtp1_header); 267 | const u_char* gtp_start = pkt.data() + offset; 268 | const u_char* gtp_payload = pkt.data() + offset; 269 | 270 | // check for sequence number field and NPDU field 271 | if ((gtpu->flags & (GTP1_F_NPDU | GTP1_F_SEQ)) != 0) 272 | offset += 4; 273 | 274 | // parse the extension bit 275 | if ((gtpu->flags & GTP1_F_EXTHDR) != 0) { 276 | // skip all extensions present 277 | uint16_t ext_type; 278 | do { 279 | uint16_t word = *((uint16_t*)gtp_payload); 280 | gtp_payload += 2; 281 | 282 | uint16_t ext_size = (word & 0xff00) >> 8; 283 | if (ext_size != 0) { 284 | uint16_t i; 285 | 286 | ext_size = (ext_size << 1) - 2; 287 | for (i = 0; i < ext_size; i++) { 288 | gtp_payload += 2; 289 | } 290 | 291 | uint16_t word = *((uint16_t*)gtp_payload); 292 | gtp_payload += 2; 293 | 294 | ext_type = (word & 0x00ff); 295 | } else { 296 | ext_type = 0; 297 | } 298 | } while (ext_type != 0); 299 | } 300 | 301 | offset += (gtp_payload - gtp_start); 302 | 303 | // check that a valid IPv4 layer is following: 304 | return do_ip_layer_parse(pkt, offset, offsetIpInner, ipver, remainingLen, info); 305 | } 306 | 307 | ParserRetCode_t get_gtpu_inner_transport_start_offset(const Packet& pkt, 308 | int* offsetTransportInner, 309 | int* ipprotInner, 310 | int* remainingLen, 311 | FlowInfo* info) 312 | { 313 | int ipStartOffset = 0, ipver = 0, len_after_ip_start = 0; 314 | ParserRetCode_t ret = get_gtpu_inner_ip_start_offset(pkt, &ipStartOffset, &ipver, &len_after_ip_start, info); 315 | if (UNLIKELY(ret != GPRC_VALID_PKT)) 316 | return ret; 317 | 318 | return do_transport_layer_parse(pkt, ipStartOffset, ipver, len_after_ip_start, 319 | offsetTransportInner, ipprotInner, remainingLen, info); 320 | } 321 | 322 | //------------------------------------------------------------------------------ 323 | // Global Functions - parsing stats 324 | //------------------------------------------------------------------------------ 325 | 326 | void update_parsing_stats(const Packet& pkt, ParsingStats& outstats) 327 | { 328 | ParserRetCode_t ret; 329 | 330 | // increment the total: it will be used to compute percentages later 331 | outstats.pkts_total++; 332 | 333 | // TODO: this way to do the stats is pretty much non-efficient: 334 | 335 | ret = get_gtpu_inner_transport_start_offset(pkt, NULL, NULL, NULL, NULL); 336 | if (ret == GPRC_VALID_PKT) { 337 | outstats.pkts_valid_gtpu_transport++; 338 | return; 339 | } 340 | // else: try to parse inner layers 341 | 342 | ret = get_gtpu_inner_ip_start_offset(pkt, NULL, NULL, NULL, NULL); 343 | if (ret == GPRC_VALID_PKT) { 344 | outstats.pkts_valid_gtpu_ip++; 345 | return; 346 | } 347 | // else: try to parse inner layers 348 | 349 | ret = get_transport_start_offset(pkt, NULL, NULL, NULL, NULL); 350 | if (ret == GPRC_VALID_PKT) { 351 | outstats.pkts_valid_tranport++; 352 | return; 353 | } 354 | // else: try to parse inner layers 355 | 356 | ret = get_ip_start_offset(pkt, NULL, NULL, NULL, NULL); 357 | if (ret == GPRC_VALID_PKT) { 358 | outstats.pkts_valid_ip++; 359 | return; 360 | } 361 | // else: try to parse inner layers 362 | 363 | outstats.pkts_invalid++; 364 | } 365 | 366 | //------------------------------------------------------------------------------ 367 | // Global Functions - flow hashing 368 | //------------------------------------------------------------------------------ 369 | 370 | flow_hash_t compute_flow_hash(const Packet& pkt) 371 | { 372 | ParserRetCode_t ret; 373 | FlowInfo flow_info; 374 | int /* offsetIp = 0,*/ offsetTransport = 0, ip_prot = 0 /*, ipver = 0*/; 375 | 376 | // detect if this is an encapsulated packet or not 377 | ret = get_gtpu_inner_transport_start_offset(pkt, &offsetTransport, &ip_prot, NULL, &flow_info); 378 | if (ret == GPRC_VALID_PKT) { 379 | // this is a GTPu-tunnelled packet: use the extracted information to 380 | // compute the flow hash of the INNER frame: 381 | return flow_info.compute_flow_hash(); 382 | } else { 383 | // this is not a GTPu packet... try parsing it as a non-encapsulated packet: 384 | ret = get_transport_start_offset(pkt, &offsetTransport, &ip_prot, NULL, &flow_info); 385 | if (ret == GPRC_VALID_PKT) { 386 | return flow_info.compute_flow_hash(); 387 | } 388 | } 389 | 390 | return INVALID_FLOW_HASH; // looks like an invalid packet 391 | } 392 | 393 | //------------------------------------------------------------------------------ 394 | // FlowInfo 395 | //------------------------------------------------------------------------------ 396 | 397 | flow_hash_t FlowInfo::compute_flow_hash() 398 | { 399 | /* 400 | IMPORTANT: the flow hash must be the same for both uplink & downlink packets that belong to the same flow; 401 | in other words it needs to have commutative property w.r.t. src/dst fields; for this reason 402 | the hash is computed as the SUM of src hash and dst hash 403 | */ 404 | flow_hash_t ip_hash = m_ip_src.get_hash() + m_ip_dst.get_hash(); 405 | flow_hash_t port_hash = // fn 406 | FastHash64((const char*)&m_port_src, sizeof(m_port_src), 0) + // fn 407 | FastHash64((const char*)&m_port_dst, sizeof(m_port_dst), 0); 408 | m_hash = ip_hash + port_hash; 409 | 410 | return m_hash; 411 | } 412 | -------------------------------------------------------------------------------- /src/parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * parse.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef PARSE_H_ 26 | #define PARSE_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include "ipaddress.h" 33 | #include "large-pcap-analyzer.h" 34 | #include "packet.h" 35 | #include 36 | 37 | //------------------------------------------------------------------------------ 38 | // Constants 39 | //------------------------------------------------------------------------------ 40 | 41 | //------------------------------------------------------------------------------ 42 | // Types 43 | //------------------------------------------------------------------------------ 44 | 45 | typedef enum { 46 | GPRC_VALID_PKT = 0, 47 | 48 | GPRC_UNKNOWN_ETHERTYPE = -1, 49 | GPRC_NOT_GTPU_PKT = -2, 50 | GPRC_TOO_SHORT_PKT = -3, 51 | GPRC_INVALID_PKT = -4, 52 | } ParserRetCode_t; 53 | 54 | typedef enum { 55 | FLOW_FOUND = 1, 56 | FLOW_FOUND_SYN, 57 | FLOW_FOUND_SYN_AND_SYNACK, 58 | FLOW_FOUND_SYN_AND_SYNACK_AND_ACK, 59 | FLOW_FOUND_SYN_AND_SYNACK_AND_ACK_AND_DATA, 60 | } TcpFlowStatus_t; 61 | 62 | typedef uint64_t flow_hash_t; // init to INVALID_FLOW_HASH 63 | #if __cplusplus <= 199711L 64 | // no C++11 support 65 | #include 66 | typedef std::map flow_map_t; 67 | #else 68 | // C++11 support available 69 | #include 70 | typedef std::unordered_map flow_map_t; 71 | #endif 72 | 73 | class ParsingStats { 74 | public: 75 | ParsingStats() 76 | { 77 | pkts_valid_gtpu_transport = 0; 78 | pkts_valid_gtpu_ip = 0; 79 | pkts_valid_tranport = 0; 80 | pkts_valid_ip = 0; 81 | pkts_invalid = 0; 82 | pkts_total = 0; 83 | } 84 | 85 | double perc_pkts_valid_gtpu_transport() const 86 | { 87 | return 100.0 * double(pkts_valid_gtpu_transport) / double(pkts_total); 88 | } 89 | double perc_pkts_valid_gtpu_ip() const 90 | { 91 | return 100.0 * double(pkts_valid_gtpu_ip) / double(pkts_total); 92 | } 93 | double perc_pkts_valid_tranport() const 94 | { 95 | return 100.0 * double(pkts_valid_tranport) / double(pkts_total); 96 | } 97 | double perc_pkts_valid_ip() const 98 | { 99 | return 100.0 * double(pkts_valid_ip) / double(pkts_total); 100 | } 101 | double perc_pkts_invalid() const 102 | { 103 | return 100.0 * double(pkts_invalid) / double(pkts_total); 104 | } 105 | 106 | public: 107 | // FIXME: put m_ in front of variable names 108 | uint64_t pkts_valid_gtpu_transport; 109 | uint64_t pkts_valid_gtpu_ip; 110 | uint64_t pkts_valid_tranport; 111 | uint64_t pkts_valid_ip; 112 | uint64_t pkts_invalid; 113 | 114 | uint64_t pkts_total; 115 | }; 116 | 117 | class FlowInfo { 118 | public: 119 | // identifiers of the flow: 120 | IpAddress m_ip_src; 121 | IpAddress m_ip_dst; 122 | uint8_t m_ip_proto = 0; 123 | uint16_t m_port_src = 0; 124 | uint16_t m_port_dst = 0; 125 | 126 | flow_hash_t compute_flow_hash(); 127 | flow_hash_t get_flow_hash() const 128 | { 129 | // this function assumes that compute_flow_hash() has already been invoked 130 | return m_hash; 131 | } 132 | 133 | private: 134 | flow_hash_t m_hash = 0; 135 | }; 136 | 137 | //------------------------------------------------------------------------------ 138 | // Packet Parsing Functions 139 | //------------------------------------------------------------------------------ 140 | 141 | extern ParserRetCode_t get_transport_start_offset( // fn 142 | const Packet& pkt, 143 | int* offsetTransportOut, 144 | int* ipprotOut, 145 | int* remainingLenOut, 146 | FlowInfo* infoOut); 147 | 148 | extern ParserRetCode_t get_gtpu_inner_ip_start_offset( // fn 149 | const Packet& pkt, 150 | int* offsetIpInnerOut, 151 | int* ipverOut, 152 | int* remainingLenOut, 153 | FlowInfo* infoOut); 154 | 155 | extern ParserRetCode_t get_gtpu_inner_transport_start_offset( 156 | const Packet& pkt, 157 | int* offsetTransportInnerOut, 158 | int* ipprotInnerOut, 159 | int* remainingLenOut, 160 | FlowInfo* infoOut); 161 | 162 | extern void update_parsing_stats(const Packet& pkt, ParsingStats& outstats); 163 | 164 | extern flow_hash_t compute_flow_hash(const Packet& pkt); 165 | 166 | #endif // PARSE_H_ 167 | -------------------------------------------------------------------------------- /src/pcap_helpers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * pcap_helpers.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * 8 | * LICENSE: 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | MA 02110-1301, USA. 23 | 24 | */ 25 | 26 | //------------------------------------------------------------------------------ 27 | // Includes 28 | //------------------------------------------------------------------------------ 29 | 30 | #include "pcap_helpers.h" 31 | #include "printf_helpers.h" 32 | 33 | // adapted from 34 | // http://sourcecodebrowser.com/libpcapnav/0.8/pcapnav__append_8c.html#a918994d1d2d679e4aaad41f7724360ea 35 | 36 | pcap_dumper_t* pcap_dump_append(pcap_t* pcap, const char* filename) 37 | { 38 | char pcap_errbuf[PCAP_ERRBUF_SIZE]; 39 | pcap_t* pn = NULL; 40 | FILE* result = NULL; 41 | 42 | pn = pcap_open_offline(filename, pcap_errbuf); 43 | if (pn == NULL) { 44 | 45 | result = (FILE*)pcap_dump_open(pcap, filename); 46 | if (result) { 47 | // file does not exit... create it with the regular PCAP API function 48 | return (pcap_dumper_t*)result; 49 | } else { 50 | printf_error("Cannot open file: %s\n", pcap_errbuf); 51 | return NULL; 52 | } 53 | } 54 | 55 | /* Check whether the linklayer protocols are compatible -- if not, 56 | * then we cannot append (at least not without linklayer adaptors). 57 | * 58 | * Note that we do NOT check against pn->trace.filehdr.linktype 59 | * directly. Pcap's internal mapping mechanism may cause a different 60 | * value to be stored in the header structure than reported through 61 | * pcap_datalink(), so we must make sure we use pcap_datalink() in 62 | * both cases to ensure comparability. 63 | */ 64 | if (pcap_datalink(pn) != pcap_datalink(pcap)) { 65 | printf_error("linklayer protocols incompatible (%i/%i)", pcap_datalink(pn), 66 | pcap_datalink(pcap)); 67 | pcap_close(pn); 68 | return NULL; 69 | } 70 | 71 | if (!(result = fopen(filename, "r+"))) { 72 | printf_error("Error opening '%s' in r+ mode.\n", filename); 73 | goto error_return; 74 | } 75 | 76 | #if 0 77 | /* Check whether the snaplen will need to be updated: */ 78 | if (pcap_snapshot(pn) < pcap_snapshot(pcap)) 79 | { 80 | //struct pcap_file_header filehdr; 81 | 82 | printf_error( "snaplen needs updating from %d to %d.\n", 83 | pcap_snapshot(pn), pcap_snapshot(pcap)); 84 | 85 | /* 86 | filehdr = pn->trace.filehdr; 87 | filehdr.snaplen = pcap_snapshot(pcap); 88 | 89 | if (fwrite(&filehdr, sizeof(struct pcap_file_header), 1, result) != 1) 90 | { 91 | D(("Cannot write corrected file header.\n")); 92 | goto error_return; 93 | }*/ 94 | goto error_return; 95 | } 96 | #endif 97 | 98 | if (fseek(result, 0, SEEK_END) < 0) { 99 | printf_error("Error seeking to end of file.\n"); 100 | goto error_return; 101 | } 102 | 103 | #if 0 104 | if (mode == PCAPNAV_DUMP_APPEND_SAFE) 105 | { 106 | if (! append_fix_trunc_packet(pn, result)) 107 | { 108 | D(("Fixing truncated packet failed.\n")); 109 | goto error_return; 110 | } 111 | } 112 | #endif 113 | 114 | pcap_close(pn); 115 | return (pcap_dumper_t*)result; 116 | 117 | error_return: 118 | pcap_close(pn); 119 | return NULL; 120 | } 121 | 122 | int pcap_compile_bpf(struct bpf_program* program, const char* buf) 123 | { 124 | pcap_t* p; 125 | int ret; 126 | 127 | p = pcap_open_dead(DLT_EN10MB, MAX_SNAPLEN); 128 | if (p == NULL) { 129 | return -1; 130 | } 131 | 132 | ret = pcap_compile(p, program, buf, 0 /* optimize */, PCAP_NETMASK_UNKNOWN /* process packet from any network */); 133 | pcap_close(p); 134 | return ret; 135 | } 136 | -------------------------------------------------------------------------------- /src/pcap_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pcap_helpers.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef PCAP_HELPERS_H_ 26 | #define PCAP_HELPERS_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include "large-pcap-analyzer.h" 33 | #include 34 | 35 | // libpcap dependency: 36 | // NOTE: in version 1.7.x there is no pcap/pcap.h, just a pcap.h apparently: 37 | //#include 38 | #include 39 | 40 | //------------------------------------------------------------------------------ 41 | // Constants 42 | //------------------------------------------------------------------------------ 43 | 44 | //------------------------------------------------------------------------------ 45 | // Functions 46 | //------------------------------------------------------------------------------ 47 | 48 | pcap_dumper_t* pcap_dump_append(pcap_t* pcap, const char* filename); 49 | 50 | // NOTE: apparently pcap_compile_nopcap() got deprecated six months ago in libpcap v1.11.0: 51 | // https://github.com/the-tcpdump-group/libpcap/blob/753dc6beddea935c4f0365c3e8634b44db527a82/CHANGES#L19 52 | // so we provide our own substitution (also featuring simplified argument list) 53 | int pcap_compile_bpf(struct bpf_program* program, const char* buf); 54 | 55 | #endif // PCAP_HELPERS_H_ 56 | -------------------------------------------------------------------------------- /src/printf_helpers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * printf_helpers.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * 8 | * LICENSE: 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | MA 02110-1301, USA. 23 | 24 | */ 25 | 26 | //------------------------------------------------------------------------------ 27 | // Includes 28 | //------------------------------------------------------------------------------ 29 | 30 | #include "printf_helpers.h" 31 | #include "large-pcap-analyzer.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | //------------------------------------------------------------------------------ 38 | // Global Functions 39 | //------------------------------------------------------------------------------ 40 | 41 | void printf_verbose(const char* fmtstr, ...) 42 | { 43 | va_list args; 44 | va_start(args, fmtstr); 45 | if (g_config.m_verbose) { 46 | assert(!g_config.m_quiet); 47 | vprintf(fmtstr, args); 48 | } 49 | va_end(args); 50 | } 51 | 52 | void printf_normal(const char* fmtstr, ...) 53 | { 54 | va_list args; 55 | va_start(args, fmtstr); 56 | if (!g_config.m_quiet) 57 | vprintf(fmtstr, args); 58 | va_end(args); 59 | } 60 | 61 | void printf_quiet(const char* fmtstr, ...) 62 | { 63 | va_list args; 64 | va_start(args, fmtstr); 65 | if (g_config.m_quiet) 66 | vprintf(fmtstr, args); 67 | va_end(args); 68 | } 69 | 70 | void printf_error(const char* fmtstr, ...) 71 | { 72 | va_list args; 73 | va_start(args, fmtstr); 74 | // if (g_quiet) // even if quiet mode is ON, do print errors out 75 | vfprintf(stderr, fmtstr, args); 76 | va_end(args); 77 | } 78 | -------------------------------------------------------------------------------- /src/printf_helpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * printf_helpers.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef PRINTF_HELPERS_H_ 26 | #define PRINTF_HELPERS_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Functions 30 | //------------------------------------------------------------------------------ 31 | 32 | extern void printf_verbose(const char* fmtstr, ...); 33 | extern void printf_normal(const char* fmtstr, ...); 34 | extern void printf_error(const char* fmtstr, ...); 35 | extern void printf_quiet(const char* fmtstr, ...); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/process_file.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * process_file.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * 8 | * LICENSE: 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | MA 02110-1301, USA. 23 | 24 | */ 25 | 26 | //------------------------------------------------------------------------------ 27 | // Includes 28 | //------------------------------------------------------------------------------ 29 | 30 | #include "filter.h" 31 | #include "large-pcap-analyzer.h" 32 | #include "parse.h" 33 | #include "pcap_helpers.h" 34 | #include "printf_helpers.h" 35 | #include "timestamp_pkt_processor.h" 36 | 37 | #include 38 | #include 39 | #include 40 | #include /* includes net/ethernet.h */ 41 | #include 42 | #include /* superset of previous */ 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | 56 | //------------------------------------------------------------------------------ 57 | // Static Functions 58 | //------------------------------------------------------------------------------ 59 | 60 | static bool 61 | firstpass_process_pcap_handle_for_tcp_streams(pcap_t* pcap_handle_in, FilterCriteria* filter, unsigned long* nvalidflowsOUT) 62 | { 63 | unsigned long nloaded_pkts = 0, ninvalid_pkts = 0, nnottcp_pkts = 0; 64 | unsigned long nfound_streams = 0, nsyn_streams = 0, nsyn_synack_streams = 0, 65 | nfull3way_streams = 0, nfull3way_with_data_streams = 0; 66 | struct timeval start, stop; 67 | const u_char* pcap_packet; 68 | struct pcap_pkthdr* pcap_header; 69 | 70 | // the output of this function is saved inside the FILTER object: 71 | filter->flow_map().clear(); 72 | 73 | gettimeofday(&start, NULL); 74 | while (!g_config.m_termination_requested && pcap_next_ex(pcap_handle_in, &pcap_header, &pcap_packet) > 0) { 75 | Packet pkt(pcap_header, pcap_packet); 76 | 77 | nloaded_pkts++; 78 | if ((nloaded_pkts % MILLION) == 0 && nloaded_pkts > 0) 79 | printf_verbose("%luM packets loaded from PCAP...\n", 80 | nloaded_pkts / MILLION); 81 | 82 | // first, detect if this is a TCP SYN/SYN-ACK packet 83 | bool is_tcp_syn = false, is_tcp_syn_ack = false, is_tcp_ack = false; 84 | FlowInfo flow_info; 85 | 86 | int offsetInnerTransport = 0, innerIpProt = 0, 87 | len_after_transport_start = 0; 88 | ParserRetCode_t ret = get_gtpu_inner_transport_start_offset( 89 | pkt, &offsetInnerTransport, &innerIpProt, &len_after_transport_start, 90 | &flow_info); 91 | if (ret != GPRC_VALID_PKT) { 92 | // not a GTPu packet... try treating it as non-encapsulated TCP packet: 93 | ParserRetCode_t ret = get_transport_start_offset(pkt, &offsetInnerTransport, &innerIpProt, 94 | &len_after_transport_start, &flow_info); 95 | if (ret != GPRC_VALID_PKT) { 96 | offsetInnerTransport = 0; 97 | innerIpProt = 0; 98 | ninvalid_pkts++; 99 | continue; 100 | } 101 | } 102 | 103 | if (innerIpProt != IPPROTO_TCP) { 104 | nnottcp_pkts++; 105 | continue; 106 | } 107 | 108 | // then save the state for the TCP connection associated to this packet: 109 | 110 | flow_hash_t hash = flow_info.compute_flow_hash(); 111 | assert(hash != INVALID_FLOW_HASH); 112 | std::pair result = filter->flow_map().insert( 113 | std::pair(hash, 114 | FLOW_FOUND)); 115 | if (result.second) 116 | nfound_streams++; // this stream is a new connection 117 | 118 | const struct tcphdr* tcp = (const struct tcphdr*)(pcap_packet + offsetInnerTransport); 119 | if (tcp->syn == 1 && tcp->ack == 0) 120 | is_tcp_syn = true; 121 | if (tcp->syn == 1 && tcp->ack == 1) 122 | is_tcp_syn_ack = true; 123 | if (tcp->syn == 0 && tcp->ack == 1) 124 | is_tcp_ack = true; 125 | 126 | int transport_hdr_len = 4 * tcp->doff; 127 | int len_after_transport_end = len_after_transport_start - transport_hdr_len; 128 | 129 | if (is_tcp_syn) { 130 | assert(!is_tcp_syn_ack); 131 | assert(!is_tcp_ack); 132 | 133 | // SYN packet found, remember this: 134 | 135 | flow_map_t::iterator entry = filter->flow_map().find(hash); 136 | if (entry != filter->flow_map().end()) { 137 | if (entry->second == FLOW_FOUND) 138 | nsyn_streams++; 139 | 140 | entry->second = FLOW_FOUND_SYN; // reset status to only SYN found 141 | } 142 | } else if (is_tcp_syn_ack) { 143 | assert(!is_tcp_syn); 144 | assert(!is_tcp_ack); 145 | 146 | flow_map_t::iterator entry = filter->flow_map().find(hash); 147 | if (entry != filter->flow_map().end() && entry->second == FLOW_FOUND_SYN) { 148 | entry->second = FLOW_FOUND_SYN_AND_SYNACK; // existing connection, found 149 | // SYN-ACK packet for that 150 | nsyn_synack_streams++; 151 | } 152 | } else if (is_tcp_ack) { 153 | assert(!is_tcp_syn); 154 | assert(!is_tcp_syn_ack); 155 | 156 | flow_map_t::iterator entry = filter->flow_map().find(hash); 157 | if (entry != filter->flow_map().end()) { 158 | if (entry->second == FLOW_FOUND_SYN_AND_SYNACK) { 159 | entry->second = FLOW_FOUND_SYN_AND_SYNACK_AND_ACK; // existing connection, found 160 | // the 3way handshake for that! 161 | nfull3way_streams++; 162 | 163 | if (len_after_transport_end > 0) { 164 | entry->second = FLOW_FOUND_SYN_AND_SYNACK_AND_ACK_AND_DATA; // existing 165 | // connection, found 166 | // the 1st data 167 | // packet after 3way 168 | // handshake 169 | nfull3way_with_data_streams++; 170 | } 171 | } else if (entry->second == FLOW_FOUND_SYN_AND_SYNACK_AND_ACK && len_after_transport_end > 0) { 172 | entry->second = FLOW_FOUND_SYN_AND_SYNACK_AND_ACK_AND_DATA; // existing 173 | // connection, found 174 | // the 1st data packet 175 | // after 3way 176 | // handshake 177 | nfull3way_with_data_streams++; 178 | } 179 | } 180 | } else if (len_after_transport_end > 0) { 181 | assert(!is_tcp_syn); 182 | assert(!is_tcp_syn_ack); 183 | assert(!is_tcp_ack); 184 | 185 | // looks like a TCP data packet: no SYN/ACK flags and there is payload 186 | // after TCP header 187 | 188 | flow_map_t::iterator entry = filter->flow_map().find(hash); 189 | if (entry != filter->flow_map().end() && entry->second == FLOW_FOUND_SYN_AND_SYNACK_AND_ACK) { 190 | entry->second = FLOW_FOUND_SYN_AND_SYNACK_AND_ACK_AND_DATA; // existing connection, 191 | // found the 1st data 192 | // packet after 3way 193 | // handshake 194 | nfull3way_with_data_streams++; 195 | } 196 | } 197 | } 198 | gettimeofday(&stop, NULL); 199 | 200 | printf_verbose("Processing took %i seconds.\n", 201 | (int)(stop.tv_sec - start.tv_sec)); 202 | printf_verbose("Detected %lu invalid packets and %lu non-TCP packets (on " 203 | "total of %lu packets)\n", 204 | ninvalid_pkts, nnottcp_pkts, nloaded_pkts); 205 | 206 | printf_normal( 207 | "Detected flows:\n Having at least 1SYN: %lu\n Having SYN-SYNACK: " 208 | "%lu\n Having full 3way handshake: %lu\n Having full 3way handshake " 209 | "and data: %lu\n Total TCP flows found: %lu\n", 210 | nsyn_streams, nsyn_synack_streams, nfull3way_streams, 211 | nfull3way_with_data_streams, nfound_streams); 212 | 213 | if (nvalidflowsOUT) 214 | *nvalidflowsOUT = nfound_streams; 215 | 216 | return true; 217 | } 218 | 219 | static bool process_pcap_handle( 220 | const std::string& infile, 221 | pcap_t* pcap_handle_in, 222 | FilterCriteria* filter, /* can be NULL */ 223 | IPacketProcessor* pktprocessor, /* can be NULL */ 224 | pcap_dumper_t* pcap_dumper, /* can be NULL */ 225 | unsigned long* nloadedOUT, 226 | unsigned long* nmatchingOUT) 227 | { 228 | unsigned long nloaded = 0, nmatching = 0, ngtpu = 0; 229 | struct timeval start, stop; 230 | ParsingStats parsing_stats; 231 | 232 | const u_char* pcap_packet; 233 | struct pcap_pkthdr* pcap_header; 234 | 235 | std::string pcapfilter_desc = ""; 236 | if (filter && filter->is_capture_filter_set()) 237 | pcapfilter_desc = " (matching PCAP filter)"; 238 | 239 | gettimeofday(&start, NULL); 240 | Packet tempPkt; 241 | while (!g_config.m_termination_requested && pcap_next_ex(pcap_handle_in, &pcap_header, &pcap_packet) > 0) { 242 | Packet pkt(pcap_header, pcap_packet); 243 | 244 | if ((nloaded % MILLION) == 0 && nloaded > 0) 245 | printf_verbose("%luM packets loaded from PCAP%s...\n", nloaded / MILLION, 246 | pcapfilter_desc.c_str()); 247 | 248 | // filter and save to output eventually 249 | 250 | bool is_gtpu = false; 251 | bool toprocess = true; 252 | 253 | if (filter) 254 | toprocess = filter->is_matching(pkt, &is_gtpu); 255 | // else: filtering disabled, save all packets 256 | 257 | if (toprocess) { 258 | if (pktprocessor) { 259 | bool pktWasChanged = false; 260 | if (!pktprocessor->process_packet(pkt, tempPkt, 261 | nmatching /* this is the index of the saved packets */, 262 | pktWasChanged)) { 263 | printf_error("Error while processing packet %lu. Aborting.\n", nmatching); 264 | return false; 265 | } 266 | 267 | if (pktWasChanged) { 268 | if (pcap_dumper) 269 | pcap_dump((u_char*)pcap_dumper, tempPkt.header(), tempPkt.data()); 270 | } else { 271 | // dump original packet 272 | if (pcap_dumper) 273 | pcap_dump((u_char*)pcap_dumper, pcap_header, pcap_packet); 274 | } 275 | } else { 276 | // no packet processor provided: just dump original packet 277 | if (pcap_dumper) 278 | pcap_dump((u_char*)pcap_dumper, pcap_header, pcap_packet); 279 | } 280 | 281 | nmatching++; 282 | } 283 | if (is_gtpu) 284 | ngtpu++; 285 | 286 | if (g_config.m_parsing_stats) { 287 | update_parsing_stats(pkt, parsing_stats); 288 | } 289 | 290 | // advance main stats counters: 291 | 292 | nloaded++; 293 | } 294 | gettimeofday(&stop, NULL); 295 | 296 | printf_verbose("Processing took %i seconds.\n", (int)(stop.tv_sec - start.tv_sec)); 297 | printf_normal("%luM packets (%lu packets) were loaded from PCAP%s.\n", nloaded / MILLION, nloaded, pcapfilter_desc.c_str()); 298 | 299 | if (filter) { 300 | if (!filter->post_filtering(nloaded)) 301 | return false; 302 | } 303 | 304 | if (pktprocessor) { 305 | if (!pktprocessor->post_processing(infile, nloaded)) 306 | return false; 307 | } 308 | 309 | if (pcap_dumper) { 310 | if (filter && filter->is_some_filter_active()) { 311 | printf_normal("%luM packets (%lu packets) matched the filtering criteria " 312 | "(search string / PCAP filters / TCP streams filter) and " 313 | "were saved into output PCAP.\n", 314 | nmatching / MILLION, nmatching); 315 | } else if (filter && !filter->is_some_filter_active()) { 316 | assert(nmatching == 0); 317 | printf_normal("No criteria for packet selection specified (search string " 318 | "/ GTPu filter / TCP streams filter) so nothing was " 319 | "written into output PCAP.\n"); 320 | } else if (pktprocessor) { 321 | printf_normal("%luM packets (%lu packets) were processed and saved into output PCAP.\n", 322 | nmatching / MILLION, nmatching); 323 | } 324 | } 325 | 326 | if (g_config.m_parsing_stats) { 327 | if (g_config.m_quiet) { 328 | // be machine-friendly: use CSV format 329 | printf_quiet( 330 | "GTPu pkts with valid inner transport,GTPu pkts with valid inner " 331 | "IP,Pkts with valid transport,Pkts with valid IP,Pkts invalid\n"); 332 | printf_quiet( 333 | "%lu,%lu,%lu,%lu,%lu\n", parsing_stats.pkts_valid_gtpu_transport, 334 | parsing_stats.pkts_valid_gtpu_ip, parsing_stats.pkts_valid_tranport, 335 | parsing_stats.pkts_valid_ip, parsing_stats.pkts_invalid); 336 | } else { 337 | printf_normal("Parsing stats: %.2f%% GTPu with valid inner transport, " 338 | "%.2f%% GTPu with valid inner IP, %.2f%% with valid " 339 | "transport, %.2f%% with valid IP, %.2f%% invalid.\n", 340 | parsing_stats.perc_pkts_valid_gtpu_transport(), 341 | parsing_stats.perc_pkts_valid_gtpu_ip(), 342 | parsing_stats.perc_pkts_valid_tranport(), 343 | parsing_stats.perc_pkts_valid_ip(), 344 | parsing_stats.perc_pkts_invalid()); 345 | } 346 | } 347 | 348 | // provide output info 349 | if (nloadedOUT) 350 | *nloadedOUT += nloaded; 351 | if (nmatchingOUT) 352 | *nmatchingOUT += nmatching; 353 | 354 | return true; 355 | } 356 | 357 | //------------------------------------------------------------------------------ 358 | // process_file 359 | //------------------------------------------------------------------------------ 360 | 361 | bool process_file( 362 | const std::string& infile, const std::string& outfile, bool outfile_append, FilterCriteria* filter, 363 | IPacketProcessor* pktprocessor, unsigned long* nloadedOUT, unsigned long* nmatchingOUT) 364 | { 365 | char pcap_errbuf[PCAP_ERRBUF_SIZE]; 366 | pcap_t* pcap_handle_in = NULL; 367 | pcap_dumper_t* pcap_dumper = NULL; 368 | 369 | struct stat st; 370 | memset(&st, 0, sizeof(st)); 371 | stat(infile.c_str(), &st); 372 | 373 | // open infile 374 | pcap_handle_in = pcap_open_offline(infile.c_str(), pcap_errbuf); 375 | if (pcap_handle_in == NULL) { 376 | printf_error("Cannot open file: %s\n", pcap_errbuf); 377 | return false; 378 | } 379 | printf_verbose("Analyzing PCAP file '%s'...\n", infile.c_str()); 380 | if (st.st_size) 381 | printf_verbose("The PCAP file has size %.2fGiB = %luMiB.\n", (double)st.st_size / (double)GB, st.st_size / MB); 382 | 383 | // open outfile 384 | if (!outfile.empty()) { 385 | if (outfile_append) { 386 | // NOTE: the PCAP produced by appending cannot be opened correctly by 387 | // Wireshark in some cases... 388 | 389 | pcap_dumper = pcap_dump_append(pcap_handle_in, outfile.c_str()); 390 | if (pcap_dumper == NULL) 391 | return false; 392 | 393 | printf_normal("Successfully opened output PCAP '%s' in APPEND mode\n", 394 | outfile.c_str()); 395 | } else { 396 | // NOTE: the pcap_dump_open() seems to open always PCAP files with the MAGIC 0xa1b2c3d4 397 | // that indicates MICROSECOND timestamp accuracy; see 398 | // https://wiki.wireshark.org/Development/LibpcapFileFormat#nanosecond-pcap 399 | pcap_dumper = pcap_dump_open(pcap_handle_in, outfile.c_str()); 400 | if (!pcap_dumper) { 401 | printf_error("Cannot open file: %s\n", pcap_geterr(pcap_handle_in)); 402 | return false; 403 | } 404 | printf_normal("Successfully opened output PCAP '%s'\n", outfile.c_str()); 405 | } 406 | } 407 | 408 | // do the real job 409 | 410 | if (filter->needs_2passes()) { 411 | // special mode: this requires 2 pass over the PCAP file: first one to get 412 | // hash values for valid FLOWS; second one to actually filter all flows with 413 | // valid hashes 414 | 415 | // first pass: 416 | printf_normal("TCP connection filtering enabled: performing first pass\n"); 417 | 418 | unsigned long nvalidflows = 0; 419 | if (!firstpass_process_pcap_handle_for_tcp_streams(pcap_handle_in, filter, &nvalidflows)) 420 | return false; 421 | 422 | if (nvalidflows) { 423 | // second pass: 424 | 425 | printf_normal("TCP connection filtering enabled: performing second pass\n"); 426 | 427 | // reopen infile 428 | pcap_close(pcap_handle_in); 429 | pcap_handle_in = pcap_open_offline(infile.c_str(), pcap_errbuf); 430 | if (pcap_handle_in == NULL) { 431 | printf_error("Cannot open file: %s\n", pcap_errbuf); 432 | return false; 433 | } 434 | 435 | printf_verbose("Analyzing PCAP file '%s'...\n", infile.c_str()); 436 | if (st.st_size) 437 | printf_verbose("The PCAP file has size %.2fGiB = %luMiB.\n", (double)st.st_size / (double)GB, st.st_size / MB); 438 | 439 | if (!process_pcap_handle(infile.c_str(), pcap_handle_in, filter, pktprocessor, pcap_dumper, nloadedOUT, nmatchingOUT)) 440 | return false; 441 | } else { 442 | return false; 443 | } 444 | } else { 445 | // if no filtering is given, but we want to process output packets, then we 446 | // invert the selection criteria: by default each packet of the input will 447 | // be processed (by default an empty FilterCriteria instance would instead 448 | // discard ALL packets!) 449 | FilterCriteria* filterToUse = filter; 450 | if (!filter->is_some_filter_active()) { 451 | printf_verbose("Selected some packet processing operation but no filter was specified: processing ALL input packets\n"); 452 | filterToUse = NULL; // disable filter-out logic 453 | } 454 | 455 | if (pktprocessor && pktprocessor->needs_2passes()) { 456 | // first pass 457 | printf_normal("Packet processing require 2 passes: performing first pass\n"); 458 | unsigned long nFilteredPkts = 0; 459 | pktprocessor->set_pass_index(0); 460 | if (!process_pcap_handle(infile.c_str(), pcap_handle_in, filterToUse, pktprocessor, NULL, NULL, &nFilteredPkts)) 461 | return false; 462 | 463 | if (nFilteredPkts) { 464 | printf_normal("Packet processing require 2 passes: performing second pass\n"); 465 | 466 | // reopen infile 467 | pcap_close(pcap_handle_in); 468 | pcap_handle_in = pcap_open_offline(infile.c_str(), pcap_errbuf); 469 | if (pcap_handle_in == NULL) { 470 | printf_error("Cannot open file: %s\n", pcap_errbuf); 471 | return false; 472 | } 473 | 474 | // re-process this time with PROCESSOR and OUTPUT DUMPER active! 475 | pktprocessor->set_pass_index(1); 476 | if (!process_pcap_handle(infile.c_str(), pcap_handle_in, filterToUse, pktprocessor, pcap_dumper, nloadedOUT, nmatchingOUT)) 477 | return false; 478 | } 479 | } else { 480 | // standard mode: do all the processing in 1 pass 481 | 482 | if (!process_pcap_handle(infile.c_str(), pcap_handle_in, filterToUse, pktprocessor, pcap_dumper, nloadedOUT, nmatchingOUT)) 483 | return false; 484 | } 485 | } 486 | 487 | // cleanup 488 | if (!outfile.empty()) 489 | pcap_dump_close(pcap_dumper); 490 | pcap_close(pcap_handle_in); 491 | 492 | return true; 493 | } 494 | -------------------------------------------------------------------------------- /src/process_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * process_file.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * 8 | * LICENSE: 9 | This program is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation; either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 | MA 02110-1301, USA. 23 | 24 | */ 25 | 26 | #ifndef LPA_PROCESS_FILE_H_ 27 | #define LPA_PROCESS_FILE_H_ 28 | 29 | //------------------------------------------------------------------------------ 30 | // Includes 31 | //------------------------------------------------------------------------------ 32 | 33 | #include 34 | 35 | class FilterCriteria; 36 | class IPacketProcessor; 37 | 38 | bool process_file( 39 | const std::string& infile, const std::string& outfile, bool outfile_append, FilterCriteria* filter, 40 | IPacketProcessor* processorcfg, unsigned long* nloadedOUT, unsigned long* nmatchingOUT); 41 | 42 | #endif // LPA_PROCESS_FILE_H_ -------------------------------------------------------------------------------- /src/processor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * processor.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef PROCESSING_H_ 26 | #define PROCESSING_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include "packet.h" 33 | #include 34 | 35 | //------------------------------------------------------------------------------ 36 | // IPacketProcessor 37 | // This is an abstract class to represent any type of stateful packet processing 38 | // that can be carried out by LPA 39 | //------------------------------------------------------------------------------ 40 | 41 | class IPacketProcessor { 42 | public: 43 | IPacketProcessor() 44 | { 45 | m_current_pass = 0; 46 | } 47 | 48 | ~IPacketProcessor() {} 49 | 50 | // some packet processor specialization might need to process each PCAP file twice: 51 | virtual bool needs_2passes() const 52 | { 53 | return false; 54 | } 55 | 56 | void set_pass_index(unsigned int passIdx) { m_current_pass = passIdx; } 57 | 58 | unsigned int get_pass_index() const { return m_current_pass; } 59 | 60 | // returns true if the processing is successful or false if it should be 61 | // aborted. NOTE: pktWasChanged will be set to true if output packet has been 62 | // filled or false if no action was performed on the input packet and thus the 63 | // caller should use the pktIn instance 64 | virtual bool process_packet(const Packet& pktIn, Packet& pktOut, unsigned int pktIdx, bool& pktWasChangedOut) = 0; 65 | 66 | // called after processing an entire PCAP file composed by "totNumPkts" 67 | virtual bool post_processing(const std::string& infile, unsigned int totNumPkts) = 0; 68 | 69 | private: 70 | // configuration: 71 | unsigned int m_current_pass; // 0 or 1 72 | }; 73 | 74 | #endif // PROCESSING_H_ 75 | -------------------------------------------------------------------------------- /src/timestamp_pkt_processor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * timestamp_pkt_processor.cpp 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | //------------------------------------------------------------------------------ 26 | // Includes 27 | //------------------------------------------------------------------------------ 28 | 29 | #include "timestamp_pkt_processor.h" 30 | #include "large-pcap-analyzer.h" 31 | #include "printf_helpers.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include /* includes net/ethernet.h */ 37 | #include 38 | #include /* superset of previous */ 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | //------------------------------------------------------------------------------ 45 | // Globals 46 | //------------------------------------------------------------------------------ 47 | 48 | bool String2TimestampInSecs(const std::string& str, double& result) 49 | { 50 | std::stringstream ss(str); 51 | if (!(ss >> result)) { 52 | return false; 53 | } 54 | 55 | return true; 56 | } 57 | 58 | //------------------------------------------------------------------------------ 59 | // TimestampPacketProcessor 60 | //------------------------------------------------------------------------------ 61 | 62 | bool TimestampPacketProcessor::prepare_processor(bool print_timestamp_analysis, const std::string& set_duration, bool preserve_ifg, const std::string& timestamp_file) 63 | { 64 | m_print_timestamp_analysis = print_timestamp_analysis; 65 | if (!set_duration.empty()) { 66 | // the duration string can be a number in format 67 | // SECONDS.FRACTIONAL_SECONDS 68 | // or: HH:MM:SS.FRACTIONAL_SECONDS 69 | // ^^^^^^^^ 70 | // 8 characters 71 | int num_dots = std::count(set_duration.begin(), set_duration.end(), '.'); 72 | int num_colons = std::count(set_duration.begin(), set_duration.end(), ':'); 73 | if (num_dots == 1 && num_colons == 0) { 74 | // FIRST SYNTAX FORMAT 75 | m_new_duration_secs = atof(set_duration.c_str()); 76 | } else if (num_dots == 0 && num_colons == 0) { 77 | // FIRST SYNTAX FORMAT WITHOT FRACTIONAL SECS 78 | m_new_duration_secs = atoi(set_duration.c_str()); 79 | } else if (num_dots <= 1 && num_colons == 2 && set_duration.size() >= 8 /* chars */ && set_duration[2] == ':' && set_duration[5] == ':') { 80 | 81 | // SECOND SYNTAX FORMAT 82 | int hh = atoi(set_duration.substr(0, 2).c_str()); 83 | int mm = atoi(set_duration.substr(3, 5).c_str()); 84 | double ss = atof(set_duration.substr(6).c_str()); 85 | 86 | m_new_duration_secs = hh * 3600 + mm * 60 + ss; 87 | 88 | } else { 89 | printf_error("Cannot parse PCAP duration to set: %s\n", 90 | set_duration.c_str()); 91 | return false; 92 | } 93 | 94 | if (preserve_ifg) 95 | m_proc_mode = PROCMODE_CHANGE_DURATION_PRESERVE_IFG; 96 | else 97 | m_proc_mode = PROCMODE_CHANGE_DURATION_RESET_IFG; 98 | printf_verbose("PCAP duration will be set to: %f secs (IFG will be %s)\n", 99 | m_new_duration_secs, preserve_ifg ? "preserved" : "reset"); 100 | } else if (!timestamp_file.empty()) { 101 | // validate input file: 102 | 103 | m_timestamps_input_file = timestamp_file; 104 | std::ifstream infile(timestamp_file); 105 | if (infile.good()) { 106 | printf_normal("Successfully opened input timings file '%s'\n", 107 | timestamp_file.c_str()); 108 | } else { 109 | printf_error("Cannot open the file with packet timings '%s'\n", 110 | timestamp_file.c_str()); 111 | return false; 112 | } 113 | 114 | size_t lineIdx = 0; 115 | std::string line; 116 | double ts; 117 | while (std::getline(infile, line)) { 118 | if (line.empty()) { 119 | // give 1-based line index: 120 | printf_error( 121 | "Invalid empty line %d in the file with packet timings '%s'\n", 122 | lineIdx + 1, timestamp_file.c_str()); 123 | return false; 124 | } 125 | if (!String2TimestampInSecs(line, ts)) { 126 | // give 1-based line index: 127 | printf_error("Invalid timestamp at line %d in the file with packet " 128 | "timings '%s': %s\n", 129 | lineIdx + 1, timestamp_file.c_str(), line.c_str()); 130 | return false; 131 | } 132 | m_timestamps.push_back(ts); 133 | lineIdx++; 134 | } 135 | 136 | m_proc_mode = PROCMODE_SET_TIMESTAMPS; 137 | printf_verbose("%zu timestamps loaded from '%s'\n", m_timestamps.size(), 138 | timestamp_file.c_str()); 139 | } 140 | 141 | return true; 142 | } 143 | 144 | bool TimestampPacketProcessor::process_packet(const Packet& pktIn, Packet& pktOut, unsigned int pktIdx, bool& pktWasChangedOut) 145 | { 146 | pktWasChangedOut = false; // by default: no proc done, use original packet 147 | 148 | if (IPacketProcessor::get_pass_index() == 0) { 149 | m_num_input_pkts++; 150 | 151 | // regardless of which "processing mode" has been chosen, save timestamps of first/last pkts; 152 | // these are used to 153 | // * provide some basic timing info during the post_processing() phase 154 | // * support the PROCMODE_CHANGE_DURATION_RESET_IFG/PROCMODE_CHANGE_DURATION_PRESERVE_IFG modes 155 | if (UNLIKELY(pktIdx == 0)) { 156 | assert(m_first_pkt_ts_sec == 0); 157 | m_first_pkt_ts_sec = pktIn.pcap_timestamp_to_seconds(); 158 | m_last_pkt_ts_sec = m_first_pkt_ts_sec; 159 | printf_verbose("First pkt timestamp is %f\n", m_first_pkt_ts_sec); 160 | } else { 161 | // remember the timestamp of the last packet: 162 | m_last_pkt_ts_sec = pktIn.pcap_timestamp_to_seconds(); 163 | } 164 | 165 | // caplen indicates what has been _really_ captured 166 | // len indicates how long was the original packet 167 | m_nbytes_pcap += pktIn.header()->caplen; 168 | m_nbytes_original += pktIn.header()->len; 169 | } 170 | 171 | switch (m_proc_mode) { 172 | case PROCMODE_NONE: { 173 | pktWasChangedOut = false; // no proc done, use original packet 174 | } break; 175 | 176 | case PROCMODE_CHANGE_DURATION_RESET_IFG: 177 | case PROCMODE_CHANGE_DURATION_PRESERVE_IFG: { 178 | if (IPacketProcessor::get_pass_index() == 1) { 179 | // second pass 180 | 181 | assert(m_new_duration_secs > 0); 182 | assert(m_num_input_pkts > 0); 183 | 184 | // it's not always garantueed that the timestamp of the last packet is valid; indeed the user might 185 | // be trying to rewrite timestamps in an invalid PCAP file (having wrong timestamps) for which the libpcap 186 | // is returning 0 as timestamp 187 | //assert(m_last_pkt_ts_sec > 0); 188 | 189 | if (pktIdx == 0) { 190 | printf_verbose("First pkt timestamp is %f and there are %zu pkts; target duration is %f\n", 191 | m_first_pkt_ts_sec, m_num_input_pkts, m_new_duration_secs); 192 | 193 | if (m_first_pkt_ts_sec == 0) { 194 | printf_error( 195 | "WARNING: invalid timestamp set to zero (Thursday, 1 January 1970 00:00:00) for the first packet. This is unusual and typically indicates an invalid PCAP file.\n"); 196 | 197 | // make sure we set the timestamp to ZERO since the incoming packet has probably invalid timestamp: 198 | pktOut.copy(pktIn.header(), pktIn.data()); 199 | pktOut.set_timestamp_from_seconds(m_first_pkt_ts_sec); 200 | pktWasChangedOut = true; 201 | 202 | } else { 203 | pktWasChangedOut = false; // no proc done, use original packet 204 | } 205 | 206 | } else { 207 | double thisPktTs = 0; 208 | 209 | if (m_proc_mode == PROCMODE_CHANGE_DURATION_RESET_IFG) { 210 | double secInterPktGap = m_new_duration_secs / m_num_input_pkts; 211 | thisPktTs = m_first_pkt_ts_sec + secInterPktGap * (pktIdx + 1); 212 | } else // PROCMODE_CHANGE_DURATION_PRESERVE_IFG 213 | { 214 | // this code executes only during the second pass, so m_last_pkt_ts_sec is now valid: 215 | double originalDuration = m_last_pkt_ts_sec - m_first_pkt_ts_sec; // constant for the whole PCAP of course 216 | double pktTsOffsetSincePcapStart = pktIn.pcap_timestamp_to_seconds() - m_first_pkt_ts_sec; 217 | 218 | double newPktOffsetSincePcapStart = pktTsOffsetSincePcapStart * (m_new_duration_secs / originalDuration); 219 | thisPktTs = m_first_pkt_ts_sec + newPktOffsetSincePcapStart; 220 | 221 | // printf_verbose("pkt %u: original ts=%f, currentIFG=%f\n", 222 | // m_first_pkt_ts_sec, m_num_input_pkts, m_new_duration_secs); 223 | } 224 | 225 | pktOut.copy(pktIn.header(), pktIn.data()); 226 | pktOut.set_timestamp_from_seconds(thisPktTs); 227 | pktWasChangedOut = true; 228 | } 229 | 230 | m_previous_pkt_ts_sec = pktIn.pcap_timestamp_to_seconds(); 231 | } 232 | } break; 233 | 234 | case PROCMODE_SET_TIMESTAMPS: { 235 | if (pktIdx >= m_timestamps.size()) { 236 | printf_error( 237 | "Too few timestamps specified in the file with timestamps '%s': " 238 | "found %zu but input PCAP has more than %zu packets.\n", 239 | m_timestamps_input_file.c_str(), m_timestamps.size(), 240 | m_timestamps.size()); 241 | return false; // abort processing! 242 | } 243 | 244 | pktOut.copy(pktIn.header(), pktIn.data()); 245 | pktOut.set_timestamp_from_seconds(m_timestamps[pktIdx]); 246 | 247 | pktWasChangedOut = true; 248 | } break; 249 | 250 | default: 251 | assert(0); 252 | return false; 253 | } 254 | 255 | return true; 256 | } 257 | 258 | bool TimestampPacketProcessor::print_timestamp_analysis() // internal helper function 259 | { 260 | if (m_num_input_pkts == 1) { 261 | // corner case: PCAP with just 1 packet... duration is zero by definition: 262 | if (g_config.m_quiet) 263 | printf_quiet("%.6f\n", 0.0f); // be machine-friendly and indicate an error 264 | else 265 | printf_normal("The PCAP contains just 1 packet: duration is %.2fsec.\n", 0.0f); 266 | 267 | return true; 268 | } 269 | 270 | if (m_first_pkt_ts_sec <= 0 && m_last_pkt_ts_sec <= 0) { 271 | // corner case: negative or null timestamps at start/end of the PCAP 272 | if (g_config.m_quiet) 273 | printf_quiet("%.6f\n", -1.0f); // be machine-friendly and indicate an error 274 | else 275 | printf_normal("Apparently both the first and last packet packets of the PCAP have no valid timestamp... cannot compute PCAP duration.\n"); 276 | return false; 277 | } 278 | 279 | if (m_first_pkt_ts_sec < SMALL_NUM && m_last_pkt_ts_sec == SMALL_NUM) { 280 | // another corner case: close-to-zero timestamps 281 | if (g_config.m_quiet) 282 | printf_quiet("%.6f\n", -1.0f); // be machine-friendly and indicate an error 283 | else 284 | printf_normal("Apparently the packets of the PCAP have no valid timestamp (extremely small at least)... cannot compute PCAP duration.\n"); 285 | 286 | return false; 287 | } 288 | 289 | // normal case: 290 | double duration_sec = m_last_pkt_ts_sec - m_first_pkt_ts_sec; 291 | 292 | if (g_config.m_quiet) 293 | printf_quiet("%.6f\n", duration_sec); // be machine-friendly 294 | else 295 | printf_normal("Last packet has a timestamp offset = %.2fsec = %.2fmin = %.2fhours\n", 296 | duration_sec, duration_sec / 60.0, duration_sec / 3600.0); 297 | 298 | printf_verbose("Bytes loaded from PCAP = %lukiB = %luMiB; total bytes on wire = %lukiB = %luMiB\n", 299 | m_nbytes_pcap / KB, m_nbytes_pcap / MB, m_nbytes_original / KB, m_nbytes_original / MB); 300 | if (m_nbytes_pcap == m_nbytes_original) 301 | printf_verbose(" => all packets in the PCAP have been captured WITHOUT truncation.\n"); 302 | 303 | if (duration_sec > 0) { 304 | printf_normal("Tcpreplay should replay this PCAP at an average of %.2fMbps / %.2fpps to respect PCAP timings.\n", 305 | (float)(8 * m_nbytes_pcap / MB) / duration_sec, (float)m_num_input_pkts / duration_sec); 306 | } else { 307 | printf_normal("Cannot compute optimal tcpreplay speed for replaying: duration is null or negative.\n"); 308 | return false; 309 | } 310 | return true; 311 | } 312 | 313 | bool TimestampPacketProcessor::post_processing(const std::string& /*infile*/, unsigned int totNumPkts) 314 | { 315 | bool timestamp_analysis_success = true; 316 | if (m_print_timestamp_analysis) 317 | timestamp_analysis_success = print_timestamp_analysis(); 318 | 319 | bool timestamp_change_success = true; 320 | switch (m_proc_mode) { 321 | case PROCMODE_NONE: 322 | case PROCMODE_CHANGE_DURATION_RESET_IFG: 323 | case PROCMODE_CHANGE_DURATION_PRESERVE_IFG: 324 | timestamp_change_success = true; // no error 325 | break; 326 | 327 | case PROCMODE_SET_TIMESTAMPS: { 328 | if (totNumPkts < m_timestamps.size()) { 329 | printf_error("Too many timestamps specified in the file with timestamps '%s': %zu but input PCAP has %zu packets.\n", 330 | m_timestamps_input_file.c_str(), m_timestamps.size(), 331 | totNumPkts); 332 | timestamp_change_success = false; 333 | } else 334 | timestamp_change_success = true; 335 | } break; 336 | 337 | default: 338 | assert(0); 339 | return false; 340 | } 341 | 342 | return timestamp_analysis_success && timestamp_change_success; 343 | } 344 | -------------------------------------------------------------------------------- /src/timestamp_pkt_processor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * timestamp_pkt_processor.h 3 | * 4 | * Author: Francesco Montorsi 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef TIMESTAMP_PKT_PROCESSOR_H_ 26 | #define TIMESTAMP_PKT_PROCESSOR_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include "processor.h" 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | //------------------------------------------------------------------------------ 39 | // Types 40 | //------------------------------------------------------------------------------ 41 | 42 | enum TimestampProcessingModes { 43 | PROCMODE_NONE, 44 | PROCMODE_CHANGE_DURATION_RESET_IFG, 45 | PROCMODE_CHANGE_DURATION_PRESERVE_IFG, 46 | PROCMODE_SET_TIMESTAMPS 47 | }; 48 | 49 | //------------------------------------------------------------------------------ 50 | // TimestampPacketProcessor 51 | // Packet processor specialized in manipulation of packet timestamps 52 | //------------------------------------------------------------------------------ 53 | 54 | class TimestampPacketProcessor : public IPacketProcessor { 55 | public: 56 | TimestampPacketProcessor() 57 | { 58 | // config 59 | m_proc_mode = PROCMODE_NONE; 60 | m_print_timestamp_analysis = false; 61 | m_new_duration_secs = 0; 62 | 63 | // status 64 | m_first_pkt_ts_sec = 0; 65 | m_last_pkt_ts_sec = 0; 66 | m_previous_pkt_ts_sec = 0; 67 | m_num_input_pkts = 0; 68 | m_nbytes_pcap = 0; 69 | m_nbytes_original = 0; 70 | } 71 | 72 | ~TimestampPacketProcessor() { } 73 | 74 | bool prepare_processor(bool print_timestamp_analysis, const std::string& set_duration, bool preserve_ifg, const std::string& timestamp_file); 75 | 76 | virtual bool is_some_processing_active() const 77 | { 78 | return m_proc_mode != PROCMODE_NONE; 79 | } 80 | 81 | // to compute correctly the timestamps in --set-duration mode, we need 2 82 | // passes: first to find out how many packets are present in the PCAP and then 83 | // to actually alter timestamps: 84 | virtual bool needs_2passes() const override 85 | { 86 | return m_proc_mode == PROCMODE_CHANGE_DURATION_RESET_IFG || m_proc_mode == PROCMODE_CHANGE_DURATION_PRESERVE_IFG; 87 | } 88 | 89 | // does 90 | virtual bool process_packet(const Packet& pktIn, Packet& pktOut, unsigned int pktIdx, bool& pktWasChangedOut) override; 91 | 92 | virtual bool post_processing(const std::string& file, unsigned int totNumPkts) override; 93 | 94 | protected: 95 | bool print_timestamp_analysis(); 96 | 97 | private: 98 | // configuration: 99 | TimestampProcessingModes m_proc_mode; 100 | bool m_print_timestamp_analysis; 101 | double m_new_duration_secs; 102 | std::vector m_timestamps; // timestamps loaded from input file, to apply to all packets 103 | std::string m_timestamps_input_file; 104 | 105 | // status: 106 | double m_first_pkt_ts_sec; 107 | double m_last_pkt_ts_sec; 108 | double m_previous_pkt_ts_sec; 109 | unsigned long m_num_input_pkts; 110 | uint64_t m_nbytes_pcap; 111 | uint64_t m_nbytes_original; 112 | }; 113 | 114 | #endif // TIMESTAMP_PKT_PROCESSOR_H_ 115 | -------------------------------------------------------------------------------- /src/trafficstats_pkt_processor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * trafficstats_pkt_processor.cpp 3 | * 4 | * Author: Giovanni Tosatti 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | //------------------------------------------------------------------------------ 26 | // Includes 27 | //------------------------------------------------------------------------------ 28 | 29 | #include "trafficstats_pkt_processor.h" 30 | #include "large-pcap-analyzer.h" 31 | #include "printf_helpers.h" 32 | 33 | #include 34 | #include 35 | #include 36 | #include /* includes net/ethernet.h */ 37 | #include 38 | #include /* superset of previous */ 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | //------------------------------------------------------------------------------ 45 | // Globals 46 | //------------------------------------------------------------------------------ 47 | 48 | //------------------------------------------------------------------------------ 49 | // TrafficStatsPacketProcessor 50 | //------------------------------------------------------------------------------ 51 | 52 | bool TrafficStatsPacketProcessor::process_packet(const Packet& pktIn, Packet& pktOut, unsigned int pktIdx, bool& pktWasChangedOut) 53 | { 54 | (void)pktIdx; // not used 55 | pktWasChangedOut = false; // this processor never edits the packet 56 | pktOut = pktIn; // this processor never edits the packet 57 | m_num_input_pkts++; 58 | 59 | ParserRetCode_t ret = GPRC_NOT_GTPU_PKT; 60 | FlowInfo flow_id; 61 | int offset_transport = 0, ip_proto = 0; 62 | 63 | // parse the packet 64 | if (m_inner) { 65 | // detect if this is a GTPu-encapsulated packet or not 66 | ret = get_gtpu_inner_transport_start_offset(pktIn, &offset_transport, &ip_proto, NULL, &flow_id); 67 | if (ret == GPRC_NOT_GTPU_PKT) { 68 | // not a GTPu packet... look at the network/transport layers that are present: 69 | ret = get_transport_start_offset(pktIn, &offset_transport, &ip_proto, NULL, &flow_id); 70 | } 71 | } else { 72 | // just check the outer layer also for GTPu-encapsulated packets 73 | ret = get_transport_start_offset(pktIn, &offset_transport, &ip_proto, NULL, &flow_id); 74 | } 75 | 76 | if (UNLIKELY(ret != GPRC_VALID_PKT)) { 77 | m_num_parse_failed_pkts++; 78 | return true; // keep going, don't stop here 79 | } 80 | 81 | // flow hash lookup 82 | flow_hash_t hash = flow_id.compute_flow_hash(); 83 | traffic_stats_by_flow_t::iterator itr = m_conn_map.find(hash); 84 | if (itr != m_conn_map.end()) { 85 | // This flow is already present -- just update its stats 86 | itr->second.update_stats(pktIn.len()); 87 | } else { 88 | // This is the first packet of a new flow -- add it to the map 89 | m_conn_map.insert(std::make_pair(hash, FlowStats(flow_id, pktIn.len()))); 90 | } 91 | 92 | return true; 93 | } 94 | 95 | bool TrafficStatsPacketProcessor::post_processing(const std::string& /* infile */, unsigned int /* totNumPkts */) 96 | { 97 | // summarize the result so far 98 | printf_normal("Packet parsing failed for %lu/%lu pkts. Total number of packets/flows detected: %ld/%zu.\n", 99 | m_num_parse_failed_pkts, m_num_input_pkts, m_num_input_pkts - m_num_parse_failed_pkts, m_conn_map.size()); 100 | 101 | // Create a new temp map to sort the connections based on number of packets (key sorted in descending order) 102 | std::multimap> temp; 103 | uint64_t total_pkts = 0, total_bytes = 0; 104 | for (auto conn : m_conn_map) { 105 | uint64_t n_packets = conn.second.get_packets(); 106 | const FlowStats& stats = conn.second; 107 | temp.insert(std::make_pair(n_packets, stats)); 108 | total_pkts += n_packets; 109 | total_bytes += conn.second.get_bytes(); 110 | } 111 | 112 | // If needed, save the report in an output CSV file 113 | std::ofstream fout; 114 | if (!m_report_outfile.empty()) { 115 | fout.open(m_report_outfile); 116 | if (!fout) { 117 | printf_error("Error opening the output file [%s]: %s\n", m_report_outfile.c_str(), strerror(errno)); 118 | return false; 119 | } 120 | } 121 | 122 | // Print first TOP N entries of "temp" 123 | unsigned int nflow = 0; 124 | char csv_line[8192]; 125 | for (auto conn_top : temp) { 126 | if (nflow >= m_topflow_max /* one of the topN flows?*/ && m_topflow_max > 0 /* print all flows? */) { 127 | // stop here the processing: the topN flows have been processed 128 | break; 129 | } 130 | 131 | if (nflow == 0) { 132 | std::string csv_header = "flow_num,num_pkts,%pkts,num_bytes,%bytes,flow_hash,ip_src,ip_dst,ip_proto,port_src,port_dst"; // all columns 133 | if (fout.is_open()) { 134 | fout << csv_header << std::endl; 135 | } else { 136 | printf_normal("Traffic report in CSV format:\n%s\n", csv_header.c_str()); 137 | } 138 | } 139 | 140 | // prepare CSV line 141 | double pkt_percentage = ((double)(conn_top.second.get_packets()) / total_pkts) * 100; 142 | double bytes_percentage = ((double)(conn_top.second.get_bytes()) / total_bytes) * 100; 143 | snprintf(csv_line, sizeof(csv_line), "%u,%lu,%.2f,%lu,%.2f,%lX,%s,%s,%d,%d,%d\n", 144 | nflow, 145 | conn_top.second.get_packets(), 146 | pkt_percentage, 147 | conn_top.second.get_bytes(), 148 | bytes_percentage, 149 | conn_top.second.get_flow_info().get_flow_hash(), 150 | conn_top.second.get_flow_info().m_ip_src.toString().c_str(), 151 | conn_top.second.get_flow_info().m_ip_dst.toString().c_str(), 152 | conn_top.second.get_flow_info().m_ip_proto, 153 | conn_top.second.get_flow_info().m_port_src, 154 | conn_top.second.get_flow_info().m_port_dst); 155 | 156 | // write output 157 | if (fout.is_open()) { 158 | fout << csv_line; 159 | } else { 160 | printf_normal("%s", csv_line); 161 | } 162 | 163 | nflow++; 164 | } 165 | if (fout.is_open()) 166 | printf_normal("Written %lu lines in CSV traffic report output file %s.\n", nflow, m_report_outfile.c_str()); 167 | else 168 | printf_normal("Completed generation of %lu lines of traffic report.\n", nflow); 169 | 170 | // Clear current stats/map 171 | m_num_input_pkts = 0; 172 | m_conn_map.clear(); 173 | 174 | return true; 175 | } 176 | -------------------------------------------------------------------------------- /src/trafficstats_pkt_processor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * trafficstats_pkt_processor.h 3 | * 4 | * Author: Giovanni Tosatti 5 | * Website: https://github.com/f18m/large-pcap-analyzer 6 | * 7 | * LICENSE: 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 | MA 02110-1301, USA. 22 | 23 | */ 24 | 25 | #ifndef TRAFFIC_STATS_PROCESSOR_H_ 26 | #define TRAFFIC_STATS_PROCESSOR_H_ 27 | 28 | //------------------------------------------------------------------------------ 29 | // Includes 30 | //------------------------------------------------------------------------------ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "parse.h" 39 | #include "processor.h" 40 | 41 | #include 42 | #include 43 | #include 44 | 45 | //------------------------------------------------------------------------------ 46 | // FlowStats 47 | // Packet processor specialized in manipulation of packet to extract Traffic Stats 48 | //------------------------------------------------------------------------------ 49 | 50 | class FlowStats { 51 | public: 52 | FlowStats() 53 | { 54 | } 55 | FlowStats(const FlowInfo& info, uint64_t first_pkt_len) 56 | { 57 | m_flow_info = info; 58 | update_stats(first_pkt_len); 59 | } 60 | FlowInfo& get_flow_info() 61 | { 62 | return m_flow_info; 63 | } 64 | void update_stats(uint64_t new_pkt_len) 65 | { 66 | m_npackets++; 67 | m_nbytes += new_pkt_len; 68 | } 69 | 70 | uint64_t get_packets() const 71 | { 72 | return m_npackets; 73 | } 74 | uint64_t get_bytes() const 75 | { 76 | return m_nbytes; 77 | } 78 | 79 | private: 80 | // identifier of the flow: 81 | FlowInfo m_flow_info; 82 | // stats about this flow: 83 | uint64_t m_npackets = 0; 84 | uint64_t m_nbytes = 0; 85 | }; 86 | 87 | typedef std::unordered_map traffic_stats_by_flow_t; 88 | 89 | //------------------------------------------------------------------------------ 90 | // TrafficStatsPacketProcessor 91 | // Packet processor specialized in manipulation of packet to extract Traffic Stats 92 | //------------------------------------------------------------------------------ 93 | 94 | class TrafficStatsPacketProcessor : public IPacketProcessor { 95 | public: 96 | TrafficStatsPacketProcessor() 97 | { 98 | } 99 | 100 | ~TrafficStatsPacketProcessor() { } 101 | 102 | bool prepare_processor(bool inner, unsigned int topflow_max, const std::string& report_outfile) 103 | { 104 | m_inner = inner; 105 | m_topflow_max = topflow_max; 106 | m_report_outfile = report_outfile; 107 | return true; 108 | } 109 | 110 | // does 111 | virtual bool process_packet(const Packet& pktIn, Packet& pktOut, unsigned int pktIdx, bool& pktWasChangedOut) override; 112 | virtual bool post_processing(const std::string& file, unsigned int totNumPkts) override; 113 | 114 | private: 115 | // configuration: 116 | bool m_inner = false; 117 | unsigned int m_topflow_max = 0; 118 | std::string m_report_outfile; 119 | 120 | // status: 121 | uint64_t m_num_input_pkts = 0; 122 | uint64_t m_num_parse_failed_pkts = 0; 123 | traffic_stats_by_flow_t m_conn_map; 124 | }; 125 | 126 | #endif // TRAFFIC_STATS_PROCESSOR_H_ 127 | -------------------------------------------------------------------------------- /test-pcaps/invalid_timestamp1_negative_ts.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/invalid_timestamp1_negative_ts.pcap -------------------------------------------------------------------------------- /test-pcaps/invalid_timestamp2_zero_ts.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/invalid_timestamp2_zero_ts.pcap -------------------------------------------------------------------------------- /test-pcaps/ipv4_ftp.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/ipv4_ftp.pcap -------------------------------------------------------------------------------- /test-pcaps/ipv4_gtpc_single_pkt.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/ipv4_gtpc_single_pkt.pcap -------------------------------------------------------------------------------- /test-pcaps/ipv4_gtpu_fragmented.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/ipv4_gtpu_fragmented.pcap -------------------------------------------------------------------------------- /test-pcaps/ipv4_gtpu_https.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/ipv4_gtpu_https.pcap -------------------------------------------------------------------------------- /test-pcaps/ipv4_sctp_iua.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/ipv4_sctp_iua.pcap -------------------------------------------------------------------------------- /test-pcaps/ipv4_tcp_flags.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/ipv4_tcp_flags.pcap -------------------------------------------------------------------------------- /test-pcaps/timestamps-30pkts.txt: -------------------------------------------------------------------------------- 1 | 1549740000.000000000 2 | 1549740000.001947000 3 | 1549740000.068559000 4 | 1549740000.070674000 5 | 1549740000.193331000 6 | 1549740000.260293000 7 | 1549740000.261555000 8 | 1549740000.324570000 9 | 1549740001.312409000 10 | 1549740001.312413000 11 | 1549740001.458407000 12 | 1549740001.460801000 13 | 1549740001.460850000 14 | 1549740001.460909000 15 | 1549740001.589370000 16 | 1549740001.622833000 17 | 1549740001.624358000 18 | 1549740001.770166000 19 | 1549740001.771399000 20 | 1549740001.881553000 21 | 1549740001.883478000 22 | 1549740002.032125000 23 | 1549740002.033322000 24 | 1549740002.163551000 25 | 1549740002.166581000 26 | 1549740002.275086000 27 | 1549740002.275092000 28 | 1549740003.422629000 29 | 1549740003.423425000 30 | 1549740004.000000000 31 | -------------------------------------------------------------------------------- /test-pcaps/timestamps-invalid.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | a string is invalid here! 5 | 4 -------------------------------------------------------------------------------- /test-pcaps/timing-test.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f18m/large-pcap-analyzer/f0f0d602ea311e120898a5670a924d1fd718412f/test-pcaps/timing-test.pcap -------------------------------------------------------------------------------- /test-pcaps/traffic_ipv4_ftp.csv: -------------------------------------------------------------------------------- 1 | flow_num,num_pkts,%pkts,num_bytes,%bytes,flow_hash,ip_src,ip_dst,ip_proto,port_src,port_dst 2 | 0,40440,99.93,41415294,99.99,A9112AFAB5DB7ECF,192.168.101.153,10.4.72.4,6,1091,55274 3 | 1,22,0.05,1727,0.00,87B2121BA811F176,192.168.101.153,10.4.72.4,6,1090,21 4 | 2,8,0.02,639,0.00,4197295C4F955483,192.168.101.153,10.4.72.4,6,1074,21 5 | -------------------------------------------------------------------------------- /test-pcaps/traffic_ipv4_gtpu_https.csv: -------------------------------------------------------------------------------- 1 | flow_num,num_pkts,%pkts,num_bytes,%bytes,flow_hash,ip_src,ip_dst,ip_proto,port_src,port_dst 2 | 0,5562,30.56,5779325,34.98,5346E247DCBCA579,10.85.73.237,202.122.145.141,6,49789,443 3 | 1,5043,27.71,5262864,31.86,359E06A6A0671690,10.85.73.237,202.122.145.141,6,40461,443 4 | 2,4328,23.78,4470177,27.06,D0D80A5F6A8B3F4F,10.85.73.237,202.122.145.141,6,50059,443 5 | 3,849,4.66,214889,1.30,13111CC415F92E54,10.85.73.237,58.71.141.53,6,49473,10000 6 | 4,209,1.15,46214,0.28,5255AFFF70F6BFB5,10.85.73.237,58.71.141.53,6,49407,10000 7 | 5,144,0.79,33478,0.20,DE86E0ED622568E9,10.85.73.237,208.67.76.95,6,49461,443 8 | 6,112,0.62,74928,0.45,EC297958A1CA3946,10.85.73.237,216.58.196.74,6,54805,443 9 | 7,101,0.55,28010,0.17,E26097004F952A54,10.85.73.237,208.67.76.95,6,49460,443 10 | 8,59,0.32,10551,0.06,D3CD1D4C2E38C2C2,10.85.73.237,121.123.204.19,6,49458,80 11 | 9,56,0.31,17508,0.11,54FD688F6BAAAF89,10.85.73.237,65.52.108.154,6,49453,443 12 | 10,55,0.30,13279,0.08,ED0A432DF2A5EE81,10.85.73.237,208.67.76.95,6,49467,443 13 | 11,54,0.30,32983,0.20,EF9D8687291701FA,10.85.73.237,173.194.120.104,6,35885,443 14 | 12,54,0.30,19207,0.12,6A46E413E1F259E2,10.85.73.237,208.67.76.95,6,49465,443 15 | 13,53,0.29,39033,0.24,9EE97DF5BC0F842A,10.85.73.237,216.58.196.74,6,34761,443 16 | 14,52,0.29,29551,0.18,9BB77760C3836AA4,10.85.73.237,216.58.196.74,6,41784,443 17 | 15,51,0.28,35355,0.21,225A44584805B3D1,10.85.73.237,216.58.196.46,6,40092,443 18 | 16,49,0.27,13900,0.08,237BC559BF742923,10.85.73.237,208.67.76.95,6,49466,443 19 | 17,43,0.24,31137,0.19,DA9BAF5E595C770E,10.85.73.237,216.58.196.46,6,55685,443 20 | 18,41,0.23,26149,0.16,60178460EFC20F6F,10.85.73.237,74.125.200.101,6,43318,443 21 | 19,41,0.23,6380,0.04,67B5368B388FE45,10.85.73.237,31.13.67.3,6,36094,443 22 | 20,40,0.22,7536,0.05,F104F3E6A9B65DFF,10.85.73.237,31.13.67.37,6,34917,443 23 | 21,38,0.21,5666,0.03,F5D34E6F0353242A,10.85.73.237,31.13.67.3,6,43656,443 24 | 22,38,0.21,20322,0.12,706E755AADC90CFF,10.85.73.237,173.194.120.104,6,40495,443 25 | 23,35,0.19,10172,0.06,DF81BE607B4F19FE,10.85.73.237,40.122.131.63,6,49464,443 26 | 24,34,0.19,9799,0.06,C600ACA558C4E68,10.85.73.237,216.58.196.74,6,38038,443 27 | 25,33,0.18,21328,0.13,B49F85E701227BD7,10.85.73.237,216.58.196.46,6,38996,443 28 | 26,33,0.18,21976,0.13,A5E6029408072B7A,10.85.73.237,216.58.196.46,6,42438,443 29 | 27,31,0.17,4018,0.02,50F923050C11ADD3,10.85.73.237,184.173.147.53,6,36494,5222 30 | 28,29,0.16,17160,0.10,DD563202AB802DDE,10.85.73.237,216.58.196.46,6,51090,443 31 | 29,28,0.15,12991,0.08,AE07BC8FF5349D9E,10.85.73.237,173.194.126.5,6,40182,443 32 | 30,27,0.15,14597,0.09,C44E11B7A769D783,10.85.73.237,216.58.196.46,6,33025,443 33 | 31,26,0.14,5686,0.03,507912A1B61BA652,10.85.73.237,31.13.67.1,6,47919,443 34 | 32,26,0.14,5216,0.03,743AF4C7B211C1B2,10.85.73.237,31.13.67.37,6,34916,443 35 | 33,26,0.14,4210,0.03,C87D6363962C93ED,10.85.73.237,31.13.67.3,6,53978,443 36 | 34,25,0.14,9836,0.06,9A3B69B3AE986FA9,10.85.73.237,216.58.196.46,6,40993,443 37 | 35,23,0.13,8436,0.05,5F9D8CF3B19D480D,10.85.73.237,216.58.208.35,6,40788,443 38 | 36,23,0.13,10112,0.06,C96FD6E35DE5D280,10.85.73.237,111.221.29.254,6,49454,443 39 | 37,23,0.13,5390,0.03,FE3F4B0678682B62,10.85.73.237,31.13.67.52,6,39374,443 40 | 38,22,0.12,4092,0.02,CA17F2249AD869C1,10.85.73.237,31.13.67.3,6,54527,443 41 | 39,21,0.12,4781,0.03,596479170DD0904F,10.85.73.237,31.13.67.1,6,48615,443 42 | 40,20,0.11,3165,0.02,41A5C8D9684268D4,10.85.73.237,31.13.67.3,6,56109,443 43 | 41,20,0.11,3164,0.02,15DB9D072944DDB1,10.85.73.237,31.13.67.3,6,60160,443 44 | 42,20,0.11,7669,0.05,C7B4584008A17A84,10.85.73.237,173.194.126.57,6,34202,443 45 | 43,20,0.11,7287,0.04,8354944624F62D64,10.85.73.237,31.13.67.1,6,54271,443 46 | 44,18,0.10,2952,0.02,8227E65D6BDAFF62,10.85.73.237,31.13.67.3,6,36734,443 47 | 45,18,0.10,2955,0.02,4A0DC1B9D0AFAD5C,10.85.73.237,31.13.67.3,6,58054,443 48 | 46,17,0.09,5805,0.04,C327EF6C4D48E44A,10.85.73.237,208.67.76.95,6,42692,443 49 | 47,17,0.09,4605,0.03,8825501BDEF82D80,10.85.73.237,52.74.216.114,6,42064,5223 50 | 48,17,0.09,5773,0.03,1682BD92BF4F152,10.85.73.237,208.67.76.95,6,46779,443 51 | 49,17,0.09,3304,0.02,5D69F39C861E8BC4,10.85.73.237,184.26.255.163,6,49459,80 52 | 50,16,0.09,3162,0.02,B98CA2B6D3E05F18,10.85.73.237,74.125.130.188,6,34056,5228 53 | 51,15,0.08,2394,0.01,50AA1E2767CCC1CB,10.85.73.237,31.13.67.3,6,40525,443 54 | 52,14,0.08,4248,0.03,AB0D3A64DE601E4F,10.85.73.237,208.67.76.95,6,47606,443 55 | 53,14,0.08,4248,0.03,4489144F121B4D8E,10.85.73.237,208.67.76.95,6,53006,443 56 | 54,13,0.07,2628,0.02,9B360EB1D56CFF79,10.85.73.237,31.13.67.3,6,42778,443 57 | 55,13,0.07,3628,0.02,78AA6B4A4AF65C49,10.85.73.237,208.67.76.95,6,50204,443 58 | 56,11,0.06,2787,0.02,8217C9EB1C3AF919,10.85.73.237,208.67.76.95,6,58910,443 59 | 57,11,0.06,2771,0.02,AEBD73BC02FE2E04,10.85.73.237,208.67.76.95,6,49803,443 60 | 58,11,0.06,1497,0.01,4EE6C5CD29D80E60,10.85.73.237,216.58.196.78,6,51105,80 61 | 59,11,0.06,2771,0.02,73503734D74C0EA7,10.85.73.237,208.67.76.95,6,33664,443 62 | 60,9,0.05,1782,0.01,1DE462F1F6BB6C9C,10.85.73.237,208.67.76.95,6,36357,443 63 | 61,7,0.04,1768,0.01,5696657FA58EF6D5,10.85.73.237,208.67.76.95,6,49333,443 64 | 62,5,0.03,1030,0.01,595E01886D484356,10.85.73.237,208.67.76.95,6,49322,443 65 | 63,5,0.03,1338,0.01,4CE92B0E54724634,10.85.73.237,208.67.76.95,6,49341,443 66 | 64,5,0.03,558,0.00,434D2CEFA854866F,10.85.73.237,216.58.196.74,6,38039,443 67 | 65,4,0.02,460,0.00,9712C284922AA285,10.85.73.237,173.194.126.5,6,58745,443 68 | 66,4,0.02,644,0.00,F29B6AFF133486B9,10.85.73.237,8.8.8.8,17,64789,53 69 | 67,4,0.02,706,0.00,644F48D66DD32ED4,10.85.73.237,8.8.8.8,17,57044,53 70 | 68,3,0.02,326,0.00,A43B883797EAC481,10.85.73.237,10.17.0.49,6,49489,443 71 | 69,3,0.02,326,0.00,1BD1F7EA654E3723,10.85.73.237,208.67.76.100,6,49485,5723 72 | 70,3,0.02,326,0.00,1B8706865FBC1C09,10.85.73.237,208.67.76.100,6,49484,8530 73 | 71,3,0.02,326,0.00,D858546DC7A555FE,10.85.73.237,208.67.76.100,6,49480,8530 74 | 72,3,0.02,326,0.00,CD26E5734F1304BB,10.85.73.237,192.168.116.25,6,49477,1688 75 | 73,3,0.02,326,0.00,EA8421CC00A0A621,10.85.73.237,208.67.76.100,6,49481,8530 76 | 74,3,0.02,326,0.00,750F8E55A3007750,10.85.73.237,208.67.76.100,6,49457,5723 77 | 75,3,0.02,326,0.00,850764E66B79D748,10.85.73.237,10.17.0.49,6,49482,443 78 | 76,3,0.02,326,0.00,D4E51F5072526C89,10.85.73.237,10.17.0.238,6,49483,80 79 | 77,3,0.02,326,0.00,D1329BAD7D408417,10.85.73.237,10.17.0.49,6,49456,443 80 | 78,3,0.02,326,0.00,AB0636B3C5671929,10.85.73.237,10.17.0.238,6,49488,80 81 | 79,3,0.02,326,0.00,43F7DB09720677F9,10.85.73.237,208.67.76.100,6,49463,5723 82 | 80,3,0.02,326,0.00,295E893DDD47A72D,10.85.73.237,10.17.0.49,6,49462,443 83 | 81,3,0.02,326,0.00,7457CD0C91246482,10.85.73.237,10.17.0.49,6,49471,443 84 | 82,3,0.02,326,0.00,C87DD1BAA3D5D3F7,10.85.73.237,10.17.0.49,6,49468,443 85 | 83,3,0.02,326,0.00,B1FA316E49924283,10.85.73.237,208.67.76.100,6,49470,5723 86 | 84,3,0.02,326,0.00,E740A778850F036C,10.85.73.237,10.17.0.238,6,49490,80 87 | 85,3,0.02,326,0.00,A4B2F0E9E8677E9A,10.85.73.237,208.67.76.100,6,49487,8531 88 | 86,3,0.02,326,0.00,B7625410FD4FEE62,10.85.73.237,10.17.0.238,6,49474,80 89 | 87,3,0.02,326,0.00,2F77300026911C30,10.85.73.237,192.168.116.25,6,49455,1688 90 | 88,3,0.02,326,0.00,C6CE3D8D84E3E922,10.85.73.237,208.67.76.100,6,49475,8530 91 | 89,2,0.01,411,0.00,F0D641CFF0922A99,10.85.73.237,58.71.132.10,17,17537,53 92 | 90,2,0.01,411,0.00,489146F2B8D9DE9F,10.85.73.237,58.71.132.10,17,2961,53 93 | 91,2,0.01,327,0.00,83D3BCD7FAD3F822,10.85.73.237,8.8.8.8,17,62990,53 94 | 92,2,0.01,300,0.00,1975F611AA3227DF,10.85.73.237,8.8.8.8,17,59178,53 95 | 93,2,0.01,309,0.00,6F35BFB26BA13BCA,10.85.73.237,8.8.8.8,17,53706,53 96 | 94,2,0.01,329,0.00,BE9F995291F78BD5,10.85.73.237,8.8.8.8,17,55090,53 97 | 95,2,0.01,302,0.00,7373DB8237F45E2E,10.85.73.237,8.8.8.8,17,50974,53 98 | 96,2,0.01,296,0.00,BADC0F0F3B05AF17,10.85.73.237,8.8.8.8,17,52158,53 99 | 97,2,0.01,220,0.00,31574999C963E0BB,10.85.73.237,10.17.0.238,6,49479,80 100 | 98,2,0.01,295,0.00,C3DD383DF9164B58,10.85.73.237,8.8.8.8,17,60236,53 101 | 99,2,0.01,288,0.00,B8AFB9DAE708FD6C,10.85.73.237,8.8.8.8,17,60274,53 102 | 100,2,0.01,282,0.00,32291B2D78D9A8E0,10.85.73.237,8.8.8.8,17,50683,53 103 | 101,2,0.01,431,0.00,D2C251E6AF734321,10.85.73.237,8.8.8.8,17,63043,53 104 | 102,2,0.01,299,0.00,C39E906E5209C304,10.85.73.237,8.8.8.8,17,51397,53 105 | 103,2,0.01,294,0.00,240F99A598E78A4D,10.85.73.237,8.8.8.8,17,63919,53 106 | 104,2,0.01,441,0.00,4221119D9C475A85,10.85.73.237,58.71.132.10,17,28624,53 107 | 105,2,0.01,289,0.00,D7AEA0CE813AFEE2,10.85.73.237,8.8.8.8,17,65179,53 108 | 106,2,0.01,302,0.00,4B24B286572A2923,10.85.73.237,8.8.8.8,17,52959,53 109 | 107,2,0.01,317,0.00,10D1A5EFABBF6DCE,10.85.73.237,8.8.8.8,17,59612,53 110 | 108,2,0.01,347,0.00,70775762160223F1,10.85.73.237,8.8.8.8,17,57947,53 111 | 109,2,0.01,317,0.00,A654B4EF907E5168,10.85.73.237,8.8.8.8,17,55775,53 112 | 110,2,0.01,337,0.00,748316CB19C6467D,10.85.73.237,8.8.8.8,17,50883,53 113 | 111,2,0.01,271,0.00,155FFA73FD971FE8,10.85.73.237,58.71.132.10,17,46563,53 114 | 112,2,0.01,314,0.00,4786A2D2D3C5D4E2,10.85.73.237,8.8.8.8,17,55246,53 115 | 113,2,0.01,411,0.00,94A066207332BCC,10.85.73.237,58.71.132.10,17,5195,53 116 | 114,2,0.01,294,0.00,C944312FF295C02,10.85.73.237,8.8.8.8,17,51558,53 117 | 115,2,0.01,286,0.00,60B75B8B52A5711B,10.85.73.237,8.8.8.8,17,58342,53 118 | 116,2,0.01,290,0.00,33B30BB048E2E15C,10.85.73.237,58.71.132.10,17,13119,53 119 | 117,2,0.01,356,0.00,2C423CFF6BB0A3FF,10.85.73.237,8.8.8.8,17,52355,53 120 | 118,2,0.01,392,0.00,17EB2D0C700558AE,10.85.73.237,8.8.8.8,17,63422,53 121 | 119,2,0.01,322,0.00,C53806AE158B9353,10.85.73.237,8.8.8.8,17,61829,53 122 | 120,2,0.01,291,0.00,894557E40240F29A,10.85.73.237,8.8.8.8,17,58514,53 123 | 121,2,0.01,286,0.00,F050D61B636723EF,10.85.73.237,8.8.8.8,17,62099,53 124 | 122,2,0.01,322,0.00,2EE7BF01EE1E698B,10.85.73.237,8.8.8.8,17,52064,53 125 | 123,2,0.01,352,0.00,707BCB33E0D98A90,10.85.73.237,8.8.8.8,17,65467,53 126 | 124,2,0.01,298,0.00,609C433F130552E7,10.85.73.237,58.71.132.10,17,41546,53 127 | 125,2,0.01,326,0.00,2052373D01BA2883,10.85.73.237,58.71.132.10,17,58450,53 128 | 126,2,0.01,254,0.00,26441F422DB880C,10.85.73.237,58.71.132.10,17,26242,53 129 | 127,2,0.01,411,0.00,FD4D0B4A6F039006,10.85.73.237,58.71.132.10,17,3171,53 130 | 128,2,0.01,287,0.00,97D260E33ED2EBB5,10.85.73.237,208.67.76.6,17,49882,53 131 | 129,2,0.01,411,0.00,E46A22515B1E07C2,10.85.73.237,58.71.132.10,17,46106,53 132 | 130,2,0.01,381,0.00,36425D16E213C73,10.85.73.237,58.71.132.10,17,3333,53 133 | 131,2,0.01,250,0.00,AA4CB6F99357004F,10.85.73.237,58.71.132.10,17,25398,53 134 | 132,2,0.01,327,0.00,86B6831BFB1D3AD2,10.85.73.237,8.8.8.8,17,53830,53 135 | 133,2,0.01,250,0.00,130BBBB466556118,10.85.73.237,8.8.8.8,17,55117,53 136 | 134,2,0.01,292,0.00,CF6F707308E2B0D8,10.85.73.237,8.8.8.8,17,50366,53 137 | 135,2,0.01,352,0.00,448D0E3DCACAAB45,10.85.73.237,8.8.8.8,17,62248,53 138 | 136,2,0.01,352,0.00,B929AB4C723A8AC3,10.85.73.237,8.8.8.8,17,49603,53 139 | 137,2,0.01,352,0.00,4417D380D1EF5239,10.85.73.237,8.8.8.8,17,64379,53 140 | 138,2,0.01,262,0.00,DABFC29D09FD174C,10.85.73.237,8.8.8.8,17,58920,53 141 | 139,2,0.01,411,0.00,FF9D61DAD3D8810F,10.85.73.237,58.71.132.10,17,23684,53 142 | 140,2,0.01,307,0.00,619E670BDF2F5AFA,10.85.73.237,8.8.8.8,17,59952,53 143 | 141,2,0.01,292,0.00,3CF41C0AF69C1978,10.85.73.237,8.8.8.8,17,49919,53 144 | 142,2,0.01,302,0.00,6B0B37B33729DB3,10.85.73.237,8.8.8.8,17,50770,53 145 | 143,2,0.01,297,0.00,30E66B51594AF1A9,10.85.73.237,58.71.132.10,17,26978,53 146 | 144,2,0.01,322,0.00,7C4CB5261FF16E7F,10.85.73.237,8.8.8.8,17,59627,53 147 | 145,2,0.01,322,0.00,881D14176E080904,10.85.73.237,8.8.8.8,17,59248,53 148 | 146,2,0.01,375,0.00,62E3844FC66631C8,10.85.73.237,8.8.8.8,17,61904,53 149 | 147,2,0.01,281,0.00,CB6D6B1EB46DAA30,10.85.73.237,58.71.132.10,17,16733,53 150 | 148,2,0.01,216,0.00,40BC0277E9EDC282,10.85.73.237,10.17.0.238,6,49486,80 151 | 149,2,0.01,456,0.00,4E291928F8F2CDF7,10.85.73.237,58.71.132.10,17,25190,53 152 | 150,2,0.01,441,0.00,2B76B7675BF1BF1C,10.85.73.237,58.71.132.10,17,1233,53 153 | 151,2,0.01,302,0.00,A8799CEE0E283662,10.85.73.237,8.8.8.8,17,49384,53 154 | 152,2,0.01,298,0.00,744CDA115CCB5041,10.85.73.237,58.71.132.10,17,42794,53 155 | 153,2,0.01,429,0.00,94AAC8F9666FEC82,10.85.73.237,8.8.8.8,17,61472,53 156 | 154,2,0.01,344,0.00,D8B5717CC04F8A6,10.85.73.237,8.8.8.8,17,62151,53 157 | 155,2,0.01,220,0.00,D95DCFB34F5C2A83,10.85.73.237,208.67.76.100,6,49472,5723 158 | 156,2,0.01,264,0.00,B93FAA097AE3D980,10.85.73.237,23.102.23.44,17,123,123 159 | 157,2,0.01,431,0.00,96713F7C661E0B5A,10.85.73.237,8.8.8.8,17,58811,53 160 | 158,2,0.01,252,0.00,83C1259EA10B9AC2,10.85.73.237,8.8.8.8,17,63796,53 161 | 159,2,0.01,315,0.00,4889688E771FBD89,10.85.73.237,8.8.8.8,17,50933,53 162 | 160,2,0.01,322,0.00,683B3BF1F5E1F289,10.85.73.237,8.8.8.8,17,63951,53 163 | 161,2,0.01,292,0.00,811403D76431A4FA,10.85.73.237,8.8.8.8,17,58978,53 164 | 162,2,0.01,322,0.00,AB4512BB09B87107,10.85.73.237,8.8.8.8,17,60467,53 165 | 163,2,0.01,352,0.00,1E5516D068D358F9,10.85.73.237,8.8.8.8,17,60615,53 166 | 164,2,0.01,352,0.00,C3763B23324B8ECC,10.85.73.237,8.8.8.8,17,56671,53 167 | 165,2,0.01,352,0.00,7CBE98B20A23BF87,10.85.73.237,8.8.8.8,17,49475,53 168 | 166,2,0.01,322,0.00,C7B9FE5508ADB1B4,10.85.73.237,8.8.8.8,17,55781,53 169 | 167,2,0.01,286,0.00,1A19B56BC5607A31,10.85.73.237,8.8.8.8,17,62003,53 170 | 168,2,0.01,302,0.00,9D9325AEBCA1ACEE,10.85.73.237,8.8.8.8,17,62283,53 171 | 169,2,0.01,344,0.00,21C0940603CEE95E,10.85.73.237,8.8.8.8,17,50135,53 172 | 170,2,0.01,319,0.00,DA84C58933E2651E,10.85.73.237,8.8.8.8,17,57580,53 173 | 171,2,0.01,320,0.00,473E5351335E36F8,10.85.73.237,58.71.132.10,17,62745,53 174 | 172,2,0.01,307,0.00,F5E762ED86F7DC31,10.85.73.237,8.8.8.8,17,54031,53 175 | 173,2,0.01,319,0.00,3DD900CAF7CF3ADF,10.85.73.237,58.71.132.10,17,32542,53 176 | 174,2,0.01,252,0.00,37652D351AE3A348,10.85.73.237,8.8.8.8,17,55950,53 177 | 175,2,0.01,322,0.00,648E61FEE059ECDC,10.85.73.237,8.8.8.8,17,49411,53 178 | 176,2,0.01,314,0.00,992B955B38DCAEBE,10.85.73.237,8.8.8.8,17,63055,53 179 | 177,2,0.01,356,0.00,BF57C6EED0970421,10.85.73.237,8.8.8.8,17,52169,53 180 | 178,2,0.01,352,0.00,D49A76B16D339950,10.85.73.237,8.8.8.8,17,63699,53 181 | 179,2,0.01,314,0.00,66B46498E26FC8E6,10.85.73.237,8.8.8.8,17,61870,53 182 | 180,2,0.01,250,0.00,D08823D9DC0E1B93,10.85.73.237,8.8.8.8,17,49346,53 183 | 181,2,0.01,323,0.00,8FA670486C5A6BA6,10.85.73.237,8.8.8.8,17,62878,53 184 | 182,2,0.01,352,0.00,4460EEDACD48334,10.85.73.237,8.8.8.8,17,51835,53 185 | 183,2,0.01,316,0.00,B7F3DEF84EA40E91,10.85.73.237,8.8.8.8,17,50917,53 186 | 184,2,0.01,411,0.00,256D6AF88AE63188,10.85.73.237,58.71.132.10,17,31319,53 187 | 185,2,0.01,352,0.00,90B0F3486AC72EC0,10.85.73.237,8.8.8.8,17,55352,53 188 | 186,2,0.01,344,0.00,7A3C666745B3868F,10.85.73.237,8.8.8.8,17,64421,53 189 | 187,2,0.01,352,0.00,940D6263EA10BD72,10.85.73.237,8.8.8.8,17,50991,53 190 | 188,2,0.01,322,0.00,79E981A6EE319D3,10.85.73.237,8.8.8.8,17,49861,53 191 | 189,2,0.01,319,0.00,2A06A8F1E9423710,10.85.73.237,8.8.8.8,17,56912,53 192 | 190,2,0.01,352,0.00,F64CE7754888262E,10.85.73.237,8.8.8.8,17,64298,53 193 | 191,2,0.01,356,0.00,D4206D2FB14D473B,10.85.73.237,8.8.8.8,17,57054,53 194 | 192,2,0.01,322,0.00,18FCC5294C22AAB3,10.85.73.237,8.8.8.8,17,49283,53 195 | 193,2,0.01,282,0.00,C50E47B4CFC223DF,10.85.73.237,58.71.132.10,17,21824,53 196 | 194,2,0.01,300,0.00,7A838101FE2FDEFD,10.85.73.237,8.8.8.8,17,59599,53 197 | 195,2,0.01,362,0.00,E4F35B171EE107B2,10.85.73.237,58.71.136.10,17,10702,53 198 | 196,2,0.01,287,0.00,6025F313043A5A3D,10.85.73.237,208.67.76.6,17,55014,53 199 | 197,2,0.01,290,0.00,C997AED7A001052D,10.85.73.237,58.71.132.10,17,20545,53 200 | 198,2,0.01,322,0.00,4FCCC88B7FD92332,10.85.73.237,8.8.8.8,17,55011,53 201 | 199,2,0.01,356,0.00,F9945D93778585EB,10.85.73.237,8.8.8.8,17,54131,53 202 | 200,2,0.01,254,0.00,70E31B08BB7EE198,10.85.73.237,8.8.8.8,17,63801,53 203 | 201,2,0.01,431,0.00,4CF396AE2D4D63B7,10.85.73.237,8.8.8.8,17,60976,53 204 | 202,2,0.01,375,0.00,DB8E0FBAB73AFEBF,10.85.73.237,8.8.8.8,17,65175,53 205 | 203,2,0.01,299,0.00,DB7F60EBCE43FC9B,10.85.73.237,8.8.8.8,17,54157,53 206 | 204,2,0.01,319,0.00,A6B4394AA37D87AA,10.85.73.237,8.8.8.8,17,52939,53 207 | 205,2,0.01,299,0.00,D0EC23A7E232E558,10.85.73.237,8.8.8.8,17,62862,53 208 | 206,2,0.01,284,0.00,DD06A591CF426E6C,10.85.73.237,58.71.132.10,17,33163,53 209 | 207,2,0.01,356,0.00,BC16DB567B1106D5,10.85.73.237,8.8.8.8,17,54158,53 210 | 208,2,0.01,347,0.00,B12E4A056484EC17,10.85.73.237,8.8.8.8,17,58354,53 211 | 209,2,0.01,262,0.00,574F79144219265,10.85.73.237,8.8.8.8,17,56208,53 212 | 210,1,0.01,110,0.00,82FD2FC0EFB95845,10.85.73.237,208.67.76.100,6,49491,5723 213 | 211,1,0.01,119,0.00,E0CAEB864424280D,10.85.73.237,58.71.132.10,17,34268,53 214 | 212,1,0.01,200,0.00,A8A7508ED497C3A2,10.85.73.237,58.71.132.10,1,0,0 215 | -------------------------------------------------------------------------------- /update_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -u 4 | 5 | CURR_VER="$1" 6 | NEW_VER="$2" 7 | 8 | for file in configure.ac snap/snapcraft.yaml spec/large-pcap-analyzer.spec README.md; do 9 | echo "Updating version $CURR_VER -> $NEW_VER in $file" 10 | sed -i "s@$CURR_VER@$NEW_VER@" $file 11 | done 12 | --------------------------------------------------------------------------------