Blame view

net/sched/cls_route.c 14.4 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
  /*
   * net/sched/cls_route.c	ROUTE4 classifier.
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   */
  
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
9
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  #include <linux/errno.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/skbuff.h>
0ba480538   Patrick McHardy   [NET_SCHED]: Remo...
15
16
17
  #include <net/dst.h>
  #include <net/route.h>
  #include <net/netlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
  #include <net/act_api.h>
  #include <net/pkt_cls.h>
  
  /*
cc7ec456f   Eric Dumazet   net_sched: cleanups
22
23
24
25
26
   * 1. For now we assume that route tags < 256.
   *    It allows to use direct table lookups, instead of hash tables.
   * 2. For now we assume that "from TAG" and "fromdev DEV" statements
   *    are mutually  exclusive.
   * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
   */
cc7ec456f   Eric Dumazet   net_sched: cleanups
28
  struct route4_fastmap {
1109c0054   John Fastabend   net: sched: RCU c...
29
30
31
  	struct route4_filter		*filter;
  	u32				id;
  	int				iif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
33
  struct route4_head {
1109c0054   John Fastabend   net: sched: RCU c...
34
35
36
  	struct route4_fastmap		fastmap[16];
  	struct route4_bucket __rcu	*table[256 + 1];
  	struct rcu_head			rcu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
38
  struct route4_bucket {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  	/* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */
1109c0054   John Fastabend   net: sched: RCU c...
40
41
  	struct route4_filter __rcu	*ht[16 + 16 + 1];
  	struct rcu_head			rcu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
43
  struct route4_filter {
1109c0054   John Fastabend   net: sched: RCU c...
44
  	struct route4_filter __rcu	*next;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
50
51
  	u32			id;
  	int			iif;
  
  	struct tcf_result	res;
  	struct tcf_exts		exts;
  	u32			handle;
  	struct route4_bucket	*bkt;
1109c0054   John Fastabend   net: sched: RCU c...
52
  	struct tcf_proto	*tp;
aaa908ffb   Cong Wang   net_sched: switch...
53
  	struct rcu_work		rwork;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  };
cc7ec456f   Eric Dumazet   net_sched: cleanups
55
  #define ROUTE4_FAILURE ((struct route4_filter *)(-1L))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56

cc7ec456f   Eric Dumazet   net_sched: cleanups
57
  static inline int route4_fastmap_hash(u32 id, int iif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
59
  	return id & 0xF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  }
1109c0054   John Fastabend   net: sched: RCU c...
61
  static DEFINE_SPINLOCK(fastmap_lock);
cc7ec456f   Eric Dumazet   net_sched: cleanups
62
  static void
1109c0054   John Fastabend   net: sched: RCU c...
63
  route4_reset_fastmap(struct route4_head *head)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  {
1109c0054   John Fastabend   net: sched: RCU c...
65
  	spin_lock_bh(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  	memset(head->fastmap, 0, sizeof(head->fastmap));
1109c0054   John Fastabend   net: sched: RCU c...
67
  	spin_unlock_bh(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
69
  static void
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
  route4_set_fastmap(struct route4_head *head, u32 id, int iif,
  		   struct route4_filter *f)
  {
  	int h = route4_fastmap_hash(id, iif);
cc7ec456f   Eric Dumazet   net_sched: cleanups
74

1109c0054   John Fastabend   net: sched: RCU c...
75
76
  	/* fastmap updates must look atomic to aling id, iff, filter */
  	spin_lock_bh(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
  	head->fastmap[h].id = id;
  	head->fastmap[h].iif = iif;
  	head->fastmap[h].filter = f;
1109c0054   John Fastabend   net: sched: RCU c...
80
  	spin_unlock_bh(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
82
  static inline int route4_hash_to(u32 id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
84
  	return id & 0xFF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
86
  static inline int route4_hash_from(u32 id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
88
  	return (id >> 16) & 0xF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
90
  static inline int route4_hash_iif(int iif)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
92
  	return 16 + ((iif >> 16) & 0xF);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  }
cc7ec456f   Eric Dumazet   net_sched: cleanups
94
  static inline int route4_hash_wild(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
100
101
  {
  	return 32;
  }
  
  #define ROUTE4_APPLY_RESULT()					\
  {								\
  	*res = f->res;						\
6fc6d06e5   Jiri Pirko   net: sched: remov...
102
  	if (tcf_exts_has_actions(&f->exts)) {			\
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
104
105
106
107
108
109
110
111
112
  		int r = tcf_exts_exec(skb, &f->exts, res);	\
  		if (r < 0) {					\
  			dont_cache = 1;				\
  			continue;				\
  		}						\
  		return r;					\
  	} else if (!dont_cache)					\
  		route4_set_fastmap(head, id, iif, f);		\
  	return 0;						\
  }
dc7f9f6e8   Eric Dumazet   net: sched: const...
113
  static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
  			   struct tcf_result *res)
  {
1109c0054   John Fastabend   net: sched: RCU c...
116
  	struct route4_head *head = rcu_dereference_bh(tp->root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
  	struct dst_entry *dst;
  	struct route4_bucket *b;
  	struct route4_filter *f;
  	u32 id, h;
  	int iif, dont_cache = 0;
cc7ec456f   Eric Dumazet   net_sched: cleanups
122
123
  	dst = skb_dst(skb);
  	if (!dst)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
  		goto failure;
  
  	id = dst->tclassid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127

92101b3b2   David S. Miller   ipv4: Prepare for...
128
  	iif = inet_iif(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
  
  	h = route4_fastmap_hash(id, iif);
1109c0054   John Fastabend   net: sched: RCU c...
131
132
  
  	spin_lock(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
  	if (id == head->fastmap[h].id &&
  	    iif == head->fastmap[h].iif &&
  	    (f = head->fastmap[h].filter) != NULL) {
1109c0054   John Fastabend   net: sched: RCU c...
136
137
  		if (f == ROUTE4_FAILURE) {
  			spin_unlock(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  			goto failure;
1109c0054   John Fastabend   net: sched: RCU c...
139
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  
  		*res = f->res;
1109c0054   John Fastabend   net: sched: RCU c...
142
  		spin_unlock(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
  		return 0;
  	}
1109c0054   John Fastabend   net: sched: RCU c...
145
  	spin_unlock(&fastmap_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
  
  	h = route4_hash_to(id);
  
  restart:
1109c0054   John Fastabend   net: sched: RCU c...
150
  	b = rcu_dereference_bh(head->table[h]);
cc7ec456f   Eric Dumazet   net_sched: cleanups
151
  	if (b) {
1109c0054   John Fastabend   net: sched: RCU c...
152
153
154
  		for (f = rcu_dereference_bh(b->ht[route4_hash_from(id)]);
  		     f;
  		     f = rcu_dereference_bh(f->next))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
  			if (f->id == id)
  				ROUTE4_APPLY_RESULT();
1109c0054   John Fastabend   net: sched: RCU c...
157
158
159
  		for (f = rcu_dereference_bh(b->ht[route4_hash_iif(iif)]);
  		     f;
  		     f = rcu_dereference_bh(f->next))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  			if (f->iif == iif)
  				ROUTE4_APPLY_RESULT();
1109c0054   John Fastabend   net: sched: RCU c...
162
163
164
  		for (f = rcu_dereference_bh(b->ht[route4_hash_wild()]);
  		     f;
  		     f = rcu_dereference_bh(f->next))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  			ROUTE4_APPLY_RESULT();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
171
172
173
174
175
176
  	}
  	if (h < 256) {
  		h = 256;
  		id &= ~0xFFFF;
  		goto restart;
  	}
  
  	if (!dont_cache)
  		route4_set_fastmap(head, id, iif, ROUTE4_FAILURE);
  failure:
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
  }
  
  static inline u32 to_hash(u32 id)
  {
cc7ec456f   Eric Dumazet   net_sched: cleanups
181
182
183
  	u32 h = id & 0xFF;
  
  	if (id & 0x8000)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
188
189
190
191
192
193
194
195
  		h += 256;
  	return h;
  }
  
  static inline u32 from_hash(u32 id)
  {
  	id &= 0xFFFF;
  	if (id == 0xFFFF)
  		return 32;
  	if (!(id & 0x8000)) {
  		if (id > 255)
  			return 256;
cc7ec456f   Eric Dumazet   net_sched: cleanups
196
  		return id & 0xF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  	}
cc7ec456f   Eric Dumazet   net_sched: cleanups
198
  	return 16 + (id & 0xF);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  }
8113c0956   WANG Cong   net_sched: use vo...
200
  static void *route4_get(struct tcf_proto *tp, u32 handle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
1109c0054   John Fastabend   net: sched: RCU c...
202
  	struct route4_head *head = rtnl_dereference(tp->root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
  	struct route4_bucket *b;
  	struct route4_filter *f;
cc7ec456f   Eric Dumazet   net_sched: cleanups
205
  	unsigned int h1, h2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
  	h1 = to_hash(handle);
  	if (h1 > 256)
8113c0956   WANG Cong   net_sched: use vo...
209
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210

cc7ec456f   Eric Dumazet   net_sched: cleanups
211
  	h2 = from_hash(handle >> 16);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  	if (h2 > 32)
8113c0956   WANG Cong   net_sched: use vo...
213
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214

1109c0054   John Fastabend   net: sched: RCU c...
215
  	b = rtnl_dereference(head->table[h1]);
cc7ec456f   Eric Dumazet   net_sched: cleanups
216
  	if (b) {
1109c0054   John Fastabend   net: sched: RCU c...
217
218
219
  		for (f = rtnl_dereference(b->ht[h2]);
  		     f;
  		     f = rtnl_dereference(f->next))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
  			if (f->handle == handle)
8113c0956   WANG Cong   net_sched: use vo...
221
  				return f;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  	}
8113c0956   WANG Cong   net_sched: use vo...
223
  	return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
  static int route4_init(struct tcf_proto *tp)
  {
a05c2d112   WANG Cong   net_sched: move t...
227
228
229
230
231
232
233
  	struct route4_head *head;
  
  	head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
  	if (head == NULL)
  		return -ENOBUFS;
  
  	rcu_assign_pointer(tp->root, head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
  	return 0;
  }
3fd51de5e   Cong Wang   cls_route: use tc...
236
237
238
239
240
241
  static void __route4_delete_filter(struct route4_filter *f)
  {
  	tcf_exts_destroy(&f->exts);
  	tcf_exts_put_net(&f->exts);
  	kfree(f);
  }
c2f3f31d4   Cong Wang   net_sched: use tc...
242
  static void route4_delete_filter_work(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
243
  {
aaa908ffb   Cong Wang   net_sched: switch...
244
245
246
  	struct route4_filter *f = container_of(to_rcu_work(work),
  					       struct route4_filter,
  					       rwork);
c2f3f31d4   Cong Wang   net_sched: use tc...
247
  	rtnl_lock();
3fd51de5e   Cong Wang   cls_route: use tc...
248
  	__route4_delete_filter(f);
c2f3f31d4   Cong Wang   net_sched: use tc...
249
250
  	rtnl_unlock();
  }
aaa908ffb   Cong Wang   net_sched: switch...
251
  static void route4_queue_work(struct route4_filter *f)
c2f3f31d4   Cong Wang   net_sched: use tc...
252
  {
aaa908ffb   Cong Wang   net_sched: switch...
253
  	tcf_queue_work(&f->rwork, route4_delete_filter_work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  }
12db03b65   Vlad Buslov   net: sched: exten...
255
256
  static void route4_destroy(struct tcf_proto *tp, bool rtnl_held,
  			   struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
  {
1109c0054   John Fastabend   net: sched: RCU c...
258
  	struct route4_head *head = rtnl_dereference(tp->root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
  	int h1, h2;
  
  	if (head == NULL)
763dbf632   WANG Cong   net_sched: move t...
262
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263

cc7ec456f   Eric Dumazet   net_sched: cleanups
264
  	for (h1 = 0; h1 <= 256; h1++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  		struct route4_bucket *b;
1109c0054   John Fastabend   net: sched: RCU c...
266
  		b = rtnl_dereference(head->table[h1]);
cc7ec456f   Eric Dumazet   net_sched: cleanups
267
268
  		if (b) {
  			for (h2 = 0; h2 <= 32; h2++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  				struct route4_filter *f;
1109c0054   John Fastabend   net: sched: RCU c...
270
271
272
273
274
  				while ((f = rtnl_dereference(b->ht[h2])) != NULL) {
  					struct route4_filter *next;
  
  					next = rtnl_dereference(f->next);
  					RCU_INIT_POINTER(b->ht[h2], next);
18cdb37eb   John Fastabend   net: sched: do no...
275
  					tcf_unbind_filter(tp, &f->res);
3fd51de5e   Cong Wang   cls_route: use tc...
276
  					if (tcf_exts_get_net(&f->exts))
aaa908ffb   Cong Wang   net_sched: switch...
277
  						route4_queue_work(f);
3fd51de5e   Cong Wang   cls_route: use tc...
278
279
  					else
  						__route4_delete_filter(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
  				}
  			}
1109c0054   John Fastabend   net: sched: RCU c...
282
283
  			RCU_INIT_POINTER(head->table[h1], NULL);
  			kfree_rcu(b, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
  		}
  	}
1109c0054   John Fastabend   net: sched: RCU c...
286
  	kfree_rcu(head, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  }
571acf210   Alexander Aring   net: sched: cls: ...
288
  static int route4_delete(struct tcf_proto *tp, void *arg, bool *last,
12db03b65   Vlad Buslov   net: sched: exten...
289
  			 bool rtnl_held, struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  {
1109c0054   John Fastabend   net: sched: RCU c...
291
  	struct route4_head *head = rtnl_dereference(tp->root);
8113c0956   WANG Cong   net_sched: use vo...
292
  	struct route4_filter *f = arg;
1109c0054   John Fastabend   net: sched: RCU c...
293
294
  	struct route4_filter __rcu **fp;
  	struct route4_filter *nf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
  	struct route4_bucket *b;
1109c0054   John Fastabend   net: sched: RCU c...
296
  	unsigned int h = 0;
763dbf632   WANG Cong   net_sched: move t...
297
  	int i, h1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
  
  	if (!head || !f)
  		return -EINVAL;
  
  	h = f->handle;
  	b = f->bkt;
1109c0054   John Fastabend   net: sched: RCU c...
304
305
306
307
308
309
  	fp = &b->ht[from_hash(h >> 16)];
  	for (nf = rtnl_dereference(*fp); nf;
  	     fp = &nf->next, nf = rtnl_dereference(*fp)) {
  		if (nf == f) {
  			/* unlink it */
  			RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310

1109c0054   John Fastabend   net: sched: RCU c...
311
312
313
314
315
  			/* Remove any fastmap lookups that might ref filter
  			 * notice we unlink'd the filter so we can't get it
  			 * back in the fastmap.
  			 */
  			route4_reset_fastmap(head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316

1109c0054   John Fastabend   net: sched: RCU c...
317
  			/* Delete it */
18cdb37eb   John Fastabend   net: sched: do no...
318
  			tcf_unbind_filter(tp, &f->res);
3fd51de5e   Cong Wang   cls_route: use tc...
319
  			tcf_exts_get_net(&f->exts);
aaa908ffb   Cong Wang   net_sched: switch...
320
  			tcf_queue_work(&f->rwork, route4_delete_filter_work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321

1109c0054   John Fastabend   net: sched: RCU c...
322
323
324
325
326
327
  			/* Strip RTNL protected tree */
  			for (i = 0; i <= 32; i++) {
  				struct route4_filter *rt;
  
  				rt = rtnl_dereference(b->ht[i]);
  				if (rt)
763dbf632   WANG Cong   net_sched: move t...
328
  					goto out;
1109c0054   John Fastabend   net: sched: RCU c...
329
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
  
  			/* OK, session has no flows */
1109c0054   John Fastabend   net: sched: RCU c...
332
333
  			RCU_INIT_POINTER(head->table[to_hash(h)], NULL);
  			kfree_rcu(b, rcu);
763dbf632   WANG Cong   net_sched: move t...
334
335
336
  			break;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337

763dbf632   WANG Cong   net_sched: move t...
338
339
340
341
342
343
  out:
  	*last = true;
  	for (h1 = 0; h1 <= 256; h1++) {
  		if (rcu_access_pointer(head->table[h1])) {
  			*last = false;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
  		}
  	}
763dbf632   WANG Cong   net_sched: move t...
346

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
  	return 0;
  }
6fa8c0144   Patrick McHardy   [NET_SCHED]: Use ...
349
350
351
352
353
354
  static const struct nla_policy route4_policy[TCA_ROUTE4_MAX + 1] = {
  	[TCA_ROUTE4_CLASSID]	= { .type = NLA_U32 },
  	[TCA_ROUTE4_TO]		= { .type = NLA_U32 },
  	[TCA_ROUTE4_FROM]	= { .type = NLA_U32 },
  	[TCA_ROUTE4_IIF]	= { .type = NLA_U32 },
  };
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
355
356
357
  static int route4_set_parms(struct net *net, struct tcf_proto *tp,
  			    unsigned long base, struct route4_filter *f,
  			    u32 handle, struct route4_head *head,
2f7ef2f87   Cong Wang   sched, cls: check...
358
  			    struct nlattr **tb, struct nlattr *est, int new,
50a561900   Alexander Aring   net: sched: cls: ...
359
  			    bool ovr, struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
362
363
364
  	u32 id = 0, to = 0, nhandle = 0x8000;
  	struct route4_filter *fp;
  	unsigned int h1;
  	struct route4_bucket *b;
b9a24bb76   WANG Cong   net_sched: proper...
365
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366

ec6743a10   Vlad Buslov   net: sched: track...
367
  	err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true, extack);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
  	if (err < 0)
  		return err;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
370
  	if (tb[TCA_ROUTE4_TO]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
  		if (new && handle & 0x8000)
8c98d571b   Jiri Pirko   net: sched: cls_r...
372
  			return -EINVAL;
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
373
  		to = nla_get_u32(tb[TCA_ROUTE4_TO]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  		if (to > 0xFF)
8c98d571b   Jiri Pirko   net: sched: cls_r...
375
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
  		nhandle = to;
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
378
379
  	if (tb[TCA_ROUTE4_FROM]) {
  		if (tb[TCA_ROUTE4_IIF])
8c98d571b   Jiri Pirko   net: sched: cls_r...
380
  			return -EINVAL;
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
381
  		id = nla_get_u32(tb[TCA_ROUTE4_FROM]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  		if (id > 0xFF)
8c98d571b   Jiri Pirko   net: sched: cls_r...
383
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  		nhandle |= id << 16;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
385
  	} else if (tb[TCA_ROUTE4_IIF]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
386
  		id = nla_get_u32(tb[TCA_ROUTE4_IIF]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
  		if (id > 0x7FFF)
8c98d571b   Jiri Pirko   net: sched: cls_r...
388
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
394
395
  		nhandle |= (id | 0x8000) << 16;
  	} else
  		nhandle |= 0xFFFF << 16;
  
  	if (handle && new) {
  		nhandle |= handle & 0x7F00;
  		if (nhandle != handle)
8c98d571b   Jiri Pirko   net: sched: cls_r...
396
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
398
399
  	}
  
  	h1 = to_hash(nhandle);
1109c0054   John Fastabend   net: sched: RCU c...
400
  	b = rtnl_dereference(head->table[h1]);
cc7ec456f   Eric Dumazet   net_sched: cleanups
401
  	if (!b) {
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
402
  		b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  		if (b == NULL)
8c98d571b   Jiri Pirko   net: sched: cls_r...
404
  			return -ENOBUFS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405

1109c0054   John Fastabend   net: sched: RCU c...
406
  		rcu_assign_pointer(head->table[h1], b);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
  	} else {
  		unsigned int h2 = from_hash(nhandle >> 16);
cc7ec456f   Eric Dumazet   net_sched: cleanups
409

1109c0054   John Fastabend   net: sched: RCU c...
410
411
412
  		for (fp = rtnl_dereference(b->ht[h2]);
  		     fp;
  		     fp = rtnl_dereference(fp->next))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
  			if (fp->handle == f->handle)
8c98d571b   Jiri Pirko   net: sched: cls_r...
414
  				return -EEXIST;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
  	}
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
416
  	if (tb[TCA_ROUTE4_TO])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
  		f->id = to;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
418
  	if (tb[TCA_ROUTE4_FROM])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
  		f->id = to | id<<16;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
420
  	else if (tb[TCA_ROUTE4_IIF])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
423
424
  		f->iif = id;
  
  	f->handle = nhandle;
  	f->bkt = b;
1109c0054   John Fastabend   net: sched: RCU c...
425
  	f->tp = tp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426

add93b610   Patrick McHardy   [NET_SCHED]: Conv...
427
  	if (tb[TCA_ROUTE4_CLASSID]) {
1587bac49   Patrick McHardy   [NET_SCHED]: Use ...
428
  		f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
  		tcf_bind_filter(tp, &f->res, base);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
  }
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
433
  static int route4_change(struct net *net, struct sk_buff *in_skb,
5a7a5555a   Jamal Hadi Salim   net sched: stylis...
434
  			 struct tcf_proto *tp, unsigned long base, u32 handle,
7306db38a   Alexander Aring   net: sched: cls: ...
435
  			 struct nlattr **tca, void **arg, bool ovr,
12db03b65   Vlad Buslov   net: sched: exten...
436
  			 bool rtnl_held, struct netlink_ext_ack *extack)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  {
1109c0054   John Fastabend   net: sched: RCU c...
438
439
440
  	struct route4_head *head = rtnl_dereference(tp->root);
  	struct route4_filter __rcu **fp;
  	struct route4_filter *fold, *f1, *pfp, *f = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  	struct route4_bucket *b;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
442
443
  	struct nlattr *opt = tca[TCA_OPTIONS];
  	struct nlattr *tb[TCA_ROUTE4_MAX + 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
  	unsigned int h, th;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
  	int err;
1109c0054   John Fastabend   net: sched: RCU c...
446
  	bool new = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
  
  	if (opt == NULL)
  		return handle ? -EINVAL : 0;
8cb081746   Johannes Berg   netlink: make val...
450
451
  	err = nla_parse_nested_deprecated(tb, TCA_ROUTE4_MAX, opt,
  					  route4_policy, NULL);
cee63723b   Patrick McHardy   [NET_SCHED]: Prop...
452
453
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454

8113c0956   WANG Cong   net_sched: use vo...
455
  	fold = *arg;
1109c0054   John Fastabend   net: sched: RCU c...
456
  	if (fold && handle && fold->handle != handle)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  	err = -ENOBUFS;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
459
  	f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
1109c0054   John Fastabend   net: sched: RCU c...
460
  	if (!f)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462

14215108a   Cong Wang   net_sched: initia...
463
  	err = tcf_exts_init(&f->exts, net, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
b9a24bb76   WANG Cong   net_sched: proper...
464
465
  	if (err < 0)
  		goto errout;
1109c0054   John Fastabend   net: sched: RCU c...
466
467
468
469
470
471
472
473
474
475
  	if (fold) {
  		f->id = fold->id;
  		f->iif = fold->iif;
  		f->res = fold->res;
  		f->handle = fold->handle;
  
  		f->tp = fold->tp;
  		f->bkt = fold->bkt;
  		new = false;
  	}
c1b52739e   Benjamin LaHaise   pkt_sched: namesp...
476
  	err = route4_set_parms(net, tp, base, f, handle, head, tb,
50a561900   Alexander Aring   net: sched: cls: ...
477
  			       tca[TCA_RATE], new, ovr, extack);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
  	if (err < 0)
  		goto errout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
  	h = from_hash(f->handle >> 16);
1109c0054   John Fastabend   net: sched: RCU c...
481
482
483
484
  	fp = &f->bkt->ht[h];
  	for (pfp = rtnl_dereference(*fp);
  	     (f1 = rtnl_dereference(*fp)) != NULL;
  	     fp = &f1->next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
  		if (f->handle < f1->handle)
  			break;
f36fe1c49   Jiri Pirko   net: sched: intro...
487
  	tcf_block_netif_keep_dst(tp->chain->block);
1109c0054   John Fastabend   net: sched: RCU c...
488
489
  	rcu_assign_pointer(f->next, f1);
  	rcu_assign_pointer(*fp, f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490

1109c0054   John Fastabend   net: sched: RCU c...
491
492
493
494
  	if (fold && fold->handle && f->handle != fold->handle) {
  		th = to_hash(fold->handle);
  		h = from_hash(fold->handle >> 16);
  		b = rtnl_dereference(head->table[th]);
cc7ec456f   Eric Dumazet   net_sched: cleanups
495
  		if (b) {
1109c0054   John Fastabend   net: sched: RCU c...
496
497
498
499
  			fp = &b->ht[h];
  			for (pfp = rtnl_dereference(*fp); pfp;
  			     fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
  				if (pfp == f) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
504
505
  					*fp = f->next;
  					break;
  				}
  			}
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506

1109c0054   John Fastabend   net: sched: RCU c...
507
  	route4_reset_fastmap(head);
8113c0956   WANG Cong   net_sched: use vo...
508
  	*arg = f;
18cdb37eb   John Fastabend   net: sched: do no...
509
510
  	if (fold) {
  		tcf_unbind_filter(tp, &fold->res);
3fd51de5e   Cong Wang   cls_route: use tc...
511
  		tcf_exts_get_net(&fold->exts);
aaa908ffb   Cong Wang   net_sched: switch...
512
  		tcf_queue_work(&fold->rwork, route4_delete_filter_work);
18cdb37eb   John Fastabend   net: sched: do no...
513
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
  	return 0;
  
  errout:
21641c2e1   WANG Cong   net_sched: check ...
517
518
  	if (f)
  		tcf_exts_destroy(&f->exts);
a51482bde   Jesper Juhl   [NET]: kfree cleanup
519
  	kfree(f);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
  	return err;
  }
12db03b65   Vlad Buslov   net: sched: exten...
522
523
  static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg,
  			bool rtnl_held)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
  {
1109c0054   John Fastabend   net: sched: RCU c...
525
  	struct route4_head *head = rtnl_dereference(tp->root);
cc7ec456f   Eric Dumazet   net_sched: cleanups
526
  	unsigned int h, h1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527

3027ff41f   Vlad Buslov   net: sched: route...
528
  	if (head == NULL || arg->stop)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
  		return;
  
  	for (h = 0; h <= 256; h++) {
1109c0054   John Fastabend   net: sched: RCU c...
532
  		struct route4_bucket *b = rtnl_dereference(head->table[h]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
  
  		if (b) {
  			for (h1 = 0; h1 <= 32; h1++) {
  				struct route4_filter *f;
1109c0054   John Fastabend   net: sched: RCU c...
537
538
539
  				for (f = rtnl_dereference(b->ht[h1]);
  				     f;
  				     f = rtnl_dereference(f->next)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
  					if (arg->count < arg->skip) {
  						arg->count++;
  						continue;
  					}
8113c0956   WANG Cong   net_sched: use vo...
544
  					if (arg->fn(tp, f, arg) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
548
549
550
551
552
553
  						arg->stop = 1;
  						return;
  					}
  					arg->count++;
  				}
  			}
  		}
  	}
  }
8113c0956   WANG Cong   net_sched: use vo...
554
  static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh,
12db03b65   Vlad Buslov   net: sched: exten...
555
  		       struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
  {
8113c0956   WANG Cong   net_sched: use vo...
557
  	struct route4_filter *f = fh;
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
558
  	struct nlattr *nest;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
561
562
563
564
  	u32 id;
  
  	if (f == NULL)
  		return skb->len;
  
  	t->tcm_handle = f->handle;
ae0be8de9   Michal Kubecek   netlink: make nla...
565
  	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
566
567
  	if (nest == NULL)
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568

cc7ec456f   Eric Dumazet   net_sched: cleanups
569
570
  	if (!(f->handle & 0x8000)) {
  		id = f->id & 0xFF;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
571
572
  		if (nla_put_u32(skb, TCA_ROUTE4_TO, id))
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  	}
cc7ec456f   Eric Dumazet   net_sched: cleanups
574
  	if (f->handle & 0x80000000) {
1b34ec43c   David S. Miller   pkt_sched: Stop u...
575
576
577
  		if ((f->handle >> 16) != 0xFFFF &&
  		    nla_put_u32(skb, TCA_ROUTE4_IIF, f->iif))
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
  	} else {
cc7ec456f   Eric Dumazet   net_sched: cleanups
579
  		id = f->id >> 16;
1b34ec43c   David S. Miller   pkt_sched: Stop u...
580
581
  		if (nla_put_u32(skb, TCA_ROUTE4_FROM, id))
  			goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  	}
1b34ec43c   David S. Miller   pkt_sched: Stop u...
583
584
585
  	if (f->res.classid &&
  	    nla_put_u32(skb, TCA_ROUTE4_CLASSID, f->res.classid))
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586

5da57f422   WANG Cong   net_sched: cls: r...
587
  	if (tcf_exts_dump(skb, &f->exts) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
588
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
589

4b3550ef5   Patrick McHardy   [NET_SCHED]: Use ...
590
  	nla_nest_end(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591

5da57f422   WANG Cong   net_sched: cls: r...
592
  	if (tcf_exts_dump_stats(skb, &f->exts) < 0)
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
593
  		goto nla_put_failure;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
594
595
  
  	return skb->len;
add93b610   Patrick McHardy   [NET_SCHED]: Conv...
596
  nla_put_failure:
6ea3b446b   Jiri Pirko   net: sched: cls: ...
597
  	nla_nest_cancel(skb, nest);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
  	return -1;
  }
55ec468d3   Cong Wang   net_sched: fix op...
600
601
  static void route4_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
  			      unsigned long base)
07d79fc7d   Cong Wang   net_sched: add re...
602
603
  {
  	struct route4_filter *f = fh;
55ec468d3   Cong Wang   net_sched: fix op...
604
605
606
607
608
609
  	if (f && f->res.classid == classid) {
  		if (cl)
  			__tcf_bind_filter(q, &f->res, base);
  		else
  			__tcf_unbind_filter(q, &f->res);
  	}
07d79fc7d   Cong Wang   net_sched: add re...
610
  }
2eb9d75c7   Patrick McHardy   [NET_SCHED]: mark...
611
  static struct tcf_proto_ops cls_route4_ops __read_mostly = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
  	.kind		=	"route",
  	.classify	=	route4_classify,
  	.init		=	route4_init,
  	.destroy	=	route4_destroy,
  	.get		=	route4_get,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
  	.change		=	route4_change,
  	.delete		=	route4_delete,
  	.walk		=	route4_walk,
  	.dump		=	route4_dump,
07d79fc7d   Cong Wang   net_sched: add re...
621
  	.bind_class	=	route4_bind_class,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
  	.owner		=	THIS_MODULE,
  };
  
  static int __init init_route4(void)
  {
  	return register_tcf_proto_ops(&cls_route4_ops);
  }
  
  static void __exit exit_route4(void)
  {
  	unregister_tcf_proto_ops(&cls_route4_ops);
  }
  
  module_init(init_route4)
  module_exit(exit_route4)
  MODULE_LICENSE("GPL");