Blame view

net/netfilter/nf_tables_offload.c 14.1 KB
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  /* SPDX-License-Identifier: GPL-2.0 */
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/netfilter.h>
  #include <net/flow_offload.h>
  #include <net/netfilter/nf_tables.h>
  #include <net/netfilter/nf_tables_offload.h>
  #include <net/pkt_cls.h>
  
  static struct nft_flow_rule *nft_flow_rule_alloc(int num_actions)
  {
  	struct nft_flow_rule *flow;
  
  	flow = kzalloc(sizeof(struct nft_flow_rule), GFP_KERNEL);
  	if (!flow)
  		return NULL;
  
  	flow->rule = flow_rule_alloc(num_actions);
  	if (!flow->rule) {
  		kfree(flow);
  		return NULL;
  	}
  
  	flow->rule->match.dissector	= &flow->match.dissector;
  	flow->rule->match.mask		= &flow->match.mask;
  	flow->rule->match.key		= &flow->match.key;
  
  	return flow;
  }
3c78e9e0d   Pablo Neira Ayuso   netfilter: nftabl...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow,
  				 enum flow_dissector_key_id addr_type)
  {
  	struct nft_flow_match *match = &flow->match;
  	struct nft_flow_key *mask = &match->mask;
  	struct nft_flow_key *key = &match->key;
  
  	if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL))
  		return;
  
  	key->control.addr_type = addr_type;
  	mask->control.addr_type = 0xffff;
  	match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL);
  	match->dissector.offset[FLOW_DISSECTOR_KEY_CONTROL] =
  		offsetof(struct nft_flow_key, control);
  }
be2861dc3   Pablo Neira Ayuso   netfilter: nft_{f...
46
47
  struct nft_flow_rule *nft_flow_rule_create(struct net *net,
  					   const struct nft_rule *rule)
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
48
  {
b44492afd   Arnd Bergmann   netfilter: nf_tab...
49
  	struct nft_offload_ctx *ctx;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
50
51
52
53
54
  	struct nft_flow_rule *flow;
  	int num_actions = 0, err;
  	struct nft_expr *expr;
  
  	expr = nft_expr_first(rule);
31cc578ae   Saeed Mirzamohammadi   netfilter: nftabl...
55
  	while (nft_expr_more(rule, expr)) {
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
56
57
58
59
60
  		if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION)
  			num_actions++;
  
  		expr = nft_expr_next(expr);
  	}
81ec61074   Pablo Neira Ayuso   netfilter: nf_tab...
61
62
  	if (num_actions == 0)
  		return ERR_PTR(-EOPNOTSUPP);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
63
64
65
66
67
  	flow = nft_flow_rule_alloc(num_actions);
  	if (!flow)
  		return ERR_PTR(-ENOMEM);
  
  	expr = nft_expr_first(rule);
b44492afd   Arnd Bergmann   netfilter: nf_tab...
68
69
70
71
72
73
  
  	ctx = kzalloc(sizeof(struct nft_offload_ctx), GFP_KERNEL);
  	if (!ctx) {
  		err = -ENOMEM;
  		goto err_out;
  	}
be2861dc3   Pablo Neira Ayuso   netfilter: nft_{f...
74
  	ctx->net = net;
b44492afd   Arnd Bergmann   netfilter: nf_tab...
75
  	ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
31cc578ae   Saeed Mirzamohammadi   netfilter: nftabl...
76
  	while (nft_expr_more(rule, expr)) {
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
77
78
79
80
  		if (!expr->ops->offload) {
  			err = -EOPNOTSUPP;
  			goto err_out;
  		}
b44492afd   Arnd Bergmann   netfilter: nf_tab...
81
  		err = expr->ops->offload(ctx, flow, expr);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
82
83
84
85
86
  		if (err < 0)
  			goto err_out;
  
  		expr = nft_expr_next(expr);
  	}
b44492afd   Arnd Bergmann   netfilter: nf_tab...
87
88
  	flow->proto = ctx->dep.l3num;
  	kfree(ctx);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
89
90
91
  
  	return flow;
  err_out:
b44492afd   Arnd Bergmann   netfilter: nf_tab...
92
  	kfree(ctx);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
93
94
95
96
97
98
99
  	nft_flow_rule_destroy(flow);
  
  	return ERR_PTR(err);
  }
  
  void nft_flow_rule_destroy(struct nft_flow_rule *flow)
  {
be2861dc3   Pablo Neira Ayuso   netfilter: nft_{f...
100
101
102
103
104
105
106
107
108
109
110
111
112
  	struct flow_action_entry *entry;
  	int i;
  
  	flow_action_for_each(i, entry, &flow->rule->action) {
  		switch (entry->id) {
  		case FLOW_ACTION_REDIRECT:
  		case FLOW_ACTION_MIRRED:
  			dev_put(entry->dev);
  			break;
  		default:
  			break;
  		}
  	}
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  	kfree(flow->rule);
  	kfree(flow);
  }
  
  void nft_offload_set_dependency(struct nft_offload_ctx *ctx,
  				enum nft_offload_dep_type type)
  {
  	ctx->dep.type = type;
  }
  
  void nft_offload_update_dependency(struct nft_offload_ctx *ctx,
  				   const void *data, u32 len)
  {
  	switch (ctx->dep.type) {
  	case NFT_OFFLOAD_DEP_NETWORK:
  		WARN_ON(len != sizeof(__u16));
  		memcpy(&ctx->dep.l3num, data, sizeof(__u16));
  		break;
  	case NFT_OFFLOAD_DEP_TRANSPORT:
  		WARN_ON(len != sizeof(__u8));
  		memcpy(&ctx->dep.protonum, data, sizeof(__u8));
  		break;
  	default:
  		break;
  	}
  	ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC;
  }
  
  static void nft_flow_offload_common_init(struct flow_cls_common_offload *common,
3bc158f8d   Pablo Neira Ayuso   netfilter: nf_tab...
142
143
  					 __be16 proto, int priority,
  					 struct netlink_ext_ack *extack)
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
144
145
  {
  	common->protocol = proto;
3bc158f8d   Pablo Neira Ayuso   netfilter: nf_tab...
146
  	common->prio = priority;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
147
148
  	common->extack = extack;
  }
b58288804   Pablo Neira Ayuso   netfilter: nf_tab...
149
150
  static int nft_setup_cb_call(enum tc_setup_type type, void *type_data,
  			     struct list_head *cb_list)
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
151
152
153
  {
  	struct flow_block_cb *block_cb;
  	int err;
b58288804   Pablo Neira Ayuso   netfilter: nf_tab...
154
  	list_for_each_entry(block_cb, cb_list, list) {
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
155
156
157
158
159
160
  		err = block_cb->cb(type, type_data, block_cb->cb_priv);
  		if (err < 0)
  			return err;
  	}
  	return 0;
  }
3bc158f8d   Pablo Neira Ayuso   netfilter: nf_tab...
161
162
163
164
165
166
167
168
  int nft_chain_offload_priority(struct nft_base_chain *basechain)
  {
  	if (basechain->ops.priority <= 0 ||
  	    basechain->ops.priority > USHRT_MAX)
  		return -1;
  
  	return 0;
  }
c5d275276   Pablo Neira Ayuso   netfilter: nf_tab...
169
170
171
172
  static void nft_flow_cls_offload_setup(struct flow_cls_offload *cls_flow,
  				       const struct nft_base_chain *basechain,
  				       const struct nft_rule *rule,
  				       const struct nft_flow_rule *flow,
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
173
  				       struct netlink_ext_ack *extack,
c5d275276   Pablo Neira Ayuso   netfilter: nf_tab...
174
175
  				       enum flow_cls_command command)
  {
c5d275276   Pablo Neira Ayuso   netfilter: nf_tab...
176
177
178
179
180
181
182
183
  	__be16 proto = ETH_P_ALL;
  
  	memset(cls_flow, 0, sizeof(*cls_flow));
  
  	if (flow)
  		proto = flow->proto;
  
  	nft_flow_offload_common_init(&cls_flow->common, proto,
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
184
  				     basechain->ops.priority, extack);
c5d275276   Pablo Neira Ayuso   netfilter: nf_tab...
185
186
187
188
189
  	cls_flow->command = command;
  	cls_flow->cookie = (unsigned long) rule;
  	if (flow)
  		cls_flow->rule = flow->rule;
  }
e211aab73   wenxu   netfilter: nf_tab...
190
191
192
  static int nft_flow_offload_rule(struct nft_chain *chain,
  				 struct nft_rule *rule,
  				 struct nft_flow_rule *flow,
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
193
194
  				 enum flow_cls_command command)
  {
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
195
  	struct netlink_ext_ack extack = {};
c5d275276   Pablo Neira Ayuso   netfilter: nf_tab...
196
  	struct flow_cls_offload cls_flow;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
197
  	struct nft_base_chain *basechain;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
198

e211aab73   wenxu   netfilter: nf_tab...
199
  	if (!nft_is_base_chain(chain))
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
200
  		return -EOPNOTSUPP;
e211aab73   wenxu   netfilter: nf_tab...
201
  	basechain = nft_base_chain(chain);
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
202
203
  	nft_flow_cls_offload_setup(&cls_flow, basechain, rule, flow, &extack,
  				   command);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
204

b58288804   Pablo Neira Ayuso   netfilter: nf_tab...
205
206
  	return nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow,
  				 &basechain->flow_block.cb_list);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
207
208
209
210
211
  }
  
  static int nft_flow_offload_bind(struct flow_block_offload *bo,
  				 struct nft_base_chain *basechain)
  {
14bfb13f0   Pablo Neira Ayuso   net: flow_offload...
212
  	list_splice(&bo->cb_list, &basechain->flow_block.cb_list);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
213
214
215
216
217
218
219
  	return 0;
  }
  
  static int nft_flow_offload_unbind(struct flow_block_offload *bo,
  				   struct nft_base_chain *basechain)
  {
  	struct flow_block_cb *block_cb, *next;
bbaef955a   Pablo Neira Ayuso   netfilter: nf_tab...
220
  	struct flow_cls_offload cls_flow;
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
221
  	struct netlink_ext_ack extack;
bbaef955a   Pablo Neira Ayuso   netfilter: nf_tab...
222
223
224
225
226
  	struct nft_chain *chain;
  	struct nft_rule *rule;
  
  	chain = &basechain->chain;
  	list_for_each_entry(rule, &chain->rules, list) {
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
227
  		memset(&extack, 0, sizeof(extack));
bbaef955a   Pablo Neira Ayuso   netfilter: nf_tab...
228
  		nft_flow_cls_offload_setup(&cls_flow, basechain, rule, NULL,
be193f5e2   Pablo Neira Ayuso   netfilter: nf_tab...
229
  					   &extack, FLOW_CLS_DESTROY);
bbaef955a   Pablo Neira Ayuso   netfilter: nf_tab...
230
231
  		nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow, &bo->cb_list);
  	}
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
232
233
234
235
236
237
238
239
  
  	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
  		list_del(&block_cb->list);
  		flow_block_cb_free(block_cb);
  	}
  
  	return 0;
  }
9a32669fe   wenxu   netfilter: nf_tab...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  static int nft_block_setup(struct nft_base_chain *basechain,
  			   struct flow_block_offload *bo,
  			   enum flow_block_command cmd)
  {
  	int err;
  
  	switch (cmd) {
  	case FLOW_BLOCK_BIND:
  		err = nft_flow_offload_bind(bo, basechain);
  		break;
  	case FLOW_BLOCK_UNBIND:
  		err = nft_flow_offload_unbind(bo, basechain);
  		break;
  	default:
  		WARN_ON_ONCE(1);
  		err = -EOPNOTSUPP;
  	}
  
  	return err;
  }
75ceaf862   Pablo Neira Ayuso   netfilter: nf_tab...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  static void nft_flow_block_offload_init(struct flow_block_offload *bo,
  					struct net *net,
  					enum flow_block_command cmd,
  					struct nft_base_chain *basechain,
  					struct netlink_ext_ack *extack)
  {
  	memset(bo, 0, sizeof(*bo));
  	bo->net		= net;
  	bo->block	= &basechain->flow_block;
  	bo->command	= cmd;
  	bo->binder_type	= FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS;
  	bo->extack	= extack;
  	INIT_LIST_HEAD(&bo->cb_list);
  }
9a32669fe   wenxu   netfilter: nf_tab...
274
275
276
277
278
  static int nft_block_offload_cmd(struct nft_base_chain *chain,
  				 struct net_device *dev,
  				 enum flow_block_command cmd)
  {
  	struct netlink_ext_ack extack = {};
75ceaf862   Pablo Neira Ayuso   netfilter: nf_tab...
279
  	struct flow_block_offload bo;
9a32669fe   wenxu   netfilter: nf_tab...
280
  	int err;
75ceaf862   Pablo Neira Ayuso   netfilter: nf_tab...
281
  	nft_flow_block_offload_init(&bo, dev_net(dev), cmd, chain, &extack);
9a32669fe   wenxu   netfilter: nf_tab...
282
283
284
285
286
287
288
  
  	err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
  	if (err < 0)
  		return err;
  
  	return nft_block_setup(chain, &bo, cmd);
  }
0fdcf78d5   Pablo Neira Ayuso   net: use flow_ind...
289
290
291
292
293
294
295
296
297
298
299
  static void nft_indr_block_cleanup(struct flow_block_cb *block_cb)
  {
  	struct nft_base_chain *basechain = block_cb->indr.data;
  	struct net_device *dev = block_cb->indr.dev;
  	struct netlink_ext_ack extack = {};
  	struct net *net = dev_net(dev);
  	struct flow_block_offload bo;
  
  	nft_flow_block_offload_init(&bo, dev_net(dev), FLOW_BLOCK_UNBIND,
  				    basechain, &extack);
  	mutex_lock(&net->nft.commit_mutex);
a1db21786   wenxu   net: flow_offload...
300
  	list_del(&block_cb->driver_list);
0fdcf78d5   Pablo Neira Ayuso   net: use flow_ind...
301
302
303
304
305
306
  	list_move(&block_cb->list, &bo.cb_list);
  	nft_flow_offload_unbind(&bo, basechain);
  	mutex_unlock(&net->nft.commit_mutex);
  }
  
  static int nft_indr_block_offload_cmd(struct nft_base_chain *basechain,
9a32669fe   wenxu   netfilter: nf_tab...
307
308
309
  				      struct net_device *dev,
  				      enum flow_block_command cmd)
  {
9a32669fe   wenxu   netfilter: nf_tab...
310
  	struct netlink_ext_ack extack = {};
75ceaf862   Pablo Neira Ayuso   netfilter: nf_tab...
311
  	struct flow_block_offload bo;
0fdcf78d5   Pablo Neira Ayuso   net: use flow_ind...
312
  	int err;
9a32669fe   wenxu   netfilter: nf_tab...
313

0fdcf78d5   Pablo Neira Ayuso   net: use flow_ind...
314
  	nft_flow_block_offload_init(&bo, dev_net(dev), cmd, basechain, &extack);
9a32669fe   wenxu   netfilter: nf_tab...
315

c40f4e50b   Petr Machata   net: sched: Pass ...
316
  	err = flow_indr_dev_setup_offload(dev, NULL, TC_SETUP_BLOCK, basechain, &bo,
0fdcf78d5   Pablo Neira Ayuso   net: use flow_ind...
317
318
319
  					  nft_indr_block_cleanup);
  	if (err < 0)
  		return err;
9a32669fe   wenxu   netfilter: nf_tab...
320
321
322
  
  	if (list_empty(&bo.cb_list))
  		return -EOPNOTSUPP;
0fdcf78d5   Pablo Neira Ayuso   net: use flow_ind...
323
  	return nft_block_setup(basechain, &bo, cmd);
9a32669fe   wenxu   netfilter: nf_tab...
324
  }
6df5490fb   Pablo Neira Ayuso   netfilter: nf_tab...
325
326
327
328
329
330
331
332
333
334
335
336
337
  static int nft_chain_offload_cmd(struct nft_base_chain *basechain,
  				 struct net_device *dev,
  				 enum flow_block_command cmd)
  {
  	int err;
  
  	if (dev->netdev_ops->ndo_setup_tc)
  		err = nft_block_offload_cmd(basechain, dev, cmd);
  	else
  		err = nft_indr_block_offload_cmd(basechain, dev, cmd);
  
  	return err;
  }
ead3952ea   Pablo Neira Ayuso   netfilter: nf_tab...
338
  static int nft_flow_block_chain(struct nft_base_chain *basechain,
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
339
  				const struct net_device *this_dev,
ead3952ea   Pablo Neira Ayuso   netfilter: nf_tab...
340
341
  				enum flow_block_command cmd)
  {
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
342
343
  	struct net_device *dev;
  	struct nft_hook *hook;
671312e1a   Pablo Neira Ayuso   netfilter: nf_tab...
344
  	int err, i = 0;
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
345
346
347
348
349
  
  	list_for_each_entry(hook, &basechain->hook_list, list) {
  		dev = hook->ops.dev;
  		if (this_dev && this_dev != dev)
  			continue;
ead3952ea   Pablo Neira Ayuso   netfilter: nf_tab...
350

6df5490fb   Pablo Neira Ayuso   netfilter: nf_tab...
351
  		err = nft_chain_offload_cmd(basechain, dev, cmd);
671312e1a   Pablo Neira Ayuso   netfilter: nf_tab...
352
353
354
  		if (err < 0 && cmd == FLOW_BLOCK_BIND) {
  			if (!this_dev)
  				goto err_flow_block;
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
355
  			return err;
671312e1a   Pablo Neira Ayuso   netfilter: nf_tab...
356
357
  		}
  		i++;
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
358
359
360
  	}
  
  	return 0;
671312e1a   Pablo Neira Ayuso   netfilter: nf_tab...
361
362
363
364
365
366
367
368
369
370
  
  err_flow_block:
  	list_for_each_entry(hook, &basechain->hook_list, list) {
  		if (i-- <= 0)
  			break;
  
  		dev = hook->ops.dev;
  		nft_chain_offload_cmd(basechain, dev, FLOW_BLOCK_UNBIND);
  	}
  	return err;
ead3952ea   Pablo Neira Ayuso   netfilter: nf_tab...
371
  }
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
372
  static int nft_flow_offload_chain(struct nft_chain *chain, u8 *ppolicy,
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
373
374
  				  enum flow_block_command cmd)
  {
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
375
  	struct nft_base_chain *basechain;
8fc618c52   wenxu   netfilter: nf_tab...
376
  	u8 policy;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
377
378
379
380
381
  
  	if (!nft_is_base_chain(chain))
  		return -EOPNOTSUPP;
  
  	basechain = nft_base_chain(chain);
8fc618c52   wenxu   netfilter: nf_tab...
382
  	policy = ppolicy ? *ppolicy : basechain->policy;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
383
  	/* Only default policy to accept is supported for now. */
ff175d0b0   Pablo Neira Ayuso   netfilter: nf_tab...
384
  	if (cmd == FLOW_BLOCK_BIND && policy == NF_DROP)
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
385
  		return -EOPNOTSUPP;
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
386
  	return nft_flow_block_chain(basechain, NULL, cmd);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
387
  }
63b48c73f   Pablo Neira Ayuso   netfilter: nf_tab...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  static void nft_flow_rule_offload_abort(struct net *net,
  					struct nft_trans *trans)
  {
  	int err = 0;
  
  	list_for_each_entry_continue_reverse(trans, &net->nft.commit_list, list) {
  		if (trans->ctx.family != NFPROTO_NETDEV)
  			continue;
  
  		switch (trans->msg_type) {
  		case NFT_MSG_NEWCHAIN:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
  			    nft_trans_chain_update(trans))
  				continue;
  
  			err = nft_flow_offload_chain(trans->ctx.chain, NULL,
  						     FLOW_BLOCK_UNBIND);
  			break;
  		case NFT_MSG_DELCHAIN:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
  
  			err = nft_flow_offload_chain(trans->ctx.chain, NULL,
  						     FLOW_BLOCK_BIND);
  			break;
  		case NFT_MSG_NEWRULE:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
  
  			err = nft_flow_offload_rule(trans->ctx.chain,
  						    nft_trans_rule(trans),
  						    NULL, FLOW_CLS_DESTROY);
  			break;
  		case NFT_MSG_DELRULE:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
  
  			err = nft_flow_offload_rule(trans->ctx.chain,
  						    nft_trans_rule(trans),
  						    nft_trans_flow_rule(trans),
  						    FLOW_CLS_REPLACE);
  			break;
  		}
  
  		if (WARN_ON_ONCE(err))
  			break;
  	}
  }
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
436
437
438
439
  int nft_flow_rule_offload_commit(struct net *net)
  {
  	struct nft_trans *trans;
  	int err = 0;
8fc618c52   wenxu   netfilter: nf_tab...
440
  	u8 policy;
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
441
442
443
444
445
446
447
  
  	list_for_each_entry(trans, &net->nft.commit_list, list) {
  		if (trans->ctx.family != NFPROTO_NETDEV)
  			continue;
  
  		switch (trans->msg_type) {
  		case NFT_MSG_NEWCHAIN:
88c749840   Pablo Neira Ayuso   netfilter: nf_tab...
448
449
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD) ||
  			    nft_trans_chain_update(trans))
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
450
  				continue;
8fc618c52   wenxu   netfilter: nf_tab...
451
452
453
  			policy = nft_trans_chain_policy(trans);
  			err = nft_flow_offload_chain(trans->ctx.chain, &policy,
  						     FLOW_BLOCK_BIND);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
454
455
456
457
  			break;
  		case NFT_MSG_DELCHAIN:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
8fc618c52   wenxu   netfilter: nf_tab...
458
459
  			policy = nft_trans_chain_policy(trans);
  			err = nft_flow_offload_chain(trans->ctx.chain, &policy,
085461c89   Pablo Neira Ayuso   netfilter: nf_tab...
460
  						     FLOW_BLOCK_UNBIND);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
461
462
463
464
465
466
  			break;
  		case NFT_MSG_NEWRULE:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
  
  			if (trans->ctx.flags & NLM_F_REPLACE ||
23403cd88   Pablo Neira Ayuso   netfilter: nf_tab...
467
468
469
470
  			    !(trans->ctx.flags & NLM_F_APPEND)) {
  				err = -EOPNOTSUPP;
  				break;
  			}
e211aab73   wenxu   netfilter: nf_tab...
471
472
473
474
  			err = nft_flow_offload_rule(trans->ctx.chain,
  						    nft_trans_rule(trans),
  						    nft_trans_flow_rule(trans),
  						    FLOW_CLS_REPLACE);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
475
476
477
478
  			break;
  		case NFT_MSG_DELRULE:
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
e211aab73   wenxu   netfilter: nf_tab...
479
480
  			err = nft_flow_offload_rule(trans->ctx.chain,
  						    nft_trans_rule(trans),
6ca61c7a8   Pablo Neira Ayuso   netfilter: nf_tab...
481
  						    NULL, FLOW_CLS_DESTROY);
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
482
483
  			break;
  		}
63b48c73f   Pablo Neira Ayuso   netfilter: nf_tab...
484
485
  		if (err) {
  			nft_flow_rule_offload_abort(net, trans);
23403cd88   Pablo Neira Ayuso   netfilter: nf_tab...
486
  			break;
63b48c73f   Pablo Neira Ayuso   netfilter: nf_tab...
487
  		}
23403cd88   Pablo Neira Ayuso   netfilter: nf_tab...
488
489
490
491
492
493
494
495
  	}
  
  	list_for_each_entry(trans, &net->nft.commit_list, list) {
  		if (trans->ctx.family != NFPROTO_NETDEV)
  			continue;
  
  		switch (trans->msg_type) {
  		case NFT_MSG_NEWRULE:
63b48c73f   Pablo Neira Ayuso   netfilter: nf_tab...
496
  		case NFT_MSG_DELRULE:
23403cd88   Pablo Neira Ayuso   netfilter: nf_tab...
497
498
499
500
501
502
503
504
  			if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
  
  			nft_flow_rule_destroy(nft_trans_flow_rule(trans));
  			break;
  		default:
  			break;
  		}
c9626a2cb   Pablo Neira Ayuso   netfilter: nf_tab...
505
506
507
508
  	}
  
  	return err;
  }
9a32669fe   wenxu   netfilter: nf_tab...
509

504882db8   wenxu   netfilter: nf_tab...
510
  static struct nft_chain *__nft_offload_get_chain(struct net_device *dev)
9a32669fe   wenxu   netfilter: nf_tab...
511
  {
504882db8   wenxu   netfilter: nf_tab...
512
  	struct nft_base_chain *basechain;
9a32669fe   wenxu   netfilter: nf_tab...
513
  	struct net *net = dev_net(dev);
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
514
  	struct nft_hook *hook, *found;
9a32669fe   wenxu   netfilter: nf_tab...
515
  	const struct nft_table *table;
504882db8   wenxu   netfilter: nf_tab...
516
  	struct nft_chain *chain;
9a32669fe   wenxu   netfilter: nf_tab...
517

504882db8   wenxu   netfilter: nf_tab...
518
  	list_for_each_entry(table, &net->nft.tables, list) {
9a32669fe   wenxu   netfilter: nf_tab...
519
520
  		if (table->family != NFPROTO_NETDEV)
  			continue;
504882db8   wenxu   netfilter: nf_tab...
521
522
523
524
  		list_for_each_entry(chain, &table->chains, list) {
  			if (!nft_is_base_chain(chain) ||
  			    !(chain->flags & NFT_CHAIN_HW_OFFLOAD))
  				continue;
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
525
  			found = NULL;
504882db8   wenxu   netfilter: nf_tab...
526
  			basechain = nft_base_chain(chain);
d54725cd1   Pablo Neira Ayuso   netfilter: nf_tab...
527
528
529
530
531
532
533
534
  			list_for_each_entry(hook, &basechain->hook_list, list) {
  				if (hook->ops.dev != dev)
  					continue;
  
  				found = hook;
  				break;
  			}
  			if (!found)
504882db8   wenxu   netfilter: nf_tab...
535
536
537
  				continue;
  
  			return chain;
9a32669fe   wenxu   netfilter: nf_tab...
538
539
  		}
  	}
504882db8   wenxu   netfilter: nf_tab...
540
541
542
  
  	return NULL;
  }
06d392cbe   wenxu   netfilter: nf_tab...
543
544
545
546
547
548
  static int nft_offload_netdev_event(struct notifier_block *this,
  				    unsigned long event, void *ptr)
  {
  	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
  	struct net *net = dev_net(dev);
  	struct nft_chain *chain;
d1f4c9664   wenxu   netfilter: nf_tab...
549
550
  	if (event != NETDEV_UNREGISTER)
  		return NOTIFY_DONE;
06d392cbe   wenxu   netfilter: nf_tab...
551
552
553
  	mutex_lock(&net->nft.commit_mutex);
  	chain = __nft_offload_get_chain(dev);
  	if (chain)
bbaef955a   Pablo Neira Ayuso   netfilter: nf_tab...
554
555
  		nft_flow_block_chain(nft_base_chain(chain), dev,
  				     FLOW_BLOCK_UNBIND);
06d392cbe   wenxu   netfilter: nf_tab...
556
557
558
559
  	mutex_unlock(&net->nft.commit_mutex);
  
  	return NOTIFY_DONE;
  }
06d392cbe   wenxu   netfilter: nf_tab...
560
561
562
563
564
  static struct notifier_block nft_offload_netdev_notifier = {
  	.notifier_call	= nft_offload_netdev_event,
  };
  
  int nft_offload_init(void)
3474a2c62   Pablo Neira Ayuso   netfilter: nf_tab...
565
  {
709ffbe19   Pablo Neira Ayuso   net: remove indir...
566
  	return register_netdevice_notifier(&nft_offload_netdev_notifier);
3474a2c62   Pablo Neira Ayuso   netfilter: nf_tab...
567
568
569
570
  }
  
  void nft_offload_exit(void)
  {
06d392cbe   wenxu   netfilter: nf_tab...
571
  	unregister_netdevice_notifier(&nft_offload_netdev_notifier);
3474a2c62   Pablo Neira Ayuso   netfilter: nf_tab...
572
  }