1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * net/sched/em_ipt.c IPtables matches Ematch
5 * (c) 2018 Eyal Birger <eyal.birger@gmail.com>
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>
22 const struct xt_match
*match
;
25 u8 match_data
[] __aligned(8);
28 struct em_ipt_xt_match
{
30 int (*validate_match_data
)(struct nlattr
**tb
, u8 mrev
);
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
},
42 static int check_match(struct net
*net
, struct em_ipt_match
*im
, int mdata_len
)
44 struct xt_mtchk_param mtpar
= {};
51 mtpar
.table
= "filter";
52 mtpar
.hook_mask
= 1 << im
->hook
;
53 mtpar
.family
= im
->match
->family
;
54 mtpar
.match
= im
->match
;
56 mtpar
.matchinfo
= (void *)im
->match_data
;
57 return xt_check_match(&mtpar
, mdata_len
, 0, 0);
60 static int policy_validate_match_data(struct nlattr
**tb
, u8 mrev
)
63 pr_err("only policy match revision 0 supported");
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");
75 static int addrtype_validate_match_data(struct nlattr
**tb
, u8 mrev
)
78 pr_err("only addrtype match revision 1 supported");
85 static const struct em_ipt_xt_match em_ipt_xt_matches
[] = {
87 .match_name
= "policy",
88 .validate_match_data
= policy_validate_match_data
91 .match_name
= "addrtype",
92 .validate_match_data
= addrtype_validate_match_data
97 static struct xt_match
*get_xt_match(struct nlattr
**tb
)
99 const struct em_ipt_xt_match
*m
;
100 struct nlattr
*mname_attr
;
101 u8 nfproto
, mrev
= 0;
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
))
110 if (!m
->match_name
) {
111 pr_err("Unsupported xt match");
112 return ERR_PTR(-EINVAL
);
115 if (tb
[TCA_EM_IPT_MATCH_REVISION
])
116 mrev
= nla_get_u8(tb
[TCA_EM_IPT_MATCH_REVISION
]);
118 ret
= m
->validate_match_data(tb
, mrev
);
122 nfproto
= nla_get_u8(tb
[TCA_EM_IPT_NFPROTO
]);
123 return xt_request_find_match(nfproto
, m
->match_name
, mrev
);
126 static int em_ipt_change(struct net
*net
, void *data
, int data_len
,
127 struct tcf_ematch
*em
)
129 struct nlattr
*tb
[TCA_EM_IPT_MAX
+ 1];
130 struct em_ipt_match
*im
= NULL
;
131 struct xt_match
*match
;
135 ret
= nla_parse_deprecated(tb
, TCA_EM_IPT_MAX
, data
, data_len
,
136 em_ipt_policy
, NULL
);
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
])
144 nfproto
= nla_get_u8(tb
[TCA_EM_IPT_NFPROTO
]);
153 match
= get_xt_match(tb
);
155 pr_err("unable to load match\n");
156 return PTR_ERR(match
);
159 mdata_len
= XT_ALIGN(nla_len(tb
[TCA_EM_IPT_MATCH_DATA
]));
160 im
= kzalloc(sizeof(*im
) + mdata_len
, GFP_KERNEL
);
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
);
171 ret
= check_match(net
, im
, mdata_len
);
175 em
->datalen
= sizeof(*im
) + mdata_len
;
176 em
->data
= (unsigned long)im
;
181 module_put(match
->me
);
185 static void em_ipt_destroy(struct tcf_ematch
*em
)
187 struct em_ipt_match
*im
= (void *)em
->data
;
192 if (im
->match
->destroy
) {
193 struct xt_mtdtor_param par
= {
196 .matchinfo
= im
->match_data
,
197 .family
= im
->match
->family
199 im
->match
->destroy(&par
);
201 module_put(im
->match
->me
);
205 static int em_ipt_match(struct sk_buff
*skb
, struct tcf_ematch
*em
,
206 struct tcf_pkt_info
*info
)
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
;
215 switch (tc_skb_protocol(skb
)) {
216 case htons(ETH_P_IP
):
217 if (!pskb_network_may_pull(skb
, sizeof(struct iphdr
)))
219 if (nfproto
== NFPROTO_UNSPEC
)
220 nfproto
= NFPROTO_IPV4
;
222 case htons(ETH_P_IPV6
):
223 if (!pskb_network_may_pull(skb
, sizeof(struct ipv6hdr
)))
225 if (nfproto
== NFPROTO_UNSPEC
)
226 nfproto
= NFPROTO_IPV6
;
235 indev
= dev_get_by_index_rcu(em
->net
, skb
->skb_iif
);
237 nf_hook_state_init(&state
, im
->hook
, nfproto
,
238 indev
?: skb
->dev
, skb
->dev
, NULL
, em
->net
, NULL
);
240 acpar
.match
= im
->match
;
241 acpar
.matchinfo
= im
->match_data
;
242 acpar
.state
= &state
;
244 ret
= im
->match
->match(skb
, &acpar
);
250 static int em_ipt_dump(struct sk_buff
*skb
, struct tcf_ematch
*em
)
252 struct em_ipt_match
*im
= (void *)em
->data
;
254 if (nla_put_string(skb
, TCA_EM_IPT_MATCH_NAME
, im
->match
->name
) < 0)
256 if (nla_put_u32(skb
, TCA_EM_IPT_HOOK
, im
->hook
) < 0)
258 if (nla_put_u8(skb
, TCA_EM_IPT_MATCH_REVISION
, im
->match
->revision
) < 0)
260 if (nla_put_u8(skb
, TCA_EM_IPT_NFPROTO
, im
->nfproto
) < 0)
262 if (nla_put(skb
, TCA_EM_IPT_MATCH_DATA
,
263 im
->match
->usersize
?: im
->match
->matchsize
,
270 static struct tcf_ematch_ops em_ipt_ops
= {
272 .change
= em_ipt_change
,
273 .destroy
= em_ipt_destroy
,
274 .match
= em_ipt_match
,
276 .owner
= THIS_MODULE
,
277 .link
= LIST_HEAD_INIT(em_ipt_ops
.link
)
280 static int __init
init_em_ipt(void)
282 return tcf_em_register(&em_ipt_ops
);
285 static void __exit
exit_em_ipt(void)
287 tcf_em_unregister(&em_ipt_ops
);
290 MODULE_LICENSE("GPL");
291 MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
292 MODULE_DESCRIPTION("TC extended match for IPtables matches");
294 module_init(init_em_ipt
);
295 module_exit(exit_em_ipt
);
297 MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT
);