Packet Inspection Using Divert Sockets

DiversionFor a long time ago, I did not write about OpenBSD which remains one of my favorite operating system. The last version (4.7) was released in May and introduced, as usual, a lot of interesting changes.

OpenBSD comes of course with it’s own firewall called pf (“packet filter“). Plenty of features are available but pf gained even more power since the last OpenBSD release with the introduction of the divert sockets support (for both IPv4 & IPv6). What’s this?

First, divert sockets are not new. They are available for a while on FreeBSD (also supporting the pf firewall) or Linux since kernel version 2.2). When you need to intercept packets passing through your UNIX server, you use the library libpcap (and the associated tools like tcpdump) or raw sockets. But this operation remains “read only” – I mean – you just grab a copy of the packets for further analyze. Did you ever dream about a cool way to process packets outside the core firewall process and, even better, re-injecting them in the traffic flow? That’s the purpose of the divert sockets.

First of all, some important remarks:

  • Packets sent to a divert socket will be processed in user space! This could have huge security impacts.
  • Processing packets and re-injecting them use lot of time (compared to the time required by the firewall to process them), this could lead to huge performance impacts.
  • Altering the payload of some TCP packets can be dangerous. No big issue if the payload size remains the same but if it changes, the TCP session between the hosts will be desynchronized! (issue related to the sequence number).

If you always keep in mind those remarks, let’s see how divert sockets work. Let’s imagine a firewall between a private network and the Internet (see figure below). We can instruct the firewall to divert all outgoing packets on port TCP/80 to a process running on user space.

Diver Socket
(Click to enlarge)

How to achieve this? At pf level, we configure the divers socket (via your pf.conf):

    pass out on $int from any to any port 80 divert $divert_port keep state

The process running in user space utilizes a special type of RAW socket called divert (IPPROTO_DIVERT) that allows you to receive and send like a regular socket. But here, the divert socket is bound to a port which is used by the firewall ($divert_port in the example above) to send packets. A basic example in C:

    printf("Creating a divert socket\n");
    fd=socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);
    if (fd==-1) {
       printf("Could not open a divert socket\n");
       exit(1);
    }
    bindPort.sin_family=AF_INET;
    bindPort.sin_port=8000;
    bindPort.sin_addr.s_addr=0;
    printf("Binding a socket\n");
    s=bind(fd, &bindPort, sizeof(struct sockaddr_in));

Once done, wait for incoming packets, process them and re-inject them (optional):

    sinlen=sizeof(struct sockaddr_in);
    printf("Waiting for some packets...\n");
    while(1) {
       n=recvfrom(fd, packet, BUFSIZE, 0, &sin, &sinlen);
       hdr=(struct iphdr*)packet;

       processPacket(packet);

       printf("Reinjecting packet\n");
       n=sendto(fd, packet, n ,0, &sin, sinlen);
    }

If you prefer to develop in Perl, there is a Net::Divert module available on the CPAN repository. Here is a little example:

    use Net::Divert;
    my $divsocket = Net::Divert->new('127.0.0.1',9999);
    $divsocket->getPackets(\&processPacket);

    sub processPacket {
       my ($packet,$tag) = @_;

       # ... Perform further processing ...

       # Re-inject the packet
       $divsocket->putPacket($packet,$tag);
    }

Those are simple example, I’m not a developer ;-). What can you do with divert sockets? There are plenty of implementations:

  • Data Leakage Prevention (DLP): Generate alerts when patterns are detected in the payload of inspected packets. In case of re-injection, hide sensitive date (SSN, CC, …) with “XXXXXX”.
  • Quality of Service
  • Statistics
  • IDS
  • URL filtering
  • DNS answer rewriting

Use your imagination! But, once again, take care of the security! Diverted packets will travel across different rings during the inspection!

Related information:

5 comments

  1. Thanks for your article.

    The only thing I didn’t understand is why port 8000 at bindPort.sin_port=8000?

    Thanks.

  2. Nice. I’ll be writing a tool to inject arbitrary data in IPv4/6 communication, it could then be used with expect to rapidly write some of the implementations you suggested — for fun and profit, see you on ports@.

  3. linux 2.4/2.6 still offer divert sockets?
    AFAIK, ipq followed divert sockets in 2.4, nfq followed ipq in 2.6.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.