From 4e6add7faa9c39d699112defcd036779ae89c9f9 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Tue, 23 Apr 2019 09:40:47 +0200 Subject: [PATCH] bypass: generalize iface bypass stats Introduce functions in util-device.c to be able to manage the flow bypassed count stats. --- src/suricata.c | 1 + src/unix-manager.c | 8 ++-- src/util-device.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++ src/util-device.h | 5 +++ src/util-ebpf.c | 56 +++------------------------ 5 files changed, 113 insertions(+), 53 deletions(-) diff --git a/src/suricata.c b/src/suricata.c index 08ad01dec4..c5d6d22b6a 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -2703,6 +2703,7 @@ static int PostConfLoadedSetup(SCInstance *suri) StorageInit(); #ifdef HAVE_PACKET_EBPF EBPFRegisterExtension(); + LiveDevRegisterExtension(); #endif AppLayerSetup(); diff --git a/src/unix-manager.c b/src/unix-manager.c index b328227a76..882bbde75c 100644 --- a/src/unix-manager.c +++ b/src/unix-manager.c @@ -1190,10 +1190,12 @@ void UnixManagerThreadSpawnNonRunmode(void) UnixManagerRegisterCommand("iface-stat", LiveDeviceIfaceStat, NULL, UNIX_CMD_TAKE_ARGS); UnixManagerRegisterCommand("iface-list", LiveDeviceIfaceList, NULL, 0); + UnixManagerRegisterCommand("iface-bypassed-stat", + LiveDeviceGetBypassedStats, NULL, 0); + /* For backward compatibility */ + UnixManagerRegisterCommand("ebpf-bypassed-stat", + LiveDeviceGetBypassedStats, NULL, 0); UnixManagerThreadSpawn(0); -#ifdef HAVE_PACKET_EBPF - UnixManagerRegisterCommand("ebpf-bypassed-stats", EBPFGetBypassedStats, NULL, 0); -#endif } } } diff --git a/src/util-device.c b/src/util-device.c index 551a398963..2eecc11f9c 100644 --- a/src/util-device.c +++ b/src/util-device.c @@ -24,6 +24,8 @@ #define MAX_DEVNAME 10 +static int g_bypass_storage_id = -1; + /** * \file * @@ -45,9 +47,15 @@ static TAILQ_HEAD(, LiveDevice_) live_devices = static TAILQ_HEAD(, LiveDeviceName_) pre_live_devices = TAILQ_HEAD_INITIALIZER(pre_live_devices); +typedef struct BypassInfo_ { + SC_ATOMIC_DECLARE(uint64_t, ipv4_hash_count); + SC_ATOMIC_DECLARE(uint64_t, ipv6_hash_count); +} BypassInfo; + /** if set to 0 when we don't have real devices */ static int live_devices_stats = 1; + static int LiveSafeDeviceName(const char *devname, char *newdevname, size_t destlen); @@ -452,3 +460,91 @@ void LiveDeviceFinalize(void) SCFree(ld); } } + +static void LiveDevExtensionFree(void *x) +{ + if (x) + SCFree(x); +} + +/** + * Register bypass stats storage + */ +void LiveDevRegisterExtension(void) +{ + g_bypass_storage_id = LiveDevStorageRegister("bypass_stats", sizeof(void *), + NULL, LiveDevExtensionFree); +} + +/** + * Prepare a LiveDevice so we can set bypass stats + */ +int LiveDevUseBypass(LiveDevice *dev) +{ + BypassInfo *bpinfo = SCCalloc(1, sizeof(*bpinfo)); + if (bpinfo == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate bypass info structure"); + return -1; + } + + SC_ATOMIC_INIT(bpinfo->ipv4_hash_count); + SC_ATOMIC_INIT(bpinfo->ipv4_hash_count); + + LiveDevSetStorageById(dev, g_bypass_storage_id, bpinfo); + return 0; +} + +/** + * Set number of currently bypassed flows for a protocol family + * + * \param dev pointer to LiveDevice to set stats for + * \param cnt number of currently bypassed flows + * \param family AF_INET to set IPv4 count or AF_INET6 to set IPv6 count + */ +void LiveDevSetBypassStats(LiveDevice *dev, uint64_t cnt, int family) +{ + BypassInfo *bpfdata = LiveDevGetStorageById(dev, g_bypass_storage_id); + if (bpfdata) { + if (family == AF_INET) { + SC_ATOMIC_SET(bpfdata->ipv4_hash_count, cnt); + } else if (family == AF_INET6) { + SC_ATOMIC_SET(bpfdata->ipv6_hash_count, cnt); + } + } +} + +#ifdef BUILD_UNIX_SOCKET +TmEcode LiveDeviceGetBypassedStats(json_t *cmd, json_t *answer, void *data) +{ + LiveDevice *ldev = NULL, *ndev; + + json_t *ifaces = NULL; + while(LiveDeviceForEach(&ldev, &ndev)) { + BypassInfo *bpinfo = LiveDevGetStorageById(ldev, g_bypass_storage_id); + if (bpinfo) { + uint64_t ipv4_hash_count = SC_ATOMIC_GET(bpinfo->ipv4_hash_count); + uint64_t ipv6_hash_count = SC_ATOMIC_GET(bpinfo->ipv6_hash_count); + json_t *iface = json_object(); + if (ifaces == NULL) { + ifaces = json_object(); + if (ifaces == NULL) { + json_object_set_new(answer, "message", + json_string("internal error at json object creation")); + return TM_ECODE_FAILED; + } + } + json_object_set_new(iface, "ipv4_count", json_integer(ipv4_hash_count)); + json_object_set_new(iface, "ipv6_count", json_integer(ipv6_hash_count)); + json_object_set_new(ifaces, ldev->dev, iface); + } + } + if (ifaces) { + json_object_set_new(answer, "message", ifaces); + SCReturnInt(TM_ECODE_OK); + } + + json_object_set_new(answer, "message", + json_string("No interface using bypass")); + SCReturnInt(TM_ECODE_FAILED); +} +#endif diff --git a/src/util-device.h b/src/util-device.h index d14825b1b4..a8991af97c 100644 --- a/src/util-device.h +++ b/src/util-device.h @@ -60,8 +60,12 @@ typedef struct LiveDeviceName_ { TAILQ_ENTRY(LiveDeviceName_) next; } LiveDeviceName; +void LiveDevRegisterExtension(void); + int LiveRegisterDeviceName(const char *dev); int LiveRegisterDevice(const char *dev); +int LiveDevUseBypass(LiveDevice *dev); +void LiveDevSetBypassStats(LiveDevice *dev, uint64_t cnt, int family); int LiveGetDeviceCount(void); const char *LiveGetDeviceName(int number); LiveDevice *LiveGetDevice(const char *dev); @@ -78,6 +82,7 @@ void LiveDeviceFinalize(void); #ifdef BUILD_UNIX_SOCKET TmEcode LiveDeviceIfaceStat(json_t *cmd, json_t *server_msg, void *data); TmEcode LiveDeviceIfaceList(json_t *cmd, json_t *server_msg, void *data); +TmEcode LiveDeviceGetBypassedStats(json_t *cmd, json_t *answer, void *data); #endif #endif /* __UTIL_DEVICE_H__ */ diff --git a/src/util-ebpf.c b/src/util-ebpf.c index 92c998d4a6..149af41828 100644 --- a/src/util-ebpf.c +++ b/src/util-ebpf.c @@ -71,8 +71,6 @@ struct bpf_map_item { struct bpf_maps_info { struct bpf_map_item array[BPF_MAP_MAX_COUNT]; - SC_ATOMIC_DECLARE(uint64_t, ipv4_hash_count); - SC_ATOMIC_DECLARE(uint64_t, ipv6_hash_count); int last; }; @@ -221,8 +219,7 @@ static int EBPFLoadPinnedMaps(LiveDevice *livedev, struct ebpf_timeout_config *c SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate bpf map array"); return -1; } - SC_ATOMIC_INIT(bpf_map_data->ipv4_hash_count); - SC_ATOMIC_INIT(bpf_map_data->ipv6_hash_count); + if (config->mode == AFP_MODE_XDP_BYPASS) { bpf_map_data->array[0].fd = fd_v4; bpf_map_data->array[0].name = SCStrdup("flow_table_v4"); @@ -279,6 +276,8 @@ static int EBPFLoadPinnedMaps(LiveDevice *livedev, struct ebpf_timeout_config *c /* Attach the bpf_maps_info to the LiveDevice via the device storage */ LiveDevSetStorageById(livedev, g_livedev_storage_id, bpf_map_data); + /* Declare that device will use bypass stats */ + LiveDevUseBypass(livedev); return 0; @@ -411,8 +410,6 @@ int EBPFLoadFile(const char *iface, const char *path, const char * section, SCLogError(SC_ERR_MEM_ALLOC, "Can't allocate bpf map array"); return -1; } - SC_ATOMIC_INIT(bpf_map_data->ipv4_hash_count); - SC_ATOMIC_INIT(bpf_map_data->ipv6_hash_count); /* Store the maps in bpf_maps_info:: */ bpf_map__for_each(map, bpfobj) { @@ -453,6 +450,7 @@ int EBPFLoadFile(const char *iface, const char *path, const char * section, /* Attach the bpf_maps_info to the LiveDevice via the device storage */ LiveDevSetStorageById(livedev, g_livedev_storage_id, bpf_map_data); + LiveDevUseBypass(livedev); /* Finally we get the file descriptor for our eBPF program. We will use * the fd to attach the program to the socket (eBPF case) or to the device @@ -655,10 +653,7 @@ static int EBPFForEachFlowV4Table(LiveDevice *dev, const char *name, } SC_ATOMIC_ADD(dev->bypassed, flowstats->packets); - struct bpf_maps_info *bpfdata = LiveDevGetStorageById(dev, g_livedev_storage_id); - if (bpfdata) { - SC_ATOMIC_SET(bpfdata->ipv4_hash_count, hash_cnt); - } + LiveDevSetBypassStats(dev, hash_cnt, AF_INET); return found; } @@ -750,10 +745,7 @@ static int EBPFForEachFlowV6Table(LiveDevice *dev, const char *name, } SC_ATOMIC_ADD(dev->bypassed, flowstats->packets); - struct bpf_maps_info *bpfdata = LiveDevGetStorageById(dev, g_livedev_storage_id); - if (bpfdata) { - SC_ATOMIC_SET(bpfdata->ipv6_hash_count, hash_cnt); - } + LiveDevSetBypassStats(dev, hash_cnt, AF_INET6); return found; } @@ -819,42 +811,6 @@ int EBPFCheckBypassedFlowTimeout(struct flows_stats *bypassstats, return ret; } -#ifdef BUILD_UNIX_SOCKET -TmEcode EBPFGetBypassedStats(json_t *cmd, json_t *answer, void *data) -{ - LiveDevice *ldev = NULL, *ndev; - - json_t *ifaces = NULL; - while(LiveDeviceForEach(&ldev, &ndev)) { - struct bpf_maps_info *bpfdata = LiveDevGetStorageById(ldev, g_livedev_storage_id); - if (bpfdata) { - uint64_t ipv4_hash_count = SC_ATOMIC_GET(bpfdata->ipv4_hash_count); - uint64_t ipv6_hash_count = SC_ATOMIC_GET(bpfdata->ipv6_hash_count); - json_t *iface = json_object(); - if (ifaces == NULL) { - ifaces = json_object(); - if (ifaces == NULL) { - json_object_set_new(answer, "message", - json_string("internal error at json object creation")); - return TM_ECODE_FAILED; - } - } - json_object_set_new(iface, "ipv4_count", json_integer(ipv4_hash_count)); - json_object_set_new(iface, "ipv6_count", json_integer(ipv6_hash_count)); - json_object_set_new(ifaces, ldev->dev, iface); - } - } - if (ifaces) { - json_object_set_new(answer, "message", ifaces); - SCReturnInt(TM_ECODE_OK); - } - - json_object_set_new(answer, "message", - json_string("No interface using eBPF bypass")); - SCReturnInt(TM_ECODE_FAILED); -} -#endif - void EBPFRegisterExtension(void) { g_livedev_storage_id = LiveDevStorageRegister("bpfmap", sizeof(void *), NULL, BpfMapsInfoFree);