Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livep...
[linux-2.6-microblaze.git] / net / netfilter / nf_osf.c
1 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2 #include <linux/module.h>
3 #include <linux/kernel.h>
4
5 #include <linux/capability.h>
6 #include <linux/if.h>
7 #include <linux/inetdevice.h>
8 #include <linux/ip.h>
9 #include <linux/list.h>
10 #include <linux/rculist.h>
11 #include <linux/skbuff.h>
12 #include <linux/slab.h>
13 #include <linux/tcp.h>
14
15 #include <net/ip.h>
16 #include <net/tcp.h>
17
18 #include <linux/netfilter/nfnetlink.h>
19 #include <linux/netfilter/x_tables.h>
20 #include <net/netfilter/nf_log.h>
21 #include <linux/netfilter/nf_osf.h>
22
23 static inline int nf_osf_ttl(const struct sk_buff *skb,
24                              const struct nf_osf_info *info,
25                              unsigned char f_ttl)
26 {
27         const struct iphdr *ip = ip_hdr(skb);
28
29         if (info->flags & NF_OSF_TTL) {
30                 if (info->ttl == NF_OSF_TTL_TRUE)
31                         return ip->ttl == f_ttl;
32                 if (info->ttl == NF_OSF_TTL_NOCHECK)
33                         return 1;
34                 else if (ip->ttl <= f_ttl)
35                         return 1;
36                 else {
37                         struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
38                         int ret = 0;
39
40                         for_ifa(in_dev) {
41                                 if (inet_ifa_match(ip->saddr, ifa)) {
42                                         ret = (ip->ttl == f_ttl);
43                                         break;
44                                 }
45                         }
46                         endfor_ifa(in_dev);
47
48                         return ret;
49                 }
50         }
51
52         return ip->ttl == f_ttl;
53 }
54
55 bool
56 nf_osf_match(const struct sk_buff *skb, u_int8_t family,
57              int hooknum, struct net_device *in, struct net_device *out,
58              const struct nf_osf_info *info, struct net *net,
59              const struct list_head *nf_osf_fingers)
60 {
61         const unsigned char *optp = NULL, *_optp = NULL;
62         unsigned int optsize = 0, check_WSS = 0;
63         int fmatch = FMATCH_WRONG, fcount = 0;
64         const struct iphdr *ip = ip_hdr(skb);
65         const struct nf_osf_user_finger *f;
66         unsigned char opts[MAX_IPOPTLEN];
67         const struct nf_osf_finger *kf;
68         u16 window, totlen, mss = 0;
69         const struct tcphdr *tcp;
70         struct tcphdr _tcph;
71         bool df;
72
73         tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
74         if (!tcp)
75                 return false;
76
77         if (!tcp->syn)
78                 return false;
79
80         totlen = ntohs(ip->tot_len);
81         df = ntohs(ip->frag_off) & IP_DF;
82         window = ntohs(tcp->window);
83
84         if (tcp->doff * 4 > sizeof(struct tcphdr)) {
85                 optsize = tcp->doff * 4 - sizeof(struct tcphdr);
86
87                 _optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) +
88                                 sizeof(struct tcphdr), optsize, opts);
89         }
90
91         list_for_each_entry_rcu(kf, &nf_osf_fingers[df], finger_entry) {
92                 int foptsize, optnum;
93
94                 f = &kf->finger;
95
96                 if (!(info->flags & NF_OSF_LOG) && strcmp(info->genre, f->genre))
97                         continue;
98
99                 optp = _optp;
100                 fmatch = FMATCH_WRONG;
101
102                 if (totlen != f->ss || !nf_osf_ttl(skb, info, f->ttl))
103                         continue;
104
105                 /*
106                  * Should not happen if userspace parser was written correctly.
107                  */
108                 if (f->wss.wc >= OSF_WSS_MAX)
109                         continue;
110
111                 /* Check options */
112
113                 foptsize = 0;
114                 for (optnum = 0; optnum < f->opt_num; ++optnum)
115                         foptsize += f->opt[optnum].length;
116
117                 if (foptsize > MAX_IPOPTLEN ||
118                     optsize > MAX_IPOPTLEN ||
119                     optsize != foptsize)
120                         continue;
121
122                 check_WSS = f->wss.wc;
123
124                 for (optnum = 0; optnum < f->opt_num; ++optnum) {
125                         if (f->opt[optnum].kind == (*optp)) {
126                                 __u32 len = f->opt[optnum].length;
127                                 const __u8 *optend = optp + len;
128
129                                 fmatch = FMATCH_OK;
130
131                                 switch (*optp) {
132                                 case OSFOPT_MSS:
133                                         mss = optp[3];
134                                         mss <<= 8;
135                                         mss |= optp[2];
136
137                                         mss = ntohs((__force __be16)mss);
138                                         break;
139                                 case OSFOPT_TS:
140                                         break;
141                                 }
142
143                                 optp = optend;
144                         } else
145                                 fmatch = FMATCH_OPT_WRONG;
146
147                         if (fmatch != FMATCH_OK)
148                                 break;
149                 }
150
151                 if (fmatch != FMATCH_OPT_WRONG) {
152                         fmatch = FMATCH_WRONG;
153
154                         switch (check_WSS) {
155                         case OSF_WSS_PLAIN:
156                                 if (f->wss.val == 0 || window == f->wss.val)
157                                         fmatch = FMATCH_OK;
158                                 break;
159                         case OSF_WSS_MSS:
160                                 /*
161                                  * Some smart modems decrease mangle MSS to
162                                  * SMART_MSS_2, so we check standard, decreased
163                                  * and the one provided in the fingerprint MSS
164                                  * values.
165                                  */
166 #define SMART_MSS_1     1460
167 #define SMART_MSS_2     1448
168                                 if (window == f->wss.val * mss ||
169                                     window == f->wss.val * SMART_MSS_1 ||
170                                     window == f->wss.val * SMART_MSS_2)
171                                         fmatch = FMATCH_OK;
172                                 break;
173                         case OSF_WSS_MTU:
174                                 if (window == f->wss.val * (mss + 40) ||
175                                     window == f->wss.val * (SMART_MSS_1 + 40) ||
176                                     window == f->wss.val * (SMART_MSS_2 + 40))
177                                         fmatch = FMATCH_OK;
178                                 break;
179                         case OSF_WSS_MODULO:
180                                 if ((window % f->wss.val) == 0)
181                                         fmatch = FMATCH_OK;
182                                 break;
183                         }
184                 }
185
186                 if (fmatch != FMATCH_OK)
187                         continue;
188
189                 fcount++;
190
191                 if (info->flags & NF_OSF_LOG)
192                         nf_log_packet(net, family, hooknum, skb,
193                                       in, out, NULL,
194                                       "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n",
195                                       f->genre, f->version, f->subtype,
196                                       &ip->saddr, ntohs(tcp->source),
197                                       &ip->daddr, ntohs(tcp->dest),
198                                       f->ttl - ip->ttl);
199
200                 if ((info->flags & NF_OSF_LOG) &&
201                     info->loglevel == NF_OSF_LOGLEVEL_FIRST)
202                         break;
203         }
204
205         if (!fcount && (info->flags & NF_OSF_LOG))
206                 nf_log_packet(net, family, hooknum, skb, in, out, NULL,
207                               "Remote OS is not known: %pI4:%u -> %pI4:%u\n",
208                               &ip->saddr, ntohs(tcp->source),
209                               &ip->daddr, ntohs(tcp->dest));
210
211         if (fcount)
212                 fmatch = FMATCH_OK;
213
214         return fmatch == FMATCH_OK;
215 }
216 EXPORT_SYMBOL_GPL(nf_osf_match);
217
218 MODULE_LICENSE("GPL");