Enrutamiento rápido y NAT en Linux

A medida que las direcciones IPv4 se agotan, muchos operadores de telecomunicaciones se enfrentan a la necesidad de proporcionar a sus clientes acceso a la red mediante la traducción de direcciones. En este artículo, le diré cómo puede obtener un rendimiento NAT de nivel de operador en servidores básicos.

Un poco de historia

El tema del agotamiento del espacio de direcciones IPv4 ya no es nuevo. En algún momento, aparecieron listas de espera en RIPE, luego surgieron intercambios en los que se negociaron bloques de direcciones y se cerraron acuerdos para arrendarlos. Poco a poco, los operadores de telecomunicaciones comenzaron a brindar servicios de acceso a Internet mediante traducción de direcciones y puertos. Algunos no lograron obtener suficientes direcciones para emitir una dirección "blanca" a cada suscriptor, mientras que otros comenzaron a ahorrar dinero negándose a comprar direcciones en el mercado secundario. Los fabricantes de equipos de red apoyaron esta idea, porque Esta funcionalidad normalmente requiere licencias o módulos de extensión adicionales. Por ejemplo, en la línea de enrutadores MX de Juniper (excepto los últimos MX104 y MX204), puede realizar NAPT en una tarjeta de servicio MS-MIC separada, Cisco ASR1k requiere una licencia CGN, Cisco ASR9k requiere un módulo A9K-ISM-100 separado y una licencia A9K-CGN -LIC para él. En general, el placer cuesta mucho dinero.

IPTables

La tarea de realizar NAT no requiere recursos informáticos especializados, puede resolverse mediante procesadores de uso general que se instalan, por ejemplo, en cualquier enrutador doméstico. A escala de un operador de telecomunicaciones, este problema se puede resolver utilizando servidores básicos que ejecuten FreeBSD (ipfw/pf) o GNU/Linux (iptables). No consideraremos FreeBSD, porque... Dejé de usar este sistema operativo hace bastante tiempo, así que nos quedaremos con GNU/Linux.

Habilitar la traducción de direcciones no es nada difícil. Primero necesitas registrar una regla en iptables en la tabla nat:

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to <pool_start_addr>-<pool_end_addr> --persistent

El sistema operativo cargará el módulo nf_conntrack, que monitoreará todas las conexiones activas y realizará las conversiones necesarias. Hay varias sutilezas aquí. En primer lugar, dado que estamos hablando de NAT en la escala de un operador de telecomunicaciones, es necesario ajustar los tiempos de espera, porque con los valores predeterminados el tamaño de la tabla de traducción crecerá rápidamente hasta valores catastróficos. A continuación se muestra un ejemplo de la configuración que utilicé en mis servidores:

net.ipv4.ip_forward = 1
net.ipv4.ip_local_port_range = 8192 65535

net.netfilter.nf_conntrack_generic_timeout = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 600
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 45
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 60
net.netfilter.nf_conntrack_icmpv6_timeout = 30
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.netfilter.nf_conntrack_checksum=0

Y en segundo lugar, dado que el tamaño predeterminado de la tabla de traducción no está diseñado para funcionar en las condiciones de un operador de telecomunicaciones, es necesario aumentarlo:

net.netfilter.nf_conntrack_max = 3145728

También es necesario aumentar la cantidad de depósitos para la tabla hash que almacena todas las transmisiones (esta es una opción en el módulo nf_conntrack):

options nf_conntrack hashsize=1572864

Después de estas simples manipulaciones, se obtiene un diseño completamente funcional que puede traducir una gran cantidad de direcciones de clientes en un grupo de direcciones externas. Sin embargo, el rendimiento de esta solución deja mucho que desear. En mis primeros intentos de usar GNU/Linux para NAT (alrededor de 2013), pude obtener un rendimiento de alrededor de 7 Gbit/s a 0.8 Mpps por servidor (Xeon E5-1650v2). Desde entonces, se han realizado muchas optimizaciones diferentes en la pila de red del kernel GNU/Linux, el rendimiento de un servidor en el mismo hardware ha aumentado a casi 18-19 Gbit/s a 1.8-1.9 Mpps (estos eran los valores máximos) , pero la demanda de volumen de tráfico procesado por un servidor creció mucho más rápido. Como resultado, se desarrollaron esquemas para equilibrar la carga en diferentes servidores, pero todo esto aumentó la complejidad de configurar, mantener y mantener la calidad de los servicios prestados.

NFTables

Hoy en día, una tendencia de moda en el software de "cambio de bolsas" es el uso de DPDK y XDP. Se han escrito muchos artículos sobre este tema, se han pronunciado muchos discursos diferentes y están apareciendo productos comerciales (por ejemplo, SKAT de VasExperts). Pero teniendo en cuenta los limitados recursos de programación de los operadores de telecomunicaciones, es bastante problemático crear por su cuenta cualquier "producto" basado en estos marcos. En el futuro será mucho más difícil utilizar una solución de este tipo; en particular, será necesario desarrollar herramientas de diagnóstico. Por ejemplo, el tcpdump estándar con DPDK no funcionará así y no "verá" los paquetes enviados de vuelta a los cables mediante XDP. En medio de toda la charla sobre nuevas tecnologías para enviar paquetes al espacio del usuario, pasaron desapercibidas. informes и Artículo Pablo Neira Ayuso, mantenedor de iptables, sobre el desarrollo de la descarga de flujo en nftables. Echemos un vistazo más de cerca a este mecanismo.

La idea principal es que si el enrutador pasó paquetes de una sesión en ambas direcciones del flujo (la sesión TCP pasó al estado ESTABLECIDO), entonces no es necesario pasar los paquetes posteriores de esta sesión a través de todas las reglas del firewall, porque Todas estas comprobaciones finalizarán cuando el paquete se transfiera más al enrutamiento. Y en realidad no necesitamos seleccionar una ruta: ya sabemos a qué interfaz y a qué host debemos enviar paquetes dentro de esta sesión. Todo lo que queda es almacenar esta información y utilizarla para enrutar en una etapa temprana del procesamiento de paquetes. Al realizar NAT, es necesario almacenar adicionalmente información sobre cambios en direcciones y puertos traducidos por el módulo nf_conntrack. Sí, por supuesto, en este caso varias políticas y otras reglas de información y estadísticas en iptables dejan de funcionar, pero en el marco de la tarea de un NAT permanente separado o, por ejemplo, una frontera, esto no es tan importante, porque los servicios se distribuyen entre dispositivos.

Configuración

Para utilizar esta función necesitamos:

  • Utilice un grano fresco. A pesar de que la funcionalidad en sí apareció en el kernel 4.16, durante bastante tiempo estuvo muy "cruda" y regularmente causaba pánico en el kernel. Todo se estabilizó alrededor de diciembre de 2019, cuando se lanzaron los kernels LTS 4.19.90 y 5.4.5.
  • Reescriba las reglas de iptables en formato nftables usando una versión bastante reciente de nftables. Funciona exactamente en la versión 0.9.0

Si en principio todo está claro con el primer punto, lo principal es no olvidar incluir el módulo en la configuración durante el montaje (CONFIG_NFT_FLOW_OFFLOAD=m), entonces el segundo punto requiere explicación. Las reglas de nftables se describen de forma completamente diferente a las de iptables. Документация revela casi todos los puntos, también hay especiales convertidores reglas de iptables a nftables. Por lo tanto, solo daré un ejemplo de configuración de NAT y descarga de flujo. Una pequeña leyenda por ejemplo: , - estas son las interfaces de red a través de las cuales pasa el tráfico; en realidad, puede haber más de dos. , — la dirección inicial y final del rango de direcciones "blancas".

La configuración de NAT es muy sencilla:

#! /usr/sbin/nft -f

table nat {
        chain postrouting {
                type nat hook postrouting priority 100;
                oif <o_if> snat to <pool_addr_start>-<pool_addr_end> persistent
        }
}

Con la descarga de flujo es un poco más complicado, pero bastante comprensible:

#! /usr/sbin/nft -f

table inet filter {
        flowtable fastnat {
                hook ingress priority 0
                devices = { <i_if>, <o_if> }
        }

        chain forward {
                type filter hook forward priority 0; policy accept;
                ip protocol { tcp , udp } flow offload @fastnat;
        }
}

Ésa, de hecho, es toda la configuración. Ahora todo el tráfico TCP/UDP caerá en la tabla fastnat y se procesará mucho más rápido.

resultados

Para dejar claro lo “mucho más rápido” que es esto, adjuntaré una captura de pantalla de la carga en dos servidores reales, con el mismo hardware (Xeon E5-1650v2), idénticamente configurados, usando el mismo kernel de Linux, pero realizando NAT en iptables. (NAT4) y en nftables (NAT5).

Enrutamiento rápido y NAT en Linux

No hay gráfica de paquetes por segundo en la captura, pero en el perfil de carga de estos servidores el tamaño medio de paquete ronda los 800 bytes, por lo que los valores llegan hasta los 1.5Mpps. Como puede ver, el servidor con nftables tiene una enorme reserva de rendimiento. Actualmente, este servidor procesa hasta 30 Gbit/s a 3 Mpps y es claramente capaz de cumplir con la limitación de la red física de 40 Gbps, al tiempo que tiene recursos de CPU libres.

Espero que este material sea útil para los ingenieros de redes que intentan mejorar el rendimiento de sus servidores.

Fuente: habr.com

Añadir un comentario