/*
- * Copyright (c) 2008-2017 Nicira, Inc.
+ * Copyright (c) 2008-2019 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
#include <config.h>
+#include <sys/types.h>
+#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <net/if.h>
-#include <netinet/in.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "dirs.h"
#include "dpctl.h"
#include "dpif.h"
+#include "dpif-provider.h"
#include "openvswitch/dynamic-string.h"
#include "flow.h"
#include "openvswitch/match.h"
#include "netdev.h"
-#include "netdev-dpdk.h"
#include "netlink.h"
#include "odp-util.h"
#include "openvswitch/ofpbuf.h"
#include "timeval.h"
#include "unixctl.h"
#include "util.h"
-#include "openvswitch/ofp-parse.h"
-#include "openvswitch/vlog.h"
-VLOG_DEFINE_THIS_MODULE(dpctl);
+#include "openvswitch/ofp-flow.h"
+#include "openvswitch/ofp-port.h"
+
+enum {
+ DPCTL_FLOWS_ADD = 0,
+ DPCTL_FLOWS_DEL,
+ DPCTL_FLOWS_MOD
+};
typedef int dpctl_command_handler(int argc, const char *argv[],
struct dpctl_params *);
return result;
}
+static bool
+dp_exists(const char *queried_dp)
+{
+ char *queried_name, *queried_type;
+ dp_parse_name(queried_dp, &queried_name, &queried_type);
+ struct sset dpif_names = SSET_INITIALIZER(&dpif_names),
+ dpif_types = SSET_INITIALIZER(&dpif_types);
+ dp_enumerate_types(&dpif_types);
+
+ bool found = (sset_contains(&dpif_types, queried_type) &&
+ !dp_enumerate_names(queried_type, &dpif_names) &&
+ sset_contains(&dpif_names, queried_name));
+
+ sset_destroy(&dpif_names);
+ sset_destroy(&dpif_types);
+ free(queried_name);
+ free(queried_type);
+ return found;
+}
+
+static bool
+dp_arg_exists(int argc, const char *argv[])
+{
+ return argc > 1 && dp_exists(argv[1]);
+}
+
+/* Open a dpif with an optional name argument.
+ *
+ * The datapath name is not a mandatory parameter for this command. If it is
+ * not specified, we retrieve it from the current setup, assuming only one
+ * exists. On success stores the opened dpif in '*dpifp'. */
+static int
+opt_dpif_open(int argc, const char *argv[], struct dpctl_params *dpctl_p,
+ int max_args, struct dpif **dpifp)
+{
+ char *dpname;
+
+ if (dp_arg_exists(argc, argv)) {
+ dpname = xstrdup(argv[1]);
+ } else if (argc != max_args) {
+ dpname = get_one_dp(dpctl_p);
+ } else {
+ /* If the arguments are the maximum possible number and there is no
+ * valid datapath argument, then we fall into the case of dpname is
+ * NULL, since this is an error. */
+ dpname = NULL;
+ }
+
+ int error = 0;
+ if (!dpname) {
+ error = EINVAL;
+ dpctl_error(dpctl_p, error, "datapath not found");
+ } else {
+ error = parsed_dpif_open(dpname, false, dpifp);
+ free(dpname);
+ if (error) {
+ dpctl_error(dpctl_p, error, "opening datapath");
+ }
+ }
+ return error;
+}
+
static int
dpctl_add_dp(int argc, const char *argv[],
struct dpctl_params *dpctl_p)
dpctl_print(dpctl_p, "%s:\n", dpif_name(dpif));
if (!dpif_get_dp_stats(dpif, &stats)) {
- dpctl_print(dpctl_p, "\tlookups: hit:%"PRIu64" missed:%"PRIu64
- " lost:%"PRIu64"\n\tflows: %"PRIu64"\n",
+ dpctl_print(dpctl_p, " lookups: hit:%"PRIu64" missed:%"PRIu64
+ " lost:%"PRIu64"\n flows: %"PRIu64"\n",
stats.n_hit, stats.n_missed, stats.n_lost, stats.n_flows);
if (stats.n_masks != UINT32_MAX) {
uint64_t n_pkts = stats.n_hit + stats.n_missed;
double avg = n_pkts ? (double) stats.n_mask_hit / n_pkts : 0.0;
- dpctl_print(dpctl_p, "\tmasks: hit:%"PRIu64" total:%"PRIu32
+ dpctl_print(dpctl_p, " masks: hit:%"PRIu64" total:%"PRIu32
" hit/pkt:%.2f\n",
stats.n_mask_hit, stats.n_masks, avg);
}
continue;
}
- dpctl_print(dpctl_p, "\tport %u: %s",
+ dpctl_print(dpctl_p, " port %u: %s",
dpif_port.port_no, dpif_port.name);
if (strcmp(dpif_port.type, "system")) {
error = netdev_get_stats(netdev, &s);
if (!error) {
netdev_close(netdev);
- print_stat(dpctl_p, "\t\tRX packets:", s.rx_packets);
+ print_stat(dpctl_p, " RX packets:", s.rx_packets);
print_stat(dpctl_p, " errors:", s.rx_errors);
print_stat(dpctl_p, " dropped:", s.rx_dropped);
print_stat(dpctl_p, " overruns:", s.rx_over_errors);
print_stat(dpctl_p, " frame:", s.rx_frame_errors);
dpctl_print(dpctl_p, "\n");
- print_stat(dpctl_p, "\t\tTX packets:", s.tx_packets);
+ print_stat(dpctl_p, " TX packets:", s.tx_packets);
print_stat(dpctl_p, " errors:", s.tx_errors);
print_stat(dpctl_p, " dropped:", s.tx_dropped);
print_stat(dpctl_p, " aborted:", s.tx_aborted_errors);
print_stat(dpctl_p, " carrier:", s.tx_carrier_errors);
dpctl_print(dpctl_p, "\n");
- print_stat(dpctl_p, "\t\tcollisions:", s.collisions);
+ print_stat(dpctl_p, " collisions:", s.collisions);
dpctl_print(dpctl_p, "\n");
- print_stat(dpctl_p, "\t\tRX bytes:", s.rx_bytes);
+ print_stat(dpctl_p, " RX bytes:", s.rx_bytes);
print_human_size(dpctl_p, s.rx_bytes);
print_stat(dpctl_p, " TX bytes:", s.tx_bytes);
print_human_size(dpctl_p, s.tx_bytes);
static void
format_dpif_flow(struct ds *ds, const struct dpif_flow *f, struct hmap *ports,
- char *type, struct dpctl_params *dpctl_p)
+ struct dpctl_params *dpctl_p)
{
if (dpctl_p->verbosity && f->ufid_present) {
odp_format_ufid(&f->ufid, ds);
ds_put_cstr(ds, ", ");
dpif_flow_stats_format(&f->stats, ds);
- if (dpctl_p->verbosity && !type && f->offloaded) {
- ds_put_cstr(ds, ", offloaded:yes");
+ if (dpctl_p->verbosity && f->attrs.offloaded) {
+ if (f->attrs.dp_layer && !strcmp(f->attrs.dp_layer, "ovs")) {
+ ds_put_cstr(ds, ", offloaded:partial");
+ } else {
+ ds_put_cstr(ds, ", offloaded:yes");
+ }
+ }
+ if (dpctl_p->verbosity && f->attrs.dp_layer) {
+ ds_put_format(ds, ", dp:%s", f->attrs.dp_layer);
}
ds_put_cstr(ds, ", actions:");
format_odp_actions(ds, f->actions, f->actions_len, ports);
+ if (dpctl_p->verbosity && f->attrs.dp_extra_info) {
+ ds_put_format(ds, ", dp-extra-info:%s", f->attrs.dp_extra_info);
+ }
}
-static char *supported_dump_types[] = {
- "offloaded",
- "ovs",
+struct dump_types {
+ bool ovs;
+ bool tc;
+ bool dpdk;
+ bool offloaded;
+ bool non_offloaded;
+ bool partially_offloaded;
};
+static void
+enable_all_dump_types(struct dump_types *dump_types)
+{
+ dump_types->ovs = true;
+ dump_types->tc = true;
+ dump_types->dpdk = true;
+ dump_types->offloaded = true;
+ dump_types->non_offloaded = true;
+ dump_types->partially_offloaded = true;
+}
+
+static int
+populate_dump_types(char *types_list, struct dump_types *dump_types,
+ struct dpctl_params *dpctl_p)
+{
+ if (!types_list) {
+ enable_all_dump_types(dump_types);
+ return 0;
+ }
+
+ char *current_type;
+
+ while (types_list && types_list[0] != '\0') {
+ current_type = types_list;
+ size_t type_len = strcspn(current_type, ",");
+
+ types_list += type_len + (types_list[type_len] != '\0');
+ current_type[type_len] = '\0';
+
+ if (!strcmp(current_type, "ovs")) {
+ dump_types->ovs = true;
+ } else if (!strcmp(current_type, "tc")) {
+ dump_types->tc = true;
+ } else if (!strcmp(current_type, "dpdk")) {
+ dump_types->dpdk = true;
+ } else if (!strcmp(current_type, "offloaded")) {
+ dump_types->offloaded = true;
+ } else if (!strcmp(current_type, "non-offloaded")) {
+ dump_types->non_offloaded = true;
+ } else if (!strcmp(current_type, "partially-offloaded")) {
+ dump_types->partially_offloaded = true;
+ } else if (!strcmp(current_type, "all")) {
+ enable_all_dump_types(dump_types);
+ } else {
+ dpctl_error(dpctl_p, EINVAL, "Failed to parse type (%s)",
+ current_type);
+ return EINVAL;
+ }
+ }
+ return 0;
+}
+
+static void
+determine_dpif_flow_dump_types(struct dump_types *dump_types,
+ struct dpif_flow_dump_types *dpif_dump_types)
+{
+ dpif_dump_types->ovs_flows = dump_types->ovs || dump_types->non_offloaded;
+ dpif_dump_types->netdev_flows = dump_types->tc || dump_types->offloaded
+ || dump_types->non_offloaded
+ || dump_types->dpdk
+ || dump_types->partially_offloaded;
+}
+
+static bool
+flow_passes_type_filter(const struct dpif_flow *f,
+ struct dump_types *dump_types)
+{
+ if (dump_types->ovs && !strcmp(f->attrs.dp_layer, "ovs")) {
+ return true;
+ }
+ if (dump_types->tc && !strcmp(f->attrs.dp_layer, "tc")) {
+ return true;
+ }
+ if (dump_types->dpdk && !strcmp(f->attrs.dp_layer, "dpdk")) {
+ return true;
+ }
+ if (dump_types->offloaded && f->attrs.offloaded &&
+ strcmp(f->attrs.dp_layer, "ovs")) {
+ return true;
+ }
+ if (dump_types->partially_offloaded && f->attrs.offloaded &&
+ !strcmp(f->attrs.dp_layer, "ovs")) {
+ return true;
+ }
+ if (dump_types->non_offloaded && !(f->attrs.offloaded)) {
+ return true;
+ }
+ return false;
+}
+
static struct hmap *
dpctl_get_portno_names(struct dpif *dpif, const struct dpctl_params *dpctl_p)
{
{
struct dpif *dpif;
struct ds ds;
- char *name;
char *filter = NULL;
- char *type = NULL;
struct flow flow_filter;
struct flow_wildcards wc_filter;
+ char *types_list = NULL;
+ struct dump_types dump_types;
+ struct dpif_flow_dump_types dpif_dump_types;
struct dpif_flow_dump_thread *flow_dump_thread;
struct dpif_flow_dump *flow_dump;
struct dpif_flow f;
int pmd_id = PMD_ID_NULL;
+ bool pmd_id_filter = false;
int lastargc = 0;
int error;
lastargc = argc;
if (!strncmp(argv[argc - 1], "filter=", 7) && !filter) {
filter = xstrdup(argv[--argc] + 7);
- } else if (!strncmp(argv[argc - 1], "type=", 5) && !type) {
- type = xstrdup(argv[--argc] + 5);
- }
- }
+ } else if (!strncmp(argv[argc - 1], "type=", 5) && !types_list) {
+ if (!dpctl_p->is_appctl) {
+ dpctl_error(dpctl_p, 0,
+ "Invalid argument 'type'. "
+ "Use 'ovs-appctl dpctl/dump-flows' instead.");
+ error = EINVAL;
+ goto out_free;
+ }
+ types_list = xstrdup(argv[--argc] + 5);
+ } else if (!strncmp(argv[argc - 1], "pmd=", 4)) {
+ if (!ovs_scan(argv[--argc], "pmd=%d", &pmd_id)) {
+ error = EINVAL;
+ goto out_free;
+ }
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc == 1 - we retrieve it from the
- * current setup, assuming only one exists. */
- name = (argc > 1) ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!name) {
- error = EINVAL;
- goto out_free;
+ if (pmd_id == -1) {
+ pmd_id = NON_PMD_CORE_ID;
+ }
+ pmd_id_filter = true;
+ }
}
- error = parsed_dpif_open(name, false, &dpif);
- free(name);
+ error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
goto out_free;
}
}
}
- if (type) {
- error = EINVAL;
- for (int i = 0; i < ARRAY_SIZE(supported_dump_types); i++) {
- if (!strcmp(supported_dump_types[i], type)) {
- error = 0;
- break;
- }
- }
- if (error) {
- dpctl_error(dpctl_p, error, "Failed to parse type (%s)", type);
- goto out_free;
- }
+ memset(&dump_types, 0, sizeof dump_types);
+ error = populate_dump_types(types_list, &dump_types, dpctl_p);
+ if (error) {
+ goto out_dpifclose;
}
+ determine_dpif_flow_dump_types(&dump_types, &dpif_dump_types);
/* Make sure that these values are different. PMD_ID_NULL means that the
* pmd is unspecified (e.g. because the datapath doesn't have different
ds_init(&ds);
memset(&f, 0, sizeof f);
- flow_dump = dpif_flow_dump_create(dpif, false, (type ? type : "dpctl"));
+ flow_dump = dpif_flow_dump_create(dpif, false, &dpif_dump_types);
flow_dump_thread = dpif_flow_dump_thread_create(flow_dump);
while (dpif_flow_dump_next(flow_dump_thread, &f, 1)) {
if (filter) {
struct match match, match_filter;
struct minimatch minimatch;
- odp_flow_key_to_flow(f.key, f.key_len, &flow);
- odp_flow_key_to_mask(f.mask, f.mask_len, &wc, &flow);
+ odp_flow_key_to_flow(f.key, f.key_len, &flow, NULL);
+ odp_flow_key_to_mask(f.mask, f.mask_len, &wc, &flow, NULL);
match_init(&match, &flow, &wc);
match_init(&match_filter, &flow_filter, &wc);
/* If 'pmd_id' is specified, overlapping flows could be dumped from
* different pmd threads. So, separates dumps from different pmds
* by printing a title line. */
- if (pmd_id != f.pmd_id) {
+ if (!pmd_id_filter && pmd_id != f.pmd_id) {
if (f.pmd_id == NON_PMD_CORE_ID) {
- ds_put_format(&ds, "flow-dump from non-dpdk interfaces:\n");
+ ds_put_format(&ds, "flow-dump from the main thread:\n");
} else {
ds_put_format(&ds, "flow-dump from pmd on cpu core: %d\n",
f.pmd_id);
}
pmd_id = f.pmd_id;
}
- format_dpif_flow(&ds, &f, portno_names, type, dpctl_p);
-
- dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
+ if (pmd_id == f.pmd_id &&
+ flow_passes_type_filter(&f, &dump_types)) {
+ format_dpif_flow(&ds, &f, portno_names, dpctl_p);
+ dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
+ }
}
dpif_flow_dump_thread_destroy(flow_dump_thread);
error = dpif_flow_dump_destroy(flow_dump);
dpif_close(dpif);
out_free:
free(filter);
- free(type);
+ free(types_list);
return error;
}
static int
-dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
- struct dpctl_params *dpctl_p)
+dpctl_put_flow_dpif(struct dpif *dpif, const char *key_s,
+ const char *actions_s,
+ enum dpif_flow_put_flags flags,
+ struct dpctl_params *dpctl_p)
{
- const char *key_s = argv[argc - 2];
- const char *actions_s = argv[argc - 1];
struct dpif_flow_stats stats;
struct dpif_port dpif_port;
struct dpif_port_dump port_dump;
struct ofpbuf actions;
struct ofpbuf key;
struct ofpbuf mask;
- struct dpif *dpif;
ovs_u128 ufid;
bool ufid_present;
- char *dp_name;
struct simap port_names;
int n, error;
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc < 4 - we retrieve it from the
- * current setup, assuming only one exists. */
- dp_name = argc == 4 ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!dp_name) {
- return EINVAL;
- }
- error = parsed_dpif_open(dp_name, false, &dpif);
- free(dp_name);
- if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
- return error;
- }
-
ufid_present = false;
n = odp_ufid_from_string(key_s, &ufid);
if (n < 0) {
ofpbuf_init(&key, 0);
ofpbuf_init(&mask, 0);
- error = odp_flow_from_string(key_s, &port_names, &key, &mask);
+ char *error_s;
+ error = odp_flow_from_string(key_s, &port_names, &key, &mask, &error_s);
simap_destroy(&port_names);
if (error) {
- dpctl_error(dpctl_p, error, "parsing flow key");
+ dpctl_error(dpctl_p, error, "parsing flow key (%s)", error_s);
+ free(error_s);
goto out_freekeymask;
}
goto out_freeactions;
}
+ if (!ufid_present && dpctl_p->is_appctl) {
+ /* Generating UFID for this flow so it could be offloaded to HW. We're
+ * not doing that if invoked from ovs-dpctl utility because
+ * odp_flow_key_hash() uses randomly generated base for flow hashes
+ * that will be different for each invocation. And, anyway, offloading
+ * is only available via appctl. */
+ odp_flow_key_hash(key.data, key.size, &ufid);
+ ufid_present = true;
+ }
+
/* The flow will be added on all pmds currently in the datapath. */
error = dpif_flow_put(dpif, flags,
key.data, key.size,
out_freekeymask:
ofpbuf_uninit(&mask);
ofpbuf_uninit(&key);
+ return error;
+}
+
+static int
+dpctl_put_flow(int argc, const char *argv[], enum dpif_flow_put_flags flags,
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error;
+
+ error = opt_dpif_open(argc, argv, dpctl_p, 4, &dpif);
+ if (error) {
+ return error;
+ }
+
+ error = dpctl_put_flow_dpif(dpif, argv[argc - 2], argv[argc - 1], flags,
+ dpctl_p);
+
dpif_close(dpif);
return error;
}
const char *key_s = argv[argc - 1];
struct dpif_flow flow;
struct dpif *dpif;
- char *dp_name;
ovs_u128 ufid;
struct ofpbuf buf;
uint64_t stub[DPIF_FLOW_BUFSIZE / 8];
struct ds ds;
int n, error;
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc < 3 - we retrieve it from the
- * current setup, assuming only one exists. */
- dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!dp_name) {
- return EINVAL;
- }
- error = parsed_dpif_open(dp_name, false, &dpif);
- free(dp_name);
+ error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
return error;
}
}
ds_init(&ds);
- format_dpif_flow(&ds, &flow, portno_names, NULL, dpctl_p);
+ format_dpif_flow(&ds, &flow, portno_names, dpctl_p);
dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
ds_destroy(&ds);
}
static int
-dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
+dpctl_del_flow_dpif(struct dpif *dpif, const char *key_s,
+ struct dpctl_params *dpctl_p)
{
- const char *key_s = argv[argc - 1];
struct dpif_flow_stats stats;
struct dpif_port dpif_port;
struct dpif_port_dump port_dump;
struct ofpbuf key;
struct ofpbuf mask; /* To be ignored. */
- struct dpif *dpif;
+
ovs_u128 ufid;
+ bool ufid_generated;
bool ufid_present;
- char *dp_name;
struct simap port_names;
int n, error;
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc < 3 - we retrieve it from the
- * current setup, assuming only one exists. */
- dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!dp_name) {
- return EINVAL;
- }
- error = parsed_dpif_open(dp_name, false, &dpif);
- free(dp_name);
- if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
- return error;
- }
-
ufid_present = false;
n = odp_ufid_from_string(key_s, &ufid);
if (n < 0) {
ofpbuf_init(&key, 0);
ofpbuf_init(&mask, 0);
- error = odp_flow_from_string(key_s, &port_names, &key, &mask);
+ char *error_s;
+ error = odp_flow_from_string(key_s, &port_names, &key, &mask, &error_s);
if (error) {
- dpctl_error(dpctl_p, error, "parsing flow key");
+ dpctl_error(dpctl_p, error, "%s", error_s);
+ free(error_s);
goto out;
}
+ if (!ufid_present && dpctl_p->is_appctl) {
+ /* While adding flow via appctl we're generating UFID to make HW
+ * offloading possible. Generating UFID here to be sure that such
+ * flows could be removed the same way they were added. */
+ odp_flow_key_hash(key.data, key.size, &ufid);
+ ufid_present = ufid_generated = true;
+ }
+
/* The flow will be deleted from all pmds currently in the datapath. */
error = dpif_flow_del(dpif, key.data, key.size,
ufid_present ? &ufid : NULL, PMD_ID_NULL,
if (error) {
dpctl_error(dpctl_p, error, "deleting flow");
- if (error == ENOENT && !ufid_present) {
+ if (error == ENOENT && (!ufid_present || ufid_generated)) {
struct ds s;
ds_init(&s);
ofpbuf_uninit(&mask);
ofpbuf_uninit(&key);
simap_destroy(&port_names);
+ return error;
+}
+
+static int
+dpctl_del_flow(int argc, const char *argv[], struct dpctl_params *dpctl_p)
+{
+ const char *key_s = argv[argc - 1];
+ struct dpif *dpif;
+ int error;
+
+ error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+ if (error) {
+ return error;
+ }
+
+ error = dpctl_del_flow_dpif(dpif, key_s, dpctl_p);
+
dpif_close(dpif);
return error;
}
+static int
+dpctl_parse_flow_line(int command, struct ds *s, char **flow, char **action)
+{
+ const char *line = ds_cstr(s);
+ size_t len;
+
+ /* First figure out the command, or fallback to FLOWS_ADD. */
+ line += strspn(line, " \t\r\n");
+ len = strcspn(line, ", \t\r\n");
+
+ if (!strncmp(line, "add", len)) {
+ command = DPCTL_FLOWS_ADD;
+ } else if (!strncmp(line, "delete", len)) {
+ command = DPCTL_FLOWS_DEL;
+ } else if (!strncmp(line, "modify", len)) {
+ command = DPCTL_FLOWS_MOD;
+ } else {
+ len = 0;
+ }
+ line += len;
+
+ /* Isolate flow and action (for add/modify). */
+ line += strspn(line, " \t\r\n");
+ len = strcspn(line, " \t\r\n");
+
+ if (len == 0) {
+ *flow = NULL;
+ *action = NULL;
+ return command;
+ }
+
+ *flow = xzalloc(len + 1);
+ ovs_strlcpy(*flow, line, len + 1);
+
+ line += len;
+ line += strspn(line, " \t\r\n");
+ if (strlen(line)) {
+ *action = xstrdup(line);
+ } else {
+ *action = NULL;
+ }
+
+ return command;
+}
+
+static int
+dpctl_process_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
+{
+ const char *file_name = argv[argc - 1];
+ int line_number = 0;
+ struct dpif *dpif;
+ struct ds line;
+ FILE *stream;
+ int error;
+ int def_cmd = DPCTL_FLOWS_ADD;
+
+ if (strstr(argv[0], "mod-flows")) {
+ def_cmd = DPCTL_FLOWS_MOD;
+ } else if (strstr(argv[0], "del-flows")) {
+ def_cmd = DPCTL_FLOWS_DEL;
+ }
+
+ error = opt_dpif_open(argc, argv, dpctl_p, 4, &dpif);
+ if (error) {
+ return error;
+ }
+
+ stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
+ if (!stream) {
+ error = errno;
+ dpctl_error(dpctl_p, error, "Opening file \"%s\" failed", file_name);
+ goto out_close_dpif;
+ }
+
+ ds_init(&line);
+ while (!ds_get_preprocessed_line(&line, stream, &line_number)) {
+ /* We do not process all the lines first and then execute the actions
+ * as we would like to take commands as a continuous stream of
+ * commands from stdin.
+ */
+ char *flow = NULL;
+ char *action = NULL;
+ int cmd = dpctl_parse_flow_line(def_cmd, &line, &flow, &action);
+
+ if ((!flow && !action)
+ || ((cmd == DPCTL_FLOWS_ADD || cmd == DPCTL_FLOWS_MOD) && !action)
+ || (cmd == DPCTL_FLOWS_DEL && action)) {
+ dpctl_error(dpctl_p, 0,
+ "Failed parsing line number %u, skipped!",
+ line_number);
+ } else {
+ switch (cmd) {
+ case DPCTL_FLOWS_ADD:
+ dpctl_put_flow_dpif(dpif, flow, action,
+ DPIF_FP_CREATE, dpctl_p);
+ break;
+ case DPCTL_FLOWS_MOD:
+ dpctl_put_flow_dpif(dpif, flow, action,
+ DPIF_FP_MODIFY, dpctl_p);
+ break;
+ case DPCTL_FLOWS_DEL:
+ dpctl_del_flow_dpif(dpif, flow, dpctl_p);
+ break;
+ }
+ }
+
+ free(flow);
+ free(action);
+ }
+
+ ds_destroy(&line);
+ if (stream != stdin) {
+ fclose(stream);
+ }
+out_close_dpif:
+ dpif_close(dpif);
+ return 0;
+}
+
static int
dpctl_del_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
{
struct dpif *dpif;
- char *name;
int error;
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc < 2 - we retrieve it from the
- * current setup, assuming only one exists. */
- name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!name) {
- return EINVAL;
+ if ((!dp_arg_exists(argc, argv) && argc == 2) || argc > 2) {
+ return dpctl_process_flows(argc, argv, dpctl_p);
}
- error = parsed_dpif_open(name, false, &dpif);
- free(name);
+
+ error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
return error;
}
for (; commands->name; commands++) {
const struct dpctl_command *c = commands;
+ if (dpctl_p->is_appctl && !strcmp(c->name, "help")) {
+ continue;
+ }
+
ds_put_format(&ds, " %s%-23s %s\n", dpctl_p->is_appctl ? "dpctl/" : "",
c->name, c->usage);
}
return 0;
}
+\f
static int
dpctl_dump_conntrack(int argc, const char *argv[],
uint16_t zone, *pzone = NULL;
int tot_bkts;
struct dpif *dpif;
- char *name;
int error;
if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) {
pzone = &zone;
argc--;
}
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc < 2 - we retrieve it from the
- * current setup, assuming only one exists. */
- name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!name) {
- return EINVAL;
- }
- error = parsed_dpif_open(name, false, &dpif);
- free(name);
+
+ error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
return error;
}
dpctl_flush_conntrack(int argc, const char *argv[],
struct dpctl_params *dpctl_p)
{
- struct dpif *dpif;
+ struct dpif *dpif = NULL;
struct ct_dpif_tuple tuple, *ptuple = NULL;
struct ds ds = DS_EMPTY_INITIALIZER;
uint16_t zone, *pzone = NULL;
- char *name;
- int error, i = 1;
- bool got_dpif = false;
-
- /* Parse datapath name. It is not a mandatory parameter for this command.
- * If it is not specified, we retrieve it from the current setup,
- * assuming only one exists. */
- if (argc >= 2) {
- error = parsed_dpif_open(argv[i], false, &dpif);
- if (!error) {
- got_dpif = true;
- i++;
- } else if (argc == 4) {
- dpctl_error(dpctl_p, error, "invalid datapath");
- return error;
- }
- }
- if (!got_dpif) {
- name = get_one_dp(dpctl_p);
- if (!name) {
- return EINVAL;
- }
- error = parsed_dpif_open(name, false, &dpif);
- free(name);
- if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
- return error;
- }
+ int error;
+ int args = argc - 1;
+
+ /* Parse ct tuple */
+ if (args && ct_dpif_parse_tuple(&tuple, argv[args], &ds)) {
+ ptuple = &tuple;
+ args--;
}
/* Parse zone */
- if (argc > i && ovs_scan(argv[i], "zone=%"SCNu16, &zone)) {
+ if (args && ovs_scan(argv[args], "zone=%"SCNu16, &zone)) {
pzone = &zone;
- i++;
+ args--;
}
+
/* Report error if there are more than one unparsed argument. */
- if (argc - i > 1) {
- ds_put_cstr(&ds, "invalid zone");
+ if (args > 1) {
+ ds_put_cstr(&ds, "invalid arguments");
error = EINVAL;
goto error;
}
- /* Parse ct tuple */
- if (argc > i && ct_dpif_parse_tuple(&tuple, argv[i], &ds)) {
- ptuple = &tuple;
- i++;
- }
- /* Report error if there is an unparsed argument. */
- if (argc - i) {
- error = EINVAL;
- goto error;
+ error = opt_dpif_open(argc, argv, dpctl_p, 4, &dpif);
+ if (error) {
+ return error;
}
error = ct_dpif_flush(dpif, pzone, ptuple);
struct dpctl_params *dpctl_p)
{
struct dpif *dpif;
- char *name;
-
struct ct_dpif_dump_state *dump;
struct ct_dpif_entry cte;
uint16_t zone, *pzone = NULL;
int tot_bkts;
- bool verbose = false;
int lastargc = 0;
int proto_stats[CT_STATS_MAX];
int tcp_conn_per_states[CT_DPIF_TCPS_MAX_NUM];
int error;
+ bool verbose = dpctl_p->verbosity;
+
while (argc > 1 && lastargc != argc) {
lastargc = argc;
if (!strncmp(argv[argc - 1], "verbose", 7)) {
+ /* Support "verbose" argument for backwards compatibility. */
verbose = true;
argc--;
} else if (!strncmp(argv[argc - 1], "zone=", 5)) {
}
}
}
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc == 1 - we retrieve it from the
- * current setup, assuming only one exists. */
- name = (argc > 1) ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!name) {
- return EINVAL;
- }
- error = parsed_dpif_open(name, false, &dpif);
- free(name);
+ error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
return error;
}
dpctl_print(dpctl_p, "Connections Stats:\n Total: %d\n", tot_conn);
if (proto_stats[CT_STATS_TCP]) {
- dpctl_print(dpctl_p, "\tTCP: %d\n", proto_stats[CT_STATS_TCP]);
+ dpctl_print(dpctl_p, " TCP: %d\n", proto_stats[CT_STATS_TCP]);
if (verbose) {
- dpctl_print(dpctl_p, "\t Conn per TCP states:\n");
+ dpctl_print(dpctl_p, " Conn per TCP states:\n");
for (int i = 0; i < CT_DPIF_TCPS_MAX_NUM; i++) {
if (tcp_conn_per_states[i]) {
struct ds s = DS_EMPTY_INITIALIZER;
}
}
if (proto_stats[CT_STATS_UDP]) {
- dpctl_print(dpctl_p, "\tUDP: %d\n", proto_stats[CT_STATS_UDP]);
+ dpctl_print(dpctl_p, " UDP: %d\n", proto_stats[CT_STATS_UDP]);
}
if (proto_stats[CT_STATS_UDPLITE]) {
- dpctl_print(dpctl_p, "\tUDPLITE: %d\n", proto_stats[CT_STATS_UDPLITE]);
+ dpctl_print(dpctl_p, " UDPLITE: %d\n", proto_stats[CT_STATS_UDPLITE]);
}
if (proto_stats[CT_STATS_SCTP]) {
- dpctl_print(dpctl_p, "\tSCTP: %d\n", proto_stats[CT_STATS_SCTP]);
+ dpctl_print(dpctl_p, " SCTP: %d\n", proto_stats[CT_STATS_SCTP]);
}
if (proto_stats[CT_STATS_ICMP]) {
- dpctl_print(dpctl_p, "\tICMP: %d\n", proto_stats[CT_STATS_ICMP]);
+ dpctl_print(dpctl_p, " ICMP: %d\n", proto_stats[CT_STATS_ICMP]);
}
if (proto_stats[CT_STATS_DCCP]) {
- dpctl_print(dpctl_p, "\tDCCP: %d\n", proto_stats[CT_STATS_DCCP]);
+ dpctl_print(dpctl_p, " DCCP: %d\n", proto_stats[CT_STATS_DCCP]);
}
if (proto_stats[CT_STATS_IGMP]) {
- dpctl_print(dpctl_p, "\tIGMP: %d\n", proto_stats[CT_STATS_IGMP]);
+ dpctl_print(dpctl_p, " IGMP: %d\n", proto_stats[CT_STATS_IGMP]);
}
if (proto_stats[CT_STATS_OTHER]) {
- dpctl_print(dpctl_p, "\tOther: %d\n", proto_stats[CT_STATS_OTHER]);
+ dpctl_print(dpctl_p, " Other: %d\n", proto_stats[CT_STATS_OTHER]);
}
ct_dpif_dump_done(dump);
struct dpctl_params *dpctl_p)
{
struct dpif *dpif;
- char *name;
-
struct ct_dpif_dump_state *dump;
struct ct_dpif_entry cte;
uint16_t gt = 0; /* Threshold: display value when greater than gt. */
}
}
- /* The datapath name is not a mandatory parameter for this command.
- * If it is not specified - so argc < 2 - we retrieve it from the
- * current setup, assuming only one exists. */
- name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(dpctl_p);
- if (!name) {
- return EINVAL;
- }
-
- error = parsed_dpif_open(name, false, &dpif);
- free(name);
+ error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
if (error) {
- dpctl_error(dpctl_p, error, "opening datapath");
return error;
}
return error;
}
\f
+static int
+dpctl_ct_set_maxconns(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+ if (!error) {
+ uint32_t maxconns;
+ if (ovs_scan(argv[argc - 1], "%"SCNu32, &maxconns)) {
+ error = ct_dpif_set_maxconns(dpif, maxconns);
+
+ if (!error) {
+ dpctl_print(dpctl_p, "setting maxconns successful");
+ } else {
+ dpctl_error(dpctl_p, error, "ct set maxconns failed");
+ }
+ } else {
+ error = EINVAL;
+ dpctl_error(dpctl_p, error, "maxconns missing or malformed");
+ }
+ dpif_close(dpif);
+ }
+
+ return error;
+}
+
+static int
+dpctl_ct_get_maxconns(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
+ if (!error) {
+ uint32_t maxconns;
+ error = ct_dpif_get_maxconns(dpif, &maxconns);
+
+ if (!error) {
+ dpctl_print(dpctl_p, "%u\n", maxconns);
+ } else {
+ dpctl_error(dpctl_p, error, "maxconns could not be retrieved");
+ }
+ dpif_close(dpif);
+ }
+
+ return error;
+}
+
+static int
+dpctl_ct_get_nconns(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
+ if (!error) {
+ uint32_t nconns;
+ error = ct_dpif_get_nconns(dpif, &nconns);
+
+ if (!error) {
+ dpctl_print(dpctl_p, "%u\n", nconns);
+ } else {
+ dpctl_error(dpctl_p, error, "nconns could not be retrieved");
+ }
+ dpif_close(dpif);
+ }
+
+ return error;
+}
+
+static int
+dpctl_ct_set_tcp_seq_chk__(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p, bool enabled)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+ if (!error) {
+ error = ct_dpif_set_tcp_seq_chk(dpif, enabled);
+ if (!error) {
+ dpctl_print(dpctl_p,
+ "%s TCP sequence checking successful",
+ enabled ? "enabling" : "disabling");
+ } else {
+ dpctl_error(dpctl_p, error,
+ "%s TCP sequence checking failed",
+ enabled ? "enabling" : "disabling");
+ }
+ dpif_close(dpif);
+ }
+ return error;
+}
+
+static int
+dpctl_ct_enable_tcp_seq_chk(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ return dpctl_ct_set_tcp_seq_chk__(argc, argv, dpctl_p, true);
+}
+
+static int
+dpctl_ct_disable_tcp_seq_chk(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ return dpctl_ct_set_tcp_seq_chk__(argc, argv, dpctl_p, false);
+}
+
+static int
+dpctl_ct_get_tcp_seq_chk(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
+ if (!error) {
+ bool enabled;
+ error = ct_dpif_get_tcp_seq_chk(dpif, &enabled);
+ if (!error) {
+ dpctl_print(dpctl_p, "TCP sequence checking: %s\n",
+ enabled ? "enabled" : "disabled");
+ } else {
+ dpctl_error(dpctl_p, error, "TCP sequence checking query failed");
+ }
+ dpif_close(dpif);
+ }
+ return error;
+}
+
+static int
+dpctl_ct_set_limits(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ int i = dp_arg_exists(argc, argv) ? 2 : 1;
+ uint32_t default_limit, *p_default_limit = NULL;
+ struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
+
+ int error = opt_dpif_open(argc, argv, dpctl_p, INT_MAX, &dpif);
+ if (error) {
+ return error;
+ }
+
+ /* Parse default limit */
+ if (!strncmp(argv[i], "default=", 8)) {
+ if (ovs_scan(argv[i], "default=%"SCNu32, &default_limit)) {
+ p_default_limit = &default_limit;
+ i++;
+ } else {
+ ds_put_cstr(&ds, "invalid default limit");
+ error = EINVAL;
+ goto error;
+ }
+ }
+
+ /* Parse ct zone limit tuples */
+ while (i < argc) {
+ uint16_t zone;
+ uint32_t limit;
+ if (!ct_dpif_parse_zone_limit_tuple(argv[i++], &zone, &limit, &ds)) {
+ error = EINVAL;
+ goto error;
+ }
+ ct_dpif_push_zone_limit(&zone_limits, zone, limit, 0);
+ }
+
+ error = ct_dpif_set_limits(dpif, p_default_limit, &zone_limits);
+ if (!error) {
+ ct_dpif_free_zone_limits(&zone_limits);
+ dpif_close(dpif);
+ return 0;
+ } else {
+ ds_put_cstr(&ds, "failed to set conntrack limit");
+ }
+
+error:
+ dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+ ds_destroy(&ds);
+ ct_dpif_free_zone_limits(&zone_limits);
+ dpif_close(dpif);
+ return error;
+}
+
+static int
+parse_ct_limit_zones(const char *argv, struct ovs_list *zone_limits,
+ struct ds *ds)
+{
+ char *save_ptr = NULL, *argcopy, *next_zone;
+ uint16_t zone;
+
+ if (strncmp(argv, "zone=", 5)) {
+ ds_put_format(ds, "invalid argument %s", argv);
+ return EINVAL;
+ }
+
+ argcopy = xstrdup(argv + 5);
+ next_zone = strtok_r(argcopy, ",", &save_ptr);
+
+ do {
+ if (ovs_scan(next_zone, "%"SCNu16, &zone)) {
+ ct_dpif_push_zone_limit(zone_limits, zone, 0, 0);
+ } else {
+ ds_put_cstr(ds, "invalid zone");
+ free(argcopy);
+ return EINVAL;
+ }
+ } while ((next_zone = strtok_r(NULL, ",", &save_ptr)) != NULL);
+
+ free(argcopy);
+ return 0;
+}
+
+static int
+dpctl_ct_del_limits(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ int error;
+ int i = dp_arg_exists(argc, argv) ? 2 : 1;
+ struct ovs_list zone_limits = OVS_LIST_INITIALIZER(&zone_limits);
+
+ error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+ if (error) {
+ return error;
+ }
+
+ error = parse_ct_limit_zones(argv[i], &zone_limits, &ds);
+ if (error) {
+ goto error;
+ }
+
+ error = ct_dpif_del_limits(dpif, &zone_limits);
+ if (!error) {
+ goto out;
+ } else {
+ ds_put_cstr(&ds, "failed to delete conntrack limit");
+ }
+
+error:
+ dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+ ds_destroy(&ds);
+out:
+ ct_dpif_free_zone_limits(&zone_limits);
+ dpif_close(dpif);
+ return error;
+}
+
+static int
+dpctl_ct_get_limits(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ uint32_t default_limit;
+ int i = dp_arg_exists(argc, argv) ? 2 : 1;
+ struct ovs_list list_query = OVS_LIST_INITIALIZER(&list_query);
+ struct ovs_list list_reply = OVS_LIST_INITIALIZER(&list_reply);
+
+ int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+ if (error) {
+ return error;
+ }
+
+ if (argc > i) {
+ error = parse_ct_limit_zones(argv[i], &list_query, &ds);
+ if (error) {
+ goto error;
+ }
+ }
+
+ error = ct_dpif_get_limits(dpif, &default_limit, &list_query,
+ &list_reply);
+ if (!error) {
+ ct_dpif_format_zone_limits(default_limit, &list_reply, &ds);
+ dpctl_print(dpctl_p, "%s\n", ds_cstr(&ds));
+ goto out;
+ } else {
+ ds_put_format(&ds, "failed to get conntrack limit %s",
+ ovs_strerror(error));
+ }
+
+error:
+ dpctl_error(dpctl_p, error, "%s", ds_cstr(&ds));
+out:
+ ds_destroy(&ds);
+ ct_dpif_free_zone_limits(&list_query);
+ ct_dpif_free_zone_limits(&list_reply);
+ dpif_close(dpif);
+ return error;
+}
+
+static int
+ipf_set_enabled__(int argc, const char *argv[], struct dpctl_params *dpctl_p,
+ bool enabled)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 4, &dpif);
+ if (!error) {
+ char v4_or_v6[3] = {0};
+ if (ovs_scan(argv[argc - 1], "%2s", v4_or_v6) &&
+ (!strncmp(v4_or_v6, "v4", 2) || !strncmp(v4_or_v6, "v6", 2))) {
+ error = ct_dpif_ipf_set_enabled(
+ dpif, !strncmp(v4_or_v6, "v6", 2), enabled);
+ if (!error) {
+ dpctl_print(dpctl_p,
+ "%s fragmentation reassembly successful",
+ enabled ? "enabling" : "disabling");
+ } else {
+ dpctl_error(dpctl_p, error,
+ "%s fragmentation reassembly failed",
+ enabled ? "enabling" : "disabling");
+ }
+ } else {
+ error = EINVAL;
+ dpctl_error(dpctl_p, error,
+ "parameter missing: 'v4' for IPv4 or 'v6' for IPv6");
+ }
+ dpif_close(dpif);
+ }
+ return error;
+}
+
+static int
+dpctl_ipf_set_enabled(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ return ipf_set_enabled__(argc, argv, dpctl_p, true);
+}
+
+static int
+dpctl_ipf_set_disabled(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ return ipf_set_enabled__(argc, argv, dpctl_p, false);
+}
+
+static int
+dpctl_ipf_set_min_frag(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 4, &dpif);
+ if (!error) {
+ char v4_or_v6[3] = {0};
+ if (ovs_scan(argv[argc - 2], "%2s", v4_or_v6) &&
+ (!strncmp(v4_or_v6, "v4", 2) || !strncmp(v4_or_v6, "v6", 2))) {
+ uint32_t min_fragment;
+ if (ovs_scan(argv[argc - 1], "%"SCNu32, &min_fragment)) {
+ error = ct_dpif_ipf_set_min_frag(
+ dpif, !strncmp(v4_or_v6, "v6", 2), min_fragment);
+ if (!error) {
+ dpctl_print(dpctl_p,
+ "setting minimum fragment size successful");
+ } else {
+ dpctl_error(dpctl_p, error,
+ "requested minimum fragment size too small;"
+ " see documentation");
+ }
+ } else {
+ error = EINVAL;
+ dpctl_error(dpctl_p, error,
+ "parameter missing for minimum fragment size");
+ }
+ } else {
+ error = EINVAL;
+ dpctl_error(dpctl_p, error,
+ "parameter missing: v4 for IPv4 or v6 for IPv6");
+ }
+ dpif_close(dpif);
+ }
+
+ return error;
+}
+
+static int
+dpctl_ipf_set_max_nfrags(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 3, &dpif);
+ if (!error) {
+ uint32_t nfrags_max;
+ if (ovs_scan(argv[argc - 1], "%"SCNu32, &nfrags_max)) {
+ error = ct_dpif_ipf_set_max_nfrags(dpif, nfrags_max);
+ if (!error) {
+ dpctl_print(dpctl_p,
+ "setting maximum fragments successful");
+ } else {
+ dpctl_error(dpctl_p, error,
+ "setting maximum fragments failed");
+ }
+ } else {
+ error = EINVAL;
+ dpctl_error(dpctl_p, error,
+ "parameter missing for maximum fragments");
+ }
+ dpif_close(dpif);
+ }
+
+ return error;
+}
+
+static void
+dpctl_dump_ipf(struct dpif *dpif, struct dpctl_params *dpctl_p)
+{
+ struct ipf_dump_ctx *dump_ctx;
+ char *dump;
+
+ int error = ct_dpif_ipf_dump_start(dpif, &dump_ctx);
+ if (error) {
+ dpctl_error(dpctl_p, error, "starting ipf list dump");
+ /* Nothing to clean up, just return. */
+ return;
+ }
+
+ dpctl_print(dpctl_p, "\n Fragment Lists:\n\n");
+ while (!(error = ct_dpif_ipf_dump_next(dpif, dump_ctx, &dump))) {
+ dpctl_print(dpctl_p, "%s\n", dump);
+ free(dump);
+ }
+
+ if (error && error != EOF) {
+ dpctl_error(dpctl_p, error, "dumping ipf lists failed");
+ }
+
+ ct_dpif_ipf_dump_done(dpif, dump_ctx);
+}
+
+static int
+dpctl_ct_ipf_get_status(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ int error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
+
+ if (!error) {
+ struct dpif_ipf_status dpif_ipf_status;
+ error = ct_dpif_ipf_get_status(dpif, &dpif_ipf_status);
+
+ if (!error) {
+ dpctl_print(dpctl_p, " Fragmentation Module Status\n");
+ dpctl_print(dpctl_p, " ---------------------------\n");
+ dpctl_print(dpctl_p, " v4 enabled: %u\n",
+ dpif_ipf_status.v4.enabled);
+ dpctl_print(dpctl_p, " v6 enabled: %u\n",
+ dpif_ipf_status.v6.enabled);
+ dpctl_print(dpctl_p, " max num frags (v4/v6): %u\n",
+ dpif_ipf_status.nfrag_max);
+ dpctl_print(dpctl_p, " num frag: %u\n",
+ dpif_ipf_status.nfrag);
+ dpctl_print(dpctl_p, " min v4 frag size: %u\n",
+ dpif_ipf_status.v4.min_frag_size);
+ dpctl_print(dpctl_p, " v4 frags accepted: %"PRIu64"\n",
+ dpif_ipf_status.v4.nfrag_accepted);
+ dpctl_print(dpctl_p, " v4 frags completed: %"PRIu64"\n",
+ dpif_ipf_status.v4.nfrag_completed_sent);
+ dpctl_print(dpctl_p, " v4 frags expired: %"PRIu64"\n",
+ dpif_ipf_status.v4.nfrag_expired_sent);
+ dpctl_print(dpctl_p, " v4 frags too small: %"PRIu64"\n",
+ dpif_ipf_status.v4.nfrag_too_small);
+ dpctl_print(dpctl_p, " v4 frags overlapped: %"PRIu64"\n",
+ dpif_ipf_status.v4.nfrag_overlap);
+ dpctl_print(dpctl_p, " v4 frags purged: %"PRIu64"\n",
+ dpif_ipf_status.v4.nfrag_purged);
+
+ dpctl_print(dpctl_p, " min v6 frag size: %u\n",
+ dpif_ipf_status.v6.min_frag_size);
+ dpctl_print(dpctl_p, " v6 frags accepted: %"PRIu64"\n",
+ dpif_ipf_status.v6.nfrag_accepted);
+ dpctl_print(dpctl_p, " v6 frags completed: %"PRIu64"\n",
+ dpif_ipf_status.v6.nfrag_completed_sent);
+ dpctl_print(dpctl_p, " v6 frags expired: %"PRIu64"\n",
+ dpif_ipf_status.v6.nfrag_expired_sent);
+ dpctl_print(dpctl_p, " v6 frags too small: %"PRIu64"\n",
+ dpif_ipf_status.v6.nfrag_too_small);
+ dpctl_print(dpctl_p, " v6 frags overlapped: %"PRIu64"\n",
+ dpif_ipf_status.v6.nfrag_overlap);
+ dpctl_print(dpctl_p, " v6 frags purged: %"PRIu64"\n",
+ dpif_ipf_status.v6.nfrag_purged);
+ } else {
+ dpctl_error(dpctl_p, error,
+ "ipf status could not be retrieved");
+ return error;
+ }
+
+ if (dpctl_p->verbosity) {
+ dpctl_dump_ipf(dpif, dpctl_p);
+ }
+
+ dpif_close(dpif);
+ }
+
+ return error;
+}
+
/* Undocumented commands for unit testing. */
static int
/* Parse flow key. */
ofpbuf_init(&keybuf, 0);
- error = odp_flow_from_string(argv[1], &port_names, &keybuf, NULL);
+ char *error_s;
+ error = odp_flow_from_string(argv[1], &port_names, &keybuf, NULL,
+ &error_s);
if (error) {
- dpctl_error(dpctl_p, error, "odp_flow_key_from_string");
+ dpctl_error(dpctl_p, error, "odp_flow_key_from_string (%s)", error_s);
+ free(error_s);
goto out_freekeybuf;
}
&s, dpctl_p->verbosity);
dpctl_print(dpctl_p, "input flow: %s\n", ds_cstr(&s));
- error = odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow);
+ error = odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow, &error_s);
if (error) {
- dpctl_error(dpctl_p, error, "odp_flow_key_to_flow");
+ dpctl_error(dpctl_p, error, "odp_flow_key_to_flow failed (%s)",
+ error_s ? error_s : "reason unknown");
+ free(error_s);
goto out_freekeybuf;
}
{ "del-if", "dp iface...", 2, INT_MAX, dpctl_del_if, DP_RW },
{ "set-if", "dp iface...", 2, INT_MAX, dpctl_set_if, DP_RW },
{ "dump-dps", "", 0, 0, dpctl_dump_dps, DP_RO },
- { "show", "[dp...]", 0, INT_MAX, dpctl_show, DP_RO },
- { "dump-flows", "[dp] [filter=..] [type=..]",
- 0, 3, dpctl_dump_flows, DP_RO },
+ { "show", "[-s] [dp...]", 0, INT_MAX, dpctl_show, DP_RO },
+ { "dump-flows", "[-m] [--names] [dp] [filter=..] [type=..] [pmd=..]",
+ 0, 6, dpctl_dump_flows, DP_RO },
{ "add-flow", "[dp] flow actions", 2, 3, dpctl_add_flow, DP_RW },
{ "mod-flow", "[dp] flow actions", 2, 3, dpctl_mod_flow, DP_RW },
{ "get-flow", "[dp] ufid", 1, 2, dpctl_get_flow, DP_RO },
{ "del-flow", "[dp] flow", 1, 2, dpctl_del_flow, DP_RW },
- { "del-flows", "[dp]", 0, 1, dpctl_del_flows, DP_RW },
- { "dump-conntrack", "[dp] [zone=N]", 0, 2, dpctl_dump_conntrack, DP_RO },
+ { "add-flows", "[dp] file", 1, 2, dpctl_process_flows, DP_RW },
+ { "mod-flows", "[dp] file", 1, 2, dpctl_process_flows, DP_RW },
+ { "del-flows", "[dp] [file]", 0, 2, dpctl_del_flows, DP_RW },
+ { "dump-conntrack", "[-m] [-s] [dp] [zone=N]",
+ 0, 4, dpctl_dump_conntrack, DP_RO },
{ "flush-conntrack", "[dp] [zone=N] [ct-tuple]", 0, 3,
dpctl_flush_conntrack, DP_RW },
- { "ct-stats-show", "[dp] [zone=N] [verbose]",
+ { "ct-stats-show", "[dp] [zone=N]",
0, 3, dpctl_ct_stats_show, DP_RO },
{ "ct-bkts", "[dp] [gt=N]", 0, 2, dpctl_ct_bkts, DP_RO },
+ { "ct-set-maxconns", "[dp] maxconns", 1, 2, dpctl_ct_set_maxconns,
+ DP_RW },
+ { "ct-get-maxconns", "[dp]", 0, 1, dpctl_ct_get_maxconns, DP_RO },
+ { "ct-get-nconns", "[dp]", 0, 1, dpctl_ct_get_nconns, DP_RO },
+ { "ct-enable-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_enable_tcp_seq_chk,
+ DP_RW },
+ { "ct-disable-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_disable_tcp_seq_chk,
+ DP_RW },
+ { "ct-get-tcp-seq-chk", "[dp]", 0, 1, dpctl_ct_get_tcp_seq_chk, DP_RO },
+ { "ct-set-limits", "[dp] [default=L] [zone=N,limit=L]...", 1, INT_MAX,
+ dpctl_ct_set_limits, DP_RO },
+ { "ct-del-limits", "[dp] zone=N1[,N2]...", 1, 2, dpctl_ct_del_limits,
+ DP_RO },
+ { "ct-get-limits", "[dp] [zone=N1[,N2]...]", 0, 2, dpctl_ct_get_limits,
+ DP_RO },
+ { "ipf-set-enabled", "[dp] v4|v6", 1, 2, dpctl_ipf_set_enabled, DP_RW },
+ { "ipf-set-disabled", "[dp] v4|v6", 1, 2, dpctl_ipf_set_disabled, DP_RW },
+ { "ipf-set-min-frag", "[dp] v4|v6 minfragment", 2, 3,
+ dpctl_ipf_set_min_frag, DP_RW },
+ { "ipf-set-max-nfrags", "[dp] maxfrags", 1, 2,
+ dpctl_ipf_set_max_nfrags, DP_RW },
+ { "ipf-get-status", "[dp]", 0, 1, dpctl_ct_ipf_get_status,
+ DP_RO },
{ "help", "", 0, INT_MAX, dpctl_help, DP_RO },
{ "list-commands", "", 0, INT_MAX, dpctl_list_commands, DP_RO },
if (!set_names) {
dpctl_p.names = dpctl_p.verbosity > 0;
}
- VLOG_INFO("set_names=%d verbosity=%d names=%d", set_names,
- dpctl_p.verbosity, dpctl_p.names);
if (!error) {
dpctl_command_handler *handler = (dpctl_command_handler *) aux;