Here is the situation: in my scenario I have, 3 computers, A, B and C.
Computer A sends data to computer B. Computer B captures these packets with pcap, appends the headers, redoes the checksums, and injects it out another ethernet interface to computer C. So basically A sends to C, though through C's point of view, the data is coming from computer B.
My problem is this: following TCPDUMP's tutorial on dissecting a captured packet, I've learned to calculate offsets and using typecasting to obtain ethernet, ip, and tcp header structures. The method of doing so is shown below:
ethernet = (struct sniff_ethernet*)(packet);
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
if (size_ip < 20) {
printf(" * Invalid IP header length: %u bytes\n", size_ip);
return;
}
tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
size_tcp = TH_OFF(tcp)*4;
if (size_tcp < 20) {
printf(" * Invalid TCP header length: %u bytes\n", size_tcp);
return;
}
payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
Because I want to inject the captured packet to send it from computer B to computer C, I must modify some of the source/destination information and recalculate the checksum when I'm done. However, my issue is, since this data is now separated into structures of ethernet header, IP header, and TCP headers, how do I put it back together into a u_char
that pcap_inject
can use?
Is it possible to do some sort of concatenation here?
From the code I see here, you're not actually dissecting the memory that libpcap
captured for you. Each of the casting operations simply tells the compiler how you intend to treat the bytes starting from a pointer -- what size those objects are, what offsets to find which pieces of data and how long they are.
If you modify this memory through those pointers, you've modified the one and only copy of it in the process memory -- and can use some of the "more basic" pointers to hand the entire block of memory to sendmsg(2)
or whatever without needing to reassemble the data -- you never took it apart.
Update
To inject the packets back on the network you need to use the raw(7)
socket type; the IPPROTO_RAW
socket option is required to send TCP packets through a raw(7)
socket -- otherwise, all TCP packets would be directed to the raw(7)
socket you open, making networking on the machine difficult to use.
The raw(7)
sockets will perform some re-calculation tasks for you:
A protocol of IPPROTO_RAW implies enabled IP_HDRINCL and is
able to send any IP protocol that is specified in the passed
header. Receiving of all IP protocols via IPPROTO_RAW is not
possible using raw sockets.
┌───────────────────────────────────────────────────┐
│IP Header fields modified on sending by IP_HDRINCL │
├──────────────────────┬────────────────────────────┤
│IP Checksum │Always filled in. │
├──────────────────────┼────────────────────────────┤
│Source Address │Filled in when zero. │
├──────────────────────┼────────────────────────────┤
│Packet Id │Filled in when zero. │
├──────────────────────┼────────────────────────────┤
│Total Length │Always filled in. │
└──────────────────────+────────────────────────────┘
If IP_HDRINCL is specified and the IP header has a nonzero
destination address then the destination address of the
socket is used to route the packet. When MSG_DONTROUTE is
specified, the destination address should refer to a local
interface, otherwise a routing table lookup is done anyway
but gatewayed routes are ignored.
If IP_HDRINCL isn't set, then IP header options can be set on
raw sockets with setsockopt(2); see ip(7) for more
information.
Let the kernel re-calculate whatever it is willing to do for you.