2020-04-29 12:25:11 -07:00
|
|
|
/*
|
|
|
|
* 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 <errno.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 *
|
2022-07-11 18:55:02 +02:00
|
|
|
timeout_policy_lookup_protected(struct conntrack *ct, int32_t tp_id)
|
2020-04-29 12:25:11 -07:00
|
|
|
OVS_REQUIRES(ct->ct_lock)
|
|
|
|
{
|
|
|
|
struct timeout_policy *tp;
|
|
|
|
uint32_t hash;
|
|
|
|
|
|
|
|
hash = hash_int(tp_id, ct->hash_basis);
|
2022-07-11 18:55:02 +02:00
|
|
|
CMAP_FOR_EACH_WITH_HASH_PROTECTED (tp, node, hash,
|
|
|
|
&ct->timeout_policies) {
|
2020-04-29 12:25:11 -07:00
|
|
|
if (tp->policy.id == tp_id) {
|
|
|
|
return tp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-07-11 18:55:02 +02:00
|
|
|
static struct timeout_policy *
|
|
|
|
timeout_policy_lookup(struct conntrack *ct, int32_t tp_id)
|
2020-04-29 12:25:11 -07:00
|
|
|
{
|
|
|
|
struct timeout_policy *tp;
|
2022-07-11 18:55:02 +02:00
|
|
|
uint32_t hash;
|
2020-04-29 12:25:11 -07:00
|
|
|
|
2022-07-11 18:55:02 +02:00
|
|
|
hash = hash_int(tp_id, ct->hash_basis);
|
|
|
|
CMAP_FOR_EACH_WITH_HASH (tp, node, hash, &ct->timeout_policies) {
|
|
|
|
if (tp->policy.id == tp_id) {
|
|
|
|
return tp;
|
|
|
|
}
|
2020-04-29 12:25:11 -07:00
|
|
|
}
|
2022-07-11 18:55:02 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2020-04-29 12:25:11 -07:00
|
|
|
|
2022-07-11 18:55:02 +02:00
|
|
|
struct timeout_policy *
|
|
|
|
timeout_policy_get(struct conntrack *ct, int32_t tp_id)
|
|
|
|
{
|
|
|
|
return timeout_policy_lookup(ct, tp_id);
|
2020-04-29 12:25:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2022-07-11 18:55:02 +02:00
|
|
|
cmap_insert(&ct->timeout_policies, &tp->node, hash);
|
2020-04-29 12:25:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
timeout_policy_clean(struct conntrack *ct, struct timeout_policy *tp)
|
|
|
|
OVS_REQUIRES(ct->ct_lock)
|
|
|
|
{
|
2022-07-11 18:55:02 +02:00
|
|
|
uint32_t hash = hash_int(tp->policy.id, ct->hash_basis);
|
|
|
|
cmap_remove(&ct->timeout_policies, &tp->node, hash);
|
|
|
|
ovsrcu_postpone(free, tp);
|
2020-04-29 12:25:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2022-07-11 18:55:02 +02:00
|
|
|
timeout_policy_delete__(struct conntrack *ct, uint32_t tp_id,
|
|
|
|
bool warn_on_error)
|
2020-04-29 12:25:11 -07:00
|
|
|
OVS_REQUIRES(ct->ct_lock)
|
|
|
|
{
|
2022-07-11 18:55:02 +02:00
|
|
|
struct timeout_policy *tp;
|
2020-04-29 12:25:11 -07:00
|
|
|
int err = 0;
|
|
|
|
|
2022-07-11 18:55:02 +02:00
|
|
|
tp = timeout_policy_lookup_protected(ct, tp_id);
|
2020-04-29 12:25:11 -07:00
|
|
|
if (tp) {
|
|
|
|
timeout_policy_clean(ct, tp);
|
2022-07-11 18:55:02 +02:00
|
|
|
} else if (warn_on_error) {
|
2020-04-29 12:25:11 -07:00
|
|
|
VLOG_WARN_RL(&rl, "Failed to delete a non-existent timeout "
|
|
|
|
"policy: id=%d", tp_id);
|
|
|
|
err = ENOENT;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
timeout_policy_delete(struct conntrack *ct, uint32_t tp_id)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
ovs_mutex_lock(&ct->ct_lock);
|
2022-07-11 18:55:02 +02:00
|
|
|
err = timeout_policy_delete__(ct, tp_id, true);
|
2020-04-29 12:25:11 -07:00
|
|
|
ovs_mutex_unlock(&ct->ct_lock);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
timeout_policy_init(struct conntrack *ct)
|
|
|
|
OVS_REQUIRES(ct->ct_lock)
|
|
|
|
{
|
|
|
|
struct timeout_policy tp;
|
|
|
|
|
2022-07-11 18:55:02 +02:00
|
|
|
cmap_init(&ct->timeout_policies);
|
2020-04-29 12:25:11 -07:00
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
|
|
|
uint32_t tp_id = new_tp->policy.id;
|
2022-07-11 18:55:02 +02:00
|
|
|
int err = 0;
|
2020-04-29 12:25:11 -07:00
|
|
|
|
|
|
|
ovs_mutex_lock(&ct->ct_lock);
|
2022-07-11 18:55:02 +02:00
|
|
|
timeout_policy_delete__(ct, tp_id, false);
|
2020-04-29 12:25:11 -07:00
|
|
|
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_MULTIPLE;
|
|
|
|
case CT_TM_OTHER_MULTIPLE:
|
|
|
|
return CT_DPIF_TP_ATTR_UDP_SINGLE;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
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, "Update timeout %s zone=%u with policy id=%d "
|
|
|
|
"val=%u sec.",
|
2023-08-30 21:29:51 +02:00
|
|
|
ct_timeout_str[tm], conn->key_node[CT_DIR_FWD].key.zone,
|
|
|
|
conn->tp_id, val);
|
2020-04-29 12:25:11 -07:00
|
|
|
|
2022-07-11 18:55:15 +02:00
|
|
|
atomic_store_relaxed(&conn->expiration, now + val * 1000);
|
2020-04-29 12:25:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
conn_init_expiration(struct conntrack *ct, struct conn *conn,
|
|
|
|
enum ct_timeout tm, long long now)
|
|
|
|
{
|
|
|
|
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.",
|
2023-08-30 21:29:51 +02:00
|
|
|
ct_timeout_str[tm], conn->key_node[CT_DIR_FWD].key.zone,
|
|
|
|
conn->tp_id, val);
|
2020-04-29 12:25:11 -07:00
|
|
|
|
2022-07-11 18:55:08 +02:00
|
|
|
conn->expiration = now + val * 1000;
|
2020-04-29 12:25:11 -07:00
|
|
|
}
|