Mikolaj, Before I get into this, do you really have a connection where your total bandwidth in both directions is pooled? If so you will need to modify my approach somewhat, as I've not been in that situation myself.
For reference, my full rule set for my home network appears at the end of this message. PF only queues on outbound traffic, so to shape your traffic in both directions you must be operating PF in a router or bridge configuration. People sometimes make the mistake of thinking that this means that PF cannot queue download traffic. As I said earlier, PF cannot queue inbound traffic on an interface. There is a difference between queuing inbound traffic and queuing download, because inbound traffic relates to an interface while download is applied as a perspective dependent concept. With the correct configuration, you can configure your external interface to queue packets for "upload" and your internal interface to queue packets for "download". I use quotes to indicate that these terms are relative to the perspective of a machine behind your router on your internal network. In short, the problem with keeping state across interfaces (PF's default) is that it makes it impractical, if not impossible, to have packets in different queues on both your internal and external network interfaces. To fix this, you need to configure PF to keep state on a per interface basis. This is done with a declaration in PF of "set state-policy if-bound". Once that is done, a little extra work needs to be done with your pass rules. 1. You will have to determine a way to identify the priority of new packets that you wish to pass in from your internal network, such as by IP, IP range, VLAN, or real interface. On each of those "pass in" rules you will assign the packet to the corresponding queue on the external interface and tag the packet with an identifier for the type of queuing it needs. 2. On your external interface you will need "pass out" rules that sort the traffic according to how they were tagged by the internal interface "pass in" rule. The "pass out" rule will assign the packet to the corresponding queue on the internal interface. 3. The mechanism of keeping state will take care of the rest. 4. If you plan to have any open ports on your external interface (ssh, http, bittorrent), you will need to repeat the above using the external interface in step 1, and the internal interface in step 2. 5. You will also need at least one rule to allow packets to pass out from each interface on the router/bridge machine itself. You can queue these specifically or let them go to the default queue. The examples of this in my rule set below should be evident if you take the time to understand it. You might also want to read a question I asked a few days ago on this list. It will help you to understand a strange limitation I've encountered with this type of configuration. See http://marc.info/?l=openbsd-misc&m=135325644931124&w=2 if you are interested. No one has responded to that message, so I am not sure if the defect exists in my rule set or in PF itself. My intuition tells me that PF is the problem, but my experience tells me that my intuition cannot be trusted in these matters. :) I hope this is what you were looking for. Breen Ouellette ------------------- # PF optimized for home router. # UPDATED: 2012-Nov-17 ########### # Preamble: #### # REMEMBER: Enable the following line in /etc/sysctl.conf: # net.inet.ip.forwarding=1 #### # <Blocked1Hr> table requires matching crontab entry to expire blocked IPs: # * * * * * pfctl -t Blocked1Hr -T expire 3600 > /dev/null 2>&1 #### # Filtering of ssh abusers also requires that sshd_config is updated to listen on ports 10 and 16. ExtIf = "em1" ExtIP = "(em1)" IntIf = "em0" VLAN1If = "vlan1" VLAN1Net = "vlan1:network" VLAN2If = "vlan2" VLAN2Net = "vlan2:network" AthenaIP = "172.16.0.1" ScreenerIP = "172.16.0.2" LGO2XIP = "192.168.0.100" SkypeIP = "192.168.0.50" table <authpf_users> persist table <Blocked1Hr> persist table <SSHBlockedOnce> persist table <SSHBlockedTwice> persist table <SSHBlockedPerm> persist table <CdnNets> const persist file "/etc/cdn_nets.pftable" table <MartianNets> const persist file "/etc/martian_nets.pftable" table <SSHAllowedIPs> const persist file "/etc/ssh_ips.pftable" # Internal network queuing. altq on $IntIf cbq bandwidth 5250Kb queue \ { LGO2X_DL, PC_DL, Skype_DL, TV_DL, WiFi_DL } queue LGO2X_DL bandwidth 650Kb priority 2 cbq(borrow ecn) queue PC_DL bandwidth 1500Kb priority 1 cbq(borrow ecn default) queue Skype_DL bandwidth 100Kb priority 2 cbq(ecn) queue TV_DL bandwidth 2000Kb priority 1 cbq(borrow ecn) queue WiFi_DL bandwidth 1000Kb priority 1 cbq(borrow ecn) # ISP network queuing. altq on $ExtIf cbq bandwidth 550Kb queue \ { ACK_UL, LGO2X_UL, PC_UL, Skype_UL, TV_UL, WiFi_UL } # 27400bps ACK_UL is required for each 1Mbps of total download bandwitdh, # assuming a 40bit ACK packet size. Real world results may vary and values # used here were derived through experimentation. # Too much ACK bandwidth is better than not enough, given borrow # relationships. queue ACK_UL bandwidth 176Kb priority 7 cbq(ecn) queue LGO2X_UL bandwidth 64Kb priority 1 cbq(borrow ecn) queue PC_UL bandwidth 82Kb priority 0 cbq(borrow ecn default) queue Skype_UL bandwidth 100Kb priority 1 cbq(ecn) queue TV_UL bandwidth 64Kb priority 0 cbq(borrow ecn) queue WiFi_UL bandwidth 64Kb priority 0 cbq(borrow ecn) set skip on lo set state-policy if-bound block block quick inet6 block in quick from <SSHBlockedPerm> block out quick to <SSHBlockedPerm> block in quick on $ExtIf from <MartianNets> block out quick on $ExtIf to <MartianNets> block in quick on $ExtIf from <Blocked1Hr> block out quick on $ExtIf to <Blocked1Hr> block in quick proto tcp from <SSHBlockedOnce> to any port ssh block in quick proto tcp from <SSHBlockedTwice> to any port 16 match out on $ExtIf inet to ! <MartianNets> \ nat-to $ExtIP port 1024:65535 received-on $VLAN1If match out on $ExtIf inet to ! <MartianNets> \ nat-to $ExtIP port 1024:65535 received-on $VLAN2If pass in quick on $VLAN1If inet proto udp \ from any port bootpc to { 255.255.255.255/32, $VLAN1If } port bootps pass in quick on $VLAN1If inet proto udp \ from $AthenaIP to $VLAN1If port ntp pass in quick on $VLAN1If inet proto tcp \ from $VLAN1Net to $VLAN1If port ssh keep state \ (max-src-conn 16, max-src-conn-rate 8/300, overload <SSHBlockedPerm>) \ queue(PC_DL) \ tag SSH_INTERNAL pass in quick on $VLAN1If inet \ from $ScreenerIP to ! <MartianNets> \ queue(TV_DL) \ tag TV_PACKET pass in quick on $VLAN1If inet \ from $VLAN1Net to ! <MartianNets> \ queue(PC_DL) \ tag PC_PACKET pass out quick on $VLAN1If inet \ queue(TV_DL) \ tagged TV_PACKET pass out quick on $VLAN2If inet \ queue(PC_DL) \ tagged SSH_INTERNAL pass out quick on $VLAN1If inet \ from $VLAN1If \ queue(PC_DL) pass in quick on $VLAN2If inet proto udp \ from any port bootpc to { 255.255.255.255/32, $IntIf } port bootps pass in quick on $VLAN2If inet proto tcp \ from $VLAN2Net to $VLAN2If port ssh keep state \ (max-src-conn 16, max-src-conn-rate 8/300, overload <SSHBlockedPerm>) \ queue(WiFi_DL) \ tag SSH_INTERNAL pass in quick on $VLAN2If inet \ from $LGO2XIP to ! <MartianNets> \ queue(LGO2X_DL) \ tag LGO2X_PACKET pass in quick on $VLAN2If inet \ from $SkypeIP to ! <MartianNets> \ queue(Skype_DL) \ tag SKYPE_PACKET pass in quick on $VLAN2If inet \ from <authpf_users> to ! <MartianNets> \ queue(WiFi_DL) \ tag WIFI_PACKET pass out quick on $VLAN2If inet \ queue(WiFi_DL) \ tagged SSH_INTERNAL pass out quick on $VLAN2If inet \ from $VLAN2If \ queue(WiFi_DL) pass in quick on $ExtIf inet proto tcp \ from ! <MartianNets> to $ExtIP port 65152 rdr-to $ScreenerIP keep state \ (max-src-conn-rate 4/60, tcp.established 60, overload <Blocked1Hr>) \ queue(TV_UL, ACK_UL) \ tag TV_PACKET pass in quick on $ExtIf inet proto tcp \ from <CdnNets> to $ExtIP port ssh keep state \ (max-src-conn 16, max-src-conn-rate 8/300, overload <SSHBlockedOnce>) \ queue(PC_UL, ACK_UL) pass in quick on $ExtIf inet proto tcp \ from <SSHIPs> to $ExtIP port ssh keep state \ (max-src-conn 16, max-src-conn-rate 8/300, overload <SSHBlockedOnce>) \ queue(PC_UL, ACK_UL) pass in quick on $ExtIf inet proto tcp \ from <SSHBlockedOnce> to $ExtIP port 16 keep state \ (max-src-conn 12, max-src-conn-rate 6/900, overload <SSHBlockedTwice>) \ queue(PC_UL, ACK_UL) pass in quick on $ExtIf inet proto tcp \ from <SSHBlockedTwice> to $ExtIP port 10 keep state \ (max-src-conn 8, max-src-conn-rate 4/3600, overload <SSHBlockedPerm>) \ queue(PC_UL, ACK_UL) pass out quick on $ExtIf inet \ queue(TV_UL, ACK_UL) \ tagged TV_PACKET pass out quick on $ExtIf inet \ queue(PC_UL, ACK_UL) \ tagged PC_PACKET pass out quick on $ExtIf inet \ queue(LGO2X_UL, ACK_UL) \ tagged LGO2X_PACKET pass out quick on $ExtIf inet \ queue(Skype_UL, ACK_UL) \ tagged SKYPE_PACKET pass out quick on $ExtIf inet \ queue(WiFi_UL, ACK_UL) \ tagged WIFI_PACKET pass out quick on $ExtIf inet \ from $ExtIP \ queue(PC_UL, ACK_UL) # EOF: /etc/pf.conf

