Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
af0d29cd PM |
2 | /* |
3 | * Xtables module for matching the value of the IPv4/IPv6 and TCP ECN bits | |
1da177e4 | 4 | * |
1da177e4 | 5 | * (C) 2002 by Harald Welte <laforge@gnumonks.org> |
af0d29cd | 6 | * (C) 2011 Patrick McHardy <kaber@trash.net> |
1da177e4 | 7 | */ |
ff67e4e4 | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
6709dbbb JE |
9 | #include <linux/in.h> |
10 | #include <linux/ip.h> | |
c9bdd4b5 | 11 | #include <net/ip.h> |
1da177e4 LT |
12 | #include <linux/module.h> |
13 | #include <linux/skbuff.h> | |
14 | #include <linux/tcp.h> | |
15 | ||
6709dbbb | 16 | #include <linux/netfilter/x_tables.h> |
a4c6f9d3 | 17 | #include <linux/netfilter/xt_ecn.h> |
1da177e4 | 18 | #include <linux/netfilter_ipv4/ip_tables.h> |
af0d29cd | 19 | #include <linux/netfilter_ipv6/ip6_tables.h> |
1da177e4 LT |
20 | |
21 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | |
af0d29cd | 22 | MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match"); |
1da177e4 | 23 | MODULE_LICENSE("GPL"); |
d446a820 | 24 | MODULE_ALIAS("ipt_ecn"); |
af0d29cd | 25 | MODULE_ALIAS("ip6t_ecn"); |
1da177e4 | 26 | |
af0d29cd | 27 | static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par) |
1da177e4 | 28 | { |
af0d29cd | 29 | const struct xt_ecn_info *einfo = par->matchinfo; |
a47362a2 JE |
30 | struct tcphdr _tcph; |
31 | const struct tcphdr *th; | |
1da177e4 LT |
32 | |
33 | /* In practice, TCP match does this, so can't fail. But let's | |
34 | * be good citizens. | |
35 | */ | |
af0d29cd | 36 | th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); |
42c344a3 | 37 | if (th == NULL) |
1d93a9cb | 38 | return false; |
1da177e4 | 39 | |
a4c6f9d3 JE |
40 | if (einfo->operation & XT_ECN_OP_MATCH_ECE) { |
41 | if (einfo->invert & XT_ECN_OP_MATCH_ECE) { | |
1da177e4 | 42 | if (th->ece == 1) |
1d93a9cb | 43 | return false; |
1da177e4 LT |
44 | } else { |
45 | if (th->ece == 0) | |
1d93a9cb | 46 | return false; |
1da177e4 LT |
47 | } |
48 | } | |
49 | ||
a4c6f9d3 JE |
50 | if (einfo->operation & XT_ECN_OP_MATCH_CWR) { |
51 | if (einfo->invert & XT_ECN_OP_MATCH_CWR) { | |
1da177e4 | 52 | if (th->cwr == 1) |
1d93a9cb | 53 | return false; |
1da177e4 LT |
54 | } else { |
55 | if (th->cwr == 0) | |
1d93a9cb | 56 | return false; |
1da177e4 LT |
57 | } |
58 | } | |
59 | ||
1d93a9cb | 60 | return true; |
1da177e4 LT |
61 | } |
62 | ||
af0d29cd PM |
63 | static 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 | ||
70 | static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par) | |
1da177e4 | 71 | { |
a4c6f9d3 | 72 | const struct xt_ecn_info *info = par->matchinfo; |
1da177e4 | 73 | |
42c344a3 JE |
74 | if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info)) |
75 | return false; | |
1da177e4 | 76 | |
42c344a3 JE |
77 | if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) && |
78 | !match_tcp(skb, par)) | |
79 | return false; | |
1da177e4 | 80 | |
1d93a9cb | 81 | return true; |
1da177e4 LT |
82 | } |
83 | ||
af0d29cd | 84 | static int ecn_mt_check4(const struct xt_mtchk_param *par) |
1da177e4 | 85 | { |
a4c6f9d3 | 86 | const struct xt_ecn_info *info = par->matchinfo; |
9b4fce7a | 87 | const struct ipt_ip *ip = par->entryinfo; |
1da177e4 | 88 | |
a4c6f9d3 | 89 | if (info->operation & XT_ECN_OP_MATCH_MASK) |
bd414ee6 | 90 | return -EINVAL; |
1da177e4 | 91 | |
a4c6f9d3 | 92 | if (info->invert & XT_ECN_OP_MATCH_MASK) |
bd414ee6 | 93 | return -EINVAL; |
1da177e4 | 94 | |
a4c6f9d3 | 95 | if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) && |
58d5a025 | 96 | (ip->proto != IPPROTO_TCP || ip->invflags & IPT_INV_PROTO)) { |
b2606644 | 97 | pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n"); |
bd414ee6 | 98 | return -EINVAL; |
1da177e4 LT |
99 | } |
100 | ||
bd414ee6 | 101 | return 0; |
1da177e4 LT |
102 | } |
103 | ||
af0d29cd PM |
104 | static 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 | ||
112 | static 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 | ||
126 | static 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)) { | |
b2606644 | 139 | pr_info_ratelimited("cannot match TCP bits for non-tcp packets\n"); |
af0d29cd PM |
140 | return -EINVAL; |
141 | } | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static 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 | }, | |
1da177e4 LT |
163 | }; |
164 | ||
d3c5ee6d | 165 | static int __init ecn_mt_init(void) |
1da177e4 | 166 | { |
af0d29cd | 167 | return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg)); |
1da177e4 LT |
168 | } |
169 | ||
d3c5ee6d | 170 | static void __exit ecn_mt_exit(void) |
1da177e4 | 171 | { |
af0d29cd | 172 | xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg)); |
1da177e4 LT |
173 | } |
174 | ||
d3c5ee6d JE |
175 | module_init(ecn_mt_init); |
176 | module_exit(ecn_mt_exit); |