blob: b96e8203ac54982e562ea083f390eff5d213477e [file] [log] [blame]
Thomas Gleixnerd2912cb2019-06-04 10:11:33 +02001// SPDX-License-Identifier: GPL-2.0-only
Patrick McHardyaf0d29c2011-06-09 15:20:27 +02002/*
3 * Xtables module for matching the value of the IPv4/IPv6 and TCP ECN bits
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * (C) 2002 by Harald Welte <laforge@gnumonks.org>
Patrick McHardyaf0d29c2011-06-09 15:20:27 +02006 * (C) 2011 Patrick McHardy <kaber@trash.net>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 */
Jan Engelhardtff67e4e2010-03-19 21:08:16 +01008#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Jan Engelhardt6709dbb2007-02-07 15:11:19 -08009#include <linux/in.h>
10#include <linux/ip.h>
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -030011#include <net/ip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/tcp.h>
15
Jan Engelhardt6709dbb2007-02-07 15:11:19 -080016#include <linux/netfilter/x_tables.h>
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020017#include <linux/netfilter/xt_ecn.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/netfilter_ipv4/ip_tables.h>
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020019#include <linux/netfilter_ipv6/ip6_tables.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
21MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020022MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match");
Linus Torvalds1da177e2005-04-16 15:20:36 -070023MODULE_LICENSE("GPL");
Jan Engelhardtd446a8202011-06-09 21:03:07 +020024MODULE_ALIAS("ipt_ecn");
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020025MODULE_ALIAS("ip6t_ecn");
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020027static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020029 const struct xt_ecn_info *einfo = par->matchinfo;
Jan Engelhardta47362a2007-07-07 22:16:55 -070030 struct tcphdr _tcph;
31 const struct tcphdr *th;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33 /* In practice, TCP match does this, so can't fail. But let's
34 * be good citizens.
35 */
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020036 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
Jan Engelhardt42c344a2011-06-09 22:16:50 +020037 if (th == NULL)
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070038 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020040 if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
41 if (einfo->invert & XT_ECN_OP_MATCH_ECE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 if (th->ece == 1)
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070043 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 } else {
45 if (th->ece == 0)
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070046 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 }
48 }
49
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020050 if (einfo->operation & XT_ECN_OP_MATCH_CWR) {
51 if (einfo->invert & XT_ECN_OP_MATCH_CWR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 if (th->cwr == 1)
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070053 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 } else {
55 if (th->cwr == 0)
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070056 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 }
58 }
59
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070060 return true;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061}
62
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020063static inline bool match_ip(const struct sk_buff *skb,
64 const struct xt_ecn_info *einfo)
65{
66 return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
67 !!(einfo->invert & XT_ECN_OP_MATCH_IP);
68}
69
70static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -070071{
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020072 const struct xt_ecn_info *info = par->matchinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Jan Engelhardt42c344a2011-06-09 22:16:50 +020074 if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info))
75 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
Jan Engelhardt42c344a2011-06-09 22:16:50 +020077 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
78 !match_tcp(skb, par))
79 return false;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Jan Engelhardt1d93a9c2007-07-07 22:15:35 -070081 return true;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082}
83
Patrick McHardyaf0d29c2011-06-09 15:20:27 +020084static int ecn_mt_check4(const struct xt_mtchk_param *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085{
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020086 const struct xt_ecn_info *info = par->matchinfo;
Jan Engelhardt9b4fce72008-10-08 11:35:18 +020087 const struct ipt_ip *ip = par->entryinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020089 if (info->operation & XT_ECN_OP_MATCH_MASK)
Jan Engelhardtbd414ee2010-03-23 16:35:56 +010090 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020092 if (info->invert & XT_ECN_OP_MATCH_MASK)
Jan Engelhardtbd414ee2010-03-23 16:35:56 +010093 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
Jan Engelhardta4c6f9d2011-06-09 21:15:37 +020095 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
Patrick McHardy58d5a022011-06-16 17:24:17 +020096 (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) {
Florian Westphalb2606642018-02-09 15:52:07 +010097 pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
Jan Engelhardtbd414ee2010-03-23 16:35:56 +010098 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 }
100
Jan Engelhardtbd414ee2010-03-23 16:35:56 +0100101 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102}
103
Patrick McHardyaf0d29c2011-06-09 15:20:27 +0200104static inline bool match_ipv6(const struct sk_buff *skb,
105 const struct xt_ecn_info *einfo)
106{
107 return (((ipv6_hdr(skb)->flow_lbl[0] >> 4) & XT_ECN_IP_MASK) ==
108 einfo->ip_ect) ^
109 !!(einfo->invert & XT_ECN_OP_MATCH_IP);
110}
111
112static bool ecn_mt6(const struct sk_buff *skb, struct xt_action_param *par)
113{
114 const struct xt_ecn_info *info = par->matchinfo;
115
116 if (info->operation & XT_ECN_OP_MATCH_IP && !match_ipv6(skb, info))
117 return false;
118
119 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
120 !match_tcp(skb, par))
121 return false;
122
123 return true;
124}
125
126static int ecn_mt_check6(const struct xt_mtchk_param *par)
127{
128 const struct xt_ecn_info *info = par->matchinfo;
129 const struct ip6t_ip6 *ip = par->entryinfo;
130
131 if (info->operation & XT_ECN_OP_MATCH_MASK)
132 return -EINVAL;
133
134 if (info->invert & XT_ECN_OP_MATCH_MASK)
135 return -EINVAL;
136
137 if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
138 (ip->proto != IPPROTO_TCP || ip->invflags & IP6T_INV_PROTO)) {
Florian Westphalb2606642018-02-09 15:52:07 +0100139 pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n");
Patrick McHardyaf0d29c2011-06-09 15:20:27 +0200140 return -EINVAL;
141 }
142
143 return 0;
144}
145
146static struct xt_match ecn_mt_reg[] __read_mostly = {
147 {
148 .name = "ecn",
149 .family = NFPROTO_IPV4,
150 .match = ecn_mt4,
151 .matchsize = sizeof(struct xt_ecn_info),
152 .checkentry = ecn_mt_check4,
153 .me = THIS_MODULE,
154 },
155 {
156 .name = "ecn",
157 .family = NFPROTO_IPV6,
158 .match = ecn_mt6,
159 .matchsize = sizeof(struct xt_ecn_info),
160 .checkentry = ecn_mt_check6,
161 .me = THIS_MODULE,
162 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163};
164
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800165static int __init ecn_mt_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
Patrick McHardyaf0d29c2011-06-09 15:20:27 +0200167 return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168}
169
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800170static void __exit ecn_mt_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
Patrick McHardyaf0d29c2011-06-09 15:20:27 +0200172 xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
Jan Engelhardtd3c5ee62007-12-04 23:24:03 -0800175module_init(ecn_mt_init);
176module_exit(ecn_mt_exit);