xref: /openbmc/linux/net/sched/cls_flow.c (revision d32fd6bb9f2bc8178cdd65ebec1ad670a8bfa241)
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