Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2015, 2016 Nicira, 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 : : #include "conntrack.h"
19 : :
20 : : #include <errno.h>
21 : : #include <sys/types.h>
22 : : #include <netinet/in.h>
23 : : #include <netinet/icmp6.h>
24 : :
25 : : #include "bitmap.h"
26 : : #include "conntrack-private.h"
27 : : #include "coverage.h"
28 : : #include "csum.h"
29 : : #include "ct-dpif.h"
30 : : #include "dp-packet.h"
31 : : #include "flow.h"
32 : : #include "netdev.h"
33 : : #include "odp-netlink.h"
34 : : #include "openvswitch/hmap.h"
35 : : #include "openvswitch/vlog.h"
36 : : #include "ovs-rcu.h"
37 : : #include "ovs-thread.h"
38 : : #include "poll-loop.h"
39 : : #include "random.h"
40 : : #include "timeval.h"
41 : :
42 : 20190 : VLOG_DEFINE_THIS_MODULE(conntrack);
43 : :
44 : 92814 : COVERAGE_DEFINE(conntrack_full);
45 : 92814 : COVERAGE_DEFINE(conntrack_long_cleanup);
46 : :
47 : : struct conn_lookup_ctx {
48 : : struct conn_key key;
49 : : struct conn *conn;
50 : : uint32_t hash;
51 : : bool reply;
52 : : bool related;
53 : : };
54 : :
55 : : static bool conn_key_extract(struct conntrack *, struct dp_packet *,
56 : : ovs_be16 dl_type, struct conn_lookup_ctx *,
57 : : uint16_t zone);
58 : : static uint32_t conn_key_hash(const struct conn_key *, uint32_t basis);
59 : : static void conn_key_reverse(struct conn_key *);
60 : : static void conn_key_lookup(struct conntrack_bucket *ctb,
61 : : struct conn_lookup_ctx *ctx,
62 : : long long now);
63 : : static bool valid_new(struct dp_packet *pkt, struct conn_key *);
64 : : static struct conn *new_conn(struct conntrack_bucket *, struct dp_packet *pkt,
65 : : struct conn_key *, long long now);
66 : : static void delete_conn(struct conn *);
67 : : static enum ct_update_res conn_update(struct conn *,
68 : : struct conntrack_bucket *ctb,
69 : : struct dp_packet *, bool reply,
70 : : long long now);
71 : : static bool conn_expired(struct conn *, long long now);
72 : : static void set_mark(struct dp_packet *, struct conn *,
73 : : uint32_t val, uint32_t mask);
74 : : static void set_label(struct dp_packet *, struct conn *,
75 : : const struct ovs_key_ct_labels *val,
76 : : const struct ovs_key_ct_labels *mask);
77 : : static void *clean_thread_main(void *f_);
78 : :
79 : : static struct ct_l4_proto *l4_protos[] = {
80 : : [IPPROTO_TCP] = &ct_proto_tcp,
81 : : [IPPROTO_UDP] = &ct_proto_other,
82 : : [IPPROTO_ICMP] = &ct_proto_icmp4,
83 : : [IPPROTO_ICMPV6] = &ct_proto_icmp6,
84 : : };
85 : :
86 : : long long ct_timeout_val[] = {
87 : : #define CT_TIMEOUT(NAME, VAL) [CT_TM_##NAME] = VAL,
88 : : CT_TIMEOUTS
89 : : #undef CT_TIMEOUT
90 : : };
91 : :
92 : : /* If the total number of connections goes above this value, no new connections
93 : : * are accepted */
94 : : #define DEFAULT_N_CONN_LIMIT 3000000
95 : :
96 : : /* Initializes the connection tracker 'ct'. The caller is responsible for
97 : : * calling 'conntrack_destroy()', when the instance is not needed anymore */
98 : : void
99 : 553 : conntrack_init(struct conntrack *ct)
100 : : {
101 : : unsigned i, j;
102 : 553 : long long now = time_msec();
103 : :
104 [ + + ]: 142121 : for (i = 0; i < CONNTRACK_BUCKETS; i++) {
105 : 141568 : struct conntrack_bucket *ctb = &ct->buckets[i];
106 : :
107 : 141568 : ct_lock_init(&ctb->lock);
108 : 141568 : ct_lock_lock(&ctb->lock);
109 : 141568 : hmap_init(&ctb->connections);
110 [ + + ]: 1698816 : for (j = 0; j < ARRAY_SIZE(ctb->exp_lists); j++) {
111 : 1557248 : ovs_list_init(&ctb->exp_lists[j]);
112 : : }
113 : 141568 : ct_lock_unlock(&ctb->lock);
114 : 141568 : ovs_mutex_init(&ctb->cleanup_mutex);
115 : 141568 : ovs_mutex_lock(&ctb->cleanup_mutex);
116 : 141568 : ctb->next_cleanup = now + CT_TM_MIN;
117 : 141568 : ovs_mutex_unlock(&ctb->cleanup_mutex);
118 : : }
119 : 553 : ct->hash_basis = random_uint32();
120 : 553 : atomic_count_init(&ct->n_conn, 0);
121 : 553 : atomic_init(&ct->n_conn_limit, DEFAULT_N_CONN_LIMIT);
122 : 553 : latch_init(&ct->clean_thread_exit);
123 : 553 : ct->clean_thread = ovs_thread_create("ct_clean", clean_thread_main, ct);
124 : 553 : }
125 : :
126 : : /* Destroys the connection tracker 'ct' and frees all the allocated memory. */
127 : : void
128 : 2 : conntrack_destroy(struct conntrack *ct)
129 : : {
130 : : unsigned i;
131 : :
132 : 2 : latch_set(&ct->clean_thread_exit);
133 : 2 : pthread_join(ct->clean_thread, NULL);
134 : 2 : latch_destroy(&ct->clean_thread_exit);
135 [ + + ]: 514 : for (i = 0; i < CONNTRACK_BUCKETS; i++) {
136 : 512 : struct conntrack_bucket *ctb = &ct->buckets[i];
137 : : struct conn *conn;
138 : :
139 : 512 : ovs_mutex_destroy(&ctb->cleanup_mutex);
140 : 512 : ct_lock_lock(&ctb->lock);
141 [ + - ][ - + ]: 512 : HMAP_FOR_EACH_POP(conn, node, &ctb->connections) {
[ - + ]
142 : 0 : atomic_count_dec(&ct->n_conn);
143 : 0 : delete_conn(conn);
144 : : }
145 : 512 : hmap_destroy(&ctb->connections);
146 : 512 : ct_lock_unlock(&ctb->lock);
147 : 512 : ct_lock_destroy(&ctb->lock);
148 : : }
149 : 2 : }
150 : :
151 : 1390 : static unsigned hash_to_bucket(uint32_t hash)
152 : : {
153 : : /* Extracts the most significant bits in hash. The least significant bits
154 : : * are already used internally by the hmap implementation. */
155 : : BUILD_ASSERT(CONNTRACK_BUCKETS_SHIFT < 32 && CONNTRACK_BUCKETS_SHIFT >= 1);
156 : :
157 : 1390 : return (hash >> (32 - CONNTRACK_BUCKETS_SHIFT)) % CONNTRACK_BUCKETS;
158 : : }
159 : :
160 : : static void
161 : 624 : write_ct_md(struct dp_packet *pkt, uint16_t state, uint16_t zone,
162 : : uint32_t mark, ovs_u128 label)
163 : : {
164 : 624 : pkt->md.ct_state = state | CS_TRACKED;
165 : 624 : pkt->md.ct_zone = zone;
166 : 624 : pkt->md.ct_mark = mark;
167 : 624 : pkt->md.ct_label = label;
168 : 624 : }
169 : :
170 : : static struct conn *
171 : 146 : conn_not_found(struct conntrack *ct, struct dp_packet *pkt,
172 : : struct conn_lookup_ctx *ctx, uint16_t *state, bool commit,
173 : : long long now)
174 : : {
175 : 146 : unsigned bucket = hash_to_bucket(ctx->hash);
176 : 146 : struct conn *nc = NULL;
177 : :
178 [ + + ]: 146 : if (!valid_new(pkt, &ctx->key)) {
179 : 6 : *state |= CS_INVALID;
180 : 6 : return nc;
181 : : }
182 : :
183 : 140 : *state |= CS_NEW;
184 : :
185 [ + + ]: 140 : if (commit) {
186 : : unsigned int n_conn_limit;
187 : :
188 : 80 : atomic_read_relaxed(&ct->n_conn_limit, &n_conn_limit);
189 : :
190 [ - + ]: 80 : if (atomic_count_get(&ct->n_conn) >= n_conn_limit) {
191 : 0 : COVERAGE_INC(conntrack_full);
192 : 0 : return nc;
193 : : }
194 : :
195 : 80 : nc = new_conn(&ct->buckets[bucket], pkt, &ctx->key, now);
196 : :
197 : 80 : memcpy(&nc->rev_key, &ctx->key, sizeof nc->rev_key);
198 : :
199 : 80 : conn_key_reverse(&nc->rev_key);
200 : 80 : hmap_insert(&ct->buckets[bucket].connections, &nc->node, ctx->hash);
201 : 80 : atomic_count_inc(&ct->n_conn);
202 : : }
203 : :
204 : 140 : return nc;
205 : : }
206 : :
207 : : static struct conn *
208 : 622 : process_one(struct conntrack *ct, struct dp_packet *pkt,
209 : : struct conn_lookup_ctx *ctx, uint16_t zone,
210 : : bool commit, long long now)
211 : : {
212 : 622 : unsigned bucket = hash_to_bucket(ctx->hash);
213 : 622 : struct conn *conn = ctx->conn;
214 : 622 : uint16_t state = 0;
215 : :
216 [ + + ]: 622 : if (conn) {
217 [ + + ]: 477 : if (ctx->related) {
218 : 3 : state |= CS_RELATED;
219 [ + - ]: 3 : if (ctx->reply) {
220 : 3 : state |= CS_REPLY_DIR;
221 : : }
222 : : } else {
223 : : enum ct_update_res res;
224 : :
225 : 474 : res = conn_update(conn, &ct->buckets[bucket], pkt,
226 : 474 : ctx->reply, now);
227 : :
228 [ + - + - ]: 474 : switch (res) {
229 : : case CT_UPDATE_VALID:
230 : 473 : state |= CS_ESTABLISHED;
231 [ + + ]: 473 : if (ctx->reply) {
232 : 264 : state |= CS_REPLY_DIR;
233 : : }
234 : 473 : break;
235 : : case CT_UPDATE_INVALID:
236 : 0 : state |= CS_INVALID;
237 : 0 : break;
238 : : case CT_UPDATE_NEW:
239 : 1 : ovs_list_remove(&conn->exp_node);
240 : 1 : hmap_remove(&ct->buckets[bucket].connections, &conn->node);
241 : 1 : atomic_count_dec(&ct->n_conn);
242 : 1 : delete_conn(conn);
243 : 1 : conn = conn_not_found(ct, pkt, ctx, &state, commit, now);
244 : 477 : break;
245 : : default:
246 : 0 : OVS_NOT_REACHED();
247 : : }
248 : : }
249 : : } else {
250 : 145 : conn = conn_not_found(ct, pkt, ctx, &state, commit, now);
251 : : }
252 : :
253 [ + + ][ + + ]: 622 : write_ct_md(pkt, state, zone, conn ? conn->mark : 0,
254 : : conn ? conn->label : OVS_U128_ZERO);
255 : :
256 : 622 : return conn;
257 : : }
258 : :
259 : : /* Sends the packets in '*pkt_batch' through the connection tracker 'ct'. All
260 : : * the packets should have the same 'dl_type' (IPv4 or IPv6) and should have
261 : : * the l3 and and l4 offset properly set.
262 : : *
263 : : * If 'commit' is true, the packets are allowed to create new entries in the
264 : : * connection tables. 'setmark', if not NULL, should point to a two
265 : : * elements array containing a value and a mask to set the connection mark.
266 : : * 'setlabel' behaves similarly for the connection label.*/
267 : : int
268 : 624 : conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
269 : : ovs_be16 dl_type, bool commit, uint16_t zone,
270 : : const uint32_t *setmark,
271 : : const struct ovs_key_ct_labels *setlabel,
272 : : const char *helper)
273 : 624 : {
274 : 624 : struct dp_packet **pkts = pkt_batch->packets;
275 : 624 : size_t cnt = pkt_batch->count;
276 : : #if !defined(__CHECKER__) && !defined(_WIN32)
277 : 624 : const size_t KEY_ARRAY_SIZE = cnt;
278 : : #else
279 : : enum { KEY_ARRAY_SIZE = NETDEV_MAX_BURST };
280 : : #endif
281 : 624 : struct conn_lookup_ctx ctxs[KEY_ARRAY_SIZE];
282 : : int8_t bucket_list[CONNTRACK_BUCKETS];
283 : : struct {
284 : : unsigned bucket;
285 : : unsigned long maps;
286 : 624 : } arr[KEY_ARRAY_SIZE];
287 : 624 : long long now = time_msec();
288 : 624 : size_t i = 0;
289 : 624 : uint8_t arrcnt = 0;
290 : :
291 : : BUILD_ASSERT_DECL(sizeof arr[0].maps * CHAR_BIT >= NETDEV_MAX_BURST);
292 : :
293 [ - + ]: 624 : if (helper) {
294 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
295 : :
296 [ # # ]: 0 : VLOG_WARN_RL(&rl, "ALG helper \"%s\" not supported", helper);
297 : : /* Continue without the helper */
298 : : }
299 : :
300 : 624 : memset(bucket_list, INT8_C(-1), sizeof bucket_list);
301 [ + + ]: 1248 : for (i = 0; i < cnt; i++) {
302 : : unsigned bucket;
303 : :
304 [ + + ]: 624 : if (!conn_key_extract(ct, pkts[i], dl_type, &ctxs[i], zone)) {
305 : 2 : write_ct_md(pkts[i], CS_INVALID, zone, 0, OVS_U128_ZERO);
306 : 2 : continue;
307 : : }
308 : :
309 : 622 : bucket = hash_to_bucket(ctxs[i].hash);
310 [ + - ]: 622 : if (bucket_list[bucket] == INT8_C(-1)) {
311 : 622 : bucket_list[bucket] = arrcnt;
312 : :
313 : 622 : arr[arrcnt].maps = 0;
314 : 622 : ULLONG_SET1(arr[arrcnt].maps, i);
315 : 622 : arr[arrcnt++].bucket = bucket;
316 : : } else {
317 : 0 : ULLONG_SET1(arr[bucket_list[bucket]].maps, i);
318 : 0 : arr[bucket_list[bucket]].maps |= 1UL << i;
319 : : }
320 : : }
321 : :
322 [ + + ]: 1246 : for (i = 0; i < arrcnt; i++) {
323 : 622 : struct conntrack_bucket *ctb = &ct->buckets[arr[i].bucket];
324 : : size_t j;
325 : :
326 : 622 : ct_lock_lock(&ctb->lock);
327 : :
328 [ + + ]: 1244 : ULLONG_FOR_EACH_1(j, arr[i].maps) {
329 : : struct conn *conn;
330 : :
331 : 622 : conn_key_lookup(ctb, &ctxs[j], now);
332 : :
333 : 622 : conn = process_one(ct, pkts[j], &ctxs[j], zone, commit, now);
334 : :
335 [ + + ][ + + ]: 622 : if (conn && setmark) {
336 : 50 : set_mark(pkts[j], conn, setmark[0], setmark[1]);
337 : : }
338 : :
339 [ + + ][ + + ]: 622 : if (conn && setlabel) {
340 : 44 : set_label(pkts[j], conn, &setlabel[0], &setlabel[1]);
341 : : }
342 : : }
343 : 622 : ct_lock_unlock(&ctb->lock);
344 : : }
345 : :
346 : 624 : return 0;
347 : : }
348 : :
349 : : static void
350 : 50 : set_mark(struct dp_packet *pkt, struct conn *conn, uint32_t val, uint32_t mask)
351 : : {
352 : 50 : pkt->md.ct_mark = val | (pkt->md.ct_mark & ~(mask));
353 : 50 : conn->mark = pkt->md.ct_mark;
354 : 50 : }
355 : :
356 : : static void
357 : 44 : set_label(struct dp_packet *pkt, struct conn *conn,
358 : : const struct ovs_key_ct_labels *val,
359 : : const struct ovs_key_ct_labels *mask)
360 : : {
361 : : ovs_u128 v, m;
362 : :
363 : 44 : memcpy(&v, val, sizeof v);
364 : 44 : memcpy(&m, mask, sizeof m);
365 : :
366 : 88 : pkt->md.ct_label.u64.lo = v.u64.lo
367 : 44 : | (pkt->md.ct_label.u64.lo & ~(m.u64.lo));
368 : 88 : pkt->md.ct_label.u64.hi = v.u64.hi
369 : 44 : | (pkt->md.ct_label.u64.hi & ~(m.u64.hi));
370 : 44 : conn->label = pkt->md.ct_label;
371 : 44 : }
372 : :
373 : : /* Delete the expired connections from 'ctb', up to 'limit'. Returns the
374 : : * earliest expiration time among the remaining connections in 'ctb'. Returns
375 : : * LLONG_MAX if 'ctb' is empty. The return value might be smaller than 'now',
376 : : * if 'limit' is reached */
377 : : static long long
378 : 16896 : sweep_bucket(struct conntrack *ct, struct conntrack_bucket *ctb, long long now,
379 : : size_t limit)
380 : : OVS_REQUIRES(ctb->lock)
381 : : {
382 : : struct conn *conn, *next;
383 : 16896 : long long min_expiration = LLONG_MAX;
384 : : unsigned i;
385 : 16896 : size_t count = 0;
386 : :
387 [ + + ]: 202752 : for (i = 0; i < N_CT_TM; i++) {
388 [ + + ][ + + ]: 185857 : LIST_FOR_EACH_SAFE (conn, next, exp_node, &ctb->exp_lists[i]) {
389 [ + - ][ - + ]: 1 : if (!conn_expired(conn, now) || count >= limit) {
390 : 0 : min_expiration = MIN(min_expiration, conn->expiration);
391 [ # # ]: 0 : if (count >= limit) {
392 : : /* Do not check other lists. */
393 : 0 : COVERAGE_INC(conntrack_long_cleanup);
394 : 0 : return min_expiration;
395 : : }
396 : 0 : break;
397 : : }
398 : 1 : ovs_list_remove(&conn->exp_node);
399 : 1 : hmap_remove(&ctb->connections, &conn->node);
400 : 1 : atomic_count_dec(&ct->n_conn);
401 : 1 : delete_conn(conn);
402 : 1 : count++;
403 : : }
404 : : }
405 : :
406 : 16896 : return min_expiration;
407 : : }
408 : :
409 : : /* Cleans up old connection entries from 'ct'. Returns the time when the
410 : : * next expiration might happen. The return value might be smaller than
411 : : * 'now', meaning that an internal limit has been reached, and some expired
412 : : * connections have not been deleted. */
413 : : static long long
414 : 5853 : conntrack_clean(struct conntrack *ct, long long now)
415 : : {
416 : 5853 : long long next_wakeup = now + CT_TM_MIN;
417 : : unsigned int n_conn_limit;
418 : 5853 : size_t clean_count = 0;
419 : : unsigned i;
420 : :
421 : 5853 : atomic_read_relaxed(&ct->n_conn_limit, &n_conn_limit);
422 : :
423 [ + + ]: 1504221 : for (i = 0; i < CONNTRACK_BUCKETS; i++) {
424 : 1498368 : struct conntrack_bucket *ctb = &ct->buckets[i];
425 : : size_t prev_count;
426 : : long long min_exp;
427 : :
428 : 1498368 : ovs_mutex_lock(&ctb->cleanup_mutex);
429 [ + + ]: 1498368 : if (ctb->next_cleanup > now) {
430 : 1481472 : goto next_bucket;
431 : : }
432 : :
433 : 16896 : ct_lock_lock(&ctb->lock);
434 : 16896 : prev_count = hmap_count(&ctb->connections);
435 : : /* If the connections are well distributed among buckets, we want to
436 : : * limit to 10% of the global limit equally split among buckets. If
437 : : * the bucket is busier than the others, we limit to 10% of its
438 : : * current size. */
439 : 16896 : min_exp = sweep_bucket(ct, ctb, now,
440 : 33792 : MAX(prev_count/10, n_conn_limit/(CONNTRACK_BUCKETS*10)));
441 : 16896 : clean_count += prev_count - hmap_count(&ctb->connections);
442 : :
443 [ + - ]: 16896 : if (min_exp > now) {
444 : : /* We call hmap_shrink() only if sweep_bucket() managed to delete
445 : : * every expired connection. */
446 : 16896 : hmap_shrink(&ctb->connections);
447 : : }
448 : :
449 : 16896 : ct_lock_unlock(&ctb->lock);
450 : :
451 : 16896 : ctb->next_cleanup = MIN(min_exp, now + CT_TM_MIN);
452 : :
453 : : next_bucket:
454 : 1498368 : next_wakeup = MIN(next_wakeup, ctb->next_cleanup);
455 : 1498368 : ovs_mutex_unlock(&ctb->cleanup_mutex);
456 : : }
457 : :
458 [ - + ]: 5853 : VLOG_DBG("conntrack cleanup %"PRIuSIZE" entries in %lld msec",
459 : : clean_count, time_msec() - now);
460 : :
461 : 5853 : return next_wakeup;
462 : : }
463 : :
464 : : /* Cleanup:
465 : : *
466 : : * We must call conntrack_clean() periodically. conntrack_clean() return
467 : : * value gives an hint on when the next cleanup must be done (either because
468 : : * there is an actual connection that expires, or because a new connection
469 : : * might be created with the minimum timeout).
470 : : *
471 : : * The logic below has two goals:
472 : : *
473 : : * - We want to reduce the number of wakeups and batch connection cleanup
474 : : * when the load is not very high. CT_CLEAN_INTERVAL ensures that if we
475 : : * are coping with the current cleanup tasks, then we wait at least
476 : : * 5 seconds to do further cleanup.
477 : : *
478 : : * - We don't want to keep the buckets locked too long, as we might prevent
479 : : * traffic from flowing. CT_CLEAN_MIN_INTERVAL ensures that if cleanup is
480 : : * behind, there is at least some 200ms blocks of time when buckets will be
481 : : * left alone, so the datapath can operate unhindered.
482 : : */
483 : : #define CT_CLEAN_INTERVAL 5000 /* 5 seconds */
484 : : #define CT_CLEAN_MIN_INTERVAL 200 /* 0.2 seconds */
485 : :
486 : : static void *
487 : 553 : clean_thread_main(void *f_)
488 : : {
489 : 553 : struct conntrack *ct = f_;
490 : :
491 [ + + ]: 5855 : while (!latch_is_set(&ct->clean_thread_exit)) {
492 : : long long next_wake;
493 : 5853 : long long now = time_msec();
494 : :
495 : 5853 : next_wake = conntrack_clean(ct, now);
496 : :
497 [ - + ]: 5853 : if (next_wake < now) {
498 : 0 : poll_timer_wait_until(now + CT_CLEAN_MIN_INTERVAL);
499 : : } else {
500 : 5853 : poll_timer_wait_until(MAX(next_wake, now + CT_CLEAN_INTERVAL));
501 : : }
502 : 5853 : latch_wait(&ct->clean_thread_exit);
503 : 5853 : poll_block();
504 : : }
505 : :
506 : 2 : return NULL;
507 : : }
508 : :
509 : : /* Key extraction */
510 : :
511 : : /* The function stores a pointer to the first byte after the header in
512 : : * '*new_data', if 'new_data' is not NULL. If it is NULL, the caller is
513 : : * not interested in the header's tail, meaning that the header has
514 : : * already been parsed (e.g. by flow_extract): we take this as a hint to
515 : : * save a few checks. If 'validate_checksum' is true, the function returns
516 : : * false if the IPv4 checksum is invalid. */
517 : : static inline bool
518 : 582 : extract_l3_ipv4(struct conn_key *key, const void *data, size_t size,
519 : : const char **new_data, bool validate_checksum)
520 : : {
521 : 582 : const struct ip_header *ip = data;
522 : : size_t ip_len;
523 : :
524 [ + + ]: 582 : if (new_data) {
525 [ - + ]: 4 : if (OVS_UNLIKELY(size < IP_HEADER_LEN)) {
526 : 0 : return false;
527 : : }
528 : : }
529 : :
530 : 582 : ip_len = IP_IHL(ip->ip_ihl_ver) * 4;
531 : :
532 [ + + ]: 582 : if (new_data) {
533 [ - + ]: 4 : if (OVS_UNLIKELY(ip_len < IP_HEADER_LEN)) {
534 : 0 : return false;
535 : : }
536 [ - + ]: 4 : if (OVS_UNLIKELY(size < ip_len)) {
537 : 0 : return false;
538 : : }
539 : :
540 : 4 : *new_data = (char *) data + ip_len;
541 : : }
542 : :
543 [ - + ]: 582 : if (IP_IS_FRAGMENT(ip->ip_frag_off)) {
544 : 0 : return false;
545 : : }
546 : :
547 [ + + ][ + + ]: 582 : if (validate_checksum && csum(data, ip_len) != 0) {
548 : 1 : return false;
549 : : }
550 : :
551 : 581 : key->src.addr.ipv4 = ip->ip_src;
552 : 581 : key->dst.addr.ipv4 = ip->ip_dst;
553 : 581 : key->nw_proto = ip->ip_proto;
554 : :
555 : 581 : return true;
556 : : }
557 : :
558 : : /* The function stores a pointer to the first byte after the header in
559 : : * '*new_data', if 'new_data' is not NULL. If it is NULL, the caller is
560 : : * not interested in the header's tail, meaning that the header has
561 : : * already been parsed (e.g. by flow_extract): we take this as a hint to
562 : : * save a few checks. */
563 : : static inline bool
564 : 45 : extract_l3_ipv6(struct conn_key *key, const void *data, size_t size,
565 : : const char **new_data)
566 : : {
567 : 45 : const struct ovs_16aligned_ip6_hdr *ip6 = data;
568 : 45 : uint8_t nw_proto = ip6->ip6_nxt;
569 : 45 : uint8_t nw_frag = 0;
570 : :
571 [ - + ]: 45 : if (new_data) {
572 [ # # ]: 0 : if (OVS_UNLIKELY(size < sizeof *ip6)) {
573 : 0 : return false;
574 : : }
575 : : }
576 : :
577 : 45 : data = ip6 + 1;
578 : 45 : size -= sizeof *ip6;
579 : :
580 [ - + ]: 45 : if (!parse_ipv6_ext_hdrs(&data, &size, &nw_proto, &nw_frag)) {
581 : 0 : return false;
582 : : }
583 : :
584 [ - + ]: 45 : if (new_data) {
585 : 0 : *new_data = data;
586 : : }
587 : :
588 [ - + ]: 45 : if (nw_frag) {
589 : 0 : return false;
590 : : }
591 : :
592 : 45 : key->src.addr.ipv6 = ip6->ip6_src;
593 : 45 : key->dst.addr.ipv6 = ip6->ip6_dst;
594 : 45 : key->nw_proto = nw_proto;
595 : :
596 : 45 : return true;
597 : : }
598 : :
599 : : static inline bool
600 : 600 : checksum_valid(const struct conn_key *key, const void *data, size_t size,
601 : : const void *l3)
602 : : {
603 : 600 : uint32_t csum = 0;
604 : :
605 [ + + ]: 600 : if (key->dl_type == htons(ETH_TYPE_IP)) {
606 : 555 : csum = packet_csum_pseudoheader(l3);
607 [ + - ]: 45 : } else if (key->dl_type == htons(ETH_TYPE_IPV6)) {
608 : 45 : csum = packet_csum_pseudoheader6(l3);
609 : : } else {
610 : 0 : return false;
611 : : }
612 : :
613 : 600 : csum = csum_continue(csum, data, size);
614 : :
615 : 600 : return csum_finish(csum) == 0;
616 : : }
617 : :
618 : : static inline bool
619 : 543 : check_l4_tcp(const struct conn_key *key, const void *data, size_t size,
620 : : const void *l3)
621 : : {
622 : 543 : const struct tcp_header *tcp = data;
623 : 543 : size_t tcp_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
624 : :
625 [ + - ][ - + ]: 543 : if (OVS_UNLIKELY(tcp_len < TCP_HEADER_LEN || tcp_len > size)) {
626 : 0 : return false;
627 : : }
628 : :
629 : 543 : return checksum_valid(key, data, size, l3);
630 : : }
631 : :
632 : : static inline bool
633 : 45 : check_l4_udp(const struct conn_key *key, const void *data, size_t size,
634 : : const void *l3)
635 : : {
636 : 45 : const struct udp_header *udp = data;
637 : 45 : size_t udp_len = ntohs(udp->udp_len);
638 : :
639 [ + - ][ - + ]: 45 : if (OVS_UNLIKELY(udp_len < UDP_HEADER_LEN || udp_len > size)) {
640 : 0 : return false;
641 : : }
642 : :
643 : : /* Validation must be skipped if checksum is 0 on IPv4 packets */
644 [ - + ]: 48 : return (udp->udp_csum == 0 && key->dl_type == htons(ETH_TYPE_IP))
645 [ + + ][ + - ]: 48 : || checksum_valid(key, data, size, l3);
646 : : }
647 : :
648 : : static inline bool
649 : 19 : check_l4_icmp(const void *data, size_t size)
650 : : {
651 : 19 : return csum(data, size) == 0;
652 : : }
653 : :
654 : : static inline bool
655 : 15 : check_l4_icmp6(const struct conn_key *key, const void *data, size_t size,
656 : : const void *l3)
657 : : {
658 : 15 : return checksum_valid(key, data, size, l3);
659 : : }
660 : :
661 : : static inline bool
662 : 543 : extract_l4_tcp(struct conn_key *key, const void *data, size_t size)
663 : : {
664 : 543 : const struct tcp_header *tcp = data;
665 : :
666 [ - + ]: 543 : if (OVS_UNLIKELY(size < TCP_HEADER_LEN)) {
667 : 0 : return false;
668 : : }
669 : :
670 : 543 : key->src.port = tcp->tcp_src;
671 : 543 : key->dst.port = tcp->tcp_dst;
672 : :
673 : : /* Port 0 is invalid */
674 [ + - ][ + - ]: 543 : return key->src.port && key->dst.port;
675 : : }
676 : :
677 : : static inline bool
678 : 49 : extract_l4_udp(struct conn_key *key, const void *data, size_t size)
679 : : {
680 : 49 : const struct udp_header *udp = data;
681 : :
682 [ - + ]: 49 : if (OVS_UNLIKELY(size < UDP_HEADER_LEN)) {
683 : 0 : return false;
684 : : }
685 : :
686 : 49 : key->src.port = udp->udp_src;
687 : 49 : key->dst.port = udp->udp_dst;
688 : :
689 : : /* Port 0 is invalid */
690 [ + - ][ + - ]: 49 : return key->src.port && key->dst.port;
691 : : }
692 : :
693 : : static inline bool extract_l4(struct conn_key *key, const void *data,
694 : : size_t size, bool *related, const void *l3);
695 : :
696 : : static uint8_t
697 : 15 : reverse_icmp_type(uint8_t type)
698 : : {
699 [ + + - - : 15 : switch (type) {
- - - ]
700 : : case ICMP4_ECHO_REQUEST:
701 : 12 : return ICMP4_ECHO_REPLY;
702 : : case ICMP4_ECHO_REPLY:
703 : 3 : return ICMP4_ECHO_REQUEST;
704 : :
705 : : case ICMP4_TIMESTAMP:
706 : 0 : return ICMP4_TIMESTAMPREPLY;
707 : : case ICMP4_TIMESTAMPREPLY:
708 : 0 : return ICMP4_TIMESTAMP;
709 : :
710 : : case ICMP4_INFOREQUEST:
711 : 0 : return ICMP4_INFOREPLY;
712 : : case ICMP4_INFOREPLY:
713 : 0 : return ICMP4_INFOREQUEST;
714 : : default:
715 : 0 : OVS_NOT_REACHED();
716 : : }
717 : : }
718 : :
719 : : /* If 'related' is not NULL and the function is processing an ICMP
720 : : * error packet, extract the l3 and l4 fields from the nested header
721 : : * instead and set *related to true. If 'related' is NULL we're
722 : : * already processing a nested header and no such recursion is
723 : : * possible */
724 : : static inline int
725 : 19 : extract_l4_icmp(struct conn_key *key, const void *data, size_t size,
726 : : bool *related)
727 : : {
728 : 19 : const struct icmp_header *icmp = data;
729 : :
730 [ - + ]: 19 : if (OVS_UNLIKELY(size < ICMP_HEADER_LEN)) {
731 : 0 : return false;
732 : : }
733 : :
734 [ + + - ]: 19 : switch (icmp->icmp_type) {
735 : : case ICMP4_ECHO_REQUEST:
736 : : case ICMP4_ECHO_REPLY:
737 : : case ICMP4_TIMESTAMP:
738 : : case ICMP4_TIMESTAMPREPLY:
739 : : case ICMP4_INFOREQUEST:
740 : : case ICMP4_INFOREPLY:
741 [ - + ]: 15 : if (icmp->icmp_code != 0) {
742 : 0 : return false;
743 : : }
744 : : /* Separate ICMP connection: identified using id */
745 : 15 : key->src.icmp_id = key->dst.icmp_id = icmp->icmp_fields.echo.id;
746 : 15 : key->src.icmp_type = icmp->icmp_type;
747 : 15 : key->dst.icmp_type = reverse_icmp_type(icmp->icmp_type);
748 : 15 : break;
749 : : case ICMP4_DST_UNREACH:
750 : : case ICMP4_TIME_EXCEEDED:
751 : : case ICMP4_PARAM_PROB:
752 : : case ICMP4_SOURCEQUENCH:
753 : : case ICMP4_REDIRECT: {
754 : : /* ICMP packet part of another connection. We should
755 : : * extract the key from embedded packet header */
756 : : struct conn_key inner_key;
757 : 4 : const char *l3 = (const char *) (icmp + 1);
758 : 4 : const char *tail = (const char *) data + size;
759 : : const char *l4;
760 : : bool ok;
761 : :
762 [ - + ]: 4 : if (!related) {
763 : 0 : return false;
764 : : }
765 : :
766 : 4 : memset(&inner_key, 0, sizeof inner_key);
767 : 4 : inner_key.dl_type = htons(ETH_TYPE_IP);
768 : 4 : ok = extract_l3_ipv4(&inner_key, l3, tail - l3, &l4, false);
769 [ - + ]: 4 : if (!ok) {
770 : 0 : return false;
771 : : }
772 : :
773 : : /* pf doesn't do this, but it seems a good idea */
774 [ + - ]: 4 : if (inner_key.src.addr.ipv4_aligned != key->dst.addr.ipv4_aligned
775 [ - + ]: 4 : || inner_key.dst.addr.ipv4_aligned != key->src.addr.ipv4_aligned) {
776 : 0 : return false;
777 : : }
778 : :
779 : 4 : key->src = inner_key.src;
780 : 4 : key->dst = inner_key.dst;
781 : 4 : key->nw_proto = inner_key.nw_proto;
782 : :
783 : 4 : ok = extract_l4(key, l4, tail - l4, NULL, l3);
784 [ + - ]: 4 : if (ok) {
785 : 4 : conn_key_reverse(key);
786 : 4 : *related = true;
787 : : }
788 : 4 : return ok;
789 : : }
790 : : default:
791 : 0 : return false;
792 : : }
793 : :
794 : 15 : return true;
795 : : }
796 : :
797 : : static uint8_t
798 : 15 : reverse_icmp6_type(uint8_t type)
799 : : {
800 [ + + - ]: 15 : switch (type) {
801 : : case ICMP6_ECHO_REQUEST:
802 : 11 : return ICMP6_ECHO_REPLY;
803 : : case ICMP6_ECHO_REPLY:
804 : 4 : return ICMP6_ECHO_REQUEST;
805 : : default:
806 : 0 : OVS_NOT_REACHED();
807 : : }
808 : : }
809 : :
810 : : /* If 'related' is not NULL and the function is processing an ICMP
811 : : * error packet, extract the l3 and l4 fields from the nested header
812 : : * instead and set *related to true. If 'related' is NULL we're
813 : : * already processing a nested header and no such recursion is
814 : : * possible */
815 : : static inline bool
816 : 15 : extract_l4_icmp6(struct conn_key *key, const void *data, size_t size,
817 : : bool *related)
818 : : {
819 : 15 : const struct icmp6_header *icmp6 = data;
820 : :
821 : : /* All the messages that we support need at least 4 bytes after
822 : : * the header */
823 [ - + ]: 15 : if (size < sizeof *icmp6 + 4) {
824 : 0 : return false;
825 : : }
826 : :
827 [ + - - ]: 15 : switch (icmp6->icmp6_type) {
828 : : case ICMP6_ECHO_REQUEST:
829 : : case ICMP6_ECHO_REPLY:
830 [ - + ]: 15 : if (icmp6->icmp6_code != 0) {
831 : 0 : return false;
832 : : }
833 : : /* Separate ICMP connection: identified using id */
834 : 15 : key->src.icmp_id = key->dst.icmp_id = *(ovs_be16 *) (icmp6 + 1);
835 : 15 : key->src.icmp_type = icmp6->icmp6_type;
836 : 15 : key->dst.icmp_type = reverse_icmp6_type(icmp6->icmp6_type);
837 : 15 : break;
838 : : case ICMP6_DST_UNREACH:
839 : : case ICMP6_PACKET_TOO_BIG:
840 : : case ICMP6_TIME_EXCEEDED:
841 : : case ICMP6_PARAM_PROB: {
842 : : /* ICMP packet part of another connection. We should
843 : : * extract the key from embedded packet header */
844 : : struct conn_key inner_key;
845 : 0 : const char *l3 = (const char *) icmp6 + 8;
846 : 0 : const char *tail = (const char *) data + size;
847 : 0 : const char *l4 = NULL;
848 : : bool ok;
849 : :
850 [ # # ]: 0 : if (!related) {
851 : 0 : return false;
852 : : }
853 : :
854 : 0 : memset(&inner_key, 0, sizeof inner_key);
855 : 0 : inner_key.dl_type = htons(ETH_TYPE_IPV6);
856 : 0 : ok = extract_l3_ipv6(&inner_key, l3, tail - l3, &l4);
857 [ # # ]: 0 : if (!ok) {
858 : 0 : return false;
859 : : }
860 : :
861 : : /* pf doesn't do this, but it seems a good idea */
862 [ # # ]: 0 : if (!ipv6_addr_equals(&inner_key.src.addr.ipv6_aligned,
863 : 0 : &key->dst.addr.ipv6_aligned)
864 [ # # ]: 0 : || !ipv6_addr_equals(&inner_key.dst.addr.ipv6_aligned,
865 : 0 : &key->src.addr.ipv6_aligned)) {
866 : 0 : return false;
867 : : }
868 : :
869 : 0 : key->src = inner_key.src;
870 : 0 : key->dst = inner_key.dst;
871 : 0 : key->nw_proto = inner_key.nw_proto;
872 : :
873 : 0 : ok = extract_l4(key, l4, tail - l4, NULL, l3);
874 [ # # ]: 0 : if (ok) {
875 : 0 : conn_key_reverse(key);
876 : 0 : *related = true;
877 : : }
878 : 0 : return ok;
879 : : }
880 : : default:
881 : 0 : return false;
882 : : }
883 : :
884 : 15 : return true;
885 : : }
886 : :
887 : : /* Extract l4 fields into 'key', which must already contain valid l3
888 : : * members.
889 : : *
890 : : * If 'related' is not NULL and an ICMP error packet is being
891 : : * processed, the function will extract the key from the packet nested
892 : : * in the ICMP paylod and set '*related' to true.
893 : : *
894 : : * If 'related' is NULL, it means that we're already parsing a header nested
895 : : * in an ICMP error. In this case, we skip checksum and length validation. */
896 : : static inline bool
897 : 626 : extract_l4(struct conn_key *key, const void *data, size_t size, bool *related,
898 : : const void *l3)
899 : : {
900 [ + + ]: 626 : if (key->nw_proto == IPPROTO_TCP) {
901 [ + - ]: 1086 : return (!related || check_l4_tcp(key, data, size, l3))
902 [ + - ][ + - ]: 1086 : && extract_l4_tcp(key, data, size);
903 [ + + ]: 83 : } else if (key->nw_proto == IPPROTO_UDP) {
904 [ + - ]: 94 : return (!related || check_l4_udp(key, data, size, l3))
905 [ + + ][ + - ]: 94 : && extract_l4_udp(key, data, size);
906 [ + + ]: 34 : } else if (key->dl_type == htons(ETH_TYPE_IP)
907 [ + - ]: 19 : && key->nw_proto == IPPROTO_ICMP) {
908 [ + - ]: 38 : return (!related || check_l4_icmp(data, size))
909 [ + - ][ + - ]: 38 : && extract_l4_icmp(key, data, size, related);
910 [ + - ]: 15 : } else if (key->dl_type == htons(ETH_TYPE_IPV6)
911 [ + - ]: 15 : && key->nw_proto == IPPROTO_ICMPV6) {
912 [ + - ]: 30 : return (!related || check_l4_icmp6(key, data, size, l3))
913 [ + - ][ + - ]: 30 : && extract_l4_icmp6(key, data, size, related);
914 : : } else {
915 : 0 : return false;
916 : : }
917 : : }
918 : :
919 : : static bool
920 : 624 : conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type,
921 : : struct conn_lookup_ctx *ctx, uint16_t zone)
922 : : {
923 : 624 : const struct eth_header *l2 = dp_packet_l2(pkt);
924 : 624 : const struct ip_header *l3 = dp_packet_l3(pkt);
925 : 624 : const char *l4 = dp_packet_l4(pkt);
926 : 624 : const char *tail = dp_packet_tail(pkt);
927 : : bool ok;
928 : :
929 : 624 : memset(ctx, 0, sizeof *ctx);
930 : :
931 [ + - ][ + - ]: 624 : if (!l2 || !l3 || !l4) {
[ + + ]
932 : 1 : return false;
933 : : }
934 : :
935 : 623 : ctx->key.zone = zone;
936 : :
937 : : /* XXX In this function we parse the packet (again, it has already
938 : : * gone through miniflow_extract()) for two reasons:
939 : : *
940 : : * 1) To extract the l3 addresses and l4 ports.
941 : : * We already have the l3 and l4 headers' pointers. Extracting
942 : : * the l3 addresses and the l4 ports is really cheap, since they
943 : : * can be found at fixed locations.
944 : : * 2) To extract the l4 type.
945 : : * Extracting the l4 types, for IPv6 can be quite expensive, because
946 : : * it's not at a fixed location.
947 : : *
948 : : * Here's a way to avoid (2) with the help of the datapath.
949 : : * The datapath doesn't keep the packet's extracted flow[1], so
950 : : * using that is not an option. We could use the packet's matching
951 : : * megaflow, but we have to make sure that the l4 type (nw_proto)
952 : : * is unwildcarded. This means either:
953 : : *
954 : : * a) dpif-netdev unwildcards the l4 type when a new flow is installed
955 : : * if the actions contains ct().
956 : : *
957 : : * b) ofproto-dpif-xlate unwildcards the l4 type when translating a ct()
958 : : * action. This is already done in different actions, but it's
959 : : * unnecessary for the kernel.
960 : : *
961 : : * ---
962 : : * [1] The reasons for this are that keeping the flow increases
963 : : * (slightly) the cache footprint and increases computation
964 : : * time as we move the packet around. Most importantly, the flow
965 : : * should be updated by the actions and this can be slow, as
966 : : * we use a sparse representation (miniflow).
967 : : *
968 : : */
969 : 623 : ctx->key.dl_type = dl_type;
970 [ + + ]: 623 : if (ctx->key.dl_type == htons(ETH_TYPE_IP)) {
971 : 578 : ok = extract_l3_ipv4(&ctx->key, l3, tail - (char *) l3, NULL, true);
972 [ + - ]: 45 : } else if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
973 : 45 : ok = extract_l3_ipv6(&ctx->key, l3, tail - (char *) l3, NULL);
974 : : } else {
975 : 0 : ok = false;
976 : : }
977 : :
978 [ + + ]: 623 : if (ok) {
979 [ + - ]: 622 : if (extract_l4(&ctx->key, l4, tail - l4, &ctx->related, l3)) {
980 : 622 : ctx->hash = conn_key_hash(&ctx->key, ct->hash_basis);
981 : 622 : return true;
982 : : }
983 : : }
984 : :
985 : 1 : return false;
986 : : }
987 : :
988 : : /* Symmetric */
989 : : static uint32_t
990 : 622 : conn_key_hash(const struct conn_key *key, uint32_t basis)
991 : : {
992 : : uint32_t hsrc, hdst, hash;
993 : : int i;
994 : :
995 : 622 : hsrc = hdst = basis;
996 : :
997 : : /* Hash the source and destination tuple */
998 [ + + ]: 3732 : for (i = 0; i < sizeof(key->src) / sizeof(uint32_t); i++) {
999 : 3110 : hsrc = hash_add(hsrc, ((uint32_t *) &key->src)[i]);
1000 : 3110 : hdst = hash_add(hdst, ((uint32_t *) &key->dst)[i]);
1001 : : }
1002 : :
1003 : : /* Even if source and destination are swapped the hash will be the same. */
1004 : 622 : hash = hsrc ^ hdst;
1005 : :
1006 : : /* Hash the rest of the key(L3 and L4 types and zone). */
1007 : 622 : hash = hash_words((uint32_t *) &key->dst + 1,
1008 : : (uint32_t *) (key + 1) - (uint32_t *) (&key->dst + 1),
1009 : : hash);
1010 : :
1011 : 622 : return hash;
1012 : : }
1013 : :
1014 : : static void
1015 : 84 : conn_key_reverse(struct conn_key *key)
1016 : : {
1017 : : struct ct_endpoint tmp;
1018 : :
1019 : 84 : tmp = key->src;
1020 : 84 : key->src = key->dst;
1021 : 84 : key->dst = tmp;
1022 : 84 : }
1023 : :
1024 : : static void
1025 : 622 : conn_key_lookup(struct conntrack_bucket *ctb,
1026 : : struct conn_lookup_ctx *ctx,
1027 : : long long now)
1028 : : {
1029 : 622 : uint32_t hash = ctx->hash;
1030 : : struct conn *conn;
1031 : :
1032 : 622 : ctx->conn = NULL;
1033 : :
1034 [ + + ][ - + ]: 734 : HMAP_FOR_EACH_WITH_HASH (conn, node, hash, &ctb->connections) {
1035 [ + + ]: 589 : if (!memcmp(&conn->key, &ctx->key, sizeof(conn->key))
1036 [ + - ]: 210 : && !conn_expired(conn, now)) {
1037 : 210 : ctx->conn = conn;
1038 : 210 : ctx->reply = false;
1039 : 210 : break;
1040 : : }
1041 [ + + ]: 379 : if (!memcmp(&conn->rev_key, &ctx->key, sizeof(conn->rev_key))
1042 [ + - ]: 267 : && !conn_expired(conn, now)) {
1043 : 267 : ctx->conn = conn;
1044 : 267 : ctx->reply = true;
1045 : 267 : break;
1046 : : }
1047 : : }
1048 : 622 : }
1049 : :
1050 : : static enum ct_update_res
1051 : 474 : conn_update(struct conn *conn, struct conntrack_bucket *ctb,
1052 : : struct dp_packet *pkt, bool reply, long long now)
1053 : : {
1054 : 474 : return l4_protos[conn->key.nw_proto]->conn_update(conn, ctb, pkt,
1055 : : reply, now);
1056 : : }
1057 : :
1058 : : static bool
1059 : 478 : conn_expired(struct conn *conn, long long now)
1060 : : {
1061 : 478 : return now >= conn->expiration;
1062 : : }
1063 : :
1064 : : static bool
1065 : 146 : valid_new(struct dp_packet *pkt, struct conn_key *key)
1066 : : {
1067 : 146 : return l4_protos[key->nw_proto]->valid_new(pkt);
1068 : : }
1069 : :
1070 : : static struct conn *
1071 : 80 : new_conn(struct conntrack_bucket *ctb, struct dp_packet *pkt,
1072 : : struct conn_key *key, long long now)
1073 : : {
1074 : : struct conn *newconn;
1075 : :
1076 : 80 : newconn = l4_protos[key->nw_proto]->new_conn(ctb, pkt, now);
1077 : :
1078 [ + - ]: 80 : if (newconn) {
1079 : 80 : newconn->key = *key;
1080 : : }
1081 : :
1082 : 80 : return newconn;
1083 : : }
1084 : :
1085 : : static void
1086 : 4 : delete_conn(struct conn *conn)
1087 : : {
1088 : 4 : free(conn);
1089 : 4 : }
1090 : :
1091 : : static void
1092 : 192 : ct_endpoint_to_ct_dpif_inet_addr(const struct ct_addr *a,
1093 : : union ct_dpif_inet_addr *b,
1094 : : ovs_be16 dl_type)
1095 : : {
1096 [ + + ]: 192 : if (dl_type == htons(ETH_TYPE_IP)) {
1097 : 180 : b->ip = a->ipv4_aligned;
1098 [ + - ]: 12 : } else if (dl_type == htons(ETH_TYPE_IPV6)){
1099 : 12 : b->in6 = a->ipv6_aligned;
1100 : : }
1101 : 192 : }
1102 : :
1103 : : static void
1104 : 96 : conn_key_to_tuple(const struct conn_key *key, struct ct_dpif_tuple *tuple)
1105 : : {
1106 [ + + ]: 96 : if (key->dl_type == htons(ETH_TYPE_IP)) {
1107 : 90 : tuple->l3_type = AF_INET;
1108 [ + - ]: 6 : } else if (key->dl_type == htons(ETH_TYPE_IPV6)) {
1109 : 6 : tuple->l3_type = AF_INET6;
1110 : : }
1111 : 96 : tuple->ip_proto = key->nw_proto;
1112 : 96 : ct_endpoint_to_ct_dpif_inet_addr(&key->src.addr, &tuple->src,
1113 : 96 : key->dl_type);
1114 : 96 : ct_endpoint_to_ct_dpif_inet_addr(&key->dst.addr, &tuple->dst,
1115 : 96 : key->dl_type);
1116 : :
1117 [ + + ][ + + ]: 96 : if (key->nw_proto == IPPROTO_ICMP || key->nw_proto == IPPROTO_ICMPV6) {
1118 : 4 : tuple->icmp_id = key->src.icmp_id;
1119 : 4 : tuple->icmp_type = key->src.icmp_type;
1120 : 4 : tuple->icmp_code = key->src.icmp_code;
1121 : : } else {
1122 : 92 : tuple->src_port = key->src.port;
1123 : 92 : tuple->dst_port = key->dst.port;
1124 : : }
1125 : 96 : }
1126 : :
1127 : : static void
1128 : 48 : conn_to_ct_dpif_entry(const struct conn *conn, struct ct_dpif_entry *entry,
1129 : : long long now)
1130 : : {
1131 : : struct ct_l4_proto *class;
1132 : : long long expiration;
1133 : 48 : memset(entry, 0, sizeof *entry);
1134 : 48 : conn_key_to_tuple(&conn->key, &entry->tuple_orig);
1135 : 48 : conn_key_to_tuple(&conn->rev_key, &entry->tuple_reply);
1136 : :
1137 : 48 : entry->zone = conn->key.zone;
1138 : 48 : entry->mark = conn->mark;
1139 : :
1140 : 48 : memcpy(&entry->labels, &conn->label, sizeof(entry->labels));
1141 : : /* Not implemented yet */
1142 : 48 : entry->timestamp.start = 0;
1143 : 48 : entry->timestamp.stop = 0;
1144 : :
1145 : 48 : expiration = conn->expiration - now;
1146 [ + - ]: 48 : entry->timeout = (expiration > 0) ? expiration / 1000 : 0;
1147 : :
1148 : 48 : class = l4_protos[conn->key.nw_proto];
1149 [ + + ]: 48 : if (class->conn_get_protoinfo) {
1150 : 46 : class->conn_get_protoinfo(conn, &entry->protoinfo);
1151 : : }
1152 : 48 : }
1153 : :
1154 : : int
1155 : 16 : conntrack_dump_start(struct conntrack *ct, struct conntrack_dump *dump,
1156 : : const uint16_t *pzone)
1157 : : {
1158 : 16 : memset(dump, 0, sizeof(*dump));
1159 [ - + ]: 16 : if (pzone) {
1160 : 0 : dump->zone = *pzone;
1161 : 0 : dump->filter_zone = true;
1162 : : }
1163 : 16 : dump->ct = ct;
1164 : :
1165 : 16 : return 0;
1166 : : }
1167 : :
1168 : : int
1169 : 64 : conntrack_dump_next(struct conntrack_dump *dump, struct ct_dpif_entry *entry)
1170 : : {
1171 : 64 : struct conntrack *ct = dump->ct;
1172 : 64 : long long now = time_msec();
1173 : :
1174 [ + + ]: 4160 : while (dump->bucket < CONNTRACK_BUCKETS) {
1175 : : struct hmap_node *node;
1176 : :
1177 : 4144 : ct_lock_lock(&ct->buckets[dump->bucket].lock);
1178 : : for (;;) {
1179 : : struct conn *conn;
1180 : :
1181 : 4144 : node = hmap_at_position(&ct->buckets[dump->bucket].connections,
1182 : : &dump->bucket_pos);
1183 [ + + ]: 4144 : if (!node) {
1184 : 4096 : break;
1185 : : }
1186 : 48 : INIT_CONTAINER(conn, node, node);
1187 [ - + ][ # # ]: 48 : if (!dump->filter_zone || conn->key.zone == dump->zone) {
1188 : 48 : conn_to_ct_dpif_entry(conn, entry, now);
1189 : 48 : break;
1190 : : }
1191 : : /* Else continue, until we find an entry in the appropriate zone
1192 : : * or the bucket has been scanned completely. */
1193 : 0 : }
1194 : 4144 : ct_lock_unlock(&ct->buckets[dump->bucket].lock);
1195 : :
1196 [ + + ]: 4144 : if (!node) {
1197 : 4096 : memset(&dump->bucket_pos, 0, sizeof dump->bucket_pos);
1198 : 4096 : dump->bucket++;
1199 : : } else {
1200 : 48 : return 0;
1201 : : }
1202 : : }
1203 : 16 : return EOF;
1204 : : }
1205 : :
1206 : : int
1207 : 16 : conntrack_dump_done(struct conntrack_dump *dump OVS_UNUSED)
1208 : : {
1209 : 16 : return 0;
1210 : : }
1211 : :
1212 : : int
1213 : 2 : conntrack_flush(struct conntrack *ct, const uint16_t *zone)
1214 : : {
1215 : : unsigned i;
1216 : :
1217 [ + + ]: 514 : for (i = 0; i < CONNTRACK_BUCKETS; i++) {
1218 : : struct conn *conn, *next;
1219 : :
1220 : 512 : ct_lock_lock(&ct->buckets[i].lock);
1221 [ + + ][ - + ]: 514 : HMAP_FOR_EACH_SAFE(conn, next, node, &ct->buckets[i].connections) {
[ + + ]
1222 [ - + ][ # # ]: 2 : if (!zone || *zone == conn->key.zone) {
1223 : 2 : ovs_list_remove(&conn->exp_node);
1224 : 2 : hmap_remove(&ct->buckets[i].connections, &conn->node);
1225 : 2 : atomic_count_dec(&ct->n_conn);
1226 : 2 : delete_conn(conn);
1227 : : }
1228 : : }
1229 : 512 : ct_lock_unlock(&ct->buckets[i].lock);
1230 : : }
1231 : :
1232 : 2 : return 0;
1233 : : }
|