162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * net/sched/em_text.c Textsearch ematch 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Thomas Graf <tgraf@suug.ch> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/skbuff.h> 1462306a36Sopenharmony_ci#include <linux/textsearch.h> 1562306a36Sopenharmony_ci#include <linux/tc_ematch/tc_em_text.h> 1662306a36Sopenharmony_ci#include <net/pkt_cls.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct text_match { 1962306a36Sopenharmony_ci u16 from_offset; 2062306a36Sopenharmony_ci u16 to_offset; 2162306a36Sopenharmony_ci u8 from_layer; 2262306a36Sopenharmony_ci u8 to_layer; 2362306a36Sopenharmony_ci struct ts_config *config; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define EM_TEXT_PRIV(m) ((struct text_match *) (m)->data) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int em_text_match(struct sk_buff *skb, struct tcf_ematch *m, 2962306a36Sopenharmony_ci struct tcf_pkt_info *info) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct text_match *tm = EM_TEXT_PRIV(m); 3262306a36Sopenharmony_ci int from, to; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci from = tcf_get_base_ptr(skb, tm->from_layer) - skb->data; 3562306a36Sopenharmony_ci from += tm->from_offset; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci to = tcf_get_base_ptr(skb, tm->to_layer) - skb->data; 3862306a36Sopenharmony_ci to += tm->to_offset; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return skb_find_text(skb, from, to, tm->config) != UINT_MAX; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int em_text_change(struct net *net, void *data, int len, 4462306a36Sopenharmony_ci struct tcf_ematch *m) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct text_match *tm; 4762306a36Sopenharmony_ci struct tcf_em_text *conf = data; 4862306a36Sopenharmony_ci struct ts_config *ts_conf; 4962306a36Sopenharmony_ci int flags = 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (len < sizeof(*conf) || len < (sizeof(*conf) + conf->pattern_len)) 5262306a36Sopenharmony_ci return -EINVAL; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (conf->from_layer > conf->to_layer) 5562306a36Sopenharmony_ci return -EINVAL; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (conf->from_layer == conf->to_layer && 5862306a36Sopenharmony_ci conf->from_offset > conf->to_offset) 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciretry: 6262306a36Sopenharmony_ci ts_conf = textsearch_prepare(conf->algo, (u8 *) conf + sizeof(*conf), 6362306a36Sopenharmony_ci conf->pattern_len, GFP_KERNEL, flags); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (flags & TS_AUTOLOAD) 6662306a36Sopenharmony_ci rtnl_lock(); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (IS_ERR(ts_conf)) { 6962306a36Sopenharmony_ci if (PTR_ERR(ts_conf) == -ENOENT && !(flags & TS_AUTOLOAD)) { 7062306a36Sopenharmony_ci rtnl_unlock(); 7162306a36Sopenharmony_ci flags |= TS_AUTOLOAD; 7262306a36Sopenharmony_ci goto retry; 7362306a36Sopenharmony_ci } else 7462306a36Sopenharmony_ci return PTR_ERR(ts_conf); 7562306a36Sopenharmony_ci } else if (flags & TS_AUTOLOAD) { 7662306a36Sopenharmony_ci textsearch_destroy(ts_conf); 7762306a36Sopenharmony_ci return -EAGAIN; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci tm = kmalloc(sizeof(*tm), GFP_KERNEL); 8162306a36Sopenharmony_ci if (tm == NULL) { 8262306a36Sopenharmony_ci textsearch_destroy(ts_conf); 8362306a36Sopenharmony_ci return -ENOBUFS; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci tm->from_offset = conf->from_offset; 8762306a36Sopenharmony_ci tm->to_offset = conf->to_offset; 8862306a36Sopenharmony_ci tm->from_layer = conf->from_layer; 8962306a36Sopenharmony_ci tm->to_layer = conf->to_layer; 9062306a36Sopenharmony_ci tm->config = ts_conf; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci m->datalen = sizeof(*tm); 9362306a36Sopenharmony_ci m->data = (unsigned long) tm; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void em_text_destroy(struct tcf_ematch *m) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (EM_TEXT_PRIV(m) && EM_TEXT_PRIV(m)->config) { 10162306a36Sopenharmony_ci textsearch_destroy(EM_TEXT_PRIV(m)->config); 10262306a36Sopenharmony_ci kfree(EM_TEXT_PRIV(m)); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int em_text_dump(struct sk_buff *skb, struct tcf_ematch *m) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct text_match *tm = EM_TEXT_PRIV(m); 10962306a36Sopenharmony_ci struct tcf_em_text conf; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci strncpy(conf.algo, tm->config->ops->name, sizeof(conf.algo) - 1); 11262306a36Sopenharmony_ci conf.from_offset = tm->from_offset; 11362306a36Sopenharmony_ci conf.to_offset = tm->to_offset; 11462306a36Sopenharmony_ci conf.from_layer = tm->from_layer; 11562306a36Sopenharmony_ci conf.to_layer = tm->to_layer; 11662306a36Sopenharmony_ci conf.pattern_len = textsearch_get_pattern_len(tm->config); 11762306a36Sopenharmony_ci conf.pad = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (nla_put_nohdr(skb, sizeof(conf), &conf) < 0) 12062306a36Sopenharmony_ci goto nla_put_failure; 12162306a36Sopenharmony_ci if (nla_append(skb, conf.pattern_len, 12262306a36Sopenharmony_ci textsearch_get_pattern(tm->config)) < 0) 12362306a36Sopenharmony_ci goto nla_put_failure; 12462306a36Sopenharmony_ci return 0; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cinla_put_failure: 12762306a36Sopenharmony_ci return -1; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic struct tcf_ematch_ops em_text_ops = { 13162306a36Sopenharmony_ci .kind = TCF_EM_TEXT, 13262306a36Sopenharmony_ci .change = em_text_change, 13362306a36Sopenharmony_ci .match = em_text_match, 13462306a36Sopenharmony_ci .destroy = em_text_destroy, 13562306a36Sopenharmony_ci .dump = em_text_dump, 13662306a36Sopenharmony_ci .owner = THIS_MODULE, 13762306a36Sopenharmony_ci .link = LIST_HEAD_INIT(em_text_ops.link) 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int __init init_em_text(void) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return tcf_em_register(&em_text_ops); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void __exit exit_em_text(void) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci tcf_em_unregister(&em_text_ops); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cimodule_init(init_em_text); 15362306a36Sopenharmony_cimodule_exit(exit_em_text); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciMODULE_ALIAS_TCF_EMATCH(TCF_EM_TEXT); 156