Lossy Inbound Links
Here’s a short companion walkthrough to my Performance @Scale talk on making a lossy inbound link, in the style of the Magic Modem. For a full OS router OS built around this with global ISP traffic shaping details and a simple UI, check out manaOS
VM Setup and OS Install
Download debian-9.0.0-amd64-netinst.iso (or similar, should work with 9.4.0), and VirtualBox if you need them.
VirtualBox configuration
New Virtual Machine:
Expert Mode
Name: lldemo
Type: Linux
Version: Debian (64-bit)
Memory Size: 512MB
Hard disk: Create a virtual hard disk now
Hard disk file type: VDI
Storage on physical hard disk: Dynamically allocated
File location and size: 8GB
Create
File menu, "Host Network Manager..."
Create
This example uses 192.168.56.1/24 for the Host, adjust as needed
Under "Adapter", "Configure Adapter Manually", IPv4 Address: 192.168.56.1 with mask 255.255.255.0
Choose the "DHCP Server" tab
Ensure "Enable Server" is checked
Server Address: 192.168.56.2, Mask 255.255.255.0
Lower Address Bound: 192.168.53.3
Upper Address Bound: 192.168.56.254
Select "Apply"
Right click lldemo, choose "Settings..."
Choose the "Storage" tab
Click the empty CD/DVD
Click the CD/DVD icon next to the "Optical Drive: IDE Secondary Master" menu
"Choose Virtual Optical Disk File", select the debian image
Choose the "Network" tab
Configure Adapter 2, Enable, Host-only Adapter, Name: vboxnet0
OS Installation
Start the image, Install, perform a normal debian install, enp0s3 should correspond to VirtualBox Adapter 1, while enp0s8 corresponds to VirtualBox Adapter 2 (Host-only adapter on vboxnet0)
- Primary interface should be enp0s3
- Creating the sudoable user “wiz” with password “wiz” for this example
- For software selection, select only “SSH server”
The machine should reboot and leave you at the login prompt. Login and configure the second network interface:
sudo vim.tiny /etc/network/interfaces
# add these two lines for enp0s8, similar to enp0s3:
allow-hotplug enp0s8
iface enp0s8 inet dhcp
sudo reboot # or just restart networking
After reboot, check the IP address that the VM retrieved, it should be 192.168.56.3:
ip addr show dev enp0s8
From the host machine, ping and ssh should now work:
ping 192.168.56.3
# for the rest of this example, keep this ssh open:
ssh -v -o "DynamicForward 1080" wiz@192.168.56.3
Lossy Link Configuration
# Setup forwarding and masquerading:
sudo sysctl -w net.ipv4.conf.all.forwarding=1
sudo iptables -t nat -A POSTROUTING -o enp0s3 -jMASQUERADE
# Add the IFB interface for inbound (internet download) shaping
sudo modprobe -v ifb numifbs=1
sudo ip link set up dev ifb0
# Send inbound (internet download) traffic to the ifb:
sudo tc qdisc add dev enp0s3 ingress
sudo tc filter add dev enp0s3 parent ffff: \
protocol ip u32 match u32 0 0 flowid 1:1 \
action mirred egress redirect dev ifb0
# Setup negligible network emulation, 2 special traffic classes:
sudo tc qdisc add dev ifb0 root handle 1: htb
sudo tc class add dev ifb0 parent 1: classid 1:2 htb rate 100Mbit
sudo tc qdisc add dev ifb0 parent 1:2 handle 12: netem delay 0ms loss 0%
sudo tc class add dev ifb0 parent 1: classid 1:3 htb rate 100Mbit
sudo tc qdisc add dev ifb0 parent 1:3 handle 13: netem delay 0ms loss 0%
# can always reset to scratch with:
# tc qdisc del dev ifb0 root
# and reinstall the above network classes
# Assign all traffic to class 1:3:
sudo tc filter add dev ifb0 protocol ip u32 match ip src 0.0.0.0/0 classid 1:3
# Ensure there are no running instances of Chrome, then start one that uses the proxy:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--incognito --proxy-server="socks5://127.0.0.1:1080" \
--host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE 127.0.0.1"
# Set all traffic in class 1:3 to moderately horrible:
tc qdisc replace dev ifb0 parent 1:3 handle 13: netem delay 2394ms loss 11% rate 4Mbit
# Reset class 1:3 to negligible delay:
tc qdisc replace dev ifb0 parent 1:3 handle 13: netem delay 0ms
# Assign routes on AWS EC2 in us-east-1 to class 1:2:
sudo apt-get install -y jq curl
curl -s https://ip-ranges.amazonaws.com/ip-ranges.json | \
jq '.prefixes[] | select(.region == "us-east-1" and .service == "EC2") | .ip_prefix' | \
tr -d '"' | \
xargs -I "%" printf "tc filter add dev ifb0 protocol ip u32 match ip src % classid 1:2\n" | \
sudo bash
# Set most traffic to slow, but traffic to AWS EC2 in us-east-1 to fast:
sudo tc qdisc replace dev ifb0 parent 1:3 handle 13: netem delay 1000ms loss 5% rate 1Mbit
sudo tc qdisc replace dev ifb0 parent 1:2 handle 12: netem delay 5ms loss 1% rate 10Mbit
# Configure your networks to taste, adjust the parameters to those collected
# by your RUM metrics with per-packet modeling