Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: sched: add em_ipt ematch for calling xtables matches

The commit a new tc ematch for using netfilter xtable matches.

This allows early classification as well as mirroning/redirecting traffic
based on logic implemented in netfilter extensions.

Current supported use case is classification based on the incoming IPSec
state used during decpsulation using the 'policy' iptables extension
(xt_policy).

The module dynamically fetches the netfilter match module and calls
it using a fake xt_action_param structure based on validated userspace
provided parameters.

As the xt_policy match does not access skb->data, no skb modifications
are needed on match.

Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Eyal Birger and committed by
David S. Miller
ccc007e4 022ddbca

+292 -1
+2 -1
include/uapi/linux/pkt_cls.h
··· 555 555 #define TCF_EM_VLAN 6 556 556 #define TCF_EM_CANID 7 557 557 #define TCF_EM_IPSET 8 558 - #define TCF_EM_MAX 8 558 + #define TCF_EM_IPT 9 559 + #define TCF_EM_MAX 9 559 560 560 561 enum { 561 562 TCF_EM_PROG_TC
+20
include/uapi/linux/tc_ematch/tc_em_ipt.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + #ifndef __LINUX_TC_EM_IPT_H 3 + #define __LINUX_TC_EM_IPT_H 4 + 5 + #include <linux/types.h> 6 + #include <linux/pkt_cls.h> 7 + 8 + enum { 9 + TCA_EM_IPT_UNSPEC, 10 + TCA_EM_IPT_HOOK, 11 + TCA_EM_IPT_MATCH_NAME, 12 + TCA_EM_IPT_MATCH_REVISION, 13 + TCA_EM_IPT_NFPROTO, 14 + TCA_EM_IPT_MATCH_DATA, 15 + __TCA_EM_IPT_MAX 16 + }; 17 + 18 + #define TCA_EM_IPT_MAX (__TCA_EM_IPT_MAX - 1) 19 + 20 + #endif
+12
net/sched/Kconfig
··· 658 658 To compile this code as a module, choose M here: the 659 659 module will be called em_ipset. 660 660 661 + config NET_EMATCH_IPT 662 + tristate "IPtables Matches" 663 + depends on NET_EMATCH && NETFILTER && NETFILTER_XTABLES 664 + ---help--- 665 + Say Y here to be able to classify packets based on iptables 666 + matches. 667 + Current supported match is "policy" which allows packet classification 668 + based on IPsec policy that was used during decapsulation 669 + 670 + To compile this code as a module, choose M here: the 671 + module will be called em_ipt. 672 + 661 673 config NET_CLS_ACT 662 674 bool "Actions" 663 675 select NET_CLS
+1
net/sched/Makefile
··· 75 75 obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o 76 76 obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o 77 77 obj-$(CONFIG_NET_EMATCH_IPSET) += em_ipset.o 78 + obj-$(CONFIG_NET_EMATCH_IPT) += em_ipt.o
+257
net/sched/em_ipt.c
··· 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(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 + 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);