/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 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 "command-line.h"
#include "compiler.h"
+#include "ct-dpif.h"
#include "dirs.h"
#include "dpctl.h"
#include "dpif.h"
-#include "dpif-netdev.h"
#include "dynamic-string.h"
#include "flow.h"
#include "match.h"
#include "netdev.h"
+#include "netdev-dpdk.h"
#include "netlink.h"
#include "odp-util.h"
#include "ofp-parse.h"
dpctl_command_handler *handler;
};
static const struct dpctl_command *get_all_dpctl_commands(void);
+static void dpctl_print(struct dpctl_params *dpctl_p, const char *fmt, ...)
+ OVS_PRINTF_FORMAT(2, 3);
+static void dpctl_error(struct dpctl_params* dpctl_p, int err_no,
+ const char *fmt, ...)
+ OVS_PRINTF_FORMAT(3, 4);
static void
dpctl_puts(struct dpctl_params *dpctl_p, bool error, const char *string)
"%s: can't change type from %s to %s",
name, type, value);
error = EINVAL;
+ goto next_destroy_args;
}
} else if (!strcmp(key, "port_no")) {
if (port_no != u32_to_odp(atoi(value))) {
dpctl_error(dpctl_p, 0, "%s: can't change port number from"
" %"PRIu32" to %d", name, port_no, atoi(value));
error = EINVAL;
+ goto next_destroy_args;
}
} else if (value[0] == '\0') {
smap_remove(&args, key);
}
/* Update configuration. */
- error = netdev_set_config(netdev, &args, NULL);
+ char *err_s = NULL;
+ error = netdev_set_config(netdev, &args, &err_s);
+ if (err_s || error) {
+ dpctl_error(dpctl_p, error, "%s",
+ err_s ? err_s : "Error updating configuration");
+ free(err_s);
+ }
if (error) {
goto next_destroy_args;
}
}
}
}
- dpif_close(dpif);
+}
+
+typedef void (*dps_for_each_cb)(struct dpif *, struct dpctl_params *);
+
+static int
+dps_for_each(struct dpctl_params *dpctl_p, dps_for_each_cb cb)
+{
+ struct sset dpif_names = SSET_INITIALIZER(&dpif_names),
+ dpif_types = SSET_INITIALIZER(&dpif_types);
+ int error, openerror = 0, enumerror = 0;
+ const char *type, *name;
+ bool at_least_one = false;
+
+ dp_enumerate_types(&dpif_types);
+
+ SSET_FOR_EACH (type, &dpif_types) {
+ error = dp_enumerate_names(type, &dpif_names);
+ if (error) {
+ enumerror = error;
+ }
+
+ SSET_FOR_EACH (name, &dpif_names) {
+ struct dpif *dpif;
+
+ at_least_one = true;
+ error = dpif_open(name, type, &dpif);
+ if (!error) {
+ cb(dpif, dpctl_p);
+ dpif_close(dpif);
+ } else {
+ openerror = error;
+ dpctl_error(dpctl_p, error, "opening datapath %s failed",
+ name);
+ }
+ }
+ }
+
+ sset_destroy(&dpif_names);
+ sset_destroy(&dpif_types);
+
+ /* If there has been an error while opening a datapath it should be
+ * reported. Otherwise, we want to ignore the errors generated by
+ * dp_enumerate_names() if at least one datapath has been discovered,
+ * because they're not interesting for the user. This happens, for
+ * example, if OVS is using a userspace datapath and the kernel module
+ * is not loaded. */
+ if (openerror) {
+ return openerror;
+ } else {
+ return at_least_one ? 0 : enumerror;
+ }
}
static int
error = parsed_dpif_open(name, false, &dpif);
if (!error) {
show_dpif(dpif, dpctl_p);
+ dpif_close(dpif);
} else {
dpctl_error(dpctl_p, error, "opening datapath %s failed",
name);
}
}
} else {
- struct sset types;
- const char *type;
-
- sset_init(&types);
- dp_enumerate_types(&types);
- SSET_FOR_EACH (type, &types) {
- struct sset names;
- const char *name;
-
- sset_init(&names);
- error = dp_enumerate_names(type, &names);
- if (error) {
- lasterror = error;
- goto next;
- }
- SSET_FOR_EACH (name, &names) {
- struct dpif *dpif;
-
- error = dpif_open(name, type, &dpif);
- if (!error) {
- show_dpif(dpif, dpctl_p);
- } else {
- dpctl_error(dpctl_p, error, "opening datapath %s failed",
- name);
- lasterror = error;
- }
- }
-next:
- sset_destroy(&names);
- }
- sset_destroy(&types);
+ lasterror = dps_for_each(dpctl_p, show_dpif);
}
+
return lasterror;
}
+static void
+dump_cb(struct dpif *dpif, struct dpctl_params *dpctl_p)
+{
+ dpctl_print(dpctl_p, "%s\n", dpif_name(dpif));
+}
+
static int
dpctl_dump_dps(int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
struct dpctl_params *dpctl_p)
{
- struct sset dpif_names, dpif_types;
- const char *type;
- int error, lasterror = 0;
-
- sset_init(&dpif_names);
- sset_init(&dpif_types);
- dp_enumerate_types(&dpif_types);
-
- SSET_FOR_EACH (type, &dpif_types) {
- const char *name;
-
- error = dp_enumerate_names(type, &dpif_names);
- if (error) {
- lasterror = error;
- }
-
- SSET_FOR_EACH (name, &dpif_names) {
- struct dpif *dpif;
- if (!dpif_open(name, type, &dpif)) {
- dpctl_print(dpctl_p, "%s\n", dpif_name(dpif));
- dpif_close(dpif);
- }
- }
- }
-
- sset_destroy(&dpif_names);
- sset_destroy(&dpif_types);
- return lasterror;
+ return dps_for_each(dpctl_p, dump_cb);
}
static void
format_dpif_flow(struct ds *ds, const struct dpif_flow *f, struct hmap *ports,
struct dpctl_params *dpctl_p)
{
- if (dpctl_p->verbosity) {
- if (f->ufid_present) {
- odp_format_ufid(&f->ufid, ds);
- ds_put_cstr(ds, ", ");
- } else {
- ds_put_cstr(ds, "ufid:<empty>, ");
- }
+ if (dpctl_p->verbosity && f->ufid_present) {
+ odp_format_ufid(&f->ufid, ds);
+ ds_put_cstr(ds, ", ");
}
odp_flow_format(f->key, f->key_len, f->mask, f->mask_len, ports, ds,
dpctl_p->verbosity);
}
}
+ /* 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
+ * pmd threads), while NON_PMD_CORE_ID refers to every non pmd threads
+ * in the userspace datapath */
+ BUILD_ASSERT(PMD_ID_NULL != NON_PMD_CORE_ID);
+
ds_init(&ds);
flow_dump = dpif_flow_dump_create(dpif, false);
flow_dump_thread = dpif_flow_dump_thread_create(flow_dump);
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.masks, &flow);
+ odp_flow_key_to_mask(f.mask, f.mask_len, f.key, f.key_len,
+ &wc, &flow);
match_init(&match, &flow, &wc);
match_init(&match_filter, &flow_filter, &wc);
FOR_EACH_CORE_ON_NUMA (iter, dump) {
if (ovs_numa_core_is_pinned(iter->core_id)) {
error = dpif_flow_put(dpif, flags,
- ofpbuf_data(&key), ofpbuf_size(&key),
- ofpbuf_size(&mask) == 0 ? NULL : ofpbuf_data(&mask),
- ofpbuf_size(&mask), ofpbuf_data(&actions),
- ofpbuf_size(&actions), ufid_present ? &ufid : NULL,
+ key.data, key.size,
+ mask.size == 0 ? NULL : mask.data,
+ mask.size, actions.data,
+ actions.size, ufid_present ? &ufid : NULL,
iter->core_id, dpctl_p->print_statistics ? &stats : NULL);
}
}
}
} else {
error = dpif_flow_put(dpif, flags,
- ofpbuf_data(&key), ofpbuf_size(&key),
- ofpbuf_size(&mask) == 0 ? NULL : ofpbuf_data(&mask),
- ofpbuf_size(&mask), ofpbuf_data(&actions),
- ofpbuf_size(&actions), ufid_present ? &ufid : NULL,
+ key.data, key.size,
+ mask.size == 0 ? NULL : mask.data,
+ mask.size, actions.data,
+ actions.size, ufid_present ? &ufid : NULL,
PMD_ID_NULL, dpctl_p->print_statistics ? &stats : NULL);
}
if (error) {
FOR_EACH_CORE_ON_NUMA (iter, dump) {
if (ovs_numa_core_is_pinned(iter->core_id)) {
- error = dpif_flow_del(dpif, ofpbuf_data(&key),
- ofpbuf_size(&key), ufid_present ? &ufid : NULL,
+ error = dpif_flow_del(dpif, key.data,
+ key.size, ufid_present ? &ufid : NULL,
iter->core_id, dpctl_p->print_statistics ? &stats : NULL);
}
}
error = EINVAL;
}
} else {
- error = dpif_flow_del(dpif, ofpbuf_data(&key), ofpbuf_size(&key),
+ error = dpif_flow_del(dpif, key.data, key.size,
ufid_present ? &ufid : NULL, PMD_ID_NULL,
dpctl_p->print_statistics ? &stats : NULL);
}
return 0;
}
+
+static int
+dpctl_dump_conntrack(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct ct_dpif_dump_state *dump;
+ struct ct_dpif_entry cte;
+ uint16_t zone, *pzone = NULL;
+ struct dpif *dpif;
+ char *name;
+ int error;
+
+ if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) {
+ pzone = &zone;
+ argc--;
+ }
+ name = (argc == 2) ? xstrdup(argv[1]) : 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;
+ }
+
+ error = ct_dpif_dump_start(dpif, &dump, pzone);
+ if (error) {
+ dpctl_error(dpctl_p, error, "starting conntrack dump");
+ dpif_close(dpif);
+ return error;
+ }
+
+ while (!ct_dpif_dump_next(dump, &cte)) {
+ struct ds s = DS_EMPTY_INITIALIZER;
+
+ ct_dpif_format_entry(&cte, &s, dpctl_p->verbosity,
+ dpctl_p->print_statistics);
+ ct_dpif_entry_uninit(&cte);
+
+ dpctl_print(dpctl_p, "%s\n", ds_cstr(&s));
+ ds_destroy(&s);
+ }
+ ct_dpif_dump_done(dump);
+ dpif_close(dpif);
+ return error;
+}
+
+static int
+dpctl_flush_conntrack(int argc, const char *argv[],
+ struct dpctl_params *dpctl_p)
+{
+ struct dpif *dpif;
+ uint16_t zone, *pzone = NULL;
+ char *name;
+ int error;
+
+ if (argc > 1 && ovs_scan(argv[argc - 1], "zone=%"SCNu16, &zone)) {
+ pzone = &zone;
+ argc--;
+ }
+ name = (argc == 2) ? xstrdup(argv[1]) : 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;
+ }
+
+ error = ct_dpif_flush(dpif, pzone);
+
+ dpif_close(dpif);
+ return error;
+}
\f
/* Undocumented commands for unit testing. */
}
ds_init(&s);
- format_odp_actions(&s, ofpbuf_data(&actions), ofpbuf_size(&actions));
+ format_odp_actions(&s, actions.data, actions.size);
dpctl_print(dpctl_p, "%s\n", ds_cstr(&s));
ds_destroy(&s);
}
ds_clear(&s);
- odp_flow_format(ofpbuf_data(&keybuf), ofpbuf_size(&keybuf), NULL, 0, NULL,
+ odp_flow_format(keybuf.data, keybuf.size, NULL, 0, NULL,
&s, dpctl_p->verbosity);
dpctl_print(dpctl_p, "input flow: %s\n", ds_cstr(&s));
- error = odp_flow_key_to_flow(ofpbuf_data(&keybuf), ofpbuf_size(&keybuf),
- &flow);
+ error = odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow);
if (error) {
dpctl_error(dpctl_p, error, "odp_flow_key_to_flow");
goto out_freekeybuf;
if (dpctl_p->verbosity) {
ds_clear(&s);
- format_odp_actions(&s, ofpbuf_data(&odp_actions),
- ofpbuf_size(&odp_actions));
+ format_odp_actions(&s, odp_actions.data, odp_actions.size);
dpctl_print(dpctl_p, "input actions: %s\n", ds_cstr(&s));
}
hmap_init(&actions_per_flow);
- NL_ATTR_FOR_EACH (a, left, ofpbuf_data(&odp_actions),
- ofpbuf_size(&odp_actions)) {
+ NL_ATTR_FOR_EACH (a, left, odp_actions.data, odp_actions.size) {
const struct ovs_action_push_vlan *push;
switch(nl_attr_type(a)) {
case OVS_ACTION_ATTR_POP_VLAN:
for (i = 0; i < n_afs; i++) {
struct actions_for_flow *af = afs[i];
- sort_output_actions(ofpbuf_data(&af->actions),
- ofpbuf_size(&af->actions));
+ sort_output_actions(af->actions.data, af->actions.size);
if (af->flow.vlan_tci != htons(0)) {
dpctl_print(dpctl_p, "vlan(vid=%"PRIu16",pcp=%d): ",
}
ds_clear(&s);
- format_odp_actions(&s, ofpbuf_data(&af->actions),
- ofpbuf_size(&af->actions));
- dpctl_print(dpctl_p, ds_cstr(&s));
+ format_odp_actions(&s, af->actions.data, af->actions.size);
+ dpctl_puts(dpctl_p, false, ds_cstr(&s));
ofpbuf_uninit(&af->actions);
free(af);
{ "get-flow", "get-flow [dp] ufid", 1, 2, dpctl_get_flow },
{ "del-flow", "del-flow [dp] flow", 1, 2, dpctl_del_flow },
{ "del-flows", "[dp]", 0, 1, dpctl_del_flows },
+ { "dump-conntrack", "[dp] [zone=N]", 0, 2, dpctl_dump_conntrack },
+ { "flush-conntrack", "[dp] [zone=N]", 0, 2, dpctl_flush_conntrack },
{ "help", "", 0, INT_MAX, dpctl_help },
{ "list-commands", "", 0, INT_MAX, dpctl_list_commands },
void *aux)
{
struct ds ds = DS_EMPTY_INITIALIZER;
- struct dpctl_params dpctl_p;
- bool opt_parse_err = false;
-
- dpctl_command_handler *handler = (dpctl_command_handler *) aux;
+ bool error = false;
- dpctl_p.print_statistics = false;
- dpctl_p.zero_statistics = false;
- dpctl_p.may_create = false;
- dpctl_p.verbosity = 0;
+ struct dpctl_params dpctl_p = {
+ .is_appctl = true,
+ .output = dpctl_unixctl_print,
+ .aux = &ds,
+ };
/* Parse options (like getopt). Unfortunately it does
* not seem a good idea to call getopt_long() here, since it uses global
* variables */
- while (argc > 1 && !opt_parse_err) {
+ while (argc > 1 && !error) {
const char *arg = argv[1];
if (!strncmp(arg, "--", 2)) {
/* Long option */
dpctl_p.verbosity++;
} else {
ds_put_format(&ds, "Unrecognized option %s", argv[1]);
- opt_parse_err = true;
+ error = true;
}
} else if (arg[0] == '-' && arg[1] != '\0') {
/* Short option[s] */
const char *opt = &arg[1];
- while (*opt && !opt_parse_err) {
+ while (*opt && !error) {
switch (*opt) {
case 'm':
dpctl_p.verbosity++;
break;
default:
ds_put_format(&ds, "Unrecognized option -%c", *opt);
- opt_parse_err = true;
+ error = true;
break;
}
opt++;
break;
}
- if (opt_parse_err) {
+ if (error) {
break;
}
argv++;
argc--;
}
- if (!opt_parse_err) {
- dpctl_p.is_appctl = true;
- dpctl_p.output = dpctl_unixctl_print;
- dpctl_p.aux = &ds;
-
- handler(argc, argv, &dpctl_p);
+ if (!error) {
+ dpctl_command_handler *handler = (dpctl_command_handler *) aux;
+ error = handler(argc, argv, &dpctl_p) != 0;
}
- unixctl_command_reply(conn, ds_cstr(&ds));
+ if (error) {
+ unixctl_command_reply_error(conn, ds_cstr(&ds));
+ } else {
+ unixctl_command_reply(conn, ds_cstr(&ds));
+ }
ds_destroy(&ds);
}
const struct dpctl_command *p;
for (p = all_commands; p->name != NULL; p++) {
- char *cmd_name = xasprintf("dpctl/%s", p->name);
- unixctl_command_register(cmd_name, "", p->min_args, p->max_args,
- dpctl_unixctl_handler, p->handler);
- free(cmd_name);
+ if (strcmp(p->name, "help")) {
+ char *cmd_name = xasprintf("dpctl/%s", p->name);
+ unixctl_command_register(cmd_name, "", p->min_args, p->max_args,
+ dpctl_unixctl_handler, p->handler);
+ free(cmd_name);
+ }
}
}