2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
2011-01-04 17:00:36 -08:00
|
|
|
|
* Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07: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:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* 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.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <inttypes.h>
|
2010-05-26 10:05:19 -07:00
|
|
|
|
#include <sys/socket.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <net/if.h>
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
|
|
#include "command-line.h"
|
|
|
|
|
#include "compiler.h"
|
|
|
|
|
#include "dirs.h"
|
|
|
|
|
#include "dpif.h"
|
|
|
|
|
#include "dynamic-string.h"
|
|
|
|
|
#include "netdev.h"
|
|
|
|
|
#include "odp-util.h"
|
2010-12-03 14:41:38 -08:00
|
|
|
|
#include "shash.h"
|
2009-08-04 15:13:40 -07:00
|
|
|
|
#include "svec.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "timeval.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "vlog.h"
|
2010-07-16 11:02:49 -07:00
|
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(dpctl);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2009-10-23 11:49:39 -07:00
|
|
|
|
static const struct command all_commands[];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
static void usage(void) NO_RETURN;
|
|
|
|
|
static void parse_options(int argc, char *argv[]);
|
|
|
|
|
|
2009-10-23 11:49:39 -07:00
|
|
|
|
int
|
|
|
|
|
main(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
set_program_name(argv[0]);
|
|
|
|
|
parse_options(argc, argv);
|
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
2009-10-23 11:49:39 -07:00
|
|
|
|
run_command(argc - optind, argv + optind, all_commands);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
parse_options(int argc, char *argv[])
|
|
|
|
|
{
|
2010-01-04 18:55:00 -08:00
|
|
|
|
enum {
|
|
|
|
|
OPT_DUMMY = UCHAR_MAX + 1,
|
|
|
|
|
VLOG_OPTION_ENUMS
|
|
|
|
|
};
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static struct option long_options[] = {
|
|
|
|
|
{"timeout", required_argument, 0, 't'},
|
|
|
|
|
{"help", no_argument, 0, 'h'},
|
|
|
|
|
{"version", no_argument, 0, 'V'},
|
2010-01-04 18:55:00 -08:00
|
|
|
|
VLOG_LONG_OPTIONS,
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{0, 0, 0, 0},
|
|
|
|
|
};
|
|
|
|
|
char *short_options = long_options_to_short_options(long_options);
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
unsigned long int timeout;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
c = getopt_long(argc, argv, short_options, long_options, NULL);
|
|
|
|
|
if (c == -1) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
case 't':
|
|
|
|
|
timeout = strtoul(optarg, NULL, 10);
|
|
|
|
|
if (timeout <= 0) {
|
|
|
|
|
ovs_fatal(0, "value %s on -t or --timeout is not at least 1",
|
|
|
|
|
optarg);
|
|
|
|
|
} else {
|
|
|
|
|
time_alarm(timeout);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
|
usage();
|
|
|
|
|
|
|
|
|
|
case 'V':
|
|
|
|
|
OVS_PRINT_VERSION(0, 0);
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
|
2010-01-04 18:55:00 -08:00
|
|
|
|
VLOG_OPTION_HANDLERS
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(short_options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
usage(void)
|
|
|
|
|
{
|
|
|
|
|
printf("%s: Open vSwitch datapath management utility\n"
|
|
|
|
|
"usage: %s [OPTIONS] COMMAND [ARG...]\n"
|
|
|
|
|
" add-dp DP [IFACE...] add new datapath DP (with IFACEs)\n"
|
|
|
|
|
" del-dp DP delete local datapath DP\n"
|
|
|
|
|
" add-if DP IFACE... add each IFACE as a port on DP\n"
|
|
|
|
|
" del-if DP IFACE... delete each IFACE from DP\n"
|
2009-08-04 15:13:40 -07:00
|
|
|
|
" dump-dps display names of all datapaths\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
" show show basic info on all datapaths\n"
|
|
|
|
|
" show DP... show basic info on each DP\n"
|
|
|
|
|
" dump-flows DP display flows in DP\n"
|
2010-10-08 16:36:13 -07:00
|
|
|
|
" del-flows DP delete all flows from DP\n",
|
2009-07-08 13:19:16 -07:00
|
|
|
|
program_name, program_name);
|
|
|
|
|
vlog_usage();
|
|
|
|
|
printf("\nOther options:\n"
|
|
|
|
|
" -t, --timeout=SECS give up after SECS seconds\n"
|
|
|
|
|
" -h, --help display this help message\n"
|
|
|
|
|
" -V, --version display version information\n");
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void run(int retval, const char *message, ...)
|
|
|
|
|
PRINTF_FORMAT(2, 3);
|
|
|
|
|
|
|
|
|
|
static void run(int retval, const char *message, ...)
|
|
|
|
|
{
|
|
|
|
|
if (retval) {
|
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "%s: ", program_name);
|
|
|
|
|
va_start(args, message);
|
|
|
|
|
vfprintf(stderr, message, args);
|
|
|
|
|
va_end(args);
|
|
|
|
|
if (retval == EOF) {
|
|
|
|
|
fputs(": unexpected end of file\n", stderr);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, ": %s\n", strerror(retval));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_add_if(int argc, char *argv[]);
|
|
|
|
|
|
|
|
|
|
static int if_up(const char *netdev_name)
|
|
|
|
|
{
|
|
|
|
|
struct netdev *netdev;
|
|
|
|
|
int retval;
|
|
|
|
|
|
2010-01-12 16:01:43 -05:00
|
|
|
|
retval = netdev_open_default(netdev_name, &netdev);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (!retval) {
|
|
|
|
|
retval = netdev_turn_flags_on(netdev, NETDEV_UP, true);
|
|
|
|
|
netdev_close(netdev);
|
|
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
static int
|
|
|
|
|
parsed_dpif_open(const char *arg_, bool create, struct dpif **dpifp)
|
|
|
|
|
{
|
|
|
|
|
int result;
|
|
|
|
|
char *name, *type;
|
|
|
|
|
|
|
|
|
|
dp_parse_name(arg_, &name, &type);
|
|
|
|
|
|
|
|
|
|
if (create) {
|
|
|
|
|
result = dpif_create(name, type, dpifp);
|
|
|
|
|
} else {
|
|
|
|
|
result = dpif_open(name, type, dpifp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(name);
|
|
|
|
|
free(type);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_add_dp(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2010-01-22 14:37:10 -05:00
|
|
|
|
run(parsed_dpif_open(argv[1], true, &dpif), "add_dp");
|
2009-06-16 10:09:10 -07:00
|
|
|
|
dpif_close(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (argc > 2) {
|
|
|
|
|
do_add_if(argc, argv);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_del_dp(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2010-01-22 14:37:10 -05:00
|
|
|
|
run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
|
2009-06-16 10:09:10 -07:00
|
|
|
|
run(dpif_delete(dpif), "del_dp");
|
|
|
|
|
dpif_close(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_add_if(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
bool failure = false;
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int i;
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
for (i = 2; i < argc; i++) {
|
|
|
|
|
char *save_ptr = NULL;
|
2010-12-03 14:41:38 -08:00
|
|
|
|
struct netdev_options options;
|
|
|
|
|
struct netdev *netdev;
|
|
|
|
|
struct shash args;
|
|
|
|
|
char *option;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int error;
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
options.name = strtok_r(argv[i], ",", &save_ptr);
|
|
|
|
|
options.type = "system";
|
|
|
|
|
options.args = &args;
|
|
|
|
|
options.ethertype = NETDEV_ETH_TYPE_NONE;
|
|
|
|
|
|
|
|
|
|
if (!options.name) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ovs_error(0, "%s is not a valid network device name", argv[i]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
shash_init(&args);
|
2011-03-04 12:46:37 -08:00
|
|
|
|
while ((option = strtok_r(NULL, ",", &save_ptr)) != NULL) {
|
2010-12-03 14:41:38 -08:00
|
|
|
|
char *save_ptr_2 = NULL;
|
|
|
|
|
char *key, *value;
|
|
|
|
|
|
|
|
|
|
key = strtok_r(option, "=", &save_ptr_2);
|
|
|
|
|
value = strtok_r(NULL, "", &save_ptr_2);
|
|
|
|
|
if (!value) {
|
|
|
|
|
value = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strcmp(key, "type")) {
|
|
|
|
|
options.type = value;
|
|
|
|
|
} else if (!shash_add_once(&args, key, value)) {
|
|
|
|
|
ovs_error(0, "duplicate \"%s\" option", key);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-03 14:41:38 -08:00
|
|
|
|
error = netdev_open(&options, &netdev);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_error(error, "%s: failed to open network device",
|
|
|
|
|
options.name);
|
|
|
|
|
} else {
|
|
|
|
|
error = dpif_port_add(dpif, netdev, NULL);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_error(error, "adding %s to %s failed",
|
|
|
|
|
options.name, argv[1]);
|
|
|
|
|
} else {
|
|
|
|
|
error = if_up(options.name);
|
|
|
|
|
}
|
|
|
|
|
netdev_close(netdev);
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
failure = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-16 10:09:10 -07:00
|
|
|
|
dpif_close(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (failure) {
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
get_port_number(struct dpif *dpif, const char *name, uint16_t *port)
|
|
|
|
|
{
|
2011-01-23 18:48:02 -08:00
|
|
|
|
struct dpif_port dpif_port;
|
2010-12-27 12:46:48 -08:00
|
|
|
|
|
2011-01-23 18:48:02 -08:00
|
|
|
|
if (!dpif_port_query_by_name(dpif, name, &dpif_port)) {
|
|
|
|
|
*port = dpif_port.port_no;
|
|
|
|
|
dpif_port_destroy(&dpif_port);
|
2010-12-27 12:46:48 -08:00
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
ovs_error(0, "no port named %s", name);
|
|
|
|
|
return false;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_del_if(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
bool failure = false;
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int i;
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
for (i = 2; i < argc; i++) {
|
|
|
|
|
const char *name = argv[i];
|
|
|
|
|
uint16_t port;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
if (!name[strspn(name, "0123456789")]) {
|
|
|
|
|
port = atoi(name);
|
2009-06-16 10:09:10 -07:00
|
|
|
|
} else if (!get_port_number(dpif, name, &port)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
failure = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-16 10:09:10 -07:00
|
|
|
|
error = dpif_port_del(dpif, port);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_error(error, "deleting port %s from %s failed", name, argv[1]);
|
|
|
|
|
failure = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-16 10:09:10 -07:00
|
|
|
|
dpif_close(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (failure) {
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
show_dpif(struct dpif *dpif)
|
|
|
|
|
{
|
datapath: Change listing ports to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to add new
features to the kernel vport layer without changing userspace software. In
turn, that means that the odp_port structure must become variable-length.
This does not, however, fit in well with the ODP_PORT_LIST ioctl in its
current form, because that would require userspace to know how much space
to allocate for each port in advance, or to allocate as much space as
could possibly be needed. Neither choice is very attractive.
This commit prepares for a different solution, by replacing ODP_PORT_LIST
by a new ioctl ODP_VPORT_DUMP that retrieves information about a single
vport from the datapath on each call. It is much cleaner to allocate the
maximum amount of space for a single vport than to do so for possibly a
large number of vports.
It would be faster to retrieve a number of vports in batch instead of just
one at a time, but that will naturally happen later when the kernel
datapath interface is changed to use Netlink, so this patch does not bother
with it.
The Netlink version won't need to take the starting port number from
userspace, since Netlink sockets can keep track of that state as part
of their "dump" feature.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2011-01-10 13:12:12 -08:00
|
|
|
|
struct dpif_port_dump dump;
|
2011-01-23 18:48:02 -08:00
|
|
|
|
struct dpif_port dpif_port;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct odp_stats stats;
|
|
|
|
|
|
2009-06-16 11:00:22 -07:00
|
|
|
|
printf("%s:\n", dpif_name(dpif));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (!dpif_get_dp_stats(dpif, &stats)) {
|
2009-11-06 10:43:50 -08:00
|
|
|
|
printf("\tlookups: frags:%llu, hit:%llu, missed:%llu, lost:%llu\n",
|
|
|
|
|
(unsigned long long int) stats.n_frags,
|
|
|
|
|
(unsigned long long int) stats.n_hit,
|
|
|
|
|
(unsigned long long int) stats.n_missed,
|
|
|
|
|
(unsigned long long int) stats.n_lost);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2011-01-23 18:48:02 -08:00
|
|
|
|
DPIF_PORT_FOR_EACH (&dpif_port, &dump, dpif) {
|
|
|
|
|
printf("\tport %u: %s", dpif_port.port_no, dpif_port.name);
|
2010-12-18 01:07:06 -08:00
|
|
|
|
|
2011-01-23 18:48:02 -08:00
|
|
|
|
if (strcmp(dpif_port.type, "system")) {
|
2010-12-29 15:20:33 -08:00
|
|
|
|
struct netdev_options netdev_options;
|
|
|
|
|
struct netdev *netdev;
|
|
|
|
|
int error;
|
|
|
|
|
|
2011-01-23 18:48:02 -08:00
|
|
|
|
printf (" (%s", dpif_port.type);
|
2010-12-29 15:20:33 -08:00
|
|
|
|
|
2011-01-23 18:48:02 -08:00
|
|
|
|
netdev_options.name = dpif_port.name;
|
|
|
|
|
netdev_options.type = dpif_port.type;
|
2010-12-29 15:20:33 -08:00
|
|
|
|
netdev_options.args = NULL;
|
|
|
|
|
netdev_options.ethertype = NETDEV_ETH_TYPE_NONE;
|
|
|
|
|
error = netdev_open(&netdev_options, &netdev);
|
|
|
|
|
if (!error) {
|
|
|
|
|
const struct shash_node **nodes;
|
|
|
|
|
const struct shash *config;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
config = netdev_get_config(netdev);
|
|
|
|
|
nodes = shash_sort(config);
|
|
|
|
|
for (i = 0; i < shash_count(config); i++) {
|
|
|
|
|
const struct shash_node *node = nodes[i];
|
|
|
|
|
printf("%c %s=%s", i ? ',' : ':',
|
|
|
|
|
node->name, (char *) node->data);
|
|
|
|
|
}
|
|
|
|
|
free(nodes);
|
|
|
|
|
|
|
|
|
|
netdev_close(netdev);
|
|
|
|
|
} else {
|
|
|
|
|
printf(": open failed (%s)", strerror(error));
|
|
|
|
|
}
|
|
|
|
|
putchar(')');
|
|
|
|
|
}
|
|
|
|
|
putchar('\n');
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
dpif_close(dpif);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2009-08-04 15:15:48 -07:00
|
|
|
|
do_show(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
bool failure = false;
|
|
|
|
|
if (argc > 1) {
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
|
const char *name = argv[i];
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int error;
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
error = parsed_dpif_open(name, false, &dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (!error) {
|
2009-06-16 10:09:10 -07:00
|
|
|
|
show_dpif(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
|
|
|
|
ovs_error(error, "opening datapath %s failed", name);
|
|
|
|
|
failure = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2011-01-05 15:33:09 -08:00
|
|
|
|
struct svec types;
|
|
|
|
|
const char *type;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
svec_init(&types);
|
|
|
|
|
dp_enumerate_types(&types);
|
|
|
|
|
SVEC_FOR_EACH (i, type, &types) {
|
|
|
|
|
struct svec names;
|
|
|
|
|
const char *name;
|
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
|
|
svec_init(&names);
|
|
|
|
|
if (dp_enumerate_names(type, &names)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
failure = true;
|
2011-01-05 15:33:09 -08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
SVEC_FOR_EACH (j, name, &names) {
|
|
|
|
|
struct dpif *dpif;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
error = dpif_open(name, type, &dpif);
|
|
|
|
|
if (!error) {
|
|
|
|
|
show_dpif(dpif);
|
|
|
|
|
} else {
|
|
|
|
|
ovs_error(error, "opening datapath %s failed", name);
|
|
|
|
|
failure = true;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2011-01-05 15:33:09 -08:00
|
|
|
|
svec_destroy(&names);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2011-01-05 15:33:09 -08:00
|
|
|
|
svec_destroy(&types);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
if (failure) {
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-04 15:13:40 -07:00
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_dump_dps(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-08-04 15:13:40 -07:00
|
|
|
|
{
|
2010-01-22 14:37:10 -05:00
|
|
|
|
struct svec dpif_names, dpif_types;
|
2009-08-04 15:13:40 -07:00
|
|
|
|
unsigned int i;
|
2010-01-22 14:37:10 -05:00
|
|
|
|
int error = 0;
|
2009-08-04 15:13:40 -07:00
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
svec_init(&dpif_names);
|
|
|
|
|
svec_init(&dpif_types);
|
|
|
|
|
dp_enumerate_types(&dpif_types);
|
2009-08-04 15:13:40 -07:00
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
for (i = 0; i < dpif_types.n; i++) {
|
|
|
|
|
unsigned int j;
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
retval = dp_enumerate_names(dpif_types.names[i], &dpif_names);
|
|
|
|
|
if (retval) {
|
|
|
|
|
error = retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (j = 0; j < dpif_names.n; j++) {
|
|
|
|
|
struct dpif *dpif;
|
|
|
|
|
if (!dpif_open(dpif_names.names[j], dpif_types.names[i], &dpif)) {
|
|
|
|
|
printf("%s\n", dpif_name(dpif));
|
|
|
|
|
dpif_close(dpif);
|
|
|
|
|
}
|
2009-08-04 15:13:40 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
svec_destroy(&dpif_names);
|
|
|
|
|
svec_destroy(&dpif_types);
|
2009-08-04 15:13:40 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_dump_flows(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-01-26 07:11:50 -08:00
|
|
|
|
const struct dpif_flow_stats *stats;
|
2011-01-26 07:03:39 -08:00
|
|
|
|
const struct nlattr *actions;
|
datapath: Change listing flows to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
In turn, that means that flow keys must become variable-length. This does
not, however, fit in well with the ODP_FLOW_LIST ioctl in its current form,
because that would require userspace to know how much space to allocate
for each flow's key in advance, or to allocate as much space as could
possibly be needed. Neither choice is very attractive.
This commit prepares for a different solution, by replacing ODP_FLOW_LIST
by a new ioctl ODP_FLOW_DUMP that retrieves a single flow from the datapath
on each call. It is much cleaner to allocate the maximum amount of space
for a single flow key than to do so for possibly a very large number of
flow keys.
As a side effect, this patch also fixes a race condition that sometimes
made "ovs-dpctl dump-flows" print an error: previously, flows were listed
and then their actions were retrieved, which left a window in which
ovs-vswitchd could delete the flow. Now dumping a flow and its actions is
a single step, closing that window.
Dumping all of the flows in a datapath is no longer an atomic step, so now
it is possible to miss some flows or see a single flow twice during
iteration, if the flow table is modified by another process. It doesn't
look like this should be a problem for ovs-vswitchd.
It would be faster to retrieve a number of flows in batch instead of just
one at a time, but that will naturally happen later when the kernel
datapath interface is changed to use Netlink, so this patch does not bother
with it.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2010-12-28 10:39:52 -08:00
|
|
|
|
struct dpif_flow_dump dump;
|
2011-01-26 07:03:39 -08:00
|
|
|
|
const struct nlattr *key;
|
|
|
|
|
size_t actions_len;
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2011-01-26 07:03:39 -08:00
|
|
|
|
size_t key_len;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct ds ds;
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
ds_init(&ds);
|
datapath: Change listing flows to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
In turn, that means that flow keys must become variable-length. This does
not, however, fit in well with the ODP_FLOW_LIST ioctl in its current form,
because that would require userspace to know how much space to allocate
for each flow's key in advance, or to allocate as much space as could
possibly be needed. Neither choice is very attractive.
This commit prepares for a different solution, by replacing ODP_FLOW_LIST
by a new ioctl ODP_FLOW_DUMP that retrieves a single flow from the datapath
on each call. It is much cleaner to allocate the maximum amount of space
for a single flow key than to do so for possibly a very large number of
flow keys.
As a side effect, this patch also fixes a race condition that sometimes
made "ovs-dpctl dump-flows" print an error: previously, flows were listed
and then their actions were retrieved, which left a window in which
ovs-vswitchd could delete the flow. Now dumping a flow and its actions is
a single step, closing that window.
Dumping all of the flows in a datapath is no longer an atomic step, so now
it is possible to miss some flows or see a single flow twice during
iteration, if the flow table is modified by another process. It doesn't
look like this should be a problem for ovs-vswitchd.
It would be faster to retrieve a number of flows in batch instead of just
one at a time, but that will naturally happen later when the kernel
datapath interface is changed to use Netlink, so this patch does not bother
with it.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2010-12-28 10:39:52 -08:00
|
|
|
|
dpif_flow_dump_start(&dump, dpif);
|
2011-01-26 07:03:39 -08:00
|
|
|
|
while (dpif_flow_dump_next(&dump, &key, &key_len,
|
|
|
|
|
&actions, &actions_len, &stats)) {
|
datapath: Change listing flows to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
In turn, that means that flow keys must become variable-length. This does
not, however, fit in well with the ODP_FLOW_LIST ioctl in its current form,
because that would require userspace to know how much space to allocate
for each flow's key in advance, or to allocate as much space as could
possibly be needed. Neither choice is very attractive.
This commit prepares for a different solution, by replacing ODP_FLOW_LIST
by a new ioctl ODP_FLOW_DUMP that retrieves a single flow from the datapath
on each call. It is much cleaner to allocate the maximum amount of space
for a single flow key than to do so for possibly a very large number of
flow keys.
As a side effect, this patch also fixes a race condition that sometimes
made "ovs-dpctl dump-flows" print an error: previously, flows were listed
and then their actions were retrieved, which left a window in which
ovs-vswitchd could delete the flow. Now dumping a flow and its actions is
a single step, closing that window.
Dumping all of the flows in a datapath is no longer an atomic step, so now
it is possible to miss some flows or see a single flow twice during
iteration, if the flow table is modified by another process. It doesn't
look like this should be a problem for ovs-vswitchd.
It would be faster to retrieve a number of flows in batch instead of just
one at a time, but that will naturally happen later when the kernel
datapath interface is changed to use Netlink, so this patch does not bother
with it.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2010-12-28 10:39:52 -08:00
|
|
|
|
ds_clear(&ds);
|
2011-01-26 07:03:39 -08:00
|
|
|
|
odp_flow_key_format(key, key_len, &ds);
|
|
|
|
|
ds_put_cstr(&ds, ", ");
|
2011-01-26 07:11:50 -08:00
|
|
|
|
dpif_flow_stats_format(stats, &ds);
|
2011-01-26 07:03:39 -08:00
|
|
|
|
ds_put_cstr(&ds, ", actions:");
|
|
|
|
|
format_odp_actions(&ds, actions, actions_len);
|
datapath: Change listing flows to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
In turn, that means that flow keys must become variable-length. This does
not, however, fit in well with the ODP_FLOW_LIST ioctl in its current form,
because that would require userspace to know how much space to allocate
for each flow's key in advance, or to allocate as much space as could
possibly be needed. Neither choice is very attractive.
This commit prepares for a different solution, by replacing ODP_FLOW_LIST
by a new ioctl ODP_FLOW_DUMP that retrieves a single flow from the datapath
on each call. It is much cleaner to allocate the maximum amount of space
for a single flow key than to do so for possibly a very large number of
flow keys.
As a side effect, this patch also fixes a race condition that sometimes
made "ovs-dpctl dump-flows" print an error: previously, flows were listed
and then their actions were retrieved, which left a window in which
ovs-vswitchd could delete the flow. Now dumping a flow and its actions is
a single step, closing that window.
Dumping all of the flows in a datapath is no longer an atomic step, so now
it is possible to miss some flows or see a single flow twice during
iteration, if the flow table is modified by another process. It doesn't
look like this should be a problem for ovs-vswitchd.
It would be faster to retrieve a number of flows in batch instead of just
one at a time, but that will naturally happen later when the kernel
datapath interface is changed to use Netlink, so this patch does not bother
with it.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2010-12-28 10:39:52 -08:00
|
|
|
|
printf("%s\n", ds_cstr(&ds));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
datapath: Change listing flows to use an iterator concept.
One of the goals for Open vSwitch is to decouple kernel and userspace
software, so that either one can be upgraded or rolled back independent of
the other. To do this in full generality, it must be possible to change
the kernel's idea of the flow key separately from the userspace version.
In turn, that means that flow keys must become variable-length. This does
not, however, fit in well with the ODP_FLOW_LIST ioctl in its current form,
because that would require userspace to know how much space to allocate
for each flow's key in advance, or to allocate as much space as could
possibly be needed. Neither choice is very attractive.
This commit prepares for a different solution, by replacing ODP_FLOW_LIST
by a new ioctl ODP_FLOW_DUMP that retrieves a single flow from the datapath
on each call. It is much cleaner to allocate the maximum amount of space
for a single flow key than to do so for possibly a very large number of
flow keys.
As a side effect, this patch also fixes a race condition that sometimes
made "ovs-dpctl dump-flows" print an error: previously, flows were listed
and then their actions were retrieved, which left a window in which
ovs-vswitchd could delete the flow. Now dumping a flow and its actions is
a single step, closing that window.
Dumping all of the flows in a datapath is no longer an atomic step, so now
it is possible to miss some flows or see a single flow twice during
iteration, if the flow table is modified by another process. It doesn't
look like this should be a problem for ovs-vswitchd.
It would be faster to retrieve a number of flows in batch instead of just
one at a time, but that will naturally happen later when the kernel
datapath interface is changed to use Netlink, so this patch does not bother
with it.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
2010-12-28 10:39:52 -08:00
|
|
|
|
dpif_flow_dump_done(&dump);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_destroy(&ds);
|
2009-06-16 10:09:10 -07:00
|
|
|
|
dpif_close(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_del_flows(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2009-06-16 10:09:10 -07:00
|
|
|
|
struct dpif *dpif;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
run(parsed_dpif_open(argv[1], false, &dpif), "opening datapath");
|
2009-06-16 10:09:10 -07:00
|
|
|
|
run(dpif_flow_flush(dpif), "deleting all flows");
|
|
|
|
|
dpif_close(dpif);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-02-11 10:59:47 -08:00
|
|
|
|
do_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
usage();
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-23 11:49:39 -07:00
|
|
|
|
static const struct command all_commands[] = {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{ "add-dp", 1, INT_MAX, do_add_dp },
|
|
|
|
|
{ "del-dp", 1, 1, do_del_dp },
|
|
|
|
|
{ "add-if", 2, INT_MAX, do_add_if },
|
|
|
|
|
{ "del-if", 2, INT_MAX, do_del_if },
|
2009-08-04 15:13:40 -07:00
|
|
|
|
{ "dump-dps", 0, 0, do_dump_dps },
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{ "show", 0, INT_MAX, do_show },
|
|
|
|
|
{ "dump-flows", 1, 1, do_dump_flows },
|
|
|
|
|
{ "del-flows", 1, 1, do_del_flows },
|
|
|
|
|
{ "help", 0, INT_MAX, do_help },
|
|
|
|
|
{ NULL, 0, 0, NULL },
|
|
|
|
|
};
|