pf_osfp.c revision 1.1 1 1.1 itojun /* $OpenBSD: pf_osfp.c,v 1.9 2004/01/04 20:08:42 pvalchev Exp $ */
2 1.1 itojun
3 1.1 itojun /*
4 1.1 itojun * Copyright (c) 2003 Mike Frantzen <frantzen (at) w4g.org>
5 1.1 itojun *
6 1.1 itojun * Permission to use, copy, modify, and distribute this software for any
7 1.1 itojun * purpose with or without fee is hereby granted, provided that the above
8 1.1 itojun * copyright notice and this permission notice appear in all copies.
9 1.1 itojun *
10 1.1 itojun * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 itojun * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 itojun * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 itojun * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 itojun * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 itojun * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 itojun * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 itojun *
18 1.1 itojun */
19 1.1 itojun
20 1.1 itojun #include <sys/param.h>
21 1.1 itojun #include <sys/socket.h>
22 1.1 itojun #ifdef _KERNEL
23 1.1 itojun # include <sys/systm.h>
24 1.1 itojun #endif /* _KERNEL */
25 1.1 itojun #include <sys/mbuf.h>
26 1.1 itojun
27 1.1 itojun #include <netinet/in.h>
28 1.1 itojun #include <netinet/in_systm.h>
29 1.1 itojun #include <netinet/ip.h>
30 1.1 itojun #include <netinet/tcp.h>
31 1.1 itojun
32 1.1 itojun #include <net/if.h>
33 1.1 itojun #include <net/pfvar.h>
34 1.1 itojun
35 1.1 itojun #ifdef INET6
36 1.1 itojun #include <netinet/ip6.h>
37 1.1 itojun #endif /* INET6 */
38 1.1 itojun
39 1.1 itojun
40 1.1 itojun #ifdef _KERNEL
41 1.1 itojun # define DPFPRINTF(format, x...) \
42 1.1 itojun if (pf_status.debug >= PF_DEBUG_NOISY) \
43 1.1 itojun printf(format , ##x)
44 1.1 itojun typedef struct pool pool_t;
45 1.1 itojun
46 1.1 itojun #else
47 1.1 itojun /* Userland equivalents so we can lend code to tcpdump et al. */
48 1.1 itojun
49 1.1 itojun # include <arpa/inet.h>
50 1.1 itojun # include <errno.h>
51 1.1 itojun # include <stdio.h>
52 1.1 itojun # include <stdlib.h>
53 1.1 itojun # include <string.h>
54 1.1 itojun # define pool_t int
55 1.1 itojun # define pool_get(pool, flags) malloc(*(pool))
56 1.1 itojun # define pool_put(pool, item) free(item)
57 1.1 itojun # define pool_init(pool, size, a, ao, f, m, p) (*(pool)) = (size)
58 1.1 itojun
59 1.1 itojun # ifdef PFDEBUG
60 1.1 itojun # include <sys/stdarg.h>
61 1.1 itojun # define DPFPRINTF(format, x...) fprintf(stderr, format , ##x)
62 1.1 itojun # else
63 1.1 itojun # define DPFPRINTF(format, x...) ((void)0)
64 1.1 itojun # endif /* PFDEBUG */
65 1.1 itojun #endif /* _KERNEL */
66 1.1 itojun
67 1.1 itojun
68 1.1 itojun SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list;
69 1.1 itojun pool_t pf_osfp_entry_pl;
70 1.1 itojun pool_t pf_osfp_pl;
71 1.1 itojun
72 1.1 itojun struct pf_os_fingerprint *pf_osfp_find(struct pf_osfp_list *,
73 1.1 itojun struct pf_os_fingerprint *, u_int8_t);
74 1.1 itojun struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_osfp_list *,
75 1.1 itojun struct pf_os_fingerprint *);
76 1.1 itojun void pf_osfp_insert(struct pf_osfp_list *,
77 1.1 itojun struct pf_os_fingerprint *);
78 1.1 itojun
79 1.1 itojun
80 1.1 itojun #ifdef _KERNEL
81 1.1 itojun /*
82 1.1 itojun * Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
83 1.1 itojun * Returns the list of possible OSes.
84 1.1 itojun */
85 1.1 itojun struct pf_osfp_enlist *
86 1.1 itojun pf_osfp_fingerprint(struct pf_pdesc *pd, struct mbuf *m, int off,
87 1.1 itojun const struct tcphdr *tcp)
88 1.1 itojun {
89 1.1 itojun struct ip *ip;
90 1.1 itojun char hdr[60];
91 1.1 itojun
92 1.1 itojun /* XXX don't have a fingerprint database for IPv6 :-( */
93 1.1 itojun if (pd->af != PF_INET || pd->proto != IPPROTO_TCP || (tcp->th_off << 2)
94 1.1 itojun < sizeof(*tcp))
95 1.1 itojun return (NULL);
96 1.1 itojun
97 1.1 itojun ip = mtod(m, struct ip *);
98 1.1 itojun if (!pf_pull_hdr(m, off, hdr, tcp->th_off << 2, NULL, NULL, pd->af))
99 1.1 itojun return (NULL);
100 1.1 itojun
101 1.1 itojun return (pf_osfp_fingerprint_hdr(ip, (struct tcphdr *)hdr));
102 1.1 itojun }
103 1.1 itojun #endif /* _KERNEL */
104 1.1 itojun
105 1.1 itojun struct pf_osfp_enlist *
106 1.1 itojun pf_osfp_fingerprint_hdr(const struct ip *ip, const struct tcphdr *tcp)
107 1.1 itojun {
108 1.1 itojun struct pf_os_fingerprint fp, *fpresult;
109 1.1 itojun int cnt, optlen = 0;
110 1.1 itojun const u_int8_t *optp;
111 1.1 itojun
112 1.1 itojun if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN || (ip->ip_off &
113 1.1 itojun htons(IP_OFFMASK)))
114 1.1 itojun return (NULL);
115 1.1 itojun
116 1.1 itojun memset(&fp, 0, sizeof(fp));
117 1.1 itojun
118 1.1 itojun fp.fp_psize = ntohs(ip->ip_len);
119 1.1 itojun fp.fp_ttl = ip->ip_ttl;
120 1.1 itojun if (ip->ip_off & htons(IP_DF))
121 1.1 itojun fp.fp_flags |= PF_OSFP_DF;
122 1.1 itojun fp.fp_wsize = ntohs(tcp->th_win);
123 1.1 itojun
124 1.1 itojun
125 1.1 itojun cnt = (tcp->th_off << 2) - sizeof(*tcp);
126 1.1 itojun optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
127 1.1 itojun for (; cnt > 0; cnt -= optlen, optp += optlen) {
128 1.1 itojun if (*optp == TCPOPT_EOL)
129 1.1 itojun break;
130 1.1 itojun
131 1.1 itojun fp.fp_optcnt++;
132 1.1 itojun if (*optp == TCPOPT_NOP) {
133 1.1 itojun fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
134 1.1 itojun PF_OSFP_TCPOPT_NOP;
135 1.1 itojun optlen = 1;
136 1.1 itojun } else {
137 1.1 itojun if (cnt < 2)
138 1.1 itojun return (NULL);
139 1.1 itojun optlen = optp[1];
140 1.1 itojun if (optlen > cnt || optlen < 2)
141 1.1 itojun return (NULL);
142 1.1 itojun switch (*optp) {
143 1.1 itojun case TCPOPT_MAXSEG:
144 1.1 itojun if (optlen >= TCPOLEN_MAXSEG)
145 1.1 itojun memcpy(&fp.fp_mss, &optp[2],
146 1.1 itojun sizeof(fp.fp_mss));
147 1.1 itojun fp.fp_tcpopts = (fp.fp_tcpopts <<
148 1.1 itojun PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
149 1.1 itojun NTOHS(fp.fp_mss);
150 1.1 itojun break;
151 1.1 itojun case TCPOPT_WINDOW:
152 1.1 itojun if (optlen >= TCPOLEN_WINDOW)
153 1.1 itojun memcpy(&fp.fp_wscale, &optp[2],
154 1.1 itojun sizeof(fp.fp_wscale));
155 1.1 itojun NTOHS(fp.fp_wscale);
156 1.1 itojun fp.fp_tcpopts = (fp.fp_tcpopts <<
157 1.1 itojun PF_OSFP_TCPOPT_BITS) |
158 1.1 itojun PF_OSFP_TCPOPT_WSCALE;
159 1.1 itojun break;
160 1.1 itojun case TCPOPT_SACK_PERMITTED:
161 1.1 itojun fp.fp_tcpopts = (fp.fp_tcpopts <<
162 1.1 itojun PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
163 1.1 itojun break;
164 1.1 itojun case TCPOPT_TIMESTAMP:
165 1.1 itojun if (optlen >= TCPOLEN_TIMESTAMP) {
166 1.1 itojun u_int32_t ts;
167 1.1 itojun memcpy(&ts, &optp[2], sizeof(ts));
168 1.1 itojun if (ts == 0)
169 1.1 itojun fp.fp_flags |= PF_OSFP_TS0;
170 1.1 itojun
171 1.1 itojun }
172 1.1 itojun fp.fp_tcpopts = (fp.fp_tcpopts <<
173 1.1 itojun PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
174 1.1 itojun break;
175 1.1 itojun default:
176 1.1 itojun return (NULL);
177 1.1 itojun }
178 1.1 itojun }
179 1.1 itojun optlen = MAX(optlen, 1); /* paranoia */
180 1.1 itojun }
181 1.1 itojun
182 1.1 itojun DPFPRINTF("fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) "
183 1.1 itojun "(TS=%s,M=%s%d,W=%s%d)\n",
184 1.1 itojun inet_ntoa(ip->ip_src), ntohs(tcp->th_sport),
185 1.1 itojun fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
186 1.1 itojun fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
187 1.1 itojun (fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
188 1.1 itojun (fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
189 1.1 itojun (fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
190 1.1 itojun fp.fp_mss,
191 1.1 itojun (fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
192 1.1 itojun (fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
193 1.1 itojun fp.fp_wscale);
194 1.1 itojun
195 1.1 itojun if ((fpresult = pf_osfp_find(&pf_osfp_list, &fp,
196 1.1 itojun PF_OSFP_MAXTTL_OFFSET)))
197 1.1 itojun return (&fpresult->fp_oses);
198 1.1 itojun return (NULL);
199 1.1 itojun }
200 1.1 itojun
201 1.1 itojun /* Match a fingerprint ID against a list of OSes */
202 1.1 itojun int
203 1.1 itojun pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
204 1.1 itojun {
205 1.1 itojun struct pf_osfp_entry *entry;
206 1.1 itojun int os_class, os_version, os_subtype;
207 1.1 itojun int en_class, en_version, en_subtype;
208 1.1 itojun
209 1.1 itojun if (os == PF_OSFP_ANY)
210 1.1 itojun return (1);
211 1.1 itojun if (list == NULL) {
212 1.1 itojun DPFPRINTF("osfp no match against %x\n", os);
213 1.1 itojun return (os == PF_OSFP_UNKNOWN);
214 1.1 itojun }
215 1.1 itojun PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
216 1.1 itojun SLIST_FOREACH(entry, list, fp_entry) {
217 1.1 itojun PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
218 1.1 itojun if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
219 1.1 itojun (os_version == PF_OSFP_ANY || en_version == os_version) &&
220 1.1 itojun (os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
221 1.1 itojun DPFPRINTF("osfp matched %s %s %s %x==%x\n",
222 1.1 itojun entry->fp_class_nm, entry->fp_version_nm,
223 1.1 itojun entry->fp_subtype_nm, os, entry->fp_os);
224 1.1 itojun return (1);
225 1.1 itojun }
226 1.1 itojun }
227 1.1 itojun DPFPRINTF("fingerprint 0x%x didn't match\n", os);
228 1.1 itojun return (0);
229 1.1 itojun }
230 1.1 itojun
231 1.1 itojun /* Initialize the OS fingerprint system */
232 1.1 itojun void
233 1.1 itojun pf_osfp_initialize(void)
234 1.1 itojun {
235 1.1 itojun pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0, 0, 0,
236 1.1 itojun "pfosfpen", NULL);
237 1.1 itojun pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0, 0, 0,
238 1.1 itojun "pfosfp", NULL);
239 1.1 itojun SLIST_INIT(&pf_osfp_list);
240 1.1 itojun }
241 1.1 itojun
242 1.1 itojun /* Flush the fingerprint list */
243 1.1 itojun void
244 1.1 itojun pf_osfp_flush(void)
245 1.1 itojun {
246 1.1 itojun struct pf_os_fingerprint *fp;
247 1.1 itojun struct pf_osfp_entry *entry;
248 1.1 itojun
249 1.1 itojun while ((fp = SLIST_FIRST(&pf_osfp_list))) {
250 1.1 itojun SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
251 1.1 itojun while ((entry = SLIST_FIRST(&fp->fp_oses))) {
252 1.1 itojun SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
253 1.1 itojun pool_put(&pf_osfp_entry_pl, entry);
254 1.1 itojun }
255 1.1 itojun pool_put(&pf_osfp_pl, fp);
256 1.1 itojun }
257 1.1 itojun }
258 1.1 itojun
259 1.1 itojun
260 1.1 itojun /* Add a fingerprint */
261 1.1 itojun int
262 1.1 itojun pf_osfp_add(struct pf_osfp_ioctl *fpioc)
263 1.1 itojun {
264 1.1 itojun struct pf_os_fingerprint *fp, fpadd;
265 1.1 itojun struct pf_osfp_entry *entry;
266 1.1 itojun
267 1.1 itojun memset(&fpadd, 0, sizeof(fpadd));
268 1.1 itojun fpadd.fp_tcpopts = fpioc->fp_tcpopts;
269 1.1 itojun fpadd.fp_wsize = fpioc->fp_wsize;
270 1.1 itojun fpadd.fp_psize = fpioc->fp_psize;
271 1.1 itojun fpadd.fp_mss = fpioc->fp_mss;
272 1.1 itojun fpadd.fp_flags = fpioc->fp_flags;
273 1.1 itojun fpadd.fp_optcnt = fpioc->fp_optcnt;
274 1.1 itojun fpadd.fp_wscale = fpioc->fp_wscale;
275 1.1 itojun fpadd.fp_ttl = fpioc->fp_ttl;
276 1.1 itojun
277 1.1 itojun DPFPRINTF("adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
278 1.1 itojun "(TS=%s,M=%s%d,W=%s%d) %x\n",
279 1.1 itojun fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
280 1.1 itojun fpioc->fp_os.fp_subtype_nm,
281 1.1 itojun (fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
282 1.1 itojun (fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
283 1.1 itojun (fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
284 1.1 itojun (fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
285 1.1 itojun fpadd.fp_wsize,
286 1.1 itojun fpadd.fp_ttl,
287 1.1 itojun (fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
288 1.1 itojun (fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
289 1.1 itojun (fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
290 1.1 itojun fpadd.fp_psize,
291 1.1 itojun (long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
292 1.1 itojun (fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
293 1.1 itojun (fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
294 1.1 itojun (fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
295 1.1 itojun fpadd.fp_mss,
296 1.1 itojun (fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
297 1.1 itojun (fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
298 1.1 itojun fpadd.fp_wscale,
299 1.1 itojun fpioc->fp_os.fp_os);
300 1.1 itojun
301 1.1 itojun
302 1.1 itojun if ((fp = pf_osfp_find_exact(&pf_osfp_list, &fpadd))) {
303 1.1 itojun SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
304 1.1 itojun if (PF_OSFP_ENTRY_EQ(entry, &fpioc->fp_os))
305 1.1 itojun return (EEXIST);
306 1.1 itojun }
307 1.1 itojun if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL)
308 1.1 itojun return (ENOMEM);
309 1.1 itojun } else {
310 1.1 itojun if ((fp = pool_get(&pf_osfp_pl, PR_NOWAIT)) == NULL)
311 1.1 itojun return (ENOMEM);
312 1.1 itojun memset(fp, 0, sizeof(*fp));
313 1.1 itojun fp->fp_tcpopts = fpioc->fp_tcpopts;
314 1.1 itojun fp->fp_wsize = fpioc->fp_wsize;
315 1.1 itojun fp->fp_psize = fpioc->fp_psize;
316 1.1 itojun fp->fp_mss = fpioc->fp_mss;
317 1.1 itojun fp->fp_flags = fpioc->fp_flags;
318 1.1 itojun fp->fp_optcnt = fpioc->fp_optcnt;
319 1.1 itojun fp->fp_wscale = fpioc->fp_wscale;
320 1.1 itojun fp->fp_ttl = fpioc->fp_ttl;
321 1.1 itojun SLIST_INIT(&fp->fp_oses);
322 1.1 itojun if ((entry = pool_get(&pf_osfp_entry_pl, PR_NOWAIT)) == NULL) {
323 1.1 itojun pool_put(&pf_osfp_pl, fp);
324 1.1 itojun return (ENOMEM);
325 1.1 itojun }
326 1.1 itojun pf_osfp_insert(&pf_osfp_list, fp);
327 1.1 itojun }
328 1.1 itojun memcpy(entry, &fpioc->fp_os, sizeof(*entry));
329 1.1 itojun
330 1.1 itojun /* Make sure the strings are NUL terminated */
331 1.1 itojun entry->fp_class_nm[sizeof(entry->fp_class_nm)-1] = '\0';
332 1.1 itojun entry->fp_version_nm[sizeof(entry->fp_version_nm)-1] = '\0';
333 1.1 itojun entry->fp_subtype_nm[sizeof(entry->fp_subtype_nm)-1] = '\0';
334 1.1 itojun
335 1.1 itojun SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
336 1.1 itojun
337 1.1 itojun #ifdef PFDEBUG
338 1.1 itojun if ((fp = pf_osfp_validate()))
339 1.1 itojun printf("Invalid fingerprint list\n");
340 1.1 itojun #endif /* PFDEBUG */
341 1.1 itojun return (0);
342 1.1 itojun }
343 1.1 itojun
344 1.1 itojun
345 1.1 itojun /* Find a fingerprint in the list */
346 1.1 itojun struct pf_os_fingerprint *
347 1.1 itojun pf_osfp_find(struct pf_osfp_list *list, struct pf_os_fingerprint *find,
348 1.1 itojun u_int8_t ttldiff)
349 1.1 itojun {
350 1.1 itojun struct pf_os_fingerprint *f;
351 1.1 itojun
352 1.1 itojun #define MATCH_INT(_MOD, _DC, _field) \
353 1.1 itojun if ((f->fp_flags & _DC) == 0) { \
354 1.1 itojun if ((f->fp_flags & _MOD) == 0) { \
355 1.1 itojun if (f->_field != find->_field) \
356 1.1 itojun continue; \
357 1.1 itojun } else { \
358 1.1 itojun if (f->_field == 0 || find->_field % f->_field) \
359 1.1 itojun continue; \
360 1.1 itojun } \
361 1.1 itojun }
362 1.1 itojun
363 1.1 itojun SLIST_FOREACH(f, list, fp_next) {
364 1.1 itojun if (f->fp_tcpopts != find->fp_tcpopts ||
365 1.1 itojun f->fp_optcnt != find->fp_optcnt ||
366 1.1 itojun f->fp_ttl < find->fp_ttl ||
367 1.1 itojun f->fp_ttl - find->fp_ttl > ttldiff ||
368 1.1 itojun (f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
369 1.1 itojun (find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
370 1.1 itojun continue;
371 1.1 itojun
372 1.1 itojun MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
373 1.1 itojun MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
374 1.1 itojun MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
375 1.1 itojun if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
376 1.1 itojun if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
377 1.1 itojun if (find->fp_mss == 0)
378 1.1 itojun continue;
379 1.1 itojun
380 1.1 itojun /* Some "smart" NAT devices and DSL routers will tweak the MSS size and
381 1.1 itojun * will set it to whatever is suitable for the link type.
382 1.1 itojun */
383 1.1 itojun #define SMART_MSS 1460
384 1.1 itojun if ((find->fp_wsize % find->fp_mss ||
385 1.1 itojun find->fp_wsize / find->fp_mss !=
386 1.1 itojun f->fp_wsize) &&
387 1.1 itojun (find->fp_wsize % SMART_MSS ||
388 1.1 itojun find->fp_wsize / SMART_MSS !=
389 1.1 itojun f->fp_wsize))
390 1.1 itojun continue;
391 1.1 itojun } else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
392 1.1 itojun if (find->fp_mss == 0)
393 1.1 itojun continue;
394 1.1 itojun
395 1.1 itojun #define MTUOFF (sizeof(struct ip) + sizeof(struct tcphdr))
396 1.1 itojun #define SMART_MTU (SMART_MSS + MTUOFF)
397 1.1 itojun if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
398 1.1 itojun find->fp_wsize / (find->fp_mss + MTUOFF) !=
399 1.1 itojun f->fp_wsize) &&
400 1.1 itojun (find->fp_wsize % SMART_MTU ||
401 1.1 itojun find->fp_wsize / SMART_MTU !=
402 1.1 itojun f->fp_wsize))
403 1.1 itojun continue;
404 1.1 itojun } else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
405 1.1 itojun if (f->fp_wsize == 0 || find->fp_wsize %
406 1.1 itojun f->fp_wsize)
407 1.1 itojun continue;
408 1.1 itojun } else {
409 1.1 itojun if (f->fp_wsize != find->fp_wsize)
410 1.1 itojun continue;
411 1.1 itojun }
412 1.1 itojun }
413 1.1 itojun return (f);
414 1.1 itojun }
415 1.1 itojun
416 1.1 itojun return (NULL);
417 1.1 itojun }
418 1.1 itojun
419 1.1 itojun /* Find an exact fingerprint in the list */
420 1.1 itojun struct pf_os_fingerprint *
421 1.1 itojun pf_osfp_find_exact(struct pf_osfp_list *list, struct pf_os_fingerprint *find)
422 1.1 itojun {
423 1.1 itojun struct pf_os_fingerprint *f;
424 1.1 itojun
425 1.1 itojun SLIST_FOREACH(f, list, fp_next) {
426 1.1 itojun if (f->fp_tcpopts == find->fp_tcpopts &&
427 1.1 itojun f->fp_wsize == find->fp_wsize &&
428 1.1 itojun f->fp_psize == find->fp_psize &&
429 1.1 itojun f->fp_mss == find->fp_mss &&
430 1.1 itojun f->fp_flags == find->fp_flags &&
431 1.1 itojun f->fp_optcnt == find->fp_optcnt &&
432 1.1 itojun f->fp_wscale == find->fp_wscale &&
433 1.1 itojun f->fp_ttl == find->fp_ttl)
434 1.1 itojun return (f);
435 1.1 itojun }
436 1.1 itojun
437 1.1 itojun return (NULL);
438 1.1 itojun }
439 1.1 itojun
440 1.1 itojun /* Insert a fingerprint into the list */
441 1.1 itojun void
442 1.1 itojun pf_osfp_insert(struct pf_osfp_list *list, struct pf_os_fingerprint *ins)
443 1.1 itojun {
444 1.1 itojun struct pf_os_fingerprint *f, *prev = NULL;
445 1.1 itojun
446 1.1 itojun /* XXX need to go semi tree based. can key on tcp options */
447 1.1 itojun
448 1.1 itojun SLIST_FOREACH(f, list, fp_next)
449 1.1 itojun prev = f;
450 1.1 itojun if (prev)
451 1.1 itojun SLIST_INSERT_AFTER(prev, ins, fp_next);
452 1.1 itojun else
453 1.1 itojun SLIST_INSERT_HEAD(list, ins, fp_next);
454 1.1 itojun }
455 1.1 itojun
456 1.1 itojun /* Fill a fingerprint by its number (from an ioctl) */
457 1.1 itojun int
458 1.1 itojun pf_osfp_get(struct pf_osfp_ioctl *fpioc)
459 1.1 itojun {
460 1.1 itojun struct pf_os_fingerprint *fp;
461 1.1 itojun struct pf_osfp_entry *entry;
462 1.1 itojun int num = fpioc->fp_getnum;
463 1.1 itojun int i = 0;
464 1.1 itojun
465 1.1 itojun
466 1.1 itojun memset(fpioc, 0, sizeof(*fpioc));
467 1.1 itojun SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
468 1.1 itojun SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
469 1.1 itojun if (i++ == num) {
470 1.1 itojun fpioc->fp_mss = fp->fp_mss;
471 1.1 itojun fpioc->fp_wsize = fp->fp_wsize;
472 1.1 itojun fpioc->fp_flags = fp->fp_flags;
473 1.1 itojun fpioc->fp_psize = fp->fp_psize;
474 1.1 itojun fpioc->fp_ttl = fp->fp_ttl;
475 1.1 itojun fpioc->fp_wscale = fp->fp_wscale;
476 1.1 itojun fpioc->fp_getnum = num;
477 1.1 itojun memcpy(&fpioc->fp_os, entry,
478 1.1 itojun sizeof(fpioc->fp_os));
479 1.1 itojun return (0);
480 1.1 itojun }
481 1.1 itojun }
482 1.1 itojun }
483 1.1 itojun
484 1.1 itojun return (EBUSY);
485 1.1 itojun }
486 1.1 itojun
487 1.1 itojun
488 1.1 itojun /* Validate that each signature is reachable */
489 1.1 itojun struct pf_os_fingerprint *
490 1.1 itojun pf_osfp_validate(void)
491 1.1 itojun {
492 1.1 itojun struct pf_os_fingerprint *f, *f2, find;
493 1.1 itojun
494 1.1 itojun SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
495 1.1 itojun memcpy(&find, f, sizeof(find));
496 1.1 itojun
497 1.1 itojun /* We do a few MSS/th_win percolations to make things unique */
498 1.1 itojun if (find.fp_mss == 0)
499 1.1 itojun find.fp_mss = 128;
500 1.1 itojun if (f->fp_flags & PF_OSFP_WSIZE_MSS)
501 1.1 itojun find.fp_wsize *= find.fp_mss, 1;
502 1.1 itojun else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
503 1.1 itojun find.fp_wsize *= (find.fp_mss + 40);
504 1.1 itojun else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
505 1.1 itojun find.fp_wsize *= 2;
506 1.1 itojun if (f != (f2 = pf_osfp_find(&pf_osfp_list, &find, 0))) {
507 1.1 itojun if (f2)
508 1.1 itojun printf("Found \"%s %s %s\" instead of "
509 1.1 itojun "\"%s %s %s\"\n",
510 1.1 itojun SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
511 1.1 itojun SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
512 1.1 itojun SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
513 1.1 itojun SLIST_FIRST(&f->fp_oses)->fp_class_nm,
514 1.1 itojun SLIST_FIRST(&f->fp_oses)->fp_version_nm,
515 1.1 itojun SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
516 1.1 itojun else
517 1.1 itojun printf("Couldn't find \"%s %s %s\"\n",
518 1.1 itojun SLIST_FIRST(&f->fp_oses)->fp_class_nm,
519 1.1 itojun SLIST_FIRST(&f->fp_oses)->fp_version_nm,
520 1.1 itojun SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
521 1.1 itojun return (f);
522 1.1 itojun }
523 1.1 itojun }
524 1.1 itojun return (NULL);
525 1.1 itojun }
526