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; 42bc3103f1SAmir Vadai struct flow_dissector_key_keyid enc_key_id; 43bc3103f1SAmir Vadai union { 44bc3103f1SAmir Vadai struct flow_dissector_key_ipv4_addrs enc_ipv4; 45bc3103f1SAmir Vadai struct flow_dissector_key_ipv6_addrs enc_ipv6; 46bc3103f1SAmir Vadai }; 47f4d997fdSHadar Hen Zion struct flow_dissector_key_ports enc_tp; 4877b9900eSJiri Pirko } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ 4977b9900eSJiri Pirko 5077b9900eSJiri Pirko struct fl_flow_mask_range { 5177b9900eSJiri Pirko unsigned short int start; 5277b9900eSJiri Pirko unsigned short int end; 5377b9900eSJiri Pirko }; 5477b9900eSJiri Pirko 5577b9900eSJiri Pirko struct fl_flow_mask { 5677b9900eSJiri Pirko struct fl_flow_key key; 5777b9900eSJiri Pirko struct fl_flow_mask_range range; 5877b9900eSJiri Pirko struct rcu_head rcu; 5977b9900eSJiri Pirko }; 6077b9900eSJiri Pirko 6177b9900eSJiri Pirko struct cls_fl_head { 6277b9900eSJiri Pirko struct rhashtable ht; 6377b9900eSJiri Pirko struct fl_flow_mask mask; 6477b9900eSJiri Pirko struct flow_dissector dissector; 6577b9900eSJiri Pirko u32 hgen; 6677b9900eSJiri Pirko bool mask_assigned; 6777b9900eSJiri Pirko struct list_head filters; 6877b9900eSJiri Pirko struct rhashtable_params ht_params; 69d9363774SDaniel Borkmann union { 70d9363774SDaniel Borkmann struct work_struct work; 7177b9900eSJiri Pirko struct rcu_head rcu; 7277b9900eSJiri Pirko }; 73d9363774SDaniel Borkmann }; 7477b9900eSJiri Pirko 7577b9900eSJiri Pirko struct cls_fl_filter { 7677b9900eSJiri Pirko struct rhash_head ht_node; 7777b9900eSJiri Pirko struct fl_flow_key mkey; 7877b9900eSJiri Pirko struct tcf_exts exts; 7977b9900eSJiri Pirko struct tcf_result res; 8077b9900eSJiri Pirko struct fl_flow_key key; 8177b9900eSJiri Pirko struct list_head list; 8277b9900eSJiri Pirko u32 handle; 83e69985c6SAmir Vadai u32 flags; 8477b9900eSJiri Pirko struct rcu_head rcu; 857091d8c7SHadar Hen Zion struct tc_to_netdev tc; 867091d8c7SHadar Hen Zion struct net_device *hw_dev; 8777b9900eSJiri Pirko }; 8877b9900eSJiri Pirko 8977b9900eSJiri Pirko static unsigned short int fl_mask_range(const struct fl_flow_mask *mask) 9077b9900eSJiri Pirko { 9177b9900eSJiri Pirko return mask->range.end - mask->range.start; 9277b9900eSJiri Pirko } 9377b9900eSJiri Pirko 9477b9900eSJiri Pirko static void fl_mask_update_range(struct fl_flow_mask *mask) 9577b9900eSJiri Pirko { 9677b9900eSJiri Pirko const u8 *bytes = (const u8 *) &mask->key; 9777b9900eSJiri Pirko size_t size = sizeof(mask->key); 9877b9900eSJiri Pirko size_t i, first = 0, last = size - 1; 9977b9900eSJiri Pirko 10077b9900eSJiri Pirko for (i = 0; i < sizeof(mask->key); i++) { 10177b9900eSJiri Pirko if (bytes[i]) { 10277b9900eSJiri Pirko if (!first && i) 10377b9900eSJiri Pirko first = i; 10477b9900eSJiri Pirko last = i; 10577b9900eSJiri Pirko } 10677b9900eSJiri Pirko } 10777b9900eSJiri Pirko mask->range.start = rounddown(first, sizeof(long)); 10877b9900eSJiri Pirko mask->range.end = roundup(last + 1, sizeof(long)); 10977b9900eSJiri Pirko } 11077b9900eSJiri Pirko 11177b9900eSJiri Pirko static void *fl_key_get_start(struct fl_flow_key *key, 11277b9900eSJiri Pirko const struct fl_flow_mask *mask) 11377b9900eSJiri Pirko { 11477b9900eSJiri Pirko return (u8 *) key + mask->range.start; 11577b9900eSJiri Pirko } 11677b9900eSJiri Pirko 11777b9900eSJiri Pirko static void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key, 11877b9900eSJiri Pirko struct fl_flow_mask *mask) 11977b9900eSJiri Pirko { 12077b9900eSJiri Pirko const long *lkey = fl_key_get_start(key, mask); 12177b9900eSJiri Pirko const long *lmask = fl_key_get_start(&mask->key, mask); 12277b9900eSJiri Pirko long *lmkey = fl_key_get_start(mkey, mask); 12377b9900eSJiri Pirko int i; 12477b9900eSJiri Pirko 12577b9900eSJiri Pirko for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) 12677b9900eSJiri Pirko *lmkey++ = *lkey++ & *lmask++; 12777b9900eSJiri Pirko } 12877b9900eSJiri Pirko 12977b9900eSJiri Pirko static void fl_clear_masked_range(struct fl_flow_key *key, 13077b9900eSJiri Pirko struct fl_flow_mask *mask) 13177b9900eSJiri Pirko { 13277b9900eSJiri Pirko memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask)); 13377b9900eSJiri Pirko } 13477b9900eSJiri Pirko 13577b9900eSJiri Pirko static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp, 13677b9900eSJiri Pirko struct tcf_result *res) 13777b9900eSJiri Pirko { 13877b9900eSJiri Pirko struct cls_fl_head *head = rcu_dereference_bh(tp->root); 13977b9900eSJiri Pirko struct cls_fl_filter *f; 14077b9900eSJiri Pirko struct fl_flow_key skb_key; 14177b9900eSJiri Pirko struct fl_flow_key skb_mkey; 142bc3103f1SAmir Vadai struct ip_tunnel_info *info; 14377b9900eSJiri Pirko 144e69985c6SAmir Vadai if (!atomic_read(&head->ht.nelems)) 145e69985c6SAmir Vadai return -1; 146e69985c6SAmir Vadai 14777b9900eSJiri Pirko fl_clear_masked_range(&skb_key, &head->mask); 148bc3103f1SAmir Vadai 149bc3103f1SAmir Vadai info = skb_tunnel_info(skb); 150bc3103f1SAmir Vadai if (info) { 151bc3103f1SAmir Vadai struct ip_tunnel_key *key = &info->key; 152bc3103f1SAmir Vadai 153bc3103f1SAmir Vadai switch (ip_tunnel_info_af(info)) { 154bc3103f1SAmir Vadai case AF_INET: 155bc3103f1SAmir Vadai skb_key.enc_ipv4.src = key->u.ipv4.src; 156bc3103f1SAmir Vadai skb_key.enc_ipv4.dst = key->u.ipv4.dst; 157bc3103f1SAmir Vadai break; 158bc3103f1SAmir Vadai case AF_INET6: 159bc3103f1SAmir Vadai skb_key.enc_ipv6.src = key->u.ipv6.src; 160bc3103f1SAmir Vadai skb_key.enc_ipv6.dst = key->u.ipv6.dst; 161bc3103f1SAmir Vadai break; 162bc3103f1SAmir Vadai } 163bc3103f1SAmir Vadai 164bc3103f1SAmir Vadai skb_key.enc_key_id.keyid = tunnel_id_to_key32(key->tun_id); 165f4d997fdSHadar Hen Zion skb_key.enc_tp.src = key->tp_src; 166f4d997fdSHadar Hen Zion skb_key.enc_tp.dst = key->tp_dst; 167bc3103f1SAmir Vadai } 168bc3103f1SAmir Vadai 16977b9900eSJiri Pirko skb_key.indev_ifindex = skb->skb_iif; 17077b9900eSJiri Pirko /* skb_flow_dissect() does not set n_proto in case an unknown protocol, 17177b9900eSJiri Pirko * so do it rather here. 17277b9900eSJiri Pirko */ 17377b9900eSJiri Pirko skb_key.basic.n_proto = skb->protocol; 174cd79a238STom Herbert skb_flow_dissect(skb, &head->dissector, &skb_key, 0); 17577b9900eSJiri Pirko 17677b9900eSJiri Pirko fl_set_masked_key(&skb_mkey, &skb_key, &head->mask); 17777b9900eSJiri Pirko 17877b9900eSJiri Pirko f = rhashtable_lookup_fast(&head->ht, 17977b9900eSJiri Pirko fl_key_get_start(&skb_mkey, &head->mask), 18077b9900eSJiri Pirko head->ht_params); 181e8eb36cdSAmir Vadai if (f && !tc_skip_sw(f->flags)) { 18277b9900eSJiri Pirko *res = f->res; 18377b9900eSJiri Pirko return tcf_exts_exec(skb, &f->exts, res); 18477b9900eSJiri Pirko } 18577b9900eSJiri Pirko return -1; 18677b9900eSJiri Pirko } 18777b9900eSJiri Pirko 18877b9900eSJiri Pirko static int fl_init(struct tcf_proto *tp) 18977b9900eSJiri Pirko { 19077b9900eSJiri Pirko struct cls_fl_head *head; 19177b9900eSJiri Pirko 19277b9900eSJiri Pirko head = kzalloc(sizeof(*head), GFP_KERNEL); 19377b9900eSJiri Pirko if (!head) 19477b9900eSJiri Pirko return -ENOBUFS; 19577b9900eSJiri Pirko 19677b9900eSJiri Pirko INIT_LIST_HEAD_RCU(&head->filters); 19777b9900eSJiri Pirko rcu_assign_pointer(tp->root, head); 19877b9900eSJiri Pirko 19977b9900eSJiri Pirko return 0; 20077b9900eSJiri Pirko } 20177b9900eSJiri Pirko 20277b9900eSJiri Pirko static void fl_destroy_filter(struct rcu_head *head) 20377b9900eSJiri Pirko { 20477b9900eSJiri Pirko struct cls_fl_filter *f = container_of(head, struct cls_fl_filter, rcu); 20577b9900eSJiri Pirko 20677b9900eSJiri Pirko tcf_exts_destroy(&f->exts); 20777b9900eSJiri Pirko kfree(f); 20877b9900eSJiri Pirko } 20977b9900eSJiri Pirko 2103036dab6SHadar Hen Zion static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) 2115b33f488SAmir Vadai { 2125b33f488SAmir Vadai struct tc_cls_flower_offload offload = {0}; 2137091d8c7SHadar Hen Zion struct net_device *dev = f->hw_dev; 2147091d8c7SHadar Hen Zion struct tc_to_netdev *tc = &f->tc; 2155b33f488SAmir Vadai 21679685219SHadar Hen Zion if (!tc_can_offload(dev, tp)) 2175b33f488SAmir Vadai return; 2185b33f488SAmir Vadai 2195b33f488SAmir Vadai offload.command = TC_CLSFLOWER_DESTROY; 2203036dab6SHadar Hen Zion offload.cookie = (unsigned long)f; 2215b33f488SAmir Vadai 2227091d8c7SHadar Hen Zion tc->type = TC_SETUP_CLSFLOWER; 2237091d8c7SHadar Hen Zion tc->cls_flower = &offload; 2245b33f488SAmir Vadai 2257091d8c7SHadar Hen Zion dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, tc); 2265b33f488SAmir Vadai } 2275b33f488SAmir Vadai 228e8eb36cdSAmir Vadai static int fl_hw_replace_filter(struct tcf_proto *tp, 2295b33f488SAmir Vadai struct flow_dissector *dissector, 2305b33f488SAmir Vadai struct fl_flow_key *mask, 2313036dab6SHadar Hen Zion struct cls_fl_filter *f) 2325b33f488SAmir Vadai { 2335b33f488SAmir Vadai struct net_device *dev = tp->q->dev_queue->dev; 2345b33f488SAmir Vadai struct tc_cls_flower_offload offload = {0}; 2357091d8c7SHadar Hen Zion struct tc_to_netdev *tc = &f->tc; 236e8eb36cdSAmir Vadai int err; 2375b33f488SAmir Vadai 2387091d8c7SHadar Hen Zion if (!tc_can_offload(dev, tp)) { 2397091d8c7SHadar Hen Zion if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev)) 2403036dab6SHadar Hen Zion return tc_skip_sw(f->flags) ? -EINVAL : 0; 2417091d8c7SHadar Hen Zion dev = f->hw_dev; 2427091d8c7SHadar Hen Zion tc->egress_dev = true; 2437091d8c7SHadar Hen Zion } else { 2447091d8c7SHadar Hen Zion f->hw_dev = dev; 2457091d8c7SHadar Hen Zion } 2465b33f488SAmir Vadai 2475b33f488SAmir Vadai offload.command = TC_CLSFLOWER_REPLACE; 2483036dab6SHadar Hen Zion offload.cookie = (unsigned long)f; 2495b33f488SAmir Vadai offload.dissector = dissector; 2505b33f488SAmir Vadai offload.mask = mask; 2513036dab6SHadar Hen Zion offload.key = &f->key; 2523036dab6SHadar Hen Zion offload.exts = &f->exts; 2535b33f488SAmir Vadai 2547091d8c7SHadar Hen Zion tc->type = TC_SETUP_CLSFLOWER; 2557091d8c7SHadar Hen Zion tc->cls_flower = &offload; 2565b33f488SAmir Vadai 2575a7a5555SJamal Hadi Salim err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, 2587091d8c7SHadar Hen Zion tc); 259e8eb36cdSAmir Vadai 2603036dab6SHadar Hen Zion if (tc_skip_sw(f->flags)) 261e8eb36cdSAmir Vadai return err; 262e8eb36cdSAmir Vadai return 0; 2635b33f488SAmir Vadai } 2645b33f488SAmir Vadai 26510cbc684SAmir Vadai static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) 26610cbc684SAmir Vadai { 26710cbc684SAmir Vadai struct tc_cls_flower_offload offload = {0}; 2687091d8c7SHadar Hen Zion struct net_device *dev = f->hw_dev; 2697091d8c7SHadar Hen Zion struct tc_to_netdev *tc = &f->tc; 27010cbc684SAmir Vadai 27179685219SHadar Hen Zion if (!tc_can_offload(dev, tp)) 27210cbc684SAmir Vadai return; 27310cbc684SAmir Vadai 27410cbc684SAmir Vadai offload.command = TC_CLSFLOWER_STATS; 27510cbc684SAmir Vadai offload.cookie = (unsigned long)f; 27610cbc684SAmir Vadai offload.exts = &f->exts; 27710cbc684SAmir Vadai 2787091d8c7SHadar Hen Zion tc->type = TC_SETUP_CLSFLOWER; 2797091d8c7SHadar Hen Zion tc->cls_flower = &offload; 28010cbc684SAmir Vadai 2817091d8c7SHadar Hen Zion dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->protocol, tc); 28210cbc684SAmir Vadai } 28310cbc684SAmir Vadai 28413fa876eSRoi Dayan static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) 28513fa876eSRoi Dayan { 28613fa876eSRoi Dayan list_del_rcu(&f->list); 28779685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 2883036dab6SHadar Hen Zion fl_hw_destroy_filter(tp, f); 28913fa876eSRoi Dayan tcf_unbind_filter(tp, &f->res); 29013fa876eSRoi Dayan call_rcu(&f->rcu, fl_destroy_filter); 29113fa876eSRoi Dayan } 29213fa876eSRoi Dayan 293d9363774SDaniel Borkmann static void fl_destroy_sleepable(struct work_struct *work) 294d9363774SDaniel Borkmann { 295d9363774SDaniel Borkmann struct cls_fl_head *head = container_of(work, struct cls_fl_head, 296d9363774SDaniel Borkmann work); 297d9363774SDaniel Borkmann if (head->mask_assigned) 298d9363774SDaniel Borkmann rhashtable_destroy(&head->ht); 299d9363774SDaniel Borkmann kfree(head); 300d9363774SDaniel Borkmann module_put(THIS_MODULE); 301d9363774SDaniel Borkmann } 302d9363774SDaniel Borkmann 303d9363774SDaniel Borkmann static void fl_destroy_rcu(struct rcu_head *rcu) 304d9363774SDaniel Borkmann { 305d9363774SDaniel Borkmann struct cls_fl_head *head = container_of(rcu, struct cls_fl_head, rcu); 306d9363774SDaniel Borkmann 307d9363774SDaniel Borkmann INIT_WORK(&head->work, fl_destroy_sleepable); 308d9363774SDaniel Borkmann schedule_work(&head->work); 309d9363774SDaniel Borkmann } 310d9363774SDaniel Borkmann 31177b9900eSJiri Pirko static bool fl_destroy(struct tcf_proto *tp, bool force) 31277b9900eSJiri Pirko { 31377b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 31477b9900eSJiri Pirko struct cls_fl_filter *f, *next; 31577b9900eSJiri Pirko 31677b9900eSJiri Pirko if (!force && !list_empty(&head->filters)) 31777b9900eSJiri Pirko return false; 31877b9900eSJiri Pirko 31913fa876eSRoi Dayan list_for_each_entry_safe(f, next, &head->filters, list) 32013fa876eSRoi Dayan __fl_delete(tp, f); 321d9363774SDaniel Borkmann 322d9363774SDaniel Borkmann __module_get(THIS_MODULE); 323d9363774SDaniel Borkmann call_rcu(&head->rcu, fl_destroy_rcu); 324*2745529aSDavid S. Miller 32577b9900eSJiri Pirko return true; 32677b9900eSJiri Pirko } 32777b9900eSJiri Pirko 32877b9900eSJiri Pirko static unsigned long fl_get(struct tcf_proto *tp, u32 handle) 32977b9900eSJiri Pirko { 33077b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 33177b9900eSJiri Pirko struct cls_fl_filter *f; 33277b9900eSJiri Pirko 33377b9900eSJiri Pirko list_for_each_entry(f, &head->filters, list) 33477b9900eSJiri Pirko if (f->handle == handle) 33577b9900eSJiri Pirko return (unsigned long) f; 33677b9900eSJiri Pirko return 0; 33777b9900eSJiri Pirko } 33877b9900eSJiri Pirko 33977b9900eSJiri Pirko static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { 34077b9900eSJiri Pirko [TCA_FLOWER_UNSPEC] = { .type = NLA_UNSPEC }, 34177b9900eSJiri Pirko [TCA_FLOWER_CLASSID] = { .type = NLA_U32 }, 34277b9900eSJiri Pirko [TCA_FLOWER_INDEV] = { .type = NLA_STRING, 34377b9900eSJiri Pirko .len = IFNAMSIZ }, 34477b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST] = { .len = ETH_ALEN }, 34577b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_DST_MASK] = { .len = ETH_ALEN }, 34677b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC] = { .len = ETH_ALEN }, 34777b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .len = ETH_ALEN }, 34877b9900eSJiri Pirko [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, 34977b9900eSJiri Pirko [TCA_FLOWER_KEY_IP_PROTO] = { .type = NLA_U8 }, 35077b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, 35177b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, 35277b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, 35377b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, 35477b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 35577b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 35677b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 35777b9900eSJiri Pirko [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 35877b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_SRC] = { .type = NLA_U16 }, 35977b9900eSJiri Pirko [TCA_FLOWER_KEY_TCP_DST] = { .type = NLA_U16 }, 360b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_SRC] = { .type = NLA_U16 }, 361b175c3a4SJamal Hadi Salim [TCA_FLOWER_KEY_UDP_DST] = { .type = NLA_U16 }, 3629399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, 3639399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, 3649399ae9aSHadar Hen Zion [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, 365bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, 366bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, 367bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 }, 368bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, 369bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 }, 370bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 371bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 372bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 373bc3103f1SAmir Vadai [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 374aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NLA_U16 }, 375aa72d708SOr Gerlitz [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, 376aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, 377aa72d708SOr Gerlitz [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, 3785976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, 3795976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, 3805976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, 3815976c5f4SSimon Horman [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, 382f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, 383f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, 384f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, 385f4d997fdSHadar Hen Zion [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, 38677b9900eSJiri Pirko }; 38777b9900eSJiri Pirko 38877b9900eSJiri Pirko static void fl_set_key_val(struct nlattr **tb, 38977b9900eSJiri Pirko void *val, int val_type, 39077b9900eSJiri Pirko void *mask, int mask_type, int len) 39177b9900eSJiri Pirko { 39277b9900eSJiri Pirko if (!tb[val_type]) 39377b9900eSJiri Pirko return; 39477b9900eSJiri Pirko memcpy(val, nla_data(tb[val_type]), len); 39577b9900eSJiri Pirko if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type]) 39677b9900eSJiri Pirko memset(mask, 0xff, len); 39777b9900eSJiri Pirko else 39877b9900eSJiri Pirko memcpy(mask, nla_data(tb[mask_type]), len); 39977b9900eSJiri Pirko } 40077b9900eSJiri Pirko 4019399ae9aSHadar Hen Zion static void fl_set_key_vlan(struct nlattr **tb, 4029399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_val, 4039399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *key_mask) 4049399ae9aSHadar Hen Zion { 4059399ae9aSHadar Hen Zion #define VLAN_PRIORITY_MASK 0x7 4069399ae9aSHadar Hen Zion 4079399ae9aSHadar Hen Zion if (tb[TCA_FLOWER_KEY_VLAN_ID]) { 4089399ae9aSHadar Hen Zion key_val->vlan_id = 4099399ae9aSHadar Hen Zion nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ID]) & VLAN_VID_MASK; 4109399ae9aSHadar Hen Zion key_mask->vlan_id = VLAN_VID_MASK; 4119399ae9aSHadar Hen Zion } 4129399ae9aSHadar Hen Zion if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { 4139399ae9aSHadar Hen Zion key_val->vlan_priority = 4149399ae9aSHadar Hen Zion nla_get_u8(tb[TCA_FLOWER_KEY_VLAN_PRIO]) & 4159399ae9aSHadar Hen Zion VLAN_PRIORITY_MASK; 4169399ae9aSHadar Hen Zion key_mask->vlan_priority = VLAN_PRIORITY_MASK; 4179399ae9aSHadar Hen Zion } 4189399ae9aSHadar Hen Zion } 4199399ae9aSHadar Hen Zion 42077b9900eSJiri Pirko static int fl_set_key(struct net *net, struct nlattr **tb, 42177b9900eSJiri Pirko struct fl_flow_key *key, struct fl_flow_key *mask) 42277b9900eSJiri Pirko { 4239399ae9aSHadar Hen Zion __be16 ethertype; 424dd3aa3b5SBrian Haley #ifdef CONFIG_NET_CLS_IND 42577b9900eSJiri Pirko if (tb[TCA_FLOWER_INDEV]) { 426dd3aa3b5SBrian Haley int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV]); 42777b9900eSJiri Pirko if (err < 0) 42877b9900eSJiri Pirko return err; 42977b9900eSJiri Pirko key->indev_ifindex = err; 43077b9900eSJiri Pirko mask->indev_ifindex = 0xffffffff; 43177b9900eSJiri Pirko } 432dd3aa3b5SBrian Haley #endif 43377b9900eSJiri Pirko 43477b9900eSJiri Pirko fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 43577b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 43677b9900eSJiri Pirko sizeof(key->eth.dst)); 43777b9900eSJiri Pirko fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 43877b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 43977b9900eSJiri Pirko sizeof(key->eth.src)); 44066530bdfSJamal Hadi Salim 4410b498a52SArnd Bergmann if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { 4429399ae9aSHadar Hen Zion ethertype = nla_get_be16(tb[TCA_FLOWER_KEY_ETH_TYPE]); 4439399ae9aSHadar Hen Zion 4449399ae9aSHadar Hen Zion if (ethertype == htons(ETH_P_8021Q)) { 4459399ae9aSHadar Hen Zion fl_set_key_vlan(tb, &key->vlan, &mask->vlan); 4469399ae9aSHadar Hen Zion fl_set_key_val(tb, &key->basic.n_proto, 4479399ae9aSHadar Hen Zion TCA_FLOWER_KEY_VLAN_ETH_TYPE, 44877b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 44977b9900eSJiri Pirko sizeof(key->basic.n_proto)); 4509399ae9aSHadar Hen Zion } else { 4519399ae9aSHadar Hen Zion key->basic.n_proto = ethertype; 4529399ae9aSHadar Hen Zion mask->basic.n_proto = cpu_to_be16(~0); 4539399ae9aSHadar Hen Zion } 4540b498a52SArnd Bergmann } 45566530bdfSJamal Hadi Salim 45677b9900eSJiri Pirko if (key->basic.n_proto == htons(ETH_P_IP) || 45777b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) { 45877b9900eSJiri Pirko fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 45977b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 46077b9900eSJiri Pirko sizeof(key->basic.ip_proto)); 46177b9900eSJiri Pirko } 46266530bdfSJamal Hadi Salim 46366530bdfSJamal Hadi Salim if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) { 46466530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 46577b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 46677b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 46777b9900eSJiri Pirko sizeof(key->ipv4.src)); 46877b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 46977b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 47077b9900eSJiri Pirko sizeof(key->ipv4.dst)); 47166530bdfSJamal Hadi Salim } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) { 47266530bdfSJamal Hadi Salim key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 47377b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 47477b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 47577b9900eSJiri Pirko sizeof(key->ipv6.src)); 47677b9900eSJiri Pirko fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 47777b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 47877b9900eSJiri Pirko sizeof(key->ipv6.dst)); 47977b9900eSJiri Pirko } 48066530bdfSJamal Hadi Salim 48177b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP) { 48277b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 483aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 48477b9900eSJiri Pirko sizeof(key->tp.src)); 48577b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 486aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 48777b9900eSJiri Pirko sizeof(key->tp.dst)); 48877b9900eSJiri Pirko } else if (key->basic.ip_proto == IPPROTO_UDP) { 48977b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 490aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 49177b9900eSJiri Pirko sizeof(key->tp.src)); 49277b9900eSJiri Pirko fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 493aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 49477b9900eSJiri Pirko sizeof(key->tp.dst)); 4955976c5f4SSimon Horman } else if (key->basic.ip_proto == IPPROTO_SCTP) { 4965976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 4975976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 4985976c5f4SSimon Horman sizeof(key->tp.src)); 4995976c5f4SSimon Horman fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 5005976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 5015976c5f4SSimon Horman sizeof(key->tp.dst)); 50277b9900eSJiri Pirko } 50377b9900eSJiri Pirko 504bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || 505bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) { 506bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 507bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.src, 508bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, 509bc3103f1SAmir Vadai &mask->enc_ipv4.src, 510bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 511bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)); 512bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv4.dst, 513bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, 514bc3103f1SAmir Vadai &mask->enc_ipv4.dst, 515bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 516bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)); 517bc3103f1SAmir Vadai } 518bc3103f1SAmir Vadai 519bc3103f1SAmir Vadai if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] || 520bc3103f1SAmir Vadai tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) { 521bc3103f1SAmir Vadai key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 522bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.src, 523bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, 524bc3103f1SAmir Vadai &mask->enc_ipv6.src, 525bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 526bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)); 527bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_ipv6.dst, 528bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 529bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 530bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 531bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)); 532bc3103f1SAmir Vadai } 533bc3103f1SAmir Vadai 534bc3103f1SAmir Vadai fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID, 535eb523f42SHadar Hen Zion &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, 536bc3103f1SAmir Vadai sizeof(key->enc_key_id.keyid)); 537bc3103f1SAmir Vadai 538f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 539f4d997fdSHadar Hen Zion &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 540f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)); 541f4d997fdSHadar Hen Zion 542f4d997fdSHadar Hen Zion fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 543f4d997fdSHadar Hen Zion &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 544f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst)); 545f4d997fdSHadar Hen Zion 54677b9900eSJiri Pirko return 0; 54777b9900eSJiri Pirko } 54877b9900eSJiri Pirko 54977b9900eSJiri Pirko static bool fl_mask_eq(struct fl_flow_mask *mask1, 55077b9900eSJiri Pirko struct fl_flow_mask *mask2) 55177b9900eSJiri Pirko { 55277b9900eSJiri Pirko const long *lmask1 = fl_key_get_start(&mask1->key, mask1); 55377b9900eSJiri Pirko const long *lmask2 = fl_key_get_start(&mask2->key, mask2); 55477b9900eSJiri Pirko 55577b9900eSJiri Pirko return !memcmp(&mask1->range, &mask2->range, sizeof(mask1->range)) && 55677b9900eSJiri Pirko !memcmp(lmask1, lmask2, fl_mask_range(mask1)); 55777b9900eSJiri Pirko } 55877b9900eSJiri Pirko 55977b9900eSJiri Pirko static const struct rhashtable_params fl_ht_params = { 56077b9900eSJiri Pirko .key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */ 56177b9900eSJiri Pirko .head_offset = offsetof(struct cls_fl_filter, ht_node), 56277b9900eSJiri Pirko .automatic_shrinking = true, 56377b9900eSJiri Pirko }; 56477b9900eSJiri Pirko 56577b9900eSJiri Pirko static int fl_init_hashtable(struct cls_fl_head *head, 56677b9900eSJiri Pirko struct fl_flow_mask *mask) 56777b9900eSJiri Pirko { 56877b9900eSJiri Pirko head->ht_params = fl_ht_params; 56977b9900eSJiri Pirko head->ht_params.key_len = fl_mask_range(mask); 57077b9900eSJiri Pirko head->ht_params.key_offset += mask->range.start; 57177b9900eSJiri Pirko 57277b9900eSJiri Pirko return rhashtable_init(&head->ht, &head->ht_params); 57377b9900eSJiri Pirko } 57477b9900eSJiri Pirko 57577b9900eSJiri Pirko #define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) 57677b9900eSJiri Pirko #define FL_KEY_MEMBER_SIZE(member) (sizeof(((struct fl_flow_key *) 0)->member)) 57777b9900eSJiri Pirko 578339ba878SHadar Hen Zion #define FL_KEY_IS_MASKED(mask, member) \ 579339ba878SHadar Hen Zion memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ 580339ba878SHadar Hen Zion 0, FL_KEY_MEMBER_SIZE(member)) \ 58177b9900eSJiri Pirko 58277b9900eSJiri Pirko #define FL_KEY_SET(keys, cnt, id, member) \ 58377b9900eSJiri Pirko do { \ 58477b9900eSJiri Pirko keys[cnt].key_id = id; \ 58577b9900eSJiri Pirko keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member); \ 58677b9900eSJiri Pirko cnt++; \ 58777b9900eSJiri Pirko } while(0); 58877b9900eSJiri Pirko 589339ba878SHadar Hen Zion #define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member) \ 59077b9900eSJiri Pirko do { \ 591339ba878SHadar Hen Zion if (FL_KEY_IS_MASKED(mask, member)) \ 59277b9900eSJiri Pirko FL_KEY_SET(keys, cnt, id, member); \ 59377b9900eSJiri Pirko } while(0); 59477b9900eSJiri Pirko 59577b9900eSJiri Pirko static void fl_init_dissector(struct cls_fl_head *head, 59677b9900eSJiri Pirko struct fl_flow_mask *mask) 59777b9900eSJiri Pirko { 59877b9900eSJiri Pirko struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX]; 59977b9900eSJiri Pirko size_t cnt = 0; 60077b9900eSJiri Pirko 60142aecaa9STom Herbert FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control); 60277b9900eSJiri Pirko FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic); 603339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 60477b9900eSJiri Pirko FLOW_DISSECTOR_KEY_ETH_ADDRS, eth); 605339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 60677b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); 607339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 60877b9900eSJiri Pirko FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); 609339ba878SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 61077b9900eSJiri Pirko FLOW_DISSECTOR_KEY_PORTS, tp); 6119399ae9aSHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 6129399ae9aSHadar Hen Zion FLOW_DISSECTOR_KEY_VLAN, vlan); 613519d1052SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 614519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); 615519d1052SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 616519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); 617519d1052SHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 618519d1052SHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); 619519d1052SHadar Hen Zion if (FL_KEY_IS_MASKED(&mask->key, enc_ipv4) || 620519d1052SHadar Hen Zion FL_KEY_IS_MASKED(&mask->key, enc_ipv6)) 621519d1052SHadar Hen Zion FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, 622519d1052SHadar Hen Zion enc_control); 623f4d997fdSHadar Hen Zion FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, 624f4d997fdSHadar Hen Zion FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); 62577b9900eSJiri Pirko 62677b9900eSJiri Pirko skb_flow_dissector_init(&head->dissector, keys, cnt); 62777b9900eSJiri Pirko } 62877b9900eSJiri Pirko 62977b9900eSJiri Pirko static int fl_check_assign_mask(struct cls_fl_head *head, 63077b9900eSJiri Pirko struct fl_flow_mask *mask) 63177b9900eSJiri Pirko { 63277b9900eSJiri Pirko int err; 63377b9900eSJiri Pirko 63477b9900eSJiri Pirko if (head->mask_assigned) { 63577b9900eSJiri Pirko if (!fl_mask_eq(&head->mask, mask)) 63677b9900eSJiri Pirko return -EINVAL; 63777b9900eSJiri Pirko else 63877b9900eSJiri Pirko return 0; 63977b9900eSJiri Pirko } 64077b9900eSJiri Pirko 64177b9900eSJiri Pirko /* Mask is not assigned yet. So assign it and init hashtable 64277b9900eSJiri Pirko * according to that. 64377b9900eSJiri Pirko */ 64477b9900eSJiri Pirko err = fl_init_hashtable(head, mask); 64577b9900eSJiri Pirko if (err) 64677b9900eSJiri Pirko return err; 64777b9900eSJiri Pirko memcpy(&head->mask, mask, sizeof(head->mask)); 64877b9900eSJiri Pirko head->mask_assigned = true; 64977b9900eSJiri Pirko 65077b9900eSJiri Pirko fl_init_dissector(head, mask); 65177b9900eSJiri Pirko 65277b9900eSJiri Pirko return 0; 65377b9900eSJiri Pirko } 65477b9900eSJiri Pirko 65577b9900eSJiri Pirko static int fl_set_parms(struct net *net, struct tcf_proto *tp, 65677b9900eSJiri Pirko struct cls_fl_filter *f, struct fl_flow_mask *mask, 65777b9900eSJiri Pirko unsigned long base, struct nlattr **tb, 65877b9900eSJiri Pirko struct nlattr *est, bool ovr) 65977b9900eSJiri Pirko { 66077b9900eSJiri Pirko struct tcf_exts e; 66177b9900eSJiri Pirko int err; 66277b9900eSJiri Pirko 663b9a24bb7SWANG Cong err = tcf_exts_init(&e, TCA_FLOWER_ACT, 0); 66477b9900eSJiri Pirko if (err < 0) 66577b9900eSJiri Pirko return err; 666b9a24bb7SWANG Cong err = tcf_exts_validate(net, tp, tb, est, &e, ovr); 667b9a24bb7SWANG Cong if (err < 0) 668b9a24bb7SWANG Cong goto errout; 66977b9900eSJiri Pirko 67077b9900eSJiri Pirko if (tb[TCA_FLOWER_CLASSID]) { 67177b9900eSJiri Pirko f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); 67277b9900eSJiri Pirko tcf_bind_filter(tp, &f->res, base); 67377b9900eSJiri Pirko } 67477b9900eSJiri Pirko 67577b9900eSJiri Pirko err = fl_set_key(net, tb, &f->key, &mask->key); 67677b9900eSJiri Pirko if (err) 67777b9900eSJiri Pirko goto errout; 67877b9900eSJiri Pirko 67977b9900eSJiri Pirko fl_mask_update_range(mask); 68077b9900eSJiri Pirko fl_set_masked_key(&f->mkey, &f->key, mask); 68177b9900eSJiri Pirko 68277b9900eSJiri Pirko tcf_exts_change(tp, &f->exts, &e); 68377b9900eSJiri Pirko 68477b9900eSJiri Pirko return 0; 68577b9900eSJiri Pirko errout: 68677b9900eSJiri Pirko tcf_exts_destroy(&e); 68777b9900eSJiri Pirko return err; 68877b9900eSJiri Pirko } 68977b9900eSJiri Pirko 69077b9900eSJiri Pirko static u32 fl_grab_new_handle(struct tcf_proto *tp, 69177b9900eSJiri Pirko struct cls_fl_head *head) 69277b9900eSJiri Pirko { 69377b9900eSJiri Pirko unsigned int i = 0x80000000; 69477b9900eSJiri Pirko u32 handle; 69577b9900eSJiri Pirko 69677b9900eSJiri Pirko do { 69777b9900eSJiri Pirko if (++head->hgen == 0x7FFFFFFF) 69877b9900eSJiri Pirko head->hgen = 1; 69977b9900eSJiri Pirko } while (--i > 0 && fl_get(tp, head->hgen)); 70077b9900eSJiri Pirko 70177b9900eSJiri Pirko if (unlikely(i == 0)) { 70277b9900eSJiri Pirko pr_err("Insufficient number of handles\n"); 70377b9900eSJiri Pirko handle = 0; 70477b9900eSJiri Pirko } else { 70577b9900eSJiri Pirko handle = head->hgen; 70677b9900eSJiri Pirko } 70777b9900eSJiri Pirko 70877b9900eSJiri Pirko return handle; 70977b9900eSJiri Pirko } 71077b9900eSJiri Pirko 71177b9900eSJiri Pirko static int fl_change(struct net *net, struct sk_buff *in_skb, 71277b9900eSJiri Pirko struct tcf_proto *tp, unsigned long base, 71377b9900eSJiri Pirko u32 handle, struct nlattr **tca, 71477b9900eSJiri Pirko unsigned long *arg, bool ovr) 71577b9900eSJiri Pirko { 71677b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 71777b9900eSJiri Pirko struct cls_fl_filter *fold = (struct cls_fl_filter *) *arg; 71877b9900eSJiri Pirko struct cls_fl_filter *fnew; 71977b9900eSJiri Pirko struct nlattr *tb[TCA_FLOWER_MAX + 1]; 72077b9900eSJiri Pirko struct fl_flow_mask mask = {}; 72177b9900eSJiri Pirko int err; 72277b9900eSJiri Pirko 72377b9900eSJiri Pirko if (!tca[TCA_OPTIONS]) 72477b9900eSJiri Pirko return -EINVAL; 72577b9900eSJiri Pirko 72677b9900eSJiri Pirko err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS], fl_policy); 72777b9900eSJiri Pirko if (err < 0) 72877b9900eSJiri Pirko return err; 72977b9900eSJiri Pirko 73077b9900eSJiri Pirko if (fold && handle && fold->handle != handle) 73177b9900eSJiri Pirko return -EINVAL; 73277b9900eSJiri Pirko 73377b9900eSJiri Pirko fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 73477b9900eSJiri Pirko if (!fnew) 73577b9900eSJiri Pirko return -ENOBUFS; 73677b9900eSJiri Pirko 737b9a24bb7SWANG Cong err = tcf_exts_init(&fnew->exts, TCA_FLOWER_ACT, 0); 738b9a24bb7SWANG Cong if (err < 0) 739b9a24bb7SWANG Cong goto errout; 74077b9900eSJiri Pirko 74177b9900eSJiri Pirko if (!handle) { 74277b9900eSJiri Pirko handle = fl_grab_new_handle(tp, head); 74377b9900eSJiri Pirko if (!handle) { 74477b9900eSJiri Pirko err = -EINVAL; 74577b9900eSJiri Pirko goto errout; 74677b9900eSJiri Pirko } 74777b9900eSJiri Pirko } 74877b9900eSJiri Pirko fnew->handle = handle; 74977b9900eSJiri Pirko 750e69985c6SAmir Vadai if (tb[TCA_FLOWER_FLAGS]) { 751e69985c6SAmir Vadai fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); 752e69985c6SAmir Vadai 753e69985c6SAmir Vadai if (!tc_flags_valid(fnew->flags)) { 754e69985c6SAmir Vadai err = -EINVAL; 755e69985c6SAmir Vadai goto errout; 756e69985c6SAmir Vadai } 757e69985c6SAmir Vadai } 7585b33f488SAmir Vadai 75977b9900eSJiri Pirko err = fl_set_parms(net, tp, fnew, &mask, base, tb, tca[TCA_RATE], ovr); 76077b9900eSJiri Pirko if (err) 76177b9900eSJiri Pirko goto errout; 76277b9900eSJiri Pirko 76377b9900eSJiri Pirko err = fl_check_assign_mask(head, &mask); 76477b9900eSJiri Pirko if (err) 76577b9900eSJiri Pirko goto errout; 76677b9900eSJiri Pirko 767e8eb36cdSAmir Vadai if (!tc_skip_sw(fnew->flags)) { 76877b9900eSJiri Pirko err = rhashtable_insert_fast(&head->ht, &fnew->ht_node, 76977b9900eSJiri Pirko head->ht_params); 77077b9900eSJiri Pirko if (err) 77177b9900eSJiri Pirko goto errout; 772e69985c6SAmir Vadai } 7735b33f488SAmir Vadai 77479685219SHadar Hen Zion if (!tc_skip_hw(fnew->flags)) { 775e8eb36cdSAmir Vadai err = fl_hw_replace_filter(tp, 7765b33f488SAmir Vadai &head->dissector, 7775b33f488SAmir Vadai &mask.key, 7783036dab6SHadar Hen Zion fnew); 779e8eb36cdSAmir Vadai if (err) 780e8eb36cdSAmir Vadai goto errout; 78179685219SHadar Hen Zion } 7825b33f488SAmir Vadai 7835b33f488SAmir Vadai if (fold) { 784725cbb62SJiri Pirko if (!tc_skip_sw(fold->flags)) 78577b9900eSJiri Pirko rhashtable_remove_fast(&head->ht, &fold->ht_node, 78677b9900eSJiri Pirko head->ht_params); 78779685219SHadar Hen Zion if (!tc_skip_hw(fold->flags)) 7883036dab6SHadar Hen Zion fl_hw_destroy_filter(tp, fold); 7895b33f488SAmir Vadai } 79077b9900eSJiri Pirko 79177b9900eSJiri Pirko *arg = (unsigned long) fnew; 79277b9900eSJiri Pirko 79377b9900eSJiri Pirko if (fold) { 794ff3532f2SDaniel Borkmann list_replace_rcu(&fold->list, &fnew->list); 79577b9900eSJiri Pirko tcf_unbind_filter(tp, &fold->res); 79677b9900eSJiri Pirko call_rcu(&fold->rcu, fl_destroy_filter); 79777b9900eSJiri Pirko } else { 79877b9900eSJiri Pirko list_add_tail_rcu(&fnew->list, &head->filters); 79977b9900eSJiri Pirko } 80077b9900eSJiri Pirko 80177b9900eSJiri Pirko return 0; 80277b9900eSJiri Pirko 80377b9900eSJiri Pirko errout: 804b9a24bb7SWANG Cong tcf_exts_destroy(&fnew->exts); 80577b9900eSJiri Pirko kfree(fnew); 80677b9900eSJiri Pirko return err; 80777b9900eSJiri Pirko } 80877b9900eSJiri Pirko 80977b9900eSJiri Pirko static int fl_delete(struct tcf_proto *tp, unsigned long arg) 81077b9900eSJiri Pirko { 81177b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 81277b9900eSJiri Pirko struct cls_fl_filter *f = (struct cls_fl_filter *) arg; 81377b9900eSJiri Pirko 814725cbb62SJiri Pirko if (!tc_skip_sw(f->flags)) 81577b9900eSJiri Pirko rhashtable_remove_fast(&head->ht, &f->ht_node, 81677b9900eSJiri Pirko head->ht_params); 81713fa876eSRoi Dayan __fl_delete(tp, f); 81877b9900eSJiri Pirko return 0; 81977b9900eSJiri Pirko } 82077b9900eSJiri Pirko 82177b9900eSJiri Pirko static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg) 82277b9900eSJiri Pirko { 82377b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 82477b9900eSJiri Pirko struct cls_fl_filter *f; 82577b9900eSJiri Pirko 82677b9900eSJiri Pirko list_for_each_entry_rcu(f, &head->filters, list) { 82777b9900eSJiri Pirko if (arg->count < arg->skip) 82877b9900eSJiri Pirko goto skip; 82977b9900eSJiri Pirko if (arg->fn(tp, (unsigned long) f, arg) < 0) { 83077b9900eSJiri Pirko arg->stop = 1; 83177b9900eSJiri Pirko break; 83277b9900eSJiri Pirko } 83377b9900eSJiri Pirko skip: 83477b9900eSJiri Pirko arg->count++; 83577b9900eSJiri Pirko } 83677b9900eSJiri Pirko } 83777b9900eSJiri Pirko 83877b9900eSJiri Pirko static int fl_dump_key_val(struct sk_buff *skb, 83977b9900eSJiri Pirko void *val, int val_type, 84077b9900eSJiri Pirko void *mask, int mask_type, int len) 84177b9900eSJiri Pirko { 84277b9900eSJiri Pirko int err; 84377b9900eSJiri Pirko 84477b9900eSJiri Pirko if (!memchr_inv(mask, 0, len)) 84577b9900eSJiri Pirko return 0; 84677b9900eSJiri Pirko err = nla_put(skb, val_type, len, val); 84777b9900eSJiri Pirko if (err) 84877b9900eSJiri Pirko return err; 84977b9900eSJiri Pirko if (mask_type != TCA_FLOWER_UNSPEC) { 85077b9900eSJiri Pirko err = nla_put(skb, mask_type, len, mask); 85177b9900eSJiri Pirko if (err) 85277b9900eSJiri Pirko return err; 85377b9900eSJiri Pirko } 85477b9900eSJiri Pirko return 0; 85577b9900eSJiri Pirko } 85677b9900eSJiri Pirko 8579399ae9aSHadar Hen Zion static int fl_dump_key_vlan(struct sk_buff *skb, 8589399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_key, 8599399ae9aSHadar Hen Zion struct flow_dissector_key_vlan *vlan_mask) 8609399ae9aSHadar Hen Zion { 8619399ae9aSHadar Hen Zion int err; 8629399ae9aSHadar Hen Zion 8639399ae9aSHadar Hen Zion if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask))) 8649399ae9aSHadar Hen Zion return 0; 8659399ae9aSHadar Hen Zion if (vlan_mask->vlan_id) { 8669399ae9aSHadar Hen Zion err = nla_put_u16(skb, TCA_FLOWER_KEY_VLAN_ID, 8679399ae9aSHadar Hen Zion vlan_key->vlan_id); 8689399ae9aSHadar Hen Zion if (err) 8699399ae9aSHadar Hen Zion return err; 8709399ae9aSHadar Hen Zion } 8719399ae9aSHadar Hen Zion if (vlan_mask->vlan_priority) { 8729399ae9aSHadar Hen Zion err = nla_put_u8(skb, TCA_FLOWER_KEY_VLAN_PRIO, 8739399ae9aSHadar Hen Zion vlan_key->vlan_priority); 8749399ae9aSHadar Hen Zion if (err) 8759399ae9aSHadar Hen Zion return err; 8769399ae9aSHadar Hen Zion } 8779399ae9aSHadar Hen Zion return 0; 8789399ae9aSHadar Hen Zion } 8799399ae9aSHadar Hen Zion 88077b9900eSJiri Pirko static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, 88177b9900eSJiri Pirko struct sk_buff *skb, struct tcmsg *t) 88277b9900eSJiri Pirko { 88377b9900eSJiri Pirko struct cls_fl_head *head = rtnl_dereference(tp->root); 88477b9900eSJiri Pirko struct cls_fl_filter *f = (struct cls_fl_filter *) fh; 88577b9900eSJiri Pirko struct nlattr *nest; 88677b9900eSJiri Pirko struct fl_flow_key *key, *mask; 88777b9900eSJiri Pirko 88877b9900eSJiri Pirko if (!f) 88977b9900eSJiri Pirko return skb->len; 89077b9900eSJiri Pirko 89177b9900eSJiri Pirko t->tcm_handle = f->handle; 89277b9900eSJiri Pirko 89377b9900eSJiri Pirko nest = nla_nest_start(skb, TCA_OPTIONS); 89477b9900eSJiri Pirko if (!nest) 89577b9900eSJiri Pirko goto nla_put_failure; 89677b9900eSJiri Pirko 89777b9900eSJiri Pirko if (f->res.classid && 89877b9900eSJiri Pirko nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid)) 89977b9900eSJiri Pirko goto nla_put_failure; 90077b9900eSJiri Pirko 90177b9900eSJiri Pirko key = &f->key; 90277b9900eSJiri Pirko mask = &head->mask.key; 90377b9900eSJiri Pirko 90477b9900eSJiri Pirko if (mask->indev_ifindex) { 90577b9900eSJiri Pirko struct net_device *dev; 90677b9900eSJiri Pirko 90777b9900eSJiri Pirko dev = __dev_get_by_index(net, key->indev_ifindex); 90877b9900eSJiri Pirko if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name)) 90977b9900eSJiri Pirko goto nla_put_failure; 91077b9900eSJiri Pirko } 91177b9900eSJiri Pirko 91279685219SHadar Hen Zion if (!tc_skip_hw(f->flags)) 91310cbc684SAmir Vadai fl_hw_update_stats(tp, f); 91410cbc684SAmir Vadai 91577b9900eSJiri Pirko if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 91677b9900eSJiri Pirko mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 91777b9900eSJiri Pirko sizeof(key->eth.dst)) || 91877b9900eSJiri Pirko fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 91977b9900eSJiri Pirko mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 92077b9900eSJiri Pirko sizeof(key->eth.src)) || 92177b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE, 92277b9900eSJiri Pirko &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 92377b9900eSJiri Pirko sizeof(key->basic.n_proto))) 92477b9900eSJiri Pirko goto nla_put_failure; 9259399ae9aSHadar Hen Zion 9269399ae9aSHadar Hen Zion if (fl_dump_key_vlan(skb, &key->vlan, &mask->vlan)) 9279399ae9aSHadar Hen Zion goto nla_put_failure; 9289399ae9aSHadar Hen Zion 92977b9900eSJiri Pirko if ((key->basic.n_proto == htons(ETH_P_IP) || 93077b9900eSJiri Pirko key->basic.n_proto == htons(ETH_P_IPV6)) && 93177b9900eSJiri Pirko fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 93277b9900eSJiri Pirko &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 93377b9900eSJiri Pirko sizeof(key->basic.ip_proto))) 93477b9900eSJiri Pirko goto nla_put_failure; 93577b9900eSJiri Pirko 936c3f83241STom Herbert if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 93777b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 93877b9900eSJiri Pirko &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 93977b9900eSJiri Pirko sizeof(key->ipv4.src)) || 94077b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 94177b9900eSJiri Pirko &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 94277b9900eSJiri Pirko sizeof(key->ipv4.dst)))) 94377b9900eSJiri Pirko goto nla_put_failure; 944c3f83241STom Herbert else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 94577b9900eSJiri Pirko (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 94677b9900eSJiri Pirko &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 94777b9900eSJiri Pirko sizeof(key->ipv6.src)) || 94877b9900eSJiri Pirko fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 94977b9900eSJiri Pirko &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 95077b9900eSJiri Pirko sizeof(key->ipv6.dst)))) 95177b9900eSJiri Pirko goto nla_put_failure; 95277b9900eSJiri Pirko 95377b9900eSJiri Pirko if (key->basic.ip_proto == IPPROTO_TCP && 95477b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 955aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 95677b9900eSJiri Pirko sizeof(key->tp.src)) || 95777b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 958aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 95977b9900eSJiri Pirko sizeof(key->tp.dst)))) 96077b9900eSJiri Pirko goto nla_put_failure; 96177b9900eSJiri Pirko else if (key->basic.ip_proto == IPPROTO_UDP && 96277b9900eSJiri Pirko (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 963aa72d708SOr Gerlitz &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 96477b9900eSJiri Pirko sizeof(key->tp.src)) || 96577b9900eSJiri Pirko fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 966aa72d708SOr Gerlitz &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 96777b9900eSJiri Pirko sizeof(key->tp.dst)))) 96877b9900eSJiri Pirko goto nla_put_failure; 9695976c5f4SSimon Horman else if (key->basic.ip_proto == IPPROTO_SCTP && 9705976c5f4SSimon Horman (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 9715976c5f4SSimon Horman &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 9725976c5f4SSimon Horman sizeof(key->tp.src)) || 9735976c5f4SSimon Horman fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 9745976c5f4SSimon Horman &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 9755976c5f4SSimon Horman sizeof(key->tp.dst)))) 9765976c5f4SSimon Horman goto nla_put_failure; 97777b9900eSJiri Pirko 978bc3103f1SAmir Vadai if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 979bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv4.src, 980bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src, 981bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 982bc3103f1SAmir Vadai sizeof(key->enc_ipv4.src)) || 983bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv4.dst, 984bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst, 985bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 986bc3103f1SAmir Vadai sizeof(key->enc_ipv4.dst)))) 987bc3103f1SAmir Vadai goto nla_put_failure; 988bc3103f1SAmir Vadai else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 989bc3103f1SAmir Vadai (fl_dump_key_val(skb, &key->enc_ipv6.src, 990bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src, 991bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 992bc3103f1SAmir Vadai sizeof(key->enc_ipv6.src)) || 993bc3103f1SAmir Vadai fl_dump_key_val(skb, &key->enc_ipv6.dst, 994bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST, 995bc3103f1SAmir Vadai &mask->enc_ipv6.dst, 996bc3103f1SAmir Vadai TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 997bc3103f1SAmir Vadai sizeof(key->enc_ipv6.dst)))) 998bc3103f1SAmir Vadai goto nla_put_failure; 999bc3103f1SAmir Vadai 1000bc3103f1SAmir Vadai if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, 1001eb523f42SHadar Hen Zion &mask->enc_key_id, TCA_FLOWER_UNSPEC, 1002f4d997fdSHadar Hen Zion sizeof(key->enc_key_id)) || 1003f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.src, 1004f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 1005f4d997fdSHadar Hen Zion &mask->enc_tp.src, 1006f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 1007f4d997fdSHadar Hen Zion sizeof(key->enc_tp.src)) || 1008f4d997fdSHadar Hen Zion fl_dump_key_val(skb, &key->enc_tp.dst, 1009f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 1010f4d997fdSHadar Hen Zion &mask->enc_tp.dst, 1011f4d997fdSHadar Hen Zion TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 1012f4d997fdSHadar Hen Zion sizeof(key->enc_tp.dst))) 1013bc3103f1SAmir Vadai goto nla_put_failure; 1014bc3103f1SAmir Vadai 1015e69985c6SAmir Vadai nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags); 1016e69985c6SAmir Vadai 101777b9900eSJiri Pirko if (tcf_exts_dump(skb, &f->exts)) 101877b9900eSJiri Pirko goto nla_put_failure; 101977b9900eSJiri Pirko 102077b9900eSJiri Pirko nla_nest_end(skb, nest); 102177b9900eSJiri Pirko 102277b9900eSJiri Pirko if (tcf_exts_dump_stats(skb, &f->exts) < 0) 102377b9900eSJiri Pirko goto nla_put_failure; 102477b9900eSJiri Pirko 102577b9900eSJiri Pirko return skb->len; 102677b9900eSJiri Pirko 102777b9900eSJiri Pirko nla_put_failure: 102877b9900eSJiri Pirko nla_nest_cancel(skb, nest); 102977b9900eSJiri Pirko return -1; 103077b9900eSJiri Pirko } 103177b9900eSJiri Pirko 103277b9900eSJiri Pirko static struct tcf_proto_ops cls_fl_ops __read_mostly = { 103377b9900eSJiri Pirko .kind = "flower", 103477b9900eSJiri Pirko .classify = fl_classify, 103577b9900eSJiri Pirko .init = fl_init, 103677b9900eSJiri Pirko .destroy = fl_destroy, 103777b9900eSJiri Pirko .get = fl_get, 103877b9900eSJiri Pirko .change = fl_change, 103977b9900eSJiri Pirko .delete = fl_delete, 104077b9900eSJiri Pirko .walk = fl_walk, 104177b9900eSJiri Pirko .dump = fl_dump, 104277b9900eSJiri Pirko .owner = THIS_MODULE, 104377b9900eSJiri Pirko }; 104477b9900eSJiri Pirko 104577b9900eSJiri Pirko static int __init cls_fl_init(void) 104677b9900eSJiri Pirko { 104777b9900eSJiri Pirko return register_tcf_proto_ops(&cls_fl_ops); 104877b9900eSJiri Pirko } 104977b9900eSJiri Pirko 105077b9900eSJiri Pirko static void __exit cls_fl_exit(void) 105177b9900eSJiri Pirko { 105277b9900eSJiri Pirko unregister_tcf_proto_ops(&cls_fl_ops); 105377b9900eSJiri Pirko } 105477b9900eSJiri Pirko 105577b9900eSJiri Pirko module_init(cls_fl_init); 105677b9900eSJiri Pirko module_exit(cls_fl_exit); 105777b9900eSJiri Pirko 105877b9900eSJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 105977b9900eSJiri Pirko MODULE_DESCRIPTION("Flower classifier"); 106077b9900eSJiri Pirko MODULE_LICENSE("GPL v2"); 1061