diff options
-rw-r--r-- | include/linux/netfilter/Kbuild | 1 | ||||
-rw-r--r-- | include/linux/netfilter/nfnetlink.h | 3 | ||||
-rw-r--r-- | include/linux/netfilter/xt_osf.h | 133 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 13 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_osf.c | 428 |
6 files changed, 578 insertions, 1 deletions
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild index af9d2fb97212..2aea50399c0b 100644 --- a/include/linux/netfilter/Kbuild +++ b/include/linux/netfilter/Kbuild | |||
@@ -33,6 +33,7 @@ header-y += xt_limit.h | |||
33 | header-y += xt_mac.h | 33 | header-y += xt_mac.h |
34 | header-y += xt_mark.h | 34 | header-y += xt_mark.h |
35 | header-y += xt_multiport.h | 35 | header-y += xt_multiport.h |
36 | header-y += xt_osf.h | ||
36 | header-y += xt_owner.h | 37 | header-y += xt_owner.h |
37 | header-y += xt_pkttype.h | 38 | header-y += xt_pkttype.h |
38 | header-y += xt_quota.h | 39 | header-y += xt_quota.h |
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 2214e5161461..bff4d5741d98 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h | |||
@@ -46,7 +46,8 @@ struct nfgenmsg { | |||
46 | #define NFNL_SUBSYS_CTNETLINK_EXP 2 | 46 | #define NFNL_SUBSYS_CTNETLINK_EXP 2 |
47 | #define NFNL_SUBSYS_QUEUE 3 | 47 | #define NFNL_SUBSYS_QUEUE 3 |
48 | #define NFNL_SUBSYS_ULOG 4 | 48 | #define NFNL_SUBSYS_ULOG 4 |
49 | #define NFNL_SUBSYS_COUNT 5 | 49 | #define NFNL_SUBSYS_OSF 5 |
50 | #define NFNL_SUBSYS_COUNT 6 | ||
50 | 51 | ||
51 | #ifdef __KERNEL__ | 52 | #ifdef __KERNEL__ |
52 | 53 | ||
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h new file mode 100644 index 000000000000..fd2272e0959a --- /dev/null +++ b/include/linux/netfilter/xt_osf.h | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.ru> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #ifndef _XT_OSF_H | ||
21 | #define _XT_OSF_H | ||
22 | |||
23 | #define MAXGENRELEN 32 | ||
24 | |||
25 | #define XT_OSF_GENRE (1<<0) | ||
26 | #define XT_OSF_TTL (1<<1) | ||
27 | #define XT_OSF_LOG (1<<2) | ||
28 | #define XT_OSF_INVERT (1<<3) | ||
29 | |||
30 | #define XT_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ | ||
31 | #define XT_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */ | ||
32 | #define XT_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ | ||
33 | |||
34 | #define XT_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */ | ||
35 | #define XT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */ | ||
36 | #define XT_OSF_TTL_NOCHECK 2 /* Do not compare ip and fingerprint TTL at all */ | ||
37 | |||
38 | struct xt_osf_info { | ||
39 | char genre[MAXGENRELEN]; | ||
40 | __u32 len; | ||
41 | __u32 flags; | ||
42 | __u32 loglevel; | ||
43 | __u32 ttl; | ||
44 | }; | ||
45 | |||
46 | /* | ||
47 | * Wildcard MSS (kind of). | ||
48 | * It is used to implement a state machine for the different wildcard values | ||
49 | * of the MSS and window sizes. | ||
50 | */ | ||
51 | struct xt_osf_wc { | ||
52 | __u32 wc; | ||
53 | __u32 val; | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * This struct represents IANA options | ||
58 | * http://www.iana.org/assignments/tcp-parameters | ||
59 | */ | ||
60 | struct xt_osf_opt { | ||
61 | __u16 kind, length; | ||
62 | struct xt_osf_wc wc; | ||
63 | }; | ||
64 | |||
65 | struct xt_osf_user_finger { | ||
66 | struct xt_osf_wc wss; | ||
67 | |||
68 | __u8 ttl, df; | ||
69 | __u16 ss, mss; | ||
70 | __u16 opt_num; | ||
71 | |||
72 | char genre[MAXGENRELEN]; | ||
73 | char version[MAXGENRELEN]; | ||
74 | char subtype[MAXGENRELEN]; | ||
75 | |||
76 | /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ | ||
77 | struct xt_osf_opt opt[MAX_IPOPTLEN]; | ||
78 | }; | ||
79 | |||
80 | struct xt_osf_nlmsg { | ||
81 | struct xt_osf_user_finger f; | ||
82 | struct iphdr ip; | ||
83 | struct tcphdr tcp; | ||
84 | }; | ||
85 | |||
86 | /* Defines for IANA option kinds */ | ||
87 | |||
88 | enum iana_options { | ||
89 | OSFOPT_EOL = 0, /* End of options */ | ||
90 | OSFOPT_NOP, /* NOP */ | ||
91 | OSFOPT_MSS, /* Maximum segment size */ | ||
92 | OSFOPT_WSO, /* Window scale option */ | ||
93 | OSFOPT_SACKP, /* SACK permitted */ | ||
94 | OSFOPT_SACK, /* SACK */ | ||
95 | OSFOPT_ECHO, | ||
96 | OSFOPT_ECHOREPLY, | ||
97 | OSFOPT_TS, /* Timestamp option */ | ||
98 | OSFOPT_POCP, /* Partial Order Connection Permitted */ | ||
99 | OSFOPT_POSP, /* Partial Order Service Profile */ | ||
100 | |||
101 | /* Others are not used in the current OSF */ | ||
102 | OSFOPT_EMPTY = 255, | ||
103 | }; | ||
104 | |||
105 | /* | ||
106 | * Initial window size option state machine: multiple of mss, mtu or | ||
107 | * plain numeric value. Can also be made as plain numeric value which | ||
108 | * is not a multiple of specified value. | ||
109 | */ | ||
110 | enum xt_osf_window_size_options { | ||
111 | OSF_WSS_PLAIN = 0, | ||
112 | OSF_WSS_MSS, | ||
113 | OSF_WSS_MTU, | ||
114 | OSF_WSS_MODULO, | ||
115 | OSF_WSS_MAX, | ||
116 | }; | ||
117 | |||
118 | /* | ||
119 | * Add/remove fingerprint from the kernel. | ||
120 | */ | ||
121 | enum xt_osf_msg_types { | ||
122 | OSF_MSG_ADD, | ||
123 | OSF_MSG_REMOVE, | ||
124 | OSF_MSG_MAX, | ||
125 | }; | ||
126 | |||
127 | enum xt_osf_attr_type { | ||
128 | OSF_ATTR_UNSPEC, | ||
129 | OSF_ATTR_FINGER, | ||
130 | OSF_ATTR_MAX, | ||
131 | }; | ||
132 | |||
133 | #endif /* _XT_OSF_H */ | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index cb3ad741ebf8..79ba47f042c0 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -917,6 +917,19 @@ config NETFILTER_XT_MATCH_U32 | |||
917 | 917 | ||
918 | Details and examples are in the kernel module source. | 918 | Details and examples are in the kernel module source. |
919 | 919 | ||
920 | config NETFILTER_XT_MATCH_OSF | ||
921 | tristate '"osf" Passive OS fingerprint match' | ||
922 | depends on NETFILTER_ADVANCED && NETFILTER_NETLINK | ||
923 | help | ||
924 | This option selects the Passive OS Fingerprinting match module | ||
925 | that allows to passively match the remote operating system by | ||
926 | analyzing incoming TCP SYN packets. | ||
927 | |||
928 | Rules and loading software can be downloaded from | ||
929 | http://www.ioremap.net/projects/osf | ||
930 | |||
931 | To compile it as a module, choose M here. If unsure, say N. | ||
932 | |||
920 | endif # NETFILTER_XTABLES | 933 | endif # NETFILTER_XTABLES |
921 | 934 | ||
922 | endmenu | 935 | endmenu |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 6282060fbda9..49f62ee4e9ff 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o | |||
77 | obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o | 77 | obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o |
78 | obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o | 78 | obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o |
79 | obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o | 79 | obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o |
80 | obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o | ||
80 | obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o | 81 | obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o |
81 | obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o | 82 | obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o |
82 | obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o | 83 | obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o |
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c new file mode 100644 index 000000000000..863e40977a4d --- /dev/null +++ b/net/netfilter/xt_osf.c | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net> | ||
3 | * | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/kernel.h> | ||
22 | |||
23 | #include <linux/if.h> | ||
24 | #include <linux/inetdevice.h> | ||
25 | #include <linux/ip.h> | ||
26 | #include <linux/list.h> | ||
27 | #include <linux/rculist.h> | ||
28 | #include <linux/skbuff.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/tcp.h> | ||
31 | |||
32 | #include <net/ip.h> | ||
33 | #include <net/tcp.h> | ||
34 | |||
35 | #include <linux/netfilter/nfnetlink.h> | ||
36 | #include <linux/netfilter/x_tables.h> | ||
37 | #include <net/netfilter/nf_log.h> | ||
38 | #include <linux/netfilter/xt_osf.h> | ||
39 | |||
40 | struct xt_osf_finger { | ||
41 | struct rcu_head rcu_head; | ||
42 | struct list_head finger_entry; | ||
43 | struct xt_osf_user_finger finger; | ||
44 | }; | ||
45 | |||
46 | enum osf_fmatch_states { | ||
47 | /* Packet does not match the fingerprint */ | ||
48 | FMATCH_WRONG = 0, | ||
49 | /* Packet matches the fingerprint */ | ||
50 | FMATCH_OK, | ||
51 | /* Options do not match the fingerprint, but header does */ | ||
52 | FMATCH_OPT_WRONG, | ||
53 | }; | ||
54 | |||
55 | /* | ||
56 | * Indexed by dont-fragment bit. | ||
57 | * It is the only constant value in the fingerprint. | ||
58 | */ | ||
59 | static struct list_head xt_osf_fingers[2]; | ||
60 | |||
61 | static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = { | ||
62 | [OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) }, | ||
63 | }; | ||
64 | |||
65 | static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head) | ||
66 | { | ||
67 | struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head); | ||
68 | |||
69 | kfree(f); | ||
70 | } | ||
71 | |||
72 | static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb, | ||
73 | struct nlmsghdr *nlh, struct nlattr *osf_attrs[]) | ||
74 | { | ||
75 | struct xt_osf_user_finger *f; | ||
76 | struct xt_osf_finger *kf = NULL, *sf; | ||
77 | int err = 0; | ||
78 | |||
79 | if (!osf_attrs[OSF_ATTR_FINGER]) | ||
80 | return -EINVAL; | ||
81 | |||
82 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) | ||
83 | return -EINVAL; | ||
84 | |||
85 | f = nla_data(osf_attrs[OSF_ATTR_FINGER]); | ||
86 | |||
87 | kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL); | ||
88 | if (!kf) | ||
89 | return -ENOMEM; | ||
90 | |||
91 | memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger)); | ||
92 | |||
93 | list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) { | ||
94 | if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger))) | ||
95 | continue; | ||
96 | |||
97 | kfree(kf); | ||
98 | kf = NULL; | ||
99 | |||
100 | if (nlh->nlmsg_flags & NLM_F_EXCL) | ||
101 | err = -EEXIST; | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * We are protected by nfnl mutex. | ||
107 | */ | ||
108 | if (kf) | ||
109 | list_add_tail_rcu(&kf->finger_entry, &xt_osf_fingers[!!f->df]); | ||
110 | |||
111 | return err; | ||
112 | } | ||
113 | |||
114 | static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb, | ||
115 | struct nlmsghdr *nlh, struct nlattr *osf_attrs[]) | ||
116 | { | ||
117 | struct xt_osf_user_finger *f; | ||
118 | struct xt_osf_finger *sf; | ||
119 | int err = ENOENT; | ||
120 | |||
121 | if (!osf_attrs[OSF_ATTR_FINGER]) | ||
122 | return -EINVAL; | ||
123 | |||
124 | f = nla_data(osf_attrs[OSF_ATTR_FINGER]); | ||
125 | |||
126 | list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) { | ||
127 | if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger))) | ||
128 | continue; | ||
129 | |||
130 | /* | ||
131 | * We are protected by nfnl mutex. | ||
132 | */ | ||
133 | list_del_rcu(&sf->finger_entry); | ||
134 | call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu); | ||
135 | |||
136 | err = 0; | ||
137 | break; | ||
138 | } | ||
139 | |||
140 | return err; | ||
141 | } | ||
142 | |||
143 | static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = { | ||
144 | [OSF_MSG_ADD] = { | ||
145 | .call = xt_osf_add_callback, | ||
146 | .attr_count = OSF_ATTR_MAX, | ||
147 | .policy = xt_osf_policy, | ||
148 | }, | ||
149 | [OSF_MSG_REMOVE] = { | ||
150 | .call = xt_osf_remove_callback, | ||
151 | .attr_count = OSF_ATTR_MAX, | ||
152 | .policy = xt_osf_policy, | ||
153 | }, | ||
154 | }; | ||
155 | |||
156 | static const struct nfnetlink_subsystem xt_osf_nfnetlink = { | ||
157 | .name = "osf", | ||
158 | .subsys_id = NFNL_SUBSYS_OSF, | ||
159 | .cb_count = OSF_MSG_MAX, | ||
160 | .cb = xt_osf_nfnetlink_callbacks, | ||
161 | }; | ||
162 | |||
163 | static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info, | ||
164 | unsigned char f_ttl) | ||
165 | { | ||
166 | const struct iphdr *ip = ip_hdr(skb); | ||
167 | |||
168 | if (info->flags & XT_OSF_TTL) { | ||
169 | if (info->ttl == XT_OSF_TTL_TRUE) | ||
170 | return ip->ttl == f_ttl; | ||
171 | if (info->ttl == XT_OSF_TTL_NOCHECK) | ||
172 | return 1; | ||
173 | else if (ip->ttl <= f_ttl) | ||
174 | return 1; | ||
175 | else { | ||
176 | struct in_device *in_dev = __in_dev_get_rcu(skb->dev); | ||
177 | int ret = 0; | ||
178 | |||
179 | for_ifa(in_dev) { | ||
180 | if (inet_ifa_match(ip->saddr, ifa)) { | ||
181 | ret = (ip->ttl == f_ttl); | ||
182 | break; | ||
183 | } | ||
184 | } | ||
185 | endfor_ifa(in_dev); | ||
186 | |||
187 | return ret; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | return ip->ttl == f_ttl; | ||
192 | } | ||
193 | |||
194 | static bool xt_osf_match_packet(const struct sk_buff *skb, | ||
195 | const struct xt_match_param *p) | ||
196 | { | ||
197 | const struct xt_osf_info *info = p->matchinfo; | ||
198 | const struct iphdr *ip = ip_hdr(skb); | ||
199 | const struct tcphdr *tcp; | ||
200 | struct tcphdr _tcph; | ||
201 | int fmatch = FMATCH_WRONG, fcount = 0; | ||
202 | unsigned int optsize = 0, check_WSS = 0; | ||
203 | u16 window, totlen, mss = 0; | ||
204 | bool df; | ||
205 | const unsigned char *optp = NULL, *_optp = NULL; | ||
206 | unsigned char opts[MAX_IPOPTLEN]; | ||
207 | const struct xt_osf_finger *kf; | ||
208 | const struct xt_osf_user_finger *f; | ||
209 | |||
210 | if (!info) | ||
211 | return false; | ||
212 | |||
213 | tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph); | ||
214 | if (!tcp) | ||
215 | return false; | ||
216 | |||
217 | if (!tcp->syn) | ||
218 | return false; | ||
219 | |||
220 | totlen = ntohs(ip->tot_len); | ||
221 | df = ntohs(ip->frag_off) & IP_DF; | ||
222 | window = ntohs(tcp->window); | ||
223 | |||
224 | if (tcp->doff * 4 > sizeof(struct tcphdr)) { | ||
225 | optsize = tcp->doff * 4 - sizeof(struct tcphdr); | ||
226 | |||
227 | _optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) + | ||
228 | sizeof(struct tcphdr), optsize, opts); | ||
229 | } | ||
230 | |||
231 | rcu_read_lock(); | ||
232 | list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) { | ||
233 | f = &kf->finger; | ||
234 | |||
235 | if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre)) | ||
236 | continue; | ||
237 | |||
238 | optp = _optp; | ||
239 | fmatch = FMATCH_WRONG; | ||
240 | |||
241 | if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) { | ||
242 | int foptsize, optnum; | ||
243 | |||
244 | /* | ||
245 | * Should not happen if userspace parser was written correctly. | ||
246 | */ | ||
247 | if (f->wss.wc >= OSF_WSS_MAX) | ||
248 | continue; | ||
249 | |||
250 | /* Check options */ | ||
251 | |||
252 | foptsize = 0; | ||
253 | for (optnum = 0; optnum < f->opt_num; ++optnum) | ||
254 | foptsize += f->opt[optnum].length; | ||
255 | |||
256 | if (foptsize > MAX_IPOPTLEN || | ||
257 | optsize > MAX_IPOPTLEN || | ||
258 | optsize != foptsize) | ||
259 | continue; | ||
260 | |||
261 | check_WSS = f->wss.wc; | ||
262 | |||
263 | for (optnum = 0; optnum < f->opt_num; ++optnum) { | ||
264 | if (f->opt[optnum].kind == (*optp)) { | ||
265 | __u32 len = f->opt[optnum].length; | ||
266 | const __u8 *optend = optp + len; | ||
267 | int loop_cont = 0; | ||
268 | |||
269 | fmatch = FMATCH_OK; | ||
270 | |||
271 | switch (*optp) { | ||
272 | case OSFOPT_MSS: | ||
273 | mss = optp[3]; | ||
274 | mss <<= 8; | ||
275 | mss |= optp[2]; | ||
276 | |||
277 | mss = ntohs(mss); | ||
278 | break; | ||
279 | case OSFOPT_TS: | ||
280 | loop_cont = 1; | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | optp = optend; | ||
285 | } else | ||
286 | fmatch = FMATCH_OPT_WRONG; | ||
287 | |||
288 | if (fmatch != FMATCH_OK) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | if (fmatch != FMATCH_OPT_WRONG) { | ||
293 | fmatch = FMATCH_WRONG; | ||
294 | |||
295 | switch (check_WSS) { | ||
296 | case OSF_WSS_PLAIN: | ||
297 | if (f->wss.val == 0 || window == f->wss.val) | ||
298 | fmatch = FMATCH_OK; | ||
299 | break; | ||
300 | case OSF_WSS_MSS: | ||
301 | /* | ||
302 | * Some smart modems decrease mangle MSS to | ||
303 | * SMART_MSS_2, so we check standard, decreased | ||
304 | * and the one provided in the fingerprint MSS | ||
305 | * values. | ||
306 | */ | ||
307 | #define SMART_MSS_1 1460 | ||
308 | #define SMART_MSS_2 1448 | ||
309 | if (window == f->wss.val * mss || | ||
310 | window == f->wss.val * SMART_MSS_1 || | ||
311 | window == f->wss.val * SMART_MSS_2) | ||
312 | fmatch = FMATCH_OK; | ||
313 | break; | ||
314 | case OSF_WSS_MTU: | ||
315 | if (window == f->wss.val * (mss + 40) || | ||
316 | window == f->wss.val * (SMART_MSS_1 + 40) || | ||
317 | window == f->wss.val * (SMART_MSS_2 + 40)) | ||
318 | fmatch = FMATCH_OK; | ||
319 | break; | ||
320 | case OSF_WSS_MODULO: | ||
321 | if ((window % f->wss.val) == 0) | ||
322 | fmatch = FMATCH_OK; | ||
323 | break; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | if (fmatch != FMATCH_OK) | ||
328 | continue; | ||
329 | |||
330 | fcount++; | ||
331 | |||
332 | if (info->flags & XT_OSF_LOG) | ||
333 | nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL, | ||
334 | "%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n", | ||
335 | f->genre, f->version, f->subtype, | ||
336 | &ip->saddr, ntohs(tcp->source), | ||
337 | &ip->daddr, ntohs(tcp->dest), | ||
338 | f->ttl - ip->ttl); | ||
339 | |||
340 | if ((info->flags & XT_OSF_LOG) && | ||
341 | info->loglevel == XT_OSF_LOGLEVEL_FIRST) | ||
342 | break; | ||
343 | } | ||
344 | } | ||
345 | rcu_read_unlock(); | ||
346 | |||
347 | if (!fcount && (info->flags & XT_OSF_LOG)) | ||
348 | nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL, | ||
349 | "Remote OS is not known: %pi4:%u -> %pi4:%u\n", | ||
350 | &ip->saddr, ntohs(tcp->source), | ||
351 | &ip->daddr, ntohs(tcp->dest)); | ||
352 | |||
353 | if (fcount) | ||
354 | fmatch = FMATCH_OK; | ||
355 | |||
356 | return fmatch == FMATCH_OK; | ||
357 | } | ||
358 | |||
359 | static struct xt_match xt_osf_match = { | ||
360 | .name = "osf", | ||
361 | .revision = 0, | ||
362 | .family = NFPROTO_IPV4, | ||
363 | .proto = IPPROTO_TCP, | ||
364 | .hooks = (1 << NF_INET_LOCAL_IN) | | ||
365 | (1 << NF_INET_PRE_ROUTING) | | ||
366 | (1 << NF_INET_FORWARD), | ||
367 | .match = xt_osf_match_packet, | ||
368 | .matchsize = sizeof(struct xt_osf_info), | ||
369 | .me = THIS_MODULE, | ||
370 | }; | ||
371 | |||
372 | static int __init xt_osf_init(void) | ||
373 | { | ||
374 | int err = -EINVAL; | ||
375 | int i; | ||
376 | |||
377 | for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) | ||
378 | INIT_LIST_HEAD(&xt_osf_fingers[i]); | ||
379 | |||
380 | err = nfnetlink_subsys_register(&xt_osf_nfnetlink); | ||
381 | if (err < 0) { | ||
382 | printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err); | ||
383 | goto err_out_exit; | ||
384 | } | ||
385 | |||
386 | err = xt_register_match(&xt_osf_match); | ||
387 | if (err) { | ||
388 | printk(KERN_ERR "Failed (%d) to register OS fingerprint " | ||
389 | "matching module.\n", err); | ||
390 | goto err_out_remove; | ||
391 | } | ||
392 | |||
393 | return 0; | ||
394 | |||
395 | err_out_remove: | ||
396 | nfnetlink_subsys_unregister(&xt_osf_nfnetlink); | ||
397 | err_out_exit: | ||
398 | return err; | ||
399 | } | ||
400 | |||
401 | static void __exit xt_osf_fini(void) | ||
402 | { | ||
403 | struct xt_osf_finger *f; | ||
404 | int i; | ||
405 | |||
406 | nfnetlink_subsys_unregister(&xt_osf_nfnetlink); | ||
407 | xt_unregister_match(&xt_osf_match); | ||
408 | |||
409 | rcu_read_lock(); | ||
410 | for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) { | ||
411 | |||
412 | list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) { | ||
413 | list_del_rcu(&f->finger_entry); | ||
414 | call_rcu(&f->rcu_head, xt_osf_finger_free_rcu); | ||
415 | } | ||
416 | } | ||
417 | rcu_read_unlock(); | ||
418 | |||
419 | rcu_barrier(); | ||
420 | } | ||
421 | |||
422 | module_init(xt_osf_init); | ||
423 | module_exit(xt_osf_fini); | ||
424 | |||
425 | MODULE_LICENSE("GPL"); | ||
426 | MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>"); | ||
427 | MODULE_DESCRIPTION("Passive OS fingerprint matching."); | ||
428 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF); | ||