On 6/19/20 12:15 AM, Ariel Levkovich wrote:
Allow setting a hash value to a packet for a future match. The action will determine the packet's hash result according to the selected hash type. The first option is to select a basic asymmetric l4 hash calculation on the packet headers which will either use the skb->hash value as if such was already calculated and set by the device driver, or it will perform the kernel jenkins hash function on the packet which will generate the result otherwise. The other option is for user to provide an BPF program which is dedicated to calculate the hash. In such case the program is loaded and used by tc to perform the hash calculation and provide it to the hash action to be stored in skb->hash field.
The commit description is a bit misleading, since setting a custom skb->hash from BPF program can be done already via {cls,act}_bpf from *within* BPF in tc for ~3 years by now via [0]. [0] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ded092cd73c2c56a394b936f86897f29b2e131c0
The BPF option can be useful for future HW offload support of the hash calculation by emulating the HW hash function when it's different than the kernel's but yet we want to maintain consistency between the SW and the HW.
... use case should say 'dummy SW module for HW offload'. ;-/
Usage is as follows: $ tc filter add dev ens1f0_0 ingress \ prio 1 chain 0 proto ip \ flower ip_proto tcp \ action hash bpf object-file <bpf file> \ action goto chain 2
[...]
Signed-off-by: Ariel Levkovich <lariel@xxxxxxxxxxxx> Reviewed-by: Jiri Pirko <jiri@xxxxxxxxxxxx>
[...]
+static int tcf_hash_act(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res) +{ + struct tcf_hash *h = to_hash(a); + struct tcf_hash_params *p; + int action; + u32 hash; + + tcf_lastuse_update(&h->tcf_tm); + tcf_action_update_bstats(&h->common, skb); + + p = rcu_dereference_bh(h->hash_p); + + action = READ_ONCE(h->tcf_action); + + switch (p->alg) { + case TCA_HASH_ALG_L4: + hash = skb_get_hash(skb); + /* If a hash basis was provided, add it into + * hash calculation here and re-set skb->hash + * to the new result with sw_hash indication + * and keeping the l4 hash indication. + */ + hash = jhash_1word(hash, p->basis); + __skb_set_sw_hash(skb, hash, skb->l4_hash); + break; + case TCA_HASH_ALG_BPF: + __skb_push(skb, skb->mac_len); + bpf_compute_data_pointers(skb); + hash = BPF_PROG_RUN(p->prog, skb); + __skb_pull(skb, skb->mac_len); + /* The BPF program hash function type is + * unknown so only the sw hash bit is set. + */ + __skb_set_sw_hash(skb, hash, false);
Given this runs BPF_PROG_TYPE_SCHED_ACT-typed programs, act_hash.c now becomes pretty much another clone of {cls,act}_bpf.c with the full feature set in terms of what it can do with the program, like header mangling, encap/decap, setting tunnel keys, cloning + redirecting skbs, etc. This is rather misleading given you basically would only want the direct pkt access bits and plain insns for calc'ing the hash, but none of the other features.
+ break; + } + + return action; +}
[...]
+static int tcf_hash_bpf_init(struct nlattr **tb, struct tcf_hash_params *params) +{ + struct bpf_prog *fp; + char *name = NULL; + u32 bpf_fd; + + bpf_fd = nla_get_u32(tb[TCA_HASH_BPF_FD]); + + fp = bpf_prog_get_type(bpf_fd, BPF_PROG_TYPE_SCHED_ACT); + if (IS_ERR(fp)) + return PTR_ERR(fp); + + if (tb[TCA_HASH_BPF_NAME]) { + name = nla_memdup(tb[TCA_HASH_BPF_NAME], GFP_KERNEL); + if (!name) { + bpf_prog_put(fp); + return -ENOMEM; + } + } + + params->bpf_name = name; + params->prog = fp; + + return 0; +}