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> 2177b9900eSJiri Pirko 2277b9900eSJiri Pirko #include <net/sch_generic.h> 2377b9900eSJiri Pirko #include <net/pkt_cls.h> 2477b9900eSJiri Pirko #include <net/ip.h> 2577b9900eSJiri Pirko #include <net/flow_dissector.h> 2677b9900eSJiri Pirko 27bc3103f1SAmir Vadai #include <net/dst.h> 28bc3103f1SAmir Vadai #include <net/dst_metadata.h> 29bc3103f1SAmir Vadai 3077b9900eSJiri Pirko struct fl_flow_key { 3177b9900eSJiri Pirko int indev_ifindex; 3242aecaa9STom Herbert struct flow_dissector_key_control control; 33bc3103f1SAmir Vadai struct flow_dissector_key_control enc_control; 3477b9900eSJiri Pirko struct flow_dissector_key_basic basic; 3577b9900eSJiri Pirko struct flow_dissector_key_eth_addrs eth; 369399ae9aSHadar Hen Zion struct flow_dissector_key_vlan vlan; 3777b9900eSJiri Pirko union { 38c3f83241STom Herbert struct flow_dissector_key_ipv4_addrs ipv4; 3977b9900eSJiri Pirko struct flow_dissector_key_ipv6_addrs ipv6; 4077b9900eSJiri Pirko }; 4177b9900eSJiri Pirko struct flow_dissector_key_ports tp; 427b684884SSimon Horman struct flow_dissector_key_icmp icmp; 4399d31326SSimon Horman struct flow_dissector_key_arp arp; 44bc3103f1SAmir Vadai struct flow_dissector_key_keyid enc_key_id; 45bc3103f1SAmir Vadai union { 46bc3103f1SAmir Vadai struct flow_dissector_key_ipv4_addrs enc_ipv4; 47bc3103f1SAmir Vadai struct flow_dissector_key_ipv6_addrs enc_ipv6; 48bc3103f1SAmir Vadai }; 49f4d997fdSHadar Hen Zion struct flow_dissector_key_ports enc_tp; 5077b9900eSJiri Pirko } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ 5177b9900eSJiri Pirko 5277b9900eSJiri Pirko struct fl_flow_mask_range { 5377b9900eSJiri Pirko unsigned short int start; 5477b9900eSJiri Pirko unsigned short int end; 5577b9900eSJiri Pirko }; 5677b9900eSJiri Pirko 5777b9900eSJiri Pirko struct fl_flow_mask { 5877b9900eSJiri Pirko struct fl_flow_key key; 5977b9900eSJiri Pirko struct fl_flow_mask_range range; 6077b9900eSJiri Pirko struct rcu_head rcu; 6177b9900eSJiri Pirko }; 6277b9900eSJiri Pirko 6377b9900eSJiri Pirko struct cls_fl_head { 6477b9900eSJiri Pirko struct rhashtable ht; 6577b9900eSJiri Pirko struct fl_flow_mask mask; 6677b9900eSJiri Pirko struct flow_dissector dissector; 6777b9900eSJiri Pirko u32 hgen; 6877b9900eSJiri Pirko bool mask_assigned; 6977b9900eSJiri Pirko struct list_head filters; 7077b9900eSJiri Pirko struct rhashtable_params ht_params; 71d9363774SDaniel Borkmann union { 72d9363774SDaniel Borkmann struct work_struct work; 7377b9900eSJiri Pirko struct rcu_head rcu; 7477b9900eSJiri Pirko }; 75d9363774SDaniel Borkmann }; 7677b9900eSJiri Pirko 7777b9900eSJiri Pirko struct cls_fl_filter { 7877b9900eSJiri Pirko struct rhash_head ht_node; 7977b9900eSJiri Pirko struct fl_flow_key mkey; 8077b9900eSJiri Pirko struct tcf_exts exts; 8177b9900eSJiri Pirko struct tcf_result res; 8277b9900eSJiri Pirko struct fl_flow_key key; 8377b9900eSJiri Pirko struct list_head list; 8477b9900eSJiri Pirko u32 handle; 85e69985c6SAmir Vadai u32 flags; 8677b9900eSJiri Pirko struct rcu_head rcu; 877091d8c7SHadar Hen Zion struct tc_to_netdev tc; 887091d8c7SHadar Hen Zion struct net_device *hw_dev; 8977b9900eSJiri Pirko }; 9077b9900eSJiri Pirko 9177b9900eSJiri Pirko static unsigned short int fl_mask_range(const struct fl_flow_mask *mask) 9277b9900eSJiri Pirko { 9377b9900eSJiri Pirko return mask->range.end - mask->range.start; 9477b9900eSJiri Pirko } 9577b9900eSJiri Pirko 9677b9900eSJiri Pirko static void fl_mask_update_range(struct fl_flow_mask *mask) 9777b9900eSJiri Pirko { 9877b9900eSJiri Pirko const u8 *bytes = (const u8 *) &mask->key; 9977b9900eSJiri Pirko size_t size = sizeof(mask->key); 10077b9900eSJiri Pirko size_t i, first = 0, last = size - 1; 10177b9900eSJiri Pirko 10277b9900eSJiri Pirko for (i = 0; i < sizeof(mask->key); i++) { 10377b9900eSJiri Pirko if (bytes[i]) { 10477b9900eSJiri Pirko if (!first && i) 10577b9900eSJiri Pirko first = i; 10677b9900eSJiri Pirko last = i; 10777b9900eSJiri Pirko } 10877b9900eSJiri Pirko } 10977b9900eSJiri Pirko mask->range.start = rounddown(first, sizeof(long)); 11077b9900eSJiri Pirko mask->range.end = roundup(last + 1, sizeof(long)); 11177b9900eSJiri Pirko } 11277b9900eSJiri Pirko 11377b9900eSJiri Pirko static void *fl_key_get_start(struct fl_flow_key *key, 11477b9900eSJiri Pirko const struct fl_flow_mask *mask) 11577b9900eSJiri Pirko { 11677b9900eSJiri Pirko return (u8 *) key + mask->range.start; 11777b9900eSJiri Pirko } 11877b9900eSJiri Pirko 11977b9900eSJiri Pirko static void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key, 12077b9900eSJiri Pirko struct fl_flow_mask *mask) 12177b9900eSJiri Pirko { 12277b9900eSJiri Pirko const long *lkey = fl_key_get_start(key, mask); 12377b9900eSJiri Pirko const long *lmask = fl_key_get_start(&mask->key, mask); 12477b9900eSJiri Pirko long *lmkey = fl_key_get_start(mkey, mask); 12577b9900eSJiri Pirko int i; 12677b9900eSJiri Pirko 12777b9900eSJiri Pirko for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) 12877b9900eSJiri Pirko *lmkey++ = *lkey++ & *lmask++; 12977b9900eSJiri Pirko } 13077b9900eSJiri Pirko 13177b9900eSJiri Pirko static void fl_clear_masked_range(struct fl_flow_key *key, 13277b9900eSJiri Pirko struct fl_flow_mask *mask) 13377b9900eSJiri Pirko { 13477b9900eSJiri Pirko memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask)); 13577b9900eSJiri Pirko } 13677b9900eSJiri Pirko 137a3308d8fSPaul Blakey static struct cls_fl_filter *fl_lookup(struct cls_fl_head *head, 138a3308d8fSPaul Blakey struct fl_flow_key *mkey) 139a3308d8fSPaul Blakey { 140a3308d8fSPaul Blakey return rhashtable_lookup_fast(&head->ht, 141a3308d8fSPaul Blakey fl_key_get_start(mkey, &head->mask), 142a3308d8fSPaul Blakey head->ht_params); 143a3308d8fSPaul Blakey } 144a3308d8fSPaul Blakey 14577b9900eSJiri Pirko static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, 14677b9900eSJiri Pirko struct tcf_result *res) 14777b9900eSJiri Pirko { 14877b9900eSJiri Pirko struct cls_fl_head *head = rcu_dereference_bh(tp->root); 14977b9900eSJiri Pirko struct cls_fl_filter *f; 15077b9900eSJiri Pirko struct fl_flow_key skb_key; 15177b9900eSJiri Pirko struct fl_flow_key skb_mkey; 152bc3103f1SAmir Vadai struct ip_tunnel_info *info; 15377b9900eSJiri Pirko 154e69985c6SAmir Vadai if (!atomic_read(&head->ht.nelems)) 155e69985c6SAmir Vadai return -1; 156e69985c6SAmir Vadai 15777b9900eSJiri Pirko fl_clear_masked_range(&skb_key, &head->mask); 158bc3103f1SAmir Vadai 159bc3103f1SAmir Vadai info = skb_tunnel_info(skb); 160bc3103f1SAmir Vadai if (info) { 161bc3103f1SAmir Vadai struct ip_tunnel_key *key = &info->key; 162bc3103f1SAmir Vadai 163bc3103f1SAmir Vadai switch (ip_tunnel_info_af(info)) { 164bc3103f1SAmir Vadai case AF_INET: 1650df0f207SPaul Blakey skb_key.enc_control.addr_type = 1660df0f207SPaul Blakey FLOW_DISSECTOR_KEY_IPV4_ADDRS; 167bc3103f1SAmir Vadai skb_key.enc_ipv4.src = key->u.ipv4.src; 168bc3103f1SAmir Vadai skb_key.enc_ipv4.dst = key->u.ipv4.dst; 169bc3103f1SAmir Vadai break; 170bc3103f1SAmir Vadai case AF_INET6: 1710df0f207SPaul Blakey skb_key.enc_control.addr_type = 1720df0f207SPaul Blakey FLOW_DISSECTOR_KEY_IPV6_ADDRS; 173bc3103f1SAmir Vadai skb_key.enc_ipv6.src = key->u.ipv6.src; 174bc3103f1SAmir Vadai skb_key.enc_ipv6.dst = key->u.ipv6.dst; 175bc3103f1SAmir Vadai break; 176bc3103f1SAmir Vadai } 177bc3103f1SAmir Vadai 178bc3103f1SAmir Vadai skb_key.enc_key_id.keyid = tunnel_id_to_key32(key->tun_id); 179f4d997fdSHadar Hen Zion skb_key.enc_tp.src = key->tp_src; 180f4d997fdSHadar Hen Zion skb_key.enc_tp.dst = key->tp_dst; 181bc3103f1SAmir Vadai } 182bc3103f1SAmir Vadai 18377b9900eSJiri Pirko skb_key.indev_ifindex = skb->skb_iif; 18477b9900eSJiri Pirko /* skb_flow_dissect() does not set n_proto in case an unknown protocol, 18577b9900eSJiri Pirko * so do it rather here. 18677b9900eSJiri Pirko */ 18777b9900eSJiri Pirko skb_key.basic.n_proto = skb->protocol; 188cd79a238STom Herbert skb_flow_dissect(skb, &head->dissector, &skb_key, 0); 18977b9900eSJiri Pirko 19077b9900eSJiri Pirko fl_set_masked_key(&skb_mkey, &skb_key, &head->mask); 19177b9900eSJiri Pirko 192a3308d8fSPaul Blakey f = fl_lookup(head, &skb_mkey); 193e8eb36cdSAmir Vadai if (f && !tc_skip_sw(f->flags)) { 19477b9900eSJiri Pirko *res = f->res; 19577b9900eSJiri Pirko return tcf_exts_exec(skb, &f->exts, res); 19677b9900eSJiri Pirko } 19777b9900eSJiri Pirko return -1; 19877b9900eSJiri Pirko } 19977b9900eSJiri Pirko 20077b9900eSJiri Pirko static int fl_init(struct tcf_proto *tp) 20177b9900eSJiri Pirko { 20277b9900eSJiri Pirko struct cls_fl_head *head; 20377b9900eSJiri Pirko 20477b9900eSJiri Pirko head = kzalloc(sizeof(*head), GFP_KERNEL); 20577b9900eSJiri Pirko if (!head) 20677b9900eSJiri Pirko return -ENOBUFS; 20777b9900eSJiri Pirko 20877b9900eSJiri Pirko INIT_LIST_HEAD_RCU(&head->filters); 20977b9900eSJiri Pirko rcu_assign_pointer(tp->root, head); 21077b9900eSJiri Pirko 21177b9900eSJiri Pirko return 0; 21277b9900eSJiri Pirko } 21377b9900eSJiri Pirko 21477b9900eSJiri Pirko static void fl_destroy_filter(struct rcu_head *head) 21577b9900eSJiri Pirko { 21677b9900eSJiri Pirko struct cls_fl_filter *f = container_of(head, struct cls_fl_filter, rcu); 21777b9900eSJiri Pirko 21877b9900eSJiri Pirko tcf_exts_destroy(&f->exts); 21977b9900eSJiri Pirko kfree(f); 22077b9900eSJiri Pirko } 22177b9900eSJiri Pirko 2223036dab6SHadar Hen Zion static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) 2235b33f488SAmir Vadai { 2245b33f488SAmir Vadai struct tc_cls_flower_offload offload = {0}; 2257091d8c7SHadar Hen Zion struct net_device *dev = f->hw_dev; 2267091d8c7SHadar Hen Zion struct tc_to_netdev *tc = &f->tc; 2275b33f488SAmir Vadai 22879685219SHadar Hen Zion if (!tc_can_offload(dev, tp)) 2295b33f488SAmir Vadai return; 2305b33f488SAmir Vadai 2315b33f488SAmir Vadai offload.command = TC_CLSFLOWER_DESTROY; 23269ca05ceSJiri Pirko offload.prio = tp->prio; 2333036dab6SHadar Hen Zion offload.cookie = (unsigned long)f; 2345b33f488SAmir Vadai 2357091d8c7SHadar Hen Zion tc->type = TC_SETUP_CLSFLOWER; 2367091d8c7SHadar Hen Zion tc->cls_flower = &offload; 2375b33f488SAmir Vadai 2387091d8c7SHadar Hen Zion dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, tc); 2395b33f488SAmir Vadai } 2405b33f488SAmir Vadai 241e8eb36cdSAmir Vadai static int fl_hw_replace_filter(struct tcf_proto *tp, 2425b33f488SAmir Vadai struct flow_dissector *dissector, 2435b33f488SAmir Vadai struct fl_flow_key *mask, 2443036dab6SHadar Hen Zion struct cls_fl_filter *f) 2455b33f488SAmir Vadai { 2465b33f488SAmir Vadai struct net_device *dev = tp->q->dev_queue->dev; 2475b33f488SAmir Vadai struct tc_cls_flower_offload offload = {0}; 2487091d8c7SHadar Hen Zion struct tc_to_netdev *tc = &f->tc; 249e8eb36cdSAmir Vadai int err; 2505b33f488SAmir Vadai 2517091d8c7SHadar Hen Zion if (!tc_can_offload(dev, tp)) { 252a6e16931SHadar Hen Zion if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev) || 253a6e16931SHadar Hen Zion (f->hw_dev && !tc_can_offload(f->hw_dev, tp))) { 254a6e16931SHadar Hen Zion f->hw_dev = dev; 2553036dab6SHadar Hen Zion return tc_skip_sw(f->flags) ? -EINVAL : 0; 256a6e16931SHadar Hen Zion } 2577091d8c7SHadar Hen Zion dev = f->hw_dev; 2587091d8c7SHadar Hen Zion tc->egress_dev = true; 2597091d8c7SHadar Hen Zion } else { 2607091d8c7SHadar Hen Zion f->hw_dev = dev; 2617091d8c7SHadar Hen Zion } 2625b33f488SAmir Vadai 2635b33f488SAmir Vadai offload.command = TC_CLSFLOWER_REPLACE; 26469ca05ceSJiri Pirko offload.prio = tp->prio; 2653036dab6SHadar Hen Zion offload.cookie = (unsigned long)f; 2665b33f488SAmir Vadai offload.dissector = dissector; 2675b33f488SAmir Vadai offload.mask = mask; 268f93bd17bSPaul Blakey offload.key = &f->mkey; 2693036dab6SHadar Hen Zion offload.exts = &f->exts; 2705b33f488SAmir Vadai 2717091d8c7SHadar Hen Zion tc->type = TC_SETUP_CLSFLOWER; 2727091d8c7SHadar Hen Zion tc->cls_flower = &offload; 2735b33f488SAmir Vadai 2745a7a5555SJamal Hadi Salim err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, 2757091d8c7SHadar Hen Zion tc); 276e8eb36cdSAmir Vadai 2773036dab6SHadar Hen Zion if (tc_skip_sw(f->flags)) 278e8eb36cdSAmir Vadai return err; 279e8eb36cdSAmir Vadai return 0; 2805b33f488SAmir Vadai } 2815b33f488SAmir Vadai 28210cbc684SAmir Vadai static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) 28310cbc684SAmir Vadai { 28410cbc684SAmir Vadai struct tc_cls_flower_offload offload = {0}; 2857091d8c7SHadar Hen Zion struct net_device *dev = f->hw_dev; 2867091d8c7SHadar Hen Zion struct tc_to_netdev *tc = &f->tc; 28710cbc684SAmir Vadai 28879685219SHadar Hen Zion if (!tc_can_offload(dev, tp)) 28910cbc684SAmir Vadai return; 29010cbc684SAmir Vadai 29110cbc684SAmir Vadai offload.command = TC_CLSFLOWER_STATS; 29269ca05ceSJiri Pirko offload.prio = tp->prio; 29310cbc684SAmir Vadai offload.cookie = (unsigned long)f; 29410cbc684SAmir Vadai offload.exts = &f->exts; 29510cbc684SAmir Vadai 2967091d8c7SHadar Hen Zion tc->type = TC_SETUP_CLSFLOWER; 2977091d8c7SHadar Hen Zion tc->cls_flower = &offload; 29810cbc684SAmir Vadai 2997091d8c7SHadar Hen Zion dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, tc); 30010cbc684SAmir Vadai } 30110cbc684SAmir Vadai 30213fa876eSRoi Dayan static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) 30313fa876eSRoi Dayan { 30413fa876eSRoi Dayan list_del_rcu(&f->list); 30579685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 3063036dab6SHadar Hen Zion fl_hw_destroy_filter(tp, f); 30713fa876eSRoi Dayan tcf_unbind_filter(tp, &f->res); 30813fa876eSRoi Dayan call_rcu(&f->rcu, fl_destroy_filter); 30913fa876eSRoi Dayan } 31013fa876eSRoi Dayan 311d9363774SDaniel Borkmann static void fl_destroy_sleepable(struct work_struct *work) 312d9363774SDaniel Borkmann { 313d9363774SDaniel Borkmann struct cls_fl_head *head = container_of(work, struct cls_fl_head, 314d9363774SDaniel Borkmann work); 315d9363774SDaniel Borkmann if (head->mask_assigned) 316d9363774SDaniel Borkmann rhashtable_destroy(&head->ht); 317d9363774SDaniel Borkmann kfree(head); 318d9363774SDaniel Borkmann module_put(THIS_MODULE); 319d9363774SDaniel Borkmann } 320d9363774SDaniel Borkmann 321d9363774SDaniel Borkmann static void fl_destroy_rcu(struct rcu_head *rcu) 322d9363774SDaniel Borkmann { 323d9363774SDaniel Borkmann struct cls_fl_head *head = container_of(rcu, struct cls_fl_head, rcu); 324d9363774SDaniel Borkmann 325d9363774SDaniel Borkmann INIT_WORK(&head->work, fl_destroy_sleepable); 326d9363774SDaniel Borkmann schedule_work(&head->work); 327d9363774SDaniel Borkmann } 328d9363774SDaniel Borkmann 32977b9900eSJiri Pirko static bool fl_destroy(struct tcf_proto *tp, bool force) 33077b9900eSJiri Pirko { 33177b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 33277b9900eSJiri Pirko struct cls_fl_filter *f, *next; 33377b9900eSJiri Pirko 33477b9900eSJiri Pirko if (!force && !list_empty(&head->filters)) 33577b9900eSJiri Pirko return false; 33677b9900eSJiri Pirko 33713fa876eSRoi Dayan list_for_each_entry_safe(f, next, &head->filters, list) 33813fa876eSRoi Dayan __fl_delete(tp, f); 339d9363774SDaniel Borkmann 340d9363774SDaniel Borkmann __module_get(THIS_MODULE); 341d9363774SDaniel Borkmann call_rcu(&head->rcu, fl_destroy_rcu); 3422745529aSDavid S. Miller 34377b9900eSJiri Pirko return true; 34477b9900eSJiri Pirko } 34577b9900eSJiri Pirko 34677b9900eSJiri Pirko static unsigned long fl_get(struct tcf_proto *tp, u32 handle) 34777b9900eSJiri Pirko { 34877b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 34977b9900eSJiri Pirko struct cls_fl_filter *f; 35077b9900eSJiri Pirko 35177b9900eSJiri Pirko list_for_each_entry(f, &head->filters, list) 35277b9900eSJiri Pirko if (f->handle == handle) 35377b9900eSJiri Pirko return (unsigned long) f; 35477b9900eSJiri Pirko return 0; 35577b9900eSJiri Pirko } 35677b9900eSJiri Pirko 35777b9900eSJiri Pirko static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { 35877b9900eSJiri Pirko [TCA_FLOWER_UNSPEC] = { .type = NLA_UNSPEC }, 35977b9900eSJiri Pirko [TCA_FLOWER_CLASSID] = { .type = NLA_U32 }, 36077b9900eSJiri Pirko [TCA_FLOWER_INDEV] = { .type = NLA_STRING, 36177b9900eSJiri Pirko .len = IFNAMSIZ }, 36277b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST] = { .len = ETH_ALEN }, 36377b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST_MASK] = { .len = ETH_ALEN }, 36477b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC] = { .len = ETH_ALEN }, 36577b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .len = ETH_ALEN }, 36677b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, 36777b9900eSJiri Pirko [TCA_FLOWER_KEY_IP_PROTO] = { .type = NLA_U8 }, 36877b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, 36977b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, 37077b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, 37177b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, 37277b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 37377b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 37477b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 37577b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 37677b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_SRC] = { .type = NLA_U16 }, 37777b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_DST] = { .type = NLA_U16 }, 378b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_SRC] = { .type = NLA_U16 }, 379b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_DST] = { .type = NLA_U16 }, 3809399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, 3819399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, 3829399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, 383bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, 384bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, 385bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 }, 386bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, 387bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 }, 388bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 389bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 390bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 391bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 392aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NLA_U16 }, 393aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, 394aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, 395aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, 3965976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, 3975976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, 3985976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, 3995976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, 400f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, 401f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, 402f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, 403f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, 404faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, 405faa3ffceSOr Gerlitz [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, 4067b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NLA_U8 }, 4077b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 }, 4087b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NLA_U8 }, 4097b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 }, 4107b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NLA_U8 }, 4117b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 }, 4127b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NLA_U8 }, 4137b684884SSimon Horman [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 }, 41499d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP] = { .type = NLA_U32 }, 41599d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NLA_U32 }, 41699d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP] = { .type = NLA_U32 }, 41799d31326SSimon Horman [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NLA_U32 }, 41899d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP] = { .type = NLA_U8 }, 41999d31326SSimon Horman [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NLA_U8 }, 42099d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA] = { .len = ETH_ALEN }, 42199d31326SSimon Horman [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .len = ETH_ALEN }, 42299d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA] = { .len = ETH_ALEN }, 42399d31326SSimon Horman [TCA_FLOWER_KEY_ARP_THA_MASK] = { .len = ETH_ALEN }, 42477b9900eSJiri Pirko }; 42577b9900eSJiri Pirko 42677b9900eSJiri Pirko static void fl_set_key_val(struct nlattr **tb, 42777b9900eSJiri Pirko void *val, int val_type, 42877b9900eSJiri Pirko void *mask, int mask_type, int len) 42977b9900eSJiri Pirko { 43077b9900eSJiri Pirko if (!tb[val_type]) 43177b9900eSJiri Pirko return; 43277b9900eSJiri Pirko memcpy(val, nla_data(tb[val_type]), len); 43377b9900eSJiri Pirko if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type]) 43477b9900eSJiri Pirko memset(mask, 0xff, len); 43577b9900eSJiri Pirko else 43677b9900eSJiri Pirko memcpy(mask, nla_data(tb[mask_type]), len); 43777b9900eSJiri Pirko } 43877b9900eSJiri Pirko 4399399ae9aSHadar Hen Zion static void fl_set_key_vlan(struct nlattr **tb, 4409399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_val, 4419399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_mask) 4429399ae9aSHadar Hen Zion { 4439399ae9aSHadar Hen Zion #define VLAN_PRIORITY_MASK 0x7 4449399ae9aSHadar Hen Zion 4459399ae9aSHadar Hen Zion if (tb[TCA_FLOWER_KEY_VLAN_ID]) { 4469399ae9aSHadar Hen Zion key_val->vlan_id = 4479399ae9aSHadar Hen Zion nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ID]) & VLAN_VID_MASK; 4489399ae9aSHadar Hen Zion key_mask->vlan_id = VLAN_VID_MASK; 4499399ae9aSHadar Hen Zion } 4509399ae9aSHadar Hen Zion if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { 4519399ae9aSHadar Hen Zion key_val->vlan_priority = 4529399ae9aSHadar Hen Zion nla_get_u8(tb[TCA_FLOWER_KEY_VLAN_PRIO]) & 4539399ae9aSHadar Hen Zion VLAN_PRIORITY_MASK; 4549399ae9aSHadar Hen Zion key_mask->vlan_priority = VLAN_PRIORITY_MASK; 4559399ae9aSHadar Hen Zion } 4569399ae9aSHadar Hen Zion } 4579399ae9aSHadar Hen Zion 458faa3ffceSOr Gerlitz static void fl_set_key_flag(u32 flower_key, u32 flower_mask, 459faa3ffceSOr Gerlitz u32 *dissector_key, u32 *dissector_mask, 460faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 461faa3ffceSOr Gerlitz { 462faa3ffceSOr Gerlitz if (flower_mask & flower_flag_bit) { 463faa3ffceSOr Gerlitz *dissector_mask |= dissector_flag_bit; 464faa3ffceSOr Gerlitz if (flower_key & flower_flag_bit) 465faa3ffceSOr Gerlitz *dissector_key |= dissector_flag_bit; 466faa3ffceSOr Gerlitz } 467faa3ffceSOr Gerlitz } 468faa3ffceSOr Gerlitz 469d9724772SOr Gerlitz static int fl_set_key_flags(struct nlattr **tb, 470faa3ffceSOr Gerlitz u32 *flags_key, u32 *flags_mask) 471faa3ffceSOr Gerlitz { 472faa3ffceSOr Gerlitz u32 key, mask; 473faa3ffceSOr Gerlitz 474d9724772SOr Gerlitz /* mask is mandatory for flags */ 475d9724772SOr Gerlitz if (!tb[TCA_FLOWER_KEY_FLAGS_MASK]) 476d9724772SOr Gerlitz return -EINVAL; 477faa3ffceSOr Gerlitz 478faa3ffceSOr Gerlitz key = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS])); 479faa3ffceSOr Gerlitz mask = be32_to_cpu(nla_get_u32(tb[TCA_FLOWER_KEY_FLAGS_MASK])); 480faa3ffceSOr Gerlitz 481faa3ffceSOr Gerlitz *flags_key = 0; 482faa3ffceSOr Gerlitz *flags_mask = 0; 483faa3ffceSOr Gerlitz 484faa3ffceSOr Gerlitz fl_set_key_flag(key, mask, flags_key, flags_mask, 485faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 486d9724772SOr Gerlitz 487d9724772SOr Gerlitz return 0; 488faa3ffceSOr Gerlitz } 489faa3ffceSOr Gerlitz 49077b9900eSJiri Pirko static int fl_set_key(struct net *net, struct nlattr **tb, 49177b9900eSJiri Pirko struct fl_flow_key *key, struct fl_flow_key *mask) 49277b9900eSJiri Pirko { 4939399ae9aSHadar Hen Zion __be16 ethertype; 494d9724772SOr Gerlitz int ret = 0; 495dd3aa3b5SBrian Haley #ifdef CONFIG_NET_CLS_IND 49677b9900eSJiri Pirko if (tb[TCA_FLOWER_INDEV]) { 497dd3aa3b5SBrian Haley int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV]); 49877b9900eSJiri Pirko if (err < 0) 49977b9900eSJiri Pirko return err; 50077b9900eSJiri Pirko key->indev_ifindex = err; 50177b9900eSJiri Pirko mask->indev_ifindex = 0xffffffff; 50277b9900eSJiri Pirko } 503dd3aa3b5SBrian Haley #endif 50477b9900eSJiri Pirko 50577b9900eSJiri Pirko fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 50677b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 50777b9900eSJiri Pirko sizeof(key->eth.dst)); 50877b9900eSJiri Pirko fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 50977b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 51077b9900eSJiri Pirko sizeof(key->eth.src)); 51166530bdfSJamal Hadi Salim 5120b498a52SArnd Bergmann if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { 5139399ae9aSHadar Hen Zion ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]); 5149399ae9aSHadar Hen Zion 5159399ae9aSHadar Hen Zion if (ethertype == htons(ETH_P_8021Q)) { 5169399ae9aSHadar Hen Zion fl_set_key_vlan(tb, &key->vlan, &mask->vlan); 5179399ae9aSHadar Hen Zion fl_set_key_val(tb, &key->basic.n_proto, 5189399ae9aSHadar Hen Zion TCA_FLOWER_KEY_VLAN_ETH_TYPE, 51977b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 52077b9900eSJiri Pirko sizeof(key->basic.n_proto)); 5219399ae9aSHadar Hen Zion } else { 5229399ae9aSHadar Hen Zion key->basic.n_proto = ethertype; 5239399ae9aSHadar Hen Zion mask->basic.n_proto = cpu_to_be16(~0); 5249399ae9aSHadar Hen Zion } 5250b498a52SArnd Bergmann } 52666530bdfSJamal Hadi Salim 52777b9900eSJiri Pirko if (key->basic.n_proto == htons(ETH_P_IP) || 52877b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) { 52977b9900eSJiri Pirko fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 53077b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 53177b9900eSJiri Pirko sizeof(key->basic.ip_proto)); 53277b9900eSJiri Pirko } 53366530bdfSJamal Hadi Salim 53466530bdfSJamal Hadi Salim if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) { 53566530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 536970bfcd0SPaul Blakey mask->control.addr_type = ~0; 53777b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 53877b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 53977b9900eSJiri Pirko sizeof(key->ipv4.src)); 54077b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 54177b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 54277b9900eSJiri Pirko sizeof(key->ipv4.dst)); 54366530bdfSJamal Hadi Salim } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) { 54466530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 545970bfcd0SPaul Blakey mask->control.addr_type = ~0; 54677b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 54777b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 54877b9900eSJiri Pirko sizeof(key->ipv6.src)); 54977b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 55077b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 55177b9900eSJiri Pirko sizeof(key->ipv6.dst)); 55277b9900eSJiri Pirko } 55366530bdfSJamal Hadi Salim 55477b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP) { 55577b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 556aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 55777b9900eSJiri Pirko sizeof(key->tp.src)); 55877b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 559aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 56077b9900eSJiri Pirko sizeof(key->tp.dst)); 56177b9900eSJiri Pirko } else if (key->basic.ip_proto == IPPROTO_UDP) { 56277b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 563aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 56477b9900eSJiri Pirko sizeof(key->tp.src)); 56577b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 566aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 56777b9900eSJiri Pirko sizeof(key->tp.dst)); 5685976c5f4SSimon Horman } else if (key->basic.ip_proto == IPPROTO_SCTP) { 5695976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 5705976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 5715976c5f4SSimon Horman sizeof(key->tp.src)); 5725976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 5735976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 5745976c5f4SSimon Horman sizeof(key->tp.dst)); 5757b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IP) && 5767b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP) { 5777b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE, 5787b684884SSimon Horman &mask->icmp.type, 5797b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 5807b684884SSimon Horman sizeof(key->icmp.type)); 5817b684884SSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, 5827b684884SSimon Horman &mask->icmp.code, 5837b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 5847b684884SSimon Horman sizeof(key->icmp.code)); 5857b684884SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_IPV6) && 5867b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6) { 5877b684884SSimon Horman fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE, 5887b684884SSimon Horman &mask->icmp.type, 5897b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 5907b684884SSimon Horman sizeof(key->icmp.type)); 591040587afSSimon Horman fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE, 5927b684884SSimon Horman &mask->icmp.code, 593040587afSSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 5947b684884SSimon Horman sizeof(key->icmp.code)); 59599d31326SSimon Horman } else if (key->basic.n_proto == htons(ETH_P_ARP) || 59699d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) { 59799d31326SSimon Horman fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, 59899d31326SSimon Horman &mask->arp.sip, TCA_FLOWER_KEY_ARP_SIP_MASK, 59999d31326SSimon Horman sizeof(key->arp.sip)); 60099d31326SSimon Horman fl_set_key_val(tb, &key->arp.tip, TCA_FLOWER_KEY_ARP_TIP, 60199d31326SSimon Horman &mask->arp.tip, TCA_FLOWER_KEY_ARP_TIP_MASK, 60299d31326SSimon Horman sizeof(key->arp.tip)); 60399d31326SSimon Horman fl_set_key_val(tb, &key->arp.op, TCA_FLOWER_KEY_ARP_OP, 60499d31326SSimon Horman &mask->arp.op, TCA_FLOWER_KEY_ARP_OP_MASK, 60599d31326SSimon Horman sizeof(key->arp.op)); 60699d31326SSimon Horman fl_set_key_val(tb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 60799d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 60899d31326SSimon Horman sizeof(key->arp.sha)); 60999d31326SSimon Horman fl_set_key_val(tb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 61099d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 61199d31326SSimon Horman sizeof(key->arp.tha)); 61277b9900eSJiri Pirko } 61377b9900eSJiri Pirko 614bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || 615bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) { 616bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 617970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 618bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.src, 619bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, 620bc3103f1SAmir Vadai &mask->enc_ipv4.src, 621bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 622bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)); 623bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.dst, 624bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, 625bc3103f1SAmir Vadai &mask->enc_ipv4.dst, 626bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 627bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)); 628bc3103f1SAmir Vadai } 629bc3103f1SAmir Vadai 630bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] || 631bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) { 632bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 633970bfcd0SPaul Blakey mask->enc_control.addr_type = ~0; 634bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.src, 635bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, 636bc3103f1SAmir Vadai &mask->enc_ipv6.src, 637bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 638bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)); 639bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.dst, 640bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 641bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 642bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 643bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)); 644bc3103f1SAmir Vadai } 645bc3103f1SAmir Vadai 646bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID, 647eb523f42SHadar Hen Zion &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, 648bc3103f1SAmir Vadai sizeof(key->enc_key_id.keyid)); 649bc3103f1SAmir Vadai 650f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 651f4d997fdSHadar Hen Zion &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 652f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)); 653f4d997fdSHadar Hen Zion 654f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 655f4d997fdSHadar Hen Zion &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 656f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst)); 657f4d997fdSHadar Hen Zion 658d9724772SOr Gerlitz if (tb[TCA_FLOWER_KEY_FLAGS]) 659d9724772SOr Gerlitz ret = fl_set_key_flags(tb, &key->control.flags, &mask->control.flags); 660faa3ffceSOr Gerlitz 661d9724772SOr Gerlitz return ret; 66277b9900eSJiri Pirko } 66377b9900eSJiri Pirko 66477b9900eSJiri Pirko static bool fl_mask_eq(struct fl_flow_mask *mask1, 66577b9900eSJiri Pirko struct fl_flow_mask *mask2) 66677b9900eSJiri Pirko { 66777b9900eSJiri Pirko const long *lmask1 = fl_key_get_start(&mask1->key, mask1); 66877b9900eSJiri Pirko const long *lmask2 = fl_key_get_start(&mask2->key, mask2); 66977b9900eSJiri Pirko 67077b9900eSJiri Pirko return !memcmp(&mask1->range, &mask2->range, sizeof(mask1->range)) && 67177b9900eSJiri Pirko !memcmp(lmask1, lmask2, fl_mask_range(mask1)); 67277b9900eSJiri Pirko } 67377b9900eSJiri Pirko 67477b9900eSJiri Pirko static const struct rhashtable_params fl_ht_params = { 67577b9900eSJiri Pirko .key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */ 67677b9900eSJiri Pirko .head_offset = offsetof(struct cls_fl_filter, ht_node), 67777b9900eSJiri Pirko .automatic_shrinking = true, 67877b9900eSJiri Pirko }; 67977b9900eSJiri Pirko 68077b9900eSJiri Pirko static int fl_init_hashtable(struct cls_fl_head *head, 68177b9900eSJiri Pirko struct fl_flow_mask *mask) 68277b9900eSJiri Pirko { 68377b9900eSJiri Pirko head->ht_params = fl_ht_params; 68477b9900eSJiri Pirko head->ht_params.key_len = fl_mask_range(mask); 68577b9900eSJiri Pirko head->ht_params.key_offset += mask->range.start; 68677b9900eSJiri Pirko 68777b9900eSJiri Pirko return rhashtable_init(&head->ht, &head->ht_params); 68877b9900eSJiri Pirko } 68977b9900eSJiri Pirko 69077b9900eSJiri Pirko #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) 69177b9900eSJiri Pirko #define FL_KEY_MEMBER_SIZE(member) (sizeof(((struct fl_flow_key *) 0)->member)) 69277b9900eSJiri Pirko 693339ba878SHadar Hen Zion #define FL_KEY_IS_MASKED(mask, member) \ 694339ba878SHadar Hen Zion memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ 695339ba878SHadar Hen Zion 0, FL_KEY_MEMBER_SIZE(member)) \ 69677b9900eSJiri Pirko 69777b9900eSJiri Pirko #define FL_KEY_SET(keys, cnt, id, member) \ 69877b9900eSJiri Pirko do { \ 69977b9900eSJiri Pirko keys[cnt].key_id = id; \ 70077b9900eSJiri Pirko keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member); \ 70177b9900eSJiri Pirko cnt++; \ 70277b9900eSJiri Pirko } while(0); 70377b9900eSJiri Pirko 704339ba878SHadar Hen Zion #define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member) \ 70577b9900eSJiri Pirko do { \ 706339ba878SHadar Hen Zion if (FL_KEY_IS_MASKED(mask, member)) \ 70777b9900eSJiri Pirko FL_KEY_SET(keys, cnt, id, member); \ 70877b9900eSJiri Pirko } while(0); 70977b9900eSJiri Pirko 71077b9900eSJiri Pirko static void fl_init_dissector(struct cls_fl_head *head, 71177b9900eSJiri Pirko struct fl_flow_mask *mask) 71277b9900eSJiri Pirko { 71377b9900eSJiri Pirko struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX]; 71477b9900eSJiri Pirko size_t cnt = 0; 71577b9900eSJiri Pirko 71642aecaa9STom Herbert FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control); 71777b9900eSJiri Pirko FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic); 718339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 71977b9900eSJiri Pirko FLOW_DISSECTOR_KEY_ETH_ADDRS, eth); 720339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 72177b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); 722339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 72377b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); 724339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 72577b9900eSJiri Pirko FLOW_DISSECTOR_KEY_PORTS, tp); 7269399ae9aSHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 7277b684884SSimon Horman FLOW_DISSECTOR_KEY_ICMP, icmp); 7287b684884SSimon Horman FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 72999d31326SSimon Horman FLOW_DISSECTOR_KEY_ARP, arp); 73099d31326SSimon Horman FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 7319399ae9aSHadar Hen Zion FLOW_DISSECTOR_KEY_VLAN, vlan); 732519d1052SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 733519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); 734519d1052SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 735519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); 736519d1052SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 737519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); 738519d1052SHadar Hen Zion if (FL_KEY_IS_MASKED(&mask->key, enc_ipv4) || 739519d1052SHadar Hen Zion FL_KEY_IS_MASKED(&mask->key, enc_ipv6)) 740519d1052SHadar Hen Zion FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, 741519d1052SHadar Hen Zion enc_control); 742f4d997fdSHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 743f4d997fdSHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); 74477b9900eSJiri Pirko 74577b9900eSJiri Pirko skb_flow_dissector_init(&head->dissector, keys, cnt); 74677b9900eSJiri Pirko } 74777b9900eSJiri Pirko 74877b9900eSJiri Pirko static int fl_check_assign_mask(struct cls_fl_head *head, 74977b9900eSJiri Pirko struct fl_flow_mask *mask) 75077b9900eSJiri Pirko { 75177b9900eSJiri Pirko int err; 75277b9900eSJiri Pirko 75377b9900eSJiri Pirko if (head->mask_assigned) { 75477b9900eSJiri Pirko if (!fl_mask_eq(&head->mask, mask)) 75577b9900eSJiri Pirko return -EINVAL; 75677b9900eSJiri Pirko else 75777b9900eSJiri Pirko return 0; 75877b9900eSJiri Pirko } 75977b9900eSJiri Pirko 76077b9900eSJiri Pirko /* Mask is not assigned yet. So assign it and init hashtable 76177b9900eSJiri Pirko * according to that. 76277b9900eSJiri Pirko */ 76377b9900eSJiri Pirko err = fl_init_hashtable(head, mask); 76477b9900eSJiri Pirko if (err) 76577b9900eSJiri Pirko return err; 76677b9900eSJiri Pirko memcpy(&head->mask, mask, sizeof(head->mask)); 76777b9900eSJiri Pirko head->mask_assigned = true; 76877b9900eSJiri Pirko 76977b9900eSJiri Pirko fl_init_dissector(head, mask); 77077b9900eSJiri Pirko 77177b9900eSJiri Pirko return 0; 77277b9900eSJiri Pirko } 77377b9900eSJiri Pirko 77477b9900eSJiri Pirko static int fl_set_parms(struct net *net, struct tcf_proto *tp, 77577b9900eSJiri Pirko struct cls_fl_filter *f, struct fl_flow_mask *mask, 77677b9900eSJiri Pirko unsigned long base, struct nlattr **tb, 77777b9900eSJiri Pirko struct nlattr *est, bool ovr) 77877b9900eSJiri Pirko { 77977b9900eSJiri Pirko struct tcf_exts e; 78077b9900eSJiri Pirko int err; 78177b9900eSJiri Pirko 782b9a24bb7SWANG Cong err = tcf_exts_init(&e, TCA_FLOWER_ACT, 0); 78377b9900eSJiri Pirko if (err < 0) 78477b9900eSJiri Pirko return err; 785b9a24bb7SWANG Cong err = tcf_exts_validate(net, tp, tb, est, &e, ovr); 786b9a24bb7SWANG Cong if (err < 0) 787b9a24bb7SWANG Cong goto errout; 78877b9900eSJiri Pirko 78977b9900eSJiri Pirko if (tb[TCA_FLOWER_CLASSID]) { 79077b9900eSJiri Pirko f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); 79177b9900eSJiri Pirko tcf_bind_filter(tp, &f->res, base); 79277b9900eSJiri Pirko } 79377b9900eSJiri Pirko 79477b9900eSJiri Pirko err = fl_set_key(net, tb, &f->key, &mask->key); 79577b9900eSJiri Pirko if (err) 79677b9900eSJiri Pirko goto errout; 79777b9900eSJiri Pirko 79877b9900eSJiri Pirko fl_mask_update_range(mask); 79977b9900eSJiri Pirko fl_set_masked_key(&f->mkey, &f->key, mask); 80077b9900eSJiri Pirko 80177b9900eSJiri Pirko tcf_exts_change(tp, &f->exts, &e); 80277b9900eSJiri Pirko 80377b9900eSJiri Pirko return 0; 80477b9900eSJiri Pirko errout: 80577b9900eSJiri Pirko tcf_exts_destroy(&e); 80677b9900eSJiri Pirko return err; 80777b9900eSJiri Pirko } 80877b9900eSJiri Pirko 80977b9900eSJiri Pirko static u32 fl_grab_new_handle(struct tcf_proto *tp, 81077b9900eSJiri Pirko struct cls_fl_head *head) 81177b9900eSJiri Pirko { 81277b9900eSJiri Pirko unsigned int i = 0x80000000; 81377b9900eSJiri Pirko u32 handle; 81477b9900eSJiri Pirko 81577b9900eSJiri Pirko do { 81677b9900eSJiri Pirko if (++head->hgen == 0x7FFFFFFF) 81777b9900eSJiri Pirko head->hgen = 1; 81877b9900eSJiri Pirko } while (--i > 0 && fl_get(tp, head->hgen)); 81977b9900eSJiri Pirko 82077b9900eSJiri Pirko if (unlikely(i == 0)) { 82177b9900eSJiri Pirko pr_err("Insufficient number of handles\n"); 82277b9900eSJiri Pirko handle = 0; 82377b9900eSJiri Pirko } else { 82477b9900eSJiri Pirko handle = head->hgen; 82577b9900eSJiri Pirko } 82677b9900eSJiri Pirko 82777b9900eSJiri Pirko return handle; 82877b9900eSJiri Pirko } 82977b9900eSJiri Pirko 83077b9900eSJiri Pirko static int fl_change(struct net *net, struct sk_buff *in_skb, 83177b9900eSJiri Pirko struct tcf_proto *tp, unsigned long base, 83277b9900eSJiri Pirko u32 handle, struct nlattr **tca, 83377b9900eSJiri Pirko unsigned long *arg, bool ovr) 83477b9900eSJiri Pirko { 83577b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 83677b9900eSJiri Pirko struct cls_fl_filter *fold = (struct cls_fl_filter *) *arg; 83777b9900eSJiri Pirko struct cls_fl_filter *fnew; 83839b7b6a6SArnd Bergmann struct nlattr **tb; 83977b9900eSJiri Pirko struct fl_flow_mask mask = {}; 84077b9900eSJiri Pirko int err; 84177b9900eSJiri Pirko 84277b9900eSJiri Pirko if (!tca[TCA_OPTIONS]) 84377b9900eSJiri Pirko return -EINVAL; 84477b9900eSJiri Pirko 84539b7b6a6SArnd Bergmann tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 84639b7b6a6SArnd Bergmann if (!tb) 84739b7b6a6SArnd Bergmann return -ENOBUFS; 84839b7b6a6SArnd Bergmann 84977b9900eSJiri Pirko err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS], fl_policy); 85077b9900eSJiri Pirko if (err < 0) 85139b7b6a6SArnd Bergmann goto errout_tb; 85277b9900eSJiri Pirko 85339b7b6a6SArnd Bergmann if (fold && handle && fold->handle != handle) { 85439b7b6a6SArnd Bergmann err = -EINVAL; 85539b7b6a6SArnd Bergmann goto errout_tb; 85639b7b6a6SArnd Bergmann } 85777b9900eSJiri Pirko 85877b9900eSJiri Pirko fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 85939b7b6a6SArnd Bergmann if (!fnew) { 86039b7b6a6SArnd Bergmann err = -ENOBUFS; 86139b7b6a6SArnd Bergmann goto errout_tb; 86239b7b6a6SArnd Bergmann } 86377b9900eSJiri Pirko 864b9a24bb7SWANG Cong err = tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0); 865b9a24bb7SWANG Cong if (err < 0) 866b9a24bb7SWANG Cong goto errout; 86777b9900eSJiri Pirko 86877b9900eSJiri Pirko if (!handle) { 86977b9900eSJiri Pirko handle = fl_grab_new_handle(tp, head); 87077b9900eSJiri Pirko if (!handle) { 87177b9900eSJiri Pirko err = -EINVAL; 87277b9900eSJiri Pirko goto errout; 87377b9900eSJiri Pirko } 87477b9900eSJiri Pirko } 87577b9900eSJiri Pirko fnew->handle = handle; 87677b9900eSJiri Pirko 877e69985c6SAmir Vadai if (tb[TCA_FLOWER_FLAGS]) { 878e69985c6SAmir Vadai fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); 879e69985c6SAmir Vadai 880e69985c6SAmir Vadai if (!tc_flags_valid(fnew->flags)) { 881e69985c6SAmir Vadai err = -EINVAL; 882e69985c6SAmir Vadai goto errout; 883e69985c6SAmir Vadai } 884e69985c6SAmir Vadai } 8855b33f488SAmir Vadai 88677b9900eSJiri Pirko err = fl_set_parms(net, tp, fnew, &mask, base, tb, tca[TCA_RATE], ovr); 88777b9900eSJiri Pirko if (err) 88877b9900eSJiri Pirko goto errout; 88977b9900eSJiri Pirko 89077b9900eSJiri Pirko err = fl_check_assign_mask(head, &mask); 89177b9900eSJiri Pirko if (err) 89277b9900eSJiri Pirko goto errout; 89377b9900eSJiri Pirko 894e8eb36cdSAmir Vadai if (!tc_skip_sw(fnew->flags)) { 895a3308d8fSPaul Blakey if (!fold && fl_lookup(head, &fnew->mkey)) { 896a3308d8fSPaul Blakey err = -EEXIST; 897a3308d8fSPaul Blakey goto errout; 898a3308d8fSPaul Blakey } 899a3308d8fSPaul Blakey 90077b9900eSJiri Pirko err = rhashtable_insert_fast(&head->ht, &fnew->ht_node, 90177b9900eSJiri Pirko head->ht_params); 90277b9900eSJiri Pirko if (err) 90377b9900eSJiri Pirko goto errout; 904e69985c6SAmir Vadai } 9055b33f488SAmir Vadai 90679685219SHadar Hen Zion if (!tc_skip_hw(fnew->flags)) { 907e8eb36cdSAmir Vadai err = fl_hw_replace_filter(tp, 9085b33f488SAmir Vadai &head->dissector, 9095b33f488SAmir Vadai &mask.key, 9103036dab6SHadar Hen Zion fnew); 911e8eb36cdSAmir Vadai if (err) 912e8eb36cdSAmir Vadai goto errout; 91379685219SHadar Hen Zion } 9145b33f488SAmir Vadai 9155b33f488SAmir Vadai if (fold) { 916725cbb62SJiri Pirko if (!tc_skip_sw(fold->flags)) 91777b9900eSJiri Pirko rhashtable_remove_fast(&head->ht, &fold->ht_node, 91877b9900eSJiri Pirko head->ht_params); 91979685219SHadar Hen Zion if (!tc_skip_hw(fold->flags)) 9203036dab6SHadar Hen Zion fl_hw_destroy_filter(tp, fold); 9215b33f488SAmir Vadai } 92277b9900eSJiri Pirko 92377b9900eSJiri Pirko *arg = (unsigned long) fnew; 92477b9900eSJiri Pirko 92577b9900eSJiri Pirko if (fold) { 926ff3532f2SDaniel Borkmann list_replace_rcu(&fold->list, &fnew->list); 92777b9900eSJiri Pirko tcf_unbind_filter(tp, &fold->res); 92877b9900eSJiri Pirko call_rcu(&fold->rcu, fl_destroy_filter); 92977b9900eSJiri Pirko } else { 93077b9900eSJiri Pirko list_add_tail_rcu(&fnew->list, &head->filters); 93177b9900eSJiri Pirko } 93277b9900eSJiri Pirko 93339b7b6a6SArnd Bergmann kfree(tb); 93477b9900eSJiri Pirko return 0; 93577b9900eSJiri Pirko 93677b9900eSJiri Pirko errout: 937b9a24bb7SWANG Cong tcf_exts_destroy(&fnew->exts); 93877b9900eSJiri Pirko kfree(fnew); 93939b7b6a6SArnd Bergmann errout_tb: 94039b7b6a6SArnd Bergmann kfree(tb); 94177b9900eSJiri Pirko return err; 94277b9900eSJiri Pirko } 94377b9900eSJiri Pirko 94477b9900eSJiri Pirko static int fl_delete(struct tcf_proto *tp, unsigned long arg) 94577b9900eSJiri Pirko { 94677b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 94777b9900eSJiri Pirko struct cls_fl_filter *f = (struct cls_fl_filter *) arg; 94877b9900eSJiri Pirko 949725cbb62SJiri Pirko if (!tc_skip_sw(f->flags)) 95077b9900eSJiri Pirko rhashtable_remove_fast(&head->ht, &f->ht_node, 95177b9900eSJiri Pirko head->ht_params); 95213fa876eSRoi Dayan __fl_delete(tp, f); 95377b9900eSJiri Pirko return 0; 95477b9900eSJiri Pirko } 95577b9900eSJiri Pirko 95677b9900eSJiri Pirko static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg) 95777b9900eSJiri Pirko { 95877b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 95977b9900eSJiri Pirko struct cls_fl_filter *f; 96077b9900eSJiri Pirko 96177b9900eSJiri Pirko list_for_each_entry_rcu(f, &head->filters, list) { 96277b9900eSJiri Pirko if (arg->count < arg->skip) 96377b9900eSJiri Pirko goto skip; 96477b9900eSJiri Pirko if (arg->fn(tp, (unsigned long) f, arg) < 0) { 96577b9900eSJiri Pirko arg->stop = 1; 96677b9900eSJiri Pirko break; 96777b9900eSJiri Pirko } 96877b9900eSJiri Pirko skip: 96977b9900eSJiri Pirko arg->count++; 97077b9900eSJiri Pirko } 97177b9900eSJiri Pirko } 97277b9900eSJiri Pirko 97377b9900eSJiri Pirko static int fl_dump_key_val(struct sk_buff *skb, 97477b9900eSJiri Pirko void *val, int val_type, 97577b9900eSJiri Pirko void *mask, int mask_type, int len) 97677b9900eSJiri Pirko { 97777b9900eSJiri Pirko int err; 97877b9900eSJiri Pirko 97977b9900eSJiri Pirko if (!memchr_inv(mask, 0, len)) 98077b9900eSJiri Pirko return 0; 98177b9900eSJiri Pirko err = nla_put(skb, val_type, len, val); 98277b9900eSJiri Pirko if (err) 98377b9900eSJiri Pirko return err; 98477b9900eSJiri Pirko if (mask_type != TCA_FLOWER_UNSPEC) { 98577b9900eSJiri Pirko err = nla_put(skb, mask_type, len, mask); 98677b9900eSJiri Pirko if (err) 98777b9900eSJiri Pirko return err; 98877b9900eSJiri Pirko } 98977b9900eSJiri Pirko return 0; 99077b9900eSJiri Pirko } 99177b9900eSJiri Pirko 9929399ae9aSHadar Hen Zion static int fl_dump_key_vlan(struct sk_buff *skb, 9939399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_key, 9949399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_mask) 9959399ae9aSHadar Hen Zion { 9969399ae9aSHadar Hen Zion int err; 9979399ae9aSHadar Hen Zion 9989399ae9aSHadar Hen Zion if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask))) 9999399ae9aSHadar Hen Zion return 0; 10009399ae9aSHadar Hen Zion if (vlan_mask->vlan_id) { 10019399ae9aSHadar Hen Zion err = nla_put_u16(skb, TCA_FLOWER_KEY_VLAN_ID, 10029399ae9aSHadar Hen Zion vlan_key->vlan_id); 10039399ae9aSHadar Hen Zion if (err) 10049399ae9aSHadar Hen Zion return err; 10059399ae9aSHadar Hen Zion } 10069399ae9aSHadar Hen Zion if (vlan_mask->vlan_priority) { 10079399ae9aSHadar Hen Zion err = nla_put_u8(skb, TCA_FLOWER_KEY_VLAN_PRIO, 10089399ae9aSHadar Hen Zion vlan_key->vlan_priority); 10099399ae9aSHadar Hen Zion if (err) 10109399ae9aSHadar Hen Zion return err; 10119399ae9aSHadar Hen Zion } 10129399ae9aSHadar Hen Zion return 0; 10139399ae9aSHadar Hen Zion } 10149399ae9aSHadar Hen Zion 1015faa3ffceSOr Gerlitz static void fl_get_key_flag(u32 dissector_key, u32 dissector_mask, 1016faa3ffceSOr Gerlitz u32 *flower_key, u32 *flower_mask, 1017faa3ffceSOr Gerlitz u32 flower_flag_bit, u32 dissector_flag_bit) 1018faa3ffceSOr Gerlitz { 1019faa3ffceSOr Gerlitz if (dissector_mask & dissector_flag_bit) { 1020faa3ffceSOr Gerlitz *flower_mask |= flower_flag_bit; 1021faa3ffceSOr Gerlitz if (dissector_key & dissector_flag_bit) 1022faa3ffceSOr Gerlitz *flower_key |= flower_flag_bit; 1023faa3ffceSOr Gerlitz } 1024faa3ffceSOr Gerlitz } 1025faa3ffceSOr Gerlitz 1026faa3ffceSOr Gerlitz static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) 1027faa3ffceSOr Gerlitz { 1028faa3ffceSOr Gerlitz u32 key, mask; 1029faa3ffceSOr Gerlitz __be32 _key, _mask; 1030faa3ffceSOr Gerlitz int err; 1031faa3ffceSOr Gerlitz 1032faa3ffceSOr Gerlitz if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask))) 1033faa3ffceSOr Gerlitz return 0; 1034faa3ffceSOr Gerlitz 1035faa3ffceSOr Gerlitz key = 0; 1036faa3ffceSOr Gerlitz mask = 0; 1037faa3ffceSOr Gerlitz 1038faa3ffceSOr Gerlitz fl_get_key_flag(flags_key, flags_mask, &key, &mask, 1039faa3ffceSOr Gerlitz TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 1040faa3ffceSOr Gerlitz 1041faa3ffceSOr Gerlitz _key = cpu_to_be32(key); 1042faa3ffceSOr Gerlitz _mask = cpu_to_be32(mask); 1043faa3ffceSOr Gerlitz 1044faa3ffceSOr Gerlitz err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key); 1045faa3ffceSOr Gerlitz if (err) 1046faa3ffceSOr Gerlitz return err; 1047faa3ffceSOr Gerlitz 1048faa3ffceSOr Gerlitz return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); 1049faa3ffceSOr Gerlitz } 1050faa3ffceSOr Gerlitz 105177b9900eSJiri Pirko static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, 105277b9900eSJiri Pirko struct sk_buff *skb, struct tcmsg *t) 105377b9900eSJiri Pirko { 105477b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 105577b9900eSJiri Pirko struct cls_fl_filter *f = (struct cls_fl_filter *) fh; 105677b9900eSJiri Pirko struct nlattr *nest; 105777b9900eSJiri Pirko struct fl_flow_key *key, *mask; 105877b9900eSJiri Pirko 105977b9900eSJiri Pirko if (!f) 106077b9900eSJiri Pirko return skb->len; 106177b9900eSJiri Pirko 106277b9900eSJiri Pirko t->tcm_handle = f->handle; 106377b9900eSJiri Pirko 106477b9900eSJiri Pirko nest = nla_nest_start(skb, TCA_OPTIONS); 106577b9900eSJiri Pirko if (!nest) 106677b9900eSJiri Pirko goto nla_put_failure; 106777b9900eSJiri Pirko 106877b9900eSJiri Pirko if (f->res.classid && 106977b9900eSJiri Pirko nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid)) 107077b9900eSJiri Pirko goto nla_put_failure; 107177b9900eSJiri Pirko 107277b9900eSJiri Pirko key = &f->key; 107377b9900eSJiri Pirko mask = &head->mask.key; 107477b9900eSJiri Pirko 107577b9900eSJiri Pirko if (mask->indev_ifindex) { 107677b9900eSJiri Pirko struct net_device *dev; 107777b9900eSJiri Pirko 107877b9900eSJiri Pirko dev = __dev_get_by_index(net, key->indev_ifindex); 107977b9900eSJiri Pirko if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name)) 108077b9900eSJiri Pirko goto nla_put_failure; 108177b9900eSJiri Pirko } 108277b9900eSJiri Pirko 108379685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 108410cbc684SAmir Vadai fl_hw_update_stats(tp, f); 108510cbc684SAmir Vadai 108677b9900eSJiri Pirko if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 108777b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 108877b9900eSJiri Pirko sizeof(key->eth.dst)) || 108977b9900eSJiri Pirko fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 109077b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 109177b9900eSJiri Pirko sizeof(key->eth.src)) || 109277b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE, 109377b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 109477b9900eSJiri Pirko sizeof(key->basic.n_proto))) 109577b9900eSJiri Pirko goto nla_put_failure; 10969399ae9aSHadar Hen Zion 10979399ae9aSHadar Hen Zion if (fl_dump_key_vlan(skb, &key->vlan, &mask->vlan)) 10989399ae9aSHadar Hen Zion goto nla_put_failure; 10999399ae9aSHadar Hen Zion 110077b9900eSJiri Pirko if ((key->basic.n_proto == htons(ETH_P_IP) || 110177b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) && 110277b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 110377b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 110477b9900eSJiri Pirko sizeof(key->basic.ip_proto))) 110577b9900eSJiri Pirko goto nla_put_failure; 110677b9900eSJiri Pirko 1107c3f83241STom Herbert if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 110877b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 110977b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 111077b9900eSJiri Pirko sizeof(key->ipv4.src)) || 111177b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 111277b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 111377b9900eSJiri Pirko sizeof(key->ipv4.dst)))) 111477b9900eSJiri Pirko goto nla_put_failure; 1115c3f83241STom Herbert else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 111677b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 111777b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 111877b9900eSJiri Pirko sizeof(key->ipv6.src)) || 111977b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 112077b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 112177b9900eSJiri Pirko sizeof(key->ipv6.dst)))) 112277b9900eSJiri Pirko goto nla_put_failure; 112377b9900eSJiri Pirko 112477b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP && 112577b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 1126aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 112777b9900eSJiri Pirko sizeof(key->tp.src)) || 112877b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 1129aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 113077b9900eSJiri Pirko sizeof(key->tp.dst)))) 113177b9900eSJiri Pirko goto nla_put_failure; 113277b9900eSJiri Pirko else if (key->basic.ip_proto == IPPROTO_UDP && 113377b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 1134aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 113577b9900eSJiri Pirko sizeof(key->tp.src)) || 113677b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 1137aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 113877b9900eSJiri Pirko sizeof(key->tp.dst)))) 113977b9900eSJiri Pirko goto nla_put_failure; 11405976c5f4SSimon Horman else if (key->basic.ip_proto == IPPROTO_SCTP && 11415976c5f4SSimon Horman (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 11425976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 11435976c5f4SSimon Horman sizeof(key->tp.src)) || 11445976c5f4SSimon Horman fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 11455976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 11465976c5f4SSimon Horman sizeof(key->tp.dst)))) 11475976c5f4SSimon Horman goto nla_put_failure; 11487b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IP) && 11497b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMP && 11507b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 11517b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type, 11527b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 11537b684884SSimon Horman sizeof(key->icmp.type)) || 11547b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 11557b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code, 11567b684884SSimon Horman TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 11577b684884SSimon Horman sizeof(key->icmp.code)))) 11587b684884SSimon Horman goto nla_put_failure; 11597b684884SSimon Horman else if (key->basic.n_proto == htons(ETH_P_IPV6) && 11607b684884SSimon Horman key->basic.ip_proto == IPPROTO_ICMPV6 && 11617b684884SSimon Horman (fl_dump_key_val(skb, &key->icmp.type, 11627b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type, 11637b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 11647b684884SSimon Horman sizeof(key->icmp.type)) || 11657b684884SSimon Horman fl_dump_key_val(skb, &key->icmp.code, 11667b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code, 11677b684884SSimon Horman TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 11687b684884SSimon Horman sizeof(key->icmp.code)))) 11697b684884SSimon Horman goto nla_put_failure; 117099d31326SSimon Horman else if ((key->basic.n_proto == htons(ETH_P_ARP) || 117199d31326SSimon Horman key->basic.n_proto == htons(ETH_P_RARP)) && 117299d31326SSimon Horman (fl_dump_key_val(skb, &key->arp.sip, 117399d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP, &mask->arp.sip, 117499d31326SSimon Horman TCA_FLOWER_KEY_ARP_SIP_MASK, 117599d31326SSimon Horman sizeof(key->arp.sip)) || 117699d31326SSimon Horman fl_dump_key_val(skb, &key->arp.tip, 117799d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP, &mask->arp.tip, 117899d31326SSimon Horman TCA_FLOWER_KEY_ARP_TIP_MASK, 117999d31326SSimon Horman sizeof(key->arp.tip)) || 118099d31326SSimon Horman fl_dump_key_val(skb, &key->arp.op, 118199d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP, &mask->arp.op, 118299d31326SSimon Horman TCA_FLOWER_KEY_ARP_OP_MASK, 118399d31326SSimon Horman sizeof(key->arp.op)) || 118499d31326SSimon Horman fl_dump_key_val(skb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 118599d31326SSimon Horman mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 118699d31326SSimon Horman sizeof(key->arp.sha)) || 118799d31326SSimon Horman fl_dump_key_val(skb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 118899d31326SSimon Horman mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 118999d31326SSimon Horman sizeof(key->arp.tha)))) 119099d31326SSimon Horman goto nla_put_failure; 119177b9900eSJiri Pirko 1192bc3103f1SAmir Vadai if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 1193bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv4.src, 1194bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src, 1195bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 1196bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)) || 1197bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv4.dst, 1198bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst, 1199bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 1200bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)))) 1201bc3103f1SAmir Vadai goto nla_put_failure; 1202bc3103f1SAmir Vadai else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 1203bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv6.src, 1204bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src, 1205bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 1206bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)) || 1207bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv6.dst, 1208bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 1209bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 1210bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 1211bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)))) 1212bc3103f1SAmir Vadai goto nla_put_failure; 1213bc3103f1SAmir Vadai 1214bc3103f1SAmir Vadai if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, 1215eb523f42SHadar Hen Zion &mask->enc_key_id, TCA_FLOWER_UNSPEC, 1216f4d997fdSHadar Hen Zion sizeof(key->enc_key_id)) || 1217f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.src, 1218f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 1219f4d997fdSHadar Hen Zion &mask->enc_tp.src, 1220f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 1221f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)) || 1222f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.dst, 1223f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 1224f4d997fdSHadar Hen Zion &mask->enc_tp.dst, 1225f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 1226f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst))) 1227bc3103f1SAmir Vadai goto nla_put_failure; 1228bc3103f1SAmir Vadai 1229faa3ffceSOr Gerlitz if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) 1230faa3ffceSOr Gerlitz goto nla_put_failure; 1231faa3ffceSOr Gerlitz 1232*749e6720SOr Gerlitz if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags)) 1233*749e6720SOr Gerlitz goto nla_put_failure; 1234e69985c6SAmir Vadai 123577b9900eSJiri Pirko if (tcf_exts_dump(skb, &f->exts)) 123677b9900eSJiri Pirko goto nla_put_failure; 123777b9900eSJiri Pirko 123877b9900eSJiri Pirko nla_nest_end(skb, nest); 123977b9900eSJiri Pirko 124077b9900eSJiri Pirko if (tcf_exts_dump_stats(skb, &f->exts) < 0) 124177b9900eSJiri Pirko goto nla_put_failure; 124277b9900eSJiri Pirko 124377b9900eSJiri Pirko return skb->len; 124477b9900eSJiri Pirko 124577b9900eSJiri Pirko nla_put_failure: 124677b9900eSJiri Pirko nla_nest_cancel(skb, nest); 124777b9900eSJiri Pirko return -1; 124877b9900eSJiri Pirko } 124977b9900eSJiri Pirko 125077b9900eSJiri Pirko static struct tcf_proto_ops cls_fl_ops __read_mostly = { 125177b9900eSJiri Pirko .kind = "flower", 125277b9900eSJiri Pirko .classify = fl_classify, 125377b9900eSJiri Pirko .init = fl_init, 125477b9900eSJiri Pirko .destroy = fl_destroy, 125577b9900eSJiri Pirko .get = fl_get, 125677b9900eSJiri Pirko .change = fl_change, 125777b9900eSJiri Pirko .delete = fl_delete, 125877b9900eSJiri Pirko .walk = fl_walk, 125977b9900eSJiri Pirko .dump = fl_dump, 126077b9900eSJiri Pirko .owner = THIS_MODULE, 126177b9900eSJiri Pirko }; 126277b9900eSJiri Pirko 126377b9900eSJiri Pirko static int __init cls_fl_init(void) 126477b9900eSJiri Pirko { 126577b9900eSJiri Pirko return register_tcf_proto_ops(&cls_fl_ops); 126677b9900eSJiri Pirko } 126777b9900eSJiri Pirko 126877b9900eSJiri Pirko static void __exit cls_fl_exit(void) 126977b9900eSJiri Pirko { 127077b9900eSJiri Pirko unregister_tcf_proto_ops(&cls_fl_ops); 127177b9900eSJiri Pirko } 127277b9900eSJiri Pirko 127377b9900eSJiri Pirko module_init(cls_fl_init); 127477b9900eSJiri Pirko module_exit(cls_fl_exit); 127577b9900eSJiri Pirko 127677b9900eSJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 127777b9900eSJiri Pirko MODULE_DESCRIPTION("Flower classifier"); 127877b9900eSJiri Pirko MODULE_LICENSE("GPL v2"); 1279