162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/cls_flow.c Generic flow classifier 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/list.h> 1162306a36Sopenharmony_ci#include <linux/jhash.h> 1262306a36Sopenharmony_ci#include <linux/random.h> 1362306a36Sopenharmony_ci#include <linux/pkt_cls.h> 1462306a36Sopenharmony_ci#include <linux/skbuff.h> 1562306a36Sopenharmony_ci#include <linux/in.h> 1662306a36Sopenharmony_ci#include <linux/ip.h> 1762306a36Sopenharmony_ci#include <linux/ipv6.h> 1862306a36Sopenharmony_ci#include <linux/if_vlan.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <net/inet_sock.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <net/pkt_cls.h> 2462306a36Sopenharmony_ci#include <net/ip.h> 2562306a36Sopenharmony_ci#include <net/route.h> 2662306a36Sopenharmony_ci#include <net/flow_dissector.h> 2762306a36Sopenharmony_ci#include <net/tc_wrapper.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NF_CONNTRACK) 3062306a36Sopenharmony_ci#include <net/netfilter/nf_conntrack.h> 3162306a36Sopenharmony_ci#endif 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct flow_head { 3462306a36Sopenharmony_ci struct list_head filters; 3562306a36Sopenharmony_ci struct rcu_head rcu; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct flow_filter { 3962306a36Sopenharmony_ci struct list_head list; 4062306a36Sopenharmony_ci struct tcf_exts exts; 4162306a36Sopenharmony_ci struct tcf_ematch_tree ematches; 4262306a36Sopenharmony_ci struct tcf_proto *tp; 4362306a36Sopenharmony_ci struct timer_list perturb_timer; 4462306a36Sopenharmony_ci u32 perturb_period; 4562306a36Sopenharmony_ci u32 handle; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci u32 nkeys; 4862306a36Sopenharmony_ci u32 keymask; 4962306a36Sopenharmony_ci u32 mode; 5062306a36Sopenharmony_ci u32 mask; 5162306a36Sopenharmony_ci u32 xor; 5262306a36Sopenharmony_ci u32 rshift; 5362306a36Sopenharmony_ci u32 addend; 5462306a36Sopenharmony_ci u32 divisor; 5562306a36Sopenharmony_ci u32 baseclass; 5662306a36Sopenharmony_ci u32 hashrnd; 5762306a36Sopenharmony_ci struct rcu_work rwork; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline u32 addr_fold(void *addr) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci unsigned long a = (unsigned long)addr; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic u32 flow_get_src(const struct sk_buff *skb, const struct flow_keys *flow) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci __be32 src = flow_get_u32_src(flow); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (src) 7262306a36Sopenharmony_ci return ntohl(src); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return addr_fold(skb->sk); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic u32 flow_get_dst(const struct sk_buff *skb, const struct flow_keys *flow) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci __be32 dst = flow_get_u32_dst(flow); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (dst) 8262306a36Sopenharmony_ci return ntohl(dst); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return addr_fold(skb_dst(skb)) ^ (__force u16)skb_protocol(skb, true); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic u32 flow_get_proto(const struct sk_buff *skb, 8862306a36Sopenharmony_ci const struct flow_keys *flow) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return flow->basic.ip_proto; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic u32 flow_get_proto_src(const struct sk_buff *skb, 9462306a36Sopenharmony_ci const struct flow_keys *flow) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci if (flow->ports.ports) 9762306a36Sopenharmony_ci return ntohs(flow->ports.src); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return addr_fold(skb->sk); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic u32 flow_get_proto_dst(const struct sk_buff *skb, 10362306a36Sopenharmony_ci const struct flow_keys *flow) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci if (flow->ports.ports) 10662306a36Sopenharmony_ci return ntohs(flow->ports.dst); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return addr_fold(skb_dst(skb)) ^ (__force u16)skb_protocol(skb, true); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic u32 flow_get_iif(const struct sk_buff *skb) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci return skb->skb_iif; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic u32 flow_get_priority(const struct sk_buff *skb) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return skb->priority; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic u32 flow_get_mark(const struct sk_buff *skb) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return skb->mark; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic u32 flow_get_nfct(const struct sk_buff *skb) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NF_CONNTRACK) 12962306a36Sopenharmony_ci return addr_fold(skb_nfct(skb)); 13062306a36Sopenharmony_ci#else 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NF_CONNTRACK) 13662306a36Sopenharmony_ci#define CTTUPLE(skb, member) \ 13762306a36Sopenharmony_ci({ \ 13862306a36Sopenharmony_ci enum ip_conntrack_info ctinfo; \ 13962306a36Sopenharmony_ci const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); \ 14062306a36Sopenharmony_ci if (ct == NULL) \ 14162306a36Sopenharmony_ci goto fallback; \ 14262306a36Sopenharmony_ci ct->tuplehash[CTINFO2DIR(ctinfo)].tuple.member; \ 14362306a36Sopenharmony_ci}) 14462306a36Sopenharmony_ci#else 14562306a36Sopenharmony_ci#define CTTUPLE(skb, member) \ 14662306a36Sopenharmony_ci({ \ 14762306a36Sopenharmony_ci goto fallback; \ 14862306a36Sopenharmony_ci 0; \ 14962306a36Sopenharmony_ci}) 15062306a36Sopenharmony_ci#endif 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic u32 flow_get_nfct_src(const struct sk_buff *skb, 15362306a36Sopenharmony_ci const struct flow_keys *flow) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci switch (skb_protocol(skb, true)) { 15662306a36Sopenharmony_ci case htons(ETH_P_IP): 15762306a36Sopenharmony_ci return ntohl(CTTUPLE(skb, src.u3.ip)); 15862306a36Sopenharmony_ci case htons(ETH_P_IPV6): 15962306a36Sopenharmony_ci return ntohl(CTTUPLE(skb, src.u3.ip6[3])); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_cifallback: 16262306a36Sopenharmony_ci return flow_get_src(skb, flow); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic u32 flow_get_nfct_dst(const struct sk_buff *skb, 16662306a36Sopenharmony_ci const struct flow_keys *flow) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci switch (skb_protocol(skb, true)) { 16962306a36Sopenharmony_ci case htons(ETH_P_IP): 17062306a36Sopenharmony_ci return ntohl(CTTUPLE(skb, dst.u3.ip)); 17162306a36Sopenharmony_ci case htons(ETH_P_IPV6): 17262306a36Sopenharmony_ci return ntohl(CTTUPLE(skb, dst.u3.ip6[3])); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_cifallback: 17562306a36Sopenharmony_ci return flow_get_dst(skb, flow); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic u32 flow_get_nfct_proto_src(const struct sk_buff *skb, 17962306a36Sopenharmony_ci const struct flow_keys *flow) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci return ntohs(CTTUPLE(skb, src.u.all)); 18262306a36Sopenharmony_cifallback: 18362306a36Sopenharmony_ci return flow_get_proto_src(skb, flow); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic u32 flow_get_nfct_proto_dst(const struct sk_buff *skb, 18762306a36Sopenharmony_ci const struct flow_keys *flow) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return ntohs(CTTUPLE(skb, dst.u.all)); 19062306a36Sopenharmony_cifallback: 19162306a36Sopenharmony_ci return flow_get_proto_dst(skb, flow); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic u32 flow_get_rtclassid(const struct sk_buff *skb) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci#ifdef CONFIG_IP_ROUTE_CLASSID 19762306a36Sopenharmony_ci if (skb_dst(skb)) 19862306a36Sopenharmony_ci return skb_dst(skb)->tclassid; 19962306a36Sopenharmony_ci#endif 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic u32 flow_get_skuid(const struct sk_buff *skb) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct sock *sk = skb_to_full_sk(skb); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (sk && sk->sk_socket && sk->sk_socket->file) { 20862306a36Sopenharmony_ci kuid_t skuid = sk->sk_socket->file->f_cred->fsuid; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return from_kuid(&init_user_ns, skuid); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic u32 flow_get_skgid(const struct sk_buff *skb) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct sock *sk = skb_to_full_sk(skb); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (sk && sk->sk_socket && sk->sk_socket->file) { 22062306a36Sopenharmony_ci kgid_t skgid = sk->sk_socket->file->f_cred->fsgid; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return from_kgid(&init_user_ns, skgid); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic u32 flow_get_vlan_tag(const struct sk_buff *skb) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci u16 tag; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (vlan_get_tag(skb, &tag) < 0) 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci return tag & VLAN_VID_MASK; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic u32 flow_get_rxhash(struct sk_buff *skb) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci return skb_get_hash(skb); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic u32 flow_key_get(struct sk_buff *skb, int key, struct flow_keys *flow) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci switch (key) { 24462306a36Sopenharmony_ci case FLOW_KEY_SRC: 24562306a36Sopenharmony_ci return flow_get_src(skb, flow); 24662306a36Sopenharmony_ci case FLOW_KEY_DST: 24762306a36Sopenharmony_ci return flow_get_dst(skb, flow); 24862306a36Sopenharmony_ci case FLOW_KEY_PROTO: 24962306a36Sopenharmony_ci return flow_get_proto(skb, flow); 25062306a36Sopenharmony_ci case FLOW_KEY_PROTO_SRC: 25162306a36Sopenharmony_ci return flow_get_proto_src(skb, flow); 25262306a36Sopenharmony_ci case FLOW_KEY_PROTO_DST: 25362306a36Sopenharmony_ci return flow_get_proto_dst(skb, flow); 25462306a36Sopenharmony_ci case FLOW_KEY_IIF: 25562306a36Sopenharmony_ci return flow_get_iif(skb); 25662306a36Sopenharmony_ci case FLOW_KEY_PRIORITY: 25762306a36Sopenharmony_ci return flow_get_priority(skb); 25862306a36Sopenharmony_ci case FLOW_KEY_MARK: 25962306a36Sopenharmony_ci return flow_get_mark(skb); 26062306a36Sopenharmony_ci case FLOW_KEY_NFCT: 26162306a36Sopenharmony_ci return flow_get_nfct(skb); 26262306a36Sopenharmony_ci case FLOW_KEY_NFCT_SRC: 26362306a36Sopenharmony_ci return flow_get_nfct_src(skb, flow); 26462306a36Sopenharmony_ci case FLOW_KEY_NFCT_DST: 26562306a36Sopenharmony_ci return flow_get_nfct_dst(skb, flow); 26662306a36Sopenharmony_ci case FLOW_KEY_NFCT_PROTO_SRC: 26762306a36Sopenharmony_ci return flow_get_nfct_proto_src(skb, flow); 26862306a36Sopenharmony_ci case FLOW_KEY_NFCT_PROTO_DST: 26962306a36Sopenharmony_ci return flow_get_nfct_proto_dst(skb, flow); 27062306a36Sopenharmony_ci case FLOW_KEY_RTCLASSID: 27162306a36Sopenharmony_ci return flow_get_rtclassid(skb); 27262306a36Sopenharmony_ci case FLOW_KEY_SKUID: 27362306a36Sopenharmony_ci return flow_get_skuid(skb); 27462306a36Sopenharmony_ci case FLOW_KEY_SKGID: 27562306a36Sopenharmony_ci return flow_get_skgid(skb); 27662306a36Sopenharmony_ci case FLOW_KEY_VLAN_TAG: 27762306a36Sopenharmony_ci return flow_get_vlan_tag(skb); 27862306a36Sopenharmony_ci case FLOW_KEY_RXHASH: 27962306a36Sopenharmony_ci return flow_get_rxhash(skb); 28062306a36Sopenharmony_ci default: 28162306a36Sopenharmony_ci WARN_ON(1); 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci#define FLOW_KEYS_NEEDED ((1 << FLOW_KEY_SRC) | \ 28762306a36Sopenharmony_ci (1 << FLOW_KEY_DST) | \ 28862306a36Sopenharmony_ci (1 << FLOW_KEY_PROTO) | \ 28962306a36Sopenharmony_ci (1 << FLOW_KEY_PROTO_SRC) | \ 29062306a36Sopenharmony_ci (1 << FLOW_KEY_PROTO_DST) | \ 29162306a36Sopenharmony_ci (1 << FLOW_KEY_NFCT_SRC) | \ 29262306a36Sopenharmony_ci (1 << FLOW_KEY_NFCT_DST) | \ 29362306a36Sopenharmony_ci (1 << FLOW_KEY_NFCT_PROTO_SRC) | \ 29462306a36Sopenharmony_ci (1 << FLOW_KEY_NFCT_PROTO_DST)) 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciTC_INDIRECT_SCOPE int flow_classify(struct sk_buff *skb, 29762306a36Sopenharmony_ci const struct tcf_proto *tp, 29862306a36Sopenharmony_ci struct tcf_result *res) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct flow_head *head = rcu_dereference_bh(tp->root); 30162306a36Sopenharmony_ci struct flow_filter *f; 30262306a36Sopenharmony_ci u32 keymask; 30362306a36Sopenharmony_ci u32 classid; 30462306a36Sopenharmony_ci unsigned int n, key; 30562306a36Sopenharmony_ci int r; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci list_for_each_entry_rcu(f, &head->filters, list) { 30862306a36Sopenharmony_ci u32 keys[FLOW_KEY_MAX + 1]; 30962306a36Sopenharmony_ci struct flow_keys flow_keys; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!tcf_em_tree_match(skb, &f->ematches, NULL)) 31262306a36Sopenharmony_ci continue; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci keymask = f->keymask; 31562306a36Sopenharmony_ci if (keymask & FLOW_KEYS_NEEDED) 31662306a36Sopenharmony_ci skb_flow_dissect_flow_keys(skb, &flow_keys, 0); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci for (n = 0; n < f->nkeys; n++) { 31962306a36Sopenharmony_ci key = ffs(keymask) - 1; 32062306a36Sopenharmony_ci keymask &= ~(1 << key); 32162306a36Sopenharmony_ci keys[n] = flow_key_get(skb, key, &flow_keys); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (f->mode == FLOW_MODE_HASH) 32562306a36Sopenharmony_ci classid = jhash2(keys, f->nkeys, f->hashrnd); 32662306a36Sopenharmony_ci else { 32762306a36Sopenharmony_ci classid = keys[0]; 32862306a36Sopenharmony_ci classid = (classid & f->mask) ^ f->xor; 32962306a36Sopenharmony_ci classid = (classid >> f->rshift) + f->addend; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (f->divisor) 33362306a36Sopenharmony_ci classid %= f->divisor; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci res->class = 0; 33662306a36Sopenharmony_ci res->classid = TC_H_MAKE(f->baseclass, f->baseclass + classid); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci r = tcf_exts_exec(skb, &f->exts, res); 33962306a36Sopenharmony_ci if (r < 0) 34062306a36Sopenharmony_ci continue; 34162306a36Sopenharmony_ci return r; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci return -1; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic void flow_perturbation(struct timer_list *t) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct flow_filter *f = from_timer(f, t, perturb_timer); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci get_random_bytes(&f->hashrnd, 4); 35162306a36Sopenharmony_ci if (f->perturb_period) 35262306a36Sopenharmony_ci mod_timer(&f->perturb_timer, jiffies + f->perturb_period); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { 35662306a36Sopenharmony_ci [TCA_FLOW_KEYS] = { .type = NLA_U32 }, 35762306a36Sopenharmony_ci [TCA_FLOW_MODE] = { .type = NLA_U32 }, 35862306a36Sopenharmony_ci [TCA_FLOW_BASECLASS] = { .type = NLA_U32 }, 35962306a36Sopenharmony_ci [TCA_FLOW_RSHIFT] = { .type = NLA_U32 }, 36062306a36Sopenharmony_ci [TCA_FLOW_ADDEND] = { .type = NLA_U32 }, 36162306a36Sopenharmony_ci [TCA_FLOW_MASK] = { .type = NLA_U32 }, 36262306a36Sopenharmony_ci [TCA_FLOW_XOR] = { .type = NLA_U32 }, 36362306a36Sopenharmony_ci [TCA_FLOW_DIVISOR] = { .type = NLA_U32 }, 36462306a36Sopenharmony_ci [TCA_FLOW_ACT] = { .type = NLA_NESTED }, 36562306a36Sopenharmony_ci [TCA_FLOW_POLICE] = { .type = NLA_NESTED }, 36662306a36Sopenharmony_ci [TCA_FLOW_EMATCHES] = { .type = NLA_NESTED }, 36762306a36Sopenharmony_ci [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void __flow_destroy_filter(struct flow_filter *f) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci timer_shutdown_sync(&f->perturb_timer); 37362306a36Sopenharmony_ci tcf_exts_destroy(&f->exts); 37462306a36Sopenharmony_ci tcf_em_tree_destroy(&f->ematches); 37562306a36Sopenharmony_ci tcf_exts_put_net(&f->exts); 37662306a36Sopenharmony_ci kfree(f); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void flow_destroy_filter_work(struct work_struct *work) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct flow_filter *f = container_of(to_rcu_work(work), 38262306a36Sopenharmony_ci struct flow_filter, 38362306a36Sopenharmony_ci rwork); 38462306a36Sopenharmony_ci rtnl_lock(); 38562306a36Sopenharmony_ci __flow_destroy_filter(f); 38662306a36Sopenharmony_ci rtnl_unlock(); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int flow_change(struct net *net, struct sk_buff *in_skb, 39062306a36Sopenharmony_ci struct tcf_proto *tp, unsigned long base, 39162306a36Sopenharmony_ci u32 handle, struct nlattr **tca, 39262306a36Sopenharmony_ci void **arg, u32 flags, 39362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct flow_head *head = rtnl_dereference(tp->root); 39662306a36Sopenharmony_ci struct flow_filter *fold, *fnew; 39762306a36Sopenharmony_ci struct nlattr *opt = tca[TCA_OPTIONS]; 39862306a36Sopenharmony_ci struct nlattr *tb[TCA_FLOW_MAX + 1]; 39962306a36Sopenharmony_ci unsigned int nkeys = 0; 40062306a36Sopenharmony_ci unsigned int perturb_period = 0; 40162306a36Sopenharmony_ci u32 baseclass = 0; 40262306a36Sopenharmony_ci u32 keymask = 0; 40362306a36Sopenharmony_ci u32 mode; 40462306a36Sopenharmony_ci int err; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (opt == NULL) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, TCA_FLOW_MAX, opt, flow_policy, 41062306a36Sopenharmony_ci NULL); 41162306a36Sopenharmony_ci if (err < 0) 41262306a36Sopenharmony_ci return err; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (tb[TCA_FLOW_BASECLASS]) { 41562306a36Sopenharmony_ci baseclass = nla_get_u32(tb[TCA_FLOW_BASECLASS]); 41662306a36Sopenharmony_ci if (TC_H_MIN(baseclass) == 0) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (tb[TCA_FLOW_KEYS]) { 42162306a36Sopenharmony_ci keymask = nla_get_u32(tb[TCA_FLOW_KEYS]); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci nkeys = hweight32(keymask); 42462306a36Sopenharmony_ci if (nkeys == 0) 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (fls(keymask) - 1 > FLOW_KEY_MAX) 42862306a36Sopenharmony_ci return -EOPNOTSUPP; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if ((keymask & (FLOW_KEY_SKUID|FLOW_KEY_SKGID)) && 43162306a36Sopenharmony_ci sk_user_ns(NETLINK_CB(in_skb).sk) != &init_user_ns) 43262306a36Sopenharmony_ci return -EOPNOTSUPP; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); 43662306a36Sopenharmony_ci if (!fnew) 43762306a36Sopenharmony_ci return -ENOBUFS; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &fnew->ematches); 44062306a36Sopenharmony_ci if (err < 0) 44162306a36Sopenharmony_ci goto err1; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci err = tcf_exts_init(&fnew->exts, net, TCA_FLOW_ACT, TCA_FLOW_POLICE); 44462306a36Sopenharmony_ci if (err < 0) 44562306a36Sopenharmony_ci goto err2; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &fnew->exts, flags, 44862306a36Sopenharmony_ci extack); 44962306a36Sopenharmony_ci if (err < 0) 45062306a36Sopenharmony_ci goto err2; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci fold = *arg; 45362306a36Sopenharmony_ci if (fold) { 45462306a36Sopenharmony_ci err = -EINVAL; 45562306a36Sopenharmony_ci if (fold->handle != handle && handle) 45662306a36Sopenharmony_ci goto err2; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Copy fold into fnew */ 45962306a36Sopenharmony_ci fnew->tp = fold->tp; 46062306a36Sopenharmony_ci fnew->handle = fold->handle; 46162306a36Sopenharmony_ci fnew->nkeys = fold->nkeys; 46262306a36Sopenharmony_ci fnew->keymask = fold->keymask; 46362306a36Sopenharmony_ci fnew->mode = fold->mode; 46462306a36Sopenharmony_ci fnew->mask = fold->mask; 46562306a36Sopenharmony_ci fnew->xor = fold->xor; 46662306a36Sopenharmony_ci fnew->rshift = fold->rshift; 46762306a36Sopenharmony_ci fnew->addend = fold->addend; 46862306a36Sopenharmony_ci fnew->divisor = fold->divisor; 46962306a36Sopenharmony_ci fnew->baseclass = fold->baseclass; 47062306a36Sopenharmony_ci fnew->hashrnd = fold->hashrnd; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci mode = fold->mode; 47362306a36Sopenharmony_ci if (tb[TCA_FLOW_MODE]) 47462306a36Sopenharmony_ci mode = nla_get_u32(tb[TCA_FLOW_MODE]); 47562306a36Sopenharmony_ci if (mode != FLOW_MODE_HASH && nkeys > 1) 47662306a36Sopenharmony_ci goto err2; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (mode == FLOW_MODE_HASH) 47962306a36Sopenharmony_ci perturb_period = fold->perturb_period; 48062306a36Sopenharmony_ci if (tb[TCA_FLOW_PERTURB]) { 48162306a36Sopenharmony_ci if (mode != FLOW_MODE_HASH) 48262306a36Sopenharmony_ci goto err2; 48362306a36Sopenharmony_ci perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci } else { 48662306a36Sopenharmony_ci err = -EINVAL; 48762306a36Sopenharmony_ci if (!handle) 48862306a36Sopenharmony_ci goto err2; 48962306a36Sopenharmony_ci if (!tb[TCA_FLOW_KEYS]) 49062306a36Sopenharmony_ci goto err2; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci mode = FLOW_MODE_MAP; 49362306a36Sopenharmony_ci if (tb[TCA_FLOW_MODE]) 49462306a36Sopenharmony_ci mode = nla_get_u32(tb[TCA_FLOW_MODE]); 49562306a36Sopenharmony_ci if (mode != FLOW_MODE_HASH && nkeys > 1) 49662306a36Sopenharmony_ci goto err2; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (tb[TCA_FLOW_PERTURB]) { 49962306a36Sopenharmony_ci if (mode != FLOW_MODE_HASH) 50062306a36Sopenharmony_ci goto err2; 50162306a36Sopenharmony_ci perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (TC_H_MAJ(baseclass) == 0) { 50562306a36Sopenharmony_ci struct Qdisc *q = tcf_block_q(tp->chain->block); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci baseclass = TC_H_MAKE(q->handle, baseclass); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci if (TC_H_MIN(baseclass) == 0) 51062306a36Sopenharmony_ci baseclass = TC_H_MAKE(baseclass, 1); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci fnew->handle = handle; 51362306a36Sopenharmony_ci fnew->mask = ~0U; 51462306a36Sopenharmony_ci fnew->tp = tp; 51562306a36Sopenharmony_ci get_random_bytes(&fnew->hashrnd, 4); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci timer_setup(&fnew->perturb_timer, flow_perturbation, TIMER_DEFERRABLE); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci tcf_block_netif_keep_dst(tp->chain->block); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (tb[TCA_FLOW_KEYS]) { 52362306a36Sopenharmony_ci fnew->keymask = keymask; 52462306a36Sopenharmony_ci fnew->nkeys = nkeys; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci fnew->mode = mode; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (tb[TCA_FLOW_MASK]) 53062306a36Sopenharmony_ci fnew->mask = nla_get_u32(tb[TCA_FLOW_MASK]); 53162306a36Sopenharmony_ci if (tb[TCA_FLOW_XOR]) 53262306a36Sopenharmony_ci fnew->xor = nla_get_u32(tb[TCA_FLOW_XOR]); 53362306a36Sopenharmony_ci if (tb[TCA_FLOW_RSHIFT]) 53462306a36Sopenharmony_ci fnew->rshift = nla_get_u32(tb[TCA_FLOW_RSHIFT]); 53562306a36Sopenharmony_ci if (tb[TCA_FLOW_ADDEND]) 53662306a36Sopenharmony_ci fnew->addend = nla_get_u32(tb[TCA_FLOW_ADDEND]); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (tb[TCA_FLOW_DIVISOR]) 53962306a36Sopenharmony_ci fnew->divisor = nla_get_u32(tb[TCA_FLOW_DIVISOR]); 54062306a36Sopenharmony_ci if (baseclass) 54162306a36Sopenharmony_ci fnew->baseclass = baseclass; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci fnew->perturb_period = perturb_period; 54462306a36Sopenharmony_ci if (perturb_period) 54562306a36Sopenharmony_ci mod_timer(&fnew->perturb_timer, jiffies + perturb_period); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!*arg) 54862306a36Sopenharmony_ci list_add_tail_rcu(&fnew->list, &head->filters); 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci list_replace_rcu(&fold->list, &fnew->list); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci *arg = fnew; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (fold) { 55562306a36Sopenharmony_ci tcf_exts_get_net(&fold->exts); 55662306a36Sopenharmony_ci tcf_queue_work(&fold->rwork, flow_destroy_filter_work); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cierr2: 56162306a36Sopenharmony_ci tcf_exts_destroy(&fnew->exts); 56262306a36Sopenharmony_ci tcf_em_tree_destroy(&fnew->ematches); 56362306a36Sopenharmony_cierr1: 56462306a36Sopenharmony_ci kfree(fnew); 56562306a36Sopenharmony_ci return err; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int flow_delete(struct tcf_proto *tp, void *arg, bool *last, 56962306a36Sopenharmony_ci bool rtnl_held, struct netlink_ext_ack *extack) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct flow_head *head = rtnl_dereference(tp->root); 57262306a36Sopenharmony_ci struct flow_filter *f = arg; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci list_del_rcu(&f->list); 57562306a36Sopenharmony_ci tcf_exts_get_net(&f->exts); 57662306a36Sopenharmony_ci tcf_queue_work(&f->rwork, flow_destroy_filter_work); 57762306a36Sopenharmony_ci *last = list_empty(&head->filters); 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_cistatic int flow_init(struct tcf_proto *tp) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct flow_head *head; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci head = kzalloc(sizeof(*head), GFP_KERNEL); 58662306a36Sopenharmony_ci if (head == NULL) 58762306a36Sopenharmony_ci return -ENOBUFS; 58862306a36Sopenharmony_ci INIT_LIST_HEAD(&head->filters); 58962306a36Sopenharmony_ci rcu_assign_pointer(tp->root, head); 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void flow_destroy(struct tcf_proto *tp, bool rtnl_held, 59462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct flow_head *head = rtnl_dereference(tp->root); 59762306a36Sopenharmony_ci struct flow_filter *f, *next; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci list_for_each_entry_safe(f, next, &head->filters, list) { 60062306a36Sopenharmony_ci list_del_rcu(&f->list); 60162306a36Sopenharmony_ci if (tcf_exts_get_net(&f->exts)) 60262306a36Sopenharmony_ci tcf_queue_work(&f->rwork, flow_destroy_filter_work); 60362306a36Sopenharmony_ci else 60462306a36Sopenharmony_ci __flow_destroy_filter(f); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci kfree_rcu(head, rcu); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void *flow_get(struct tcf_proto *tp, u32 handle) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct flow_head *head = rtnl_dereference(tp->root); 61262306a36Sopenharmony_ci struct flow_filter *f; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci list_for_each_entry(f, &head->filters, list) 61562306a36Sopenharmony_ci if (f->handle == handle) 61662306a36Sopenharmony_ci return f; 61762306a36Sopenharmony_ci return NULL; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int flow_dump(struct net *net, struct tcf_proto *tp, void *fh, 62162306a36Sopenharmony_ci struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct flow_filter *f = fh; 62462306a36Sopenharmony_ci struct nlattr *nest; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (f == NULL) 62762306a36Sopenharmony_ci return skb->len; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci t->tcm_handle = f->handle; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, TCA_OPTIONS); 63262306a36Sopenharmony_ci if (nest == NULL) 63362306a36Sopenharmony_ci goto nla_put_failure; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_FLOW_KEYS, f->keymask) || 63662306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_MODE, f->mode)) 63762306a36Sopenharmony_ci goto nla_put_failure; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (f->mask != ~0 || f->xor != 0) { 64062306a36Sopenharmony_ci if (nla_put_u32(skb, TCA_FLOW_MASK, f->mask) || 64162306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_XOR, f->xor)) 64262306a36Sopenharmony_ci goto nla_put_failure; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci if (f->rshift && 64562306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_RSHIFT, f->rshift)) 64662306a36Sopenharmony_ci goto nla_put_failure; 64762306a36Sopenharmony_ci if (f->addend && 64862306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_ADDEND, f->addend)) 64962306a36Sopenharmony_ci goto nla_put_failure; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (f->divisor && 65262306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_DIVISOR, f->divisor)) 65362306a36Sopenharmony_ci goto nla_put_failure; 65462306a36Sopenharmony_ci if (f->baseclass && 65562306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_BASECLASS, f->baseclass)) 65662306a36Sopenharmony_ci goto nla_put_failure; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (f->perturb_period && 65962306a36Sopenharmony_ci nla_put_u32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ)) 66062306a36Sopenharmony_ci goto nla_put_failure; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (tcf_exts_dump(skb, &f->exts) < 0) 66362306a36Sopenharmony_ci goto nla_put_failure; 66462306a36Sopenharmony_ci#ifdef CONFIG_NET_EMATCH 66562306a36Sopenharmony_ci if (f->ematches.hdr.nmatches && 66662306a36Sopenharmony_ci tcf_em_tree_dump(skb, &f->ematches, TCA_FLOW_EMATCHES) < 0) 66762306a36Sopenharmony_ci goto nla_put_failure; 66862306a36Sopenharmony_ci#endif 66962306a36Sopenharmony_ci nla_nest_end(skb, nest); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (tcf_exts_dump_stats(skb, &f->exts) < 0) 67262306a36Sopenharmony_ci goto nla_put_failure; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return skb->len; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cinla_put_failure: 67762306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 67862306a36Sopenharmony_ci return -1; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg, 68262306a36Sopenharmony_ci bool rtnl_held) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct flow_head *head = rtnl_dereference(tp->root); 68562306a36Sopenharmony_ci struct flow_filter *f; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci list_for_each_entry(f, &head->filters, list) { 68862306a36Sopenharmony_ci if (!tc_cls_stats_dump(tp, arg, f)) 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic struct tcf_proto_ops cls_flow_ops __read_mostly = { 69462306a36Sopenharmony_ci .kind = "flow", 69562306a36Sopenharmony_ci .classify = flow_classify, 69662306a36Sopenharmony_ci .init = flow_init, 69762306a36Sopenharmony_ci .destroy = flow_destroy, 69862306a36Sopenharmony_ci .change = flow_change, 69962306a36Sopenharmony_ci .delete = flow_delete, 70062306a36Sopenharmony_ci .get = flow_get, 70162306a36Sopenharmony_ci .dump = flow_dump, 70262306a36Sopenharmony_ci .walk = flow_walk, 70362306a36Sopenharmony_ci .owner = THIS_MODULE, 70462306a36Sopenharmony_ci}; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int __init cls_flow_init(void) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci return register_tcf_proto_ops(&cls_flow_ops); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void __exit cls_flow_exit(void) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci unregister_tcf_proto_ops(&cls_flow_ops); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cimodule_init(cls_flow_init); 71762306a36Sopenharmony_cimodule_exit(cls_flow_exit); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 72062306a36Sopenharmony_ciMODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 72162306a36Sopenharmony_ciMODULE_DESCRIPTION("TC flow classifier"); 722