Bandwidth limiting on OS-X [Yosemite, El Capitan or *BSD]

Since Yosemite OS-X is not using ipfw but pf. So if you’re using those ipfw commands to limit your bandwidth, well… they won’t work. You’ll have to learn and implement some pf rules. The usage of it is pretty much the same as on *BSD. I was about to write some words about it when I found the one below on Reddit written by ClapsOn2and4. Great and helpful article!

…. To approach this, the first step is to observe the IPs you are sending to when doing amazon uploads. I’m a unix fellow, so I’ll show you how to do this using traditional unix tools. If you’ve never used Terminal in OS X, go read or watch a basic tutorial or two until you’re comfortable with the idea of running basic commands (including ‘sudo’). When you’re ready, get an amazon upload ready and then open Terminal and run the following command:

sudo tcpdump -q -n -i pktap -k

(‘sudo’ will want your OS X account password, as it runs a command with elevated privileges. The password won’t be shown as you type it, not even as bullets. Be careful with sudo 🙂 (also note that tcpdump’s ability to show application names is unique to OS X)

Start your amazon upload. You should see a bunch of scrolling in the Terminal window. Wait a few seconds, then type control-c in Terminal to terminate the tcpdump command and stop the scrolling. Now examine the output (scroll up if needed) and look for lines containing references to Amazon Cloud Drive (perhaps a little truncated), such as:

20:28:04.332600 (en0, proc Amazon Cloud Dr:11843, svc BE, out) IP 192.168.2.89.50917 > 107.23.224.190.443: tcp 1448

The above line logs some information about an outbound packet (note the word ‘out’ before the close paren). The source address (i.e. my computer) is the one on the left of the ‘>’, 192.168.2.89, and the remote address is on the right of the ‘>’, 107.23.224.190, which resolves to ec2-107-23-224-190.compute-1.amazonaws.com. Note that the local and destination ports (50917 and 443, respectively) are displayed right after each IP address in the tcpdump output.

You could then apply the bandwidth limiting rule to any packets sent to 107.23.224.190, but it’s likely that your uploads might also be sent to other nearby amazon servers. To find the range of addresses used by Amazon that this address is inside, we can use the ‘whois’ command (back in Terminal) to look up various information about the IP address, and filter the output of the whois command with grep to only show the line including the word NetRange (the info we want):

╭─ andre@foci ~
╰─ $ whois 107.23.224.190 | grep NetRange
NetRange:       107.20.0.0 - 107.23.255.255

There are several ways to express an IP range, but one common way typically supported by firewalls is CIDR notation, which looks like an IP address followed by a slash followed by the number of bits in the netmask, e.g. 192.168.42.42/24. To express the range of addresses returned by the whois command in CIDR notation, we can use an online subnet calculator, or a local program such as whatmask. Feed the amazon server IP to the subnet calculator, then pick a subnet size and calculate. The results include the first and last usable host addresses of that server’s subnet for the specified mask. Keep trying different masks until the first and last addresses (almost) line up with the NetRange info from whois. I say ‘almost’ because the NetRange info includes the network address and broadcast address, but the subnet calculator calls those out separately, so the first / last address will each be off by one. Smaller masks specify larger ranges of addresses. In this case, the CIDR notation we’re looking for is 107.23.224.190/14 (a relatively large subnet):

First host:  107.20.0.1
 Last host:  107.23.255.254

In the firewall rule that sends traffic to the bandwidth limited queue, specify (for this example, ymmv) 107.23.224.190/14 as the remote address (aside: this would more commonly be written by using the first address in the subnet: 107.20.0.0/14, which means the same thing but looks cleaner, however any address in the range is fine).

A complete example of how to configure this without Murus is codified in the following script, which defines a PF table called <amazonws> to hold all the subnets, read from a text file. Create a file called amazonws and put subnet entries in it, one per line. In the script, edit the path in the ‘table <amazonws>’ line to point to the file you created. Also edit the bandwidth limit on the ‘dnctl pipe’ line as desired.

#!/bin/bash

# Reset dummynet to default config
dnctl -f flush

# Compose an addendum to the default config; creates a new anchor
# and a table whose contents are loaded from the specified file
read -d '' -r PF <<EOF
dummynet-anchor "amazon.bw"
anchor "amazon.bw"
table <amazonws> persist file "/Users/andre/Documents/amazonws"
EOF

# Reset PF to default config and apply our addendum
(cat /etc/pf.conf && echo "$PF") | pfctl -q -f -

# Configure the new anchor
cat <<EOF | pfctl -q -a amazon.bw -f -
no dummynet quick on lo0 all
dummynet out proto tcp from any to <amazonws> port 1:65535 pipe 1
EOF

# Create the dummynet queue
dnctl pipe 1 config bw 1Mbit/s

# Activate PF
pfctl -E

To use the script, put the above in a file, e.g. called amazon.bw, then make the file executable, and run it with sudo:

chmod +x amazon.bw
sudo ./amazon.bw

To make it run automatically at startup, you could create a launchd file for it, or add it to crontab using the special @reboot crontab interval

To disable PF:

sudo pfctl -d

To reset PF to Apple defaults:

sudo dnctl flush
sudo pfctl -f /etc/pf.conf

If this works for a while and then stops working, that’s probably because you are now uploading to an address in a different subnet. Do the above steps again to find the new amazon server address, then find the subnet for that address, and add it to the amazonws file, and re-run the script.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.