📜 ⬆️ ⬇️

Home Internet: routing two (and more) providers based on Bird Routing Daemon

Probably, many home Internet users have come across how to parallelize two or more Internet channels on a home network.
This problem can be solved both hardware (using any cheap or expensive equipment) and software.
What is the routing model to choose? You can immediately drop RIP / OSPF / BGP, since this is the home Internet and is more than confident (in my case, it has been verified) that you will not want to do support on the provider side for free.
I opted for the bird .

So, the starting position:


My network configuration:


I am a fan of apt, but as it turned out, the apte is a rather outdated version of bird (1.2.5-1) and there is no multipath support.
We'll have to collect it by hand. I deliberately omit any flame beforehand, how not to turn debian into slackware.
')
# mkdir -p /usr/local/src/bird && cd /usr/local/src/bird # wget ftp://bird.network.cz/pub/bird/bird-1.3.8.tar.gz # tar xf bird-1.3.8.tar.gz # cd bird-1.3.8 # ./configure --prefix=/usr --sysconfdir=/etc/bird --localstatedir=/var # make # make install 


As it turned out, there is no startup script in Debian for sorts.
We generate a start-up script (brazenly taken from the same obsolete package to the apt-e and slightly corrected) /etc/init.d/bird with the following content:

 #! /bin/sh ### BEGIN INIT INFO # Provides: bird # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 ### END INIT INFO # Author: Ondřej Surý <ondrej@sury.org> # # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="Internet routing daemon" NAME=bird DAEMON=/usr/sbin/$NAME DAEMON_ARGS="-c /etc/bird/bird.conf" #PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --name $NAME --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --name $NAME --exec $DAEMON -- \ $DAEMON_ARGS \ || return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME --exec $DAEMON RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --name $NAME --exec $DAEMON [ "$?" = 2 ] && return 2 return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --name $NAME --exec $DAEMON return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # log_daemon_msg "Reloading $DESC" "$NAME" do_reload log_end_msg $? ;; restart) log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 exit 3 ;; esac 


The startup script is ready, we add to the auto-launch:
 # chmod +x /etc/init.d/bird # update-rc.d bird enable update-rc.d: using dependency based boot sequencing 


Create a directory for the log file:
 # mkdir /var/log/bird 


Now we proceed to configuring the bird itself.

To get started, backup the original config and create a new one:
 # cd /etc/bird # mv bird.conf bird-orig.conf # :> bird.conf 


Then open it for editing:
 # # Logging # #  . # all  { debug, trace, info, remote, warning, error, auth, fatal, bug } # ,    2      (  ,     ): # log "/var/log/bird/debug.log" { debug }; # log "/var/log/bird/warning.log" { warning }; log "/var/log/bird/bird.log" all; # Router ID: #   IP-   ,      ( !) . router id 192.168.254.254; # # Debugging # #  : # all | off | { states, routes, filters, interfaces, events, packets } debug protocols { routes, interfaces }; # # Protocols # #  ,        (   -  eth0  eth2) protocol direct { interface "eth0", "eth2"; } # # Tables # #    #   ISP1  ISP2: table ISP_ISP1; table ISP_ISP2; #  "": table other; #  "master" -    (main). ,  main ! table master; # # Protocol 'static' # protocol static { table ISP_ISP1; description "Home internet ISP1"; check link on; preference 100; route 0.0.0.0/0 via 10.10.10.1; } protocol static { table ISP_ISP2; description "Home internet ISP2"; check link on; preference 150; route 0.0.0.0/0 via 172.17.5.1; } protocol static { table other; description "Other custom static routes"; preference 200; #    (   )  /etc/bird/static_route.d/ include "/etc/bird/static_route.d/*.conf"; } protocol static { table master; description "Common Table"; #   ,    default- route 0.0.0.0/0 multipath via 10.10.10.1 via 172.17.5.1; } # # Protocol 'kernel' # #      'debug all'.        . #    'all'  .   ,  -    ,    . # #  'persist' -   bird  ,  ,   (  "") bird. #  'learn' - ,  bird  ""  ,      #   10/11/254 (  ,   10, 11, 254). # protocol kernel { table ISP_ISP1; persist; learn; scan time 20; kernel table 10; export all; # debug all; } protocol kernel { table ISP_ISP2; persist; learn; scan time 20; kernel table 11; export all; # debug all; } protocol kernel { table master; persist; learn; scan time 20; kernel table 254; export all; # debug all; } # # Protocol 'pipe. # #   (default). #   ,     master    ,    ,   ISP_ISP1, ISP_ISP2  other. protocol pipe { table master; peer table ISP_ISP1; peer table ISP_ISP2; peer table other; import all; # debug all; } 


Now we create a directory for manual config files and, for example, add some routes:

 # mkdir /etc/bird/static_route.d/ # cat << EOF >/etc/bird/static_route.d/ISP1_LAN.conf route 10.0.5.0/24 via 10.10.10.1; route 175.5.25.0/27 via 10.10.10.1; #   " ", ,     IP  ISP1 EOF # cat << EOF >/etc/bird/static_route.d/ISP2_LAN.conf route 194.22.253.23/27 via 172.17.5.1; #   " ", ,     IP  ISP2 route 5.9.0.0/16 via 172.17.5.1; #   " ", ,     IP  ISP2 EOF 


That's all, bird setup is over.
Now go to setting up the routing tables.

Add tables to / etc / iproute2 / rt_tables:
 # cat << EOF >>/etc/iproute2/rt_tables 10 ISP1 11 ISP2 EOF 


Now you need to make sure that when you request from the ISP1 network - the packets go back to ISP1, and not on a different interface.
To do this, add ip rule (s):
To do this, create scripts iprules:

Code /etc/network/if-up.d/iprules:
 #!/bin/bash ISP1_NETWORKS="10.0.5.0/24 175.5.25.0/27" ISP2_NETWORKS="194.22.253.23/27 5.9.0.0/16" if [ "${LOGICAL}" = "eth0" ]; then for NET in ${ISP1_NETWORKS}; do ip ru a from ${NET} table ISP1 2>/dev/null 1>/dev/null done fi if [ "${LOGICAL}" = "eth2" ]; then for NET in ${ISP2_NETWORKS}; do ip ru a from ${NET} table ISP2 2>/dev/null 1>/dev/null done fi 


Code /etc/network/if-down.d/iprules:
 #!/bin/bash ISP1_NETWORKS="10.0.5.0/24 175.5.25.0/27" ISP2_NETWORKS="194.22.253.23/27 5.9.0.0/16" if [ "${LOGICAL}" = "eth0" ]; then for NET in ${ISP1_NETWORKS}; do ip ru d from ${NET} table ISP1 2>/dev/null 1>/dev/null done fi if [ "${LOGICAL}" = "eth2" ]; then for NET in ${ISP2_NETWORKS}; do ip ru d from ${NET} table ISP2 2>/dev/null 1>/dev/null done fi 


Put a flag + x:
 # chmod +x /etc/network/if-{up,down}.d/iprules 


Now we rebuild everything:

In order not to copy the team, let's make it smarter:
 # LOGICAL=eth0 /etc/network/if-up.d/iprules # LOGICAL=eth2 /etc/network/if-up.d/iprules 


Run the bird:
 # invoke-rc.d bird start 


Checking:
 # ip r 10.0.5.0/24 via 10.10.10.1 dev eth0 proto bird 175.5.25.0/27 via 10.10.10.1 dev eth0 proto bird 194.22.253.23/27 via 172.17.5.1 dev eth2 proto bird 5.9.0.0/16 via 172.17.5.1 dev eth2 proto bird 10.10.10.0/24 dev eth0 proto kernel scope link src 10.10.10.106 172.17.5.0/24 dev eth2 proto kernel scope link src 172.17.5.105 192.168.254.0/24 dev eth1 proto kernel scope link src 192.168.254.254 default proto bird nexthop via 10.10.10.1 dev eth0 weight 1 nexthop via 172.17.5.1 dev eth2 weight 1 


Now pull out the cord, disconnect the network cable from ISP1 (eth0), see what has changed:
 # ip r | grep -E "(default|nexthop)" default proto bird nexthop via 10.10.10.1 dev eth0 weight 1 dead nexthop via 172.17.5.1 dev eth2 weight 1 


That is, if one of the providers is accidentally disconnected, the traffic will go through the other, the disabled provider will be monitored every 20 seconds (see the scan time 20 option in the kernel protocols) and will automatically remove the 'dead' when it appears.

In conclusion: in the same way, you can add 10 providers.

Source: https://habr.com/ru/post/160047/


All Articles