Skip to content

Instantly share code, notes, and snippets.

@girst
Last active April 6, 2022 03:02
Embed
What would you like to do?
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.

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