Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * net/sched/em_ipt.c IPtables matches Ematch
0004  *
0005  * (c) 2018 Eyal Birger <eyal.birger@gmail.com>
0006  */
0007 
0008 #include <linux/gfp.h>
0009 #include <linux/module.h>
0010 #include <linux/types.h>
0011 #include <linux/kernel.h>
0012 #include <linux/string.h>
0013 #include <linux/skbuff.h>
0014 #include <linux/tc_ematch/tc_em_ipt.h>
0015 #include <linux/netfilter.h>
0016 #include <linux/netfilter/x_tables.h>
0017 #include <linux/netfilter_ipv4/ip_tables.h>
0018 #include <linux/netfilter_ipv6/ip6_tables.h>
0019 #include <net/pkt_cls.h>
0020 
0021 struct em_ipt_match {
0022     const struct xt_match *match;
0023     u32 hook;
0024     u8 nfproto;
0025     u8 match_data[] __aligned(8);
0026 };
0027 
0028 struct em_ipt_xt_match {
0029     char *match_name;
0030     int (*validate_match_data)(struct nlattr **tb, u8 mrev);
0031 };
0032 
0033 static const struct nla_policy em_ipt_policy[TCA_EM_IPT_MAX + 1] = {
0034     [TCA_EM_IPT_MATCH_NAME]     = { .type = NLA_STRING,
0035                         .len = XT_EXTENSION_MAXNAMELEN },
0036     [TCA_EM_IPT_MATCH_REVISION] = { .type = NLA_U8 },
0037     [TCA_EM_IPT_HOOK]       = { .type = NLA_U32 },
0038     [TCA_EM_IPT_NFPROTO]        = { .type = NLA_U8 },
0039     [TCA_EM_IPT_MATCH_DATA]     = { .type = NLA_UNSPEC },
0040 };
0041 
0042 static int check_match(struct net *net, struct em_ipt_match *im, int mdata_len)
0043 {
0044     struct xt_mtchk_param mtpar = {};
0045     union {
0046         struct ipt_entry e4;
0047         struct ip6t_entry e6;
0048     } e = {};
0049 
0050     mtpar.net   = net;
0051     mtpar.table = "filter";
0052     mtpar.hook_mask = 1 << im->hook;
0053     mtpar.family    = im->match->family;
0054     mtpar.match = im->match;
0055     mtpar.entryinfo = &e;
0056     mtpar.matchinfo = (void *)im->match_data;
0057     return xt_check_match(&mtpar, mdata_len, 0, 0);
0058 }
0059 
0060 static int policy_validate_match_data(struct nlattr **tb, u8 mrev)
0061 {
0062     if (mrev != 0) {
0063         pr_err("only policy match revision 0 supported");
0064         return -EINVAL;
0065     }
0066 
0067     if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) != NF_INET_PRE_ROUTING) {
0068         pr_err("policy can only be matched on NF_INET_PRE_ROUTING");
0069         return -EINVAL;
0070     }
0071 
0072     return 0;
0073 }
0074 
0075 static int addrtype_validate_match_data(struct nlattr **tb, u8 mrev)
0076 {
0077     if (mrev != 1) {
0078         pr_err("only addrtype match revision 1 supported");
0079         return -EINVAL;
0080     }
0081 
0082     return 0;
0083 }
0084 
0085 static const struct em_ipt_xt_match em_ipt_xt_matches[] = {
0086     {
0087         .match_name = "policy",
0088         .validate_match_data = policy_validate_match_data
0089     },
0090     {
0091         .match_name = "addrtype",
0092         .validate_match_data = addrtype_validate_match_data
0093     },
0094     {}
0095 };
0096 
0097 static struct xt_match *get_xt_match(struct nlattr **tb)
0098 {
0099     const struct em_ipt_xt_match *m;
0100     struct nlattr *mname_attr;
0101     u8 nfproto, mrev = 0;
0102     int ret;
0103 
0104     mname_attr = tb[TCA_EM_IPT_MATCH_NAME];
0105     for (m = em_ipt_xt_matches; m->match_name; m++) {
0106         if (!nla_strcmp(mname_attr, m->match_name))
0107             break;
0108     }
0109 
0110     if (!m->match_name) {
0111         pr_err("Unsupported xt match");
0112         return ERR_PTR(-EINVAL);
0113     }
0114 
0115     if (tb[TCA_EM_IPT_MATCH_REVISION])
0116         mrev = nla_get_u8(tb[TCA_EM_IPT_MATCH_REVISION]);
0117 
0118     ret = m->validate_match_data(tb, mrev);
0119     if (ret < 0)
0120         return ERR_PTR(ret);
0121 
0122     nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
0123     return xt_request_find_match(nfproto, m->match_name, mrev);
0124 }
0125 
0126 static int em_ipt_change(struct net *net, void *data, int data_len,
0127              struct tcf_ematch *em)
0128 {
0129     struct nlattr *tb[TCA_EM_IPT_MAX + 1];
0130     struct em_ipt_match *im = NULL;
0131     struct xt_match *match;
0132     int mdata_len, ret;
0133     u8 nfproto;
0134 
0135     ret = nla_parse_deprecated(tb, TCA_EM_IPT_MAX, data, data_len,
0136                    em_ipt_policy, NULL);
0137     if (ret < 0)
0138         return ret;
0139 
0140     if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] ||
0141         !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO])
0142         return -EINVAL;
0143 
0144     nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
0145     switch (nfproto) {
0146     case NFPROTO_IPV4:
0147     case NFPROTO_IPV6:
0148         break;
0149     default:
0150         return -EINVAL;
0151     }
0152 
0153     match = get_xt_match(tb);
0154     if (IS_ERR(match)) {
0155         pr_err("unable to load match\n");
0156         return PTR_ERR(match);
0157     }
0158 
0159     mdata_len = XT_ALIGN(nla_len(tb[TCA_EM_IPT_MATCH_DATA]));
0160     im = kzalloc(sizeof(*im) + mdata_len, GFP_KERNEL);
0161     if (!im) {
0162         ret = -ENOMEM;
0163         goto err;
0164     }
0165 
0166     im->match = match;
0167     im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]);
0168     im->nfproto = nfproto;
0169     nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len);
0170 
0171     ret = check_match(net, im, mdata_len);
0172     if (ret)
0173         goto err;
0174 
0175     em->datalen = sizeof(*im) + mdata_len;
0176     em->data = (unsigned long)im;
0177     return 0;
0178 
0179 err:
0180     kfree(im);
0181     module_put(match->me);
0182     return ret;
0183 }
0184 
0185 static void em_ipt_destroy(struct tcf_ematch *em)
0186 {
0187     struct em_ipt_match *im = (void *)em->data;
0188 
0189     if (!im)
0190         return;
0191 
0192     if (im->match->destroy) {
0193         struct xt_mtdtor_param par = {
0194             .net = em->net,
0195             .match = im->match,
0196             .matchinfo = im->match_data,
0197             .family = im->match->family
0198         };
0199         im->match->destroy(&par);
0200     }
0201     module_put(im->match->me);
0202     kfree(im);
0203 }
0204 
0205 static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em,
0206             struct tcf_pkt_info *info)
0207 {
0208     const struct em_ipt_match *im = (const void *)em->data;
0209     struct xt_action_param acpar = {};
0210     struct net_device *indev = NULL;
0211     u8 nfproto = im->match->family;
0212     struct nf_hook_state state;
0213     int ret;
0214 
0215     switch (skb_protocol(skb, true)) {
0216     case htons(ETH_P_IP):
0217         if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
0218             return 0;
0219         if (nfproto == NFPROTO_UNSPEC)
0220             nfproto = NFPROTO_IPV4;
0221         break;
0222     case htons(ETH_P_IPV6):
0223         if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
0224             return 0;
0225         if (nfproto == NFPROTO_UNSPEC)
0226             nfproto = NFPROTO_IPV6;
0227         break;
0228     default:
0229         return 0;
0230     }
0231 
0232     rcu_read_lock();
0233 
0234     if (skb->skb_iif)
0235         indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
0236 
0237     nf_hook_state_init(&state, im->hook, nfproto,
0238                indev ?: skb->dev, skb->dev, NULL, em->net, NULL);
0239 
0240     acpar.match = im->match;
0241     acpar.matchinfo = im->match_data;
0242     acpar.state = &state;
0243 
0244     ret = im->match->match(skb, &acpar);
0245 
0246     rcu_read_unlock();
0247     return ret;
0248 }
0249 
0250 static int em_ipt_dump(struct sk_buff *skb, struct tcf_ematch *em)
0251 {
0252     struct em_ipt_match *im = (void *)em->data;
0253 
0254     if (nla_put_string(skb, TCA_EM_IPT_MATCH_NAME, im->match->name) < 0)
0255         return -EMSGSIZE;
0256     if (nla_put_u32(skb, TCA_EM_IPT_HOOK, im->hook) < 0)
0257         return -EMSGSIZE;
0258     if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0)
0259         return -EMSGSIZE;
0260     if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->nfproto) < 0)
0261         return -EMSGSIZE;
0262     if (nla_put(skb, TCA_EM_IPT_MATCH_DATA,
0263             im->match->usersize ?: im->match->matchsize,
0264             im->match_data) < 0)
0265         return -EMSGSIZE;
0266 
0267     return 0;
0268 }
0269 
0270 static struct tcf_ematch_ops em_ipt_ops = {
0271     .kind     = TCF_EM_IPT,
0272     .change   = em_ipt_change,
0273     .destroy  = em_ipt_destroy,
0274     .match    = em_ipt_match,
0275     .dump     = em_ipt_dump,
0276     .owner    = THIS_MODULE,
0277     .link     = LIST_HEAD_INIT(em_ipt_ops.link)
0278 };
0279 
0280 static int __init init_em_ipt(void)
0281 {
0282     return tcf_em_register(&em_ipt_ops);
0283 }
0284 
0285 static void __exit exit_em_ipt(void)
0286 {
0287     tcf_em_unregister(&em_ipt_ops);
0288 }
0289 
0290 MODULE_LICENSE("GPL");
0291 MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
0292 MODULE_DESCRIPTION("TC extended match for IPtables matches");
0293 
0294 module_init(init_em_ipt);
0295 module_exit(exit_em_ipt);
0296 
0297 MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT);