Commit 751fcac1 authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nftables

Pablo Neira Ayuso says:

====================
nf_tables updates for net-next

The following patchset contains the following nf_tables updates,
mostly updates from Patrick McHardy, they are:

* Add the "inet" table and filter chain type for this new netfilter
  family: NFPROTO_INET. This special table/chain allows IPv4 and IPv6
  rules, this should help to simplify the burden in the administration
  of dual stack firewalls. This also includes several patches to prepare
  the infrastructure for this new table and a new meta extension to
  match the layer 3 and 4 protocol numbers, from Patrick McHardy.

* Load both IPv4 and IPv6 conntrack modules in nft_ct if the rule is used
  in NFPROTO_INET, as we don't certainly know which one would be used,
  also from Patrick McHardy.

* Do not allow to delete a table that contains sets, otherwise these
  sets become orphan, from Patrick McHardy.

* Hold a reference to the corresponding nf_tables family module when
  creating a table of that family type, to avoid the module deletion
  when in use, from Patrick McHardy.

* Update chain counters before setting the chain policy to ensure that
  we don't leave the chain in inconsistent state in case of errors (aka.
  restore chain atomicity). This also fixes a possible leak if it fails
  to allocate the chain counters if no counters are passed to be restored,
  from Patrick McHardy.

* Don't check for overflows in the table counter if we are just renaming
  a chain, from Patrick McHardy.

* Replay the netlink request after dropping the nfnl lock to load the
  module that supports provides a chain type, from Patrick.

* Fix chain type module references, from Patrick.

* Several cleanups, function renames, constification and code
  refactorizations also from Patrick McHardy.

* Add support to set the connmark, this can be used to set it based on
  the meta mark (similar feature to -j CONNMARK --restore), from
  Kristian Evensen.

* A couple of fixes to the recently added meta/set support and nft_reject,
  and fix missing chain type unregistration if we fail to register our
  the family table/filter chain type, from myself.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents bfec4c3b cf4dfa85
......@@ -13,9 +13,10 @@ struct nft_pktinfo {
struct sk_buff *skb;
const struct net_device *in;
const struct net_device *out;
u8 hooknum;
const struct nf_hook_ops *ops;
u8 nhoff;
u8 thoff;
u8 tprot;
/* for x_tables compatibility */
struct xt_action_param xt;
};
......@@ -29,7 +30,8 @@ static inline void nft_set_pktinfo(struct nft_pktinfo *pkt,
pkt->skb = skb;
pkt->in = pkt->xt.in = in;
pkt->out = pkt->xt.out = out;
pkt->hooknum = pkt->xt.hooknum = ops->hooknum;
pkt->ops = ops;
pkt->xt.hooknum = ops->hooknum;
pkt->xt.family = ops->pf;
}
......@@ -421,6 +423,8 @@ struct nft_stats {
u64 pkts;
};
#define NFT_HOOK_OPS_MAX 2
/**
* struct nft_base_chain - nf_tables base chain
*
......@@ -431,8 +435,8 @@ struct nft_stats {
* @chain: the chain
*/
struct nft_base_chain {
struct nf_hook_ops ops;
enum nft_chain_type type;
struct nf_hook_ops ops[NFT_HOOK_OPS_MAX];
const struct nf_chain_type *type;
u8 policy;
struct nft_stats __percpu *stats;
struct nft_chain chain;
......@@ -443,7 +447,7 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai
return container_of(chain, struct nft_base_chain, chain);
}
unsigned int nft_do_chain_pktinfo(struct nft_pktinfo *pkt,
unsigned int nft_do_chain(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops);
/**
......@@ -475,6 +479,8 @@ struct nft_table {
* @nhooks: number of hooks in this family
* @owner: module owner
* @tables: used internally
* @nops: number of hook ops in this family
* @hook_ops_init: initialization function for chain hook ops
* @hooks: hookfn overrides for packet validation
*/
struct nft_af_info {
......@@ -483,23 +489,36 @@ struct nft_af_info {
unsigned int nhooks;
struct module *owner;
struct list_head tables;
unsigned int nops;
void (*hook_ops_init)(struct nf_hook_ops *,
unsigned int);
nf_hookfn *hooks[NF_MAX_HOOKS];
};
int nft_register_afinfo(struct net *, struct nft_af_info *);
void nft_unregister_afinfo(struct nft_af_info *);
/**
* struct nf_chain_type - nf_tables chain type info
*
* @name: name of the type
* @type: numeric identifier
* @family: address family
* @owner: module owner
* @hook_mask: mask of valid hooks
* @hooks: hookfn overrides
*/
struct nf_chain_type {
unsigned int hook_mask;
const char *name;
enum nft_chain_type type;
nf_hookfn *fn[NF_MAX_HOOKS];
struct module *me;
int family;
struct module *owner;
unsigned int hook_mask;
nf_hookfn *hooks[NF_MAX_HOOKS];
};
int nft_register_chain_type(struct nf_chain_type *);
void nft_unregister_chain_type(struct nf_chain_type *);
int nft_register_chain_type(const struct nf_chain_type *);
void nft_unregister_chain_type(const struct nf_chain_type *);
int nft_register_expr(struct nft_expr_type *);
void nft_unregister_expr(struct nft_expr_type *);
......
......@@ -15,9 +15,12 @@ nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
nft_set_pktinfo(pkt, ops, skb, in, out);
pkt->xt.thoff = ip_hdrlen(pkt->skb);
ip = ip_hdr(pkt->skb);
pkt->tprot = ip->protocol;
pkt->xt.thoff = ip_hdrlen(pkt->skb);
pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
}
extern struct nft_af_info nft_af_ipv4;
#endif
......@@ -21,10 +21,13 @@ nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
if (protohdr < 0)
return -1;
pkt->tprot = protohdr;
pkt->xt.thoff = thoff;
pkt->xt.fragoff = frag_off;
return 0;
}
extern struct nft_af_info nft_af_ipv6;
#endif
......@@ -10,6 +10,7 @@ struct netns_nftables {
struct list_head commit_list;
struct nft_af_info *ipv4;
struct nft_af_info *ipv6;
struct nft_af_info *inet;
struct nft_af_info *arp;
struct nft_af_info *bridge;
u8 gencursor;
......
......@@ -53,6 +53,7 @@ enum nf_inet_hooks {
enum {
NFPROTO_UNSPEC = 0,
NFPROTO_INET = 1,
NFPROTO_IPV4 = 2,
NFPROTO_ARP = 3,
NFPROTO_BRIDGE = 7,
......
......@@ -531,6 +531,8 @@ enum nft_exthdr_attributes {
* @NFT_META_NFTRACE: packet nftrace bit
* @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid)
* @NFT_META_SECMARK: packet secmark (skb->secmark)
* @NFT_META_NFPROTO: netfilter protocol
* @NFT_META_L4PROTO: layer 4 protocol number
*/
enum nft_meta_keys {
NFT_META_LEN,
......@@ -548,6 +550,8 @@ enum nft_meta_keys {
NFT_META_NFTRACE,
NFT_META_RTCLASSID,
NFT_META_SECMARK,
NFT_META_NFPROTO,
NFT_META_L4PROTO,
};
/**
......@@ -605,12 +609,14 @@ enum nft_ct_keys {
* @NFTA_CT_DREG: destination register (NLA_U32)
* @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys)
* @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8)
* @NFTA_CT_SREG: source register (NLA_U32)
*/
enum nft_ct_attributes {
NFTA_CT_UNSPEC,
NFTA_CT_DREG,
NFTA_CT_KEY,
NFTA_CT_DIRECTION,
NFTA_CT_SREG,
__NFTA_CT_MAX
};
#define NFTA_CT_MAX (__NFTA_CT_MAX - 1)
......
......@@ -14,10 +14,30 @@
#include <linux/netfilter_bridge.h>
#include <net/netfilter/nf_tables.h>
static unsigned int
nft_do_chain_bridge(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
nft_set_pktinfo(&pkt, ops, skb, in, out);
return nft_do_chain(&pkt, ops);
}
static struct nft_af_info nft_af_bridge __read_mostly = {
.family = NFPROTO_BRIDGE,
.nhooks = NF_BR_NUMHOOKS,
.owner = THIS_MODULE,
.nops = 1,
.hooks = {
[NF_BR_LOCAL_IN] = nft_do_chain_bridge,
[NF_BR_FORWARD] = nft_do_chain_bridge,
[NF_BR_LOCAL_OUT] = nft_do_chain_bridge,
},
};
static int nf_tables_bridge_init_net(struct net *net)
......@@ -48,32 +68,14 @@ static struct pernet_operations nf_tables_bridge_net_ops = {
.exit = nf_tables_bridge_exit_net,
};
static unsigned int
nft_do_chain_bridge(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
nft_set_pktinfo(&pkt, ops, skb, in, out);
return nft_do_chain_pktinfo(&pkt, ops);
}
static struct nf_chain_type filter_bridge = {
.family = NFPROTO_BRIDGE,
static const struct nf_chain_type filter_bridge = {
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.family = NFPROTO_BRIDGE,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_BR_LOCAL_IN) |
(1 << NF_BR_FORWARD) |
(1 << NF_BR_LOCAL_OUT),
.fn = {
[NF_BR_LOCAL_IN] = nft_do_chain_bridge,
[NF_BR_FORWARD] = nft_do_chain_bridge,
[NF_BR_LOCAL_OUT] = nft_do_chain_bridge,
},
};
static int __init nf_tables_bridge_init(void)
......
......@@ -14,10 +14,30 @@
#include <linux/netfilter_arp.h>
#include <net/netfilter/nf_tables.h>
static unsigned int
nft_do_chain_arp(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
nft_set_pktinfo(&pkt, ops, skb, in, out);
return nft_do_chain(&pkt, ops);
}
static struct nft_af_info nft_af_arp __read_mostly = {
.family = NFPROTO_ARP,
.nhooks = NF_ARP_NUMHOOKS,
.owner = THIS_MODULE,
.nops = 1,
.hooks = {
[NF_ARP_IN] = nft_do_chain_arp,
[NF_ARP_OUT] = nft_do_chain_arp,
[NF_ARP_FORWARD] = nft_do_chain_arp,
},
};
static int nf_tables_arp_init_net(struct net *net)
......@@ -48,32 +68,14 @@ static struct pernet_operations nf_tables_arp_net_ops = {
.exit = nf_tables_arp_exit_net,
};
static unsigned int
nft_do_chain_arp(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
nft_set_pktinfo(&pkt, ops, skb, in, out);
return nft_do_chain_pktinfo(&pkt, ops);
}
static struct nf_chain_type filter_arp = {
.family = NFPROTO_ARP,
static const struct nf_chain_type filter_arp = {
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.family = NFPROTO_ARP,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_ARP_IN) |
(1 << NF_ARP_OUT) |
(1 << NF_ARP_FORWARD),
.fn = {
[NF_ARP_IN] = nft_do_chain_arp,
[NF_ARP_OUT] = nft_do_chain_arp,
[NF_ARP_FORWARD] = nft_do_chain_arp,
},
};
static int __init nf_tables_arp_init(void)
......
......@@ -18,7 +18,7 @@
#include <net/ip.h>
#include <net/netfilter/nf_tables_ipv4.h>
static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
static unsigned int nft_do_chain_ipv4(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -26,6 +26,17 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
{
struct nft_pktinfo pkt;
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
return nft_do_chain(&pkt, ops);
}
static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if (unlikely(skb->len < sizeof(struct iphdr) ||
ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) {
if (net_ratelimit())
......@@ -33,19 +44,24 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops,
"packet\n");
return NF_ACCEPT;
}
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
return nft_do_chain_pktinfo(&pkt, ops);
return nft_do_chain_ipv4(ops, skb, in, out, okfn);
}
static struct nft_af_info nft_af_ipv4 __read_mostly = {
struct nft_af_info nft_af_ipv4 __read_mostly = {
.family = NFPROTO_IPV4,
.nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE,
.nops = 1,
.hooks = {
[NF_INET_LOCAL_IN] = nft_do_chain_ipv4,
[NF_INET_LOCAL_OUT] = nft_ipv4_output,
[NF_INET_FORWARD] = nft_do_chain_ipv4,
[NF_INET_PRE_ROUTING] = nft_do_chain_ipv4,
[NF_INET_POST_ROUTING] = nft_do_chain_ipv4,
},
};
EXPORT_SYMBOL_GPL(nft_af_ipv4);
static int nf_tables_ipv4_init_net(struct net *net)
{
......@@ -75,42 +91,28 @@ static struct pernet_operations nf_tables_ipv4_net_ops = {
.exit = nf_tables_ipv4_exit_net,
};
static unsigned int
nft_do_chain_ipv4(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
return nft_do_chain_pktinfo(&pkt, ops);
}
static struct nf_chain_type filter_ipv4 = {
.family = NFPROTO_IPV4,
static const struct nf_chain_type filter_ipv4 = {
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.family = NFPROTO_IPV4,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_FORWARD) |
(1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING),
.fn = {
[NF_INET_LOCAL_IN] = nft_do_chain_ipv4,
[NF_INET_LOCAL_OUT] = nft_ipv4_output,
[NF_INET_FORWARD] = nft_do_chain_ipv4,
[NF_INET_PRE_ROUTING] = nft_do_chain_ipv4,
[NF_INET_POST_ROUTING] = nft_do_chain_ipv4,
},
};
static int __init nf_tables_ipv4_init(void)
{
int ret;
nft_register_chain_type(&filter_ipv4);
return register_pernet_subsys(&nf_tables_ipv4_net_ops);
ret = register_pernet_subsys(&nf_tables_ipv4_net_ops);
if (ret < 0)
nft_unregister_chain_type(&filter_ipv4);
return ret;
}
static void __exit nf_tables_ipv4_exit(void)
......
......@@ -75,7 +75,7 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops,
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
ret = nft_do_chain_pktinfo(&pkt, ops);
ret = nft_do_chain(&pkt, ops);
if (ret != NF_ACCEPT)
return ret;
if (!nf_nat_initialized(ct, maniptype)) {
......@@ -164,21 +164,21 @@ static unsigned int nf_nat_output(const struct nf_hook_ops *ops,
return ret;
}
static struct nf_chain_type nft_chain_nat_ipv4 = {
.family = NFPROTO_IPV4,
static const struct nf_chain_type nft_chain_nat_ipv4 = {
.name = "nat",
.type = NFT_CHAIN_T_NAT,
.family = NFPROTO_IPV4,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_LOCAL_IN),
.fn = {
.hooks = {
[NF_INET_PRE_ROUTING] = nf_nat_prerouting,
[NF_INET_POST_ROUTING] = nf_nat_postrouting,
[NF_INET_LOCAL_OUT] = nf_nat_output,
[NF_INET_LOCAL_IN] = nf_nat_fn,
},
.me = THIS_MODULE,
};
static int __init nft_chain_nat_init(void)
......
......@@ -47,7 +47,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
daddr = iph->daddr;
tos = iph->tos;
ret = nft_do_chain_pktinfo(&pkt, ops);
ret = nft_do_chain(&pkt, ops);
if (ret != NF_DROP && ret != NF_QUEUE) {
iph = ip_hdr(skb);
......@@ -61,15 +61,15 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
return ret;
}
static struct nf_chain_type nft_chain_route_ipv4 = {
.family = NFPROTO_IPV4,
static const struct nf_chain_type nft_chain_route_ipv4 = {
.name = "route",
.type = NFT_CHAIN_T_ROUTE,
.family = NFPROTO_IPV4,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_LOCAL_OUT),
.fn = {
.hooks = {
[NF_INET_LOCAL_OUT] = nf_route_table_hook,
},
.me = THIS_MODULE,
};
static int __init nft_chain_route_init(void)
......
......@@ -16,7 +16,7 @@
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_ipv6.h>
static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
static unsigned int nft_do_chain_ipv6(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -24,26 +24,43 @@ static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
{
struct nft_pktinfo pkt;
/* malformed packet, drop it */
if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
return NF_DROP;
return nft_do_chain(&pkt, ops);
}
static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if (unlikely(skb->len < sizeof(struct ipv6hdr))) {
if (net_ratelimit())
pr_info("nf_tables_ipv6: ignoring short SOCK_RAW "
"packet\n");
return NF_ACCEPT;
}
if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
return NF_DROP;
return nft_do_chain_pktinfo(&pkt, ops);
return nft_do_chain_ipv6(ops, skb, in, out, okfn);
}
static struct nft_af_info nft_af_ipv6 __read_mostly = {
struct nft_af_info nft_af_ipv6 __read_mostly = {
.family = NFPROTO_IPV6,
.nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE,
.nops = 1,
.hooks = {
[NF_INET_LOCAL_IN] = nft_do_chain_ipv6,
[NF_INET_LOCAL_OUT] = nft_ipv6_output,
[NF_INET_FORWARD] = nft_do_chain_ipv6,
[NF_INET_PRE_ROUTING] = nft_do_chain_ipv6,
[NF_INET_POST_ROUTING] = nft_do_chain_ipv6,
},
};
EXPORT_SYMBOL_GPL(nft_af_ipv6);
static int nf_tables_ipv6_init_net(struct net *net)
{
......@@ -73,44 +90,28 @@ static struct pernet_operations nf_tables_ipv6_net_ops = {
.exit = nf_tables_ipv6_exit_net,
};
static unsigned int
nft_do_chain_ipv6(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
/* malformed packet, drop it */
if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
return NF_DROP;
return nft_do_chain_pktinfo(&pkt, ops);
}
static struct nf_chain_type filter_ipv6 = {
.family = NFPROTO_IPV6,
static const struct nf_chain_type filter_ipv6 = {
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.family = NFPROTO_IPV6,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_FORWARD) |
(1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING),
.fn = {
[NF_INET_LOCAL_IN] = nft_do_chain_ipv6,
[NF_INET_LOCAL_OUT] = nft_ipv6_output,
[NF_INET_FORWARD] = nft_do_chain_ipv6,
[NF_INET_PRE_ROUTING] = nft_do_chain_ipv6,
[NF_INET_POST_ROUTING] = nft_do_chain_ipv6,
},
};
static int __init nf_tables_ipv6_init(void)
{
int ret;
nft_register_chain_type(&filter_ipv6);
return register_pernet_subsys(&nf_tables_ipv6_net_ops);
ret = register_pernet_subsys(&nf_tables_ipv6_net_ops);
if (ret < 0)
nft_unregister_chain_type(&filter_ipv6);
return ret;
}
static void __exit nf_tables_ipv6_exit(void)
......
......@@ -79,7 +79,7 @@ static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
ret = nft_do_chain_pktinfo(&pkt, ops);
ret = nft_do_chain(&pkt, ops);
if (ret != NF_ACCEPT)
return ret;
if (!nf_nat_initialized(ct, maniptype)) {
......@@ -170,21 +170,21 @@ static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops,
return ret;
}
static struct nf_chain_type nft_chain_nat_ipv6 = {
.family = NFPROTO_IPV6,
static const struct nf_chain_type nft_chain_nat_ipv6 = {
.name = "nat",
.type = NFT_CHAIN_T_NAT,
.family = NFPROTO_IPV6,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_LOCAL_IN),
.fn = {
.hooks = {
[NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting,
[NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting,
[NF_INET_LOCAL_OUT] = nf_nat_ipv6_output,
[NF_INET_LOCAL_IN] = nf_nat_ipv6_fn,
},
.me = THIS_MODULE,
};
static int __init nft_chain_nat_ipv6_init(void)
......
......@@ -47,7 +47,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
/* flowlabel and prio (includes version, which shouldn't change either */
flowlabel = *((u32 *)ipv6_hdr(skb));
ret = nft_do_chain_pktinfo(&pkt, ops);
ret = nft_do_chain(&pkt, ops);
if (ret != NF_DROP && ret != NF_QUEUE &&
(memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) ||
memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) ||
......@@ -59,15 +59,15 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops,
return ret;
}
static struct nf_chain_type nft_chain_route_ipv6 = {
.family = NFPROTO_IPV6,
static const struct nf_chain_type nft_chain_route_ipv6 = {
.name = "route",
.type = NFT_CHAIN_T_ROUTE,
.family = NFPROTO_IPV6,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_LOCAL_OUT),
.fn = {
.hooks = {
[NF_INET_LOCAL_OUT] = nf_route_table_hook,
},
.me = THIS_MODULE,
};
static int __init nft_chain_route_init(void)
......
......@@ -428,6 +428,14 @@ config NF_TABLES
To compile it as a module, choose M here.
config NF_TABLES_INET
depends on NF_TABLES
select NF_TABLES_IPV4
select NF_TABLES_IPV6
tristate "Netfilter nf_tables mixed IPv4/IPv6 tables support"
help
This option enables support for a mixed IPv4/IPv6 "inet" table.
config NFT_EXTHDR
depends on NF_TABLES
tristate "Netfilter nf_tables IPv6 exthdr module"
......
......@@ -70,6 +70,7 @@ nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o
nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o
obj-$(CONFIG_NF_TABLES) += nf_tables.o
obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o
obj-$(CONFIG_NFT_COMPAT) += nft_compat.o
obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o
obj-$(CONFIG_NFT_META) += nft_meta.o
......
......@@ -124,37 +124,43 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table)
return ++table->hgenerator;
}
static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
static const struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX];
static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
static const struct nf_chain_type *
__nf_tables_chain_type_lookup(int family, const struct nlattr *nla)
{
int i;
for (i=0; i<NFT_CHAIN_T_MAX; i++) {
for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
if (chain_type[family][i] != NULL &&
!nla_strcmp(nla, chain_type[family][i]->name))
return i;
return chain_type[family][i];
}
return -1;
return NULL;
}
static int nf_tables_chain_type_lookup(const struct nft_af_info *afi,
static const struct nf_chain_type *
nf_tables_chain_type_lookup(const struct nft_af_info *afi,
const struct nlattr *nla,
bool autoload)
{
int type;
const struct nf_chain_type *type;
type = __nf_tables_chain_type_lookup(afi->family, nla);
if (type != NULL)
return type;
#ifdef CONFIG_MODULES
if (type < 0 && autoload) {
if (autoload) {
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
request_module("nft-chain-%u-%*.s", afi->family,
nla_len(nla)-1, (const char *)nla_data(nla));
nfnl_lock(NFNL_SUBSYS_NFTABLES);
type = __nf_tables_chain_type_lookup(afi->family, nla);
if (type != NULL)
return ERR_PTR(-EAGAIN);
}
#endif
return type;
return ERR_PTR(-ENOENT);
}
static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
......@@ -307,7 +313,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
return err;
}
static int nf_tables_table_enable(struct nft_table *table)
static int nf_tables_table_enable(const struct nft_af_info *afi,
struct nft_table *table)
{
struct nft_chain *chain;
int err, i = 0;
......@@ -316,7 +323,7 @@ static int nf_tables_table_enable(struct nft_table *table)
if (!(chain->flags & NFT_BASE_CHAIN))
continue;
err = nf_register_hook(&nft_base_chain(chain)->ops);
err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
if (err < 0)
goto err;
......@@ -331,18 +338,20 @@ static int nf_tables_table_enable(struct nft_table *table)
if (i-- <= 0)
break;
nf_unregister_hook(&nft_base_chain(chain)->ops);
nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
}
return err;
}
static int nf_tables_table_disable(struct nft_table *table)
static int nf_tables_table_disable(const struct nft_af_info *afi,
struct nft_table *table)
{
struct nft_chain *chain;
list_for_each_entry(chain, &table->chains, list) {
if (chain->flags & NFT_BASE_CHAIN)
nf_unregister_hook(&nft_base_chain(chain)->ops);
nf_unregister_hooks(nft_base_chain(chain)->ops,
afi->nops);
}
return 0;
......@@ -357,7 +366,7 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
int family = nfmsg->nfgen_family, ret = 0;
if (nla[NFTA_TABLE_FLAGS]) {
__be32 flags;
u32 flags;
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
if (flags & ~NFT_TABLE_F_DORMANT)
......@@ -365,12 +374,12 @@ static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb,
if ((flags & NFT_TABLE_F_DORMANT) &&
!(table->flags & NFT_TABLE_F_DORMANT)) {
ret = nf_tables_table_disable(table);
ret = nf_tables_table_disable(afi, table);
if (ret >= 0)
table->flags |= NFT_TABLE_F_DORMANT;
} else if (!(flags & NFT_TABLE_F_DORMANT) &&
table->flags & NFT_TABLE_F_DORMANT) {
ret = nf_tables_table_enable(table);
ret = nf_tables_table_enable(afi, table);
if (ret >= 0)
table->flags &= ~NFT_TABLE_F_DORMANT;
}
......@@ -393,6 +402,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
struct nft_table *table;
struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
u32 flags = 0;
afi = nf_tables_afinfo_lookup(net, family, true);
if (IS_ERR(afi))
......@@ -414,26 +424,26 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
}
table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
if (table == NULL)
return -ENOMEM;
nla_strlcpy(table->name, name, nla_len(name));
INIT_LIST_HEAD(&table->chains);
INIT_LIST_HEAD(&table->sets);
if (nla[NFTA_TABLE_FLAGS]) {
__be32 flags;
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS]));
if (flags & ~NFT_TABLE_F_DORMANT) {
kfree(table);
if (flags & ~NFT_TABLE_F_DORMANT)
return -EINVAL;
}
table->flags |= flags;
if (!try_module_get(afi->owner))
return -EAFNOSUPPORT;
table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL);
if (table == NULL) {
module_put(afi->owner);
return -ENOMEM;
}
nla_strlcpy(table->name, name, nla_len(name));
INIT_LIST_HEAD(&table->chains);
INIT_LIST_HEAD(&table->sets);
table->flags = flags;
list_add_tail(&table->list, &afi->tables);
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
return 0;
......@@ -457,16 +467,17 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(table))
return PTR_ERR(table);
if (table->use)
if (!list_empty(&table->chains) || !list_empty(&table->sets))
return -EBUSY;
list_del(&table->list);
nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
kfree(table);
module_put(afi->owner);
return 0;
}
int nft_register_chain_type(struct nf_chain_type *ctype)
int nft_register_chain_type(const struct nf_chain_type *ctype)
{
int err = 0;
......@@ -475,10 +486,6 @@ int nft_register_chain_type(struct nf_chain_type *ctype)
err = -EBUSY;
goto out;
}
if (!try_module_get(ctype->me))
goto out;
chain_type[ctype->family][ctype->type] = ctype;
out:
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
......@@ -486,11 +493,10 @@ int nft_register_chain_type(struct nf_chain_type *ctype)
}
EXPORT_SYMBOL_GPL(nft_register_chain_type);
void nft_unregister_chain_type(struct nf_chain_type *ctype)
void nft_unregister_chain_type(const struct nf_chain_type *ctype)
{
nfnl_lock(NFNL_SUBSYS_NFTABLES);
chain_type[ctype->family][ctype->type] = NULL;
module_put(ctype->me);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
}
EXPORT_SYMBOL_GPL(nft_unregister_chain_type);
......@@ -598,7 +604,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
if (chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain = nft_base_chain(chain);
const struct nf_hook_ops *ops = &basechain->ops;
const struct nf_hook_ops *ops = &basechain->ops[0];
struct nlattr *nest;
nest = nla_nest_start(skb, NFTA_CHAIN_HOOK);
......@@ -614,8 +620,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
htonl(basechain->policy)))
goto nla_put_failure;
if (nla_put_string(skb, NFTA_CHAIN_TYPE,
chain_type[ops->pf][nft_base_chain(chain)->type]->name))
if (nla_put_string(skb, NFTA_CHAIN_TYPE, basechain->type->name))
goto nla_put_failure;
if (nft_dump_stats(skb, nft_base_chain(chain)->stats))
......@@ -757,22 +762,6 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
return err;
}
static int
nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr)
{
switch (ntohl(nla_get_be32(attr))) {
case NF_DROP:
chain->policy = NF_DROP;
break;
case NF_ACCEPT:
chain->policy = NF_ACCEPT;
break;
default:
return -EINVAL;
}
return 0;
}
static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
......@@ -831,7 +820,9 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
struct nlattr *ha[NFTA_HOOK_MAX + 1];
struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
u8 policy = NF_ACCEPT;
u64 handle = 0;
unsigned int i;
int err;
bool create;
......@@ -845,9 +836,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(table))
return PTR_ERR(table);
if (table->use == UINT_MAX)
return -EOVERFLOW;
chain = NULL;
name = nla[NFTA_CHAIN_NAME];
......@@ -865,6 +853,22 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
}
}
if (nla[NFTA_CHAIN_POLICY]) {
if ((chain != NULL &&
!(chain->flags & NFT_BASE_CHAIN)) ||
nla[NFTA_CHAIN_HOOK] == NULL)
return -EOPNOTSUPP;
policy = nla_get_be32(nla[NFTA_CHAIN_POLICY]);
switch (policy) {
case NF_DROP:
case NF_ACCEPT:
break;
default:
return -EINVAL;
}
}
if (chain != NULL) {
if (nlh->nlmsg_flags & NLM_F_EXCL)
return -EEXIST;
......@@ -875,16 +879,6 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
!IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME])))
return -EEXIST;
if (nla[NFTA_CHAIN_POLICY]) {
if (!(chain->flags & NFT_BASE_CHAIN))
return -EOPNOTSUPP;
err = nf_tables_chain_policy(nft_base_chain(chain),
nla[NFTA_CHAIN_POLICY]);
if (err < 0)
return err;
}
if (nla[NFTA_CHAIN_COUNTERS]) {
if (!(chain->flags & NFT_BASE_CHAIN))
return -EOPNOTSUPP;
......@@ -895,24 +889,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
return err;
}
if (nla[NFTA_CHAIN_POLICY])
nft_base_chain(chain)->policy = policy;
if (nla[NFTA_CHAIN_HANDLE] && name)
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN);
goto notify;
}
if (table->use == UINT_MAX)
return -EOVERFLOW;
if (nla[NFTA_CHAIN_HOOK]) {
const struct nf_chain_type *type;
struct nf_hook_ops *ops;
nf_hookfn *hookfn;
u32 hooknum;
int type = NFT_CHAIN_T_DEFAULT;
u32 hooknum, priority;
type = chain_type[family][NFT_CHAIN_T_DEFAULT];
if (nla[NFTA_CHAIN_TYPE]) {
type = nf_tables_chain_type_lookup(afi,
nla[NFTA_CHAIN_TYPE],
create);
if (type < 0)
return -ENOENT;
if (IS_ERR(type))
return PTR_ERR(type);
}
err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK],
......@@ -926,46 +927,23 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
if (hooknum >= afi->nhooks)
return -EINVAL;
priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
hookfn = chain_type[family][type]->fn[hooknum];
if (hookfn == NULL)
if (!(type->hook_mask & (1 << hooknum)))
return -EOPNOTSUPP;
if (!try_module_get(type->owner))
return -ENOENT;
hookfn = type->hooks[hooknum];
basechain = kzalloc(sizeof(*basechain), GFP_KERNEL);
if (basechain == NULL)
return -ENOMEM;
basechain->type = type;
chain = &basechain->chain;
ops = &basechain->ops;
ops->pf = family;
ops->owner = afi->owner;
ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
ops->priv = chain;
ops->hook = hookfn;
if (afi->hooks[ops->hooknum])
ops->hook = afi->hooks[ops->hooknum];
chain->flags |= NFT_BASE_CHAIN;
if (nla[NFTA_CHAIN_POLICY]) {
err = nf_tables_chain_policy(basechain,
nla[NFTA_CHAIN_POLICY]);
if (err < 0) {
free_percpu(basechain->stats);
kfree(basechain);
return err;
}
} else
basechain->policy = NF_ACCEPT;
if (nla[NFTA_CHAIN_COUNTERS]) {
err = nf_tables_counters(basechain,
nla[NFTA_CHAIN_COUNTERS]);
if (err < 0) {
free_percpu(basechain->stats);
module_put(type->owner);
kfree(basechain);
return err;
}
......@@ -973,12 +951,33 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
struct nft_stats __percpu *newstats;
newstats = alloc_percpu(struct nft_stats);
if (newstats == NULL)
if (newstats == NULL) {
module_put(type->owner);
kfree(basechain);
return -ENOMEM;
}
rcu_assign_pointer(basechain->stats, newstats);
}
rcu_assign_pointer(nft_base_chain(chain)->stats,
newstats);
basechain->type = type;
chain = &basechain->chain;
for (i = 0; i < afi->nops; i++) {
ops = &basechain->ops[i];
ops->pf = family;
ops->owner = afi->owner;
ops->hooknum = hooknum;
ops->priority = priority;
ops->priv = chain;
ops->hook = afi->hooks[ops->hooknum];
if (hookfn)
ops->hook = hookfn;
if (afi->hook_ops_init)
afi->hook_ops_init(ops, i);
}
chain->flags |= NFT_BASE_CHAIN;
basechain->policy = policy;
} else {
chain = kzalloc(sizeof(*chain), GFP_KERNEL);
if (chain == NULL)
......@@ -993,8 +992,9 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN) {
err = nf_register_hook(&nft_base_chain(chain)->ops);
err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
if (err < 0) {
module_put(basechain->type->owner);
free_percpu(basechain->stats);
kfree(basechain);
return err;
......@@ -1015,6 +1015,7 @@ static void nf_tables_rcu_chain_destroy(struct rcu_head *head)
BUG_ON(chain->use > 0);
if (chain->flags & NFT_BASE_CHAIN) {
module_put(nft_base_chain(chain)->type->owner);
free_percpu(nft_base_chain(chain)->stats);
kfree(nft_base_chain(chain));
} else
......@@ -1052,7 +1053,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN)
nf_unregister_hook(&nft_base_chain(chain)->ops);
nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
family);
......
......@@ -109,14 +109,14 @@ static inline void nft_trace_packet(const struct nft_pktinfo *pkt,
{
struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
nf_log_packet(net, pkt->xt.family, pkt->hooknum, pkt->skb, pkt->in,
nf_log_packet(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in,
pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
chain->table->name, chain->name, comments[type],
rulenum);
}
unsigned int
nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
nft_do_chain(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
{
const struct nft_chain *chain = ops->priv;
const struct nft_rule *rule;
......@@ -216,7 +216,7 @@ nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)
return nft_base_chain(chain)->policy;
}
EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo);
EXPORT_SYMBOL_GPL(nft_do_chain);
int __init nf_tables_core_module_init(void)
{
......
/*
* Copyright (c) 2012-2014 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_ipv4.h>
#include <net/netfilter/nf_tables_ipv6.h>
#include <net/ip.h>
static void nft_inet_hook_ops_init(struct nf_hook_ops *ops, unsigned int n)
{
struct nft_af_info *afi;
if (n == 1)
afi = &nft_af_ipv4;
else
afi = &nft_af_ipv6;
ops->pf = afi->family;
if (afi->hooks[ops->hooknum])
ops->hook = afi->hooks[ops->hooknum];
}
static struct nft_af_info nft_af_inet __read_mostly = {
.family = NFPROTO_INET,
.nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE,
.nops = 2,
.hook_ops_init = nft_inet_hook_ops_init,
};
static int __net_init nf_tables_inet_init_net(struct net *net)
{
net->nft.inet = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
if (net->nft.inet == NULL)
return -ENOMEM;
memcpy(net->nft.inet, &nft_af_inet, sizeof(nft_af_inet));
if (nft_register_afinfo(net, net->nft.inet) < 0)
goto err;
return 0;
err:
kfree(net->nft.inet);
return -ENOMEM;
}
static void __net_exit nf_tables_inet_exit_net(struct net *net)
{
nft_unregister_afinfo(net->nft.inet);
kfree(net->nft.inet);
}
static struct pernet_operations nf_tables_inet_net_ops = {
.init = nf_tables_inet_init_net,
.exit = nf_tables_inet_exit_net,
};
static const struct nf_chain_type filter_inet = {
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.family = NFPROTO_INET,
.owner = THIS_MODULE,
.hook_mask = (1 << NF_INET_LOCAL_IN) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_FORWARD) |
(1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING),
};
static int __init nf_tables_inet_init(void)
{
int ret;
nft_register_chain_type(&filter_inet);
ret = register_pernet_subsys(&nf_tables_inet_net_ops);
if (ret < 0)
nft_unregister_chain_type(&filter_inet);
return ret;
}
static void __exit nf_tables_inet_exit(void)
{
unregister_pernet_subsys(&nf_tables_inet_net_ops);
nft_unregister_chain_type(&filter_inet);
}
module_init(nf_tables_inet_init);
module_exit(nf_tables_inet_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_FAMILY(1);
......@@ -92,7 +92,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops;
const struct nf_hook_ops *ops = &basechain->ops[0];
par->hook_mask = 1 << ops->hooknum;
}
......@@ -253,7 +253,7 @@ static int nft_target_validate(const struct nft_ctx *ctx,
if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops;
const struct nf_hook_ops *ops = &basechain->ops[0];
hook_mask = 1 << ops->hooknum;
if (hook_mask & target->hooks)
......@@ -323,7 +323,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops;
const struct nf_hook_ops *ops = &basechain->ops[0];
par->hook_mask = 1 << ops->hooknum;
}
......@@ -449,7 +449,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
if (ctx->chain->flags & NFT_BASE_CHAIN) {
const struct nft_base_chain *basechain =
nft_base_chain(ctx->chain);
const struct nf_hook_ops *ops = &basechain->ops;
const struct nf_hook_ops *ops = &basechain->ops[0];
hook_mask = 1 << ops->hooknum;
if (hook_mask & match->hooks)
......
......@@ -18,15 +18,19 @@
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_tuple.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_ecache.h>
struct nft_ct {
enum nft_ct_keys key:8;
enum ip_conntrack_dir dir:8;
union{
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
uint8_t family;
};
static void nft_ct_eval(const struct nft_expr *expr,
static void nft_ct_get_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
......@@ -123,24 +127,77 @@ static void nft_ct_eval(const struct nft_expr *expr,
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}
static void nft_ct_set_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_ct *priv = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb;
u32 value = data[priv->sreg].data[0];
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (ct == NULL)
return;
switch (priv->key) {
#ifdef CONFIG_NF_CONNTRACK_MARK
case NFT_CT_MARK:
if (ct->mark != value) {
ct->mark = value;
nf_conntrack_event_cache(IPCT_MARK, ct);
}
break;
#endif
}
}
static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
[NFTA_CT_DREG] = { .type = NLA_U32 },
[NFTA_CT_KEY] = { .type = NLA_U32 },
[NFTA_CT_DIRECTION] = { .type = NLA_U8 },
[NFTA_CT_SREG] = { .type = NLA_U32 },
};
static int nft_ct_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
static int nft_ct_l3proto_try_module_get(uint8_t family)
{
struct nft_ct *priv = nft_expr_priv(expr);
int err;
if (tb[NFTA_CT_DREG] == NULL ||
tb[NFTA_CT_KEY] == NULL)
return -EINVAL;
if (family == NFPROTO_INET) {
err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
if (err < 0)
goto err1;
err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
if (err < 0)
goto err2;
} else {
err = nf_ct_l3proto_try_module_get(family);
if (err < 0)
goto err1;
}
return 0;
err2:
nf_ct_l3proto_module_put(NFPROTO_IPV4);
err1:
return err;
}
static void nft_ct_l3proto_module_put(uint8_t family)
{
if (family == NFPROTO_INET) {
nf_ct_l3proto_module_put(NFPROTO_IPV4);
nf_ct_l3proto_module_put(NFPROTO_IPV6);
} else
nf_ct_l3proto_module_put(family);
}
static int nft_ct_init_validate_get(const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_ct *priv = nft_expr_priv(expr);
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
if (tb[NFTA_CT_DIRECTION] != NULL) {
priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
switch (priv->dir) {
......@@ -179,34 +236,72 @@ static int nft_ct_init(const struct nft_ctx *ctx,
return -EOPNOTSUPP;
}
err = nf_ct_l3proto_try_module_get(ctx->afi->family);
return 0;
}
static int nft_ct_init_validate_set(uint32_t key)
{
switch (key) {
case NFT_CT_MARK:
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int nft_ct_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_ct *priv = nft_expr_priv(expr);
int err;
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
if (tb[NFTA_CT_DREG]) {
err = nft_ct_init_validate_get(expr, tb);
if (err < 0)
return err;
priv->family = ctx->afi->family;
priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
goto err1;
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
err = nft_validate_data_load(ctx, priv->dreg, NULL,
NFT_DATA_VALUE);
if (err < 0)
goto err1;
return 0;
return err;
} else {
err = nft_ct_init_validate_set(priv->key);
if (err < 0)
return err;
err1:
nf_ct_l3proto_module_put(ctx->afi->family);
priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
return err;
}
err = nft_ct_l3proto_try_module_get(ctx->afi->family);
if (err < 0)
return err;
priv->family = ctx->afi->family;
return 0;
}
static void nft_ct_destroy(const struct nft_expr *expr)
{
struct nft_ct *priv = nft_expr_priv(expr);
nf_ct_l3proto_module_put(priv->family);
nft_ct_l3proto_module_put(priv->family);
}
static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr)
static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_ct *priv = nft_expr_priv(expr);
......@@ -222,19 +317,61 @@ static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr)
return -1;
}
static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_ct *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_ct_type;
static const struct nft_expr_ops nft_ct_ops = {
static const struct nft_expr_ops nft_ct_get_ops = {
.type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_eval,
.eval = nft_ct_get_eval,
.init = nft_ct_init,
.destroy = nft_ct_destroy,
.dump = nft_ct_dump,
.dump = nft_ct_get_dump,
};
static const struct nft_expr_ops nft_ct_set_ops = {
.type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_set_eval,
.init = nft_ct_init,
.destroy = nft_ct_destroy,
.dump = nft_ct_set_dump,
};
static const struct nft_expr_ops *
nft_ct_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
if (tb[NFTA_CT_KEY] == NULL)
return ERR_PTR(-EINVAL);
if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
return ERR_PTR(-EINVAL);
if (tb[NFTA_CT_DREG])
return &nft_ct_get_ops;
if (tb[NFTA_CT_SREG])
return &nft_ct_set_ops;
return ERR_PTR(-EINVAL);
}
static struct nft_expr_type nft_ct_type __read_mostly = {
.name = "ct",
.ops = &nft_ct_ops,
.select_ops = &nft_ct_select_ops,
.policy = nft_ct_policy,
.maxattr = NFTA_CT_MAX,
.owner = THIS_MODULE,
......
......@@ -33,7 +33,7 @@ static void nft_log_eval(const struct nft_expr *expr,
const struct nft_log *priv = nft_expr_priv(expr);
struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
nf_log_packet(net, priv->family, pkt->hooknum, pkt->skb, pkt->in,
nf_log_packet(net, priv->family, pkt->ops->hooknum, pkt->skb, pkt->in,
pkt->out, &priv->loginfo, "%s", priv->prefix);
}
......
......@@ -43,6 +43,12 @@ static void nft_meta_get_eval(const struct nft_expr *expr,
case NFT_META_PROTOCOL:
*(__be16 *)dest->data = skb->protocol;
break;
case NFT_META_NFPROTO:
dest->data[0] = pkt->ops->pf;
break;
case NFT_META_L4PROTO:
dest->data[0] = pkt->tprot;
break;
case NFT_META_PRIORITY:
dest->data[0] = skb->priority;
break;
......@@ -181,6 +187,8 @@ static int nft_meta_init_validate_get(uint32_t key)
switch (key) {
case NFT_META_LEN:
case NFT_META_PROTOCOL:
case NFT_META_NFPROTO:
case NFT_META_L4PROTO:
case NFT_META_PRIORITY:
case NFT_META_MARK:
case NFT_META_IIF:
......@@ -231,6 +239,9 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return err;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
return err;
return 0;
}
......
......@@ -34,8 +34,9 @@ static void nft_reject_eval(const struct nft_expr *expr,
const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
#endif
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
if (priv->family == NFPROTO_IPV4)
......@@ -43,15 +44,15 @@ static void nft_reject_eval(const struct nft_expr *expr,
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
else if (priv->family == NFPROTO_IPV6)
nf_send_unreach6(net, pkt->skb, priv->icmp_code,
pkt->hooknum);
pkt->ops->hooknum);
#endif
break;
case NFT_REJECT_TCP_RST:
if (priv->family == NFPROTO_IPV4)
nf_send_reset(pkt->skb, pkt->hooknum);
nf_send_reset(pkt->skb, pkt->ops->hooknum);
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
else if (priv->family == NFPROTO_IPV6)
nf_send_reset6(net, pkt->skb, pkt->hooknum);
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
#endif
break;
}
......
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