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