root/net/sched/em_ipt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. check_match
  2. policy_validate_match_data
  3. addrtype_validate_match_data
  4. get_xt_match
  5. em_ipt_change
  6. em_ipt_destroy
  7. em_ipt_match
  8. em_ipt_dump
  9. init_em_ipt
  10. exit_em_ipt

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

/* [<][>][^][v][top][bottom][index][help] */