[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20080701195648.GD7488@linux.vnet.ibm.com>
Date: Tue, 1 Jul 2008 12:56:48 -0700
From: "Paul E. McKenney" <paulmck@...ux.vnet.ibm.com>
To: Evgeniy Polyakov <johnpol@....mipt.ru>
Cc: netdev@...r.kernel.org, netfilter-devel@...r.kernel.org
Subject: Re: Passive OS fingerprinting.
On Tue, Jul 01, 2008 at 03:39:28PM +0400, Evgeniy Polyakov wrote:
> Hi.
>
> Passive OS fingerprinting iptables (xtables) allows to match incoming
> packets by different sets of SYN-packet and determine, which remote
> system is on the remote end, so you can make decisions based on OS
> type and even version at some degreee and perform various netfilter
> actions based on that knowledge.
>
> This module compares some data (WS, MSS, options and it's order, ttl, df
> and others) from packets with SYN bit set with dynamically loaded OS
> fingerprints.
>
> This version existed quite for a while in patch-o-matic(-ng), but
> suddenly was dropped and then only was updated on its own repo:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
>
> I've updated OSF to match new iptables standards (namely xtables
> support) and present new kernelspace and userspace library files in
> attach.
A few quick questions below.
Thanx, Paul
> To setup single rule, which will drop and log all Linux incoming
> access one needs to do following steps:
> # insmod ./ipt_osf.ko
> # ./load ./pf.os /proc/sys/net/ipv4/osf
> # iptables -I INPUT -j DROP -p tcp -m osf --genre Linux --log 2 \
> --ttl 2 --connector
>
> And you will find following lines in dmesg:
>
> ipt_osf: Linux [2.5-2.6::Linux 2.5/2.6] : aa:aa:aa:aa:32885 -> bb:bb:bb:bb:23 hops=3
>
> More info can be found on homepage:
> http://tservice.net.ru/~s0mbre/old/?section=projects&item=osf
>
> Enjoy!
>
> Signed-off-by: Evgeniy Polyakov <johnpol@....mipt.ru>
>
> --
> Evgeniy Polyakov
> /*
> * ipt_osf.c
> *
> * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@....mipt.ru>
> *
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> * the Free Software Foundation; either version 2 of the License, or
> * (at your option) any later version.
> *
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> * along with this program; if not, write to the Free Software
> * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> */
>
> #include <linux/module.h>
> #include <linux/kernel.h>
>
> #include <linux/types.h>
> #include <linux/string.h>
> #include <linux/smp.h>
> #include <linux/skbuff.h>
> #include <linux/file.h>
> #include <linux/ip.h>
> #include <linux/proc_fs.h>
> #include <linux/fs.h>
> #include <linux/slab.h>
> #include <linux/spinlock.h>
> #include <linux/ctype.h>
> #include <linux/list.h>
> #include <linux/if.h>
> #include <linux/inetdevice.h>
> #include <net/ip.h>
> #include <linux/tcp.h>
>
> #include <linux/netfilter/x_tables.h>
> #include <linux/netfilter_ipv4/ip_tables.h>
> #include "ipt_osf.h"
>
> #define OSF_DEBUG
>
> #ifdef OSF_DEBUG
> #define log(x...) printk(KERN_INFO "ipt_osf: " x)
> #define loga(x...) printk(x)
> #else
> #define log(x...) do {} while(0)
> #define loga(x...) do {} while(0)
> #endif
>
> #define FMATCH_WRONG 0
> #define FMATCH_OK 1
> #define FMATCH_OPT_WRONG 2
>
> #define OPTDEL ','
> #define OSFPDEL ':'
> #define MAXOPTSTRLEN 128
> #define OSFFLUSH "FLUSH"
>
> static DEFINE_SPINLOCK(ipt_osf_lock);
> static LIST_HEAD(ipt_finger_list);
>
> #ifdef CONFIG_CONNECTOR
> #include <linux/connector.h>
>
> /*
> * They should live in connector.h.
> */
> #define CN_IDX_OSF 0x0008
> #define CN_VAL_OSF 0x0000
>
> static char osf_finger_buf[sizeof(struct ipt_osf_nlmsg) +
> sizeof(struct cn_msg)];
> static struct cb_id osf_id = { CN_IDX_OSF, CN_VAL_OSF };
> static u32 osf_seq;
>
> static void ipt_osf_send_connector(struct ipt_osf_finger *f,
> const struct sk_buff *sk)
> {
> struct cn_msg *m;
> struct ipt_osf_nlmsg *data;
>
> m = (struct cn_msg *)osf_finger_buf;
> data = (struct ipt_osf_nlmsg *)(m + 1);
>
> memcpy(&m->id, &osf_id, sizeof(m->id));
> m->seq = osf_seq++;
> m->ack = 0;
> m->len = sizeof(*data);
>
> memcpy(&data->f, f, sizeof(struct ipt_osf_finger));
> memcpy(&data->ip, sk->nh.iph, sizeof(struct iphdr));
> memcpy(&data->tcp,
> (struct tcphdr *)((u32 *)sk->nh.iph + sk->nh.iph->ihl),
> sizeof(struct tcphdr));
>
> cn_netlink_send(m, m->id.idx, GFP_ATOMIC);
> }
> #else
> static void ipt_osf_send_connector(struct ipt_osf_finger *f,
> const struct sk_buff *sk)
> {
> }
> #endif
>
> static char *ipt_osf_strchr(char *ptr, char c)
> {
> char *tmp;
>
> tmp = strchr(ptr, c);
>
> while (tmp && tmp + 1 && isspace(*(tmp + 1)))
> tmp++;
>
> return tmp;
> }
>
> static struct ipt_osf_finger *ipt_osf_finger_alloc(void)
> {
> return kzalloc(sizeof(struct ipt_osf_finger), GFP_KERNEL);
> }
>
> static void ipt_osf_finger_free(struct ipt_osf_finger *f)
> {
> memset(f, 0, sizeof(struct ipt_osf_finger));
> kfree(f);
> }
>
> static void ipt_osf_parse_opt(struct ipt_osf_opt *opt, int *optnum, char *obuf,
> int olen)
> {
> int i, op;
> char *ptr, wc;
> unsigned long val;
>
> ptr = &obuf[0];
> i = 0;
> while (ptr != NULL && i < olen) {
> val = 0;
> op = 0;
> wc = 0;
> switch (obuf[i]) {
> case 'N':
> op = OSFOPT_NOP;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> *ptr = '\0';
> ptr++;
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> case 'S':
> op = OSFOPT_SACKP;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> *ptr = '\0';
> ptr++;
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> case 'T':
> op = OSFOPT_TS;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> *ptr = '\0';
> ptr++;
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> case 'W':
> op = OSFOPT_WSO;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> switch (obuf[i + 1]) {
> case '%':
> wc = '%';
> break;
> case 'S':
> wc = 'S';
> break;
> case 'T':
> wc = 'T';
> break;
> default:
> wc = 0;
> break;
> }
>
> *ptr = '\0';
> ptr++;
> if (wc)
> val = simple_strtoul(&obuf[i + 2],
> NULL, 10);
> else
> val = simple_strtoul(&obuf[i + 1],
> NULL, 10);
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> case 'M':
> op = OSFOPT_MSS;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> if (obuf[i + 1] == '%')
> wc = '%';
> *ptr = '\0';
> ptr++;
> if (wc)
> val = simple_strtoul(&obuf[i + 2],
> NULL, 10);
> else
> val = simple_strtoul(&obuf[i + 1],
> NULL, 10);
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> case 'E':
> op = OSFOPT_EOL;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> *ptr = '\0';
> ptr++;
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> default:
> op = OSFOPT_EMPTY;
> ptr = ipt_osf_strchr(&obuf[i], OPTDEL);
> if (ptr) {
> ptr++;
> i += (int)(ptr - &obuf[i]);
>
> } else
> i++;
> break;
> }
>
> if (op != OSFOPT_EMPTY) {
> opt[*optnum].kind = IANA_opts[op].kind;
> opt[*optnum].length = IANA_opts[op].length;
> opt[*optnum].wc.wc = wc;
> opt[*optnum].wc.val = val;
> (*optnum)++;
> }
> }
> }
>
> static int ipt_osf_proc_read(char *buf, char **start, off_t off, int count,
> int *eof, void *data)
> {
> struct ipt_osf_finger *f = NULL;
> int i, __count, err;
>
> *eof = 1;
> __count = count;
> count = 0;
>
> rcu_read_lock();
> list_for_each_entry(f, &ipt_finger_list, flist) {
Does the above need to be list_for_each_entry_rcu()?
> log("%s [%s]", f->genre, f->details);
>
> err = snprintf(buf + count, __count - count, "%s - %s[%s] : %s",
> f->genre, f->version, f->subtype, f->details);
> if (err == 0 || __count <= count + err)
> break;
> else
> count += err;
> if (f->opt_num) {
> loga(" OPT: ");
> /* count += sprintf(buf+count, " OPT: "); */
> for (i = 0; i < f->opt_num; ++i) {
> /*
> count += sprintf(buf+count, "%d.%c%u; ",
> f->opt[i].kind, (f->opt[i].wc.wc)?f->opt[i].wc.wc:' ',
> f->opt[i].wc.val);
> */
> loga("%d.%c%u; ",
> f->opt[i].kind,
> (f->opt[i].wc.wc) ? f->opt[i].wc.wc : ' ',
> f->opt[i].wc.val);
> }
> }
> loga("\n");
> err = snprintf(buf + count, __count - count, "\n");
> if (err == 0 || __count <= count + err)
> break;
> else
> count += err;
> }
> rcu_read_unlock();
>
> return count;
> }
>
> static int ipt_osf_proc_write(struct file *file, const char *buffer,
> unsigned long count, void *data)
> {
> int cnt, i;
> char obuf[MAXOPTSTRLEN];
> struct ipt_osf_finger *finger, *n;
> char *pbeg, *pend;
>
> if (count > 0xffff)
> return -E2BIG;
>
> if (count == strlen(OSFFLUSH) && !strncmp(buffer, OSFFLUSH, strlen(OSFFLUSH))) {
> int i = 0;
> synchronize_rcu();
> spin_lock_bh(&ipt_osf_lock);
> list_for_each_entry_safe(finger, n, &ipt_finger_list, flist) {
This is OK -- we hold the update-side lock, so don't need _rcu.
> i++;
> list_del_rcu(&finger->flist);
> ipt_osf_finger_free(finger);
Why is it safe to immediately free the element that we just removed from
an RCU-protected list? The above synchronize_rcu() won't help us given
that it is still in the list at that point.
This could be fixed by using call_rcu() in ipt_osf_finger_free().
> }
> spin_unlock_bh(&ipt_osf_lock);
>
> log("Flushed %d entries.\n", i);
>
> return count;
> }
>
> cnt = 0;
> for (i = 0; i < count && buffer[i] != '\0'; ++i)
> if (buffer[i] == ':')
> cnt++;
>
> if (cnt != 8 || i != count) {
> log("Wrong input line cnt=%d[8], len=%u[%lu]\n",
> cnt, i, count);
> return count;
> }
>
> memset(obuf, 0, sizeof(obuf));
>
> finger = ipt_osf_finger_alloc();
> if (!finger) {
> log("Failed to allocate new fingerprint entry.\n");
> return -ENOMEM;
> }
>
> pbeg = (char *)buffer;
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> if (pbeg[0] == 'S') {
> finger->wss.wc = 'S';
> if (pbeg[1] == '%')
> finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
> else if (pbeg[1] == '*')
> finger->wss.val = 0;
> else
> finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> } else if (pbeg[0] == 'T') {
> finger->wss.wc = 'T';
> if (pbeg[1] == '%')
> finger->wss.val = simple_strtoul(&pbeg[2], NULL, 10);
> else if (pbeg[1] == '*')
> finger->wss.val = 0;
> else
> finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> } else if (pbeg[0] == '%') {
> finger->wss.wc = '%';
> finger->wss.val = simple_strtoul(&pbeg[1], NULL, 10);
> } else if (isdigit(pbeg[0])) {
> finger->wss.wc = 0;
> finger->wss.val = simple_strtoul(&pbeg[0], NULL, 10);
> }
>
> pbeg = pend + 1;
> }
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> finger->ttl = simple_strtoul(pbeg, NULL, 10);
> pbeg = pend + 1;
> }
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> finger->df = simple_strtoul(pbeg, NULL, 10);
> pbeg = pend + 1;
> }
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> finger->ss = simple_strtoul(pbeg, NULL, 10);
> pbeg = pend + 1;
> }
>
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> cnt = snprintf(obuf, sizeof(obuf), "%s", pbeg);
> pbeg = pend + 1;
> }
>
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> if (pbeg[0] == '@' || pbeg[0] == '*')
> cnt = snprintf(finger->genre, sizeof(finger->genre),
> "%s", pbeg + 1);
> else
> cnt = snprintf(finger->genre, sizeof(finger->genre),
> "%s", pbeg);
> pbeg = pend + 1;
> }
>
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> cnt =
> snprintf(finger->version, sizeof(finger->version), "%s",
> pbeg);
> pbeg = pend + 1;
> }
>
> pend = ipt_osf_strchr(pbeg, OSFPDEL);
> if (pend) {
> *pend = '\0';
> cnt =
> snprintf(finger->subtype, sizeof(finger->subtype), "%s",
> pbeg);
> pbeg = pend + 1;
> }
>
> cnt = snprintf(finger->details,
> ((count - (pbeg - buffer) + 1) >
> MAXDETLEN) ? MAXDETLEN : (count - (pbeg - buffer) + 1),
> "%s", pbeg);
>
> log("%s - %s[%s] : %s\n",
> finger->genre, finger->version, finger->subtype, finger->details);
>
> ipt_osf_parse_opt(finger->opt, &finger->opt_num, obuf, sizeof(obuf));
>
> synchronize_rcu();
I don't understand what this synchronize_rcu() is doing for us.
Is the idea to make sure that all RCU readers see the prior deletes as
having happened before we do the below addition? If so, please add a
comment to this effect.
> spin_lock_bh(&ipt_osf_lock);
> list_add_tail_rcu(&finger->flist, &ipt_finger_list);
> spin_unlock_bh(&ipt_osf_lock);
>
> return count;
> }
>
> static inline int ipt_osf_ttl(const struct sk_buff *skb, struct ipt_osf_info *info,
> unsigned char f_ttl)
> {
> struct iphdr *ip = ip_hdr(skb);
> #if 0
> log("f_ttl: %u, ip_ttl: %u, info->ttl: %u, flags_ttl: %u.\n",
> f_ttl, ip->ttl, info->ttl, info->flags & IPT_OSF_TTL);
> #endif
> if (info->flags & IPT_OSF_TTL) {
> if (info->ttl == IPT_OSF_TTL_TRUE)
> return (ip->ttl == f_ttl);
> if (info->ttl == IPT_OSF_TTL_NOCHECK)
> return 1;
> else {
> struct in_device *in_dev = in_dev_get(skb->dev);
>
> for_ifa(in_dev) {
> if (inet_ifa_match(ip->saddr, ifa)) {
> in_dev_put(in_dev);
> return (ip->ttl == f_ttl);
> }
> }
> endfor_ifa(in_dev);
>
> in_dev_put(in_dev);
> return (ip->ttl <= f_ttl);
> }
> }
>
> return (ip->ttl == f_ttl);
> }
>
> static bool
> ipt_osf_match_packet(const struct sk_buff *skb,
> const struct net_device *in,
> const struct net_device *out,
> const struct xt_match *match,
> const void *matchinfo,
> int offset,
> unsigned int unused,
> bool *hotdrop)
> {
> struct ipt_osf_info *info = (struct ipt_osf_info *)matchinfo;
> struct iphdr _iph, *ip;
> struct tcphdr _tcph, *tcp;
> int fmatch = FMATCH_WRONG, fcount = 0;
> unsigned int optsize = 0, check_WSS = 0;
> u16 window, totlen, mss = 0;
> unsigned char df, *optp = NULL, *_optp = NULL;
> unsigned char opts[MAX_IPOPTLEN];
> struct ipt_osf_finger *f;
>
> if (!info)
> return 0;
>
> ip = skb_header_pointer(skb, 0, sizeof(struct iphdr), &_iph);
> if (!ip)
> return 0;
>
> tcp = skb_header_pointer(skb, ip->ihl * 4, sizeof(struct tcphdr), &_tcph);
> if (!tcp)
> return 0;
>
> if (!tcp->syn)
> return 0;
>
> totlen = ntohs(ip->tot_len);
> df = ((ntohs(ip->frag_off) & IP_DF) ? 1 : 0);
> window = ntohs(tcp->window);
>
> if (tcp->doff * 4 > sizeof(struct tcphdr)) {
> optsize = tcp->doff * 4 - sizeof(struct tcphdr);
>
> if (optsize > sizeof(opts)) {
> log("%s: BUG: too big options size: optsize=%u, max=%zu.\n",
> __func__, optsize, sizeof(opts));
> optsize = sizeof(opts);
> }
>
> _optp = optp = skb_header_pointer(skb, ip->ihl * 4 + sizeof(struct tcphdr),
> optsize, opts);
> }
>
> rcu_read_lock();
> list_for_each_entry_rcu(f, &ipt_finger_list, flist) {
> if (!(info->flags & IPT_OSF_LOG) && strcmp(info->genre, f->genre))
> continue;
>
> optp = _optp;
> fmatch = FMATCH_WRONG;
>
> if (totlen == f->ss && df == f->df && ipt_osf_ttl(skb, info, f->ttl)) {
> int foptsize, optnum;
>
> check_WSS = 0;
>
> switch (f->wss.wc) {
> case 0:
> check_WSS = 0;
> break;
> case 'S':
> check_WSS = 1;
> break;
> case 'T':
> check_WSS = 2;
> break;
> case '%':
> check_WSS = 3;
> break;
> default:
> log("Wrong fingerprint wss.wc=%d, %s - %s\n",
> f->wss.wc, f->genre, f->details);
> check_WSS = 4;
> break;
> }
> if (check_WSS == 4)
> continue;
>
> /* Check options */
>
> foptsize = 0;
> for (optnum = 0; optnum < f->opt_num; ++optnum)
> foptsize += f->opt[optnum].length;
> #if 0
> log("%s.%s.%s: optsize: %u, foptsize: %u, fopt_num: %u, optp: %p, win: %u, mss: %u, totlen: %u, df: %d, ttl: %u.\n",
> f->genre, f->version, f->subtype, optsize, foptsize, f->opt_num, optp,
> window, mss, totlen, df, ip->ttl);
> #endif
> if (foptsize > MAX_IPOPTLEN || optsize > MAX_IPOPTLEN || optsize != foptsize)
> continue;
>
> for (optnum = 0; optnum < f->opt_num; ++optnum) {
> if (f->opt[optnum].kind == (*optp)) {
> __u32 len = f->opt[optnum].length;
> __u8 *optend = optp + len;
> int loop_cont = 0;
>
> fmatch = FMATCH_OK;
>
> switch (*optp) {
> case OSFOPT_MSS:
> mss = ntohs(*(u16 *)(optp + 2));
> break;
> case OSFOPT_TS:
> loop_cont = 1;
> break;
> }
>
> #if 0
> if (loop_cont) {
> optp = optend;
> continue;
> }
> if (len != 1) {
> /* Skip kind and length fields */
> optp += 2;
>
> if (f->opt[optnum].wc.val != 0) {
> u32 tmp = 0;
> u32 copy = len > 4 ? 4 : len;
>
> /* Hmmm... It looks a bit ugly. :) */
> memcpy(&tmp, optp, copy);
>
> /* 2 + 2: optlen(2 bytes) +
> * kind(1 byte) + length(1 byte) */
> if (len == 4)
> tmp = ntohs(tmp);
> else
> tmp = ntohl(tmp);
>
> if (f->opt[optnum].wc.wc == '%') {
> if ((tmp % f->opt[optnum].wc.val) != 0)
> fmatch = FMATCH_OPT_WRONG;
> } else if (tmp != f->opt[optnum].wc.val)
> fmatch = FMATCH_OPT_WRONG;
> }
> }
> #endif
> optp = optend;
> } else
> fmatch = FMATCH_OPT_WRONG;
>
> if (fmatch != FMATCH_OK)
> break;
> }
>
> if (fmatch != FMATCH_OPT_WRONG) {
> fmatch = FMATCH_WRONG;
>
> switch (check_WSS) {
> case 0:
> if (f->wss.val == 0 || window == f->wss.val)
> fmatch = FMATCH_OK;
> break;
> case 1: /* MSS */
> #define SMART_MSS_1 1460
> #define SMART_MSS_2 1448
> if (window == f->wss.val * mss ||
> window == f->wss.val * SMART_MSS_1 ||
> window == f->wss.val * SMART_MSS_2)
> fmatch = FMATCH_OK;
> break;
> case 2: /* MTU */
> if (window == f->wss.val * (mss + 40) ||
> window == f->wss.val * (SMART_MSS_1 + 40) ||
> window == f->wss.val * (SMART_MSS_2 + 40))
> fmatch = FMATCH_OK;
> break;
> case 3: /* MOD */
> if ((window % f->wss.val) == 0)
> fmatch = FMATCH_OK;
> break;
> }
> }
>
> if (fmatch == FMATCH_OK) {
> fcount++;
> log("%s [%s:%s:%s] : %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u hops=%d\n",
> f->genre, f->version, f->subtype, f->details,
> NIPQUAD(ip->saddr), ntohs(tcp->source),
> NIPQUAD(ip->daddr), ntohs(tcp->dest),
> f->ttl - ip->ttl);
> if (info->flags & IPT_OSF_CONNECTOR)
> ipt_osf_send_connector(f, skb);
>
> if ((info->flags & IPT_OSF_LOG) &&
> info->loglevel == IPT_OSF_LOGLEVEL_FIRST)
> break;
> }
> }
> }
> rcu_read_unlock();
>
> if (!fcount && (info->flags & (IPT_OSF_LOG | IPT_OSF_NETLINK | IPT_OSF_CONNECTOR))) {
> unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
> unsigned int i, optsize;
> struct ipt_osf_finger fg;
>
> memset(&fg, 0, sizeof(fg));
> #if 1
> if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
> log("Unknown: win: %u, mss: %u, totlen: %u, df: %d, ttl: %u : ", window, mss, totlen, df, ip->ttl);
> if (optp) {
> optsize = tcp->doff * 4 - sizeof(struct tcphdr);
> if (skb_copy_bits(skb, ip->ihl * 4 + sizeof(struct tcphdr),
> opt, optsize) < 0) {
> if (info->flags & IPT_OSF_LOG)
> loga("TRUNCATED");
> if (info->flags & IPT_OSF_NETLINK)
> strcpy(fg.details, "TRUNCATED");
> } else {
> for (i = 0; i < optsize; i++) {
> if (info->flags & IPT_OSF_LOG)
> loga("%02X ", opt[i]);
> }
> if (info->flags & IPT_OSF_NETLINK)
> memcpy(fg.details, opt, min_t(unsigned int, optsize, MAXDETLEN));
> }
> }
> if ((info->flags & IPT_OSF_LOG) && (info->loglevel != IPT_OSF_LOGLEVEL_ALL_KNOWN))
> loga(" %u.%u.%u.%u:%u -> %u.%u.%u.%u:%u\n",
> NIPQUAD(ip->saddr), ntohs(tcp->source),
> NIPQUAD(ip->daddr), ntohs(tcp->dest));
> #endif
> if (info->flags & (IPT_OSF_NETLINK | IPT_OSF_CONNECTOR)) {
> fg.wss.val = window;
> fg.ttl = ip->ttl;
> fg.df = df;
> fg.ss = totlen;
> fg.mss = mss;
> strncpy(fg.genre, "Unknown", MAXGENRELEN);
>
> if (info->flags & IPT_OSF_CONNECTOR)
> ipt_osf_send_connector(&fg, skb);
> }
> }
>
> if (fcount)
> fmatch = FMATCH_OK;
>
> return (fmatch == FMATCH_OK) ? 1 : 0;
> }
>
> static bool
> ipt_osf_checkentry(const char *tablename,
> const void *data,
> const struct xt_match *match,
> void *matchinfo,
> unsigned int hook_mask)
> {
> struct ipt_ip *ip = (struct ipt_ip *)data;
>
> if (ip->proto != IPPROTO_TCP)
> return false;
>
> return true;
> }
>
> static struct xt_match ipt_osf_match = {
> .name = "osf",
> .revision = 0,
> .family = AF_INET,
> .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_PRE_ROUTING),
> .match = ipt_osf_match_packet,
> .checkentry = ipt_osf_checkentry,
> .matchsize = sizeof(struct ipt_osf_info),
> .me = THIS_MODULE,
> };
>
> static int __devinit ipt_osf_init(void)
> {
> int err = -EINVAL;
> struct proc_dir_entry *p;
>
> log("Startng OS fingerprint matching module.\n");
>
> p = create_proc_entry("osf", S_IFREG | 0644, proc_net_netfilter);
> if (!p)
> goto err_out_exit;
>
> p->write_proc = ipt_osf_proc_write;
> p->read_proc = ipt_osf_proc_read;
>
> err = xt_register_match(&ipt_osf_match);
> if (err) {
> log("Failed to register OS fingerprint matching module.\n");
> goto err_out_remove;
> }
>
> return 0;
>
> err_out_remove:
> remove_proc_entry("osf", proc_net_netfilter);
> err_out_exit:
> return err;
> }
>
> static void __devexit ipt_osf_fini(void)
> {
> struct ipt_osf_finger *f, *n;
>
> remove_proc_entry("osf", proc_net_netfilter);
> xt_unregister_match(&ipt_osf_match);
>
> list_for_each_entry_safe(f, n, &ipt_finger_list, flist) {
Why is it safe to do the above without being in an RCU read-side critical
section? Have all possible RCU readers somehow been banished? If so,
how have they been banished?
> list_del(&f->flist);
Ditto for why the list_del() is safe without the update-side lock and
why it doesn't have to be list_del_rcu().
> ipt_osf_finger_free(f);
> }
>
> log("OS fingerprint matching module finished.\n");
> }
>
> module_init(ipt_osf_init);
> module_exit(ipt_osf_fini);
>
> MODULE_LICENSE("GPL");
> MODULE_AUTHOR("Evgeniy Polyakov <johnpol@....mipt.ru>");
> MODULE_DESCRIPTION("Passive OS fingerprint matching.");
> /*
> * ipt_osf.h
> *
> * Copyright (c) 2003 Evgeniy Polyakov <johnpol@....mipt.ru>
> *
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> * the Free Software Foundation; either version 2 of the License, or
> * (at your option) any later version.
> *
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> * along with this program; if not, write to the Free Software
> * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> */
>
> #ifndef _IPT_OSF_H
> #define _IPT_OSF_H
>
> #define MAXGENRELEN 32
> #define MAXDETLEN 64
>
> #define IPT_OSF_GENRE 1
> #define IPT_OSF_TTL 2
> #define IPT_OSF_LOG 4
> #define IPT_OSF_NETLINK 8
> #define IPT_OSF_CONNECTOR 16
> #define IPT_OSF_INVERT 32
>
> #define IPT_OSF_LOGLEVEL_ALL 0
> #define IPT_OSF_LOGLEVEL_FIRST 1
> #define IPT_OSF_LOGLEVEL_ALL_KNOWN 2
>
> #define IPT_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */
> #define IPT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */
> #define IPT_OSF_TTL_NOCHECK 2 /* Do not compare ip and fingerprint TTL at all */
>
> #ifndef __KERNEL__
> #include <netinet/ip.h>
> #include <netinet/tcp.h>
>
> struct list_head {
> struct list_head *prev, *next;
> };
> #endif
>
> struct ipt_osf_info {
> char genre[MAXGENRELEN];
> __u32 len, flags, loglevel, ttl;
> } __attribute__ ((packed));
>
> struct ipt_osf_wc {
> __u8 wc;
> __u32 val;
> } __attribute__ ((packed));
>
> /* This struct represents IANA options
> * http://www.iana.org/assignments/tcp-parameters
> */
> struct ipt_osf_opt {
> __u8 kind, length;
> struct ipt_osf_wc wc;
> } __attribute__ ((packed));
>
> struct ipt_osf_finger {
> struct list_head flist;
> struct ipt_osf_wc wss;
> __u8 ttl, df;
> __u16 ss, mss;
> unsigned char genre[MAXGENRELEN];
> unsigned char version[MAXGENRELEN], subtype[MAXGENRELEN];
>
> /* Not needed, but for consistency with original table from Michal Zalewski */
> unsigned char details[MAXDETLEN];
>
> int opt_num;
> struct ipt_osf_opt opt[MAX_IPOPTLEN]; /* In case it is all NOP or EOL */
>
> } __attribute__ ((packed));
>
> struct ipt_osf_nlmsg {
> struct ipt_osf_finger f;
> struct iphdr ip;
> struct tcphdr tcp;
> } __attribute__ ((packed));
>
> #ifdef __KERNEL__
>
> #include <linux/list.h>
> #include <net/tcp.h>
>
> /* Defines for IANA option kinds */
>
> #define OSFOPT_EOL 0 /* End of options */
> #define OSFOPT_NOP 1 /* NOP */
> #define OSFOPT_MSS 2 /* Maximum segment size */
> #define OSFOPT_WSO 3 /* Window scale option */
> #define OSFOPT_SACKP 4 /* SACK permitted */
> #define OSFOPT_SACK 5 /* SACK */
> #define OSFOPT_ECHO 6
> #define OSFOPT_ECHOREPLY 7
> #define OSFOPT_TS 8 /* Timestamp option */
> #define OSFOPT_POCP 9 /* Partial Order Connection Permitted */
> #define OSFOPT_POSP 10 /* Partial Order Service Profile */
> #define OSFOPT_EMPTY 255
> /* Others are not used in current OSF */
>
> static struct ipt_osf_opt IANA_opts[] = {
> {0, 1,},
> {1, 1,},
> {2, 4,},
> {3, 3,},
> {4, 2,},
> {5, 1,}, /* SACK length is not defined */
> {6, 6,},
> {7, 6,},
> {8, 10,},
> {9, 2,},
> {10, 3,},
> {11, 1,}, /* CC: Suppose 1 */
> {12, 1,}, /* the same */
> {13, 1,}, /* and here too */
> {14, 3,},
> {15, 1,}, /* TCP Alternate Checksum Data. Length is not defined */
> {16, 1,},
> {17, 1,},
> {18, 3,},
> {19, 18,},
> {20, 1,},
> {21, 1,},
> {22, 1,},
> {23, 1,},
> {24, 1,},
> {25, 1,},
> {26, 1,},
> };
>
> #endif /* __KERNEL__ */
>
> #endif /* _IPT_OSF_H */
> /*
> * libipt_osf.c
> *
> * Copyright (c) 2003-2006 Evgeniy Polyakov <johnpol@....mipt.ru>
> *
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> * the Free Software Foundation; either version 2 of the License, or
> * (at your option) any later version.
> *
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> * along with this program; if not, write to the Free Software
> * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> */
>
> /*
> * iptables interface for OS fingerprint matching module.
> */
>
> #include <stdio.h>
> #include <netdb.h>
> #include <string.h>
> #include <stdlib.h>
> #include <getopt.h>
> #include <ctype.h>
>
> #include <iptables.h>
>
> typedef unsigned int __u32;
> typedef unsigned short __u16;
> typedef unsigned char __u8;
>
> #include "ipt_osf.h"
>
> static void osf_help(void)
> {
> printf("OS fingerprint match options:\n"
> "--genre [!] string Match a OS genre by passive fingerprinting.\n"
> "--ttl Use some TTL check extensions to determine OS:\n"
> " 0 true ip and fingerprint TTL comparison. Works for LAN.\n"
> " 1 check if ip TTL is less than fingerprint one. Works for global addresses.\n"
> " 2 do not compare TTL at all. Allows to detect NMAP, but can produce false results.\n"
> "--log level Log determined genres into dmesg even if they do not match desired one:\n"
> " 0 log all matched or unknown signatures.\n"
> " 1 log only first one.\n"
> " 2 log all known matched signatures.\n"
> "--connector Log through kernel connector [2.6.14+].\n\n"
> );
> }
>
>
> static const struct option osf_opts[] = {
> { .name = "genre", .has_arg = 1, .flag = 0, .val = '1' },
> { .name = "ttl", .has_arg = 1, .flag = 0, .val = '2' },
> { .name = "log", .has_arg = 1, .flag = 0, .val = '3' },
> { .name = "netlink", .has_arg = 0, .flag = 0, .val = '4' },
> { .name = "connector", .has_arg = 0, .flag = 0, .val = '5' },
> { .name = NULL }
> };
>
>
> static void osf_init(struct xt_entry_match *m)
> {
> }
>
> static void osf_parse_string(const unsigned char *s, struct ipt_osf_info *info)
> {
> if (strlen(s) < MAXGENRELEN)
> strcpy(info->genre, s);
> else
> exit_error(PARAMETER_PROBLEM, "Genre string too long `%s' [%d], max=%d",
> s, strlen(s), MAXGENRELEN);
> }
>
> static int osf_parse(int c, char **argv, int invert, unsigned int *flags,
> const void *entry,
> struct xt_entry_match **match)
> {
> struct ipt_osf_info *info = (struct ipt_osf_info *)(*match)->data;
>
> switch(c)
> {
> case '1': /* --genre */
> if (*flags & IPT_OSF_GENRE)
> exit_error(PARAMETER_PROBLEM, "Can't specify multiple genre parameter");
> check_inverse(optarg, &invert, &optind, 0);
> osf_parse_string(argv[optind-1], info);
> if (invert)
> info->flags |= IPT_OSF_INVERT;
> info->len=strlen((char *)info->genre);
> *flags |= IPT_OSF_GENRE;
> break;
> case '2': /* --ttl */
> if (*flags & IPT_OSF_TTL)
> exit_error(PARAMETER_PROBLEM, "Can't specify multiple ttl parameter");
> *flags |= IPT_OSF_TTL;
> info->flags |= IPT_OSF_TTL;
> info->ttl = atoi(argv[optind-1]);
> break;
> case '3': /* --log */
> if (*flags & IPT_OSF_LOG)
> exit_error(PARAMETER_PROBLEM, "Can't specify multiple log parameter");
> *flags |= IPT_OSF_LOG;
> info->loglevel = atoi(argv[optind-1]);
> info->flags |= IPT_OSF_LOG;
> break;
> case '4': /* --netlink */
> if (*flags & IPT_OSF_NETLINK)
> exit_error(PARAMETER_PROBLEM, "Can't specify multiple netlink parameter");
> *flags |= IPT_OSF_NETLINK;
> info->flags |= IPT_OSF_NETLINK;
> break;
> case '5': /* --connector */
> if (*flags & IPT_OSF_CONNECTOR)
> exit_error(PARAMETER_PROBLEM, "Can't specify multiple connector parameter");
> *flags |= IPT_OSF_CONNECTOR;
> info->flags |= IPT_OSF_CONNECTOR;
> break;
> default:
> return 0;
> }
>
> return 1;
> }
>
> static void osf_final_check(unsigned int flags)
> {
> if (!flags)
> exit_error(PARAMETER_PROBLEM, "OS fingerprint match: You must specify `--genre'");
> }
>
> static void osf_print(const void *ip, const struct xt_entry_match *match, int numeric)
> {
> const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
>
> printf("OS fingerprint match %s%s ", (info->flags & IPT_OSF_INVERT) ? "!" : "", info->genre);
> }
>
> static void osf_save(const void *ip, const struct xt_entry_match *match)
> {
> const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
>
> printf("--genre %s%s ", (info->flags & IPT_OSF_INVERT) ? "! ": "", info->genre);
> }
>
>
> static struct xtables_match osf_match = {
> .name = "osf",
> .version = XTABLES_VERSION,
> .size = XT_ALIGN(sizeof(struct ipt_osf_info)),
> .userspacesize = XT_ALIGN(sizeof(struct ipt_osf_info)),
> .help = &osf_help,
> .init = &osf_init,
> .parse = &osf_parse,
> .print = &osf_print,
> .final_check = &osf_final_check,
> .save = &osf_save,
> .extra_opts = osf_opts
> };
>
>
> void _init(void)
> {
> xtables_register_match(&osf_match);
> }
> obj-m := ipt_osf.o
>
> KDIR := /lib/modules/$(shell uname -r)/build
> IPTABLES:= /usr/include
>
> ifeq ($(IPTABLES),path_to_iptables_sources_or_header_files)
> $(error "Edit IPTABLES variable in Makefile and read README")
> endif
> PWD := $(shell pwd)
>
> iptables_version=$(shell (/sbin/iptables -V | awk {'print $$2'} | cut -c 2-))
> LCFLAGS = -DIPTABLES_VERSION=\"$(iptables_version)\"
>
> default:
> $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
>
> clean:
> rm -f *.o *.ko *.mod.* .*.cmd *~
> rm -rf .tmp_versions
> rm -f *.o *~ *.so *.ko load osfd ucon_osf
>
> lib: libipt_osf.c ipt_osf.h
> gcc $(LCFLAGS) libipt_osf.c -D_INIT=_init -fPIC -I$(IPTABLES)/include -c -o libipt_osf.o
> gcc -shared -nostdlib -rdynamic -Wl,-soname,libipt_osf.so -o libipt_osf.so libipt_osf.o
>
> bin: osfd.c load.c ucon_osf.c
> gcc -W -Wall osfd.c -o osfd
> gcc -W -Wall load.c -o load
> gcc -W -Wall ucon_osf.c -o ucon_osf
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists