Commit adf76cfe authored by David S. Miller's avatar David S. Miller
parents 17d0cdfa 24992eac
...@@ -33,6 +33,7 @@ header-y += xt_limit.h ...@@ -33,6 +33,7 @@ header-y += xt_limit.h
header-y += xt_mac.h header-y += xt_mac.h
header-y += xt_mark.h header-y += xt_mark.h
header-y += xt_multiport.h header-y += xt_multiport.h
header-y += xt_osf.h
header-y += xt_owner.h header-y += xt_owner.h
header-y += xt_pkttype.h header-y += xt_pkttype.h
header-y += xt_quota.h header-y += xt_quota.h
......
...@@ -75,75 +75,6 @@ enum ip_conntrack_status { ...@@ -75,75 +75,6 @@ enum ip_conntrack_status {
IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
}; };
/* Connection tracking event bits */
enum ip_conntrack_events
{
/* New conntrack */
IPCT_NEW_BIT = 0,
IPCT_NEW = (1 << IPCT_NEW_BIT),
/* Expected connection */
IPCT_RELATED_BIT = 1,
IPCT_RELATED = (1 << IPCT_RELATED_BIT),
/* Destroyed conntrack */
IPCT_DESTROY_BIT = 2,
IPCT_DESTROY = (1 << IPCT_DESTROY_BIT),
/* Timer has been refreshed */
IPCT_REFRESH_BIT = 3,
IPCT_REFRESH = (1 << IPCT_REFRESH_BIT),
/* Status has changed */
IPCT_STATUS_BIT = 4,
IPCT_STATUS = (1 << IPCT_STATUS_BIT),
/* Update of protocol info */
IPCT_PROTOINFO_BIT = 5,
IPCT_PROTOINFO = (1 << IPCT_PROTOINFO_BIT),
/* Volatile protocol info */
IPCT_PROTOINFO_VOLATILE_BIT = 6,
IPCT_PROTOINFO_VOLATILE = (1 << IPCT_PROTOINFO_VOLATILE_BIT),
/* New helper for conntrack */
IPCT_HELPER_BIT = 7,
IPCT_HELPER = (1 << IPCT_HELPER_BIT),
/* Update of helper info */
IPCT_HELPINFO_BIT = 8,
IPCT_HELPINFO = (1 << IPCT_HELPINFO_BIT),
/* Volatile helper info */
IPCT_HELPINFO_VOLATILE_BIT = 9,
IPCT_HELPINFO_VOLATILE = (1 << IPCT_HELPINFO_VOLATILE_BIT),
/* NAT info */
IPCT_NATINFO_BIT = 10,
IPCT_NATINFO = (1 << IPCT_NATINFO_BIT),
/* Counter highest bit has been set, unused */
IPCT_COUNTER_FILLING_BIT = 11,
IPCT_COUNTER_FILLING = (1 << IPCT_COUNTER_FILLING_BIT),
/* Mark is set */
IPCT_MARK_BIT = 12,
IPCT_MARK = (1 << IPCT_MARK_BIT),
/* NAT sequence adjustment */
IPCT_NATSEQADJ_BIT = 13,
IPCT_NATSEQADJ = (1 << IPCT_NATSEQADJ_BIT),
/* Secmark is set */
IPCT_SECMARK_BIT = 14,
IPCT_SECMARK = (1 << IPCT_SECMARK_BIT),
};
enum ip_conntrack_expect_events {
IPEXP_NEW_BIT = 0,
IPEXP_NEW = (1 << IPEXP_NEW_BIT),
};
#ifdef __KERNEL__ #ifdef __KERNEL__
struct ip_conntrack_stat struct ip_conntrack_stat
{ {
......
...@@ -15,7 +15,8 @@ enum tcp_conntrack { ...@@ -15,7 +15,8 @@ enum tcp_conntrack {
TCP_CONNTRACK_LAST_ACK, TCP_CONNTRACK_LAST_ACK,
TCP_CONNTRACK_TIME_WAIT, TCP_CONNTRACK_TIME_WAIT,
TCP_CONNTRACK_CLOSE, TCP_CONNTRACK_CLOSE,
TCP_CONNTRACK_LISTEN, TCP_CONNTRACK_LISTEN, /* obsolete */
#define TCP_CONNTRACK_SYN_SENT2 TCP_CONNTRACK_LISTEN
TCP_CONNTRACK_MAX, TCP_CONNTRACK_MAX,
TCP_CONNTRACK_IGNORE TCP_CONNTRACK_IGNORE
}; };
......
...@@ -46,7 +46,8 @@ struct nfgenmsg { ...@@ -46,7 +46,8 @@ struct nfgenmsg {
#define NFNL_SUBSYS_CTNETLINK_EXP 2 #define NFNL_SUBSYS_CTNETLINK_EXP 2
#define NFNL_SUBSYS_QUEUE 3 #define NFNL_SUBSYS_QUEUE 3
#define NFNL_SUBSYS_ULOG 4 #define NFNL_SUBSYS_ULOG 4
#define NFNL_SUBSYS_COUNT 5 #define NFNL_SUBSYS_OSF 5
#define NFNL_SUBSYS_COUNT 6
#ifdef __KERNEL__ #ifdef __KERNEL__
...@@ -75,7 +76,7 @@ extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); ...@@ -75,7 +76,7 @@ extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
extern int nfnetlink_has_listeners(unsigned int group); extern int nfnetlink_has_listeners(unsigned int group);
extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group,
int echo); int echo, gfp_t flags);
extern void nfnetlink_set_err(u32 pid, u32 group, int error); extern void nfnetlink_set_err(u32 pid, u32 group, int error);
extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags); extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags);
......
...@@ -101,6 +101,7 @@ enum ctattr_protoinfo_dccp { ...@@ -101,6 +101,7 @@ enum ctattr_protoinfo_dccp {
CTA_PROTOINFO_DCCP_UNSPEC, CTA_PROTOINFO_DCCP_UNSPEC,
CTA_PROTOINFO_DCCP_STATE, CTA_PROTOINFO_DCCP_STATE,
CTA_PROTOINFO_DCCP_ROLE, CTA_PROTOINFO_DCCP_ROLE,
CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
__CTA_PROTOINFO_DCCP_MAX, __CTA_PROTOINFO_DCCP_MAX,
}; };
#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) #define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1)
......
...@@ -184,9 +184,10 @@ struct xt_counters_info ...@@ -184,9 +184,10 @@ struct xt_counters_info
* @matchinfo: per-match data * @matchinfo: per-match data
* @fragoff: packet is a fragment, this is the data offset * @fragoff: packet is a fragment, this is the data offset
* @thoff: position of transport header relative to skb->data * @thoff: position of transport header relative to skb->data
* @hotdrop: drop packet if we had inspection problems * @hook: hook number given packet came from
* @family: Actual NFPROTO_* through which the function is invoked * @family: Actual NFPROTO_* through which the function is invoked
* (helpful when match->family == NFPROTO_UNSPEC) * (helpful when match->family == NFPROTO_UNSPEC)
* @hotdrop: drop packet if we had inspection problems
*/ */
struct xt_match_param { struct xt_match_param {
const struct net_device *in, *out; const struct net_device *in, *out;
...@@ -194,8 +195,9 @@ struct xt_match_param { ...@@ -194,8 +195,9 @@ struct xt_match_param {
const void *matchinfo; const void *matchinfo;
int fragoff; int fragoff;
unsigned int thoff; unsigned int thoff;
bool *hotdrop; unsigned int hooknum;
u_int8_t family; u_int8_t family;
bool *hotdrop;
}; };
/** /**
......
...@@ -15,4 +15,9 @@ struct xt_NFQ_info { ...@@ -15,4 +15,9 @@ struct xt_NFQ_info {
__u16 queuenum; __u16 queuenum;
}; };
struct xt_NFQ_info_v1 {
__u16 queuenum;
__u16 queues_total;
};
#endif /* _XT_NFQ_TARGET_H */ #endif /* _XT_NFQ_TARGET_H */
/*
* Copyright (c) 2003+ Evgeniy Polyakov <johnpol@2ka.mxt.ru>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _XT_OSF_H
#define _XT_OSF_H
#define MAXGENRELEN 32
#define XT_OSF_GENRE (1<<0)
#define XT_OSF_TTL (1<<1)
#define XT_OSF_LOG (1<<2)
#define XT_OSF_INVERT (1<<3)
#define XT_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */
#define XT_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */
#define XT_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */
#define XT_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */
#define XT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */
#define XT_OSF_TTL_NOCHECK 2 /* Do not compare ip and fingerprint TTL at all */
struct xt_osf_info {
char genre[MAXGENRELEN];
__u32 len;
__u32 flags;
__u32 loglevel;
__u32 ttl;
};
/*
* Wildcard MSS (kind of).
* It is used to implement a state machine for the different wildcard values
* of the MSS and window sizes.
*/
struct xt_osf_wc {
__u32 wc;
__u32 val;
};
/*
* This struct represents IANA options
* http://www.iana.org/assignments/tcp-parameters
*/
struct xt_osf_opt {
__u16 kind, length;
struct xt_osf_wc wc;
};
struct xt_osf_user_finger {
struct xt_osf_wc wss;
__u8 ttl, df;
__u16 ss, mss;
__u16 opt_num;
char genre[MAXGENRELEN];
char version[MAXGENRELEN];
char subtype[MAXGENRELEN];
/* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */
struct xt_osf_opt opt[MAX_IPOPTLEN];
};
struct xt_osf_nlmsg {
struct xt_osf_user_finger f;
struct iphdr ip;
struct tcphdr tcp;
};
/* Defines for IANA option kinds */
enum iana_options {
OSFOPT_EOL = 0, /* End of options */
OSFOPT_NOP, /* NOP */
OSFOPT_MSS, /* Maximum segment size */
OSFOPT_WSO, /* Window scale option */
OSFOPT_SACKP, /* SACK permitted */
OSFOPT_SACK, /* SACK */
OSFOPT_ECHO,
OSFOPT_ECHOREPLY,
OSFOPT_TS, /* Timestamp option */
OSFOPT_POCP, /* Partial Order Connection Permitted */
OSFOPT_POSP, /* Partial Order Service Profile */
/* Others are not used in the current OSF */
OSFOPT_EMPTY = 255,
};
/*
* Initial window size option state machine: multiple of mss, mtu or
* plain numeric value. Can also be made as plain numeric value which
* is not a multiple of specified value.
*/
enum xt_osf_window_size_options {
OSF_WSS_PLAIN = 0,
OSF_WSS_MSS,
OSF_WSS_MTU,
OSF_WSS_MODULO,
OSF_WSS_MAX,
};
/*
* Add/remove fingerprint from the kernel.
*/
enum xt_osf_msg_types {
OSF_MSG_ADD,
OSF_MSG_REMOVE,
OSF_MSG_MAX,
};
enum xt_osf_attr_type {
OSF_ATTR_UNSPEC,
OSF_ATTR_FINGER,
OSF_ATTR_MAX,
};
#endif /* _XT_OSF_H */
#ifndef _XT_SOCKET_H
#define _XT_SOCKET_H
enum {
XT_SOCKET_TRANSPARENT = 1 << 0,
};
struct xt_socket_mtinfo1 {
__u8 flags;
};
#endif /* _XT_SOCKET_H */
#ifndef _NF_CONNTRACK_ICMP_H
#define _NF_CONNTRACK_ICMP_H
/* ICMP tracking. */
#include <asm/atomic.h>
struct ip_ct_icmp
{
/* Optimization: when number in == number out, forget immediately. */
atomic_t count;
};
#endif /* _NF_CONNTRACK_ICMP_H */
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#ifndef _NF_CONNTRACK_ICMPV6_H #ifndef _NF_CONNTRACK_ICMPV6_H
#define _NF_CONNTRACK_ICMPV6_H #define _NF_CONNTRACK_ICMPV6_H
#include <asm/atomic.h>
#ifndef ICMPV6_NI_QUERY #ifndef ICMPV6_NI_QUERY
#define ICMPV6_NI_QUERY 139 #define ICMPV6_NI_QUERY 139
...@@ -18,10 +17,4 @@ ...@@ -18,10 +17,4 @@
#define ICMPV6_NI_REPLY 140 #define ICMPV6_NI_REPLY 140
#endif #endif
struct nf_ct_icmpv6
{
/* Optimization: when number in == number out, forget immediately. */
atomic_t count;
};
#endif /* _NF_CONNTRACK_ICMPV6_H */ #endif /* _NF_CONNTRACK_ICMPV6_H */
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include <linux/netfilter/nf_conntrack_dccp.h> #include <linux/netfilter/nf_conntrack_dccp.h>
#include <linux/netfilter/nf_conntrack_sctp.h> #include <linux/netfilter/nf_conntrack_sctp.h>
#include <linux/netfilter/nf_conntrack_proto_gre.h> #include <linux/netfilter/nf_conntrack_proto_gre.h>
#include <net/netfilter/ipv4/nf_conntrack_icmp.h>
#include <net/netfilter/ipv6/nf_conntrack_icmpv6.h> #include <net/netfilter/ipv6/nf_conntrack_icmpv6.h>
#include <net/netfilter/nf_conntrack_tuple.h> #include <net/netfilter/nf_conntrack_tuple.h>
...@@ -34,8 +33,6 @@ union nf_conntrack_proto { ...@@ -34,8 +33,6 @@ union nf_conntrack_proto {
struct nf_ct_dccp dccp; struct nf_ct_dccp dccp;
struct ip_ct_sctp sctp; struct ip_ct_sctp sctp;
struct ip_ct_tcp tcp; struct ip_ct_tcp tcp;
struct ip_ct_icmp icmp;
struct nf_ct_icmpv6 icmpv6;
struct nf_ct_gre gre; struct nf_ct_gre gre;
}; };
...@@ -96,6 +93,8 @@ struct nf_conn { ...@@ -96,6 +93,8 @@ struct nf_conn {
plus 1 for any connection(s) we are `master' for */ plus 1 for any connection(s) we are `master' for */
struct nf_conntrack ct_general; struct nf_conntrack ct_general;
spinlock_t lock;
/* XXX should I move this to the tail ? - Y.K */ /* XXX should I move this to the tail ? - Y.K */
/* These are my tuples; original and reply */ /* These are my tuples; original and reply */
struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
...@@ -144,6 +143,8 @@ static inline u_int8_t nf_ct_protonum(const struct nf_conn *ct) ...@@ -144,6 +143,8 @@ static inline u_int8_t nf_ct_protonum(const struct nf_conn *ct)
return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
} }
#define nf_ct_tuple(ct, dir) (&(ct)->tuplehash[dir].tuple)
/* get master conntrack via master expectation */ /* get master conntrack via master expectation */
#define master_ct(conntr) (conntr->master) #define master_ct(conntr) (conntr->master)
...@@ -201,7 +202,7 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple); ...@@ -201,7 +202,7 @@ __nf_conntrack_find(struct net *net, const struct nf_conntrack_tuple *tuple);
extern void nf_conntrack_hash_insert(struct nf_conn *ct); extern void nf_conntrack_hash_insert(struct nf_conn *ct);
extern void nf_conntrack_flush(struct net *net, u32 pid, int report); extern void nf_conntrack_flush_report(struct net *net, u32 pid, int report);
extern bool nf_ct_get_tuplepr(const struct sk_buff *skb, extern bool nf_ct_get_tuplepr(const struct sk_buff *skb,
unsigned int nhoff, u_int16_t l3num, unsigned int nhoff, u_int16_t l3num,
......
...@@ -6,11 +6,55 @@ ...@@ -6,11 +6,55 @@
#define _NF_CONNTRACK_ECACHE_H #define _NF_CONNTRACK_ECACHE_H
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <linux/notifier.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
/* Connection tracking event bits */
enum ip_conntrack_events
{
/* New conntrack */
IPCT_NEW_BIT = 0,
IPCT_NEW = (1 << IPCT_NEW_BIT),
/* Expected connection */
IPCT_RELATED_BIT = 1,
IPCT_RELATED = (1 << IPCT_RELATED_BIT),
/* Destroyed conntrack */
IPCT_DESTROY_BIT = 2,
IPCT_DESTROY = (1 << IPCT_DESTROY_BIT),
/* Status has changed */
IPCT_STATUS_BIT = 3,
IPCT_STATUS = (1 << IPCT_STATUS_BIT),
/* Update of protocol info */
IPCT_PROTOINFO_BIT = 4,
IPCT_PROTOINFO = (1 << IPCT_PROTOINFO_BIT),
/* New helper for conntrack */
IPCT_HELPER_BIT = 5,
IPCT_HELPER = (1 << IPCT_HELPER_BIT),
/* Mark is set */
IPCT_MARK_BIT = 6,
IPCT_MARK = (1 << IPCT_MARK_BIT),
/* NAT sequence adjustment */
IPCT_NATSEQADJ_BIT = 7,
IPCT_NATSEQADJ = (1 << IPCT_NATSEQADJ_BIT),
/* Secmark is set */
IPCT_SECMARK_BIT = 8,
IPCT_SECMARK = (1 << IPCT_SECMARK_BIT),
};
enum ip_conntrack_expect_events {
IPEXP_NEW_BIT = 0,
IPEXP_NEW = (1 << IPEXP_NEW_BIT),
};
#ifdef CONFIG_NF_CONNTRACK_EVENTS #ifdef CONFIG_NF_CONNTRACK_EVENTS
struct nf_conntrack_ecache { struct nf_conntrack_ecache {
struct nf_conn *ct; struct nf_conn *ct;
...@@ -24,9 +68,13 @@ struct nf_ct_event { ...@@ -24,9 +68,13 @@ struct nf_ct_event {
int report; int report;
}; };
extern struct atomic_notifier_head nf_conntrack_chain; struct nf_ct_event_notifier {
extern int nf_conntrack_register_notifier(struct notifier_block *nb); int (*fcn)(unsigned int events, struct nf_ct_event *item);
extern int nf_conntrack_unregister_notifier(struct notifier_block *nb); };
extern struct nf_ct_event_notifier *nf_conntrack_event_cb;
extern int nf_conntrack_register_notifier(struct nf_ct_event_notifier *nb);
extern void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *nb);
extern void nf_ct_deliver_cached_events(const struct nf_conn *ct); extern void nf_ct_deliver_cached_events(const struct nf_conn *ct);
extern void __nf_ct_event_cache_init(struct nf_conn *ct); extern void __nf_ct_event_cache_init(struct nf_conn *ct);
...@@ -52,13 +100,23 @@ nf_conntrack_event_report(enum ip_conntrack_events event, ...@@ -52,13 +100,23 @@ nf_conntrack_event_report(enum ip_conntrack_events event,
u32 pid, u32 pid,
int report) int report)
{ {
struct nf_ct_event_notifier *notify;
rcu_read_lock();
notify = rcu_dereference(nf_conntrack_event_cb);
if (notify == NULL)
goto out_unlock;
if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) {
struct nf_ct_event item = { struct nf_ct_event item = {
.ct = ct, .ct = ct,
.pid = pid, .pid = pid,
.report = report .report = report
}; };
if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) notify->fcn(event, &item);
atomic_notifier_call_chain(&nf_conntrack_chain, event, &item); }
out_unlock:
rcu_read_unlock();
} }
static inline void static inline void
...@@ -73,9 +131,13 @@ struct nf_exp_event { ...@@ -73,9 +131,13 @@ struct nf_exp_event {
int report; int report;
}; };
extern struct atomic_notifier_head nf_ct_expect_chain; struct nf_exp_event_notifier {
extern int nf_ct_expect_register_notifier(struct notifier_block *nb); int (*fcn)(unsigned int events, struct nf_exp_event *item);
extern int nf_ct_expect_unregister_notifier(struct notifier_block *nb); };
extern struct nf_exp_event_notifier *nf_expect_event_cb;
extern int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *nb);
extern void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *nb);
static inline void static inline void
nf_ct_expect_event_report(enum ip_conntrack_expect_events event, nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
...@@ -83,12 +145,23 @@ nf_ct_expect_event_report(enum ip_conntrack_expect_events event, ...@@ -83,12 +145,23 @@ nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
u32 pid, u32 pid,
int report) int report)
{ {
struct nf_exp_event_notifier *notify;
rcu_read_lock();
notify = rcu_dereference(nf_expect_event_cb);
if (notify == NULL)
goto out_unlock;
{
struct nf_exp_event item = { struct nf_exp_event item = {
.exp = exp, .exp = exp,
.pid = pid, .pid = pid,
.report = report .report = report
}; };
atomic_notifier_call_chain(&nf_ct_expect_chain, event, &item); notify->fcn(event, &item);
}
out_unlock:
rcu_read_unlock();
} }
static inline void static inline void
......
...@@ -59,11 +59,11 @@ struct nf_conntrack_l4proto ...@@ -59,11 +59,11 @@ struct nf_conntrack_l4proto
const struct nf_conntrack_tuple *); const struct nf_conntrack_tuple *);
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
int (*print_conntrack)(struct seq_file *s, const struct nf_conn *); int (*print_conntrack)(struct seq_file *s, struct nf_conn *);
/* convert protoinfo to nfnetink attributes */ /* convert protoinfo to nfnetink attributes */
int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla, int (*to_nlattr)(struct sk_buff *skb, struct nlattr *nla,
const struct nf_conn *ct); struct nf_conn *ct);
/* Calculate protoinfo nlattr size */ /* Calculate protoinfo nlattr size */
int (*nlattr_size)(void); int (*nlattr_size)(void);
......
...@@ -939,6 +939,15 @@ static inline u64 nla_get_u64(const struct nlattr *nla) ...@@ -939,6 +939,15 @@ static inline u64 nla_get_u64(const struct nlattr *nla)
return tmp; return tmp;
} }
/**
* nla_get_be64 - return payload of __be64 attribute
* @nla: __be64 netlink attribute
*/
static inline __be64 nla_get_be64(const struct nlattr *nla)
{
return *(__be64 *) nla_data(nla);
}
/** /**
* nla_get_flag - return payload of flag attribute * nla_get_flag - return payload of flag attribute
* @nla: flag netlink attribute * @nla: flag netlink attribute
......
...@@ -142,6 +142,12 @@ static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h, ...@@ -142,6 +142,12 @@ static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
return 0; return 0;
} }
static inline __pure
struct ebt_entry *ebt_next_entry(const struct ebt_entry *entry)
{
return (void *)entry + entry->next_offset;
}
/* Do some firewalling */ /* Do some firewalling */
unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out, const struct net_device *in, const struct net_device *out,
...@@ -164,7 +170,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, ...@@ -164,7 +170,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
mtpar.in = tgpar.in = in; mtpar.in = tgpar.in = in;
mtpar.out = tgpar.out = out; mtpar.out = tgpar.out = out;
mtpar.hotdrop = &hotdrop; mtpar.hotdrop = &hotdrop;
tgpar.hooknum = hook; mtpar.hooknum = tgpar.hooknum = hook;
read_lock_bh(&table->lock); read_lock_bh(&table->lock);
private = table->private; private = table->private;
...@@ -249,8 +255,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, ...@@ -249,8 +255,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
/* jump to a udc */ /* jump to a udc */
cs[sp].n = i + 1; cs[sp].n = i + 1;
cs[sp].chaininfo = chaininfo; cs[sp].chaininfo = chaininfo;
cs[sp].e = (struct ebt_entry *) cs[sp].e = ebt_next_entry(point);
(((char *)point) + point->next_offset);
i = 0; i = 0;
chaininfo = (struct ebt_entries *) (base + verdict); chaininfo = (struct ebt_entries *) (base + verdict);
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
...@@ -266,8 +271,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb, ...@@ -266,8 +271,7 @@ unsigned int ebt_do_table (unsigned int hook, struct sk_buff *skb,
sp++; sp++;
continue; continue;
letscontinue: letscontinue:
point = (struct ebt_entry *) point = ebt_next_entry(point);
(((char *)point) + point->next_offset);
i++; i++;
} }
...@@ -787,7 +791,7 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s ...@@ -787,7 +791,7 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s
/* this can't be 0, so the loop test is correct */ /* this can't be 0, so the loop test is correct */
cl_s[i].cs.n = pos + 1; cl_s[i].cs.n = pos + 1;
pos = 0; pos = 0;
cl_s[i].cs.e = ((void *)e + e->next_offset); cl_s[i].cs.e = ebt_next_entry(e);
e = (struct ebt_entry *)(hlp2->data); e = (struct ebt_entry *)(hlp2->data);
nentries = hlp2->nentries; nentries = hlp2->nentries;
cl_s[i].from = chain_nr; cl_s[i].from = chain_nr;
...@@ -797,7 +801,7 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s ...@@ -797,7 +801,7 @@ static int check_chainloops(struct ebt_entries *chain, struct ebt_cl_stack *cl_s
continue; continue;
} }
letscontinue: letscontinue:
e = (void *)e + e->next_offset; e = ebt_next_entry(e);
pos++; pos++;
} }
return 0; return 0;
......
...@@ -231,6 +231,12 @@ static inline struct arpt_entry *get_entry(void *base, unsigned int offset) ...@@ -231,6 +231,12 @@ static inline struct arpt_entry *get_entry(void *base, unsigned int offset)
return (struct arpt_entry *)(base + offset); return (struct arpt_entry *)(base + offset);
} }
static inline __pure
struct arpt_entry *arpt_next_entry(const struct arpt_entry *entry)
{
return (void *)entry + entry->next_offset;
}
unsigned int arpt_do_table(struct sk_buff *skb, unsigned int arpt_do_table(struct sk_buff *skb,
unsigned int hook, unsigned int hook,
const struct net_device *in, const struct net_device *in,
...@@ -267,13 +273,16 @@ unsigned int arpt_do_table(struct sk_buff *skb, ...@@ -267,13 +273,16 @@ unsigned int arpt_do_table(struct sk_buff *skb,
arp = arp_hdr(skb); arp = arp_hdr(skb);
do { do {
if (arp_packet_match(arp, skb->dev, indev, outdev, &e->arp)) {
struct arpt_entry_target *t; struct arpt_entry_target *t;
int hdr_len; int hdr_len;
if (!arp_packet_match(arp, skb->dev, indev, outdev, &e->arp)) {
e = arpt_next_entry(e);
continue;
}
hdr_len = sizeof(*arp) + (2 * sizeof(struct in_addr)) + hdr_len = sizeof(*arp) + (2 * sizeof(struct in_addr)) +
(2 * skb->dev->addr_len); (2 * skb->dev->addr_len);
ADD_COUNTER(e->counters, hdr_len, 1); ADD_COUNTER(e->counters, hdr_len, 1);
t = arpt_get_target(e); t = arpt_get_target(e);
...@@ -290,44 +299,38 @@ unsigned int arpt_do_table(struct sk_buff *skb, ...@@ -290,44 +299,38 @@ unsigned int arpt_do_table(struct sk_buff *skb,
break; break;
} }
e = back; e = back;
back = get_entry(table_base, back = get_entry(table_base, back->comefrom);
back->comefrom);
continue; continue;
} }
if (table_base + v if (table_base + v
!= (void *)e + e->next_offset) { != arpt_next_entry(e)) {
/* Save old back ptr in next entry */ /* Save old back ptr in next entry */
struct arpt_entry *next struct arpt_entry *next = arpt_next_entry(e);
= (void *)e + e->next_offset; next->comefrom = (void *)back - table_base;
next->comefrom =
(void *)back - table_base;
/* set back pointer to next entry */ /* set back pointer to next entry */
back = next; back = next;
} }
e = get_entry(table_base, v); e = get_entry(table_base, v);
} else { continue;
}
/* Targets which reenter must return /* Targets which reenter must return
* abs. verdicts * abs. verdicts
*/ */
tgpar.target = t->u.kernel.target; tgpar.target = t->u.kernel.target;
tgpar.targinfo = t->data; tgpar.targinfo = t->data;
verdict = t->u.kernel.target->target(skb, verdict = t->u.kernel.target->target(skb, &tgpar);
&tgpar);
/* Target might have changed stuff. */ /* Target might have changed stuff. */
arp = arp_hdr(skb); arp = arp_hdr(skb);
if (verdict == ARPT_CONTINUE) if (verdict == ARPT_CONTINUE)
e = (void *)e + e->next_offset; e = arpt_next_entry(e);
else else
/* Verdict */ /* Verdict */
break; break;
}
} else {
e = (void *)e + e->next_offset;
}
} while (!hotdrop); } while (!hotdrop);
xt_info_rdunlock_bh(); xt_info_rdunlock_bh();
......
...@@ -596,7 +596,7 @@ static int __init ip_queue_init(void) ...@@ -596,7 +596,7 @@ static int __init ip_queue_init(void)
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
ipq_sysctl_header = register_sysctl_paths(net_ipv4_ctl_path, ipq_table); ipq_sysctl_header = register_sysctl_paths(net_ipv4_ctl_path, ipq_table);
#endif #endif
status = nf_register_queue_handler(PF_INET, &nfqh); status = nf_register_queue_handler(NFPROTO_IPV4, &nfqh);
if (status < 0) { if (status < 0) {
printk(KERN_ERR "ip_queue: failed to register queue handler\n"); printk(KERN_ERR "ip_queue: failed to register queue handler\n");
goto cleanup_sysctl; goto cleanup_sysctl;
......
...@@ -238,8 +238,8 @@ static struct nf_loginfo trace_loginfo = { ...@@ -238,8 +238,8 @@ static struct nf_loginfo trace_loginfo = {
/* Mildly perf critical (only if packet tracing is on) */ /* Mildly perf critical (only if packet tracing is on) */
static inline int static inline int
get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e, get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e,
char *hookname, char **chainname, const char *hookname, const char **chainname,
char **comment, unsigned int *rulenum) const char **comment, unsigned int *rulenum)
{ {
struct ipt_standard_target *t = (void *)ipt_get_target(s); struct ipt_standard_target *t = (void *)ipt_get_target(s);
...@@ -257,8 +257,8 @@ get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e, ...@@ -257,8 +257,8 @@ get_chainname_rulenum(struct ipt_entry *s, struct ipt_entry *e,
&& unconditional(&s->ip)) { && unconditional(&s->ip)) {
/* Tail of chains: STANDARD target (return/policy) */ /* Tail of chains: STANDARD target (return/policy) */
*comment = *chainname == hookname *comment = *chainname == hookname
? (char *)comments[NF_IP_TRACE_COMMENT_POLICY] ? comments[NF_IP_TRACE_COMMENT_POLICY]
: (char *)comments[NF_IP_TRACE_COMMENT_RETURN]; : comments[NF_IP_TRACE_COMMENT_RETURN];
} }
return 1; return 1;
} else } else
...@@ -277,14 +277,14 @@ static void trace_packet(struct sk_buff *skb, ...@@ -277,14 +277,14 @@ static void trace_packet(struct sk_buff *skb,
{ {
void *table_base; void *table_base;
const struct ipt_entry *root; const struct ipt_entry *root;
char *hookname, *chainname, *comment; const char *hookname, *chainname, *comment;
unsigned int rulenum = 0; unsigned int rulenum = 0;
table_base = (void *)private->entries[smp_processor_id()]; table_base = private->entries[smp_processor_id()];
root = get_entry(table_base, private->hook_entry[hook]); root = get_entry(table_base, private->hook_entry[hook]);
hookname = chainname = (char *)hooknames[hook]; hookname = chainname = hooknames[hook];
comment = (char *)comments[NF_IP_TRACE_COMMENT_RULE]; comment = comments[NF_IP_TRACE_COMMENT_RULE];
IPT_ENTRY_ITERATE(root, IPT_ENTRY_ITERATE(root,
private->size - private->hook_entry[hook], private->size - private->hook_entry[hook],
...@@ -297,6 +297,12 @@ static void trace_packet(struct sk_buff *skb, ...@@ -297,6 +297,12 @@ static void trace_packet(struct sk_buff *skb,
} }
#endif #endif
static inline __pure
struct ipt_entry *ipt_next_entry(const struct ipt_entry *entry)
{
return (void *)entry + entry->next_offset;
}
/* Returns one of the generic firewall policies, like NF_ACCEPT. */ /* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int unsigned int
ipt_do_table(struct sk_buff *skb, ipt_do_table(struct sk_buff *skb,
...@@ -305,6 +311,8 @@ ipt_do_table(struct sk_buff *skb, ...@@ -305,6 +311,8 @@ ipt_do_table(struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
struct xt_table *table) struct xt_table *table)
{ {
#define tb_comefrom ((struct ipt_entry *)table_base)->comefrom
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
const struct iphdr *ip; const struct iphdr *ip;
u_int16_t datalen; u_int16_t datalen;
...@@ -335,7 +343,7 @@ ipt_do_table(struct sk_buff *skb, ...@@ -335,7 +343,7 @@ ipt_do_table(struct sk_buff *skb,
mtpar.in = tgpar.in = in; mtpar.in = tgpar.in = in;
mtpar.out = tgpar.out = out; mtpar.out = tgpar.out = out;
mtpar.family = tgpar.family = NFPROTO_IPV4; mtpar.family = tgpar.family = NFPROTO_IPV4;
tgpar.hooknum = hook; mtpar.hooknum = tgpar.hooknum = hook;
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); IP_NF_ASSERT(table->valid_hooks & (1 << hook));
xt_info_rdlock_bh(); xt_info_rdlock_bh();
...@@ -348,14 +356,16 @@ ipt_do_table(struct sk_buff *skb, ...@@ -348,14 +356,16 @@ ipt_do_table(struct sk_buff *skb,
back = get_entry(table_base, private->underflow[hook]); back = get_entry(table_base, private->underflow[hook]);
do { do {
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
if (ip_packet_match(ip, indev, outdev,
&e->ip, mtpar.fragoff)) {
struct ipt_entry_target *t; struct ipt_entry_target *t;
if (IPT_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) IP_NF_ASSERT(e);
goto no_match; IP_NF_ASSERT(back);
if (!ip_packet_match(ip, indev, outdev,
&e->ip, mtpar.fragoff) ||
IPT_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) {
e = ipt_next_entry(e);
continue;
}
ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1); ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
...@@ -381,59 +391,49 @@ ipt_do_table(struct sk_buff *skb, ...@@ -381,59 +391,49 @@ ipt_do_table(struct sk_buff *skb,
break; break;
} }
e = back; e = back;
back = get_entry(table_base, back = get_entry(table_base, back->comefrom);
back->comefrom);
continue; continue;
} }
if (table_base + v != (void *)e + e->next_offset if (table_base + v != ipt_next_entry(e)
&& !(e->ip.flags & IPT_F_GOTO)) { && !(e->ip.flags & IPT_F_GOTO)) {
/* Save old back ptr in next entry */ /* Save old back ptr in next entry */
struct ipt_entry *next struct ipt_entry *next = ipt_next_entry(e);
= (void *)e + e->next_offset; next->comefrom = (void *)back - table_base;
next->comefrom
= (void *)back - table_base;
/* set back pointer to next entry */ /* set back pointer to next entry */
back = next; back = next;
} }
e = get_entry(table_base, v); e = get_entry(table_base, v);
} else { continue;
}
/* Targets which reenter must return /* Targets which reenter must return
abs. verdicts */ abs. verdicts */
tgpar.target = t->u.kernel.target; tgpar.target = t->u.kernel.target;
tgpar.targinfo = t->data; tgpar.targinfo = t->data;
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
((struct ipt_entry *)table_base)->comefrom tb_comefrom = 0xeeeeeeec;
= 0xeeeeeeec;
#endif #endif
verdict = t->u.kernel.target->target(skb, verdict = t->u.kernel.target->target(skb, &tgpar);
&tgpar);
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
if (((struct ipt_entry *)table_base)->comefrom if (tb_comefrom != 0xeeeeeeec && verdict == IPT_CONTINUE) {
!= 0xeeeeeeec
&& verdict == IPT_CONTINUE) {
printk("Target %s reentered!\n", printk("Target %s reentered!\n",
t->u.kernel.target->name); t->u.kernel.target->name);
verdict = NF_DROP; verdict = NF_DROP;
} }
((struct ipt_entry *)table_base)->comefrom tb_comefrom = 0x57acc001;
= 0x57acc001;
#endif #endif
/* Target might have changed stuff. */ /* Target might have changed stuff. */
ip = ip_hdr(skb); ip = ip_hdr(skb);
datalen = skb->len - ip->ihl * 4; datalen = skb->len - ip->ihl * 4;
if (verdict == IPT_CONTINUE) if (verdict == IPT_CONTINUE)
e = (void *)e + e->next_offset; e = ipt_next_entry(e);
else else
/* Verdict */ /* Verdict */
break; break;
}
} else {
no_match:
e = (void *)e + e->next_offset;
}
} while (!hotdrop); } while (!hotdrop);
xt_info_rdunlock_bh(); xt_info_rdunlock_bh();
...@@ -444,6 +444,8 @@ ipt_do_table(struct sk_buff *skb, ...@@ -444,6 +444,8 @@ ipt_do_table(struct sk_buff *skb,
return NF_DROP; return NF_DROP;
else return verdict; else return verdict;
#endif #endif
#undef tb_comefrom
} }
/* Figures out from what hook each rule can be called: returns 0 if /* Figures out from what hook each rule can be called: returns 0 if
...@@ -2158,7 +2160,7 @@ static bool icmp_checkentry(const struct xt_mtchk_param *par) ...@@ -2158,7 +2160,7 @@ static bool icmp_checkentry(const struct xt_mtchk_param *par)
static struct xt_target ipt_standard_target __read_mostly = { static struct xt_target ipt_standard_target __read_mostly = {
.name = IPT_STANDARD_TARGET, .name = IPT_STANDARD_TARGET,
.targetsize = sizeof(int), .targetsize = sizeof(int),
.family = AF_INET, .family = NFPROTO_IPV4,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compatsize = sizeof(compat_int_t), .compatsize = sizeof(compat_int_t),
.compat_from_user = compat_standard_from_user, .compat_from_user = compat_standard_from_user,
...@@ -2170,7 +2172,7 @@ static struct xt_target ipt_error_target __read_mostly = { ...@@ -2170,7 +2172,7 @@ static struct xt_target ipt_error_target __read_mostly = {
.name = IPT_ERROR_TARGET, .name = IPT_ERROR_TARGET,
.target = ipt_error, .target = ipt_error,
.targetsize = IPT_FUNCTION_MAXNAMELEN, .targetsize = IPT_FUNCTION_MAXNAMELEN,
.family = AF_INET, .family = NFPROTO_IPV4,
}; };
static struct nf_sockopt_ops ipt_sockopts = { static struct nf_sockopt_ops ipt_sockopts = {
...@@ -2196,17 +2198,17 @@ static struct xt_match icmp_matchstruct __read_mostly = { ...@@ -2196,17 +2198,17 @@ static struct xt_match icmp_matchstruct __read_mostly = {
.matchsize = sizeof(struct ipt_icmp), .matchsize = sizeof(struct ipt_icmp),
.checkentry = icmp_checkentry, .checkentry = icmp_checkentry,
.proto = IPPROTO_ICMP, .proto = IPPROTO_ICMP,
.family = AF_INET, .family = NFPROTO_IPV4,
}; };
static int __net_init ip_tables_net_init(struct net *net) static int __net_init ip_tables_net_init(struct net *net)
{ {
return xt_proto_init(net, AF_INET); return xt_proto_init(net, NFPROTO_IPV4);
} }
static void __net_exit ip_tables_net_exit(struct net *net) static void __net_exit ip_tables_net_exit(struct net *net)
{ {
xt_proto_fini(net, AF_INET); xt_proto_fini(net, NFPROTO_IPV4);
} }
static struct pernet_operations ip_tables_net_ops = { static struct pernet_operations ip_tables_net_ops = {
......
...@@ -27,9 +27,6 @@ MODULE_LICENSE("GPL"); ...@@ -27,9 +27,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>"); MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
MODULE_DESCRIPTION("Xtables: automatic-address SNAT"); MODULE_DESCRIPTION("Xtables: automatic-address SNAT");
/* Lock protects masq region inside conntrack */
static DEFINE_RWLOCK(masq_lock);
/* FIXME: Multiple targets. --RR */ /* FIXME: Multiple targets. --RR */
static bool masquerade_tg_check(const struct xt_tgchk_param *par) static bool masquerade_tg_check(const struct xt_tgchk_param *par)
{ {
...@@ -79,9 +76,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_target_param *par) ...@@ -79,9 +76,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_target_param *par)
return NF_DROP; return NF_DROP;
} }
write_lock_bh(&masq_lock);
nat->masq_index = par->out->ifindex; nat->masq_index = par->out->ifindex;
write_unlock_bh(&masq_lock);
/* Transfer from original range. */ /* Transfer from original range. */
newrange = ((struct nf_nat_range) newrange = ((struct nf_nat_range)
...@@ -97,16 +92,11 @@ static int ...@@ -97,16 +92,11 @@ static int
device_cmp(struct nf_conn *i, void *ifindex) device_cmp(struct nf_conn *i, void *ifindex)
{ {
const struct nf_conn_nat *nat = nfct_nat(i); const struct nf_conn_nat *nat = nfct_nat(i);
int ret;
if (!nat) if (!nat)
return 0; return 0;
read_lock_bh(&masq_lock); return nat->masq_index == (int)(long)ifindex;
ret = (nat->masq_index == (int)(long)ifindex);
read_unlock_bh(&masq_lock);
return ret;
} }
static int masq_device_event(struct notifier_block *this, static int masq_device_event(struct notifier_block *this,
......
...@@ -82,18 +82,10 @@ static int icmp_packet(struct nf_conn *ct, ...@@ -82,18 +82,10 @@ static int icmp_packet(struct nf_conn *ct,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum) unsigned int hooknum)
{ {
/* Try to delete connection immediately after all replies: /* Do not immediately delete the connection after the first
won't actually vanish as we still have skb, and del_timer successful reply to avoid excessive conntrackd traffic
means this will only run once even if count hits zero twice and also to handle correctly ICMP echo reply duplicates. */
(theoretically possible with SMP) */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
if (atomic_dec_and_test(&ct->proto.icmp.count))
nf_ct_kill_acct(ct, ctinfo, skb);
} else {
atomic_inc(&ct->proto.icmp.count);
nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, ct);
nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout); nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout);
}
return NF_ACCEPT; return NF_ACCEPT;
} }
...@@ -117,7 +109,6 @@ static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -117,7 +109,6 @@ static bool icmp_new(struct nf_conn *ct, const struct sk_buff *skb,
nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple); nf_ct_dump_tuple_ip(&ct->tuplehash[0].tuple);
return false; return false;
} }
atomic_set(&ct->proto.icmp.count, 0);
return true; return true;
} }
......
...@@ -598,7 +598,7 @@ static int __init ip6_queue_init(void) ...@@ -598,7 +598,7 @@ static int __init ip6_queue_init(void)
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
ipq_sysctl_header = register_sysctl_paths(net_ipv6_ctl_path, ipq_table); ipq_sysctl_header = register_sysctl_paths(net_ipv6_ctl_path, ipq_table);
#endif #endif
status = nf_register_queue_handler(PF_INET6, &nfqh); status = nf_register_queue_handler(NFPROTO_IPV6, &nfqh);
if (status < 0) { if (status < 0) {
printk(KERN_ERR "ip6_queue: failed to register queue handler\n"); printk(KERN_ERR "ip6_queue: failed to register queue handler\n");
goto cleanup_sysctl; goto cleanup_sysctl;
......
...@@ -270,8 +270,8 @@ static struct nf_loginfo trace_loginfo = { ...@@ -270,8 +270,8 @@ static struct nf_loginfo trace_loginfo = {
/* Mildly perf critical (only if packet tracing is on) */ /* Mildly perf critical (only if packet tracing is on) */
static inline int static inline int
get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e, get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
char *hookname, char **chainname, const char *hookname, const char **chainname,
char **comment, unsigned int *rulenum) const char **comment, unsigned int *rulenum)
{ {
struct ip6t_standard_target *t = (void *)ip6t_get_target(s); struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
...@@ -289,8 +289,8 @@ get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e, ...@@ -289,8 +289,8 @@ get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
&& unconditional(&s->ipv6)) { && unconditional(&s->ipv6)) {
/* Tail of chains: STANDARD target (return/policy) */ /* Tail of chains: STANDARD target (return/policy) */
*comment = *chainname == hookname *comment = *chainname == hookname
? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY] ? comments[NF_IP6_TRACE_COMMENT_POLICY]
: (char *)comments[NF_IP6_TRACE_COMMENT_RETURN]; : comments[NF_IP6_TRACE_COMMENT_RETURN];
} }
return 1; return 1;
} else } else
...@@ -309,14 +309,14 @@ static void trace_packet(struct sk_buff *skb, ...@@ -309,14 +309,14 @@ static void trace_packet(struct sk_buff *skb,
{ {
void *table_base; void *table_base;
const struct ip6t_entry *root; const struct ip6t_entry *root;
char *hookname, *chainname, *comment; const char *hookname, *chainname, *comment;
unsigned int rulenum = 0; unsigned int rulenum = 0;
table_base = (void *)private->entries[smp_processor_id()]; table_base = private->entries[smp_processor_id()];
root = get_entry(table_base, private->hook_entry[hook]); root = get_entry(table_base, private->hook_entry[hook]);
hookname = chainname = (char *)hooknames[hook]; hookname = chainname = hooknames[hook];
comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE]; comment = comments[NF_IP6_TRACE_COMMENT_RULE];
IP6T_ENTRY_ITERATE(root, IP6T_ENTRY_ITERATE(root,
private->size - private->hook_entry[hook], private->size - private->hook_entry[hook],
...@@ -329,6 +329,12 @@ static void trace_packet(struct sk_buff *skb, ...@@ -329,6 +329,12 @@ static void trace_packet(struct sk_buff *skb,
} }
#endif #endif
static inline __pure struct ip6t_entry *
ip6t_next_entry(const struct ip6t_entry *entry)
{
return (void *)entry + entry->next_offset;
}
/* Returns one of the generic firewall policies, like NF_ACCEPT. */ /* Returns one of the generic firewall policies, like NF_ACCEPT. */
unsigned int unsigned int
ip6t_do_table(struct sk_buff *skb, ip6t_do_table(struct sk_buff *skb,
...@@ -337,6 +343,8 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -337,6 +343,8 @@ ip6t_do_table(struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
struct xt_table *table) struct xt_table *table)
{ {
#define tb_comefrom ((struct ip6t_entry *)table_base)->comefrom
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long)))); static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
bool hotdrop = false; bool hotdrop = false;
/* Initializing verdict to NF_DROP keeps gcc happy. */ /* Initializing verdict to NF_DROP keeps gcc happy. */
...@@ -361,7 +369,7 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -361,7 +369,7 @@ ip6t_do_table(struct sk_buff *skb,
mtpar.in = tgpar.in = in; mtpar.in = tgpar.in = in;
mtpar.out = tgpar.out = out; mtpar.out = tgpar.out = out;
mtpar.family = tgpar.family = NFPROTO_IPV6; mtpar.family = tgpar.family = NFPROTO_IPV6;
tgpar.hooknum = hook; mtpar.hooknum = tgpar.hooknum = hook;
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); IP_NF_ASSERT(table->valid_hooks & (1 << hook));
...@@ -375,14 +383,16 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -375,14 +383,16 @@ ip6t_do_table(struct sk_buff *skb,
back = get_entry(table_base, private->underflow[hook]); back = get_entry(table_base, private->underflow[hook]);
do { do {
IP_NF_ASSERT(e);
IP_NF_ASSERT(back);
if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
&mtpar.thoff, &mtpar.fragoff, &hotdrop)) {
struct ip6t_entry_target *t; struct ip6t_entry_target *t;
if (IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) IP_NF_ASSERT(e);
goto no_match; IP_NF_ASSERT(back);
if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
&mtpar.thoff, &mtpar.fragoff, &hotdrop) ||
IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) {
e = ip6t_next_entry(e);
continue;
}
ADD_COUNTER(e->counters, ADD_COUNTER(e->counters,
ntohs(ipv6_hdr(skb)->payload_len) + ntohs(ipv6_hdr(skb)->payload_len) +
...@@ -410,61 +420,49 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -410,61 +420,49 @@ ip6t_do_table(struct sk_buff *skb,
break; break;
} }
e = back; e = back;
back = get_entry(table_base, back = get_entry(table_base, back->comefrom);
back->comefrom);
continue; continue;
} }
if (table_base + v != (void *)e + e->next_offset if (table_base + v != ip6t_next_entry(e)
&& !(e->ipv6.flags & IP6T_F_GOTO)) { && !(e->ipv6.flags & IP6T_F_GOTO)) {
/* Save old back ptr in next entry */ /* Save old back ptr in next entry */
struct ip6t_entry *next struct ip6t_entry *next = ip6t_next_entry(e);
= (void *)e + e->next_offset; next->comefrom = (void *)back - table_base;
next->comefrom
= (void *)back - table_base;
/* set back pointer to next entry */ /* set back pointer to next entry */
back = next; back = next;
} }
e = get_entry(table_base, v); e = get_entry(table_base, v);
} else { continue;
}
/* Targets which reenter must return /* Targets which reenter must return
abs. verdicts */ abs. verdicts */
tgpar.target = t->u.kernel.target; tgpar.target = t->u.kernel.target;
tgpar.targinfo = t->data; tgpar.targinfo = t->data;
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
((struct ip6t_entry *)table_base)->comefrom tb_comefrom = 0xeeeeeeec;
= 0xeeeeeeec;
#endif #endif
verdict = t->u.kernel.target->target(skb, verdict = t->u.kernel.target->target(skb, &tgpar);
&tgpar);
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
if (((struct ip6t_entry *)table_base)->comefrom if (tb_comefrom != 0xeeeeeeec && verdict == IP6T_CONTINUE) {
!= 0xeeeeeeec
&& verdict == IP6T_CONTINUE) {
printk("Target %s reentered!\n", printk("Target %s reentered!\n",
t->u.kernel.target->name); t->u.kernel.target->name);
verdict = NF_DROP; verdict = NF_DROP;
} }
((struct ip6t_entry *)table_base)->comefrom tb_comefrom = 0x57acc001;
= 0x57acc001;
#endif #endif
if (verdict == IP6T_CONTINUE) if (verdict == IP6T_CONTINUE)
e = (void *)e + e->next_offset; e = ip6t_next_entry(e);
else else
/* Verdict */ /* Verdict */
break; break;
}
} else {
no_match:
e = (void *)e + e->next_offset;
}
} while (!hotdrop); } while (!hotdrop);
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON; tb_comefrom = NETFILTER_LINK_POISON;
#endif #endif
xt_info_rdunlock_bh(); xt_info_rdunlock_bh();
...@@ -475,6 +473,8 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -475,6 +473,8 @@ ip6t_do_table(struct sk_buff *skb,
return NF_DROP; return NF_DROP;
else return verdict; else return verdict;
#endif #endif
#undef tb_comefrom
} }
/* Figures out from what hook each rule can be called: returns 0 if /* Figures out from what hook each rule can be called: returns 0 if
...@@ -2191,7 +2191,7 @@ static bool icmp6_checkentry(const struct xt_mtchk_param *par) ...@@ -2191,7 +2191,7 @@ static bool icmp6_checkentry(const struct xt_mtchk_param *par)
static struct xt_target ip6t_standard_target __read_mostly = { static struct xt_target ip6t_standard_target __read_mostly = {
.name = IP6T_STANDARD_TARGET, .name = IP6T_STANDARD_TARGET,
.targetsize = sizeof(int), .targetsize = sizeof(int),
.family = AF_INET6, .family = NFPROTO_IPV6,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compatsize = sizeof(compat_int_t), .compatsize = sizeof(compat_int_t),
.compat_from_user = compat_standard_from_user, .compat_from_user = compat_standard_from_user,
...@@ -2203,7 +2203,7 @@ static struct xt_target ip6t_error_target __read_mostly = { ...@@ -2203,7 +2203,7 @@ static struct xt_target ip6t_error_target __read_mostly = {
.name = IP6T_ERROR_TARGET, .name = IP6T_ERROR_TARGET,
.target = ip6t_error, .target = ip6t_error,
.targetsize = IP6T_FUNCTION_MAXNAMELEN, .targetsize = IP6T_FUNCTION_MAXNAMELEN,
.family = AF_INET6, .family = NFPROTO_IPV6,
}; };
static struct nf_sockopt_ops ip6t_sockopts = { static struct nf_sockopt_ops ip6t_sockopts = {
...@@ -2229,17 +2229,17 @@ static struct xt_match icmp6_matchstruct __read_mostly = { ...@@ -2229,17 +2229,17 @@ static struct xt_match icmp6_matchstruct __read_mostly = {
.matchsize = sizeof(struct ip6t_icmp), .matchsize = sizeof(struct ip6t_icmp),
.checkentry = icmp6_checkentry, .checkentry = icmp6_checkentry,
.proto = IPPROTO_ICMPV6, .proto = IPPROTO_ICMPV6,
.family = AF_INET6, .family = NFPROTO_IPV6,
}; };
static int __net_init ip6_tables_net_init(struct net *net) static int __net_init ip6_tables_net_init(struct net *net)
{ {
return xt_proto_init(net, AF_INET6); return xt_proto_init(net, NFPROTO_IPV6);
} }
static void __net_exit ip6_tables_net_exit(struct net *net) static void __net_exit ip6_tables_net_exit(struct net *net)
{ {
xt_proto_fini(net, AF_INET6); xt_proto_fini(net, NFPROTO_IPV6);
} }
static struct pernet_operations ip6_tables_net_ops = { static struct pernet_operations ip6_tables_net_ops = {
......
...@@ -95,18 +95,10 @@ static int icmpv6_packet(struct nf_conn *ct, ...@@ -95,18 +95,10 @@ static int icmpv6_packet(struct nf_conn *ct,
u_int8_t pf, u_int8_t pf,
unsigned int hooknum) unsigned int hooknum)
{ {
/* Try to delete connection immediately after all replies: /* Do not immediately delete the connection after the first
won't actually vanish as we still have skb, and del_timer successful reply to avoid excessive conntrackd traffic
means this will only run once even if count hits zero twice and also to handle correctly ICMP echo reply duplicates. */
(theoretically possible with SMP) */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
if (atomic_dec_and_test(&ct->proto.icmp.count))
nf_ct_kill_acct(ct, ctinfo, skb);
} else {
atomic_inc(&ct->proto.icmp.count);
nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, ct);
nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout); nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout);
}
return NF_ACCEPT; return NF_ACCEPT;
} }
...@@ -132,7 +124,6 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -132,7 +124,6 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
type + 128); type + 128);
return false; return false;
} }
atomic_set(&ct->proto.icmp.count, 0);
return true; return true;
} }
......
...@@ -917,6 +917,19 @@ config NETFILTER_XT_MATCH_U32 ...@@ -917,6 +917,19 @@ config NETFILTER_XT_MATCH_U32
Details and examples are in the kernel module source. Details and examples are in the kernel module source.
config NETFILTER_XT_MATCH_OSF
tristate '"osf" Passive OS fingerprint match'
depends on NETFILTER_ADVANCED && NETFILTER_NETLINK
help
This option selects the Passive OS Fingerprinting match module
that allows to passively match the remote operating system by
analyzing incoming TCP SYN packets.
Rules and loading software can be downloaded from
http://www.ioremap.net/projects/osf
To compile it as a module, choose M here. If unsure, say N.
endif # NETFILTER_XTABLES endif # NETFILTER_XTABLES
endmenu endmenu
......
...@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o ...@@ -77,6 +77,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o obj-$(CONFIG_NETFILTER_XT_MATCH_MAC) += xt_mac.o
obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += xt_mark.o
obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o
obj-$(CONFIG_NETFILTER_XT_MATCH_OSF) += xt_osf.o
obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o
obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o
......
...@@ -398,11 +398,7 @@ __nf_conntrack_confirm(struct sk_buff *skb) ...@@ -398,11 +398,7 @@ __nf_conntrack_confirm(struct sk_buff *skb)
help = nfct_help(ct); help = nfct_help(ct);
if (help && help->helper) if (help && help->helper)
nf_conntrack_event_cache(IPCT_HELPER, ct); nf_conntrack_event_cache(IPCT_HELPER, ct);
#ifdef CONFIG_NF_NAT_NEEDED
if (test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status) ||
test_bit(IPS_DST_NAT_DONE_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_NATINFO, ct);
#endif
nf_conntrack_event_cache(master_ct(ct) ? nf_conntrack_event_cache(master_ct(ct) ?
IPCT_RELATED : IPCT_NEW, ct); IPCT_RELATED : IPCT_NEW, ct);
return NF_ACCEPT; return NF_ACCEPT;
...@@ -523,6 +519,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, ...@@ -523,6 +519,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
spin_lock_init(&ct->lock);
atomic_set(&ct->ct_general.use, 1); atomic_set(&ct->ct_general.use, 1);
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig; ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl; ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
...@@ -807,8 +804,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, ...@@ -807,8 +804,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
unsigned long extra_jiffies, unsigned long extra_jiffies,
int do_acct) int do_acct)
{ {
int event = 0;
NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct); NF_CT_ASSERT(ct->timeout.data == (unsigned long)ct);
NF_CT_ASSERT(skb); NF_CT_ASSERT(skb);
...@@ -821,7 +816,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, ...@@ -821,7 +816,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
/* If not in hash table, timer will not be active yet */ /* If not in hash table, timer will not be active yet */
if (!nf_ct_is_confirmed(ct)) { if (!nf_ct_is_confirmed(ct)) {
ct->timeout.expires = extra_jiffies; ct->timeout.expires = extra_jiffies;
event = IPCT_REFRESH;
} else { } else {
unsigned long newtime = jiffies + extra_jiffies; unsigned long newtime = jiffies + extra_jiffies;
...@@ -832,7 +826,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, ...@@ -832,7 +826,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
&& del_timer(&ct->timeout)) { && del_timer(&ct->timeout)) {
ct->timeout.expires = newtime; ct->timeout.expires = newtime;
add_timer(&ct->timeout); add_timer(&ct->timeout);
event = IPCT_REFRESH;
} }
} }
...@@ -849,10 +842,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, ...@@ -849,10 +842,6 @@ void __nf_ct_refresh_acct(struct nf_conn *ct,
} }
spin_unlock_bh(&nf_conntrack_lock); spin_unlock_bh(&nf_conntrack_lock);
/* must be unlocked when calling event cache */
if (event)
nf_conntrack_event_cache(event, ct);
} }
EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct); EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct);
...@@ -1001,7 +990,7 @@ struct __nf_ct_flush_report { ...@@ -1001,7 +990,7 @@ struct __nf_ct_flush_report {
int report; int report;
}; };
static int kill_all(struct nf_conn *i, void *data) static int kill_report(struct nf_conn *i, void *data)
{ {
struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data; struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data;
...@@ -1013,6 +1002,11 @@ static int kill_all(struct nf_conn *i, void *data) ...@@ -1013,6 +1002,11 @@ static int kill_all(struct nf_conn *i, void *data)
return 1; return 1;
} }
static int kill_all(struct nf_conn *i, void *data)
{
return 1;
}
void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size) void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size)
{ {
if (vmalloced) if (vmalloced)
...@@ -1023,15 +1017,15 @@ void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size) ...@@ -1023,15 +1017,15 @@ void nf_ct_free_hashtable(void *hash, int vmalloced, unsigned int size)
} }
EXPORT_SYMBOL_GPL(nf_ct_free_hashtable); EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
void nf_conntrack_flush(struct net *net, u32 pid, int report) void nf_conntrack_flush_report(struct net *net, u32 pid, int report)
{ {
struct __nf_ct_flush_report fr = { struct __nf_ct_flush_report fr = {
.pid = pid, .pid = pid,
.report = report, .report = report,
}; };
nf_ct_iterate_cleanup(net, kill_all, &fr); nf_ct_iterate_cleanup(net, kill_report, &fr);
} }
EXPORT_SYMBOL_GPL(nf_conntrack_flush); EXPORT_SYMBOL_GPL(nf_conntrack_flush_report);
static void nf_conntrack_cleanup_init_net(void) static void nf_conntrack_cleanup_init_net(void)
{ {
...@@ -1045,7 +1039,7 @@ static void nf_conntrack_cleanup_net(struct net *net) ...@@ -1045,7 +1039,7 @@ static void nf_conntrack_cleanup_net(struct net *net)
nf_ct_event_cache_flush(net); nf_ct_event_cache_flush(net);
nf_conntrack_ecache_fini(net); nf_conntrack_ecache_fini(net);
i_see_dead_people: i_see_dead_people:
nf_conntrack_flush(net, 0, 0); nf_ct_iterate_cleanup(net, kill_all, NULL);
if (atomic_read(&net->ct.count) != 0) { if (atomic_read(&net->ct.count) != 0) {
schedule(); schedule();
goto i_see_dead_people; goto i_see_dead_people;
......
...@@ -16,24 +16,32 @@ ...@@ -16,24 +16,32 @@
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/notifier.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
ATOMIC_NOTIFIER_HEAD(nf_conntrack_chain); static DEFINE_MUTEX(nf_ct_ecache_mutex);
EXPORT_SYMBOL_GPL(nf_conntrack_chain);
ATOMIC_NOTIFIER_HEAD(nf_ct_expect_chain); struct nf_ct_event_notifier *nf_conntrack_event_cb __read_mostly;
EXPORT_SYMBOL_GPL(nf_ct_expect_chain); EXPORT_SYMBOL_GPL(nf_conntrack_event_cb);
struct nf_exp_event_notifier *nf_expect_event_cb __read_mostly;
EXPORT_SYMBOL_GPL(nf_expect_event_cb);
/* deliver cached events and clear cache entry - must be called with locally /* deliver cached events and clear cache entry - must be called with locally
* disabled softirqs */ * disabled softirqs */
static inline void static inline void
__nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache) __nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
{ {
struct nf_ct_event_notifier *notify;
rcu_read_lock();
notify = rcu_dereference(nf_conntrack_event_cb);
if (notify == NULL)
goto out_unlock;
if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct) if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct)
&& ecache->events) { && ecache->events) {
struct nf_ct_event item = { struct nf_ct_event item = {
...@@ -42,14 +50,15 @@ __nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache) ...@@ -42,14 +50,15 @@ __nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
.report = 0 .report = 0
}; };
atomic_notifier_call_chain(&nf_conntrack_chain, notify->fcn(ecache->events, &item);
ecache->events,
&item);
} }
ecache->events = 0; ecache->events = 0;
nf_ct_put(ecache->ct); nf_ct_put(ecache->ct);
ecache->ct = NULL; ecache->ct = NULL;
out_unlock:
rcu_read_unlock();
} }
/* Deliver all cached events for a particular conntrack. This is called /* Deliver all cached events for a particular conntrack. This is called
...@@ -111,26 +120,68 @@ void nf_conntrack_ecache_fini(struct net *net) ...@@ -111,26 +120,68 @@ void nf_conntrack_ecache_fini(struct net *net)
free_percpu(net->ct.ecache); free_percpu(net->ct.ecache);
} }
int nf_conntrack_register_notifier(struct notifier_block *nb) int nf_conntrack_register_notifier(struct nf_ct_event_notifier *new)
{ {
return atomic_notifier_chain_register(&nf_conntrack_chain, nb); int ret = 0;
struct nf_ct_event_notifier *notify;
mutex_lock(&nf_ct_ecache_mutex);
notify = rcu_dereference(nf_conntrack_event_cb);
if (notify != NULL) {
ret = -EBUSY;
goto out_unlock;
}
rcu_assign_pointer(nf_conntrack_event_cb, new);
mutex_unlock(&nf_ct_ecache_mutex);
return ret;
out_unlock:
mutex_unlock(&nf_ct_ecache_mutex);
return ret;
} }
EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
int nf_conntrack_unregister_notifier(struct notifier_block *nb) void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *new)
{ {
return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb); struct nf_ct_event_notifier *notify;
mutex_lock(&nf_ct_ecache_mutex);
notify = rcu_dereference(nf_conntrack_event_cb);
BUG_ON(notify != new);
rcu_assign_pointer(nf_conntrack_event_cb, NULL);
mutex_unlock(&nf_ct_ecache_mutex);
} }
EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
int nf_ct_expect_register_notifier(struct notifier_block *nb) int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *new)
{ {
return atomic_notifier_chain_register(&nf_ct_expect_chain, nb); int ret = 0;
struct nf_exp_event_notifier *notify;
mutex_lock(&nf_ct_ecache_mutex);
notify = rcu_dereference(nf_expect_event_cb);
if (notify != NULL) {
ret = -EBUSY;
goto out_unlock;
}
rcu_assign_pointer(nf_expect_event_cb, new);
mutex_unlock(&nf_ct_ecache_mutex);
return ret;
out_unlock:
mutex_unlock(&nf_ct_ecache_mutex);
return ret;
} }
EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier); EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier);
int nf_ct_expect_unregister_notifier(struct notifier_block *nb) void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *new)
{ {
return atomic_notifier_chain_unregister(&nf_ct_expect_chain, nb); struct nf_exp_event_notifier *notify;
mutex_lock(&nf_ct_ecache_mutex);
notify = rcu_dereference(nf_expect_event_cb);
BUG_ON(notify != new);
rcu_assign_pointer(nf_expect_event_cb, NULL);
mutex_unlock(&nf_ct_ecache_mutex);
} }
EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier); EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
...@@ -338,11 +338,9 @@ static void update_nl_seq(struct nf_conn *ct, u32 nl_seq, ...@@ -338,11 +338,9 @@ static void update_nl_seq(struct nf_conn *ct, u32 nl_seq,
if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) { if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) {
info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq; info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;
nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, ct);
} else if (oldest != NUM_SEQ_TO_REMEMBER && } else if (oldest != NUM_SEQ_TO_REMEMBER &&
after(nl_seq, info->seq_aft_nl[dir][oldest])) { after(nl_seq, info->seq_aft_nl[dir][oldest])) {
info->seq_aft_nl[dir][oldest] = nl_seq; info->seq_aft_nl[dir][oldest] = nl_seq;
nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, ct);
} }
} }
......
This diff is collapsed.
...@@ -25,8 +25,6 @@ ...@@ -25,8 +25,6 @@
#include <net/netfilter/nf_conntrack_ecache.h> #include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_log.h> #include <net/netfilter/nf_log.h>
static DEFINE_RWLOCK(dccp_lock);
/* Timeouts are based on values from RFC4340: /* Timeouts are based on values from RFC4340:
* *
* - REQUEST: * - REQUEST:
...@@ -492,7 +490,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -492,7 +490,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
return NF_ACCEPT; return NF_ACCEPT;
} }
write_lock_bh(&dccp_lock); spin_lock_bh(&ct->lock);
role = ct->proto.dccp.role[dir]; role = ct->proto.dccp.role[dir];
old_state = ct->proto.dccp.state; old_state = ct->proto.dccp.state;
...@@ -536,13 +534,13 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -536,13 +534,13 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
ct->proto.dccp.last_dir = dir; ct->proto.dccp.last_dir = dir;
ct->proto.dccp.last_pkt = type; ct->proto.dccp.last_pkt = type;
write_unlock_bh(&dccp_lock); spin_unlock_bh(&ct->lock);
if (LOG_INVALID(net, IPPROTO_DCCP)) if (LOG_INVALID(net, IPPROTO_DCCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL, nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_dccp: invalid packet ignored "); "nf_ct_dccp: invalid packet ignored ");
return NF_ACCEPT; return NF_ACCEPT;
case CT_DCCP_INVALID: case CT_DCCP_INVALID:
write_unlock_bh(&dccp_lock); spin_unlock_bh(&ct->lock);
if (LOG_INVALID(net, IPPROTO_DCCP)) if (LOG_INVALID(net, IPPROTO_DCCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL, nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_dccp: invalid state transition "); "nf_ct_dccp: invalid state transition ");
...@@ -552,7 +550,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -552,7 +550,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
ct->proto.dccp.last_dir = dir; ct->proto.dccp.last_dir = dir;
ct->proto.dccp.last_pkt = type; ct->proto.dccp.last_pkt = type;
ct->proto.dccp.state = new_state; ct->proto.dccp.state = new_state;
write_unlock_bh(&dccp_lock); spin_unlock_bh(&ct->lock);
if (new_state != old_state) if (new_state != old_state)
nf_conntrack_event_cache(IPCT_PROTOINFO, ct); nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
...@@ -621,36 +619,39 @@ static int dccp_print_tuple(struct seq_file *s, ...@@ -621,36 +619,39 @@ static int dccp_print_tuple(struct seq_file *s,
ntohs(tuple->dst.u.dccp.port)); ntohs(tuple->dst.u.dccp.port));
} }
static int dccp_print_conntrack(struct seq_file *s, const struct nf_conn *ct) static int dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
{ {
return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]); return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]);
} }
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
const struct nf_conn *ct) struct nf_conn *ct)
{ {
struct nlattr *nest_parms; struct nlattr *nest_parms;
read_lock_bh(&dccp_lock); spin_lock_bh(&ct->lock);
nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP | NLA_F_NESTED); nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP | NLA_F_NESTED);
if (!nest_parms) if (!nest_parms)
goto nla_put_failure; goto nla_put_failure;
NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state); NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state);
NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_ROLE, NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_ROLE,
ct->proto.dccp.role[IP_CT_DIR_ORIGINAL]); ct->proto.dccp.role[IP_CT_DIR_ORIGINAL]);
NLA_PUT_BE64(skb, CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
cpu_to_be64(ct->proto.dccp.handshake_seq));
nla_nest_end(skb, nest_parms); nla_nest_end(skb, nest_parms);
read_unlock_bh(&dccp_lock); spin_unlock_bh(&ct->lock);
return 0; return 0;
nla_put_failure: nla_put_failure:
read_unlock_bh(&dccp_lock); spin_unlock_bh(&ct->lock);
return -1; return -1;
} }
static const struct nla_policy dccp_nla_policy[CTA_PROTOINFO_DCCP_MAX + 1] = { static const struct nla_policy dccp_nla_policy[CTA_PROTOINFO_DCCP_MAX + 1] = {
[CTA_PROTOINFO_DCCP_STATE] = { .type = NLA_U8 }, [CTA_PROTOINFO_DCCP_STATE] = { .type = NLA_U8 },
[CTA_PROTOINFO_DCCP_ROLE] = { .type = NLA_U8 }, [CTA_PROTOINFO_DCCP_ROLE] = { .type = NLA_U8 },
[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ] = { .type = NLA_U64 },
}; };
static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct) static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
...@@ -674,7 +675,7 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct) ...@@ -674,7 +675,7 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
return -EINVAL; return -EINVAL;
} }
write_lock_bh(&dccp_lock); spin_lock_bh(&ct->lock);
ct->proto.dccp.state = nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]); ct->proto.dccp.state = nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]);
if (nla_get_u8(tb[CTA_PROTOINFO_DCCP_ROLE]) == CT_DCCP_ROLE_CLIENT) { if (nla_get_u8(tb[CTA_PROTOINFO_DCCP_ROLE]) == CT_DCCP_ROLE_CLIENT) {
ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_CLIENT; ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_CLIENT;
...@@ -683,7 +684,11 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct) ...@@ -683,7 +684,11 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_SERVER; ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_SERVER;
ct->proto.dccp.role[IP_CT_DIR_REPLY] = CT_DCCP_ROLE_CLIENT; ct->proto.dccp.role[IP_CT_DIR_REPLY] = CT_DCCP_ROLE_CLIENT;
} }
write_unlock_bh(&dccp_lock); if (tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ]) {
ct->proto.dccp.handshake_seq =
be64_to_cpu(nla_get_be64(tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ]));
}
spin_unlock_bh(&ct->lock);
return 0; return 0;
} }
......
...@@ -219,8 +219,7 @@ static int gre_print_tuple(struct seq_file *s, ...@@ -219,8 +219,7 @@ static int gre_print_tuple(struct seq_file *s,
} }
/* print private data for conntrack */ /* print private data for conntrack */
static int gre_print_conntrack(struct seq_file *s, static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
const struct nf_conn *ct)
{ {
return seq_printf(s, "timeout=%u, stream_timeout=%u ", return seq_printf(s, "timeout=%u, stream_timeout=%u ",
(ct->proto.gre.timeout / HZ), (ct->proto.gre.timeout / HZ),
......
...@@ -25,9 +25,6 @@ ...@@ -25,9 +25,6 @@
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_ecache.h> #include <net/netfilter/nf_conntrack_ecache.h>
/* Protects ct->proto.sctp */
static DEFINE_RWLOCK(sctp_lock);
/* FIXME: Examine ipfilter's timeouts and conntrack transitions more /* FIXME: Examine ipfilter's timeouts and conntrack transitions more
closely. They're more complex. --RR closely. They're more complex. --RR
...@@ -164,13 +161,13 @@ static int sctp_print_tuple(struct seq_file *s, ...@@ -164,13 +161,13 @@ static int sctp_print_tuple(struct seq_file *s,
} }
/* Print out the private part of the conntrack. */ /* Print out the private part of the conntrack. */
static int sctp_print_conntrack(struct seq_file *s, const struct nf_conn *ct) static int sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
{ {
enum sctp_conntrack state; enum sctp_conntrack state;
read_lock_bh(&sctp_lock); spin_lock_bh(&ct->lock);
state = ct->proto.sctp.state; state = ct->proto.sctp.state;
read_unlock_bh(&sctp_lock); spin_unlock_bh(&ct->lock);
return seq_printf(s, "%s ", sctp_conntrack_names[state]); return seq_printf(s, "%s ", sctp_conntrack_names[state]);
} }
...@@ -318,7 +315,7 @@ static int sctp_packet(struct nf_conn *ct, ...@@ -318,7 +315,7 @@ static int sctp_packet(struct nf_conn *ct,
} }
old_state = new_state = SCTP_CONNTRACK_NONE; old_state = new_state = SCTP_CONNTRACK_NONE;
write_lock_bh(&sctp_lock); spin_lock_bh(&ct->lock);
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) { for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
/* Special cases of Verification tag check (Sec 8.5.1) */ /* Special cases of Verification tag check (Sec 8.5.1) */
if (sch->type == SCTP_CID_INIT) { if (sch->type == SCTP_CID_INIT) {
...@@ -371,7 +368,7 @@ static int sctp_packet(struct nf_conn *ct, ...@@ -371,7 +368,7 @@ static int sctp_packet(struct nf_conn *ct,
if (old_state != new_state) if (old_state != new_state)
nf_conntrack_event_cache(IPCT_PROTOINFO, ct); nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
} }
write_unlock_bh(&sctp_lock); spin_unlock_bh(&ct->lock);
nf_ct_refresh_acct(ct, ctinfo, skb, sctp_timeouts[new_state]); nf_ct_refresh_acct(ct, ctinfo, skb, sctp_timeouts[new_state]);
...@@ -386,7 +383,7 @@ static int sctp_packet(struct nf_conn *ct, ...@@ -386,7 +383,7 @@ static int sctp_packet(struct nf_conn *ct,
return NF_ACCEPT; return NF_ACCEPT;
out_unlock: out_unlock:
write_unlock_bh(&sctp_lock); spin_unlock_bh(&ct->lock);
out: out:
return -NF_ACCEPT; return -NF_ACCEPT;
} }
...@@ -469,11 +466,11 @@ static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -469,11 +466,11 @@ static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
#include <linux/netfilter/nfnetlink_conntrack.h> #include <linux/netfilter/nfnetlink_conntrack.h>
static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
const struct nf_conn *ct) struct nf_conn *ct)
{ {
struct nlattr *nest_parms; struct nlattr *nest_parms;
read_lock_bh(&sctp_lock); spin_lock_bh(&ct->lock);
nest_parms = nla_nest_start(skb, CTA_PROTOINFO_SCTP | NLA_F_NESTED); nest_parms = nla_nest_start(skb, CTA_PROTOINFO_SCTP | NLA_F_NESTED);
if (!nest_parms) if (!nest_parms)
goto nla_put_failure; goto nla_put_failure;
...@@ -488,14 +485,14 @@ static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, ...@@ -488,14 +485,14 @@ static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
CTA_PROTOINFO_SCTP_VTAG_REPLY, CTA_PROTOINFO_SCTP_VTAG_REPLY,
ct->proto.sctp.vtag[IP_CT_DIR_REPLY]); ct->proto.sctp.vtag[IP_CT_DIR_REPLY]);
read_unlock_bh(&sctp_lock); spin_unlock_bh(&ct->lock);
nla_nest_end(skb, nest_parms); nla_nest_end(skb, nest_parms);
return 0; return 0;
nla_put_failure: nla_put_failure:
read_unlock_bh(&sctp_lock); spin_unlock_bh(&ct->lock);
return -1; return -1;
} }
...@@ -527,13 +524,13 @@ static int nlattr_to_sctp(struct nlattr *cda[], struct nf_conn *ct) ...@@ -527,13 +524,13 @@ static int nlattr_to_sctp(struct nlattr *cda[], struct nf_conn *ct)
!tb[CTA_PROTOINFO_SCTP_VTAG_REPLY]) !tb[CTA_PROTOINFO_SCTP_VTAG_REPLY])
return -EINVAL; return -EINVAL;
write_lock_bh(&sctp_lock); spin_lock_bh(&ct->lock);
ct->proto.sctp.state = nla_get_u8(tb[CTA_PROTOINFO_SCTP_STATE]); ct->proto.sctp.state = nla_get_u8(tb[CTA_PROTOINFO_SCTP_STATE]);
ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] =
nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL]); nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL]);
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ct->proto.sctp.vtag[IP_CT_DIR_REPLY] =
nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_REPLY]); nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_REPLY]);
write_unlock_bh(&sctp_lock); spin_unlock_bh(&ct->lock);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -204,10 +204,10 @@ int nf_queue(struct sk_buff *skb, ...@@ -204,10 +204,10 @@ int nf_queue(struct sk_buff *skb,
queuenum); queuenum);
switch (pf) { switch (pf) {
case AF_INET: case NFPROTO_IPV4:
skb->protocol = htons(ETH_P_IP); skb->protocol = htons(ETH_P_IP);
break; break;
case AF_INET6: case NFPROTO_IPV6:
skb->protocol = htons(ETH_P_IPV6); skb->protocol = htons(ETH_P_IPV6);
break; break;
} }
......
...@@ -107,9 +107,10 @@ int nfnetlink_has_listeners(unsigned int group) ...@@ -107,9 +107,10 @@ int nfnetlink_has_listeners(unsigned int group)
} }
EXPORT_SYMBOL_GPL(nfnetlink_has_listeners); EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo) int nfnetlink_send(struct sk_buff *skb, u32 pid,
unsigned group, int echo, gfp_t flags)
{ {
return nlmsg_notify(nfnl, skb, pid, group, echo, gfp_any()); return nlmsg_notify(nfnl, skb, pid, group, echo, flags);
} }
EXPORT_SYMBOL_GPL(nfnetlink_send); EXPORT_SYMBOL_GPL(nfnetlink_send);
...@@ -136,7 +137,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -136,7 +137,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EPERM; return -EPERM;
/* All the messages must at least contain nfgenmsg */ /* All the messages must at least contain nfgenmsg */
if (nlh->nlmsg_len < NLMSG_SPACE(sizeof(struct nfgenmsg))) if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))
return 0; return 0;
type = nlh->nlmsg_type; type = nlh->nlmsg_type;
...@@ -160,19 +161,14 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -160,19 +161,14 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
u_int16_t attr_count = ss->cb[cb_id].attr_count; struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
struct nlattr *cda[attr_count+1]; struct nlattr *attr = (void *)nlh + min_len;
int attrlen = nlh->nlmsg_len - min_len;
if (likely(nlh->nlmsg_len >= min_len)) { err = nla_parse(cda, ss->cb[cb_id].attr_count,
struct nlattr *attr = (void *)nlh + NLMSG_ALIGN(min_len); attr, attrlen, ss->cb[cb_id].policy);
int attrlen = nlh->nlmsg_len - NLMSG_ALIGN(min_len);
err = nla_parse(cda, attr_count, attr, attrlen,
ss->cb[cb_id].policy);
if (err < 0) if (err < 0)
return err; return err;
} else
return -EINVAL;
err = nc->call(nfnl, skb, nlh, cda); err = nc->call(nfnl, skb, nlh, cda);
if (err == -EAGAIN) if (err == -EAGAIN)
......
...@@ -329,6 +329,32 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target, ...@@ -329,6 +329,32 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target,
} }
EXPORT_SYMBOL_GPL(xt_find_revision); EXPORT_SYMBOL_GPL(xt_find_revision);
static char *textify_hooks(char *buf, size_t size, unsigned int mask)
{
static const char *const names[] = {
"PREROUTING", "INPUT", "FORWARD",
"OUTPUT", "POSTROUTING", "BROUTING",
};
unsigned int i;
char *p = buf;
bool np = false;
int res;
*p = '\0';
for (i = 0; i < ARRAY_SIZE(names); ++i) {
if (!(mask & (1 << i)))
continue;
res = snprintf(p, size, "%s%s", np ? "/" : "", names[i]);
if (res > 0) {
size -= res;
p += res;
}
np = true;
}
return buf;
}
int xt_check_match(struct xt_mtchk_param *par, int xt_check_match(struct xt_mtchk_param *par,
unsigned int size, u_int8_t proto, bool inv_proto) unsigned int size, u_int8_t proto, bool inv_proto)
{ {
...@@ -351,9 +377,13 @@ int xt_check_match(struct xt_mtchk_param *par, ...@@ -351,9 +377,13 @@ int xt_check_match(struct xt_mtchk_param *par,
return -EINVAL; return -EINVAL;
} }
if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) { if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
printk("%s_tables: %s match: bad hook_mask %#x/%#x\n", char used[64], allow[64];
printk("%s_tables: %s match: used from hooks %s, but only "
"valid from %s\n",
xt_prefix[par->family], par->match->name, xt_prefix[par->family], par->match->name,
par->hook_mask, par->match->hooks); textify_hooks(used, sizeof(used), par->hook_mask),
textify_hooks(allow, sizeof(allow), par->match->hooks));
return -EINVAL; return -EINVAL;
} }
if (par->match->proto && (par->match->proto != proto || inv_proto)) { if (par->match->proto && (par->match->proto != proto || inv_proto)) {
...@@ -497,9 +527,13 @@ int xt_check_target(struct xt_tgchk_param *par, ...@@ -497,9 +527,13 @@ int xt_check_target(struct xt_tgchk_param *par,
return -EINVAL; return -EINVAL;
} }
if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) { if (par->target->hooks && (par->hook_mask & ~par->target->hooks) != 0) {
printk("%s_tables: %s target: bad hook_mask %#x/%#x\n", char used[64], allow[64];
printk("%s_tables: %s target: used from hooks %s, but only "
"usable from %s\n",
xt_prefix[par->family], par->target->name, xt_prefix[par->family], par->target->name,
par->hook_mask, par->target->hooks); textify_hooks(used, sizeof(used), par->hook_mask),
textify_hooks(allow, sizeof(allow), par->target->hooks));
return -EINVAL; return -EINVAL;
} }
if (par->target->proto && (par->target->proto != proto || inv_proto)) { if (par->target->proto && (par->target->proto != proto || inv_proto)) {
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/jhash.h>
#include <linux/netfilter.h> #include <linux/netfilter.h>
#include <linux/netfilter_arp.h> #include <linux/netfilter_arp.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
...@@ -23,6 +27,8 @@ MODULE_ALIAS("ipt_NFQUEUE"); ...@@ -23,6 +27,8 @@ MODULE_ALIAS("ipt_NFQUEUE");
MODULE_ALIAS("ip6t_NFQUEUE"); MODULE_ALIAS("ip6t_NFQUEUE");
MODULE_ALIAS("arpt_NFQUEUE"); MODULE_ALIAS("arpt_NFQUEUE");
static u32 jhash_initval __read_mostly;
static unsigned int static unsigned int
nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par) nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par)
{ {
...@@ -31,32 +37,105 @@ nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par) ...@@ -31,32 +37,105 @@ nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par)
return NF_QUEUE_NR(tinfo->queuenum); return NF_QUEUE_NR(tinfo->queuenum);
} }
static u32 hash_v4(const struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
u32 ipaddr;
/* packets in either direction go into same queue */
ipaddr = iph->saddr ^ iph->daddr;
return jhash_2words(ipaddr, iph->protocol, jhash_initval);
}
static unsigned int
nfqueue_tg4_v1(struct sk_buff *skb, const struct xt_target_param *par)
{
const struct xt_NFQ_info_v1 *info = par->targinfo;
u32 queue = info->queuenum;
if (info->queues_total > 1)
queue = hash_v4(skb) % info->queues_total + queue;
return NF_QUEUE_NR(queue);
}
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
static u32 hash_v6(const struct sk_buff *skb)
{
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
u32 addr[4];
addr[0] = ip6h->saddr.s6_addr32[0] ^ ip6h->daddr.s6_addr32[0];
addr[1] = ip6h->saddr.s6_addr32[1] ^ ip6h->daddr.s6_addr32[1];
addr[2] = ip6h->saddr.s6_addr32[2] ^ ip6h->daddr.s6_addr32[2];
addr[3] = ip6h->saddr.s6_addr32[3] ^ ip6h->daddr.s6_addr32[3];
return jhash2(addr, ARRAY_SIZE(addr), jhash_initval);
}
static unsigned int
nfqueue_tg6_v1(struct sk_buff *skb, const struct xt_target_param *par)
{
const struct xt_NFQ_info_v1 *info = par->targinfo;
u32 queue = info->queuenum;
if (info->queues_total > 1)
queue = hash_v6(skb) % info->queues_total + queue;
return NF_QUEUE_NR(queue);
}
#endif
static bool nfqueue_tg_v1_check(const struct xt_tgchk_param *par)
{
const struct xt_NFQ_info_v1 *info = par->targinfo;
u32 maxid;
if (info->queues_total == 0) {
pr_err("NFQUEUE: number of total queues is 0\n");
return false;
}
maxid = info->queues_total - 1 + info->queuenum;
if (maxid > 0xffff) {
pr_err("NFQUEUE: number of queues (%u) out of range (got %u)\n",
info->queues_total, maxid);
return false;
}
return true;
}
static struct xt_target nfqueue_tg_reg[] __read_mostly = { static struct xt_target nfqueue_tg_reg[] __read_mostly = {
{ {
.name = "NFQUEUE", .name = "NFQUEUE",
.family = NFPROTO_IPV4, .family = NFPROTO_UNSPEC,
.target = nfqueue_tg, .target = nfqueue_tg,
.targetsize = sizeof(struct xt_NFQ_info), .targetsize = sizeof(struct xt_NFQ_info),
.me = THIS_MODULE, .me = THIS_MODULE,
}, },
{ {
.name = "NFQUEUE", .name = "NFQUEUE",
.family = NFPROTO_IPV6, .revision = 1,
.target = nfqueue_tg, .family = NFPROTO_IPV4,
.targetsize = sizeof(struct xt_NFQ_info), .checkentry = nfqueue_tg_v1_check,
.target = nfqueue_tg4_v1,
.targetsize = sizeof(struct xt_NFQ_info_v1),
.me = THIS_MODULE, .me = THIS_MODULE,
}, },
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
{ {
.name = "NFQUEUE", .name = "NFQUEUE",
.family = NFPROTO_ARP, .revision = 1,
.target = nfqueue_tg, .family = NFPROTO_IPV6,
.targetsize = sizeof(struct xt_NFQ_info), .checkentry = nfqueue_tg_v1_check,
.target = nfqueue_tg6_v1,
.targetsize = sizeof(struct xt_NFQ_info_v1),
.me = THIS_MODULE, .me = THIS_MODULE,
}, },
#endif
}; };
static int __init nfqueue_tg_init(void) static int __init nfqueue_tg_init(void)
{ {
get_random_bytes(&jhash_initval, sizeof(jhash_initval));
return xt_register_targets(nfqueue_tg_reg, ARRAY_SIZE(nfqueue_tg_reg)); return xt_register_targets(nfqueue_tg_reg, ARRAY_SIZE(nfqueue_tg_reg));
} }
......
This diff is collapsed.
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <net/netfilter/nf_tproxy_core.h> #include <net/netfilter/nf_tproxy_core.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h> #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
#include <linux/netfilter/xt_socket.h>
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
#define XT_SOCKET_HAVE_CONNTRACK 1 #define XT_SOCKET_HAVE_CONNTRACK 1
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
...@@ -86,7 +88,8 @@ extract_icmp_fields(const struct sk_buff *skb, ...@@ -86,7 +88,8 @@ extract_icmp_fields(const struct sk_buff *skb,
static bool static bool
socket_mt(const struct sk_buff *skb, const struct xt_match_param *par) socket_match(const struct sk_buff *skb, const struct xt_match_param *par,
const struct xt_socket_mtinfo1 *info)
{ {
const struct iphdr *iph = ip_hdr(skb); const struct iphdr *iph = ip_hdr(skb);
struct udphdr _hdr, *hp = NULL; struct udphdr _hdr, *hp = NULL;
...@@ -141,10 +144,24 @@ socket_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -141,10 +144,24 @@ socket_mt(const struct sk_buff *skb, const struct xt_match_param *par)
sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
saddr, daddr, sport, dport, par->in, false); saddr, daddr, sport, dport, par->in, false);
if (sk != NULL) { if (sk != NULL) {
bool wildcard = (sk->sk_state != TCP_TIME_WAIT && inet_sk(sk)->rcv_saddr == 0); bool wildcard;
bool transparent = true;
/* Ignore sockets listening on INADDR_ANY */
wildcard = (sk->sk_state != TCP_TIME_WAIT &&
inet_sk(sk)->rcv_saddr == 0);
/* Ignore non-transparent sockets,
if XT_SOCKET_TRANSPARENT is used */
if (info && info->flags & XT_SOCKET_TRANSPARENT)
transparent = ((sk->sk_state != TCP_TIME_WAIT &&
inet_sk(sk)->transparent) ||
(sk->sk_state == TCP_TIME_WAIT &&
inet_twsk(sk)->tw_transparent));
nf_tproxy_put_sock(sk); nf_tproxy_put_sock(sk);
if (wildcard)
if (wildcard || !transparent)
sk = NULL; sk = NULL;
} }
...@@ -157,23 +174,47 @@ socket_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -157,23 +174,47 @@ socket_mt(const struct sk_buff *skb, const struct xt_match_param *par)
return (sk != NULL); return (sk != NULL);
} }
static struct xt_match socket_mt_reg __read_mostly = { static bool
socket_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
{
return socket_match(skb, par, NULL);
}
static bool
socket_mt_v1(const struct sk_buff *skb, const struct xt_match_param *par)
{
return socket_match(skb, par, par->matchinfo);
}
static struct xt_match socket_mt_reg[] __read_mostly = {
{
.name = "socket",
.revision = 0,
.family = NFPROTO_IPV4,
.match = socket_mt_v0,
.hooks = 1 << NF_INET_PRE_ROUTING,
.me = THIS_MODULE,
},
{
.name = "socket", .name = "socket",
.family = AF_INET, .revision = 1,
.match = socket_mt, .family = NFPROTO_IPV4,
.match = socket_mt_v1,
.matchsize = sizeof(struct xt_socket_mtinfo1),
.hooks = 1 << NF_INET_PRE_ROUTING, .hooks = 1 << NF_INET_PRE_ROUTING,
.me = THIS_MODULE, .me = THIS_MODULE,
},
}; };
static int __init socket_mt_init(void) static int __init socket_mt_init(void)
{ {
nf_defrag_ipv4_enable(); nf_defrag_ipv4_enable();
return xt_register_match(&socket_mt_reg); return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
} }
static void __exit socket_mt_exit(void) static void __exit socket_mt_exit(void)
{ {
xt_unregister_match(&socket_mt_reg); xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
} }
module_init(socket_mt_init); module_init(socket_mt_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment