Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Jan 2013 21:13:09 +0200
From:      Sergey Smitienko <hunter@comsys.com.ua>
To:        freebsd-net@FreeBSD.org
Subject:   pf with divert is not working properly. FreeBSD 9.1
Message-ID:  <50F6FBC5.8060300@comsys.com.ua>

next in thread | raw e-mail | index | archive | help
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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?50F6FBC5.8060300>