Los artesanos renuncian a bpftrace y toman el precio y piensan en systemtap

Quería rociar eBPF la semana pasada. Como el tiempo del fin de semana era demasiado ajustado, iba a posponerlo por una semana, pero aún así coloqué una bandera y publiqué un círculo de amigos:

ebpf es como un musgo de piel de vaca. Se ha extendido por todo el kernel de Linux. Cada punto de llamada parece muy aleatorio y no tiene ningún plan. Hace que la gente sienta que no es difícil necesitar un punto de llamada de este tipo ...

pero de hecho Si realmente intenta agregar un punto de llamada ebpf en algún lugar, sentirá que esto es muy similar al proceso de limpiar el musgo. Modifique varios archivos dispersos en varios directorios y tendrá que volver a compilar, lo que probablemente fallará. Tienes que hacerlo de nuevo. Es difícil hacerlo todo a la vez. Cuando finalmente lo

logras, se oirá un suspiro de "pero bueno " ... Una vez comparé ebpf con una célula cancerosa en expansión. Esta metáfora no da a las personas el efecto de un miedo intenso. Entonces cambié a la psoriasis. No hay ningún lugar donde debería haber puntos de llamada ebpf, y no hay necesidad de ebpf en todas partes. Estos puntos siguen aumentando. A partir del kernel 5.11, ebpf tiene 30 puntos y sigue creciendo de forma imprevista. ...

EBPF es una innovación, pero la gente obviamente está demasiado entusiasmada. Ebpf agrega puntos de llamada de manera demasiado informal, demasiado orientada a los negocios y daña la cohesión del kernel. Es muy inferior a los cinco puntos de enlace de netfilter y la experiencia de qdisc. Mecanismo bien diseñado, hay otro problema. Si los puntos ebpf se implementan en los cinco puntos de enlace de netfilter, la mayoría de los problemas de rendimiento se pueden resolver. Sin embargo, hasta ahora, no ha habido ninguno. Se siente que la comunidad ha corregido en exceso y realmente eliminado por completo el netfilter. Como símbolo de la vejez, cuando el caballo muere, las ruedas también se van.

Aquí la gente pelea como bichos. Todo lo que comen es conciencia y todo lo que tiran son pensamientos.

Finalmente llegó el fin de semana, y finalmente no pude hablar, ya ni siquiera sabía qué decir.

Ayer me sacrifiqué con sangre, este tipo de autosacrificio era una virtud en la Antigua República Romana, el cónsul se sacrificaba a Dios a cambio de la victoria en la guerra.

Para rociar eBPF, en mi trabajo y estudio habituales, he acumulado una gran cantidad de materiales.El territorio de eBPF se divide en dos partes:

  • Función de pila de protocolos de red
  • rastro

En términos de Internet, utilizo la metáfora de la psoriasis para ilustrar lo malo que es el panorama en constante crecimiento de eBPF. En términos de seguimiento de trazas, quiero hablar sobre rendimiento y funcionalidad.

Tenía la esperanza de usar estos materiales para respaldar algunas de mis opiniones extrañas. Ayer, iba a usar mi trabajo real como mi último material. Como resultado, solo verificó mi error cognitivo y solo mostró que eBPF se usa como un rastro. ¡Qué fácil es la herramienta!

Mi historia es así.

En el contexto de mucho tráfico, especialmente si su código utiliza tarjetas de red virtuales como Bonding, tun / tap, GRE, IPIP, etc., la resolución de problemas cuando se suelta skb es siempre una tarea muy problemática. Incluso si ya conoce una tupla específica de cinco, este asunto no será fácil.

¿Capturar? La captura de paquetes es siempre el primer paso, pero es solo el primer paso. Solo puede decirle si el skb se ha recibido o no. De lo contrario, debe confirmar dónde se perdió el skb. Por supuesto, si al final es imposible localizarlo, generalmente arrojará la olla al agujero negro del operador.
⁣Al igual que
con wireguard, todas las operaciones complejas, incluido el cifrado, la distribución y otras lógicas, se completan en la función xmit de la tarjeta de red virtual wireguard. Los detalles del seguimiento de wg_xmit requieren que esté muy familiarizado con el código de wireguard, pero también Tipo de artesanía extraña.
⁣ Tome
el siguiente escenario como ejemplo:
Inserte la descripción de la imagen aquí

¿Qué debo hacer si skb se redujo de 1 a 11?
⁣¿No es
este el sitio de stap? Oh, no puedo decir stap. Cuando bpftrace está disponible, stap es un poco incorrecto. Si insisto en usar stap, un grupo de personas me sugerirán que use bpftrace y dirán qué tan malo es stap, desactualizado, no amistoso.

Bueno, use bpftrace. El siguiente script puede realizar un seguimiento perfecto de skb:

#!/usr/local/bin/bpftrace
⁣
#include <linux/skbuff.h>
#include <linux/udp.h>
#include <net/sock.h>
⁣
k:encrypt_packet
{
    
    
        $skb = (struct sk_buff *)arg0;
        // 这个skb的mark需要iptables来为特定的五元组标记上,但是encrypt_packet这里是可以使用mark的最后的地方。
        // 在encrypt_packet成功返回后,skb的几乎所有附属标记都会被reset,包括skb->mark。
        // 因此这里必须用另一个标记,以确保在encrypt_packet之后还能用此特征跟踪到特定的skb。
        // 由于bpftrace只能读不能写,这里我选择直接用skb的地址!
        if ($skb->mark == 1234) {
    
    
                printf("encrypt got %p\n", $skb);
                @addr = $skb;
        }
}//k:send4
k:udp_tunnel_xmit_skb
{
    
    
        $daddr = arg4;
        $saddr = arg3;
        $skb = (struct sk_buff *)arg2;
        // 这里除了match地址之外,是不是也要match一下其它字段呢?毕竟slub中的skb是可以重用的。
        // 如果mark 1234的skb在这个之前被drop & free了,它被重新alloc后依然会到这里,这就错了!
        // 然而由于流量可控,且我是一个函数一个函数trace,上述概率极低。手艺人不求完美!
        if ($skb == @addr) {
    
    
                printf("---- skb:%p daddr:%08x saddr:%08x \n", @addr, $daddr, $saddr);
        }
}
⁣
k:iptunnel_xmit
//k:dev_queue_xmit
//k:dev_hard_start_xmit
//k:dev_queue_xmit_nit
{
    
    
        $skb = (struct sk_buff *)arg2;
        // 从裸包中取外层协议头的内容。
        $udph = (struct udphdr *)($skb->head + $skb->transport_header);
        $sport = $udph->source;
        $dport = $udph->dest;
        if ($skb == @addr) {
    
    
                $port = (($sport & 0xff00) >>8) | (($sport & 0xff) << 8);
                $port2 = (($dport & 0xff00) >>8) | (($dport & 0xff) << 8);
                printf("sport:%d  dport:%d\n", $port, $port2);
                // trace结束,重置全局变量。
                @addr = (struct sk_buff *)0;
        }
}

Ajá, creo que este es un script muy fluido. Marca skb antes de ingresar a wg_xmit, guarda su dirección antes de borrar la marca skb durante wg_xmit, y luego rastrea el skb de la dirección. Sin embargo, lo triste es que el skb se envió con éxito y no encontré nada. Sin embargo, lo triste es que el paquete interno no fue capturado en la tarjeta de red wg del wireguard opuesto.
⁣Vaya
al extremo opuesto y hágalo de nuevo y ¿está bien? Pensar es una cosa, aterrizar es otra.
⁣¿Cómo
podemos seguir trazando este skb en el extremo opuesto?

Si no hay forma de rastrear este skb, ¿cómo se distingue si el dispositivo de red intermedio deja caer el mensaje o el dispositivo de protección del mismo nivel? Dado que el extremo emisor ya puede obtener los quintuplos específicos de las capas interna y externa, por supuesto, es una idea correcta utilizar el quíntuplo externo para que coincida con el encabezado del protocolo externo en el extremo receptor. El problema es que si el quíntuplo del túnel externo es Gran reutilización de tráfico, ¿cómo igualará la tupla interna de cinco antes del descifrado de skb? El tráfico es demasiado grande, al igual que muchas capturas de paquetes no se pueden llevar a cabo debido al gran tráfico, la información que desea se verá casi instantáneamente abrumada
⁣Lo
que quiero saber es cómo hace esto bpftrace. Si este problema no se puede resolver rápida y fácilmente, tengo buenas razones para usar cosas viejas de la vejez.
⁣¡El
problema es que bpftrace no me permite modificar skb! Ahora, decidí deshacerme de bpftrace y usar stap para hacer lo correcto.
⁣Todo lo
que necesito hacer es poner una marca en un paquete específico, y la marca debe ser identificable en el extremo opuesto. Decidí usar el campo TTL inofensivo del encabezado IP. Es muy simple usar stap para lograr esto. Por cierto, también usaré stap para marcar skb, así que también eliminé la regla iptables:

#!/usr/local/bin/stap -g

%{
    
    
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/addrconf.h>

struct sk_buff *tmp = NULL;
%}

function getinfo(pskb:long)
%{
    
    
        struct sk_buff *skb = (struct sk_buff *)STAP_ARG_pskb;
        struct iphdr *hdr = ip_hdr(skb);
        const struct skb_shared_info *shinfo = skb_shinfo(skb);
        struct dst_entry *dst = skb_dst(skb);


        if (dst && !strcmp("wg0", dst->dev->name)) {
    
    
                struct tcphdr *tp = (struct tcphdr *)((char *)hdr + sizeof(struct iphdr));
                if (ntohs(tp->source) == 443 && ntohs(tp->dest) == 3663) {
    
    
                        STAP_PRINTF("sport:%d dport:%d addr:%p\n", ntohs(tp->source), ntohs(tp->dest), skb);
                        skb->mark = 1234;
                        tmp = skb;
                }
        }
%}

probe kernel.function("ip_forward")
{
    
    
        getinfo($skb)
}

function setttl(pskb:long)
%{
    
    
        struct sk_buff *skb = (struct sk_buff *)STAP_ARG_pskb;
        struct iphdr *hdr;

        if (skb == NULL)
                return;

        hdr = ip_hdr(skb);
        if (skb == tmp) {
    
    
                hdr->ttl = 120;
                tmp = NULL;
        }

%}

// 这个是在确认了skb顺利通过了wg server后,为了在wg client端跟踪所使用的。
// 由于数据包没有什么好的字段可利用来打标,就随手选了ttl,作为一个特殊值,不一定是120,180更好。。。
probe kernel.function("ip_local_out")
{
    
    
        setttl($skb)
}

Para un script de este tipo, es suficiente hacer coincidir directamente el quintuple externo con el valor TTL en el extremo opuesto. Solo necesitamos hacer coincidir el skb con el valor TTL mayor que 70 mientras que el quintuple externo coincide. bpftrace solo puede leer pero no escribir. Para que esto sea posible, solo puedo usar stap.
⁣Lo
anterior es mi actitud. No es que no acepto bpftrace, y no acepto cosas nuevas. ¡Solo quiero decir que no puedo vencer a las cosas viejas hasta la muerte mientras acepto las nuevas! No elija bpftrace debido a la popularidad de eBPF y tire stap a la basura.



Me estoy preparando para vomitar, pero me estoy preparando para rociar bpftrace cuando no puedo hacerlo, ¡mi sacrificio de sangre!

Obviamente puedo usar bpftrace para rastrear este skb específico en el extremo receptor de datos de Wireguard como el extremo de envío de datos. Después de todo, solo quiero saber dónde se colocó, lo cual es completamente innecesario para escribir. Sin embargo, para adoptar una actitud de fingir estilo, tengo que usar stap para realizar esta tarea y renunciar a bpftrace.
⁣Aunque
creo que soy un hábil trabajador de stap, casi siempre uso el modo -g guru, no porque tenga confianza, sino porque no conozco la sintaxis de stap. Casi sé C y ensamblador, casi siempre no puedo recordar la sintaxis de ningún otro lenguaje, incluido Bash ...
⁣Antes de
usar stap para rastrear la función del kernel o módulo, siempre miro el análisis de sus parámetros. :

stap -L 'module("wireguard").function("wg_allowedips_lookup_src")'

Desafortunadamente, no se pueden usar parámetros por razones desconocidas. Entonces, cuando quiero usar sus parámetros, solo puedo tomar los registros desnudos, como el siguiente:

...
probe module("wireguard").function("wg_allowedips_lookup_src")
{
    
    
        // 由于stap -L无法解析参数,只能用x86_64的调用规则直接取寄存器
        if (cmpskb(register("rsi"))) {
    
    
                a = 1;
        }
}

probe module("wireguard").function("wg_allowedips_lookup_src").return
{
    
    
        if (a == 1) {
    
    
                printf("peer returned::%p\n", register("rax"));
                a = 0;
        }
}...

Dado que se usa stap, ¿por qué no facilitar las cosas? Entonces comencé a escribir con confianza. Cambie la memoria de skb y modifique manualmente los datos de skb, esperando evitar muchos procesos innecesarios.


⁣Después de
jugar de esta manera durante unas horas, probablemente me sentí como si estuviera conduciendo el avión para reparar el motor. Estaba un poco cansado. En un momento preciso, ¡un choque o un bloqueo suave completó el sacrificio de sangre perfectamente!

Este es un trabajo en línea y obviamente el bote es mío.
"
Esto no significa que mi destreza no sea excelente. Antes de este tipo de aterrizaje brusco, he estado volando durante algunas horas después de todo, pero esto muestra un problema. Bpftrace es mejor que stap, al menos seguro". Este es precisamente el punto que quiero refutar, pero lo he demostrado.

En términos de estabilidad, dos de los rechazos de eBPF son suficientes:

  • eBPF no le permite escribir ningún código potencialmente riesgoso.
  • eBPF no le permite escribir ningún código complicado.

En stap, puede usar un while (verdadero) para bloquear el sistema, pero no en bpftrace.

...

Cuando trato con problemas similares, en realidad soy parcial. La razón por la que no me gusta usar herramientas, especialmente las herramientas nuevas, es más porque soy vago y no me gusta enfrentar y controlar mucha irrelevancia. Por ejemplo, sabía que había un dropwatch pero no lo usé. Fue porque era demasiado complicado y tenía que entender la complicada línea de comandos. En lugar de esto, debería simplemente stap probe kfree_skb y dump_stack directamente.

Al principio también me resistí a stap, porque también es lo suficientemente complejo, prefiero escribir la función ftrace desnuda, como reemplazar manualmente los primeros 5 bytes de una función con la llamada stub_handler. Ahora, incluso si estoy familiarizado con stap, sigo insistiendo en escribir solo scripts en modo guru, y todavía no me molesto en aprender la sintaxis de stap.

El uso de herramientas para mejorar la eficiencia es para personas que están familiarizadas con esta herramienta, para aquellos que no están familiarizados con la herramienta, como yo, el tiempo dedicado a aprender a usar esta herramienta me retrasará en enfrentar problemas reales. .

Del mismo modo, afilar un cuchillo no corta madera por error. Si quieres hacerlo bien, primero debes afilar tu herramienta. No estoy de acuerdo en que esto sea universal. Este tipo de discurso es para personas que frecuentemente resuelven problemas similares. Lo necesitan. Es un paradigma que un martillo puede clavar clavos. Si necesita clavar clavos con frecuencia, por supuesto necesita comprar un martillo, pero si solo necesita clavar clavos una vez, recoger un ladrillo del costado puede ser mejor que comprar uno. El martillo es más conveniente.

Si juegas un truco nuevo cada vez, por supuesto que no necesitas herramientas.

Además , el valor de "en la carretera" también contribuyó a mi visión de las herramientas. Siempre siento que soy una persona en la carretera, por eso detesto las molestias, no vuelvo a cargar, cargar y recordar las cosas que se pueden conseguir en todas partes. Me mudé de Hangzhou a Shanghai. Dado que Shanghai puede comprar edredones, ¿por qué debería devolver los edredones y tirarlos? ¿No sería mejor?

En la Edad de Piedra, las personas ya tenían una amplia gama de herramientas, pero para un hombre que estaba en la era matriarcal, la única herramienta que podían llevarse y elegir voluntariamente eran arcos y flechas, tirachinas y herramientas similares de ataque de largo alcance. Quizás incluso los cuchillos, las hachas y cosas por el estilo sean engorrosas, basta con comprender la esencia del problema en sí.


Los zapatos de cuero en Wenzhou, Zhejiang están mojados, ¡así que no engordan con la lluvia!

Supongo que te gusta

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