Commit 7f3579e1 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 net-next:

1) Add nfgenmsg field to nfnetlink's struct nfnl_info and use it.

2) Remove nft_ctx_init_from_elemattr() and nft_ctx_init_from_setattr()
   helper functions.

3) Add the nf_ct_pernet() helper function to fetch the conntrack
   pernetns data area.

4) Expose TCP and UDP flowtable offload timeouts through sysctl,
   from Oz Shlomo.

5) Add nfnetlink_hook subsystem to fetch the netfilter hook
   pipeline configuration, from Florian Westphal. This also includes
   a new field to annotate the hook type as metadata.

6) Fix unsafe memory access to non-linear skbuff in the new SCTP
   chunk support for nft_exthdr, from Phil Sutter.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4e744cb8 c5c6accd
......@@ -77,12 +77,18 @@ struct nf_hook_state {
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);
enum nf_hook_ops_type {
NF_HOOK_OP_UNDEFINED,
NF_HOOK_OP_NF_TABLES,
};
struct nf_hook_ops {
/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u_int8_t pf;
u8 pf;
enum nf_hook_ops_type hook_ops_type:8;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
......
......@@ -11,6 +11,7 @@ struct nfnl_info {
struct net *net;
struct sock *sk;
const struct nlmsghdr *nlh;
const struct nfgenmsg *nfmsg;
struct netlink_ext_ack *extack;
};
......
......@@ -346,6 +346,13 @@ nf_ct_set(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info info)
skb_set_nfct(skb, (unsigned long)ct | info);
}
extern unsigned int nf_conntrack_net_id;
static inline struct nf_conntrack_net *nf_ct_pernet(const struct net *net)
{
return net_generic(net, nf_conntrack_net_id);
}
#define NF_CT_STAT_INC(net, count) __this_cpu_inc((net)->ct.stat->count)
#define NF_CT_STAT_INC_ATOMIC(net, count) this_cpu_inc((net)->ct.stat->count)
#define NF_CT_STAT_ADD_ATOMIC(net, count, v) this_cpu_add((net)->ct.stat->count, (v))
......
......@@ -177,6 +177,8 @@ struct flow_offload {
#define NF_FLOW_TIMEOUT (30 * HZ)
#define nf_flowtable_time_stamp (u32)jiffies
unsigned long flow_offload_get_timeout(struct flow_offload *flow);
static inline __s32 nf_flow_timeout_delta(unsigned int timeout)
{
return (__s32)(timeout - nf_flowtable_time_stamp);
......
......@@ -27,6 +27,10 @@ struct nf_tcp_net {
u8 tcp_loose;
u8 tcp_be_liberal;
u8 tcp_max_retrans;
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
unsigned int offload_timeout;
unsigned int offload_pickup;
#endif
};
enum udp_conntrack {
......@@ -37,6 +41,10 @@ enum udp_conntrack {
struct nf_udp_net {
unsigned int timeouts[UDP_CT_MAX];
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
unsigned int offload_timeout;
unsigned int offload_pickup;
#endif
};
struct nf_icmp_net {
......
......@@ -60,7 +60,8 @@ struct nfgenmsg {
#define NFNL_SUBSYS_CTHELPER 9
#define NFNL_SUBSYS_NFTABLES 10
#define NFNL_SUBSYS_NFT_COMPAT 11
#define NFNL_SUBSYS_COUNT 12
#define NFNL_SUBSYS_HOOK 12
#define NFNL_SUBSYS_COUNT 13
/* Reserved control nfnetlink messages */
#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
......
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _NFNL_HOOK_H_
#define _NFNL_HOOK_H_
enum nfnl_hook_msg_types {
NFNL_MSG_HOOK_GET,
NFNL_MSG_HOOK_MAX,
};
/**
* enum nfnl_hook_attributes - netfilter hook netlink attributes
*
* @NFNLA_HOOK_HOOKNUM: netfilter hook number (NLA_U32)
* @NFNLA_HOOK_PRIORITY: netfilter hook priority (NLA_U32)
* @NFNLA_HOOK_DEV: netdevice name (NLA_STRING)
* @NFNLA_HOOK_FUNCTION_NAME: hook function name (NLA_STRING)
* @NFNLA_HOOK_MODULE_NAME: kernel module that registered this hook (NLA_STRING)
* @NFNLA_HOOK_CHAIN_INFO: basechain hook metadata (NLA_NESTED)
*/
enum nfnl_hook_attributes {
NFNLA_HOOK_UNSPEC,
NFNLA_HOOK_HOOKNUM,
NFNLA_HOOK_PRIORITY,
NFNLA_HOOK_DEV,
NFNLA_HOOK_FUNCTION_NAME,
NFNLA_HOOK_MODULE_NAME,
NFNLA_HOOK_CHAIN_INFO,
__NFNLA_HOOK_MAX
};
#define NFNLA_HOOK_MAX (__NFNLA_HOOK_MAX - 1)
/**
* enum nfnl_hook_chain_info_attributes - chain description
*
* NFNLA_HOOK_INFO_DESC: nft chain and table name (enum nft_table_attributes) (NLA_NESTED)
* NFNLA_HOOK_INFO_TYPE: chain type (enum nfnl_hook_chaintype) (NLA_U32)
*/
enum nfnl_hook_chain_info_attributes {
NFNLA_HOOK_INFO_UNSPEC,
NFNLA_HOOK_INFO_DESC,
NFNLA_HOOK_INFO_TYPE,
__NFNLA_HOOK_INFO_MAX,
};
#define NFNLA_HOOK_INFO_MAX (__NFNLA_HOOK_INFO_MAX - 1)
/**
* enum nfnl_hook_chaintype - chain type
*
* @NFNL_HOOK_TYPE_NFTABLES nf_tables base chain
*/
enum nfnl_hook_chaintype {
NFNL_HOOK_TYPE_NFTABLES = 0x1,
};
#endif /* _NFNL_HOOK_H */
......@@ -19,6 +19,16 @@ config NETFILTER_FAMILY_BRIDGE
config NETFILTER_FAMILY_ARP
bool
config NETFILTER_NETLINK_HOOK
tristate "Netfilter base hook dump support"
depends on NETFILTER_ADVANCED
depends on NF_TABLES
select NETFILTER_NETLINK
help
If this option is enabled, the kernel will include support
to list the base netfilter hooks via NFNETLINK.
This is helpful for debugging.
config NETFILTER_NETLINK_ACCT
tristate "Netfilter NFACCT over NFNETLINK interface"
depends on NETFILTER_ADVANCED
......
......@@ -22,6 +22,7 @@ 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
obj-$(CONFIG_NETFILTER_NETLINK_HOOK) += nfnetlink_hook.o
# connection tracking
obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
......
......@@ -55,8 +55,6 @@
#include "nf_internals.h"
extern unsigned int nf_conntrack_net_id;
__cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];
EXPORT_SYMBOL_GPL(nf_conntrack_locks);
......@@ -87,8 +85,6 @@ static __read_mostly bool nf_conntrack_locks_all;
static struct conntrack_gc_work conntrack_gc_work;
extern unsigned int nf_conntrack_net_id;
void nf_conntrack_lock(spinlock_t *lock) __acquires(lock)
{
/* 1) Acquire the lock */
......@@ -1404,7 +1400,7 @@ static void gc_worker(struct work_struct *work)
continue;
net = nf_ct_net(tmp);
cnet = net_generic(net, nf_conntrack_net_id);
cnet = nf_ct_pernet(net);
if (atomic_read(&cnet->count) < nf_conntrack_max95)
continue;
......@@ -1484,7 +1480,7 @@ __nf_conntrack_alloc(struct net *net,
const struct nf_conntrack_tuple *repl,
gfp_t gfp, u32 hash)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
unsigned int ct_count;
struct nf_conn *ct;
......@@ -1556,7 +1552,7 @@ void nf_conntrack_free(struct nf_conn *ct)
nf_ct_ext_destroy(ct);
kmem_cache_free(nf_conntrack_cachep, ct);
cnet = net_generic(net, nf_conntrack_net_id);
cnet = nf_ct_pernet(net);
smp_mb__before_atomic();
atomic_dec(&cnet->count);
......@@ -1614,7 +1610,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
GFP_ATOMIC);
local_bh_disable();
cnet = net_generic(net, nf_conntrack_net_id);
cnet = nf_ct_pernet(net);
if (cnet->expect_count) {
spin_lock(&nf_conntrack_expect_lock);
exp = nf_ct_find_expectation(net, zone, tuple);
......@@ -2317,7 +2313,7 @@ __nf_ct_unconfirmed_destroy(struct net *net)
void nf_ct_unconfirmed_destroy(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
might_sleep();
......@@ -2333,7 +2329,7 @@ void nf_ct_iterate_cleanup_net(struct net *net,
int (*iter)(struct nf_conn *i, void *data),
void *data, u32 portid, int report)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
struct iter_data d;
might_sleep();
......@@ -2367,7 +2363,7 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data)
down_read(&net_rwsem);
for_each_net(net) {
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
if (atomic_read(&cnet->count) == 0)
continue;
......@@ -2449,7 +2445,7 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list)
i_see_dead_people:
busy = 0;
list_for_each_entry(net, net_exit_list, exit_list) {
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
nf_ct_iterate_cleanup(kill_all, net, 0, 0);
if (atomic_read(&cnet->count) != 0)
......@@ -2733,7 +2729,7 @@ void nf_conntrack_init_end(void)
int nf_conntrack_init_net(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
int ret = -ENOMEM;
int cpu;
......
......@@ -27,8 +27,6 @@
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_extend.h>
extern unsigned int nf_conntrack_net_id;
static DEFINE_MUTEX(nf_ct_ecache_mutex);
#define ECACHE_RETRY_WAIT (HZ/10)
......@@ -348,7 +346,7 @@ EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
void nf_conntrack_ecache_work(struct net *net, enum nf_ct_ecache_state state)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
if (state == NFCT_ECACHE_DESTROY_FAIL &&
!delayed_work_pending(&cnet->ecache_dwork)) {
......@@ -371,7 +369,7 @@ static const struct nf_ct_ext_type event_extend = {
void nf_conntrack_ecache_pernet_init(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
net->ct.sysctl_events = nf_ct_events;
cnet->ct_net = &net->ct;
......@@ -380,7 +378,7 @@ void nf_conntrack_ecache_pernet_init(struct net *net)
void nf_conntrack_ecache_pernet_fini(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
cancel_delayed_work_sync(&cnet->ecache_dwork);
}
......
......@@ -43,8 +43,6 @@ unsigned int nf_ct_expect_max __read_mostly;
static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
static unsigned int nf_ct_expect_hashrnd __read_mostly;
extern unsigned int nf_conntrack_net_id;
/* nf_conntrack_expect helper functions */
void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
u32 portid, int report)
......@@ -58,7 +56,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
hlist_del_rcu(&exp->hnode);
cnet = net_generic(net, nf_conntrack_net_id);
cnet = nf_ct_pernet(net);
cnet->expect_count--;
hlist_del_rcu(&exp->lnode);
......@@ -123,7 +121,7 @@ __nf_ct_expect_find(struct net *net,
const struct nf_conntrack_zone *zone,
const struct nf_conntrack_tuple *tuple)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
struct nf_conntrack_expect *i;
unsigned int h;
......@@ -164,7 +162,7 @@ nf_ct_find_expectation(struct net *net,
const struct nf_conntrack_zone *zone,
const struct nf_conntrack_tuple *tuple)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
struct nf_conntrack_expect *i, *exp = NULL;
unsigned int h;
......@@ -397,7 +395,7 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
master_help->expecting[exp->class]++;
hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
cnet = net_generic(net, nf_conntrack_net_id);
cnet = nf_ct_pernet(net);
cnet->expect_count++;
NF_CT_STAT_INC(net, expect_create);
......@@ -468,7 +466,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
}
}
cnet = net_generic(net, nf_conntrack_net_id);
cnet = nf_ct_pernet(net);
if (cnet->expect_count >= nf_ct_expect_max) {
net_warn_ratelimited("nf_conntrack: expectation table full\n");
ret = -EMFILE;
......
......@@ -43,8 +43,6 @@ MODULE_PARM_DESC(nf_conntrack_helper,
static DEFINE_MUTEX(nf_ct_nat_helpers_mutex);
static struct list_head nf_ct_nat_helpers __read_mostly;
extern unsigned int nf_conntrack_net_id;
/* Stupid hash, but collision free for the default registrations of the
* helpers currently in the kernel. */
static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
......@@ -214,7 +212,7 @@ EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
static struct nf_conntrack_helper *
nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
if (!cnet->sysctl_auto_assign_helper) {
if (cnet->auto_assign_helper_warned)
......@@ -560,7 +558,7 @@ static const struct nf_ct_ext_type helper_extend = {
void nf_conntrack_helper_pernet_init(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
cnet->sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
}
......
......@@ -1528,7 +1528,7 @@ static int ctnetlink_del_conntrack(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const cda[])
{
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u8 family = info->nfmsg->nfgen_family;
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conntrack_zone zone;
......@@ -1541,12 +1541,12 @@ static int ctnetlink_del_conntrack(struct sk_buff *skb,
if (cda[CTA_TUPLE_ORIG])
err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG,
nfmsg->nfgen_family, &zone);
family, &zone);
else if (cda[CTA_TUPLE_REPLY])
err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY,
nfmsg->nfgen_family, &zone);
family, &zone);
else {
u_int8_t u3 = nfmsg->version ? nfmsg->nfgen_family : AF_UNSPEC;
u_int8_t u3 = info->nfmsg->version ? family : AF_UNSPEC;
return ctnetlink_flush_conntrack(info->net, cda,
NETLINK_CB(skb).portid,
......@@ -1586,8 +1586,7 @@ static int ctnetlink_get_conntrack(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const cda[])
{
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int8_t u3 = nfmsg->nfgen_family;
u_int8_t u3 = info->nfmsg->nfgen_family;
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conntrack_zone zone;
......@@ -2363,10 +2362,9 @@ static int ctnetlink_new_conntrack(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const cda[])
{
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct nf_conntrack_tuple otuple, rtuple;
struct nf_conntrack_tuple_hash *h = NULL;
u_int8_t u3 = nfmsg->nfgen_family;
u_int8_t u3 = info->nfmsg->nfgen_family;
struct nf_conntrack_zone zone;
struct nf_conn *ct;
int err;
......@@ -3259,8 +3257,7 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const cda[])
{
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int8_t u3 = nfmsg->nfgen_family;
u_int8_t u3 = info->nfmsg->nfgen_family;
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct nf_conntrack_zone zone;
......@@ -3349,8 +3346,7 @@ static int ctnetlink_del_expect(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const cda[])
{
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int8_t u3 = nfmsg->nfgen_family;
u_int8_t u3 = info->nfmsg->nfgen_family;
struct nf_conntrack_expect *exp;
struct nf_conntrack_tuple tuple;
struct nf_conntrack_zone zone;
......@@ -3601,8 +3597,7 @@ static int ctnetlink_new_expect(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const cda[])
{
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int8_t u3 = nfmsg->nfgen_family;
u_int8_t u3 = info->nfmsg->nfgen_family;
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct nf_conntrack_zone zone;
......
......@@ -42,8 +42,6 @@
#include <net/ipv6.h>
#include <net/inet_frag.h>
extern unsigned int nf_conntrack_net_id;
static DEFINE_MUTEX(nf_ct_proto_mutex);
#ifdef CONFIG_SYSCTL
......@@ -446,7 +444,7 @@ static struct nf_ct_bridge_info *nf_ct_bridge_info;
static int nf_ct_netns_do_get(struct net *net, u8 nfproto)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
bool fixup_needed = false, retry = true;
int err = 0;
retry:
......@@ -531,7 +529,7 @@ static int nf_ct_netns_do_get(struct net *net, u8 nfproto)
static void nf_ct_netns_do_put(struct net *net, u8 nfproto)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
mutex_lock(&nf_ct_proto_mutex);
switch (nfproto) {
......
......@@ -1441,6 +1441,11 @@ void nf_conntrack_tcp_init_net(struct net *net)
* will be started.
*/
tn->tcp_max_retrans = 3;
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
tn->offload_timeout = 30 * HZ;
tn->offload_pickup = 120 * HZ;
#endif
}
const struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp =
......
......@@ -270,6 +270,11 @@ void nf_conntrack_udp_init_net(struct net *net)
for (i = 0; i < UDP_CT_MAX; i++)
un->timeouts[i] = udp_timeouts[i];
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
un->offload_timeout = 30 * HZ;
un->offload_pickup = 30 * HZ;
#endif
}
const struct nf_conntrack_l4proto nf_conntrack_l4proto_udp =
......
......@@ -512,9 +512,7 @@ static void nf_conntrack_standalone_fini_proc(struct net *net)
u32 nf_conntrack_count(const struct net *net)
{
const struct nf_conntrack_net *cnet;
cnet = net_generic(net, nf_conntrack_net_id);
const struct nf_conntrack_net *cnet = nf_ct_pernet(net);
return atomic_read(&cnet->count);
}
......@@ -575,11 +573,19 @@ enum nf_ct_sysctl_index {
NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_CLOSE,
NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_RETRANS,
NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_UNACK,
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD,
NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD_PICKUP,
#endif
NF_SYSCTL_CT_PROTO_TCP_LOOSE,
NF_SYSCTL_CT_PROTO_TCP_LIBERAL,
NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS,
NF_SYSCTL_CT_PROTO_TIMEOUT_UDP,
NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM,
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD,
NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD_PICKUP,
#endif
NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP,
NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6,
#ifdef CONFIG_NF_CT_PROTO_SCTP
......@@ -762,6 +768,20 @@ static struct ctl_table nf_ct_sysctl_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD] = {
.procname = "nf_flowtable_tcp_timeout",
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD_PICKUP] = {
.procname = "nf_flowtable_tcp_pickup",
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
#endif
[NF_SYSCTL_CT_PROTO_TCP_LOOSE] = {
.procname = "nf_conntrack_tcp_loose",
.maxlen = sizeof(u8),
......@@ -796,6 +816,20 @@ static struct ctl_table nf_ct_sysctl_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
#if IS_ENABLED(CONFIG_NFT_FLOW_OFFLOAD)
[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD] = {
.procname = "nf_flowtable_udp_timeout",
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD_PICKUP] = {
.procname = "nf_flowtable_udp_pickup",
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
#endif
[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP] = {
.procname = "nf_conntrack_icmp_timeout",
.maxlen = sizeof(unsigned int),
......@@ -971,6 +1005,12 @@ static void nf_conntrack_standalone_init_tcp_sysctl(struct net *net,
XASSIGN(LIBERAL, &tn->tcp_be_liberal);
XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans);
#undef XASSIGN
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
table[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD].data = &tn->offload_timeout;
table[NF_SYSCTL_CT_PROTO_TIMEOUT_TCP_OFFLOAD_PICKUP].data = &tn->offload_pickup;
#endif
}
static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net,
......@@ -1032,7 +1072,7 @@ static void nf_conntrack_standalone_init_gre_sysctl(struct net *net,
static int nf_conntrack_standalone_init_sysctl(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
struct nf_udp_net *un = nf_udp_pernet(net);
struct ctl_table *table;
......@@ -1059,6 +1099,10 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
table[NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6].data = &nf_icmpv6_pernet(net)->timeout;
table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP].data = &un->timeouts[UDP_CT_UNREPLIED];
table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_STREAM].data = &un->timeouts[UDP_CT_REPLIED];
#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD].data = &un->offload_timeout;
table[NF_SYSCTL_CT_PROTO_TIMEOUT_UDP_OFFLOAD_PICKUP].data = &un->offload_pickup;
#endif
nf_conntrack_standalone_init_tcp_sysctl(net, table);
nf_conntrack_standalone_init_sctp_sysctl(net, table);
......@@ -1085,7 +1129,7 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
static void nf_conntrack_standalone_fini_sysctl(struct net *net)
{
struct nf_conntrack_net *cnet = net_generic(net, nf_conntrack_net_id);
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
struct ctl_table *table;
table = cnet->sysctl_header->ctl_table_arg;
......
......@@ -178,12 +178,10 @@ static void flow_offload_fixup_tcp(struct ip_ct_tcp *tcp)
tcp->seen[1].td_maxwin = 0;
}
#define NF_FLOWTABLE_TCP_PICKUP_TIMEOUT (120 * HZ)
#define NF_FLOWTABLE_UDP_PICKUP_TIMEOUT (30 * HZ)
static void flow_offload_fixup_ct_timeout(struct nf_conn *ct)
{
const struct nf_conntrack_l4proto *l4proto;
struct net *net = nf_ct_net(ct);
int l4num = nf_ct_protonum(ct);
unsigned int timeout;
......@@ -191,12 +189,17 @@ static void flow_offload_fixup_ct_timeout(struct nf_conn *ct)
if (!l4proto)
return;
if (l4num == IPPROTO_TCP)
timeout = NF_FLOWTABLE_TCP_PICKUP_TIMEOUT;
else if (l4num == IPPROTO_UDP)
timeout = NF_FLOWTABLE_UDP_PICKUP_TIMEOUT;
else
if (l4num == IPPROTO_TCP) {
struct nf_tcp_net *tn = nf_tcp_pernet(net);
timeout = tn->offload_pickup;
} else if (l4num == IPPROTO_UDP) {
struct nf_udp_net *tn = nf_udp_pernet(net);
timeout = tn->offload_pickup;
} else {
return;
}
if (nf_flow_timeout_delta(ct->timeout) > (__s32)timeout)
ct->timeout = nfct_time_stamp + timeout;
......@@ -268,11 +271,35 @@ static const struct rhashtable_params nf_flow_offload_rhash_params = {
.automatic_shrinking = true,
};
unsigned long flow_offload_get_timeout(struct flow_offload *flow)
{
const struct nf_conntrack_l4proto *l4proto;
unsigned long timeout = NF_FLOW_TIMEOUT;
struct net *net = nf_ct_net(flow->ct);
int l4num = nf_ct_protonum(flow->ct);
l4proto = nf_ct_l4proto_find(l4num);
if (!l4proto)
return timeout;
if (l4num == IPPROTO_TCP) {
struct nf_tcp_net *tn = nf_tcp_pernet(net);
timeout = tn->offload_timeout;
} else if (l4num == IPPROTO_UDP) {
struct nf_udp_net *tn = nf_udp_pernet(net);
timeout = tn->offload_timeout;
}
return timeout;
}
int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
{
int err;
flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT;
flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
err = rhashtable_insert_fast(&flow_table->rhashtable,
&flow->tuplehash[0].node,
......@@ -304,7 +331,7 @@ EXPORT_SYMBOL_GPL(flow_offload_add);
void flow_offload_refresh(struct nf_flowtable *flow_table,
struct flow_offload *flow)
{
flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT;
flow->timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
if (likely(!nf_flowtable_hw_offload(flow_table)))
return;
......
......@@ -937,7 +937,7 @@ static void flow_offload_work_stats(struct flow_offload_work *offload)
lastused = max_t(u64, stats[0].lastused, stats[1].lastused);
offload->flow->timeout = max_t(u64, offload->flow->timeout,
lastused + NF_FLOW_TIMEOUT);
lastused + flow_offload_get_timeout(offload->flow));
if (offload->flowtable->flags & NF_FLOWTABLE_COUNTER) {
if (stats[0].pkts)
......@@ -1041,7 +1041,7 @@ void nf_flow_offload_stats(struct nf_flowtable *flowtable,
__s32 delta;
delta = nf_flow_timeout_delta(flow->timeout);
if ((delta >= (9 * NF_FLOW_TIMEOUT) / 10))
if ((delta >= (9 * flow_offload_get_timeout(flow)) / 10))
return;
offload = nf_flow_offload_work_alloc(flowtable, flow, FLOW_CLS_STATS);
......
......@@ -862,10 +862,9 @@ static int nft_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
static int nf_tables_gettable(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
const struct nft_table *table;
struct net *net = info->net;
struct sk_buff *skb2;
......@@ -1068,10 +1067,9 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
struct nftables_pernet *nft_net = nft_pernet(info->net);
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
......@@ -1263,10 +1261,9 @@ static int nft_flush(struct nft_ctx *ctx, int family)
static int nf_tables_deltable(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
......@@ -1636,10 +1633,9 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
static int nf_tables_getchain(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
const struct nft_chain *chain;
struct net *net = info->net;
struct nft_table *table;
......@@ -2015,11 +2011,12 @@ static void nft_basechain_hook_init(struct nf_hook_ops *ops, u8 family,
const struct nft_chain_hook *hook,
struct nft_chain *chain)
{
ops->pf = family;
ops->hooknum = hook->num;
ops->priority = hook->priority;
ops->priv = chain;
ops->hook = hook->type->hooks[ops->hooknum];
ops->pf = family;
ops->hooknum = hook->num;
ops->priority = hook->priority;
ops->priv = chain;
ops->hook = hook->type->hooks[ops->hooknum];
ops->hook_ops_type = NF_HOOK_OP_NF_TABLES;
}
static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
......@@ -2371,10 +2368,9 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
struct nftables_pernet *nft_net = nft_pernet(info->net);
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct nft_chain *chain = NULL;
struct net *net = info->net;
const struct nlattr *attr;
......@@ -2469,10 +2465,9 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
......@@ -3096,10 +3091,9 @@ static int nf_tables_dump_rules_done(struct netlink_callback *cb)
static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
const struct nft_chain *chain;
const struct nft_rule *rule;
struct net *net = info->net;
......@@ -3237,13 +3231,12 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
struct nftables_pernet *nft_net = nft_pernet(info->net);
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
unsigned int size, i, n, ulen = 0, usize = 0;
u8 genmask = nft_genmask_next(info->net);
struct nft_rule *rule, *old_rule = NULL;
struct nft_expr_info *expr_info = NULL;
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
struct nft_flow_rule *flow;
struct nft_userdata *udata;
......@@ -3477,15 +3470,15 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
int family = nfmsg->nfgen_family, err = 0;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
struct nft_chain *chain = NULL;
struct net *net = info->net;
struct nft_table *table;
struct nft_rule *rule;
struct nft_ctx ctx;
int err = 0;
table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask,
NETLINK_CB(skb).portid);
......@@ -3665,30 +3658,6 @@ static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
[NFTA_SET_DESC_CONCAT] = { .type = NLA_NESTED },
};
static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
struct netlink_ext_ack *extack,
u8 genmask, u32 nlpid)
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
int family = nfmsg->nfgen_family;
struct nft_table *table = NULL;
if (nla[NFTA_SET_TABLE] != NULL) {
table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family,
genmask, nlpid);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_TABLE]);
return PTR_ERR(table);
}
}
nft_ctx_init(ctx, net, skb, nlh, family, table, NULL, nla);
return 0;
}
static struct nft_set *nft_set_lookup(const struct nft_table *table,
const struct nlattr *nla, u8 genmask)
{
......@@ -4068,20 +4037,26 @@ static int nf_tables_dump_sets_done(struct netlink_callback *cb)
static int nf_tables_getset(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
u8 family = info->nfmsg->nfgen_family;
struct nft_table *table = NULL;
struct net *net = info->net;
const struct nft_set *set;
struct sk_buff *skb2;
struct nft_ctx ctx;
int err;
/* Verify existence before starting dump */
err = nft_ctx_init_from_setattr(&ctx, net, skb, info->nlh, nla, extack,
genmask, 0);
if (err < 0)
return err;
if (nla[NFTA_SET_TABLE]) {
table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family,
genmask, 0);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_TABLE]);
return PTR_ERR(table);
}
}
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
......@@ -4096,12 +4071,12 @@ static int nf_tables_getset(struct sk_buff *skb, const struct nfnl_info *info,
}
/* Only accept unspec with dump */
if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
if (info->nfmsg->nfgen_family == NFPROTO_UNSPEC)
return -EAFNOSUPPORT;
if (!nla[NFTA_SET_TABLE])
return -EINVAL;
set = nft_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask);
set = nft_set_lookup(table, nla[NFTA_SET_NAME], genmask);
if (IS_ERR(set))
return PTR_ERR(set);
......@@ -4189,11 +4164,10 @@ static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u32 ktype, dtype, flags, policy, gc_int, objtype;
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
const struct nft_set_ops *ops;
struct nft_expr *expr = NULL;
struct net *net = info->net;
......@@ -4493,31 +4467,31 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
static int nf_tables_delset(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
struct nft_set *set;
struct nft_ctx ctx;
int err;
if (nfmsg->nfgen_family == NFPROTO_UNSPEC)
if (info->nfmsg->nfgen_family == NFPROTO_UNSPEC)
return -EAFNOSUPPORT;
if (nla[NFTA_SET_TABLE] == NULL)
return -EINVAL;
err = nft_ctx_init_from_setattr(&ctx, net, skb, info->nlh, nla, extack,
genmask, NETLINK_CB(skb).portid);
if (err < 0)
return err;
table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family,
genmask, NETLINK_CB(skb).portid);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_TABLE]);
return PTR_ERR(table);
}
if (nla[NFTA_SET_HANDLE]) {
attr = nla[NFTA_SET_HANDLE];
set = nft_set_lookup_byhandle(ctx.table, attr, genmask);
set = nft_set_lookup_byhandle(table, attr, genmask);
} else {
attr = nla[NFTA_SET_NAME];
set = nft_set_lookup(ctx.table, attr, genmask);
set = nft_set_lookup(table, attr, genmask);
}
if (IS_ERR(set)) {
......@@ -4531,6 +4505,8 @@ static int nf_tables_delset(struct sk_buff *skb, const struct nfnl_info *info,
return -EBUSY;
}
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
return nft_delset(&ctx, set);
}
......@@ -4732,28 +4708,6 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
[NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
};
static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
struct netlink_ext_ack *extack,
u8 genmask, u32 nlpid)
{
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
int family = nfmsg->nfgen_family;
struct nft_table *table;
table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
genmask, nlpid);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_TABLE]);
return PTR_ERR(table);
}
nft_ctx_init(ctx, net, skb, nlh, family, table, NULL, nla);
return 0;
}
static int nft_set_elem_expr_dump(struct sk_buff *skb,
const struct nft_set *set,
const struct nft_set_ext *ext)
......@@ -5211,21 +5165,27 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
{
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
struct nft_table *table;
struct nft_set *set;
struct nlattr *attr;
struct nft_ctx ctx;
int rem, err = 0;
err = nft_ctx_init_from_elemattr(&ctx, net, skb, info->nlh, nla, extack,
genmask, NETLINK_CB(skb).portid);
if (err < 0)
return err;
table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
genmask, NETLINK_CB(skb).portid);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_TABLE]);
return PTR_ERR(table);
}
set = nft_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
if (IS_ERR(set))
return PTR_ERR(set);
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
.start = nf_tables_dump_set_start,
......@@ -5994,8 +5954,10 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
struct nftables_pernet *nft_net = nft_pernet(info->net);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
struct nft_set *set;
struct nft_ctx ctx;
int rem, err;
......@@ -6003,12 +5965,14 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
if (nla[NFTA_SET_ELEM_LIST_ELEMENTS] == NULL)
return -EINVAL;
err = nft_ctx_init_from_elemattr(&ctx, net, skb, info->nlh, nla, extack,
genmask, NETLINK_CB(skb).portid);
if (err < 0)
return err;
table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
genmask, NETLINK_CB(skb).portid);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_TABLE]);
return PTR_ERR(table);
}
set = nft_set_lookup_global(net, ctx.table, nla[NFTA_SET_ELEM_LIST_SET],
set = nft_set_lookup_global(net, table, nla[NFTA_SET_ELEM_LIST_SET],
nla[NFTA_SET_ELEM_LIST_SET_ID], genmask);
if (IS_ERR(set))
return PTR_ERR(set);
......@@ -6016,6 +5980,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
return -EBUSY;
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
err = nft_add_set_elem(&ctx, set, attr, info->nlh->nlmsg_flags);
if (err < 0)
......@@ -6023,7 +5989,7 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
}
if (nft_net->validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, ctx.table);
return nft_table_validate(net, table);
return 0;
}
......@@ -6261,23 +6227,29 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
{
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
struct nft_set *set;
struct nft_ctx ctx;
int rem, err = 0;
err = nft_ctx_init_from_elemattr(&ctx, net, skb, info->nlh, nla, extack,
genmask, NETLINK_CB(skb).portid);
if (err < 0)
return err;
table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
genmask, NETLINK_CB(skb).portid);
if (IS_ERR(table)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_ELEM_LIST_TABLE]);
return PTR_ERR(table);
}
set = nft_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
if (IS_ERR(set))
return PTR_ERR(set);
if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
return -EBUSY;
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
return nft_set_flush(&ctx, set, genmask);
......@@ -6545,11 +6517,10 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,
static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
const struct nft_object_type *type;
int family = nfmsg->nfgen_family;
struct net *net = info->net;
struct nft_table *table;
struct nft_object *obj;
......@@ -6801,10 +6772,9 @@ static int nf_tables_dump_obj_done(struct netlink_callback *cb)
static int nf_tables_getobj(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_cur(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
const struct nft_table *table;
struct net *net = info->net;
struct nft_object *obj;
......@@ -6891,10 +6861,9 @@ static void nft_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj)
static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct net *net = info->net;
const struct nlattr *attr;
struct nft_table *table;
......@@ -7322,12 +7291,11 @@ static int nf_tables_newflowtable(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
struct nft_flowtable_hook flowtable_hook;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
const struct nf_flowtable_type *type;
int family = nfmsg->nfgen_family;
struct nft_flowtable *flowtable;
struct nft_hook *hook, *next;
struct net *net = info->net;
......@@ -7511,10 +7479,9 @@ static int nf_tables_delflowtable(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct nft_flowtable *flowtable;
struct net *net = info->net;
const struct nlattr *attr;
......@@ -7706,9 +7673,8 @@ static int nf_tables_getflowtable(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
{
const struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u8 genmask = nft_genmask_cur(info->net);
int family = nfmsg->nfgen_family;
u8 family = info->nfmsg->nfgen_family;
struct nft_flowtable *flowtable;
const struct nft_table *table;
struct net *net = info->net;
......
......@@ -68,6 +68,7 @@ static const char *const nfnl_lockdep_names[NFNL_SUBSYS_COUNT] = {
[NFNL_SUBSYS_CTHELPER] = "nfnl_subsys_cthelper",
[NFNL_SUBSYS_NFTABLES] = "nfnl_subsys_nftables",
[NFNL_SUBSYS_NFT_COMPAT] = "nfnl_subsys_nftcompat",
[NFNL_SUBSYS_HOOK] = "nfnl_subsys_hook",
};
static const int nfnl_group2type[NFNLGRP_MAX+1] = {
......@@ -256,6 +257,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
.net = net,
.sk = nfnlnet->nfnl,
.nlh = nlh,
.nfmsg = nlmsg_data(nlh),
.extack = extack,
};
......@@ -491,6 +493,7 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
.net = net,
.sk = nfnlnet->nfnl,
.nlh = nlh,
.nfmsg = nlmsg_data(nlh),
.extack = &extack,
};
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2021 Red Hat GmbH
*
* Author: Florian Westphal <fw@strlen.de>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/netlink.h>
#include <linux/slab.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_hook.h>
#include <net/netfilter/nf_tables.h>
#include <net/sock.h>
static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
[NFNLA_HOOK_HOOKNUM] = { .type = NLA_U32 },
[NFNLA_HOOK_PRIORITY] = { .type = NLA_U32 },
[NFNLA_HOOK_DEV] = { .type = NLA_STRING,
.len = IFNAMSIZ - 1 },
[NFNLA_HOOK_FUNCTION_NAME] = { .type = NLA_NUL_STRING,
.len = KSYM_NAME_LEN, },
[NFNLA_HOOK_MODULE_NAME] = { .type = NLA_NUL_STRING,
.len = MODULE_NAME_LEN, },
[NFNLA_HOOK_CHAIN_INFO] = { .type = NLA_NESTED, },
};
static int nf_netlink_dump_start_rcu(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh,
struct netlink_dump_control *c)
{
int err;
if (!try_module_get(THIS_MODULE))
return -EINVAL;
rcu_read_unlock();
err = netlink_dump_start(nlsk, skb, nlh, c);
rcu_read_lock();
module_put(THIS_MODULE);
return err;
}
struct nfnl_dump_hook_data {
char devname[IFNAMSIZ];
unsigned long headv;
u8 hook;
};
static int nfnl_hook_put_nft_chain_info(struct sk_buff *nlskb,
const struct nfnl_dump_hook_data *ctx,
unsigned int seq,
const struct nf_hook_ops *ops)
{
struct net *net = sock_net(nlskb->sk);
struct nlattr *nest, *nest2;
struct nft_chain *chain;
int ret = 0;
if (ops->hook_ops_type != NF_HOOK_OP_NF_TABLES)
return 0;
chain = ops->priv;
if (WARN_ON_ONCE(!chain))
return 0;
if (!nft_is_active(net, chain))
return 0;
nest = nla_nest_start(nlskb, NFNLA_HOOK_CHAIN_INFO);
if (!nest)
return -EMSGSIZE;
ret = nla_put_be32(nlskb, NFNLA_HOOK_INFO_TYPE,
htonl(NFNL_HOOK_TYPE_NFTABLES));
if (ret)
goto cancel_nest;
nest2 = nla_nest_start(nlskb, NFNLA_HOOK_INFO_DESC);
if (!nest2)
goto cancel_nest;
ret = nla_put_string(nlskb, NFTA_CHAIN_TABLE, chain->table->name);
if (ret)
goto cancel_nest;
ret = nla_put_string(nlskb, NFTA_CHAIN_NAME, chain->name);
if (ret)
goto cancel_nest;
nla_nest_end(nlskb, nest2);
nla_nest_end(nlskb, nest);
return ret;
cancel_nest:
nla_nest_cancel(nlskb, nest);
return -EMSGSIZE;
}
static int nfnl_hook_dump_one(struct sk_buff *nlskb,
const struct nfnl_dump_hook_data *ctx,
const struct nf_hook_ops *ops,
unsigned int seq)
{
u16 event = nfnl_msg_type(NFNL_SUBSYS_HOOK, NFNL_MSG_HOOK_GET);
unsigned int portid = NETLINK_CB(nlskb).portid;
struct nlmsghdr *nlh;
int ret = -EMSGSIZE;
#ifdef CONFIG_KALLSYMS
char sym[KSYM_SYMBOL_LEN];
char *module_name;
#endif
nlh = nfnl_msg_put(nlskb, portid, seq, event,
NLM_F_MULTI, ops->pf, NFNETLINK_V0, 0);
if (!nlh)
goto nla_put_failure;
#ifdef CONFIG_KALLSYMS
ret = snprintf(sym, sizeof(sym), "%ps", ops->hook);
if (ret < 0 || ret > (int)sizeof(sym))
goto nla_put_failure;
module_name = strstr(sym, " [");
if (module_name) {
char *end;
module_name += 2;
end = strchr(module_name, ']');
if (end) {
*end = 0;
ret = nla_put_string(nlskb, NFNLA_HOOK_MODULE_NAME, module_name);
if (ret)
goto nla_put_failure;
}
}
ret = nla_put_string(nlskb, NFNLA_HOOK_FUNCTION_NAME, sym);
if (ret)
goto nla_put_failure;
#endif
ret = nla_put_be32(nlskb, NFNLA_HOOK_HOOKNUM, htonl(ops->hooknum));
if (ret)
goto nla_put_failure;
ret = nla_put_be32(nlskb, NFNLA_HOOK_PRIORITY, htonl(ops->priority));
if (ret)
goto nla_put_failure;
ret = nfnl_hook_put_nft_chain_info(nlskb, ctx, seq, ops);
if (ret)
goto nla_put_failure;
nlmsg_end(nlskb, nlh);
return 0;
nla_put_failure:
nlmsg_trim(nlskb, nlh);
return ret;
}
static const struct nf_hook_entries *
nfnl_hook_entries_head(u8 pf, unsigned int hook, struct net *net, const char *dev)
{
const struct nf_hook_entries *hook_head = NULL;
struct net_device *netdev;
switch (pf) {
case NFPROTO_IPV4:
if (hook >= ARRAY_SIZE(net->nf.hooks_ipv4))
return ERR_PTR(-EINVAL);
hook_head = rcu_dereference(net->nf.hooks_ipv4[hook]);
break;
case NFPROTO_IPV6:
if (hook >= ARRAY_SIZE(net->nf.hooks_ipv6))
return ERR_PTR(-EINVAL);
hook_head = rcu_dereference(net->nf.hooks_ipv6[hook]);
break;
case NFPROTO_ARP:
#ifdef CONFIG_NETFILTER_FAMILY_ARP
if (hook >= ARRAY_SIZE(net->nf.hooks_arp))
return ERR_PTR(-EINVAL);
hook_head = rcu_dereference(net->nf.hooks_arp[hook]);
#endif
break;
case NFPROTO_BRIDGE:
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
if (hook >= ARRAY_SIZE(net->nf.hooks_bridge))
return ERR_PTR(-EINVAL);
hook_head = rcu_dereference(net->nf.hooks_bridge[hook]);
#endif
break;
#if IS_ENABLED(CONFIG_DECNET)
case NFPROTO_DECNET:
if (hook >= ARRAY_SIZE(net->nf.hooks_decnet))
return ERR_PTR(-EINVAL);
hook_head = rcu_dereference(net->nf.hooks_decnet[hook]);
break;
#endif
#ifdef CONFIG_NETFILTER_INGRESS
case NFPROTO_NETDEV:
if (hook != NF_NETDEV_INGRESS)
return ERR_PTR(-EOPNOTSUPP);
if (!dev)
return ERR_PTR(-ENODEV);
netdev = dev_get_by_name_rcu(net, dev);
if (!netdev)
return ERR_PTR(-ENODEV);
return rcu_dereference(netdev->nf_hooks_ingress);
#endif
default:
return ERR_PTR(-EPROTONOSUPPORT);
}
return hook_head;
}
static int nfnl_hook_dump(struct sk_buff *nlskb,
struct netlink_callback *cb)
{
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
struct nfnl_dump_hook_data *ctx = cb->data;
int err, family = nfmsg->nfgen_family;
struct net *net = sock_net(nlskb->sk);
struct nf_hook_ops * const *ops;
const struct nf_hook_entries *e;
unsigned int i = cb->args[0];
rcu_read_lock();
e = nfnl_hook_entries_head(family, ctx->hook, net, ctx->devname);
if (!e)
goto done;
if (IS_ERR(e)) {
cb->seq++;
goto done;
}
if ((unsigned long)e != ctx->headv || i >= e->num_hook_entries)
cb->seq++;
ops = nf_hook_entries_get_hook_ops(e);
for (; i < e->num_hook_entries; i++) {
err = nfnl_hook_dump_one(nlskb, ctx, ops[i], cb->seq);
if (err)
break;
}
done:
nl_dump_check_consistent(cb, nlmsg_hdr(nlskb));
rcu_read_unlock();
cb->args[0] = i;
return nlskb->len;
}
static int nfnl_hook_dump_start(struct netlink_callback *cb)
{
const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
const struct nlattr * const *nla = cb->data;
struct nfnl_dump_hook_data *ctx = NULL;
struct net *net = sock_net(cb->skb->sk);
u8 family = nfmsg->nfgen_family;
char name[IFNAMSIZ] = "";
const void *head;
u32 hooknum;
hooknum = ntohl(nla_get_be32(nla[NFNLA_HOOK_HOOKNUM]));
if (hooknum > 255)
return -EINVAL;
if (family == NFPROTO_NETDEV) {
if (!nla[NFNLA_HOOK_DEV])
return -EINVAL;
nla_strscpy(name, nla[NFNLA_HOOK_DEV], sizeof(name));
}
rcu_read_lock();
/* Not dereferenced; for consistency check only */
head = nfnl_hook_entries_head(family, hooknum, net, name);
rcu_read_unlock();
if (head && IS_ERR(head))
return PTR_ERR(head);
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
strscpy(ctx->devname, name, sizeof(ctx->devname));
ctx->headv = (unsigned long)head;
ctx->hook = hooknum;
cb->seq = 1;
cb->data = ctx;
return 0;
}
static int nfnl_hook_dump_stop(struct netlink_callback *cb)
{
kfree(cb->data);
return 0;
}
static int nfnl_hook_get(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const nla[])
{
if (!nla[NFNLA_HOOK_HOOKNUM])
return -EINVAL;
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = {
.start = nfnl_hook_dump_start,
.done = nfnl_hook_dump_stop,
.dump = nfnl_hook_dump,
.module = THIS_MODULE,
.data = (void *)nla,
};
return nf_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
}
return -EOPNOTSUPP;
}
static const struct nfnl_callback nfnl_hook_cb[NFNL_MSG_HOOK_MAX] = {
[NFNL_MSG_HOOK_GET] = {
.call = nfnl_hook_get,
.type = NFNL_CB_RCU,
.attr_count = NFNLA_HOOK_MAX,
.policy = nfnl_hook_nla_policy
},
};
static const struct nfnetlink_subsystem nfhook_subsys = {
.name = "nfhook",
.subsys_id = NFNL_SUBSYS_HOOK,
.cb_count = NFNL_MSG_HOOK_MAX,
.cb = nfnl_hook_cb,
};
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_HOOK);
static int __init nfnetlink_hook_init(void)
{
return nfnetlink_subsys_register(&nfhook_subsys);
}
static void __exit nfnetlink_hook_exit(void)
{
nfnetlink_subsys_unregister(&nfhook_subsys);
}
module_init(nfnetlink_hook_init);
module_exit(nfnetlink_hook_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
MODULE_DESCRIPTION("nfnetlink_hook: list registered netfilter hooks");
......@@ -871,15 +871,14 @@ static int nfulnl_recv_config(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nfula[])
{
struct nfnl_log_net *log = nfnl_log_pernet(info->net);
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int16_t group_num = ntohs(nfmsg->res_id);
u_int16_t group_num = ntohs(info->nfmsg->res_id);
struct nfulnl_msg_config_cmd *cmd = NULL;
struct nfulnl_instance *inst;
u16 flags = 0;
int ret = 0;
if (nfula[NFULA_CFG_CMD]) {
u_int8_t pf = nfmsg->nfgen_family;
u_int8_t pf = info->nfmsg->nfgen_family;
cmd = nla_data(nfula[NFULA_CFG_CMD]);
/* Commands without queue context */
......
......@@ -1051,8 +1051,7 @@ static int nfqnl_recv_verdict_batch(struct sk_buff *skb,
const struct nlattr * const nfqa[])
{
struct nfnl_queue_net *q = nfnl_queue_pernet(info->net);
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u16 queue_num = ntohs(nfmsg->res_id);
u16 queue_num = ntohs(info->nfmsg->res_id);
struct nf_queue_entry *entry, *tmp;
struct nfqnl_msg_verdict_hdr *vhdr;
struct nfqnl_instance *queue;
......@@ -1160,8 +1159,7 @@ static int nfqnl_recv_verdict(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nfqa[])
{
struct nfnl_queue_net *q = nfnl_queue_pernet(info->net);
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int16_t queue_num = ntohs(nfmsg->res_id);
u_int16_t queue_num = ntohs(info->nfmsg->res_id);
struct nfqnl_msg_verdict_hdr *vhdr;
enum ip_conntrack_info ctinfo;
struct nfqnl_instance *queue;
......@@ -1243,8 +1241,7 @@ static int nfqnl_recv_config(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nfqa[])
{
struct nfnl_queue_net *q = nfnl_queue_pernet(info->net);
struct nfgenmsg *nfmsg = nlmsg_data(info->nlh);
u_int16_t queue_num = ntohs(nfmsg->res_id);
u_int16_t queue_num = ntohs(info->nfmsg->res_id);
struct nfqnl_msg_config_cmd *cmd = NULL;
struct nfqnl_instance *queue;
__u32 flags = 0, mask = 0;
......
......@@ -625,7 +625,7 @@ static int nfnl_compat_get_rcu(struct sk_buff *skb,
const struct nfnl_info *info,
const struct nlattr * const tb[])
{
struct nfgenmsg *nfmsg;
u8 family = info->nfmsg->nfgen_family;
const char *name, *fmt;
struct sk_buff *skb2;
int ret = 0, target;
......@@ -640,9 +640,7 @@ static int nfnl_compat_get_rcu(struct sk_buff *skb,
rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV]));
target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE]));
nfmsg = nlmsg_data(info->nlh);
switch(nfmsg->nfgen_family) {
switch(family) {
case AF_INET:
fmt = "ipt_%s";
break;
......@@ -656,8 +654,7 @@ static int nfnl_compat_get_rcu(struct sk_buff *skb,
fmt = "arpt_%s";
break;
default:
pr_err("nft_compat: unsupported protocol %d\n",
nfmsg->nfgen_family);
pr_err("nft_compat: unsupported protocol %d\n", family);
return -EINVAL;
}
......@@ -665,9 +662,8 @@ static int nfnl_compat_get_rcu(struct sk_buff *skb,
return -EINVAL;
rcu_read_unlock();
try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name,
rev, target, &ret),
fmt, name);
try_then_request_module(xt_find_revision(family, name, rev, target, &ret),
fmt, name);
if (ret < 0)
goto out_put;
......@@ -682,8 +678,7 @@ static int nfnl_compat_get_rcu(struct sk_buff *skb,
info->nlh->nlmsg_seq,
NFNL_MSG_TYPE(info->nlh->nlmsg_type),
NFNL_MSG_COMPAT_GET,
nfmsg->nfgen_family,
name, ret, target) <= 0) {
family, name, ret, target) <= 0) {
kfree_skb(skb2);
goto out_put;
}
......
......@@ -327,7 +327,9 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
break;
dest[priv->len / NFT_REG32_SIZE] = 0;
memcpy(dest, (char *)sch + priv->offset, priv->len);
if (skb_copy_bits(pkt->skb, offset + priv->offset,
dest, priv->len) < 0)
break;
return;
}
offset += SCTP_PAD4(ntohs(sch->length));
......
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