12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e5dfb815SPatrick McHardy /*
3e5dfb815SPatrick McHardy * net/sched/cls_flow.c Generic flow classifier
4e5dfb815SPatrick McHardy *
5e5dfb815SPatrick McHardy * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
6e5dfb815SPatrick McHardy */
7e5dfb815SPatrick McHardy
8e5dfb815SPatrick McHardy #include <linux/kernel.h>
9e5dfb815SPatrick McHardy #include <linux/init.h>
10e5dfb815SPatrick McHardy #include <linux/list.h>
11e5dfb815SPatrick McHardy #include <linux/jhash.h>
12e5dfb815SPatrick McHardy #include <linux/random.h>
13e5dfb815SPatrick McHardy #include <linux/pkt_cls.h>
14e5dfb815SPatrick McHardy #include <linux/skbuff.h>
15e5dfb815SPatrick McHardy #include <linux/in.h>
16e5dfb815SPatrick McHardy #include <linux/ip.h>
17e5dfb815SPatrick McHardy #include <linux/ipv6.h>
189ec13810SPatrick McHardy #include <linux/if_vlan.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
203a9a231dSPaul Gortmaker #include <linux/module.h>
21743b2a66SEric Dumazet #include <net/inet_sock.h>
22e5dfb815SPatrick McHardy
23e5dfb815SPatrick McHardy #include <net/pkt_cls.h>
24e5dfb815SPatrick McHardy #include <net/ip.h>
25e5dfb815SPatrick McHardy #include <net/route.h>
261bd758ebSJiri Pirko #include <net/flow_dissector.h>
279f3101dcSPedro Tammela #include <net/tc_wrapper.h>
286bd2a9afSEric Dumazet
290013de38SJavier Martinez Canillas #if IS_ENABLED(CONFIG_NF_CONNTRACK)
30e5dfb815SPatrick McHardy #include <net/netfilter/nf_conntrack.h>
31e5dfb815SPatrick McHardy #endif
32e5dfb815SPatrick McHardy
33e5dfb815SPatrick McHardy struct flow_head {
34e5dfb815SPatrick McHardy struct list_head filters;
3570da9f0bSJohn Fastabend struct rcu_head rcu;
36e5dfb815SPatrick McHardy };
37e5dfb815SPatrick McHardy
38e5dfb815SPatrick McHardy struct flow_filter {
39e5dfb815SPatrick McHardy struct list_head list;
40e5dfb815SPatrick McHardy struct tcf_exts exts;
41e5dfb815SPatrick McHardy struct tcf_ematch_tree ematches;
4270da9f0bSJohn Fastabend struct tcf_proto *tp;
4372d9794fSPatrick McHardy struct timer_list perturb_timer;
4472d9794fSPatrick McHardy u32 perturb_period;
45e5dfb815SPatrick McHardy u32 handle;
46e5dfb815SPatrick McHardy
47e5dfb815SPatrick McHardy u32 nkeys;
48e5dfb815SPatrick McHardy u32 keymask;
49e5dfb815SPatrick McHardy u32 mode;
50e5dfb815SPatrick McHardy u32 mask;
51e5dfb815SPatrick McHardy u32 xor;
52e5dfb815SPatrick McHardy u32 rshift;
53e5dfb815SPatrick McHardy u32 addend;
54e5dfb815SPatrick McHardy u32 divisor;
55e5dfb815SPatrick McHardy u32 baseclass;
5672d9794fSPatrick McHardy u32 hashrnd;
57aaa908ffSCong Wang struct rcu_work rwork;
5894cdb475SCong Wang };
59e5dfb815SPatrick McHardy
addr_fold(void * addr)60e5dfb815SPatrick McHardy static inline u32 addr_fold(void *addr)
61e5dfb815SPatrick McHardy {
62e5dfb815SPatrick McHardy unsigned long a = (unsigned long)addr;
63e5dfb815SPatrick McHardy
64e5dfb815SPatrick McHardy return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0);
65e5dfb815SPatrick McHardy }
66e5dfb815SPatrick McHardy
flow_get_src(const struct sk_buff * skb,const struct flow_keys * flow)676bd2a9afSEric Dumazet static u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow)
68e5dfb815SPatrick McHardy {
69c3f83241STom Herbert __be32 src = flow_get_u32_src(flow);
70c3f83241STom Herbert
71c3f83241STom Herbert if (src)
72c3f83241STom Herbert return ntohl(src);
73c3f83241STom Herbert
74e5dfb815SPatrick McHardy return addr_fold(skb->sk);
75e5dfb815SPatrick McHardy }
76e5dfb815SPatrick McHardy
flow_get_dst(const struct sk_buff * skb,const struct flow_keys * flow)776bd2a9afSEric Dumazet static u32 flow_get_dst(const struct sk_buff *skb, const struct flow_keys *flow)
78e5dfb815SPatrick McHardy {
79c3f83241STom Herbert __be32 dst = flow_get_u32_dst(flow);
80c3f83241STom Herbert
81c3f83241STom Herbert if (dst)
82c3f83241STom Herbert return ntohl(dst);
83c3f83241STom Herbert
84d7bf2ebeSToke Høiland-Jørgensen return addr_fold(skb_dst(skb)) ^ (__force u16)skb_protocol(skb, true);
85e5dfb815SPatrick McHardy }
86e5dfb815SPatrick McHardy
flow_get_proto(const struct sk_buff * skb,const struct flow_keys * flow)875a7a5555SJamal Hadi Salim static u32 flow_get_proto(const struct sk_buff *skb,
885a7a5555SJamal Hadi Salim const struct flow_keys *flow)
89e5dfb815SPatrick McHardy {
9006635a35SJiri Pirko return flow->basic.ip_proto;
91e5dfb815SPatrick McHardy }
92e5dfb815SPatrick McHardy
flow_get_proto_src(const struct sk_buff * skb,const struct flow_keys * flow)935a7a5555SJamal Hadi Salim static u32 flow_get_proto_src(const struct sk_buff *skb,
945a7a5555SJamal Hadi Salim const struct flow_keys *flow)
95e5dfb815SPatrick McHardy {
9606635a35SJiri Pirko if (flow->ports.ports)
9759346afeSJiri Pirko return ntohs(flow->ports.src);
98859c2012SEric Dumazet
994b95c3d4SChangli Gao return addr_fold(skb->sk);
100e5dfb815SPatrick McHardy }
101e5dfb815SPatrick McHardy
flow_get_proto_dst(const struct sk_buff * skb,const struct flow_keys * flow)1025a7a5555SJamal Hadi Salim static u32 flow_get_proto_dst(const struct sk_buff *skb,
1035a7a5555SJamal Hadi Salim const struct flow_keys *flow)
104e5dfb815SPatrick McHardy {
10506635a35SJiri Pirko if (flow->ports.ports)
10659346afeSJiri Pirko return ntohs(flow->ports.dst);
107e5dfb815SPatrick McHardy
108d7bf2ebeSToke Høiland-Jørgensen return addr_fold(skb_dst(skb)) ^ (__force u16)skb_protocol(skb, true);
109e5dfb815SPatrick McHardy }
110e5dfb815SPatrick McHardy
flow_get_iif(const struct sk_buff * skb)111e5dfb815SPatrick McHardy static u32 flow_get_iif(const struct sk_buff *skb)
112e5dfb815SPatrick McHardy {
1138964be4aSEric Dumazet return skb->skb_iif;
114e5dfb815SPatrick McHardy }
115e5dfb815SPatrick McHardy
flow_get_priority(const struct sk_buff * skb)116e5dfb815SPatrick McHardy static u32 flow_get_priority(const struct sk_buff *skb)
117e5dfb815SPatrick McHardy {
118e5dfb815SPatrick McHardy return skb->priority;
119e5dfb815SPatrick McHardy }
120e5dfb815SPatrick McHardy
flow_get_mark(const struct sk_buff * skb)121e5dfb815SPatrick McHardy static u32 flow_get_mark(const struct sk_buff *skb)
122e5dfb815SPatrick McHardy {
123e5dfb815SPatrick McHardy return skb->mark;
124e5dfb815SPatrick McHardy }
125e5dfb815SPatrick McHardy
flow_get_nfct(const struct sk_buff * skb)126e5dfb815SPatrick McHardy static u32 flow_get_nfct(const struct sk_buff *skb)
127e5dfb815SPatrick McHardy {
1280013de38SJavier Martinez Canillas #if IS_ENABLED(CONFIG_NF_CONNTRACK)
129cb9c6836SFlorian Westphal return addr_fold(skb_nfct(skb));
130e5dfb815SPatrick McHardy #else
131e5dfb815SPatrick McHardy return 0;
132e5dfb815SPatrick McHardy #endif
133e5dfb815SPatrick McHardy }
134e5dfb815SPatrick McHardy
1350013de38SJavier Martinez Canillas #if IS_ENABLED(CONFIG_NF_CONNTRACK)
136e5dfb815SPatrick McHardy #define CTTUPLE(skb, member) \
137e5dfb815SPatrick McHardy ({ \
138e5dfb815SPatrick McHardy enum ip_conntrack_info ctinfo; \
139859c2012SEric Dumazet const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); \
140e5dfb815SPatrick McHardy if (ct == NULL) \
141e5dfb815SPatrick McHardy goto fallback; \
142e5dfb815SPatrick McHardy ct->tuplehash[CTINFO2DIR(ctinfo)].tuple.member; \
143e5dfb815SPatrick McHardy })
144e5dfb815SPatrick McHardy #else
145e5dfb815SPatrick McHardy #define CTTUPLE(skb, member) \
146e5dfb815SPatrick McHardy ({ \
147e5dfb815SPatrick McHardy goto fallback; \
148e5dfb815SPatrick McHardy 0; \
149e5dfb815SPatrick McHardy })
150e5dfb815SPatrick McHardy #endif
151e5dfb815SPatrick McHardy
flow_get_nfct_src(const struct sk_buff * skb,const struct flow_keys * flow)1525a7a5555SJamal Hadi Salim static u32 flow_get_nfct_src(const struct sk_buff *skb,
1535a7a5555SJamal Hadi Salim const struct flow_keys *flow)
154e5dfb815SPatrick McHardy {
155d7bf2ebeSToke Høiland-Jørgensen switch (skb_protocol(skb, true)) {
15660678040SArnaldo Carvalho de Melo case htons(ETH_P_IP):
157e5dfb815SPatrick McHardy return ntohl(CTTUPLE(skb, src.u3.ip));
15860678040SArnaldo Carvalho de Melo case htons(ETH_P_IPV6):
159e5dfb815SPatrick McHardy return ntohl(CTTUPLE(skb, src.u3.ip6[3]));
160e5dfb815SPatrick McHardy }
161e5dfb815SPatrick McHardy fallback:
1626bd2a9afSEric Dumazet return flow_get_src(skb, flow);
163e5dfb815SPatrick McHardy }
164e5dfb815SPatrick McHardy
flow_get_nfct_dst(const struct sk_buff * skb,const struct flow_keys * flow)1655a7a5555SJamal Hadi Salim static u32 flow_get_nfct_dst(const struct sk_buff *skb,
1665a7a5555SJamal Hadi Salim const struct flow_keys *flow)
167e5dfb815SPatrick McHardy {
168d7bf2ebeSToke Høiland-Jørgensen switch (skb_protocol(skb, true)) {
16960678040SArnaldo Carvalho de Melo case htons(ETH_P_IP):
170e5dfb815SPatrick McHardy return ntohl(CTTUPLE(skb, dst.u3.ip));
17160678040SArnaldo Carvalho de Melo case htons(ETH_P_IPV6):
172e5dfb815SPatrick McHardy return ntohl(CTTUPLE(skb, dst.u3.ip6[3]));
173e5dfb815SPatrick McHardy }
174e5dfb815SPatrick McHardy fallback:
1756bd2a9afSEric Dumazet return flow_get_dst(skb, flow);
176e5dfb815SPatrick McHardy }
177e5dfb815SPatrick McHardy
flow_get_nfct_proto_src(const struct sk_buff * skb,const struct flow_keys * flow)1785a7a5555SJamal Hadi Salim static u32 flow_get_nfct_proto_src(const struct sk_buff *skb,
1795a7a5555SJamal Hadi Salim const struct flow_keys *flow)
180e5dfb815SPatrick McHardy {
181e5dfb815SPatrick McHardy return ntohs(CTTUPLE(skb, src.u.all));
182e5dfb815SPatrick McHardy fallback:
1836bd2a9afSEric Dumazet return flow_get_proto_src(skb, flow);
184e5dfb815SPatrick McHardy }
185e5dfb815SPatrick McHardy
flow_get_nfct_proto_dst(const struct sk_buff * skb,const struct flow_keys * flow)1865a7a5555SJamal Hadi Salim static u32 flow_get_nfct_proto_dst(const struct sk_buff *skb,
1875a7a5555SJamal Hadi Salim const struct flow_keys *flow)
188e5dfb815SPatrick McHardy {
189e5dfb815SPatrick McHardy return ntohs(CTTUPLE(skb, dst.u.all));
190e5dfb815SPatrick McHardy fallback:
1916bd2a9afSEric Dumazet return flow_get_proto_dst(skb, flow);
192e5dfb815SPatrick McHardy }
193e5dfb815SPatrick McHardy
flow_get_rtclassid(const struct sk_buff * skb)194e5dfb815SPatrick McHardy static u32 flow_get_rtclassid(const struct sk_buff *skb)
195e5dfb815SPatrick McHardy {
196c7066f70SPatrick McHardy #ifdef CONFIG_IP_ROUTE_CLASSID
197adf30907SEric Dumazet if (skb_dst(skb))
198adf30907SEric Dumazet return skb_dst(skb)->tclassid;
199e5dfb815SPatrick McHardy #endif
200e5dfb815SPatrick McHardy return 0;
201e5dfb815SPatrick McHardy }
202e5dfb815SPatrick McHardy
flow_get_skuid(const struct sk_buff * skb)203e5dfb815SPatrick McHardy static u32 flow_get_skuid(const struct sk_buff *skb)
204e5dfb815SPatrick McHardy {
205743b2a66SEric Dumazet struct sock *sk = skb_to_full_sk(skb);
206743b2a66SEric Dumazet
207743b2a66SEric Dumazet if (sk && sk->sk_socket && sk->sk_socket->file) {
208743b2a66SEric Dumazet kuid_t skuid = sk->sk_socket->file->f_cred->fsuid;
209743b2a66SEric Dumazet
210a6c6796cSEric W. Biederman return from_kuid(&init_user_ns, skuid);
211a6c6796cSEric W. Biederman }
212e5dfb815SPatrick McHardy return 0;
213e5dfb815SPatrick McHardy }
214e5dfb815SPatrick McHardy
flow_get_skgid(const struct sk_buff * skb)215e5dfb815SPatrick McHardy static u32 flow_get_skgid(const struct sk_buff *skb)
216e5dfb815SPatrick McHardy {
217743b2a66SEric Dumazet struct sock *sk = skb_to_full_sk(skb);
218743b2a66SEric Dumazet
219743b2a66SEric Dumazet if (sk && sk->sk_socket && sk->sk_socket->file) {
220743b2a66SEric Dumazet kgid_t skgid = sk->sk_socket->file->f_cred->fsgid;
221743b2a66SEric Dumazet
222a6c6796cSEric W. Biederman return from_kgid(&init_user_ns, skgid);
223a6c6796cSEric W. Biederman }
224e5dfb815SPatrick McHardy return 0;
225e5dfb815SPatrick McHardy }
226e5dfb815SPatrick McHardy
flow_get_vlan_tag(const struct sk_buff * skb)2279ec13810SPatrick McHardy static u32 flow_get_vlan_tag(const struct sk_buff *skb)
2289ec13810SPatrick McHardy {
2293f649ab7SKees Cook u16 tag;
2309ec13810SPatrick McHardy
2319ec13810SPatrick McHardy if (vlan_get_tag(skb, &tag) < 0)
2329ec13810SPatrick McHardy return 0;
2339ec13810SPatrick McHardy return tag & VLAN_VID_MASK;
2349ec13810SPatrick McHardy }
2359ec13810SPatrick McHardy
flow_get_rxhash(struct sk_buff * skb)236739a91efSChangli Gao static u32 flow_get_rxhash(struct sk_buff *skb)
237739a91efSChangli Gao {
2383958afa1STom Herbert return skb_get_hash(skb);
239739a91efSChangli Gao }
240739a91efSChangli Gao
flow_key_get(struct sk_buff * skb,int key,struct flow_keys * flow)2416bd2a9afSEric Dumazet static u32 flow_key_get(struct sk_buff *skb, int key, struct flow_keys *flow)
242e5dfb815SPatrick McHardy {
243e5dfb815SPatrick McHardy switch (key) {
244e5dfb815SPatrick McHardy case FLOW_KEY_SRC:
2456bd2a9afSEric Dumazet return flow_get_src(skb, flow);
246e5dfb815SPatrick McHardy case FLOW_KEY_DST:
2476bd2a9afSEric Dumazet return flow_get_dst(skb, flow);
248e5dfb815SPatrick McHardy case FLOW_KEY_PROTO:
2496bd2a9afSEric Dumazet return flow_get_proto(skb, flow);
250e5dfb815SPatrick McHardy case FLOW_KEY_PROTO_SRC:
2516bd2a9afSEric Dumazet return flow_get_proto_src(skb, flow);
252e5dfb815SPatrick McHardy case FLOW_KEY_PROTO_DST:
2536bd2a9afSEric Dumazet return flow_get_proto_dst(skb, flow);
254e5dfb815SPatrick McHardy case FLOW_KEY_IIF:
255e5dfb815SPatrick McHardy return flow_get_iif(skb);
256e5dfb815SPatrick McHardy case FLOW_KEY_PRIORITY:
257e5dfb815SPatrick McHardy return flow_get_priority(skb);
258e5dfb815SPatrick McHardy case FLOW_KEY_MARK:
259e5dfb815SPatrick McHardy return flow_get_mark(skb);
260e5dfb815SPatrick McHardy case FLOW_KEY_NFCT:
261e5dfb815SPatrick McHardy return flow_get_nfct(skb);
262e5dfb815SPatrick McHardy case FLOW_KEY_NFCT_SRC:
2636bd2a9afSEric Dumazet return flow_get_nfct_src(skb, flow);
264e5dfb815SPatrick McHardy case FLOW_KEY_NFCT_DST:
2656bd2a9afSEric Dumazet return flow_get_nfct_dst(skb, flow);
266e5dfb815SPatrick McHardy case FLOW_KEY_NFCT_PROTO_SRC:
2676bd2a9afSEric Dumazet return flow_get_nfct_proto_src(skb, flow);
268e5dfb815SPatrick McHardy case FLOW_KEY_NFCT_PROTO_DST:
2696bd2a9afSEric Dumazet return flow_get_nfct_proto_dst(skb, flow);
270e5dfb815SPatrick McHardy case FLOW_KEY_RTCLASSID:
271e5dfb815SPatrick McHardy return flow_get_rtclassid(skb);
272e5dfb815SPatrick McHardy case FLOW_KEY_SKUID:
273e5dfb815SPatrick McHardy return flow_get_skuid(skb);
274e5dfb815SPatrick McHardy case FLOW_KEY_SKGID:
275e5dfb815SPatrick McHardy return flow_get_skgid(skb);
2769ec13810SPatrick McHardy case FLOW_KEY_VLAN_TAG:
2779ec13810SPatrick McHardy return flow_get_vlan_tag(skb);
278739a91efSChangli Gao case FLOW_KEY_RXHASH:
279739a91efSChangli Gao return flow_get_rxhash(skb);
280e5dfb815SPatrick McHardy default:
281e5dfb815SPatrick McHardy WARN_ON(1);
282e5dfb815SPatrick McHardy return 0;
283e5dfb815SPatrick McHardy }
284e5dfb815SPatrick McHardy }
285e5dfb815SPatrick McHardy
2866bd2a9afSEric Dumazet #define FLOW_KEYS_NEEDED ((1 << FLOW_KEY_SRC) | \
2876bd2a9afSEric Dumazet (1 << FLOW_KEY_DST) | \
2886bd2a9afSEric Dumazet (1 << FLOW_KEY_PROTO) | \
2896bd2a9afSEric Dumazet (1 << FLOW_KEY_PROTO_SRC) | \
2906bd2a9afSEric Dumazet (1 << FLOW_KEY_PROTO_DST) | \
2916bd2a9afSEric Dumazet (1 << FLOW_KEY_NFCT_SRC) | \
2926bd2a9afSEric Dumazet (1 << FLOW_KEY_NFCT_DST) | \
2936bd2a9afSEric Dumazet (1 << FLOW_KEY_NFCT_PROTO_SRC) | \
2946bd2a9afSEric Dumazet (1 << FLOW_KEY_NFCT_PROTO_DST))
2956bd2a9afSEric Dumazet
flow_classify(struct sk_buff * skb,const struct tcf_proto * tp,struct tcf_result * res)2969f3101dcSPedro Tammela TC_INDIRECT_SCOPE int flow_classify(struct sk_buff *skb,
2979f3101dcSPedro Tammela const struct tcf_proto *tp,
298e5dfb815SPatrick McHardy struct tcf_result *res)
299e5dfb815SPatrick McHardy {
30070da9f0bSJohn Fastabend struct flow_head *head = rcu_dereference_bh(tp->root);
301e5dfb815SPatrick McHardy struct flow_filter *f;
302e5dfb815SPatrick McHardy u32 keymask;
303e5dfb815SPatrick McHardy u32 classid;
304e5dfb815SPatrick McHardy unsigned int n, key;
305e5dfb815SPatrick McHardy int r;
306e5dfb815SPatrick McHardy
30770da9f0bSJohn Fastabend list_for_each_entry_rcu(f, &head->filters, list) {
3083a53943bSEric Dumazet u32 keys[FLOW_KEY_MAX + 1];
3096bd2a9afSEric Dumazet struct flow_keys flow_keys;
310e5dfb815SPatrick McHardy
311e5dfb815SPatrick McHardy if (!tcf_em_tree_match(skb, &f->ematches, NULL))
312e5dfb815SPatrick McHardy continue;
313e5dfb815SPatrick McHardy
314e5dfb815SPatrick McHardy keymask = f->keymask;
3156bd2a9afSEric Dumazet if (keymask & FLOW_KEYS_NEEDED)
316cd79a238STom Herbert skb_flow_dissect_flow_keys(skb, &flow_keys, 0);
317e5dfb815SPatrick McHardy
318e5dfb815SPatrick McHardy for (n = 0; n < f->nkeys; n++) {
319e5dfb815SPatrick McHardy key = ffs(keymask) - 1;
320e5dfb815SPatrick McHardy keymask &= ~(1 << key);
3216bd2a9afSEric Dumazet keys[n] = flow_key_get(skb, key, &flow_keys);
322e5dfb815SPatrick McHardy }
323e5dfb815SPatrick McHardy
324e5dfb815SPatrick McHardy if (f->mode == FLOW_MODE_HASH)
32572d9794fSPatrick McHardy classid = jhash2(keys, f->nkeys, f->hashrnd);
326e5dfb815SPatrick McHardy else {
327e5dfb815SPatrick McHardy classid = keys[0];
328e5dfb815SPatrick McHardy classid = (classid & f->mask) ^ f->xor;
329e5dfb815SPatrick McHardy classid = (classid >> f->rshift) + f->addend;
330e5dfb815SPatrick McHardy }
331e5dfb815SPatrick McHardy
332e5dfb815SPatrick McHardy if (f->divisor)
333e5dfb815SPatrick McHardy classid %= f->divisor;
334e5dfb815SPatrick McHardy
335e5dfb815SPatrick McHardy res->class = 0;
336e5dfb815SPatrick McHardy res->classid = TC_H_MAKE(f->baseclass, f->baseclass + classid);
337e5dfb815SPatrick McHardy
338e5dfb815SPatrick McHardy r = tcf_exts_exec(skb, &f->exts, res);
339e5dfb815SPatrick McHardy if (r < 0)
340e5dfb815SPatrick McHardy continue;
341e5dfb815SPatrick McHardy return r;
342e5dfb815SPatrick McHardy }
343e5dfb815SPatrick McHardy return -1;
344e5dfb815SPatrick McHardy }
345e5dfb815SPatrick McHardy
flow_perturbation(struct timer_list * t)346cdeabbb8SKees Cook static void flow_perturbation(struct timer_list *t)
34772d9794fSPatrick McHardy {
348cdeabbb8SKees Cook struct flow_filter *f = from_timer(f, t, perturb_timer);
34972d9794fSPatrick McHardy
35072d9794fSPatrick McHardy get_random_bytes(&f->hashrnd, 4);
35172d9794fSPatrick McHardy if (f->perturb_period)
35272d9794fSPatrick McHardy mod_timer(&f->perturb_timer, jiffies + f->perturb_period);
35372d9794fSPatrick McHardy }
35472d9794fSPatrick McHardy
355e5dfb815SPatrick McHardy static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = {
356e5dfb815SPatrick McHardy [TCA_FLOW_KEYS] = { .type = NLA_U32 },
357e5dfb815SPatrick McHardy [TCA_FLOW_MODE] = { .type = NLA_U32 },
358e5dfb815SPatrick McHardy [TCA_FLOW_BASECLASS] = { .type = NLA_U32 },
359*e54beb9aSEric Dumazet [TCA_FLOW_RSHIFT] = NLA_POLICY_MAX(NLA_U32,
360*e54beb9aSEric Dumazet 31 /* BITS_PER_U32 - 1 */),
361e5dfb815SPatrick McHardy [TCA_FLOW_ADDEND] = { .type = NLA_U32 },
362e5dfb815SPatrick McHardy [TCA_FLOW_MASK] = { .type = NLA_U32 },
363e5dfb815SPatrick McHardy [TCA_FLOW_XOR] = { .type = NLA_U32 },
364e5dfb815SPatrick McHardy [TCA_FLOW_DIVISOR] = { .type = NLA_U32 },
365e5dfb815SPatrick McHardy [TCA_FLOW_ACT] = { .type = NLA_NESTED },
366e5dfb815SPatrick McHardy [TCA_FLOW_POLICE] = { .type = NLA_NESTED },
367e5dfb815SPatrick McHardy [TCA_FLOW_EMATCHES] = { .type = NLA_NESTED },
36872d9794fSPatrick McHardy [TCA_FLOW_PERTURB] = { .type = NLA_U32 },
369e5dfb815SPatrick McHardy };
370e5dfb815SPatrick McHardy
__flow_destroy_filter(struct flow_filter * f)37122f7cec9SCong Wang static void __flow_destroy_filter(struct flow_filter *f)
37222f7cec9SCong Wang {
373292a089dSSteven Rostedt (Google) timer_shutdown_sync(&f->perturb_timer);
37422f7cec9SCong Wang tcf_exts_destroy(&f->exts);
37522f7cec9SCong Wang tcf_em_tree_destroy(&f->ematches);
37622f7cec9SCong Wang tcf_exts_put_net(&f->exts);
37722f7cec9SCong Wang kfree(f);
37822f7cec9SCong Wang }
37922f7cec9SCong Wang
flow_destroy_filter_work(struct work_struct * work)38094cdb475SCong Wang static void flow_destroy_filter_work(struct work_struct *work)
38170da9f0bSJohn Fastabend {
382aaa908ffSCong Wang struct flow_filter *f = container_of(to_rcu_work(work),
383aaa908ffSCong Wang struct flow_filter,
384aaa908ffSCong Wang rwork);
38594cdb475SCong Wang rtnl_lock();
38622f7cec9SCong Wang __flow_destroy_filter(f);
38794cdb475SCong Wang rtnl_unlock();
38894cdb475SCong Wang }
38994cdb475SCong Wang
flow_change(struct net * net,struct sk_buff * in_skb,struct tcf_proto * tp,unsigned long base,u32 handle,struct nlattr ** tca,void ** arg,u32 flags,struct netlink_ext_ack * extack)390c1b52739SBenjamin LaHaise static int flow_change(struct net *net, struct sk_buff *in_skb,
391af4c6641SEric W. Biederman struct tcf_proto *tp, unsigned long base,
392e5dfb815SPatrick McHardy u32 handle, struct nlattr **tca,
393695176bfSCong Wang void **arg, u32 flags,
39412db03b6SVlad Buslov struct netlink_ext_ack *extack)
395e5dfb815SPatrick McHardy {
39670da9f0bSJohn Fastabend struct flow_head *head = rtnl_dereference(tp->root);
39770da9f0bSJohn Fastabend struct flow_filter *fold, *fnew;
398e5dfb815SPatrick McHardy struct nlattr *opt = tca[TCA_OPTIONS];
399e5dfb815SPatrick McHardy struct nlattr *tb[TCA_FLOW_MAX + 1];
400e5dfb815SPatrick McHardy unsigned int nkeys = 0;
40172d9794fSPatrick McHardy unsigned int perturb_period = 0;
402e5dfb815SPatrick McHardy u32 baseclass = 0;
403e5dfb815SPatrick McHardy u32 keymask = 0;
404e5dfb815SPatrick McHardy u32 mode;
405e5dfb815SPatrick McHardy int err;
406e5dfb815SPatrick McHardy
407e5dfb815SPatrick McHardy if (opt == NULL)
408e5dfb815SPatrick McHardy return -EINVAL;
409e5dfb815SPatrick McHardy
4108cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_FLOW_MAX, opt, flow_policy,
4118cb08174SJohannes Berg NULL);
412e5dfb815SPatrick McHardy if (err < 0)
413e5dfb815SPatrick McHardy return err;
414e5dfb815SPatrick McHardy
415e5dfb815SPatrick McHardy if (tb[TCA_FLOW_BASECLASS]) {
416e5dfb815SPatrick McHardy baseclass = nla_get_u32(tb[TCA_FLOW_BASECLASS]);
417e5dfb815SPatrick McHardy if (TC_H_MIN(baseclass) == 0)
418e5dfb815SPatrick McHardy return -EINVAL;
419e5dfb815SPatrick McHardy }
420e5dfb815SPatrick McHardy
421e5dfb815SPatrick McHardy if (tb[TCA_FLOW_KEYS]) {
422e5dfb815SPatrick McHardy keymask = nla_get_u32(tb[TCA_FLOW_KEYS]);
423e5dfb815SPatrick McHardy
424e5dfb815SPatrick McHardy nkeys = hweight32(keymask);
425e5dfb815SPatrick McHardy if (nkeys == 0)
426e5dfb815SPatrick McHardy return -EINVAL;
4274f250491SPatrick McHardy
4284f250491SPatrick McHardy if (fls(keymask) - 1 > FLOW_KEY_MAX)
4294f250491SPatrick McHardy return -EOPNOTSUPP;
430a6c6796cSEric W. Biederman
431a6c6796cSEric W. Biederman if ((keymask & (FLOW_KEY_SKUID|FLOW_KEY_SKGID)) &&
432e32123e5SPatrick McHardy sk_user_ns(NETLINK_CB(in_skb).sk) != &init_user_ns)
433a6c6796cSEric W. Biederman return -EOPNOTSUPP;
434e5dfb815SPatrick McHardy }
435e5dfb815SPatrick McHardy
43670da9f0bSJohn Fastabend fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
43770da9f0bSJohn Fastabend if (!fnew)
438c09fc2e1SJiri Pirko return -ENOBUFS;
4394ebc1e3cSJiri Pirko
4404ebc1e3cSJiri Pirko err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &fnew->ematches);
4414ebc1e3cSJiri Pirko if (err < 0)
442c09fc2e1SJiri Pirko goto err1;
443e5dfb815SPatrick McHardy
44414215108SCong Wang err = tcf_exts_init(&fnew->exts, net, TCA_FLOW_ACT, TCA_FLOW_POLICE);
445b9a24bb7SWANG Cong if (err < 0)
446c09fc2e1SJiri Pirko goto err2;
447c09fc2e1SJiri Pirko
448695176bfSCong Wang err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &fnew->exts, flags,
449695176bfSCong Wang extack);
450c09fc2e1SJiri Pirko if (err < 0)
451c09fc2e1SJiri Pirko goto err2;
45232b2f4b1SDaniel Borkmann
4538113c095SWANG Cong fold = *arg;
45470da9f0bSJohn Fastabend if (fold) {
45570da9f0bSJohn Fastabend err = -EINVAL;
45670da9f0bSJohn Fastabend if (fold->handle != handle && handle)
457c09fc2e1SJiri Pirko goto err2;
45870da9f0bSJohn Fastabend
45970da9f0bSJohn Fastabend /* Copy fold into fnew */
46070da9f0bSJohn Fastabend fnew->tp = fold->tp;
46170da9f0bSJohn Fastabend fnew->handle = fold->handle;
46270da9f0bSJohn Fastabend fnew->nkeys = fold->nkeys;
46370da9f0bSJohn Fastabend fnew->keymask = fold->keymask;
46470da9f0bSJohn Fastabend fnew->mode = fold->mode;
46570da9f0bSJohn Fastabend fnew->mask = fold->mask;
46670da9f0bSJohn Fastabend fnew->xor = fold->xor;
46770da9f0bSJohn Fastabend fnew->rshift = fold->rshift;
46870da9f0bSJohn Fastabend fnew->addend = fold->addend;
46970da9f0bSJohn Fastabend fnew->divisor = fold->divisor;
47070da9f0bSJohn Fastabend fnew->baseclass = fold->baseclass;
47170da9f0bSJohn Fastabend fnew->hashrnd = fold->hashrnd;
47270da9f0bSJohn Fastabend
47370da9f0bSJohn Fastabend mode = fold->mode;
474e5dfb815SPatrick McHardy if (tb[TCA_FLOW_MODE])
475e5dfb815SPatrick McHardy mode = nla_get_u32(tb[TCA_FLOW_MODE]);
476e5dfb815SPatrick McHardy if (mode != FLOW_MODE_HASH && nkeys > 1)
477c09fc2e1SJiri Pirko goto err2;
47872d9794fSPatrick McHardy
47972d9794fSPatrick McHardy if (mode == FLOW_MODE_HASH)
48070da9f0bSJohn Fastabend perturb_period = fold->perturb_period;
48172d9794fSPatrick McHardy if (tb[TCA_FLOW_PERTURB]) {
48272d9794fSPatrick McHardy if (mode != FLOW_MODE_HASH)
483c09fc2e1SJiri Pirko goto err2;
48472d9794fSPatrick McHardy perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
48572d9794fSPatrick McHardy }
486e5dfb815SPatrick McHardy } else {
487e5dfb815SPatrick McHardy err = -EINVAL;
488e5dfb815SPatrick McHardy if (!handle)
489c09fc2e1SJiri Pirko goto err2;
490e5dfb815SPatrick McHardy if (!tb[TCA_FLOW_KEYS])
491c09fc2e1SJiri Pirko goto err2;
492e5dfb815SPatrick McHardy
493e5dfb815SPatrick McHardy mode = FLOW_MODE_MAP;
494e5dfb815SPatrick McHardy if (tb[TCA_FLOW_MODE])
495e5dfb815SPatrick McHardy mode = nla_get_u32(tb[TCA_FLOW_MODE]);
496e5dfb815SPatrick McHardy if (mode != FLOW_MODE_HASH && nkeys > 1)
497c09fc2e1SJiri Pirko goto err2;
498e5dfb815SPatrick McHardy
49972d9794fSPatrick McHardy if (tb[TCA_FLOW_PERTURB]) {
50072d9794fSPatrick McHardy if (mode != FLOW_MODE_HASH)
501c09fc2e1SJiri Pirko goto err2;
50272d9794fSPatrick McHardy perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
50372d9794fSPatrick McHardy }
50472d9794fSPatrick McHardy
5051abf2720SJiri Pirko if (TC_H_MAJ(baseclass) == 0) {
5061abf2720SJiri Pirko struct Qdisc *q = tcf_block_q(tp->chain->block);
5071abf2720SJiri Pirko
5081abf2720SJiri Pirko baseclass = TC_H_MAKE(q->handle, baseclass);
5091abf2720SJiri Pirko }
510e5dfb815SPatrick McHardy if (TC_H_MIN(baseclass) == 0)
511e5dfb815SPatrick McHardy baseclass = TC_H_MAKE(baseclass, 1);
512e5dfb815SPatrick McHardy
51370da9f0bSJohn Fastabend fnew->handle = handle;
51470da9f0bSJohn Fastabend fnew->mask = ~0U;
51570da9f0bSJohn Fastabend fnew->tp = tp;
51670da9f0bSJohn Fastabend get_random_bytes(&fnew->hashrnd, 4);
517e5dfb815SPatrick McHardy }
518e5dfb815SPatrick McHardy
519cdeabbb8SKees Cook timer_setup(&fnew->perturb_timer, flow_perturbation, TIMER_DEFERRABLE);
520e5dfb815SPatrick McHardy
521f36fe1c4SJiri Pirko tcf_block_netif_keep_dst(tp->chain->block);
52202875878SEric Dumazet
523e5dfb815SPatrick McHardy if (tb[TCA_FLOW_KEYS]) {
52470da9f0bSJohn Fastabend fnew->keymask = keymask;
52570da9f0bSJohn Fastabend fnew->nkeys = nkeys;
526e5dfb815SPatrick McHardy }
527e5dfb815SPatrick McHardy
52870da9f0bSJohn Fastabend fnew->mode = mode;
529e5dfb815SPatrick McHardy
530e5dfb815SPatrick McHardy if (tb[TCA_FLOW_MASK])
53170da9f0bSJohn Fastabend fnew->mask = nla_get_u32(tb[TCA_FLOW_MASK]);
532e5dfb815SPatrick McHardy if (tb[TCA_FLOW_XOR])
53370da9f0bSJohn Fastabend fnew->xor = nla_get_u32(tb[TCA_FLOW_XOR]);
534e5dfb815SPatrick McHardy if (tb[TCA_FLOW_RSHIFT])
53570da9f0bSJohn Fastabend fnew->rshift = nla_get_u32(tb[TCA_FLOW_RSHIFT]);
536e5dfb815SPatrick McHardy if (tb[TCA_FLOW_ADDEND])
53770da9f0bSJohn Fastabend fnew->addend = nla_get_u32(tb[TCA_FLOW_ADDEND]);
538e5dfb815SPatrick McHardy
539e5dfb815SPatrick McHardy if (tb[TCA_FLOW_DIVISOR])
54070da9f0bSJohn Fastabend fnew->divisor = nla_get_u32(tb[TCA_FLOW_DIVISOR]);
541e5dfb815SPatrick McHardy if (baseclass)
54270da9f0bSJohn Fastabend fnew->baseclass = baseclass;
543e5dfb815SPatrick McHardy
54470da9f0bSJohn Fastabend fnew->perturb_period = perturb_period;
54572d9794fSPatrick McHardy if (perturb_period)
54670da9f0bSJohn Fastabend mod_timer(&fnew->perturb_timer, jiffies + perturb_period);
54772d9794fSPatrick McHardy
5488113c095SWANG Cong if (!*arg)
54970da9f0bSJohn Fastabend list_add_tail_rcu(&fnew->list, &head->filters);
55070da9f0bSJohn Fastabend else
55132b2f4b1SDaniel Borkmann list_replace_rcu(&fold->list, &fnew->list);
552e5dfb815SPatrick McHardy
5538113c095SWANG Cong *arg = fnew;
554e5dfb815SPatrick McHardy
55522f7cec9SCong Wang if (fold) {
55622f7cec9SCong Wang tcf_exts_get_net(&fold->exts);
557aaa908ffSCong Wang tcf_queue_work(&fold->rwork, flow_destroy_filter_work);
55822f7cec9SCong Wang }
559e5dfb815SPatrick McHardy return 0;
560e5dfb815SPatrick McHardy
561c09fc2e1SJiri Pirko err2:
562b9a24bb7SWANG Cong tcf_exts_destroy(&fnew->exts);
5634ebc1e3cSJiri Pirko tcf_em_tree_destroy(&fnew->ematches);
564e5dfb815SPatrick McHardy err1:
565c09fc2e1SJiri Pirko kfree(fnew);
566e5dfb815SPatrick McHardy return err;
567e5dfb815SPatrick McHardy }
568e5dfb815SPatrick McHardy
flow_delete(struct tcf_proto * tp,void * arg,bool * last,bool rtnl_held,struct netlink_ext_ack * extack)569571acf21SAlexander Aring static int flow_delete(struct tcf_proto *tp, void *arg, bool *last,
57012db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack)
571e5dfb815SPatrick McHardy {
572763dbf63SWANG Cong struct flow_head *head = rtnl_dereference(tp->root);
5738113c095SWANG Cong struct flow_filter *f = arg;
574e5dfb815SPatrick McHardy
57570da9f0bSJohn Fastabend list_del_rcu(&f->list);
57622f7cec9SCong Wang tcf_exts_get_net(&f->exts);
577aaa908ffSCong Wang tcf_queue_work(&f->rwork, flow_destroy_filter_work);
578763dbf63SWANG Cong *last = list_empty(&head->filters);
579e5dfb815SPatrick McHardy return 0;
580e5dfb815SPatrick McHardy }
581e5dfb815SPatrick McHardy
flow_init(struct tcf_proto * tp)582e5dfb815SPatrick McHardy static int flow_init(struct tcf_proto *tp)
583e5dfb815SPatrick McHardy {
584e5dfb815SPatrick McHardy struct flow_head *head;
585e5dfb815SPatrick McHardy
586e5dfb815SPatrick McHardy head = kzalloc(sizeof(*head), GFP_KERNEL);
587e5dfb815SPatrick McHardy if (head == NULL)
588e5dfb815SPatrick McHardy return -ENOBUFS;
589e5dfb815SPatrick McHardy INIT_LIST_HEAD(&head->filters);
59070da9f0bSJohn Fastabend rcu_assign_pointer(tp->root, head);
591e5dfb815SPatrick McHardy return 0;
592e5dfb815SPatrick McHardy }
593e5dfb815SPatrick McHardy
flow_destroy(struct tcf_proto * tp,bool rtnl_held,struct netlink_ext_ack * extack)59412db03b6SVlad Buslov static void flow_destroy(struct tcf_proto *tp, bool rtnl_held,
59512db03b6SVlad Buslov struct netlink_ext_ack *extack)
596e5dfb815SPatrick McHardy {
59770da9f0bSJohn Fastabend struct flow_head *head = rtnl_dereference(tp->root);
598e5dfb815SPatrick McHardy struct flow_filter *f, *next;
599e5dfb815SPatrick McHardy
600e5dfb815SPatrick McHardy list_for_each_entry_safe(f, next, &head->filters, list) {
60170da9f0bSJohn Fastabend list_del_rcu(&f->list);
60222f7cec9SCong Wang if (tcf_exts_get_net(&f->exts))
603aaa908ffSCong Wang tcf_queue_work(&f->rwork, flow_destroy_filter_work);
60422f7cec9SCong Wang else
60522f7cec9SCong Wang __flow_destroy_filter(f);
606e5dfb815SPatrick McHardy }
60770da9f0bSJohn Fastabend kfree_rcu(head, rcu);
608e5dfb815SPatrick McHardy }
609e5dfb815SPatrick McHardy
flow_get(struct tcf_proto * tp,u32 handle)6108113c095SWANG Cong static void *flow_get(struct tcf_proto *tp, u32 handle)
611e5dfb815SPatrick McHardy {
61270da9f0bSJohn Fastabend struct flow_head *head = rtnl_dereference(tp->root);
613e5dfb815SPatrick McHardy struct flow_filter *f;
614e5dfb815SPatrick McHardy
6156a659cd0SJiri Pirko list_for_each_entry(f, &head->filters, list)
616e5dfb815SPatrick McHardy if (f->handle == handle)
6178113c095SWANG Cong return f;
6188113c095SWANG Cong return NULL;
619e5dfb815SPatrick McHardy }
620e5dfb815SPatrick McHardy
flow_dump(struct net * net,struct tcf_proto * tp,void * fh,struct sk_buff * skb,struct tcmsg * t,bool rtnl_held)6218113c095SWANG Cong static int flow_dump(struct net *net, struct tcf_proto *tp, void *fh,
62212db03b6SVlad Buslov struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
623e5dfb815SPatrick McHardy {
6248113c095SWANG Cong struct flow_filter *f = fh;
625e5dfb815SPatrick McHardy struct nlattr *nest;
626e5dfb815SPatrick McHardy
627e5dfb815SPatrick McHardy if (f == NULL)
628e5dfb815SPatrick McHardy return skb->len;
629e5dfb815SPatrick McHardy
630e5dfb815SPatrick McHardy t->tcm_handle = f->handle;
631e5dfb815SPatrick McHardy
632ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
633e5dfb815SPatrick McHardy if (nest == NULL)
634e5dfb815SPatrick McHardy goto nla_put_failure;
635e5dfb815SPatrick McHardy
6361b34ec43SDavid S. Miller if (nla_put_u32(skb, TCA_FLOW_KEYS, f->keymask) ||
6371b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_MODE, f->mode))
6381b34ec43SDavid S. Miller goto nla_put_failure;
639e5dfb815SPatrick McHardy
640e5dfb815SPatrick McHardy if (f->mask != ~0 || f->xor != 0) {
6411b34ec43SDavid S. Miller if (nla_put_u32(skb, TCA_FLOW_MASK, f->mask) ||
6421b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_XOR, f->xor))
6431b34ec43SDavid S. Miller goto nla_put_failure;
644e5dfb815SPatrick McHardy }
6451b34ec43SDavid S. Miller if (f->rshift &&
6461b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_RSHIFT, f->rshift))
6471b34ec43SDavid S. Miller goto nla_put_failure;
6481b34ec43SDavid S. Miller if (f->addend &&
6491b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_ADDEND, f->addend))
6501b34ec43SDavid S. Miller goto nla_put_failure;
651e5dfb815SPatrick McHardy
6521b34ec43SDavid S. Miller if (f->divisor &&
6531b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_DIVISOR, f->divisor))
6541b34ec43SDavid S. Miller goto nla_put_failure;
6551b34ec43SDavid S. Miller if (f->baseclass &&
6561b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_BASECLASS, f->baseclass))
6571b34ec43SDavid S. Miller goto nla_put_failure;
658e5dfb815SPatrick McHardy
6591b34ec43SDavid S. Miller if (f->perturb_period &&
6601b34ec43SDavid S. Miller nla_put_u32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ))
6611b34ec43SDavid S. Miller goto nla_put_failure;
66272d9794fSPatrick McHardy
6635da57f42SWANG Cong if (tcf_exts_dump(skb, &f->exts) < 0)
664e5dfb815SPatrick McHardy goto nla_put_failure;
6650aead543SRami Rosen #ifdef CONFIG_NET_EMATCH
666e5dfb815SPatrick McHardy if (f->ematches.hdr.nmatches &&
667e5dfb815SPatrick McHardy tcf_em_tree_dump(skb, &f->ematches, TCA_FLOW_EMATCHES) < 0)
668e5dfb815SPatrick McHardy goto nla_put_failure;
6690aead543SRami Rosen #endif
670e5dfb815SPatrick McHardy nla_nest_end(skb, nest);
671e5dfb815SPatrick McHardy
6725da57f42SWANG Cong if (tcf_exts_dump_stats(skb, &f->exts) < 0)
673e5dfb815SPatrick McHardy goto nla_put_failure;
674e5dfb815SPatrick McHardy
675e5dfb815SPatrick McHardy return skb->len;
676e5dfb815SPatrick McHardy
677e5dfb815SPatrick McHardy nla_put_failure:
6786ea3b446SJiri Pirko nla_nest_cancel(skb, nest);
679e5dfb815SPatrick McHardy return -1;
680e5dfb815SPatrick McHardy }
681e5dfb815SPatrick McHardy
flow_walk(struct tcf_proto * tp,struct tcf_walker * arg,bool rtnl_held)68212db03b6SVlad Buslov static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg,
68312db03b6SVlad Buslov bool rtnl_held)
684e5dfb815SPatrick McHardy {
68570da9f0bSJohn Fastabend struct flow_head *head = rtnl_dereference(tp->root);
686e5dfb815SPatrick McHardy struct flow_filter *f;
687e5dfb815SPatrick McHardy
6886a659cd0SJiri Pirko list_for_each_entry(f, &head->filters, list) {
6895508ff7cSZhengchao Shao if (!tc_cls_stats_dump(tp, arg, f))
690e5dfb815SPatrick McHardy break;
691e5dfb815SPatrick McHardy }
692e5dfb815SPatrick McHardy }
693e5dfb815SPatrick McHardy
694e5dfb815SPatrick McHardy static struct tcf_proto_ops cls_flow_ops __read_mostly = {
695e5dfb815SPatrick McHardy .kind = "flow",
696e5dfb815SPatrick McHardy .classify = flow_classify,
697e5dfb815SPatrick McHardy .init = flow_init,
698e5dfb815SPatrick McHardy .destroy = flow_destroy,
699e5dfb815SPatrick McHardy .change = flow_change,
700e5dfb815SPatrick McHardy .delete = flow_delete,
701e5dfb815SPatrick McHardy .get = flow_get,
702e5dfb815SPatrick McHardy .dump = flow_dump,
703e5dfb815SPatrick McHardy .walk = flow_walk,
704e5dfb815SPatrick McHardy .owner = THIS_MODULE,
705e5dfb815SPatrick McHardy };
706e5dfb815SPatrick McHardy
cls_flow_init(void)707e5dfb815SPatrick McHardy static int __init cls_flow_init(void)
708e5dfb815SPatrick McHardy {
709e5dfb815SPatrick McHardy return register_tcf_proto_ops(&cls_flow_ops);
710e5dfb815SPatrick McHardy }
711e5dfb815SPatrick McHardy
cls_flow_exit(void)712e5dfb815SPatrick McHardy static void __exit cls_flow_exit(void)
713e5dfb815SPatrick McHardy {
714e5dfb815SPatrick McHardy unregister_tcf_proto_ops(&cls_flow_ops);
715e5dfb815SPatrick McHardy }
716e5dfb815SPatrick McHardy
717e5dfb815SPatrick McHardy module_init(cls_flow_init);
718e5dfb815SPatrick McHardy module_exit(cls_flow_exit);
719e5dfb815SPatrick McHardy
720e5dfb815SPatrick McHardy MODULE_LICENSE("GPL");
721e5dfb815SPatrick McHardy MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
722e5dfb815SPatrick McHardy MODULE_DESCRIPTION("TC flow classifier");
723