All of lore.kernel.org
 help / color / mirror / Atom feed
* Passive OS fingerprint xtables match.
@ 2009-02-12 17:12 Evgeniy Polyakov
  2009-02-12 17:42 ` Paul E. McKenney
                   ` (3 more replies)
  0 siblings, 4 replies; 29+ 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] 29+ messages in thread

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:12 Passive OS fingerprint xtables match 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
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 29+ 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] 29+ 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; 29+ 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] 29+ messages in thread

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:12 Passive OS fingerprint xtables match 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-12 18:26 ` Passive OS fingerprint xtables match (iptables part) Jan Engelhardt
  2009-02-18 15:14 ` Passive OS fingerprint xtables match Patrick McHardy
  3 siblings, 1 reply; 29+ 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] 29+ messages in thread

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


>
>#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;

These should not done here.. it likely causes a "redefinition"
warning or compile error of sorts. Include <linux/types.h> if
in doubt.

>static void osf_help(void)
>{
>	printf("OS fingerprint match options:\n"
>		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"

The syntax should be  [!] --genre string, that is what most
others use. Then the check_inverse call also be removed.

>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 }
>};

.flag can be omitted; .has_arg=true

>			osf_parse_string(argv[optind-1], info);
>			if (invert)
>				info->flags |= IPT_OSF_INVERT;
>			info->len=strlen((char *)info->genre);

This cast seems unnecessary.

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

Make use of xtables_strtoui to do bounds checking on the TTL value.

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

Similarly, put ! before.

>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

The & for function pointers is not needed (and actually makes
macro substituion break in some cases, just in case I need
an excuse)

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ messages in thread

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

On Thu, Feb 12, 2009 at 07:26:53PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >#include <xtables.h>
> >
> >typedef unsigned int __u32;
> >typedef unsigned short __u16;
> >typedef unsigned char __u8;
> 
> These should not done here.. it likely causes a "redefinition"
> warning or compile error of sorts. Include <linux/types.h> if
> in doubt.

It does not since linux/types.h header is not included, but better use
existing header of course.

> >static void osf_help(void)
> >{
> >	printf("OS fingerprint match options:\n"
> >		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
> 
> The syntax should be  [!] --genre string, that is what most
> others use. Then the check_inverse call also be removed.

ok.

> >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 }
> >};
> 
> .flag can be omitted; .has_arg=true
> 
> >			osf_parse_string(argv[optind-1], info);
> >			if (invert)
> >				info->flags |= IPT_OSF_INVERT;
> >			info->len=strlen((char *)info->genre);
> 
> This cast seems unnecessary.

Yup.

> >		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]);
> 
> Make use of xtables_strtoui to do bounds checking on the TTL value.

Hmm...
$ grep xtables_strtoui -r /tmp/iptables-1.4.2
$

> >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);
> >}
> 
> Similarly, put ! before.

ok.

> >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
> 
> The & for function pointers is not needed (and actually makes
> macro substituion break in some cases, just in case I need
> an excuse)

Well, having & clearly shows it is a pointer and not a value, especially
when it is not clear from the name what it should be.

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ messages in thread

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


On Thursday 2009-02-12 20:18, Evgeniy Polyakov wrote:
>On Thu, Feb 12, 2009 at 07:26:53PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
>> >#include <xtables.h>
>> >
>> >typedef unsigned int __u32;
>> >typedef unsigned short __u16;
>> >typedef unsigned char __u8;
>> 
>> These should not done here.. it likely causes a "redefinition"
>> warning or compile error of sorts. Include <linux/types.h> if
>> in doubt.
>
>It does not since linux/types.h header is not included, but better use
>existing header of course.

Usually it happens to be slurped in via <xtables.h> or so;
since many xt_foobar.h files use __u32 too and we do not have
a problem with them, manually adding the typedefs seems redundant.

>> >		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]);
>> 
>> Make use of xtables_strtoui to do bounds checking on the TTL value.
>
>Hmm...
>$ grep xtables_strtoui -r /tmp/iptables-1.4.2
>$

It is going to be in 1.4.3, after whose release is the earliest point
ipt_osf (or xt_osf :) will probably find its way into the mainlines.
In 1.4.2, it is still called strtonum.

>> >    .save		= &osf_save,
>> >    .extra_opts		= osf_opts
>> 
>> The & for function pointers is not needed (and actually makes
>> macro substituion break in some cases, just in case I need
>> an excuse)
>
>Well, having & clearly shows it is a pointer and not a value, especially
>when it is not clear from the name what it should be.

I think we're good - there is no & in any other extension.

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ messages in thread

* Re: Passive OS fingerprint xtables match (iptables part)
  2009-02-12 20:19     ` Jan Engelhardt
@ 2009-02-13 12:48       ` Evgeniy Polyakov
  0 siblings, 0 replies; 29+ messages in thread
From: Evgeniy Polyakov @ 2009-02-13 12:48 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:19:18PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >> Make use of xtables_strtoui to do bounds checking on the TTL value.
> >
> >Hmm...
> >$ grep xtables_strtoui -r /tmp/iptables-1.4.2
> >$
> 
> It is going to be in 1.4.3, after whose release is the earliest point
> ipt_osf (or xt_osf :) will probably find its way into the mainlines.
> In 1.4.2, it is still called strtonum.

But it can be built with older (and already running) installation, with
xtables_strtoui() this will not be allowed.

-- 
	Evgeniy Polyakov

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

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

On Thu, Feb 12, 2009 at 07:26:53PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >static void osf_help(void)
> >{
> >	printf("OS fingerprint match options:\n"
> >		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
> 
> The syntax should be  [!] --genre string, that is what most
> others use. Then the check_inverse call also be removed.

Actually not, it has genre not Linux (Windows, Solaris, HPUX and so on).

-- 
	Evgeniy Polyakov

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

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


On Friday 2009-02-13 13:52, Evgeniy Polyakov wrote:
>On Thu, Feb 12, 2009 at 07:26:53PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
>> >static void osf_help(void)
>> >{
>> >	printf("OS fingerprint match options:\n"
>> >		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
>> 
>> The syntax should be  [!] --genre string, that is what most
>> others use. Then the check_inverse call also be removed.
>
>Actually not, it has genre not Linux (Windows, Solaris, HPUX and so on).

It may not coincide with English grammar, but it is easier to parse.

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ messages in thread

* Re: Passive OS fingerprint xtables match (iptables part)
  2009-02-13 13:03     ` Jan Engelhardt
@ 2009-02-13 13:12       ` Evgeniy Polyakov
  2009-02-13 13:54         ` Jan Engelhardt
  0 siblings, 1 reply; 29+ messages in thread
From: Evgeniy Polyakov @ 2009-02-13 13:12 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:03:10PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >> >	printf("OS fingerprint match options:\n"
> >> >		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
> >> 
> >> The syntax should be  [!] --genre string, that is what most
> >> others use. Then the check_inverse call also be removed.
> >
> >Actually not, it has genre not Linux (Windows, Solaris, HPUX and so on).
> 
> It may not coincide with English grammar, but it is easier to parse.

'! --genre Linux' means this option was not specified,
'--genre ! Linux' means everything but Linux.

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ messages in thread

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


On Friday 2009-02-13 14:12, Evgeniy Polyakov wrote:
>On Fri, Feb 13, 2009 at 02:03:10PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
>> >> >	printf("OS fingerprint match options:\n"
>> >> >		"--genre [!] string	Match a OS genre by passive fingerprinting.\n"
>> >> 
>> >> The syntax should be  [!] --genre string, that is what most
>> >> others use. Then the check_inverse call also be removed.
>> >
>> >Actually not, it has genre not Linux (Windows, Solaris, HPUX and so on).
>> 
>> It may not coincide with English grammar, but it is easier to parse.
>
>'! --genre Linux' means this option was not specified,
>'--genre ! Linux' means everything but Linux.

Well not in iptables. Not specifying an option is represented by 
voidness/absence of any string. (E.g. iptables -d 192.168.0.0/16  vs.
iptables ! -s 172.16.0.0/12 -d 192.168.0.0/16)
Also, (!(--genre == "linux")) is eqv. (--genre != "linux")
is equivalent in boolean logic  ;-)

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ messages in thread

* Re: Passive OS fingerprint xtables match (iptables part)
  2009-02-13 13:54         ` Jan Engelhardt
@ 2009-02-15 17:35           ` Evgeniy Polyakov
  0 siblings, 0 replies; 29+ messages in thread
From: Evgeniy Polyakov @ 2009-02-15 17:35 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:54:25PM +0100, Jan Engelhardt (jengelh@medozas.de) wrote:
> >'! --genre Linux' means this option was not specified,
> >'--genre ! Linux' means everything but Linux.
> 
> Well not in iptables. Not specifying an option is represented by 
> voidness/absence of any string. (E.g. iptables -d 192.168.0.0/16  vs.
> iptables ! -s 172.16.0.0/12 -d 192.168.0.0/16)
> Also, (!(--genre == "linux")) is eqv. (--genre != "linux")
> is equivalent in boolean logic  ;-)

And that's not what should be :)
In the code it is intended to be:
(--genre && (--genre != Linux))

But I really do not care where to put a '!' sign in the documentation
string :)

-- 
	Evgeniy Polyakov

^ permalink raw reply	[flat|nested] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ messages in thread

* Re: Passive OS fingerprint xtables match.
  2009-02-12 17:12 Passive OS fingerprint xtables match Evgeniy Polyakov
                   ` (2 preceding siblings ...)
  2009-02-12 18:26 ` Passive OS fingerprint xtables match (iptables part) Jan Engelhardt
@ 2009-02-18 15:14 ` Patrick McHardy
  3 siblings, 0 replies; 29+ 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] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ 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; 29+ 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] 29+ messages in thread

end of thread, other threads:[~2009-02-19 11:56 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-02-12 17:12 Passive OS fingerprint xtables match 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-12 18:26 ` Passive OS fingerprint xtables match (iptables part) Jan Engelhardt
2009-02-12 19:18   ` Evgeniy Polyakov
2009-02-12 20:19     ` Jan Engelhardt
2009-02-13 12:48       ` Evgeniy Polyakov
2009-02-13 12:52   ` Evgeniy Polyakov
2009-02-13 13:03     ` Jan Engelhardt
2009-02-13 13:12       ` Evgeniy Polyakov
2009-02-13 13:54         ` Jan Engelhardt
2009-02-15 17:35           ` Evgeniy Polyakov
2009-02-18 15:14 ` Passive OS fingerprint xtables match Patrick McHardy

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.