162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/cls_flower.c Flower classifier 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Jiri Pirko <jiri@resnulli.us> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/rhashtable.h> 1262306a36Sopenharmony_ci#include <linux/workqueue.h> 1362306a36Sopenharmony_ci#include <linux/refcount.h> 1462306a36Sopenharmony_ci#include <linux/bitfield.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/if_ether.h> 1762306a36Sopenharmony_ci#include <linux/in6.h> 1862306a36Sopenharmony_ci#include <linux/ip.h> 1962306a36Sopenharmony_ci#include <linux/mpls.h> 2062306a36Sopenharmony_ci#include <linux/ppp_defs.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <net/sch_generic.h> 2362306a36Sopenharmony_ci#include <net/pkt_cls.h> 2462306a36Sopenharmony_ci#include <net/pkt_sched.h> 2562306a36Sopenharmony_ci#include <net/ip.h> 2662306a36Sopenharmony_ci#include <net/flow_dissector.h> 2762306a36Sopenharmony_ci#include <net/geneve.h> 2862306a36Sopenharmony_ci#include <net/vxlan.h> 2962306a36Sopenharmony_ci#include <net/erspan.h> 3062306a36Sopenharmony_ci#include <net/gtp.h> 3162306a36Sopenharmony_ci#include <net/tc_wrapper.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <net/dst.h> 3462306a36Sopenharmony_ci#include <net/dst_metadata.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <uapi/linux/netfilter/nf_conntrack_common.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define TCA_FLOWER_KEY_CT_FLAGS_MAX \ 3962306a36Sopenharmony_ci ((__TCA_FLOWER_KEY_CT_FLAGS_MAX - 1) << 1) 4062306a36Sopenharmony_ci#define TCA_FLOWER_KEY_CT_FLAGS_MASK \ 4162306a36Sopenharmony_ci (TCA_FLOWER_KEY_CT_FLAGS_MAX - 1) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct fl_flow_key { 4462306a36Sopenharmony_ci struct flow_dissector_key_meta meta; 4562306a36Sopenharmony_ci struct flow_dissector_key_control control; 4662306a36Sopenharmony_ci struct flow_dissector_key_control enc_control; 4762306a36Sopenharmony_ci struct flow_dissector_key_basic basic; 4862306a36Sopenharmony_ci struct flow_dissector_key_eth_addrs eth; 4962306a36Sopenharmony_ci struct flow_dissector_key_vlan vlan; 5062306a36Sopenharmony_ci struct flow_dissector_key_vlan cvlan; 5162306a36Sopenharmony_ci union { 5262306a36Sopenharmony_ci struct flow_dissector_key_ipv4_addrs ipv4; 5362306a36Sopenharmony_ci struct flow_dissector_key_ipv6_addrs ipv6; 5462306a36Sopenharmony_ci }; 5562306a36Sopenharmony_ci struct flow_dissector_key_ports tp; 5662306a36Sopenharmony_ci struct flow_dissector_key_icmp icmp; 5762306a36Sopenharmony_ci struct flow_dissector_key_arp arp; 5862306a36Sopenharmony_ci struct flow_dissector_key_keyid enc_key_id; 5962306a36Sopenharmony_ci union { 6062306a36Sopenharmony_ci struct flow_dissector_key_ipv4_addrs enc_ipv4; 6162306a36Sopenharmony_ci struct flow_dissector_key_ipv6_addrs enc_ipv6; 6262306a36Sopenharmony_ci }; 6362306a36Sopenharmony_ci struct flow_dissector_key_ports enc_tp; 6462306a36Sopenharmony_ci struct flow_dissector_key_mpls mpls; 6562306a36Sopenharmony_ci struct flow_dissector_key_tcp tcp; 6662306a36Sopenharmony_ci struct flow_dissector_key_ip ip; 6762306a36Sopenharmony_ci struct flow_dissector_key_ip enc_ip; 6862306a36Sopenharmony_ci struct flow_dissector_key_enc_opts enc_opts; 6962306a36Sopenharmony_ci struct flow_dissector_key_ports_range tp_range; 7062306a36Sopenharmony_ci struct flow_dissector_key_ct ct; 7162306a36Sopenharmony_ci struct flow_dissector_key_hash hash; 7262306a36Sopenharmony_ci struct flow_dissector_key_num_of_vlans num_of_vlans; 7362306a36Sopenharmony_ci struct flow_dissector_key_pppoe pppoe; 7462306a36Sopenharmony_ci struct flow_dissector_key_l2tpv3 l2tpv3; 7562306a36Sopenharmony_ci struct flow_dissector_key_ipsec ipsec; 7662306a36Sopenharmony_ci struct flow_dissector_key_cfm cfm; 7762306a36Sopenharmony_ci} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct fl_flow_mask_range { 8062306a36Sopenharmony_ci unsigned short int start; 8162306a36Sopenharmony_ci unsigned short int end; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct fl_flow_mask { 8562306a36Sopenharmony_ci struct fl_flow_key key; 8662306a36Sopenharmony_ci struct fl_flow_mask_range range; 8762306a36Sopenharmony_ci u32 flags; 8862306a36Sopenharmony_ci struct rhash_head ht_node; 8962306a36Sopenharmony_ci struct rhashtable ht; 9062306a36Sopenharmony_ci struct rhashtable_params filter_ht_params; 9162306a36Sopenharmony_ci struct flow_dissector dissector; 9262306a36Sopenharmony_ci struct list_head filters; 9362306a36Sopenharmony_ci struct rcu_work rwork; 9462306a36Sopenharmony_ci struct list_head list; 9562306a36Sopenharmony_ci refcount_t refcnt; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct fl_flow_tmplt { 9962306a36Sopenharmony_ci struct fl_flow_key dummy_key; 10062306a36Sopenharmony_ci struct fl_flow_key mask; 10162306a36Sopenharmony_ci struct flow_dissector dissector; 10262306a36Sopenharmony_ci struct tcf_chain *chain; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct cls_fl_head { 10662306a36Sopenharmony_ci struct rhashtable ht; 10762306a36Sopenharmony_ci spinlock_t masks_lock; /* Protect masks list */ 10862306a36Sopenharmony_ci struct list_head masks; 10962306a36Sopenharmony_ci struct list_head hw_filters; 11062306a36Sopenharmony_ci struct rcu_work rwork; 11162306a36Sopenharmony_ci struct idr handle_idr; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct cls_fl_filter { 11562306a36Sopenharmony_ci struct fl_flow_mask *mask; 11662306a36Sopenharmony_ci struct rhash_head ht_node; 11762306a36Sopenharmony_ci struct fl_flow_key mkey; 11862306a36Sopenharmony_ci struct tcf_exts exts; 11962306a36Sopenharmony_ci struct tcf_result res; 12062306a36Sopenharmony_ci struct fl_flow_key key; 12162306a36Sopenharmony_ci struct list_head list; 12262306a36Sopenharmony_ci struct list_head hw_list; 12362306a36Sopenharmony_ci u32 handle; 12462306a36Sopenharmony_ci u32 flags; 12562306a36Sopenharmony_ci u32 in_hw_count; 12662306a36Sopenharmony_ci u8 needs_tc_skb_ext:1; 12762306a36Sopenharmony_ci struct rcu_work rwork; 12862306a36Sopenharmony_ci struct net_device *hw_dev; 12962306a36Sopenharmony_ci /* Flower classifier is unlocked, which means that its reference counter 13062306a36Sopenharmony_ci * can be changed concurrently without any kind of external 13162306a36Sopenharmony_ci * synchronization. Use atomic reference counter to be concurrency-safe. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci refcount_t refcnt; 13462306a36Sopenharmony_ci bool deleted; 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const struct rhashtable_params mask_ht_params = { 13862306a36Sopenharmony_ci .key_offset = offsetof(struct fl_flow_mask, key), 13962306a36Sopenharmony_ci .key_len = sizeof(struct fl_flow_key), 14062306a36Sopenharmony_ci .head_offset = offsetof(struct fl_flow_mask, ht_node), 14162306a36Sopenharmony_ci .automatic_shrinking = true, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic unsigned short int fl_mask_range(const struct fl_flow_mask *mask) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci return mask->range.end - mask->range.start; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void fl_mask_update_range(struct fl_flow_mask *mask) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci const u8 *bytes = (const u8 *) &mask->key; 15262306a36Sopenharmony_ci size_t size = sizeof(mask->key); 15362306a36Sopenharmony_ci size_t i, first = 0, last; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci for (i = 0; i < size; i++) { 15662306a36Sopenharmony_ci if (bytes[i]) { 15762306a36Sopenharmony_ci first = i; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci last = first; 16262306a36Sopenharmony_ci for (i = size - 1; i != first; i--) { 16362306a36Sopenharmony_ci if (bytes[i]) { 16462306a36Sopenharmony_ci last = i; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci mask->range.start = rounddown(first, sizeof(long)); 16962306a36Sopenharmony_ci mask->range.end = roundup(last + 1, sizeof(long)); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void *fl_key_get_start(struct fl_flow_key *key, 17362306a36Sopenharmony_ci const struct fl_flow_mask *mask) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return (u8 *) key + mask->range.start; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void fl_set_masked_key(struct fl_flow_key *mkey, struct fl_flow_key *key, 17962306a36Sopenharmony_ci struct fl_flow_mask *mask) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci const long *lkey = fl_key_get_start(key, mask); 18262306a36Sopenharmony_ci const long *lmask = fl_key_get_start(&mask->key, mask); 18362306a36Sopenharmony_ci long *lmkey = fl_key_get_start(mkey, mask); 18462306a36Sopenharmony_ci int i; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) 18762306a36Sopenharmony_ci *lmkey++ = *lkey++ & *lmask++; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic bool fl_mask_fits_tmplt(struct fl_flow_tmplt *tmplt, 19162306a36Sopenharmony_ci struct fl_flow_mask *mask) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci const long *lmask = fl_key_get_start(&mask->key, mask); 19462306a36Sopenharmony_ci const long *ltmplt; 19562306a36Sopenharmony_ci int i; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!tmplt) 19862306a36Sopenharmony_ci return true; 19962306a36Sopenharmony_ci ltmplt = fl_key_get_start(&tmplt->mask, mask); 20062306a36Sopenharmony_ci for (i = 0; i < fl_mask_range(mask); i += sizeof(long)) { 20162306a36Sopenharmony_ci if (~*ltmplt++ & *lmask++) 20262306a36Sopenharmony_ci return false; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return true; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void fl_clear_masked_range(struct fl_flow_key *key, 20862306a36Sopenharmony_ci struct fl_flow_mask *mask) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci memset(fl_key_get_start(key, mask), 0, fl_mask_range(mask)); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic bool fl_range_port_dst_cmp(struct cls_fl_filter *filter, 21462306a36Sopenharmony_ci struct fl_flow_key *key, 21562306a36Sopenharmony_ci struct fl_flow_key *mkey) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci u16 min_mask, max_mask, min_val, max_val; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci min_mask = ntohs(filter->mask->key.tp_range.tp_min.dst); 22062306a36Sopenharmony_ci max_mask = ntohs(filter->mask->key.tp_range.tp_max.dst); 22162306a36Sopenharmony_ci min_val = ntohs(filter->key.tp_range.tp_min.dst); 22262306a36Sopenharmony_ci max_val = ntohs(filter->key.tp_range.tp_max.dst); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (min_mask && max_mask) { 22562306a36Sopenharmony_ci if (ntohs(key->tp_range.tp.dst) < min_val || 22662306a36Sopenharmony_ci ntohs(key->tp_range.tp.dst) > max_val) 22762306a36Sopenharmony_ci return false; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* skb does not have min and max values */ 23062306a36Sopenharmony_ci mkey->tp_range.tp_min.dst = filter->mkey.tp_range.tp_min.dst; 23162306a36Sopenharmony_ci mkey->tp_range.tp_max.dst = filter->mkey.tp_range.tp_max.dst; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci return true; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic bool fl_range_port_src_cmp(struct cls_fl_filter *filter, 23762306a36Sopenharmony_ci struct fl_flow_key *key, 23862306a36Sopenharmony_ci struct fl_flow_key *mkey) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci u16 min_mask, max_mask, min_val, max_val; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci min_mask = ntohs(filter->mask->key.tp_range.tp_min.src); 24362306a36Sopenharmony_ci max_mask = ntohs(filter->mask->key.tp_range.tp_max.src); 24462306a36Sopenharmony_ci min_val = ntohs(filter->key.tp_range.tp_min.src); 24562306a36Sopenharmony_ci max_val = ntohs(filter->key.tp_range.tp_max.src); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (min_mask && max_mask) { 24862306a36Sopenharmony_ci if (ntohs(key->tp_range.tp.src) < min_val || 24962306a36Sopenharmony_ci ntohs(key->tp_range.tp.src) > max_val) 25062306a36Sopenharmony_ci return false; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* skb does not have min and max values */ 25362306a36Sopenharmony_ci mkey->tp_range.tp_min.src = filter->mkey.tp_range.tp_min.src; 25462306a36Sopenharmony_ci mkey->tp_range.tp_max.src = filter->mkey.tp_range.tp_max.src; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci return true; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic struct cls_fl_filter *__fl_lookup(struct fl_flow_mask *mask, 26062306a36Sopenharmony_ci struct fl_flow_key *mkey) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci return rhashtable_lookup_fast(&mask->ht, fl_key_get_start(mkey, mask), 26362306a36Sopenharmony_ci mask->filter_ht_params); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic struct cls_fl_filter *fl_lookup_range(struct fl_flow_mask *mask, 26762306a36Sopenharmony_ci struct fl_flow_key *mkey, 26862306a36Sopenharmony_ci struct fl_flow_key *key) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct cls_fl_filter *filter, *f; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci list_for_each_entry_rcu(filter, &mask->filters, list) { 27362306a36Sopenharmony_ci if (!fl_range_port_dst_cmp(filter, key, mkey)) 27462306a36Sopenharmony_ci continue; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!fl_range_port_src_cmp(filter, key, mkey)) 27762306a36Sopenharmony_ci continue; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci f = __fl_lookup(mask, mkey); 28062306a36Sopenharmony_ci if (f) 28162306a36Sopenharmony_ci return f; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci return NULL; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic noinline_for_stack 28762306a36Sopenharmony_cistruct cls_fl_filter *fl_mask_lookup(struct fl_flow_mask *mask, struct fl_flow_key *key) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct fl_flow_key mkey; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci fl_set_masked_key(&mkey, key, mask); 29262306a36Sopenharmony_ci if ((mask->flags & TCA_FLOWER_MASK_FLAGS_RANGE)) 29362306a36Sopenharmony_ci return fl_lookup_range(mask, &mkey, key); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return __fl_lookup(mask, &mkey); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic u16 fl_ct_info_to_flower_map[] = { 29962306a36Sopenharmony_ci [IP_CT_ESTABLISHED] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 30062306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED, 30162306a36Sopenharmony_ci [IP_CT_RELATED] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 30262306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_RELATED, 30362306a36Sopenharmony_ci [IP_CT_ESTABLISHED_REPLY] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 30462306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED | 30562306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_REPLY, 30662306a36Sopenharmony_ci [IP_CT_RELATED_REPLY] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 30762306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_RELATED | 30862306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_REPLY, 30962306a36Sopenharmony_ci [IP_CT_NEW] = TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 31062306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_NEW, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciTC_INDIRECT_SCOPE int fl_classify(struct sk_buff *skb, 31462306a36Sopenharmony_ci const struct tcf_proto *tp, 31562306a36Sopenharmony_ci struct tcf_result *res) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct cls_fl_head *head = rcu_dereference_bh(tp->root); 31862306a36Sopenharmony_ci bool post_ct = tc_skb_cb(skb)->post_ct; 31962306a36Sopenharmony_ci u16 zone = tc_skb_cb(skb)->zone; 32062306a36Sopenharmony_ci struct fl_flow_key skb_key; 32162306a36Sopenharmony_ci struct fl_flow_mask *mask; 32262306a36Sopenharmony_ci struct cls_fl_filter *f; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci list_for_each_entry_rcu(mask, &head->masks, list) { 32562306a36Sopenharmony_ci flow_dissector_init_keys(&skb_key.control, &skb_key.basic); 32662306a36Sopenharmony_ci fl_clear_masked_range(&skb_key, mask); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci skb_flow_dissect_meta(skb, &mask->dissector, &skb_key); 32962306a36Sopenharmony_ci /* skb_flow_dissect() does not set n_proto in case an unknown 33062306a36Sopenharmony_ci * protocol, so do it rather here. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci skb_key.basic.n_proto = skb_protocol(skb, false); 33362306a36Sopenharmony_ci skb_flow_dissect_tunnel_info(skb, &mask->dissector, &skb_key); 33462306a36Sopenharmony_ci skb_flow_dissect_ct(skb, &mask->dissector, &skb_key, 33562306a36Sopenharmony_ci fl_ct_info_to_flower_map, 33662306a36Sopenharmony_ci ARRAY_SIZE(fl_ct_info_to_flower_map), 33762306a36Sopenharmony_ci post_ct, zone); 33862306a36Sopenharmony_ci skb_flow_dissect_hash(skb, &mask->dissector, &skb_key); 33962306a36Sopenharmony_ci skb_flow_dissect(skb, &mask->dissector, &skb_key, 34062306a36Sopenharmony_ci FLOW_DISSECTOR_F_STOP_BEFORE_ENCAP); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci f = fl_mask_lookup(mask, &skb_key); 34362306a36Sopenharmony_ci if (f && !tc_skip_sw(f->flags)) { 34462306a36Sopenharmony_ci *res = f->res; 34562306a36Sopenharmony_ci return tcf_exts_exec(skb, &f->exts, res); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci return -1; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int fl_init(struct tcf_proto *tp) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct cls_fl_head *head; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci head = kzalloc(sizeof(*head), GFP_KERNEL); 35662306a36Sopenharmony_ci if (!head) 35762306a36Sopenharmony_ci return -ENOBUFS; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci spin_lock_init(&head->masks_lock); 36062306a36Sopenharmony_ci INIT_LIST_HEAD_RCU(&head->masks); 36162306a36Sopenharmony_ci INIT_LIST_HEAD(&head->hw_filters); 36262306a36Sopenharmony_ci rcu_assign_pointer(tp->root, head); 36362306a36Sopenharmony_ci idr_init(&head->handle_idr); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return rhashtable_init(&head->ht, &mask_ht_params); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void fl_mask_free(struct fl_flow_mask *mask, bool mask_init_done) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci /* temporary masks don't have their filters list and ht initialized */ 37162306a36Sopenharmony_ci if (mask_init_done) { 37262306a36Sopenharmony_ci WARN_ON(!list_empty(&mask->filters)); 37362306a36Sopenharmony_ci rhashtable_destroy(&mask->ht); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci kfree(mask); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void fl_mask_free_work(struct work_struct *work) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct fl_flow_mask *mask = container_of(to_rcu_work(work), 38162306a36Sopenharmony_ci struct fl_flow_mask, rwork); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci fl_mask_free(mask, true); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void fl_uninit_mask_free_work(struct work_struct *work) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct fl_flow_mask *mask = container_of(to_rcu_work(work), 38962306a36Sopenharmony_ci struct fl_flow_mask, rwork); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci fl_mask_free(mask, false); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic bool fl_mask_put(struct cls_fl_head *head, struct fl_flow_mask *mask) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci if (!refcount_dec_and_test(&mask->refcnt)) 39762306a36Sopenharmony_ci return false; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci rhashtable_remove_fast(&head->ht, &mask->ht_node, mask_ht_params); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci spin_lock(&head->masks_lock); 40262306a36Sopenharmony_ci list_del_rcu(&mask->list); 40362306a36Sopenharmony_ci spin_unlock(&head->masks_lock); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci tcf_queue_work(&mask->rwork, fl_mask_free_work); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return true; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic struct cls_fl_head *fl_head_dereference(struct tcf_proto *tp) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci /* Flower classifier only changes root pointer during init and destroy. 41362306a36Sopenharmony_ci * Users must obtain reference to tcf_proto instance before calling its 41462306a36Sopenharmony_ci * API, so tp->root pointer is protected from concurrent call to 41562306a36Sopenharmony_ci * fl_destroy() by reference counting. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci return rcu_dereference_raw(tp->root); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void __fl_destroy_filter(struct cls_fl_filter *f) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci if (f->needs_tc_skb_ext) 42362306a36Sopenharmony_ci tc_skb_ext_tc_disable(); 42462306a36Sopenharmony_ci tcf_exts_destroy(&f->exts); 42562306a36Sopenharmony_ci tcf_exts_put_net(&f->exts); 42662306a36Sopenharmony_ci kfree(f); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic void fl_destroy_filter_work(struct work_struct *work) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct cls_fl_filter *f = container_of(to_rcu_work(work), 43262306a36Sopenharmony_ci struct cls_fl_filter, rwork); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci __fl_destroy_filter(f); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f, 43862306a36Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct tcf_block *block = tp->chain->block; 44162306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack); 44462306a36Sopenharmony_ci cls_flower.command = FLOW_CLS_DESTROY; 44562306a36Sopenharmony_ci cls_flower.cookie = (unsigned long) f; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci tc_setup_cb_destroy(block, tp, TC_SETUP_CLSFLOWER, &cls_flower, false, 44862306a36Sopenharmony_ci &f->flags, &f->in_hw_count, rtnl_held); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic int fl_hw_replace_filter(struct tcf_proto *tp, 45362306a36Sopenharmony_ci struct cls_fl_filter *f, bool rtnl_held, 45462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct tcf_block *block = tp->chain->block; 45762306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 45862306a36Sopenharmony_ci bool skip_sw = tc_skip_sw(f->flags); 45962306a36Sopenharmony_ci int err = 0; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci cls_flower.rule = flow_rule_alloc(tcf_exts_num_actions(&f->exts)); 46262306a36Sopenharmony_ci if (!cls_flower.rule) 46362306a36Sopenharmony_ci return -ENOMEM; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, extack); 46662306a36Sopenharmony_ci cls_flower.command = FLOW_CLS_REPLACE; 46762306a36Sopenharmony_ci cls_flower.cookie = (unsigned long) f; 46862306a36Sopenharmony_ci cls_flower.rule->match.dissector = &f->mask->dissector; 46962306a36Sopenharmony_ci cls_flower.rule->match.mask = &f->mask->key; 47062306a36Sopenharmony_ci cls_flower.rule->match.key = &f->mkey; 47162306a36Sopenharmony_ci cls_flower.classid = f->res.classid; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci err = tc_setup_offload_action(&cls_flower.rule->action, &f->exts, 47462306a36Sopenharmony_ci cls_flower.common.extack); 47562306a36Sopenharmony_ci if (err) { 47662306a36Sopenharmony_ci kfree(cls_flower.rule); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return skip_sw ? err : 0; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci err = tc_setup_cb_add(block, tp, TC_SETUP_CLSFLOWER, &cls_flower, 48262306a36Sopenharmony_ci skip_sw, &f->flags, &f->in_hw_count, rtnl_held); 48362306a36Sopenharmony_ci tc_cleanup_offload_action(&cls_flower.rule->action); 48462306a36Sopenharmony_ci kfree(cls_flower.rule); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (err) { 48762306a36Sopenharmony_ci fl_hw_destroy_filter(tp, f, rtnl_held, NULL); 48862306a36Sopenharmony_ci return err; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) 49262306a36Sopenharmony_ci return -EINVAL; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f, 49862306a36Sopenharmony_ci bool rtnl_held) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct tcf_block *block = tp->chain->block; 50162306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, NULL); 50462306a36Sopenharmony_ci cls_flower.command = FLOW_CLS_STATS; 50562306a36Sopenharmony_ci cls_flower.cookie = (unsigned long) f; 50662306a36Sopenharmony_ci cls_flower.classid = f->res.classid; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false, 50962306a36Sopenharmony_ci rtnl_held); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci tcf_exts_hw_stats_update(&f->exts, &cls_flower.stats, cls_flower.use_act_stats); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void __fl_put(struct cls_fl_filter *f) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci if (!refcount_dec_and_test(&f->refcnt)) 51762306a36Sopenharmony_ci return; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (tcf_exts_get_net(&f->exts)) 52062306a36Sopenharmony_ci tcf_queue_work(&f->rwork, fl_destroy_filter_work); 52162306a36Sopenharmony_ci else 52262306a36Sopenharmony_ci __fl_destroy_filter(f); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic struct cls_fl_filter *__fl_get(struct cls_fl_head *head, u32 handle) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct cls_fl_filter *f; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci rcu_read_lock(); 53062306a36Sopenharmony_ci f = idr_find(&head->handle_idr, handle); 53162306a36Sopenharmony_ci if (f && !refcount_inc_not_zero(&f->refcnt)) 53262306a36Sopenharmony_ci f = NULL; 53362306a36Sopenharmony_ci rcu_read_unlock(); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return f; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic struct tcf_exts *fl_get_exts(const struct tcf_proto *tp, u32 handle) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct cls_fl_head *head = rcu_dereference_bh(tp->root); 54162306a36Sopenharmony_ci struct cls_fl_filter *f; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci f = idr_find(&head->handle_idr, handle); 54462306a36Sopenharmony_ci return f ? &f->exts : NULL; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f, 54862306a36Sopenharmony_ci bool *last, bool rtnl_held, 54962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci *last = false; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci spin_lock(&tp->lock); 55662306a36Sopenharmony_ci if (f->deleted) { 55762306a36Sopenharmony_ci spin_unlock(&tp->lock); 55862306a36Sopenharmony_ci return -ENOENT; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci f->deleted = true; 56262306a36Sopenharmony_ci rhashtable_remove_fast(&f->mask->ht, &f->ht_node, 56362306a36Sopenharmony_ci f->mask->filter_ht_params); 56462306a36Sopenharmony_ci idr_remove(&head->handle_idr, f->handle); 56562306a36Sopenharmony_ci list_del_rcu(&f->list); 56662306a36Sopenharmony_ci spin_unlock(&tp->lock); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci *last = fl_mask_put(head, f->mask); 56962306a36Sopenharmony_ci if (!tc_skip_hw(f->flags)) 57062306a36Sopenharmony_ci fl_hw_destroy_filter(tp, f, rtnl_held, extack); 57162306a36Sopenharmony_ci tcf_unbind_filter(tp, &f->res); 57262306a36Sopenharmony_ci __fl_put(f); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic void fl_destroy_sleepable(struct work_struct *work) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct cls_fl_head *head = container_of(to_rcu_work(work), 58062306a36Sopenharmony_ci struct cls_fl_head, 58162306a36Sopenharmony_ci rwork); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci rhashtable_destroy(&head->ht); 58462306a36Sopenharmony_ci kfree(head); 58562306a36Sopenharmony_ci module_put(THIS_MODULE); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void fl_destroy(struct tcf_proto *tp, bool rtnl_held, 58962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 59262306a36Sopenharmony_ci struct fl_flow_mask *mask, *next_mask; 59362306a36Sopenharmony_ci struct cls_fl_filter *f, *next; 59462306a36Sopenharmony_ci bool last; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci list_for_each_entry_safe(mask, next_mask, &head->masks, list) { 59762306a36Sopenharmony_ci list_for_each_entry_safe(f, next, &mask->filters, list) { 59862306a36Sopenharmony_ci __fl_delete(tp, f, &last, rtnl_held, extack); 59962306a36Sopenharmony_ci if (last) 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci idr_destroy(&head->handle_idr); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci __module_get(THIS_MODULE); 60662306a36Sopenharmony_ci tcf_queue_work(&head->rwork, fl_destroy_sleepable); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void fl_put(struct tcf_proto *tp, void *arg) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct cls_fl_filter *f = arg; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci __fl_put(f); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic void *fl_get(struct tcf_proto *tp, u32 handle) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return __fl_get(head, handle); 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { 62462306a36Sopenharmony_ci [TCA_FLOWER_UNSPEC] = { .strict_start_type = 62562306a36Sopenharmony_ci TCA_FLOWER_L2_MISS }, 62662306a36Sopenharmony_ci [TCA_FLOWER_CLASSID] = { .type = NLA_U32 }, 62762306a36Sopenharmony_ci [TCA_FLOWER_INDEV] = { .type = NLA_STRING, 62862306a36Sopenharmony_ci .len = IFNAMSIZ }, 62962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ETH_DST] = { .len = ETH_ALEN }, 63062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ETH_DST_MASK] = { .len = ETH_ALEN }, 63162306a36Sopenharmony_ci [TCA_FLOWER_KEY_ETH_SRC] = { .len = ETH_ALEN }, 63262306a36Sopenharmony_ci [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .len = ETH_ALEN }, 63362306a36Sopenharmony_ci [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, 63462306a36Sopenharmony_ci [TCA_FLOWER_KEY_IP_PROTO] = { .type = NLA_U8 }, 63562306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, 63662306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, 63762306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, 63862306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, 63962306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 64062306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 64162306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 64262306a36Sopenharmony_ci [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 64362306a36Sopenharmony_ci [TCA_FLOWER_KEY_TCP_SRC] = { .type = NLA_U16 }, 64462306a36Sopenharmony_ci [TCA_FLOWER_KEY_TCP_DST] = { .type = NLA_U16 }, 64562306a36Sopenharmony_ci [TCA_FLOWER_KEY_UDP_SRC] = { .type = NLA_U16 }, 64662306a36Sopenharmony_ci [TCA_FLOWER_KEY_UDP_DST] = { .type = NLA_U16 }, 64762306a36Sopenharmony_ci [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, 64862306a36Sopenharmony_ci [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, 64962306a36Sopenharmony_ci [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, 65062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, 65162306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, 65262306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NLA_U32 }, 65362306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, 65462306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NLA_U32 }, 65562306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, 65662306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .len = sizeof(struct in6_addr) }, 65762306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, 65862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .len = sizeof(struct in6_addr) }, 65962306a36Sopenharmony_ci [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NLA_U16 }, 66062306a36Sopenharmony_ci [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NLA_U16 }, 66162306a36Sopenharmony_ci [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NLA_U16 }, 66262306a36Sopenharmony_ci [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NLA_U16 }, 66362306a36Sopenharmony_ci [TCA_FLOWER_KEY_SCTP_SRC_MASK] = { .type = NLA_U16 }, 66462306a36Sopenharmony_ci [TCA_FLOWER_KEY_SCTP_DST_MASK] = { .type = NLA_U16 }, 66562306a36Sopenharmony_ci [TCA_FLOWER_KEY_SCTP_SRC] = { .type = NLA_U16 }, 66662306a36Sopenharmony_ci [TCA_FLOWER_KEY_SCTP_DST] = { .type = NLA_U16 }, 66762306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT] = { .type = NLA_U16 }, 66862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK] = { .type = NLA_U16 }, 66962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NLA_U16 }, 67062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK] = { .type = NLA_U16 }, 67162306a36Sopenharmony_ci [TCA_FLOWER_KEY_FLAGS] = { .type = NLA_U32 }, 67262306a36Sopenharmony_ci [TCA_FLOWER_KEY_FLAGS_MASK] = { .type = NLA_U32 }, 67362306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV4_TYPE] = { .type = NLA_U8 }, 67462306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV4_TYPE_MASK] = { .type = NLA_U8 }, 67562306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV4_CODE] = { .type = NLA_U8 }, 67662306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV4_CODE_MASK] = { .type = NLA_U8 }, 67762306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV6_TYPE] = { .type = NLA_U8 }, 67862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV6_TYPE_MASK] = { .type = NLA_U8 }, 67962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV6_CODE] = { .type = NLA_U8 }, 68062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ICMPV6_CODE_MASK] = { .type = NLA_U8 }, 68162306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_SIP] = { .type = NLA_U32 }, 68262306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_SIP_MASK] = { .type = NLA_U32 }, 68362306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_TIP] = { .type = NLA_U32 }, 68462306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_TIP_MASK] = { .type = NLA_U32 }, 68562306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_OP] = { .type = NLA_U8 }, 68662306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_OP_MASK] = { .type = NLA_U8 }, 68762306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_SHA] = { .len = ETH_ALEN }, 68862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .len = ETH_ALEN }, 68962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_THA] = { .len = ETH_ALEN }, 69062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ARP_THA_MASK] = { .len = ETH_ALEN }, 69162306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NLA_U8 }, 69262306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NLA_U8 }, 69362306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_TC] = { .type = NLA_U8 }, 69462306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NLA_U32 }, 69562306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_OPTS] = { .type = NLA_NESTED }, 69662306a36Sopenharmony_ci [TCA_FLOWER_KEY_TCP_FLAGS] = { .type = NLA_U16 }, 69762306a36Sopenharmony_ci [TCA_FLOWER_KEY_TCP_FLAGS_MASK] = { .type = NLA_U16 }, 69862306a36Sopenharmony_ci [TCA_FLOWER_KEY_IP_TOS] = { .type = NLA_U8 }, 69962306a36Sopenharmony_ci [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NLA_U8 }, 70062306a36Sopenharmony_ci [TCA_FLOWER_KEY_IP_TTL] = { .type = NLA_U8 }, 70162306a36Sopenharmony_ci [TCA_FLOWER_KEY_IP_TTL_MASK] = { .type = NLA_U8 }, 70262306a36Sopenharmony_ci [TCA_FLOWER_KEY_CVLAN_ID] = { .type = NLA_U16 }, 70362306a36Sopenharmony_ci [TCA_FLOWER_KEY_CVLAN_PRIO] = { .type = NLA_U8 }, 70462306a36Sopenharmony_ci [TCA_FLOWER_KEY_CVLAN_ETH_TYPE] = { .type = NLA_U16 }, 70562306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IP_TOS] = { .type = NLA_U8 }, 70662306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IP_TOS_MASK] = { .type = NLA_U8 }, 70762306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IP_TTL] = { .type = NLA_U8 }, 70862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NLA_U8 }, 70962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS] = { .type = NLA_NESTED }, 71062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NLA_NESTED }, 71162306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_STATE] = 71262306a36Sopenharmony_ci NLA_POLICY_MASK(NLA_U16, TCA_FLOWER_KEY_CT_FLAGS_MASK), 71362306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_STATE_MASK] = 71462306a36Sopenharmony_ci NLA_POLICY_MASK(NLA_U16, TCA_FLOWER_KEY_CT_FLAGS_MASK), 71562306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_ZONE] = { .type = NLA_U16 }, 71662306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_ZONE_MASK] = { .type = NLA_U16 }, 71762306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_MARK] = { .type = NLA_U32 }, 71862306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_MARK_MASK] = { .type = NLA_U32 }, 71962306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_LABELS] = { .type = NLA_BINARY, 72062306a36Sopenharmony_ci .len = 128 / BITS_PER_BYTE }, 72162306a36Sopenharmony_ci [TCA_FLOWER_KEY_CT_LABELS_MASK] = { .type = NLA_BINARY, 72262306a36Sopenharmony_ci .len = 128 / BITS_PER_BYTE }, 72362306a36Sopenharmony_ci [TCA_FLOWER_FLAGS] = { .type = NLA_U32 }, 72462306a36Sopenharmony_ci [TCA_FLOWER_KEY_HASH] = { .type = NLA_U32 }, 72562306a36Sopenharmony_ci [TCA_FLOWER_KEY_HASH_MASK] = { .type = NLA_U32 }, 72662306a36Sopenharmony_ci [TCA_FLOWER_KEY_NUM_OF_VLANS] = { .type = NLA_U8 }, 72762306a36Sopenharmony_ci [TCA_FLOWER_KEY_PPPOE_SID] = { .type = NLA_U16 }, 72862306a36Sopenharmony_ci [TCA_FLOWER_KEY_PPP_PROTO] = { .type = NLA_U16 }, 72962306a36Sopenharmony_ci [TCA_FLOWER_KEY_L2TPV3_SID] = { .type = NLA_U32 }, 73062306a36Sopenharmony_ci [TCA_FLOWER_KEY_SPI] = { .type = NLA_U32 }, 73162306a36Sopenharmony_ci [TCA_FLOWER_KEY_SPI_MASK] = { .type = NLA_U32 }, 73262306a36Sopenharmony_ci [TCA_FLOWER_L2_MISS] = NLA_POLICY_MAX(NLA_U8, 1), 73362306a36Sopenharmony_ci [TCA_FLOWER_KEY_CFM] = { .type = NLA_NESTED }, 73462306a36Sopenharmony_ci}; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic const struct nla_policy 73762306a36Sopenharmony_cienc_opts_policy[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1] = { 73862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS_UNSPEC] = { 73962306a36Sopenharmony_ci .strict_start_type = TCA_FLOWER_KEY_ENC_OPTS_VXLAN }, 74062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED }, 74162306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS_VXLAN] = { .type = NLA_NESTED }, 74262306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS_ERSPAN] = { .type = NLA_NESTED }, 74362306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPTS_GTP] = { .type = NLA_NESTED }, 74462306a36Sopenharmony_ci}; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic const struct nla_policy 74762306a36Sopenharmony_cigeneve_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1] = { 74862306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] = { .type = NLA_U16 }, 74962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] = { .type = NLA_U8 }, 75062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA] = { .type = NLA_BINARY, 75162306a36Sopenharmony_ci .len = 128 }, 75262306a36Sopenharmony_ci}; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic const struct nla_policy 75562306a36Sopenharmony_civxlan_opt_policy[TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX + 1] = { 75662306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP] = { .type = NLA_U32 }, 75762306a36Sopenharmony_ci}; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic const struct nla_policy 76062306a36Sopenharmony_cierspan_opt_policy[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1] = { 76162306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER] = { .type = NLA_U8 }, 76262306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX] = { .type = NLA_U32 }, 76362306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR] = { .type = NLA_U8 }, 76462306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID] = { .type = NLA_U8 }, 76562306a36Sopenharmony_ci}; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic const struct nla_policy 76862306a36Sopenharmony_cigtp_opt_policy[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1] = { 76962306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE] = { .type = NLA_U8 }, 77062306a36Sopenharmony_ci [TCA_FLOWER_KEY_ENC_OPT_GTP_QFI] = { .type = NLA_U8 }, 77162306a36Sopenharmony_ci}; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const struct nla_policy 77462306a36Sopenharmony_cimpls_stack_entry_policy[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1] = { 77562306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH] = { .type = NLA_U8 }, 77662306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL] = { .type = NLA_U8 }, 77762306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS] = { .type = NLA_U8 }, 77862306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_OPT_LSE_TC] = { .type = NLA_U8 }, 77962306a36Sopenharmony_ci [TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL] = { .type = NLA_U32 }, 78062306a36Sopenharmony_ci}; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic const struct nla_policy 78362306a36Sopenharmony_cicfm_opt_policy[TCA_FLOWER_KEY_CFM_OPT_MAX + 1] = { 78462306a36Sopenharmony_ci [TCA_FLOWER_KEY_CFM_MD_LEVEL] = NLA_POLICY_MAX(NLA_U8, 78562306a36Sopenharmony_ci FLOW_DIS_CFM_MDL_MAX), 78662306a36Sopenharmony_ci [TCA_FLOWER_KEY_CFM_OPCODE] = { .type = NLA_U8 }, 78762306a36Sopenharmony_ci}; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic void fl_set_key_val(struct nlattr **tb, 79062306a36Sopenharmony_ci void *val, int val_type, 79162306a36Sopenharmony_ci void *mask, int mask_type, int len) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci if (!tb[val_type]) 79462306a36Sopenharmony_ci return; 79562306a36Sopenharmony_ci nla_memcpy(val, tb[val_type], len); 79662306a36Sopenharmony_ci if (mask_type == TCA_FLOWER_UNSPEC || !tb[mask_type]) 79762306a36Sopenharmony_ci memset(mask, 0xff, len); 79862306a36Sopenharmony_ci else 79962306a36Sopenharmony_ci nla_memcpy(mask, tb[mask_type], len); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int fl_set_key_spi(struct nlattr **tb, struct fl_flow_key *key, 80362306a36Sopenharmony_ci struct fl_flow_key *mask, 80462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci if (key->basic.ip_proto != IPPROTO_ESP && 80762306a36Sopenharmony_ci key->basic.ip_proto != IPPROTO_AH) { 80862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 80962306a36Sopenharmony_ci "Protocol must be either ESP or AH"); 81062306a36Sopenharmony_ci return -EINVAL; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci fl_set_key_val(tb, &key->ipsec.spi, 81462306a36Sopenharmony_ci TCA_FLOWER_KEY_SPI, 81562306a36Sopenharmony_ci &mask->ipsec.spi, TCA_FLOWER_KEY_SPI_MASK, 81662306a36Sopenharmony_ci sizeof(key->ipsec.spi)); 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int fl_set_key_port_range(struct nlattr **tb, struct fl_flow_key *key, 82162306a36Sopenharmony_ci struct fl_flow_key *mask, 82262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp_range.tp_min.dst, 82562306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_DST_MIN, &mask->tp_range.tp_min.dst, 82662306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_min.dst)); 82762306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp_range.tp_max.dst, 82862306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_DST_MAX, &mask->tp_range.tp_max.dst, 82962306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_max.dst)); 83062306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp_range.tp_min.src, 83162306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_SRC_MIN, &mask->tp_range.tp_min.src, 83262306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_min.src)); 83362306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp_range.tp_max.src, 83462306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_SRC_MAX, &mask->tp_range.tp_max.src, 83562306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, sizeof(key->tp_range.tp_max.src)); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (mask->tp_range.tp_min.dst != mask->tp_range.tp_max.dst) { 83862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 83962306a36Sopenharmony_ci "Both min and max destination ports must be specified"); 84062306a36Sopenharmony_ci return -EINVAL; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci if (mask->tp_range.tp_min.src != mask->tp_range.tp_max.src) { 84362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 84462306a36Sopenharmony_ci "Both min and max source ports must be specified"); 84562306a36Sopenharmony_ci return -EINVAL; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci if (mask->tp_range.tp_min.dst && mask->tp_range.tp_max.dst && 84862306a36Sopenharmony_ci ntohs(key->tp_range.tp_max.dst) <= 84962306a36Sopenharmony_ci ntohs(key->tp_range.tp_min.dst)) { 85062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 85162306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_PORT_DST_MIN], 85262306a36Sopenharmony_ci "Invalid destination port range (min must be strictly smaller than max)"); 85362306a36Sopenharmony_ci return -EINVAL; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci if (mask->tp_range.tp_min.src && mask->tp_range.tp_max.src && 85662306a36Sopenharmony_ci ntohs(key->tp_range.tp_max.src) <= 85762306a36Sopenharmony_ci ntohs(key->tp_range.tp_min.src)) { 85862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 85962306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_PORT_SRC_MIN], 86062306a36Sopenharmony_ci "Invalid source port range (min must be strictly smaller than max)"); 86162306a36Sopenharmony_ci return -EINVAL; 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int fl_set_key_mpls_lse(const struct nlattr *nla_lse, 86862306a36Sopenharmony_ci struct flow_dissector_key_mpls *key_val, 86962306a36Sopenharmony_ci struct flow_dissector_key_mpls *key_mask, 87062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct nlattr *tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX + 1]; 87362306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_mask; 87462306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_val; 87562306a36Sopenharmony_ci u8 lse_index; 87662306a36Sopenharmony_ci u8 depth; 87762306a36Sopenharmony_ci int err; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci err = nla_parse_nested(tb, TCA_FLOWER_KEY_MPLS_OPT_LSE_MAX, nla_lse, 88062306a36Sopenharmony_ci mpls_stack_entry_policy, extack); 88162306a36Sopenharmony_ci if (err < 0) 88262306a36Sopenharmony_ci return err; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (!tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH]) { 88562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing MPLS option \"depth\""); 88662306a36Sopenharmony_ci return -EINVAL; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci depth = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH]); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* LSE depth starts at 1, for consistency with terminology used by 89262306a36Sopenharmony_ci * RFC 3031 (section 3.9), where depth 0 refers to unlabeled packets. 89362306a36Sopenharmony_ci */ 89462306a36Sopenharmony_ci if (depth < 1 || depth > FLOW_DIS_MPLS_MAX) { 89562306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 89662306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH], 89762306a36Sopenharmony_ci "Invalid MPLS depth"); 89862306a36Sopenharmony_ci return -EINVAL; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci lse_index = depth - 1; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci dissector_set_mpls_lse(key_val, lse_index); 90362306a36Sopenharmony_ci dissector_set_mpls_lse(key_mask, lse_index); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci lse_val = &key_val->ls[lse_index]; 90662306a36Sopenharmony_ci lse_mask = &key_mask->ls[lse_index]; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL]) { 90962306a36Sopenharmony_ci lse_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL]); 91062306a36Sopenharmony_ci lse_mask->mpls_ttl = MPLS_TTL_MASK; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS]) { 91362306a36Sopenharmony_ci u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS]); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (bos & ~MPLS_BOS_MASK) { 91662306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 91762306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS], 91862306a36Sopenharmony_ci "Bottom Of Stack (BOS) must be 0 or 1"); 91962306a36Sopenharmony_ci return -EINVAL; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci lse_val->mpls_bos = bos; 92262306a36Sopenharmony_ci lse_mask->mpls_bos = MPLS_BOS_MASK; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TC]) { 92562306a36Sopenharmony_ci u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TC]); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (tc & ~MPLS_TC_MASK) { 92862306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 92962306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_TC], 93062306a36Sopenharmony_ci "Traffic Class (TC) must be between 0 and 7"); 93162306a36Sopenharmony_ci return -EINVAL; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci lse_val->mpls_tc = tc; 93462306a36Sopenharmony_ci lse_mask->mpls_tc = MPLS_TC_MASK; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL]) { 93762306a36Sopenharmony_ci u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL]); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (label & ~MPLS_LABEL_MASK) { 94062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 94162306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL], 94262306a36Sopenharmony_ci "Label must be between 0 and 1048575"); 94362306a36Sopenharmony_ci return -EINVAL; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci lse_val->mpls_label = label; 94662306a36Sopenharmony_ci lse_mask->mpls_label = MPLS_LABEL_MASK; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci return 0; 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic int fl_set_key_mpls_opts(const struct nlattr *nla_mpls_opts, 95362306a36Sopenharmony_ci struct flow_dissector_key_mpls *key_val, 95462306a36Sopenharmony_ci struct flow_dissector_key_mpls *key_mask, 95562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct nlattr *nla_lse; 95862306a36Sopenharmony_ci int rem; 95962306a36Sopenharmony_ci int err; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (!(nla_mpls_opts->nla_type & NLA_F_NESTED)) { 96262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, nla_mpls_opts, 96362306a36Sopenharmony_ci "NLA_F_NESTED is missing"); 96462306a36Sopenharmony_ci return -EINVAL; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci nla_for_each_nested(nla_lse, nla_mpls_opts, rem) { 96862306a36Sopenharmony_ci if (nla_type(nla_lse) != TCA_FLOWER_KEY_MPLS_OPTS_LSE) { 96962306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, nla_lse, 97062306a36Sopenharmony_ci "Invalid MPLS option type"); 97162306a36Sopenharmony_ci return -EINVAL; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci err = fl_set_key_mpls_lse(nla_lse, key_val, key_mask, extack); 97562306a36Sopenharmony_ci if (err < 0) 97662306a36Sopenharmony_ci return err; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci if (rem) { 97962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 98062306a36Sopenharmony_ci "Bytes leftover after parsing MPLS options"); 98162306a36Sopenharmony_ci return -EINVAL; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic int fl_set_key_mpls(struct nlattr **tb, 98862306a36Sopenharmony_ci struct flow_dissector_key_mpls *key_val, 98962306a36Sopenharmony_ci struct flow_dissector_key_mpls *key_mask, 99062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_mask; 99362306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_val; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_OPTS]) { 99662306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_TTL] || 99762306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_BOS] || 99862306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_TC] || 99962306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_LABEL]) { 100062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 100162306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_OPTS], 100262306a36Sopenharmony_ci "MPLS label, Traffic Class, Bottom Of Stack and Time To Live must be encapsulated in the MPLS options attribute"); 100362306a36Sopenharmony_ci return -EBADMSG; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return fl_set_key_mpls_opts(tb[TCA_FLOWER_KEY_MPLS_OPTS], 100762306a36Sopenharmony_ci key_val, key_mask, extack); 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci lse_val = &key_val->ls[0]; 101162306a36Sopenharmony_ci lse_mask = &key_mask->ls[0]; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_TTL]) { 101462306a36Sopenharmony_ci lse_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]); 101562306a36Sopenharmony_ci lse_mask->mpls_ttl = MPLS_TTL_MASK; 101662306a36Sopenharmony_ci dissector_set_mpls_lse(key_val, 0); 101762306a36Sopenharmony_ci dissector_set_mpls_lse(key_mask, 0); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_BOS]) { 102062306a36Sopenharmony_ci u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (bos & ~MPLS_BOS_MASK) { 102362306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 102462306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_BOS], 102562306a36Sopenharmony_ci "Bottom Of Stack (BOS) must be 0 or 1"); 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci lse_val->mpls_bos = bos; 102962306a36Sopenharmony_ci lse_mask->mpls_bos = MPLS_BOS_MASK; 103062306a36Sopenharmony_ci dissector_set_mpls_lse(key_val, 0); 103162306a36Sopenharmony_ci dissector_set_mpls_lse(key_mask, 0); 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_TC]) { 103462306a36Sopenharmony_ci u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci if (tc & ~MPLS_TC_MASK) { 103762306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 103862306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_TC], 103962306a36Sopenharmony_ci "Traffic Class (TC) must be between 0 and 7"); 104062306a36Sopenharmony_ci return -EINVAL; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci lse_val->mpls_tc = tc; 104362306a36Sopenharmony_ci lse_mask->mpls_tc = MPLS_TC_MASK; 104462306a36Sopenharmony_ci dissector_set_mpls_lse(key_val, 0); 104562306a36Sopenharmony_ci dissector_set_mpls_lse(key_mask, 0); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) { 104862306a36Sopenharmony_ci u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (label & ~MPLS_LABEL_MASK) { 105162306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, 105262306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_MPLS_LABEL], 105362306a36Sopenharmony_ci "Label must be between 0 and 1048575"); 105462306a36Sopenharmony_ci return -EINVAL; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci lse_val->mpls_label = label; 105762306a36Sopenharmony_ci lse_mask->mpls_label = MPLS_LABEL_MASK; 105862306a36Sopenharmony_ci dissector_set_mpls_lse(key_val, 0); 105962306a36Sopenharmony_ci dissector_set_mpls_lse(key_mask, 0); 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci return 0; 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic void fl_set_key_vlan(struct nlattr **tb, 106562306a36Sopenharmony_ci __be16 ethertype, 106662306a36Sopenharmony_ci int vlan_id_key, int vlan_prio_key, 106762306a36Sopenharmony_ci int vlan_next_eth_type_key, 106862306a36Sopenharmony_ci struct flow_dissector_key_vlan *key_val, 106962306a36Sopenharmony_ci struct flow_dissector_key_vlan *key_mask) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci#define VLAN_PRIORITY_MASK 0x7 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (tb[vlan_id_key]) { 107462306a36Sopenharmony_ci key_val->vlan_id = 107562306a36Sopenharmony_ci nla_get_u16(tb[vlan_id_key]) & VLAN_VID_MASK; 107662306a36Sopenharmony_ci key_mask->vlan_id = VLAN_VID_MASK; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci if (tb[vlan_prio_key]) { 107962306a36Sopenharmony_ci key_val->vlan_priority = 108062306a36Sopenharmony_ci nla_get_u8(tb[vlan_prio_key]) & 108162306a36Sopenharmony_ci VLAN_PRIORITY_MASK; 108262306a36Sopenharmony_ci key_mask->vlan_priority = VLAN_PRIORITY_MASK; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci if (ethertype) { 108562306a36Sopenharmony_ci key_val->vlan_tpid = ethertype; 108662306a36Sopenharmony_ci key_mask->vlan_tpid = cpu_to_be16(~0); 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci if (tb[vlan_next_eth_type_key]) { 108962306a36Sopenharmony_ci key_val->vlan_eth_type = 109062306a36Sopenharmony_ci nla_get_be16(tb[vlan_next_eth_type_key]); 109162306a36Sopenharmony_ci key_mask->vlan_eth_type = cpu_to_be16(~0); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic void fl_set_key_pppoe(struct nlattr **tb, 109662306a36Sopenharmony_ci struct flow_dissector_key_pppoe *key_val, 109762306a36Sopenharmony_ci struct flow_dissector_key_pppoe *key_mask, 109862306a36Sopenharmony_ci struct fl_flow_key *key, 109962306a36Sopenharmony_ci struct fl_flow_key *mask) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci /* key_val::type must be set to ETH_P_PPP_SES 110262306a36Sopenharmony_ci * because ETH_P_PPP_SES was stored in basic.n_proto 110362306a36Sopenharmony_ci * which might get overwritten by ppp_proto 110462306a36Sopenharmony_ci * or might be set to 0, the role of key_val::type 110562306a36Sopenharmony_ci * is similar to vlan_key::tpid 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_ci key_val->type = htons(ETH_P_PPP_SES); 110862306a36Sopenharmony_ci key_mask->type = cpu_to_be16(~0); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_PPPOE_SID]) { 111162306a36Sopenharmony_ci key_val->session_id = 111262306a36Sopenharmony_ci nla_get_be16(tb[TCA_FLOWER_KEY_PPPOE_SID]); 111362306a36Sopenharmony_ci key_mask->session_id = cpu_to_be16(~0); 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_PPP_PROTO]) { 111662306a36Sopenharmony_ci key_val->ppp_proto = 111762306a36Sopenharmony_ci nla_get_be16(tb[TCA_FLOWER_KEY_PPP_PROTO]); 111862306a36Sopenharmony_ci key_mask->ppp_proto = cpu_to_be16(~0); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (key_val->ppp_proto == htons(PPP_IP)) { 112162306a36Sopenharmony_ci key->basic.n_proto = htons(ETH_P_IP); 112262306a36Sopenharmony_ci mask->basic.n_proto = cpu_to_be16(~0); 112362306a36Sopenharmony_ci } else if (key_val->ppp_proto == htons(PPP_IPV6)) { 112462306a36Sopenharmony_ci key->basic.n_proto = htons(ETH_P_IPV6); 112562306a36Sopenharmony_ci mask->basic.n_proto = cpu_to_be16(~0); 112662306a36Sopenharmony_ci } else if (key_val->ppp_proto == htons(PPP_MPLS_UC)) { 112762306a36Sopenharmony_ci key->basic.n_proto = htons(ETH_P_MPLS_UC); 112862306a36Sopenharmony_ci mask->basic.n_proto = cpu_to_be16(~0); 112962306a36Sopenharmony_ci } else if (key_val->ppp_proto == htons(PPP_MPLS_MC)) { 113062306a36Sopenharmony_ci key->basic.n_proto = htons(ETH_P_MPLS_MC); 113162306a36Sopenharmony_ci mask->basic.n_proto = cpu_to_be16(~0); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci } else { 113462306a36Sopenharmony_ci key->basic.n_proto = 0; 113562306a36Sopenharmony_ci mask->basic.n_proto = cpu_to_be16(0); 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic void fl_set_key_flag(u32 flower_key, u32 flower_mask, 114062306a36Sopenharmony_ci u32 *dissector_key, u32 *dissector_mask, 114162306a36Sopenharmony_ci u32 flower_flag_bit, u32 dissector_flag_bit) 114262306a36Sopenharmony_ci{ 114362306a36Sopenharmony_ci if (flower_mask & flower_flag_bit) { 114462306a36Sopenharmony_ci *dissector_mask |= dissector_flag_bit; 114562306a36Sopenharmony_ci if (flower_key & flower_flag_bit) 114662306a36Sopenharmony_ci *dissector_key |= dissector_flag_bit; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic int fl_set_key_flags(struct nlattr **tb, u32 *flags_key, 115162306a36Sopenharmony_ci u32 *flags_mask, struct netlink_ext_ack *extack) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci u32 key, mask; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* mask is mandatory for flags */ 115662306a36Sopenharmony_ci if (!tb[TCA_FLOWER_KEY_FLAGS_MASK]) { 115762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing flags mask"); 115862306a36Sopenharmony_ci return -EINVAL; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci key = be32_to_cpu(nla_get_be32(tb[TCA_FLOWER_KEY_FLAGS])); 116262306a36Sopenharmony_ci mask = be32_to_cpu(nla_get_be32(tb[TCA_FLOWER_KEY_FLAGS_MASK])); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci *flags_key = 0; 116562306a36Sopenharmony_ci *flags_mask = 0; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci fl_set_key_flag(key, mask, flags_key, flags_mask, 116862306a36Sopenharmony_ci TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 116962306a36Sopenharmony_ci fl_set_key_flag(key, mask, flags_key, flags_mask, 117062306a36Sopenharmony_ci TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 117162306a36Sopenharmony_ci FLOW_DIS_FIRST_FRAG); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci return 0; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic void fl_set_key_ip(struct nlattr **tb, bool encap, 117762306a36Sopenharmony_ci struct flow_dissector_key_ip *key, 117862306a36Sopenharmony_ci struct flow_dissector_key_ip *mask) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 118162306a36Sopenharmony_ci int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 118262306a36Sopenharmony_ci int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 118362306a36Sopenharmony_ci int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci fl_set_key_val(tb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)); 118662306a36Sopenharmony_ci fl_set_key_val(tb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl)); 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic int fl_set_geneve_opt(const struct nlattr *nla, struct fl_flow_key *key, 119062306a36Sopenharmony_ci int depth, int option_len, 119162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX + 1]; 119462306a36Sopenharmony_ci struct nlattr *class = NULL, *type = NULL, *data = NULL; 119562306a36Sopenharmony_ci struct geneve_opt *opt; 119662306a36Sopenharmony_ci int err, data_len = 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (option_len > sizeof(struct geneve_opt)) 119962306a36Sopenharmony_ci data_len = option_len - sizeof(struct geneve_opt); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (key->enc_opts.len > FLOW_DIS_TUN_OPTS_MAX - 4) 120262306a36Sopenharmony_ci return -ERANGE; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci opt = (struct geneve_opt *)&key->enc_opts.data[key->enc_opts.len]; 120562306a36Sopenharmony_ci memset(opt, 0xff, option_len); 120662306a36Sopenharmony_ci opt->length = data_len / 4; 120762306a36Sopenharmony_ci opt->r1 = 0; 120862306a36Sopenharmony_ci opt->r2 = 0; 120962306a36Sopenharmony_ci opt->r3 = 0; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* If no mask has been prodived we assume an exact match. */ 121262306a36Sopenharmony_ci if (!depth) 121362306a36Sopenharmony_ci return sizeof(struct geneve_opt) + data_len; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GENEVE) { 121662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Non-geneve option type for mask"); 121762306a36Sopenharmony_ci return -EINVAL; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, 122162306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX, 122262306a36Sopenharmony_ci nla, geneve_opt_policy, extack); 122362306a36Sopenharmony_ci if (err < 0) 122462306a36Sopenharmony_ci return err; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* We are not allowed to omit any of CLASS, TYPE or DATA 122762306a36Sopenharmony_ci * fields from the key. 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci if (!option_len && 123062306a36Sopenharmony_ci (!tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS] || 123162306a36Sopenharmony_ci !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE] || 123262306a36Sopenharmony_ci !tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA])) { 123362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data"); 123462306a36Sopenharmony_ci return -EINVAL; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* Omitting any of CLASS, TYPE or DATA fields is allowed 123862306a36Sopenharmony_ci * for the mask. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]) { 124162306a36Sopenharmony_ci int new_len = key->enc_opts.len; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci data = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA]; 124462306a36Sopenharmony_ci data_len = nla_len(data); 124562306a36Sopenharmony_ci if (data_len < 4) { 124662306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long"); 124762306a36Sopenharmony_ci return -ERANGE; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci if (data_len % 4) { 125062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long"); 125162306a36Sopenharmony_ci return -ERANGE; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci new_len += sizeof(struct geneve_opt) + data_len; 125562306a36Sopenharmony_ci BUILD_BUG_ON(FLOW_DIS_TUN_OPTS_MAX != IP_TUNNEL_OPTS_MAX); 125662306a36Sopenharmony_ci if (new_len > FLOW_DIS_TUN_OPTS_MAX) { 125762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Tunnel options exceeds max size"); 125862306a36Sopenharmony_ci return -ERANGE; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci opt->length = data_len / 4; 126162306a36Sopenharmony_ci memcpy(opt->opt_data, nla_data(data), data_len); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]) { 126562306a36Sopenharmony_ci class = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS]; 126662306a36Sopenharmony_ci opt->opt_class = nla_get_be16(class); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]) { 127062306a36Sopenharmony_ci type = tb[TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE]; 127162306a36Sopenharmony_ci opt->type = nla_get_u8(type); 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci return sizeof(struct geneve_opt) + data_len; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic int fl_set_vxlan_opt(const struct nlattr *nla, struct fl_flow_key *key, 127862306a36Sopenharmony_ci int depth, int option_len, 127962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX + 1]; 128262306a36Sopenharmony_ci struct vxlan_metadata *md; 128362306a36Sopenharmony_ci int err; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci md = (struct vxlan_metadata *)&key->enc_opts.data[key->enc_opts.len]; 128662306a36Sopenharmony_ci memset(md, 0xff, sizeof(*md)); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (!depth) 128962306a36Sopenharmony_ci return sizeof(*md); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_VXLAN) { 129262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Non-vxlan option type for mask"); 129362306a36Sopenharmony_ci return -EINVAL; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_VXLAN_MAX, nla, 129762306a36Sopenharmony_ci vxlan_opt_policy, extack); 129862306a36Sopenharmony_ci if (err < 0) 129962306a36Sopenharmony_ci return err; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (!option_len && !tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP]) { 130262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing tunnel key vxlan option gbp"); 130362306a36Sopenharmony_ci return -EINVAL; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP]) { 130762306a36Sopenharmony_ci md->gbp = nla_get_u32(tb[TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP]); 130862306a36Sopenharmony_ci md->gbp &= VXLAN_GBP_MASK; 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci return sizeof(*md); 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cistatic int fl_set_erspan_opt(const struct nlattr *nla, struct fl_flow_key *key, 131562306a36Sopenharmony_ci int depth, int option_len, 131662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX + 1]; 131962306a36Sopenharmony_ci struct erspan_metadata *md; 132062306a36Sopenharmony_ci int err; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci md = (struct erspan_metadata *)&key->enc_opts.data[key->enc_opts.len]; 132362306a36Sopenharmony_ci memset(md, 0xff, sizeof(*md)); 132462306a36Sopenharmony_ci md->version = 1; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (!depth) 132762306a36Sopenharmony_ci return sizeof(*md); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_ERSPAN) { 133062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Non-erspan option type for mask"); 133162306a36Sopenharmony_ci return -EINVAL; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX, nla, 133562306a36Sopenharmony_ci erspan_opt_policy, extack); 133662306a36Sopenharmony_ci if (err < 0) 133762306a36Sopenharmony_ci return err; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (!option_len && !tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER]) { 134062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option ver"); 134162306a36Sopenharmony_ci return -EINVAL; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER]) 134562306a36Sopenharmony_ci md->version = nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER]); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (md->version == 1) { 134862306a36Sopenharmony_ci if (!option_len && !tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX]) { 134962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option index"); 135062306a36Sopenharmony_ci return -EINVAL; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX]) { 135362306a36Sopenharmony_ci nla = tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX]; 135462306a36Sopenharmony_ci memset(&md->u, 0x00, sizeof(md->u)); 135562306a36Sopenharmony_ci md->u.index = nla_get_be32(nla); 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci } else if (md->version == 2) { 135862306a36Sopenharmony_ci if (!option_len && (!tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR] || 135962306a36Sopenharmony_ci !tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID])) { 136062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Missing tunnel key erspan option dir or hwid"); 136162306a36Sopenharmony_ci return -EINVAL; 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR]) { 136462306a36Sopenharmony_ci nla = tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR]; 136562306a36Sopenharmony_ci md->u.md2.dir = nla_get_u8(nla); 136662306a36Sopenharmony_ci } 136762306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID]) { 136862306a36Sopenharmony_ci nla = tb[TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID]; 136962306a36Sopenharmony_ci set_hwid(&md->u.md2, nla_get_u8(nla)); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci } else { 137262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Tunnel key erspan option ver is incorrect"); 137362306a36Sopenharmony_ci return -EINVAL; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci return sizeof(*md); 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic int fl_set_gtp_opt(const struct nlattr *nla, struct fl_flow_key *key, 138062306a36Sopenharmony_ci int depth, int option_len, 138162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct nlattr *tb[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1]; 138462306a36Sopenharmony_ci struct gtp_pdu_session_info *sinfo; 138562306a36Sopenharmony_ci u8 len = key->enc_opts.len; 138662306a36Sopenharmony_ci int err; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci sinfo = (struct gtp_pdu_session_info *)&key->enc_opts.data[len]; 138962306a36Sopenharmony_ci memset(sinfo, 0xff, option_len); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (!depth) 139262306a36Sopenharmony_ci return sizeof(*sinfo); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci if (nla_type(nla) != TCA_FLOWER_KEY_ENC_OPTS_GTP) { 139562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Non-gtp option type for mask"); 139662306a36Sopenharmony_ci return -EINVAL; 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci err = nla_parse_nested(tb, TCA_FLOWER_KEY_ENC_OPT_GTP_MAX, nla, 140062306a36Sopenharmony_ci gtp_opt_policy, extack); 140162306a36Sopenharmony_ci if (err < 0) 140262306a36Sopenharmony_ci return err; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (!option_len && 140562306a36Sopenharmony_ci (!tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE] || 140662306a36Sopenharmony_ci !tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI])) { 140762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 140862306a36Sopenharmony_ci "Missing tunnel key gtp option pdu type or qfi"); 140962306a36Sopenharmony_ci return -EINVAL; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]) 141362306a36Sopenharmony_ci sinfo->pdu_type = 141462306a36Sopenharmony_ci nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]) 141762306a36Sopenharmony_ci sinfo->qfi = nla_get_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci return sizeof(*sinfo); 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key, 142362306a36Sopenharmony_ci struct fl_flow_key *mask, 142462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci const struct nlattr *nla_enc_key, *nla_opt_key, *nla_opt_msk = NULL; 142762306a36Sopenharmony_ci int err, option_len, key_depth, msk_depth = 0; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci err = nla_validate_nested_deprecated(tb[TCA_FLOWER_KEY_ENC_OPTS], 143062306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_OPTS_MAX, 143162306a36Sopenharmony_ci enc_opts_policy, extack); 143262306a36Sopenharmony_ci if (err) 143362306a36Sopenharmony_ci return err; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci nla_enc_key = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS]); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]) { 143862306a36Sopenharmony_ci err = nla_validate_nested_deprecated(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK], 143962306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_OPTS_MAX, 144062306a36Sopenharmony_ci enc_opts_policy, extack); 144162306a36Sopenharmony_ci if (err) 144262306a36Sopenharmony_ci return err; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci nla_opt_msk = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); 144562306a36Sopenharmony_ci msk_depth = nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]); 144662306a36Sopenharmony_ci if (!nla_ok(nla_opt_msk, msk_depth)) { 144762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid nested attribute for masks"); 144862306a36Sopenharmony_ci return -EINVAL; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci nla_for_each_attr(nla_opt_key, nla_enc_key, 145362306a36Sopenharmony_ci nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS]), key_depth) { 145462306a36Sopenharmony_ci switch (nla_type(nla_opt_key)) { 145562306a36Sopenharmony_ci case TCA_FLOWER_KEY_ENC_OPTS_GENEVE: 145662306a36Sopenharmony_ci if (key->enc_opts.dst_opt_type && 145762306a36Sopenharmony_ci key->enc_opts.dst_opt_type != TUNNEL_GENEVE_OPT) { 145862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Duplicate type for geneve options"); 145962306a36Sopenharmony_ci return -EINVAL; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci option_len = 0; 146262306a36Sopenharmony_ci key->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT; 146362306a36Sopenharmony_ci option_len = fl_set_geneve_opt(nla_opt_key, key, 146462306a36Sopenharmony_ci key_depth, option_len, 146562306a36Sopenharmony_ci extack); 146662306a36Sopenharmony_ci if (option_len < 0) 146762306a36Sopenharmony_ci return option_len; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci key->enc_opts.len += option_len; 147062306a36Sopenharmony_ci /* At the same time we need to parse through the mask 147162306a36Sopenharmony_ci * in order to verify exact and mask attribute lengths. 147262306a36Sopenharmony_ci */ 147362306a36Sopenharmony_ci mask->enc_opts.dst_opt_type = TUNNEL_GENEVE_OPT; 147462306a36Sopenharmony_ci option_len = fl_set_geneve_opt(nla_opt_msk, mask, 147562306a36Sopenharmony_ci msk_depth, option_len, 147662306a36Sopenharmony_ci extack); 147762306a36Sopenharmony_ci if (option_len < 0) 147862306a36Sopenharmony_ci return option_len; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci mask->enc_opts.len += option_len; 148162306a36Sopenharmony_ci if (key->enc_opts.len != mask->enc_opts.len) { 148262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Key and mask miss aligned"); 148362306a36Sopenharmony_ci return -EINVAL; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci case TCA_FLOWER_KEY_ENC_OPTS_VXLAN: 148762306a36Sopenharmony_ci if (key->enc_opts.dst_opt_type) { 148862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Duplicate type for vxlan options"); 148962306a36Sopenharmony_ci return -EINVAL; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci option_len = 0; 149262306a36Sopenharmony_ci key->enc_opts.dst_opt_type = TUNNEL_VXLAN_OPT; 149362306a36Sopenharmony_ci option_len = fl_set_vxlan_opt(nla_opt_key, key, 149462306a36Sopenharmony_ci key_depth, option_len, 149562306a36Sopenharmony_ci extack); 149662306a36Sopenharmony_ci if (option_len < 0) 149762306a36Sopenharmony_ci return option_len; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci key->enc_opts.len += option_len; 150062306a36Sopenharmony_ci /* At the same time we need to parse through the mask 150162306a36Sopenharmony_ci * in order to verify exact and mask attribute lengths. 150262306a36Sopenharmony_ci */ 150362306a36Sopenharmony_ci mask->enc_opts.dst_opt_type = TUNNEL_VXLAN_OPT; 150462306a36Sopenharmony_ci option_len = fl_set_vxlan_opt(nla_opt_msk, mask, 150562306a36Sopenharmony_ci msk_depth, option_len, 150662306a36Sopenharmony_ci extack); 150762306a36Sopenharmony_ci if (option_len < 0) 150862306a36Sopenharmony_ci return option_len; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci mask->enc_opts.len += option_len; 151162306a36Sopenharmony_ci if (key->enc_opts.len != mask->enc_opts.len) { 151262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Key and mask miss aligned"); 151362306a36Sopenharmony_ci return -EINVAL; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci break; 151662306a36Sopenharmony_ci case TCA_FLOWER_KEY_ENC_OPTS_ERSPAN: 151762306a36Sopenharmony_ci if (key->enc_opts.dst_opt_type) { 151862306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Duplicate type for erspan options"); 151962306a36Sopenharmony_ci return -EINVAL; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci option_len = 0; 152262306a36Sopenharmony_ci key->enc_opts.dst_opt_type = TUNNEL_ERSPAN_OPT; 152362306a36Sopenharmony_ci option_len = fl_set_erspan_opt(nla_opt_key, key, 152462306a36Sopenharmony_ci key_depth, option_len, 152562306a36Sopenharmony_ci extack); 152662306a36Sopenharmony_ci if (option_len < 0) 152762306a36Sopenharmony_ci return option_len; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci key->enc_opts.len += option_len; 153062306a36Sopenharmony_ci /* At the same time we need to parse through the mask 153162306a36Sopenharmony_ci * in order to verify exact and mask attribute lengths. 153262306a36Sopenharmony_ci */ 153362306a36Sopenharmony_ci mask->enc_opts.dst_opt_type = TUNNEL_ERSPAN_OPT; 153462306a36Sopenharmony_ci option_len = fl_set_erspan_opt(nla_opt_msk, mask, 153562306a36Sopenharmony_ci msk_depth, option_len, 153662306a36Sopenharmony_ci extack); 153762306a36Sopenharmony_ci if (option_len < 0) 153862306a36Sopenharmony_ci return option_len; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci mask->enc_opts.len += option_len; 154162306a36Sopenharmony_ci if (key->enc_opts.len != mask->enc_opts.len) { 154262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Key and mask miss aligned"); 154362306a36Sopenharmony_ci return -EINVAL; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci break; 154662306a36Sopenharmony_ci case TCA_FLOWER_KEY_ENC_OPTS_GTP: 154762306a36Sopenharmony_ci if (key->enc_opts.dst_opt_type) { 154862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 154962306a36Sopenharmony_ci "Duplicate type for gtp options"); 155062306a36Sopenharmony_ci return -EINVAL; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci option_len = 0; 155362306a36Sopenharmony_ci key->enc_opts.dst_opt_type = TUNNEL_GTP_OPT; 155462306a36Sopenharmony_ci option_len = fl_set_gtp_opt(nla_opt_key, key, 155562306a36Sopenharmony_ci key_depth, option_len, 155662306a36Sopenharmony_ci extack); 155762306a36Sopenharmony_ci if (option_len < 0) 155862306a36Sopenharmony_ci return option_len; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci key->enc_opts.len += option_len; 156162306a36Sopenharmony_ci /* At the same time we need to parse through the mask 156262306a36Sopenharmony_ci * in order to verify exact and mask attribute lengths. 156362306a36Sopenharmony_ci */ 156462306a36Sopenharmony_ci mask->enc_opts.dst_opt_type = TUNNEL_GTP_OPT; 156562306a36Sopenharmony_ci option_len = fl_set_gtp_opt(nla_opt_msk, mask, 156662306a36Sopenharmony_ci msk_depth, option_len, 156762306a36Sopenharmony_ci extack); 156862306a36Sopenharmony_ci if (option_len < 0) 156962306a36Sopenharmony_ci return option_len; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci mask->enc_opts.len += option_len; 157262306a36Sopenharmony_ci if (key->enc_opts.len != mask->enc_opts.len) { 157362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 157462306a36Sopenharmony_ci "Key and mask miss aligned"); 157562306a36Sopenharmony_ci return -EINVAL; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci break; 157862306a36Sopenharmony_ci default: 157962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Unknown tunnel option type"); 158062306a36Sopenharmony_ci return -EINVAL; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci if (!msk_depth) 158462306a36Sopenharmony_ci continue; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci if (!nla_ok(nla_opt_msk, msk_depth)) { 158762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "A mask attribute is invalid"); 158862306a36Sopenharmony_ci return -EINVAL; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci nla_opt_msk = nla_next(nla_opt_msk, &msk_depth); 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci return 0; 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic int fl_validate_ct_state(u16 state, struct nlattr *tb, 159762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci if (state && !(state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED)) { 160062306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb, 160162306a36Sopenharmony_ci "no trk, so no other flag can be set"); 160262306a36Sopenharmony_ci return -EINVAL; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (state & TCA_FLOWER_KEY_CT_FLAGS_NEW && 160662306a36Sopenharmony_ci state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) { 160762306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb, 160862306a36Sopenharmony_ci "new and est are mutually exclusive"); 160962306a36Sopenharmony_ci return -EINVAL; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (state & TCA_FLOWER_KEY_CT_FLAGS_INVALID && 161362306a36Sopenharmony_ci state & ~(TCA_FLOWER_KEY_CT_FLAGS_TRACKED | 161462306a36Sopenharmony_ci TCA_FLOWER_KEY_CT_FLAGS_INVALID)) { 161562306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb, 161662306a36Sopenharmony_ci "when inv is set, only trk may be set"); 161762306a36Sopenharmony_ci return -EINVAL; 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (state & TCA_FLOWER_KEY_CT_FLAGS_NEW && 162162306a36Sopenharmony_ci state & TCA_FLOWER_KEY_CT_FLAGS_REPLY) { 162262306a36Sopenharmony_ci NL_SET_ERR_MSG_ATTR(extack, tb, 162362306a36Sopenharmony_ci "new and rpl are mutually exclusive"); 162462306a36Sopenharmony_ci return -EINVAL; 162562306a36Sopenharmony_ci } 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci return 0; 162862306a36Sopenharmony_ci} 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_cistatic int fl_set_key_ct(struct nlattr **tb, 163162306a36Sopenharmony_ci struct flow_dissector_key_ct *key, 163262306a36Sopenharmony_ci struct flow_dissector_key_ct *mask, 163362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 163462306a36Sopenharmony_ci{ 163562306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_CT_STATE]) { 163662306a36Sopenharmony_ci int err; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_NF_CONNTRACK)) { 163962306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Conntrack isn't enabled"); 164062306a36Sopenharmony_ci return -EOPNOTSUPP; 164162306a36Sopenharmony_ci } 164262306a36Sopenharmony_ci fl_set_key_val(tb, &key->ct_state, TCA_FLOWER_KEY_CT_STATE, 164362306a36Sopenharmony_ci &mask->ct_state, TCA_FLOWER_KEY_CT_STATE_MASK, 164462306a36Sopenharmony_ci sizeof(key->ct_state)); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci err = fl_validate_ct_state(key->ct_state & mask->ct_state, 164762306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_CT_STATE_MASK], 164862306a36Sopenharmony_ci extack); 164962306a36Sopenharmony_ci if (err) 165062306a36Sopenharmony_ci return err; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_CT_ZONE]) { 165462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)) { 165562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Conntrack zones isn't enabled"); 165662306a36Sopenharmony_ci return -EOPNOTSUPP; 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci fl_set_key_val(tb, &key->ct_zone, TCA_FLOWER_KEY_CT_ZONE, 165962306a36Sopenharmony_ci &mask->ct_zone, TCA_FLOWER_KEY_CT_ZONE_MASK, 166062306a36Sopenharmony_ci sizeof(key->ct_zone)); 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_CT_MARK]) { 166362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)) { 166462306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Conntrack mark isn't enabled"); 166562306a36Sopenharmony_ci return -EOPNOTSUPP; 166662306a36Sopenharmony_ci } 166762306a36Sopenharmony_ci fl_set_key_val(tb, &key->ct_mark, TCA_FLOWER_KEY_CT_MARK, 166862306a36Sopenharmony_ci &mask->ct_mark, TCA_FLOWER_KEY_CT_MARK_MASK, 166962306a36Sopenharmony_ci sizeof(key->ct_mark)); 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_CT_LABELS]) { 167262306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS)) { 167362306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Conntrack labels aren't enabled"); 167462306a36Sopenharmony_ci return -EOPNOTSUPP; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci fl_set_key_val(tb, key->ct_labels, TCA_FLOWER_KEY_CT_LABELS, 167762306a36Sopenharmony_ci mask->ct_labels, TCA_FLOWER_KEY_CT_LABELS_MASK, 167862306a36Sopenharmony_ci sizeof(key->ct_labels)); 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci return 0; 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic bool is_vlan_key(struct nlattr *tb, __be16 *ethertype, 168562306a36Sopenharmony_ci struct fl_flow_key *key, struct fl_flow_key *mask, 168662306a36Sopenharmony_ci int vthresh) 168762306a36Sopenharmony_ci{ 168862306a36Sopenharmony_ci const bool good_num_of_vlans = key->num_of_vlans.num_of_vlans > vthresh; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci if (!tb) { 169162306a36Sopenharmony_ci *ethertype = 0; 169262306a36Sopenharmony_ci return good_num_of_vlans; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci *ethertype = nla_get_be16(tb); 169662306a36Sopenharmony_ci if (good_num_of_vlans || eth_type_vlan(*ethertype)) 169762306a36Sopenharmony_ci return true; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci key->basic.n_proto = *ethertype; 170062306a36Sopenharmony_ci mask->basic.n_proto = cpu_to_be16(~0); 170162306a36Sopenharmony_ci return false; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_cistatic void fl_set_key_cfm_md_level(struct nlattr **tb, 170562306a36Sopenharmony_ci struct fl_flow_key *key, 170662306a36Sopenharmony_ci struct fl_flow_key *mask, 170762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci u8 level; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (!tb[TCA_FLOWER_KEY_CFM_MD_LEVEL]) 171262306a36Sopenharmony_ci return; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci level = nla_get_u8(tb[TCA_FLOWER_KEY_CFM_MD_LEVEL]); 171562306a36Sopenharmony_ci key->cfm.mdl_ver = FIELD_PREP(FLOW_DIS_CFM_MDL_MASK, level); 171662306a36Sopenharmony_ci mask->cfm.mdl_ver = FLOW_DIS_CFM_MDL_MASK; 171762306a36Sopenharmony_ci} 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_cistatic void fl_set_key_cfm_opcode(struct nlattr **tb, 172062306a36Sopenharmony_ci struct fl_flow_key *key, 172162306a36Sopenharmony_ci struct fl_flow_key *mask, 172262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 172362306a36Sopenharmony_ci{ 172462306a36Sopenharmony_ci fl_set_key_val(tb, &key->cfm.opcode, TCA_FLOWER_KEY_CFM_OPCODE, 172562306a36Sopenharmony_ci &mask->cfm.opcode, TCA_FLOWER_UNSPEC, 172662306a36Sopenharmony_ci sizeof(key->cfm.opcode)); 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_cistatic int fl_set_key_cfm(struct nlattr **tb, 173062306a36Sopenharmony_ci struct fl_flow_key *key, 173162306a36Sopenharmony_ci struct fl_flow_key *mask, 173262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci struct nlattr *nla_cfm_opt[TCA_FLOWER_KEY_CFM_OPT_MAX + 1]; 173562306a36Sopenharmony_ci int err; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (!tb[TCA_FLOWER_KEY_CFM]) 173862306a36Sopenharmony_ci return 0; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci err = nla_parse_nested(nla_cfm_opt, TCA_FLOWER_KEY_CFM_OPT_MAX, 174162306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_CFM], cfm_opt_policy, extack); 174262306a36Sopenharmony_ci if (err < 0) 174362306a36Sopenharmony_ci return err; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci fl_set_key_cfm_opcode(nla_cfm_opt, key, mask, extack); 174662306a36Sopenharmony_ci fl_set_key_cfm_md_level(nla_cfm_opt, key, mask, extack); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci return 0; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic int fl_set_key(struct net *net, struct nlattr **tb, 175262306a36Sopenharmony_ci struct fl_flow_key *key, struct fl_flow_key *mask, 175362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci __be16 ethertype; 175662306a36Sopenharmony_ci int ret = 0; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (tb[TCA_FLOWER_INDEV]) { 175962306a36Sopenharmony_ci int err = tcf_change_indev(net, tb[TCA_FLOWER_INDEV], extack); 176062306a36Sopenharmony_ci if (err < 0) 176162306a36Sopenharmony_ci return err; 176262306a36Sopenharmony_ci key->meta.ingress_ifindex = err; 176362306a36Sopenharmony_ci mask->meta.ingress_ifindex = 0xffffffff; 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci fl_set_key_val(tb, &key->meta.l2_miss, TCA_FLOWER_L2_MISS, 176762306a36Sopenharmony_ci &mask->meta.l2_miss, TCA_FLOWER_UNSPEC, 176862306a36Sopenharmony_ci sizeof(key->meta.l2_miss)); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci fl_set_key_val(tb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 177162306a36Sopenharmony_ci mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 177262306a36Sopenharmony_ci sizeof(key->eth.dst)); 177362306a36Sopenharmony_ci fl_set_key_val(tb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 177462306a36Sopenharmony_ci mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 177562306a36Sopenharmony_ci sizeof(key->eth.src)); 177662306a36Sopenharmony_ci fl_set_key_val(tb, &key->num_of_vlans, 177762306a36Sopenharmony_ci TCA_FLOWER_KEY_NUM_OF_VLANS, 177862306a36Sopenharmony_ci &mask->num_of_vlans, 177962306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, 178062306a36Sopenharmony_ci sizeof(key->num_of_vlans)); 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci if (is_vlan_key(tb[TCA_FLOWER_KEY_ETH_TYPE], ðertype, key, mask, 0)) { 178362306a36Sopenharmony_ci fl_set_key_vlan(tb, ethertype, TCA_FLOWER_KEY_VLAN_ID, 178462306a36Sopenharmony_ci TCA_FLOWER_KEY_VLAN_PRIO, 178562306a36Sopenharmony_ci TCA_FLOWER_KEY_VLAN_ETH_TYPE, 178662306a36Sopenharmony_ci &key->vlan, &mask->vlan); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci if (is_vlan_key(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE], 178962306a36Sopenharmony_ci ðertype, key, mask, 1)) { 179062306a36Sopenharmony_ci fl_set_key_vlan(tb, ethertype, 179162306a36Sopenharmony_ci TCA_FLOWER_KEY_CVLAN_ID, 179262306a36Sopenharmony_ci TCA_FLOWER_KEY_CVLAN_PRIO, 179362306a36Sopenharmony_ci TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 179462306a36Sopenharmony_ci &key->cvlan, &mask->cvlan); 179562306a36Sopenharmony_ci fl_set_key_val(tb, &key->basic.n_proto, 179662306a36Sopenharmony_ci TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 179762306a36Sopenharmony_ci &mask->basic.n_proto, 179862306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, 179962306a36Sopenharmony_ci sizeof(key->basic.n_proto)); 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (key->basic.n_proto == htons(ETH_P_PPP_SES)) 180462306a36Sopenharmony_ci fl_set_key_pppoe(tb, &key->pppoe, &mask->pppoe, key, mask); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci if (key->basic.n_proto == htons(ETH_P_IP) || 180762306a36Sopenharmony_ci key->basic.n_proto == htons(ETH_P_IPV6)) { 180862306a36Sopenharmony_ci fl_set_key_val(tb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 180962306a36Sopenharmony_ci &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 181062306a36Sopenharmony_ci sizeof(key->basic.ip_proto)); 181162306a36Sopenharmony_ci fl_set_key_ip(tb, false, &key->ip, &mask->ip); 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_IPV4_SRC] || tb[TCA_FLOWER_KEY_IPV4_DST]) { 181562306a36Sopenharmony_ci key->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 181662306a36Sopenharmony_ci mask->control.addr_type = ~0; 181762306a36Sopenharmony_ci fl_set_key_val(tb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 181862306a36Sopenharmony_ci &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 181962306a36Sopenharmony_ci sizeof(key->ipv4.src)); 182062306a36Sopenharmony_ci fl_set_key_val(tb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 182162306a36Sopenharmony_ci &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 182262306a36Sopenharmony_ci sizeof(key->ipv4.dst)); 182362306a36Sopenharmony_ci } else if (tb[TCA_FLOWER_KEY_IPV6_SRC] || tb[TCA_FLOWER_KEY_IPV6_DST]) { 182462306a36Sopenharmony_ci key->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 182562306a36Sopenharmony_ci mask->control.addr_type = ~0; 182662306a36Sopenharmony_ci fl_set_key_val(tb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 182762306a36Sopenharmony_ci &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 182862306a36Sopenharmony_ci sizeof(key->ipv6.src)); 182962306a36Sopenharmony_ci fl_set_key_val(tb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 183062306a36Sopenharmony_ci &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 183162306a36Sopenharmony_ci sizeof(key->ipv6.dst)); 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci if (key->basic.ip_proto == IPPROTO_TCP) { 183562306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 183662306a36Sopenharmony_ci &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 183762306a36Sopenharmony_ci sizeof(key->tp.src)); 183862306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 183962306a36Sopenharmony_ci &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 184062306a36Sopenharmony_ci sizeof(key->tp.dst)); 184162306a36Sopenharmony_ci fl_set_key_val(tb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 184262306a36Sopenharmony_ci &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 184362306a36Sopenharmony_ci sizeof(key->tcp.flags)); 184462306a36Sopenharmony_ci } else if (key->basic.ip_proto == IPPROTO_UDP) { 184562306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 184662306a36Sopenharmony_ci &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 184762306a36Sopenharmony_ci sizeof(key->tp.src)); 184862306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 184962306a36Sopenharmony_ci &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 185062306a36Sopenharmony_ci sizeof(key->tp.dst)); 185162306a36Sopenharmony_ci } else if (key->basic.ip_proto == IPPROTO_SCTP) { 185262306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 185362306a36Sopenharmony_ci &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 185462306a36Sopenharmony_ci sizeof(key->tp.src)); 185562306a36Sopenharmony_ci fl_set_key_val(tb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 185662306a36Sopenharmony_ci &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 185762306a36Sopenharmony_ci sizeof(key->tp.dst)); 185862306a36Sopenharmony_ci } else if (key->basic.n_proto == htons(ETH_P_IP) && 185962306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_ICMP) { 186062306a36Sopenharmony_ci fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV4_TYPE, 186162306a36Sopenharmony_ci &mask->icmp.type, 186262306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 186362306a36Sopenharmony_ci sizeof(key->icmp.type)); 186462306a36Sopenharmony_ci fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV4_CODE, 186562306a36Sopenharmony_ci &mask->icmp.code, 186662306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 186762306a36Sopenharmony_ci sizeof(key->icmp.code)); 186862306a36Sopenharmony_ci } else if (key->basic.n_proto == htons(ETH_P_IPV6) && 186962306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_ICMPV6) { 187062306a36Sopenharmony_ci fl_set_key_val(tb, &key->icmp.type, TCA_FLOWER_KEY_ICMPV6_TYPE, 187162306a36Sopenharmony_ci &mask->icmp.type, 187262306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 187362306a36Sopenharmony_ci sizeof(key->icmp.type)); 187462306a36Sopenharmony_ci fl_set_key_val(tb, &key->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE, 187562306a36Sopenharmony_ci &mask->icmp.code, 187662306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 187762306a36Sopenharmony_ci sizeof(key->icmp.code)); 187862306a36Sopenharmony_ci } else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) || 187962306a36Sopenharmony_ci key->basic.n_proto == htons(ETH_P_MPLS_MC)) { 188062306a36Sopenharmony_ci ret = fl_set_key_mpls(tb, &key->mpls, &mask->mpls, extack); 188162306a36Sopenharmony_ci if (ret) 188262306a36Sopenharmony_ci return ret; 188362306a36Sopenharmony_ci } else if (key->basic.n_proto == htons(ETH_P_ARP) || 188462306a36Sopenharmony_ci key->basic.n_proto == htons(ETH_P_RARP)) { 188562306a36Sopenharmony_ci fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, 188662306a36Sopenharmony_ci &mask->arp.sip, TCA_FLOWER_KEY_ARP_SIP_MASK, 188762306a36Sopenharmony_ci sizeof(key->arp.sip)); 188862306a36Sopenharmony_ci fl_set_key_val(tb, &key->arp.tip, TCA_FLOWER_KEY_ARP_TIP, 188962306a36Sopenharmony_ci &mask->arp.tip, TCA_FLOWER_KEY_ARP_TIP_MASK, 189062306a36Sopenharmony_ci sizeof(key->arp.tip)); 189162306a36Sopenharmony_ci fl_set_key_val(tb, &key->arp.op, TCA_FLOWER_KEY_ARP_OP, 189262306a36Sopenharmony_ci &mask->arp.op, TCA_FLOWER_KEY_ARP_OP_MASK, 189362306a36Sopenharmony_ci sizeof(key->arp.op)); 189462306a36Sopenharmony_ci fl_set_key_val(tb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 189562306a36Sopenharmony_ci mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 189662306a36Sopenharmony_ci sizeof(key->arp.sha)); 189762306a36Sopenharmony_ci fl_set_key_val(tb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 189862306a36Sopenharmony_ci mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 189962306a36Sopenharmony_ci sizeof(key->arp.tha)); 190062306a36Sopenharmony_ci } else if (key->basic.ip_proto == IPPROTO_L2TP) { 190162306a36Sopenharmony_ci fl_set_key_val(tb, &key->l2tpv3.session_id, 190262306a36Sopenharmony_ci TCA_FLOWER_KEY_L2TPV3_SID, 190362306a36Sopenharmony_ci &mask->l2tpv3.session_id, TCA_FLOWER_UNSPEC, 190462306a36Sopenharmony_ci sizeof(key->l2tpv3.session_id)); 190562306a36Sopenharmony_ci } else if (key->basic.n_proto == htons(ETH_P_CFM)) { 190662306a36Sopenharmony_ci ret = fl_set_key_cfm(tb, key, mask, extack); 190762306a36Sopenharmony_ci if (ret) 190862306a36Sopenharmony_ci return ret; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci if (key->basic.ip_proto == IPPROTO_TCP || 191262306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_UDP || 191362306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_SCTP) { 191462306a36Sopenharmony_ci ret = fl_set_key_port_range(tb, key, mask, extack); 191562306a36Sopenharmony_ci if (ret) 191662306a36Sopenharmony_ci return ret; 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_SPI]) { 192062306a36Sopenharmony_ci ret = fl_set_key_spi(tb, key, mask, extack); 192162306a36Sopenharmony_ci if (ret) 192262306a36Sopenharmony_ci return ret; 192362306a36Sopenharmony_ci } 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_IPV4_SRC] || 192662306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_ENC_IPV4_DST]) { 192762306a36Sopenharmony_ci key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; 192862306a36Sopenharmony_ci mask->enc_control.addr_type = ~0; 192962306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_ipv4.src, 193062306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_SRC, 193162306a36Sopenharmony_ci &mask->enc_ipv4.src, 193262306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 193362306a36Sopenharmony_ci sizeof(key->enc_ipv4.src)); 193462306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_ipv4.dst, 193562306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_DST, 193662306a36Sopenharmony_ci &mask->enc_ipv4.dst, 193762306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 193862306a36Sopenharmony_ci sizeof(key->enc_ipv4.dst)); 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_IPV6_SRC] || 194262306a36Sopenharmony_ci tb[TCA_FLOWER_KEY_ENC_IPV6_DST]) { 194362306a36Sopenharmony_ci key->enc_control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; 194462306a36Sopenharmony_ci mask->enc_control.addr_type = ~0; 194562306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_ipv6.src, 194662306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_SRC, 194762306a36Sopenharmony_ci &mask->enc_ipv6.src, 194862306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 194962306a36Sopenharmony_ci sizeof(key->enc_ipv6.src)); 195062306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_ipv6.dst, 195162306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_DST, 195262306a36Sopenharmony_ci &mask->enc_ipv6.dst, 195362306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 195462306a36Sopenharmony_ci sizeof(key->enc_ipv6.dst)); 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_key_id.keyid, TCA_FLOWER_KEY_ENC_KEY_ID, 195862306a36Sopenharmony_ci &mask->enc_key_id.keyid, TCA_FLOWER_UNSPEC, 195962306a36Sopenharmony_ci sizeof(key->enc_key_id.keyid)); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 196262306a36Sopenharmony_ci &mask->enc_tp.src, TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 196362306a36Sopenharmony_ci sizeof(key->enc_tp.src)); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci fl_set_key_val(tb, &key->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 196662306a36Sopenharmony_ci &mask->enc_tp.dst, TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 196762306a36Sopenharmony_ci sizeof(key->enc_tp.dst)); 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci fl_set_key_ip(tb, true, &key->enc_ip, &mask->enc_ip); 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci fl_set_key_val(tb, &key->hash.hash, TCA_FLOWER_KEY_HASH, 197262306a36Sopenharmony_ci &mask->hash.hash, TCA_FLOWER_KEY_HASH_MASK, 197362306a36Sopenharmony_ci sizeof(key->hash.hash)); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_ENC_OPTS]) { 197662306a36Sopenharmony_ci ret = fl_set_enc_opt(tb, key, mask, extack); 197762306a36Sopenharmony_ci if (ret) 197862306a36Sopenharmony_ci return ret; 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci ret = fl_set_key_ct(tb, &key->ct, &mask->ct, extack); 198262306a36Sopenharmony_ci if (ret) 198362306a36Sopenharmony_ci return ret; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci if (tb[TCA_FLOWER_KEY_FLAGS]) 198662306a36Sopenharmony_ci ret = fl_set_key_flags(tb, &key->control.flags, 198762306a36Sopenharmony_ci &mask->control.flags, extack); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci return ret; 199062306a36Sopenharmony_ci} 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_cistatic void fl_mask_copy(struct fl_flow_mask *dst, 199362306a36Sopenharmony_ci struct fl_flow_mask *src) 199462306a36Sopenharmony_ci{ 199562306a36Sopenharmony_ci const void *psrc = fl_key_get_start(&src->key, src); 199662306a36Sopenharmony_ci void *pdst = fl_key_get_start(&dst->key, src); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci memcpy(pdst, psrc, fl_mask_range(src)); 199962306a36Sopenharmony_ci dst->range = src->range; 200062306a36Sopenharmony_ci} 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_cistatic const struct rhashtable_params fl_ht_params = { 200362306a36Sopenharmony_ci .key_offset = offsetof(struct cls_fl_filter, mkey), /* base offset */ 200462306a36Sopenharmony_ci .head_offset = offsetof(struct cls_fl_filter, ht_node), 200562306a36Sopenharmony_ci .automatic_shrinking = true, 200662306a36Sopenharmony_ci}; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_cistatic int fl_init_mask_hashtable(struct fl_flow_mask *mask) 200962306a36Sopenharmony_ci{ 201062306a36Sopenharmony_ci mask->filter_ht_params = fl_ht_params; 201162306a36Sopenharmony_ci mask->filter_ht_params.key_len = fl_mask_range(mask); 201262306a36Sopenharmony_ci mask->filter_ht_params.key_offset += mask->range.start; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci return rhashtable_init(&mask->ht, &mask->filter_ht_params); 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci#define FL_KEY_MEMBER_OFFSET(member) offsetof(struct fl_flow_key, member) 201862306a36Sopenharmony_ci#define FL_KEY_MEMBER_SIZE(member) sizeof_field(struct fl_flow_key, member) 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci#define FL_KEY_IS_MASKED(mask, member) \ 202162306a36Sopenharmony_ci memchr_inv(((char *)mask) + FL_KEY_MEMBER_OFFSET(member), \ 202262306a36Sopenharmony_ci 0, FL_KEY_MEMBER_SIZE(member)) \ 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci#define FL_KEY_SET(keys, cnt, id, member) \ 202562306a36Sopenharmony_ci do { \ 202662306a36Sopenharmony_ci keys[cnt].key_id = id; \ 202762306a36Sopenharmony_ci keys[cnt].offset = FL_KEY_MEMBER_OFFSET(member); \ 202862306a36Sopenharmony_ci cnt++; \ 202962306a36Sopenharmony_ci } while(0); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci#define FL_KEY_SET_IF_MASKED(mask, keys, cnt, id, member) \ 203262306a36Sopenharmony_ci do { \ 203362306a36Sopenharmony_ci if (FL_KEY_IS_MASKED(mask, member)) \ 203462306a36Sopenharmony_ci FL_KEY_SET(keys, cnt, id, member); \ 203562306a36Sopenharmony_ci } while(0); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_cistatic void fl_init_dissector(struct flow_dissector *dissector, 203862306a36Sopenharmony_ci struct fl_flow_key *mask) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct flow_dissector_key keys[FLOW_DISSECTOR_KEY_MAX]; 204162306a36Sopenharmony_ci size_t cnt = 0; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 204462306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_META, meta); 204562306a36Sopenharmony_ci FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_CONTROL, control); 204662306a36Sopenharmony_ci FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_BASIC, basic); 204762306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 204862306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ETH_ADDRS, eth); 204962306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 205062306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_IPV4_ADDRS, ipv4); 205162306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 205262306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_IPV6_ADDRS, ipv6); 205362306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 205462306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_PORTS, tp); 205562306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 205662306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_PORTS_RANGE, tp_range); 205762306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 205862306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_IP, ip); 205962306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 206062306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_TCP, tcp); 206162306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 206262306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ICMP, icmp); 206362306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 206462306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ARP, arp); 206562306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 206662306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_MPLS, mpls); 206762306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 206862306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_VLAN, vlan); 206962306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 207062306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_CVLAN, cvlan); 207162306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 207262306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_KEYID, enc_key_id); 207362306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 207462306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, enc_ipv4); 207562306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 207662306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, enc_ipv6); 207762306a36Sopenharmony_ci if (FL_KEY_IS_MASKED(mask, enc_ipv4) || 207862306a36Sopenharmony_ci FL_KEY_IS_MASKED(mask, enc_ipv6)) 207962306a36Sopenharmony_ci FL_KEY_SET(keys, cnt, FLOW_DISSECTOR_KEY_ENC_CONTROL, 208062306a36Sopenharmony_ci enc_control); 208162306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 208262306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_PORTS, enc_tp); 208362306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 208462306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_IP, enc_ip); 208562306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 208662306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_ENC_OPTS, enc_opts); 208762306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 208862306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_CT, ct); 208962306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 209062306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_HASH, hash); 209162306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 209262306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_NUM_OF_VLANS, num_of_vlans); 209362306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 209462306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_PPPOE, pppoe); 209562306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 209662306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_L2TPV3, l2tpv3); 209762306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 209862306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_IPSEC, ipsec); 209962306a36Sopenharmony_ci FL_KEY_SET_IF_MASKED(mask, keys, cnt, 210062306a36Sopenharmony_ci FLOW_DISSECTOR_KEY_CFM, cfm); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci skb_flow_dissector_init(dissector, keys, cnt); 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic struct fl_flow_mask *fl_create_new_mask(struct cls_fl_head *head, 210662306a36Sopenharmony_ci struct fl_flow_mask *mask) 210762306a36Sopenharmony_ci{ 210862306a36Sopenharmony_ci struct fl_flow_mask *newmask; 210962306a36Sopenharmony_ci int err; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci newmask = kzalloc(sizeof(*newmask), GFP_KERNEL); 211262306a36Sopenharmony_ci if (!newmask) 211362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci fl_mask_copy(newmask, mask); 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci if ((newmask->key.tp_range.tp_min.dst && 211862306a36Sopenharmony_ci newmask->key.tp_range.tp_max.dst) || 211962306a36Sopenharmony_ci (newmask->key.tp_range.tp_min.src && 212062306a36Sopenharmony_ci newmask->key.tp_range.tp_max.src)) 212162306a36Sopenharmony_ci newmask->flags |= TCA_FLOWER_MASK_FLAGS_RANGE; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci err = fl_init_mask_hashtable(newmask); 212462306a36Sopenharmony_ci if (err) 212562306a36Sopenharmony_ci goto errout_free; 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci fl_init_dissector(&newmask->dissector, &newmask->key); 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci INIT_LIST_HEAD_RCU(&newmask->filters); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci refcount_set(&newmask->refcnt, 1); 213262306a36Sopenharmony_ci err = rhashtable_replace_fast(&head->ht, &mask->ht_node, 213362306a36Sopenharmony_ci &newmask->ht_node, mask_ht_params); 213462306a36Sopenharmony_ci if (err) 213562306a36Sopenharmony_ci goto errout_destroy; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci spin_lock(&head->masks_lock); 213862306a36Sopenharmony_ci list_add_tail_rcu(&newmask->list, &head->masks); 213962306a36Sopenharmony_ci spin_unlock(&head->masks_lock); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci return newmask; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_cierrout_destroy: 214462306a36Sopenharmony_ci rhashtable_destroy(&newmask->ht); 214562306a36Sopenharmony_cierrout_free: 214662306a36Sopenharmony_ci kfree(newmask); 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci return ERR_PTR(err); 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_cistatic int fl_check_assign_mask(struct cls_fl_head *head, 215262306a36Sopenharmony_ci struct cls_fl_filter *fnew, 215362306a36Sopenharmony_ci struct cls_fl_filter *fold, 215462306a36Sopenharmony_ci struct fl_flow_mask *mask) 215562306a36Sopenharmony_ci{ 215662306a36Sopenharmony_ci struct fl_flow_mask *newmask; 215762306a36Sopenharmony_ci int ret = 0; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci rcu_read_lock(); 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci /* Insert mask as temporary node to prevent concurrent creation of mask 216262306a36Sopenharmony_ci * with same key. Any concurrent lookups with same key will return 216362306a36Sopenharmony_ci * -EAGAIN because mask's refcnt is zero. 216462306a36Sopenharmony_ci */ 216562306a36Sopenharmony_ci fnew->mask = rhashtable_lookup_get_insert_fast(&head->ht, 216662306a36Sopenharmony_ci &mask->ht_node, 216762306a36Sopenharmony_ci mask_ht_params); 216862306a36Sopenharmony_ci if (!fnew->mask) { 216962306a36Sopenharmony_ci rcu_read_unlock(); 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if (fold) { 217262306a36Sopenharmony_ci ret = -EINVAL; 217362306a36Sopenharmony_ci goto errout_cleanup; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci newmask = fl_create_new_mask(head, mask); 217762306a36Sopenharmony_ci if (IS_ERR(newmask)) { 217862306a36Sopenharmony_ci ret = PTR_ERR(newmask); 217962306a36Sopenharmony_ci goto errout_cleanup; 218062306a36Sopenharmony_ci } 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci fnew->mask = newmask; 218362306a36Sopenharmony_ci return 0; 218462306a36Sopenharmony_ci } else if (IS_ERR(fnew->mask)) { 218562306a36Sopenharmony_ci ret = PTR_ERR(fnew->mask); 218662306a36Sopenharmony_ci } else if (fold && fold->mask != fnew->mask) { 218762306a36Sopenharmony_ci ret = -EINVAL; 218862306a36Sopenharmony_ci } else if (!refcount_inc_not_zero(&fnew->mask->refcnt)) { 218962306a36Sopenharmony_ci /* Mask was deleted concurrently, try again */ 219062306a36Sopenharmony_ci ret = -EAGAIN; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci rcu_read_unlock(); 219362306a36Sopenharmony_ci return ret; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_cierrout_cleanup: 219662306a36Sopenharmony_ci rhashtable_remove_fast(&head->ht, &mask->ht_node, 219762306a36Sopenharmony_ci mask_ht_params); 219862306a36Sopenharmony_ci return ret; 219962306a36Sopenharmony_ci} 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_cistatic bool fl_needs_tc_skb_ext(const struct fl_flow_key *mask) 220262306a36Sopenharmony_ci{ 220362306a36Sopenharmony_ci return mask->meta.l2_miss; 220462306a36Sopenharmony_ci} 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_cistatic int fl_ht_insert_unique(struct cls_fl_filter *fnew, 220762306a36Sopenharmony_ci struct cls_fl_filter *fold, 220862306a36Sopenharmony_ci bool *in_ht) 220962306a36Sopenharmony_ci{ 221062306a36Sopenharmony_ci struct fl_flow_mask *mask = fnew->mask; 221162306a36Sopenharmony_ci int err; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci err = rhashtable_lookup_insert_fast(&mask->ht, 221462306a36Sopenharmony_ci &fnew->ht_node, 221562306a36Sopenharmony_ci mask->filter_ht_params); 221662306a36Sopenharmony_ci if (err) { 221762306a36Sopenharmony_ci *in_ht = false; 221862306a36Sopenharmony_ci /* It is okay if filter with same key exists when 221962306a36Sopenharmony_ci * overwriting. 222062306a36Sopenharmony_ci */ 222162306a36Sopenharmony_ci return fold && err == -EEXIST ? 0 : err; 222262306a36Sopenharmony_ci } 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci *in_ht = true; 222562306a36Sopenharmony_ci return 0; 222662306a36Sopenharmony_ci} 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_cistatic int fl_change(struct net *net, struct sk_buff *in_skb, 222962306a36Sopenharmony_ci struct tcf_proto *tp, unsigned long base, 223062306a36Sopenharmony_ci u32 handle, struct nlattr **tca, 223162306a36Sopenharmony_ci void **arg, u32 flags, 223262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 223562306a36Sopenharmony_ci bool rtnl_held = !(flags & TCA_ACT_FLAGS_NO_RTNL); 223662306a36Sopenharmony_ci struct cls_fl_filter *fold = *arg; 223762306a36Sopenharmony_ci bool bound_to_filter = false; 223862306a36Sopenharmony_ci struct cls_fl_filter *fnew; 223962306a36Sopenharmony_ci struct fl_flow_mask *mask; 224062306a36Sopenharmony_ci struct nlattr **tb; 224162306a36Sopenharmony_ci bool in_ht; 224262306a36Sopenharmony_ci int err; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci if (!tca[TCA_OPTIONS]) { 224562306a36Sopenharmony_ci err = -EINVAL; 224662306a36Sopenharmony_ci goto errout_fold; 224762306a36Sopenharmony_ci } 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci mask = kzalloc(sizeof(struct fl_flow_mask), GFP_KERNEL); 225062306a36Sopenharmony_ci if (!mask) { 225162306a36Sopenharmony_ci err = -ENOBUFS; 225262306a36Sopenharmony_ci goto errout_fold; 225362306a36Sopenharmony_ci } 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 225662306a36Sopenharmony_ci if (!tb) { 225762306a36Sopenharmony_ci err = -ENOBUFS; 225862306a36Sopenharmony_ci goto errout_mask_alloc; 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_FLOWER_MAX, 226262306a36Sopenharmony_ci tca[TCA_OPTIONS], fl_policy, NULL); 226362306a36Sopenharmony_ci if (err < 0) 226462306a36Sopenharmony_ci goto errout_tb; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci if (fold && handle && fold->handle != handle) { 226762306a36Sopenharmony_ci err = -EINVAL; 226862306a36Sopenharmony_ci goto errout_tb; 226962306a36Sopenharmony_ci } 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 227262306a36Sopenharmony_ci if (!fnew) { 227362306a36Sopenharmony_ci err = -ENOBUFS; 227462306a36Sopenharmony_ci goto errout_tb; 227562306a36Sopenharmony_ci } 227662306a36Sopenharmony_ci INIT_LIST_HEAD(&fnew->hw_list); 227762306a36Sopenharmony_ci refcount_set(&fnew->refcnt, 1); 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci if (tb[TCA_FLOWER_FLAGS]) { 228062306a36Sopenharmony_ci fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (!tc_flags_valid(fnew->flags)) { 228362306a36Sopenharmony_ci kfree(fnew); 228462306a36Sopenharmony_ci err = -EINVAL; 228562306a36Sopenharmony_ci goto errout_tb; 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci if (!fold) { 229062306a36Sopenharmony_ci spin_lock(&tp->lock); 229162306a36Sopenharmony_ci if (!handle) { 229262306a36Sopenharmony_ci handle = 1; 229362306a36Sopenharmony_ci err = idr_alloc_u32(&head->handle_idr, NULL, &handle, 229462306a36Sopenharmony_ci INT_MAX, GFP_ATOMIC); 229562306a36Sopenharmony_ci } else { 229662306a36Sopenharmony_ci err = idr_alloc_u32(&head->handle_idr, NULL, &handle, 229762306a36Sopenharmony_ci handle, GFP_ATOMIC); 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci /* Filter with specified handle was concurrently 230062306a36Sopenharmony_ci * inserted after initial check in cls_api. This is not 230162306a36Sopenharmony_ci * necessarily an error if NLM_F_EXCL is not set in 230262306a36Sopenharmony_ci * message flags. Returning EAGAIN will cause cls_api to 230362306a36Sopenharmony_ci * try to update concurrently inserted rule. 230462306a36Sopenharmony_ci */ 230562306a36Sopenharmony_ci if (err == -ENOSPC) 230662306a36Sopenharmony_ci err = -EAGAIN; 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci spin_unlock(&tp->lock); 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci if (err) { 231162306a36Sopenharmony_ci kfree(fnew); 231262306a36Sopenharmony_ci goto errout_tb; 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci } 231562306a36Sopenharmony_ci fnew->handle = handle; 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci err = tcf_exts_init_ex(&fnew->exts, net, TCA_FLOWER_ACT, 0, tp, handle, 231862306a36Sopenharmony_ci !tc_skip_hw(fnew->flags)); 231962306a36Sopenharmony_ci if (err < 0) 232062306a36Sopenharmony_ci goto errout_idr; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci err = tcf_exts_validate_ex(net, tp, tb, tca[TCA_RATE], 232362306a36Sopenharmony_ci &fnew->exts, flags, fnew->flags, 232462306a36Sopenharmony_ci extack); 232562306a36Sopenharmony_ci if (err < 0) 232662306a36Sopenharmony_ci goto errout_idr; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci if (tb[TCA_FLOWER_CLASSID]) { 232962306a36Sopenharmony_ci fnew->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); 233062306a36Sopenharmony_ci if (flags & TCA_ACT_FLAGS_NO_RTNL) 233162306a36Sopenharmony_ci rtnl_lock(); 233262306a36Sopenharmony_ci tcf_bind_filter(tp, &fnew->res, base); 233362306a36Sopenharmony_ci if (flags & TCA_ACT_FLAGS_NO_RTNL) 233462306a36Sopenharmony_ci rtnl_unlock(); 233562306a36Sopenharmony_ci bound_to_filter = true; 233662306a36Sopenharmony_ci } 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci err = fl_set_key(net, tb, &fnew->key, &mask->key, extack); 233962306a36Sopenharmony_ci if (err) 234062306a36Sopenharmony_ci goto unbind_filter; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci fl_mask_update_range(mask); 234362306a36Sopenharmony_ci fl_set_masked_key(&fnew->mkey, &fnew->key, mask); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci if (!fl_mask_fits_tmplt(tp->chain->tmplt_priv, mask)) { 234662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Mask does not fit the template"); 234762306a36Sopenharmony_ci err = -EINVAL; 234862306a36Sopenharmony_ci goto unbind_filter; 234962306a36Sopenharmony_ci } 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci /* Enable tc skb extension if filter matches on data extracted from 235262306a36Sopenharmony_ci * this extension. 235362306a36Sopenharmony_ci */ 235462306a36Sopenharmony_ci if (fl_needs_tc_skb_ext(&mask->key)) { 235562306a36Sopenharmony_ci fnew->needs_tc_skb_ext = 1; 235662306a36Sopenharmony_ci tc_skb_ext_tc_enable(); 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci err = fl_check_assign_mask(head, fnew, fold, mask); 236062306a36Sopenharmony_ci if (err) 236162306a36Sopenharmony_ci goto unbind_filter; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci err = fl_ht_insert_unique(fnew, fold, &in_ht); 236462306a36Sopenharmony_ci if (err) 236562306a36Sopenharmony_ci goto errout_mask; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci if (!tc_skip_hw(fnew->flags)) { 236862306a36Sopenharmony_ci err = fl_hw_replace_filter(tp, fnew, rtnl_held, extack); 236962306a36Sopenharmony_ci if (err) 237062306a36Sopenharmony_ci goto errout_ht; 237162306a36Sopenharmony_ci } 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci if (!tc_in_hw(fnew->flags)) 237462306a36Sopenharmony_ci fnew->flags |= TCA_CLS_FLAGS_NOT_IN_HW; 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci spin_lock(&tp->lock); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* tp was deleted concurrently. -EAGAIN will cause caller to lookup 237962306a36Sopenharmony_ci * proto again or create new one, if necessary. 238062306a36Sopenharmony_ci */ 238162306a36Sopenharmony_ci if (tp->deleting) { 238262306a36Sopenharmony_ci err = -EAGAIN; 238362306a36Sopenharmony_ci goto errout_hw; 238462306a36Sopenharmony_ci } 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci if (fold) { 238762306a36Sopenharmony_ci /* Fold filter was deleted concurrently. Retry lookup. */ 238862306a36Sopenharmony_ci if (fold->deleted) { 238962306a36Sopenharmony_ci err = -EAGAIN; 239062306a36Sopenharmony_ci goto errout_hw; 239162306a36Sopenharmony_ci } 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci fnew->handle = handle; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci if (!in_ht) { 239662306a36Sopenharmony_ci struct rhashtable_params params = 239762306a36Sopenharmony_ci fnew->mask->filter_ht_params; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci err = rhashtable_insert_fast(&fnew->mask->ht, 240062306a36Sopenharmony_ci &fnew->ht_node, 240162306a36Sopenharmony_ci params); 240262306a36Sopenharmony_ci if (err) 240362306a36Sopenharmony_ci goto errout_hw; 240462306a36Sopenharmony_ci in_ht = true; 240562306a36Sopenharmony_ci } 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci refcount_inc(&fnew->refcnt); 240862306a36Sopenharmony_ci rhashtable_remove_fast(&fold->mask->ht, 240962306a36Sopenharmony_ci &fold->ht_node, 241062306a36Sopenharmony_ci fold->mask->filter_ht_params); 241162306a36Sopenharmony_ci idr_replace(&head->handle_idr, fnew, fnew->handle); 241262306a36Sopenharmony_ci list_replace_rcu(&fold->list, &fnew->list); 241362306a36Sopenharmony_ci fold->deleted = true; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci spin_unlock(&tp->lock); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci fl_mask_put(head, fold->mask); 241862306a36Sopenharmony_ci if (!tc_skip_hw(fold->flags)) 241962306a36Sopenharmony_ci fl_hw_destroy_filter(tp, fold, rtnl_held, NULL); 242062306a36Sopenharmony_ci tcf_unbind_filter(tp, &fold->res); 242162306a36Sopenharmony_ci /* Caller holds reference to fold, so refcnt is always > 0 242262306a36Sopenharmony_ci * after this. 242362306a36Sopenharmony_ci */ 242462306a36Sopenharmony_ci refcount_dec(&fold->refcnt); 242562306a36Sopenharmony_ci __fl_put(fold); 242662306a36Sopenharmony_ci } else { 242762306a36Sopenharmony_ci idr_replace(&head->handle_idr, fnew, fnew->handle); 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci refcount_inc(&fnew->refcnt); 243062306a36Sopenharmony_ci list_add_tail_rcu(&fnew->list, &fnew->mask->filters); 243162306a36Sopenharmony_ci spin_unlock(&tp->lock); 243262306a36Sopenharmony_ci } 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci *arg = fnew; 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci kfree(tb); 243762306a36Sopenharmony_ci tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); 243862306a36Sopenharmony_ci return 0; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_cierrout_ht: 244162306a36Sopenharmony_ci spin_lock(&tp->lock); 244262306a36Sopenharmony_cierrout_hw: 244362306a36Sopenharmony_ci fnew->deleted = true; 244462306a36Sopenharmony_ci spin_unlock(&tp->lock); 244562306a36Sopenharmony_ci if (!tc_skip_hw(fnew->flags)) 244662306a36Sopenharmony_ci fl_hw_destroy_filter(tp, fnew, rtnl_held, NULL); 244762306a36Sopenharmony_ci if (in_ht) 244862306a36Sopenharmony_ci rhashtable_remove_fast(&fnew->mask->ht, &fnew->ht_node, 244962306a36Sopenharmony_ci fnew->mask->filter_ht_params); 245062306a36Sopenharmony_cierrout_mask: 245162306a36Sopenharmony_ci fl_mask_put(head, fnew->mask); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ciunbind_filter: 245462306a36Sopenharmony_ci if (bound_to_filter) { 245562306a36Sopenharmony_ci if (flags & TCA_ACT_FLAGS_NO_RTNL) 245662306a36Sopenharmony_ci rtnl_lock(); 245762306a36Sopenharmony_ci tcf_unbind_filter(tp, &fnew->res); 245862306a36Sopenharmony_ci if (flags & TCA_ACT_FLAGS_NO_RTNL) 245962306a36Sopenharmony_ci rtnl_unlock(); 246062306a36Sopenharmony_ci } 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_cierrout_idr: 246362306a36Sopenharmony_ci if (!fold) { 246462306a36Sopenharmony_ci spin_lock(&tp->lock); 246562306a36Sopenharmony_ci idr_remove(&head->handle_idr, fnew->handle); 246662306a36Sopenharmony_ci spin_unlock(&tp->lock); 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci __fl_put(fnew); 246962306a36Sopenharmony_cierrout_tb: 247062306a36Sopenharmony_ci kfree(tb); 247162306a36Sopenharmony_cierrout_mask_alloc: 247262306a36Sopenharmony_ci tcf_queue_work(&mask->rwork, fl_uninit_mask_free_work); 247362306a36Sopenharmony_cierrout_fold: 247462306a36Sopenharmony_ci if (fold) 247562306a36Sopenharmony_ci __fl_put(fold); 247662306a36Sopenharmony_ci return err; 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic int fl_delete(struct tcf_proto *tp, void *arg, bool *last, 248062306a36Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 248362306a36Sopenharmony_ci struct cls_fl_filter *f = arg; 248462306a36Sopenharmony_ci bool last_on_mask; 248562306a36Sopenharmony_ci int err = 0; 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci err = __fl_delete(tp, f, &last_on_mask, rtnl_held, extack); 248862306a36Sopenharmony_ci *last = list_empty(&head->masks); 248962306a36Sopenharmony_ci __fl_put(f); 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci return err; 249262306a36Sopenharmony_ci} 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_cistatic void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg, 249562306a36Sopenharmony_ci bool rtnl_held) 249662306a36Sopenharmony_ci{ 249762306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 249862306a36Sopenharmony_ci unsigned long id = arg->cookie, tmp; 249962306a36Sopenharmony_ci struct cls_fl_filter *f; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci arg->count = arg->skip; 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci rcu_read_lock(); 250462306a36Sopenharmony_ci idr_for_each_entry_continue_ul(&head->handle_idr, f, tmp, id) { 250562306a36Sopenharmony_ci /* don't return filters that are being deleted */ 250662306a36Sopenharmony_ci if (!f || !refcount_inc_not_zero(&f->refcnt)) 250762306a36Sopenharmony_ci continue; 250862306a36Sopenharmony_ci rcu_read_unlock(); 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci if (arg->fn(tp, f, arg) < 0) { 251162306a36Sopenharmony_ci __fl_put(f); 251262306a36Sopenharmony_ci arg->stop = 1; 251362306a36Sopenharmony_ci rcu_read_lock(); 251462306a36Sopenharmony_ci break; 251562306a36Sopenharmony_ci } 251662306a36Sopenharmony_ci __fl_put(f); 251762306a36Sopenharmony_ci arg->count++; 251862306a36Sopenharmony_ci rcu_read_lock(); 251962306a36Sopenharmony_ci } 252062306a36Sopenharmony_ci rcu_read_unlock(); 252162306a36Sopenharmony_ci arg->cookie = id; 252262306a36Sopenharmony_ci} 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_cistatic struct cls_fl_filter * 252562306a36Sopenharmony_cifl_get_next_hw_filter(struct tcf_proto *tp, struct cls_fl_filter *f, bool add) 252662306a36Sopenharmony_ci{ 252762306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci spin_lock(&tp->lock); 253062306a36Sopenharmony_ci if (list_empty(&head->hw_filters)) { 253162306a36Sopenharmony_ci spin_unlock(&tp->lock); 253262306a36Sopenharmony_ci return NULL; 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci if (!f) 253662306a36Sopenharmony_ci f = list_entry(&head->hw_filters, struct cls_fl_filter, 253762306a36Sopenharmony_ci hw_list); 253862306a36Sopenharmony_ci list_for_each_entry_continue(f, &head->hw_filters, hw_list) { 253962306a36Sopenharmony_ci if (!(add && f->deleted) && refcount_inc_not_zero(&f->refcnt)) { 254062306a36Sopenharmony_ci spin_unlock(&tp->lock); 254162306a36Sopenharmony_ci return f; 254262306a36Sopenharmony_ci } 254362306a36Sopenharmony_ci } 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci spin_unlock(&tp->lock); 254662306a36Sopenharmony_ci return NULL; 254762306a36Sopenharmony_ci} 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_cistatic int fl_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, 255062306a36Sopenharmony_ci void *cb_priv, struct netlink_ext_ack *extack) 255162306a36Sopenharmony_ci{ 255262306a36Sopenharmony_ci struct tcf_block *block = tp->chain->block; 255362306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 255462306a36Sopenharmony_ci struct cls_fl_filter *f = NULL; 255562306a36Sopenharmony_ci int err; 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci /* hw_filters list can only be changed by hw offload functions after 255862306a36Sopenharmony_ci * obtaining rtnl lock. Make sure it is not changed while reoffload is 255962306a36Sopenharmony_ci * iterating it. 256062306a36Sopenharmony_ci */ 256162306a36Sopenharmony_ci ASSERT_RTNL(); 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci while ((f = fl_get_next_hw_filter(tp, f, add))) { 256462306a36Sopenharmony_ci cls_flower.rule = 256562306a36Sopenharmony_ci flow_rule_alloc(tcf_exts_num_actions(&f->exts)); 256662306a36Sopenharmony_ci if (!cls_flower.rule) { 256762306a36Sopenharmony_ci __fl_put(f); 256862306a36Sopenharmony_ci return -ENOMEM; 256962306a36Sopenharmony_ci } 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci tc_cls_common_offload_init(&cls_flower.common, tp, f->flags, 257262306a36Sopenharmony_ci extack); 257362306a36Sopenharmony_ci cls_flower.command = add ? 257462306a36Sopenharmony_ci FLOW_CLS_REPLACE : FLOW_CLS_DESTROY; 257562306a36Sopenharmony_ci cls_flower.cookie = (unsigned long)f; 257662306a36Sopenharmony_ci cls_flower.rule->match.dissector = &f->mask->dissector; 257762306a36Sopenharmony_ci cls_flower.rule->match.mask = &f->mask->key; 257862306a36Sopenharmony_ci cls_flower.rule->match.key = &f->mkey; 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci err = tc_setup_offload_action(&cls_flower.rule->action, &f->exts, 258162306a36Sopenharmony_ci cls_flower.common.extack); 258262306a36Sopenharmony_ci if (err) { 258362306a36Sopenharmony_ci kfree(cls_flower.rule); 258462306a36Sopenharmony_ci if (tc_skip_sw(f->flags)) { 258562306a36Sopenharmony_ci __fl_put(f); 258662306a36Sopenharmony_ci return err; 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci goto next_flow; 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci cls_flower.classid = f->res.classid; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci err = tc_setup_cb_reoffload(block, tp, add, cb, 259462306a36Sopenharmony_ci TC_SETUP_CLSFLOWER, &cls_flower, 259562306a36Sopenharmony_ci cb_priv, &f->flags, 259662306a36Sopenharmony_ci &f->in_hw_count); 259762306a36Sopenharmony_ci tc_cleanup_offload_action(&cls_flower.rule->action); 259862306a36Sopenharmony_ci kfree(cls_flower.rule); 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci if (err) { 260162306a36Sopenharmony_ci __fl_put(f); 260262306a36Sopenharmony_ci return err; 260362306a36Sopenharmony_ci } 260462306a36Sopenharmony_cinext_flow: 260562306a36Sopenharmony_ci __fl_put(f); 260662306a36Sopenharmony_ci } 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci return 0; 260962306a36Sopenharmony_ci} 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_cistatic void fl_hw_add(struct tcf_proto *tp, void *type_data) 261262306a36Sopenharmony_ci{ 261362306a36Sopenharmony_ci struct flow_cls_offload *cls_flower = type_data; 261462306a36Sopenharmony_ci struct cls_fl_filter *f = 261562306a36Sopenharmony_ci (struct cls_fl_filter *) cls_flower->cookie; 261662306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci spin_lock(&tp->lock); 261962306a36Sopenharmony_ci list_add(&f->hw_list, &head->hw_filters); 262062306a36Sopenharmony_ci spin_unlock(&tp->lock); 262162306a36Sopenharmony_ci} 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_cistatic void fl_hw_del(struct tcf_proto *tp, void *type_data) 262462306a36Sopenharmony_ci{ 262562306a36Sopenharmony_ci struct flow_cls_offload *cls_flower = type_data; 262662306a36Sopenharmony_ci struct cls_fl_filter *f = 262762306a36Sopenharmony_ci (struct cls_fl_filter *) cls_flower->cookie; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci spin_lock(&tp->lock); 263062306a36Sopenharmony_ci if (!list_empty(&f->hw_list)) 263162306a36Sopenharmony_ci list_del_init(&f->hw_list); 263262306a36Sopenharmony_ci spin_unlock(&tp->lock); 263362306a36Sopenharmony_ci} 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_cistatic int fl_hw_create_tmplt(struct tcf_chain *chain, 263662306a36Sopenharmony_ci struct fl_flow_tmplt *tmplt) 263762306a36Sopenharmony_ci{ 263862306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 263962306a36Sopenharmony_ci struct tcf_block *block = chain->block; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci cls_flower.rule = flow_rule_alloc(0); 264262306a36Sopenharmony_ci if (!cls_flower.rule) 264362306a36Sopenharmony_ci return -ENOMEM; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci cls_flower.common.chain_index = chain->index; 264662306a36Sopenharmony_ci cls_flower.command = FLOW_CLS_TMPLT_CREATE; 264762306a36Sopenharmony_ci cls_flower.cookie = (unsigned long) tmplt; 264862306a36Sopenharmony_ci cls_flower.rule->match.dissector = &tmplt->dissector; 264962306a36Sopenharmony_ci cls_flower.rule->match.mask = &tmplt->mask; 265062306a36Sopenharmony_ci cls_flower.rule->match.key = &tmplt->dummy_key; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci /* We don't care if driver (any of them) fails to handle this 265362306a36Sopenharmony_ci * call. It serves just as a hint for it. 265462306a36Sopenharmony_ci */ 265562306a36Sopenharmony_ci tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false, true); 265662306a36Sopenharmony_ci kfree(cls_flower.rule); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci return 0; 265962306a36Sopenharmony_ci} 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_cistatic void fl_hw_destroy_tmplt(struct tcf_chain *chain, 266262306a36Sopenharmony_ci struct fl_flow_tmplt *tmplt) 266362306a36Sopenharmony_ci{ 266462306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 266562306a36Sopenharmony_ci struct tcf_block *block = chain->block; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci cls_flower.common.chain_index = chain->index; 266862306a36Sopenharmony_ci cls_flower.command = FLOW_CLS_TMPLT_DESTROY; 266962306a36Sopenharmony_ci cls_flower.cookie = (unsigned long) tmplt; 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, false, true); 267262306a36Sopenharmony_ci} 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_cistatic void *fl_tmplt_create(struct net *net, struct tcf_chain *chain, 267562306a36Sopenharmony_ci struct nlattr **tca, 267662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 267762306a36Sopenharmony_ci{ 267862306a36Sopenharmony_ci struct fl_flow_tmplt *tmplt; 267962306a36Sopenharmony_ci struct nlattr **tb; 268062306a36Sopenharmony_ci int err; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci if (!tca[TCA_OPTIONS]) 268362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci tb = kcalloc(TCA_FLOWER_MAX + 1, sizeof(struct nlattr *), GFP_KERNEL); 268662306a36Sopenharmony_ci if (!tb) 268762306a36Sopenharmony_ci return ERR_PTR(-ENOBUFS); 268862306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_FLOWER_MAX, 268962306a36Sopenharmony_ci tca[TCA_OPTIONS], fl_policy, NULL); 269062306a36Sopenharmony_ci if (err) 269162306a36Sopenharmony_ci goto errout_tb; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci tmplt = kzalloc(sizeof(*tmplt), GFP_KERNEL); 269462306a36Sopenharmony_ci if (!tmplt) { 269562306a36Sopenharmony_ci err = -ENOMEM; 269662306a36Sopenharmony_ci goto errout_tb; 269762306a36Sopenharmony_ci } 269862306a36Sopenharmony_ci tmplt->chain = chain; 269962306a36Sopenharmony_ci err = fl_set_key(net, tb, &tmplt->dummy_key, &tmplt->mask, extack); 270062306a36Sopenharmony_ci if (err) 270162306a36Sopenharmony_ci goto errout_tmplt; 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci fl_init_dissector(&tmplt->dissector, &tmplt->mask); 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci err = fl_hw_create_tmplt(chain, tmplt); 270662306a36Sopenharmony_ci if (err) 270762306a36Sopenharmony_ci goto errout_tmplt; 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci kfree(tb); 271062306a36Sopenharmony_ci return tmplt; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_cierrout_tmplt: 271362306a36Sopenharmony_ci kfree(tmplt); 271462306a36Sopenharmony_cierrout_tb: 271562306a36Sopenharmony_ci kfree(tb); 271662306a36Sopenharmony_ci return ERR_PTR(err); 271762306a36Sopenharmony_ci} 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_cistatic void fl_tmplt_destroy(void *tmplt_priv) 272062306a36Sopenharmony_ci{ 272162306a36Sopenharmony_ci struct fl_flow_tmplt *tmplt = tmplt_priv; 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci fl_hw_destroy_tmplt(tmplt->chain, tmplt); 272462306a36Sopenharmony_ci kfree(tmplt); 272562306a36Sopenharmony_ci} 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_cistatic void fl_tmplt_reoffload(struct tcf_chain *chain, bool add, 272862306a36Sopenharmony_ci flow_setup_cb_t *cb, void *cb_priv) 272962306a36Sopenharmony_ci{ 273062306a36Sopenharmony_ci struct fl_flow_tmplt *tmplt = chain->tmplt_priv; 273162306a36Sopenharmony_ci struct flow_cls_offload cls_flower = {}; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci cls_flower.rule = flow_rule_alloc(0); 273462306a36Sopenharmony_ci if (!cls_flower.rule) 273562306a36Sopenharmony_ci return; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci cls_flower.common.chain_index = chain->index; 273862306a36Sopenharmony_ci cls_flower.command = add ? FLOW_CLS_TMPLT_CREATE : 273962306a36Sopenharmony_ci FLOW_CLS_TMPLT_DESTROY; 274062306a36Sopenharmony_ci cls_flower.cookie = (unsigned long) tmplt; 274162306a36Sopenharmony_ci cls_flower.rule->match.dissector = &tmplt->dissector; 274262306a36Sopenharmony_ci cls_flower.rule->match.mask = &tmplt->mask; 274362306a36Sopenharmony_ci cls_flower.rule->match.key = &tmplt->dummy_key; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv); 274662306a36Sopenharmony_ci kfree(cls_flower.rule); 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic int fl_dump_key_val(struct sk_buff *skb, 275062306a36Sopenharmony_ci void *val, int val_type, 275162306a36Sopenharmony_ci void *mask, int mask_type, int len) 275262306a36Sopenharmony_ci{ 275362306a36Sopenharmony_ci int err; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci if (!memchr_inv(mask, 0, len)) 275662306a36Sopenharmony_ci return 0; 275762306a36Sopenharmony_ci err = nla_put(skb, val_type, len, val); 275862306a36Sopenharmony_ci if (err) 275962306a36Sopenharmony_ci return err; 276062306a36Sopenharmony_ci if (mask_type != TCA_FLOWER_UNSPEC) { 276162306a36Sopenharmony_ci err = nla_put(skb, mask_type, len, mask); 276262306a36Sopenharmony_ci if (err) 276362306a36Sopenharmony_ci return err; 276462306a36Sopenharmony_ci } 276562306a36Sopenharmony_ci return 0; 276662306a36Sopenharmony_ci} 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_cistatic int fl_dump_key_port_range(struct sk_buff *skb, struct fl_flow_key *key, 276962306a36Sopenharmony_ci struct fl_flow_key *mask) 277062306a36Sopenharmony_ci{ 277162306a36Sopenharmony_ci if (fl_dump_key_val(skb, &key->tp_range.tp_min.dst, 277262306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_DST_MIN, 277362306a36Sopenharmony_ci &mask->tp_range.tp_min.dst, TCA_FLOWER_UNSPEC, 277462306a36Sopenharmony_ci sizeof(key->tp_range.tp_min.dst)) || 277562306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tp_range.tp_max.dst, 277662306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_DST_MAX, 277762306a36Sopenharmony_ci &mask->tp_range.tp_max.dst, TCA_FLOWER_UNSPEC, 277862306a36Sopenharmony_ci sizeof(key->tp_range.tp_max.dst)) || 277962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tp_range.tp_min.src, 278062306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_SRC_MIN, 278162306a36Sopenharmony_ci &mask->tp_range.tp_min.src, TCA_FLOWER_UNSPEC, 278262306a36Sopenharmony_ci sizeof(key->tp_range.tp_min.src)) || 278362306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tp_range.tp_max.src, 278462306a36Sopenharmony_ci TCA_FLOWER_KEY_PORT_SRC_MAX, 278562306a36Sopenharmony_ci &mask->tp_range.tp_max.src, TCA_FLOWER_UNSPEC, 278662306a36Sopenharmony_ci sizeof(key->tp_range.tp_max.src))) 278762306a36Sopenharmony_ci return -1; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci return 0; 279062306a36Sopenharmony_ci} 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_cistatic int fl_dump_key_mpls_opt_lse(struct sk_buff *skb, 279362306a36Sopenharmony_ci struct flow_dissector_key_mpls *mpls_key, 279462306a36Sopenharmony_ci struct flow_dissector_key_mpls *mpls_mask, 279562306a36Sopenharmony_ci u8 lse_index) 279662306a36Sopenharmony_ci{ 279762306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_mask = &mpls_mask->ls[lse_index]; 279862306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_key = &mpls_key->ls[lse_index]; 279962306a36Sopenharmony_ci int err; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_OPT_LSE_DEPTH, 280262306a36Sopenharmony_ci lse_index + 1); 280362306a36Sopenharmony_ci if (err) 280462306a36Sopenharmony_ci return err; 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci if (lse_mask->mpls_ttl) { 280762306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_OPT_LSE_TTL, 280862306a36Sopenharmony_ci lse_key->mpls_ttl); 280962306a36Sopenharmony_ci if (err) 281062306a36Sopenharmony_ci return err; 281162306a36Sopenharmony_ci } 281262306a36Sopenharmony_ci if (lse_mask->mpls_bos) { 281362306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_OPT_LSE_BOS, 281462306a36Sopenharmony_ci lse_key->mpls_bos); 281562306a36Sopenharmony_ci if (err) 281662306a36Sopenharmony_ci return err; 281762306a36Sopenharmony_ci } 281862306a36Sopenharmony_ci if (lse_mask->mpls_tc) { 281962306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_OPT_LSE_TC, 282062306a36Sopenharmony_ci lse_key->mpls_tc); 282162306a36Sopenharmony_ci if (err) 282262306a36Sopenharmony_ci return err; 282362306a36Sopenharmony_ci } 282462306a36Sopenharmony_ci if (lse_mask->mpls_label) { 282562306a36Sopenharmony_ci err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_OPT_LSE_LABEL, 282662306a36Sopenharmony_ci lse_key->mpls_label); 282762306a36Sopenharmony_ci if (err) 282862306a36Sopenharmony_ci return err; 282962306a36Sopenharmony_ci } 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci return 0; 283262306a36Sopenharmony_ci} 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_cistatic int fl_dump_key_mpls_opts(struct sk_buff *skb, 283562306a36Sopenharmony_ci struct flow_dissector_key_mpls *mpls_key, 283662306a36Sopenharmony_ci struct flow_dissector_key_mpls *mpls_mask) 283762306a36Sopenharmony_ci{ 283862306a36Sopenharmony_ci struct nlattr *opts; 283962306a36Sopenharmony_ci struct nlattr *lse; 284062306a36Sopenharmony_ci u8 lse_index; 284162306a36Sopenharmony_ci int err; 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci opts = nla_nest_start(skb, TCA_FLOWER_KEY_MPLS_OPTS); 284462306a36Sopenharmony_ci if (!opts) 284562306a36Sopenharmony_ci return -EMSGSIZE; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci for (lse_index = 0; lse_index < FLOW_DIS_MPLS_MAX; lse_index++) { 284862306a36Sopenharmony_ci if (!(mpls_mask->used_lses & 1 << lse_index)) 284962306a36Sopenharmony_ci continue; 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci lse = nla_nest_start(skb, TCA_FLOWER_KEY_MPLS_OPTS_LSE); 285262306a36Sopenharmony_ci if (!lse) { 285362306a36Sopenharmony_ci err = -EMSGSIZE; 285462306a36Sopenharmony_ci goto err_opts; 285562306a36Sopenharmony_ci } 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci err = fl_dump_key_mpls_opt_lse(skb, mpls_key, mpls_mask, 285862306a36Sopenharmony_ci lse_index); 285962306a36Sopenharmony_ci if (err) 286062306a36Sopenharmony_ci goto err_opts_lse; 286162306a36Sopenharmony_ci nla_nest_end(skb, lse); 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci nla_nest_end(skb, opts); 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci return 0; 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_cierr_opts_lse: 286862306a36Sopenharmony_ci nla_nest_cancel(skb, lse); 286962306a36Sopenharmony_cierr_opts: 287062306a36Sopenharmony_ci nla_nest_cancel(skb, opts); 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci return err; 287362306a36Sopenharmony_ci} 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_cistatic int fl_dump_key_mpls(struct sk_buff *skb, 287662306a36Sopenharmony_ci struct flow_dissector_key_mpls *mpls_key, 287762306a36Sopenharmony_ci struct flow_dissector_key_mpls *mpls_mask) 287862306a36Sopenharmony_ci{ 287962306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_mask; 288062306a36Sopenharmony_ci struct flow_dissector_mpls_lse *lse_key; 288162306a36Sopenharmony_ci int err; 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci if (!mpls_mask->used_lses) 288462306a36Sopenharmony_ci return 0; 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_ci lse_mask = &mpls_mask->ls[0]; 288762306a36Sopenharmony_ci lse_key = &mpls_key->ls[0]; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci /* For backward compatibility, don't use the MPLS nested attributes if 289062306a36Sopenharmony_ci * the rule can be expressed using the old attributes. 289162306a36Sopenharmony_ci */ 289262306a36Sopenharmony_ci if (mpls_mask->used_lses & ~1 || 289362306a36Sopenharmony_ci (!lse_mask->mpls_ttl && !lse_mask->mpls_bos && 289462306a36Sopenharmony_ci !lse_mask->mpls_tc && !lse_mask->mpls_label)) 289562306a36Sopenharmony_ci return fl_dump_key_mpls_opts(skb, mpls_key, mpls_mask); 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci if (lse_mask->mpls_ttl) { 289862306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TTL, 289962306a36Sopenharmony_ci lse_key->mpls_ttl); 290062306a36Sopenharmony_ci if (err) 290162306a36Sopenharmony_ci return err; 290262306a36Sopenharmony_ci } 290362306a36Sopenharmony_ci if (lse_mask->mpls_tc) { 290462306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TC, 290562306a36Sopenharmony_ci lse_key->mpls_tc); 290662306a36Sopenharmony_ci if (err) 290762306a36Sopenharmony_ci return err; 290862306a36Sopenharmony_ci } 290962306a36Sopenharmony_ci if (lse_mask->mpls_label) { 291062306a36Sopenharmony_ci err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_LABEL, 291162306a36Sopenharmony_ci lse_key->mpls_label); 291262306a36Sopenharmony_ci if (err) 291362306a36Sopenharmony_ci return err; 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci if (lse_mask->mpls_bos) { 291662306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_BOS, 291762306a36Sopenharmony_ci lse_key->mpls_bos); 291862306a36Sopenharmony_ci if (err) 291962306a36Sopenharmony_ci return err; 292062306a36Sopenharmony_ci } 292162306a36Sopenharmony_ci return 0; 292262306a36Sopenharmony_ci} 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_cistatic int fl_dump_key_ip(struct sk_buff *skb, bool encap, 292562306a36Sopenharmony_ci struct flow_dissector_key_ip *key, 292662306a36Sopenharmony_ci struct flow_dissector_key_ip *mask) 292762306a36Sopenharmony_ci{ 292862306a36Sopenharmony_ci int tos_key = encap ? TCA_FLOWER_KEY_ENC_IP_TOS : TCA_FLOWER_KEY_IP_TOS; 292962306a36Sopenharmony_ci int ttl_key = encap ? TCA_FLOWER_KEY_ENC_IP_TTL : TCA_FLOWER_KEY_IP_TTL; 293062306a36Sopenharmony_ci int tos_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TOS_MASK : TCA_FLOWER_KEY_IP_TOS_MASK; 293162306a36Sopenharmony_ci int ttl_mask = encap ? TCA_FLOWER_KEY_ENC_IP_TTL_MASK : TCA_FLOWER_KEY_IP_TTL_MASK; 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci if (fl_dump_key_val(skb, &key->tos, tos_key, &mask->tos, tos_mask, sizeof(key->tos)) || 293462306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ttl, ttl_key, &mask->ttl, ttl_mask, sizeof(key->ttl))) 293562306a36Sopenharmony_ci return -1; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci return 0; 293862306a36Sopenharmony_ci} 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_cistatic int fl_dump_key_vlan(struct sk_buff *skb, 294162306a36Sopenharmony_ci int vlan_id_key, int vlan_prio_key, 294262306a36Sopenharmony_ci struct flow_dissector_key_vlan *vlan_key, 294362306a36Sopenharmony_ci struct flow_dissector_key_vlan *vlan_mask) 294462306a36Sopenharmony_ci{ 294562306a36Sopenharmony_ci int err; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci if (!memchr_inv(vlan_mask, 0, sizeof(*vlan_mask))) 294862306a36Sopenharmony_ci return 0; 294962306a36Sopenharmony_ci if (vlan_mask->vlan_id) { 295062306a36Sopenharmony_ci err = nla_put_u16(skb, vlan_id_key, 295162306a36Sopenharmony_ci vlan_key->vlan_id); 295262306a36Sopenharmony_ci if (err) 295362306a36Sopenharmony_ci return err; 295462306a36Sopenharmony_ci } 295562306a36Sopenharmony_ci if (vlan_mask->vlan_priority) { 295662306a36Sopenharmony_ci err = nla_put_u8(skb, vlan_prio_key, 295762306a36Sopenharmony_ci vlan_key->vlan_priority); 295862306a36Sopenharmony_ci if (err) 295962306a36Sopenharmony_ci return err; 296062306a36Sopenharmony_ci } 296162306a36Sopenharmony_ci return 0; 296262306a36Sopenharmony_ci} 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_cistatic void fl_get_key_flag(u32 dissector_key, u32 dissector_mask, 296562306a36Sopenharmony_ci u32 *flower_key, u32 *flower_mask, 296662306a36Sopenharmony_ci u32 flower_flag_bit, u32 dissector_flag_bit) 296762306a36Sopenharmony_ci{ 296862306a36Sopenharmony_ci if (dissector_mask & dissector_flag_bit) { 296962306a36Sopenharmony_ci *flower_mask |= flower_flag_bit; 297062306a36Sopenharmony_ci if (dissector_key & dissector_flag_bit) 297162306a36Sopenharmony_ci *flower_key |= flower_flag_bit; 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci} 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_cistatic int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) 297662306a36Sopenharmony_ci{ 297762306a36Sopenharmony_ci u32 key, mask; 297862306a36Sopenharmony_ci __be32 _key, _mask; 297962306a36Sopenharmony_ci int err; 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci if (!memchr_inv(&flags_mask, 0, sizeof(flags_mask))) 298262306a36Sopenharmony_ci return 0; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci key = 0; 298562306a36Sopenharmony_ci mask = 0; 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci fl_get_key_flag(flags_key, flags_mask, &key, &mask, 298862306a36Sopenharmony_ci TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT, FLOW_DIS_IS_FRAGMENT); 298962306a36Sopenharmony_ci fl_get_key_flag(flags_key, flags_mask, &key, &mask, 299062306a36Sopenharmony_ci TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST, 299162306a36Sopenharmony_ci FLOW_DIS_FIRST_FRAG); 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci _key = cpu_to_be32(key); 299462306a36Sopenharmony_ci _mask = cpu_to_be32(mask); 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci err = nla_put(skb, TCA_FLOWER_KEY_FLAGS, 4, &_key); 299762306a36Sopenharmony_ci if (err) 299862306a36Sopenharmony_ci return err; 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); 300162306a36Sopenharmony_ci} 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_cistatic int fl_dump_key_geneve_opt(struct sk_buff *skb, 300462306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *enc_opts) 300562306a36Sopenharmony_ci{ 300662306a36Sopenharmony_ci struct geneve_opt *opt; 300762306a36Sopenharmony_ci struct nlattr *nest; 300862306a36Sopenharmony_ci int opt_off = 0; 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_GENEVE); 301162306a36Sopenharmony_ci if (!nest) 301262306a36Sopenharmony_ci goto nla_put_failure; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci while (enc_opts->len > opt_off) { 301562306a36Sopenharmony_ci opt = (struct geneve_opt *)&enc_opts->data[opt_off]; 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci if (nla_put_be16(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, 301862306a36Sopenharmony_ci opt->opt_class)) 301962306a36Sopenharmony_ci goto nla_put_failure; 302062306a36Sopenharmony_ci if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, 302162306a36Sopenharmony_ci opt->type)) 302262306a36Sopenharmony_ci goto nla_put_failure; 302362306a36Sopenharmony_ci if (nla_put(skb, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, 302462306a36Sopenharmony_ci opt->length * 4, opt->opt_data)) 302562306a36Sopenharmony_ci goto nla_put_failure; 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci opt_off += sizeof(struct geneve_opt) + opt->length * 4; 302862306a36Sopenharmony_ci } 302962306a36Sopenharmony_ci nla_nest_end(skb, nest); 303062306a36Sopenharmony_ci return 0; 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_cinla_put_failure: 303362306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 303462306a36Sopenharmony_ci return -EMSGSIZE; 303562306a36Sopenharmony_ci} 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_cistatic int fl_dump_key_vxlan_opt(struct sk_buff *skb, 303862306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *enc_opts) 303962306a36Sopenharmony_ci{ 304062306a36Sopenharmony_ci struct vxlan_metadata *md; 304162306a36Sopenharmony_ci struct nlattr *nest; 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_VXLAN); 304462306a36Sopenharmony_ci if (!nest) 304562306a36Sopenharmony_ci goto nla_put_failure; 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci md = (struct vxlan_metadata *)&enc_opts->data[0]; 304862306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_FLOWER_KEY_ENC_OPT_VXLAN_GBP, md->gbp)) 304962306a36Sopenharmony_ci goto nla_put_failure; 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci nla_nest_end(skb, nest); 305262306a36Sopenharmony_ci return 0; 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_cinla_put_failure: 305562306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 305662306a36Sopenharmony_ci return -EMSGSIZE; 305762306a36Sopenharmony_ci} 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_cistatic int fl_dump_key_erspan_opt(struct sk_buff *skb, 306062306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *enc_opts) 306162306a36Sopenharmony_ci{ 306262306a36Sopenharmony_ci struct erspan_metadata *md; 306362306a36Sopenharmony_ci struct nlattr *nest; 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_ERSPAN); 306662306a36Sopenharmony_ci if (!nest) 306762306a36Sopenharmony_ci goto nla_put_failure; 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci md = (struct erspan_metadata *)&enc_opts->data[0]; 307062306a36Sopenharmony_ci if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_VER, md->version)) 307162306a36Sopenharmony_ci goto nla_put_failure; 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci if (md->version == 1 && 307462306a36Sopenharmony_ci nla_put_be32(skb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_INDEX, md->u.index)) 307562306a36Sopenharmony_ci goto nla_put_failure; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci if (md->version == 2 && 307862306a36Sopenharmony_ci (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_DIR, 307962306a36Sopenharmony_ci md->u.md2.dir) || 308062306a36Sopenharmony_ci nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_ERSPAN_HWID, 308162306a36Sopenharmony_ci get_hwid(&md->u.md2)))) 308262306a36Sopenharmony_ci goto nla_put_failure; 308362306a36Sopenharmony_ci 308462306a36Sopenharmony_ci nla_nest_end(skb, nest); 308562306a36Sopenharmony_ci return 0; 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_cinla_put_failure: 308862306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 308962306a36Sopenharmony_ci return -EMSGSIZE; 309062306a36Sopenharmony_ci} 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_cistatic int fl_dump_key_gtp_opt(struct sk_buff *skb, 309362306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *enc_opts) 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci{ 309662306a36Sopenharmony_ci struct gtp_pdu_session_info *session_info; 309762306a36Sopenharmony_ci struct nlattr *nest; 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_FLOWER_KEY_ENC_OPTS_GTP); 310062306a36Sopenharmony_ci if (!nest) 310162306a36Sopenharmony_ci goto nla_put_failure; 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci session_info = (struct gtp_pdu_session_info *)&enc_opts->data[0]; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE, 310662306a36Sopenharmony_ci session_info->pdu_type)) 310762306a36Sopenharmony_ci goto nla_put_failure; 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci if (nla_put_u8(skb, TCA_FLOWER_KEY_ENC_OPT_GTP_QFI, session_info->qfi)) 311062306a36Sopenharmony_ci goto nla_put_failure; 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci nla_nest_end(skb, nest); 311362306a36Sopenharmony_ci return 0; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_cinla_put_failure: 311662306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 311762306a36Sopenharmony_ci return -EMSGSIZE; 311862306a36Sopenharmony_ci} 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_cistatic int fl_dump_key_ct(struct sk_buff *skb, 312162306a36Sopenharmony_ci struct flow_dissector_key_ct *key, 312262306a36Sopenharmony_ci struct flow_dissector_key_ct *mask) 312362306a36Sopenharmony_ci{ 312462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NF_CONNTRACK) && 312562306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ct_state, TCA_FLOWER_KEY_CT_STATE, 312662306a36Sopenharmony_ci &mask->ct_state, TCA_FLOWER_KEY_CT_STATE_MASK, 312762306a36Sopenharmony_ci sizeof(key->ct_state))) 312862306a36Sopenharmony_ci goto nla_put_failure; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES) && 313162306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ct_zone, TCA_FLOWER_KEY_CT_ZONE, 313262306a36Sopenharmony_ci &mask->ct_zone, TCA_FLOWER_KEY_CT_ZONE_MASK, 313362306a36Sopenharmony_ci sizeof(key->ct_zone))) 313462306a36Sopenharmony_ci goto nla_put_failure; 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) && 313762306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ct_mark, TCA_FLOWER_KEY_CT_MARK, 313862306a36Sopenharmony_ci &mask->ct_mark, TCA_FLOWER_KEY_CT_MARK_MASK, 313962306a36Sopenharmony_ci sizeof(key->ct_mark))) 314062306a36Sopenharmony_ci goto nla_put_failure; 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) && 314362306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ct_labels, TCA_FLOWER_KEY_CT_LABELS, 314462306a36Sopenharmony_ci &mask->ct_labels, TCA_FLOWER_KEY_CT_LABELS_MASK, 314562306a36Sopenharmony_ci sizeof(key->ct_labels))) 314662306a36Sopenharmony_ci goto nla_put_failure; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci return 0; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_cinla_put_failure: 315162306a36Sopenharmony_ci return -EMSGSIZE; 315262306a36Sopenharmony_ci} 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_cistatic int fl_dump_key_cfm(struct sk_buff *skb, 315562306a36Sopenharmony_ci struct flow_dissector_key_cfm *key, 315662306a36Sopenharmony_ci struct flow_dissector_key_cfm *mask) 315762306a36Sopenharmony_ci{ 315862306a36Sopenharmony_ci struct nlattr *opts; 315962306a36Sopenharmony_ci int err; 316062306a36Sopenharmony_ci u8 mdl; 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci if (!memchr_inv(mask, 0, sizeof(*mask))) 316362306a36Sopenharmony_ci return 0; 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci opts = nla_nest_start(skb, TCA_FLOWER_KEY_CFM); 316662306a36Sopenharmony_ci if (!opts) 316762306a36Sopenharmony_ci return -EMSGSIZE; 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci if (FIELD_GET(FLOW_DIS_CFM_MDL_MASK, mask->mdl_ver)) { 317062306a36Sopenharmony_ci mdl = FIELD_GET(FLOW_DIS_CFM_MDL_MASK, key->mdl_ver); 317162306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_CFM_MD_LEVEL, mdl); 317262306a36Sopenharmony_ci if (err) 317362306a36Sopenharmony_ci goto err_cfm_opts; 317462306a36Sopenharmony_ci } 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci if (mask->opcode) { 317762306a36Sopenharmony_ci err = nla_put_u8(skb, TCA_FLOWER_KEY_CFM_OPCODE, key->opcode); 317862306a36Sopenharmony_ci if (err) 317962306a36Sopenharmony_ci goto err_cfm_opts; 318062306a36Sopenharmony_ci } 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci nla_nest_end(skb, opts); 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci return 0; 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_cierr_cfm_opts: 318762306a36Sopenharmony_ci nla_nest_cancel(skb, opts); 318862306a36Sopenharmony_ci return err; 318962306a36Sopenharmony_ci} 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_cistatic int fl_dump_key_options(struct sk_buff *skb, int enc_opt_type, 319262306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *enc_opts) 319362306a36Sopenharmony_ci{ 319462306a36Sopenharmony_ci struct nlattr *nest; 319562306a36Sopenharmony_ci int err; 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci if (!enc_opts->len) 319862306a36Sopenharmony_ci return 0; 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, enc_opt_type); 320162306a36Sopenharmony_ci if (!nest) 320262306a36Sopenharmony_ci goto nla_put_failure; 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci switch (enc_opts->dst_opt_type) { 320562306a36Sopenharmony_ci case TUNNEL_GENEVE_OPT: 320662306a36Sopenharmony_ci err = fl_dump_key_geneve_opt(skb, enc_opts); 320762306a36Sopenharmony_ci if (err) 320862306a36Sopenharmony_ci goto nla_put_failure; 320962306a36Sopenharmony_ci break; 321062306a36Sopenharmony_ci case TUNNEL_VXLAN_OPT: 321162306a36Sopenharmony_ci err = fl_dump_key_vxlan_opt(skb, enc_opts); 321262306a36Sopenharmony_ci if (err) 321362306a36Sopenharmony_ci goto nla_put_failure; 321462306a36Sopenharmony_ci break; 321562306a36Sopenharmony_ci case TUNNEL_ERSPAN_OPT: 321662306a36Sopenharmony_ci err = fl_dump_key_erspan_opt(skb, enc_opts); 321762306a36Sopenharmony_ci if (err) 321862306a36Sopenharmony_ci goto nla_put_failure; 321962306a36Sopenharmony_ci break; 322062306a36Sopenharmony_ci case TUNNEL_GTP_OPT: 322162306a36Sopenharmony_ci err = fl_dump_key_gtp_opt(skb, enc_opts); 322262306a36Sopenharmony_ci if (err) 322362306a36Sopenharmony_ci goto nla_put_failure; 322462306a36Sopenharmony_ci break; 322562306a36Sopenharmony_ci default: 322662306a36Sopenharmony_ci goto nla_put_failure; 322762306a36Sopenharmony_ci } 322862306a36Sopenharmony_ci nla_nest_end(skb, nest); 322962306a36Sopenharmony_ci return 0; 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_cinla_put_failure: 323262306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 323362306a36Sopenharmony_ci return -EMSGSIZE; 323462306a36Sopenharmony_ci} 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_cistatic int fl_dump_key_enc_opt(struct sk_buff *skb, 323762306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *key_opts, 323862306a36Sopenharmony_ci struct flow_dissector_key_enc_opts *msk_opts) 323962306a36Sopenharmony_ci{ 324062306a36Sopenharmony_ci int err; 324162306a36Sopenharmony_ci 324262306a36Sopenharmony_ci err = fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS, key_opts); 324362306a36Sopenharmony_ci if (err) 324462306a36Sopenharmony_ci return err; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci return fl_dump_key_options(skb, TCA_FLOWER_KEY_ENC_OPTS_MASK, msk_opts); 324762306a36Sopenharmony_ci} 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_cistatic int fl_dump_key(struct sk_buff *skb, struct net *net, 325062306a36Sopenharmony_ci struct fl_flow_key *key, struct fl_flow_key *mask) 325162306a36Sopenharmony_ci{ 325262306a36Sopenharmony_ci if (mask->meta.ingress_ifindex) { 325362306a36Sopenharmony_ci struct net_device *dev; 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci dev = __dev_get_by_index(net, key->meta.ingress_ifindex); 325662306a36Sopenharmony_ci if (dev && nla_put_string(skb, TCA_FLOWER_INDEV, dev->name)) 325762306a36Sopenharmony_ci goto nla_put_failure; 325862306a36Sopenharmony_ci } 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci if (fl_dump_key_val(skb, &key->meta.l2_miss, 326162306a36Sopenharmony_ci TCA_FLOWER_L2_MISS, &mask->meta.l2_miss, 326262306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, sizeof(key->meta.l2_miss))) 326362306a36Sopenharmony_ci goto nla_put_failure; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci if (fl_dump_key_val(skb, key->eth.dst, TCA_FLOWER_KEY_ETH_DST, 326662306a36Sopenharmony_ci mask->eth.dst, TCA_FLOWER_KEY_ETH_DST_MASK, 326762306a36Sopenharmony_ci sizeof(key->eth.dst)) || 326862306a36Sopenharmony_ci fl_dump_key_val(skb, key->eth.src, TCA_FLOWER_KEY_ETH_SRC, 326962306a36Sopenharmony_ci mask->eth.src, TCA_FLOWER_KEY_ETH_SRC_MASK, 327062306a36Sopenharmony_ci sizeof(key->eth.src)) || 327162306a36Sopenharmony_ci fl_dump_key_val(skb, &key->basic.n_proto, TCA_FLOWER_KEY_ETH_TYPE, 327262306a36Sopenharmony_ci &mask->basic.n_proto, TCA_FLOWER_UNSPEC, 327362306a36Sopenharmony_ci sizeof(key->basic.n_proto))) 327462306a36Sopenharmony_ci goto nla_put_failure; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci if (mask->num_of_vlans.num_of_vlans) { 327762306a36Sopenharmony_ci if (nla_put_u8(skb, TCA_FLOWER_KEY_NUM_OF_VLANS, key->num_of_vlans.num_of_vlans)) 327862306a36Sopenharmony_ci goto nla_put_failure; 327962306a36Sopenharmony_ci } 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci if (fl_dump_key_mpls(skb, &key->mpls, &mask->mpls)) 328262306a36Sopenharmony_ci goto nla_put_failure; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_VLAN_ID, 328562306a36Sopenharmony_ci TCA_FLOWER_KEY_VLAN_PRIO, &key->vlan, &mask->vlan)) 328662306a36Sopenharmony_ci goto nla_put_failure; 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci if (fl_dump_key_vlan(skb, TCA_FLOWER_KEY_CVLAN_ID, 328962306a36Sopenharmony_ci TCA_FLOWER_KEY_CVLAN_PRIO, 329062306a36Sopenharmony_ci &key->cvlan, &mask->cvlan) || 329162306a36Sopenharmony_ci (mask->cvlan.vlan_tpid && 329262306a36Sopenharmony_ci nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 329362306a36Sopenharmony_ci key->cvlan.vlan_tpid))) 329462306a36Sopenharmony_ci goto nla_put_failure; 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci if (mask->basic.n_proto) { 329762306a36Sopenharmony_ci if (mask->cvlan.vlan_eth_type) { 329862306a36Sopenharmony_ci if (nla_put_be16(skb, TCA_FLOWER_KEY_CVLAN_ETH_TYPE, 329962306a36Sopenharmony_ci key->basic.n_proto)) 330062306a36Sopenharmony_ci goto nla_put_failure; 330162306a36Sopenharmony_ci } else if (mask->vlan.vlan_eth_type) { 330262306a36Sopenharmony_ci if (nla_put_be16(skb, TCA_FLOWER_KEY_VLAN_ETH_TYPE, 330362306a36Sopenharmony_ci key->vlan.vlan_eth_type)) 330462306a36Sopenharmony_ci goto nla_put_failure; 330562306a36Sopenharmony_ci } 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci if ((key->basic.n_proto == htons(ETH_P_IP) || 330962306a36Sopenharmony_ci key->basic.n_proto == htons(ETH_P_IPV6)) && 331062306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->basic.ip_proto, TCA_FLOWER_KEY_IP_PROTO, 331162306a36Sopenharmony_ci &mask->basic.ip_proto, TCA_FLOWER_UNSPEC, 331262306a36Sopenharmony_ci sizeof(key->basic.ip_proto)) || 331362306a36Sopenharmony_ci fl_dump_key_ip(skb, false, &key->ip, &mask->ip))) 331462306a36Sopenharmony_ci goto nla_put_failure; 331562306a36Sopenharmony_ci 331662306a36Sopenharmony_ci if (mask->pppoe.session_id) { 331762306a36Sopenharmony_ci if (nla_put_be16(skb, TCA_FLOWER_KEY_PPPOE_SID, 331862306a36Sopenharmony_ci key->pppoe.session_id)) 331962306a36Sopenharmony_ci goto nla_put_failure; 332062306a36Sopenharmony_ci } 332162306a36Sopenharmony_ci if (mask->basic.n_proto && mask->pppoe.ppp_proto) { 332262306a36Sopenharmony_ci if (nla_put_be16(skb, TCA_FLOWER_KEY_PPP_PROTO, 332362306a36Sopenharmony_ci key->pppoe.ppp_proto)) 332462306a36Sopenharmony_ci goto nla_put_failure; 332562306a36Sopenharmony_ci } 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 332862306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC, 332962306a36Sopenharmony_ci &mask->ipv4.src, TCA_FLOWER_KEY_IPV4_SRC_MASK, 333062306a36Sopenharmony_ci sizeof(key->ipv4.src)) || 333162306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST, 333262306a36Sopenharmony_ci &mask->ipv4.dst, TCA_FLOWER_KEY_IPV4_DST_MASK, 333362306a36Sopenharmony_ci sizeof(key->ipv4.dst)))) 333462306a36Sopenharmony_ci goto nla_put_failure; 333562306a36Sopenharmony_ci else if (key->control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 333662306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC, 333762306a36Sopenharmony_ci &mask->ipv6.src, TCA_FLOWER_KEY_IPV6_SRC_MASK, 333862306a36Sopenharmony_ci sizeof(key->ipv6.src)) || 333962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST, 334062306a36Sopenharmony_ci &mask->ipv6.dst, TCA_FLOWER_KEY_IPV6_DST_MASK, 334162306a36Sopenharmony_ci sizeof(key->ipv6.dst)))) 334262306a36Sopenharmony_ci goto nla_put_failure; 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci if (key->basic.ip_proto == IPPROTO_TCP && 334562306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_TCP_SRC, 334662306a36Sopenharmony_ci &mask->tp.src, TCA_FLOWER_KEY_TCP_SRC_MASK, 334762306a36Sopenharmony_ci sizeof(key->tp.src)) || 334862306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_TCP_DST, 334962306a36Sopenharmony_ci &mask->tp.dst, TCA_FLOWER_KEY_TCP_DST_MASK, 335062306a36Sopenharmony_ci sizeof(key->tp.dst)) || 335162306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS, 335262306a36Sopenharmony_ci &mask->tcp.flags, TCA_FLOWER_KEY_TCP_FLAGS_MASK, 335362306a36Sopenharmony_ci sizeof(key->tcp.flags)))) 335462306a36Sopenharmony_ci goto nla_put_failure; 335562306a36Sopenharmony_ci else if (key->basic.ip_proto == IPPROTO_UDP && 335662306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_UDP_SRC, 335762306a36Sopenharmony_ci &mask->tp.src, TCA_FLOWER_KEY_UDP_SRC_MASK, 335862306a36Sopenharmony_ci sizeof(key->tp.src)) || 335962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_UDP_DST, 336062306a36Sopenharmony_ci &mask->tp.dst, TCA_FLOWER_KEY_UDP_DST_MASK, 336162306a36Sopenharmony_ci sizeof(key->tp.dst)))) 336262306a36Sopenharmony_ci goto nla_put_failure; 336362306a36Sopenharmony_ci else if (key->basic.ip_proto == IPPROTO_SCTP && 336462306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->tp.src, TCA_FLOWER_KEY_SCTP_SRC, 336562306a36Sopenharmony_ci &mask->tp.src, TCA_FLOWER_KEY_SCTP_SRC_MASK, 336662306a36Sopenharmony_ci sizeof(key->tp.src)) || 336762306a36Sopenharmony_ci fl_dump_key_val(skb, &key->tp.dst, TCA_FLOWER_KEY_SCTP_DST, 336862306a36Sopenharmony_ci &mask->tp.dst, TCA_FLOWER_KEY_SCTP_DST_MASK, 336962306a36Sopenharmony_ci sizeof(key->tp.dst)))) 337062306a36Sopenharmony_ci goto nla_put_failure; 337162306a36Sopenharmony_ci else if (key->basic.n_proto == htons(ETH_P_IP) && 337262306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_ICMP && 337362306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->icmp.type, 337462306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV4_TYPE, &mask->icmp.type, 337562306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV4_TYPE_MASK, 337662306a36Sopenharmony_ci sizeof(key->icmp.type)) || 337762306a36Sopenharmony_ci fl_dump_key_val(skb, &key->icmp.code, 337862306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV4_CODE, &mask->icmp.code, 337962306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV4_CODE_MASK, 338062306a36Sopenharmony_ci sizeof(key->icmp.code)))) 338162306a36Sopenharmony_ci goto nla_put_failure; 338262306a36Sopenharmony_ci else if (key->basic.n_proto == htons(ETH_P_IPV6) && 338362306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_ICMPV6 && 338462306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->icmp.type, 338562306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV6_TYPE, &mask->icmp.type, 338662306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV6_TYPE_MASK, 338762306a36Sopenharmony_ci sizeof(key->icmp.type)) || 338862306a36Sopenharmony_ci fl_dump_key_val(skb, &key->icmp.code, 338962306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV6_CODE, &mask->icmp.code, 339062306a36Sopenharmony_ci TCA_FLOWER_KEY_ICMPV6_CODE_MASK, 339162306a36Sopenharmony_ci sizeof(key->icmp.code)))) 339262306a36Sopenharmony_ci goto nla_put_failure; 339362306a36Sopenharmony_ci else if ((key->basic.n_proto == htons(ETH_P_ARP) || 339462306a36Sopenharmony_ci key->basic.n_proto == htons(ETH_P_RARP)) && 339562306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->arp.sip, 339662306a36Sopenharmony_ci TCA_FLOWER_KEY_ARP_SIP, &mask->arp.sip, 339762306a36Sopenharmony_ci TCA_FLOWER_KEY_ARP_SIP_MASK, 339862306a36Sopenharmony_ci sizeof(key->arp.sip)) || 339962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->arp.tip, 340062306a36Sopenharmony_ci TCA_FLOWER_KEY_ARP_TIP, &mask->arp.tip, 340162306a36Sopenharmony_ci TCA_FLOWER_KEY_ARP_TIP_MASK, 340262306a36Sopenharmony_ci sizeof(key->arp.tip)) || 340362306a36Sopenharmony_ci fl_dump_key_val(skb, &key->arp.op, 340462306a36Sopenharmony_ci TCA_FLOWER_KEY_ARP_OP, &mask->arp.op, 340562306a36Sopenharmony_ci TCA_FLOWER_KEY_ARP_OP_MASK, 340662306a36Sopenharmony_ci sizeof(key->arp.op)) || 340762306a36Sopenharmony_ci fl_dump_key_val(skb, key->arp.sha, TCA_FLOWER_KEY_ARP_SHA, 340862306a36Sopenharmony_ci mask->arp.sha, TCA_FLOWER_KEY_ARP_SHA_MASK, 340962306a36Sopenharmony_ci sizeof(key->arp.sha)) || 341062306a36Sopenharmony_ci fl_dump_key_val(skb, key->arp.tha, TCA_FLOWER_KEY_ARP_THA, 341162306a36Sopenharmony_ci mask->arp.tha, TCA_FLOWER_KEY_ARP_THA_MASK, 341262306a36Sopenharmony_ci sizeof(key->arp.tha)))) 341362306a36Sopenharmony_ci goto nla_put_failure; 341462306a36Sopenharmony_ci else if (key->basic.ip_proto == IPPROTO_L2TP && 341562306a36Sopenharmony_ci fl_dump_key_val(skb, &key->l2tpv3.session_id, 341662306a36Sopenharmony_ci TCA_FLOWER_KEY_L2TPV3_SID, 341762306a36Sopenharmony_ci &mask->l2tpv3.session_id, 341862306a36Sopenharmony_ci TCA_FLOWER_UNSPEC, 341962306a36Sopenharmony_ci sizeof(key->l2tpv3.session_id))) 342062306a36Sopenharmony_ci goto nla_put_failure; 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci if (key->ipsec.spi && 342362306a36Sopenharmony_ci fl_dump_key_val(skb, &key->ipsec.spi, TCA_FLOWER_KEY_SPI, 342462306a36Sopenharmony_ci &mask->ipsec.spi, TCA_FLOWER_KEY_SPI_MASK, 342562306a36Sopenharmony_ci sizeof(key->ipsec.spi))) 342662306a36Sopenharmony_ci goto nla_put_failure; 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_ci if ((key->basic.ip_proto == IPPROTO_TCP || 342962306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_UDP || 343062306a36Sopenharmony_ci key->basic.ip_proto == IPPROTO_SCTP) && 343162306a36Sopenharmony_ci fl_dump_key_port_range(skb, key, mask)) 343262306a36Sopenharmony_ci goto nla_put_failure; 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS && 343562306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->enc_ipv4.src, 343662306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_SRC, &mask->enc_ipv4.src, 343762306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK, 343862306a36Sopenharmony_ci sizeof(key->enc_ipv4.src)) || 343962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->enc_ipv4.dst, 344062306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_DST, &mask->enc_ipv4.dst, 344162306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV4_DST_MASK, 344262306a36Sopenharmony_ci sizeof(key->enc_ipv4.dst)))) 344362306a36Sopenharmony_ci goto nla_put_failure; 344462306a36Sopenharmony_ci else if (key->enc_control.addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS && 344562306a36Sopenharmony_ci (fl_dump_key_val(skb, &key->enc_ipv6.src, 344662306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_SRC, &mask->enc_ipv6.src, 344762306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK, 344862306a36Sopenharmony_ci sizeof(key->enc_ipv6.src)) || 344962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->enc_ipv6.dst, 345062306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_DST, 345162306a36Sopenharmony_ci &mask->enc_ipv6.dst, 345262306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_IPV6_DST_MASK, 345362306a36Sopenharmony_ci sizeof(key->enc_ipv6.dst)))) 345462306a36Sopenharmony_ci goto nla_put_failure; 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci if (fl_dump_key_val(skb, &key->enc_key_id, TCA_FLOWER_KEY_ENC_KEY_ID, 345762306a36Sopenharmony_ci &mask->enc_key_id, TCA_FLOWER_UNSPEC, 345862306a36Sopenharmony_ci sizeof(key->enc_key_id)) || 345962306a36Sopenharmony_ci fl_dump_key_val(skb, &key->enc_tp.src, 346062306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_UDP_SRC_PORT, 346162306a36Sopenharmony_ci &mask->enc_tp.src, 346262306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_UDP_SRC_PORT_MASK, 346362306a36Sopenharmony_ci sizeof(key->enc_tp.src)) || 346462306a36Sopenharmony_ci fl_dump_key_val(skb, &key->enc_tp.dst, 346562306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_UDP_DST_PORT, 346662306a36Sopenharmony_ci &mask->enc_tp.dst, 346762306a36Sopenharmony_ci TCA_FLOWER_KEY_ENC_UDP_DST_PORT_MASK, 346862306a36Sopenharmony_ci sizeof(key->enc_tp.dst)) || 346962306a36Sopenharmony_ci fl_dump_key_ip(skb, true, &key->enc_ip, &mask->enc_ip) || 347062306a36Sopenharmony_ci fl_dump_key_enc_opt(skb, &key->enc_opts, &mask->enc_opts)) 347162306a36Sopenharmony_ci goto nla_put_failure; 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci if (fl_dump_key_ct(skb, &key->ct, &mask->ct)) 347462306a36Sopenharmony_ci goto nla_put_failure; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci if (fl_dump_key_flags(skb, key->control.flags, mask->control.flags)) 347762306a36Sopenharmony_ci goto nla_put_failure; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci if (fl_dump_key_val(skb, &key->hash.hash, TCA_FLOWER_KEY_HASH, 348062306a36Sopenharmony_ci &mask->hash.hash, TCA_FLOWER_KEY_HASH_MASK, 348162306a36Sopenharmony_ci sizeof(key->hash.hash))) 348262306a36Sopenharmony_ci goto nla_put_failure; 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci if (fl_dump_key_cfm(skb, &key->cfm, &mask->cfm)) 348562306a36Sopenharmony_ci goto nla_put_failure; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci return 0; 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_cinla_put_failure: 349062306a36Sopenharmony_ci return -EMSGSIZE; 349162306a36Sopenharmony_ci} 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_cistatic int fl_dump(struct net *net, struct tcf_proto *tp, void *fh, 349462306a36Sopenharmony_ci struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 349562306a36Sopenharmony_ci{ 349662306a36Sopenharmony_ci struct cls_fl_filter *f = fh; 349762306a36Sopenharmony_ci struct nlattr *nest; 349862306a36Sopenharmony_ci struct fl_flow_key *key, *mask; 349962306a36Sopenharmony_ci bool skip_hw; 350062306a36Sopenharmony_ci 350162306a36Sopenharmony_ci if (!f) 350262306a36Sopenharmony_ci return skb->len; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci t->tcm_handle = f->handle; 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 350762306a36Sopenharmony_ci if (!nest) 350862306a36Sopenharmony_ci goto nla_put_failure; 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci spin_lock(&tp->lock); 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci if (f->res.classid && 351362306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOWER_CLASSID, f->res.classid)) 351462306a36Sopenharmony_ci goto nla_put_failure_locked; 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci key = &f->key; 351762306a36Sopenharmony_ci mask = &f->mask->key; 351862306a36Sopenharmony_ci skip_hw = tc_skip_hw(f->flags); 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_ci if (fl_dump_key(skb, net, key, mask)) 352162306a36Sopenharmony_ci goto nla_put_failure_locked; 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags)) 352462306a36Sopenharmony_ci goto nla_put_failure_locked; 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci spin_unlock(&tp->lock); 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci if (!skip_hw) 352962306a36Sopenharmony_ci fl_hw_update_stats(tp, f, rtnl_held); 353062306a36Sopenharmony_ci 353162306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_FLOWER_IN_HW_COUNT, f->in_hw_count)) 353262306a36Sopenharmony_ci goto nla_put_failure; 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci if (tcf_exts_dump(skb, &f->exts)) 353562306a36Sopenharmony_ci goto nla_put_failure; 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci nla_nest_end(skb, nest); 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_ci if (tcf_exts_dump_stats(skb, &f->exts) < 0) 354062306a36Sopenharmony_ci goto nla_put_failure; 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci return skb->len; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_cinla_put_failure_locked: 354562306a36Sopenharmony_ci spin_unlock(&tp->lock); 354662306a36Sopenharmony_cinla_put_failure: 354762306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 354862306a36Sopenharmony_ci return -1; 354962306a36Sopenharmony_ci} 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_cistatic int fl_terse_dump(struct net *net, struct tcf_proto *tp, void *fh, 355262306a36Sopenharmony_ci struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 355362306a36Sopenharmony_ci{ 355462306a36Sopenharmony_ci struct cls_fl_filter *f = fh; 355562306a36Sopenharmony_ci struct nlattr *nest; 355662306a36Sopenharmony_ci bool skip_hw; 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci if (!f) 355962306a36Sopenharmony_ci return skb->len; 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci t->tcm_handle = f->handle; 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 356462306a36Sopenharmony_ci if (!nest) 356562306a36Sopenharmony_ci goto nla_put_failure; 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci spin_lock(&tp->lock); 356862306a36Sopenharmony_ci 356962306a36Sopenharmony_ci skip_hw = tc_skip_hw(f->flags); 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ci if (f->flags && nla_put_u32(skb, TCA_FLOWER_FLAGS, f->flags)) 357262306a36Sopenharmony_ci goto nla_put_failure_locked; 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci spin_unlock(&tp->lock); 357562306a36Sopenharmony_ci 357662306a36Sopenharmony_ci if (!skip_hw) 357762306a36Sopenharmony_ci fl_hw_update_stats(tp, f, rtnl_held); 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci if (tcf_exts_terse_dump(skb, &f->exts)) 358062306a36Sopenharmony_ci goto nla_put_failure; 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_ci nla_nest_end(skb, nest); 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci return skb->len; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_cinla_put_failure_locked: 358762306a36Sopenharmony_ci spin_unlock(&tp->lock); 358862306a36Sopenharmony_cinla_put_failure: 358962306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 359062306a36Sopenharmony_ci return -1; 359162306a36Sopenharmony_ci} 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_cistatic int fl_tmplt_dump(struct sk_buff *skb, struct net *net, void *tmplt_priv) 359462306a36Sopenharmony_ci{ 359562306a36Sopenharmony_ci struct fl_flow_tmplt *tmplt = tmplt_priv; 359662306a36Sopenharmony_ci struct fl_flow_key *key, *mask; 359762306a36Sopenharmony_ci struct nlattr *nest; 359862306a36Sopenharmony_ci 359962306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 360062306a36Sopenharmony_ci if (!nest) 360162306a36Sopenharmony_ci goto nla_put_failure; 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci key = &tmplt->dummy_key; 360462306a36Sopenharmony_ci mask = &tmplt->mask; 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci if (fl_dump_key(skb, net, key, mask)) 360762306a36Sopenharmony_ci goto nla_put_failure; 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ci nla_nest_end(skb, nest); 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ci return skb->len; 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_cinla_put_failure: 361462306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 361562306a36Sopenharmony_ci return -EMSGSIZE; 361662306a36Sopenharmony_ci} 361762306a36Sopenharmony_ci 361862306a36Sopenharmony_cistatic void fl_bind_class(void *fh, u32 classid, unsigned long cl, void *q, 361962306a36Sopenharmony_ci unsigned long base) 362062306a36Sopenharmony_ci{ 362162306a36Sopenharmony_ci struct cls_fl_filter *f = fh; 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci tc_cls_bind_class(classid, cl, q, &f->res, base); 362462306a36Sopenharmony_ci} 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_cistatic bool fl_delete_empty(struct tcf_proto *tp) 362762306a36Sopenharmony_ci{ 362862306a36Sopenharmony_ci struct cls_fl_head *head = fl_head_dereference(tp); 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_ci spin_lock(&tp->lock); 363162306a36Sopenharmony_ci tp->deleting = idr_is_empty(&head->handle_idr); 363262306a36Sopenharmony_ci spin_unlock(&tp->lock); 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci return tp->deleting; 363562306a36Sopenharmony_ci} 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_cistatic struct tcf_proto_ops cls_fl_ops __read_mostly = { 363862306a36Sopenharmony_ci .kind = "flower", 363962306a36Sopenharmony_ci .classify = fl_classify, 364062306a36Sopenharmony_ci .init = fl_init, 364162306a36Sopenharmony_ci .destroy = fl_destroy, 364262306a36Sopenharmony_ci .get = fl_get, 364362306a36Sopenharmony_ci .put = fl_put, 364462306a36Sopenharmony_ci .change = fl_change, 364562306a36Sopenharmony_ci .delete = fl_delete, 364662306a36Sopenharmony_ci .delete_empty = fl_delete_empty, 364762306a36Sopenharmony_ci .walk = fl_walk, 364862306a36Sopenharmony_ci .reoffload = fl_reoffload, 364962306a36Sopenharmony_ci .hw_add = fl_hw_add, 365062306a36Sopenharmony_ci .hw_del = fl_hw_del, 365162306a36Sopenharmony_ci .dump = fl_dump, 365262306a36Sopenharmony_ci .terse_dump = fl_terse_dump, 365362306a36Sopenharmony_ci .bind_class = fl_bind_class, 365462306a36Sopenharmony_ci .tmplt_create = fl_tmplt_create, 365562306a36Sopenharmony_ci .tmplt_destroy = fl_tmplt_destroy, 365662306a36Sopenharmony_ci .tmplt_reoffload = fl_tmplt_reoffload, 365762306a36Sopenharmony_ci .tmplt_dump = fl_tmplt_dump, 365862306a36Sopenharmony_ci .get_exts = fl_get_exts, 365962306a36Sopenharmony_ci .owner = THIS_MODULE, 366062306a36Sopenharmony_ci .flags = TCF_PROTO_OPS_DOIT_UNLOCKED, 366162306a36Sopenharmony_ci}; 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_cistatic int __init cls_fl_init(void) 366462306a36Sopenharmony_ci{ 366562306a36Sopenharmony_ci return register_tcf_proto_ops(&cls_fl_ops); 366662306a36Sopenharmony_ci} 366762306a36Sopenharmony_ci 366862306a36Sopenharmony_cistatic void __exit cls_fl_exit(void) 366962306a36Sopenharmony_ci{ 367062306a36Sopenharmony_ci unregister_tcf_proto_ops(&cls_fl_ops); 367162306a36Sopenharmony_ci} 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_cimodule_init(cls_fl_init); 367462306a36Sopenharmony_cimodule_exit(cls_fl_exit); 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ciMODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 367762306a36Sopenharmony_ciMODULE_DESCRIPTION("Flower classifier"); 367862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3679