Merge master.kernel.org:/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6
[linux-2.6] / net / netfilter / xt_tcpmss.c
1 /* Kernel module to match TCP MSS values. */
2
3 /* Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
4  * Portions (C) 2005 by Harald Welte <laforge@netfilter.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <net/tcp.h>
14
15 #include <linux/netfilter/xt_tcpmss.h>
16 #include <linux/netfilter/x_tables.h>
17
18 #include <linux/netfilter_ipv4/ip_tables.h>
19 #include <linux/netfilter_ipv6/ip6_tables.h>
20
21 MODULE_LICENSE("GPL");
22 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
23 MODULE_DESCRIPTION("iptables TCP MSS match module");
24 MODULE_ALIAS("ipt_tcpmss");
25
26 static bool
27 match(const struct sk_buff *skb,
28       const struct net_device *in,
29       const struct net_device *out,
30       const struct xt_match *match,
31       const void *matchinfo,
32       int offset,
33       unsigned int protoff,
34       bool *hotdrop)
35 {
36         const struct xt_tcpmss_match_info *info = matchinfo;
37         struct tcphdr _tcph, *th;
38         /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
39         u8 _opt[15 * 4 - sizeof(_tcph)], *op;
40         unsigned int i, optlen;
41
42         /* If we don't have the whole header, drop packet. */
43         th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
44         if (th == NULL)
45                 goto dropit;
46
47         /* Malformed. */
48         if (th->doff*4 < sizeof(*th))
49                 goto dropit;
50
51         optlen = th->doff*4 - sizeof(*th);
52         if (!optlen)
53                 goto out;
54
55         /* Truncated options. */
56         op = skb_header_pointer(skb, protoff + sizeof(*th), optlen, _opt);
57         if (op == NULL)
58                 goto dropit;
59
60         for (i = 0; i < optlen; ) {
61                 if (op[i] == TCPOPT_MSS
62                     && (optlen - i) >= TCPOLEN_MSS
63                     && op[i+1] == TCPOLEN_MSS) {
64                         u_int16_t mssval;
65
66                         mssval = (op[i+2] << 8) | op[i+3];
67
68                         return (mssval >= info->mss_min &&
69                                 mssval <= info->mss_max) ^ info->invert;
70                 }
71                 if (op[i] < 2)
72                         i++;
73                 else
74                         i += op[i+1] ? : 1;
75         }
76 out:
77         return info->invert;
78
79 dropit:
80         *hotdrop = true;
81         return false;
82 }
83
84 static struct xt_match xt_tcpmss_match[] __read_mostly = {
85         {
86                 .name           = "tcpmss",
87                 .family         = AF_INET,
88                 .match          = match,
89                 .matchsize      = sizeof(struct xt_tcpmss_match_info),
90                 .proto          = IPPROTO_TCP,
91                 .me             = THIS_MODULE,
92         },
93         {
94                 .name           = "tcpmss",
95                 .family         = AF_INET6,
96                 .match          = match,
97                 .matchsize      = sizeof(struct xt_tcpmss_match_info),
98                 .proto          = IPPROTO_TCP,
99                 .me             = THIS_MODULE,
100         },
101 };
102
103 static int __init xt_tcpmss_init(void)
104 {
105         return xt_register_matches(xt_tcpmss_match,
106                                    ARRAY_SIZE(xt_tcpmss_match));
107 }
108
109 static void __exit xt_tcpmss_fini(void)
110 {
111         xt_unregister_matches(xt_tcpmss_match, ARRAY_SIZE(xt_tcpmss_match));
112 }
113
114 module_init(xt_tcpmss_init);
115 module_exit(xt_tcpmss_fini);