bpf(2) System Calls Manual bpf(2) NOM bpf - Lancer une commande sur une mappe ou un programme BPF SYNOPSIS #include int bpf(int cmd, union bpf_attr *attr, unsigned int size); DESCRIPTION L'appel systeme bpf() effectue une serie d'operations liees aux Berkeley Packet Filters etendus (<< filtres de paquets Berkeley >>). BPF etendu (ou eBPF) est identique au BPF << classique >> originel (cBPF) utilise pour filtrer les paquets reseau. Pour les programmes tant cBPF qu'eBPF, le noyau analyse de maniere statique les programmes avant de les charger, afin de garantir qu'ils ne puissent pas mettre en danger le systeme en cours d'execution. eBPF etend cBPF de plusieurs manieres, notamment par la possibilite d'appeler un ensemble fixe de fonctions d'aide du noyau (a l'aide de l'extension d'opcode BPF_CALL fournie par eBPF) et d'acceder aux structures de donnees partagees telles que les mappes eBPF. Conception/architecture de BPF etendu Les mappes eBPF sont des structures de donnees generiques pour stocker differents types de donnees. Les types de donnees sont generalement traites comme des blobs binaires, donc l'utilisateur indique seulement la taille de la cle et celle de la valeur au moment de la creation de la mappe. En d'autres termes, la cle/valeur d'une mappe donnee peut avoir une structure arbitraire. Un processus utilisateur peut creer plusieurs mappes (dont les paires cle/valeur sont des octets de donnees opaques) et y acceder par les descripteurs de fichier. Differents programmes eBPF peuvent acceder aux memes mappes en parallele. Il appartient au processus utilisateur et au programme eBPF de decider ce qu'ils stockent dans leurs mappes. Il existe un type de mappe special appele un tableau de programmes (<< program array >>). Ce type de mappe stocke des descripteurs de fichiers qui renvoient a d'autres programmes eBPF. Quand une recherche est effectuee sur la mappe, le flux du programme est redirige directement au debut d'un autre programme eBPF et il ne renvoie rien au programme appelant. Le niveau de redirections est de 32 pour eviter de creer des boucles infinies. Au moment de l'execution, les descripteurs de fichier du programme stockes dans la mappe peuvent etre modifies, donc la fonctionnalite de programme peut etre modifiee sur la base d'exigences specifiques. Tous les programmes auxquels renvoie une mappe tableau de programmes (program-array) doivent avoir ete precedemment charges dans le noyau avec bpf(). Si une recherche de mappe echoue, le programme en cours poursuit son execution. Voir BPF_MAP_TYPE_PROG_ARRAY ci-dessous pour des details. Generalement, les programmes eBPF sont charges par le processus de l'utilisateur et decharges automatiquement quand le processus se termine. Dans certains cas, par exemple tc-bpf(8), le programme restera en vie dans le noyau meme apres que le processus qui l'a charge est fini. Dans ce cas, le sous-systeme tc garde une reference au programme eBPF apres que le descripteur de fichier est ferme par le programme de l'espace utilisateur. Ainsi, la survie d'un programme specifique dans le noyau depend de la maniere dont il a ete rattache a un sous-systeme donne du noyau apres qu'il a ete charge par bpf(). Chaque programme eBPF est un ensemble d'instructions qu'on peut executer en securite jusqu'a leur fin. Un verificateur interne au noyau determine de maniere statique ce que le programme eBPF interrompt et s'il peut etre execute en toute securite. Pendant la verification, le noyau ajoute un numero de reference de maniere incrementale pour chacune des mappes utilisees par le programme eBPF, si bien que les mappes qui y sont rattachees ne peuvent pas etre supprimees avant que le programme soit decharge. Les programmes eBPF peuvent etre rattaches a differents evenements. Ces evenements peuvent etre l'arrivee de paquets reseaux, le tracage d'evenements, la classification d'evenements en disciplines de files d'attente reseau (pour les programmes eBPF rattaches a un classificateur tc(8)), et d'autres types qui pourront etre ajoutes dans le futur. Un nouvel evenement provoque l'execution d'un programme eBPF, qui peut stocker des informations sur l'evenement dans des mappes eBPF. Par-dela les donnees stockees, les programmes eBPF peuvent appeler un ensemble fixe de fonctions d'aide internes au noyau. Un meme programme eBPF peut etre rattache a plusieurs evenements (evt) et divers programmes eBPF peuvent acceder a la meme mappe : tracage tracage tracage paquet paquet paquet evt A evt B evt C sur eth0 sur eth1 sur eth2 | | | | | ^ | | | | v | --> tracage <-- tracage socket tc ingress tc egress prog_1 prog_2 prog_3 classifieur action | | | | prog_4 prog_5 |--- -----| |------| mappe_3 | | mappe_1 mappe_2 --| mappe_4 |-- Arguments L'operation a effectuer par l'appel systeme bpf() est determinee par le parametre cmd. Chaque operation prend un parametre, fourni par attr, qui est un pointeur vers une union de type bpf_attr (voir ci-dessous). Les champs inutilises et de remplissage doivent etre mis a zero avant l'appel. Le parametre size est la taille de l'union vers laquelle pointe attr. La valeur fournie dans cmd est une parmi : BPF_MAP_CREATE Creer une mappe et renvoyer un descripteur de fichier qui s'y rapporte. Le drapeau de descripteur de fichier close-on-exec (voir fcntl(2)) est automatiquement active pour le nouveau descripteur de fichier. BPF_MAP_LOOKUP_ELEM Chercher un element par cle dans une mappe specifiee et renvoyer sa valeur. BPF_MAP_UPDATE_ELEM Creer ou mettre a jour un element (paire cle/valeur) dans une mappe specifiee. BPF_MAP_DELETE_ELEM Chercher et effacer un element par cle dans une mappe specifiee. BPF_MAP_GET_NEXT_KEY Chercher un element par cle dans une mappe specifiee et renvoyer la cle de l'element suivant. BPF_PROG_LOAD Verifier et charger un programme eBPF, en renvoyant un nouveau descripteur de fichier associe au programme. Le drapeau de descripteur de fichier close-on-exec (voir fcntl(2)) est active automatiquement pour le nouveau descripteur de fichier. L'union bpf_attr consiste dans diverses structures anonymes utilisees par differentes commandes bpf() : union bpf_attr { struct { /* Utilise par BPF_MAP_CREATE */ __u32 map_type; __u32 key_size; /* taille de la cle en octets */ __u32 value_size; /* taille de la valeur en octets */ __u32 max_entries; /* nombre maximal d'entrees dans une mappe */ }; struct { /* Utilise par les commandes BPF_MAP_*_ELEM et BPF_MAP_GET_NEXT_KEY */ __u32 map_fd; __aligned_u64 key; union { __aligned_u64 value; __aligned_u64 next_key; }; __u64 flags; }; struct { /* Utilise par BPF_PROG_LOAD */ __u32 prog_type; __u32 insn_cnt; __aligned_u64 insns; /* 'const struct bpf_insn *' */ __aligned_u64 license; /* 'const char *' */ __u32 log_level; /* niveau de bavardage du verificateur */ __u32 log_size; /* taille du tampon utilisateur */ __aligned_u64 log_buf; /* l'utilisateur a fourni 'char *' de tampon */ __u32 kern_version; /* verifier quand prog_type=kprobe (depuis Linux 4.1) */ }; } __attribute__((aligned(8))); mappes eBPF Les mappes sont des structures de donnees generiques pour stocker differents types de donnees. Elles permettent de partager des donnees entre des programmes eBPF du noyau, mais aussi entre les applications du noyau et de l'espace utilisateur. Chaque type de mappe a les attributs suivants : - type - nombre maximal d'elements - taille de la cle en octets - valeur de la cle en octets Les fonctions enveloppe suivantes montrent la maniere dont diverses commandes bpf() peuvent etre utilisees pour acceder aux mappes. Les fonctions utilisent le parametre cmd pour appeler differentes operations. BPF_MAP_CREATE La commande BPF_MAP_CREATE cree une nouvelle mappe, renvoyant un nouveau descripteur de fichier qui s'y rapporte. int bpf_create_map(enum bpf_map_type map_type, unsigned int key_size, unsigned int value_size, unsigned int max_entries) { union bpf_attr attr = { .map_type = map_type, .key_size = key_size, .value_size = value_size, .max_entries = max_entries }; return bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } La nouvelle mappe possede le type indique avec map_type et les attributs indiques dans key_size, value_size et max_entries. En cas de succes, cette operation renvoie un descripteur de fichier. En cas d'erreur, -1 est renvoye et errno est positionne sur EINVAL, EPERM ou ENOMEM. Les attributs key_size et value_size seront utilises par le verificateur lors du chargement du programme pour verifier que le programme appelle les fonctions d'aide bpf_map_*_elem() avec une key correctement initialisee et pour verifier que le programme n'accede pas a une value de l'element de la mappe au-dela de la value_size indiquee. Par exemple, quand une mappe est creee avec key_size de 8 et que le programme eBPF appelle un bpf_map_lookup_elem(map_fd, fp - 4) le programme sera rejete, puisque la fonction d'aide du noyau bpf_map_lookup_elem(map_fd, void *key) s'attend a lire 8 octets a l'endroit ou pointe key, mais l'adresse de depart fp - 4 (ou fp est le haut de la pile) cree un acces de la pile hors limites. De meme, lorsqu'une mappe est creee avec une value_size de 1 et que le programme eBPF contient value = bpf_map_lookup_elem(...); *(u32 *) value = 1; le programme sera rejete puisqu'il accede au pointeur value au-dela de la la limite value_size d'un octet specifiee. Actuellement, les valeurs suivantes sont prises en charge par map_type : enum bpf_map_type { BPF_MAP_TYPE_UNSPEC, /* Reserver 0 comme type de mappe non valable */ BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PROG_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_MAP_TYPE_PERCPU_HASH, BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_STACK_TRACE, BPF_MAP_TYPE_CGROUP_ARRAY, BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, BPF_MAP_TYPE_CPUMAP, BPF_MAP_TYPE_XSKMAP, BPF_MAP_TYPE_SOCKHASH, BPF_MAP_TYPE_CGROUP_STORAGE, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE, BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_STACK, /* Voir /usr/include/linux/bpf.h pour la liste complete. */ }; map_type selectionne une des implementations de mappe disponibles dans le noyau. Pour tous les types de mappe, les programmes eBPF accedent aux mappes avec les memes fonctions d'aide bpf_map_lookup_elem() et bpf_map_update_elem(). Vous trouverez ci-dessous plus de details sur les differents types de mappes. BPF_MAP_LOOKUP_ELEM La commande BPF_MAP_LOOKUP_ELEM cherche un element avec une key donnee dans la mappe a laquelle se rapporte le descripteur de fichier fd. int bpf_lookup_elem(int fd, const void *key, void *value) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), .value = ptr_to_u64(value), }; return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr)); } Si un element est trouve, l'operation renvoie zero et stocke la valeur de l'element dans value, qui doit pointer vers un tampon de value_size octets. Si aucun element n'est trouve, l'operation renvoie -1 et errno est positionne sur ENOENT. BPF_MAP_UPDATE_ELEM La commande BPF_MAP_UPDATE_ELEM cree ou met a jour un element avec une key/value donnee dans la mappe a laquelle se rapporte le descripteur de fichier fd. int bpf_update_elem(int fd, const void *key, const void *value, uint64_t flags) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), .value = ptr_to_u64(value), .flags = flags, }; return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); } Le parametre flags devrait etre forme d'une des manieres suivantes : BPF_ANY Creer un nouvel element ou mettre a jour un element existant. BPF_NOEXIST Creer un nouvel element seulement s'il n'existe pas. BPF_EXIST Mettre a jour un element existant. En cas de succes, l'operation renvoie zero. En cas d'erreur, -1 est renvoye et errno est positionne sur EINVAL, EPERM, ENOMEM ou E2BIG. E2BIG indique que le nombre d'elements de la mappe a atteint la limite max_entries specifiee au moment de la creation de la mappe. EEXIST sera renvoye si flags specifie BPF_NOEXIST et si l'element contenant key existe deja sur la mappe. ENOENT sera renvoye si flags specifie BPF_EXIST et si l'element contenant key n'existe pas sur la mappe. BPF_MAP_DELETE_ELEM La commande BPF_MAP_DELETE_ELEM efface l'element dont la cle est key sur la mappe a laquelle se rapporte le descripteur de fichier fd. int bpf_delete_elem(int fd, const void *key) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), }; return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr)); } S'il reussit, cet appel systeme renvoie 0. Si l'element n'est pas trouve, -1 est renvoye et errno est positionne sur ENOENT. BPF_MAP_GET_NEXT_KEY La commande BPF_MAP_GET_NEXT_KEY recherche un element par key sur la mappe a laquelle se refere le descripteur de fichier fd et elle definit le pointeur next_key vers la cle du prochain element. int bpf_get_next_key(int fd, const void *key, void *next_key) { union bpf_attr attr = { .map_fd = fd, .key = ptr_to_u64(key), .next_key = ptr_to_u64(next_key), }; return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr)); } Si key est trouvee, l'operation renvoie zero et next_key pointe vers la cle de l'element suivant. Si key n'est pas trouvee, l'operation renvoie zero et next_key pointe vers la cle du premier element. Si key est le dernier element, -1 est renvoye et errno est positionne sur ENOENT. Les autres valeurs possibles de errno sont ENOMEM, EFAULT, EPERM et EINVAL. Cette methode peut etre utilisee pour iterer entre tous les elements d'une mappe. close(map_fd) Effacer la mappe a laquelle se refere le descripteur de fichier map_fd. Quand le programme de l'espace utilisateur ayant cree la mappe se termine, toutes les mappes sont effacees automatiquement (mais voir REMARQUES). Types de mappe eBPF Les types de mappe suivants sont pris en charge : BPF_MAP_TYPE_HASH Les mappes table de hachage (hash-table) presentent les caracteristiques suivantes : - Les mappes sont creees et detruites par les programmes dans l'espace utilisateur. Tant les programmes eBPF que ceux de l'espace utilisateur peuvent effectuer des operations de recherche, de mise a jour et d'effacement. - Le noyau se charge d'allouer et de liberer les paires cle/valeur. - L'aide map_update_elem() echouera si vous inserez un nouvel element quand la limite max_entries est atteinte (cela garantit que les programmes eBPF ne peuvent pas epuiser la memoire). - map_update_elem() remplace atomiquement les elements existants. Les mappes table de hachage (hash-table) sont optimisees pour accelerer la recherche. BPF_MAP_TYPE_ARRAY Les mappes tableau (array) presentent les caracteristiques suivantes : - Elles sont optimisees pour une recherche plus rapide. A l'avenir, le compilateur du verificateur/JIT pourrait reconnaitre les operations lookup() qui utilisent une cle constante et l'optimiser dans un pointeur constant. Il est egalement possible d'optimiser une cle non constante dans un pointeur arithmetique direct, car les pointeurs et les value_size sont constants durant toute la vie des programmes eBPF. En d'autres termes, array_map_lookup_elem() peut etre mise << inline >> par le compilateur du verificateur/JIT tout en preservant l'acces concurrent a cette mappe a partir de l'espace utilisateur. - Tous les elements du tableau sont prealloues et initialises a zero au moment de l'initialisation - La cle est un indice de tableau et doit etre exactement de quatre octets. - map_delete_elem() echoue avec l'erreur EINVAL, car les elements ne peuvent pas etre effaces. - map_update_elem() remplace les elements de maniere non atomique ; pour des mises a jour atomiques, vous devriez plutot utiliser une mappe table de hachage (hash-table). Toutefois, il existe un cas particulier qui peut aussi etre utilise avec les tableaux : le __sync_fetch_and_add() interne atomique peut etre utilise sur des compteurs atomiques en 32 ou 64 bits. Par exemple, il peut s'appliquer sur la valeur entiere si elle represente un compteur unique ou, si une structure contient plusieurs compteurs, il pourrait etre utilise sur des compteurs individuels. Cela est tres souvent utile pour agreger et compter des evenements. Voici quelques cas d'usage des mappes tableau (array) : - Sous forme de variables eBPF << globales >> : un tableau d'un element dont la cle (indice) est 0 et dont la valeur est un ensemble de variables << globales >> que les programmes eBPF peuvent utiliser pour conserver leur etat entre les evenements. - Agregation d'evenements de tracage dans un ensemble fixe de << buckets >>. - Comptabilite des evenements reseaux, par exemple le nombre de paquets et leur taille. BPF_MAP_TYPE_PROG_ARRAY (depuis Linux 4.2) Une mappe tableau de programmes est un type special de mappe tableau dont les valeurs ne contiennent que des descripteurs de fichier qui se rapportent a d'autres programmes eBPF. Ainsi, tant key_size que value_size doivent etre d'exactement quatre octets. Cette mappe est utilisee en association avec l'aide bpf_tail_call(). Cela signifie qu'un programme eBPF auquel est rattache un tableau de programmes (program array) peut appeler a partir du noyau void bpf_tail_call(void *context, void *prog_map, unsigned int index); et donc remplacer le flux de son propre programme par celui du programme sur la tranche du tableau de programmes donne s'il y en a un. Vous pouvez considerer cela comme un saut de tableau vers un autre programme eBPF. Le programme appele reutilisera ensuite la meme pile. Quand un saut vers un nouveau programme a ete fait, il ne renverra plus a l'ancien programme. Si aucun programme eBPF n'est trouve sur l'indice donne du tableau de programmes (car la tranche de la mappe ne contient pas de descripteur de fichier de programme valable, la recherche d'indice/cle indiquee depasse la plage ou la limite de 32 appels en interne a ete depassee), l'execution continue avec le programme eBPF actuel. Cela peut etre utilise comme solution de repli pour les cas par defaut. Une mappe tableau de programmes sert, par exemple, a tracer ou mettre en reseau, a gerer des appels systeme individuels ou des protocoles dans leurs propres sous-programmes et a utiliser leurs identifiants comme identifiant individuel de mappe. Cette approche peut apporter des gains de performance et permet de depasser la limite du nombre d'instructions d'un programme eBPF. Dans des environnements dynamiques, un demon de l'espace utilisateur pourrait remplacer de maniere atomique des sous-programmes au moment de leur execution par de nouvelles versions, pour modifier le comportement general d'un programme, par exemple, si les regles globales changent. Programmes eBPF La commande BPF_PROG_LOAD est utilisee pour charger un programme eBPF dans le noyau. Le code de retour de cette commande est un nouveau descripteur de fichier associe a ce programme eBPF. char bpf_log_buf[LOG_BUF_SIZE]; int bpf_prog_load(enum bpf_prog_type type, const struct bpf_insn *insns, int insn_cnt, const char *license) { union bpf_attr attr = { .prog_type = type, .insns = ptr_to_u64(insns), .insn_cnt = insn_cnt, .license = ptr_to_u64(license), .log_buf = ptr_to_u64(bpf_log_buf), .log_size = LOG_BUF_SIZE, .log_level = 1, }; return bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } prog_type est un des types de programme suivants : enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC, /* Reserver 0 comme type de programme non valable */ BPF_PROG_TYPE_SOCKET_FILTER, BPF_PROG_TYPE_KPROBE, BPF_PROG_TYPE_SCHED_CLS, BPF_PROG_TYPE_SCHED_ACT, BPF_PROG_TYPE_TRACEPOINT, BPF_PROG_TYPE_XDP, BPF_PROG_TYPE_PERF_EVENT, BPF_PROG_TYPE_CGROUP_SKB, BPF_PROG_TYPE_CGROUP_SOCK, BPF_PROG_TYPE_LWT_IN, BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, BPF_PROG_TYPE_CGROUP_DEVICE, BPF_PROG_TYPE_SK_MSG, BPF_PROG_TYPE_RAW_TRACEPOINT, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_PROG_TYPE_LWT_SEG6LOCAL, BPF_PROG_TYPE_LIRC_MODE2, BPF_PROG_TYPE_SK_REUSEPORT, BPF_PROG_TYPE_FLOW_DISSECTOR, /* Voir /usr/include/linux/bpf.h pour la liste complete. */ }; Pour plus de details sur le type de programme eBPF, voir ci-dessous. Les autres champs de bpf_attr sont definis comme suit : - insns est un tableau d'instructions struct bpf_insn. - insn_cnt est le nombre d'instructions du programme auquel se rapporte insns. - license est une chaine de licence, qui doit etre compatible GPL pour appeler les fonctions d'aide marquees comme gpl_only (les regles de licence sont les memes que celles pour les modules du noyau, pour que meme des licences duales, telles que << Dual BSD/GPL >>, puissent etre utilisees). - log_buf est un pointeur vers un tampon alloue a l'appelant (caller-allocated) ou le verificateur du noyau peut stocker le journal de sa verification. Ce journal est une chaine de plusieurs lignes qui peut etre verifiee par l'auteur du programme pour comprendre la maniere par laquelle le verificateur est arrive a la conclusion que le programme eBPF n'est pas sur. Le format de sortie peut changer n'importe quand puisque le verificateur evolue. - log_size dimensionne le tampon vers lequel pointe log_buf. Si la taille du tampon n'est pas assez grande pour stocker tous les messages du verificateur, -1 est renvoye et errno est positionne sur ENOSPC. - Le niveau de precisions log_level du verificateur. Une valeur de zero signifie que le verificateur ne generera aucun journal ; dans ce cas log_buf doit etre un pointeur NULL et log_size doit valoir zero. Le fait d'appliquer close(2) au descripteur de fichier renvoye par BPF_PROG_LOAD dechargera le programme eBPF (mais voir les REMARQUES). Les mappes sont accessibles a partir des programmes eBPF et elles sont utilisees pour echanger des donnees entre des programmes eBPF et entre des programmes eBPF et d'autres de l'espace utilisateur. Par exemple, des programmes eBPF peuvent traiter divers evenements (comme kprobe, packets) et stocker leurs donnees dans une mappe, et les programmes de l'espace utilisateur peuvent alors recuperer ces donnees dans la mappe. Inversement, des programmes de l'espace utilisateur peuvent utiliser une mappe en tant que mecanisme de configuration, la mappe etant peuplee par des valeurs verifiees par le programme eBPF qui modifie ensuite son comportement a la volee en fonction de ces valeurs. Types de programme eBPF Le type de programme eBPF (prog_type) determine le sous-ensemble de fonctions d'aide du noyau que peut appeler le programme. Le type de programme determine egalement le format d'entree du programme (contexte) - le format de struct bpf_context (qui est le blob de donnees passe au programme eBPF en tant que premier parametre). Par exemple, un programme de tracage n'a pas exactement le meme sous-jeu de fonctions d'aide qu'un programme de filtrage de socket (bien qu'ils peuvent en avoir en commun). De meme l'entree (le contexte) d'un programme de tracage est un jeu de valeurs de registre, alors que ce sera un paquet reseau pour le filtrage de socket. Le jeu de fonctions disponibles pour les programmes eBPF d'un type donne pourra augmenter dans le futur. Les types de programmes suivants sont pris en charge : BPF_PROG_TYPE_SOCKET_FILTER (depuis Linux 3.19) Actuellement, le jeu de fonctions pour BPF_PROG_TYPE_SOCKET_FILTER est : bpf_map_lookup_elem(map_fd, void *key) /* rechercher la cle dans une map_fd */ bpf_map_update_elem(map_fd, void *key, void *value) /* mettre a jour la cle/valeur */ bpf_map_delete_elem(map_fd, void *key) /* effacer la cle d'une map_fd */ Le parametre bpf_context est un pointeur vers une struct __sk_buff. BPF_PROG_TYPE_KPROBE (depuis Linux 4.1) [A documenter] BPF_PROG_TYPE_SCHED_CLS (depuis Linux 4.1) [A documenter] BPF_PROG_TYPE_SCHED_ACT (depuis Linux 4.1) [A documenter] Evenements Une fois qu'un programme est charge, il peut etre rattache a un evenement. Divers sous-systemes du noyau ont plusieurs manieres de le faire. Depuis Linux 3.19, l'appel suivant rattachera le programme prog_fd au socket sockfd, qui a ete precedemment cree par un appel socket(2) : setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)); Depuis Linux 4.1, l'appel suivant peut etre utilise pour rattacher un programme eBPF auquel se rapporte le descripteur de fichier prog_fd a un descripteur de fichier d'evenement perf, event_fd, cree par un appel precedent a perf_event_open(2) : ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd); VALEUR RENVOYEE Pour qu'un appel reussisse, le code de retour depend de l'operation : BPF_MAP_CREATE Le nouveau descripteur de fichier associe a la mappe eBPF. BPF_PROG_LOAD Le nouveau descripteur de fichier associe au programme eBPF. Toutes les autres commandes : Zero. En cas d'erreur, la valeur de retour est -1 et errno est definie pour preciser l'erreur. ERREURS E2BIG Le programme eBPF est trop grand ou une mappe a atteint la limite max_entries (nombre maximal d'elements). EACCES Pour BPF_PROG_LOAD, meme si toutes les instructions du programme sont valables, le programme a ete rejete car il a ete considere comme non sur. Cela est possible s'il a eu un acces a une zone de la memoire interdite ou a une pile ou un registre non initialise, ou parce que les contraintes de la fonction ne correspondent pas aux types reels, ou qu'il y a eu un acces memoire non aligne. Dans ce cas, il est recommande d'appeler bpf() a nouveau, avec log_level = 1 et d'examiner le log_buf pour connaitre la raison exacte fournie par le verificateur. EAGAIN Pour BPF_PROG_LOAD, indique que les ressources necessaires sont bloquees. Cela se produit quand le verificateur detecte des signaux en attente alors qu'il verifie la validite du programme bpf. Dans ce cas, appeler a nouveau simplement bpf() avec les memes parametres. EBADF fd n'est pas un descripteur de fichier ouvert. EFAULT Un des pointeurs (key ou value ou log_buf ou insns) depasse l'espace d'adressage accessible. EINVAL La valeur indiquee dans cmd n'est pas reconnue par ce noyau. EINVAL Pour BPF_MAP_CREATE, soit map_type, soit les attributs ne sont pas autorises. EINVAL Pour des commandes BPF_MAP_*_ELEM, certains champs de union bpf_attr non utilises par cette commande n'ont pas ete positionnes sur zero. EINVAL Pour BPF_PROG_LOAD, indique une tentative de charger un programme non valable. Les programmes eBPF peuvent etre juges non valables du fait d'instructions non reconnues, de l'utilisation de champs reserves, de depassements de plage, de boucles infinies ou d'appels a des fonctions inconnues. ENOENT Pour BPF_MAP_LOOKUP_ELEM ou BPF_MAP_DELETE_ELEM, indique qu'un element avec la key donnee n'a pas ete trouve. ENOMEM Ne peut pas allouer suffisamment de memoire. EPERM L'appel a ete fait sans privileges suffisants (sans la capacite CAP_SYS_ADMIN). STANDARDS Linux. HISTORIQUE Linux 3.18. NOTES Avant Linux 4.4, toutes les commandes bpf() exigeaient que l'appelant ait la capacite CAP_SYS_ADMIN. Depuis Linux 4.4 jusqu'a present, un utilisateur non privilegie peut creer des programmes limites de type BPF_PROG_TYPE_SOCKET_FILTER et mappes associees. Toutefois, ils ne peuvent pas stocker des pointeurs du noyau dans les mappes et ils sont actuellement limites aux fonctions d'aide suivantes : - get_random - get_smp_processor_id - tail_call - ktime_get_ns Un acces sans privileges peut etre bloque en ecrivant la valeur 1 dans le fichier /proc/sys/kernel/unprivileged_bpf_disabled. Les objets eBPF (les mappes et les programmes) peuvent etre partages entre les processus. Par exemple, apres fork(2), l'enfant recupere les descripteurs de fichier qui se rapportent aux memes objets eBPF. De plus, les descripteurs de fichier qui se rapportent aux objets eBPF peuvent etre transferes a travers des sockets de domaine UNIX. Les descripteurs de fichier qui se rapportent aux objets eBPF peuvent etre dupliques de la maniere habituelle, en utilisant dup(2) ou des appels similaires. Un objet eBPF n'est desalloue qu'apres que tous les descripteurs de fichier qui se rapportent a l'objet sont fermes. Les programmes eBPF peuvent etre ecrits en C restreint compile en bytecode eBPF (en utilisant le compilateur clang). Diverses fonctionnalites sont absentes de ce C restreint, telles que les boucles, les variables globales, les fonctions variadiques, les nombres decimaux et le passage de structures comme parametres d'une fonction. Vous pouvez trouver des exemples dans les fichiers samples/bpf/*_kern.c de l'arborescence des sources du noyau. Le noyau contient un compilateur << just-in-time (JIT) >> qui traduit du bytecode eBPF en langage machine natif pour de meilleures performances. Avant Linux 4.15, le compilateur JIT est desactive par defaut, mais ce qu'il fait peut etre controle en ecrivant une des chaines suivantes d'entiers dans le fichier /proc/sys/net/core/bpf_jit_enable : 0 Desactiver la compilation JIT (par defaut). 1 Compilation normale. 2 Mode debogage. Les opcodes generes sont ecrits en hexadecimal dans le journal du noyau. Ces opcodes peuvent alors etre desassembles avec le programme tools/net/bpf_jit_disasm.c fourni dans l'arborescence des sources du noyau. Depuis Linux 4.15, le noyau peut etre configure avec l'option CONFIG_BPF_JIT_ALWAYS_ON. Dans ce cas, le compilateur JIT est toujours active et bpf_jit_enable est positionne sur 1 et immuable (cette option de configuration du noyau est fournie pour contrer une des attaques Spectre contre l'interpreteur BPF). Le compilateur JIT pour eBPF est actuellement disponible pour les architectures suivantes : - x86-64 (depuis Linux 3.18 ; cBPF depuis Linux 3.0) ; - ARM32 (depuis Linux 3.18 ; cBPF depuis Linux 3.4) ; - SPARC 32 (depuis Linux 3.18 ; cBPF depuis Linux 3.5) ; - ARM-64 (depuis Linux 3.18) ;; - s390 (depuis Linux 4.1 ; cBPF depuis Linux 3.7) ; - PowerPC 64 (depuis Linux 4.8 ; cBPF depuis Linux 3.1) ; - SPARC 64 (depuis Linux 4.12) ; - x86-32 (depuis Linux 4.18) ; - MIPS 64 (depuis Linux 4.18 ; cBPF depuis Linux 3.16) ; - riscv (depuis Linux 5.1). EXEMPLES /* Exemple de bpf+sockets : * 1. Creer une mappe tableau de 256 elements * 2. Charger le programme qui compte le nombre de paquets recus * r0 = skb->data[ETH_HLEN + offsetof(struct iphdr, protocol)] * map[r0]++ * 3. Rattacher prog_fd au socket brut a l'aide de setsockopt() * 4. Afficher le nombre de paquets TCP/UDP recus toutes les secondes */ int main(int argc, char *argv[]) { int sock, map_fd, prog_fd, key; long long value = 0, tcp_cnt, udp_cnt; map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 256); if (map_fd < 0) { printf("impossible de creer la projection '%s'\n", strerror(errno)); /* probablement non lance en tant que root */ return 1; } struct bpf_insn prog[] = { BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* r6 = r1 */ BPF_LD_ABS(BPF_B, ETH_HLEN + offsetof(struct iphdr, protocol)), /* r0 = ip->proto */ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = r2 - 4 */ BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* r1 = map_fd */ BPF_CALL_FUNC(BPF_FUNC_map_lookup_elem), /* r0 = map_lookup(r1, r2) */ BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), /* if (r0 == 0) goto pc+2 */ BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */ BPF_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* lock *(u64 *) r0 += r1 */ BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */ BPF_EXIT_INSN(), /* return r0 */ }; prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog) / sizeof(prog[0]), "GPL"); sock = open_raw_sock("lo"); assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd)) == 0); for (;;) { key = IPPROTO_TCP; assert(bpf_lookup_elem(map_fd, &key, &tcp_cnt) == 0); key = IPPROTO_UDP; assert(bpf_lookup_elem(map_fd, &key, &udp_cnt) == 0); printf("TCP %lld UDP %lld packets\n", tcp_cnt, udp_cnt); sleep(1); } return 0; } Vous pouvez trouvez du code complet operationnel dans le repertoire samples/bpf de l'arborescence des sources du noyau. VOIR AUSSI seccomp(2), bpf-helpers(7), socket(7), tc(8), tc-bpf(8) Les BPF classique et etendu sont expliques dans le fichier Documentation/networking/filter.txt des sources du noyau. TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot , Cedric Boutillier , Frederic Hantrais et Jean-Philippe MENGUAL Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 1 novembre 2023 bpf(2)