# How to use pf's divert-to in FreeBSD9



## chunlinyao (Aug 19, 2011)

Hi

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


```
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 
	
	



```
nc -l 8080
```
 and a divert python script

```
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?


----------



## copypaiste (Aug 19, 2011)

Did you check pflog?


----------



## DutchDaemon (Aug 19, 2011)

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:


```
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
```


----------



## chunlinyao (Aug 20, 2011)

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:
> 
> 
> ```
> ...



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


----------



## chunlinyao (Aug 20, 2011)

copypaiste said:
			
		

> Did you check pflog?


Yes, the tcpdump -vvvnepi pflog0 show a SYN packet passed by this rule.


----------



## chunlinyao (Aug 20, 2011)

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?


----------



## chunlinyao (Aug 21, 2011)

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 (Aug 25, 2011)

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

The ipfw and pf have difference at this line
/usr/src/sys/netinet/ip_divert.c:282

```
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

```
((struct ipfw_rule_ref *)(ipfwtag+1))->info = ntohs(r->divert.port);
```
It lacked a ntohs.


```
--- 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

```
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


----------

