xref: /openbmc/linux/net/netfilter/xt_ecn.c (revision d2912cb1)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2af0d29cdSPatrick McHardy /*
3af0d29cdSPatrick McHardy  * Xtables module for matching the value of the IPv4/IPv6 and TCP ECN bits
4d446a820SJan Engelhardt  *
5d446a820SJan Engelhardt  * (C) 2002 by Harald Welte <laforge@gnumonks.org>
6af0d29cdSPatrick McHardy  * (C) 2011 Patrick McHardy <kaber@trash.net>
7d446a820SJan Engelhardt  */
8d446a820SJan Engelhardt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9d446a820SJan Engelhardt #include <linux/in.h>
10d446a820SJan Engelhardt #include <linux/ip.h>
11d446a820SJan Engelhardt #include <net/ip.h>
12d446a820SJan Engelhardt #include <linux/module.h>
13d446a820SJan Engelhardt #include <linux/skbuff.h>
14d446a820SJan Engelhardt #include <linux/tcp.h>
15d446a820SJan Engelhardt 
16d446a820SJan Engelhardt #include <linux/netfilter/x_tables.h>
17a4c6f9d3SJan Engelhardt #include <linux/netfilter/xt_ecn.h>
18d446a820SJan Engelhardt #include <linux/netfilter_ipv4/ip_tables.h>
19af0d29cdSPatrick McHardy #include <linux/netfilter_ipv6/ip6_tables.h>
20d446a820SJan Engelhardt 
21d446a820SJan Engelhardt MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
22af0d29cdSPatrick McHardy MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match");
23d446a820SJan Engelhardt MODULE_LICENSE("GPL");
24d446a820SJan Engelhardt MODULE_ALIAS("ipt_ecn");
25af0d29cdSPatrick McHardy MODULE_ALIAS("ip6t_ecn");
26d446a820SJan Engelhardt 
match_tcp(const struct sk_buff * skb,struct xt_action_param * par)27af0d29cdSPatrick McHardy static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
28d446a820SJan Engelhardt {
29af0d29cdSPatrick McHardy 	const struct xt_ecn_info *einfo = par->matchinfo;
30d446a820SJan Engelhardt 	struct tcphdr _tcph;
31d446a820SJan Engelhardt 	const struct tcphdr *th;
32d446a820SJan Engelhardt 
33d446a820SJan Engelhardt 	/* In practice, TCP match does this, so can't fail.  But let's
34d446a820SJan Engelhardt 	 * be good citizens.
35d446a820SJan Engelhardt 	 */
36af0d29cdSPatrick McHardy 	th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
3742c344a3SJan Engelhardt 	if (th == NULL)
38d446a820SJan Engelhardt 		return false;
39d446a820SJan Engelhardt 
40a4c6f9d3SJan Engelhardt 	if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
41a4c6f9d3SJan Engelhardt 		if (einfo->invert & XT_ECN_OP_MATCH_ECE) {
42d446a820SJan Engelhardt 			if (th->ece == 1)
43d446a820SJan Engelhardt 				return false;
44d446a820SJan Engelhardt 		} else {
45d446a820SJan Engelhardt 			if (th->ece == 0)
46d446a820SJan Engelhardt 				return false;
47d446a820SJan Engelhardt 		}
48d446a820SJan Engelhardt 	}
49d446a820SJan Engelhardt 
50a4c6f9d3SJan Engelhardt 	if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
51a4c6f9d3SJan Engelhardt 		if (einfo->invert & XT_ECN_OP_MATCH_CWR) {
52d446a820SJan Engelhardt 			if (th->cwr == 1)
53d446a820SJan Engelhardt 				return false;
54d446a820SJan Engelhardt 		} else {
55d446a820SJan Engelhardt 			if (th->cwr == 0)
56d446a820SJan Engelhardt 				return false;
57d446a820SJan Engelhardt 		}
58d446a820SJan Engelhardt 	}
59d446a820SJan Engelhardt 
60d446a820SJan Engelhardt 	return true;
61d446a820SJan Engelhardt }
62d446a820SJan Engelhardt 
match_ip(const struct sk_buff * skb,const struct xt_ecn_info * einfo)63af0d29cdSPatrick McHardy static inline bool match_ip(const struct sk_buff *skb,
64af0d29cdSPatrick McHardy 			    const struct xt_ecn_info *einfo)
65af0d29cdSPatrick McHardy {
66af0d29cdSPatrick McHardy 	return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
67af0d29cdSPatrick McHardy 	       !!(einfo->invert & XT_ECN_OP_MATCH_IP);
68af0d29cdSPatrick McHardy }
69af0d29cdSPatrick McHardy 
ecn_mt4(const struct sk_buff * skb,struct xt_action_param * par)70af0d29cdSPatrick McHardy static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
71d446a820SJan Engelhardt {
72a4c6f9d3SJan Engelhardt 	const struct xt_ecn_info *info = par->matchinfo;
73d446a820SJan Engelhardt 
7442c344a3SJan Engelhardt 	if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info))
75d446a820SJan Engelhardt 		return false;
76d446a820SJan Engelhardt 
7742c344a3SJan Engelhardt 	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
7842c344a3SJan Engelhardt 	    !match_tcp(skb, par))
79d446a820SJan Engelhardt 		return false;
80d446a820SJan Engelhardt 
81d446a820SJan Engelhardt 	return true;
82d446a820SJan Engelhardt }
83d446a820SJan Engelhardt 
ecn_mt_check4(const struct xt_mtchk_param * par)84af0d29cdSPatrick McHardy static int ecn_mt_check4(const struct xt_mtchk_param *par)
85d446a820SJan Engelhardt {
86a4c6f9d3SJan Engelhardt 	const struct xt_ecn_info *info = par->matchinfo;
87d446a820SJan Engelhardt 	const struct ipt_ip *ip = par->entryinfo;
88d446a820SJan Engelhardt 
89a4c6f9d3SJan Engelhardt 	if (info->operation & XT_ECN_OP_MATCH_MASK)
90d446a820SJan Engelhardt 		return -EINVAL;
91d446a820SJan Engelhardt 
92a4c6f9d3SJan Engelhardt 	if (info->invert & XT_ECN_OP_MATCH_MASK)
93d446a820SJan Engelhardt 		return -EINVAL;
94d446a820SJan Engelhardt 
95a4c6f9d3SJan Engelhardt 	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
96d446a820SJan Engelhardt 	    (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) {
97b2606644SFlorian Westphal 		pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
98d446a820SJan Engelhardt 		return -EINVAL;
99d446a820SJan Engelhardt 	}
100d446a820SJan Engelhardt 
101d446a820SJan Engelhardt 	return 0;
102d446a820SJan Engelhardt }
103d446a820SJan Engelhardt 
match_ipv6(const struct sk_buff * skb,const struct xt_ecn_info * einfo)104af0d29cdSPatrick McHardy static inline bool match_ipv6(const struct sk_buff *skb,
105af0d29cdSPatrick McHardy 			      const struct xt_ecn_info *einfo)
106af0d29cdSPatrick McHardy {
107af0d29cdSPatrick McHardy 	return (((ipv6_hdr(skb)->flow_lbl[0] >> 4) & XT_ECN_IP_MASK) ==
108af0d29cdSPatrick McHardy 	        einfo->ip_ect) ^
109af0d29cdSPatrick McHardy 	       !!(einfo->invert & XT_ECN_OP_MATCH_IP);
110af0d29cdSPatrick McHardy }
111af0d29cdSPatrick McHardy 
ecn_mt6(const struct sk_buff * skb,struct xt_action_param * par)112af0d29cdSPatrick McHardy static bool ecn_mt6(const struct sk_buff *skb, struct xt_action_param *par)
113af0d29cdSPatrick McHardy {
114af0d29cdSPatrick McHardy 	const struct xt_ecn_info *info = par->matchinfo;
115af0d29cdSPatrick McHardy 
116af0d29cdSPatrick McHardy 	if (info->operation & XT_ECN_OP_MATCH_IP && !match_ipv6(skb, info))
117af0d29cdSPatrick McHardy 		return false;
118af0d29cdSPatrick McHardy 
119af0d29cdSPatrick McHardy 	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
120af0d29cdSPatrick McHardy 	    !match_tcp(skb, par))
121af0d29cdSPatrick McHardy 		return false;
122af0d29cdSPatrick McHardy 
123af0d29cdSPatrick McHardy 	return true;
124af0d29cdSPatrick McHardy }
125af0d29cdSPatrick McHardy 
ecn_mt_check6(const struct xt_mtchk_param * par)126af0d29cdSPatrick McHardy static int ecn_mt_check6(const struct xt_mtchk_param *par)
127af0d29cdSPatrick McHardy {
128af0d29cdSPatrick McHardy 	const struct xt_ecn_info *info = par->matchinfo;
129af0d29cdSPatrick McHardy 	const struct ip6t_ip6 *ip = par->entryinfo;
130af0d29cdSPatrick McHardy 
131af0d29cdSPatrick McHardy 	if (info->operation & XT_ECN_OP_MATCH_MASK)
132af0d29cdSPatrick McHardy 		return -EINVAL;
133af0d29cdSPatrick McHardy 
134af0d29cdSPatrick McHardy 	if (info->invert & XT_ECN_OP_MATCH_MASK)
135af0d29cdSPatrick McHardy 		return -EINVAL;
136af0d29cdSPatrick McHardy 
137af0d29cdSPatrick McHardy 	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
138af0d29cdSPatrick McHardy 	    (ip->proto != IPPROTO_TCP || ip->invflags & IP6T_INV_PROTO)) {
139b2606644SFlorian Westphal 		pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
140af0d29cdSPatrick McHardy 		return -EINVAL;
141af0d29cdSPatrick McHardy 	}
142af0d29cdSPatrick McHardy 
143af0d29cdSPatrick McHardy 	return 0;
144af0d29cdSPatrick McHardy }
145af0d29cdSPatrick McHardy 
146af0d29cdSPatrick McHardy static struct xt_match ecn_mt_reg[] __read_mostly = {
147af0d29cdSPatrick McHardy 	{
148d446a820SJan Engelhardt 		.name		= "ecn",
149d446a820SJan Engelhardt 		.family		= NFPROTO_IPV4,
150af0d29cdSPatrick McHardy 		.match		= ecn_mt4,
151a4c6f9d3SJan Engelhardt 		.matchsize	= sizeof(struct xt_ecn_info),
152af0d29cdSPatrick McHardy 		.checkentry	= ecn_mt_check4,
153d446a820SJan Engelhardt 		.me		= THIS_MODULE,
154af0d29cdSPatrick McHardy 	},
155af0d29cdSPatrick McHardy 	{
156af0d29cdSPatrick McHardy 		.name		= "ecn",
157af0d29cdSPatrick McHardy 		.family		= NFPROTO_IPV6,
158af0d29cdSPatrick McHardy 		.match		= ecn_mt6,
159af0d29cdSPatrick McHardy 		.matchsize	= sizeof(struct xt_ecn_info),
160af0d29cdSPatrick McHardy 		.checkentry	= ecn_mt_check6,
161af0d29cdSPatrick McHardy 		.me		= THIS_MODULE,
162af0d29cdSPatrick McHardy 	},
163d446a820SJan Engelhardt };
164d446a820SJan Engelhardt 
ecn_mt_init(void)165d446a820SJan Engelhardt static int __init ecn_mt_init(void)
166d446a820SJan Engelhardt {
167af0d29cdSPatrick McHardy 	return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
168d446a820SJan Engelhardt }
169d446a820SJan Engelhardt 
ecn_mt_exit(void)170d446a820SJan Engelhardt static void __exit ecn_mt_exit(void)
171d446a820SJan Engelhardt {
172af0d29cdSPatrick McHardy 	xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
173d446a820SJan Engelhardt }
174d446a820SJan Engelhardt 
175d446a820SJan Engelhardt module_init(ecn_mt_init);
176d446a820SJan Engelhardt module_exit(ecn_mt_exit);
177