Commit 95d1815f authored by Jakub Kicinski's avatar Jakub Kicinski

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

Pablo Neira Ayuso says:

====================
Netfilter/IPVS updates for net-next

1) Incorrect error check in nft_expr_inner_parse(), from Dan Carpenter.

2) Add DATA_SENT state to SCTP connection tracking helper, from
   Sriram Yagnaraman.

3) Consolidate nf_confirm for ipv4 and ipv6, from Florian Westphal.

4) Add bitmask support for ipset, from Vishwanath Pai.

5) Handle icmpv6 redirects as RELATED, from Florian Westphal.

6) Add WARN_ON_ONCE() to impossible case in flowtable datapath,
   from Li Qiong.

7) A large batch of IPVS updates to replace timer-based estimators by
   kthreads to scale up wrt. CPUs and workload (millions of estimators).

Julian Anastasov says:

	This patchset implements stats estimation in kthread context.
It replaces the code that runs on single CPU in timer context every 2
seconds and causing latency splats as shown in reports [1], [2], [3].
The solution targets setups with thousands of IPVS services,
destinations and multi-CPU boxes.

	Spread the estimation on multiple (configured) CPUs and multiple
time slots (timer ticks) by using multiple chains organized under RCU
rules.  When stats are not needed, it is recommended to use
run_estimation=0 as already implemented before this change.

RCU Locking:

- As stats are now RCU-locked, tot_stats, svc and dest which
hold estimator structures are now always freed from RCU
callback. This ensures RCU grace period after the
ip_vs_stop_estimator() call.

Kthread data:

- every kthread works over its own data structure and all
such structures are attached to array. For now we limit
kthreads depending on the number of CPUs.

- even while there can be a kthread structure, its task
may not be running, eg. before first service is added or
while the sysctl var is set to an empty cpulist or
when run_estimation is set to 0 to disable the estimation.

- the allocated kthread context may grow from 1 to 50
allocated structures for timer ticks which saves memory for
setups with small number of estimators

- a task and its structure may be released if all
estimators are unlinked from its chains, leaving the
slot in the array empty

- every kthread data structure allows limited number
of estimators. Kthread 0 is also used to initially
calculate the max number of estimators to allow in every
chain considering a sub-100 microsecond cond_resched
rate. This number can be from 1 to hundreds.

- kthread 0 has an additional job of optimizing the
adding of estimators: they are first added in
temp list (est_temp_list) and later kthread 0
distributes them to other kthreads. The optimization
is based on the fact that newly added estimator
should be estimated after 2 seconds, so we have the
time to offload the adding to chain from controlling
process to kthread 0.

- to add new estimators we use the last added kthread
context (est_add_ktid). The new estimators are linked to
the chains just before the estimated one, based on add_row.
This ensures their estimation will start after 2 seconds.
If estimators are added in bursts, common case if all
services and dests are initially configured, we may
spread the estimators to more chains and as result,
reducing the initial delay below 2 seconds.

Many thanks to Jiri Wiesner for his valuable comments
and for spending a lot of time reviewing and testing
the changes on different platforms with 48-256 CPUs and
1-8 NUMA nodes under different cpufreq governors.

The new IPVS estimators do not use workqueue infrastructure
because:

- The estimation can take long time when using multiple IPVS rules (eg.
  millions estimator structures) and especially when box has multiple
  CPUs due to the for_each_possible_cpu usage that expects packets from
  any CPU. With est_nice sysctl we have more control how to prioritize the
  estimation kthreads compared to other processes/kthreads that have
  latency requirements (such as servers). As a benefit, we can see these
  kthreads in top and decide if we will need some further control to limit
  their CPU usage (max number of structure to estimate per kthread).

- with kthreads we run code that is read-mostly, no write/lock
  operations to process the estimators in 2-second intervals.

- work items are one-shot: as estimators are processed every
  2 seconds, they need to be re-added every time. This again
  loads the timers (add_timer) if we use delayed works, as there are
  no kthreads to do the timings.

[1] Report from Yunhong Jiang:
    https://lore.kernel.org/netdev/D25792C1-1B89-45DE-9F10-EC350DC04ADC@gmail.com/
[2] https://marc.info/?l=linux-virtual-server&m=159679809118027&w=2
[3] Report from Dust:
    https://archive.linuxvirtualserver.org/html/lvs-devel/2020-12/msg00000.html

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  ipvs: run_estimation should control the kthread tasks
  ipvs: add est_cpulist and est_nice sysctl vars
  ipvs: use kthreads for stats estimation
  ipvs: use u64_stats_t for the per-cpu counters
  ipvs: use common functions for stats allocation
  ipvs: add rcu protection to stats
  netfilter: flowtable: add a 'default' case to flowtable datapath
  netfilter: conntrack: set icmpv6 redirects as RELATED
  netfilter: ipset: Add support for new bitmask parameter
  netfilter: conntrack: merge ipv4+ipv6 confirm functions
  netfilter: conntrack: add sctp DATA_SENT state
  netfilter: nft_inner: fix IS_ERR() vs NULL check
====================

Link: https://lore.kernel.org/r/20221211101204.1751-1-pablo@netfilter.orgSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 15eb1621 144361c1
...@@ -129,6 +129,26 @@ drop_packet - INTEGER ...@@ -129,6 +129,26 @@ drop_packet - INTEGER
threshold. When the mode 3 is set, the always mode drop rate threshold. When the mode 3 is set, the always mode drop rate
is controlled by the /proc/sys/net/ipv4/vs/am_droprate. is controlled by the /proc/sys/net/ipv4/vs/am_droprate.
est_cpulist - CPULIST
Allowed CPUs for estimation kthreads
Syntax: standard cpulist format
empty list - stop kthread tasks and estimation
default - the system's housekeeping CPUs for kthreads
Example:
"all": all possible CPUs
"0-N": all possible CPUs, N denotes last CPU number
"0,1-N:1/2": first and all CPUs with odd number
"": empty list
est_nice - INTEGER
default 0
Valid range: -20 (more favorable) .. 19 (less favorable)
Niceness value to use for the estimation kthreads (scheduling
priority)
expire_nodest_conn - BOOLEAN expire_nodest_conn - BOOLEAN
- 0 - disabled (default) - 0 - disabled (default)
- not 0 - enabled - not 0 - enabled
...@@ -304,8 +324,8 @@ run_estimation - BOOLEAN ...@@ -304,8 +324,8 @@ run_estimation - BOOLEAN
0 - disabled 0 - disabled
not 0 - enabled (default) not 0 - enabled (default)
If disabled, the estimation will be stop, and you can't see If disabled, the estimation will be suspended and kthread tasks
any update on speed estimation data. stopped.
You can always re-enable estimation by setting this value to 1. You can always re-enable estimation by setting this value to 1.
But be careful, the first estimation after re-enable is not But be careful, the first estimation after re-enable is not
......
...@@ -515,6 +515,16 @@ ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo, ...@@ -515,6 +515,16 @@ ip_set_init_skbinfo(struct ip_set_skbinfo *skbinfo,
*skbinfo = ext->skbinfo; *skbinfo = ext->skbinfo;
} }
static inline void
nf_inet_addr_mask_inplace(union nf_inet_addr *a1,
const union nf_inet_addr *mask)
{
a1->all[0] &= mask->all[0];
a1->all[1] &= mask->all[1];
a1->all[2] &= mask->all[2];
a1->all[3] &= mask->all[3];
}
#define IP_SET_INIT_KEXT(skb, opt, set) \ #define IP_SET_INIT_KEXT(skb, opt, set) \
{ .bytes = (skb)->len, .packets = 1, .target = true,\ { .bytes = (skb)->len, .packets = 1, .target = true,\
.timeout = ip_set_adt_opt_timeout(opt, set) } .timeout = ip_set_adt_opt_timeout(opt, set) }
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#endif #endif
#include <net/net_namespace.h> /* Netw namespace */ #include <net/net_namespace.h> /* Netw namespace */
#include <linux/sched/isolation.h>
#define IP_VS_HDR_INVERSE 1 #define IP_VS_HDR_INVERSE 1
#define IP_VS_HDR_ICMP 2 #define IP_VS_HDR_ICMP 2
...@@ -42,6 +43,8 @@ static inline struct netns_ipvs *net_ipvs(struct net* net) ...@@ -42,6 +43,8 @@ static inline struct netns_ipvs *net_ipvs(struct net* net)
/* Connections' size value needed by ip_vs_ctl.c */ /* Connections' size value needed by ip_vs_ctl.c */
extern int ip_vs_conn_tab_size; extern int ip_vs_conn_tab_size;
extern struct mutex __ip_vs_mutex;
struct ip_vs_iphdr { struct ip_vs_iphdr {
int hdr_flags; /* ipvs flags */ int hdr_flags; /* ipvs flags */
__u32 off; /* Where IP or IPv4 header starts */ __u32 off; /* Where IP or IPv4 header starts */
...@@ -351,11 +354,11 @@ struct ip_vs_seq { ...@@ -351,11 +354,11 @@ struct ip_vs_seq {
/* counters per cpu */ /* counters per cpu */
struct ip_vs_counters { struct ip_vs_counters {
__u64 conns; /* connections scheduled */ u64_stats_t conns; /* connections scheduled */
__u64 inpkts; /* incoming packets */ u64_stats_t inpkts; /* incoming packets */
__u64 outpkts; /* outgoing packets */ u64_stats_t outpkts; /* outgoing packets */
__u64 inbytes; /* incoming bytes */ u64_stats_t inbytes; /* incoming bytes */
__u64 outbytes; /* outgoing bytes */ u64_stats_t outbytes; /* outgoing bytes */
}; };
/* Stats per cpu */ /* Stats per cpu */
struct ip_vs_cpu_stats { struct ip_vs_cpu_stats {
...@@ -363,9 +366,12 @@ struct ip_vs_cpu_stats { ...@@ -363,9 +366,12 @@ struct ip_vs_cpu_stats {
struct u64_stats_sync syncp; struct u64_stats_sync syncp;
}; };
/* Default nice for estimator kthreads */
#define IPVS_EST_NICE 0
/* IPVS statistics objects */ /* IPVS statistics objects */
struct ip_vs_estimator { struct ip_vs_estimator {
struct list_head list; struct hlist_node list;
u64 last_inbytes; u64 last_inbytes;
u64 last_outbytes; u64 last_outbytes;
...@@ -378,6 +384,10 @@ struct ip_vs_estimator { ...@@ -378,6 +384,10 @@ struct ip_vs_estimator {
u64 outpps; u64 outpps;
u64 inbps; u64 inbps;
u64 outbps; u64 outbps;
s32 ktid:16, /* kthread ID, -1=temp list */
ktrow:8, /* row/tick ID for kthread */
ktcid:8; /* chain ID for kthread tick */
}; };
/* /*
...@@ -405,6 +415,76 @@ struct ip_vs_stats { ...@@ -405,6 +415,76 @@ struct ip_vs_stats {
struct ip_vs_kstats kstats0; /* reset values */ struct ip_vs_kstats kstats0; /* reset values */
}; };
struct ip_vs_stats_rcu {
struct ip_vs_stats s;
struct rcu_head rcu_head;
};
int ip_vs_stats_init_alloc(struct ip_vs_stats *s);
struct ip_vs_stats *ip_vs_stats_alloc(void);
void ip_vs_stats_release(struct ip_vs_stats *stats);
void ip_vs_stats_free(struct ip_vs_stats *stats);
/* Process estimators in multiple timer ticks (20/50/100, see ktrow) */
#define IPVS_EST_NTICKS 50
/* Estimation uses a 2-second period containing ticks (in jiffies) */
#define IPVS_EST_TICK ((2 * HZ) / IPVS_EST_NTICKS)
/* Limit of CPU load per kthread (8 for 12.5%), ratio of CPU capacity (1/C).
* Value of 4 and above ensures kthreads will take work without exceeding
* the CPU capacity under different circumstances.
*/
#define IPVS_EST_LOAD_DIVISOR 8
/* Kthreads should not have work that exceeds the CPU load above 50% */
#define IPVS_EST_CPU_KTHREADS (IPVS_EST_LOAD_DIVISOR / 2)
/* Desired number of chains per timer tick (chain load factor in 100us units),
* 48=4.8ms of 40ms tick (12% CPU usage):
* 2 sec * 1000 ms in sec * 10 (100us in ms) / 8 (12.5%) / 50
*/
#define IPVS_EST_CHAIN_FACTOR \
ALIGN_DOWN(2 * 1000 * 10 / IPVS_EST_LOAD_DIVISOR / IPVS_EST_NTICKS, 8)
/* Compiled number of chains per tick
* The defines should match cond_resched_rcu
*/
#if defined(CONFIG_DEBUG_ATOMIC_SLEEP) || !defined(CONFIG_PREEMPT_RCU)
#define IPVS_EST_TICK_CHAINS IPVS_EST_CHAIN_FACTOR
#else
#define IPVS_EST_TICK_CHAINS 1
#endif
#if IPVS_EST_NTICKS > 127
#error Too many timer ticks for ktrow
#endif
/* Multiple chains processed in same tick */
struct ip_vs_est_tick_data {
struct hlist_head chains[IPVS_EST_TICK_CHAINS];
DECLARE_BITMAP(present, IPVS_EST_TICK_CHAINS);
DECLARE_BITMAP(full, IPVS_EST_TICK_CHAINS);
int chain_len[IPVS_EST_TICK_CHAINS];
};
/* Context for estimation kthread */
struct ip_vs_est_kt_data {
struct netns_ipvs *ipvs;
struct task_struct *task; /* task if running */
struct ip_vs_est_tick_data __rcu *ticks[IPVS_EST_NTICKS];
DECLARE_BITMAP(avail, IPVS_EST_NTICKS); /* tick has space for ests */
unsigned long est_timer; /* estimation timer (jiffies) */
struct ip_vs_stats *calc_stats; /* Used for calculation */
int tick_len[IPVS_EST_NTICKS]; /* est count */
int id; /* ktid per netns */
int chain_max; /* max ests per tick chain */
int tick_max; /* max ests per tick */
int est_count; /* attached ests to kthread */
int est_max_count; /* max ests per kthread */
int add_row; /* row for new ests */
int est_row; /* estimated row */
};
struct dst_entry; struct dst_entry;
struct iphdr; struct iphdr;
struct ip_vs_conn; struct ip_vs_conn;
...@@ -688,6 +768,7 @@ struct ip_vs_dest { ...@@ -688,6 +768,7 @@ struct ip_vs_dest {
union nf_inet_addr vaddr; /* virtual IP address */ union nf_inet_addr vaddr; /* virtual IP address */
__u32 vfwmark; /* firewall mark of service */ __u32 vfwmark; /* firewall mark of service */
struct rcu_head rcu_head;
struct list_head t_list; /* in dest_trash */ struct list_head t_list; /* in dest_trash */
unsigned int in_rs_table:1; /* we are in rs_table */ unsigned int in_rs_table:1; /* we are in rs_table */
}; };
...@@ -869,7 +950,7 @@ struct netns_ipvs { ...@@ -869,7 +950,7 @@ struct netns_ipvs {
atomic_t conn_count; /* connection counter */ atomic_t conn_count; /* connection counter */
/* ip_vs_ctl */ /* ip_vs_ctl */
struct ip_vs_stats tot_stats; /* Statistics & est. */ struct ip_vs_stats_rcu *tot_stats; /* Statistics & est. */
int num_services; /* no of virtual services */ int num_services; /* no of virtual services */
int num_services6; /* IPv6 virtual services */ int num_services6; /* IPv6 virtual services */
...@@ -932,6 +1013,12 @@ struct netns_ipvs { ...@@ -932,6 +1013,12 @@ struct netns_ipvs {
int sysctl_schedule_icmp; int sysctl_schedule_icmp;
int sysctl_ignore_tunneled; int sysctl_ignore_tunneled;
int sysctl_run_estimation; int sysctl_run_estimation;
#ifdef CONFIG_SYSCTL
cpumask_var_t sysctl_est_cpulist; /* kthread cpumask */
int est_cpulist_valid; /* cpulist set */
int sysctl_est_nice; /* kthread nice */
int est_stopped; /* stop tasks */
#endif
/* ip_vs_lblc */ /* ip_vs_lblc */
int sysctl_lblc_expiration; int sysctl_lblc_expiration;
...@@ -942,9 +1029,17 @@ struct netns_ipvs { ...@@ -942,9 +1029,17 @@ struct netns_ipvs {
struct ctl_table_header *lblcr_ctl_header; struct ctl_table_header *lblcr_ctl_header;
struct ctl_table *lblcr_ctl_table; struct ctl_table *lblcr_ctl_table;
/* ip_vs_est */ /* ip_vs_est */
struct list_head est_list; /* estimator list */ struct delayed_work est_reload_work;/* Reload kthread tasks */
spinlock_t est_lock; struct mutex est_mutex; /* protect kthread tasks */
struct timer_list est_timer; /* Estimation timer */ struct hlist_head est_temp_list; /* Ests during calc phase */
struct ip_vs_est_kt_data **est_kt_arr; /* Array of kthread data ptrs */
unsigned long est_max_threads;/* Hard limit of kthreads */
int est_calc_phase; /* Calculation phase */
int est_chain_max; /* Calculated chain_max */
int est_kt_count; /* Allocated ptrs */
int est_add_ktid; /* ktid where to add ests */
atomic_t est_genid; /* kthreads reload genid */
atomic_t est_genid_done; /* applied genid */
/* ip_vs_sync */ /* ip_vs_sync */
spinlock_t sync_lock; spinlock_t sync_lock;
struct ipvs_master_sync_state *ms; struct ipvs_master_sync_state *ms;
...@@ -1077,6 +1172,19 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs) ...@@ -1077,6 +1172,19 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs)
return ipvs->sysctl_run_estimation; return ipvs->sysctl_run_estimation;
} }
static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs)
{
if (ipvs->est_cpulist_valid)
return ipvs->sysctl_est_cpulist;
else
return housekeeping_cpumask(HK_TYPE_KTHREAD);
}
static inline int sysctl_est_nice(struct netns_ipvs *ipvs)
{
return ipvs->sysctl_est_nice;
}
#else #else
static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs) static inline int sysctl_sync_threshold(struct netns_ipvs *ipvs)
...@@ -1174,6 +1282,16 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs) ...@@ -1174,6 +1282,16 @@ static inline int sysctl_run_estimation(struct netns_ipvs *ipvs)
return 1; return 1;
} }
static inline const struct cpumask *sysctl_est_cpulist(struct netns_ipvs *ipvs)
{
return housekeeping_cpumask(HK_TYPE_KTHREAD);
}
static inline int sysctl_est_nice(struct netns_ipvs *ipvs)
{
return IPVS_EST_NICE;
}
#endif #endif
/* IPVS core functions /* IPVS core functions
...@@ -1475,10 +1593,41 @@ int stop_sync_thread(struct netns_ipvs *ipvs, int state); ...@@ -1475,10 +1593,41 @@ int stop_sync_thread(struct netns_ipvs *ipvs, int state);
void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts); void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts);
/* IPVS rate estimator prototypes (from ip_vs_est.c) */ /* IPVS rate estimator prototypes (from ip_vs_est.c) */
void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats);
void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats); void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats);
void ip_vs_zero_estimator(struct ip_vs_stats *stats); void ip_vs_zero_estimator(struct ip_vs_stats *stats);
void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats); void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats);
void ip_vs_est_reload_start(struct netns_ipvs *ipvs);
int ip_vs_est_kthread_start(struct netns_ipvs *ipvs,
struct ip_vs_est_kt_data *kd);
void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd);
static inline void ip_vs_est_stopped_recalc(struct netns_ipvs *ipvs)
{
#ifdef CONFIG_SYSCTL
/* Stop tasks while cpulist is empty or if disabled with flag */
ipvs->est_stopped = !sysctl_run_estimation(ipvs) ||
(ipvs->est_cpulist_valid &&
cpumask_empty(sysctl_est_cpulist(ipvs)));
#endif
}
static inline bool ip_vs_est_stopped(struct netns_ipvs *ipvs)
{
#ifdef CONFIG_SYSCTL
return ipvs->est_stopped;
#else
return false;
#endif
}
static inline int ip_vs_est_max_threads(struct netns_ipvs *ipvs)
{
unsigned int limit = IPVS_EST_CPU_KTHREADS *
cpumask_weight(sysctl_est_cpulist(ipvs));
return max(1U, limit);
}
/* Various IPVS packet transmitters (from ip_vs_xmit.c) */ /* Various IPVS packet transmitters (from ip_vs_xmit.c) */
int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
......
...@@ -71,8 +71,7 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb) ...@@ -71,8 +71,7 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb)
return ret; return ret;
} }
unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff, unsigned int nf_confirm(void *priv, struct sk_buff *skb, const struct nf_hook_state *state);
struct nf_conn *ct, enum ip_conntrack_info ctinfo);
void print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, void print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_l4proto *proto); const struct nf_conntrack_l4proto *proto);
......
...@@ -85,6 +85,7 @@ enum { ...@@ -85,6 +85,7 @@ enum {
IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */ IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO, /* 9 */
IPSET_ATTR_MARK, /* 10 */ IPSET_ATTR_MARK, /* 10 */
IPSET_ATTR_MARKMASK, /* 11 */ IPSET_ATTR_MARKMASK, /* 11 */
IPSET_ATTR_BITMASK, /* 12 */
/* Reserve empty slots */ /* Reserve empty slots */
IPSET_ATTR_CADT_MAX = 16, IPSET_ATTR_CADT_MAX = 16,
/* Create-only specific attributes */ /* Create-only specific attributes */
...@@ -153,6 +154,7 @@ enum ipset_errno { ...@@ -153,6 +154,7 @@ enum ipset_errno {
IPSET_ERR_COMMENT, IPSET_ERR_COMMENT,
IPSET_ERR_INVALID_MARKMASK, IPSET_ERR_INVALID_MARKMASK,
IPSET_ERR_SKBINFO, IPSET_ERR_SKBINFO,
IPSET_ERR_BITMASK_NETMASK_EXCL,
/* Type specific error codes */ /* Type specific error codes */
IPSET_ERR_TYPE_SPECIFIC = 4352, IPSET_ERR_TYPE_SPECIFIC = 4352,
......
...@@ -16,6 +16,7 @@ enum sctp_conntrack { ...@@ -16,6 +16,7 @@ enum sctp_conntrack {
SCTP_CONNTRACK_SHUTDOWN_ACK_SENT, SCTP_CONNTRACK_SHUTDOWN_ACK_SENT,
SCTP_CONNTRACK_HEARTBEAT_SENT, SCTP_CONNTRACK_HEARTBEAT_SENT,
SCTP_CONNTRACK_HEARTBEAT_ACKED, SCTP_CONNTRACK_HEARTBEAT_ACKED,
SCTP_CONNTRACK_DATA_SENT,
SCTP_CONNTRACK_MAX SCTP_CONNTRACK_MAX
}; };
......
...@@ -95,6 +95,7 @@ enum ctattr_timeout_sctp { ...@@ -95,6 +95,7 @@ enum ctattr_timeout_sctp {
CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT, CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT,
CTA_TIMEOUT_SCTP_HEARTBEAT_SENT, CTA_TIMEOUT_SCTP_HEARTBEAT_SENT,
CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED, CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED,
CTA_TIMEOUT_SCTP_DATA_SENT,
__CTA_TIMEOUT_SCTP_MAX __CTA_TIMEOUT_SCTP_MAX
}; };
#define CTA_TIMEOUT_SCTP_MAX (__CTA_TIMEOUT_SCTP_MAX - 1) #define CTA_TIMEOUT_SCTP_MAX (__CTA_TIMEOUT_SCTP_MAX - 1)
......
...@@ -366,42 +366,12 @@ static int nf_ct_bridge_refrag_post(struct net *net, struct sock *sk, ...@@ -366,42 +366,12 @@ static int nf_ct_bridge_refrag_post(struct net *net, struct sock *sk,
return br_dev_queue_push_xmit(net, sk, skb); return br_dev_queue_push_xmit(net, sk, skb);
} }
static unsigned int nf_ct_bridge_confirm(struct sk_buff *skb)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
int protoff;
ct = nf_ct_get(skb, &ctinfo);
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
return nf_conntrack_confirm(skb);
switch (skb->protocol) {
case htons(ETH_P_IP):
protoff = skb_network_offset(skb) + ip_hdrlen(skb);
break;
case htons(ETH_P_IPV6): {
unsigned char pnum = ipv6_hdr(skb)->nexthdr;
__be16 frag_off;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
&frag_off);
if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
return nf_conntrack_confirm(skb);
}
break;
default:
return NF_ACCEPT;
}
return nf_confirm(skb, protoff, ct, ctinfo);
}
static unsigned int nf_ct_bridge_post(void *priv, struct sk_buff *skb, static unsigned int nf_ct_bridge_post(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state) const struct nf_hook_state *state)
{ {
int ret; int ret;
ret = nf_ct_bridge_confirm(skb); ret = nf_confirm(priv, skb, state);
if (ret != NF_ACCEPT) if (ret != NF_ACCEPT)
return ret; return ret;
......
...@@ -159,6 +159,17 @@ htable_size(u8 hbits) ...@@ -159,6 +159,17 @@ htable_size(u8 hbits)
(SET_WITH_TIMEOUT(set) && \ (SET_WITH_TIMEOUT(set) && \
ip_set_timeout_expired(ext_timeout(d, set))) ip_set_timeout_expired(ext_timeout(d, set)))
#if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
static const union nf_inet_addr onesmask = {
.all[0] = 0xffffffff,
.all[1] = 0xffffffff,
.all[2] = 0xffffffff,
.all[3] = 0xffffffff
};
static const union nf_inet_addr zeromask = {};
#endif
#endif /* _IP_SET_HASH_GEN_H */ #endif /* _IP_SET_HASH_GEN_H */
#ifndef MTYPE #ifndef MTYPE
...@@ -283,8 +294,9 @@ struct htype { ...@@ -283,8 +294,9 @@ struct htype {
u32 markmask; /* markmask value for mark mask to store */ u32 markmask; /* markmask value for mark mask to store */
#endif #endif
u8 bucketsize; /* max elements in an array block */ u8 bucketsize; /* max elements in an array block */
#ifdef IP_SET_HASH_WITH_NETMASK #if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
u8 netmask; /* netmask value for subnets to store */ u8 netmask; /* netmask value for subnets to store */
union nf_inet_addr bitmask; /* stores bitmask */
#endif #endif
struct list_head ad; /* Resize add|del backlist */ struct list_head ad; /* Resize add|del backlist */
struct mtype_elem next; /* temporary storage for uadd */ struct mtype_elem next; /* temporary storage for uadd */
...@@ -459,8 +471,8 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b) ...@@ -459,8 +471,8 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b)
/* Resizing changes htable_bits, so we ignore it */ /* Resizing changes htable_bits, so we ignore it */
return x->maxelem == y->maxelem && return x->maxelem == y->maxelem &&
a->timeout == b->timeout && a->timeout == b->timeout &&
#ifdef IP_SET_HASH_WITH_NETMASK #if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
x->netmask == y->netmask && nf_inet_addr_cmp(&x->bitmask, &y->bitmask) &&
#endif #endif
#ifdef IP_SET_HASH_WITH_MARKMASK #ifdef IP_SET_HASH_WITH_MARKMASK
x->markmask == y->markmask && x->markmask == y->markmask &&
...@@ -1264,9 +1276,21 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) ...@@ -1264,9 +1276,21 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
htonl(jhash_size(htable_bits))) || htonl(jhash_size(htable_bits))) ||
nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
goto nla_put_failure; goto nla_put_failure;
#ifdef IP_SET_HASH_WITH_BITMASK
/* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask
* and not bitmask. These two are mutually exclusive. */
if (h->netmask == HOST_MASK && !nf_inet_addr_cmp(&onesmask, &h->bitmask)) {
if (set->family == NFPROTO_IPV4) {
if (nla_put_ipaddr4(skb, IPSET_ATTR_BITMASK, h->bitmask.ip))
goto nla_put_failure;
} else if (set->family == NFPROTO_IPV6) {
if (nla_put_ipaddr6(skb, IPSET_ATTR_BITMASK, &h->bitmask.in6))
goto nla_put_failure;
}
}
#endif
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
if (h->netmask != HOST_MASK && if (h->netmask != HOST_MASK && nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
goto nla_put_failure; goto nla_put_failure;
#endif #endif
#ifdef IP_SET_HASH_WITH_MARKMASK #ifdef IP_SET_HASH_WITH_MARKMASK
...@@ -1429,8 +1453,10 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, ...@@ -1429,8 +1453,10 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
u32 markmask; u32 markmask;
#endif #endif
u8 hbits; u8 hbits;
#ifdef IP_SET_HASH_WITH_NETMASK #if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
u8 netmask; int ret __attribute__((unused)) = 0;
u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
union nf_inet_addr bitmask = onesmask;
#endif #endif
size_t hsize; size_t hsize;
struct htype *h; struct htype *h;
...@@ -1468,7 +1494,6 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, ...@@ -1468,7 +1494,6 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
#endif #endif
#ifdef IP_SET_HASH_WITH_NETMASK #ifdef IP_SET_HASH_WITH_NETMASK
netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
if (tb[IPSET_ATTR_NETMASK]) { if (tb[IPSET_ATTR_NETMASK]) {
netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
...@@ -1476,6 +1501,33 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, ...@@ -1476,6 +1501,33 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
(set->family == NFPROTO_IPV6 && netmask > 128) || (set->family == NFPROTO_IPV6 && netmask > 128) ||
netmask == 0) netmask == 0)
return -IPSET_ERR_INVALID_NETMASK; return -IPSET_ERR_INVALID_NETMASK;
/* we convert netmask to bitmask and store it */
if (set->family == NFPROTO_IPV4)
bitmask.ip = ip_set_netmask(netmask);
else
ip6_netmask(&bitmask, netmask);
}
#endif
#ifdef IP_SET_HASH_WITH_BITMASK
if (tb[IPSET_ATTR_BITMASK]) {
/* bitmask and netmask do the same thing, allow only one of these options */
if (tb[IPSET_ATTR_NETMASK])
return -IPSET_ERR_BITMASK_NETMASK_EXCL;
if (set->family == NFPROTO_IPV4) {
ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_BITMASK], &bitmask.ip);
if (ret || !bitmask.ip)
return -IPSET_ERR_INVALID_NETMASK;
} else if (set->family == NFPROTO_IPV6) {
ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_BITMASK], &bitmask);
if (ret || ipv6_addr_any(&bitmask.in6))
return -IPSET_ERR_INVALID_NETMASK;
}
if (nf_inet_addr_cmp(&bitmask, &zeromask))
return -IPSET_ERR_INVALID_NETMASK;
} }
#endif #endif
...@@ -1518,7 +1570,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, ...@@ -1518,7 +1570,8 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
for (i = 0; i < ahash_numof_locks(hbits); i++) for (i = 0; i < ahash_numof_locks(hbits); i++)
spin_lock_init(&t->hregion[i].lock); spin_lock_init(&t->hregion[i].lock);
h->maxelem = maxelem; h->maxelem = maxelem;
#ifdef IP_SET_HASH_WITH_NETMASK #if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK)
h->bitmask = bitmask;
h->netmask = netmask; h->netmask = netmask;
#endif #endif
#ifdef IP_SET_HASH_WITH_MARKMASK #ifdef IP_SET_HASH_WITH_MARKMASK
......
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
/* 2 Comments support */ /* 2 Comments support */
/* 3 Forceadd support */ /* 3 Forceadd support */
/* 4 skbinfo support */ /* 4 skbinfo support */
#define IPSET_TYPE_REV_MAX 5 /* bucketsize, initval support */ /* 5 bucketsize, initval support */
#define IPSET_TYPE_REV_MAX 6 /* bitmask support */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
...@@ -34,6 +35,7 @@ MODULE_ALIAS("ip_set_hash:ip"); ...@@ -34,6 +35,7 @@ MODULE_ALIAS("ip_set_hash:ip");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_ip #define HTYPE hash_ip
#define IP_SET_HASH_WITH_NETMASK #define IP_SET_HASH_WITH_NETMASK
#define IP_SET_HASH_WITH_BITMASK
/* IPv4 variant */ /* IPv4 variant */
...@@ -86,7 +88,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, ...@@ -86,7 +88,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
__be32 ip; __be32 ip;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
ip &= ip_set_netmask(h->netmask); ip &= h->bitmask.ip;
if (ip == 0) if (ip == 0)
return -EINVAL; return -EINVAL;
...@@ -119,7 +121,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -119,7 +121,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret) if (ret)
return ret; return ret;
ip &= ip_set_hostmask(h->netmask); ip &= ntohl(h->bitmask.ip);
e.ip = htonl(ip); e.ip = htonl(ip);
if (e.ip == 0) if (e.ip == 0)
return -IPSET_ERR_HASH_ELEM; return -IPSET_ERR_HASH_ELEM;
...@@ -185,12 +187,6 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1, ...@@ -185,12 +187,6 @@ hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6); return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6);
} }
static void
hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
{
ip6_netmask(ip, prefix);
}
static bool static bool
hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e) hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e)
{ {
...@@ -227,7 +223,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, ...@@ -227,7 +223,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
hash_ip6_netmask(&e.ip, h->netmask); nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
if (ipv6_addr_any(&e.ip.in6)) if (ipv6_addr_any(&e.ip.in6))
return -EINVAL; return -EINVAL;
...@@ -266,7 +262,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -266,7 +262,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret) if (ret)
return ret; return ret;
hash_ip6_netmask(&e.ip, h->netmask); nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
if (ipv6_addr_any(&e.ip.in6)) if (ipv6_addr_any(&e.ip.in6))
return -IPSET_ERR_HASH_ELEM; return -IPSET_ERR_HASH_ELEM;
...@@ -293,6 +289,7 @@ static struct ip_set_type hash_ip_type __read_mostly = { ...@@ -293,6 +289,7 @@ static struct ip_set_type hash_ip_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 }, [IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_BITMASK] = { .type = NLA_NESTED },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
}, },
.adt_policy = { .adt_policy = {
......
...@@ -26,7 +26,8 @@ ...@@ -26,7 +26,8 @@
/* 3 Comments support added */ /* 3 Comments support added */
/* 4 Forceadd support added */ /* 4 Forceadd support added */
/* 5 skbinfo support added */ /* 5 skbinfo support added */
#define IPSET_TYPE_REV_MAX 6 /* bucketsize, initval support added */ /* 6 bucketsize, initval support added */
#define IPSET_TYPE_REV_MAX 7 /* bitmask support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>"); MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
...@@ -35,6 +36,8 @@ MODULE_ALIAS("ip_set_hash:ip,port"); ...@@ -35,6 +36,8 @@ MODULE_ALIAS("ip_set_hash:ip,port");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_ipport #define HTYPE hash_ipport
#define IP_SET_HASH_WITH_NETMASK
#define IP_SET_HASH_WITH_BITMASK
/* IPv4 variant */ /* IPv4 variant */
...@@ -92,12 +95,16 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, ...@@ -92,12 +95,16 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb,
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem e = { .ip = 0 }; struct hash_ipport4_elem e = { .ip = 0 };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
const struct MTYPE *h = set->data;
if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto)) &e.port, &e.proto))
return -EINVAL; return -EINVAL;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
e.ip &= h->bitmask.ip;
if (e.ip == 0)
return -EINVAL;
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
} }
...@@ -129,6 +136,10 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -129,6 +136,10 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret) if (ret)
return ret; return ret;
e.ip &= h->bitmask.ip;
if (e.ip == 0)
return -EINVAL;
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
...@@ -253,12 +264,17 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, ...@@ -253,12 +264,17 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb,
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport6_elem e = { .ip = { .all = { 0 } } }; struct hash_ipport6_elem e = { .ip = { .all = { 0 } } };
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
const struct MTYPE *h = set->data;
if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC,
&e.port, &e.proto)) &e.port, &e.proto))
return -EINVAL; return -EINVAL;
ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
if (ipv6_addr_any(&e.ip.in6))
return -EINVAL;
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
} }
...@@ -298,6 +314,10 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -298,6 +314,10 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[],
if (ret) if (ret)
return ret; return ret;
nf_inet_addr_mask_inplace(&e.ip, &h->bitmask);
if (ipv6_addr_any(&e.ip.in6))
return -EINVAL;
e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); e.port = nla_get_be16(tb[IPSET_ATTR_PORT]);
if (tb[IPSET_ATTR_PROTO]) { if (tb[IPSET_ATTR_PROTO]) {
...@@ -356,6 +376,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = { ...@@ -356,6 +376,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = {
[IPSET_ATTR_PROTO] = { .type = NLA_U8 }, [IPSET_ATTR_PROTO] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_BITMASK] = { .type = NLA_NESTED },
}, },
.adt_policy = { .adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP] = { .type = NLA_NESTED },
......
...@@ -23,7 +23,8 @@ ...@@ -23,7 +23,8 @@
#define IPSET_TYPE_REV_MIN 0 #define IPSET_TYPE_REV_MIN 0
/* 1 Forceadd support added */ /* 1 Forceadd support added */
/* 2 skbinfo support added */ /* 2 skbinfo support added */
#define IPSET_TYPE_REV_MAX 3 /* bucketsize, initval support added */ /* 3 bucketsize, initval support added */
#define IPSET_TYPE_REV_MAX 4 /* bitmask support added */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>"); MODULE_AUTHOR("Oliver Smith <oliver@8.c.9.b.0.7.4.0.1.0.0.2.ip6.arpa>");
...@@ -33,6 +34,8 @@ MODULE_ALIAS("ip_set_hash:net,net"); ...@@ -33,6 +34,8 @@ MODULE_ALIAS("ip_set_hash:net,net");
/* Type specific function prefix */ /* Type specific function prefix */
#define HTYPE hash_netnet #define HTYPE hash_netnet
#define IP_SET_HASH_WITH_NETS #define IP_SET_HASH_WITH_NETS
#define IP_SET_HASH_WITH_NETMASK
#define IP_SET_HASH_WITH_BITMASK
#define IPSET_NET_COUNT 2 #define IPSET_NET_COUNT 2
/* IPv4 variants */ /* IPv4 variants */
...@@ -153,8 +156,8 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, ...@@ -153,8 +156,8 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]);
ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]); ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]);
e.ip[0] &= ip_set_netmask(e.cidr[0]); e.ip[0] &= (ip_set_netmask(e.cidr[0]) & h->bitmask.ip);
e.ip[1] &= ip_set_netmask(e.cidr[1]); e.ip[1] &= (ip_set_netmask(e.cidr[1]) & h->bitmask.ip);
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
} }
...@@ -213,8 +216,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -213,8 +216,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] || if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] ||
tb[IPSET_ATTR_IP2_TO])) { tb[IPSET_ATTR_IP2_TO])) {
e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); e.ip[0] = htonl(ip & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[0]));
e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); e.ip[1] = htonl(ip2_from & ntohl(h->bitmask.ip) & ip_set_hostmask(e.cidr[1]));
ret = adtfn(set, &e, &ext, &ext, flags); ret = adtfn(set, &e, &ext, &ext, flags);
return ip_set_enomatch(ret, flags, adt, set) ? -ret : return ip_set_enomatch(ret, flags, adt, set) ? -ret :
ip_set_eexist(ret, flags) ? 0 : ret; ip_set_eexist(ret, flags) ? 0 : ret;
...@@ -404,6 +407,11 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, ...@@ -404,6 +407,11 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb,
ip6_netmask(&e.ip[0], e.cidr[0]); ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]); ip6_netmask(&e.ip[1], e.cidr[1]);
nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask);
nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask);
if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6))
return -EINVAL;
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
} }
...@@ -414,6 +422,7 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -414,6 +422,7 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet6_elem e = { }; struct hash_netnet6_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set); struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
const struct hash_netnet6 *h = set->data;
int ret; int ret;
if (tb[IPSET_ATTR_LINENO]) if (tb[IPSET_ATTR_LINENO])
...@@ -453,6 +462,11 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], ...@@ -453,6 +462,11 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[],
ip6_netmask(&e.ip[0], e.cidr[0]); ip6_netmask(&e.ip[0], e.cidr[0]);
ip6_netmask(&e.ip[1], e.cidr[1]); ip6_netmask(&e.ip[1], e.cidr[1]);
nf_inet_addr_mask_inplace(&e.ip[0], &h->bitmask);
nf_inet_addr_mask_inplace(&e.ip[1], &h->bitmask);
if (e.cidr[0] == HOST_MASK && ipv6_addr_any(&e.ip[0].in6))
return -IPSET_ERR_HASH_ELEM;
if (tb[IPSET_ATTR_CADT_FLAGS]) { if (tb[IPSET_ATTR_CADT_FLAGS]) {
u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
...@@ -484,6 +498,8 @@ static struct ip_set_type hash_netnet_type __read_mostly = { ...@@ -484,6 +498,8 @@ static struct ip_set_type hash_netnet_type __read_mostly = {
[IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, [IPSET_ATTR_RESIZE] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_NETMASK] = { .type = NLA_U8 },
[IPSET_ATTR_BITMASK] = { .type = NLA_NESTED },
}, },
.adt_policy = { .adt_policy = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP] = { .type = NLA_NESTED },
......
...@@ -132,21 +132,21 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -132,21 +132,21 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
s = this_cpu_ptr(dest->stats.cpustats); s = this_cpu_ptr(dest->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.inpkts++; u64_stats_inc(&s->cnt.inpkts);
s->cnt.inbytes += skb->len; u64_stats_add(&s->cnt.inbytes, skb->len);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
svc = rcu_dereference(dest->svc); svc = rcu_dereference(dest->svc);
s = this_cpu_ptr(svc->stats.cpustats); s = this_cpu_ptr(svc->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.inpkts++; u64_stats_inc(&s->cnt.inpkts);
s->cnt.inbytes += skb->len; u64_stats_add(&s->cnt.inbytes, skb->len);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
s = this_cpu_ptr(ipvs->tot_stats.cpustats); s = this_cpu_ptr(ipvs->tot_stats->s.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.inpkts++; u64_stats_inc(&s->cnt.inpkts);
s->cnt.inbytes += skb->len; u64_stats_add(&s->cnt.inbytes, skb->len);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
local_bh_enable(); local_bh_enable();
...@@ -168,21 +168,21 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) ...@@ -168,21 +168,21 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
s = this_cpu_ptr(dest->stats.cpustats); s = this_cpu_ptr(dest->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.outpkts++; u64_stats_inc(&s->cnt.outpkts);
s->cnt.outbytes += skb->len; u64_stats_add(&s->cnt.outbytes, skb->len);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
svc = rcu_dereference(dest->svc); svc = rcu_dereference(dest->svc);
s = this_cpu_ptr(svc->stats.cpustats); s = this_cpu_ptr(svc->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.outpkts++; u64_stats_inc(&s->cnt.outpkts);
s->cnt.outbytes += skb->len; u64_stats_add(&s->cnt.outbytes, skb->len);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
s = this_cpu_ptr(ipvs->tot_stats.cpustats); s = this_cpu_ptr(ipvs->tot_stats->s.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.outpkts++; u64_stats_inc(&s->cnt.outpkts);
s->cnt.outbytes += skb->len; u64_stats_add(&s->cnt.outbytes, skb->len);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
local_bh_enable(); local_bh_enable();
...@@ -200,17 +200,17 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc) ...@@ -200,17 +200,17 @@ ip_vs_conn_stats(struct ip_vs_conn *cp, struct ip_vs_service *svc)
s = this_cpu_ptr(cp->dest->stats.cpustats); s = this_cpu_ptr(cp->dest->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.conns++; u64_stats_inc(&s->cnt.conns);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
s = this_cpu_ptr(svc->stats.cpustats); s = this_cpu_ptr(svc->stats.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.conns++; u64_stats_inc(&s->cnt.conns);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
s = this_cpu_ptr(ipvs->tot_stats.cpustats); s = this_cpu_ptr(ipvs->tot_stats->s.cpustats);
u64_stats_update_begin(&s->syncp); u64_stats_update_begin(&s->syncp);
s->cnt.conns++; u64_stats_inc(&s->cnt.conns);
u64_stats_update_end(&s->syncp); u64_stats_update_end(&s->syncp);
local_bh_enable(); local_bh_enable();
...@@ -2448,6 +2448,10 @@ static void __exit ip_vs_cleanup(void) ...@@ -2448,6 +2448,10 @@ static void __exit ip_vs_cleanup(void)
ip_vs_conn_cleanup(); ip_vs_conn_cleanup();
ip_vs_protocol_cleanup(); ip_vs_protocol_cleanup();
ip_vs_control_cleanup(); ip_vs_control_cleanup();
/* common rcu_barrier() used by:
* - ip_vs_control_cleanup()
*/
rcu_barrier();
pr_info("ipvs unloaded.\n"); pr_info("ipvs unloaded.\n");
} }
......
...@@ -49,8 +49,7 @@ ...@@ -49,8 +49,7 @@
MODULE_ALIAS_GENL_FAMILY(IPVS_GENL_NAME); MODULE_ALIAS_GENL_FAMILY(IPVS_GENL_NAME);
/* semaphore for IPVS sockopts. And, [gs]etsockopt may sleep. */ DEFINE_MUTEX(__ip_vs_mutex); /* Serialize configuration with sockopt/netlink */
static DEFINE_MUTEX(__ip_vs_mutex);
/* sysctl variables */ /* sysctl variables */
...@@ -241,6 +240,47 @@ static void defense_work_handler(struct work_struct *work) ...@@ -241,6 +240,47 @@ static void defense_work_handler(struct work_struct *work)
} }
#endif #endif
static void est_reload_work_handler(struct work_struct *work)
{
struct netns_ipvs *ipvs =
container_of(work, struct netns_ipvs, est_reload_work.work);
int genid_done = atomic_read(&ipvs->est_genid_done);
unsigned long delay = HZ / 10; /* repeat startups after failure */
bool repeat = false;
int genid;
int id;
mutex_lock(&ipvs->est_mutex);
genid = atomic_read(&ipvs->est_genid);
for (id = 0; id < ipvs->est_kt_count; id++) {
struct ip_vs_est_kt_data *kd = ipvs->est_kt_arr[id];
/* netns clean up started, abort delayed work */
if (!ipvs->enable)
goto unlock;
if (!kd)
continue;
/* New config ? Stop kthread tasks */
if (genid != genid_done)
ip_vs_est_kthread_stop(kd);
if (!kd->task && !ip_vs_est_stopped(ipvs)) {
/* Do not start kthreads above 0 in calc phase */
if ((!id || !ipvs->est_calc_phase) &&
ip_vs_est_kthread_start(ipvs, kd) < 0)
repeat = true;
}
}
atomic_set(&ipvs->est_genid_done, genid);
if (repeat)
queue_delayed_work(system_long_wq, &ipvs->est_reload_work,
delay);
unlock:
mutex_unlock(&ipvs->est_mutex);
}
int int
ip_vs_use_count_inc(void) ip_vs_use_count_inc(void)
{ {
...@@ -471,7 +511,7 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc) ...@@ -471,7 +511,7 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc)
static void ip_vs_service_free(struct ip_vs_service *svc) static void ip_vs_service_free(struct ip_vs_service *svc)
{ {
free_percpu(svc->stats.cpustats); ip_vs_stats_release(&svc->stats);
kfree(svc); kfree(svc);
} }
...@@ -483,17 +523,14 @@ static void ip_vs_service_rcu_free(struct rcu_head *head) ...@@ -483,17 +523,14 @@ static void ip_vs_service_rcu_free(struct rcu_head *head)
ip_vs_service_free(svc); ip_vs_service_free(svc);
} }
static void __ip_vs_svc_put(struct ip_vs_service *svc, bool do_delay) static void __ip_vs_svc_put(struct ip_vs_service *svc)
{ {
if (atomic_dec_and_test(&svc->refcnt)) { if (atomic_dec_and_test(&svc->refcnt)) {
IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n",
svc->fwmark, svc->fwmark,
IP_VS_DBG_ADDR(svc->af, &svc->addr), IP_VS_DBG_ADDR(svc->af, &svc->addr),
ntohs(svc->port)); ntohs(svc->port));
if (do_delay)
call_rcu(&svc->rcu_head, ip_vs_service_rcu_free); call_rcu(&svc->rcu_head, ip_vs_service_rcu_free);
else
ip_vs_service_free(svc);
} }
} }
...@@ -780,14 +817,22 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, int dest_af, ...@@ -780,14 +817,22 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, int dest_af,
return dest; return dest;
} }
static void ip_vs_dest_rcu_free(struct rcu_head *head)
{
struct ip_vs_dest *dest;
dest = container_of(head, struct ip_vs_dest, rcu_head);
ip_vs_stats_release(&dest->stats);
ip_vs_dest_put_and_free(dest);
}
static void ip_vs_dest_free(struct ip_vs_dest *dest) static void ip_vs_dest_free(struct ip_vs_dest *dest)
{ {
struct ip_vs_service *svc = rcu_dereference_protected(dest->svc, 1); struct ip_vs_service *svc = rcu_dereference_protected(dest->svc, 1);
__ip_vs_dst_cache_reset(dest); __ip_vs_dst_cache_reset(dest);
__ip_vs_svc_put(svc, false); __ip_vs_svc_put(svc);
free_percpu(dest->stats.cpustats); call_rcu(&dest->rcu_head, ip_vs_dest_rcu_free);
ip_vs_dest_put_and_free(dest);
} }
/* /*
...@@ -811,12 +856,22 @@ static void ip_vs_trash_cleanup(struct netns_ipvs *ipvs) ...@@ -811,12 +856,22 @@ static void ip_vs_trash_cleanup(struct netns_ipvs *ipvs)
} }
} }
static void ip_vs_stats_rcu_free(struct rcu_head *head)
{
struct ip_vs_stats_rcu *rs = container_of(head,
struct ip_vs_stats_rcu,
rcu_head);
ip_vs_stats_release(&rs->s);
kfree(rs);
}
static void static void
ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src)
{ {
#define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->kstats.c - src->kstats0.c #define IP_VS_SHOW_STATS_COUNTER(c) dst->c = src->kstats.c - src->kstats0.c
spin_lock_bh(&src->lock); spin_lock(&src->lock);
IP_VS_SHOW_STATS_COUNTER(conns); IP_VS_SHOW_STATS_COUNTER(conns);
IP_VS_SHOW_STATS_COUNTER(inpkts); IP_VS_SHOW_STATS_COUNTER(inpkts);
...@@ -826,7 +881,7 @@ ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src) ...@@ -826,7 +881,7 @@ ip_vs_copy_stats(struct ip_vs_kstats *dst, struct ip_vs_stats *src)
ip_vs_read_estimator(dst, src); ip_vs_read_estimator(dst, src);
spin_unlock_bh(&src->lock); spin_unlock(&src->lock);
} }
static void static void
...@@ -847,7 +902,7 @@ ip_vs_export_stats_user(struct ip_vs_stats_user *dst, struct ip_vs_kstats *src) ...@@ -847,7 +902,7 @@ ip_vs_export_stats_user(struct ip_vs_stats_user *dst, struct ip_vs_kstats *src)
static void static void
ip_vs_zero_stats(struct ip_vs_stats *stats) ip_vs_zero_stats(struct ip_vs_stats *stats)
{ {
spin_lock_bh(&stats->lock); spin_lock(&stats->lock);
/* get current counters as zero point, rates are zeroed */ /* get current counters as zero point, rates are zeroed */
...@@ -861,7 +916,48 @@ ip_vs_zero_stats(struct ip_vs_stats *stats) ...@@ -861,7 +916,48 @@ ip_vs_zero_stats(struct ip_vs_stats *stats)
ip_vs_zero_estimator(stats); ip_vs_zero_estimator(stats);
spin_unlock_bh(&stats->lock); spin_unlock(&stats->lock);
}
/* Allocate fields after kzalloc */
int ip_vs_stats_init_alloc(struct ip_vs_stats *s)
{
int i;
spin_lock_init(&s->lock);
s->cpustats = alloc_percpu(struct ip_vs_cpu_stats);
if (!s->cpustats)
return -ENOMEM;
for_each_possible_cpu(i) {
struct ip_vs_cpu_stats *cs = per_cpu_ptr(s->cpustats, i);
u64_stats_init(&cs->syncp);
}
return 0;
}
struct ip_vs_stats *ip_vs_stats_alloc(void)
{
struct ip_vs_stats *s = kzalloc(sizeof(*s), GFP_KERNEL);
if (s && ip_vs_stats_init_alloc(s) >= 0)
return s;
kfree(s);
return NULL;
}
void ip_vs_stats_release(struct ip_vs_stats *stats)
{
free_percpu(stats->cpustats);
}
void ip_vs_stats_free(struct ip_vs_stats *stats)
{
if (stats) {
ip_vs_stats_release(stats);
kfree(stats);
}
} }
/* /*
...@@ -923,7 +1019,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, ...@@ -923,7 +1019,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
if (old_svc != svc) { if (old_svc != svc) {
ip_vs_zero_stats(&dest->stats); ip_vs_zero_stats(&dest->stats);
__ip_vs_bind_svc(dest, svc); __ip_vs_bind_svc(dest, svc);
__ip_vs_svc_put(old_svc, true); __ip_vs_svc_put(old_svc);
} }
} }
...@@ -942,7 +1038,6 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, ...@@ -942,7 +1038,6 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
spin_unlock_bh(&dest->dst_lock); spin_unlock_bh(&dest->dst_lock);
if (add) { if (add) {
ip_vs_start_estimator(svc->ipvs, &dest->stats);
list_add_rcu(&dest->n_list, &svc->destinations); list_add_rcu(&dest->n_list, &svc->destinations);
svc->num_dests++; svc->num_dests++;
sched = rcu_dereference_protected(svc->scheduler, 1); sched = rcu_dereference_protected(svc->scheduler, 1);
...@@ -963,14 +1058,13 @@ static int ...@@ -963,14 +1058,13 @@ static int
ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
{ {
struct ip_vs_dest *dest; struct ip_vs_dest *dest;
unsigned int atype, i; unsigned int atype;
int ret;
EnterFunction(2); EnterFunction(2);
#ifdef CONFIG_IP_VS_IPV6 #ifdef CONFIG_IP_VS_IPV6
if (udest->af == AF_INET6) { if (udest->af == AF_INET6) {
int ret;
atype = ipv6_addr_type(&udest->addr.in6); atype = ipv6_addr_type(&udest->addr.in6);
if ((!(atype & IPV6_ADDR_UNICAST) || if ((!(atype & IPV6_ADDR_UNICAST) ||
atype & IPV6_ADDR_LINKLOCAL) && atype & IPV6_ADDR_LINKLOCAL) &&
...@@ -992,15 +1086,13 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) ...@@ -992,15 +1086,13 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
if (dest == NULL) if (dest == NULL)
return -ENOMEM; return -ENOMEM;
dest->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); ret = ip_vs_stats_init_alloc(&dest->stats);
if (!dest->stats.cpustats) if (ret < 0)
goto err_alloc; goto err_alloc;
for_each_possible_cpu(i) { ret = ip_vs_start_estimator(svc->ipvs, &dest->stats);
struct ip_vs_cpu_stats *ip_vs_dest_stats; if (ret < 0)
ip_vs_dest_stats = per_cpu_ptr(dest->stats.cpustats, i); goto err_stats;
u64_stats_init(&ip_vs_dest_stats->syncp);
}
dest->af = udest->af; dest->af = udest->af;
dest->protocol = svc->protocol; dest->protocol = svc->protocol;
...@@ -1017,15 +1109,17 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) ...@@ -1017,15 +1109,17 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
INIT_HLIST_NODE(&dest->d_list); INIT_HLIST_NODE(&dest->d_list);
spin_lock_init(&dest->dst_lock); spin_lock_init(&dest->dst_lock);
spin_lock_init(&dest->stats.lock);
__ip_vs_update_dest(svc, dest, udest, 1); __ip_vs_update_dest(svc, dest, udest, 1);
LeaveFunction(2); LeaveFunction(2);
return 0; return 0;
err_stats:
ip_vs_stats_release(&dest->stats);
err_alloc: err_alloc:
kfree(dest); kfree(dest);
return -ENOMEM; return ret;
} }
...@@ -1087,14 +1181,18 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) ...@@ -1087,14 +1181,18 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
IP_VS_DBG_ADDR(svc->af, &dest->vaddr), IP_VS_DBG_ADDR(svc->af, &dest->vaddr),
ntohs(dest->vport)); ntohs(dest->vport));
ret = ip_vs_start_estimator(svc->ipvs, &dest->stats);
if (ret < 0)
goto err;
__ip_vs_update_dest(svc, dest, udest, 1); __ip_vs_update_dest(svc, dest, udest, 1);
ret = 0;
} else { } else {
/* /*
* Allocate and initialize the dest structure * Allocate and initialize the dest structure
*/ */
ret = ip_vs_new_dest(svc, udest); ret = ip_vs_new_dest(svc, udest);
} }
err:
LeaveFunction(2); LeaveFunction(2);
return ret; return ret;
...@@ -1284,7 +1382,7 @@ static int ...@@ -1284,7 +1382,7 @@ static int
ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
struct ip_vs_service **svc_p) struct ip_vs_service **svc_p)
{ {
int ret = 0, i; int ret = 0;
struct ip_vs_scheduler *sched = NULL; struct ip_vs_scheduler *sched = NULL;
struct ip_vs_pe *pe = NULL; struct ip_vs_pe *pe = NULL;
struct ip_vs_service *svc = NULL; struct ip_vs_service *svc = NULL;
...@@ -1344,18 +1442,9 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ...@@ -1344,18 +1442,9 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
ret = -ENOMEM; ret = -ENOMEM;
goto out_err; goto out_err;
} }
svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats); ret = ip_vs_stats_init_alloc(&svc->stats);
if (!svc->stats.cpustats) { if (ret < 0)
ret = -ENOMEM;
goto out_err; goto out_err;
}
for_each_possible_cpu(i) {
struct ip_vs_cpu_stats *ip_vs_stats;
ip_vs_stats = per_cpu_ptr(svc->stats.cpustats, i);
u64_stats_init(&ip_vs_stats->syncp);
}
/* I'm the first user of the service */ /* I'm the first user of the service */
atomic_set(&svc->refcnt, 0); atomic_set(&svc->refcnt, 0);
...@@ -1372,7 +1461,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ...@@ -1372,7 +1461,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
INIT_LIST_HEAD(&svc->destinations); INIT_LIST_HEAD(&svc->destinations);
spin_lock_init(&svc->sched_lock); spin_lock_init(&svc->sched_lock);
spin_lock_init(&svc->stats.lock);
/* Bind the scheduler */ /* Bind the scheduler */
if (sched) { if (sched) {
...@@ -1382,6 +1470,10 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ...@@ -1382,6 +1470,10 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
sched = NULL; sched = NULL;
} }
ret = ip_vs_start_estimator(ipvs, &svc->stats);
if (ret < 0)
goto out_err;
/* Bind the ct retriever */ /* Bind the ct retriever */
RCU_INIT_POINTER(svc->pe, pe); RCU_INIT_POINTER(svc->pe, pe);
pe = NULL; pe = NULL;
...@@ -1394,8 +1486,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ...@@ -1394,8 +1486,6 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
if (svc->pe && svc->pe->conn_out) if (svc->pe && svc->pe->conn_out)
atomic_inc(&ipvs->conn_out_counter); atomic_inc(&ipvs->conn_out_counter);
ip_vs_start_estimator(ipvs, &svc->stats);
/* Count only IPv4 services for old get/setsockopt interface */ /* Count only IPv4 services for old get/setsockopt interface */
if (svc->af == AF_INET) if (svc->af == AF_INET)
ipvs->num_services++; ipvs->num_services++;
...@@ -1406,8 +1496,15 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u, ...@@ -1406,8 +1496,15 @@ ip_vs_add_service(struct netns_ipvs *ipvs, struct ip_vs_service_user_kern *u,
ip_vs_svc_hash(svc); ip_vs_svc_hash(svc);
*svc_p = svc; *svc_p = svc;
if (!ipvs->enable) {
/* Now there is a service - full throttle */ /* Now there is a service - full throttle */
ipvs->enable = 1; ipvs->enable = 1;
/* Start estimation for first time */
ip_vs_est_reload_start(ipvs);
}
return 0; return 0;
...@@ -1571,7 +1668,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup) ...@@ -1571,7 +1668,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup)
/* /*
* Free the service if nobody refers to it * Free the service if nobody refers to it
*/ */
__ip_vs_svc_put(svc, true); __ip_vs_svc_put(svc);
/* decrease the module use count */ /* decrease the module use count */
ip_vs_use_count_dec(); ip_vs_use_count_dec();
...@@ -1761,7 +1858,7 @@ static int ip_vs_zero_all(struct netns_ipvs *ipvs) ...@@ -1761,7 +1858,7 @@ static int ip_vs_zero_all(struct netns_ipvs *ipvs)
} }
} }
ip_vs_zero_stats(&ipvs->tot_stats); ip_vs_zero_stats(&ipvs->tot_stats->s);
return 0; return 0;
} }
...@@ -1843,6 +1940,148 @@ proc_do_sync_ports(struct ctl_table *table, int write, ...@@ -1843,6 +1940,148 @@ proc_do_sync_ports(struct ctl_table *table, int write,
return rc; return rc;
} }
static int ipvs_proc_est_cpumask_set(struct ctl_table *table, void *buffer)
{
struct netns_ipvs *ipvs = table->extra2;
cpumask_var_t *valp = table->data;
cpumask_var_t newmask;
int ret;
if (!zalloc_cpumask_var(&newmask, GFP_KERNEL))
return -ENOMEM;
ret = cpulist_parse(buffer, newmask);
if (ret)
goto out;
mutex_lock(&ipvs->est_mutex);
if (!ipvs->est_cpulist_valid) {
if (!zalloc_cpumask_var(valp, GFP_KERNEL)) {
ret = -ENOMEM;
goto unlock;
}
ipvs->est_cpulist_valid = 1;
}
cpumask_and(newmask, newmask, &current->cpus_mask);
cpumask_copy(*valp, newmask);
/* est_max_threads may depend on cpulist size */
ipvs->est_max_threads = ip_vs_est_max_threads(ipvs);
ipvs->est_calc_phase = 1;
ip_vs_est_reload_start(ipvs);
unlock:
mutex_unlock(&ipvs->est_mutex);
out:
free_cpumask_var(newmask);
return ret;
}
static int ipvs_proc_est_cpumask_get(struct ctl_table *table, void *buffer,
size_t size)
{
struct netns_ipvs *ipvs = table->extra2;
cpumask_var_t *valp = table->data;
struct cpumask *mask;
int ret;
mutex_lock(&ipvs->est_mutex);
if (ipvs->est_cpulist_valid)
mask = *valp;
else
mask = (struct cpumask *)housekeeping_cpumask(HK_TYPE_KTHREAD);
ret = scnprintf(buffer, size, "%*pbl\n", cpumask_pr_args(mask));
mutex_unlock(&ipvs->est_mutex);
return ret;
}
static int ipvs_proc_est_cpulist(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
/* Ignore both read and write(append) if *ppos not 0 */
if (*ppos || !*lenp) {
*lenp = 0;
return 0;
}
if (write) {
/* proc_sys_call_handler() appends terminator */
ret = ipvs_proc_est_cpumask_set(table, buffer);
if (ret >= 0)
*ppos += *lenp;
} else {
/* proc_sys_call_handler() allocates 1 byte for terminator */
ret = ipvs_proc_est_cpumask_get(table, buffer, *lenp + 1);
if (ret >= 0) {
*lenp = ret;
*ppos += *lenp;
ret = 0;
}
}
return ret;
}
static int ipvs_proc_est_nice(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct netns_ipvs *ipvs = table->extra2;
int *valp = table->data;
int val = *valp;
int ret;
struct ctl_table tmp_table = {
.data = &val,
.maxlen = sizeof(int),
.mode = table->mode,
};
ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
if (write && ret >= 0) {
if (val < MIN_NICE || val > MAX_NICE) {
ret = -EINVAL;
} else {
mutex_lock(&ipvs->est_mutex);
if (*valp != val) {
*valp = val;
ip_vs_est_reload_start(ipvs);
}
mutex_unlock(&ipvs->est_mutex);
}
}
return ret;
}
static int ipvs_proc_run_estimation(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct netns_ipvs *ipvs = table->extra2;
int *valp = table->data;
int val = *valp;
int ret;
struct ctl_table tmp_table = {
.data = &val,
.maxlen = sizeof(int),
.mode = table->mode,
};
ret = proc_dointvec(&tmp_table, write, buffer, lenp, ppos);
if (write && ret >= 0) {
mutex_lock(&ipvs->est_mutex);
if (*valp != val) {
*valp = val;
ip_vs_est_reload_start(ipvs);
}
mutex_unlock(&ipvs->est_mutex);
}
return ret;
}
/* /*
* IPVS sysctl table (under the /proc/sys/net/ipv4/vs/) * IPVS sysctl table (under the /proc/sys/net/ipv4/vs/)
* Do not change order or insert new entries without * Do not change order or insert new entries without
...@@ -2017,7 +2256,19 @@ static struct ctl_table vs_vars[] = { ...@@ -2017,7 +2256,19 @@ static struct ctl_table vs_vars[] = {
.procname = "run_estimation", .procname = "run_estimation",
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec, .proc_handler = ipvs_proc_run_estimation,
},
{
.procname = "est_cpulist",
.maxlen = NR_CPUS, /* unused */
.mode = 0644,
.proc_handler = ipvs_proc_est_cpulist,
},
{
.procname = "est_nice",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = ipvs_proc_est_nice,
}, },
#ifdef CONFIG_IP_VS_DEBUG #ifdef CONFIG_IP_VS_DEBUG
{ {
...@@ -2255,7 +2506,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) ...@@ -2255,7 +2506,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v)
seq_puts(seq, seq_puts(seq,
" Conns Packets Packets Bytes Bytes\n"); " Conns Packets Packets Bytes Bytes\n");
ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats); ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats->s);
seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n\n", seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n\n",
(unsigned long long)show.conns, (unsigned long long)show.conns,
(unsigned long long)show.inpkts, (unsigned long long)show.inpkts,
...@@ -2279,7 +2530,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) ...@@ -2279,7 +2530,7 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v)
static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v)
{ {
struct net *net = seq_file_single_net(seq); struct net *net = seq_file_single_net(seq);
struct ip_vs_stats *tot_stats = &net_ipvs(net)->tot_stats; struct ip_vs_stats *tot_stats = &net_ipvs(net)->tot_stats->s;
struct ip_vs_cpu_stats __percpu *cpustats = tot_stats->cpustats; struct ip_vs_cpu_stats __percpu *cpustats = tot_stats->cpustats;
struct ip_vs_kstats kstats; struct ip_vs_kstats kstats;
int i; int i;
...@@ -2297,11 +2548,11 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) ...@@ -2297,11 +2548,11 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v)
do { do {
start = u64_stats_fetch_begin(&u->syncp); start = u64_stats_fetch_begin(&u->syncp);
conns = u->cnt.conns; conns = u64_stats_read(&u->cnt.conns);
inpkts = u->cnt.inpkts; inpkts = u64_stats_read(&u->cnt.inpkts);
outpkts = u->cnt.outpkts; outpkts = u64_stats_read(&u->cnt.outpkts);
inbytes = u->cnt.inbytes; inbytes = u64_stats_read(&u->cnt.inbytes);
outbytes = u->cnt.outbytes; outbytes = u64_stats_read(&u->cnt.outbytes);
} while (u64_stats_fetch_retry(&u->syncp, start)); } while (u64_stats_fetch_retry(&u->syncp, start));
seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n", seq_printf(seq, "%3X %8LX %8LX %8LX %16LX %16LX\n",
...@@ -4027,13 +4278,17 @@ static void ip_vs_genl_unregister(void) ...@@ -4027,13 +4278,17 @@ static void ip_vs_genl_unregister(void)
static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
{ {
struct net *net = ipvs->net; struct net *net = ipvs->net;
int idx;
struct ctl_table *tbl; struct ctl_table *tbl;
int idx, ret;
atomic_set(&ipvs->dropentry, 0); atomic_set(&ipvs->dropentry, 0);
spin_lock_init(&ipvs->dropentry_lock); spin_lock_init(&ipvs->dropentry_lock);
spin_lock_init(&ipvs->droppacket_lock); spin_lock_init(&ipvs->droppacket_lock);
spin_lock_init(&ipvs->securetcp_lock); spin_lock_init(&ipvs->securetcp_lock);
INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler);
INIT_DELAYED_WORK(&ipvs->expire_nodest_conn_work,
expire_nodest_conn_handler);
ipvs->est_stopped = 0;
if (!net_eq(net, &init_net)) { if (!net_eq(net, &init_net)) {
tbl = kmemdup(vs_vars, sizeof(vs_vars), GFP_KERNEL); tbl = kmemdup(vs_vars, sizeof(vs_vars), GFP_KERNEL);
...@@ -4094,31 +4349,44 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs) ...@@ -4094,31 +4349,44 @@ static int __net_init ip_vs_control_net_init_sysctl(struct netns_ipvs *ipvs)
tbl[idx++].data = &ipvs->sysctl_schedule_icmp; tbl[idx++].data = &ipvs->sysctl_schedule_icmp;
tbl[idx++].data = &ipvs->sysctl_ignore_tunneled; tbl[idx++].data = &ipvs->sysctl_ignore_tunneled;
ipvs->sysctl_run_estimation = 1; ipvs->sysctl_run_estimation = 1;
tbl[idx].extra2 = ipvs;
tbl[idx++].data = &ipvs->sysctl_run_estimation; tbl[idx++].data = &ipvs->sysctl_run_estimation;
ipvs->est_cpulist_valid = 0;
tbl[idx].extra2 = ipvs;
tbl[idx++].data = &ipvs->sysctl_est_cpulist;
ipvs->sysctl_est_nice = IPVS_EST_NICE;
tbl[idx].extra2 = ipvs;
tbl[idx++].data = &ipvs->sysctl_est_nice;
#ifdef CONFIG_IP_VS_DEBUG #ifdef CONFIG_IP_VS_DEBUG
/* Global sysctls must be ro in non-init netns */ /* Global sysctls must be ro in non-init netns */
if (!net_eq(net, &init_net)) if (!net_eq(net, &init_net))
tbl[idx++].mode = 0444; tbl[idx++].mode = 0444;
#endif #endif
ret = -ENOMEM;
ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl); ipvs->sysctl_hdr = register_net_sysctl(net, "net/ipv4/vs", tbl);
if (ipvs->sysctl_hdr == NULL) { if (!ipvs->sysctl_hdr)
if (!net_eq(net, &init_net)) goto err;
kfree(tbl);
return -ENOMEM;
}
ip_vs_start_estimator(ipvs, &ipvs->tot_stats);
ipvs->sysctl_tbl = tbl; ipvs->sysctl_tbl = tbl;
ret = ip_vs_start_estimator(ipvs, &ipvs->tot_stats->s);
if (ret < 0)
goto err;
/* Schedule defense work */ /* Schedule defense work */
INIT_DELAYED_WORK(&ipvs->defense_work, defense_work_handler);
queue_delayed_work(system_long_wq, &ipvs->defense_work, queue_delayed_work(system_long_wq, &ipvs->defense_work,
DEFENSE_TIMER_PERIOD); DEFENSE_TIMER_PERIOD);
/* Init delayed work for expiring no dest conn */
INIT_DELAYED_WORK(&ipvs->expire_nodest_conn_work,
expire_nodest_conn_handler);
return 0; return 0;
err:
unregister_net_sysctl_table(ipvs->sysctl_hdr);
if (!net_eq(net, &init_net))
kfree(tbl);
return ret;
} }
static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs)
...@@ -4129,7 +4397,10 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs) ...@@ -4129,7 +4397,10 @@ static void __net_exit ip_vs_control_net_cleanup_sysctl(struct netns_ipvs *ipvs)
cancel_delayed_work_sync(&ipvs->defense_work); cancel_delayed_work_sync(&ipvs->defense_work);
cancel_work_sync(&ipvs->defense_work.work); cancel_work_sync(&ipvs->defense_work.work);
unregister_net_sysctl_table(ipvs->sysctl_hdr); unregister_net_sysctl_table(ipvs->sysctl_hdr);
ip_vs_stop_estimator(ipvs, &ipvs->tot_stats); ip_vs_stop_estimator(ipvs, &ipvs->tot_stats->s);
if (ipvs->est_cpulist_valid)
free_cpumask_var(ipvs->sysctl_est_cpulist);
if (!net_eq(net, &init_net)) if (!net_eq(net, &init_net))
kfree(ipvs->sysctl_tbl); kfree(ipvs->sysctl_tbl);
...@@ -4151,7 +4422,8 @@ static struct notifier_block ip_vs_dst_notifier = { ...@@ -4151,7 +4422,8 @@ static struct notifier_block ip_vs_dst_notifier = {
int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)
{ {
int i, idx; int ret = -ENOMEM;
int idx;
/* Initialize rs_table */ /* Initialize rs_table */
for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++) for (idx = 0; idx < IP_VS_RTAB_SIZE; idx++)
...@@ -4164,18 +4436,14 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) ...@@ -4164,18 +4436,14 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)
atomic_set(&ipvs->nullsvc_counter, 0); atomic_set(&ipvs->nullsvc_counter, 0);
atomic_set(&ipvs->conn_out_counter, 0); atomic_set(&ipvs->conn_out_counter, 0);
/* procfs stats */ INIT_DELAYED_WORK(&ipvs->est_reload_work, est_reload_work_handler);
ipvs->tot_stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats);
if (!ipvs->tot_stats.cpustats)
return -ENOMEM;
for_each_possible_cpu(i) { /* procfs stats */
struct ip_vs_cpu_stats *ipvs_tot_stats; ipvs->tot_stats = kzalloc(sizeof(*ipvs->tot_stats), GFP_KERNEL);
ipvs_tot_stats = per_cpu_ptr(ipvs->tot_stats.cpustats, i); if (!ipvs->tot_stats)
u64_stats_init(&ipvs_tot_stats->syncp); goto out;
} if (ip_vs_stats_init_alloc(&ipvs->tot_stats->s) < 0)
goto err_tot_stats;
spin_lock_init(&ipvs->tot_stats.lock);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (!proc_create_net("ip_vs", 0, ipvs->net->proc_net, if (!proc_create_net("ip_vs", 0, ipvs->net->proc_net,
...@@ -4190,7 +4458,8 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) ...@@ -4190,7 +4458,8 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)
goto err_percpu; goto err_percpu;
#endif #endif
if (ip_vs_control_net_init_sysctl(ipvs)) ret = ip_vs_control_net_init_sysctl(ipvs);
if (ret < 0)
goto err; goto err;
return 0; return 0;
...@@ -4207,20 +4476,26 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs) ...@@ -4207,20 +4476,26 @@ int __net_init ip_vs_control_net_init(struct netns_ipvs *ipvs)
err_vs: err_vs:
#endif #endif
free_percpu(ipvs->tot_stats.cpustats); ip_vs_stats_release(&ipvs->tot_stats->s);
return -ENOMEM;
err_tot_stats:
kfree(ipvs->tot_stats);
out:
return ret;
} }
void __net_exit ip_vs_control_net_cleanup(struct netns_ipvs *ipvs) void __net_exit ip_vs_control_net_cleanup(struct netns_ipvs *ipvs)
{ {
ip_vs_trash_cleanup(ipvs); ip_vs_trash_cleanup(ipvs);
ip_vs_control_net_cleanup_sysctl(ipvs); ip_vs_control_net_cleanup_sysctl(ipvs);
cancel_delayed_work_sync(&ipvs->est_reload_work);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
remove_proc_entry("ip_vs_stats_percpu", ipvs->net->proc_net); remove_proc_entry("ip_vs_stats_percpu", ipvs->net->proc_net);
remove_proc_entry("ip_vs_stats", ipvs->net->proc_net); remove_proc_entry("ip_vs_stats", ipvs->net->proc_net);
remove_proc_entry("ip_vs", ipvs->net->proc_net); remove_proc_entry("ip_vs", ipvs->net->proc_net);
#endif #endif
free_percpu(ipvs->tot_stats.cpustats); call_rcu(&ipvs->tot_stats->rcu_head, ip_vs_stats_rcu_free);
} }
int __init ip_vs_register_nl_ioctl(void) int __init ip_vs_register_nl_ioctl(void)
...@@ -4280,5 +4555,6 @@ void ip_vs_control_cleanup(void) ...@@ -4280,5 +4555,6 @@ void ip_vs_control_cleanup(void)
{ {
EnterFunction(2); EnterFunction(2);
unregister_netdevice_notifier(&ip_vs_dst_notifier); unregister_netdevice_notifier(&ip_vs_dst_notifier);
/* relying on common rcu_barrier() in ip_vs_cleanup() */
LeaveFunction(2); LeaveFunction(2);
} }
...@@ -30,9 +30,6 @@ ...@@ -30,9 +30,6 @@
long interval, it is easy to implement a user level daemon which long interval, it is easy to implement a user level daemon which
periodically reads those statistical counters and measure rate. periodically reads those statistical counters and measure rate.
Currently, the measurement is activated by slow timer handler. Hope
this measurement will not introduce too much load.
We measure rate during the last 8 seconds every 2 seconds: We measure rate during the last 8 seconds every 2 seconds:
avgrate = avgrate*(1-W) + rate*W avgrate = avgrate*(1-W) + rate*W
...@@ -47,68 +44,79 @@ ...@@ -47,68 +44,79 @@
to 32-bit values for conns, packets, bps, cps and pps. to 32-bit values for conns, packets, bps, cps and pps.
* A lot of code is taken from net/core/gen_estimator.c * A lot of code is taken from net/core/gen_estimator.c
*/
/* KEY POINTS:
* Make a summary from each cpu - cpustats counters are updated per-cpu in SoftIRQ context with BH disabled
- kthreads read the cpustats to update the estimators (svcs, dests, total)
- the states of estimators can be read (get stats) or modified (zero stats)
from processes
KTHREADS:
- estimators are added initially to est_temp_list and later kthread 0
distributes them to one or many kthreads for estimation
- kthread contexts are created and attached to array
- the kthread tasks are started when first service is added, before that
the total stats are not estimated
- when configuration (cpulist/nice) is changed, the tasks are restarted
by work (est_reload_work)
- kthread tasks are stopped while the cpulist is empty
- the kthread context holds lists with estimators (chains) which are
processed every 2 seconds
- as estimators can be added dynamically and in bursts, we try to spread
them to multiple chains which are estimated at different time
- on start, kthread 0 enters calculation phase to determine the chain limits
and the limit of estimators per kthread
- est_add_ktid: ktid where to add new ests, can point to empty slot where
we should add kt data
*/ */
static void ip_vs_read_cpu_stats(struct ip_vs_kstats *sum,
struct ip_vs_cpu_stats __percpu *stats)
{
int i;
bool add = false;
for_each_possible_cpu(i) { static struct lock_class_key __ipvs_est_key;
struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i);
unsigned int start;
u64 conns, inpkts, outpkts, inbytes, outbytes;
if (add) {
do {
start = u64_stats_fetch_begin(&s->syncp);
conns = s->cnt.conns;
inpkts = s->cnt.inpkts;
outpkts = s->cnt.outpkts;
inbytes = s->cnt.inbytes;
outbytes = s->cnt.outbytes;
} while (u64_stats_fetch_retry(&s->syncp, start));
sum->conns += conns;
sum->inpkts += inpkts;
sum->outpkts += outpkts;
sum->inbytes += inbytes;
sum->outbytes += outbytes;
} else {
add = true;
do {
start = u64_stats_fetch_begin(&s->syncp);
sum->conns = s->cnt.conns;
sum->inpkts = s->cnt.inpkts;
sum->outpkts = s->cnt.outpkts;
sum->inbytes = s->cnt.inbytes;
sum->outbytes = s->cnt.outbytes;
} while (u64_stats_fetch_retry(&s->syncp, start));
}
}
}
static void ip_vs_est_calc_phase(struct netns_ipvs *ipvs);
static void ip_vs_est_drain_temp_list(struct netns_ipvs *ipvs);
static void estimation_timer(struct timer_list *t) static void ip_vs_chain_estimation(struct hlist_head *chain)
{ {
struct ip_vs_estimator *e; struct ip_vs_estimator *e;
struct ip_vs_cpu_stats *c;
struct ip_vs_stats *s; struct ip_vs_stats *s;
u64 rate; u64 rate;
struct netns_ipvs *ipvs = from_timer(ipvs, t, est_timer);
if (!sysctl_run_estimation(ipvs)) hlist_for_each_entry_rcu(e, chain, list) {
goto skip; u64 conns, inpkts, outpkts, inbytes, outbytes;
u64 kconns = 0, kinpkts = 0, koutpkts = 0;
u64 kinbytes = 0, koutbytes = 0;
unsigned int start;
int i;
if (kthread_should_stop())
break;
spin_lock(&ipvs->est_lock);
list_for_each_entry(e, &ipvs->est_list, list) {
s = container_of(e, struct ip_vs_stats, est); s = container_of(e, struct ip_vs_stats, est);
for_each_possible_cpu(i) {
c = per_cpu_ptr(s->cpustats, i);
do {
start = u64_stats_fetch_begin(&c->syncp);
conns = u64_stats_read(&c->cnt.conns);
inpkts = u64_stats_read(&c->cnt.inpkts);
outpkts = u64_stats_read(&c->cnt.outpkts);
inbytes = u64_stats_read(&c->cnt.inbytes);
outbytes = u64_stats_read(&c->cnt.outbytes);
} while (u64_stats_fetch_retry(&c->syncp, start));
kconns += conns;
kinpkts += inpkts;
koutpkts += outpkts;
kinbytes += inbytes;
koutbytes += outbytes;
}
spin_lock(&s->lock); spin_lock(&s->lock);
ip_vs_read_cpu_stats(&s->kstats, s->cpustats);
s->kstats.conns = kconns;
s->kstats.inpkts = kinpkts;
s->kstats.outpkts = koutpkts;
s->kstats.inbytes = kinbytes;
s->kstats.outbytes = koutbytes;
/* scaled by 2^10, but divided 2 seconds */ /* scaled by 2^10, but divided 2 seconds */
rate = (s->kstats.conns - e->last_conns) << 9; rate = (s->kstats.conns - e->last_conns) << 9;
...@@ -133,30 +141,757 @@ static void estimation_timer(struct timer_list *t) ...@@ -133,30 +141,757 @@ static void estimation_timer(struct timer_list *t)
e->outbps += ((s64)rate - (s64)e->outbps) >> 2; e->outbps += ((s64)rate - (s64)e->outbps) >> 2;
spin_unlock(&s->lock); spin_unlock(&s->lock);
} }
spin_unlock(&ipvs->est_lock); }
static void ip_vs_tick_estimation(struct ip_vs_est_kt_data *kd, int row)
{
struct ip_vs_est_tick_data *td;
int cid;
rcu_read_lock();
td = rcu_dereference(kd->ticks[row]);
if (!td)
goto out;
for_each_set_bit(cid, td->present, IPVS_EST_TICK_CHAINS) {
if (kthread_should_stop())
break;
ip_vs_chain_estimation(&td->chains[cid]);
cond_resched_rcu();
td = rcu_dereference(kd->ticks[row]);
if (!td)
break;
}
out:
rcu_read_unlock();
}
static int ip_vs_estimation_kthread(void *data)
{
struct ip_vs_est_kt_data *kd = data;
struct netns_ipvs *ipvs = kd->ipvs;
int row = kd->est_row;
unsigned long now;
int id = kd->id;
long gap;
if (id > 0) {
if (!ipvs->est_chain_max)
return 0;
} else {
if (!ipvs->est_chain_max) {
ipvs->est_calc_phase = 1;
/* commit est_calc_phase before reading est_genid */
smp_mb();
}
/* kthread 0 will handle the calc phase */
if (ipvs->est_calc_phase)
ip_vs_est_calc_phase(ipvs);
}
while (1) {
if (!id && !hlist_empty(&ipvs->est_temp_list))
ip_vs_est_drain_temp_list(ipvs);
set_current_state(TASK_IDLE);
if (kthread_should_stop())
break;
/* before estimation, check if we should sleep */
now = jiffies;
gap = kd->est_timer - now;
if (gap > 0) {
if (gap > IPVS_EST_TICK) {
kd->est_timer = now - IPVS_EST_TICK;
gap = IPVS_EST_TICK;
}
schedule_timeout(gap);
} else {
__set_current_state(TASK_RUNNING);
if (gap < -8 * IPVS_EST_TICK)
kd->est_timer = now;
}
if (kd->tick_len[row])
ip_vs_tick_estimation(kd, row);
row++;
if (row >= IPVS_EST_NTICKS)
row = 0;
WRITE_ONCE(kd->est_row, row);
kd->est_timer += IPVS_EST_TICK;
}
__set_current_state(TASK_RUNNING);
return 0;
}
/* Schedule stop/start for kthread tasks */
void ip_vs_est_reload_start(struct netns_ipvs *ipvs)
{
/* Ignore reloads before first service is added */
if (!ipvs->enable)
return;
ip_vs_est_stopped_recalc(ipvs);
/* Bump the kthread configuration genid */
atomic_inc(&ipvs->est_genid);
queue_delayed_work(system_long_wq, &ipvs->est_reload_work, 0);
}
/* Start kthread task with current configuration */
int ip_vs_est_kthread_start(struct netns_ipvs *ipvs,
struct ip_vs_est_kt_data *kd)
{
unsigned long now;
int ret = 0;
long gap;
lockdep_assert_held(&ipvs->est_mutex);
if (kd->task)
goto out;
now = jiffies;
gap = kd->est_timer - now;
/* Sync est_timer if task is starting later */
if (abs(gap) > 4 * IPVS_EST_TICK)
kd->est_timer = now;
kd->task = kthread_create(ip_vs_estimation_kthread, kd, "ipvs-e:%d:%d",
ipvs->gen, kd->id);
if (IS_ERR(kd->task)) {
ret = PTR_ERR(kd->task);
kd->task = NULL;
goto out;
}
set_user_nice(kd->task, sysctl_est_nice(ipvs));
set_cpus_allowed_ptr(kd->task, sysctl_est_cpulist(ipvs));
pr_info("starting estimator thread %d...\n", kd->id);
wake_up_process(kd->task);
out:
return ret;
}
void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd)
{
if (kd->task) {
pr_info("stopping estimator thread %d...\n", kd->id);
kthread_stop(kd->task);
kd->task = NULL;
}
}
/* Apply parameters to kthread */
static void ip_vs_est_set_params(struct netns_ipvs *ipvs,
struct ip_vs_est_kt_data *kd)
{
kd->chain_max = ipvs->est_chain_max;
/* We are using single chain on RCU preemption */
if (IPVS_EST_TICK_CHAINS == 1)
kd->chain_max *= IPVS_EST_CHAIN_FACTOR;
kd->tick_max = IPVS_EST_TICK_CHAINS * kd->chain_max;
kd->est_max_count = IPVS_EST_NTICKS * kd->tick_max;
}
/* Create and start estimation kthread in a free or new array slot */
static int ip_vs_est_add_kthread(struct netns_ipvs *ipvs)
{
struct ip_vs_est_kt_data *kd = NULL;
int id = ipvs->est_kt_count;
int ret = -ENOMEM;
void *arr = NULL;
int i;
if ((unsigned long)ipvs->est_kt_count >= ipvs->est_max_threads &&
ipvs->enable && ipvs->est_max_threads)
return -EINVAL;
mutex_lock(&ipvs->est_mutex);
for (i = 0; i < id; i++) {
if (!ipvs->est_kt_arr[i])
break;
}
if (i >= id) {
arr = krealloc_array(ipvs->est_kt_arr, id + 1,
sizeof(struct ip_vs_est_kt_data *),
GFP_KERNEL);
if (!arr)
goto out;
ipvs->est_kt_arr = arr;
} else {
id = i;
}
kd = kzalloc(sizeof(*kd), GFP_KERNEL);
if (!kd)
goto out;
kd->ipvs = ipvs;
bitmap_fill(kd->avail, IPVS_EST_NTICKS);
kd->est_timer = jiffies;
kd->id = id;
ip_vs_est_set_params(ipvs, kd);
/* Pre-allocate stats used in calc phase */
if (!id && !kd->calc_stats) {
kd->calc_stats = ip_vs_stats_alloc();
if (!kd->calc_stats)
goto out;
}
/* Start kthread tasks only when services are present */
if (ipvs->enable && !ip_vs_est_stopped(ipvs)) {
ret = ip_vs_est_kthread_start(ipvs, kd);
if (ret < 0)
goto out;
}
if (arr)
ipvs->est_kt_count++;
ipvs->est_kt_arr[id] = kd;
kd = NULL;
/* Use most recent kthread for new ests */
ipvs->est_add_ktid = id;
ret = 0;
out:
mutex_unlock(&ipvs->est_mutex);
if (kd) {
ip_vs_stats_free(kd->calc_stats);
kfree(kd);
}
return ret;
}
/* Select ktid where to add new ests: available, unused or new slot */
static void ip_vs_est_update_ktid(struct netns_ipvs *ipvs)
{
int ktid, best = ipvs->est_kt_count;
struct ip_vs_est_kt_data *kd;
for (ktid = 0; ktid < ipvs->est_kt_count; ktid++) {
kd = ipvs->est_kt_arr[ktid];
if (kd) {
if (kd->est_count < kd->est_max_count) {
best = ktid;
break;
}
} else if (ktid < best) {
best = ktid;
}
}
ipvs->est_add_ktid = best;
}
/* Add estimator to current kthread (est_add_ktid) */
static int ip_vs_enqueue_estimator(struct netns_ipvs *ipvs,
struct ip_vs_estimator *est)
{
struct ip_vs_est_kt_data *kd = NULL;
struct ip_vs_est_tick_data *td;
int ktid, row, crow, cid, ret;
int delay = est->ktrow;
BUILD_BUG_ON_MSG(IPVS_EST_TICK_CHAINS > 127,
"Too many chains for ktcid");
if (ipvs->est_add_ktid < ipvs->est_kt_count) {
kd = ipvs->est_kt_arr[ipvs->est_add_ktid];
if (kd)
goto add_est;
}
skip: ret = ip_vs_est_add_kthread(ipvs);
mod_timer(&ipvs->est_timer, jiffies + 2*HZ); if (ret < 0)
goto out;
kd = ipvs->est_kt_arr[ipvs->est_add_ktid];
add_est:
ktid = kd->id;
/* For small number of estimators prefer to use few ticks,
* otherwise try to add into the last estimated row.
* est_row and add_row point after the row we should use
*/
if (kd->est_count >= 2 * kd->tick_max || delay < IPVS_EST_NTICKS - 1)
crow = READ_ONCE(kd->est_row);
else
crow = kd->add_row;
crow += delay;
if (crow >= IPVS_EST_NTICKS)
crow -= IPVS_EST_NTICKS;
/* Assume initial delay ? */
if (delay >= IPVS_EST_NTICKS - 1) {
/* Preserve initial delay or decrease it if no space in tick */
row = crow;
if (crow < IPVS_EST_NTICKS - 1) {
crow++;
row = find_last_bit(kd->avail, crow);
}
if (row >= crow)
row = find_last_bit(kd->avail, IPVS_EST_NTICKS);
} else {
/* Preserve delay or increase it if no space in tick */
row = IPVS_EST_NTICKS;
if (crow > 0)
row = find_next_bit(kd->avail, IPVS_EST_NTICKS, crow);
if (row >= IPVS_EST_NTICKS)
row = find_first_bit(kd->avail, IPVS_EST_NTICKS);
}
td = rcu_dereference_protected(kd->ticks[row], 1);
if (!td) {
td = kzalloc(sizeof(*td), GFP_KERNEL);
if (!td) {
ret = -ENOMEM;
goto out;
}
rcu_assign_pointer(kd->ticks[row], td);
}
cid = find_first_zero_bit(td->full, IPVS_EST_TICK_CHAINS);
kd->est_count++;
kd->tick_len[row]++;
if (!td->chain_len[cid])
__set_bit(cid, td->present);
td->chain_len[cid]++;
est->ktid = ktid;
est->ktrow = row;
est->ktcid = cid;
hlist_add_head_rcu(&est->list, &td->chains[cid]);
if (td->chain_len[cid] >= kd->chain_max) {
__set_bit(cid, td->full);
if (kd->tick_len[row] >= kd->tick_max)
__clear_bit(row, kd->avail);
}
/* Update est_add_ktid to point to first available/empty kt slot */
if (kd->est_count == kd->est_max_count)
ip_vs_est_update_ktid(ipvs);
ret = 0;
out:
return ret;
} }
void ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) /* Start estimation for stats */
int ip_vs_start_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
{ {
struct ip_vs_estimator *est = &stats->est; struct ip_vs_estimator *est = &stats->est;
int ret;
if (!ipvs->est_max_threads && ipvs->enable)
ipvs->est_max_threads = ip_vs_est_max_threads(ipvs);
est->ktid = -1;
est->ktrow = IPVS_EST_NTICKS - 1; /* Initial delay */
INIT_LIST_HEAD(&est->list); /* We prefer this code to be short, kthread 0 will requeue the
* estimator to available chain. If tasks are disabled, we
* will not allocate much memory, just for kt 0.
*/
ret = 0;
if (!ipvs->est_kt_count || !ipvs->est_kt_arr[0])
ret = ip_vs_est_add_kthread(ipvs);
if (ret >= 0)
hlist_add_head(&est->list, &ipvs->est_temp_list);
else
INIT_HLIST_NODE(&est->list);
return ret;
}
spin_lock_bh(&ipvs->est_lock); static void ip_vs_est_kthread_destroy(struct ip_vs_est_kt_data *kd)
list_add(&est->list, &ipvs->est_list); {
spin_unlock_bh(&ipvs->est_lock); if (kd) {
if (kd->task) {
pr_info("stop unused estimator thread %d...\n", kd->id);
kthread_stop(kd->task);
}
ip_vs_stats_free(kd->calc_stats);
kfree(kd);
}
} }
/* Unlink estimator from chain */
void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats) void ip_vs_stop_estimator(struct netns_ipvs *ipvs, struct ip_vs_stats *stats)
{ {
struct ip_vs_estimator *est = &stats->est; struct ip_vs_estimator *est = &stats->est;
struct ip_vs_est_tick_data *td;
struct ip_vs_est_kt_data *kd;
int ktid = est->ktid;
int row = est->ktrow;
int cid = est->ktcid;
/* Failed to add to chain ? */
if (hlist_unhashed(&est->list))
return;
/* On return, estimator can be freed, dequeue it now */
/* In est_temp_list ? */
if (ktid < 0) {
hlist_del(&est->list);
goto end_kt0;
}
hlist_del_rcu(&est->list);
kd = ipvs->est_kt_arr[ktid];
td = rcu_dereference_protected(kd->ticks[row], 1);
__clear_bit(cid, td->full);
td->chain_len[cid]--;
if (!td->chain_len[cid])
__clear_bit(cid, td->present);
kd->tick_len[row]--;
__set_bit(row, kd->avail);
if (!kd->tick_len[row]) {
RCU_INIT_POINTER(kd->ticks[row], NULL);
kfree_rcu(td);
}
kd->est_count--;
if (kd->est_count) {
/* This kt slot can become available just now, prefer it */
if (ktid < ipvs->est_add_ktid)
ipvs->est_add_ktid = ktid;
return;
}
if (ktid > 0) {
mutex_lock(&ipvs->est_mutex);
ip_vs_est_kthread_destroy(kd);
ipvs->est_kt_arr[ktid] = NULL;
if (ktid == ipvs->est_kt_count - 1) {
ipvs->est_kt_count--;
while (ipvs->est_kt_count > 1 &&
!ipvs->est_kt_arr[ipvs->est_kt_count - 1])
ipvs->est_kt_count--;
}
mutex_unlock(&ipvs->est_mutex);
/* This slot is now empty, prefer another available kt slot */
if (ktid == ipvs->est_add_ktid)
ip_vs_est_update_ktid(ipvs);
}
end_kt0:
/* kt 0 is freed after all other kthreads and chains are empty */
if (ipvs->est_kt_count == 1 && hlist_empty(&ipvs->est_temp_list)) {
kd = ipvs->est_kt_arr[0];
if (!kd || !kd->est_count) {
mutex_lock(&ipvs->est_mutex);
if (kd) {
ip_vs_est_kthread_destroy(kd);
ipvs->est_kt_arr[0] = NULL;
}
ipvs->est_kt_count--;
mutex_unlock(&ipvs->est_mutex);
ipvs->est_add_ktid = 0;
}
}
}
/* Register all ests from est_temp_list to kthreads */
static void ip_vs_est_drain_temp_list(struct netns_ipvs *ipvs)
{
struct ip_vs_estimator *est;
while (1) {
int max = 16;
mutex_lock(&__ip_vs_mutex);
while (max-- > 0) {
est = hlist_entry_safe(ipvs->est_temp_list.first,
struct ip_vs_estimator, list);
if (est) {
if (kthread_should_stop())
goto unlock;
hlist_del_init(&est->list);
if (ip_vs_enqueue_estimator(ipvs, est) >= 0)
continue;
est->ktid = -1;
hlist_add_head(&est->list,
&ipvs->est_temp_list);
/* Abort, some entries will not be estimated
* until next attempt
*/
}
goto unlock;
}
mutex_unlock(&__ip_vs_mutex);
cond_resched();
}
unlock:
mutex_unlock(&__ip_vs_mutex);
}
/* Calculate limits for all kthreads */
static int ip_vs_est_calc_limits(struct netns_ipvs *ipvs, int *chain_max)
{
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
struct ip_vs_est_kt_data *kd;
struct hlist_head chain;
struct ip_vs_stats *s;
int cache_factor = 4;
int i, loops, ntest;
s32 min_est = 0;
ktime_t t1, t2;
s64 diff, val;
int max = 8;
int ret = 1;
INIT_HLIST_HEAD(&chain);
mutex_lock(&__ip_vs_mutex);
kd = ipvs->est_kt_arr[0];
mutex_unlock(&__ip_vs_mutex);
s = kd ? kd->calc_stats : NULL;
if (!s)
goto out;
hlist_add_head(&s->est.list, &chain);
loops = 1;
/* Get best result from many tests */
for (ntest = 0; ntest < 12; ntest++) {
if (!(ntest & 3)) {
/* Wait for cpufreq frequency transition */
wait_event_idle_timeout(wq, kthread_should_stop(),
HZ / 50);
if (!ipvs->enable || kthread_should_stop())
goto stop;
}
local_bh_disable();
rcu_read_lock();
/* Put stats in cache */
ip_vs_chain_estimation(&chain);
t1 = ktime_get();
for (i = loops * cache_factor; i > 0; i--)
ip_vs_chain_estimation(&chain);
t2 = ktime_get();
rcu_read_unlock();
local_bh_enable();
if (!ipvs->enable || kthread_should_stop())
goto stop;
cond_resched();
diff = ktime_to_ns(ktime_sub(t2, t1));
if (diff <= 1 * NSEC_PER_USEC) {
/* Do more loops on low time resolution */
loops *= 2;
continue;
}
if (diff >= NSEC_PER_SEC)
continue;
val = diff;
do_div(val, loops);
if (!min_est || val < min_est) {
min_est = val;
/* goal: 95usec per chain */
val = 95 * NSEC_PER_USEC;
if (val >= min_est) {
do_div(val, min_est);
max = (int)val;
} else {
max = 1;
}
}
}
out:
if (s)
hlist_del_init(&s->est.list);
*chain_max = max;
return ret;
spin_lock_bh(&ipvs->est_lock); stop:
list_del(&est->list); ret = 0;
spin_unlock_bh(&ipvs->est_lock); goto out;
}
/* Calculate the parameters and apply them in context of kt #0
* ECP: est_calc_phase
* ECM: est_chain_max
* ECP ECM Insert Chain enable Description
* ---------------------------------------------------------------------------
* 0 0 est_temp_list 0 create kt #0 context
* 0 0 est_temp_list 0->1 service added, start kthread #0 task
* 0->1 0 est_temp_list 1 kt task #0 started, enters calc phase
* 1 0 est_temp_list 1 kt #0: determine est_chain_max,
* stop tasks, move ests to est_temp_list
* and free kd for kthreads 1..last
* 1->0 0->N kt chains 1 ests can go to kthreads
* 0 N kt chains 1 drain est_temp_list, create new kthread
* contexts, start tasks, estimate
*/
static void ip_vs_est_calc_phase(struct netns_ipvs *ipvs)
{
int genid = atomic_read(&ipvs->est_genid);
struct ip_vs_est_tick_data *td;
struct ip_vs_est_kt_data *kd;
struct ip_vs_estimator *est;
struct ip_vs_stats *stats;
int id, row, cid, delay;
bool last, last_td;
int chain_max;
int step;
if (!ip_vs_est_calc_limits(ipvs, &chain_max))
return;
mutex_lock(&__ip_vs_mutex);
/* Stop all other tasks, so that we can immediately move the
* estimators to est_temp_list without RCU grace period
*/
mutex_lock(&ipvs->est_mutex);
for (id = 1; id < ipvs->est_kt_count; id++) {
/* netns clean up started, abort */
if (!ipvs->enable)
goto unlock2;
kd = ipvs->est_kt_arr[id];
if (!kd)
continue;
ip_vs_est_kthread_stop(kd);
}
mutex_unlock(&ipvs->est_mutex);
/* Move all estimators to est_temp_list but carefully,
* all estimators and kthread data can be released while
* we reschedule. Even for kthread 0.
*/
step = 0;
/* Order entries in est_temp_list in ascending delay, so now
* walk delay(desc), id(desc), cid(asc)
*/
delay = IPVS_EST_NTICKS;
next_delay:
delay--;
if (delay < 0)
goto end_dequeue;
last_kt:
/* Destroy contexts backwards */
id = ipvs->est_kt_count;
next_kt:
if (!ipvs->enable || kthread_should_stop())
goto unlock;
id--;
if (id < 0)
goto next_delay;
kd = ipvs->est_kt_arr[id];
if (!kd)
goto next_kt;
/* kt 0 can exist with empty chains */
if (!id && kd->est_count <= 1)
goto next_delay;
row = kd->est_row + delay;
if (row >= IPVS_EST_NTICKS)
row -= IPVS_EST_NTICKS;
td = rcu_dereference_protected(kd->ticks[row], 1);
if (!td)
goto next_kt;
cid = 0;
walk_chain:
if (kthread_should_stop())
goto unlock;
step++;
if (!(step & 63)) {
/* Give chance estimators to be added (to est_temp_list)
* and deleted (releasing kthread contexts)
*/
mutex_unlock(&__ip_vs_mutex);
cond_resched();
mutex_lock(&__ip_vs_mutex);
/* Current kt released ? */
if (id >= ipvs->est_kt_count)
goto last_kt;
if (kd != ipvs->est_kt_arr[id])
goto next_kt;
/* Current td released ? */
if (td != rcu_dereference_protected(kd->ticks[row], 1))
goto next_kt;
/* No fatal changes on the current kd and td */
}
est = hlist_entry_safe(td->chains[cid].first, struct ip_vs_estimator,
list);
if (!est) {
cid++;
if (cid >= IPVS_EST_TICK_CHAINS)
goto next_kt;
goto walk_chain;
}
/* We can cheat and increase est_count to protect kt 0 context
* from release but we prefer to keep the last estimator
*/
last = kd->est_count <= 1;
/* Do not free kt #0 data */
if (!id && last)
goto next_delay;
last_td = kd->tick_len[row] <= 1;
stats = container_of(est, struct ip_vs_stats, est);
ip_vs_stop_estimator(ipvs, stats);
/* Tasks are stopped, move without RCU grace period */
est->ktid = -1;
est->ktrow = row - kd->est_row;
if (est->ktrow < 0)
est->ktrow += IPVS_EST_NTICKS;
hlist_add_head(&est->list, &ipvs->est_temp_list);
/* kd freed ? */
if (last)
goto next_kt;
/* td freed ? */
if (last_td)
goto next_kt;
goto walk_chain;
end_dequeue:
/* All estimators removed while calculating ? */
if (!ipvs->est_kt_count)
goto unlock;
kd = ipvs->est_kt_arr[0];
if (!kd)
goto unlock;
kd->add_row = kd->est_row;
ipvs->est_chain_max = chain_max;
ip_vs_est_set_params(ipvs, kd);
pr_info("using max %d ests per chain, %d per kthread\n",
kd->chain_max, kd->est_max_count);
/* Try to keep tot_stats in kt0, enqueue it early */
if (ipvs->tot_stats && !hlist_unhashed(&ipvs->tot_stats->s.est.list) &&
ipvs->tot_stats->s.est.ktid == -1) {
hlist_del(&ipvs->tot_stats->s.est.list);
hlist_add_head(&ipvs->tot_stats->s.est.list,
&ipvs->est_temp_list);
}
mutex_lock(&ipvs->est_mutex);
/* We completed the calc phase, new calc phase not requested */
if (genid == atomic_read(&ipvs->est_genid))
ipvs->est_calc_phase = 0;
unlock2:
mutex_unlock(&ipvs->est_mutex);
unlock:
mutex_unlock(&__ip_vs_mutex);
} }
void ip_vs_zero_estimator(struct ip_vs_stats *stats) void ip_vs_zero_estimator(struct ip_vs_stats *stats)
...@@ -191,14 +926,25 @@ void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats) ...@@ -191,14 +926,25 @@ void ip_vs_read_estimator(struct ip_vs_kstats *dst, struct ip_vs_stats *stats)
int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs) int __net_init ip_vs_estimator_net_init(struct netns_ipvs *ipvs)
{ {
INIT_LIST_HEAD(&ipvs->est_list); INIT_HLIST_HEAD(&ipvs->est_temp_list);
spin_lock_init(&ipvs->est_lock); ipvs->est_kt_arr = NULL;
timer_setup(&ipvs->est_timer, estimation_timer, 0); ipvs->est_max_threads = 0;
mod_timer(&ipvs->est_timer, jiffies + 2 * HZ); ipvs->est_calc_phase = 0;
ipvs->est_chain_max = 0;
ipvs->est_kt_count = 0;
ipvs->est_add_ktid = 0;
atomic_set(&ipvs->est_genid, 0);
atomic_set(&ipvs->est_genid_done, 0);
__mutex_init(&ipvs->est_mutex, "ipvs->est_mutex", &__ipvs_est_key);
return 0; return 0;
} }
void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs) void __net_exit ip_vs_estimator_net_cleanup(struct netns_ipvs *ipvs)
{ {
del_timer_sync(&ipvs->est_timer); int i;
for (i = 0; i < ipvs->est_kt_count; i++)
ip_vs_est_kthread_destroy(ipvs->est_kt_arr[i]);
kfree(ipvs->est_kt_arr);
mutex_destroy(&ipvs->est_mutex);
} }
...@@ -121,17 +121,61 @@ const struct nf_conntrack_l4proto *nf_ct_l4proto_find(u8 l4proto) ...@@ -121,17 +121,61 @@ const struct nf_conntrack_l4proto *nf_ct_l4proto_find(u8 l4proto)
}; };
EXPORT_SYMBOL_GPL(nf_ct_l4proto_find); EXPORT_SYMBOL_GPL(nf_ct_l4proto_find);
unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff, static bool in_vrf_postrouting(const struct nf_hook_state *state)
struct nf_conn *ct, enum ip_conntrack_info ctinfo) {
#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
if (state->hook == NF_INET_POST_ROUTING &&
netif_is_l3_master(state->out))
return true;
#endif
return false;
}
unsigned int nf_confirm(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{ {
const struct nf_conn_help *help; const struct nf_conn_help *help;
enum ip_conntrack_info ctinfo;
unsigned int protoff;
struct nf_conn *ct;
bool seqadj_needed;
__be16 frag_off;
u8 pnum;
ct = nf_ct_get(skb, &ctinfo);
if (!ct || in_vrf_postrouting(state))
return NF_ACCEPT;
help = nfct_help(ct); help = nfct_help(ct);
seqadj_needed = test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb);
if (!help && !seqadj_needed)
return nf_conntrack_confirm(skb);
/* helper->help() do not expect ICMP packets */
if (ctinfo == IP_CT_RELATED_REPLY)
return nf_conntrack_confirm(skb);
switch (nf_ct_l3num(ct)) {
case NFPROTO_IPV4:
protoff = skb_network_offset(skb) + ip_hdrlen(skb);
break;
case NFPROTO_IPV6:
pnum = ipv6_hdr(skb)->nexthdr;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, &frag_off);
if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
return nf_conntrack_confirm(skb);
break;
default:
return nf_conntrack_confirm(skb);
}
if (help) { if (help) {
const struct nf_conntrack_helper *helper; const struct nf_conntrack_helper *helper;
int ret; int ret;
/* rcu_read_lock()ed by nf_hook_thresh */ /* rcu_read_lock()ed by nf_hook */
helper = rcu_dereference(help->helper); helper = rcu_dereference(help->helper);
if (helper) { if (helper) {
ret = helper->help(skb, ret = helper->help(skb,
...@@ -142,48 +186,17 @@ unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff, ...@@ -142,48 +186,17 @@ unsigned int nf_confirm(struct sk_buff *skb, unsigned int protoff,
} }
} }
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && if (seqadj_needed &&
!nf_is_loopback_packet(skb)) { !nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) {
if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) {
NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
return NF_DROP; return NF_DROP;
} }
}
/* We've seen it coming out the other side: confirm it */ /* We've seen it coming out the other side: confirm it */
return nf_conntrack_confirm(skb); return nf_conntrack_confirm(skb);
} }
EXPORT_SYMBOL_GPL(nf_confirm); EXPORT_SYMBOL_GPL(nf_confirm);
static bool in_vrf_postrouting(const struct nf_hook_state *state)
{
#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
if (state->hook == NF_INET_POST_ROUTING &&
netif_is_l3_master(state->out))
return true;
#endif
return false;
}
static unsigned int ipv4_confirm(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
ct = nf_ct_get(skb, &ctinfo);
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
return nf_conntrack_confirm(skb);
if (in_vrf_postrouting(state))
return NF_ACCEPT;
return nf_confirm(skb,
skb_network_offset(skb) + ip_hdrlen(skb),
ct, ctinfo);
}
static unsigned int ipv4_conntrack_in(void *priv, static unsigned int ipv4_conntrack_in(void *priv,
struct sk_buff *skb, struct sk_buff *skb,
const struct nf_hook_state *state) const struct nf_hook_state *state)
...@@ -230,13 +243,13 @@ static const struct nf_hook_ops ipv4_conntrack_ops[] = { ...@@ -230,13 +243,13 @@ static const struct nf_hook_ops ipv4_conntrack_ops[] = {
.priority = NF_IP_PRI_CONNTRACK, .priority = NF_IP_PRI_CONNTRACK,
}, },
{ {
.hook = ipv4_confirm, .hook = nf_confirm,
.pf = NFPROTO_IPV4, .pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM, .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
}, },
{ {
.hook = ipv4_confirm, .hook = nf_confirm,
.pf = NFPROTO_IPV4, .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN, .hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM, .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
...@@ -373,33 +386,6 @@ static struct nf_sockopt_ops so_getorigdst6 = { ...@@ -373,33 +386,6 @@ static struct nf_sockopt_ops so_getorigdst6 = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static unsigned int ipv6_confirm(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
unsigned char pnum = ipv6_hdr(skb)->nexthdr;
__be16 frag_off;
int protoff;
ct = nf_ct_get(skb, &ctinfo);
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
return nf_conntrack_confirm(skb);
if (in_vrf_postrouting(state))
return NF_ACCEPT;
protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum,
&frag_off);
if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
pr_debug("proto header not found\n");
return nf_conntrack_confirm(skb);
}
return nf_confirm(skb, protoff, ct, ctinfo);
}
static unsigned int ipv6_conntrack_in(void *priv, static unsigned int ipv6_conntrack_in(void *priv,
struct sk_buff *skb, struct sk_buff *skb,
const struct nf_hook_state *state) const struct nf_hook_state *state)
...@@ -428,13 +414,13 @@ static const struct nf_hook_ops ipv6_conntrack_ops[] = { ...@@ -428,13 +414,13 @@ static const struct nf_hook_ops ipv6_conntrack_ops[] = {
.priority = NF_IP6_PRI_CONNTRACK, .priority = NF_IP6_PRI_CONNTRACK,
}, },
{ {
.hook = ipv6_confirm, .hook = nf_confirm,
.pf = NFPROTO_IPV6, .pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_LAST, .priority = NF_IP6_PRI_LAST,
}, },
{ {
.hook = ipv6_confirm, .hook = nf_confirm,
.pf = NFPROTO_IPV6, .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN, .hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_LAST - 1, .priority = NF_IP6_PRI_LAST - 1,
......
...@@ -129,6 +129,56 @@ static void icmpv6_error_log(const struct sk_buff *skb, ...@@ -129,6 +129,56 @@ static void icmpv6_error_log(const struct sk_buff *skb,
nf_l4proto_log_invalid(skb, state, IPPROTO_ICMPV6, "%s", msg); nf_l4proto_log_invalid(skb, state, IPPROTO_ICMPV6, "%s", msg);
} }
static noinline_for_stack int
nf_conntrack_icmpv6_redirect(struct nf_conn *tmpl, struct sk_buff *skb,
unsigned int dataoff,
const struct nf_hook_state *state)
{
u8 hl = ipv6_hdr(skb)->hop_limit;
union nf_inet_addr outer_daddr;
union {
struct nd_opt_hdr nd_opt;
struct rd_msg rd_msg;
} tmp;
const struct nd_opt_hdr *nd_opt;
const struct rd_msg *rd_msg;
rd_msg = skb_header_pointer(skb, dataoff, sizeof(*rd_msg), &tmp.rd_msg);
if (!rd_msg) {
icmpv6_error_log(skb, state, "short redirect");
return -NF_ACCEPT;
}
if (rd_msg->icmph.icmp6_code != 0)
return NF_ACCEPT;
if (hl != 255 || !(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
icmpv6_error_log(skb, state, "invalid saddr or hoplimit for redirect");
return -NF_ACCEPT;
}
dataoff += sizeof(*rd_msg);
/* warning: rd_msg no longer usable after this call */
nd_opt = skb_header_pointer(skb, dataoff, sizeof(*nd_opt), &tmp.nd_opt);
if (!nd_opt || nd_opt->nd_opt_len == 0) {
icmpv6_error_log(skb, state, "redirect without options");
return -NF_ACCEPT;
}
/* We could call ndisc_parse_options(), but it would need
* skb_linearize() and a bit more work.
*/
if (nd_opt->nd_opt_type != ND_OPT_REDIRECT_HDR)
return NF_ACCEPT;
memcpy(&outer_daddr.ip6, &ipv6_hdr(skb)->daddr,
sizeof(outer_daddr.ip6));
dataoff += 8;
return nf_conntrack_inet_error(tmpl, skb, dataoff, state,
IPPROTO_ICMPV6, &outer_daddr);
}
int nf_conntrack_icmpv6_error(struct nf_conn *tmpl, int nf_conntrack_icmpv6_error(struct nf_conn *tmpl,
struct sk_buff *skb, struct sk_buff *skb,
unsigned int dataoff, unsigned int dataoff,
...@@ -159,6 +209,9 @@ int nf_conntrack_icmpv6_error(struct nf_conn *tmpl, ...@@ -159,6 +209,9 @@ int nf_conntrack_icmpv6_error(struct nf_conn *tmpl,
return NF_ACCEPT; return NF_ACCEPT;
} }
if (icmp6h->icmp6_type == NDISC_REDIRECT)
return nf_conntrack_icmpv6_redirect(tmpl, skb, dataoff, state);
/* is not error message ? */ /* is not error message ? */
if (icmp6h->icmp6_type >= 128) if (icmp6h->icmp6_type >= 128)
return NF_ACCEPT; return NF_ACCEPT;
......
...@@ -60,6 +60,7 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = { ...@@ -60,6 +60,7 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
[SCTP_CONNTRACK_SHUTDOWN_ACK_SENT] = 3 SECS, [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT] = 3 SECS,
[SCTP_CONNTRACK_HEARTBEAT_SENT] = 30 SECS, [SCTP_CONNTRACK_HEARTBEAT_SENT] = 30 SECS,
[SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS, [SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS,
[SCTP_CONNTRACK_DATA_SENT] = 30 SECS,
}; };
#define SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1 #define SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1
...@@ -74,6 +75,7 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = { ...@@ -74,6 +75,7 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT #define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
#define sHS SCTP_CONNTRACK_HEARTBEAT_SENT #define sHS SCTP_CONNTRACK_HEARTBEAT_SENT
#define sHA SCTP_CONNTRACK_HEARTBEAT_ACKED #define sHA SCTP_CONNTRACK_HEARTBEAT_ACKED
#define sDS SCTP_CONNTRACK_DATA_SENT
#define sIV SCTP_CONNTRACK_MAX #define sIV SCTP_CONNTRACK_MAX
/* /*
...@@ -90,15 +92,16 @@ COOKIE WAIT - We have seen an INIT chunk in the original direction, or als ...@@ -90,15 +92,16 @@ COOKIE WAIT - We have seen an INIT chunk in the original direction, or als
COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction. COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction.
ESTABLISHED - We have seen a COOKIE_ACK in the reply direction. ESTABLISHED - We have seen a COOKIE_ACK in the reply direction.
SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction. SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction.
SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin. SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply direction.
SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
to that of the SHUTDOWN chunk. to that of the SHUTDOWN chunk.
CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of
the SHUTDOWN chunk. Connection is closed. the SHUTDOWN chunk. Connection is closed.
HEARTBEAT_SENT - We have seen a HEARTBEAT in a new flow. HEARTBEAT_SENT - We have seen a HEARTBEAT in a new flow.
HEARTBEAT_ACKED - We have seen a HEARTBEAT-ACK in the direction opposite to HEARTBEAT_ACKED - We have seen a HEARTBEAT-ACK/DATA/SACK in the direction
that of the HEARTBEAT chunk. Secondary connection is opposite to that of the HEARTBEAT/DATA chunk. Secondary connection
established. is established.
DATA_SENT - We have seen a DATA/SACK in a new flow.
*/ */
/* TODO /* TODO
...@@ -112,36 +115,38 @@ cookie echoed to closed. ...@@ -112,36 +115,38 @@ cookie echoed to closed.
*/ */
/* SCTP conntrack state transitions */ /* SCTP conntrack state transitions */
static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = { static const u8 sctp_conntracks[2][12][SCTP_CONNTRACK_MAX] = {
{ {
/* ORIGINAL */ /* ORIGINAL */
/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA */ /* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */
/* init */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW, sHA}, /* init */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW, sHA, sCW},
/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA}, /* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},
/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL}, /* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL, sSS}, /* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL, sSS, sCL},
/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA, sHA}, /* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA, sHA, sSA},
/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA},/* Can't have Stale cookie*/ /* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't have Stale cookie*/
/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL, sHA},/* 5.2.4 - Big TODO */ /* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* 5.2.4 - Big TODO */
/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA},/* Can't come in orig dir */ /* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't come in orig dir */
/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL, sHA}, /* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL, sHA, sCL},
/* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA}, /* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS},
/* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA} /* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS},
/* data/sack */ {sDS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS}
}, },
{ {
/* REPLY */ /* REPLY */
/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA */ /* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */
/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA},/* INIT in sCL Big TODO */ /* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* INIT in sCL Big TODO */
/* init_ack */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA}, /* init_ack */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},
/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV, sCL}, /* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV, sCL, sIV},
/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV, sSR}, /* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV, sSR, sIV},
/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV, sHA}, /* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV, sHA, sIV},
/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV, sHA}, /* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV, sHA, sIV},
/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA},/* Can't come in reply dir */ /* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* Can't come in reply dir */
/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV, sHA}, /* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV, sHA, sIV},
/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV, sHA}, /* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV, sHA, sIV},
/* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA}, /* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sHA},
/* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA} /* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA},
/* data/sack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA},
} }
}; };
...@@ -253,6 +258,11 @@ static int sctp_new_state(enum ip_conntrack_dir dir, ...@@ -253,6 +258,11 @@ static int sctp_new_state(enum ip_conntrack_dir dir,
pr_debug("SCTP_CID_HEARTBEAT_ACK"); pr_debug("SCTP_CID_HEARTBEAT_ACK");
i = 10; i = 10;
break; break;
case SCTP_CID_DATA:
case SCTP_CID_SACK:
pr_debug("SCTP_CID_DATA/SACK");
i = 11;
break;
default: default:
/* Other chunks like DATA or SACK do not change the state */ /* Other chunks like DATA or SACK do not change the state */
pr_debug("Unknown chunk type, Will stay in %s\n", pr_debug("Unknown chunk type, Will stay in %s\n",
...@@ -306,7 +316,9 @@ sctp_new(struct nf_conn *ct, const struct sk_buff *skb, ...@@ -306,7 +316,9 @@ sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
ih->init_tag); ih->init_tag);
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag; ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag;
} else if (sch->type == SCTP_CID_HEARTBEAT) { } else if (sch->type == SCTP_CID_HEARTBEAT ||
sch->type == SCTP_CID_DATA ||
sch->type == SCTP_CID_SACK) {
pr_debug("Setting vtag %x for secondary conntrack\n", pr_debug("Setting vtag %x for secondary conntrack\n",
sh->vtag); sh->vtag);
ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag; ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag;
...@@ -392,8 +404,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, ...@@ -392,8 +404,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
if (!sctp_new(ct, skb, sh, dataoff)) if (!sctp_new(ct, skb, sh, dataoff))
return -NF_ACCEPT; return -NF_ACCEPT;
} } else {
/* Check the verification tag (Sec 8.5) */ /* Check the verification tag (Sec 8.5) */
if (!test_bit(SCTP_CID_INIT, map) && if (!test_bit(SCTP_CID_INIT, map) &&
!test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) && !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
...@@ -406,6 +417,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, ...@@ -406,6 +417,7 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
pr_debug("Verification tag check failed\n"); pr_debug("Verification tag check failed\n");
goto out; goto out;
} }
}
old_state = new_state = SCTP_CONNTRACK_NONE; old_state = new_state = SCTP_CONNTRACK_NONE;
spin_lock_bh(&ct->lock); spin_lock_bh(&ct->lock);
...@@ -464,6 +476,11 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, ...@@ -464,6 +476,11 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
} else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) { } else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED; ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
} }
} else if (sch->type == SCTP_CID_DATA || sch->type == SCTP_CID_SACK) {
if (ct->proto.sctp.vtag[dir] == 0) {
pr_debug("Setting vtag %x for dir %d\n", sh->vtag, dir);
ct->proto.sctp.vtag[dir] = sh->vtag;
}
} }
old_state = ct->proto.sctp.state; old_state = ct->proto.sctp.state;
...@@ -684,6 +701,7 @@ sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = { ...@@ -684,6 +701,7 @@ sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = {
[CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = { .type = NLA_U32 }, [CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = { .type = NLA_U32 },
[CTA_TIMEOUT_SCTP_HEARTBEAT_SENT] = { .type = NLA_U32 }, [CTA_TIMEOUT_SCTP_HEARTBEAT_SENT] = { .type = NLA_U32 },
[CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED] = { .type = NLA_U32 }, [CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED] = { .type = NLA_U32 },
[CTA_TIMEOUT_SCTP_DATA_SENT] = { .type = NLA_U32 },
}; };
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
......
...@@ -602,6 +602,7 @@ enum nf_ct_sysctl_index { ...@@ -602,6 +602,7 @@ enum nf_ct_sysctl_index {
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT, NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT,
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT, NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT,
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED, NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED,
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT,
#endif #endif
#ifdef CONFIG_NF_CT_PROTO_DCCP #ifdef CONFIG_NF_CT_PROTO_DCCP
NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST, NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST,
...@@ -892,6 +893,12 @@ static struct ctl_table nf_ct_sysctl_table[] = { ...@@ -892,6 +893,12 @@ static struct ctl_table nf_ct_sysctl_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_jiffies, .proc_handler = proc_dointvec_jiffies,
}, },
[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT] = {
.procname = "nf_conntrack_sctp_timeout_data_sent",
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
#endif #endif
#ifdef CONFIG_NF_CT_PROTO_DCCP #ifdef CONFIG_NF_CT_PROTO_DCCP
[NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST] = { [NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST] = {
...@@ -1036,6 +1043,7 @@ static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net, ...@@ -1036,6 +1043,7 @@ static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net,
XASSIGN(SHUTDOWN_ACK_SENT, sn); XASSIGN(SHUTDOWN_ACK_SENT, sn);
XASSIGN(HEARTBEAT_SENT, sn); XASSIGN(HEARTBEAT_SENT, sn);
XASSIGN(HEARTBEAT_ACKED, sn); XASSIGN(HEARTBEAT_ACKED, sn);
XASSIGN(DATA_SENT, sn);
#undef XASSIGN #undef XASSIGN
#endif #endif
} }
......
...@@ -421,6 +421,10 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, ...@@ -421,6 +421,10 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
if (ret == NF_DROP) if (ret == NF_DROP)
flow_offload_teardown(flow); flow_offload_teardown(flow);
break; break;
default:
WARN_ON_ONCE(1);
ret = NF_DROP;
break;
} }
return ret; return ret;
...@@ -682,6 +686,10 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ...@@ -682,6 +686,10 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
if (ret == NF_DROP) if (ret == NF_DROP)
flow_offload_teardown(flow); flow_offload_teardown(flow);
break; break;
default:
WARN_ON_ONCE(1);
ret = NF_DROP;
break;
} }
return ret; return ret;
......
...@@ -2873,8 +2873,8 @@ int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, ...@@ -2873,8 +2873,8 @@ int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla,
return -EINVAL; return -EINVAL;
type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]); type = __nft_expr_type_get(ctx->family, tb[NFTA_EXPR_NAME]);
if (IS_ERR(type)) if (!type)
return PTR_ERR(type); return -ENOENT;
if (!type->inner_ops) if (!type->inner_ops)
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -35,6 +35,8 @@ cleanup() { ...@@ -35,6 +35,8 @@ cleanup() {
for i in 1 2;do ip netns del nsrouter$i;done for i in 1 2;do ip netns del nsrouter$i;done
} }
trap cleanup EXIT
ipv4() { ipv4() {
echo -n 192.168.$1.2 echo -n 192.168.$1.2
} }
...@@ -146,11 +148,17 @@ ip netns exec nsclient1 nft -f - <<EOF ...@@ -146,11 +148,17 @@ ip netns exec nsclient1 nft -f - <<EOF
table inet filter { table inet filter {
counter unknown { } counter unknown { }
counter related { } counter related { }
counter redir4 { }
counter redir6 { }
chain input { chain input {
type filter hook input priority 0; policy accept; type filter hook input priority 0; policy accept;
meta l4proto { icmp, icmpv6 } ct state established,untracked accept
icmp type "redirect" ct state "related" counter name "redir4" accept
icmpv6 type "nd-redirect" ct state "related" counter name "redir6" accept
meta l4proto { icmp, icmpv6 } ct state established,untracked accept
meta l4proto { icmp, icmpv6 } ct state "related" counter name "related" accept meta l4proto { icmp, icmpv6 } ct state "related" counter name "related" accept
counter name "unknown" drop counter name "unknown" drop
} }
} }
...@@ -279,5 +287,29 @@ else ...@@ -279,5 +287,29 @@ else
echo "ERROR: icmp error RELATED state test has failed" echo "ERROR: icmp error RELATED state test has failed"
fi fi
cleanup # add 'bad' route, expect icmp REDIRECT to be generated
ip netns exec nsclient1 ip route add 192.168.1.42 via 192.168.1.1
ip netns exec nsclient1 ip route add dead:1::42 via dead:1::1
ip netns exec "nsclient1" ping -q -c 2 192.168.1.42 > /dev/null
expect="packets 1 bytes 112"
check_counter nsclient1 "redir4" "$expect"
if [ $? -ne 0 ];then
ret=1
fi
ip netns exec "nsclient1" ping -c 1 dead:1::42 > /dev/null
expect="packets 1 bytes 192"
check_counter nsclient1 "redir6" "$expect"
if [ $? -ne 0 ];then
ret=1
fi
if [ $ret -eq 0 ];then
echo "PASS: icmp redirects had RELATED state"
else
echo "ERROR: icmp redirect RELATED state test has failed"
fi
exit $ret exit $ret
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