Commit b5bb1438 authored by David S. Miller's avatar David S. Miller
parents bb4f92b3 1d45209d
...@@ -223,4 +223,7 @@ config LEDS_TRIGGER_DEFAULT_ON ...@@ -223,4 +223,7 @@ config LEDS_TRIGGER_DEFAULT_ON
This allows LEDs to be initialised in the ON state. This allows LEDs to be initialised in the ON state.
If unsure, say Y. If unsure, say Y.
comment "iptables trigger is under Netfilter config (LED target)"
depends on LEDS_TRIGGERS
endif # NEW_LEDS endif # NEW_LEDS
...@@ -7,16 +7,21 @@ header-y += xt_CLASSIFY.h ...@@ -7,16 +7,21 @@ header-y += xt_CLASSIFY.h
header-y += xt_CONNMARK.h header-y += xt_CONNMARK.h
header-y += xt_CONNSECMARK.h header-y += xt_CONNSECMARK.h
header-y += xt_DSCP.h header-y += xt_DSCP.h
header-y += xt_LED.h
header-y += xt_MARK.h header-y += xt_MARK.h
header-y += xt_NFLOG.h header-y += xt_NFLOG.h
header-y += xt_NFQUEUE.h header-y += xt_NFQUEUE.h
header-y += xt_RATEEST.h header-y += xt_RATEEST.h
header-y += xt_SECMARK.h header-y += xt_SECMARK.h
header-y += xt_TCPMSS.h header-y += xt_TCPMSS.h
header-y += xt_TCPOPTSTRIP.h
header-y += xt_TPROXY.h
header-y += xt_comment.h header-y += xt_comment.h
header-y += xt_connbytes.h header-y += xt_connbytes.h
header-y += xt_connlimit.h
header-y += xt_connmark.h header-y += xt_connmark.h
header-y += xt_conntrack.h header-y += xt_conntrack.h
header-y += xt_cluster.h
header-y += xt_dccp.h header-y += xt_dccp.h
header-y += xt_dscp.h header-y += xt_dscp.h
header-y += xt_esp.h header-y += xt_esp.h
...@@ -30,6 +35,7 @@ header-y += xt_mark.h ...@@ -30,6 +35,7 @@ header-y += xt_mark.h
header-y += xt_multiport.h header-y += xt_multiport.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_rateest.h header-y += xt_rateest.h
header-y += xt_realm.h header-y += xt_realm.h
header-y += xt_recent.h header-y += xt_recent.h
...@@ -39,6 +45,8 @@ header-y += xt_statistic.h ...@@ -39,6 +45,8 @@ header-y += xt_statistic.h
header-y += xt_string.h header-y += xt_string.h
header-y += xt_tcpmss.h header-y += xt_tcpmss.h
header-y += xt_tcpudp.h header-y += xt_tcpudp.h
header-y += xt_time.h
header-y += xt_u32.h
unifdef-y += nf_conntrack_common.h unifdef-y += nf_conntrack_common.h
unifdef-y += nf_conntrack_ftp.h unifdef-y += nf_conntrack_ftp.h
......
...@@ -76,6 +76,7 @@ extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); ...@@ -76,6 +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);
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);
extern void nfnl_lock(void); extern void nfnl_lock(void);
......
...@@ -349,23 +349,22 @@ struct xt_table ...@@ -349,23 +349,22 @@ struct xt_table
{ {
struct list_head list; struct list_head list;
/* A unique name... */
const char name[XT_TABLE_MAXNAMELEN];
/* What hooks you will enter on */ /* What hooks you will enter on */
unsigned int valid_hooks; unsigned int valid_hooks;
/* Lock for the curtain */ /* Lock for the curtain */
rwlock_t lock; struct mutex lock;
/* Man behind the curtain... */ /* Man behind the curtain... */
//struct ip6t_table_info *private; struct xt_table_info *private;
void *private;
/* Set this to THIS_MODULE if you are a module, otherwise NULL */ /* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me; struct module *me;
u_int8_t af; /* address/protocol family */ u_int8_t af; /* address/protocol family */
/* A unique name... */
const char name[XT_TABLE_MAXNAMELEN];
}; };
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
...@@ -386,7 +385,7 @@ struct xt_table_info ...@@ -386,7 +385,7 @@ struct xt_table_info
/* ipt_entry tables: one per CPU */ /* ipt_entry tables: one per CPU */
/* Note : this field MUST be the last one, see XT_TABLE_INFO_SZ */ /* Note : this field MUST be the last one, see XT_TABLE_INFO_SZ */
char *entries[1]; void *entries[1];
}; };
#define XT_TABLE_INFO_SZ (offsetof(struct xt_table_info, entries) \ #define XT_TABLE_INFO_SZ (offsetof(struct xt_table_info, entries) \
...@@ -433,6 +432,8 @@ extern void xt_proto_fini(struct net *net, u_int8_t af); ...@@ -433,6 +432,8 @@ extern void xt_proto_fini(struct net *net, u_int8_t af);
extern struct xt_table_info *xt_alloc_table_info(unsigned int size); extern struct xt_table_info *xt_alloc_table_info(unsigned int size);
extern void xt_free_table_info(struct xt_table_info *info); extern void xt_free_table_info(struct xt_table_info *info);
extern void xt_table_entry_swap_rcu(struct xt_table_info *old,
struct xt_table_info *new);
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#include <net/compat.h> #include <net/compat.h>
......
#ifndef _XT_LED_H
#define _XT_LED_H
struct xt_led_info {
char id[27]; /* Unique ID for this trigger in the LED class */
__u8 always_blink; /* Blink even if the LED is already on */
__u32 delay; /* Delay until LED is switched off after trigger */
/* Kernel data used in the module */
void *internal_data __attribute__((aligned(8)));
};
#endif /* _XT_LED_H */
#ifndef _XT_CLUSTER_MATCH_H
#define _XT_CLUSTER_MATCH_H
enum xt_cluster_flags {
XT_CLUSTER_F_INV = (1 << 0)
};
struct xt_cluster_match_info {
u_int32_t total_nodes;
u_int32_t node_mask;
u_int32_t hash_seed;
u_int32_t flags;
};
#endif /* _XT_CLUSTER_MATCH_H */
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
/* timings are in milliseconds. */ /* timings are in milliseconds. */
#define XT_LIMIT_SCALE 10000 #define XT_LIMIT_SCALE 10000
struct xt_limit_priv;
/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 /* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490
seconds, or one every 59 hours. */ seconds, or one every 59 hours. */
struct xt_rateinfo { struct xt_rateinfo {
...@@ -11,11 +13,10 @@ struct xt_rateinfo { ...@@ -11,11 +13,10 @@ struct xt_rateinfo {
u_int32_t burst; /* Period multiplier for upper limit. */ u_int32_t burst; /* Period multiplier for upper limit. */
/* Used internally by the kernel */ /* Used internally by the kernel */
unsigned long prev; unsigned long prev; /* moved to xt_limit_priv */
u_int32_t credit; u_int32_t credit; /* moved to xt_limit_priv */
u_int32_t credit_cap, cost; u_int32_t credit_cap, cost;
/* Ugly, ugly fucker. */ struct xt_limit_priv *master;
struct xt_rateinfo *master;
}; };
#endif /*_XT_RATE_H*/ #endif /*_XT_RATE_H*/
...@@ -6,13 +6,15 @@ enum xt_quota_flags { ...@@ -6,13 +6,15 @@ enum xt_quota_flags {
}; };
#define XT_QUOTA_MASK 0x1 #define XT_QUOTA_MASK 0x1
struct xt_quota_priv;
struct xt_quota_info { struct xt_quota_info {
u_int32_t flags; u_int32_t flags;
u_int32_t pad; u_int32_t pad;
/* Used internally by the kernel */ /* Used internally by the kernel */
aligned_u64 quota; aligned_u64 quota;
struct xt_quota_info *master; struct xt_quota_priv *master;
}; };
#endif /* _XT_QUOTA_H */ #endif /* _XT_QUOTA_H */
...@@ -13,6 +13,8 @@ enum xt_statistic_flags { ...@@ -13,6 +13,8 @@ enum xt_statistic_flags {
}; };
#define XT_STATISTIC_MASK 0x1 #define XT_STATISTIC_MASK 0x1
struct xt_statistic_priv;
struct xt_statistic_info { struct xt_statistic_info {
u_int16_t mode; u_int16_t mode;
u_int16_t flags; u_int16_t flags;
...@@ -23,11 +25,10 @@ struct xt_statistic_info { ...@@ -23,11 +25,10 @@ struct xt_statistic_info {
struct { struct {
u_int32_t every; u_int32_t every;
u_int32_t packet; u_int32_t packet;
/* Used internally by the kernel */ u_int32_t count; /* unused */
u_int32_t count;
} nth; } nth;
} u; } u;
struct xt_statistic_info *master __attribute__((aligned(8))); struct xt_statistic_priv *master __attribute__((aligned(8)));
}; };
#endif /* _XT_STATISTIC_H */ #endif /* _XT_STATISTIC_H */
...@@ -11,6 +11,7 @@ header-y += ip6t_length.h ...@@ -11,6 +11,7 @@ header-y += ip6t_length.h
header-y += ip6t_limit.h header-y += ip6t_limit.h
header-y += ip6t_mac.h header-y += ip6t_mac.h
header-y += ip6t_mark.h header-y += ip6t_mark.h
header-y += ip6t_mh.h
header-y += ip6t_multiport.h header-y += ip6t_multiport.h
header-y += ip6t_opts.h header-y += ip6t_opts.h
header-y += ip6t_owner.h header-y += ip6t_owner.h
......
...@@ -287,7 +287,7 @@ static inline int nf_ct_is_untracked(const struct sk_buff *skb) ...@@ -287,7 +287,7 @@ static inline int nf_ct_is_untracked(const struct sk_buff *skb)
extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
extern unsigned int nf_conntrack_htable_size; extern unsigned int nf_conntrack_htable_size;
extern int nf_conntrack_max; extern unsigned int nf_conntrack_max;
#define NF_CT_STAT_INC(net, count) \ #define NF_CT_STAT_INC(net, count) \
(per_cpu_ptr((net)->ct.stat, raw_smp_processor_id())->count++) (per_cpu_ptr((net)->ct.stat, raw_smp_processor_id())->count++)
......
...@@ -90,10 +90,7 @@ struct nf_conntrack_l4proto ...@@ -90,10 +90,7 @@ struct nf_conntrack_l4proto
struct module *me; struct module *me;
}; };
/* Existing built-in protocols */ /* Existing built-in generic protocol */
extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6;
extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4;
extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6;
extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic;
#define MAX_NF_CT_PROTO 256 #define MAX_NF_CT_PROTO 256
...@@ -101,11 +98,6 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; ...@@ -101,11 +98,6 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic;
extern struct nf_conntrack_l4proto * extern struct nf_conntrack_l4proto *
__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto); __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto);
extern struct nf_conntrack_l4proto *
nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t protocol);
extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p);
/* Protocol registration. */ /* Protocol registration. */
extern int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *proto); extern int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *proto);
extern void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *proto); extern void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *proto);
......
#ifndef _NF_LOG_H #ifndef _NF_LOG_H
#define _NF_LOG_H #define _NF_LOG_H
#include <linux/netfilter.h>
/* those NF_LOG_* defines and struct nf_loginfo are legacy definitios that will /* those NF_LOG_* defines and struct nf_loginfo are legacy definitios that will
* disappear once iptables is replaced with pkttables. Please DO NOT use them * disappear once iptables is replaced with pkttables. Please DO NOT use them
* for any new code! */ * for any new code! */
...@@ -40,12 +42,15 @@ struct nf_logger { ...@@ -40,12 +42,15 @@ struct nf_logger {
struct module *me; struct module *me;
nf_logfn *logfn; nf_logfn *logfn;
char *name; char *name;
struct list_head list[NFPROTO_NUMPROTO];
}; };
/* Function to register/unregister log function. */ /* Function to register/unregister log function. */
int nf_log_register(u_int8_t pf, const struct nf_logger *logger); int nf_log_register(u_int8_t pf, struct nf_logger *logger);
void nf_log_unregister(const struct nf_logger *logger); void nf_log_unregister(struct nf_logger *logger);
void nf_log_unregister_pf(u_int8_t pf);
int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger);
void nf_log_unbind_pf(u_int8_t pf);
/* Calls the registered backend logging function */ /* Calls the registered backend logging function */
void nf_log_packet(u_int8_t pf, void nf_log_packet(u_int8_t pf,
......
...@@ -46,7 +46,6 @@ static struct ebt_table broute_table = ...@@ -46,7 +46,6 @@ static struct ebt_table broute_table =
.name = "broute", .name = "broute",
.table = &initial_table, .table = &initial_table,
.valid_hooks = 1 << NF_BR_BROUTING, .valid_hooks = 1 << NF_BR_BROUTING,
.lock = __RW_LOCK_UNLOCKED(broute_table.lock),
.check = check, .check = check,
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
......
...@@ -55,7 +55,6 @@ static struct ebt_table frame_filter = ...@@ -55,7 +55,6 @@ static struct ebt_table frame_filter =
.name = "filter", .name = "filter",
.table = &initial_table, .table = &initial_table,
.valid_hooks = FILTER_VALID_HOOKS, .valid_hooks = FILTER_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(frame_filter.lock),
.check = check, .check = check,
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
......
...@@ -55,7 +55,6 @@ static struct ebt_table frame_nat = ...@@ -55,7 +55,6 @@ static struct ebt_table frame_nat =
.name = "nat", .name = "nat",
.table = &initial_table, .table = &initial_table,
.valid_hooks = NAT_VALID_HOOKS, .valid_hooks = NAT_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(frame_nat.lock),
.check = check, .check = check,
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
......
...@@ -31,7 +31,7 @@ config NF_CONNTRACK_PROC_COMPAT ...@@ -31,7 +31,7 @@ config NF_CONNTRACK_PROC_COMPAT
default y default y
help help
This option enables /proc and sysctl compatibility with the old This option enables /proc and sysctl compatibility with the old
layer 3 dependant connection tracking. This is needed to keep layer 3 dependent connection tracking. This is needed to keep
old programs that have not been adapted to the new names working. old programs that have not been adapted to the new names working.
If unsure, say Y. If unsure, say Y.
...@@ -95,11 +95,11 @@ config IP_NF_MATCH_ECN ...@@ -95,11 +95,11 @@ config IP_NF_MATCH_ECN
config IP_NF_MATCH_TTL config IP_NF_MATCH_TTL
tristate '"ttl" match support' tristate '"ttl" match support'
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED
help select NETFILTER_XT_MATCH_HL
This adds CONFIG_IP_NF_MATCH_TTL option, which enabled the user ---help---
to match packets by their TTL value. This is a backwards-compat option for the user's convenience
(e.g. when running oldconfig). It selects
To compile it as a module, choose M here. If unsure, say N. CONFIG_NETFILTER_XT_MATCH_HL.
# `filter', generic and specific targets # `filter', generic and specific targets
config IP_NF_FILTER config IP_NF_FILTER
...@@ -323,19 +323,13 @@ config IP_NF_TARGET_ECN ...@@ -323,19 +323,13 @@ config IP_NF_TARGET_ECN
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
config IP_NF_TARGET_TTL config IP_NF_TARGET_TTL
tristate 'TTL target support' tristate '"TTL" target support'
depends on IP_NF_MANGLE
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED
help select NETFILTER_XT_TARGET_HL
This option adds a `TTL' target, which enables the user to modify ---help---
the TTL value of the IP header. This is a backwards-compat option for the user's convenience
(e.g. when running oldconfig). It selects
While it is safe to decrement/lower the TTL, this target also enables CONFIG_NETFILTER_XT_TARGET_HL.
functionality to increment and set the TTL value of the IP header to
arbitrary values. This is EXTREMELY DANGEROUS since you can easily
create immortal packets that loop forever on the network.
To compile it as a module, choose M here. If unsure, say N.
# raw + specific targets # raw + specific targets
config IP_NF_RAW config IP_NF_RAW
......
...@@ -51,7 +51,6 @@ obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o ...@@ -51,7 +51,6 @@ obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o
obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o
obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o
obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o
obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
# targets # targets
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
...@@ -61,7 +60,6 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o ...@@ -61,7 +60,6 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o
obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
# generic ARP tables # generic ARP tables
......
...@@ -73,6 +73,36 @@ static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap, ...@@ -73,6 +73,36 @@ static inline int arp_devaddr_compare(const struct arpt_devaddr_info *ap,
return (ret != 0); return (ret != 0);
} }
/*
* Unfortunatly, _b and _mask are not aligned to an int (or long int)
* Some arches dont care, unrolling the loop is a win on them.
*/
static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
{
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
const unsigned long *a = (const unsigned long *)_a;
const unsigned long *b = (const unsigned long *)_b;
const unsigned long *mask = (const unsigned long *)_mask;
unsigned long ret;
ret = (a[0] ^ b[0]) & mask[0];
if (IFNAMSIZ > sizeof(unsigned long))
ret |= (a[1] ^ b[1]) & mask[1];
if (IFNAMSIZ > 2 * sizeof(unsigned long))
ret |= (a[2] ^ b[2]) & mask[2];
if (IFNAMSIZ > 3 * sizeof(unsigned long))
ret |= (a[3] ^ b[3]) & mask[3];
BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
#else
unsigned long ret = 0;
int i;
for (i = 0; i < IFNAMSIZ; i++)
ret |= (_a[i] ^ _b[i]) & _mask[i];
#endif
return ret;
}
/* Returns whether packet matches rule or not. */ /* Returns whether packet matches rule or not. */
static inline int arp_packet_match(const struct arphdr *arphdr, static inline int arp_packet_match(const struct arphdr *arphdr,
struct net_device *dev, struct net_device *dev,
...@@ -83,7 +113,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr, ...@@ -83,7 +113,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
const char *arpptr = (char *)(arphdr + 1); const char *arpptr = (char *)(arphdr + 1);
const char *src_devaddr, *tgt_devaddr; const char *src_devaddr, *tgt_devaddr;
__be32 src_ipaddr, tgt_ipaddr; __be32 src_ipaddr, tgt_ipaddr;
int i, ret; long ret;
#define FWINV(bool, invflg) ((bool) ^ !!(arpinfo->invflags & (invflg))) #define FWINV(bool, invflg) ((bool) ^ !!(arpinfo->invflags & (invflg)))
...@@ -156,10 +186,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr, ...@@ -156,10 +186,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
} }
/* Look for ifname matches. */ /* Look for ifname matches. */
for (i = 0, ret = 0; i < IFNAMSIZ; i++) { ret = ifname_compare(indev, arpinfo->iniface, arpinfo->iniface_mask);
ret |= (indev[i] ^ arpinfo->iniface[i])
& arpinfo->iniface_mask[i];
}
if (FWINV(ret != 0, ARPT_INV_VIA_IN)) { if (FWINV(ret != 0, ARPT_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n", dprintf("VIA in mismatch (%s vs %s).%s\n",
...@@ -168,10 +195,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr, ...@@ -168,10 +195,7 @@ static inline int arp_packet_match(const struct arphdr *arphdr,
return 0; return 0;
} }
for (i = 0, ret = 0; i < IFNAMSIZ; i++) { ret = ifname_compare(outdev, arpinfo->outiface, arpinfo->outiface_mask);
ret |= (outdev[i] ^ arpinfo->outiface[i])
& arpinfo->outiface_mask[i];
}
if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) { if (FWINV(ret != 0, ARPT_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n", dprintf("VIA out mismatch (%s vs %s).%s\n",
...@@ -221,7 +245,7 @@ unsigned int arpt_do_table(struct sk_buff *skb, ...@@ -221,7 +245,7 @@ unsigned int arpt_do_table(struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
struct xt_table *table) struct xt_table *table)
{ {
static const char nulldevname[IFNAMSIZ]; static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
unsigned int verdict = NF_DROP; unsigned int verdict = NF_DROP;
const struct arphdr *arp; const struct arphdr *arp;
bool hotdrop = false; bool hotdrop = false;
...@@ -237,9 +261,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, ...@@ -237,9 +261,10 @@ unsigned int arpt_do_table(struct sk_buff *skb,
indev = in ? in->name : nulldevname; indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname; outdev = out ? out->name : nulldevname;
read_lock_bh(&table->lock); rcu_read_lock();
private = table->private; private = rcu_dereference(table->private);
table_base = (void *)private->entries[smp_processor_id()]; table_base = rcu_dereference(private->entries[smp_processor_id()]);
e = get_entry(table_base, private->hook_entry[hook]); e = get_entry(table_base, private->hook_entry[hook]);
back = get_entry(table_base, private->underflow[hook]); back = get_entry(table_base, private->underflow[hook]);
...@@ -311,7 +336,8 @@ unsigned int arpt_do_table(struct sk_buff *skb, ...@@ -311,7 +336,8 @@ unsigned int arpt_do_table(struct sk_buff *skb,
e = (void *)e + e->next_offset; e = (void *)e + e->next_offset;
} }
} while (!hotdrop); } while (!hotdrop);
read_unlock_bh(&table->lock);
rcu_read_unlock();
if (hotdrop) if (hotdrop)
return NF_DROP; return NF_DROP;
...@@ -714,11 +740,65 @@ static void get_counters(const struct xt_table_info *t, ...@@ -714,11 +740,65 @@ static void get_counters(const struct xt_table_info *t,
} }
} }
static inline struct xt_counters *alloc_counters(struct xt_table *table)
/* We're lazy, and add to the first CPU; overflow works its fey magic
* and everything is OK. */
static int
add_counter_to_entry(struct arpt_entry *e,
const struct xt_counters addme[],
unsigned int *i)
{
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
(*i)++;
return 0;
}
/* Take values from counters and add them back onto the current cpu */
static void put_counters(struct xt_table_info *t,
const struct xt_counters counters[])
{
unsigned int i, cpu;
local_bh_disable();
cpu = smp_processor_id();
i = 0;
ARPT_ENTRY_ITERATE(t->entries[cpu],
t->size,
add_counter_to_entry,
counters,
&i);
local_bh_enable();
}
static inline int
zero_entry_counter(struct arpt_entry *e, void *arg)
{
e->counters.bcnt = 0;
e->counters.pcnt = 0;
return 0;
}
static void
clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info)
{
unsigned int cpu;
const void *loc_cpu_entry = info->entries[raw_smp_processor_id()];
memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
for_each_possible_cpu(cpu) {
memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size);
ARPT_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size,
zero_entry_counter, NULL);
}
}
static struct xt_counters *alloc_counters(struct xt_table *table)
{ {
unsigned int countersize; unsigned int countersize;
struct xt_counters *counters; struct xt_counters *counters;
const struct xt_table_info *private = table->private; struct xt_table_info *private = table->private;
struct xt_table_info *info;
/* We need atomic snapshot of counters: rest doesn't change /* We need atomic snapshot of counters: rest doesn't change
* (other than comefrom, which userspace doesn't care * (other than comefrom, which userspace doesn't care
...@@ -728,14 +808,30 @@ static inline struct xt_counters *alloc_counters(struct xt_table *table) ...@@ -728,14 +808,30 @@ static inline struct xt_counters *alloc_counters(struct xt_table *table)
counters = vmalloc_node(countersize, numa_node_id()); counters = vmalloc_node(countersize, numa_node_id());
if (counters == NULL) if (counters == NULL)
return ERR_PTR(-ENOMEM); goto nomem;
info = xt_alloc_table_info(private->size);
if (!info)
goto free_counters;
clone_counters(info, private);
mutex_lock(&table->lock);
xt_table_entry_swap_rcu(private, info);
synchronize_net(); /* Wait until smoke has cleared */
get_counters(info, counters);
put_counters(private, counters);
mutex_unlock(&table->lock);
/* First, sum counters... */ xt_free_table_info(info);
write_lock_bh(&table->lock);
get_counters(private, counters);
write_unlock_bh(&table->lock);
return counters; return counters;
free_counters:
vfree(counters);
nomem:
return ERR_PTR(-ENOMEM);
} }
static int copy_entries_to_user(unsigned int total_size, static int copy_entries_to_user(unsigned int total_size,
...@@ -1075,20 +1171,6 @@ static int do_replace(struct net *net, void __user *user, unsigned int len) ...@@ -1075,20 +1171,6 @@ static int do_replace(struct net *net, void __user *user, unsigned int len)
return ret; return ret;
} }
/* We're lazy, and add to the first CPU; overflow works its fey magic
* and everything is OK.
*/
static inline int add_counter_to_entry(struct arpt_entry *e,
const struct xt_counters addme[],
unsigned int *i)
{
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
(*i)++;
return 0;
}
static int do_add_counters(struct net *net, void __user *user, unsigned int len, static int do_add_counters(struct net *net, void __user *user, unsigned int len,
int compat) int compat)
{ {
...@@ -1148,13 +1230,14 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len, ...@@ -1148,13 +1230,14 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len,
goto free; goto free;
} }
write_lock_bh(&t->lock); mutex_lock(&t->lock);
private = t->private; private = t->private;
if (private->number != num_counters) { if (private->number != num_counters) {
ret = -EINVAL; ret = -EINVAL;
goto unlock_up_free; goto unlock_up_free;
} }
preempt_disable();
i = 0; i = 0;
/* Choose the copy that is on our node */ /* Choose the copy that is on our node */
loc_cpu_entry = private->entries[smp_processor_id()]; loc_cpu_entry = private->entries[smp_processor_id()];
...@@ -1163,8 +1246,10 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len, ...@@ -1163,8 +1246,10 @@ static int do_add_counters(struct net *net, void __user *user, unsigned int len,
add_counter_to_entry, add_counter_to_entry,
paddc, paddc,
&i); &i);
preempt_enable();
unlock_up_free: unlock_up_free:
write_unlock_bh(&t->lock); mutex_unlock(&t->lock);
xt_table_unlock(t); xt_table_unlock(t);
module_put(t->me); module_put(t->me);
free: free:
......
...@@ -48,8 +48,6 @@ static struct ...@@ -48,8 +48,6 @@ static struct
static struct xt_table packet_filter = { static struct xt_table packet_filter = {
.name = "filter", .name = "filter",
.valid_hooks = FILTER_VALID_HOOKS, .valid_hooks = FILTER_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_filter.lock),
.private = NULL,
.me = THIS_MODULE, .me = THIS_MODULE,
.af = NFPROTO_ARP, .af = NFPROTO_ARP,
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/net.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/sock.h> #include <net/sock.h>
...@@ -640,6 +641,7 @@ static void __exit ip_queue_fini(void) ...@@ -640,6 +641,7 @@ static void __exit ip_queue_fini(void)
MODULE_DESCRIPTION("IPv4 packet queue handler"); MODULE_DESCRIPTION("IPv4 packet queue handler");
MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_FIREWALL);
module_init(ip_queue_init); module_init(ip_queue_init);
module_exit(ip_queue_fini); module_exit(ip_queue_fini);
...@@ -74,6 +74,25 @@ do { \ ...@@ -74,6 +74,25 @@ do { \
Hence the start of any table is given by get_table() below. */ Hence the start of any table is given by get_table() below. */
static unsigned long ifname_compare(const char *_a, const char *_b,
const unsigned char *_mask)
{
const unsigned long *a = (const unsigned long *)_a;
const unsigned long *b = (const unsigned long *)_b;
const unsigned long *mask = (const unsigned long *)_mask;
unsigned long ret;
ret = (a[0] ^ b[0]) & mask[0];
if (IFNAMSIZ > sizeof(unsigned long))
ret |= (a[1] ^ b[1]) & mask[1];
if (IFNAMSIZ > 2 * sizeof(unsigned long))
ret |= (a[2] ^ b[2]) & mask[2];
if (IFNAMSIZ > 3 * sizeof(unsigned long))
ret |= (a[3] ^ b[3]) & mask[3];
BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
return ret;
}
/* Returns whether matches rule or not. */ /* Returns whether matches rule or not. */
/* Performance critical - called for every packet */ /* Performance critical - called for every packet */
static inline bool static inline bool
...@@ -83,7 +102,6 @@ ip_packet_match(const struct iphdr *ip, ...@@ -83,7 +102,6 @@ ip_packet_match(const struct iphdr *ip,
const struct ipt_ip *ipinfo, const struct ipt_ip *ipinfo,
int isfrag) int isfrag)
{ {
size_t i;
unsigned long ret; unsigned long ret;
#define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg))) #define FWINV(bool, invflg) ((bool) ^ !!(ipinfo->invflags & (invflg)))
...@@ -103,12 +121,7 @@ ip_packet_match(const struct iphdr *ip, ...@@ -103,12 +121,7 @@ ip_packet_match(const struct iphdr *ip,
return false; return false;
} }
/* Look for ifname matches; this should unroll nicely. */ ret = ifname_compare(indev, ipinfo->iniface, ipinfo->iniface_mask);
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
ret |= (((const unsigned long *)indev)[i]
^ ((const unsigned long *)ipinfo->iniface)[i])
& ((const unsigned long *)ipinfo->iniface_mask)[i];
}
if (FWINV(ret != 0, IPT_INV_VIA_IN)) { if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n", dprintf("VIA in mismatch (%s vs %s).%s\n",
...@@ -117,11 +130,7 @@ ip_packet_match(const struct iphdr *ip, ...@@ -117,11 +130,7 @@ ip_packet_match(const struct iphdr *ip,
return false; return false;
} }
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { ret = ifname_compare(outdev, ipinfo->outiface, ipinfo->outiface_mask);
ret |= (((const unsigned long *)outdev)[i]
^ ((const unsigned long *)ipinfo->outiface)[i])
& ((const unsigned long *)ipinfo->outiface_mask)[i];
}
if (FWINV(ret != 0, IPT_INV_VIA_OUT)) { if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n", dprintf("VIA out mismatch (%s vs %s).%s\n",
...@@ -347,10 +356,12 @@ ipt_do_table(struct sk_buff *skb, ...@@ -347,10 +356,12 @@ ipt_do_table(struct sk_buff *skb,
mtpar.family = tgpar.family = NFPROTO_IPV4; mtpar.family = tgpar.family = NFPROTO_IPV4;
tgpar.hooknum = hook; tgpar.hooknum = hook;
read_lock_bh(&table->lock);
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); IP_NF_ASSERT(table->valid_hooks & (1 << hook));
private = table->private;
table_base = (void *)private->entries[smp_processor_id()]; rcu_read_lock();
private = rcu_dereference(table->private);
table_base = rcu_dereference(private->entries[smp_processor_id()]);
e = get_entry(table_base, private->hook_entry[hook]); e = get_entry(table_base, private->hook_entry[hook]);
/* For return from builtin chain */ /* For return from builtin chain */
...@@ -445,7 +456,7 @@ ipt_do_table(struct sk_buff *skb, ...@@ -445,7 +456,7 @@ ipt_do_table(struct sk_buff *skb,
} }
} while (!hotdrop); } while (!hotdrop);
read_unlock_bh(&table->lock); rcu_read_unlock();
#ifdef DEBUG_ALLOW_ALL #ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT; return NF_ACCEPT;
...@@ -924,13 +935,68 @@ get_counters(const struct xt_table_info *t, ...@@ -924,13 +935,68 @@ get_counters(const struct xt_table_info *t,
counters, counters,
&i); &i);
} }
}
/* We're lazy, and add to the first CPU; overflow works its fey magic
* and everything is OK. */
static int
add_counter_to_entry(struct ipt_entry *e,
const struct xt_counters addme[],
unsigned int *i)
{
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
(*i)++;
return 0;
}
/* Take values from counters and add them back onto the current cpu */
static void put_counters(struct xt_table_info *t,
const struct xt_counters counters[])
{
unsigned int i, cpu;
local_bh_disable();
cpu = smp_processor_id();
i = 0;
IPT_ENTRY_ITERATE(t->entries[cpu],
t->size,
add_counter_to_entry,
counters,
&i);
local_bh_enable();
}
static inline int
zero_entry_counter(struct ipt_entry *e, void *arg)
{
e->counters.bcnt = 0;
e->counters.pcnt = 0;
return 0;
}
static void
clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info)
{
unsigned int cpu;
const void *loc_cpu_entry = info->entries[raw_smp_processor_id()];
memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
for_each_possible_cpu(cpu) {
memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size);
IPT_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size,
zero_entry_counter, NULL);
}
} }
static struct xt_counters * alloc_counters(struct xt_table *table) static struct xt_counters * alloc_counters(struct xt_table *table)
{ {
unsigned int countersize; unsigned int countersize;
struct xt_counters *counters; struct xt_counters *counters;
const struct xt_table_info *private = table->private; struct xt_table_info *private = table->private;
struct xt_table_info *info;
/* We need atomic snapshot of counters: rest doesn't change /* We need atomic snapshot of counters: rest doesn't change
(other than comefrom, which userspace doesn't care (other than comefrom, which userspace doesn't care
...@@ -939,14 +1005,30 @@ static struct xt_counters * alloc_counters(struct xt_table *table) ...@@ -939,14 +1005,30 @@ static struct xt_counters * alloc_counters(struct xt_table *table)
counters = vmalloc_node(countersize, numa_node_id()); counters = vmalloc_node(countersize, numa_node_id());
if (counters == NULL) if (counters == NULL)
return ERR_PTR(-ENOMEM); goto nomem;
info = xt_alloc_table_info(private->size);
if (!info)
goto free_counters;
/* First, sum counters... */ clone_counters(info, private);
write_lock_bh(&table->lock);
get_counters(private, counters); mutex_lock(&table->lock);
write_unlock_bh(&table->lock); xt_table_entry_swap_rcu(private, info);
synchronize_net(); /* Wait until smoke has cleared */
get_counters(info, counters);
put_counters(private, counters);
mutex_unlock(&table->lock);
xt_free_table_info(info);
return counters; return counters;
free_counters:
vfree(counters);
nomem:
return ERR_PTR(-ENOMEM);
} }
static int static int
...@@ -1312,27 +1394,6 @@ do_replace(struct net *net, void __user *user, unsigned int len) ...@@ -1312,27 +1394,6 @@ do_replace(struct net *net, void __user *user, unsigned int len)
return ret; return ret;
} }
/* We're lazy, and add to the first CPU; overflow works its fey magic
* and everything is OK. */
static int
add_counter_to_entry(struct ipt_entry *e,
const struct xt_counters addme[],
unsigned int *i)
{
#if 0
duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
*i,
(long unsigned int)e->counters.pcnt,
(long unsigned int)e->counters.bcnt,
(long unsigned int)addme[*i].pcnt,
(long unsigned int)addme[*i].bcnt);
#endif
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
(*i)++;
return 0;
}
static int static int
do_add_counters(struct net *net, void __user *user, unsigned int len, int compat) do_add_counters(struct net *net, void __user *user, unsigned int len, int compat)
...@@ -1393,13 +1454,14 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat ...@@ -1393,13 +1454,14 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat
goto free; goto free;
} }
write_lock_bh(&t->lock); mutex_lock(&t->lock);
private = t->private; private = t->private;
if (private->number != num_counters) { if (private->number != num_counters) {
ret = -EINVAL; ret = -EINVAL;
goto unlock_up_free; goto unlock_up_free;
} }
preempt_disable();
i = 0; i = 0;
/* Choose the copy that is on our node */ /* Choose the copy that is on our node */
loc_cpu_entry = private->entries[raw_smp_processor_id()]; loc_cpu_entry = private->entries[raw_smp_processor_id()];
...@@ -1408,8 +1470,9 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat ...@@ -1408,8 +1470,9 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, int compat
add_counter_to_entry, add_counter_to_entry,
paddc, paddc,
&i); &i);
preempt_enable();
unlock_up_free: unlock_up_free:
write_unlock_bh(&t->lock); mutex_unlock(&t->lock);
xt_table_unlock(t); xt_table_unlock(t);
module_put(t->me); module_put(t->me);
free: free:
......
...@@ -464,7 +464,7 @@ static struct xt_target log_tg_reg __read_mostly = { ...@@ -464,7 +464,7 @@ static struct xt_target log_tg_reg __read_mostly = {
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
static const struct nf_logger ipt_log_logger ={ static struct nf_logger ipt_log_logger __read_mostly = {
.name = "ipt_LOG", .name = "ipt_LOG",
.logfn = &ipt_log_packet, .logfn = &ipt_log_packet,
.me = THIS_MODULE, .me = THIS_MODULE,
......
...@@ -379,7 +379,7 @@ static struct xt_target ulog_tg_reg __read_mostly = { ...@@ -379,7 +379,7 @@ static struct xt_target ulog_tg_reg __read_mostly = {
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
static struct nf_logger ipt_ulog_logger = { static struct nf_logger ipt_ulog_logger __read_mostly = {
.name = "ipt_ULOG", .name = "ipt_ULOG",
.logfn = ipt_logfn, .logfn = ipt_logfn,
.me = THIS_MODULE, .me = THIS_MODULE,
......
/* IP tables module for matching the value of the TTL
*
* (C) 2000,2001 by Harald Welte <laforge@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4/ipt_ttl.h>
#include <linux/netfilter/x_tables.h>
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("Xtables: IPv4 TTL field match");
MODULE_LICENSE("GPL");
static bool ttl_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
const struct ipt_ttl_info *info = par->matchinfo;
const u8 ttl = ip_hdr(skb)->ttl;
switch (info->mode) {
case IPT_TTL_EQ:
return ttl == info->ttl;
case IPT_TTL_NE:
return ttl != info->ttl;
case IPT_TTL_LT:
return ttl < info->ttl;
case IPT_TTL_GT:
return ttl > info->ttl;
default:
printk(KERN_WARNING "ipt_ttl: unknown mode %d\n",
info->mode);
return false;
}
return false;
}
static struct xt_match ttl_mt_reg __read_mostly = {
.name = "ttl",
.family = NFPROTO_IPV4,
.match = ttl_mt,
.matchsize = sizeof(struct ipt_ttl_info),
.me = THIS_MODULE,
};
static int __init ttl_mt_init(void)
{
return xt_register_match(&ttl_mt_reg);
}
static void __exit ttl_mt_exit(void)
{
xt_unregister_match(&ttl_mt_reg);
}
module_init(ttl_mt_init);
module_exit(ttl_mt_exit);
...@@ -56,7 +56,6 @@ static struct ...@@ -56,7 +56,6 @@ static struct
static struct xt_table packet_filter = { static struct xt_table packet_filter = {
.name = "filter", .name = "filter",
.valid_hooks = FILTER_VALID_HOOKS, .valid_hooks = FILTER_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_filter.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET, .af = AF_INET,
}; };
......
...@@ -67,7 +67,6 @@ static struct ...@@ -67,7 +67,6 @@ static struct
static struct xt_table packet_mangler = { static struct xt_table packet_mangler = {
.name = "mangle", .name = "mangle",
.valid_hooks = MANGLE_VALID_HOOKS, .valid_hooks = MANGLE_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_mangler.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET, .af = AF_INET,
}; };
......
...@@ -39,7 +39,6 @@ static struct ...@@ -39,7 +39,6 @@ static struct
static struct xt_table packet_raw = { static struct xt_table packet_raw = {
.name = "raw", .name = "raw",
.valid_hooks = RAW_VALID_HOOKS, .valid_hooks = RAW_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_raw.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET, .af = AF_INET,
}; };
......
...@@ -60,7 +60,6 @@ static struct ...@@ -60,7 +60,6 @@ static struct
static struct xt_table security_table = { static struct xt_table security_table = {
.name = "security", .name = "security",
.valid_hooks = SECURITY_VALID_HOOKS, .valid_hooks = SECURITY_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(security_table.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET, .af = AF_INET,
}; };
......
...@@ -120,8 +120,10 @@ static unsigned int ipv4_confirm(unsigned int hooknum, ...@@ -120,8 +120,10 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
typeof(nf_nat_seq_adjust_hook) seq_adjust; typeof(nf_nat_seq_adjust_hook) seq_adjust;
seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
if (!seq_adjust || !seq_adjust(skb, ct, ctinfo)) if (!seq_adjust || !seq_adjust(skb, ct, ctinfo)) {
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
return NF_DROP; return NF_DROP;
}
} }
out: out:
/* We've seen it coming out the other side: confirm it */ /* We've seen it coming out the other side: confirm it */
......
...@@ -61,7 +61,6 @@ static struct ...@@ -61,7 +61,6 @@ static struct
static struct xt_table nat_table = { static struct xt_table nat_table = {
.name = "nat", .name = "nat",
.valid_hooks = NAT_VALID_HOOKS, .valid_hooks = NAT_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(nat_table.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET, .af = AF_INET,
}; };
......
...@@ -95,13 +95,13 @@ config IP6_NF_MATCH_OPTS ...@@ -95,13 +95,13 @@ config IP6_NF_MATCH_OPTS
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
config IP6_NF_MATCH_HL config IP6_NF_MATCH_HL
tristate '"hl" match support' tristate '"hl" hoplimit match support'
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED
help select NETFILTER_XT_MATCH_HL
HL matching allows you to match packets based on the hop ---help---
limit of the packet. This is a backwards-compat option for the user's convenience
(e.g. when running oldconfig). It selects
To compile it as a module, choose M here. If unsure, say N. COFNIG_NETFILTER_XT_MATCH_HL.
config IP6_NF_MATCH_IPV6HEADER config IP6_NF_MATCH_IPV6HEADER
tristate '"ipv6header" IPv6 Extension Headers Match' tristate '"ipv6header" IPv6 Extension Headers Match'
...@@ -130,6 +130,15 @@ config IP6_NF_MATCH_RT ...@@ -130,6 +130,15 @@ config IP6_NF_MATCH_RT
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
# The targets # The targets
config IP6_NF_TARGET_HL
tristate '"HL" hoplimit target support'
depends on NETFILTER_ADVANCED
select NETFILTER_XT_TARGET_HL
---help---
This is a backwards-compat option for the user's convenience
(e.g. when running oldconfig). It selects
COFNIG_NETFILTER_XT_TARGET_HL.
config IP6_NF_TARGET_LOG config IP6_NF_TARGET_LOG
tristate "LOG target support" tristate "LOG target support"
default m if NETFILTER_ADVANCED=n default m if NETFILTER_ADVANCED=n
...@@ -170,23 +179,6 @@ config IP6_NF_MANGLE ...@@ -170,23 +179,6 @@ config IP6_NF_MANGLE
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
config IP6_NF_TARGET_HL
tristate 'HL (hoplimit) target support'
depends on IP6_NF_MANGLE
depends on NETFILTER_ADVANCED
help
This option adds a `HL' target, which enables the user to decrement
the hoplimit value of the IPv6 header or set it to a given (lower)
value.
While it is safe to decrement the hoplimit value, this option also
enables functionality to increment and set the hoplimit value of the
IPv6 header to arbitrary values. This is EXTREMELY DANGEROUS since
you can easily create immortal packets that loop forever on the
network.
To compile it as a module, choose M here. If unsure, say N.
config IP6_NF_RAW config IP6_NF_RAW
tristate 'raw table support (required for TRACE)' tristate 'raw table support (required for TRACE)'
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED
......
...@@ -20,13 +20,11 @@ obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o ...@@ -20,13 +20,11 @@ obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o obj-$(CONFIG_IP6_NF_MATCH_FRAG) += ip6t_frag.o
obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o
obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o obj-$(CONFIG_IP6_NF_MATCH_MH) += ip6t_mh.o
obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o
obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o
# targets # targets
obj-$(CONFIG_IP6_NF_TARGET_HL) += ip6t_HL.o
obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
...@@ -643,6 +643,7 @@ static void __exit ip6_queue_fini(void) ...@@ -643,6 +643,7 @@ static void __exit ip6_queue_fini(void)
MODULE_DESCRIPTION("IPv6 packet queue handler"); MODULE_DESCRIPTION("IPv6 packet queue handler");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_IP6_FW);
module_init(ip6_queue_init); module_init(ip6_queue_init);
module_exit(ip6_queue_fini); module_exit(ip6_queue_fini);
...@@ -89,6 +89,25 @@ ip6t_ext_hdr(u8 nexthdr) ...@@ -89,6 +89,25 @@ ip6t_ext_hdr(u8 nexthdr)
(nexthdr == IPPROTO_DSTOPTS) ); (nexthdr == IPPROTO_DSTOPTS) );
} }
static unsigned long ifname_compare(const char *_a, const char *_b,
const unsigned char *_mask)
{
const unsigned long *a = (const unsigned long *)_a;
const unsigned long *b = (const unsigned long *)_b;
const unsigned long *mask = (const unsigned long *)_mask;
unsigned long ret;
ret = (a[0] ^ b[0]) & mask[0];
if (IFNAMSIZ > sizeof(unsigned long))
ret |= (a[1] ^ b[1]) & mask[1];
if (IFNAMSIZ > 2 * sizeof(unsigned long))
ret |= (a[2] ^ b[2]) & mask[2];
if (IFNAMSIZ > 3 * sizeof(unsigned long))
ret |= (a[3] ^ b[3]) & mask[3];
BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
return ret;
}
/* Returns whether matches rule or not. */ /* Returns whether matches rule or not. */
/* Performance critical - called for every packet */ /* Performance critical - called for every packet */
static inline bool static inline bool
...@@ -99,7 +118,6 @@ ip6_packet_match(const struct sk_buff *skb, ...@@ -99,7 +118,6 @@ ip6_packet_match(const struct sk_buff *skb,
unsigned int *protoff, unsigned int *protoff,
int *fragoff, bool *hotdrop) int *fragoff, bool *hotdrop)
{ {
size_t i;
unsigned long ret; unsigned long ret;
const struct ipv6hdr *ipv6 = ipv6_hdr(skb); const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
...@@ -120,12 +138,7 @@ ip6_packet_match(const struct sk_buff *skb, ...@@ -120,12 +138,7 @@ ip6_packet_match(const struct sk_buff *skb,
return false; return false;
} }
/* Look for ifname matches; this should unroll nicely. */ ret = ifname_compare(indev, ip6info->iniface, ip6info->iniface_mask);
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
ret |= (((const unsigned long *)indev)[i]
^ ((const unsigned long *)ip6info->iniface)[i])
& ((const unsigned long *)ip6info->iniface_mask)[i];
}
if (FWINV(ret != 0, IP6T_INV_VIA_IN)) { if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
dprintf("VIA in mismatch (%s vs %s).%s\n", dprintf("VIA in mismatch (%s vs %s).%s\n",
...@@ -134,11 +147,7 @@ ip6_packet_match(const struct sk_buff *skb, ...@@ -134,11 +147,7 @@ ip6_packet_match(const struct sk_buff *skb,
return false; return false;
} }
for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { ret = ifname_compare(outdev, ip6info->outiface, ip6info->outiface_mask);
ret |= (((const unsigned long *)outdev)[i]
^ ((const unsigned long *)ip6info->outiface)[i])
& ((const unsigned long *)ip6info->outiface_mask)[i];
}
if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) { if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
dprintf("VIA out mismatch (%s vs %s).%s\n", dprintf("VIA out mismatch (%s vs %s).%s\n",
...@@ -373,10 +382,12 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -373,10 +382,12 @@ ip6t_do_table(struct sk_buff *skb,
mtpar.family = tgpar.family = NFPROTO_IPV6; mtpar.family = tgpar.family = NFPROTO_IPV6;
tgpar.hooknum = hook; tgpar.hooknum = hook;
read_lock_bh(&table->lock);
IP_NF_ASSERT(table->valid_hooks & (1 << hook)); IP_NF_ASSERT(table->valid_hooks & (1 << hook));
private = table->private;
table_base = (void *)private->entries[smp_processor_id()]; rcu_read_lock();
private = rcu_dereference(table->private);
table_base = rcu_dereference(private->entries[smp_processor_id()]);
e = get_entry(table_base, private->hook_entry[hook]); e = get_entry(table_base, private->hook_entry[hook]);
/* For return from builtin chain */ /* For return from builtin chain */
...@@ -474,7 +485,7 @@ ip6t_do_table(struct sk_buff *skb, ...@@ -474,7 +485,7 @@ ip6t_do_table(struct sk_buff *skb,
#ifdef CONFIG_NETFILTER_DEBUG #ifdef CONFIG_NETFILTER_DEBUG
((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON; ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
#endif #endif
read_unlock_bh(&table->lock); rcu_read_unlock();
#ifdef DEBUG_ALLOW_ALL #ifdef DEBUG_ALLOW_ALL
return NF_ACCEPT; return NF_ACCEPT;
...@@ -955,11 +966,64 @@ get_counters(const struct xt_table_info *t, ...@@ -955,11 +966,64 @@ get_counters(const struct xt_table_info *t,
} }
} }
/* We're lazy, and add to the first CPU; overflow works its fey magic
* and everything is OK. */
static int
add_counter_to_entry(struct ip6t_entry *e,
const struct xt_counters addme[],
unsigned int *i)
{
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
(*i)++;
return 0;
}
/* Take values from counters and add them back onto the current cpu */
static void put_counters(struct xt_table_info *t,
const struct xt_counters counters[])
{
unsigned int i, cpu;
local_bh_disable();
cpu = smp_processor_id();
i = 0;
IP6T_ENTRY_ITERATE(t->entries[cpu],
t->size,
add_counter_to_entry,
counters,
&i);
local_bh_enable();
}
static inline int
zero_entry_counter(struct ip6t_entry *e, void *arg)
{
e->counters.bcnt = 0;
e->counters.pcnt = 0;
return 0;
}
static void
clone_counters(struct xt_table_info *newinfo, const struct xt_table_info *info)
{
unsigned int cpu;
const void *loc_cpu_entry = info->entries[raw_smp_processor_id()];
memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
for_each_possible_cpu(cpu) {
memcpy(newinfo->entries[cpu], loc_cpu_entry, info->size);
IP6T_ENTRY_ITERATE(newinfo->entries[cpu], newinfo->size,
zero_entry_counter, NULL);
}
}
static struct xt_counters *alloc_counters(struct xt_table *table) static struct xt_counters *alloc_counters(struct xt_table *table)
{ {
unsigned int countersize; unsigned int countersize;
struct xt_counters *counters; struct xt_counters *counters;
const struct xt_table_info *private = table->private; struct xt_table_info *private = table->private;
struct xt_table_info *info;
/* We need atomic snapshot of counters: rest doesn't change /* We need atomic snapshot of counters: rest doesn't change
(other than comefrom, which userspace doesn't care (other than comefrom, which userspace doesn't care
...@@ -968,14 +1032,28 @@ static struct xt_counters *alloc_counters(struct xt_table *table) ...@@ -968,14 +1032,28 @@ static struct xt_counters *alloc_counters(struct xt_table *table)
counters = vmalloc_node(countersize, numa_node_id()); counters = vmalloc_node(countersize, numa_node_id());
if (counters == NULL) if (counters == NULL)
return ERR_PTR(-ENOMEM); goto nomem;
info = xt_alloc_table_info(private->size);
if (!info)
goto free_counters;
clone_counters(info, private);
/* First, sum counters... */ mutex_lock(&table->lock);
write_lock_bh(&table->lock); xt_table_entry_swap_rcu(private, info);
get_counters(private, counters); synchronize_net(); /* Wait until smoke has cleared */
write_unlock_bh(&table->lock);
return counters; get_counters(info, counters);
put_counters(private, counters);
mutex_unlock(&table->lock);
xt_free_table_info(info);
free_counters:
vfree(counters);
nomem:
return ERR_PTR(-ENOMEM);
} }
static int static int
...@@ -1342,28 +1420,6 @@ do_replace(struct net *net, void __user *user, unsigned int len) ...@@ -1342,28 +1420,6 @@ do_replace(struct net *net, void __user *user, unsigned int len)
return ret; return ret;
} }
/* We're lazy, and add to the first CPU; overflow works its fey magic
* and everything is OK. */
static inline int
add_counter_to_entry(struct ip6t_entry *e,
const struct xt_counters addme[],
unsigned int *i)
{
#if 0
duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
*i,
(long unsigned int)e->counters.pcnt,
(long unsigned int)e->counters.bcnt,
(long unsigned int)addme[*i].pcnt,
(long unsigned int)addme[*i].bcnt);
#endif
ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
(*i)++;
return 0;
}
static int static int
do_add_counters(struct net *net, void __user *user, unsigned int len, do_add_counters(struct net *net, void __user *user, unsigned int len,
int compat) int compat)
...@@ -1424,13 +1480,14 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, ...@@ -1424,13 +1480,14 @@ do_add_counters(struct net *net, void __user *user, unsigned int len,
goto free; goto free;
} }
write_lock_bh(&t->lock); mutex_lock(&t->lock);
private = t->private; private = t->private;
if (private->number != num_counters) { if (private->number != num_counters) {
ret = -EINVAL; ret = -EINVAL;
goto unlock_up_free; goto unlock_up_free;
} }
preempt_disable();
i = 0; i = 0;
/* Choose the copy that is on our node */ /* Choose the copy that is on our node */
loc_cpu_entry = private->entries[raw_smp_processor_id()]; loc_cpu_entry = private->entries[raw_smp_processor_id()];
...@@ -1439,8 +1496,9 @@ do_add_counters(struct net *net, void __user *user, unsigned int len, ...@@ -1439,8 +1496,9 @@ do_add_counters(struct net *net, void __user *user, unsigned int len,
add_counter_to_entry, add_counter_to_entry,
paddc, paddc,
&i); &i);
preempt_enable();
unlock_up_free: unlock_up_free:
write_unlock_bh(&t->lock); mutex_unlock(&t->lock);
xt_table_unlock(t); xt_table_unlock(t);
module_put(t->me); module_put(t->me);
free: free:
......
/*
* Hop Limit modification target for ip6tables
* Maciej Soltysiak <solt@dns.toxicfilms.tv>
* Based on HW's TTL module
*
* This software is distributed under the terms of GNU GPL
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv6/ip6t_HL.h>
MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
MODULE_DESCRIPTION("Xtables: IPv6 Hop Limit field modification target");
MODULE_LICENSE("GPL");
static unsigned int
hl_tg6(struct sk_buff *skb, const struct xt_target_param *par)
{
struct ipv6hdr *ip6h;
const struct ip6t_HL_info *info = par->targinfo;
int new_hl;
if (!skb_make_writable(skb, skb->len))
return NF_DROP;
ip6h = ipv6_hdr(skb);
switch (info->mode) {
case IP6T_HL_SET:
new_hl = info->hop_limit;
break;
case IP6T_HL_INC:
new_hl = ip6h->hop_limit + info->hop_limit;
if (new_hl > 255)
new_hl = 255;
break;
case IP6T_HL_DEC:
new_hl = ip6h->hop_limit - info->hop_limit;
if (new_hl < 0)
new_hl = 0;
break;
default:
new_hl = ip6h->hop_limit;
break;
}
ip6h->hop_limit = new_hl;
return XT_CONTINUE;
}
static bool hl_tg6_check(const struct xt_tgchk_param *par)
{
const struct ip6t_HL_info *info = par->targinfo;
if (info->mode > IP6T_HL_MAXMODE) {
printk(KERN_WARNING "ip6t_HL: invalid or unknown Mode %u\n",
info->mode);
return false;
}
if (info->mode != IP6T_HL_SET && info->hop_limit == 0) {
printk(KERN_WARNING "ip6t_HL: increment/decrement doesn't "
"make sense with value 0\n");
return false;
}
return true;
}
static struct xt_target hl_tg6_reg __read_mostly = {
.name = "HL",
.family = NFPROTO_IPV6,
.target = hl_tg6,
.targetsize = sizeof(struct ip6t_HL_info),
.table = "mangle",
.checkentry = hl_tg6_check,
.me = THIS_MODULE
};
static int __init hl_tg6_init(void)
{
return xt_register_target(&hl_tg6_reg);
}
static void __exit hl_tg6_exit(void)
{
xt_unregister_target(&hl_tg6_reg);
}
module_init(hl_tg6_init);
module_exit(hl_tg6_exit);
...@@ -477,7 +477,7 @@ static struct xt_target log_tg6_reg __read_mostly = { ...@@ -477,7 +477,7 @@ static struct xt_target log_tg6_reg __read_mostly = {
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
static const struct nf_logger ip6t_logger = { static struct nf_logger ip6t_logger __read_mostly = {
.name = "ip6t_LOG", .name = "ip6t_LOG",
.logfn = &ip6t_log_packet, .logfn = &ip6t_log_packet,
.me = THIS_MODULE, .me = THIS_MODULE,
......
...@@ -54,7 +54,6 @@ static struct ...@@ -54,7 +54,6 @@ static struct
static struct xt_table packet_filter = { static struct xt_table packet_filter = {
.name = "filter", .name = "filter",
.valid_hooks = FILTER_VALID_HOOKS, .valid_hooks = FILTER_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_filter.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET6, .af = AF_INET6,
}; };
......
...@@ -60,7 +60,6 @@ static struct ...@@ -60,7 +60,6 @@ static struct
static struct xt_table packet_mangler = { static struct xt_table packet_mangler = {
.name = "mangle", .name = "mangle",
.valid_hooks = MANGLE_VALID_HOOKS, .valid_hooks = MANGLE_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_mangler.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET6, .af = AF_INET6,
}; };
......
...@@ -38,7 +38,6 @@ static struct ...@@ -38,7 +38,6 @@ static struct
static struct xt_table packet_raw = { static struct xt_table packet_raw = {
.name = "raw", .name = "raw",
.valid_hooks = RAW_VALID_HOOKS, .valid_hooks = RAW_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(packet_raw.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET6, .af = AF_INET6,
}; };
......
...@@ -59,7 +59,6 @@ static struct ...@@ -59,7 +59,6 @@ static struct
static struct xt_table security_table = { static struct xt_table security_table = {
.name = "security", .name = "security",
.valid_hooks = SECURITY_VALID_HOOKS, .valid_hooks = SECURITY_VALID_HOOKS,
.lock = __RW_LOCK_UNLOCKED(security_table.lock),
.me = THIS_MODULE, .me = THIS_MODULE,
.af = AF_INET6, .af = AF_INET6,
}; };
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_l3proto.h> #include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple) struct nf_conntrack_tuple *tuple)
......
...@@ -126,6 +126,10 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -126,6 +126,10 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb,
pr_debug("icmpv6: can't create new conn with type %u\n", pr_debug("icmpv6: can't create new conn with type %u\n",
type + 128); type + 128);
nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple); nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple);
if (LOG_INVALID(nf_ct_net(ct), IPPROTO_ICMPV6))
nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL,
"nf_ct_icmpv6: invalid new with type %d ",
type + 128);
return false; return false;
} }
atomic_set(&ct->proto.icmp.count, 0); atomic_set(&ct->proto.icmp.count, 0);
......
...@@ -357,6 +357,45 @@ config NETFILTER_XT_TARGET_DSCP ...@@ -357,6 +357,45 @@ config NETFILTER_XT_TARGET_DSCP
To compile it as a module, choose M here. If unsure, say N. To compile it as a module, choose M here. If unsure, say N.
config NETFILTER_XT_TARGET_HL
tristate '"HL" hoplimit target support'
depends on IP_NF_MANGLE || IP6_NF_MANGLE
depends on NETFILTER_ADVANCED
---help---
This option adds the "HL" (for IPv6) and "TTL" (for IPv4)
targets, which enable the user to change the
hoplimit/time-to-live value of the IP header.
While it is safe to decrement the hoplimit/TTL value, the
modules also allow to increment and set the hoplimit value of
the header to arbitrary values. This is EXTREMELY DANGEROUS
since you can easily create immortal packets that loop
forever on the network.
config NETFILTER_XT_TARGET_LED
tristate '"LED" target support'
depends on LEDS_CLASS
depends on NETFILTER_ADVANCED
help
This option adds a `LED' target, which allows you to blink LEDs in
response to particular packets passing through your machine.
This can be used to turn a spare LED into a network activity LED,
which only flashes in response to FTP transfers, for example. Or
you could have an LED which lights up for a minute or two every time
somebody connects to your machine via SSH.
You will need support for the "led" class to make this work.
To create an LED trigger for incoming SSH traffic:
iptables -A INPUT -p tcp --dport 22 -j LED --led-trigger-id ssh --led-delay 1000
Then attach the new trigger to an LED on your system:
echo netfilter-ssh > /sys/class/leds/<ledname>/trigger
For more information on the LEDs available on your system, see
Documentation/leds-class.txt
config NETFILTER_XT_TARGET_MARK config NETFILTER_XT_TARGET_MARK
tristate '"MARK" target support' tristate '"MARK" target support'
default m if NETFILTER_ADVANCED=n default m if NETFILTER_ADVANCED=n
...@@ -488,6 +527,22 @@ config NETFILTER_XT_TARGET_TCPOPTSTRIP ...@@ -488,6 +527,22 @@ config NETFILTER_XT_TARGET_TCPOPTSTRIP
This option adds a "TCPOPTSTRIP" target, which allows you to strip This option adds a "TCPOPTSTRIP" target, which allows you to strip
TCP options from TCP packets. TCP options from TCP packets.
config NETFILTER_XT_MATCH_CLUSTER
tristate '"cluster" match support'
depends on NF_CONNTRACK
depends on NETFILTER_ADVANCED
---help---
This option allows you to build work-load-sharing clusters of
network servers/stateful firewalls without having a dedicated
load-balancing router/server/switch. Basically, this match returns
true when the packet must be handled by this cluster node. Thus,
all nodes see all packets and this match decides which node handles
what packets. The work-load sharing algorithm is based on source
address hashing.
If you say Y or M here, try `iptables -m cluster --help` for
more information.
config NETFILTER_XT_MATCH_COMMENT config NETFILTER_XT_MATCH_COMMENT
tristate '"comment" match support' tristate '"comment" match support'
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED
...@@ -605,6 +660,14 @@ config NETFILTER_XT_MATCH_HELPER ...@@ -605,6 +660,14 @@ config NETFILTER_XT_MATCH_HELPER
To compile it as a module, choose M here. If unsure, say Y. To compile it as a module, choose M here. If unsure, say Y.
config NETFILTER_XT_MATCH_HL
tristate '"hl" hoplimit/TTL match support'
depends on NETFILTER_ADVANCED
---help---
HL matching allows you to match packets based on the hoplimit
in the IPv6 header, or the time-to-live field in the IPv4
header of the packet.
config NETFILTER_XT_MATCH_IPRANGE config NETFILTER_XT_MATCH_IPRANGE
tristate '"iprange" address range match support' tristate '"iprange" address range match support'
depends on NETFILTER_ADVANCED depends on NETFILTER_ADVANCED
......
...@@ -45,6 +45,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o ...@@ -45,6 +45,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o
obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o
...@@ -57,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o ...@@ -57,6 +59,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o
obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o
# matches # matches
obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o
...@@ -67,6 +70,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o ...@@ -67,6 +70,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_DSCP) += xt_dscp.o
obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o obj-$(CONFIG_NETFILTER_XT_MATCH_ESP) += xt_esp.o
obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o
obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o obj-$(CONFIG_NETFILTER_XT_MATCH_HELPER) += xt_helper.o
obj-$(CONFIG_NETFILTER_XT_MATCH_HL) += xt_hl.o
obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o obj-$(CONFIG_NETFILTER_XT_MATCH_IPRANGE) += xt_iprange.o
obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o obj-$(CONFIG_NETFILTER_XT_MATCH_LENGTH) += xt_length.o
obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o obj-$(CONFIG_NETFILTER_XT_MATCH_LIMIT) += xt_limit.o
......
...@@ -174,7 +174,6 @@ int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, ...@@ -174,7 +174,6 @@ int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
outdev, &elem, okfn, hook_thresh); outdev, &elem, okfn, hook_thresh);
if (verdict == NF_ACCEPT || verdict == NF_STOP) { if (verdict == NF_ACCEPT || verdict == NF_STOP) {
ret = 1; ret = 1;
goto unlock;
} else if (verdict == NF_DROP) { } else if (verdict == NF_DROP) {
kfree_skb(skb); kfree_skb(skb);
ret = -EPERM; ret = -EPERM;
...@@ -183,7 +182,6 @@ int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, ...@@ -183,7 +182,6 @@ int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
verdict >> NF_VERDICT_BITS)) verdict >> NF_VERDICT_BITS))
goto next_hook; goto next_hook;
} }
unlock:
rcu_read_unlock(); rcu_read_unlock();
return ret; return ret;
} }
......
...@@ -54,7 +54,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_lock); ...@@ -54,7 +54,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_lock);
unsigned int nf_conntrack_htable_size __read_mostly; unsigned int nf_conntrack_htable_size __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);
int nf_conntrack_max __read_mostly; unsigned int nf_conntrack_max __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_max); EXPORT_SYMBOL_GPL(nf_conntrack_max);
struct nf_conn nf_conntrack_untracked __read_mostly; struct nf_conn nf_conntrack_untracked __read_mostly;
...@@ -472,7 +472,8 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, ...@@ -472,7 +472,8 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
struct nf_conn *ct; struct nf_conn *ct;
if (unlikely(!nf_conntrack_hash_rnd_initted)) { if (unlikely(!nf_conntrack_hash_rnd_initted)) {
get_random_bytes(&nf_conntrack_hash_rnd, 4); get_random_bytes(&nf_conntrack_hash_rnd,
sizeof(nf_conntrack_hash_rnd));
nf_conntrack_hash_rnd_initted = 1; nf_conntrack_hash_rnd_initted = 1;
} }
...@@ -516,16 +517,17 @@ EXPORT_SYMBOL_GPL(nf_conntrack_alloc); ...@@ -516,16 +517,17 @@ EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
static void nf_conntrack_free_rcu(struct rcu_head *head) static void nf_conntrack_free_rcu(struct rcu_head *head)
{ {
struct nf_conn *ct = container_of(head, struct nf_conn, rcu); struct nf_conn *ct = container_of(head, struct nf_conn, rcu);
struct net *net = nf_ct_net(ct);
nf_ct_ext_free(ct); nf_ct_ext_free(ct);
kmem_cache_free(nf_conntrack_cachep, ct); kmem_cache_free(nf_conntrack_cachep, ct);
atomic_dec(&net->ct.count);
} }
void nf_conntrack_free(struct nf_conn *ct) void nf_conntrack_free(struct nf_conn *ct)
{ {
struct net *net = nf_ct_net(ct);
nf_ct_ext_destroy(ct); nf_ct_ext_destroy(ct);
atomic_dec(&net->ct.count);
call_rcu(&ct->rcu, nf_conntrack_free_rcu); call_rcu(&ct->rcu, nf_conntrack_free_rcu);
} }
EXPORT_SYMBOL_GPL(nf_conntrack_free); EXPORT_SYMBOL_GPL(nf_conntrack_free);
...@@ -733,6 +735,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, ...@@ -733,6 +735,8 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
nf_conntrack_put(skb->nfct); nf_conntrack_put(skb->nfct);
skb->nfct = NULL; skb->nfct = NULL;
NF_CT_STAT_INC_ATOMIC(net, invalid); NF_CT_STAT_INC_ATOMIC(net, invalid);
if (ret == -NF_DROP)
NF_CT_STAT_INC_ATOMIC(net, drop);
return -ret; return -ret;
} }
...@@ -1103,7 +1107,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) ...@@ -1103,7 +1107,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
/* We have to rehahs for the new table anyway, so we also can /* We have to rehahs for the new table anyway, so we also can
* use a newrandom seed */ * use a newrandom seed */
get_random_bytes(&rnd, 4); get_random_bytes(&rnd, sizeof(rnd));
/* Lookups in the old hash might happen in parallel, which means we /* Lookups in the old hash might happen in parallel, which means we
* might get false negatives during connection lookup. New connections * might get false negatives during connection lookup. New connections
......
...@@ -72,7 +72,8 @@ static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple ...@@ -72,7 +72,8 @@ static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple
unsigned int hash; unsigned int hash;
if (unlikely(!nf_ct_expect_hash_rnd_initted)) { if (unlikely(!nf_ct_expect_hash_rnd_initted)) {
get_random_bytes(&nf_ct_expect_hash_rnd, 4); get_random_bytes(&nf_ct_expect_hash_rnd,
sizeof(nf_ct_expect_hash_rnd));
nf_ct_expect_hash_rnd_initted = 1; nf_ct_expect_hash_rnd_initted = 1;
} }
......
...@@ -518,6 +518,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, ...@@ -518,6 +518,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
nla_put_failure: nla_put_failure:
rcu_read_unlock(); rcu_read_unlock();
nlmsg_failure: nlmsg_failure:
nfnetlink_set_err(0, group, -ENOBUFS);
kfree_skb(skb); kfree_skb(skb);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
...@@ -599,7 +600,8 @@ ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple) ...@@ -599,7 +600,8 @@ ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple)
nla_parse_nested(tb, CTA_IP_MAX, attr, NULL); nla_parse_nested(tb, CTA_IP_MAX, attr, NULL);
l3proto = nf_ct_l3proto_find_get(tuple->src.l3num); rcu_read_lock();
l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
if (likely(l3proto->nlattr_to_tuple)) { if (likely(l3proto->nlattr_to_tuple)) {
ret = nla_validate_nested(attr, CTA_IP_MAX, ret = nla_validate_nested(attr, CTA_IP_MAX,
...@@ -608,7 +610,7 @@ ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple) ...@@ -608,7 +610,7 @@ ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_tuple *tuple)
ret = l3proto->nlattr_to_tuple(tb, tuple); ret = l3proto->nlattr_to_tuple(tb, tuple);
} }
nf_ct_l3proto_put(l3proto); rcu_read_unlock();
return ret; return ret;
} }
...@@ -633,7 +635,8 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr, ...@@ -633,7 +635,8 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr,
return -EINVAL; return -EINVAL;
tuple->dst.protonum = nla_get_u8(tb[CTA_PROTO_NUM]); tuple->dst.protonum = nla_get_u8(tb[CTA_PROTO_NUM]);
l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum); rcu_read_lock();
l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
if (likely(l4proto->nlattr_to_tuple)) { if (likely(l4proto->nlattr_to_tuple)) {
ret = nla_validate_nested(attr, CTA_PROTO_MAX, ret = nla_validate_nested(attr, CTA_PROTO_MAX,
...@@ -642,7 +645,7 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr, ...@@ -642,7 +645,7 @@ ctnetlink_parse_tuple_proto(struct nlattr *attr,
ret = l4proto->nlattr_to_tuple(tb, tuple); ret = l4proto->nlattr_to_tuple(tb, tuple);
} }
nf_ct_l4proto_put(l4proto); rcu_read_unlock();
return ret; return ret;
} }
...@@ -989,10 +992,11 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[]) ...@@ -989,10 +992,11 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, struct nlattr *cda[])
nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, NULL); nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, NULL);
l4proto = nf_ct_l4proto_find_get(nf_ct_l3num(ct), nf_ct_protonum(ct)); rcu_read_lock();
l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
if (l4proto->from_nlattr) if (l4proto->from_nlattr)
err = l4proto->from_nlattr(tb, ct); err = l4proto->from_nlattr(tb, ct);
nf_ct_l4proto_put(l4proto); rcu_read_unlock();
return err; return err;
} }
...@@ -1062,6 +1066,10 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[]) ...@@ -1062,6 +1066,10 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
{ {
int err; int err;
/* only allow NAT changes and master assignation for new conntracks */
if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST] || cda[CTA_TUPLE_MASTER])
return -EOPNOTSUPP;
if (cda[CTA_HELP]) { if (cda[CTA_HELP]) {
err = ctnetlink_change_helper(ct, cda); err = ctnetlink_change_helper(ct, cda);
if (err < 0) if (err < 0)
...@@ -1124,13 +1132,11 @@ ctnetlink_event_report(struct nf_conn *ct, u32 pid, int report) ...@@ -1124,13 +1132,11 @@ ctnetlink_event_report(struct nf_conn *ct, u32 pid, int report)
report); report);
} }
static int static struct nf_conn *
ctnetlink_create_conntrack(struct nlattr *cda[], ctnetlink_create_conntrack(struct nlattr *cda[],
struct nf_conntrack_tuple *otuple, struct nf_conntrack_tuple *otuple,
struct nf_conntrack_tuple *rtuple, struct nf_conntrack_tuple *rtuple,
struct nf_conn *master_ct, u8 u3)
u32 pid,
int report)
{ {
struct nf_conn *ct; struct nf_conn *ct;
int err = -EINVAL; int err = -EINVAL;
...@@ -1138,10 +1144,10 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1138,10 +1144,10 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_ATOMIC); ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_ATOMIC);
if (IS_ERR(ct)) if (IS_ERR(ct))
return -ENOMEM; return ERR_PTR(-ENOMEM);
if (!cda[CTA_TIMEOUT]) if (!cda[CTA_TIMEOUT])
goto err; goto err1;
ct->timeout.expires = ntohl(nla_get_be32(cda[CTA_TIMEOUT])); ct->timeout.expires = ntohl(nla_get_be32(cda[CTA_TIMEOUT]));
ct->timeout.expires = jiffies + ct->timeout.expires * HZ; ct->timeout.expires = jiffies + ct->timeout.expires * HZ;
...@@ -1152,10 +1158,8 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1152,10 +1158,8 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
char *helpname; char *helpname;
err = ctnetlink_parse_help(cda[CTA_HELP], &helpname); err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
if (err < 0) { if (err < 0)
rcu_read_unlock(); goto err2;
goto err;
}
helper = __nf_conntrack_helper_find_byname(helpname); helper = __nf_conntrack_helper_find_byname(helpname);
if (helper == NULL) { if (helper == NULL) {
...@@ -1163,28 +1167,26 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1163,28 +1167,26 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
if (request_module("nfct-helper-%s", helpname) < 0) { if (request_module("nfct-helper-%s", helpname) < 0) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto err; goto err1;
} }
rcu_read_lock(); rcu_read_lock();
helper = __nf_conntrack_helper_find_byname(helpname); helper = __nf_conntrack_helper_find_byname(helpname);
if (helper) { if (helper) {
rcu_read_unlock();
err = -EAGAIN; err = -EAGAIN;
goto err; goto err2;
} }
rcu_read_unlock(); rcu_read_unlock();
#endif #endif
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto err; goto err1;
} else { } else {
struct nf_conn_help *help; struct nf_conn_help *help;
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help == NULL) { if (help == NULL) {
rcu_read_unlock();
err = -ENOMEM; err = -ENOMEM;
goto err; goto err2;
} }
/* not in hash table yet so not strictly necessary */ /* not in hash table yet so not strictly necessary */
...@@ -1193,44 +1195,34 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1193,44 +1195,34 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
} else { } else {
/* try an implicit helper assignation */ /* try an implicit helper assignation */
err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC); err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
if (err < 0) { if (err < 0)
rcu_read_unlock(); goto err2;
goto err;
}
} }
if (cda[CTA_STATUS]) { if (cda[CTA_STATUS]) {
err = ctnetlink_change_status(ct, cda); err = ctnetlink_change_status(ct, cda);
if (err < 0) { if (err < 0)
rcu_read_unlock(); goto err2;
goto err;
}
} }
if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) { if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
err = ctnetlink_change_nat(ct, cda); err = ctnetlink_change_nat(ct, cda);
if (err < 0) { if (err < 0)
rcu_read_unlock(); goto err2;
goto err;
}
} }
#ifdef CONFIG_NF_NAT_NEEDED #ifdef CONFIG_NF_NAT_NEEDED
if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) {
err = ctnetlink_change_nat_seq_adj(ct, cda); err = ctnetlink_change_nat_seq_adj(ct, cda);
if (err < 0) { if (err < 0)
rcu_read_unlock(); goto err2;
goto err;
}
} }
#endif #endif
if (cda[CTA_PROTOINFO]) { if (cda[CTA_PROTOINFO]) {
err = ctnetlink_change_protoinfo(ct, cda); err = ctnetlink_change_protoinfo(ct, cda);
if (err < 0) { if (err < 0)
rcu_read_unlock(); goto err2;
goto err;
}
} }
nf_ct_acct_ext_add(ct, GFP_ATOMIC); nf_ct_acct_ext_add(ct, GFP_ATOMIC);
...@@ -1241,23 +1233,37 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1241,23 +1233,37 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
#endif #endif
/* setup master conntrack: this is a confirmed expectation */ /* setup master conntrack: this is a confirmed expectation */
if (master_ct) { if (cda[CTA_TUPLE_MASTER]) {
struct nf_conntrack_tuple master;
struct nf_conntrack_tuple_hash *master_h;
struct nf_conn *master_ct;
err = ctnetlink_parse_tuple(cda, &master, CTA_TUPLE_MASTER, u3);
if (err < 0)
goto err2;
master_h = __nf_conntrack_find(&init_net, &master);
if (master_h == NULL) {
err = -ENOENT;
goto err2;
}
master_ct = nf_ct_tuplehash_to_ctrack(master_h);
nf_conntrack_get(&master_ct->ct_general);
__set_bit(IPS_EXPECTED_BIT, &ct->status); __set_bit(IPS_EXPECTED_BIT, &ct->status);
ct->master = master_ct; ct->master = master_ct;
} }
nf_conntrack_get(&ct->ct_general);
add_timer(&ct->timeout); add_timer(&ct->timeout);
nf_conntrack_hash_insert(ct); nf_conntrack_hash_insert(ct);
rcu_read_unlock(); rcu_read_unlock();
ctnetlink_event_report(ct, pid, report);
nf_ct_put(ct);
return 0; return ct;
err: err2:
rcu_read_unlock();
err1:
nf_conntrack_free(ct); nf_conntrack_free(ct);
return err; return ERR_PTR(err);
} }
static int static int
...@@ -1289,38 +1295,25 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1289,38 +1295,25 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
h = __nf_conntrack_find(&init_net, &rtuple); h = __nf_conntrack_find(&init_net, &rtuple);
if (h == NULL) { if (h == NULL) {
struct nf_conntrack_tuple master; err = -ENOENT;
struct nf_conntrack_tuple_hash *master_h = NULL; if (nlh->nlmsg_flags & NLM_F_CREATE) {
struct nf_conn *master_ct = NULL; struct nf_conn *ct;
if (cda[CTA_TUPLE_MASTER]) {
err = ctnetlink_parse_tuple(cda,
&master,
CTA_TUPLE_MASTER,
u3);
if (err < 0)
goto out_unlock;
master_h = __nf_conntrack_find(&init_net, &master); ct = ctnetlink_create_conntrack(cda, &otuple,
if (master_h == NULL) { &rtuple, u3);
err = -ENOENT; if (IS_ERR(ct)) {
err = PTR_ERR(ct);
goto out_unlock; goto out_unlock;
} }
master_ct = nf_ct_tuplehash_to_ctrack(master_h); err = 0;
nf_conntrack_get(&master_ct->ct_general); nf_conntrack_get(&ct->ct_general);
} spin_unlock_bh(&nf_conntrack_lock);
ctnetlink_event_report(ct,
err = -ENOENT; NETLINK_CB(skb).pid,
if (nlh->nlmsg_flags & NLM_F_CREATE) nlmsg_report(nlh));
err = ctnetlink_create_conntrack(cda, nf_ct_put(ct);
&otuple, } else
&rtuple, spin_unlock_bh(&nf_conntrack_lock);
master_ct,
NETLINK_CB(skb).pid,
nlmsg_report(nlh));
spin_unlock_bh(&nf_conntrack_lock);
if (err < 0 && master_ct)
nf_ct_put(master_ct);
return err; return err;
} }
...@@ -1332,17 +1325,6 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1332,17 +1325,6 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
if (!(nlh->nlmsg_flags & NLM_F_EXCL)) { if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
/* we only allow nat config for new conntracks */
if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
err = -EOPNOTSUPP;
goto out_unlock;
}
/* can't link an existing conntrack to a master */
if (cda[CTA_TUPLE_MASTER]) {
err = -EOPNOTSUPP;
goto out_unlock;
}
err = ctnetlink_change_conntrack(ct, cda); err = ctnetlink_change_conntrack(ct, cda);
if (err == 0) { if (err == 0) {
nf_conntrack_get(&ct->ct_general); nf_conntrack_get(&ct->ct_general);
...@@ -1533,6 +1515,7 @@ static int ctnetlink_expect_event(struct notifier_block *this, ...@@ -1533,6 +1515,7 @@ static int ctnetlink_expect_event(struct notifier_block *this,
nla_put_failure: nla_put_failure:
rcu_read_unlock(); rcu_read_unlock();
nlmsg_failure: nlmsg_failure:
nfnetlink_set_err(0, 0, -ENOBUFS);
kfree_skb(skb); kfree_skb(skb);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
......
...@@ -74,27 +74,6 @@ EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); ...@@ -74,27 +74,6 @@ EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
/* this is guaranteed to always return a valid protocol helper, since /* this is guaranteed to always return a valid protocol helper, since
* it falls back to generic_protocol */ * it falls back to generic_protocol */
struct nf_conntrack_l4proto *
nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
{
struct nf_conntrack_l4proto *p;
rcu_read_lock();
p = __nf_ct_l4proto_find(l3proto, l4proto);
if (!try_module_get(p->me))
p = &nf_conntrack_l4proto_generic;
rcu_read_unlock();
return p;
}
EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
{
module_put(p->me);
}
EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
struct nf_conntrack_l3proto * struct nf_conntrack_l3proto *
nf_ct_l3proto_find_get(u_int16_t l3proto) nf_ct_l3proto_find_get(u_int16_t l3proto)
{ {
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/dccp.h> #include <linux/dccp.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <linux/netfilter/nfnetlink_conntrack.h> #include <linux/netfilter/nfnetlink_conntrack.h>
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_l4proto.h> #include <net/netfilter/nf_conntrack_l4proto.h>
...@@ -23,8 +26,6 @@ ...@@ -23,8 +26,6 @@
static DEFINE_RWLOCK(dccp_lock); static DEFINE_RWLOCK(dccp_lock);
static int nf_ct_dccp_loose __read_mostly = 1;
/* Timeouts are based on values from RFC4340: /* Timeouts are based on values from RFC4340:
* *
* - REQUEST: * - REQUEST:
...@@ -72,16 +73,6 @@ static int nf_ct_dccp_loose __read_mostly = 1; ...@@ -72,16 +73,6 @@ static int nf_ct_dccp_loose __read_mostly = 1;
#define DCCP_MSL (2 * 60 * HZ) #define DCCP_MSL (2 * 60 * HZ)
static unsigned int dccp_timeout[CT_DCCP_MAX + 1] __read_mostly = {
[CT_DCCP_REQUEST] = 2 * DCCP_MSL,
[CT_DCCP_RESPOND] = 4 * DCCP_MSL,
[CT_DCCP_PARTOPEN] = 4 * DCCP_MSL,
[CT_DCCP_OPEN] = 12 * 3600 * HZ,
[CT_DCCP_CLOSEREQ] = 64 * HZ,
[CT_DCCP_CLOSING] = 64 * HZ,
[CT_DCCP_TIMEWAIT] = 2 * DCCP_MSL,
};
static const char * const dccp_state_names[] = { static const char * const dccp_state_names[] = {
[CT_DCCP_NONE] = "NONE", [CT_DCCP_NONE] = "NONE",
[CT_DCCP_REQUEST] = "REQUEST", [CT_DCCP_REQUEST] = "REQUEST",
...@@ -393,6 +384,22 @@ dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] = ...@@ -393,6 +384,22 @@ dccp_state_table[CT_DCCP_ROLE_MAX + 1][DCCP_PKT_SYNCACK + 1][CT_DCCP_MAX + 1] =
}, },
}; };
/* this module per-net specifics */
static int dccp_net_id;
struct dccp_net {
int dccp_loose;
unsigned int dccp_timeout[CT_DCCP_MAX + 1];
#ifdef CONFIG_SYSCTL
struct ctl_table_header *sysctl_header;
struct ctl_table *sysctl_table;
#endif
};
static inline struct dccp_net *dccp_pernet(struct net *net)
{
return net_generic(net, dccp_net_id);
}
static bool dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, static bool dccp_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
struct nf_conntrack_tuple *tuple) struct nf_conntrack_tuple *tuple)
{ {
...@@ -419,6 +426,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -419,6 +426,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
unsigned int dataoff) unsigned int dataoff)
{ {
struct net *net = nf_ct_net(ct); struct net *net = nf_ct_net(ct);
struct dccp_net *dn;
struct dccp_hdr _dh, *dh; struct dccp_hdr _dh, *dh;
const char *msg; const char *msg;
u_int8_t state; u_int8_t state;
...@@ -429,7 +437,8 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -429,7 +437,8 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb,
state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE]; state = dccp_state_table[CT_DCCP_ROLE_CLIENT][dh->dccph_type][CT_DCCP_NONE];
switch (state) { switch (state) {
default: default:
if (nf_ct_dccp_loose == 0) { dn = dccp_pernet(net);
if (dn->dccp_loose == 0) {
msg = "nf_ct_dccp: not picking up existing connection "; msg = "nf_ct_dccp: not picking up existing connection ";
goto out_invalid; goto out_invalid;
} }
...@@ -465,6 +474,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -465,6 +474,7 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
u_int8_t pf, unsigned int hooknum) u_int8_t pf, unsigned int hooknum)
{ {
struct net *net = nf_ct_net(ct); struct net *net = nf_ct_net(ct);
struct dccp_net *dn;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct dccp_hdr _dh, *dh; struct dccp_hdr _dh, *dh;
u_int8_t type, old_state, new_state; u_int8_t type, old_state, new_state;
...@@ -542,7 +552,9 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -542,7 +552,9 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb,
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); write_unlock_bh(&dccp_lock);
nf_ct_refresh_acct(ct, ctinfo, skb, dccp_timeout[new_state]);
dn = dccp_pernet(net);
nf_ct_refresh_acct(ct, ctinfo, skb, dn->dccp_timeout[new_state]);
return NF_ACCEPT; return NF_ACCEPT;
} }
...@@ -660,13 +672,11 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct) ...@@ -660,13 +672,11 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
#endif #endif
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
static unsigned int dccp_sysctl_table_users; /* template, data assigned later */
static struct ctl_table_header *dccp_sysctl_header; static struct ctl_table dccp_sysctl_table[] = {
static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_request", .procname = "nf_conntrack_dccp_timeout_request",
.data = &dccp_timeout[CT_DCCP_REQUEST],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -674,7 +684,6 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -674,7 +684,6 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_respond", .procname = "nf_conntrack_dccp_timeout_respond",
.data = &dccp_timeout[CT_DCCP_RESPOND],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -682,7 +691,6 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -682,7 +691,6 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_partopen", .procname = "nf_conntrack_dccp_timeout_partopen",
.data = &dccp_timeout[CT_DCCP_PARTOPEN],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -690,7 +698,6 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -690,7 +698,6 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_open", .procname = "nf_conntrack_dccp_timeout_open",
.data = &dccp_timeout[CT_DCCP_OPEN],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -698,7 +705,6 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -698,7 +705,6 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_closereq", .procname = "nf_conntrack_dccp_timeout_closereq",
.data = &dccp_timeout[CT_DCCP_CLOSEREQ],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -706,7 +712,6 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -706,7 +712,6 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_closing", .procname = "nf_conntrack_dccp_timeout_closing",
.data = &dccp_timeout[CT_DCCP_CLOSING],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -714,7 +719,6 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -714,7 +719,6 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_timeout_timewait", .procname = "nf_conntrack_dccp_timeout_timewait",
.data = &dccp_timeout[CT_DCCP_TIMEWAIT],
.maxlen = sizeof(unsigned int), .maxlen = sizeof(unsigned int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
...@@ -722,8 +726,7 @@ static ctl_table dccp_sysctl_table[] = { ...@@ -722,8 +726,7 @@ static ctl_table dccp_sysctl_table[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
.procname = "nf_conntrack_dccp_loose", .procname = "nf_conntrack_dccp_loose",
.data = &nf_ct_dccp_loose, .maxlen = sizeof(int),
.maxlen = sizeof(nf_ct_dccp_loose),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec, .proc_handler = proc_dointvec,
}, },
...@@ -751,11 +754,6 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = { ...@@ -751,11 +754,6 @@ static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy, .nla_policy = nf_ct_port_nla_policy,
#endif #endif
#ifdef CONFIG_SYSCTL
.ctl_table_users = &dccp_sysctl_table_users,
.ctl_table_header = &dccp_sysctl_header,
.ctl_table = dccp_sysctl_table,
#endif
}; };
static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
...@@ -776,34 +774,107 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { ...@@ -776,34 +774,107 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nla_policy = nf_ct_port_nla_policy, .nla_policy = nf_ct_port_nla_policy,
#endif #endif
};
static __net_init int dccp_net_init(struct net *net)
{
struct dccp_net *dn;
int err;
dn = kmalloc(sizeof(*dn), GFP_KERNEL);
if (!dn)
return -ENOMEM;
/* default values */
dn->dccp_loose = 1;
dn->dccp_timeout[CT_DCCP_REQUEST] = 2 * DCCP_MSL;
dn->dccp_timeout[CT_DCCP_RESPOND] = 4 * DCCP_MSL;
dn->dccp_timeout[CT_DCCP_PARTOPEN] = 4 * DCCP_MSL;
dn->dccp_timeout[CT_DCCP_OPEN] = 12 * 3600 * HZ;
dn->dccp_timeout[CT_DCCP_CLOSEREQ] = 64 * HZ;
dn->dccp_timeout[CT_DCCP_CLOSING] = 64 * HZ;
dn->dccp_timeout[CT_DCCP_TIMEWAIT] = 2 * DCCP_MSL;
err = net_assign_generic(net, dccp_net_id, dn);
if (err)
goto out;
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
.ctl_table_users = &dccp_sysctl_table_users, err = -ENOMEM;
.ctl_table_header = &dccp_sysctl_header, dn->sysctl_table = kmemdup(dccp_sysctl_table,
.ctl_table = dccp_sysctl_table, sizeof(dccp_sysctl_table), GFP_KERNEL);
if (!dn->sysctl_table)
goto out;
dn->sysctl_table[0].data = &dn->dccp_timeout[CT_DCCP_REQUEST];
dn->sysctl_table[1].data = &dn->dccp_timeout[CT_DCCP_RESPOND];
dn->sysctl_table[2].data = &dn->dccp_timeout[CT_DCCP_PARTOPEN];
dn->sysctl_table[3].data = &dn->dccp_timeout[CT_DCCP_OPEN];
dn->sysctl_table[4].data = &dn->dccp_timeout[CT_DCCP_CLOSEREQ];
dn->sysctl_table[5].data = &dn->dccp_timeout[CT_DCCP_CLOSING];
dn->sysctl_table[6].data = &dn->dccp_timeout[CT_DCCP_TIMEWAIT];
dn->sysctl_table[7].data = &dn->dccp_loose;
dn->sysctl_header = register_net_sysctl_table(net,
nf_net_netfilter_sysctl_path, dn->sysctl_table);
if (!dn->sysctl_header) {
kfree(dn->sysctl_table);
goto out;
}
#endif #endif
return 0;
out:
kfree(dn);
return err;
}
static __net_exit void dccp_net_exit(struct net *net)
{
struct dccp_net *dn = dccp_pernet(net);
#ifdef CONFIG_SYSCTL
unregister_net_sysctl_table(dn->sysctl_header);
kfree(dn->sysctl_table);
#endif
kfree(dn);
net_assign_generic(net, dccp_net_id, NULL);
}
static struct pernet_operations dccp_net_ops = {
.init = dccp_net_init,
.exit = dccp_net_exit,
}; };
static int __init nf_conntrack_proto_dccp_init(void) static int __init nf_conntrack_proto_dccp_init(void)
{ {
int err; int err;
err = nf_conntrack_l4proto_register(&dccp_proto4); err = register_pernet_gen_subsys(&dccp_net_id, &dccp_net_ops);
if (err < 0) if (err < 0)
goto err1; goto err1;
err = nf_conntrack_l4proto_register(&dccp_proto6); err = nf_conntrack_l4proto_register(&dccp_proto4);
if (err < 0) if (err < 0)
goto err2; goto err2;
err = nf_conntrack_l4proto_register(&dccp_proto6);
if (err < 0)
goto err3;
return 0; return 0;
err2: err3:
nf_conntrack_l4proto_unregister(&dccp_proto4); nf_conntrack_l4proto_unregister(&dccp_proto4);
err2:
unregister_pernet_gen_subsys(dccp_net_id, &dccp_net_ops);
err1: err1:
return err; return err;
} }
static void __exit nf_conntrack_proto_dccp_fini(void) static void __exit nf_conntrack_proto_dccp_fini(void)
{ {
unregister_pernet_gen_subsys(dccp_net_id, &dccp_net_ops);
nf_conntrack_l4proto_unregister(&dccp_proto6); nf_conntrack_l4proto_unregister(&dccp_proto6);
nf_conntrack_l4proto_unregister(&dccp_proto4); nf_conntrack_l4proto_unregister(&dccp_proto4);
} }
......
...@@ -92,7 +92,7 @@ static struct ctl_table generic_compat_sysctl_table[] = { ...@@ -92,7 +92,7 @@ static struct ctl_table generic_compat_sysctl_table[] = {
struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly =
{ {
.l3proto = PF_UNSPEC, .l3proto = PF_UNSPEC,
.l4proto = 0, .l4proto = 255,
.name = "unknown", .name = "unknown",
.pkt_to_tuple = generic_pkt_to_tuple, .pkt_to_tuple = generic_pkt_to_tuple,
.invert_tuple = generic_invert_tuple, .invert_tuple = generic_invert_tuple,
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#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>
#include <net/netfilter/nf_log.h> #include <net/netfilter/nf_log.h>
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
/* Protects ct->proto.tcp */ /* Protects ct->proto.tcp */
static DEFINE_RWLOCK(tcp_lock); static DEFINE_RWLOCK(tcp_lock);
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#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>
#include <net/netfilter/nf_log.h> #include <net/netfilter/nf_log.h>
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ; static unsigned int nf_ct_udp_timeout __read_mostly = 30*HZ;
static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ; static unsigned int nf_ct_udp_timeout_stream __read_mostly = 180*HZ;
......
...@@ -14,58 +14,63 @@ ...@@ -14,58 +14,63 @@
LOG target modules */ LOG target modules */
#define NF_LOG_PREFIXLEN 128 #define NF_LOG_PREFIXLEN 128
#define NFLOGGER_NAME_LEN 64
static const struct nf_logger *nf_loggers[NFPROTO_NUMPROTO] __read_mostly; static const struct nf_logger *nf_loggers[NFPROTO_NUMPROTO] __read_mostly;
static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly;
static DEFINE_MUTEX(nf_log_mutex); static DEFINE_MUTEX(nf_log_mutex);
/* return EBUSY if somebody else is registered, EEXIST if the same logger static struct nf_logger *__find_logger(int pf, const char *str_logger)
* is registred, 0 on success. */
int nf_log_register(u_int8_t pf, const struct nf_logger *logger)
{ {
int ret; struct nf_logger *t;
if (pf >= ARRAY_SIZE(nf_loggers)) list_for_each_entry(t, &nf_loggers_l[pf], list[pf]) {
return -EINVAL; if (!strnicmp(str_logger, t->name, strlen(t->name)))
return t;
/* Any setup of logging members must be done before }
* substituting pointer. */
ret = mutex_lock_interruptible(&nf_log_mutex);
if (ret < 0)
return ret;
if (!nf_loggers[pf])
rcu_assign_pointer(nf_loggers[pf], logger);
else if (nf_loggers[pf] == logger)
ret = -EEXIST;
else
ret = -EBUSY;
mutex_unlock(&nf_log_mutex); return NULL;
return ret;
} }
EXPORT_SYMBOL(nf_log_register);
void nf_log_unregister_pf(u_int8_t pf) /* return EEXIST if the same logger is registred, 0 on success. */
int nf_log_register(u_int8_t pf, struct nf_logger *logger)
{ {
const struct nf_logger *llog;
if (pf >= ARRAY_SIZE(nf_loggers)) if (pf >= ARRAY_SIZE(nf_loggers))
return; return -EINVAL;
mutex_lock(&nf_log_mutex); mutex_lock(&nf_log_mutex);
rcu_assign_pointer(nf_loggers[pf], NULL);
if (pf == NFPROTO_UNSPEC) {
int i;
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
list_add_tail(&(logger->list[i]), &(nf_loggers_l[i]));
} else {
/* register at end of list to honor first register win */
list_add_tail(&logger->list[pf], &nf_loggers_l[pf]);
llog = rcu_dereference(nf_loggers[pf]);
if (llog == NULL)
rcu_assign_pointer(nf_loggers[pf], logger);
}
mutex_unlock(&nf_log_mutex); mutex_unlock(&nf_log_mutex);
/* Give time to concurrent readers. */ return 0;
synchronize_rcu();
} }
EXPORT_SYMBOL(nf_log_unregister_pf); EXPORT_SYMBOL(nf_log_register);
void nf_log_unregister(const struct nf_logger *logger) void nf_log_unregister(struct nf_logger *logger)
{ {
const struct nf_logger *c_logger;
int i; int i;
mutex_lock(&nf_log_mutex); mutex_lock(&nf_log_mutex);
for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) { for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) {
if (nf_loggers[i] == logger) c_logger = rcu_dereference(nf_loggers[i]);
if (c_logger == logger)
rcu_assign_pointer(nf_loggers[i], NULL); rcu_assign_pointer(nf_loggers[i], NULL);
list_del(&logger->list[i]);
} }
mutex_unlock(&nf_log_mutex); mutex_unlock(&nf_log_mutex);
...@@ -73,6 +78,27 @@ void nf_log_unregister(const struct nf_logger *logger) ...@@ -73,6 +78,27 @@ void nf_log_unregister(const struct nf_logger *logger)
} }
EXPORT_SYMBOL(nf_log_unregister); EXPORT_SYMBOL(nf_log_unregister);
int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger)
{
mutex_lock(&nf_log_mutex);
if (__find_logger(pf, logger->name) == NULL) {
mutex_unlock(&nf_log_mutex);
return -ENOENT;
}
rcu_assign_pointer(nf_loggers[pf], logger);
mutex_unlock(&nf_log_mutex);
return 0;
}
EXPORT_SYMBOL(nf_log_bind_pf);
void nf_log_unbind_pf(u_int8_t pf)
{
mutex_lock(&nf_log_mutex);
rcu_assign_pointer(nf_loggers[pf], NULL);
mutex_unlock(&nf_log_mutex);
}
EXPORT_SYMBOL(nf_log_unbind_pf);
void nf_log_packet(u_int8_t pf, void nf_log_packet(u_int8_t pf,
unsigned int hooknum, unsigned int hooknum,
const struct sk_buff *skb, const struct sk_buff *skb,
...@@ -129,13 +155,37 @@ static int seq_show(struct seq_file *s, void *v) ...@@ -129,13 +155,37 @@ static int seq_show(struct seq_file *s, void *v)
{ {
loff_t *pos = v; loff_t *pos = v;
const struct nf_logger *logger; const struct nf_logger *logger;
struct nf_logger *t;
int ret;
logger = rcu_dereference(nf_loggers[*pos]); logger = rcu_dereference(nf_loggers[*pos]);
if (!logger) if (!logger)
return seq_printf(s, "%2lld NONE\n", *pos); ret = seq_printf(s, "%2lld NONE (", *pos);
else
ret = seq_printf(s, "%2lld %s (", *pos, logger->name);
if (ret < 0)
return ret;
mutex_lock(&nf_log_mutex);
list_for_each_entry(t, &nf_loggers_l[*pos], list[*pos]) {
ret = seq_printf(s, "%s", t->name);
if (ret < 0) {
mutex_unlock(&nf_log_mutex);
return ret;
}
if (&t->list[*pos] != nf_loggers_l[*pos].prev) {
ret = seq_printf(s, ",");
if (ret < 0) {
mutex_unlock(&nf_log_mutex);
return ret;
}
}
}
mutex_unlock(&nf_log_mutex);
return seq_printf(s, "%2lld %s\n", *pos, logger->name); return seq_printf(s, ")\n");
} }
static const struct seq_operations nflog_seq_ops = { static const struct seq_operations nflog_seq_ops = {
...@@ -158,15 +208,102 @@ static const struct file_operations nflog_file_ops = { ...@@ -158,15 +208,102 @@ static const struct file_operations nflog_file_ops = {
.release = seq_release, .release = seq_release,
}; };
#endif /* PROC_FS */ #endif /* PROC_FS */
#ifdef CONFIG_SYSCTL
struct ctl_path nf_log_sysctl_path[] = {
{ .procname = "net", .ctl_name = CTL_NET, },
{ .procname = "netfilter", .ctl_name = NET_NETFILTER, },
{ .procname = "nf_log", .ctl_name = CTL_UNNUMBERED, },
{ }
};
static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];
static struct ctl_table_header *nf_log_dir_header;
static int nf_log_proc_dostring(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp, loff_t *ppos)
{
const struct nf_logger *logger;
int r = 0;
int tindex = (unsigned long)table->extra1;
if (write) {
if (!strcmp(buffer, "NONE")) {
nf_log_unbind_pf(tindex);
return 0;
}
mutex_lock(&nf_log_mutex);
logger = __find_logger(tindex, buffer);
if (logger == NULL) {
mutex_unlock(&nf_log_mutex);
return -ENOENT;
}
rcu_assign_pointer(nf_loggers[tindex], logger);
mutex_unlock(&nf_log_mutex);
} else {
rcu_read_lock();
logger = rcu_dereference(nf_loggers[tindex]);
if (!logger)
table->data = "NONE";
else
table->data = logger->name;
r = proc_dostring(table, write, filp, buffer, lenp, ppos);
rcu_read_unlock();
}
return r;
}
static __init int netfilter_log_sysctl_init(void)
{
int i;
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
snprintf(nf_log_sysctl_fnames[i-NFPROTO_UNSPEC], 3, "%d", i);
nf_log_sysctl_table[i].ctl_name = CTL_UNNUMBERED;
nf_log_sysctl_table[i].procname =
nf_log_sysctl_fnames[i-NFPROTO_UNSPEC];
nf_log_sysctl_table[i].data = NULL;
nf_log_sysctl_table[i].maxlen =
NFLOGGER_NAME_LEN * sizeof(char);
nf_log_sysctl_table[i].mode = 0644;
nf_log_sysctl_table[i].proc_handler = nf_log_proc_dostring;
nf_log_sysctl_table[i].extra1 = (void *)(unsigned long) i;
}
nf_log_dir_header = register_sysctl_paths(nf_log_sysctl_path,
nf_log_sysctl_table);
if (!nf_log_dir_header)
return -ENOMEM;
return 0;
}
#else
static __init int netfilter_log_sysctl_init(void)
{
return 0;
}
#endif /* CONFIG_SYSCTL */
int __init netfilter_log_init(void) int __init netfilter_log_init(void)
{ {
int i, r;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (!proc_create("nf_log", S_IRUGO, if (!proc_create("nf_log", S_IRUGO,
proc_net_netfilter, &nflog_file_ops)) proc_net_netfilter, &nflog_file_ops))
return -1; return -1;
#endif #endif
/* Errors will trigger panic, unroll on error is unnecessary. */
r = netfilter_log_sysctl_init();
if (r < 0)
return r;
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
INIT_LIST_HEAD(&(nf_loggers_l[i]));
return 0; return 0;
} }
...@@ -113,6 +113,12 @@ int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo) ...@@ -113,6 +113,12 @@ int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo)
} }
EXPORT_SYMBOL_GPL(nfnetlink_send); EXPORT_SYMBOL_GPL(nfnetlink_send);
void nfnetlink_set_err(u32 pid, u32 group, int error)
{
netlink_set_err(nfnl, pid, group, error);
}
EXPORT_SYMBOL_GPL(nfnetlink_set_err);
int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags) int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags)
{ {
return netlink_unicast(nfnl, skb, pid, flags); return netlink_unicast(nfnl, skb, pid, flags);
......
...@@ -693,7 +693,7 @@ nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, ...@@ -693,7 +693,7 @@ nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb,
return -ENOTSUPP; return -ENOTSUPP;
} }
static const struct nf_logger nfulnl_logger = { static struct nf_logger nfulnl_logger __read_mostly = {
.name = "nfnetlink_log", .name = "nfnetlink_log",
.logfn = &nfulnl_log_packet, .logfn = &nfulnl_log_packet,
.me = THIS_MODULE, .me = THIS_MODULE,
...@@ -725,9 +725,9 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, ...@@ -725,9 +725,9 @@ nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
/* Commands without queue context */ /* Commands without queue context */
switch (cmd->command) { switch (cmd->command) {
case NFULNL_CFG_CMD_PF_BIND: case NFULNL_CFG_CMD_PF_BIND:
return nf_log_register(pf, &nfulnl_logger); return nf_log_bind_pf(pf, &nfulnl_logger);
case NFULNL_CFG_CMD_PF_UNBIND: case NFULNL_CFG_CMD_PF_UNBIND:
nf_log_unregister_pf(pf); nf_log_unbind_pf(pf);
return 0; return 0;
} }
} }
...@@ -952,17 +952,25 @@ static int __init nfnetlink_log_init(void) ...@@ -952,17 +952,25 @@ static int __init nfnetlink_log_init(void)
goto cleanup_netlink_notifier; goto cleanup_netlink_notifier;
} }
status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger);
if (status < 0) {
printk(KERN_ERR "log: failed to register logger\n");
goto cleanup_subsys;
}
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (!proc_create("nfnetlink_log", 0440, if (!proc_create("nfnetlink_log", 0440,
proc_net_netfilter, &nful_file_ops)) proc_net_netfilter, &nful_file_ops))
goto cleanup_subsys; goto cleanup_logger;
#endif #endif
return status; return status;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
cleanup_logger:
nf_log_unregister(&nfulnl_logger);
#endif
cleanup_subsys: cleanup_subsys:
nfnetlink_subsys_unregister(&nfulnl_subsys); nfnetlink_subsys_unregister(&nfulnl_subsys);
#endif
cleanup_netlink_notifier: cleanup_netlink_notifier:
netlink_unregister_notifier(&nfulnl_rtnl_notifier); netlink_unregister_notifier(&nfulnl_rtnl_notifier);
return status; return status;
......
...@@ -625,6 +625,20 @@ void xt_free_table_info(struct xt_table_info *info) ...@@ -625,6 +625,20 @@ void xt_free_table_info(struct xt_table_info *info)
} }
EXPORT_SYMBOL(xt_free_table_info); EXPORT_SYMBOL(xt_free_table_info);
void xt_table_entry_swap_rcu(struct xt_table_info *oldinfo,
struct xt_table_info *newinfo)
{
unsigned int cpu;
for_each_possible_cpu(cpu) {
void *p = oldinfo->entries[cpu];
rcu_assign_pointer(oldinfo->entries[cpu], newinfo->entries[cpu]);
newinfo->entries[cpu] = p;
}
}
EXPORT_SYMBOL_GPL(xt_table_entry_swap_rcu);
/* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */ /* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */
struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af,
const char *name) const char *name)
...@@ -671,21 +685,22 @@ xt_replace_table(struct xt_table *table, ...@@ -671,21 +685,22 @@ xt_replace_table(struct xt_table *table,
struct xt_table_info *oldinfo, *private; struct xt_table_info *oldinfo, *private;
/* Do the substitution. */ /* Do the substitution. */
write_lock_bh(&table->lock); mutex_lock(&table->lock);
private = table->private; private = table->private;
/* Check inside lock: is the old number correct? */ /* Check inside lock: is the old number correct? */
if (num_counters != private->number) { if (num_counters != private->number) {
duprintf("num_counters != table->private->number (%u/%u)\n", duprintf("num_counters != table->private->number (%u/%u)\n",
num_counters, private->number); num_counters, private->number);
write_unlock_bh(&table->lock); mutex_unlock(&table->lock);
*error = -EAGAIN; *error = -EAGAIN;
return NULL; return NULL;
} }
oldinfo = private; oldinfo = private;
table->private = newinfo; rcu_assign_pointer(table->private, newinfo);
newinfo->initial_entries = oldinfo->initial_entries; newinfo->initial_entries = oldinfo->initial_entries;
write_unlock_bh(&table->lock); mutex_unlock(&table->lock);
synchronize_net();
return oldinfo; return oldinfo;
} }
EXPORT_SYMBOL_GPL(xt_replace_table); EXPORT_SYMBOL_GPL(xt_replace_table);
...@@ -719,7 +734,8 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table *table, ...@@ -719,7 +734,8 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table *table,
/* Simplifies replace_table code. */ /* Simplifies replace_table code. */
table->private = bootstrap; table->private = bootstrap;
rwlock_init(&table->lock); mutex_init(&table->lock);
if (!xt_replace_table(table, 0, newinfo, &ret)) if (!xt_replace_table(table, 0, newinfo, &ret))
goto unlock; goto unlock;
......
/* TTL modification target for IP tables /*
* TTL modification target for IP tables
* (C) 2000,2005 by Harald Welte <laforge@netfilter.org> * (C) 2000,2005 by Harald Welte <laforge@netfilter.org>
* *
* Hop Limit modification target for ip6tables
* Maciej Soltysiak <solt@dns.toxicfilms.tv>
*
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/checksum.h> #include <net/checksum.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ipt_TTL.h> #include <linux/netfilter_ipv4/ipt_TTL.h>
#include <linux/netfilter_ipv6/ip6t_HL.h>
MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
MODULE_DESCRIPTION("Xtables: IPv4 TTL field modification target"); MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
MODULE_DESCRIPTION("Xtables: Hoplimit/TTL Limit field modification target");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static unsigned int static unsigned int
...@@ -59,6 +65,42 @@ ttl_tg(struct sk_buff *skb, const struct xt_target_param *par) ...@@ -59,6 +65,42 @@ ttl_tg(struct sk_buff *skb, const struct xt_target_param *par)
return XT_CONTINUE; return XT_CONTINUE;
} }
static unsigned int
hl_tg6(struct sk_buff *skb, const struct xt_target_param *par)
{
struct ipv6hdr *ip6h;
const struct ip6t_HL_info *info = par->targinfo;
int new_hl;
if (!skb_make_writable(skb, skb->len))
return NF_DROP;
ip6h = ipv6_hdr(skb);
switch (info->mode) {
case IP6T_HL_SET:
new_hl = info->hop_limit;
break;
case IP6T_HL_INC:
new_hl = ip6h->hop_limit + info->hop_limit;
if (new_hl > 255)
new_hl = 255;
break;
case IP6T_HL_DEC:
new_hl = ip6h->hop_limit - info->hop_limit;
if (new_hl < 0)
new_hl = 0;
break;
default:
new_hl = ip6h->hop_limit;
break;
}
ip6h->hop_limit = new_hl;
return XT_CONTINUE;
}
static bool ttl_tg_check(const struct xt_tgchk_param *par) static bool ttl_tg_check(const struct xt_tgchk_param *par)
{ {
const struct ipt_TTL_info *info = par->targinfo; const struct ipt_TTL_info *info = par->targinfo;
...@@ -73,25 +115,57 @@ static bool ttl_tg_check(const struct xt_tgchk_param *par) ...@@ -73,25 +115,57 @@ static bool ttl_tg_check(const struct xt_tgchk_param *par)
return true; return true;
} }
static struct xt_target ttl_tg_reg __read_mostly = { static bool hl_tg6_check(const struct xt_tgchk_param *par)
.name = "TTL", {
.family = NFPROTO_IPV4, const struct ip6t_HL_info *info = par->targinfo;
.target = ttl_tg,
.targetsize = sizeof(struct ipt_TTL_info), if (info->mode > IP6T_HL_MAXMODE) {
.table = "mangle", printk(KERN_WARNING "ip6t_HL: invalid or unknown Mode %u\n",
.checkentry = ttl_tg_check, info->mode);
.me = THIS_MODULE, return false;
}
if (info->mode != IP6T_HL_SET && info->hop_limit == 0) {
printk(KERN_WARNING "ip6t_HL: increment/decrement doesn't "
"make sense with value 0\n");
return false;
}
return true;
}
static struct xt_target hl_tg_reg[] __read_mostly = {
{
.name = "TTL",
.revision = 0,
.family = NFPROTO_IPV4,
.target = ttl_tg,
.targetsize = sizeof(struct ipt_TTL_info),
.table = "mangle",
.checkentry = ttl_tg_check,
.me = THIS_MODULE,
},
{
.name = "HL",
.revision = 0,
.family = NFPROTO_IPV6,
.target = hl_tg6,
.targetsize = sizeof(struct ip6t_HL_info),
.table = "mangle",
.checkentry = hl_tg6_check,
.me = THIS_MODULE,
},
}; };
static int __init ttl_tg_init(void) static int __init hl_tg_init(void)
{ {
return xt_register_target(&ttl_tg_reg); return xt_register_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg));
} }
static void __exit ttl_tg_exit(void) static void __exit hl_tg_exit(void)
{ {
xt_unregister_target(&ttl_tg_reg); xt_unregister_targets(hl_tg_reg, ARRAY_SIZE(hl_tg_reg));
} }
module_init(ttl_tg_init); module_init(hl_tg_init);
module_exit(ttl_tg_exit); module_exit(hl_tg_exit);
MODULE_ALIAS("ipt_TTL");
MODULE_ALIAS("ip6t_HL");
/*
* xt_LED.c - netfilter target to make LEDs blink upon packet matches
*
* Copyright (C) 2008 Adam Nielsen <a.nielsen@shikadi.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; version 2 of the License.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter/x_tables.h>
#include <linux/leds.h>
#include <linux/mutex.h>
#include <linux/netfilter/xt_LED.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Adam Nielsen <a.nielsen@shikadi.net>");
MODULE_DESCRIPTION("Xtables: trigger LED devices on packet match");
/*
* This is declared in here (the kernel module) only, to avoid having these
* dependencies in userspace code. This is what xt_led_info.internal_data
* points to.
*/
struct xt_led_info_internal {
struct led_trigger netfilter_led_trigger;
struct timer_list timer;
};
static unsigned int
led_tg(struct sk_buff *skb, const struct xt_target_param *par)
{
const struct xt_led_info *ledinfo = par->targinfo;
struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
/*
* If "always blink" is enabled, and there's still some time until the
* LED will switch off, briefly switch it off now.
*/
if ((ledinfo->delay > 0) && ledinfo->always_blink &&
timer_pending(&ledinternal->timer))
led_trigger_event(&ledinternal->netfilter_led_trigger,LED_OFF);
led_trigger_event(&ledinternal->netfilter_led_trigger, LED_FULL);
/* If there's a positive delay, start/update the timer */
if (ledinfo->delay > 0) {
mod_timer(&ledinternal->timer,
jiffies + msecs_to_jiffies(ledinfo->delay));
/* Otherwise if there was no delay given, blink as fast as possible */
} else if (ledinfo->delay == 0) {
led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
}
/* else the delay is negative, which means switch on and stay on */
return XT_CONTINUE;
}
static void led_timeout_callback(unsigned long data)
{
struct xt_led_info *ledinfo = (struct xt_led_info *)data;
struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
led_trigger_event(&ledinternal->netfilter_led_trigger, LED_OFF);
}
static bool led_tg_check(const struct xt_tgchk_param *par)
{
struct xt_led_info *ledinfo = par->targinfo;
struct xt_led_info_internal *ledinternal;
int err;
if (ledinfo->id[0] == '\0') {
printk(KERN_ERR KBUILD_MODNAME ": No 'id' parameter given.\n");
return false;
}
ledinternal = kzalloc(sizeof(struct xt_led_info_internal), GFP_KERNEL);
if (!ledinternal) {
printk(KERN_CRIT KBUILD_MODNAME ": out of memory\n");
return false;
}
ledinternal->netfilter_led_trigger.name = ledinfo->id;
err = led_trigger_register(&ledinternal->netfilter_led_trigger);
if (err) {
printk(KERN_CRIT KBUILD_MODNAME
": led_trigger_register() failed\n");
if (err == -EEXIST)
printk(KERN_ERR KBUILD_MODNAME
": Trigger name is already in use.\n");
goto exit_alloc;
}
/* See if we need to set up a timer */
if (ledinfo->delay > 0)
setup_timer(&ledinternal->timer, led_timeout_callback,
(unsigned long)ledinfo);
ledinfo->internal_data = ledinternal;
return true;
exit_alloc:
kfree(ledinternal);
return false;
}
static void led_tg_destroy(const struct xt_tgdtor_param *par)
{
const struct xt_led_info *ledinfo = par->targinfo;
struct xt_led_info_internal *ledinternal = ledinfo->internal_data;
if (ledinfo->delay > 0)
del_timer_sync(&ledinternal->timer);
led_trigger_unregister(&ledinternal->netfilter_led_trigger);
kfree(ledinternal);
}
static struct xt_target led_tg_reg __read_mostly = {
.name = "LED",
.revision = 0,
.family = NFPROTO_UNSPEC,
.target = led_tg,
.targetsize = XT_ALIGN(sizeof(struct xt_led_info)),
.checkentry = led_tg_check,
.destroy = led_tg_destroy,
.me = THIS_MODULE,
};
static int __init led_tg_init(void)
{
return xt_register_target(&led_tg_reg);
}
static void __exit led_tg_exit(void)
{
xt_unregister_target(&led_tg_reg);
}
module_init(led_tg_init);
module_exit(led_tg_exit);
/*
* (C) 2008-2009 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/jhash.h>
#include <linux/ip.h>
#include <net/ipv6.h>
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <linux/netfilter/xt_cluster.h>
static inline u_int32_t nf_ct_orig_ipv4_src(const struct nf_conn *ct)
{
return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
}
static inline const void *nf_ct_orig_ipv6_src(const struct nf_conn *ct)
{
return ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6;
}
static inline u_int32_t
xt_cluster_hash_ipv4(u_int32_t ip, const struct xt_cluster_match_info *info)
{
return jhash_1word(ip, info->hash_seed);
}
static inline u_int32_t
xt_cluster_hash_ipv6(const void *ip, const struct xt_cluster_match_info *info)
{
return jhash2(ip, NF_CT_TUPLE_L3SIZE / sizeof(__u32), info->hash_seed);
}
static inline u_int32_t
xt_cluster_hash(const struct nf_conn *ct,
const struct xt_cluster_match_info *info)
{
u_int32_t hash = 0;
switch(nf_ct_l3num(ct)) {
case AF_INET:
hash = xt_cluster_hash_ipv4(nf_ct_orig_ipv4_src(ct), info);
break;
case AF_INET6:
hash = xt_cluster_hash_ipv6(nf_ct_orig_ipv6_src(ct), info);
break;
default:
WARN_ON(1);
break;
}
return (((u64)hash * info->total_nodes) >> 32);
}
static inline bool
xt_cluster_is_multicast_addr(const struct sk_buff *skb, u_int8_t family)
{
bool is_multicast = false;
switch(family) {
case NFPROTO_IPV4:
is_multicast = ipv4_is_multicast(ip_hdr(skb)->daddr);
break;
case NFPROTO_IPV6:
is_multicast = ipv6_addr_type(&ipv6_hdr(skb)->daddr) &
IPV6_ADDR_MULTICAST;
break;
default:
WARN_ON(1);
break;
}
return is_multicast;
}
static bool
xt_cluster_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
struct sk_buff *pskb = (struct sk_buff *)skb;
const struct xt_cluster_match_info *info = par->matchinfo;
const struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
unsigned long hash;
/* This match assumes that all nodes see the same packets. This can be
* achieved if the switch that connects the cluster nodes support some
* sort of 'port mirroring'. However, if your switch does not support
* this, your cluster nodes can reply ARP request using a multicast MAC
* address. Thus, your switch will flood the same packets to the
* cluster nodes with the same multicast MAC address. Using a multicast
* link address is a RFC 1812 (section 3.3.2) violation, but this works
* fine in practise.
*
* Unfortunately, if you use the multicast MAC address, the link layer
* sets skbuff's pkt_type to PACKET_MULTICAST, which is not accepted
* by TCP and others for packets coming to this node. For that reason,
* this match mangles skbuff's pkt_type if it detects a packet
* addressed to a unicast address but using PACKET_MULTICAST. Yes, I
* know, matches should not alter packets, but we are doing this here
* because we would need to add a PKTTYPE target for this sole purpose.
*/
if (!xt_cluster_is_multicast_addr(skb, par->family) &&
skb->pkt_type == PACKET_MULTICAST) {
pskb->pkt_type = PACKET_HOST;
}
ct = nf_ct_get(skb, &ctinfo);
if (ct == NULL)
return false;
if (ct == &nf_conntrack_untracked)
return false;
if (ct->master)
hash = xt_cluster_hash(ct->master, info);
else
hash = xt_cluster_hash(ct, info);
return !!((1 << hash) & info->node_mask) ^
!!(info->flags & XT_CLUSTER_F_INV);
}
static bool xt_cluster_mt_checkentry(const struct xt_mtchk_param *par)
{
struct xt_cluster_match_info *info = par->matchinfo;
if (info->node_mask >= (1 << info->total_nodes)) {
printk(KERN_ERR "xt_cluster: this node mask cannot be "
"higher than the total number of nodes\n");
return false;
}
return true;
}
static struct xt_match xt_cluster_match __read_mostly = {
.name = "cluster",
.family = NFPROTO_UNSPEC,
.match = xt_cluster_mt,
.checkentry = xt_cluster_mt_checkentry,
.matchsize = sizeof(struct xt_cluster_match_info),
.me = THIS_MODULE,
};
static int __init xt_cluster_mt_init(void)
{
return xt_register_match(&xt_cluster_match);
}
static void __exit xt_cluster_mt_fini(void)
{
xt_unregister_match(&xt_cluster_match);
}
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Xtables: hash-based cluster match");
MODULE_ALIAS("ipt_cluster");
MODULE_ALIAS("ip6t_cluster");
module_init(xt_cluster_mt_init);
module_exit(xt_cluster_mt_fini);
...@@ -149,7 +149,7 @@ dsthash_alloc_init(struct xt_hashlimit_htable *ht, ...@@ -149,7 +149,7 @@ dsthash_alloc_init(struct xt_hashlimit_htable *ht,
/* initialize hash with random val at the time we allocate /* initialize hash with random val at the time we allocate
* the first hashtable entry */ * the first hashtable entry */
if (!ht->rnd_initialized) { if (!ht->rnd_initialized) {
get_random_bytes(&ht->rnd, 4); get_random_bytes(&ht->rnd, sizeof(ht->rnd));
ht->rnd_initialized = 1; ht->rnd_initialized = 1;
} }
...@@ -565,8 +565,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo, ...@@ -565,8 +565,7 @@ hashlimit_init_dst(const struct xt_hashlimit_htable *hinfo,
static bool static bool
hashlimit_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par) hashlimit_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
const struct xt_hashlimit_info *r = const struct xt_hashlimit_info *r = par->matchinfo;
((const struct xt_hashlimit_info *)par->matchinfo)->u.master;
struct xt_hashlimit_htable *hinfo = r->hinfo; struct xt_hashlimit_htable *hinfo = r->hinfo;
unsigned long now = jiffies; unsigned long now = jiffies;
struct dsthash_ent *dh; struct dsthash_ent *dh;
...@@ -702,8 +701,6 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) ...@@ -702,8 +701,6 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
} }
mutex_unlock(&hlimit_mutex); mutex_unlock(&hlimit_mutex);
/* Ugly hack: For SMP, we only want to use one set */
r->u.master = r;
return true; return true;
} }
......
/* Hop Limit matching module */ /*
* IP tables module for matching the value of the TTL
/* (C) 2001-2002 Maciej Soltysiak <solt@dns.toxicfilms.tv> * (C) 2000,2001 by Harald Welte <laforge@netfilter.org>
* Based on HW's ttl module *
* Hop Limit matching module
* (C) 2001-2002 Maciej Soltysiak <solt@dns.toxicfilms.tv>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/ip.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netfilter_ipv6/ip6t_hl.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ipt_ttl.h>
#include <linux/netfilter_ipv6/ip6t_hl.h>
MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>"); MODULE_AUTHOR("Maciej Soltysiak <solt@dns.toxicfilms.tv>");
MODULE_DESCRIPTION("Xtables: IPv6 Hop Limit field match"); MODULE_DESCRIPTION("Xtables: Hoplimit/TTL field match");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_ttl");
MODULE_ALIAS("ip6t_hl");
static bool ttl_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
const struct ipt_ttl_info *info = par->matchinfo;
const u8 ttl = ip_hdr(skb)->ttl;
switch (info->mode) {
case IPT_TTL_EQ:
return ttl == info->ttl;
case IPT_TTL_NE:
return ttl != info->ttl;
case IPT_TTL_LT:
return ttl < info->ttl;
case IPT_TTL_GT:
return ttl > info->ttl;
default:
printk(KERN_WARNING "ipt_ttl: unknown mode %d\n",
info->mode);
return false;
}
return false;
}
static bool hl_mt6(const struct sk_buff *skb, const struct xt_match_param *par) static bool hl_mt6(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
...@@ -46,23 +75,34 @@ static bool hl_mt6(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -46,23 +75,34 @@ static bool hl_mt6(const struct sk_buff *skb, const struct xt_match_param *par)
return false; return false;
} }
static struct xt_match hl_mt6_reg __read_mostly = { static struct xt_match hl_mt_reg[] __read_mostly = {
.name = "hl", {
.family = NFPROTO_IPV6, .name = "ttl",
.match = hl_mt6, .revision = 0,
.matchsize = sizeof(struct ip6t_hl_info), .family = NFPROTO_IPV4,
.me = THIS_MODULE, .match = ttl_mt,
.matchsize = sizeof(struct ipt_ttl_info),
.me = THIS_MODULE,
},
{
.name = "hl",
.revision = 0,
.family = NFPROTO_IPV6,
.match = hl_mt6,
.matchsize = sizeof(struct ip6t_hl_info),
.me = THIS_MODULE,
},
}; };
static int __init hl_mt6_init(void) static int __init hl_mt_init(void)
{ {
return xt_register_match(&hl_mt6_reg); return xt_register_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg));
} }
static void __exit hl_mt6_exit(void) static void __exit hl_mt_exit(void)
{ {
xt_unregister_match(&hl_mt6_reg); xt_unregister_matches(hl_mt_reg, ARRAY_SIZE(hl_mt_reg));
} }
module_init(hl_mt6_init); module_init(hl_mt_init);
module_exit(hl_mt6_exit); module_exit(hl_mt_exit);
...@@ -14,6 +14,11 @@ ...@@ -14,6 +14,11 @@
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_limit.h> #include <linux/netfilter/xt_limit.h>
struct xt_limit_priv {
unsigned long prev;
uint32_t credit;
};
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>"); MODULE_AUTHOR("Herve Eychenne <rv@wallfire.org>");
MODULE_DESCRIPTION("Xtables: rate-limit match"); MODULE_DESCRIPTION("Xtables: rate-limit match");
...@@ -60,18 +65,18 @@ static DEFINE_SPINLOCK(limit_lock); ...@@ -60,18 +65,18 @@ static DEFINE_SPINLOCK(limit_lock);
static bool static bool
limit_mt(const struct sk_buff *skb, const struct xt_match_param *par) limit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
struct xt_rateinfo *r = const struct xt_rateinfo *r = par->matchinfo;
((const struct xt_rateinfo *)par->matchinfo)->master; struct xt_limit_priv *priv = r->master;
unsigned long now = jiffies; unsigned long now = jiffies;
spin_lock_bh(&limit_lock); spin_lock_bh(&limit_lock);
r->credit += (now - xchg(&r->prev, now)) * CREDITS_PER_JIFFY; priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY;
if (r->credit > r->credit_cap) if (priv->credit > r->credit_cap)
r->credit = r->credit_cap; priv->credit = r->credit_cap;
if (r->credit >= r->cost) { if (priv->credit >= r->cost) {
/* We're not limited. */ /* We're not limited. */
r->credit -= r->cost; priv->credit -= r->cost;
spin_unlock_bh(&limit_lock); spin_unlock_bh(&limit_lock);
return true; return true;
} }
...@@ -95,6 +100,7 @@ user2credits(u_int32_t user) ...@@ -95,6 +100,7 @@ user2credits(u_int32_t user)
static bool limit_mt_check(const struct xt_mtchk_param *par) static bool limit_mt_check(const struct xt_mtchk_param *par)
{ {
struct xt_rateinfo *r = par->matchinfo; struct xt_rateinfo *r = par->matchinfo;
struct xt_limit_priv *priv;
/* Check for overflow. */ /* Check for overflow. */
if (r->burst == 0 if (r->burst == 0
...@@ -104,19 +110,30 @@ static bool limit_mt_check(const struct xt_mtchk_param *par) ...@@ -104,19 +110,30 @@ static bool limit_mt_check(const struct xt_mtchk_param *par)
return false; return false;
} }
/* For SMP, we only want to use one set of counters. */ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
r->master = r; if (priv == NULL)
return -ENOMEM;
/* For SMP, we only want to use one set of state. */
r->master = priv;
if (r->cost == 0) { if (r->cost == 0) {
/* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies * /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
128. */ 128. */
r->prev = jiffies; priv->prev = jiffies;
r->credit = user2credits(r->avg * r->burst); /* Credits full. */ priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */ r->credit_cap = user2credits(r->avg * r->burst); /* Credits full. */
r->cost = user2credits(r->avg); r->cost = user2credits(r->avg);
} }
return true; return true;
} }
static void limit_mt_destroy(const struct xt_mtdtor_param *par)
{
const struct xt_rateinfo *info = par->matchinfo;
kfree(info->master);
}
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
struct compat_xt_rateinfo { struct compat_xt_rateinfo {
u_int32_t avg; u_int32_t avg;
...@@ -167,6 +184,7 @@ static struct xt_match limit_mt_reg __read_mostly = { ...@@ -167,6 +184,7 @@ static struct xt_match limit_mt_reg __read_mostly = {
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.match = limit_mt, .match = limit_mt,
.checkentry = limit_mt_check, .checkentry = limit_mt_check,
.destroy = limit_mt_destroy,
.matchsize = sizeof(struct xt_rateinfo), .matchsize = sizeof(struct xt_rateinfo),
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compatsize = sizeof(struct compat_xt_rateinfo), .compatsize = sizeof(struct compat_xt_rateinfo),
......
...@@ -20,13 +20,30 @@ MODULE_DESCRIPTION("Xtables: Bridge physical device match"); ...@@ -20,13 +20,30 @@ MODULE_DESCRIPTION("Xtables: Bridge physical device match");
MODULE_ALIAS("ipt_physdev"); MODULE_ALIAS("ipt_physdev");
MODULE_ALIAS("ip6t_physdev"); MODULE_ALIAS("ip6t_physdev");
static unsigned long ifname_compare(const char *_a, const char *_b, const char *_mask)
{
const unsigned long *a = (const unsigned long *)_a;
const unsigned long *b = (const unsigned long *)_b;
const unsigned long *mask = (const unsigned long *)_mask;
unsigned long ret;
ret = (a[0] ^ b[0]) & mask[0];
if (IFNAMSIZ > sizeof(unsigned long))
ret |= (a[1] ^ b[1]) & mask[1];
if (IFNAMSIZ > 2 * sizeof(unsigned long))
ret |= (a[2] ^ b[2]) & mask[2];
if (IFNAMSIZ > 3 * sizeof(unsigned long))
ret |= (a[3] ^ b[3]) & mask[3];
BUILD_BUG_ON(IFNAMSIZ > 4 * sizeof(unsigned long));
return ret;
}
static bool static bool
physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par) physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
int i; static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
static const char nulldevname[IFNAMSIZ];
const struct xt_physdev_info *info = par->matchinfo; const struct xt_physdev_info *info = par->matchinfo;
bool ret; unsigned long ret;
const char *indev, *outdev; const char *indev, *outdev;
const struct nf_bridge_info *nf_bridge; const struct nf_bridge_info *nf_bridge;
...@@ -68,11 +85,7 @@ physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -68,11 +85,7 @@ physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par)
if (!(info->bitmask & XT_PHYSDEV_OP_IN)) if (!(info->bitmask & XT_PHYSDEV_OP_IN))
goto match_outdev; goto match_outdev;
indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname; indev = nf_bridge->physindev ? nf_bridge->physindev->name : nulldevname;
for (i = 0, ret = false; i < IFNAMSIZ/sizeof(unsigned int); i++) { ret = ifname_compare(indev, info->physindev, info->in_mask);
ret |= (((const unsigned int *)indev)[i]
^ ((const unsigned int *)info->physindev)[i])
& ((const unsigned int *)info->in_mask)[i];
}
if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN)) if (!ret ^ !(info->invert & XT_PHYSDEV_OP_IN))
return false; return false;
...@@ -82,13 +95,9 @@ physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -82,13 +95,9 @@ physdev_mt(const struct sk_buff *skb, const struct xt_match_param *par)
return true; return true;
outdev = nf_bridge->physoutdev ? outdev = nf_bridge->physoutdev ?
nf_bridge->physoutdev->name : nulldevname; nf_bridge->physoutdev->name : nulldevname;
for (i = 0, ret = false; i < IFNAMSIZ/sizeof(unsigned int); i++) { ret = ifname_compare(outdev, info->physoutdev, info->out_mask);
ret |= (((const unsigned int *)outdev)[i]
^ ((const unsigned int *)info->physoutdev)[i])
& ((const unsigned int *)info->out_mask)[i];
}
return ret ^ !(info->invert & XT_PHYSDEV_OP_OUT); return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
} }
static bool physdev_mt_check(const struct xt_mtchk_param *par) static bool physdev_mt_check(const struct xt_mtchk_param *par)
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_quota.h> #include <linux/netfilter/xt_quota.h>
struct xt_quota_priv {
uint64_t quota;
};
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sam Johnston <samj@samj.net>"); MODULE_AUTHOR("Sam Johnston <samj@samj.net>");
MODULE_DESCRIPTION("Xtables: countdown quota match"); MODULE_DESCRIPTION("Xtables: countdown quota match");
...@@ -20,18 +24,20 @@ static DEFINE_SPINLOCK(quota_lock); ...@@ -20,18 +24,20 @@ static DEFINE_SPINLOCK(quota_lock);
static bool static bool
quota_mt(const struct sk_buff *skb, const struct xt_match_param *par) quota_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
struct xt_quota_info *q = struct xt_quota_info *q = (void *)par->matchinfo;
((const struct xt_quota_info *)par->matchinfo)->master; struct xt_quota_priv *priv = q->master;
bool ret = q->flags & XT_QUOTA_INVERT; bool ret = q->flags & XT_QUOTA_INVERT;
spin_lock_bh(&quota_lock); spin_lock_bh(&quota_lock);
if (q->quota >= skb->len) { if (priv->quota >= skb->len) {
q->quota -= skb->len; priv->quota -= skb->len;
ret = !ret; ret = !ret;
} else { } else {
/* we do not allow even small packets from now on */ /* we do not allow even small packets from now on */
q->quota = 0; priv->quota = 0;
} }
/* Copy quota back to matchinfo so that iptables can display it */
q->quota = priv->quota;
spin_unlock_bh(&quota_lock); spin_unlock_bh(&quota_lock);
return ret; return ret;
...@@ -43,17 +49,28 @@ static bool quota_mt_check(const struct xt_mtchk_param *par) ...@@ -43,17 +49,28 @@ static bool quota_mt_check(const struct xt_mtchk_param *par)
if (q->flags & ~XT_QUOTA_MASK) if (q->flags & ~XT_QUOTA_MASK)
return false; return false;
/* For SMP, we only want to use one set of counters. */
q->master = q; q->master = kmalloc(sizeof(*q->master), GFP_KERNEL);
if (q->master == NULL)
return -ENOMEM;
return true; return true;
} }
static void quota_mt_destroy(const struct xt_mtdtor_param *par)
{
const struct xt_quota_info *q = par->matchinfo;
kfree(q->master);
}
static struct xt_match quota_mt_reg __read_mostly = { static struct xt_match quota_mt_reg __read_mostly = {
.name = "quota", .name = "quota",
.revision = 0, .revision = 0,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.match = quota_mt, .match = quota_mt,
.checkentry = quota_mt_check, .checkentry = quota_mt_check,
.destroy = quota_mt_destroy,
.matchsize = sizeof(struct xt_quota_info), .matchsize = sizeof(struct xt_quota_info),
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
......
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
#include <linux/netfilter/xt_statistic.h> #include <linux/netfilter/xt_statistic.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
struct xt_statistic_priv {
uint32_t count;
};
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_DESCRIPTION("Xtables: statistics-based matching (\"Nth\", random)"); MODULE_DESCRIPTION("Xtables: statistics-based matching (\"Nth\", random)");
...@@ -27,7 +31,7 @@ static DEFINE_SPINLOCK(nth_lock); ...@@ -27,7 +31,7 @@ static DEFINE_SPINLOCK(nth_lock);
static bool static bool
statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
struct xt_statistic_info *info = (void *)par->matchinfo; const struct xt_statistic_info *info = par->matchinfo;
bool ret = info->flags & XT_STATISTIC_INVERT; bool ret = info->flags & XT_STATISTIC_INVERT;
switch (info->mode) { switch (info->mode) {
...@@ -36,10 +40,9 @@ statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -36,10 +40,9 @@ statistic_mt(const struct sk_buff *skb, const struct xt_match_param *par)
ret = !ret; ret = !ret;
break; break;
case XT_STATISTIC_MODE_NTH: case XT_STATISTIC_MODE_NTH:
info = info->master;
spin_lock_bh(&nth_lock); spin_lock_bh(&nth_lock);
if (info->u.nth.count++ == info->u.nth.every) { if (info->master->count++ == info->u.nth.every) {
info->u.nth.count = 0; info->master->count = 0;
ret = !ret; ret = !ret;
} }
spin_unlock_bh(&nth_lock); spin_unlock_bh(&nth_lock);
...@@ -56,16 +59,31 @@ static bool statistic_mt_check(const struct xt_mtchk_param *par) ...@@ -56,16 +59,31 @@ static bool statistic_mt_check(const struct xt_mtchk_param *par)
if (info->mode > XT_STATISTIC_MODE_MAX || if (info->mode > XT_STATISTIC_MODE_MAX ||
info->flags & ~XT_STATISTIC_MASK) info->flags & ~XT_STATISTIC_MASK)
return false; return false;
info->master = info;
info->master = kzalloc(sizeof(*info->master), GFP_KERNEL);
if (info->master == NULL) {
printk(KERN_ERR KBUILD_MODNAME ": Out of memory\n");
return false;
}
info->master->count = info->u.nth.count;
return true; return true;
} }
static void statistic_mt_destroy(const struct xt_mtdtor_param *par)
{
const struct xt_statistic_info *info = par->matchinfo;
kfree(info->master);
}
static struct xt_match xt_statistic_mt_reg __read_mostly = { static struct xt_match xt_statistic_mt_reg __read_mostly = {
.name = "statistic", .name = "statistic",
.revision = 0, .revision = 0,
.family = NFPROTO_UNSPEC, .family = NFPROTO_UNSPEC,
.match = statistic_mt, .match = statistic_mt,
.checkentry = statistic_mt_check, .checkentry = statistic_mt_check,
.destroy = statistic_mt_destroy,
.matchsize = sizeof(struct xt_statistic_info), .matchsize = sizeof(struct xt_statistic_info),
.me = THIS_MODULE, .me = THIS_MODULE,
}; };
......
...@@ -1117,6 +1117,7 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code) ...@@ -1117,6 +1117,7 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
read_unlock(&nl_table_lock); read_unlock(&nl_table_lock);
} }
EXPORT_SYMBOL(netlink_set_err);
/* must be called with netlink table grabbed */ /* must be called with netlink table grabbed */
static void netlink_update_socket_mc(struct netlink_sock *nlk, static void netlink_update_socket_mc(struct netlink_sock *nlk,
......
...@@ -61,7 +61,7 @@ static struct ctl_table_root net_sysctl_root = { ...@@ -61,7 +61,7 @@ static struct ctl_table_root net_sysctl_root = {
static int net_ctl_ro_header_perms(struct ctl_table_root *root, static int net_ctl_ro_header_perms(struct ctl_table_root *root,
struct nsproxy *namespaces, struct ctl_table *table) struct nsproxy *namespaces, struct ctl_table *table)
{ {
if (namespaces->net_ns == &init_net) if (net_eq(namespaces->net_ns, &init_net))
return table->mode; return table->mode;
else else
return table->mode & ~0222; return table->mode & ~0222;
......
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