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