10 Commits

Author SHA1 Message Date
1b98555ed3 armhf 2025-03-23 19:02:21 +01:00
c640aa6f22 pios fix 2025-03-23 18:57:20 +01:00
c6584cde87 typo 2025-03-23 13:57:37 +01:00
4300e30f04 propper ip6 support, raspberry builds 2025-03-23 13:55:17 +01:00
0fdd4a9783 updated readme and added logos 2025-03-23 11:20:39 +01:00
49173fef25 let's try again 2025-03-22 19:00:45 +01:00
8e586dade0 paralell 2025-03-22 18:28:55 +01:00
c7e16616a1 again for the stupid ones 2025-03-22 18:11:13 +01:00
9d91fda7e1 PACKAGENAME not defined 2025-03-22 18:08:15 +01:00
3c007c3fcc test2 2025-03-22 18:04:29 +01:00
6 changed files with 150 additions and 64 deletions

View File

@@ -3,11 +3,13 @@ stages:
- build - build
- publish - publish
variables: variables:
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
build-bookworm: build-bookworm:
stage: build stage: build
needs: []
only: only:
- tags - tags
tags: tags:
@@ -25,6 +27,8 @@ build-bookworm:
publish-bookworm: publish-bookworm:
stage: publish stage: publish
needs:
- build-bookworm
dependencies: dependencies:
- build-bookworm - build-bookworm
only: only:
@@ -34,11 +38,12 @@ publish-bookworm:
script: script:
- apt-get update && apt-get install -y curl - apt-get update && apt-get install -y curl
- ls -la build - ls -la build
- echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/bookworm/pcapmirror_0.3-1_amd64.deb" - 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_0.3-1_amd64.deb"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/bookworm/pcapmirror_0.3-1_amd64.deb' - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_0.3-1_amd64.deb'
build-sid: build-sid:
stage: build stage: build
needs: []
only: only:
- tags - tags
tags: tags:
@@ -56,6 +61,8 @@ build-sid:
publish-sid: publish-sid:
stage: publish stage: publish
needs:
- build-sid
dependencies: dependencies:
- build-sid - build-sid
only: only:
@@ -65,11 +72,12 @@ publish-sid:
script: script:
- apt-get update && apt-get install -y curl - apt-get update && apt-get install -y curl
- ls -la build - ls -la build
- echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/sid/pcapmirror_0.3-1_amd64.deb" - 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/sid/pcapmirror_0.3-1_amd64.deb"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/sid/pcapmirror_0.3-1_amd64.deb' - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_amd64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/sid/pcapmirror_0.3-1_amd64.deb'
build-rocky9: build-rocky9:
stage: build stage: build
needs: []
only: only:
- tags - tags
tags: tags:
@@ -90,6 +98,8 @@ build-rocky9:
publish-rocky9: publish-rocky9:
stage: publish stage: publish
needs:
- build-rocky9
dependencies: dependencies:
- build-rocky9 - build-rocky9
only: only:
@@ -99,11 +109,12 @@ publish-rocky9:
script: script:
- apt-get update && apt-get install -y curl - apt-get update && apt-get install -y curl
- ls -la build - ls -la build
- echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/rockylinux9/pcapmirror-0.3-1.el8.x86_64.rpm" - 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux9/pcapmirror-0.3-1.el8.x86_64.rpm"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-0.3-*.el9.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/rockylinux9/pcapmirror-0.3-1.el8.x86_64.rpm' - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-0.3-*.el9.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux9/pcapmirror-0.3-1.el8.x86_64.rpm'
build-rocky8: build-rocky8:
stage: build stage: build
needs: []
only: only:
- tags - tags
tags: tags:
@@ -124,6 +135,8 @@ build-rocky8:
publish-rocky8: publish-rocky8:
stage: publish stage: publish
needs:
- build-rocky8
dependencies: dependencies:
- build-rocky8 - build-rocky8
only: only:
@@ -133,5 +146,39 @@ publish-rocky8:
script: script:
- apt-get update && apt-get install -y curl - apt-get update && apt-get install -y curl
- ls -la build - ls -la build
- echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm" - 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-0.3-*.el8.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGENAME}/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm' - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror-0.3-*.el8.x86_64.rpm ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm'
build-pios12:
stage: build
needs: []
only:
- tags
tags:
- pios12
script:
- tar -czf ../pcapmirror_0.3.orig.tar.gz --exclude=debian .
- apt-get update && apt-get install -y libpcap-dev
- dpkg-buildpackage -uc -us
- mkdir -p build
- mv ../pcapmirror*.* build/
artifacts:
paths:
- build
publish-pios12:
stage: publish
needs:
- build-pios12
dependencies:
- build-pios12
only:
- tags
tags:
- bookworm
script:
- apt-get update && apt-get install -y curl
- ls -la build
- 'echo "Uploading to: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_0.3-1_armhf.deb"'
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_armhf.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_0.3-1_armhf.deb'

View File

@@ -1,4 +1,7 @@
# pcapmirror # pcapmirror
![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). 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,14 @@ 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). * -4 Force IPv4 host lookup
* -h: Show this help message. * -6 Force IPv6 host lookup
* -v Enable verbose mode
* -h Show this help message
### Example: ### Example:
@@ -45,7 +50,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 +62,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
``` ```

BIN
logo/pcapmirror_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

120
main.c
View File

@@ -13,13 +13,9 @@ Copyright (c) 2025, Matthias Cramer, cramer@freestone.net
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h>
#include <unistd.h> #include <unistd.h>
#include <netinet/ip6.h>
#define ENABLE_IPV6
#ifdef ENABLE_IPV6
#include <netinet/ip6.h> // Include for IPv6 header definition
#endif
#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
@@ -48,12 +44,14 @@ int is_little_endian() {
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(" -4 Force IPv4 host lookup\n");
printf(" -h Show this help message\n"); printf(" -6 Force IPv6 host lookup\n");
printf(" -v Enable 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);
} }
@@ -63,14 +61,17 @@ int main(int argc, char *argv[]) {
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
char *filter_exp = "tcp port 8088"; // Default filter char *filter_exp = "tcp port 8088"; // 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
// 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
// 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 +79,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,27 +93,77 @@ 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
} }
} }
// Check if destination IP is provided // Check if destination IP is provided
if (dest_ip == NULL) { if (mirror_host == NULL) {
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) { // Resolve the destination address
perror("inet_pton"); memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
hints.ai_socktype = SOCK_DGRAM; // Datagram socket
if (force_ipv4) {
hints.ai_family = AF_INET; // Force IPv4
} else if (force_ipv6) {
hints.ai_family = AF_INET6; // Force IPv6
}
if (getaddrinfo(mirror_host, NULL, &hints, &res) != 0) {
perror("getaddrinfo");
return 1; return 1;
} }
dest_addr.sin_port = htons(dest_port); // Set the port // Create UDP socket
sockfd = socket(res->ai_family, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
freeaddrinfo(res);
return 1;
}
// 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);
// If no interface is specified, find all devices // If no interface is specified, find all devices
if (dev_name == NULL) { if (dev_name == NULL) {
@@ -195,17 +234,10 @@ 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;
printf("Using interface: %s\n", dev_name);
printf("Using filter: %s\n", filter_exp);
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)
@@ -225,9 +257,7 @@ int main(int argc, char *argv[]) {
printf("IPv4 Packet: %s -> %s, IP Protocol: %d\n", printf("IPv4 Packet: %s -> %s, IP Protocol: %d\n",
source_ip_str, dest_ip_str, ip_header->ip_p); source_ip_str, dest_ip_str, ip_header->ip_p);
} }
} } else if (ip_protocol == 6) {
#ifdef ENABLE_IPV6
else if (ip_protocol == 6) {
// IPv6 // IPv6
ip6_header = (struct ip6_hdr*)(packet + ETHERNET_HEADER_LENGTH); 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_src), source_ip_str, INET6_ADDRSTRLEN);
@@ -237,9 +267,7 @@ int main(int argc, char *argv[]) {
printf("IPv6 Packet: %s -> %s, Next Header: %d\n", printf("IPv6 Packet: %s -> %s, Next Header: %d\n",
source_ip_str, dest_ip_str, ip6_header->ip6_nxt); source_ip_str, dest_ip_str, ip6_header->ip6_nxt);
} }
} } else {
#endif
else {
printf("Non-IP Packet\n"); printf("Non-IP Packet\n");
continue; continue;
} }

View File

@@ -18,12 +18,18 @@ Specify the capture interface (e.g., eth0).
.B \-f \fIfilter\fR .B \-f \fIfilter\fR
Specify the capture filter in BPF syntax (e.g., tcp port 80). Specify the capture filter in BPF syntax (e.g., tcp port 80).
.TP .TP
.B \-r \fIip_address\fR .B \-r \fIhost/ipv4/ipv6\fR
Specify the destination IP address (required). Specify the destination host (required).
.TP .TP
.B \-p \fIport\fR .B \-p \fIport\fR
Specify the destination port (default: 37008). Specify the destination port (default: 37008).
.TP .TP
.B \-4
Force IPv4 host lookup.
.TP
.B \-6
Force IPv6 host lookup.
.TP
.B \-v .B \-v
Enable verbose mode (prints packet information). Enable verbose mode (prints packet information).
.TP .TP