sshd has no features that limit the bandwidth used by a given ssh connection, so scp or sftp transfers can consume as much bandwidth as the underlying connection can support. If this connection is shared, this might be undesirable – one transfer will always be trying to saturate the pipe, forcing other communications to contend for bandwidth. Conveniently, the OpenBSD ipfw firewall includes a traffic shaping filter named dummynet which can used to simulate a wide variety of network conditions, including artificially limiting the bandwidth for traffic matching a given ipfw rule. This can be leveraged to limit bandwidth for external ssh connections.

I ran into two issues when trying to get this working:

  1. Confusingly the ipfw manpage still suggests that the ipfw command can be used to configure dummynet. In reality, all it can now do is create a dummynet pipe; dnctl is used to configure the pipe once created.
  2. The dummynet kernel module is neither compiled into the kernel nor loaded by default.

The below script incorporates solutions to both the above challenges, defines the rules necessary to base-configure ipfw, and adds a 10Mbit/s bandwidth limit for outgoing ssh connections:

ipfw -q -f flush
# since we have dynamic rules later in the set (those with keep-state), adding this here reduces rule-scanning once a dynamic rule is created
ipfw -q add 00100 check-state
# must always allow unfettered comms on the loopback address
ipfw -q add 00001 allow all from any to any via lo0
# Load the dummynet kernel model
kldload dummynet
# Use dummynet to create a virtual pipe between any local address and any external address, applied only for outgoing traffic
ipfw -q add 00102 pipe 1 ip from me to any out
# Set the maximum bandwith for the pipe I have defined
dnctl pipe 1 config bw 10Mbit/s

Note that the above only limits bandwidth. If you have needs to limit access by IP (ranges), then further ipfw rules will be required.

With the above put into a file named, say, /usr/local/etc/ and set executable by root, the following two lines need to be added to /etc/rc.conf to start the firewall at boot time and install the rules: