All of lore.kernel.org
 help / color / mirror / Atom feed
* Passive OS fingerprint xtables match.
@ 2009-01-29 17:20 Evgeniy Polyakov
  2009-01-30  1:05 ` Paul E. McKenney
  2009-02-09 16:09 ` Patrick McHardy
  0 siblings, 2 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-01-29 17:20 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, David Miller, Paul E. McKenney

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
and loaded via netlink connector into the kernel via special util found
in archive. It will also listen for events about matching packets.

Archive also contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

This release moves all rules initialization to be handled over the
netlink and introduces lookup tables to speed-up RCU finger matching
a bit. Also fixed module unloading RCU completion race noticed by
Paul McKenney.

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os

Example usage:
# modrpobe ipt_osf
# ./ucon_osf -f ./pf.os
^C Daemon will listen for incoming match events 
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector

You will find something like this in the syslog:
ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

diff --git a/include/linux/connector.h b/include/linux/connector.h
index 5c7f946..b77e6fa 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -39,6 +39,8 @@
 #define CN_IDX_V86D			0x4
 #define CN_VAL_V86D_UVESAFB		0x1
 #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
+#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
+#define CN_VAL_OSF			0x6
 
 #define CN_NETLINK_USERS		6
 
diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
new file mode 100644
index 0000000..c2d2c7a
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_osf.h
@@ -0,0 +1,120 @@
+/*
+ * ipt_osf.h
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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<<0)
+#define	IPT_OSF_TTL		(1<<1)
+#define IPT_OSF_LOG		(1<<2)
+#define IPT_OSF_UNUSED		(1<<3)
+#define IPT_OSF_CONNECTOR	(1<<4)
+#define IPT_OSF_INVERT		(1<<5)
+
+#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 */
+
+#define MAX_IPOPTLEN		40
+
+struct ipt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ */
+struct ipt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct ipt_osf_opt {
+	__u16			kind, length;
+	struct ipt_osf_wc	wc;
+};
+
+struct ipt_osf_user_finger {
+	struct ipt_osf_wc wss;
+
+	__u8 ttl, df;
+	__u16 ss, mss;
+	int opt_num;
+
+	char genre[MAXGENRELEN];
+	char version[MAXGENRELEN];
+	char subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct ipt_osf_opt opt[MAX_IPOPTLEN];
+};
+
+struct ipt_osf_nlmsg {
+	struct ipt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* 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 */
+
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+#include <net/tcp.h>
+
+struct ipt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct ipt_osf_user_finger	finger;
+};
+
+#endif				/* __KERNEL__ */
+
+#endif				/* _IPT_OSF_H */
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 3816e1d..280f779 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -101,6 +101,16 @@ config IP_NF_MATCH_TTL
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_NF_MATCH_OSF
+	tristate '"osf" match support'
+	depends on NETFILTER_ADVANCED && CONNECTOR
+	help
+	  Passive OS fingerprint matching module.
+	  You should download and install rule loading software from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 # `filter', generic and specific targets
 config IP_NF_FILTER
 	tristate "Packet filtering"
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 5f9b650..98daea9 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
 obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
 obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
 obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
+obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
 
 # targets
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
diff --git a/net/ipv4/netfilter/ipt_osf.c b/net/ipv4/netfilter/ipt_osf.c
new file mode 100644
index 0000000..9356a44
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_osf.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/connector.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/rculist.h>
+#include <linux/smp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+
+#include <net/ip.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_osf.h>
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct ipt_osf_finger_storage
+{
+	struct list_head		finger_list;
+	spinlock_t			finger_lock;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+struct ipt_osf_finger_storage ipt_osf_fingers[2];
+
+struct ipt_osf_message {
+	struct cn_msg		cmsg;
+	struct ipt_osf_nlmsg	nlmsg;
+};
+
+static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
+
+static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
+static u32 osf_seq;
+
+static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
+				   const struct sk_buff *skb)
+{
+	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
+	struct ipt_osf_nlmsg *data = &msg->nlmsg;
+	struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *tcph = tcp_hdr(skb);
+
+	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
+	msg->cmsg.seq = osf_seq++;
+	msg->cmsg.ack = 0;
+	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
+
+	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
+	memcpy(&data->ip, iph, sizeof(struct iphdr));
+	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
+
+	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
+}
+
+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 (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 if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+	
+	return (ip->ttl == f_ttl);
+}
+
+static bool ipt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
+	struct iphdr *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 *kf;
+	struct ipt_osf_user_finger *f;
+	struct ipt_osf_finger_storage *st;
+
+	if (!info)
+		return 0;
+
+	ip = ip_hdr(skb);
+	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))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
+				optsize, opts);
+	}
+
+	st = &ipt_osf_fingers[!!df];
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		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:
+				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 (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;
+					}
+
+					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)
+				continue;
+
+			fcount++;
+			if (info->flags & IPT_OSF_LOG)
+				printk(KERN_INFO "%s [%s:%s] : "
+					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
+					f->genre, f->version, f->subtype, 
+					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_CONNECTOR))) {
+		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
+		unsigned int i, optsize;
+		struct ipt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+#if 1
+		if (info->flags & IPT_OSF_LOG) {
+			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
+				printk(KERN_INFO "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)
+					printk("TRUNCATED");
+				else 
+					for (i = 0; i < optsize; i++)
+						printk("%02X ", opt[i]);
+			}
+
+			printk(" %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_CONNECTOR) {
+			fg.wss.val = window;
+			fg.ttl = ip->ttl;
+			fg.df = df;
+			fg.ss = totlen;
+			fg.mss = mss;
+			strncpy(fg.genre, "Unknown", MAXGENRELEN);
+
+			ipt_osf_send_connector(&fg, skb);
+		}
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return (fmatch == FMATCH_OK) ? 1 : 0;
+}
+
+static bool
+ipt_osf_checkentry(const struct xt_mtchk_param *m)
+{
+	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
+
+	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 void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static void osf_cn_callback(void *data)
+{
+	struct cn_msg *msg = data;
+	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
+	struct ipt_osf_finger *kf = NULL, *sf;
+	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
+
+	/*
+	 * If msg->ack is set to 0 then we add attached fingerprint,
+	 * otherwise remove, and in this case we do not need to allocate data.
+	 */
+	if (!msg->ack) {
+		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
+		if (!kf)
+			return;
+
+		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
+			continue;
+
+		if (msg->ack) {
+			spin_lock_bh(&st->finger_lock);
+			list_del_rcu(&sf->finger_entry);
+			spin_unlock_bh(&st->finger_lock);
+			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
+		} else {
+			kfree(kf);
+			kf = NULL;
+		}
+
+		break;
+	}
+
+	if (kf) {
+		spin_lock_bh(&st->finger_lock);
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+		spin_unlock_bh(&st->finger_lock);
+
+		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
+	}
+	rcu_read_unlock();
+}
+
+static int __devinit ipt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+		spin_lock_init(&st->finger_lock);
+	}
+
+	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&ipt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	cn_del_callback(&cn_osf_id);
+err_out_exit:
+	return err;
+}
+
+static void __devexit ipt_osf_fini(void)
+{
+	struct ipt_osf_finger *f;
+	int i;
+
+	cn_del_callback(&cn_osf_id);
+	xt_unregister_match(&ipt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(ipt_osf_init);
+module_exit(ipt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");

-- 
	Evgeniy Polyakov

[-- Attachment #2: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4895 bytes --]

/*
 * libipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <xtables.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 = "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 '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 #3: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4895 bytes --]

/*
 * libipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <xtables.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 = "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 '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);
}

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

* Re: Passive OS fingerprint xtables match.
  2009-01-29 17:20 Passive OS fingerprint xtables match Evgeniy Polyakov
@ 2009-01-30  1:05 ` Paul E. McKenney
  2009-02-09 16:09 ` Patrick McHardy
  1 sibling, 0 replies; 54+ messages in thread
From: Paul E. McKenney @ 2009-01-30  1:05 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: Patrick McHardy, netdev, David Miller

On Thu, Jan 29, 2009 at 08:20:30PM +0300, Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.
> 
> Fingerprint matching rules can be downloaded from OpenBSD source tree
> and loaded via netlink connector into the kernel via special util found
> in archive. It will also listen for events about matching packets.
> 
> Archive also contains library file (also attached), which was shipped
> with iptables extensions some time ago (at least when ipt_osf existed
> in patch-o-matic).
> 
> This release moves all rules initialization to be handled over the
> netlink and introduces lookup tables to speed-up RCU finger matching
> a bit. Also fixed module unloading RCU completion race noticed by
> Paul McKenney.
> 
> Fingerprints can be downloaded from
> http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
> 
> Example usage:
> # modrpobe ipt_osf
> # ./ucon_osf -f ./pf.os
> ^C Daemon will listen for incoming match events 
> -d switch removes fingerprints
> # iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector
> 
> You will find something like this in the syslog:
> ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
> 
> Passive OS fingerprint homepage (archives, examples):
> http://www.ioremap.net/projects/osf

Looks good from an RCU perspective!

Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>

> Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>
> 
> diff --git a/include/linux/connector.h b/include/linux/connector.h
> index 5c7f946..b77e6fa 100644
> --- a/include/linux/connector.h
> +++ b/include/linux/connector.h
> @@ -39,6 +39,8 @@
>  #define CN_IDX_V86D			0x4
>  #define CN_VAL_V86D_UVESAFB		0x1
>  #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
> +#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
> +#define CN_VAL_OSF			0x6
> 
>  #define CN_NETLINK_USERS		6
> 
> diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
> new file mode 100644
> index 0000000..c2d2c7a
> --- /dev/null
> +++ b/include/linux/netfilter_ipv4/ipt_osf.h
> @@ -0,0 +1,120 @@
> +/*
> + * ipt_osf.h
> + *
> + * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
> + *
> + *
> + * 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<<0)
> +#define	IPT_OSF_TTL		(1<<1)
> +#define IPT_OSF_LOG		(1<<2)
> +#define IPT_OSF_UNUSED		(1<<3)
> +#define IPT_OSF_CONNECTOR	(1<<4)
> +#define IPT_OSF_INVERT		(1<<5)
> +
> +#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 */
> +
> +#define MAX_IPOPTLEN		40
> +
> +struct ipt_osf_info {
> +	char			genre[MAXGENRELEN];
> +	__u32			len;
> +	__u32			flags;
> +	__u32			loglevel;
> +	__u32			ttl;
> +};
> +
> +/*
> + * Wildcard MSS (kind of).
> + */
> +struct ipt_osf_wc {
> +	__u32			wc;
> +	__u32			val;
> +};
> +
> +/*
> + * This struct represents IANA options
> + * http://www.iana.org/assignments/tcp-parameters
> + */
> +struct ipt_osf_opt {
> +	__u16			kind, length;
> +	struct ipt_osf_wc	wc;
> +};
> +
> +struct ipt_osf_user_finger {
> +	struct ipt_osf_wc wss;
> +
> +	__u8 ttl, df;
> +	__u16 ss, mss;
> +	int opt_num;
> +
> +	char genre[MAXGENRELEN];
> +	char version[MAXGENRELEN];
> +	char subtype[MAXGENRELEN];
> +
> +	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
> +	struct ipt_osf_opt opt[MAX_IPOPTLEN];
> +};
> +
> +struct ipt_osf_nlmsg {
> +	struct ipt_osf_user_finger	f;
> +	struct iphdr		ip;
> +	struct tcphdr		tcp;
> +};
> +
> +/* 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 */
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/list.h>
> +#include <net/tcp.h>
> +
> +struct ipt_osf_finger {
> +	struct rcu_head			rcu_head;
> +	struct list_head		finger_entry;
> +	struct ipt_osf_user_finger	finger;
> +};
> +
> +#endif				/* __KERNEL__ */
> +
> +#endif				/* _IPT_OSF_H */
> diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
> index 3816e1d..280f779 100644
> --- a/net/ipv4/netfilter/Kconfig
> +++ b/net/ipv4/netfilter/Kconfig
> @@ -101,6 +101,16 @@ config IP_NF_MATCH_TTL
> 
>  	  To compile it as a module, choose M here.  If unsure, say N.
> 
> +config IP_NF_MATCH_OSF
> +	tristate '"osf" match support'
> +	depends on NETFILTER_ADVANCED && CONNECTOR
> +	help
> +	  Passive OS fingerprint matching module.
> +	  You should download and install rule loading software from
> +	  http://www.ioremap.net/projects/osf
> +
> +	  To compile it as a module, choose M here.  If unsure, say N.
> +
>  # `filter', generic and specific targets
>  config IP_NF_FILTER
>  	tristate "Packet filtering"
> diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
> index 5f9b650..98daea9 100644
> --- a/net/ipv4/netfilter/Makefile
> +++ b/net/ipv4/netfilter/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
>  obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
>  obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
>  obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
> +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
> 
>  # targets
>  obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
> diff --git a/net/ipv4/netfilter/ipt_osf.c b/net/ipv4/netfilter/ipt_osf.c
> new file mode 100644
> index 0000000..9356a44
> --- /dev/null
> +++ b/net/ipv4/netfilter/ipt_osf.c
> @@ -0,0 +1,476 @@
> +/*
> + * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
> + *
> + *
> + * 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/connector.h>
> +#include <linux/if.h>
> +#include <linux/inetdevice.h>
> +#include <linux/ip.h>
> +#include <linux/list.h>
> +#include <linux/percpu.h>
> +#include <linux/rculist.h>
> +#include <linux/smp.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/tcp.h>
> +#include <linux/types.h>
> +
> +#include <net/ip.h>
> +
> +#include <linux/netfilter/x_tables.h>
> +#include <linux/netfilter_ipv4/ip_tables.h>
> +#include <linux/netfilter_ipv4/ipt_osf.h>
> +
> +enum osf_fmatch_states {
> +	/* Packet does not match the fingerprint */
> +	FMATCH_WRONG = 0,
> +	/* Packet matches the fingerprint */
> +	FMATCH_OK,
> +	/* Options do not match the fingerprint, but header does */
> +	FMATCH_OPT_WRONG,
> +};
> +
> +struct ipt_osf_finger_storage
> +{
> +	struct list_head		finger_list;
> +	spinlock_t			finger_lock;
> +};
> +
> +/*
> + * Indexed by dont-fragment bit.
> + * It is the only constant value in the fingerprint.
> + */
> +struct ipt_osf_finger_storage ipt_osf_fingers[2];
> +
> +struct ipt_osf_message {
> +	struct cn_msg		cmsg;
> +	struct ipt_osf_nlmsg	nlmsg;
> +};
> +
> +static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
> +
> +static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
> +static u32 osf_seq;
> +
> +static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
> +				   const struct sk_buff *skb)
> +{
> +	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
> +	struct ipt_osf_nlmsg *data = &msg->nlmsg;
> +	struct iphdr *iph = ip_hdr(skb);
> +	struct tcphdr *tcph = tcp_hdr(skb);
> +
> +	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
> +	msg->cmsg.seq = osf_seq++;
> +	msg->cmsg.ack = 0;
> +	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
> +
> +	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
> +	memcpy(&data->ip, iph, sizeof(struct iphdr));
> +	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
> +
> +	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
> +}
> +
> +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 (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 if (ip->ttl <= f_ttl)
> +			return 1;
> +		else {
> +			struct in_device *in_dev = in_dev_get(skb->dev);
> +			int ret = 0;
> +
> +			for_ifa(in_dev) {
> +				if (inet_ifa_match(ip->saddr, ifa)) {
> +					ret = (ip->ttl == f_ttl);
> +					break;
> +				}
> +			}
> +			endfor_ifa(in_dev);
> +
> +			in_dev_put(in_dev);
> +			return ret;
> +		}
> +	}
> +	
> +	return (ip->ttl == f_ttl);
> +}
> +
> +static bool ipt_osf_match_packet(const struct sk_buff *skb,
> +		const struct xt_match_param *p)
> +{
> +	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
> +	struct iphdr *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 *kf;
> +	struct ipt_osf_user_finger *f;
> +	struct ipt_osf_finger_storage *st;
> +
> +	if (!info)
> +		return 0;
> +
> +	ip = ip_hdr(skb);
> +	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))
> +			optsize = sizeof(opts);
> +
> +		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
> +				optsize, opts);
> +	}
> +
> +	st = &ipt_osf_fingers[!!df];
> +	
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
> +		f = &kf->finger;
> +
> +		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:
> +				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 (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;
> +					}
> +
> +					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)
> +				continue;
> +
> +			fcount++;
> +			if (info->flags & IPT_OSF_LOG)
> +				printk(KERN_INFO "%s [%s:%s] : "
> +					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
> +					f->genre, f->version, f->subtype, 
> +					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_CONNECTOR))) {
> +		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
> +		unsigned int i, optsize;
> +		struct ipt_osf_user_finger fg;
> +
> +		memset(&fg, 0, sizeof(fg));
> +#if 1
> +		if (info->flags & IPT_OSF_LOG) {
> +			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
> +				printk(KERN_INFO "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)
> +					printk("TRUNCATED");
> +				else 
> +					for (i = 0; i < optsize; i++)
> +						printk("%02X ", opt[i]);
> +			}
> +
> +			printk(" %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_CONNECTOR) {
> +			fg.wss.val = window;
> +			fg.ttl = ip->ttl;
> +			fg.df = df;
> +			fg.ss = totlen;
> +			fg.mss = mss;
> +			strncpy(fg.genre, "Unknown", MAXGENRELEN);
> +
> +			ipt_osf_send_connector(&fg, skb);
> +		}
> +	}
> +
> +	if (fcount)
> +		fmatch = FMATCH_OK;
> +
> +	return (fmatch == FMATCH_OK) ? 1 : 0;
> +}
> +
> +static bool
> +ipt_osf_checkentry(const struct xt_mtchk_param *m)
> +{
> +	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
> +
> +	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 void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
> +{
> +	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
> +
> +	kfree(f);
> +}
> +
> +static void osf_cn_callback(void *data)
> +{
> +	struct cn_msg *msg = data;
> +	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
> +	struct ipt_osf_finger *kf = NULL, *sf;
> +	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
> +
> +	/*
> +	 * If msg->ack is set to 0 then we add attached fingerprint,
> +	 * otherwise remove, and in this case we do not need to allocate data.
> +	 */
> +	if (!msg->ack) {
> +		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
> +		if (!kf)
> +			return;
> +
> +		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
> +	}
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
> +		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
> +			continue;
> +
> +		if (msg->ack) {
> +			spin_lock_bh(&st->finger_lock);
> +			list_del_rcu(&sf->finger_entry);
> +			spin_unlock_bh(&st->finger_lock);
> +			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
> +		} else {
> +			kfree(kf);
> +			kf = NULL;
> +		}
> +
> +		break;
> +	}
> +
> +	if (kf) {
> +		spin_lock_bh(&st->finger_lock);
> +		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
> +		spin_unlock_bh(&st->finger_lock);
> +
> +		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
> +	}
> +	rcu_read_unlock();
> +}
> +
> +static int __devinit ipt_osf_init(void)
> +{
> +	int err = -EINVAL;
> +	int i;
> +
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		INIT_LIST_HEAD(&st->finger_list);
> +		spin_lock_init(&st->finger_lock);
> +	}
> +
> +	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
> +		goto err_out_exit;
> +	}
> +
> +	err = xt_register_match(&ipt_osf_match);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
> +				"matching module.\n", err);
> +		goto err_out_remove;
> +	}
> +
> +	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
> +
> +	return 0;
> +
> +err_out_remove:
> +	cn_del_callback(&cn_osf_id);
> +err_out_exit:
> +	return err;
> +}
> +
> +static void __devexit ipt_osf_fini(void)
> +{
> +	struct ipt_osf_finger *f;
> +	int i;
> +
> +	cn_del_callback(&cn_osf_id);
> +	xt_unregister_match(&ipt_osf_match);
> +
> +	rcu_read_lock();
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> +			list_del_rcu(&f->finger_entry);
> +			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	rcu_barrier();
> +
> +	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
> +}
> +
> +module_init(ipt_osf_init);
> +module_exit(ipt_osf_fini);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
> +MODULE_DESCRIPTION("Passive OS fingerprint matching.");
> 
> -- 
> 	Evgeniy Polyakov

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
>  *
>  *
>  * 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 <xtables.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 = "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 '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);
> }

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
>  *
>  *
>  * 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 <xtables.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 = "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 '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);
> }


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

* Re: Passive OS fingerprint xtables match.
  2009-01-29 17:20 Passive OS fingerprint xtables match Evgeniy Polyakov
  2009-01-30  1:05 ` Paul E. McKenney
@ 2009-02-09 16:09 ` Patrick McHardy
  2009-02-13 12:49   ` Evgeniy Polyakov
  1 sibling, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-02-09 16:09 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.
> 
> Fingerprint matching rules can be downloaded from OpenBSD source tree
> and loaded via netlink connector into the kernel via special util found
> in archive. It will also listen for events about matching packets.
> 
> Archive also contains library file (also attached), which was shipped
> with iptables extensions some time ago (at least when ipt_osf existed
> in patch-o-matic).
> 
> This release moves all rules initialization to be handled over the
> netlink and introduces lookup tables to speed-up RCU finger matching
> a bit. Also fixed module unloading RCU completion race noticed by
> Paul McKenney.

Sorry for ignoring this for so long. I really don't have much of an
opinion on this except for what I said before:

- I would prefer a mechanism built on u32 if possible

- I want to hear at least one person speaking in favour of inclusion
   since I don't have much of an opinion of my own, but am somewhat
   doubtful how useful this is

I guess you could call Paul's "cool stuff" that, but please resend
once more to netfilter-devel :) Anyone who thinks this is useful
please speak up.


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

* Re: Passive OS fingerprint xtables match.
  2009-02-09 16:09 ` Patrick McHardy
@ 2009-02-13 12:49   ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-13 12:49 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Hi Patrick.

On Mon, Feb 09, 2009 at 05:09:21PM +0100, Patrick McHardy (kaber@trash.net) wrote:
> Sorry for ignoring this for so long. I really don't have much of an
> opinion on this except for what I said before:
> 
> - I would prefer a mechanism built on u32 if possible

u32 actually can not be used here, since it is not possible to implement
a state machine to match different wildcards for MSS for example. Like
modulus operation.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-06-08 15:06 ` Patrick McHardy
@ 2009-06-08 17:25   ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-06-08 17:25 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

On Mon, Jun 08, 2009 at 05:06:08PM +0200, Patrick McHardy (kaber@trash.net) wrote:
> Applied, thanks. I've made one minor change:
> 
> --- a/include/linux/netfilter/Kbuild
> +++ b/include/linux/netfilter/Kbuild
> @@ -33,6 +33,7 @@ header-y += xt_limit.h
>  header-y += xt_mac.h
>  header-y += xt_mark.h
>  header-y += xt_multiport.h
> +header-y += xt_osf.h
>  header-y += xt_owner.h
>  header-y += xt_pkttype.h
>  header-y += xt_quota.h

No problem, thank you!

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-06-07 15:17 Evgeniy Polyakov
@ 2009-06-08 15:06 ` Patrick McHardy
  2009-06-08 17:25   ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-06-08 15:06 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.

Applied, thanks. I've made one minor change:

--- a/include/linux/netfilter/Kbuild
+++ b/include/linux/netfilter/Kbuild
@@ -33,6 +33,7 @@ header-y += xt_limit.h
  header-y += xt_mac.h
  header-y += xt_mark.h
  header-y += xt_multiport.h
+header-y += xt_osf.h
  header-y += xt_owner.h
  header-y += xt_pkttype.h
  header-y += xt_quota.h

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

* Passive OS fingerprint xtables match.
@ 2009-06-07 15:17 Evgeniy Polyakov
  2009-06-08 15:06 ` Patrick McHardy
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-06-07 15:17 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
or found in archive and loaded via netfilter netlink subsystem into
the kernel via special util found in archive.

Archive contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

Following changes were made in this release:
 * added NLM_F_CREATE/NLM_F_EXCL checks
 * dropped _rcu list traversing helpers in the protected add/remove calls
 * dropped unneded structures, debug prints, obscure comment and check

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
or can be found in archive

Example usage:
# modrpobe xt_osf
# ./nfnl_osf -f ./pf.os
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2

Please consider for inclusion.
Thank you.

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

 include/linux/netfilter/nfnetlink.h |    3 +-
 include/linux/netfilter/xt_osf.h    |  133 +++++++++++
 net/netfilter/Kconfig               |   13 +
 net/netfilter/Makefile              |    1 +
 net/netfilter/xt_osf.c              |  428 +++++++++++++++++++++++++++++++++++
 5 files changed, 577 insertions(+), 1 deletions(-)

diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index c600083..bb9c9ae 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -46,7 +46,8 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_CTNETLINK_EXP	2
 #define NFNL_SUBSYS_QUEUE		3
 #define NFNL_SUBSYS_ULOG		4
-#define NFNL_SUBSYS_COUNT		5
+#define NFNL_SUBSYS_OSF			5
+#define NFNL_SUBSYS_COUNT		6
 
 #ifdef __KERNEL__
 
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h
new file mode 100644
index 0000000..fd2272e
--- /dev/null
+++ b/include/linux/netfilter/xt_osf.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.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 _XT_OSF_H
+#define _XT_OSF_H
+
+#define MAXGENRELEN		32
+
+#define XT_OSF_GENRE		(1<<0)
+#define	XT_OSF_TTL		(1<<1)
+#define XT_OSF_LOG		(1<<2)
+#define XT_OSF_INVERT		(1<<3)
+
+#define XT_OSF_LOGLEVEL_ALL	0	/* log all matched fingerprints */
+#define XT_OSF_LOGLEVEL_FIRST	1	/* log only the first matced fingerprint */
+#define XT_OSF_LOGLEVEL_ALL_KNOWN	2 /* do not log unknown packets */
+
+#define XT_OSF_TTL_TRUE		0	/* True ip and fingerprint TTL comparison */
+#define XT_OSF_TTL_LESS		1	/* Check if ip TTL is less than fingerprint one */
+#define XT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
+
+struct xt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct xt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct xt_osf_opt {
+	__u16			kind, length;
+	struct xt_osf_wc	wc;
+};
+
+struct xt_osf_user_finger {
+	struct xt_osf_wc	wss;
+
+	__u8			ttl, df;
+	__u16			ss, mss;
+	__u16			opt_num;
+
+	char			genre[MAXGENRELEN];
+	char			version[MAXGENRELEN];
+	char			subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct xt_osf_opt	opt[MAX_IPOPTLEN];
+};
+
+struct xt_osf_nlmsg {
+	struct xt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* Defines for IANA option kinds */
+
+enum iana_options {
+	OSFOPT_EOL = 0,		/* End of options */
+	OSFOPT_NOP, 		/* NOP */
+	OSFOPT_MSS, 		/* Maximum segment size */
+	OSFOPT_WSO, 		/* Window scale option */
+	OSFOPT_SACKP,		/* SACK permitted */
+	OSFOPT_SACK,		/* SACK */
+	OSFOPT_ECHO,
+	OSFOPT_ECHOREPLY,
+	OSFOPT_TS,		/* Timestamp option */
+	OSFOPT_POCP,		/* Partial Order Connection Permitted */
+	OSFOPT_POSP,		/* Partial Order Service Profile */
+
+	/* Others are not used in the current OSF */
+	OSFOPT_EMPTY = 255,
+};
+
+/*
+ * Initial window size option state machine: multiple of mss, mtu or
+ * plain numeric value. Can also be made as plain numeric value which
+ * is not a multiple of specified value.
+ */
+enum xt_osf_window_size_options {
+	OSF_WSS_PLAIN	= 0,
+	OSF_WSS_MSS,
+	OSF_WSS_MTU,
+	OSF_WSS_MODULO,
+	OSF_WSS_MAX,
+};
+
+/*
+ * Add/remove fingerprint from the kernel.
+ */
+enum xt_osf_msg_types {
+	OSF_MSG_ADD,
+	OSF_MSG_REMOVE,
+	OSF_MSG_MAX,
+};
+
+enum xt_osf_attr_type {
+	OSF_ATTR_UNSPEC,
+	OSF_ATTR_FINGER,
+	OSF_ATTR_MAX,
+};
+
+#endif				/* _XT_OSF_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 2329c5f..3a6ecee 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -916,6 +916,19 @@ config NETFILTER_XT_MATCH_U32
 
 	  Details and examples are in the kernel module source.
 
+config NETFILTER_XT_MATCH_OSF
+	tristate '"osf" Passive OS fingerprint match'
+	depends on NETFILTER_ADVANCED && NETFILTER_NETLINK
+	help
+	  This option selects the Passive OS Fingerprinting match module
+	  that allows to passively match the remote operating system by
+	  analyzing incoming TCP SYN packets.
+
+	  Rules and loading software can be downloaded from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 endif # NETFILTER_XTABLES
 
 endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 6282060..49f62ee 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
new file mode 100644
index 0000000..863e409
--- /dev/null
+++ b/net/netfilter/xt_osf.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/rculist.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/x_tables.h>
+#include <net/netfilter/nf_log.h>
+#include <linux/netfilter/xt_osf.h>
+
+struct xt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct xt_osf_user_finger	finger;
+};
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+static struct list_head xt_osf_fingers[2];
+
+static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
+	[OSF_ATTR_FINGER]	= { .len = sizeof(struct xt_osf_user_finger) },
+};
+
+static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
+			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
+{
+	struct xt_osf_user_finger *f;
+	struct xt_osf_finger *kf = NULL, *sf;
+	int err = 0;
+
+	if (!osf_attrs[OSF_ATTR_FINGER])
+		return -EINVAL;
+
+	if (!(nlh->nlmsg_flags & NLM_F_CREATE))
+		return -EINVAL;
+
+	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
+
+	kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
+	if (!kf)
+		return -ENOMEM;
+
+	memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
+
+	list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
+			continue;
+
+		kfree(kf);
+		kf = NULL;
+
+		if (nlh->nlmsg_flags & NLM_F_EXCL)
+			err = -EEXIST;
+		break;
+	}
+
+	/*
+	 * We are protected by nfnl mutex.
+	 */
+	if (kf)
+		list_add_tail_rcu(&kf->finger_entry, &xt_osf_fingers[!!f->df]);
+
+	return err;
+}
+
+static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb,
+			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
+{
+	struct xt_osf_user_finger *f;
+	struct xt_osf_finger *sf;
+	int err = ENOENT;
+
+	if (!osf_attrs[OSF_ATTR_FINGER])
+		return -EINVAL;
+
+	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
+
+	list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
+			continue;
+
+		/*
+		 * We are protected by nfnl mutex.
+		 */
+		list_del_rcu(&sf->finger_entry);
+		call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu);
+
+		err = 0;
+		break;
+	}
+
+	return err;
+}
+
+static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
+	[OSF_MSG_ADD]	= {
+		.call		= xt_osf_add_callback,
+		.attr_count	= OSF_ATTR_MAX,
+		.policy		= xt_osf_policy,
+	},
+	[OSF_MSG_REMOVE]	= {
+		.call		= xt_osf_remove_callback,
+		.attr_count	= OSF_ATTR_MAX,
+		.policy		= xt_osf_policy,
+	},
+};
+
+static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
+	.name			= "osf",
+	.subsys_id		= NFNL_SUBSYS_OSF,
+	.cb_count		= OSF_MSG_MAX,
+	.cb			= xt_osf_nfnetlink_callbacks,
+};
+
+static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
+			    unsigned char f_ttl)
+{
+	const struct iphdr *ip = ip_hdr(skb);
+
+	if (info->flags & XT_OSF_TTL) {
+		if (info->ttl == XT_OSF_TTL_TRUE)
+			return ip->ttl == f_ttl;
+		if (info->ttl == XT_OSF_TTL_NOCHECK)
+			return 1;
+		else if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			return ret;
+		}
+	}
+
+	return ip->ttl == f_ttl;
+}
+
+static bool xt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	const struct xt_osf_info *info = p->matchinfo;
+	const struct iphdr *ip = ip_hdr(skb);
+	const struct tcphdr *tcp;
+	struct tcphdr _tcph;
+	int fmatch = FMATCH_WRONG, fcount = 0;
+	unsigned int optsize = 0, check_WSS = 0;
+	u16 window, totlen, mss = 0;
+	bool df;
+	const unsigned char *optp = NULL, *_optp = NULL;
+	unsigned char opts[MAX_IPOPTLEN];
+	const struct xt_osf_finger *kf;
+	const struct xt_osf_user_finger *f;
+
+	if (!info)
+		return false;
+
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
+	if (!tcp)
+		return false;
+
+	if (!tcp->syn)
+		return false;
+
+	totlen = ntohs(ip->tot_len);
+	df = ntohs(ip->frag_off) & IP_DF;
+	window = ntohs(tcp->window);
+
+	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
+		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
+
+		_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) +
+				sizeof(struct tcphdr), optsize, opts);
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) {
+		f = &kf->finger;
+
+		if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
+			continue;
+
+		optp = _optp;
+		fmatch = FMATCH_WRONG;
+
+		if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) {
+			int foptsize, optnum;
+
+			/*
+			 * Should not happen if userspace parser was written correctly.
+			 */
+			if (f->wss.wc >= OSF_WSS_MAX)
+				continue;
+
+			/* Check options */
+
+			foptsize = 0;
+			for (optnum = 0; optnum < f->opt_num; ++optnum)
+				foptsize += f->opt[optnum].length;
+
+			if (foptsize > MAX_IPOPTLEN ||
+				optsize > MAX_IPOPTLEN ||
+				optsize != foptsize)
+				continue;
+
+			check_WSS = f->wss.wc;
+
+			for (optnum = 0; optnum < f->opt_num; ++optnum) {
+				if (f->opt[optnum].kind == (*optp)) {
+					__u32 len = f->opt[optnum].length;
+					const __u8 *optend = optp + len;
+					int loop_cont = 0;
+
+					fmatch = FMATCH_OK;
+
+					switch (*optp) {
+					case OSFOPT_MSS:
+						mss = optp[3];
+						mss <<= 8;
+						mss |= optp[2];
+
+						mss = ntohs(mss);
+						break;
+					case OSFOPT_TS:
+						loop_cont = 1;
+						break;
+					}
+
+					optp = optend;
+				} else
+					fmatch = FMATCH_OPT_WRONG;
+
+				if (fmatch != FMATCH_OK)
+					break;
+			}
+
+			if (fmatch != FMATCH_OPT_WRONG) {
+				fmatch = FMATCH_WRONG;
+
+				switch (check_WSS) {
+				case OSF_WSS_PLAIN:
+					if (f->wss.val == 0 || window == f->wss.val)
+						fmatch = FMATCH_OK;
+					break;
+				case OSF_WSS_MSS:
+					/*
+					 * Some smart modems decrease mangle MSS to 
+					 * SMART_MSS_2, so we check standard, decreased
+					 * and the one provided in the fingerprint MSS
+					 * values.
+					 */
+#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 OSF_WSS_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 OSF_WSS_MODULO:
+					if ((window % f->wss.val) == 0)
+						fmatch = FMATCH_OK;
+					break;
+				}
+			}
+
+			if (fmatch != FMATCH_OK)
+				continue;
+
+			fcount++;
+
+			if (info->flags & XT_OSF_LOG)
+				nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+					"%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
+					f->genre, f->version, f->subtype,
+					&ip->saddr, ntohs(tcp->source),
+					&ip->daddr, ntohs(tcp->dest),
+					f->ttl - ip->ttl);
+
+			if ((info->flags & XT_OSF_LOG) &&
+			    info->loglevel == XT_OSF_LOGLEVEL_FIRST)
+				break;
+		}
+	}
+	rcu_read_unlock();
+
+	if (!fcount && (info->flags & XT_OSF_LOG))
+		nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+			"Remote OS is not known: %pi4:%u -> %pi4:%u\n",
+				&ip->saddr, ntohs(tcp->source),
+				&ip->daddr, ntohs(tcp->dest));
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return fmatch == FMATCH_OK;
+}
+
+static struct xt_match xt_osf_match = {
+	.name 		= "osf",
+	.revision	= 0,
+	.family		= NFPROTO_IPV4,
+	.proto		= IPPROTO_TCP,
+	.hooks      	= (1 << NF_INET_LOCAL_IN) |
+				(1 << NF_INET_PRE_ROUTING) |
+				(1 << NF_INET_FORWARD),
+	.match 		= xt_osf_match_packet,
+	.matchsize	= sizeof(struct xt_osf_info),
+	.me		= THIS_MODULE,
+};
+
+static int __init xt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i)
+		INIT_LIST_HEAD(&xt_osf_fingers[i]);
+
+	err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
+	if (err < 0) {
+		printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&xt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	return 0;
+
+err_out_remove:
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+err_out_exit:
+	return err;
+}
+
+static void __exit xt_osf_fini(void)
+{
+	struct xt_osf_finger *f;
+	int i;
+
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+	xt_unregister_match(&xt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+
+		list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, xt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+}
+
+module_init(xt_osf_init);
+module_exit(xt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);

-- 
	Evgeniy Polyakov

[-- Attachment #2: libxt_osf.c --]
[-- Type: text/x-csrc, Size: 4751 bytes --]

/*
 * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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
 */

/*
 * xtables 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 <linux/types.h>

#include <xtables.h>

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

#include "xt_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"
		);
}


static const struct option osf_opts[] = {
	{ .name = "genre",	.has_arg = true, .val = '1' },
	{ .name = "ttl",	.has_arg = true, .val = '2' },
	{ .name = "log",	.has_arg = true, .val = '3' },
	{ .name = NULL }
};


static void osf_parse_string(const unsigned char *s, struct xt_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 xt_osf_info *info = (struct xt_osf_info *)(*match)->data;
	
	switch(c) {
		case '1': /* --genre */
			if (*flags & XT_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 |= XT_OSF_INVERT;
			info->len=strlen(info->genre);
			*flags |= XT_OSF_GENRE;
			break;
		case '2': /* --ttl */
			if (*flags & XT_OSF_TTL)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple ttl parameter");
			*flags |= XT_OSF_TTL;
			info->flags |= XT_OSF_TTL;
			if (!xtables_strtoui(argv[optind-1], NULL, &info->ttl, 0, 2))
				xtables_error(PARAMETER_PROBLEM, "TTL parameter is too big");
			break;
		case '3': /* --log */
			if (*flags & XT_OSF_LOG)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
			*flags |= XT_OSF_LOG;
			if (!xtables_strtoui(argv[optind-1], NULL, &info->loglevel, 0, 2))
				xtables_error(PARAMETER_PROBLEM, "Log level parameter is too big");
			info->flags |= XT_OSF_LOG;
			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 xt_osf_info *info = (const struct xt_osf_info*) match->data;

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

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

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

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

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

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

* Re: Passive OS fingerprint xtables match.
  2009-06-05 11:54 ` Patrick McHardy
  2009-06-05 13:10   ` Jan Engelhardt
@ 2009-06-07 15:12   ` Evgeniy Polyakov
  1 sibling, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-06-07 15:12 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Hi Patrick.

I've added mentioned cleanups except the one described below.

On Fri, Jun 05, 2009 at 01:54:15PM +0200, Patrick McHardy (kaber@trash.net) wrote:

> >+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), 
> >&_tcph);
> >+	if (!tcp)
> >+		return false;
> >+
> >+	if (!tcp->syn)
> >+		return false;
> >+
> >+	totlen = ntohs(ip->tot_len);
> >+	df = ntohs(ip->frag_off) & IP_DF;
> >+	window = ntohs(tcp->window);
> >+
> >+	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
> >+		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
> 
> tcp_optlen()?

TCP header is potentially copied above and transport header may be not
set, so I work with that part of skb without assuming it was previously
set by lower layers.

> >+		if (info->flags & XT_OSF_LOG) {
> >+			if (info->loglevel != XT_OSF_LOGLEVEL_ALL_KNOWN)
> >+				nf_log_packet(p->hooknum, 0, skb, p->in, 
> >p->out, NULL,
> >+					"window: %u, mss: %u, totlen: %u, 
> >df: %d, ttl: %u : ",
> >+					window, mss, totlen, df, ip->ttl);
> 
> nf_log_packet() can pass the entire packet to userspace. Header-field
> extraction should be done by whatever is logging in userspace. This
> looks very much like debugging anyways, where is the actual message?

Message itself is 'printed' couple of lines below, but apparently it is
not needed for nf_log_packet, only for the printk()-based debug, so I
dropped that part.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-06-05 13:30     ` Patrick McHardy
@ 2009-06-05 13:44       ` Jan Engelhardt
  0 siblings, 0 replies; 54+ messages in thread
From: Jan Engelhardt @ 2009-06-05 13:44 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Evgeniy Polyakov, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Friday 2009-06-05 15:30, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> On Friday 2009-06-05 13:54, Patrick McHardy wrote:
>>   
>>>> +
>>>> +		if (optsize > sizeof(opts))
>>>> +			optsize = sizeof(opts);
>>>>       
>>> How can this happen? The doff field can only represent up to 40
>>> bytes of option length.
>>>     
>>
>> Forged packets?
>
>How would that increase the size of the doff field? :)

Ah never mind. Just had to read the RFC... again.

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

* Re: Passive OS fingerprint xtables match.
  2009-06-05 13:10   ` Jan Engelhardt
@ 2009-06-05 13:30     ` Patrick McHardy
  2009-06-05 13:44       ` Jan Engelhardt
  0 siblings, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-06-05 13:30 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Evgeniy Polyakov, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Jan Engelhardt wrote:
> On Friday 2009-06-05 13:54, Patrick McHardy wrote:
>   
>>> +
>>> +		if (optsize > sizeof(opts))
>>> +			optsize = sizeof(opts);
>>>       
>> How can this happen? The doff field can only represent up to 40
>> bytes of option length.
>>     
>
> Forged packets?

How would that increase the size of the doff field? :)
>>> +	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
>>>       
>> Please no messages on successful module load. Or at least
>> not when statically built, but preferrably not at all.
>>     
>
> By that logic should not the messages-on-load for netfilter be removed? ;-)
>   

I wouldn't care, but there's a difference between the main module printing a
message and extensions printing a message.


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

* Re: Passive OS fingerprint xtables match.
  2009-06-05 11:54 ` Patrick McHardy
@ 2009-06-05 13:10   ` Jan Engelhardt
  2009-06-05 13:30     ` Patrick McHardy
  2009-06-07 15:12   ` Evgeniy Polyakov
  1 sibling, 1 reply; 54+ messages in thread
From: Jan Engelhardt @ 2009-06-05 13:10 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Evgeniy Polyakov, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Friday 2009-06-05 13:54, Patrick McHardy wrote:
>> +
>> +		if (optsize > sizeof(opts))
>> +			optsize = sizeof(opts);
>
> How can this happen? The doff field can only represent up to 40
> bytes of option length.

Forged packets?

>> +	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
>
> Please no messages on successful module load. Or at least
> not when statically built, but preferrably not at all.

By that logic should not the messages-on-load for netfilter be removed? ;-)


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

* Re: Passive OS fingerprint xtables match.
  2009-06-04 16:22 Evgeniy Polyakov
@ 2009-06-05 11:54 ` Patrick McHardy
  2009-06-05 13:10   ` Jan Engelhardt
  2009-06-07 15:12   ` Evgeniy Polyakov
  0 siblings, 2 replies; 54+ messages in thread
From: Patrick McHardy @ 2009-06-05 11:54 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> 
> This version switches from printk to nf_log_packet() interface,
> introduces separate add/remove commands for fingerprint processing,
> drops unused variables and includes, adds more comments and contains
> other misc cleanups.

Thanks, this looks much better already. A few final requests:

> +++ b/net/netfilter/xt_osf.c
> +
> +struct xt_osf_finger_storage {
> +	struct list_head		finger_list;
> +};

Without the lock the struct doesn't seem useful anymore. Any reason
for not removing it?

> +static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
> +			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
> +{
> +	struct xt_osf_user_finger *f;
> +	struct xt_osf_finger *kf = NULL, *sf;
> +	struct xt_osf_finger_storage *st;
> +	int err = 0;
> +
> +	if (!osf_attrs[OSF_ATTR_FINGER])
> +		return -EINVAL;
> +
> +	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
> +	st = &xt_osf_fingers[!!f->df];
> +
> +	kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
> +	if (!kf)
> +		return -ENOMEM;
> +
> +	memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
> +		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
> +			continue;
> +
> +		/*
> +		 * We are protected by nfnl mutex.

Which means we don't need RCU here (and for removal), right?

> +		 */
> +		kfree(kf);
> +		kf = NULL;
> +
> +		err = -EEXIST;
> +		break;

Please verify that the correct netlink flags are set to avoid
problems in case someone wants to add replacements later on.
This means addition always requires NLM_F_CREATE. NLM_F_REPLACE
is currently not supported, but EEXIST should only be returned
when NLM_F_EXCL is set.

> +static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
> +			    unsigned char f_ttl)
> +{
> +	const struct iphdr *ip = ip_hdr(skb);
> +
> +	if (info->flags & XT_OSF_TTL) {
> +		if (info->ttl == XT_OSF_TTL_TRUE)
> +			return ip->ttl == f_ttl;
> +		if (info->ttl == XT_OSF_TTL_NOCHECK)
> +			return 1;
> +		else if (ip->ttl <= f_ttl)
> +			return 1;
> +		else {
> +			struct in_device *in_dev = in_dev_get(skb->dev);

This code is only running inside RCU read side section.
You could use the non-refcounted variant.

> +			int ret = 0;
> +
> +			for_ifa(in_dev) {
> +				if (inet_ifa_match(ip->saddr, ifa)) {
> +					ret = (ip->ttl == f_ttl);
> +					break;
> +				}
> +			}
> +			endfor_ifa(in_dev);
> +
> +			in_dev_put(in_dev);
> +			return ret;
> +		}
> +	}
> +
> +	return ip->ttl == f_ttl;
> +}
> +
> +static bool xt_osf_match_packet(const struct sk_buff *skb,
> +		const struct xt_match_param *p)
> +{
> +	const struct xt_osf_info *info = p->matchinfo;
> +	const struct iphdr *ip = ip_hdr(skb);
> +	const struct tcphdr *tcp;
> +	struct tcphdr _tcph;
> +	int fmatch = FMATCH_WRONG, fcount = 0;
> +	unsigned int optsize = 0, check_WSS = 0;
> +	u16 window, totlen, mss = 0;
> +	bool df;
> +	const unsigned char *optp = NULL, *_optp = NULL;
> +	unsigned char opts[MAX_IPOPTLEN];
> +	const struct xt_osf_finger *kf;
> +	const struct xt_osf_user_finger *f;
> +	const struct xt_osf_finger_storage *st;
> +
> +	if (!info)
> +		return false;
> +
> +	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
> +	if (!tcp)
> +		return false;
> +
> +	if (!tcp->syn)
> +		return false;
> +
> +	totlen = ntohs(ip->tot_len);
> +	df = ntohs(ip->frag_off) & IP_DF;
> +	window = ntohs(tcp->window);
> +
> +	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
> +		optsize = tcp->doff * 4 - sizeof(struct tcphdr);

tcp_optlen()?

> +
> +		if (optsize > sizeof(opts))
> +			optsize = sizeof(opts);

How can this happen? The doff field can only represent up to 40
bytes of option length.

> +
> +		_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) +
> +				sizeof(struct tcphdr), optsize, opts);
> +	}
> +
> +	st = &xt_osf_fingers[df];
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
> +		f = &kf->finger;
> +
> +		if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
> +			continue;
> +
> +		optp = _optp;
> +		fmatch = FMATCH_WRONG;
> +
> +		if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) {
> +			int foptsize, optnum;
> +
> +			/*
> +			 * Should not happen if userspace parser was written correctly.
> +			 */
> +			if (f->wss.wc >= OSF_WSS_MAX)
> +				continue;
> +
> +			/* Check options */
> +
> +			foptsize = 0;
> +			for (optnum = 0; optnum < f->opt_num; ++optnum)
> +				foptsize += f->opt[optnum].length;
> +
> +			if (foptsize > MAX_IPOPTLEN ||
> +				optsize > MAX_IPOPTLEN ||
> +				optsize != foptsize)
> +				continue;
> +
> +			check_WSS = f->wss.wc;
> +
> +			for (optnum = 0; optnum < f->opt_num; ++optnum) {
> +				if (f->opt[optnum].kind == (*optp)) {
> +					__u32 len = f->opt[optnum].length;
> +					const __u8 *optend = optp + len;
> +					int loop_cont = 0;
> +
> +					fmatch = FMATCH_OK;
> +
> +					switch (*optp) {
> +					case OSFOPT_MSS:
> +						mss = optp[3];
> +						mss <<= 8;
> +						mss |= optp[2];
> +
> +						mss = ntohs(mss);
> +						break;
> +					case OSFOPT_TS:
> +						loop_cont = 1;
> +						break;
> +					}
> +
> +					optp = optend;
> +				} else
> +					fmatch = FMATCH_OPT_WRONG;
> +
> +				if (fmatch != FMATCH_OK)
> +					break;
> +			}
> +
> +			if (fmatch != FMATCH_OPT_WRONG) {
> +				fmatch = FMATCH_WRONG;
> +
> +				switch (check_WSS) {
> +				case OSF_WSS_PLAIN:
> +					if (f->wss.val == 0 || window == f->wss.val)
> +						fmatch = FMATCH_OK;
> +					break;
> +				case OSF_WSS_MSS:
> +					/*
> +					 * Some smart modems decrease mangle MSS to 
> +					 * SMART_MSS_2, so we check standard, decreased
> +					 * and the one provided in the fingerprint MSS
> +					 * values.
> +					 */
> +#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 OSF_WSS_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 OSF_WSS_MODULO:
> +					if ((window % f->wss.val) == 0)
> +						fmatch = FMATCH_OK;
> +					break;
> +				}
> +			}
> +
> +			if (fmatch != FMATCH_OK)
> +				continue;
> +
> +			fcount++;
> +			/*
> +			 * Everyone uses NULL loginfo (well, everyone uses
> +			 * NULL for all but format and message fields), so
> +			 * I did not set it up either.
> +			 */

That comment doesn't provide much value IMO. A loginfo field is only
passed in by the various LOG targets.

> +			if (info->flags & XT_OSF_LOG)
> +				nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
> +					"%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
> +					f->genre, f->version, f->subtype,
> +					&ip->saddr, ntohs(tcp->source),
> +					&ip->daddr, ntohs(tcp->dest),
> +					f->ttl - ip->ttl);
> +
> +			if ((info->flags & XT_OSF_LOG) &&
> +			    info->loglevel == XT_OSF_LOGLEVEL_FIRST)
> +				break;
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	if (!fcount && (info->flags & XT_OSF_LOG)) {
> +		unsigned int i;
> +		struct xt_osf_user_finger fg;
> +
> +		memset(&fg, 0, sizeof(fg));
> +
> +		if (info->flags & XT_OSF_LOG) {
> +			if (info->loglevel != XT_OSF_LOGLEVEL_ALL_KNOWN)
> +				nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
> +					"window: %u, mss: %u, totlen: %u, df: %d, ttl: %u : ",
> +					window, mss, totlen, df, ip->ttl);

nf_log_packet() can pass the entire packet to userspace. Header-field
extraction should be done by whatever is logging in userspace. This
looks very much like debugging anyways, where is the actual message?

> +			if (_optp) {
> +				optp = _optp;
> +				for (i = 0; i < optsize; i++)
> +					nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
> +						"%02X ", optp[i]);

Same here and other logging calls.

> +			}
> +
> +			nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
> +					"%pi4:%u -> %pi4:%u\n",
> +			     &ip->saddr, ntohs(tcp->source),
> +			     &ip->daddr, ntohs(tcp->dest));
> +		}
> +	}
> +
> +	if (fcount)
> +		fmatch = FMATCH_OK;
> +
> +	return fmatch == FMATCH_OK;
> +}
> +
> +static struct xt_match xt_osf_match = {
> +	.name 		= "osf",
> +	.revision	= 0,
> +	.family		= NFPROTO_IPV4,
> +	.proto		= IPPROTO_TCP,
> +	.hooks      	= (1 << NF_INET_LOCAL_IN) |
> +				(1 << NF_INET_PRE_ROUTING) |
> +				(1 << NF_INET_FORWARD),
> +	.match 		= xt_osf_match_packet,
> +	.matchsize	= sizeof(struct xt_osf_info),
> +	.me		= THIS_MODULE,
> +};
> +
> +static int __init xt_osf_init(void)
> +{
> +	int err = -EINVAL;
> +	int i;
> +
> +	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
> +		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
> +
> +		INIT_LIST_HEAD(&st->finger_list);
> +	}
> +
> +	err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
> +	if (err < 0) {
> +		printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err);
> +		goto err_out_exit;
> +	}
> +
> +	err = xt_register_match(&xt_osf_match);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
> +				"matching module.\n", err);
> +		goto err_out_remove;
> +	}
> +
> +	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");

Please no messages on successful module load. Or at least
not when statically built, but preferrably not at all.

> +
> +	return 0;
> +
> +err_out_remove:
> +	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
> +err_out_exit:
> +	return err;
> +}
> +
> +static void __exit xt_osf_fini(void)
> +{
> +	struct xt_osf_finger *f;
> +	int i;
> +
> +	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
> +	xt_unregister_match(&xt_osf_match);
> +
> +	rcu_read_lock();
> +	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
> +		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
> +
> +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> +			list_del_rcu(&f->finger_entry);
> +			call_rcu(&f->rcu_head, xt_osf_finger_free_rcu);
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	rcu_barrier();
> +
> +	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");

Even less on unload :)

> +}
> +
> +module_init(xt_osf_init);
> +module_exit(xt_osf_fini);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
> +MODULE_DESCRIPTION("Passive OS fingerprint matching.");
> +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);
> 
> 


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

* Passive OS fingerprint xtables match.
@ 2009-06-04 16:22 Evgeniy Polyakov
  2009-06-05 11:54 ` Patrick McHardy
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-06-04 16:22 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
and loaded via netfilter netlink subsystem into the kernel via special
util found in archive.

Archive contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

This version switches from printk to nf_log_packet() interface,
introduces separate add/remove commands for fingerprint processing,
drops unused variables and includes, adds more comments and contains
other misc cleanups.

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os

Example usage:
# modrpobe xt_osf
# ./nfnl_osf -f ./pf.os
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2

Please consider for inclusion.
Thank you.

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

 include/linux/netfilter/nfnetlink.h |    3 +-
 include/linux/netfilter/xt_osf.h    |  133 ++++++++++
 net/netfilter/Kconfig               |   13 +
 net/netfilter/Makefile              |    1 +
 net/netfilter/xt_osf.c              |  474 +++++++++++++++++++++++++++++++++++
 5 files changed, 623 insertions(+), 1 deletions(-)

diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index c600083..bb9c9ae 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -46,7 +46,8 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_CTNETLINK_EXP	2
 #define NFNL_SUBSYS_QUEUE		3
 #define NFNL_SUBSYS_ULOG		4
-#define NFNL_SUBSYS_COUNT		5
+#define NFNL_SUBSYS_OSF			5
+#define NFNL_SUBSYS_COUNT		6
 
 #ifdef __KERNEL__
 
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h
new file mode 100644
index 0000000..fd2272e
--- /dev/null
+++ b/include/linux/netfilter/xt_osf.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.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 _XT_OSF_H
+#define _XT_OSF_H
+
+#define MAXGENRELEN		32
+
+#define XT_OSF_GENRE		(1<<0)
+#define	XT_OSF_TTL		(1<<1)
+#define XT_OSF_LOG		(1<<2)
+#define XT_OSF_INVERT		(1<<3)
+
+#define XT_OSF_LOGLEVEL_ALL	0	/* log all matched fingerprints */
+#define XT_OSF_LOGLEVEL_FIRST	1	/* log only the first matced fingerprint */
+#define XT_OSF_LOGLEVEL_ALL_KNOWN	2 /* do not log unknown packets */
+
+#define XT_OSF_TTL_TRUE		0	/* True ip and fingerprint TTL comparison */
+#define XT_OSF_TTL_LESS		1	/* Check if ip TTL is less than fingerprint one */
+#define XT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
+
+struct xt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct xt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct xt_osf_opt {
+	__u16			kind, length;
+	struct xt_osf_wc	wc;
+};
+
+struct xt_osf_user_finger {
+	struct xt_osf_wc	wss;
+
+	__u8			ttl, df;
+	__u16			ss, mss;
+	__u16			opt_num;
+
+	char			genre[MAXGENRELEN];
+	char			version[MAXGENRELEN];
+	char			subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct xt_osf_opt	opt[MAX_IPOPTLEN];
+};
+
+struct xt_osf_nlmsg {
+	struct xt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* Defines for IANA option kinds */
+
+enum iana_options {
+	OSFOPT_EOL = 0,		/* End of options */
+	OSFOPT_NOP, 		/* NOP */
+	OSFOPT_MSS, 		/* Maximum segment size */
+	OSFOPT_WSO, 		/* Window scale option */
+	OSFOPT_SACKP,		/* SACK permitted */
+	OSFOPT_SACK,		/* SACK */
+	OSFOPT_ECHO,
+	OSFOPT_ECHOREPLY,
+	OSFOPT_TS,		/* Timestamp option */
+	OSFOPT_POCP,		/* Partial Order Connection Permitted */
+	OSFOPT_POSP,		/* Partial Order Service Profile */
+
+	/* Others are not used in the current OSF */
+	OSFOPT_EMPTY = 255,
+};
+
+/*
+ * Initial window size option state machine: multiple of mss, mtu or
+ * plain numeric value. Can also be made as plain numeric value which
+ * is not a multiple of specified value.
+ */
+enum xt_osf_window_size_options {
+	OSF_WSS_PLAIN	= 0,
+	OSF_WSS_MSS,
+	OSF_WSS_MTU,
+	OSF_WSS_MODULO,
+	OSF_WSS_MAX,
+};
+
+/*
+ * Add/remove fingerprint from the kernel.
+ */
+enum xt_osf_msg_types {
+	OSF_MSG_ADD,
+	OSF_MSG_REMOVE,
+	OSF_MSG_MAX,
+};
+
+enum xt_osf_attr_type {
+	OSF_ATTR_UNSPEC,
+	OSF_ATTR_FINGER,
+	OSF_ATTR_MAX,
+};
+
+#endif				/* _XT_OSF_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 2329c5f..3a6ecee 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -916,6 +916,19 @@ config NETFILTER_XT_MATCH_U32
 
 	  Details and examples are in the kernel module source.
 
+config NETFILTER_XT_MATCH_OSF
+	tristate '"osf" Passive OS fingerprint match'
+	depends on NETFILTER_ADVANCED && NETFILTER_NETLINK
+	help
+	  This option selects the Passive OS Fingerprinting match module
+	  that allows to passively match the remote operating system by
+	  analyzing incoming TCP SYN packets.
+
+	  Rules and loading software can be downloaded from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 endif # NETFILTER_XTABLES
 
 endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 6282060..49f62ee 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
new file mode 100644
index 0000000..55cabe4
--- /dev/null
+++ b/net/netfilter/xt_osf.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/rculist.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/x_tables.h>
+#include <net/netfilter/nf_log.h>
+#include <linux/netfilter/xt_osf.h>
+
+struct xt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct xt_osf_user_finger	finger;
+};
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct xt_osf_finger_storage {
+	struct list_head		finger_list;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+static struct xt_osf_finger_storage xt_osf_fingers[2];
+
+static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
+	[OSF_ATTR_FINGER]	= { .len = sizeof(struct xt_osf_user_finger) },
+};
+
+static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
+			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
+{
+	struct xt_osf_user_finger *f;
+	struct xt_osf_finger *kf = NULL, *sf;
+	struct xt_osf_finger_storage *st;
+	int err = 0;
+
+	if (!osf_attrs[OSF_ATTR_FINGER])
+		return -EINVAL;
+
+	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
+	st = &xt_osf_fingers[!!f->df];
+
+	kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
+	if (!kf)
+		return -ENOMEM;
+
+	memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
+			continue;
+
+		/*
+		 * We are protected by nfnl mutex.
+		 */
+		kfree(kf);
+		kf = NULL;
+
+		err = -EEXIST;
+		break;
+	}
+
+	if (kf)
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+	rcu_read_unlock();
+
+	return err;
+}
+
+static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb,
+			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
+{
+	struct xt_osf_user_finger *f;
+	struct xt_osf_finger *sf;
+	struct xt_osf_finger_storage *st;
+	int err = ENOENT;
+
+	if (!osf_attrs[OSF_ATTR_FINGER])
+		return -EINVAL;
+
+	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
+	st = &xt_osf_fingers[!!f->df];
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
+			continue;
+
+		/*
+		 * We are protected by nfnl mutex.
+		 */
+		list_del_rcu(&sf->finger_entry);
+		call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu);
+
+		err = 0;
+		break;
+	}
+	rcu_read_unlock();
+
+	return err;
+}
+
+static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
+	[OSF_MSG_ADD]	= {
+		.call		= xt_osf_add_callback,
+		.attr_count	= OSF_ATTR_MAX,
+		.policy		= xt_osf_policy,
+	},
+	[OSF_MSG_REMOVE]	= {
+		.call		= xt_osf_remove_callback,
+		.attr_count	= OSF_ATTR_MAX,
+		.policy		= xt_osf_policy,
+	},
+};
+
+static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
+	.name			= "osf",
+	.subsys_id		= NFNL_SUBSYS_OSF,
+	.cb_count		= OSF_MSG_MAX,
+	.cb			= xt_osf_nfnetlink_callbacks,
+};
+
+static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
+			    unsigned char f_ttl)
+{
+	const struct iphdr *ip = ip_hdr(skb);
+
+	if (info->flags & XT_OSF_TTL) {
+		if (info->ttl == XT_OSF_TTL_TRUE)
+			return ip->ttl == f_ttl;
+		if (info->ttl == XT_OSF_TTL_NOCHECK)
+			return 1;
+		else if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+
+	return ip->ttl == f_ttl;
+}
+
+static bool xt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	const struct xt_osf_info *info = p->matchinfo;
+	const struct iphdr *ip = ip_hdr(skb);
+	const struct tcphdr *tcp;
+	struct tcphdr _tcph;
+	int fmatch = FMATCH_WRONG, fcount = 0;
+	unsigned int optsize = 0, check_WSS = 0;
+	u16 window, totlen, mss = 0;
+	bool df;
+	const unsigned char *optp = NULL, *_optp = NULL;
+	unsigned char opts[MAX_IPOPTLEN];
+	const struct xt_osf_finger *kf;
+	const struct xt_osf_user_finger *f;
+	const struct xt_osf_finger_storage *st;
+
+	if (!info)
+		return false;
+
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
+	if (!tcp)
+		return false;
+
+	if (!tcp->syn)
+		return false;
+
+	totlen = ntohs(ip->tot_len);
+	df = ntohs(ip->frag_off) & IP_DF;
+	window = ntohs(tcp->window);
+
+	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
+		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
+
+		if (optsize > sizeof(opts))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) +
+				sizeof(struct tcphdr), optsize, opts);
+	}
+
+	st = &xt_osf_fingers[df];
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
+			continue;
+
+		optp = _optp;
+		fmatch = FMATCH_WRONG;
+
+		if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) {
+			int foptsize, optnum;
+
+			/*
+			 * Should not happen if userspace parser was written correctly.
+			 */
+			if (f->wss.wc >= OSF_WSS_MAX)
+				continue;
+
+			/* Check options */
+
+			foptsize = 0;
+			for (optnum = 0; optnum < f->opt_num; ++optnum)
+				foptsize += f->opt[optnum].length;
+
+			if (foptsize > MAX_IPOPTLEN ||
+				optsize > MAX_IPOPTLEN ||
+				optsize != foptsize)
+				continue;
+
+			check_WSS = f->wss.wc;
+
+			for (optnum = 0; optnum < f->opt_num; ++optnum) {
+				if (f->opt[optnum].kind == (*optp)) {
+					__u32 len = f->opt[optnum].length;
+					const __u8 *optend = optp + len;
+					int loop_cont = 0;
+
+					fmatch = FMATCH_OK;
+
+					switch (*optp) {
+					case OSFOPT_MSS:
+						mss = optp[3];
+						mss <<= 8;
+						mss |= optp[2];
+
+						mss = ntohs(mss);
+						break;
+					case OSFOPT_TS:
+						loop_cont = 1;
+						break;
+					}
+
+					optp = optend;
+				} else
+					fmatch = FMATCH_OPT_WRONG;
+
+				if (fmatch != FMATCH_OK)
+					break;
+			}
+
+			if (fmatch != FMATCH_OPT_WRONG) {
+				fmatch = FMATCH_WRONG;
+
+				switch (check_WSS) {
+				case OSF_WSS_PLAIN:
+					if (f->wss.val == 0 || window == f->wss.val)
+						fmatch = FMATCH_OK;
+					break;
+				case OSF_WSS_MSS:
+					/*
+					 * Some smart modems decrease mangle MSS to 
+					 * SMART_MSS_2, so we check standard, decreased
+					 * and the one provided in the fingerprint MSS
+					 * values.
+					 */
+#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 OSF_WSS_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 OSF_WSS_MODULO:
+					if ((window % f->wss.val) == 0)
+						fmatch = FMATCH_OK;
+					break;
+				}
+			}
+
+			if (fmatch != FMATCH_OK)
+				continue;
+
+			fcount++;
+			/*
+			 * Everyone uses NULL loginfo (well, everyone uses
+			 * NULL for all but format and message fields), so
+			 * I did not set it up either.
+			 */
+			if (info->flags & XT_OSF_LOG)
+				nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+					"%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
+					f->genre, f->version, f->subtype,
+					&ip->saddr, ntohs(tcp->source),
+					&ip->daddr, ntohs(tcp->dest),
+					f->ttl - ip->ttl);
+
+			if ((info->flags & XT_OSF_LOG) &&
+			    info->loglevel == XT_OSF_LOGLEVEL_FIRST)
+				break;
+		}
+	}
+	rcu_read_unlock();
+
+	if (!fcount && (info->flags & XT_OSF_LOG)) {
+		unsigned int i;
+		struct xt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+
+		if (info->flags & XT_OSF_LOG) {
+			if (info->loglevel != XT_OSF_LOGLEVEL_ALL_KNOWN)
+				nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+					"window: %u, mss: %u, totlen: %u, df: %d, ttl: %u : ",
+					window, mss, totlen, df, ip->ttl);
+			if (_optp) {
+				optp = _optp;
+				for (i = 0; i < optsize; i++)
+					nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+						"%02X ", optp[i]);
+			}
+
+			nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+					"%pi4:%u -> %pi4:%u\n",
+			     &ip->saddr, ntohs(tcp->source),
+			     &ip->daddr, ntohs(tcp->dest));
+		}
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return fmatch == FMATCH_OK;
+}
+
+static struct xt_match xt_osf_match = {
+	.name 		= "osf",
+	.revision	= 0,
+	.family		= NFPROTO_IPV4,
+	.proto		= IPPROTO_TCP,
+	.hooks      	= (1 << NF_INET_LOCAL_IN) |
+				(1 << NF_INET_PRE_ROUTING) |
+				(1 << NF_INET_FORWARD),
+	.match 		= xt_osf_match_packet,
+	.matchsize	= sizeof(struct xt_osf_info),
+	.me		= THIS_MODULE,
+};
+
+static int __init xt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+	}
+
+	err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
+	if (err < 0) {
+		printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&xt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+err_out_exit:
+	return err;
+}
+
+static void __exit xt_osf_fini(void)
+{
+	struct xt_osf_finger *f;
+	int i;
+
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+	xt_unregister_match(&xt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, xt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(xt_osf_init);
+module_exit(xt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);

-- 
	Evgeniy Polyakov

[-- Attachment #2: libxt_osf.c --]
[-- Type: text/x-csrc, Size: 4751 bytes --]

/*
 * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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
 */

/*
 * xtables 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 <linux/types.h>

#include <xtables.h>

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

#include "xt_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"
		);
}


static const struct option osf_opts[] = {
	{ .name = "genre",	.has_arg = true, .val = '1' },
	{ .name = "ttl",	.has_arg = true, .val = '2' },
	{ .name = "log",	.has_arg = true, .val = '3' },
	{ .name = NULL }
};


static void osf_parse_string(const unsigned char *s, struct xt_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 xt_osf_info *info = (struct xt_osf_info *)(*match)->data;
	
	switch(c) {
		case '1': /* --genre */
			if (*flags & XT_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 |= XT_OSF_INVERT;
			info->len=strlen(info->genre);
			*flags |= XT_OSF_GENRE;
			break;
		case '2': /* --ttl */
			if (*flags & XT_OSF_TTL)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple ttl parameter");
			*flags |= XT_OSF_TTL;
			info->flags |= XT_OSF_TTL;
			if (!xtables_strtoui(argv[optind-1], NULL, &info->ttl, 0, 2))
				xtables_error(PARAMETER_PROBLEM, "TTL parameter is too big");
			break;
		case '3': /* --log */
			if (*flags & XT_OSF_LOG)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
			*flags |= XT_OSF_LOG;
			if (!xtables_strtoui(argv[optind-1], NULL, &info->loglevel, 0, 2))
				xtables_error(PARAMETER_PROBLEM, "Log level parameter is too big");
			info->flags |= XT_OSF_LOG;
			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 xt_osf_info *info = (const struct xt_osf_info*) match->data;

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

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

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

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

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

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

* Re: Passive OS fingerprint xtables match.
  2009-03-30  6:20           ` Evgeniy Polyakov
@ 2009-05-01 20:15             ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-05-01 20:15 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Hi.

On Mon, Mar 30, 2009 at 10:20:52AM +0400, Evgeniy Polyakov (zbr@ioremap.net) wrote:
> On Thu, Mar 26, 2009 at 04:47:38PM +0100, Patrick McHardy (kaber@trash.net) wrote:
> > Thanks. I'll wait for Dave's decision before applying it.
> 
> Looks like David did not pull it in, please queue OSF and its library
> into netfilter tree.
> Thank you.

Just curious... Ping? :)

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-26 15:47         ` Patrick McHardy
@ 2009-03-30  6:20           ` Evgeniy Polyakov
  2009-05-01 20:15             ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-30  6:20 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

On Thu, Mar 26, 2009 at 04:47:38PM +0100, Patrick McHardy (kaber@trash.net) wrote:
> Thanks. I'll wait for Dave's decision before applying it.

Looks like David did not pull it in, please queue OSF and its library
into netfilter tree.
Thank you.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-26 15:41       ` Evgeniy Polyakov
@ 2009-03-26 15:47         ` Patrick McHardy
  2009-03-30  6:20           ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-03-26 15:47 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> On Thu, Mar 26, 2009 at 04:08:07PM +0100, Patrick McHardy (kaber@trash.net) wrote:
>> nfnetlink also supports notifications. Please get rid of this,
>> it should be no problem to resurrect the necessary parts later
>> if this is desired.
> 
> Ok, patch below does not contain it.

Thanks. I'll wait for Dave's decision before applying it.

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

* Re: Passive OS fingerprint xtables match.
  2009-03-26 15:08     ` Patrick McHardy
@ 2009-03-26 15:41       ` Evgeniy Polyakov
  2009-03-26 15:47         ` Patrick McHardy
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-26 15:41 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

On Thu, Mar 26, 2009 at 04:08:07PM +0100, Patrick McHardy (kaber@trash.net) wrote:
> nfnetlink also supports notifications. Please get rid of this,
> it should be no problem to resurrect the necessary parts later
> if this is desired.

Ok, patch below does not contain it.

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 7d8e045..71babbd 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -46,7 +46,8 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_CTNETLINK_EXP	2
 #define NFNL_SUBSYS_QUEUE		3
 #define NFNL_SUBSYS_ULOG		4
-#define NFNL_SUBSYS_COUNT		5
+#define NFNL_SUBSYS_OSF			5
+#define NFNL_SUBSYS_COUNT		6
 
 #ifdef __KERNEL__
 
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h
new file mode 100644
index 0000000..11903a9
--- /dev/null
+++ b/include/linux/netfilter/xt_osf.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.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 _XT_OSF_H
+#define _XT_OSF_H
+
+#define MAXGENRELEN		32
+#define MAXDETLEN		64
+
+#define XT_OSF_GENRE		(1<<0)
+#define	XT_OSF_TTL		(1<<1)
+#define XT_OSF_LOG		(1<<2)
+#define XT_OSF_UNUSED		(1<<3)
+#define XT_OSF_CONNECTOR	(1<<4)
+#define XT_OSF_INVERT		(1<<5)
+
+#define XT_OSF_LOGLEVEL_ALL	0
+#define XT_OSF_LOGLEVEL_FIRST	1
+#define XT_OSF_LOGLEVEL_ALL_KNOWN	2
+
+#define XT_OSF_TTL_TRUE		0	/* True ip and fingerprint TTL comparison */
+#define XT_OSF_TTL_LESS		1	/* Check if ip TTL is less than fingerprint one */
+#define XT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
+
+struct xt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct xt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct xt_osf_opt {
+	__u16			kind, length;
+	struct xt_osf_wc	wc;
+};
+
+struct xt_osf_user_finger {
+	struct xt_osf_wc	wss;
+
+	__u8			ttl, df;
+	__u16			ss, mss;
+	__u16			opt_num;
+
+	char			genre[MAXGENRELEN];
+	char			version[MAXGENRELEN];
+	char			subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct xt_osf_opt	opt[MAX_IPOPTLEN];
+};
+
+struct xt_osf_nlmsg {
+	struct xt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* Defines for IANA option kinds */
+
+enum iana_options {
+	OSFOPT_EOL = 0,		/* End of options */
+	OSFOPT_NOP, 		/* NOP */
+	OSFOPT_MSS, 		/* Maximum segment size */
+	OSFOPT_WSO, 		/* Window scale option */
+	OSFOPT_SACKP,		/* SACK permitted */
+	OSFOPT_SACK,		/* SACK */
+	OSFOPT_ECHO,
+	OSFOPT_ECHOREPLY,
+	OSFOPT_TS,		/* Timestamp option */
+	OSFOPT_POCP,		/* Partial Order Connection Permitted */
+	OSFOPT_POSP,		/* Partial Order Service Profile */
+
+	/* Others are not used in the current OSF */
+	OSFOPT_EMPTY = 255,
+};
+
+enum xt_osf_msg_types {
+	OSF_MSG_SETUP,
+	OSF_MSG_MAX,
+};
+
+enum xt_osf_attr_type {
+	OSF_ATTR_UNSPEC,
+	OSF_ATTR_FINGER,
+	OSF_ATTR_MAX,
+};
+
+#endif				/* _XT_OSF_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index c2bac9c..1e74c97 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -853,6 +853,19 @@ config NETFILTER_XT_MATCH_U32
 
 	  Details and examples are in the kernel module source.
 
+config NETFILTER_XT_MATCH_OSF
+	tristate '"osf" Passive OS fingerprint match'
+	depends on NETFILTER_ADVANCED
+	help
+	  This option selects the Passive OS Fingerprinting match module
+	  that allows to passively match the remote operating system by
+	  analyzing incoming TCP SYN packets.
+
+	  Rules and loading software can be downloaded from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 endif # NETFILTER_XTABLES
 
 endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index da3d909..96b7754 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
new file mode 100644
index 0000000..65e94ad
--- /dev/null
+++ b/net/netfilter/xt_osf.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/connector.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/rculist.h>
+#include <linux/smp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_osf.h>
+
+struct xt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct xt_osf_user_finger	finger;
+};
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct xt_osf_finger_storage
+{
+	struct list_head		finger_list;
+	spinlock_t			finger_lock;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+struct xt_osf_finger_storage xt_osf_fingers[2];
+
+struct xt_osf_message {
+	struct cn_msg		cmsg;
+	struct xt_osf_nlmsg	nlmsg;
+};
+
+static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
+	[OSF_ATTR_FINGER]	= { .len = sizeof(struct xt_osf_user_finger) },
+};
+
+static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static int xt_osf_setup_callback(struct sock *ctnl, struct sk_buff *skb,
+			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
+{
+	struct xt_osf_user_finger *f;
+	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+	u16 delete = ntohs(nfmsg->res_id);
+	struct xt_osf_finger *kf = NULL, *sf;
+	struct xt_osf_finger_storage *st;
+	int err;
+
+	if (!osf_attrs[OSF_ATTR_FINGER])
+		return -EINVAL;
+
+	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
+	st = &xt_osf_fingers[!!f->df];
+
+	/*
+	 * If 'delete' is set to 0 then we add attached fingerprint,
+	 * otherwise remove, and in this case we do not need to allocate data.
+	 */
+	if (!delete) {
+		kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
+		if (!kf)
+			return -ENOMEM;
+
+		memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
+	}
+
+	err = -ENOENT;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
+			continue;
+
+		if (delete) {
+			spin_lock_bh(&st->finger_lock);
+			list_del_rcu(&sf->finger_entry);
+			spin_unlock_bh(&st->finger_lock);
+			call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu);
+		} else {
+			kfree(kf);
+			kf = NULL;
+		}
+
+		err = 0;
+		break;
+	}
+
+	if (kf) {
+		spin_lock_bh(&st->finger_lock);
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+		spin_unlock_bh(&st->finger_lock);
+#if 0
+		printk(KERN_INFO "Added rule for %s:%s:%s.\n",
+			kf->finger.genre, kf->finger.version, kf->finger.subtype);
+#endif
+	}
+	rcu_read_unlock();
+
+	return 0;
+}
+
+static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
+	[OSF_MSG_SETUP]	= {
+		.call		= xt_osf_setup_callback,
+		.attr_count	= OSF_ATTR_MAX,
+		.policy		= xt_osf_policy,
+	},
+};
+
+static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
+	.name			= "osf",
+	.subsys_id		= NFNL_SUBSYS_OSF,
+	.cb_count		= OSF_MSG_MAX,
+	.cb			= xt_osf_nfnetlink_callbacks,
+};
+
+static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
+			    unsigned char f_ttl)
+{
+	const struct iphdr *ip = ip_hdr(skb);
+
+	if (info->flags & XT_OSF_TTL) {
+		if (info->ttl == XT_OSF_TTL_TRUE)
+			return ip->ttl == f_ttl;
+		if (info->ttl == XT_OSF_TTL_NOCHECK)
+			return 1;
+		else if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+	
+	return ip->ttl == f_ttl;
+}
+
+static bool xt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	const struct xt_osf_info *info = p->matchinfo;
+	const struct iphdr *ip = ip_hdr(skb);
+	const struct tcphdr *tcp;
+	struct tcphdr _tcph;
+	int fmatch = FMATCH_WRONG, fcount = 0;
+	unsigned int optsize = 0, check_WSS = 0;
+	u16 window, totlen, mss = 0;
+	bool df;
+	const unsigned char *optp = NULL, *_optp = NULL;
+	unsigned char opts[MAX_IPOPTLEN];
+	const struct xt_osf_finger *kf;
+	const struct xt_osf_user_finger *f;
+	const struct xt_osf_finger_storage *st;
+
+	if (!info)
+		return false;
+
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
+	if (!tcp)
+		return false;
+
+	if (!tcp->syn)
+		return false;
+
+	totlen = ntohs(ip->tot_len);
+	df = ntohs(ip->frag_off) & IP_DF;
+	window = ntohs(tcp->window);
+
+	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
+		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
+
+		if (optsize > sizeof(opts))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) + sizeof(struct tcphdr), 
+				optsize, opts);
+	}
+
+	st = &xt_osf_fingers[df];
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
+			continue;
+
+		optp = _optp;
+		fmatch = FMATCH_WRONG;
+
+		if (totlen == f->ss && xt_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:
+				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 (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;
+					const __u8 *optend = optp + len;
+					int loop_cont = 0;
+
+					fmatch = FMATCH_OK;
+
+					switch (*optp) {
+					case OSFOPT_MSS:
+						mss = optp[3];
+						mss <<= 8;
+						mss |= optp[2];
+
+						mss = ntohs(mss);
+						break;
+					case OSFOPT_TS:
+						loop_cont = 1;
+						break;
+					}
+
+					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)
+				continue;
+
+			fcount++;
+			if (info->flags & XT_OSF_LOG)
+				printk(KERN_INFO "%s [%s:%s] : "
+					"%pi4:%d -> %pi4:%d hops=%d\n",
+					f->genre, f->version, f->subtype, 
+					&ip->saddr, ntohs(tcp->source), 
+					&ip->daddr, ntohs(tcp->dest), 
+					f->ttl - ip->ttl);
+
+			if ((info->flags & XT_OSF_LOG) &&
+			    info->loglevel == XT_OSF_LOGLEVEL_FIRST)
+				break;
+		}
+	}
+	rcu_read_unlock();
+
+	if (!fcount && (info->flags & (XT_OSF_LOG | XT_OSF_CONNECTOR))) {
+		unsigned int i;
+		struct xt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+#if 1
+		if (info->flags & XT_OSF_LOG) {
+			if (info->loglevel != XT_OSF_LOGLEVEL_ALL_KNOWN)
+				printk(KERN_INFO "Unknown: win: %u, mss: %u, "
+					"totlen: %u, df: %d, ttl: %u : ",
+					window, mss, totlen, df, ip->ttl);
+			else
+				printk(KERN_INFO "");
+			if (_optp) {
+				optp = _optp;
+				for (i = 0; i < optsize; i++)
+					printk("%02X ", optp[i]);
+			}
+
+			printk("%pi4:%u -> %pi4:%u\n",
+			     &ip->saddr, ntohs(tcp->source),
+			     &ip->daddr, ntohs(tcp->dest));
+		}
+#endif
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return fmatch == FMATCH_OK;
+}
+
+static struct xt_match xt_osf_match = {
+	.name 		= "osf",
+	.revision	= 0,
+	.family		= NFPROTO_IPV4,
+	.proto		= IPPROTO_TCP,
+	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_FORWARD),
+	.match 		= xt_osf_match_packet,
+	.matchsize	= sizeof(struct xt_osf_info),
+	.me		= THIS_MODULE,
+};
+
+static int __init xt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+		spin_lock_init(&st->finger_lock);
+	}
+
+	err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
+	if (err < 0) {
+		printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&xt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+err_out_exit:
+	return err;
+}
+
+static void __exit xt_osf_fini(void)
+{
+	struct xt_osf_finger *f;
+	int i;
+
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+	xt_unregister_match(&xt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, xt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(xt_osf_init);
+module_exit(xt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);


-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-26 14:59   ` Evgeniy Polyakov
@ 2009-03-26 15:08     ` Patrick McHardy
  2009-03-26 15:41       ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-03-26 15:08 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> On Thu, Mar 26, 2009 at 03:18:46PM +0100, Patrick McHardy (kaber@trash.net) wrote:
>> Evgeniy Polyakov wrote:
>>> +static void xt_osf_send_connector(const struct xt_osf_user_finger *f,
>>> +				   const struct sk_buff *skb)
>>> +{
>>> +#if 1
>>> +	return;
>>> +#else
>> Please get rid of the connector remains.
>>
>> The decision whether to still merge it in this window is up to Dave.
>> I already have some late-comers queued up that I hope to still get
>> in and I don't want to unnecessarily decrease my chances :)
> 
> I left that chunk to the case when we will want to send notifications to
> the userspace, right now we do not , so it is commented. And yes, names
> were not changed :)

nfnetlink also supports notifications. Please get rid of this,
it should be no problem to resurrect the necessary parts later
if this is desired.

> If Dave will refuse to pull it, please queue it into the netfilter tree
> for the next merge window update, since I will miss it almost for sure.

Will do.



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

* Re: Passive OS fingerprint xtables match.
  2009-03-26 14:18 ` Patrick McHardy
@ 2009-03-26 14:59   ` Evgeniy Polyakov
  2009-03-26 15:08     ` Patrick McHardy
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-26 14:59 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

On Thu, Mar 26, 2009 at 03:18:46PM +0100, Patrick McHardy (kaber@trash.net) wrote:
> Evgeniy Polyakov wrote:
> >+static void xt_osf_send_connector(const struct xt_osf_user_finger *f,
> >+				   const struct sk_buff *skb)
> >+{
> >+#if 1
> >+	return;
> >+#else
> 
> Please get rid of the connector remains.
> 
> The decision whether to still merge it in this window is up to Dave.
> I already have some late-comers queued up that I hope to still get
> in and I don't want to unnecessarily decrease my chances :)

I left that chunk to the case when we will want to send notifications to
the userspace, right now we do not , so it is commented. And yes, names
were not changed :)

If Dave will refuse to pull it, please queue it into the netfilter tree
for the next merge window update, since I will miss it almost for sure.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-26 14:14 Evgeniy Polyakov
@ 2009-03-26 14:18 ` Patrick McHardy
  2009-03-26 14:59   ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-03-26 14:18 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> +static void xt_osf_send_connector(const struct xt_osf_user_finger *f,
> +				   const struct sk_buff *skb)
> +{
> +#if 1
> +	return;
> +#else

Please get rid of the connector remains.

The decision whether to still merge it in this window is up to Dave.
I already have some late-comers queued up that I hope to still get
in and I don't want to unnecessarily decrease my chances :)

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

* Passive OS fingerprint xtables match.
@ 2009-03-26 14:14 Evgeniy Polyakov
  2009-03-26 14:18 ` Patrick McHardy
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-26 14:14 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
and loaded via netfilter netlink subsystem into the kernel via special
util found in archive.

Archive contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

This release moves library build to the 1.4.3 features and adds
nfnetlink support for the module configuration.
Also I incorporated all comments which were rised during the previous
sends.

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os

Example usage:
# modrpobe xt_osf
# ./nfnl_osf -f ./pf.os
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2

You will find something like this in the syslog:
Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139 hops=4
Linux [2.5:] : 1.2.3.4:44448 -> 11.22.33.44:22 hops=4

Please consider for inclusion.
Thank you.

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

And a small foot note: netfilter guys, you have really, Really, REALLY
weird feel of the reality since porting whatever else to the netfilter
netlink code requires non-trivial amount of god-blessed substances
processed by the brain of the poor human.

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 7d8e045..71babbd 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -46,7 +46,8 @@ struct nfgenmsg {
 #define NFNL_SUBSYS_CTNETLINK_EXP	2
 #define NFNL_SUBSYS_QUEUE		3
 #define NFNL_SUBSYS_ULOG		4
-#define NFNL_SUBSYS_COUNT		5
+#define NFNL_SUBSYS_OSF			5
+#define NFNL_SUBSYS_COUNT		6
 
 #ifdef __KERNEL__
 
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h
new file mode 100644
index 0000000..11903a9
--- /dev/null
+++ b/include/linux/netfilter/xt_osf.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.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 _XT_OSF_H
+#define _XT_OSF_H
+
+#define MAXGENRELEN		32
+#define MAXDETLEN		64
+
+#define XT_OSF_GENRE		(1<<0)
+#define	XT_OSF_TTL		(1<<1)
+#define XT_OSF_LOG		(1<<2)
+#define XT_OSF_UNUSED		(1<<3)
+#define XT_OSF_CONNECTOR	(1<<4)
+#define XT_OSF_INVERT		(1<<5)
+
+#define XT_OSF_LOGLEVEL_ALL	0
+#define XT_OSF_LOGLEVEL_FIRST	1
+#define XT_OSF_LOGLEVEL_ALL_KNOWN	2
+
+#define XT_OSF_TTL_TRUE		0	/* True ip and fingerprint TTL comparison */
+#define XT_OSF_TTL_LESS		1	/* Check if ip TTL is less than fingerprint one */
+#define XT_OSF_TTL_NOCHECK	2	/* Do not compare ip and fingerprint TTL at all */
+
+struct xt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct xt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct xt_osf_opt {
+	__u16			kind, length;
+	struct xt_osf_wc	wc;
+};
+
+struct xt_osf_user_finger {
+	struct xt_osf_wc	wss;
+
+	__u8			ttl, df;
+	__u16			ss, mss;
+	__u16			opt_num;
+
+	char			genre[MAXGENRELEN];
+	char			version[MAXGENRELEN];
+	char			subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct xt_osf_opt	opt[MAX_IPOPTLEN];
+};
+
+struct xt_osf_nlmsg {
+	struct xt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* Defines for IANA option kinds */
+
+enum iana_options {
+	OSFOPT_EOL = 0,		/* End of options */
+	OSFOPT_NOP, 		/* NOP */
+	OSFOPT_MSS, 		/* Maximum segment size */
+	OSFOPT_WSO, 		/* Window scale option */
+	OSFOPT_SACKP,		/* SACK permitted */
+	OSFOPT_SACK,		/* SACK */
+	OSFOPT_ECHO,
+	OSFOPT_ECHOREPLY,
+	OSFOPT_TS,		/* Timestamp option */
+	OSFOPT_POCP,		/* Partial Order Connection Permitted */
+	OSFOPT_POSP,		/* Partial Order Service Profile */
+
+	/* Others are not used in the current OSF */
+	OSFOPT_EMPTY = 255,
+};
+
+enum xt_osf_msg_types {
+	OSF_MSG_SETUP,
+	OSF_MSG_MAX,
+};
+
+enum xt_osf_attr_type {
+	OSF_ATTR_UNSPEC,
+	OSF_ATTR_FINGER,
+	OSF_ATTR_MAX,
+};
+
+#endif				/* _XT_OSF_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index c2bac9c..1e74c97 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -853,6 +853,19 @@ config NETFILTER_XT_MATCH_U32
 
 	  Details and examples are in the kernel module source.
 
+config NETFILTER_XT_MATCH_OSF
+	tristate '"osf" Passive OS fingerprint match'
+	depends on NETFILTER_ADVANCED
+	help
+	  This option selects the Passive OS Fingerprinting match module
+	  that allows to passively match the remote operating system by
+	  analyzing incoming TCP SYN packets.
+
+	  Rules and loading software can be downloaded from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 endif # NETFILTER_XTABLES
 
 endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index da3d909..96b7754 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
new file mode 100644
index 0000000..9c15cb2
--- /dev/null
+++ b/net/netfilter/xt_osf.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/connector.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/rculist.h>
+#include <linux/smp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_osf.h>
+
+struct xt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct xt_osf_user_finger	finger;
+};
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct xt_osf_finger_storage
+{
+	struct list_head		finger_list;
+	spinlock_t			finger_lock;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+struct xt_osf_finger_storage xt_osf_fingers[2];
+
+struct xt_osf_message {
+	struct cn_msg		cmsg;
+	struct xt_osf_nlmsg	nlmsg;
+};
+
+static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
+	[OSF_ATTR_FINGER]	= { .len = sizeof(struct xt_osf_user_finger) },
+};
+
+static void xt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct xt_osf_finger *f = container_of(rcu_head, struct xt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static int xt_osf_setup_callback(struct sock *ctnl, struct sk_buff *skb,
+			struct nlmsghdr *nlh, struct nlattr *osf_attrs[])
+{
+	struct xt_osf_user_finger *f;
+	struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);
+	u16 delete = ntohs(nfmsg->res_id);
+	struct xt_osf_finger *kf = NULL, *sf;
+	struct xt_osf_finger_storage *st;
+	int err;
+
+	if (!osf_attrs[OSF_ATTR_FINGER])
+		return -EINVAL;
+
+	f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
+	st = &xt_osf_fingers[!!f->df];
+
+	/*
+	 * If 'delete' is set to 0 then we add attached fingerprint,
+	 * otherwise remove, and in this case we do not need to allocate data.
+	 */
+	if (!delete) {
+		kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
+		if (!kf)
+			return -ENOMEM;
+
+		memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
+	}
+
+	err = -ENOENT;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
+			continue;
+
+		if (delete) {
+			spin_lock_bh(&st->finger_lock);
+			list_del_rcu(&sf->finger_entry);
+			spin_unlock_bh(&st->finger_lock);
+			call_rcu(&sf->rcu_head, xt_osf_finger_free_rcu);
+		} else {
+			kfree(kf);
+			kf = NULL;
+		}
+
+		err = 0;
+		break;
+	}
+
+	if (kf) {
+		spin_lock_bh(&st->finger_lock);
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+		spin_unlock_bh(&st->finger_lock);
+#if 0
+		printk(KERN_INFO "Added rule for %s:%s:%s.\n",
+			kf->finger.genre, kf->finger.version, kf->finger.subtype);
+#endif
+	}
+	rcu_read_unlock();
+
+	return 0;
+}
+
+static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
+	[OSF_MSG_SETUP]	= {
+		.call		= xt_osf_setup_callback,
+		.attr_count	= OSF_ATTR_MAX,
+		.policy		= xt_osf_policy,
+	},
+};
+
+static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
+	.name			= "osf",
+	.subsys_id		= NFNL_SUBSYS_OSF,
+	.cb_count		= OSF_MSG_MAX,
+	.cb			= xt_osf_nfnetlink_callbacks,
+};
+
+static void xt_osf_send_connector(const struct xt_osf_user_finger *f,
+				   const struct sk_buff *skb)
+{
+#if 1
+	return;
+#else
+	struct xt_osf_message *msg = &per_cpu(xt_osf_mbuf, smp_processor_id());
+	struct xt_osf_nlmsg *data = &msg->nlmsg;
+	struct tcphdr *tcp;
+
+
+	memcpy(&data->f, f, sizeof(struct xt_osf_user_finger));
+	memcpy(&data->ip, ip_hdr(skb), sizeof(struct iphdr));
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &data->tcp);
+	if (tcp)
+		memcpy(&data->tcp, tcp, sizeof(struct tcphdr));
+
+	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
+#endif
+}
+
+static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
+			    unsigned char f_ttl)
+{
+	const struct iphdr *ip = ip_hdr(skb);
+
+	if (info->flags & XT_OSF_TTL) {
+		if (info->ttl == XT_OSF_TTL_TRUE)
+			return ip->ttl == f_ttl;
+		if (info->ttl == XT_OSF_TTL_NOCHECK)
+			return 1;
+		else if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+	
+	return ip->ttl == f_ttl;
+}
+
+static bool xt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	const struct xt_osf_info *info = p->matchinfo;
+	const struct iphdr *ip = ip_hdr(skb);
+	const struct tcphdr *tcp;
+	struct tcphdr _tcph;
+	int fmatch = FMATCH_WRONG, fcount = 0;
+	unsigned int optsize = 0, check_WSS = 0;
+	u16 window, totlen, mss = 0;
+	bool df;
+	const unsigned char *optp = NULL, *_optp = NULL;
+	unsigned char opts[MAX_IPOPTLEN];
+	const struct xt_osf_finger *kf;
+	const struct xt_osf_user_finger *f;
+	const struct xt_osf_finger_storage *st;
+
+	if (!info)
+		return false;
+
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
+	if (!tcp)
+		return false;
+
+	if (!tcp->syn)
+		return false;
+
+	totlen = ntohs(ip->tot_len);
+	df = ntohs(ip->frag_off) & IP_DF;
+	window = ntohs(tcp->window);
+
+	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
+		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
+
+		if (optsize > sizeof(opts))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) + sizeof(struct tcphdr), 
+				optsize, opts);
+	}
+
+	st = &xt_osf_fingers[df];
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
+			continue;
+
+		optp = _optp;
+		fmatch = FMATCH_WRONG;
+
+		if (totlen == f->ss && xt_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:
+				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 (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;
+					const __u8 *optend = optp + len;
+					int loop_cont = 0;
+
+					fmatch = FMATCH_OK;
+
+					switch (*optp) {
+					case OSFOPT_MSS:
+						mss = optp[3];
+						mss <<= 8;
+						mss |= optp[2];
+
+						mss = ntohs(mss);
+						break;
+					case OSFOPT_TS:
+						loop_cont = 1;
+						break;
+					}
+
+					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)
+				continue;
+
+			fcount++;
+			if (info->flags & XT_OSF_LOG)
+				printk(KERN_INFO "%s [%s:%s] : "
+					"%pi4:%d -> %pi4:%d hops=%d\n",
+					f->genre, f->version, f->subtype, 
+					&ip->saddr, ntohs(tcp->source), 
+					&ip->daddr, ntohs(tcp->dest), 
+					f->ttl - ip->ttl);
+
+			if (info->flags & XT_OSF_CONNECTOR)
+				xt_osf_send_connector(f, skb);
+
+			if ((info->flags & XT_OSF_LOG) &&
+			    info->loglevel == XT_OSF_LOGLEVEL_FIRST)
+				break;
+		}
+	}
+	rcu_read_unlock();
+
+	if (!fcount && (info->flags & (XT_OSF_LOG | XT_OSF_CONNECTOR))) {
+		unsigned int i;
+		struct xt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+#if 1
+		if (info->flags & XT_OSF_LOG) {
+			if (info->loglevel != XT_OSF_LOGLEVEL_ALL_KNOWN)
+				printk(KERN_INFO "Unknown: win: %u, mss: %u, "
+					"totlen: %u, df: %d, ttl: %u : ",
+					window, mss, totlen, df, ip->ttl);
+			else
+				printk(KERN_INFO "");
+			if (_optp) {
+				optp = _optp;
+				for (i = 0; i < optsize; i++)
+					printk("%02X ", optp[i]);
+			}
+
+			printk("%pi4:%u -> %pi4:%u\n",
+			     &ip->saddr, ntohs(tcp->source),
+			     &ip->daddr, ntohs(tcp->dest));
+		}
+#endif
+		if (info->flags & XT_OSF_CONNECTOR) {
+			fg.wss.val = window;
+			fg.ttl = ip->ttl;
+			fg.df = df;
+			fg.ss = totlen;
+			fg.mss = mss;
+			strncpy(fg.genre, "Unknown", MAXGENRELEN);
+
+			xt_osf_send_connector(&fg, skb);
+		}
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return fmatch == FMATCH_OK;
+}
+
+static struct xt_match xt_osf_match = {
+	.name 		= "osf",
+	.revision	= 0,
+	.family		= NFPROTO_IPV4,
+	.proto		= IPPROTO_TCP,
+	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_FORWARD),
+	.match 		= xt_osf_match_packet,
+	.matchsize	= sizeof(struct xt_osf_info),
+	.me		= THIS_MODULE,
+};
+
+static int __init xt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+		spin_lock_init(&st->finger_lock);
+	}
+
+	err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
+	if (err < 0) {
+		printk(KERN_ERR "Failed (%d) to register OSF nsfnetlink helper.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&xt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+err_out_exit:
+	return err;
+}
+
+static void __exit xt_osf_fini(void)
+{
+	struct xt_osf_finger *f;
+	int i;
+
+	nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
+	xt_unregister_match(&xt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
+		struct xt_osf_finger_storage *st = &xt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, xt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(xt_osf_init);
+module_exit(xt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");
+MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);


-- 
	Evgeniy Polyakov

[-- Attachment #2: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4773 bytes --]

/*
 * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <linux/types.h>

#include <xtables.h>

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

#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"
		);
}


static const struct option osf_opts[] = {
	{ .name = "genre",	.has_arg = true, .val = '1' },
	{ .name = "ttl",	.has_arg = true, .val = '2' },
	{ .name = "log",	.has_arg = true, .val = '3' },
	{ .name = NULL }
};


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(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;
			if (!xtables_strtoui(argv[optind-1], NULL, &info->ttl, 0, 2))
				xtables_error(PARAMETER_PROBLEM, "TTL parameter is too big");
			break;
		case '3': /* --log */
			if (*flags & IPT_OSF_LOG)
				exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
			*flags |= IPT_OSF_LOG;
			if (!xtables_strtoui(argv[optind-1], NULL, &info->loglevel, 0, 2))
				xtables_error(PARAMETER_PROBLEM, "Log level parameter is too big");
			info->flags |= IPT_OSF_LOG;
			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,
	.parse		= osf_parse,
	.print		= osf_print,
	.final_check	= osf_final_check,
	.save		= osf_save,
	.extra_opts	= osf_opts,
	.family		= NFPROTO_IPV4
};

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

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

* Re: Passive OS fingerprint xtables match.
  2009-03-11 10:00   ` Evgeniy Polyakov
@ 2009-03-16 14:42     ` Patrick McHardy
  0 siblings, 0 replies; 54+ messages in thread
From: Patrick McHardy @ 2009-03-16 14:42 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Pablo Neira Ayuso, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> Hi Pablo.
> 
> On Wed, Mar 11, 2009 at 10:54:21AM +0100, Pablo Neira Ayuso (pablo@netfilter.org) wrote:
>>> Fingerprint matching rules can be downloaded from OpenBSD source tree
>>> and loaded via netlink connector into the kernel via special util found
>>> in archive. It will also listen for events about matching packets.
>> I like this feature. We have nfnetlink so I don't see why we should use
>> the netlink connector instead.

I fully agree.

> OSF exists about 6 years already, netlink configuration was added in
> 2005, I do not remember if nfnetlink existed those days (IIRC it did
> not, since I reused ULOG netlink first), right now I just cleanup
> what was written before.

We do have nfnetlink today however, so this argument does no longer
apply. I don't mind the order in which things are fixed up of course,
but before merging, it needs to be converted to nfnetlink.

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 21:01 ` Jesper Dangaard Brouer
  2009-03-10 21:54   ` Evgeniy Polyakov
@ 2009-03-16 14:40   ` Patrick McHardy
  1 sibling, 0 replies; 54+ messages in thread
From: Patrick McHardy @ 2009-03-16 14:40 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: Evgeniy Polyakov, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt, hawk

Jesper Dangaard Brouer wrote:
> On Tue, 10 Mar 2009, Evgeniy Polyakov wrote:
> 
>> +            call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
>> +        }
>> +    }
>> +    rcu_read_unlock();
> 
> Should the list_del_rcu() not be protected by a spinlock?
> 
> 
>> +    rcu_barrier();
> 
> In some of my code I call synchronize_net(), is it enough to call 
> rcu_barrier()?
> 
> What is the difference between:
> 
>  synchronize_rcu()
>  synchronize_net()
>  rcu_barrier()

synchronize_net() is just a call to synchronize_rcu(), so their
functionality is equivalent. synchronize_net() is however only
supposed to synchronize with RX packet processing, which is usually
not enough for netfilter. So I prefer synchronize_rcu() for clarity.

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 16:07 ` Jan Engelhardt
@ 2009-03-11 21:43   ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-11 21:43 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Hi Jan.

Thanks for the review, I will incorporate the changes and respin it soon.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-11  9:54 ` Pablo Neira Ayuso
@ 2009-03-11 10:00   ` Evgeniy Polyakov
  2009-03-16 14:42     ` Patrick McHardy
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-11 10:00 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Hi Pablo.

On Wed, Mar 11, 2009 at 10:54:21AM +0100, Pablo Neira Ayuso (pablo@netfilter.org) wrote:
> > Fingerprint matching rules can be downloaded from OpenBSD source tree
> > and loaded via netlink connector into the kernel via special util found
> > in archive. It will also listen for events about matching packets.
> 
> I like this feature. We have nfnetlink so I don't see why we should use
> the netlink connector instead.

OSF exists about 6 years already, netlink configuration was added in
2005, I do not remember if nfnetlink existed those days (IIRC it did
not, since I reused ULOG netlink first), right now I just cleanup
what was written before.

> BTW, is there any difference with regards to userspace p0f apart from
> having this integrated into iptables?

There should be no major differences, there are some tweaks for the
MTU comparison, maybe something else.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 15:13 Evgeniy Polyakov
                   ` (2 preceding siblings ...)
  2009-03-10 21:01 ` Jesper Dangaard Brouer
@ 2009-03-11  9:54 ` Pablo Neira Ayuso
  2009-03-11 10:00   ` Evgeniy Polyakov
  3 siblings, 1 reply; 54+ messages in thread
From: Pablo Neira Ayuso @ 2009-03-11  9:54 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.
> 
> Fingerprint matching rules can be downloaded from OpenBSD source tree
> and loaded via netlink connector into the kernel via special util found
> in archive. It will also listen for events about matching packets.

I like this feature. We have nfnetlink so I don't see why we should use
the netlink connector instead.

BTW, is there any difference with regards to userspace p0f apart from
having this integrated into iptables?

-- 
"Los honestos son inadaptados sociales" -- Les Luthiers

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 21:01 ` Jesper Dangaard Brouer
@ 2009-03-10 21:54   ` Evgeniy Polyakov
  2009-03-16 14:40   ` Patrick McHardy
  1 sibling, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-10 21:54 UTC (permalink / raw)
  To: Jesper Dangaard Brouer
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt, hawk

Hi.

On Tue, Mar 10, 2009 at 10:01:30PM +0100, Jesper Dangaard Brouer (hawk@diku.dk) wrote:
> >+static void __exit ipt_osf_fini(void)
> >+{
> >+	struct ipt_osf_finger *f;
> >+	int i;
> >+
> >+	cn_del_callback(&cn_osf_id);
> >+	xt_unregister_match(&ipt_osf_match);
> >+
> >+	rcu_read_lock();
> >+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> >+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> >+
> >+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> 
> spin_lock(&st->finger_lock); //???
> >+			list_del_rcu(&f->finger_entry);
> spin_unlock(&st->finger_lock);
> 
> >+			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
> >+		}
> >+	}
> >+	rcu_read_unlock();
> 
> Should the list_del_rcu() not be protected by a spinlock?

Not required at this place - all users are already unregistered and
no code can access this list except module exit path.

> >+	rcu_barrier();
> 
> In some of my code I call synchronize_net(), is it enough to call 
> rcu_barrier()?

It is enough here, rcu_barrier() will wait until all scheduled
call_rcu() are completed, that's what we need. But in some cases we
should only wait for the whole grace period to elapse, then one has to use
synchronize_rcu() and friends. rcu_barrier() will wait for the callbacks
to be executed, while they are executed after grace period has elapsed,
so it implicitly includes synchronize_rcu(), but effectively they are
the same: both functions register rcu callback and wait for the
completion, rcu_barrier() is a bit more enhanced, since it has several
types.

> What is the difference between:
> 
>  synchronize_rcu()
>  synchronize_net()

Those are essentially the same - synchronize_net() has additional
might_sleep()  call. Both will wait until grace period elapced - i.e.
all currently RCU protected sections completed.

>  rcu_barrier()

It will wait until all scheduled rcu callbacks are executed.

So from the description they look different, but implementation
suggestes that effectively they are the same, except that there are a
bit different invocation types for the barrier.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 15:13 Evgeniy Polyakov
  2009-03-10 16:01 ` Evgeniy Polyakov
  2009-03-10 16:07 ` Jan Engelhardt
@ 2009-03-10 21:01 ` Jesper Dangaard Brouer
  2009-03-10 21:54   ` Evgeniy Polyakov
  2009-03-16 14:40   ` Patrick McHardy
  2009-03-11  9:54 ` Pablo Neira Ayuso
  3 siblings, 2 replies; 54+ messages in thread
From: Jesper Dangaard Brouer @ 2009-03-10 21:01 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt, hawk

On Tue, 10 Mar 2009, Evgeniy Polyakov wrote:

> +struct ipt_osf_finger {
> +	struct rcu_head			rcu_head;
> +	struct list_head		finger_entry;
> +	struct ipt_osf_user_finger	finger;
> +};

> +struct ipt_osf_finger_storage
> +{
> +	struct list_head		finger_list;
> +	spinlock_t			finger_lock;
> +};
> +
> +/*
> + * Indexed by dont-fragment bit.
> + * It is the only constant value in the fingerprint.
> + */
> +struct ipt_osf_finger_storage ipt_osf_fingers[2];
> +
> +static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);

> +static void __exit ipt_osf_fini(void)
> +{
> +	struct ipt_osf_finger *f;
> +	int i;
> +
> +	cn_del_callback(&cn_osf_id);
> +	xt_unregister_match(&ipt_osf_match);
> +
> +	rcu_read_lock();
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {

spin_lock(&st->finger_lock); //???
> +			list_del_rcu(&f->finger_entry);
spin_unlock(&st->finger_lock);

> +			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
> +		}
> +	}
> +	rcu_read_unlock();

Should the list_del_rcu() not be protected by a spinlock?


> +	rcu_barrier();

In some of my code I call synchronize_net(), is it enough to call 
rcu_barrier()?

What is the difference between:

  synchronize_rcu()
  synchronize_net()
  rcu_barrier()

Hilsen
   Jesper Brouer

--
-------------------------------------------------------------------
MSc. Master of Computer Science
Dept. of Computer Science, University of Copenhagen
Author of http://www.adsl-optimizer.dk
-------------------------------------------------------------------

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 15:13 Evgeniy Polyakov
  2009-03-10 16:01 ` Evgeniy Polyakov
@ 2009-03-10 16:07 ` Jan Engelhardt
  2009-03-11 21:43   ` Evgeniy Polyakov
  2009-03-10 21:01 ` Jesper Dangaard Brouer
  2009-03-11  9:54 ` Pablo Neira Ayuso
  3 siblings, 1 reply; 54+ messages in thread
From: Jan Engelhardt @ 2009-03-10 16:07 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Tuesday 2009-03-10 16:13, Evgeniy Polyakov wrote:
>
>[xt_osf]

This starts to get its shape. I'm in favor of it.


>--- /dev/null
>+++ b/include/linux/netfilter/xt_osf.h
>@@ -0,0 +1,110 @@
>+/*
>+ * xt_osf.h
>+ *

Common-sense policy wants that filenames like these not be part of the file.

>+#ifndef _IPT_OSF_H
>+#define _IPT_OSF_H
>+
>+#define MAXGENRELEN		32
>+#define MAXDETLEN		64
>+
>+#define IPT_OSF_GENRE		(1<<0)
>+#define	IPT_OSF_TTL		(1<<1)
>+#define IPT_OSF_LOG		(1<<2)
>+#define IPT_OSF_UNUSED		(1<<3)
>+#define IPT_OSF_CONNECTOR	(1<<4)
>+#define IPT_OSF_INVERT		(1<<5)
>+
>+#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 */
>+
>+struct ipt_osf_info {
>+	char			genre[MAXGENRELEN];
>+	__u32			len;
>+	__u32			flags;
>+	__u32			loglevel;
>+	__u32			ttl;
>+};
>[more struct ipt_osf...]

Since the module's name is xt_osf now, it would make sense to follow
this in the struct names too.

>diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
>index c2bac9c..34aafec 100644
>--- a/net/netfilter/Kconfig
>+++ b/net/netfilter/Kconfig
>@@ -853,6 +853,19 @@ config NETFILTER_XT_MATCH_U32
> 
> 	  Details and examples are in the kernel module source.
> 
>+config NETFILTER_XT_MATCH_OSF
>+	tristate 'Passive OS fingerprint match support'

Just a minor thing, could you add "osf" somewhere in the short-text
so that people instantly know the name of the module (without having
to consult the help text), like the other entries. For example

	tristate '"osf" Passive OS fingerprint match'

>+	depends on NETFILTER_ADVANCED
>+	help
>+	  Passive OS fingerprint matching module. Allows to passively match
>+	  remote operation system analyzing incoming TCP packets with SYN
>+	  bit set.

A bit of rewording I suggest.

	This option selects the Passive OS Fingerprinting match module
	that allows to passively match the
	remote operating system by analyzing incoming TCP SYN packets.

>+
>+	  You should download and install rule loading software from
>+	  http://www.ioremap.net/projects/osf

should -> can

	Rules and loading software can be downloaded from
	http://ioremap.net/projects/osf/

>diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
>index da3d909..ec3a6e0 100644
>--- a/net/netfilter/Makefile
>+++ b/net/netfilter/Makefile
>@@ -89,6 +89,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
> obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
> obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o
> obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
>+obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o

Please have the list sorted alphabetically. (This keeps merge conflicts
down because everybody does not try to append at the end.)

>diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
>new file mode 100644
>index 0000000..f8b5a16
>--- /dev/null
>+++ b/net/netfilter/xt_osf.c
>+
>+static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
>+				   const struct sk_buff *skb)
>+{
>+	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
>+	struct ipt_osf_nlmsg *data = &msg->nlmsg;
>+	struct iphdr *iph = ip_hdr(skb);
>+	struct tcphdr *tcph = tcp_hdr(skb);

iph and tcph are only used in memcpy and could be directly substituted:

	memcpy(&data->ip, ip_hdr(skb), ...)
	memcpy(&data->tcp, tcp_hdr(skb), ...)

>+	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
>+	msg->cmsg.seq = osf_seq++;
>+	msg->cmsg.ack = 0;
>+	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
>+
>+	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
>+	memcpy(&data->ip, iph, sizeof(struct iphdr));

xt_osf does not look at IP options, is that so?

>+	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));

xt_osf does not look at TCP options, is not it?

Note that you cannot directly use tcp_hdr(skb) as the
skb->transport_header has not yet been initialized (it still points
at skb->network_header) because the packet has not yet been seen by
the next handler. This affects PREROUTING, and INPUT chains (and
BROUTING, for ebtables people, but irrelevant here).

The packet may also be fragmented or non-linear.

You actually do the right thing in ipt_osf_match_packet.

>+static inline int ipt_osf_ttl(const struct sk_buff *skb, struct ipt_osf_info *info,

const struct ipt_osf_info *

>+			    unsigned char f_ttl)
>+{
>+	struct iphdr *ip = ip_hdr(skb);
const struct iphdr *iph

>+	if (info->flags & IPT_OSF_TTL) {
>+		if (info->ttl == IPT_OSF_TTL_TRUE)
>+			return (ip->ttl == f_ttl);

redundant pair of ()

>+		if (info->ttl == IPT_OSF_TTL_NOCHECK)
>+			return 1;
>+		else if (ip->ttl <= f_ttl)
>+			return 1;
>+		else {
>+			struct in_device *in_dev = in_dev_get(skb->dev);
>+			int ret = 0;
>+
>+			for_ifa(in_dev) {
>+				if (inet_ifa_match(ip->saddr, ifa)) {
>+					ret = (ip->ttl == f_ttl);
>+					break;
>+				}
>+			}
>+			endfor_ifa(in_dev);
>+
>+			in_dev_put(in_dev);
>+			return ret;
>+		}
>+	}
>+	
>+	return (ip->ttl == f_ttl);

redundant pair of ()

>+}
>+
>+static bool ipt_osf_match_packet(const struct sk_buff *skb,
>+		const struct xt_match_param *p)
>+{
>+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;

no cast needed. const advised.

>+	struct iphdr *ip;
>+	struct tcphdr _tcph, *tcp;
const

>+	int fmatch = FMATCH_WRONG, fcount = 0;
>+	unsigned int optsize = 0, check_WSS = 0;
>+	u16 window, totlen, mss = 0;
>+	bool df;
>+	unsigned char *optp = NULL, *_optp = NULL;
const

>+	unsigned char opts[MAX_IPOPTLEN];

>+	struct ipt_osf_finger *kf;
>+	struct ipt_osf_user_finger *f;
>+	struct ipt_osf_finger_storage *st;
three const, as far as possible

>+
>+	if (!info)
>+		return false;
>+
>+	ip = ip_hdr(skb);
>+	if (!ip)
>+		return false;

ip_hdr is always returning non-NULL anyway.

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

>[...]
>+	}
>+
>+	if (fcount)
>+		fmatch = FMATCH_OK;
>+
>+	return fmatch == FMATCH_OK;
>+}
>+
>+static struct xt_match ipt_osf_match = {
>+	.name 		= "osf",
>+	.revision	= 0,
>+	.family		= NFPROTO_IPV4,
>+	.proto		= IPPROTO_TCP,
>+	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING),

Would allow FORWARD work?

>+	.match 		= ipt_osf_match_packet,
>+	.matchsize	= sizeof(struct ipt_osf_info),
>+	.me		= THIS_MODULE,
>+};
>+


-Jan

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

* Re: Passive OS fingerprint xtables match.
  2009-03-10 15:13 Evgeniy Polyakov
@ 2009-03-10 16:01 ` Evgeniy Polyakov
  2009-03-10 16:07 ` Jan Engelhardt
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-10 16:01 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

On Tue, Mar 10, 2009 at 06:13:57PM +0300, Evgeniy Polyakov (zbr@ioremap.net) wrote:
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.
> 
> Fingerprint matching rules can be downloaded from OpenBSD source tree
> and loaded via netlink connector into the kernel via special util found
> in archive. It will also listen for events about matching packets.
> 
> Archive also contains library file (also attached), which was shipped
> with iptables extensions some time ago (at least when ipt_osf existed
> in patch-o-matic).
> 
> This release implements suggestions found during the code review like
> codying style, structure split and tighter packing, bool and %pi4
> usage and similar changes.

Not the latest version, it misses the following fix from the parallel
tree.

    Fixed TCP header copy to the userspace when given option is enabled.

diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 3114bbd..e619f09 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -83,7 +83,7 @@ static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
 	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
 	struct ipt_osf_nlmsg *data = &msg->nlmsg;
 	struct iphdr *iph = ip_hdr(skb);
-	struct tcphdr *tcph = tcp_hdr(skb);
+	struct tcphdr *tcp;
 
 	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
 	msg->cmsg.seq = osf_seq++;
@@ -92,7 +92,9 @@ static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
 
 	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
 	memcpy(&data->ip, iph, sizeof(struct iphdr));
-	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &data->tcp);
+	if (tcp)
+		memcpy(&data->tcp, tcp, sizeof(struct tcphdr));
 
 	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
 }


-- 
	Evgeniy Polyakov

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

* Passive OS fingerprint xtables match.
@ 2009-03-10 15:13 Evgeniy Polyakov
  2009-03-10 16:01 ` Evgeniy Polyakov
                   ` (3 more replies)
  0 siblings, 4 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-03-10 15:13 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist, Jan Engelhardt

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
and loaded via netlink connector into the kernel via special util found
in archive. It will also listen for events about matching packets.

Archive also contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

This release implements suggestions found during the code review like
codying style, structure split and tighter packing, bool and %pi4
usage and similar changes.

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os

Example usage:
# modrpobe xt_osf
# ./ucon_osf -f ./pf.os
^C Daemon will listen for incoming match events 
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector

You will find something like this in the syslog:
ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139

Please consider for inclusion.
Thank you.

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

diff --git a/include/linux/connector.h b/include/linux/connector.h
index 34f2789..da6595e 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -39,6 +39,8 @@
 #define CN_IDX_V86D			0x4
 #define CN_VAL_V86D_UVESAFB		0x1
 #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
+#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
+#define CN_VAL_OSF			0x0
 
 #define CN_NETLINK_USERS		6
 
diff --git a/include/linux/netfilter/xt_osf.h b/include/linux/netfilter/xt_osf.h
new file mode 100644
index 0000000..a7696be
--- /dev/null
+++ b/include/linux/netfilter/xt_osf.h
@@ -0,0 +1,110 @@
+/*
+ * xt_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<<0)
+#define	IPT_OSF_TTL		(1<<1)
+#define IPT_OSF_LOG		(1<<2)
+#define IPT_OSF_UNUSED		(1<<3)
+#define IPT_OSF_CONNECTOR	(1<<4)
+#define IPT_OSF_INVERT		(1<<5)
+
+#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 */
+
+struct ipt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ * It is used to implement a state machine for the different wildcard values
+ * of the MSS and window sizes.
+ */
+struct ipt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct ipt_osf_opt {
+	__u16			kind, length;
+	struct ipt_osf_wc	wc;
+};
+
+struct ipt_osf_user_finger {
+	struct ipt_osf_wc	wss;
+
+	__u8			ttl, df;
+	__u16			ss, mss;
+	__u16			opt_num;
+
+	char			genre[MAXGENRELEN];
+	char			version[MAXGENRELEN];
+	char			subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct ipt_osf_opt	opt[MAX_IPOPTLEN];
+};
+
+struct ipt_osf_nlmsg {
+	struct ipt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* Defines for IANA option kinds */
+
+enum iana_options {
+	OSFOPT_EOL = 0,		/* End of options */
+	OSFOPT_NOP, 		/* NOP */
+	OSFOPT_MSS, 		/* Maximum segment size */
+	OSFOPT_WSO, 		/* Window scale option */
+	OSFOPT_SACKP,		/* SACK permitted */
+	OSFOPT_SACK,		/* SACK */
+	OSFOPT_ECHO,
+	OSFOPT_ECHOREPLY,
+	OSFOPT_TS,		/* Timestamp option */
+	OSFOPT_POCP,		/* Partial Order Connection Permitted */
+	OSFOPT_POSP,		/* Partial Order Service Profile */
+
+	/* Others are not used in the current OSF */
+	OSFOPT_EMPTY = 255,
+};
+
+#endif				/* _IPT_OSF_H */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index c2bac9c..34aafec 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -853,6 +853,19 @@ config NETFILTER_XT_MATCH_U32
 
 	  Details and examples are in the kernel module source.
 
+config NETFILTER_XT_MATCH_OSF
+	tristate 'Passive OS fingerprint match support'
+	depends on NETFILTER_ADVANCED
+	help
+	  Passive OS fingerprint matching module. Allows to passively match
+	  remote operation system analyzing incoming TCP packets with SYN
+	  bit set.
+
+	  You should download and install rule loading software from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 endif # NETFILTER_XTABLES
 
 endmenu
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index da3d909..ec3a6e0 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
 
 # IPVS
 obj-$(CONFIG_IP_VS) += ipvs/
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
new file mode 100644
index 0000000..f8b5a16
--- /dev/null
+++ b/net/netfilter/xt_osf.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/connector.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/rculist.h>
+#include <linux/smp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_osf.h>
+
+struct ipt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct ipt_osf_user_finger	finger;
+};
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct ipt_osf_finger_storage
+{
+	struct list_head		finger_list;
+	spinlock_t			finger_lock;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+struct ipt_osf_finger_storage ipt_osf_fingers[2];
+
+struct ipt_osf_message {
+	struct cn_msg		cmsg;
+	struct ipt_osf_nlmsg	nlmsg;
+};
+
+static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
+
+static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
+static u32 osf_seq;
+
+static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
+				   const struct sk_buff *skb)
+{
+	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
+	struct ipt_osf_nlmsg *data = &msg->nlmsg;
+	struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *tcph = tcp_hdr(skb);
+
+	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
+	msg->cmsg.seq = osf_seq++;
+	msg->cmsg.ack = 0;
+	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
+
+	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
+	memcpy(&data->ip, iph, sizeof(struct iphdr));
+	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
+
+	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
+}
+
+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 (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 if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+	
+	return (ip->ttl == f_ttl);
+}
+
+static bool ipt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
+	struct iphdr *ip;
+	struct tcphdr _tcph, *tcp;
+	int fmatch = FMATCH_WRONG, fcount = 0;
+	unsigned int optsize = 0, check_WSS = 0;
+	u16 window, totlen, mss = 0;
+	bool df;
+	unsigned char *optp = NULL, *_optp = NULL;
+	unsigned char opts[MAX_IPOPTLEN];
+	struct ipt_osf_finger *kf;
+	struct ipt_osf_user_finger *f;
+	struct ipt_osf_finger_storage *st;
+
+	if (!info)
+		return false;
+
+	ip = ip_hdr(skb);
+	if (!ip)
+		return false;
+
+	tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
+	if (!tcp)
+		return false;
+
+	if (!tcp->syn)
+		return false;
+
+	totlen = ntohs(ip->tot_len);
+	df = ntohs(ip->frag_off) & IP_DF;
+	window = ntohs(tcp->window);
+
+	if (tcp->doff * 4 > sizeof(struct tcphdr)) {
+		optsize = tcp->doff * 4 - sizeof(struct tcphdr);
+
+		if (optsize > sizeof(opts))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) + sizeof(struct tcphdr), 
+				optsize, opts);
+	}
+
+	st = &ipt_osf_fingers[df];
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
+			continue;
+
+		optp = _optp;
+		fmatch = FMATCH_WRONG;
+
+		if (totlen == f->ss && 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:
+				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 (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 = optp[3];
+						mss <<= 8;
+						mss |= optp[2];
+
+						mss = ntohs(mss);
+						break;
+					case OSFOPT_TS:
+						loop_cont = 1;
+						break;
+					}
+
+					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)
+				continue;
+
+			fcount++;
+			if (info->flags & IPT_OSF_LOG)
+				printk(KERN_INFO "%s [%s:%s] : "
+					"%pi4:%d -> %pi4:%d hops=%d\n",
+					f->genre, f->version, f->subtype, 
+					&ip->saddr, ntohs(tcp->source), 
+					&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_CONNECTOR))) {
+		unsigned int i;
+		struct ipt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+#if 1
+		if (info->flags & IPT_OSF_LOG) {
+			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
+				printk(KERN_INFO "Unknown: win: %u, mss: %u, "
+					"totlen: %u, df: %d, ttl: %u : ",
+					window, mss, totlen, df, ip->ttl);
+			else
+				printk(KERN_INFO "");
+			if (_optp) {
+				optp = _optp;
+				for (i = 0; i < optsize; i++)
+					printk("%02X ", optp[i]);
+			}
+
+			printk("%pi4:%u -> %pi4:%u\n",
+			     &ip->saddr, ntohs(tcp->source),
+			     &ip->daddr, ntohs(tcp->dest));
+		}
+#endif
+		if (info->flags & 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);
+
+			ipt_osf_send_connector(&fg, skb);
+		}
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return fmatch == FMATCH_OK;
+}
+
+static struct xt_match ipt_osf_match = {
+	.name 		= "osf",
+	.revision	= 0,
+	.family		= NFPROTO_IPV4,
+	.proto		= IPPROTO_TCP,
+	.hooks      	= (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING),
+	.match 		= ipt_osf_match_packet,
+	.matchsize	= sizeof(struct ipt_osf_info),
+	.me		= THIS_MODULE,
+};
+
+static void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static void osf_cn_callback(void *data)
+{
+	struct cn_msg *msg = data;
+	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
+	struct ipt_osf_finger *kf = NULL, *sf;
+	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
+
+	/*
+	 * If msg->ack is set to 0 then we add attached fingerprint,
+	 * otherwise remove, and in this case we do not need to allocate data.
+	 */
+	if (!msg->ack) {
+		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
+		if (!kf)
+			return;
+
+		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
+			continue;
+
+		if (msg->ack) {
+			spin_lock_bh(&st->finger_lock);
+			list_del_rcu(&sf->finger_entry);
+			spin_unlock_bh(&st->finger_lock);
+			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
+		} else {
+			kfree(kf);
+			kf = NULL;
+		}
+
+		break;
+	}
+
+	if (kf) {
+		spin_lock_bh(&st->finger_lock);
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+		spin_unlock_bh(&st->finger_lock);
+#if 0
+		printk(KERN_INFO "Added rule for %s:%s:%s.\n",
+			kf->finger.genre, kf->finger.version, kf->finger.subtype);
+#endif
+	}
+	rcu_read_unlock();
+}
+
+static int __init ipt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+		spin_lock_init(&st->finger_lock);
+	}
+
+	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&ipt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	cn_del_callback(&cn_osf_id);
+err_out_exit:
+	return err;
+}
+
+static void __exit ipt_osf_fini(void)
+{
+	struct ipt_osf_finger *f;
+	int i;
+
+	cn_del_callback(&cn_osf_id);
+	xt_unregister_match(&ipt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(ipt_osf_init);
+module_exit(ipt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");

-- 
	Evgeniy Polyakov

[-- Attachment #2: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4984 bytes --]

/*
 * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <linux/types.h>

#include <xtables.h>

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

#include <linux/netfilter/xt_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.\n\n"
		);
}


static const struct option osf_opts[] = {
	{ .name = "genre",	.has_arg = true, .val = '1' },
	{ .name = "ttl",	.has_arg = true, .val = '2' },
	{ .name = "log",	.has_arg = true, .val = '3' },
	{ .name = "connector",	.has_arg = false, .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(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 '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);
}

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

* Re: Passive OS fingerprint xtables match.
  2009-02-18 15:30             ` Jan Engelhardt
@ 2009-02-19 11:56               ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-19 11:56 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

On Wed, Feb 18, 2009 at 04:30:03PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >As of IPv6 support - it could be fairly trivial, the only thing we need
> >is to dereference TCP header with the appropriate offset anf get don't
> >fragment bit (ipv6 does not have it, so we could check both entries).
> 
> But IPv6 can have Fragment Headers, serving about the same purpose.
> 
> >But... I do not have IPv6 network to test the changes (and definitely
> >did not have it 6 years ago), so it was never implemented :)
> 
> There exist lots of free VM solutions - and I think UML existed
> 6 years ago, too.

It is not that simple. IPv6 was never a out-of-the-box solution, and in
case of this module we either need to setup a router, or use ipv6-aware
application (fortunately telnet knows that in the recent distros).

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-18 15:07           ` Evgeniy Polyakov
@ 2009-02-18 15:30             ` Jan Engelhardt
  2009-02-19 11:56               ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Jan Engelhardt @ 2009-02-18 15:30 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Wednesday 2009-02-18 16:07, Evgeniy Polyakov wrote:
>
>As of IPv6 support - it could be fairly trivial, the only thing we need
>is to dereference TCP header with the appropriate offset anf get don't
>fragment bit (ipv6 does not have it, so we could check both entries).

But IPv6 can have Fragment Headers, serving about the same purpose.

>But... I do not have IPv6 network to test the changes (and definitely
>did not have it 6 years ago), so it was never implemented :)

There exist lots of free VM solutions - and I think UML existed
6 years ago, too.

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

* Re: Passive OS fingerprint xtables match.
  2009-02-18 15:00       ` Patrick McHardy
@ 2009-02-18 15:28         ` Jan Engelhardt
  0 siblings, 0 replies; 54+ messages in thread
From: Jan Engelhardt @ 2009-02-18 15:28 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Evgeniy Polyakov, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Wednesday 2009-02-18 16:00, Patrick McHardy wrote:
> Jan Engelhardt wrote:
>> On Thursday 2009-02-12 19:57, Evgeniy Polyakov wrote:
>>>> Are you sure it is safe to use arch-dependent types like 'int'?
>>> I would be very surprised if Linux will ever run on weird arch where int
>>> is not 32 bits.
>>
>> If GCC had a switch to compile with I16 or I64, I could test,
>> but it does not. 'long', as used in the netfilter includes,
>> already bit people in pre-2.6.19, and nowadays we have that compat
>> crap thing in place.. Just don't take any chance, and go the safe
>> route with uint32_t.
>
> Tons of APIs will break when the size of int changes and differs
> between userspace and kernel, this is a non-realistic risk in my
> opinion.

It is very realistic. Compilers for Microsoft DOS/Windows used to
map 'int' to a 16-bit type, and they changed that when 32-bit
became modern. Don't you think that too broke lots of stuff?

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:12 Evgeniy Polyakov
  2009-02-12 17:42 ` Paul E. McKenney
  2009-02-12 18:22 ` Jan Engelhardt
@ 2009-02-18 15:14 ` Patrick McHardy
  2 siblings, 0 replies; 54+ messages in thread
From: Patrick McHardy @ 2009-02-18 15:14 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Evgeniy Polyakov wrote:
> +config IP_NF_MATCH_OSF
> +	tristate '"osf" match support'
> +	depends on NETFILTER_ADVANCED && CONNECTOR

I can't see any further code issues than those already pointed
out, however I would really like to see the use of connector go.
We do already have nfnetlink specifically for netfilter that can
do the same thing, I see no reason to use connector instead.


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

* Re: Passive OS fingerprint xtables match.
  2009-02-18 15:02         ` Patrick McHardy
@ 2009-02-18 15:07           ` Evgeniy Polyakov
  2009-02-18 15:30             ` Jan Engelhardt
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-18 15:07 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: Jan Engelhardt, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

On Wed, Feb 18, 2009 at 04:02:32PM +0100, Patrick McHardy (kaber@trash.net) wrote:
> Evgeniy Polyakov wrote:
> >But I placed OSF into net/ipv4/netfilter/ipt_osf.c, should it be moved
> >into different location?
> 
> Not necessarily as long as it only supports IPv4, but it would be
> good to have the API flexible enough from the start to also support
> IPv6. But I don't see a problem with this in the current patchset.

I've already moved it in the my testing tree as long as incorporate all
other changes, I will run it for some time and resubmit tomorrow or so
:)

As of IPv6 support - it could be fairly trivial, the only thing we need
is to dereference TCP header with the appropriate offset anf get don't
fragment bit (ipv6 does not have it, so we could check both entries).

But... I do not have IPv6 network to test the changes (and definitely
did not have it 6 years ago), so it was never implemented :)

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-13 13:03       ` Evgeniy Polyakov
  2009-02-13 13:51         ` Jan Engelhardt
@ 2009-02-18 15:02         ` Patrick McHardy
  2009-02-18 15:07           ` Evgeniy Polyakov
  1 sibling, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-02-18 15:02 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Jan Engelhardt, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Evgeniy Polyakov wrote:
> But I placed OSF into net/ipv4/netfilter/ipt_osf.c, should it be moved
> into different location?

Not necessarily as long as it only supports IPv4, but it would be
good to have the API flexible enough from the start to also support
IPv6. But I don't see a problem with this in the current patchset.



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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 20:12     ` Jan Engelhardt
  2009-02-13 13:03       ` Evgeniy Polyakov
@ 2009-02-18 15:00       ` Patrick McHardy
  2009-02-18 15:28         ` Jan Engelhardt
  1 sibling, 1 reply; 54+ messages in thread
From: Patrick McHardy @ 2009-02-18 15:00 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Evgeniy Polyakov, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Jan Engelhardt wrote:
> On Thursday 2009-02-12 19:57, Evgeniy Polyakov wrote:
>>> Are you sure it is safe to use arch-dependent types like 'int'?
>> I would be very surprised if Linux will ever run on weird arch where int
>> is not 32 bits.
> 
> If GCC had a switch to compile with I16 or I64, I could test,
> but it does not. 'long', as used in the netfilter includes,
> already bit people in pre-2.6.19, and nowadays we have that compat
> crap thing in place.. Just don't take any chance, and go the safe
> route with uint32_t.

Tons of APIs will break when the size of int changes and differs
between userspace and kernel, this is a non-realistic risk in my
opinion. I do however prefer the fixed types myself since they
make the size more visible.



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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:42 ` Paul E. McKenney
  2009-02-12 17:51   ` Evgeniy Polyakov
@ 2009-02-18 14:55   ` Patrick McHardy
  1 sibling, 0 replies; 54+ messages in thread
From: Patrick McHardy @ 2009-02-18 14:55 UTC (permalink / raw)
  To: paulmck
  Cc: Evgeniy Polyakov, netdev, David Miller,
	Netfilter Development Mailinglist

Paul E. McKenney wrote:
> On Thu, Feb 12, 2009 at 08:12:45PM +0300, Evgeniy Polyakov wrote:
>> 
>> Resending after Patrick's request :)
>>
>> Passive OS fingerprint homepage (archives, examples):
>> http://www.ioremap.net/projects/osf
> 
> I advocate using this to get more accurate censuses of machines
> accessing given web servers, given the tendency of browsers to lie
> about themselves in order to avoid being shut out of certain web sites.

Fair enough :) I'll look over the patches and will apply them if
everything is fine.

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

* Re: Passive OS fingerprint xtables match.
  2009-02-13 14:41             ` Jan Engelhardt
@ 2009-02-15 17:32               ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-15 17:32 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

On Fri, Feb 13, 2009 at 03:41:36PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> But still it has to jump over anything it does not check.

Yes, that may be unaligned. I think instead of get_unaligned() it will
be faster to access it as two chars instead.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-13 14:22           ` Evgeniy Polyakov
@ 2009-02-13 14:41             ` Jan Engelhardt
  2009-02-15 17:32               ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Jan Engelhardt @ 2009-02-13 14:41 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Friday 2009-02-13 15:22, Evgeniy Polyakov wrote:
>> >
>> >It cant, header is fixed and every option length is also fixed (and its
>> >size is checked).
>> 
>> This RFC-compliant ("An option may begin on any octet boundary.")
>> option byte stream seems to produce an odd optp value:
>> 
>> 01 02 04 05 a0 00
>> 
>> or where did I go wrong?
>
>OSF checks only MSS and timestamp options.

But still it has to jump over anything it does not check.

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

* Re: Passive OS fingerprint xtables match.
  2009-02-13 13:51         ` Jan Engelhardt
@ 2009-02-13 14:22           ` Evgeniy Polyakov
  2009-02-13 14:41             ` Jan Engelhardt
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-13 14:22 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

On Fri, Feb 13, 2009 at 02:51:48PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >> config NETFILTER_XT_MATCH_COMMENT
> >>         tristate  '"comment" match support'
> >>         depends on NETFILTER_ADVANCED
> >>         ---help---
> >
> >But I placed OSF into net/ipv4/netfilter/ipt_osf.c, should it be moved
> >into different location?
> 
> That would be preferable -> net/netfilter/xt_osf.c.

Ok, I will move it there.

> >> >proper offset (modulo of the option size).
> >> 
> >> What if optp is odd?
> >
> >It cant, header is fixed and every option length is also fixed (and its
> >size is checked).
> 
> This RFC-compliant ("An option may begin on any octet boundary.")
> option byte stream seems to produce an odd optp value:
> 
> 01 02 04 05 a0 00
> 
> or where did I go wrong?

OSF checks only MSS and timestamp options.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-13 13:03       ` Evgeniy Polyakov
@ 2009-02-13 13:51         ` Jan Engelhardt
  2009-02-13 14:22           ` Evgeniy Polyakov
  2009-02-18 15:02         ` Patrick McHardy
  1 sibling, 1 reply; 54+ messages in thread
From: Jan Engelhardt @ 2009-02-13 13:51 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Friday 2009-02-13 14:03, Evgeniy Polyakov wrote:
>> >What's this? It does not exist in the net/ipv4/netfilter/Kconfig
>> 
>> net/netfilter/Kconfig: (e.g.)
>> 
>> config NETFILTER_XT_MATCH_COMMENT
>>         tristate  '"comment" match support'
>>         depends on NETFILTER_ADVANCED
>>         ---help---
>
>But I placed OSF into net/ipv4/netfilter/ipt_osf.c, should it be moved
>into different location?

That would be preferable -> net/netfilter/xt_osf.c.

>> >> >+					case OSFOPT_MSS:
>> >> >+						mss = ntohs(*(u16 *)(optp + 2));
>> >> 
>> >> This needs get_unaligned(), in case someone sends a malicious packet.
>> >
>> >Hmmm, why is this needed? We dereference linear kernel pointer at
>> >proper offset (modulo of the option size).
>> 
>> What if optp is odd?
>
>It cant, header is fixed and every option length is also fixed (and its
>size is checked).

This RFC-compliant ("An option may begin on any octet boundary.")
option byte stream seems to produce an odd optp value:

01 02 04 05 a0 00

or where did I go wrong?

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 20:12     ` Jan Engelhardt
@ 2009-02-13 13:03       ` Evgeniy Polyakov
  2009-02-13 13:51         ` Jan Engelhardt
  2009-02-18 15:02         ` Patrick McHardy
  2009-02-18 15:00       ` Patrick McHardy
  1 sibling, 2 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-13 13:03 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

On Thu, Feb 12, 2009 at 09:12:05PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >> >+config IP_NF_MATCH_OSF
> >> >+	tristate '"osf" match support'
> >> >+	depends on NETFILTER_ADVANCED && CONNECTOR
> >> >+	help
> >> >+	  Passive OS fingerprint matching module.
> >> >+	  You should download and install rule loading software from
> >> >+	  http://www.ioremap.net/projects/osf
> >> >+
> >> >+	  To compile it as a module, choose M here.  If unsure, say N.
> >> >+
> >> 
> >> Please do use NETFILTER_XT_ and its section.
> >
> >What's this? It does not exist in the net/ipv4/netfilter/Kconfig
> 
> net/netfilter/Kconfig: (e.g.)
> 
> config NETFILTER_XT_MATCH_COMMENT
>         tristate  '"comment" match support'
>         depends on NETFILTER_ADVANCED
>         ---help---

But I placed OSF into net/ipv4/netfilter/ipt_osf.c, should it be moved
into different location?

> >> >+			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));
> >> 
> >> This needs get_unaligned(), in case someone sends a malicious packet.
> >
> >Hmmm, why is this needed? We dereference linear kernel pointer at
> >proper offset (modulo of the option size).
> 
> What if optp is odd?

It cant, header is fixed and every option length is also fixed (and its
size is checked).

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:51   ` Evgeniy Polyakov
@ 2009-02-12 20:41     ` Paul E. McKenney
  0 siblings, 0 replies; 54+ messages in thread
From: Paul E. McKenney @ 2009-02-12 20:41 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Netfilter Development Mailinglist

On Thu, Feb 12, 2009 at 08:51:30PM +0300, Evgeniy Polyakov wrote:
> Hi.
> 
> On Thu, Feb 12, 2009 at 09:42:53AM -0800, Paul E. McKenney (paulmck@linux.vnet.ibm.com) wrote:
> > > Passive OS fingerprint homepage (archives, examples):
> > > http://www.ioremap.net/projects/osf
> > 
> > I advocate using this to get more accurate censuses of machines
> > accessing given web servers, given the tendency of browsers to lie
> > about themselves in order to avoid being shut out of certain web sites.
> > 
> > One question about the module-unload sequence below.
> > 
> > Given a reasonable answer to that question, I am OK with this from
> > an RCU viewpoint.
> 
> Thanks a lot Paul, but are they actually questions? You answered all of them :)

The last one was.  Your answer was that you prevented any concurrent
access, but I frankly don't understand the netfilter/connector code
well enough to do more than simply believe you.

							Thanx, Paul

> > > +static void __devexit ipt_osf_fini(void)
> > > +{
> > > +	struct ipt_osf_finger *f;
> > > +	int i;
> > > +
> > > +	cn_del_callback(&cn_osf_id);
> > > +	xt_unregister_match(&ipt_osf_match);
> > > +
> > > +	rcu_read_lock();
> > > +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> > > +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> > > +
> > > +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> > > +			list_del_rcu(&f->finger_entry);
> > 
> > For the above to be safe:
> > 
> > o	Any remaining RCU callbacks cannot reference the list
> > 	(and your callbacks do in fact meet this constraint).
> 
> They do not access that list.
> 
> > o	Any timers have to have fired or been cancelled (but you
> > 	don't seem to have any timers, so not a problem).
> 
> No timers, tasklets, work queues or whatever else postponing the work.
> 
> > o	All pathways to the data structure have to have been
> > 	shut down.  This is the tough one -- or is it simply
> > 	a requirement that the guy removing the module has shut
> > 	down all requests?
> 
> They can be accessed via connector configuration, but it is stopped
> above (and it waits for currently active users to run away);
> and netfilter path, which should be prevented after match was also
> unregistered above.
> 
> -- 
> 	Evgeniy Polyakov
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 18:57   ` Evgeniy Polyakov
@ 2009-02-12 20:12     ` Jan Engelhardt
  2009-02-13 13:03       ` Evgeniy Polyakov
  2009-02-18 15:00       ` Patrick McHardy
  0 siblings, 2 replies; 54+ messages in thread
From: Jan Engelhardt @ 2009-02-12 20:12 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Thursday 2009-02-12 19:57, Evgeniy Polyakov wrote:
>>  +/**
>> >+ * Wildcard MSS (kind of).
>>    * @wc:	wild card what(?)
>>    * @val:	some value?
>> >+ */
>> >+struct ipt_osf_wc {
>> >+	__u32			wc;
>> >+	__u32			val;
>> >+};
>> 
>
>This is a weird wildcards implementation for the MSS value.
>They can be pure wildcards and several subtypes which form the window
>size and MSS state machine.

Good to know. Please add this explanation as a comment
in the next iteration.

>> >+	int opt_num;
>> >+
>> >+	char genre[MAXGENRELEN];
>> >+	char version[MAXGENRELEN];
>> >+	char subtype[MAXGENRELEN];
>> >+
>> >+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
>> >+	struct ipt_osf_opt opt[MAX_IPOPTLEN];
>> >+};
>> 
>> Are you sure it is safe to use arch-dependent types like 'int'?
>
>I would be very surprised if Linux will ever run on weird arch where int
>is not 32 bits.

If GCC had a switch to compile with I16 or I64, I could test,
but it does not. 'long', as used in the netfilter includes,
already bit people in pre-2.6.19, and nowadays we have that compat
crap thing in place.. Just don't take any chance, and go the safe
route with uint32_t.

>> >+config IP_NF_MATCH_OSF
>> >+	tristate '"osf" match support'
>> >+	depends on NETFILTER_ADVANCED && CONNECTOR
>> >+	help
>> >+	  Passive OS fingerprint matching module.
>> >+	  You should download and install rule loading software from
>> >+	  http://www.ioremap.net/projects/osf
>> >+
>> >+	  To compile it as a module, choose M here.  If unsure, say N.
>> >+
>> 
>> Please do use NETFILTER_XT_ and its section.
>
>What's this? It does not exist in the net/ipv4/netfilter/Kconfig

net/netfilter/Kconfig: (e.g.)

config NETFILTER_XT_MATCH_COMMENT
        tristate  '"comment" match support'
        depends on NETFILTER_ADVANCED
        ---help---

>> >+static bool ipt_osf_match_packet(const struct sk_buff *skb,
>> >+		const struct xt_match_param *p)
>> >+{
>> >+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
>> >+	struct iphdr *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;
>> 
>> Maybe this should be 'bool df'?
>
>Although it is a bit, it is not really a boolean type.

The underlying implementation is not of importance here.
It behaves like a bool, and that's all what is needed.

>
>> >+			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));
>> 
>> This needs get_unaligned(), in case someone sends a malicious packet.
>
>Hmmm, why is this needed? We dereference linear kernel pointer at
>proper offset (modulo of the option size).

What if optp is odd?

>> Do this function via
>> 	.proto = IPPROTO_TCP,
>> in the osf_match struct instead:
>
>Hmm, it is not used in the existing net/ipv4/netfilter modules.

net/ipv4/netfilter/ is about the most dusted place in Netfilter-land.
Take a grep in net/netfilter/ ;-)

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 18:22 ` Jan Engelhardt
@ 2009-02-12 18:57   ` Evgeniy Polyakov
  2009-02-12 20:12     ` Jan Engelhardt
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-12 18:57 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

Hi Jan.

Thanks a lot for the review.

On Thu, Feb 12, 2009 at 07:22:25PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
> >new file mode 100644
> >index 0000000..c2d2c7a
> >--- /dev/null
> >+++ b/include/linux/netfilter_ipv4/ipt_osf.h
> 
> Much of this I think should be upgraded to the xt_ prefixing
> already, even if it does not do IPv6 yet.

Maybe, I follow the existing naming conventions.

> >+struct ipt_osf_info {
> >+	char			genre[MAXGENRELEN];
> >+	__u32			len;
> >+	__u32			flags;
> >+	__u32			loglevel;
> >+	__u32			ttl;
> >+};
> 
> I do not think you need an entire u32 for loglevel and ttl.

They are aligned to 8 bytes, although this is not strickly needed in
this case, it does not hurt.
 
> Could there at least be some description as to what the
> following fields are?
>  +/**
> >+ * Wildcard MSS (kind of).
>    * @wc:	wild card what(?)
>    * @val:	some value?
> >+ */
> >+struct ipt_osf_wc {
> >+	__u32			wc;
> >+	__u32			val;
> >+};
> 

This is a weird wildcards implementation for the MSS value.
They can be pure wildcards and several subtypes which form the window
size and MSS state machine.

> >+struct ipt_osf_user_finger {
> >+	struct ipt_osf_wc wss;
> >+
> >+	__u8 ttl, df;
> >+	__u16 ss, mss;
> >+	int opt_num;
> >+
> >+	char genre[MAXGENRELEN];
> >+	char version[MAXGENRELEN];
> >+	char subtype[MAXGENRELEN];
> >+
> >+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
> >+	struct ipt_osf_opt opt[MAX_IPOPTLEN];
> >+};
> 
> Are you sure it is safe to use arch-dependent types like 'int'?

I would be very surprised if Linux will ever run on weird arch where int
is not 32 bits.

> >+/* 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 */
> 
> Wrapping this into an enum seems to look nicer.
> 
> >+#ifdef __KERNEL__
> >+
> >+#include <linux/list.h>
> >+#include <net/tcp.h>
> >+
> >+struct ipt_osf_finger {
> >+	struct rcu_head			rcu_head;
> >+	struct list_head		finger_entry;
> >+	struct ipt_osf_user_finger	finger;
> >+};
> >+
> >+#endif				/* __KERNEL__ */
> >+
> >+#endif				/* _IPT_OSF_H */
> 
> Move this section to the .c file.

Ok.

> >+config IP_NF_MATCH_OSF
> >+	tristate '"osf" match support'
> >+	depends on NETFILTER_ADVANCED && CONNECTOR
> >+	help
> >+	  Passive OS fingerprint matching module.
> >+	  You should download and install rule loading software from
> >+	  http://www.ioremap.net/projects/osf
> >+
> >+	  To compile it as a module, choose M here.  If unsure, say N.
> >+
> 
> Please do use NETFILTER_XT_ and its section.

What's this? It does not exist in the net/ipv4/netfilter/Kconfig

> >+static bool ipt_osf_match_packet(const struct sk_buff *skb,
> >+		const struct xt_match_param *p)
> >+{
> >+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
> >+	struct iphdr *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;
> 
> Maybe this should be 'bool df'?

Although it is a bit, it is not really a boolean type.

> >+	unsigned char opts[MAX_IPOPTLEN];
> >+	struct ipt_osf_finger *kf;
> >+	struct ipt_osf_user_finger *f;
> >+	struct ipt_osf_finger_storage *st;
> >+
> >+	if (!info)
> >+		return 0;
> 
> Use 'false' - the function returns bool.

Yup.

> >+
> >+	ip = ip_hdr(skb);
> >+	if (!ip)
> >+		return 0;
> >+
> >+	tcp = skb_header_pointer(skb, ip->ihl * 4, sizeof(struct tcphdr), &_tcph);
> 
> Use ip_hdrlen(skb) instead of ihl*4.
> 
> >+	df = ((ntohs(ip->frag_off) & IP_DF) ? 1 : 0);
> 
> Together with bool df, it's just  df = ntohs(ip_frag_off) & IP_DF;
> 
> >+	st = &ipt_osf_fingers[!!df];
> 
> And the !! becomes redundant.

I have no strong opinion if this should be bool or int, but logically it
is not boolean type.

> >+			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));
> 
> This needs get_unaligned(), in case someone sends a malicious packet.

Hmmm, why is this needed? We dereference linear kernel pointer at
proper offset (modulo of the option size).

> >+			if (info->flags & IPT_OSF_LOG)
> >+				printk(KERN_INFO "%s [%s:%s] : "
> >+					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
> >+					f->genre, f->version, f->subtype, 
> >+					NIPQUAD(ip->saddr), ntohs(tcp->source), 
> >+					NIPQUAD(ip->daddr), ntohs(tcp->dest), 
> >+					f->ttl - ip->ttl);
> 
> Dave told me NIPQUAD is slated for ripout. Though, I have not
> looked if there is already a new format specifier for v4 addresses.

Its from days when it did not exist. I will update the format.

> >+	return (fmatch == FMATCH_OK) ? 1 : 0;
> 
> Just fmatch == FMATCH_OK is sufficient for bool-returning functions.

Yes.

> >+static bool
> >+ipt_osf_checkentry(const struct xt_mtchk_param *m)
> >+{
> >+	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
> >+
> >+	if (ip->proto != IPPROTO_TCP)
> >+		return false;
> >+
> >+	return true;
> >+}
> 
> Do this function via
> 	.proto = IPPROTO_TCP,
> in the osf_match struct instead:

Hmm, it is not used in the existing net/ipv4/netfilter modules.

> >+static struct xt_match ipt_osf_match = {
> >+	.name 		= "osf",
> >+	.revision	= 0,
> >+	.family		= AF_INET,
> 
> 	.family = NFPROTO_IPV4
> 

Will add.

> >+	.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,
> >+};
> 
> >+	if (kf) {
> >+		spin_lock_bh(&st->finger_lock);
> >+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
> >+		spin_unlock_bh(&st->finger_lock);
> >+
> >+		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
> 
> Missing verbosity level.

Yup.

> >+static void __devexit ipt_osf_fini(void)
> 
> Why __devinit and __devexit, should not this be __init/__exit?

It can be __init/__exit, I will replace.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:12 Evgeniy Polyakov
  2009-02-12 17:42 ` Paul E. McKenney
@ 2009-02-12 18:22 ` Jan Engelhardt
  2009-02-12 18:57   ` Evgeniy Polyakov
  2009-02-18 15:14 ` Patrick McHardy
  2 siblings, 1 reply; 54+ messages in thread
From: Jan Engelhardt @ 2009-02-12 18:22 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist


On Thursday 2009-02-12 18:12, Evgeniy Polyakov wrote:
>
>You will find something like this in the syslog:
>ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
>
>Passive OS fingerprint homepage (archives, examples):
>http://www.ioremap.net/projects/osf

>diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
>new file mode 100644
>index 0000000..c2d2c7a
>--- /dev/null
>+++ b/include/linux/netfilter_ipv4/ipt_osf.h

Much of this I think should be upgraded to the xt_ prefixing
already, even if it does not do IPv6 yet.

>+struct ipt_osf_info {
>+	char			genre[MAXGENRELEN];
>+	__u32			len;
>+	__u32			flags;
>+	__u32			loglevel;
>+	__u32			ttl;
>+};

I do not think you need an entire u32 for loglevel and ttl.


Could there at least be some description as to what the
following fields are?
 +/**
>+ * Wildcard MSS (kind of).
   * @wc:	wild card what(?)
   * @val:	some value?
>+ */
>+struct ipt_osf_wc {
>+	__u32			wc;
>+	__u32			val;
>+};

>+struct ipt_osf_user_finger {
>+	struct ipt_osf_wc wss;
>+
>+	__u8 ttl, df;
>+	__u16 ss, mss;
>+	int opt_num;
>+
>+	char genre[MAXGENRELEN];
>+	char version[MAXGENRELEN];
>+	char subtype[MAXGENRELEN];
>+
>+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
>+	struct ipt_osf_opt opt[MAX_IPOPTLEN];
>+};

Are you sure it is safe to use arch-dependent types like 'int'?

>+/* 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 */

Wrapping this into an enum seems to look nicer.

>+#ifdef __KERNEL__
>+
>+#include <linux/list.h>
>+#include <net/tcp.h>
>+
>+struct ipt_osf_finger {
>+	struct rcu_head			rcu_head;
>+	struct list_head		finger_entry;
>+	struct ipt_osf_user_finger	finger;
>+};
>+
>+#endif				/* __KERNEL__ */
>+
>+#endif				/* _IPT_OSF_H */

Move this section to the .c file.

>+config IP_NF_MATCH_OSF
>+	tristate '"osf" match support'
>+	depends on NETFILTER_ADVANCED && CONNECTOR
>+	help
>+	  Passive OS fingerprint matching module.
>+	  You should download and install rule loading software from
>+	  http://www.ioremap.net/projects/osf
>+
>+	  To compile it as a module, choose M here.  If unsure, say N.
>+

Please do use NETFILTER_XT_ and its section.

>--- /dev/null
>+++ b/net/ipv4/netfilter/ipt_osf.c
>+
>+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 (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 if (ip->ttl <= f_ttl)
>+			return 1;
>+		else {
>+			struct in_device *in_dev = in_dev_get(skb->dev);
>+			int ret = 0;
>+
>+			for_ifa(in_dev) {
>+				if (inet_ifa_match(ip->saddr, ifa)) {
>+					ret = (ip->ttl == f_ttl);
>+					break;
>+				}
>+			}
>+			endfor_ifa(in_dev);
>+
>+			in_dev_put(in_dev);
>+			return ret;
>+		}
>+	}
>+	
>+	return (ip->ttl == f_ttl);
>+}
>+
>+static bool ipt_osf_match_packet(const struct sk_buff *skb,
>+		const struct xt_match_param *p)
>+{
>+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
>+	struct iphdr *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;

Maybe this should be 'bool df'?

>+	unsigned char opts[MAX_IPOPTLEN];
>+	struct ipt_osf_finger *kf;
>+	struct ipt_osf_user_finger *f;
>+	struct ipt_osf_finger_storage *st;
>+
>+	if (!info)
>+		return 0;

Use 'false' - the function returns bool.

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

Use ip_hdrlen(skb) instead of ihl*4.

>+	df = ((ntohs(ip->frag_off) & IP_DF) ? 1 : 0);

Together with bool df, it's just  df = ntohs(ip_frag_off) & IP_DF;

>+	st = &ipt_osf_fingers[!!df];

And the !! becomes redundant.

>+			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));

This needs get_unaligned(), in case someone sends a malicious packet.

>+			if (info->flags & IPT_OSF_LOG)
>+				printk(KERN_INFO "%s [%s:%s] : "
>+					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
>+					f->genre, f->version, f->subtype, 
>+					NIPQUAD(ip->saddr), ntohs(tcp->source), 
>+					NIPQUAD(ip->daddr), ntohs(tcp->dest), 
>+					f->ttl - ip->ttl);

Dave told me NIPQUAD is slated for ripout. Though, I have not
looked if there is already a new format specifier for v4 addresses.

>+	return (fmatch == FMATCH_OK) ? 1 : 0;

Just fmatch == FMATCH_OK is sufficient for bool-returning functions.

>+static bool
>+ipt_osf_checkentry(const struct xt_mtchk_param *m)
>+{
>+	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
>+
>+	if (ip->proto != IPPROTO_TCP)
>+		return false;
>+
>+	return true;
>+}

Do this function via
	.proto = IPPROTO_TCP,
in the osf_match struct instead:

>+static struct xt_match ipt_osf_match = {
>+	.name 		= "osf",
>+	.revision	= 0,
>+	.family		= AF_INET,

	.family = NFPROTO_IPV4

>+	.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,
>+};

>+	if (kf) {
>+		spin_lock_bh(&st->finger_lock);
>+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
>+		spin_unlock_bh(&st->finger_lock);
>+
>+		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);

Missing verbosity level.

>+static void __devexit ipt_osf_fini(void)

Why __devinit and __devexit, should not this be __init/__exit?


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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:42 ` Paul E. McKenney
@ 2009-02-12 17:51   ` Evgeniy Polyakov
  2009-02-12 20:41     ` Paul E. McKenney
  2009-02-18 14:55   ` Patrick McHardy
  1 sibling, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-12 17:51 UTC (permalink / raw)
  To: Paul E. McKenney
  Cc: Patrick McHardy, netdev, David Miller, Netfilter Development Mailinglist

Hi.

On Thu, Feb 12, 2009 at 09:42:53AM -0800, Paul E. McKenney (paulmck@linux.vnet.ibm.com) wrote:
> > Passive OS fingerprint homepage (archives, examples):
> > http://www.ioremap.net/projects/osf
> 
> I advocate using this to get more accurate censuses of machines
> accessing given web servers, given the tendency of browsers to lie
> about themselves in order to avoid being shut out of certain web sites.
> 
> One question about the module-unload sequence below.
> 
> Given a reasonable answer to that question, I am OK with this from
> an RCU viewpoint.

Thanks a lot Paul, but are they actually questions? You answered all of them :)

> > +static void __devexit ipt_osf_fini(void)
> > +{
> > +	struct ipt_osf_finger *f;
> > +	int i;
> > +
> > +	cn_del_callback(&cn_osf_id);
> > +	xt_unregister_match(&ipt_osf_match);
> > +
> > +	rcu_read_lock();
> > +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> > +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> > +
> > +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> > +			list_del_rcu(&f->finger_entry);
> 
> For the above to be safe:
> 
> o	Any remaining RCU callbacks cannot reference the list
> 	(and your callbacks do in fact meet this constraint).

They do not access that list.

> o	Any timers have to have fired or been cancelled (but you
> 	don't seem to have any timers, so not a problem).

No timers, tasklets, work queues or whatever else postponing the work.

> o	All pathways to the data structure have to have been
> 	shut down.  This is the tough one -- or is it simply
> 	a requirement that the guy removing the module has shut
> 	down all requests?

They can be accessed via connector configuration, but it is stopped
above (and it waits for currently active users to run away);
and netfilter path, which should be prevented after match was also
unregistered above.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:12 Evgeniy Polyakov
@ 2009-02-12 17:42 ` Paul E. McKenney
  2009-02-12 17:51   ` Evgeniy Polyakov
  2009-02-18 14:55   ` Patrick McHardy
  2009-02-12 18:22 ` Jan Engelhardt
  2009-02-18 15:14 ` Patrick McHardy
  2 siblings, 2 replies; 54+ messages in thread
From: Paul E. McKenney @ 2009-02-12 17:42 UTC (permalink / raw)
  To: Evgeniy Polyakov
  Cc: Patrick McHardy, netdev, David Miller, Netfilter Development Mailinglist

On Thu, Feb 12, 2009 at 08:12:45PM +0300, Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.
> 
> Fingerprint matching rules can be downloaded from OpenBSD source tree
> and loaded via netlink connector into the kernel via special util found
> in archive. It will also listen for events about matching packets.
> 
> Archive also contains library file (also attached), which was shipped
> with iptables extensions some time ago (at least when ipt_osf existed
> in patch-o-matic).
> 
> This release moves all rules initialization to be handled over the
> netlink and introduces lookup tables to speed-up RCU finger matching
> a bit. Also fixed module unloading RCU completion race noticed by
> Paul McKenney.
> 
> Resending after Patrick's request :)
> 
> Fingerprints can be downloaded from
> http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
> 
> Example usage:
> # modrpobe ipt_osf
> # ./ucon_osf -f ./pf.os
> ^C Daemon will listen for incoming match events 
> -d switch removes fingerprints
> # iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector
> 
> You will find something like this in the syslog:
> ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
> 
> Passive OS fingerprint homepage (archives, examples):
> http://www.ioremap.net/projects/osf

I advocate using this to get more accurate censuses of machines
accessing given web servers, given the tendency of browsers to lie
about themselves in order to avoid being shut out of certain web sites.

One question about the module-unload sequence below.

Given a reasonable answer to that question, I am OK with this from
an RCU viewpoint.

						Thanx, Paul

> Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>
> 
> diff --git a/include/linux/connector.h b/include/linux/connector.h
> index 5c7f946..b77e6fa 100644
> --- a/include/linux/connector.h
> +++ b/include/linux/connector.h
> @@ -39,6 +39,8 @@
>  #define CN_IDX_V86D			0x4
>  #define CN_VAL_V86D_UVESAFB		0x1
>  #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
> +#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
> +#define CN_VAL_OSF			0x6
> 
>  #define CN_NETLINK_USERS		6
> 
> diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
> new file mode 100644
> index 0000000..c2d2c7a
> --- /dev/null
> +++ b/include/linux/netfilter_ipv4/ipt_osf.h
> @@ -0,0 +1,120 @@
> +/*
> + * ipt_osf.h
> + *
> + * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
> + *
> + *
> + * 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<<0)
> +#define	IPT_OSF_TTL		(1<<1)
> +#define IPT_OSF_LOG		(1<<2)
> +#define IPT_OSF_UNUSED		(1<<3)
> +#define IPT_OSF_CONNECTOR	(1<<4)
> +#define IPT_OSF_INVERT		(1<<5)
> +
> +#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 */
> +
> +#define MAX_IPOPTLEN		40
> +
> +struct ipt_osf_info {
> +	char			genre[MAXGENRELEN];
> +	__u32			len;
> +	__u32			flags;
> +	__u32			loglevel;
> +	__u32			ttl;
> +};
> +
> +/*
> + * Wildcard MSS (kind of).
> + */
> +struct ipt_osf_wc {
> +	__u32			wc;
> +	__u32			val;
> +};
> +
> +/*
> + * This struct represents IANA options
> + * http://www.iana.org/assignments/tcp-parameters
> + */
> +struct ipt_osf_opt {
> +	__u16			kind, length;
> +	struct ipt_osf_wc	wc;
> +};
> +
> +struct ipt_osf_user_finger {
> +	struct ipt_osf_wc wss;
> +
> +	__u8 ttl, df;
> +	__u16 ss, mss;
> +	int opt_num;
> +
> +	char genre[MAXGENRELEN];
> +	char version[MAXGENRELEN];
> +	char subtype[MAXGENRELEN];
> +
> +	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
> +	struct ipt_osf_opt opt[MAX_IPOPTLEN];
> +};
> +
> +struct ipt_osf_nlmsg {
> +	struct ipt_osf_user_finger	f;
> +	struct iphdr		ip;
> +	struct tcphdr		tcp;
> +};
> +
> +/* 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 */
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/list.h>
> +#include <net/tcp.h>
> +
> +struct ipt_osf_finger {
> +	struct rcu_head			rcu_head;
> +	struct list_head		finger_entry;
> +	struct ipt_osf_user_finger	finger;
> +};
> +
> +#endif				/* __KERNEL__ */
> +
> +#endif				/* _IPT_OSF_H */
> diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
> index 3816e1d..280f779 100644
> --- a/net/ipv4/netfilter/Kconfig
> +++ b/net/ipv4/netfilter/Kconfig
> @@ -101,6 +101,16 @@ config IP_NF_MATCH_TTL
> 
>  	  To compile it as a module, choose M here.  If unsure, say N.
> 
> +config IP_NF_MATCH_OSF
> +	tristate '"osf" match support'
> +	depends on NETFILTER_ADVANCED && CONNECTOR
> +	help
> +	  Passive OS fingerprint matching module.
> +	  You should download and install rule loading software from
> +	  http://www.ioremap.net/projects/osf
> +
> +	  To compile it as a module, choose M here.  If unsure, say N.
> +
>  # `filter', generic and specific targets
>  config IP_NF_FILTER
>  	tristate "Packet filtering"
> diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
> index 5f9b650..98daea9 100644
> --- a/net/ipv4/netfilter/Makefile
> +++ b/net/ipv4/netfilter/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
>  obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
>  obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
>  obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
> +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
> 
>  # targets
>  obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
> diff --git a/net/ipv4/netfilter/ipt_osf.c b/net/ipv4/netfilter/ipt_osf.c
> new file mode 100644
> index 0000000..9356a44
> --- /dev/null
> +++ b/net/ipv4/netfilter/ipt_osf.c
> @@ -0,0 +1,476 @@
> +/*
> + * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
> + *
> + *
> + * 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/connector.h>
> +#include <linux/if.h>
> +#include <linux/inetdevice.h>
> +#include <linux/ip.h>
> +#include <linux/list.h>
> +#include <linux/percpu.h>
> +#include <linux/rculist.h>
> +#include <linux/smp.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/tcp.h>
> +#include <linux/types.h>
> +
> +#include <net/ip.h>
> +
> +#include <linux/netfilter/x_tables.h>
> +#include <linux/netfilter_ipv4/ip_tables.h>
> +#include <linux/netfilter_ipv4/ipt_osf.h>
> +
> +enum osf_fmatch_states {
> +	/* Packet does not match the fingerprint */
> +	FMATCH_WRONG = 0,
> +	/* Packet matches the fingerprint */
> +	FMATCH_OK,
> +	/* Options do not match the fingerprint, but header does */
> +	FMATCH_OPT_WRONG,
> +};
> +
> +struct ipt_osf_finger_storage
> +{
> +	struct list_head		finger_list;
> +	spinlock_t			finger_lock;
> +};
> +
> +/*
> + * Indexed by dont-fragment bit.
> + * It is the only constant value in the fingerprint.
> + */
> +struct ipt_osf_finger_storage ipt_osf_fingers[2];
> +
> +struct ipt_osf_message {
> +	struct cn_msg		cmsg;
> +	struct ipt_osf_nlmsg	nlmsg;
> +};
> +
> +static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
> +
> +static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
> +static u32 osf_seq;
> +
> +static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
> +				   const struct sk_buff *skb)
> +{
> +	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
> +	struct ipt_osf_nlmsg *data = &msg->nlmsg;
> +	struct iphdr *iph = ip_hdr(skb);
> +	struct tcphdr *tcph = tcp_hdr(skb);
> +
> +	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
> +	msg->cmsg.seq = osf_seq++;
> +	msg->cmsg.ack = 0;
> +	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
> +
> +	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
> +	memcpy(&data->ip, iph, sizeof(struct iphdr));
> +	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
> +
> +	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
> +}
> +
> +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 (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 if (ip->ttl <= f_ttl)
> +			return 1;
> +		else {
> +			struct in_device *in_dev = in_dev_get(skb->dev);
> +			int ret = 0;
> +
> +			for_ifa(in_dev) {
> +				if (inet_ifa_match(ip->saddr, ifa)) {
> +					ret = (ip->ttl == f_ttl);
> +					break;
> +				}
> +			}
> +			endfor_ifa(in_dev);
> +
> +			in_dev_put(in_dev);
> +			return ret;
> +		}
> +	}
> +	
> +	return (ip->ttl == f_ttl);
> +}
> +
> +static bool ipt_osf_match_packet(const struct sk_buff *skb,
> +		const struct xt_match_param *p)
> +{
> +	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
> +	struct iphdr *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 *kf;
> +	struct ipt_osf_user_finger *f;
> +	struct ipt_osf_finger_storage *st;
> +
> +	if (!info)
> +		return 0;
> +
> +	ip = ip_hdr(skb);
> +	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))
> +			optsize = sizeof(opts);
> +
> +		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
> +				optsize, opts);
> +	}
> +
> +	st = &ipt_osf_fingers[!!df];
> +	
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
> +		f = &kf->finger;
> +
> +		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:
> +				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 (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;
> +					}
> +
> +					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)
> +				continue;
> +
> +			fcount++;
> +			if (info->flags & IPT_OSF_LOG)
> +				printk(KERN_INFO "%s [%s:%s] : "
> +					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
> +					f->genre, f->version, f->subtype, 
> +					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_CONNECTOR))) {
> +		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
> +		unsigned int i, optsize;
> +		struct ipt_osf_user_finger fg;
> +
> +		memset(&fg, 0, sizeof(fg));
> +#if 1
> +		if (info->flags & IPT_OSF_LOG) {
> +			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
> +				printk(KERN_INFO "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)
> +					printk("TRUNCATED");
> +				else 
> +					for (i = 0; i < optsize; i++)
> +						printk("%02X ", opt[i]);
> +			}
> +
> +			printk(" %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_CONNECTOR) {
> +			fg.wss.val = window;
> +			fg.ttl = ip->ttl;
> +			fg.df = df;
> +			fg.ss = totlen;
> +			fg.mss = mss;
> +			strncpy(fg.genre, "Unknown", MAXGENRELEN);
> +
> +			ipt_osf_send_connector(&fg, skb);
> +		}
> +	}
> +
> +	if (fcount)
> +		fmatch = FMATCH_OK;
> +
> +	return (fmatch == FMATCH_OK) ? 1 : 0;
> +}
> +
> +static bool
> +ipt_osf_checkentry(const struct xt_mtchk_param *m)
> +{
> +	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
> +
> +	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 void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
> +{
> +	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
> +
> +	kfree(f);
> +}
> +
> +static void osf_cn_callback(void *data)
> +{
> +	struct cn_msg *msg = data;
> +	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
> +	struct ipt_osf_finger *kf = NULL, *sf;
> +	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
> +
> +	/*
> +	 * If msg->ack is set to 0 then we add attached fingerprint,
> +	 * otherwise remove, and in this case we do not need to allocate data.
> +	 */
> +	if (!msg->ack) {
> +		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
> +		if (!kf)
> +			return;
> +
> +		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
> +	}
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
> +		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
> +			continue;
> +
> +		if (msg->ack) {
> +			spin_lock_bh(&st->finger_lock);
> +			list_del_rcu(&sf->finger_entry);
> +			spin_unlock_bh(&st->finger_lock);
> +			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
> +		} else {
> +			kfree(kf);
> +			kf = NULL;
> +		}
> +
> +		break;
> +	}
> +
> +	if (kf) {
> +		spin_lock_bh(&st->finger_lock);
> +		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
> +		spin_unlock_bh(&st->finger_lock);
> +
> +		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
> +	}
> +	rcu_read_unlock();
> +}
> +
> +static int __devinit ipt_osf_init(void)
> +{
> +	int err = -EINVAL;
> +	int i;
> +
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		INIT_LIST_HEAD(&st->finger_list);
> +		spin_lock_init(&st->finger_lock);
> +	}
> +
> +	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
> +		goto err_out_exit;
> +	}
> +
> +	err = xt_register_match(&ipt_osf_match);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
> +				"matching module.\n", err);
> +		goto err_out_remove;
> +	}
> +
> +	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
> +
> +	return 0;
> +
> +err_out_remove:
> +	cn_del_callback(&cn_osf_id);
> +err_out_exit:
> +	return err;
> +}
> +
> +static void __devexit ipt_osf_fini(void)
> +{
> +	struct ipt_osf_finger *f;
> +	int i;
> +
> +	cn_del_callback(&cn_osf_id);
> +	xt_unregister_match(&ipt_osf_match);
> +
> +	rcu_read_lock();
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> +			list_del_rcu(&f->finger_entry);

For the above to be safe:

o	Any remaining RCU callbacks cannot reference the list
	(and your callbacks do in fact meet this constraint).

o	Any timers have to have fired or been cancelled (but you
	don't seem to have any timers, so not a problem).

o	All pathways to the data structure have to have been
	shut down.  This is the tough one -- or is it simply
	a requirement that the guy removing the module has shut
	down all requests?

> +			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	rcu_barrier();
> +
> +	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
> +}
> +
> +module_init(ipt_osf_init);
> +module_exit(ipt_osf_fini);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
> +MODULE_DESCRIPTION("Passive OS fingerprint matching.");
> 
> -- 
> 	Evgeniy Polyakov

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
>  *
>  *
>  * 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 <xtables.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 = "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 '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);
> }

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
>  *
>  *
>  * 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 <xtables.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 = "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 '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);
> }


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

* Passive OS fingerprint xtables match.
@ 2009-02-12 17:12 Evgeniy Polyakov
  2009-02-12 17:42 ` Paul E. McKenney
                   ` (2 more replies)
  0 siblings, 3 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-02-12 17:12 UTC (permalink / raw)
  To: Patrick McHardy
  Cc: netdev, David Miller, Paul E. McKenney,
	Netfilter Development Mailinglist

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
and loaded via netlink connector into the kernel via special util found
in archive. It will also listen for events about matching packets.

Archive also contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

This release moves all rules initialization to be handled over the
netlink and introduces lookup tables to speed-up RCU finger matching
a bit. Also fixed module unloading RCU completion race noticed by
Paul McKenney.

Resending after Patrick's request :)

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os

Example usage:
# modrpobe ipt_osf
# ./ucon_osf -f ./pf.os
^C Daemon will listen for incoming match events 
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector

You will find something like this in the syslog:
ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

diff --git a/include/linux/connector.h b/include/linux/connector.h
index 5c7f946..b77e6fa 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -39,6 +39,8 @@
 #define CN_IDX_V86D			0x4
 #define CN_VAL_V86D_UVESAFB		0x1
 #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
+#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
+#define CN_VAL_OSF			0x6
 
 #define CN_NETLINK_USERS		6
 
diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
new file mode 100644
index 0000000..c2d2c7a
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_osf.h
@@ -0,0 +1,120 @@
+/*
+ * ipt_osf.h
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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<<0)
+#define	IPT_OSF_TTL		(1<<1)
+#define IPT_OSF_LOG		(1<<2)
+#define IPT_OSF_UNUSED		(1<<3)
+#define IPT_OSF_CONNECTOR	(1<<4)
+#define IPT_OSF_INVERT		(1<<5)
+
+#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 */
+
+#define MAX_IPOPTLEN		40
+
+struct ipt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ */
+struct ipt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct ipt_osf_opt {
+	__u16			kind, length;
+	struct ipt_osf_wc	wc;
+};
+
+struct ipt_osf_user_finger {
+	struct ipt_osf_wc wss;
+
+	__u8 ttl, df;
+	__u16 ss, mss;
+	int opt_num;
+
+	char genre[MAXGENRELEN];
+	char version[MAXGENRELEN];
+	char subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct ipt_osf_opt opt[MAX_IPOPTLEN];
+};
+
+struct ipt_osf_nlmsg {
+	struct ipt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* 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 */
+
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+#include <net/tcp.h>
+
+struct ipt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct ipt_osf_user_finger	finger;
+};
+
+#endif				/* __KERNEL__ */
+
+#endif				/* _IPT_OSF_H */
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 3816e1d..280f779 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -101,6 +101,16 @@ config IP_NF_MATCH_TTL
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_NF_MATCH_OSF
+	tristate '"osf" match support'
+	depends on NETFILTER_ADVANCED && CONNECTOR
+	help
+	  Passive OS fingerprint matching module.
+	  You should download and install rule loading software from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 # `filter', generic and specific targets
 config IP_NF_FILTER
 	tristate "Packet filtering"
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 5f9b650..98daea9 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
 obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
 obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
 obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
+obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
 
 # targets
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
diff --git a/net/ipv4/netfilter/ipt_osf.c b/net/ipv4/netfilter/ipt_osf.c
new file mode 100644
index 0000000..9356a44
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_osf.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/connector.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/rculist.h>
+#include <linux/smp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+
+#include <net/ip.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_osf.h>
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct ipt_osf_finger_storage
+{
+	struct list_head		finger_list;
+	spinlock_t			finger_lock;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+struct ipt_osf_finger_storage ipt_osf_fingers[2];
+
+struct ipt_osf_message {
+	struct cn_msg		cmsg;
+	struct ipt_osf_nlmsg	nlmsg;
+};
+
+static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
+
+static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
+static u32 osf_seq;
+
+static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
+				   const struct sk_buff *skb)
+{
+	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
+	struct ipt_osf_nlmsg *data = &msg->nlmsg;
+	struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *tcph = tcp_hdr(skb);
+
+	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
+	msg->cmsg.seq = osf_seq++;
+	msg->cmsg.ack = 0;
+	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
+
+	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
+	memcpy(&data->ip, iph, sizeof(struct iphdr));
+	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
+
+	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
+}
+
+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 (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 if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+	
+	return (ip->ttl == f_ttl);
+}
+
+static bool ipt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
+	struct iphdr *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 *kf;
+	struct ipt_osf_user_finger *f;
+	struct ipt_osf_finger_storage *st;
+
+	if (!info)
+		return 0;
+
+	ip = ip_hdr(skb);
+	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))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
+				optsize, opts);
+	}
+
+	st = &ipt_osf_fingers[!!df];
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		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:
+				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 (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;
+					}
+
+					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)
+				continue;
+
+			fcount++;
+			if (info->flags & IPT_OSF_LOG)
+				printk(KERN_INFO "%s [%s:%s] : "
+					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
+					f->genre, f->version, f->subtype, 
+					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_CONNECTOR))) {
+		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
+		unsigned int i, optsize;
+		struct ipt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+#if 1
+		if (info->flags & IPT_OSF_LOG) {
+			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
+				printk(KERN_INFO "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)
+					printk("TRUNCATED");
+				else 
+					for (i = 0; i < optsize; i++)
+						printk("%02X ", opt[i]);
+			}
+
+			printk(" %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_CONNECTOR) {
+			fg.wss.val = window;
+			fg.ttl = ip->ttl;
+			fg.df = df;
+			fg.ss = totlen;
+			fg.mss = mss;
+			strncpy(fg.genre, "Unknown", MAXGENRELEN);
+
+			ipt_osf_send_connector(&fg, skb);
+		}
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return (fmatch == FMATCH_OK) ? 1 : 0;
+}
+
+static bool
+ipt_osf_checkentry(const struct xt_mtchk_param *m)
+{
+	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
+
+	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 void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static void osf_cn_callback(void *data)
+{
+	struct cn_msg *msg = data;
+	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
+	struct ipt_osf_finger *kf = NULL, *sf;
+	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
+
+	/*
+	 * If msg->ack is set to 0 then we add attached fingerprint,
+	 * otherwise remove, and in this case we do not need to allocate data.
+	 */
+	if (!msg->ack) {
+		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
+		if (!kf)
+			return;
+
+		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
+			continue;
+
+		if (msg->ack) {
+			spin_lock_bh(&st->finger_lock);
+			list_del_rcu(&sf->finger_entry);
+			spin_unlock_bh(&st->finger_lock);
+			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
+		} else {
+			kfree(kf);
+			kf = NULL;
+		}
+
+		break;
+	}
+
+	if (kf) {
+		spin_lock_bh(&st->finger_lock);
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+		spin_unlock_bh(&st->finger_lock);
+
+		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
+	}
+	rcu_read_unlock();
+}
+
+static int __devinit ipt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+		spin_lock_init(&st->finger_lock);
+	}
+
+	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&ipt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	cn_del_callback(&cn_osf_id);
+err_out_exit:
+	return err;
+}
+
+static void __devexit ipt_osf_fini(void)
+{
+	struct ipt_osf_finger *f;
+	int i;
+
+	cn_del_callback(&cn_osf_id);
+	xt_unregister_match(&ipt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	rcu_barrier();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(ipt_osf_init);
+module_exit(ipt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");

-- 
	Evgeniy Polyakov

[-- Attachment #2: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4895 bytes --]

/*
 * libipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <xtables.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 = "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 '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 #3: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4895 bytes --]

/*
 * libipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <xtables.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 = "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 '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);
}

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

* Re: Passive OS fingerprint xtables match.
  2009-01-29  3:36 ` Paul E. McKenney
@ 2009-01-29 15:03   ` Evgeniy Polyakov
  0 siblings, 0 replies; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-01-29 15:03 UTC (permalink / raw)
  To: Paul E. McKenney; +Cc: Patrick McHardy, netdev, David Miller

Hi Paul.

On Wed, Jan 28, 2009 at 07:36:13PM -0800, Paul E. McKenney (paulmck@linux.vnet.ibm.com) wrote:
> > Passive OS fingerprint homepage (archives, examples):
> > http://www.ioremap.net/projects/osf
> 
> Cool stuff!!!

Thank you :)

> However, I believe you need an rcu_barrier() in the module-exit function
> as noted below.

> > +static void __devexit ipt_osf_fini(void)
> > +{
> > +	struct ipt_osf_finger *f;
> > +	int i;
> > +
> > +	cn_del_callback(&cn_osf_id);
> > +	xt_unregister_match(&ipt_osf_match);
> > +
> > +	rcu_read_lock();
> > +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> > +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> > +
> > +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> > +			list_del_rcu(&f->finger_entry);
> > +			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
> > +		}
> > +	}
> > +	rcu_read_unlock();
> 
> Don't we need an rcu_barrier() here so that the preceding RCU callbacks
> are guaranteed to complete before the module text/data/bss vanish?
> 
> Whatever does the rmmod is responsible for making sure that there are no
> additional callers into the various entry points once the rmmod starts,
> I take it?  I don't see anything here that prevents something like that
> from happening (though I easily could be missing something).

All objects freed there were dynamically allocated, so we just
kfree()'ing some data not accessing static data potentially destroyed by
the rmmod and not accessing statically created, so there should be no
problems as far as I can see.

-- 
	Evgeniy Polyakov

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

* Re: Passive OS fingerprint xtables match.
  2009-01-27 22:55 Evgeniy Polyakov
@ 2009-01-29  3:36 ` Paul E. McKenney
  2009-01-29 15:03   ` Evgeniy Polyakov
  0 siblings, 1 reply; 54+ messages in thread
From: Paul E. McKenney @ 2009-01-29  3:36 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: Patrick McHardy, netdev, David Miller

On Wed, Jan 28, 2009 at 01:55:45AM +0300, Evgeniy Polyakov wrote:
> Hi.
> 
> Passive OS fingerprinting netfilter module allows to passively detect
> remote OS 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.
> 
> Fingerprint matching rules can be downloaded from OpenBSD source tree
> and loaded via netlink connector into the kernel via special util found
> in archive. It will also listen for events about matching packets.
> 
> Archive also contains library file (also attached), which was shipped
> with iptables extensions some time ago (at least when ipt_osf existed
> in patch-o-matic).
> 
> This release moves all rules initialization to be handled over the
> netlink and introduces lookup tables to speed-up RCU finger matching
> a bit. Actually it is a second resend of the same patch :)
> 
> Fingerprints can be downloaded from
> http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os
> 
> Example usage:
> # modrpobe ipt_osf
> # ./ucon_osf -f ./pf.os
> ^C Daemon will listen for incoming match events 
> -d switch removes fingerprints
> # iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector
> 
> You will find something like this in the syslog:
> ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139
> 
> Passive OS fingerprint homepage (archives, examples):
> http://www.ioremap.net/projects/osf

Cool stuff!!!

However, I believe you need an rcu_barrier() in the module-exit function
as noted below.

							Thanx, Paul

> Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>
> 
> diff --git a/include/linux/connector.h b/include/linux/connector.h
> index 5c7f946..b77e6fa 100644
> --- a/include/linux/connector.h
> +++ b/include/linux/connector.h
> @@ -39,6 +39,8 @@
>  #define CN_IDX_V86D			0x4
>  #define CN_VAL_V86D_UVESAFB		0x1
>  #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
> +#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
> +#define CN_VAL_OSF			0x6
> 
>  #define CN_NETLINK_USERS		6
> 
> diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
> new file mode 100644
> index 0000000..c2d2c7a
> --- /dev/null
> +++ b/include/linux/netfilter_ipv4/ipt_osf.h
> @@ -0,0 +1,120 @@
> +/*
> + * ipt_osf.h
> + *
> + * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
> + *
> + *
> + * 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<<0)
> +#define	IPT_OSF_TTL		(1<<1)
> +#define IPT_OSF_LOG		(1<<2)
> +#define IPT_OSF_UNUSED		(1<<3)
> +#define IPT_OSF_CONNECTOR	(1<<4)
> +#define IPT_OSF_INVERT		(1<<5)
> +
> +#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 */
> +
> +#define MAX_IPOPTLEN		40
> +
> +struct ipt_osf_info {
> +	char			genre[MAXGENRELEN];
> +	__u32			len;
> +	__u32			flags;
> +	__u32			loglevel;
> +	__u32			ttl;
> +};
> +
> +/*
> + * Wildcard MSS (kind of).
> + */
> +struct ipt_osf_wc {
> +	__u32			wc;
> +	__u32			val;
> +};
> +
> +/*
> + * This struct represents IANA options
> + * http://www.iana.org/assignments/tcp-parameters
> + */
> +struct ipt_osf_opt {
> +	__u16			kind, length;
> +	struct ipt_osf_wc	wc;
> +};
> +
> +struct ipt_osf_user_finger {
> +	struct ipt_osf_wc wss;
> +
> +	__u8 ttl, df;
> +	__u16 ss, mss;
> +	int opt_num;
> +
> +	char genre[MAXGENRELEN];
> +	char version[MAXGENRELEN];
> +	char subtype[MAXGENRELEN];
> +
> +	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
> +	struct ipt_osf_opt opt[MAX_IPOPTLEN];
> +};
> +
> +struct ipt_osf_nlmsg {
> +	struct ipt_osf_user_finger	f;
> +	struct iphdr		ip;
> +	struct tcphdr		tcp;
> +};
> +
> +/* 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 */
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/list.h>
> +#include <net/tcp.h>
> +
> +struct ipt_osf_finger {
> +	struct rcu_head			rcu_head;
> +	struct list_head		finger_entry;
> +	struct ipt_osf_user_finger	finger;
> +};
> +
> +#endif				/* __KERNEL__ */
> +
> +#endif				/* _IPT_OSF_H */
> diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
> index 3816e1d..280f779 100644
> --- a/net/ipv4/netfilter/Kconfig
> +++ b/net/ipv4/netfilter/Kconfig
> @@ -101,6 +101,16 @@ config IP_NF_MATCH_TTL
> 
>  	  To compile it as a module, choose M here.  If unsure, say N.
> 
> +config IP_NF_MATCH_OSF
> +	tristate '"osf" match support'
> +	depends on NETFILTER_ADVANCED && CONNECTOR
> +	help
> +	  Passive OS fingerprint matching module.
> +	  You should download and install rule loading software from
> +	  http://www.ioremap.net/projects/osf
> +
> +	  To compile it as a module, choose M here.  If unsure, say N.
> +
>  # `filter', generic and specific targets
>  config IP_NF_FILTER
>  	tristate "Packet filtering"
> diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
> index 5f9b650..98daea9 100644
> --- a/net/ipv4/netfilter/Makefile
> +++ b/net/ipv4/netfilter/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
>  obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
>  obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
>  obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
> +obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
> 
>  # targets
>  obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
> diff --git a/net/ipv4/netfilter/ipt_osf.c b/net/ipv4/netfilter/ipt_osf.c
> new file mode 100644
> index 0000000..9356a44
> --- /dev/null
> +++ b/net/ipv4/netfilter/ipt_osf.c
> @@ -0,0 +1,474 @@
> +/*
> + * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
> + *
> + *
> + * 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/connector.h>
> +#include <linux/if.h>
> +#include <linux/inetdevice.h>
> +#include <linux/ip.h>
> +#include <linux/list.h>
> +#include <linux/percpu.h>
> +#include <linux/rculist.h>
> +#include <linux/smp.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/tcp.h>
> +#include <linux/types.h>
> +
> +#include <net/ip.h>
> +
> +#include <linux/netfilter/x_tables.h>
> +#include <linux/netfilter_ipv4/ip_tables.h>
> +#include <linux/netfilter_ipv4/ipt_osf.h>
> +
> +enum osf_fmatch_states {
> +	/* Packet does not match the fingerprint */
> +	FMATCH_WRONG = 0,
> +	/* Packet matches the fingerprint */
> +	FMATCH_OK,
> +	/* Options do not match the fingerprint, but header does */
> +	FMATCH_OPT_WRONG,
> +};
> +
> +struct ipt_osf_finger_storage
> +{
> +	struct list_head		finger_list;
> +	spinlock_t			finger_lock;
> +};
> +
> +/*
> + * Indexed by dont-fragment bit.
> + * It is the only constant value in the fingerprint.
> + */
> +struct ipt_osf_finger_storage ipt_osf_fingers[2];
> +
> +struct ipt_osf_message {
> +	struct cn_msg		cmsg;
> +	struct ipt_osf_nlmsg	nlmsg;
> +};
> +
> +static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
> +
> +static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
> +static u32 osf_seq;
> +
> +static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
> +				   const struct sk_buff *skb)
> +{
> +	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
> +	struct ipt_osf_nlmsg *data = &msg->nlmsg;
> +	struct iphdr *iph = ip_hdr(skb);
> +	struct tcphdr *tcph = tcp_hdr(skb);
> +
> +	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
> +	msg->cmsg.seq = osf_seq++;
> +	msg->cmsg.ack = 0;
> +	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
> +
> +	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
> +	memcpy(&data->ip, iph, sizeof(struct iphdr));
> +	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
> +
> +	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
> +}
> +
> +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 (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 if (ip->ttl <= f_ttl)
> +			return 1;
> +		else {
> +			struct in_device *in_dev = in_dev_get(skb->dev);
> +			int ret = 0;
> +
> +			for_ifa(in_dev) {
> +				if (inet_ifa_match(ip->saddr, ifa)) {
> +					ret = (ip->ttl == f_ttl);
> +					break;
> +				}
> +			}
> +			endfor_ifa(in_dev);
> +
> +			in_dev_put(in_dev);
> +			return ret;
> +		}
> +	}
> +	
> +	return (ip->ttl == f_ttl);
> +}
> +
> +static bool ipt_osf_match_packet(const struct sk_buff *skb,
> +		const struct xt_match_param *p)
> +{
> +	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
> +	struct iphdr *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 *kf;
> +	struct ipt_osf_user_finger *f;
> +	struct ipt_osf_finger_storage *st;
> +
> +	if (!info)
> +		return 0;
> +
> +	ip = ip_hdr(skb);
> +	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))
> +			optsize = sizeof(opts);
> +
> +		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
> +				optsize, opts);
> +	}
> +
> +	st = &ipt_osf_fingers[!!df];
> +	
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
> +		f = &kf->finger;
> +
> +		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:
> +				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 (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;
> +					}
> +
> +					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)
> +				continue;
> +
> +			fcount++;
> +			if (info->flags & IPT_OSF_LOG)
> +				printk(KERN_INFO "%s [%s:%s] : "
> +					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
> +					f->genre, f->version, f->subtype, 
> +					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_CONNECTOR))) {
> +		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
> +		unsigned int i, optsize;
> +		struct ipt_osf_user_finger fg;
> +
> +		memset(&fg, 0, sizeof(fg));
> +#if 1
> +		if (info->flags & IPT_OSF_LOG) {
> +			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
> +				printk(KERN_INFO "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)
> +					printk("TRUNCATED");
> +				else 
> +					for (i = 0; i < optsize; i++)
> +						printk("%02X ", opt[i]);
> +			}
> +
> +			printk(" %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_CONNECTOR) {
> +			fg.wss.val = window;
> +			fg.ttl = ip->ttl;
> +			fg.df = df;
> +			fg.ss = totlen;
> +			fg.mss = mss;
> +			strncpy(fg.genre, "Unknown", MAXGENRELEN);
> +
> +			ipt_osf_send_connector(&fg, skb);
> +		}
> +	}
> +
> +	if (fcount)
> +		fmatch = FMATCH_OK;
> +
> +	return (fmatch == FMATCH_OK) ? 1 : 0;
> +}
> +
> +static bool
> +ipt_osf_checkentry(const struct xt_mtchk_param *m)
> +{
> +	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
> +
> +	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 void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
> +{
> +	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
> +
> +	kfree(f);
> +}
> +
> +static void osf_cn_callback(void *data)
> +{
> +	struct cn_msg *msg = data;
> +	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
> +	struct ipt_osf_finger *kf = NULL, *sf;
> +	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
> +
> +	/*
> +	 * If msg->ack is set to 0 then we add attached fingerprint,
> +	 * otherwise remove, and in this case we do not need to allocate data.
> +	 */
> +	if (!msg->ack) {
> +		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
> +		if (!kf)
> +			return;
> +
> +		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
> +	}
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
> +		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
> +			continue;
> +
> +		if (msg->ack) {
> +			spin_lock_bh(&st->finger_lock);
> +			list_del_rcu(&sf->finger_entry);
> +			spin_unlock_bh(&st->finger_lock);
> +			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
> +		} else {
> +			kfree(kf);
> +			kf = NULL;
> +		}
> +
> +		break;
> +	}
> +
> +	if (kf) {
> +		spin_lock_bh(&st->finger_lock);
> +		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
> +		spin_unlock_bh(&st->finger_lock);
> +
> +		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
> +	}
> +	rcu_read_unlock();
> +}
> +
> +static int __devinit ipt_osf_init(void)
> +{
> +	int err = -EINVAL;
> +	int i;
> +
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		INIT_LIST_HEAD(&st->finger_list);
> +		spin_lock_init(&st->finger_lock);
> +	}
> +
> +	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
> +		goto err_out_exit;
> +	}
> +
> +	err = xt_register_match(&ipt_osf_match);
> +	if (err) {
> +		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
> +				"matching module.\n", err);
> +		goto err_out_remove;
> +	}
> +
> +	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
> +
> +	return 0;
> +
> +err_out_remove:
> +	cn_del_callback(&cn_osf_id);
> +err_out_exit:
> +	return err;
> +}
> +
> +static void __devexit ipt_osf_fini(void)
> +{
> +	struct ipt_osf_finger *f;
> +	int i;
> +
> +	cn_del_callback(&cn_osf_id);
> +	xt_unregister_match(&ipt_osf_match);
> +
> +	rcu_read_lock();
> +	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
> +		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
> +
> +		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
> +			list_del_rcu(&f->finger_entry);
> +			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
> +		}
> +	}
> +	rcu_read_unlock();

Don't we need an rcu_barrier() here so that the preceding RCU callbacks
are guaranteed to complete before the module text/data/bss vanish?

Whatever does the rmmod is responsible for making sure that there are no
additional callers into the various entry points once the rmmod starts,
I take it?  I don't see anything here that prevents something like that
from happening (though I easily could be missing something).

> +	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
> +}
> +
> +module_init(ipt_osf_init);
> +module_exit(ipt_osf_fini);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
> +MODULE_DESCRIPTION("Passive OS fingerprint matching.");
> 
> -- 
> 	Evgeniy Polyakov

> /*
>  * libipt_osf.c
>  *
>  * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
>  *
>  *
>  * 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 <xtables.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 = "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 '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);
> }


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

* Passive OS fingerprint xtables match.
@ 2009-01-27 22:55 Evgeniy Polyakov
  2009-01-29  3:36 ` Paul E. McKenney
  0 siblings, 1 reply; 54+ messages in thread
From: Evgeniy Polyakov @ 2009-01-27 22:55 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, David Miller

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

Hi.

Passive OS fingerprinting netfilter module allows to passively detect
remote OS 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.

Fingerprint matching rules can be downloaded from OpenBSD source tree
and loaded via netlink connector into the kernel via special util found
in archive. It will also listen for events about matching packets.

Archive also contains library file (also attached), which was shipped
with iptables extensions some time ago (at least when ipt_osf existed
in patch-o-matic).

This release moves all rules initialization to be handled over the
netlink and introduces lookup tables to speed-up RCU finger matching
a bit. Actually it is a second resend of the same patch :)

Fingerprints can be downloaded from
http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os

Example usage:
# modrpobe ipt_osf
# ./ucon_osf -f ./pf.os
^C Daemon will listen for incoming match events 
-d switch removes fingerprints
# iptables -I INPUT -j ACCEPT -p tcp -m osf --genre Linux --log 0 --ttl 2 --connector

You will find something like this in the syslog:
ipt_osf: Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> 11.22.33.44:139

Passive OS fingerprint homepage (archives, examples):
http://www.ioremap.net/projects/osf

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>

diff --git a/include/linux/connector.h b/include/linux/connector.h
index 5c7f946..b77e6fa 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -39,6 +39,8 @@
 #define CN_IDX_V86D			0x4
 #define CN_VAL_V86D_UVESAFB		0x1
 #define CN_IDX_BB			0x5	/* BlackBoard, from the TSP GPL sampling framework */
+#define CN_IDX_OSF			0x6	/* Passive OS fingerprint iptables module */
+#define CN_VAL_OSF			0x6
 
 #define CN_NETLINK_USERS		6
 
diff --git a/include/linux/netfilter_ipv4/ipt_osf.h b/include/linux/netfilter_ipv4/ipt_osf.h
new file mode 100644
index 0000000..c2d2c7a
--- /dev/null
+++ b/include/linux/netfilter_ipv4/ipt_osf.h
@@ -0,0 +1,120 @@
+/*
+ * ipt_osf.h
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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<<0)
+#define	IPT_OSF_TTL		(1<<1)
+#define IPT_OSF_LOG		(1<<2)
+#define IPT_OSF_UNUSED		(1<<3)
+#define IPT_OSF_CONNECTOR	(1<<4)
+#define IPT_OSF_INVERT		(1<<5)
+
+#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 */
+
+#define MAX_IPOPTLEN		40
+
+struct ipt_osf_info {
+	char			genre[MAXGENRELEN];
+	__u32			len;
+	__u32			flags;
+	__u32			loglevel;
+	__u32			ttl;
+};
+
+/*
+ * Wildcard MSS (kind of).
+ */
+struct ipt_osf_wc {
+	__u32			wc;
+	__u32			val;
+};
+
+/*
+ * This struct represents IANA options
+ * http://www.iana.org/assignments/tcp-parameters
+ */
+struct ipt_osf_opt {
+	__u16			kind, length;
+	struct ipt_osf_wc	wc;
+};
+
+struct ipt_osf_user_finger {
+	struct ipt_osf_wc wss;
+
+	__u8 ttl, df;
+	__u16 ss, mss;
+	int opt_num;
+
+	char genre[MAXGENRELEN];
+	char version[MAXGENRELEN];
+	char subtype[MAXGENRELEN];
+
+	/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
+	struct ipt_osf_opt opt[MAX_IPOPTLEN];
+};
+
+struct ipt_osf_nlmsg {
+	struct ipt_osf_user_finger	f;
+	struct iphdr		ip;
+	struct tcphdr		tcp;
+};
+
+/* 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 */
+
+#ifdef __KERNEL__
+
+#include <linux/list.h>
+#include <net/tcp.h>
+
+struct ipt_osf_finger {
+	struct rcu_head			rcu_head;
+	struct list_head		finger_entry;
+	struct ipt_osf_user_finger	finger;
+};
+
+#endif				/* __KERNEL__ */
+
+#endif				/* _IPT_OSF_H */
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 3816e1d..280f779 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -101,6 +101,16 @@ config IP_NF_MATCH_TTL
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_NF_MATCH_OSF
+	tristate '"osf" match support'
+	depends on NETFILTER_ADVANCED && CONNECTOR
+	help
+	  Passive OS fingerprint matching module.
+	  You should download and install rule loading software from
+	  http://www.ioremap.net/projects/osf
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 # `filter', generic and specific targets
 config IP_NF_FILTER
 	tristate "Packet filtering"
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 5f9b650..98daea9 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
 obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
 obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
 obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
+obj-$(CONFIG_IP_NF_MATCH_OSF) += ipt_osf.o
 
 # targets
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
diff --git a/net/ipv4/netfilter/ipt_osf.c b/net/ipv4/netfilter/ipt_osf.c
new file mode 100644
index 0000000..9356a44
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_osf.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net>
+ *
+ *
+ * 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/connector.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/list.h>
+#include <linux/percpu.h>
+#include <linux/rculist.h>
+#include <linux/smp.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+
+#include <net/ip.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_osf.h>
+
+enum osf_fmatch_states {
+	/* Packet does not match the fingerprint */
+	FMATCH_WRONG = 0,
+	/* Packet matches the fingerprint */
+	FMATCH_OK,
+	/* Options do not match the fingerprint, but header does */
+	FMATCH_OPT_WRONG,
+};
+
+struct ipt_osf_finger_storage
+{
+	struct list_head		finger_list;
+	spinlock_t			finger_lock;
+};
+
+/*
+ * Indexed by dont-fragment bit.
+ * It is the only constant value in the fingerprint.
+ */
+struct ipt_osf_finger_storage ipt_osf_fingers[2];
+
+struct ipt_osf_message {
+	struct cn_msg		cmsg;
+	struct ipt_osf_nlmsg	nlmsg;
+};
+
+static DEFINE_PER_CPU(struct ipt_osf_message, ipt_osf_mbuf);
+
+static struct cb_id cn_osf_id = { CN_IDX_OSF, CN_VAL_OSF };
+static u32 osf_seq;
+
+static void ipt_osf_send_connector(struct ipt_osf_user_finger *f,
+				   const struct sk_buff *skb)
+{
+	struct ipt_osf_message *msg = &per_cpu(ipt_osf_mbuf, smp_processor_id());
+	struct ipt_osf_nlmsg *data = &msg->nlmsg;
+	struct iphdr *iph = ip_hdr(skb);
+	struct tcphdr *tcph = tcp_hdr(skb);
+
+	memcpy(&msg->cmsg.id, &cn_osf_id, sizeof(struct cn_msg));
+	msg->cmsg.seq = osf_seq++;
+	msg->cmsg.ack = 0;
+	msg->cmsg.len = sizeof(struct ipt_osf_nlmsg);
+
+	memcpy(&data->f, f, sizeof(struct ipt_osf_user_finger));
+	memcpy(&data->ip, iph, sizeof(struct iphdr));
+	memcpy(&data->tcp, tcph, sizeof(struct tcphdr));
+
+	cn_netlink_send(&msg->cmsg, CN_IDX_OSF, GFP_ATOMIC);
+}
+
+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 (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 if (ip->ttl <= f_ttl)
+			return 1;
+		else {
+			struct in_device *in_dev = in_dev_get(skb->dev);
+			int ret = 0;
+
+			for_ifa(in_dev) {
+				if (inet_ifa_match(ip->saddr, ifa)) {
+					ret = (ip->ttl == f_ttl);
+					break;
+				}
+			}
+			endfor_ifa(in_dev);
+
+			in_dev_put(in_dev);
+			return ret;
+		}
+	}
+	
+	return (ip->ttl == f_ttl);
+}
+
+static bool ipt_osf_match_packet(const struct sk_buff *skb,
+		const struct xt_match_param *p)
+{
+	struct ipt_osf_info *info = (struct ipt_osf_info *)p->matchinfo;
+	struct iphdr *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 *kf;
+	struct ipt_osf_user_finger *f;
+	struct ipt_osf_finger_storage *st;
+
+	if (!info)
+		return 0;
+
+	ip = ip_hdr(skb);
+	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))
+			optsize = sizeof(opts);
+
+		_optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr), 
+				optsize, opts);
+	}
+
+	st = &ipt_osf_fingers[!!df];
+	
+	rcu_read_lock();
+	list_for_each_entry_rcu(kf, &st->finger_list, finger_entry) {
+		f = &kf->finger;
+
+		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:
+				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 (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;
+					}
+
+					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)
+				continue;
+
+			fcount++;
+			if (info->flags & IPT_OSF_LOG)
+				printk(KERN_INFO "%s [%s:%s] : "
+					"%u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
+					f->genre, f->version, f->subtype, 
+					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_CONNECTOR))) {
+		unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
+		unsigned int i, optsize;
+		struct ipt_osf_user_finger fg;
+
+		memset(&fg, 0, sizeof(fg));
+#if 1
+		if (info->flags & IPT_OSF_LOG) {
+			if (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN)
+				printk(KERN_INFO "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)
+					printk("TRUNCATED");
+				else 
+					for (i = 0; i < optsize; i++)
+						printk("%02X ", opt[i]);
+			}
+
+			printk(" %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_CONNECTOR) {
+			fg.wss.val = window;
+			fg.ttl = ip->ttl;
+			fg.df = df;
+			fg.ss = totlen;
+			fg.mss = mss;
+			strncpy(fg.genre, "Unknown", MAXGENRELEN);
+
+			ipt_osf_send_connector(&fg, skb);
+		}
+	}
+
+	if (fcount)
+		fmatch = FMATCH_OK;
+
+	return (fmatch == FMATCH_OK) ? 1 : 0;
+}
+
+static bool
+ipt_osf_checkentry(const struct xt_mtchk_param *m)
+{
+	struct ipt_ip *ip = (struct ipt_ip *)m->entryinfo;
+
+	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 void ipt_osf_finger_free_rcu(struct rcu_head *rcu_head)
+{
+	struct ipt_osf_finger *f = container_of(rcu_head, struct ipt_osf_finger, rcu_head);
+
+	kfree(f);
+}
+
+static void osf_cn_callback(void *data)
+{
+	struct cn_msg *msg = data;
+	struct ipt_osf_user_finger *f = (struct ipt_osf_user_finger *)(msg + 1);
+	struct ipt_osf_finger *kf = NULL, *sf;
+	struct ipt_osf_finger_storage *st = &ipt_osf_fingers[!!f->df];
+
+	/*
+	 * If msg->ack is set to 0 then we add attached fingerprint,
+	 * otherwise remove, and in this case we do not need to allocate data.
+	 */
+	if (!msg->ack) {
+		kf = kmalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
+		if (!kf)
+			return;
+
+		memcpy(&kf->finger, f, sizeof(struct ipt_osf_user_finger));
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(sf, &st->finger_list, finger_entry) {
+		if (memcmp(&sf->finger, f, sizeof(struct ipt_osf_user_finger)))
+			continue;
+
+		if (msg->ack) {
+			spin_lock_bh(&st->finger_lock);
+			list_del_rcu(&sf->finger_entry);
+			spin_unlock_bh(&st->finger_lock);
+			call_rcu(&sf->rcu_head, ipt_osf_finger_free_rcu);
+		} else {
+			kfree(kf);
+			kf = NULL;
+		}
+
+		break;
+	}
+
+	if (kf) {
+		spin_lock_bh(&st->finger_lock);
+		list_add_tail_rcu(&kf->finger_entry, &st->finger_list);
+		spin_unlock_bh(&st->finger_lock);
+
+		printk("%s: added rule for %s:%s:%s.\n", __func__, kf->finger.genre, kf->finger.version, kf->finger.subtype);
+	}
+	rcu_read_unlock();
+}
+
+static int __devinit ipt_osf_init(void)
+{
+	int err = -EINVAL;
+	int i;
+
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		INIT_LIST_HEAD(&st->finger_list);
+		spin_lock_init(&st->finger_lock);
+	}
+
+	err = cn_add_callback(&cn_osf_id, "osf", osf_cn_callback);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OSF connector.\n", err);
+		goto err_out_exit;
+	}
+
+	err = xt_register_match(&ipt_osf_match);
+	if (err) {
+		printk(KERN_ERR "Failed (%d) to register OS fingerprint "
+				"matching module.\n", err);
+		goto err_out_remove;
+	}
+
+	printk(KERN_INFO "Started passive OS fingerprint matching module.\n");
+
+	return 0;
+
+err_out_remove:
+	cn_del_callback(&cn_osf_id);
+err_out_exit:
+	return err;
+}
+
+static void __devexit ipt_osf_fini(void)
+{
+	struct ipt_osf_finger *f;
+	int i;
+
+	cn_del_callback(&cn_osf_id);
+	xt_unregister_match(&ipt_osf_match);
+
+	rcu_read_lock();
+	for (i=0; i<ARRAY_SIZE(ipt_osf_fingers); ++i) {
+		struct ipt_osf_finger_storage *st = &ipt_osf_fingers[i];
+
+		list_for_each_entry_rcu(f, &st->finger_list, finger_entry) {
+			list_del_rcu(&f->finger_entry);
+			call_rcu(&f->rcu_head, ipt_osf_finger_free_rcu);
+		}
+	}
+	rcu_read_unlock();
+
+	printk(KERN_INFO "Passive OS fingerprint matching module finished.\n");
+}
+
+module_init(ipt_osf_init);
+module_exit(ipt_osf_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
+MODULE_DESCRIPTION("Passive OS fingerprint matching.");

-- 
	Evgeniy Polyakov

[-- Attachment #2: libipt_osf.c --]
[-- Type: text/x-csrc, Size: 4895 bytes --]

/*
 * libipt_osf.c
 *
 * Copyright (c) 2003-2006 Evgeniy Polyakov <zbr@ioremap.net>
 *
 *
 * 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 <xtables.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 = "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 '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);
}

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

end of thread, other threads:[~2009-06-08 17:25 UTC | newest]

Thread overview: 54+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-01-29 17:20 Passive OS fingerprint xtables match Evgeniy Polyakov
2009-01-30  1:05 ` Paul E. McKenney
2009-02-09 16:09 ` Patrick McHardy
2009-02-13 12:49   ` Evgeniy Polyakov
  -- strict thread matches above, loose matches on Subject: below --
2009-06-07 15:17 Evgeniy Polyakov
2009-06-08 15:06 ` Patrick McHardy
2009-06-08 17:25   ` Evgeniy Polyakov
2009-06-04 16:22 Evgeniy Polyakov
2009-06-05 11:54 ` Patrick McHardy
2009-06-05 13:10   ` Jan Engelhardt
2009-06-05 13:30     ` Patrick McHardy
2009-06-05 13:44       ` Jan Engelhardt
2009-06-07 15:12   ` Evgeniy Polyakov
2009-03-26 14:14 Evgeniy Polyakov
2009-03-26 14:18 ` Patrick McHardy
2009-03-26 14:59   ` Evgeniy Polyakov
2009-03-26 15:08     ` Patrick McHardy
2009-03-26 15:41       ` Evgeniy Polyakov
2009-03-26 15:47         ` Patrick McHardy
2009-03-30  6:20           ` Evgeniy Polyakov
2009-05-01 20:15             ` Evgeniy Polyakov
2009-03-10 15:13 Evgeniy Polyakov
2009-03-10 16:01 ` Evgeniy Polyakov
2009-03-10 16:07 ` Jan Engelhardt
2009-03-11 21:43   ` Evgeniy Polyakov
2009-03-10 21:01 ` Jesper Dangaard Brouer
2009-03-10 21:54   ` Evgeniy Polyakov
2009-03-16 14:40   ` Patrick McHardy
2009-03-11  9:54 ` Pablo Neira Ayuso
2009-03-11 10:00   ` Evgeniy Polyakov
2009-03-16 14:42     ` Patrick McHardy
2009-02-12 17:12 Evgeniy Polyakov
2009-02-12 17:42 ` Paul E. McKenney
2009-02-12 17:51   ` Evgeniy Polyakov
2009-02-12 20:41     ` Paul E. McKenney
2009-02-18 14:55   ` Patrick McHardy
2009-02-12 18:22 ` Jan Engelhardt
2009-02-12 18:57   ` Evgeniy Polyakov
2009-02-12 20:12     ` Jan Engelhardt
2009-02-13 13:03       ` Evgeniy Polyakov
2009-02-13 13:51         ` Jan Engelhardt
2009-02-13 14:22           ` Evgeniy Polyakov
2009-02-13 14:41             ` Jan Engelhardt
2009-02-15 17:32               ` Evgeniy Polyakov
2009-02-18 15:02         ` Patrick McHardy
2009-02-18 15:07           ` Evgeniy Polyakov
2009-02-18 15:30             ` Jan Engelhardt
2009-02-19 11:56               ` Evgeniy Polyakov
2009-02-18 15:00       ` Patrick McHardy
2009-02-18 15:28         ` Jan Engelhardt
2009-02-18 15:14 ` Patrick McHardy
2009-01-27 22:55 Evgeniy Polyakov
2009-01-29  3:36 ` Paul E. McKenney
2009-01-29 15:03   ` Evgeniy Polyakov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.