A continuación se presenta un ejemplo de configuración en el que se usa una Raspberry Pi para crear un router 3G/4G. Además de presentar la configuración se incluye una configuración avanzada para dotarlo de QoS y limitar el tráfico de los dispositivos conectados.
Instalación básica
En el proceso mostrado se parte de una imagen básica arm64 descargada desde https://downloads.raspberrypi.org/. Recomiendo usar la imagen de Debian mínima (lite) en una Raspberry Pi 4, aunque en una Pi 3 todo funciona de forma similar.
Para no necesitar teclado y pantalla se pueden realizar las siguientes modificaciones en la tarjeta SD antes de ponerla en la Raspberry Pi. Se monta la partición boot y se hacen los siguientes cambios:
- Para conexión directa con un cable de red, editar
cmdline.txt
y añadir después de rootwaitip=192.168.99.99
y en el otro extremo una IP de la misma red. - Para activar el servidor SSH: crear un fichero ssh en /boot con
touch ssh
.
Recuerda que la partición boot es la partición VFAT de la imagen descargada.
Conexión via modem 3G/4G
En las pruebas que he realizado en los últimos años me han funcionado prácticamente todos los modem USB que he comprado. Tanto de marcas conocidas como algunos chinos. Con algunos de ellos puedes tener problemas de desconexión pero suele ser porque se sobrecalienta y se apaga, asegúrate que esté todo bien ventilado.
La configuración propuesta para el modem USB opere es la siguiente:
- Permitir extraer o poner el modem con la Raspberry Pi funcionando.
- Cambiar el gestor de red por defecto a NetworkManager.
Tras el primer arranque con la imagen Debian lite hay que entrar y cambiar el gestor de red por NetworkManager mediante:
apt install network-manager
apt purge openresolv dhcpcd5
sytemctl enable NetworkManager
Tras un reinicio la red estará funcionando con NetworkManager y se puede
comprobar ejecutando nmcli
. En este momento se conecta el modem USB y se
comprueba si lo ha reconocido mediante
$ ip a
4: wwan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 5a:e5:3c:15:8a:00 brd ff:ff:ff:ff:ff:ff
En este caso aparece la interfaz wwan0
y mediante el modem-manager mmcli
se puede comprobar el model:
$ mmcli -L
/org/freedesktop/ModemManager1/Modem/0 [QUALCOMM INCORPORATED] 0
Es importante el número de modem, que es 0
, para configurar el APN. En el
ejemplo mostrado se usa una tarjeta de simyo, a la que previamente se le ha
desactivado el PIN. En caso de otro operador, solo hay que introducir los
datos correctos:
$ nmcli c add con-name "simyo" type gsm ifname "*" apn "orangeworld"
$ nmcli c mod simyo gsm.username ""
$ nmcli c mod simyo gsm.password ""
En el caso de simyo no hay usuario y contraseña. Aunque existe un asistente en
modo texto para network-manager: nmtui-edit
esta configuración no se puede
hacer con este asistente. En cambio, sí se puede ver con nmtui-edit
la
conexión y activarla con el comando nmtui-connect
.
Llegado a este punto se va a configurar para que la conexión se reconecte
siempre de forma ininterrumpida. Por defecto el número de reintentos de conexión
es 3, se puede ver con nmcli
, siendo la referencia de estos parámetros
estos:
nm-settings-nmcli
$ nmcli conn show simyo
connection.id: simyo
connection.uuid: d1228d30-aad4-4966-b606-8f2270f7d3ff
connection.stable-id: --
connection.type: gsm
connection.interface-name: --
connection.autoconnect: sí
connection.autoconnect-priority: 0
connection.autoconnect-retries: -1 (default)
connection.multi-connect: 0 (default)
connection.auth-retries: -1
connection.timestamp: 1576525195
connection.read-only: no
connection.permissions: --
connection.zone: --
connection.master: --
connection.slave-type: --
connection.autoconnect-slaves: -1 (default)
connection.secondaries: --
connection.gateway-ping-timeout: 0
connection.metered: desconocido
connection.lldp: default
connection.mdns: -1 (default)
connection.llmnr: -1 (default)
El problema está con la opción connection.auth-retries: -1
, donde -1
significa 3 veces, hay que ponerlo a 0 para que sea infinito. También es
necesario cambiar autoconnect-retries -1
ya que -1 significa 4 veces y también
hay que ponerla a cero:
nmcli c modify simyo connection.autoconnect-retries 0
nmcli c modify simyo connection.auth-retries 0
Para finalizar hay que activar la conexión y ponerla en modo automático mediante:
nmcli c mod simyo connection.autoconnect yes
nmcli c up simyo
Crear el punto de acceso y compartir Internet
Sobre esto hay multitud de información en la red, habitualmente usando: hostapd, dnsmasq e iptables. Aquí se va a realizar de diferente modo:
- El punto de acceso se creará con NetworkManager
- El firewall con nftables
- El servidor de nombre y dhcp sí se realizará con dnsmasq
El punto de acceso Wifi se configura mediante nmtui-edit
añadiendo
una nueva conexión de tipo WiFi con las opciones:
En la configuración de la captura anterior hay que asegurarse de:
- Indicar el dispositivo wlan0
- Establecer el modo en "Punto de acceso"
- Establecer la configuración IPv4 en manual y asignar únicamente una IP
Con esto se activa sólo el punto de acceso pero queda por configurar el sistema operativo para que reenvíe paquetes
Activando el reenvío (forwarding)
Esto es lo que siempre se olvida activar, editar fichero /etc/sysctl.conf
y
descomentar la línea:
net.ipv4.ip_forward=1
para activarlo y comprobar que está correcto ejecutar el comando indicado y comprobar la salida
$ sysctl -p
net.ipv4.ip_forward = 1
Ahora dnsmasq (dhcp server + dns cache)
dnsmasq es un servidor DHCP y DNS caché muy usado, existiendo muchas páginas con información para su configuración. Recomiendo una configuración básica siguiendo las instrucciones dnsmasq - Debian
Para instalarlo:
apt install dnsmasq
Para configurarlo se crea un fichero nuevo /etc/dnsmasq.d/router.conf
. Se va a
crear un pool de 20 Ips y se utilizarán los DNS de Google. Es importante que la
IP coincida con la IP estática asignada a wlan0
cuando se configuró
NetworkManager. El contenido de /etc/dnsmasq.d/router.conf
inicialmente
puede ser el siguiente
interface=wlan0
listen-address=192.168.20.1
bind-interfaces
server=8.8.8.8
domain-needed
bogus-priv
dhcp-range=192.168.20.100,192.168.20.120,24h
Para activar el servicio en el arranque y probar si funciona correctamente se debe iniciar el servicio y comprobar si no hay errores mediante:
systemctl enable dnsmasq
systemctl restart dnsmasq
systemctl status dnsmasq
Debido a un bug el inicio puede fallar, es recomendable cambiar el servicio
con systemctl edit dnsmasq.service
y añadir:
[Unit]
After=network-online.target
Wants=network-online.target
Después es recomendable recargar la configuración de systemd con:
systemctl daemon-reload
Firewall y NAT con NFTables
Para compartir Internet se usará nftables ya que iptables está obsoleto y es buen momento para ir aprendiendo cosas nuevas.
apt purge iptables
apt install nftables
Probablemente en la distribución ya está instalado, pero no está activo el en el arranque del sistema por ello hay que ejecutar:
systemctl enable nftables
La activación del servicio en el paso anterior hace que el sistema tras
iniciar la red cargue siempre el fichero /etc/nftables.conf
que es
donde está el firewall del sistema.
Para activar NAT hay que editar el fichero /etc/nftables.conf
y añadir al
final del mismo:
table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
masquerade random,persistent
}
}
Para cargar esta configuración se puede ejecutar directamente
/etc/nftables.conf
o reiniciar el servicio que es lo mismo:
systemctl restart nftables
A partir de este momento si todo ha ido bien funcionará el nuevo punto de acceso compartiendo Internet.
Añadiendo la calidad de servicio / control de tráfico
Añadiendo QoS a este sistema persigue conseguir los siguientes objetivos:
- Evitar agotar la tarifa de datos móvil del modem
- Evitar que uno de los dispositivos conectados al router se quede con todo el ancho de banda y se queden los demás sin servicio.
Para conseguir lo anterior, la solución aquí presentada hace lo siguiente:
- Se establece un límite al ancho de banda global (entrada/salida) de la conexión externa del modem.
- Se reparte de manera equitativa el ancho de banda externo configurado en el punto anterior entre todos los dispositivos conectados al router, y a cada dispositivo se le garantiza al menos un mínimo de ancho de banda.
Para que todo opere correctamente la configuración anterior debe ser coherente de forma que:
- El límite exterior debe ser inferior al ancho de banda de la conexión externa. Esto es complicado de saber, pues el modem puede alternar en 3G/4G. La mejor opción es medirla o ir probando sabiendo cual es la máxima.
- Hay que asegurarse que la suma de los anchos de banda garantizados no exceda el límite del punto anterior.
Si deseas profundizar en este tema las referencias son:
- Página oficial: Linux Advanced Routing & Traffic Control
- Un manual con una explicación extensa en español sobre el tema, usada en la docencia oficial que imparto Laboratorio-5-Controldetráfico.pdf
Configuración propuesta
El procedimiento propuesto consiste en cargar un script QoS cuando se active la
conexión del modem. Para ello, en NetworkManager cuando ocurre un evento se
ejecutan los scripts del directorio /etc/NetworkManager/dispatcher.d/
. Así,
hay que añadir nuevo script que compruebe la interfaz que se está disparando es
wwan0
y añadir el control de tráfico.
En este primer ejemplo simple, sólo se va a limitar el tráfico entrante y saliente a un determinado caudal. No se hará reparto equitativo entre las diferentes IPs conectadas al punto de acceso. Si tienes muchos equipos consumiendo datos y deseas que todos tengan siempre algo de ancho de banda debes realizar la configuración avanzada contada en la próxima sección.
En este ejemplo básico se creará el fichero
/etc/NetworkManager/dispatcher.d/modem-qos.sh
con permiso de ejecución: chmod
+x /etc/NetworkManager/dispatcher.d/modem-qos.sh
. El contenido sería el
mostrado pero se deben establecer los límites adecuados en las variables
I_LIMIT=4Mbit
y O_LIMIT=4Mbit
#!/usr/bin/env bash
interface=$1
event=$2
INET=wwan0 # Salida del modem
WIFI=wlan0 # Wifi en modo AP
I_LIMIT=4Mbit ## límite en la entrada aplicada en wifi
O_LIMIT=2Mbit ## límite en salida aplicada en wwan0
function start {
set -x
# Tráfico entrante
tc qdisc del dev $WIFI root 2> /dev/null
tc qdisc add dev $WIFI root handle 1: htb default 30
tc class add dev $WIFI parent 1: classid 1:1 htb rate $I_LIMIT burst 5K
## Tráfico saliente
tc qdisc del dev $INET root 2> /dev/null
tc qdisc add dev $INET root handle 2: htb default 30
tc class add dev $INET parent 2: classid 2:1 htb rate $O_LIMIT burst 5K
set +x
}
function stop {
# Tráfico entrante
tc qdisc del dev $WIFI root 2> /dev/null
## Tráfico saliente
tc qdisc del dev $INET root 2> /dev/null
}
if [[ $interface = "wwan0" ]] && [[ $event = "up" ]]
then
start
fi
if [[ $interface = "wwan0" ]] && [[ $event = "down" ]]
then
stop
fi
Para comprobar su la configuración es correcta basta con quitar y poner el modem USB. Si se ha disparado correctamente el script anterior se podrá ver en el fichero syslog y además se puede comprobar mediante los comandos tc que muestran las estadísticas:
tc -s -d qdisc show dev wlan0
tc -s -d class show dev wlan0
tc -s -d qdisc show dev wwan0
tc -s -d class show dev wwan0
Versión avanzada de calidad de servicio / control de tráfico
En esta segunda versión se asigna límites a cada una de las IPs conectadas. Estás deben de coincidir con las configuradas anteriormente con dnsmasq. Además estoy dando prioridad a las conexiones SSH, a los paquetes DNS y permitiendo la red local a máxima velocidad para asegurarme que todo funciona mejor.
En este ejemplo los parámetros a establecer son los siguientes:
- IP_LIMIT: Mínimo ancho de banda de entrada garantizado a cada IP
- I_LIMIT: Máximo ancho de banda de entrada alcanzable por IP
- O_LIMIT: Límite global del ancho de banda de salida
Para que la configuración opere correctamente hay que considerar que si se permiten 10 dispositivos conectados simultáneamente entonces la siguiente restricción debe cumplirse: 10 × IP_LIMIT ≤ O_LIMIT
#!/usr/bin/env bash
interface=$1
event=$2
# Trafic shaper
INET=wwan0
WIFI=wlan0
IP_LIMIT=500kbit # Mínimo ancho de banda de entrada garantizada a cada IP (rate)
I_LIMIT=5Mbit # Máximo ancho de banda de entrada alcanzable por IP (ceil)
O_LIMIT=2Mbit # Límite global del ancho de banda de salida
function start {
# Tráfico entrante
tc qdisc del dev $WIFI root 2> /dev/null
tc qdisc add dev $WIFI root handle 1: htb default 30
tc class add dev $WIFI parent 1: classid 1:1 htb rate $I_LIMIT burst 5K
tc class add dev $WIFI parent 1: classid 1:2 htb rate 100Mbit # Red local
tc class add dev $WIFI parent 1:1 classid 1:10 htb rate 150kbit ceil $I_LIMIT prio 1
tc class add dev $WIFI parent 1:1 classid 1:30 htb rate 100kbit ceil $I_LIMIT prio 2
# Filtro para red local
tc filter add dev $WIFI \
protocol ip parent 1: prio 1 u32 \
match ip src 192.168.20.0/24 \
match ip dst 192.168.20.0/24 \
flowid 1:2
# Filtro para tráfico SSH
tc filter add dev $WIFI \
protocol ip parent 1: prio 1 u32 \
match ip sport 22 0xffff \
flowid 1:10
# Filtro para tráfico SSH
tc filter add dev $WIFI \
protocol ip parent 1: prio 1 u32 \
match ip dport 22 0xffff \
flowid 1:10
# Filtro para tráfico DNS
tc filter add dev $WIFI protocol ip parent 1: prio 1 u32 \
match ip sport 53 0xffff flowid 1:10
for ip in {100..120} ; do
tc class add dev $WIFI parent 1:1 classid 1:$ip htb rate $IP_LIMIT ceil $I_LIMIT prio 2
tc filter add dev $WIFI parent 1:0 protocol ip prio 1 u32 \
match ip dst 192.168.20.$ip flowid 1:$ip
done
## Tráfico saliente
tc qdisc del dev $INET root 2> /dev/null
tc qdisc add dev $INET root handle 2: htb default 30
tc class add dev $INET parent 2: classid 2:1 htb rate $O_LIMIT burst 5K
tc class add dev $INET parent 2:1 classid 2:10 htb rate 500kbit ceil $O_LIMIT prio 1
tc class add dev $INET parent 2:1 classid 2:30 htb rate 200kbit ceil $O_LIMIT prio 2
# Filtro para tráfico DNS saliente
tc filter add dev $INET protocol ip parent 2: prio 1 u32 \
match ip dport 53 0xffff flowid 2:10
}
function stop {
# Tráfico entrante
tc qdisc del dev $WIFI root 2> /dev/null
## Tráfico saliente
tc qdisc del dev $INET root 2> /dev/null
}
if [[ $interface = "wwan0" ]] && [[ $event = "up" ]]
then
start
fi
if [[ $interface = "wwan0" ]] && [[ $event = "down" ]]
then
stop
fi