Blame view

net/sched/cls_flow.c 16.5 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
2
3
4
5
  /*
   * net/sched/cls_flow.c		Generic flow classifier
   *
   * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
6
7
8
9
10
11
12
13
14
15
16
17
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/jhash.h>
  #include <linux/random.h>
  #include <linux/pkt_cls.h>
  #include <linux/skbuff.h>
  #include <linux/in.h>
  #include <linux/ip.h>
  #include <linux/ipv6.h>
9ec138101   Patrick McHardy   [NET_SCHED]: cls_...
18
  #include <linux/if_vlan.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
3a9a231d9   Paul Gortmaker   net: Fix files ex...
20
  #include <linux/module.h>
743b2a667   Eric Dumazet   sched: cls_flow: ...
21
  #include <net/inet_sock.h>
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
22
23
24
25
  
  #include <net/pkt_cls.h>
  #include <net/ip.h>
  #include <net/route.h>
1bd758eb1   Jiri Pirko   net: change name ...
26
  #include <net/flow_dissector.h>
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
27

0013de38a   Javier Martinez Canillas   net: sched: use I...
28
  #if IS_ENABLED(CONFIG_NF_CONNTRACK)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
29
30
31
32
33
  #include <net/netfilter/nf_conntrack.h>
  #endif
  
  struct flow_head {
  	struct list_head	filters;
70da9f0bf   John Fastabend   net: sched: cls_f...
34
  	struct rcu_head		rcu;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
35
36
37
38
39
40
  };
  
  struct flow_filter {
  	struct list_head	list;
  	struct tcf_exts		exts;
  	struct tcf_ematch_tree	ematches;
70da9f0bf   John Fastabend   net: sched: cls_f...
41
  	struct tcf_proto	*tp;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
42
43
  	struct timer_list	perturb_timer;
  	u32			perturb_period;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
44
45
46
47
48
49
50
51
52
53
54
  	u32			handle;
  
  	u32			nkeys;
  	u32			keymask;
  	u32			mode;
  	u32			mask;
  	u32			xor;
  	u32			rshift;
  	u32			addend;
  	u32			divisor;
  	u32			baseclass;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
55
  	u32			hashrnd;
aaa908ffb   Cong Wang   net_sched: switch...
56
  	struct rcu_work		rwork;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
57
  };
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
58
59
60
61
62
63
  static inline u32 addr_fold(void *addr)
  {
  	unsigned long a = (unsigned long)addr;
  
  	return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0);
  }
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
64
  static u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
65
  {
c3f832418   Tom Herbert   net: Add full IPv...
66
67
68
69
  	__be32 src = flow_get_u32_src(flow);
  
  	if (src)
  		return ntohl(src);
4b95c3d40   Changli Gao   cls_flow: add san...
70
  	return addr_fold(skb->sk);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
71
  }
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
72
  static u32 flow_get_dst(const struct sk_buff *skb, const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
73
  {
c3f832418   Tom Herbert   net: Add full IPv...
74
75
76
77
  	__be32 dst = flow_get_u32_dst(flow);
  
  	if (dst)
  		return ntohl(dst);
d8b9605d2   Jiri Pirko   net: sched: fix s...
78
  	return addr_fold(skb_dst(skb)) ^ (__force u16) tc_skb_protocol(skb);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
79
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
80
81
  static u32 flow_get_proto(const struct sk_buff *skb,
  			  const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
82
  {
06635a35d   Jiri Pirko   flow_dissect: use...
83
  	return flow->basic.ip_proto;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
84
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
85
86
  static u32 flow_get_proto_src(const struct sk_buff *skb,
  			      const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
87
  {
06635a35d   Jiri Pirko   flow_dissect: use...
88
  	if (flow->ports.ports)
59346afe7   Jiri Pirko   flow_dissector: c...
89
  		return ntohs(flow->ports.src);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
90

859c20123   Eric Dumazet   net_sched: cls_fl...
91
92
  	return addr_fold(skb->sk);
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
93
94
  static u32 flow_get_proto_dst(const struct sk_buff *skb,
  			      const struct flow_keys *flow)
859c20123   Eric Dumazet   net_sched: cls_fl...
95
  {
06635a35d   Jiri Pirko   flow_dissect: use...
96
  	if (flow->ports.ports)
59346afe7   Jiri Pirko   flow_dissector: c...
97
  		return ntohs(flow->ports.dst);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
98

d8b9605d2   Jiri Pirko   net: sched: fix s...
99
  	return addr_fold(skb_dst(skb)) ^ (__force u16) tc_skb_protocol(skb);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
100
101
102
103
  }
  
  static u32 flow_get_iif(const struct sk_buff *skb)
  {
8964be4a9   Eric Dumazet   net: rename skb->...
104
  	return skb->skb_iif;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  }
  
  static u32 flow_get_priority(const struct sk_buff *skb)
  {
  	return skb->priority;
  }
  
  static u32 flow_get_mark(const struct sk_buff *skb)
  {
  	return skb->mark;
  }
  
  static u32 flow_get_nfct(const struct sk_buff *skb)
  {
0013de38a   Javier Martinez Canillas   net: sched: use I...
119
  #if IS_ENABLED(CONFIG_NF_CONNTRACK)
cb9c68363   Florian Westphal   skbuff: add and u...
120
  	return addr_fold(skb_nfct(skb));
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
121
122
123
124
  #else
  	return 0;
  #endif
  }
0013de38a   Javier Martinez Canillas   net: sched: use I...
125
  #if IS_ENABLED(CONFIG_NF_CONNTRACK)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
126
127
128
  #define CTTUPLE(skb, member)						\
  ({									\
  	enum ip_conntrack_info ctinfo;					\
859c20123   Eric Dumazet   net_sched: cls_fl...
129
  	const struct nf_conn *ct = nf_ct_get(skb, &ctinfo);		\
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
130
131
132
133
134
135
136
137
138
139
140
  	if (ct == NULL)							\
  		goto fallback;						\
  	ct->tuplehash[CTINFO2DIR(ctinfo)].tuple.member;			\
  })
  #else
  #define CTTUPLE(skb, member)						\
  ({									\
  	goto fallback;							\
  	0;								\
  })
  #endif
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
141
142
  static u32 flow_get_nfct_src(const struct sk_buff *skb,
  			     const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
143
  {
d8b9605d2   Jiri Pirko   net: sched: fix s...
144
  	switch (tc_skb_protocol(skb)) {
606780404   Arnaldo Carvalho de Melo   net: Use hton[sl]...
145
  	case htons(ETH_P_IP):
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
146
  		return ntohl(CTTUPLE(skb, src.u3.ip));
606780404   Arnaldo Carvalho de Melo   net: Use hton[sl]...
147
  	case htons(ETH_P_IPV6):
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
148
149
150
  		return ntohl(CTTUPLE(skb, src.u3.ip6[3]));
  	}
  fallback:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
151
  	return flow_get_src(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
152
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
153
154
  static u32 flow_get_nfct_dst(const struct sk_buff *skb,
  			     const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
155
  {
d8b9605d2   Jiri Pirko   net: sched: fix s...
156
  	switch (tc_skb_protocol(skb)) {
606780404   Arnaldo Carvalho de Melo   net: Use hton[sl]...
157
  	case htons(ETH_P_IP):
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
158
  		return ntohl(CTTUPLE(skb, dst.u3.ip));
606780404   Arnaldo Carvalho de Melo   net: Use hton[sl]...
159
  	case htons(ETH_P_IPV6):
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
160
161
162
  		return ntohl(CTTUPLE(skb, dst.u3.ip6[3]));
  	}
  fallback:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
163
  	return flow_get_dst(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
164
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
165
166
  static u32 flow_get_nfct_proto_src(const struct sk_buff *skb,
  				   const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
167
168
169
  {
  	return ntohs(CTTUPLE(skb, src.u.all));
  fallback:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
170
  	return flow_get_proto_src(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
171
  }
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
172
173
  static u32 flow_get_nfct_proto_dst(const struct sk_buff *skb,
  				   const struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
174
175
176
  {
  	return ntohs(CTTUPLE(skb, dst.u.all));
  fallback:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
177
  	return flow_get_proto_dst(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
178
179
180
181
  }
  
  static u32 flow_get_rtclassid(const struct sk_buff *skb)
  {
c7066f70d   Patrick McHardy   netfilter: fix Kc...
182
  #ifdef CONFIG_IP_ROUTE_CLASSID
adf30907d   Eric Dumazet   net: skb->dst acc...
183
184
  	if (skb_dst(skb))
  		return skb_dst(skb)->tclassid;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
185
186
187
188
189
190
  #endif
  	return 0;
  }
  
  static u32 flow_get_skuid(const struct sk_buff *skb)
  {
743b2a667   Eric Dumazet   sched: cls_flow: ...
191
192
193
194
  	struct sock *sk = skb_to_full_sk(skb);
  
  	if (sk && sk->sk_socket && sk->sk_socket->file) {
  		kuid_t skuid = sk->sk_socket->file->f_cred->fsuid;
a6c6796c7   Eric W. Biederman   userns: Convert c...
195
196
  		return from_kuid(&init_user_ns, skuid);
  	}
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
197
198
199
200
201
  	return 0;
  }
  
  static u32 flow_get_skgid(const struct sk_buff *skb)
  {
743b2a667   Eric Dumazet   sched: cls_flow: ...
202
203
204
205
  	struct sock *sk = skb_to_full_sk(skb);
  
  	if (sk && sk->sk_socket && sk->sk_socket->file) {
  		kgid_t skgid = sk->sk_socket->file->f_cred->fsgid;
a6c6796c7   Eric W. Biederman   userns: Convert c...
206
207
  		return from_kgid(&init_user_ns, skgid);
  	}
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
208
209
  	return 0;
  }
9ec138101   Patrick McHardy   [NET_SCHED]: cls_...
210
211
212
213
214
215
216
217
  static u32 flow_get_vlan_tag(const struct sk_buff *skb)
  {
  	u16 uninitialized_var(tag);
  
  	if (vlan_get_tag(skb, &tag) < 0)
  		return 0;
  	return tag & VLAN_VID_MASK;
  }
739a91ef0   Changli Gao   net_sched: cls_fl...
218
219
  static u32 flow_get_rxhash(struct sk_buff *skb)
  {
3958afa1b   Tom Herbert   net: Change skb_g...
220
  	return skb_get_hash(skb);
739a91ef0   Changli Gao   net_sched: cls_fl...
221
  }
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
222
  static u32 flow_key_get(struct sk_buff *skb, int key, struct flow_keys *flow)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
223
224
225
  {
  	switch (key) {
  	case FLOW_KEY_SRC:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
226
  		return flow_get_src(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
227
  	case FLOW_KEY_DST:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
228
  		return flow_get_dst(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
229
  	case FLOW_KEY_PROTO:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
230
  		return flow_get_proto(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
231
  	case FLOW_KEY_PROTO_SRC:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
232
  		return flow_get_proto_src(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
233
  	case FLOW_KEY_PROTO_DST:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
234
  		return flow_get_proto_dst(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
235
236
237
238
239
240
241
242
243
  	case FLOW_KEY_IIF:
  		return flow_get_iif(skb);
  	case FLOW_KEY_PRIORITY:
  		return flow_get_priority(skb);
  	case FLOW_KEY_MARK:
  		return flow_get_mark(skb);
  	case FLOW_KEY_NFCT:
  		return flow_get_nfct(skb);
  	case FLOW_KEY_NFCT_SRC:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
244
  		return flow_get_nfct_src(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
245
  	case FLOW_KEY_NFCT_DST:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
246
  		return flow_get_nfct_dst(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
247
  	case FLOW_KEY_NFCT_PROTO_SRC:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
248
  		return flow_get_nfct_proto_src(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
249
  	case FLOW_KEY_NFCT_PROTO_DST:
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
250
  		return flow_get_nfct_proto_dst(skb, flow);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
251
252
253
254
255
256
  	case FLOW_KEY_RTCLASSID:
  		return flow_get_rtclassid(skb);
  	case FLOW_KEY_SKUID:
  		return flow_get_skuid(skb);
  	case FLOW_KEY_SKGID:
  		return flow_get_skgid(skb);
9ec138101   Patrick McHardy   [NET_SCHED]: cls_...
257
258
  	case FLOW_KEY_VLAN_TAG:
  		return flow_get_vlan_tag(skb);
739a91ef0   Changli Gao   net_sched: cls_fl...
259
260
  	case FLOW_KEY_RXHASH:
  		return flow_get_rxhash(skb);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
261
262
263
264
265
  	default:
  		WARN_ON(1);
  		return 0;
  	}
  }
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
266
267
268
269
270
271
272
273
274
  #define FLOW_KEYS_NEEDED ((1 << FLOW_KEY_SRC) | 		\
  			  (1 << FLOW_KEY_DST) |			\
  			  (1 << FLOW_KEY_PROTO) |		\
  			  (1 << FLOW_KEY_PROTO_SRC) |		\
  			  (1 << FLOW_KEY_PROTO_DST) | 		\
  			  (1 << FLOW_KEY_NFCT_SRC) |		\
  			  (1 << FLOW_KEY_NFCT_DST) |		\
  			  (1 << FLOW_KEY_NFCT_PROTO_SRC) |	\
  			  (1 << FLOW_KEY_NFCT_PROTO_DST))
dc7f9f6e8   Eric Dumazet   net: sched: const...
275
  static int flow_classify(struct sk_buff *skb, const struct tcf_proto *tp,
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
276
277
  			 struct tcf_result *res)
  {
70da9f0bf   John Fastabend   net: sched: cls_f...
278
  	struct flow_head *head = rcu_dereference_bh(tp->root);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
279
280
281
282
283
  	struct flow_filter *f;
  	u32 keymask;
  	u32 classid;
  	unsigned int n, key;
  	int r;
70da9f0bf   John Fastabend   net: sched: cls_f...
284
  	list_for_each_entry_rcu(f, &head->filters, list) {
3a53943b5   Eric Dumazet   cls_flow: remove ...
285
  		u32 keys[FLOW_KEY_MAX + 1];
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
286
  		struct flow_keys flow_keys;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
287
288
289
290
291
  
  		if (!tcf_em_tree_match(skb, &f->ematches, NULL))
  			continue;
  
  		keymask = f->keymask;
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
292
  		if (keymask & FLOW_KEYS_NEEDED)
cd79a2382   Tom Herbert   flow_dissector: A...
293
  			skb_flow_dissect_flow_keys(skb, &flow_keys, 0);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
294
295
296
297
  
  		for (n = 0; n < f->nkeys; n++) {
  			key = ffs(keymask) - 1;
  			keymask &= ~(1 << key);
6bd2a9af1   Eric Dumazet   cls_flow: use skb...
298
  			keys[n] = flow_key_get(skb, key, &flow_keys);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
299
300
301
  		}
  
  		if (f->mode == FLOW_MODE_HASH)
72d9794f4   Patrick McHardy   net-sched: cls_fl...
302
  			classid = jhash2(keys, f->nkeys, f->hashrnd);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
  		else {
  			classid = keys[0];
  			classid = (classid & f->mask) ^ f->xor;
  			classid = (classid >> f->rshift) + f->addend;
  		}
  
  		if (f->divisor)
  			classid %= f->divisor;
  
  		res->class   = 0;
  		res->classid = TC_H_MAKE(f->baseclass, f->baseclass + classid);
  
  		r = tcf_exts_exec(skb, &f->exts, res);
  		if (r < 0)
  			continue;
  		return r;
  	}
  	return -1;
  }
cdeabbb88   Kees Cook   net: sched: Conve...
322
  static void flow_perturbation(struct timer_list *t)
72d9794f4   Patrick McHardy   net-sched: cls_fl...
323
  {
cdeabbb88   Kees Cook   net: sched: Conve...
324
  	struct flow_filter *f = from_timer(f, t, perturb_timer);
72d9794f4   Patrick McHardy   net-sched: cls_fl...
325
326
327
328
329
  
  	get_random_bytes(&f->hashrnd, 4);
  	if (f->perturb_period)
  		mod_timer(&f->perturb_timer, jiffies + f->perturb_period);
  }
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
330
331
332
333
334
335
336
337
338
339
340
341
  static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
  	[TCA_FLOW_KEYS]		= { .type = NLA_U32 },
  	[TCA_FLOW_MODE]		= { .type = NLA_U32 },
  	[TCA_FLOW_BASECLASS]	= { .type = NLA_U32 },
  	[TCA_FLOW_RSHIFT]	= { .type = NLA_U32 },
  	[TCA_FLOW_ADDEND]	= { .type = NLA_U32 },
  	[TCA_FLOW_MASK]		= { .type = NLA_U32 },
  	[TCA_FLOW_XOR]		= { .type = NLA_U32 },
  	[TCA_FLOW_DIVISOR]	= { .type = NLA_U32 },
  	[TCA_FLOW_ACT]		= { .type = NLA_NESTED },
  	[TCA_FLOW_POLICE]	= { .type = NLA_NESTED },
  	[TCA_FLOW_EMATCHES]	= { .type = NLA_NESTED },
72d9794f4   Patrick McHardy   net-sched: cls_fl...
342
  	[TCA_FLOW_PERTURB]	= { .type = NLA_U32 },
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
343
  };
22f7cec93   Cong Wang   cls_flow: use tcf...
344
  static void __flow_destroy_filter(struct flow_filter *f)
70da9f0bf   John Fastabend   net: sched: cls_f...
345
  {
70da9f0bf   John Fastabend   net: sched: cls_f...
346
  	del_timer_sync(&f->perturb_timer);
18d0264f6   WANG Cong   net_sched: remove...
347
  	tcf_exts_destroy(&f->exts);
82a470f11   John Fastabend   net: sched: remov...
348
  	tcf_em_tree_destroy(&f->ematches);
22f7cec93   Cong Wang   cls_flow: use tcf...
349
  	tcf_exts_put_net(&f->exts);
70da9f0bf   John Fastabend   net: sched: cls_f...
350
  	kfree(f);
22f7cec93   Cong Wang   cls_flow: use tcf...
351
352
353
354
  }
  
  static void flow_destroy_filter_work(struct work_struct *work)
  {
aaa908ffb   Cong Wang   net_sched: switch...
355
356
357
  	struct flow_filter *f = container_of(to_rcu_work(work),
  					     struct flow_filter,
  					     rwork);
22f7cec93   Cong Wang   cls_flow: use tcf...
358
359
  	rtnl_lock();
  	__flow_destroy_filter(f);
94cdb4756   Cong Wang   net_sched: use tc...
360
361
  	rtnl_unlock();
  }
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
362
  static int flow_change(struct net *net, struct sk_buff *in_skb,
af4c6641f   Eric W. Biederman   net sched: Pass t...
363
  		       struct tcf_proto *tp, unsigned long base,
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
364
  		       u32 handle, struct nlattr **tca,
12db03b65   Vlad Buslov   net: sched: exten...
365
366
  		       void **arg, bool ovr, bool rtnl_held,
  		       struct netlink_ext_ack *extack)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
367
  {
70da9f0bf   John Fastabend   net: sched: cls_f...
368
369
  	struct flow_head *head = rtnl_dereference(tp->root);
  	struct flow_filter *fold, *fnew;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
370
371
  	struct nlattr *opt = tca[TCA_OPTIONS];
  	struct nlattr *tb[TCA_FLOW_MAX + 1];
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
372
  	unsigned int nkeys = 0;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
373
  	unsigned int perturb_period = 0;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
374
375
376
377
378
379
380
  	u32 baseclass = 0;
  	u32 keymask = 0;
  	u32 mode;
  	int err;
  
  	if (opt == NULL)
  		return -EINVAL;
8cb081746   Johannes Berg   netlink: make val...
381
382
  	err = nla_parse_nested_deprecated(tb, TCA_FLOW_MAX, opt, flow_policy,
  					  NULL);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
383
384
385
386
387
388
389
390
391
392
393
  	if (err < 0)
  		return err;
  
  	if (tb[TCA_FLOW_BASECLASS]) {
  		baseclass = nla_get_u32(tb[TCA_FLOW_BASECLASS]);
  		if (TC_H_MIN(baseclass) == 0)
  			return -EINVAL;
  	}
  
  	if (tb[TCA_FLOW_KEYS]) {
  		keymask = nla_get_u32(tb[TCA_FLOW_KEYS]);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
394
395
396
397
  
  		nkeys = hweight32(keymask);
  		if (nkeys == 0)
  			return -EINVAL;
4f2504910   Patrick McHardy   [NET_SCHED]: cls_...
398
399
400
  
  		if (fls(keymask) - 1 > FLOW_KEY_MAX)
  			return -EOPNOTSUPP;
a6c6796c7   Eric W. Biederman   userns: Convert c...
401
402
  
  		if ((keymask & (FLOW_KEY_SKUID|FLOW_KEY_SKGID)) &&
e32123e59   Patrick McHardy   netlink: rename s...
403
  		    sk_user_ns(NETLINK_CB(in_skb).sk) != &init_user_ns)
a6c6796c7   Eric W. Biederman   userns: Convert c...
404
  			return -EOPNOTSUPP;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
405
  	}
70da9f0bf   John Fastabend   net: sched: cls_f...
406
407
  	fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
  	if (!fnew)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
408
  		return -ENOBUFS;
4ebc1e3cf   Jiri Pirko   net: sched: remov...
409
410
411
  
  	err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &fnew->ematches);
  	if (err < 0)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
412
  		goto err1;
70da9f0bf   John Fastabend   net: sched: cls_f...
413

14215108a   Cong Wang   net_sched: initia...
414
  	err = tcf_exts_init(&fnew->exts, net, TCA_FLOW_ACT, TCA_FLOW_POLICE);
b9a24bb76   WANG Cong   net_sched: proper...
415
  	if (err < 0)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
416
  		goto err2;
50a561900   Alexander Aring   net: sched: cls: ...
417
  	err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &fnew->exts, ovr,
ec6743a10   Vlad Buslov   net: sched: track...
418
  				true, extack);
c09fc2e11   Jiri Pirko   net: sched: cls_f...
419
420
  	if (err < 0)
  		goto err2;
32b2f4b19   Daniel Borkmann   sched: cls_flow: ...
421

8113c0956   WANG Cong   net_sched: use vo...
422
  	fold = *arg;
70da9f0bf   John Fastabend   net: sched: cls_f...
423
  	if (fold) {
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
424
  		err = -EINVAL;
70da9f0bf   John Fastabend   net: sched: cls_f...
425
  		if (fold->handle != handle && handle)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
426
  			goto err2;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
427

70da9f0bf   John Fastabend   net: sched: cls_f...
428
  		/* Copy fold into fnew */
70da9f0bf   John Fastabend   net: sched: cls_f...
429
  		fnew->tp = fold->tp;
70da9f0bf   John Fastabend   net: sched: cls_f...
430
431
432
433
434
435
436
437
438
439
440
441
442
  		fnew->handle = fold->handle;
  		fnew->nkeys = fold->nkeys;
  		fnew->keymask = fold->keymask;
  		fnew->mode = fold->mode;
  		fnew->mask = fold->mask;
  		fnew->xor = fold->xor;
  		fnew->rshift = fold->rshift;
  		fnew->addend = fold->addend;
  		fnew->divisor = fold->divisor;
  		fnew->baseclass = fold->baseclass;
  		fnew->hashrnd = fold->hashrnd;
  
  		mode = fold->mode;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
443
444
445
  		if (tb[TCA_FLOW_MODE])
  			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
  		if (mode != FLOW_MODE_HASH && nkeys > 1)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
446
  			goto err2;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
447
448
  
  		if (mode == FLOW_MODE_HASH)
70da9f0bf   John Fastabend   net: sched: cls_f...
449
  			perturb_period = fold->perturb_period;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
450
451
  		if (tb[TCA_FLOW_PERTURB]) {
  			if (mode != FLOW_MODE_HASH)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
452
  				goto err2;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
453
454
  			perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
  		}
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
455
456
457
  	} else {
  		err = -EINVAL;
  		if (!handle)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
458
  			goto err2;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
459
  		if (!tb[TCA_FLOW_KEYS])
c09fc2e11   Jiri Pirko   net: sched: cls_f...
460
  			goto err2;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
461
462
463
464
465
  
  		mode = FLOW_MODE_MAP;
  		if (tb[TCA_FLOW_MODE])
  			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
  		if (mode != FLOW_MODE_HASH && nkeys > 1)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
466
  			goto err2;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
467

72d9794f4   Patrick McHardy   net-sched: cls_fl...
468
469
  		if (tb[TCA_FLOW_PERTURB]) {
  			if (mode != FLOW_MODE_HASH)
c09fc2e11   Jiri Pirko   net: sched: cls_f...
470
  				goto err2;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
471
472
  			perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
  		}
1abf27202   Jiri Pirko   net: sched: tcind...
473
474
475
476
477
  		if (TC_H_MAJ(baseclass) == 0) {
  			struct Qdisc *q = tcf_block_q(tp->chain->block);
  
  			baseclass = TC_H_MAKE(q->handle, baseclass);
  		}
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
478
479
  		if (TC_H_MIN(baseclass) == 0)
  			baseclass = TC_H_MAKE(baseclass, 1);
70da9f0bf   John Fastabend   net: sched: cls_f...
480
481
482
483
  		fnew->handle = handle;
  		fnew->mask  = ~0U;
  		fnew->tp = tp;
  		get_random_bytes(&fnew->hashrnd, 4);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
484
  	}
cdeabbb88   Kees Cook   net: sched: Conve...
485
  	timer_setup(&fnew->perturb_timer, flow_perturbation, TIMER_DEFERRABLE);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
486

f36fe1c49   Jiri Pirko   net: sched: intro...
487
  	tcf_block_netif_keep_dst(tp->chain->block);
028758788   Eric Dumazet   net: better IFF_X...
488

e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
489
  	if (tb[TCA_FLOW_KEYS]) {
70da9f0bf   John Fastabend   net: sched: cls_f...
490
491
  		fnew->keymask = keymask;
  		fnew->nkeys   = nkeys;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
492
  	}
70da9f0bf   John Fastabend   net: sched: cls_f...
493
  	fnew->mode = mode;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
494
495
  
  	if (tb[TCA_FLOW_MASK])
70da9f0bf   John Fastabend   net: sched: cls_f...
496
  		fnew->mask = nla_get_u32(tb[TCA_FLOW_MASK]);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
497
  	if (tb[TCA_FLOW_XOR])
70da9f0bf   John Fastabend   net: sched: cls_f...
498
  		fnew->xor = nla_get_u32(tb[TCA_FLOW_XOR]);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
499
  	if (tb[TCA_FLOW_RSHIFT])
70da9f0bf   John Fastabend   net: sched: cls_f...
500
  		fnew->rshift = nla_get_u32(tb[TCA_FLOW_RSHIFT]);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
501
  	if (tb[TCA_FLOW_ADDEND])
70da9f0bf   John Fastabend   net: sched: cls_f...
502
  		fnew->addend = nla_get_u32(tb[TCA_FLOW_ADDEND]);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
503
504
  
  	if (tb[TCA_FLOW_DIVISOR])
70da9f0bf   John Fastabend   net: sched: cls_f...
505
  		fnew->divisor = nla_get_u32(tb[TCA_FLOW_DIVISOR]);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
506
  	if (baseclass)
70da9f0bf   John Fastabend   net: sched: cls_f...
507
  		fnew->baseclass = baseclass;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
508

70da9f0bf   John Fastabend   net: sched: cls_f...
509
  	fnew->perturb_period = perturb_period;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
510
  	if (perturb_period)
70da9f0bf   John Fastabend   net: sched: cls_f...
511
  		mod_timer(&fnew->perturb_timer, jiffies + perturb_period);
72d9794f4   Patrick McHardy   net-sched: cls_fl...
512

8113c0956   WANG Cong   net_sched: use vo...
513
  	if (!*arg)
70da9f0bf   John Fastabend   net: sched: cls_f...
514
515
  		list_add_tail_rcu(&fnew->list, &head->filters);
  	else
32b2f4b19   Daniel Borkmann   sched: cls_flow: ...
516
  		list_replace_rcu(&fold->list, &fnew->list);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
517

8113c0956   WANG Cong   net_sched: use vo...
518
  	*arg = fnew;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
519

22f7cec93   Cong Wang   cls_flow: use tcf...
520
521
  	if (fold) {
  		tcf_exts_get_net(&fold->exts);
aaa908ffb   Cong Wang   net_sched: switch...
522
  		tcf_queue_work(&fold->rwork, flow_destroy_filter_work);
22f7cec93   Cong Wang   cls_flow: use tcf...
523
  	}
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
524
  	return 0;
c09fc2e11   Jiri Pirko   net: sched: cls_f...
525
  err2:
b9a24bb76   WANG Cong   net_sched: proper...
526
  	tcf_exts_destroy(&fnew->exts);
4ebc1e3cf   Jiri Pirko   net: sched: remov...
527
  	tcf_em_tree_destroy(&fnew->ematches);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
528
  err1:
c09fc2e11   Jiri Pirko   net: sched: cls_f...
529
  	kfree(fnew);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
530
531
  	return err;
  }
571acf210   Alexander Aring   net: sched: cls: ...
532
  static int flow_delete(struct tcf_proto *tp, void *arg, bool *last,
12db03b65   Vlad Buslov   net: sched: exten...
533
  		       bool rtnl_held, struct netlink_ext_ack *extack)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
534
  {
763dbf632   WANG Cong   net_sched: move t...
535
  	struct flow_head *head = rtnl_dereference(tp->root);
8113c0956   WANG Cong   net_sched: use vo...
536
  	struct flow_filter *f = arg;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
537

70da9f0bf   John Fastabend   net: sched: cls_f...
538
  	list_del_rcu(&f->list);
22f7cec93   Cong Wang   cls_flow: use tcf...
539
  	tcf_exts_get_net(&f->exts);
aaa908ffb   Cong Wang   net_sched: switch...
540
  	tcf_queue_work(&f->rwork, flow_destroy_filter_work);
763dbf632   WANG Cong   net_sched: move t...
541
  	*last = list_empty(&head->filters);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
542
543
544
545
546
547
  	return 0;
  }
  
  static int flow_init(struct tcf_proto *tp)
  {
  	struct flow_head *head;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
548
549
550
551
  	head = kzalloc(sizeof(*head), GFP_KERNEL);
  	if (head == NULL)
  		return -ENOBUFS;
  	INIT_LIST_HEAD(&head->filters);
70da9f0bf   John Fastabend   net: sched: cls_f...
552
  	rcu_assign_pointer(tp->root, head);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
553
554
  	return 0;
  }
12db03b65   Vlad Buslov   net: sched: exten...
555
556
  static void flow_destroy(struct tcf_proto *tp, bool rtnl_held,
  			 struct netlink_ext_ack *extack)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
557
  {
70da9f0bf   John Fastabend   net: sched: cls_f...
558
  	struct flow_head *head = rtnl_dereference(tp->root);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
559
560
561
  	struct flow_filter *f, *next;
  
  	list_for_each_entry_safe(f, next, &head->filters, list) {
70da9f0bf   John Fastabend   net: sched: cls_f...
562
  		list_del_rcu(&f->list);
22f7cec93   Cong Wang   cls_flow: use tcf...
563
  		if (tcf_exts_get_net(&f->exts))
aaa908ffb   Cong Wang   net_sched: switch...
564
  			tcf_queue_work(&f->rwork, flow_destroy_filter_work);
22f7cec93   Cong Wang   cls_flow: use tcf...
565
566
  		else
  			__flow_destroy_filter(f);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
567
  	}
70da9f0bf   John Fastabend   net: sched: cls_f...
568
  	kfree_rcu(head, rcu);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
569
  }
8113c0956   WANG Cong   net_sched: use vo...
570
  static void *flow_get(struct tcf_proto *tp, u32 handle)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
571
  {
70da9f0bf   John Fastabend   net: sched: cls_f...
572
  	struct flow_head *head = rtnl_dereference(tp->root);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
573
  	struct flow_filter *f;
6a659cd06   Jiri Pirko   net_sched: cls_fl...
574
  	list_for_each_entry(f, &head->filters, list)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
575
  		if (f->handle == handle)
8113c0956   WANG Cong   net_sched: use vo...
576
577
  			return f;
  	return NULL;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
578
  }
8113c0956   WANG Cong   net_sched: use vo...
579
  static int flow_dump(struct net *net, struct tcf_proto *tp, void *fh,
12db03b65   Vlad Buslov   net: sched: exten...
580
  		     struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
581
  {
8113c0956   WANG Cong   net_sched: use vo...
582
  	struct flow_filter *f = fh;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
583
584
585
586
587
588
  	struct nlattr *nest;
  
  	if (f == NULL)
  		return skb->len;
  
  	t->tcm_handle = f->handle;
ae0be8de9   Michal Kubecek   netlink: make nla...
589
  	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
590
591
  	if (nest == NULL)
  		goto nla_put_failure;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
592
593
594
  	if (nla_put_u32(skb, TCA_FLOW_KEYS, f->keymask) ||
  	    nla_put_u32(skb, TCA_FLOW_MODE, f->mode))
  		goto nla_put_failure;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
595
596
  
  	if (f->mask != ~0 || f->xor != 0) {
1b34ec43c   David S. Miller   pkt_sched: Stop u...
597
598
599
  		if (nla_put_u32(skb, TCA_FLOW_MASK, f->mask) ||
  		    nla_put_u32(skb, TCA_FLOW_XOR, f->xor))
  			goto nla_put_failure;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
600
  	}
1b34ec43c   David S. Miller   pkt_sched: Stop u...
601
602
603
604
605
606
  	if (f->rshift &&
  	    nla_put_u32(skb, TCA_FLOW_RSHIFT, f->rshift))
  		goto nla_put_failure;
  	if (f->addend &&
  	    nla_put_u32(skb, TCA_FLOW_ADDEND, f->addend))
  		goto nla_put_failure;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
607

1b34ec43c   David S. Miller   pkt_sched: Stop u...
608
609
610
611
612
613
  	if (f->divisor &&
  	    nla_put_u32(skb, TCA_FLOW_DIVISOR, f->divisor))
  		goto nla_put_failure;
  	if (f->baseclass &&
  	    nla_put_u32(skb, TCA_FLOW_BASECLASS, f->baseclass))
  		goto nla_put_failure;
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
614

1b34ec43c   David S. Miller   pkt_sched: Stop u...
615
616
617
  	if (f->perturb_period &&
  	    nla_put_u32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ))
  		goto nla_put_failure;
72d9794f4   Patrick McHardy   net-sched: cls_fl...
618

5da57f422   WANG Cong   net_sched: cls: r...
619
  	if (tcf_exts_dump(skb, &f->exts) < 0)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
620
  		goto nla_put_failure;
0aead5434   Rami Rosen   [NET_SCHED]: Add ...
621
  #ifdef CONFIG_NET_EMATCH
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
622
623
624
  	if (f->ematches.hdr.nmatches &&
  	    tcf_em_tree_dump(skb, &f->ematches, TCA_FLOW_EMATCHES) < 0)
  		goto nla_put_failure;
0aead5434   Rami Rosen   [NET_SCHED]: Add ...
625
  #endif
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
626
  	nla_nest_end(skb, nest);
5da57f422   WANG Cong   net_sched: cls: r...
627
  	if (tcf_exts_dump_stats(skb, &f->exts) < 0)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
628
629
630
631
632
  		goto nla_put_failure;
  
  	return skb->len;
  
  nla_put_failure:
6ea3b446b   Jiri Pirko   net: sched: cls: ...
633
  	nla_nest_cancel(skb, nest);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
634
635
  	return -1;
  }
12db03b65   Vlad Buslov   net: sched: exten...
636
637
  static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg,
  		      bool rtnl_held)
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
638
  {
70da9f0bf   John Fastabend   net: sched: cls_f...
639
  	struct flow_head *head = rtnl_dereference(tp->root);
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
640
  	struct flow_filter *f;
6a659cd06   Jiri Pirko   net_sched: cls_fl...
641
  	list_for_each_entry(f, &head->filters, list) {
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
642
643
  		if (arg->count < arg->skip)
  			goto skip;
8113c0956   WANG Cong   net_sched: use vo...
644
  		if (arg->fn(tp, f, arg) < 0) {
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
  			arg->stop = 1;
  			break;
  		}
  skip:
  		arg->count++;
  	}
  }
  
  static struct tcf_proto_ops cls_flow_ops __read_mostly = {
  	.kind		= "flow",
  	.classify	= flow_classify,
  	.init		= flow_init,
  	.destroy	= flow_destroy,
  	.change		= flow_change,
  	.delete		= flow_delete,
  	.get		= flow_get,
e5dfb8151   Patrick McHardy   [NET_SCHED]: Add ...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
  	.dump		= flow_dump,
  	.walk		= flow_walk,
  	.owner		= THIS_MODULE,
  };
  
  static int __init cls_flow_init(void)
  {
  	return register_tcf_proto_ops(&cls_flow_ops);
  }
  
  static void __exit cls_flow_exit(void)
  {
  	unregister_tcf_proto_ops(&cls_flow_ops);
  }
  
  module_init(cls_flow_init);
  module_exit(cls_flow_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
  MODULE_DESCRIPTION("TC flow classifier");