31 Commits

Author SHA1 Message Date
a542f6ff7e Strip on install, change compiler to cc so no change on BSD is needed 2025-10-30 23:00:02 +01:00
255f1d0773 rocky10 and trixie-publish 2025-09-17 20:13:05 +02:00
ce6e5ec9a8 clean gzipped man 2025-08-26 13:47:14 +02:00
66b45a06f2 static does not work 2025-08-25 22:26:02 +02:00
6ca07a3f6a additional libraries 2025-08-25 22:22:15 +02:00
316f792927 static build 2025-08-25 22:17:20 +02:00
65f3e923c1 fix pios 2025-08-25 21:57:40 +02:00
204b636727 fix 2025-08-25 21:52:58 +02:00
3f58677aa0 ubuntu builds 2025-08-25 21:51:18 +02:00
5dd567ee20 generic docker build 2025-08-25 20:27:13 +02:00
626b379a54 build all with the sam runner, but different image 2025-08-25 20:03:46 +02:00
4d25135e58 image version 2025-08-25 18:28:38 +02:00
0d41857e52 trixie build prepare 2025-08-25 18:12:03 +02:00
ebcd9f697d rocky version 2025-04-20 17:10:31 +02:00
d2133f7531 Merge branch 'erspan' into 'main'
Erspan Support

See merge request cramer/pcapmirror!1
2025-04-20 16:54:46 +02:00
c202697f7b ERSPAN RELEASE 2025-04-20 16:53:26 +02:00
9fcac378ae additional parameters 2025-04-03 20:39:31 +02:00
87d74f0db9 first working erspan implementation 2025-04-03 20:11:42 +02:00
6baf639a9c error when interface is not specified 2025-04-03 17:09:25 +02:00
e3097eec3a back to original no benefit 2025-03-30 15:32:28 +02:00
a2c0ad8bfb paralell build try 2025-03-30 15:26:01 +02:00
e39c130a69 extended changelog 2025-03-30 15:18:00 +02:00
a128da1328 vlan and qinq handling, protocol printout 2025-03-30 15:15:24 +02:00
99fbd6f600 Bump version to 0.5 2025-03-29 11:47:39 +01:00
5e595b0ae7 new packet match count option 2025-03-29 10:45:34 +01:00
5ee45654f2 works on OpenBSD 2025-03-28 21:55:33 +01:00
1da4934e59 include cleanup, compiles on openbsd, but does not work 2025-03-28 21:17:19 +01:00
0897fa1755 new packet decoder with arp support. removed default filter 2025-03-28 20:26:05 +01:00
1233bf5ede code cleanup 2025-03-26 21:27:18 +01:00
038a6de68e any interface is not supported because of missing ethernet header 2025-03-26 07:36:41 +01:00
39a8b7c016 added -l to list all interfaces 2025-03-25 06:42:58 +01:00
8 changed files with 628 additions and 155 deletions

View File

@@ -1,11 +1,10 @@
stages: stages:
- build - build
- publish - publish
variables: variables:
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
VERSION: 0.4 VERSION: 0.6
build-bookworm: build-bookworm:
stage: build stage: build
@@ -13,7 +12,8 @@ build-bookworm:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-bookworm:v1
script: script:
- tar -czf ../pcapmirror_$VERSION.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
@@ -34,11 +34,45 @@ publish-bookworm:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-trixie:v1
script: script:
- apt-get update && apt-get install -y curl
- ls -la build - 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-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-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-trixie:v1
script:
- 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
@@ -46,7 +80,8 @@ build-sid:
only: only:
- tags - tags
tags: tags:
- sid - docker-generic
image: debian-package-builder-sid:v1
script: script:
- tar -czf ../pcapmirror_$VERSION.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
@@ -67,11 +102,48 @@ publish-sid:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-trixie:v1
script: script:
- apt-get update && apt-get install -y curl
- ls -la build - 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-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-sid_amd64.deb'
build-rocky10:
stage: build
needs: []
only:
- tags
tags:
- docker-generic
image: rockylinux-package-builder-10: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-rocky10:
stage: publish
needs:
- build-rocky10
dependencies:
- build-rocky10
only:
- tags
tags:
- docker-generic
image: debian-package-builder-trixie:v1
script:
- ls -la build
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-$VERSION-*.el10.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux10/pcapmirror-$VERSION-1.el10.x86_64.rpm'
build-rocky9: build-rocky9:
stage: build stage: build
@@ -79,11 +151,12 @@ build-rocky9:
only: only:
- tags - tags
tags: tags:
- rocky9 - docker-generic
image: rockylinux-package-builder-9:v1
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-v$VERSION.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
@@ -103,11 +176,11 @@ publish-rocky9:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-trixie:v1
script: script:
- apt-get update && apt-get install -y curl
- ls -la build - 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' - '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.el9.x86_64.rpm'
build-rocky8: build-rocky8:
stage: build stage: build
@@ -115,11 +188,12 @@ build-rocky8:
only: only:
- tags - tags
tags: tags:
- rocky8 - docker-generic
image: rockylinux-package-builder-8:v1
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-v$VERSION.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
@@ -139,9 +213,9 @@ publish-rocky8:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-trixie:v1
script: script:
- apt-get update && apt-get install -y curl
- ls -la build - 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' - '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'
@@ -151,7 +225,8 @@ build-pios12:
only: only:
- tags - tags
tags: tags:
- pios12 - docker-generic
image: pios-package-builder-bookworm:v1
script: script:
- tar -czf ../pcapmirror_$VERSION.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
@@ -172,11 +247,11 @@ publish-pios12:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-trixie:v1
script: script:
- apt-get update && apt-get install -y curl
- ls -la build - 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' - '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: build-pios12-64:
stage: build stage: build
@@ -184,7 +259,8 @@ build-pios12-64:
only: only:
- tags - tags
tags: tags:
- pios12-64 - docker-generic
image: pios-package-builder-bookworm:64-v1
script: script:
- tar -czf ../pcapmirror_$VERSION.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
@@ -205,8 +281,77 @@ publish-pios12-64:
only: only:
- tags - tags
tags: tags:
- bookworm - docker-generic
image: debian-package-builder-trixie:v1
script: script:
- apt-get update && apt-get install -y curl
- ls -la build - 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' - '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-trixie:v1
script:
- 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-trixie:v1
script:
- 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'

View File

@@ -1,7 +1,7 @@
# Makefile for pcapmirror # Makefile for pcapmirror
# Compiler # Compiler
CC = gcc CC = cc
# Compiler flags # Compiler flags
CFLAGS = -Wall -g CFLAGS = -Wall -g
@@ -24,6 +24,9 @@ PREFIX = /usr
# Default rule # Default rule
all: $(TARGET) man all: $(TARGET) man
static: $(OBJS)
$(CC) $(CFLAGS) -static $(OBJS) -o $(TARGET) $(LIBS) -ldbus-1 -lsystemd -lcap
# Create executable # Create executable
$(TARGET): $(OBJS) $(TARGET): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(TARGET) $(LIBS) $(CC) $(CFLAGS) $(OBJS) -o $(TARGET) $(LIBS)
@@ -37,12 +40,12 @@ man:
# Clean up object files and executable # Clean up object files and executable
clean: clean:
rm -f -f $(OBJS) $(TARGET) rm -f -f $(OBJS) $(TARGET) pcapmirror.8.gz
# 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 -s -D $(TARGET) $(DESTDIR)$(PREFIX)/bin/$(TARGET)
install -D $(TARGET).8 $(DESTDIR)$(PREFIX)/share/man/man8/$(TARGET).8 install -D $(TARGET).8 $(DESTDIR)$(PREFIX)/share/man/man8/$(TARGET).8

View File

@@ -2,7 +2,7 @@
# pcapmirror # pcapmirror
![pcapmirror logo](logo/pcapmirror_logo_small.png) ![pcapmirror logo](logo/pcapmirror_logo_small.png)
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
@@ -16,9 +16,14 @@ pcapmirror [options]
* -f <filter> Specify the capture filter (BPF syntax) * -f <filter> Specify the capture filter (BPF syntax)
* -r <host/ipv4/ipv6> Specify the destination host (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)
* -e Use ERSPAN encapsulation (default: TZSP)
* -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 * -4 Force IPv4 host lookup
* -6 Force IPv6 host lookup * -6 Force IPv6 host lookup
* -l List available network interfaces.
* -v Enable verbose mode * -v Enable verbose mode
* -c Count matching packets (overrides verbose mode)
* -h Show this help message * -h Show this help message
### Example: ### Example:
@@ -46,6 +51,15 @@ 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, 13 + unstable (sid)
* Rocky Linux 8, 9, 10
* PiOS 12 (bookworm)
* OpenBSD 7.6, 7.7
* MacOS 15
Compile the program: Compile the program:
```bash ```bash
make make

17
debian/changelog vendored
View File

@@ -1,3 +1,20 @@
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 pcapmirror (0.4-1) unstable; urgency=medium
* IPv6 support for remote destination * IPv6 support for remote destination

4
debian/control vendored
View File

@@ -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.

424
main.c
View File

@@ -6,21 +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 <netinet/in.h>
#include <unistd.h> #include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pcap.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.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 {
@@ -35,10 +38,85 @@ 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) {
@@ -48,18 +126,22 @@ void print_usage(const char *program_name) {
printf(" -f <filter> Specify the capture filter (BPF syntax)\n"); printf(" -f <filter> Specify the capture filter (BPF syntax)\n");
printf(" -r <host/ipv4/ipv6> Specify the destination host (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(" -e Use ERSPAN encapsulation (default: TZSP)\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(" -4 Force IPv4 host lookup\n");
printf(" -6 Force IPv6 host lookup\n"); printf(" -6 Force IPv6 host lookup\n");
printf(" -l List available network interfaces\n");
printf(" -v Enable verbose mode\n"); printf(" -v Enable verbose mode\n");
printf(" -c Count matching packets (overrides verbose mode)\n");
printf(" -h Show this help message\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 *mirror_host = 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
@@ -67,11 +149,21 @@ int main(int argc, char *argv[]) {
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_ipv4 = 0; // Flag to force IPv4 lookup
int force_ipv6 = 0; // Flag to force IPv6 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 addrinfo hints, *res; struct addrinfo hints, *res;
struct sockaddr_storage dest_addr; // Declare dest_addr 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)) {
@@ -102,16 +194,50 @@ int main(int argc, char *argv[]) {
force_ipv4 = 1; // Force IPv4 lookup force_ipv4 = 1; // Force IPv4 lookup
} else if (strcmp(argv[i], "-6") == 0) { } else if (strcmp(argv[i], "-6") == 0) {
force_ipv6 = 1; // Force IPv6 lookup 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 (mirror_host == 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;
} }
// Check that interface is not any
if (dev_name != NULL && strcmp(dev_name, "any") == 0) {
fprintf(stderr, "Error: Interface 'any' is not supported.\n");
return 1;
}
// Check if the interface is specified
if (dev_name == NULL) {
fprintf(stderr, "Error: Interface must be specified\n");
return 1;
}
// Resolve the destination address // Resolve the destination address
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6 hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
@@ -128,13 +254,45 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
// Create UDP socket // 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;
}
} else {
// Create a UDP socket for TZSP
sockfd = socket(res->ai_family, SOCK_DGRAM, 0); sockfd = socket(res->ai_family, SOCK_DGRAM, 0);
if (sockfd == -1) { if (sockfd == -1) {
perror("socket"); perror("socket");
freeaddrinfo(res); freeaddrinfo(res);
return 1; return 1;
} }
}
memset(&dest_addr, 0, sizeof(dest_addr));
// Set the destination address // Set the destination address
if (res->ai_family == AF_INET) { if (res->ai_family == AF_INET) {
@@ -165,34 +323,6 @@ int main(int argc, char *argv[]) {
printf("Resolved Destination IP: %s\n", resolved_ip); printf("Resolved Destination IP: %s\n", resolved_ip);
printf("Destination Port: %d\n", dest_port); printf("Destination Port: %d\n", dest_port);
// If no interface is specified, find all devices
if (dev_name == NULL) {
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
return(1);
}
// Print the available devices for debugging
/*
pcap_if_t *device;
printf("Available devices:\n");
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 (alldevs == NULL) {
fprintf(stderr, "No devices found. Make sure you have permissions to capture traffic.\n");
return 1;
}
dev_name = alldevs->name; // Use the name of the first device
} else {
// Interface specified via command line, no need to find all devices
alldevs = NULL; // Set alldevs to NULL to avoid potential issues
}
pcap_t *handle; pcap_t *handle;
struct bpf_program fp; struct bpf_program fp;
bpf_u_int32 mask; bpf_u_int32 mask;
@@ -204,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);
} }
@@ -237,41 +358,192 @@ int main(int argc, char *argv[]) {
struct ip *ip_header; // Declare ip4_header struct ip *ip_header; // Declare ip4_header
struct ip6_hdr *ip6_header; // Declare ip6_header struct ip6_hdr *ip6_header; // Declare ip6_header
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
gettimeofday(&last_count, NULL);
printf("\n");
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(&current_time, NULL);
// IPv4
inet_ntop(AF_INET, &(ip_header->ip_src), source_ip_str, INET6_ADDRSTRLEN); long elapsed_ms = current_time.tv_sec * 1000 + (current_time.tv_usec /1000)-
inet_ntop(AF_INET, &(ip_header->ip_dst), dest_ip_str, INET6_ADDRSTRLEN); (last_count.tv_sec * 1000 + (last_count.tv_usec /1000));
if (elapsed_ms >= 500) {
printf("\rPacket count: %llu", packet_count);
fflush(stdout);
last_count = current_time; // Reset the timer
}
}
if (verbose) { if (verbose) {
printf("IPv4 Packet: %s -> %s, IP Protocol: %d\n",
source_ip_str, dest_ip_str, ip_header->ip_p); // 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));
} }
} else if (ip_protocol == 6) {
// IPv6 if (ether_type == ETHERTYPE_IP) {
ip6_header = (struct ip6_hdr*)(packet + ETHERNET_HEADER_LENGTH); // 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_src), source_ip_str, INET6_ADDRSTRLEN);
inet_ntop(AF_INET6, &(ip6_header->ip6_dst), dest_ip_str, INET6_ADDRSTRLEN); inet_ntop(AF_INET6, &(ip6_header->ip6_dst), dest_ip_str, INET6_ADDRSTRLEN);
if (verbose) { printf("IPv6 Packet: %s -> %s, Next Header: %s\n",
printf("IPv6 Packet: %s -> %s, Next Header: %d\n", source_ip_str, dest_ip_str, lookup_protocol_name(ip6_header->ip6_nxt));
source_ip_str, dest_ip_str, 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);
}
}
// Encapsulation logic
if (use_erspan) {
// 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 { } else {
printf("Non-IP Packet\n"); ip_header.ip_src.s_addr = inet_addr("192.168.1.1"); // Default source IP
continue;
} }
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 // Create TZSP Header
struct tzsp_header tzsp; struct tzsp_header tzsp;
tzsp.version = 1; // TZSP Version 1 tzsp.version = 1; // TZSP Version 1
@@ -302,18 +574,16 @@ int main(int argc, char *argv[]) {
memcpy(ptr, packet, header.caplen); memcpy(ptr, packet, header.caplen);
// Send packet via UDP with TZSP encapsulation // Send packet via UDP with TZSP encapsulation
if (sendto(sockfd, tzsp_packet, total_length, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) == -1) { if (sendto(sockfd, tzsp_packet, total_length, 0, (struct sockaddr *)&dest_addr, dest_addr_size) == -1) {
perror("sendto"); perror("sendto");
} }
free(tzsp_packet); // Free allocated memory 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);
} }

View File

@@ -1,4 +1,4 @@
.TH PCAPMIRROR 1 "March 24, 2025" "pcapmirror 0.4" "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
@@ -24,15 +24,30 @@ Specify the destination host (required).
.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 .B \-4
Force IPv4 host lookup. Force IPv4 host lookup.
.TP .TP
.B \-6 .B \-6
Force IPv6 host lookup. Force IPv6 host lookup.
.TP .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.

View File

@@ -1,5 +1,5 @@
Name: pcapmirror Name: pcapmirror
Version: 0.4 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,7 +26,16 @@ pcapmirror is a command-line tool for capturing and mirroring network traffic us
%changelog %changelog
* Sat Mar 24 2025 Matthias Cramer <cramer@freesone.net> 0.4-1 * 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 - IPv6 support for remote destination
- remote destination can now also be hostname - remote destination can now also be hostname
- added option to enforce IPv4 and IPv6 for remote destination - added option to enforce IPv4 and IPv6 for remote destination