177b9900eSJiri Pirko /* 277b9900eSJiri Pirko * net/sched/cls_flower.c Flower classifier 377b9900eSJiri Pirko * 477b9900eSJiri Pirko * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us> 577b9900eSJiri Pirko * 677b9900eSJiri Pirko * This program is free software; you can redistribute it and/or modify 777b9900eSJiri Pirko * it under the terms of the GNU General Public License as published by 877b9900eSJiri Pirko * the Free Software Foundation; either version 2 of the License, or 977b9900eSJiri Pirko * (at your option) any later version. 1077b9900eSJiri Pirko */ 1177b9900eSJiri Pirko 1277b9900eSJiri Pirko #include <linux/kernel.h> 1377b9900eSJiri Pirko #include <linux/init.h> 1477b9900eSJiri Pirko #include <linux/module.h> 1577b9900eSJiri Pirko #include <linux/rhashtable.h> 16d9363774SDaniel Borkmann #include <linux/workqueue.h> 1777b9900eSJiri Pirko 1877b9900eSJiri Pirko #include <linux/if_ether.h> 1977b9900eSJiri Pirko #include <linux/in6.h> 2077b9900eSJiri Pirko #include <linux/ip.h> 21a577d8f7SBenjamin LaHaise #include <linux/mpls.h> 2277b9900eSJiri Pirko 2377b9900eSJiri Pirko #include <net/sch_generic.h> 2477b9900eSJiri Pirko #include <net/pkt_cls.h> 2577b9900eSJiri Pirko #include <net/ip.h> 2677b9900eSJiri Pirko #include <net/flow_dissector.h> 2777b9900eSJiri Pirko 28bc3103f1SAmir Vadai #include <net/dst.h> 29bc3103f1SAmir Vadai #include <net/dst_metadata.h> 30bc3103f1SAmir Vadai 3177b9900eSJiri Pirko struct fl_flow_key { 3277b9900eSJiri Pirko int indev_ifindex; 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; 5677b9900eSJiri Pirko } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ 5777b9900eSJiri Pirko 5877b9900eSJiri Pirko struct fl_flow_mask_range { 5977b9900eSJiri Pirko unsigned short int start; 6077b9900eSJiri Pirko unsigned short int end; 6177b9900eSJiri Pirko }; 6277b9900eSJiri Pirko 6377b9900eSJiri Pirko struct fl_flow_mask { 6477b9900eSJiri Pirko struct fl_flow_key key; 6577b9900eSJiri Pirko struct fl_flow_mask_range range; 6605cd271fSPaul Blakey struct rhash_head ht_node; 6705cd271fSPaul Blakey struct rhashtable ht; 6805cd271fSPaul Blakey struct rhashtable_params filter_ht_params; 6905cd271fSPaul Blakey struct flow_dissector dissector; 7005cd271fSPaul Blakey struct list_head filters; 7144a5cd43SPaolo Abeni struct rcu_work rwork; 7205cd271fSPaul Blakey struct list_head list; 7377b9900eSJiri Pirko }; 7477b9900eSJiri Pirko 7577b9900eSJiri Pirko struct cls_fl_head { 7677b9900eSJiri Pirko struct rhashtable ht; 7705cd271fSPaul Blakey struct list_head masks; 78aaa908ffSCong Wang struct rcu_work rwork; 79c15ab236SChris Mi struct idr handle_idr; 80d9363774SDaniel Borkmann }; 8177b9900eSJiri Pirko 8277b9900eSJiri Pirko struct cls_fl_filter { 8305cd271fSPaul Blakey struct fl_flow_mask *mask; 8477b9900eSJiri Pirko struct rhash_head ht_node; 8577b9900eSJiri Pirko struct fl_flow_key mkey; 8677b9900eSJiri Pirko struct tcf_exts exts; 8777b9900eSJiri Pirko struct tcf_result res; 8877b9900eSJiri Pirko struct fl_flow_key key; 8977b9900eSJiri Pirko struct list_head list; 9077b9900eSJiri Pirko u32 handle; 91e69985c6SAmir Vadai u32 flags; 9231533cbaSJohn Hurley unsigned int in_hw_count; 93aaa908ffSCong Wang struct rcu_work rwork; 947091d8c7SHadar Hen Zion struct net_device *hw_dev; 9577b9900eSJiri Pirko }; 9677b9900eSJiri Pirko 9705cd271fSPaul Blakey static const struct rhashtable_params mask_ht_params = { 9805cd271fSPaul Blakey .key_offset = offsetof(struct fl_flow_mask, key), 9905cd271fSPaul Blakey .key_len = sizeof(struct fl_flow_key), 10005cd271fSPaul Blakey .head_offset = offsetof(struct fl_flow_mask, ht_node), 10105cd271fSPaul Blakey .automatic_shrinking = true, 10205cd271fSPaul Blakey }; 10305cd271fSPaul Blakey 10477b9900eSJiri Pirko static unsigned short int fl_mask_range(const struct fl_flow_mask *mask) 10577b9900eSJiri Pirko { 10677b9900eSJiri Pirko return mask->range.end - mask->range.start; 10777b9900eSJiri Pirko } 10877b9900eSJiri Pirko 10977b9900eSJiri Pirko static void fl_mask_update_range(struct fl_flow_mask *mask) 11077b9900eSJiri Pirko { 11177b9900eSJiri Pirko const u8 *bytes = (const u8 *) &mask->key; 11277b9900eSJiri Pirko size_t size = sizeof(mask->key); 11305cd271fSPaul Blakey size_t i, first = 0, last; 11477b9900eSJiri Pirko 11505cd271fSPaul Blakey for (i = 0; i < size; i++) { 11677b9900eSJiri Pirko if (bytes[i]) { 11777b9900eSJiri Pirko first = i; 11805cd271fSPaul Blakey break; 11905cd271fSPaul Blakey } 12005cd271fSPaul Blakey } 12105cd271fSPaul Blakey last = first; 12205cd271fSPaul Blakey for (i = size - 1; i != first; i--) { 12305cd271fSPaul Blakey if (bytes[i]) { 12477b9900eSJiri Pirko last = i; 12505cd271fSPaul Blakey break; 12677b9900eSJiri Pirko } 12777b9900eSJiri Pirko } 12877b9900eSJiri Pirko mask->range.start = rounddown(first, sizeof(long)); 12977b9900eSJiri Pirko mask->range.end = roundup(last + 1, sizeof(long)); 13077b9900eSJiri Pirko } 13177b9900eSJiri Pirko 13277b9900eSJiri Pirko static void *fl_key_get_start(struct fl_flow_key *key, 13377b9900eSJiri Pirko const struct fl_flow_mask *mask) 13477b9900eSJiri Pirko { 13577b9900eSJiri Pirko return (u8 *) key + mask->range.start; 13677b9900eSJiri Pirko } 13777b9900eSJiri Pirko 13877b9900eSJiri Pirko static void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key, 13977b9900eSJiri Pirko struct fl_flow_mask *mask) 14077b9900eSJiri Pirko { 14177b9900eSJiri Pirko const long *lkey = fl_key_get_start(key, mask); 14277b9900eSJiri Pirko const long *lmask = fl_key_get_start(&mask->key, mask); 14377b9900eSJiri Pirko long *lmkey = fl_key_get_start(mkey, mask); 14477b9900eSJiri Pirko int i; 14577b9900eSJiri Pirko 14677b9900eSJiri Pirko for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) 14777b9900eSJiri Pirko *lmkey++ = *lkey++ & *lmask++; 14877b9900eSJiri Pirko } 14977b9900eSJiri Pirko 15077b9900eSJiri Pirko static void fl_clear_masked_range(struct fl_flow_key *key, 15177b9900eSJiri Pirko struct fl_flow_mask *mask) 15277b9900eSJiri Pirko { 15377b9900eSJiri Pirko memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask)); 15477b9900eSJiri Pirko } 15577b9900eSJiri Pirko 15605cd271fSPaul Blakey static struct cls_fl_filter *fl_lookup(struct fl_flow_mask *mask, 157a3308d8fSPaul Blakey struct fl_flow_key *mkey) 158a3308d8fSPaul Blakey { 15905cd271fSPaul Blakey return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask), 16005cd271fSPaul Blakey mask->filter_ht_params); 161a3308d8fSPaul Blakey } 162a3308d8fSPaul Blakey 16377b9900eSJiri Pirko static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, 16477b9900eSJiri Pirko struct tcf_result *res) 16577b9900eSJiri Pirko { 16677b9900eSJiri Pirko struct cls_fl_head *head = rcu_dereference_bh(tp->root); 16777b9900eSJiri Pirko struct cls_fl_filter *f; 16805cd271fSPaul Blakey struct fl_flow_mask *mask; 16977b9900eSJiri Pirko struct fl_flow_key skb_key; 17077b9900eSJiri Pirko struct fl_flow_key skb_mkey; 17177b9900eSJiri Pirko 17205cd271fSPaul Blakey list_for_each_entry_rcu(mask, &head->masks, list) { 17305cd271fSPaul Blakey fl_clear_masked_range(&skb_key, mask); 174bc3103f1SAmir Vadai 17577b9900eSJiri Pirko skb_key.indev_ifindex = skb->skb_iif; 17605cd271fSPaul Blakey /* skb_flow_dissect() does not set n_proto in case an unknown 17705cd271fSPaul Blakey * protocol, so do it rather here. 17877b9900eSJiri Pirko */ 17977b9900eSJiri Pirko skb_key.basic.n_proto = skb->protocol; 18005cd271fSPaul Blakey skb_flow_dissect_tunnel_info(skb, &mask->dissector, &skb_key); 18105cd271fSPaul Blakey skb_flow_dissect(skb, &mask->dissector, &skb_key, 0); 18277b9900eSJiri Pirko 18305cd271fSPaul Blakey fl_set_masked_key(&skb_mkey, &skb_key, mask); 18477b9900eSJiri Pirko 18505cd271fSPaul Blakey f = fl_lookup(mask, &skb_mkey); 186e8eb36cdSAmir Vadai if (f && !tc_skip_sw(f->flags)) { 18777b9900eSJiri Pirko *res = f->res; 18877b9900eSJiri Pirko return tcf_exts_exec(skb, &f->exts, res); 18977b9900eSJiri Pirko } 19005cd271fSPaul Blakey } 19177b9900eSJiri Pirko return -1; 19277b9900eSJiri Pirko } 19377b9900eSJiri Pirko 19477b9900eSJiri Pirko static int fl_init(struct tcf_proto *tp) 19577b9900eSJiri Pirko { 19677b9900eSJiri Pirko struct cls_fl_head *head; 19777b9900eSJiri Pirko 19877b9900eSJiri Pirko head = kzalloc(sizeof(*head), GFP_KERNEL); 19977b9900eSJiri Pirko if (!head) 20077b9900eSJiri Pirko return -ENOBUFS; 20177b9900eSJiri Pirko 20205cd271fSPaul Blakey INIT_LIST_HEAD_RCU(&head->masks); 20377b9900eSJiri Pirko rcu_assign_pointer(tp->root, head); 204c15ab236SChris Mi idr_init(&head->handle_idr); 20577b9900eSJiri Pirko 20605cd271fSPaul Blakey return rhashtable_init(&head->ht, &mask_ht_params); 20705cd271fSPaul Blakey } 20805cd271fSPaul Blakey 20944a5cd43SPaolo Abeni static void fl_mask_free(struct fl_flow_mask *mask) 21044a5cd43SPaolo Abeni { 21144a5cd43SPaolo Abeni rhashtable_destroy(&mask->ht); 21244a5cd43SPaolo Abeni kfree(mask); 21344a5cd43SPaolo Abeni } 21444a5cd43SPaolo Abeni 21544a5cd43SPaolo Abeni static void fl_mask_free_work(struct work_struct *work) 21644a5cd43SPaolo Abeni { 21744a5cd43SPaolo Abeni struct fl_flow_mask *mask = container_of(to_rcu_work(work), 21844a5cd43SPaolo Abeni struct fl_flow_mask, rwork); 21944a5cd43SPaolo Abeni 22044a5cd43SPaolo Abeni fl_mask_free(mask); 22144a5cd43SPaolo Abeni } 22244a5cd43SPaolo Abeni 22305cd271fSPaul Blakey static bool fl_mask_put(struct cls_fl_head *head, struct fl_flow_mask *mask, 22405cd271fSPaul Blakey bool async) 22505cd271fSPaul Blakey { 22605cd271fSPaul Blakey if (!list_empty(&mask->filters)) 22705cd271fSPaul Blakey return false; 22805cd271fSPaul Blakey 22905cd271fSPaul Blakey rhashtable_remove_fast(&head->ht, &mask->ht_node, mask_ht_params); 23005cd271fSPaul Blakey list_del_rcu(&mask->list); 23105cd271fSPaul Blakey if (async) 23244a5cd43SPaolo Abeni tcf_queue_work(&mask->rwork, fl_mask_free_work); 23305cd271fSPaul Blakey else 23444a5cd43SPaolo Abeni fl_mask_free(mask); 23505cd271fSPaul Blakey 23605cd271fSPaul Blakey return true; 23777b9900eSJiri Pirko } 23877b9900eSJiri Pirko 2390dadc117SCong Wang static void __fl_destroy_filter(struct cls_fl_filter *f) 2400dadc117SCong Wang { 2410dadc117SCong Wang tcf_exts_destroy(&f->exts); 2420dadc117SCong Wang tcf_exts_put_net(&f->exts); 2430dadc117SCong Wang kfree(f); 2440dadc117SCong Wang } 2450dadc117SCong Wang 2460552c8afSCong Wang static void fl_destroy_filter_work(struct work_struct *work) 2470552c8afSCong Wang { 248aaa908ffSCong Wang struct cls_fl_filter *f = container_of(to_rcu_work(work), 249aaa908ffSCong Wang struct cls_fl_filter, rwork); 2500552c8afSCong Wang 2510552c8afSCong Wang rtnl_lock(); 2520dadc117SCong Wang __fl_destroy_filter(f); 2530552c8afSCong Wang rtnl_unlock(); 2540552c8afSCong Wang } 2550552c8afSCong Wang 2561b0f8037SJakub Kicinski static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f, 2571b0f8037SJakub Kicinski struct netlink_ext_ack *extack) 2585b33f488SAmir Vadai { 259de4784caSJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 260208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 2615b33f488SAmir Vadai 2621b0f8037SJakub Kicinski tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack); 263de4784caSJiri Pirko cls_flower.command = TC_CLSFLOWER_DESTROY; 264de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 2655b33f488SAmir Vadai 266208c0f4bSJiri Pirko tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, 267717503b9SJiri Pirko &cls_flower, false); 268caa72601SJiri Pirko tcf_block_offload_dec(block, &f->flags); 2695b33f488SAmir Vadai } 2705b33f488SAmir Vadai 271e8eb36cdSAmir Vadai static int fl_hw_replace_filter(struct tcf_proto *tp, 27241002038SQuentin Monnet struct cls_fl_filter *f, 27341002038SQuentin Monnet struct netlink_ext_ack *extack) 2745b33f488SAmir Vadai { 275de4784caSJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 276208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 277717503b9SJiri Pirko bool skip_sw = tc_skip_sw(f->flags); 278e8eb36cdSAmir Vadai int err; 2795b33f488SAmir Vadai 280ea205940SJakub Kicinski tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack); 281de4784caSJiri Pirko cls_flower.command = TC_CLSFLOWER_REPLACE; 282de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 28305cd271fSPaul Blakey cls_flower.dissector = &f->mask->dissector; 28405cd271fSPaul Blakey cls_flower.mask = &f->mask->key; 285de4784caSJiri Pirko cls_flower.key = &f->mkey; 286de4784caSJiri Pirko cls_flower.exts = &f->exts; 287384c181eSAmritha Nambiar cls_flower.classid = f->res.classid; 2885b33f488SAmir Vadai 289208c0f4bSJiri Pirko err = tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, 290717503b9SJiri Pirko &cls_flower, skip_sw); 291717503b9SJiri Pirko if (err < 0) { 2921b0f8037SJakub Kicinski fl_hw_destroy_filter(tp, f, NULL); 293717503b9SJiri Pirko return err; 294717503b9SJiri Pirko } else if (err > 0) { 29531533cbaSJohn Hurley f->in_hw_count = err; 296caa72601SJiri Pirko tcf_block_offload_inc(block, &f->flags); 297717503b9SJiri Pirko } 298717503b9SJiri Pirko 299717503b9SJiri Pirko if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) 300717503b9SJiri Pirko return -EINVAL; 301717503b9SJiri Pirko 302e8eb36cdSAmir Vadai return 0; 3035b33f488SAmir Vadai } 3045b33f488SAmir Vadai 30510cbc684SAmir Vadai static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) 30610cbc684SAmir Vadai { 307de4784caSJiri Pirko struct tc_cls_flower_offload cls_flower = {}; 308208c0f4bSJiri Pirko struct tcf_block *block = tp->chain->block; 30910cbc684SAmir Vadai 310ea205940SJakub Kicinski tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL); 311de4784caSJiri Pirko cls_flower.command = TC_CLSFLOWER_STATS; 312de4784caSJiri Pirko cls_flower.cookie = (unsigned long) f; 313de4784caSJiri Pirko cls_flower.exts = &f->exts; 314384c181eSAmritha Nambiar cls_flower.classid = f->res.classid; 31510cbc684SAmir Vadai 316208c0f4bSJiri Pirko tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, 317717503b9SJiri Pirko &cls_flower, false); 31810cbc684SAmir Vadai } 31910cbc684SAmir Vadai 32005cd271fSPaul Blakey static bool __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, 3211b0f8037SJakub Kicinski struct netlink_ext_ack *extack) 32213fa876eSRoi Dayan { 323c15ab236SChris Mi struct cls_fl_head *head = rtnl_dereference(tp->root); 32405cd271fSPaul Blakey bool async = tcf_exts_get_net(&f->exts); 32505cd271fSPaul Blakey bool last; 326c15ab236SChris Mi 3279c160941SMatthew Wilcox idr_remove(&head->handle_idr, f->handle); 32813fa876eSRoi Dayan list_del_rcu(&f->list); 32905cd271fSPaul Blakey last = fl_mask_put(head, f->mask, async); 33079685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 3311b0f8037SJakub Kicinski fl_hw_destroy_filter(tp, f, extack); 33213fa876eSRoi Dayan tcf_unbind_filter(tp, &f->res); 33305cd271fSPaul Blakey if (async) 334aaa908ffSCong Wang tcf_queue_work(&f->rwork, fl_destroy_filter_work); 3350dadc117SCong Wang else 3360dadc117SCong Wang __fl_destroy_filter(f); 33705cd271fSPaul Blakey 33805cd271fSPaul Blakey return last; 33913fa876eSRoi Dayan } 34013fa876eSRoi Dayan 341d9363774SDaniel Borkmann static void fl_destroy_sleepable(struct work_struct *work) 342d9363774SDaniel Borkmann { 343aaa908ffSCong Wang struct cls_fl_head *head = container_of(to_rcu_work(work), 344aaa908ffSCong Wang struct cls_fl_head, 345aaa908ffSCong Wang rwork); 346de9dc650SPaul Blakey 347de9dc650SPaul Blakey rhashtable_destroy(&head->ht); 348d9363774SDaniel Borkmann kfree(head); 349d9363774SDaniel Borkmann module_put(THIS_MODULE); 350d9363774SDaniel Borkmann } 351d9363774SDaniel Borkmann 352715df5ecSJakub Kicinski static void fl_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack) 35377b9900eSJiri Pirko { 35477b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 35505cd271fSPaul Blakey struct fl_flow_mask *mask, *next_mask; 35677b9900eSJiri Pirko struct cls_fl_filter *f, *next; 35777b9900eSJiri Pirko 35805cd271fSPaul Blakey list_for_each_entry_safe(mask, next_mask, &head->masks, list) { 35905cd271fSPaul Blakey list_for_each_entry_safe(f, next, &mask->filters, list) { 36005cd271fSPaul Blakey if (__fl_delete(tp, f, extack)) 36105cd271fSPaul Blakey break; 36205cd271fSPaul Blakey } 36305cd271fSPaul Blakey } 364c15ab236SChris Mi idr_destroy(&head->handle_idr); 365d9363774SDaniel Borkmann 366d9363774SDaniel Borkmann __module_get(THIS_MODULE); 367aaa908ffSCong Wang tcf_queue_work(&head->rwork, fl_destroy_sleepable); 36877b9900eSJiri Pirko } 36977b9900eSJiri Pirko 3708113c095SWANG Cong static void *fl_get(struct tcf_proto *tp, u32 handle) 37177b9900eSJiri Pirko { 37277b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 37377b9900eSJiri Pirko 374322d884bSMatthew Wilcox return idr_find(&head->handle_idr, handle); 37577b9900eSJiri Pirko } 37677b9900eSJiri Pirko 37777b9900eSJiri Pirko static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { 37877b9900eSJiri Pirko [TCA_FLOWER_UNSPEC] = { .type = NLA_UNSPEC }, 37977b9900eSJiri Pirko [TCA_FLOWER_CLASSID] = { .type = NLA_U32 }, 38077b9900eSJiri Pirko [TCA_FLOWER_INDEV] = { .type = NLA_STRING, 38177b9900eSJiri Pirko .len = IFNAMSIZ }, 38277b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST] = { .len = ETH_ALEN }, 38377b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST_MASK] = { .len = ETH_ALEN }, 38477b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC] = { .len = ETH_ALEN }, 38577b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .len = ETH_ALEN }, 38677b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, 38777b9900eSJiri Pirko [TCA_FLOWER_KEY_IP_PROTO] = { .type = NLA_U8 }, 38877b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, 38977b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, 39077b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, 39177b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, 39277b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 39377b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 39477b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 39577b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 39677b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_SRC] = { .type = NLA_U16 }, 39777b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_DST] = { .type = NLA_U16 }, 398b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_SRC] = { .type = NLA_U16 }, 399b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_DST] = { .type = NLA_U16 }, 4009399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, 4019399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, 4029399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, 403bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, 404bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, 405bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 }, 406bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, 407bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 }, 408bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 409bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 410bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 411bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 412aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NLA_U16 }, 413aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, 414aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, 415aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, 4165976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, 4175976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, 4185976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, 4195976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, 420f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, 421f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, 422f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, 423f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, 424faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, 425faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, 4267b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NLA_U8 }, 4277b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 }, 4287b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NLA_U8 }, 4297b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 }, 4307b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NLA_U8 }, 4317b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 }, 4327b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NLA_U8 }, 4337b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 }, 43499d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP] = { .type = NLA_U32 }, 43599d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NLA_U32 }, 43699d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP] = { .type = NLA_U32 }, 43799d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NLA_U32 }, 43899d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP] = { .type = NLA_U8 }, 43999d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NLA_U8 }, 44099d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA] = { .len = ETH_ALEN }, 44199d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .len = ETH_ALEN }, 44299d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA] = { .len = ETH_ALEN }, 44399d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA_MASK] = { .len = ETH_ALEN }, 444a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NLA_U8 }, 445a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NLA_U8 }, 446a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_TC] = { .type = NLA_U8 }, 447a577d8f7SBenjamin LaHaise [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NLA_U32 }, 448fdfc7dd6SJiri Pirko [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NLA_U16 }, 449fdfc7dd6SJiri Pirko [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NLA_U16 }, 4504d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TOS] = { .type = NLA_U8 }, 4514d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NLA_U8 }, 4524d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TTL] = { .type = NLA_U8 }, 4534d80cc0aSOr Gerlitz [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NLA_U8 }, 454d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NLA_U16 }, 455d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NLA_U8 }, 456d64efd09SJianbo Liu [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NLA_U16 }, 4570e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NLA_U8 }, 4580e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NLA_U8 }, 4590e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NLA_U8 }, 4600e2c17b6SOr Gerlitz [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 }, 46177b9900eSJiri Pirko }; 46277b9900eSJiri Pirko 46377b9900eSJiri Pirko static void fl_set_key_val(struct nlattr **tb, 46477b9900eSJiri Pirko void *val, int val_type, 46577b9900eSJiri Pirko void *mask, int mask_type, int len) 46677b9900eSJiri Pirko { 46777b9900eSJiri Pirko if (!tb[val_type]) 46877b9900eSJiri Pirko return; 46977b9900eSJiri Pirko memcpy(val, nla_data(tb[val_type]), len); 47077b9900eSJiri Pirko if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type]) 47177b9900eSJiri Pirko memset(mask, 0xff, len); 47277b9900eSJiri Pirko else 47377b9900eSJiri Pirko memcpy(mask, nla_data(tb[mask_type]), len); 47477b9900eSJiri Pirko } 47577b9900eSJiri Pirko 4761a7fca63SBenjamin LaHaise static int fl_set_key_mpls(struct nlattr **tb, 477a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *key_val, 478a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *key_mask) 479a577d8f7SBenjamin LaHaise { 480a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_TTL]) { 481a577d8f7SBenjamin LaHaise key_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]); 482a577d8f7SBenjamin LaHaise key_mask->mpls_ttl = MPLS_TTL_MASK; 483a577d8f7SBenjamin LaHaise } 484a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_BOS]) { 4851a7fca63SBenjamin LaHaise u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); 4861a7fca63SBenjamin LaHaise 4871a7fca63SBenjamin LaHaise if (bos & ~MPLS_BOS_MASK) 4881a7fca63SBenjamin LaHaise return -EINVAL; 4891a7fca63SBenjamin LaHaise key_val->mpls_bos = bos; 490a577d8f7SBenjamin LaHaise key_mask->mpls_bos = MPLS_BOS_MASK; 491a577d8f7SBenjamin LaHaise } 492a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_TC]) { 4931a7fca63SBenjamin LaHaise u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]); 4941a7fca63SBenjamin LaHaise 4951a7fca63SBenjamin LaHaise if (tc & ~MPLS_TC_MASK) 4961a7fca63SBenjamin LaHaise return -EINVAL; 4971a7fca63SBenjamin LaHaise key_val->mpls_tc = tc; 498a577d8f7SBenjamin LaHaise key_mask->mpls_tc = MPLS_TC_MASK; 499a577d8f7SBenjamin LaHaise } 500a577d8f7SBenjamin LaHaise if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) { 5011a7fca63SBenjamin LaHaise u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]); 5021a7fca63SBenjamin LaHaise 5031a7fca63SBenjamin LaHaise if (label & ~MPLS_LABEL_MASK) 5041a7fca63SBenjamin LaHaise return -EINVAL; 5051a7fca63SBenjamin LaHaise key_val->mpls_label = label; 506a577d8f7SBenjamin LaHaise key_mask->mpls_label = MPLS_LABEL_MASK; 507a577d8f7SBenjamin LaHaise } 5081a7fca63SBenjamin LaHaise return 0; 509a577d8f7SBenjamin LaHaise } 510a577d8f7SBenjamin LaHaise 5119399ae9aSHadar Hen Zion static void fl_set_key_vlan(struct nlattr **tb, 512aaab0834SJianbo Liu __be16 ethertype, 513d64efd09SJianbo Liu int vlan_id_key, int vlan_prio_key, 5149399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_val, 5159399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_mask) 5169399ae9aSHadar Hen Zion { 5179399ae9aSHadar Hen Zion #define VLAN_PRIORITY_MASK 0x7 5189399ae9aSHadar Hen Zion 519d64efd09SJianbo Liu if (tb[vlan_id_key]) { 5209399ae9aSHadar Hen Zion key_val->vlan_id = 521d64efd09SJianbo Liu nla_get_u16(tb[vlan_id_key]) & VLAN_VID_MASK; 5229399ae9aSHadar Hen Zion key_mask->vlan_id = VLAN_VID_MASK; 5239399ae9aSHadar Hen Zion } 524d64efd09SJianbo Liu if (tb[vlan_prio_key]) { 5259399ae9aSHadar Hen Zion key_val->vlan_priority = 526d64efd09SJianbo Liu nla_get_u8(tb[vlan_prio_key]) & 5279399ae9aSHadar Hen Zion VLAN_PRIORITY_MASK; 5289399ae9aSHadar Hen Zion key_mask->vlan_priority = VLAN_PRIORITY_MASK; 5299399ae9aSHadar Hen Zion } 530aaab0834SJianbo Liu key_val->vlan_tpid = ethertype; 531aaab0834SJianbo Liu key_mask->vlan_tpid = cpu_to_be16(~0); 5329399ae9aSHadar Hen Zion } 5339399ae9aSHadar Hen Zion 534faa3ffceSOr Gerlitz static void fl_set_key_flag(u32 flower_key, u32 flower_mask, 535faa3ffceSOr Gerlitz u32 *dissector_key, u32 *dissector_mask, 536faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 537faa3ffceSOr Gerlitz { 538faa3ffceSOr Gerlitz if (flower_mask & flower_flag_bit) { 539faa3ffceSOr Gerlitz *dissector_mask |= dissector_flag_bit; 540faa3ffceSOr Gerlitz if (flower_key & flower_flag_bit) 541faa3ffceSOr Gerlitz *dissector_key |= dissector_flag_bit; 542faa3ffceSOr Gerlitz } 543faa3ffceSOr Gerlitz } 544faa3ffceSOr Gerlitz 545d9724772SOr Gerlitz static int fl_set_key_flags(struct nlattr **tb, 546faa3ffceSOr Gerlitz u32 *flags_key, u32 *flags_mask) 547faa3ffceSOr Gerlitz { 548faa3ffceSOr Gerlitz u32 key, mask; 549faa3ffceSOr Gerlitz 550d9724772SOr Gerlitz /* mask is mandatory for flags */ 551d9724772SOr Gerlitz if (!tb[TCA_FLOWER_KEY_FLAGS_MASK]) 552d9724772SOr Gerlitz return -EINVAL; 553faa3ffceSOr Gerlitz 554faa3ffceSOr Gerlitz key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS])); 555faa3ffceSOr Gerlitz mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK])); 556faa3ffceSOr Gerlitz 557faa3ffceSOr Gerlitz *flags_key = 0; 558faa3ffceSOr Gerlitz *flags_mask = 0; 559faa3ffceSOr Gerlitz 560faa3ffceSOr Gerlitz fl_set_key_flag(key, mask, flags_key, flags_mask, 561faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 562459d153dSPieter Jansen van Vuuren fl_set_key_flag(key, mask, flags_key, flags_mask, 563459d153dSPieter Jansen van Vuuren TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 564459d153dSPieter Jansen van Vuuren FLOW_DIS_FIRST_FRAG); 565d9724772SOr Gerlitz 566d9724772SOr Gerlitz return 0; 567faa3ffceSOr Gerlitz } 568faa3ffceSOr Gerlitz 5690e2c17b6SOr Gerlitz static void fl_set_key_ip(struct nlattr **tb, bool encap, 5704d80cc0aSOr Gerlitz struct flow_dissector_key_ip *key, 5714d80cc0aSOr Gerlitz struct flow_dissector_key_ip *mask) 5724d80cc0aSOr Gerlitz { 5730e2c17b6SOr Gerlitz int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 5740e2c17b6SOr Gerlitz int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 5750e2c17b6SOr Gerlitz int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 5760e2c17b6SOr Gerlitz int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 5774d80cc0aSOr Gerlitz 5780e2c17b6SOr Gerlitz fl_set_key_val(tb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)); 5790e2c17b6SOr Gerlitz fl_set_key_val(tb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl)); 5804d80cc0aSOr Gerlitz } 5814d80cc0aSOr Gerlitz 58277b9900eSJiri Pirko static int fl_set_key(struct net *net, struct nlattr **tb, 5831057c55fSAlexander Aring struct fl_flow_key *key, struct fl_flow_key *mask, 5841057c55fSAlexander Aring struct netlink_ext_ack *extack) 58577b9900eSJiri Pirko { 5869399ae9aSHadar Hen Zion __be16 ethertype; 587d9724772SOr Gerlitz int ret = 0; 588dd3aa3b5SBrian Haley #ifdef CONFIG_NET_CLS_IND 58977b9900eSJiri Pirko if (tb[TCA_FLOWER_INDEV]) { 5901057c55fSAlexander Aring int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV], extack); 59177b9900eSJiri Pirko if (err < 0) 59277b9900eSJiri Pirko return err; 59377b9900eSJiri Pirko key->indev_ifindex = err; 59477b9900eSJiri Pirko mask->indev_ifindex = 0xffffffff; 59577b9900eSJiri Pirko } 596dd3aa3b5SBrian Haley #endif 59777b9900eSJiri Pirko 59877b9900eSJiri Pirko fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 59977b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 60077b9900eSJiri Pirko sizeof(key->eth.dst)); 60177b9900eSJiri Pirko fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 60277b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 60377b9900eSJiri Pirko sizeof(key->eth.src)); 60466530bdfSJamal Hadi Salim 6050b498a52SArnd Bergmann if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { 6069399ae9aSHadar Hen Zion ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]); 6079399ae9aSHadar Hen Zion 608aaab0834SJianbo Liu if (eth_type_vlan(ethertype)) { 609d64efd09SJianbo Liu fl_set_key_vlan(tb, ethertype, TCA_FLOWER_KEY_VLAN_ID, 610d64efd09SJianbo Liu TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, 611d64efd09SJianbo Liu &mask->vlan); 612d64efd09SJianbo Liu 6135e9a0fe4SJianbo Liu if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { 614d64efd09SJianbo Liu ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); 615d64efd09SJianbo Liu if (eth_type_vlan(ethertype)) { 616d64efd09SJianbo Liu fl_set_key_vlan(tb, ethertype, 617d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_ID, 618d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_PRIO, 619d64efd09SJianbo Liu &key->cvlan, &mask->cvlan); 6209399ae9aSHadar Hen Zion fl_set_key_val(tb, &key->basic.n_proto, 621d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 622d64efd09SJianbo Liu &mask->basic.n_proto, 623d64efd09SJianbo Liu TCA_FLOWER_UNSPEC, 62477b9900eSJiri Pirko sizeof(key->basic.n_proto)); 6259399ae9aSHadar Hen Zion } else { 6269399ae9aSHadar Hen Zion key->basic.n_proto = ethertype; 6279399ae9aSHadar Hen Zion mask->basic.n_proto = cpu_to_be16(~0); 6289399ae9aSHadar Hen Zion } 6295e9a0fe4SJianbo Liu } 630d64efd09SJianbo Liu } else { 631d64efd09SJianbo Liu key->basic.n_proto = ethertype; 632d64efd09SJianbo Liu mask->basic.n_proto = cpu_to_be16(~0); 633d64efd09SJianbo Liu } 6340b498a52SArnd Bergmann } 63566530bdfSJamal Hadi Salim 63677b9900eSJiri Pirko if (key->basic.n_proto == htons(ETH_P_IP) || 63777b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) { 63877b9900eSJiri Pirko fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 63977b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 64077b9900eSJiri Pirko sizeof(key->basic.ip_proto)); 6410e2c17b6SOr Gerlitz fl_set_key_ip(tb, false, &key->ip, &mask->ip); 64277b9900eSJiri Pirko } 64366530bdfSJamal Hadi Salim 64466530bdfSJamal Hadi Salim if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) { 64566530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 646970bfcd0SPaul Blakey mask->control.addr_type = ~0; 64777b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 64877b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 64977b9900eSJiri Pirko sizeof(key->ipv4.src)); 65077b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 65177b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 65277b9900eSJiri Pirko sizeof(key->ipv4.dst)); 65366530bdfSJamal Hadi Salim } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) { 65466530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 655970bfcd0SPaul Blakey mask->control.addr_type = ~0; 65677b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 65777b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 65877b9900eSJiri Pirko sizeof(key->ipv6.src)); 65977b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 66077b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 66177b9900eSJiri Pirko sizeof(key->ipv6.dst)); 66277b9900eSJiri Pirko } 66366530bdfSJamal Hadi Salim 66477b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP) { 66577b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 666aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 66777b9900eSJiri Pirko sizeof(key->tp.src)); 66877b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 669aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 67077b9900eSJiri Pirko sizeof(key->tp.dst)); 671fdfc7dd6SJiri Pirko fl_set_key_val(tb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 672fdfc7dd6SJiri Pirko &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 673fdfc7dd6SJiri Pirko sizeof(key->tcp.flags)); 67477b9900eSJiri Pirko } else if (key->basic.ip_proto == IPPROTO_UDP) { 67577b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 676aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 67777b9900eSJiri Pirko sizeof(key->tp.src)); 67877b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 679aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 68077b9900eSJiri Pirko sizeof(key->tp.dst)); 6815976c5f4SSimon Horman } else if (key->basic.ip_proto == IPPROTO_SCTP) { 6825976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 6835976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 6845976c5f4SSimon Horman sizeof(key->tp.src)); 6855976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 6865976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 6875976c5f4SSimon Horman sizeof(key->tp.dst)); 6887b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IP) && 6897b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP) { 6907b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE, 6917b684884SSimon Horman &mask->icmp.type, 6927b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 6937b684884SSimon Horman sizeof(key->icmp.type)); 6947b684884SSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, 6957b684884SSimon Horman &mask->icmp.code, 6967b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 6977b684884SSimon Horman sizeof(key->icmp.code)); 6987b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IPV6) && 6997b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6) { 7007b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE, 7017b684884SSimon Horman &mask->icmp.type, 7027b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 7037b684884SSimon Horman sizeof(key->icmp.type)); 704040587afSSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE, 7057b684884SSimon Horman &mask->icmp.code, 706040587afSSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 7077b684884SSimon Horman sizeof(key->icmp.code)); 708a577d8f7SBenjamin LaHaise } else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) || 709a577d8f7SBenjamin LaHaise key->basic.n_proto == htons(ETH_P_MPLS_MC)) { 7101a7fca63SBenjamin LaHaise ret = fl_set_key_mpls(tb, &key->mpls, &mask->mpls); 7111a7fca63SBenjamin LaHaise if (ret) 7121a7fca63SBenjamin LaHaise return ret; 71399d31326SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_ARP) || 71499d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) { 71599d31326SSimon Horman fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, 71699d31326SSimon Horman &mask->arp.sip, TCA_FLOWER_KEY_ARP_SIP_MASK, 71799d31326SSimon Horman sizeof(key->arp.sip)); 71899d31326SSimon Horman fl_set_key_val(tb, &key->arp.tip, TCA_FLOWER_KEY_ARP_TIP, 71999d31326SSimon Horman &mask->arp.tip, TCA_FLOWER_KEY_ARP_TIP_MASK, 72099d31326SSimon Horman sizeof(key->arp.tip)); 72199d31326SSimon Horman fl_set_key_val(tb, &key->arp.op, TCA_FLOWER_KEY_ARP_OP, 72299d31326SSimon Horman &mask->arp.op, TCA_FLOWER_KEY_ARP_OP_MASK, 72399d31326SSimon Horman sizeof(key->arp.op)); 72499d31326SSimon Horman fl_set_key_val(tb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 72599d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 72699d31326SSimon Horman sizeof(key->arp.sha)); 72799d31326SSimon Horman fl_set_key_val(tb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 72899d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 72999d31326SSimon Horman sizeof(key->arp.tha)); 73077b9900eSJiri Pirko } 73177b9900eSJiri Pirko 732bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || 733bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) { 734bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 735970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 736bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.src, 737bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, 738bc3103f1SAmir Vadai &mask->enc_ipv4.src, 739bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 740bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)); 741bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.dst, 742bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, 743bc3103f1SAmir Vadai &mask->enc_ipv4.dst, 744bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 745bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)); 746bc3103f1SAmir Vadai } 747bc3103f1SAmir Vadai 748bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] || 749bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) { 750bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 751970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 752bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.src, 753bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, 754bc3103f1SAmir Vadai &mask->enc_ipv6.src, 755bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 756bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)); 757bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.dst, 758bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 759bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 760bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 761bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)); 762bc3103f1SAmir Vadai } 763bc3103f1SAmir Vadai 764bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID, 765eb523f42SHadar Hen Zion &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, 766bc3103f1SAmir Vadai sizeof(key->enc_key_id.keyid)); 767bc3103f1SAmir Vadai 768f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 769f4d997fdSHadar Hen Zion &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 770f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)); 771f4d997fdSHadar Hen Zion 772f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 773f4d997fdSHadar Hen Zion &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 774f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst)); 775f4d997fdSHadar Hen Zion 7760e2c17b6SOr Gerlitz fl_set_key_ip(tb, true, &key->enc_ip, &mask->enc_ip); 7770e2c17b6SOr Gerlitz 778d9724772SOr Gerlitz if (tb[TCA_FLOWER_KEY_FLAGS]) 779d9724772SOr Gerlitz ret = fl_set_key_flags(tb, &key->control.flags, &mask->control.flags); 780faa3ffceSOr Gerlitz 781d9724772SOr Gerlitz return ret; 78277b9900eSJiri Pirko } 78377b9900eSJiri Pirko 78405cd271fSPaul Blakey static void fl_mask_copy(struct fl_flow_mask *dst, 78505cd271fSPaul Blakey struct fl_flow_mask *src) 78677b9900eSJiri Pirko { 78705cd271fSPaul Blakey const void *psrc = fl_key_get_start(&src->key, src); 78805cd271fSPaul Blakey void *pdst = fl_key_get_start(&dst->key, src); 78977b9900eSJiri Pirko 79005cd271fSPaul Blakey memcpy(pdst, psrc, fl_mask_range(src)); 79105cd271fSPaul Blakey dst->range = src->range; 79277b9900eSJiri Pirko } 79377b9900eSJiri Pirko 79477b9900eSJiri Pirko static const struct rhashtable_params fl_ht_params = { 79577b9900eSJiri Pirko .key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */ 79677b9900eSJiri Pirko .head_offset = offsetof(struct cls_fl_filter, ht_node), 79777b9900eSJiri Pirko .automatic_shrinking = true, 79877b9900eSJiri Pirko }; 79977b9900eSJiri Pirko 80005cd271fSPaul Blakey static int fl_init_mask_hashtable(struct fl_flow_mask *mask) 80177b9900eSJiri Pirko { 80205cd271fSPaul Blakey mask->filter_ht_params = fl_ht_params; 80305cd271fSPaul Blakey mask->filter_ht_params.key_len = fl_mask_range(mask); 80405cd271fSPaul Blakey mask->filter_ht_params.key_offset += mask->range.start; 80577b9900eSJiri Pirko 80605cd271fSPaul Blakey return rhashtable_init(&mask->ht, &mask->filter_ht_params); 80777b9900eSJiri Pirko } 80877b9900eSJiri Pirko 80977b9900eSJiri Pirko #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) 81077b9900eSJiri Pirko #define FL_KEY_MEMBER_SIZE(member) (sizeof(((struct fl_flow_key *) 0)->member)) 81177b9900eSJiri Pirko 812339ba878SHadar Hen Zion #define FL_KEY_IS_MASKED(mask, member) \ 813339ba878SHadar Hen Zion memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ 814339ba878SHadar Hen Zion 0, FL_KEY_MEMBER_SIZE(member)) \ 81577b9900eSJiri Pirko 81677b9900eSJiri Pirko #define FL_KEY_SET(keys, cnt, id, member) \ 81777b9900eSJiri Pirko do { \ 81877b9900eSJiri Pirko keys[cnt].key_id = id; \ 81977b9900eSJiri Pirko keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member); \ 82077b9900eSJiri Pirko cnt++; \ 82177b9900eSJiri Pirko } while(0); 82277b9900eSJiri Pirko 823339ba878SHadar Hen Zion #define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member) \ 82477b9900eSJiri Pirko do { \ 825339ba878SHadar Hen Zion if (FL_KEY_IS_MASKED(mask, member)) \ 82677b9900eSJiri Pirko FL_KEY_SET(keys, cnt, id, member); \ 82777b9900eSJiri Pirko } while(0); 82877b9900eSJiri Pirko 829*33fb5cbaSJiri Pirko static void fl_init_dissector(struct flow_dissector *dissector, 830*33fb5cbaSJiri Pirko struct fl_flow_key *mask) 83177b9900eSJiri Pirko { 83277b9900eSJiri Pirko struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX]; 83377b9900eSJiri Pirko size_t cnt = 0; 83477b9900eSJiri Pirko 83542aecaa9STom Herbert FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control); 83677b9900eSJiri Pirko FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic); 837*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 83877b9900eSJiri Pirko FLOW_DISSECTOR_KEY_ETH_ADDRS, eth); 839*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 84077b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); 841*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 84277b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); 843*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 84477b9900eSJiri Pirko FLOW_DISSECTOR_KEY_PORTS, tp); 845*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 8464d80cc0aSOr Gerlitz FLOW_DISSECTOR_KEY_IP, ip); 847*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 848fdfc7dd6SJiri Pirko FLOW_DISSECTOR_KEY_TCP, tcp); 849*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 8507b684884SSimon Horman FLOW_DISSECTOR_KEY_ICMP, icmp); 851*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 85299d31326SSimon Horman FLOW_DISSECTOR_KEY_ARP, arp); 853*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 854a577d8f7SBenjamin LaHaise FLOW_DISSECTOR_KEY_MPLS, mpls); 855*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 8569399ae9aSHadar Hen Zion FLOW_DISSECTOR_KEY_VLAN, vlan); 857*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 858d64efd09SJianbo Liu FLOW_DISSECTOR_KEY_CVLAN, cvlan); 859*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 860519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); 861*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 862519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); 863*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 864519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); 865*33fb5cbaSJiri Pirko if (FL_KEY_IS_MASKED(mask, enc_ipv4) || 866*33fb5cbaSJiri Pirko FL_KEY_IS_MASKED(mask, enc_ipv6)) 867519d1052SHadar Hen Zion FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, 868519d1052SHadar Hen Zion enc_control); 869*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 870f4d997fdSHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); 871*33fb5cbaSJiri Pirko FL_KEY_SET_IF_MASKED(mask, keys, cnt, 8720e2c17b6SOr Gerlitz FLOW_DISSECTOR_KEY_ENC_IP, enc_ip); 87377b9900eSJiri Pirko 874*33fb5cbaSJiri Pirko skb_flow_dissector_init(dissector, keys, cnt); 87505cd271fSPaul Blakey } 87605cd271fSPaul Blakey 87705cd271fSPaul Blakey static struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head, 87805cd271fSPaul Blakey struct fl_flow_mask *mask) 87905cd271fSPaul Blakey { 88005cd271fSPaul Blakey struct fl_flow_mask *newmask; 88105cd271fSPaul Blakey int err; 88205cd271fSPaul Blakey 88305cd271fSPaul Blakey newmask = kzalloc(sizeof(*newmask), GFP_KERNEL); 88405cd271fSPaul Blakey if (!newmask) 88505cd271fSPaul Blakey return ERR_PTR(-ENOMEM); 88605cd271fSPaul Blakey 88705cd271fSPaul Blakey fl_mask_copy(newmask, mask); 88805cd271fSPaul Blakey 88905cd271fSPaul Blakey err = fl_init_mask_hashtable(newmask); 89005cd271fSPaul Blakey if (err) 89105cd271fSPaul Blakey goto errout_free; 89205cd271fSPaul Blakey 893*33fb5cbaSJiri Pirko fl_init_dissector(&newmask->dissector, &newmask->key); 89405cd271fSPaul Blakey 89505cd271fSPaul Blakey INIT_LIST_HEAD_RCU(&newmask->filters); 89605cd271fSPaul Blakey 89705cd271fSPaul Blakey err = rhashtable_insert_fast(&head->ht, &newmask->ht_node, 89805cd271fSPaul Blakey mask_ht_params); 89905cd271fSPaul Blakey if (err) 90005cd271fSPaul Blakey goto errout_destroy; 90105cd271fSPaul Blakey 90205cd271fSPaul Blakey list_add_tail_rcu(&newmask->list, &head->masks); 90305cd271fSPaul Blakey 90405cd271fSPaul Blakey return newmask; 90505cd271fSPaul Blakey 90605cd271fSPaul Blakey errout_destroy: 90705cd271fSPaul Blakey rhashtable_destroy(&newmask->ht); 90805cd271fSPaul Blakey errout_free: 90905cd271fSPaul Blakey kfree(newmask); 91005cd271fSPaul Blakey 91105cd271fSPaul Blakey return ERR_PTR(err); 91277b9900eSJiri Pirko } 91377b9900eSJiri Pirko 91477b9900eSJiri Pirko static int fl_check_assign_mask(struct cls_fl_head *head, 91505cd271fSPaul Blakey struct cls_fl_filter *fnew, 91605cd271fSPaul Blakey struct cls_fl_filter *fold, 91777b9900eSJiri Pirko struct fl_flow_mask *mask) 91877b9900eSJiri Pirko { 91905cd271fSPaul Blakey struct fl_flow_mask *newmask; 92077b9900eSJiri Pirko 92105cd271fSPaul Blakey fnew->mask = rhashtable_lookup_fast(&head->ht, mask, mask_ht_params); 92205cd271fSPaul Blakey if (!fnew->mask) { 92305cd271fSPaul Blakey if (fold) 92477b9900eSJiri Pirko return -EINVAL; 92505cd271fSPaul Blakey 92605cd271fSPaul Blakey newmask = fl_create_new_mask(head, mask); 92705cd271fSPaul Blakey if (IS_ERR(newmask)) 92805cd271fSPaul Blakey return PTR_ERR(newmask); 92905cd271fSPaul Blakey 93005cd271fSPaul Blakey fnew->mask = newmask; 931f6521c58SPaul Blakey } else if (fold && fold->mask != fnew->mask) { 93205cd271fSPaul Blakey return -EINVAL; 93377b9900eSJiri Pirko } 93477b9900eSJiri Pirko 93577b9900eSJiri Pirko return 0; 93677b9900eSJiri Pirko } 93777b9900eSJiri Pirko 93877b9900eSJiri Pirko static int fl_set_parms(struct net *net, struct tcf_proto *tp, 93977b9900eSJiri Pirko struct cls_fl_filter *f, struct fl_flow_mask *mask, 94077b9900eSJiri Pirko unsigned long base, struct nlattr **tb, 94150a56190SAlexander Aring struct nlattr *est, bool ovr, 94250a56190SAlexander Aring struct netlink_ext_ack *extack) 94377b9900eSJiri Pirko { 94477b9900eSJiri Pirko int err; 94577b9900eSJiri Pirko 94650a56190SAlexander Aring err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr, extack); 94777b9900eSJiri Pirko if (err < 0) 94877b9900eSJiri Pirko return err; 94977b9900eSJiri Pirko 95077b9900eSJiri Pirko if (tb[TCA_FLOWER_CLASSID]) { 95177b9900eSJiri Pirko f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); 95277b9900eSJiri Pirko tcf_bind_filter(tp, &f->res, base); 95377b9900eSJiri Pirko } 95477b9900eSJiri Pirko 9551057c55fSAlexander Aring err = fl_set_key(net, tb, &f->key, &mask->key, extack); 95677b9900eSJiri Pirko if (err) 95745507529SJiri Pirko return err; 95877b9900eSJiri Pirko 95977b9900eSJiri Pirko fl_mask_update_range(mask); 96077b9900eSJiri Pirko fl_set_masked_key(&f->mkey, &f->key, mask); 96177b9900eSJiri Pirko 96277b9900eSJiri Pirko return 0; 96377b9900eSJiri Pirko } 96477b9900eSJiri Pirko 96577b9900eSJiri Pirko static int fl_change(struct net *net, struct sk_buff *in_skb, 96677b9900eSJiri Pirko struct tcf_proto *tp, unsigned long base, 96777b9900eSJiri Pirko u32 handle, struct nlattr **tca, 9687306db38SAlexander Aring void **arg, bool ovr, struct netlink_ext_ack *extack) 96977b9900eSJiri Pirko { 97077b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 9718113c095SWANG Cong struct cls_fl_filter *fold = *arg; 97277b9900eSJiri Pirko struct cls_fl_filter *fnew; 97339b7b6a6SArnd Bergmann struct nlattr **tb; 97477b9900eSJiri Pirko struct fl_flow_mask mask = {}; 97577b9900eSJiri Pirko int err; 97677b9900eSJiri Pirko 97777b9900eSJiri Pirko if (!tca[TCA_OPTIONS]) 97877b9900eSJiri Pirko return -EINVAL; 97977b9900eSJiri Pirko 98039b7b6a6SArnd Bergmann tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 98139b7b6a6SArnd Bergmann if (!tb) 98239b7b6a6SArnd Bergmann return -ENOBUFS; 98339b7b6a6SArnd Bergmann 984fceb6435SJohannes Berg err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS], 985fceb6435SJohannes Berg fl_policy, NULL); 98677b9900eSJiri Pirko if (err < 0) 98739b7b6a6SArnd Bergmann goto errout_tb; 98877b9900eSJiri Pirko 98939b7b6a6SArnd Bergmann if (fold && handle && fold->handle != handle) { 99039b7b6a6SArnd Bergmann err = -EINVAL; 99139b7b6a6SArnd Bergmann goto errout_tb; 99239b7b6a6SArnd Bergmann } 99377b9900eSJiri Pirko 99477b9900eSJiri Pirko fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 99539b7b6a6SArnd Bergmann if (!fnew) { 99639b7b6a6SArnd Bergmann err = -ENOBUFS; 99739b7b6a6SArnd Bergmann goto errout_tb; 99839b7b6a6SArnd Bergmann } 99977b9900eSJiri Pirko 1000b9a24bb7SWANG Cong err = tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0); 1001b9a24bb7SWANG Cong if (err < 0) 1002b9a24bb7SWANG Cong goto errout; 100377b9900eSJiri Pirko 100477b9900eSJiri Pirko if (!handle) { 100585bd0438SMatthew Wilcox handle = 1; 100685bd0438SMatthew Wilcox err = idr_alloc_u32(&head->handle_idr, fnew, &handle, 100785bd0438SMatthew Wilcox INT_MAX, GFP_KERNEL); 100885bd0438SMatthew Wilcox } else if (!fold) { 1009c15ab236SChris Mi /* user specifies a handle and it doesn't exist */ 101085bd0438SMatthew Wilcox err = idr_alloc_u32(&head->handle_idr, fnew, &handle, 101185bd0438SMatthew Wilcox handle, GFP_KERNEL); 101285bd0438SMatthew Wilcox } 1013c15ab236SChris Mi if (err) 1014c15ab236SChris Mi goto errout; 101585bd0438SMatthew Wilcox fnew->handle = handle; 101677b9900eSJiri Pirko 1017e69985c6SAmir Vadai if (tb[TCA_FLOWER_FLAGS]) { 1018e69985c6SAmir Vadai fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); 1019e69985c6SAmir Vadai 1020e69985c6SAmir Vadai if (!tc_flags_valid(fnew->flags)) { 1021e69985c6SAmir Vadai err = -EINVAL; 1022fe2502e4SCong Wang goto errout_idr; 1023e69985c6SAmir Vadai } 1024e69985c6SAmir Vadai } 10255b33f488SAmir Vadai 102650a56190SAlexander Aring err = fl_set_parms(net, tp, fnew, &mask, base, tb, tca[TCA_RATE], ovr, 102750a56190SAlexander Aring extack); 102877b9900eSJiri Pirko if (err) 1029fe2502e4SCong Wang goto errout_idr; 103077b9900eSJiri Pirko 103105cd271fSPaul Blakey err = fl_check_assign_mask(head, fnew, fold, &mask); 103277b9900eSJiri Pirko if (err) 1033fe2502e4SCong Wang goto errout_idr; 103477b9900eSJiri Pirko 1035e8eb36cdSAmir Vadai if (!tc_skip_sw(fnew->flags)) { 103605cd271fSPaul Blakey if (!fold && fl_lookup(fnew->mask, &fnew->mkey)) { 1037a3308d8fSPaul Blakey err = -EEXIST; 103805cd271fSPaul Blakey goto errout_mask; 1039a3308d8fSPaul Blakey } 1040a3308d8fSPaul Blakey 104105cd271fSPaul Blakey err = rhashtable_insert_fast(&fnew->mask->ht, &fnew->ht_node, 104205cd271fSPaul Blakey fnew->mask->filter_ht_params); 104377b9900eSJiri Pirko if (err) 104405cd271fSPaul Blakey goto errout_mask; 1045e69985c6SAmir Vadai } 10465b33f488SAmir Vadai 104779685219SHadar Hen Zion if (!tc_skip_hw(fnew->flags)) { 104805cd271fSPaul Blakey err = fl_hw_replace_filter(tp, fnew, extack); 1049e8eb36cdSAmir Vadai if (err) 105005cd271fSPaul Blakey goto errout_mask; 105179685219SHadar Hen Zion } 10525b33f488SAmir Vadai 105355593960SOr Gerlitz if (!tc_in_hw(fnew->flags)) 105455593960SOr Gerlitz fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW; 105555593960SOr Gerlitz 10565b33f488SAmir Vadai if (fold) { 1057725cbb62SJiri Pirko if (!tc_skip_sw(fold->flags)) 105805cd271fSPaul Blakey rhashtable_remove_fast(&fold->mask->ht, 105905cd271fSPaul Blakey &fold->ht_node, 106005cd271fSPaul Blakey fold->mask->filter_ht_params); 106179685219SHadar Hen Zion if (!tc_skip_hw(fold->flags)) 10621b0f8037SJakub Kicinski fl_hw_destroy_filter(tp, fold, NULL); 10635b33f488SAmir Vadai } 106477b9900eSJiri Pirko 10658113c095SWANG Cong *arg = fnew; 106677b9900eSJiri Pirko 106777b9900eSJiri Pirko if (fold) { 1068234a4624SMatthew Wilcox idr_replace(&head->handle_idr, fnew, fnew->handle); 1069ff3532f2SDaniel Borkmann list_replace_rcu(&fold->list, &fnew->list); 107077b9900eSJiri Pirko tcf_unbind_filter(tp, &fold->res); 10710dadc117SCong Wang tcf_exts_get_net(&fold->exts); 1072aaa908ffSCong Wang tcf_queue_work(&fold->rwork, fl_destroy_filter_work); 107377b9900eSJiri Pirko } else { 107405cd271fSPaul Blakey list_add_tail_rcu(&fnew->list, &fnew->mask->filters); 107577b9900eSJiri Pirko } 107677b9900eSJiri Pirko 107739b7b6a6SArnd Bergmann kfree(tb); 107877b9900eSJiri Pirko return 0; 107977b9900eSJiri Pirko 108005cd271fSPaul Blakey errout_mask: 108105cd271fSPaul Blakey fl_mask_put(head, fnew->mask, false); 108205cd271fSPaul Blakey 1083fe2502e4SCong Wang errout_idr: 10848258d2daSPaul Blakey if (!fold) 10859c160941SMatthew Wilcox idr_remove(&head->handle_idr, fnew->handle); 108677b9900eSJiri Pirko errout: 1087b9a24bb7SWANG Cong tcf_exts_destroy(&fnew->exts); 108877b9900eSJiri Pirko kfree(fnew); 108939b7b6a6SArnd Bergmann errout_tb: 109039b7b6a6SArnd Bergmann kfree(tb); 109177b9900eSJiri Pirko return err; 109277b9900eSJiri Pirko } 109377b9900eSJiri Pirko 1094571acf21SAlexander Aring static int fl_delete(struct tcf_proto *tp, void *arg, bool *last, 1095571acf21SAlexander Aring struct netlink_ext_ack *extack) 109677b9900eSJiri Pirko { 109777b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 10988113c095SWANG Cong struct cls_fl_filter *f = arg; 109977b9900eSJiri Pirko 1100725cbb62SJiri Pirko if (!tc_skip_sw(f->flags)) 110105cd271fSPaul Blakey rhashtable_remove_fast(&f->mask->ht, &f->ht_node, 110205cd271fSPaul Blakey f->mask->filter_ht_params); 11031b0f8037SJakub Kicinski __fl_delete(tp, f, extack); 110405cd271fSPaul Blakey *last = list_empty(&head->masks); 110577b9900eSJiri Pirko return 0; 110677b9900eSJiri Pirko } 110777b9900eSJiri Pirko 110877b9900eSJiri Pirko static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg) 110977b9900eSJiri Pirko { 111077b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 111177b9900eSJiri Pirko struct cls_fl_filter *f; 111277b9900eSJiri Pirko 111301683a14SVlad Buslov arg->count = arg->skip; 111401683a14SVlad Buslov 111501683a14SVlad Buslov while ((f = idr_get_next_ul(&head->handle_idr, 111601683a14SVlad Buslov &arg->cookie)) != NULL) { 11178113c095SWANG Cong if (arg->fn(tp, f, arg) < 0) { 111877b9900eSJiri Pirko arg->stop = 1; 111977b9900eSJiri Pirko break; 112077b9900eSJiri Pirko } 112101683a14SVlad Buslov arg->cookie = f->handle + 1; 112277b9900eSJiri Pirko arg->count++; 112377b9900eSJiri Pirko } 112477b9900eSJiri Pirko } 112577b9900eSJiri Pirko 112631533cbaSJohn Hurley static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb, 112731533cbaSJohn Hurley void *cb_priv, struct netlink_ext_ack *extack) 112831533cbaSJohn Hurley { 112931533cbaSJohn Hurley struct cls_fl_head *head = rtnl_dereference(tp->root); 113031533cbaSJohn Hurley struct tc_cls_flower_offload cls_flower = {}; 113131533cbaSJohn Hurley struct tcf_block *block = tp->chain->block; 113231533cbaSJohn Hurley struct fl_flow_mask *mask; 113331533cbaSJohn Hurley struct cls_fl_filter *f; 113431533cbaSJohn Hurley int err; 113531533cbaSJohn Hurley 113631533cbaSJohn Hurley list_for_each_entry(mask, &head->masks, list) { 113731533cbaSJohn Hurley list_for_each_entry(f, &mask->filters, list) { 113831533cbaSJohn Hurley if (tc_skip_hw(f->flags)) 113931533cbaSJohn Hurley continue; 114031533cbaSJohn Hurley 114131533cbaSJohn Hurley tc_cls_common_offload_init(&cls_flower.common, tp, 114231533cbaSJohn Hurley f->flags, extack); 114331533cbaSJohn Hurley cls_flower.command = add ? 114431533cbaSJohn Hurley TC_CLSFLOWER_REPLACE : TC_CLSFLOWER_DESTROY; 114531533cbaSJohn Hurley cls_flower.cookie = (unsigned long)f; 114631533cbaSJohn Hurley cls_flower.dissector = &mask->dissector; 114731533cbaSJohn Hurley cls_flower.mask = &f->mkey; 114831533cbaSJohn Hurley cls_flower.key = &f->key; 114931533cbaSJohn Hurley cls_flower.exts = &f->exts; 115031533cbaSJohn Hurley cls_flower.classid = f->res.classid; 115131533cbaSJohn Hurley 115231533cbaSJohn Hurley err = cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv); 115331533cbaSJohn Hurley if (err) { 115431533cbaSJohn Hurley if (add && tc_skip_sw(f->flags)) 115531533cbaSJohn Hurley return err; 115631533cbaSJohn Hurley continue; 115731533cbaSJohn Hurley } 115831533cbaSJohn Hurley 115931533cbaSJohn Hurley tc_cls_offload_cnt_update(block, &f->in_hw_count, 116031533cbaSJohn Hurley &f->flags, add); 116131533cbaSJohn Hurley } 116231533cbaSJohn Hurley } 116331533cbaSJohn Hurley 116431533cbaSJohn Hurley return 0; 116531533cbaSJohn Hurley } 116631533cbaSJohn Hurley 116777b9900eSJiri Pirko static int fl_dump_key_val(struct sk_buff *skb, 116877b9900eSJiri Pirko void *val, int val_type, 116977b9900eSJiri Pirko void *mask, int mask_type, int len) 117077b9900eSJiri Pirko { 117177b9900eSJiri Pirko int err; 117277b9900eSJiri Pirko 117377b9900eSJiri Pirko if (!memchr_inv(mask, 0, len)) 117477b9900eSJiri Pirko return 0; 117577b9900eSJiri Pirko err = nla_put(skb, val_type, len, val); 117677b9900eSJiri Pirko if (err) 117777b9900eSJiri Pirko return err; 117877b9900eSJiri Pirko if (mask_type != TCA_FLOWER_UNSPEC) { 117977b9900eSJiri Pirko err = nla_put(skb, mask_type, len, mask); 118077b9900eSJiri Pirko if (err) 118177b9900eSJiri Pirko return err; 118277b9900eSJiri Pirko } 118377b9900eSJiri Pirko return 0; 118477b9900eSJiri Pirko } 118577b9900eSJiri Pirko 1186a577d8f7SBenjamin LaHaise static int fl_dump_key_mpls(struct sk_buff *skb, 1187a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *mpls_key, 1188a577d8f7SBenjamin LaHaise struct flow_dissector_key_mpls *mpls_mask) 1189a577d8f7SBenjamin LaHaise { 1190a577d8f7SBenjamin LaHaise int err; 1191a577d8f7SBenjamin LaHaise 1192a577d8f7SBenjamin LaHaise if (!memchr_inv(mpls_mask, 0, sizeof(*mpls_mask))) 1193a577d8f7SBenjamin LaHaise return 0; 1194a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_ttl) { 1195a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TTL, 1196a577d8f7SBenjamin LaHaise mpls_key->mpls_ttl); 1197a577d8f7SBenjamin LaHaise if (err) 1198a577d8f7SBenjamin LaHaise return err; 1199a577d8f7SBenjamin LaHaise } 1200a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_tc) { 1201a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TC, 1202a577d8f7SBenjamin LaHaise mpls_key->mpls_tc); 1203a577d8f7SBenjamin LaHaise if (err) 1204a577d8f7SBenjamin LaHaise return err; 1205a577d8f7SBenjamin LaHaise } 1206a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_label) { 1207a577d8f7SBenjamin LaHaise err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_LABEL, 1208a577d8f7SBenjamin LaHaise mpls_key->mpls_label); 1209a577d8f7SBenjamin LaHaise if (err) 1210a577d8f7SBenjamin LaHaise return err; 1211a577d8f7SBenjamin LaHaise } 1212a577d8f7SBenjamin LaHaise if (mpls_mask->mpls_bos) { 1213a577d8f7SBenjamin LaHaise err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_BOS, 1214a577d8f7SBenjamin LaHaise mpls_key->mpls_bos); 1215a577d8f7SBenjamin LaHaise if (err) 1216a577d8f7SBenjamin LaHaise return err; 1217a577d8f7SBenjamin LaHaise } 1218a577d8f7SBenjamin LaHaise return 0; 1219a577d8f7SBenjamin LaHaise } 1220a577d8f7SBenjamin LaHaise 12210e2c17b6SOr Gerlitz static int fl_dump_key_ip(struct sk_buff *skb, bool encap, 12224d80cc0aSOr Gerlitz struct flow_dissector_key_ip *key, 12234d80cc0aSOr Gerlitz struct flow_dissector_key_ip *mask) 12244d80cc0aSOr Gerlitz { 12250e2c17b6SOr Gerlitz int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 12260e2c17b6SOr Gerlitz int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 12270e2c17b6SOr Gerlitz int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 12280e2c17b6SOr Gerlitz int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 12290e2c17b6SOr Gerlitz 12300e2c17b6SOr Gerlitz if (fl_dump_key_val(skb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)) || 12310e2c17b6SOr Gerlitz fl_dump_key_val(skb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl))) 12324d80cc0aSOr Gerlitz return -1; 12334d80cc0aSOr Gerlitz 12344d80cc0aSOr Gerlitz return 0; 12354d80cc0aSOr Gerlitz } 12364d80cc0aSOr Gerlitz 12379399ae9aSHadar Hen Zion static int fl_dump_key_vlan(struct sk_buff *skb, 1238d64efd09SJianbo Liu int vlan_id_key, int vlan_prio_key, 12399399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_key, 12409399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_mask) 12419399ae9aSHadar Hen Zion { 12429399ae9aSHadar Hen Zion int err; 12439399ae9aSHadar Hen Zion 12449399ae9aSHadar Hen Zion if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask))) 12459399ae9aSHadar Hen Zion return 0; 12469399ae9aSHadar Hen Zion if (vlan_mask->vlan_id) { 1247d64efd09SJianbo Liu err = nla_put_u16(skb, vlan_id_key, 12489399ae9aSHadar Hen Zion vlan_key->vlan_id); 12499399ae9aSHadar Hen Zion if (err) 12509399ae9aSHadar Hen Zion return err; 12519399ae9aSHadar Hen Zion } 12529399ae9aSHadar Hen Zion if (vlan_mask->vlan_priority) { 1253d64efd09SJianbo Liu err = nla_put_u8(skb, vlan_prio_key, 12549399ae9aSHadar Hen Zion vlan_key->vlan_priority); 12559399ae9aSHadar Hen Zion if (err) 12569399ae9aSHadar Hen Zion return err; 12579399ae9aSHadar Hen Zion } 12589399ae9aSHadar Hen Zion return 0; 12599399ae9aSHadar Hen Zion } 12609399ae9aSHadar Hen Zion 1261faa3ffceSOr Gerlitz static void fl_get_key_flag(u32 dissector_key, u32 dissector_mask, 1262faa3ffceSOr Gerlitz u32 *flower_key, u32 *flower_mask, 1263faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 1264faa3ffceSOr Gerlitz { 1265faa3ffceSOr Gerlitz if (dissector_mask & dissector_flag_bit) { 1266faa3ffceSOr Gerlitz *flower_mask |= flower_flag_bit; 1267faa3ffceSOr Gerlitz if (dissector_key & dissector_flag_bit) 1268faa3ffceSOr Gerlitz *flower_key |= flower_flag_bit; 1269faa3ffceSOr Gerlitz } 1270faa3ffceSOr Gerlitz } 1271faa3ffceSOr Gerlitz 1272faa3ffceSOr Gerlitz static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) 1273faa3ffceSOr Gerlitz { 1274faa3ffceSOr Gerlitz u32 key, mask; 1275faa3ffceSOr Gerlitz __be32 _key, _mask; 1276faa3ffceSOr Gerlitz int err; 1277faa3ffceSOr Gerlitz 1278faa3ffceSOr Gerlitz if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask))) 1279faa3ffceSOr Gerlitz return 0; 1280faa3ffceSOr Gerlitz 1281faa3ffceSOr Gerlitz key = 0; 1282faa3ffceSOr Gerlitz mask = 0; 1283faa3ffceSOr Gerlitz 1284faa3ffceSOr Gerlitz fl_get_key_flag(flags_key, flags_mask, &key, &mask, 1285faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 1286459d153dSPieter Jansen van Vuuren fl_get_key_flag(flags_key, flags_mask, &key, &mask, 1287459d153dSPieter Jansen van Vuuren TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 1288459d153dSPieter Jansen van Vuuren FLOW_DIS_FIRST_FRAG); 1289faa3ffceSOr Gerlitz 1290faa3ffceSOr Gerlitz _key = cpu_to_be32(key); 1291faa3ffceSOr Gerlitz _mask = cpu_to_be32(mask); 1292faa3ffceSOr Gerlitz 1293faa3ffceSOr Gerlitz err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key); 1294faa3ffceSOr Gerlitz if (err) 1295faa3ffceSOr Gerlitz return err; 1296faa3ffceSOr Gerlitz 1297faa3ffceSOr Gerlitz return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); 1298faa3ffceSOr Gerlitz } 1299faa3ffceSOr Gerlitz 1300f5749081SJiri Pirko static int fl_dump_key(struct sk_buff *skb, struct net *net, 1301f5749081SJiri Pirko struct fl_flow_key *key, struct fl_flow_key *mask) 130277b9900eSJiri Pirko { 130377b9900eSJiri Pirko if (mask->indev_ifindex) { 130477b9900eSJiri Pirko struct net_device *dev; 130577b9900eSJiri Pirko 130677b9900eSJiri Pirko dev = __dev_get_by_index(net, key->indev_ifindex); 130777b9900eSJiri Pirko if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name)) 130877b9900eSJiri Pirko goto nla_put_failure; 130977b9900eSJiri Pirko } 131077b9900eSJiri Pirko 131177b9900eSJiri Pirko if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 131277b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 131377b9900eSJiri Pirko sizeof(key->eth.dst)) || 131477b9900eSJiri Pirko fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 131577b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 131677b9900eSJiri Pirko sizeof(key->eth.src)) || 131777b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE, 131877b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 131977b9900eSJiri Pirko sizeof(key->basic.n_proto))) 132077b9900eSJiri Pirko goto nla_put_failure; 13219399ae9aSHadar Hen Zion 1322a577d8f7SBenjamin LaHaise if (fl_dump_key_mpls(skb, &key->mpls, &mask->mpls)) 1323a577d8f7SBenjamin LaHaise goto nla_put_failure; 1324a577d8f7SBenjamin LaHaise 1325d64efd09SJianbo Liu if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_VLAN_ID, 1326d64efd09SJianbo Liu TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, &mask->vlan)) 13279399ae9aSHadar Hen Zion goto nla_put_failure; 13289399ae9aSHadar Hen Zion 1329d64efd09SJianbo Liu if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_CVLAN_ID, 1330d64efd09SJianbo Liu TCA_FLOWER_KEY_CVLAN_PRIO, 1331d64efd09SJianbo Liu &key->cvlan, &mask->cvlan) || 1332d64efd09SJianbo Liu (mask->cvlan.vlan_tpid && 1333d64efd09SJianbo Liu nla_put_u16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 1334d64efd09SJianbo Liu key->cvlan.vlan_tpid))) 1335d3069512SJianbo Liu goto nla_put_failure; 1336d3069512SJianbo Liu 13375e9a0fe4SJianbo Liu if (mask->basic.n_proto) { 1338d64efd09SJianbo Liu if (mask->cvlan.vlan_tpid) { 1339d64efd09SJianbo Liu if (nla_put_be16(skb, TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 1340d64efd09SJianbo Liu key->basic.n_proto)) 1341d64efd09SJianbo Liu goto nla_put_failure; 1342d64efd09SJianbo Liu } else if (mask->vlan.vlan_tpid) { 1343d64efd09SJianbo Liu if (nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 1344d64efd09SJianbo Liu key->basic.n_proto)) 1345d64efd09SJianbo Liu goto nla_put_failure; 1346d64efd09SJianbo Liu } 13475e9a0fe4SJianbo Liu } 1348d64efd09SJianbo Liu 134977b9900eSJiri Pirko if ((key->basic.n_proto == htons(ETH_P_IP) || 135077b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) && 13514d80cc0aSOr Gerlitz (fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 135277b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 13534d80cc0aSOr Gerlitz sizeof(key->basic.ip_proto)) || 13540e2c17b6SOr Gerlitz fl_dump_key_ip(skb, false, &key->ip, &mask->ip))) 135577b9900eSJiri Pirko goto nla_put_failure; 135677b9900eSJiri Pirko 1357c3f83241STom Herbert if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 135877b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 135977b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 136077b9900eSJiri Pirko sizeof(key->ipv4.src)) || 136177b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 136277b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 136377b9900eSJiri Pirko sizeof(key->ipv4.dst)))) 136477b9900eSJiri Pirko goto nla_put_failure; 1365c3f83241STom Herbert else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 136677b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 136777b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 136877b9900eSJiri Pirko sizeof(key->ipv6.src)) || 136977b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 137077b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 137177b9900eSJiri Pirko sizeof(key->ipv6.dst)))) 137277b9900eSJiri Pirko goto nla_put_failure; 137377b9900eSJiri Pirko 137477b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP && 137577b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 1376aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 137777b9900eSJiri Pirko sizeof(key->tp.src)) || 137877b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 1379aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 1380fdfc7dd6SJiri Pirko sizeof(key->tp.dst)) || 1381fdfc7dd6SJiri Pirko fl_dump_key_val(skb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 1382fdfc7dd6SJiri Pirko &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 1383fdfc7dd6SJiri Pirko sizeof(key->tcp.flags)))) 138477b9900eSJiri Pirko goto nla_put_failure; 138577b9900eSJiri Pirko else if (key->basic.ip_proto == IPPROTO_UDP && 138677b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 1387aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 138877b9900eSJiri Pirko sizeof(key->tp.src)) || 138977b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 1390aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 139177b9900eSJiri Pirko sizeof(key->tp.dst)))) 139277b9900eSJiri Pirko goto nla_put_failure; 13935976c5f4SSimon Horman else if (key->basic.ip_proto == IPPROTO_SCTP && 13945976c5f4SSimon Horman (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 13955976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 13965976c5f4SSimon Horman sizeof(key->tp.src)) || 13975976c5f4SSimon Horman fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 13985976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 13995976c5f4SSimon Horman sizeof(key->tp.dst)))) 14005976c5f4SSimon Horman goto nla_put_failure; 14017b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IP) && 14027b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP && 14037b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 14047b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type, 14057b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 14067b684884SSimon Horman sizeof(key->icmp.type)) || 14077b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 14087b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code, 14097b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 14107b684884SSimon Horman sizeof(key->icmp.code)))) 14117b684884SSimon Horman goto nla_put_failure; 14127b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IPV6) && 14137b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6 && 14147b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 14157b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type, 14167b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 14177b684884SSimon Horman sizeof(key->icmp.type)) || 14187b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 14197b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code, 14207b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 14217b684884SSimon Horman sizeof(key->icmp.code)))) 14227b684884SSimon Horman goto nla_put_failure; 142399d31326SSimon Horman else if ((key->basic.n_proto == htons(ETH_P_ARP) || 142499d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) && 142599d31326SSimon Horman (fl_dump_key_val(skb, &key->arp.sip, 142699d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP, &mask->arp.sip, 142799d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP_MASK, 142899d31326SSimon Horman sizeof(key->arp.sip)) || 142999d31326SSimon Horman fl_dump_key_val(skb, &key->arp.tip, 143099d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP, &mask->arp.tip, 143199d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP_MASK, 143299d31326SSimon Horman sizeof(key->arp.tip)) || 143399d31326SSimon Horman fl_dump_key_val(skb, &key->arp.op, 143499d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP, &mask->arp.op, 143599d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP_MASK, 143699d31326SSimon Horman sizeof(key->arp.op)) || 143799d31326SSimon Horman fl_dump_key_val(skb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 143899d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 143999d31326SSimon Horman sizeof(key->arp.sha)) || 144099d31326SSimon Horman fl_dump_key_val(skb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 144199d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 144299d31326SSimon Horman sizeof(key->arp.tha)))) 144399d31326SSimon Horman goto nla_put_failure; 144477b9900eSJiri Pirko 1445bc3103f1SAmir Vadai if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 1446bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv4.src, 1447bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src, 1448bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 1449bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)) || 1450bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv4.dst, 1451bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst, 1452bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 1453bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)))) 1454bc3103f1SAmir Vadai goto nla_put_failure; 1455bc3103f1SAmir Vadai else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 1456bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv6.src, 1457bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src, 1458bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 1459bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)) || 1460bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv6.dst, 1461bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 1462bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 1463bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 1464bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)))) 1465bc3103f1SAmir Vadai goto nla_put_failure; 1466bc3103f1SAmir Vadai 1467bc3103f1SAmir Vadai if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, 1468eb523f42SHadar Hen Zion &mask->enc_key_id, TCA_FLOWER_UNSPEC, 1469f4d997fdSHadar Hen Zion sizeof(key->enc_key_id)) || 1470f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.src, 1471f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 1472f4d997fdSHadar Hen Zion &mask->enc_tp.src, 1473f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 1474f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)) || 1475f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.dst, 1476f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 1477f4d997fdSHadar Hen Zion &mask->enc_tp.dst, 1478f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 14790e2c17b6SOr Gerlitz sizeof(key->enc_tp.dst)) || 14800e2c17b6SOr Gerlitz fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip)) 1481bc3103f1SAmir Vadai goto nla_put_failure; 1482bc3103f1SAmir Vadai 1483faa3ffceSOr Gerlitz if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) 1484faa3ffceSOr Gerlitz goto nla_put_failure; 1485faa3ffceSOr Gerlitz 1486f5749081SJiri Pirko return 0; 1487f5749081SJiri Pirko 1488f5749081SJiri Pirko nla_put_failure: 1489f5749081SJiri Pirko return -EMSGSIZE; 1490f5749081SJiri Pirko } 1491f5749081SJiri Pirko 1492f5749081SJiri Pirko static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh, 1493f5749081SJiri Pirko struct sk_buff *skb, struct tcmsg *t) 1494f5749081SJiri Pirko { 1495f5749081SJiri Pirko struct cls_fl_filter *f = fh; 1496f5749081SJiri Pirko struct nlattr *nest; 1497f5749081SJiri Pirko struct fl_flow_key *key, *mask; 1498f5749081SJiri Pirko 1499f5749081SJiri Pirko if (!f) 1500f5749081SJiri Pirko return skb->len; 1501f5749081SJiri Pirko 1502f5749081SJiri Pirko t->tcm_handle = f->handle; 1503f5749081SJiri Pirko 1504f5749081SJiri Pirko nest = nla_nest_start(skb, TCA_OPTIONS); 1505f5749081SJiri Pirko if (!nest) 1506f5749081SJiri Pirko goto nla_put_failure; 1507f5749081SJiri Pirko 1508f5749081SJiri Pirko if (f->res.classid && 1509f5749081SJiri Pirko nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid)) 1510f5749081SJiri Pirko goto nla_put_failure; 1511f5749081SJiri Pirko 1512f5749081SJiri Pirko key = &f->key; 1513f5749081SJiri Pirko mask = &f->mask->key; 1514f5749081SJiri Pirko 1515f5749081SJiri Pirko if (fl_dump_key(skb, net, key, mask)) 1516f5749081SJiri Pirko goto nla_put_failure; 1517f5749081SJiri Pirko 1518f5749081SJiri Pirko if (!tc_skip_hw(f->flags)) 1519f5749081SJiri Pirko fl_hw_update_stats(tp, f); 1520f5749081SJiri Pirko 1521749e6720SOr Gerlitz if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags)) 1522749e6720SOr Gerlitz goto nla_put_failure; 1523e69985c6SAmir Vadai 152477b9900eSJiri Pirko if (tcf_exts_dump(skb, &f->exts)) 152577b9900eSJiri Pirko goto nla_put_failure; 152677b9900eSJiri Pirko 152777b9900eSJiri Pirko nla_nest_end(skb, nest); 152877b9900eSJiri Pirko 152977b9900eSJiri Pirko if (tcf_exts_dump_stats(skb, &f->exts) < 0) 153077b9900eSJiri Pirko goto nla_put_failure; 153177b9900eSJiri Pirko 153277b9900eSJiri Pirko return skb->len; 153377b9900eSJiri Pirko 153477b9900eSJiri Pirko nla_put_failure: 153577b9900eSJiri Pirko nla_nest_cancel(skb, nest); 153677b9900eSJiri Pirko return -1; 153777b9900eSJiri Pirko } 153877b9900eSJiri Pirko 153907d79fc7SCong Wang static void fl_bind_class(void *fh, u32 classid, unsigned long cl) 154007d79fc7SCong Wang { 154107d79fc7SCong Wang struct cls_fl_filter *f = fh; 154207d79fc7SCong Wang 154307d79fc7SCong Wang if (f && f->res.classid == classid) 154407d79fc7SCong Wang f->res.class = cl; 154507d79fc7SCong Wang } 154607d79fc7SCong Wang 154777b9900eSJiri Pirko static struct tcf_proto_ops cls_fl_ops __read_mostly = { 154877b9900eSJiri Pirko .kind = "flower", 154977b9900eSJiri Pirko .classify = fl_classify, 155077b9900eSJiri Pirko .init = fl_init, 155177b9900eSJiri Pirko .destroy = fl_destroy, 155277b9900eSJiri Pirko .get = fl_get, 155377b9900eSJiri Pirko .change = fl_change, 155477b9900eSJiri Pirko .delete = fl_delete, 155577b9900eSJiri Pirko .walk = fl_walk, 155631533cbaSJohn Hurley .reoffload = fl_reoffload, 155777b9900eSJiri Pirko .dump = fl_dump, 155807d79fc7SCong Wang .bind_class = fl_bind_class, 155977b9900eSJiri Pirko .owner = THIS_MODULE, 156077b9900eSJiri Pirko }; 156177b9900eSJiri Pirko 156277b9900eSJiri Pirko static int __init cls_fl_init(void) 156377b9900eSJiri Pirko { 156477b9900eSJiri Pirko return register_tcf_proto_ops(&cls_fl_ops); 156577b9900eSJiri Pirko } 156677b9900eSJiri Pirko 156777b9900eSJiri Pirko static void __exit cls_fl_exit(void) 156877b9900eSJiri Pirko { 156977b9900eSJiri Pirko unregister_tcf_proto_ops(&cls_fl_ops); 157077b9900eSJiri Pirko } 157177b9900eSJiri Pirko 157277b9900eSJiri Pirko module_init(cls_fl_init); 157377b9900eSJiri Pirko module_exit(cls_fl_exit); 157477b9900eSJiri Pirko 157577b9900eSJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 157677b9900eSJiri Pirko MODULE_DESCRIPTION("Flower classifier"); 157777b9900eSJiri Pirko MODULE_LICENSE("GPL v2"); 1578