├── .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 | [](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/) |  | CentOS, RHEL, Fedora, RockyLinux, AlmaLinux, openSUSE Tumbleweed |
44 | | [Snap](https://snapcraft.io/large-pcap-analyzer) | [](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