How to use pf's divert-to in FreeBSD9

Hi

FreeBSD9's pf support divert-to option.I just can't figure out how to let it work.

Code:
pass in log on em1 inet proto tcp from any to 192.168.1.1 flags S/SA keep state divert-to 8080

Without divert-to 8080, packet will go out through em0.So divert-to have some effect.

The problem is when I listen to 127.0.0.1:8080 I can not get any packet.I tried
Code:
nc -l 8080
and a divert python script
Code:
import socket
import select
import re

IPPROTO_DIVERT = 258

sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, IPPROTO_DIVERT)
fd = sock.bind(('127.0.0.1', 8080))
sock.setblocking(True)

MSGLEN = 32768

while(1):
 msg = ''
 while len(msg) < MSGLEN:
  chunk = sock.recv(MSGLEN-len(msg))
  if chunk == '':
   raise RuntimeError, "Socket gone"
  msg = msg + chunk

  t = re.compile(r"(?P(.*)(GET|POST)(.*)(HTTP/\d+\.\d+)(.*)(Host: )([a-zA-Z\.0-9-]*)(.*))", re.DOTALL)
  m = t.match(msg)
  if m:
   print m.group(3) + " " + m.group(4) + " " + m.group(5) + " " + m.group(8)

Nothing works.
Does someone make it work?
 
Don't you need 127.0.0.1 after that statement? pf.conf(5) does not appear to treat either <host> or <port> as optional. And don't you need to define a destination port on the pass rule as well? I'd expect something like:

Code:
pass in log on em1 inet proto tcp from any to 192.168.1.1 port 80 flags S/SA keep state divert-to 127.0.0.1 port 8080
 
DutchDaemon said:
Don't you need 127.0.0.1 after that statement? pf.conf(5) does not appear to treat either <host> or <port> as optional. And don't you need to define a destination port on the pass rule as well? I'd expect something like:

Code:
pass in log on em1 inet proto tcp from any to 192.168.1.1 port 80 flags S/SA keep state divert-to 127.0.0.1 port 8080

I added 127.0.0.1 but pfctl -sr show the rule without it.
I will try to add the destination port.
 
chunlinyao said:
I will try to add the destination port.

Add destination port to pass rule not change the result.
divert-to require ipdivert.ko, if ipdivert.ko not loaded, pf will ignore the divert-to option.That python script can capture packet from ipfw tee ....with ipfw divert I can get SYN packet.
I think pf called ip_divert_ptrin pf.c.But where the packets goes?
 
After some research I found pf and ipfw all called ip_divert.c:divert_packet function.
I use ddb to set breakpoint in divert_packet function.Both ipfw and pf divert rule step in that function.But when use pf it seems can't found a matched sa.
I am not very familiar with ddb and assembler language.Still try to find why it not work.
 
chunlinyao said:
After some research I found pf and ipfw all called ip_divert.c:divert_packet function.
I use ddb to set breakpoint in divert_packet function.Both ipfw and pf divert rule step in that function.But when use pf it seems can't found a matched sa.
I am not very familiar with ddb and assembler language.Still try to find why it not work.

After use kgdb I found the bug. This is my first time to debug a kernel:p

The ipfw and pf have difference at this line
/usr/src/sys/netinet/ip_divert.c:282
Code:
nport = htons((u_int16_t)(((struct ipfw_rule_ref *)(mtag+1))->info));
when use ipfw nport = 36895 but when use pf nport = 8080
It seems sys/contrib/pf/net/pf.c:6970 should change to this
Code:
((struct ipfw_rule_ref *)(ipfwtag+1))->info = ntohs(r->divert.port);
It lacked a ntohs.

Code:
--- pf.c.orig	2011-08-25 12:08:12.000000000 +0800
+++ pf.c	2011-08-25 12:08:41.000000000 +0800
@@ -6967,7 +6967,7 @@
 		ipfwtag = m_tag_alloc(MTAG_IPFW_RULE, 0,
 				sizeof(struct ipfw_rule_ref), M_NOWAIT | M_ZERO);
 		if (ipfwtag != NULL) {
-			((struct ipfw_rule_ref *)(ipfwtag+1))->info = r->divert.port;
+			((struct ipfw_rule_ref *)(ipfwtag+1))->info = ntohs(r->divert.port);
 			((struct ipfw_rule_ref *)(ipfwtag+1))->rulenum = dir;
 
 			m_tag_prepend(m, ipfwtag);
There's a trick, change my pf rule to work around this
Code:
pass on em0 from any to 192.168.1.1 port 80 divert-to localhost port 36895
This rule will divert packets to port 8080
 
Back
Top