Preprocess packets send to/from interface

Is it somehow possible to intercept packet data just before it reaches an interface, and just after it is received? E.g. preprocess it and send it back on its way?

I wanted to create a custom solution like this great program called vtun. It creates a virtual device that acts as an interface. This enables it to do all sorts of things like traffic compression, encryption, shaping etc.

I was wondering if it can be done easier now (vtun is quite old…). My target OS is Ubuntu 14.04.

Asked By: er453r

||

Yes, the Netfilter framework of the Linux kernel is flexible enough to make this possible.

I am not sure what are your expectations by saying “custom solution” and “if it can be done easier now”. I assume you are prepared to write code in order to do low-level packet processing.

The general idea is the following:

  1. Create iptables rules which will pass the traffic from the desired table (filter, nat, mangle) to the user-space, through the QUEUE target.
  2. You will be able to access the packets you sent to the queue by using the libnetfilter_queue library or nfqueue-bindings (if you’re working with Perl or Python).
  3. Process the packets the way you see fit and send them back on their way.

Keep in mind that you will be working with raw IP packets, TCP segments or UDP datagrams (depending on the type of traffic you want to process) and it will be your responsibility to correctly re-assemble the traffic, maintain checksum correctness on packet level and everything else that your operating system’s TCP/IP stack magically takes care of behind the scenes.

If you’re planning to work in Python, I would suggest that you use dpkt or scapy to work with packets or TCP segments. It will make things much more easier.

Answered By: dkaragasidis

Here is a sample code using @dkaragasidis suggestions. It reads the packet data, and passes it along. You can modify the packet in the handler function.

Compile with: -lnetfilter_queue -lnfnetlink

Add rule with (example): sudo iptables -A OUTPUT -p udp --dport 4444 -j NFQUEUE --queue-num 0

Remove rule with (example): sudo iptables -D OUTPUT -p udp --dport 4444 -j NFQUEUE --queue-num 0

Test with: nc -lu 4444 and nc -u YOUR_IP 4444

#include <netinet/in.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <stdio.h>

int handler(struct nfq_q_handle *myQueue, struct nfgenmsg *msg, struct nfq_data *pkt,   void *cbData) {
    int id = 0;
    struct nfqnl_msg_packet_hdr *header;

    if( header = nfq_get_msg_packet_hdr(pkt) )
        id = ntohl(header->packet_id);

    unsigned char *pktData;

    int len = nfq_get_payload(pkt, &pktData);

    printf("data[ %d ]:n", len);

    int i;
    for (i = 0; i < len; i++)
        printf("%2d 0x%02x %3d %cn", i, pktData[i], pktData[i], pktData[i]);

    printf("n");

    return nfq_set_verdict(myQueue, id, NF_ACCEPT, len, pktData);
}

int main(int argc, char **argv) {
    struct nfq_handle *nfqHandle;
    struct nfq_q_handle *myQueue;
    struct nfnl_handle *netlinkHandle;

    int fd, res;
    char buf[4096];

    // queue connection
    if (!(nfqHandle = nfq_open())) {
        perror("Error in nfq_open()");
        return(-1);
    }

    // bind this handler
    if (nfq_bind_pf(nfqHandle, AF_INET) < 0) {
        perror("Error in nfq_bind_pf()");
        return(1);
    }

    // define a handler
    if (!(myQueue = nfq_create_queue(nfqHandle, 0, &handler, NULL))) {
        perror("Error in nfq_create_queue()");
        return(1);
    }

    // turn on packet copy mode
    if (nfq_set_mode(myQueue, NFQNL_COPY_PACKET, 0xffff) < 0) {
        perror("Could not set packet copy mode");
        return(1);
    }

    netlinkHandle = nfq_nfnlh(nfqHandle);
    fd = nfnl_fd(netlinkHandle);

    while ((res = recv(fd, buf, sizeof(buf), 0)) && res >= 0)
        nfq_handle_packet(nfqHandle, buf, res);

    nfq_destroy_queue(myQueue);
    nfq_close(nfqHandle);

    return 0;
}
Answered By: er453r