xref: /linux/net/sched/cls_flower.c (revision 1f15bb4f399ceccd85522b24af31198f53516f9b)
177b9900eSJiri Pirko /*
277b9900eSJiri Pirko  * net/sched/cls_flower.c		Flower classifier
377b9900eSJiri Pirko  *
477b9900eSJiri Pirko  * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us>
577b9900eSJiri Pirko  *
677b9900eSJiri Pirko  * This program is free software; you can redistribute it and/or modify
777b9900eSJiri Pirko  * it under the terms of the GNU General Public License as published by
877b9900eSJiri Pirko  * the Free Software Foundation; either version 2 of the License, or
977b9900eSJiri Pirko  * (at your option) any later version.
1077b9900eSJiri Pirko  */
1177b9900eSJiri Pirko 
1277b9900eSJiri Pirko #include <linux/kernel.h>
1377b9900eSJiri Pirko #include <linux/init.h>
1477b9900eSJiri Pirko #include <linux/module.h>
1577b9900eSJiri Pirko #include <linux/rhashtable.h>
16d9363774SDaniel Borkmann #include <linux/workqueue.h>
1777b9900eSJiri Pirko 
1877b9900eSJiri Pirko #include <linux/if_ether.h>
1977b9900eSJiri Pirko #include <linux/in6.h>
2077b9900eSJiri Pirko #include <linux/ip.h>
21a577d8f7SBenjamin LaHaise #include <linux/mpls.h>
2277b9900eSJiri Pirko 
2377b9900eSJiri Pirko #include <net/sch_generic.h>
2477b9900eSJiri Pirko #include <net/pkt_cls.h>
2577b9900eSJiri Pirko #include <net/ip.h>
2677b9900eSJiri Pirko #include <net/flow_dissector.h>
270a6e7778SPieter Jansen van Vuuren #include <net/geneve.h>
2877b9900eSJiri Pirko 
29bc3103f1SAmir Vadai #include <net/dst.h>
30bc3103f1SAmir Vadai #include <net/dst_metadata.h>
31bc3103f1SAmir Vadai 
3277b9900eSJiri Pirko struct fl_flow_key {
3377b9900eSJiri Pirko 	int	indev_ifindex;
3442aecaa9STom Herbert 	struct flow_dissector_key_control control;
35bc3103f1SAmir Vadai 	struct flow_dissector_key_control enc_control;
3677b9900eSJiri Pirko 	struct flow_dissector_key_basic basic;
3777b9900eSJiri Pirko 	struct flow_dissector_key_eth_addrs eth;
389399ae9aSHadar Hen Zion 	struct flow_dissector_key_vlan vlan;
39d64efd09SJianbo Liu 	struct flow_dissector_key_vlan cvlan;
4077b9900eSJiri Pirko 	union {
41c3f83241STom Herbert 		struct flow_dissector_key_ipv4_addrs ipv4;
4277b9900eSJiri Pirko 		struct flow_dissector_key_ipv6_addrs ipv6;
4377b9900eSJiri Pirko 	};
4477b9900eSJiri Pirko 	struct flow_dissector_key_ports tp;
457b684884SSimon Horman 	struct flow_dissector_key_icmp icmp;
4699d31326SSimon Horman 	struct flow_dissector_key_arp arp;
47bc3103f1SAmir Vadai 	struct flow_dissector_key_keyid enc_key_id;
48bc3103f1SAmir Vadai 	union {
49bc3103f1SAmir Vadai 		struct flow_dissector_key_ipv4_addrs enc_ipv4;
50bc3103f1SAmir Vadai 		struct flow_dissector_key_ipv6_addrs enc_ipv6;
51bc3103f1SAmir Vadai 	};
52f4d997fdSHadar Hen Zion 	struct flow_dissector_key_ports enc_tp;
53a577d8f7SBenjamin LaHaise 	struct flow_dissector_key_mpls mpls;
54fdfc7dd6SJiri Pirko 	struct flow_dissector_key_tcp tcp;
554d80cc0aSOr Gerlitz 	struct flow_dissector_key_ip ip;
560e2c17b6SOr Gerlitz 	struct flow_dissector_key_ip enc_ip;
570a6e7778SPieter Jansen van Vuuren 	struct flow_dissector_key_enc_opts enc_opts;
585c72299fSAmritha Nambiar 	struct flow_dissector_key_ports tp_min;
595c72299fSAmritha Nambiar 	struct flow_dissector_key_ports tp_max;
6077b9900eSJiri Pirko } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
6177b9900eSJiri Pirko 
6277b9900eSJiri Pirko struct fl_flow_mask_range {
6377b9900eSJiri Pirko 	unsigned short int start;
6477b9900eSJiri Pirko 	unsigned short int end;
6577b9900eSJiri Pirko };
6677b9900eSJiri Pirko 
6777b9900eSJiri Pirko struct fl_flow_mask {
6877b9900eSJiri Pirko 	struct fl_flow_key key;
6977b9900eSJiri Pirko 	struct fl_flow_mask_range range;
705c72299fSAmritha Nambiar 	u32 flags;
7105cd271fSPaul Blakey 	struct rhash_head ht_node;
7205cd271fSPaul Blakey 	struct rhashtable ht;
7305cd271fSPaul Blakey 	struct rhashtable_params filter_ht_params;
7405cd271fSPaul Blakey 	struct flow_dissector dissector;
7505cd271fSPaul Blakey 	struct list_head filters;
7644a5cd43SPaolo Abeni 	struct rcu_work rwork;
7705cd271fSPaul Blakey 	struct list_head list;
7877b9900eSJiri Pirko };
7977b9900eSJiri Pirko 
80b95ec7ebSJiri Pirko struct fl_flow_tmplt {
81b95ec7ebSJiri Pirko 	struct fl_flow_key dummy_key;
82b95ec7ebSJiri Pirko 	struct fl_flow_key mask;
83b95ec7ebSJiri Pirko 	struct flow_dissector dissector;
84b95ec7ebSJiri Pirko 	struct tcf_chain *chain;
85b95ec7ebSJiri Pirko };
86b95ec7ebSJiri Pirko 
8777b9900eSJiri Pirko struct cls_fl_head {
8877b9900eSJiri Pirko 	struct rhashtable ht;
8905cd271fSPaul Blakey 	struct list_head masks;
90aaa908ffSCong Wang 	struct rcu_work rwork;
91c15ab236SChris Mi 	struct idr handle_idr;
92d9363774SDaniel Borkmann };
9377b9900eSJiri Pirko 
9477b9900eSJiri Pirko struct cls_fl_filter {
9505cd271fSPaul Blakey 	struct fl_flow_mask *mask;
9677b9900eSJiri Pirko 	struct rhash_head ht_node;
9777b9900eSJiri Pirko 	struct fl_flow_key mkey;
9877b9900eSJiri Pirko 	struct tcf_exts exts;
9977b9900eSJiri Pirko 	struct tcf_result res;
10077b9900eSJiri Pirko 	struct fl_flow_key key;
10177b9900eSJiri Pirko 	struct list_head list;
10277b9900eSJiri Pirko 	u32 handle;
103e69985c6SAmir Vadai 	u32 flags;
10486c55361SVlad Buslov 	u32 in_hw_count;
105aaa908ffSCong Wang 	struct rcu_work rwork;
1067091d8c7SHadar Hen Zion 	struct net_device *hw_dev;
10777b9900eSJiri Pirko };
10877b9900eSJiri Pirko 
10905cd271fSPaul Blakey static const struct rhashtable_params mask_ht_params = {
11005cd271fSPaul Blakey 	.key_offset = offsetof(struct fl_flow_mask, key),
11105cd271fSPaul Blakey 	.key_len = sizeof(struct fl_flow_key),
11205cd271fSPaul Blakey 	.head_offset = offsetof(struct fl_flow_mask, ht_node),
11305cd271fSPaul Blakey 	.automatic_shrinking = true,
11405cd271fSPaul Blakey };
11505cd271fSPaul Blakey 
11677b9900eSJiri Pirko static unsigned short int fl_mask_range(const struct fl_flow_mask *mask)
11777b9900eSJiri Pirko {
11877b9900eSJiri Pirko 	return mask->range.end - mask->range.start;
11977b9900eSJiri Pirko }
12077b9900eSJiri Pirko 
12177b9900eSJiri Pirko static void fl_mask_update_range(struct fl_flow_mask *mask)
12277b9900eSJiri Pirko {
12377b9900eSJiri Pirko 	const u8 *bytes = (const u8 *) &mask->key;
12477b9900eSJiri Pirko 	size_t size = sizeof(mask->key);
12505cd271fSPaul Blakey 	size_t i, first = 0, last;
12677b9900eSJiri Pirko 
12705cd271fSPaul Blakey 	for (i = 0; i < size; i++) {
12877b9900eSJiri Pirko 		if (bytes[i]) {
12977b9900eSJiri Pirko 			first = i;
13005cd271fSPaul Blakey 			break;
13105cd271fSPaul Blakey 		}
13205cd271fSPaul Blakey 	}
13305cd271fSPaul Blakey 	last = first;
13405cd271fSPaul Blakey 	for (i = size - 1; i != first; i--) {
13505cd271fSPaul Blakey 		if (bytes[i]) {
13677b9900eSJiri Pirko 			last = i;
13705cd271fSPaul Blakey 			break;
13877b9900eSJiri Pirko 		}
13977b9900eSJiri Pirko 	}
14077b9900eSJiri Pirko 	mask->range.start = rounddown(first, sizeof(long));
14177b9900eSJiri Pirko 	mask->range.end = roundup(last + 1, sizeof(long));
14277b9900eSJiri Pirko }
14377b9900eSJiri Pirko 
14477b9900eSJiri Pirko static void *fl_key_get_start(struct fl_flow_key *key,
14577b9900eSJiri Pirko 			      const struct fl_flow_mask *mask)
14677b9900eSJiri Pirko {
14777b9900eSJiri Pirko 	return (u8 *) key + mask->range.start;
14877b9900eSJiri Pirko }
14977b9900eSJiri Pirko 
15077b9900eSJiri Pirko static void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key,
15177b9900eSJiri Pirko 			      struct fl_flow_mask *mask)
15277b9900eSJiri Pirko {
15377b9900eSJiri Pirko 	const long *lkey = fl_key_get_start(key, mask);
15477b9900eSJiri Pirko 	const long *lmask = fl_key_get_start(&mask->key, mask);
15577b9900eSJiri Pirko 	long *lmkey = fl_key_get_start(mkey, mask);
15677b9900eSJiri Pirko 	int i;
15777b9900eSJiri Pirko 
15877b9900eSJiri Pirko 	for (i = 0; i < fl_mask_range(mask); i += sizeof(long))
15977b9900eSJiri Pirko 		*lmkey++ = *lkey++ & *lmask++;
16077b9900eSJiri Pirko }
16177b9900eSJiri Pirko 
162b95ec7ebSJiri Pirko static bool fl_mask_fits_tmplt(struct fl_flow_tmplt *tmplt,
163b95ec7ebSJiri Pirko 			       struct fl_flow_mask *mask)
164b95ec7ebSJiri Pirko {
165b95ec7ebSJiri Pirko 	const long *lmask = fl_key_get_start(&mask->key, mask);
166b95ec7ebSJiri Pirko 	const long *ltmplt;
167b95ec7ebSJiri Pirko 	int i;
168b95ec7ebSJiri Pirko 
169b95ec7ebSJiri Pirko 	if (!tmplt)
170b95ec7ebSJiri Pirko 		return true;
171b95ec7ebSJiri Pirko 	ltmplt = fl_key_get_start(&tmplt->mask, mask);
172b95ec7ebSJiri Pirko 	for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) {
173b95ec7ebSJiri Pirko 		if (~*ltmplt++ & *lmask++)
174b95ec7ebSJiri Pirko 			return false;
175b95ec7ebSJiri Pirko 	}
176b95ec7ebSJiri Pirko 	return true;
177b95ec7ebSJiri Pirko }
178b95ec7ebSJiri Pirko 
17977b9900eSJiri Pirko static void fl_clear_masked_range(struct fl_flow_key *key,
18077b9900eSJiri Pirko 				  struct fl_flow_mask *mask)
18177b9900eSJiri Pirko {
18277b9900eSJiri Pirko 	memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask));
18377b9900eSJiri Pirko }
18477b9900eSJiri Pirko 
1855c72299fSAmritha Nambiar static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter,
1865c72299fSAmritha Nambiar 				  struct fl_flow_key *key,
1875c72299fSAmritha Nambiar 				  struct fl_flow_key *mkey)
1885c72299fSAmritha Nambiar {
1895c72299fSAmritha Nambiar 	__be16 min_mask, max_mask, min_val, max_val;
1905c72299fSAmritha Nambiar 
1915c72299fSAmritha Nambiar 	min_mask = htons(filter->mask->key.tp_min.dst);
1925c72299fSAmritha Nambiar 	max_mask = htons(filter->mask->key.tp_max.dst);
1935c72299fSAmritha Nambiar 	min_val = htons(filter->key.tp_min.dst);
1945c72299fSAmritha Nambiar 	max_val = htons(filter->key.tp_max.dst);
1955c72299fSAmritha Nambiar 
1965c72299fSAmritha Nambiar 	if (min_mask && max_mask) {
1975c72299fSAmritha Nambiar 		if (htons(key->tp.dst) < min_val ||
1985c72299fSAmritha Nambiar 		    htons(key->tp.dst) > max_val)
1995c72299fSAmritha Nambiar 			return false;
2005c72299fSAmritha Nambiar 
2015c72299fSAmritha Nambiar 		/* skb does not have min and max values */
2025c72299fSAmritha Nambiar 		mkey->tp_min.dst = filter->mkey.tp_min.dst;
2035c72299fSAmritha Nambiar 		mkey->tp_max.dst = filter->mkey.tp_max.dst;
2045c72299fSAmritha Nambiar 	}
2055c72299fSAmritha Nambiar 	return true;
2065c72299fSAmritha Nambiar }
2075c72299fSAmritha Nambiar 
2085c72299fSAmritha Nambiar static bool fl_range_port_src_cmp(struct cls_fl_filter *filter,
2095c72299fSAmritha Nambiar 				  struct fl_flow_key *key,
2105c72299fSAmritha Nambiar 				  struct fl_flow_key *mkey)
2115c72299fSAmritha Nambiar {
2125c72299fSAmritha Nambiar 	__be16 min_mask, max_mask, min_val, max_val;
2135c72299fSAmritha Nambiar 
2145c72299fSAmritha Nambiar 	min_mask = htons(filter->mask->key.tp_min.src);
2155c72299fSAmritha Nambiar 	max_mask = htons(filter->mask->key.tp_max.src);
2165c72299fSAmritha Nambiar 	min_val = htons(filter->key.tp_min.src);
2175c72299fSAmritha Nambiar 	max_val = htons(filter->key.tp_max.src);
2185c72299fSAmritha Nambiar 
2195c72299fSAmritha Nambiar 	if (min_mask && max_mask) {
2205c72299fSAmritha Nambiar 		if (htons(key->tp.src) < min_val ||
2215c72299fSAmritha Nambiar 		    htons(key->tp.src) > max_val)
2225c72299fSAmritha Nambiar 			return false;
2235c72299fSAmritha Nambiar 
2245c72299fSAmritha Nambiar 		/* skb does not have min and max values */
2255c72299fSAmritha Nambiar 		mkey->tp_min.src = filter->mkey.tp_min.src;
2265c72299fSAmritha Nambiar 		mkey->tp_max.src = filter->mkey.tp_max.src;
2275c72299fSAmritha Nambiar 	}
2285c72299fSAmritha Nambiar 	return true;
2295c72299fSAmritha Nambiar }
2305c72299fSAmritha Nambiar 
2315c72299fSAmritha Nambiar static struct cls_fl_filter *__fl_lookup(struct fl_flow_mask *mask,
232a3308d8fSPaul Blakey 					 struct fl_flow_key *mkey)
233a3308d8fSPaul Blakey {
23405cd271fSPaul Blakey 	return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask),
23505cd271fSPaul Blakey 				      mask->filter_ht_params);
236a3308d8fSPaul Blakey }
237a3308d8fSPaul Blakey 
2385c72299fSAmritha Nambiar static struct cls_fl_filter *fl_lookup_range(struct fl_flow_mask *mask,
2395c72299fSAmritha Nambiar 					     struct fl_flow_key *mkey,
2405c72299fSAmritha Nambiar 					     struct fl_flow_key *key)
2415c72299fSAmritha Nambiar {
2425c72299fSAmritha Nambiar 	struct cls_fl_filter *filter, *f;
2435c72299fSAmritha Nambiar 
2445c72299fSAmritha Nambiar 	list_for_each_entry_rcu(filter, &mask->filters, list) {
2455c72299fSAmritha Nambiar 		if (!fl_range_port_dst_cmp(filter, key, mkey))
2465c72299fSAmritha Nambiar 			continue;
2475c72299fSAmritha Nambiar 
2485c72299fSAmritha Nambiar 		if (!fl_range_port_src_cmp(filter, key, mkey))
2495c72299fSAmritha Nambiar 			continue;
2505c72299fSAmritha Nambiar 
2515c72299fSAmritha Nambiar 		f = __fl_lookup(mask, mkey);
2525c72299fSAmritha Nambiar 		if (f)
2535c72299fSAmritha Nambiar 			return f;
2545c72299fSAmritha Nambiar 	}
2555c72299fSAmritha Nambiar 	return NULL;
2565c72299fSAmritha Nambiar }
2575c72299fSAmritha Nambiar 
2585c72299fSAmritha Nambiar static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask,
2595c72299fSAmritha Nambiar 				       struct fl_flow_key *mkey,
2605c72299fSAmritha Nambiar 				       struct fl_flow_key *key)
2615c72299fSAmritha Nambiar {
2625c72299fSAmritha Nambiar 	if ((mask->flags & TCA_FLOWER_MASK_FLAGS_RANGE))
2635c72299fSAmritha Nambiar 		return fl_lookup_range(mask, mkey, key);
2645c72299fSAmritha Nambiar 
2655c72299fSAmritha Nambiar 	return __fl_lookup(mask, mkey);
2665c72299fSAmritha Nambiar }
2675c72299fSAmritha Nambiar 
26877b9900eSJiri Pirko static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
26977b9900eSJiri Pirko 		       struct tcf_result *res)
27077b9900eSJiri Pirko {
27177b9900eSJiri Pirko 	struct cls_fl_head *head = rcu_dereference_bh(tp->root);
27277b9900eSJiri Pirko 	struct cls_fl_filter *f;
27305cd271fSPaul Blakey 	struct fl_flow_mask *mask;
27477b9900eSJiri Pirko 	struct fl_flow_key skb_key;
27577b9900eSJiri Pirko 	struct fl_flow_key skb_mkey;
27677b9900eSJiri Pirko 
27705cd271fSPaul Blakey 	list_for_each_entry_rcu(mask, &head->masks, list) {
27805cd271fSPaul Blakey 		fl_clear_masked_range(&skb_key, mask);
279bc3103f1SAmir Vadai 
28077b9900eSJiri Pirko 		skb_key.indev_ifindex = skb->skb_iif;
28105cd271fSPaul Blakey 		/* skb_flow_dissect() does not set n_proto in case an unknown
28205cd271fSPaul Blakey 		 * protocol, so do it rather here.
28377b9900eSJiri Pirko 		 */
28477b9900eSJiri Pirko 		skb_key.basic.n_proto = skb->protocol;
28505cd271fSPaul Blakey 		skb_flow_dissect_tunnel_info(skb, &mask->dissector, &skb_key);
28605cd271fSPaul Blakey 		skb_flow_dissect(skb, &mask->dissector, &skb_key, 0);
28777b9900eSJiri Pirko 
28805cd271fSPaul Blakey 		fl_set_masked_key(&skb_mkey, &skb_key, mask);
28977b9900eSJiri Pirko 
2905c72299fSAmritha Nambiar 		f = fl_lookup(mask, &skb_mkey, &skb_key);
291e8eb36cdSAmir Vadai 		if (f && !tc_skip_sw(f->flags)) {
29277b9900eSJiri Pirko 			*res = f->res;
29377b9900eSJiri Pirko 			return tcf_exts_exec(skb, &f->exts, res);
29477b9900eSJiri Pirko 		}
29505cd271fSPaul Blakey 	}
29677b9900eSJiri Pirko 	return -1;
29777b9900eSJiri Pirko }
29877b9900eSJiri Pirko 
29977b9900eSJiri Pirko static int fl_init(struct tcf_proto *tp)
30077b9900eSJiri Pirko {
30177b9900eSJiri Pirko 	struct cls_fl_head *head;
30277b9900eSJiri Pirko 
30377b9900eSJiri Pirko 	head = kzalloc(sizeof(*head), GFP_KERNEL);
30477b9900eSJiri Pirko 	if (!head)
30577b9900eSJiri Pirko 		return -ENOBUFS;
30677b9900eSJiri Pirko 
30705cd271fSPaul Blakey 	INIT_LIST_HEAD_RCU(&head->masks);
30877b9900eSJiri Pirko 	rcu_assign_pointer(tp->root, head);
309c15ab236SChris Mi 	idr_init(&head->handle_idr);
31077b9900eSJiri Pirko 
31105cd271fSPaul Blakey 	return rhashtable_init(&head->ht, &mask_ht_params);
31205cd271fSPaul Blakey }
31305cd271fSPaul Blakey 
31444a5cd43SPaolo Abeni static void fl_mask_free(struct fl_flow_mask *mask)
31544a5cd43SPaolo Abeni {
31644a5cd43SPaolo Abeni 	rhashtable_destroy(&mask->ht);
31744a5cd43SPaolo Abeni 	kfree(mask);
31844a5cd43SPaolo Abeni }
31944a5cd43SPaolo Abeni 
32044a5cd43SPaolo Abeni static void fl_mask_free_work(struct work_struct *work)
32144a5cd43SPaolo Abeni {
32244a5cd43SPaolo Abeni 	struct fl_flow_mask *mask = container_of(to_rcu_work(work),
32344a5cd43SPaolo Abeni 						 struct fl_flow_mask, rwork);
32444a5cd43SPaolo Abeni 
32544a5cd43SPaolo Abeni 	fl_mask_free(mask);
32644a5cd43SPaolo Abeni }
32744a5cd43SPaolo Abeni 
32805cd271fSPaul Blakey static bool fl_mask_put(struct cls_fl_head *head, struct fl_flow_mask *mask,
32905cd271fSPaul Blakey 			bool async)
33005cd271fSPaul Blakey {
33105cd271fSPaul Blakey 	if (!list_empty(&mask->filters))
33205cd271fSPaul Blakey 		return false;
33305cd271fSPaul Blakey 
33405cd271fSPaul Blakey 	rhashtable_remove_fast(&head->ht, &mask->ht_node, mask_ht_params);
33505cd271fSPaul Blakey 	list_del_rcu(&mask->list);
33605cd271fSPaul Blakey 	if (async)
33744a5cd43SPaolo Abeni 		tcf_queue_work(&mask->rwork, fl_mask_free_work);
33805cd271fSPaul Blakey 	else
33944a5cd43SPaolo Abeni 		fl_mask_free(mask);
34005cd271fSPaul Blakey 
34105cd271fSPaul Blakey 	return true;
34277b9900eSJiri Pirko }
34377b9900eSJiri Pirko 
3440dadc117SCong Wang static void __fl_destroy_filter(struct cls_fl_filter *f)
3450dadc117SCong Wang {
3460dadc117SCong Wang 	tcf_exts_destroy(&f->exts);
3470dadc117SCong Wang 	tcf_exts_put_net(&f->exts);
3480dadc117SCong Wang 	kfree(f);
3490dadc117SCong Wang }
3500dadc117SCong Wang 
3510552c8afSCong Wang static void fl_destroy_filter_work(struct work_struct *work)
3520552c8afSCong Wang {
353aaa908ffSCong Wang 	struct cls_fl_filter *f = container_of(to_rcu_work(work),
354aaa908ffSCong Wang 					struct cls_fl_filter, rwork);
3550552c8afSCong Wang 
3560552c8afSCong Wang 	rtnl_lock();
3570dadc117SCong Wang 	__fl_destroy_filter(f);
3580552c8afSCong Wang 	rtnl_unlock();
3590552c8afSCong Wang }
3600552c8afSCong Wang 
3611b0f8037SJakub Kicinski static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f,
3621b0f8037SJakub Kicinski 				 struct netlink_ext_ack *extack)
3635b33f488SAmir Vadai {
364de4784caSJiri Pirko 	struct tc_cls_flower_offload cls_flower = {};
365208c0f4bSJiri Pirko 	struct tcf_block *block = tp->chain->block;
3665b33f488SAmir Vadai 
3671b0f8037SJakub Kicinski 	tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack);
368de4784caSJiri Pirko 	cls_flower.command = TC_CLSFLOWER_DESTROY;
369de4784caSJiri Pirko 	cls_flower.cookie = (unsigned long) f;
3705b33f488SAmir Vadai 
371aeb3fecdSCong Wang 	tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
372caa72601SJiri Pirko 	tcf_block_offload_dec(block, &f->flags);
3735b33f488SAmir Vadai }
3745b33f488SAmir Vadai 
375e8eb36cdSAmir Vadai static int fl_hw_replace_filter(struct tcf_proto *tp,
37641002038SQuentin Monnet 				struct cls_fl_filter *f,
37741002038SQuentin Monnet 				struct netlink_ext_ack *extack)
3785b33f488SAmir Vadai {
379de4784caSJiri Pirko 	struct tc_cls_flower_offload cls_flower = {};
380208c0f4bSJiri Pirko 	struct tcf_block *block = tp->chain->block;
381717503b9SJiri Pirko 	bool skip_sw = tc_skip_sw(f->flags);
382e8eb36cdSAmir Vadai 	int err;
3835b33f488SAmir Vadai 
384e3ab786bSPablo Neira Ayuso 	cls_flower.rule = flow_rule_alloc(tcf_exts_num_actions(&f->exts));
3858f256622SPablo Neira Ayuso 	if (!cls_flower.rule)
3868f256622SPablo Neira Ayuso 		return -ENOMEM;
3878f256622SPablo Neira Ayuso 
388ea205940SJakub Kicinski 	tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack);
389de4784caSJiri Pirko 	cls_flower.command = TC_CLSFLOWER_REPLACE;
390de4784caSJiri Pirko 	cls_flower.cookie = (unsigned long) f;
3918f256622SPablo Neira Ayuso 	cls_flower.rule->match.dissector = &f->mask->dissector;
3928f256622SPablo Neira Ayuso 	cls_flower.rule->match.mask = &f->mask->key;
3938f256622SPablo Neira Ayuso 	cls_flower.rule->match.key = &f->mkey;
394384c181eSAmritha Nambiar 	cls_flower.classid = f->res.classid;
3955b33f488SAmir Vadai 
3963a7b6861SPablo Neira Ayuso 	err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts);
3973a7b6861SPablo Neira Ayuso 	if (err) {
3983a7b6861SPablo Neira Ayuso 		kfree(cls_flower.rule);
399*1f15bb4fSVlad Buslov 		if (skip_sw) {
400*1f15bb4fSVlad Buslov 			NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action");
4013a7b6861SPablo Neira Ayuso 			return err;
4023a7b6861SPablo Neira Ayuso 		}
403*1f15bb4fSVlad Buslov 		return 0;
404*1f15bb4fSVlad Buslov 	}
4053a7b6861SPablo Neira Ayuso 
406aeb3fecdSCong Wang 	err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
4078f256622SPablo Neira Ayuso 	kfree(cls_flower.rule);
4088f256622SPablo Neira Ayuso 
409717503b9SJiri Pirko 	if (err < 0) {
4101b0f8037SJakub Kicinski 		fl_hw_destroy_filter(tp, f, NULL);
411717503b9SJiri Pirko 		return err;
412717503b9SJiri Pirko 	} else if (err > 0) {
41331533cbaSJohn Hurley 		f->in_hw_count = err;
414caa72601SJiri Pirko 		tcf_block_offload_inc(block, &f->flags);
415717503b9SJiri Pirko 	}
416717503b9SJiri Pirko 
417717503b9SJiri Pirko 	if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW))
418717503b9SJiri Pirko 		return -EINVAL;
419717503b9SJiri Pirko 
420e8eb36cdSAmir Vadai 	return 0;
4215b33f488SAmir Vadai }
4225b33f488SAmir Vadai 
42310cbc684SAmir Vadai static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f)
42410cbc684SAmir Vadai {
425de4784caSJiri Pirko 	struct tc_cls_flower_offload cls_flower = {};
426208c0f4bSJiri Pirko 	struct tcf_block *block = tp->chain->block;
42710cbc684SAmir Vadai 
428ea205940SJakub Kicinski 	tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL);
429de4784caSJiri Pirko 	cls_flower.command = TC_CLSFLOWER_STATS;
430de4784caSJiri Pirko 	cls_flower.cookie = (unsigned long) f;
431384c181eSAmritha Nambiar 	cls_flower.classid = f->res.classid;
43210cbc684SAmir Vadai 
433aeb3fecdSCong Wang 	tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
4343b1903efSPablo Neira Ayuso 
4353b1903efSPablo Neira Ayuso 	tcf_exts_stats_update(&f->exts, cls_flower.stats.bytes,
4363b1903efSPablo Neira Ayuso 			      cls_flower.stats.pkts,
4373b1903efSPablo Neira Ayuso 			      cls_flower.stats.lastused);
43810cbc684SAmir Vadai }
43910cbc684SAmir Vadai 
44005cd271fSPaul Blakey static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f,
4411b0f8037SJakub Kicinski 			struct netlink_ext_ack *extack)
44213fa876eSRoi Dayan {
443c15ab236SChris Mi 	struct cls_fl_head *head = rtnl_dereference(tp->root);
44405cd271fSPaul Blakey 	bool async = tcf_exts_get_net(&f->exts);
44505cd271fSPaul Blakey 	bool last;
446c15ab236SChris Mi 
4479c160941SMatthew Wilcox 	idr_remove(&head->handle_idr, f->handle);
44813fa876eSRoi Dayan 	list_del_rcu(&f->list);
44905cd271fSPaul Blakey 	last = fl_mask_put(head, f->mask, async);
45079685219SHadar Hen Zion 	if (!tc_skip_hw(f->flags))
4511b0f8037SJakub Kicinski 		fl_hw_destroy_filter(tp, f, extack);
45213fa876eSRoi Dayan 	tcf_unbind_filter(tp, &f->res);
45305cd271fSPaul Blakey 	if (async)
454aaa908ffSCong Wang 		tcf_queue_work(&f->rwork, fl_destroy_filter_work);
4550dadc117SCong Wang 	else
4560dadc117SCong Wang 		__fl_destroy_filter(f);
45705cd271fSPaul Blakey 
45805cd271fSPaul Blakey 	return last;
45913fa876eSRoi Dayan }
46013fa876eSRoi Dayan 
461d9363774SDaniel Borkmann static void fl_destroy_sleepable(struct work_struct *work)
462d9363774SDaniel Borkmann {
463aaa908ffSCong Wang 	struct cls_fl_head *head = container_of(to_rcu_work(work),
464aaa908ffSCong Wang 						struct cls_fl_head,
465aaa908ffSCong Wang 						rwork);
466de9dc650SPaul Blakey 
467de9dc650SPaul Blakey 	rhashtable_destroy(&head->ht);
468d9363774SDaniel Borkmann 	kfree(head);
469d9363774SDaniel Borkmann 	module_put(THIS_MODULE);
470d9363774SDaniel Borkmann }
471d9363774SDaniel Borkmann 
47212db03b6SVlad Buslov static void fl_destroy(struct tcf_proto *tp, bool rtnl_held,
47312db03b6SVlad Buslov 		       struct netlink_ext_ack *extack)
47477b9900eSJiri Pirko {
47577b9900eSJiri Pirko 	struct cls_fl_head *head = rtnl_dereference(tp->root);
47605cd271fSPaul Blakey 	struct fl_flow_mask *mask, *next_mask;
47777b9900eSJiri Pirko 	struct cls_fl_filter *f, *next;
47877b9900eSJiri Pirko 
47905cd271fSPaul Blakey 	list_for_each_entry_safe(mask, next_mask, &head->masks, list) {
48005cd271fSPaul Blakey 		list_for_each_entry_safe(f, next, &mask->filters, list) {
48105cd271fSPaul Blakey 			if (__fl_delete(tp, f, extack))
48205cd271fSPaul Blakey 				break;
48305cd271fSPaul Blakey 		}
48405cd271fSPaul Blakey 	}
485c15ab236SChris Mi 	idr_destroy(&head->handle_idr);
486d9363774SDaniel Borkmann 
487d9363774SDaniel Borkmann 	__module_get(THIS_MODULE);
488aaa908ffSCong Wang 	tcf_queue_work(&head->rwork, fl_destroy_sleepable);
48977b9900eSJiri Pirko }
49077b9900eSJiri Pirko 
4918113c095SWANG Cong static void *fl_get(struct tcf_proto *tp, u32 handle)
49277b9900eSJiri Pirko {
49377b9900eSJiri Pirko 	struct cls_fl_head *head = rtnl_dereference(tp->root);
49477b9900eSJiri Pirko 
495322d884bSMatthew Wilcox 	return idr_find(&head->handle_idr, handle);
49677b9900eSJiri Pirko }
49777b9900eSJiri Pirko 
49877b9900eSJiri Pirko static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = {
49977b9900eSJiri Pirko 	[TCA_FLOWER_UNSPEC]		= { .type = NLA_UNSPEC },
50077b9900eSJiri Pirko 	[TCA_FLOWER_CLASSID]		= { .type = NLA_U32 },
50177b9900eSJiri Pirko 	[TCA_FLOWER_INDEV]		= { .type = NLA_STRING,
50277b9900eSJiri Pirko 					    .len = IFNAMSIZ },
50377b9900eSJiri Pirko 	[TCA_FLOWER_KEY_ETH_DST]	= { .len = ETH_ALEN },
50477b9900eSJiri Pirko 	[TCA_FLOWER_KEY_ETH_DST_MASK]	= { .len = ETH_ALEN },
50577b9900eSJiri Pirko 	[TCA_FLOWER_KEY_ETH_SRC]	= { .len = ETH_ALEN },
50677b9900eSJiri Pirko 	[TCA_FLOWER_KEY_ETH_SRC_MASK]	= { .len = ETH_ALEN },
50777b9900eSJiri Pirko 	[TCA_FLOWER_KEY_ETH_TYPE]	= { .type = NLA_U16 },
50877b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IP_PROTO]	= { .type = NLA_U8 },
50977b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV4_SRC]	= { .type = NLA_U32 },
51077b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV4_SRC_MASK]	= { .type = NLA_U32 },
51177b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV4_DST]	= { .type = NLA_U32 },
51277b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV4_DST_MASK]	= { .type = NLA_U32 },
51377b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV6_SRC]	= { .len = sizeof(struct in6_addr) },
51477b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV6_SRC_MASK]	= { .len = sizeof(struct in6_addr) },
51577b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV6_DST]	= { .len = sizeof(struct in6_addr) },
51677b9900eSJiri Pirko 	[TCA_FLOWER_KEY_IPV6_DST_MASK]	= { .len = sizeof(struct in6_addr) },
51777b9900eSJiri Pirko 	[TCA_FLOWER_KEY_TCP_SRC]	= { .type = NLA_U16 },
51877b9900eSJiri Pirko 	[TCA_FLOWER_KEY_TCP_DST]	= { .type = NLA_U16 },
519b175c3a4SJamal Hadi Salim 	[TCA_FLOWER_KEY_UDP_SRC]	= { .type = NLA_U16 },
520b175c3a4SJamal Hadi Salim 	[TCA_FLOWER_KEY_UDP_DST]	= { .type = NLA_U16 },
5219399ae9aSHadar Hen Zion 	[TCA_FLOWER_KEY_VLAN_ID]	= { .type = NLA_U16 },
5229399ae9aSHadar Hen Zion 	[TCA_FLOWER_KEY_VLAN_PRIO]	= { .type = NLA_U8 },
5239399ae9aSHadar Hen Zion 	[TCA_FLOWER_KEY_VLAN_ETH_TYPE]	= { .type = NLA_U16 },
524bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_KEY_ID]	= { .type = NLA_U32 },
525bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV4_SRC]	= { .type = NLA_U32 },
526bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 },
527bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV4_DST]	= { .type = NLA_U32 },
528bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 },
529bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV6_SRC]	= { .len = sizeof(struct in6_addr) },
530bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) },
531bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV6_DST]	= { .len = sizeof(struct in6_addr) },
532bc3103f1SAmir Vadai 	[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) },
533aa72d708SOr Gerlitz 	[TCA_FLOWER_KEY_TCP_SRC_MASK]	= { .type = NLA_U16 },
534aa72d708SOr Gerlitz 	[TCA_FLOWER_KEY_TCP_DST_MASK]	= { .type = NLA_U16 },
535aa72d708SOr Gerlitz 	[TCA_FLOWER_KEY_UDP_SRC_MASK]	= { .type = NLA_U16 },
536aa72d708SOr Gerlitz 	[TCA_FLOWER_KEY_UDP_DST_MASK]	= { .type = NLA_U16 },
5375976c5f4SSimon Horman 	[TCA_FLOWER_KEY_SCTP_SRC_MASK]	= { .type = NLA_U16 },
5385976c5f4SSimon Horman 	[TCA_FLOWER_KEY_SCTP_DST_MASK]	= { .type = NLA_U16 },
5395976c5f4SSimon Horman 	[TCA_FLOWER_KEY_SCTP_SRC]	= { .type = NLA_U16 },
5405976c5f4SSimon Horman 	[TCA_FLOWER_KEY_SCTP_DST]	= { .type = NLA_U16 },
541f4d997fdSHadar Hen Zion 	[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT]	= { .type = NLA_U16 },
542f4d997fdSHadar Hen Zion 	[TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK]	= { .type = NLA_U16 },
543f4d997fdSHadar Hen Zion 	[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]	= { .type = NLA_U16 },
544f4d997fdSHadar Hen Zion 	[TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK]	= { .type = NLA_U16 },
545faa3ffceSOr Gerlitz 	[TCA_FLOWER_KEY_FLAGS]		= { .type = NLA_U32 },
546faa3ffceSOr Gerlitz 	[TCA_FLOWER_KEY_FLAGS_MASK]	= { .type = NLA_U32 },
5477b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV4_TYPE]	= { .type = NLA_U8 },
5487b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 },
5497b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV4_CODE]	= { .type = NLA_U8 },
5507b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 },
5517b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV6_TYPE]	= { .type = NLA_U8 },
5527b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 },
5537b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV6_CODE]	= { .type = NLA_U8 },
5547b684884SSimon Horman 	[TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 },
55599d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_SIP]	= { .type = NLA_U32 },
55699d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_SIP_MASK]	= { .type = NLA_U32 },
55799d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_TIP]	= { .type = NLA_U32 },
55899d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_TIP_MASK]	= { .type = NLA_U32 },
55999d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_OP]		= { .type = NLA_U8 },
56099d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_OP_MASK]	= { .type = NLA_U8 },
56199d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_SHA]	= { .len = ETH_ALEN },
56299d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_SHA_MASK]	= { .len = ETH_ALEN },
56399d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_THA]	= { .len = ETH_ALEN },
56499d31326SSimon Horman 	[TCA_FLOWER_KEY_ARP_THA_MASK]	= { .len = ETH_ALEN },
565a577d8f7SBenjamin LaHaise 	[TCA_FLOWER_KEY_MPLS_TTL]	= { .type = NLA_U8 },
566a577d8f7SBenjamin LaHaise 	[TCA_FLOWER_KEY_MPLS_BOS]	= { .type = NLA_U8 },
567a577d8f7SBenjamin LaHaise 	[TCA_FLOWER_KEY_MPLS_TC]	= { .type = NLA_U8 },
568a577d8f7SBenjamin LaHaise 	[TCA_FLOWER_KEY_MPLS_LABEL]	= { .type = NLA_U32 },
569fdfc7dd6SJiri Pirko 	[TCA_FLOWER_KEY_TCP_FLAGS]	= { .type = NLA_U16 },
570fdfc7dd6SJiri Pirko 	[TCA_FLOWER_KEY_TCP_FLAGS_MASK]	= { .type = NLA_U16 },
5714d80cc0aSOr Gerlitz 	[TCA_FLOWER_KEY_IP_TOS]		= { .type = NLA_U8 },
5724d80cc0aSOr Gerlitz 	[TCA_FLOWER_KEY_IP_TOS_MASK]	= { .type = NLA_U8 },
5734d80cc0aSOr Gerlitz 	[TCA_FLOWER_KEY_IP_TTL]		= { .type = NLA_U8 },
5744d80cc0aSOr Gerlitz 	[TCA_FLOWER_KEY_IP_TTL_MASK]	= { .type = NLA_U8 },
575d64efd09SJianbo Liu 	[TCA_FLOWER_KEY_CVLAN_ID]	= { .type = NLA_U16 },
576d64efd09SJianbo Liu 	[TCA_FLOWER_KEY_CVLAN_PRIO]	= { .type = NLA_U8 },
577d64efd09SJianbo Liu 	[TCA_FLOWER_KEY_CVLAN_ETH_TYPE]	= { .type = NLA_U16 },
5780e2c17b6SOr Gerlitz 	[TCA_FLOWER_KEY_ENC_IP_TOS]	= { .type = NLA_U8 },
5790e2c17b6SOr Gerlitz 	[TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NLA_U8 },
5800e2c17b6SOr Gerlitz 	[TCA_FLOWER_KEY_ENC_IP_TTL]	 = { .type = NLA_U8 },
5810e2c17b6SOr Gerlitz 	[TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 },
5820a6e7778SPieter Jansen van Vuuren 	[TCA_FLOWER_KEY_ENC_OPTS]	= { .type = NLA_NESTED },
5830a6e7778SPieter Jansen van Vuuren 	[TCA_FLOWER_KEY_ENC_OPTS_MASK]	= { .type = NLA_NESTED },
5840a6e7778SPieter Jansen van Vuuren };
5850a6e7778SPieter Jansen van Vuuren 
5860a6e7778SPieter Jansen van Vuuren static const struct nla_policy
5870a6e7778SPieter Jansen van Vuuren enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = {
5880a6e7778SPieter Jansen van Vuuren 	[TCA_FLOWER_KEY_ENC_OPTS_GENEVE]        = { .type = NLA_NESTED },
5890a6e7778SPieter Jansen van Vuuren };
5900a6e7778SPieter Jansen van Vuuren 
5910a6e7778SPieter Jansen van Vuuren static const struct nla_policy
5920a6e7778SPieter Jansen van Vuuren geneve_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1] = {
5930a6e7778SPieter Jansen van Vuuren 	[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]      = { .type = NLA_U16 },
5940a6e7778SPieter Jansen van Vuuren 	[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]       = { .type = NLA_U8 },
5950a6e7778SPieter Jansen van Vuuren 	[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]       = { .type = NLA_BINARY,
5960a6e7778SPieter Jansen van Vuuren 						       .len = 128 },
59777b9900eSJiri Pirko };
59877b9900eSJiri Pirko 
59977b9900eSJiri Pirko static void fl_set_key_val(struct nlattr **tb,
60077b9900eSJiri Pirko 			   void *val, int val_type,
60177b9900eSJiri Pirko 			   void *mask, int mask_type, int len)
60277b9900eSJiri Pirko {
60377b9900eSJiri Pirko 	if (!tb[val_type])
60477b9900eSJiri Pirko 		return;
60577b9900eSJiri Pirko 	memcpy(val, nla_data(tb[val_type]), len);
60677b9900eSJiri Pirko 	if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type])
60777b9900eSJiri Pirko 		memset(mask, 0xff, len);
60877b9900eSJiri Pirko 	else
60977b9900eSJiri Pirko 		memcpy(mask, nla_data(tb[mask_type]), len);
61077b9900eSJiri Pirko }
61177b9900eSJiri Pirko 
6125c72299fSAmritha Nambiar static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key,
6135c72299fSAmritha Nambiar 				 struct fl_flow_key *mask)
6145c72299fSAmritha Nambiar {
6155c72299fSAmritha Nambiar 	fl_set_key_val(tb, &key->tp_min.dst,
6165c72299fSAmritha Nambiar 		       TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst,
6175c72299fSAmritha Nambiar 		       TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst));
6185c72299fSAmritha Nambiar 	fl_set_key_val(tb, &key->tp_max.dst,
6195c72299fSAmritha Nambiar 		       TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst,
6205c72299fSAmritha Nambiar 		       TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst));
6215c72299fSAmritha Nambiar 	fl_set_key_val(tb, &key->tp_min.src,
6225c72299fSAmritha Nambiar 		       TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src,
6235c72299fSAmritha Nambiar 		       TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src));
6245c72299fSAmritha Nambiar 	fl_set_key_val(tb, &key->tp_max.src,
6255c72299fSAmritha Nambiar 		       TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src,
6265c72299fSAmritha Nambiar 		       TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src));
6275c72299fSAmritha Nambiar 
6285c72299fSAmritha Nambiar 	if ((mask->tp_min.dst && mask->tp_max.dst &&
6295c72299fSAmritha Nambiar 	     htons(key->tp_max.dst) <= htons(key->tp_min.dst)) ||
6305c72299fSAmritha Nambiar 	     (mask->tp_min.src && mask->tp_max.src &&
6315c72299fSAmritha Nambiar 	      htons(key->tp_max.src) <= htons(key->tp_min.src)))
6325c72299fSAmritha Nambiar 		return -EINVAL;
6335c72299fSAmritha Nambiar 
6345c72299fSAmritha Nambiar 	return 0;
6355c72299fSAmritha Nambiar }
6365c72299fSAmritha Nambiar 
6371a7fca63SBenjamin LaHaise static int fl_set_key_mpls(struct nlattr **tb,
638a577d8f7SBenjamin LaHaise 			   struct flow_dissector_key_mpls *key_val,
639a577d8f7SBenjamin LaHaise 			   struct flow_dissector_key_mpls *key_mask)
640a577d8f7SBenjamin LaHaise {
641a577d8f7SBenjamin LaHaise 	if (tb[TCA_FLOWER_KEY_MPLS_TTL]) {
642a577d8f7SBenjamin LaHaise 		key_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]);
643a577d8f7SBenjamin LaHaise 		key_mask->mpls_ttl = MPLS_TTL_MASK;
644a577d8f7SBenjamin LaHaise 	}
645a577d8f7SBenjamin LaHaise 	if (tb[TCA_FLOWER_KEY_MPLS_BOS]) {
6461a7fca63SBenjamin LaHaise 		u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]);
6471a7fca63SBenjamin LaHaise 
6481a7fca63SBenjamin LaHaise 		if (bos & ~MPLS_BOS_MASK)
6491a7fca63SBenjamin LaHaise 			return -EINVAL;
6501a7fca63SBenjamin LaHaise 		key_val->mpls_bos = bos;
651a577d8f7SBenjamin LaHaise 		key_mask->mpls_bos = MPLS_BOS_MASK;
652a577d8f7SBenjamin LaHaise 	}
653a577d8f7SBenjamin LaHaise 	if (tb[TCA_FLOWER_KEY_MPLS_TC]) {
6541a7fca63SBenjamin LaHaise 		u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]);
6551a7fca63SBenjamin LaHaise 
6561a7fca63SBenjamin LaHaise 		if (tc & ~MPLS_TC_MASK)
6571a7fca63SBenjamin LaHaise 			return -EINVAL;
6581a7fca63SBenjamin LaHaise 		key_val->mpls_tc = tc;
659a577d8f7SBenjamin LaHaise 		key_mask->mpls_tc = MPLS_TC_MASK;
660a577d8f7SBenjamin LaHaise 	}
661a577d8f7SBenjamin LaHaise 	if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) {
6621a7fca63SBenjamin LaHaise 		u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]);
6631a7fca63SBenjamin LaHaise 
6641a7fca63SBenjamin LaHaise 		if (label & ~MPLS_LABEL_MASK)
6651a7fca63SBenjamin LaHaise 			return -EINVAL;
6661a7fca63SBenjamin LaHaise 		key_val->mpls_label = label;
667a577d8f7SBenjamin LaHaise 		key_mask->mpls_label = MPLS_LABEL_MASK;
668a577d8f7SBenjamin LaHaise 	}
6691a7fca63SBenjamin LaHaise 	return 0;
670a577d8f7SBenjamin LaHaise }
671a577d8f7SBenjamin LaHaise 
6729399ae9aSHadar Hen Zion static void fl_set_key_vlan(struct nlattr **tb,
673aaab0834SJianbo Liu 			    __be16 ethertype,
674d64efd09SJianbo Liu 			    int vlan_id_key, int vlan_prio_key,
6759399ae9aSHadar Hen Zion 			    struct flow_dissector_key_vlan *key_val,
6769399ae9aSHadar Hen Zion 			    struct flow_dissector_key_vlan *key_mask)
6779399ae9aSHadar Hen Zion {
6789399ae9aSHadar Hen Zion #define VLAN_PRIORITY_MASK	0x7
6799399ae9aSHadar Hen Zion 
680d64efd09SJianbo Liu 	if (tb[vlan_id_key]) {
6819399ae9aSHadar Hen Zion 		key_val->vlan_id =
682d64efd09SJianbo Liu 			nla_get_u16(tb[vlan_id_key]) & VLAN_VID_MASK;
6839399ae9aSHadar Hen Zion 		key_mask->vlan_id = VLAN_VID_MASK;
6849399ae9aSHadar Hen Zion 	}
685d64efd09SJianbo Liu 	if (tb[vlan_prio_key]) {
6869399ae9aSHadar Hen Zion 		key_val->vlan_priority =
687d64efd09SJianbo Liu 			nla_get_u8(tb[vlan_prio_key]) &
6889399ae9aSHadar Hen Zion 			VLAN_PRIORITY_MASK;
6899399ae9aSHadar Hen Zion 		key_mask->vlan_priority = VLAN_PRIORITY_MASK;
6909399ae9aSHadar Hen Zion 	}
691aaab0834SJianbo Liu 	key_val->vlan_tpid = ethertype;
692aaab0834SJianbo Liu 	key_mask->vlan_tpid = cpu_to_be16(~0);
6939399ae9aSHadar Hen Zion }
6949399ae9aSHadar Hen Zion 
695faa3ffceSOr Gerlitz static void fl_set_key_flag(u32 flower_key, u32 flower_mask,
696faa3ffceSOr Gerlitz 			    u32 *dissector_key, u32 *dissector_mask,
697faa3ffceSOr Gerlitz 			    u32 flower_flag_bit, u32 dissector_flag_bit)
698faa3ffceSOr Gerlitz {
699faa3ffceSOr Gerlitz 	if (flower_mask & flower_flag_bit) {
700faa3ffceSOr Gerlitz 		*dissector_mask |= dissector_flag_bit;
701faa3ffceSOr Gerlitz 		if (flower_key & flower_flag_bit)
702faa3ffceSOr Gerlitz 			*dissector_key |= dissector_flag_bit;
703faa3ffceSOr Gerlitz 	}
704faa3ffceSOr Gerlitz }
705faa3ffceSOr Gerlitz 
706d9724772SOr Gerlitz static int fl_set_key_flags(struct nlattr **tb,
707faa3ffceSOr Gerlitz 			    u32 *flags_key, u32 *flags_mask)
708faa3ffceSOr Gerlitz {
709faa3ffceSOr Gerlitz 	u32 key, mask;
710faa3ffceSOr Gerlitz 
711d9724772SOr Gerlitz 	/* mask is mandatory for flags */
712d9724772SOr Gerlitz 	if (!tb[TCA_FLOWER_KEY_FLAGS_MASK])
713d9724772SOr Gerlitz 		return -EINVAL;
714faa3ffceSOr Gerlitz 
715faa3ffceSOr Gerlitz 	key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS]));
716faa3ffceSOr Gerlitz 	mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK]));
717faa3ffceSOr Gerlitz 
718faa3ffceSOr Gerlitz 	*flags_key  = 0;
719faa3ffceSOr Gerlitz 	*flags_mask = 0;
720faa3ffceSOr Gerlitz 
721faa3ffceSOr Gerlitz 	fl_set_key_flag(key, mask, flags_key, flags_mask,
722faa3ffceSOr Gerlitz 			TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT);
723459d153dSPieter Jansen van Vuuren 	fl_set_key_flag(key, mask, flags_key, flags_mask,
724459d153dSPieter Jansen van Vuuren 			TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST,
725459d153dSPieter Jansen van Vuuren 			FLOW_DIS_FIRST_FRAG);
726d9724772SOr Gerlitz 
727d9724772SOr Gerlitz 	return 0;
728faa3ffceSOr Gerlitz }
729faa3ffceSOr Gerlitz 
7300e2c17b6SOr Gerlitz static void fl_set_key_ip(struct nlattr **tb, bool encap,
7314d80cc0aSOr Gerlitz 			  struct flow_dissector_key_ip *key,
7324d80cc0aSOr Gerlitz 			  struct flow_dissector_key_ip *mask)
7334d80cc0aSOr Gerlitz {
7340e2c17b6SOr Gerlitz 	int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS;
7350e2c17b6SOr Gerlitz 	int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL;
7360e2c17b6SOr Gerlitz 	int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK;
7370e2c17b6SOr Gerlitz 	int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK;
7384d80cc0aSOr Gerlitz 
7390e2c17b6SOr Gerlitz 	fl_set_key_val(tb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos));
7400e2c17b6SOr Gerlitz 	fl_set_key_val(tb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl));
7414d80cc0aSOr Gerlitz }
7424d80cc0aSOr Gerlitz 
7430a6e7778SPieter Jansen van Vuuren static int fl_set_geneve_opt(const struct nlattr *nla, struct fl_flow_key *key,
7440a6e7778SPieter Jansen van Vuuren 			     int depth, int option_len,
7450a6e7778SPieter Jansen van Vuuren 			     struct netlink_ext_ack *extack)
7460a6e7778SPieter Jansen van Vuuren {
7470a6e7778SPieter Jansen van Vuuren 	struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1];
7480a6e7778SPieter Jansen van Vuuren 	struct nlattr *class = NULL, *type = NULL, *data = NULL;
7490a6e7778SPieter Jansen van Vuuren 	struct geneve_opt *opt;
7500a6e7778SPieter Jansen van Vuuren 	int err, data_len = 0;
7510a6e7778SPieter Jansen van Vuuren 
7520a6e7778SPieter Jansen van Vuuren 	if (option_len > sizeof(struct geneve_opt))
7530a6e7778SPieter Jansen van Vuuren 		data_len = option_len - sizeof(struct geneve_opt);
7540a6e7778SPieter Jansen van Vuuren 
7550a6e7778SPieter Jansen van Vuuren 	opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len];
7560a6e7778SPieter Jansen van Vuuren 	memset(opt, 0xff, option_len);
7570a6e7778SPieter Jansen van Vuuren 	opt->length = data_len / 4;
7580a6e7778SPieter Jansen van Vuuren 	opt->r1 = 0;
7590a6e7778SPieter Jansen van Vuuren 	opt->r2 = 0;
7600a6e7778SPieter Jansen van Vuuren 	opt->r3 = 0;
7610a6e7778SPieter Jansen van Vuuren 
7620a6e7778SPieter Jansen van Vuuren 	/* If no mask has been prodived we assume an exact match. */
7630a6e7778SPieter Jansen van Vuuren 	if (!depth)
7640a6e7778SPieter Jansen van Vuuren 		return sizeof(struct geneve_opt) + data_len;
7650a6e7778SPieter Jansen van Vuuren 
7660a6e7778SPieter Jansen van Vuuren 	if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GENEVE) {
7670a6e7778SPieter Jansen van Vuuren 		NL_SET_ERR_MSG(extack, "Non-geneve option type for mask");
7680a6e7778SPieter Jansen van Vuuren 		return -EINVAL;
7690a6e7778SPieter Jansen van Vuuren 	}
7700a6e7778SPieter Jansen van Vuuren 
7710a6e7778SPieter Jansen van Vuuren 	err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
7720a6e7778SPieter Jansen van Vuuren 			       nla, geneve_opt_policy, extack);
7730a6e7778SPieter Jansen van Vuuren 	if (err < 0)
7740a6e7778SPieter Jansen van Vuuren 		return err;
7750a6e7778SPieter Jansen van Vuuren 
7760a6e7778SPieter Jansen van Vuuren 	/* We are not allowed to omit any of CLASS, TYPE or DATA
7770a6e7778SPieter Jansen van Vuuren 	 * fields from the key.
7780a6e7778SPieter Jansen van Vuuren 	 */
7790a6e7778SPieter Jansen van Vuuren 	if (!option_len &&
7800a6e7778SPieter Jansen van Vuuren 	    (!tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] ||
7810a6e7778SPieter Jansen van Vuuren 	     !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] ||
7820a6e7778SPieter Jansen van Vuuren 	     !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA])) {
7830a6e7778SPieter Jansen van Vuuren 		NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data");
7840a6e7778SPieter Jansen van Vuuren 		return -EINVAL;
7850a6e7778SPieter Jansen van Vuuren 	}
7860a6e7778SPieter Jansen van Vuuren 
7870a6e7778SPieter Jansen van Vuuren 	/* Omitting any of CLASS, TYPE or DATA fields is allowed
7880a6e7778SPieter Jansen van Vuuren 	 * for the mask.
7890a6e7778SPieter Jansen van Vuuren 	 */
7900a6e7778SPieter Jansen van Vuuren 	if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]) {
7910a6e7778SPieter Jansen van Vuuren 		int new_len = key->enc_opts.len;
7920a6e7778SPieter Jansen van Vuuren 
7930a6e7778SPieter Jansen van Vuuren 		data = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA];
7940a6e7778SPieter Jansen van Vuuren 		data_len = nla_len(data);
7950a6e7778SPieter Jansen van Vuuren 		if (data_len < 4) {
7960a6e7778SPieter Jansen van Vuuren 			NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long");
7970a6e7778SPieter Jansen van Vuuren 			return -ERANGE;
7980a6e7778SPieter Jansen van Vuuren 		}
7990a6e7778SPieter Jansen van Vuuren 		if (data_len % 4) {
8000a6e7778SPieter Jansen van Vuuren 			NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long");
8010a6e7778SPieter Jansen van Vuuren 			return -ERANGE;
8020a6e7778SPieter Jansen van Vuuren 		}
8030a6e7778SPieter Jansen van Vuuren 
8040a6e7778SPieter Jansen van Vuuren 		new_len += sizeof(struct geneve_opt) + data_len;
8050a6e7778SPieter Jansen van Vuuren 		BUILD_BUG_ON(FLOW_DIS_TUN_OPTS_MAX != IP_TUNNEL_OPTS_MAX);
8060a6e7778SPieter Jansen van Vuuren 		if (new_len > FLOW_DIS_TUN_OPTS_MAX) {
8070a6e7778SPieter Jansen van Vuuren 			NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size");
8080a6e7778SPieter Jansen van Vuuren 			return -ERANGE;
8090a6e7778SPieter Jansen van Vuuren 		}
8100a6e7778SPieter Jansen van Vuuren 		opt->length = data_len / 4;
8110a6e7778SPieter Jansen van Vuuren 		memcpy(opt->opt_data, nla_data(data), data_len);
8120a6e7778SPieter Jansen van Vuuren 	}
8130a6e7778SPieter Jansen van Vuuren 
8140a6e7778SPieter Jansen van Vuuren 	if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]) {
8150a6e7778SPieter Jansen van Vuuren 		class = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS];
8160a6e7778SPieter Jansen van Vuuren 		opt->opt_class = nla_get_be16(class);
8170a6e7778SPieter Jansen van Vuuren 	}
8180a6e7778SPieter Jansen van Vuuren 
8190a6e7778SPieter Jansen van Vuuren 	if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]) {
8200a6e7778SPieter Jansen van Vuuren 		type = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE];
8210a6e7778SPieter Jansen van Vuuren 		opt->type = nla_get_u8(type);
8220a6e7778SPieter Jansen van Vuuren 	}
8230a6e7778SPieter Jansen van Vuuren 
8240a6e7778SPieter Jansen van Vuuren 	return sizeof(struct geneve_opt) + data_len;
8250a6e7778SPieter Jansen van Vuuren }
8260a6e7778SPieter Jansen van Vuuren 
8270a6e7778SPieter Jansen van Vuuren static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
8280a6e7778SPieter Jansen van Vuuren 			  struct fl_flow_key *mask,
8290a6e7778SPieter Jansen van Vuuren 			  struct netlink_ext_ack *extack)
8300a6e7778SPieter Jansen van Vuuren {
8310a6e7778SPieter Jansen van Vuuren 	const struct nlattr *nla_enc_key, *nla_opt_key, *nla_opt_msk = NULL;
83263c82997SJakub Kicinski 	int err, option_len, key_depth, msk_depth = 0;
83363c82997SJakub Kicinski 
83463c82997SJakub Kicinski 	err = nla_validate_nested(tb[TCA_FLOWER_KEY_ENC_OPTS],
83563c82997SJakub Kicinski 				  TCA_FLOWER_KEY_ENC_OPTS_MAX,
83663c82997SJakub Kicinski 				  enc_opts_policy, extack);
83763c82997SJakub Kicinski 	if (err)
83863c82997SJakub Kicinski 		return err;
8390a6e7778SPieter Jansen van Vuuren 
8400a6e7778SPieter Jansen van Vuuren 	nla_enc_key = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS]);
8410a6e7778SPieter Jansen van Vuuren 
8420a6e7778SPieter Jansen van Vuuren 	if (tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]) {
84363c82997SJakub Kicinski 		err = nla_validate_nested(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK],
84463c82997SJakub Kicinski 					  TCA_FLOWER_KEY_ENC_OPTS_MAX,
84563c82997SJakub Kicinski 					  enc_opts_policy, extack);
84663c82997SJakub Kicinski 		if (err)
84763c82997SJakub Kicinski 			return err;
84863c82997SJakub Kicinski 
8490a6e7778SPieter Jansen van Vuuren 		nla_opt_msk = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
8500a6e7778SPieter Jansen van Vuuren 		msk_depth = nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
8510a6e7778SPieter Jansen van Vuuren 	}
8520a6e7778SPieter Jansen van Vuuren 
8530a6e7778SPieter Jansen van Vuuren 	nla_for_each_attr(nla_opt_key, nla_enc_key,
8540a6e7778SPieter Jansen van Vuuren 			  nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS]), key_depth) {
8550a6e7778SPieter Jansen van Vuuren 		switch (nla_type(nla_opt_key)) {
8560a6e7778SPieter Jansen van Vuuren 		case TCA_FLOWER_KEY_ENC_OPTS_GENEVE:
8570a6e7778SPieter Jansen van Vuuren 			option_len = 0;
8580a6e7778SPieter Jansen van Vuuren 			key->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT;
8590a6e7778SPieter Jansen van Vuuren 			option_len = fl_set_geneve_opt(nla_opt_key, key,
8600a6e7778SPieter Jansen van Vuuren 						       key_depth, option_len,
8610a6e7778SPieter Jansen van Vuuren 						       extack);
8620a6e7778SPieter Jansen van Vuuren 			if (option_len < 0)
8630a6e7778SPieter Jansen van Vuuren 				return option_len;
8640a6e7778SPieter Jansen van Vuuren 
8650a6e7778SPieter Jansen van Vuuren 			key->enc_opts.len += option_len;
8660a6e7778SPieter Jansen van Vuuren 			/* At the same time we need to parse through the mask
8670a6e7778SPieter Jansen van Vuuren 			 * in order to verify exact and mask attribute lengths.
8680a6e7778SPieter Jansen van Vuuren 			 */
8690a6e7778SPieter Jansen van Vuuren 			mask->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT;
8700a6e7778SPieter Jansen van Vuuren 			option_len = fl_set_geneve_opt(nla_opt_msk, mask,
8710a6e7778SPieter Jansen van Vuuren 						       msk_depth, option_len,
8720a6e7778SPieter Jansen van Vuuren 						       extack);
8730a6e7778SPieter Jansen van Vuuren 			if (option_len < 0)
8740a6e7778SPieter Jansen van Vuuren 				return option_len;
8750a6e7778SPieter Jansen van Vuuren 
8760a6e7778SPieter Jansen van Vuuren 			mask->enc_opts.len += option_len;
8770a6e7778SPieter Jansen van Vuuren 			if (key->enc_opts.len != mask->enc_opts.len) {
8780a6e7778SPieter Jansen van Vuuren 				NL_SET_ERR_MSG(extack, "Key and mask miss aligned");
8790a6e7778SPieter Jansen van Vuuren 				return -EINVAL;
8800a6e7778SPieter Jansen van Vuuren 			}
8810a6e7778SPieter Jansen van Vuuren 
8820a6e7778SPieter Jansen van Vuuren 			if (msk_depth)
8830a6e7778SPieter Jansen van Vuuren 				nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
8840a6e7778SPieter Jansen van Vuuren 			break;
8850a6e7778SPieter Jansen van Vuuren 		default:
8860a6e7778SPieter Jansen van Vuuren 			NL_SET_ERR_MSG(extack, "Unknown tunnel option type");
8870a6e7778SPieter Jansen van Vuuren 			return -EINVAL;
8880a6e7778SPieter Jansen van Vuuren 		}
8890a6e7778SPieter Jansen van Vuuren 	}
8900a6e7778SPieter Jansen van Vuuren 
8910a6e7778SPieter Jansen van Vuuren 	return 0;
8920a6e7778SPieter Jansen van Vuuren }
8930a6e7778SPieter Jansen van Vuuren 
89477b9900eSJiri Pirko static int fl_set_key(struct net *net, struct nlattr **tb,
8951057c55fSAlexander Aring 		      struct fl_flow_key *key, struct fl_flow_key *mask,
8961057c55fSAlexander Aring 		      struct netlink_ext_ack *extack)
89777b9900eSJiri Pirko {
8989399ae9aSHadar Hen Zion 	__be16 ethertype;
899d9724772SOr Gerlitz 	int ret = 0;
900dd3aa3b5SBrian Haley #ifdef CONFIG_NET_CLS_IND
90177b9900eSJiri Pirko 	if (tb[TCA_FLOWER_INDEV]) {
9021057c55fSAlexander Aring 		int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV], extack);
90377b9900eSJiri Pirko 		if (err < 0)
90477b9900eSJiri Pirko 			return err;
90577b9900eSJiri Pirko 		key->indev_ifindex = err;
90677b9900eSJiri Pirko 		mask->indev_ifindex = 0xffffffff;
90777b9900eSJiri Pirko 	}
908dd3aa3b5SBrian Haley #endif
90977b9900eSJiri Pirko 
91077b9900eSJiri Pirko 	fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST,
91177b9900eSJiri Pirko 		       mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK,
91277b9900eSJiri Pirko 		       sizeof(key->eth.dst));
91377b9900eSJiri Pirko 	fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC,
91477b9900eSJiri Pirko 		       mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK,
91577b9900eSJiri Pirko 		       sizeof(key->eth.src));
91666530bdfSJamal Hadi Salim 
9170b498a52SArnd Bergmann 	if (tb[TCA_FLOWER_KEY_ETH_TYPE]) {
9189399ae9aSHadar Hen Zion 		ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]);
9199399ae9aSHadar Hen Zion 
920aaab0834SJianbo Liu 		if (eth_type_vlan(ethertype)) {
921d64efd09SJianbo Liu 			fl_set_key_vlan(tb, ethertype, TCA_FLOWER_KEY_VLAN_ID,
922d64efd09SJianbo Liu 					TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan,
923d64efd09SJianbo Liu 					&mask->vlan);
924d64efd09SJianbo Liu 
9255e9a0fe4SJianbo Liu 			if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) {
926d64efd09SJianbo Liu 				ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]);
927d64efd09SJianbo Liu 				if (eth_type_vlan(ethertype)) {
928d64efd09SJianbo Liu 					fl_set_key_vlan(tb, ethertype,
929d64efd09SJianbo Liu 							TCA_FLOWER_KEY_CVLAN_ID,
930d64efd09SJianbo Liu 							TCA_FLOWER_KEY_CVLAN_PRIO,
931d64efd09SJianbo Liu 							&key->cvlan, &mask->cvlan);
9329399ae9aSHadar Hen Zion 					fl_set_key_val(tb, &key->basic.n_proto,
933d64efd09SJianbo Liu 						       TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
934d64efd09SJianbo Liu 						       &mask->basic.n_proto,
935d64efd09SJianbo Liu 						       TCA_FLOWER_UNSPEC,
93677b9900eSJiri Pirko 						       sizeof(key->basic.n_proto));
9379399ae9aSHadar Hen Zion 				} else {
9389399ae9aSHadar Hen Zion 					key->basic.n_proto = ethertype;
9399399ae9aSHadar Hen Zion 					mask->basic.n_proto = cpu_to_be16(~0);
9409399ae9aSHadar Hen Zion 				}
9415e9a0fe4SJianbo Liu 			}
942d64efd09SJianbo Liu 		} else {
943d64efd09SJianbo Liu 			key->basic.n_proto = ethertype;
944d64efd09SJianbo Liu 			mask->basic.n_proto = cpu_to_be16(~0);
945d64efd09SJianbo Liu 		}
9460b498a52SArnd Bergmann 	}
94766530bdfSJamal Hadi Salim 
94877b9900eSJiri Pirko 	if (key->basic.n_proto == htons(ETH_P_IP) ||
94977b9900eSJiri Pirko 	    key->basic.n_proto == htons(ETH_P_IPV6)) {
95077b9900eSJiri Pirko 		fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO,
95177b9900eSJiri Pirko 			       &mask->basic.ip_proto, TCA_FLOWER_UNSPEC,
95277b9900eSJiri Pirko 			       sizeof(key->basic.ip_proto));
9530e2c17b6SOr Gerlitz 		fl_set_key_ip(tb, false, &key->ip, &mask->ip);
95477b9900eSJiri Pirko 	}
95566530bdfSJamal Hadi Salim 
95666530bdfSJamal Hadi Salim 	if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) {
95766530bdfSJamal Hadi Salim 		key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
958970bfcd0SPaul Blakey 		mask->control.addr_type = ~0;
95977b9900eSJiri Pirko 		fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC,
96077b9900eSJiri Pirko 			       &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK,
96177b9900eSJiri Pirko 			       sizeof(key->ipv4.src));
96277b9900eSJiri Pirko 		fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST,
96377b9900eSJiri Pirko 			       &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK,
96477b9900eSJiri Pirko 			       sizeof(key->ipv4.dst));
96566530bdfSJamal Hadi Salim 	} else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) {
96666530bdfSJamal Hadi Salim 		key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
967970bfcd0SPaul Blakey 		mask->control.addr_type = ~0;
96877b9900eSJiri Pirko 		fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC,
96977b9900eSJiri Pirko 			       &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK,
97077b9900eSJiri Pirko 			       sizeof(key->ipv6.src));
97177b9900eSJiri Pirko 		fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST,
97277b9900eSJiri Pirko 			       &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK,
97377b9900eSJiri Pirko 			       sizeof(key->ipv6.dst));
97477b9900eSJiri Pirko 	}
97566530bdfSJamal Hadi Salim 
97677b9900eSJiri Pirko 	if (key->basic.ip_proto == IPPROTO_TCP) {
97777b9900eSJiri Pirko 		fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC,
978aa72d708SOr Gerlitz 			       &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK,
97977b9900eSJiri Pirko 			       sizeof(key->tp.src));
98077b9900eSJiri Pirko 		fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST,
981aa72d708SOr Gerlitz 			       &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK,
98277b9900eSJiri Pirko 			       sizeof(key->tp.dst));
983fdfc7dd6SJiri Pirko 		fl_set_key_val(tb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS,
984fdfc7dd6SJiri Pirko 			       &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK,
985fdfc7dd6SJiri Pirko 			       sizeof(key->tcp.flags));
98677b9900eSJiri Pirko 	} else if (key->basic.ip_proto == IPPROTO_UDP) {
98777b9900eSJiri Pirko 		fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC,
988aa72d708SOr Gerlitz 			       &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK,
98977b9900eSJiri Pirko 			       sizeof(key->tp.src));
99077b9900eSJiri Pirko 		fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST,
991aa72d708SOr Gerlitz 			       &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK,
99277b9900eSJiri Pirko 			       sizeof(key->tp.dst));
9935976c5f4SSimon Horman 	} else if (key->basic.ip_proto == IPPROTO_SCTP) {
9945976c5f4SSimon Horman 		fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC,
9955976c5f4SSimon Horman 			       &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK,
9965976c5f4SSimon Horman 			       sizeof(key->tp.src));
9975976c5f4SSimon Horman 		fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST,
9985976c5f4SSimon Horman 			       &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK,
9995976c5f4SSimon Horman 			       sizeof(key->tp.dst));
10007b684884SSimon Horman 	} else if (key->basic.n_proto == htons(ETH_P_IP) &&
10017b684884SSimon Horman 		   key->basic.ip_proto == IPPROTO_ICMP) {
10027b684884SSimon Horman 		fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE,
10037b684884SSimon Horman 			       &mask->icmp.type,
10047b684884SSimon Horman 			       TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,
10057b684884SSimon Horman 			       sizeof(key->icmp.type));
10067b684884SSimon Horman 		fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE,
10077b684884SSimon Horman 			       &mask->icmp.code,
10087b684884SSimon Horman 			       TCA_FLOWER_KEY_ICMPV4_CODE_MASK,
10097b684884SSimon Horman 			       sizeof(key->icmp.code));
10107b684884SSimon Horman 	} else if (key->basic.n_proto == htons(ETH_P_IPV6) &&
10117b684884SSimon Horman 		   key->basic.ip_proto == IPPROTO_ICMPV6) {
10127b684884SSimon Horman 		fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE,
10137b684884SSimon Horman 			       &mask->icmp.type,
10147b684884SSimon Horman 			       TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,
10157b684884SSimon Horman 			       sizeof(key->icmp.type));
1016040587afSSimon Horman 		fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE,
10177b684884SSimon Horman 			       &mask->icmp.code,
1018040587afSSimon Horman 			       TCA_FLOWER_KEY_ICMPV6_CODE_MASK,
10197b684884SSimon Horman 			       sizeof(key->icmp.code));
1020a577d8f7SBenjamin LaHaise 	} else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) ||
1021a577d8f7SBenjamin LaHaise 		   key->basic.n_proto == htons(ETH_P_MPLS_MC)) {
10221a7fca63SBenjamin LaHaise 		ret = fl_set_key_mpls(tb, &key->mpls, &mask->mpls);
10231a7fca63SBenjamin LaHaise 		if (ret)
10241a7fca63SBenjamin LaHaise 			return ret;
102599d31326SSimon Horman 	} else if (key->basic.n_proto == htons(ETH_P_ARP) ||
102699d31326SSimon Horman 		   key->basic.n_proto == htons(ETH_P_RARP)) {
102799d31326SSimon Horman 		fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP,
102899d31326SSimon Horman 			       &mask->arp.sip, TCA_FLOWER_KEY_ARP_SIP_MASK,
102999d31326SSimon Horman 			       sizeof(key->arp.sip));
103099d31326SSimon Horman 		fl_set_key_val(tb, &key->arp.tip, TCA_FLOWER_KEY_ARP_TIP,
103199d31326SSimon Horman 			       &mask->arp.tip, TCA_FLOWER_KEY_ARP_TIP_MASK,
103299d31326SSimon Horman 			       sizeof(key->arp.tip));
103399d31326SSimon Horman 		fl_set_key_val(tb, &key->arp.op, TCA_FLOWER_KEY_ARP_OP,
103499d31326SSimon Horman 			       &mask->arp.op, TCA_FLOWER_KEY_ARP_OP_MASK,
103599d31326SSimon Horman 			       sizeof(key->arp.op));
103699d31326SSimon Horman 		fl_set_key_val(tb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA,
103799d31326SSimon Horman 			       mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK,
103899d31326SSimon Horman 			       sizeof(key->arp.sha));
103999d31326SSimon Horman 		fl_set_key_val(tb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA,
104099d31326SSimon Horman 			       mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK,
104199d31326SSimon Horman 			       sizeof(key->arp.tha));
104277b9900eSJiri Pirko 	}
104377b9900eSJiri Pirko 
10445c72299fSAmritha Nambiar 	if (key->basic.ip_proto == IPPROTO_TCP ||
10455c72299fSAmritha Nambiar 	    key->basic.ip_proto == IPPROTO_UDP ||
10465c72299fSAmritha Nambiar 	    key->basic.ip_proto == IPPROTO_SCTP) {
10475c72299fSAmritha Nambiar 		ret = fl_set_key_port_range(tb, key, mask);
10485c72299fSAmritha Nambiar 		if (ret)
10495c72299fSAmritha Nambiar 			return ret;
10505c72299fSAmritha Nambiar 	}
10515c72299fSAmritha Nambiar 
1052bc3103f1SAmir Vadai 	if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] ||
1053bc3103f1SAmir Vadai 	    tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) {
1054bc3103f1SAmir Vadai 		key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
1055970bfcd0SPaul Blakey 		mask->enc_control.addr_type = ~0;
1056bc3103f1SAmir Vadai 		fl_set_key_val(tb, &key->enc_ipv4.src,
1057bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV4_SRC,
1058bc3103f1SAmir Vadai 			       &mask->enc_ipv4.src,
1059bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,
1060bc3103f1SAmir Vadai 			       sizeof(key->enc_ipv4.src));
1061bc3103f1SAmir Vadai 		fl_set_key_val(tb, &key->enc_ipv4.dst,
1062bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV4_DST,
1063bc3103f1SAmir Vadai 			       &mask->enc_ipv4.dst,
1064bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
1065bc3103f1SAmir Vadai 			       sizeof(key->enc_ipv4.dst));
1066bc3103f1SAmir Vadai 	}
1067bc3103f1SAmir Vadai 
1068bc3103f1SAmir Vadai 	if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] ||
1069bc3103f1SAmir Vadai 	    tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) {
1070bc3103f1SAmir Vadai 		key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
1071970bfcd0SPaul Blakey 		mask->enc_control.addr_type = ~0;
1072bc3103f1SAmir Vadai 		fl_set_key_val(tb, &key->enc_ipv6.src,
1073bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV6_SRC,
1074bc3103f1SAmir Vadai 			       &mask->enc_ipv6.src,
1075bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,
1076bc3103f1SAmir Vadai 			       sizeof(key->enc_ipv6.src));
1077bc3103f1SAmir Vadai 		fl_set_key_val(tb, &key->enc_ipv6.dst,
1078bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV6_DST,
1079bc3103f1SAmir Vadai 			       &mask->enc_ipv6.dst,
1080bc3103f1SAmir Vadai 			       TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,
1081bc3103f1SAmir Vadai 			       sizeof(key->enc_ipv6.dst));
1082bc3103f1SAmir Vadai 	}
1083bc3103f1SAmir Vadai 
1084bc3103f1SAmir Vadai 	fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID,
1085eb523f42SHadar Hen Zion 		       &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC,
1086bc3103f1SAmir Vadai 		       sizeof(key->enc_key_id.keyid));
1087bc3103f1SAmir Vadai 
1088f4d997fdSHadar Hen Zion 	fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT,
1089f4d997fdSHadar Hen Zion 		       &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK,
1090f4d997fdSHadar Hen Zion 		       sizeof(key->enc_tp.src));
1091f4d997fdSHadar Hen Zion 
1092f4d997fdSHadar Hen Zion 	fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT,
1093f4d997fdSHadar Hen Zion 		       &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,
1094f4d997fdSHadar Hen Zion 		       sizeof(key->enc_tp.dst));
1095f4d997fdSHadar Hen Zion 
10960e2c17b6SOr Gerlitz 	fl_set_key_ip(tb, true, &key->enc_ip, &mask->enc_ip);
10970e2c17b6SOr Gerlitz 
10980a6e7778SPieter Jansen van Vuuren 	if (tb[TCA_FLOWER_KEY_ENC_OPTS]) {
10990a6e7778SPieter Jansen van Vuuren 		ret = fl_set_enc_opt(tb, key, mask, extack);
11000a6e7778SPieter Jansen van Vuuren 		if (ret)
11010a6e7778SPieter Jansen van Vuuren 			return ret;
11020a6e7778SPieter Jansen van Vuuren 	}
11030a6e7778SPieter Jansen van Vuuren 
1104d9724772SOr Gerlitz 	if (tb[TCA_FLOWER_KEY_FLAGS])
1105d9724772SOr Gerlitz 		ret = fl_set_key_flags(tb, &key->control.flags, &mask->control.flags);
1106faa3ffceSOr Gerlitz 
1107d9724772SOr Gerlitz 	return ret;
110877b9900eSJiri Pirko }
110977b9900eSJiri Pirko 
111005cd271fSPaul Blakey static void fl_mask_copy(struct fl_flow_mask *dst,
111105cd271fSPaul Blakey 			 struct fl_flow_mask *src)
111277b9900eSJiri Pirko {
111305cd271fSPaul Blakey 	const void *psrc = fl_key_get_start(&src->key, src);
111405cd271fSPaul Blakey 	void *pdst = fl_key_get_start(&dst->key, src);
111577b9900eSJiri Pirko 
111605cd271fSPaul Blakey 	memcpy(pdst, psrc, fl_mask_range(src));
111705cd271fSPaul Blakey 	dst->range = src->range;
111877b9900eSJiri Pirko }
111977b9900eSJiri Pirko 
112077b9900eSJiri Pirko static const struct rhashtable_params fl_ht_params = {
112177b9900eSJiri Pirko 	.key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */
112277b9900eSJiri Pirko 	.head_offset = offsetof(struct cls_fl_filter, ht_node),
112377b9900eSJiri Pirko 	.automatic_shrinking = true,
112477b9900eSJiri Pirko };
112577b9900eSJiri Pirko 
112605cd271fSPaul Blakey static int fl_init_mask_hashtable(struct fl_flow_mask *mask)
112777b9900eSJiri Pirko {
112805cd271fSPaul Blakey 	mask->filter_ht_params = fl_ht_params;
112905cd271fSPaul Blakey 	mask->filter_ht_params.key_len = fl_mask_range(mask);
113005cd271fSPaul Blakey 	mask->filter_ht_params.key_offset += mask->range.start;
113177b9900eSJiri Pirko 
113205cd271fSPaul Blakey 	return rhashtable_init(&mask->ht, &mask->filter_ht_params);
113377b9900eSJiri Pirko }
113477b9900eSJiri Pirko 
113577b9900eSJiri Pirko #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member)
1136cb205a81Szhong jiang #define FL_KEY_MEMBER_SIZE(member) FIELD_SIZEOF(struct fl_flow_key, member)
113777b9900eSJiri Pirko 
1138339ba878SHadar Hen Zion #define FL_KEY_IS_MASKED(mask, member)						\
1139339ba878SHadar Hen Zion 	memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member),		\
1140339ba878SHadar Hen Zion 		   0, FL_KEY_MEMBER_SIZE(member))				\
114177b9900eSJiri Pirko 
114277b9900eSJiri Pirko #define FL_KEY_SET(keys, cnt, id, member)					\
114377b9900eSJiri Pirko 	do {									\
114477b9900eSJiri Pirko 		keys[cnt].key_id = id;						\
114577b9900eSJiri Pirko 		keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member);		\
114677b9900eSJiri Pirko 		cnt++;								\
114777b9900eSJiri Pirko 	} while(0);
114877b9900eSJiri Pirko 
1149339ba878SHadar Hen Zion #define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member)			\
115077b9900eSJiri Pirko 	do {									\
1151339ba878SHadar Hen Zion 		if (FL_KEY_IS_MASKED(mask, member))				\
115277b9900eSJiri Pirko 			FL_KEY_SET(keys, cnt, id, member);			\
115377b9900eSJiri Pirko 	} while(0);
115477b9900eSJiri Pirko 
115533fb5cbaSJiri Pirko static void fl_init_dissector(struct flow_dissector *dissector,
115633fb5cbaSJiri Pirko 			      struct fl_flow_key *mask)
115777b9900eSJiri Pirko {
115877b9900eSJiri Pirko 	struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX];
115977b9900eSJiri Pirko 	size_t cnt = 0;
116077b9900eSJiri Pirko 
116142aecaa9STom Herbert 	FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control);
116277b9900eSJiri Pirko 	FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic);
116333fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
116477b9900eSJiri Pirko 			     FLOW_DISSECTOR_KEY_ETH_ADDRS, eth);
116533fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
116677b9900eSJiri Pirko 			     FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4);
116733fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
116877b9900eSJiri Pirko 			     FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6);
11695c72299fSAmritha Nambiar 	if (FL_KEY_IS_MASKED(mask, tp) ||
11705c72299fSAmritha Nambiar 	    FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max))
11715c72299fSAmritha Nambiar 		FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp);
117233fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
11734d80cc0aSOr Gerlitz 			     FLOW_DISSECTOR_KEY_IP, ip);
117433fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1175fdfc7dd6SJiri Pirko 			     FLOW_DISSECTOR_KEY_TCP, tcp);
117633fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
11777b684884SSimon Horman 			     FLOW_DISSECTOR_KEY_ICMP, icmp);
117833fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
117999d31326SSimon Horman 			     FLOW_DISSECTOR_KEY_ARP, arp);
118033fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1181a577d8f7SBenjamin LaHaise 			     FLOW_DISSECTOR_KEY_MPLS, mpls);
118233fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
11839399ae9aSHadar Hen Zion 			     FLOW_DISSECTOR_KEY_VLAN, vlan);
118433fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1185d64efd09SJianbo Liu 			     FLOW_DISSECTOR_KEY_CVLAN, cvlan);
118633fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1187519d1052SHadar Hen Zion 			     FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id);
118833fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1189519d1052SHadar Hen Zion 			     FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4);
119033fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1191519d1052SHadar Hen Zion 			     FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6);
119233fb5cbaSJiri Pirko 	if (FL_KEY_IS_MASKED(mask, enc_ipv4) ||
119333fb5cbaSJiri Pirko 	    FL_KEY_IS_MASKED(mask, enc_ipv6))
1194519d1052SHadar Hen Zion 		FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL,
1195519d1052SHadar Hen Zion 			   enc_control);
119633fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
1197f4d997fdSHadar Hen Zion 			     FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp);
119833fb5cbaSJiri Pirko 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
11990e2c17b6SOr Gerlitz 			     FLOW_DISSECTOR_KEY_ENC_IP, enc_ip);
12000a6e7778SPieter Jansen van Vuuren 	FL_KEY_SET_IF_MASKED(mask, keys, cnt,
12010a6e7778SPieter Jansen van Vuuren 			     FLOW_DISSECTOR_KEY_ENC_OPTS, enc_opts);
120277b9900eSJiri Pirko 
120333fb5cbaSJiri Pirko 	skb_flow_dissector_init(dissector, keys, cnt);
120405cd271fSPaul Blakey }
120505cd271fSPaul Blakey 
120605cd271fSPaul Blakey static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head,
120705cd271fSPaul Blakey 					       struct fl_flow_mask *mask)
120805cd271fSPaul Blakey {
120905cd271fSPaul Blakey 	struct fl_flow_mask *newmask;
121005cd271fSPaul Blakey 	int err;
121105cd271fSPaul Blakey 
121205cd271fSPaul Blakey 	newmask = kzalloc(sizeof(*newmask), GFP_KERNEL);
121305cd271fSPaul Blakey 	if (!newmask)
121405cd271fSPaul Blakey 		return ERR_PTR(-ENOMEM);
121505cd271fSPaul Blakey 
121605cd271fSPaul Blakey 	fl_mask_copy(newmask, mask);
121705cd271fSPaul Blakey 
12185c72299fSAmritha Nambiar 	if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) ||
12195c72299fSAmritha Nambiar 	    (newmask->key.tp_min.src && newmask->key.tp_max.src))
12205c72299fSAmritha Nambiar 		newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE;
12215c72299fSAmritha Nambiar 
122205cd271fSPaul Blakey 	err = fl_init_mask_hashtable(newmask);
122305cd271fSPaul Blakey 	if (err)
122405cd271fSPaul Blakey 		goto errout_free;
122505cd271fSPaul Blakey 
122633fb5cbaSJiri Pirko 	fl_init_dissector(&newmask->dissector, &newmask->key);
122705cd271fSPaul Blakey 
122805cd271fSPaul Blakey 	INIT_LIST_HEAD_RCU(&newmask->filters);
122905cd271fSPaul Blakey 
123005cd271fSPaul Blakey 	err = rhashtable_insert_fast(&head->ht, &newmask->ht_node,
123105cd271fSPaul Blakey 				     mask_ht_params);
123205cd271fSPaul Blakey 	if (err)
123305cd271fSPaul Blakey 		goto errout_destroy;
123405cd271fSPaul Blakey 
123505cd271fSPaul Blakey 	list_add_tail_rcu(&newmask->list, &head->masks);
123605cd271fSPaul Blakey 
123705cd271fSPaul Blakey 	return newmask;
123805cd271fSPaul Blakey 
123905cd271fSPaul Blakey errout_destroy:
124005cd271fSPaul Blakey 	rhashtable_destroy(&newmask->ht);
124105cd271fSPaul Blakey errout_free:
124205cd271fSPaul Blakey 	kfree(newmask);
124305cd271fSPaul Blakey 
124405cd271fSPaul Blakey 	return ERR_PTR(err);
124577b9900eSJiri Pirko }
124677b9900eSJiri Pirko 
124777b9900eSJiri Pirko static int fl_check_assign_mask(struct cls_fl_head *head,
124805cd271fSPaul Blakey 				struct cls_fl_filter *fnew,
124905cd271fSPaul Blakey 				struct cls_fl_filter *fold,
125077b9900eSJiri Pirko 				struct fl_flow_mask *mask)
125177b9900eSJiri Pirko {
125205cd271fSPaul Blakey 	struct fl_flow_mask *newmask;
125377b9900eSJiri Pirko 
125405cd271fSPaul Blakey 	fnew->mask = rhashtable_lookup_fast(&head->ht, mask, mask_ht_params);
125505cd271fSPaul Blakey 	if (!fnew->mask) {
125605cd271fSPaul Blakey 		if (fold)
125777b9900eSJiri Pirko 			return -EINVAL;
125805cd271fSPaul Blakey 
125905cd271fSPaul Blakey 		newmask = fl_create_new_mask(head, mask);
126005cd271fSPaul Blakey 		if (IS_ERR(newmask))
126105cd271fSPaul Blakey 			return PTR_ERR(newmask);
126205cd271fSPaul Blakey 
126305cd271fSPaul Blakey 		fnew->mask = newmask;
1264f6521c58SPaul Blakey 	} else if (fold && fold->mask != fnew->mask) {
126505cd271fSPaul Blakey 		return -EINVAL;
126677b9900eSJiri Pirko 	}
126777b9900eSJiri Pirko 
126877b9900eSJiri Pirko 	return 0;
126977b9900eSJiri Pirko }
127077b9900eSJiri Pirko 
127177b9900eSJiri Pirko static int fl_set_parms(struct net *net, struct tcf_proto *tp,
127277b9900eSJiri Pirko 			struct cls_fl_filter *f, struct fl_flow_mask *mask,
127377b9900eSJiri Pirko 			unsigned long base, struct nlattr **tb,
127450a56190SAlexander Aring 			struct nlattr *est, bool ovr,
1275b95ec7ebSJiri Pirko 			struct fl_flow_tmplt *tmplt,
127650a56190SAlexander Aring 			struct netlink_ext_ack *extack)
127777b9900eSJiri Pirko {
127877b9900eSJiri Pirko 	int err;
127977b9900eSJiri Pirko 
1280ec6743a1SVlad Buslov 	err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, true,
1281ec6743a1SVlad Buslov 				extack);
128277b9900eSJiri Pirko 	if (err < 0)
128377b9900eSJiri Pirko 		return err;
128477b9900eSJiri Pirko 
128577b9900eSJiri Pirko 	if (tb[TCA_FLOWER_CLASSID]) {
128677b9900eSJiri Pirko 		f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]);
128777b9900eSJiri Pirko 		tcf_bind_filter(tp, &f->res, base);
128877b9900eSJiri Pirko 	}
128977b9900eSJiri Pirko 
12901057c55fSAlexander Aring 	err = fl_set_key(net, tb, &f->key, &mask->key, extack);
129177b9900eSJiri Pirko 	if (err)
129245507529SJiri Pirko 		return err;
129377b9900eSJiri Pirko 
129477b9900eSJiri Pirko 	fl_mask_update_range(mask);
129577b9900eSJiri Pirko 	fl_set_masked_key(&f->mkey, &f->key, mask);
129677b9900eSJiri Pirko 
1297b95ec7ebSJiri Pirko 	if (!fl_mask_fits_tmplt(tmplt, mask)) {
1298b95ec7ebSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Mask does not fit the template");
1299b95ec7ebSJiri Pirko 		return -EINVAL;
1300b95ec7ebSJiri Pirko 	}
1301b95ec7ebSJiri Pirko 
130277b9900eSJiri Pirko 	return 0;
130377b9900eSJiri Pirko }
130477b9900eSJiri Pirko 
130577b9900eSJiri Pirko static int fl_change(struct net *net, struct sk_buff *in_skb,
130677b9900eSJiri Pirko 		     struct tcf_proto *tp, unsigned long base,
130777b9900eSJiri Pirko 		     u32 handle, struct nlattr **tca,
130812db03b6SVlad Buslov 		     void **arg, bool ovr, bool rtnl_held,
130912db03b6SVlad Buslov 		     struct netlink_ext_ack *extack)
131077b9900eSJiri Pirko {
131177b9900eSJiri Pirko 	struct cls_fl_head *head = rtnl_dereference(tp->root);
13128113c095SWANG Cong 	struct cls_fl_filter *fold = *arg;
131377b9900eSJiri Pirko 	struct cls_fl_filter *fnew;
13142cddd201SIvan Vecera 	struct fl_flow_mask *mask;
131539b7b6a6SArnd Bergmann 	struct nlattr **tb;
131677b9900eSJiri Pirko 	int err;
131777b9900eSJiri Pirko 
131877b9900eSJiri Pirko 	if (!tca[TCA_OPTIONS])
131977b9900eSJiri Pirko 		return -EINVAL;
132077b9900eSJiri Pirko 
13212cddd201SIvan Vecera 	mask = kzalloc(sizeof(struct fl_flow_mask), GFP_KERNEL);
13222cddd201SIvan Vecera 	if (!mask)
132339b7b6a6SArnd Bergmann 		return -ENOBUFS;
132439b7b6a6SArnd Bergmann 
13252cddd201SIvan Vecera 	tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL);
13262cddd201SIvan Vecera 	if (!tb) {
13272cddd201SIvan Vecera 		err = -ENOBUFS;
13282cddd201SIvan Vecera 		goto errout_mask_alloc;
13292cddd201SIvan Vecera 	}
13302cddd201SIvan Vecera 
1331fceb6435SJohannes Berg 	err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS],
1332fceb6435SJohannes Berg 			       fl_policy, NULL);
133377b9900eSJiri Pirko 	if (err < 0)
133439b7b6a6SArnd Bergmann 		goto errout_tb;
133577b9900eSJiri Pirko 
133639b7b6a6SArnd Bergmann 	if (fold && handle && fold->handle != handle) {
133739b7b6a6SArnd Bergmann 		err = -EINVAL;
133839b7b6a6SArnd Bergmann 		goto errout_tb;
133939b7b6a6SArnd Bergmann 	}
134077b9900eSJiri Pirko 
134177b9900eSJiri Pirko 	fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
134239b7b6a6SArnd Bergmann 	if (!fnew) {
134339b7b6a6SArnd Bergmann 		err = -ENOBUFS;
134439b7b6a6SArnd Bergmann 		goto errout_tb;
134539b7b6a6SArnd Bergmann 	}
134677b9900eSJiri Pirko 
1347b9a24bb7SWANG Cong 	err = tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0);
1348b9a24bb7SWANG Cong 	if (err < 0)
1349b9a24bb7SWANG Cong 		goto errout;
135077b9900eSJiri Pirko 
135177b9900eSJiri Pirko 	if (!handle) {
135285bd0438SMatthew Wilcox 		handle = 1;
135385bd0438SMatthew Wilcox 		err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
135485bd0438SMatthew Wilcox 				    INT_MAX, GFP_KERNEL);
135585bd0438SMatthew Wilcox 	} else if (!fold) {
1356c15ab236SChris Mi 		/* user specifies a handle and it doesn't exist */
135785bd0438SMatthew Wilcox 		err = idr_alloc_u32(&head->handle_idr, fnew, &handle,
135885bd0438SMatthew Wilcox 				    handle, GFP_KERNEL);
135985bd0438SMatthew Wilcox 	}
1360c15ab236SChris Mi 	if (err)
1361c15ab236SChris Mi 		goto errout;
136285bd0438SMatthew Wilcox 	fnew->handle = handle;
136377b9900eSJiri Pirko 
1364e69985c6SAmir Vadai 	if (tb[TCA_FLOWER_FLAGS]) {
1365e69985c6SAmir Vadai 		fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
1366e69985c6SAmir Vadai 
1367e69985c6SAmir Vadai 		if (!tc_flags_valid(fnew->flags)) {
1368e69985c6SAmir Vadai 			err = -EINVAL;
1369fe2502e4SCong Wang 			goto errout_idr;
1370e69985c6SAmir Vadai 		}
1371e69985c6SAmir Vadai 	}
13725b33f488SAmir Vadai 
13732cddd201SIvan Vecera 	err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE], ovr,
1374b95ec7ebSJiri Pirko 			   tp->chain->tmplt_priv, extack);
137577b9900eSJiri Pirko 	if (err)
1376fe2502e4SCong Wang 		goto errout_idr;
137777b9900eSJiri Pirko 
13782cddd201SIvan Vecera 	err = fl_check_assign_mask(head, fnew, fold, mask);
137977b9900eSJiri Pirko 	if (err)
1380fe2502e4SCong Wang 		goto errout_idr;
138177b9900eSJiri Pirko 
13825c72299fSAmritha Nambiar 	if (!fold && __fl_lookup(fnew->mask, &fnew->mkey)) {
1383a3308d8fSPaul Blakey 		err = -EEXIST;
138405cd271fSPaul Blakey 		goto errout_mask;
1385a3308d8fSPaul Blakey 	}
1386a3308d8fSPaul Blakey 
138705cd271fSPaul Blakey 	err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node,
138805cd271fSPaul Blakey 				     fnew->mask->filter_ht_params);
138977b9900eSJiri Pirko 	if (err)
139005cd271fSPaul Blakey 		goto errout_mask;
13915b33f488SAmir Vadai 
139279685219SHadar Hen Zion 	if (!tc_skip_hw(fnew->flags)) {
139305cd271fSPaul Blakey 		err = fl_hw_replace_filter(tp, fnew, extack);
1394e8eb36cdSAmir Vadai 		if (err)
1395c1f7e029SPetr Machata 			goto errout_mask_ht;
139679685219SHadar Hen Zion 	}
13975b33f488SAmir Vadai 
139855593960SOr Gerlitz 	if (!tc_in_hw(fnew->flags))
139955593960SOr Gerlitz 		fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
140055593960SOr Gerlitz 
14015b33f488SAmir Vadai 	if (fold) {
140205cd271fSPaul Blakey 		rhashtable_remove_fast(&fold->mask->ht,
140305cd271fSPaul Blakey 				       &fold->ht_node,
140405cd271fSPaul Blakey 				       fold->mask->filter_ht_params);
140579685219SHadar Hen Zion 		if (!tc_skip_hw(fold->flags))
14061b0f8037SJakub Kicinski 			fl_hw_destroy_filter(tp, fold, NULL);
14075b33f488SAmir Vadai 	}
140877b9900eSJiri Pirko 
14098113c095SWANG Cong 	*arg = fnew;
141077b9900eSJiri Pirko 
141177b9900eSJiri Pirko 	if (fold) {
1412234a4624SMatthew Wilcox 		idr_replace(&head->handle_idr, fnew, fnew->handle);
1413ff3532f2SDaniel Borkmann 		list_replace_rcu(&fold->list, &fnew->list);
141477b9900eSJiri Pirko 		tcf_unbind_filter(tp, &fold->res);
14150dadc117SCong Wang 		tcf_exts_get_net(&fold->exts);
1416aaa908ffSCong Wang 		tcf_queue_work(&fold->rwork, fl_destroy_filter_work);
141777b9900eSJiri Pirko 	} else {
141805cd271fSPaul Blakey 		list_add_tail_rcu(&fnew->list, &fnew->mask->filters);
141977b9900eSJiri Pirko 	}
142077b9900eSJiri Pirko 
142139b7b6a6SArnd Bergmann 	kfree(tb);
14222cddd201SIvan Vecera 	kfree(mask);
142377b9900eSJiri Pirko 	return 0;
142477b9900eSJiri Pirko 
1425c1f7e029SPetr Machata errout_mask_ht:
1426c1f7e029SPetr Machata 	rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node,
1427c1f7e029SPetr Machata 			       fnew->mask->filter_ht_params);
1428c1f7e029SPetr Machata 
142905cd271fSPaul Blakey errout_mask:
143005cd271fSPaul Blakey 	fl_mask_put(head, fnew->mask, false);
143105cd271fSPaul Blakey 
1432fe2502e4SCong Wang errout_idr:
14338258d2daSPaul Blakey 	if (!fold)
14349c160941SMatthew Wilcox 		idr_remove(&head->handle_idr, fnew->handle);
143577b9900eSJiri Pirko errout:
1436b9a24bb7SWANG Cong 	tcf_exts_destroy(&fnew->exts);
143777b9900eSJiri Pirko 	kfree(fnew);
143839b7b6a6SArnd Bergmann errout_tb:
143939b7b6a6SArnd Bergmann 	kfree(tb);
14402cddd201SIvan Vecera errout_mask_alloc:
14412cddd201SIvan Vecera 	kfree(mask);
144277b9900eSJiri Pirko 	return err;
144377b9900eSJiri Pirko }
144477b9900eSJiri Pirko 
1445571acf21SAlexander Aring static int fl_delete(struct tcf_proto *tp, void *arg, bool *last,
144612db03b6SVlad Buslov 		     bool rtnl_held, struct netlink_ext_ack *extack)
144777b9900eSJiri Pirko {
144877b9900eSJiri Pirko 	struct cls_fl_head *head = rtnl_dereference(tp->root);
14498113c095SWANG Cong 	struct cls_fl_filter *f = arg;
145077b9900eSJiri Pirko 
145105cd271fSPaul Blakey 	rhashtable_remove_fast(&f->mask->ht, &f->ht_node,
145205cd271fSPaul Blakey 			       f->mask->filter_ht_params);
14531b0f8037SJakub Kicinski 	__fl_delete(tp, f, extack);
145405cd271fSPaul Blakey 	*last = list_empty(&head->masks);
145577b9900eSJiri Pirko 	return 0;
145677b9900eSJiri Pirko }
145777b9900eSJiri Pirko 
145812db03b6SVlad Buslov static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg,
145912db03b6SVlad Buslov 		    bool rtnl_held)
146077b9900eSJiri Pirko {
146177b9900eSJiri Pirko 	struct cls_fl_head *head = rtnl_dereference(tp->root);
146277b9900eSJiri Pirko 	struct cls_fl_filter *f;
146377b9900eSJiri Pirko 
146401683a14SVlad Buslov 	arg->count = arg->skip;
146501683a14SVlad Buslov 
146601683a14SVlad Buslov 	while ((f = idr_get_next_ul(&head->handle_idr,
146701683a14SVlad Buslov 				    &arg->cookie)) != NULL) {
14688113c095SWANG Cong 		if (arg->fn(tp, f, arg) < 0) {
146977b9900eSJiri Pirko 			arg->stop = 1;
147077b9900eSJiri Pirko 			break;
147177b9900eSJiri Pirko 		}
147201683a14SVlad Buslov 		arg->cookie = f->handle + 1;
147377b9900eSJiri Pirko 		arg->count++;
147477b9900eSJiri Pirko 	}
147577b9900eSJiri Pirko }
147677b9900eSJiri Pirko 
147731533cbaSJohn Hurley static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
147831533cbaSJohn Hurley 			void *cb_priv, struct netlink_ext_ack *extack)
147931533cbaSJohn Hurley {
148031533cbaSJohn Hurley 	struct cls_fl_head *head = rtnl_dereference(tp->root);
148131533cbaSJohn Hurley 	struct tc_cls_flower_offload cls_flower = {};
148231533cbaSJohn Hurley 	struct tcf_block *block = tp->chain->block;
148331533cbaSJohn Hurley 	struct fl_flow_mask *mask;
148431533cbaSJohn Hurley 	struct cls_fl_filter *f;
148531533cbaSJohn Hurley 	int err;
148631533cbaSJohn Hurley 
148731533cbaSJohn Hurley 	list_for_each_entry(mask, &head->masks, list) {
148831533cbaSJohn Hurley 		list_for_each_entry(f, &mask->filters, list) {
148931533cbaSJohn Hurley 			if (tc_skip_hw(f->flags))
149031533cbaSJohn Hurley 				continue;
149131533cbaSJohn Hurley 
1492e3ab786bSPablo Neira Ayuso 			cls_flower.rule =
1493e3ab786bSPablo Neira Ayuso 				flow_rule_alloc(tcf_exts_num_actions(&f->exts));
14948f256622SPablo Neira Ayuso 			if (!cls_flower.rule)
14958f256622SPablo Neira Ayuso 				return -ENOMEM;
14968f256622SPablo Neira Ayuso 
149731533cbaSJohn Hurley 			tc_cls_common_offload_init(&cls_flower.common, tp,
149831533cbaSJohn Hurley 						   f->flags, extack);
149931533cbaSJohn Hurley 			cls_flower.command = add ?
150031533cbaSJohn Hurley 				TC_CLSFLOWER_REPLACE : TC_CLSFLOWER_DESTROY;
150131533cbaSJohn Hurley 			cls_flower.cookie = (unsigned long)f;
15028f256622SPablo Neira Ayuso 			cls_flower.rule->match.dissector = &mask->dissector;
15038f256622SPablo Neira Ayuso 			cls_flower.rule->match.mask = &mask->key;
15048f256622SPablo Neira Ayuso 			cls_flower.rule->match.key = &f->mkey;
15053a7b6861SPablo Neira Ayuso 
15063a7b6861SPablo Neira Ayuso 			err = tc_setup_flow_action(&cls_flower.rule->action,
15073a7b6861SPablo Neira Ayuso 						   &f->exts);
15083a7b6861SPablo Neira Ayuso 			if (err) {
15093a7b6861SPablo Neira Ayuso 				kfree(cls_flower.rule);
1510*1f15bb4fSVlad Buslov 				if (tc_skip_sw(f->flags)) {
1511*1f15bb4fSVlad Buslov 					NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action");
15123a7b6861SPablo Neira Ayuso 					return err;
15133a7b6861SPablo Neira Ayuso 				}
1514*1f15bb4fSVlad Buslov 				continue;
1515*1f15bb4fSVlad Buslov 			}
15163a7b6861SPablo Neira Ayuso 
151731533cbaSJohn Hurley 			cls_flower.classid = f->res.classid;
151831533cbaSJohn Hurley 
151931533cbaSJohn Hurley 			err = cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv);
15208f256622SPablo Neira Ayuso 			kfree(cls_flower.rule);
15218f256622SPablo Neira Ayuso 
152231533cbaSJohn Hurley 			if (err) {
152331533cbaSJohn Hurley 				if (add && tc_skip_sw(f->flags))
152431533cbaSJohn Hurley 					return err;
152531533cbaSJohn Hurley 				continue;
152631533cbaSJohn Hurley 			}
152731533cbaSJohn Hurley 
152831533cbaSJohn Hurley 			tc_cls_offload_cnt_update(block, &f->in_hw_count,
152931533cbaSJohn Hurley 						  &f->flags, add);
153031533cbaSJohn Hurley 		}
153131533cbaSJohn Hurley 	}
153231533cbaSJohn Hurley 
153331533cbaSJohn Hurley 	return 0;
153431533cbaSJohn Hurley }
153531533cbaSJohn Hurley 
15368f256622SPablo Neira Ayuso static int fl_hw_create_tmplt(struct tcf_chain *chain,
153734738452SJiri Pirko 			      struct fl_flow_tmplt *tmplt)
153834738452SJiri Pirko {
153934738452SJiri Pirko 	struct tc_cls_flower_offload cls_flower = {};
154034738452SJiri Pirko 	struct tcf_block *block = chain->block;
154134738452SJiri Pirko 
1542e3ab786bSPablo Neira Ayuso 	cls_flower.rule = flow_rule_alloc(0);
15438f256622SPablo Neira Ayuso 	if (!cls_flower.rule)
15448f256622SPablo Neira Ayuso 		return -ENOMEM;
15458f256622SPablo Neira Ayuso 
154634738452SJiri Pirko 	cls_flower.common.chain_index = chain->index;
154734738452SJiri Pirko 	cls_flower.command = TC_CLSFLOWER_TMPLT_CREATE;
154834738452SJiri Pirko 	cls_flower.cookie = (unsigned long) tmplt;
15498f256622SPablo Neira Ayuso 	cls_flower.rule->match.dissector = &tmplt->dissector;
15508f256622SPablo Neira Ayuso 	cls_flower.rule->match.mask = &tmplt->mask;
15518f256622SPablo Neira Ayuso 	cls_flower.rule->match.key = &tmplt->dummy_key;
155234738452SJiri Pirko 
155334738452SJiri Pirko 	/* We don't care if driver (any of them) fails to handle this
155434738452SJiri Pirko 	 * call. It serves just as a hint for it.
155534738452SJiri Pirko 	 */
1556aeb3fecdSCong Wang 	tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
15578f256622SPablo Neira Ayuso 	kfree(cls_flower.rule);
15588f256622SPablo Neira Ayuso 
15598f256622SPablo Neira Ayuso 	return 0;
156034738452SJiri Pirko }
156134738452SJiri Pirko 
156234738452SJiri Pirko static void fl_hw_destroy_tmplt(struct tcf_chain *chain,
156334738452SJiri Pirko 				struct fl_flow_tmplt *tmplt)
156434738452SJiri Pirko {
156534738452SJiri Pirko 	struct tc_cls_flower_offload cls_flower = {};
156634738452SJiri Pirko 	struct tcf_block *block = chain->block;
156734738452SJiri Pirko 
156834738452SJiri Pirko 	cls_flower.common.chain_index = chain->index;
156934738452SJiri Pirko 	cls_flower.command = TC_CLSFLOWER_TMPLT_DESTROY;
157034738452SJiri Pirko 	cls_flower.cookie = (unsigned long) tmplt;
157134738452SJiri Pirko 
1572aeb3fecdSCong Wang 	tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false);
157334738452SJiri Pirko }
157434738452SJiri Pirko 
1575b95ec7ebSJiri Pirko static void *fl_tmplt_create(struct net *net, struct tcf_chain *chain,
1576b95ec7ebSJiri Pirko 			     struct nlattr **tca,
1577b95ec7ebSJiri Pirko 			     struct netlink_ext_ack *extack)
1578b95ec7ebSJiri Pirko {
1579b95ec7ebSJiri Pirko 	struct fl_flow_tmplt *tmplt;
1580b95ec7ebSJiri Pirko 	struct nlattr **tb;
1581b95ec7ebSJiri Pirko 	int err;
1582b95ec7ebSJiri Pirko 
1583b95ec7ebSJiri Pirko 	if (!tca[TCA_OPTIONS])
1584b95ec7ebSJiri Pirko 		return ERR_PTR(-EINVAL);
1585b95ec7ebSJiri Pirko 
1586b95ec7ebSJiri Pirko 	tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL);
1587b95ec7ebSJiri Pirko 	if (!tb)
1588b95ec7ebSJiri Pirko 		return ERR_PTR(-ENOBUFS);
1589b95ec7ebSJiri Pirko 	err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS],
1590b95ec7ebSJiri Pirko 			       fl_policy, NULL);
1591b95ec7ebSJiri Pirko 	if (err)
1592b95ec7ebSJiri Pirko 		goto errout_tb;
1593b95ec7ebSJiri Pirko 
1594b95ec7ebSJiri Pirko 	tmplt = kzalloc(sizeof(*tmplt), GFP_KERNEL);
15951cbc36a5SDan Carpenter 	if (!tmplt) {
15961cbc36a5SDan Carpenter 		err = -ENOMEM;
1597b95ec7ebSJiri Pirko 		goto errout_tb;
15981cbc36a5SDan Carpenter 	}
1599b95ec7ebSJiri Pirko 	tmplt->chain = chain;
1600b95ec7ebSJiri Pirko 	err = fl_set_key(net, tb, &tmplt->dummy_key, &tmplt->mask, extack);
1601b95ec7ebSJiri Pirko 	if (err)
1602b95ec7ebSJiri Pirko 		goto errout_tmplt;
1603b95ec7ebSJiri Pirko 
1604b95ec7ebSJiri Pirko 	fl_init_dissector(&tmplt->dissector, &tmplt->mask);
1605b95ec7ebSJiri Pirko 
16068f256622SPablo Neira Ayuso 	err = fl_hw_create_tmplt(chain, tmplt);
16078f256622SPablo Neira Ayuso 	if (err)
16088f256622SPablo Neira Ayuso 		goto errout_tmplt;
160934738452SJiri Pirko 
16108f256622SPablo Neira Ayuso 	kfree(tb);
1611b95ec7ebSJiri Pirko 	return tmplt;
1612b95ec7ebSJiri Pirko 
1613b95ec7ebSJiri Pirko errout_tmplt:
1614b95ec7ebSJiri Pirko 	kfree(tmplt);
1615b95ec7ebSJiri Pirko errout_tb:
1616b95ec7ebSJiri Pirko 	kfree(tb);
1617b95ec7ebSJiri Pirko 	return ERR_PTR(err);
1618b95ec7ebSJiri Pirko }
1619b95ec7ebSJiri Pirko 
1620b95ec7ebSJiri Pirko static void fl_tmplt_destroy(void *tmplt_priv)
1621b95ec7ebSJiri Pirko {
1622b95ec7ebSJiri Pirko 	struct fl_flow_tmplt *tmplt = tmplt_priv;
1623b95ec7ebSJiri Pirko 
162495278ddaSCong Wang 	fl_hw_destroy_tmplt(tmplt->chain, tmplt);
162595278ddaSCong Wang 	kfree(tmplt);
1626b95ec7ebSJiri Pirko }
1627b95ec7ebSJiri Pirko 
162877b9900eSJiri Pirko static int fl_dump_key_val(struct sk_buff *skb,
162977b9900eSJiri Pirko 			   void *val, int val_type,
163077b9900eSJiri Pirko 			   void *mask, int mask_type, int len)
163177b9900eSJiri Pirko {
163277b9900eSJiri Pirko 	int err;
163377b9900eSJiri Pirko 
163477b9900eSJiri Pirko 	if (!memchr_inv(mask, 0, len))
163577b9900eSJiri Pirko 		return 0;
163677b9900eSJiri Pirko 	err = nla_put(skb, val_type, len, val);
163777b9900eSJiri Pirko 	if (err)
163877b9900eSJiri Pirko 		return err;
163977b9900eSJiri Pirko 	if (mask_type != TCA_FLOWER_UNSPEC) {
164077b9900eSJiri Pirko 		err = nla_put(skb, mask_type, len, mask);
164177b9900eSJiri Pirko 		if (err)
164277b9900eSJiri Pirko 			return err;
164377b9900eSJiri Pirko 	}
164477b9900eSJiri Pirko 	return 0;
164577b9900eSJiri Pirko }
164677b9900eSJiri Pirko 
16475c72299fSAmritha Nambiar static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key,
16485c72299fSAmritha Nambiar 				  struct fl_flow_key *mask)
16495c72299fSAmritha Nambiar {
16505c72299fSAmritha Nambiar 	if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN,
16515c72299fSAmritha Nambiar 			    &mask->tp_min.dst, TCA_FLOWER_UNSPEC,
16525c72299fSAmritha Nambiar 			    sizeof(key->tp_min.dst)) ||
16535c72299fSAmritha Nambiar 	    fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX,
16545c72299fSAmritha Nambiar 			    &mask->tp_max.dst, TCA_FLOWER_UNSPEC,
16555c72299fSAmritha Nambiar 			    sizeof(key->tp_max.dst)) ||
16565c72299fSAmritha Nambiar 	    fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN,
16575c72299fSAmritha Nambiar 			    &mask->tp_min.src, TCA_FLOWER_UNSPEC,
16585c72299fSAmritha Nambiar 			    sizeof(key->tp_min.src)) ||
16595c72299fSAmritha Nambiar 	    fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX,
16605c72299fSAmritha Nambiar 			    &mask->tp_max.src, TCA_FLOWER_UNSPEC,
16615c72299fSAmritha Nambiar 			    sizeof(key->tp_max.src)))
16625c72299fSAmritha Nambiar 		return -1;
16635c72299fSAmritha Nambiar 
16645c72299fSAmritha Nambiar 	return 0;
16655c72299fSAmritha Nambiar }
16665c72299fSAmritha Nambiar 
1667a577d8f7SBenjamin LaHaise static int fl_dump_key_mpls(struct sk_buff *skb,
1668a577d8f7SBenjamin LaHaise 			    struct flow_dissector_key_mpls *mpls_key,
1669a577d8f7SBenjamin LaHaise 			    struct flow_dissector_key_mpls *mpls_mask)
1670a577d8f7SBenjamin LaHaise {
1671a577d8f7SBenjamin LaHaise 	int err;
1672a577d8f7SBenjamin LaHaise 
1673a577d8f7SBenjamin LaHaise 	if (!memchr_inv(mpls_mask, 0, sizeof(*mpls_mask)))
1674a577d8f7SBenjamin LaHaise 		return 0;
1675a577d8f7SBenjamin LaHaise 	if (mpls_mask->mpls_ttl) {
1676a577d8f7SBenjamin LaHaise 		err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TTL,
1677a577d8f7SBenjamin LaHaise 				 mpls_key->mpls_ttl);
1678a577d8f7SBenjamin LaHaise 		if (err)
1679a577d8f7SBenjamin LaHaise 			return err;
1680a577d8f7SBenjamin LaHaise 	}
1681a577d8f7SBenjamin LaHaise 	if (mpls_mask->mpls_tc) {
1682a577d8f7SBenjamin LaHaise 		err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TC,
1683a577d8f7SBenjamin LaHaise 				 mpls_key->mpls_tc);
1684a577d8f7SBenjamin LaHaise 		if (err)
1685a577d8f7SBenjamin LaHaise 			return err;
1686a577d8f7SBenjamin LaHaise 	}
1687a577d8f7SBenjamin LaHaise 	if (mpls_mask->mpls_label) {
1688a577d8f7SBenjamin LaHaise 		err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_LABEL,
1689a577d8f7SBenjamin LaHaise 				  mpls_key->mpls_label);
1690a577d8f7SBenjamin LaHaise 		if (err)
1691a577d8f7SBenjamin LaHaise 			return err;
1692a577d8f7SBenjamin LaHaise 	}
1693a577d8f7SBenjamin LaHaise 	if (mpls_mask->mpls_bos) {
1694a577d8f7SBenjamin LaHaise 		err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_BOS,
1695a577d8f7SBenjamin LaHaise 				 mpls_key->mpls_bos);
1696a577d8f7SBenjamin LaHaise 		if (err)
1697a577d8f7SBenjamin LaHaise 			return err;
1698a577d8f7SBenjamin LaHaise 	}
1699a577d8f7SBenjamin LaHaise 	return 0;
1700a577d8f7SBenjamin LaHaise }
1701a577d8f7SBenjamin LaHaise 
17020e2c17b6SOr Gerlitz static int fl_dump_key_ip(struct sk_buff *skb, bool encap,
17034d80cc0aSOr Gerlitz 			  struct flow_dissector_key_ip *key,
17044d80cc0aSOr Gerlitz 			  struct flow_dissector_key_ip *mask)
17054d80cc0aSOr Gerlitz {
17060e2c17b6SOr Gerlitz 	int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS;
17070e2c17b6SOr Gerlitz 	int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL;
17080e2c17b6SOr Gerlitz 	int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK;
17090e2c17b6SOr Gerlitz 	int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK;
17100e2c17b6SOr Gerlitz 
17110e2c17b6SOr Gerlitz 	if (fl_dump_key_val(skb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)) ||
17120e2c17b6SOr Gerlitz 	    fl_dump_key_val(skb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl)))
17134d80cc0aSOr Gerlitz 		return -1;
17144d80cc0aSOr Gerlitz 
17154d80cc0aSOr Gerlitz 	return 0;
17164d80cc0aSOr Gerlitz }
17174d80cc0aSOr Gerlitz 
17189399ae9aSHadar Hen Zion static int fl_dump_key_vlan(struct sk_buff *skb,
1719d64efd09SJianbo Liu 			    int vlan_id_key, int vlan_prio_key,
17209399ae9aSHadar Hen Zion 			    struct flow_dissector_key_vlan *vlan_key,
17219399ae9aSHadar Hen Zion 			    struct flow_dissector_key_vlan *vlan_mask)
17229399ae9aSHadar Hen Zion {
17239399ae9aSHadar Hen Zion 	int err;
17249399ae9aSHadar Hen Zion 
17259399ae9aSHadar Hen Zion 	if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask)))
17269399ae9aSHadar Hen Zion 		return 0;
17279399ae9aSHadar Hen Zion 	if (vlan_mask->vlan_id) {
1728d64efd09SJianbo Liu 		err = nla_put_u16(skb, vlan_id_key,
17299399ae9aSHadar Hen Zion 				  vlan_key->vlan_id);
17309399ae9aSHadar Hen Zion 		if (err)
17319399ae9aSHadar Hen Zion 			return err;
17329399ae9aSHadar Hen Zion 	}
17339399ae9aSHadar Hen Zion 	if (vlan_mask->vlan_priority) {
1734d64efd09SJianbo Liu 		err = nla_put_u8(skb, vlan_prio_key,
17359399ae9aSHadar Hen Zion 				 vlan_key->vlan_priority);
17369399ae9aSHadar Hen Zion 		if (err)
17379399ae9aSHadar Hen Zion 			return err;
17389399ae9aSHadar Hen Zion 	}
17399399ae9aSHadar Hen Zion 	return 0;
17409399ae9aSHadar Hen Zion }
17419399ae9aSHadar Hen Zion 
1742faa3ffceSOr Gerlitz static void fl_get_key_flag(u32 dissector_key, u32 dissector_mask,
1743faa3ffceSOr Gerlitz 			    u32 *flower_key, u32 *flower_mask,
1744faa3ffceSOr Gerlitz 			    u32 flower_flag_bit, u32 dissector_flag_bit)
1745faa3ffceSOr Gerlitz {
1746faa3ffceSOr Gerlitz 	if (dissector_mask & dissector_flag_bit) {
1747faa3ffceSOr Gerlitz 		*flower_mask |= flower_flag_bit;
1748faa3ffceSOr Gerlitz 		if (dissector_key & dissector_flag_bit)
1749faa3ffceSOr Gerlitz 			*flower_key |= flower_flag_bit;
1750faa3ffceSOr Gerlitz 	}
1751faa3ffceSOr Gerlitz }
1752faa3ffceSOr Gerlitz 
1753faa3ffceSOr Gerlitz static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask)
1754faa3ffceSOr Gerlitz {
1755faa3ffceSOr Gerlitz 	u32 key, mask;
1756faa3ffceSOr Gerlitz 	__be32 _key, _mask;
1757faa3ffceSOr Gerlitz 	int err;
1758faa3ffceSOr Gerlitz 
1759faa3ffceSOr Gerlitz 	if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask)))
1760faa3ffceSOr Gerlitz 		return 0;
1761faa3ffceSOr Gerlitz 
1762faa3ffceSOr Gerlitz 	key = 0;
1763faa3ffceSOr Gerlitz 	mask = 0;
1764faa3ffceSOr Gerlitz 
1765faa3ffceSOr Gerlitz 	fl_get_key_flag(flags_key, flags_mask, &key, &mask,
1766faa3ffceSOr Gerlitz 			TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT);
1767459d153dSPieter Jansen van Vuuren 	fl_get_key_flag(flags_key, flags_mask, &key, &mask,
1768459d153dSPieter Jansen van Vuuren 			TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST,
1769459d153dSPieter Jansen van Vuuren 			FLOW_DIS_FIRST_FRAG);
1770faa3ffceSOr Gerlitz 
1771faa3ffceSOr Gerlitz 	_key = cpu_to_be32(key);
1772faa3ffceSOr Gerlitz 	_mask = cpu_to_be32(mask);
1773faa3ffceSOr Gerlitz 
1774faa3ffceSOr Gerlitz 	err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key);
1775faa3ffceSOr Gerlitz 	if (err)
1776faa3ffceSOr Gerlitz 		return err;
1777faa3ffceSOr Gerlitz 
1778faa3ffceSOr Gerlitz 	return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask);
1779faa3ffceSOr Gerlitz }
1780faa3ffceSOr Gerlitz 
17810a6e7778SPieter Jansen van Vuuren static int fl_dump_key_geneve_opt(struct sk_buff *skb,
17820a6e7778SPieter Jansen van Vuuren 				  struct flow_dissector_key_enc_opts *enc_opts)
17830a6e7778SPieter Jansen van Vuuren {
17840a6e7778SPieter Jansen van Vuuren 	struct geneve_opt *opt;
17850a6e7778SPieter Jansen van Vuuren 	struct nlattr *nest;
17860a6e7778SPieter Jansen van Vuuren 	int opt_off = 0;
17870a6e7778SPieter Jansen van Vuuren 
17880a6e7778SPieter Jansen van Vuuren 	nest = nla_nest_start(skb, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
17890a6e7778SPieter Jansen van Vuuren 	if (!nest)
17900a6e7778SPieter Jansen van Vuuren 		goto nla_put_failure;
17910a6e7778SPieter Jansen van Vuuren 
17920a6e7778SPieter Jansen van Vuuren 	while (enc_opts->len > opt_off) {
17930a6e7778SPieter Jansen van Vuuren 		opt = (struct geneve_opt *)&enc_opts->data[opt_off];
17940a6e7778SPieter Jansen van Vuuren 
17950a6e7778SPieter Jansen van Vuuren 		if (nla_put_be16(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,
17960a6e7778SPieter Jansen van Vuuren 				 opt->opt_class))
17970a6e7778SPieter Jansen van Vuuren 			goto nla_put_failure;
17980a6e7778SPieter Jansen van Vuuren 		if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE,
17990a6e7778SPieter Jansen van Vuuren 			       opt->type))
18000a6e7778SPieter Jansen van Vuuren 			goto nla_put_failure;
18010a6e7778SPieter Jansen van Vuuren 		if (nla_put(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA,
18020a6e7778SPieter Jansen van Vuuren 			    opt->length * 4, opt->opt_data))
18030a6e7778SPieter Jansen van Vuuren 			goto nla_put_failure;
18040a6e7778SPieter Jansen van Vuuren 
18050a6e7778SPieter Jansen van Vuuren 		opt_off += sizeof(struct geneve_opt) + opt->length * 4;
18060a6e7778SPieter Jansen van Vuuren 	}
18070a6e7778SPieter Jansen van Vuuren 	nla_nest_end(skb, nest);
18080a6e7778SPieter Jansen van Vuuren 	return 0;
18090a6e7778SPieter Jansen van Vuuren 
18100a6e7778SPieter Jansen van Vuuren nla_put_failure:
18110a6e7778SPieter Jansen van Vuuren 	nla_nest_cancel(skb, nest);
18120a6e7778SPieter Jansen van Vuuren 	return -EMSGSIZE;
18130a6e7778SPieter Jansen van Vuuren }
18140a6e7778SPieter Jansen van Vuuren 
18150a6e7778SPieter Jansen van Vuuren static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type,
18160a6e7778SPieter Jansen van Vuuren 			       struct flow_dissector_key_enc_opts *enc_opts)
18170a6e7778SPieter Jansen van Vuuren {
18180a6e7778SPieter Jansen van Vuuren 	struct nlattr *nest;
18190a6e7778SPieter Jansen van Vuuren 	int err;
18200a6e7778SPieter Jansen van Vuuren 
18210a6e7778SPieter Jansen van Vuuren 	if (!enc_opts->len)
18220a6e7778SPieter Jansen van Vuuren 		return 0;
18230a6e7778SPieter Jansen van Vuuren 
18240a6e7778SPieter Jansen van Vuuren 	nest = nla_nest_start(skb, enc_opt_type);
18250a6e7778SPieter Jansen van Vuuren 	if (!nest)
18260a6e7778SPieter Jansen van Vuuren 		goto nla_put_failure;
18270a6e7778SPieter Jansen van Vuuren 
18280a6e7778SPieter Jansen van Vuuren 	switch (enc_opts->dst_opt_type) {
18290a6e7778SPieter Jansen van Vuuren 	case TUNNEL_GENEVE_OPT:
18300a6e7778SPieter Jansen van Vuuren 		err = fl_dump_key_geneve_opt(skb, enc_opts);
18310a6e7778SPieter Jansen van Vuuren 		if (err)
18320a6e7778SPieter Jansen van Vuuren 			goto nla_put_failure;
18330a6e7778SPieter Jansen van Vuuren 		break;
18340a6e7778SPieter Jansen van Vuuren 	default:
18350a6e7778SPieter Jansen van Vuuren 		goto nla_put_failure;
18360a6e7778SPieter Jansen van Vuuren 	}
18370a6e7778SPieter Jansen van Vuuren 	nla_nest_end(skb, nest);
18380a6e7778SPieter Jansen van Vuuren 	return 0;
18390a6e7778SPieter Jansen van Vuuren 
18400a6e7778SPieter Jansen van Vuuren nla_put_failure:
18410a6e7778SPieter Jansen van Vuuren 	nla_nest_cancel(skb, nest);
18420a6e7778SPieter Jansen van Vuuren 	return -EMSGSIZE;
18430a6e7778SPieter Jansen van Vuuren }
18440a6e7778SPieter Jansen van Vuuren 
18450a6e7778SPieter Jansen van Vuuren static int fl_dump_key_enc_opt(struct sk_buff *skb,
18460a6e7778SPieter Jansen van Vuuren 			       struct flow_dissector_key_enc_opts *key_opts,
18470a6e7778SPieter Jansen van Vuuren 			       struct flow_dissector_key_enc_opts *msk_opts)
18480a6e7778SPieter Jansen van Vuuren {
18490a6e7778SPieter Jansen van Vuuren 	int err;
18500a6e7778SPieter Jansen van Vuuren 
18510a6e7778SPieter Jansen van Vuuren 	err = fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS, key_opts);
18520a6e7778SPieter Jansen van Vuuren 	if (err)
18530a6e7778SPieter Jansen van Vuuren 		return err;
18540a6e7778SPieter Jansen van Vuuren 
18550a6e7778SPieter Jansen van Vuuren 	return fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS_MASK, msk_opts);
18560a6e7778SPieter Jansen van Vuuren }
18570a6e7778SPieter Jansen van Vuuren 
1858f5749081SJiri Pirko static int fl_dump_key(struct sk_buff *skb, struct net *net,
1859f5749081SJiri Pirko 		       struct fl_flow_key *key, struct fl_flow_key *mask)
186077b9900eSJiri Pirko {
186177b9900eSJiri Pirko 	if (mask->indev_ifindex) {
186277b9900eSJiri Pirko 		struct net_device *dev;
186377b9900eSJiri Pirko 
186477b9900eSJiri Pirko 		dev = __dev_get_by_index(net, key->indev_ifindex);
186577b9900eSJiri Pirko 		if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name))
186677b9900eSJiri Pirko 			goto nla_put_failure;
186777b9900eSJiri Pirko 	}
186877b9900eSJiri Pirko 
186977b9900eSJiri Pirko 	if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST,
187077b9900eSJiri Pirko 			    mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK,
187177b9900eSJiri Pirko 			    sizeof(key->eth.dst)) ||
187277b9900eSJiri Pirko 	    fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC,
187377b9900eSJiri Pirko 			    mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK,
187477b9900eSJiri Pirko 			    sizeof(key->eth.src)) ||
187577b9900eSJiri Pirko 	    fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE,
187677b9900eSJiri Pirko 			    &mask->basic.n_proto, TCA_FLOWER_UNSPEC,
187777b9900eSJiri Pirko 			    sizeof(key->basic.n_proto)))
187877b9900eSJiri Pirko 		goto nla_put_failure;
18799399ae9aSHadar Hen Zion 
1880a577d8f7SBenjamin LaHaise 	if (fl_dump_key_mpls(skb, &key->mpls, &mask->mpls))
1881a577d8f7SBenjamin LaHaise 		goto nla_put_failure;
1882a577d8f7SBenjamin LaHaise 
1883d64efd09SJianbo Liu 	if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_VLAN_ID,
1884d64efd09SJianbo Liu 			     TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, &mask->vlan))
18859399ae9aSHadar Hen Zion 		goto nla_put_failure;
18869399ae9aSHadar Hen Zion 
1887d64efd09SJianbo Liu 	if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_CVLAN_ID,
1888d64efd09SJianbo Liu 			     TCA_FLOWER_KEY_CVLAN_PRIO,
1889d64efd09SJianbo Liu 			     &key->cvlan, &mask->cvlan) ||
1890d64efd09SJianbo Liu 	    (mask->cvlan.vlan_tpid &&
1891158abbf1SJianbo Liu 	     nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
1892d64efd09SJianbo Liu 			  key->cvlan.vlan_tpid)))
1893d3069512SJianbo Liu 		goto nla_put_failure;
1894d3069512SJianbo Liu 
18955e9a0fe4SJianbo Liu 	if (mask->basic.n_proto) {
1896d64efd09SJianbo Liu 		if (mask->cvlan.vlan_tpid) {
1897d64efd09SJianbo Liu 			if (nla_put_be16(skb, TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
1898d64efd09SJianbo Liu 					 key->basic.n_proto))
1899d64efd09SJianbo Liu 				goto nla_put_failure;
1900d64efd09SJianbo Liu 		} else if (mask->vlan.vlan_tpid) {
1901d64efd09SJianbo Liu 			if (nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE,
1902d64efd09SJianbo Liu 					 key->basic.n_proto))
1903d64efd09SJianbo Liu 				goto nla_put_failure;
1904d64efd09SJianbo Liu 		}
19055e9a0fe4SJianbo Liu 	}
1906d64efd09SJianbo Liu 
190777b9900eSJiri Pirko 	if ((key->basic.n_proto == htons(ETH_P_IP) ||
190877b9900eSJiri Pirko 	     key->basic.n_proto == htons(ETH_P_IPV6)) &&
19094d80cc0aSOr Gerlitz 	    (fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO,
191077b9900eSJiri Pirko 			    &mask->basic.ip_proto, TCA_FLOWER_UNSPEC,
19114d80cc0aSOr Gerlitz 			    sizeof(key->basic.ip_proto)) ||
19120e2c17b6SOr Gerlitz 	    fl_dump_key_ip(skb, false, &key->ip, &mask->ip)))
191377b9900eSJiri Pirko 		goto nla_put_failure;
191477b9900eSJiri Pirko 
1915c3f83241STom Herbert 	if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS &&
191677b9900eSJiri Pirko 	    (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC,
191777b9900eSJiri Pirko 			     &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK,
191877b9900eSJiri Pirko 			     sizeof(key->ipv4.src)) ||
191977b9900eSJiri Pirko 	     fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST,
192077b9900eSJiri Pirko 			     &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK,
192177b9900eSJiri Pirko 			     sizeof(key->ipv4.dst))))
192277b9900eSJiri Pirko 		goto nla_put_failure;
1923c3f83241STom Herbert 	else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS &&
192477b9900eSJiri Pirko 		 (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC,
192577b9900eSJiri Pirko 				  &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK,
192677b9900eSJiri Pirko 				  sizeof(key->ipv6.src)) ||
192777b9900eSJiri Pirko 		  fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST,
192877b9900eSJiri Pirko 				  &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK,
192977b9900eSJiri Pirko 				  sizeof(key->ipv6.dst))))
193077b9900eSJiri Pirko 		goto nla_put_failure;
193177b9900eSJiri Pirko 
193277b9900eSJiri Pirko 	if (key->basic.ip_proto == IPPROTO_TCP &&
193377b9900eSJiri Pirko 	    (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC,
1934aa72d708SOr Gerlitz 			     &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK,
193577b9900eSJiri Pirko 			     sizeof(key->tp.src)) ||
193677b9900eSJiri Pirko 	     fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST,
1937aa72d708SOr Gerlitz 			     &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK,
1938fdfc7dd6SJiri Pirko 			     sizeof(key->tp.dst)) ||
1939fdfc7dd6SJiri Pirko 	     fl_dump_key_val(skb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS,
1940fdfc7dd6SJiri Pirko 			     &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK,
1941fdfc7dd6SJiri Pirko 			     sizeof(key->tcp.flags))))
194277b9900eSJiri Pirko 		goto nla_put_failure;
194377b9900eSJiri Pirko 	else if (key->basic.ip_proto == IPPROTO_UDP &&
194477b9900eSJiri Pirko 		 (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC,
1945aa72d708SOr Gerlitz 				  &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK,
194677b9900eSJiri Pirko 				  sizeof(key->tp.src)) ||
194777b9900eSJiri Pirko 		  fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST,
1948aa72d708SOr Gerlitz 				  &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK,
194977b9900eSJiri Pirko 				  sizeof(key->tp.dst))))
195077b9900eSJiri Pirko 		goto nla_put_failure;
19515976c5f4SSimon Horman 	else if (key->basic.ip_proto == IPPROTO_SCTP &&
19525976c5f4SSimon Horman 		 (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC,
19535976c5f4SSimon Horman 				  &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK,
19545976c5f4SSimon Horman 				  sizeof(key->tp.src)) ||
19555976c5f4SSimon Horman 		  fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST,
19565976c5f4SSimon Horman 				  &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK,
19575976c5f4SSimon Horman 				  sizeof(key->tp.dst))))
19585976c5f4SSimon Horman 		goto nla_put_failure;
19597b684884SSimon Horman 	else if (key->basic.n_proto == htons(ETH_P_IP) &&
19607b684884SSimon Horman 		 key->basic.ip_proto == IPPROTO_ICMP &&
19617b684884SSimon Horman 		 (fl_dump_key_val(skb, &key->icmp.type,
19627b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type,
19637b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV4_TYPE_MASK,
19647b684884SSimon Horman 				  sizeof(key->icmp.type)) ||
19657b684884SSimon Horman 		  fl_dump_key_val(skb, &key->icmp.code,
19667b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code,
19677b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV4_CODE_MASK,
19687b684884SSimon Horman 				  sizeof(key->icmp.code))))
19697b684884SSimon Horman 		goto nla_put_failure;
19707b684884SSimon Horman 	else if (key->basic.n_proto == htons(ETH_P_IPV6) &&
19717b684884SSimon Horman 		 key->basic.ip_proto == IPPROTO_ICMPV6 &&
19727b684884SSimon Horman 		 (fl_dump_key_val(skb, &key->icmp.type,
19737b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type,
19747b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV6_TYPE_MASK,
19757b684884SSimon Horman 				  sizeof(key->icmp.type)) ||
19767b684884SSimon Horman 		  fl_dump_key_val(skb, &key->icmp.code,
19777b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code,
19787b684884SSimon Horman 				  TCA_FLOWER_KEY_ICMPV6_CODE_MASK,
19797b684884SSimon Horman 				  sizeof(key->icmp.code))))
19807b684884SSimon Horman 		goto nla_put_failure;
198199d31326SSimon Horman 	else if ((key->basic.n_proto == htons(ETH_P_ARP) ||
198299d31326SSimon Horman 		  key->basic.n_proto == htons(ETH_P_RARP)) &&
198399d31326SSimon Horman 		 (fl_dump_key_val(skb, &key->arp.sip,
198499d31326SSimon Horman 				  TCA_FLOWER_KEY_ARP_SIP, &mask->arp.sip,
198599d31326SSimon Horman 				  TCA_FLOWER_KEY_ARP_SIP_MASK,
198699d31326SSimon Horman 				  sizeof(key->arp.sip)) ||
198799d31326SSimon Horman 		  fl_dump_key_val(skb, &key->arp.tip,
198899d31326SSimon Horman 				  TCA_FLOWER_KEY_ARP_TIP, &mask->arp.tip,
198999d31326SSimon Horman 				  TCA_FLOWER_KEY_ARP_TIP_MASK,
199099d31326SSimon Horman 				  sizeof(key->arp.tip)) ||
199199d31326SSimon Horman 		  fl_dump_key_val(skb, &key->arp.op,
199299d31326SSimon Horman 				  TCA_FLOWER_KEY_ARP_OP, &mask->arp.op,
199399d31326SSimon Horman 				  TCA_FLOWER_KEY_ARP_OP_MASK,
199499d31326SSimon Horman 				  sizeof(key->arp.op)) ||
199599d31326SSimon Horman 		  fl_dump_key_val(skb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA,
199699d31326SSimon Horman 				  mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK,
199799d31326SSimon Horman 				  sizeof(key->arp.sha)) ||
199899d31326SSimon Horman 		  fl_dump_key_val(skb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA,
199999d31326SSimon Horman 				  mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK,
200099d31326SSimon Horman 				  sizeof(key->arp.tha))))
200199d31326SSimon Horman 		goto nla_put_failure;
200277b9900eSJiri Pirko 
20035c72299fSAmritha Nambiar 	if ((key->basic.ip_proto == IPPROTO_TCP ||
20045c72299fSAmritha Nambiar 	     key->basic.ip_proto == IPPROTO_UDP ||
20055c72299fSAmritha Nambiar 	     key->basic.ip_proto == IPPROTO_SCTP) &&
20065c72299fSAmritha Nambiar 	     fl_dump_key_port_range(skb, key, mask))
20075c72299fSAmritha Nambiar 		goto nla_put_failure;
20085c72299fSAmritha Nambiar 
2009bc3103f1SAmir Vadai 	if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS &&
2010bc3103f1SAmir Vadai 	    (fl_dump_key_val(skb, &key->enc_ipv4.src,
2011bc3103f1SAmir Vadai 			    TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src,
2012bc3103f1SAmir Vadai 			    TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK,
2013bc3103f1SAmir Vadai 			    sizeof(key->enc_ipv4.src)) ||
2014bc3103f1SAmir Vadai 	     fl_dump_key_val(skb, &key->enc_ipv4.dst,
2015bc3103f1SAmir Vadai 			     TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst,
2016bc3103f1SAmir Vadai 			     TCA_FLOWER_KEY_ENC_IPV4_DST_MASK,
2017bc3103f1SAmir Vadai 			     sizeof(key->enc_ipv4.dst))))
2018bc3103f1SAmir Vadai 		goto nla_put_failure;
2019bc3103f1SAmir Vadai 	else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS &&
2020bc3103f1SAmir Vadai 		 (fl_dump_key_val(skb, &key->enc_ipv6.src,
2021bc3103f1SAmir Vadai 			    TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src,
2022bc3103f1SAmir Vadai 			    TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK,
2023bc3103f1SAmir Vadai 			    sizeof(key->enc_ipv6.src)) ||
2024bc3103f1SAmir Vadai 		 fl_dump_key_val(skb, &key->enc_ipv6.dst,
2025bc3103f1SAmir Vadai 				 TCA_FLOWER_KEY_ENC_IPV6_DST,
2026bc3103f1SAmir Vadai 				 &mask->enc_ipv6.dst,
2027bc3103f1SAmir Vadai 				 TCA_FLOWER_KEY_ENC_IPV6_DST_MASK,
2028bc3103f1SAmir Vadai 			    sizeof(key->enc_ipv6.dst))))
2029bc3103f1SAmir Vadai 		goto nla_put_failure;
2030bc3103f1SAmir Vadai 
2031bc3103f1SAmir Vadai 	if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID,
2032eb523f42SHadar Hen Zion 			    &mask->enc_key_id, TCA_FLOWER_UNSPEC,
2033f4d997fdSHadar Hen Zion 			    sizeof(key->enc_key_id)) ||
2034f4d997fdSHadar Hen Zion 	    fl_dump_key_val(skb, &key->enc_tp.src,
2035f4d997fdSHadar Hen Zion 			    TCA_FLOWER_KEY_ENC_UDP_SRC_PORT,
2036f4d997fdSHadar Hen Zion 			    &mask->enc_tp.src,
2037f4d997fdSHadar Hen Zion 			    TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK,
2038f4d997fdSHadar Hen Zion 			    sizeof(key->enc_tp.src)) ||
2039f4d997fdSHadar Hen Zion 	    fl_dump_key_val(skb, &key->enc_tp.dst,
2040f4d997fdSHadar Hen Zion 			    TCA_FLOWER_KEY_ENC_UDP_DST_PORT,
2041f4d997fdSHadar Hen Zion 			    &mask->enc_tp.dst,
2042f4d997fdSHadar Hen Zion 			    TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK,
20430e2c17b6SOr Gerlitz 			    sizeof(key->enc_tp.dst)) ||
20440a6e7778SPieter Jansen van Vuuren 	    fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip) ||
20450a6e7778SPieter Jansen van Vuuren 	    fl_dump_key_enc_opt(skb, &key->enc_opts, &mask->enc_opts))
2046bc3103f1SAmir Vadai 		goto nla_put_failure;
2047bc3103f1SAmir Vadai 
2048faa3ffceSOr Gerlitz 	if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags))
2049faa3ffceSOr Gerlitz 		goto nla_put_failure;
2050faa3ffceSOr Gerlitz 
2051f5749081SJiri Pirko 	return 0;
2052f5749081SJiri Pirko 
2053f5749081SJiri Pirko nla_put_failure:
2054f5749081SJiri Pirko 	return -EMSGSIZE;
2055f5749081SJiri Pirko }
2056f5749081SJiri Pirko 
2057f5749081SJiri Pirko static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh,
205812db03b6SVlad Buslov 		   struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
2059f5749081SJiri Pirko {
2060f5749081SJiri Pirko 	struct cls_fl_filter *f = fh;
2061f5749081SJiri Pirko 	struct nlattr *nest;
2062f5749081SJiri Pirko 	struct fl_flow_key *key, *mask;
2063f5749081SJiri Pirko 
2064f5749081SJiri Pirko 	if (!f)
2065f5749081SJiri Pirko 		return skb->len;
2066f5749081SJiri Pirko 
2067f5749081SJiri Pirko 	t->tcm_handle = f->handle;
2068f5749081SJiri Pirko 
2069f5749081SJiri Pirko 	nest = nla_nest_start(skb, TCA_OPTIONS);
2070f5749081SJiri Pirko 	if (!nest)
2071f5749081SJiri Pirko 		goto nla_put_failure;
2072f5749081SJiri Pirko 
2073f5749081SJiri Pirko 	if (f->res.classid &&
2074f5749081SJiri Pirko 	    nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid))
2075f5749081SJiri Pirko 		goto nla_put_failure;
2076f5749081SJiri Pirko 
2077f5749081SJiri Pirko 	key = &f->key;
2078f5749081SJiri Pirko 	mask = &f->mask->key;
2079f5749081SJiri Pirko 
2080f5749081SJiri Pirko 	if (fl_dump_key(skb, net, key, mask))
2081f5749081SJiri Pirko 		goto nla_put_failure;
2082f5749081SJiri Pirko 
2083f5749081SJiri Pirko 	if (!tc_skip_hw(f->flags))
2084f5749081SJiri Pirko 		fl_hw_update_stats(tp, f);
2085f5749081SJiri Pirko 
2086749e6720SOr Gerlitz 	if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags))
2087749e6720SOr Gerlitz 		goto nla_put_failure;
2088e69985c6SAmir Vadai 
208986c55361SVlad Buslov 	if (nla_put_u32(skb, TCA_FLOWER_IN_HW_COUNT, f->in_hw_count))
209086c55361SVlad Buslov 		goto nla_put_failure;
209186c55361SVlad Buslov 
209277b9900eSJiri Pirko 	if (tcf_exts_dump(skb, &f->exts))
209377b9900eSJiri Pirko 		goto nla_put_failure;
209477b9900eSJiri Pirko 
209577b9900eSJiri Pirko 	nla_nest_end(skb, nest);
209677b9900eSJiri Pirko 
209777b9900eSJiri Pirko 	if (tcf_exts_dump_stats(skb, &f->exts) < 0)
209877b9900eSJiri Pirko 		goto nla_put_failure;
209977b9900eSJiri Pirko 
210077b9900eSJiri Pirko 	return skb->len;
210177b9900eSJiri Pirko 
210277b9900eSJiri Pirko nla_put_failure:
210377b9900eSJiri Pirko 	nla_nest_cancel(skb, nest);
210477b9900eSJiri Pirko 	return -1;
210577b9900eSJiri Pirko }
210677b9900eSJiri Pirko 
2107b95ec7ebSJiri Pirko static int fl_tmplt_dump(struct sk_buff *skb, struct net *net, void *tmplt_priv)
2108b95ec7ebSJiri Pirko {
2109b95ec7ebSJiri Pirko 	struct fl_flow_tmplt *tmplt = tmplt_priv;
2110b95ec7ebSJiri Pirko 	struct fl_flow_key *key, *mask;
2111b95ec7ebSJiri Pirko 	struct nlattr *nest;
2112b95ec7ebSJiri Pirko 
2113b95ec7ebSJiri Pirko 	nest = nla_nest_start(skb, TCA_OPTIONS);
2114b95ec7ebSJiri Pirko 	if (!nest)
2115b95ec7ebSJiri Pirko 		goto nla_put_failure;
2116b95ec7ebSJiri Pirko 
2117b95ec7ebSJiri Pirko 	key = &tmplt->dummy_key;
2118b95ec7ebSJiri Pirko 	mask = &tmplt->mask;
2119b95ec7ebSJiri Pirko 
2120b95ec7ebSJiri Pirko 	if (fl_dump_key(skb, net, key, mask))
2121b95ec7ebSJiri Pirko 		goto nla_put_failure;
2122b95ec7ebSJiri Pirko 
2123b95ec7ebSJiri Pirko 	nla_nest_end(skb, nest);
2124b95ec7ebSJiri Pirko 
2125b95ec7ebSJiri Pirko 	return skb->len;
2126b95ec7ebSJiri Pirko 
2127b95ec7ebSJiri Pirko nla_put_failure:
2128b95ec7ebSJiri Pirko 	nla_nest_cancel(skb, nest);
2129b95ec7ebSJiri Pirko 	return -EMSGSIZE;
2130b95ec7ebSJiri Pirko }
2131b95ec7ebSJiri Pirko 
213207d79fc7SCong Wang static void fl_bind_class(void *fh, u32 classid, unsigned long cl)
213307d79fc7SCong Wang {
213407d79fc7SCong Wang 	struct cls_fl_filter *f = fh;
213507d79fc7SCong Wang 
213607d79fc7SCong Wang 	if (f && f->res.classid == classid)
213707d79fc7SCong Wang 		f->res.class = cl;
213807d79fc7SCong Wang }
213907d79fc7SCong Wang 
214077b9900eSJiri Pirko static struct tcf_proto_ops cls_fl_ops __read_mostly = {
214177b9900eSJiri Pirko 	.kind		= "flower",
214277b9900eSJiri Pirko 	.classify	= fl_classify,
214377b9900eSJiri Pirko 	.init		= fl_init,
214477b9900eSJiri Pirko 	.destroy	= fl_destroy,
214577b9900eSJiri Pirko 	.get		= fl_get,
214677b9900eSJiri Pirko 	.change		= fl_change,
214777b9900eSJiri Pirko 	.delete		= fl_delete,
214877b9900eSJiri Pirko 	.walk		= fl_walk,
214931533cbaSJohn Hurley 	.reoffload	= fl_reoffload,
215077b9900eSJiri Pirko 	.dump		= fl_dump,
215107d79fc7SCong Wang 	.bind_class	= fl_bind_class,
2152b95ec7ebSJiri Pirko 	.tmplt_create	= fl_tmplt_create,
2153b95ec7ebSJiri Pirko 	.tmplt_destroy	= fl_tmplt_destroy,
2154b95ec7ebSJiri Pirko 	.tmplt_dump	= fl_tmplt_dump,
215577b9900eSJiri Pirko 	.owner		= THIS_MODULE,
215677b9900eSJiri Pirko };
215777b9900eSJiri Pirko 
215877b9900eSJiri Pirko static int __init cls_fl_init(void)
215977b9900eSJiri Pirko {
216077b9900eSJiri Pirko 	return register_tcf_proto_ops(&cls_fl_ops);
216177b9900eSJiri Pirko }
216277b9900eSJiri Pirko 
216377b9900eSJiri Pirko static void __exit cls_fl_exit(void)
216477b9900eSJiri Pirko {
216577b9900eSJiri Pirko 	unregister_tcf_proto_ops(&cls_fl_ops);
216677b9900eSJiri Pirko }
216777b9900eSJiri Pirko 
216877b9900eSJiri Pirko module_init(cls_fl_init);
216977b9900eSJiri Pirko module_exit(cls_fl_exit);
217077b9900eSJiri Pirko 
217177b9900eSJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
217277b9900eSJiri Pirko MODULE_DESCRIPTION("Flower classifier");
217377b9900eSJiri Pirko MODULE_LICENSE("GPL v2");
2174