Man page - bpf(2)

Packages contains this manual

Available languages:

en fr ru

Manual

bpf

NOM
SYNOPSIS
DESCRIPTION
Conception/architecture de BPF étendu
Argument
mappes eBPF
Types de mappe eBPF
Programmes eBPF
Types de programme eBPF
ÉvĂ©nements
VALEUR RENVOYÉE
ERREURS
STANDARDS
HISTORIQUE
NOTES
EXEMPLES
VOIR AUSSI
TRADUCTION

NOM

bpf - Lancer une commande sur une mappe ou un programme BPF

SYNOPSIS

#include <linux/bpf.h>

int bpf(int cmd , union bpf_attr * attr , unsigned int size );

DESCRIPTION

L’appel systĂšme bpf () effectue une sĂ©rie d’opĂ©rations liĂ©es aux Berkeley Packet Filters Ă©tendus (« filtres de paquets Berkeley »). BPF Ă©tendu (ou eBPF) est identique au BPF « classique » originel (cBPF) utilisĂ© pour filtrer les paquets rĂ©seau. Pour les programmes tant cBPF qu’eBPF, le noyau analyse de maniĂšre statique les programmes avant de les charger, afin de garantir qu’ils ne puissent pas mettre en danger le systĂšme en cours d’exĂ©cution.

eBPF Ă©tend cBPF de plusieurs maniĂšres, notamment par la possibilitĂ© d’appeler un ensemble fixĂ© de fonctions d’aide du noyau (Ă  l’aide de l’extension d’opcode BPF_CALL fournie par eBPF) et d’accĂ©der aux structures de donnĂ©es partagĂ©es telles que les mappes eBPF.

Conception/architecture de BPF étendu

Les mappes eBPF sont des structures de donnĂ©es gĂ©nĂ©riques pour stocker diffĂ©rents types de donnĂ©es. Les types de donnĂ©es sont gĂ©nĂ©ralement traitĂ©s comme des blobs binaires, donc l’utilisateur indique seulement la taille de la clĂ© et celle de la valeur au moment de la crĂ©ation de la mappe. En d’autres termes, la clĂ©/valeur d’une mappe donnĂ©e peut avoir une structure arbitraire.

Un processus utilisateur peut crĂ©er plusieurs mappes (dont les paires clĂ©/valeur sont des octets de donnĂ©es opaques) et y accĂ©der par les descripteurs de fichier. DiffĂ©rents programmes eBPF peuvent accĂ©der aux mĂȘmes mappes en parallĂšle. Il appartient au processus utilisateur et au programme eBPF de dĂ©cider ce qu’ils stockent dans leurs mappes.

Il existe un type de mappe spĂ©cial appelĂ© un tableau de programmes (« program array »). Ce type de mappe stocke des descripteurs de fichiers qui renvoient Ă  d’autres programmes eBPF. Quand une recherche est effectuĂ©e sur la mappe, le flux du programme est redirigĂ© directement au dĂ©but d’un autre programme eBPF et il ne renvoie rien au programme appelant. Le niveau de redirections est de 32 pour Ă©viter de crĂ©er des boucles infinies. Au moment de l’exĂ©cution, les descripteurs de fichier du programme stockĂ©s dans la mappe peuvent ĂȘtre modifiĂ©s, donc la fonctionnalitĂ© de programme peut ĂȘtre modifiĂ©e sur la base d’exigences spĂ©cifiques. Tous les programmes auxquels renvoie une mappe tableau de programmes (program-array) doivent avoir Ă©tĂ© prĂ©cĂ©demment chargĂ©s dans le noyau avec bpf (). Si une recherche de mappe Ă©choue, le programme en cours poursuit son exĂ©cution. Voir BPF_MAP_TYPE_PROG_ARRAY ci-dessous pour des dĂ©tails.

GĂ©nĂ©ralement, les programmes eBPF sont chargĂ©s par le processus de l’utilisateur et dĂ©chargĂ©s automatiquement quand le processus se termine. Dans certains cas, par exemple tc-bpf (8), le programme restera en vie dans le noyau mĂȘme aprĂšs que le processus qui l’a chargĂ© est fini. Dans ce cas, le sous-systĂšme tc garde une rĂ©fĂ©rence au programme eBPF aprĂšs que le descripteur de fichier est fermĂ© par le programme de l’espace utilisateur. Ainsi, la survie d’un programme spĂ©cifique dans le noyau dĂ©pend de la maniĂšre dont il a Ă©tĂ© rattachĂ© Ă  un sous-systĂšme donnĂ© du noyau aprĂšs qu’il a Ă©tĂ© chargĂ© par bpf ().

Chaque programme eBPF est un ensemble d’instructions qu’on peut exĂ©cuter en sĂ©curitĂ© jusqu’à leur fin. Un vĂ©rificateur interne au noyau dĂ©termine de maniĂšre statique ce que le programme eBPF interrompt et s’il peut ĂȘtre exĂ©cutĂ© en toute sĂ©curitĂ©. Pendant la vĂ©rification, le noyau ajoute un numĂ©ro de rĂ©fĂ©rence de maniĂšre incrĂ©mentale pour chacune des mappes utilisĂ©es par le programme eBPF, si bien que les mappes qui y sont rattachĂ©es ne peuvent pas ĂȘtre supprimĂ©es avant que le programme soit dĂ©chargĂ©.

Les programmes eBPF peuvent ĂȘtre rattachĂ©s Ă  diffĂ©rents Ă©vĂ©nements. Ces Ă©vĂ©nements peuvent ĂȘtre l’arrivĂ©e de paquets rĂ©seaux, le traçage d’évĂ©nements, la classification d’évĂ©nements en disciplines de files d’attente rĂ©seau (pour les programmes eBPF rattachĂ©s Ă  un classificateur tc (8)), et d’autres types qui pourront ĂȘtre ajoutĂ©s dans le futur. Un nouvel Ă©vĂ©nement provoque l’exĂ©cution d’un programme eBPF, qui peut stocker des informations sur l’évĂšnement dans des mappes eBPF. Par-delĂ  les donnĂ©es stockĂ©es, les programmes eBPF peuvent appeler un ensemble fixĂ© de fonctions d’aide internes au noyau.

Un mĂȘme programme eBPF peut ĂȘtre rattachĂ© Ă  plusieurs Ă©vĂ©nements (Ă©vt) et divers programmes eBPF peuvent accĂ©der Ă  la mĂȘme mappe :

traçage traçage traçage paquet paquet paquet
évt A évt B évt C sur eth0 sur eth1 sur eth2
| | | | | ^
| | | | v |
--> traçage <-- traçage 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 |--

Argument

L’opĂ©ration Ă  effectuer par l’appel systĂšme bpf () est dĂ©terminĂ©e par le paramĂštre cmd . Chaque opĂ©ration prend un paramĂštre, fourni par attr , qui est un pointeur vers une union de type bpf_attr (voir ci-dessous). Les champs inutilisĂ©s et de remplissage doivent ĂȘtre mis Ă  zĂ©ro avant l’appel. Le paramĂštre size est la taille de l’union vers laquelle pointe attr .

La valeur fournie dans cmd est une parmi :
BPF_MAP_CREATE

CrĂ©er 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 activĂ© pour le nouveau descripteur de fichier.

BPF_MAP_LOOKUP_ELEM

Chercher un élément par clé dans une mappe spécifiée et renvoyer sa valeur.

BPF_MAP_UPDATE_ELEM

Créer ou mettre à jour un élément (paire clé/valeur) dans une mappe spécifiée.

BPF_MAP_DELETE_ELEM

Chercher et effacer un élément par clé dans une mappe spécifiée.

BPF_MAP_GET_NEXT_KEY

Chercher un Ă©lĂ©ment par clĂ© dans une mappe spĂ©cifiĂ©e et renvoyer la clĂ© de l’élĂ©ment suivant.

BPF_PROG_LOAD

Vérifier et charger un programme eBPF, en renvoyant un nouveau descripteur de fichier associé au programme. Le drapeau de descripteur de fichier close-on-exec (voir fcntl (2)) est activé automatiquement pour le nouveau descripteur de fichier.

L’union bpf_attr consiste dans diverses structures anonymes utilisĂ©es par diffĂ©rentes commandes bpf () :

union bpf_attr {
struct { /* Utilisé par BPF_MAP_CREATE */
__u32 map_type;
__u32 key_size; /* taille de la clé en octets */
__u32 value_size; /* taille de la valeur en octets */
__u32 max_entries; /* nombre maximal d’entrĂ©es
dans une mappe */
};
struct { /* Utilisé 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 { /* Utilisé 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 vérificateur */
__u32 log_size; /* taille du tampon utilisateur */
__aligned_u64 log_buf; /* l’utilisateur a fourni 'char *'
de tampon */
__u32 kern_version;
/* vérifier quand prog_type=kprobe
(depuis Linux 4.1) */
};
} __attribute__((aligned(8)));

mappes eBPF

Les mappes sont des structures de donnĂ©es gĂ©nĂ©riques pour stocker diffĂ©rents types de donnĂ©es. Elles permettent de partager des donnĂ©es 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’élĂ©ments

-

taille de la clé en octets

-

valeur de la clé en octets

Les fonctions enveloppe suivantes montrent la maniĂšre dont diverses commandes bpf () peuvent ĂȘtre utilisĂ©es pour accĂ©der aux mappes. Les fonctions utilisent le paramĂštre cmd pour appeler diffĂ©rentes opĂ©rations.
BPF_MAP_CREATE

La commande BPF_MAP_CREATE crĂ©e 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 possĂšde le type indiquĂ© avec map_type et les attributs indiquĂ©s dans key_size , value_size et max_entries . En cas de succĂšs, cette opĂ©ration renvoie un descripteur de fichier. En cas d’erreur, -1 est renvoyĂ© et errno est positionnĂ© sur EINVAL , EPERM ou ENOMEM .

Les attributs key_size et value_size seront utilisĂ©s par le vĂ©rificateur lors du chargement du programme pour vĂ©rifier que le programme appelle les fonctions d’aide bpf_map_*_elem () avec une key correctement initialisĂ©e et pour vĂ©rifier que le programme n’accĂšde pas Ă  une value de l’élĂ©ment de la mappe au-delĂ  de la value_size indiquĂ©e. Par exemple, quand une mappe est créée avec key_size de 8 et que le programme eBPF appelle un

bpf_map_lookup_elem(map_fd, fp - 4)

le programme sera rejetĂ©, puisque la fonction d’aide du noyau

bpf_map_lookup_elem(map_fd, void *key)

s’attend Ă  lire 8 octets Ă  l’endroit oĂč pointe key , mais l’adresse de dĂ©part fp - 4 (oĂč fp est le haut de la pile) crĂ©e un accĂšs de la pile hors limites.

De mĂȘme, lorsqu’une mappe est créée avec une value_size de 1 et que le programme eBPF contient

value = bpf_map_lookup_elem(...);
*(u32 *) value = 1;

le programme sera rejetĂ© puisqu’il accĂšde au pointeur value au-delĂ  de la la limite value_size d’un octet spĂ©cifiĂ©e.

Actuellement, les valeurs suivantes sont prises en charge par map_type :

enum bpf_map_type {
BPF_MAP_TYPE_UNSPEC, /* Réserver 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 complĂšte. */
};

map_type sĂ©lectionne une des implĂ©mentations de mappe disponibles dans le noyau. Pour tous les types de mappe, les programmes eBPF accĂšdent aux mappes avec les mĂȘmes fonctions d’aide bpf_map_lookup_elem () et bpf_map_update_elem (). Vous trouverez ci-dessous plus de dĂ©tails sur les diffĂ©rents types de mappes.

BPF_MAP_LOOKUP_ELEM

La commande BPF_MAP_LOOKUP_ELEM cherche un élément avec une key donnée dans la mappe à 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 Ă©lĂ©ment est trouvĂ©, l’opĂ©ration renvoie zĂ©ro et stocke la valeur de l’élĂ©ment dans value , qui doit pointer vers un tampon de value_size octets.

Si aucun Ă©lĂ©ment n’est trouvĂ©, l’opĂ©ration renvoie -1 et errno est positionnĂ© sur ENOENT .

BPF_MAP_UPDATE_ELEM

La commande BPF_MAP_UPDATE_ELEM crée ou met à jour un élément avec une key/value donnée dans la mappe à 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 paramĂštre flags devrait ĂȘtre formĂ© d’une des maniĂšres suivantes :
BPF_ANY

Créer un nouvel élément ou mettre à jour un élément existant.

BPF_NOEXIST

CrĂ©er un nouvel Ă©lĂ©ment seulement s’il n’existe pas.

BPF_EXIST

Mettre à jour un élément existant.

En cas de succĂšs, l’opĂ©ration renvoie zĂ©ro. En cas d’erreur, -1 est renvoyĂ© et errno est positionnĂ© sur EINVAL , EPERM , ENOMEM ou E2BIG . E2BIG indique que le nombre d’élĂ©ments de la mappe a atteint la limite max_entries spĂ©cifiĂ©e au moment de la crĂ©ation de la mappe. EEXIST sera renvoyĂ© si flags spĂ©cifie BPF_NOEXIST et si l’élĂ©ment contenant key existe dĂ©jĂ  sur la mappe. ENOENT sera renvoyĂ© si flags spĂ©cifie BPF_EXIST et si l’élĂ©ment contenant key n’existe pas sur la mappe.

BPF_MAP_DELETE_ELEM

La commande BPF_MAP_DELETE_ELEM efface l’élĂ©ment dont la clĂ© est key sur la mappe Ă  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 rĂ©ussit, cet appel systĂšme renvoie 0 . Si l’élĂ©ment n’est pas trouvĂ©, -1 est renvoyĂ© et errno est positionnĂ© sur ENOENT .

BPF_MAP_GET_NEXT_KEY

La commande BPF_MAP_GET_NEXT_KEY recherche un élément par key sur la mappe à laquelle se réfÚre le descripteur de fichier fd et elle définit le pointeur next_key vers la clé du prochain élément.

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 trouvĂ©e, l’opĂ©ration renvoie zĂ©ro et next_key pointe vers la clĂ© de l’élĂ©ment suivant. Si key n’est pas trouvĂ©e, l’opĂ©ration renvoie zĂ©ro et next_key pointe vers la clĂ© du premier Ă©lĂ©ment. Si key est le dernier Ă©lĂ©ment, -1 est renvoyĂ© et errno est positionnĂ© sur ENOENT . Les autres valeurs possibles de errno sont ENOMEM , EFAULT , EPERM et EINVAL . Cette mĂ©thode peut ĂȘtre utilisĂ©e pour itĂ©rer entre tous les Ă©lĂ©ments d’une mappe.

close(map_fd)

Effacer la mappe Ă  laquelle se rĂ©fĂšre le descripteur de fichier map_fd . Quand le programme de l’espace utilisateur ayant créé la mappe se termine, toutes les mappes sont effacĂ©es 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) présentent les caractéristiques suivantes :

-

Les mappes sont créées et dĂ©truites par les programmes dans l’espace utilisateur. Tant les programmes eBPF que ceux de l’espace utilisateur peuvent effectuer des opĂ©rations de recherche, de mise Ă  jour et d’effacement.

-

Le noyau se charge d’allouer et de libĂ©rer les paires clĂ©/valeur.

-

L’aide map_update_elem () Ă©chouera si vous insĂ©rez un nouvel Ă©lĂ©ment quand la limite max_entries est atteinte (cela garantit que les programmes eBPF ne peuvent pas Ă©puiser la mĂ©moire).

-

map_update_elem () remplace atomiquement les éléments existants.

Les mappes table de hachage (hash-table) sont optimisées pour accélérer la recherche.

BPF_MAP_TYPE_ARRAY

Les mappes tableau (array) présentent les caractéristiques suivantes :

-

Elles sont optimisĂ©es pour une recherche plus rapide. À l’avenir, le compilateur du vĂ©rificateur/JIT pourrait reconnaĂźtre les opĂ©rations lookup () qui utilisent une clĂ© constante et l’optimiser dans un pointeur constant. Il est Ă©galement possible d’optimiser une clĂ© non constante dans un pointeur arithmĂ©tique 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 ĂȘtre mise « inline » par le compilateur du vĂ©rificateur/JIT tout en prĂ©servant l’accĂšs concurrent Ă  cette mappe Ă  partir de l’espace utilisateur.

-

Tous les Ă©lĂ©ments du tableau sont prĂ©allouĂ©s et initialisĂ©s Ă  zĂ©ro au moment de l’initialisation

-

La clĂ© est un indice de tableau et doit ĂȘtre exactement de quatre octets.

-

map_delete_elem () Ă©choue avec l’erreur EINVAL , car les Ă©lĂ©ments ne peuvent pas ĂȘtre effacĂ©s.

-

map_update_elem () remplace les Ă©lĂ©ments de maniĂšre non atomique ; pour des mises Ă  jour atomiques, vous devriez plutĂŽt utiliser une mappe table de hachage (hash-table). Toutefois, il existe un cas particulier qui peut aussi ĂȘtre utilisĂ© avec les tableaux : le __sync_fetch_and_add() interne atomique peut ĂȘtre utilisĂ© sur des compteurs atomiques en 32 ou 64 bits. Par exemple, il peut s’appliquer sur la valeur entiĂšre si elle reprĂ©sente un compteur unique ou, si une structure contient plusieurs compteurs, il pourrait ĂȘtre utilisĂ© sur des compteurs individuels. Cela est trĂšs souvent utile pour agrĂ©ger et compter des Ă©vĂ©nements.

Voici quelques cas d’usage des mappes tableau (array) :

-

Sous forme de variables eBPF « globales » : un tableau d’un Ă©lĂ©ment dont la clĂ© (indice) est 0 et dont la valeur est un ensemble de variables « globales » que les programmes eBPF peuvent utiliser pour conserver leur Ă©tat entre les Ă©vĂ©nements.

-

AgrĂ©gation d’évĂ©nements de traçage dans un ensemble fixe de « buckets ».

-

Comptabilité des événements réseaux, 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 spĂ©cial de mappe tableau dont les valeurs ne contiennent que des descripteurs de fichier qui se rapportent Ă  d’autres programmes eBPF. Ainsi, tant key_size que value_size doivent ĂȘtre d’exactement quatre octets. Cette mappe est utilisĂ©e en association avec l’aide bpf_tail_call ().

Cela signifie qu’un programme eBPF auquel est rattachĂ© un tableau de programmes (program array) peut appeler Ă  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 donnĂ© s’il y en a un. Vous pouvez considĂ©rer cela comme un saut de tableau vers un autre programme eBPF. Le programme appelĂ© rĂ©utilisera ensuite la mĂȘme pile. Quand un saut vers un nouveau programme a Ă©tĂ© fait, il ne renverra plus Ă  l’ancien programme.

Si aucun programme eBPF n’est trouvĂ© sur l’indice donnĂ© du tableau de programmes (car la tranche de la mappe ne contient pas de descripteur de fichier de programme valable, la recherche d’indice/clĂ© indiquĂ©e dĂ©passe la plage ou la limite de 32 appels en interne a Ă©tĂ© dĂ©passĂ©e), l’exĂ©cution continue avec le programme eBPF actuel. Cela peut ĂȘtre utilisĂ© comme solution de repli pour les cas par dĂ©faut.

Une mappe tableau de programmes sert, par exemple, Ă  tracer ou mettre en rĂ©seau, Ă  gĂ©rer des appels systĂšme individuels ou des protocoles dans leurs propres sous-programmes et Ă  utiliser leurs identifiants comme identifiant individuel de mappe. Cette approche peut apporter des gains de performance et permet de dĂ©passer la limite du nombre d’instructions d’un programme eBPF. Dans des environnements dynamiques, un dĂ©mon de l’espace utilisateur pourrait remplacer de maniĂšre atomique des sous-programmes au moment de leur exĂ©cution par de nouvelles versions, pour modifier le comportement gĂ©nĂ©ral d’un programme, par exemple, si les rĂšgles globales changent.

Programmes eBPF

La commande BPF_PROG_LOAD est utilisée pour charger un programme eBPF dans le noyau. Le code de retour de cette commande est un nouveau descripteur de fichier associé à 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, /* Réserver 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 complĂšte. */
};

Pour plus de détails sur le type de programme eBPF, voir ci-dessous.

Les autres champs de bpf_attr sont définis 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 chaĂźne de licence, qui doit ĂȘtre compatible GPL pour appeler les fonctions d’aide marquĂ©es comme gpl_only (les rĂšgles de licence sont les mĂȘmes que celles pour les modules du noyau, pour que mĂȘme des licences duales, telles que « Dual BSD/GPL », puissent ĂȘtre utilisĂ©es).

-

log_buf est un pointeur vers un tampon allouĂ© Ă  l’appelant (caller-allocated) oĂč le vĂ©rificateur du noyau peut stocker le journal de sa vĂ©rification. Ce journal est une chaĂźne de plusieurs lignes qui peut ĂȘtre vĂ©rifiĂ©e par l’auteur du programme pour comprendre la maniĂšre par laquelle le vĂ©rificateur est arrivĂ© Ă  la conclusion que le programme eBPF n’est pas sĂ»r. Le format de sortie peut changer n’importe quand puisque le vĂ©rificateur Ă©volue.

-

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 vĂ©rificateur, -1 est renvoyĂ© et errno est positionnĂ© sur ENOSPC .

-

Le niveau de prĂ©cisions log_level du vĂ©rificateur. Une valeur de zĂ©ro signifie que le vĂ©rificateur ne gĂ©nĂšrera aucun journal ; dans ce cas log_buf doit ĂȘtre un pointeur NULL et log_size doit valoir zĂ©ro.

Le fait d’appliquer close (2) au descripteur de fichier renvoyĂ© par BPF_PROG_LOAD dĂ©chargera le programme eBPF (mais voir les REMARQUES).

Les mappes sont accessibles Ă  partir des programmes eBPF et elles sont utilisĂ©es pour Ă©changer des donnĂ©es entre des programmes eBPF et entre des programmes eBPF et d’autres de l’espace utilisateur. Par exemple, des programmes eBPF peuvent traiter divers Ă©vĂ©nements (comme kprobe, packets) et stocker leurs donnĂ©es dans une mappe, et les programmes de l’espace utilisateur peuvent alors rĂ©cupĂ©rer ces donnĂ©es dans la mappe. Inversement, des programmes de l’espace utilisateur peuvent utiliser une mappe en tant que mĂ©canisme de configuration, la mappe Ă©tant peuplĂ©e par des valeurs vĂ©rifiĂ©es par le programme eBPF qui modifie ensuite son comportement Ă  la volĂ©e en fonction de ces valeurs.

Types de programme eBPF

Le type de programme eBPF ( prog_type ) dĂ©termine le sous-ensemble de fonctions d’aide du noyau que peut appeler le programme. Le type de programme dĂ©termine Ă©galement le format d’entrĂ©e du programme (contexte) – le format de struct bpf_context (qui est le blob de donnĂ©es passĂ© au programme eBPF en tant que premier paramĂštre).

Par exemple, un programme de traçage n’a pas exactement le mĂȘme sous-jeu de fonctions d’aide qu’un programme de filtrage de socket (bien qu’ils peuvent en avoir en commun). De mĂȘme l’entrĂ©e (le contexte) d’un programme de traçage est un jeu de valeurs de registre, alors que ce sera un paquet rĂ©seau pour le filtrage de socket.

Le jeu de fonctions disponibles pour les programmes eBPF d’un type donnĂ© 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 clé dans une map_fd */
bpf_map_update_elem(map_fd, void *key, void *value)
/* mettre à jour la clé/valeur */
bpf_map_delete_elem(map_fd, void *key)
/* effacer la clĂ© d’une map_fd */

Le paramĂštre bpf_context est un pointeur vers une struct __sk_buff .

BPF_PROG_TYPE_KPROBE (depuis Linux 4.1)

[À documenter]

BPF_PROG_TYPE_SCHED_CLS (depuis Linux 4.1)

[À documenter]

BPF_PROG_TYPE_SCHED_ACT (depuis Linux 4.1)

[À documenter]

ÉvĂ©nements

Une fois qu’un programme est chargĂ©, il peut ĂȘtre rattachĂ© Ă  un Ă©vĂ©nement. Divers sous-systĂšmes du noyau ont plusieurs maniĂšres de le faire.

Depuis Linux 3.19, l’appel suivant rattachera le programme prog_fd au socket sockfd , qui a Ă©tĂ© prĂ©cĂ©demment créé 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 ĂȘtre utilisĂ© pour rattacher un programme eBPF auquel se rapporte le descripteur de fichier prog_fd Ă  un descripteur de fichier d’évĂ©nement perf , event_fd , créé par un appel prĂ©cĂ©dent Ă  perf_event_open (2) :

ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);

VALEUR RENVOYÉE

Pour qu’un appel rĂ©ussisse, le code de retour dĂ©pend de l’opĂ©ration :
BPF_MAP_CREATE

Le nouveau descripteur de fichier associé à la mappe eBPF.

BPF_PROG_LOAD

Le nouveau descripteur de fichier associé au programme eBPF.

Toutes les autres commandes :

Zéro.

En cas d’erreur, la valeur de retour est -1 et errno est dĂ©finie pour prĂ©ciser l’erreur.

ERREURS

E2BIG

Le programme eBPF est trop grand ou une mappe a atteint la limite max_entries (nombre maximal d’élĂ©ments).

EACCES

Pour BPF_PROG_LOAD , mĂȘme si toutes les instructions du programme sont valables, le programme a Ă©tĂ© rejetĂ© car il a Ă©tĂ© considĂ©rĂ© comme non sĂ»r. Cela est possible s’il a eu un accĂšs Ă  une zone de la mĂ©moire interdite ou Ă  une pile ou un registre non initialisĂ©, ou parce que les contraintes de la fonction ne correspondent pas aux types rĂ©els, ou qu’il y a eu un accĂšs mĂ©moire non alignĂ©. Dans ce cas, il est recommandĂ© d’appeler bpf () Ă  nouveau, avec log_level = 1 et d’examiner le log_buf pour connaĂźtre la raison exacte fournie par le vĂ©rificateur.

EAGAIN

Pour BPF_PROG_LOAD , indique que les ressources nĂ©cessaires sont bloquĂ©es. Cela se produit quand le vĂ©rificateur dĂ©tecte des signaux en attente alors qu’il vĂ©rifie la validitĂ© du programme bpf. Dans ce cas, appeler Ă  nouveau simplement bpf () avec les mĂȘmes paramĂštres.

EBADF

fd n’est pas un descripteur de fichier ouvert

EFAULT

Un des pointeurs ( key ou value ou log_buf ou insns ) dĂ©passe l’espace d’adressage accessible.

EINVAL

La valeur indiquĂ©e dans cmd n’est pas reconnue par ce noyau.

EINVAL

Pour BPF_MAP_CREATE , soit map_type , soit les attributs ne sont pas autorisés.

EINVAL

Pour des commandes BPF_MAP_*_ELEM , certains champs de union bpf_attr non utilisĂ©s par cette commande n’ont pas Ă©tĂ© positionnĂ©s sur zĂ©ro.

EINVAL

Pour BPF_PROG_LOAD , indique une tentative de charger un programme non valable. Les programmes eBPF peuvent ĂȘtre jugĂ©s non valables du fait d’instructions non reconnues, de l’utilisation de champs rĂ©servĂ©s, de dĂ©passements de plage, de boucles infinies ou d’appels Ă  des fonctions inconnues.

ENOENT

Pour BPF_MAP_LOOKUP_ELEM ou BPF_MAP_DELETE_ELEM , indique qu’un Ă©lĂ©ment avec la key donnĂ©e n’a pas Ă©tĂ© trouvĂ©.

ENOMEM

Ne peut pas allouer suffisamment de mémoire.

EPERM

L’appel a Ă©tĂ© fait sans privilĂšges suffisants (sans la capacitĂ© CAP_SYS_ADMIN ).

STANDARDS

Linux.

HISTORIQUE

Linux 3.18.

NOTES

Avant Linux 4.4, toutes les commandes bpf () exigeaient que l’appelant ait la capacitĂ© CAP_SYS_ADMIN . Depuis Linux 4.4 jusqu’à prĂ©sent, un utilisateur non privilĂ©giĂ© peut crĂ©er des programmes limitĂ©s de type BPF_PROG_TYPE_SOCKET_FILTER et mappes associĂ©es. Toutefois, ils ne peuvent pas stocker des pointeurs du noyau dans les mappes et ils sont actuellement limitĂ©s aux fonctions d’aide suivantes :

-

get_random

-

get_smp_processor_id

-

tail_call

-

ktime_get_ns

Un accĂšs sans privilĂšges peut ĂȘtre bloquĂ© en Ă©crivant la valeur 1 dans le fichier /proc/sys/kernel/unprivileged_bpf_disabled .

Les objets eBPF (les mappes et les programmes) peuvent ĂȘtre partagĂ©s entre les processus. Par exemple, aprĂšs fork (2), l’enfant rĂ©cupĂšre les descripteurs de fichier qui se rapportent aux mĂȘmes objets eBPF. De plus, les descripteurs de fichier qui se rapportent aux objets eBPF peuvent ĂȘtre transfĂ©rĂ©s Ă  travers des sockets de domaine UNIX. Les descripteurs de fichier qui se rapportent aux objets eBPF peuvent ĂȘtre dupliquĂ©s de la maniĂšre habituelle, en utilisant dup (2) ou des appels similaires. Un objet eBPF n’est dĂ©sallouĂ© qu’aprĂšs que tous les descripteurs de fichier qui se rapportent Ă  l’objet sont fermĂ©s.

Les programmes eBPF peuvent ĂȘtre Ă©crits en C restreint compilĂ© en bytecode eBPF (en utilisant le compilateur clang ). Diverses fonctionnalitĂ©s sont absentes de ce C restreint, telles que les boucles, les variables globales, les fonctions variadiques, les nombres dĂ©cimaux et le passage de structures comme paramĂštres 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 dĂ©sactivĂ© par dĂ©faut, mais ce qu’il fait peut ĂȘtre contrĂŽlĂ© en Ă©crivant une des chaĂźnes suivantes d’entiers dans le fichier /proc/sys/net/core/bpf_jit_enable :

0

Désactiver la compilation JIT (par défaut).

1

Compilation normale.

2

Mode dĂ©bogage. Les opcodes gĂ©nĂ©rĂ©s sont Ă©crits en hexadĂ©cimal dans le journal du noyau. Ces opcodes peuvent alors ĂȘtre dĂ©sassemblĂ©s avec le programme tools/net/bpf_jit_disasm.c fourni dans l’arborescence des sources du noyau.

Depuis Linux 4.15, le noyau peut ĂȘtre configurĂ© avec l’option CONFIG_BPF_JIT_ALWAYS_ON . Dans ce cas, le compilateur JIT est toujours activĂ© et bpf_jit_enable est positionnĂ© sur 1 et immuable (cette option de configuration du noyau est fournie pour contrer une des attaques Spectre contre l’interprĂ©teur 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. Créer une mappe tableau de 256 éléments
* 2. Charger le programme qui compte le nombre de paquets reçus
* r0 = skb->data[ETH_HLEN + offsetof(struct iphdr, protocol)]
* map[r0]++
* 3. Rattacher prog_fd au socket brut à l’aide de setsockopt()
* 4. Afficher le nombre de paquets TCP/UDP reçus 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 créer la projection '%s'\n", strerror(errno));
/* probablement non lancé 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 opĂ©rationnel dans le rĂ©pertoire 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 étendu sont expliqués dans le fichier Documentation/networking/filter.txt des sources du noyau.

TRADUCTION

La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org>, Cédric Boutillier <cedric.boutillier@gmail.com>, Frédéric Hantrais <fhantrais@gmail.com> et Jean-Philippe MENGUAL <jpmengual@debian.org>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n’y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org .