Dos problemas típicos de Linux Netfilter / NAT

Un día de la semana pasada, de camino a casa desde el trabajo, ¿cómo fue ser despreciado por un grupo de novatos y peces gordos de Internet en un grupo de tecnología de tercera categoría? ¿Solo porque estoy hablando de Netfilter y no de eBPF, XDP, DPDK? Bueno, debo decirlo bien.

Hace diez años y más, esa fue la edad de oro de Netfilter. Casi cualquier función relacionada con la red se puede implementar en Netfilter. En ese momento, las personas que sabían que Netfilter eran definitivamente los grandes nombres en el campo de las redes Linux, pero cuando ingresaron a la era de Internet móvil Impulsados ​​por el trasfondo de un gran tráfico y una gran concurrencia, los gigantes de Internet han hecho que la pila de protocolos del kernel de Linux parezca incapaz, por lo que varios métodos de optimización han aparecido, casi revolucionando la pila de protocolos del kernel.

La gente mueve la lógica de reenvío de la pila de protocolos al modo de usuario, y la gente pasa por alto en la parte inferior de la tarjeta de red, pero para ser honesto, hasta 2014, todavía había muy pocas personas que se especializaran en reenvío.

Hay mucha gente y un gran poder. Cuando las necesidades de los gigantes de Internet en esta área se hacen cada vez más fuertes, en un entorno involuntario como China, todos están pululando en este campo específico. Después de 2016, si su currículum está en Sin escribir algunas optimizaciones en el enrutamiento y el reenvío, el currículum básicamente no es aceptable En un instante, casi todo el mundo se convirtió en un experto en omisión de pila de protocolos del kernel.

Desafortunadamente, seré muchos "expertos" en esta área , ¡la mayoría de ellos son basura! Muchos de ellos ni siquiera entienden lo básico, pero XDP y DPDK son muy fáciles de jugar, lo que hace que la gente se sienta un poco injusta, pero aprovecharon la oportunidad y el viento, y comencé a bendecirlos.

No solo el bypass de la pila de protocolos del kernel, sino también los principales fabricantes de hardware están llegando. Hoy en día, las ideas de SmartNIC han estado volando por todo el cielo. Dije en broma que la Raspberry Pi se simula como una tarjeta de red inteligente, y el cable USB realiza la interfaz norte-sur. Todos pueden jugar SmartNIC. . No hay muchas bromas al respecto.

...

Parece una divagación. Daré más detalles sobre lo anterior cuando tenga tiempo, pero no ahora. Ahora quiero hablar sobre algunas cosas específicas.

Admito que ahora es la era de eBPF, XDP, DPDK, y quiero hablar del antiguo Netfilter, que está muy desactualizado.

Netfilter representa la vejez, en conferencias altas, foros de alto nivel o incluso algunos grupos WeChat de tercera categoría, no se puede decir Netfilter, la gente incluso se molesta en rociarlo, Netfilter representa complejidad, atraso e ineficiencia. No emprendedor. En 2021, no es correcto hablar de Netfilter, pero soy un representante de la era antigua. Aunque admito que los productos de la nueva era son más simples y eficientes, todavía quiero hablar de Netfilter si no hay nada que hacer.

En los últimos dos años, creo que la persistencia es más importante que la explicación. En primer lugar, admito que la tecnología que he dominado está desactualizada. En segundo lugar, estoy aprendiendo constantemente nuevas tecnologías. Finalmente, debo enfatizar que incluso si Netfilter está desactualizado, sigue siendo muy poderoso.

No quiero seguir explicando y difundiendo la tecnología de Netfilter como una vieja escuela. Solo quiero ser como un arqueólogo de moda y seguir investigando más sobre las cosas detrás de Netfilter. Solo espero que estas cosas puedan guiar los productos de la nueva era para que no se conviertan en Al menos retrasará convertirse en el próximo Netfilter.

Hoy hablaré principalmente sobre dos problemas con Netfilter / NAT.

Registro retrasado de nf_conntrack HOOK

Todo el mundo sabe que nf_conntrack no es bueno, sí, es complicado, afecta seriamente el rendimiento de la máquina autónoma, ha sido criticado. Ha habido quejas y, por supuesto, debo haberme quejado. Mientras el módulo del kernel NAT esté cargado, el mecanismo nf_conntrack del que depende estará habilitado. Como resultado, se rastrea todo el tráfico, incluso si no agrega una regla NAT Lo mismo es cierto, a menos que agregue explícitamente una regla NOTRACK, lo que desafortunadamente aumenta la complejidad de la operación.

Los administradores generales de operación y mantenimiento no comprenden los principios internos de nf_conntrack, en su opinión, se trata de una caja negra que proporciona funciones específicas. Pero si no comprende los principios internos de esto, no podrá comprender dónde está el cuello de botella en el rendimiento y será muy complicado cuando algo salga mal. La pregunta es, ¿es apropiado habilitar nf_conntrack sin ningún motivo? Sé que NAT depende de nf_conntrack, pero claramente no tengo una NAT configurada.

Para resolver este problema, el kernel después de 4.10 cambió al registro retrasado de nf_conntrack:

  • Cuando agregue su primera regla NAT, se registrará el GANCHO de nf_conntrack.

Cuando agregue la primera regla NAT, se llamará a xt_nat_checkentry y todo lo que tiene que hacer es retrasar el registro de nf_conntrack HOOK.

Bueno, el problema parece estar resuelto. Cuando no se agregan reglas NAT, nf_conntrack no está habilitado de hecho, pero el nuevo problema confunde a la gente:

  • Cuando se agrega una regla que no es NAT (no SNAT, DNAT, MASQURADE) cuando no hay una regla NAT en la tabla nat, ¿por qué no entra en vigor?

Por ejemplo, agregar las siguientes reglas sin ninguna regla NAT:

iptables -t nat -A OUTPUT -j MARK --set-mark 123

Por supuesto, sé que solo el primer paquete de la secuencia coincidirá con esta regla El problema es que incluso el primer paquete de la secuencia no se marcará con la marca 123. Cuando use iptables -t nat -L -v para ver el recuento de tráfico, encontrará que ningún primer paquete de flujo originado localmente coincidirá con esta regla.

Si comprende el retraso en el registro de nf_conntrack, ciertamente conoce la respuesta. Dado que no hay una regla NAT, nf_conntrack no se ha registrado, por lo que cuando el primer paquete del flujo ingresa a la función de procesamiento HOOK de NAT, su conntrack dependiente está vacío, por lo que no coincidirá más con la regla en la tabla nat, marque Por supuesto que no estará marcado. No es de extrañar que otros, que le dijeron que no hiciera NAT en NAT pero que hicieran otra cosa, si no me creen, intenten hacer DROP en NAT.

Este no es todo el problema, el problema es que para la mayoría de las personas, podría pensar con qué frecuencia se aplicará esta marca -set-mark, pero en ocasiones no se hará efectivo. Para la mayoría de los administradores de operaciones y mantenimiento, no puede esperar que comprendan lo que sucederá si realiza acciones que no sean NAT en la tabla nat, y mucho menos que comprendan lo que sucede detrás de todo esto.

Finalmente, una pregunta metafísica es, ¿es realmente bueno retrasar el registro de nf_conntrack? ¿Soluciona muchos problemas o causa muchos problemas? Creo que es el último.

El significado del registro retrasado de nf_conntrack es solo para retrasar ligeramente el impacto de nf_conntrack en todo el sistema. Siempre que agregue una regla NAT, afectará inmediatamente a todo el mundo. Al mismo tiempo, el registro retrasado es un comportamiento cuando las personas quieren hacer algo más en la tabla NAT. La gente está preocupada. Como operación equivalente, bajo la premisa de que el módulo nf_conntrack está cargado y registrado, ¿por qué no dejar el problema al operador, es decir, cargar el módulo NAT inmediatamente antes de agregar las reglas NAT? Retrasar la carga del módulo NAT es más directo que retrasar el registro de nf_coonntrack, por no mencionar ¿Cuál es el punto de cargar el módulo NAT sin configurar reglas NAT?

El problema de iptables y nftables haciendo NAT al mismo tiempo

El mecanismo NAT del kernel de Linux siempre ha sido utilizado solo por iptables, y su lógica de procesamiento no es problemática. La lógica de operación de NAT es la siguiente:

  • Solo el primer paquete del flujo que aún no ha completado la coincidencia de NAT coincidirá con el conjunto de reglas de NAT.
  • Después de que se hace coincidir cualquier conjunto de reglas, independientemente de si la regla coincide o no, la coincidencia de NAT está configurada para completarse.

Para el primer paquete del flujo, después de atravesar el conjunto de reglas NAT, con el fin de garantizar la unicidad global del quíntuple, sin importar si la regla NAT se alcanza o no, todos los quíntuples conectados deben incluirse en el mismo espacio de nombres global, razón por la cual Incluso si el flujo que no cumple con la regla NAT realiza una vinculación de asignación nula, en la operación de vinculación de asignación nula, si es necesario garantizar la unicidad de la 5-tupla, el puerto de origen del flujo aún se puede convertir. Al final de la operación, se establecerá Señal completa de NAT. Este es un detalle importante de la lógica NAT.

Ha estado bien, no ha habido ningún problema, pero después de nftables, las cosas han cambiado. Nftables tiene su propia lógica de coincidencia de reglas, pero comparte un conjunto de infraestructura con iptables. De acuerdo con los dos principios anteriores, iptables y nftables no pueden coexistir, porque no importa qué conjunto de reglas coincida primero el paquete, se establecerá la coincidencia de NAT Completado, esto hará que otro conjunto de reglas deje de coincidir.

Inicialmente quería analizar este caso. Afortunadamente, este problema se ha solucionado. El parche para solucionarlo es el siguiente:
https://lore.kernel.org/netfilter-devel/[email protected]/

El núcleo es actualizar los dos principios anteriores:

  • Solo el primer paquete del flujo que aún no ha completado la coincidencia de NAT coincidirá con el conjunto de reglas de NAT.
  • Una vez que todos los conjuntos de reglas coinciden, independientemente de si la regla coincide o no, la coincidencia de NAT está configurada para completarse.

El punto clave es el siguiente fragmento de código:

 		if (!nf_nat_initialized(ct, maniptype)) {
    
    
+			struct nf_nat_lookup_hook_priv *lpriv = priv;
+			struct nf_hook_entries *e = rcu_dereference(lpriv->entries);
 			unsigned int ret;
-
-			ret = do_chain(priv, skb, state); // 这里是个回调函数,要么是iptables的,要么是nftables的,或者你自己写的
-			if (ret != NF_ACCEPT)
-				return ret;
-
-			if (nf_nat_initialized(ct, HOOK2MANIP(state->hook)))
-				break;
-
+			int i;
+
+			if (!e)
+				goto null_bind;
+
+			for (i = 0; i < e->num_hook_entries; i++) {
    
     // 更新成了在一个循环中完成所有规则集的匹配。
+				ret = e->hooks[i].hook(e->hooks[i].priv, skb,
+						       state);
+				if (ret != NF_ACCEPT)
+					return ret;
+				if (nf_nat_initialized(ct, maniptype))
+					goto do_nat;
+			}
+null_bind:
 			ret = nf_nat_alloc_null_binding(ct, state->hook);
 			if (ret != NF_ACCEPT)
 				return ret;

Este tipo de problema es difícil de solucionar, pero si está familiarizado con herramientas de rastreo como systemtap, es mucho más fácil de manejar. El siguiente script puede enumerar los ganchos registrados en un punto de gancho de Netfilter:

#!/usr/bin/stap

global hook

probe kernel.function("nf_hook_slow")
{
    
    
	if ($state->hook != hook)
		next;
	num = $e->num_hook_entries;
	for (i = 0; i < num; i++) {
    
    
		hfn = @cast(&$e->hooks[i], "struct nf_hook_entry")->hook;
		s1 = modname(hfn);
		if (s1 == "kernel") {
    
    
			s1 = symname(hfn);
			printf("%s at [kernel]\n", s1);
		} else {
    
    
			printf("%p at [%s]\n", hfn, s1);
		}
	}
	exit();
}

probe begin {
    
    
	hook = $1;
}

Usé esto para encontrar el módulo nftables y la pelea de iptables.

Aunque el problema de iptables y nftables no pueden coexistir está resuelto, ¿habrá otros pozos en el futuro? desconocido. En términos de diseño, en la función HOOK de NAT, do_chain se pasa como una devolución de llamada, lo cual no es correcto en sí mismo. Esto parece indicarle al implementador del nuevo conjunto de reglas que solo necesita proporcionar una función de devolución de llamada diferente. Sin embargo, la verdad es Sí, solo iptables usará la suposición errónea de NAT. Desde el principio, el implementador de NAT HOOK se vio obligado a escribir la lógica de "solo se puede hacer coincidir un conjunto de reglas" .

¡Cuántos problemas de este tipo quedan en el kernel, demasiados! Por ejemplo, la implementación del protocolo TCP es otro ejemplo. Sin embargo, no estoy aquí para expresar una queja negativa y despectiva. Por el contrario, creo que esta es la raíz de la evolución del sistema, al igual que los organismos unicelulares nunca han imaginado cómo será un ser humano. Ni siquiera tienen la capacidad de imaginar. Pero es a través de la constante reestructuración de nosotros mismos en la dolorosa abnegación de millones de años que lo hemos logrado nosotros mismos y la ecología que nos rodea.Hasta el día de hoy, el cuerpo humano todavía tiene muchos defectos indiscutibles.

El almuerzo llegará pronto, gracias al loco, gracias a Xiaoxiao y al Sr. Anderson por darme esta mañana para escribir estas cosas desordenadas.


Los zapatos de cuero en Wenzhou, Zhejiang están mojados, por lo que no engordan con la lluvia.

Supongo que te gusta

Origin blog.csdn.net/dog250/article/details/112058744
Recomendado
Clasificación