Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/conntrack-tp.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020 VMware, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at:
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <config.h>
18
19
#include <errno.h>
20
#include "conntrack-private.h"
21
#include "conntrack-tp.h"
22
#include "ct-dpif.h"
23
#include "openvswitch/vlog.h"
24
25
VLOG_DEFINE_THIS_MODULE(conntrack_tp);
26
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
27
28
static const char *ct_timeout_str[] = {
29
#define CT_TIMEOUT(NAME) #NAME,
30
    CT_TIMEOUTS
31
#undef CT_TIMEOUT
32
};
33
34
/* Default timeout policy in seconds. */
35
static unsigned int ct_dpif_netdev_tp_def[] = {
36
    [CT_DPIF_TP_ATTR_TCP_SYN_SENT] = 30,
37
    [CT_DPIF_TP_ATTR_TCP_SYN_RECV] = 30,
38
    [CT_DPIF_TP_ATTR_TCP_ESTABLISHED] = 24 * 60 * 60,
39
    [CT_DPIF_TP_ATTR_TCP_FIN_WAIT] = 15 * 60,
40
    [CT_DPIF_TP_ATTR_TCP_TIME_WAIT] = 45,
41
    [CT_DPIF_TP_ATTR_TCP_CLOSE] = 30,
42
    [CT_DPIF_TP_ATTR_UDP_FIRST] = 60,
43
    [CT_DPIF_TP_ATTR_UDP_SINGLE] = 60,
44
    [CT_DPIF_TP_ATTR_UDP_MULTIPLE] = 30,
45
    [CT_DPIF_TP_ATTR_ICMP_FIRST] = 60,
46
    [CT_DPIF_TP_ATTR_ICMP_REPLY] = 30,
47
};
48
49
static struct timeout_policy *
50
timeout_policy_lookup_protected(struct conntrack *ct, int32_t tp_id)
51
    OVS_REQUIRES(ct->ct_lock)
52
0
{
53
0
    struct timeout_policy *tp;
54
0
    uint32_t hash;
55
56
0
    hash = hash_int(tp_id, ct->hash_basis);
57
0
    CMAP_FOR_EACH_WITH_HASH_PROTECTED (tp, node, hash,
58
0
                                       &ct->timeout_policies) {
59
0
        if (tp->policy.id == tp_id) {
60
0
            return tp;
61
0
        }
62
0
    }
63
0
    return NULL;
64
0
}
65
66
static struct timeout_policy *
67
timeout_policy_lookup(struct conntrack *ct, int32_t tp_id)
68
0
{
69
0
    struct timeout_policy *tp;
70
0
    uint32_t hash;
71
72
0
    hash = hash_int(tp_id, ct->hash_basis);
73
0
    CMAP_FOR_EACH_WITH_HASH (tp, node, hash, &ct->timeout_policies) {
74
0
        if (tp->policy.id == tp_id) {
75
0
            return tp;
76
0
        }
77
0
    }
78
0
    return NULL;
79
0
}
80
81
struct timeout_policy *
82
timeout_policy_get(struct conntrack *ct, int32_t tp_id)
83
0
{
84
0
    return timeout_policy_lookup(ct, tp_id);
85
0
}
86
87
static void
88
update_existing_tp(struct timeout_policy *tp_dst,
89
                   const struct timeout_policy *tp_src)
90
0
{
91
0
    struct ct_dpif_timeout_policy *dst;
92
0
    const struct ct_dpif_timeout_policy *src;
93
0
    int i;
94
95
0
    dst = &tp_dst->policy;
96
0
    src = &tp_src->policy;
97
98
    /* Set the value and present bit to dst if present
99
     * bit in src is set.
100
     */
101
0
    for (i = 0; i < ARRAY_SIZE(dst->attrs); i++) {
102
0
        if (src->present & (1 << i)) {
103
0
            dst->attrs[i] = src->attrs[i];
104
0
            dst->present |= (1 << i);
105
0
        }
106
0
    }
107
0
}
108
109
static void
110
init_default_tp(struct timeout_policy *tp, uint32_t tp_id)
111
0
{
112
0
    tp->policy.id = tp_id;
113
    /* Initialize the timeout value to default, but not
114
     * setting the present bit.
115
     */
116
0
    tp->policy.present = 0;
117
0
    memcpy(tp->policy.attrs, ct_dpif_netdev_tp_def,
118
0
           sizeof tp->policy.attrs);
119
0
}
120
121
static void
122
timeout_policy_create(struct conntrack *ct,
123
                      struct timeout_policy *new_tp)
124
    OVS_REQUIRES(ct->ct_lock)
125
0
{
126
0
    uint32_t tp_id = new_tp->policy.id;
127
0
    struct timeout_policy *tp;
128
0
    uint32_t hash;
129
130
0
    tp = xzalloc(sizeof *tp);
131
0
    init_default_tp(tp, tp_id);
132
0
    update_existing_tp(tp, new_tp);
133
0
    hash = hash_int(tp_id, ct->hash_basis);
134
0
    cmap_insert(&ct->timeout_policies, &tp->node, hash);
135
0
}
136
137
static void
138
timeout_policy_clean(struct conntrack *ct, struct timeout_policy *tp)
139
    OVS_REQUIRES(ct->ct_lock)
140
0
{
141
0
    uint32_t hash = hash_int(tp->policy.id, ct->hash_basis);
142
0
    cmap_remove(&ct->timeout_policies, &tp->node, hash);
143
0
    ovsrcu_postpone(free, tp);
144
0
}
145
146
static int
147
timeout_policy_delete__(struct conntrack *ct, uint32_t tp_id,
148
                        bool warn_on_error)
149
    OVS_REQUIRES(ct->ct_lock)
150
0
{
151
0
    struct timeout_policy *tp;
152
0
    int err = 0;
153
154
0
    tp = timeout_policy_lookup_protected(ct, tp_id);
155
0
    if (tp) {
156
0
        timeout_policy_clean(ct, tp);
157
0
    } else if (warn_on_error) {
158
0
        VLOG_WARN_RL(&rl, "Failed to delete a non-existent timeout "
159
0
                     "policy: id=%d", tp_id);
160
0
        err = ENOENT;
161
0
    }
162
0
    return err;
163
0
}
164
165
int
166
timeout_policy_delete(struct conntrack *ct, uint32_t tp_id)
167
0
{
168
0
    int err;
169
170
0
    ovs_mutex_lock(&ct->ct_lock);
171
0
    err = timeout_policy_delete__(ct, tp_id, true);
172
0
    ovs_mutex_unlock(&ct->ct_lock);
173
0
    return err;
174
0
}
175
176
void
177
timeout_policy_init(struct conntrack *ct)
178
    OVS_REQUIRES(ct->ct_lock)
179
0
{
180
0
    struct timeout_policy tp;
181
182
0
    cmap_init(&ct->timeout_policies);
183
184
    /* Create default timeout policy. */
185
0
    memset(&tp, 0, sizeof tp);
186
0
    tp.policy.id = DEFAULT_TP_ID;
187
0
    timeout_policy_create(ct, &tp);
188
0
}
189
190
int
191
timeout_policy_update(struct conntrack *ct,
192
                      struct timeout_policy *new_tp)
193
0
{
194
0
    uint32_t tp_id = new_tp->policy.id;
195
0
    int err = 0;
196
197
0
    ovs_mutex_lock(&ct->ct_lock);
198
0
    timeout_policy_delete__(ct, tp_id, false);
199
0
    timeout_policy_create(ct, new_tp);
200
0
    ovs_mutex_unlock(&ct->ct_lock);
201
0
    return err;
202
0
}
203
204
static enum ct_dpif_tp_attr
205
tm_to_ct_dpif_tp(enum ct_timeout tm)
206
0
{
207
0
    switch (tm) {
208
0
    case CT_TM_TCP_FIRST_PACKET:
209
0
        return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
210
0
    case CT_TM_TCP_OPENING:
211
0
        return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
212
0
    case CT_TM_TCP_ESTABLISHED:
213
0
        return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
214
0
    case CT_TM_TCP_CLOSING:
215
0
        return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
216
0
    case CT_TM_TCP_FIN_WAIT:
217
0
        return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
218
0
    case CT_TM_TCP_CLOSED:
219
0
        return CT_DPIF_TP_ATTR_TCP_CLOSE;
220
0
    case CT_TM_OTHER_FIRST:
221
0
        return CT_DPIF_TP_ATTR_UDP_FIRST;
222
0
    case CT_TM_OTHER_BIDIR:
223
0
        return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
224
0
    case CT_TM_OTHER_MULTIPLE:
225
0
        return CT_DPIF_TP_ATTR_UDP_SINGLE;
226
0
    case CT_TM_ICMP_FIRST:
227
0
        return CT_DPIF_TP_ATTR_ICMP_FIRST;
228
0
    case CT_TM_ICMP_REPLY:
229
0
        return CT_DPIF_TP_ATTR_ICMP_REPLY;
230
0
    case N_CT_TM:
231
0
    default:
232
0
        OVS_NOT_REACHED();
233
0
        break;
234
0
    }
235
0
    OVS_NOT_REACHED();
236
0
    return CT_DPIF_TP_ATTR_MAX;
237
0
}
238
239
/* The conn entry lock must be held on entry and exit. */
240
void
241
conn_update_expiration(struct conntrack *ct, struct conn *conn,
242
                       enum ct_timeout tm, long long now)
243
    OVS_REQUIRES(conn->lock)
244
0
{
245
0
    struct timeout_policy *tp;
246
0
    uint32_t val;
247
248
0
    tp = timeout_policy_lookup(ct, conn->tp_id);
249
0
    if (tp) {
250
0
        val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)];
251
0
    } else {
252
0
        val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)];
253
0
    }
254
0
    VLOG_DBG_RL(&rl, "Update timeout %s zone=%u with policy id=%d "
255
0
                "val=%u sec.",
256
0
                ct_timeout_str[tm], conn->key_node[CT_DIR_FWD].key.zone,
257
0
                conn->tp_id, val);
258
259
0
    atomic_store_relaxed(&conn->expiration, now + val * 1000);
260
0
}
261
262
void
263
conn_init_expiration(struct conntrack *ct, struct conn *conn,
264
                     enum ct_timeout tm, long long now)
265
0
{
266
0
    struct timeout_policy *tp;
267
0
    uint32_t val;
268
269
0
    tp = timeout_policy_lookup(ct, conn->tp_id);
270
0
    if (tp) {
271
0
        val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)];
272
0
    } else {
273
0
        val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)];
274
0
    }
275
276
0
    VLOG_DBG_RL(&rl, "Init timeout %s zone=%u with policy id=%d val=%u sec.",
277
0
                ct_timeout_str[tm], conn->key_node[CT_DIR_FWD].key.zone,
278
0
                conn->tp_id, val);
279
280
0
    conn->expiration = now + val * 1000;
281
0
}