]> git.proxmox.com Git - mirror_ovs.git/blob - lib/conntrack-tp.c
ovsdb-idl: Fix iteration over tracked rows with no actual data.
[mirror_ovs.git] / lib / conntrack-tp.c
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(struct conntrack *ct, int32_t tp_id)
51 OVS_REQUIRES(ct->ct_lock)
52 {
53 struct timeout_policy *tp;
54 uint32_t hash;
55
56 hash = hash_int(tp_id, ct->hash_basis);
57 HMAP_FOR_EACH_IN_BUCKET (tp, node, hash, &ct->timeout_policies) {
58 if (tp->policy.id == tp_id) {
59 return tp;
60 }
61 }
62 return NULL;
63 }
64
65 struct timeout_policy *
66 timeout_policy_get(struct conntrack *ct, int32_t tp_id)
67 {
68 struct timeout_policy *tp;
69
70 ovs_mutex_lock(&ct->ct_lock);
71 tp = timeout_policy_lookup(ct, tp_id);
72 if (!tp) {
73 ovs_mutex_unlock(&ct->ct_lock);
74 return NULL;
75 }
76
77 ovs_mutex_unlock(&ct->ct_lock);
78 return tp;
79 }
80
81 static void
82 update_existing_tp(struct timeout_policy *tp_dst,
83 const struct timeout_policy *tp_src)
84 {
85 struct ct_dpif_timeout_policy *dst;
86 const struct ct_dpif_timeout_policy *src;
87 int i;
88
89 dst = &tp_dst->policy;
90 src = &tp_src->policy;
91
92 /* Set the value and present bit to dst if present
93 * bit in src is set.
94 */
95 for (i = 0; i < ARRAY_SIZE(dst->attrs); i++) {
96 if (src->present & (1 << i)) {
97 dst->attrs[i] = src->attrs[i];
98 dst->present |= (1 << i);
99 }
100 }
101 }
102
103 static void
104 init_default_tp(struct timeout_policy *tp, uint32_t tp_id)
105 {
106 tp->policy.id = tp_id;
107 /* Initialize the timeout value to default, but not
108 * setting the present bit.
109 */
110 tp->policy.present = 0;
111 memcpy(tp->policy.attrs, ct_dpif_netdev_tp_def,
112 sizeof tp->policy.attrs);
113 }
114
115 static void
116 timeout_policy_create(struct conntrack *ct,
117 struct timeout_policy *new_tp)
118 OVS_REQUIRES(ct->ct_lock)
119 {
120 uint32_t tp_id = new_tp->policy.id;
121 struct timeout_policy *tp;
122 uint32_t hash;
123
124 tp = xzalloc(sizeof *tp);
125 init_default_tp(tp, tp_id);
126 update_existing_tp(tp, new_tp);
127 hash = hash_int(tp_id, ct->hash_basis);
128 hmap_insert(&ct->timeout_policies, &tp->node, hash);
129 }
130
131 static void
132 timeout_policy_clean(struct conntrack *ct, struct timeout_policy *tp)
133 OVS_REQUIRES(ct->ct_lock)
134 {
135 hmap_remove(&ct->timeout_policies, &tp->node);
136 free(tp);
137 }
138
139 static int
140 timeout_policy_delete__(struct conntrack *ct, uint32_t tp_id)
141 OVS_REQUIRES(ct->ct_lock)
142 {
143 int err = 0;
144 struct timeout_policy *tp = timeout_policy_lookup(ct, tp_id);
145
146 if (tp) {
147 timeout_policy_clean(ct, tp);
148 } else {
149 VLOG_WARN_RL(&rl, "Failed to delete a non-existent timeout "
150 "policy: id=%d", tp_id);
151 err = ENOENT;
152 }
153 return err;
154 }
155
156 int
157 timeout_policy_delete(struct conntrack *ct, uint32_t tp_id)
158 {
159 int err;
160
161 ovs_mutex_lock(&ct->ct_lock);
162 err = timeout_policy_delete__(ct, tp_id);
163 ovs_mutex_unlock(&ct->ct_lock);
164 return err;
165 }
166
167 void
168 timeout_policy_init(struct conntrack *ct)
169 OVS_REQUIRES(ct->ct_lock)
170 {
171 struct timeout_policy tp;
172
173 hmap_init(&ct->timeout_policies);
174
175 /* Create default timeout policy. */
176 memset(&tp, 0, sizeof tp);
177 tp.policy.id = DEFAULT_TP_ID;
178 timeout_policy_create(ct, &tp);
179 }
180
181 int
182 timeout_policy_update(struct conntrack *ct,
183 struct timeout_policy *new_tp)
184 {
185 int err = 0;
186 uint32_t tp_id = new_tp->policy.id;
187
188 ovs_mutex_lock(&ct->ct_lock);
189 struct timeout_policy *tp = timeout_policy_lookup(ct, tp_id);
190 if (tp) {
191 err = timeout_policy_delete__(ct, tp_id);
192 }
193 timeout_policy_create(ct, new_tp);
194 ovs_mutex_unlock(&ct->ct_lock);
195 return err;
196 }
197
198 static enum ct_dpif_tp_attr
199 tm_to_ct_dpif_tp(enum ct_timeout tm)
200 {
201 switch (tm) {
202 case CT_TM_TCP_FIRST_PACKET:
203 return CT_DPIF_TP_ATTR_TCP_SYN_SENT;
204 case CT_TM_TCP_OPENING:
205 return CT_DPIF_TP_ATTR_TCP_SYN_RECV;
206 case CT_TM_TCP_ESTABLISHED:
207 return CT_DPIF_TP_ATTR_TCP_ESTABLISHED;
208 case CT_TM_TCP_CLOSING:
209 return CT_DPIF_TP_ATTR_TCP_FIN_WAIT;
210 case CT_TM_TCP_FIN_WAIT:
211 return CT_DPIF_TP_ATTR_TCP_TIME_WAIT;
212 case CT_TM_TCP_CLOSED:
213 return CT_DPIF_TP_ATTR_TCP_CLOSE;
214 case CT_TM_OTHER_FIRST:
215 return CT_DPIF_TP_ATTR_UDP_FIRST;
216 case CT_TM_OTHER_BIDIR:
217 return CT_DPIF_TP_ATTR_UDP_MULTIPLE;
218 case CT_TM_OTHER_MULTIPLE:
219 return CT_DPIF_TP_ATTR_UDP_SINGLE;
220 case CT_TM_ICMP_FIRST:
221 return CT_DPIF_TP_ATTR_ICMP_FIRST;
222 case CT_TM_ICMP_REPLY:
223 return CT_DPIF_TP_ATTR_ICMP_REPLY;
224 case N_CT_TM:
225 default:
226 OVS_NOT_REACHED();
227 break;
228 }
229 OVS_NOT_REACHED();
230 return CT_DPIF_TP_ATTR_MAX;
231 }
232
233 static void
234 conn_update_expiration__(struct conntrack *ct, struct conn *conn,
235 enum ct_timeout tm, long long now,
236 uint32_t tp_value)
237 OVS_REQUIRES(conn->lock)
238 {
239 ovs_mutex_unlock(&conn->lock);
240
241 ovs_mutex_lock(&ct->ct_lock);
242 ovs_mutex_lock(&conn->lock);
243 if (!conn->cleaned) {
244 conn->expiration = now + tp_value * 1000;
245 ovs_list_remove(&conn->exp_node);
246 ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node);
247 }
248 ovs_mutex_unlock(&conn->lock);
249 ovs_mutex_unlock(&ct->ct_lock);
250
251 ovs_mutex_lock(&conn->lock);
252 }
253
254 /* The conn entry lock must be held on entry and exit. */
255 void
256 conn_update_expiration(struct conntrack *ct, struct conn *conn,
257 enum ct_timeout tm, long long now)
258 OVS_REQUIRES(conn->lock)
259 {
260 struct timeout_policy *tp;
261 uint32_t val;
262
263 ovs_mutex_unlock(&conn->lock);
264
265 ovs_mutex_lock(&ct->ct_lock);
266 ovs_mutex_lock(&conn->lock);
267 tp = timeout_policy_lookup(ct, conn->tp_id);
268 if (tp) {
269 val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)];
270 } else {
271 val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)];
272 }
273 ovs_mutex_unlock(&conn->lock);
274 ovs_mutex_unlock(&ct->ct_lock);
275
276 ovs_mutex_lock(&conn->lock);
277 VLOG_DBG_RL(&rl, "Update timeout %s zone=%u with policy id=%d "
278 "val=%u sec.",
279 ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
280
281 conn_update_expiration__(ct, conn, tm, now, val);
282 }
283
284 static void
285 conn_init_expiration__(struct conntrack *ct, struct conn *conn,
286 enum ct_timeout tm, long long now,
287 uint32_t tp_value)
288 {
289 conn->expiration = now + tp_value * 1000;
290 ovs_list_push_back(&ct->exp_lists[tm], &conn->exp_node);
291 }
292
293 /* ct_lock must be held. */
294 void
295 conn_init_expiration(struct conntrack *ct, struct conn *conn,
296 enum ct_timeout tm, long long now)
297 OVS_REQUIRES(ct->ct_lock)
298 {
299 struct timeout_policy *tp;
300 uint32_t val;
301
302 tp = timeout_policy_lookup(ct, conn->tp_id);
303 if (tp) {
304 val = tp->policy.attrs[tm_to_ct_dpif_tp(tm)];
305 } else {
306 val = ct_dpif_netdev_tp_def[tm_to_ct_dpif_tp(tm)];
307 }
308
309 VLOG_DBG_RL(&rl, "Init timeout %s zone=%u with policy id=%d val=%u sec.",
310 ct_timeout_str[tm], conn->key.zone, conn->tp_id, val);
311
312 conn_init_expiration__(ct, conn, tm, now, val);
313 }