CGNAT na prática
Objetivo
Com o esgotamento do IPv4 mundialmente, precisamos tomar algumas providências para que a Internet não pare. As que vejo de imediatas são: IPv6 e CGNAT (Carrier Grade NAT). O IPv6 é a real solução para os problemas de esgotamento e o CGNAT seria a "gambiarra" necessária para continuar com o IPv4 até que a Internet esteja 100% em IPv6. Nesse artigo será explicado como montar uma caixa CGNAT Determinística usando GNU/Linux e Mikrotik RouterOS. Esse artigo foi baseado no treinamento da Semana de Capacitação do NIC.br e que pode ser encontrado com o título CONCEITOS E IMPLEMENTAÇÃO DE CGNAT aqui como palestra e material de apoio e o vídeo do treinamento no Youtube aqui.
Diagrama
No BNG é configurado uma PBR (Policy Based Routing) onde apenas IPs do bloco 100.64.0.0/22 serão roteados diretamente para a caixa CGNAT. Qualquer IPv4 público ou IPv6, serão roteados diretamente para a Borda. Isso evita processamento e tráfego desnecessário na caixa CGNAT.
No diagrama ao lado a linha amarela simboliza o tráfego do bloco 100.64.0.0/22 indo para o CGNAT. A linha vermelha seria o tráfego já traduzido para um IP da rede 198.18.0.0/27 e encaminhado para a Borda. A linha verde é o tráfego mais limpo, sem "gambiarras" e o real objetivo que devemos seguir para uma Internet melhor usando IPv6.
A Borda é um equipamento onde podemos inserir algumas regras de filtros de pacotes stateless para filtrar alguns pacotes indesejados como por exemplo: determinados spoofings e BOGONs. Também onde serão feitas ACLs para filtros BGP. Ação 1 e 2 do MANRS.
O Cliente nesse diagrama aparece conectado com o IPv4 de CGNAT 100.64.0.2 e IPv6 2001:0db8:f18:0:a941:6164:1a79:c0f3. Todo o acesso IPv4 desse cliente e nesse exemplo, para a Internet, sairá com o IP 198.18.0.0 usando as portas entre 5056 e 7071, conforme mostraremos no script gerador de regras de CGNAT.
CGNAT no GNU/Linux
Hardware e Sistema que utilizaremos no GNU/Linux
- 2x Intel® Xeon® Silver 4215R Processor (3.20 GHz, 11M Cache, 8 núcleos/16 threads). Ambiente NUMA (non-uniform memory access).
- 32Gb de ram.
- 2x SSD 240 Gb RAID1.
- 2x Interfaces de rede Intel XL710-QDA2 (2 portas de 40 Gbps).
- GNU/Linux Debian 11 (Bullseye).
Vamos configurar um LACP com as duas portas de cada interface, para que possamos ter um backup, caso algum módulo apresente algum problema. Seu ambiente de produção pode ser diferente e por isso precisamos ter alguns cuidados na hora de montarmos o conjunto de hardware e não obtermos surpresas.
1º Verifique algumas especificações da interface de rede que será usada. Por exemplo a Intel XL710-QDA2:
- 2 portas de 40 Gbps.
- PCIe 3.0 x8 (8.0 GT/s).
Com essa informação seu equipamento não poderá possuir slots PCIe inferiores a esta especificação, caso contrário terá problemas de desempenho.
Você também precisa estar atento para as limitações de barramento por versão x lane (x1):
- PCIe 1.0/1.1 - 2.5 GT/s - (8b/10b encoding) - 2 Gbps.
- PCIe 2.0/2.1 - 5.0 GT/s - (8b/10b encoding) - 4 Gbps.
- PCIe 3.0/3.1 - 8.0 GT/s - (128b/130b encoding) - ~7,88 Gbps.
- PCIe 4.0 - 16 GT/s - (128b/130b encoding) - ~15,76 Gbps.
Calculando a capacidade
Se observarmos a XL710-QDA2 é PCIe 3.0 x8 (8 lanes) ou seja o barramento irá suportar:
- 8.0 GT/s * (128b/130b encoding) * 8 lanes = 63,01 Gbps
O objetivo do LACP nesse caso, não seria alcançar os 80 Gbps de capacidade em cada interface, mesmo porque cada barramento das interfaces é limitado em 63,01 Gbps, mas manteremos um backup dos 40 Gbps.
Nessa configuração teríamos teoricamente 63,01 Gbps de entrada e 63,01 Gbps de saída. Mas para esse cenário precisaremos fazer uma coisa chamada CPU Affinity. Nesse caso colocaríamos um processador dedicado para cada interface de rede. É um cenário mais complexo do que com 1 processador apenas, inclusive necessitamos de olhar o datasheet da motherboard e identificar quais slots PCIe são diretamente controlados por qual CPU. Se temos a CPU0 e CPU1, uma interface precisará ficar no slot controlado pela CPU0 e a outra interface no slot controlado pela CPU1 e observar a quantidade de lanes no slot para ver se suporta a mesma quantidade de lanes da interface de rede.
Falando um pouco sobre PPS (Packet Per Second) para calcular por exemplo 1 Gbps de tráfego na ethernet, a quantidade de PPS que o sistema precisaria suportar encaminhar teríamos: 1.000.000.000/8/1518 = 82.345 packets per second.
Existe um comando no GNU/Linux para você saber se o seu equipamento com processadores físicos, conseguirá trabalhar com o CPU Affinity:
# cat /sys/class/net/<interface>/device/numa_node
Se o resultado do comando acima for -1 então esse equipamento não trabalhará com o CPU Affinity. Isso porque cada interface precisa estar sendo gerenciada por um node específico. Se são 2 processadores então o resultado deveria ser 0 de CPU0 ou 1 de CPU1.
A seguir veremos um exemplo de datasheet da motherboard S2600WF:
Se observarmos o datasheet acima veremos que temos o PCIe Riser #1, Riser #2 e Riser #3. Cada Riser possui slots PCIe que são gerenciados por determinada CPU. Se colocássemos as duas interfaces de rede nos slots do Riser #2 e Riser #3, estaríamos pendurando tudo apenas no processador 2. Isso foi apenas para mostrar a complexidade de quando usamos um equipamento NUMA e estamos somente escolhendo o hardware adequado. Ainda não chegamos na configuração do CPU Affinity.
Para sabermos quais cores estão relacionados para uma determinada CPU, utilizamos os comandos abaixo:
# cat /sys/devices/system/node/node0/cpulist 0-7 # cat /sys/devices/system/node/node1/cpulist 8-15
No exemplo acima a CPU0 tem os cores de 0 a 7 e a CPU1, os cores de 8 a 15, ou seja, é um equipamento com 16 cores.
Tuning antes do CPU Affinity
Também é importante, para aumento de performance, que seja desabilitado na BIOS o HT (Hyper Threading).
Antes de configurarmos algumas coisas no nosso ambiente, precisaremos instalar uma ferramenta importante para o nosso tuning; vamos instalar o pacote ethtool. Ele servirá para fazermos alguns ajustes nas nossas interfaces de rede. Alguns fabricantes podem não permitir certas alterações mas com as interfaces da Intel sempre obtive os resultados esperados.
# apt install ethtool
No nosso exemplo acima vimos que o equipamento possui 16 cores sendo que 8 cores por CPU. Então, para esse caso, faremos um ajuste nas interfaces para ficarem preparadas para receberem 8 cores em cada através das IRQs. Usamos o parâmetro -l do ethtool para listar o Pre-set maximums combined da interface e o parâmetro -L para alterar esse valor. Façamos então a alteração:
# ethtool -L enp5s0f0 combined 8 # ethtool -L enp5s0f1 combined 8 # ethtool -L enp6s0f0 combined 8 # ethtool -L enp6s0f1 combined 8
Com os comandos acima deixamos preparadas as interfaces para aceitarem 8 cores em cada uma através das IRQs.
Não podemos usar o programa irqbalance para o CPU Affinity, pois este faz migração de contextos entre os cores e isso é ruim. Como no nosso exemplo estamos usando uma interface Intel, utilizaremos um script da própria Intel para realizar o CPU Affinity de forma mais fácil. Esse script se chama set_irq_affinity e vem acompanhado com os fontes do driver da interface. Ex.: Intel Network Adapter
Código do script set_irq_affinity
#!/bin/bash # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2015 - 2019, Intel Corporation # # Affinitize interrupts to cores # # typical usage is (as root): # set_irq_affinity -x local eth1 <eth2> <eth3> # set_irq_affinity -s eth1 # # to get help: # set_irq_affinity usage() { echo echo "Usage: option -s <interface> to show current settings only" echo "Usage: $0 [-x|-X] [all|local|remote [<node>]|one <core>|custom|<cores>] <interface> ..." echo " Options: " echo " -s Shows current affinity settings" echo " -x Configure XPS as well as smp_affinity" echo " -X Disable XPS but set smp_affinity" echo " [all] is the default value" echo " [remote [<node>]] can be followed by a specific node number" echo " Examples:" echo " $0 -s eth1 # Show settings on eth1" echo " $0 all eth1 eth2 # eth1 and eth2 to all cores" echo " $0 one 2 eth1 # eth1 to core 2 only" echo " $0 local eth1 # eth1 to local cores only" echo " $0 remote eth1 # eth1 to remote cores only" echo " $0 custom eth1 # prompt for eth1 interface" echo " $0 0-7,16-23 eth0 # eth1 to cores 0-7 and 16-23" echo exit 1 } usageX() { echo "options -x and -X cannot both be specified, pick one" exit 1 } if [ "$1" == "-x" ]; then XPS_ENA=1 shift fi if [ "$1" == "-s" ]; then SHOW=1 echo Show affinity settings shift fi if [ "$1" == "-X" ]; then if [ -n "$XPS_ENA" ]; then usageX fi XPS_DIS=2 shift fi if [ "$1" == -x ]; then usageX fi if [ -n "$XPS_ENA" ] && [ -n "$XPS_DIS" ]; then usageX fi if [ -z "$XPS_ENA" ]; then XPS_ENA=$XPS_DIS fi SED=`which sed` if [[ ! -x $SED ]]; then echo " $0: ERROR: sed not found in path, this script requires sed" exit 1 fi num='^[0-9]+$' # search helpers NOZEROCOMMA="s/^[0,]*//" # Vars AFF=$1 shift case "$AFF" in remote) [[ $1 =~ $num ]] && rnode=$1 && shift ;; one) [[ $1 =~ $num ]] && cnt=$1 && shift ;; all) ;; local) ;; custom) ;; [0-9]*) ;; -h|--help) usage ;; "") usage ;; *) IFACES=$AFF && AFF=all ;; # Backwards compat mode esac # append the interfaces listed to the string with spaces while [ "$#" -ne "0" ] ; do IFACES+=" $1" shift done # for now the user must specify interfaces if [ -z "$IFACES" ]; then usage exit 2 fi notfound() { echo $MYIFACE: not found exit 15 } # check the interfaces exist for MYIFACE in $IFACES; do grep -q $MYIFACE /proc/net/dev || notfound done # support functions build_mask() { VEC=$core if [ $VEC -ge 32 ] then MASK_FILL="" MASK_ZERO="00000000" let "IDX = $VEC / 32" for ((i=1; i<=$IDX;i++)) do MASK_FILL="${MASK_FILL},${MASK_ZERO}" done let "VEC -= 32 * $IDX" MASK_TMP=$((1<<$VEC)) MASK=$(printf "%X%s" $MASK_TMP $MASK_FILL) else MASK_TMP=$((1<<$VEC)) MASK=$(printf "%X" $MASK_TMP) fi } show_affinity() { # returns the MASK variable build_mask SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity` HINT=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/affinity_hint` printf "ACTUAL %s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I printf "HINT %s %d %s <- /proc/irq/$IRQ/affinity_hint\n" $IFACE $core $HINT IRQ_CHECK=`grep '[-,]' /proc/irq/$IRQ/smp_affinity_list` if [ ! -z $IRQ_CHECK ]; then printf " WARNING -- SMP_AFFINITY is assigned to multiple cores $IRQ_CHECK\n" fi if [ "$SMP_I" != "$HINT" ]; then printf " WARNING -- SMP_AFFINITY VALUE does not match AFFINITY_HINT \n" fi printf "NODE %s %d %s <- /proc/irq/$IRQ/node\n" $IFACE $core `cat /proc/irq/$IRQ/node` printf "LIST %s %d [%s] <- /proc/irq/$IRQ/smp_affinity_list\n" $IFACE $core `cat /proc/irq/$IRQ/smp_affinity_list` printf "XPS %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus` $IFACE $((n-1)) if [ -z `ls /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` ]; then echo "WARNING: xps rxqs not supported on $IFACE" else printf "XPSRXQs %s %d %s <- /sys/class/net/%s/queues/tx-%d/xps_rxqs\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_rxqs` $IFACE $((n-1)) fi printf "TX_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/tx_maxrate\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/tx_maxrate` $IFACE $((n-1)) printf "BQLIMIT %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit` $IFACE $((n-1)) printf "BQL_MAX %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_max\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_max` $IFACE $((n-1)) printf "BQL_MIN %s %d %s <- /sys/class/net/%s/queues/tx-%d/byte_queue_limits/limit_min\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/tx-$((n-1))/byte_queue_limits/limit_min` $IFACE $((n-1)) if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` ]; then echo "WARNING: aRFS is not supported on $IFACE" else printf "RPSFCNT %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_flow_cnt\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_flow_cnt` $IFACE $((n-1)) fi if [ -z `ls /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` ]; then echo "WARNING: rps_cpus is not available on $IFACE" else printf "RPSCPU %s %d %s <- /sys/class/net/%s/queues/rx-%d/rps_cpus\n" $IFACE $core `cat /sys/class/net/$IFACE/queues/rx-$((n-1))/rps_cpus` $IFACE $((n-1)) fi echo } set_affinity() { # returns the MASK variable build_mask printf "%s" $MASK > /proc/irq/$IRQ/smp_affinity printf "%s %d %s -> /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $MASK SMP_I=`sed -E "${NOZEROCOMMA}" /proc/irq/$IRQ/smp_affinity` if [ "$SMP_I" != "$MASK" ]; then printf " ACTUAL\t%s %d %s <- /proc/irq/$IRQ/smp_affinity\n" $IFACE $core $SMP_I printf " WARNING -- SMP_AFFINITY setting failed\n" fi case "$XPS_ENA" in 1) printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1)) printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus ;; 2) MASK=0 printf "%s %d %s -> /sys/class/net/%s/queues/tx-%d/xps_cpus\n" $IFACE $core $MASK $IFACE $((n-1)) printf "%s" $MASK > /sys/class/net/$IFACE/queues/tx-$((n-1))/xps_cpus ;; *) esac } # Allow usage of , or - # parse_range () { RANGE=${@//,/ } RANGE=${RANGE//-/..} LIST="" for r in $RANGE; do # eval lets us use vars in {#..#} range [[ $r =~ '..' ]] && r="$(eval echo {$r})" LIST+=" $r" done echo $LIST } # Affinitize interrupts # doaff() { CORES=$(parse_range $CORES) ncores=$(echo $CORES | wc -w) n=1 # this script only supports interrupt vectors in pairs, # modification would be required to support a single Tx or Rx queue # per interrupt vector queues="${IFACE}-.*TxRx" irqs=$(grep "$queues" /proc/interrupts | cut -f1 -d:) [ -z "$irqs" ] && irqs=$(grep $IFACE /proc/interrupts | cut -f1 -d:) [ -z "$irqs" ] && irqs=$(for i in `ls -1 /sys/class/net/${IFACE}/device/msi_irqs | sort -n` ;do grep -w $i: /proc/interrupts | egrep -v 'fdir|async|misc|ctrl' | cut -f 1 -d :; done) [ -z "$irqs" ] && echo "Error: Could not find interrupts for $IFACE" if [ "$SHOW" == "1" ] ; then echo "TYPE IFACE CORE MASK -> FILE" echo "============================" else echo "IFACE CORE MASK -> FILE" echo "=======================" fi for IRQ in $irqs; do [ "$n" -gt "$ncores" ] && n=1 j=1 # much faster than calling cut for each for i in $CORES; do [ $((j++)) -ge $n ] && break done core=$i if [ "$SHOW" == "1" ] ; then show_affinity else set_affinity fi ((n++)) done } # these next 2 lines would allow script to auto-determine interfaces #[ -z "$IFACES" ] && IFACES=$(ls /sys/class/net) #[ -z "$IFACES" ] && echo "Error: No interfaces up" && exit 1 # echo IFACES is $IFACES CORES=$(</sys/devices/system/cpu/online) [ "$CORES" ] || CORES=$(grep ^proc /proc/cpuinfo | cut -f2 -d:) # Core list for each node from sysfs node_dir=/sys/devices/system/node for i in $(ls -d $node_dir/node*); do i=${i/*node/} corelist[$i]=$(<$node_dir/node${i}/cpulist) done for IFACE in $IFACES; do # echo $IFACE being modified dev_dir=/sys/class/net/$IFACE/device [ -e $dev_dir/numa_node ] && node=$(<$dev_dir/numa_node) [ "$node" ] && [ "$node" -gt 0 ] || node=0 case "$AFF" in local) CORES=${corelist[$node]} ;; remote) [ "$rnode" ] || { [ $node -eq 0 ] && rnode=1 || rnode=0; } CORES=${corelist[$rnode]} ;; one) [ -n "$cnt" ] || cnt=0 CORES=$cnt ;; all) CORES=$CORES ;; custom) echo -n "Input cores for $IFACE (ex. 0-7,15-23): " read CORES ;; [0-9]*) CORES=$AFF ;; *) usage exit 1 ;; esac # call the worker function doaff done # check for irqbalance running IRQBALANCE_ON=`ps ax | grep -v grep | grep -q irqbalance; echo $?` if [ "$IRQBALANCE_ON" == "0" ] ; then echo " WARNING: irqbalance is running and will" echo " likely override this script's affinitization." echo " Please stop the irqbalance service and/or execute" echo " 'killall irqbalance'" exit 2 fi
CPU Affinity
Agora que preparamos as interfaces, façamos os apontamentos dos cores da seguinte forma. Vamos supor que colocamos o script em /root/scripts:
# /root/scripts/set_irq_affinity 0-7 enp5s0f0 # /root/scripts/set_irq_affinity 0-7 enp5s0f1 # /root/scripts/set_irq_affinity 8-15 enp6s0f0 # /root/scripts/set_irq_affinity 8-15 enp6s0f1
Mais alguns tunings
Vamos fazer mais alguns ajustes nas interfaces com o ethtool. Dessa vez vamos aumentar os Rings RX e TX. Mas antes vamos listar os valores que podemos usar:
# ethtool -g enp5s0f0 Ring parameters for enp5s0f0: Pre-set maximums: RX: 4096 RX Mini: n/a RX Jumbo: n/a TX: 4096 Current hardware settings: RX: 512 RX Mini: n/a RX Jumbo: n/a TX: 512
Acima vemos que o valor máximo é de 4096 tanto para TX, quanto para RX mas está configurado para 512 em RX e TX. Façamos então:
# ethtool -G enp5s0f0 rx 4096 tx 4096 # ethtool -G enp5s0f1 rx 4096 tx 4096 # ethtool -G enp6s0f0 rx 4096 tx 4096 # ethtool -G enp6s0f1 rx 4096 tx 4096
Vamos desabilitar as seguintes options das interfaces: TSO, GRO e GSO.
# ethtool -K enp5s0f0 tso off gro off gso off # ethtool -K enp5s0f1 tso off gro off gso off # ethtool -K enp6s0f0 tso off gro off gso off # ethtool -K enp6s0f1 tso off gro off gso off
Aumentaremos o txqueuelen para 10000:
# ip link set enp5s0f0 txqueuelen 10000 # ip link set enp5s0f1 txqueuelen 10000 # ip link set enp6s0f0 txqueuelen 10000 # ip link set enp6s0f1 txqueuelen 10000
Salvando a configuração e criando o LACP
Tudo que fizemos até o momento será perdido no próximo reboot do sistema, então faremos com que esses comandos sejam executados sempre que o sistema iniciar. Para isso vamos deixar o nosso arquivo /etc/network/interfaces configurado conforme nosso diagrama, usando LACP e executando nossos comandos anteriores.
Antes precisaremos instalar o pacote ifenslave para que o bonding funcione:
# apt install ifenslave # modprobe bonding # echo "bonding" >> /etc/modules
Abaixo o nosso /etc/network/interfaces já com todas as configurações que fizemos anteriormente e seguindo nosso diagrama de exemplo:
# This file describes the network interfaces available on your system # and how to activate them. For more information, see interfaces(5). source /etc/network/interfaces.d/* # The loopback network interface auto lo iface lo inet loopback auto bond0 iface bond0 inet static bond-slaves enp5s0f0 enp5s0f1 bond_mode 802.3ad bond-ad_select bandwidth bond_miimon 100 bond_downdelay 200 bond_updelay 200 bond-lacp-rate 1 bond-xmit-hash-policy layer2+3 address 10.0.10.172/24 gateway 10.0.10.1 pre-up /usr/sbin/ethtool -L enp5s0f0 combined 8 pre-up /usr/sbin/ethtool -L enp5s0f1 combined 8 pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f0 pre-up /root/scripts/set_irq_affinity 0-7 enp5s0f1 pre-up /usr/sbin/ethtool -G enp5s0f0 rx 4096 tx 4096 pre-up /usr/sbin/ethtool -G enp5s0f1 rx 4096 tx 4096 pre-up /usr/sbin/ethtool -K enp5s0f0 tso off gro off gso off pre-up /usr/sbin/ethtool -K enp5s0f1 tso off gro off gso off pre-up /usr/sbin/ip link set enp5s0f0 txqueuelen 10000 pre-up /usr/sbin/ip link set enp5s0f1 txqueuelen 10000 auto bond1 iface bond1 inet static bond-slaves enp6s0f0 enp6s0f1 bond_mode 802.3ad bond-ad_select bandwidth bond_miimon 100 bond_downdelay 200 bond_updelay 200 bond-lacp-rate 1 bond-xmit-hash-policy layer2+3 address 192.168.0.1/24 pre-up /usr/sbin/ethtool -L enp6s0f0 combined 8 pre-up /usr/sbin/ethtool -L enp6s0f1 combined 8 pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f0 pre-up /root/scripts/set_irq_affinity 8-15 enp6s0f1 pre-up /usr/sbin/ethtool -G enp6s0f0 rx 4096 tx 4096 pre-up /usr/sbin/ethtool -G enp6s0f1 rx 4096 tx 4096 pre-up /usr/sbin/ethtool -K enp6s0f0 tso off gro off gso off pre-up /usr/sbin/ethtool -K enp6s0f1 tso off gro off gso off pre-up /usr/sbin/ip link set enp6s0f0 txqueuelen 10000 pre-up /usr/sbin/ip link set enp6s0f1 txqueuelen 10000
Atualizando o Kernel
Colocaremos o kernel do backports. Para isso deixe o seu /etc/apt/sources conforme abaixo e rode os comandos na sequência:
deb http://security.debian.org/debian-security bullseye-security main contrib non-free deb http://deb.debian.org/debian bullseye main non-free contrib deb http://deb.debian.org/debian bullseye-updates main contrib non-free deb http://deb.debian.org/debian bullseye-backports main contrib non-free
# apt update # apt install -t bullseye-backports linux-image-amd64 # reboot
Protegendo contra static loop e preparando o ambiente do CGNAT
O static loop é algo que, definitivamente, pode derrubar toda a sua operação se não for devidamente tratado e pode ser facilmente explorado por pessoas mal intencionadas. A causa do problema é uma rota estática para um prefixo IP (seja IPv4 ou IPv6), que aponta para um next-hop e nesse destino não existe nenhuma informação sobre o prefixo IP na tabela de rotas local, obrigando o pacote a retornar para o seu gateway default e ficando nesse loop até que expire o TTL (Time To Live) do pacote. Isso ocorre muito nos casos em que temos concentradores PPPoE (BNG) e caixas CGNAT como esta que estaremos fazendo. Em Recomendações sobre Mitigação DDoS temos outras dicas de segurança sobre o assunto DDoS.
Crie um arquivo /etc/rc.local e dentro colocaremos algumas coisas como as blackholes para cada prefixo IPv4 público que usaremos no nosso servidor de exemplo e rotas de retorno para o nosso BNG:
# > /etc/rc.local # chmod +x /etc/rc.local
Dentro teremos:
#!/bin/sh -e /usr/sbin/ip route add blackhole 198.18.0.0/27 metric 254 /usr/sbin/ip route add 100.64.0.0/22 via 192.168.0.2
No exemplo acima estamos colocando em blackhole o nosso prefixo IPv4 público deste tutorial que é o 198.18.0.0/27 e adicionando uma rota de retorno do prefixo 100.64.0.0/22 usado no nosso BNG para o next-hop 192.168.0.2.