Skip to content

Instantly share code, notes, and snippets.

@girst
Last active March 8, 2024 13:26
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • 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)
@kokizzu
Copy link

kokizzu commented Feb 14, 2020

cool script :3
btw how to clear this rules?

also some command gives an error, but it works on certain interface (enp0s20u6u2 and wlp4s0) in my case

sudo ./load-balance.sh enp0s20u6u2 enx00e04c60b8f6 wlp4s0
+ '[' 0 -eq 0 -a 3 -ge 2 ']'
++ for IF in "$@"
++ loadbal enp0s20u6u2
++ IFS=:
++ read IFACE WEIGHT
++ TABLE=enp0s20u6u2loadbalance
++ grep -q -w enp0s20u6u2loadbalance /etc/iproute2/rt_tables
+++ ip -o -4 addr show enp0s20u6u2
+++ awk '-F(\\s|/)+' '{print $4}'
++ MY_IP=192.168.42.40
+++ ip route show dev enp0s20u6u2
+++ awk '/default/ {print $3}'
++ GW_IP=192.168.42.129
+++ ip route show dev enp0s20u6u2
+++ awk '/proto kernel/ {print $1}'
++ SUBNT=192.168.42.0/24
++ ip route add 192.168.42.0/24 dev enp0s20u6u2 src 192.168.42.40 table enp0s20u6u2loadbalance
RTNETLINK answers: File exists
++ ip route add default via 192.168.42.129 table enp0s20u6u2loadbalance
RTNETLINK answers: File exists
++ ip rule add from 192.168.42.40 table enp0s20u6u2loadbalance
++ echo nexthop via 192.168.42.129 dev enp0s20u6u2 weight 1
++ for IF in "$@"
++ loadbal enx00e04c60b8f6
++ IFS=:
++ read IFACE WEIGHT
++ TABLE=enx00e04c60b8f6loadbalance
++ grep -q -w enx00e04c60b8f6loadbalance /etc/iproute2/rt_tables
+++ ip -o -4 addr show enx00e04c60b8f6
+++ awk '-F(\\s|/)+' '{print $4}'
++ MY_IP=10.0.0.1
+++ ip route show dev enx00e04c60b8f6
+++ awk '/default/ {print $3}'
++ GW_IP=
+++ ip route show dev enx00e04c60b8f6
+++ awk '/proto kernel/ {print $1}'
++ SUBNT=10.0.0.0/8
++ ip route add 10.0.0.0/8 dev enx00e04c60b8f6 src 10.0.0.1 table enx00e04c60b8f6loadbalance
RTNETLINK answers: File exists
++ ip route add default via table enx00e04c60b8f6loadbalance
Error: any valid address is expected rather than "table".
++ ip rule add from 10.0.0.1 table enx00e04c60b8f6loadbalance
++ echo nexthop via dev enx00e04c60b8f6 weight 1
++ for IF in "$@"
++ loadbal wlp4s0
++ IFS=:
++ read IFACE WEIGHT
++ TABLE=wlp4s0loadbalance
++ grep -q -w wlp4s0loadbalance /etc/iproute2/rt_tables
+++ ip -o -4 addr show wlp4s0
+++ awk '-F(\\s|/)+' '{print $4}'
++ MY_IP=192.168.0.132
+++ ip route show dev wlp4s0
+++ awk '/default/ {print $3}'
++ GW_IP=192.168.0.254
+++ ip route show dev wlp4s0
+++ awk '/proto kernel/ {print $1}'
++ SUBNT=192.168.0.0/24
++ ip route add 192.168.0.0/24 dev wlp4s0 src 192.168.0.132 table wlp4s0loadbalance
RTNETLINK answers: File exists
++ ip route add default via 192.168.0.254 table wlp4s0loadbalance
RTNETLINK answers: File exists
++ ip rule add from 192.168.0.132 table wlp4s0loadbalance
++ echo nexthop via 192.168.0.254 dev wlp4s0 weight 1
+ ip route add default scope global nexthop via 192.168.42.129 dev enp0s20u6u2 weight 1 nexthop via dev enx00e04c60b8f6 weight 1 nexthop via 192.168.0.254 dev wlp4s0 weight 1
Error: inet address is expected rather than "dev".

@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