From: Eric Leblond Date: Mon, 8 Jan 2018 23:25:02 +0000 (+0100) Subject: af-packet: XDP bypass in IPS mode X-Git-Tag: suricata-4.1.0-beta1~186 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f04391031f113dbab7bae506906628e606a6adaf;p=thirdparty%2Fsuricata.git af-packet: XDP bypass in IPS mode Implement XDP bypass in IPS mode by using XDP redirect to send packets from bypassed flow directly to the transmission interface. --- diff --git a/ebpf/xdp_filter.c b/ebpf/xdp_filter.c index 57b90836c8..e2b03277d1 100644 --- a/ebpf/xdp_filter.c +++ b/ebpf/xdp_filter.c @@ -102,6 +102,20 @@ struct bpf_map_def SEC("maps") cpus_count = { .max_entries = 1, }; +struct bpf_map_def SEC("maps") tx_peer = { + .type = BPF_MAP_TYPE_DEVMAP, + .key_size = sizeof(int), + .value_size = sizeof(int), + .max_entries = 1, +}; + +struct bpf_map_def SEC("maps") tx_peer_int = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .max_entries = 1, +}; + static __always_inline int get_sport(void *trans_data, void *data_end, uint8_t protocol) { @@ -157,6 +171,8 @@ static int __always_inline filter_ipv4(void *data, __u64 nh_off, void *data_end) uint32_t key0 = 0; uint32_t *cpu_max = bpf_map_lookup_elem(&cpus_count, &key0); uint32_t *cpu_selected; + int *iface_peer; + int tx_port = 0; if ((void *)(iph + 1) > data_end) return XDP_PASS; @@ -195,7 +211,12 @@ static int __always_inline filter_ipv4(void *data, __u64 nh_off, void *data_end) value->packets++; value->bytes += data_end - data; - return XDP_DROP; + iface_peer = bpf_map_lookup_elem(&tx_peer_int, &key0); + if (!iface_peer) { + return XDP_DROP; + } else { + return bpf_redirect_map(&tx_peer, tx_port, 0); + } } if (cpu_max && *cpu_max) { @@ -221,6 +242,8 @@ static int __always_inline filter_ipv6(void *data, __u64 nh_off, void *data_end) uint32_t key0 = 0; int *cpu_max = bpf_map_lookup_elem(&cpus_count, &key0); uint32_t *cpu_selected; + int tx_port = 0; + int *iface_peer; if ((void *)(ip6h + 1) > data_end) return 0; @@ -250,7 +273,13 @@ static int __always_inline filter_ipv6(void *data, __u64 nh_off, void *data_end) value->packets++; value->bytes += data_end - data; value->time = bpf_ktime_get_ns(); - return XDP_DROP; + + iface_peer = bpf_map_lookup_elem(&tx_peer_int, &key0); + if (!iface_peer) { + return XDP_DROP; + } else { + return bpf_redirect_map(&tx_peer, tx_port, 0); + } } if (cpu_max && *cpu_max) { cpu_dest = (tuple.src[0] + tuple.dst[0] + tuple.src[3] + tuple.dst[3]) % *cpu_max; diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index 3f1695e853..fe8923ce53 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -409,6 +409,7 @@ static void *ParseAFPConfig(const char *iface) aconf->flags |= AFP_BYPASS; RunModeEnablesBypassManager(); BypassedFlowManagerRegisterCheckFunc(EBPFCheckBypassedFlowTimeout); + BypassedFlowManagerRegisterUpdateFunc(EBPFUpdateFlow); #else SCLogError(SC_ERR_UNIMPLEMENTED, "Bypass set but eBPF support is not built-in"); #endif @@ -496,6 +497,10 @@ static void *ParseAFPConfig(const char *iface) EBPFBuildCPUSet(NULL, aconf->iface); } } + /* we have a peer and we use bypass so we can set up XDP iface redirect */ + if (aconf->out_iface) { + EBPFSetPeerIface(aconf->iface, aconf->out_iface); + } } #else SCLogError(SC_ERR_UNIMPLEMENTED, "XDP support is not built-in"); diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 2dcc10e25b..040ab196a5 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -2360,6 +2360,7 @@ static int AFPBypassCallback(Packet *p) if (AFPInsertHalfFlow(p->afp_v.v4_map_fd, &key, inittime) == 0) { return 0; } + EBPFUpdateFlow(p->flow, p); return 1; } /* For IPv6 case we don't handle extended header in eBPF */ @@ -2390,6 +2391,7 @@ static int AFPBypassCallback(Packet *p) if (AFPInsertHalfFlow(p->afp_v.v6_map_fd, &key, inittime) == 0) { return 0; } + EBPFUpdateFlow(p->flow, p); return 1; } #endif diff --git a/src/util-ebpf.c b/src/util-ebpf.c index b46d8a7ac5..4bde54dedb 100644 --- a/src/util-ebpf.c +++ b/src/util-ebpf.c @@ -46,6 +46,7 @@ #include "util-device.h" #include "device-storage.h" +#include "flow-storage.h" #include #include @@ -57,6 +58,7 @@ #define BYPASSED_FLOW_TIMEOUT 60 static int g_livedev_storage_id = -1; +static int g_flow_storage_id = -1; struct bpf_map_item { char * name; @@ -68,6 +70,11 @@ struct bpf_maps_info { int last; }; +typedef struct BypassedIfaceList_ { + LiveDevice *dev; + struct BypassedIfaceList_ *next; +} BypassedIfaceList; + static void BpfMapsInfoFree(void *bpf) { struct bpf_maps_info *bpfinfo = (struct bpf_maps_info *)bpf; @@ -80,6 +87,17 @@ static void BpfMapsInfoFree(void *bpf) SCFree(bpfinfo); } +static void BypassedListFree(void *ifl) +{ + BypassedIfaceList *mifl = (BypassedIfaceList *)ifl; + BypassedIfaceList *nifl; + while (mifl) { + nifl = mifl->next; + SCFree(mifl); + mifl = nifl; + } +} + static void EBPFDeleteKey(int fd, void *key) { bpf_map_delete_elem(fd, key); @@ -517,6 +535,7 @@ int EBPFCheckBypassedFlowTimeout(struct flows_stats *bypassstats, void EBPFRegisterExtension(void) { g_livedev_storage_id = LiveDevStorageRegister("bpfmap", sizeof(void *), NULL, BpfMapsInfoFree); + g_flow_storage_id = FlowStorageRegister("bypassedlist", sizeof(void *), NULL, BypassedListFree); } @@ -585,6 +604,74 @@ void EBPFBuildCPUSet(ConfNode *node, char *iface) BPF_ANY); } +int EBPFSetPeerIface(const char *iface, const char *out_iface) +{ + int mapfd = EBPFGetMapFDByName(iface, "tx_peer"); + if (mapfd < 0) { + SCLogError(SC_ERR_INVALID_VALUE, + "Unable to find 'tx_peer' map"); + return -1; + } + int intmapfd = EBPFGetMapFDByName(iface, "tx_peer_int"); + if (intmapfd < 0) { + SCLogError(SC_ERR_INVALID_VALUE, + "Unable to find 'tx_peer_int' map"); + return -1; + } + + int key0 = 0; + unsigned int peer_index = if_nametoindex(out_iface); + if (peer_index == 0) { + SCLogError(SC_ERR_INVALID_VALUE, "No iface '%s'", out_iface); + return -1; + } + int ret = bpf_map_update_elem(mapfd, &key0, &peer_index, BPF_ANY); + if (ret) { + SCLogError(SC_ERR_AFP_CREATE, "Create peer entry failed (err:%d)", ret); + return -1; + } + ret = bpf_map_update_elem(intmapfd, &key0, &peer_index, BPF_ANY); + if (ret) { + SCLogError(SC_ERR_AFP_CREATE, "Create peer entry failed (err:%d)", ret); + return -1; + } + return 0; +} + +int EBPFUpdateFlow(Flow *f, Packet *p) +{ + BypassedIfaceList *ifl = (BypassedIfaceList *)FlowGetStorageById(f, g_flow_storage_id); + if (ifl == NULL) { + ifl = SCCalloc(1, sizeof(*ifl)); + if (ifl == NULL) { + return 0; + } + ifl->dev = p->livedev; + FlowSetStorageById(f, g_flow_storage_id, ifl); + return 1; + } + /* Look for packet iface in the list */ + BypassedIfaceList *ldev = ifl; + while (ldev) { + if (p->livedev == ldev->dev) { + return 1; + } + ldev = ldev->next; + } + /* Call bypass function if ever not in the list */ + p->BypassPacketsFlow(p); + + /* Add iface to the list */ + BypassedIfaceList *nifl = SCCalloc(1, sizeof(*nifl)); + if (nifl == NULL) { + return 0; + } + nifl->dev = p->livedev; + nifl->next = ifl; + FlowSetStorageById(f, g_flow_storage_id, nifl); + return 1; +} + #endif /* HAVE_PACKET_XDP */ #endif diff --git a/src/util-ebpf.h b/src/util-ebpf.h index f9acc54347..a4d167c3af 100644 --- a/src/util-ebpf.h +++ b/src/util-ebpf.h @@ -74,6 +74,10 @@ void EBPFRegisterExtension(void); void EBPFBuildCPUSet(ConfNode *node, char *iface); +int EBPFSetPeerIface(const char *iface, const char *out_iface); + +int EBPFUpdateFlow(Flow *f, Packet *p); + #endif #endif