]>
Commit | Line | Data |
---|---|---|
c9626a2c PNA |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #include <linux/init.h> | |
3 | #include <linux/module.h> | |
4 | #include <linux/netfilter.h> | |
5 | #include <net/flow_offload.h> | |
6 | #include <net/netfilter/nf_tables.h> | |
7 | #include <net/netfilter/nf_tables_offload.h> | |
8 | #include <net/pkt_cls.h> | |
9 | ||
0854db2a FW |
10 | extern unsigned int nf_tables_net_id; |
11 | ||
c9626a2c PNA |
12 | static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions) |
13 | { | |
14 | struct nft_flow_rule *flow; | |
15 | ||
16 | flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL); | |
17 | if (!flow) | |
18 | return NULL; | |
19 | ||
20 | flow->rule = flow_rule_alloc(num_actions); | |
21 | if (!flow->rule) { | |
22 | kfree(flow); | |
23 | return NULL; | |
24 | } | |
25 | ||
26 | flow->rule->match.dissector = &flow->match.dissector; | |
27 | flow->rule->match.mask = &flow->match.mask; | |
28 | flow->rule->match.key = &flow->match.key; | |
29 | ||
30 | return flow; | |
31 | } | |
32 | ||
3c78e9e0 PNA |
33 | void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow, |
34 | enum flow_dissector_key_id addr_type) | |
35 | { | |
36 | struct nft_flow_match *match = &flow->match; | |
37 | struct nft_flow_key *mask = &match->mask; | |
38 | struct nft_flow_key *key = &match->key; | |
39 | ||
40 | if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) | |
41 | return; | |
42 | ||
43 | key->control.addr_type = addr_type; | |
44 | mask->control.addr_type = 0xffff; | |
45 | match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); | |
46 | match->dissector.offset[FLOW_DISSECTOR_KEY_CONTROL] = | |
47 | offsetof(struct nft_flow_key, control); | |
48 | } | |
49 | ||
783003f3 PNA |
50 | struct nft_offload_ethertype { |
51 | __be16 value; | |
52 | __be16 mask; | |
53 | }; | |
54 | ||
55 | static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx, | |
56 | struct nft_flow_rule *flow) | |
57 | { | |
58 | struct nft_flow_match *match = &flow->match; | |
59 | struct nft_offload_ethertype ethertype; | |
60 | ||
61 | if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL) && | |
62 | match->key.basic.n_proto != htons(ETH_P_8021Q) && | |
63 | match->key.basic.n_proto != htons(ETH_P_8021AD)) | |
64 | return; | |
65 | ||
66 | ethertype.value = match->key.basic.n_proto; | |
67 | ethertype.mask = match->mask.basic.n_proto; | |
68 | ||
69 | if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_VLAN) && | |
70 | (match->key.vlan.vlan_tpid == htons(ETH_P_8021Q) || | |
71 | match->key.vlan.vlan_tpid == htons(ETH_P_8021AD))) { | |
72 | match->key.basic.n_proto = match->key.cvlan.vlan_tpid; | |
73 | match->mask.basic.n_proto = match->mask.cvlan.vlan_tpid; | |
74 | match->key.cvlan.vlan_tpid = match->key.vlan.vlan_tpid; | |
75 | match->mask.cvlan.vlan_tpid = match->mask.vlan.vlan_tpid; | |
76 | match->key.vlan.vlan_tpid = ethertype.value; | |
77 | match->mask.vlan.vlan_tpid = ethertype.mask; | |
78 | match->dissector.offset[FLOW_DISSECTOR_KEY_CVLAN] = | |
79 | offsetof(struct nft_flow_key, cvlan); | |
80 | match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN); | |
81 | } else { | |
82 | match->key.basic.n_proto = match->key.vlan.vlan_tpid; | |
83 | match->mask.basic.n_proto = match->mask.vlan.vlan_tpid; | |
84 | match->key.vlan.vlan_tpid = ethertype.value; | |
85 | match->mask.vlan.vlan_tpid = ethertype.mask; | |
86 | match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] = | |
87 | offsetof(struct nft_flow_key, vlan); | |
88 | match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_VLAN); | |
89 | } | |
90 | } | |
91 | ||
be2861dc PNA |
92 | struct nft_flow_rule *nft_flow_rule_create(struct net *net, |
93 | const struct nft_rule *rule) | |
c9626a2c | 94 | { |
b44492af | 95 | struct nft_offload_ctx *ctx; |
c9626a2c PNA |
96 | struct nft_flow_rule *flow; |
97 | int num_actions = 0, err; | |
98 | struct nft_expr *expr; | |
99 | ||
100 | expr = nft_expr_first(rule); | |
31cc578a | 101 | while (nft_expr_more(rule, expr)) { |
c9626a2c PNA |
102 | if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION) |
103 | num_actions++; | |
104 | ||
105 | expr = nft_expr_next(expr); | |
106 | } | |
107 | ||
81ec6107 PNA |
108 | if (num_actions == 0) |
109 | return ERR_PTR(-EOPNOTSUPP); | |
110 | ||
c9626a2c PNA |
111 | flow = nft_flow_rule_alloc(num_actions); |
112 | if (!flow) | |
113 | return ERR_PTR(-ENOMEM); | |
114 | ||
115 | expr = nft_expr_first(rule); | |
b44492af AB |
116 | |
117 | ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL); | |
118 | if (!ctx) { | |
119 | err = -ENOMEM; | |
120 | goto err_out; | |
121 | } | |
be2861dc | 122 | ctx->net = net; |
b44492af AB |
123 | ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC; |
124 | ||
31cc578a | 125 | while (nft_expr_more(rule, expr)) { |
c9626a2c PNA |
126 | if (!expr->ops->offload) { |
127 | err = -EOPNOTSUPP; | |
128 | goto err_out; | |
129 | } | |
b44492af | 130 | err = expr->ops->offload(ctx, flow, expr); |
c9626a2c PNA |
131 | if (err < 0) |
132 | goto err_out; | |
133 | ||
134 | expr = nft_expr_next(expr); | |
135 | } | |
783003f3 PNA |
136 | nft_flow_rule_transfer_vlan(ctx, flow); |
137 | ||
b44492af AB |
138 | flow->proto = ctx->dep.l3num; |
139 | kfree(ctx); | |
c9626a2c PNA |
140 | |
141 | return flow; | |
142 | err_out: | |
b44492af | 143 | kfree(ctx); |
c9626a2c PNA |
144 | nft_flow_rule_destroy(flow); |
145 | ||
146 | return ERR_PTR(err); | |
147 | } | |
148 | ||
149 | void nft_flow_rule_destroy(struct nft_flow_rule *flow) | |
150 | { | |
be2861dc PNA |
151 | struct flow_action_entry *entry; |
152 | int i; | |
153 | ||
154 | flow_action_for_each(i, entry, &flow->rule->action) { | |
155 | switch (entry->id) { | |
156 | case FLOW_ACTION_REDIRECT: | |
157 | case FLOW_ACTION_MIRRED: | |
158 | dev_put(entry->dev); | |
159 | break; | |
160 | default: | |
161 | break; | |
162 | } | |
163 | } | |
c9626a2c PNA |
164 | kfree(flow->rule); |
165 | kfree(flow); | |
166 | } | |
167 | ||
168 | void nft_offload_set_dependency(struct nft_offload_ctx *ctx, | |
169 | enum nft_offload_dep_type type) | |
170 | { | |
171 | ctx->dep.type = type; | |
172 | } | |
173 | ||
174 | void nft_offload_update_dependency(struct nft_offload_ctx *ctx, | |
175 | const void *data, u32 len) | |
176 | { | |
177 | switch (ctx->dep.type) { | |
178 | case NFT_OFFLOAD_DEP_NETWORK: | |
179 | WARN_ON(len != sizeof(__u16)); | |
180 | memcpy(&ctx->dep.l3num, data, sizeof(__u16)); | |
181 | break; | |
182 | case NFT_OFFLOAD_DEP_TRANSPORT: | |
183 | WARN_ON(len != sizeof(__u8)); | |
184 | memcpy(&ctx->dep.protonum, data, sizeof(__u8)); | |
185 | break; | |
186 | default: | |
187 | break; | |
188 | } | |
189 | ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC; | |
190 | } | |
191 | ||
192 | static void nft_flow_offload_common_init(struct flow_cls_common_offload *common, | |
3bc158f8 PNA |
193 | __be16 proto, int priority, |
194 | struct netlink_ext_ack *extack) | |
c9626a2c PNA |
195 | { |
196 | common->protocol = proto; | |
3bc158f8 | 197 | common->prio = priority; |
c9626a2c PNA |
198 | common->extack = extack; |
199 | } | |
200 | ||
b5828880 PNA |
201 | static int nft_setup_cb_call(enum tc_setup_type type, void *type_data, |
202 | struct list_head *cb_list) | |
c9626a2c PNA |
203 | { |
204 | struct flow_block_cb *block_cb; | |
205 | int err; | |
206 | ||
b5828880 | 207 | list_for_each_entry(block_cb, cb_list, list) { |
c9626a2c PNA |
208 | err = block_cb->cb(type, type_data, block_cb->cb_priv); |
209 | if (err < 0) | |
210 | return err; | |
211 | } | |
212 | return 0; | |
213 | } | |
214 | ||
3bc158f8 PNA |
215 | int nft_chain_offload_priority(struct nft_base_chain *basechain) |
216 | { | |
217 | if (basechain->ops.priority <= 0 || | |
218 | basechain->ops.priority > USHRT_MAX) | |
219 | return -1; | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
c5d27527 PNA |
224 | static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow, |
225 | const struct nft_base_chain *basechain, | |
226 | const struct nft_rule *rule, | |
227 | const struct nft_flow_rule *flow, | |
be193f5e | 228 | struct netlink_ext_ack *extack, |
c5d27527 PNA |
229 | enum flow_cls_command command) |
230 | { | |
c5d27527 PNA |
231 | __be16 proto = ETH_P_ALL; |
232 | ||
233 | memset(cls_flow, 0, sizeof(*cls_flow)); | |
234 | ||
235 | if (flow) | |
236 | proto = flow->proto; | |
237 | ||
238 | nft_flow_offload_common_init(&cls_flow->common, proto, | |
be193f5e | 239 | basechain->ops.priority, extack); |
c5d27527 PNA |
240 | cls_flow->command = command; |
241 | cls_flow->cookie = (unsigned long) rule; | |
242 | if (flow) | |
243 | cls_flow->rule = flow->rule; | |
244 | } | |
245 | ||
b72920f6 PNA |
246 | static int nft_flow_offload_cmd(const struct nft_chain *chain, |
247 | const struct nft_rule *rule, | |
248 | struct nft_flow_rule *flow, | |
249 | enum flow_cls_command command, | |
250 | struct flow_cls_offload *cls_flow) | |
c9626a2c | 251 | { |
be193f5e | 252 | struct netlink_ext_ack extack = {}; |
c9626a2c | 253 | struct nft_base_chain *basechain; |
c9626a2c | 254 | |
e211aab7 | 255 | if (!nft_is_base_chain(chain)) |
c9626a2c PNA |
256 | return -EOPNOTSUPP; |
257 | ||
e211aab7 | 258 | basechain = nft_base_chain(chain); |
b72920f6 | 259 | nft_flow_cls_offload_setup(cls_flow, basechain, rule, flow, &extack, |
be193f5e | 260 | command); |
c9626a2c | 261 | |
b72920f6 | 262 | return nft_setup_cb_call(TC_SETUP_CLSFLOWER, cls_flow, |
b5828880 | 263 | &basechain->flow_block.cb_list); |
c9626a2c PNA |
264 | } |
265 | ||
b72920f6 PNA |
266 | static int nft_flow_offload_rule(const struct nft_chain *chain, |
267 | struct nft_rule *rule, | |
268 | struct nft_flow_rule *flow, | |
269 | enum flow_cls_command command) | |
270 | { | |
271 | struct flow_cls_offload cls_flow; | |
272 | ||
273 | return nft_flow_offload_cmd(chain, rule, flow, command, &cls_flow); | |
274 | } | |
275 | ||
276 | int nft_flow_rule_stats(const struct nft_chain *chain, | |
277 | const struct nft_rule *rule) | |
278 | { | |
279 | struct flow_cls_offload cls_flow = {}; | |
280 | struct nft_expr *expr, *next; | |
281 | int err; | |
282 | ||
283 | err = nft_flow_offload_cmd(chain, rule, NULL, FLOW_CLS_STATS, | |
284 | &cls_flow); | |
285 | if (err < 0) | |
286 | return err; | |
287 | ||
288 | nft_rule_for_each_expr(expr, next, rule) { | |
289 | if (expr->ops->offload_stats) | |
290 | expr->ops->offload_stats(expr, &cls_flow.stats); | |
291 | } | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
c9626a2c PNA |
296 | static int nft_flow_offload_bind(struct flow_block_offload *bo, |
297 | struct nft_base_chain *basechain) | |
298 | { | |
14bfb13f | 299 | list_splice(&bo->cb_list, &basechain->flow_block.cb_list); |
c9626a2c PNA |
300 | return 0; |
301 | } | |
302 | ||
303 | static int nft_flow_offload_unbind(struct flow_block_offload *bo, | |
304 | struct nft_base_chain *basechain) | |
305 | { | |
306 | struct flow_block_cb *block_cb, *next; | |
bbaef955 | 307 | struct flow_cls_offload cls_flow; |
be193f5e | 308 | struct netlink_ext_ack extack; |
bbaef955 PNA |
309 | struct nft_chain *chain; |
310 | struct nft_rule *rule; | |
311 | ||
312 | chain = &basechain->chain; | |
313 | list_for_each_entry(rule, &chain->rules, list) { | |
be193f5e | 314 | memset(&extack, 0, sizeof(extack)); |
bbaef955 | 315 | nft_flow_cls_offload_setup(&cls_flow, basechain, rule, NULL, |
be193f5e | 316 | &extack, FLOW_CLS_DESTROY); |
bbaef955 PNA |
317 | nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow, &bo->cb_list); |
318 | } | |
c9626a2c PNA |
319 | |
320 | list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) { | |
321 | list_del(&block_cb->list); | |
322 | flow_block_cb_free(block_cb); | |
323 | } | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
9a32669f | 328 | static int nft_block_setup(struct nft_base_chain *basechain, |
329 | struct flow_block_offload *bo, | |
330 | enum flow_block_command cmd) | |
331 | { | |
332 | int err; | |
333 | ||
334 | switch (cmd) { | |
335 | case FLOW_BLOCK_BIND: | |
336 | err = nft_flow_offload_bind(bo, basechain); | |
337 | break; | |
338 | case FLOW_BLOCK_UNBIND: | |
339 | err = nft_flow_offload_unbind(bo, basechain); | |
340 | break; | |
341 | default: | |
342 | WARN_ON_ONCE(1); | |
343 | err = -EOPNOTSUPP; | |
344 | } | |
345 | ||
346 | return err; | |
347 | } | |
348 | ||
75ceaf86 PNA |
349 | static void nft_flow_block_offload_init(struct flow_block_offload *bo, |
350 | struct net *net, | |
351 | enum flow_block_command cmd, | |
352 | struct nft_base_chain *basechain, | |
353 | struct netlink_ext_ack *extack) | |
354 | { | |
355 | memset(bo, 0, sizeof(*bo)); | |
356 | bo->net = net; | |
357 | bo->block = &basechain->flow_block; | |
358 | bo->command = cmd; | |
359 | bo->binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS; | |
360 | bo->extack = extack; | |
361 | INIT_LIST_HEAD(&bo->cb_list); | |
362 | } | |
363 | ||
9a32669f | 364 | static int nft_block_offload_cmd(struct nft_base_chain *chain, |
365 | struct net_device *dev, | |
366 | enum flow_block_command cmd) | |
367 | { | |
368 | struct netlink_ext_ack extack = {}; | |
75ceaf86 | 369 | struct flow_block_offload bo; |
9a32669f | 370 | int err; |
371 | ||
75ceaf86 | 372 | nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack); |
9a32669f | 373 | |
374 | err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); | |
375 | if (err < 0) | |
376 | return err; | |
377 | ||
378 | return nft_block_setup(chain, &bo, cmd); | |
379 | } | |
380 | ||
0fdcf78d PNA |
381 | static void nft_indr_block_cleanup(struct flow_block_cb *block_cb) |
382 | { | |
383 | struct nft_base_chain *basechain = block_cb->indr.data; | |
384 | struct net_device *dev = block_cb->indr.dev; | |
385 | struct netlink_ext_ack extack = {}; | |
0854db2a | 386 | struct nftables_pernet *nft_net; |
0fdcf78d PNA |
387 | struct net *net = dev_net(dev); |
388 | struct flow_block_offload bo; | |
389 | ||
390 | nft_flow_block_offload_init(&bo, dev_net(dev), FLOW_BLOCK_UNBIND, | |
391 | basechain, &extack); | |
0854db2a FW |
392 | nft_net = net_generic(net, nf_tables_net_id); |
393 | mutex_lock(&nft_net->commit_mutex); | |
a1db2178 | 394 | list_del(&block_cb->driver_list); |
0fdcf78d PNA |
395 | list_move(&block_cb->list, &bo.cb_list); |
396 | nft_flow_offload_unbind(&bo, basechain); | |
0854db2a | 397 | mutex_unlock(&nft_net->commit_mutex); |
0fdcf78d PNA |
398 | } |
399 | ||
400 | static int nft_indr_block_offload_cmd(struct nft_base_chain *basechain, | |
9a32669f | 401 | struct net_device *dev, |
402 | enum flow_block_command cmd) | |
403 | { | |
9a32669f | 404 | struct netlink_ext_ack extack = {}; |
75ceaf86 | 405 | struct flow_block_offload bo; |
0fdcf78d | 406 | int err; |
9a32669f | 407 | |
0fdcf78d | 408 | nft_flow_block_offload_init(&bo, dev_net(dev), cmd, basechain, &extack); |
9a32669f | 409 | |
c40f4e50 | 410 | err = flow_indr_dev_setup_offload(dev, NULL, TC_SETUP_BLOCK, basechain, &bo, |
0fdcf78d PNA |
411 | nft_indr_block_cleanup); |
412 | if (err < 0) | |
413 | return err; | |
9a32669f | 414 | |
415 | if (list_empty(&bo.cb_list)) | |
416 | return -EOPNOTSUPP; | |
417 | ||
0fdcf78d | 418 | return nft_block_setup(basechain, &bo, cmd); |
9a32669f | 419 | } |
420 | ||
6df5490f PNA |
421 | static int nft_chain_offload_cmd(struct nft_base_chain *basechain, |
422 | struct net_device *dev, | |
423 | enum flow_block_command cmd) | |
424 | { | |
425 | int err; | |
426 | ||
427 | if (dev->netdev_ops->ndo_setup_tc) | |
428 | err = nft_block_offload_cmd(basechain, dev, cmd); | |
429 | else | |
430 | err = nft_indr_block_offload_cmd(basechain, dev, cmd); | |
431 | ||
432 | return err; | |
433 | } | |
434 | ||
ead3952e | 435 | static int nft_flow_block_chain(struct nft_base_chain *basechain, |
d54725cd | 436 | const struct net_device *this_dev, |
ead3952e PNA |
437 | enum flow_block_command cmd) |
438 | { | |
d54725cd PNA |
439 | struct net_device *dev; |
440 | struct nft_hook *hook; | |
671312e1 | 441 | int err, i = 0; |
d54725cd PNA |
442 | |
443 | list_for_each_entry(hook, &basechain->hook_list, list) { | |
444 | dev = hook->ops.dev; | |
445 | if (this_dev && this_dev != dev) | |
446 | continue; | |
ead3952e | 447 | |
6df5490f | 448 | err = nft_chain_offload_cmd(basechain, dev, cmd); |
671312e1 PNA |
449 | if (err < 0 && cmd == FLOW_BLOCK_BIND) { |
450 | if (!this_dev) | |
451 | goto err_flow_block; | |
452 | ||
d54725cd | 453 | return err; |
671312e1 PNA |
454 | } |
455 | i++; | |
d54725cd PNA |
456 | } |
457 | ||
458 | return 0; | |
671312e1 PNA |
459 | |
460 | err_flow_block: | |
461 | list_for_each_entry(hook, &basechain->hook_list, list) { | |
462 | if (i-- <= 0) | |
463 | break; | |
464 | ||
465 | dev = hook->ops.dev; | |
466 | nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND); | |
467 | } | |
468 | return err; | |
ead3952e PNA |
469 | } |
470 | ||
d54725cd | 471 | static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy, |
c9626a2c PNA |
472 | enum flow_block_command cmd) |
473 | { | |
c9626a2c | 474 | struct nft_base_chain *basechain; |
8fc618c5 | 475 | u8 policy; |
c9626a2c PNA |
476 | |
477 | if (!nft_is_base_chain(chain)) | |
478 | return -EOPNOTSUPP; | |
479 | ||
480 | basechain = nft_base_chain(chain); | |
8fc618c5 | 481 | policy = ppolicy ? *ppolicy : basechain->policy; |
482 | ||
c9626a2c | 483 | /* Only default policy to accept is supported for now. */ |
ff175d0b | 484 | if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP) |
c9626a2c PNA |
485 | return -EOPNOTSUPP; |
486 | ||
d54725cd | 487 | return nft_flow_block_chain(basechain, NULL, cmd); |
c9626a2c PNA |
488 | } |
489 | ||
63b48c73 PNA |
490 | static void nft_flow_rule_offload_abort(struct net *net, |
491 | struct nft_trans *trans) | |
492 | { | |
0854db2a | 493 | struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); |
63b48c73 PNA |
494 | int err = 0; |
495 | ||
0854db2a | 496 | list_for_each_entry_continue_reverse(trans, &nft_net->commit_list, list) { |
63b48c73 PNA |
497 | if (trans->ctx.family != NFPROTO_NETDEV) |
498 | continue; | |
499 | ||
500 | switch (trans->msg_type) { | |
501 | case NFT_MSG_NEWCHAIN: | |
502 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) || | |
503 | nft_trans_chain_update(trans)) | |
504 | continue; | |
505 | ||
506 | err = nft_flow_offload_chain(trans->ctx.chain, NULL, | |
507 | FLOW_BLOCK_UNBIND); | |
508 | break; | |
509 | case NFT_MSG_DELCHAIN: | |
510 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
511 | continue; | |
512 | ||
513 | err = nft_flow_offload_chain(trans->ctx.chain, NULL, | |
514 | FLOW_BLOCK_BIND); | |
515 | break; | |
516 | case NFT_MSG_NEWRULE: | |
517 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
518 | continue; | |
519 | ||
520 | err = nft_flow_offload_rule(trans->ctx.chain, | |
521 | nft_trans_rule(trans), | |
522 | NULL, FLOW_CLS_DESTROY); | |
523 | break; | |
524 | case NFT_MSG_DELRULE: | |
525 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
526 | continue; | |
527 | ||
528 | err = nft_flow_offload_rule(trans->ctx.chain, | |
529 | nft_trans_rule(trans), | |
530 | nft_trans_flow_rule(trans), | |
531 | FLOW_CLS_REPLACE); | |
532 | break; | |
533 | } | |
534 | ||
535 | if (WARN_ON_ONCE(err)) | |
536 | break; | |
537 | } | |
538 | } | |
539 | ||
c9626a2c PNA |
540 | int nft_flow_rule_offload_commit(struct net *net) |
541 | { | |
0854db2a | 542 | struct nftables_pernet *nft_net = net_generic(net, nf_tables_net_id); |
c9626a2c PNA |
543 | struct nft_trans *trans; |
544 | int err = 0; | |
8fc618c5 | 545 | u8 policy; |
c9626a2c | 546 | |
0854db2a | 547 | list_for_each_entry(trans, &nft_net->commit_list, list) { |
c9626a2c PNA |
548 | if (trans->ctx.family != NFPROTO_NETDEV) |
549 | continue; | |
550 | ||
551 | switch (trans->msg_type) { | |
552 | case NFT_MSG_NEWCHAIN: | |
88c74984 PNA |
553 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) || |
554 | nft_trans_chain_update(trans)) | |
c9626a2c PNA |
555 | continue; |
556 | ||
8fc618c5 | 557 | policy = nft_trans_chain_policy(trans); |
558 | err = nft_flow_offload_chain(trans->ctx.chain, &policy, | |
559 | FLOW_BLOCK_BIND); | |
c9626a2c PNA |
560 | break; |
561 | case NFT_MSG_DELCHAIN: | |
562 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
563 | continue; | |
564 | ||
8fc618c5 | 565 | policy = nft_trans_chain_policy(trans); |
566 | err = nft_flow_offload_chain(trans->ctx.chain, &policy, | |
085461c8 | 567 | FLOW_BLOCK_UNBIND); |
c9626a2c PNA |
568 | break; |
569 | case NFT_MSG_NEWRULE: | |
570 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
571 | continue; | |
572 | ||
573 | if (trans->ctx.flags & NLM_F_REPLACE || | |
23403cd8 PNA |
574 | !(trans->ctx.flags & NLM_F_APPEND)) { |
575 | err = -EOPNOTSUPP; | |
576 | break; | |
577 | } | |
e211aab7 | 578 | err = nft_flow_offload_rule(trans->ctx.chain, |
579 | nft_trans_rule(trans), | |
580 | nft_trans_flow_rule(trans), | |
581 | FLOW_CLS_REPLACE); | |
c9626a2c PNA |
582 | break; |
583 | case NFT_MSG_DELRULE: | |
584 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
585 | continue; | |
586 | ||
e211aab7 | 587 | err = nft_flow_offload_rule(trans->ctx.chain, |
588 | nft_trans_rule(trans), | |
6ca61c7a | 589 | NULL, FLOW_CLS_DESTROY); |
c9626a2c PNA |
590 | break; |
591 | } | |
592 | ||
63b48c73 PNA |
593 | if (err) { |
594 | nft_flow_rule_offload_abort(net, trans); | |
23403cd8 | 595 | break; |
63b48c73 | 596 | } |
23403cd8 PNA |
597 | } |
598 | ||
0854db2a | 599 | list_for_each_entry(trans, &nft_net->commit_list, list) { |
23403cd8 PNA |
600 | if (trans->ctx.family != NFPROTO_NETDEV) |
601 | continue; | |
602 | ||
603 | switch (trans->msg_type) { | |
604 | case NFT_MSG_NEWRULE: | |
63b48c73 | 605 | case NFT_MSG_DELRULE: |
23403cd8 PNA |
606 | if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)) |
607 | continue; | |
608 | ||
609 | nft_flow_rule_destroy(nft_trans_flow_rule(trans)); | |
610 | break; | |
611 | default: | |
612 | break; | |
613 | } | |
c9626a2c PNA |
614 | } |
615 | ||
616 | return err; | |
617 | } | |
9a32669f | 618 | |
0854db2a FW |
619 | static struct nft_chain *__nft_offload_get_chain(const struct nftables_pernet *nft_net, |
620 | struct net_device *dev) | |
9a32669f | 621 | { |
504882db | 622 | struct nft_base_chain *basechain; |
d54725cd | 623 | struct nft_hook *hook, *found; |
9a32669f | 624 | const struct nft_table *table; |
504882db | 625 | struct nft_chain *chain; |
9a32669f | 626 | |
0854db2a | 627 | list_for_each_entry(table, &nft_net->tables, list) { |
9a32669f | 628 | if (table->family != NFPROTO_NETDEV) |
629 | continue; | |
630 | ||
504882db | 631 | list_for_each_entry(chain, &table->chains, list) { |
632 | if (!nft_is_base_chain(chain) || | |
633 | !(chain->flags & NFT_CHAIN_HW_OFFLOAD)) | |
634 | continue; | |
635 | ||
d54725cd | 636 | found = NULL; |
504882db | 637 | basechain = nft_base_chain(chain); |
d54725cd PNA |
638 | list_for_each_entry(hook, &basechain->hook_list, list) { |
639 | if (hook->ops.dev != dev) | |
640 | continue; | |
641 | ||
642 | found = hook; | |
643 | break; | |
644 | } | |
645 | if (!found) | |
504882db | 646 | continue; |
647 | ||
648 | return chain; | |
9a32669f | 649 | } |
650 | } | |
504882db | 651 | |
652 | return NULL; | |
653 | } | |
654 | ||
06d392cb | 655 | static int nft_offload_netdev_event(struct notifier_block *this, |
656 | unsigned long event, void *ptr) | |
657 | { | |
658 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | |
0854db2a | 659 | struct nftables_pernet *nft_net; |
06d392cb | 660 | struct net *net = dev_net(dev); |
661 | struct nft_chain *chain; | |
662 | ||
d1f4c966 | 663 | if (event != NETDEV_UNREGISTER) |
664 | return NOTIFY_DONE; | |
665 | ||
0854db2a FW |
666 | nft_net = net_generic(net, nf_tables_net_id); |
667 | mutex_lock(&nft_net->commit_mutex); | |
668 | chain = __nft_offload_get_chain(nft_net, dev); | |
06d392cb | 669 | if (chain) |
bbaef955 PNA |
670 | nft_flow_block_chain(nft_base_chain(chain), dev, |
671 | FLOW_BLOCK_UNBIND); | |
672 | ||
0854db2a | 673 | mutex_unlock(&nft_net->commit_mutex); |
06d392cb | 674 | |
675 | return NOTIFY_DONE; | |
676 | } | |
677 | ||
06d392cb | 678 | static struct notifier_block nft_offload_netdev_notifier = { |
679 | .notifier_call = nft_offload_netdev_event, | |
680 | }; | |
681 | ||
682 | int nft_offload_init(void) | |
3474a2c6 | 683 | { |
709ffbe1 | 684 | return register_netdevice_notifier(&nft_offload_netdev_notifier); |
3474a2c6 PNA |
685 | } |
686 | ||
687 | void nft_offload_exit(void) | |
688 | { | |
06d392cb | 689 | unregister_netdevice_notifier(&nft_offload_netdev_notifier); |
3474a2c6 | 690 | } |