alistair23-linux/net/netfilter/xt_osf.c
Jeff Kirsher e664eabd18 netfilter: Fix FSF address in file headers
Several files refer to an old address for the Free Software Foundation
in the file header comment.  Resolve by replacing the address with
the URL <http://www.gnu.org/licenses/> so that we do not have to keep
updating the header comments anytime the address changes.

CC: netfilter@vger.kernel.org
CC: Pablo Neira Ayuso <pablo@netfilter.org>
CC: Patrick McHardy <kaber@trash.net>
CC: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2013-12-06 12:37:57 -05:00

426 lines
9.8 KiB
C

/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <linux/ip.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_log.h>
#include <linux/netfilter/xt_osf.h>
struct xt_osf_finger {
struct rcu_head rcu_head;
struct list_head finger_entry;
struct xt_osf_user_finger finger;
};
enum osf_fmatch_states {
/* Packet does not match the fingerprint */
FMATCH_WRONG = 0,
/* Packet matches the fingerprint */
FMATCH_OK,
/* Options do not match the fingerprint, but header does */
FMATCH_OPT_WRONG,
};
/*
* Indexed by dont-fragment bit.
* It is the only constant value in the fingerprint.
*/
static struct list_head xt_osf_fingers[2];
static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
[OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) },
};
static int xt_osf_add_callback(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[])
{
struct xt_osf_user_finger *f;
struct xt_osf_finger *kf = NULL, *sf;
int err = 0;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
if (!(nlh->nlmsg_flags & NLM_F_CREATE))
return -EINVAL;
f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
if (!kf)
return -ENOMEM;
memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
continue;
kfree(kf);
kf = NULL;
if (nlh->nlmsg_flags & NLM_F_EXCL)
err = -EEXIST;
break;
}
/*
* We are protected by nfnl mutex.
*/
if (kf)
list_add_tail_rcu(&kf->finger_entry, &xt_osf_fingers[!!f->df]);
return err;
}
static int xt_osf_remove_callback(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[])
{
struct xt_osf_user_finger *f;
struct xt_osf_finger *sf;
int err = -ENOENT;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
continue;
/*
* We are protected by nfnl mutex.
*/
list_del_rcu(&sf->finger_entry);
kfree_rcu(sf, rcu_head);
err = 0;
break;
}
return err;
}
static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
[OSF_MSG_ADD] = {
.call = xt_osf_add_callback,
.attr_count = OSF_ATTR_MAX,
.policy = xt_osf_policy,
},
[OSF_MSG_REMOVE] = {
.call = xt_osf_remove_callback,
.attr_count = OSF_ATTR_MAX,
.policy = xt_osf_policy,
},
};
static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
.name = "osf",
.subsys_id = NFNL_SUBSYS_OSF,
.cb_count = OSF_MSG_MAX,
.cb = xt_osf_nfnetlink_callbacks,
};
static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info,
unsigned char f_ttl)
{
const struct iphdr *ip = ip_hdr(skb);
if (info->flags & XT_OSF_TTL) {
if (info->ttl == XT_OSF_TTL_TRUE)
return ip->ttl == f_ttl;
if (info->ttl == XT_OSF_TTL_NOCHECK)
return 1;
else if (ip->ttl <= f_ttl)
return 1;
else {
struct in_device *in_dev = __in_dev_get_rcu(skb->dev);
int ret = 0;
for_ifa(in_dev) {
if (inet_ifa_match(ip->saddr, ifa)) {
ret = (ip->ttl == f_ttl);
break;
}
}
endfor_ifa(in_dev);
return ret;
}
}
return ip->ttl == f_ttl;
}
static bool
xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
{
const struct xt_osf_info *info = p->matchinfo;
const struct iphdr *ip = ip_hdr(skb);
const struct tcphdr *tcp;
struct tcphdr _tcph;
int fmatch = FMATCH_WRONG, fcount = 0;
unsigned int optsize = 0, check_WSS = 0;
u16 window, totlen, mss = 0;
bool df;
const unsigned char *optp = NULL, *_optp = NULL;
unsigned char opts[MAX_IPOPTLEN];
const struct xt_osf_finger *kf;
const struct xt_osf_user_finger *f;
struct net *net = dev_net(p->in ? p->in : p->out);
if (!info)
return false;
tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph);
if (!tcp)
return false;
if (!tcp->syn)
return false;
totlen = ntohs(ip->tot_len);
df = ntohs(ip->frag_off) & IP_DF;
window = ntohs(tcp->window);
if (tcp->doff * 4 > sizeof(struct tcphdr)) {
optsize = tcp->doff * 4 - sizeof(struct tcphdr);
_optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) +
sizeof(struct tcphdr), optsize, opts);
}
rcu_read_lock();
list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) {
f = &kf->finger;
if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre))
continue;
optp = _optp;
fmatch = FMATCH_WRONG;
if (totlen == f->ss && xt_osf_ttl(skb, info, f->ttl)) {
int foptsize, optnum;
/*
* Should not happen if userspace parser was written correctly.
*/
if (f->wss.wc >= OSF_WSS_MAX)
continue;
/* Check options */
foptsize = 0;
for (optnum = 0; optnum < f->opt_num; ++optnum)
foptsize += f->opt[optnum].length;
if (foptsize > MAX_IPOPTLEN ||
optsize > MAX_IPOPTLEN ||
optsize != foptsize)
continue;
check_WSS = f->wss.wc;
for (optnum = 0; optnum < f->opt_num; ++optnum) {
if (f->opt[optnum].kind == (*optp)) {
__u32 len = f->opt[optnum].length;
const __u8 *optend = optp + len;
int loop_cont = 0;
fmatch = FMATCH_OK;
switch (*optp) {
case OSFOPT_MSS:
mss = optp[3];
mss <<= 8;
mss |= optp[2];
mss = ntohs((__force __be16)mss);
break;
case OSFOPT_TS:
loop_cont = 1;
break;
}
optp = optend;
} else
fmatch = FMATCH_OPT_WRONG;
if (fmatch != FMATCH_OK)
break;
}
if (fmatch != FMATCH_OPT_WRONG) {
fmatch = FMATCH_WRONG;
switch (check_WSS) {
case OSF_WSS_PLAIN:
if (f->wss.val == 0 || window == f->wss.val)
fmatch = FMATCH_OK;
break;
case OSF_WSS_MSS:
/*
* Some smart modems decrease mangle MSS to
* SMART_MSS_2, so we check standard, decreased
* and the one provided in the fingerprint MSS
* values.
*/
#define SMART_MSS_1 1460
#define SMART_MSS_2 1448
if (window == f->wss.val * mss ||
window == f->wss.val * SMART_MSS_1 ||
window == f->wss.val * SMART_MSS_2)
fmatch = FMATCH_OK;
break;
case OSF_WSS_MTU:
if (window == f->wss.val * (mss + 40) ||
window == f->wss.val * (SMART_MSS_1 + 40) ||
window == f->wss.val * (SMART_MSS_2 + 40))
fmatch = FMATCH_OK;
break;
case OSF_WSS_MODULO:
if ((window % f->wss.val) == 0)
fmatch = FMATCH_OK;
break;
}
}
if (fmatch != FMATCH_OK)
continue;
fcount++;
if (info->flags & XT_OSF_LOG)
nf_log_packet(net, p->family, p->hooknum, skb,
p->in, p->out, NULL,
"%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n",
f->genre, f->version, f->subtype,
&ip->saddr, ntohs(tcp->source),
&ip->daddr, ntohs(tcp->dest),
f->ttl - ip->ttl);
if ((info->flags & XT_OSF_LOG) &&
info->loglevel == XT_OSF_LOGLEVEL_FIRST)
break;
}
}
rcu_read_unlock();
if (!fcount && (info->flags & XT_OSF_LOG))
nf_log_packet(net, p->family, p->hooknum, skb, p->in,
p->out, NULL,
"Remote OS is not known: %pI4:%u -> %pI4:%u\n",
&ip->saddr, ntohs(tcp->source),
&ip->daddr, ntohs(tcp->dest));
if (fcount)
fmatch = FMATCH_OK;
return fmatch == FMATCH_OK;
}
static struct xt_match xt_osf_match = {
.name = "osf",
.revision = 0,
.family = NFPROTO_IPV4,
.proto = IPPROTO_TCP,
.hooks = (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_FORWARD),
.match = xt_osf_match_packet,
.matchsize = sizeof(struct xt_osf_info),
.me = THIS_MODULE,
};
static int __init xt_osf_init(void)
{
int err = -EINVAL;
int i;
for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i)
INIT_LIST_HEAD(&xt_osf_fingers[i]);
err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
if (err < 0) {
pr_err("Failed to register OSF nsfnetlink helper (%d)\n", err);
goto err_out_exit;
}
err = xt_register_match(&xt_osf_match);
if (err) {
pr_err("Failed to register OS fingerprint "
"matching module (%d)\n", err);
goto err_out_remove;
}
return 0;
err_out_remove:
nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
err_out_exit:
return err;
}
static void __exit xt_osf_fini(void)
{
struct xt_osf_finger *f;
int i;
nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
xt_unregister_match(&xt_osf_match);
rcu_read_lock();
for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) {
list_del_rcu(&f->finger_entry);
kfree_rcu(f, rcu_head);
}
}
rcu_read_unlock();
rcu_barrier();
}
module_init(xt_osf_init);
module_exit(xt_osf_fini);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
MODULE_DESCRIPTION("Passive OS fingerprint matching.");
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_OSF);