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