Commit da33edcc authored by David S. Miller's avatar David S. Miller

Merge branch 'net-next' of git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nftables

Pablo Neira Ayuso says:

====================
netfilter updates: nf_tables pull request

The following patchset contains the current original nf_tables tree
condensed in 17 patches. I have organized them by chronogical order
since the original nf_tables code was released in 2009 and by
dependencies between the different patches.

The patches are:

1) Adapt all existing hooks in the tree to pass hook ops to the
   hook callback function, required by nf_tables, from Patrick McHardy.

2) Move alloc_null_binding to nf_nat_core, as it is now also needed by
   nf_tables and ip_tables, original patch from Patrick McHardy but
   required major changes to adapt it to the current tree that I made.

3) Add nf_tables core, including the netlink API, the packet filtering
   engine, expressions and built-in tables, from Patrick McHardy. This
   patch includes accumulated fixes since 2009 and minor enhancements.
   The patch description contains a list of references to the original
   patches for the record. For those that are not familiar to the
   original work, see [1], [2] and [3].

4) Add netlink set API, this replaces the original set infrastructure
   to introduce a netlink API to add/delete sets and to add/delete
   set elements. This includes two set types: the hash and the rb-tree
   sets (used for interval based matching). The main difference with
   ipset is that this infrastructure is data type agnostic. Patch from
   Patrick McHardy.

5) Allow expression operation overload, this API change allows us to
   provide define expression subtypes depending on the configuration
   that is received from user-space via Netlink. It is used by follow
   up patches to provide optimized versions of the payload and cmp
   expressions and the x_tables compatibility layer, from Patrick
   McHardy.

6) Add optimized data comparison operation, it requires the previous
   patch, from Patrick McHardy.

7) Add optimized payload implementation, it requires patch 5, from
   Patrick McHardy.

8) Convert built-in tables to chain types. Each chain type have special
   semantics (filter, route and nat) that are used by userspace to
   configure the chain behaviour. The main chain regarding iptables
   is that tables become containers of chain, with no specific semantics.
   However, you may still configure your tables and chains to retain
   iptables like semantics, patch from me.

9) Add compatibility layer for x_tables. This patch adds support to
   use all existing x_tables extensions from nf_tables, this is used
   to provide a userspace utility that accepts iptables syntax but
   used internally the nf_tables kernel core. This patch includes
   missing features in the nf_tables core such as the per-chain
   stats, default chain policy and number of chain references, which
   are required by the iptables compatibility userspace tool. Patch
   from me.

10) Fix transport protocol matching, this fix is a side effect of the
    x_tables compatibility layer, which now provides a pointer to the
    transport header, from me.

11) Add support for dormant tables, this feature allows you to disable
    all chains and rules that are contained in one table, from me.

12) Add IPv6 NAT support. At the time nf_tables was made, there was no
    NAT IPv6 support yet, from Tomasz Bursztyka.

13) Complete net namespace support. This patch register the protocol
    family per net namespace, so tables (thus, other objects contained
    in tables such as sets, chains and rules) are only visible from the
    corresponding net namespace, from me.

14) Add the insert operation to the nf_tables netlink API, this requires
    adding a new position attribute that allow us to locate where in the
    ruleset a rule needs to be inserted, from Eric Leblond.

15) Add rule batching support, including atomic rule-set updates by
    using rule-set generations. This patch includes a change to nfnetlink
    to include two new control messages to indicate the beginning and
    the end of a batch. The end message is interpreted as the commit
    message, if it's missing, then the rule-set updates contained in the
    batch are aborted, from me.

16) Add trace support to the nf_tables packet filtering core, from me.

17) Add ARP filtering support, original patch from Patrick McHardy, but
    adapted to fit into the chain type infrastructure. This was recovered
    to be used by nft userspace tool and our compatibility arptables
    userspace tool.

There is still work to do to fully replace x_tables [4] [5] but that can
be done incrementally by extending our netlink API. Moreover, looking at
netfilter-devel and the amount of contributions to nf_tables we've been
getting, I think it would be good to have it mainstream to avoid accumulating
large patchsets skip continuous rebases.

I tried to provide a reasonable patchset, we have more than 100 accumulated
patches in the original nf_tables tree, so I collapsed many of the small
fixes to the main patch we had since 2009 and provide a small batch for
review to netdev, while trying to retain part of the history.

For those who didn't give a try to nf_tables yet, there's a quick howto
available from Eric Leblond that describes how to get things working [6].

Comments/reviews welcome.

Thanks!

[1] http://lwn.net/Articles/324251/
[2] http://workshop.netfilter.org/2013/wiki/images/e/ee/Nftables-osd-2013-developer.pdf
[3] http://lwn.net/Articles/564095/
[4] http://people.netfilter.org/pablo/map-pending-work.txt
[4] http://people.netfilter.org/pablo/nftables-todo.txt
[5] https://home.regit.org/netfilter-en/nftables-quick-howto/
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 78dea8cc ed683f13
......@@ -42,7 +42,8 @@ int netfilter_init(void);
struct sk_buff;
typedef unsigned int nf_hookfn(unsigned int hooknum,
struct nf_hook_ops;
typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -52,12 +53,13 @@ struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook;
struct module *owner;
u_int8_t pf;
unsigned int hooknum;
nf_hookfn *hook;
struct module *owner;
void *priv;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
int priority;
};
struct nf_sockopt_ops {
......
......@@ -14,6 +14,9 @@ struct nfnl_callback {
int (*call_rcu)(struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
int (*call_batch)(struct sock *nl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const cda[]);
const struct nla_policy *policy; /* netlink attribute policy */
const u_int16_t attr_count; /* number of nlattr's */
};
......@@ -23,6 +26,8 @@ struct nfnetlink_subsystem {
__u8 subsys_id; /* nfnetlink subsystem ID */
__u8 cb_count; /* number of callbacks */
const struct nfnl_callback *cb; /* callback for individual types */
int (*commit)(struct sk_buff *skb);
int (*abort)(struct sk_buff *skb);
};
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
......
......@@ -22,6 +22,7 @@
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
#include <net/netns/conntrack.h>
#endif
#include <net/netns/nftables.h>
#include <net/netns/xfrm.h>
struct user_namespace;
......@@ -101,6 +102,9 @@ struct net {
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct netns_ct ct;
#endif
#if defined(CONFIG_NF_TABLES) || defined(CONFIG_NF_TABLES_MODULE)
struct netns_nftables nft;
#endif
#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6)
struct netns_nf_frag nf_frag;
#endif
......
......@@ -45,6 +45,9 @@ unsigned int nf_nat_setup_info(struct nf_conn *ct,
const struct nf_nat_range *range,
enum nf_nat_manip_type maniptype);
extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct,
unsigned int hooknum);
/* Is this tuple already taken? (not by us)*/
int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
const struct nf_conn *ignored_conntrack);
......
This diff is collapsed.
#ifndef _NET_NF_TABLES_CORE_H
#define _NET_NF_TABLES_CORE_H
extern int nf_tables_core_module_init(void);
extern void nf_tables_core_module_exit(void);
extern int nft_immediate_module_init(void);
extern void nft_immediate_module_exit(void);
struct nft_cmp_fast_expr {
u32 data;
enum nft_registers sreg:8;
u8 len;
};
extern const struct nft_expr_ops nft_cmp_fast_ops;
extern int nft_cmp_module_init(void);
extern void nft_cmp_module_exit(void);
extern int nft_lookup_module_init(void);
extern void nft_lookup_module_exit(void);
extern int nft_bitwise_module_init(void);
extern void nft_bitwise_module_exit(void);
extern int nft_byteorder_module_init(void);
extern void nft_byteorder_module_exit(void);
struct nft_payload {
enum nft_payload_bases base:8;
u8 offset;
u8 len;
enum nft_registers dreg:8;
};
extern const struct nft_expr_ops nft_payload_fast_ops;
extern int nft_payload_module_init(void);
extern void nft_payload_module_exit(void);
#endif /* _NET_NF_TABLES_CORE_H */
#ifndef _NF_TABLES_IPV4_H_
#define _NF_TABLES_IPV4_H_
#include <net/netfilter/nf_tables.h>
#include <net/ip.h>
static inline void
nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out)
{
struct iphdr *ip;
nft_set_pktinfo(pkt, ops, skb, in, out);
pkt->xt.thoff = ip_hdrlen(pkt->skb);
ip = ip_hdr(pkt->skb);
pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
}
#endif
#ifndef _NF_TABLES_IPV6_H_
#define _NF_TABLES_IPV6_H_
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/ipv6.h>
static inline int
nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out)
{
int protohdr, thoff = 0;
unsigned short frag_off;
nft_set_pktinfo(pkt, ops, skb, in, out);
protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
/* If malformed, drop it */
if (protohdr < 0)
return -1;
pkt->xt.thoff = thoff;
pkt->xt.fragoff = frag_off;
return 0;
}
#endif
#ifndef _NETNS_NFTABLES_H_
#define _NETNS_NFTABLES_H_
#include <linux/list.h>
struct nft_af_info;
struct netns_nftables {
struct list_head af_info;
struct list_head commit_list;
struct nft_af_info *ipv4;
struct nft_af_info *ipv6;
struct nft_af_info *arp;
struct nft_af_info *bridge;
u8 gencursor;
u8 genctr;
};
#endif
......@@ -5,6 +5,8 @@ header-y += nf_conntrack_ftp.h
header-y += nf_conntrack_sctp.h
header-y += nf_conntrack_tcp.h
header-y += nf_conntrack_tuple_common.h
header-y += nf_tables.h
header-y += nf_tables_compat.h
header-y += nf_nat.h
header-y += nfnetlink.h
header-y += nfnetlink_acct.h
......
......@@ -25,6 +25,10 @@ enum ip_conntrack_info {
IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
};
#define NF_CT_STATE_INVALID_BIT (1 << 0)
#define NF_CT_STATE_BIT(ctinfo) (1 << ((ctinfo) % IP_CT_IS_REPLY + 1))
#define NF_CT_STATE_UNTRACKED_BIT (1 << (IP_CT_NUMBER + 1))
/* Bitset representing status of connection. */
enum ip_conntrack_status {
/* It's an expected connection: bit 0 set. This bit never changed */
......
This diff is collapsed.
#ifndef _NFT_COMPAT_NFNETLINK_H_
#define _NFT_COMPAT_NFNETLINK_H_
enum nft_target_attributes {
NFTA_TARGET_UNSPEC,
NFTA_TARGET_NAME,
NFTA_TARGET_REV,
NFTA_TARGET_INFO,
__NFTA_TARGET_MAX
};
#define NFTA_TARGET_MAX (__NFTA_TARGET_MAX - 1)
enum nft_match_attributes {
NFTA_MATCH_UNSPEC,
NFTA_MATCH_NAME,
NFTA_MATCH_REV,
NFTA_MATCH_INFO,
__NFTA_MATCH_MAX
};
#define NFTA_MATCH_MAX (__NFTA_MATCH_MAX - 1)
#define NFT_COMPAT_NAME_MAX 32
enum {
NFNL_MSG_COMPAT_GET,
NFNL_MSG_COMPAT_MAX
};
enum {
NFTA_COMPAT_UNSPEC = 0,
NFTA_COMPAT_NAME,
NFTA_COMPAT_REV,
NFTA_COMPAT_TYPE,
__NFTA_COMPAT_MAX,
};
#define NFTA_COMPAT_MAX (__NFTA_COMPAT_MAX - 1)
#endif
......@@ -18,6 +18,8 @@ enum nfnetlink_groups {
#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE
NFNLGRP_CONNTRACK_EXP_DESTROY,
#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY
NFNLGRP_NFTABLES,
#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES
__NFNLGRP_MAX,
};
#define NFNLGRP_MAX (__NFNLGRP_MAX - 1)
......@@ -51,6 +53,12 @@ struct nfgenmsg {
#define NFNL_SUBSYS_ACCT 7
#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
#define NFNL_SUBSYS_CTHELPER 9
#define NFNL_SUBSYS_COUNT 10
#define NFNL_SUBSYS_NFTABLES 10
#define NFNL_SUBSYS_NFT_COMPAT 11
#define NFNL_SUBSYS_COUNT 12
/* Reserved control nfnetlink messages */
#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1
#endif /* _UAPI_NFNETLINK_H */
......@@ -619,7 +619,7 @@ static int check_hbh_len(struct sk_buff *skb)
/* Replicate the checks that IPv6 does on packet reception and pass the packet
* to ip6tables, which doesn't support NAT, so things are fairly simple. */
static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -669,7 +669,8 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
* receiving device) to make netfilter happy, the REDIRECT
* target in particular. Save the original destination IP
* address to be able to detect DNAT afterwards. */
static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
static unsigned int br_nf_pre_routing(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 *))
......@@ -691,7 +692,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
return NF_ACCEPT;
nf_bridge_pull_encap_header_rcsum(skb);
return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
return br_nf_pre_routing_ipv6(ops, skb, in, out, okfn);
}
if (!brnf_call_iptables && !br->nf_call_iptables)
......@@ -727,7 +728,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb,
* took place when the packet entered the bridge), but we
* register an IPv4 PRE_ROUTING 'sabotage' hook that will
* prevent this from happening. */
static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb,
static unsigned int br_nf_local_in(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 *))
......@@ -765,7 +767,8 @@ static int br_nf_forward_finish(struct sk_buff *skb)
* but we are still able to filter on the 'real' indev/outdev
* because of the physdev module. For ARP, indev and outdev are the
* bridge ports. */
static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
static unsigned int br_nf_forward_ip(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 *))
......@@ -818,7 +821,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb,
return NF_STOLEN;
}
static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
static unsigned int br_nf_forward_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 *))
......@@ -878,7 +882,8 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb)
#endif
/* PF_BRIDGE/POST_ROUTING ********************************************/
static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
static unsigned int br_nf_post_routing(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 *))
......@@ -923,7 +928,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb,
/* IP/SABOTAGE *****************************************************/
/* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
* for the second time. */
static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb,
static unsigned int ip_sabotage_in(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 *))
......
#
# Bridge netfilter configuration
#
#
config NF_TABLES_BRIDGE
tristate "Ethernet Bridge nf_tables support"
menuconfig BRIDGE_NF_EBTABLES
tristate "Ethernet Bridge tables (ebtables) support"
......
......@@ -2,6 +2,8 @@
# Makefile for the netfilter modules for Link Layer filtering on a bridge.
#
obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
# tables
......
......@@ -60,17 +60,21 @@ static const struct ebt_table frame_filter =
};
static unsigned int
ebt_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, int (*okfn)(struct sk_buff *))
ebt_in_hook(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 *))
{
return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_filter);
return ebt_do_table(ops->hooknum, skb, in, out,
dev_net(in)->xt.frame_filter);
}
static unsigned int
ebt_out_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, int (*okfn)(struct sk_buff *))
ebt_out_hook(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 *))
{
return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_filter);
return ebt_do_table(ops->hooknum, skb, in, out,
dev_net(out)->xt.frame_filter);
}
static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
......
......@@ -60,17 +60,21 @@ static struct ebt_table frame_nat =
};
static unsigned int
ebt_nat_in(unsigned int hook, struct sk_buff *skb, const struct net_device *in
, const struct net_device *out, int (*okfn)(struct sk_buff *))
ebt_nat_in(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 *))
{
return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_nat);
return ebt_do_table(ops->hooknum, skb, in, out,
dev_net(in)->xt.frame_nat);
}
static unsigned int
ebt_nat_out(unsigned int hook, struct sk_buff *skb, const struct net_device *in
, const struct net_device *out, int (*okfn)(struct sk_buff *))
ebt_nat_out(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 *))
{
return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_nat);
return ebt_do_table(ops->hooknum, skb, in, out,
dev_net(out)->xt.frame_nat);
}
static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
......
/*
* Copyright (c) 2008 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netfilter_bridge.h>
#include <net/netfilter/nf_tables.h>
static struct nft_af_info nft_af_bridge __read_mostly = {
.family = NFPROTO_BRIDGE,
.nhooks = NF_BR_NUMHOOKS,
.owner = THIS_MODULE,
};
static int nf_tables_bridge_init_net(struct net *net)
{
net->nft.bridge = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
if (net->nft.bridge == NULL)
return -ENOMEM;
memcpy(net->nft.bridge, &nft_af_bridge, sizeof(nft_af_bridge));
if (nft_register_afinfo(net, net->nft.bridge) < 0)
goto err;
return 0;
err:
kfree(net->nft.bridge);
return -ENOMEM;
}
static void nf_tables_bridge_exit_net(struct net *net)
{
nft_unregister_afinfo(net->nft.bridge);
kfree(net->nft.bridge);
}
static struct pernet_operations nf_tables_bridge_net_ops = {
.init = nf_tables_bridge_init_net,
.exit = nf_tables_bridge_exit_net,
};
static int __init nf_tables_bridge_init(void)
{
return register_pernet_subsys(&nf_tables_bridge_net_ops);
}
static void __exit nf_tables_bridge_exit(void)
{
return unregister_pernet_subsys(&nf_tables_bridge_net_ops);
}
module_init(nf_tables_bridge_init);
module_exit(nf_tables_bridge_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_FAMILY(AF_BRIDGE);
......@@ -87,7 +87,7 @@ static void dnrmg_send_peer(struct sk_buff *skb)
}
static unsigned int dnrmg_hook(unsigned int hook,
static unsigned int dnrmg_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......
......@@ -36,6 +36,27 @@ config NF_CONNTRACK_PROC_COMPAT
If unsure, say Y.
config NF_TABLES_IPV4
depends on NF_TABLES
tristate "IPv4 nf_tables support"
config NFT_REJECT_IPV4
depends on NF_TABLES_IPV4
tristate "nf_tables IPv4 reject support"
config NFT_CHAIN_ROUTE_IPV4
depends on NF_TABLES_IPV4
tristate "IPv4 nf_tables route chain support"
config NFT_CHAIN_NAT_IPV4
depends on NF_TABLES_IPV4
depends on NF_NAT_IPV4 && NFT_NAT
tristate "IPv4 nf_tables nat chain support"
config NF_TABLES_ARP
depends on NF_TABLES
tristate "ARP nf_tables support"
config IP_NF_IPTABLES
tristate "IP tables support (required for filtering/masq/NAT)"
default m if NETFILTER_ADVANCED=n
......
......@@ -27,6 +27,12 @@ obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o
# NAT protocols (nf_nat)
obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o
obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o
obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o
# generic IP tables
obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o
......
......@@ -27,13 +27,14 @@ static const struct xt_table packet_filter = {
/* The work comes in here from netfilter.c */
static unsigned int
arptable_filter_hook(unsigned int hook, struct sk_buff *skb,
arptable_filter_hook(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 *))
{
const struct net *net = dev_net((in != NULL) ? in : out);
return arpt_do_table(skb, hook, in, out, net->ipv4.arptable_filter);
return arpt_do_table(skb, ops->hooknum, in, out,
net->ipv4.arptable_filter);
}
static struct nf_hook_ops *arpfilter_ops __read_mostly;
......
......@@ -483,7 +483,7 @@ static void arp_print(struct arp_payload *payload)
#endif
static unsigned int
arp_mangle(unsigned int hook,
arp_mangle(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......
......@@ -297,7 +297,7 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par)
return XT_CONTINUE;
}
static unsigned int ipv4_synproxy_hook(unsigned int hooknum,
static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......
......@@ -33,20 +33,21 @@ static const struct xt_table packet_filter = {
};
static unsigned int
iptable_filter_hook(unsigned int hook, struct sk_buff *skb,
iptable_filter_hook(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 *))
{
const struct net *net;
if (hook == NF_INET_LOCAL_OUT &&
if (ops->hooknum == NF_INET_LOCAL_OUT &&
(skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr)))
/* root is playing with raw sockets. */
return NF_ACCEPT;
net = dev_net((in != NULL) ? in : out);
return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_filter);
return ipt_do_table(skb, ops->hooknum, in, out,
net->ipv4.iptable_filter);
}
static struct nf_hook_ops *filter_ops __read_mostly;
......
......@@ -79,19 +79,19 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out)
/* The work comes in here from netfilter.c. */
static unsigned int
iptable_mangle_hook(unsigned int hook,
iptable_mangle_hook(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 (hook == NF_INET_LOCAL_OUT)
if (ops->hooknum == NF_INET_LOCAL_OUT)
return ipt_mangle_out(skb, out);
if (hook == NF_INET_POST_ROUTING)
return ipt_do_table(skb, hook, in, out,
if (ops->hooknum == NF_INET_POST_ROUTING)
return ipt_do_table(skb, ops->hooknum, in, out,
dev_net(out)->ipv4.iptable_mangle);
/* PREROUTING/INPUT/FORWARD: */
return ipt_do_table(skb, hook, in, out,
return ipt_do_table(skb, ops->hooknum, in, out,
dev_net(in)->ipv4.iptable_mangle);
}
......
......@@ -61,7 +61,7 @@ static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
}
static unsigned int
nf_nat_ipv4_fn(unsigned int hooknum,
nf_nat_ipv4_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -71,7 +71,7 @@ nf_nat_ipv4_fn(unsigned int hooknum,
enum ip_conntrack_info ctinfo;
struct nf_conn_nat *nat;
/* maniptype == SRC for postrouting. */
enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
/* We never see fragments: conntrack defrags on pre-routing
* and local-out, and nf_nat_out protects post-routing.
......@@ -108,7 +108,7 @@ nf_nat_ipv4_fn(unsigned int hooknum,
case IP_CT_RELATED_REPLY:
if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
hooknum))
ops->hooknum))
return NF_DROP;
else
return NF_ACCEPT;
......@@ -121,14 +121,14 @@ nf_nat_ipv4_fn(unsigned int hooknum,
if (!nf_nat_initialized(ct, maniptype)) {
unsigned int ret;
ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct);
if (ret != NF_ACCEPT)
return ret;
} else {
pr_debug("Already setup manip %s for ct %p\n",
maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
goto oif_changed;
}
break;
......@@ -137,11 +137,11 @@ nf_nat_ipv4_fn(unsigned int hooknum,
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == IP_CT_ESTABLISHED_REPLY);
if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
goto oif_changed;
}
return nf_nat_packet(ct, ctinfo, hooknum, skb);
return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
oif_changed:
nf_ct_kill_acct(ct, ctinfo, skb);
......@@ -149,7 +149,7 @@ nf_nat_ipv4_fn(unsigned int hooknum,
}
static unsigned int
nf_nat_ipv4_in(unsigned int hooknum,
nf_nat_ipv4_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -158,7 +158,7 @@ nf_nat_ipv4_in(unsigned int hooknum,
unsigned int ret;
__be32 daddr = ip_hdr(skb)->daddr;
ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
daddr != ip_hdr(skb)->daddr)
skb_dst_drop(skb);
......@@ -167,7 +167,7 @@ nf_nat_ipv4_in(unsigned int hooknum,
}
static unsigned int
nf_nat_ipv4_out(unsigned int hooknum,
nf_nat_ipv4_out(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -185,7 +185,7 @@ nf_nat_ipv4_out(unsigned int hooknum,
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN &&
!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
......@@ -207,7 +207,7 @@ nf_nat_ipv4_out(unsigned int hooknum,
}
static unsigned int
nf_nat_ipv4_local_fn(unsigned int hooknum,
nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -223,7 +223,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn);
ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
......
......@@ -20,20 +20,20 @@ static const struct xt_table packet_raw = {
/* The work comes in here from netfilter.c. */
static unsigned int
iptable_raw_hook(unsigned int hook, struct sk_buff *skb,
iptable_raw_hook(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 *))
{
const struct net *net;
if (hook == NF_INET_LOCAL_OUT &&
if (ops->hooknum == NF_INET_LOCAL_OUT &&
(skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr)))
/* root is playing with raw sockets. */
return NF_ACCEPT;
net = dev_net((in != NULL) ? in : out);
return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_raw);
return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.iptable_raw);
}
static struct nf_hook_ops *rawtable_ops __read_mostly;
......
......@@ -37,21 +37,22 @@ static const struct xt_table security_table = {
};
static unsigned int
iptable_security_hook(unsigned int hook, struct sk_buff *skb,
iptable_security_hook(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 *))
{
const struct net *net;
if (hook == NF_INET_LOCAL_OUT &&
if (ops->hooknum == NF_INET_LOCAL_OUT &&
(skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr)))
/* Somebody is playing with raw sockets. */
return NF_ACCEPT;
net = dev_net((in != NULL) ? in : out);
return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_security);
return ipt_do_table(skb, ops->hooknum, in, out,
net->ipv4.iptable_security);
}
static struct nf_hook_ops *sectbl_ops __read_mostly;
......
......@@ -92,7 +92,7 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
return NF_ACCEPT;
}
static unsigned int ipv4_helper(unsigned int hooknum,
static unsigned int ipv4_helper(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -121,7 +121,7 @@ static unsigned int ipv4_helper(unsigned int hooknum,
ct, ctinfo);
}
static unsigned int ipv4_confirm(unsigned int hooknum,
static unsigned int ipv4_confirm(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -147,16 +147,16 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
return nf_conntrack_confirm(skb);
}
static unsigned int ipv4_conntrack_in(unsigned int hooknum,
static unsigned int ipv4_conntrack_in(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 *))
{
return nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb);
return nf_conntrack_in(dev_net(in), PF_INET, ops->hooknum, skb);
}
static unsigned int ipv4_conntrack_local(unsigned int hooknum,
static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -166,7 +166,7 @@ static unsigned int ipv4_conntrack_local(unsigned int hooknum,
if (skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
return nf_conntrack_in(dev_net(out), PF_INET, hooknum, skb);
return nf_conntrack_in(dev_net(out), PF_INET, ops->hooknum, skb);
}
/* Connection tracking may drop packets, but never alters them, so
......
......@@ -60,7 +60,7 @@ static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum,
return IP_DEFRAG_CONNTRACK_OUT + zone;
}
static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -83,7 +83,9 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum,
#endif
/* Gather fragments. */
if (ip_is_fragment(ip_hdr(skb))) {
enum ip_defrag_users user = nf_ct_defrag_user(hooknum, skb);
enum ip_defrag_users user =
nf_ct_defrag_user(ops->hooknum, skb);
if (nf_ct_ipv4_gather_frags(skb, user))
return NF_STOLEN;
}
......
/*
* Copyright (c) 2008-2010 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netfilter_arp.h>
#include <net/netfilter/nf_tables.h>
static struct nft_af_info nft_af_arp __read_mostly = {
.family = NFPROTO_ARP,
.nhooks = NF_ARP_NUMHOOKS,
.owner = THIS_MODULE,
};
static int nf_tables_arp_init_net(struct net *net)
{
net->nft.arp = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
if (net->nft.arp== NULL)
return -ENOMEM;
memcpy(net->nft.arp, &nft_af_arp, sizeof(nft_af_arp));
if (nft_register_afinfo(net, net->nft.arp) < 0)
goto err;
return 0;
err:
kfree(net->nft.arp);
return -ENOMEM;
}
static void nf_tables_arp_exit_net(struct net *net)
{
nft_unregister_afinfo(net->nft.arp);
kfree(net->nft.arp);
}
static struct pernet_operations nf_tables_arp_net_ops = {
.init = nf_tables_arp_init_net,
.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,
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.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)
{
int ret;
nft_register_chain_type(&filter_arp);
ret = register_pernet_subsys(&nf_tables_arp_net_ops);
if (ret < 0)
nft_unregister_chain_type(&filter_arp);
return ret;
}
static void __exit nf_tables_arp_exit(void)
{
unregister_pernet_subsys(&nf_tables_arp_net_ops);
nft_unregister_chain_type(&filter_arp);
}
module_init(nf_tables_arp_init);
module_exit(nf_tables_arp_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_FAMILY(3); /* NFPROTO_ARP */
/*
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012-2013 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/netfilter_ipv4.h>
#include <net/netfilter/nf_tables.h>
#include <net/net_namespace.h>
#include <net/ip.h>
#include <net/net_namespace.h>
#include <net/netfilter/nf_tables_ipv4.h>
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 *))
{
struct nft_pktinfo pkt;
if (unlikely(skb->len < sizeof(struct iphdr) ||
ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) {
if (net_ratelimit())
pr_info("nf_tables_ipv4: ignoring short SOCK_RAW "
"packet\n");
return NF_ACCEPT;
}
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
return nft_do_chain_pktinfo(&pkt, ops);
}
static struct nft_af_info nft_af_ipv4 __read_mostly = {
.family = NFPROTO_IPV4,
.nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE,
.hooks = {
[NF_INET_LOCAL_OUT] = nft_ipv4_output,
},
};
static int nf_tables_ipv4_init_net(struct net *net)
{
net->nft.ipv4 = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
if (net->nft.ipv4 == NULL)
return -ENOMEM;
memcpy(net->nft.ipv4, &nft_af_ipv4, sizeof(nft_af_ipv4));
if (nft_register_afinfo(net, net->nft.ipv4) < 0)
goto err;
return 0;
err:
kfree(net->nft.ipv4);
return -ENOMEM;
}
static void nf_tables_ipv4_exit_net(struct net *net)
{
nft_unregister_afinfo(net->nft.ipv4);
kfree(net->nft.ipv4);
}
static struct pernet_operations nf_tables_ipv4_net_ops = {
.init = nf_tables_ipv4_init_net,
.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,
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.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)
{
nft_register_chain_type(&filter_ipv4);
return register_pernet_subsys(&nf_tables_ipv4_net_ops);
}
static void __exit nf_tables_ipv4_exit(void)
{
unregister_pernet_subsys(&nf_tables_ipv4_net_ops);
nft_unregister_chain_type(&filter_ipv4);
}
module_init(nf_tables_ipv4_init);
module_exit(nf_tables_ipv4_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_FAMILY(AF_INET);
/*
* Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
* Copyright (c) 2012 Intel Corporation
*
* 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_ipv4.h>
#include <net/netfilter/nf_nat_l3proto.h>
#include <net/ip.h>
/*
* NAT chains
*/
static unsigned int nf_nat_fn(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 *))
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_conn_nat *nat;
enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
struct nft_pktinfo pkt;
unsigned int ret;
if (ct == NULL || nf_ct_is_untracked(ct))
return NF_ACCEPT;
NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)));
nat = nfct_nat(ct);
if (nat == NULL) {
/* Conntrack module was loaded late, can't add extension. */
if (nf_ct_is_confirmed(ct))
return NF_ACCEPT;
nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
if (nat == NULL)
return NF_ACCEPT;
}
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED + IP_CT_IS_REPLY:
if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
ops->hooknum))
return NF_DROP;
else
return NF_ACCEPT;
}
/* Fall through */
case IP_CT_NEW:
if (nf_nat_initialized(ct, maniptype))
break;
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
ret = nft_do_chain_pktinfo(&pkt, ops);
if (ret != NF_ACCEPT)
return ret;
if (!nf_nat_initialized(ct, maniptype)) {
ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
if (ret != NF_ACCEPT)
return ret;
}
default:
break;
}
return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
}
static unsigned int nf_nat_prerouting(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 *))
{
__be32 daddr = ip_hdr(skb)->daddr;
unsigned int ret;
ret = nf_nat_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
ip_hdr(skb)->daddr != daddr) {
skb_dst_drop(skb);
}
return ret;
}
static unsigned int nf_nat_postrouting(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 *))
{
enum ip_conntrack_info ctinfo __maybe_unused;
const struct nf_conn *ct __maybe_unused;
unsigned int ret;
ret = nf_nat_fn(ops, skb, in, out, okfn);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (ct->tuplehash[dir].tuple.src.u3.ip !=
ct->tuplehash[!dir].tuple.dst.u3.ip ||
ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all)
return nf_xfrm_me_harder(skb, AF_INET) == 0 ?
ret : NF_DROP;
}
#endif
return ret;
}
static unsigned int nf_nat_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 *))
{
enum ip_conntrack_info ctinfo;
const struct nf_conn *ct;
unsigned int ret;
ret = nf_nat_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (ct->tuplehash[dir].tuple.dst.u3.ip !=
ct->tuplehash[!dir].tuple.src.u3.ip) {
if (ip_route_me_harder(skb, RTN_UNSPEC))
ret = NF_DROP;
}
#ifdef CONFIG_XFRM
else if (ct->tuplehash[dir].tuple.dst.u.all !=
ct->tuplehash[!dir].tuple.src.u.all)
if (nf_xfrm_me_harder(skb, AF_INET))
ret = NF_DROP;
#endif
}
return ret;
}
static struct nf_chain_type nft_chain_nat_ipv4 = {
.family = NFPROTO_IPV4,
.name = "nat",
.type = NFT_CHAIN_T_NAT,
.hook_mask = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_LOCAL_IN),
.fn = {
[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)
{
int err;
err = nft_register_chain_type(&nft_chain_nat_ipv4);
if (err < 0)
return err;
return 0;
}
static void __exit nft_chain_nat_exit(void)
{
nft_unregister_chain_type(&nft_chain_nat_ipv4);
}
module_init(nft_chain_nat_init);
module_exit(nft_chain_nat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat");
/*
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_ipv4.h>
#include <net/route.h>
#include <net/ip.h>
static unsigned int nf_route_table_hook(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 *))
{
unsigned int ret;
struct nft_pktinfo pkt;
u32 mark;
__be32 saddr, daddr;
u_int8_t tos;
const struct iphdr *iph;
/* root is playing with raw sockets. */
if (skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr))
return NF_ACCEPT;
nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
mark = skb->mark;
iph = ip_hdr(skb);
saddr = iph->saddr;
daddr = iph->daddr;
tos = iph->tos;
ret = nft_do_chain_pktinfo(&pkt, ops);
if (ret != NF_DROP && ret != NF_QUEUE) {
iph = ip_hdr(skb);
if (iph->saddr != saddr ||
iph->daddr != daddr ||
skb->mark != mark ||
iph->tos != tos)
if (ip_route_me_harder(skb, RTN_UNSPEC))
ret = NF_DROP;
}
return ret;
}
static struct nf_chain_type nft_chain_route_ipv4 = {
.family = NFPROTO_IPV4,
.name = "route",
.type = NFT_CHAIN_T_ROUTE,
.hook_mask = (1 << NF_INET_LOCAL_OUT),
.fn = {
[NF_INET_LOCAL_OUT] = nf_route_table_hook,
},
.me = THIS_MODULE,
};
static int __init nft_chain_route_init(void)
{
return nft_register_chain_type(&nft_chain_route_ipv4);
}
static void __exit nft_chain_route_exit(void)
{
nft_unregister_chain_type(&nft_chain_route_ipv4);
}
module_init(nft_chain_route_init);
module_exit(nft_chain_route_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_CHAIN(AF_INET, "route");
/*
* Copyright (c) 2008-2009 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#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/icmp.h>
struct nft_reject {
enum nft_reject_types type:8;
u8 icmp_code;
};
static void nft_reject_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
icmp_send(pkt->skb, ICMP_DEST_UNREACH, priv->icmp_code, 0);
break;
case NFT_REJECT_TCP_RST:
break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
}
static const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = {
[NFTA_REJECT_TYPE] = { .type = NLA_U32 },
[NFTA_REJECT_ICMP_CODE] = { .type = NLA_U8 },
};
static int nft_reject_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_reject *priv = nft_expr_priv(expr);
if (tb[NFTA_REJECT_TYPE] == NULL)
return -EINVAL;
priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
return -EINVAL;
priv->icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
case NFT_REJECT_TCP_RST:
break;
default:
return -EINVAL;
}
return 0;
}
static int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_reject *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_REJECT_TYPE, priv->type))
goto nla_put_failure;
switch (priv->type) {
case NFT_REJECT_ICMP_UNREACH:
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
goto nla_put_failure;
break;
}
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_reject_type;
static const struct nft_expr_ops nft_reject_ops = {
.type = &nft_reject_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.eval = nft_reject_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
};
static struct nft_expr_type nft_reject_type __read_mostly = {
.name = "reject",
.ops = &nft_reject_ops,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
.owner = THIS_MODULE,
};
static int __init nft_reject_module_init(void)
{
return nft_register_expr(&nft_reject_type);
}
static void __exit nft_reject_module_exit(void)
{
nft_unregister_expr(&nft_reject_type);
}
module_init(nft_reject_module_init);
module_exit(nft_reject_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_EXPR("reject");
......@@ -25,6 +25,19 @@ config NF_CONNTRACK_IPV6
To compile it as a module, choose M here. If unsure, say N.
config NF_TABLES_IPV6
depends on NF_TABLES
tristate "IPv6 nf_tables support"
config NFT_CHAIN_ROUTE_IPV6
depends on NF_TABLES_IPV6
tristate "IPv6 nf_tables route chain support"
config NFT_CHAIN_NAT_IPV6
depends on NF_TABLES_IPV6
depends on NF_NAT_IPV6 && NFT_NAT
tristate "IPv6 nf_tables nat chain support"
config IP6_NF_IPTABLES
tristate "IP6 tables support (required for filtering)"
depends on INET && IPV6
......
......@@ -23,6 +23,11 @@ obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o
# nf_tables
obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
# matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o
......
......@@ -312,7 +312,7 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
return XT_CONTINUE;
}
static unsigned int ipv6_synproxy_hook(unsigned int hooknum,
static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......
......@@ -32,13 +32,14 @@ static const struct xt_table packet_filter = {
/* The work comes in here from netfilter.c. */
static unsigned int
ip6table_filter_hook(unsigned int hook, struct sk_buff *skb,
ip6table_filter_hook(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 *))
{
const struct net *net = dev_net((in != NULL) ? in : out);
return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_filter);
return ip6t_do_table(skb, ops->hooknum, in, out,
net->ipv6.ip6table_filter);
}
static struct nf_hook_ops *filter_ops __read_mostly;
......
......@@ -76,17 +76,17 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out)
/* The work comes in here from netfilter.c. */
static unsigned int
ip6table_mangle_hook(unsigned int hook, struct sk_buff *skb,
ip6table_mangle_hook(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 (hook == NF_INET_LOCAL_OUT)
if (ops->hooknum == NF_INET_LOCAL_OUT)
return ip6t_mangle_out(skb, out);
if (hook == NF_INET_POST_ROUTING)
return ip6t_do_table(skb, hook, in, out,
if (ops->hooknum == NF_INET_POST_ROUTING)
return ip6t_do_table(skb, ops->hooknum, in, out,
dev_net(out)->ipv6.ip6table_mangle);
/* INPUT/FORWARD */
return ip6t_do_table(skb, hook, in, out,
return ip6t_do_table(skb, ops->hooknum, in, out,
dev_net(in)->ipv6.ip6table_mangle);
}
......
......@@ -63,7 +63,7 @@ static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
}
static unsigned int
nf_nat_ipv6_fn(unsigned int hooknum,
nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -72,7 +72,7 @@ nf_nat_ipv6_fn(unsigned int hooknum,
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
struct nf_conn_nat *nat;
enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
__be16 frag_off;
int hdrlen;
u8 nexthdr;
......@@ -111,7 +111,8 @@ nf_nat_ipv6_fn(unsigned int hooknum,
if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
hooknum, hdrlen))
ops->hooknum,
hdrlen))
return NF_DROP;
else
return NF_ACCEPT;
......@@ -124,14 +125,14 @@ nf_nat_ipv6_fn(unsigned int hooknum,
if (!nf_nat_initialized(ct, maniptype)) {
unsigned int ret;
ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct);
if (ret != NF_ACCEPT)
return ret;
} else {
pr_debug("Already setup manip %s for ct %p\n",
maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
goto oif_changed;
}
break;
......@@ -140,11 +141,11 @@ nf_nat_ipv6_fn(unsigned int hooknum,
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == IP_CT_ESTABLISHED_REPLY);
if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
goto oif_changed;
}
return nf_nat_packet(ct, ctinfo, hooknum, skb);
return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
oif_changed:
nf_ct_kill_acct(ct, ctinfo, skb);
......@@ -152,7 +153,7 @@ nf_nat_ipv6_fn(unsigned int hooknum,
}
static unsigned int
nf_nat_ipv6_in(unsigned int hooknum,
nf_nat_ipv6_in(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -161,7 +162,7 @@ nf_nat_ipv6_in(unsigned int hooknum,
unsigned int ret;
struct in6_addr daddr = ipv6_hdr(skb)->daddr;
ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
skb_dst_drop(skb);
......@@ -170,7 +171,7 @@ nf_nat_ipv6_in(unsigned int hooknum,
}
static unsigned int
nf_nat_ipv6_out(unsigned int hooknum,
nf_nat_ipv6_out(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -187,7 +188,7 @@ nf_nat_ipv6_out(unsigned int hooknum,
if (skb->len < sizeof(struct ipv6hdr))
return NF_ACCEPT;
ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN &&
!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
......@@ -209,7 +210,7 @@ nf_nat_ipv6_out(unsigned int hooknum,
}
static unsigned int
nf_nat_ipv6_local_fn(unsigned int hooknum,
nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -224,7 +225,7 @@ nf_nat_ipv6_local_fn(unsigned int hooknum,
if (skb->len < sizeof(struct ipv6hdr))
return NF_ACCEPT;
ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
......
......@@ -19,13 +19,14 @@ static const struct xt_table packet_raw = {
/* The work comes in here from netfilter.c. */
static unsigned int
ip6table_raw_hook(unsigned int hook, struct sk_buff *skb,
ip6table_raw_hook(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 *))
{
const struct net *net = dev_net((in != NULL) ? in : out);
return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_raw);
return ip6t_do_table(skb, ops->hooknum, in, out,
net->ipv6.ip6table_raw);
}
static struct nf_hook_ops *rawtable_ops __read_mostly;
......
......@@ -36,14 +36,15 @@ static const struct xt_table security_table = {
};
static unsigned int
ip6table_security_hook(unsigned int hook, struct sk_buff *skb,
ip6table_security_hook(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 *))
{
const struct net *net = dev_net((in != NULL) ? in : out);
return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_security);
return ip6t_do_table(skb, ops->hooknum, in, out,
net->ipv6.ip6table_security);
}
static struct nf_hook_ops *sectbl_ops __read_mostly;
......
......@@ -95,7 +95,7 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
return NF_ACCEPT;
}
static unsigned int ipv6_helper(unsigned int hooknum,
static unsigned int ipv6_helper(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -133,7 +133,7 @@ static unsigned int ipv6_helper(unsigned int hooknum,
return helper->help(skb, protoff, ct, ctinfo);
}
static unsigned int ipv6_confirm(unsigned int hooknum,
static unsigned int ipv6_confirm(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -219,16 +219,17 @@ static unsigned int __ipv6_conntrack_in(struct net *net,
return nf_conntrack_in(net, PF_INET6, hooknum, skb);
}
static unsigned int ipv6_conntrack_in(unsigned int hooknum,
static unsigned int ipv6_conntrack_in(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 *))
{
return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn);
return __ipv6_conntrack_in(dev_net(in), ops->hooknum, skb, in, out,
okfn);
}
static unsigned int ipv6_conntrack_local(unsigned int hooknum,
static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -239,7 +240,8 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum,
net_notice_ratelimited("ipv6_conntrack_local: packet too short\n");
return NF_ACCEPT;
}
return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn);
return __ipv6_conntrack_in(dev_net(out), ops->hooknum, skb, in, out,
okfn);
}
static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
......
......@@ -52,7 +52,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
}
static unsigned int ipv6_defrag(unsigned int hooknum,
static unsigned int ipv6_defrag(const struct nf_hook_ops *ops,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
......@@ -66,7 +66,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
return NF_ACCEPT;
#endif
reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(ops->hooknum, skb));
/* queued */
if (reasm == NULL)
return NF_STOLEN;
......@@ -75,7 +75,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
if (reasm == skb)
return NF_ACCEPT;
nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in,
nf_ct_frag6_output(ops->hooknum, reasm, (struct net_device *)in,
(struct net_device *)out, okfn);
return NF_STOLEN;
......
/*
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012-2013 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ipv6.h>
#include <linux/netfilter_ipv6.h>
#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,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct nft_pktinfo pkt;
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);
}
static struct nft_af_info nft_af_ipv6 __read_mostly = {
.family = NFPROTO_IPV6,
.nhooks = NF_INET_NUMHOOKS,
.owner = THIS_MODULE,
.hooks = {
[NF_INET_LOCAL_OUT] = nft_ipv6_output,
},
};
static int nf_tables_ipv6_init_net(struct net *net)
{
net->nft.ipv6 = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
if (net->nft.ipv6 == NULL)
return -ENOMEM;
memcpy(net->nft.ipv6, &nft_af_ipv6, sizeof(nft_af_ipv6));
if (nft_register_afinfo(net, net->nft.ipv6) < 0)
goto err;
return 0;
err:
kfree(net->nft.ipv6);
return -ENOMEM;
}
static void nf_tables_ipv6_exit_net(struct net *net)
{
nft_unregister_afinfo(net->nft.ipv6);
kfree(net->nft.ipv6);
}
static struct pernet_operations nf_tables_ipv6_net_ops = {
.init = nf_tables_ipv6_init_net,
.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,
.name = "filter",
.type = NFT_CHAIN_T_DEFAULT,
.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)
{
nft_register_chain_type(&filter_ipv6);
return register_pernet_subsys(&nf_tables_ipv6_net_ops);
}
static void __exit nf_tables_ipv6_exit(void)
{
unregister_pernet_subsys(&nf_tables_ipv6_net_ops);
nft_unregister_chain_type(&filter_ipv6);
}
module_init(nf_tables_ipv6_init);
module_exit(nf_tables_ipv6_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_FAMILY(AF_INET6);
/*
* Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_ipv6.h>
#include <net/netfilter/nf_nat_l3proto.h>
#include <net/ipv6.h>
/*
* IPv6 NAT chains
*/
static unsigned int nf_nat_ipv6_fn(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 *))
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_conn_nat *nat;
enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
__be16 frag_off;
int hdrlen;
u8 nexthdr;
struct nft_pktinfo pkt;
unsigned int ret;
if (ct == NULL || nf_ct_is_untracked(ct))
return NF_ACCEPT;
nat = nfct_nat(ct);
if (nat == NULL) {
/* Conntrack module was loaded late, can't add extension. */
if (nf_ct_is_confirmed(ct))
return NF_ACCEPT;
nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
if (nat == NULL)
return NF_ACCEPT;
}
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED + IP_CT_IS_REPLY:
nexthdr = ipv6_hdr(skb)->nexthdr;
hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
&nexthdr, &frag_off);
if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
ops->hooknum,
hdrlen))
return NF_DROP;
else
return NF_ACCEPT;
}
/* Fall through */
case IP_CT_NEW:
if (nf_nat_initialized(ct, maniptype))
break;
nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
ret = nft_do_chain_pktinfo(&pkt, ops);
if (ret != NF_ACCEPT)
return ret;
if (!nf_nat_initialized(ct, maniptype)) {
ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
if (ret != NF_ACCEPT)
return ret;
}
default:
break;
}
return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
}
static unsigned int nf_nat_ipv6_prerouting(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 in6_addr daddr = ipv6_hdr(skb)->daddr;
unsigned int ret;
ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
skb_dst_drop(skb);
return ret;
}
static unsigned int nf_nat_ipv6_postrouting(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 *))
{
enum ip_conntrack_info ctinfo __maybe_unused;
const struct nf_conn *ct __maybe_unused;
unsigned int ret;
ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
#ifdef CONFIG_XFRM
if (ret != NF_DROP && ret != NF_STOLEN &&
!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3) ||
(ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all))
if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
ret = NF_DROP;
}
#endif
return ret;
}
static unsigned int nf_nat_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 *))
{
enum ip_conntrack_info ctinfo;
const struct nf_conn *ct;
unsigned int ret;
ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
if (ret != NF_DROP && ret != NF_STOLEN &&
(ct = nf_ct_get(skb, &ctinfo)) != NULL) {
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
&ct->tuplehash[!dir].tuple.src.u3)) {
if (ip6_route_me_harder(skb))
ret = NF_DROP;
}
#ifdef CONFIG_XFRM
else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
ct->tuplehash[dir].tuple.dst.u.all !=
ct->tuplehash[!dir].tuple.src.u.all)
if (nf_xfrm_me_harder(skb, AF_INET6))
ret = NF_DROP;
#endif
}
return ret;
}
static struct nf_chain_type nft_chain_nat_ipv6 = {
.family = NFPROTO_IPV6,
.name = "nat",
.type = NFT_CHAIN_T_NAT,
.hook_mask = (1 << NF_INET_PRE_ROUTING) |
(1 << NF_INET_POST_ROUTING) |
(1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_LOCAL_IN),
.fn = {
[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)
{
int err;
err = nft_register_chain_type(&nft_chain_nat_ipv6);
if (err < 0)
return err;
return 0;
}
static void __exit nft_chain_nat_ipv6_exit(void)
{
nft_unregister_chain_type(&nft_chain_nat_ipv6);
}
module_init(nft_chain_nat_ipv6_init);
module_exit(nft_chain_nat_ipv6_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat");
/*
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
* Copyright (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_ipv6.h>
#include <net/route.h>
static unsigned int nf_route_table_hook(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 *))
{
unsigned int ret;
struct nft_pktinfo pkt;
struct in6_addr saddr, daddr;
u_int8_t hop_limit;
u32 mark, flowlabel;
/* malformed packet, drop it */
if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0)
return NF_DROP;
/* save source/dest address, mark, hoplimit, flowlabel, priority */
memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr));
memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr));
mark = skb->mark;
hop_limit = ipv6_hdr(skb)->hop_limit;
/* flowlabel and prio (includes version, which shouldn't change either */
flowlabel = *((u32 *)ipv6_hdr(skb));
ret = nft_do_chain_pktinfo(&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)) ||
skb->mark != mark ||
ipv6_hdr(skb)->hop_limit != hop_limit ||
flowlabel != *((u_int32_t *)ipv6_hdr(skb))))
return ip6_route_me_harder(skb) == 0 ? ret : NF_DROP;
return ret;
}
static struct nf_chain_type nft_chain_route_ipv6 = {
.family = NFPROTO_IPV6,
.name = "route",
.type = NFT_CHAIN_T_ROUTE,
.hook_mask = (1 << NF_INET_LOCAL_OUT),
.fn = {
[NF_INET_LOCAL_OUT] = nf_route_table_hook,
},
.me = THIS_MODULE,
};
static int __init nft_chain_route_init(void)
{
return nft_register_chain_type(&nft_chain_route_ipv6);
}
static void __exit nft_chain_route_exit(void)
{
nft_unregister_chain_type(&nft_chain_route_ipv6);
}
module_init(nft_chain_route_init);
module_exit(nft_chain_route_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_CHAIN(AF_INET6, "route");
......@@ -413,6 +413,58 @@ config NETFILTER_SYNPROXY
endif # NF_CONNTRACK
config NF_TABLES
depends on NETFILTER_NETLINK
tristate "Netfilter nf_tables support"
config NFT_EXTHDR
depends on NF_TABLES
tristate "Netfilter nf_tables IPv6 exthdr module"
config NFT_META
depends on NF_TABLES
tristate "Netfilter nf_tables meta module"
config NFT_CT
depends on NF_TABLES
depends on NF_CONNTRACK
tristate "Netfilter nf_tables conntrack module"
config NFT_RBTREE
depends on NF_TABLES
tristate "Netfilter nf_tables rbtree set module"
config NFT_HASH
depends on NF_TABLES
tristate "Netfilter nf_tables hash set module"
config NFT_COUNTER
depends on NF_TABLES
tristate "Netfilter nf_tables counter module"
config NFT_LOG
depends on NF_TABLES
tristate "Netfilter nf_tables log module"
config NFT_LIMIT
depends on NF_TABLES
tristate "Netfilter nf_tables limit module"
config NFT_NAT
depends on NF_TABLES
depends on NF_CONNTRACK
depends on NF_NAT
tristate "Netfilter nf_tables nat module"
config NFT_COMPAT
depends on NF_TABLES
depends on NETFILTER_XTABLES
tristate "Netfilter x_tables over nf_tables module"
help
This is required if you intend to use any of existing
x_tables match/target extensions over the nf_tables
framework.
config NETFILTER_XTABLES
tristate "Netfilter Xtables support (required for ip_tables)"
default m if NETFILTER_ADVANCED=n
......
......@@ -64,6 +64,24 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o
# SYNPROXY
obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o
# nf_tables
nf_tables-objs += nf_tables_core.o nf_tables_api.o
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_NFT_COMPAT) += nft_compat.o
obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o
obj-$(CONFIG_NFT_META) += nft_meta.o
obj-$(CONFIG_NFT_CT) += nft_ct.o
obj-$(CONFIG_NFT_LIMIT) += nft_limit.o
obj-$(CONFIG_NFT_NAT) += nft_nat.o
#nf_tables-objs += nft_meta_target.o
obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o
obj-$(CONFIG_NFT_HASH) += nft_hash.o
obj-$(CONFIG_NFT_COUNTER) += nft_counter.o
obj-$(CONFIG_NFT_LOG) += nft_log.o
# generic X tables
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
......
......@@ -146,7 +146,7 @@ unsigned int nf_iterate(struct list_head *head,
/* Optimization: we don't need to hold module
reference here, since function can't sleep. --RR */
repeat:
verdict = (*elemp)->hook(hook, skb, indev, outdev, okfn);
verdict = (*elemp)->hook(*elemp, skb, indev, outdev, okfn);
if (verdict != NF_ACCEPT) {
#ifdef CONFIG_NETFILTER_DEBUG
if (unlikely((verdict & NF_VERDICT_MASK)
......
......@@ -1239,11 +1239,11 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
* Check if packet is reply for established ip_vs_conn.
*/
static unsigned int
ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb,
ip_vs_reply4(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 *))
{
return ip_vs_out(hooknum, skb, AF_INET);
return ip_vs_out(ops->hooknum, skb, AF_INET);
}
/*
......@@ -1251,11 +1251,11 @@ ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb,
* Check if packet is reply for established ip_vs_conn.
*/
static unsigned int
ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb,
ip_vs_local_reply4(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 *))
{
return ip_vs_out(hooknum, skb, AF_INET);
return ip_vs_out(ops->hooknum, skb, AF_INET);
}
#ifdef CONFIG_IP_VS_IPV6
......@@ -1266,11 +1266,11 @@ ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb,
* Check if packet is reply for established ip_vs_conn.
*/
static unsigned int
ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb,
ip_vs_reply6(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 *))
{
return ip_vs_out(hooknum, skb, AF_INET6);
return ip_vs_out(ops->hooknum, skb, AF_INET6);
}
/*
......@@ -1278,11 +1278,11 @@ ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb,
* Check if packet is reply for established ip_vs_conn.
*/
static unsigned int
ip_vs_local_reply6(unsigned int hooknum, struct sk_buff *skb,
ip_vs_local_reply6(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 *))
{
return ip_vs_out(hooknum, skb, AF_INET6);
return ip_vs_out(ops->hooknum, skb, AF_INET6);
}
#endif
......@@ -1733,12 +1733,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
* Schedule and forward packets from remote clients
*/
static unsigned int
ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb,
ip_vs_remote_request4(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 *))
{
return ip_vs_in(hooknum, skb, AF_INET);
return ip_vs_in(ops->hooknum, skb, AF_INET);
}
/*
......@@ -1746,11 +1746,11 @@ ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb,
* Schedule and forward packets from local clients
*/
static unsigned int
ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb,
ip_vs_local_request4(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 *))
{
return ip_vs_in(hooknum, skb, AF_INET);
return ip_vs_in(ops->hooknum, skb, AF_INET);
}
#ifdef CONFIG_IP_VS_IPV6
......@@ -1760,7 +1760,7 @@ ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb,
* Copy info from first fragment, to the rest of them.
*/
static unsigned int
ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb,
ip_vs_preroute_frag6(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 *))
......@@ -1792,12 +1792,12 @@ ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb,
* Schedule and forward packets from remote clients
*/
static unsigned int
ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb,
ip_vs_remote_request6(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 *))
{
return ip_vs_in(hooknum, skb, AF_INET6);
return ip_vs_in(ops->hooknum, skb, AF_INET6);
}
/*
......@@ -1805,11 +1805,11 @@ ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb,
* Schedule and forward packets from local clients
*/
static unsigned int
ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb,
ip_vs_local_request6(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 *))
{
return ip_vs_in(hooknum, skb, AF_INET6);
return ip_vs_in(ops->hooknum, skb, AF_INET6);
}
#endif
......@@ -1825,7 +1825,7 @@ ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb,
* and send them to ip_vs_in_icmp.
*/
static unsigned int
ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb,
ip_vs_forward_icmp(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 *))
{
......@@ -1842,12 +1842,12 @@ ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb,
if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
return NF_ACCEPT;
return ip_vs_in_icmp(skb, &r, hooknum);
return ip_vs_in_icmp(skb, &r, ops->hooknum);
}
#ifdef CONFIG_IP_VS_IPV6
static unsigned int
ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
ip_vs_forward_icmp_v6(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 *))
{
......@@ -1866,7 +1866,7 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable))
return NF_ACCEPT;
return ip_vs_in_icmp_v6(skb, &r, hooknum, &iphdr);
return ip_vs_in_icmp_v6(skb, &r, ops->hooknum, &iphdr);
}
#endif
......
......@@ -432,6 +432,26 @@ nf_nat_setup_info(struct nf_conn *ct,
}
EXPORT_SYMBOL(nf_nat_setup_info);
unsigned int
nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
{
/* Force range to this IP; let proto decide mapping for
* per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
* Use reply in case it's already been mangled (eg local packet).
*/
union nf_inet_addr ip =
(HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3 :
ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3);
struct nf_nat_range range = {
.flags = NF_NAT_RANGE_MAP_IPS,
.min_addr = ip,
.max_addr = ip,
};
return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
}
EXPORT_SYMBOL_GPL(nf_nat_alloc_null_binding);
/* Do packet manipulations according to nf_nat_setup_info. */
unsigned int nf_nat_packet(struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
......
This diff is collapsed.
/*
* Copyright (c) 2008 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_log.h>
static void nft_cmp_fast_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1])
{
const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
u32 mask;
mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - priv->len);
if ((data[priv->sreg].data[0] & mask) == priv->data)
return;
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}
static bool nft_payload_fast_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_payload *priv = nft_expr_priv(expr);
const struct sk_buff *skb = pkt->skb;
struct nft_data *dest = &data[priv->dreg];
unsigned char *ptr;
if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
ptr = skb_network_header(skb);
else
ptr = skb_network_header(skb) + pkt->xt.thoff;
ptr += priv->offset;
if (unlikely(ptr + priv->len >= skb_tail_pointer(skb)))
return false;
if (priv->len == 2)
*(u16 *)dest->data = *(u16 *)ptr;
else if (priv->len == 4)
*(u32 *)dest->data = *(u32 *)ptr;
else
*(u8 *)dest->data = *(u8 *)ptr;
return true;
}
struct nft_jumpstack {
const struct nft_chain *chain;
const struct nft_rule *rule;
int rulenum;
};
static inline void
nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt,
struct nft_jumpstack *jumpstack, unsigned int stackptr)
{
struct nft_stats __percpu *stats;
const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this;
rcu_read_lock_bh();
stats = rcu_dereference(nft_base_chain(chain)->stats);
__this_cpu_inc(stats->pkts);
__this_cpu_add(stats->bytes, pkt->skb->len);
rcu_read_unlock_bh();
}
enum nft_trace {
NFT_TRACE_RULE,
NFT_TRACE_RETURN,
NFT_TRACE_POLICY,
};
static const char *const comments[] = {
[NFT_TRACE_RULE] = "rule",
[NFT_TRACE_RETURN] = "return",
[NFT_TRACE_POLICY] = "policy",
};
static struct nf_loginfo trace_loginfo = {
.type = NF_LOG_TYPE_LOG,
.u = {
.log = {
.level = 4,
.logflags = NF_LOG_MASK,
},
},
};
static inline void nft_trace_packet(const struct nft_pktinfo *pkt,
const struct nft_chain *chain,
int rulenum, enum nft_trace type)
{
struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
nf_log_packet(net, pkt->xt.family, pkt->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)
{
const struct nft_chain *chain = ops->priv;
const struct nft_rule *rule;
const struct nft_expr *expr, *last;
struct nft_data data[NFT_REG_MAX + 1];
unsigned int stackptr = 0;
struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];
int rulenum = 0;
/*
* Cache cursor to avoid problems in case that the cursor is updated
* while traversing the ruleset.
*/
unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor);
do_chain:
rule = list_entry(&chain->rules, struct nft_rule, list);
next_rule:
data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
list_for_each_entry_continue_rcu(rule, &chain->rules, list) {
/* This rule is not active, skip. */
if (unlikely(rule->genmask & (1 << gencursor)))
continue;
rulenum++;
nft_rule_for_each_expr(expr, last, rule) {
if (expr->ops == &nft_cmp_fast_ops)
nft_cmp_fast_eval(expr, data);
else if (expr->ops != &nft_payload_fast_ops ||
!nft_payload_fast_eval(expr, data, pkt))
expr->ops->eval(expr, data, pkt);
if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)
break;
}
switch (data[NFT_REG_VERDICT].verdict) {
case NFT_BREAK:
data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
/* fall through */
case NFT_CONTINUE:
continue;
}
break;
}
switch (data[NFT_REG_VERDICT].verdict) {
case NF_ACCEPT:
case NF_DROP:
case NF_QUEUE:
if (unlikely(pkt->skb->nf_trace))
nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
return data[NFT_REG_VERDICT].verdict;
case NFT_JUMP:
if (unlikely(pkt->skb->nf_trace))
nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);
BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);
jumpstack[stackptr].chain = chain;
jumpstack[stackptr].rule = rule;
jumpstack[stackptr].rulenum = rulenum;
stackptr++;
/* fall through */
case NFT_GOTO:
chain = data[NFT_REG_VERDICT].chain;
goto do_chain;
case NFT_RETURN:
if (unlikely(pkt->skb->nf_trace))
nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);
/* fall through */
case NFT_CONTINUE:
break;
default:
WARN_ON(1);
}
if (stackptr > 0) {
if (unlikely(pkt->skb->nf_trace))
nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN);
stackptr--;
chain = jumpstack[stackptr].chain;
rule = jumpstack[stackptr].rule;
rulenum = jumpstack[stackptr].rulenum;
goto next_rule;
}
nft_chain_stats(chain, pkt, jumpstack, stackptr);
if (unlikely(pkt->skb->nf_trace))
nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY);
return nft_base_chain(chain)->policy;
}
EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo);
int __init nf_tables_core_module_init(void)
{
int err;
err = nft_immediate_module_init();
if (err < 0)
goto err1;
err = nft_cmp_module_init();
if (err < 0)
goto err2;
err = nft_lookup_module_init();
if (err < 0)
goto err3;
err = nft_bitwise_module_init();
if (err < 0)
goto err4;
err = nft_byteorder_module_init();
if (err < 0)
goto err5;
err = nft_payload_module_init();
if (err < 0)
goto err6;
return 0;
err6:
nft_byteorder_module_exit();
err5:
nft_bitwise_module_exit();
err4:
nft_lookup_module_exit();
err3:
nft_cmp_module_exit();
err2:
nft_immediate_module_exit();
err1:
return err;
}
void nf_tables_core_module_exit(void)
{
nft_payload_module_exit();
nft_byteorder_module_exit();
nft_bitwise_module_exit();
nft_lookup_module_exit();
nft_cmp_module_exit();
nft_immediate_module_exit();
}
......@@ -147,9 +147,6 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
const struct nfnetlink_subsystem *ss;
int type, err;
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
/* All the messages must at least contain nfgenmsg */
if (nlmsg_len(nlh) < sizeof(struct nfgenmsg))
return 0;
......@@ -217,9 +214,179 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
}
}
static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
u_int16_t subsys_id)
{
struct sk_buff *nskb, *oskb = skb;
struct net *net = sock_net(skb->sk);
const struct nfnetlink_subsystem *ss;
const struct nfnl_callback *nc;
bool success = true, done = false;
int err;
if (subsys_id >= NFNL_SUBSYS_COUNT)
return netlink_ack(skb, nlh, -EINVAL);
replay:
nskb = netlink_skb_clone(oskb, GFP_KERNEL);
if (!nskb)
return netlink_ack(oskb, nlh, -ENOMEM);
nskb->sk = oskb->sk;
skb = nskb;
nfnl_lock(subsys_id);
ss = rcu_dereference_protected(table[subsys_id].subsys,
lockdep_is_held(&table[subsys_id].mutex));
if (!ss) {
#ifdef CONFIG_MODULES
nfnl_unlock(subsys_id);
request_module("nfnetlink-subsys-%d", subsys_id);
nfnl_lock(subsys_id);
ss = rcu_dereference_protected(table[subsys_id].subsys,
lockdep_is_held(&table[subsys_id].mutex));
if (!ss)
#endif
{
nfnl_unlock(subsys_id);
kfree_skb(nskb);
return netlink_ack(skb, nlh, -EOPNOTSUPP);
}
}
if (!ss->commit || !ss->abort) {
nfnl_unlock(subsys_id);
kfree_skb(nskb);
return netlink_ack(skb, nlh, -EOPNOTSUPP);
}
while (skb->len >= nlmsg_total_size(0)) {
int msglen, type;
nlh = nlmsg_hdr(skb);
err = 0;
if (nlh->nlmsg_len < NLMSG_HDRLEN) {
err = -EINVAL;
goto ack;
}
/* Only requests are handled by the kernel */
if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) {
err = -EINVAL;
goto ack;
}
type = nlh->nlmsg_type;
if (type == NFNL_MSG_BATCH_BEGIN) {
/* Malformed: Batch begin twice */
success = false;
goto done;
} else if (type == NFNL_MSG_BATCH_END) {
done = true;
goto done;
} else if (type < NLMSG_MIN_TYPE) {
err = -EINVAL;
goto ack;
}
/* We only accept a batch with messages for the same
* subsystem.
*/
if (NFNL_SUBSYS_ID(type) != subsys_id) {
err = -EINVAL;
goto ack;
}
nc = nfnetlink_find_client(type, ss);
if (!nc) {
err = -EINVAL;
goto ack;
}
{
int min_len = nlmsg_total_size(sizeof(struct nfgenmsg));
u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);
struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
struct nlattr *attr = (void *)nlh + min_len;
int attrlen = nlh->nlmsg_len - min_len;
err = nla_parse(cda, ss->cb[cb_id].attr_count,
attr, attrlen, ss->cb[cb_id].policy);
if (err < 0)
goto ack;
if (nc->call_batch) {
err = nc->call_batch(net->nfnl, skb, nlh,
(const struct nlattr **)cda);
}
/* The lock was released to autoload some module, we
* have to abort and start from scratch using the
* original skb.
*/
if (err == -EAGAIN) {
ss->abort(skb);
nfnl_unlock(subsys_id);
kfree_skb(nskb);
goto replay;
}
}
ack:
if (nlh->nlmsg_flags & NLM_F_ACK || err) {
/* We don't stop processing the batch on errors, thus,
* userspace gets all the errors that the batch
* triggers.
*/
netlink_ack(skb, nlh, err);
if (err)
success = false;
}
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
skb_pull(skb, msglen);
}
done:
if (success && done)
ss->commit(skb);
else
ss->abort(skb);
nfnl_unlock(subsys_id);
kfree_skb(nskb);
}
static void nfnetlink_rcv(struct sk_buff *skb)
{
netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
struct nlmsghdr *nlh = nlmsg_hdr(skb);
struct net *net = sock_net(skb->sk);
int msglen;
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return netlink_ack(skb, nlh, -EPERM);
if (nlh->nlmsg_len < NLMSG_HDRLEN ||
skb->len < nlh->nlmsg_len)
return;
if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) {
struct nfgenmsg *nfgenmsg;
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
msglen = skb->len;
if (nlh->nlmsg_len < NLMSG_HDRLEN ||
skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg))
return;
nfgenmsg = nlmsg_data(nlh);
skb_pull(skb, msglen);
nfnetlink_rcv_batch(skb, nlh, nfgenmsg->res_id);
} else {
netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
}
}
#ifdef CONFIG_MODULES
......
/*
* Copyright (c) 2008-2009 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#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_core.h>
#include <net/netfilter/nf_tables.h>
struct nft_bitwise {
enum nft_registers sreg:8;
enum nft_registers dreg:8;
u8 len;
struct nft_data mask;
struct nft_data xor;
};
static void nft_bitwise_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_bitwise *priv = nft_expr_priv(expr);
const struct nft_data *src = &data[priv->sreg];
struct nft_data *dst = &data[priv->dreg];
unsigned int i;
for (i = 0; i < DIV_ROUND_UP(priv->len, 4); i++) {
dst->data[i] = (src->data[i] & priv->mask.data[i]) ^
priv->xor.data[i];
}
}
static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
[NFTA_BITWISE_SREG] = { .type = NLA_U32 },
[NFTA_BITWISE_DREG] = { .type = NLA_U32 },
[NFTA_BITWISE_LEN] = { .type = NLA_U32 },
[NFTA_BITWISE_MASK] = { .type = NLA_NESTED },
[NFTA_BITWISE_XOR] = { .type = NLA_NESTED },
};
static int nft_bitwise_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_bitwise *priv = nft_expr_priv(expr);
struct nft_data_desc d1, d2;
int err;
if (tb[NFTA_BITWISE_SREG] == NULL ||
tb[NFTA_BITWISE_DREG] == NULL ||
tb[NFTA_BITWISE_LEN] == NULL ||
tb[NFTA_BITWISE_MASK] == NULL ||
tb[NFTA_BITWISE_XOR] == NULL)
return -EINVAL;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
return err;
priv->dreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
if (err < 0)
return err;
priv->len = ntohl(nla_get_be32(tb[NFTA_BITWISE_LEN]));
err = nft_data_init(NULL, &priv->mask, &d1, tb[NFTA_BITWISE_MASK]);
if (err < 0)
return err;
if (d1.len != priv->len)
return -EINVAL;
err = nft_data_init(NULL, &priv->xor, &d2, tb[NFTA_BITWISE_XOR]);
if (err < 0)
return err;
if (d2.len != priv->len)
return -EINVAL;
return 0;
}
static int nft_bitwise_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_bitwise *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_BITWISE_SREG, htonl(priv->sreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BITWISE_DREG, htonl(priv->dreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BITWISE_LEN, htonl(priv->len)))
goto nla_put_failure;
if (nft_data_dump(skb, NFTA_BITWISE_MASK, &priv->mask,
NFT_DATA_VALUE, priv->len) < 0)
goto nla_put_failure;
if (nft_data_dump(skb, NFTA_BITWISE_XOR, &priv->xor,
NFT_DATA_VALUE, priv->len) < 0)
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_bitwise_type;
static const struct nft_expr_ops nft_bitwise_ops = {
.type = &nft_bitwise_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)),
.eval = nft_bitwise_eval,
.init = nft_bitwise_init,
.dump = nft_bitwise_dump,
};
static struct nft_expr_type nft_bitwise_type __read_mostly = {
.name = "bitwise",
.ops = &nft_bitwise_ops,
.policy = nft_bitwise_policy,
.maxattr = NFTA_BITWISE_MAX,
.owner = THIS_MODULE,
};
int __init nft_bitwise_module_init(void)
{
return nft_register_expr(&nft_bitwise_type);
}
void nft_bitwise_module_exit(void)
{
nft_unregister_expr(&nft_bitwise_type);
}
/*
* Copyright (c) 2008-2009 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#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_core.h>
#include <net/netfilter/nf_tables.h>
struct nft_byteorder {
enum nft_registers sreg:8;
enum nft_registers dreg:8;
enum nft_byteorder_ops op:8;
u8 len;
u8 size;
};
static void nft_byteorder_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_byteorder *priv = nft_expr_priv(expr);
struct nft_data *src = &data[priv->sreg], *dst = &data[priv->dreg];
union { u32 u32; u16 u16; } *s, *d;
unsigned int i;
s = (void *)src->data;
d = (void *)dst->data;
switch (priv->size) {
case 4:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 4; i++)
d[i].u32 = ntohl((__force __be32)s[i].u32);
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 4; i++)
d[i].u32 = (__force __u32)htonl(s[i].u32);
break;
}
break;
case 2:
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
for (i = 0; i < priv->len / 2; i++)
d[i].u16 = ntohs((__force __be16)s[i].u16);
break;
case NFT_BYTEORDER_HTON:
for (i = 0; i < priv->len / 2; i++)
d[i].u16 = (__force __u16)htons(s[i].u16);
break;
}
break;
}
}
static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = {
[NFTA_BYTEORDER_SREG] = { .type = NLA_U32 },
[NFTA_BYTEORDER_DREG] = { .type = NLA_U32 },
[NFTA_BYTEORDER_OP] = { .type = NLA_U32 },
[NFTA_BYTEORDER_LEN] = { .type = NLA_U32 },
[NFTA_BYTEORDER_SIZE] = { .type = NLA_U32 },
};
static int nft_byteorder_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_byteorder *priv = nft_expr_priv(expr);
int err;
if (tb[NFTA_BYTEORDER_SREG] == NULL ||
tb[NFTA_BYTEORDER_DREG] == NULL ||
tb[NFTA_BYTEORDER_LEN] == NULL ||
tb[NFTA_BYTEORDER_SIZE] == NULL ||
tb[NFTA_BYTEORDER_OP] == NULL)
return -EINVAL;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
return err;
priv->dreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
if (err < 0)
return err;
priv->op = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_OP]));
switch (priv->op) {
case NFT_BYTEORDER_NTOH:
case NFT_BYTEORDER_HTON:
break;
default:
return -EINVAL;
}
priv->len = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_LEN]));
if (priv->len == 0 || priv->len > FIELD_SIZEOF(struct nft_data, data))
return -EINVAL;
priv->size = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SIZE]));
switch (priv->size) {
case 2:
case 4:
break;
default:
return -EINVAL;
}
return 0;
}
static int nft_byteorder_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_byteorder *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_BYTEORDER_SREG, htonl(priv->sreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_DREG, htonl(priv->dreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_byteorder_type;
static const struct nft_expr_ops nft_byteorder_ops = {
.type = &nft_byteorder_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)),
.eval = nft_byteorder_eval,
.init = nft_byteorder_init,
.dump = nft_byteorder_dump,
};
static struct nft_expr_type nft_byteorder_type __read_mostly = {
.name = "byteorder",
.ops = &nft_byteorder_ops,
.policy = nft_byteorder_policy,
.maxattr = NFTA_BYTEORDER_MAX,
.owner = THIS_MODULE,
};
int __init nft_byteorder_module_init(void)
{
return nft_register_expr(&nft_byteorder_type);
}
void nft_byteorder_module_exit(void)
{
nft_unregister_expr(&nft_byteorder_type);
}
/*
* Copyright (c) 2008-2009 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#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_core.h>
#include <net/netfilter/nf_tables.h>
struct nft_cmp_expr {
struct nft_data data;
enum nft_registers sreg:8;
u8 len;
enum nft_cmp_ops op:8;
};
static void nft_cmp_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_cmp_expr *priv = nft_expr_priv(expr);
int d;
d = nft_data_cmp(&data[priv->sreg], &priv->data, priv->len);
switch (priv->op) {
case NFT_CMP_EQ:
if (d != 0)
goto mismatch;
break;
case NFT_CMP_NEQ:
if (d == 0)
goto mismatch;
break;
case NFT_CMP_LT:
if (d == 0)
goto mismatch;
case NFT_CMP_LTE:
if (d > 0)
goto mismatch;
break;
case NFT_CMP_GT:
if (d == 0)
goto mismatch;
case NFT_CMP_GTE:
if (d < 0)
goto mismatch;
break;
}
return;
mismatch:
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}
static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = {
[NFTA_CMP_SREG] = { .type = NLA_U32 },
[NFTA_CMP_OP] = { .type = NLA_U32 },
[NFTA_CMP_DATA] = { .type = NLA_NESTED },
};
static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_cmp_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
int err;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
BUG_ON(err < 0);
priv->len = desc.len;
return 0;
}
static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_cmp_expr *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op)))
goto nla_put_failure;
if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data,
NFT_DATA_VALUE, priv->len) < 0)
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_cmp_type;
static const struct nft_expr_ops nft_cmp_ops = {
.type = &nft_cmp_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)),
.eval = nft_cmp_eval,
.init = nft_cmp_init,
.dump = nft_cmp_dump,
};
static int nft_cmp_fast_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
struct nft_data data;
u32 mask;
int err;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
BUG_ON(err < 0);
desc.len *= BITS_PER_BYTE;
mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - desc.len);
priv->data = data.data[0] & mask;
priv->len = desc.len;
return 0;
}
static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
struct nft_data data;
if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ)))
goto nla_put_failure;
data.data[0] = priv->data;
if (nft_data_dump(skb, NFTA_CMP_DATA, &data,
NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0)
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
const struct nft_expr_ops nft_cmp_fast_ops = {
.type = &nft_cmp_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)),
.eval = NULL, /* inlined */
.init = nft_cmp_fast_init,
.dump = nft_cmp_fast_dump,
};
static const struct nft_expr_ops *
nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
{
struct nft_data_desc desc;
struct nft_data data;
enum nft_registers sreg;
enum nft_cmp_ops op;
int err;
if (tb[NFTA_CMP_SREG] == NULL ||
tb[NFTA_CMP_OP] == NULL ||
tb[NFTA_CMP_DATA] == NULL)
return ERR_PTR(-EINVAL);
sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
err = nft_validate_input_register(sreg);
if (err < 0)
return ERR_PTR(err);
op = ntohl(nla_get_be32(tb[NFTA_CMP_OP]));
switch (op) {
case NFT_CMP_EQ:
case NFT_CMP_NEQ:
case NFT_CMP_LT:
case NFT_CMP_LTE:
case NFT_CMP_GT:
case NFT_CMP_GTE:
break;
default:
return ERR_PTR(-EINVAL);
}
err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
if (err < 0)
return ERR_PTR(err);
if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ)
return &nft_cmp_fast_ops;
else
return &nft_cmp_ops;
}
static struct nft_expr_type nft_cmp_type __read_mostly = {
.name = "cmp",
.select_ops = nft_cmp_select_ops,
.policy = nft_cmp_policy,
.maxattr = NFTA_CMP_MAX,
.owner = THIS_MODULE,
};
int __init nft_cmp_module_init(void)
{
return nft_register_expr(&nft_cmp_type);
}
void nft_cmp_module_exit(void)
{
nft_unregister_expr(&nft_cmp_type);
}
This diff is collapsed.
/*
* Copyright (c) 2008-2009 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/seqlock.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
struct nft_counter {
seqlock_t lock;
u64 bytes;
u64 packets;
};
static void nft_counter_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_counter *priv = nft_expr_priv(expr);
write_seqlock_bh(&priv->lock);
priv->bytes += pkt->skb->len;
priv->packets++;
write_sequnlock_bh(&priv->lock);
}
static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
struct nft_counter *priv = nft_expr_priv(expr);
unsigned int seq;
u64 bytes;
u64 packets;
do {
seq = read_seqbegin(&priv->lock);
bytes = priv->bytes;
packets = priv->packets;
} while (read_seqretry(&priv->lock, seq));
if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(bytes)))
goto nla_put_failure;
if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(packets)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
};
static int nft_counter_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_counter *priv = nft_expr_priv(expr);
if (tb[NFTA_COUNTER_PACKETS])
priv->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
if (tb[NFTA_COUNTER_BYTES])
priv->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
seqlock_init(&priv->lock);
return 0;
}
static struct nft_expr_type nft_counter_type;
static const struct nft_expr_ops nft_counter_ops = {
.type = &nft_counter_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_counter)),
.eval = nft_counter_eval,
.init = nft_counter_init,
.dump = nft_counter_dump,
};
static struct nft_expr_type nft_counter_type __read_mostly = {
.name = "counter",
.ops = &nft_counter_ops,
.policy = nft_counter_policy,
.maxattr = NFTA_COUNTER_MAX,
.owner = THIS_MODULE,
};
static int __init nft_counter_module_init(void)
{
return nft_register_expr(&nft_counter_type);
}
static void __exit nft_counter_module_exit(void)
{
nft_unregister_expr(&nft_counter_type);
}
module_init(nft_counter_module_init);
module_exit(nft_counter_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_EXPR("counter");
This diff is collapsed.
/*
* Copyright (c) 2008-2009 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
struct nft_template {
};
static void nft_template_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_template *priv = nft_expr_priv(expr);
}
static const struct nla_policy nft_template_policy[NFTA_TEMPLATE_MAX + 1] = {
[NFTA_TEMPLATE_ATTR] = { .type = NLA_U32 },
};
static int nft_template_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_template *priv = nft_expr_priv(expr);
return 0;
}
static void nft_template_destroy(const struct nft_ctx *ctx,
const struct nft_expr *expr)
{
struct nft_template *priv = nft_expr_priv(expr);
}
static int nft_template_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_template *priv = nft_expr_priv(expr);
NLA_PUT_BE32(skb, NFTA_TEMPLATE_ATTR, priv->field);
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_template_type;
static const struct nft_expr_ops nft_template_ops = {
.type = &nft_template_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_template)),
.eval = nft_template_eval,
.init = nft_template_init,
.destroy = nft_template_destroy,
.dump = nft_template_dump,
};
static struct nft_expr_type nft_template_type __read_mostly = {
.name = "template",
.ops = &nft_template_ops,
.policy = nft_template_policy,
.maxattr = NFTA_TEMPLATE_MAX,
.owner = THIS_MODULE,
};
static int __init nft_template_module_init(void)
{
return nft_register_expr(&nft_template_type);
}
static void __exit nft_template_module_exit(void)
{
nft_unregister_expr(&nft_template_type);
}
module_init(nft_template_module_init);
module_exit(nft_template_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_EXPR("template");
/*
* Copyright (c) 2008 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.
*
* Development of this code funded by Astaro AG (http://www.astaro.com/)
*/
#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>
// FIXME:
#include <net/ipv6.h>
struct nft_exthdr {
u8 type;
u8 offset;
u8 len;
enum nft_registers dreg:8;
};
static void nft_exthdr_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
struct nft_exthdr *priv = nft_expr_priv(expr);
struct nft_data *dest = &data[priv->dreg];
unsigned int offset;
int err;
err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
if (err < 0)
goto err;
offset += priv->offset;
if (skb_copy_bits(pkt->skb, offset, dest->data, priv->len) < 0)
goto err;
return;
err:
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}
static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
[NFTA_EXTHDR_DREG] = { .type = NLA_U32 },
[NFTA_EXTHDR_TYPE] = { .type = NLA_U8 },
[NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 },
[NFTA_EXTHDR_LEN] = { .type = NLA_U32 },
};
static int nft_exthdr_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_exthdr *priv = nft_expr_priv(expr);
int err;
if (tb[NFTA_EXTHDR_DREG] == NULL ||
tb[NFTA_EXTHDR_TYPE] == NULL ||
tb[NFTA_EXTHDR_OFFSET] == NULL ||
tb[NFTA_EXTHDR_LEN] == NULL)
return -EINVAL;
priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
priv->offset = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OFFSET]));
priv->len = ntohl(nla_get_be32(tb[NFTA_EXTHDR_LEN]));
if (priv->len == 0 ||
priv->len > FIELD_SIZEOF(struct nft_data, data))
return -EINVAL;
priv->dreg = ntohl(nla_get_be32(tb[NFTA_EXTHDR_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
}
static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_exthdr *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_EXTHDR_DREG, htonl(priv->dreg)))
goto nla_put_failure;
if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_exthdr_type;
static const struct nft_expr_ops nft_exthdr_ops = {
.type = &nft_exthdr_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
.eval = nft_exthdr_eval,
.init = nft_exthdr_init,
.dump = nft_exthdr_dump,
};
static struct nft_expr_type nft_exthdr_type __read_mostly = {
.name = "exthdr",
.ops = &nft_exthdr_ops,
.policy = nft_exthdr_policy,
.maxattr = NFTA_EXTHDR_MAX,
.owner = THIS_MODULE,
};
static int __init nft_exthdr_module_init(void)
{
return nft_register_expr(&nft_exthdr_type);
}
static void __exit nft_exthdr_module_exit(void)
{
nft_unregister_expr(&nft_exthdr_type);
}
module_init(nft_exthdr_module_init);
module_exit(nft_exthdr_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_EXPR("exthdr");
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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