Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / net / netfilter / xt_tcpudp.c
1 #include <linux/types.h>
2 #include <linux/module.h>
3 #include <net/ip.h>
4 #include <linux/ipv6.h>
5 #include <net/ipv6.h>
6 #include <net/tcp.h>
7 #include <net/udp.h>
8 #include <linux/netfilter/x_tables.h>
9 #include <linux/netfilter/xt_tcpudp.h>
10 #include <linux/netfilter_ipv4/ip_tables.h>
11 #include <linux/netfilter_ipv6/ip6_tables.h>
12
13 MODULE_DESCRIPTION("x_tables match for TCP and UDP(-Lite), supports IPv4 and IPv6");
14 MODULE_LICENSE("GPL");
15 MODULE_ALIAS("xt_tcp");
16 MODULE_ALIAS("xt_udp");
17 MODULE_ALIAS("ipt_udp");
18 MODULE_ALIAS("ipt_tcp");
19 MODULE_ALIAS("ip6t_udp");
20 MODULE_ALIAS("ip6t_tcp");
21
22 #ifdef DEBUG_IP_FIREWALL_USER
23 #define duprintf(format, args...) printk(format , ## args)
24 #else
25 #define duprintf(format, args...)
26 #endif
27
28
29 /* Returns 1 if the port is matched by the range, 0 otherwise */
30 static inline bool
31 port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert)
32 {
33         return (port >= min && port <= max) ^ invert;
34 }
35
36 static bool
37 tcp_find_option(u_int8_t option,
38                 const struct sk_buff *skb,
39                 unsigned int protoff,
40                 unsigned int optlen,
41                 bool invert,
42                 bool *hotdrop)
43 {
44         /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
45         u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
46         unsigned int i;
47
48         duprintf("tcp_match: finding option\n");
49
50         if (!optlen)
51                 return invert;
52
53         /* If we don't have the whole header, drop packet. */
54         op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr),
55                                 optlen, _opt);
56         if (op == NULL) {
57                 *hotdrop = true;
58                 return false;
59         }
60
61         for (i = 0; i < optlen; ) {
62                 if (op[i] == option) return !invert;
63                 if (op[i] < 2) i++;
64                 else i += op[i+1]?:1;
65         }
66
67         return invert;
68 }
69
70 static bool
71 tcp_match(const struct sk_buff *skb,
72           const struct net_device *in,
73           const struct net_device *out,
74           const struct xt_match *match,
75           const void *matchinfo,
76           int offset,
77           unsigned int protoff,
78           bool *hotdrop)
79 {
80         struct tcphdr _tcph, *th;
81         const struct xt_tcp *tcpinfo = matchinfo;
82
83         if (offset) {
84                 /* To quote Alan:
85
86                    Don't allow a fragment of TCP 8 bytes in. Nobody normal
87                    causes this. Its a cracker trying to break in by doing a
88                    flag overwrite to pass the direction checks.
89                 */
90                 if (offset == 1) {
91                         duprintf("Dropping evil TCP offset=1 frag.\n");
92                         *hotdrop = true;
93                 }
94                 /* Must not be a fragment. */
95                 return false;
96         }
97
98 #define FWINVTCP(bool, invflg) ((bool) ^ !!(tcpinfo->invflags & (invflg)))
99
100         th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
101         if (th == NULL) {
102                 /* We've been asked to examine this packet, and we
103                    can't.  Hence, no choice but to drop. */
104                 duprintf("Dropping evil TCP offset=0 tinygram.\n");
105                 *hotdrop = true;
106                 return false;
107         }
108
109         if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
110                         ntohs(th->source),
111                         !!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
112                 return false;
113         if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
114                         ntohs(th->dest),
115                         !!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
116                 return false;
117         if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
118                       == tcpinfo->flg_cmp,
119                       XT_TCP_INV_FLAGS))
120                 return false;
121         if (tcpinfo->option) {
122                 if (th->doff * 4 < sizeof(_tcph)) {
123                         *hotdrop = true;
124                         return false;
125                 }
126                 if (!tcp_find_option(tcpinfo->option, skb, protoff,
127                                      th->doff*4 - sizeof(_tcph),
128                                      tcpinfo->invflags & XT_TCP_INV_OPTION,
129                                      hotdrop))
130                         return false;
131         }
132         return true;
133 }
134
135 /* Called when user tries to insert an entry of this type. */
136 static bool
137 tcp_checkentry(const char *tablename,
138                const void *info,
139                const struct xt_match *match,
140                void *matchinfo,
141                unsigned int hook_mask)
142 {
143         const struct xt_tcp *tcpinfo = matchinfo;
144
145         /* Must specify no unknown invflags */
146         return !(tcpinfo->invflags & ~XT_TCP_INV_MASK);
147 }
148
149 static bool
150 udp_match(const struct sk_buff *skb,
151           const struct net_device *in,
152           const struct net_device *out,
153           const struct xt_match *match,
154           const void *matchinfo,
155           int offset,
156           unsigned int protoff,
157           bool *hotdrop)
158 {
159         struct udphdr _udph, *uh;
160         const struct xt_udp *udpinfo = matchinfo;
161
162         /* Must not be a fragment. */
163         if (offset)
164                 return false;
165
166         uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
167         if (uh == NULL) {
168                 /* We've been asked to examine this packet, and we
169                    can't.  Hence, no choice but to drop. */
170                 duprintf("Dropping evil UDP tinygram.\n");
171                 *hotdrop = true;
172                 return false;
173         }
174
175         return port_match(udpinfo->spts[0], udpinfo->spts[1],
176                           ntohs(uh->source),
177                           !!(udpinfo->invflags & XT_UDP_INV_SRCPT))
178                 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
179                               ntohs(uh->dest),
180                               !!(udpinfo->invflags & XT_UDP_INV_DSTPT));
181 }
182
183 /* Called when user tries to insert an entry of this type. */
184 static bool
185 udp_checkentry(const char *tablename,
186                const void *info,
187                const struct xt_match *match,
188                void *matchinfo,
189                unsigned int hook_mask)
190 {
191         const struct xt_udp *udpinfo = matchinfo;
192
193         /* Must specify no unknown invflags */
194         return !(udpinfo->invflags & ~XT_UDP_INV_MASK);
195 }
196
197 static struct xt_match xt_tcpudp_match[] __read_mostly = {
198         {
199                 .name           = "tcp",
200                 .family         = AF_INET,
201                 .checkentry     = tcp_checkentry,
202                 .match          = tcp_match,
203                 .matchsize      = sizeof(struct xt_tcp),
204                 .proto          = IPPROTO_TCP,
205                 .me             = THIS_MODULE,
206         },
207         {
208                 .name           = "tcp",
209                 .family         = AF_INET6,
210                 .checkentry     = tcp_checkentry,
211                 .match          = tcp_match,
212                 .matchsize      = sizeof(struct xt_tcp),
213                 .proto          = IPPROTO_TCP,
214                 .me             = THIS_MODULE,
215         },
216         {
217                 .name           = "udp",
218                 .family         = AF_INET,
219                 .checkentry     = udp_checkentry,
220                 .match          = udp_match,
221                 .matchsize      = sizeof(struct xt_udp),
222                 .proto          = IPPROTO_UDP,
223                 .me             = THIS_MODULE,
224         },
225         {
226                 .name           = "udp",
227                 .family         = AF_INET6,
228                 .checkentry     = udp_checkentry,
229                 .match          = udp_match,
230                 .matchsize      = sizeof(struct xt_udp),
231                 .proto          = IPPROTO_UDP,
232                 .me             = THIS_MODULE,
233         },
234         {
235                 .name           = "udplite",
236                 .family         = AF_INET,
237                 .checkentry     = udp_checkentry,
238                 .match          = udp_match,
239                 .matchsize      = sizeof(struct xt_udp),
240                 .proto          = IPPROTO_UDPLITE,
241                 .me             = THIS_MODULE,
242         },
243         {
244                 .name           = "udplite",
245                 .family         = AF_INET6,
246                 .checkentry     = udp_checkentry,
247                 .match          = udp_match,
248                 .matchsize      = sizeof(struct xt_udp),
249                 .proto          = IPPROTO_UDPLITE,
250                 .me             = THIS_MODULE,
251         },
252 };
253
254 static int __init xt_tcpudp_init(void)
255 {
256         return xt_register_matches(xt_tcpudp_match,
257                                    ARRAY_SIZE(xt_tcpudp_match));
258 }
259
260 static void __exit xt_tcpudp_fini(void)
261 {
262         xt_unregister_matches(xt_tcpudp_match, ARRAY_SIZE(xt_tcpudp_match));
263 }
264
265 module_init(xt_tcpudp_init);
266 module_exit(xt_tcpudp_fini);