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 29e0ace68aSPaul Blakey #include <uapi/linux/netfilter/nf_conntrack_common.h> 30e0ace68aSPaul 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; 59e0ace68aSPaul 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 278e0ace68aSPaul Blakey static u16 fl_ct_info_to_flower_map[] = { 279e0ace68aSPaul Blakey [IP_CT_ESTABLISHED] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 280e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED, 281e0ace68aSPaul Blakey [IP_CT_RELATED] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 282e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_RELATED, 283e0ace68aSPaul Blakey [IP_CT_ESTABLISHED_REPLY] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 284e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED, 285e0ace68aSPaul Blakey [IP_CT_RELATED_REPLY] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 286e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_RELATED, 287e0ace68aSPaul Blakey [IP_CT_NEW] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 288e0ace68aSPaul Blakey TCA_FLOWER_KEY_CT_FLAGS_NEW, 289e0ace68aSPaul Blakey }; 290e0ace68aSPaul 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; 296e0ace68aSPaul Blakey struct fl_flow_key skb_key; 297e0ace68aSPaul Blakey struct fl_flow_mask *mask; 298e0ace68aSPaul 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); 309e0ace68aSPaul Blakey skb_flow_dissect_ct(skb, &mask->dissector, &skb_key, 310e0ace68aSPaul Blakey fl_ct_info_to_flower_map, 311e0ace68aSPaul 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 { 412208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 413f9e30088SPablo Neira Ayuso struct flow_cls_offload cls_flower = {}; 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); 419f9e30088SPablo Neira Ayuso cls_flower.command = FLOW_CLS_DESTROY; 420de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 4215b33f488SAmir Vadai 422*40119211SVlad Buslov tc_setup_cb_destroy(block, tp, TC_SETUP_CLSFLOWER, &cls_flower, false, 423*40119211SVlad Buslov &f->flags, &f->in_hw_count, true); 4243d81e711SVlad Buslov spin_lock(&tp->lock); 425c049d56eSVlad Buslov list_del_init(&f->hw_list); 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); 437208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 438f9e30088SPablo Neira Ayuso struct flow_cls_offload cls_flower = {}; 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); 452f9e30088SPablo Neira Ayuso cls_flower.command = FLOW_CLS_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 469*40119211SVlad Buslov err = tc_setup_cb_add(block, tp, TC_SETUP_CLSFLOWER, &cls_flower, 470*40119211SVlad Buslov skip_sw, &f->flags, &f->in_hw_count, true); 4718f256622SPablo Neira Ayuso kfree(cls_flower.rule); 4728f256622SPablo Neira Ayuso 473*40119211SVlad Buslov if (err) { 474c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, f, true, NULL); 475c24e43d8SVlad Buslov goto errout; 476717503b9SJiri Pirko } 477717503b9SJiri Pirko 478c24e43d8SVlad Buslov if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) { 479c24e43d8SVlad Buslov err = -EINVAL; 480c24e43d8SVlad Buslov goto errout; 4815b33f488SAmir Vadai } 4825b33f488SAmir Vadai 483c049d56eSVlad Buslov spin_lock(&tp->lock); 484c049d56eSVlad Buslov list_add(&f->hw_list, &head->hw_filters); 485c049d56eSVlad Buslov spin_unlock(&tp->lock); 486c24e43d8SVlad Buslov errout: 487c24e43d8SVlad Buslov if (!rtnl_held) 488c24e43d8SVlad Buslov rtnl_unlock(); 489c24e43d8SVlad Buslov 490c24e43d8SVlad Buslov return err; 491c24e43d8SVlad Buslov } 492c24e43d8SVlad Buslov 493c24e43d8SVlad Buslov static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f, 494c24e43d8SVlad Buslov bool rtnl_held) 49510cbc684SAmir Vadai { 496208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 497f9e30088SPablo Neira Ayuso struct flow_cls_offload cls_flower = {}; 49810cbc684SAmir Vadai 499c24e43d8SVlad Buslov if (!rtnl_held) 500c24e43d8SVlad Buslov rtnl_lock(); 501c24e43d8SVlad Buslov 502d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL); 503f9e30088SPablo Neira Ayuso cls_flower.command = FLOW_CLS_STATS; 504de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 505384c181eSAmritha Nambiar cls_flower.classid = f->res.classid; 50610cbc684SAmir Vadai 507*40119211SVlad Buslov tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false, true); 5083b1903efSPablo Neira Ayuso 5093b1903efSPablo Neira Ayuso tcf_exts_stats_update(&f->exts, cls_flower.stats.bytes, 5103b1903efSPablo Neira Ayuso cls_flower.stats.pkts, 5113b1903efSPablo Neira Ayuso cls_flower.stats.lastused); 512c24e43d8SVlad Buslov 513c24e43d8SVlad Buslov if (!rtnl_held) 514c24e43d8SVlad Buslov rtnl_unlock(); 51510cbc684SAmir Vadai } 51610cbc684SAmir Vadai 51706177558SVlad Buslov static void __fl_put(struct cls_fl_filter *f) 51806177558SVlad Buslov { 51906177558SVlad Buslov if (!refcount_dec_and_test(&f->refcnt)) 52006177558SVlad Buslov return; 52106177558SVlad Buslov 52206177558SVlad Buslov if (tcf_exts_get_net(&f->exts)) 52306177558SVlad Buslov tcf_queue_work(&f->rwork, fl_destroy_filter_work); 52406177558SVlad Buslov else 52506177558SVlad Buslov __fl_destroy_filter(f); 52606177558SVlad Buslov } 52706177558SVlad Buslov 52806177558SVlad Buslov static struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle) 52906177558SVlad Buslov { 53006177558SVlad Buslov struct cls_fl_filter *f; 53106177558SVlad Buslov 53206177558SVlad Buslov rcu_read_lock(); 53306177558SVlad Buslov f = idr_find(&head->handle_idr, handle); 53406177558SVlad Buslov if (f && !refcount_inc_not_zero(&f->refcnt)) 53506177558SVlad Buslov f = NULL; 53606177558SVlad Buslov rcu_read_unlock(); 53706177558SVlad Buslov 53806177558SVlad Buslov return f; 53906177558SVlad Buslov } 54006177558SVlad Buslov 541b2552b8cSVlad Buslov static int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, 542c24e43d8SVlad Buslov bool *last, bool rtnl_held, 543c24e43d8SVlad Buslov struct netlink_ext_ack *extack) 54413fa876eSRoi Dayan { 545e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 546c15ab236SChris Mi 547b2552b8cSVlad Buslov *last = false; 548b2552b8cSVlad Buslov 5493d81e711SVlad Buslov spin_lock(&tp->lock); 5503d81e711SVlad Buslov if (f->deleted) { 5513d81e711SVlad Buslov spin_unlock(&tp->lock); 552b2552b8cSVlad Buslov return -ENOENT; 5533d81e711SVlad Buslov } 554b2552b8cSVlad Buslov 555b2552b8cSVlad Buslov f->deleted = true; 556b2552b8cSVlad Buslov rhashtable_remove_fast(&f->mask->ht, &f->ht_node, 557b2552b8cSVlad Buslov f->mask->filter_ht_params); 5589c160941SMatthew Wilcox idr_remove(&head->handle_idr, f->handle); 55913fa876eSRoi Dayan list_del_rcu(&f->list); 5603d81e711SVlad Buslov spin_unlock(&tp->lock); 5613d81e711SVlad Buslov 5629994677cSVlad Buslov *last = fl_mask_put(head, f->mask); 56379685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 564c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, f, rtnl_held, extack); 56513fa876eSRoi Dayan tcf_unbind_filter(tp, &f->res); 56606177558SVlad Buslov __fl_put(f); 56705cd271fSPaul Blakey 568b2552b8cSVlad Buslov return 0; 56913fa876eSRoi Dayan } 57013fa876eSRoi Dayan 571d9363774SDaniel Borkmann static void fl_destroy_sleepable(struct work_struct *work) 572d9363774SDaniel Borkmann { 573aaa908ffSCong Wang struct cls_fl_head *head = container_of(to_rcu_work(work), 574aaa908ffSCong Wang struct cls_fl_head, 575aaa908ffSCong Wang rwork); 576de9dc650SPaul Blakey 577de9dc650SPaul Blakey rhashtable_destroy(&head->ht); 578d9363774SDaniel Borkmann kfree(head); 579d9363774SDaniel Borkmann module_put(THIS_MODULE); 580d9363774SDaniel Borkmann } 581d9363774SDaniel Borkmann 58212db03b6SVlad Buslov static void fl_destroy(struct tcf_proto *tp, bool rtnl_held, 58312db03b6SVlad Buslov struct netlink_ext_ack *extack) 58477b9900eSJiri Pirko { 585e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 58605cd271fSPaul Blakey struct fl_flow_mask *mask, *next_mask; 58777b9900eSJiri Pirko struct cls_fl_filter *f, *next; 588b2552b8cSVlad Buslov bool last; 58977b9900eSJiri Pirko 59005cd271fSPaul Blakey list_for_each_entry_safe(mask, next_mask, &head->masks, list) { 59105cd271fSPaul Blakey list_for_each_entry_safe(f, next, &mask->filters, list) { 592c24e43d8SVlad Buslov __fl_delete(tp, f, &last, rtnl_held, extack); 593b2552b8cSVlad Buslov if (last) 59405cd271fSPaul Blakey break; 59505cd271fSPaul Blakey } 59605cd271fSPaul Blakey } 597c15ab236SChris Mi idr_destroy(&head->handle_idr); 598d9363774SDaniel Borkmann 599d9363774SDaniel Borkmann __module_get(THIS_MODULE); 600aaa908ffSCong Wang tcf_queue_work(&head->rwork, fl_destroy_sleepable); 60177b9900eSJiri Pirko } 60277b9900eSJiri Pirko 60306177558SVlad Buslov static void fl_put(struct tcf_proto *tp, void *arg) 60406177558SVlad Buslov { 60506177558SVlad Buslov struct cls_fl_filter *f = arg; 60606177558SVlad Buslov 60706177558SVlad Buslov __fl_put(f); 60806177558SVlad Buslov } 60906177558SVlad Buslov 6108113c095SWANG Cong static void *fl_get(struct tcf_proto *tp, u32 handle) 61177b9900eSJiri Pirko { 612e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 61377b9900eSJiri Pirko 61406177558SVlad Buslov return __fl_get(head, handle); 61577b9900eSJiri Pirko } 61677b9900eSJiri Pirko 61777b9900eSJiri Pirko static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { 61877b9900eSJiri Pirko [TCA_FLOWER_UNSPEC] = { .type = NLA_UNSPEC }, 61977b9900eSJiri Pirko [TCA_FLOWER_CLASSID] = { .type = NLA_U32 }, 62077b9900eSJiri Pirko [TCA_FLOWER_INDEV] = { .type = NLA_STRING, 62177b9900eSJiri Pirko .len = IFNAMSIZ }, 62277b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST] = { .len = ETH_ALEN }, 62377b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST_MASK] = { .len = ETH_ALEN }, 62477b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC] = { .len = ETH_ALEN }, 62577b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .len = ETH_ALEN }, 62677b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, 62777b9900eSJiri Pirko [TCA_FLOWER_KEY_IP_PROTO] = { .type = NLA_U8 }, 62877b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, 62977b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, 63077b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, 63177b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, 63277b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 63377b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 63477b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 63577b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 63677b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_SRC] = { .type = NLA_U16 }, 63777b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_DST] = { .type = NLA_U16 }, 638b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_SRC] = { .type = NLA_U16 }, 639b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_DST] = { .type = NLA_U16 }, 6409399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, 6419399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, 6429399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, 643bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, 644bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, 645bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 }, 646bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, 647bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 }, 648bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 649bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 650bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 651bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 652aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NLA_U16 }, 653aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, 654aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, 655aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, 6565976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, 6575976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, 6585976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, 6595976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, 660f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, 661f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, 662f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, 663f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, 664faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, 665faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, 6667b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NLA_U8 }, 6677b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 }, 6687b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NLA_U8 }, 6697b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 }, 6707b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NLA_U8 }, 6717b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 }, 6727b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NLA_U8 }, 6737b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 }, 67499d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP] = { .type = NLA_U32 }, 67599d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NLA_U32 }, 67699d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP] = { .type = NLA_U32 }, 67799d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NLA_U32 }, 67899d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP] = { .type = NLA_U8 }, 67999d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NLA_U8 }, 68099d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA] = { .len = ETH_ALEN }, 68199d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .len = ETH_ALEN }, 68299d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA] = { .len = ETH_ALEN }, 68399d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA_MASK] = { .len = ETH_ALEN }, 684a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NLA_U8 }, 685a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NLA_U8 }, 686a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_TC] = { .type = NLA_U8 }, 687a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NLA_U32 }, 688fdfc7dd6SJiri Pirko [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NLA_U16 }, 689fdfc7dd6SJiri Pirko [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NLA_U16 }, 6904d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TOS] = { .type = NLA_U8 }, 6914d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NLA_U8 }, 6924d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TTL] = { .type = NLA_U8 }, 6934d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NLA_U8 }, 694d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NLA_U16 }, 695d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NLA_U8 }, 696d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NLA_U16 }, 6970e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NLA_U8 }, 6980e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NLA_U8 }, 6990e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NLA_U8 }, 7000e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 }, 7010a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NLA_NESTED }, 7020a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NLA_NESTED }, 703e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_STATE] = { .type = NLA_U16 }, 704e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_STATE_MASK] = { .type = NLA_U16 }, 705e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_ZONE] = { .type = NLA_U16 }, 706e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NLA_U16 }, 707e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_MARK] = { .type = NLA_U32 }, 708e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NLA_U32 }, 709e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_LABELS] = { .type = NLA_BINARY, 710e0ace68aSPaul Blakey .len = 128 / BITS_PER_BYTE }, 711e0ace68aSPaul Blakey [TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NLA_BINARY, 712e0ace68aSPaul Blakey .len = 128 / BITS_PER_BYTE }, 7130a6e7778SPieter Jansen van Vuuren }; 7140a6e7778SPieter Jansen van Vuuren 7150a6e7778SPieter Jansen van Vuuren static const struct nla_policy 7160a6e7778SPieter Jansen van Vuuren enc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = { 7170a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED }, 7180a6e7778SPieter Jansen van Vuuren }; 7190a6e7778SPieter Jansen van Vuuren 7200a6e7778SPieter Jansen van Vuuren static const struct nla_policy 7210a6e7778SPieter Jansen van Vuuren geneve_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1] = { 7220a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] = { .type = NLA_U16 }, 7230a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] = { .type = NLA_U8 }, 7240a6e7778SPieter Jansen van Vuuren [TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA] = { .type = NLA_BINARY, 7250a6e7778SPieter Jansen van Vuuren .len = 128 }, 72677b9900eSJiri Pirko }; 72777b9900eSJiri Pirko 72877b9900eSJiri Pirko static void fl_set_key_val(struct nlattr **tb, 72977b9900eSJiri Pirko void *val, int val_type, 73077b9900eSJiri Pirko void *mask, int mask_type, int len) 73177b9900eSJiri Pirko { 73277b9900eSJiri Pirko if (!tb[val_type]) 73377b9900eSJiri Pirko return; 734e0ace68aSPaul Blakey nla_memcpy(val, tb[val_type], len); 73577b9900eSJiri Pirko if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type]) 73677b9900eSJiri Pirko memset(mask, 0xff, len); 73777b9900eSJiri Pirko else 738e0ace68aSPaul Blakey nla_memcpy(mask, tb[mask_type], len); 73977b9900eSJiri Pirko } 74077b9900eSJiri Pirko 7415c72299fSAmritha Nambiar static int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key, 7425c72299fSAmritha Nambiar struct fl_flow_key *mask) 7435c72299fSAmritha Nambiar { 7445c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_min.dst, 7455c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_min.dst, 7465c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_min.dst)); 7475c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_max.dst, 7485c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_max.dst, 7495c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_max.dst)); 7505c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_min.src, 7515c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_min.src, 7525c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_min.src)); 7535c72299fSAmritha Nambiar fl_set_key_val(tb, &key->tp_max.src, 7545c72299fSAmritha Nambiar TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_max.src, 7555c72299fSAmritha Nambiar TCA_FLOWER_UNSPEC, sizeof(key->tp_max.src)); 7565c72299fSAmritha Nambiar 7575c72299fSAmritha Nambiar if ((mask->tp_min.dst && mask->tp_max.dst && 7585c72299fSAmritha Nambiar htons(key->tp_max.dst) <= htons(key->tp_min.dst)) || 7595c72299fSAmritha Nambiar (mask->tp_min.src && mask->tp_max.src && 7605c72299fSAmritha Nambiar htons(key->tp_max.src) <= htons(key->tp_min.src))) 7615c72299fSAmritha Nambiar return -EINVAL; 7625c72299fSAmritha Nambiar 7635c72299fSAmritha Nambiar return 0; 7645c72299fSAmritha Nambiar } 7655c72299fSAmritha Nambiar 7661a7fca63SBenjamin LaHaise static int fl_set_key_mpls(struct nlattr **tb, 767a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *key_val, 768a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *key_mask) 769a577d8f7SBenjamin LaHaise { 770a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_TTL]) { 771a577d8f7SBenjamin LaHaise key_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]); 772a577d8f7SBenjamin LaHaise key_mask->mpls_ttl = MPLS_TTL_MASK; 773a577d8f7SBenjamin LaHaise } 774a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_BOS]) { 7751a7fca63SBenjamin LaHaise u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); 7761a7fca63SBenjamin LaHaise 7771a7fca63SBenjamin LaHaise if (bos & ~MPLS_BOS_MASK) 7781a7fca63SBenjamin LaHaise return -EINVAL; 7791a7fca63SBenjamin LaHaise key_val->mpls_bos = bos; 780a577d8f7SBenjamin LaHaise key_mask->mpls_bos = MPLS_BOS_MASK; 781a577d8f7SBenjamin LaHaise } 782a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_TC]) { 7831a7fca63SBenjamin LaHaise u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]); 7841a7fca63SBenjamin LaHaise 7851a7fca63SBenjamin LaHaise if (tc & ~MPLS_TC_MASK) 7861a7fca63SBenjamin LaHaise return -EINVAL; 7871a7fca63SBenjamin LaHaise key_val->mpls_tc = tc; 788a577d8f7SBenjamin LaHaise key_mask->mpls_tc = MPLS_TC_MASK; 789a577d8f7SBenjamin LaHaise } 790a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) { 7911a7fca63SBenjamin LaHaise u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]); 7921a7fca63SBenjamin LaHaise 7931a7fca63SBenjamin LaHaise if (label & ~MPLS_LABEL_MASK) 7941a7fca63SBenjamin LaHaise return -EINVAL; 7951a7fca63SBenjamin LaHaise key_val->mpls_label = label; 796a577d8f7SBenjamin LaHaise key_mask->mpls_label = MPLS_LABEL_MASK; 797a577d8f7SBenjamin LaHaise } 7981a7fca63SBenjamin LaHaise return 0; 799a577d8f7SBenjamin LaHaise } 800a577d8f7SBenjamin LaHaise 8019399ae9aSHadar Hen Zion static void fl_set_key_vlan(struct nlattr **tb, 802aaab0834SJianbo Liu __be16 ethertype, 803d64efd09SJianbo Liu int vlan_id_key, int vlan_prio_key, 8049399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_val, 8059399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_mask) 8069399ae9aSHadar Hen Zion { 8079399ae9aSHadar Hen Zion #define VLAN_PRIORITY_MASK 0x7 8089399ae9aSHadar Hen Zion 809d64efd09SJianbo Liu if (tb[vlan_id_key]) { 8109399ae9aSHadar Hen Zion key_val->vlan_id = 811d64efd09SJianbo Liu nla_get_u16(tb[vlan_id_key]) & VLAN_VID_MASK; 8129399ae9aSHadar Hen Zion key_mask->vlan_id = VLAN_VID_MASK; 8139399ae9aSHadar Hen Zion } 814d64efd09SJianbo Liu if (tb[vlan_prio_key]) { 8159399ae9aSHadar Hen Zion key_val->vlan_priority = 816d64efd09SJianbo Liu nla_get_u8(tb[vlan_prio_key]) & 8179399ae9aSHadar Hen Zion VLAN_PRIORITY_MASK; 8189399ae9aSHadar Hen Zion key_mask->vlan_priority = VLAN_PRIORITY_MASK; 8199399ae9aSHadar Hen Zion } 820aaab0834SJianbo Liu key_val->vlan_tpid = ethertype; 821aaab0834SJianbo Liu key_mask->vlan_tpid = cpu_to_be16(~0); 8229399ae9aSHadar Hen Zion } 8239399ae9aSHadar Hen Zion 824faa3ffceSOr Gerlitz static void fl_set_key_flag(u32 flower_key, u32 flower_mask, 825faa3ffceSOr Gerlitz u32 *dissector_key, u32 *dissector_mask, 826faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 827faa3ffceSOr Gerlitz { 828faa3ffceSOr Gerlitz if (flower_mask & flower_flag_bit) { 829faa3ffceSOr Gerlitz *dissector_mask |= dissector_flag_bit; 830faa3ffceSOr Gerlitz if (flower_key & flower_flag_bit) 831faa3ffceSOr Gerlitz *dissector_key |= dissector_flag_bit; 832faa3ffceSOr Gerlitz } 833faa3ffceSOr Gerlitz } 834faa3ffceSOr Gerlitz 835d9724772SOr Gerlitz static int fl_set_key_flags(struct nlattr **tb, 836faa3ffceSOr Gerlitz u32 *flags_key, u32 *flags_mask) 837faa3ffceSOr Gerlitz { 838faa3ffceSOr Gerlitz u32 key, mask; 839faa3ffceSOr Gerlitz 840d9724772SOr Gerlitz /* mask is mandatory for flags */ 841d9724772SOr Gerlitz if (!tb[TCA_FLOWER_KEY_FLAGS_MASK]) 842d9724772SOr Gerlitz return -EINVAL; 843faa3ffceSOr Gerlitz 844faa3ffceSOr Gerlitz key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS])); 845faa3ffceSOr Gerlitz mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK])); 846faa3ffceSOr Gerlitz 847faa3ffceSOr Gerlitz *flags_key = 0; 848faa3ffceSOr Gerlitz *flags_mask = 0; 849faa3ffceSOr Gerlitz 850faa3ffceSOr Gerlitz fl_set_key_flag(key, mask, flags_key, flags_mask, 851faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 852459d153dSPieter Jansen van Vuuren fl_set_key_flag(key, mask, flags_key, flags_mask, 853459d153dSPieter Jansen van Vuuren TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 854459d153dSPieter Jansen van Vuuren FLOW_DIS_FIRST_FRAG); 855d9724772SOr Gerlitz 856d9724772SOr Gerlitz return 0; 857faa3ffceSOr Gerlitz } 858faa3ffceSOr Gerlitz 8590e2c17b6SOr Gerlitz static void fl_set_key_ip(struct nlattr **tb, bool encap, 8604d80cc0aSOr Gerlitz struct flow_dissector_key_ip *key, 8614d80cc0aSOr Gerlitz struct flow_dissector_key_ip *mask) 8624d80cc0aSOr Gerlitz { 8630e2c17b6SOr Gerlitz int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 8640e2c17b6SOr Gerlitz int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 8650e2c17b6SOr Gerlitz int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 8660e2c17b6SOr Gerlitz int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 8674d80cc0aSOr Gerlitz 8680e2c17b6SOr Gerlitz fl_set_key_val(tb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)); 8690e2c17b6SOr Gerlitz fl_set_key_val(tb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl)); 8704d80cc0aSOr Gerlitz } 8714d80cc0aSOr Gerlitz 8720a6e7778SPieter Jansen van Vuuren static int fl_set_geneve_opt(const struct nlattr *nla, struct fl_flow_key *key, 8730a6e7778SPieter Jansen van Vuuren int depth, int option_len, 8740a6e7778SPieter Jansen van Vuuren struct netlink_ext_ack *extack) 8750a6e7778SPieter Jansen van Vuuren { 8760a6e7778SPieter Jansen van Vuuren struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1]; 8770a6e7778SPieter Jansen van Vuuren struct nlattr *class = NULL, *type = NULL, *data = NULL; 8780a6e7778SPieter Jansen van Vuuren struct geneve_opt *opt; 8790a6e7778SPieter Jansen van Vuuren int err, data_len = 0; 8800a6e7778SPieter Jansen van Vuuren 8810a6e7778SPieter Jansen van Vuuren if (option_len > sizeof(struct geneve_opt)) 8820a6e7778SPieter Jansen van Vuuren data_len = option_len - sizeof(struct geneve_opt); 8830a6e7778SPieter Jansen van Vuuren 8840a6e7778SPieter Jansen van Vuuren opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len]; 8850a6e7778SPieter Jansen van Vuuren memset(opt, 0xff, option_len); 8860a6e7778SPieter Jansen van Vuuren opt->length = data_len / 4; 8870a6e7778SPieter Jansen van Vuuren opt->r1 = 0; 8880a6e7778SPieter Jansen van Vuuren opt->r2 = 0; 8890a6e7778SPieter Jansen van Vuuren opt->r3 = 0; 8900a6e7778SPieter Jansen van Vuuren 8910a6e7778SPieter Jansen van Vuuren /* If no mask has been prodived we assume an exact match. */ 8920a6e7778SPieter Jansen van Vuuren if (!depth) 8930a6e7778SPieter Jansen van Vuuren return sizeof(struct geneve_opt) + data_len; 8940a6e7778SPieter Jansen van Vuuren 8950a6e7778SPieter Jansen van Vuuren if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GENEVE) { 8960a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Non-geneve option type for mask"); 8970a6e7778SPieter Jansen van Vuuren return -EINVAL; 8980a6e7778SPieter Jansen van Vuuren } 8990a6e7778SPieter Jansen van Vuuren 9008cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, 9018cb08174SJohannes Berg TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, 9020a6e7778SPieter Jansen van Vuuren nla, geneve_opt_policy, extack); 9030a6e7778SPieter Jansen van Vuuren if (err < 0) 9040a6e7778SPieter Jansen van Vuuren return err; 9050a6e7778SPieter Jansen van Vuuren 9060a6e7778SPieter Jansen van Vuuren /* We are not allowed to omit any of CLASS, TYPE or DATA 9070a6e7778SPieter Jansen van Vuuren * fields from the key. 9080a6e7778SPieter Jansen van Vuuren */ 9090a6e7778SPieter Jansen van Vuuren if (!option_len && 9100a6e7778SPieter Jansen van Vuuren (!tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] || 9110a6e7778SPieter Jansen van Vuuren !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] || 9120a6e7778SPieter Jansen van Vuuren !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA])) { 9130a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data"); 9140a6e7778SPieter Jansen van Vuuren return -EINVAL; 9150a6e7778SPieter Jansen van Vuuren } 9160a6e7778SPieter Jansen van Vuuren 9170a6e7778SPieter Jansen van Vuuren /* Omitting any of CLASS, TYPE or DATA fields is allowed 9180a6e7778SPieter Jansen van Vuuren * for the mask. 9190a6e7778SPieter Jansen van Vuuren */ 9200a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]) { 9210a6e7778SPieter Jansen van Vuuren int new_len = key->enc_opts.len; 9220a6e7778SPieter Jansen van Vuuren 9230a6e7778SPieter Jansen van Vuuren data = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]; 9240a6e7778SPieter Jansen van Vuuren data_len = nla_len(data); 9250a6e7778SPieter Jansen van Vuuren if (data_len < 4) { 9260a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long"); 9270a6e7778SPieter Jansen van Vuuren return -ERANGE; 9280a6e7778SPieter Jansen van Vuuren } 9290a6e7778SPieter Jansen van Vuuren if (data_len % 4) { 9300a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long"); 9310a6e7778SPieter Jansen van Vuuren return -ERANGE; 9320a6e7778SPieter Jansen van Vuuren } 9330a6e7778SPieter Jansen van Vuuren 9340a6e7778SPieter Jansen van Vuuren new_len += sizeof(struct geneve_opt) + data_len; 9350a6e7778SPieter Jansen van Vuuren BUILD_BUG_ON(FLOW_DIS_TUN_OPTS_MAX != IP_TUNNEL_OPTS_MAX); 9360a6e7778SPieter Jansen van Vuuren if (new_len > FLOW_DIS_TUN_OPTS_MAX) { 9370a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size"); 9380a6e7778SPieter Jansen van Vuuren return -ERANGE; 9390a6e7778SPieter Jansen van Vuuren } 9400a6e7778SPieter Jansen van Vuuren opt->length = data_len / 4; 9410a6e7778SPieter Jansen van Vuuren memcpy(opt->opt_data, nla_data(data), data_len); 9420a6e7778SPieter Jansen van Vuuren } 9430a6e7778SPieter Jansen van Vuuren 9440a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]) { 9450a6e7778SPieter Jansen van Vuuren class = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]; 9460a6e7778SPieter Jansen van Vuuren opt->opt_class = nla_get_be16(class); 9470a6e7778SPieter Jansen van Vuuren } 9480a6e7778SPieter Jansen van Vuuren 9490a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]) { 9500a6e7778SPieter Jansen van Vuuren type = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]; 9510a6e7778SPieter Jansen van Vuuren opt->type = nla_get_u8(type); 9520a6e7778SPieter Jansen van Vuuren } 9530a6e7778SPieter Jansen van Vuuren 9540a6e7778SPieter Jansen van Vuuren return sizeof(struct geneve_opt) + data_len; 9550a6e7778SPieter Jansen van Vuuren } 9560a6e7778SPieter Jansen van Vuuren 9570a6e7778SPieter Jansen van Vuuren static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, 9580a6e7778SPieter Jansen van Vuuren struct fl_flow_key *mask, 9590a6e7778SPieter Jansen van Vuuren struct netlink_ext_ack *extack) 9600a6e7778SPieter Jansen van Vuuren { 9610a6e7778SPieter Jansen van Vuuren const struct nlattr *nla_enc_key, *nla_opt_key, *nla_opt_msk = NULL; 96263c82997SJakub Kicinski int err, option_len, key_depth, msk_depth = 0; 96363c82997SJakub Kicinski 9648cb08174SJohannes Berg err = nla_validate_nested_deprecated(tb[TCA_FLOWER_KEY_ENC_OPTS], 96563c82997SJakub Kicinski TCA_FLOWER_KEY_ENC_OPTS_MAX, 96663c82997SJakub Kicinski enc_opts_policy, extack); 96763c82997SJakub Kicinski if (err) 96863c82997SJakub Kicinski return err; 9690a6e7778SPieter Jansen van Vuuren 9700a6e7778SPieter Jansen van Vuuren nla_enc_key = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS]); 9710a6e7778SPieter Jansen van Vuuren 9720a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]) { 9738cb08174SJohannes Berg err = nla_validate_nested_deprecated(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK], 97463c82997SJakub Kicinski TCA_FLOWER_KEY_ENC_OPTS_MAX, 97563c82997SJakub Kicinski enc_opts_policy, extack); 97663c82997SJakub Kicinski if (err) 97763c82997SJakub Kicinski return err; 97863c82997SJakub Kicinski 9790a6e7778SPieter Jansen van Vuuren nla_opt_msk = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); 9800a6e7778SPieter Jansen van Vuuren msk_depth = nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); 9810a6e7778SPieter Jansen van Vuuren } 9820a6e7778SPieter Jansen van Vuuren 9830a6e7778SPieter Jansen van Vuuren nla_for_each_attr(nla_opt_key, nla_enc_key, 9840a6e7778SPieter Jansen van Vuuren nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS]), key_depth) { 9850a6e7778SPieter Jansen van Vuuren switch (nla_type(nla_opt_key)) { 9860a6e7778SPieter Jansen van Vuuren case TCA_FLOWER_KEY_ENC_OPTS_GENEVE: 9870a6e7778SPieter Jansen van Vuuren option_len = 0; 9880a6e7778SPieter Jansen van Vuuren key->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT; 9890a6e7778SPieter Jansen van Vuuren option_len = fl_set_geneve_opt(nla_opt_key, key, 9900a6e7778SPieter Jansen van Vuuren key_depth, option_len, 9910a6e7778SPieter Jansen van Vuuren extack); 9920a6e7778SPieter Jansen van Vuuren if (option_len < 0) 9930a6e7778SPieter Jansen van Vuuren return option_len; 9940a6e7778SPieter Jansen van Vuuren 9950a6e7778SPieter Jansen van Vuuren key->enc_opts.len += option_len; 9960a6e7778SPieter Jansen van Vuuren /* At the same time we need to parse through the mask 9970a6e7778SPieter Jansen van Vuuren * in order to verify exact and mask attribute lengths. 9980a6e7778SPieter Jansen van Vuuren */ 9990a6e7778SPieter Jansen van Vuuren mask->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT; 10000a6e7778SPieter Jansen van Vuuren option_len = fl_set_geneve_opt(nla_opt_msk, mask, 10010a6e7778SPieter Jansen van Vuuren msk_depth, option_len, 10020a6e7778SPieter Jansen van Vuuren extack); 10030a6e7778SPieter Jansen van Vuuren if (option_len < 0) 10040a6e7778SPieter Jansen van Vuuren return option_len; 10050a6e7778SPieter Jansen van Vuuren 10060a6e7778SPieter Jansen van Vuuren mask->enc_opts.len += option_len; 10070a6e7778SPieter Jansen van Vuuren if (key->enc_opts.len != mask->enc_opts.len) { 10080a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Key and mask miss aligned"); 10090a6e7778SPieter Jansen van Vuuren return -EINVAL; 10100a6e7778SPieter Jansen van Vuuren } 10110a6e7778SPieter Jansen van Vuuren 10120a6e7778SPieter Jansen van Vuuren if (msk_depth) 10130a6e7778SPieter Jansen van Vuuren nla_opt_msk = nla_next(nla_opt_msk, &msk_depth); 10140a6e7778SPieter Jansen van Vuuren break; 10150a6e7778SPieter Jansen van Vuuren default: 10160a6e7778SPieter Jansen van Vuuren NL_SET_ERR_MSG(extack, "Unknown tunnel option type"); 10170a6e7778SPieter Jansen van Vuuren return -EINVAL; 10180a6e7778SPieter Jansen van Vuuren } 10190a6e7778SPieter Jansen van Vuuren } 10200a6e7778SPieter Jansen van Vuuren 10210a6e7778SPieter Jansen van Vuuren return 0; 10220a6e7778SPieter Jansen van Vuuren } 10230a6e7778SPieter Jansen van Vuuren 1024e0ace68aSPaul Blakey static int fl_set_key_ct(struct nlattr **tb, 1025e0ace68aSPaul Blakey struct flow_dissector_key_ct *key, 1026e0ace68aSPaul Blakey struct flow_dissector_key_ct *mask, 1027e0ace68aSPaul Blakey struct netlink_ext_ack *extack) 1028e0ace68aSPaul Blakey { 1029e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_STATE]) { 1030e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK)) { 1031e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack isn't enabled"); 1032e0ace68aSPaul Blakey return -EOPNOTSUPP; 1033e0ace68aSPaul Blakey } 1034e0ace68aSPaul Blakey fl_set_key_val(tb, &key->ct_state, TCA_FLOWER_KEY_CT_STATE, 1035e0ace68aSPaul Blakey &mask->ct_state, TCA_FLOWER_KEY_CT_STATE_MASK, 1036e0ace68aSPaul Blakey sizeof(key->ct_state)); 1037e0ace68aSPaul Blakey } 1038e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_ZONE]) { 1039e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { 1040e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack zones isn't enabled"); 1041e0ace68aSPaul Blakey return -EOPNOTSUPP; 1042e0ace68aSPaul Blakey } 1043e0ace68aSPaul Blakey fl_set_key_val(tb, &key->ct_zone, TCA_FLOWER_KEY_CT_ZONE, 1044e0ace68aSPaul Blakey &mask->ct_zone, TCA_FLOWER_KEY_CT_ZONE_MASK, 1045e0ace68aSPaul Blakey sizeof(key->ct_zone)); 1046e0ace68aSPaul Blakey } 1047e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_MARK]) { 1048e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)) { 1049e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack mark isn't enabled"); 1050e0ace68aSPaul Blakey return -EOPNOTSUPP; 1051e0ace68aSPaul Blakey } 1052e0ace68aSPaul Blakey fl_set_key_val(tb, &key->ct_mark, TCA_FLOWER_KEY_CT_MARK, 1053e0ace68aSPaul Blakey &mask->ct_mark, TCA_FLOWER_KEY_CT_MARK_MASK, 1054e0ace68aSPaul Blakey sizeof(key->ct_mark)); 1055e0ace68aSPaul Blakey } 1056e0ace68aSPaul Blakey if (tb[TCA_FLOWER_KEY_CT_LABELS]) { 1057e0ace68aSPaul Blakey if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS)) { 1058e0ace68aSPaul Blakey NL_SET_ERR_MSG(extack, "Conntrack labels aren't enabled"); 1059e0ace68aSPaul Blakey return -EOPNOTSUPP; 1060e0ace68aSPaul Blakey } 1061e0ace68aSPaul Blakey fl_set_key_val(tb, key->ct_labels, TCA_FLOWER_KEY_CT_LABELS, 1062e0ace68aSPaul Blakey mask->ct_labels, TCA_FLOWER_KEY_CT_LABELS_MASK, 1063e0ace68aSPaul Blakey sizeof(key->ct_labels)); 1064e0ace68aSPaul Blakey } 1065e0ace68aSPaul Blakey 1066e0ace68aSPaul Blakey return 0; 1067e0ace68aSPaul Blakey } 1068e0ace68aSPaul Blakey 106977b9900eSJiri Pirko static int fl_set_key(struct net *net, struct nlattr **tb, 10701057c55fSAlexander Aring struct fl_flow_key *key, struct fl_flow_key *mask, 10711057c55fSAlexander Aring struct netlink_ext_ack *extack) 107277b9900eSJiri Pirko { 10739399ae9aSHadar Hen Zion __be16 ethertype; 1074d9724772SOr Gerlitz int ret = 0; 1075a5148626SJiri Pirko 107677b9900eSJiri Pirko if (tb[TCA_FLOWER_INDEV]) { 10771057c55fSAlexander Aring int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV], extack); 107877b9900eSJiri Pirko if (err < 0) 107977b9900eSJiri Pirko return err; 10808212ed77SJiri Pirko key->meta.ingress_ifindex = err; 10818212ed77SJiri Pirko mask->meta.ingress_ifindex = 0xffffffff; 108277b9900eSJiri Pirko } 108377b9900eSJiri Pirko 108477b9900eSJiri Pirko fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 108577b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 108677b9900eSJiri Pirko sizeof(key->eth.dst)); 108777b9900eSJiri Pirko fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 108877b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 108977b9900eSJiri Pirko sizeof(key->eth.src)); 109066530bdfSJamal Hadi Salim 10910b498a52SArnd Bergmann if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { 10929399ae9aSHadar Hen Zion ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]); 10939399ae9aSHadar Hen Zion 1094aaab0834SJianbo Liu if (eth_type_vlan(ethertype)) { 1095d64efd09SJianbo Liu fl_set_key_vlan(tb, ethertype, TCA_FLOWER_KEY_VLAN_ID, 1096d64efd09SJianbo Liu TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, 1097d64efd09SJianbo Liu &mask->vlan); 1098d64efd09SJianbo Liu 10995e9a0fe4SJianbo Liu if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { 1100d64efd09SJianbo Liu ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); 1101d64efd09SJianbo Liu if (eth_type_vlan(ethertype)) { 1102d64efd09SJianbo Liu fl_set_key_vlan(tb, ethertype, 1103d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_ID, 1104d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_PRIO, 1105d64efd09SJianbo Liu &key->cvlan, &mask->cvlan); 11069399ae9aSHadar Hen Zion fl_set_key_val(tb, &key->basic.n_proto, 1107d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 1108d64efd09SJianbo Liu &mask->basic.n_proto, 1109d64efd09SJianbo Liu TCA_FLOWER_UNSPEC, 111077b9900eSJiri Pirko sizeof(key->basic.n_proto)); 11119399ae9aSHadar Hen Zion } else { 11129399ae9aSHadar Hen Zion key->basic.n_proto = ethertype; 11139399ae9aSHadar Hen Zion mask->basic.n_proto = cpu_to_be16(~0); 11149399ae9aSHadar Hen Zion } 11155e9a0fe4SJianbo Liu } 1116d64efd09SJianbo Liu } else { 1117d64efd09SJianbo Liu key->basic.n_proto = ethertype; 1118d64efd09SJianbo Liu mask->basic.n_proto = cpu_to_be16(~0); 1119d64efd09SJianbo Liu } 11200b498a52SArnd Bergmann } 112166530bdfSJamal Hadi Salim 112277b9900eSJiri Pirko if (key->basic.n_proto == htons(ETH_P_IP) || 112377b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) { 112477b9900eSJiri Pirko fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 112577b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 112677b9900eSJiri Pirko sizeof(key->basic.ip_proto)); 11270e2c17b6SOr Gerlitz fl_set_key_ip(tb, false, &key->ip, &mask->ip); 112877b9900eSJiri Pirko } 112966530bdfSJamal Hadi Salim 113066530bdfSJamal Hadi Salim if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) { 113166530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 1132970bfcd0SPaul Blakey mask->control.addr_type = ~0; 113377b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 113477b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 113577b9900eSJiri Pirko sizeof(key->ipv4.src)); 113677b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 113777b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 113877b9900eSJiri Pirko sizeof(key->ipv4.dst)); 113966530bdfSJamal Hadi Salim } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) { 114066530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 1141970bfcd0SPaul Blakey mask->control.addr_type = ~0; 114277b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 114377b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 114477b9900eSJiri Pirko sizeof(key->ipv6.src)); 114577b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 114677b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 114777b9900eSJiri Pirko sizeof(key->ipv6.dst)); 114877b9900eSJiri Pirko } 114966530bdfSJamal Hadi Salim 115077b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP) { 115177b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 1152aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 115377b9900eSJiri Pirko sizeof(key->tp.src)); 115477b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 1155aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 115677b9900eSJiri Pirko sizeof(key->tp.dst)); 1157fdfc7dd6SJiri Pirko fl_set_key_val(tb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 1158fdfc7dd6SJiri Pirko &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 1159fdfc7dd6SJiri Pirko sizeof(key->tcp.flags)); 116077b9900eSJiri Pirko } else if (key->basic.ip_proto == IPPROTO_UDP) { 116177b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 1162aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 116377b9900eSJiri Pirko sizeof(key->tp.src)); 116477b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 1165aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 116677b9900eSJiri Pirko sizeof(key->tp.dst)); 11675976c5f4SSimon Horman } else if (key->basic.ip_proto == IPPROTO_SCTP) { 11685976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 11695976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 11705976c5f4SSimon Horman sizeof(key->tp.src)); 11715976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 11725976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 11735976c5f4SSimon Horman sizeof(key->tp.dst)); 11747b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IP) && 11757b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP) { 11767b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE, 11777b684884SSimon Horman &mask->icmp.type, 11787b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 11797b684884SSimon Horman sizeof(key->icmp.type)); 11807b684884SSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, 11817b684884SSimon Horman &mask->icmp.code, 11827b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 11837b684884SSimon Horman sizeof(key->icmp.code)); 11847b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IPV6) && 11857b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6) { 11867b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE, 11877b684884SSimon Horman &mask->icmp.type, 11887b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 11897b684884SSimon Horman sizeof(key->icmp.type)); 1190040587afSSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE, 11917b684884SSimon Horman &mask->icmp.code, 1192040587afSSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 11937b684884SSimon Horman sizeof(key->icmp.code)); 1194a577d8f7SBenjamin LaHaise } else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) || 1195a577d8f7SBenjamin LaHaise key->basic.n_proto == htons(ETH_P_MPLS_MC)) { 11961a7fca63SBenjamin LaHaise ret = fl_set_key_mpls(tb, &key->mpls, &mask->mpls); 11971a7fca63SBenjamin LaHaise if (ret) 11981a7fca63SBenjamin LaHaise return ret; 119999d31326SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_ARP) || 120099d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) { 120199d31326SSimon Horman fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, 120299d31326SSimon Horman &mask->arp.sip, TCA_FLOWER_KEY_ARP_SIP_MASK, 120399d31326SSimon Horman sizeof(key->arp.sip)); 120499d31326SSimon Horman fl_set_key_val(tb, &key->arp.tip, TCA_FLOWER_KEY_ARP_TIP, 120599d31326SSimon Horman &mask->arp.tip, TCA_FLOWER_KEY_ARP_TIP_MASK, 120699d31326SSimon Horman sizeof(key->arp.tip)); 120799d31326SSimon Horman fl_set_key_val(tb, &key->arp.op, TCA_FLOWER_KEY_ARP_OP, 120899d31326SSimon Horman &mask->arp.op, TCA_FLOWER_KEY_ARP_OP_MASK, 120999d31326SSimon Horman sizeof(key->arp.op)); 121099d31326SSimon Horman fl_set_key_val(tb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 121199d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 121299d31326SSimon Horman sizeof(key->arp.sha)); 121399d31326SSimon Horman fl_set_key_val(tb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 121499d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 121599d31326SSimon Horman sizeof(key->arp.tha)); 121677b9900eSJiri Pirko } 121777b9900eSJiri Pirko 12185c72299fSAmritha Nambiar if (key->basic.ip_proto == IPPROTO_TCP || 12195c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_UDP || 12205c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_SCTP) { 12215c72299fSAmritha Nambiar ret = fl_set_key_port_range(tb, key, mask); 12225c72299fSAmritha Nambiar if (ret) 12235c72299fSAmritha Nambiar return ret; 12245c72299fSAmritha Nambiar } 12255c72299fSAmritha Nambiar 1226bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || 1227bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) { 1228bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 1229970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 1230bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.src, 1231bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, 1232bc3103f1SAmir Vadai &mask->enc_ipv4.src, 1233bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 1234bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)); 1235bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.dst, 1236bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, 1237bc3103f1SAmir Vadai &mask->enc_ipv4.dst, 1238bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 1239bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)); 1240bc3103f1SAmir Vadai } 1241bc3103f1SAmir Vadai 1242bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] || 1243bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) { 1244bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 1245970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 1246bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.src, 1247bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, 1248bc3103f1SAmir Vadai &mask->enc_ipv6.src, 1249bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 1250bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)); 1251bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.dst, 1252bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 1253bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 1254bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 1255bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)); 1256bc3103f1SAmir Vadai } 1257bc3103f1SAmir Vadai 1258bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID, 1259eb523f42SHadar Hen Zion &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, 1260bc3103f1SAmir Vadai sizeof(key->enc_key_id.keyid)); 1261bc3103f1SAmir Vadai 1262f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 1263f4d997fdSHadar Hen Zion &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 1264f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)); 1265f4d997fdSHadar Hen Zion 1266f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 1267f4d997fdSHadar Hen Zion &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 1268f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst)); 1269f4d997fdSHadar Hen Zion 12700e2c17b6SOr Gerlitz fl_set_key_ip(tb, true, &key->enc_ip, &mask->enc_ip); 12710e2c17b6SOr Gerlitz 12720a6e7778SPieter Jansen van Vuuren if (tb[TCA_FLOWER_KEY_ENC_OPTS]) { 12730a6e7778SPieter Jansen van Vuuren ret = fl_set_enc_opt(tb, key, mask, extack); 12740a6e7778SPieter Jansen van Vuuren if (ret) 12750a6e7778SPieter Jansen van Vuuren return ret; 12760a6e7778SPieter Jansen van Vuuren } 12770a6e7778SPieter Jansen van Vuuren 1278e0ace68aSPaul Blakey ret = fl_set_key_ct(tb, &key->ct, &mask->ct, extack); 1279e0ace68aSPaul Blakey if (ret) 1280e0ace68aSPaul Blakey return ret; 1281e0ace68aSPaul Blakey 1282d9724772SOr Gerlitz if (tb[TCA_FLOWER_KEY_FLAGS]) 1283d9724772SOr Gerlitz ret = fl_set_key_flags(tb, &key->control.flags, &mask->control.flags); 1284faa3ffceSOr Gerlitz 1285d9724772SOr Gerlitz return ret; 128677b9900eSJiri Pirko } 128777b9900eSJiri Pirko 128805cd271fSPaul Blakey static void fl_mask_copy(struct fl_flow_mask *dst, 128905cd271fSPaul Blakey struct fl_flow_mask *src) 129077b9900eSJiri Pirko { 129105cd271fSPaul Blakey const void *psrc = fl_key_get_start(&src->key, src); 129205cd271fSPaul Blakey void *pdst = fl_key_get_start(&dst->key, src); 129377b9900eSJiri Pirko 129405cd271fSPaul Blakey memcpy(pdst, psrc, fl_mask_range(src)); 129505cd271fSPaul Blakey dst->range = src->range; 129677b9900eSJiri Pirko } 129777b9900eSJiri Pirko 129877b9900eSJiri Pirko static const struct rhashtable_params fl_ht_params = { 129977b9900eSJiri Pirko .key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */ 130077b9900eSJiri Pirko .head_offset = offsetof(struct cls_fl_filter, ht_node), 130177b9900eSJiri Pirko .automatic_shrinking = true, 130277b9900eSJiri Pirko }; 130377b9900eSJiri Pirko 130405cd271fSPaul Blakey static int fl_init_mask_hashtable(struct fl_flow_mask *mask) 130577b9900eSJiri Pirko { 130605cd271fSPaul Blakey mask->filter_ht_params = fl_ht_params; 130705cd271fSPaul Blakey mask->filter_ht_params.key_len = fl_mask_range(mask); 130805cd271fSPaul Blakey mask->filter_ht_params.key_offset += mask->range.start; 130977b9900eSJiri Pirko 131005cd271fSPaul Blakey return rhashtable_init(&mask->ht, &mask->filter_ht_params); 131177b9900eSJiri Pirko } 131277b9900eSJiri Pirko 131377b9900eSJiri Pirko #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) 1314cb205a81Szhong jiang #define FL_KEY_MEMBER_SIZE(member) FIELD_SIZEOF(struct fl_flow_key, member) 131577b9900eSJiri Pirko 1316339ba878SHadar Hen Zion #define FL_KEY_IS_MASKED(mask, member) \ 1317339ba878SHadar Hen Zion memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ 1318339ba878SHadar Hen Zion 0, FL_KEY_MEMBER_SIZE(member)) \ 131977b9900eSJiri Pirko 132077b9900eSJiri Pirko #define FL_KEY_SET(keys, cnt, id, member) \ 132177b9900eSJiri Pirko do { \ 132277b9900eSJiri Pirko keys[cnt].key_id = id; \ 132377b9900eSJiri Pirko keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member); \ 132477b9900eSJiri Pirko cnt++; \ 132577b9900eSJiri Pirko } while(0); 132677b9900eSJiri Pirko 1327339ba878SHadar Hen Zion #define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member) \ 132877b9900eSJiri Pirko do { \ 1329339ba878SHadar Hen Zion if (FL_KEY_IS_MASKED(mask, member)) \ 133077b9900eSJiri Pirko FL_KEY_SET(keys, cnt, id, member); \ 133177b9900eSJiri Pirko } while(0); 133277b9900eSJiri Pirko 133333fb5cbaSJiri Pirko static void fl_init_dissector(struct flow_dissector *dissector, 133433fb5cbaSJiri Pirko struct fl_flow_key *mask) 133577b9900eSJiri Pirko { 133677b9900eSJiri Pirko struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX]; 133777b9900eSJiri Pirko size_t cnt = 0; 133877b9900eSJiri Pirko 13398212ed77SJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13408212ed77SJiri Pirko FLOW_DISSECTOR_KEY_META, meta); 134142aecaa9STom Herbert FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control); 134277b9900eSJiri Pirko FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic); 134333fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 134477b9900eSJiri Pirko FLOW_DISSECTOR_KEY_ETH_ADDRS, eth); 134533fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 134677b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); 134733fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 134877b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); 13495c72299fSAmritha Nambiar if (FL_KEY_IS_MASKED(mask, tp) || 13505c72299fSAmritha Nambiar FL_KEY_IS_MASKED(mask, tp_min) || FL_KEY_IS_MASKED(mask, tp_max)) 13515c72299fSAmritha Nambiar FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_PORTS, tp); 135233fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13534d80cc0aSOr Gerlitz FLOW_DISSECTOR_KEY_IP, ip); 135433fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1355fdfc7dd6SJiri Pirko FLOW_DISSECTOR_KEY_TCP, tcp); 135633fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13577b684884SSimon Horman FLOW_DISSECTOR_KEY_ICMP, icmp); 135833fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 135999d31326SSimon Horman FLOW_DISSECTOR_KEY_ARP, arp); 136033fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1361a577d8f7SBenjamin LaHaise FLOW_DISSECTOR_KEY_MPLS, mpls); 136233fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13639399ae9aSHadar Hen Zion FLOW_DISSECTOR_KEY_VLAN, vlan); 136433fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1365d64efd09SJianbo Liu FLOW_DISSECTOR_KEY_CVLAN, cvlan); 136633fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1367519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); 136833fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1369519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); 137033fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1371519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); 137233fb5cbaSJiri Pirko if (FL_KEY_IS_MASKED(mask, enc_ipv4) || 137333fb5cbaSJiri Pirko FL_KEY_IS_MASKED(mask, enc_ipv6)) 1374519d1052SHadar Hen Zion FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, 1375519d1052SHadar Hen Zion enc_control); 137633fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1377f4d997fdSHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); 137833fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13790e2c17b6SOr Gerlitz FLOW_DISSECTOR_KEY_ENC_IP, enc_ip); 13800a6e7778SPieter Jansen van Vuuren FL_KEY_SET_IF_MASKED(mask, keys, cnt, 13810a6e7778SPieter Jansen van Vuuren FLOW_DISSECTOR_KEY_ENC_OPTS, enc_opts); 1382e0ace68aSPaul Blakey FL_KEY_SET_IF_MASKED(mask, keys, cnt, 1383e0ace68aSPaul Blakey FLOW_DISSECTOR_KEY_CT, ct); 138477b9900eSJiri Pirko 138533fb5cbaSJiri Pirko skb_flow_dissector_init(dissector, keys, cnt); 138605cd271fSPaul Blakey } 138705cd271fSPaul Blakey 138805cd271fSPaul Blakey static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head, 138905cd271fSPaul Blakey struct fl_flow_mask *mask) 139005cd271fSPaul Blakey { 139105cd271fSPaul Blakey struct fl_flow_mask *newmask; 139205cd271fSPaul Blakey int err; 139305cd271fSPaul Blakey 139405cd271fSPaul Blakey newmask = kzalloc(sizeof(*newmask), GFP_KERNEL); 139505cd271fSPaul Blakey if (!newmask) 139605cd271fSPaul Blakey return ERR_PTR(-ENOMEM); 139705cd271fSPaul Blakey 139805cd271fSPaul Blakey fl_mask_copy(newmask, mask); 139905cd271fSPaul Blakey 14005c72299fSAmritha Nambiar if ((newmask->key.tp_min.dst && newmask->key.tp_max.dst) || 14015c72299fSAmritha Nambiar (newmask->key.tp_min.src && newmask->key.tp_max.src)) 14025c72299fSAmritha Nambiar newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE; 14035c72299fSAmritha Nambiar 140405cd271fSPaul Blakey err = fl_init_mask_hashtable(newmask); 140505cd271fSPaul Blakey if (err) 140605cd271fSPaul Blakey goto errout_free; 140705cd271fSPaul Blakey 140833fb5cbaSJiri Pirko fl_init_dissector(&newmask->dissector, &newmask->key); 140905cd271fSPaul Blakey 141005cd271fSPaul Blakey INIT_LIST_HEAD_RCU(&newmask->filters); 141105cd271fSPaul Blakey 1412f48ef4d5SVlad Buslov refcount_set(&newmask->refcnt, 1); 1413195c234dSVlad Buslov err = rhashtable_replace_fast(&head->ht, &mask->ht_node, 1414195c234dSVlad Buslov &newmask->ht_node, mask_ht_params); 141505cd271fSPaul Blakey if (err) 141605cd271fSPaul Blakey goto errout_destroy; 141705cd271fSPaul Blakey 1418259e60f9SVlad Buslov spin_lock(&head->masks_lock); 141905cd271fSPaul Blakey list_add_tail_rcu(&newmask->list, &head->masks); 1420259e60f9SVlad Buslov spin_unlock(&head->masks_lock); 142105cd271fSPaul Blakey 142205cd271fSPaul Blakey return newmask; 142305cd271fSPaul Blakey 142405cd271fSPaul Blakey errout_destroy: 142505cd271fSPaul Blakey rhashtable_destroy(&newmask->ht); 142605cd271fSPaul Blakey errout_free: 142705cd271fSPaul Blakey kfree(newmask); 142805cd271fSPaul Blakey 142905cd271fSPaul Blakey return ERR_PTR(err); 143077b9900eSJiri Pirko } 143177b9900eSJiri Pirko 143277b9900eSJiri Pirko static int fl_check_assign_mask(struct cls_fl_head *head, 143305cd271fSPaul Blakey struct cls_fl_filter *fnew, 143405cd271fSPaul Blakey struct cls_fl_filter *fold, 143577b9900eSJiri Pirko struct fl_flow_mask *mask) 143677b9900eSJiri Pirko { 143705cd271fSPaul Blakey struct fl_flow_mask *newmask; 1438f48ef4d5SVlad Buslov int ret = 0; 143977b9900eSJiri Pirko 1440f48ef4d5SVlad Buslov rcu_read_lock(); 1441195c234dSVlad Buslov 1442195c234dSVlad Buslov /* Insert mask as temporary node to prevent concurrent creation of mask 1443195c234dSVlad Buslov * with same key. Any concurrent lookups with same key will return 144499815f50SVlad Buslov * -EAGAIN because mask's refcnt is zero. 1445195c234dSVlad Buslov */ 1446195c234dSVlad Buslov fnew->mask = rhashtable_lookup_get_insert_fast(&head->ht, 1447195c234dSVlad Buslov &mask->ht_node, 1448195c234dSVlad Buslov mask_ht_params); 144905cd271fSPaul Blakey if (!fnew->mask) { 1450f48ef4d5SVlad Buslov rcu_read_unlock(); 1451f48ef4d5SVlad Buslov 1452195c234dSVlad Buslov if (fold) { 1453195c234dSVlad Buslov ret = -EINVAL; 1454195c234dSVlad Buslov goto errout_cleanup; 1455195c234dSVlad Buslov } 145605cd271fSPaul Blakey 145705cd271fSPaul Blakey newmask = fl_create_new_mask(head, mask); 1458195c234dSVlad Buslov if (IS_ERR(newmask)) { 1459195c234dSVlad Buslov ret = PTR_ERR(newmask); 1460195c234dSVlad Buslov goto errout_cleanup; 1461195c234dSVlad Buslov } 146205cd271fSPaul Blakey 146305cd271fSPaul Blakey fnew->mask = newmask; 146477b9900eSJiri Pirko return 0; 1465195c234dSVlad Buslov } else if (IS_ERR(fnew->mask)) { 1466195c234dSVlad Buslov ret = PTR_ERR(fnew->mask); 1467f48ef4d5SVlad Buslov } else if (fold && fold->mask != fnew->mask) { 1468f48ef4d5SVlad Buslov ret = -EINVAL; 1469f48ef4d5SVlad Buslov } else if (!refcount_inc_not_zero(&fnew->mask->refcnt)) { 1470f48ef4d5SVlad Buslov /* Mask was deleted concurrently, try again */ 1471f48ef4d5SVlad Buslov ret = -EAGAIN; 1472f48ef4d5SVlad Buslov } 1473f48ef4d5SVlad Buslov rcu_read_unlock(); 1474f48ef4d5SVlad Buslov return ret; 1475195c234dSVlad Buslov 1476195c234dSVlad Buslov errout_cleanup: 1477195c234dSVlad Buslov rhashtable_remove_fast(&head->ht, &mask->ht_node, 1478195c234dSVlad Buslov mask_ht_params); 1479195c234dSVlad Buslov return ret; 148077b9900eSJiri Pirko } 148177b9900eSJiri Pirko 148277b9900eSJiri Pirko static int fl_set_parms(struct net *net, struct tcf_proto *tp, 148377b9900eSJiri Pirko struct cls_fl_filter *f, struct fl_flow_mask *mask, 148477b9900eSJiri Pirko unsigned long base, struct nlattr **tb, 148550a56190SAlexander Aring struct nlattr *est, bool ovr, 1486c24e43d8SVlad Buslov struct fl_flow_tmplt *tmplt, bool rtnl_held, 148750a56190SAlexander Aring struct netlink_ext_ack *extack) 148877b9900eSJiri Pirko { 148977b9900eSJiri Pirko int err; 149077b9900eSJiri Pirko 1491c24e43d8SVlad Buslov err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, rtnl_held, 1492ec6743a1SVlad Buslov extack); 149377b9900eSJiri Pirko if (err < 0) 149477b9900eSJiri Pirko return err; 149577b9900eSJiri Pirko 149677b9900eSJiri Pirko if (tb[TCA_FLOWER_CLASSID]) { 149777b9900eSJiri Pirko f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); 1498c24e43d8SVlad Buslov if (!rtnl_held) 1499c24e43d8SVlad Buslov rtnl_lock(); 150077b9900eSJiri Pirko tcf_bind_filter(tp, &f->res, base); 1501c24e43d8SVlad Buslov if (!rtnl_held) 1502c24e43d8SVlad Buslov rtnl_unlock(); 150377b9900eSJiri Pirko } 150477b9900eSJiri Pirko 15051057c55fSAlexander Aring err = fl_set_key(net, tb, &f->key, &mask->key, extack); 150677b9900eSJiri Pirko if (err) 150745507529SJiri Pirko return err; 150877b9900eSJiri Pirko 150977b9900eSJiri Pirko fl_mask_update_range(mask); 151077b9900eSJiri Pirko fl_set_masked_key(&f->mkey, &f->key, mask); 151177b9900eSJiri Pirko 1512b95ec7ebSJiri Pirko if (!fl_mask_fits_tmplt(tmplt, mask)) { 1513b95ec7ebSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Mask does not fit the template"); 1514b95ec7ebSJiri Pirko return -EINVAL; 1515b95ec7ebSJiri Pirko } 1516b95ec7ebSJiri Pirko 151777b9900eSJiri Pirko return 0; 151877b9900eSJiri Pirko } 151977b9900eSJiri Pirko 15201f17f774SVlad Buslov static int fl_ht_insert_unique(struct cls_fl_filter *fnew, 15211f17f774SVlad Buslov struct cls_fl_filter *fold, 15221f17f774SVlad Buslov bool *in_ht) 15231f17f774SVlad Buslov { 15241f17f774SVlad Buslov struct fl_flow_mask *mask = fnew->mask; 15251f17f774SVlad Buslov int err; 15261f17f774SVlad Buslov 15279e35552aSVlad Buslov err = rhashtable_lookup_insert_fast(&mask->ht, 15281f17f774SVlad Buslov &fnew->ht_node, 15291f17f774SVlad Buslov mask->filter_ht_params); 15301f17f774SVlad Buslov if (err) { 15311f17f774SVlad Buslov *in_ht = false; 15321f17f774SVlad Buslov /* It is okay if filter with same key exists when 15331f17f774SVlad Buslov * overwriting. 15341f17f774SVlad Buslov */ 15351f17f774SVlad Buslov return fold && err == -EEXIST ? 0 : err; 15361f17f774SVlad Buslov } 15371f17f774SVlad Buslov 15381f17f774SVlad Buslov *in_ht = true; 15391f17f774SVlad Buslov return 0; 15401f17f774SVlad Buslov } 15411f17f774SVlad Buslov 154277b9900eSJiri Pirko static int fl_change(struct net *net, struct sk_buff *in_skb, 154377b9900eSJiri Pirko struct tcf_proto *tp, unsigned long base, 154477b9900eSJiri Pirko u32 handle, struct nlattr **tca, 154512db03b6SVlad Buslov void **arg, bool ovr, bool rtnl_held, 154612db03b6SVlad Buslov struct netlink_ext_ack *extack) 154777b9900eSJiri Pirko { 1548e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 15498113c095SWANG Cong struct cls_fl_filter *fold = *arg; 155077b9900eSJiri Pirko struct cls_fl_filter *fnew; 15512cddd201SIvan Vecera struct fl_flow_mask *mask; 155239b7b6a6SArnd Bergmann struct nlattr **tb; 15531f17f774SVlad Buslov bool in_ht; 155477b9900eSJiri Pirko int err; 155577b9900eSJiri Pirko 155606177558SVlad Buslov if (!tca[TCA_OPTIONS]) { 155706177558SVlad Buslov err = -EINVAL; 155806177558SVlad Buslov goto errout_fold; 155906177558SVlad Buslov } 156077b9900eSJiri Pirko 15612cddd201SIvan Vecera mask = kzalloc(sizeof(struct fl_flow_mask), GFP_KERNEL); 156206177558SVlad Buslov if (!mask) { 156306177558SVlad Buslov err = -ENOBUFS; 156406177558SVlad Buslov goto errout_fold; 156506177558SVlad Buslov } 156639b7b6a6SArnd Bergmann 15672cddd201SIvan Vecera tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 15682cddd201SIvan Vecera if (!tb) { 15692cddd201SIvan Vecera err = -ENOBUFS; 15702cddd201SIvan Vecera goto errout_mask_alloc; 15712cddd201SIvan Vecera } 15722cddd201SIvan Vecera 15738cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_FLOWER_MAX, 15748cb08174SJohannes Berg tca[TCA_OPTIONS], fl_policy, NULL); 157577b9900eSJiri Pirko if (err < 0) 157639b7b6a6SArnd Bergmann goto errout_tb; 157777b9900eSJiri Pirko 157839b7b6a6SArnd Bergmann if (fold && handle && fold->handle != handle) { 157939b7b6a6SArnd Bergmann err = -EINVAL; 158039b7b6a6SArnd Bergmann goto errout_tb; 158139b7b6a6SArnd Bergmann } 158277b9900eSJiri Pirko 158377b9900eSJiri Pirko fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 158439b7b6a6SArnd Bergmann if (!fnew) { 158539b7b6a6SArnd Bergmann err = -ENOBUFS; 158639b7b6a6SArnd Bergmann goto errout_tb; 158739b7b6a6SArnd Bergmann } 1588c049d56eSVlad Buslov INIT_LIST_HEAD(&fnew->hw_list); 158906177558SVlad Buslov refcount_set(&fnew->refcnt, 1); 159077b9900eSJiri Pirko 159114215108SCong Wang err = tcf_exts_init(&fnew->exts, net, TCA_FLOWER_ACT, 0); 1592b9a24bb7SWANG Cong if (err < 0) 1593b9a24bb7SWANG Cong goto errout; 159477b9900eSJiri Pirko 1595ecb3dea4SVlad Buslov if (tb[TCA_FLOWER_FLAGS]) { 1596ecb3dea4SVlad Buslov fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); 1597ecb3dea4SVlad Buslov 1598ecb3dea4SVlad Buslov if (!tc_flags_valid(fnew->flags)) { 1599ecb3dea4SVlad Buslov err = -EINVAL; 1600ecb3dea4SVlad Buslov goto errout; 1601ecb3dea4SVlad Buslov } 1602ecb3dea4SVlad Buslov } 1603ecb3dea4SVlad Buslov 1604ecb3dea4SVlad Buslov err = fl_set_parms(net, tp, fnew, mask, base, tb, tca[TCA_RATE], ovr, 1605c24e43d8SVlad Buslov tp->chain->tmplt_priv, rtnl_held, extack); 1606ecb3dea4SVlad Buslov if (err) 1607ecb3dea4SVlad Buslov goto errout; 1608ecb3dea4SVlad Buslov 1609ecb3dea4SVlad Buslov err = fl_check_assign_mask(head, fnew, fold, mask); 1610ecb3dea4SVlad Buslov if (err) 1611ecb3dea4SVlad Buslov goto errout; 1612ecb3dea4SVlad Buslov 16131f17f774SVlad Buslov err = fl_ht_insert_unique(fnew, fold, &in_ht); 16141f17f774SVlad Buslov if (err) 16151f17f774SVlad Buslov goto errout_mask; 16161f17f774SVlad Buslov 161779685219SHadar Hen Zion if (!tc_skip_hw(fnew->flags)) { 1618c24e43d8SVlad Buslov err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack); 1619e8eb36cdSAmir Vadai if (err) 16201f17f774SVlad Buslov goto errout_ht; 162179685219SHadar Hen Zion } 16225b33f488SAmir Vadai 162355593960SOr Gerlitz if (!tc_in_hw(fnew->flags)) 162455593960SOr Gerlitz fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW; 162555593960SOr Gerlitz 16263d81e711SVlad Buslov spin_lock(&tp->lock); 16273d81e711SVlad Buslov 1628272ffaadSVlad Buslov /* tp was deleted concurrently. -EAGAIN will cause caller to lookup 1629272ffaadSVlad Buslov * proto again or create new one, if necessary. 1630272ffaadSVlad Buslov */ 1631272ffaadSVlad Buslov if (tp->deleting) { 1632272ffaadSVlad Buslov err = -EAGAIN; 1633272ffaadSVlad Buslov goto errout_hw; 1634272ffaadSVlad Buslov } 1635272ffaadSVlad Buslov 16365b33f488SAmir Vadai if (fold) { 1637b2552b8cSVlad Buslov /* Fold filter was deleted concurrently. Retry lookup. */ 1638b2552b8cSVlad Buslov if (fold->deleted) { 1639b2552b8cSVlad Buslov err = -EAGAIN; 1640b2552b8cSVlad Buslov goto errout_hw; 1641b2552b8cSVlad Buslov } 1642b2552b8cSVlad Buslov 1643620da486SVlad Buslov fnew->handle = handle; 1644620da486SVlad Buslov 16451f17f774SVlad Buslov if (!in_ht) { 16461f17f774SVlad Buslov struct rhashtable_params params = 16471f17f774SVlad Buslov fnew->mask->filter_ht_params; 16481f17f774SVlad Buslov 16491f17f774SVlad Buslov err = rhashtable_insert_fast(&fnew->mask->ht, 16501f17f774SVlad Buslov &fnew->ht_node, 16511f17f774SVlad Buslov params); 1652620da486SVlad Buslov if (err) 1653620da486SVlad Buslov goto errout_hw; 16541f17f774SVlad Buslov in_ht = true; 16551f17f774SVlad Buslov } 1656620da486SVlad Buslov 1657c049d56eSVlad Buslov refcount_inc(&fnew->refcnt); 165805cd271fSPaul Blakey rhashtable_remove_fast(&fold->mask->ht, 165905cd271fSPaul Blakey &fold->ht_node, 166005cd271fSPaul Blakey fold->mask->filter_ht_params); 1661234a4624SMatthew Wilcox idr_replace(&head->handle_idr, fnew, fnew->handle); 1662ff3532f2SDaniel Borkmann list_replace_rcu(&fold->list, &fnew->list); 1663b2552b8cSVlad Buslov fold->deleted = true; 1664620da486SVlad Buslov 16653d81e711SVlad Buslov spin_unlock(&tp->lock); 16663d81e711SVlad Buslov 16679994677cSVlad Buslov fl_mask_put(head, fold->mask); 1668620da486SVlad Buslov if (!tc_skip_hw(fold->flags)) 1669c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, fold, rtnl_held, NULL); 167077b9900eSJiri Pirko tcf_unbind_filter(tp, &fold->res); 167106177558SVlad Buslov /* Caller holds reference to fold, so refcnt is always > 0 167206177558SVlad Buslov * after this. 167306177558SVlad Buslov */ 167406177558SVlad Buslov refcount_dec(&fold->refcnt); 167506177558SVlad Buslov __fl_put(fold); 167677b9900eSJiri Pirko } else { 1677620da486SVlad Buslov if (handle) { 1678620da486SVlad Buslov /* user specifies a handle and it doesn't exist */ 1679620da486SVlad Buslov err = idr_alloc_u32(&head->handle_idr, fnew, &handle, 1680620da486SVlad Buslov handle, GFP_ATOMIC); 16819a2d9389SVlad Buslov 16829a2d9389SVlad Buslov /* Filter with specified handle was concurrently 16839a2d9389SVlad Buslov * inserted after initial check in cls_api. This is not 16849a2d9389SVlad Buslov * necessarily an error if NLM_F_EXCL is not set in 16859a2d9389SVlad Buslov * message flags. Returning EAGAIN will cause cls_api to 16869a2d9389SVlad Buslov * try to update concurrently inserted rule. 16879a2d9389SVlad Buslov */ 16889a2d9389SVlad Buslov if (err == -ENOSPC) 16899a2d9389SVlad Buslov err = -EAGAIN; 1690620da486SVlad Buslov } else { 1691620da486SVlad Buslov handle = 1; 1692620da486SVlad Buslov err = idr_alloc_u32(&head->handle_idr, fnew, &handle, 1693620da486SVlad Buslov INT_MAX, GFP_ATOMIC); 1694620da486SVlad Buslov } 1695620da486SVlad Buslov if (err) 1696620da486SVlad Buslov goto errout_hw; 1697620da486SVlad Buslov 1698c049d56eSVlad Buslov refcount_inc(&fnew->refcnt); 1699620da486SVlad Buslov fnew->handle = handle; 170005cd271fSPaul Blakey list_add_tail_rcu(&fnew->list, &fnew->mask->filters); 17013d81e711SVlad Buslov spin_unlock(&tp->lock); 170277b9900eSJiri Pirko } 170377b9900eSJiri Pirko 1704620da486SVlad Buslov *arg = fnew; 1705620da486SVlad Buslov 170639b7b6a6SArnd Bergmann kfree(tb); 170799815f50SVlad Buslov tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); 170877b9900eSJiri Pirko return 0; 170977b9900eSJiri Pirko 1710c049d56eSVlad Buslov errout_ht: 1711c049d56eSVlad Buslov spin_lock(&tp->lock); 1712620da486SVlad Buslov errout_hw: 1713c049d56eSVlad Buslov fnew->deleted = true; 17143d81e711SVlad Buslov spin_unlock(&tp->lock); 1715620da486SVlad Buslov if (!tc_skip_hw(fnew->flags)) 1716c24e43d8SVlad Buslov fl_hw_destroy_filter(tp, fnew, rtnl_held, NULL); 17171f17f774SVlad Buslov if (in_ht) 17181f17f774SVlad Buslov rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node, 17191f17f774SVlad Buslov fnew->mask->filter_ht_params); 1720ecb3dea4SVlad Buslov errout_mask: 17219994677cSVlad Buslov fl_mask_put(head, fnew->mask); 172277b9900eSJiri Pirko errout: 1723c049d56eSVlad Buslov __fl_put(fnew); 172439b7b6a6SArnd Bergmann errout_tb: 172539b7b6a6SArnd Bergmann kfree(tb); 17262cddd201SIvan Vecera errout_mask_alloc: 172799815f50SVlad Buslov tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); 172806177558SVlad Buslov errout_fold: 172906177558SVlad Buslov if (fold) 173006177558SVlad Buslov __fl_put(fold); 173177b9900eSJiri Pirko return err; 173277b9900eSJiri Pirko } 173377b9900eSJiri Pirko 1734571acf21SAlexander Aring static int fl_delete(struct tcf_proto *tp, void *arg, bool *last, 173512db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack) 173677b9900eSJiri Pirko { 1737e474619aSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 17388113c095SWANG Cong struct cls_fl_filter *f = arg; 1739b2552b8cSVlad Buslov bool last_on_mask; 1740b2552b8cSVlad Buslov int err = 0; 174177b9900eSJiri Pirko 1742c24e43d8SVlad Buslov err = __fl_delete(tp, f, &last_on_mask, rtnl_held, extack); 174305cd271fSPaul Blakey *last = list_empty(&head->masks); 174406177558SVlad Buslov __fl_put(f); 174506177558SVlad Buslov 1746b2552b8cSVlad Buslov return err; 174777b9900eSJiri Pirko } 174877b9900eSJiri Pirko 174912db03b6SVlad Buslov static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg, 175012db03b6SVlad Buslov bool rtnl_held) 175177b9900eSJiri Pirko { 1752d39d7149SCong Wang struct cls_fl_head *head = fl_head_dereference(tp); 1753d39d7149SCong Wang unsigned long id = arg->cookie, tmp; 175477b9900eSJiri Pirko struct cls_fl_filter *f; 175577b9900eSJiri Pirko 175601683a14SVlad Buslov arg->count = arg->skip; 175701683a14SVlad Buslov 1758d39d7149SCong Wang idr_for_each_entry_continue_ul(&head->handle_idr, f, tmp, id) { 1759d39d7149SCong Wang /* don't return filters that are being deleted */ 1760d39d7149SCong Wang if (!refcount_inc_not_zero(&f->refcnt)) 1761d39d7149SCong Wang continue; 17628113c095SWANG Cong if (arg->fn(tp, f, arg) < 0) { 176306177558SVlad Buslov __fl_put(f); 176477b9900eSJiri Pirko arg->stop = 1; 176577b9900eSJiri Pirko break; 176677b9900eSJiri Pirko } 176706177558SVlad Buslov __fl_put(f); 176877b9900eSJiri Pirko arg->count++; 176977b9900eSJiri Pirko } 1770d39d7149SCong Wang arg->cookie = id; 177177b9900eSJiri Pirko } 177277b9900eSJiri Pirko 1773c049d56eSVlad Buslov static struct cls_fl_filter * 1774c049d56eSVlad Buslov fl_get_next_hw_filter(struct tcf_proto *tp, struct cls_fl_filter *f, bool add) 1775c049d56eSVlad Buslov { 1776c049d56eSVlad Buslov struct cls_fl_head *head = fl_head_dereference(tp); 1777c049d56eSVlad Buslov 1778c049d56eSVlad Buslov spin_lock(&tp->lock); 1779c049d56eSVlad Buslov if (list_empty(&head->hw_filters)) { 1780c049d56eSVlad Buslov spin_unlock(&tp->lock); 1781c049d56eSVlad Buslov return NULL; 1782c049d56eSVlad Buslov } 1783c049d56eSVlad Buslov 1784c049d56eSVlad Buslov if (!f) 1785c049d56eSVlad Buslov f = list_entry(&head->hw_filters, struct cls_fl_filter, 1786c049d56eSVlad Buslov hw_list); 1787c049d56eSVlad Buslov list_for_each_entry_continue(f, &head->hw_filters, hw_list) { 1788c049d56eSVlad Buslov if (!(add && f->deleted) && refcount_inc_not_zero(&f->refcnt)) { 1789c049d56eSVlad Buslov spin_unlock(&tp->lock); 1790c049d56eSVlad Buslov return f; 1791c049d56eSVlad Buslov } 1792c049d56eSVlad Buslov } 1793c049d56eSVlad Buslov 1794c049d56eSVlad Buslov spin_unlock(&tp->lock); 1795c049d56eSVlad Buslov return NULL; 1796c049d56eSVlad Buslov } 1797c049d56eSVlad Buslov 1798a7323311SPablo Neira Ayuso static int fl_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, 179931533cbaSJohn Hurley void *cb_priv, struct netlink_ext_ack *extack) 180031533cbaSJohn Hurley { 180131533cbaSJohn Hurley struct tcf_block *block = tp->chain->block; 1802f9e30088SPablo Neira Ayuso struct flow_cls_offload cls_flower = {}; 1803c049d56eSVlad Buslov struct cls_fl_filter *f = NULL; 180431533cbaSJohn Hurley int err; 180531533cbaSJohn Hurley 1806c049d56eSVlad Buslov /* hw_filters list can only be changed by hw offload functions after 1807c049d56eSVlad Buslov * obtaining rtnl lock. Make sure it is not changed while reoffload is 1808c049d56eSVlad Buslov * iterating it. 1809c049d56eSVlad Buslov */ 1810c049d56eSVlad Buslov ASSERT_RTNL(); 181131533cbaSJohn Hurley 1812c049d56eSVlad Buslov while ((f = fl_get_next_hw_filter(tp, f, add))) { 1813e3ab786bSPablo Neira Ayuso cls_flower.rule = 1814e3ab786bSPablo Neira Ayuso flow_rule_alloc(tcf_exts_num_actions(&f->exts)); 181595e27a4dSJohn Hurley if (!cls_flower.rule) { 181695e27a4dSJohn Hurley __fl_put(f); 18178f256622SPablo Neira Ayuso return -ENOMEM; 181895e27a4dSJohn Hurley } 18198f256622SPablo Neira Ayuso 182095e27a4dSJohn Hurley tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, 1821d6787147SPieter Jansen van Vuuren extack); 182231533cbaSJohn Hurley cls_flower.command = add ? 1823f9e30088SPablo Neira Ayuso FLOW_CLS_REPLACE : FLOW_CLS_DESTROY; 182431533cbaSJohn Hurley cls_flower.cookie = (unsigned long)f; 182595e27a4dSJohn Hurley cls_flower.rule->match.dissector = &f->mask->dissector; 182695e27a4dSJohn Hurley cls_flower.rule->match.mask = &f->mask->key; 18278f256622SPablo Neira Ayuso cls_flower.rule->match.key = &f->mkey; 18283a7b6861SPablo Neira Ayuso 182995e27a4dSJohn Hurley err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts); 18303a7b6861SPablo Neira Ayuso if (err) { 18313a7b6861SPablo Neira Ayuso kfree(cls_flower.rule); 18321f15bb4fSVlad Buslov if (tc_skip_sw(f->flags)) { 18331f15bb4fSVlad Buslov NL_SET_ERR_MSG_MOD(extack, "Failed to setup flow action"); 183495e27a4dSJohn Hurley __fl_put(f); 18353a7b6861SPablo Neira Ayuso return err; 18363a7b6861SPablo Neira Ayuso } 183795e27a4dSJohn Hurley goto next_flow; 18381f15bb4fSVlad Buslov } 18393a7b6861SPablo Neira Ayuso 184031533cbaSJohn Hurley cls_flower.classid = f->res.classid; 184131533cbaSJohn Hurley 1842*40119211SVlad Buslov err = tc_setup_cb_reoffload(block, tp, add, cb, 1843*40119211SVlad Buslov TC_SETUP_CLSFLOWER, &cls_flower, 1844*40119211SVlad Buslov cb_priv, &f->flags, 1845*40119211SVlad Buslov &f->in_hw_count); 18468f256622SPablo Neira Ayuso kfree(cls_flower.rule); 18478f256622SPablo Neira Ayuso 184831533cbaSJohn Hurley if (err) { 184995e27a4dSJohn Hurley __fl_put(f); 185031533cbaSJohn Hurley return err; 185195e27a4dSJohn Hurley } 185295e27a4dSJohn Hurley next_flow: 185395e27a4dSJohn Hurley __fl_put(f); 185431533cbaSJohn Hurley } 185531533cbaSJohn Hurley 185631533cbaSJohn Hurley return 0; 185731533cbaSJohn Hurley } 185831533cbaSJohn Hurley 18598f256622SPablo Neira Ayuso static int fl_hw_create_tmplt(struct tcf_chain *chain, 186034738452SJiri Pirko struct fl_flow_tmplt *tmplt) 186134738452SJiri Pirko { 1862f9e30088SPablo Neira Ayuso struct flow_cls_offload cls_flower = {}; 186334738452SJiri Pirko struct tcf_block *block = chain->block; 186434738452SJiri Pirko 1865e3ab786bSPablo Neira Ayuso cls_flower.rule = flow_rule_alloc(0); 18668f256622SPablo Neira Ayuso if (!cls_flower.rule) 18678f256622SPablo Neira Ayuso return -ENOMEM; 18688f256622SPablo Neira Ayuso 186934738452SJiri Pirko cls_flower.common.chain_index = chain->index; 1870f9e30088SPablo Neira Ayuso cls_flower.command = FLOW_CLS_TMPLT_CREATE; 187134738452SJiri Pirko cls_flower.cookie = (unsigned long) tmplt; 18728f256622SPablo Neira Ayuso cls_flower.rule->match.dissector = &tmplt->dissector; 18738f256622SPablo Neira Ayuso cls_flower.rule->match.mask = &tmplt->mask; 18748f256622SPablo Neira Ayuso cls_flower.rule->match.key = &tmplt->dummy_key; 187534738452SJiri Pirko 187634738452SJiri Pirko /* We don't care if driver (any of them) fails to handle this 187734738452SJiri Pirko * call. It serves just as a hint for it. 187834738452SJiri Pirko */ 1879*40119211SVlad Buslov tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false, true); 18808f256622SPablo Neira Ayuso kfree(cls_flower.rule); 18818f256622SPablo Neira Ayuso 18828f256622SPablo Neira Ayuso return 0; 188334738452SJiri Pirko } 188434738452SJiri Pirko 188534738452SJiri Pirko static void fl_hw_destroy_tmplt(struct tcf_chain *chain, 188634738452SJiri Pirko struct fl_flow_tmplt *tmplt) 188734738452SJiri Pirko { 1888f9e30088SPablo Neira Ayuso struct flow_cls_offload cls_flower = {}; 188934738452SJiri Pirko struct tcf_block *block = chain->block; 189034738452SJiri Pirko 189134738452SJiri Pirko cls_flower.common.chain_index = chain->index; 1892f9e30088SPablo Neira Ayuso cls_flower.command = FLOW_CLS_TMPLT_DESTROY; 189334738452SJiri Pirko cls_flower.cookie = (unsigned long) tmplt; 189434738452SJiri Pirko 1895*40119211SVlad Buslov tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false, true); 189634738452SJiri Pirko } 189734738452SJiri Pirko 1898b95ec7ebSJiri Pirko static void *fl_tmplt_create(struct net *net, struct tcf_chain *chain, 1899b95ec7ebSJiri Pirko struct nlattr **tca, 1900b95ec7ebSJiri Pirko struct netlink_ext_ack *extack) 1901b95ec7ebSJiri Pirko { 1902b95ec7ebSJiri Pirko struct fl_flow_tmplt *tmplt; 1903b95ec7ebSJiri Pirko struct nlattr **tb; 1904b95ec7ebSJiri Pirko int err; 1905b95ec7ebSJiri Pirko 1906b95ec7ebSJiri Pirko if (!tca[TCA_OPTIONS]) 1907b95ec7ebSJiri Pirko return ERR_PTR(-EINVAL); 1908b95ec7ebSJiri Pirko 1909b95ec7ebSJiri Pirko tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 1910b95ec7ebSJiri Pirko if (!tb) 1911b95ec7ebSJiri Pirko return ERR_PTR(-ENOBUFS); 19128cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_FLOWER_MAX, 19138cb08174SJohannes Berg tca[TCA_OPTIONS], fl_policy, NULL); 1914b95ec7ebSJiri Pirko if (err) 1915b95ec7ebSJiri Pirko goto errout_tb; 1916b95ec7ebSJiri Pirko 1917b95ec7ebSJiri Pirko tmplt = kzalloc(sizeof(*tmplt), GFP_KERNEL); 19181cbc36a5SDan Carpenter if (!tmplt) { 19191cbc36a5SDan Carpenter err = -ENOMEM; 1920b95ec7ebSJiri Pirko goto errout_tb; 19211cbc36a5SDan Carpenter } 1922b95ec7ebSJiri Pirko tmplt->chain = chain; 1923b95ec7ebSJiri Pirko err = fl_set_key(net, tb, &tmplt->dummy_key, &tmplt->mask, extack); 1924b95ec7ebSJiri Pirko if (err) 1925b95ec7ebSJiri Pirko goto errout_tmplt; 1926b95ec7ebSJiri Pirko 1927b95ec7ebSJiri Pirko fl_init_dissector(&tmplt->dissector, &tmplt->mask); 1928b95ec7ebSJiri Pirko 19298f256622SPablo Neira Ayuso err = fl_hw_create_tmplt(chain, tmplt); 19308f256622SPablo Neira Ayuso if (err) 19318f256622SPablo Neira Ayuso goto errout_tmplt; 193234738452SJiri Pirko 19338f256622SPablo Neira Ayuso kfree(tb); 1934b95ec7ebSJiri Pirko return tmplt; 1935b95ec7ebSJiri Pirko 1936b95ec7ebSJiri Pirko errout_tmplt: 1937b95ec7ebSJiri Pirko kfree(tmplt); 1938b95ec7ebSJiri Pirko errout_tb: 1939b95ec7ebSJiri Pirko kfree(tb); 1940b95ec7ebSJiri Pirko return ERR_PTR(err); 1941b95ec7ebSJiri Pirko } 1942b95ec7ebSJiri Pirko 1943b95ec7ebSJiri Pirko static void fl_tmplt_destroy(void *tmplt_priv) 1944b95ec7ebSJiri Pirko { 1945b95ec7ebSJiri Pirko struct fl_flow_tmplt *tmplt = tmplt_priv; 1946b95ec7ebSJiri Pirko 194795278ddaSCong Wang fl_hw_destroy_tmplt(tmplt->chain, tmplt); 194895278ddaSCong Wang kfree(tmplt); 1949b95ec7ebSJiri Pirko } 1950b95ec7ebSJiri Pirko 195177b9900eSJiri Pirko static int fl_dump_key_val(struct sk_buff *skb, 195277b9900eSJiri Pirko void *val, int val_type, 195377b9900eSJiri Pirko void *mask, int mask_type, int len) 195477b9900eSJiri Pirko { 195577b9900eSJiri Pirko int err; 195677b9900eSJiri Pirko 195777b9900eSJiri Pirko if (!memchr_inv(mask, 0, len)) 195877b9900eSJiri Pirko return 0; 195977b9900eSJiri Pirko err = nla_put(skb, val_type, len, val); 196077b9900eSJiri Pirko if (err) 196177b9900eSJiri Pirko return err; 196277b9900eSJiri Pirko if (mask_type != TCA_FLOWER_UNSPEC) { 196377b9900eSJiri Pirko err = nla_put(skb, mask_type, len, mask); 196477b9900eSJiri Pirko if (err) 196577b9900eSJiri Pirko return err; 196677b9900eSJiri Pirko } 196777b9900eSJiri Pirko return 0; 196877b9900eSJiri Pirko } 196977b9900eSJiri Pirko 19705c72299fSAmritha Nambiar static int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key, 19715c72299fSAmritha Nambiar struct fl_flow_key *mask) 19725c72299fSAmritha Nambiar { 19735c72299fSAmritha Nambiar if (fl_dump_key_val(skb, &key->tp_min.dst, TCA_FLOWER_KEY_PORT_DST_MIN, 19745c72299fSAmritha Nambiar &mask->tp_min.dst, TCA_FLOWER_UNSPEC, 19755c72299fSAmritha Nambiar sizeof(key->tp_min.dst)) || 19765c72299fSAmritha Nambiar fl_dump_key_val(skb, &key->tp_max.dst, TCA_FLOWER_KEY_PORT_DST_MAX, 19775c72299fSAmritha Nambiar &mask->tp_max.dst, TCA_FLOWER_UNSPEC, 19785c72299fSAmritha Nambiar sizeof(key->tp_max.dst)) || 19795c72299fSAmritha Nambiar fl_dump_key_val(skb, &key->tp_min.src, TCA_FLOWER_KEY_PORT_SRC_MIN, 19805c72299fSAmritha Nambiar &mask->tp_min.src, TCA_FLOWER_UNSPEC, 19815c72299fSAmritha Nambiar sizeof(key->tp_min.src)) || 19825c72299fSAmritha Nambiar fl_dump_key_val(skb, &key->tp_max.src, TCA_FLOWER_KEY_PORT_SRC_MAX, 19835c72299fSAmritha Nambiar &mask->tp_max.src, TCA_FLOWER_UNSPEC, 19845c72299fSAmritha Nambiar sizeof(key->tp_max.src))) 19855c72299fSAmritha Nambiar return -1; 19865c72299fSAmritha Nambiar 19875c72299fSAmritha Nambiar return 0; 19885c72299fSAmritha Nambiar } 19895c72299fSAmritha Nambiar 1990a577d8f7SBenjamin LaHaise static int fl_dump_key_mpls(struct sk_buff *skb, 1991a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *mpls_key, 1992a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *mpls_mask) 1993a577d8f7SBenjamin LaHaise { 1994a577d8f7SBenjamin LaHaise int err; 1995a577d8f7SBenjamin LaHaise 1996a577d8f7SBenjamin LaHaise if (!memchr_inv(mpls_mask, 0, sizeof(*mpls_mask))) 1997a577d8f7SBenjamin LaHaise return 0; 1998a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_ttl) { 1999a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TTL, 2000a577d8f7SBenjamin LaHaise mpls_key->mpls_ttl); 2001a577d8f7SBenjamin LaHaise if (err) 2002a577d8f7SBenjamin LaHaise return err; 2003a577d8f7SBenjamin LaHaise } 2004a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_tc) { 2005a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TC, 2006a577d8f7SBenjamin LaHaise mpls_key->mpls_tc); 2007a577d8f7SBenjamin LaHaise if (err) 2008a577d8f7SBenjamin LaHaise return err; 2009a577d8f7SBenjamin LaHaise } 2010a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_label) { 2011a577d8f7SBenjamin LaHaise err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_LABEL, 2012a577d8f7SBenjamin LaHaise mpls_key->mpls_label); 2013a577d8f7SBenjamin LaHaise if (err) 2014a577d8f7SBenjamin LaHaise return err; 2015a577d8f7SBenjamin LaHaise } 2016a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_bos) { 2017a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_BOS, 2018a577d8f7SBenjamin LaHaise mpls_key->mpls_bos); 2019a577d8f7SBenjamin LaHaise if (err) 2020a577d8f7SBenjamin LaHaise return err; 2021a577d8f7SBenjamin LaHaise } 2022a577d8f7SBenjamin LaHaise return 0; 2023a577d8f7SBenjamin LaHaise } 2024a577d8f7SBenjamin LaHaise 20250e2c17b6SOr Gerlitz static int fl_dump_key_ip(struct sk_buff *skb, bool encap, 20264d80cc0aSOr Gerlitz struct flow_dissector_key_ip *key, 20274d80cc0aSOr Gerlitz struct flow_dissector_key_ip *mask) 20284d80cc0aSOr Gerlitz { 20290e2c17b6SOr Gerlitz int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 20300e2c17b6SOr Gerlitz int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 20310e2c17b6SOr Gerlitz int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 20320e2c17b6SOr Gerlitz int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 20330e2c17b6SOr Gerlitz 20340e2c17b6SOr Gerlitz if (fl_dump_key_val(skb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)) || 20350e2c17b6SOr Gerlitz fl_dump_key_val(skb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl))) 20364d80cc0aSOr Gerlitz return -1; 20374d80cc0aSOr Gerlitz 20384d80cc0aSOr Gerlitz return 0; 20394d80cc0aSOr Gerlitz } 20404d80cc0aSOr Gerlitz 20419399ae9aSHadar Hen Zion static int fl_dump_key_vlan(struct sk_buff *skb, 2042d64efd09SJianbo Liu int vlan_id_key, int vlan_prio_key, 20439399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_key, 20449399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_mask) 20459399ae9aSHadar Hen Zion { 20469399ae9aSHadar Hen Zion int err; 20479399ae9aSHadar Hen Zion 20489399ae9aSHadar Hen Zion if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask))) 20499399ae9aSHadar Hen Zion return 0; 20509399ae9aSHadar Hen Zion if (vlan_mask->vlan_id) { 2051d64efd09SJianbo Liu err = nla_put_u16(skb, vlan_id_key, 20529399ae9aSHadar Hen Zion vlan_key->vlan_id); 20539399ae9aSHadar Hen Zion if (err) 20549399ae9aSHadar Hen Zion return err; 20559399ae9aSHadar Hen Zion } 20569399ae9aSHadar Hen Zion if (vlan_mask->vlan_priority) { 2057d64efd09SJianbo Liu err = nla_put_u8(skb, vlan_prio_key, 20589399ae9aSHadar Hen Zion vlan_key->vlan_priority); 20599399ae9aSHadar Hen Zion if (err) 20609399ae9aSHadar Hen Zion return err; 20619399ae9aSHadar Hen Zion } 20629399ae9aSHadar Hen Zion return 0; 20639399ae9aSHadar Hen Zion } 20649399ae9aSHadar Hen Zion 2065faa3ffceSOr Gerlitz static void fl_get_key_flag(u32 dissector_key, u32 dissector_mask, 2066faa3ffceSOr Gerlitz u32 *flower_key, u32 *flower_mask, 2067faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 2068faa3ffceSOr Gerlitz { 2069faa3ffceSOr Gerlitz if (dissector_mask & dissector_flag_bit) { 2070faa3ffceSOr Gerlitz *flower_mask |= flower_flag_bit; 2071faa3ffceSOr Gerlitz if (dissector_key & dissector_flag_bit) 2072faa3ffceSOr Gerlitz *flower_key |= flower_flag_bit; 2073faa3ffceSOr Gerlitz } 2074faa3ffceSOr Gerlitz } 2075faa3ffceSOr Gerlitz 2076faa3ffceSOr Gerlitz static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) 2077faa3ffceSOr Gerlitz { 2078faa3ffceSOr Gerlitz u32 key, mask; 2079faa3ffceSOr Gerlitz __be32 _key, _mask; 2080faa3ffceSOr Gerlitz int err; 2081faa3ffceSOr Gerlitz 2082faa3ffceSOr Gerlitz if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask))) 2083faa3ffceSOr Gerlitz return 0; 2084faa3ffceSOr Gerlitz 2085faa3ffceSOr Gerlitz key = 0; 2086faa3ffceSOr Gerlitz mask = 0; 2087faa3ffceSOr Gerlitz 2088faa3ffceSOr Gerlitz fl_get_key_flag(flags_key, flags_mask, &key, &mask, 2089faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 2090459d153dSPieter Jansen van Vuuren fl_get_key_flag(flags_key, flags_mask, &key, &mask, 2091459d153dSPieter Jansen van Vuuren TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 2092459d153dSPieter Jansen van Vuuren FLOW_DIS_FIRST_FRAG); 2093faa3ffceSOr Gerlitz 2094faa3ffceSOr Gerlitz _key = cpu_to_be32(key); 2095faa3ffceSOr Gerlitz _mask = cpu_to_be32(mask); 2096faa3ffceSOr Gerlitz 2097faa3ffceSOr Gerlitz err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key); 2098faa3ffceSOr Gerlitz if (err) 2099faa3ffceSOr Gerlitz return err; 2100faa3ffceSOr Gerlitz 2101faa3ffceSOr Gerlitz return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); 2102faa3ffceSOr Gerlitz } 2103faa3ffceSOr Gerlitz 21040a6e7778SPieter Jansen van Vuuren static int fl_dump_key_geneve_opt(struct sk_buff *skb, 21050a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *enc_opts) 21060a6e7778SPieter Jansen van Vuuren { 21070a6e7778SPieter Jansen van Vuuren struct geneve_opt *opt; 21080a6e7778SPieter Jansen van Vuuren struct nlattr *nest; 21090a6e7778SPieter Jansen van Vuuren int opt_off = 0; 21100a6e7778SPieter Jansen van Vuuren 2111ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_GENEVE); 21120a6e7778SPieter Jansen van Vuuren if (!nest) 21130a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21140a6e7778SPieter Jansen van Vuuren 21150a6e7778SPieter Jansen van Vuuren while (enc_opts->len > opt_off) { 21160a6e7778SPieter Jansen van Vuuren opt = (struct geneve_opt *)&enc_opts->data[opt_off]; 21170a6e7778SPieter Jansen van Vuuren 21180a6e7778SPieter Jansen van Vuuren if (nla_put_be16(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, 21190a6e7778SPieter Jansen van Vuuren opt->opt_class)) 21200a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21210a6e7778SPieter Jansen van Vuuren if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, 21220a6e7778SPieter Jansen van Vuuren opt->type)) 21230a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21240a6e7778SPieter Jansen van Vuuren if (nla_put(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, 21250a6e7778SPieter Jansen van Vuuren opt->length * 4, opt->opt_data)) 21260a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21270a6e7778SPieter Jansen van Vuuren 21280a6e7778SPieter Jansen van Vuuren opt_off += sizeof(struct geneve_opt) + opt->length * 4; 21290a6e7778SPieter Jansen van Vuuren } 21300a6e7778SPieter Jansen van Vuuren nla_nest_end(skb, nest); 21310a6e7778SPieter Jansen van Vuuren return 0; 21320a6e7778SPieter Jansen van Vuuren 21330a6e7778SPieter Jansen van Vuuren nla_put_failure: 21340a6e7778SPieter Jansen van Vuuren nla_nest_cancel(skb, nest); 21350a6e7778SPieter Jansen van Vuuren return -EMSGSIZE; 21360a6e7778SPieter Jansen van Vuuren } 21370a6e7778SPieter Jansen van Vuuren 2138e0ace68aSPaul Blakey static int fl_dump_key_ct(struct sk_buff *skb, 2139e0ace68aSPaul Blakey struct flow_dissector_key_ct *key, 2140e0ace68aSPaul Blakey struct flow_dissector_key_ct *mask) 2141e0ace68aSPaul Blakey { 2142e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK) && 2143e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_state, TCA_FLOWER_KEY_CT_STATE, 2144e0ace68aSPaul Blakey &mask->ct_state, TCA_FLOWER_KEY_CT_STATE_MASK, 2145e0ace68aSPaul Blakey sizeof(key->ct_state))) 2146e0ace68aSPaul Blakey goto nla_put_failure; 2147e0ace68aSPaul Blakey 2148e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 2149e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_zone, TCA_FLOWER_KEY_CT_ZONE, 2150e0ace68aSPaul Blakey &mask->ct_zone, TCA_FLOWER_KEY_CT_ZONE_MASK, 2151e0ace68aSPaul Blakey sizeof(key->ct_zone))) 2152e0ace68aSPaul Blakey goto nla_put_failure; 2153e0ace68aSPaul Blakey 2154e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 2155e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_mark, TCA_FLOWER_KEY_CT_MARK, 2156e0ace68aSPaul Blakey &mask->ct_mark, TCA_FLOWER_KEY_CT_MARK_MASK, 2157e0ace68aSPaul Blakey sizeof(key->ct_mark))) 2158e0ace68aSPaul Blakey goto nla_put_failure; 2159e0ace68aSPaul Blakey 2160e0ace68aSPaul Blakey if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 2161e0ace68aSPaul Blakey fl_dump_key_val(skb, &key->ct_labels, TCA_FLOWER_KEY_CT_LABELS, 2162e0ace68aSPaul Blakey &mask->ct_labels, TCA_FLOWER_KEY_CT_LABELS_MASK, 2163e0ace68aSPaul Blakey sizeof(key->ct_labels))) 2164e0ace68aSPaul Blakey goto nla_put_failure; 2165e0ace68aSPaul Blakey 2166e0ace68aSPaul Blakey return 0; 2167e0ace68aSPaul Blakey 2168e0ace68aSPaul Blakey nla_put_failure: 2169e0ace68aSPaul Blakey return -EMSGSIZE; 2170e0ace68aSPaul Blakey } 2171e0ace68aSPaul Blakey 21720a6e7778SPieter Jansen van Vuuren static int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type, 21730a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *enc_opts) 21740a6e7778SPieter Jansen van Vuuren { 21750a6e7778SPieter Jansen van Vuuren struct nlattr *nest; 21760a6e7778SPieter Jansen van Vuuren int err; 21770a6e7778SPieter Jansen van Vuuren 21780a6e7778SPieter Jansen van Vuuren if (!enc_opts->len) 21790a6e7778SPieter Jansen van Vuuren return 0; 21800a6e7778SPieter Jansen van Vuuren 2181ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, enc_opt_type); 21820a6e7778SPieter Jansen van Vuuren if (!nest) 21830a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21840a6e7778SPieter Jansen van Vuuren 21850a6e7778SPieter Jansen van Vuuren switch (enc_opts->dst_opt_type) { 21860a6e7778SPieter Jansen van Vuuren case TUNNEL_GENEVE_OPT: 21870a6e7778SPieter Jansen van Vuuren err = fl_dump_key_geneve_opt(skb, enc_opts); 21880a6e7778SPieter Jansen van Vuuren if (err) 21890a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21900a6e7778SPieter Jansen van Vuuren break; 21910a6e7778SPieter Jansen van Vuuren default: 21920a6e7778SPieter Jansen van Vuuren goto nla_put_failure; 21930a6e7778SPieter Jansen van Vuuren } 21940a6e7778SPieter Jansen van Vuuren nla_nest_end(skb, nest); 21950a6e7778SPieter Jansen van Vuuren return 0; 21960a6e7778SPieter Jansen van Vuuren 21970a6e7778SPieter Jansen van Vuuren nla_put_failure: 21980a6e7778SPieter Jansen van Vuuren nla_nest_cancel(skb, nest); 21990a6e7778SPieter Jansen van Vuuren return -EMSGSIZE; 22000a6e7778SPieter Jansen van Vuuren } 22010a6e7778SPieter Jansen van Vuuren 22020a6e7778SPieter Jansen van Vuuren static int fl_dump_key_enc_opt(struct sk_buff *skb, 22030a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *key_opts, 22040a6e7778SPieter Jansen van Vuuren struct flow_dissector_key_enc_opts *msk_opts) 22050a6e7778SPieter Jansen van Vuuren { 22060a6e7778SPieter Jansen van Vuuren int err; 22070a6e7778SPieter Jansen van Vuuren 22080a6e7778SPieter Jansen van Vuuren err = fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS, key_opts); 22090a6e7778SPieter Jansen van Vuuren if (err) 22100a6e7778SPieter Jansen van Vuuren return err; 22110a6e7778SPieter Jansen van Vuuren 22120a6e7778SPieter Jansen van Vuuren return fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS_MASK, msk_opts); 22130a6e7778SPieter Jansen van Vuuren } 22140a6e7778SPieter Jansen van Vuuren 2215f5749081SJiri Pirko static int fl_dump_key(struct sk_buff *skb, struct net *net, 2216f5749081SJiri Pirko struct fl_flow_key *key, struct fl_flow_key *mask) 221777b9900eSJiri Pirko { 22188212ed77SJiri Pirko if (mask->meta.ingress_ifindex) { 221977b9900eSJiri Pirko struct net_device *dev; 222077b9900eSJiri Pirko 22218212ed77SJiri Pirko dev = __dev_get_by_index(net, key->meta.ingress_ifindex); 222277b9900eSJiri Pirko if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name)) 222377b9900eSJiri Pirko goto nla_put_failure; 222477b9900eSJiri Pirko } 222577b9900eSJiri Pirko 222677b9900eSJiri Pirko if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 222777b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 222877b9900eSJiri Pirko sizeof(key->eth.dst)) || 222977b9900eSJiri Pirko fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 223077b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 223177b9900eSJiri Pirko sizeof(key->eth.src)) || 223277b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE, 223377b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 223477b9900eSJiri Pirko sizeof(key->basic.n_proto))) 223577b9900eSJiri Pirko goto nla_put_failure; 22369399ae9aSHadar Hen Zion 2237a577d8f7SBenjamin LaHaise if (fl_dump_key_mpls(skb, &key->mpls, &mask->mpls)) 2238a577d8f7SBenjamin LaHaise goto nla_put_failure; 2239a577d8f7SBenjamin LaHaise 2240d64efd09SJianbo Liu if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_VLAN_ID, 2241d64efd09SJianbo Liu TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, &mask->vlan)) 22429399ae9aSHadar Hen Zion goto nla_put_failure; 22439399ae9aSHadar Hen Zion 2244d64efd09SJianbo Liu if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_CVLAN_ID, 2245d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_PRIO, 2246d64efd09SJianbo Liu &key->cvlan, &mask->cvlan) || 2247d64efd09SJianbo Liu (mask->cvlan.vlan_tpid && 2248158abbf1SJianbo Liu nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 2249d64efd09SJianbo Liu key->cvlan.vlan_tpid))) 2250d3069512SJianbo Liu goto nla_put_failure; 2251d3069512SJianbo Liu 22525e9a0fe4SJianbo Liu if (mask->basic.n_proto) { 2253d64efd09SJianbo Liu if (mask->cvlan.vlan_tpid) { 2254d64efd09SJianbo Liu if (nla_put_be16(skb, TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 2255d64efd09SJianbo Liu key->basic.n_proto)) 2256d64efd09SJianbo Liu goto nla_put_failure; 2257d64efd09SJianbo Liu } else if (mask->vlan.vlan_tpid) { 2258d64efd09SJianbo Liu if (nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 2259d64efd09SJianbo Liu key->basic.n_proto)) 2260d64efd09SJianbo Liu goto nla_put_failure; 2261d64efd09SJianbo Liu } 22625e9a0fe4SJianbo Liu } 2263d64efd09SJianbo Liu 226477b9900eSJiri Pirko if ((key->basic.n_proto == htons(ETH_P_IP) || 226577b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) && 22664d80cc0aSOr Gerlitz (fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 226777b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 22684d80cc0aSOr Gerlitz sizeof(key->basic.ip_proto)) || 22690e2c17b6SOr Gerlitz fl_dump_key_ip(skb, false, &key->ip, &mask->ip))) 227077b9900eSJiri Pirko goto nla_put_failure; 227177b9900eSJiri Pirko 2272c3f83241STom Herbert if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 227377b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 227477b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 227577b9900eSJiri Pirko sizeof(key->ipv4.src)) || 227677b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 227777b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 227877b9900eSJiri Pirko sizeof(key->ipv4.dst)))) 227977b9900eSJiri Pirko goto nla_put_failure; 2280c3f83241STom Herbert else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 228177b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 228277b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 228377b9900eSJiri Pirko sizeof(key->ipv6.src)) || 228477b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 228577b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 228677b9900eSJiri Pirko sizeof(key->ipv6.dst)))) 228777b9900eSJiri Pirko goto nla_put_failure; 228877b9900eSJiri Pirko 228977b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP && 229077b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 2291aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 229277b9900eSJiri Pirko sizeof(key->tp.src)) || 229377b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 2294aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 2295fdfc7dd6SJiri Pirko sizeof(key->tp.dst)) || 2296fdfc7dd6SJiri Pirko fl_dump_key_val(skb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 2297fdfc7dd6SJiri Pirko &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 2298fdfc7dd6SJiri Pirko sizeof(key->tcp.flags)))) 229977b9900eSJiri Pirko goto nla_put_failure; 230077b9900eSJiri Pirko else if (key->basic.ip_proto == IPPROTO_UDP && 230177b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 2302aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 230377b9900eSJiri Pirko sizeof(key->tp.src)) || 230477b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 2305aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 230677b9900eSJiri Pirko sizeof(key->tp.dst)))) 230777b9900eSJiri Pirko goto nla_put_failure; 23085976c5f4SSimon Horman else if (key->basic.ip_proto == IPPROTO_SCTP && 23095976c5f4SSimon Horman (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 23105976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 23115976c5f4SSimon Horman sizeof(key->tp.src)) || 23125976c5f4SSimon Horman fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 23135976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 23145976c5f4SSimon Horman sizeof(key->tp.dst)))) 23155976c5f4SSimon Horman goto nla_put_failure; 23167b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IP) && 23177b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP && 23187b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 23197b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type, 23207b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 23217b684884SSimon Horman sizeof(key->icmp.type)) || 23227b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 23237b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code, 23247b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 23257b684884SSimon Horman sizeof(key->icmp.code)))) 23267b684884SSimon Horman goto nla_put_failure; 23277b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IPV6) && 23287b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6 && 23297b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 23307b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type, 23317b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 23327b684884SSimon Horman sizeof(key->icmp.type)) || 23337b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 23347b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code, 23357b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 23367b684884SSimon Horman sizeof(key->icmp.code)))) 23377b684884SSimon Horman goto nla_put_failure; 233899d31326SSimon Horman else if ((key->basic.n_proto == htons(ETH_P_ARP) || 233999d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) && 234099d31326SSimon Horman (fl_dump_key_val(skb, &key->arp.sip, 234199d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP, &mask->arp.sip, 234299d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP_MASK, 234399d31326SSimon Horman sizeof(key->arp.sip)) || 234499d31326SSimon Horman fl_dump_key_val(skb, &key->arp.tip, 234599d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP, &mask->arp.tip, 234699d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP_MASK, 234799d31326SSimon Horman sizeof(key->arp.tip)) || 234899d31326SSimon Horman fl_dump_key_val(skb, &key->arp.op, 234999d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP, &mask->arp.op, 235099d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP_MASK, 235199d31326SSimon Horman sizeof(key->arp.op)) || 235299d31326SSimon Horman fl_dump_key_val(skb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 235399d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 235499d31326SSimon Horman sizeof(key->arp.sha)) || 235599d31326SSimon Horman fl_dump_key_val(skb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 235699d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 235799d31326SSimon Horman sizeof(key->arp.tha)))) 235899d31326SSimon Horman goto nla_put_failure; 235977b9900eSJiri Pirko 23605c72299fSAmritha Nambiar if ((key->basic.ip_proto == IPPROTO_TCP || 23615c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_UDP || 23625c72299fSAmritha Nambiar key->basic.ip_proto == IPPROTO_SCTP) && 23635c72299fSAmritha Nambiar fl_dump_key_port_range(skb, key, mask)) 23645c72299fSAmritha Nambiar goto nla_put_failure; 23655c72299fSAmritha Nambiar 2366bc3103f1SAmir Vadai if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 2367bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv4.src, 2368bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src, 2369bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 2370bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)) || 2371bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv4.dst, 2372bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst, 2373bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 2374bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)))) 2375bc3103f1SAmir Vadai goto nla_put_failure; 2376bc3103f1SAmir Vadai else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 2377bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv6.src, 2378bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src, 2379bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 2380bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)) || 2381bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv6.dst, 2382bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 2383bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 2384bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 2385bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)))) 2386bc3103f1SAmir Vadai goto nla_put_failure; 2387bc3103f1SAmir Vadai 2388bc3103f1SAmir Vadai if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, 2389eb523f42SHadar Hen Zion &mask->enc_key_id, TCA_FLOWER_UNSPEC, 2390f4d997fdSHadar Hen Zion sizeof(key->enc_key_id)) || 2391f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.src, 2392f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 2393f4d997fdSHadar Hen Zion &mask->enc_tp.src, 2394f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 2395f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)) || 2396f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.dst, 2397f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 2398f4d997fdSHadar Hen Zion &mask->enc_tp.dst, 2399f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 24000e2c17b6SOr Gerlitz sizeof(key->enc_tp.dst)) || 24010a6e7778SPieter Jansen van Vuuren fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip) || 24020a6e7778SPieter Jansen van Vuuren fl_dump_key_enc_opt(skb, &key->enc_opts, &mask->enc_opts)) 2403bc3103f1SAmir Vadai goto nla_put_failure; 2404bc3103f1SAmir Vadai 2405e0ace68aSPaul Blakey if (fl_dump_key_ct(skb, &key->ct, &mask->ct)) 2406e0ace68aSPaul Blakey goto nla_put_failure; 2407e0ace68aSPaul Blakey 2408faa3ffceSOr Gerlitz if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) 2409faa3ffceSOr Gerlitz goto nla_put_failure; 2410faa3ffceSOr Gerlitz 2411f5749081SJiri Pirko return 0; 2412f5749081SJiri Pirko 2413f5749081SJiri Pirko nla_put_failure: 2414f5749081SJiri Pirko return -EMSGSIZE; 2415f5749081SJiri Pirko } 2416f5749081SJiri Pirko 2417f5749081SJiri Pirko static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh, 241812db03b6SVlad Buslov struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 2419f5749081SJiri Pirko { 2420f5749081SJiri Pirko struct cls_fl_filter *f = fh; 2421f5749081SJiri Pirko struct nlattr *nest; 2422f5749081SJiri Pirko struct fl_flow_key *key, *mask; 24233d81e711SVlad Buslov bool skip_hw; 2424f5749081SJiri Pirko 2425f5749081SJiri Pirko if (!f) 2426f5749081SJiri Pirko return skb->len; 2427f5749081SJiri Pirko 2428f5749081SJiri Pirko t->tcm_handle = f->handle; 2429f5749081SJiri Pirko 2430ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 2431f5749081SJiri Pirko if (!nest) 2432f5749081SJiri Pirko goto nla_put_failure; 2433f5749081SJiri Pirko 24343d81e711SVlad Buslov spin_lock(&tp->lock); 24353d81e711SVlad Buslov 2436f5749081SJiri Pirko if (f->res.classid && 2437f5749081SJiri Pirko nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid)) 24383d81e711SVlad Buslov goto nla_put_failure_locked; 2439f5749081SJiri Pirko 2440f5749081SJiri Pirko key = &f->key; 2441f5749081SJiri Pirko mask = &f->mask->key; 24423d81e711SVlad Buslov skip_hw = tc_skip_hw(f->flags); 2443f5749081SJiri Pirko 2444f5749081SJiri Pirko if (fl_dump_key(skb, net, key, mask)) 24453d81e711SVlad Buslov goto nla_put_failure_locked; 2446f5749081SJiri Pirko 2447749e6720SOr Gerlitz if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags)) 24483d81e711SVlad Buslov goto nla_put_failure_locked; 24493d81e711SVlad Buslov 24503d81e711SVlad Buslov spin_unlock(&tp->lock); 24513d81e711SVlad Buslov 24523d81e711SVlad Buslov if (!skip_hw) 2453c24e43d8SVlad Buslov fl_hw_update_stats(tp, f, rtnl_held); 2454e69985c6SAmir Vadai 245586c55361SVlad Buslov if (nla_put_u32(skb, TCA_FLOWER_IN_HW_COUNT, f->in_hw_count)) 245686c55361SVlad Buslov goto nla_put_failure; 245786c55361SVlad Buslov 245877b9900eSJiri Pirko if (tcf_exts_dump(skb, &f->exts)) 245977b9900eSJiri Pirko goto nla_put_failure; 246077b9900eSJiri Pirko 246177b9900eSJiri Pirko nla_nest_end(skb, nest); 246277b9900eSJiri Pirko 246377b9900eSJiri Pirko if (tcf_exts_dump_stats(skb, &f->exts) < 0) 246477b9900eSJiri Pirko goto nla_put_failure; 246577b9900eSJiri Pirko 246677b9900eSJiri Pirko return skb->len; 246777b9900eSJiri Pirko 24683d81e711SVlad Buslov nla_put_failure_locked: 24693d81e711SVlad Buslov spin_unlock(&tp->lock); 247077b9900eSJiri Pirko nla_put_failure: 247177b9900eSJiri Pirko nla_nest_cancel(skb, nest); 247277b9900eSJiri Pirko return -1; 247377b9900eSJiri Pirko } 247477b9900eSJiri Pirko 2475b95ec7ebSJiri Pirko static int fl_tmplt_dump(struct sk_buff *skb, struct net *net, void *tmplt_priv) 2476b95ec7ebSJiri Pirko { 2477b95ec7ebSJiri Pirko struct fl_flow_tmplt *tmplt = tmplt_priv; 2478b95ec7ebSJiri Pirko struct fl_flow_key *key, *mask; 2479b95ec7ebSJiri Pirko struct nlattr *nest; 2480b95ec7ebSJiri Pirko 2481ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 2482b95ec7ebSJiri Pirko if (!nest) 2483b95ec7ebSJiri Pirko goto nla_put_failure; 2484b95ec7ebSJiri Pirko 2485b95ec7ebSJiri Pirko key = &tmplt->dummy_key; 2486b95ec7ebSJiri Pirko mask = &tmplt->mask; 2487b95ec7ebSJiri Pirko 2488b95ec7ebSJiri Pirko if (fl_dump_key(skb, net, key, mask)) 2489b95ec7ebSJiri Pirko goto nla_put_failure; 2490b95ec7ebSJiri Pirko 2491b95ec7ebSJiri Pirko nla_nest_end(skb, nest); 2492b95ec7ebSJiri Pirko 2493b95ec7ebSJiri Pirko return skb->len; 2494b95ec7ebSJiri Pirko 2495b95ec7ebSJiri Pirko nla_put_failure: 2496b95ec7ebSJiri Pirko nla_nest_cancel(skb, nest); 2497b95ec7ebSJiri Pirko return -EMSGSIZE; 2498b95ec7ebSJiri Pirko } 2499b95ec7ebSJiri Pirko 250007d79fc7SCong Wang static void fl_bind_class(void *fh, u32 classid, unsigned long cl) 250107d79fc7SCong Wang { 250207d79fc7SCong Wang struct cls_fl_filter *f = fh; 250307d79fc7SCong Wang 250407d79fc7SCong Wang if (f && f->res.classid == classid) 250507d79fc7SCong Wang f->res.class = cl; 250607d79fc7SCong Wang } 250707d79fc7SCong Wang 250877b9900eSJiri Pirko static struct tcf_proto_ops cls_fl_ops __read_mostly = { 250977b9900eSJiri Pirko .kind = "flower", 251077b9900eSJiri Pirko .classify = fl_classify, 251177b9900eSJiri Pirko .init = fl_init, 251277b9900eSJiri Pirko .destroy = fl_destroy, 251377b9900eSJiri Pirko .get = fl_get, 251406177558SVlad Buslov .put = fl_put, 251577b9900eSJiri Pirko .change = fl_change, 251677b9900eSJiri Pirko .delete = fl_delete, 251777b9900eSJiri Pirko .walk = fl_walk, 251831533cbaSJohn Hurley .reoffload = fl_reoffload, 251977b9900eSJiri Pirko .dump = fl_dump, 252007d79fc7SCong Wang .bind_class = fl_bind_class, 2521b95ec7ebSJiri Pirko .tmplt_create = fl_tmplt_create, 2522b95ec7ebSJiri Pirko .tmplt_destroy = fl_tmplt_destroy, 2523b95ec7ebSJiri Pirko .tmplt_dump = fl_tmplt_dump, 252477b9900eSJiri Pirko .owner = THIS_MODULE, 252592149190SVlad Buslov .flags = TCF_PROTO_OPS_DOIT_UNLOCKED, 252677b9900eSJiri Pirko }; 252777b9900eSJiri Pirko 252877b9900eSJiri Pirko static int __init cls_fl_init(void) 252977b9900eSJiri Pirko { 253077b9900eSJiri Pirko return register_tcf_proto_ops(&cls_fl_ops); 253177b9900eSJiri Pirko } 253277b9900eSJiri Pirko 253377b9900eSJiri Pirko static void __exit cls_fl_exit(void) 253477b9900eSJiri Pirko { 253577b9900eSJiri Pirko unregister_tcf_proto_ops(&cls_fl_ops); 253677b9900eSJiri Pirko } 253777b9900eSJiri Pirko 253877b9900eSJiri Pirko module_init(cls_fl_init); 253977b9900eSJiri Pirko module_exit(cls_fl_exit); 254077b9900eSJiri Pirko 254177b9900eSJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 254277b9900eSJiri Pirko MODULE_DESCRIPTION("Flower classifier"); 254377b9900eSJiri Pirko MODULE_LICENSE("GPL v2"); 2544