A smooth move to 4G using Linux

One of the problems that I faced in the past is how I switched from DSL to 4G connection. My primary internet connection is DSL, and things are bad when you try to upload something big or want to do a videocall without issues.

I used 4G over the phone, but the experience it's terrible. Also, it's not a smooth change: disconnect the cable, connect to your mobile wifi, etc. If you got a call, meanwhile you're using 4G connection, the connection goes down, a lot of downsides for a permanent workspace.

Over the last weeks, I'm running a new setup, where I still use ADSL as a primary connection, but If I use VLAN 100, I'll use an external 4G antenna. Let me explain how I build this setup.

Hardware needed

  • Edgerouter X: This is the primary router; it does the LoadBalance.
  • Zyxel LTE7460: The 4G modem that I found. The best thing is that it supports bridge mode, so no double NAT issues.
  • Linux computer
  • Wan connection

Edgerouter X, load balance based on VLAN

Edgerouter X is, in my opinion, one of the best purchases that you can do. An old, but a powerful router, where you can achieve many things with a super simple DSL (Domain-specific Language) config.

The main behaviour will be: SWITCH0 interface will support a VLAN 100, when traffic goes into this virtual interface, it'll use a modify rule that will send traffic to a different Load Balancer. Here is the config:

VLAN 100 definition:

switch switch0 {
  address 192.168.25.1/24
  description Local
  firewall {
    in {
      modify balance
    }
  }
  mtu 1500
  switch-port {
    interface eth0 {
    }
    interface eth1 {
    }
    interface eth2 {
    }
    vlan-aware disable
  }
  vif 100 {
    firewall {
      in {
        modify balance_100
      }
    }
  }
}

Load Balance definition:

load-balance {
  group 4G {
    interface eth4 {
    }
    interface pppoe0 {
      failover-only
    }
    lb-local enable
    lb-local-metric-change disable
  }

  group G {
    interface pppoe0 {
    }
    interface pppoe1 {
    }
    interface eth4 {
      failover-only
    }
    lb-local enable
    lb-local-metric-change disable
  }
}

Outbound Firewall modify rule:

modify balance_100 {
  rule 10 {
    action modify
    description "do not balance lan to lan"
    destination {
      group {
        network-group PRIVATE_NETS
      }
    }
    log disable
    modify {
      table main
    }
  }
  rule 20 {
    action modify
    description "do NOT load balance destination public address"
    destination {
      group {
        address-group ADDRv4_pppoe0
      }
    }
    modify {
      table main
    }
  }
  rule 21 {
    action modify
    description "do NOT load balance destination public address"
    destination {
      group {
        address-group ADDRv4_pppoe1
      }
    }
    modify {
      table main
    }
  }
  rule 30 {
    action modify
    description "do NOT load balance destination public address"
    destination {
      group {
        address-group ADDRv4_eth4
      }
    }
    modify {
      table main
    }
  }
  rule 100 {
    action modify
    log disable
    modify {
      lb-group 4G
    }
  }
}

Linux set VLAN on the fly.

When setting a VLAN on a Linux system, I always used vconfig, but I wanted something fast and no permanent networking configuration involved in this case.

Linux TC (Traffic-control) is not commonly used, but it has a lot of power to change interface datapath configurations on the fly. One of the options that you can do is adding a VLAN tag when a condition matches.

So, I made a simple script, it enables or disables the VLAN 100. The best way is to use QDISC(queueing discipline) filters on the egress, this is the main action:

tc qdisc add dev $interface handle 1: root prio
tc filter add dev $interface parent 1: protocol ip matchall action vlan push id $VLAN_TO_PUSH

If the traffic is IP, in my case, it will set the VLAN 100, and traffic goes over 4G connection. Actions can be more powerful such as port/IP filtering, etc..

The complete script is the following:

#!/bin/bash

ACTION=$1
export VLAN_TO_PUSH=100

echoerr() { echo "$@" 1>&2; }

function get_local_interfaces() {
  LOCAL_INTERFACES=$(ip --json a show | jq -r '.[] | select((.operstate == "UP") and ((.addr_info?[0].local|tostring)|test("^192.*"))) | .ifname+"\t"+.addr_info?[0].local')
  echo -n "$LOCAL_INTERFACES"
}

function disable_4g {
  local interface=$1
  echoerr "Disable 4G connection"
  tc qdisc del dev $interface root
}

function enable_4g {
  local interface=$1
  echoerr "Enable 4G connection"
  tc qdisc add dev $interface handle 1: root prio
  tc filter add dev $interface parent 1: protocol ip matchall action vlan push id $VLAN_TO_PUSH
}

function is_interface_on_4g {
  local interface=$1
  tc filter show dev $interface parent 1: | grep "^filter" 1>&2
  if [[ $? -eq 1 ]]; then
    echoerr "Inferface '${interface}' does not have tc filter"
    false
  else
    echoerr "Inferface '${interface}' contains tc filter"
    true
  fi

}

function toggle_interface() {
  local interface=$1
  if is_interface_on_4g $interface; then
    disable_4g $interface
  else
    enable_4g $interface
  fi
}

function toggle() {
  IFS=$'\n'; for line in $(get_local_interfaces); do
    local interface=$(echo -n $line | awk '{print $1}')
    toggle_interface $interface
  done
}

function list() {
  local RESULT=""
  IFS=$'\n'; for line in $(get_local_interfaces); do
    local interface=$(echo -n $line | awk '{print $1}')
    DEFAULT_MODE="DSL"
    if is_interface_on_4g $interface; then
      RESULT="${RESULT}${interface}=4G "
    else
      RESULT="${RESULT}${interface}=DSL "
    fi
  done
  echo $RESULT
}

if [[ $ACTION == "toggle" ]]; then
  toggle
else
  list
fi

Argos Gnome Shell Extensions

Because I pay a lot of the 4G connection, I want to have something seamless and be notified when I use it. Argos Gnome Shell extension is the best way to get that info; a simple script displays if an interface is using DSL or 4G and I also add a toggle button, here is the complete script:

#!/usr/bin/env bash

export STATUS=$(~/bin/toggle_dsl.sh  2>/dev/null)
echo "${STATUS}"
echo "---"
echo "šŸ“”toggle | bash='~/bin/toggle_dsl.sh toggle' terminal=false"

Finally, this is the seamless setup, moving to 4G is easy; I do not need an external phone, I'm no longer worried about receiving calls. Because It uses a 4G antenna when ADSL is down, I can failover without problems! By far, this was one of the best productivity hacks that I made in 2020!

Yup, I already sign up on Startlink.

Cover image: https://pxhere.com/en/photo/1592827

Show Comments