Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next

Pablo Neira Ayuso says:

====================
netfilter/ipvs updates for net-next

The following patchset contains Netfilter updates for your net-next
tree, this includes the NAT redirection support for nf_tables, the
cgroup support for nft meta and conntrack zone support for the connlimit
match. Coming after those, a bunch of sparse warning fixes, missing
netns bits and cleanups. More specifically, they are:

1) Prepare IPv4 and IPv6 NAT redirect code to use it from nf_tables,
   patches from Arturo Borrero.

2) Introduce the nf_tables redir expression, from Arturo Borrero.

3) Remove an unnecessary assignment in ip_vs_xmit/__ip_vs_get_out_rt().
   Patch from Alex Gartrell.

4) Add nft_log_dereference() macro to the nf_log infrastructure, patch
   from Marcelo Leitner.

5) Add some extra validation when registering logger families, also
   from Marcelo.

6) Some spelling cleanups from stephen hemminger.

7) Fix sparse warning in nf_logger_find_get().

8) Add cgroup support to nf_tables meta, patch from Ana Rey.

9) A Kconfig fix for the new redir expression and fix sparse warnings in
   the new redir expression.

10) Fix several sparse warnings in the netfilter tree, from
    Florian Westphal.

11) Reduce verbosity when OOM in nfnetlink_log. User can basically do
    nothing when this situation occurs.

12) Add conntrack zone support to xt_connlimit, again from Florian.

13) Add netnamespace support to the h323 conntrack helper, contributed
    by Vasily Averin.

14) Remove unnecessary nul-pointer checks before free_percpu() and
    module_put(), from Markus Elfring.

15) Use pr_fmt in nfnetlink_log, again patch from Marcelo Leitner.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2014-11-24 16:00:58 -05:00
commit 958d03b016
38 changed files with 636 additions and 156 deletions

View file

@ -0,0 +1,9 @@
#ifndef _NF_NAT_REDIRECT_IPV4_H_
#define _NF_NAT_REDIRECT_IPV4_H_
unsigned int
nf_nat_redirect_ipv4(struct sk_buff *skb,
const struct nf_nat_ipv4_multi_range_compat *mr,
unsigned int hooknum);
#endif /* _NF_NAT_REDIRECT_IPV4_H_ */

View file

@ -0,0 +1,8 @@
#ifndef _NF_NAT_REDIRECT_IPV6_H_
#define _NF_NAT_REDIRECT_IPV6_H_
unsigned int
nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
unsigned int hooknum);
#endif /* _NF_NAT_REDIRECT_IPV6_H_ */

View file

@ -0,0 +1,21 @@
#ifndef _NFT_REDIR_H_
#define _NFT_REDIR_H_
struct nft_redir {
enum nft_registers sreg_proto_min:8;
enum nft_registers sreg_proto_max:8;
u16 flags;
};
extern const struct nla_policy nft_redir_policy[];
int nft_redir_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr);
int nft_redir_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nft_data **data);
#endif /* _NFT_REDIR_H_ */

View file

@ -579,6 +579,7 @@ enum nft_exthdr_attributes {
* @NFT_META_CPU: cpu id through smp_processor_id() * @NFT_META_CPU: cpu id through smp_processor_id()
* @NFT_META_IIFGROUP: packet input interface group * @NFT_META_IIFGROUP: packet input interface group
* @NFT_META_OIFGROUP: packet output interface group * @NFT_META_OIFGROUP: packet output interface group
* @NFT_META_CGROUP: socket control group (skb->sk->sk_classid)
*/ */
enum nft_meta_keys { enum nft_meta_keys {
NFT_META_LEN, NFT_META_LEN,
@ -604,6 +605,7 @@ enum nft_meta_keys {
NFT_META_CPU, NFT_META_CPU,
NFT_META_IIFGROUP, NFT_META_IIFGROUP,
NFT_META_OIFGROUP, NFT_META_OIFGROUP,
NFT_META_CGROUP,
}; };
/** /**
@ -837,6 +839,22 @@ enum nft_masq_attributes {
}; };
#define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1) #define NFTA_MASQ_MAX (__NFTA_MASQ_MAX - 1)
/**
* enum nft_redir_attributes - nf_tables redirect expression netlink attributes
*
* @NFTA_REDIR_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
* @NFTA_REDIR_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
* @NFTA_REDIR_FLAGS: NAT flags (see NF_NAT_RANGE_* in linux/netfilter/nf_nat.h) (NLA_U32)
*/
enum nft_redir_attributes {
NFTA_REDIR_UNSPEC,
NFTA_REDIR_REG_PROTO_MIN,
NFTA_REDIR_REG_PROTO_MAX,
NFTA_REDIR_FLAGS,
__NFTA_REDIR_MAX
};
#define NFTA_REDIR_MAX (__NFTA_REDIR_MAX - 1)
/** /**
* enum nft_gen_attributes - nf_tables ruleset generation attributes * enum nft_gen_attributes - nf_tables ruleset generation attributes
* *

View file

@ -35,6 +35,7 @@
#include <net/ip.h> #include <net/ip.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/route.h> #include <net/route.h>
#include <net/netfilter/br_netfilter.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "br_private.h" #include "br_private.h"

View file

@ -104,6 +104,12 @@ config NF_NAT_MASQUERADE_IPV4
This is the kernel functionality to provide NAT in the masquerade This is the kernel functionality to provide NAT in the masquerade
flavour (automatic source address selection). flavour (automatic source address selection).
config NF_NAT_REDIRECT_IPV4
tristate "IPv4 redirect support"
help
This is the kernel functionality to provide NAT in the redirect
flavour (redirect packets to local machine).
config NFT_MASQ_IPV4 config NFT_MASQ_IPV4
tristate "IPv4 masquerading support for nf_tables" tristate "IPv4 masquerading support for nf_tables"
depends on NF_TABLES_IPV4 depends on NF_TABLES_IPV4
@ -113,6 +119,15 @@ config NFT_MASQ_IPV4
This is the expression that provides IPv4 masquerading support for This is the expression that provides IPv4 masquerading support for
nf_tables. nf_tables.
config NFT_REDIR_IPV4
tristate "IPv4 redirect support for nf_tables"
depends on NF_TABLES_IPV4
depends on NFT_REDIR
select NF_NAT_REDIRECT_IPV4
help
This is the expression that provides IPv4 redirect support for
nf_tables.
config NF_NAT_SNMP_BASIC config NF_NAT_SNMP_BASIC
tristate "Basic SNMP-ALG support" tristate "Basic SNMP-ALG support"
depends on NF_CONNTRACK_SNMP depends on NF_CONNTRACK_SNMP

View file

@ -31,6 +31,7 @@ obj-$(CONFIG_NF_NAT_H323) += nf_nat_h323.o
obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o obj-$(CONFIG_NF_NAT_PPTP) += nf_nat_pptp.o
obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o
obj-$(CONFIG_NF_NAT_MASQUERADE_IPV4) += nf_nat_masquerade_ipv4.o obj-$(CONFIG_NF_NAT_MASQUERADE_IPV4) += nf_nat_masquerade_ipv4.o
obj-$(CONFIG_NF_NAT_REDIRECT_IPV4) += nf_nat_redirect_ipv4.o
# NAT protocols (nf_nat) # NAT protocols (nf_nat)
obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
@ -40,6 +41,7 @@ obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o
obj-$(CONFIG_NFT_REDIR_IPV4) += nft_redir_ipv4.o
obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o
# generic IP tables # generic IP tables

View file

@ -10,6 +10,7 @@
* 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.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@ -74,12 +75,12 @@ static void dump_arp_packet(struct nf_log_buf *m,
ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst); ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst);
} }
void nf_log_arp_packet(struct net *net, u_int8_t pf, static void nf_log_arp_packet(struct net *net, u_int8_t pf,
unsigned int hooknum, const struct sk_buff *skb, unsigned int hooknum, const struct sk_buff *skb,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
const struct nf_loginfo *loginfo, const struct nf_loginfo *loginfo,
const char *prefix) const char *prefix)
{ {
struct nf_log_buf *m; struct nf_log_buf *m;
@ -130,8 +131,17 @@ static int __init nf_log_arp_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
nf_log_register(NFPROTO_ARP, &nf_arp_logger); ret = nf_log_register(NFPROTO_ARP, &nf_arp_logger);
if (ret < 0) {
pr_err("failed to register logger\n");
goto err1;
}
return 0; return 0;
err1:
unregister_pernet_subsys(&nf_log_arp_net_ops);
return ret;
} }
static void __exit nf_log_arp_exit(void) static void __exit nf_log_arp_exit(void)

View file

@ -5,6 +5,7 @@
* 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.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@ -366,8 +367,17 @@ static int __init nf_log_ipv4_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
nf_log_register(NFPROTO_IPV4, &nf_ip_logger); ret = nf_log_register(NFPROTO_IPV4, &nf_ip_logger);
if (ret < 0) {
pr_err("failed to register logger\n");
goto err1;
}
return 0; return 0;
err1:
unregister_pernet_subsys(&nf_log_ipv4_net_ops);
return ret;
} }
static void __exit nf_log_ipv4_exit(void) static void __exit nf_log_ipv4_exit(void)

View file

@ -0,0 +1,82 @@
/*
* (C) 1999-2001 Paul `Rusty' Russell
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
* Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
*
* 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.
*
* Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
* NAT funded by Astaro.
*/
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/types.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h>
#include <net/addrconf.h>
#include <net/checksum.h>
#include <net/protocol.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/ipv4/nf_nat_redirect.h>
unsigned int
nf_nat_redirect_ipv4(struct sk_buff *skb,
const struct nf_nat_ipv4_multi_range_compat *mr,
unsigned int hooknum)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
__be32 newdst;
struct nf_nat_range newrange;
NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING ||
hooknum == NF_INET_LOCAL_OUT);
ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
/* Local packets: make them go to loopback */
if (hooknum == NF_INET_LOCAL_OUT) {
newdst = htonl(0x7F000001);
} else {
struct in_device *indev;
struct in_ifaddr *ifa;
newdst = 0;
rcu_read_lock();
indev = __in_dev_get_rcu(skb->dev);
if (indev != NULL) {
ifa = indev->ifa_list;
newdst = ifa->ifa_local;
}
rcu_read_unlock();
if (!newdst)
return NF_DROP;
}
/* Transfer from original range. */
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
newrange.min_addr.ip = newdst;
newrange.max_addr.ip = newdst;
newrange.min_proto = mr->range[0].min;
newrange.max_proto = mr->range[0].max;
/* Hand modified range to generic setup. */
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
}
EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");

View file

@ -11,6 +11,7 @@
#include <net/tcp.h> #include <net/tcp.h>
#include <net/route.h> #include <net/route.h>
#include <net/dst.h> #include <net/dst.h>
#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h> #include <linux/netfilter_ipv4.h>
#include <net/netfilter/ipv4/nf_reject.h> #include <net/netfilter/ipv4/nf_reject.h>

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/ipv4/nf_nat_redirect.h>
#include <net/netfilter/nft_redir.h>
static void nft_redir_ipv4_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_redir *priv = nft_expr_priv(expr);
struct nf_nat_ipv4_multi_range_compat mr;
unsigned int verdict;
memset(&mr, 0, sizeof(mr));
if (priv->sreg_proto_min) {
mr.range[0].min.all = (__force __be16)
data[priv->sreg_proto_min].data[0];
mr.range[0].max.all = (__force __be16)
data[priv->sreg_proto_max].data[0];
mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
}
mr.range[0].flags |= priv->flags;
verdict = nf_nat_redirect_ipv4(pkt->skb, &mr, pkt->ops->hooknum);
data[NFT_REG_VERDICT].verdict = verdict;
}
static struct nft_expr_type nft_redir_ipv4_type;
static const struct nft_expr_ops nft_redir_ipv4_ops = {
.type = &nft_redir_ipv4_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
.eval = nft_redir_ipv4_eval,
.init = nft_redir_init,
.dump = nft_redir_dump,
.validate = nft_redir_validate,
};
static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
.family = NFPROTO_IPV4,
.name = "redir",
.ops = &nft_redir_ipv4_ops,
.policy = nft_redir_policy,
.maxattr = NFTA_REDIR_MAX,
.owner = THIS_MODULE,
};
static int __init nft_redir_ipv4_module_init(void)
{
return nft_register_expr(&nft_redir_ipv4_type);
}
static void __exit nft_redir_ipv4_module_exit(void)
{
nft_unregister_expr(&nft_redir_ipv4_type);
}
module_init(nft_redir_ipv4_module_init);
module_exit(nft_redir_ipv4_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir");

View file

@ -19,9 +19,9 @@
#include <net/netfilter/ipv4/nf_reject.h> #include <net/netfilter/ipv4/nf_reject.h>
#include <net/netfilter/nft_reject.h> #include <net/netfilter/nft_reject.h>
void nft_reject_ipv4_eval(const struct nft_expr *expr, static void nft_reject_ipv4_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1], struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
{ {
struct nft_reject *priv = nft_expr_priv(expr); struct nft_reject *priv = nft_expr_priv(expr);
@ -36,7 +36,6 @@ void nft_reject_ipv4_eval(const struct nft_expr *expr,
data[NFT_REG_VERDICT].verdict = NF_DROP; data[NFT_REG_VERDICT].verdict = NF_DROP;
} }
EXPORT_SYMBOL_GPL(nft_reject_ipv4_eval);
static struct nft_expr_type nft_reject_ipv4_type; static struct nft_expr_type nft_reject_ipv4_type;
static const struct nft_expr_ops nft_reject_ipv4_ops = { static const struct nft_expr_ops nft_reject_ipv4_ops = {

View file

@ -82,6 +82,12 @@ config NF_NAT_MASQUERADE_IPV6
This is the kernel functionality to provide NAT in the masquerade This is the kernel functionality to provide NAT in the masquerade
flavour (automatic source address selection) for IPv6. flavour (automatic source address selection) for IPv6.
config NF_NAT_REDIRECT_IPV6
tristate "IPv6 redirect support"
help
This is the kernel functionality to provide NAT in the redirect
flavour (redirect packet to local machine) for IPv6.
config NFT_MASQ_IPV6 config NFT_MASQ_IPV6
tristate "IPv6 masquerade support for nf_tables" tristate "IPv6 masquerade support for nf_tables"
depends on NF_TABLES_IPV6 depends on NF_TABLES_IPV6
@ -91,6 +97,15 @@ config NFT_MASQ_IPV6
This is the expression that provides IPv4 masquerading support for This is the expression that provides IPv4 masquerading support for
nf_tables. nf_tables.
config NFT_REDIR_IPV6
tristate "IPv6 redirect support for nf_tables"
depends on NF_TABLES_IPV6
depends on NFT_REDIR
select NF_NAT_REDIRECT_IPV6
help
This is the expression that provides IPv4 redirect support for
nf_tables.
endif # NF_NAT_IPV6 endif # NF_NAT_IPV6
config IP6_NF_IPTABLES config IP6_NF_IPTABLES

View file

@ -19,6 +19,7 @@ obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o
nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o
obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
obj-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o obj-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o
obj-$(CONFIG_NF_NAT_REDIRECT_IPV6) += nf_nat_redirect_ipv6.o
# defrag # defrag
nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
@ -36,6 +37,7 @@ obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o
obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o
obj-$(CONFIG_NFT_REDIR_IPV6) += nft_redir_ipv6.o
# matches # matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o

View file

@ -5,6 +5,7 @@
* 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.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@ -398,8 +399,17 @@ static int __init nf_log_ipv6_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
nf_log_register(NFPROTO_IPV6, &nf_ip6_logger); ret = nf_log_register(NFPROTO_IPV6, &nf_ip6_logger);
if (ret < 0) {
pr_err("failed to register logger\n");
goto err1;
}
return 0; return 0;
err1:
unregister_pernet_subsys(&nf_log_ipv6_net_ops);
return ret;
} }
static void __exit nf_log_ipv6_exit(void) static void __exit nf_log_ipv6_exit(void)

View file

@ -0,0 +1,75 @@
/*
* (C) 1999-2001 Paul `Rusty' Russell
* (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
* Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
*
* 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.
*
* Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
* NAT funded by Astaro.
*/
#include <linux/if.h>
#include <linux/inetdevice.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/types.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/x_tables.h>
#include <net/addrconf.h>
#include <net/checksum.h>
#include <net/protocol.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/ipv6/nf_nat_redirect.h>
static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
unsigned int
nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
unsigned int hooknum)
{
struct nf_nat_range newrange;
struct in6_addr newdst;
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (hooknum == NF_INET_LOCAL_OUT) {
newdst = loopback_addr;
} else {
struct inet6_dev *idev;
struct inet6_ifaddr *ifa;
bool addr = false;
rcu_read_lock();
idev = __in6_dev_get(skb->dev);
if (idev != NULL) {
list_for_each_entry(ifa, &idev->addr_list, if_list) {
newdst = ifa->addr;
addr = true;
break;
}
}
rcu_read_unlock();
if (!addr)
return NF_DROP;
}
newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
newrange.min_addr.in6 = newdst;
newrange.max_addr.in6 = newdst;
newrange.min_proto = range->min_proto;
newrange.max_proto = range->max_proto;
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
}
EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");

View file

@ -11,6 +11,7 @@
#include <net/ip6_route.h> #include <net/ip6_route.h>
#include <net/ip6_fib.h> #include <net/ip6_fib.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h> #include <linux/netfilter_ipv6.h>
#include <net/netfilter/ipv6/nf_reject.h> #include <net/netfilter/ipv6/nf_reject.h>

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nft_redir.h>
#include <net/netfilter/ipv6/nf_nat_redirect.h>
static void nft_redir_ipv6_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_redir *priv = nft_expr_priv(expr);
struct nf_nat_range range;
unsigned int verdict;
memset(&range, 0, sizeof(range));
if (priv->sreg_proto_min) {
range.min_proto.all = (__force __be16)
data[priv->sreg_proto_min].data[0];
range.max_proto.all = (__force __be16)
data[priv->sreg_proto_max].data[0];
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
}
range.flags |= priv->flags;
verdict = nf_nat_redirect_ipv6(pkt->skb, &range, pkt->ops->hooknum);
data[NFT_REG_VERDICT].verdict = verdict;
}
static struct nft_expr_type nft_redir_ipv6_type;
static const struct nft_expr_ops nft_redir_ipv6_ops = {
.type = &nft_redir_ipv6_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
.eval = nft_redir_ipv6_eval,
.init = nft_redir_init,
.dump = nft_redir_dump,
.validate = nft_redir_validate,
};
static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
.family = NFPROTO_IPV6,
.name = "redir",
.ops = &nft_redir_ipv6_ops,
.policy = nft_redir_policy,
.maxattr = NFTA_REDIR_MAX,
.owner = THIS_MODULE,
};
static int __init nft_redir_ipv6_module_init(void)
{
return nft_register_expr(&nft_redir_ipv6_type);
}
static void __exit nft_redir_ipv6_module_exit(void)
{
nft_unregister_expr(&nft_redir_ipv6_type);
}
module_init(nft_redir_ipv6_module_init);
module_exit(nft_redir_ipv6_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir");

View file

@ -19,9 +19,9 @@
#include <net/netfilter/nft_reject.h> #include <net/netfilter/nft_reject.h>
#include <net/netfilter/ipv6/nf_reject.h> #include <net/netfilter/ipv6/nf_reject.h>
void nft_reject_ipv6_eval(const struct nft_expr *expr, static void nft_reject_ipv6_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1], struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
{ {
struct nft_reject *priv = nft_expr_priv(expr); struct nft_reject *priv = nft_expr_priv(expr);
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out); struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
@ -38,7 +38,6 @@ void nft_reject_ipv6_eval(const struct nft_expr *expr,
data[NFT_REG_VERDICT].verdict = NF_DROP; data[NFT_REG_VERDICT].verdict = NF_DROP;
} }
EXPORT_SYMBOL_GPL(nft_reject_ipv6_eval);
static struct nft_expr_type nft_reject_ipv6_type; static struct nft_expr_type nft_reject_ipv6_type;
static const struct nft_expr_ops nft_reject_ipv6_ops = { static const struct nft_expr_ops nft_reject_ipv6_ops = {

View file

@ -505,6 +505,15 @@ config NFT_MASQ
This option adds the "masquerade" expression that you can use This option adds the "masquerade" expression that you can use
to perform NAT in the masquerade flavour. to perform NAT in the masquerade flavour.
config NFT_REDIR
depends on NF_TABLES
depends on NF_CONNTRACK
depends on NF_NAT
tristate "Netfilter nf_tables redirect support"
help
This options adds the "redirect" expression that you can use
to perform NAT in the redirect flavour.
config NFT_NAT config NFT_NAT
depends on NF_TABLES depends on NF_TABLES
depends on NF_CONNTRACK depends on NF_CONNTRACK
@ -835,6 +844,8 @@ config NETFILTER_XT_TARGET_RATEEST
config NETFILTER_XT_TARGET_REDIRECT config NETFILTER_XT_TARGET_REDIRECT
tristate "REDIRECT target support" tristate "REDIRECT target support"
depends on NF_NAT depends on NF_NAT
select NF_NAT_REDIRECT_IPV4 if NF_NAT_IPV4
select NF_NAT_REDIRECT_IPV6 if NF_NAT_IPV6
---help--- ---help---
REDIRECT is a special case of NAT: all incoming connections are REDIRECT is a special case of NAT: all incoming connections are
mapped onto the incoming interface's address, causing the packets to mapped onto the incoming interface's address, causing the packets to

View file

@ -88,6 +88,7 @@ obj-$(CONFIG_NFT_HASH) += nft_hash.o
obj-$(CONFIG_NFT_COUNTER) += nft_counter.o obj-$(CONFIG_NFT_COUNTER) += nft_counter.o
obj-$(CONFIG_NFT_LOG) += nft_log.o obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o obj-$(CONFIG_NFT_MASQ) += nft_masq.o
obj-$(CONFIG_NFT_REDIR) += nft_redir.o
# generic X tables # generic X tables
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o

View file

@ -17,6 +17,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/netfilter_ipv6.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/mutex.h> #include <linux/mutex.h>

View file

@ -465,8 +465,7 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc)
static void ip_vs_service_free(struct ip_vs_service *svc) static void ip_vs_service_free(struct ip_vs_service *svc)
{ {
if (svc->stats.cpustats) free_percpu(svc->stats.cpustats);
free_percpu(svc->stats.cpustats);
kfree(svc); kfree(svc);
} }

View file

@ -37,8 +37,7 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
rcu_read_unlock(); rcu_read_unlock();
return pe; return pe;
} }
if (pe->module) module_put(pe->module);
module_put(pe->module);
} }
rcu_read_unlock(); rcu_read_unlock();

View file

@ -104,8 +104,7 @@ static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name)
mutex_unlock(&ip_vs_sched_mutex); mutex_unlock(&ip_vs_sched_mutex);
return sched; return sched;
} }
if (sched->module) module_put(sched->module);
module_put(sched->module);
} }
mutex_unlock(&ip_vs_sched_mutex); mutex_unlock(&ip_vs_sched_mutex);

View file

@ -820,8 +820,7 @@ ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc,
p->pe_data = kmemdup(pe_data, pe_data_len, GFP_ATOMIC); p->pe_data = kmemdup(pe_data, pe_data_len, GFP_ATOMIC);
if (!p->pe_data) { if (!p->pe_data) {
if (p->pe->module) module_put(p->pe->module);
module_put(p->pe->module);
return -ENOMEM; return -ENOMEM;
} }
p->pe_data_len = pe_data_len; p->pe_data_len = pe_data_len;

View file

@ -293,7 +293,6 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
&dest->addr.ip, &dest_dst->dst_saddr.ip, &dest->addr.ip, &dest_dst->dst_saddr.ip,
atomic_read(&rt->dst.__refcnt)); atomic_read(&rt->dst.__refcnt));
} }
daddr = dest->addr.ip;
if (ret_saddr) if (ret_saddr)
*ret_saddr = dest_dst->dst_saddr.ip; *ret_saddr = dest_dst->dst_saddr.ip;
} else { } else {

View file

@ -728,7 +728,8 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
/* If the calling party is on the same side of the forward-to party, /* If the calling party is on the same side of the forward-to party,
* we don't need to track the second call */ * we don't need to track the second call */
static int callforward_do_filter(const union nf_inet_addr *src, static int callforward_do_filter(struct net *net,
const union nf_inet_addr *src,
const union nf_inet_addr *dst, const union nf_inet_addr *dst,
u_int8_t family) u_int8_t family)
{ {
@ -750,9 +751,9 @@ static int callforward_do_filter(const union nf_inet_addr *src,
memset(&fl2, 0, sizeof(fl2)); memset(&fl2, 0, sizeof(fl2));
fl2.daddr = dst->ip; fl2.daddr = dst->ip;
if (!afinfo->route(&init_net, (struct dst_entry **)&rt1, if (!afinfo->route(net, (struct dst_entry **)&rt1,
flowi4_to_flowi(&fl1), false)) { flowi4_to_flowi(&fl1), false)) {
if (!afinfo->route(&init_net, (struct dst_entry **)&rt2, if (!afinfo->route(net, (struct dst_entry **)&rt2,
flowi4_to_flowi(&fl2), false)) { flowi4_to_flowi(&fl2), false)) {
if (rt_nexthop(rt1, fl1.daddr) == if (rt_nexthop(rt1, fl1.daddr) ==
rt_nexthop(rt2, fl2.daddr) && rt_nexthop(rt2, fl2.daddr) &&
@ -774,9 +775,9 @@ static int callforward_do_filter(const union nf_inet_addr *src,
memset(&fl2, 0, sizeof(fl2)); memset(&fl2, 0, sizeof(fl2));
fl2.daddr = dst->in6; fl2.daddr = dst->in6;
if (!afinfo->route(&init_net, (struct dst_entry **)&rt1, if (!afinfo->route(net, (struct dst_entry **)&rt1,
flowi6_to_flowi(&fl1), false)) { flowi6_to_flowi(&fl1), false)) {
if (!afinfo->route(&init_net, (struct dst_entry **)&rt2, if (!afinfo->route(net, (struct dst_entry **)&rt2,
flowi6_to_flowi(&fl2), false)) { flowi6_to_flowi(&fl2), false)) {
if (ipv6_addr_equal(rt6_nexthop(rt1), if (ipv6_addr_equal(rt6_nexthop(rt1),
rt6_nexthop(rt2)) && rt6_nexthop(rt2)) &&
@ -807,6 +808,7 @@ static int expect_callforwarding(struct sk_buff *skb,
__be16 port; __be16 port;
union nf_inet_addr addr; union nf_inet_addr addr;
struct nf_conntrack_expect *exp; struct nf_conntrack_expect *exp;
struct net *net = nf_ct_net(ct);
typeof(nat_callforwarding_hook) nat_callforwarding; typeof(nat_callforwarding_hook) nat_callforwarding;
/* Read alternativeAddress */ /* Read alternativeAddress */
@ -816,7 +818,7 @@ static int expect_callforwarding(struct sk_buff *skb,
/* If the calling party is on the same side of the forward-to party, /* If the calling party is on the same side of the forward-to party,
* we don't need to track the second call */ * we don't need to track the second call */
if (callforward_filter && if (callforward_filter &&
callforward_do_filter(&addr, &ct->tuplehash[!dir].tuple.src.u3, callforward_do_filter(net, &addr, &ct->tuplehash[!dir].tuple.src.u3,
nf_ct_l3num(ct))) { nf_ct_l3num(ct))) {
pr_debug("nf_ct_q931: Call Forwarding not tracked\n"); pr_debug("nf_ct_q931: Call Forwarding not tracked\n");
return 0; return 0;

View file

@ -250,7 +250,7 @@ out:
} }
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper); EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
/* appropiate ct lock protecting must be taken by caller */ /* appropriate ct lock protecting must be taken by caller */
static inline int unhelp(struct nf_conntrack_tuple_hash *i, static inline int unhelp(struct nf_conntrack_tuple_hash *i,
const struct nf_conntrack_helper *me) const struct nf_conntrack_helper *me)
{ {

View file

@ -19,6 +19,9 @@
static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly; static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly;
static DEFINE_MUTEX(nf_log_mutex); static DEFINE_MUTEX(nf_log_mutex);
#define nft_log_dereference(logger) \
rcu_dereference_protected(logger, lockdep_is_held(&nf_log_mutex))
static struct nf_logger *__find_logger(int pf, const char *str_logger) static struct nf_logger *__find_logger(int pf, const char *str_logger)
{ {
struct nf_logger *log; struct nf_logger *log;
@ -28,8 +31,7 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)
if (loggers[pf][i] == NULL) if (loggers[pf][i] == NULL)
continue; continue;
log = rcu_dereference_protected(loggers[pf][i], log = nft_log_dereference(loggers[pf][i]);
lockdep_is_held(&nf_log_mutex));
if (!strncasecmp(str_logger, log->name, strlen(log->name))) if (!strncasecmp(str_logger, log->name, strlen(log->name)))
return log; return log;
} }
@ -45,8 +47,7 @@ void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
return; return;
mutex_lock(&nf_log_mutex); mutex_lock(&nf_log_mutex);
log = rcu_dereference_protected(net->nf.nf_loggers[pf], log = nft_log_dereference(net->nf.nf_loggers[pf]);
lockdep_is_held(&nf_log_mutex));
if (log == NULL) if (log == NULL)
rcu_assign_pointer(net->nf.nf_loggers[pf], logger); rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
@ -61,8 +62,7 @@ void nf_log_unset(struct net *net, const struct nf_logger *logger)
mutex_lock(&nf_log_mutex); mutex_lock(&nf_log_mutex);
for (i = 0; i < NFPROTO_NUMPROTO; i++) { for (i = 0; i < NFPROTO_NUMPROTO; i++) {
log = rcu_dereference_protected(net->nf.nf_loggers[i], log = nft_log_dereference(net->nf.nf_loggers[i]);
lockdep_is_held(&nf_log_mutex));
if (log == logger) if (log == logger)
RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL); RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
} }
@ -75,6 +75,7 @@ EXPORT_SYMBOL(nf_log_unset);
int nf_log_register(u_int8_t pf, struct nf_logger *logger) int nf_log_register(u_int8_t pf, struct nf_logger *logger)
{ {
int i; int i;
int ret = 0;
if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers)) if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers))
return -EINVAL; return -EINVAL;
@ -82,16 +83,25 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger)
mutex_lock(&nf_log_mutex); mutex_lock(&nf_log_mutex);
if (pf == NFPROTO_UNSPEC) { if (pf == NFPROTO_UNSPEC) {
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
if (rcu_access_pointer(loggers[i][logger->type])) {
ret = -EEXIST;
goto unlock;
}
}
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
rcu_assign_pointer(loggers[i][logger->type], logger); rcu_assign_pointer(loggers[i][logger->type], logger);
} else { } else {
/* register at end of list to honor first register win */ if (rcu_access_pointer(loggers[pf][logger->type])) {
ret = -EEXIST;
goto unlock;
}
rcu_assign_pointer(loggers[pf][logger->type], logger); rcu_assign_pointer(loggers[pf][logger->type], logger);
} }
unlock:
mutex_unlock(&nf_log_mutex); mutex_unlock(&nf_log_mutex);
return ret;
return 0;
} }
EXPORT_SYMBOL(nf_log_register); EXPORT_SYMBOL(nf_log_register);
@ -144,8 +154,7 @@ int nf_logger_find_get(int pf, enum nf_log_type type)
struct nf_logger *logger; struct nf_logger *logger;
int ret = -ENOENT; int ret = -ENOENT;
logger = loggers[pf][type]; if (rcu_access_pointer(loggers[pf][type]) == NULL)
if (logger == NULL)
request_module("nf-logger-%u-%u", pf, type); request_module("nf-logger-%u-%u", pf, type);
rcu_read_lock(); rcu_read_lock();
@ -297,8 +306,7 @@ static int seq_show(struct seq_file *s, void *v)
int i, ret; int i, ret;
struct net *net = seq_file_net(s); struct net *net = seq_file_net(s);
logger = rcu_dereference_protected(net->nf.nf_loggers[*pos], logger = nft_log_dereference(net->nf.nf_loggers[*pos]);
lockdep_is_held(&nf_log_mutex));
if (!logger) if (!logger)
ret = seq_printf(s, "%2lld NONE (", *pos); ret = seq_printf(s, "%2lld NONE (", *pos);
@ -312,8 +320,7 @@ static int seq_show(struct seq_file *s, void *v)
if (loggers[*pos][i] == NULL) if (loggers[*pos][i] == NULL)
continue; continue;
logger = rcu_dereference_protected(loggers[*pos][i], logger = nft_log_dereference(loggers[*pos][i]);
lockdep_is_held(&nf_log_mutex));
ret = seq_printf(s, "%s", logger->name); ret = seq_printf(s, "%s", logger->name);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -385,8 +392,7 @@ static int nf_log_proc_dostring(struct ctl_table *table, int write,
mutex_unlock(&nf_log_mutex); mutex_unlock(&nf_log_mutex);
} else { } else {
mutex_lock(&nf_log_mutex); mutex_lock(&nf_log_mutex);
logger = rcu_dereference_protected(net->nf.nf_loggers[tindex], logger = nft_log_dereference(net->nf.nf_loggers[tindex]);
lockdep_is_held(&nf_log_mutex));
if (!logger) if (!logger)
table->data = "NONE"; table->data = "NONE";
else else

View file

@ -2477,7 +2477,7 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
int err; int err;
/* Verify existance before starting dump */ /* Verify existence before starting dump */
err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla); err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
if (err < 0) if (err < 0)
return err; return err;
@ -3665,8 +3665,7 @@ static int nf_tables_abort(struct sk_buff *skb)
break; break;
case NFT_MSG_NEWCHAIN: case NFT_MSG_NEWCHAIN:
if (nft_trans_chain_update(trans)) { if (nft_trans_chain_update(trans)) {
if (nft_trans_chain_stats(trans)) free_percpu(nft_trans_chain_stats(trans));
free_percpu(nft_trans_chain_stats(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
} else { } else {

View file

@ -12,6 +12,9 @@
* 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.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
@ -337,9 +340,6 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
skb = nfnetlink_alloc_skb(net, pkt_size, skb = nfnetlink_alloc_skb(net, pkt_size,
peer_portid, GFP_ATOMIC); peer_portid, GFP_ATOMIC);
if (!skb)
pr_err("nfnetlink_log: can't even alloc %u bytes\n",
pkt_size);
} }
} }
@ -570,10 +570,8 @@ __build_packet_message(struct nfnl_log_net *log,
struct nlattr *nla; struct nlattr *nla;
int size = nla_attr_size(data_len); int size = nla_attr_size(data_len);
if (skb_tailroom(inst->skb) < nla_total_size(data_len)) { if (skb_tailroom(inst->skb) < nla_total_size(data_len))
printk(KERN_WARNING "nfnetlink_log: no tailroom!\n"); goto nla_put_failure;
return -1;
}
nla = (struct nlattr *)skb_put(inst->skb, nla_total_size(data_len)); nla = (struct nlattr *)skb_put(inst->skb, nla_total_size(data_len));
nla->nla_type = NFULA_PAYLOAD; nla->nla_type = NFULA_PAYLOAD;
@ -1069,19 +1067,19 @@ static int __init nfnetlink_log_init(void)
netlink_register_notifier(&nfulnl_rtnl_notifier); netlink_register_notifier(&nfulnl_rtnl_notifier);
status = nfnetlink_subsys_register(&nfulnl_subsys); status = nfnetlink_subsys_register(&nfulnl_subsys);
if (status < 0) { if (status < 0) {
pr_err("log: failed to create netlink socket\n"); pr_err("failed to create netlink socket\n");
goto cleanup_netlink_notifier; goto cleanup_netlink_notifier;
} }
status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger); status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger);
if (status < 0) { if (status < 0) {
pr_err("log: failed to register logger\n"); pr_err("failed to register logger\n");
goto cleanup_subsys; goto cleanup_subsys;
} }
status = register_pernet_subsys(&nfnl_log_net_ops); status = register_pernet_subsys(&nfnl_log_net_ops);
if (status < 0) { if (status < 0) {
pr_err("log: failed to register pernet ops\n"); pr_err("failed to register pernet ops\n");
goto cleanup_logger; goto cleanup_logger;
} }
return status; return status;

View file

@ -165,6 +165,12 @@ void nft_meta_get_eval(const struct nft_expr *expr,
goto err; goto err;
dest->data[0] = out->group; dest->data[0] = out->group;
break; break;
case NFT_META_CGROUP:
if (skb->sk == NULL)
break;
dest->data[0] = skb->sk->sk_classid;
break;
default: default:
WARN_ON(1); WARN_ON(1);
goto err; goto err;
@ -240,6 +246,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
case NFT_META_CPU: case NFT_META_CPU:
case NFT_META_IIFGROUP: case NFT_META_IIFGROUP:
case NFT_META_OIFGROUP: case NFT_META_OIFGROUP:
case NFT_META_CGROUP:
break; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;

99
net/netfilter/nft_redir.c Normal file
View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_redir.h>
const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = {
[NFTA_REDIR_REG_PROTO_MIN] = { .type = NLA_U32 },
[NFTA_REDIR_REG_PROTO_MAX] = { .type = NLA_U32 },
[NFTA_REDIR_FLAGS] = { .type = NLA_U32 },
};
EXPORT_SYMBOL_GPL(nft_redir_policy);
int nft_redir_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_redir *priv = nft_expr_priv(expr);
int err;
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
if (err < 0)
return err;
if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
priv->sreg_proto_min =
ntohl(nla_get_be32(tb[NFTA_REDIR_REG_PROTO_MIN]));
err = nft_validate_input_register(priv->sreg_proto_min);
if (err < 0)
return err;
if (tb[NFTA_REDIR_REG_PROTO_MAX]) {
priv->sreg_proto_max =
ntohl(nla_get_be32(tb[NFTA_REDIR_REG_PROTO_MAX]));
err = nft_validate_input_register(priv->sreg_proto_max);
if (err < 0)
return err;
} else {
priv->sreg_proto_max = priv->sreg_proto_min;
}
}
if (tb[NFTA_REDIR_FLAGS]) {
priv->flags = ntohl(nla_get_be32(tb[NFTA_REDIR_FLAGS]));
if (priv->flags & ~NF_NAT_RANGE_MASK)
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL_GPL(nft_redir_init);
int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_redir *priv = nft_expr_priv(expr);
if (priv->sreg_proto_min) {
if (nla_put_be32(skb, NFTA_REDIR_REG_PROTO_MIN,
htonl(priv->sreg_proto_min)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_REDIR_REG_PROTO_MAX,
htonl(priv->sreg_proto_max)))
goto nla_put_failure;
}
if (priv->flags != 0 &&
nla_put_be32(skb, NFTA_REDIR_FLAGS, htonl(priv->flags)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
EXPORT_SYMBOL_GPL(nft_redir_dump);
int nft_redir_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nft_data **data)
{
return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
}
EXPORT_SYMBOL_GPL(nft_redir_validate);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");

View file

@ -37,7 +37,8 @@ dscp_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (!skb_make_writable(skb, sizeof(struct iphdr))) if (!skb_make_writable(skb, sizeof(struct iphdr)))
return NF_DROP; return NF_DROP;
ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK), ipv4_change_dsfield(ip_hdr(skb),
(__force __u8)(~XT_DSCP_MASK),
dinfo->dscp << XT_DSCP_SHIFT); dinfo->dscp << XT_DSCP_SHIFT);
} }
@ -54,7 +55,8 @@ dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par)
if (!skb_make_writable(skb, sizeof(struct ipv6hdr))) if (!skb_make_writable(skb, sizeof(struct ipv6hdr)))
return NF_DROP; return NF_DROP;
ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK), ipv6_change_dsfield(ipv6_hdr(skb),
(__force __u8)(~XT_DSCP_MASK),
dinfo->dscp << XT_DSCP_SHIFT); dinfo->dscp << XT_DSCP_SHIFT);
} }
return XT_CONTINUE; return XT_CONTINUE;

View file

@ -26,48 +26,13 @@
#include <net/checksum.h> #include <net/checksum.h>
#include <net/protocol.h> #include <net/protocol.h>
#include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat.h>
#include <net/netfilter/ipv4/nf_nat_redirect.h>
static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; #include <net/netfilter/ipv6/nf_nat_redirect.h>
static unsigned int static unsigned int
redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par) redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par)
{ {
const struct nf_nat_range *range = par->targinfo; return nf_nat_redirect_ipv6(skb, par->targinfo, par->hooknum);
struct nf_nat_range newrange;
struct in6_addr newdst;
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (par->hooknum == NF_INET_LOCAL_OUT)
newdst = loopback_addr;
else {
struct inet6_dev *idev;
struct inet6_ifaddr *ifa;
bool addr = false;
rcu_read_lock();
idev = __in6_dev_get(skb->dev);
if (idev != NULL) {
list_for_each_entry(ifa, &idev->addr_list, if_list) {
newdst = ifa->addr;
addr = true;
break;
}
}
rcu_read_unlock();
if (!addr)
return NF_DROP;
}
newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
newrange.min_addr.in6 = newdst;
newrange.max_addr.in6 = newdst;
newrange.min_proto = range->min_proto;
newrange.max_proto = range->max_proto;
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
} }
static int redirect_tg6_checkentry(const struct xt_tgchk_param *par) static int redirect_tg6_checkentry(const struct xt_tgchk_param *par)
@ -98,48 +63,7 @@ static int redirect_tg4_check(const struct xt_tgchk_param *par)
static unsigned int static unsigned int
redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par) redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par)
{ {
struct nf_conn *ct; return nf_nat_redirect_ipv4(skb, par->targinfo, par->hooknum);
enum ip_conntrack_info ctinfo;
__be32 newdst;
const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
struct nf_nat_range newrange;
NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
par->hooknum == NF_INET_LOCAL_OUT);
ct = nf_ct_get(skb, &ctinfo);
NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
/* Local packets: make them go to loopback */
if (par->hooknum == NF_INET_LOCAL_OUT)
newdst = htonl(0x7F000001);
else {
struct in_device *indev;
struct in_ifaddr *ifa;
newdst = 0;
rcu_read_lock();
indev = __in_dev_get_rcu(skb->dev);
if (indev && (ifa = indev->ifa_list))
newdst = ifa->ifa_local;
rcu_read_unlock();
if (!newdst)
return NF_DROP;
}
/* Transfer from original range. */
memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
newrange.min_addr.ip = newdst;
newrange.max_addr.ip = newdst;
newrange.min_proto = mr->range[0].min;
newrange.max_proto = mr->range[0].max;
/* Hand modified range to generic setup. */
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
} }
static struct xt_target redirect_tg_reg[] __read_mostly = { static struct xt_target redirect_tg_reg[] __read_mostly = {

View file

@ -134,6 +134,7 @@ static bool add_hlist(struct hlist_head *head,
static unsigned int check_hlist(struct net *net, static unsigned int check_hlist(struct net *net,
struct hlist_head *head, struct hlist_head *head,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
u16 zone,
bool *addit) bool *addit)
{ {
const struct nf_conntrack_tuple_hash *found; const struct nf_conntrack_tuple_hash *found;
@ -147,8 +148,7 @@ static unsigned int check_hlist(struct net *net,
/* check the saved connections */ /* check the saved connections */
hlist_for_each_entry_safe(conn, n, head, node) { hlist_for_each_entry_safe(conn, n, head, node) {
found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE, found = nf_conntrack_find_get(net, zone, &conn->tuple);
&conn->tuple);
if (found == NULL) { if (found == NULL) {
hlist_del(&conn->node); hlist_del(&conn->node);
kmem_cache_free(connlimit_conn_cachep, conn); kmem_cache_free(connlimit_conn_cachep, conn);
@ -201,7 +201,7 @@ static unsigned int
count_tree(struct net *net, struct rb_root *root, count_tree(struct net *net, struct rb_root *root,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr, const union nf_inet_addr *mask, const union nf_inet_addr *addr, const union nf_inet_addr *mask,
u8 family) u8 family, u16 zone)
{ {
struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES]; struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES];
struct rb_node **rbnode, *parent; struct rb_node **rbnode, *parent;
@ -229,7 +229,7 @@ count_tree(struct net *net, struct rb_root *root,
} else { } else {
/* same source network -> be counted! */ /* same source network -> be counted! */
unsigned int count; unsigned int count;
count = check_hlist(net, &rbconn->hhead, tuple, &addit); count = check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
tree_nodes_free(root, gc_nodes, gc_count); tree_nodes_free(root, gc_nodes, gc_count);
if (!addit) if (!addit)
@ -245,7 +245,7 @@ count_tree(struct net *net, struct rb_root *root,
continue; continue;
/* only used for GC on hhead, retval and 'addit' ignored */ /* only used for GC on hhead, retval and 'addit' ignored */
check_hlist(net, &rbconn->hhead, tuple, &addit); check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
if (hlist_empty(&rbconn->hhead)) if (hlist_empty(&rbconn->hhead))
gc_nodes[gc_count++] = rbconn; gc_nodes[gc_count++] = rbconn;
} }
@ -290,7 +290,7 @@ static int count_them(struct net *net,
const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr, const union nf_inet_addr *addr,
const union nf_inet_addr *mask, const union nf_inet_addr *mask,
u_int8_t family) u_int8_t family, u16 zone)
{ {
struct rb_root *root; struct rb_root *root;
int count; int count;
@ -306,7 +306,7 @@ static int count_them(struct net *net,
spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
count = count_tree(net, root, tuple, addr, mask, family); count = count_tree(net, root, tuple, addr, mask, family, zone);
spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
@ -324,13 +324,16 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
const struct nf_conn *ct; const struct nf_conn *ct;
unsigned int connections; unsigned int connections;
u16 zone = NF_CT_DEFAULT_ZONE;
ct = nf_ct_get(skb, &ctinfo); ct = nf_ct_get(skb, &ctinfo);
if (ct != NULL) if (ct != NULL) {
tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb), zone = nf_ct_zone(ct);
par->family, &tuple)) } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
par->family, &tuple)) {
goto hotdrop; goto hotdrop;
}
if (par->family == NFPROTO_IPV6) { if (par->family == NFPROTO_IPV6) {
const struct ipv6hdr *iph = ipv6_hdr(skb); const struct ipv6hdr *iph = ipv6_hdr(skb);
@ -343,7 +346,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
} }
connections = count_them(net, info->data, tuple_ptr, &addr, connections = count_them(net, info->data, tuple_ptr, &addr,
&info->mask, par->family); &info->mask, par->family, zone);
if (connections == 0) if (connections == 0)
/* kmalloc failed, drop it entirely */ /* kmalloc failed, drop it entirely */
goto hotdrop; goto hotdrop;