Rebuilding a packet to inject via pcap

2019-08-09 15:24发布

问题:

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?

回答1:

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.