Commit 074fb880 authored by David S. Miller's avatar David S. Miller

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

Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for your net-next tree:

1) Support for transparent proxying for nf_tables, from Mate Eckl.

2) Patchset to add OS passive fingerprint recognition for nf_tables,
   from Fernando Fernandez. This takes common code from xt_osf and
   place it into the new nfnetlink_osf module for codebase sharing.

3) Lightweight tunneling support for nf_tables.

4) meta and lookup are likely going to be used in rulesets, make them
   direct calls. From Florian Westphal.

A bunch of incremental updates:

5) use PTR_ERR_OR_ZERO() from nft_numgen, from YueHaibing.

6) Use kvmalloc_array() to allocate hashtables, from Li RongQing.

7) Explicit dependencies between nfnetlink_cttimeout and conntrack
   timeout extensions, from Harsha Sharma.

8) Simplify NLM_F_CREATE handling in nf_tables.

9) Removed unused variable in the get element command, from
   YueHaibing.

10) Expose bridge hook priorities through uapi, from Mate Eckl.

And a few fixes for previous Netfilter batch for net-next:

11) Use per-netns mutex from flowtable event, from Florian Westphal.

12) Remove explicit dependency on iptables CT target from conntrack
    zones, from Florian.

13) Fix use-after-free in rmmod nf_conntrack path, also from Florian.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c1c8626f 483f3fdc
#include <uapi/linux/netfilter/nf_osf.h>
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _NFOSF_H
#define _NFOSF_H
#include <uapi/linux/netfilter/nfnetlink_osf.h>
/* Initial window size option state machine: multiple of mss, mtu or
* plain numeric value. Can also be made as plain numeric value which
......@@ -21,6 +25,8 @@ enum osf_fmatch_states {
FMATCH_OPT_WRONG,
};
extern struct list_head nf_osf_fingers[2];
struct nf_osf_finger {
struct rcu_head rcu_head;
struct list_head finger_entry;
......@@ -31,3 +37,8 @@ bool nf_osf_match(const struct sk_buff *skb, u_int8_t family,
int hooknum, struct net_device *in, struct net_device *out,
const struct nf_osf_info *info, struct net *net,
const struct list_head *nf_osf_fingers);
const char *nf_osf_find(const struct sk_buff *skb,
const struct list_head *nf_osf_fingers);
#endif /* _NFOSF_H */
......@@ -5,17 +5,6 @@
#include <uapi/linux/netfilter_bridge.h>
#include <linux/skbuff.h>
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
NF_BR_PRI_NAT_DST_BRIDGED = -300,
NF_BR_PRI_FILTER_BRIDGED = -200,
NF_BR_PRI_BRNF = 0,
NF_BR_PRI_NAT_DST_OTHER = 100,
NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_SRC = 300,
NF_BR_PRI_LAST = INT_MAX,
};
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
......
......@@ -176,8 +176,6 @@ void nf_ct_netns_put(struct net *net, u8 nfproto);
*/
void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls);
void nf_ct_free_hashtable(void *hash, unsigned int size);
int nf_conntrack_hash_check_insert(struct nf_conn *ct);
bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report);
......
......@@ -71,4 +71,11 @@ extern struct nft_set_type nft_set_hash_fast_type;
extern struct nft_set_type nft_set_rbtree_type;
extern struct nft_set_type nft_set_bitmap_type;
struct nft_expr;
struct nft_regs;
struct nft_pktinfo;
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs, const struct nft_pktinfo *pkt);
void nft_lookup_eval(const struct nft_expr *expr,
struct nft_regs *regs, const struct nft_pktinfo *pkt);
#endif /* _NET_NF_TABLES_CORE_H */
......@@ -1252,6 +1252,22 @@ enum nft_nat_attributes {
};
#define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1)
/**
* enum nft_tproxy_attributes - nf_tables tproxy expression netlink attributes
*
* NFTA_TPROXY_FAMILY: Target address family (NLA_U32: nft_registers)
* NFTA_TPROXY_REG_ADDR: Target address register (NLA_U32: nft_registers)
* NFTA_TPROXY_REG_PORT: Target port register (NLA_U32: nft_registers)
*/
enum nft_tproxy_attributes {
NFTA_TPROXY_UNSPEC,
NFTA_TPROXY_FAMILY,
NFTA_TPROXY_REG_ADDR,
NFTA_TPROXY_REG_PORT,
__NFTA_TPROXY_MAX
};
#define NFTA_TPROXY_MAX (__NFTA_TPROXY_MAX - 1)
/**
* enum nft_masq_attributes - nf_tables masquerade expression attributes
*
......@@ -1400,7 +1416,8 @@ enum nft_ct_helper_attributes {
#define NFT_OBJECT_CT_HELPER 3
#define NFT_OBJECT_LIMIT 4
#define NFT_OBJECT_CONNLIMIT 5
#define __NFT_OBJECT_MAX 6
#define NFT_OBJECT_TUNNEL 6
#define __NFT_OBJECT_MAX 7
#define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1)
/**
......@@ -1463,6 +1480,13 @@ enum nft_flowtable_hook_attributes {
};
#define NFTA_FLOWTABLE_HOOK_MAX (__NFTA_FLOWTABLE_HOOK_MAX - 1)
enum nft_osf_attributes {
NFTA_OSF_UNSPEC,
NFTA_OSF_DREG,
__NFTA_OSF_MAX,
};
#define NFTA_OSF_MAX (__NFTA_OSF_MAX - 1)
/**
* enum nft_device_attributes - nf_tables device netlink attributes
*
......@@ -1557,4 +1581,85 @@ enum nft_ng_types {
};
#define NFT_NG_MAX (__NFT_NG_MAX - 1)
enum nft_tunnel_key_ip_attributes {
NFTA_TUNNEL_KEY_IP_UNSPEC,
NFTA_TUNNEL_KEY_IP_SRC,
NFTA_TUNNEL_KEY_IP_DST,
__NFTA_TUNNEL_KEY_IP_MAX
};
#define NFTA_TUNNEL_KEY_IP_MAX (__NFTA_TUNNEL_KEY_IP_MAX - 1)
enum nft_tunnel_ip6_attributes {
NFTA_TUNNEL_KEY_IP6_UNSPEC,
NFTA_TUNNEL_KEY_IP6_SRC,
NFTA_TUNNEL_KEY_IP6_DST,
NFTA_TUNNEL_KEY_IP6_FLOWLABEL,
__NFTA_TUNNEL_KEY_IP6_MAX
};
#define NFTA_TUNNEL_KEY_IP6_MAX (__NFTA_TUNNEL_KEY_IP6_MAX - 1)
enum nft_tunnel_opts_attributes {
NFTA_TUNNEL_KEY_OPTS_UNSPEC,
NFTA_TUNNEL_KEY_OPTS_VXLAN,
NFTA_TUNNEL_KEY_OPTS_ERSPAN,
__NFTA_TUNNEL_KEY_OPTS_MAX
};
#define NFTA_TUNNEL_KEY_OPTS_MAX (__NFTA_TUNNEL_KEY_OPTS_MAX - 1)
enum nft_tunnel_opts_vxlan_attributes {
NFTA_TUNNEL_KEY_VXLAN_UNSPEC,
NFTA_TUNNEL_KEY_VXLAN_GBP,
__NFTA_TUNNEL_KEY_VXLAN_MAX
};
#define NFTA_TUNNEL_KEY_VXLAN_MAX (__NFTA_TUNNEL_KEY_VXLAN_MAX - 1)
enum nft_tunnel_opts_erspan_attributes {
NFTA_TUNNEL_KEY_ERSPAN_UNSPEC,
NFTA_TUNNEL_KEY_ERSPAN_VERSION,
NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX,
NFTA_TUNNEL_KEY_ERSPAN_V2_HWID,
NFTA_TUNNEL_KEY_ERSPAN_V2_DIR,
__NFTA_TUNNEL_KEY_ERSPAN_MAX
};
#define NFTA_TUNNEL_KEY_ERSPAN_MAX (__NFTA_TUNNEL_KEY_ERSPAN_MAX - 1)
enum nft_tunnel_flags {
NFT_TUNNEL_F_ZERO_CSUM_TX = (1 << 0),
NFT_TUNNEL_F_DONT_FRAGMENT = (1 << 1),
NFT_TUNNEL_F_SEQ_NUMBER = (1 << 2),
};
#define NFT_TUNNEL_F_MASK (NFT_TUNNEL_F_ZERO_CSUM_TX | \
NFT_TUNNEL_F_DONT_FRAGMENT | \
NFT_TUNNEL_F_SEQ_NUMBER)
enum nft_tunnel_key_attributes {
NFTA_TUNNEL_KEY_UNSPEC,
NFTA_TUNNEL_KEY_ID,
NFTA_TUNNEL_KEY_IP,
NFTA_TUNNEL_KEY_IP6,
NFTA_TUNNEL_KEY_FLAGS,
NFTA_TUNNEL_KEY_TOS,
NFTA_TUNNEL_KEY_TTL,
NFTA_TUNNEL_KEY_SPORT,
NFTA_TUNNEL_KEY_DPORT,
NFTA_TUNNEL_KEY_OPTS,
__NFTA_TUNNEL_KEY_MAX
};
#define NFTA_TUNNEL_KEY_MAX (__NFTA_TUNNEL_KEY_MAX - 1)
enum nft_tunnel_keys {
NFT_TUNNEL_PATH,
NFT_TUNNEL_ID,
__NFT_TUNNEL_MAX
};
#define NFT_TUNNEL_MAX (__NFT_TUNNEL_MAX - 1)
enum nft_tunnel_attributes {
NFTA_TUNNEL_UNSPEC,
NFTA_TUNNEL_KEY,
NFTA_TUNNEL_DREG,
__NFTA_TUNNEL_MAX
};
#define NFTA_TUNNEL_MAX (__NFTA_TUNNEL_MAX - 1)
#endif /* _LINUX_NF_TABLES_H */
......@@ -94,4 +94,13 @@ enum nf_osf_attr_type {
OSF_ATTR_MAX,
};
/*
* Add/remove fingerprint from the kernel.
*/
enum nf_osf_msg_types {
OSF_MSG_ADD,
OSF_MSG_REMOVE,
OSF_MSG_MAX,
};
#endif /* _NF_OSF_H */
......@@ -23,7 +23,7 @@
#include <linux/types.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/netfilter/nf_osf.h>
#include <linux/netfilter/nfnetlink_osf.h>
#define XT_OSF_GENRE NF_OSF_GENRE
#define XT_OSF_INVERT NF_OSF_INVERT
......@@ -47,13 +47,6 @@
#define xt_osf_nlmsg nf_osf_nlmsg
#define xt_osf_attr_type nf_osf_attr_type
/*
* Add/remove fingerprint from the kernel.
*/
enum xt_osf_msg_types {
OSF_MSG_ADD,
OSF_MSG_REMOVE,
OSF_MSG_MAX,
};
#define xt_osf_msg_types nf_osf_msg_types
#endif /* _XT_OSF_H */
......@@ -26,4 +26,15 @@
#define NF_BR_BROUTING 5
#define NF_BR_NUMHOOKS 6
enum nf_br_hook_priorities {
NF_BR_PRI_FIRST = INT_MIN,
NF_BR_PRI_NAT_DST_BRIDGED = -300,
NF_BR_PRI_FILTER_BRIDGED = -200,
NF_BR_PRI_BRNF = 0,
NF_BR_PRI_NAT_DST_OTHER = 100,
NF_BR_PRI_FILTER_OTHER = 200,
NF_BR_PRI_NAT_SRC = 300,
NF_BR_PRI_LAST = INT_MAX,
};
#endif /* _UAPI__LINUX_BRIDGE_NETFILTER_H */
......@@ -26,6 +26,7 @@
#include <linux/if_pppox.h>
#include <linux/ppp_defs.h>
#include <linux/netfilter_bridge.h>
#include <uapi/linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h>
......
......@@ -9,6 +9,7 @@
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <uapi/linux/netfilter_bridge.h>
#include <linux/module.h>
#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
......
......@@ -9,6 +9,7 @@
*/
#include <linux/netfilter_bridge/ebtables.h>
#include <uapi/linux/netfilter_bridge.h>
#include <linux/module.h>
#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
......
......@@ -307,6 +307,7 @@ void metadata_dst_free(struct metadata_dst *md_dst)
#endif
kfree(md_dst);
}
EXPORT_SYMBOL_GPL(metadata_dst_free);
struct metadata_dst __percpu *
metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags)
......
......@@ -46,6 +46,14 @@ config NETFILTER_NETLINK_LOG
and is also scheduled to replace the old syslog-based ipt_LOG
and ip6t_LOG modules.
config NETFILTER_NETLINK_OSF
tristate "Netfilter OSF over NFNETLINK interface"
depends on NETFILTER_ADVANCED
select NETFILTER_NETLINK
help
If this option is enabled, the kernel will include support
for passive OS fingerprint via NFNETLINK.
config NF_CONNTRACK
tristate "Netfilter connection tracking support"
default m if NETFILTER_ADVANCED=n
......@@ -98,7 +106,6 @@ config NF_CONNTRACK_SECMARK
config NF_CONNTRACK_ZONES
bool 'Connection tracking zones'
depends on NETFILTER_ADVANCED
depends on NETFILTER_XT_TARGET_CT
help
This option enables support for connection tracking zones.
Normally, each connection needs to have a unique system wide
......@@ -150,10 +157,11 @@ config NF_CONNTRACK_TIMESTAMP
If unsure, say `N'.
config NF_CONNTRACK_LABELS
bool
bool "Connection tracking labels"
help
This option enables support for assigning user-defined flag bits
to connection tracking entries. It selected by the connlabel match.
to connection tracking entries. It can be used with xtables connlabel
match and the nftables ct expression.
config NF_CT_PROTO_DCCP
bool 'DCCP protocol connection tracking support'
......@@ -357,6 +365,7 @@ config NF_CT_NETLINK_TIMEOUT
tristate 'Connection tracking timeout tuning via Netlink'
select NETFILTER_NETLINK
depends on NETFILTER_ADVANCED
depends on NF_CONNTRACK_TIMEOUT
help
This option enables support for connection tracking timeout
fine-grain tuning. This allows you to attach specific timeout
......@@ -442,9 +451,6 @@ config NETFILTER_SYNPROXY
endif # NF_CONNTRACK
config NF_OSF
tristate
config NF_TABLES
select NETFILTER_NETLINK
tristate "Netfilter nf_tables support"
......@@ -553,6 +559,12 @@ config NFT_NAT
This option adds the "nat" expression that you can use to perform
typical Network Address Translation (NAT) packet transformations.
config NFT_TUNNEL
tristate "Netfilter nf_tables tunnel module"
help
This option adds the "tunnel" expression that you can use to set
tunneling policies.
config NFT_OBJREF
tristate "Netfilter nf_tables stateful object reference module"
help
......@@ -622,6 +634,23 @@ config NFT_SOCKET
This option allows matching for the presence or absence of a
corresponding socket and its attributes.
config NFT_OSF
tristate "Netfilter nf_tables passive OS fingerprint support"
depends on NETFILTER_ADVANCED
select NETFILTER_NETLINK_OSF
help
This option allows matching packets from an specific OS.
config NFT_TPROXY
tristate "Netfilter nf_tables tproxy support"
depends on IPV6 || IPV6=n
select NF_DEFRAG_IPV4
select NF_DEFRAG_IPV6 if NF_TABLES_IPV6
select NF_TPROXY_IPV4
select NF_TPROXY_IPV6 if NF_TABLES_IPV6
help
This makes transparent proxy support available in nftables.
if NF_TABLES_NETDEV
config NF_DUP_NETDEV
......@@ -1368,8 +1397,8 @@ config NETFILTER_XT_MATCH_NFACCT
config NETFILTER_XT_MATCH_OSF
tristate '"osf" Passive OS fingerprint match'
depends on NETFILTER_ADVANCED && NETFILTER_NETLINK
select NF_OSF
depends on NETFILTER_ADVANCED
select NETFILTER_NETLINK_OSF
help
This option selects the Passive OS Fingerprinting match module
that allows to passively match the remote operating system by
......
......@@ -20,6 +20,7 @@ obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
obj-$(CONFIG_NETFILTER_NETLINK_ACCT) += nfnetlink_acct.o
obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
obj-$(CONFIG_NETFILTER_NETLINK_OSF) += nfnetlink_osf.o
# connection tracking
obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
......@@ -100,6 +101,7 @@ obj-$(CONFIG_NFT_QUEUE) += nft_queue.o
obj-$(CONFIG_NFT_QUOTA) += nft_quota.o
obj-$(CONFIG_NFT_REJECT) += nft_reject.o
obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o
obj-$(CONFIG_NFT_TUNNEL) += nft_tunnel.o
obj-$(CONFIG_NFT_COUNTER) += nft_counter.o
obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o
......@@ -108,8 +110,9 @@ obj-$(CONFIG_NFT_HASH) += nft_hash.o
obj-$(CONFIG_NFT_FIB) += nft_fib.o
obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o
obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o
obj-$(CONFIG_NF_OSF) += nf_osf.o
obj-$(CONFIG_NFT_SOCKET) += nft_socket.o
obj-$(CONFIG_NFT_OSF) += nft_osf.o
obj-$(CONFIG_NFT_TPROXY) += nft_tproxy.o
# nf_tables netdev
obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o
......
......@@ -2022,16 +2022,6 @@ static int kill_all(struct nf_conn *i, void *data)
return net_eq(nf_ct_net(i), data);
}
void nf_ct_free_hashtable(void *hash, unsigned int size)
{
if (is_vmalloc_addr(hash))
vfree(hash);
else
free_pages((unsigned long)hash,
get_order(sizeof(struct hlist_head) * size));
}
EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
void nf_conntrack_cleanup_start(void)
{
conntrack_gc_work.exiting = true;
......@@ -2042,7 +2032,7 @@ void nf_conntrack_cleanup_end(void)
{
RCU_INIT_POINTER(nf_ct_hook, NULL);
cancel_delayed_work_sync(&conntrack_gc_work.dwork);
nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size);
kvfree(nf_conntrack_hash);
nf_conntrack_proto_fini();
nf_conntrack_seqadj_fini();
......@@ -2108,7 +2098,6 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
{
struct hlist_nulls_head *hash;
unsigned int nr_slots, i;
size_t sz;
if (*sizep > (UINT_MAX / sizeof(struct hlist_nulls_head)))
return NULL;
......@@ -2116,14 +2105,8 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head));
nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head));
if (nr_slots > (UINT_MAX / sizeof(struct hlist_nulls_head)))
return NULL;
sz = nr_slots * sizeof(struct hlist_nulls_head);
hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO,
get_order(sz));
if (!hash)
hash = vzalloc(sz);
hash = kvmalloc_array(nr_slots, sizeof(struct hlist_nulls_head),
GFP_KERNEL | __GFP_ZERO);
if (hash && nulls)
for (i = 0; i < nr_slots; i++)
......@@ -2150,7 +2133,7 @@ int nf_conntrack_hash_resize(unsigned int hashsize)
old_size = nf_conntrack_htable_size;
if (old_size == hashsize) {
nf_ct_free_hashtable(hash, hashsize);
kvfree(hash);
return 0;
}
......@@ -2186,7 +2169,7 @@ int nf_conntrack_hash_resize(unsigned int hashsize)
local_bh_enable();
synchronize_net();
nf_ct_free_hashtable(old_hash, old_size);
kvfree(old_hash);
return 0;
}
......@@ -2350,7 +2333,7 @@ int nf_conntrack_init_start(void)
err_expect:
kmem_cache_destroy(nf_conntrack_cachep);
err_cachep:
nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size);
kvfree(nf_conntrack_hash);
return ret;
}
......
......@@ -712,5 +712,5 @@ void nf_conntrack_expect_fini(void)
{
rcu_barrier(); /* Wait for call_rcu() before destroy */
kmem_cache_destroy(nf_ct_expect_cachep);
nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_hsize);
kvfree(nf_ct_expect_hash);
}
......@@ -562,12 +562,12 @@ int nf_conntrack_helper_init(void)
return 0;
out_extend:
nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
kvfree(nf_ct_helper_hash);
return ret;
}
void nf_conntrack_helper_fini(void)
{
nf_ct_extend_unregister(&helper_extend);
nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
kvfree(nf_ct_helper_hash);
}
......@@ -940,14 +940,13 @@ void nf_conntrack_proto_fini(void)
{
unsigned int i;
nf_ct_l4proto_unregister(builtin_l4proto,
ARRAY_SIZE(builtin_l4proto));
nf_unregister_sockopt(&so_getorigdst);
#if IS_ENABLED(CONFIG_IPV6)
nf_unregister_sockopt(&so_getorigdst6);
#endif
/* free l3proto protocol tables */
/* No need to call nf_ct_l4proto_unregister(), the register
* tables are free'd here anyway.
*/
for (i = 0; i < ARRAY_SIZE(nf_ct_protos); i++)
kfree(nf_ct_protos[i]);
}
......
......@@ -1056,7 +1056,7 @@ static int __init nf_nat_init(void)
ret = nf_ct_extend_register(&nat_extend);
if (ret < 0) {
nf_ct_free_hashtable(nf_nat_bysource, nf_nat_htable_size);
kvfree(nf_nat_bysource);
pr_err("Unable to register extension\n");
return ret;
}
......@@ -1094,7 +1094,7 @@ static void __exit nf_nat_cleanup(void)
for (i = 0; i < NFPROTO_NUMPROTO; i++)
kfree(nf_nat_l4protos[i]);
synchronize_net();
nf_ct_free_hashtable(nf_nat_bysource, nf_nat_htable_size);
kvfree(nf_nat_bysource);
unregister_pernet_subsys(&nat_net_ops);
}
......
......@@ -1442,7 +1442,7 @@ struct nft_chain_hook {
static int nft_chain_parse_hook(struct net *net,
const struct nlattr * const nla[],
struct nft_chain_hook *hook, u8 family,
bool create)
bool autoload)
{
struct nlattr *ha[NFTA_HOOK_MAX + 1];
const struct nft_chain_type *type;
......@@ -1467,7 +1467,7 @@ static int nft_chain_parse_hook(struct net *net,
type = chain_type[family][NFT_CHAIN_T_DEFAULT];
if (nla[NFTA_CHAIN_TYPE]) {
type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE],
family, create);
family, autoload);
if (IS_ERR(type))
return PTR_ERR(type);
}
......@@ -1534,7 +1534,7 @@ static struct nft_rule **nf_tables_chain_alloc_rules(const struct nft_chain *cha
}
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
u8 policy, bool create)
u8 policy)
{
const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table;
......@@ -1552,7 +1552,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
struct nft_chain_hook hook;
struct nf_hook_ops *ops;
err = nft_chain_parse_hook(net, nla, &hook, family, create);
err = nft_chain_parse_hook(net, nla, &hook, family, true);
if (err < 0)
return err;
......@@ -1643,8 +1643,7 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
return err;
}
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
bool create)
static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy)
{
const struct nlattr * const *nla = ctx->nla;
struct nft_table *table = ctx->table;
......@@ -1661,7 +1660,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
return -EBUSY;
err = nft_chain_parse_hook(ctx->net, nla, &hook, ctx->family,
create);
false);
if (err < 0)
return err;
......@@ -1761,9 +1760,6 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
u8 policy = NF_ACCEPT;
struct nft_ctx ctx;
u64 handle = 0;
bool create;
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
lockdep_assert_held(&net->nft.commit_mutex);
......@@ -1828,10 +1824,10 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk,
if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
return nf_tables_updchain(&ctx, genmask, policy, create);
return nf_tables_updchain(&ctx, genmask, policy);
}
return nf_tables_addchain(&ctx, family, genmask, policy, create);
return nf_tables_addchain(&ctx, family, genmask, policy);
}
static int nf_tables_delchain(struct net *net, struct sock *nlsk,
......@@ -2529,13 +2525,10 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
struct nlattr *tmp;
unsigned int size, i, n, ulen = 0, usize = 0;
int err, rem;
bool create;
u64 handle, pos_handle;
lockdep_assert_held(&net->nft.commit_mutex);
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_TABLE]);
......@@ -2565,7 +2558,8 @@ static int nf_tables_newrule(struct net *net, struct sock *nlsk,
else
return -EOPNOTSUPP;
} else {
if (!create || nlh->nlmsg_flags & NLM_F_REPLACE)
if (!(nlh->nlmsg_flags & NLM_F_CREATE) ||
nlh->nlmsg_flags & NLM_F_REPLACE)
return -EINVAL;
handle = nf_tables_alloc_handle(table);
......@@ -3361,7 +3355,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
struct nft_ctx ctx;
char *name;
unsigned int size;
bool create;
u64 timeout;
u32 ktype, dtype, flags, policy, gc_int, objtype;
struct nft_set_desc desc;
......@@ -3462,8 +3455,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
return err;
}
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family, genmask);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_TABLE]);
......@@ -4029,7 +4020,6 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr)
{
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
const struct nft_set_ext *ext;
struct nft_data_desc desc;
struct nft_set_elem elem;
struct sk_buff *skb;
......@@ -4063,7 +4053,6 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return PTR_ERR(priv);
elem.priv = priv;
ext = nft_set_elem_ext(set, &elem);
err = -ENOMEM;
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
......@@ -5940,13 +5929,13 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
if (!net)
return 0;
nfnl_lock(NFNL_SUBSYS_NFTABLES);
mutex_lock(&net->nft.commit_mutex);
list_for_each_entry(table, &net->nft.tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) {
nft_flowtable_event(event, dev, flowtable);
}
}
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
mutex_unlock(&net->nft.commit_mutex);
put_net(net);
return NOTIFY_DONE;
}
......
......@@ -120,6 +120,20 @@ struct nft_jumpstack {
struct nft_rule *const *rules;
};
static void expr_call_ops_eval(const struct nft_expr *expr,
struct nft_regs *regs,
struct nft_pktinfo *pkt)
{
unsigned long e = (unsigned long)expr->ops->eval;
if (e == (unsigned long)nft_meta_get_eval)
nft_meta_get_eval(expr, regs, pkt);
else if (e == (unsigned long)nft_lookup_eval)
nft_lookup_eval(expr, regs, pkt);
else
expr->ops->eval(expr, regs, pkt);
}
unsigned int
nft_do_chain(struct nft_pktinfo *pkt, void *priv)
{
......@@ -153,7 +167,7 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv)
nft_cmp_fast_eval(expr, &regs);
else if (expr->ops != &nft_payload_fast_ops ||
!nft_payload_fast_eval(expr, &regs, pkt))
expr->ops->eval(expr, &regs, pkt);
expr_call_ops_eval(expr, &regs, pkt);
if (regs.verdict.code != NFT_CONTINUE)
break;
......
......@@ -503,7 +503,6 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl,
return err;
}
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
static struct ctnl_timeout *
ctnl_timeout_find_get(struct net *net, const char *name)
{
......@@ -534,7 +533,6 @@ static void ctnl_timeout_put(struct ctnl_timeout *timeout)
module_put(THIS_MODULE);
}
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = {
[IPCTNL_MSG_TIMEOUT_NEW] = { .call = cttimeout_new_timeout,
......@@ -605,10 +603,8 @@ static int __init cttimeout_init(void)
"nfnetlink.\n");
goto err_out;
}
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, ctnl_timeout_find_get);
RCU_INIT_POINTER(nf_ct_timeout_put_hook, ctnl_timeout_put);
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
return 0;
err_out:
......@@ -621,11 +617,9 @@ static void __exit cttimeout_exit(void)
nfnetlink_subsys_unregister(&cttimeout_subsys);
unregister_pernet_subsys(&cttimeout_ops);
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
synchronize_rcu();
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
}
module_init(cttimeout_init);
......
......@@ -18,7 +18,14 @@
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_log.h>
#include <linux/netfilter/nf_osf.h>
#include <linux/netfilter/nfnetlink_osf.h>
/*
* Indexed by dont-fragment bit.
* It is the only constant value in the fingerprint.
*/
struct list_head nf_osf_fingers[2];
EXPORT_SYMBOL_GPL(nf_osf_fingers);
static inline int nf_osf_ttl(const struct sk_buff *skb,
int ttl_check, unsigned char f_ttl)
......@@ -249,4 +256,181 @@ nf_osf_match(const struct sk_buff *skb, u_int8_t family,
}
EXPORT_SYMBOL_GPL(nf_osf_match);
const char *nf_osf_find(const struct sk_buff *skb,
const struct list_head *nf_osf_fingers)
{
const struct iphdr *ip = ip_hdr(skb);
const struct nf_osf_user_finger *f;
unsigned char opts[MAX_IPOPTLEN];
const struct nf_osf_finger *kf;
struct nf_osf_hdr_ctx ctx;
const struct tcphdr *tcp;
const char *genre = NULL;
memset(&ctx, 0, sizeof(ctx));
tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts);
if (!tcp)
return false;
list_for_each_entry_rcu(kf, &nf_osf_fingers[ctx.df], finger_entry) {
f = &kf->finger;
if (!nf_osf_match_one(skb, f, -1, &ctx))
continue;
genre = f->genre;
break;
}
return genre;
}
EXPORT_SYMBOL_GPL(nf_osf_find);
static const struct nla_policy nfnl_osf_policy[OSF_ATTR_MAX + 1] = {
[OSF_ATTR_FINGER] = { .len = sizeof(struct nf_osf_user_finger) },
};
static int nfnl_osf_add_callback(struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[],
struct netlink_ext_ack *extack)
{
struct nf_osf_user_finger *f;
struct nf_osf_finger *kf = NULL, *sf;
int err = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
if (!(nlh->nlmsg_flags & NLM_F_CREATE))
return -EINVAL;
f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
kf = kmalloc(sizeof(struct nf_osf_finger), GFP_KERNEL);
if (!kf)
return -ENOMEM;
memcpy(&kf->finger, f, sizeof(struct nf_osf_user_finger));
list_for_each_entry(sf, &nf_osf_fingers[!!f->df], finger_entry) {
if (memcmp(&sf->finger, f, sizeof(struct nf_osf_user_finger)))
continue;
kfree(kf);
kf = NULL;
if (nlh->nlmsg_flags & NLM_F_EXCL)
err = -EEXIST;
break;
}
/*
* We are protected by nfnl mutex.
*/
if (kf)
list_add_tail_rcu(&kf->finger_entry, &nf_osf_fingers[!!f->df]);
return err;
}
static int nfnl_osf_remove_callback(struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[],
struct netlink_ext_ack *extack)
{
struct nf_osf_user_finger *f;
struct nf_osf_finger *sf;
int err = -ENOENT;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
list_for_each_entry(sf, &nf_osf_fingers[!!f->df], finger_entry) {
if (memcmp(&sf->finger, f, sizeof(struct nf_osf_user_finger)))
continue;
/*
* We are protected by nfnl mutex.
*/
list_del_rcu(&sf->finger_entry);
kfree_rcu(sf, rcu_head);
err = 0;
break;
}
return err;
}
static const struct nfnl_callback nfnl_osf_callbacks[OSF_MSG_MAX] = {
[OSF_MSG_ADD] = {
.call = nfnl_osf_add_callback,
.attr_count = OSF_ATTR_MAX,
.policy = nfnl_osf_policy,
},
[OSF_MSG_REMOVE] = {
.call = nfnl_osf_remove_callback,
.attr_count = OSF_ATTR_MAX,
.policy = nfnl_osf_policy,
},
};
static const struct nfnetlink_subsystem nfnl_osf_subsys = {
.name = "osf",
.subsys_id = NFNL_SUBSYS_OSF,
.cb_count = OSF_MSG_MAX,
.cb = nfnl_osf_callbacks,
};
static int __init nfnl_osf_init(void)
{
int err = -EINVAL;
int i;
for (i = 0; i < ARRAY_SIZE(nf_osf_fingers); ++i)
INIT_LIST_HEAD(&nf_osf_fingers[i]);
err = nfnetlink_subsys_register(&nfnl_osf_subsys);
if (err < 0) {
pr_err("Failed to register OSF nsfnetlink helper (%d)\n", err);
goto err_out_exit;
}
return 0;
err_out_exit:
return err;
}
static void __exit nfnl_osf_fini(void)
{
struct nf_osf_finger *f;
int i;
nfnetlink_subsys_unregister(&nfnl_osf_subsys);
rcu_read_lock();
for (i = 0; i < ARRAY_SIZE(nf_osf_fingers); ++i) {
list_for_each_entry_rcu(f, &nf_osf_fingers[i], finger_entry) {
list_del_rcu(&f->finger_entry);
kfree_rcu(f, rcu_head);
}
}
rcu_read_unlock();
rcu_barrier();
}
module_init(nfnl_osf_init);
module_exit(nfnl_osf_fini);
MODULE_LICENSE("GPL");
......@@ -26,9 +26,9 @@ struct nft_lookup {
struct nft_set_binding binding;
};
static void nft_lookup_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
void nft_lookup_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_lookup *priv = nft_expr_priv(expr);
const struct nft_set *set = priv->set;
......
......@@ -41,9 +41,9 @@ static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state);
#include "../bridge/br_private.h"
#endif
static void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_meta *priv = nft_expr_priv(expr);
const struct sk_buff *skb = pkt->skb;
......
......@@ -237,10 +237,8 @@ static int nft_ng_random_map_init(const struct nft_ctx *ctx,
priv->map = nft_set_lookup_global(ctx->net, ctx->table,
tb[NFTA_NG_SET_NAME],
tb[NFTA_NG_SET_ID], genmask);
if (IS_ERR(priv->map))
return PTR_ERR(priv->map);
return 0;
return PTR_ERR_OR_ZERO(priv->map);
}
static int nft_ng_random_dump(struct sk_buff *skb, const struct nft_expr *expr)
......
#include <net/ip.h>
#include <net/tcp.h>
#include <net/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink_osf.h>
#define OSF_GENRE_SIZE 32
struct nft_osf {
enum nft_registers dreg:8;
};
static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = {
[NFTA_OSF_DREG] = { .type = NLA_U32 },
};
static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_osf *priv = nft_expr_priv(expr);
u32 *dest = &regs->data[priv->dreg];
struct sk_buff *skb = pkt->skb;
const struct tcphdr *tcp;
struct tcphdr _tcph;
const char *os_name;
tcp = skb_header_pointer(skb, ip_hdrlen(skb),
sizeof(struct tcphdr), &_tcph);
if (!tcp) {
regs->verdict.code = NFT_BREAK;
return;
}
if (!tcp->syn) {
regs->verdict.code = NFT_BREAK;
return;
}
os_name = nf_osf_find(skb, nf_osf_fingers);
if (!os_name)
strncpy((char *)dest, "unknown", IFNAMSIZ);
else
strncpy((char *)dest, os_name, IFNAMSIZ);
}
static int nft_osf_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_osf *priv = nft_expr_priv(expr);
int err;
priv->dreg = nft_parse_register(tb[NFTA_OSF_DREG]);
err = nft_validate_register_store(ctx, priv->dreg, NULL,
NFTA_DATA_VALUE, OSF_GENRE_SIZE);
if (err < 0)
return err;
return 0;
}
static int nft_osf_dump(struct sk_buff *skb, const struct nft_expr *expr)
{
const struct nft_osf *priv = nft_expr_priv(expr);
if (nft_dump_register(skb, NFTA_OSF_DREG, priv->dreg))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_osf_type;
static const struct nft_expr_ops nft_osf_op = {
.eval = nft_osf_eval,
.size = NFT_EXPR_SIZE(sizeof(struct nft_osf)),
.init = nft_osf_init,
.dump = nft_osf_dump,
.type = &nft_osf_type,
};
static struct nft_expr_type nft_osf_type __read_mostly = {
.ops = &nft_osf_op,
.name = "osf",
.owner = THIS_MODULE,
.policy = nft_osf_policy,
.maxattr = NFTA_OSF_MAX,
};
static int __init nft_osf_module_init(void)
{
return nft_register_expr(&nft_osf_type);
}
static void __exit nft_osf_module_exit(void)
{
return nft_unregister_expr(&nft_osf_type);
}
module_init(nft_osf_module_init);
module_exit(nft_osf_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>");
MODULE_ALIAS_NFT_EXPR("osf");
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/module.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tproxy.h>
#include <net/inet_sock.h>
#include <net/tcp.h>
#include <linux/if_ether.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
#endif
struct nft_tproxy {
enum nft_registers sreg_addr:8;
enum nft_registers sreg_port:8;
u8 family;
};
static void nft_tproxy_eval_v4(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_tproxy *priv = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb;
const struct iphdr *iph = ip_hdr(skb);
struct udphdr _hdr, *hp;
__be32 taddr = 0;
__be16 tport = 0;
struct sock *sk;
hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
if (!hp) {
regs->verdict.code = NFT_BREAK;
return;
}
/* check if there's an ongoing connection on the packet addresses, this
* happens if the redirect already happened and the current packet
* belongs to an already established connection
*/
sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
iph->saddr, iph->daddr,
hp->source, hp->dest,
skb->dev, NF_TPROXY_LOOKUP_ESTABLISHED);
if (priv->sreg_addr)
taddr = regs->data[priv->sreg_addr];
taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr);
if (priv->sreg_port)
tport = regs->data[priv->sreg_port];
if (!tport)
tport = hp->dest;
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
if (sk && sk->sk_state == TCP_TIME_WAIT) {
/* reopening a TIME_WAIT connection needs special handling */
sk = nf_tproxy_handle_time_wait4(nft_net(pkt), skb, taddr, tport, sk);
} else if (!sk) {
/* no, there's no established connection, check if
* there's a listener on the redirected addr/port
*/
sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
iph->saddr, taddr,
hp->source, tport,
skb->dev, NF_TPROXY_LOOKUP_LISTENER);
}
if (sk && nf_tproxy_sk_is_transparent(sk))
nf_tproxy_assign_sock(skb, sk);
else
regs->verdict.code = NFT_BREAK;
}
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
static void nft_tproxy_eval_v6(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_tproxy *priv = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb;
const struct ipv6hdr *iph = ipv6_hdr(skb);
struct in6_addr taddr = {0};
int thoff = pkt->xt.thoff;
struct udphdr _hdr, *hp;
__be16 tport = 0;
struct sock *sk;
int l4proto;
if (!pkt->tprot_set) {
regs->verdict.code = NFT_BREAK;
return;
}
l4proto = pkt->tprot;
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
if (hp == NULL) {
regs->verdict.code = NFT_BREAK;
return;
}
/* check if there's an ongoing connection on the packet addresses, this
* happens if the redirect already happened and the current packet
* belongs to an already established connection
*/
sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff, l4proto,
&iph->saddr, &iph->daddr,
hp->source, hp->dest,
nft_in(pkt), NF_TPROXY_LOOKUP_ESTABLISHED);
if (priv->sreg_addr)
memcpy(&taddr, &regs->data[priv->sreg_addr], sizeof(taddr));
taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr);
if (priv->sreg_port)
tport = regs->data[priv->sreg_port];
if (!tport)
tport = hp->dest;
/* UDP has no TCP_TIME_WAIT state, so we never enter here */
if (sk && sk->sk_state == TCP_TIME_WAIT) {
/* reopening a TIME_WAIT connection needs special handling */
sk = nf_tproxy_handle_time_wait6(skb, l4proto, thoff,
nft_net(pkt),
&taddr,
tport,
sk);
} else if (!sk) {
/* no there's no established connection, check if
* there's a listener on the redirected addr/port
*/
sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff,
l4proto, &iph->saddr, &taddr,
hp->source, tport,
nft_in(pkt), NF_TPROXY_LOOKUP_LISTENER);
}
/* NOTE: assign_sock consumes our sk reference */
if (sk && nf_tproxy_sk_is_transparent(sk))
nf_tproxy_assign_sock(skb, sk);
else
regs->verdict.code = NFT_BREAK;
}
#endif
static void nft_tproxy_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_tproxy *priv = nft_expr_priv(expr);
switch (nft_pf(pkt)) {
case NFPROTO_IPV4:
switch (priv->family) {
case NFPROTO_IPV4:
case NFPROTO_UNSPEC:
nft_tproxy_eval_v4(expr, regs, pkt);
return;
}
break;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case NFPROTO_IPV6:
switch (priv->family) {
case NFPROTO_IPV6:
case NFPROTO_UNSPEC:
nft_tproxy_eval_v6(expr, regs, pkt);
return;
}
#endif
}
regs->verdict.code = NFT_BREAK;
}
static const struct nla_policy nft_tproxy_policy[NFTA_TPROXY_MAX + 1] = {
[NFTA_TPROXY_FAMILY] = { .type = NLA_U32 },
[NFTA_TPROXY_REG_ADDR] = { .type = NLA_U32 },
[NFTA_TPROXY_REG_PORT] = { .type = NLA_U32 },
};
static int nft_tproxy_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_tproxy *priv = nft_expr_priv(expr);
unsigned int alen = 0;
int err;
if (!tb[NFTA_TPROXY_FAMILY] ||
(!tb[NFTA_TPROXY_REG_ADDR] && !tb[NFTA_TPROXY_REG_PORT]))
return -EINVAL;
priv->family = ntohl(nla_get_be32(tb[NFTA_TPROXY_FAMILY]));
switch (ctx->family) {
case NFPROTO_IPV4:
if (priv->family != NFPROTO_IPV4)
return -EINVAL;
break;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case NFPROTO_IPV6:
if (priv->family != NFPROTO_IPV6)
return -EINVAL;
break;
#endif
case NFPROTO_INET:
break;
default:
return -EOPNOTSUPP;
}
/* Address is specified but the rule family is not set accordingly */
if (priv->family == NFPROTO_UNSPEC && tb[NFTA_TPROXY_REG_ADDR])
return -EINVAL;
switch (priv->family) {
case NFPROTO_IPV4:
alen = FIELD_SIZEOF(union nf_inet_addr, in);
err = nf_defrag_ipv4_enable(ctx->net);
if (err)
return err;
break;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
case NFPROTO_IPV6:
alen = FIELD_SIZEOF(union nf_inet_addr, in6);
err = nf_defrag_ipv6_enable(ctx->net);
if (err)
return err;
break;
#endif
case NFPROTO_UNSPEC:
/* No address is specified here */
err = nf_defrag_ipv4_enable(ctx->net);
if (err)
return err;
#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
err = nf_defrag_ipv6_enable(ctx->net);
if (err)
return err;
#endif
break;
default:
return -EOPNOTSUPP;
}
if (tb[NFTA_TPROXY_REG_ADDR]) {
priv->sreg_addr = nft_parse_register(tb[NFTA_TPROXY_REG_ADDR]);
err = nft_validate_register_load(priv->sreg_addr, alen);
if (err < 0)
return err;
}
if (tb[NFTA_TPROXY_REG_PORT]) {
priv->sreg_port = nft_parse_register(tb[NFTA_TPROXY_REG_PORT]);
err = nft_validate_register_load(priv->sreg_port, sizeof(u16));
if (err < 0)
return err;
}
return 0;
}
static int nft_tproxy_dump(struct sk_buff *skb,
const struct nft_expr *expr)
{
const struct nft_tproxy *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_TPROXY_FAMILY, htonl(priv->family)))
return -1;
if (priv->sreg_addr &&
nft_dump_register(skb, NFTA_TPROXY_REG_ADDR, priv->sreg_addr))
return -1;
if (priv->sreg_port &&
nft_dump_register(skb, NFTA_TPROXY_REG_PORT, priv->sreg_port))
return -1;
return 0;
}
static struct nft_expr_type nft_tproxy_type;
static const struct nft_expr_ops nft_tproxy_ops = {
.type = &nft_tproxy_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_tproxy)),
.eval = nft_tproxy_eval,
.init = nft_tproxy_init,
.dump = nft_tproxy_dump,
};
static struct nft_expr_type nft_tproxy_type __read_mostly = {
.name = "tproxy",
.ops = &nft_tproxy_ops,
.policy = nft_tproxy_policy,
.maxattr = NFTA_TPROXY_MAX,
.owner = THIS_MODULE,
};
static int __init nft_tproxy_module_init(void)
{
return nft_register_expr(&nft_tproxy_type);
}
static void __exit nft_tproxy_module_exit(void)
{
nft_unregister_expr(&nft_tproxy_type);
}
module_init(nft_tproxy_module_init);
module_exit(nft_tproxy_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Máté Eckl");
MODULE_DESCRIPTION("nf_tables tproxy support module");
MODULE_ALIAS_NFT_EXPR("tproxy");
/* SPDX-License-Identifier: GPL-2.0 */
#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>
#include <net/dst_metadata.h>
#include <net/ip_tunnels.h>
#include <net/vxlan.h>
#include <net/erspan.h>
struct nft_tunnel {
enum nft_tunnel_keys key:8;
enum nft_registers dreg:8;
};
static void nft_tunnel_get_eval(const struct nft_expr *expr,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct nft_tunnel *priv = nft_expr_priv(expr);
u32 *dest = &regs->data[priv->dreg];
struct ip_tunnel_info *tun_info;
tun_info = skb_tunnel_info(pkt->skb);
switch (priv->key) {
case NFT_TUNNEL_PATH:
nft_reg_store8(dest, !!tun_info);
break;
case NFT_TUNNEL_ID:
if (!tun_info) {
regs->verdict.code = NFT_BREAK;
return;
}
*dest = ntohl(tunnel_id_to_key32(tun_info->key.tun_id));
break;
default:
WARN_ON(1);
regs->verdict.code = NFT_BREAK;
}
}
static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = {
[NFTA_TUNNEL_KEY] = { .type = NLA_U32 },
[NFTA_TUNNEL_DREG] = { .type = NLA_U32 },
};
static int nft_tunnel_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_tunnel *priv = nft_expr_priv(expr);
u32 len;
if (!tb[NFTA_TUNNEL_KEY] &&
!tb[NFTA_TUNNEL_DREG])
return -EINVAL;
priv->key = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY]));
switch (priv->key) {
case NFT_TUNNEL_PATH:
len = sizeof(u8);
break;
case NFT_TUNNEL_ID:
len = sizeof(u32);
break;
default:
return -EOPNOTSUPP;
}
priv->dreg = nft_parse_register(tb[NFTA_TUNNEL_DREG]);
return nft_validate_register_store(ctx, priv->dreg, NULL,
NFT_DATA_VALUE, len);
}
static int nft_tunnel_get_dump(struct sk_buff *skb,
const struct nft_expr *expr)
{
const struct nft_tunnel *priv = nft_expr_priv(expr);
if (nla_put_be32(skb, NFTA_TUNNEL_KEY, htonl(priv->key)))
goto nla_put_failure;
if (nft_dump_register(skb, NFTA_TUNNEL_DREG, priv->dreg))
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static struct nft_expr_type nft_tunnel_type;
static const struct nft_expr_ops nft_tunnel_get_ops = {
.type = &nft_tunnel_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_tunnel)),
.eval = nft_tunnel_get_eval,
.init = nft_tunnel_get_init,
.dump = nft_tunnel_get_dump,
};
static struct nft_expr_type nft_tunnel_type __read_mostly = {
.name = "tunnel",
.ops = &nft_tunnel_get_ops,
.policy = nft_tunnel_policy,
.maxattr = NFTA_TUNNEL_MAX,
.owner = THIS_MODULE,
};
struct nft_tunnel_opts {
union {
struct vxlan_metadata vxlan;
struct erspan_metadata erspan;
} u;
u32 len;
__be16 flags;
};
struct nft_tunnel_obj {
struct metadata_dst *md;
struct nft_tunnel_opts opts;
};
static const struct nla_policy nft_tunnel_ip_policy[NFTA_TUNNEL_KEY_IP_MAX + 1] = {
[NFTA_TUNNEL_KEY_IP_SRC] = { .type = NLA_U32 },
[NFTA_TUNNEL_KEY_IP_DST] = { .type = NLA_U32 },
};
static int nft_tunnel_obj_ip_init(const struct nft_ctx *ctx,
const struct nlattr *attr,
struct ip_tunnel_info *info)
{
struct nlattr *tb[NFTA_TUNNEL_KEY_IP_MAX + 1];
int err;
err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_IP_MAX, attr,
nft_tunnel_ip_policy, NULL);
if (err < 0)
return err;
if (!tb[NFTA_TUNNEL_KEY_IP_DST])
return -EINVAL;
if (tb[NFTA_TUNNEL_KEY_IP_SRC])
info->key.u.ipv4.src = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP_SRC]);
if (tb[NFTA_TUNNEL_KEY_IP_DST])
info->key.u.ipv4.dst = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP_DST]);
return 0;
}
static const struct nla_policy nft_tunnel_ip6_policy[NFTA_TUNNEL_KEY_IP6_MAX + 1] = {
[NFTA_TUNNEL_KEY_IP6_SRC] = { .len = sizeof(struct in6_addr), },
[NFTA_TUNNEL_KEY_IP6_DST] = { .len = sizeof(struct in6_addr), },
[NFTA_TUNNEL_KEY_IP6_FLOWLABEL] = { .type = NLA_U32, }
};
static int nft_tunnel_obj_ip6_init(const struct nft_ctx *ctx,
const struct nlattr *attr,
struct ip_tunnel_info *info)
{
struct nlattr *tb[NFTA_TUNNEL_KEY_IP6_MAX + 1];
int err;
err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_IP6_MAX, attr,
nft_tunnel_ip6_policy, NULL);
if (err < 0)
return err;
if (!tb[NFTA_TUNNEL_KEY_IP6_DST])
return -EINVAL;
if (tb[NFTA_TUNNEL_KEY_IP6_SRC]) {
memcpy(&info->key.u.ipv6.src,
nla_data(tb[NFTA_TUNNEL_KEY_IP6_SRC]),
sizeof(struct in6_addr));
}
if (tb[NFTA_TUNNEL_KEY_IP6_DST]) {
memcpy(&info->key.u.ipv6.dst,
nla_data(tb[NFTA_TUNNEL_KEY_IP6_DST]),
sizeof(struct in6_addr));
}
if (tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL])
info->key.label = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]);
info->mode |= IP_TUNNEL_INFO_IPV6;
return 0;
}
static const struct nla_policy nft_tunnel_opts_vxlan_policy[NFTA_TUNNEL_KEY_VXLAN_MAX + 1] = {
[NFTA_TUNNEL_KEY_VXLAN_GBP] = { .type = NLA_U32 },
};
static int nft_tunnel_obj_vxlan_init(const struct nlattr *attr,
struct nft_tunnel_opts *opts)
{
struct nlattr *tb[NFTA_TUNNEL_KEY_VXLAN_MAX + 1];
int err;
err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_VXLAN_MAX, attr,
nft_tunnel_opts_vxlan_policy, NULL);
if (err < 0)
return err;
if (!tb[NFTA_TUNNEL_KEY_VXLAN_GBP])
return -EINVAL;
opts->u.vxlan.gbp = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_VXLAN_GBP]));
opts->len = sizeof(struct vxlan_metadata);
opts->flags = TUNNEL_VXLAN_OPT;
return 0;
}
static const struct nla_policy nft_tunnel_opts_erspan_policy[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = {
[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX] = { .type = NLA_U32 },
[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 },
[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID] = { .type = NLA_U8 },
};
static int nft_tunnel_obj_erspan_init(const struct nlattr *attr,
struct nft_tunnel_opts *opts)
{
struct nlattr *tb[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1];
uint8_t hwid, dir;
int err, version;
err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_ERSPAN_MAX, attr,
nft_tunnel_opts_erspan_policy, NULL);
if (err < 0)
return err;
version = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION]));
switch (version) {
case ERSPAN_VERSION:
if (!tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX])
return -EINVAL;
opts->u.erspan.u.index =
nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]);
break;
case ERSPAN_VERSION2:
if (!tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] ||
!tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID])
return -EINVAL;
hwid = nla_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]);
dir = nla_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR]);
set_hwid(&opts->u.erspan.u.md2, hwid);
opts->u.erspan.u.md2.dir = dir;
break;
default:
return -EOPNOTSUPP;
}
opts->u.erspan.version = version;
opts->len = sizeof(struct erspan_metadata);
opts->flags = TUNNEL_ERSPAN_OPT;
return 0;
}
static const struct nla_policy nft_tunnel_opts_policy[NFTA_TUNNEL_KEY_OPTS_MAX + 1] = {
[NFTA_TUNNEL_KEY_OPTS_VXLAN] = { .type = NLA_NESTED, },
[NFTA_TUNNEL_KEY_OPTS_ERSPAN] = { .type = NLA_NESTED, },
};
static int nft_tunnel_obj_opts_init(const struct nft_ctx *ctx,
const struct nlattr *attr,
struct ip_tunnel_info *info,
struct nft_tunnel_opts *opts)
{
struct nlattr *tb[NFTA_TUNNEL_KEY_OPTS_MAX + 1];
int err;
err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_OPTS_MAX, attr,
nft_tunnel_opts_policy, NULL);
if (err < 0)
return err;
if (tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]) {
err = nft_tunnel_obj_vxlan_init(tb[NFTA_TUNNEL_KEY_OPTS_VXLAN],
opts);
} else if (tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]) {
err = nft_tunnel_obj_erspan_init(tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN],
opts);
} else {
return -EOPNOTSUPP;
}
return err;
}
static const struct nla_policy nft_tunnel_key_policy[NFTA_TUNNEL_KEY_MAX + 1] = {
[NFTA_TUNNEL_KEY_IP] = { .type = NLA_NESTED, },
[NFTA_TUNNEL_KEY_IP6] = { .type = NLA_NESTED, },
[NFTA_TUNNEL_KEY_ID] = { .type = NLA_U32, },
[NFTA_TUNNEL_KEY_FLAGS] = { .type = NLA_U32, },
[NFTA_TUNNEL_KEY_TOS] = { .type = NLA_U8, },
[NFTA_TUNNEL_KEY_TTL] = { .type = NLA_U8, },
[NFTA_TUNNEL_KEY_OPTS] = { .type = NLA_NESTED, },
};
static int nft_tunnel_obj_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[],
struct nft_object *obj)
{
struct nft_tunnel_obj *priv = nft_obj_data(obj);
struct ip_tunnel_info info;
struct metadata_dst *md;
int err;
if (!tb[NFTA_TUNNEL_KEY_ID])
return -EINVAL;
memset(&info, 0, sizeof(info));
info.mode = IP_TUNNEL_INFO_TX;
info.key.tun_id = key32_to_tunnel_id(nla_get_be32(tb[NFTA_TUNNEL_KEY_ID]));
info.key.tun_flags = TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_NOCACHE;
if (tb[NFTA_TUNNEL_KEY_IP]) {
err = nft_tunnel_obj_ip_init(ctx, tb[NFTA_TUNNEL_KEY_IP], &info);
if (err < 0)
return err;
} else if (tb[NFTA_TUNNEL_KEY_IP6]) {
err = nft_tunnel_obj_ip6_init(ctx, tb[NFTA_TUNNEL_KEY_IP6], &info);
if (err < 0)
return err;
} else {
return -EINVAL;
}
if (tb[NFTA_TUNNEL_KEY_SPORT]) {
info.key.tp_src = nla_get_be16(tb[NFTA_TUNNEL_KEY_SPORT]);
}
if (tb[NFTA_TUNNEL_KEY_DPORT]) {
info.key.tp_dst = nla_get_be16(tb[NFTA_TUNNEL_KEY_DPORT]);
}
if (tb[NFTA_TUNNEL_KEY_FLAGS]) {
u32 tun_flags;
tun_flags = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_FLAGS]));
if (tun_flags & ~NFT_TUNNEL_F_MASK)
return -EOPNOTSUPP;
if (tun_flags & NFT_TUNNEL_F_ZERO_CSUM_TX)
info.key.tun_flags &= ~TUNNEL_CSUM;
if (tun_flags & NFT_TUNNEL_F_DONT_FRAGMENT)
info.key.tun_flags |= TUNNEL_DONT_FRAGMENT;
if (tun_flags & NFT_TUNNEL_F_SEQ_NUMBER)
info.key.tun_flags |= TUNNEL_SEQ;
}
if (tb[NFTA_TUNNEL_KEY_TOS])
info.key.tos = nla_get_u8(tb[NFTA_TUNNEL_KEY_TOS]);
if (tb[NFTA_TUNNEL_KEY_TTL])
info.key.ttl = nla_get_u8(tb[NFTA_TUNNEL_KEY_TTL]);
else
info.key.ttl = U8_MAX;
if (tb[NFTA_TUNNEL_KEY_OPTS]) {
err = nft_tunnel_obj_opts_init(ctx, tb[NFTA_TUNNEL_KEY_OPTS],
&info, &priv->opts);
if (err < 0)
return err;
}
md = metadata_dst_alloc(priv->opts.len, METADATA_IP_TUNNEL, GFP_KERNEL);
if (!md)
return -ENOMEM;
memcpy(&md->u.tun_info, &info, sizeof(info));
ip_tunnel_info_opts_set(&md->u.tun_info, &priv->opts.u, priv->opts.len,
priv->opts.flags);
priv->md = md;
return 0;
}
static inline void nft_tunnel_obj_eval(struct nft_object *obj,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
struct nft_tunnel_obj *priv = nft_obj_data(obj);
struct sk_buff *skb = pkt->skb;
skb_dst_drop(skb);
dst_hold((struct dst_entry *) priv->md);
skb_dst_set(skb, (struct dst_entry *) priv->md);
}
static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info)
{
struct nlattr *nest;
if (info->mode & IP_TUNNEL_INFO_IPV6) {
nest = nla_nest_start(skb, NFTA_TUNNEL_KEY_IP6);
if (!nest)
return -1;
if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC, &info->key.u.ipv6.src) < 0 ||
nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST, &info->key.u.ipv6.dst) < 0 ||
nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL, info->key.label))
return -1;
nla_nest_end(skb, nest);
} else {
nest = nla_nest_start(skb, NFTA_TUNNEL_KEY_IP);
if (!nest)
return -1;
if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC, info->key.u.ipv4.src) < 0 ||
nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST, info->key.u.ipv4.dst) < 0)
return -1;
nla_nest_end(skb, nest);
}
return 0;
}
static int nft_tunnel_opts_dump(struct sk_buff *skb,
struct nft_tunnel_obj *priv)
{
struct nft_tunnel_opts *opts = &priv->opts;
struct nlattr *nest;
nest = nla_nest_start(skb, NFTA_TUNNEL_KEY_OPTS);
if (!nest)
return -1;
if (opts->flags & TUNNEL_VXLAN_OPT) {
if (nla_put_be32(skb, NFTA_TUNNEL_KEY_VXLAN_GBP,
htonl(opts->u.vxlan.gbp)))
return -1;
} else if (opts->flags & TUNNEL_ERSPAN_OPT) {
switch (opts->u.erspan.version) {
case ERSPAN_VERSION:
if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX,
opts->u.erspan.u.index))
return -1;
break;
case ERSPAN_VERSION2:
if (nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_HWID,
get_hwid(&opts->u.erspan.u.md2)) ||
nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_DIR,
opts->u.erspan.u.md2.dir))
return -1;
break;
}
}
nla_nest_end(skb, nest);
return 0;
}
static int nft_tunnel_ports_dump(struct sk_buff *skb,
struct ip_tunnel_info *info)
{
if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, htons(info->key.tp_src)) < 0 ||
nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, htons(info->key.tp_dst)) < 0)
return -1;
return 0;
}
static int nft_tunnel_flags_dump(struct sk_buff *skb,
struct ip_tunnel_info *info)
{
u32 flags = 0;
if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT)
flags |= NFT_TUNNEL_F_DONT_FRAGMENT;
if (!(info->key.tun_flags & TUNNEL_CSUM))
flags |= NFT_TUNNEL_F_ZERO_CSUM_TX;
if (info->key.tun_flags & TUNNEL_SEQ)
flags |= NFT_TUNNEL_F_SEQ_NUMBER;
if (nla_put_be32(skb, NFTA_TUNNEL_KEY_FLAGS, htonl(flags)) < 0)
return -1;
return 0;
}
static int nft_tunnel_obj_dump(struct sk_buff *skb,
struct nft_object *obj, bool reset)
{
struct nft_tunnel_obj *priv = nft_obj_data(obj);
struct ip_tunnel_info *info = &priv->md->u.tun_info;
if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ID,
tunnel_id_to_key32(info->key.tun_id)) ||
nft_tunnel_ip_dump(skb, info) < 0 ||
nft_tunnel_ports_dump(skb, info) < 0 ||
nft_tunnel_flags_dump(skb, info) < 0 ||
nla_put_u8(skb, NFTA_TUNNEL_KEY_TOS, info->key.tos) ||
nla_put_u8(skb, NFTA_TUNNEL_KEY_TTL, info->key.ttl) ||
nft_tunnel_opts_dump(skb, priv) < 0)
goto nla_put_failure;
return 0;
nla_put_failure:
return -1;
}
static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx,
struct nft_object *obj)
{
struct nft_tunnel_obj *priv = nft_obj_data(obj);
metadata_dst_free(priv->md);
}
static struct nft_object_type nft_tunnel_obj_type;
static const struct nft_object_ops nft_tunnel_obj_ops = {
.type = &nft_tunnel_obj_type,
.size = sizeof(struct nft_tunnel_obj),
.eval = nft_tunnel_obj_eval,
.init = nft_tunnel_obj_init,
.destroy = nft_tunnel_obj_destroy,
.dump = nft_tunnel_obj_dump,
};
static struct nft_object_type nft_tunnel_obj_type __read_mostly = {
.type = NFT_OBJECT_TUNNEL,
.ops = &nft_tunnel_obj_ops,
.maxattr = NFTA_TUNNEL_KEY_MAX,
.policy = nft_tunnel_key_policy,
.owner = THIS_MODULE,
};
static int __init nft_tunnel_module_init(void)
{
int err;
err = nft_register_expr(&nft_tunnel_type);
if (err < 0)
return err;
err = nft_register_obj(&nft_tunnel_obj_type);
if (err < 0)
nft_unregister_expr(&nft_tunnel_type);
return err;
}
static void __exit nft_tunnel_module_exit(void)
{
nft_unregister_obj(&nft_tunnel_obj_type);
nft_unregister_expr(&nft_tunnel_type);
}
module_init(nft_tunnel_module_init);
module_exit(nft_tunnel_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_ALIAS_NFT_EXPR("tunnel");
MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_TUNNEL);
......@@ -93,10 +93,8 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par)
/* init private data */
info->data = nf_conncount_init(par->net, par->family, keylen);
if (IS_ERR(info->data))
return PTR_ERR(info->data);
return 0;
return PTR_ERR_OR_ZERO(info->data);
}
static void connlimit_mt_destroy(const struct xt_mtdtor_param *par)
......
......@@ -37,118 +37,6 @@
#include <net/netfilter/nf_log.h>
#include <linux/netfilter/xt_osf.h>
/*
* Indexed by dont-fragment bit.
* It is the only constant value in the fingerprint.
*/
static struct list_head xt_osf_fingers[2];
static const struct nla_policy xt_osf_policy[OSF_ATTR_MAX + 1] = {
[OSF_ATTR_FINGER] = { .len = sizeof(struct xt_osf_user_finger) },
};
static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[],
struct netlink_ext_ack *extack)
{
struct xt_osf_user_finger *f;
struct xt_osf_finger *kf = NULL, *sf;
int err = 0;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
if (!(nlh->nlmsg_flags & NLM_F_CREATE))
return -EINVAL;
f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
kf = kmalloc(sizeof(struct xt_osf_finger), GFP_KERNEL);
if (!kf)
return -ENOMEM;
memcpy(&kf->finger, f, sizeof(struct xt_osf_user_finger));
list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
continue;
kfree(kf);
kf = NULL;
if (nlh->nlmsg_flags & NLM_F_EXCL)
err = -EEXIST;
break;
}
/*
* We are protected by nfnl mutex.
*/
if (kf)
list_add_tail_rcu(&kf->finger_entry, &xt_osf_fingers[!!f->df]);
return err;
}
static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const osf_attrs[],
struct netlink_ext_ack *extack)
{
struct xt_osf_user_finger *f;
struct xt_osf_finger *sf;
int err = -ENOENT;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (!osf_attrs[OSF_ATTR_FINGER])
return -EINVAL;
f = nla_data(osf_attrs[OSF_ATTR_FINGER]);
list_for_each_entry(sf, &xt_osf_fingers[!!f->df], finger_entry) {
if (memcmp(&sf->finger, f, sizeof(struct xt_osf_user_finger)))
continue;
/*
* We are protected by nfnl mutex.
*/
list_del_rcu(&sf->finger_entry);
kfree_rcu(sf, rcu_head);
err = 0;
break;
}
return err;
}
static const struct nfnl_callback xt_osf_nfnetlink_callbacks[OSF_MSG_MAX] = {
[OSF_MSG_ADD] = {
.call = xt_osf_add_callback,
.attr_count = OSF_ATTR_MAX,
.policy = xt_osf_policy,
},
[OSF_MSG_REMOVE] = {
.call = xt_osf_remove_callback,
.attr_count = OSF_ATTR_MAX,
.policy = xt_osf_policy,
},
};
static const struct nfnetlink_subsystem xt_osf_nfnetlink = {
.name = "osf",
.subsys_id = NFNL_SUBSYS_OSF,
.cb_count = OSF_MSG_MAX,
.cb = xt_osf_nfnetlink_callbacks,
};
static bool
xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
{
......@@ -159,7 +47,7 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
return false;
return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p),
xt_out(p), info, net, xt_osf_fingers);
xt_out(p), info, net, nf_osf_fingers);
}
static struct xt_match xt_osf_match = {
......@@ -177,52 +65,21 @@ static struct xt_match xt_osf_match = {
static int __init xt_osf_init(void)
{
int err = -EINVAL;
int i;
for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i)
INIT_LIST_HEAD(&xt_osf_fingers[i]);
err = nfnetlink_subsys_register(&xt_osf_nfnetlink);
if (err < 0) {
pr_err("Failed to register OSF nsfnetlink helper (%d)\n", err);
goto err_out_exit;
}
int err;
err = xt_register_match(&xt_osf_match);
if (err) {
pr_err("Failed to register OS fingerprint "
"matching module (%d)\n", err);
goto err_out_remove;
return err;
}
return 0;
err_out_remove:
nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
err_out_exit:
return err;
}
static void __exit xt_osf_fini(void)
{
struct xt_osf_finger *f;
int i;
nfnetlink_subsys_unregister(&xt_osf_nfnetlink);
xt_unregister_match(&xt_osf_match);
rcu_read_lock();
for (i=0; i<ARRAY_SIZE(xt_osf_fingers); ++i) {
list_for_each_entry_rcu(f, &xt_osf_fingers[i], finger_entry) {
list_del_rcu(&f->finger_entry);
kfree_rcu(f, rcu_head);
}
}
rcu_read_unlock();
rcu_barrier();
}
module_init(xt_osf_init);
......
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