#include <cgroup_helpers.h>
 #include <network_helpers.h>
 
+#include "progs/cg_storage_multi.h"
+
 #include "cg_storage_multi_egress_only.skel.h"
+#include "cg_storage_multi_egress_ingress.skel.h"
 
 #define PARENT_CGROUP "/cgroup_storage"
 #define CHILD_CGROUP "/cgroup_storage/child"
 static int duration;
 
 static bool assert_storage(struct bpf_map *map, const char *cgroup_path,
-                          __u32 expected)
+                          struct cgroup_value *expected)
 {
        struct bpf_cgroup_storage_key key = {0};
-       __u32 value;
+       struct cgroup_value value;
        int map_fd;
 
        map_fd = bpf_map__fd(map);
        if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) < 0,
                  "map-lookup", "errno %d", errno))
                return true;
-       if (CHECK(value != expected,
-                 "assert-storage", "got %u expected %u", value, expected))
+       if (CHECK(memcmp(&value, expected, sizeof(struct cgroup_value)),
+                 "assert-storage", "storages differ"))
                return true;
 
        return false;
 static bool assert_storage_noexist(struct bpf_map *map, const char *cgroup_path)
 {
        struct bpf_cgroup_storage_key key = {0};
-       __u32 value;
+       struct cgroup_value value;
        int map_fd;
 
        map_fd = bpf_map__fd(map);
 static void test_egress_only(int parent_cgroup_fd, int child_cgroup_fd)
 {
        struct cg_storage_multi_egress_only *obj;
+       struct cgroup_value expected_cgroup_value;
        struct bpf_link *parent_link = NULL, *child_link = NULL;
        bool err;
 
        if (CHECK(obj->bss->invocations != 1,
                  "first-invoke", "invocations=%d", obj->bss->invocations))
                goto close_bpf_object;
-       if (assert_storage(obj->maps.cgroup_storage, PARENT_CGROUP, 1))
+       expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };
+       if (assert_storage(obj->maps.cgroup_storage,
+                          PARENT_CGROUP, &expected_cgroup_value))
                goto close_bpf_object;
        if (assert_storage_noexist(obj->maps.cgroup_storage, CHILD_CGROUP))
                goto close_bpf_object;
        if (CHECK(obj->bss->invocations != 3,
                  "second-invoke", "invocations=%d", obj->bss->invocations))
                goto close_bpf_object;
-       if (assert_storage(obj->maps.cgroup_storage, PARENT_CGROUP, 2))
+       expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 };
+       if (assert_storage(obj->maps.cgroup_storage,
+                          PARENT_CGROUP, &expected_cgroup_value))
                goto close_bpf_object;
-       if (assert_storage(obj->maps.cgroup_storage, CHILD_CGROUP, 1))
+       expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 };
+       if (assert_storage(obj->maps.cgroup_storage,
+                          CHILD_CGROUP, &expected_cgroup_value))
                goto close_bpf_object;
 
 close_bpf_object:
        cg_storage_multi_egress_only__destroy(obj);
 }
 
+static void test_egress_ingress(int parent_cgroup_fd, int child_cgroup_fd)
+{
+       struct cg_storage_multi_egress_ingress *obj;
+
+       /* Cannot load both programs due to verifier failure:
+        * "only one cgroup storage of each type is allowed"
+        */
+       obj = cg_storage_multi_egress_ingress__open_and_load();
+       CHECK(obj || errno != EBUSY,
+             "skel-load", "errno %d, expected EBUSY", errno);
+
+       cg_storage_multi_egress_ingress__destroy(obj);
+}
+
 void test_cg_storage_multi(void)
 {
        int parent_cgroup_fd = -1, child_cgroup_fd = -1;
        if (test__start_subtest("egress_only"))
                test_egress_only(parent_cgroup_fd, child_cgroup_fd);
 
+       if (test__start_subtest("egress_ingress"))
+               test_egress_ingress(parent_cgroup_fd, child_cgroup_fd);
+
 close_cgroup_fd:
        close(child_cgroup_fd);
        close(parent_cgroup_fd);
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright 2020 Google LLC.
+ */
+
+#include <errno.h>
+#include <linux/bpf.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <bpf/bpf_helpers.h>
+
+#include "progs/cg_storage_multi.h"
+
+struct {
+       __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
+       __type(key, struct bpf_cgroup_storage_key);
+       __type(value, struct cgroup_value);
+} cgroup_storage SEC(".maps");
+
+__u32 invocations = 0;
+
+SEC("cgroup_skb/egress")
+int egress(struct __sk_buff *skb)
+{
+       struct cgroup_value *ptr_cg_storage =
+               bpf_get_local_storage(&cgroup_storage, 0);
+
+       __sync_fetch_and_add(&ptr_cg_storage->egress_pkts, 1);
+       __sync_fetch_and_add(&invocations, 1);
+
+       return 1;
+}
+
+SEC("cgroup_skb/ingress")
+int ingress(struct __sk_buff *skb)
+{
+       struct cgroup_value *ptr_cg_storage =
+               bpf_get_local_storage(&cgroup_storage, 0);
+
+       __sync_fetch_and_add(&ptr_cg_storage->ingress_pkts, 1);
+       __sync_fetch_and_add(&invocations, 1);
+
+       return 1;
+}