linux/net/sched/em_ipt.c
<<
>>
Prefs
   1/*
   2 * net/sched/em_ipt.c IPtables matches Ematch
   3 *
   4 * (c) 2018 Eyal Birger <eyal.birger@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the License, or (at your option) any later version.
  10 */
  11
  12#include <linux/gfp.h>
  13#include <linux/module.h>
  14#include <linux/types.h>
  15#include <linux/kernel.h>
  16#include <linux/string.h>
  17#include <linux/skbuff.h>
  18#include <linux/tc_ematch/tc_em_ipt.h>
  19#include <linux/netfilter.h>
  20#include <linux/netfilter/x_tables.h>
  21#include <linux/netfilter_ipv4/ip_tables.h>
  22#include <linux/netfilter_ipv6/ip6_tables.h>
  23#include <net/pkt_cls.h>
  24
  25struct em_ipt_match {
  26        const struct xt_match *match;
  27        u32 hook;
  28        u8 match_data[0] __aligned(8);
  29};
  30
  31struct em_ipt_xt_match {
  32        char *match_name;
  33        int (*validate_match_data)(struct nlattr **tb, u8 mrev);
  34};
  35
  36static const struct nla_policy em_ipt_policy[TCA_EM_IPT_MAX + 1] = {
  37        [TCA_EM_IPT_MATCH_NAME]         = { .type = NLA_STRING,
  38                                            .len = XT_EXTENSION_MAXNAMELEN },
  39        [TCA_EM_IPT_MATCH_REVISION]     = { .type = NLA_U8 },
  40        [TCA_EM_IPT_HOOK]               = { .type = NLA_U32 },
  41        [TCA_EM_IPT_NFPROTO]            = { .type = NLA_U8 },
  42        [TCA_EM_IPT_MATCH_DATA]         = { .type = NLA_UNSPEC },
  43};
  44
  45static int check_match(struct net *net, struct em_ipt_match *im, int mdata_len)
  46{
  47        struct xt_mtchk_param mtpar = {};
  48        union {
  49                struct ipt_entry e4;
  50                struct ip6t_entry e6;
  51        } e = {};
  52
  53        mtpar.net       = net;
  54        mtpar.table     = "filter";
  55        mtpar.hook_mask = 1 << im->hook;
  56        mtpar.family    = im->match->family;
  57        mtpar.match     = im->match;
  58        mtpar.entryinfo = &e;
  59        mtpar.matchinfo = (void *)im->match_data;
  60        return xt_check_match(&mtpar, mdata_len, 0, 0);
  61}
  62
  63static int policy_validate_match_data(struct nlattr **tb, u8 mrev)
  64{
  65        if (mrev != 0) {
  66                pr_err("only policy match revision 0 supported");
  67                return -EINVAL;
  68        }
  69
  70        if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) != NF_INET_PRE_ROUTING) {
  71                pr_err("policy can only be matched on NF_INET_PRE_ROUTING");
  72                return -EINVAL;
  73        }
  74
  75        return 0;
  76}
  77
  78static const struct em_ipt_xt_match em_ipt_xt_matches[] = {
  79        {
  80                .match_name = "policy",
  81                .validate_match_data = policy_validate_match_data
  82        },
  83        {}
  84};
  85
  86static struct xt_match *get_xt_match(struct nlattr **tb)
  87{
  88        const struct em_ipt_xt_match *m;
  89        struct nlattr *mname_attr;
  90        u8 nfproto, mrev = 0;
  91        int ret;
  92
  93        mname_attr = tb[TCA_EM_IPT_MATCH_NAME];
  94        for (m = em_ipt_xt_matches; m->match_name; m++) {
  95                if (!nla_strcmp(mname_attr, m->match_name))
  96                        break;
  97        }
  98
  99        if (!m->match_name) {
 100                pr_err("Unsupported xt match");
 101                return ERR_PTR(-EINVAL);
 102        }
 103
 104        if (tb[TCA_EM_IPT_MATCH_REVISION])
 105                mrev = nla_get_u8(tb[TCA_EM_IPT_MATCH_REVISION]);
 106
 107        ret = m->validate_match_data(tb, mrev);
 108        if (ret < 0)
 109                return ERR_PTR(ret);
 110
 111        nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
 112        return xt_request_find_match(nfproto, m->match_name, mrev);
 113}
 114
 115static int em_ipt_change(struct net *net, void *data, int data_len,
 116                         struct tcf_ematch *em)
 117{
 118        struct nlattr *tb[TCA_EM_IPT_MAX + 1];
 119        struct em_ipt_match *im = NULL;
 120        struct xt_match *match;
 121        int mdata_len, ret;
 122
 123        ret = nla_parse(tb, TCA_EM_IPT_MAX, data, data_len, em_ipt_policy,
 124                        NULL);
 125        if (ret < 0)
 126                return ret;
 127
 128        if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] ||
 129            !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO])
 130                return -EINVAL;
 131
 132        match = get_xt_match(tb);
 133        if (IS_ERR(match)) {
 134                pr_err("unable to load match\n");
 135                return PTR_ERR(match);
 136        }
 137
 138        mdata_len = XT_ALIGN(nla_len(tb[TCA_EM_IPT_MATCH_DATA]));
 139        im = kzalloc(sizeof(*im) + mdata_len, GFP_KERNEL);
 140        if (!im) {
 141                ret = -ENOMEM;
 142                goto err;
 143        }
 144
 145        im->match = match;
 146        im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]);
 147        nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len);
 148
 149        ret = check_match(net, im, mdata_len);
 150        if (ret)
 151                goto err;
 152
 153        em->datalen = sizeof(*im) + mdata_len;
 154        em->data = (unsigned long)im;
 155        return 0;
 156
 157err:
 158        kfree(im);
 159        module_put(match->me);
 160        return ret;
 161}
 162
 163static void em_ipt_destroy(struct tcf_ematch *em)
 164{
 165        struct em_ipt_match *im = (void *)em->data;
 166
 167        if (!im)
 168                return;
 169
 170        if (im->match->destroy) {
 171                struct xt_mtdtor_param par = {
 172                        .net = em->net,
 173                        .match = im->match,
 174                        .matchinfo = im->match_data,
 175                        .family = im->match->family
 176                };
 177                im->match->destroy(&par);
 178        }
 179        module_put(im->match->me);
 180        kfree((void *)im);
 181}
 182
 183static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em,
 184                        struct tcf_pkt_info *info)
 185{
 186        const struct em_ipt_match *im = (const void *)em->data;
 187        struct xt_action_param acpar = {};
 188        struct net_device *indev = NULL;
 189        struct nf_hook_state state;
 190        int ret;
 191
 192        rcu_read_lock();
 193
 194        if (skb->skb_iif)
 195                indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
 196
 197        nf_hook_state_init(&state, im->hook, im->match->family,
 198                           indev ?: skb->dev, skb->dev, NULL, em->net, NULL);
 199
 200        acpar.match = im->match;
 201        acpar.matchinfo = im->match_data;
 202        acpar.state = &state;
 203
 204        ret = im->match->match(skb, &acpar);
 205
 206        rcu_read_unlock();
 207        return ret;
 208}
 209
 210static int em_ipt_dump(struct sk_buff *skb, struct tcf_ematch *em)
 211{
 212        struct em_ipt_match *im = (void *)em->data;
 213
 214        if (nla_put_string(skb, TCA_EM_IPT_MATCH_NAME, im->match->name) < 0)
 215                return -EMSGSIZE;
 216        if (nla_put_u32(skb, TCA_EM_IPT_HOOK, im->hook) < 0)
 217                return -EMSGSIZE;
 218        if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0)
 219                return -EMSGSIZE;
 220        if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->match->family) < 0)
 221                return -EMSGSIZE;
 222        if (nla_put(skb, TCA_EM_IPT_MATCH_DATA,
 223                    im->match->usersize ?: im->match->matchsize,
 224                    im->match_data) < 0)
 225                return -EMSGSIZE;
 226
 227        return 0;
 228}
 229
 230static struct tcf_ematch_ops em_ipt_ops = {
 231        .kind     = TCF_EM_IPT,
 232        .change   = em_ipt_change,
 233        .destroy  = em_ipt_destroy,
 234        .match    = em_ipt_match,
 235        .dump     = em_ipt_dump,
 236        .owner    = THIS_MODULE,
 237        .link     = LIST_HEAD_INIT(em_ipt_ops.link)
 238};
 239
 240static int __init init_em_ipt(void)
 241{
 242        return tcf_em_register(&em_ipt_ops);
 243}
 244
 245static void __exit exit_em_ipt(void)
 246{
 247        tcf_em_unregister(&em_ipt_ops);
 248}
 249
 250MODULE_LICENSE("GPL");
 251MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
 252MODULE_DESCRIPTION("TC extended match for IPtables matches");
 253
 254module_init(init_em_ipt);
 255module_exit(exit_em_ipt);
 256
 257MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT);
 258