12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 277b9900eSJiri Pirko /* 377b9900eSJiri Pirko * net/sched/cls_flower.c Flower classifier 477b9900eSJiri Pirko * 577b9900eSJiri Pirko * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us> 677b9900eSJiri Pirko */ 777b9900eSJiri Pirko 877b9900eSJiri Pirko #include <linux/kernel.h> 977b9900eSJiri Pirko #include <linux/init.h> 1077b9900eSJiri Pirko #include <linux/module.h> 1177b9900eSJiri Pirko #include <linux/rhashtable.h> 12d9363774SDaniel Borkmann #include <linux/workqueue.h> 1306177558SVlad Buslov #include <linux/refcount.h> 1477b9900eSJiri Pirko 1577b9900eSJiri Pirko #include <linux/if_ether.h> 1677b9900eSJiri Pirko #include <linux/in6.h> 1777b9900eSJiri Pirko #include <linux/ip.h> 18a577d8f7SBenjamin LaHaise #include <linux/mpls.h> 1977b9900eSJiri Pirko 2077b9900eSJiri Pirko #include <net/sch_generic.h> 2177b9900eSJiri Pirko #include <net/pkt_cls.h> 2277b9900eSJiri Pirko #include <net/ip.h> 2377b9900eSJiri Pirko #include <net/flow_dissector.h> 240a6e7778SPieter Jansen van Vuuren #include <net/geneve.h> 2577b9900eSJiri Pirko 26bc3103f1SAmir Vadai #include <net/dst.h> 27bc3103f1SAmir Vadai #include <net/dst_metadata.h> 28bc3103f1SAmir Vadai 29*e0ace68aSPaul Blakey #include <uapi/linux/netfilter/nf_conntrack_common.h> 30*e0ace68aSPaul Blakey 3177b9900eSJiri Pirko struct fl_flow_key { 328212ed77SJiri Pirko struct flow_dissector_key_meta meta; 3342aecaa9STom Herbert struct flow_dissector_key_control control; 34bc3103f1SAmir Vadai struct flow_dissector_key_control enc_control; 3577b9900eSJiri Pirko struct flow_dissector_key_basic basic; 3677b9900eSJiri Pirko struct flow_dissector_key_eth_addrs eth; 379399ae9aSHadar Hen Zion struct flow_dissector_key_vlan vlan; 38d64efd09SJianbo Liu struct flow_dissector_key_vlan cvlan; 3977b9900eSJiri Pirko union { 40c3f83241STom Herbert struct flow_dissector_key_ipv4_addrs ipv4; 4177b9900eSJiri Pirko struct flow_dissector_key_ipv6_addrs ipv6; 4277b9900eSJiri Pirko }; 4377b9900eSJiri Pirko struct flow_dissector_key_ports tp; 447b684884SSimon Horman struct flow_dissector_key_icmp icmp; 4599d31326SSimon Horman struct flow_dissector_key_arp arp; 46bc3103f1SAmir Vadai struct flow_dissector_key_keyid enc_key_id; 47bc3103f1SAmir Vadai union { 48bc3103f1SAmir Vadai struct flow_dissector_key_ipv4_addrs enc_ipv4; 49bc3103f1SAmir Vadai struct flow_dissector_key_ipv6_addrs enc_ipv6; 50bc3103f1SAmir Vadai }; 51f4d997fdSHadar Hen Zion struct flow_dissector_key_ports enc_tp; 52a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls mpls; 53fdfc7dd6SJiri Pirko struct flow_dissector_key_tcp tcp; 544d80cc0aSOr Gerlitz struct flow_dissector_key_ip ip; 550e2c17b6SOr Gerlitz struct flow_dissector_key_ip enc_ip; 560a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts enc_opts; 575c72299fSAmritha Nambiar struct flow_dissector_key_ports tp_min; 585c72299fSAmritha Nambiar struct flow_dissector_key_ports tp_max; 59*e0ace68aSPaul Blakey struct flow_dissector_key_ct ct; 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; 78f48ef4d5SVlad Buslov refcount_t refcnt; 7977b9900eSJiri Pirko }; 8077b9900eSJiri Pirko 81b95ec7ebSJiri Pirko struct fl_flow_tmplt { 82b95ec7ebSJiri Pirko struct fl_flow_key dummy_key; 83b95ec7ebSJiri Pirko struct fl_flow_key mask; 84b95ec7ebSJiri Pirko struct flow_dissector dissector; 85b95ec7ebSJiri Pirko struct tcf_chain *chain; 86b95ec7ebSJiri Pirko }; 87b95ec7ebSJiri Pirko 8877b9900eSJiri Pirko struct cls_fl_head { 8977b9900eSJiri Pirko struct rhashtable ht; 90259e60f9SVlad Buslov spinlock_t masks_lock; /* Protect masks list */ 9105cd271fSPaul Blakey struct list_head masks; 92c049d56eSVlad Buslov struct list_head hw_filters; 93aaa908ffSCong Wang struct rcu_work rwork; 94c15ab236SChris Mi struct idr handle_idr; 95d9363774SDaniel Borkmann }; 9677b9900eSJiri Pirko 9777b9900eSJiri Pirko struct cls_fl_filter { 9805cd271fSPaul Blakey struct fl_flow_mask *mask; 9977b9900eSJiri Pirko struct rhash_head ht_node; 10077b9900eSJiri Pirko struct fl_flow_key mkey; 10177b9900eSJiri Pirko struct tcf_exts exts; 10277b9900eSJiri Pirko struct tcf_result res; 10377b9900eSJiri Pirko struct fl_flow_key key; 10477b9900eSJiri Pirko struct list_head list; 105c049d56eSVlad Buslov struct list_head hw_list; 10677b9900eSJiri Pirko u32 handle; 107e69985c6SAmir Vadai u32 flags; 10886c55361SVlad Buslov u32 in_hw_count; 109aaa908ffSCong Wang struct rcu_work rwork; 1107091d8c7SHadar Hen Zion struct net_device *hw_dev; 11106177558SVlad Buslov /* Flower classifier is unlocked, which means that its reference counter 11206177558SVlad Buslov * can be changed concurrently without any kind of external 11306177558SVlad Buslov * synchronization. Use atomic reference counter to be concurrency-safe. 11406177558SVlad Buslov */ 11506177558SVlad Buslov refcount_t refcnt; 116b2552b8cSVlad Buslov bool deleted; 11777b9900eSJiri Pirko }; 11877b9900eSJiri Pirko 11905cd271fSPaul Blakey static const struct rhashtable_params mask_ht_params = { 12005cd271fSPaul Blakey .key_offset = offsetof(struct fl_flow_mask, key), 12105cd271fSPaul Blakey .key_len = sizeof(struct fl_flow_key), 12205cd271fSPaul Blakey .head_offset = offsetof(struct fl_flow_mask, ht_node), 12305cd271fSPaul Blakey .automatic_shrinking = true, 12405cd271fSPaul Blakey }; 12505cd271fSPaul Blakey 12677b9900eSJiri Pirko static unsigned short int fl_mask_range(const struct fl_flow_mask *mask) 12777b9900eSJiri Pirko { 12877b9900eSJiri Pirko return mask->range.end - mask->range.start; 12977b9900eSJiri Pirko } 13077b9900eSJiri Pirko 13177b9900eSJiri Pirko static void fl_mask_update_range(struct fl_flow_mask *mask) 13277b9900eSJiri Pirko { 13377b9900eSJiri Pirko const u8 *bytes = (const u8 *) &mask->key; 13477b9900eSJiri Pirko size_t size = sizeof(mask->key); 13505cd271fSPaul Blakey size_t i, first = 0, last; 13677b9900eSJiri Pirko 13705cd271fSPaul Blakey for (i = 0; i < size; i++) { 13877b9900eSJiri Pirko if (bytes[i]) { 13977b9900eSJiri Pirko first = i; 14005cd271fSPaul Blakey break; 14105cd271fSPaul Blakey } 14205cd271fSPaul Blakey } 14305cd271fSPaul Blakey last = first; 14405cd271fSPaul Blakey for (i = size - 1; i != first; i--) { 14505cd271fSPaul Blakey if (bytes[i]) { 14677b9900eSJiri Pirko last = i; 14705cd271fSPaul Blakey break; 14877b9900eSJiri Pirko } 14977b9900eSJiri Pirko } 15077b9900eSJiri Pirko mask->range.start = rounddown(first, sizeof(long)); 15177b9900eSJiri Pirko mask->range.end = roundup(last + 1, sizeof(long)); 15277b9900eSJiri Pirko } 15377b9900eSJiri Pirko 15477b9900eSJiri Pirko static void *fl_key_get_start(struct fl_flow_key *key, 15577b9900eSJiri Pirko const struct fl_flow_mask *mask) 15677b9900eSJiri Pirko { 15777b9900eSJiri Pirko return (u8 *) key + mask->range.start; 15877b9900eSJiri Pirko } 15977b9900eSJiri Pirko 16077b9900eSJiri Pirko static void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key, 16177b9900eSJiri Pirko struct fl_flow_mask *mask) 16277b9900eSJiri Pirko { 16377b9900eSJiri Pirko const long *lkey = fl_key_get_start(key, mask); 16477b9900eSJiri Pirko const long *lmask = fl_key_get_start(&mask->key, mask); 16577b9900eSJiri Pirko long *lmkey = fl_key_get_start(mkey, mask); 16677b9900eSJiri Pirko int i; 16777b9900eSJiri Pirko 16877b9900eSJiri Pirko for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) 16977b9900eSJiri Pirko *lmkey++ = *lkey++ & *lmask++; 17077b9900eSJiri Pirko } 17177b9900eSJiri Pirko 172b95ec7ebSJiri Pirko static bool fl_mask_fits_tmplt(struct fl_flow_tmplt *tmplt, 173b95ec7ebSJiri Pirko struct fl_flow_mask *mask) 174b95ec7ebSJiri Pirko { 175b95ec7ebSJiri Pirko const long *lmask = fl_key_get_start(&mask->key, mask); 176b95ec7ebSJiri Pirko const long *ltmplt; 177b95ec7ebSJiri Pirko int i; 178b95ec7ebSJiri Pirko 179b95ec7ebSJiri Pirko if (!tmplt) 180b95ec7ebSJiri Pirko return true; 181b95ec7ebSJiri Pirko ltmplt = fl_key_get_start(&tmplt->mask, mask); 182b95ec7ebSJiri Pirko for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) { 183b95ec7ebSJiri Pirko if (~*ltmplt++ & *lmask++) 184b95ec7ebSJiri Pirko return false; 185b95ec7ebSJiri Pirko } 186b95ec7ebSJiri Pirko return true; 187b95ec7ebSJiri Pirko } 188b95ec7ebSJiri Pirko 18977b9900eSJiri Pirko static void fl_clear_masked_range(struct fl_flow_key *key, 19077b9900eSJiri Pirko struct fl_flow_mask *mask) 19177b9900eSJiri Pirko { 19277b9900eSJiri Pirko memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask)); 19377b9900eSJiri Pirko } 19477b9900eSJiri Pirko 1955c72299fSAmritha Nambiar static bool fl_range_port_dst_cmp(struct cls_fl_filter *filter, 1965c72299fSAmritha Nambiar struct fl_flow_key *key, 1975c72299fSAmritha Nambiar struct fl_flow_key *mkey) 1985c72299fSAmritha Nambiar { 1995c72299fSAmritha Nambiar __be16 min_mask, max_mask, min_val, max_val; 2005c72299fSAmritha Nambiar 2015c72299fSAmritha Nambiar min_mask = htons(filter->mask->key.tp_min.dst); 2025c72299fSAmritha Nambiar max_mask = htons(filter->mask->key.tp_max.dst); 2035c72299fSAmritha Nambiar min_val = htons(filter->key.tp_min.dst); 2045c72299fSAmritha Nambiar max_val = htons(filter->key.tp_max.dst); 2055c72299fSAmritha Nambiar 2065c72299fSAmritha Nambiar if (min_mask && max_mask) { 2075c72299fSAmritha Nambiar if (htons(key->tp.dst) < min_val || 2085c72299fSAmritha Nambiar htons(key->tp.dst) > max_val) 2095c72299fSAmritha Nambiar return false; 2105c72299fSAmritha Nambiar 2115c72299fSAmritha Nambiar /* skb does not have min and max values */ 2125c72299fSAmritha Nambiar mkey->tp_min.dst = filter->mkey.tp_min.dst; 2135c72299fSAmritha Nambiar mkey->tp_max.dst = filter->mkey.tp_max.dst; 2145c72299fSAmritha Nambiar } 2155c72299fSAmritha Nambiar return true; 2165c72299fSAmritha Nambiar } 2175c72299fSAmritha Nambiar 2185c72299fSAmritha Nambiar static bool fl_range_port_src_cmp(struct cls_fl_filter *filter, 2195c72299fSAmritha Nambiar struct fl_flow_key *key, 2205c72299fSAmritha Nambiar struct fl_flow_key *mkey) 2215c72299fSAmritha Nambiar { 2225c72299fSAmritha Nambiar __be16 min_mask, max_mask, min_val, max_val; 2235c72299fSAmritha Nambiar 2245c72299fSAmritha Nambiar min_mask = htons(filter->mask->key.tp_min.src); 2255c72299fSAmritha Nambiar max_mask = htons(filter->mask->key.tp_max.src); 2265c72299fSAmritha Nambiar min_val = htons(filter->key.tp_min.src); 2275c72299fSAmritha Nambiar max_val = htons(filter->key.tp_max.src); 2285c72299fSAmritha Nambiar 2295c72299fSAmritha Nambiar if (min_mask && max_mask) { 2305c72299fSAmritha Nambiar if (htons(key->tp.src) < min_val || 2315c72299fSAmritha Nambiar htons(key->tp.src) > max_val) 2325c72299fSAmritha Nambiar return false; 2335c72299fSAmritha Nambiar 2345c72299fSAmritha Nambiar /* skb does not have min and max values */ 2355c72299fSAmritha Nambiar mkey->tp_min.src = filter->mkey.tp_min.src; 2365c72299fSAmritha Nambiar mkey->tp_max.src = filter->mkey.tp_max.src; 2375c72299fSAmritha Nambiar } 2385c72299fSAmritha Nambiar return true; 2395c72299fSAmritha Nambiar } 2405c72299fSAmritha Nambiar 2415c72299fSAmritha Nambiar static struct cls_fl_filter *__fl_lookup(struct fl_flow_mask *mask, 242a3308d8fSPaul Blakey struct fl_flow_key *mkey) 243a3308d8fSPaul Blakey { 24405cd271fSPaul Blakey return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask), 24505cd271fSPaul Blakey mask->filter_ht_params); 246a3308d8fSPaul Blakey } 247a3308d8fSPaul Blakey 2485c72299fSAmritha Nambiar static struct cls_fl_filter *fl_lookup_range(struct fl_flow_mask *mask, 2495c72299fSAmritha Nambiar struct fl_flow_key *mkey, 2505c72299fSAmritha Nambiar struct fl_flow_key *key) 2515c72299fSAmritha Nambiar { 2525c72299fSAmritha Nambiar struct cls_fl_filter *filter, *f; 2535c72299fSAmritha Nambiar 2545c72299fSAmritha Nambiar list_for_each_entry_rcu(filter, &mask->filters, list) { 2555c72299fSAmritha Nambiar if (!fl_range_port_dst_cmp(filter, key, mkey)) 2565c72299fSAmritha Nambiar continue; 2575c72299fSAmritha Nambiar 2585c72299fSAmritha Nambiar if (!fl_range_port_src_cmp(filter, key, mkey)) 2595c72299fSAmritha Nambiar continue; 2605c72299fSAmritha Nambiar 2615c72299fSAmritha Nambiar f = __fl_lookup(mask, mkey); 2625c72299fSAmritha Nambiar if (f) 2635c72299fSAmritha Nambiar return f; 2645c72299fSAmritha Nambiar } 2655c72299fSAmritha Nambiar return NULL; 2665c72299fSAmritha Nambiar } 2675c72299fSAmritha Nambiar 2685c72299fSAmritha Nambiar static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask, 2695c72299fSAmritha Nambiar struct fl_flow_key *mkey, 2705c72299fSAmritha Nambiar struct fl_flow_key *key) 2715c72299fSAmritha Nambiar { 2725c72299fSAmritha Nambiar if ((mask->flags & TCA_FLOWER_MASK_FLAGS_RANGE)) 2735c72299fSAmritha Nambiar return fl_lookup_range(mask, mkey, key); 2745c72299fSAmritha Nambiar 2755c72299fSAmritha Nambiar return __fl_lookup(mask, mkey); 2765c72299fSAmritha Nambiar } 2775c72299fSAmritha Nambiar 278*e0ace68aSPaul Blakey static u16 fl_ct_info_to_flower_map[] = { 279*e0ace68aSPaul Blakey [IP_CT_ESTABLISHED] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 280*e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED, 281*e0ace68aSPaul Blakey [IP_CT_RELATED] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 282*e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_RELATED, 283*e0ace68aSPaul Blakey [IP_CT_ESTABLISHED_REPLY] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 284*e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED, 285*e0ace68aSPaul Blakey [IP_CT_RELATED_REPLY] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 286*e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_RELATED, 287*e0ace68aSPaul Blakey [IP_CT_NEW] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 288*e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_NEW, 289*e0ace68aSPaul Blakey }; 290*e0ace68aSPaul Blakey 29177b9900eSJiri Pirko static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, 29277b9900eSJiri Pirko struct tcf_result *res) 29377b9900eSJiri Pirko { 29477b9900eSJiri Pirko struct cls_fl_head *head = rcu_dereference_bh(tp->root); 29577b9900eSJiri Pirko struct fl_flow_key skb_mkey; 296*e0ace68aSPaul Blakey struct fl_flow_key skb_key; 297*e0ace68aSPaul Blakey struct fl_flow_mask *mask; 298*e0ace68aSPaul Blakey struct cls_fl_filter *f; 29977b9900eSJiri Pirko 30005cd271fSPaul Blakey list_for_each_entry_rcu(mask, &head->masks, list) { 30105cd271fSPaul Blakey fl_clear_masked_range(&skb_key, mask); 302bc3103f1SAmir Vadai 3038212ed77SJiri Pirko skb_flow_dissect_meta(skb, &mask->dissector, &skb_key); 30405cd271fSPaul Blakey /* skb_flow_dissect() does not set n_proto in case an unknown 30505cd271fSPaul Blakey * protocol, so do it rather here. 30677b9900eSJiri Pirko */ 30777b9900eSJiri Pirko skb_key.basic.n_proto = skb->protocol; 30805cd271fSPaul Blakey skb_flow_dissect_tunnel_info(skb, &mask->dissector, &skb_key); 309*e0ace68aSPaul Blakey skb_flow_dissect_ct(skb, &mask->dissector, &skb_key, 310*e0ace68aSPaul Blakey fl_ct_info_to_flower_map, 311*e0ace68aSPaul Blakey ARRAY_SIZE(fl_ct_info_to_flower_map)); 31205cd271fSPaul Blakey skb_flow_dissect(skb, &mask->dissector, &skb_key, 0); 31377b9900eSJiri Pirko 31405cd271fSPaul Blakey fl_set_masked_key(&skb_mkey, &skb_key, mask); 31577b9900eSJiri Pirko 3165c72299fSAmritha Nambiar f = fl_lookup(mask, &skb_mkey, &skb_key); 317e8eb36cdSAmir Vadai if (f && !tc_skip_sw(f->flags)) { 31877b9900eSJiri Pirko *res = f->res; 31977b9900eSJiri Pirko return tcf_exts_exec(skb, &f->exts, res); 32077b9900eSJiri Pirko } 32105cd271fSPaul Blakey } 32277b9900eSJiri Pirko return -1; 32377b9900eSJiri Pirko } 32477b9900eSJiri Pirko 32577b9900eSJiri Pirko static int fl_init(struct tcf_proto *tp) 32677b9900eSJiri Pirko { 32777b9900eSJiri Pirko struct cls_fl_head *head; 32877b9900eSJiri Pirko 32977b9900eSJiri Pirko head = kzalloc(sizeof(*head), GFP_KERNEL); 33077b9900eSJiri Pirko if (!head) 33177b9900eSJiri Pirko return -ENOBUFS; 33277b9900eSJiri Pirko 333259e60f9SVlad Buslov spin_lock_init(&head->masks_lock); 33405cd271fSPaul Blakey INIT_LIST_HEAD_RCU(&head->masks); 335c049d56eSVlad Buslov INIT_LIST_HEAD(&head->hw_filters); 33677b9900eSJiri Pirko rcu_assign_pointer(tp->root, head); 337c15ab236SChris Mi idr_init(&head->handle_idr); 33877b9900eSJiri Pirko 33905cd271fSPaul Blakey return rhashtable_init(&head->ht, &mask_ht_params); 34005cd271fSPaul Blakey } 34105cd271fSPaul Blakey 34299815f50SVlad Buslov static void fl_mask_free(struct fl_flow_mask *mask, bool mask_init_done) 34344a5cd43SPaolo Abeni { 34499815f50SVlad Buslov /* temporary masks don't have their filters list and ht initialized */ 34599815f50SVlad Buslov if (mask_init_done) { 346f48ef4d5SVlad Buslov WARN_ON(!list_empty(&mask->filters)); 34744a5cd43SPaolo Abeni rhashtable_destroy(&mask->ht); 34899815f50SVlad Buslov } 34944a5cd43SPaolo Abeni kfree(mask); 35044a5cd43SPaolo Abeni } 35144a5cd43SPaolo Abeni 35244a5cd43SPaolo Abeni static void fl_mask_free_work(struct work_struct *work) 35344a5cd43SPaolo Abeni { 35444a5cd43SPaolo Abeni struct fl_flow_mask *mask = container_of(to_rcu_work(work), 35544a5cd43SPaolo Abeni struct fl_flow_mask, rwork); 35644a5cd43SPaolo Abeni 35799815f50SVlad Buslov fl_mask_free(mask, true); 35899815f50SVlad Buslov } 35999815f50SVlad Buslov 36099815f50SVlad Buslov static void fl_uninit_mask_free_work(struct work_struct *work) 36199815f50SVlad Buslov { 36299815f50SVlad Buslov struct fl_flow_mask *mask = container_of(to_rcu_work(work), 36399815f50SVlad Buslov struct fl_flow_mask, rwork); 36499815f50SVlad Buslov 36599815f50SVlad Buslov fl_mask_free(mask, false); 36644a5cd43SPaolo Abeni } 36744a5cd43SPaolo Abeni 3689994677cSVlad Buslov static bool fl_mask_put(struct cls_fl_head *head, struct fl_flow_mask *mask) 36905cd271fSPaul Blakey { 370f48ef4d5SVlad Buslov if (!refcount_dec_and_test(&mask->refcnt)) 37105cd271fSPaul Blakey return false; 37205cd271fSPaul Blakey 37305cd271fSPaul Blakey rhashtable_remove_fast(&head->ht, &mask->ht_node, mask_ht_params); 374259e60f9SVlad Buslov 375259e60f9SVlad Buslov spin_lock(&head->masks_lock); 37605cd271fSPaul Blakey list_del_rcu(&mask->list); 377259e60f9SVlad Buslov spin_unlock(&head->masks_lock); 378259e60f9SVlad Buslov 37944a5cd43SPaolo Abeni tcf_queue_work(&mask->rwork, fl_mask_free_work); 38005cd271fSPaul Blakey 38105cd271fSPaul Blakey return true; 38277b9900eSJiri Pirko } 38377b9900eSJiri Pirko 384c049d56eSVlad Buslov static struct cls_fl_head *fl_head_dereference(struct tcf_proto *tp) 385c049d56eSVlad Buslov { 386c049d56eSVlad Buslov /* Flower classifier only changes root pointer during init and destroy. 387c049d56eSVlad Buslov * Users must obtain reference to tcf_proto instance before calling its 388c049d56eSVlad Buslov * API, so tp->root pointer is protected from concurrent call to 389c049d56eSVlad Buslov * fl_destroy() by reference counting. 390c049d56eSVlad Buslov */ 391c049d56eSVlad Buslov return rcu_dereference_raw(tp->root); 392c049d56eSVlad Buslov } 393c049d56eSVlad Buslov 3940dadc117SCong Wang static void __fl_destroy_filter(struct cls_fl_filter *f) 3950dadc117SCong Wang { 3960dadc117SCong Wang tcf_exts_destroy(&f->exts); 3970dadc117SCong Wang tcf_exts_put_net(&f->exts); 3980dadc117SCong Wang kfree(f); 3990dadc117SCong Wang } 4000dadc117SCong Wang 4010552c8afSCong Wang static void fl_destroy_filter_work(struct work_struct *work) 4020552c8afSCong Wang { 403aaa908ffSCong Wang struct cls_fl_filter *f = container_of(to_rcu_work(work), 404aaa908ffSCong Wang struct cls_fl_filter, rwork); 4050552c8afSCong Wang 4060dadc117SCong Wang __fl_destroy_filter(f); 4070552c8afSCong Wang } 4080552c8afSCong Wang 4091b0f8037SJakub Kicinski static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f, 410c24e43d8SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 4115b33f488SAmir Vadai { 412de4784caSJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 413208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 4145b33f488SAmir Vadai 415c24e43d8SVlad Buslov if (!rtnl_held) 416c24e43d8SVlad Buslov rtnl_lock(); 417c24e43d8SVlad Buslov 418d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack); 419de4784caSJiri Pirko cls_flower.command = TC_CLSFLOWER_DESTROY; 420de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 4215b33f488SAmir Vadai 422aeb3fecdSCong Wang tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false); 4233d81e711SVlad Buslov spin_lock(&tp->lock); 424c049d56eSVlad Buslov list_del_init(&f->hw_list); 425caa72601SJiri Pirko tcf_block_offload_dec(block, &f->flags); 4263d81e711SVlad Buslov spin_unlock(&tp->lock); 427c24e43d8SVlad Buslov 428c24e43d8SVlad Buslov if (!rtnl_held) 429c24e43d8SVlad Buslov rtnl_unlock(); 4305b33f488SAmir Vadai } 4315b33f488SAmir Vadai 432e8eb36cdSAmir Vadai static int fl_hw_replace_filter(struct tcf_proto *tp, 433c24e43d8SVlad Buslov struct cls_fl_filter *f, bool rtnl_held, 43441002038SQuentin Monnet struct netlink_ext_ack *extack) 4355b33f488SAmir Vadai { 436c049d56eSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 437de4784caSJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 438208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 439717503b9SJiri Pirko bool skip_sw = tc_skip_sw(f->flags); 440c24e43d8SVlad Buslov int err = 0; 441c24e43d8SVlad Buslov 442c24e43d8SVlad Buslov if (!rtnl_held) 443c24e43d8SVlad Buslov rtnl_lock(); 4445b33f488SAmir Vadai 445e3ab786bSPablo Neira Ayuso cls_flower.rule = flow_rule_alloc(tcf_exts_num_actions(&f->exts)); 446c24e43d8SVlad Buslov if (!cls_flower.rule) { 447c24e43d8SVlad Buslov err = -ENOMEM; 448c24e43d8SVlad Buslov goto errout; 449c24e43d8SVlad Buslov } 4508f256622SPablo Neira Ayuso 451d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack); 452de4784caSJiri Pirko cls_flower.command = TC_CLSFLOWER_REPLACE; 453de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 4548f256622SPablo Neira Ayuso cls_flower.rule->match.dissector = &f->mask->dissector; 4558f256622SPablo Neira Ayuso cls_flower.rule->match.mask = &f->mask->key; 4568f256622SPablo Neira Ayuso cls_flower.rule->match.key = &f->mkey; 457384c181eSAmritha Nambiar cls_flower.classid = f->res.classid; 4585b33f488SAmir Vadai 4593a7b6861SPablo Neira Ayuso err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts); 4603a7b6861SPablo Neira Ayuso if (err) { 4613a7b6861SPablo Neira Ayuso kfree(cls_flower.rule); 462c24e43d8SVlad Buslov if (skip_sw) 4631f15bb4fSVlad Buslov NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action"); 464c24e43d8SVlad Buslov else 465c24e43d8SVlad Buslov err = 0; 466c24e43d8SVlad Buslov goto errout; 4671f15bb4fSVlad Buslov } 4683a7b6861SPablo Neira Ayuso 469aeb3fecdSCong Wang err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw); 4708f256622SPablo Neira Ayuso kfree(cls_flower.rule); 4718f256622SPablo Neira Ayuso 472717503b9SJiri Pirko if (err < 0) { 473c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, f, true, NULL); 474c24e43d8SVlad Buslov goto errout; 475717503b9SJiri Pirko } else if (err > 0) { 47631533cbaSJohn Hurley f->in_hw_count = err; 477c24e43d8SVlad Buslov err = 0; 4783d81e711SVlad Buslov spin_lock(&tp->lock); 479caa72601SJiri Pirko tcf_block_offload_inc(block, &f->flags); 4803d81e711SVlad Buslov spin_unlock(&tp->lock); 481717503b9SJiri Pirko } 482717503b9SJiri Pirko 483c24e43d8SVlad Buslov if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) { 484c24e43d8SVlad Buslov err = -EINVAL; 485c24e43d8SVlad Buslov goto errout; 4865b33f488SAmir Vadai } 4875b33f488SAmir Vadai 488c049d56eSVlad Buslov spin_lock(&tp->lock); 489c049d56eSVlad Buslov list_add(&f->hw_list, &head->hw_filters); 490c049d56eSVlad Buslov spin_unlock(&tp->lock); 491c24e43d8SVlad Buslov errout: 492c24e43d8SVlad Buslov if (!rtnl_held) 493c24e43d8SVlad Buslov rtnl_unlock(); 494c24e43d8SVlad Buslov 495c24e43d8SVlad Buslov return err; 496c24e43d8SVlad Buslov } 497c24e43d8SVlad Buslov 498c24e43d8SVlad Buslov static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f, 499c24e43d8SVlad Buslov bool rtnl_held) 50010cbc684SAmir Vadai { 501de4784caSJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 502208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 50310cbc684SAmir Vadai 504c24e43d8SVlad Buslov if (!rtnl_held) 505c24e43d8SVlad Buslov rtnl_lock(); 506c24e43d8SVlad Buslov 507d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL); 508de4784caSJiri Pirko cls_flower.command = TC_CLSFLOWER_STATS; 509de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 510384c181eSAmritha Nambiar cls_flower.classid = f->res.classid; 51110cbc684SAmir Vadai 512aeb3fecdSCong Wang tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false); 5133b1903efSPablo Neira Ayuso 5143b1903efSPablo Neira Ayuso tcf_exts_stats_update(&f->exts, cls_flower.stats.bytes, 5153b1903efSPablo Neira Ayuso cls_flower.stats.pkts, 5163b1903efSPablo Neira Ayuso cls_flower.stats.lastused); 517c24e43d8SVlad Buslov 518c24e43d8SVlad Buslov if (!rtnl_held) 519c24e43d8SVlad Buslov rtnl_unlock(); 52010cbc684SAmir Vadai } 52110cbc684SAmir Vadai 52206177558SVlad Buslov static void __fl_put(struct cls_fl_filter *f) 52306177558SVlad Buslov { 52406177558SVlad Buslov if (!refcount_dec_and_test(&f->refcnt)) 52506177558SVlad Buslov return; 52606177558SVlad Buslov 52706177558SVlad Buslov if (tcf_exts_get_net(&f->exts)) 52806177558SVlad Buslov tcf_queue_work(&f->rwork, fl_destroy_filter_work); 52906177558SVlad Buslov else 53006177558SVlad Buslov __fl_destroy_filter(f); 53106177558SVlad Buslov } 53206177558SVlad Buslov 53306177558SVlad Buslov static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle) 53406177558SVlad Buslov { 53506177558SVlad Buslov struct cls_fl_filter *f; 53606177558SVlad Buslov 53706177558SVlad Buslov rcu_read_lock(); 53806177558SVlad Buslov f = idr_find(&head->handle_idr, handle); 53906177558SVlad Buslov if (f && !refcount_inc_not_zero(&f->refcnt)) 54006177558SVlad Buslov f = NULL; 54106177558SVlad Buslov rcu_read_unlock(); 54206177558SVlad Buslov 54306177558SVlad Buslov return f; 54406177558SVlad Buslov } 54506177558SVlad Buslov 546b2552b8cSVlad Buslov static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, 547c24e43d8SVlad Buslov bool *last, bool rtnl_held, 548c24e43d8SVlad Buslov struct netlink_ext_ack *extack) 54913fa876eSRoi Dayan { 550e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 551c15ab236SChris Mi 552b2552b8cSVlad Buslov *last = false; 553b2552b8cSVlad Buslov 5543d81e711SVlad Buslov spin_lock(&tp->lock); 5553d81e711SVlad Buslov if (f->deleted) { 5563d81e711SVlad Buslov spin_unlock(&tp->lock); 557b2552b8cSVlad Buslov return -ENOENT; 5583d81e711SVlad Buslov } 559b2552b8cSVlad Buslov 560b2552b8cSVlad Buslov f->deleted = true; 561b2552b8cSVlad Buslov rhashtable_remove_fast(&f->mask->ht, &f->ht_node, 562b2552b8cSVlad Buslov f->mask->filter_ht_params); 5639c160941SMatthew Wilcox idr_remove(&head->handle_idr, f->handle); 56413fa876eSRoi Dayan list_del_rcu(&f->list); 5653d81e711SVlad Buslov spin_unlock(&tp->lock); 5663d81e711SVlad Buslov 5679994677cSVlad Buslov *last = fl_mask_put(head, f->mask); 56879685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 569c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, f, rtnl_held, extack); 57013fa876eSRoi Dayan tcf_unbind_filter(tp, &f->res); 57106177558SVlad Buslov __fl_put(f); 57205cd271fSPaul Blakey 573b2552b8cSVlad Buslov return 0; 57413fa876eSRoi Dayan } 57513fa876eSRoi Dayan 576d9363774SDaniel Borkmann static void fl_destroy_sleepable(struct work_struct *work) 577d9363774SDaniel Borkmann { 578aaa908ffSCong Wang struct cls_fl_head *head = container_of(to_rcu_work(work), 579aaa908ffSCong Wang struct cls_fl_head, 580aaa908ffSCong Wang rwork); 581de9dc650SPaul Blakey 582de9dc650SPaul Blakey rhashtable_destroy(&head->ht); 583d9363774SDaniel Borkmann kfree(head); 584d9363774SDaniel Borkmann module_put(THIS_MODULE); 585d9363774SDaniel Borkmann } 586d9363774SDaniel Borkmann 58712db03b6SVlad Buslov static void fl_destroy(struct tcf_proto *tp, bool rtnl_held, 58812db03b6SVlad Buslov struct netlink_ext_ack *extack) 58977b9900eSJiri Pirko { 590e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 59105cd271fSPaul Blakey struct fl_flow_mask *mask, *next_mask; 59277b9900eSJiri Pirko struct cls_fl_filter *f, *next; 593b2552b8cSVlad Buslov bool last; 59477b9900eSJiri Pirko 59505cd271fSPaul Blakey list_for_each_entry_safe(mask, next_mask, &head->masks, list) { 59605cd271fSPaul Blakey list_for_each_entry_safe(f, next, &mask->filters, list) { 597c24e43d8SVlad Buslov __fl_delete(tp, f, &last, rtnl_held, extack); 598b2552b8cSVlad Buslov if (last) 59905cd271fSPaul Blakey break; 60005cd271fSPaul Blakey } 60105cd271fSPaul Blakey } 602c15ab236SChris Mi idr_destroy(&head->handle_idr); 603d9363774SDaniel Borkmann 604d9363774SDaniel Borkmann __module_get(THIS_MODULE); 605aaa908ffSCong Wang tcf_queue_work(&head->rwork, fl_destroy_sleepable); 60677b9900eSJiri Pirko } 60777b9900eSJiri Pirko 60806177558SVlad Buslov static void fl_put(struct tcf_proto *tp, void *arg) 60906177558SVlad Buslov { 61006177558SVlad Buslov struct cls_fl_filter *f = arg; 61106177558SVlad Buslov 61206177558SVlad Buslov __fl_put(f); 61306177558SVlad Buslov } 61406177558SVlad Buslov 6158113c095SWANG Cong static void *fl_get(struct tcf_proto *tp, u32 handle) 61677b9900eSJiri Pirko { 617e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 61877b9900eSJiri Pirko 61906177558SVlad Buslov return __fl_get(head, handle); 62077b9900eSJiri Pirko } 62177b9900eSJiri Pirko 62277b9900eSJiri Pirko static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { 62377b9900eSJiri Pirko [TCA_FLOWER_UNSPEC] = { .type = NLA_UNSPEC }, 62477b9900eSJiri Pirko [TCA_FLOWER_CLASSID] = { .type = NLA_U32 }, 62577b9900eSJiri Pirko [TCA_FLOWER_INDEV] = { .type = NLA_STRING, 62677b9900eSJiri Pirko .len = IFNAMSIZ }, 62777b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST] = { .len = ETH_ALEN }, 62877b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST_MASK] = { .len = ETH_ALEN }, 62977b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC] = { .len = ETH_ALEN }, 63077b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .len = ETH_ALEN }, 63177b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, 63277b9900eSJiri Pirko [TCA_FLOWER_KEY_IP_PROTO] = { .type = NLA_U8 }, 63377b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, 63477b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, 63577b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, 63677b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, 63777b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 63877b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 63977b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 64077b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 64177b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_SRC] = { .type = NLA_U16 }, 64277b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_DST] = { .type = NLA_U16 }, 643b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_SRC] = { .type = NLA_U16 }, 644b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_DST] = { .type = NLA_U16 }, 6459399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, 6469399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, 6479399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, 648bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, 649bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, 650bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 }, 651bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, 652bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 }, 653bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 654bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 655bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 656bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 657aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NLA_U16 }, 658aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, 659aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, 660aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, 6615976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, 6625976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, 6635976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, 6645976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, 665f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, 666f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, 667f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, 668f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, 669faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, 670faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, 6717b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NLA_U8 }, 6727b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 }, 6737b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NLA_U8 }, 6747b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 }, 6757b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NLA_U8 }, 6767b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 }, 6777b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NLA_U8 }, 6787b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 }, 67999d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP] = { .type = NLA_U32 }, 68099d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NLA_U32 }, 68199d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP] = { .type = NLA_U32 }, 68299d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NLA_U32 }, 68399d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP] = { .type = NLA_U8 }, 68499d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NLA_U8 }, 68599d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA] = { .len = ETH_ALEN }, 68699d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .len = ETH_ALEN }, 68799d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA] = { .len = ETH_ALEN }, 68899d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA_MASK] = { .len = ETH_ALEN }, 689a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NLA_U8 }, 690a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NLA_U8 }, 691a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_TC] = { .type = NLA_U8 }, 692a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NLA_U32 }, 693fdfc7dd6SJiri Pirko [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NLA_U16 }, 694fdfc7dd6SJiri Pirko [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NLA_U16 }, 6954d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TOS] = { .type = NLA_U8 }, 6964d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NLA_U8 }, 6974d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TTL] = { .type = NLA_U8 }, 6984d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NLA_U8 }, 699d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NLA_U16 }, 700d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NLA_U8 }, 701d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NLA_U16 }, 7020e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NLA_U8 }, 7030e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NLA_U8 }, 7040e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NLA_U8 }, 7050e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 }, 7060a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NLA_NESTED }, 7070a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NLA_NESTED }, 708*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_STATE] = { .type = NLA_U16 }, 709*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NLA_U16 }, 710*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_ZONE] = { .type = NLA_U16 }, 711*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NLA_U16 }, 712*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_MARK] = { .type = NLA_U32 }, 713*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NLA_U32 }, 714*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_LABELS] = { .type = NLA_BINARY, 715*e0ace68aSPaul Blakey .len = 128 / BITS_PER_BYTE }, 716*e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NLA_BINARY, 717*e0ace68aSPaul Blakey .len = 128 / BITS_PER_BYTE }, 7180a6e7778SPieter Jansen van Vuuren }; 7190a6e7778SPieter Jansen van Vuuren 7200a6e7778SPieter Jansen van Vuuren static const struct nla_policy 7210a6e7778SPieter Jansen van Vuuren enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = { 7220a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED }, 7230a6e7778SPieter Jansen van Vuuren }; 7240a6e7778SPieter Jansen van Vuuren 7250a6e7778SPieter Jansen van Vuuren static const struct nla_policy 7260a6e7778SPieter Jansen van Vuuren geneve_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1] = { 7270a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] = { .type = NLA_U16 }, 7280a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] = { .type = NLA_U8 }, 7290a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA] = { .type = NLA_BINARY, 7300a6e7778SPieter Jansen van Vuuren .len = 128 }, 73177b9900eSJiri Pirko }; 73277b9900eSJiri Pirko 73377b9900eSJiri Pirko static void fl_set_key_val(struct nlattr **tb, 73477b9900eSJiri Pirko void *val, int val_type, 73577b9900eSJiri Pirko void *mask, int mask_type, int len) 73677b9900eSJiri Pirko { 73777b9900eSJiri Pirko if (!tb[val_type]) 73877b9900eSJiri Pirko return; 739*e0ace68aSPaul Blakey nla_memcpy(val, tb[val_type], len); 74077b9900eSJiri Pirko if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type]) 74177b9900eSJiri Pirko memset(mask, 0xff, len); 74277b9900eSJiri Pirko else 743*e0ace68aSPaul Blakey nla_memcpy(mask, tb[mask_type], len); 74477b9900eSJiri Pirko } 74577b9900eSJiri Pirko 7465c72299fSAmritha Nambiar static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key, 7475c72299fSAmritha Nambiar struct fl_flow_key *mask) 7485c72299fSAmritha Nambiar { 7495c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_min.dst, 7505c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst, 7515c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst)); 7525c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_max.dst, 7535c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst, 7545c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst)); 7555c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_min.src, 7565c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src, 7575c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src)); 7585c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_max.src, 7595c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src, 7605c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src)); 7615c72299fSAmritha Nambiar 7625c72299fSAmritha Nambiar if ((mask->tp_min.dst && mask->tp_max.dst && 7635c72299fSAmritha Nambiar htons(key->tp_max.dst) <= htons(key->tp_min.dst)) || 7645c72299fSAmritha Nambiar (mask->tp_min.src && mask->tp_max.src && 7655c72299fSAmritha Nambiar htons(key->tp_max.src) <= htons(key->tp_min.src))) 7665c72299fSAmritha Nambiar return -EINVAL; 7675c72299fSAmritha Nambiar 7685c72299fSAmritha Nambiar return 0; 7695c72299fSAmritha Nambiar } 7705c72299fSAmritha Nambiar 7711a7fca63SBenjamin LaHaise static int fl_set_key_mpls(struct nlattr **tb, 772a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *key_val, 773a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *key_mask) 774a577d8f7SBenjamin LaHaise { 775a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_TTL]) { 776a577d8f7SBenjamin LaHaise key_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]); 777a577d8f7SBenjamin LaHaise key_mask->mpls_ttl = MPLS_TTL_MASK; 778a577d8f7SBenjamin LaHaise } 779a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_BOS]) { 7801a7fca63SBenjamin LaHaise u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); 7811a7fca63SBenjamin LaHaise 7821a7fca63SBenjamin LaHaise if (bos & ~MPLS_BOS_MASK) 7831a7fca63SBenjamin LaHaise return -EINVAL; 7841a7fca63SBenjamin LaHaise key_val->mpls_bos = bos; 785a577d8f7SBenjamin LaHaise key_mask->mpls_bos = MPLS_BOS_MASK; 786a577d8f7SBenjamin LaHaise } 787a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_TC]) { 7881a7fca63SBenjamin LaHaise u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]); 7891a7fca63SBenjamin LaHaise 7901a7fca63SBenjamin LaHaise if (tc & ~MPLS_TC_MASK) 7911a7fca63SBenjamin LaHaise return -EINVAL; 7921a7fca63SBenjamin LaHaise key_val->mpls_tc = tc; 793a577d8f7SBenjamin LaHaise key_mask->mpls_tc = MPLS_TC_MASK; 794a577d8f7SBenjamin LaHaise } 795a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) { 7961a7fca63SBenjamin LaHaise u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]); 7971a7fca63SBenjamin LaHaise 7981a7fca63SBenjamin LaHaise if (label & ~MPLS_LABEL_MASK) 7991a7fca63SBenjamin LaHaise return -EINVAL; 8001a7fca63SBenjamin LaHaise key_val->mpls_label = label; 801a577d8f7SBenjamin LaHaise key_mask->mpls_label = MPLS_LABEL_MASK; 802a577d8f7SBenjamin LaHaise } 8031a7fca63SBenjamin LaHaise return 0; 804a577d8f7SBenjamin LaHaise } 805a577d8f7SBenjamin LaHaise 8069399ae9aSHadar Hen Zion static void fl_set_key_vlan(struct nlattr **tb, 807aaab0834SJianbo Liu __be16 ethertype, 808d64efd09SJianbo Liu int vlan_id_key, int vlan_prio_key, 8099399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_val, 8109399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_mask) 8119399ae9aSHadar Hen Zion { 8129399ae9aSHadar Hen Zion #define VLAN_PRIORITY_MASK 0x7 8139399ae9aSHadar Hen Zion 814d64efd09SJianbo Liu if (tb[vlan_id_key]) { 8159399ae9aSHadar Hen Zion key_val->vlan_id = 816d64efd09SJianbo Liu nla_get_u16(tb[vlan_id_key]) & VLAN_VID_MASK; 8179399ae9aSHadar Hen Zion key_mask->vlan_id = VLAN_VID_MASK; 8189399ae9aSHadar Hen Zion } 819d64efd09SJianbo Liu if (tb[vlan_prio_key]) { 8209399ae9aSHadar Hen Zion key_val->vlan_priority = 821d64efd09SJianbo Liu nla_get_u8(tb[vlan_prio_key]) & 8229399ae9aSHadar Hen Zion VLAN_PRIORITY_MASK; 8239399ae9aSHadar Hen Zion key_mask->vlan_priority = VLAN_PRIORITY_MASK; 8249399ae9aSHadar Hen Zion } 825aaab0834SJianbo Liu key_val->vlan_tpid = ethertype; 826aaab0834SJianbo Liu key_mask->vlan_tpid = cpu_to_be16(~0); 8279399ae9aSHadar Hen Zion } 8289399ae9aSHadar Hen Zion 829faa3ffceSOr Gerlitz static void fl_set_key_flag(u32 flower_key, u32 flower_mask, 830faa3ffceSOr Gerlitz u32 *dissector_key, u32 *dissector_mask, 831faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 832faa3ffceSOr Gerlitz { 833faa3ffceSOr Gerlitz if (flower_mask & flower_flag_bit) { 834faa3ffceSOr Gerlitz *dissector_mask |= dissector_flag_bit; 835faa3ffceSOr Gerlitz if (flower_key & flower_flag_bit) 836faa3ffceSOr Gerlitz *dissector_key |= dissector_flag_bit; 837faa3ffceSOr Gerlitz } 838faa3ffceSOr Gerlitz } 839faa3ffceSOr Gerlitz 840d9724772SOr Gerlitz static int fl_set_key_flags(struct nlattr **tb, 841faa3ffceSOr Gerlitz u32 *flags_key, u32 *flags_mask) 842faa3ffceSOr Gerlitz { 843faa3ffceSOr Gerlitz u32 key, mask; 844faa3ffceSOr Gerlitz 845d9724772SOr Gerlitz /* mask is mandatory for flags */ 846d9724772SOr Gerlitz if (!tb[TCA_FLOWER_KEY_FLAGS_MASK]) 847d9724772SOr Gerlitz return -EINVAL; 848faa3ffceSOr Gerlitz 849faa3ffceSOr Gerlitz key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS])); 850faa3ffceSOr Gerlitz mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK])); 851faa3ffceSOr Gerlitz 852faa3ffceSOr Gerlitz *flags_key = 0; 853faa3ffceSOr Gerlitz *flags_mask = 0; 854faa3ffceSOr Gerlitz 855faa3ffceSOr Gerlitz fl_set_key_flag(key, mask, flags_key, flags_mask, 856faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 857459d153dSPieter Jansen van Vuuren fl_set_key_flag(key, mask, flags_key, flags_mask, 858459d153dSPieter Jansen van Vuuren TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 859459d153dSPieter Jansen van Vuuren FLOW_DIS_FIRST_FRAG); 860d9724772SOr Gerlitz 861d9724772SOr Gerlitz return 0; 862faa3ffceSOr Gerlitz } 863faa3ffceSOr Gerlitz 8640e2c17b6SOr Gerlitz static void fl_set_key_ip(struct nlattr **tb, bool encap, 8654d80cc0aSOr Gerlitz struct flow_dissector_key_ip *key, 8664d80cc0aSOr Gerlitz struct flow_dissector_key_ip *mask) 8674d80cc0aSOr Gerlitz { 8680e2c17b6SOr Gerlitz int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 8690e2c17b6SOr Gerlitz int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 8700e2c17b6SOr Gerlitz int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 8710e2c17b6SOr Gerlitz int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 8724d80cc0aSOr Gerlitz 8730e2c17b6SOr Gerlitz fl_set_key_val(tb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)); 8740e2c17b6SOr Gerlitz fl_set_key_val(tb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl)); 8754d80cc0aSOr Gerlitz } 8764d80cc0aSOr Gerlitz 8770a6e7778SPieter Jansen van Vuuren static int fl_set_geneve_opt(const struct nlattr *nla, struct fl_flow_key *key, 8780a6e7778SPieter Jansen van Vuuren int depth, int option_len, 8790a6e7778SPieter Jansen van Vuuren struct netlink_ext_ack *extack) 8800a6e7778SPieter Jansen van Vuuren { 8810a6e7778SPieter Jansen van Vuuren struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1]; 8820a6e7778SPieter Jansen van Vuuren struct nlattr *class = NULL, *type = NULL, *data = NULL; 8830a6e7778SPieter Jansen van Vuuren struct geneve_opt *opt; 8840a6e7778SPieter Jansen van Vuuren int err, data_len = 0; 8850a6e7778SPieter Jansen van Vuuren 8860a6e7778SPieter Jansen van Vuuren if (option_len > sizeof(struct geneve_opt)) 8870a6e7778SPieter Jansen van Vuuren data_len = option_len - sizeof(struct geneve_opt); 8880a6e7778SPieter Jansen van Vuuren 8890a6e7778SPieter Jansen van Vuuren opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len]; 8900a6e7778SPieter Jansen van Vuuren memset(opt, 0xff, option_len); 8910a6e7778SPieter Jansen van Vuuren opt->length = data_len / 4; 8920a6e7778SPieter Jansen van Vuuren opt->r1 = 0; 8930a6e7778SPieter Jansen van Vuuren opt->r2 = 0; 8940a6e7778SPieter Jansen van Vuuren opt->r3 = 0; 8950a6e7778SPieter Jansen van Vuuren 8960a6e7778SPieter Jansen van Vuuren /* If no mask has been prodived we assume an exact match. */ 8970a6e7778SPieter Jansen van Vuuren if (!depth) 8980a6e7778SPieter Jansen van Vuuren return sizeof(struct geneve_opt) + data_len; 8990a6e7778SPieter Jansen van Vuuren 9000a6e7778SPieter Jansen van Vuuren if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GENEVE) { 9010a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Non-geneve option type for mask"); 9020a6e7778SPieter Jansen van Vuuren return -EINVAL; 9030a6e7778SPieter Jansen van Vuuren } 9040a6e7778SPieter Jansen van Vuuren 9058cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, 9068cb08174SJohannes Berg TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, 9070a6e7778SPieter Jansen van Vuuren nla, geneve_opt_policy, extack); 9080a6e7778SPieter Jansen van Vuuren if (err < 0) 9090a6e7778SPieter Jansen van Vuuren return err; 9100a6e7778SPieter Jansen van Vuuren 9110a6e7778SPieter Jansen van Vuuren /* We are not allowed to omit any of CLASS, TYPE or DATA 9120a6e7778SPieter Jansen van Vuuren * fields from the key. 9130a6e7778SPieter Jansen van Vuuren */ 9140a6e7778SPieter Jansen van Vuuren if (!option_len && 9150a6e7778SPieter Jansen van Vuuren (!tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] || 9160a6e7778SPieter Jansen van Vuuren !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] || 9170a6e7778SPieter Jansen van Vuuren !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA])) { 9180a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data"); 9190a6e7778SPieter Jansen van Vuuren return -EINVAL; 9200a6e7778SPieter Jansen van Vuuren } 9210a6e7778SPieter Jansen van Vuuren 9220a6e7778SPieter Jansen van Vuuren /* Omitting any of CLASS, TYPE or DATA fields is allowed 9230a6e7778SPieter Jansen van Vuuren * for the mask. 9240a6e7778SPieter Jansen van Vuuren */ 9250a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]) { 9260a6e7778SPieter Jansen van Vuuren int new_len = key->enc_opts.len; 9270a6e7778SPieter Jansen van Vuuren 9280a6e7778SPieter Jansen van Vuuren data = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]; 9290a6e7778SPieter Jansen van Vuuren data_len = nla_len(data); 9300a6e7778SPieter Jansen van Vuuren if (data_len < 4) { 9310a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long"); 9320a6e7778SPieter Jansen van Vuuren return -ERANGE; 9330a6e7778SPieter Jansen van Vuuren } 9340a6e7778SPieter Jansen van Vuuren if (data_len % 4) { 9350a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long"); 9360a6e7778SPieter Jansen van Vuuren return -ERANGE; 9370a6e7778SPieter Jansen van Vuuren } 9380a6e7778SPieter Jansen van Vuuren 9390a6e7778SPieter Jansen van Vuuren new_len += sizeof(struct geneve_opt) + data_len; 9400a6e7778SPieter Jansen van Vuuren BUILD_BUG_ON(FLOW_DIS_TUN_OPTS_MAX != IP_TUNNEL_OPTS_MAX); 9410a6e7778SPieter Jansen van Vuuren if (new_len > FLOW_DIS_TUN_OPTS_MAX) { 9420a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size"); 9430a6e7778SPieter Jansen van Vuuren return -ERANGE; 9440a6e7778SPieter Jansen van Vuuren } 9450a6e7778SPieter Jansen van Vuuren opt->length = data_len / 4; 9460a6e7778SPieter Jansen van Vuuren memcpy(opt->opt_data, nla_data(data), data_len); 9470a6e7778SPieter Jansen van Vuuren } 9480a6e7778SPieter Jansen van Vuuren 9490a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]) { 9500a6e7778SPieter Jansen van Vuuren class = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]; 9510a6e7778SPieter Jansen van Vuuren opt->opt_class = nla_get_be16(class); 9520a6e7778SPieter Jansen van Vuuren } 9530a6e7778SPieter Jansen van Vuuren 9540a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]) { 9550a6e7778SPieter Jansen van Vuuren type = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]; 9560a6e7778SPieter Jansen van Vuuren opt->type = nla_get_u8(type); 9570a6e7778SPieter Jansen van Vuuren } 9580a6e7778SPieter Jansen van Vuuren 9590a6e7778SPieter Jansen van Vuuren return sizeof(struct geneve_opt) + data_len; 9600a6e7778SPieter Jansen van Vuuren } 9610a6e7778SPieter Jansen van Vuuren 9620a6e7778SPieter Jansen van Vuuren static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, 9630a6e7778SPieter Jansen van Vuuren struct fl_flow_key *mask, 9640a6e7778SPieter Jansen van Vuuren struct netlink_ext_ack *extack) 9650a6e7778SPieter Jansen van Vuuren { 9660a6e7778SPieter Jansen van Vuuren const struct nlattr *nla_enc_key, *nla_opt_key, *nla_opt_msk = NULL; 96763c82997SJakub Kicinski int err, option_len, key_depth, msk_depth = 0; 96863c82997SJakub Kicinski 9698cb08174SJohannes Berg err = nla_validate_nested_deprecated(tb[TCA_FLOWER_KEY_ENC_OPTS], 97063c82997SJakub Kicinski TCA_FLOWER_KEY_ENC_OPTS_MAX, 97163c82997SJakub Kicinski enc_opts_policy, extack); 97263c82997SJakub Kicinski if (err) 97363c82997SJakub Kicinski return err; 9740a6e7778SPieter Jansen van Vuuren 9750a6e7778SPieter Jansen van Vuuren nla_enc_key = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS]); 9760a6e7778SPieter Jansen van Vuuren 9770a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]) { 9788cb08174SJohannes Berg err = nla_validate_nested_deprecated(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK], 97963c82997SJakub Kicinski TCA_FLOWER_KEY_ENC_OPTS_MAX, 98063c82997SJakub Kicinski enc_opts_policy, extack); 98163c82997SJakub Kicinski if (err) 98263c82997SJakub Kicinski return err; 98363c82997SJakub Kicinski 9840a6e7778SPieter Jansen van Vuuren nla_opt_msk = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); 9850a6e7778SPieter Jansen van Vuuren msk_depth = nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); 9860a6e7778SPieter Jansen van Vuuren } 9870a6e7778SPieter Jansen van Vuuren 9880a6e7778SPieter Jansen van Vuuren nla_for_each_attr(nla_opt_key, nla_enc_key, 9890a6e7778SPieter Jansen van Vuuren nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS]), key_depth) { 9900a6e7778SPieter Jansen van Vuuren switch (nla_type(nla_opt_key)) { 9910a6e7778SPieter Jansen van Vuuren case TCA_FLOWER_KEY_ENC_OPTS_GENEVE: 9920a6e7778SPieter Jansen van Vuuren option_len = 0; 9930a6e7778SPieter Jansen van Vuuren key->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT; 9940a6e7778SPieter Jansen van Vuuren option_len = fl_set_geneve_opt(nla_opt_key, key, 9950a6e7778SPieter Jansen van Vuuren key_depth, option_len, 9960a6e7778SPieter Jansen van Vuuren extack); 9970a6e7778SPieter Jansen van Vuuren if (option_len < 0) 9980a6e7778SPieter Jansen van Vuuren return option_len; 9990a6e7778SPieter Jansen van Vuuren 10000a6e7778SPieter Jansen van Vuuren key->enc_opts.len += option_len; 10010a6e7778SPieter Jansen van Vuuren /* At the same time we need to parse through the mask 10020a6e7778SPieter Jansen van Vuuren * in order to verify exact and mask attribute lengths. 10030a6e7778SPieter Jansen van Vuuren */ 10040a6e7778SPieter Jansen van Vuuren mask->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT; 10050a6e7778SPieter Jansen van Vuuren option_len = fl_set_geneve_opt(nla_opt_msk, mask, 10060a6e7778SPieter Jansen van Vuuren msk_depth, option_len, 10070a6e7778SPieter Jansen van Vuuren extack); 10080a6e7778SPieter Jansen van Vuuren if (option_len < 0) 10090a6e7778SPieter Jansen van Vuuren return option_len; 10100a6e7778SPieter Jansen van Vuuren 10110a6e7778SPieter Jansen van Vuuren mask->enc_opts.len += option_len; 10120a6e7778SPieter Jansen van Vuuren if (key->enc_opts.len != mask->enc_opts.len) { 10130a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Key and mask miss aligned"); 10140a6e7778SPieter Jansen van Vuuren return -EINVAL; 10150a6e7778SPieter Jansen van Vuuren } 10160a6e7778SPieter Jansen van Vuuren 10170a6e7778SPieter Jansen van Vuuren if (msk_depth) 10180a6e7778SPieter Jansen van Vuuren nla_opt_msk = nla_next(nla_opt_msk, &msk_depth); 10190a6e7778SPieter Jansen van Vuuren break; 10200a6e7778SPieter Jansen van Vuuren default: 10210a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Unknown tunnel option type"); 10220a6e7778SPieter Jansen van Vuuren return -EINVAL; 10230a6e7778SPieter Jansen van Vuuren } 10240a6e7778SPieter Jansen van Vuuren } 10250a6e7778SPieter Jansen van Vuuren 10260a6e7778SPieter Jansen van Vuuren return 0; 10270a6e7778SPieter Jansen van Vuuren } 10280a6e7778SPieter Jansen van Vuuren 1029*e0ace68aSPaul Blakey static int fl_set_key_ct(struct nlattr **tb, 1030*e0ace68aSPaul Blakey struct flow_dissector_key_ct *key, 1031*e0ace68aSPaul Blakey struct flow_dissector_key_ct *mask, 1032*e0ace68aSPaul Blakey struct netlink_ext_ack *extack) 1033*e0ace68aSPaul Blakey { 1034*e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_STATE]) { 1035*e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK)) { 1036*e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack isn't enabled"); 1037*e0ace68aSPaul Blakey return -EOPNOTSUPP; 1038*e0ace68aSPaul Blakey } 1039*e0ace68aSPaul Blakey fl_set_key_val(tb, &key->ct_state, TCA_FLOWER_KEY_CT_STATE, 1040*e0ace68aSPaul Blakey &mask->ct_state, TCA_FLOWER_KEY_CT_STATE_MASK, 1041*e0ace68aSPaul Blakey sizeof(key->ct_state)); 1042*e0ace68aSPaul Blakey } 1043*e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_ZONE]) { 1044*e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { 1045*e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack zones isn't enabled"); 1046*e0ace68aSPaul Blakey return -EOPNOTSUPP; 1047*e0ace68aSPaul Blakey } 1048*e0ace68aSPaul Blakey fl_set_key_val(tb, &key->ct_zone, TCA_FLOWER_KEY_CT_ZONE, 1049*e0ace68aSPaul Blakey &mask->ct_zone, TCA_FLOWER_KEY_CT_ZONE_MASK, 1050*e0ace68aSPaul Blakey sizeof(key->ct_zone)); 1051*e0ace68aSPaul Blakey } 1052*e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_MARK]) { 1053*e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)) { 1054*e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack mark isn't enabled"); 1055*e0ace68aSPaul Blakey return -EOPNOTSUPP; 1056*e0ace68aSPaul Blakey } 1057*e0ace68aSPaul Blakey fl_set_key_val(tb, &key->ct_mark, TCA_FLOWER_KEY_CT_MARK, 1058*e0ace68aSPaul Blakey &mask->ct_mark, TCA_FLOWER_KEY_CT_MARK_MASK, 1059*e0ace68aSPaul Blakey sizeof(key->ct_mark)); 1060*e0ace68aSPaul Blakey } 1061*e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_LABELS]) { 1062*e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS)) { 1063*e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack labels aren't enabled"); 1064*e0ace68aSPaul Blakey return -EOPNOTSUPP; 1065*e0ace68aSPaul Blakey } 1066*e0ace68aSPaul Blakey fl_set_key_val(tb, key->ct_labels, TCA_FLOWER_KEY_CT_LABELS, 1067*e0ace68aSPaul Blakey mask->ct_labels, TCA_FLOWER_KEY_CT_LABELS_MASK, 1068*e0ace68aSPaul Blakey sizeof(key->ct_labels)); 1069*e0ace68aSPaul Blakey } 1070*e0ace68aSPaul Blakey 1071*e0ace68aSPaul Blakey return 0; 1072*e0ace68aSPaul Blakey } 1073*e0ace68aSPaul Blakey 107477b9900eSJiri Pirko static int fl_set_key(struct net *net, struct nlattr **tb, 10751057c55fSAlexander Aring struct fl_flow_key *key, struct fl_flow_key *mask, 10761057c55fSAlexander Aring struct netlink_ext_ack *extack) 107777b9900eSJiri Pirko { 10789399ae9aSHadar Hen Zion __be16 ethertype; 1079d9724772SOr Gerlitz int ret = 0; 1080a5148626SJiri Pirko 108177b9900eSJiri Pirko if (tb[TCA_FLOWER_INDEV]) { 10821057c55fSAlexander Aring int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV], extack); 108377b9900eSJiri Pirko if (err < 0) 108477b9900eSJiri Pirko return err; 10858212ed77SJiri Pirko key->meta.ingress_ifindex = err; 10868212ed77SJiri Pirko mask->meta.ingress_ifindex = 0xffffffff; 108777b9900eSJiri Pirko } 108877b9900eSJiri Pirko 108977b9900eSJiri Pirko fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 109077b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 109177b9900eSJiri Pirko sizeof(key->eth.dst)); 109277b9900eSJiri Pirko fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 109377b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 109477b9900eSJiri Pirko sizeof(key->eth.src)); 109566530bdfSJamal Hadi Salim 10960b498a52SArnd Bergmann if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { 10979399ae9aSHadar Hen Zion ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]); 10989399ae9aSHadar Hen Zion 1099aaab0834SJianbo Liu if (eth_type_vlan(ethertype)) { 1100d64efd09SJianbo Liu fl_set_key_vlan(tb, ethertype, TCA_FLOWER_KEY_VLAN_ID, 1101d64efd09SJianbo Liu TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, 1102d64efd09SJianbo Liu &mask->vlan); 1103d64efd09SJianbo Liu 11045e9a0fe4SJianbo Liu if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { 1105d64efd09SJianbo Liu ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); 1106d64efd09SJianbo Liu if (eth_type_vlan(ethertype)) { 1107d64efd09SJianbo Liu fl_set_key_vlan(tb, ethertype, 1108d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_ID, 1109d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_PRIO, 1110d64efd09SJianbo Liu &key->cvlan, &mask->cvlan); 11119399ae9aSHadar Hen Zion fl_set_key_val(tb, &key->basic.n_proto, 1112d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 1113d64efd09SJianbo Liu &mask->basic.n_proto, 1114d64efd09SJianbo Liu TCA_FLOWER_UNSPEC, 111577b9900eSJiri Pirko sizeof(key->basic.n_proto)); 11169399ae9aSHadar Hen Zion } else { 11179399ae9aSHadar Hen Zion key->basic.n_proto = ethertype; 11189399ae9aSHadar Hen Zion mask->basic.n_proto = cpu_to_be16(~0); 11199399ae9aSHadar Hen Zion } 11205e9a0fe4SJianbo Liu } 1121d64efd09SJianbo Liu } else { 1122d64efd09SJianbo Liu key->basic.n_proto = ethertype; 1123d64efd09SJianbo Liu mask->basic.n_proto = cpu_to_be16(~0); 1124d64efd09SJianbo Liu } 11250b498a52SArnd Bergmann } 112666530bdfSJamal Hadi Salim 112777b9900eSJiri Pirko if (key->basic.n_proto == htons(ETH_P_IP) || 112877b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) { 112977b9900eSJiri Pirko fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 113077b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 113177b9900eSJiri Pirko sizeof(key->basic.ip_proto)); 11320e2c17b6SOr Gerlitz fl_set_key_ip(tb, false, &key->ip, &mask->ip); 113377b9900eSJiri Pirko } 113466530bdfSJamal Hadi Salim 113566530bdfSJamal Hadi Salim if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) { 113666530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 1137970bfcd0SPaul Blakey mask->control.addr_type = ~0; 113877b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 113977b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 114077b9900eSJiri Pirko sizeof(key->ipv4.src)); 114177b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 114277b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 114377b9900eSJiri Pirko sizeof(key->ipv4.dst)); 114466530bdfSJamal Hadi Salim } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) { 114566530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 1146970bfcd0SPaul Blakey mask->control.addr_type = ~0; 114777b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 114877b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 114977b9900eSJiri Pirko sizeof(key->ipv6.src)); 115077b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 115177b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 115277b9900eSJiri Pirko sizeof(key->ipv6.dst)); 115377b9900eSJiri Pirko } 115466530bdfSJamal Hadi Salim 115577b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP) { 115677b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 1157aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 115877b9900eSJiri Pirko sizeof(key->tp.src)); 115977b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 1160aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 116177b9900eSJiri Pirko sizeof(key->tp.dst)); 1162fdfc7dd6SJiri Pirko fl_set_key_val(tb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 1163fdfc7dd6SJiri Pirko &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 1164fdfc7dd6SJiri Pirko sizeof(key->tcp.flags)); 116577b9900eSJiri Pirko } else if (key->basic.ip_proto == IPPROTO_UDP) { 116677b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 1167aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 116877b9900eSJiri Pirko sizeof(key->tp.src)); 116977b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 1170aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 117177b9900eSJiri Pirko sizeof(key->tp.dst)); 11725976c5f4SSimon Horman } else if (key->basic.ip_proto == IPPROTO_SCTP) { 11735976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 11745976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 11755976c5f4SSimon Horman sizeof(key->tp.src)); 11765976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 11775976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 11785976c5f4SSimon Horman sizeof(key->tp.dst)); 11797b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IP) && 11807b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP) { 11817b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE, 11827b684884SSimon Horman &mask->icmp.type, 11837b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 11847b684884SSimon Horman sizeof(key->icmp.type)); 11857b684884SSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, 11867b684884SSimon Horman &mask->icmp.code, 11877b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 11887b684884SSimon Horman sizeof(key->icmp.code)); 11897b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IPV6) && 11907b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6) { 11917b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE, 11927b684884SSimon Horman &mask->icmp.type, 11937b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 11947b684884SSimon Horman sizeof(key->icmp.type)); 1195040587afSSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE, 11967b684884SSimon Horman &mask->icmp.code, 1197040587afSSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 11987b684884SSimon Horman sizeof(key->icmp.code)); 1199a577d8f7SBenjamin LaHaise } else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) || 1200a577d8f7SBenjamin LaHaise key->basic.n_proto == htons(ETH_P_MPLS_MC)) { 12011a7fca63SBenjamin LaHaise ret = fl_set_key_mpls(tb, &key->mpls, &mask->mpls); 12021a7fca63SBenjamin LaHaise if (ret) 12031a7fca63SBenjamin LaHaise return ret; 120499d31326SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_ARP) || 120599d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) { 120699d31326SSimon Horman fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, 120799d31326SSimon Horman &mask->arp.sip, TCA_FLOWER_KEY_ARP_SIP_MASK, 120899d31326SSimon Horman sizeof(key->arp.sip)); 120999d31326SSimon Horman fl_set_key_val(tb, &key->arp.tip, TCA_FLOWER_KEY_ARP_TIP, 121099d31326SSimon Horman &mask->arp.tip, TCA_FLOWER_KEY_ARP_TIP_MASK, 121199d31326SSimon Horman sizeof(key->arp.tip)); 121299d31326SSimon Horman fl_set_key_val(tb, &key->arp.op, TCA_FLOWER_KEY_ARP_OP, 121399d31326SSimon Horman &mask->arp.op, TCA_FLOWER_KEY_ARP_OP_MASK, 121499d31326SSimon Horman sizeof(key->arp.op)); 121599d31326SSimon Horman fl_set_key_val(tb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 121699d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 121799d31326SSimon Horman sizeof(key->arp.sha)); 121899d31326SSimon Horman fl_set_key_val(tb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 121999d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 122099d31326SSimon Horman sizeof(key->arp.tha)); 122177b9900eSJiri Pirko } 122277b9900eSJiri Pirko 12235c72299fSAmritha Nambiar if (key->basic.ip_proto == IPPROTO_TCP || 12245c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_UDP || 12255c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_SCTP) { 12265c72299fSAmritha Nambiar ret = fl_set_key_port_range(tb, key, mask); 12275c72299fSAmritha Nambiar if (ret) 12285c72299fSAmritha Nambiar return ret; 12295c72299fSAmritha Nambiar } 12305c72299fSAmritha Nambiar 1231bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || 1232bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) { 1233bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 1234970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 1235bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.src, 1236bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, 1237bc3103f1SAmir Vadai &mask->enc_ipv4.src, 1238bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 1239bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)); 1240bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.dst, 1241bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, 1242bc3103f1SAmir Vadai &mask->enc_ipv4.dst, 1243bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 1244bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)); 1245bc3103f1SAmir Vadai } 1246bc3103f1SAmir Vadai 1247bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] || 1248bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) { 1249bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 1250970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 1251bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.src, 1252bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, 1253bc3103f1SAmir Vadai &mask->enc_ipv6.src, 1254bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 1255bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)); 1256bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.dst, 1257bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 1258bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 1259bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 1260bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)); 1261bc3103f1SAmir Vadai } 1262bc3103f1SAmir Vadai 1263bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID, 1264eb523f42SHadar Hen Zion &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, 1265bc3103f1SAmir Vadai sizeof(key->enc_key_id.keyid)); 1266bc3103f1SAmir Vadai 1267f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 1268f4d997fdSHadar Hen Zion &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 1269f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)); 1270f4d997fdSHadar Hen Zion 1271f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 1272f4d997fdSHadar Hen Zion &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 1273f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst)); 1274f4d997fdSHadar Hen Zion 12750e2c17b6SOr Gerlitz fl_set_key_ip(tb, true, &key->enc_ip, &mask->enc_ip); 12760e2c17b6SOr Gerlitz 12770a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPTS]) { 12780a6e7778SPieter Jansen van Vuuren ret = fl_set_enc_opt(tb, key, mask, extack); 12790a6e7778SPieter Jansen van Vuuren if (ret) 12800a6e7778SPieter Jansen van Vuuren return ret; 12810a6e7778SPieter Jansen van Vuuren } 12820a6e7778SPieter Jansen van Vuuren 1283*e0ace68aSPaul Blakey ret = fl_set_key_ct(tb, &key->ct, &mask->ct, extack); 1284*e0ace68aSPaul Blakey if (ret) 1285*e0ace68aSPaul Blakey return ret; 1286*e0ace68aSPaul Blakey 1287d9724772SOr Gerlitz if (tb[TCA_FLOWER_KEY_FLAGS]) 1288d9724772SOr Gerlitz ret = fl_set_key_flags(tb, &key->control.flags, &mask->control.flags); 1289faa3ffceSOr Gerlitz 1290d9724772SOr Gerlitz return ret; 129177b9900eSJiri Pirko } 129277b9900eSJiri Pirko 129305cd271fSPaul Blakey static void fl_mask_copy(struct fl_flow_mask *dst, 129405cd271fSPaul Blakey struct fl_flow_mask *src) 129577b9900eSJiri Pirko { 129605cd271fSPaul Blakey const void *psrc = fl_key_get_start(&src->key, src); 129705cd271fSPaul Blakey void *pdst = fl_key_get_start(&dst->key, src); 129877b9900eSJiri Pirko 129905cd271fSPaul Blakey memcpy(pdst, psrc, fl_mask_range(src)); 130005cd271fSPaul Blakey dst->range = src->range; 130177b9900eSJiri Pirko } 130277b9900eSJiri Pirko 130377b9900eSJiri Pirko static const struct rhashtable_params fl_ht_params = { 130477b9900eSJiri Pirko .key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */ 130577b9900eSJiri Pirko .head_offset = offsetof(struct cls_fl_filter, ht_node), 130677b9900eSJiri Pirko .automatic_shrinking = true, 130777b9900eSJiri Pirko }; 130877b9900eSJiri Pirko 130905cd271fSPaul Blakey static int fl_init_mask_hashtable(struct fl_flow_mask *mask) 131077b9900eSJiri Pirko { 131105cd271fSPaul Blakey mask->filter_ht_params = fl_ht_params; 131205cd271fSPaul Blakey mask->filter_ht_params.key_len = fl_mask_range(mask); 131305cd271fSPaul Blakey mask->filter_ht_params.key_offset += mask->range.start; 131477b9900eSJiri Pirko 131505cd271fSPaul Blakey return rhashtable_init(&mask->ht, &mask->filter_ht_params); 131677b9900eSJiri Pirko } 131777b9900eSJiri Pirko 131877b9900eSJiri Pirko #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) 1319cb205a81Szhong jiang #define FL_KEY_MEMBER_SIZE(member) FIELD_SIZEOF(struct fl_flow_key, member) 132077b9900eSJiri Pirko 1321339ba878SHadar Hen Zion #define FL_KEY_IS_MASKED(mask, member) \ 1322339ba878SHadar Hen Zion memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ 1323339ba878SHadar Hen Zion 0, FL_KEY_MEMBER_SIZE(member)) \ 132477b9900eSJiri Pirko 132577b9900eSJiri Pirko #define FL_KEY_SET(keys, cnt, id, member) \ 132677b9900eSJiri Pirko do { \ 132777b9900eSJiri Pirko keys[cnt].key_id = id; \ 132877b9900eSJiri Pirko keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member); \ 132977b9900eSJiri Pirko cnt++; \ 133077b9900eSJiri Pirko } while(0); 133177b9900eSJiri Pirko 1332339ba878SHadar Hen Zion #define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member) \ 133377b9900eSJiri Pirko do { \ 1334339ba878SHadar Hen Zion if (FL_KEY_IS_MASKED(mask, member)) \ 133577b9900eSJiri Pirko FL_KEY_SET(keys, cnt, id, member); \ 133677b9900eSJiri Pirko } while(0); 133777b9900eSJiri Pirko 133833fb5cbaSJiri Pirko static void fl_init_dissector(struct flow_dissector *dissector, 133933fb5cbaSJiri Pirko struct fl_flow_key *mask) 134077b9900eSJiri Pirko { 134177b9900eSJiri Pirko struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX]; 134277b9900eSJiri Pirko size_t cnt = 0; 134377b9900eSJiri Pirko 13448212ed77SJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13458212ed77SJiri Pirko FLOW_DISSECTOR_KEY_META, meta); 134642aecaa9STom Herbert FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control); 134777b9900eSJiri Pirko FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic); 134833fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 134977b9900eSJiri Pirko FLOW_DISSECTOR_KEY_ETH_ADDRS, eth); 135033fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 135177b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); 135233fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 135377b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); 13545c72299fSAmritha Nambiar if (FL_KEY_IS_MASKED(mask, tp) || 13555c72299fSAmritha Nambiar FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max)) 13565c72299fSAmritha Nambiar FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp); 135733fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13584d80cc0aSOr Gerlitz FLOW_DISSECTOR_KEY_IP, ip); 135933fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1360fdfc7dd6SJiri Pirko FLOW_DISSECTOR_KEY_TCP, tcp); 136133fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13627b684884SSimon Horman FLOW_DISSECTOR_KEY_ICMP, icmp); 136333fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 136499d31326SSimon Horman FLOW_DISSECTOR_KEY_ARP, arp); 136533fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1366a577d8f7SBenjamin LaHaise FLOW_DISSECTOR_KEY_MPLS, mpls); 136733fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13689399ae9aSHadar Hen Zion FLOW_DISSECTOR_KEY_VLAN, vlan); 136933fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1370d64efd09SJianbo Liu FLOW_DISSECTOR_KEY_CVLAN, cvlan); 137133fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1372519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); 137333fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1374519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); 137533fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1376519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); 137733fb5cbaSJiri Pirko if (FL_KEY_IS_MASKED(mask, enc_ipv4) || 137833fb5cbaSJiri Pirko FL_KEY_IS_MASKED(mask, enc_ipv6)) 1379519d1052SHadar Hen Zion FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, 1380519d1052SHadar Hen Zion enc_control); 138133fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1382f4d997fdSHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); 138333fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13840e2c17b6SOr Gerlitz FLOW_DISSECTOR_KEY_ENC_IP, enc_ip); 13850a6e7778SPieter Jansen van Vuuren FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13860a6e7778SPieter Jansen van Vuuren FLOW_DISSECTOR_KEY_ENC_OPTS, enc_opts); 1387*e0ace68aSPaul Blakey FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1388*e0ace68aSPaul Blakey FLOW_DISSECTOR_KEY_CT, ct); 138977b9900eSJiri Pirko 139033fb5cbaSJiri Pirko skb_flow_dissector_init(dissector, keys, cnt); 139105cd271fSPaul Blakey } 139205cd271fSPaul Blakey 139305cd271fSPaul Blakey static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head, 139405cd271fSPaul Blakey struct fl_flow_mask *mask) 139505cd271fSPaul Blakey { 139605cd271fSPaul Blakey struct fl_flow_mask *newmask; 139705cd271fSPaul Blakey int err; 139805cd271fSPaul Blakey 139905cd271fSPaul Blakey newmask = kzalloc(sizeof(*newmask), GFP_KERNEL); 140005cd271fSPaul Blakey if (!newmask) 140105cd271fSPaul Blakey return ERR_PTR(-ENOMEM); 140205cd271fSPaul Blakey 140305cd271fSPaul Blakey fl_mask_copy(newmask, mask); 140405cd271fSPaul Blakey 14055c72299fSAmritha Nambiar if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) || 14065c72299fSAmritha Nambiar (newmask->key.tp_min.src && newmask->key.tp_max.src)) 14075c72299fSAmritha Nambiar newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE; 14085c72299fSAmritha Nambiar 140905cd271fSPaul Blakey err = fl_init_mask_hashtable(newmask); 141005cd271fSPaul Blakey if (err) 141105cd271fSPaul Blakey goto errout_free; 141205cd271fSPaul Blakey 141333fb5cbaSJiri Pirko fl_init_dissector(&newmask->dissector, &newmask->key); 141405cd271fSPaul Blakey 141505cd271fSPaul Blakey INIT_LIST_HEAD_RCU(&newmask->filters); 141605cd271fSPaul Blakey 1417f48ef4d5SVlad Buslov refcount_set(&newmask->refcnt, 1); 1418195c234dSVlad Buslov err = rhashtable_replace_fast(&head->ht, &mask->ht_node, 1419195c234dSVlad Buslov &newmask->ht_node, mask_ht_params); 142005cd271fSPaul Blakey if (err) 142105cd271fSPaul Blakey goto errout_destroy; 142205cd271fSPaul Blakey 1423259e60f9SVlad Buslov spin_lock(&head->masks_lock); 142405cd271fSPaul Blakey list_add_tail_rcu(&newmask->list, &head->masks); 1425259e60f9SVlad Buslov spin_unlock(&head->masks_lock); 142605cd271fSPaul Blakey 142705cd271fSPaul Blakey return newmask; 142805cd271fSPaul Blakey 142905cd271fSPaul Blakey errout_destroy: 143005cd271fSPaul Blakey rhashtable_destroy(&newmask->ht); 143105cd271fSPaul Blakey errout_free: 143205cd271fSPaul Blakey kfree(newmask); 143305cd271fSPaul Blakey 143405cd271fSPaul Blakey return ERR_PTR(err); 143577b9900eSJiri Pirko } 143677b9900eSJiri Pirko 143777b9900eSJiri Pirko static int fl_check_assign_mask(struct cls_fl_head *head, 143805cd271fSPaul Blakey struct cls_fl_filter *fnew, 143905cd271fSPaul Blakey struct cls_fl_filter *fold, 144077b9900eSJiri Pirko struct fl_flow_mask *mask) 144177b9900eSJiri Pirko { 144205cd271fSPaul Blakey struct fl_flow_mask *newmask; 1443f48ef4d5SVlad Buslov int ret = 0; 144477b9900eSJiri Pirko 1445f48ef4d5SVlad Buslov rcu_read_lock(); 1446195c234dSVlad Buslov 1447195c234dSVlad Buslov /* Insert mask as temporary node to prevent concurrent creation of mask 1448195c234dSVlad Buslov * with same key. Any concurrent lookups with same key will return 144999815f50SVlad Buslov * -EAGAIN because mask's refcnt is zero. 1450195c234dSVlad Buslov */ 1451195c234dSVlad Buslov fnew->mask = rhashtable_lookup_get_insert_fast(&head->ht, 1452195c234dSVlad Buslov &mask->ht_node, 1453195c234dSVlad Buslov mask_ht_params); 145405cd271fSPaul Blakey if (!fnew->mask) { 1455f48ef4d5SVlad Buslov rcu_read_unlock(); 1456f48ef4d5SVlad Buslov 1457195c234dSVlad Buslov if (fold) { 1458195c234dSVlad Buslov ret = -EINVAL; 1459195c234dSVlad Buslov goto errout_cleanup; 1460195c234dSVlad Buslov } 146105cd271fSPaul Blakey 146205cd271fSPaul Blakey newmask = fl_create_new_mask(head, mask); 1463195c234dSVlad Buslov if (IS_ERR(newmask)) { 1464195c234dSVlad Buslov ret = PTR_ERR(newmask); 1465195c234dSVlad Buslov goto errout_cleanup; 1466195c234dSVlad Buslov } 146705cd271fSPaul Blakey 146805cd271fSPaul Blakey fnew->mask = newmask; 146977b9900eSJiri Pirko return 0; 1470195c234dSVlad Buslov } else if (IS_ERR(fnew->mask)) { 1471195c234dSVlad Buslov ret = PTR_ERR(fnew->mask); 1472f48ef4d5SVlad Buslov } else if (fold && fold->mask != fnew->mask) { 1473f48ef4d5SVlad Buslov ret = -EINVAL; 1474f48ef4d5SVlad Buslov } else if (!refcount_inc_not_zero(&fnew->mask->refcnt)) { 1475f48ef4d5SVlad Buslov /* Mask was deleted concurrently, try again */ 1476f48ef4d5SVlad Buslov ret = -EAGAIN; 1477f48ef4d5SVlad Buslov } 1478f48ef4d5SVlad Buslov rcu_read_unlock(); 1479f48ef4d5SVlad Buslov return ret; 1480195c234dSVlad Buslov 1481195c234dSVlad Buslov errout_cleanup: 1482195c234dSVlad Buslov rhashtable_remove_fast(&head->ht, &mask->ht_node, 1483195c234dSVlad Buslov mask_ht_params); 1484195c234dSVlad Buslov return ret; 148577b9900eSJiri Pirko } 148677b9900eSJiri Pirko 148777b9900eSJiri Pirko static int fl_set_parms(struct net *net, struct tcf_proto *tp, 148877b9900eSJiri Pirko struct cls_fl_filter *f, struct fl_flow_mask *mask, 148977b9900eSJiri Pirko unsigned long base, struct nlattr **tb, 149050a56190SAlexander Aring struct nlattr *est, bool ovr, 1491c24e43d8SVlad Buslov struct fl_flow_tmplt *tmplt, bool rtnl_held, 149250a56190SAlexander Aring struct netlink_ext_ack *extack) 149377b9900eSJiri Pirko { 149477b9900eSJiri Pirko int err; 149577b9900eSJiri Pirko 1496c24e43d8SVlad Buslov err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, rtnl_held, 1497ec6743a1SVlad Buslov extack); 149877b9900eSJiri Pirko if (err < 0) 149977b9900eSJiri Pirko return err; 150077b9900eSJiri Pirko 150177b9900eSJiri Pirko if (tb[TCA_FLOWER_CLASSID]) { 150277b9900eSJiri Pirko f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); 1503c24e43d8SVlad Buslov if (!rtnl_held) 1504c24e43d8SVlad Buslov rtnl_lock(); 150577b9900eSJiri Pirko tcf_bind_filter(tp, &f->res, base); 1506c24e43d8SVlad Buslov if (!rtnl_held) 1507c24e43d8SVlad Buslov rtnl_unlock(); 150877b9900eSJiri Pirko } 150977b9900eSJiri Pirko 15101057c55fSAlexander Aring err = fl_set_key(net, tb, &f->key, &mask->key, extack); 151177b9900eSJiri Pirko if (err) 151245507529SJiri Pirko return err; 151377b9900eSJiri Pirko 151477b9900eSJiri Pirko fl_mask_update_range(mask); 151577b9900eSJiri Pirko fl_set_masked_key(&f->mkey, &f->key, mask); 151677b9900eSJiri Pirko 1517b95ec7ebSJiri Pirko if (!fl_mask_fits_tmplt(tmplt, mask)) { 1518b95ec7ebSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Mask does not fit the template"); 1519b95ec7ebSJiri Pirko return -EINVAL; 1520b95ec7ebSJiri Pirko } 1521b95ec7ebSJiri Pirko 152277b9900eSJiri Pirko return 0; 152377b9900eSJiri Pirko } 152477b9900eSJiri Pirko 15251f17f774SVlad Buslov static int fl_ht_insert_unique(struct cls_fl_filter *fnew, 15261f17f774SVlad Buslov struct cls_fl_filter *fold, 15271f17f774SVlad Buslov bool *in_ht) 15281f17f774SVlad Buslov { 15291f17f774SVlad Buslov struct fl_flow_mask *mask = fnew->mask; 15301f17f774SVlad Buslov int err; 15311f17f774SVlad Buslov 15329e35552aSVlad Buslov err = rhashtable_lookup_insert_fast(&mask->ht, 15331f17f774SVlad Buslov &fnew->ht_node, 15341f17f774SVlad Buslov mask->filter_ht_params); 15351f17f774SVlad Buslov if (err) { 15361f17f774SVlad Buslov *in_ht = false; 15371f17f774SVlad Buslov /* It is okay if filter with same key exists when 15381f17f774SVlad Buslov * overwriting. 15391f17f774SVlad Buslov */ 15401f17f774SVlad Buslov return fold && err == -EEXIST ? 0 : err; 15411f17f774SVlad Buslov } 15421f17f774SVlad Buslov 15431f17f774SVlad Buslov *in_ht = true; 15441f17f774SVlad Buslov return 0; 15451f17f774SVlad Buslov } 15461f17f774SVlad Buslov 154777b9900eSJiri Pirko static int fl_change(struct net *net, struct sk_buff *in_skb, 154877b9900eSJiri Pirko struct tcf_proto *tp, unsigned long base, 154977b9900eSJiri Pirko u32 handle, struct nlattr **tca, 155012db03b6SVlad Buslov void **arg, bool ovr, bool rtnl_held, 155112db03b6SVlad Buslov struct netlink_ext_ack *extack) 155277b9900eSJiri Pirko { 1553e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 15548113c095SWANG Cong struct cls_fl_filter *fold = *arg; 155577b9900eSJiri Pirko struct cls_fl_filter *fnew; 15562cddd201SIvan Vecera struct fl_flow_mask *mask; 155739b7b6a6SArnd Bergmann struct nlattr **tb; 15581f17f774SVlad Buslov bool in_ht; 155977b9900eSJiri Pirko int err; 156077b9900eSJiri Pirko 156106177558SVlad Buslov if (!tca[TCA_OPTIONS]) { 156206177558SVlad Buslov err = -EINVAL; 156306177558SVlad Buslov goto errout_fold; 156406177558SVlad Buslov } 156577b9900eSJiri Pirko 15662cddd201SIvan Vecera mask = kzalloc(sizeof(struct fl_flow_mask), GFP_KERNEL); 156706177558SVlad Buslov if (!mask) { 156806177558SVlad Buslov err = -ENOBUFS; 156906177558SVlad Buslov goto errout_fold; 157006177558SVlad Buslov } 157139b7b6a6SArnd Bergmann 15722cddd201SIvan Vecera tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 15732cddd201SIvan Vecera if (!tb) { 15742cddd201SIvan Vecera err = -ENOBUFS; 15752cddd201SIvan Vecera goto errout_mask_alloc; 15762cddd201SIvan Vecera } 15772cddd201SIvan Vecera 15788cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_FLOWER_MAX, 15798cb08174SJohannes Berg tca[TCA_OPTIONS], fl_policy, NULL); 158077b9900eSJiri Pirko if (err < 0) 158139b7b6a6SArnd Bergmann goto errout_tb; 158277b9900eSJiri Pirko 158339b7b6a6SArnd Bergmann if (fold && handle && fold->handle != handle) { 158439b7b6a6SArnd Bergmann err = -EINVAL; 158539b7b6a6SArnd Bergmann goto errout_tb; 158639b7b6a6SArnd Bergmann } 158777b9900eSJiri Pirko 158877b9900eSJiri Pirko fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 158939b7b6a6SArnd Bergmann if (!fnew) { 159039b7b6a6SArnd Bergmann err = -ENOBUFS; 159139b7b6a6SArnd Bergmann goto errout_tb; 159239b7b6a6SArnd Bergmann } 1593c049d56eSVlad Buslov INIT_LIST_HEAD(&fnew->hw_list); 159406177558SVlad Buslov refcount_set(&fnew->refcnt, 1); 159577b9900eSJiri Pirko 159614215108SCong Wang err = tcf_exts_init(&fnew->exts, net, TCA_FLOWER_ACT, 0); 1597b9a24bb7SWANG Cong if (err < 0) 1598b9a24bb7SWANG Cong goto errout; 159977b9900eSJiri Pirko 1600ecb3dea4SVlad Buslov if (tb[TCA_FLOWER_FLAGS]) { 1601ecb3dea4SVlad Buslov fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); 1602ecb3dea4SVlad Buslov 1603ecb3dea4SVlad Buslov if (!tc_flags_valid(fnew->flags)) { 1604ecb3dea4SVlad Buslov err = -EINVAL; 1605ecb3dea4SVlad Buslov goto errout; 1606ecb3dea4SVlad Buslov } 1607ecb3dea4SVlad Buslov } 1608ecb3dea4SVlad Buslov 1609ecb3dea4SVlad Buslov err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE], ovr, 1610c24e43d8SVlad Buslov tp->chain->tmplt_priv, rtnl_held, extack); 1611ecb3dea4SVlad Buslov if (err) 1612ecb3dea4SVlad Buslov goto errout; 1613ecb3dea4SVlad Buslov 1614ecb3dea4SVlad Buslov err = fl_check_assign_mask(head, fnew, fold, mask); 1615ecb3dea4SVlad Buslov if (err) 1616ecb3dea4SVlad Buslov goto errout; 1617ecb3dea4SVlad Buslov 16181f17f774SVlad Buslov err = fl_ht_insert_unique(fnew, fold, &in_ht); 16191f17f774SVlad Buslov if (err) 16201f17f774SVlad Buslov goto errout_mask; 16211f17f774SVlad Buslov 162279685219SHadar Hen Zion if (!tc_skip_hw(fnew->flags)) { 1623c24e43d8SVlad Buslov err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack); 1624e8eb36cdSAmir Vadai if (err) 16251f17f774SVlad Buslov goto errout_ht; 162679685219SHadar Hen Zion } 16275b33f488SAmir Vadai 162855593960SOr Gerlitz if (!tc_in_hw(fnew->flags)) 162955593960SOr Gerlitz fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW; 163055593960SOr Gerlitz 16313d81e711SVlad Buslov spin_lock(&tp->lock); 16323d81e711SVlad Buslov 1633272ffaadSVlad Buslov /* tp was deleted concurrently. -EAGAIN will cause caller to lookup 1634272ffaadSVlad Buslov * proto again or create new one, if necessary. 1635272ffaadSVlad Buslov */ 1636272ffaadSVlad Buslov if (tp->deleting) { 1637272ffaadSVlad Buslov err = -EAGAIN; 1638272ffaadSVlad Buslov goto errout_hw; 1639272ffaadSVlad Buslov } 1640272ffaadSVlad Buslov 16415b33f488SAmir Vadai if (fold) { 1642b2552b8cSVlad Buslov /* Fold filter was deleted concurrently. Retry lookup. */ 1643b2552b8cSVlad Buslov if (fold->deleted) { 1644b2552b8cSVlad Buslov err = -EAGAIN; 1645b2552b8cSVlad Buslov goto errout_hw; 1646b2552b8cSVlad Buslov } 1647b2552b8cSVlad Buslov 1648620da486SVlad Buslov fnew->handle = handle; 1649620da486SVlad Buslov 16501f17f774SVlad Buslov if (!in_ht) { 16511f17f774SVlad Buslov struct rhashtable_params params = 16521f17f774SVlad Buslov fnew->mask->filter_ht_params; 16531f17f774SVlad Buslov 16541f17f774SVlad Buslov err = rhashtable_insert_fast(&fnew->mask->ht, 16551f17f774SVlad Buslov &fnew->ht_node, 16561f17f774SVlad Buslov params); 1657620da486SVlad Buslov if (err) 1658620da486SVlad Buslov goto errout_hw; 16591f17f774SVlad Buslov in_ht = true; 16601f17f774SVlad Buslov } 1661620da486SVlad Buslov 1662c049d56eSVlad Buslov refcount_inc(&fnew->refcnt); 166305cd271fSPaul Blakey rhashtable_remove_fast(&fold->mask->ht, 166405cd271fSPaul Blakey &fold->ht_node, 166505cd271fSPaul Blakey fold->mask->filter_ht_params); 1666234a4624SMatthew Wilcox idr_replace(&head->handle_idr, fnew, fnew->handle); 1667ff3532f2SDaniel Borkmann list_replace_rcu(&fold->list, &fnew->list); 1668b2552b8cSVlad Buslov fold->deleted = true; 1669620da486SVlad Buslov 16703d81e711SVlad Buslov spin_unlock(&tp->lock); 16713d81e711SVlad Buslov 16729994677cSVlad Buslov fl_mask_put(head, fold->mask); 1673620da486SVlad Buslov if (!tc_skip_hw(fold->flags)) 1674c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, fold, rtnl_held, NULL); 167577b9900eSJiri Pirko tcf_unbind_filter(tp, &fold->res); 167606177558SVlad Buslov /* Caller holds reference to fold, so refcnt is always > 0 167706177558SVlad Buslov * after this. 167806177558SVlad Buslov */ 167906177558SVlad Buslov refcount_dec(&fold->refcnt); 168006177558SVlad Buslov __fl_put(fold); 168177b9900eSJiri Pirko } else { 1682620da486SVlad Buslov if (handle) { 1683620da486SVlad Buslov /* user specifies a handle and it doesn't exist */ 1684620da486SVlad Buslov err = idr_alloc_u32(&head->handle_idr, fnew, &handle, 1685620da486SVlad Buslov handle, GFP_ATOMIC); 16869a2d9389SVlad Buslov 16879a2d9389SVlad Buslov /* Filter with specified handle was concurrently 16889a2d9389SVlad Buslov * inserted after initial check in cls_api. This is not 16899a2d9389SVlad Buslov * necessarily an error if NLM_F_EXCL is not set in 16909a2d9389SVlad Buslov * message flags. Returning EAGAIN will cause cls_api to 16919a2d9389SVlad Buslov * try to update concurrently inserted rule. 16929a2d9389SVlad Buslov */ 16939a2d9389SVlad Buslov if (err == -ENOSPC) 16949a2d9389SVlad Buslov err = -EAGAIN; 1695620da486SVlad Buslov } else { 1696620da486SVlad Buslov handle = 1; 1697620da486SVlad Buslov err = idr_alloc_u32(&head->handle_idr, fnew, &handle, 1698620da486SVlad Buslov INT_MAX, GFP_ATOMIC); 1699620da486SVlad Buslov } 1700620da486SVlad Buslov if (err) 1701620da486SVlad Buslov goto errout_hw; 1702620da486SVlad Buslov 1703c049d56eSVlad Buslov refcount_inc(&fnew->refcnt); 1704620da486SVlad Buslov fnew->handle = handle; 170505cd271fSPaul Blakey list_add_tail_rcu(&fnew->list, &fnew->mask->filters); 17063d81e711SVlad Buslov spin_unlock(&tp->lock); 170777b9900eSJiri Pirko } 170877b9900eSJiri Pirko 1709620da486SVlad Buslov *arg = fnew; 1710620da486SVlad Buslov 171139b7b6a6SArnd Bergmann kfree(tb); 171299815f50SVlad Buslov tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); 171377b9900eSJiri Pirko return 0; 171477b9900eSJiri Pirko 1715c049d56eSVlad Buslov errout_ht: 1716c049d56eSVlad Buslov spin_lock(&tp->lock); 1717620da486SVlad Buslov errout_hw: 1718c049d56eSVlad Buslov fnew->deleted = true; 17193d81e711SVlad Buslov spin_unlock(&tp->lock); 1720620da486SVlad Buslov if (!tc_skip_hw(fnew->flags)) 1721c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, fnew, rtnl_held, NULL); 17221f17f774SVlad Buslov if (in_ht) 17231f17f774SVlad Buslov rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node, 17241f17f774SVlad Buslov fnew->mask->filter_ht_params); 1725ecb3dea4SVlad Buslov errout_mask: 17269994677cSVlad Buslov fl_mask_put(head, fnew->mask); 172777b9900eSJiri Pirko errout: 1728c049d56eSVlad Buslov __fl_put(fnew); 172939b7b6a6SArnd Bergmann errout_tb: 173039b7b6a6SArnd Bergmann kfree(tb); 17312cddd201SIvan Vecera errout_mask_alloc: 173299815f50SVlad Buslov tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); 173306177558SVlad Buslov errout_fold: 173406177558SVlad Buslov if (fold) 173506177558SVlad Buslov __fl_put(fold); 173677b9900eSJiri Pirko return err; 173777b9900eSJiri Pirko } 173877b9900eSJiri Pirko 1739571acf21SAlexander Aring static int fl_delete(struct tcf_proto *tp, void *arg, bool *last, 174012db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 174177b9900eSJiri Pirko { 1742e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 17438113c095SWANG Cong struct cls_fl_filter *f = arg; 1744b2552b8cSVlad Buslov bool last_on_mask; 1745b2552b8cSVlad Buslov int err = 0; 174677b9900eSJiri Pirko 1747c24e43d8SVlad Buslov err = __fl_delete(tp, f, &last_on_mask, rtnl_held, extack); 174805cd271fSPaul Blakey *last = list_empty(&head->masks); 174906177558SVlad Buslov __fl_put(f); 175006177558SVlad Buslov 1751b2552b8cSVlad Buslov return err; 175277b9900eSJiri Pirko } 175377b9900eSJiri Pirko 175412db03b6SVlad Buslov static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg, 175512db03b6SVlad Buslov bool rtnl_held) 175677b9900eSJiri Pirko { 1757d39d7149SCong Wang struct cls_fl_head *head = fl_head_dereference(tp); 1758d39d7149SCong Wang unsigned long id = arg->cookie, tmp; 175977b9900eSJiri Pirko struct cls_fl_filter *f; 176077b9900eSJiri Pirko 176101683a14SVlad Buslov arg->count = arg->skip; 176201683a14SVlad Buslov 1763d39d7149SCong Wang idr_for_each_entry_continue_ul(&head->handle_idr, f, tmp, id) { 1764d39d7149SCong Wang /* don't return filters that are being deleted */ 1765d39d7149SCong Wang if (!refcount_inc_not_zero(&f->refcnt)) 1766d39d7149SCong Wang continue; 17678113c095SWANG Cong if (arg->fn(tp, f, arg) < 0) { 176806177558SVlad Buslov __fl_put(f); 176977b9900eSJiri Pirko arg->stop = 1; 177077b9900eSJiri Pirko break; 177177b9900eSJiri Pirko } 177206177558SVlad Buslov __fl_put(f); 177377b9900eSJiri Pirko arg->count++; 177477b9900eSJiri Pirko } 1775d39d7149SCong Wang arg->cookie = id; 177677b9900eSJiri Pirko } 177777b9900eSJiri Pirko 1778c049d56eSVlad Buslov static struct cls_fl_filter * 1779c049d56eSVlad Buslov fl_get_next_hw_filter(struct tcf_proto *tp, struct cls_fl_filter *f, bool add) 1780c049d56eSVlad Buslov { 1781c049d56eSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 1782c049d56eSVlad Buslov 1783c049d56eSVlad Buslov spin_lock(&tp->lock); 1784c049d56eSVlad Buslov if (list_empty(&head->hw_filters)) { 1785c049d56eSVlad Buslov spin_unlock(&tp->lock); 1786c049d56eSVlad Buslov return NULL; 1787c049d56eSVlad Buslov } 1788c049d56eSVlad Buslov 1789c049d56eSVlad Buslov if (!f) 1790c049d56eSVlad Buslov f = list_entry(&head->hw_filters, struct cls_fl_filter, 1791c049d56eSVlad Buslov hw_list); 1792c049d56eSVlad Buslov list_for_each_entry_continue(f, &head->hw_filters, hw_list) { 1793c049d56eSVlad Buslov if (!(add && f->deleted) && refcount_inc_not_zero(&f->refcnt)) { 1794c049d56eSVlad Buslov spin_unlock(&tp->lock); 1795c049d56eSVlad Buslov return f; 1796c049d56eSVlad Buslov } 1797c049d56eSVlad Buslov } 1798c049d56eSVlad Buslov 1799c049d56eSVlad Buslov spin_unlock(&tp->lock); 1800c049d56eSVlad Buslov return NULL; 1801c049d56eSVlad Buslov } 1802c049d56eSVlad Buslov 180331533cbaSJohn Hurley static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb, 180431533cbaSJohn Hurley void *cb_priv, struct netlink_ext_ack *extack) 180531533cbaSJohn Hurley { 180631533cbaSJohn Hurley struct tc_cls_flower_offload cls_flower = {}; 180731533cbaSJohn Hurley struct tcf_block *block = tp->chain->block; 1808c049d56eSVlad Buslov struct cls_fl_filter *f = NULL; 180931533cbaSJohn Hurley int err; 181031533cbaSJohn Hurley 1811c049d56eSVlad Buslov /* hw_filters list can only be changed by hw offload functions after 1812c049d56eSVlad Buslov * obtaining rtnl lock. Make sure it is not changed while reoffload is 1813c049d56eSVlad Buslov * iterating it. 1814c049d56eSVlad Buslov */ 1815c049d56eSVlad Buslov ASSERT_RTNL(); 181631533cbaSJohn Hurley 1817c049d56eSVlad Buslov while ((f = fl_get_next_hw_filter(tp, f, add))) { 1818e3ab786bSPablo Neira Ayuso cls_flower.rule = 1819e3ab786bSPablo Neira Ayuso flow_rule_alloc(tcf_exts_num_actions(&f->exts)); 182095e27a4dSJohn Hurley if (!cls_flower.rule) { 182195e27a4dSJohn Hurley __fl_put(f); 18228f256622SPablo Neira Ayuso return -ENOMEM; 182395e27a4dSJohn Hurley } 18248f256622SPablo Neira Ayuso 182595e27a4dSJohn Hurley tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, 1826d6787147SPieter Jansen van Vuuren extack); 182731533cbaSJohn Hurley cls_flower.command = add ? 182831533cbaSJohn Hurley TC_CLSFLOWER_REPLACE : TC_CLSFLOWER_DESTROY; 182931533cbaSJohn Hurley cls_flower.cookie = (unsigned long)f; 183095e27a4dSJohn Hurley cls_flower.rule->match.dissector = &f->mask->dissector; 183195e27a4dSJohn Hurley cls_flower.rule->match.mask = &f->mask->key; 18328f256622SPablo Neira Ayuso cls_flower.rule->match.key = &f->mkey; 18333a7b6861SPablo Neira Ayuso 183495e27a4dSJohn Hurley err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts); 18353a7b6861SPablo Neira Ayuso if (err) { 18363a7b6861SPablo Neira Ayuso kfree(cls_flower.rule); 18371f15bb4fSVlad Buslov if (tc_skip_sw(f->flags)) { 18381f15bb4fSVlad Buslov NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action"); 183995e27a4dSJohn Hurley __fl_put(f); 18403a7b6861SPablo Neira Ayuso return err; 18413a7b6861SPablo Neira Ayuso } 184295e27a4dSJohn Hurley goto next_flow; 18431f15bb4fSVlad Buslov } 18443a7b6861SPablo Neira Ayuso 184531533cbaSJohn Hurley cls_flower.classid = f->res.classid; 184631533cbaSJohn Hurley 184731533cbaSJohn Hurley err = cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv); 18488f256622SPablo Neira Ayuso kfree(cls_flower.rule); 18498f256622SPablo Neira Ayuso 185031533cbaSJohn Hurley if (err) { 185195e27a4dSJohn Hurley if (add && tc_skip_sw(f->flags)) { 185295e27a4dSJohn Hurley __fl_put(f); 185331533cbaSJohn Hurley return err; 185495e27a4dSJohn Hurley } 185595e27a4dSJohn Hurley goto next_flow; 185631533cbaSJohn Hurley } 185731533cbaSJohn Hurley 18583d81e711SVlad Buslov spin_lock(&tp->lock); 185995e27a4dSJohn Hurley tc_cls_offload_cnt_update(block, &f->in_hw_count, &f->flags, 186095e27a4dSJohn Hurley add); 18613d81e711SVlad Buslov spin_unlock(&tp->lock); 186295e27a4dSJohn Hurley next_flow: 186395e27a4dSJohn Hurley __fl_put(f); 186431533cbaSJohn Hurley } 186531533cbaSJohn Hurley 186631533cbaSJohn Hurley return 0; 186731533cbaSJohn Hurley } 186831533cbaSJohn Hurley 18698f256622SPablo Neira Ayuso static int fl_hw_create_tmplt(struct tcf_chain *chain, 187034738452SJiri Pirko struct fl_flow_tmplt *tmplt) 187134738452SJiri Pirko { 187234738452SJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 187334738452SJiri Pirko struct tcf_block *block = chain->block; 187434738452SJiri Pirko 1875e3ab786bSPablo Neira Ayuso cls_flower.rule = flow_rule_alloc(0); 18768f256622SPablo Neira Ayuso if (!cls_flower.rule) 18778f256622SPablo Neira Ayuso return -ENOMEM; 18788f256622SPablo Neira Ayuso 187934738452SJiri Pirko cls_flower.common.chain_index = chain->index; 188034738452SJiri Pirko cls_flower.command = TC_CLSFLOWER_TMPLT_CREATE; 188134738452SJiri Pirko cls_flower.cookie = (unsigned long) tmplt; 18828f256622SPablo Neira Ayuso cls_flower.rule->match.dissector = &tmplt->dissector; 18838f256622SPablo Neira Ayuso cls_flower.rule->match.mask = &tmplt->mask; 18848f256622SPablo Neira Ayuso cls_flower.rule->match.key = &tmplt->dummy_key; 188534738452SJiri Pirko 188634738452SJiri Pirko /* We don't care if driver (any of them) fails to handle this 188734738452SJiri Pirko * call. It serves just as a hint for it. 188834738452SJiri Pirko */ 1889aeb3fecdSCong Wang tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false); 18908f256622SPablo Neira Ayuso kfree(cls_flower.rule); 18918f256622SPablo Neira Ayuso 18928f256622SPablo Neira Ayuso return 0; 189334738452SJiri Pirko } 189434738452SJiri Pirko 189534738452SJiri Pirko static void fl_hw_destroy_tmplt(struct tcf_chain *chain, 189634738452SJiri Pirko struct fl_flow_tmplt *tmplt) 189734738452SJiri Pirko { 189834738452SJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 189934738452SJiri Pirko struct tcf_block *block = chain->block; 190034738452SJiri Pirko 190134738452SJiri Pirko cls_flower.common.chain_index = chain->index; 190234738452SJiri Pirko cls_flower.command = TC_CLSFLOWER_TMPLT_DESTROY; 190334738452SJiri Pirko cls_flower.cookie = (unsigned long) tmplt; 190434738452SJiri Pirko 1905aeb3fecdSCong Wang tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false); 190634738452SJiri Pirko } 190734738452SJiri Pirko 1908b95ec7ebSJiri Pirko static void *fl_tmplt_create(struct net *net, struct tcf_chain *chain, 1909b95ec7ebSJiri Pirko struct nlattr **tca, 1910b95ec7ebSJiri Pirko struct netlink_ext_ack *extack) 1911b95ec7ebSJiri Pirko { 1912b95ec7ebSJiri Pirko struct fl_flow_tmplt *tmplt; 1913b95ec7ebSJiri Pirko struct nlattr **tb; 1914b95ec7ebSJiri Pirko int err; 1915b95ec7ebSJiri Pirko 1916b95ec7ebSJiri Pirko if (!tca[TCA_OPTIONS]) 1917b95ec7ebSJiri Pirko return ERR_PTR(-EINVAL); 1918b95ec7ebSJiri Pirko 1919b95ec7ebSJiri Pirko tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 1920b95ec7ebSJiri Pirko if (!tb) 1921b95ec7ebSJiri Pirko return ERR_PTR(-ENOBUFS); 19228cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_FLOWER_MAX, 19238cb08174SJohannes Berg tca[TCA_OPTIONS], fl_policy, NULL); 1924b95ec7ebSJiri Pirko if (err) 1925b95ec7ebSJiri Pirko goto errout_tb; 1926b95ec7ebSJiri Pirko 1927b95ec7ebSJiri Pirko tmplt = kzalloc(sizeof(*tmplt), GFP_KERNEL); 19281cbc36a5SDan Carpenter if (!tmplt) { 19291cbc36a5SDan Carpenter err = -ENOMEM; 1930b95ec7ebSJiri Pirko goto errout_tb; 19311cbc36a5SDan Carpenter } 1932b95ec7ebSJiri Pirko tmplt->chain = chain; 1933b95ec7ebSJiri Pirko err = fl_set_key(net, tb, &tmplt->dummy_key, &tmplt->mask, extack); 1934b95ec7ebSJiri Pirko if (err) 1935b95ec7ebSJiri Pirko goto errout_tmplt; 1936b95ec7ebSJiri Pirko 1937b95ec7ebSJiri Pirko fl_init_dissector(&tmplt->dissector, &tmplt->mask); 1938b95ec7ebSJiri Pirko 19398f256622SPablo Neira Ayuso err = fl_hw_create_tmplt(chain, tmplt); 19408f256622SPablo Neira Ayuso if (err) 19418f256622SPablo Neira Ayuso goto errout_tmplt; 194234738452SJiri Pirko 19438f256622SPablo Neira Ayuso kfree(tb); 1944b95ec7ebSJiri Pirko return tmplt; 1945b95ec7ebSJiri Pirko 1946b95ec7ebSJiri Pirko errout_tmplt: 1947b95ec7ebSJiri Pirko kfree(tmplt); 1948b95ec7ebSJiri Pirko errout_tb: 1949b95ec7ebSJiri Pirko kfree(tb); 1950b95ec7ebSJiri Pirko return ERR_PTR(err); 1951b95ec7ebSJiri Pirko } 1952b95ec7ebSJiri Pirko 1953b95ec7ebSJiri Pirko static void fl_tmplt_destroy(void *tmplt_priv) 1954b95ec7ebSJiri Pirko { 1955b95ec7ebSJiri Pirko struct fl_flow_tmplt *tmplt = tmplt_priv; 1956b95ec7ebSJiri Pirko 195795278ddaSCong Wang fl_hw_destroy_tmplt(tmplt->chain, tmplt); 195895278ddaSCong Wang kfree(tmplt); 1959b95ec7ebSJiri Pirko } 1960b95ec7ebSJiri Pirko 196177b9900eSJiri Pirko static int fl_dump_key_val(struct sk_buff *skb, 196277b9900eSJiri Pirko void *val, int val_type, 196377b9900eSJiri Pirko void *mask, int mask_type, int len) 196477b9900eSJiri Pirko { 196577b9900eSJiri Pirko int err; 196677b9900eSJiri Pirko 196777b9900eSJiri Pirko if (!memchr_inv(mask, 0, len)) 196877b9900eSJiri Pirko return 0; 196977b9900eSJiri Pirko err = nla_put(skb, val_type, len, val); 197077b9900eSJiri Pirko if (err) 197177b9900eSJiri Pirko return err; 197277b9900eSJiri Pirko if (mask_type != TCA_FLOWER_UNSPEC) { 197377b9900eSJiri Pirko err = nla_put(skb, mask_type, len, mask); 197477b9900eSJiri Pirko if (err) 197577b9900eSJiri Pirko return err; 197677b9900eSJiri Pirko } 197777b9900eSJiri Pirko return 0; 197877b9900eSJiri Pirko } 197977b9900eSJiri Pirko 19805c72299fSAmritha Nambiar static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key, 19815c72299fSAmritha Nambiar struct fl_flow_key *mask) 19825c72299fSAmritha Nambiar { 19835c72299fSAmritha Nambiar if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN, 19845c72299fSAmritha Nambiar &mask->tp_min.dst, TCA_FLOWER_UNSPEC, 19855c72299fSAmritha Nambiar sizeof(key->tp_min.dst)) || 19865c72299fSAmritha Nambiar fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX, 19875c72299fSAmritha Nambiar &mask->tp_max.dst, TCA_FLOWER_UNSPEC, 19885c72299fSAmritha Nambiar sizeof(key->tp_max.dst)) || 19895c72299fSAmritha Nambiar fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN, 19905c72299fSAmritha Nambiar &mask->tp_min.src, TCA_FLOWER_UNSPEC, 19915c72299fSAmritha Nambiar sizeof(key->tp_min.src)) || 19925c72299fSAmritha Nambiar fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX, 19935c72299fSAmritha Nambiar &mask->tp_max.src, TCA_FLOWER_UNSPEC, 19945c72299fSAmritha Nambiar sizeof(key->tp_max.src))) 19955c72299fSAmritha Nambiar return -1; 19965c72299fSAmritha Nambiar 19975c72299fSAmritha Nambiar return 0; 19985c72299fSAmritha Nambiar } 19995c72299fSAmritha Nambiar 2000a577d8f7SBenjamin LaHaise static int fl_dump_key_mpls(struct sk_buff *skb, 2001a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *mpls_key, 2002a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *mpls_mask) 2003a577d8f7SBenjamin LaHaise { 2004a577d8f7SBenjamin LaHaise int err; 2005a577d8f7SBenjamin LaHaise 2006a577d8f7SBenjamin LaHaise if (!memchr_inv(mpls_mask, 0, sizeof(*mpls_mask))) 2007a577d8f7SBenjamin LaHaise return 0; 2008a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_ttl) { 2009a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TTL, 2010a577d8f7SBenjamin LaHaise mpls_key->mpls_ttl); 2011a577d8f7SBenjamin LaHaise if (err) 2012a577d8f7SBenjamin LaHaise return err; 2013a577d8f7SBenjamin LaHaise } 2014a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_tc) { 2015a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TC, 2016a577d8f7SBenjamin LaHaise mpls_key->mpls_tc); 2017a577d8f7SBenjamin LaHaise if (err) 2018a577d8f7SBenjamin LaHaise return err; 2019a577d8f7SBenjamin LaHaise } 2020a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_label) { 2021a577d8f7SBenjamin LaHaise err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_LABEL, 2022a577d8f7SBenjamin LaHaise mpls_key->mpls_label); 2023a577d8f7SBenjamin LaHaise if (err) 2024a577d8f7SBenjamin LaHaise return err; 2025a577d8f7SBenjamin LaHaise } 2026a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_bos) { 2027a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_BOS, 2028a577d8f7SBenjamin LaHaise mpls_key->mpls_bos); 2029a577d8f7SBenjamin LaHaise if (err) 2030a577d8f7SBenjamin LaHaise return err; 2031a577d8f7SBenjamin LaHaise } 2032a577d8f7SBenjamin LaHaise return 0; 2033a577d8f7SBenjamin LaHaise } 2034a577d8f7SBenjamin LaHaise 20350e2c17b6SOr Gerlitz static int fl_dump_key_ip(struct sk_buff *skb, bool encap, 20364d80cc0aSOr Gerlitz struct flow_dissector_key_ip *key, 20374d80cc0aSOr Gerlitz struct flow_dissector_key_ip *mask) 20384d80cc0aSOr Gerlitz { 20390e2c17b6SOr Gerlitz int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 20400e2c17b6SOr Gerlitz int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 20410e2c17b6SOr Gerlitz int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 20420e2c17b6SOr Gerlitz int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 20430e2c17b6SOr Gerlitz 20440e2c17b6SOr Gerlitz if (fl_dump_key_val(skb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)) || 20450e2c17b6SOr Gerlitz fl_dump_key_val(skb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl))) 20464d80cc0aSOr Gerlitz return -1; 20474d80cc0aSOr Gerlitz 20484d80cc0aSOr Gerlitz return 0; 20494d80cc0aSOr Gerlitz } 20504d80cc0aSOr Gerlitz 20519399ae9aSHadar Hen Zion static int fl_dump_key_vlan(struct sk_buff *skb, 2052d64efd09SJianbo Liu int vlan_id_key, int vlan_prio_key, 20539399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_key, 20549399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_mask) 20559399ae9aSHadar Hen Zion { 20569399ae9aSHadar Hen Zion int err; 20579399ae9aSHadar Hen Zion 20589399ae9aSHadar Hen Zion if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask))) 20599399ae9aSHadar Hen Zion return 0; 20609399ae9aSHadar Hen Zion if (vlan_mask->vlan_id) { 2061d64efd09SJianbo Liu err = nla_put_u16(skb, vlan_id_key, 20629399ae9aSHadar Hen Zion vlan_key->vlan_id); 20639399ae9aSHadar Hen Zion if (err) 20649399ae9aSHadar Hen Zion return err; 20659399ae9aSHadar Hen Zion } 20669399ae9aSHadar Hen Zion if (vlan_mask->vlan_priority) { 2067d64efd09SJianbo Liu err = nla_put_u8(skb, vlan_prio_key, 20689399ae9aSHadar Hen Zion vlan_key->vlan_priority); 20699399ae9aSHadar Hen Zion if (err) 20709399ae9aSHadar Hen Zion return err; 20719399ae9aSHadar Hen Zion } 20729399ae9aSHadar Hen Zion return 0; 20739399ae9aSHadar Hen Zion } 20749399ae9aSHadar Hen Zion 2075faa3ffceSOr Gerlitz static void fl_get_key_flag(u32 dissector_key, u32 dissector_mask, 2076faa3ffceSOr Gerlitz u32 *flower_key, u32 *flower_mask, 2077faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 2078faa3ffceSOr Gerlitz { 2079faa3ffceSOr Gerlitz if (dissector_mask & dissector_flag_bit) { 2080faa3ffceSOr Gerlitz *flower_mask |= flower_flag_bit; 2081faa3ffceSOr Gerlitz if (dissector_key & dissector_flag_bit) 2082faa3ffceSOr Gerlitz *flower_key |= flower_flag_bit; 2083faa3ffceSOr Gerlitz } 2084faa3ffceSOr Gerlitz } 2085faa3ffceSOr Gerlitz 2086faa3ffceSOr Gerlitz static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) 2087faa3ffceSOr Gerlitz { 2088faa3ffceSOr Gerlitz u32 key, mask; 2089faa3ffceSOr Gerlitz __be32 _key, _mask; 2090faa3ffceSOr Gerlitz int err; 2091faa3ffceSOr Gerlitz 2092faa3ffceSOr Gerlitz if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask))) 2093faa3ffceSOr Gerlitz return 0; 2094faa3ffceSOr Gerlitz 2095faa3ffceSOr Gerlitz key = 0; 2096faa3ffceSOr Gerlitz mask = 0; 2097faa3ffceSOr Gerlitz 2098faa3ffceSOr Gerlitz fl_get_key_flag(flags_key, flags_mask, &key, &mask, 2099faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 2100459d153dSPieter Jansen van Vuuren fl_get_key_flag(flags_key, flags_mask, &key, &mask, 2101459d153dSPieter Jansen van Vuuren TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 2102459d153dSPieter Jansen van Vuuren FLOW_DIS_FIRST_FRAG); 2103faa3ffceSOr Gerlitz 2104faa3ffceSOr Gerlitz _key = cpu_to_be32(key); 2105faa3ffceSOr Gerlitz _mask = cpu_to_be32(mask); 2106faa3ffceSOr Gerlitz 2107faa3ffceSOr Gerlitz err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key); 2108faa3ffceSOr Gerlitz if (err) 2109faa3ffceSOr Gerlitz return err; 2110faa3ffceSOr Gerlitz 2111faa3ffceSOr Gerlitz return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); 2112faa3ffceSOr Gerlitz } 2113faa3ffceSOr Gerlitz 21140a6e7778SPieter Jansen van Vuuren static int fl_dump_key_geneve_opt(struct sk_buff *skb, 21150a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *enc_opts) 21160a6e7778SPieter Jansen van Vuuren { 21170a6e7778SPieter Jansen van Vuuren struct geneve_opt *opt; 21180a6e7778SPieter Jansen van Vuuren struct nlattr *nest; 21190a6e7778SPieter Jansen van Vuuren int opt_off = 0; 21200a6e7778SPieter Jansen van Vuuren 2121ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_GENEVE); 21220a6e7778SPieter Jansen van Vuuren if (!nest) 21230a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21240a6e7778SPieter Jansen van Vuuren 21250a6e7778SPieter Jansen van Vuuren while (enc_opts->len > opt_off) { 21260a6e7778SPieter Jansen van Vuuren opt = (struct geneve_opt *)&enc_opts->data[opt_off]; 21270a6e7778SPieter Jansen van Vuuren 21280a6e7778SPieter Jansen van Vuuren if (nla_put_be16(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, 21290a6e7778SPieter Jansen van Vuuren opt->opt_class)) 21300a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21310a6e7778SPieter Jansen van Vuuren if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, 21320a6e7778SPieter Jansen van Vuuren opt->type)) 21330a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21340a6e7778SPieter Jansen van Vuuren if (nla_put(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, 21350a6e7778SPieter Jansen van Vuuren opt->length * 4, opt->opt_data)) 21360a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21370a6e7778SPieter Jansen van Vuuren 21380a6e7778SPieter Jansen van Vuuren opt_off += sizeof(struct geneve_opt) + opt->length * 4; 21390a6e7778SPieter Jansen van Vuuren } 21400a6e7778SPieter Jansen van Vuuren nla_nest_end(skb, nest); 21410a6e7778SPieter Jansen van Vuuren return 0; 21420a6e7778SPieter Jansen van Vuuren 21430a6e7778SPieter Jansen van Vuuren nla_put_failure: 21440a6e7778SPieter Jansen van Vuuren nla_nest_cancel(skb, nest); 21450a6e7778SPieter Jansen van Vuuren return -EMSGSIZE; 21460a6e7778SPieter Jansen van Vuuren } 21470a6e7778SPieter Jansen van Vuuren 2148*e0ace68aSPaul Blakey static int fl_dump_key_ct(struct sk_buff *skb, 2149*e0ace68aSPaul Blakey struct flow_dissector_key_ct *key, 2150*e0ace68aSPaul Blakey struct flow_dissector_key_ct *mask) 2151*e0ace68aSPaul Blakey { 2152*e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK) && 2153*e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_state, TCA_FLOWER_KEY_CT_STATE, 2154*e0ace68aSPaul Blakey &mask->ct_state, TCA_FLOWER_KEY_CT_STATE_MASK, 2155*e0ace68aSPaul Blakey sizeof(key->ct_state))) 2156*e0ace68aSPaul Blakey goto nla_put_failure; 2157*e0ace68aSPaul Blakey 2158*e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 2159*e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_zone, TCA_FLOWER_KEY_CT_ZONE, 2160*e0ace68aSPaul Blakey &mask->ct_zone, TCA_FLOWER_KEY_CT_ZONE_MASK, 2161*e0ace68aSPaul Blakey sizeof(key->ct_zone))) 2162*e0ace68aSPaul Blakey goto nla_put_failure; 2163*e0ace68aSPaul Blakey 2164*e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 2165*e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_mark, TCA_FLOWER_KEY_CT_MARK, 2166*e0ace68aSPaul Blakey &mask->ct_mark, TCA_FLOWER_KEY_CT_MARK_MASK, 2167*e0ace68aSPaul Blakey sizeof(key->ct_mark))) 2168*e0ace68aSPaul Blakey goto nla_put_failure; 2169*e0ace68aSPaul Blakey 2170*e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 2171*e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_labels, TCA_FLOWER_KEY_CT_LABELS, 2172*e0ace68aSPaul Blakey &mask->ct_labels, TCA_FLOWER_KEY_CT_LABELS_MASK, 2173*e0ace68aSPaul Blakey sizeof(key->ct_labels))) 2174*e0ace68aSPaul Blakey goto nla_put_failure; 2175*e0ace68aSPaul Blakey 2176*e0ace68aSPaul Blakey return 0; 2177*e0ace68aSPaul Blakey 2178*e0ace68aSPaul Blakey nla_put_failure: 2179*e0ace68aSPaul Blakey return -EMSGSIZE; 2180*e0ace68aSPaul Blakey } 2181*e0ace68aSPaul Blakey 21820a6e7778SPieter Jansen van Vuuren static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type, 21830a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *enc_opts) 21840a6e7778SPieter Jansen van Vuuren { 21850a6e7778SPieter Jansen van Vuuren struct nlattr *nest; 21860a6e7778SPieter Jansen van Vuuren int err; 21870a6e7778SPieter Jansen van Vuuren 21880a6e7778SPieter Jansen van Vuuren if (!enc_opts->len) 21890a6e7778SPieter Jansen van Vuuren return 0; 21900a6e7778SPieter Jansen van Vuuren 2191ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, enc_opt_type); 21920a6e7778SPieter Jansen van Vuuren if (!nest) 21930a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21940a6e7778SPieter Jansen van Vuuren 21950a6e7778SPieter Jansen van Vuuren switch (enc_opts->dst_opt_type) { 21960a6e7778SPieter Jansen van Vuuren case TUNNEL_GENEVE_OPT: 21970a6e7778SPieter Jansen van Vuuren err = fl_dump_key_geneve_opt(skb, enc_opts); 21980a6e7778SPieter Jansen van Vuuren if (err) 21990a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 22000a6e7778SPieter Jansen van Vuuren break; 22010a6e7778SPieter Jansen van Vuuren default: 22020a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 22030a6e7778SPieter Jansen van Vuuren } 22040a6e7778SPieter Jansen van Vuuren nla_nest_end(skb, nest); 22050a6e7778SPieter Jansen van Vuuren return 0; 22060a6e7778SPieter Jansen van Vuuren 22070a6e7778SPieter Jansen van Vuuren nla_put_failure: 22080a6e7778SPieter Jansen van Vuuren nla_nest_cancel(skb, nest); 22090a6e7778SPieter Jansen van Vuuren return -EMSGSIZE; 22100a6e7778SPieter Jansen van Vuuren } 22110a6e7778SPieter Jansen van Vuuren 22120a6e7778SPieter Jansen van Vuuren static int fl_dump_key_enc_opt(struct sk_buff *skb, 22130a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *key_opts, 22140a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *msk_opts) 22150a6e7778SPieter Jansen van Vuuren { 22160a6e7778SPieter Jansen van Vuuren int err; 22170a6e7778SPieter Jansen van Vuuren 22180a6e7778SPieter Jansen van Vuuren err = fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS, key_opts); 22190a6e7778SPieter Jansen van Vuuren if (err) 22200a6e7778SPieter Jansen van Vuuren return err; 22210a6e7778SPieter Jansen van Vuuren 22220a6e7778SPieter Jansen van Vuuren return fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS_MASK, msk_opts); 22230a6e7778SPieter Jansen van Vuuren } 22240a6e7778SPieter Jansen van Vuuren 2225f5749081SJiri Pirko static int fl_dump_key(struct sk_buff *skb, struct net *net, 2226f5749081SJiri Pirko struct fl_flow_key *key, struct fl_flow_key *mask) 222777b9900eSJiri Pirko { 22288212ed77SJiri Pirko if (mask->meta.ingress_ifindex) { 222977b9900eSJiri Pirko struct net_device *dev; 223077b9900eSJiri Pirko 22318212ed77SJiri Pirko dev = __dev_get_by_index(net, key->meta.ingress_ifindex); 223277b9900eSJiri Pirko if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name)) 223377b9900eSJiri Pirko goto nla_put_failure; 223477b9900eSJiri Pirko } 223577b9900eSJiri Pirko 223677b9900eSJiri Pirko if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 223777b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 223877b9900eSJiri Pirko sizeof(key->eth.dst)) || 223977b9900eSJiri Pirko fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 224077b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 224177b9900eSJiri Pirko sizeof(key->eth.src)) || 224277b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE, 224377b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 224477b9900eSJiri Pirko sizeof(key->basic.n_proto))) 224577b9900eSJiri Pirko goto nla_put_failure; 22469399ae9aSHadar Hen Zion 2247a577d8f7SBenjamin LaHaise if (fl_dump_key_mpls(skb, &key->mpls, &mask->mpls)) 2248a577d8f7SBenjamin LaHaise goto nla_put_failure; 2249a577d8f7SBenjamin LaHaise 2250d64efd09SJianbo Liu if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_VLAN_ID, 2251d64efd09SJianbo Liu TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, &mask->vlan)) 22529399ae9aSHadar Hen Zion goto nla_put_failure; 22539399ae9aSHadar Hen Zion 2254d64efd09SJianbo Liu if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_CVLAN_ID, 2255d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_PRIO, 2256d64efd09SJianbo Liu &key->cvlan, &mask->cvlan) || 2257d64efd09SJianbo Liu (mask->cvlan.vlan_tpid && 2258158abbf1SJianbo Liu nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 2259d64efd09SJianbo Liu key->cvlan.vlan_tpid))) 2260d3069512SJianbo Liu goto nla_put_failure; 2261d3069512SJianbo Liu 22625e9a0fe4SJianbo Liu if (mask->basic.n_proto) { 2263d64efd09SJianbo Liu if (mask->cvlan.vlan_tpid) { 2264d64efd09SJianbo Liu if (nla_put_be16(skb, TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 2265d64efd09SJianbo Liu key->basic.n_proto)) 2266d64efd09SJianbo Liu goto nla_put_failure; 2267d64efd09SJianbo Liu } else if (mask->vlan.vlan_tpid) { 2268d64efd09SJianbo Liu if (nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 2269d64efd09SJianbo Liu key->basic.n_proto)) 2270d64efd09SJianbo Liu goto nla_put_failure; 2271d64efd09SJianbo Liu } 22725e9a0fe4SJianbo Liu } 2273d64efd09SJianbo Liu 227477b9900eSJiri Pirko if ((key->basic.n_proto == htons(ETH_P_IP) || 227577b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) && 22764d80cc0aSOr Gerlitz (fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 227777b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 22784d80cc0aSOr Gerlitz sizeof(key->basic.ip_proto)) || 22790e2c17b6SOr Gerlitz fl_dump_key_ip(skb, false, &key->ip, &mask->ip))) 228077b9900eSJiri Pirko goto nla_put_failure; 228177b9900eSJiri Pirko 2282c3f83241STom Herbert if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 228377b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 228477b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 228577b9900eSJiri Pirko sizeof(key->ipv4.src)) || 228677b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 228777b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 228877b9900eSJiri Pirko sizeof(key->ipv4.dst)))) 228977b9900eSJiri Pirko goto nla_put_failure; 2290c3f83241STom Herbert else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 229177b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 229277b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 229377b9900eSJiri Pirko sizeof(key->ipv6.src)) || 229477b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 229577b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 229677b9900eSJiri Pirko sizeof(key->ipv6.dst)))) 229777b9900eSJiri Pirko goto nla_put_failure; 229877b9900eSJiri Pirko 229977b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP && 230077b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 2301aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 230277b9900eSJiri Pirko sizeof(key->tp.src)) || 230377b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 2304aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 2305fdfc7dd6SJiri Pirko sizeof(key->tp.dst)) || 2306fdfc7dd6SJiri Pirko fl_dump_key_val(skb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 2307fdfc7dd6SJiri Pirko &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 2308fdfc7dd6SJiri Pirko sizeof(key->tcp.flags)))) 230977b9900eSJiri Pirko goto nla_put_failure; 231077b9900eSJiri Pirko else if (key->basic.ip_proto == IPPROTO_UDP && 231177b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 2312aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 231377b9900eSJiri Pirko sizeof(key->tp.src)) || 231477b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 2315aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 231677b9900eSJiri Pirko sizeof(key->tp.dst)))) 231777b9900eSJiri Pirko goto nla_put_failure; 23185976c5f4SSimon Horman else if (key->basic.ip_proto == IPPROTO_SCTP && 23195976c5f4SSimon Horman (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 23205976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 23215976c5f4SSimon Horman sizeof(key->tp.src)) || 23225976c5f4SSimon Horman fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 23235976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 23245976c5f4SSimon Horman sizeof(key->tp.dst)))) 23255976c5f4SSimon Horman goto nla_put_failure; 23267b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IP) && 23277b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP && 23287b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 23297b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type, 23307b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 23317b684884SSimon Horman sizeof(key->icmp.type)) || 23327b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 23337b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code, 23347b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 23357b684884SSimon Horman sizeof(key->icmp.code)))) 23367b684884SSimon Horman goto nla_put_failure; 23377b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IPV6) && 23387b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6 && 23397b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 23407b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type, 23417b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 23427b684884SSimon Horman sizeof(key->icmp.type)) || 23437b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 23447b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code, 23457b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 23467b684884SSimon Horman sizeof(key->icmp.code)))) 23477b684884SSimon Horman goto nla_put_failure; 234899d31326SSimon Horman else if ((key->basic.n_proto == htons(ETH_P_ARP) || 234999d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) && 235099d31326SSimon Horman (fl_dump_key_val(skb, &key->arp.sip, 235199d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP, &mask->arp.sip, 235299d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP_MASK, 235399d31326SSimon Horman sizeof(key->arp.sip)) || 235499d31326SSimon Horman fl_dump_key_val(skb, &key->arp.tip, 235599d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP, &mask->arp.tip, 235699d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP_MASK, 235799d31326SSimon Horman sizeof(key->arp.tip)) || 235899d31326SSimon Horman fl_dump_key_val(skb, &key->arp.op, 235999d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP, &mask->arp.op, 236099d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP_MASK, 236199d31326SSimon Horman sizeof(key->arp.op)) || 236299d31326SSimon Horman fl_dump_key_val(skb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 236399d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 236499d31326SSimon Horman sizeof(key->arp.sha)) || 236599d31326SSimon Horman fl_dump_key_val(skb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 236699d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 236799d31326SSimon Horman sizeof(key->arp.tha)))) 236899d31326SSimon Horman goto nla_put_failure; 236977b9900eSJiri Pirko 23705c72299fSAmritha Nambiar if ((key->basic.ip_proto == IPPROTO_TCP || 23715c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_UDP || 23725c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_SCTP) && 23735c72299fSAmritha Nambiar fl_dump_key_port_range(skb, key, mask)) 23745c72299fSAmritha Nambiar goto nla_put_failure; 23755c72299fSAmritha Nambiar 2376bc3103f1SAmir Vadai if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 2377bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv4.src, 2378bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src, 2379bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 2380bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)) || 2381bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv4.dst, 2382bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst, 2383bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 2384bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)))) 2385bc3103f1SAmir Vadai goto nla_put_failure; 2386bc3103f1SAmir Vadai else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 2387bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv6.src, 2388bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src, 2389bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 2390bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)) || 2391bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv6.dst, 2392bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 2393bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 2394bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 2395bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)))) 2396bc3103f1SAmir Vadai goto nla_put_failure; 2397bc3103f1SAmir Vadai 2398bc3103f1SAmir Vadai if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, 2399eb523f42SHadar Hen Zion &mask->enc_key_id, TCA_FLOWER_UNSPEC, 2400f4d997fdSHadar Hen Zion sizeof(key->enc_key_id)) || 2401f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.src, 2402f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 2403f4d997fdSHadar Hen Zion &mask->enc_tp.src, 2404f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 2405f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)) || 2406f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.dst, 2407f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 2408f4d997fdSHadar Hen Zion &mask->enc_tp.dst, 2409f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 24100e2c17b6SOr Gerlitz sizeof(key->enc_tp.dst)) || 24110a6e7778SPieter Jansen van Vuuren fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip) || 24120a6e7778SPieter Jansen van Vuuren fl_dump_key_enc_opt(skb, &key->enc_opts, &mask->enc_opts)) 2413bc3103f1SAmir Vadai goto nla_put_failure; 2414bc3103f1SAmir Vadai 2415*e0ace68aSPaul Blakey if (fl_dump_key_ct(skb, &key->ct, &mask->ct)) 2416*e0ace68aSPaul Blakey goto nla_put_failure; 2417*e0ace68aSPaul Blakey 2418faa3ffceSOr Gerlitz if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) 2419faa3ffceSOr Gerlitz goto nla_put_failure; 2420faa3ffceSOr Gerlitz 2421f5749081SJiri Pirko return 0; 2422f5749081SJiri Pirko 2423f5749081SJiri Pirko nla_put_failure: 2424f5749081SJiri Pirko return -EMSGSIZE; 2425f5749081SJiri Pirko } 2426f5749081SJiri Pirko 2427f5749081SJiri Pirko static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh, 242812db03b6SVlad Buslov struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 2429f5749081SJiri Pirko { 2430f5749081SJiri Pirko struct cls_fl_filter *f = fh; 2431f5749081SJiri Pirko struct nlattr *nest; 2432f5749081SJiri Pirko struct fl_flow_key *key, *mask; 24333d81e711SVlad Buslov bool skip_hw; 2434f5749081SJiri Pirko 2435f5749081SJiri Pirko if (!f) 2436f5749081SJiri Pirko return skb->len; 2437f5749081SJiri Pirko 2438f5749081SJiri Pirko t->tcm_handle = f->handle; 2439f5749081SJiri Pirko 2440ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 2441f5749081SJiri Pirko if (!nest) 2442f5749081SJiri Pirko goto nla_put_failure; 2443f5749081SJiri Pirko 24443d81e711SVlad Buslov spin_lock(&tp->lock); 24453d81e711SVlad Buslov 2446f5749081SJiri Pirko if (f->res.classid && 2447f5749081SJiri Pirko nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid)) 24483d81e711SVlad Buslov goto nla_put_failure_locked; 2449f5749081SJiri Pirko 2450f5749081SJiri Pirko key = &f->key; 2451f5749081SJiri Pirko mask = &f->mask->key; 24523d81e711SVlad Buslov skip_hw = tc_skip_hw(f->flags); 2453f5749081SJiri Pirko 2454f5749081SJiri Pirko if (fl_dump_key(skb, net, key, mask)) 24553d81e711SVlad Buslov goto nla_put_failure_locked; 2456f5749081SJiri Pirko 2457749e6720SOr Gerlitz if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags)) 24583d81e711SVlad Buslov goto nla_put_failure_locked; 24593d81e711SVlad Buslov 24603d81e711SVlad Buslov spin_unlock(&tp->lock); 24613d81e711SVlad Buslov 24623d81e711SVlad Buslov if (!skip_hw) 2463c24e43d8SVlad Buslov fl_hw_update_stats(tp, f, rtnl_held); 2464e69985c6SAmir Vadai 246586c55361SVlad Buslov if (nla_put_u32(skb, TCA_FLOWER_IN_HW_COUNT, f->in_hw_count)) 246686c55361SVlad Buslov goto nla_put_failure; 246786c55361SVlad Buslov 246877b9900eSJiri Pirko if (tcf_exts_dump(skb, &f->exts)) 246977b9900eSJiri Pirko goto nla_put_failure; 247077b9900eSJiri Pirko 247177b9900eSJiri Pirko nla_nest_end(skb, nest); 247277b9900eSJiri Pirko 247377b9900eSJiri Pirko if (tcf_exts_dump_stats(skb, &f->exts) < 0) 247477b9900eSJiri Pirko goto nla_put_failure; 247577b9900eSJiri Pirko 247677b9900eSJiri Pirko return skb->len; 247777b9900eSJiri Pirko 24783d81e711SVlad Buslov nla_put_failure_locked: 24793d81e711SVlad Buslov spin_unlock(&tp->lock); 248077b9900eSJiri Pirko nla_put_failure: 248177b9900eSJiri Pirko nla_nest_cancel(skb, nest); 248277b9900eSJiri Pirko return -1; 248377b9900eSJiri Pirko } 248477b9900eSJiri Pirko 2485b95ec7ebSJiri Pirko static int fl_tmplt_dump(struct sk_buff *skb, struct net *net, void *tmplt_priv) 2486b95ec7ebSJiri Pirko { 2487b95ec7ebSJiri Pirko struct fl_flow_tmplt *tmplt = tmplt_priv; 2488b95ec7ebSJiri Pirko struct fl_flow_key *key, *mask; 2489b95ec7ebSJiri Pirko struct nlattr *nest; 2490b95ec7ebSJiri Pirko 2491ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 2492b95ec7ebSJiri Pirko if (!nest) 2493b95ec7ebSJiri Pirko goto nla_put_failure; 2494b95ec7ebSJiri Pirko 2495b95ec7ebSJiri Pirko key = &tmplt->dummy_key; 2496b95ec7ebSJiri Pirko mask = &tmplt->mask; 2497b95ec7ebSJiri Pirko 2498b95ec7ebSJiri Pirko if (fl_dump_key(skb, net, key, mask)) 2499b95ec7ebSJiri Pirko goto nla_put_failure; 2500b95ec7ebSJiri Pirko 2501b95ec7ebSJiri Pirko nla_nest_end(skb, nest); 2502b95ec7ebSJiri Pirko 2503b95ec7ebSJiri Pirko return skb->len; 2504b95ec7ebSJiri Pirko 2505b95ec7ebSJiri Pirko nla_put_failure: 2506b95ec7ebSJiri Pirko nla_nest_cancel(skb, nest); 2507b95ec7ebSJiri Pirko return -EMSGSIZE; 2508b95ec7ebSJiri Pirko } 2509b95ec7ebSJiri Pirko 251007d79fc7SCong Wang static void fl_bind_class(void *fh, u32 classid, unsigned long cl) 251107d79fc7SCong Wang { 251207d79fc7SCong Wang struct cls_fl_filter *f = fh; 251307d79fc7SCong Wang 251407d79fc7SCong Wang if (f && f->res.classid == classid) 251507d79fc7SCong Wang f->res.class = cl; 251607d79fc7SCong Wang } 251707d79fc7SCong Wang 251877b9900eSJiri Pirko static struct tcf_proto_ops cls_fl_ops __read_mostly = { 251977b9900eSJiri Pirko .kind = "flower", 252077b9900eSJiri Pirko .classify = fl_classify, 252177b9900eSJiri Pirko .init = fl_init, 252277b9900eSJiri Pirko .destroy = fl_destroy, 252377b9900eSJiri Pirko .get = fl_get, 252406177558SVlad Buslov .put = fl_put, 252577b9900eSJiri Pirko .change = fl_change, 252677b9900eSJiri Pirko .delete = fl_delete, 252777b9900eSJiri Pirko .walk = fl_walk, 252831533cbaSJohn Hurley .reoffload = fl_reoffload, 252977b9900eSJiri Pirko .dump = fl_dump, 253007d79fc7SCong Wang .bind_class = fl_bind_class, 2531b95ec7ebSJiri Pirko .tmplt_create = fl_tmplt_create, 2532b95ec7ebSJiri Pirko .tmplt_destroy = fl_tmplt_destroy, 2533b95ec7ebSJiri Pirko .tmplt_dump = fl_tmplt_dump, 253477b9900eSJiri Pirko .owner = THIS_MODULE, 253592149190SVlad Buslov .flags = TCF_PROTO_OPS_DOIT_UNLOCKED, 253677b9900eSJiri Pirko }; 253777b9900eSJiri Pirko 253877b9900eSJiri Pirko static int __init cls_fl_init(void) 253977b9900eSJiri Pirko { 254077b9900eSJiri Pirko return register_tcf_proto_ops(&cls_fl_ops); 254177b9900eSJiri Pirko } 254277b9900eSJiri Pirko 254377b9900eSJiri Pirko static void __exit cls_fl_exit(void) 254477b9900eSJiri Pirko { 254577b9900eSJiri Pirko unregister_tcf_proto_ops(&cls_fl_ops); 254677b9900eSJiri Pirko } 254777b9900eSJiri Pirko 254877b9900eSJiri Pirko module_init(cls_fl_init); 254977b9900eSJiri Pirko module_exit(cls_fl_exit); 255077b9900eSJiri Pirko 255177b9900eSJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 255277b9900eSJiri Pirko MODULE_DESCRIPTION("Flower classifier"); 255377b9900eSJiri Pirko MODULE_LICENSE("GPL v2"); 2554