Comenzando con XDP---usando bpf_map en el programa eBPF

BPF MAP se puede entender esencialmente como una estructura de datos de tipo diccionario almacenada en el kernel en forma de [clave/valor] (también se puede entender como una base de datos en memoria). Por lo general, lo crea el programa eBPF que se ejecuta en el espacio del kernel y devuelve el descriptor de archivo correspondiente, y la aplicación de usuario que se ejecuta en el espacio de usuario puede acceder y operar el MAP BPF a través de este descriptor de archivo. En base a esto, BPF MAP generalmente se usa como un puente para el intercambio de datos bidireccional y la transferencia de información entre los programas eBPF del kernel (espacio del kernel) y las aplicaciones del usuario (espacio del usuario), y es una estructura de datos básica importante en los programas BPF.

1. Creación del MAPA BPF

La creación de un MAP BPF solo puede ser creada por un programa de modo de usuario al cargar un programa eBPF. Es muy simple de crear, como se muestra a continuación, simplemente créelo como una variable global ordinaria. Tenga en cuenta que __section("maps") no se puede cambiar, porque el kernel describirá la sección "maps" al cargar el programa eBPF y, si lo encuentra, creará automáticamente el BPF MAP especificado al cargar de acuerdo con el formato especificado. Entonces, este código debe escribirse en el código fuente del programa eBPF.



struct bpf_map_def __section("maps") mac_port_map = {
    
    
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(long long),
        .value_size = sizeof(int),
        .max_entries = 100,
};

2. Adición y actualización de elementos BPF MAP (modificación)

Esta función se implementa llamando a la función bpf_map_update_elem(mapa, clave, valor, opciones). Esta función se puede llamar en el programa de aplicación del espacio de usuario o en el programa eBPF, y la llamada es ligeramente diferente. Cabe señalar aquí que el último parámetro de esta función es diferente, lo que definirá diferentes comportamientos de la función.

  • Se accede al espacio de usuario a través de fd,
    que se llama a través de la declaración de función en /tools/lib/bpf/bpf.h:
int bpf_map_update_elem(int fd, const void *key, const void *value,  __u64 flags);
  • En eBPF, se accede directamente a través de punteros y
    se llama a través de la declaración de función en el archivo de encabezado /tools/testing/selftests/bpf/bpf_helpers.h:
int (*bpf_map_update_elem)(void *map, const void *key, const void *value, unsigned long long flags)
  • El último parámetro puede ser los siguientes tres parámetros
    #define BPF_ANY 0 /* Si la clave ya existe, actualice el valor correspondiente, si la clave no existe, cree un nuevo valor-clave /
    #define BPF_NOEXIST 1 /
    Solo la clave especificada por la clave Si no existe en el mapa, cree una nueva clave-valor; de lo contrario, se devolverá un error /
    #define BPF_EXIST 2 /
    Encuentre la clave-valor especificada por la clave y actualícela; de lo contrario, se devolverá un error * /

Entonces, generalmente usamos esta opción BPF_ANY 0.

3. Búsqueda y lectura de elementos BPF MAP

Se realiza llamando a la función bpf_map_lookup_elem(mapa, clave), que también se puede llamar en el espacio del kernel y del usuario. Utilice la tecla para encontrar el valor correspondiente.

  • Se accede al espacio de usuario a través de fd,
    que se llama a través de la declaración de función en /tools/lib/bpf/bpf.h:
int bpf_map_lookup_elem(int fd, const void *key, void *value);
  • En eBPF, se accede directamente a través de punteros y
    se llama a través de la declaración de función en el archivo de encabezado /tools/testing/selftests/bpf/bpf_helpers.h:
static void *(*bpf_map_lookup_elem)(void *map, const void *key)

4. Eliminación de elementos MAP BPF

Se implementa llamando a la función bpf_map_delete_elem(mapa, clave), que también se puede llamar en el espacio del núcleo y el espacio del usuario. Utilice la tecla para encontrar el valor correspondiente.

  • Se accede al espacio de usuario a través de fd,
    que se llama a través de la declaración de función en /tools/lib/bpf/bpf.h:
int bpf_map_delete_elem(int fd, const void *key);
  • En eBPF, se accede directamente a través de punteros y
    se llama a través de la declaración de función en el archivo de encabezado /tools/testing/selftests/bpf/bpf_helpers.h:
static int (*bpf_map_delete_elem)(void *map, const void *key)

5. Travesía de elementos BPF MAP

Se realiza mediante la función bpf_map_get_next_key(map, lookup_key, next_key), que solo se puede llamar en programas de espacio de usuario.

  • Se accede al espacio de usuario a través de fd,
    que se llama a través de la declaración de función en /tools/lib/bpf/bpf.h:
int bpf_map_get_next_key(int fd, const void *key, void *next_key);

6. Ejemplo de llamada

En el programa de modo de usuario, el código de bytes eBPF se importa automáticamente a XDP y la tabla de reenvío se escribe en el MAPA BPF.
En el programa eBPF, consulta el MAP BPF para enviar el mensaje anterior desde otra tarjeta de red según de qué tarjeta de red proviene el mensaje recibido.

  • Código fuente del programa de espacio de usuario
#include <stdio.h>

#include <signal.h>
#include <sys/socket.h>
#include <net/if.h>
#include <bpf/bpf.h>
#include <linux/bpf.h>
#include <linux/rtnetlink.h>
#include "/usr/src/linux-5.15/tools/testing/selftests/bpf/bpf_util.h"

#include <net/ethernet.h>
#include <linux/if_vlan.h>
#include <netinet/in.h>
#include <linux/ip.h>


int flags = XDP_FLAGS_UPDATE_IF_NOEXIST;
static int *ifindex_list;
static int mac_port_map_fd;

static void uninstall_exit(int sig)
{
    
    
        int i = 0;
        for (i = 0; i < 2; i++) {
    
    
                bpf_set_link_xdp_fd(ifindex_list[i], -1, 0);
        }
        exit(0);
}


int main(int argc, char *argv[])
{
    
    
        printf ("sizeof int = %ld\r\n", sizeof(int));
        printf ("sizeof short int = %ld\r\n", sizeof(short int));
        printf ("sizeof char = %ld\r\n", sizeof(char));
        printf ("sizeof long int = %ld\r\n", sizeof(long int));

        unsigned int indexofeth0 = 0;
        unsigned int indexofeth1 = 0;

        indexofeth0 = if_nametoindex(argv[1]);
        indexofeth1 = if_nametoindex(argv[2]);

        printf("index of %s is %d, index of %s is %d\r\n", argv[1], indexofeth0, argv[2], indexofeth1);

        int i;
        char filename[64];
        struct bpf_object *obj;
        struct bpf_prog_load_attr prog_load_attr = {
    
    
                .prog_type      = BPF_PROG_TYPE_XDP,
        };
        int prog_fd;

        snprintf(filename, sizeof(filename), "print.o");
        prog_load_attr.file = filename;

        if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd)) {
    
    
                return 1;
        }

        mac_port_map_fd = bpf_object__find_map_fd_by_name(obj, "mac_port_map");
        ifindex_list = (int *)calloc(2, sizeof(int *));

        ifindex_list[0] = if_nametoindex(argv[1]);
        ifindex_list[1] = if_nametoindex(argv[2]);

        for (i = 0; i < 2; i++) {
    
    
                if (bpf_set_link_xdp_fd(ifindex_list[i], prog_fd, flags) < 0) {
    
    
                        printf("install xdp fd failed\n");
                        return 1;
                }
        }
        signal(SIGINT, uninstall_exit);

	    bpf_map_update_elem(mac_port_map_fd, (const void *)&ifindex_list[1], (const void *)&ifindex_list[0], 0);
        printf("Update XDP item from [Port:Port] table-------------[%d]:[%d]\r\n", ifindex_list[1], ifindex_list[0]);
        bpf_map_update_elem(mac_port_map_fd, (const void *)&ifindex_list[0], (const void *)&ifindex_list[1], 0);
        printf("Update XDP item from [Port:Port] table-------------[%d]:[%d]\r\n", ifindex_list[0], ifindex_list[1]);	

        while(1){
    
    
            i++;
            sleep(1);
            printf("working...%d\r\n", i);
        }

}
  • ejemplo de código fuente eBPF

#include <stdio.h>
#include <linux/bpf.h>
#include <net/ethernet.h>
#include <linux/if_vlan.h>
#include <netinet/in.h>
#include <linux/ip.h>
#include <bpf/bpf_helpers.h>
#include <stdio.h>
#include <net/if.h>


#define i32bpf int


#ifndef __section
# define __section(NAME)                  \
   __attribute__((section(NAME), used))
#endif


// mac_port_map save the target MAC/port mapping bable,target MAC is the key, ifindex is the value
struct bpf_map_def __section("maps") mac_port_map = {
    
    
        .type = BPF_MAP_TYPE_HASH,
        .key_size = sizeof(int),
        .value_size = sizeof(int),
        .max_entries = 100,
};


__section("prog")
int xdp_ip_filter(struct xdp_md *ctx)
{
    
    
    void *end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    int ip_src;
    int ip_dst;
    unsigned char ip_protocol;
    long int offset;
    short int eth_type;
    int indexoftargetnic;
    long dst_mac = 0x323232323200;
    int in_index = ctx->ingress_ifindex, *out_index;

    char info_fmt1[] ="Dst Addr: %pI4\r\n";
    char info_fmt2[] ="Src Addr: %pI4\r\n";
    char info_fmt3[] ="-----------------\r\n";
    char info_fmt4[] ="return %d\r\n";
    char info_fmt5[] ="eth_type is 0x%x\r\n";
    char info_fmt6[] ="ip_protocal is 0x%x\r\n";
    char info_fmt7[] ="dst_mac is 0x%x,in_if is %d, out_if is %d\r\n";
	char info_fmt8[] ="do NOT find out index, XDP_PASS\r\n";
	char info_fmt9[] ="in index = %d, find out index = %d, XDP_FORWARD\r\n";
    

    struct ethhdr *eth = data;
    offset = sizeof(*eth);

 
    if (data + offset > end) {
    
    
        bpf_trace_printk(info_fmt4, sizeof(info_fmt4),1);	    
        return XDP_ABORTED;
    }
    eth_type = eth->h_proto;
    
    if (eth_type != 0x8)
    {
    
    
		if (eth_type != (short int)0xF788) 
        {
    
    
			bpf_trace_printk(info_fmt5, sizeof(info_fmt5), eth_type);
		}
    }
    
    /* 查看转发表,直接转发1588V2协议报文*/
    if (eth_type == (short int)0xF788)
    {
    
    
		/*bpf_trace_printk(info_fmt5, sizeof(info_fmt5), eth_type);*/
		out_index = bpf_map_lookup_elem(&mac_port_map, &in_index);
		if (out_index == 0) 
		{
    
    
            // 如若找不到,则上传到内核TCP/IP协议栈处理
            bpf_trace_printk(info_fmt8, sizeof(info_fmt8));
            return XDP_PASS;
        }
        else
		{
    
    
			/*bpf_trace_printk(info_fmt9, sizeof(info_fmt9), in_index, *out_index);*/
	        return bpf_redirect(*out_index,0);
		}
    } 

    struct iphdr *iph = data + offset;
    if (iph + 1 > end) {
    
    
        bpf_trace_printk(info_fmt4, sizeof(info_fmt4),3);
        return XDP_ABORTED;
    }

    return XDP_PASS;
}

char __license[] __section("license") = "GPL";

7. Comando de compilación

gcc main.c -lbpf
clang -O2 -Wall -target bpf -c print.c -o print.o

Supongo que te gusta

Origin blog.csdn.net/meihualing/article/details/131347646
Recomendado
Clasificación