Solved Can't make PF work with Dummynet on FreeBSD 14RC3 and RC4

Hi there, I am trying to setup a simple PF firewall with dummynet and
despite all my efforts I am failing. I would appreciate a helping
hand, thank you. Below is the setup description and all the details
that might be useful. I've been trying to apply pass rule for the pipe
on different interfaces, in and out, for global addresses, subnets and
local addresses and nothing seems to work. My client machine gets the
internet and everything is ok except for the fact that I can't use
dummynet with it. Please let me know if any other info needed. Thank
you

My setup is raspberry pi 3b with two interfaces ue0 (EXT) and ue1 (INT)
No bridge
ue1 spinning dhcpd and dnsmasq to control clients IP ranges and add some custom DNS resolution.
ue0 running NAT (PAT) for the ue1:network

Code:
 rc.conf

Skipped generic stuff
# IF
ifconfig_ue1="inet 192.168.31.1 netmask 255.255.255.0"
dhcpd_enable="YES"
dnsmasq_enable="YES"

# Firewall
gateway_enable="YES"
pf_enable="YES"
pflog_enable="YES"
dnctl_enable="YES"
dnctl_program="/sbin/dnctl"

Code:
pf.conf 

 # Macros and tables
ext_if = "ue0"
int_if = "ue1"
localnet = $int_if:network

# Options
set block-policy drop
set skip on lo0

# Normalization
scrub in all

# NAT (comment out if adding ext_if to bridge)
nat on $ext_if inet from ($localnet) to any -> ($ext_if)
#nat on $ext_if inet6 from ($localnet) to any -> ($ext_if:0)

# RDR anchors, mostly for port forwarding
#rdr-anchor "reggae/*" on $ext_if
#rdr-anchor "services/*" on $ext_if
# rdr-anchor "service/*" on $ext_if
antispoof quick log for ($ext_if) # comment out if adding ext_if to bridge
#anchor "blacklistd/*" in on $ext_if

# Quick rules
#block out quick inet6 all user torrent
pass in quick inet from 192.168.31.12 to any dnpipe 1

# Rules
block in log from any to (self)
pass in inet proto udp to any port bootpc
#pass in inet6 proto udp from fe80::/10 port dhcpv6-server to
fe80::/10 port dhcpv6-client
pass in proto tcp to any port ssh
pass in proto { icmp, igmp, icmp6 }
pass in on $int_if proto { tcp, udp } from any to (self)
pass out

Code:
dnctl.conf 
pipe 1 config bw 100Kbit

Code:
dnctl pipe show 
00001: 100.000 Kbit/s    0 ms burst 0
q131073  50 sl. 0 flows (1 buckets) sched 65537 weight 0 lmax 0 pri 0 droptail
 sched 65537 type FIFO flags 0x0 0 buckets 0 active

Code:
ifconfig 
lo0: flags=1008049<UP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP> metric 0 mtu 16384
        options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
        groups: lo
        nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
ue0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP>
metric 0 mtu 1500
        options=80009<RXCSUM,VLAN_MTU,LINKSTATE>
        ether b8:27:eb:ec:b9:ed
        inet 192.168.2.70 netmask 0xffffff00 broadcast 192.168.2.255
        media: Ethernet autoselect (100baseTX <full-duplex>)
        status: active
        nd6 options=2b<PERFORMNUD,ACCEPT_RTADV,IFDISABLED,AUTO_LINKLOCAL>
ue1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
 options=68009b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
        ether a0:ce:c8:5e:c7:1d
        inet 192.168.31.1 netmask 0xffffff00 broadcast 192.168.31.255
        media: Ethernet autoselect (none)
        status: no carrier
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

Code:
pf -s all 
FILTER RULES:
scrub in all fragment reassemble
block drop in log quick on ! ue0 from (ue0:network) to any
block drop in log quick from (ue0) to any
pass in quick inet from 192.168.31.12 to any flags S/SA keep state dnpipe 1
block drop in log from any to (self)
pass in on ue1 proto tcp from any to (self) flags S/SA keep state
pass in on ue1 proto udp from any to (self) keep state
pass in inet proto udp from any to any port = bootpc keep state
pass in proto tcp from any to any port = ssh flags S/SA keep state
pass in proto icmp all keep state
pass in proto igmp all keep state
pass in proto ipv6-icmp all keep state
pass out all flags S/SA keep state

Code:
scp from target 
scp root@192.168.31.12:/root/foo /root/foo
foo 0%   46MB   7.6MB/s   23:00 ETA
 
You don't have a match rule to send traffic to dummynet in your pf.conf

I have it working with this config:

dnctl.conf
Code:
## WAN
# downlink
pipe  1 config bw 475Mbits/s
sched 1 config pipe 1 type fq_codel
queue 1 config sched 1
# uplink
pipe  2 config bw 95Mbits/s
sched 2 config pipe 2 type fq_codel
queue 2 config sched 2

pf.conf
Code:
<snip>
match on $if_wan all dnqueue (2, 1)
</snip>

Since you're not setting up a schedule and just using a pipe in your config I think you can just used dnpipe instead of dnqueue in the match rule, but I have not tested this.
 
You don't have a match rule to send traffic to dummynet in your pf.conf

I have it working with this config:

dnctl.conf
Code:
## WAN
# downlink
pipe  1 config bw 475Mbits/s
sched 1 config pipe 1 type fq_codel
queue 1 config sched 1
# uplink
pipe  2 config bw 95Mbits/s
sched 2 config pipe 2 type fq_codel
queue 2 config sched 2

pf.conf
Code:
<snip>
match on $if_wan all dnqueue (2, 1)
</snip>

Since you're not setting up a schedule and just using a pipe in your config I think you can just used dnpipe instead of dnqueue in the match rule, but I have not tested this.
Hm, I add pass rule for dnpipe 1
Code:
pass in quick inet from 192.168.31.12 to any dnpipe 1

But let me try with match, maybe this is the way.

UPD: Huh, so it worked when I run it on all interfaces and I have no idea why. Also I have no idea why pass rule doesn't work at all even though I know it works this way in some setups. I assume there is something that with my setup, the packets go not as I expect them. So basically my rule is
Code:
match from 192.168.31.12 to any dnpipe 1

However if I add
Code:
match on $int_if from 192.168.31.12 to any dnpipe 1
it still doesn't work. Blows my mind but at least something works. Will go try to figure it out.

Thank you for the help, SJorge
 
I've also noticed it doesn't seem to work on a wireguard interface with a match, I've not had much luck with using it on a pass rule in general so far.

I noticed you're also using pass quick, I'm pretty sure I was also using pass quick so maybe that's causing issue. I've not messed with it since I got it working with a match rule, well working except on my wireguard interfaces that is.
 
For anyone that stumbles across this thread and is having trouble shaping with Wireguard or other services with dummynet and pf. I got it to finally work on my system by specifying "no state" for Wireguard traffic. The state was allowing it to bypass match and pass rules that would have filtered it into the dnqueue or dnpipe.

These are my configs for limiting outgoing Wireguard traffic to 300Mbit/s.

/etc/pf.conf
Code:
# Wireguard
pass in on re0 proto udp to port 51820 no state
pass out on re0 proto udp from port 51820 no state dnqueue 1

/etc/dnctl.conf
Code:
pipe 1 config bw 300Mbit
sched 1 config pipe 1 type fq_codel
queue 1 config sched 1
 
Back
Top