forked from cramer/pcapmirror
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebcd9f697d | |||
| d2133f7531 | |||
| c202697f7b | |||
| 9fcac378ae | |||
| 87d74f0db9 | |||
| 6baf639a9c | |||
| e3097eec3a | |||
| a2c0ad8bfb | |||
| e39c130a69 | |||
| a128da1328 | |||
| 99fbd6f600 | |||
| 5e595b0ae7 | |||
| 5ee45654f2 | |||
| 1da4934e59 | |||
| 0897fa1755 | |||
| 1233bf5ede | |||
| 038a6de68e | |||
| 39a8b7c016 | |||
| 4356a49271 | |||
| c6caf71afd | |||
| c2166bb7ec | |||
| eb41f3b6e4 | |||
| a17b8379bb | |||
| 1b98555ed3 | |||
| c640aa6f22 | |||
| c6584cde87 | |||
| 4300e30f04 | |||
| 0fdd4a9783 | |||
| 49173fef25 | |||
| 8e586dade0 | |||
| c7e16616a1 |
@@ -5,15 +5,17 @@ stages:
|
|||||||
|
|
||||||
variables:
|
variables:
|
||||||
DEBIAN_FRONTEND: noninteractive
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
VERSION: 0.6
|
||||||
|
|
||||||
build-bookworm:
|
build-bookworm:
|
||||||
stage: build
|
stage: build
|
||||||
|
needs: []
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
tags:
|
||||||
- bookworm
|
- bookworm
|
||||||
script:
|
script:
|
||||||
- tar -czf ../pcapmirror_0.3.orig.tar.gz --exclude=debian .
|
- tar -czf ../pcapmirror_$VERSION.orig.tar.gz --exclude=debian .
|
||||||
- apt-get update && apt-get install -y libpcap-dev
|
- apt-get update && apt-get install -y libpcap-dev
|
||||||
- dpkg-buildpackage -uc -us
|
- dpkg-buildpackage -uc -us
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
@@ -25,6 +27,8 @@ build-bookworm:
|
|||||||
|
|
||||||
publish-bookworm:
|
publish-bookworm:
|
||||||
stage: publish
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-bookworm
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-bookworm
|
- build-bookworm
|
||||||
only:
|
only:
|
||||||
@@ -34,17 +38,17 @@ publish-bookworm:
|
|||||||
script:
|
script:
|
||||||
- apt-get update && apt-get install -y curl
|
- apt-get update && apt-get install -y curl
|
||||||
- ls -la build
|
- ls -la build
|
||||||
- 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/bookworm/pcapmirror_0.3-1_amd64.deb"'
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_$VERSION-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_$VERSION-1_amd64.deb'
|
||||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/bookworm/pcapmirror_0.3-1_amd64.deb'
|
|
||||||
|
|
||||||
build-sid:
|
build-sid:
|
||||||
stage: build
|
stage: build
|
||||||
|
needs: []
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
tags:
|
||||||
- sid
|
- sid
|
||||||
script:
|
script:
|
||||||
- tar -czf ../pcapmirror_0.3.orig.tar.gz --exclude=debian .
|
- tar -czf ../pcapmirror_$VERSION.orig.tar.gz --exclude=debian .
|
||||||
- apt-get update && apt-get install -y libpcap-dev
|
- apt-get update && apt-get install -y libpcap-dev
|
||||||
- dpkg-buildpackage -uc -us
|
- dpkg-buildpackage -uc -us
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
@@ -56,6 +60,8 @@ build-sid:
|
|||||||
|
|
||||||
publish-sid:
|
publish-sid:
|
||||||
stage: publish
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-sid
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-sid
|
- build-sid
|
||||||
only:
|
only:
|
||||||
@@ -65,11 +71,11 @@ publish-sid:
|
|||||||
script:
|
script:
|
||||||
- apt-get update && apt-get install -y curl
|
- apt-get update && apt-get install -y curl
|
||||||
- ls -la build
|
- ls -la build
|
||||||
- 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/sid/pcapmirror_0.3-1_amd64.deb"'
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_$VERSION-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/sid/pcapmirror_$VERSION-1_amd64.deb'
|
||||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/sid/pcapmirror_0.3-1_amd64.deb'
|
|
||||||
|
|
||||||
build-rocky9:
|
build-rocky9:
|
||||||
stage: build
|
stage: build
|
||||||
|
needs: []
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
tags:
|
||||||
@@ -77,7 +83,7 @@ build-rocky9:
|
|||||||
script:
|
script:
|
||||||
- dnf install -y libpcap-devel
|
- dnf install -y libpcap-devel
|
||||||
- mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
- mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
- tar -czf /root/rpmbuild/SOURCES/pcapmirror-v0.3.tar.gz --exclude=debian --exclude=.git .
|
- tar -czf /root/rpmbuild/SOURCES/pcapmirror-v${VERSION}.tar.gz --exclude=debian --exclude=.git .
|
||||||
- cp -r * /root/rpmbuild/BUILD
|
- cp -r * /root/rpmbuild/BUILD
|
||||||
- rpmbuild -ba pcapmirror.spec
|
- rpmbuild -ba pcapmirror.spec
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
@@ -90,6 +96,8 @@ build-rocky9:
|
|||||||
|
|
||||||
publish-rocky9:
|
publish-rocky9:
|
||||||
stage: publish
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-rocky9
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-rocky9
|
- build-rocky9
|
||||||
only:
|
only:
|
||||||
@@ -99,11 +107,11 @@ publish-rocky9:
|
|||||||
script:
|
script:
|
||||||
- apt-get update && apt-get install -y curl
|
- apt-get update && apt-get install -y curl
|
||||||
- ls -la build
|
- ls -la build
|
||||||
- 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/rockylinux9/pcapmirror-0.3-1.el8.x86_64.rpm"'
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-$VERSION-*.el9.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux9/pcapmirror-$VERSION-1.el8.x86_64.rpm'
|
||||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-0.3-*.el9.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/rockylinux9/pcapmirror-0.3-1.el8.x86_64.rpm'
|
|
||||||
|
|
||||||
build-rocky8:
|
build-rocky8:
|
||||||
stage: build
|
stage: build
|
||||||
|
needs: []
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
tags:
|
||||||
@@ -111,7 +119,7 @@ build-rocky8:
|
|||||||
script:
|
script:
|
||||||
- dnf install -y libpcap-devel
|
- dnf install -y libpcap-devel
|
||||||
- mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
- mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
- tar -czf /root/rpmbuild/SOURCES/pcapmirror-v0.3.tar.gz --exclude=debian --exclude=.git .
|
- tar -czf /root/rpmbuild/SOURCES/pcapmirror-v${VERSION}.tar.gz --exclude=debian --exclude=.git .
|
||||||
- cp -r * /root/rpmbuild/BUILD
|
- cp -r * /root/rpmbuild/BUILD
|
||||||
- rpmbuild -ba pcapmirror.spec
|
- rpmbuild -ba pcapmirror.spec
|
||||||
- mkdir -p build
|
- mkdir -p build
|
||||||
@@ -124,6 +132,8 @@ build-rocky8:
|
|||||||
|
|
||||||
publish-rocky8:
|
publish-rocky8:
|
||||||
stage: publish
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-rocky8
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-rocky8
|
- build-rocky8
|
||||||
only:
|
only:
|
||||||
@@ -133,5 +143,70 @@ publish-rocky8:
|
|||||||
script:
|
script:
|
||||||
- apt-get update && apt-get install -y curl
|
- apt-get update && apt-get install -y curl
|
||||||
- ls -la build
|
- ls -la build
|
||||||
- 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm"'
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-$VERSION-*.el8.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux8/pcapmirror-$VERSION-1.el8.x86_64.rpm'
|
||||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-0.3-*.el8.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm'
|
|
||||||
|
build-pios12:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- pios12
|
||||||
|
script:
|
||||||
|
- tar -czf ../pcapmirror_$VERSION.orig.tar.gz --exclude=debian .
|
||||||
|
- apt-get update && apt-get install -y libpcap-dev
|
||||||
|
- dpkg-buildpackage -uc -us
|
||||||
|
- mkdir -p build
|
||||||
|
- mv ../pcapmirror*.* build/
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
|
||||||
|
publish-pios12:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-pios12
|
||||||
|
dependencies:
|
||||||
|
- build-pios12
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- bookworm
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get install -y curl
|
||||||
|
- ls -la build
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_$VERSION-1_armhf.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_$VERSION-1_armhf.deb'
|
||||||
|
|
||||||
|
build-pios12-64:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- pios12-64
|
||||||
|
script:
|
||||||
|
- tar -czf ../pcapmirror_$VERSION.orig.tar.gz --exclude=debian .
|
||||||
|
- apt-get update && apt-get install -y libpcap-dev
|
||||||
|
- dpkg-buildpackage -uc -us
|
||||||
|
- mkdir -p build
|
||||||
|
- mv ../pcapmirror*.* build/
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
|
||||||
|
publish-pios12-64:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-pios12-64
|
||||||
|
dependencies:
|
||||||
|
- build-pios12-64
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- bookworm
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get install -y curl
|
||||||
|
- ls -la build
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_$VERSION-1_arm64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_$VERSION-1_arm64.deb'
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
# pcapmirror
|
# pcapmirror
|
||||||
pcapmirror is a command-line tool for capturing network traffic and mirroring it to a remote destination using [TZSP encapsulation](https://en.wikipedia.org/wiki/TZSP). It leverages the `libpcap` library for packet capture and provides options for filtering traffic based on BPF syntax. This tool is useful for network monitoring, intrusion detection, and remote packet analysis.
|

|
||||||
|
|
||||||
|
pcapmirror is a command-line tool for capturing network traffic and mirroring it to a remote destination using [TZSP encapsulation](https://en.wikipedia.org/wiki/TZSP) or [ERSPAN](https://datatracker.ietf.org/doc/html/draft-foschiano-erspan-01). It leverages the `libpcap` library for packet capture and provides options for filtering traffic based on BPF syntax. This tool is useful for network monitoring, intrusion detection, and remote packet analysis.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -9,12 +12,19 @@ pcapmirror [options]
|
|||||||
|
|
||||||
### Options:
|
### Options:
|
||||||
|
|
||||||
* -i <interface>: Specify the capture interface (e.g., eth0).
|
* -i <interface> Specify the capture interface
|
||||||
* -f <filter>: Specify the capture filter in BPF syntax (e.g., tcp port 80).
|
* -f <filter> Specify the capture filter (BPF syntax)
|
||||||
* -r <ip_address>: Specify the destination IP address (required).
|
* -r <host/ipv4/ipv6> Specify the destination host (required)
|
||||||
* -p <port>: Specify the destination port (default: 37008).
|
* -p <port> Specify the destination port (default: 37008)
|
||||||
* -v: Enable verbose mode (prints packet information).
|
* -e Use ERSPAN encapsulation (default: TZSP)
|
||||||
* -h: Show this help message.
|
* -s <source_ip> Specify the source IP address (required for ERSPAN)
|
||||||
|
* -S <session_id> Specify the session ID (default: 42, must be between 0 and 1023)
|
||||||
|
* -4 Force IPv4 host lookup
|
||||||
|
* -6 Force IPv6 host lookup
|
||||||
|
* -l List available network interfaces.
|
||||||
|
* -v Enable verbose mode
|
||||||
|
* -c Count matching packets (overrides verbose mode)
|
||||||
|
* -h Show this help message
|
||||||
|
|
||||||
### Example:
|
### Example:
|
||||||
|
|
||||||
@@ -31,8 +41,25 @@ With this tool, you can mirror traffic directly to a running [Wireshark](https:/
|
|||||||
|
|
||||||
To avoid capturing traffic from your own monitoring machine, configure Wireshark with a capture filter of udp port 37008 or udp dst port 37008. Also, verify that your firewall permits this UDP traffic.
|
To avoid capturing traffic from your own monitoring machine, configure Wireshark with a capture filter of udp port 37008 or udp dst port 37008. Also, verify that your firewall permits this UDP traffic.
|
||||||
|
|
||||||
|
## Original Download Location
|
||||||
|
|
||||||
|
[https://git.freestone.net/cramer/pcapmirror](https://git.freestone.net/cramer/pcapmirror)
|
||||||
|
|
||||||
|
## Packages
|
||||||
|
|
||||||
|
On the original download location you will also find several prebuilt packages.
|
||||||
|
|
||||||
## Compile and Install
|
## Compile and Install
|
||||||
|
|
||||||
|
### Supported Operating Systems
|
||||||
|
Source is tested to build and function on the following operating systems
|
||||||
|
|
||||||
|
* Debian Linux 12 + unstable (sid)
|
||||||
|
* Rocky Linux 8 + 9
|
||||||
|
* PiOS 12 (bookworm)
|
||||||
|
* OpenBSD 7.6
|
||||||
|
* MacOS 15
|
||||||
|
|
||||||
Compile the program:
|
Compile the program:
|
||||||
```bash
|
```bash
|
||||||
make
|
make
|
||||||
@@ -45,7 +72,7 @@ make install
|
|||||||
|
|
||||||
This will copy the pcapmirror executable to bin. You may need to adjust the PREFIX variable in the Makefile if you want to install it to a different location.
|
This will copy the pcapmirror executable to bin. You may need to adjust the PREFIX variable in the Makefile if you want to install it to a different location.
|
||||||
|
|
||||||
Dependencies
|
### Dependencies
|
||||||
libpcap: You need to have libpcap installed on your system. On Debian/Ubuntu systems, you can install it using:
|
libpcap: You need to have libpcap installed on your system. On Debian/Ubuntu systems, you can install it using:
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install libpcap-dev
|
sudo apt-get install libpcap-dev
|
||||||
@@ -57,7 +84,7 @@ sudo yum install libpcap-devel
|
|||||||
```
|
```
|
||||||
## Build debian package
|
## Build debian package
|
||||||
|
|
||||||
If you have never built a debian pakage you probably need debhelper:
|
If you have never built a debian package you probably need debhelper:
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install debhelper
|
sudo apt-get install debhelper
|
||||||
```
|
```
|
||||||
|
|||||||
25
debian/changelog
vendored
25
debian/changelog
vendored
@@ -1,3 +1,28 @@
|
|||||||
|
pcapmirror (0.6-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Erspan Encapsulation support
|
||||||
|
* added option -e to set the encapsulation type
|
||||||
|
* added option -S to set ERSPAN session id
|
||||||
|
|
||||||
|
-- Matthias Cramer <cramer@freestone.net> Sun, 20 Apr 2025 16:50:00 +0200
|
||||||
|
|
||||||
|
pcapmirror (0.5-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* new option -c to count matching packets (overrides verbose mode)
|
||||||
|
* reworked packet decoder to also decode arp, vlan and qinq packets
|
||||||
|
* well known protocols numbers are now decoded
|
||||||
|
* works now on MacOS and OpenBSD
|
||||||
|
|
||||||
|
-- Matthias Cramer <cramer@freestone.net> Fri, 29 Mar 2025 13:40:00 +0100
|
||||||
|
|
||||||
|
pcapmirror (0.4-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* IPv6 support for remote destination
|
||||||
|
* remote destination can now also be hostname
|
||||||
|
* added option to enforce IPv4 and IPv6 for remote destination
|
||||||
|
|
||||||
|
-- Matthias Cramer <cramer@freestone.net> Fri, 24 Mar 2025 13:40:00 +0100
|
||||||
|
|
||||||
pcapmirror (0.3-1) unstable; urgency=medium
|
pcapmirror (0.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
* added manpage
|
* added manpage
|
||||||
|
|||||||
4
debian/control
vendored
4
debian/control
vendored
@@ -11,7 +11,7 @@ Architecture: any
|
|||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, libpcap0.8
|
Depends: ${shlibs:Depends}, ${misc:Depends}, libpcap0.8
|
||||||
Description: A simple packet mirroring tool using libpcap
|
Description: A simple packet mirroring tool using libpcap
|
||||||
pcapmirror is a command-line tool for capturing network traffic and
|
pcapmirror is a command-line tool for capturing network traffic and
|
||||||
mirroring it to a remote destination using TZSP encapsulation. It
|
mirroring it to a remote destination using TZSP or ERSPAN encapsulation.
|
||||||
leverages the libpcap library for packet capture and provides options
|
It leverages the libpcap library for packet capture and provides options
|
||||||
for filtering traffic based on BPF syntax. This tool is useful for
|
for filtering traffic based on BPF syntax. This tool is useful for
|
||||||
network monitoring, intrusion detection, and remote packet analysis.
|
network monitoring, intrusion detection, and remote packet analysis.
|
||||||
BIN
logo/pcapmirror_logo.png
Normal file
BIN
logo/pcapmirror_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 600 KiB |
BIN
logo/pcapmirror_logo_small.png
Normal file
BIN
logo/pcapmirror_logo_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
592
main.c
592
main.c
@@ -6,25 +6,24 @@ Copyright (c) 2025, Matthias Cramer, cramer@freestone.net
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pcap.h>
|
|
||||||
#include <netinet/ip.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
#define ENABLE_IPV6
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#ifdef ENABLE_IPV6
|
#include <pcap.h>
|
||||||
#include <netinet/ip6.h> // Include for IPv6 header definition
|
#include <netinet/in.h>
|
||||||
#endif
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#include <netinet/if_ether.h> // For Ethernet and ARP headers
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#define DEFAULT_DEST_PORT 37008 // Default TZSP port
|
#define DEFAULT_DEST_PORT 37008 // Default TZSP port
|
||||||
#define TZSP_ENCAP_LEN 4 // Length of TZSP encapsulation header
|
#define TZSP_ENCAP_LEN 4 // Length of TZSP encapsulation header
|
||||||
#define TZSP_TAGGED_LEN 1 // Length of TZSP tagged field header (type)
|
#define TZSP_TAGGED_LEN 1 // Length of TZSP tagged field header (type)
|
||||||
#define ETHERNET_HEADER_LENGTH 14
|
#define ETHERNET_HEADER_LENGTH 14 // Assuming Ethernet header is 14 bytes
|
||||||
|
|
||||||
// TZSP Header Structure
|
// TZSP Header Structure
|
||||||
struct tzsp_header {
|
struct tzsp_header {
|
||||||
@@ -39,38 +38,132 @@ struct tzsp_tagged {
|
|||||||
unsigned char type; // Tag type
|
unsigned char type; // Tag type
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to check if the system is little-endian
|
// GRE Header Structure
|
||||||
int is_little_endian() {
|
struct gre_header {
|
||||||
volatile unsigned int i=0x01234567;
|
uint16_t flags; // GRE flags
|
||||||
return (((unsigned char*)&i)[0] == 0x67);
|
uint16_t protocol; // Protocol type (0x88BE for ERSPAN)
|
||||||
|
};
|
||||||
|
|
||||||
|
// ERSPAN Header Structure
|
||||||
|
// ERSPAN Type II Header Structure
|
||||||
|
struct erspan_header {
|
||||||
|
uint32_t ver_vlan_cos_en_t_session; // Ver (4 bits), VLAN (12 bits), COS (3 bits), En (1 bit), T (1 bit), Session ID (10 bits)
|
||||||
|
uint32_t reserved_index; // Reserved (12 bits), Index (20 bits)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add this structure for ARP header parsing
|
||||||
|
struct arp_header {
|
||||||
|
uint16_t htype; // Hardware type
|
||||||
|
uint16_t ptype; // Protocol type
|
||||||
|
uint8_t hlen; // Hardware address length
|
||||||
|
uint8_t plen; // Protocol address length
|
||||||
|
uint16_t oper; // Operation (1 = request, 2 = reply)
|
||||||
|
uint8_t sha[6]; // Sender hardware address
|
||||||
|
uint8_t spa[4]; // Sender protocol address
|
||||||
|
uint8_t tha[6]; // Target hardware address
|
||||||
|
uint8_t tpa[4]; // Target protocol address
|
||||||
|
};
|
||||||
|
|
||||||
|
void list_interfaces() {
|
||||||
|
pcap_if_t *alldevs;
|
||||||
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||||||
|
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
|
||||||
|
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("Available network interfaces:\n");
|
||||||
|
for (pcap_if_t *d = alldevs; d != NULL; d = d->next) {
|
||||||
|
printf("%s", d->name);
|
||||||
|
if (d->description) {
|
||||||
|
printf(" (%s)", d->description);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
pcap_freealldevs(alldevs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to lookup protocol name or return protocol number as a string
|
||||||
|
const char *lookup_protocol_name(int protocol) {
|
||||||
|
static char buf[5]; // Buffer to hold protocol number as a string
|
||||||
|
|
||||||
|
switch (protocol) {
|
||||||
|
case 1:
|
||||||
|
return "ICMP";
|
||||||
|
case 2:
|
||||||
|
return "IGMP";
|
||||||
|
case 6:
|
||||||
|
return "TCP";
|
||||||
|
case 17:
|
||||||
|
return "UDP";
|
||||||
|
case 41:
|
||||||
|
return "IPv6";
|
||||||
|
case 47:
|
||||||
|
return "GRE";
|
||||||
|
case 50:
|
||||||
|
return "ESP";
|
||||||
|
case 51:
|
||||||
|
return "AH";
|
||||||
|
case 58:
|
||||||
|
return "ICMPv6";
|
||||||
|
case 89:
|
||||||
|
return "OSPF";
|
||||||
|
case 112:
|
||||||
|
return "VRRP";
|
||||||
|
case 124:
|
||||||
|
return "ISIS";
|
||||||
|
case 132:
|
||||||
|
return "SCTP";
|
||||||
|
default:
|
||||||
|
snprintf(buf, sizeof(buf), "%d", protocol); // Convert protocol number to string
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_usage(const char *program_name) {
|
void print_usage(const char *program_name) {
|
||||||
printf("Usage: %s [options]\n", program_name);
|
printf("Usage: %s [options]\n", program_name);
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
printf(" -i <interface> Specify the capture interface\n");
|
printf(" -i <interface> Specify the capture interface\n");
|
||||||
printf(" -f <filter> Specify the capture filter (BPF syntax)\n");
|
printf(" -f <filter> Specify the capture filter (BPF syntax)\n");
|
||||||
printf(" -r <ip_address> Specify the destination IP address (required)\n");
|
printf(" -r <host/ipv4/ipv6> Specify the destination host (required)\n");
|
||||||
printf(" -p <port> Specify the destination port (default: %d)\n", DEFAULT_DEST_PORT);
|
printf(" -p <port> Specify the destination port (default: %d)\n", DEFAULT_DEST_PORT);
|
||||||
printf(" -v Enable verbose mode\n");
|
printf(" -e Use ERSPAN encapsulation (default: TZSP)\n");
|
||||||
printf(" -h Show this help message\n");
|
printf(" -s <source_ip> Specify the source IP address (required for ERSPAN)\n");
|
||||||
|
printf(" -S <session_id> Specify the session ID (default: 42, must be between 0 and 1023)\n");
|
||||||
|
printf(" -4 Force IPv4 host lookup\n");
|
||||||
|
printf(" -6 Force IPv6 host lookup\n");
|
||||||
|
printf(" -l List available network interfaces\n");
|
||||||
|
printf(" -v Enable verbose mode\n");
|
||||||
|
printf(" -c Count matching packets (overrides verbose mode)\n");
|
||||||
|
printf(" -h Show this help message\n");
|
||||||
printf("Example:\n");
|
printf("Example:\n");
|
||||||
printf(" %s -i eth0 -f 'tcp port 80' -v -r 192.168.1.100 -p 47008\n", program_name);
|
printf(" %s -i eth0 -f 'tcp port 80' -v -r 192.168.1.100 -p 47008\n", program_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
pcap_if_t *alldevs;
|
|
||||||
char errbuf[PCAP_ERRBUF_SIZE];
|
char errbuf[PCAP_ERRBUF_SIZE];
|
||||||
char *filter_exp = "tcp port 8088"; // Default filter
|
char *filter_exp = ""; // Default filter
|
||||||
char *dev_name = NULL; // Device name
|
char *dev_name = NULL; // Device name
|
||||||
char *dest_ip = NULL; // Destination IP, no default value
|
char *mirror_host = NULL; // Destination IP, no default value
|
||||||
int dest_port = DEFAULT_DEST_PORT; // Destination port, default value
|
int dest_port = DEFAULT_DEST_PORT; // Destination port, default value
|
||||||
int i;
|
int i;
|
||||||
int verbose = 0; // Verbose flag, default is false
|
int verbose = 0; // Verbose flag, default is false
|
||||||
|
int force_ipv4 = 0; // Flag to force IPv4 lookup
|
||||||
|
int force_ipv6 = 0; // Flag to force IPv6 lookup
|
||||||
|
int list_interfaces_flag = 0; // Flag to list interfaces
|
||||||
|
|
||||||
|
// Add a variable to track the count of matching packets
|
||||||
|
int count_packets = 0; // Flag for counting packets
|
||||||
|
unsigned long long int packet_count = 0; // Counter for matching packets (64bit)
|
||||||
|
|
||||||
|
int use_erspan = 0; // Flag for ERSPAN encapsulation
|
||||||
|
char *source_address = NULL; // Source IP address, default is NULL
|
||||||
|
uint32_t session_id = 42; // Session ID (10 bits)
|
||||||
|
|
||||||
// Socket variables
|
// Socket variables
|
||||||
int sockfd;
|
int sockfd;
|
||||||
struct sockaddr_in dest_addr;
|
struct addrinfo hints, *res;
|
||||||
|
struct sockaddr_storage dest_addr; // Declare dest_addr
|
||||||
|
int dest_addr_size;
|
||||||
|
|
||||||
// Check if no arguments are given or if help is requested
|
// Check if no arguments are given or if help is requested
|
||||||
if (argc == 1 || (argc == 2 && strcmp(argv[1], "-h") == 0)) {
|
if (argc == 1 || (argc == 2 && strcmp(argv[1], "-h") == 0)) {
|
||||||
@@ -78,18 +171,6 @@ int main(int argc, char *argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create UDP socket
|
|
||||||
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
|
|
||||||
perror("socket");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set destination address
|
|
||||||
memset(&dest_addr, 0, sizeof(dest_addr));
|
|
||||||
dest_addr.sin_family = AF_INET;
|
|
||||||
dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Default to localhost
|
|
||||||
dest_addr.sin_port = htons(dest_port);
|
|
||||||
|
|
||||||
// Parse command-line arguments
|
// Parse command-line arguments
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
|
if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) {
|
||||||
@@ -104,56 +185,144 @@ int main(int argc, char *argv[]) {
|
|||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "-r") == 0 && i + 1 < argc) {
|
||||||
dest_ip = argv[i + 1]; // Set destination IP from command line
|
mirror_host = argv[i + 1]; // Set destination IP from command line
|
||||||
i++; // Skip the IP value
|
i++; // Skip the IP value
|
||||||
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
} else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||||
dest_port = atoi(argv[i + 1]); // Set destination port from command line
|
dest_port = atoi(argv[i + 1]); // Set destination port from command line
|
||||||
i++; // Skip the port value
|
i++; // Skip the port value
|
||||||
|
} else if (strcmp(argv[i], "-4") == 0) {
|
||||||
|
force_ipv4 = 1; // Force IPv4 lookup
|
||||||
|
} else if (strcmp(argv[i], "-6") == 0) {
|
||||||
|
force_ipv6 = 1; // Force IPv6 lookup
|
||||||
|
} else if (strcmp(argv[i], "-l") == 0) {
|
||||||
|
list_interfaces_flag = 1; // Set flag to list interfaces
|
||||||
|
} else if (strcmp(argv[i], "-c") == 0) {
|
||||||
|
count_packets = 1; // Enable packet counting
|
||||||
|
verbose = 0; // Disable verbose mode if -c is set
|
||||||
|
} else if (strcmp(argv[i], "-e") == 0) {
|
||||||
|
use_erspan = 1; // Enable ERSPAN encapsulation
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0 && i + 1 < argc) {
|
||||||
|
source_address = argv[i + 1]; // Set source IP from command line
|
||||||
|
i++; // Skip the source IP value
|
||||||
|
} else if (strcmp(argv[i], "-S") == 0 && i + 1 < argc) {
|
||||||
|
session_id = atoi(argv[i + 1]); // Set session ID from command line
|
||||||
|
if (session_id > 1023) { // Validate session ID (must fit in 10 bits)
|
||||||
|
fprintf(stderr, "Error: Session ID must be between 0 and 1023.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
i++; // Skip the session ID value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (list_interfaces_flag) {
|
||||||
|
list_interfaces();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if destination IP is provided
|
// Check if destination IP is provided
|
||||||
if (dest_ip == NULL) {
|
if (mirror_host == NULL && !list_interfaces_flag) {
|
||||||
fprintf(stderr, "Error: Destination IP address is required.\n");
|
fprintf(stderr, "Error: Destination IP address is required.\n");
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr) <= 0) {
|
// Check that interface is not any
|
||||||
perror("inet_pton");
|
if (dev_name != NULL && strcmp(dev_name, "any") == 0) {
|
||||||
|
fprintf(stderr, "Error: Interface 'any' is not supported.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dest_addr.sin_port = htons(dest_port); // Set the port
|
// Check if the interface is specified
|
||||||
|
|
||||||
// If no interface is specified, find all devices
|
|
||||||
if (dev_name == NULL) {
|
if (dev_name == NULL) {
|
||||||
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
|
fprintf(stderr, "Error: Interface must be specified\n");
|
||||||
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
|
return 1;
|
||||||
return(1);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Print the available devices for debugging
|
// Resolve the destination address
|
||||||
/*
|
memset(&hints, 0, sizeof(hints));
|
||||||
pcap_if_t *device;
|
hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
|
||||||
printf("Available devices:\n");
|
hints.ai_socktype = SOCK_DGRAM; // Datagram socket
|
||||||
for (device = alldevs; device != NULL; device = device->next) {
|
|
||||||
printf("%s - %s\n", device->name, (device->description != NULL) ? device->description : "No description available");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Use the first device if no device is specified
|
if (force_ipv4) {
|
||||||
if (alldevs == NULL) {
|
hints.ai_family = AF_INET; // Force IPv4
|
||||||
fprintf(stderr, "No devices found. Make sure you have permissions to capture traffic.\n");
|
} else if (force_ipv6) {
|
||||||
|
hints.ai_family = AF_INET6; // Force IPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getaddrinfo(mirror_host, NULL, &hints, &res) != 0) {
|
||||||
|
perror("getaddrinfo");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate dest_addr size
|
||||||
|
if (res->ai_family == AF_INET) {
|
||||||
|
dest_addr_size = sizeof(struct sockaddr_in);
|
||||||
|
} else if (res->ai_family == AF_INET6) {
|
||||||
|
dest_addr_size = sizeof(struct sockaddr_in6);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown address family\n");
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_erspan) {
|
||||||
|
// Create a raw socket for ERSPAN
|
||||||
|
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_GRE);
|
||||||
|
if (sockfd == -1) {
|
||||||
|
perror("socket");
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the IP_HDRINCL option to include the IP header in the packet
|
||||||
|
int optval = 1;
|
||||||
|
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) == -1) {
|
||||||
|
perror("setsockopt");
|
||||||
|
close(sockfd);
|
||||||
|
freeaddrinfo(res);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_name = alldevs->name; // Use the name of the first device
|
|
||||||
} else {
|
} else {
|
||||||
// Interface specified via command line, no need to find all devices
|
// Create a UDP socket for TZSP
|
||||||
alldevs = NULL; // Set alldevs to NULL to avoid potential issues
|
sockfd = socket(res->ai_family, SOCK_DGRAM, 0);
|
||||||
|
if (sockfd == -1) {
|
||||||
|
perror("socket");
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&dest_addr, 0, sizeof(dest_addr));
|
||||||
|
|
||||||
|
// Set the destination address
|
||||||
|
if (res->ai_family == AF_INET) {
|
||||||
|
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
|
||||||
|
ipv4->sin_port = htons(dest_port);
|
||||||
|
memcpy(&dest_addr, ipv4, sizeof(struct sockaddr_in));
|
||||||
|
} else if (res->ai_family == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
|
||||||
|
ipv6->sin6_port = htons(dest_port);
|
||||||
|
memcpy(&dest_addr, ipv6, sizeof(struct sockaddr_in6));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the destination IP address
|
||||||
|
char resolved_ip[INET6_ADDRSTRLEN];
|
||||||
|
if (res->ai_family == AF_INET) {
|
||||||
|
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
|
||||||
|
inet_ntop(AF_INET, &(ipv4->sin_addr), resolved_ip, INET6_ADDRSTRLEN);
|
||||||
|
} else if (res->ai_family == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
|
||||||
|
inet_ntop(AF_INET6, &(ipv6->sin6_addr), resolved_ip, INET6_ADDRSTRLEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the address info
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
printf("Using interface: %s\n", dev_name);
|
||||||
|
printf("Using filter: %s\n", filter_exp);
|
||||||
|
printf("Resolved Destination IP: %s\n", resolved_ip);
|
||||||
|
printf("Destination Port: %d\n", dest_port);
|
||||||
|
|
||||||
pcap_t *handle;
|
pcap_t *handle;
|
||||||
struct bpf_program fp;
|
struct bpf_program fp;
|
||||||
bpf_u_int32 mask;
|
bpf_u_int32 mask;
|
||||||
@@ -165,29 +334,20 @@ int main(int argc, char *argv[]) {
|
|||||||
mask = 0;
|
mask = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = pcap_open_live(dev_name, BUFSIZ, 1, 1000, errbuf);
|
handle = pcap_open_live(dev_name, BUFSIZ, 1, 100, errbuf);
|
||||||
if (handle == NULL) {
|
if (handle == NULL) {
|
||||||
fprintf(stderr, "Couldn't open device %s: %s\n", dev_name, errbuf);
|
fprintf(stderr, "Couldn't open device %s: %s\n", dev_name, errbuf);
|
||||||
if (alldevs != NULL) {
|
|
||||||
pcap_freealldevs(alldevs);
|
|
||||||
}
|
|
||||||
return(2);
|
return(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pcap_compile(handle, &fp, filter_exp, 1, net) == -1) {
|
if (pcap_compile(handle, &fp, filter_exp, 1, net) == -1) {
|
||||||
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
|
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
|
||||||
if (alldevs != NULL) {
|
|
||||||
pcap_freealldevs(alldevs);
|
|
||||||
}
|
|
||||||
pcap_close(handle);
|
pcap_close(handle);
|
||||||
return(2);
|
return(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pcap_setfilter(handle, &fp) == -1) {
|
if (pcap_setfilter(handle, &fp) == -1) {
|
||||||
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
|
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
|
||||||
if (alldevs != NULL) {
|
|
||||||
pcap_freealldevs(alldevs);
|
|
||||||
}
|
|
||||||
pcap_close(handle);
|
pcap_close(handle);
|
||||||
return(2);
|
return(2);
|
||||||
}
|
}
|
||||||
@@ -195,97 +355,235 @@ int main(int argc, char *argv[]) {
|
|||||||
struct pcap_pkthdr header;
|
struct pcap_pkthdr header;
|
||||||
const u_char *packet;
|
const u_char *packet;
|
||||||
char source_ip_str[INET6_ADDRSTRLEN], dest_ip_str[INET6_ADDRSTRLEN];
|
char source_ip_str[INET6_ADDRSTRLEN], dest_ip_str[INET6_ADDRSTRLEN];
|
||||||
struct ip *ip_header;
|
struct ip *ip_header; // Declare ip4_header
|
||||||
#ifdef ENABLE_IPV6
|
struct ip6_hdr *ip6_header; // Declare ip6_header
|
||||||
struct ip6_hdr *ip6_header;
|
|
||||||
#endif
|
|
||||||
int ip_protocol = 0;
|
int ip_protocol = 0;
|
||||||
|
struct timeval current_time, last_count;
|
||||||
|
static uint32_t sequence_number = 0; // Sequence number for ERSPAN packets
|
||||||
|
|
||||||
printf("Using interface: %s\n", dev_name);
|
gettimeofday(&last_count, NULL);
|
||||||
printf("Using filter: %s\n", filter_exp);
|
printf("\n");
|
||||||
printf("Destination IP: %s\n", dest_ip);
|
|
||||||
printf("Destination Port: %d\n", dest_port);
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
packet = pcap_next(handle, &header);
|
packet = pcap_next(handle, &header);
|
||||||
if (packet == NULL)
|
if (packet == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Assuming Ethernet header is 14 bytes
|
if (count_packets) {
|
||||||
// Check IP version
|
packet_count++;
|
||||||
ip_header = (struct ip*)(packet + ETHERNET_HEADER_LENGTH);
|
|
||||||
ip_protocol = ip_header->ip_v;
|
|
||||||
|
|
||||||
if (ip_protocol == 4) {
|
gettimeofday(¤t_time, NULL);
|
||||||
// IPv4
|
|
||||||
inet_ntop(AF_INET, &(ip_header->ip_src), source_ip_str, INET6_ADDRSTRLEN);
|
|
||||||
inet_ntop(AF_INET, &(ip_header->ip_dst), dest_ip_str, INET6_ADDRSTRLEN);
|
|
||||||
|
|
||||||
if (verbose) {
|
long elapsed_ms = current_time.tv_sec * 1000 + (current_time.tv_usec /1000)-
|
||||||
printf("IPv4 Packet: %s -> %s, IP Protocol: %d\n",
|
(last_count.tv_sec * 1000 + (last_count.tv_usec /1000));
|
||||||
source_ip_str, dest_ip_str, ip_header->ip_p);
|
|
||||||
|
if (elapsed_ms >= 500) {
|
||||||
|
printf("\rPacket count: %llu", packet_count);
|
||||||
|
fflush(stdout);
|
||||||
|
last_count = current_time; // Reset the timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_IPV6
|
|
||||||
else if (ip_protocol == 6) {
|
|
||||||
// IPv6
|
|
||||||
ip6_header = (struct ip6_hdr*)(packet + ETHERNET_HEADER_LENGTH);
|
|
||||||
inet_ntop(AF_INET6, &(ip6_header->ip6_src), source_ip_str, INET6_ADDRSTRLEN);
|
|
||||||
inet_ntop(AF_INET6, &(ip6_header->ip6_dst), dest_ip_str, INET6_ADDRSTRLEN);
|
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
printf("IPv6 Packet: %s -> %s, Next Header: %d\n",
|
|
||||||
source_ip_str, dest_ip_str, ip6_header->ip6_nxt);
|
// Parse Ethernet header
|
||||||
|
struct ether_header *eth_header = (struct ether_header *)packet;
|
||||||
|
|
||||||
|
// Check EtherType
|
||||||
|
uint16_t ether_type = ntohs(eth_header->ether_type);
|
||||||
|
|
||||||
|
int vlan_offset = 0; // Offset for VLAN-tagged packets
|
||||||
|
|
||||||
|
// Check for VLAN tags (including Q-in-Q)
|
||||||
|
while (ether_type == 0x8100 || ether_type == 0x88A8) {
|
||||||
|
// VLAN tag is present
|
||||||
|
vlan_offset += 4; // Each VLAN tag adds 4 bytes
|
||||||
|
uint16_t vlan_tag = ntohs(*(uint16_t *)(packet + ETHERNET_HEADER_LENGTH + vlan_offset - 4));
|
||||||
|
uint16_t vlan_id = vlan_tag & 0x0FFF; // Extract VLAN ID (12 bits)
|
||||||
|
uint8_t vlan_pcp = (vlan_tag >> 13) & 0x07; // Extract Priority Code Point (3 bits)
|
||||||
|
uint8_t vlan_dei = (vlan_tag >> 12) & 0x01; // Extract Drop Eligible Indicator (1 bit)
|
||||||
|
|
||||||
|
printf("VLAN Tag: VLAN ID=%d, PCP=%d, DEI=%d\n", vlan_id, vlan_pcp, vlan_dei);
|
||||||
|
|
||||||
|
// Update EtherType to the next protocol
|
||||||
|
ether_type = ntohs(*(uint16_t *)(packet + ETHERNET_HEADER_LENGTH + vlan_offset - 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ether_type == ETHERTYPE_IP) {
|
||||||
|
// Handle IPv4 traffic
|
||||||
|
ip_header = (struct ip *)(packet + ETHERNET_HEADER_LENGTH + vlan_offset);
|
||||||
|
ip_protocol = ip_header->ip_v & 0x0F; // Get IP version
|
||||||
|
|
||||||
|
if (ip_protocol == 4) {
|
||||||
|
inet_ntop(AF_INET, &(ip_header->ip_src.s_addr), source_ip_str, INET6_ADDRSTRLEN);
|
||||||
|
inet_ntop(AF_INET, &(ip_header->ip_dst.s_addr), dest_ip_str, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
printf("IPv4 Packet: %s -> %s, IP Protocol: %s\n",
|
||||||
|
source_ip_str, dest_ip_str, lookup_protocol_name(ip_header->ip_p));
|
||||||
|
}
|
||||||
|
} else if (ether_type == ETHERTYPE_IPV6) {
|
||||||
|
// Handle IPv6 traffic
|
||||||
|
ip6_header = (struct ip6_hdr *)(packet + ETHERNET_HEADER_LENGTH + vlan_offset);
|
||||||
|
inet_ntop(AF_INET6, &(ip6_header->ip6_src), source_ip_str, INET6_ADDRSTRLEN);
|
||||||
|
inet_ntop(AF_INET6, &(ip6_header->ip6_dst), dest_ip_str, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
printf("IPv6 Packet: %s -> %s, Next Header: %s\n",
|
||||||
|
source_ip_str, dest_ip_str, lookup_protocol_name(ip6_header->ip6_nxt));
|
||||||
|
} else if (ether_type == ETHERTYPE_ARP) {
|
||||||
|
// Handle ARP traffic
|
||||||
|
struct arp_header *arp = (struct arp_header *)(packet + ETHERNET_HEADER_LENGTH + vlan_offset);
|
||||||
|
|
||||||
|
printf("ARP Packet: Operation: %s\n",
|
||||||
|
(ntohs(arp->oper) == 1) ? "Request" : "Reply");
|
||||||
|
printf("Sender MAC: %02x:%02x:%02x:%02x:%02x:%02x, Sender IP: %d.%d.%d.%d\n",
|
||||||
|
arp->sha[0], arp->sha[1], arp->sha[2], arp->sha[3], arp->sha[4], arp->sha[5],
|
||||||
|
arp->spa[0], arp->spa[1], arp->spa[2], arp->spa[3]);
|
||||||
|
printf("Target MAC: %02x:%02x:%02x:%02x:%02x:%02x, Target IP: %d.%d.%d.%d\n",
|
||||||
|
arp->tha[0], arp->tha[1], arp->tha[2], arp->tha[3], arp->tha[4], arp->tha[5],
|
||||||
|
arp->tpa[0], arp->tpa[1], arp->tpa[2], arp->tpa[3]);
|
||||||
|
} else {
|
||||||
|
printf("Non-IP/ARP Packet, EtherType: 0x%04x\n", ether_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
else {
|
// Encapsulation logic
|
||||||
printf("Non-IP Packet\n");
|
if (use_erspan) {
|
||||||
continue;
|
// ERSPAN Encapsulation
|
||||||
|
struct ip ip_header;
|
||||||
|
struct gre_header gre;
|
||||||
|
struct erspan_header erspan;
|
||||||
|
|
||||||
|
// Set IP header fields
|
||||||
|
memset(&ip_header, 0, sizeof(ip_header));
|
||||||
|
ip_header.ip_hl = 5; // Header length (5 * 4 = 20 bytes)
|
||||||
|
ip_header.ip_v = 4; // IPv4
|
||||||
|
ip_header.ip_tos = 0; // Type of Service
|
||||||
|
ip_header.ip_len = htons(sizeof(ip_header) + sizeof(gre) + sizeof(sequence_number) + sizeof(erspan) + header.caplen);
|
||||||
|
ip_header.ip_id = htons(0); // Identification
|
||||||
|
ip_header.ip_off = 0; // Fragment offset
|
||||||
|
ip_header.ip_ttl = 64; // Time to live
|
||||||
|
ip_header.ip_p = IPPROTO_GRE; // Protocol (GRE)
|
||||||
|
ip_header.ip_dst.s_addr = ((struct sockaddr_in *)&dest_addr)->sin_addr.s_addr;
|
||||||
|
|
||||||
|
if (source_address != NULL) {
|
||||||
|
if (inet_pton(AF_INET, source_address, &(ip_header.ip_src)) != 1) {
|
||||||
|
fprintf(stderr, "Error: Invalid source IP address '%s'\n", source_address);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ip_header.ip_src.s_addr = inet_addr("192.168.1.1"); // Default source IP
|
||||||
|
}
|
||||||
|
|
||||||
|
ip_header.ip_src.s_addr = inet_addr("192.168.1.1"); // Replace with your source IP
|
||||||
|
|
||||||
|
// Set GRE header fields
|
||||||
|
gre.flags = htons(0x1000); // GRE flags (S bit set for Sequence Number Present)
|
||||||
|
gre.protocol = htons(0x88BE); // ERSPAN protocol type
|
||||||
|
|
||||||
|
// Set ERSPAN header fields
|
||||||
|
uint32_t version = 1; // Version (4 bits)
|
||||||
|
uint32_t vlan = 100; // VLAN ID (12 bits)
|
||||||
|
uint32_t cos = 5; // Class of Service (3 bits)
|
||||||
|
uint32_t en = 0; // Trunk Encapsulation Type (2 bit)
|
||||||
|
uint32_t t = 1; // Truncated (1 bit)
|
||||||
|
|
||||||
|
// Combine fields into the 32-bit ver_vlan_cos_en_t_session field
|
||||||
|
erspan.ver_vlan_cos_en_t_session =
|
||||||
|
((version & 0xF) << 28) | // Version (4 bits, shifted to bits 28-31)
|
||||||
|
((vlan & 0xFFF) << 16) | // VLAN ID (12 bits, shifted to bits 16-27)
|
||||||
|
((cos & 0x7) << 13) | // Class of Service (3 bits, shifted to bits 13-15)
|
||||||
|
((en & 0x3) << 11) | // Trunk Encapsulation Type (2 bit, bit 12)
|
||||||
|
((t & 0x1) << 10) | // Truncated (1 bit, bit 11)
|
||||||
|
(session_id & 0x3FF); // Session ID (10 bits, bits 0-9)
|
||||||
|
|
||||||
|
// Convert to network byte order
|
||||||
|
erspan.ver_vlan_cos_en_t_session = htonl(erspan.ver_vlan_cos_en_t_session);
|
||||||
|
|
||||||
|
// Set the reserved and index fields
|
||||||
|
uint32_t reserved = 0; // Reserved (12 bits)
|
||||||
|
uint32_t index = 12345; // Index (20 bits)
|
||||||
|
|
||||||
|
// Combine fields into the 32-bit reserved_index field
|
||||||
|
erspan.reserved_index =
|
||||||
|
((reserved & 0xFFF) << 20) | // Reserved (12 bits, bits 20-31)
|
||||||
|
(index & 0xFFFFF); // Index (20 bits, bits 0-19)
|
||||||
|
|
||||||
|
// Convert to network byte order
|
||||||
|
erspan.reserved_index = htonl(erspan.reserved_index);
|
||||||
|
|
||||||
|
// Calculate total length
|
||||||
|
unsigned short total_length = sizeof(ip_header) + sizeof(gre) + sizeof(sequence_number) + sizeof(erspan) + header.caplen;
|
||||||
|
|
||||||
|
// Allocate memory for ERSPAN packet
|
||||||
|
unsigned char *erspan_packet = (unsigned char *)malloc(total_length);
|
||||||
|
if (erspan_packet == NULL) {
|
||||||
|
perror("malloc");
|
||||||
|
continue; // Skip this packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy IP header, GRE header, sequence number, ERSPAN header, and packet data into the new buffer
|
||||||
|
unsigned char *ptr = erspan_packet;
|
||||||
|
memcpy(ptr, &ip_header, sizeof(ip_header));
|
||||||
|
ptr += sizeof(ip_header);
|
||||||
|
memcpy(ptr, &gre, sizeof(gre));
|
||||||
|
ptr += sizeof(gre);
|
||||||
|
uint32_t seq_num_network_order = htonl(sequence_number++);
|
||||||
|
memcpy(ptr, &seq_num_network_order, sizeof(sequence_number));
|
||||||
|
ptr += sizeof(sequence_number);
|
||||||
|
memcpy(ptr, &erspan, sizeof(erspan));
|
||||||
|
ptr += sizeof(erspan);
|
||||||
|
memcpy(ptr, packet, header.caplen);
|
||||||
|
|
||||||
|
// Send packet via raw socket
|
||||||
|
if (sendto(sockfd, erspan_packet, total_length, 0, (struct sockaddr *)&dest_addr, dest_addr_size) == -1) {
|
||||||
|
perror("sendto");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(erspan_packet); // Free allocated memory
|
||||||
|
printf("Sent ERSPAN packet with sequence number: %u\n", sequence_number - 1);
|
||||||
|
} else {
|
||||||
|
// TZSP Encapsulation
|
||||||
|
|
||||||
|
// Create TZSP Header
|
||||||
|
struct tzsp_header tzsp;
|
||||||
|
tzsp.version = 1; // TZSP Version 1
|
||||||
|
tzsp.type = 1; // Type 1 for packet
|
||||||
|
tzsp.encapsulated_protocol = htons(1); // Ethernet
|
||||||
|
|
||||||
|
// Create TZSP Tagged Field for End of Fields
|
||||||
|
struct tzsp_tagged end_tag;
|
||||||
|
end_tag.type = 1; // End of Fields
|
||||||
|
|
||||||
|
// Calculate total length
|
||||||
|
unsigned short total_length = header.caplen + TZSP_ENCAP_LEN + TZSP_TAGGED_LEN;
|
||||||
|
tzsp.length = htons(total_length);
|
||||||
|
|
||||||
|
// Allocate memory for TZSP packet
|
||||||
|
unsigned char *tzsp_packet = (unsigned char *)malloc(total_length);
|
||||||
|
if (tzsp_packet == NULL) {
|
||||||
|
perror("malloc");
|
||||||
|
continue; // Skip this packet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy TZSP header and tagged field and packet data into the new buffer
|
||||||
|
unsigned char *ptr = tzsp_packet;
|
||||||
|
memcpy(ptr, &tzsp, TZSP_ENCAP_LEN);
|
||||||
|
ptr += TZSP_ENCAP_LEN;
|
||||||
|
memcpy(ptr, &end_tag, TZSP_TAGGED_LEN);
|
||||||
|
ptr += TZSP_TAGGED_LEN;
|
||||||
|
memcpy(ptr, packet, header.caplen);
|
||||||
|
|
||||||
|
// Send packet via UDP with TZSP encapsulation
|
||||||
|
if (sendto(sockfd, tzsp_packet, total_length, 0, (struct sockaddr *)&dest_addr, dest_addr_size) == -1) {
|
||||||
|
perror("sendto");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tzsp_packet); // Free allocated memory
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create TZSP Header
|
|
||||||
struct tzsp_header tzsp;
|
|
||||||
tzsp.version = 1; // TZSP Version 1
|
|
||||||
tzsp.type = 1; // Type 1 for packet
|
|
||||||
tzsp.encapsulated_protocol = htons(1); // Ethernet
|
|
||||||
|
|
||||||
// Create TZSP Tagged Field for End of Fields
|
|
||||||
struct tzsp_tagged end_tag;
|
|
||||||
end_tag.type = 1; // End of Fields
|
|
||||||
|
|
||||||
// Calculate total length
|
|
||||||
unsigned short total_length = header.caplen + TZSP_ENCAP_LEN + TZSP_TAGGED_LEN;
|
|
||||||
tzsp.length = htons(total_length);
|
|
||||||
|
|
||||||
// Allocate memory for TZSP packet
|
|
||||||
unsigned char *tzsp_packet = (unsigned char *)malloc(total_length);
|
|
||||||
if (tzsp_packet == NULL) {
|
|
||||||
perror("malloc");
|
|
||||||
continue; // Skip this packet
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy TZSP header and tagged field and packet data into the new buffer
|
|
||||||
unsigned char *ptr = tzsp_packet;
|
|
||||||
memcpy(ptr, &tzsp, TZSP_ENCAP_LEN);
|
|
||||||
ptr += TZSP_ENCAP_LEN;
|
|
||||||
memcpy(ptr, &end_tag, TZSP_TAGGED_LEN);
|
|
||||||
ptr += TZSP_TAGGED_LEN;
|
|
||||||
memcpy(ptr, packet, header.caplen);
|
|
||||||
|
|
||||||
// Send packet via UDP with TZSP encapsulation
|
|
||||||
if (sendto(sockfd, tzsp_packet, total_length, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) == -1) {
|
|
||||||
perror("sendto");
|
|
||||||
}
|
|
||||||
|
|
||||||
free(tzsp_packet); // Free allocated memory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pcap_freecode(&fp);
|
pcap_freecode(&fp);
|
||||||
pcap_close(handle);
|
pcap_close(handle);
|
||||||
if (alldevs != NULL) {
|
|
||||||
pcap_freealldevs(alldevs); // Free the device list only if devices were found
|
|
||||||
}
|
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
27
pcapmirror.8
27
pcapmirror.8
@@ -1,4 +1,4 @@
|
|||||||
.TH PCAPMIRROR 1 "March 22, 2025" "pcapmirror 0.3" "User Commands"
|
.TH PCAPMIRROR 1 "March 24, 2025" "pcapmirror 0.5" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
pcapmirror \- A command-line tool for capturing and mirroring network traffic
|
pcapmirror \- A command-line tool for capturing and mirroring network traffic
|
||||||
|
|
||||||
@@ -18,15 +18,36 @@ Specify the capture interface (e.g., eth0).
|
|||||||
.B \-f \fIfilter\fR
|
.B \-f \fIfilter\fR
|
||||||
Specify the capture filter in BPF syntax (e.g., tcp port 80).
|
Specify the capture filter in BPF syntax (e.g., tcp port 80).
|
||||||
.TP
|
.TP
|
||||||
.B \-r \fIip_address\fR
|
.B \-r \fIhost/ipv4/ipv6\fR
|
||||||
Specify the destination IP address (required).
|
Specify the destination host (required).
|
||||||
.TP
|
.TP
|
||||||
.B \-p \fIport\fR
|
.B \-p \fIport\fR
|
||||||
Specify the destination port (default: 37008).
|
Specify the destination port (default: 37008).
|
||||||
.TP
|
.TP
|
||||||
|
.B \-e
|
||||||
|
Use ERSPAN encapsulation.
|
||||||
|
.TP
|
||||||
|
.B \-s \fIsource_ip\fR
|
||||||
|
Specify the source IP address (required for ERSPAN).
|
||||||
|
.TP
|
||||||
|
.B \-S \fIsession_id\fR
|
||||||
|
Specify the session ID (default: 42, must be between 0 and 1023)
|
||||||
|
.TP
|
||||||
|
.B \-4
|
||||||
|
Force IPv4 host lookup.
|
||||||
|
.TP
|
||||||
|
.B \-6
|
||||||
|
Force IPv6 host lookup.
|
||||||
|
.TP
|
||||||
|
.B \-l
|
||||||
|
List available network interfaces.
|
||||||
|
.TP
|
||||||
.B \-v
|
.B \-v
|
||||||
Enable verbose mode (prints packet information).
|
Enable verbose mode (prints packet information).
|
||||||
.TP
|
.TP
|
||||||
|
.B \-c
|
||||||
|
Count matching packets (overrides verbose mode)
|
||||||
|
.TP
|
||||||
.B \-h
|
.B \-h
|
||||||
Show this help message.
|
Show this help message.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: pcapmirror
|
Name: pcapmirror
|
||||||
Version: 0.3
|
Version: 0.6
|
||||||
Release: %(perl -e 'print time()')%{?dist}
|
Release: %(perl -e 'print time()')%{?dist}
|
||||||
Summary: A simple packet capture mirror
|
Summary: A simple packet capture mirror
|
||||||
License: BSD 3-Clause License
|
License: BSD 3-Clause License
|
||||||
@@ -10,7 +10,7 @@ BuildRequires: make
|
|||||||
BuildRequires: libpcap-devel
|
BuildRequires: libpcap-devel
|
||||||
|
|
||||||
%description
|
%description
|
||||||
pcapmirror is a command-line tool for capturing and mirroring network traffic using TZSP encapsulation. It leverages the `libpcap` library for packet capture and supports BPF syntax for filtering traffic.
|
pcapmirror is a command-line tool for capturing and mirroring network traffic using TZSP or ERSPAN encapsulation. It leverages the `libpcap` library for packet capture and supports BPF syntax for filtering traffic.
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%make_build
|
%make_build
|
||||||
@@ -26,6 +26,19 @@ pcapmirror is a command-line tool for capturing and mirroring network traffic us
|
|||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sun Apr 20 2025 Matthias Cramer <cramer@freestone.net> 0.6-1
|
||||||
|
- Erspan Encapsulation support
|
||||||
|
- added option -e to set the encapsulation type
|
||||||
|
- added option -S to set ERSPAN session id
|
||||||
|
* Sat Mar 29 2025 Matthias Cramer <cramer@freestone.net> 0.5-1
|
||||||
|
- new option -c to count matching packets (overrides verbose mode)
|
||||||
|
- reworked packet decoder to also decode arp, vlan and qinq packets
|
||||||
|
- well known protocols numbers are now decoded
|
||||||
|
- works now on MacOS and OpenBSD
|
||||||
|
* Mon Mar 24 2025 Matthias Cramer <cramer@freesone.net> 0.4-1
|
||||||
|
- IPv6 support for remote destination
|
||||||
|
- remote destination can now also be hostname
|
||||||
|
- added option to enforce IPv4 and IPv6 for remote destination
|
||||||
* Sat Mar 22 2025 Matthias Cramer <cramer@freesone.net> 0.3-1
|
* Sat Mar 22 2025 Matthias Cramer <cramer@freesone.net> 0.3-1
|
||||||
- added manpage
|
- added manpage
|
||||||
* Sat Mar 22 2025 Matthias Cramer <cramer@freesone.net> 0.2-1
|
* Sat Mar 22 2025 Matthias Cramer <cramer@freesone.net> 0.2-1
|
||||||
|
|||||||
Reference in New Issue
Block a user