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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
074fb88016
|
@ -1,4 +1,8 @@
|
|||
#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 @@ err_acct:
|
|||
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 @@ err1:
|
|||
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 @@ next_rule:
|
|||
nft_cmp_fast_eval(expr, ®s);
|
||||
else if (expr->ops != &nft_payload_fast_ops ||
|
||||
!nft_payload_fast_eval(expr, ®s, pkt))
|
||||
expr->ops->eval(expr, ®s, pkt);
|
||||
expr_call_ops_eval(expr, ®s, pkt);
|
||||
|
||||
if (regs.verdict.code != NFT_CONTINUE)
|
||||
break;
|
||||
|
|
|
@ -503,7 +503,6 @@ err:
|
|||
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,7 +26,7 @@ struct nft_lookup {
|
|||
struct nft_set_binding binding;
|
||||
};
|
||||
|
||||
static void nft_lookup_eval(const struct nft_expr *expr,
|
||||
void nft_lookup_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ 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,
|
||||
void nft_meta_get_eval(const struct nft_expr *expr,
|
||||
struct nft_regs *regs,
|
||||
const struct nft_pktinfo *pkt)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
#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 = ®s->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");
|
|
@ -0,0 +1,316 @@
|
|||
/* 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, ®s->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");
|
|
@ -0,0 +1,566 @@
|
|||
/* 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 = ®s->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);
|
||||
|
|
Loading…
Reference in New Issue