@@ -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 Timeout Policies 5.2 2.12 2.14 NO
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
@@ -10,6 +10,8 @@ Post-v2.13.0
* Deprecated DPDK ring ports (dpdkr) are no longer supported.
- Linux datapath:
* Support for kernel versions up to 5.5.x.
+ - Userspace datapath:
+ * Add support for conntrack zone-based timeout policy.
v2.13.0 - 14 Feb 2020
@@ -53,6 +53,8 @@ lib_libopenvswitch_la_SOURCES = \
lib/conntrack-icmp.c \
lib/conntrack-private.h \
lib/conntrack-tcp.c \
+ lib/conntrack-tp.c \
+ lib/conntrack-tp.h \
lib/conntrack-other.c \
lib/conntrack.c \
lib/conntrack.h \
@@ -22,6 +22,7 @@
#include <netinet/icmp6.h>
#include "conntrack-private.h"
+#include "conntrack-tp.h"
#include "dp-packet.h"
enum OVS_PACKED_ENUM icmp_state {
@@ -79,12 +80,13 @@ icmp6_valid_new(struct dp_packet *pkt)
static struct conn *
icmp_new_conn(struct conntrack *ct, struct dp_packet *pkt OVS_UNUSED,
- long long now)
+ long long now, uint32_t tp_id)
{
struct conn_icmp *conn = xzalloc(sizeof *conn);
conn->state = ICMPS_FIRST;
- conn_init_expiration(ct, &conn->up, icmp_timeouts[conn->state], now);
+ conn->up.tp_id = tp_id;
+ conn_init_expiration(ct, &conn->up, icmp_timeouts[conn->state], now);
return &conn->up;
}
@@ -17,6 +17,7 @@
#include <config.h>
#include "conntrack-private.h"
+#include "conntrack-tp.h"
#include "dp-packet.h"
enum OVS_PACKED_ENUM other_state {
@@ -69,12 +70,13 @@ other_valid_new(struct dp_packet *pkt OVS_UNUSED)
static struct conn *
other_new_conn(struct conntrack *ct, struct dp_packet *pkt OVS_UNUSED,
- long long now)
+ long long now, uint32_t tp_id)
{
struct conn_other *conn;
conn = xzalloc(sizeof *conn);
conn->state = OTHERS_FIRST;
+ conn->up.tp_id = tp_id;
conn_init_expiration(ct, &conn->up, other_timeouts[conn->state], now);
@@ -118,6 +118,8 @@ struct conn {
/* Immutable data. */
bool alg_related; /* True if alg data connection. */
enum ct_conn_type conn_type;
+
+ uint32_t tp_id; /* Timeout policy ID. */
};
enum ct_update_res {
@@ -131,28 +133,20 @@ enum ct_update_res {
* are listed here. The name will be prefix by CT_TM_ and the value is in
* milliseconds */
#define CT_TIMEOUTS \
- CT_TIMEOUT(TCP_FIRST_PACKET, 30 * 1000) \
- CT_TIMEOUT(TCP_OPENING, 30 * 1000) \
- CT_TIMEOUT(TCP_ESTABLISHED, 24 * 60 * 60 * 1000) \
- CT_TIMEOUT(TCP_CLOSING, 15 * 60 * 1000) \
- CT_TIMEOUT(TCP_FIN_WAIT, 45 * 1000) \
- CT_TIMEOUT(TCP_CLOSED, 30 * 1000) \
- CT_TIMEOUT(OTHER_FIRST, 60 * 1000) \
- CT_TIMEOUT(OTHER_MULTIPLE, 60 * 1000) \
- CT_TIMEOUT(OTHER_BIDIR, 30 * 1000) \
- CT_TIMEOUT(ICMP_FIRST, 60 * 1000) \
- CT_TIMEOUT(ICMP_REPLY, 30 * 1000)
-
-/* The smallest of the above values: it is used as an upper bound for the
- * interval between two rounds of cleanup of expired entries */
-#define CT_TM_MIN (30 * 1000)
-
-#define CT_TIMEOUT(NAME, VAL) BUILD_ASSERT_DECL(VAL >= CT_TM_MIN);
- CT_TIMEOUTS
-#undef CT_TIMEOUT
+ CT_TIMEOUT(TCP_FIRST_PACKET) \
+ CT_TIMEOUT(TCP_OPENING) \
+ CT_TIMEOUT(TCP_ESTABLISHED) \
+ CT_TIMEOUT(TCP_CLOSING) \
+ CT_TIMEOUT(TCP_FIN_WAIT) \
+ CT_TIMEOUT(TCP_CLOSED) \
+ CT_TIMEOUT(OTHER_FIRST) \
+ CT_TIMEOUT(OTHER_MULTIPLE) \
+ CT_TIMEOUT(OTHER_BIDIR) \
+ CT_TIMEOUT(ICMP_FIRST) \
+ CT_TIMEOUT(ICMP_REPLY)
enum ct_timeout {
-#define CT_TIMEOUT(NAME, VALUE) CT_TM_##NAME,
+#define CT_TIMEOUT(NAME) CT_TM_##NAME,
CT_TIMEOUTS
#undef CT_TIMEOUT
N_CT_TM
@@ -163,6 +157,7 @@ struct conntrack {
struct cmap conns OVS_GUARDED;
struct ovs_list exp_lists[N_CT_TM] OVS_GUARDED;
struct hmap zone_limits OVS_GUARDED;
+ struct hmap timeout_policies 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'. */
@@ -197,7 +192,7 @@ extern struct ct_l4_proto ct_proto_icmp6;
struct ct_l4_proto {
struct conn *(*new_conn)(struct conntrack *ct, struct dp_packet *pkt,
- long long now);
+ long long now, uint32_t tp_id);
bool (*valid_new)(struct dp_packet *pkt);
enum ct_update_res (*conn_update)(struct conntrack *ct, struct conn *conn,
struct dp_packet *pkt, bool reply,
@@ -206,39 +201,6 @@ struct ct_l4_proto {
struct ct_dpif_protoinfo *);
};
-extern long long ct_timeout_val[];
-
-
-/* ct_lock must be held. */
-static inline void
-conn_init_expiration(struct conntrack *ct, struct conn *conn,
- enum ct_timeout tm, long long now)
-{
- conn->expiration = now + ct_timeout_val[tm];
- ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node);
-}
-
-/* The conn entry lock must be held on entry and exit. */
-static inline void
-conn_update_expiration(struct conntrack *ct, struct conn *conn,
- enum ct_timeout tm, long long now)
- OVS_NO_THREAD_SAFETY_ANALYSIS
-{
- ovs_mutex_unlock(&conn->lock);
-
- ovs_mutex_lock(&ct->ct_lock);
- ovs_mutex_lock(&conn->lock);
- if (!conn->cleaned) {
- conn->expiration = now + ct_timeout_val[tm];
- ovs_list_remove(&conn->exp_node);
- ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node);
- }
- ovs_mutex_unlock(&conn->lock);
- ovs_mutex_unlock(&ct->ct_lock);
-
- ovs_mutex_lock(&conn->lock);
-}
-
static inline uint32_t
tcp_payload_length(struct dp_packet *pkt)
{
@@ -39,6 +39,7 @@
#include <config.h>
#include "conntrack-private.h"
+#include "conntrack-tp.h"
#include "coverage.h"
#include "ct-dpif.h"
#include "dp-packet.h"
@@ -435,7 +436,8 @@ tcp_valid_new(struct dp_packet *pkt)
}
static struct conn *
-tcp_new_conn(struct conntrack *ct, struct dp_packet *pkt, long long now)
+tcp_new_conn(struct conntrack *ct, struct dp_packet *pkt, long long now,
+ uint32_t tp_id)
{
struct conn_tcp* newconn = NULL;
struct tcp_header *tcp = dp_packet_l4(pkt);
@@ -471,6 +473,7 @@ tcp_new_conn(struct conntrack *ct, struct dp_packet *pkt, long long now)
src->state = CT_DPIF_TCPS_SYN_SENT;
dst->state = CT_DPIF_TCPS_CLOSED;
+ newconn->up.tp_id = tp_id;
conn_init_expiration(ct, &newconn->up, CT_TM_TCP_FIRST_PACKET, now);
return &newconn->up;
new file mode 100644
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2020 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "conntrack-private.h"
+#include "conntrack-tp.h"
+#include "ct-dpif.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(conntrack_tp);
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+static const char *ct_timeout_str[] = {
+#define CT_TIMEOUT(NAME) #NAME,
+ CT_TIMEOUTS
+#undef CT_TIMEOUT
+};
+
+/* Default timeout policy in seconds. */
+static unsigned int ct_dpif_netdev_tp_def[] = {
+ [CT_DPIF_TP_ATTR_TCP_SYN_SENT] = 30,
+ [CT_DPIF_TP_ATTR_TCP_SYN_RECV] = 30,
+ [CT_DPIF_TP_ATTR_TCP_ESTABLISHED] = 24 * 60 * 60,
+ [CT_DPIF_TP_ATTR_TCP_FIN_WAIT] = 15 * 60,
+ [CT_DPIF_TP_ATTR_TCP_TIME_WAIT] = 45,
+ [CT_DPIF_TP_ATTR_TCP_CLOSE] = 30,
+ [CT_DPIF_TP_ATTR_UDP_FIRST] = 60,
+ [CT_DPIF_TP_ATTR_UDP_SINGLE] = 60,
+ [CT_DPIF_TP_ATTR_UDP_MULTIPLE] = 30,
+ [CT_DPIF_TP_ATTR_ICMP_FIRST] = 60,
+ [CT_DPIF_TP_ATTR_ICMP_REPLY] = 30,
+};
+
+static struct timeout_policy *
+timeout_policy_lookup(struct conntrack *ct, int32_t tp_id)
+ OVS_REQUIRES(ct->ct_lock)
+{
+ struct timeout_policy *tp;
+ uint32_t hash;
+
+ hash = hash_int(tp_id, ct->hash_basis);
+ HMAP_FOR_EACH_IN_BUCKET (tp, node, hash, &ct->timeout_policies) {
+ if (tp->policy.id == tp_id) {
+ return tp;
+ }
+ }
+ return NULL;
+}
+
+struct timeout_policy *
+timeout_policy_get(struct conntrack *ct, int32_t tp_id)
+{
+ struct timeout_policy *tp;
+
+ ovs_mutex_lock(&ct->ct_lock);
+ tp = timeout_policy_lookup(ct, tp_id);
+ if (!tp) {
+ ovs_mutex_unlock(&ct->ct_lock);
+ return NULL;
+ }
+
+ ovs_mutex_unlock(&ct->ct_lock);
+ return tp;
+}
+
+static void
+update_existing_tp(struct timeout_policy *tp_dst,
+ const struct timeout_policy *tp_src)
+{
+ struct ct_dpif_timeout_policy *dst;
+ const struct ct_dpif_timeout_policy *src;
+ int i;
+
+ dst = &tp_dst->policy;
+ src = &tp_src->policy;
+
+ /* Set the value and present bit to dst if present
+ * bit in src is set.
+ */
+ for (i = 0; i < ARRAY_SIZE(dst->attrs); i++) {
+ if (src->present & (1 << i)) {
+ dst->attrs[i] = src->attrs[i];
+ dst->present |= (1 << i);
+ }
+ }
+}
+
+static void
+init_default_tp(struct timeout_policy *tp, uint32_t tp_id)
+{
+ tp->policy.id = tp_id;
+ /* Initialize the timeout value to default, but not
+ * setting the present bit.
+ */
+ tp->policy.present = 0;
+ memcpy(tp->policy.attrs, ct_dpif_netdev_tp_def,
+ sizeof tp->policy.attrs);
+}
+
+static void
+timeout_policy_create(struct conntrack *ct,
+ struct timeout_policy *new_tp)
+ OVS_REQUIRES(ct->ct_lock)
+{
+ uint32_t tp_id = new_tp->policy.id;
+ struct timeout_policy *tp;
+ uint32_t hash;
+
+ tp = xzalloc(sizeof *tp);
+ init_default_tp(tp, tp_id);
+ update_existing_tp(tp, new_tp);
+ hash = hash_int(tp_id, ct->hash_basis);
+ hmap_insert(&ct->timeout_policies, &tp->node, hash);
+}
+
+static void
+timeout_policy_clean(struct conntrack *ct, struct timeout_policy *tp)
+ OVS_REQUIRES(ct->ct_lock)
+{
+ hmap_remove(&ct->timeout_policies, &tp->node);
+ free(tp);
+}
+
+static void
+timeout_policy_delete__(struct conntrack *ct, uint32_t tp_id)
+ OVS_REQUIRES(ct->ct_lock)
+{
+ struct timeout_policy *tp = timeout_policy_lookup(ct, tp_id);
+ if (tp) {
+ timeout_policy_clean(ct, tp);
+ } else {
+ VLOG_WARN_RL(&rl, "Failed to delete a non-existent timeout "
+ "policy: id=%d", tp_id);
+ }
+}
+
+int
+timeout_policy_delete(struct conntrack *ct, uint32_t tp_id)
+{
+ ovs_mutex_lock(&ct->ct_lock);
+ timeout_policy_delete__(ct, tp_id);
+ ovs_mutex_unlock(&ct->ct_lock);
+ return 0;
+}
+
+void
+timeout_policy_init(struct conntrack *ct)
+ OVS_REQUIRES(ct->ct_lock)
+{
+ struct timeout_policy tp;
+
+ hmap_init(&ct->timeout_policies);
+
+ /* Create default timeout policy. */
+ memset(&tp, 0, sizeof tp);
+ tp.policy.id = DEFAULT_TP_ID;
+ timeout_policy_create(ct, &tp);
+}
+
+int
+timeout_policy_update(struct conntrack *ct,
+ struct timeout_policy *new_tp)
+{
+ int err = 0;
+ uint32_t tp_id = new_tp->policy.id;
+
+ ovs_mutex_lock(&ct->ct_lock);
+ struct timeout_policy *tp = timeout_policy_lookup(ct, tp_id);
+ if (tp) {
+ timeout_policy_delete__(ct, tp_id);
+ }
+ timeout_policy_create(ct, new_tp);
+ ovs_mutex_unlock(&ct->ct_lock);
+ return err;
+}
+
+static enum ct_dpif_tp_attr
+tm_to_ct_dpif_tp(enum ct_timeout tm)
+{
+ switch (tm) {
+ case CT_TM_TCP_FIRST_PACKET:
+ return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
+ case CT_TM_TCP_OPENING:
+ return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
+ case CT_TM_TCP_ESTABLISHED:
+ return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
+ case CT_TM_TCP_CLOSING:
+ return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
+ case CT_TM_TCP_FIN_WAIT:
+ return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
+ case CT_TM_TCP_CLOSED:
+ return CT_DPIF_TP_ATTR_TCP_CLOSE;
+ case CT_TM_OTHER_FIRST:
+ return CT_DPIF_TP_ATTR_UDP_FIRST;
+ case CT_TM_OTHER_BIDIR:
+ return CT_DPIF_TP_ATTR_UDP_SINGLE;
+ case CT_TM_OTHER_MULTIPLE:
+ return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
+ case CT_TM_ICMP_FIRST:
+ return CT_DPIF_TP_ATTR_ICMP_FIRST;
+ case CT_TM_ICMP_REPLY:
+ return CT_DPIF_TP_ATTR_ICMP_REPLY;
+ case N_CT_TM:
+ default:
+ OVS_NOT_REACHED();
+ break;
+ }
+ OVS_NOT_REACHED();
+ return CT_DPIF_TP_ATTR_MAX;
+}
+
+static void
+conn_update_expiration__(struct conntrack *ct, struct conn *conn,
+ enum ct_timeout tm, long long now,
+ uint32_t tp_value)
+ OVS_REQUIRES(conn->lock)
+{
+ ovs_mutex_unlock(&conn->lock);
+
+ ovs_mutex_lock(&ct->ct_lock);
+ ovs_mutex_lock(&conn->lock);
+ if (!conn->cleaned) {
+ conn->expiration = now + tp_value * 1000;
+ ovs_list_remove(&conn->exp_node);
+ ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node);
+ }
+ ovs_mutex_unlock(&conn->lock);
+ ovs_mutex_unlock(&ct->ct_lock);
+
+ ovs_mutex_lock(&conn->lock);
+}
+
+/* The conn entry lock must be held on entry and exit. */
+void
+conn_update_expiration(struct conntrack *ct, struct conn *conn,
+ enum ct_timeout tm, long long now)
+ OVS_REQUIRES(conn->lock)
+{
+ struct timeout_policy *tp;
+ uint32_t val;
+
+ ovs_mutex_lock(&ct->ct_lock);
+ tp = timeout_policy_lookup(ct, conn->tp_id);
+ if (tp) {
+ val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)];
+ } else {
+ val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)];
+ }
+ ovs_mutex_unlock(&ct->ct_lock);
+
+ VLOG_DBG_RL(&rl, "Update timeout %s zone=%u with policy id=%d "
+ "val=%u sec.",
+ ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
+
+ conn_update_expiration__(ct, conn, tm, now, val);
+}
+
+static void
+conn_init_expiration__(struct conntrack *ct, struct conn *conn,
+ enum ct_timeout tm, long long now,
+ uint32_t tp_value)
+{
+ conn->expiration = now + tp_value * 1000;
+ ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node);
+}
+
+/* ct_lock must be held. */
+void
+conn_init_expiration(struct conntrack *ct, struct conn *conn,
+ enum ct_timeout tm, long long now)
+ OVS_REQUIRES(ct->ct_lock)
+{
+ struct timeout_policy *tp;
+ uint32_t val;
+
+ tp = timeout_policy_lookup(ct, conn->tp_id);
+ if (tp) {
+ val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)];
+ } else {
+ val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)];
+ }
+
+ VLOG_DBG_RL(&rl, "Init timeout %s zone=%u with policy id=%d val=%u sec.",
+ ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
+
+ conn_init_expiration__(ct, conn, tm, now, val);
+}
new file mode 100644
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CONNTRACK_TP_H
+#define CONNTRACK_TP_H 1
+
+#define CT_DPIF_NETDEV_TP_MIN 30
+enum ct_timeout;
+void timeout_policy_init(struct conntrack *ct);
+int timeout_policy_update(struct conntrack *ct, struct timeout_policy *tp);
+int timeout_policy_delete(struct conntrack *ct, uint32_t tp_id);
+struct timeout_policy *timeout_policy_get(struct conntrack *ct, int32_t tp_id);
+void conn_init_expiration(struct conntrack *ct, struct conn *conn,
+ enum ct_timeout tm, long long now);
+void conn_update_expiration(struct conntrack *ct, struct conn *conn,
+ enum ct_timeout tm, long long now);
+#endif
@@ -25,6 +25,7 @@
#include "bitmap.h"
#include "conntrack.h"
#include "conntrack-private.h"
+#include "conntrack-tp.h"
#include "coverage.h"
#include "csum.h"
#include "ct-dpif.h"
@@ -89,7 +90,8 @@ static uint32_t conn_key_hash(const struct conn_key *, uint32_t basis);
static void conn_key_reverse(struct conn_key *);
static bool valid_new(struct dp_packet *pkt, struct conn_key *);
static struct conn *new_conn(struct conntrack *ct, struct dp_packet *pkt,
- struct conn_key *, long long now);
+ struct conn_key *, long long now,
+ uint32_t tp_id);
static void delete_conn_cmn(struct conn *);
static void delete_conn(struct conn *);
static void delete_conn_one(struct conn *conn);
@@ -176,12 +178,6 @@ static alg_helper alg_helpers[] = {
[CT_ALG_CTL_TFTP] = handle_tftp_ctl,
};
-long long ct_timeout_val[] = {
-#define CT_TIMEOUT(NAME, VAL) [CT_TM_##NAME] = VAL,
- CT_TIMEOUTS
-#undef CT_TIMEOUT
-};
-
/* The maximum TCP or UDP port number. */
#define CT_MAX_L4_PORT 65535
/* String buffer used for parsing FTP string messages.
@@ -313,6 +309,7 @@ conntrack_init(void)
}
hmap_init(&ct->zone_limits);
ct->zone_limit_seq = 0;
+ timeout_policy_init(ct);
ovs_mutex_unlock(&ct->ct_lock);
ct->hash_basis = random_uint32();
@@ -503,6 +500,12 @@ conntrack_destroy(struct conntrack *ct)
}
hmap_destroy(&ct->zone_limits);
+ struct timeout_policy *tp;
+ HMAP_FOR_EACH_POP (tp, node, &ct->timeout_policies) {
+ free(tp);
+ }
+ hmap_destroy(&ct->timeout_policies);
+
ovs_mutex_unlock(&ct->ct_lock);
ovs_mutex_destroy(&ct->ct_lock);
@@ -957,7 +960,7 @@ conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
struct conn_lookup_ctx *ctx, bool commit, long long now,
const struct nat_action_info_t *nat_action_info,
const char *helper, const struct alg_exp_node *alg_exp,
- enum ct_alg_ctl_type ct_alg_ctl)
+ enum ct_alg_ctl_type ct_alg_ctl, uint32_t tp_id)
OVS_REQUIRES(ct->ct_lock)
{
struct conn *nc = NULL;
@@ -988,7 +991,7 @@ conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
return nc;
}
- nc = new_conn(ct, pkt, &ctx->key, now);
+ nc = new_conn(ct, pkt, &ctx->key, now, tp_id);
memcpy(&nc->key, &ctx->key, sizeof nc->key);
memcpy(&nc->rev_key, &nc->key, sizeof nc->rev_key);
conn_key_reverse(&nc->rev_key);
@@ -1276,7 +1279,8 @@ process_one(struct conntrack *ct, struct dp_packet *pkt,
bool force, bool commit, long long now, const uint32_t *setmark,
const struct ovs_key_ct_labels *setlabel,
const struct nat_action_info_t *nat_action_info,
- ovs_be16 tp_src, ovs_be16 tp_dst, const char *helper)
+ ovs_be16 tp_src, ovs_be16 tp_dst, const char *helper,
+ uint32_t tp_id)
{
/* Reset ct_state whenever entering a new zone. */
if (pkt->md.ct_state && pkt->md.ct_zone != zone) {
@@ -1360,7 +1364,7 @@ process_one(struct conntrack *ct, struct dp_packet *pkt,
ovs_mutex_lock(&ct->ct_lock);
if (!conn_lookup(ct, &ctx->key, now, NULL, NULL)) {
conn = conn_not_found(ct, pkt, ctx, commit, now, nat_action_info,
- helper, alg_exp, ct_alg_ctl);
+ helper, alg_exp, ct_alg_ctl, tp_id);
}
ovs_mutex_unlock(&ct->ct_lock);
}
@@ -1396,7 +1400,7 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
const struct ovs_key_ct_labels *setlabel,
ovs_be16 tp_src, ovs_be16 tp_dst, const char *helper,
const struct nat_action_info_t *nat_action_info,
- long long now)
+ long long now, uint32_t tp_id)
{
ipf_preprocess_conntrack(ct->ipf, pkt_batch, now, dl_type, zone,
ct->hash_basis);
@@ -1418,7 +1422,8 @@ conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
write_ct_md(packet, zone, NULL, NULL, NULL);
} else {
process_one(ct, packet, &ctx, zone, force, commit, now, setmark,
- setlabel, nat_action_info, tp_src, tp_dst, helper);
+ setlabel, nat_action_info, tp_src, tp_dst, helper,
+ tp_id);
}
}
@@ -1524,7 +1529,7 @@ conntrack_clean(struct conntrack *ct, long long now)
atomic_read_relaxed(&ct->n_conn_limit, &n_conn_limit);
size_t clean_max = n_conn_limit > 10 ? n_conn_limit / 10 : 1;
long long min_exp = ct_sweep(ct, now, clean_max);
- long long next_wakeup = MIN(min_exp, now + CT_TM_MIN);
+ long long next_wakeup = MIN(min_exp, now + CT_DPIF_NETDEV_TP_MIN);
return next_wakeup;
}
@@ -2354,9 +2359,9 @@ valid_new(struct dp_packet *pkt, struct conn_key *key)
static struct conn *
new_conn(struct conntrack *ct, struct dp_packet *pkt, struct conn_key *key,
- long long now)
+ long long now, uint32_t tp_id)
{
- return l4_protos[key->nw_proto]->new_conn(ct, pkt, now);
+ return l4_protos[key->nw_proto]->new_conn(ct, pkt, now, tp_id);
}
static void
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include "cmap.h"
+#include "ct-dpif.h"
#include "latch.h"
#include "odp-netlink.h"
#include "openvswitch/hmap.h"
@@ -93,7 +94,7 @@ int conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
const struct ovs_key_ct_labels *setlabel,
ovs_be16 tp_src, ovs_be16 tp_dst, const char *helper,
const struct nat_action_info_t *nat_action_info,
- long long now);
+ long long now, uint32_t tp_id);
void conntrack_clear(struct dp_packet *packet);
struct conntrack_dump {
@@ -111,6 +112,11 @@ struct conntrack_zone_limit {
uint32_t zone_limit_seq; /* Used to disambiguate zone limit counts. */
};
+struct timeout_policy {
+ struct hmap_node node;
+ struct ct_dpif_timeout_policy policy;
+};
+
enum {
INVALID_ZONE = -2,
DEFAULT_ZONE = -1, /* Default zone for zone limit management. */
@@ -59,6 +59,8 @@ struct ct_dpif_timestamp {
uint64_t stop;
};
+#define DEFAULT_TP_ID 0
+
#define CT_DPIF_TCP_STATES \
CT_DPIF_TCP_STATE(CLOSED) \
CT_DPIF_TCP_STATE(LISTEN) \
@@ -36,6 +36,7 @@
#include "bitmap.h"
#include "cmap.h"
#include "conntrack.h"
+#include "conntrack-tp.h"
#include "coverage.h"
#include "ct-dpif.h"
#include "csum.h"
@@ -7342,6 +7343,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
bool commit = false;
unsigned int left;
uint16_t zone = 0;
+ uint32_t tp_id = 0;
const char *helper = NULL;
const uint32_t *setmark = NULL;
const struct ovs_key_ct_labels *setlabel = NULL;
@@ -7377,8 +7379,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
* netlink events. */
break;
case OVS_CT_ATTR_TIMEOUT:
- /* Userspace datapath does not support customized timeout
- * policy yet. */
+ if (!str_to_uint(nl_attr_get_string(b), 10, &tp_id)) {
+ VLOG_WARN("Invalid Timeout Policy ID: %s.",
+ nl_attr_get_string(b));
+ tp_id = DEFAULT_TP_ID;
+ }
break;
case OVS_CT_ATTR_NAT: {
const struct nlattr *b_nest;
@@ -7464,7 +7469,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
conntrack_execute(dp->conntrack, packets_, aux->flow->dl_type, force,
commit, zone, setmark, setlabel, aux->flow->tp_src,
aux->flow->tp_dst, helper, nat_action_info_ref,
- pmd->ctx.now / 1000);
+ pmd->ctx.now / 1000, tp_id);
break;
}
@@ -7698,6 +7703,62 @@ dpif_netdev_ct_del_limits(struct dpif *dpif OVS_UNUSED,
}
static int
+dpif_netdev_ct_set_timeout_policy(struct dpif *dpif,
+ const struct ct_dpif_timeout_policy *dpif_tp)
+{
+ struct timeout_policy tp;
+ struct dp_netdev *dp;
+
+ dp = get_dp_netdev(dpif);
+ memcpy(&tp.policy, dpif_tp, sizeof tp.policy);
+ return timeout_policy_update(dp->conntrack, &tp);
+}
+
+static int
+dpif_netdev_ct_get_timeout_policy(struct dpif *dpif, uint32_t tp_id,
+ struct ct_dpif_timeout_policy *dpif_tp)
+{
+ struct timeout_policy *tp;
+ struct dp_netdev *dp;
+ int err = 0;
+
+ dp = get_dp_netdev(dpif);
+ tp = timeout_policy_get(dp->conntrack, tp_id);
+ if (!tp) {
+ return EINVAL;
+ }
+ memcpy(dpif_tp, &tp->policy, sizeof tp->policy);
+ return err;
+}
+
+static int
+dpif_netdev_ct_del_timeout_policy(struct dpif *dpif,
+ uint32_t tp_id)
+{
+ struct dp_netdev *dp;
+ int err = 0;
+
+ dp = get_dp_netdev(dpif);
+ err = timeout_policy_delete(dp->conntrack, tp_id);
+ return err;
+}
+
+static int
+dpif_netdev_ct_get_timeout_policy_name(struct dpif *dpif OVS_UNUSED,
+ uint32_t tp_id,
+ uint16_t dl_type OVS_UNUSED,
+ uint8_t nw_proto OVS_UNUSED,
+ char **tp_name, bool *is_generic)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+
+ ds_put_format(&ds, "%"PRIu32, tp_id);
+ *tp_name = ds_steal_cstr(&ds);
+ *is_generic = true;
+ return 0;
+}
+
+static int
dpif_netdev_ipf_set_enabled(struct dpif *dpif, bool v6, bool enable)
{
struct dp_netdev *dp = get_dp_netdev(dpif);
@@ -7807,13 +7868,13 @@ const struct dpif_class dpif_netdev_class = {
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 */
+ dpif_netdev_ct_set_timeout_policy,
+ dpif_netdev_ct_get_timeout_policy,
+ dpif_netdev_ct_del_timeout_policy,
NULL, /* ct_timeout_policy_dump_start */
NULL, /* ct_timeout_policy_dump_next */
NULL, /* ct_timeout_policy_dump_done */
- NULL, /* ct_get_timeout_policy_name */
+ dpif_netdev_ct_get_timeout_policy_name,
dpif_netdev_ipf_set_enabled,
dpif_netdev_ipf_set_min_frag,
dpif_netdev_ipf_set_max_nfrags,
@@ -5426,7 +5426,8 @@ clear_existing_ct_timeout_policies(struct dpif_backer *backer)
static void
ct_zone_config_init(struct dpif_backer *backer)
{
- backer->tp_ids = id_pool_create(0, MAX_TIMEOUT_POLICY_ID);
+ backer->tp_ids = id_pool_create(DEFAULT_TP_ID + 1,
+ MAX_TIMEOUT_POLICY_ID - 1);
cmap_init(&backer->ct_zones);
hmap_init(&backer->ct_tps);
ovs_list_init(&backer->ct_tp_kill_list);
@@ -3311,8 +3311,15 @@ udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
AT_CHECK([ovs-appctl dpctl/flush-conntrack])
dnl Shorten the udp_single and icmp_first timeout in zone 5
+dnl Userspace datapath uses udp_first and icmp_reply, and
+dnl kernel datapath uses udp_single and icmp_first
VSCTL_ADD_DATAPATH_TABLE()
-AT_CHECK([ovs-vsctl add-zone-tp $DP_TYPE zone=5 udp_single=3 icmp_first=3])
+
+dnl Creating more timeout policies
+for i in `seq 1 255`; do
+ovs-vsctl --may-exist add-zone-tp $DP_TYPE zone=$i udp_first=$i udp_single=$i icmp_first=$i icmp_reply=$i;
+done
+AT_CHECK([ovs-vsctl --may-exist add-zone-tp $DP_TYPE zone=5 udp_first=1 udp_single=1 icmp_first=1 icmp_reply=1])
dnl Send ICMP and UDP traffic
NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
@@ -3327,7 +3334,7 @@ udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
dnl Wait until the timeout expire.
dnl We intend to wait a bit longer, because conntrack does not recycle the entry right after it is expired.
-sleep 4
+sleep 6
AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
])
@@ -3345,11 +3352,27 @@ udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
dnl Wait until the timeout expire.
dnl We intend to wait a bit longer, because conntrack does not recycle the entry right after it is expired.
-sleep 4
+sleep 6
AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0], [dnl
])
+dnl Set the timeout policy to default again.
+AT_CHECK([ovs-vsctl del-zone-tp $DP_TYPE zone=5])
+
+dnl Send ICMP and UDP traffic
+NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+AT_CHECK([ovs-ofctl -O OpenFlow13 packet-out br0 "in_port=1 packet=50540000000a50540000000908004500001c000000000011a4cd0a0101010a0101020001000200080000 actions=resubmit(,0)"])
+
+sleep 1
+
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2) | sort], [0], [dnl
+icmp,orig=(src=10.1.1.1,dst=10.1.1.2,id=<cleared>,type=8,code=0),reply=(src=10.1.1.2,dst=10.1.1.1,id=<cleared>,type=0,code=0),zone=5
+udp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),zone=5
+])
+
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
@@ -99,12 +99,8 @@ m4_define([CHECK_CONNTRACK_NAT])
# CHECK_CONNTRACK_TIMEOUT()
#
# Perform requirements checks for running conntrack customized timeout tests.
-* The userspace datapath does not support this feature yet.
#
-m4_define([CHECK_CONNTRACK_TIMEOUT],
-[
- AT_SKIP_IF([:])
-])
+m4_define([CHECK_CONNTRACK_TIMEOUT])
# CHECK_CT_DPIF_SET_GET_MAXCONNS()
#
@@ -90,7 +90,7 @@ ct_thread_main(void *aux_)
ovs_barrier_block(&barrier);
for (i = 0; i < n_pkts; i += batch_size) {
conntrack_execute(ct, pkt_batch, dl_type, false, true, 0, NULL, NULL,
- 0, 0, NULL, NULL, now);
+ 0, 0, NULL, NULL, now, 0);
}
ovs_barrier_block(&barrier);
destroy_packets(pkt_batch);
@@ -174,7 +174,7 @@ pcap_batch_execute_conntrack(struct conntrack *ct_,
if (flow.dl_type != dl_type) {
conntrack_execute(ct_, &new_batch, dl_type, false, true, 0,
- NULL, NULL, 0, 0, NULL, NULL, now);
+ NULL, NULL, 0, 0, NULL, NULL, now, 0);
dp_packet_batch_init(&new_batch);
}
dp_packet_batch_add(&new_batch, packet);
@@ -182,7 +182,7 @@ pcap_batch_execute_conntrack(struct conntrack *ct_,
if (!dp_packet_batch_is_empty(&new_batch)) {
conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL,
- 0, 0, NULL, NULL, now);
+ 0, 0, NULL, NULL, now, 0);
}
}
Commit 1f1613183733 ("ct-dpif, dpif-netlink: Add conntrack timeout policy support") adds conntrack timeout policy for kernel datapath. This patch enables support for the userspace datapath. I tested using the 'make check-system-userspace' which checks the timeout policies for ICMP and UDP cases. Signed-off-by: William Tu <u9012063@gmail.com> --- v4: address feedback from Yi-Hung - move default policy value to lib/conntrack-tp.c - separate icmp bug patch - refactor and fix include issues - fix the clang lock analysis annotation - keep clean interval to 5 seconds - improve tests in system-traffic.at - travis: https://travis-ci.org/github/williamtu/ovs-travis/builds/680158645 v3: - address feedback from Yi-Hung - use ID 0 as default policy - move conn_{init,update}_expiration to lib/conntrack-tp.c - s/tpid/tp_id/ - add default timeout value to CT_DPIF_TP_*_ATTRs - reduce the CT_CLEAN_INTERVAL from 5 to 3s, to make the tests run faster. - add more tests to system-traffic.at - code refactoring and renaming --- Documentation/faq/releases.rst | 2 +- NEWS | 2 + lib/automake.mk | 2 + lib/conntrack-icmp.c | 6 +- lib/conntrack-other.c | 4 +- lib/conntrack-private.h | 70 +++------ lib/conntrack-tcp.c | 5 +- lib/conntrack-tp.c | 301 +++++++++++++++++++++++++++++++++++++++ lib/conntrack-tp.h | 30 ++++ lib/conntrack.c | 37 ++--- lib/conntrack.h | 8 +- lib/ct-dpif.h | 2 + lib/dpif-netdev.c | 75 +++++++++- ofproto/ofproto-dpif.c | 3 +- tests/system-traffic.at | 29 +++- tests/system-userspace-macros.at | 6 +- tests/test-conntrack.c | 6 +- 17 files changed, 493 insertions(+), 95 deletions(-) create mode 100644 lib/conntrack-tp.c create mode 100644 lib/conntrack-tp.h