2015-11-15 22:07:25 -08:00
|
|
|
|
/*
|
2017-05-26 16:09:35 -07:00
|
|
|
|
* Copyright (c) 2015, 2017 Nicira, Inc.
|
2015-11-15 22:07:25 -08:00
|
|
|
|
*
|
|
|
|
|
* 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.h"
|
|
|
|
|
|
|
|
|
|
#include "dp-packet.h"
|
|
|
|
|
#include "fatal-signal.h"
|
|
|
|
|
#include "flow.h"
|
|
|
|
|
#include "netdev.h"
|
|
|
|
|
#include "ovs-thread.h"
|
|
|
|
|
#include "ovstest.h"
|
2016-04-14 17:00:35 -07:00
|
|
|
|
#include "pcap-file.h"
|
2015-11-15 22:07:25 -08:00
|
|
|
|
#include "timeval.h"
|
|
|
|
|
|
|
|
|
|
static const char payload[] = "50540000000a50540000000908004500001c0000000000"
|
|
|
|
|
"11a4cd0a0101010a0101020001000200080000";
|
|
|
|
|
|
|
|
|
|
static struct dp_packet_batch *
|
2016-05-25 18:10:09 -07:00
|
|
|
|
prepare_packets(size_t n, bool change, unsigned tid, ovs_be16 *dl_type)
|
2015-11-15 22:07:25 -08:00
|
|
|
|
{
|
|
|
|
|
struct dp_packet_batch *pkt_batch = xzalloc(sizeof *pkt_batch);
|
|
|
|
|
struct flow flow;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
ovs_assert(n <= ARRAY_SIZE(pkt_batch->packets));
|
|
|
|
|
|
|
|
|
|
dp_packet_batch_init(pkt_batch);
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
struct udp_header *udp;
|
|
|
|
|
struct dp_packet *pkt = dp_packet_new(sizeof payload/2);
|
|
|
|
|
|
|
|
|
|
dp_packet_put_hex(pkt, payload, NULL);
|
|
|
|
|
flow_extract(pkt, &flow);
|
|
|
|
|
|
|
|
|
|
udp = dp_packet_l4(pkt);
|
|
|
|
|
udp->udp_src = htons(ntohs(udp->udp_src) + tid);
|
|
|
|
|
|
|
|
|
|
if (change) {
|
|
|
|
|
udp->udp_dst = htons(ntohs(udp->udp_dst) + i);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 15:56:58 -08:00
|
|
|
|
dp_packet_batch_add(pkt_batch, pkt);
|
2016-05-25 18:10:09 -07:00
|
|
|
|
*dl_type = flow.dl_type;
|
2015-11-15 22:07:25 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pkt_batch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
destroy_packets(struct dp_packet_batch *pkt_batch)
|
|
|
|
|
{
|
|
|
|
|
dp_packet_delete_batch(pkt_batch, true);
|
|
|
|
|
free(pkt_batch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct thread_aux {
|
|
|
|
|
pthread_t thread;
|
|
|
|
|
unsigned tid;
|
|
|
|
|
};
|
|
|
|
|
|
2019-05-02 21:34:04 -07:00
|
|
|
|
static struct conntrack *ct;
|
2015-11-15 22:07:25 -08:00
|
|
|
|
static unsigned long n_threads, n_pkts, batch_size;
|
|
|
|
|
static bool change_conn = false;
|
|
|
|
|
static struct ovs_barrier barrier;
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
ct_thread_main(void *aux_)
|
|
|
|
|
{
|
|
|
|
|
struct thread_aux *aux = aux_;
|
|
|
|
|
struct dp_packet_batch *pkt_batch;
|
2020-08-18 16:13:29 +02:00
|
|
|
|
struct dp_packet *pkt;
|
2016-05-25 18:10:09 -07:00
|
|
|
|
ovs_be16 dl_type;
|
2015-11-15 22:07:25 -08:00
|
|
|
|
size_t i;
|
2017-08-24 22:16:53 -07:00
|
|
|
|
long long now = time_msec();
|
2015-11-15 22:07:25 -08:00
|
|
|
|
|
2016-05-25 18:10:09 -07:00
|
|
|
|
pkt_batch = prepare_packets(batch_size, change_conn, aux->tid, &dl_type);
|
2015-11-15 22:07:25 -08:00
|
|
|
|
ovs_barrier_block(&barrier);
|
|
|
|
|
for (i = 0; i < n_pkts; i += batch_size) {
|
2019-05-02 21:34:04 -07:00
|
|
|
|
conntrack_execute(ct, pkt_batch, dl_type, false, true, 0, NULL, NULL,
|
2023-12-11 12:51:01 +02:00
|
|
|
|
NULL, NULL, now, 0);
|
2020-08-18 16:13:29 +02:00
|
|
|
|
DP_PACKET_BATCH_FOR_EACH (j, pkt, pkt_batch) {
|
|
|
|
|
pkt_metadata_init_conn(&pkt->md);
|
|
|
|
|
}
|
2015-11-15 22:07:25 -08:00
|
|
|
|
}
|
|
|
|
|
ovs_barrier_block(&barrier);
|
|
|
|
|
destroy_packets(pkt_batch);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_benchmark(struct ovs_cmdl_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct thread_aux *threads;
|
|
|
|
|
long long start;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
fatal_signal_init();
|
|
|
|
|
|
|
|
|
|
/* Parse arguments */
|
|
|
|
|
n_threads = strtoul(ctx->argv[1], NULL, 0);
|
|
|
|
|
if (!n_threads) {
|
|
|
|
|
ovs_fatal(0, "n_threads must be at least one");
|
|
|
|
|
}
|
|
|
|
|
n_pkts = strtoul(ctx->argv[2], NULL, 0);
|
|
|
|
|
batch_size = strtoul(ctx->argv[3], NULL, 0);
|
|
|
|
|
if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
|
|
|
|
|
ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
|
|
|
|
|
NETDEV_MAX_BURST);
|
|
|
|
|
}
|
|
|
|
|
if (ctx->argc > 4) {
|
|
|
|
|
change_conn = strtoul(ctx->argv[4], NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
threads = xcalloc(n_threads, sizeof *threads);
|
|
|
|
|
ovs_barrier_init(&barrier, n_threads + 1);
|
2019-05-02 21:34:04 -07:00
|
|
|
|
ct = conntrack_init();
|
2015-11-15 22:07:25 -08:00
|
|
|
|
|
|
|
|
|
/* Create threads */
|
|
|
|
|
for (i = 0; i < n_threads; i++) {
|
|
|
|
|
threads[i].tid = i;
|
|
|
|
|
threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main,
|
|
|
|
|
&threads[i]);
|
|
|
|
|
}
|
|
|
|
|
/* Starts the work inside the threads */
|
|
|
|
|
ovs_barrier_block(&barrier);
|
|
|
|
|
start = time_msec();
|
|
|
|
|
|
|
|
|
|
/* Wait for the threads to finish the work */
|
|
|
|
|
ovs_barrier_block(&barrier);
|
|
|
|
|
printf("conntrack: %5lld ms\n", time_msec() - start);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_threads; i++) {
|
|
|
|
|
xpthread_join(threads[i].thread, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 21:34:04 -07:00
|
|
|
|
conntrack_destroy(ct);
|
2015-11-15 22:07:25 -08:00
|
|
|
|
ovs_barrier_destroy(&barrier);
|
|
|
|
|
free(threads);
|
|
|
|
|
}
|
2016-04-14 17:00:35 -07:00
|
|
|
|
|
2016-05-25 18:10:09 -07:00
|
|
|
|
static void
|
2018-02-27 17:34:14 -08:00
|
|
|
|
pcap_batch_execute_conntrack(struct conntrack *ct_,
|
2016-05-25 18:10:09 -07:00
|
|
|
|
struct dp_packet_batch *pkt_batch)
|
|
|
|
|
{
|
|
|
|
|
struct dp_packet_batch new_batch;
|
|
|
|
|
ovs_be16 dl_type = htons(0);
|
2017-08-24 22:16:53 -07:00
|
|
|
|
long long now = time_msec();
|
2016-05-25 18:10:09 -07:00
|
|
|
|
|
|
|
|
|
dp_packet_batch_init(&new_batch);
|
|
|
|
|
|
|
|
|
|
/* pkt_batch contains packets with different 'dl_type'. We have to
|
|
|
|
|
* call conntrack_execute() on packets with the same 'dl_type'. */
|
2017-01-17 15:56:58 -08:00
|
|
|
|
struct dp_packet *packet;
|
2018-02-27 10:41:30 -08:00
|
|
|
|
DP_PACKET_BATCH_FOR_EACH (i, packet, pkt_batch) {
|
2016-05-25 18:10:09 -07:00
|
|
|
|
struct flow flow;
|
|
|
|
|
|
|
|
|
|
/* This also initializes the l3 and l4 pointers. */
|
2017-01-17 15:56:58 -08:00
|
|
|
|
flow_extract(packet, &flow);
|
2016-05-25 18:10:09 -07:00
|
|
|
|
|
2017-01-17 15:56:58 -08:00
|
|
|
|
if (dp_packet_batch_is_empty(&new_batch)) {
|
2016-05-25 18:10:09 -07:00
|
|
|
|
dl_type = flow.dl_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flow.dl_type != dl_type) {
|
2018-02-27 17:34:14 -08:00
|
|
|
|
conntrack_execute(ct_, &new_batch, dl_type, false, true, 0,
|
2023-12-11 12:51:01 +02:00
|
|
|
|
NULL, NULL, NULL, NULL, now, 0);
|
2016-05-25 18:10:09 -07:00
|
|
|
|
dp_packet_batch_init(&new_batch);
|
|
|
|
|
}
|
2018-12-10 20:17:53 +03:00
|
|
|
|
dp_packet_batch_add(&new_batch, packet);
|
2016-05-25 18:10:09 -07:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 15:56:58 -08:00
|
|
|
|
if (!dp_packet_batch_is_empty(&new_batch)) {
|
2018-02-27 17:34:14 -08:00
|
|
|
|
conntrack_execute(ct_, &new_batch, dl_type, false, true, 0, NULL, NULL,
|
2023-12-11 12:51:01 +02:00
|
|
|
|
NULL, NULL, now, 0);
|
2016-05-25 18:10:09 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 17:00:35 -07:00
|
|
|
|
static void
|
|
|
|
|
test_pcap(struct ovs_cmdl_context *ctx)
|
|
|
|
|
{
|
2018-02-27 17:34:14 -08:00
|
|
|
|
size_t total_count, batch_size_;
|
2018-10-05 12:52:40 -04:00
|
|
|
|
struct pcap_file *pcap;
|
2017-01-17 15:56:58 -08:00
|
|
|
|
int err = 0;
|
2016-04-14 17:00:35 -07:00
|
|
|
|
|
|
|
|
|
pcap = ovs_pcap_open(ctx->argv[1], "rb");
|
|
|
|
|
if (!pcap) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-27 17:34:14 -08:00
|
|
|
|
batch_size_ = 1;
|
2016-04-14 17:00:35 -07:00
|
|
|
|
if (ctx->argc > 2) {
|
2018-02-27 17:34:14 -08:00
|
|
|
|
batch_size_ = strtoul(ctx->argv[2], NULL, 0);
|
|
|
|
|
if (batch_size_ == 0 || batch_size_ > NETDEV_MAX_BURST) {
|
2016-04-14 17:00:35 -07:00
|
|
|
|
ovs_fatal(0,
|
|
|
|
|
"batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
|
|
|
|
|
NETDEV_MAX_BURST);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fatal_signal_init();
|
|
|
|
|
|
2019-05-02 21:34:04 -07:00
|
|
|
|
ct = conntrack_init();
|
2016-04-14 17:00:35 -07:00
|
|
|
|
total_count = 0;
|
2017-07-11 21:15:21 -07:00
|
|
|
|
for (;;) {
|
2017-01-17 15:56:58 -08:00
|
|
|
|
struct dp_packet *packet;
|
|
|
|
|
struct dp_packet_batch pkt_batch_;
|
|
|
|
|
struct dp_packet_batch *batch = &pkt_batch_;
|
2016-04-14 17:00:35 -07:00
|
|
|
|
|
2017-01-17 15:56:58 -08:00
|
|
|
|
dp_packet_batch_init(batch);
|
2018-02-27 17:34:14 -08:00
|
|
|
|
for (int i = 0; i < batch_size_; i++) {
|
2017-07-11 21:15:21 -07:00
|
|
|
|
err = ovs_pcap_read(pcap, &packet, NULL);
|
|
|
|
|
if (err) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
dp_packet_batch_add(batch, packet);
|
|
|
|
|
}
|
2018-12-10 20:17:53 +03:00
|
|
|
|
if (dp_packet_batch_is_empty(batch)) {
|
2016-04-14 17:00:35 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-02 21:34:04 -07:00
|
|
|
|
pcap_batch_execute_conntrack(ct, batch);
|
2016-04-14 17:00:35 -07:00
|
|
|
|
|
2018-02-27 10:41:30 -08:00
|
|
|
|
DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
|
2016-04-14 17:00:35 -07:00
|
|
|
|
struct ds ds = DS_EMPTY_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
total_count++;
|
|
|
|
|
|
2017-01-17 15:56:58 -08:00
|
|
|
|
format_flags(&ds, ct_state_to_string, packet->md.ct_state, '|');
|
2016-04-14 17:00:35 -07:00
|
|
|
|
printf("%"PRIuSIZE": %s\n", total_count, ds_cstr(&ds));
|
|
|
|
|
|
|
|
|
|
ds_destroy(&ds);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-17 15:56:58 -08:00
|
|
|
|
dp_packet_delete_batch(batch, true);
|
2016-04-14 17:00:35 -07:00
|
|
|
|
}
|
2019-05-02 21:34:04 -07:00
|
|
|
|
conntrack_destroy(ct);
|
2018-10-05 12:52:40 -04:00
|
|
|
|
ovs_pcap_close(pcap);
|
2016-04-14 17:00:35 -07:00
|
|
|
|
}
|
2015-11-15 22:07:25 -08:00
|
|
|
|
|
|
|
|
|
static const struct ovs_cmdl_command commands[] = {
|
|
|
|
|
/* Connection tracker tests. */
|
|
|
|
|
/* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to
|
|
|
|
|
* the connection tracker, 'batch_size' per call. If 'change_connection'
|
|
|
|
|
* is '1', each packet in a batch will have a different source and
|
|
|
|
|
* destination port */
|
|
|
|
|
{"benchmark", "n_threads n_pkts batch_size [change_connection]", 3, 4,
|
2016-08-15 18:47:29 +00:00
|
|
|
|
test_benchmark, OVS_RO},
|
2016-04-14 17:00:35 -07:00
|
|
|
|
/* Reads packets from 'file' and sends them to the connection tracker,
|
|
|
|
|
* 'batch_size' (1 by default) per call, with the commit flag set.
|
|
|
|
|
* Prints the ct_state of each packet. */
|
2016-08-15 18:47:29 +00:00
|
|
|
|
{"pcap", "file [batch_size]", 1, 2, test_pcap, OVS_RO},
|
2015-11-15 22:07:25 -08:00
|
|
|
|
|
2016-08-15 18:47:29 +00:00
|
|
|
|
{NULL, NULL, 0, 0, NULL, OVS_RO},
|
2015-11-15 22:07:25 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_conntrack_main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct ovs_cmdl_context ctx = {
|
|
|
|
|
.argc = argc - 1,
|
|
|
|
|
.argv = argv + 1,
|
|
|
|
|
};
|
|
|
|
|
set_program_name(argv[0]);
|
|
|
|
|
ovs_cmdl_run_command(&ctx, commands);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OVSTEST_REGISTER("test-conntrack", test_conntrack_main);
|