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 25 struct em_ipt_match { 26 const struct xt_match *match; 27 u32 hook; 28 u8 match_data[0] __aligned(8); 29 }; 30 31 struct em_ipt_xt_match { 32 char *match_name; 33 int (*validate_match_data)(struct nlattr **tb, u8 mrev); 34 }; 35 36 static 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 45 static 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 63 static 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 78 static 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 86 static 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 115 static 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_deprecated(tb, TCA_EM_IPT_MAX, data, data_len, 124 em_ipt_policy, 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 157 err: 158 kfree(im); 159 module_put(match->me); 160 return ret; 161 } 162 163 static 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 183 static 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 210 static 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 230 static 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 240 static int __init init_em_ipt(void) 241 { 242 return tcf_em_register(&em_ipt_ops); 243 } 244 245 static void __exit exit_em_ipt(void) 246 { 247 tcf_em_unregister(&em_ipt_ops); 248 } 249 250 MODULE_LICENSE("GPL"); 251 MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>"); 252 MODULE_DESCRIPTION("TC extended match for IPtables matches"); 253 254 module_init(init_em_ipt); 255 module_exit(exit_em_ipt); 256 257 MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT); 258