diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index aefdea6..431b292 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -98,7 +98,6 @@ build-rocky9: publish-rocky9: stage: publish - needs: [] needs: - build-rocky9 dependencies: @@ -149,3 +148,40 @@ publish-rocky8: - ls -la build - '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/pcapmirror/rockylinux8/pcapmirror-0.3-1.el8.x86_64.rpm' + +build-pios12: + stage: build + needs: [] + only: + - tags + tags: + - pios12 + script: + - dnf install -y libpcap-devel + - mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} + - tar -czf /root/rpmbuild/SOURCES/pcapmirror-v0.3.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-pios12: + stage: publish + needs: + - build-rpios12 + 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_arm64.deb"' + - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/pcapmirror_0.3-1_arm64.deb ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/pcapmirror/bookworm/pcapmirror_0.3-1_arm64.deb' diff --git a/README.md b/README.md index 84d241b..085963b 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,14 @@ pcapmirror [options] ### Options: -* -i : Specify the capture interface (e.g., eth0). -* -f : Specify the capture filter in BPF syntax (e.g., tcp port 80). -* -r : Specify the destination IP address (required). -* -p : Specify the destination port (default: 37008). -* -v: Enable verbose mode (prints packet information). -* -h: Show this help message. +* -i Specify the capture interface +* -f Specify the capture filter (BPF syntax) +* -r Specify the destination host (required) +* -p Specify the destination port (default: 37008) +* -4 Force IPv4 host lookup +* -6 Force IPv6 host lookup +* -v Enable verbose mode +* -h Show this help message ### Example: diff --git a/main.c b/main.c index 99abc67..e9e3366 100644 --- a/main.c +++ b/main.c @@ -13,13 +13,9 @@ Copyright (c) 2025, Matthias Cramer, cramer@freestone.net #include #include #include +#include #include - -#define ENABLE_IPV6 - -#ifdef ENABLE_IPV6 -#include // Include for IPv6 header definition -#endif +#include #define DEFAULT_DEST_PORT 37008 // Default TZSP port #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) { printf("Usage: %s [options]\n", program_name); printf("Options:\n"); - printf(" -i Specify the capture interface\n"); - printf(" -f Specify the capture filter (BPF syntax)\n"); - printf(" -r Specify the destination IP address (required)\n"); - printf(" -p Specify the destination port (default: %d)\n", DEFAULT_DEST_PORT); - printf(" -v Enable verbose mode\n"); - printf(" -h Show this help message\n"); + printf(" -i Specify the capture interface\n"); + printf(" -f Specify the capture filter (BPF syntax)\n"); + printf(" -r Specify the destination host (required)\n"); + printf(" -p Specify the destination port (default: %d)\n", DEFAULT_DEST_PORT); + printf(" -4 Force IPv4 host lookup\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(" %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 *filter_exp = "tcp port 8088"; // Default filter 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 i; 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 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 if (argc == 1 || (argc == 2 && strcmp(argv[1], "-h") == 0)) { @@ -78,18 +79,6 @@ int main(int argc, char *argv[]) { 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 for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-f") == 0 && i + 1 < argc) { @@ -104,27 +93,77 @@ int main(int argc, char *argv[]) { print_usage(argv[0]); return 0; } 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 } else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) { dest_port = atoi(argv[i + 1]); // Set destination port from command line 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 - if (dest_ip == NULL) { + if (mirror_host == NULL) { fprintf(stderr, "Error: Destination IP address is required.\n"); print_usage(argv[0]); return 1; } - if (inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr) <= 0) { - perror("inet_pton"); + // Resolve the destination address + 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; } - 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 (dev_name == NULL) { @@ -195,17 +234,10 @@ int main(int argc, char *argv[]) { struct pcap_pkthdr header; const u_char *packet; char source_ip_str[INET6_ADDRSTRLEN], dest_ip_str[INET6_ADDRSTRLEN]; - struct ip *ip_header; -#ifdef ENABLE_IPV6 - struct ip6_hdr *ip6_header; -#endif + struct ip *ip_header; // Declare ip4_header + struct ip6_hdr *ip6_header; // Declare ip6_header 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) { packet = pcap_next(handle, &header); if (packet == NULL) @@ -225,9 +257,7 @@ int main(int argc, char *argv[]) { printf("IPv4 Packet: %s -> %s, IP Protocol: %d\n", source_ip_str, dest_ip_str, ip_header->ip_p); } - } -#ifdef ENABLE_IPV6 - else if (ip_protocol == 6) { + } 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); @@ -237,9 +267,7 @@ int main(int argc, char *argv[]) { printf("IPv6 Packet: %s -> %s, Next Header: %d\n", source_ip_str, dest_ip_str, ip6_header->ip6_nxt); } - } -#endif - else { + } else { printf("Non-IP Packet\n"); continue; } diff --git a/pcapmirror.8 b/pcapmirror.8 index a81c580..0fff879 100644 --- a/pcapmirror.8 +++ b/pcapmirror.8 @@ -18,12 +18,18 @@ Specify the capture interface (e.g., eth0). .B \-f \fIfilter\fR Specify the capture filter in BPF syntax (e.g., tcp port 80). .TP -.B \-r \fIip_address\fR -Specify the destination IP address (required). +.B \-r \fIhost/ipv4/ipv6\fR +Specify the destination host (required). .TP .B \-p \fIport\fR Specify the destination port (default: 37008). .TP +.B \-4 +Force IPv4 host lookup. +.TP +.B \-6 +Force IPv6 host lookup. +.TP .B \-v Enable verbose mode (prints packet information). .TP