Skip to content
Snippets Groups Projects
Commit c6fd80e5 authored by Florian-Dion's avatar Florian-Dion
Browse files

Ajout du plugin reseau sous ebpf

parent 2370da82
Branches
No related tags found
No related merge requests found
Pipeline #8977 failed
......@@ -30,6 +30,7 @@ target_hdr=src/sensors.h
target_mk=sensors.mk
nonsensor='counters_option|memory_option|sensors|util'
sensor_ebpf='network_ebpf'
hdr_blacklist=$nonsensor
hdr_whitelist=''
......@@ -52,10 +53,22 @@ ls_sensors() {
dprint hdr_blacklist >&2
dprint hdr_whitelist >&2
hdr_whitelist="${hdr_whitelist}|${sensor_ebpf}"
try find src -type f -name '*.h' |
sed 's,src/\(.*\)\.h,\1,' |
grep -xEv "($hdr_blacklist)" |
grep -xE "($hdr_whitelist)"
grep -xE "($hdr_whitelist)"
}
ls_ebpf_prog(){
[ -d src_ebpf ] || die 'fatal: the "src_ebpf" directory does not exit.'
try find src_ebpf -type f -name '*.bpf.c' |
sed 's,src_ebpf/\(.*\)\.bpf.c,\1,'
}
# gen_sensors_h(sensor, nb_sensors)
......@@ -107,10 +120,33 @@ gen_sensors_mk() {
done
printf '\n'
for sensor in $sensors; do
printf '$(OBJ_DIR)/%s.o: $(SRC_DIR)/%s.c $(SRC_DIR)/%s.h $(SRC_DIR)/util.h\n' \
"$sensor" "$sensor" "$sensor"
printf '\t$(CC) $(CFLAGS) -c $< -o $@\n'
printf '$(OBJ_DIR)/%s.o: $(SRC_DIR)/%s.c $(SRC_DIR)/%s.h $(SRC_DIR)/util.h $(wildcard $(OBJ_EBPF_DIR)/%s*.o)\n' \
"$sensor" "$sensor" "$sensor" "$sensor"
if [ $sensor = $sensor_ebpf ]; then
printf '\t$(CL) $(CL_FLAGS) -c $< -o $@ \n'
else
printf '\t$(CC) $(CFLAGS) -c $< -o $@\n'
fi
done
printf '\n'
}
gen_ebpf_prog_mk(){
ebpf_progs=$1
printf 'CAPTOR_OBJ_EBPF = '
for prog in $ebpf_progs; do
printf '$(OBJ_EBPF_DIR)/%s.bpf.o ' "$prog"
done
printf '\n'
for prog in $ebpf_progs; do
printf '$(OBJ_EBPF_DIR)/%s.bpf.o : $(EBPF_DIR)/%s.bpf.c vm \n' "$prog" "$prog"
printf '\t$(CL) $(EBPF_FLAGS) -c $< -o $@\n'
printf '\t$(TOOL) gen skeleton $@ > $(EBPF_DIR)/%s.skel.h\n' "$prog"
done
printf '\n'
}
detect_caps() {
......@@ -204,6 +240,7 @@ fi
done
sensors=$(ls_sensors)
ebpf_progs=$(ls_ebpf_prog)
nb_sensors=$(echo "$sensors" | sed '/^$/d' | wc -l)
if [ "$nb_sensors" -eq 0 ]; then
......@@ -212,7 +249,10 @@ if [ "$nb_sensors" -eq 0 ]; then
fi
try gen_sensors_h "$sensors" "$nb_sensors" >"$target_hdr"
try gen_sensors_mk "$sensors" >"$target_mk"
try gen_ebpf_prog_mk "$ebpf_progs" >"$target_mk"
try gen_sensors_mk "$sensors" >> "$target_mk"
try printf "CAPTOR_LDFLAGS = %s\n" "$CAPTOR_LDFLAGS" >>"$target_mk"
try printf "NVML_IFLAGS = %s\n" "$NVML_IFLAGS" >>"$target_mk"
......
......@@ -5,22 +5,30 @@ DOC_DIR = doc
OBJ_DIR = obj
LIB_DIR = lib
BIN_DIR = bin
EBPF_DIR = src_ebpf
OBJ_EBPF_DIR = obj_ebpf
TESTS_DIR = tests
BIN = mojitos
PREFIX = /usr/local
ARCH = $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/')
CC = gcc
CL= clang
TOOL = bpftool
EBPF_FLAGS = -std=gnu99 -g -O3 -target bpf -D__TARGET_ARCH_$(ARCH)
CL_FLAGS = -std=gnu99 -g -O3
CPPFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Wno-unused-function -I./lib $(NVML_IFLAGS)
CFLAGS = $(CPPFLAGS) -O3 -Werror
LDFLAGS = $(CAPTOR_LDFLAGS)
LDFLAGS = $(CAPTOR_LDFLAGS)
ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q
all: $(BIN) man
CAPTOR_OBJ_EBPF =
CAPTOR_OBJ =
CAPTOR_LDFLAGS =
CAPTOR_LDFLAGS =
NVML_IFLAGS =
include ./sensors.mk
......@@ -38,9 +46,9 @@ options:
@echo OBJ: $(OBJ)
$(BIN): $(BIN_DIR) $(OBJ) $(OBJ_DIR)/$(BIN).o
$(CC) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o $(LDFLAGS)
$(CL) -lbpf -lelf -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o $(LDFLAGS)
$(OBJ): $(OBJ_DIR)
$(OBJ): $(OBJ_DIR)
$(OBJ_DIR)/counters.o: $(SRC_DIR)/counters_option.h
$(OBJ_DIR)/memory_counters.o: $(SRC_DIR)/memory_option.h
......@@ -59,12 +67,19 @@ $(SRC_DIR)/counters_option.h: $(SRC_DIR)/counters_option.sh
$(SRC_DIR)/memory_option.h: $(SRC_DIR)/memory_option.sh
sh ./$(SRC_DIR)/memory_option.sh > $(SRC_DIR)/memory_option.h
$(OBJ_EBPF_DIR):
mkdir -p $(OBJ_EBPF_DIR)
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
$(BIN_DIR):
mkdir -p $(BIN_DIR)
vm :
$(TOOL) btf dump file /sys/kernel/btf/vmlinux format c > $(EBPF_DIR)/vmlinux.h
debug: CFLAGS = $(CPPFLAGS) -DDEBUG -g -Og
debug: $(BIN)
......@@ -105,4 +120,4 @@ uninstall:
rm -f $(PREFIX)/bin/$(BIN)
rm -f $(PREFIX)/share/man/man1/$(BIN).1
.PHONY: all clean mojitos debug format tests readme man install uninstall
.PHONY: all clean mojitos debug format tests readme man install uninstall vm
/*******************************************************
Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
This file is part of Mojitos.
Mojitos is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mojitos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MojitO/S. If not, see <https://www.gnu.org/licenses/>.
*******************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <net/if.h>
#include "../src_ebpf/network_ebpf_ingress.skel.h"
#include "../src_ebpf/network_ebpf_egress.skel.h"
#define NB_MAX_DEV 8
#define NB_SENSOR 6
#define ERROR_LOAD_PROG -1
#define ERROR_OPEN_PROG -3
#define ERROR_CREATE_HOOK -5
#define ERROR_GET_ITF -2
#define ERROR_GET_ID -4
#define ERROR_UPDATE_ELEM -6
#define ERROR_ACCESS_ELEM -7
char *_labels_network_ebpf[NB_SENSOR] = {
"%s:rxp",
"%s:txp",
"%s:rxb",
"%s:txb",
"%s:t_ingress",
"%s:t_egress",
};
struct compteur_pckt {
uint64_t data[3];
};
typedef struct compteur_pckt cpt_pckt;
struct monitoring_hook{
struct bpf_tc_hook ingress;
struct bpf_tc_hook egress;
};
typedef struct monitoring_hook monitoring_hook;
struct skel{
struct network_ebpf_ingress_bpf *skel_ingress;
struct network_ebpf_egress_bpf *skel_egress;
};
typedef struct skel network_skel;
struct Network {
uint64_t values[NB_MAX_DEV][NB_SENSOR];
uint64_t tmp_values[NB_MAX_DEV][NB_SENSOR];
network_skel *skel;
monitoring_hook tab_hook[NB_MAX_DEV];
char labels[NB_MAX_DEV][NB_SENSOR][128];
char devs[NB_MAX_DEV][128];
int error;
int ndev;
};
typedef struct Network Network;
/* créer un hook */
int create_hook_tc(monitoring_hook *tab_hook,int i,int flow,int index,int fd){
LIBBPF_OPTS(bpf_tc_hook, hook, .ifindex = index, .attach_point = flow );
libbpf_set_print(NULL);
int r = bpf_tc_hook_create(&hook);
LIBBPF_OPTS(bpf_tc_opts, opts, .prog_fd = fd);
r = bpf_tc_attach(&hook, &opts);
if (r < 0)
{
return r;
}
if(flow == BPF_TC_INGRESS){
tab_hook[i].ingress = hook;
}
else{
tab_hook[i].egress = hook;
}
return 0;
}
/* retourne le nombre d'interfaces valide */
int nb_interface (struct ifaddrs *i){
int nb = 0;
for(struct ifaddrs *t = i;t!=NULL && nb < NB_MAX_DEV;t = t->ifa_next){
if(if_nametoindex(t->ifa_name)<= nb){
return nb;
}
nb++;
}
return nb;
}
/* initialise les interfaces et les hooks */
unsigned int init_network_ebpf(char *dev, void **ptr)
{
if(dev==NULL){
exit(1);
}
struct Network *state = malloc(sizeof(struct Network));
memset(state, '\0', sizeof(*state));
state->skel = malloc(sizeof(network_skel));
state->skel->skel_ingress = network_ebpf_ingress_bpf__open();
state->skel->skel_egress = network_ebpf_egress_bpf__open();
if(!(state->skel->skel_ingress && state->skel->skel_egress)){
printf("Impossible d'ouvrir le programme\n");
state->error = ERROR_OPEN_PROG;
return ERROR_OPEN_PROG;
}
if(strcmp(dev,"X")==0){
struct ifaddrs *list_interface;
if (getifaddrs(&list_interface) < 0) {
printf(" Erreur: impossible de récupérer la liste des interfaces réseau du système\n");state->error = ERROR_GET_ITF; ERROR_GET_ITF;
}
state->ndev = nb_interface(list_interface);
int i=0;
for(struct ifaddrs *itf = list_interface ; i < state->ndev ; itf = itf->ifa_next,i++){
memcpy(&(state->devs[i]), itf->ifa_name, strlen(itf->ifa_name) + 1);
for(int j=0;j<NB_SENSOR;j++){
snprintf(state->labels[i][j], sizeof(state->labels[i][j]), _labels_network_ebpf[j], state->devs[i]);
}
}
free(list_interface);
}else{
state->ndev=1;
memcpy(&(state->devs[0]), dev, strlen(dev) + 1);
for(int i=0;i<NB_SENSOR;i++){
snprintf(state->labels[0][i], sizeof(state->labels[0][i]), _labels_network_ebpf[i], state->devs[0]);
}
}
if( network_ebpf_ingress_bpf__load(state->skel->skel_ingress) < 0 || network_ebpf_egress_bpf__load(state->skel->skel_egress) < 0){
printf("impossible de charger le programme dans le kernel\n");
state->error = ERROR_LOAD_PROG;
return ERROR_LOAD_PROG;
}
int fd_ingress = bpf_program__fd(state->skel->skel_ingress->progs.tc_test_ingress);
int fd_egress = bpf_program__fd(state->skel->skel_egress->progs.tc_test_egress);
if (!(fd_ingress && fd_egress) ){
printf("impossible de récupérer l'id du programme\n");
state->error = ERROR_GET_ID;
return ERROR_GET_ID;
}
int index;
for(int i=0; i<state->ndev; i++){
index = if_nametoindex(state->devs[i]);
if (create_hook_tc(state->tab_hook,i,BPF_TC_INGRESS,index,fd_ingress) <0 || create_hook_tc(state->tab_hook,i,BPF_TC_EGRESS,index,fd_egress) <0 ){
printf("Erreur lors de la création de un ou plusieurs hooks\n");
state->error = ERROR_CREATE_HOOK;
state->ndev=i;
return ERROR_CREATE_HOOK;
}
}
if(state->ndev==1){
int key=0;
if (bpf_map__update_elem(state->skel->skel_ingress->maps.is_multi_itf_ingress,&key,sizeof(int),&(state->ndev),sizeof(int),BPF_ANY) <0 || bpf_map__update_elem(state->skel->skel_egress->maps.is_multi_itf_egress,&key,sizeof(int),&(state->ndev),sizeof(int),BPF_ANY) <0 ){
printf("Erreur : impossible d'écrire dans une map\n");
state->error = ERROR_UPDATE_ELEM;
return ERROR_UPDATE_ELEM;
}
}
*ptr = (void *) state;
return state->ndev * NB_SENSOR;;
}
/* libère les ressources */
void clean_network_ebpf(void *ptr)
{
Network *state = ( Network *)ptr;
if (state == NULL) {
return;
}
if ( state ->error < -1 || state->error == 0 ){
if ( state->error < -3 || state->error == 0 ){
if( state->error < -4 || state->error ==0 ){
for(int i=0; i<state->ndev;i++ ){
LIBBPF_OPTS(bpf_tc_opts, opts);
opts.prog_fd = opts.prog_id = 0;
bpf_tc_detach(&(state->tab_hook[i].ingress),&opts);
bpf_tc_detach(&(state->tab_hook[i].egress),&opts);
bpf_tc_hook_destroy(&(state->tab_hook[i].ingress));
bpf_tc_hook_destroy(&(state->tab_hook[i].egress));
}
}
network_ebpf_ingress_bpf__detach(state->skel->skel_ingress);
network_ebpf_egress_bpf__detach(state->skel->skel_egress);
}
network_ebpf_ingress_bpf__destroy(state->skel->skel_ingress);
network_ebpf_egress_bpf__destroy(state->skel->skel_egress);
}
free(state->skel);
free(state);
}
/* pour récupérer les valeurs pour chaque interface*/
unsigned int get_network_ebpf(uint64_t *results, void *ptr)
{
Network *state = ( Network *)ptr;
cpt_pckt res_ingress,res_egress;
for (int i = 0; i < state->ndev; i++) {
if (bpf_map__lookup_elem(state->skel->skel_ingress->maps.my_data_ingress,&i,sizeof(int),&res_ingress,sizeof(cpt_pckt),BPF_ANY) <0 || bpf_map__lookup_elem(state->skel->skel_egress->maps.my_data_egress,&i,sizeof(int),&res_egress,sizeof(cpt_pckt),BPF_ANY) <0 ){
printf("Erreur : impossible de lire les informations contenus dans les maps \n");
return ERROR_ACCESS_ELEM;
}
for (int j = 0; j < NB_SENSOR-3; j++) {
results[i*NB_SENSOR + 2*j] = res_ingress.data[j]-state->tmp_values[i][2*j];
results[i*NB_SENSOR + 2*j + 1] = res_egress.data[j]-state->tmp_values[i][2*j+1];
state->tmp_values[i][2*j] = res_ingress.data[j];
state->tmp_values[i][2*j + 1] = res_egress.data[j];
}
}
return state->ndev * NB_SENSOR;
}
/* pour afficher les labels */
void label_network_ebpf(char **labels, void *ptr)
{
struct Network *state = (struct Network *) ptr;
for (int i = 0; i < state->ndev; i++) {
for (int j = 0; j < NB_SENSOR; j++) {
labels[i*NB_SENSOR + j] = state->labels[i][j];
}
}
}
/*******************************************************
Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
This file is part of Mojitos.
Mojitos is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mojitos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MojitO/S. If not, see <https://www.gnu.org/licenses/>.
*******************************************************/
unsigned int init_network_ebpf(char *, void **);
unsigned int get_network_ebpf(uint64_t *results, void *);
void clean_network_ebpf(void *);
void label_network_ebpf(char **labels, void *);
Sensor network_ebpf = {
.init = init_network_ebpf,
.get = get_network_ebpf,
.clean = clean_network_ebpf,
.label = label_network_ebpf,
.nb_opt = 1,
};
Optparse network_ebpf_opt[1] = {
{
.longname = "net-dev-ebpf",
.shortname = 'D',
.argtype = OPTPARSE_REQUIRED,
.usage_arg = "<net_dev>",
.usage_msg = "network monitoring with ebpf (if network_device is X, tries to detect it automatically)",
},
};
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
struct compteur_pckt {
uint64_t data[3];
};
typedef struct compteur_pckt cpt_pckt;
struct {
__uint(type,BPF_MAP_TYPE_ARRAY);
__type(key,int);
__type(value,cpt_pckt);
__uint(max_entries,8);
} my_data_egress SEC(".maps");
struct {
__uint(type,BPF_MAP_TYPE_ARRAY);
__type(key,int);
__type(value,int);
__uint(max_entries,1);
} is_multi_itf_egress SEC(".maps");
SEC("tc")
int tc_test_egress(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
uint64_t time = bpf_ktime_get_ns();
uint64_t nb_octets = data_end - data;
int key=0,* is_one_itf;
if( (is_one_itf = bpf_map_lookup_elem(&is_multi_itf_egress,&key))!=NULL){
if ( *is_one_itf==0){
key= skb->ifindex-1;
}
}
else{
bpf_printk("Erreur : impossible de savoir s'il y a une ou plusieurs interfaces \n");
return 1;
}
cpt_pckt *rec =bpf_map_lookup_elem(&my_data_egress,&key);
if(!rec){
bpf_printk("Erreur : récupération des données dans la map impossible\n");
return 1;
}
__sync_fetch_and_add(&(rec->data[0]),1);
__sync_fetch_and_add(&(rec->data[1]),nb_octets);
__sync_fetch_and_add(&(rec->data[2]),bpf_ktime_get_ns() - time);
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
File added
This diff is collapsed.
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
struct compteur_pckt {
uint64_t data[3];
};
typedef struct compteur_pckt cpt_pckt;
struct {
__uint(type,BPF_MAP_TYPE_ARRAY);
__type(key,int);
__type(value,cpt_pckt);
__uint(max_entries,8);
} my_data_ingress SEC(".maps");
struct {
__uint(type,BPF_MAP_TYPE_ARRAY);
__type(key,int);
__type(value,int);
__uint(max_entries,1);
} is_multi_itf_ingress SEC(".maps");
SEC("tc")
int tc_test_ingress(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
uint64_t time = bpf_ktime_get_ns();
uint64_t nb_octets = data_end - data;
int key=0,* is_one_itf;
if( (is_one_itf = bpf_map_lookup_elem(&is_multi_itf_ingress,&key))!=NULL){
if ( *is_one_itf==0){
key= skb->ifindex-1;
}
}
else{
bpf_printk("Erreur : impossible de savoir s'il y a une ou plusieurs interfaces \n");
return 1;
}
cpt_pckt *rec =bpf_map_lookup_elem(&my_data_ingress,&key);
if(!rec){
bpf_printk("Erreur : récupération des données dans la map impossible\n");
return 1;
}
__sync_fetch_and_add(&(rec->data[0]),1);
__sync_fetch_and_add(&(rec->data[1]),nb_octets);
__sync_fetch_and_add(&(rec->data[2]),bpf_ktime_get_ns() - time);
return 0;
}
char LICENSE[] SEC("license") = "Dual BSD/GPL";
\ No newline at end of file
File added
This diff is collapsed.
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment