Skip to content

Instantly share code, notes, and snippets.

@girst
Last active March 8, 2024 13:26
Show Gist options
  • Save girst/d9be366f108e1afa32a1d07fa0901282 to your computer and use it in GitHub Desktop.
Save girst/d9be366f108e1afa32a1d07fa0901282 to your computer and use it in GitHub Desktop.
Simple Linux Load Balancing with `iproute2`
#!/bin/bash
# Load balance multiple internet connections. Requires iproute2, awk and grep.
# (C) 2016 Tobias Girstmair, isticktoit.net, GPLv2
# Also useful: speedometer -l -r eth1 -t eth1 -m $(( 1024 * 1024 * 3 / 2 ))
# Not much user error checking is done - only pass working network connections
# script needs root to work and at least two interfaces to be useful
[ $EUID -eq 0 -a $# -ge 2 ] || {
echo "Usage (as root): $0 iface1[:weight1] iface2[:weight2] ..." >&2
exit 1
}
get_free_tblnum() { # http://stackoverflow.com/a/28702075
awk -v RS='\\s+' '{ a[$1] } END { for(i = 10; i in a; ++i); print i }'</etc/iproute2/rt_tables
}
loadbal() {
IFS=':' read IFACE WEIGHT <<< "$1"
TABLE="${IFACE}loadbalance"
if ! grep -q -w "$TABLE" /etc/iproute2/rt_tables ; then
echo "$(get_free_tblnum) $TABLE" >> /etc/iproute2/rt_tables
fi
MY_IP=$(ip -o -4 addr show $IFACE |awk -F'(\\s|/)+' '{print $4}')
GW_IP=$(ip route show dev $IFACE | awk '/default/ {print $3}')
SUBNT=$(ip route show dev $IFACE | awk '/proto kernel/ {print $1}')
ip route add $SUBNT dev $IFACE src $MY_IP table $TABLE
ip route add default via $GW_IP table $TABLE
ip rule add from $MY_IP table $TABLE
echo nexthop via $GW_IP dev $IFACE weight ${WEIGHT:-1}
}
ip route add default scope global $(for IF in "$@"; do loadbal $IF; done)
@girst
Copy link
Author

girst commented Feb 14, 2020

your enx... interface doesn't have a default gateway (run the script through sed '/add default via/s/^/test -n "$GW_IP" \&\& /' should remove that error). "file exists" errors you can ignore. i haven't bothered removing entries; just reboot and you're back to where you started.

@shakibamoshiri
Copy link

routes are cached !
there should be a way of bypassing cache or not cache at all, otherwise it wont be that much useful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment