1 /*
2  * Copyright (c) 2017 Politecnico di Torino
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "BPF.h"
18 #include <linux/version.h>
19 
20 #include "catch.hpp"
21 
22 TEST_CASE("test hash table", "[hash_table]") {
23   const std::string BPF_PROGRAM = R"(
24     BPF_TABLE("hash", int, int, myhash, 1024);
25     BPF_TABLE("array", int, int, myarray, 1024);
26   )";
27 
28   ebpf::BPF bpf;
29   ebpf::StatusTuple res(0);
30   res = bpf.init(BPF_PROGRAM);
31   REQUIRE(res.code() == 0);
32 
33   ebpf::BPFHashTable<int, int> t = bpf.get_hash_table<int, int>("myhash");
34 
35   SECTION("bad table type") {
36     // try to get table of wrong type
__anon74abbefd0102()37     auto f1 = [&](){
38       bpf.get_hash_table<int, int>("myarray");
39     };
40 
41     REQUIRE_THROWS(f1());
42   }
43 
44   SECTION("standard methods") {
45     int k, v1, v2;
46     k = 1;
47     v1 = 42;
48     // create new element
49     res = t.update_value(k, v1);
50     REQUIRE(res.code() == 0);
51     res = t.get_value(k, v2);
52     REQUIRE(res.code() == 0);
53     REQUIRE(v2 == 42);
54 
55     // update existing element
56     v1 = 69;
57     res = t.update_value(k, v1);
58     REQUIRE(res.code() == 0);
59     res = t.get_value(k, v2);
60     REQUIRE(res.code() == 0);
61     REQUIRE(v2 == 69);
62 
63     // remove existing element
64     res = t.remove_value(k);
65     REQUIRE(res.code() == 0);
66 
67     // remove non existing element
68     res = t.remove_value(k);
69     REQUIRE(res.code() != 0);
70 
71     // get non existing element
72     res = t.get_value(k, v2);
73     REQUIRE(res.code() != 0);
74   }
75 
76   SECTION("walk table") {
77     for (int i = 1; i <= 10; i++) {
78       res = t.update_value(i * 3, i);
79       REQUIRE(res.code() == 0);
80     }
81     auto offline = t.get_table_offline();
82     REQUIRE(offline.size() == 10);
83     for (const auto &pair : offline) {
84       REQUIRE(pair.first % 3 == 0);
85       REQUIRE(pair.first / 3 == pair.second);
86     }
87 
88     // clear table
89     t.clear_table_non_atomic();
90     REQUIRE(t.get_table_offline().size() == 0);
91   }
92 }
93 
94 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)
95 TEST_CASE("percpu hash table", "[percpu_hash_table]") {
96   const std::string BPF_PROGRAM = R"(
97     BPF_TABLE("percpu_hash", int, u64, myhash, 128);
98     BPF_TABLE("percpu_array", int, u64, myarray, 64);
99   )";
100 
101   ebpf::BPF bpf;
102   ebpf::StatusTuple res(0);
103   res = bpf.init(BPF_PROGRAM);
104   REQUIRE(res.code() == 0);
105 
106   ebpf::BPFPercpuHashTable<int, uint64_t> t =
107     bpf.get_percpu_hash_table<int, uint64_t>("myhash");
108   size_t ncpus = ebpf::BPFTable::get_possible_cpu_count();
109 
110   SECTION("bad table type") {
111     // try to get table of wrong type
__anon74abbefd0202()112     auto f1 = [&](){
113       bpf.get_percpu_hash_table<int, uint64_t>("myarray");
114     };
115 
116     REQUIRE_THROWS(f1());
117   }
118 
119   SECTION("standard methods") {
120     int k;
121     std::vector<uint64_t> v1(ncpus);
122     std::vector<uint64_t> v2;
123 
124     for (size_t j = 0; j < ncpus; j++) {
125       v1[j] = 42 * j;
126     }
127 
128     k = 1;
129 
130     // create new element
131     res = t.update_value(k, v1);
132     REQUIRE(res.code() == 0);
133     res = t.get_value(k, v2);
134     REQUIRE(res.code() == 0);
135     for (size_t j = 0; j < ncpus; j++) {
136       REQUIRE(v2.at(j) == 42 * j);
137     }
138 
139     // update existing element
140     for (size_t j = 0; j < ncpus; j++) {
141       v1[j] = 69 * j;
142     }
143     res = t.update_value(k, v1);
144     REQUIRE(res.code() == 0);
145     res = t.get_value(k, v2);
146     REQUIRE(res.code() == 0);
147     for (size_t j = 0; j < ncpus; j++) {
148       REQUIRE(v2.at(j) == 69 * j);
149     }
150 
151     // remove existing element
152     res = t.remove_value(k);
153     REQUIRE(res.code() == 0);
154 
155     // remove non existing element
156     res = t.remove_value(k);
157     REQUIRE(res.code() != 0);
158 
159     // get non existing element
160     res = t.get_value(k, v2);
161     REQUIRE(res.code() != 0);
162   }
163 
164   SECTION("walk table") {
165     std::vector<uint64_t> v(ncpus);
166 
167     for (int k = 3; k <= 30; k+=3) {
168       for (size_t cpu = 0; cpu < ncpus; cpu++) {
169         v[cpu] = k * cpu;
170       }
171       res = t.update_value(k, v);
172       REQUIRE(res.code() == 0);
173     }
174 
175     // get whole table
176     auto offline = t.get_table_offline();
177     REQUIRE(offline.size() == 10);
178     for (int i = 0; i < 10; i++) {
179       // check the key
180       REQUIRE(offline.at(i).first % 3 == 0);
181 
182       // check value
183       for (size_t cpu = 0; cpu < ncpus; cpu++) {
184         REQUIRE(offline.at(i).second.at(cpu) == cpu * offline.at(i).first);
185       }
186     }
187 
188     // clear table
189     t.clear_table_non_atomic();
190     REQUIRE(t.get_table_offline().size() == 0);
191   }
192 }
193 #endif
194