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.");
