2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-05 16:55:42 +00:00

conntrack: Support zone limits.

Signed-off-by: Darrell Ball <dlu998@gmail.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Darrell Ball
2019-12-03 09:14:17 -08:00
committed by Ben Pfaff
parent 551c72854e
commit a7f33fdbfb
10 changed files with 253 additions and 26 deletions

View File

@@ -118,7 +118,7 @@ Q: Are all features available with all datapaths?
Connection tracking 4.3 2.5 2.6 YES
Conntrack Fragment Reass. 4.3 2.6 2.12 YES
Conntrack Timeout Policies 5.2 2.12 NO NO
Conntrack Zone Limit 4.18 2.10 NO YES
Conntrack Zone Limit 4.18 2.10 2.13 YES
Conntrack NAT 4.6 2.6 2.8 YES
Tunnel - LISP NO 2.11 NO NO
Tunnel - STT NO 2.4 NO YES

1
NEWS
View File

@@ -7,6 +7,7 @@ Post-v2.12.0
- Userspace datapath:
* Add option to enable, disable and query TCP sequence checking in
conntrack.
* Add support for conntrack zone limits.
- AF_XDP:
* New option 'use-need-wakeup' for netdev-afxdp to control enabling
of corresponding 'need_wakeup' flag in AF_XDP rings. Enabled by default

View File

@@ -105,6 +105,12 @@ struct conn {
long long expiration;
uint32_t mark;
int seq_skew;
/* Immutable data. */
int32_t admit_zone; /* The zone for managing zone limit counts. */
uint32_t zone_limit_seq; /* Used to disambiguate zone limit counts. */
/* Mutable data. */
bool seq_skew_dir; /* TCP sequence skew direction due to NATTing of FTP
* control messages; true if reply direction. */
bool cleaned; /* True if cleaned from expiry lists. */
@@ -155,6 +161,7 @@ struct conntrack {
struct ovs_mutex ct_lock; /* Protects 2 following fields. */
struct cmap conns OVS_GUARDED;
struct ovs_list exp_lists[N_CT_TM] OVS_GUARDED;
struct hmap zone_limits OVS_GUARDED;
uint32_t hash_basis; /* Salt for hashing a connection key. */
pthread_t clean_thread; /* Periodically cleans up connection tracker. */
struct latch clean_thread_exit; /* To destroy the 'clean_thread'. */
@@ -172,6 +179,7 @@ struct conntrack {
* control context. */
struct ipf *ipf; /* Fragmentation handling context. */
uint32_t zone_limit_seq; /* Used to disambiguate zone limit counts. */
atomic_bool tcp_seq_chk; /* Check TCP sequence numbers. */
};

View File

@@ -76,6 +76,11 @@ enum ct_alg_ctl_type {
CT_ALG_CTL_SIP,
};
struct zone_limit {
struct hmap_node node;
struct conntrack_zone_limit czl;
};
static bool conn_key_extract(struct conntrack *, struct dp_packet *,
ovs_be16 dl_type, struct conn_lookup_ctx *,
uint16_t zone);
@@ -305,6 +310,8 @@ conntrack_init(void)
for (unsigned i = 0; i < ARRAY_SIZE(ct->exp_lists); i++) {
ovs_list_init(&ct->exp_lists[i]);
}
hmap_init(&ct->zone_limits);
ct->zone_limit_seq = 0;
ovs_mutex_unlock(&ct->ct_lock);
ct->hash_basis = random_uint32();
@@ -318,6 +325,111 @@ conntrack_init(void)
return ct;
}
static uint32_t
zone_key_hash(int32_t zone, uint32_t basis)
{
size_t hash = hash_int((OVS_FORCE uint32_t) zone, basis);
return hash;
}
static struct zone_limit *
zone_limit_lookup(struct conntrack *ct, int32_t zone)
OVS_REQUIRES(ct->ct_lock)
{
uint32_t hash = zone_key_hash(zone, ct->hash_basis);
struct zone_limit *zl;
HMAP_FOR_EACH_IN_BUCKET (zl, node, hash, &ct->zone_limits) {
if (zl->czl.zone == zone) {
return zl;
}
}
return NULL;
}
static struct zone_limit *
zone_limit_lookup_or_default(struct conntrack *ct, int32_t zone)
OVS_REQUIRES(ct->ct_lock)
{
struct zone_limit *zl = zone_limit_lookup(ct, zone);
return zl ? zl : zone_limit_lookup(ct, DEFAULT_ZONE);
}
struct conntrack_zone_limit
zone_limit_get(struct conntrack *ct, int32_t zone)
{
ovs_mutex_lock(&ct->ct_lock);
struct conntrack_zone_limit czl = {DEFAULT_ZONE, 0, 0, 0};
struct zone_limit *zl = zone_limit_lookup_or_default(ct, zone);
if (zl) {
czl = zl->czl;
}
ovs_mutex_unlock(&ct->ct_lock);
return czl;
}
static int
zone_limit_create(struct conntrack *ct, int32_t zone, uint32_t limit)
OVS_REQUIRES(ct->ct_lock)
{
if (zone >= DEFAULT_ZONE && zone <= MAX_ZONE) {
struct zone_limit *zl = xzalloc(sizeof *zl);
zl->czl.limit = limit;
zl->czl.zone = zone;
zl->czl.zone_limit_seq = ct->zone_limit_seq++;
uint32_t hash = zone_key_hash(zone, ct->hash_basis);
hmap_insert(&ct->zone_limits, &zl->node, hash);
return 0;
} else {
return EINVAL;
}
}
int
zone_limit_update(struct conntrack *ct, int32_t zone, uint32_t limit)
{
int err = 0;
ovs_mutex_lock(&ct->ct_lock);
struct zone_limit *zl = zone_limit_lookup(ct, zone);
if (zl) {
zl->czl.limit = limit;
VLOG_INFO("Changed zone limit of %u for zone %d", limit, zone);
} else {
err = zone_limit_create(ct, zone, limit);
if (!err) {
VLOG_INFO("Created zone limit of %u for zone %d", limit, zone);
} else {
VLOG_WARN("Request to create zone limit for invalid zone %d",
zone);
}
}
ovs_mutex_unlock(&ct->ct_lock);
return err;
}
static void
zone_limit_clean(struct conntrack *ct, struct zone_limit *zl)
OVS_REQUIRES(ct->ct_lock)
{
hmap_remove(&ct->zone_limits, &zl->node);
free(zl);
}
int
zone_limit_delete(struct conntrack *ct, uint16_t zone)
{
ovs_mutex_lock(&ct->ct_lock);
struct zone_limit *zl = zone_limit_lookup(ct, zone);
if (zl) {
zone_limit_clean(ct, zl);
VLOG_INFO("Deleted zone limit for zone %d", zone);
} else {
VLOG_INFO("Attempted delete of non-existent zone limit: zone %d",
zone);
}
ovs_mutex_unlock(&ct->ct_lock);
return 0;
}
static void
conn_clean_cmn(struct conntrack *ct, struct conn *conn)
OVS_REQUIRES(ct->ct_lock)
@@ -328,6 +440,11 @@ conn_clean_cmn(struct conntrack *ct, struct conn *conn)
uint32_t hash = conn_key_hash(&conn->key, ct->hash_basis);
cmap_remove(&ct->conns, &conn->cm_node, hash);
struct zone_limit *zl = zone_limit_lookup(ct, conn->admit_zone);
if (zl && zl->czl.zone_limit_seq == conn->zone_limit_seq) {
zl->czl.count--;
}
}
/* Must be called with 'conn' of 'conn_type' CT_CONN_TYPE_DEFAULT. Also
@@ -378,6 +495,13 @@ conntrack_destroy(struct conntrack *ct)
conn_clean_one(ct, conn);
}
cmap_destroy(&ct->conns);
struct zone_limit *zl;
HMAP_FOR_EACH_POP (zl, node, &ct->zone_limits) {
free(zl);
}
hmap_destroy(&ct->zone_limits);
ovs_mutex_unlock(&ct->ct_lock);
ovs_mutex_destroy(&ct->ct_lock);
@@ -850,6 +974,12 @@ conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
}
if (commit) {
struct zone_limit *zl = zone_limit_lookup_or_default(ct,
ctx->key.zone);
if (zl && zl->czl.count >= zl->czl.limit) {
return nc;
}
unsigned int n_conn_limit;
atomic_read_relaxed(&ct->n_conn_limit, &n_conn_limit);
if (atomic_count_get(&ct->n_conn) >= n_conn_limit) {
@@ -915,6 +1045,13 @@ conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
cmap_insert(&ct->conns, &nc->cm_node, ctx->hash);
atomic_count_inc(&ct->n_conn);
ctx->conn = nc; /* For completeness. */
if (zl) {
nc->admit_zone = zl->czl.zone;
nc->zone_limit_seq = zl->czl.zone_limit_seq;
zl->czl.count++;
} else {
nc->admit_zone = INVALID_ZONE;
}
}
return nc;

View File

@@ -104,6 +104,20 @@ struct conntrack_dump {
uint16_t zone;
};
struct conntrack_zone_limit {
int32_t zone;
uint32_t limit;
uint32_t count;
uint32_t zone_limit_seq; /* Used to disambiguate zone limit counts. */
};
enum {
INVALID_ZONE = -2,
DEFAULT_ZONE = -1, /* Default zone for zone limit management. */
MIN_ZONE = 0,
MAX_ZONE = 0xFFFF,
};
struct ct_dpif_entry;
struct ct_dpif_tuple;
@@ -121,5 +135,9 @@ int conntrack_get_nconns(struct conntrack *ct, uint32_t *nconns);
int conntrack_set_tcp_seq_chk(struct conntrack *ct, bool enabled);
bool conntrack_get_tcp_seq_chk(struct conntrack *ct);
struct ipf *conntrack_ipf_ctx(struct conntrack *ct);
struct conntrack_zone_limit zone_limit_get(struct conntrack *ct,
int32_t zone);
int zone_limit_update(struct conntrack *ct, int32_t zone, uint32_t limit);
int zone_limit_delete(struct conntrack *ct, uint16_t zone);
#endif /* conntrack.h */

View File

@@ -346,18 +346,16 @@ particular zone is not specified in the datapath, it defaults to the
default per-zone limit. A default zone may be specified with the
\fBdefault=\fIdefault_limit\fR argument. Initially, the default
per-zone limit is unlimited. An unlimited number of entries may be set
with \fB0\fR limit. Only supported for Linux kernel datapath.
with \fB0\fR limit.
.
.TP
\*(DX\fBct\-del\-limits\fR [\fIdp\fR] \fBzone=\fIzone[,zone]\fR...
Deletes the connection tracking limit for \fIzone\fR. Multiple zones may
be specified with a comma-separated list. Only supported for Linux
kernel datapath.
be specified with a comma-separated list.
.
.TP
\*(DX\fBct\-get\-limits\fR [\fIdp\fR] [\fBzone=\fIzone\fR[\fB,\fIzone\fR]...]
Retrieves the maximum allowed number of connections and current
counts per-zone. If \fIzone\fR is given, only the specified zone(s) are
printed. If no zones are specified, all the zone limits and counts are
provided. The command always displays the default zone limit. Only
supported for Linux kernel datapath.
provided. The command always displays the default zone limit.

View File

@@ -7470,6 +7470,88 @@ dpif_netdev_ct_get_tcp_seq_chk(struct dpif *dpif, bool *enabled)
return 0;
}
static int
dpif_netdev_ct_set_limits(struct dpif *dpif OVS_UNUSED,
const uint32_t *default_limits,
const struct ovs_list *zone_limits)
{
int err = 0;
struct dp_netdev *dp = get_dp_netdev(dpif);
if (default_limits) {
err = zone_limit_update(dp->conntrack, DEFAULT_ZONE, *default_limits);
if (err != 0) {
return err;
}
}
struct ct_dpif_zone_limit *zone_limit;
LIST_FOR_EACH (zone_limit, node, zone_limits) {
err = zone_limit_update(dp->conntrack, zone_limit->zone,
zone_limit->limit);
if (err != 0) {
break;
}
}
return err;
}
static int
dpif_netdev_ct_get_limits(struct dpif *dpif OVS_UNUSED,
uint32_t *default_limit,
const struct ovs_list *zone_limits_request,
struct ovs_list *zone_limits_reply)
{
struct dp_netdev *dp = get_dp_netdev(dpif);
struct conntrack_zone_limit czl;
czl = zone_limit_get(dp->conntrack, DEFAULT_ZONE);
if (czl.zone == DEFAULT_ZONE) {
*default_limit = czl.limit;
} else {
return EINVAL;
}
if (!ovs_list_is_empty(zone_limits_request)) {
struct ct_dpif_zone_limit *zone_limit;
LIST_FOR_EACH (zone_limit, node, zone_limits_request) {
czl = zone_limit_get(dp->conntrack, zone_limit->zone);
if (czl.zone == zone_limit->zone || czl.zone == DEFAULT_ZONE) {
ct_dpif_push_zone_limit(zone_limits_reply, zone_limit->zone,
czl.limit, czl.count);
} else {
return EINVAL;
}
}
} else {
for (int z = MIN_ZONE; z <= MAX_ZONE; z++) {
czl = zone_limit_get(dp->conntrack, z);
if (czl.zone == z) {
ct_dpif_push_zone_limit(zone_limits_reply, z, czl.limit,
czl.count);
}
}
}
return 0;
}
static int
dpif_netdev_ct_del_limits(struct dpif *dpif OVS_UNUSED,
const struct ovs_list *zone_limits)
{
int err = 0;
struct dp_netdev *dp = get_dp_netdev(dpif);
struct ct_dpif_zone_limit *zone_limit;
LIST_FOR_EACH (zone_limit, node, zone_limits) {
err = zone_limit_delete(dp->conntrack, zone_limit->zone);
if (err != 0) {
break;
}
}
return err;
}
static int
dpif_netdev_ipf_set_enabled(struct dpif *dpif, bool v6, bool enable)
{
@@ -7576,9 +7658,9 @@ const struct dpif_class dpif_netdev_class = {
dpif_netdev_ct_get_nconns,
dpif_netdev_ct_set_tcp_seq_chk,
dpif_netdev_ct_get_tcp_seq_chk,
NULL, /* ct_set_limits */
NULL, /* ct_get_limits */
NULL, /* ct_del_limits */
dpif_netdev_ct_set_limits,
dpif_netdev_ct_get_limits,
dpif_netdev_ct_del_limits,
NULL, /* ct_set_timeout_policy */
NULL, /* ct_get_timeout_policy */
NULL, /* ct_del_timeout_policy */

View File

@@ -110,13 +110,6 @@ m4_define([CHECK_CONNTRACK_TIMEOUT],
on_exit 'modprobe -r nfnetlink_cttimeout'
])
# CHECK_CT_DPIF_PER_ZONE_LIMIT()
#
# Perform requirements checks for running ovs-dpctl ct-[set|get|del]-limits per
# zone. The kernel datapath does support this feature. Will remove this check
# after both kernel and userspace datapath support it.
m4_define([CHECK_CT_DPIF_PER_ZONE_LIMIT])
# CHECK_CT_DPIF_SET_GET_MAXCONNS()
#
# Perform requirements checks for running ovs-dpctl ct-set-maxconns or

View File

@@ -3574,7 +3574,6 @@ AT_CLEANUP
AT_SETUP([conntrack - limit by zone])
CHECK_CONNTRACK()
CHECK_CT_DPIF_PER_ZONE_LIMIT()
OVS_TRAFFIC_VSWITCHD_START()
ADD_NAMESPACES(at_ns0, at_ns1)

View File

@@ -106,15 +106,6 @@ m4_define([CHECK_CONNTRACK_TIMEOUT],
AT_SKIP_IF([:])
])
# CHECK_CT_DPIF_PER_ZONE_LIMIT()
#
# Perform requirements checks for running ovs-dpctl ct-[set|get|del]-limits per
# zone. The userspace datapath does not support this feature yet.
m4_define([CHECK_CT_DPIF_PER_ZONE_LIMIT],
[
AT_SKIP_IF([:])
])
# CHECK_CT_DPIF_SET_GET_MAXCONNS()
#
# Perform requirements checks for running ovs-dpctl ct-set-maxconns or