]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
af-packet: XDP bypass in IPS mode
authorEric Leblond <eric@regit.org>
Mon, 8 Jan 2018 23:25:02 +0000 (00:25 +0100)
committerEric Leblond <eric@regit.org>
Tue, 6 Feb 2018 15:58:19 +0000 (16:58 +0100)
Implement XDP bypass in IPS mode by using XDP redirect to send
packets from bypassed flow directly to the transmission interface.

ebpf/xdp_filter.c
src/runmode-af-packet.c
src/source-af-packet.c
src/util-ebpf.c
src/util-ebpf.h

index 57b90836c8f00048d4c2fa6e81f8e747d6012052..e2b03277d1304d675d3ebbd16201211be21294ed 100644 (file)
@@ -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;
index 3f1695e85368bbe8a2a1597c0818016b5b584916..fe8923ce531d8d8649b51549f21ccf620aaa1620 100644 (file)
@@ -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");
index 2dcc10e25b88ce969411c2b603bf77ae97bee211..040ab196a5abc20961743898e55c247d1a856b45 100644 (file)
@@ -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
index b46d8a7ac5fffa09d0374fb1a123668eec7a1d32..4bde54dedb71c661e9f4795da466370150994b34 100644 (file)
@@ -46,6 +46,7 @@
 #include "util-device.h"
 
 #include "device-storage.h"
+#include "flow-storage.h"
 
 #include <bpf/libbpf.h>
 #include <bpf/bpf.h>
@@ -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
index f9acc543475bf1ee2258fc8329119d1e09273138..a4d167c3afbda7e6852a7ec4f8e44628ac7af861 100644 (file)
@@ -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