netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Passive OS fingerprinting.
@ 2008-07-01 11:39 Evgeniy Polyakov
  2008-07-01 11:53 ` Patrick McHardy
  2008-07-01 19:56 ` Paul E. McKenney
  0 siblings, 2 replies; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 11:39 UTC (permalink / raw)
  To: netdev; +Cc: netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 1373 bytes --]

Hi.

Passive OS fingerprinting iptables (xtables) allows to match incoming
packets by different sets of SYN-packet and determine, which remote
system is on the remote end, so you can make decisions based on OS
type and even version at some degreee and perform various netfilter
actions based on that knowledge.

This module compares some data (WS, MSS, options and it's order, ttl, df
and others) from packets with SYN bit set with dynamically loaded OS
fingerprints.

This version existed quite for a while in patch-o-matic(-ng), but
suddenly was dropped and then only was updated on its own repo:
http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf

I've updated OSF to match new iptables standards (namely xtables
support) and present new kernelspace and userspace library files in
attach.

To setup single rule, which will drop and log all Linux incoming
access one needs to do following steps:
# insmod ./ipt_osf.ko
# ./load ./pf.os /proc/sys/net/ipv4/osf
# iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
--ttl 2 --connector

And you will find following lines in dmesg:

ipt_osf: Linux [2.5-2.6::Linux 2.5/2.6] : aa:aa:aa:aa:32885 -> bb:bb:bb:bb:23 hops=3

More info can be found on homepage:
http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf

Enjoy!

Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>

-- 
	Evgeniy Polyakov

[-- Attachment #2: ipt_osf.c --]
[-- Type: text/plain, Size: 18961 bytes --]

/*
 * ipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/types.h>
#include <linux/string.h>
#include <linux/smp.h>
#include <linux/skbuff.h>
#include <linux/file.h>
#include <linux/ip.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/ctype.h>
#include <linux/list.h>
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <net/ip.h>
#include <linux/tcp.h>

#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include "ipt_osf.h"

#define OSF_DEBUG

#ifdef OSF_DEBUG
#define log(x...) 		printk(KERN_INFO "ipt_osf: " x)
#define loga(x...) 		printk(x)
#else
#define log(x...) 		do {} while(0)
#define loga(x...) 		do {} while(0)
#endif

#define FMATCH_WRONG		0
#define FMATCH_OK		1
#define FMATCH_OPT_WRONG	2

#define OPTDEL			','
#define OSFPDEL 		':'
#define MAXOPTSTRLEN		128
#define OSFFLUSH		"FLUSH"

static DEFINE_SPINLOCK(ipt_osf_lock);
static LIST_HEAD(ipt_finger_list);

#ifdef CONFIG_CONNECTOR
#include <linux/connector.h>

/*
 * They should live in connector.h.
 */
#define CN_IDX_OSF		0x0008
#define CN_VAL_OSF		0x0000

static char osf_finger_buf[sizeof(struct ipt_osf_nlmsg) +
			   sizeof(struct cn_msg)];
static struct cb_id osf_id = { CN_IDX_OSF, CN_VAL_OSF };
static u32 osf_seq;

static void ipt_osf_send_connector(struct ipt_osf_finger *f,
				   const struct sk_buff *sk)
{
	struct cn_msg *m;
	struct ipt_osf_nlmsg *data;

	m = (struct cn_msg *)osf_finger_buf;
	data = (struct ipt_osf_nlmsg *)(m + 1);

	memcpy(&m->id, &osf_id, sizeof(m->id));
	m->seq = osf_seq++;
	m->ack = 0;
	m->len = sizeof(*data);

	memcpy(&data->f, f, sizeof(struct ipt_osf_finger));
	memcpy(&data->ip, sk->nh.iph, sizeof(struct iphdr));
	memcpy(&data->tcp,
	       (struct tcphdr *)((u32 *)sk->nh.iph + sk->nh.iph->ihl),
	       sizeof(struct tcphdr));

	cn_netlink_send(m, m->id.idx, GFP_ATOMIC);
}
#else
static void ipt_osf_send_connector(struct ipt_osf_finger *f,
				   const struct sk_buff *sk)
{
}
#endif

static char *ipt_osf_strchr(char *ptr, char c)
{
	char *tmp;

	tmp = strchr(ptr, c);

	while (tmp && tmp + 1 && isspace(*(tmp + 1)))
		tmp++;

	return tmp;
}

static struct ipt_osf_finger *ipt_osf_finger_alloc(void)
{
	return kzalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
}

static void ipt_osf_finger_free(struct ipt_osf_finger *f)
{
	memset(f, 0, sizeof(struct ipt_osf_finger));
	kfree(f);
}

static void ipt_osf_parse_opt(struct ipt_osf_opt *opt, int *optnum, char *obuf,
			  int olen)
{
	int i, op;
	char *ptr, wc;
	unsigned long val;

	ptr = &obuf[0];
	i = 0;
	while (ptr != NULL && i < olen) {
		val = 0;
		op = 0;
		wc = 0;
		switch (obuf[i]) {
		case 'N':
			op = OSFOPT_NOP;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				*ptr = '\0';
				ptr++;
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		case 'S':
			op = OSFOPT_SACKP;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				*ptr = '\0';
				ptr++;
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		case 'T':
			op = OSFOPT_TS;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				*ptr = '\0';
				ptr++;
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		case 'W':
			op = OSFOPT_WSO;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				switch (obuf[i + 1]) {
				case '%':
					wc = '%';
					break;
				case 'S':
					wc = 'S';
					break;
				case 'T':
					wc = 'T';
					break;
				default:
					wc = 0;
					break;
				}

				*ptr = '\0';
				ptr++;
				if (wc)
					val = simple_strtoul(&obuf[i + 2], 
							NULL, 10);
				else
					val = simple_strtoul(&obuf[i + 1], 
							NULL, 10);
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		case 'M':
			op = OSFOPT_MSS;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				if (obuf[i + 1] == '%')
					wc = '%';
				*ptr = '\0';
				ptr++;
				if (wc)
					val = simple_strtoul(&obuf[i + 2], 
							NULL, 10);
				else
					val = simple_strtoul(&obuf[i + 1], 
							NULL, 10);
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		case 'E':
			op = OSFOPT_EOL;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				*ptr = '\0';
				ptr++;
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		default:
			op = OSFOPT_EMPTY;
			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
			if (ptr) {
				ptr++;
				i += (int)(ptr - &obuf[i]);

			} else
				i++;
			break;
		}

		if (op != OSFOPT_EMPTY) {
			opt[*optnum].kind = IANA_opts[op].kind;
			opt[*optnum].length = IANA_opts[op].length;
			opt[*optnum].wc.wc = wc;
			opt[*optnum].wc.val = val;
			(*optnum)++;
		}
	}
}

static int ipt_osf_proc_read(char *buf, char **start, off_t off, int count,
			 int *eof, void *data)
{
	struct ipt_osf_finger *f = NULL;
	int i, __count, err;

	*eof = 1;
	__count = count;
	count = 0;

	rcu_read_lock();
	list_for_each_entry(f, &ipt_finger_list, flist) {
		log("%s [%s]", f->genre, f->details);

		err = snprintf(buf + count, __count - count, "%s - %s[%s] : %s",
			       f->genre, f->version, f->subtype, f->details);
		if (err == 0 || __count <= count + err)
			break;
		else
			count += err;
		if (f->opt_num) {
			loga(" OPT: ");
			/* count += sprintf(buf+count, " OPT: "); */
			for (i = 0; i < f->opt_num; ++i) {
				/*
				count += sprintf(buf+count, "%d.%c%u; ", 
					f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', 
					f->opt[i].wc.val);
				*/
				loga("%d.%c%u; ",
				     f->opt[i].kind,
				     (f->opt[i].wc.wc) ? f->opt[i].wc.wc : ' ',
				     f->opt[i].wc.val);
			}
		}
		loga("\n");
		err = snprintf(buf + count, __count - count, "\n");
		if (err == 0 || __count <= count + err)
			break;
		else
			count += err;
	}
	rcu_read_unlock();

	return count;
}

static int ipt_osf_proc_write(struct file *file, const char *buffer,
			  unsigned long count, void *data)
{
	int cnt, i;
	char obuf[MAXOPTSTRLEN];
	struct ipt_osf_finger *finger, *n;
	char *pbeg, *pend;

	if (count > 0xffff)
		return -E2BIG;

	if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) {
		int i = 0;
		synchronize_rcu();
		spin_lock_bh(&ipt_osf_lock);
		list_for_each_entry_safe(finger, n, &ipt_finger_list, flist) {
			i++;
			list_del_rcu(&finger->flist);
			ipt_osf_finger_free(finger);
		}
		spin_unlock_bh(&ipt_osf_lock);

		log("Flushed %d entries.\n", i);

		return count;
	}

	cnt = 0;
	for (i = 0; i < count && buffer[i] != '\0'; ++i)
		if (buffer[i] == ':')
			cnt++;

	if (cnt != 8 || i != count) {
		log("Wrong input line cnt=%d[8], len=%u[%lu]\n",
		    cnt, i, count);
		return count;
	}

	memset(obuf, 0, sizeof(obuf));

	finger = ipt_osf_finger_alloc();
	if (!finger) {
		log("Failed to allocate new fingerprint entry.\n");
		return -ENOMEM;
	}

	pbeg = (char *)buffer;
	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		if (pbeg[0] == 'S') {
			finger->wss.wc = 'S';
			if (pbeg[1] == '%')
				finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
			else if (pbeg[1] == '*')
				finger->wss.val = 0;
			else
				finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
		} else if (pbeg[0] == 'T') {
			finger->wss.wc = 'T';
			if (pbeg[1] == '%')
				finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
			else if (pbeg[1] == '*')
				finger->wss.val = 0;
			else
				finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
		} else if (pbeg[0] == '%') {
			finger->wss.wc = '%';
			finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
		} else if (isdigit(pbeg[0])) {
			finger->wss.wc = 0;
			finger->wss.val = simple_strtoul(&pbeg[0], NULL, 10);
		}

		pbeg = pend + 1;
	}
	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		finger->ttl = simple_strtoul(pbeg, NULL, 10);
		pbeg = pend + 1;
	}
	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		finger->df = simple_strtoul(pbeg, NULL, 10);
		pbeg = pend + 1;
	}
	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		finger->ss = simple_strtoul(pbeg, NULL, 10);
		pbeg = pend + 1;
	}

	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
		pbeg = pend + 1;
	}

	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		if (pbeg[0] == '@' || pbeg[0] == '*')
			cnt = snprintf(finger->genre, sizeof(finger->genre), 
					"%s", pbeg + 1);
		else
			cnt = snprintf(finger->genre, sizeof(finger->genre), 
					"%s", pbeg);
		pbeg = pend + 1;
	}

	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		cnt =
		    snprintf(finger->version, sizeof(finger->version), "%s",
			     pbeg);
		pbeg = pend + 1;
	}

	pend = ipt_osf_strchr(pbeg, OSFPDEL);
	if (pend) {
		*pend = '\0';
		cnt =
		    snprintf(finger->subtype, sizeof(finger->subtype), "%s",
			     pbeg);
		pbeg = pend + 1;
	}

	cnt = snprintf(finger->details,
		       ((count - (pbeg - buffer) + 1) >
			MAXDETLEN) ? MAXDETLEN : (count - (pbeg - buffer) + 1),
		       "%s", pbeg);

	log("%s - %s[%s] : %s\n",
	    finger->genre, finger->version, finger->subtype, finger->details);

	ipt_osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));

	synchronize_rcu();
	spin_lock_bh(&ipt_osf_lock);
	list_add_tail_rcu(&finger->flist, &ipt_finger_list);
	spin_unlock_bh(&ipt_osf_lock);

	return count;
}

static inline int ipt_osf_ttl(const struct sk_buff *skb, struct ipt_osf_info *info,
			    unsigned char f_ttl)
{
	struct iphdr *ip = ip_hdr(skb);
#if 0
	log("f_ttl: %u, ip_ttl: %u, info->ttl: %u, flags_ttl: %u.\n",
			f_ttl, ip->ttl, info->ttl, info->flags & IPT_OSF_TTL);
#endif
	if (info->flags & IPT_OSF_TTL) {
		if (info->ttl == IPT_OSF_TTL_TRUE)
			return (ip->ttl == f_ttl);
		if (info->ttl == IPT_OSF_TTL_NOCHECK)
			return 1;
		else {
			struct in_device *in_dev = in_dev_get(skb->dev);

			for_ifa(in_dev) {
				if (inet_ifa_match(ip->saddr, ifa)) {
					in_dev_put(in_dev);
					return (ip->ttl == f_ttl);
				}
			}
			endfor_ifa(in_dev);

			in_dev_put(in_dev);
			return (ip->ttl <= f_ttl);
		}
	}
	
	return (ip->ttl == f_ttl);
}

static bool
ipt_osf_match_packet(const struct sk_buff *skb,
		const struct net_device *in,
		const struct net_device *out,
		const struct xt_match *match,
		const void *matchinfo,
		int offset,
		unsigned int unused,
		bool *hotdrop)
{
	struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
	struct iphdr _iph, *ip;
	struct tcphdr _tcph, *tcp;
	int fmatch = FMATCH_WRONG, fcount = 0;
	unsigned int optsize = 0, check_WSS = 0;
	u16 window, totlen, mss = 0;
	unsigned char df, *optp = NULL, *_optp = NULL;
	unsigned char opts[MAX_IPOPTLEN];
	struct ipt_osf_finger *f;

	if (!info)
		return 0;

	ip = skb_header_pointer(skb, 0, sizeof(struct iphdr), &_iph);
	if (!ip)
		return 0;

	tcp = skb_header_pointer(skb, ip->ihl * 4, sizeof(struct tcphdr), &_tcph);
	if (!tcp)
		return 0;

	if (!tcp->syn)
		return 0;

	totlen = ntohs(ip->tot_len);
	df = ((ntohs(ip->frag_off) & IP_DF) ? 1 : 0);
	window = ntohs(tcp->window);

	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
		optsize = tcp->doff * 4 - sizeof(struct tcphdr);

		if (optsize > sizeof(opts)) {
			log("%s: BUG: too big options size: optsize=%u, max=%zu.\n", 
					__func__, optsize, sizeof(opts));
			optsize = sizeof(opts);
		}

		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
				optsize, opts);
	}

	rcu_read_lock();
	list_for_each_entry_rcu(f, &ipt_finger_list, flist) {
		if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
			continue;

		optp = _optp;
		fmatch = FMATCH_WRONG;

		if (totlen == f->ss && df == f->df && ipt_osf_ttl(skb, info, f->ttl)) {
			int foptsize, optnum;

			check_WSS = 0;

			switch (f->wss.wc) {
			case 0:
				check_WSS = 0;
				break;
			case 'S':
				check_WSS = 1;
				break;
			case 'T':
				check_WSS = 2;
				break;
			case '%':
				check_WSS = 3;
				break;
			default:
				log("Wrong fingerprint wss.wc=%d, %s - %s\n",
				    f->wss.wc, f->genre, f->details);
				check_WSS = 4;
				break;
			}
			if (check_WSS == 4)
				continue;

			/* Check options */

			foptsize = 0;
			for (optnum = 0; optnum < f->opt_num; ++optnum)
				foptsize += f->opt[optnum].length;
#if 0
			log("%s.%s.%s: optsize: %u, foptsize: %u, fopt_num: %u, optp: %p, win: %u, mss: %u, totlen: %u, df: %d, ttl: %u.\n", 
					f->genre, f->version, f->subtype, optsize, foptsize, f->opt_num, optp,
					window, mss, totlen, df, ip->ttl);
#endif
			if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
				continue;

			for (optnum = 0; optnum < f->opt_num; ++optnum) {
				if (f->opt[optnum].kind == (*optp)) {
					__u32 len = f->opt[optnum].length;
					__u8 *optend = optp + len;
					int loop_cont = 0;
					
					fmatch = FMATCH_OK;

					switch (*optp) {
					case OSFOPT_MSS:
						mss = ntohs(*(u16 *)(optp + 2));
						break;
					case OSFOPT_TS:
						loop_cont = 1;
						break;
					}

#if 0
					if (loop_cont) {
						optp = optend;
						continue;
					}
					if (len != 1) {
						/* Skip kind and length fields */
						optp += 2;

						if (f->opt[optnum].wc.val != 0) {
							u32 tmp = 0;
							u32 copy = len > 4 ? 4 : len;

							/* Hmmm... It looks a bit ugly. :) */
							memcpy(&tmp, optp, copy);

							/* 2 + 2: optlen(2 bytes) + 
							 *      kind(1 byte) + length(1 byte) */
							if (len == 4)
								tmp = ntohs(tmp);
							else
								tmp = ntohl(tmp);

							if (f->opt[optnum].wc.wc == '%') {
								if ((tmp % f->opt[optnum].wc.val) != 0)
									fmatch = FMATCH_OPT_WRONG;
							} else if (tmp != f->opt[optnum].wc.val)
								fmatch = FMATCH_OPT_WRONG;
						}
					}
#endif
					optp = optend;
				} else
					fmatch = FMATCH_OPT_WRONG;

				if (fmatch != FMATCH_OK)
					break;
			}

			if (fmatch != FMATCH_OPT_WRONG) {
				fmatch = FMATCH_WRONG;

				switch (check_WSS) {
				case 0:
					if (f->wss.val == 0 || window == f->wss.val)
						fmatch = FMATCH_OK;
					break;
				case 1:	/* MSS */
#define SMART_MSS_1	1460
#define SMART_MSS_2	1448
					if (window == f->wss.val * mss ||
					    window == f->wss.val * SMART_MSS_1 ||
					    window == f->wss.val * SMART_MSS_2)
						fmatch = FMATCH_OK;
					break;
				case 2:	/* MTU */
					if (window == f->wss.val * (mss + 40) ||
					    window == f->wss.val * (SMART_MSS_1 + 40) ||
					    window == f->wss.val * (SMART_MSS_2 + 40))
						fmatch = FMATCH_OK;
					break;
				case 3:	/* MOD */
					if ((window % f->wss.val) == 0)
						fmatch = FMATCH_OK;
					break;
				}
			}

			if (fmatch == FMATCH_OK) {
				fcount++;
				log("%s [%s:%s:%s] : %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n", 
						f->genre, f->version, f->subtype, f->details, 
						NIPQUAD(ip->saddr), ntohs(tcp->source), 
						NIPQUAD(ip->daddr), ntohs(tcp->dest), 
						f->ttl - ip->ttl);
				if (info->flags & IPT_OSF_CONNECTOR)
					ipt_osf_send_connector(f, skb);

				if ((info->flags & IPT_OSF_LOG) &&
				    info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
					break;
			}
		}
	}
	rcu_read_unlock();

	if (!fcount && (info->flags & (IPT_OSF_LOG | IPT_OSF_NETLINK | IPT_OSF_CONNECTOR))) {
		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
		unsigned int i, optsize;
		struct ipt_osf_finger fg;

		memset(&fg, 0, sizeof(fg));
#if 1
		if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
			log("Unknown: win: %u, mss: %u, totlen: %u, df: %d, ttl: %u : ", window, mss, totlen, df, ip->ttl);
		if (optp) {
			optsize = tcp->doff * 4 - sizeof(struct tcphdr);
			if (skb_copy_bits(skb, ip->ihl * 4 + sizeof(struct tcphdr),
					opt, optsize) < 0) {
				if (info->flags & IPT_OSF_LOG)
					loga("TRUNCATED");
				if (info->flags & IPT_OSF_NETLINK)
					strcpy(fg.details, "TRUNCATED");
			} else {
				for (i = 0; i < optsize; i++) {
					if (info->flags & IPT_OSF_LOG)
						loga("%02X ", opt[i]);
				}
				if (info->flags & IPT_OSF_NETLINK)
					memcpy(fg.details, opt, min_t(unsigned int, optsize, MAXDETLEN));
			}
		}
		if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
			loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
			     NIPQUAD(ip->saddr), ntohs(tcp->source),
			     NIPQUAD(ip->daddr), ntohs(tcp->dest));
#endif
		if (info->flags & (IPT_OSF_NETLINK | IPT_OSF_CONNECTOR)) {
			fg.wss.val = window;
			fg.ttl = ip->ttl;
			fg.df = df;
			fg.ss = totlen;
			fg.mss = mss;
			strncpy(fg.genre, "Unknown", MAXGENRELEN);

			if (info->flags & IPT_OSF_CONNECTOR)
				ipt_osf_send_connector(&fg, skb);
		}
	}

	if (fcount)
		fmatch = FMATCH_OK;

	return (fmatch == FMATCH_OK) ? 1 : 0;
}

static bool
ipt_osf_checkentry(const char *tablename,
		const void *data,
		const struct xt_match *match,
		void *matchinfo,
		unsigned int hook_mask)
{
	struct ipt_ip *ip = (struct ipt_ip *)data;

	if (ip->proto != IPPROTO_TCP)
		return false;

	return true;
}

static struct xt_match ipt_osf_match = {
	.name 		= "osf",
	.revision	= 0,
	.family		= AF_INET,
	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING),
	.match 		= ipt_osf_match_packet,
	.checkentry	= ipt_osf_checkentry,
	.matchsize	= sizeof(struct ipt_osf_info),
	.me		= THIS_MODULE,
};

static int __devinit ipt_osf_init(void)
{
	int err = -EINVAL;
	struct proc_dir_entry *p;

	log("Startng OS fingerprint matching module.\n");

	p = create_proc_entry("osf", S_IFREG | 0644, proc_net_netfilter);
	if (!p)
		goto err_out_exit;

	p->write_proc = ipt_osf_proc_write;
	p->read_proc = ipt_osf_proc_read;

	err = xt_register_match(&ipt_osf_match);
	if (err) {
		log("Failed to register OS fingerprint matching module.\n");
		goto err_out_remove;
	}

	return 0;

err_out_remove:
	remove_proc_entry("osf", proc_net_netfilter);
err_out_exit:
	return err;
}

static void __devexit ipt_osf_fini(void)
{
	struct ipt_osf_finger *f, *n;

	remove_proc_entry("osf", proc_net_netfilter);
	xt_unregister_match(&ipt_osf_match);

	list_for_each_entry_safe(f, n, &ipt_finger_list, flist) {
		list_del(&f->flist);
		ipt_osf_finger_free(f);
	}

	log("OS fingerprint matching module finished.\n");
}

module_init(ipt_osf_init);
module_exit(ipt_osf_fini);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
MODULE_DESCRIPTION("Passive OS fingerprint matching.");

[-- Attachment #3: ipt_osf.h --]
[-- Type: text/plain, Size: 3603 bytes --]

/*
 * ipt_osf.h
 *
 * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef _IPT_OSF_H
#define _IPT_OSF_H

#define MAXGENRELEN		32
#define MAXDETLEN		64

#define IPT_OSF_GENRE		1
#define	IPT_OSF_TTL		2
#define IPT_OSF_LOG		4
#define IPT_OSF_NETLINK		8
#define IPT_OSF_CONNECTOR	16
#define IPT_OSF_INVERT		32

#define IPT_OSF_LOGLEVEL_ALL	0
#define IPT_OSF_LOGLEVEL_FIRST	1
#define IPT_OSF_LOGLEVEL_ALL_KNOWN	2

#define IPT_OSF_TTL_TRUE	0	/* True ip and fingerprint TTL comparison */
#define IPT_OSF_TTL_LESS	1	/* Check if ip TTL is less than fingerprint one */
#define IPT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */

#ifndef __KERNEL__
#include <netinet/ip.h>
#include <netinet/tcp.h>

struct list_head {
	struct list_head *prev, *next;
};
#endif

struct ipt_osf_info {
	char genre[MAXGENRELEN];
	__u32 len, flags, loglevel, ttl;
} __attribute__ ((packed));

struct ipt_osf_wc {
	__u8 wc;
	__u32 val;
} __attribute__ ((packed));

/* This struct represents IANA options
 * http://www.iana.org/assignments/tcp-parameters
 */
struct ipt_osf_opt {
	__u8 kind, length;
	struct ipt_osf_wc wc;
} __attribute__ ((packed));

struct ipt_osf_finger {
	struct list_head flist;
	struct ipt_osf_wc wss;
	__u8 ttl, df;
	__u16 ss, mss;
	unsigned char genre[MAXGENRELEN];
	unsigned char version[MAXGENRELEN], subtype[MAXGENRELEN];

	/* Not needed, but for consistency with original table from Michal Zalewski */
	unsigned char details[MAXDETLEN];

	int opt_num;
	struct ipt_osf_opt opt[MAX_IPOPTLEN];	/* In case it is all NOP or EOL */

} __attribute__ ((packed));

struct ipt_osf_nlmsg {
	struct ipt_osf_finger f;
	struct iphdr ip;
	struct tcphdr tcp;
} __attribute__ ((packed));

#ifdef __KERNEL__

#include <linux/list.h>
#include <net/tcp.h>

/* Defines for IANA option kinds */

#define OSFOPT_EOL		0	/* End of options */
#define OSFOPT_NOP		1	/* NOP */
#define OSFOPT_MSS		2	/* Maximum segment size */
#define OSFOPT_WSO		3	/* Window scale option */
#define OSFOPT_SACKP		4	/* SACK permitted */
#define OSFOPT_SACK		5	/* SACK */
#define OSFOPT_ECHO		6
#define OSFOPT_ECHOREPLY	7
#define OSFOPT_TS		8	/* Timestamp option */
#define OSFOPT_POCP		9	/* Partial Order Connection Permitted */
#define OSFOPT_POSP		10	/* Partial Order Service Profile */
#define OSFOPT_EMPTY		255
/* Others are not used in current OSF */

static struct ipt_osf_opt IANA_opts[] = {
	{0, 1,},
	{1, 1,},
	{2, 4,},
	{3, 3,},
	{4, 2,},
	{5, 1,},		/* SACK length is not defined */
	{6, 6,},
	{7, 6,},
	{8, 10,},
	{9, 2,},
	{10, 3,},
	{11, 1,},		/* CC: Suppose 1 */
	{12, 1,},		/* the same */
	{13, 1,},		/* and here too */
	{14, 3,},
	{15, 1,},		/* TCP Alternate Checksum Data. Length is not defined */
	{16, 1,},
	{17, 1,},
	{18, 3,},
	{19, 18,},
	{20, 1,},
	{21, 1,},
	{22, 1,},
	{23, 1,},
	{24, 1,},
	{25, 1,},
	{26, 1,},
};

#endif				/* __KERNEL__ */

#endif				/* _IPT_OSF_H */

[-- Attachment #4: libipt_osf.c --]
[-- Type: text/plain, Size: 5176 bytes --]

/*
 * libipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/*
 * iptables interface for OS fingerprint matching module.
 */

#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>

#include <iptables.h>

typedef unsigned int __u32;
typedef unsigned short __u16;
typedef unsigned char __u8;

#include "ipt_osf.h"

static void osf_help(void)
{
	printf("OS fingerprint match options:\n"
		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
		"--ttl			Use some TTL check extensions to determine OS:\n"
		"	0			true ip and fingerprint TTL comparison. Works for LAN.\n"
		"	1			check if ip TTL is less than fingerprint one. Works for global addresses.\n"
		"	2			do not compare TTL at all. Allows to detect NMAP, but can produce false results.\n"
		"--log level		Log determined genres into dmesg even if they do not match desired one:\n"
		"	0			log all matched or unknown signatures.\n"
		"	1			log only first one.\n"
		"	2			log all known matched signatures.\n"
		"--connector		Log through kernel connector [2.6.14+].\n\n"
		);
}


static const struct option osf_opts[] = {
	{ .name = "genre",	.has_arg = 1, .flag = 0, .val = '1' },
	{ .name = "ttl",	.has_arg = 1, .flag = 0, .val = '2' },
	{ .name = "log",	.has_arg = 1, .flag = 0, .val = '3' },
	{ .name = "netlink",	.has_arg = 0, .flag = 0, .val = '4' },
	{ .name = "connector",	.has_arg = 0, .flag = 0, .val = '5' },
	{ .name = NULL }
};


static void osf_init(struct xt_entry_match *m)
{
}

static void osf_parse_string(const unsigned char *s, struct ipt_osf_info *info)
{
	if (strlen(s) < MAXGENRELEN) 
		strcpy(info->genre, s);
	else 
		exit_error(PARAMETER_PROBLEM, "Genre string too long `%s' [%d], max=%d", 
				s, strlen(s), MAXGENRELEN);
}

static int osf_parse(int c, char **argv, int invert, unsigned int *flags,
      			const void *entry,
      			struct xt_entry_match **match)
{
	struct ipt_osf_info *info = (struct ipt_osf_info *)(*match)->data;
	
	switch(c) 
	{
		case '1': /* --genre */
			if (*flags & IPT_OSF_GENRE)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple genre parameter");
			check_inverse(optarg, &invert, &optind, 0);
			osf_parse_string(argv[optind-1], info);
			if (invert)
				info->flags |= IPT_OSF_INVERT;
			info->len=strlen((char *)info->genre);
			*flags |= IPT_OSF_GENRE;
			break;
		case '2': /* --ttl */
			if (*flags & IPT_OSF_TTL)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple ttl parameter");
			*flags |= IPT_OSF_TTL;
			info->flags |= IPT_OSF_TTL;
			info->ttl = atoi(argv[optind-1]);
			break;
		case '3': /* --log */
			if (*flags & IPT_OSF_LOG)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
			*flags |= IPT_OSF_LOG;
			info->loglevel = atoi(argv[optind-1]);
			info->flags |= IPT_OSF_LOG;
			break;
		case '4': /* --netlink */
			if (*flags & IPT_OSF_NETLINK)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple netlink parameter");
			*flags |= IPT_OSF_NETLINK;
			info->flags |= IPT_OSF_NETLINK;
			break;
		case '5': /* --connector */
			if (*flags & IPT_OSF_CONNECTOR)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple connector parameter");
			*flags |= IPT_OSF_CONNECTOR;
			info->flags |= IPT_OSF_CONNECTOR;
			break;
		default:
			return 0;
	}

	return 1;
}

static void osf_final_check(unsigned int flags)
{
	if (!flags)
		exit_error(PARAMETER_PROBLEM, "OS fingerprint match: You must specify `--genre'");
}

static void osf_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
	const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;

	printf("OS fingerprint match %s%s ", (info->flags & IPT_OSF_INVERT) ? "!" : "", info->genre);
}

static void osf_save(const void *ip, const struct xt_entry_match *match)
{
	const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;

	printf("--genre %s%s ", (info->flags & IPT_OSF_INVERT) ? "! ": "", info->genre);
}


static struct xtables_match osf_match = {
    .name		= "osf",
    .version		= XTABLES_VERSION,
    .size		= XT_ALIGN(sizeof(struct ipt_osf_info)),
    .userspacesize	= XT_ALIGN(sizeof(struct ipt_osf_info)),
    .help		= &osf_help,
    .init		= &osf_init,
    .parse		= &osf_parse,
    .print		= &osf_print,
    .final_check	= &osf_final_check,
    .save		= &osf_save,
    .extra_opts		= osf_opts
};


void _init(void)
{
	xtables_register_match(&osf_match);
}

[-- Attachment #5: Makefile --]
[-- Type: text/plain, Size: 865 bytes --]

obj-m		:= ipt_osf.o

KDIR	:= /lib/modules/$(shell uname -r)/build
IPTABLES:= /usr/include

ifeq ($(IPTABLES),path_to_iptables_sources_or_header_files)
$(error "Edit IPTABLES variable in Makefile and read README")
endif
PWD	:= $(shell pwd)

iptables_version=$(shell (/sbin/iptables -V | awk {'print $$2'} | cut -c 2-))
LCFLAGS = -DIPTABLES_VERSION=\"$(iptables_version)\"

default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
	rm -f *.o *.ko *.mod.* .*.cmd *~
	rm -rf .tmp_versions
	rm -f *.o *~ *.so *.ko load osfd ucon_osf

lib:	libipt_osf.c ipt_osf.h
	gcc $(LCFLAGS) libipt_osf.c -D_INIT=_init -fPIC -I$(IPTABLES)/include -c -o libipt_osf.o
	gcc -shared -nostdlib -rdynamic -Wl,-soname,libipt_osf.so -o libipt_osf.so libipt_osf.o

bin:	osfd.c load.c ucon_osf.c
	gcc -W -Wall osfd.c -o osfd
	gcc -W -Wall load.c -o load
	gcc -W -Wall ucon_osf.c -o ucon_osf

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 11:39 Passive OS fingerprinting Evgeniy Polyakov
@ 2008-07-01 11:53 ` Patrick McHardy
  2008-07-01 12:03   ` Evgeniy Polyakov
  2008-07-01 19:56 ` Paul E. McKenney
  1 sibling, 1 reply; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 11:53 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, netfilter-devel

Evgeniy Polyakov wrote:
> Passive OS fingerprinting iptables (xtables) allows to match incoming
> packets by different sets of SYN-packet and determine, which remote
> system is on the remote end, so you can make decisions based on OS
> type and even version at some degreee and perform various netfilter
> actions based on that knowledge.
> 
> This module compares some data (WS, MSS, options and it's order, ttl, df
> and others) from packets with SYN bit set with dynamically loaded OS
> fingerprints.

[Only some general comments without having looked at the
code in detail]

My two main objections are that this only works for TCP and
can be trivially evaded. What use cases does it have?
I'm also wondering whether this couldn't be implemented
using the u32 match.

> This version existed quite for a while in patch-o-matic(-ng), but
> suddenly was dropped and then only was updated on its own repo:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
> 
> I've updated OSF to match new iptables standards (namely xtables
> support) and present new kernelspace and userspace library files in
> attach.
> 
> To setup single rule, which will drop and log all Linux incoming
> access one needs to do following steps:
> # insmod ./ipt_osf.ko
> # ./load ./pf.os /proc/sys/net/ipv4/osf
> # iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
> --ttl 2 --connector

And I don't think it should be using connector. AFAIK we
only have a single user in the tree currently and new
stuff usually uses genetlink (which is pretty similar),
so we might be able to remove connection in the future
unless we add new users. But netfilter modules should
use nfnetlink anyway.




^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 11:53 ` Patrick McHardy
@ 2008-07-01 12:03   ` Evgeniy Polyakov
  2008-07-01 12:35     ` Patrick McHardy
  0 siblings, 1 reply; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 12:03 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, netfilter-devel

Hi Patrick.

On Tue, Jul 01, 2008 at 01:53:43PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> My two main objections are that this only works for TCP and
> can be trivially evaded. What use cases does it have?

Yes, it is TCP specific module.

> I'm also wondering whether this couldn't be implemented
> using the u32 match.

I'm not sure it is that simple. OSF uses common rules database
shared with OpenBSD (and other *BSDs as well), so converting it into u32
match would require noticeble efforts. But in theory it is probably
doable.

> >This version existed quite for a while in patch-o-matic(-ng), but
> >suddenly was dropped and then only was updated on its own repo:
> >http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
> >
> >I've updated OSF to match new iptables standards (namely xtables
> >support) and present new kernelspace and userspace library files in
> >attach.
> >
> >To setup single rule, which will drop and log all Linux incoming
> >access one needs to do following steps:
> ># insmod ./ipt_osf.ko
> ># ./load ./pf.os /proc/sys/net/ipv4/osf
> ># iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
> >--ttl 2 --connector
> 
> And I don't think it should be using connector. AFAIK we
> only have a single user in the tree currently and new
> stuff usually uses genetlink (which is pretty similar),
> so we might be able to remove connection in the future
> unless we add new users. But netfilter modules should
> use nfnetlink anyway.
 
This module was created way before genetlink was ever designed (on
behalf of connector btw :)

Also I do not know why we want to remove connector in favour of
genetlink, since the former is much simpler to work with. Connector
logging is optional in OSF.

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 12:03   ` Evgeniy Polyakov
@ 2008-07-01 12:35     ` Patrick McHardy
  2008-07-01 13:08       ` Evgeniy Polyakov
  2008-07-01 13:32       ` Jeff Garzik
  0 siblings, 2 replies; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 12:35 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, netfilter-devel

Evgeniy Polyakov wrote:
> On Tue, Jul 01, 2008 at 01:53:43PM +0200, Patrick McHardy (kaber@trash.net) wrote:
>> My two main objections are that this only works for TCP and
>> can be trivially evaded. What use cases does it have?
> 
> Yes, it is TCP specific module.

What about the use cases? I certainly like the idea you suggest in
your blog ("Ever dreamt to block all Linux users in your network
from accessing internet and allow full bandwidth to Windows worm?")
:) But something this easy to evade doesn't seem to provide a real
benefit for a firewall.

I can see that something like "Block IE6 running on Windows version X"
might be useful (NUFW can do this I think), but that needs support
from the host.

>> I'm also wondering whether this couldn't be implemented
>> using the u32 match.
> 
> I'm not sure it is that simple. OSF uses common rules database
> shared with OpenBSD (and other *BSDs as well), so converting it into u32
> match would require noticeble efforts. But in theory it is probably
> doable.

This would be preferrable in my opinion since they both allow
programmable filters, but u32 appears to be more flexible. I'm
very reluctant to add new iptables modules that don't increase
expressiveness or provide other clear benefits since we already
have an insane amount of modules.

 From the fingerprint file:

# Fingerprint entry format:
#
# wwww:ttt:D:ss:OOO...:OS:Version:Subtype:Details
#
# wwww     - window size (can be *, %nnn, Snn or Tnn).  The special values
#            "S" and "T" which are a multiple of MSS or a multiple of MTU
#            respectively.
# ttt      - initial TTL
# D        - don't fragment bit (0 - not set, 1 - set)
# ss       - overall SYN packet size
# OOO      - option value and order specification (see below)
# OS       - OS genre (Linux, Solaris, Windows)
# Version  - OS Version (2.0.27 on x86, etc)
# Subtype  - OS subtype or patchlevel (SP3, lo0)
# details  - Generic OS details

MSS can be matched using the tcpmss match, options (but not order)
using the tcp match, TTL using the ttl match, DF using the IP flags
match, packet size using the length match. That leaves WS for u32
(or adding support to the tcp match).

The conversion looks pretty easy actually.

>>> # iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
>>> --ttl 2 --connector
>> And I don't think it should be using connector. AFAIK we
>> only have a single user in the tree currently and new
>> stuff usually uses genetlink (which is pretty similar),
>> so we might be able to remove connection in the future
>> unless we add new users. But netfilter modules should
>> use nfnetlink anyway.
>  
> This module was created way before genetlink was ever designed (on
> behalf of connector btw :)

Yes, I know.

> Also I do not know why we want to remove connector in favour of
> genetlink, since the former is much simpler to work with. Connector
> logging is optional in OSF.

I'm not sure if we want to, but they appear to provide similar
functionality and connector only has a single user in the tree
so far. But thats a different discussion, for netfilter related
things nfnetlink should be used.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 12:35     ` Patrick McHardy
@ 2008-07-01 13:08       ` Evgeniy Polyakov
  2008-07-01 13:41         ` Patrick McHardy
  2008-07-01 14:26         ` Jan Engelhardt
  2008-07-01 13:32       ` Jeff Garzik
  1 sibling, 2 replies; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 13:08 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, netfilter-devel

On Tue, Jul 01, 2008 at 02:35:19PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> >>My two main objections are that this only works for TCP and
> >>can be trivially evaded. What use cases does it have?
> >
> >Yes, it is TCP specific module.
> 
> What about the use cases? I certainly like the idea you suggest in
> your blog ("Ever dreamt to block all Linux users in your network
> from accessing internet and allow full bandwidth to Windows worm?")
> :) But something this easy to evade doesn't seem to provide a real
> benefit for a firewall.

Actually worm detection is one of the use cases - I was told about
successful installations several years ago.

> I can see that something like "Block IE6 running on Windows version X"
> might be useful (NUFW can do this I think), but that needs support
> from the host.

It allows to determine that it is Windows version X, there is no way to
distinguish userspace application from each other if they use default
parameters.

OS version detection is somewhat subtle - in earlier days it was
simpler, since each major version broke/changed something especially
in XP stack, in Linux I do not know any similar changes in recent
2.6 kernels.

> >>I'm also wondering whether this couldn't be implemented
> >>using the u32 match.
> >
> >I'm not sure it is that simple. OSF uses common rules database
> >shared with OpenBSD (and other *BSDs as well), so converting it into u32
> >match would require noticeble efforts. But in theory it is probably
> >doable.
> 
> This would be preferrable in my opinion since they both allow
> programmable filters, but u32 appears to be more flexible. I'm
> very reluctant to add new iptables modules that don't increase
> expressiveness or provide other clear benefits since we already
> have an insane amount of modules.

OSF does increase expressiveness! :)
'--genre Linux' is much more clear than... Do you want me to at least
roughly draw mathing rules enciphered in u32 format?

I agree that matching can be done with u32 (even private OSF TTL games),
but it will much much much more ugly than simple module.

I am also not sure OSF should live in kernel, but what it does it does
good and there is no simple way to do the same with existing
functionality. It is possible, but not simple, and definitely not
trivial for administrator :)

> From the fingerprint file:
> 
> # Fingerprint entry format:
> #
> # wwww:ttt:D:ss:OOO...:OS:Version:Subtype:Details
> #
> # wwww     - window size (can be *, %nnn, Snn or Tnn).  The special values
> #            "S" and "T" which are a multiple of MSS or a multiple of MTU
> #            respectively.
> # ttt      - initial TTL
> # D        - don't fragment bit (0 - not set, 1 - set)
> # ss       - overall SYN packet size
> # OOO      - option value and order specification (see below)
> # OS       - OS genre (Linux, Solaris, Windows)
> # Version  - OS Version (2.0.27 on x86, etc)
> # Subtype  - OS subtype or patchlevel (SP3, lo0)
> # details  - Generic OS details
> 
> MSS can be matched using the tcpmss match, options (but not order)
> using the tcp match, TTL using the ttl match, DF using the IP flags
> match, packet size using the length match. That leaves WS for u32
> (or adding support to the tcp match).
> 
> The conversion looks pretty easy actually.

Hmm, I can assure you that simple rule for OSF matching module is much
more clear than zillions above rules, but it can be done indeed. I'm not
sure that 5 years ago all that matches existed, but right now it is
possible. Not trivial and not simple, but probably possible.

To use multple matches one has to chain them into single rule/table,
listing and understaning of which is not simple enough compared to OSF
rules. So it is helper module which allows really simple installation.

Various tricks with TTL used in OSF will complicate that chaining even
more.

> >Also I do not know why we want to remove connector in favour of
> >genetlink, since the former is much simpler to work with. Connector
> >logging is optional in OSF.
> 
> I'm not sure if we want to, but they appear to provide similar
> functionality and connector only has a single user in the tree
> so far. But thats a different discussion, for netfilter related
> things nfnetlink should be used.

IIRC there are at least threee: w1, various accountings and uvesafb.
And I authored only the first :)

There was no nfnetlink either 5 years ago, when OSF was created,
this release is just subsequent update to the project.
At some moment OSF shared netlink group with ulog, but it was
considered harmful, so I dropped support. Netlink usage is
rather trivial: it just sends information about matched packt to
userspace, so it can block it on its own, rise a message in the window
or perform some other steps. Nothing exceptionally complex :)

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 12:35     ` Patrick McHardy
  2008-07-01 13:08       ` Evgeniy Polyakov
@ 2008-07-01 13:32       ` Jeff Garzik
  2008-07-01 13:35         ` Patrick McHardy
  2008-07-01 13:39         ` Evgeniy Polyakov
  1 sibling, 2 replies; 21+ messages in thread
From: Jeff Garzik @ 2008-07-01 13:32 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Evgeniy Polyakov, netdev, netfilter-devel

Patrick McHardy wrote:
> Evgeniy Polyakov wrote:
>> On Tue, Jul 01, 2008 at 01:53:43PM +0200, Patrick McHardy 
>> (kaber@trash.net) wrote:
>>> My two main objections are that this only works for TCP and
>>> can be trivially evaded. What use cases does it have?
>>
>> Yes, it is TCP specific module.
> 
> What about the use cases? I certainly like the idea you suggest in
> your blog ("Ever dreamt to block all Linux users in your network
> from accessing internet and allow full bandwidth to Windows worm?")
> :) But something this easy to evade doesn't seem to provide a real
> benefit for a firewall.
> 
> I can see that something like "Block IE6 running on Windows version X"
> might be useful (NUFW can do this I think), but that needs support
> from the host.

Not addressing Evgeniy's module but speaking generally...

It sure would be nice for regular socket applications to have an easy, 
unprivileged way to query the OS fingerprint information of a given socket.

Speaking purely from a userspace application API perspective, it would 
be most useful for an app to be able to stop OSF collection, start OSF 
collection, and query OSF stats.  start/stop would be a refcount that 
disables in-kernel OSF when not in use.

To present a specific use case:  I would like to know if incoming SMTP 
connections are Windows or not.  That permits me to better determine if 
the incoming connection is a hijacked PC or not -- it becomes a useful 
factor in spamassassin scoring.

In this case, incoming SMTP is -always- TCP, thus being a TCP-specific 
module is not a problem.  You cover a huge swath of apps even if the 
module is TCP-specific.

Another use case is validating whether a browser is "lying" about its 
OS, when parsing HTTP user-agent info, or in general when any remote 
agent is "lying" about its OS.  Security software can use that as an 
additional red-flag factor.

	Jeff




^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:32       ` Jeff Garzik
@ 2008-07-01 13:35         ` Patrick McHardy
  2008-07-01 13:47           ` Evgeniy Polyakov
  2008-07-01 15:34           ` Jeff Garzik
  2008-07-01 13:39         ` Evgeniy Polyakov
  1 sibling, 2 replies; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 13:35 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Evgeniy Polyakov, netdev, netfilter-devel

Jeff Garzik wrote:
> Patrick McHardy wrote:
>> What about the use cases? I certainly like the idea you suggest in
>> your blog ("Ever dreamt to block all Linux users in your network
>> from accessing internet and allow full bandwidth to Windows worm?")
>> :) But something this easy to evade doesn't seem to provide a real
>> benefit for a firewall.
>>
>> I can see that something like "Block IE6 running on Windows version X"
>> might be useful (NUFW can do this I think), but that needs support
>> from the host.
>
> Not addressing Evgeniy's module but speaking generally...
>
> It sure would be nice for regular socket applications to have an easy, 
> unprivileged way to query the OS fingerprint information of a given 
> socket.

I'm not sure how much OSF depends on the TTL, but doing this
more than one hop away from the host (or without knowledge of
the number of hops) makes using the TTL basically impossible.

> Speaking purely from a userspace application API perspective, it would 
> be most useful for an app to be able to stop OSF collection, start OSF 
> collection, and query OSF stats.  start/stop would be a refcount that 
> disables in-kernel OSF when not in use.
>
> To present a specific use case:  I would like to know if incoming SMTP 
> connections are Windows or not.  That permits me to better determine 
> if the incoming connection is a hijacked PC or not -- it becomes a 
> useful factor in spamassassin scoring.
>
> In this case, incoming SMTP is -always- TCP, thus being a TCP-specific 
> module is not a problem.  You cover a huge swath of apps even if the 
> module is TCP-specific.
>
> Another use case is validating whether a browser is "lying" about its 
> OS, when parsing HTTP user-agent info, or in general when any remote 
> agent is "lying" about its OS.  Security software can use that as an 
> additional red-flag factor. 

I for one would be much happier to only have netfilter as a user
of this :)


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:32       ` Jeff Garzik
  2008-07-01 13:35         ` Patrick McHardy
@ 2008-07-01 13:39         ` Evgeniy Polyakov
  1 sibling, 0 replies; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 13:39 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Patrick McHardy, netdev, netfilter-devel

Hi Jeff.

On Tue, Jul 01, 2008 at 09:32:47AM -0400, Jeff Garzik (jeff@garzik.org) wrote:
> It sure would be nice for regular socket applications to have an easy, 
> unprivileged way to query the OS fingerprint information of a given socket.
> 
> Speaking purely from a userspace application API perspective, it would 
> be most useful for an app to be able to stop OSF collection, start OSF 
> collection, and query OSF stats.  start/stop would be a refcount that 
> disables in-kernel OSF when not in use.
> 
> To present a specific use case:  I would like to know if incoming SMTP 
> connections are Windows or not.  That permits me to better determine if 
> the incoming connection is a hijacked PC or not -- it becomes a useful 
> factor in spamassassin scoring.
> 
> In this case, incoming SMTP is -always- TCP, thus being a TCP-specific 
> module is not a problem.  You cover a huge swath of apps even if the 
> module is TCP-specific.
> 
> Another use case is validating whether a browser is "lying" about its 
> OS, when parsing HTTP user-agent info, or in general when any remote 
> agent is "lying" about its OS.  Security software can use that as an 
> additional red-flag factor.

It is possible right now in OSF: it sends a netlink notification to
userspace about received and matched packet. We can even think some more
about reverse channel - to inform kernel about some steps for this
match, it requires root priveledges though. It can also be done via
different channel (like running script to install iptables rule).

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:08       ` Evgeniy Polyakov
@ 2008-07-01 13:41         ` Patrick McHardy
  2008-07-01 14:14           ` Evgeniy Polyakov
  2008-07-01 14:26         ` Jan Engelhardt
  1 sibling, 1 reply; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 13:41 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, netfilter-devel

Evgeniy Polyakov wrote:
> On Tue, Jul 01, 2008 at 02:35:19PM +0200, Patrick McHardy (kaber@trash.net) wrote:
>   
>>>> My two main objections are that this only works for TCP and
>>>> can be trivially evaded. What use cases does it have?
>>>>         
>>> Yes, it is TCP specific module.
>>>       
>> What about the use cases? I certainly like the idea you suggest in
>> your blog ("Ever dreamt to block all Linux users in your network
>> from accessing internet and allow full bandwidth to Windows worm?")
>> :) But something this easy to evade doesn't seem to provide a real
>> benefit for a firewall.
>>     
>
> Actually worm detection is one of the use cases - I was told about
> successful installations several years ago.
>   

How does that work? I assume in combination with some kind of
rate-limit?

>>>> I'm also wondering whether this couldn't be implemented
>>>> using the u32 match.
>>>>         
>>> I'm not sure it is that simple. OSF uses common rules database
>>> shared with OpenBSD (and other *BSDs as well), so converting it into u32
>>> match would require noticeble efforts. But in theory it is probably
>>> doable.
>>>       
>> This would be preferrable in my opinion since they both allow
>> programmable filters, but u32 appears to be more flexible. I'm
>> very reluctant to add new iptables modules that don't increase
>> expressiveness or provide other clear benefits since we already
>> have an insane amount of modules.
>>     
>
> OSF does increase expressiveness! :)
> '--genre Linux' is much more clear than... Do you want me to at least
> roughly draw mathing rules enciphered in u32 format?
>
> I agree that matching can be done with u32 (even private OSF TTL games),
> but it will much much much more ugly than simple module.
>
> I am also not sure OSF should live in kernel, but what it does it does
> good and there is no simple way to do the same with existing
> functionality. It is possible, but not simple, and definitely not
> trivial for administrator :)
>   

I don't like the current way such things are implemented in iptables
(have all logic in the kernel instead of just providing a mechanism
for implementing it in userspace and presenting a nice view to the
administrator). Thats not your fault of course and your module is
also not the first one to do this.

Unfortunately its most likely not possible to convince me to like
this, so lets just say that I'm fine with merging it if someone
speaks up in favour of it :)

>>> Also I do not know why we want to remove connector in favour of
>>> genetlink, since the former is much simpler to work with. Connector
>>> logging is optional in OSF.
>>>       
>> I'm not sure if we want to, but they appear to provide similar
>> functionality and connector only has a single user in the tree
>> so far. But thats a different discussion, for netfilter related
>> things nfnetlink should be used.
>>     
>
> IIRC there are at least threee: w1, various accountings and uvesafb.
> And I authored only the first :)
>
> There was no nfnetlink either 5 years ago, when OSF was created,
> this release is just subsequent update to the project.
> At some moment OSF shared netlink group with ulog, but it was
> considered harmful, so I dropped support. Netlink usage is
> rather trivial: it just sends information about matched packt to
> userspace, so it can block it on its own, rise a message in the window
> or perform some other steps. Nothing exceptionally complex :)
>   

Yes, but I don't want to add another interface netfilter userspace
has to know about. It should either use nfnetlink and remove the proc
interface, or remove the connector interface and use proc.
Preferrably the former.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:35         ` Patrick McHardy
@ 2008-07-01 13:47           ` Evgeniy Polyakov
  2008-07-01 15:34           ` Jeff Garzik
  1 sibling, 0 replies; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 13:47 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Jeff Garzik, netdev, netfilter-devel

On Tue, Jul 01, 2008 at 03:35:02PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> >It sure would be nice for regular socket applications to have an easy, 
> >unprivileged way to query the OS fingerprint information of a given 
> >socket.
> 
> I'm not sure how much OSF depends on the TTL, but doing this
> more than one hop away from the host (or without knowledge of
> the number of hops) makes using the TTL basically impossible.

There are three modes in OSF: LAN where things are simple, no-ttl, where
things are even more simpler and false positive, and heueristic mode,
which checks ttl, but with some addons. Like if ttl is 31, it is
possible that it is OS with initial TTL being equal to 32, and other OS,
with initial TTL 48, and whatever other checks succeeded for that cases,
determine what OS is.

It works quite good in internet not only LAN, since it is frequently
only enough to roughly determine initial TTL.

> >Another use case is validating whether a browser is "lying" about its 
> >OS, when parsing HTTP user-agent info, or in general when any remote 
> >agent is "lying" about its OS.  Security software can use that as an 
> >additional red-flag factor. 
> 
> I for one would be much happier to only have netfilter as a user
> of this :)

Security checkers do like to put its hands into sooo deep places in the stack :)

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:41         ` Patrick McHardy
@ 2008-07-01 14:14           ` Evgeniy Polyakov
  2008-07-01 14:16             ` Patrick McHardy
  0 siblings, 1 reply; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 14:14 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, netfilter-devel

On Tue, Jul 01, 2008 at 03:41:58PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> >Actually worm detection is one of the use cases - I was told about
> >successful installations several years ago.
> 
> How does that work? I assume in combination with some kind of
> rate-limit?

IIRC during worm attacks all windows (according to OSF) traffic
was just routed via slow/shaped device (that was forwarding on
a gateway with appropriate NAT). Right now I would recomment to
use recent/rateest module to make fine-grained tuning of the connection.

> >I am also not sure OSF should live in kernel, but what it does it does
> >good and there is no simple way to do the same with existing
> >functionality. It is possible, but not simple, and definitely not
> >trivial for administrator :)
> 
> I don't like the current way such things are implemented in iptables
> (have all logic in the kernel instead of just providing a mechanism
> for implementing it in userspace and presenting a nice view to the
> administrator). Thats not your fault of course and your module is
> also not the first one to do this.

I bet it is not the last one :)

> Unfortunately its most likely not possible to convince me to like
> this, so lets just say that I'm fine with merging it if someone
> speaks up in favour of it :)

Cool. If no none will reply, nothing actually changes :)
OSF lived on its own all the time except several months in patch-o-matic
and then its next generation.

> >There was no nfnetlink either 5 years ago, when OSF was created,
> >this release is just subsequent update to the project.
> >At some moment OSF shared netlink group with ulog, but it was
> >considered harmful, so I dropped support. Netlink usage is
> >rather trivial: it just sends information about matched packt to
> >userspace, so it can block it on its own, rise a message in the window
> >or perform some other steps. Nothing exceptionally complex :)
> >  
> 
> Yes, but I don't want to add another interface netfilter userspace
> has to know about. It should either use nfnetlink and remove the proc
> interface, or remove the connector interface and use proc.
> Preferrably the former.

It uses proc to load rules - I do not like it either, but it was the
simplest way to do so :)

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 14:14           ` Evgeniy Polyakov
@ 2008-07-01 14:16             ` Patrick McHardy
  2008-07-01 14:48               ` Evgeniy Polyakov
  0 siblings, 1 reply; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 14:16 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, netfilter-devel

Evgeniy Polyakov wrote:
> On Tue, Jul 01, 2008 at 03:41:58PM +0200, Patrick McHardy (kaber@trash.net) wrote:
>   
>>> I am also not sure OSF should live in kernel, but what it does it does
>>> good and there is no simple way to do the same with existing
>>> functionality. It is possible, but not simple, and definitely not
>>> trivial for administrator :)
>>>       
>> I don't like the current way such things are implemented in iptables
>> (have all logic in the kernel instead of just providing a mechanism
>> for implementing it in userspace and presenting a nice view to the
>> administrator). Thats not your fault of course and your module is
>> also not the first one to do this.
>>     
>
> I bet it is not the last one :)
>   

I truely hope it will be since I'm working (slowly, as time permits)
on the *tables successor that will implement things like this in
userspace. Every module we add that adds more complicated logic in
the kernel will make adding an iptables compat layer harder.

>> Unfortunately its most likely not possible to convince me to like
>> this, so lets just say that I'm fine with merging it if someone
>> speaks up in favour of it :)
>>     
>
> Cool. If no none will reply, nothing actually changes :)
> OSF lived on its own all the time except several months in patch-o-matic
> and then its next generation.
>
>   

I'd CC the netfilter user list, its likely you'll find some voices
in favour there :)

>>> There was no nfnetlink either 5 years ago, when OSF was created,
>>> this release is just subsequent update to the project.
>>> At some moment OSF shared netlink group with ulog, but it was
>>> considered harmful, so I dropped support. Netlink usage is
>>> rather trivial: it just sends information about matched packt to
>>> userspace, so it can block it on its own, rise a message in the window
>>> or perform some other steps. Nothing exceptionally complex :)
>>>  
>>>       
>> Yes, but I don't want to add another interface netfilter userspace
>> has to know about. It should either use nfnetlink and remove the proc
>> interface, or remove the connector interface and use proc.
>> Preferrably the former.
>>     
>
> It uses proc to load rules - I do not like it either, but it was the
> simplest way to do so :)

We can rethink that part if it will actually get merged.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 14:26         ` Jan Engelhardt
@ 2008-07-01 14:25           ` Patrick McHardy
  0 siblings, 0 replies; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 14:25 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Evgeniy Polyakov, netdev, netfilter-devel

Jan Engelhardt wrote:
> On Tuesday 2008-07-01 15:08, Evgeniy Polyakov wrote:
>   
>>>> I'm not sure it is that simple. OSF uses common rules database
>>>> shared with OpenBSD (and other *BSDs as well), so converting it into u32
>>>> match would require noticeble efforts. But in theory it is probably
>>>> doable.
>>>>         
>>> This would be preferrable in my opinion since they both allow
>>> programmable filters, but u32 appears to be more flexible. I'm
>>> very reluctant to add new iptables modules that don't increase
>>> expressiveness or provide other clear benefits since we already
>>> have an insane amount of modules.
>>>       
>
> An iptables extension which you can use with -m osf --genre Linux
> but which internally uses xt_u32.ko would be the perfect solution
> ATM IMO. It would require a number of changes to the iptables API
> though...
>   

I agree that this would be much nicer. I assume you would either need
a way to associate multiple matches with a single userspace extension
or a much more intelligent parser in userspace?


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:08       ` Evgeniy Polyakov
  2008-07-01 13:41         ` Patrick McHardy
@ 2008-07-01 14:26         ` Jan Engelhardt
  2008-07-01 14:25           ` Patrick McHardy
  1 sibling, 1 reply; 21+ messages in thread
From: Jan Engelhardt @ 2008-07-01 14:26 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: Patrick McHardy, netdev, netfilter-devel


On Tuesday 2008-07-01 15:08, Evgeniy Polyakov wrote:
>> >
>> >I'm not sure it is that simple. OSF uses common rules database
>> >shared with OpenBSD (and other *BSDs as well), so converting it into u32
>> >match would require noticeble efforts. But in theory it is probably
>> >doable.
>> 
>> This would be preferrable in my opinion since they both allow
>> programmable filters, but u32 appears to be more flexible. I'm
>> very reluctant to add new iptables modules that don't increase
>> expressiveness or provide other clear benefits since we already
>> have an insane amount of modules.

An iptables extension which you can use with -m osf --genre Linux
but which internally uses xt_u32.ko would be the perfect solution
ATM IMO. It would require a number of changes to the iptables API
though...

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 14:16             ` Patrick McHardy
@ 2008-07-01 14:48               ` Evgeniy Polyakov
  2008-07-01 14:54                 ` Patrick McHardy
  0 siblings, 1 reply; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 14:48 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, netfilter-devel

On Tue, Jul 01, 2008 at 04:16:28PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> I truely hope it will be since I'm working (slowly, as time permits)
> on the *tables successor that will implement things like this in
> userspace. Every module we add that adds more complicated logic in
> the kernel will make adding an iptables compat layer harder.

It still is very tempting to implement such things as iptables modules.
For example I consider to create tunnel-like device and iptables target
to implement ip-over-dns tunnel, and I need iptables extension since I
only control single machine outside of my ISP which is not firewalled.
Having new way of writing iptables extensions requires to update
existing machines, which is not possible frequently
(like existing enterprise (r) (c) (tm) solutions...)

> I'd CC the netfilter user list, its likely you'll find some voices
> in favour there :)

:)

> >>Yes, but I don't want to add another interface netfilter userspace
> >>has to know about. It should either use nfnetlink and remove the proc
> >>interface, or remove the connector interface and use proc.
> >>Preferrably the former.
> >>    
> >
> >It uses proc to load rules - I do not like it either, but it was the
> >simplest way to do so :)
> 
> We can rethink that part if it will actually get merged.

Sure.

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 14:48               ` Evgeniy Polyakov
@ 2008-07-01 14:54                 ` Patrick McHardy
  0 siblings, 0 replies; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 14:54 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, netfilter-devel

Evgeniy Polyakov wrote:
> On Tue, Jul 01, 2008 at 04:16:28PM +0200, Patrick McHardy (kaber@trash.net) wrote:
>> I truely hope it will be since I'm working (slowly, as time permits)
>> on the *tables successor that will implement things like this in
>> userspace. Every module we add that adds more complicated logic in
>> the kernel will make adding an iptables compat layer harder.
> 
> It still is very tempting to implement such things as iptables modules.
> For example I consider to create tunnel-like device and iptables target
> to implement ip-over-dns tunnel, and I need iptables extension since I
> only control single machine outside of my ISP which is not firewalled.
> Having new way of writing iptables extensions requires to update
> existing machines, which is not possible frequently
> (like existing enterprise (r) (c) (tm) solutions...)


Yes, I only mean for matching purposes. Target functionality
can usually not be reached through combination.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 13:35         ` Patrick McHardy
  2008-07-01 13:47           ` Evgeniy Polyakov
@ 2008-07-01 15:34           ` Jeff Garzik
  2008-07-01 15:44             ` Patrick McHardy
  1 sibling, 1 reply; 21+ messages in thread
From: Jeff Garzik @ 2008-07-01 15:34 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Evgeniy Polyakov, netdev, netfilter-devel

Patrick McHardy wrote:
> Jeff Garzik wrote:
>> Patrick McHardy wrote:
>>> What about the use cases? I certainly like the idea you suggest in
>>> your blog ("Ever dreamt to block all Linux users in your network
>>> from accessing internet and allow full bandwidth to Windows worm?")
>>> :) But something this easy to evade doesn't seem to provide a real
>>> benefit for a firewall.
>>>
>>> I can see that something like "Block IE6 running on Windows version X"
>>> might be useful (NUFW can do this I think), but that needs support
>>> from the host.
>>
>> Not addressing Evgeniy's module but speaking generally...
>>
>> It sure would be nice for regular socket applications to have an easy, 
>> unprivileged way to query the OS fingerprint information of a given 
>> socket.
> 
> I'm not sure how much OSF depends on the TTL, but doing this
> more than one hop away from the host (or without knowledge of
> the number of hops) makes using the TTL basically impossible.

The userspace version works quite well over the Internet:
http://lcamtuf.coredump.cx/p0f.shtml


>> Speaking purely from a userspace application API perspective, it would 
>> be most useful for an app to be able to stop OSF collection, start OSF 
>> collection, and query OSF stats.  start/stop would be a refcount that 
>> disables in-kernel OSF when not in use.
>>
>> To present a specific use case:  I would like to know if incoming SMTP 
>> connections are Windows or not.  That permits me to better determine 
>> if the incoming connection is a hijacked PC or not -- it becomes a 
>> useful factor in spamassassin scoring.
>>
>> In this case, incoming SMTP is -always- TCP, thus being a TCP-specific 
>> module is not a problem.  You cover a huge swath of apps even if the 
>> module is TCP-specific.
>>
>> Another use case is validating whether a browser is "lying" about its 
>> OS, when parsing HTTP user-agent info, or in general when any remote 
>> agent is "lying" about its OS.  Security software can use that as an 
>> additional red-flag factor. 
> 
> I for one would be much happier to only have netfilter as a user
> of this :)

I'm not sure I understand?  When site A connects to site B via TCP, is 
there a problem letting site A know the OS of site B?

	Jeff




^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 15:34           ` Jeff Garzik
@ 2008-07-01 15:44             ` Patrick McHardy
  0 siblings, 0 replies; 21+ messages in thread
From: Patrick McHardy @ 2008-07-01 15:44 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Evgeniy Polyakov, netdev, netfilter-devel

Jeff Garzik wrote:
> Patrick McHardy wrote:
>> I for one would be much happier to only have netfilter as a user
>> of this :)
> 
> I'm not sure I understand?  When site A connects to site B via TCP, is 
> there a problem letting site A know the OS of site B?

No, I expressed myself very unclear, sorry :) I meant to say
that I would be happier to have netfilter as a user of some
generic infrastructure instead of having all of this implemented
as netfilter module.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 11:39 Passive OS fingerprinting Evgeniy Polyakov
  2008-07-01 11:53 ` Patrick McHardy
@ 2008-07-01 19:56 ` Paul E. McKenney
  2008-07-01 21:21   ` Evgeniy Polyakov
  1 sibling, 1 reply; 21+ messages in thread
From: Paul E. McKenney @ 2008-07-01 19:56 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, netfilter-devel

On Tue, Jul 01, 2008 at 03:39:28PM +0400, Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting iptables (xtables) allows to match incoming
> packets by different sets of SYN-packet and determine, which remote
> system is on the remote end, so you can make decisions based on OS
> type and even version at some degreee and perform various netfilter
> actions based on that knowledge.
> 
> This module compares some data (WS, MSS, options and it's order, ttl, df
> and others) from packets with SYN bit set with dynamically loaded OS
> fingerprints.
> 
> This version existed quite for a while in patch-o-matic(-ng), but
> suddenly was dropped and then only was updated on its own repo:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
> 
> I've updated OSF to match new iptables standards (namely xtables
> support) and present new kernelspace and userspace library files in
> attach.

A few quick questions below.

							Thanx, Paul

> To setup single rule, which will drop and log all Linux incoming
> access one needs to do following steps:
> # insmod ./ipt_osf.ko
> # ./load ./pf.os /proc/sys/net/ipv4/osf
> # iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
> --ttl 2 --connector
> 
> And you will find following lines in dmesg:
> 
> ipt_osf: Linux [2.5-2.6::Linux 2.5/2.6] : aa:aa:aa:aa:32885 -> bb:bb:bb:bb:23 hops=3
> 
> More info can be found on homepage:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
> 
> Enjoy!
> 
> Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> 
> -- 
> 	Evgeniy Polyakov

> /*
>  * ipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
>  *
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>  */
> 
> #include <linux/module.h>
> #include <linux/kernel.h>
> 
> #include <linux/types.h>
> #include <linux/string.h>
> #include <linux/smp.h>
> #include <linux/skbuff.h>
> #include <linux/file.h>
> #include <linux/ip.h>
> #include <linux/proc_fs.h>
> #include <linux/fs.h>
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/ctype.h>
> #include <linux/list.h>
> #include <linux/if.h>
> #include <linux/inetdevice.h>
> #include <net/ip.h>
> #include <linux/tcp.h>
> 
> #include <linux/netfilter/x_tables.h>
> #include <linux/netfilter_ipv4/ip_tables.h>
> #include "ipt_osf.h"
> 
> #define OSF_DEBUG
> 
> #ifdef OSF_DEBUG
> #define log(x...) 		printk(KERN_INFO "ipt_osf: " x)
> #define loga(x...) 		printk(x)
> #else
> #define log(x...) 		do {} while(0)
> #define loga(x...) 		do {} while(0)
> #endif
> 
> #define FMATCH_WRONG		0
> #define FMATCH_OK		1
> #define FMATCH_OPT_WRONG	2
> 
> #define OPTDEL			','
> #define OSFPDEL 		':'
> #define MAXOPTSTRLEN		128
> #define OSFFLUSH		"FLUSH"
> 
> static DEFINE_SPINLOCK(ipt_osf_lock);
> static LIST_HEAD(ipt_finger_list);
> 
> #ifdef CONFIG_CONNECTOR
> #include <linux/connector.h>
> 
> /*
>  * They should live in connector.h.
>  */
> #define CN_IDX_OSF		0x0008
> #define CN_VAL_OSF		0x0000
> 
> static char osf_finger_buf[sizeof(struct ipt_osf_nlmsg) +
> 			   sizeof(struct cn_msg)];
> static struct cb_id osf_id = { CN_IDX_OSF, CN_VAL_OSF };
> static u32 osf_seq;
> 
> static void ipt_osf_send_connector(struct ipt_osf_finger *f,
> 				   const struct sk_buff *sk)
> {
> 	struct cn_msg *m;
> 	struct ipt_osf_nlmsg *data;
> 
> 	m = (struct cn_msg *)osf_finger_buf;
> 	data = (struct ipt_osf_nlmsg *)(m + 1);
> 
> 	memcpy(&m->id, &osf_id, sizeof(m->id));
> 	m->seq = osf_seq++;
> 	m->ack = 0;
> 	m->len = sizeof(*data);
> 
> 	memcpy(&data->f, f, sizeof(struct ipt_osf_finger));
> 	memcpy(&data->ip, sk->nh.iph, sizeof(struct iphdr));
> 	memcpy(&data->tcp,
> 	       (struct tcphdr *)((u32 *)sk->nh.iph + sk->nh.iph->ihl),
> 	       sizeof(struct tcphdr));
> 
> 	cn_netlink_send(m, m->id.idx, GFP_ATOMIC);
> }
> #else
> static void ipt_osf_send_connector(struct ipt_osf_finger *f,
> 				   const struct sk_buff *sk)
> {
> }
> #endif
> 
> static char *ipt_osf_strchr(char *ptr, char c)
> {
> 	char *tmp;
> 
> 	tmp = strchr(ptr, c);
> 
> 	while (tmp && tmp + 1 && isspace(*(tmp + 1)))
> 		tmp++;
> 
> 	return tmp;
> }
> 
> static struct ipt_osf_finger *ipt_osf_finger_alloc(void)
> {
> 	return kzalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
> }
> 
> static void ipt_osf_finger_free(struct ipt_osf_finger *f)
> {
> 	memset(f, 0, sizeof(struct ipt_osf_finger));
> 	kfree(f);
> }
> 
> static void ipt_osf_parse_opt(struct ipt_osf_opt *opt, int *optnum, char *obuf,
> 			  int olen)
> {
> 	int i, op;
> 	char *ptr, wc;
> 	unsigned long val;
> 
> 	ptr = &obuf[0];
> 	i = 0;
> 	while (ptr != NULL && i < olen) {
> 		val = 0;
> 		op = 0;
> 		wc = 0;
> 		switch (obuf[i]) {
> 		case 'N':
> 			op = OSFOPT_NOP;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'S':
> 			op = OSFOPT_SACKP;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'T':
> 			op = OSFOPT_TS;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'W':
> 			op = OSFOPT_WSO;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				switch (obuf[i + 1]) {
> 				case '%':
> 					wc = '%';
> 					break;
> 				case 'S':
> 					wc = 'S';
> 					break;
> 				case 'T':
> 					wc = 'T';
> 					break;
> 				default:
> 					wc = 0;
> 					break;
> 				}
> 
> 				*ptr = '\0';
> 				ptr++;
> 				if (wc)
> 					val = simple_strtoul(&obuf[i + 2], 
> 							NULL, 10);
> 				else
> 					val = simple_strtoul(&obuf[i + 1], 
> 							NULL, 10);
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'M':
> 			op = OSFOPT_MSS;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				if (obuf[i + 1] == '%')
> 					wc = '%';
> 				*ptr = '\0';
> 				ptr++;
> 				if (wc)
> 					val = simple_strtoul(&obuf[i + 2], 
> 							NULL, 10);
> 				else
> 					val = simple_strtoul(&obuf[i + 1], 
> 							NULL, 10);
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		case 'E':
> 			op = OSFOPT_EOL;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				*ptr = '\0';
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		default:
> 			op = OSFOPT_EMPTY;
> 			ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> 			if (ptr) {
> 				ptr++;
> 				i += (int)(ptr - &obuf[i]);
> 
> 			} else
> 				i++;
> 			break;
> 		}
> 
> 		if (op != OSFOPT_EMPTY) {
> 			opt[*optnum].kind = IANA_opts[op].kind;
> 			opt[*optnum].length = IANA_opts[op].length;
> 			opt[*optnum].wc.wc = wc;
> 			opt[*optnum].wc.val = val;
> 			(*optnum)++;
> 		}
> 	}
> }
> 
> static int ipt_osf_proc_read(char *buf, char **start, off_t off, int count,
> 			 int *eof, void *data)
> {
> 	struct ipt_osf_finger *f = NULL;
> 	int i, __count, err;
> 
> 	*eof = 1;
> 	__count = count;
> 	count = 0;
> 
> 	rcu_read_lock();
> 	list_for_each_entry(f, &ipt_finger_list, flist) {

Does the above need to be list_for_each_entry_rcu()?

> 		log("%s [%s]", f->genre, f->details);
> 
> 		err = snprintf(buf + count, __count - count, "%s - %s[%s] : %s",
> 			       f->genre, f->version, f->subtype, f->details);
> 		if (err == 0 || __count <= count + err)
> 			break;
> 		else
> 			count += err;
> 		if (f->opt_num) {
> 			loga(" OPT: ");
> 			/* count += sprintf(buf+count, " OPT: "); */
> 			for (i = 0; i < f->opt_num; ++i) {
> 				/*
> 				count += sprintf(buf+count, "%d.%c%u; ", 
> 					f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ', 
> 					f->opt[i].wc.val);
> 				*/
> 				loga("%d.%c%u; ",
> 				     f->opt[i].kind,
> 				     (f->opt[i].wc.wc) ? f->opt[i].wc.wc : ' ',
> 				     f->opt[i].wc.val);
> 			}
> 		}
> 		loga("\n");
> 		err = snprintf(buf + count, __count - count, "\n");
> 		if (err == 0 || __count <= count + err)
> 			break;
> 		else
> 			count += err;
> 	}
> 	rcu_read_unlock();
> 
> 	return count;
> }
> 
> static int ipt_osf_proc_write(struct file *file, const char *buffer,
> 			  unsigned long count, void *data)
> {
> 	int cnt, i;
> 	char obuf[MAXOPTSTRLEN];
> 	struct ipt_osf_finger *finger, *n;
> 	char *pbeg, *pend;
> 
> 	if (count > 0xffff)
> 		return -E2BIG;
> 
> 	if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) {
> 		int i = 0;
> 		synchronize_rcu();
> 		spin_lock_bh(&ipt_osf_lock);
> 		list_for_each_entry_safe(finger, n, &ipt_finger_list, flist) {

This is OK -- we hold the update-side lock, so don't need _rcu.

> 			i++;
> 			list_del_rcu(&finger->flist);
> 			ipt_osf_finger_free(finger);

Why is it safe to immediately free the element that we just removed from
an RCU-protected list?  The above synchronize_rcu() won't help us given
that it is still in the list at that point.

This could be fixed by using call_rcu() in ipt_osf_finger_free().

> 		}
> 		spin_unlock_bh(&ipt_osf_lock);
> 
> 		log("Flushed %d entries.\n", i);
> 
> 		return count;
> 	}
> 
> 	cnt = 0;
> 	for (i = 0; i < count && buffer[i] != '\0'; ++i)
> 		if (buffer[i] == ':')
> 			cnt++;
> 
> 	if (cnt != 8 || i != count) {
> 		log("Wrong input line cnt=%d[8], len=%u[%lu]\n",
> 		    cnt, i, count);
> 		return count;
> 	}
> 
> 	memset(obuf, 0, sizeof(obuf));
> 
> 	finger = ipt_osf_finger_alloc();
> 	if (!finger) {
> 		log("Failed to allocate new fingerprint entry.\n");
> 		return -ENOMEM;
> 	}
> 
> 	pbeg = (char *)buffer;
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		if (pbeg[0] == 'S') {
> 			finger->wss.wc = 'S';
> 			if (pbeg[1] == '%')
> 				finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
> 			else if (pbeg[1] == '*')
> 				finger->wss.val = 0;
> 			else
> 				finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> 		} else if (pbeg[0] == 'T') {
> 			finger->wss.wc = 'T';
> 			if (pbeg[1] == '%')
> 				finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
> 			else if (pbeg[1] == '*')
> 				finger->wss.val = 0;
> 			else
> 				finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> 		} else if (pbeg[0] == '%') {
> 			finger->wss.wc = '%';
> 			finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> 		} else if (isdigit(pbeg[0])) {
> 			finger->wss.wc = 0;
> 			finger->wss.val = simple_strtoul(&pbeg[0], NULL, 10);
> 		}
> 
> 		pbeg = pend + 1;
> 	}
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		finger->ttl = simple_strtoul(pbeg, NULL, 10);
> 		pbeg = pend + 1;
> 	}
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		finger->df = simple_strtoul(pbeg, NULL, 10);
> 		pbeg = pend + 1;
> 	}
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		finger->ss = simple_strtoul(pbeg, NULL, 10);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		if (pbeg[0] == '@' || pbeg[0] == '*')
> 			cnt = snprintf(finger->genre, sizeof(finger->genre), 
> 					"%s", pbeg + 1);
> 		else
> 			cnt = snprintf(finger->genre, sizeof(finger->genre), 
> 					"%s", pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		cnt =
> 		    snprintf(finger->version, sizeof(finger->version), "%s",
> 			     pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	pend = ipt_osf_strchr(pbeg, OSFPDEL);
> 	if (pend) {
> 		*pend = '\0';
> 		cnt =
> 		    snprintf(finger->subtype, sizeof(finger->subtype), "%s",
> 			     pbeg);
> 		pbeg = pend + 1;
> 	}
> 
> 	cnt = snprintf(finger->details,
> 		       ((count - (pbeg - buffer) + 1) >
> 			MAXDETLEN) ? MAXDETLEN : (count - (pbeg - buffer) + 1),
> 		       "%s", pbeg);
> 
> 	log("%s - %s[%s] : %s\n",
> 	    finger->genre, finger->version, finger->subtype, finger->details);
> 
> 	ipt_osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
> 
> 	synchronize_rcu();

I don't understand what this synchronize_rcu() is doing for us.

Is the idea to make sure that all RCU readers see the prior deletes as
having happened before we do the below addition?  If so, please add a
comment to this effect.

> 	spin_lock_bh(&ipt_osf_lock);
> 	list_add_tail_rcu(&finger->flist, &ipt_finger_list);
> 	spin_unlock_bh(&ipt_osf_lock);
> 
> 	return count;
> }
> 
> static inline int ipt_osf_ttl(const struct sk_buff *skb, struct ipt_osf_info *info,
> 			    unsigned char f_ttl)
> {
> 	struct iphdr *ip = ip_hdr(skb);
> #if 0
> 	log("f_ttl: %u, ip_ttl: %u, info->ttl: %u, flags_ttl: %u.\n",
> 			f_ttl, ip->ttl, info->ttl, info->flags & IPT_OSF_TTL);
> #endif
> 	if (info->flags & IPT_OSF_TTL) {
> 		if (info->ttl == IPT_OSF_TTL_TRUE)
> 			return (ip->ttl == f_ttl);
> 		if (info->ttl == IPT_OSF_TTL_NOCHECK)
> 			return 1;
> 		else {
> 			struct in_device *in_dev = in_dev_get(skb->dev);
> 
> 			for_ifa(in_dev) {
> 				if (inet_ifa_match(ip->saddr, ifa)) {
> 					in_dev_put(in_dev);
> 					return (ip->ttl == f_ttl);
> 				}
> 			}
> 			endfor_ifa(in_dev);
> 
> 			in_dev_put(in_dev);
> 			return (ip->ttl <= f_ttl);
> 		}
> 	}
> 	
> 	return (ip->ttl == f_ttl);
> }
> 
> static bool
> ipt_osf_match_packet(const struct sk_buff *skb,
> 		const struct net_device *in,
> 		const struct net_device *out,
> 		const struct xt_match *match,
> 		const void *matchinfo,
> 		int offset,
> 		unsigned int unused,
> 		bool *hotdrop)
> {
> 	struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
> 	struct iphdr _iph, *ip;
> 	struct tcphdr _tcph, *tcp;
> 	int fmatch = FMATCH_WRONG, fcount = 0;
> 	unsigned int optsize = 0, check_WSS = 0;
> 	u16 window, totlen, mss = 0;
> 	unsigned char df, *optp = NULL, *_optp = NULL;
> 	unsigned char opts[MAX_IPOPTLEN];
> 	struct ipt_osf_finger *f;
> 
> 	if (!info)
> 		return 0;
> 
> 	ip = skb_header_pointer(skb, 0, sizeof(struct iphdr), &_iph);
> 	if (!ip)
> 		return 0;
> 
> 	tcp = skb_header_pointer(skb, ip->ihl * 4, sizeof(struct tcphdr), &_tcph);
> 	if (!tcp)
> 		return 0;
> 
> 	if (!tcp->syn)
> 		return 0;
> 
> 	totlen = ntohs(ip->tot_len);
> 	df = ((ntohs(ip->frag_off) & IP_DF) ? 1 : 0);
> 	window = ntohs(tcp->window);
> 
> 	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
> 		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
> 
> 		if (optsize > sizeof(opts)) {
> 			log("%s: BUG: too big options size: optsize=%u, max=%zu.\n", 
> 					__func__, optsize, sizeof(opts));
> 			optsize = sizeof(opts);
> 		}
> 
> 		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
> 				optsize, opts);
> 	}
> 
> 	rcu_read_lock();
> 	list_for_each_entry_rcu(f, &ipt_finger_list, flist) {
> 		if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
> 			continue;
> 
> 		optp = _optp;
> 		fmatch = FMATCH_WRONG;
> 
> 		if (totlen == f->ss && df == f->df && ipt_osf_ttl(skb, info, f->ttl)) {
> 			int foptsize, optnum;
> 
> 			check_WSS = 0;
> 
> 			switch (f->wss.wc) {
> 			case 0:
> 				check_WSS = 0;
> 				break;
> 			case 'S':
> 				check_WSS = 1;
> 				break;
> 			case 'T':
> 				check_WSS = 2;
> 				break;
> 			case '%':
> 				check_WSS = 3;
> 				break;
> 			default:
> 				log("Wrong fingerprint wss.wc=%d, %s - %s\n",
> 				    f->wss.wc, f->genre, f->details);
> 				check_WSS = 4;
> 				break;
> 			}
> 			if (check_WSS == 4)
> 				continue;
> 
> 			/* Check options */
> 
> 			foptsize = 0;
> 			for (optnum = 0; optnum < f->opt_num; ++optnum)
> 				foptsize += f->opt[optnum].length;
> #if 0
> 			log("%s.%s.%s: optsize: %u, foptsize: %u, fopt_num: %u, optp: %p, win: %u, mss: %u, totlen: %u, df: %d, ttl: %u.\n", 
> 					f->genre, f->version, f->subtype, optsize, foptsize, f->opt_num, optp,
> 					window, mss, totlen, df, ip->ttl);
> #endif
> 			if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
> 				continue;
> 
> 			for (optnum = 0; optnum < f->opt_num; ++optnum) {
> 				if (f->opt[optnum].kind == (*optp)) {
> 					__u32 len = f->opt[optnum].length;
> 					__u8 *optend = optp + len;
> 					int loop_cont = 0;
> 					
> 					fmatch = FMATCH_OK;
> 
> 					switch (*optp) {
> 					case OSFOPT_MSS:
> 						mss = ntohs(*(u16 *)(optp + 2));
> 						break;
> 					case OSFOPT_TS:
> 						loop_cont = 1;
> 						break;
> 					}
> 
> #if 0
> 					if (loop_cont) {
> 						optp = optend;
> 						continue;
> 					}
> 					if (len != 1) {
> 						/* Skip kind and length fields */
> 						optp += 2;
> 
> 						if (f->opt[optnum].wc.val != 0) {
> 							u32 tmp = 0;
> 							u32 copy = len > 4 ? 4 : len;
> 
> 							/* Hmmm... It looks a bit ugly. :) */
> 							memcpy(&tmp, optp, copy);
> 
> 							/* 2 + 2: optlen(2 bytes) + 
> 							 *      kind(1 byte) + length(1 byte) */
> 							if (len == 4)
> 								tmp = ntohs(tmp);
> 							else
> 								tmp = ntohl(tmp);
> 
> 							if (f->opt[optnum].wc.wc == '%') {
> 								if ((tmp % f->opt[optnum].wc.val) != 0)
> 									fmatch = FMATCH_OPT_WRONG;
> 							} else if (tmp != f->opt[optnum].wc.val)
> 								fmatch = FMATCH_OPT_WRONG;
> 						}
> 					}
> #endif
> 					optp = optend;
> 				} else
> 					fmatch = FMATCH_OPT_WRONG;
> 
> 				if (fmatch != FMATCH_OK)
> 					break;
> 			}
> 
> 			if (fmatch != FMATCH_OPT_WRONG) {
> 				fmatch = FMATCH_WRONG;
> 
> 				switch (check_WSS) {
> 				case 0:
> 					if (f->wss.val == 0 || window == f->wss.val)
> 						fmatch = FMATCH_OK;
> 					break;
> 				case 1:	/* MSS */
> #define SMART_MSS_1	1460
> #define SMART_MSS_2	1448
> 					if (window == f->wss.val * mss ||
> 					    window == f->wss.val * SMART_MSS_1 ||
> 					    window == f->wss.val * SMART_MSS_2)
> 						fmatch = FMATCH_OK;
> 					break;
> 				case 2:	/* MTU */
> 					if (window == f->wss.val * (mss + 40) ||
> 					    window == f->wss.val * (SMART_MSS_1 + 40) ||
> 					    window == f->wss.val * (SMART_MSS_2 + 40))
> 						fmatch = FMATCH_OK;
> 					break;
> 				case 3:	/* MOD */
> 					if ((window % f->wss.val) == 0)
> 						fmatch = FMATCH_OK;
> 					break;
> 				}
> 			}
> 
> 			if (fmatch == FMATCH_OK) {
> 				fcount++;
> 				log("%s [%s:%s:%s] : %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n", 
> 						f->genre, f->version, f->subtype, f->details, 
> 						NIPQUAD(ip->saddr), ntohs(tcp->source), 
> 						NIPQUAD(ip->daddr), ntohs(tcp->dest), 
> 						f->ttl - ip->ttl);
> 				if (info->flags & IPT_OSF_CONNECTOR)
> 					ipt_osf_send_connector(f, skb);
> 
> 				if ((info->flags & IPT_OSF_LOG) &&
> 				    info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
> 					break;
> 			}
> 		}
> 	}
> 	rcu_read_unlock();
> 
> 	if (!fcount && (info->flags & (IPT_OSF_LOG | IPT_OSF_NETLINK | IPT_OSF_CONNECTOR))) {
> 		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
> 		unsigned int i, optsize;
> 		struct ipt_osf_finger fg;
> 
> 		memset(&fg, 0, sizeof(fg));
> #if 1
> 		if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
> 			log("Unknown: win: %u, mss: %u, totlen: %u, df: %d, ttl: %u : ", window, mss, totlen, df, ip->ttl);
> 		if (optp) {
> 			optsize = tcp->doff * 4 - sizeof(struct tcphdr);
> 			if (skb_copy_bits(skb, ip->ihl * 4 + sizeof(struct tcphdr),
> 					opt, optsize) < 0) {
> 				if (info->flags & IPT_OSF_LOG)
> 					loga("TRUNCATED");
> 				if (info->flags & IPT_OSF_NETLINK)
> 					strcpy(fg.details, "TRUNCATED");
> 			} else {
> 				for (i = 0; i < optsize; i++) {
> 					if (info->flags & IPT_OSF_LOG)
> 						loga("%02X ", opt[i]);
> 				}
> 				if (info->flags & IPT_OSF_NETLINK)
> 					memcpy(fg.details, opt, min_t(unsigned int, optsize, MAXDETLEN));
> 			}
> 		}
> 		if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
> 			loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
> 			     NIPQUAD(ip->saddr), ntohs(tcp->source),
> 			     NIPQUAD(ip->daddr), ntohs(tcp->dest));
> #endif
> 		if (info->flags & (IPT_OSF_NETLINK | IPT_OSF_CONNECTOR)) {
> 			fg.wss.val = window;
> 			fg.ttl = ip->ttl;
> 			fg.df = df;
> 			fg.ss = totlen;
> 			fg.mss = mss;
> 			strncpy(fg.genre, "Unknown", MAXGENRELEN);
> 
> 			if (info->flags & IPT_OSF_CONNECTOR)
> 				ipt_osf_send_connector(&fg, skb);
> 		}
> 	}
> 
> 	if (fcount)
> 		fmatch = FMATCH_OK;
> 
> 	return (fmatch == FMATCH_OK) ? 1 : 0;
> }
> 
> static bool
> ipt_osf_checkentry(const char *tablename,
> 		const void *data,
> 		const struct xt_match *match,
> 		void *matchinfo,
> 		unsigned int hook_mask)
> {
> 	struct ipt_ip *ip = (struct ipt_ip *)data;
> 
> 	if (ip->proto != IPPROTO_TCP)
> 		return false;
> 
> 	return true;
> }
> 
> static struct xt_match ipt_osf_match = {
> 	.name 		= "osf",
> 	.revision	= 0,
> 	.family		= AF_INET,
> 	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING),
> 	.match 		= ipt_osf_match_packet,
> 	.checkentry	= ipt_osf_checkentry,
> 	.matchsize	= sizeof(struct ipt_osf_info),
> 	.me		= THIS_MODULE,
> };
> 
> static int __devinit ipt_osf_init(void)
> {
> 	int err = -EINVAL;
> 	struct proc_dir_entry *p;
> 
> 	log("Startng OS fingerprint matching module.\n");
> 
> 	p = create_proc_entry("osf", S_IFREG | 0644, proc_net_netfilter);
> 	if (!p)
> 		goto err_out_exit;
> 
> 	p->write_proc = ipt_osf_proc_write;
> 	p->read_proc = ipt_osf_proc_read;
> 
> 	err = xt_register_match(&ipt_osf_match);
> 	if (err) {
> 		log("Failed to register OS fingerprint matching module.\n");
> 		goto err_out_remove;
> 	}
> 
> 	return 0;
> 
> err_out_remove:
> 	remove_proc_entry("osf", proc_net_netfilter);
> err_out_exit:
> 	return err;
> }
> 
> static void __devexit ipt_osf_fini(void)
> {
> 	struct ipt_osf_finger *f, *n;
> 
> 	remove_proc_entry("osf", proc_net_netfilter);
> 	xt_unregister_match(&ipt_osf_match);
> 
> 	list_for_each_entry_safe(f, n, &ipt_finger_list, flist) {

Why is it safe to do the above without being in an RCU read-side critical
section?  Have all possible RCU readers somehow been banished?  If so,
how have they been banished?

> 		list_del(&f->flist);

Ditto for why the list_del() is safe without the update-side lock and
why it doesn't have to be list_del_rcu().

> 		ipt_osf_finger_free(f);
> 	}
> 
> 	log("OS fingerprint matching module finished.\n");
> }
> 
> module_init(ipt_osf_init);
> module_exit(ipt_osf_fini);
> 
> MODULE_LICENSE("GPL");
> MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
> MODULE_DESCRIPTION("Passive OS fingerprint matching.");

> /*
>  * ipt_osf.h
>  *
>  * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
>  *
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>  */
> 
> #ifndef _IPT_OSF_H
> #define _IPT_OSF_H
> 
> #define MAXGENRELEN		32
> #define MAXDETLEN		64
> 
> #define IPT_OSF_GENRE		1
> #define	IPT_OSF_TTL		2
> #define IPT_OSF_LOG		4
> #define IPT_OSF_NETLINK		8
> #define IPT_OSF_CONNECTOR	16
> #define IPT_OSF_INVERT		32
> 
> #define IPT_OSF_LOGLEVEL_ALL	0
> #define IPT_OSF_LOGLEVEL_FIRST	1
> #define IPT_OSF_LOGLEVEL_ALL_KNOWN	2
> 
> #define IPT_OSF_TTL_TRUE	0	/* True ip and fingerprint TTL comparison */
> #define IPT_OSF_TTL_LESS	1	/* Check if ip TTL is less than fingerprint one */
> #define IPT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
> 
> #ifndef __KERNEL__
> #include <netinet/ip.h>
> #include <netinet/tcp.h>
> 
> struct list_head {
> 	struct list_head *prev, *next;
> };
> #endif
> 
> struct ipt_osf_info {
> 	char genre[MAXGENRELEN];
> 	__u32 len, flags, loglevel, ttl;
> } __attribute__ ((packed));
> 
> struct ipt_osf_wc {
> 	__u8 wc;
> 	__u32 val;
> } __attribute__ ((packed));
> 
> /* This struct represents IANA options
>  * http://www.iana.org/assignments/tcp-parameters
>  */
> struct ipt_osf_opt {
> 	__u8 kind, length;
> 	struct ipt_osf_wc wc;
> } __attribute__ ((packed));
> 
> struct ipt_osf_finger {
> 	struct list_head flist;
> 	struct ipt_osf_wc wss;
> 	__u8 ttl, df;
> 	__u16 ss, mss;
> 	unsigned char genre[MAXGENRELEN];
> 	unsigned char version[MAXGENRELEN], subtype[MAXGENRELEN];
> 
> 	/* Not needed, but for consistency with original table from Michal Zalewski */
> 	unsigned char details[MAXDETLEN];
> 
> 	int opt_num;
> 	struct ipt_osf_opt opt[MAX_IPOPTLEN];	/* In case it is all NOP or EOL */
> 
> } __attribute__ ((packed));
> 
> struct ipt_osf_nlmsg {
> 	struct ipt_osf_finger f;
> 	struct iphdr ip;
> 	struct tcphdr tcp;
> } __attribute__ ((packed));
> 
> #ifdef __KERNEL__
> 
> #include <linux/list.h>
> #include <net/tcp.h>
> 
> /* Defines for IANA option kinds */
> 
> #define OSFOPT_EOL		0	/* End of options */
> #define OSFOPT_NOP		1	/* NOP */
> #define OSFOPT_MSS		2	/* Maximum segment size */
> #define OSFOPT_WSO		3	/* Window scale option */
> #define OSFOPT_SACKP		4	/* SACK permitted */
> #define OSFOPT_SACK		5	/* SACK */
> #define OSFOPT_ECHO		6
> #define OSFOPT_ECHOREPLY	7
> #define OSFOPT_TS		8	/* Timestamp option */
> #define OSFOPT_POCP		9	/* Partial Order Connection Permitted */
> #define OSFOPT_POSP		10	/* Partial Order Service Profile */
> #define OSFOPT_EMPTY		255
> /* Others are not used in current OSF */
> 
> static struct ipt_osf_opt IANA_opts[] = {
> 	{0, 1,},
> 	{1, 1,},
> 	{2, 4,},
> 	{3, 3,},
> 	{4, 2,},
> 	{5, 1,},		/* SACK length is not defined */
> 	{6, 6,},
> 	{7, 6,},
> 	{8, 10,},
> 	{9, 2,},
> 	{10, 3,},
> 	{11, 1,},		/* CC: Suppose 1 */
> 	{12, 1,},		/* the same */
> 	{13, 1,},		/* and here too */
> 	{14, 3,},
> 	{15, 1,},		/* TCP Alternate Checksum Data. Length is not defined */
> 	{16, 1,},
> 	{17, 1,},
> 	{18, 3,},
> 	{19, 18,},
> 	{20, 1,},
> 	{21, 1,},
> 	{22, 1,},
> 	{23, 1,},
> 	{24, 1,},
> 	{25, 1,},
> 	{26, 1,},
> };
> 
> #endif				/* __KERNEL__ */
> 
> #endif				/* _IPT_OSF_H */

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
>  *
>  *
>  * This program is free software; you can redistribute it and/or modify
>  * it under the terms of the GNU General Public License as published by
>  * the Free Software Foundation; either version 2 of the License, or
>  * (at your option) any later version.
>  *
>  * This program is distributed in the hope that it will be useful,
>  * but WITHOUT ANY WARRANTY; without even the implied warranty of
>  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>  * GNU General Public License for more details.
>  *
>  * You should have received a copy of the GNU General Public License
>  * along with this program; if not, write to the Free Software
>  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>  */
> 
> /*
>  * iptables interface for OS fingerprint matching module.
>  */
> 
> #include <stdio.h>
> #include <netdb.h>
> #include <string.h>
> #include <stdlib.h>
> #include <getopt.h>
> #include <ctype.h>
> 
> #include <iptables.h>
> 
> typedef unsigned int __u32;
> typedef unsigned short __u16;
> typedef unsigned char __u8;
> 
> #include "ipt_osf.h"
> 
> static void osf_help(void)
> {
> 	printf("OS fingerprint match options:\n"
> 		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
> 		"--ttl			Use some TTL check extensions to determine OS:\n"
> 		"	0			true ip and fingerprint TTL comparison. Works for LAN.\n"
> 		"	1			check if ip TTL is less than fingerprint one. Works for global addresses.\n"
> 		"	2			do not compare TTL at all. Allows to detect NMAP, but can produce false results.\n"
> 		"--log level		Log determined genres into dmesg even if they do not match desired one:\n"
> 		"	0			log all matched or unknown signatures.\n"
> 		"	1			log only first one.\n"
> 		"	2			log all known matched signatures.\n"
> 		"--connector		Log through kernel connector [2.6.14+].\n\n"
> 		);
> }
> 
> 
> static const struct option osf_opts[] = {
> 	{ .name = "genre",	.has_arg = 1, .flag = 0, .val = '1' },
> 	{ .name = "ttl",	.has_arg = 1, .flag = 0, .val = '2' },
> 	{ .name = "log",	.has_arg = 1, .flag = 0, .val = '3' },
> 	{ .name = "netlink",	.has_arg = 0, .flag = 0, .val = '4' },
> 	{ .name = "connector",	.has_arg = 0, .flag = 0, .val = '5' },
> 	{ .name = NULL }
> };
> 
> 
> static void osf_init(struct xt_entry_match *m)
> {
> }
> 
> static void osf_parse_string(const unsigned char *s, struct ipt_osf_info *info)
> {
> 	if (strlen(s) < MAXGENRELEN) 
> 		strcpy(info->genre, s);
> 	else 
> 		exit_error(PARAMETER_PROBLEM, "Genre string too long `%s' [%d], max=%d", 
> 				s, strlen(s), MAXGENRELEN);
> }
> 
> static int osf_parse(int c, char **argv, int invert, unsigned int *flags,
>       			const void *entry,
>       			struct xt_entry_match **match)
> {
> 	struct ipt_osf_info *info = (struct ipt_osf_info *)(*match)->data;
> 	
> 	switch(c) 
> 	{
> 		case '1': /* --genre */
> 			if (*flags & IPT_OSF_GENRE)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple genre parameter");
> 			check_inverse(optarg, &invert, &optind, 0);
> 			osf_parse_string(argv[optind-1], info);
> 			if (invert)
> 				info->flags |= IPT_OSF_INVERT;
> 			info->len=strlen((char *)info->genre);
> 			*flags |= IPT_OSF_GENRE;
> 			break;
> 		case '2': /* --ttl */
> 			if (*flags & IPT_OSF_TTL)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple ttl parameter");
> 			*flags |= IPT_OSF_TTL;
> 			info->flags |= IPT_OSF_TTL;
> 			info->ttl = atoi(argv[optind-1]);
> 			break;
> 		case '3': /* --log */
> 			if (*flags & IPT_OSF_LOG)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
> 			*flags |= IPT_OSF_LOG;
> 			info->loglevel = atoi(argv[optind-1]);
> 			info->flags |= IPT_OSF_LOG;
> 			break;
> 		case '4': /* --netlink */
> 			if (*flags & IPT_OSF_NETLINK)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple netlink parameter");
> 			*flags |= IPT_OSF_NETLINK;
> 			info->flags |= IPT_OSF_NETLINK;
> 			break;
> 		case '5': /* --connector */
> 			if (*flags & IPT_OSF_CONNECTOR)
> 				exit_error(PARAMETER_PROBLEM, "Can't specify multiple connector parameter");
> 			*flags |= IPT_OSF_CONNECTOR;
> 			info->flags |= IPT_OSF_CONNECTOR;
> 			break;
> 		default:
> 			return 0;
> 	}
> 
> 	return 1;
> }
> 
> static void osf_final_check(unsigned int flags)
> {
> 	if (!flags)
> 		exit_error(PARAMETER_PROBLEM, "OS fingerprint match: You must specify `--genre'");
> }
> 
> static void osf_print(const void *ip, const struct xt_entry_match *match, int numeric)
> {
> 	const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
> 
> 	printf("OS fingerprint match %s%s ", (info->flags & IPT_OSF_INVERT) ? "!" : "", info->genre);
> }
> 
> static void osf_save(const void *ip, const struct xt_entry_match *match)
> {
> 	const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
> 
> 	printf("--genre %s%s ", (info->flags & IPT_OSF_INVERT) ? "! ": "", info->genre);
> }
> 
> 
> static struct xtables_match osf_match = {
>     .name		= "osf",
>     .version		= XTABLES_VERSION,
>     .size		= XT_ALIGN(sizeof(struct ipt_osf_info)),
>     .userspacesize	= XT_ALIGN(sizeof(struct ipt_osf_info)),
>     .help		= &osf_help,
>     .init		= &osf_init,
>     .parse		= &osf_parse,
>     .print		= &osf_print,
>     .final_check	= &osf_final_check,
>     .save		= &osf_save,
>     .extra_opts		= osf_opts
> };
> 
> 
> void _init(void)
> {
> 	xtables_register_match(&osf_match);
> }

> obj-m		:= ipt_osf.o
> 
> KDIR	:= /lib/modules/$(shell uname -r)/build
> IPTABLES:= /usr/include
> 
> ifeq ($(IPTABLES),path_to_iptables_sources_or_header_files)
> $(error "Edit IPTABLES variable in Makefile and read README")
> endif
> PWD	:= $(shell pwd)
> 
> iptables_version=$(shell (/sbin/iptables -V | awk {'print $$2'} | cut -c 2-))
> LCFLAGS = -DIPTABLES_VERSION=\"$(iptables_version)\"
> 
> default:
> 	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
> 
> clean:
> 	rm -f *.o *.ko *.mod.* .*.cmd *~
> 	rm -rf .tmp_versions
> 	rm -f *.o *~ *.so *.ko load osfd ucon_osf
> 
> lib:	libipt_osf.c ipt_osf.h
> 	gcc $(LCFLAGS) libipt_osf.c -D_INIT=_init -fPIC -I$(IPTABLES)/include -c -o libipt_osf.o
> 	gcc -shared -nostdlib -rdynamic -Wl,-soname,libipt_osf.so -o libipt_osf.so libipt_osf.o
> 
> bin:	osfd.c load.c ucon_osf.c
> 	gcc -W -Wall osfd.c -o osfd
> 	gcc -W -Wall load.c -o load
> 	gcc -W -Wall ucon_osf.c -o ucon_osf


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
  2008-07-01 19:56 ` Paul E. McKenney
@ 2008-07-01 21:21   ` Evgeniy Polyakov
       [not found]     ` <20080701224149.GA8449@linux.vnet.ibm.com>
  0 siblings, 1 reply; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-01 21:21 UTC (permalink / raw)
  To: Paul E. McKenney; +Cc: netdev, netfilter-devel

Hi Paul.

On Tue, Jul 01, 2008 at 12:56:48PM -0700, Paul E. McKenney (paulmck@linux.vnet.ibm.com) wrote:
> > static int ipt_osf_proc_read(char *buf, char **start, off_t off, int count,
> > 			 int *eof, void *data)
> > {
> > 	struct ipt_osf_finger *f = NULL;
> > 	int i, __count, err;
> > 
> > 	*eof = 1;
> > 	__count = count;
> > 	count = 0;
> > 
> > 	rcu_read_lock();
> > 	list_for_each_entry(f, &ipt_finger_list, flist) {
> 
> Does the above need to be list_for_each_entry_rcu()?

Yeah, I messed with procfs handlers.

...

> > static int ipt_osf_proc_write(struct file *file, const char *buffer,
> > 			  unsigned long count, void *data)
> > {
> > 	int cnt, i;
> > 	char obuf[MAXOPTSTRLEN];
> > 	struct ipt_osf_finger *finger, *n;
> > 	char *pbeg, *pend;
> > 
> > 	if (count > 0xffff)
> > 		return -E2BIG;
> > 
> > 	if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) {
> > 		int i = 0;
> > 		synchronize_rcu();
> > 		spin_lock_bh(&ipt_osf_lock);
> > 		list_for_each_entry_safe(finger, n, &ipt_finger_list, flist) {
> 
> This is OK -- we hold the update-side lock, so don't need _rcu.
> 
> > 			i++;
> > 			list_del_rcu(&finger->flist);
> > 			ipt_osf_finger_free(finger);
> 
> Why is it safe to immediately free the element that we just removed from
> an RCU-protected list?  The above synchronize_rcu() won't help us given
> that it is still in the list at that point.
> 
> This could be fixed by using call_rcu() in ipt_osf_finger_free().

Yup, it is safe on UP machine, since without lock it is only accessed
from the bottom half, which is disabled, but running it on different CPU
will lead to crash.

procfs was not a very good choice here :)

> > 	cnt = snprintf(finger->details,
> > 		       ((count - (pbeg - buffer) + 1) >
> > 			MAXDETLEN) ? MAXDETLEN : (count - (pbeg - buffer) + 1),
> > 		       "%s", pbeg);
> > 
> > 	log("%s - %s[%s] : %s\n",
> > 	    finger->genre, finger->version, finger->subtype, finger->details);
> > 
> > 	ipt_osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
> > 
> > 	synchronize_rcu();
> 
> I don't understand what this synchronize_rcu() is doing for us.
> 
> Is the idea to make sure that all RCU readers see the prior deletes as
> having happened before we do the below addition?  If so, please add a
> comment to this effect.

Just to be sure, that  return from the call does update the list.
It is not needed in practice.

> > static void __devexit ipt_osf_fini(void)
> > {
> > 	struct ipt_osf_finger *f, *n;
> > 
> > 	remove_proc_entry("osf", proc_net_netfilter);
> > 	xt_unregister_match(&ipt_osf_match);
> > 
> > 	list_for_each_entry_safe(f, n, &ipt_finger_list, flist) {
> 
> Why is it safe to do the above without being in an RCU read-side critical
> section?  Have all possible RCU readers somehow been banished?  If so,
> how have they been banished?
> 
> > 		list_del(&f->flist);
> 
> Ditto for why the list_del() is safe without the update-side lock and
> why it doesn't have to be list_del_rcu().

Procfs was messed, it should use proper rcu freeing path, and although
procefs entry was removed, there may be some reference.

Thanks a lot for your review Paul.

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Passive OS fingerprinting.
       [not found]     ` <20080701224149.GA8449@linux.vnet.ibm.com>
@ 2008-07-02  4:46       ` Evgeniy Polyakov
  0 siblings, 0 replies; 21+ messages in thread
From: Evgeniy Polyakov @ 2008-07-02  4:46 UTC (permalink / raw)
  To: Paul E. McKenney; +Cc: netdev, netfilter-devel

On Tue, Jul 01, 2008 at 03:41:49PM -0700, Paul E. McKenney (paulmck@linux.vnet.ibm.com) wrote:
> Glad it wasn't just me going senile!!!  ;-)
> 
> Will you be fixing the procfs stuff as well?  If not, could you please
> send a pointer?

I will, probably just by moving to netlink. I think I will also
implement kind of hash table for fingerprint set.

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2008-07-02  4:46 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-07-01 11:39 Passive OS fingerprinting Evgeniy Polyakov
2008-07-01 11:53 ` Patrick McHardy
2008-07-01 12:03   ` Evgeniy Polyakov
2008-07-01 12:35     ` Patrick McHardy
2008-07-01 13:08       ` Evgeniy Polyakov
2008-07-01 13:41         ` Patrick McHardy
2008-07-01 14:14           ` Evgeniy Polyakov
2008-07-01 14:16             ` Patrick McHardy
2008-07-01 14:48               ` Evgeniy Polyakov
2008-07-01 14:54                 ` Patrick McHardy
2008-07-01 14:26         ` Jan Engelhardt
2008-07-01 14:25           ` Patrick McHardy
2008-07-01 13:32       ` Jeff Garzik
2008-07-01 13:35         ` Patrick McHardy
2008-07-01 13:47           ` Evgeniy Polyakov
2008-07-01 15:34           ` Jeff Garzik
2008-07-01 15:44             ` Patrick McHardy
2008-07-01 13:39         ` Evgeniy Polyakov
2008-07-01 19:56 ` Paul E. McKenney
2008-07-01 21:21   ` Evgeniy Polyakov
     [not found]     ` <20080701224149.GA8449@linux.vnet.ibm.com>
2008-07-02  4:46       ` Evgeniy Polyakov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).