mirror of
https://git.freestone.net/cramer/pcapmirror.git
synced 2025-12-31 03:50:27 +01:00
Compare commits
68 Commits
v0.2
...
0.6-ubuntu
| Author | SHA1 | Date | |
|---|---|---|---|
| 65f3e923c1 | |||
| 204b636727 | |||
| 3f58677aa0 | |||
| 5dd567ee20 | |||
| 626b379a54 | |||
| 4d25135e58 | |||
| 0d41857e52 | |||
| 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 | |||
| 9d91fda7e1 | |||
| 3c007c3fcc | |||
| 8ff71daa36 | |||
| 3a078c5ba2 | |||
| 232fe98fd9 | |||
| 9e30e4e7aa | |||
| 2f4c086eba | |||
| e76fb7e61f | |||
| 269488b60d | |||
| b07369b5b0 | |||
| 8e01a2823c | |||
| d86c3829f5 | |||
| 1471e39e50 | |||
| 0ffa2b82bf | |||
| f3604f7390 | |||
| 0a71915fd8 | |||
| f207c5bdc2 | |||
| 8869da41b5 | |||
| f1c0985c99 | |||
| bc23a64b46 | |||
| b334754945 | |||
| faece01978 | |||
| 02c69f2936 | |||
| c2475e04f9 | |||
| ba55d4d958 | |||
| b176c8edef | |||
| 1d67a904b9 | |||
| 646a888e84 | |||
| aa86180054 | |||
| 94266b53a9 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
|||||||
*~
|
*~
|
||||||
*.o
|
*.o
|
||||||
|
*.gz
|
||||||
pcapmirror
|
pcapmirror
|
||||||
debian/debhelper-build-stamp
|
debian/debhelper-build-stamp
|
||||||
debian/pcapmirror.substvars
|
debian/pcapmirror.substvars
|
||||||
|
debian/files
|
||||||
|
|||||||
311
.gitlab-ci.yml
311
.gitlab-ci.yml
@@ -1,18 +1,22 @@
|
|||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
- publish
|
||||||
|
|
||||||
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
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
script:
|
script:
|
||||||
- tar -czf ../pcapmirror_0.2.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
|
||||||
@@ -20,21 +24,69 @@ build-bookworm:
|
|||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/*.deb
|
- build
|
||||||
- build/*.dsc
|
|
||||||
- build/*.tar.xz
|
publish-bookworm:
|
||||||
- build/*.changes
|
stage: publish
|
||||||
- build/*.buildinfo
|
needs:
|
||||||
- build/*.diff.gz
|
- build-bookworm
|
||||||
|
dependencies:
|
||||||
|
- build-bookworm
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_$VERSION-deb12_amd64.deb'
|
||||||
|
|
||||||
|
build-trixie:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-trixie:v1
|
||||||
|
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-trixie:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-trixie
|
||||||
|
dependencies:
|
||||||
|
- build-trixie
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/trixie/pcapmirror_$VERSION-deb13_amd64.deb'
|
||||||
|
|
||||||
build-sid:
|
build-sid:
|
||||||
stage: build
|
stage: build
|
||||||
|
needs: []
|
||||||
only:
|
only:
|
||||||
- tags
|
- tags
|
||||||
tags:
|
tags:
|
||||||
- sid
|
- docker-generic
|
||||||
|
image: debian-package-builder-sid:v1
|
||||||
script:
|
script:
|
||||||
- tar -czf ../pcapmirror_0.2.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
|
||||||
@@ -42,9 +94,236 @@ build-sid:
|
|||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/*.deb
|
- build
|
||||||
- build/*.dsc
|
|
||||||
- build/*.tar.xz
|
publish-sid:
|
||||||
- build/*.changes
|
stage: publish
|
||||||
- build/*.buildinfo
|
needs:
|
||||||
- build/*.diff.gz
|
- build-sid
|
||||||
|
dependencies:
|
||||||
|
- build-sid
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/sid/pcapmirror_$VERSION-sid_amd64.deb'
|
||||||
|
|
||||||
|
build-rocky9:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: rockylinux-package-builder-9:v1
|
||||||
|
script:
|
||||||
|
- dnf install -y libpcap-devel
|
||||||
|
- mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
- tar -czf /root/rpmbuild/SOURCES/pcapmirror-v${VERSION}.tar.gz --exclude=debian --exclude=.git .
|
||||||
|
- cp -r * /root/rpmbuild/BUILD
|
||||||
|
- rpmbuild -ba pcapmirror.spec
|
||||||
|
- mkdir -p build
|
||||||
|
- mv /root/rpmbuild/RPMS/x86_64/pcapmirror*.* build/
|
||||||
|
- mv /root/rpmbuild/SRPMS/pcapmirror*.* build/
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
|
||||||
|
publish-rocky9:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-rocky9
|
||||||
|
dependencies:
|
||||||
|
- build-rocky9
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get install -y curl
|
||||||
|
- ls -la build
|
||||||
|
- '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'
|
||||||
|
|
||||||
|
build-rocky8:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: rockylinux-package-builder-8:v1
|
||||||
|
script:
|
||||||
|
- dnf install -y libpcap-devel
|
||||||
|
- mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
- tar -czf /root/rpmbuild/SOURCES/pcapmirror-v${VERSION}.tar.gz --exclude=debian --exclude=.git .
|
||||||
|
- cp -r * /root/rpmbuild/BUILD
|
||||||
|
- rpmbuild -ba pcapmirror.spec
|
||||||
|
- mkdir -p build
|
||||||
|
- mv /root/rpmbuild/RPMS/x86_64/pcapmirror*.* build/
|
||||||
|
- mv /root/rpmbuild/SRPMS/pcapmirror*.* build/
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- build
|
||||||
|
|
||||||
|
publish-rocky8:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-rocky8
|
||||||
|
dependencies:
|
||||||
|
- build-rocky8
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
script:
|
||||||
|
- apt-get update && apt-get install -y curl
|
||||||
|
- ls -la build
|
||||||
|
- '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'
|
||||||
|
|
||||||
|
build-pios12:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: pios-package-builder-bookworm:v1
|
||||||
|
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:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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-piso12_armhf.deb'
|
||||||
|
|
||||||
|
build-pios12-64:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: pios-package-builder-bookworm:64-v1
|
||||||
|
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:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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-pios12_arm64.deb'
|
||||||
|
|
||||||
|
build-plucky:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: ubuntu-package-builder-plucky:v1
|
||||||
|
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-plucky:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-plucky
|
||||||
|
dependencies:
|
||||||
|
- build-plucky
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/trixie/pcapmirror_$VERSION-ubu25.04_amd64.deb'
|
||||||
|
|
||||||
|
build-noble:
|
||||||
|
stage: build
|
||||||
|
needs: []
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: ubuntu-package-builder-noble:v1
|
||||||
|
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-noble:
|
||||||
|
stage: publish
|
||||||
|
needs:
|
||||||
|
- build-noble
|
||||||
|
dependencies:
|
||||||
|
- build-noble
|
||||||
|
only:
|
||||||
|
- tags
|
||||||
|
tags:
|
||||||
|
- docker-generic
|
||||||
|
image: debian-package-builder-bookworm:v1
|
||||||
|
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_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/trixie/pcapmirror_$VERSION-ubu24.04_amd64.deb'
|
||||||
|
|||||||
10
Makefile
10
Makefile
@@ -22,7 +22,7 @@ TARGET = pcapmirror
|
|||||||
PREFIX = /usr
|
PREFIX = /usr
|
||||||
|
|
||||||
# Default rule
|
# Default rule
|
||||||
all: $(TARGET)
|
all: $(TARGET) man
|
||||||
|
|
||||||
# Create executable
|
# Create executable
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
@@ -32,6 +32,9 @@ $(TARGET): $(OBJS)
|
|||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
man:
|
||||||
|
gzip -9 -c pcapmirror.8 > pcapmirror.8.gz
|
||||||
|
|
||||||
# Clean up object files and executable
|
# Clean up object files and executable
|
||||||
clean:
|
clean:
|
||||||
rm -f -f $(OBJS) $(TARGET)
|
rm -f -f $(OBJS) $(TARGET)
|
||||||
@@ -39,11 +42,14 @@ clean:
|
|||||||
# Install the executable
|
# Install the executable
|
||||||
install: $(TARGET)
|
install: $(TARGET)
|
||||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||||
install -D $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
|
install -D $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
|
||||||
|
install -D $(TARGET).8 $(DESTDIR)$(PREFIX)/share/man/man8/$(TARGET).8
|
||||||
|
|
||||||
|
|
||||||
# Uninstall the executable
|
# Uninstall the executable
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -f $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
|
rm -f $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
|
||||||
|
rm -f $(TARGET).8.gz $(DESTDIR)$(PREFIX)/share/man/man8/$(TARGET).8.gz
|
||||||
|
|
||||||
# Run the executable (example)
|
# Run the executable (example)
|
||||||
run: $(TARGET)
|
run: $(TARGET)
|
||||||
|
|||||||
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
|
||||||
```
|
```
|
||||||
|
|||||||
31
debian/changelog
vendored
31
debian/changelog
vendored
@@ -1,3 +1,34 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
* added manpage
|
||||||
|
|
||||||
|
-- Matthias Cramer <cramer@freestone.net> Fri, 21 Mar 2025 16:00:05 +0100
|
||||||
|
|
||||||
pcapmirror (0.2-1) unstable; urgency=medium
|
pcapmirror (0.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
* First Debian package
|
* First Debian package
|
||||||
|
|||||||
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.
|
||||||
3
debian/files
vendored
3
debian/files
vendored
@@ -1,3 +0,0 @@
|
|||||||
pcapmirror-dbgsym_0.2-1_amd64.deb debug optional automatic=yes
|
|
||||||
pcapmirror_0.2-1_amd64.buildinfo net optional
|
|
||||||
pcapmirror_0.2-1_amd64.deb net optional
|
|
||||||
1
debian/install
vendored
1
debian/install
vendored
@@ -1 +1,2 @@
|
|||||||
pcapmirror /usr/bin
|
pcapmirror /usr/bin
|
||||||
|
pcapmirror.8 /usr/share/man/man8
|
||||||
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 |
590
main.c
590
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_name = alldevs->name; // Use the name of the first device
|
// 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;
|
||||||
|
}
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
73
pcapmirror.8
Normal file
73
pcapmirror.8
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
.TH PCAPMIRROR 1 "March 24, 2025" "pcapmirror 0.5" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
pcapmirror \- A command-line tool for capturing and mirroring network traffic
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B pcapmirror
|
||||||
|
[\fIoptions\fR]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B pcapmirror
|
||||||
|
is a command-line tool for capturing network traffic and mirroring it to a remote destination using TZSP encapsulation. It leverages the \fBlibpcap\fR 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.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B \-i \fIinterface\fR
|
||||||
|
Specify the capture interface (e.g., eth0).
|
||||||
|
.TP
|
||||||
|
.B \-f \fIfilter\fR
|
||||||
|
Specify the capture filter in BPF syntax (e.g., tcp port 80).
|
||||||
|
.TP
|
||||||
|
.B \-r \fIhost/ipv4/ipv6\fR
|
||||||
|
Specify the destination host (required).
|
||||||
|
.TP
|
||||||
|
.B \-p \fIport\fR
|
||||||
|
Specify the destination port (default: 37008).
|
||||||
|
.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
|
||||||
|
Enable verbose mode (prints packet information).
|
||||||
|
.TP
|
||||||
|
.B \-c
|
||||||
|
Count matching packets (overrides verbose mode)
|
||||||
|
.TP
|
||||||
|
.B \-h
|
||||||
|
Show this help message.
|
||||||
|
|
||||||
|
.SH EXAMPLES
|
||||||
|
To capture traffic on the eth0 interface, filter for TCP port 80, and send it to the destination, use the following command:
|
||||||
|
|
||||||
|
.EX
|
||||||
|
sudo pcapmirror -i eth0 -f "tcp port 80" -r 192.168.1.100 -p 47008 -v
|
||||||
|
.EE
|
||||||
|
|
||||||
|
.SH USAGE WITH WIRESHARK
|
||||||
|
With this tool, you can mirror traffic directly to a running Wireshark.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR bpf (2), tcpdump (1), wireshark (1), pcap (3)
|
||||||
|
|
||||||
|
.SH AUTHOR
|
||||||
|
Matthias Cramer <cramer@freestone.net>
|
||||||
|
|
||||||
|
.SH COPYRIGHT
|
||||||
|
Copyright (c) 2025, Matthias Cramer. All rights reserved.
|
||||||
45
pcapmirror.spec
Normal file
45
pcapmirror.spec
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
Name: pcapmirror
|
||||||
|
Version: 0.6
|
||||||
|
Release: %(perl -e 'print time()')%{?dist}
|
||||||
|
Summary: A simple packet capture mirror
|
||||||
|
License: BSD 3-Clause License
|
||||||
|
URL: https://git.freestone.net/cramer/pcapmirror
|
||||||
|
Source: https://git.freestone.net/cramer/pcapmirror/-/archive/v%version/pcapmirror-v%version.tar.gz
|
||||||
|
BuildRequires: gcc
|
||||||
|
BuildRequires: make
|
||||||
|
BuildRequires: libpcap-devel
|
||||||
|
|
||||||
|
%description
|
||||||
|
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
|
||||||
|
%make_build
|
||||||
|
|
||||||
|
%install
|
||||||
|
%make_install
|
||||||
|
|
||||||
|
%files
|
||||||
|
%{_bindir}/pcapmirror
|
||||||
|
%{_mandir}/man8/pcapmirror.8.gz
|
||||||
|
%license LICENSE
|
||||||
|
%doc README.md
|
||||||
|
|
||||||
|
|
||||||
|
%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
|
||||||
|
- added manpage
|
||||||
|
* Sat Mar 22 2025 Matthias Cramer <cramer@freesone.net> 0.2-1
|
||||||
|
- Initial release of pcapmirror
|
||||||
Reference in New Issue
Block a user