netfilter: add nftables

This patch adds nftables which is the intended successor of iptables.
This packet filtering framework reuses the existing netfilter hooks,
the connection tracking system, the NAT subsystem, the transparent
proxying engine, the logging infrastructure and the userspace packet
queueing facilities.

In a nutshell, nftables provides a pseudo-state machine with 4 general
purpose registers of 128 bits and 1 specific purpose register to store
verdicts. This pseudo-machine comes with an extensible instruction set,
a.k.a. "expressions" in the nftables jargon. The expressions included
in this patch provide the basic functionality, they are:

* bitwise: to perform bitwise operations.
* byteorder: to change from host/network endianess.
* cmp: to compare data with the content of the registers.
* counter: to enable counters on rules.
* ct: to store conntrack keys into register.
* exthdr: to match IPv6 extension headers.
* immediate: to load data into registers.
* limit: to limit matching based on packet rate.
* log: to log packets.
* meta: to match metainformation that usually comes with the skbuff.
* nat: to perform Network Address Translation.
* payload: to fetch data from the packet payload and store it into
  registers.
* reject (IPv4 only): to explicitly close connection, eg. TCP RST.

Using this instruction-set, the userspace utility 'nft' can transform
the rules expressed in human-readable text representation (using a
new syntax, inspired by tcpdump) to nftables bytecode.

nftables also inherits the table, chain and rule objects from
iptables, but in a more configurable way, and it also includes the
original datatype-agnostic set infrastructure with mapping support.
This set infrastructure is enhanced in the follow up patch (netfilter:
nf_tables: add netlink set API).

This patch includes the following components:

* the netlink API: net/netfilter/nf_tables_api.c and
  include/uapi/netfilter/nf_tables.h
* the packet filter core: net/netfilter/nf_tables_core.c
* the expressions (described above): net/netfilter/nft_*.c
* the filter tables: arp, IPv4, IPv6 and bridge:
  net/ipv4/netfilter/nf_tables_ipv4.c
  net/ipv6/netfilter/nf_tables_ipv6.c
  net/ipv4/netfilter/nf_tables_arp.c
  net/bridge/netfilter/nf_tables_bridge.c
* the NAT table (IPv4 only):
  net/ipv4/netfilter/nf_table_nat_ipv4.c
* the route table (similar to mangle):
  net/ipv4/netfilter/nf_table_route_ipv4.c
  net/ipv6/netfilter/nf_table_route_ipv6.c
* internal definitions under:
  include/net/netfilter/nf_tables.h
  include/net/netfilter/nf_tables_core.h
* It also includes an skeleton expression:
  net/netfilter/nft_expr_template.c
  and the preliminary implementation of the meta target
  net/netfilter/nft_meta_target.c

It also includes a change in struct nf_hook_ops to add a new
pointer to store private data to the hook, that is used to store
the rule list per chain.

This patch is based on the patch from Patrick McHardy, plus merged
accumulated cleanups, fixes and small enhancements to the nftables
code that has been done since 2009, which are:

From Patrick McHardy:
* nf_tables: adjust netlink handler function signatures
* nf_tables: only retry table lookup after successful table module load
* nf_tables: fix event notification echo and avoid unnecessary messages
* nft_ct: add l3proto support
* nf_tables: pass expression context to nft_validate_data_load()
* nf_tables: remove redundant definition
* nft_ct: fix maxattr initialization
* nf_tables: fix invalid event type in nf_tables_getrule()
* nf_tables: simplify nft_data_init() usage
* nf_tables: build in more core modules
* nf_tables: fix double lookup expression unregistation
* nf_tables: move expression initialization to nf_tables_core.c
* nf_tables: build in payload module
* nf_tables: use NFPROTO constants
* nf_tables: rename pid variables to portid
* nf_tables: save 48 bits per rule
* nf_tables: introduce chain rename
* nf_tables: check for duplicate names on chain rename
* nf_tables: remove ability to specify handles for new rules
* nf_tables: return error for rule change request
* nf_tables: return error for NLM_F_REPLACE without rule handle
* nf_tables: include NLM_F_APPEND/NLM_F_REPLACE flags in rule notification
* nf_tables: fix NLM_F_MULTI usage in netlink notifications
* nf_tables: include NLM_F_APPEND in rule dumps

From Pablo Neira Ayuso:
* nf_tables: fix stack overflow in nf_tables_newrule
* nf_tables: nft_ct: fix compilation warning
* nf_tables: nft_ct: fix crash with invalid packets
* nft_log: group and qthreshold are 2^16
* nf_tables: nft_meta: fix socket uid,gid handling
* nft_counter: allow to restore counters
* nf_tables: fix module autoload
* nf_tables: allow to remove all rules placed in one chain
* nf_tables: use 64-bits rule handle instead of 16-bits
* nf_tables: fix chain after rule deletion
* nf_tables: improve deletion performance
* nf_tables: add missing code in route chain type
* nf_tables: rise maximum number of expressions from 12 to 128
* nf_tables: don't delete table if in use
* nf_tables: fix basechain release

From Tomasz Bursztyka:
* nf_tables: Add support for changing users chain's name
* nf_tables: Change chain's name to be fixed sized
* nf_tables: Add support for replacing a rule by another one
* nf_tables: Update uapi nftables netlink header documentation

From Florian Westphal:
* nft_log: group is u16, snaplen u32

From Phil Oester:
* nf_tables: operational limit match

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Patrick McHardy
2013-10-14 11:00:02 +02:00
committed by Pablo Neira Ayuso
parent f59cb0453c
commit 96518518cc
39 changed files with 6393 additions and 6 deletions

View File

@@ -53,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 {

View File

@@ -0,0 +1,301 @@
#ifndef _NET_NF_TABLES_H
#define _NET_NF_TABLES_H
#include <linux/list.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netlink.h>
struct nft_pktinfo {
struct sk_buff *skb;
const struct net_device *in;
const struct net_device *out;
u8 hooknum;
u8 nhoff;
u8 thoff;
};
struct nft_data {
union {
u32 data[4];
struct {
u32 verdict;
struct nft_chain *chain;
};
};
} __attribute__((aligned(__alignof__(u64))));
static inline int nft_data_cmp(const struct nft_data *d1,
const struct nft_data *d2,
unsigned int len)
{
return memcmp(d1->data, d2->data, len);
}
static inline void nft_data_copy(struct nft_data *dst,
const struct nft_data *src)
{
BUILD_BUG_ON(__alignof__(*dst) != __alignof__(u64));
*(u64 *)&dst->data[0] = *(u64 *)&src->data[0];
*(u64 *)&dst->data[2] = *(u64 *)&src->data[2];
}
static inline void nft_data_debug(const struct nft_data *data)
{
pr_debug("data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n",
data->data[0], data->data[1],
data->data[2], data->data[3]);
}
/**
* struct nft_ctx - nf_tables rule context
*
* @afi: address family info
* @table: the table the chain is contained in
* @chain: the chain the rule is contained in
*/
struct nft_ctx {
const struct nft_af_info *afi;
const struct nft_table *table;
const struct nft_chain *chain;
};
enum nft_data_types {
NFT_DATA_VALUE,
NFT_DATA_VERDICT,
};
struct nft_data_desc {
enum nft_data_types type;
unsigned int len;
};
extern int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
struct nft_data_desc *desc, const struct nlattr *nla);
extern void nft_data_uninit(const struct nft_data *data,
enum nft_data_types type);
extern int nft_data_dump(struct sk_buff *skb, int attr,
const struct nft_data *data,
enum nft_data_types type, unsigned int len);
static inline enum nft_data_types nft_dreg_to_type(enum nft_registers reg)
{
return reg == NFT_REG_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE;
}
extern int nft_validate_input_register(enum nft_registers reg);
extern int nft_validate_output_register(enum nft_registers reg);
extern int nft_validate_data_load(const struct nft_ctx *ctx,
enum nft_registers reg,
const struct nft_data *data,
enum nft_data_types type);
/**
* struct nft_expr_ops - nf_tables expression operations
*
* @eval: Expression evaluation function
* @init: initialization function
* @destroy: destruction function
* @dump: function to dump parameters
* @list: used internally
* @name: Identifier
* @owner: module reference
* @policy: netlink attribute policy
* @maxattr: highest netlink attribute number
* @size: full expression size, including private data size
*/
struct nft_expr;
struct nft_expr_ops {
void (*eval)(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt);
int (*init)(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
void (*destroy)(const struct nft_expr *expr);
int (*dump)(struct sk_buff *skb,
const struct nft_expr *expr);
struct list_head list;
const char *name;
struct module *owner;
const struct nla_policy *policy;
unsigned int maxattr;
unsigned int size;
};
#define NFT_EXPR_SIZE(size) (sizeof(struct nft_expr) + \
ALIGN(size, __alignof__(struct nft_expr)))
/**
* struct nft_expr - nf_tables expression
*
* @ops: expression ops
* @data: expression private data
*/
struct nft_expr {
const struct nft_expr_ops *ops;
unsigned char data[];
};
static inline void *nft_expr_priv(const struct nft_expr *expr)
{
return (void *)expr->data;
}
/**
* struct nft_rule - nf_tables rule
*
* @list: used internally
* @rcu_head: used internally for rcu
* @handle: rule handle
* @dlen: length of expression data
* @data: expression data
*/
struct nft_rule {
struct list_head list;
struct rcu_head rcu_head;
u64 handle:48,
dlen:16;
unsigned char data[]
__attribute__((aligned(__alignof__(struct nft_expr))));
};
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
{
return (struct nft_expr *)&rule->data[0];
}
static inline struct nft_expr *nft_expr_next(const struct nft_expr *expr)
{
return ((void *)expr) + expr->ops->size;
}
static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule)
{
return (struct nft_expr *)&rule->data[rule->dlen];
}
/*
* The last pointer isn't really necessary, but the compiler isn't able to
* determine that the result of nft_expr_last() is always the same since it
* can't assume that the dlen value wasn't changed within calls in the loop.
*/
#define nft_rule_for_each_expr(expr, last, rule) \
for ((expr) = nft_expr_first(rule), (last) = nft_expr_last(rule); \
(expr) != (last); \
(expr) = nft_expr_next(expr))
enum nft_chain_flags {
NFT_BASE_CHAIN = 0x1,
NFT_CHAIN_BUILTIN = 0x2,
};
/**
* struct nft_chain - nf_tables chain
*
* @rules: list of rules in the chain
* @list: used internally
* @rcu_head: used internally
* @handle: chain handle
* @flags: bitmask of enum nft_chain_flags
* @use: number of jump references to this chain
* @level: length of longest path to this chain
* @name: name of the chain
*/
struct nft_chain {
struct list_head rules;
struct list_head list;
struct rcu_head rcu_head;
u64 handle;
u8 flags;
u16 use;
u16 level;
char name[NFT_CHAIN_MAXNAMELEN];
};
/**
* struct nft_base_chain - nf_tables base chain
*
* @ops: netfilter hook ops
* @chain: the chain
*/
struct nft_base_chain {
struct nf_hook_ops ops;
struct nft_chain chain;
};
static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chain)
{
return container_of(chain, struct nft_base_chain, chain);
}
extern unsigned int nft_do_chain(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 nft_table_flags {
NFT_TABLE_BUILTIN = 0x1,
};
/**
* struct nft_table - nf_tables table
*
* @list: used internally
* @chains: chains in the table
* @sets: sets in the table
* @hgenerator: handle generator state
* @use: number of chain references to this table
* @flags: table flag (see enum nft_table_flags)
* @name: name of the table
*/
struct nft_table {
struct list_head list;
struct list_head chains;
struct list_head sets;
u64 hgenerator;
u32 use;
u16 flags;
char name[];
};
/**
* struct nft_af_info - nf_tables address family info
*
* @list: used internally
* @family: address family
* @nhooks: number of hooks in this family
* @owner: module owner
* @tables: used internally
* @hooks: hookfn overrides for packet validation
*/
struct nft_af_info {
struct list_head list;
int family;
unsigned int nhooks;
struct module *owner;
struct list_head tables;
nf_hookfn *hooks[NF_MAX_HOOKS];
};
extern int nft_register_afinfo(struct nft_af_info *);
extern void nft_unregister_afinfo(struct nft_af_info *);
extern int nft_register_table(struct nft_table *, int family);
extern void nft_unregister_table(struct nft_table *, int family);
extern int nft_register_expr(struct nft_expr_ops *);
extern void nft_unregister_expr(struct nft_expr_ops *);
#define MODULE_ALIAS_NFT_FAMILY(family) \
MODULE_ALIAS("nft-afinfo-" __stringify(family))
#define MODULE_ALIAS_NFT_TABLE(family, name) \
MODULE_ALIAS("nft-table-" __stringify(family) "-" name)
#define MODULE_ALIAS_NFT_EXPR(name) \
MODULE_ALIAS("nft-expr-" name)
#endif /* _NET_NF_TABLES_H */

View File

@@ -0,0 +1,25 @@
#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);
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);
extern int nft_payload_module_init(void);
extern void nft_payload_module_exit(void);
#endif /* _NET_NF_TABLES_CORE_H */

View File

@@ -5,6 +5,7 @@ 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_nat.h
header-y += nfnetlink.h
header-y += nfnetlink_acct.h

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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,7 @@ 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_COUNT 11
#endif /* _UAPI_NFNETLINK_H */

View File

@@ -1,6 +1,9 @@
#
# Bridge netfilter configuration
#
#
config NF_TABLES_BRIDGE
tristate "Ethernet Bridge nf_tables support"
menuconfig BRIDGE_NF_EBTABLES
tristate "Ethernet Bridge tables (ebtables) support"

View File

@@ -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

View File

@@ -0,0 +1,37 @@
/*
* 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 __init nf_tables_bridge_init(void)
{
return nft_register_afinfo(&nft_af_bridge);
}
static void __exit nf_tables_bridge_exit(void)
{
nft_unregister_afinfo(&nft_af_bridge);
}
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);

View File

@@ -36,6 +36,22 @@ 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 NF_TABLE_ROUTE_IPV4
depends on NF_TABLES_IPV4
tristate "IPv4 nf_tables route table support"
config NF_TABLE_NAT_IPV4
depends on NF_TABLES_IPV4
tristate "IPv4 nf_tables nat table support"
config IP_NF_IPTABLES
tristate "IP tables support (required for filtering/masq/NAT)"
default m if NETFILTER_ADVANCED=n

View File

@@ -27,6 +27,11 @@ 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_NF_TABLE_ROUTE_IPV4) += nf_table_route_ipv4.o
obj-$(CONFIG_NF_TABLE_NAT_IPV4) += nf_table_nat_ipv4.o
# generic IP tables
obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o

View File

@@ -0,0 +1,409 @@
/*
* 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/skbuff.h>
#include <linux/ip.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_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_nat_l3proto.h>
#include <net/ip.h>
struct nft_nat {
enum nft_registers sreg_addr_min:8;
enum nft_registers sreg_addr_max:8;
enum nft_registers sreg_proto_min:8;
enum nft_registers sreg_proto_max:8;
enum nf_nat_manip_type type;
};
static void nft_nat_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_nat *priv = nft_expr_priv(expr);
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo);
struct nf_nat_range range;
memset(&range, 0, sizeof(range));
if (priv->sreg_addr_min) {
range.min_addr.ip = data[priv->sreg_addr_min].data[0];
range.max_addr.ip = data[priv->sreg_addr_max].data[0];
range.flags |= NF_NAT_RANGE_MAP_IPS;
}
if (priv->sreg_proto_min) {
range.min_proto.all = data[priv->sreg_proto_min].data[0];
range.max_proto.all = data[priv->sreg_proto_max].data[0];
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
}
data[NFT_REG_VERDICT].verdict =
nf_nat_setup_info(ct, &range, priv->type);
}
static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = {
[NFTA_NAT_ADDR_MIN] = { .type = NLA_U32 },
[NFTA_NAT_ADDR_MAX] = { .type = NLA_U32 },
[NFTA_NAT_PROTO_MIN] = { .type = NLA_U32 },
[NFTA_NAT_PROTO_MAX] = { .type = NLA_U32 },
[NFTA_NAT_TYPE] = { .type = NLA_U32 },
};
static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_nat *priv = nft_expr_priv(expr);
int err;
if (tb[NFTA_NAT_TYPE] == NULL)
return -EINVAL;
switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) {
case NFT_NAT_SNAT:
priv->type = NF_NAT_MANIP_SRC;
break;
case NFT_NAT_DNAT:
priv->type = NF_NAT_MANIP_DST;
break;
default:
return -EINVAL;
}
if (tb[NFTA_NAT_ADDR_MIN]) {
priv->sreg_addr_min = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MIN]));
err = nft_validate_input_register(priv->sreg_addr_min);
if (err < 0)
return err;
}
if (tb[NFTA_NAT_ADDR_MAX]) {
priv->sreg_addr_max = ntohl(nla_get_be32(tb[NFTA_NAT_ADDR_MAX]));
err = nft_validate_input_register(priv->sreg_addr_max);
if (err < 0)
return err;
} else
priv->sreg_addr_max = priv->sreg_addr_min;
if (tb[NFTA_NAT_PROTO_MIN]) {
priv->sreg_proto_min = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MIN]));
err = nft_validate_input_register(priv->sreg_proto_min);
if (err < 0)
return err;
}
if (tb[NFTA_NAT_PROTO_MAX]) {
priv->sreg_proto_max = ntohl(nla_get_be32(tb[NFTA_NAT_PROTO_MAX]));
err = nft_validate_input_register(priv->sreg_proto_max);
if (err < 0)
return err;
} else
priv->sreg_proto_max = priv->sreg_proto_min;
return 0;
}
static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_nat *priv = nft_expr_priv(expr);
switch (priv->type) {
case NF_NAT_MANIP_SRC:
if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT)))
goto nla_put_failure;
break;
case NF_NAT_MANIP_DST:
if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT)))
goto nla_put_failure;
break;
}
if (nla_put_be32(skb, NFTA_NAT_ADDR_MIN, htonl(priv->sreg_addr_min)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_ADDR_MAX, htonl(priv->sreg_addr_max)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_PROTO_MIN, htonl(priv->sreg_proto_min)))
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_NAT_PROTO_MAX, htonl(priv->sreg_proto_max)))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_ops nft_nat_ops __read_mostly = {
.name = "nat",
.size = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
.owner = THIS_MODULE,
.eval = nft_nat_eval,
.init = nft_nat_init,
.dump = nft_nat_dump,
.policy = nft_nat_policy,
.maxattr = NFTA_NAT_MAX,
};
/*
* NAT table
*/
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);
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;
ret = nft_do_chain(ops, skb, in, out, okfn);
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 nft_base_chain nf_chain_nat_prerouting __read_mostly = {
.chain = {
.name = "PREROUTING",
.rules = LIST_HEAD_INIT(nf_chain_nat_prerouting.chain.rules),
.flags = NFT_BASE_CHAIN | NFT_CHAIN_BUILTIN,
},
.ops = {
.hook = nf_nat_prerouting,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_NAT_DST,
.priv = &nf_chain_nat_prerouting.chain,
},
};
static struct nft_base_chain nf_chain_nat_postrouting __read_mostly = {
.chain = {
.name = "POSTROUTING",
.rules = LIST_HEAD_INIT(nf_chain_nat_postrouting.chain.rules),
.flags = NFT_BASE_CHAIN | NFT_CHAIN_BUILTIN,
},
.ops = {
.hook = nf_nat_postrouting,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SRC,
.priv = &nf_chain_nat_postrouting.chain,
},
};
static struct nft_base_chain nf_chain_nat_output __read_mostly = {
.chain = {
.name = "OUTPUT",
.rules = LIST_HEAD_INIT(nf_chain_nat_output.chain.rules),
.flags = NFT_BASE_CHAIN | NFT_CHAIN_BUILTIN,
},
.ops = {
.hook = nf_nat_output,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_NAT_DST,
.priv = &nf_chain_nat_output.chain,
},
};
static struct nft_base_chain nf_chain_nat_input __read_mostly = {
.chain = {
.name = "INPUT",
.rules = LIST_HEAD_INIT(nf_chain_nat_input.chain.rules),
.flags = NFT_BASE_CHAIN | NFT_CHAIN_BUILTIN,
},
.ops = {
.hook = nf_nat_fn,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC,
.priv = &nf_chain_nat_input.chain,
},
};
static struct nft_table nf_table_nat_ipv4 __read_mostly = {
.name = "nat",
.chains = LIST_HEAD_INIT(nf_table_nat_ipv4.chains),
};
static int __init nf_table_nat_init(void)
{
int err;
list_add_tail(&nf_chain_nat_prerouting.chain.list,
&nf_table_nat_ipv4.chains);
list_add_tail(&nf_chain_nat_postrouting.chain.list,
&nf_table_nat_ipv4.chains);
list_add_tail(&nf_chain_nat_output.chain.list,
&nf_table_nat_ipv4.chains);
list_add_tail(&nf_chain_nat_input.chain.list,
&nf_table_nat_ipv4.chains);
err = nft_register_table(&nf_table_nat_ipv4, NFPROTO_IPV4);
if (err < 0)
goto err1;
err = nft_register_expr(&nft_nat_ops);
if (err < 0)
goto err2;
return 0;
err2:
nft_unregister_table(&nf_table_nat_ipv4, NFPROTO_IPV4);
err1:
return err;
}
static void __exit nf_table_nat_exit(void)
{
nft_unregister_expr(&nft_nat_ops);
nft_unregister_table(&nf_table_nat_ipv4, AF_INET);
}
module_init(nf_table_nat_init);
module_exit(nf_table_nat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_TABLE(AF_INET, "nat");
MODULE_ALIAS_NFT_EXPR("nat");

View File

@@ -0,0 +1,97 @@
/*
* 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.
*/
#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/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;
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;
mark = skb->mark;
iph = ip_hdr(skb);
saddr = iph->saddr;
daddr = iph->daddr;
tos = iph->tos;
ret = nft_do_chain(ops, skb, in, out, okfn);
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 nft_base_chain nf_chain_route_output __read_mostly = {
.chain = {
.name = "OUTPUT",
.rules = LIST_HEAD_INIT(nf_chain_route_output.chain.rules),
.flags = NFT_BASE_CHAIN | NFT_CHAIN_BUILTIN,
},
.ops = {
.hook = nf_route_table_hook,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_MANGLE,
.priv = &nf_chain_route_output.chain,
},
};
static struct nft_table nf_table_route_ipv4 __read_mostly = {
.name = "route",
.chains = LIST_HEAD_INIT(nf_table_route_ipv4.chains),
};
static int __init nf_table_route_init(void)
{
list_add_tail(&nf_chain_route_output.chain.list,
&nf_table_route_ipv4.chains);
return nft_register_table(&nf_table_route_ipv4, NFPROTO_IPV4);
}
static void __exit nf_table_route_exit(void)
{
nft_unregister_table(&nf_table_route_ipv4, NFPROTO_IPV4);
}
module_init(nf_table_route_init);
module_exit(nf_table_route_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_TABLE(AF_INET, "route");

View File

@@ -0,0 +1,59 @@
/*
* 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/ip.h>
#include <linux/netfilter_ipv4.h>
#include <net/netfilter/nf_tables.h>
#include <net/ip.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 *))
{
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;
}
return nft_do_chain(ops, skb, in, out, okfn);
}
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 __init nf_tables_ipv4_init(void)
{
return nft_register_afinfo(&nft_af_ipv4);
}
static void __exit nf_tables_ipv4_exit(void)
{
nft_unregister_afinfo(&nft_af_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);

View File

@@ -0,0 +1,117 @@
/*
* 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>
#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_ops reject_ops __read_mostly = {
.name = "reject",
.size = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
.owner = THIS_MODULE,
.eval = nft_reject_eval,
.init = nft_reject_init,
.dump = nft_reject_dump,
.policy = nft_reject_policy,
.maxattr = NFTA_REJECT_MAX,
};
static int __init nft_reject_module_init(void)
{
return nft_register_expr(&reject_ops);
}
static void __exit nft_reject_module_exit(void)
{
nft_unregister_expr(&reject_ops);
}
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");

View File

@@ -25,6 +25,14 @@ 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 NF_TABLE_ROUTE_IPV6
depends on NF_TABLES_IPV6
tristate "IPv6 nf_tables route table support"
config IP6_NF_IPTABLES
tristate "IP6 tables support (required for filtering)"
depends on INET && IPV6

View File

@@ -23,6 +23,10 @@ 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_NF_TABLE_ROUTE_IPV6) += nf_table_route_ipv6.o
# matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o

View File

@@ -0,0 +1,93 @@
/*
* 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/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/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 in6_addr saddr, daddr;
u_int8_t hop_limit;
u32 mark, flowlabel;
/* 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(ops, skb, in, out, okfn);
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 nft_base_chain nf_chain_route_output __read_mostly = {
.chain = {
.name = "OUTPUT",
.rules = LIST_HEAD_INIT(nf_chain_route_output.chain.rules),
.flags = NFT_BASE_CHAIN | NFT_CHAIN_BUILTIN,
},
.ops = {
.hook = nf_route_table_hook,
.owner = THIS_MODULE,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_MANGLE,
.priv = &nf_chain_route_output.chain,
},
};
static struct nft_table nf_table_route_ipv6 __read_mostly = {
.name = "route",
.chains = LIST_HEAD_INIT(nf_table_route_ipv6.chains),
};
static int __init nf_table_route_init(void)
{
list_add_tail(&nf_chain_route_output.chain.list,
&nf_table_route_ipv6.chains);
return nft_register_table(&nf_table_route_ipv6, NFPROTO_IPV6);
}
static void __exit nf_table_route_exit(void)
{
nft_unregister_table(&nf_table_route_ipv6, NFPROTO_IPV6);
}
module_init(nf_table_route_init);
module_exit(nf_table_route_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
MODULE_ALIAS_NFT_TABLE(AF_INET6, "route");

View File

@@ -0,0 +1,57 @@
/*
* 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/ipv6.h>
#include <linux/netfilter_ipv6.h>
#include <net/netfilter/nf_tables.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 *))
{
if (unlikely(skb->len < sizeof(struct ipv6hdr))) {
if (net_ratelimit())
pr_info("nf_tables_ipv6: ignoring short SOCK_RAW "
"packet\n");
return NF_ACCEPT;
}
return nft_do_chain(ops, skb, in, out, okfn);
}
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 __init nf_tables_ipv6_init(void)
{
return nft_register_afinfo(&nft_af_ipv6);
}
static void __exit nf_tables_ipv6_exit(void)
{
nft_unregister_afinfo(&nft_af_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);

Some files were not shown because too many files have changed in this diff Show More