// SPDX-License-Identifier: GPL-2.0-or-later /* * net/sched/ife.c Inter-FE action based on ForCES WG InterFE LFB * * Refer to: * draft-ietf-forces-interfelfb-03 * and * netdev01 paper: * "Distributing Linux Traffic Control Classifier-Action * Subsystem" * Authors: Jamal Hadi Salim and Damascene M. Joachimpillai * * copyright Jamal Hadi Salim (2015)
*/
staticint ife_validate_metatype(struct tcf_meta_ops *ops, void *val, int len)
{ int ret = 0; /* XXX: unfortunately cant use nla_policy at this point * because a length of 0 is valid in the case of * "allow". "use" semantics do enforce for proper * length and i couldve use nla_policy but it makes it hard * to use it just for that..
*/ if (ops->validate) return ops->validate(val, len);
if (ops->metatype == NLA_U32)
ret = ife_validate_meta_u32(val, len); elseif (ops->metatype == NLA_U16)
ret = ife_validate_meta_u16(val, len);
return ret;
}
#ifdef CONFIG_MODULES staticconstchar *ife_meta_id2name(u32 metaid)
{ switch (metaid) { case IFE_META_SKBMARK: return"skbmark"; case IFE_META_PRIO: return"skbprio"; case IFE_META_TCINDEX: return"tcindex"; default: return"unknown";
}
} #endif
/* called when adding new meta information
*/ staticint load_metaops_and_vet(u32 metaid, void *val, int len, bool rtnl_held)
{ struct tcf_meta_ops *ops = find_ife_oplist(metaid); int ret = 0;
if (!ops) {
ret = -ENOENT; #ifdef CONFIG_MODULES if (rtnl_held)
rtnl_unlock();
request_module("ife-meta-%s", ife_meta_id2name(metaid)); if (rtnl_held)
rtnl_lock();
ops = find_ife_oplist(metaid); #endif
}
if (ops) {
ret = 0; if (len)
ret = ife_validate_metatype(ops, val, len);
module_put(ops->owner);
}
return ret;
}
/* called when adding new meta information
*/ staticint __add_metainfo(conststruct tcf_meta_ops *ops, struct tcf_ife_info *ife, u32 metaid, void *metaval, int len, bool atomic, bool exists)
{ struct tcf_meta_info *mi = NULL; int ret = 0;
mi = kzalloc(sizeof(*mi), atomic ? GFP_ATOMIC : GFP_KERNEL); if (!mi) return -ENOMEM;
mi->metaid = metaid;
mi->ops = ops; if (len > 0) {
ret = ops->alloc(mi, metaval, atomic ? GFP_ATOMIC : GFP_KERNEL); if (ret != 0) {
kfree(mi); return ret;
}
}
if (exists)
spin_lock_bh(&ife->tcf_lock);
list_add_tail(&mi->metalist, &ife->metalist); if (exists)
spin_unlock_bh(&ife->tcf_lock);
if (!try_module_get(ops->owner)) return -ENOENT;
ret = __add_metainfo(ops, ife, metaid, NULL, 0, true, exists); if (ret)
module_put(ops->owner); return ret;
}
staticint add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval, int len, bool exists)
{ conststruct tcf_meta_ops *ops = find_ife_oplist(metaid); int ret;
if (!ops) return -ENOENT;
ret = __add_metainfo(ops, ife, metaid, metaval, len, false, exists); if (ret) /*put back what find_ife_oplist took */
module_put(ops->owner); return ret;
}
staticint use_all_metadata(struct tcf_ife_info *ife, bool exists)
{ struct tcf_meta_ops *o; int rc = 0; int installed = 0;
/* IFE_DECODE is 0 and indicates the opposite of IFE_ENCODE because * they cannot run as the same time. Check on all other values which * are not supported right now.
*/ if (parm->flags & ~IFE_ENCODE) return -EINVAL;
p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM;
if (parm->flags & IFE_ENCODE) { if (tb[TCA_IFE_TYPE])
ife_type = nla_get_u16(tb[TCA_IFE_TYPE]); if (tb[TCA_IFE_DMAC])
daddr = nla_data(tb[TCA_IFE_DMAC]); if (tb[TCA_IFE_SMAC])
saddr = nla_data(tb[TCA_IFE_SMAC]);
}
if (parm->flags & IFE_ENCODE) { if (daddr)
ether_addr_copy(p->eth_dst, daddr); else
eth_zero_addr(p->eth_dst);
if (saddr)
ether_addr_copy(p->eth_src, saddr); else
eth_zero_addr(p->eth_src);
p->eth_type = ife_type;
}
if (tb[TCA_IFE_METALST]) {
err = populate_metalist(ife, tb2, exists,
!(flags & TCA_ACT_FLAGS_NO_RTNL)); if (err) goto metadata_parse_err;
} else { /* if no passed metadata allow list or passed allow-all * then here we process by adding as many supported metadatum * as we can. You better have at least one else we are * going to bail out
*/
err = use_all_metadata(ife, exists); if (err) goto metadata_parse_err;
}
if (exists)
spin_lock_bh(&ife->tcf_lock); /* protected by tcf_lock when modifying existing action */
goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
p = rcu_replace_pointer(ife->params, p, 1);
if (exists)
spin_unlock_bh(&ife->tcf_lock); if (goto_ch)
tcf_chain_put_by_act(goto_ch); if (p)
kfree_rcu(p, rcu);
/* XXX: use hash to speed up */
list_for_each_entry(e, &ife->metalist, metalist) { if (metaid == e->metaid) { if (e->ops) { /* We check for decode presence already */ return e->ops->decode(skb, mdata, mlen);
}
}
}
if (find_decode_metaid(skb, ife, mtype, dlen, curr_data)) { /* abuse overlimits to count when we receive metadata * but dont have an ops for it
*/
pr_info_ratelimited("Unknown metaid %d dlen %d\n",
mtype, dlen);
qstats_overlimit_inc(this_cpu_ptr(ife->common.cpu_qstats));
}
}
if (WARN_ON(tlv_data != ifehdr_end)) {
qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); return TC_ACT_SHOT;
}
/*XXX: check if we can do this at install time instead of current * send data path
**/ staticint ife_get_sz(struct sk_buff *skb, struct tcf_ife_info *ife)
{ struct tcf_meta_info *e, *n; int tot_run_sz = 0, run_sz = 0;
list_for_each_entry_safe(e, n, &ife->metalist, metalist) { if (e->ops->check_presence) {
run_sz = e->ops->check_presence(skb, e);
tot_run_sz += run_sz;
}
}
if (!metalen) { /* no metadata to send */ /* abuse overlimits to count when we allow packet * with no metadata
*/
qstats_overlimit_inc(this_cpu_ptr(ife->common.cpu_qstats)); return action;
} /* could be stupid policy setup or mtu config
* so lets be conservative.. */ if ((action == TC_ACT_SHOT) || exceed_mtu) {
qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); return TC_ACT_SHOT;
}
if (skb_at_tc_ingress(skb))
skb_push(skb, skb->dev->hard_header_len);
ife_meta = ife_encode(skb, metalen);
spin_lock(&ife->tcf_lock);
/* XXX: we dont have a clever way of telling encode to * not repeat some of the computations that are done by * ops->presence_check...
*/
list_for_each_entry(e, &ife->metalist, metalist) { if (e->ops->encode) {
err = e->ops->encode(skb, (void *)(ife_meta + skboff),
e);
} if (err < 0) { /* too corrupt to keep around if overwritten */
spin_unlock(&ife->tcf_lock);
qstats_drop_inc(this_cpu_ptr(ife->common.cpu_qstats)); return TC_ACT_SHOT;
}
skboff += err;
}
spin_unlock(&ife->tcf_lock);
oethh = (struct ethhdr *)skb->data;
if (!is_zero_ether_addr(p->eth_src))
ether_addr_copy(oethh->h_source, p->eth_src); if (!is_zero_ether_addr(p->eth_dst))
ether_addr_copy(oethh->h_dest, p->eth_dst);
oethh->h_proto = htons(p->eth_type);
if (skb_at_tc_ingress(skb))
skb_pull(skb, skb->dev->hard_header_len);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.