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 

Reply via email to