1 /*
2 * Copyright (c) 2021 BayLibre SAS
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(npf_base, CONFIG_NET_PKT_FILTER_LOG_LEVEL);
9
10 #include <zephyr/net/net_core.h>
11 #include <zephyr/net/net_pkt_filter.h>
12 #include <zephyr/spinlock.h>
13
14 /*
15 * Our actual rule lists for supported test points
16 */
17
18 struct npf_rule_list npf_send_rules = {
19 .rule_head = SYS_SLIST_STATIC_INIT(&send_rules.rule_head),
20 .lock = { },
21 };
22
23 struct npf_rule_list npf_recv_rules = {
24 .rule_head = SYS_SLIST_STATIC_INIT(&recv_rules.rule_head),
25 .lock = { },
26 };
27
28 #ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
29 struct npf_rule_list npf_local_in_recv_rules = {
30 .rule_head = SYS_SLIST_STATIC_INIT(&local_in_recv_rules.rule_head),
31 .lock = { },
32 };
33 #endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
34
35 #ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
36 struct npf_rule_list npf_ipv4_recv_rules = {
37 .rule_head = SYS_SLIST_STATIC_INIT(&ipv4_recv_rules.rule_head),
38 .lock = { },
39 };
40 #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */
41
42 #ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
43 struct npf_rule_list npf_ipv6_recv_rules = {
44 .rule_head = SYS_SLIST_STATIC_INIT(&ipv6_recv_rules.rule_head),
45 .lock = { },
46 };
47 #endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */
48
49 /*
50 * Helper function
51 */
get_ip_rules(uint8_t pf)52 static struct npf_rule_list *get_ip_rules(uint8_t pf)
53 {
54 switch (pf) {
55 case PF_INET:
56 #ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
57 return &npf_ipv4_recv_rules;
58 #endif
59 break;
60 case PF_INET6:
61 #ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
62 return &npf_ipv6_recv_rules;
63 #endif
64 break;
65 default:
66 return NULL;
67 }
68
69 return NULL;
70 }
71
72 /*
73 * Rule application
74 */
75
76 /*
77 * All tests must be true to return true.
78 * If no tests then it is true.
79 */
apply_tests(struct npf_rule * rule,struct net_pkt * pkt)80 static bool apply_tests(struct npf_rule *rule, struct net_pkt *pkt)
81 {
82 struct npf_test *test;
83 unsigned int i;
84 bool result;
85
86 for (i = 0; i < rule->nb_tests; i++) {
87 test = rule->tests[i];
88 result = test->fn(test, pkt);
89 NET_DBG("test %p result %d", test, result);
90 if (result == false) {
91 return false;
92 }
93 }
94
95 return true;
96 }
97
98 /*
99 * We return the specified result for the first rule whose tests are all true.
100 */
evaluate(sys_slist_t * rule_head,struct net_pkt * pkt)101 static enum net_verdict evaluate(sys_slist_t *rule_head, struct net_pkt *pkt)
102 {
103 struct npf_rule *rule;
104
105 NET_DBG("rule_head %p on pkt %p", rule_head, pkt);
106
107 if (sys_slist_is_empty(rule_head)) {
108 NET_DBG("no rules");
109 return NET_OK;
110 }
111
112 SYS_SLIST_FOR_EACH_CONTAINER(rule_head, rule, node) {
113 if (apply_tests(rule, pkt) == true) {
114 return rule->result;
115 }
116 }
117
118 NET_DBG("no matching rules from rule_head %p", rule_head);
119 return NET_DROP;
120 }
121
lock_evaluate(struct npf_rule_list * rules,struct net_pkt * pkt)122 static enum net_verdict lock_evaluate(struct npf_rule_list *rules, struct net_pkt *pkt)
123 {
124 k_spinlock_key_t key = k_spin_lock(&rules->lock);
125 enum net_verdict result = evaluate(&rules->rule_head, pkt);
126
127 k_spin_unlock(&rules->lock, key);
128 return result;
129 }
130
net_pkt_filter_send_ok(struct net_pkt * pkt)131 bool net_pkt_filter_send_ok(struct net_pkt *pkt)
132 {
133 enum net_verdict result = lock_evaluate(&npf_send_rules, pkt);
134
135 return result == NET_OK;
136 }
137
net_pkt_filter_recv_ok(struct net_pkt * pkt)138 bool net_pkt_filter_recv_ok(struct net_pkt *pkt)
139 {
140 enum net_verdict result = lock_evaluate(&npf_recv_rules, pkt);
141
142 return result == NET_OK;
143 }
144
145 #ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
net_pkt_filter_local_in_recv_ok(struct net_pkt * pkt)146 bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt)
147 {
148 enum net_verdict result = lock_evaluate(&npf_local_in_recv_rules, pkt);
149
150 return result == NET_OK;
151 }
152 #endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
153
154 #if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK)
net_pkt_filter_ip_recv_ok(struct net_pkt * pkt)155 bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt)
156 {
157 struct npf_rule_list *rules = get_ip_rules(net_pkt_family(pkt));
158
159 if (!rules) {
160 NET_DBG("no rules");
161 return true;
162 }
163
164 enum net_verdict result = lock_evaluate(rules, pkt);
165
166 return result == NET_OK;
167 }
168 #endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */
169
170 /*
171 * Rule management
172 */
173
npf_insert_rule(struct npf_rule_list * rules,struct npf_rule * rule)174 void npf_insert_rule(struct npf_rule_list *rules, struct npf_rule *rule)
175 {
176 k_spinlock_key_t key = k_spin_lock(&rules->lock);
177
178 NET_DBG("inserting rule %p into %p", rule, rules);
179 sys_slist_prepend(&rules->rule_head, &rule->node);
180
181 k_spin_unlock(&rules->lock, key);
182 }
183
npf_append_rule(struct npf_rule_list * rules,struct npf_rule * rule)184 void npf_append_rule(struct npf_rule_list *rules, struct npf_rule *rule)
185 {
186 __ASSERT(sys_slist_peek_tail(&rules->rule_head) != &npf_default_ok.node, "");
187 __ASSERT(sys_slist_peek_tail(&rules->rule_head) != &npf_default_drop.node, "");
188
189 k_spinlock_key_t key = k_spin_lock(&rules->lock);
190
191 NET_DBG("appending rule %p into %p", rule, rules);
192 sys_slist_append(&rules->rule_head, &rule->node);
193
194 k_spin_unlock(&rules->lock, key);
195 }
196
npf_remove_rule(struct npf_rule_list * rules,struct npf_rule * rule)197 bool npf_remove_rule(struct npf_rule_list *rules, struct npf_rule *rule)
198 {
199 k_spinlock_key_t key = k_spin_lock(&rules->lock);
200 bool result = sys_slist_find_and_remove(&rules->rule_head, &rule->node);
201
202 k_spin_unlock(&rules->lock, key);
203 NET_DBG("removing rule %p from %p: %d", rule, rules, result);
204 return result;
205 }
206
npf_remove_all_rules(struct npf_rule_list * rules)207 bool npf_remove_all_rules(struct npf_rule_list *rules)
208 {
209 k_spinlock_key_t key = k_spin_lock(&rules->lock);
210 bool result = !sys_slist_is_empty(&rules->rule_head);
211
212 if (result) {
213 sys_slist_init(&rules->rule_head);
214 NET_DBG("removing all rules from %p", rules);
215 }
216
217 k_spin_unlock(&rules->lock, key);
218 return result;
219 }
220
221 /*
222 * Default rule list terminations.
223 */
224
225 struct npf_rule npf_default_ok = {
226 .result = NET_OK,
227 };
228
229 struct npf_rule npf_default_drop = {
230 .result = NET_DROP,
231 };
232
233 /*
234 * Some simple generic conditions
235 */
236
npf_iface_match(struct npf_test * test,struct net_pkt * pkt)237 bool npf_iface_match(struct npf_test *test, struct net_pkt *pkt)
238 {
239 struct npf_test_iface *test_iface =
240 CONTAINER_OF(test, struct npf_test_iface, test);
241
242 return test_iface->iface == net_pkt_iface(pkt);
243 }
244
npf_iface_unmatch(struct npf_test * test,struct net_pkt * pkt)245 bool npf_iface_unmatch(struct npf_test *test, struct net_pkt *pkt)
246 {
247 return !npf_iface_match(test, pkt);
248 }
249
npf_orig_iface_match(struct npf_test * test,struct net_pkt * pkt)250 bool npf_orig_iface_match(struct npf_test *test, struct net_pkt *pkt)
251 {
252 struct npf_test_iface *test_iface =
253 CONTAINER_OF(test, struct npf_test_iface, test);
254
255 return test_iface->iface == net_pkt_orig_iface(pkt);
256 }
257
npf_orig_iface_unmatch(struct npf_test * test,struct net_pkt * pkt)258 bool npf_orig_iface_unmatch(struct npf_test *test, struct net_pkt *pkt)
259 {
260 return !npf_orig_iface_match(test, pkt);
261 }
262
npf_size_inbounds(struct npf_test * test,struct net_pkt * pkt)263 bool npf_size_inbounds(struct npf_test *test, struct net_pkt *pkt)
264 {
265 struct npf_test_size_bounds *bounds =
266 CONTAINER_OF(test, struct npf_test_size_bounds, test);
267 size_t pkt_size = net_pkt_get_len(pkt);
268
269 return pkt_size >= bounds->min && pkt_size <= bounds->max;
270 }
271
npf_ip_src_addr_match(struct npf_test * test,struct net_pkt * pkt)272 bool npf_ip_src_addr_match(struct npf_test *test, struct net_pkt *pkt)
273 {
274 struct npf_test_ip *test_ip =
275 CONTAINER_OF(test, struct npf_test_ip, test);
276 uint8_t pkt_family = net_pkt_family(pkt);
277
278 for (uint32_t ip_it = 0; ip_it < test_ip->ipaddr_num; ip_it++) {
279 if (IS_ENABLED(CONFIG_NET_IPV4) && pkt_family == AF_INET) {
280 struct in_addr *addr = (struct in_addr *)NET_IPV4_HDR(pkt)->src;
281
282 if (net_ipv4_addr_cmp(addr, &((struct in_addr *)test_ip->ipaddr)[ip_it])) {
283 return true;
284 }
285 } else if (IS_ENABLED(CONFIG_NET_IPV6) && pkt_family == AF_INET6) {
286 struct in6_addr *addr = (struct in6_addr *)NET_IPV6_HDR(pkt)->src;
287
288 if (net_ipv6_addr_cmp(addr, &((struct in6_addr *)test_ip->ipaddr)[ip_it])) {
289 return true;
290 }
291 }
292 }
293 return false;
294 }
295
npf_ip_src_addr_unmatch(struct npf_test * test,struct net_pkt * pkt)296 bool npf_ip_src_addr_unmatch(struct npf_test *test, struct net_pkt *pkt)
297 {
298 return !npf_ip_src_addr_match(test, pkt);
299 }
300