基于Raw Socket的C语言TCPDump捕获数据包克隆实现需求
Got it, let's walk through exactly how to clone those TCPDump-captured packets using raw sockets in C—we'll be directly manipulating the IP and TCP headers via the iph-> and tcph-> pointers like you mentioned.
Core Overview
Raw sockets let us bypass the OS's normal network stack and craft custom IP/TCP packets from scratch. Using iph-> (pointer to IP header) and tcph-> (pointer to TCP header) lets us directly set fields that match the packets you captured with TCPDump, like source/dest IPs, ports, acknowledgment numbers, window sizes, and TCP options.
Step-by-Step Implementation
1. Open a Raw Socket
First, we need a raw socket that lets us handle TCP packets. We'll also set the IP_HDRINCL option so we can construct our own IP header (instead of letting the OS do it).
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <arpa/inet.h> int main() { // Create raw socket for TCP int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); if (sockfd < 0) { perror("Failed to create raw socket"); exit(EXIT_FAILURE); } // Set option to include our own IP header int optval = 1; if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval)) < 0) { perror("Failed to set IP_HDRINCL option"); close(sockfd); exit(EXIT_FAILURE); }
2. Define Buffer & Header Pointers
We'll use a buffer to hold our entire packet (IP header + TCP header + options). Then we'll cast parts of the buffer to struct iphdr* and struct tcphdr* to manipulate the headers.
// Buffer to hold our crafted packet char buffer[4096]; memset(buffer, 0, sizeof(buffer)); // Pointer to IP header struct iphdr *iph = (struct iphdr *)buffer; // Pointer to TCP header (starts right after IP header) struct tcphdr *tcph = (struct tcphdr *)(buffer + sizeof(struct iphdr));
3. Populate IP Header (Match TCPDump Values)
Fill in the IP header fields to match your captured packets. For your examples, the source IP is SRCIP, dest is DSTIP, protocol is TCP, etc.
// Fill IP header iph->ihl = 5; // IP header length (5 32-bit words = 20 bytes) iph->version = 4; // IPv4 iph->tos = 0; // Type of Service iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr) + 12; // 20 + 20 + 12 (TCP options) iph->id = htons(12345); // Packet ID (can be arbitrary) iph->frag_off = 0; // No fragmentation iph->ttl = 64; // Time to live iph->protocol = IPPROTO_TCP;// Protocol is TCP iph->check = 0; // We'll calculate this later iph->saddr = inet_addr("SRCIP"); // Source IP from TCPDump iph->daddr = inet_addr("DSTIP"); // Destination IP from TCPDump
4. Populate TCP Header & Options (Match TCPDump Values)
This is where we map the TCPDump output directly to tcph-> fields. Your examples are TCP ACK packets with timestamp options, so we'll set those details:
// Fill TCP header (using first example packet values) tcph->source = htons(22409); // Source port from TCPDump tcph->dest = htons(80); // Dest port from TCPDump tcph->seq = htonl(0); // For ACK packets, seq is the previous ack value (adjust as needed) tcph->ack_seq = htonl(2897); // ACK number from TCPDump tcph->doff = 8; // TCP header length (8 32-bit words = 32 bytes: 20 base + 12 options) tcph->th_flags = TH_ACK; // Flags [.] = ACK only tcph->window = htons(274); // Window size from TCPDump tcph->check = 0; // We'll calculate this later tcph->urg_ptr = 0; // No urgent pointer // Add TCP options: NOP, NOP, Timestamp (matches your TCPDump options) unsigned char *opt_ptr = (unsigned char *)(tcph + 1); *opt_ptr++ = TCPOPT_NOP; // First NOP *opt_ptr++ = TCPOPT_NOP; // Second NOP *opt_ptr++ = TCPOPT_TIMESTAMP; // Timestamp option code *opt_ptr++ = TCPOLEN_TIMESTAMP; // Option length (10 bytes) *(uint32_t *)opt_ptr = htonl(20513654); // TS val from TCPDump opt_ptr += 4; *(uint32_t *)opt_ptr = htonl(3515597033); // TS ecr from TCPDump
5. Calculate Checksum
Both IP and TCP require valid checksums. We'll use a helper function to compute them:
// Helper function to calculate checksum unsigned short csum(unsigned short *buf, int nwords) { unsigned long sum = 0; for (; nwords > 0; nwords--) sum += *buf++; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (unsigned short)(~sum); } // Calculate IP header checksum iph->check = csum((unsigned short *)buffer, iph->ihl * 2); // Calculate TCP checksum (requires pseudo-header) struct pseudo_header { uint32_t source_address; uint32_t dest_address; uint8_t placeholder; uint8_t protocol; uint16_t tcp_length; } psh; psh.source_address = inet_addr("SRCIP"); psh.dest_address = inet_addr("DSTIP"); psh.placeholder = 0; psh.protocol = IPPROTO_TCP; psh.tcp_length = htons(sizeof(struct tcphdr) + 12); // TCP header + options length int psize = sizeof(psh) + sizeof(struct tcphdr) + 12; char *pseudogram = malloc(psize); memcpy(pseudogram, &psh, sizeof(psh)); memcpy(pseudogram + sizeof(psh), tcph, sizeof(struct tcphdr) + 12); tcph->check = csum((unsigned short *)pseudogram, psize / 2); free(pseudogram);
6. Send the Cloned Packet
Finally, we'll send the packet to the destination IP:
// Set up destination address structure struct sockaddr_in dest_addr; memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = iph->daddr; // Send the packet if (sendto(sockfd, buffer, iph->tot_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) { perror("Failed to send packet"); close(sockfd); exit(EXIT_FAILURE); } close(sockfd); return 0; }
Adapting to Your Other Example Packets
To clone your second and third packets, just adjust the following fields:
tcph->ack_seq: Set tohtonl(4345)for the second packet,htonl(5793)for the thirdtcph->window: Set tohtons(296)(second) andhtons(319)(third)- If the third packet's TS val was truncated, fill in the full value from your capture
Critical Notes
- Run as Root: Raw sockets require privileged access, so you must run the program with
sudoor as the root user. - Header Compatibility: Some systems may use slightly different header structures (e.g.,
struct ipinstead ofstruct iphdr). Check your system's headers if you run into issues. - Checksum Accuracy: Incorrect checksums will cause the packet to be dropped by network devices or the target OS. Double-check your calculations.
- Libpcap Alternative: If you're reading directly from a TCPDump
.pcapfile instead of manually parsing text, use thelibpcaplibrary—it simplifies packet capture and parsing significantly.
内容的提问来源于stack exchange,提问作者Alemalakra Alemalakra

