pf with divert is not working properly. FreeBSD 9.1

Sergey Smitienko hunter at comsys.com.ua
Wed Jan 16 19:51:51 UTC 2013


Hello.
I think I found a bug in divert sockets processing in pf, or maybe I'm
missing something.
Here is my setup, machine 192.168.250.103 is a DNS server and udp
traffic coming to port 53
gets diverted to a test application. Test application is very simple, it
just prints some info on the
packet and reinjects it back to the kernel. Then divertion is done by
IPFW, all works as expected.
IPFW rule is "divert 1025 udp from any to 192.168.250.103 dst-port 53".
Then I divert packets
using pf  rule "pass in log quick on em0 inet proto udp from any to
192.168.250.103 port 53
divert-to 127.0.0.1 port 1025", I'm starting to get a loop of the same
packet comming back from
divert socket again and again. If I change my sento() call to n =
sendto(fd, packet, n, 0, (struct sockaddr*) &org, sizeof(org));,
packet riches DNS server, but then I'm getting DNS reply in my divert
socket and reply is getting looped
all over again.

I've also tried sample code from OpenBSD divert man page and I'm getting
same loop once again.
Here is my test code:

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <arpa/inet.h>

#include <errno.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void run (int port)
{

  int fd;
  struct sockaddr_in sin;
  struct sockaddr_in org;
  int   len, n, on=1;
  struct ip* ip;
  struct udphdr* udp;
  char *packet;

  packet = malloc(65536);

  if (packet == NULL) {
     warn ("malloc()");
     exit(1);
  }
  ip = (struct ip*) packet;

  fd = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT);
  if (fd < 0) {
      warn ("socket(divert)");
      exit(1);
  }

  sin.sin_len = sizeof(struct sockaddr_in);
  sin.sin_family = AF_INET;
  sin.sin_port=htons(port);
  sin.sin_addr.s_addr=inet_addr("127.0.0.1");
  len = sizeof(struct sockaddr_in);

  if (bind(fd, (struct sockaddr *)&sin, len)<0)  {
         warn("binding");
         exit(1);
  }
 
  while (1) {
    len = sizeof(struct sockaddr_in);
   
    if (getsockname(fd, (struct sockaddr*) &org, &len) < 0) {
        warn("getsockname");
        continue; 
    }
    memset(packet, 0, 65536);
    memset(&sin, 0, sizeof(sin));
    len = sizeof(sin);
    n = recvfrom(fd, packet, 65536, 0, (struct sockaddr*) &sin, &len);
    if (n < 0) {
         warn("recvfrom");
         continue;
    }
    if (n < sizeof (struct ip)) continue;

    printf ("Got %d bytes from %s:%d | ", n, inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port));
    printf ("%s:%d\n", inet_ntoa(org.sin_addr), ntohs(org.sin_port));
    printf ("%s -> ", inet_ntoa(ip->ip_src));
    printf ("%s ", inet_ntoa(ip->ip_dst));
    printf ("TTL %d, PROTO %d, hlen %d, CSUM %x\n", ip->ip_ttl,
ip->ip_p, ip->ip_hl, ip->ip_sum);
   
    udp = (struct udphdr*) (packet + ip->ip_hl*4);
    printf ("UDP src_port %d, dst_port %d\n", ntohs(udp->uh_sport),
ntohs(udp->uh_dport));
  
    n = sendto(fd, packet, n, 0, (struct sockaddr*) &sin, sizeof(sin));
    if (n < 0 ) {
        warn("sendto");
    }
  }
}

int main(void)
{
  run (1025);
} 

-- 
Sergey Smitienko



More information about the freebsd-net mailing list