2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
Enable OpenFlow 1.0, 1.1, 1.2, and 1.3 by default.
The Open vSwitch software switch now supports all the required features of
OpenFlow 1.0 through 1.3, with one known trivial exception[*]. Enable them
by default in ovs-vswitchd.
For now, ovs-ofctl only enables OpenFlow 1.0 by default. This is
because ovs-ofctl implements command such as "add-flow" as raw
OpenFlow requests, but those requests have subtly different semantics
in different OpenFlow versions. For example:
- In OpenFlow 1.0, a "mod-flow" operation that does not find any
existing flow to modify adds a new flow.
- In OpenFlow 1.1, a "mod-flow" operation that does not find any
existing flow to modify adds a new flow, but only if the
mod-flow did not match on the flow cookie.
- In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a
new flow.
[*] OpenFlow 1.1, but not any earlier or later version of OpenFlow,
requires support for VLANs introduced by Ethertype 0x88a8, but Open
vSwitch does not support this Ethertype.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Reviewed-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
2014-04-30 06:54:09 -07:00
|
|
|
|
* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
|
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>
|
2012-07-12 14:18:05 -07:00
|
|
|
|
#include <ctype.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <inttypes.h>
|
2011-03-30 17:25:36 +02:00
|
|
|
|
#include <sys/socket.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <net/if.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
2014-03-12 14:35:10 -07:00
|
|
|
|
#include <fcntl.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
2010-10-28 17:13:18 -07:00
|
|
|
|
#include "byte-order.h"
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "classifier.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "command-line.h"
|
2011-12-20 15:31:34 -08:00
|
|
|
|
#include "daemon.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "compiler.h"
|
|
|
|
|
#include "dirs.h"
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "dynamic-string.h"
|
2014-02-26 10:44:46 -08:00
|
|
|
|
#include "fatal-signal.h"
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "nx-match.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "odp-util.h"
|
2012-07-03 22:17:14 -07:00
|
|
|
|
#include "ofp-actions.h"
|
2012-01-12 15:48:19 -08:00
|
|
|
|
#include "ofp-errors.h"
|
2012-07-19 23:23:17 -07:00
|
|
|
|
#include "ofp-msgs.h"
|
2010-07-28 15:14:28 -07:00
|
|
|
|
#include "ofp-parse.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "ofp-print.h"
|
2010-05-27 13:14:05 -07:00
|
|
|
|
#include "ofp-util.h"
|
2012-11-19 14:59:32 +09:00
|
|
|
|
#include "ofp-version-opt.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "ofpbuf.h"
|
2011-04-11 15:08:19 -07:00
|
|
|
|
#include "ofproto/ofproto.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "openflow/nicira-ext.h"
|
|
|
|
|
#include "openflow/openflow.h"
|
2012-01-25 16:30:28 -08:00
|
|
|
|
#include "packets.h"
|
2013-09-23 10:28:14 -07:00
|
|
|
|
#include "pcap-file.h"
|
2011-12-20 15:31:34 -08:00
|
|
|
|
#include "poll-loop.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "random.h"
|
2010-01-06 14:35:20 -08:00
|
|
|
|
#include "stream-ssl.h"
|
2012-10-10 13:33:54 -07:00
|
|
|
|
#include "socket-util.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "timeval.h"
|
2011-12-20 15:31:34 -08:00
|
|
|
|
#include "unixctl.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "vconn.h"
|
2010-07-16 11:02:49 -07:00
|
|
|
|
#include "vlog.h"
|
2012-07-12 13:32:47 -07:00
|
|
|
|
#include "meta-flow.h"
|
|
|
|
|
#include "sort.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(ofctl);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-01-03 13:30:45 -08:00
|
|
|
|
/* --strict: Use strict matching for flow mod commands? Additionally governs
|
|
|
|
|
* use of nx_pull_match() instead of nx_pull_match_loose() in parse-nx-match.
|
|
|
|
|
*/
|
2009-10-23 11:49:39 -07:00
|
|
|
|
static bool strict;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-01-26 13:39:38 -08:00
|
|
|
|
/* --readd: If true, on replace-flows, re-add even flows that have not changed
|
2011-07-27 14:58:10 -07:00
|
|
|
|
* (to reset flow counters). */
|
|
|
|
|
static bool readd;
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* -F, --flow-format: Allowed protocols. By default, any protocol is
|
|
|
|
|
* allowed. */
|
|
|
|
|
static enum ofputil_protocol allowed_protocols = OFPUTIL_P_ANY;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
/* -P, --packet-in-format: Packet IN format to use in monitor and snoop
|
|
|
|
|
* commands. Either one of NXPIF_* to force a particular packet_in format, or
|
|
|
|
|
* -1 to let ovs-ofctl choose the default. */
|
|
|
|
|
static int preferred_packet_in_format = -1;
|
|
|
|
|
|
2010-11-23 14:34:21 -08:00
|
|
|
|
/* -m, --more: Additional verbosity for ofp-print functions. */
|
|
|
|
|
static int verbosity;
|
|
|
|
|
|
2012-02-07 16:17:13 -08:00
|
|
|
|
/* --timestamp: Print a timestamp before each received packet on "monitor" and
|
|
|
|
|
* "snoop" command? */
|
|
|
|
|
static bool timestamp;
|
|
|
|
|
|
2014-07-28 10:31:25 -07:00
|
|
|
|
/* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop"
|
|
|
|
|
commands. */
|
|
|
|
|
static char *unixctl_path;
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
/* --sort, --rsort: Sort order. */
|
|
|
|
|
enum sort_order { SORT_ASC, SORT_DESC };
|
|
|
|
|
struct sort_criterion {
|
|
|
|
|
const struct mf_field *field; /* NULL means to sort by priority. */
|
|
|
|
|
enum sort_order order;
|
|
|
|
|
};
|
|
|
|
|
static struct sort_criterion *criteria;
|
|
|
|
|
static size_t n_criteria, allocated_criteria;
|
|
|
|
|
|
2013-07-19 10:04:47 -07:00
|
|
|
|
static const struct command *get_all_commands(void);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
static void usage(void) NO_RETURN;
|
2009-10-23 11:49:39 -07:00
|
|
|
|
static void parse_options(int argc, char *argv[]);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
static bool recv_flow_stats_reply(struct vconn *, ovs_be32 send_xid,
|
|
|
|
|
struct ofpbuf **replyp,
|
|
|
|
|
struct ofputil_flow_stats *,
|
|
|
|
|
struct ofpbuf *ofpacts);
|
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]);
|
2014-05-16 12:10:50 -07:00
|
|
|
|
service_start(&argc, &argv);
|
2009-10-23 11:49:39 -07:00
|
|
|
|
parse_options(argc, argv);
|
2014-02-26 10:44:46 -08:00
|
|
|
|
fatal_ignore_sigpipe();
|
2013-07-19 10:04:47 -07:00
|
|
|
|
run_command(argc - optind, argv + optind, get_all_commands());
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
static void
|
|
|
|
|
add_sort_criterion(enum sort_order order, const char *field)
|
|
|
|
|
{
|
|
|
|
|
struct sort_criterion *sc;
|
|
|
|
|
|
|
|
|
|
if (n_criteria >= allocated_criteria) {
|
|
|
|
|
criteria = x2nrealloc(criteria, &allocated_criteria, sizeof *criteria);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sc = &criteria[n_criteria++];
|
|
|
|
|
if (!field || !strcasecmp(field, "priority")) {
|
|
|
|
|
sc->field = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
sc->field = mf_from_name(field);
|
|
|
|
|
if (!sc->field) {
|
|
|
|
|
ovs_fatal(0, "%s: unknown field name", field);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sc->order = order;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2009-10-23 11:49:39 -07:00
|
|
|
|
parse_options(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
enum {
|
2010-01-04 18:55:00 -08:00
|
|
|
|
OPT_STRICT = UCHAR_MAX + 1,
|
2011-07-27 14:58:10 -07:00
|
|
|
|
OPT_READD,
|
2012-02-07 16:17:13 -08:00
|
|
|
|
OPT_TIMESTAMP,
|
2012-07-12 13:32:47 -07:00
|
|
|
|
OPT_SORT,
|
|
|
|
|
OPT_RSORT,
|
2014-07-28 10:31:25 -07:00
|
|
|
|
OPT_UNIXCTL,
|
2011-12-20 15:31:34 -08:00
|
|
|
|
DAEMON_OPTION_ENUMS,
|
2012-11-19 14:59:32 +09:00
|
|
|
|
OFP_VERSION_OPTION_ENUMS,
|
2010-01-04 18:55:00 -08:00
|
|
|
|
VLOG_OPTION_ENUMS
|
2009-07-08 13:19:16 -07:00
|
|
|
|
};
|
2013-04-23 16:40:56 -07:00
|
|
|
|
static const struct option long_options[] = {
|
2011-05-04 13:49:42 -07:00
|
|
|
|
{"timeout", required_argument, NULL, 't'},
|
|
|
|
|
{"strict", no_argument, NULL, OPT_STRICT},
|
2011-07-27 14:58:10 -07:00
|
|
|
|
{"readd", no_argument, NULL, OPT_READD},
|
2011-05-04 13:49:42 -07:00
|
|
|
|
{"flow-format", required_argument, NULL, 'F'},
|
2011-12-09 15:48:26 -08:00
|
|
|
|
{"packet-in-format", required_argument, NULL, 'P'},
|
2011-05-04 13:49:42 -07:00
|
|
|
|
{"more", no_argument, NULL, 'm'},
|
2012-02-07 16:17:13 -08:00
|
|
|
|
{"timestamp", no_argument, NULL, OPT_TIMESTAMP},
|
2012-07-12 13:32:47 -07:00
|
|
|
|
{"sort", optional_argument, NULL, OPT_SORT},
|
|
|
|
|
{"rsort", optional_argument, NULL, OPT_RSORT},
|
2014-07-28 10:31:25 -07:00
|
|
|
|
{"unixctl", required_argument, NULL, OPT_UNIXCTL},
|
2011-05-04 13:49:42 -07:00
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
2011-12-20 15:31:34 -08:00
|
|
|
|
DAEMON_LONG_OPTIONS,
|
2012-11-19 14:59:32 +09:00
|
|
|
|
OFP_VERSION_LONG_OPTIONS,
|
2010-01-04 18:55:00 -08:00
|
|
|
|
VLOG_LONG_OPTIONS,
|
2011-05-10 09:17:37 -07:00
|
|
|
|
STREAM_SSL_LONG_OPTIONS,
|
2011-05-04 13:49:42 -07:00
|
|
|
|
{NULL, 0, NULL, 0},
|
2009-07-08 13:19:16 -07:00
|
|
|
|
};
|
|
|
|
|
char *short_options = long_options_to_short_options(long_options);
|
2012-11-28 22:22:38 -08:00
|
|
|
|
uint32_t versions;
|
|
|
|
|
enum ofputil_protocol version_protocols;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
Enable OpenFlow 1.0, 1.1, 1.2, and 1.3 by default.
The Open vSwitch software switch now supports all the required features of
OpenFlow 1.0 through 1.3, with one known trivial exception[*]. Enable them
by default in ovs-vswitchd.
For now, ovs-ofctl only enables OpenFlow 1.0 by default. This is
because ovs-ofctl implements command such as "add-flow" as raw
OpenFlow requests, but those requests have subtly different semantics
in different OpenFlow versions. For example:
- In OpenFlow 1.0, a "mod-flow" operation that does not find any
existing flow to modify adds a new flow.
- In OpenFlow 1.1, a "mod-flow" operation that does not find any
existing flow to modify adds a new flow, but only if the
mod-flow did not match on the flow cookie.
- In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a
new flow.
[*] OpenFlow 1.1, but not any earlier or later version of OpenFlow,
requires support for VLANs introduced by Ethertype 0x88a8, but Open
vSwitch does not support this Ethertype.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Reviewed-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
2014-04-30 06:54:09 -07:00
|
|
|
|
/* For now, ovs-ofctl only enables OpenFlow 1.0 by default. This is
|
|
|
|
|
* because ovs-ofctl implements command such as "add-flow" as raw OpenFlow
|
|
|
|
|
* requests, but those requests have subtly different semantics in
|
|
|
|
|
* different OpenFlow versions. For example:
|
|
|
|
|
*
|
|
|
|
|
* - In OpenFlow 1.0, a "mod-flow" operation that does not find any
|
|
|
|
|
* existing flow to modify adds a new flow.
|
|
|
|
|
*
|
|
|
|
|
* - In OpenFlow 1.1, a "mod-flow" operation that does not find any
|
|
|
|
|
* existing flow to modify adds a new flow, but only if the mod-flow
|
|
|
|
|
* did not match on the flow cookie.
|
|
|
|
|
*
|
|
|
|
|
* - In OpenFlow 1.2 and a later, a "mod-flow" operation never adds a
|
|
|
|
|
* new flow.
|
|
|
|
|
*/
|
|
|
|
|
set_allowed_ofp_versions("OpenFlow10");
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
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;
|
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
case 'F':
|
2012-02-10 13:30:23 -08:00
|
|
|
|
allowed_protocols = ofputil_protocols_from_string(optarg);
|
|
|
|
|
if (!allowed_protocols) {
|
|
|
|
|
ovs_fatal(0, "%s: invalid flow format(s)", optarg);
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
case 'P':
|
|
|
|
|
preferred_packet_in_format =
|
|
|
|
|
ofputil_packet_in_format_from_string(optarg);
|
|
|
|
|
if (preferred_packet_in_format < 0) {
|
|
|
|
|
ovs_fatal(0, "unknown packet-in format `%s'", optarg);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2010-11-23 14:34:21 -08:00
|
|
|
|
case 'm':
|
|
|
|
|
verbosity++;
|
|
|
|
|
break;
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
case 'h':
|
|
|
|
|
usage();
|
|
|
|
|
|
|
|
|
|
case OPT_STRICT:
|
2009-10-23 11:49:39 -07:00
|
|
|
|
strict = true;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
|
2011-07-27 14:58:10 -07:00
|
|
|
|
case OPT_READD:
|
|
|
|
|
readd = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2012-02-07 16:17:13 -08:00
|
|
|
|
case OPT_TIMESTAMP:
|
|
|
|
|
timestamp = true;
|
|
|
|
|
break;
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
case OPT_SORT:
|
|
|
|
|
add_sort_criterion(SORT_ASC, optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OPT_RSORT:
|
|
|
|
|
add_sort_criterion(SORT_DESC, optarg);
|
|
|
|
|
break;
|
|
|
|
|
|
2014-07-28 10:31:25 -07:00
|
|
|
|
case OPT_UNIXCTL:
|
|
|
|
|
unixctl_path = optarg;
|
|
|
|
|
break;
|
|
|
|
|
|
2011-12-20 15:31:34 -08:00
|
|
|
|
DAEMON_OPTION_HANDLERS
|
2012-11-19 14:59:32 +09:00
|
|
|
|
OFP_VERSION_OPTION_HANDLERS
|
2010-01-04 18:55:00 -08:00
|
|
|
|
VLOG_OPTION_HANDLERS
|
2010-01-06 14:35:20 -08:00
|
|
|
|
STREAM_SSL_OPTION_HANDLERS
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
case '?':
|
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-12 13:32:47 -07:00
|
|
|
|
|
|
|
|
|
if (n_criteria) {
|
|
|
|
|
/* Always do a final sort pass based on priority. */
|
|
|
|
|
add_sort_criterion(SORT_DESC, "priority");
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
free(short_options);
|
2012-11-28 22:22:38 -08:00
|
|
|
|
|
|
|
|
|
versions = get_allowed_ofp_versions();
|
|
|
|
|
version_protocols = ofputil_protocols_from_version_bitmap(versions);
|
|
|
|
|
if (!(allowed_protocols & version_protocols)) {
|
|
|
|
|
char *protocols = ofputil_protocols_to_string(allowed_protocols);
|
|
|
|
|
struct ds version_s = DS_EMPTY_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
ofputil_format_version_bitmap_names(&version_s, versions);
|
|
|
|
|
ovs_fatal(0, "None of the enabled OpenFlow versions (%s) supports "
|
|
|
|
|
"any of the enabled flow formats (%s). (Use -O to enable "
|
|
|
|
|
"additional OpenFlow versions or -F to enable additional "
|
|
|
|
|
"flow formats.)", ds_cstr(&version_s), protocols);
|
|
|
|
|
}
|
|
|
|
|
allowed_protocols &= version_protocols;
|
|
|
|
|
mask_allowed_ofp_versions(ofputil_protocols_to_version_bitmap(
|
|
|
|
|
allowed_protocols));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
usage(void)
|
|
|
|
|
{
|
|
|
|
|
printf("%s: OpenFlow switch management utility\n"
|
|
|
|
|
"usage: %s [OPTIONS] COMMAND [ARG...]\n"
|
|
|
|
|
"\nFor OpenFlow switches:\n"
|
|
|
|
|
" show SWITCH show OpenFlow information\n"
|
|
|
|
|
" dump-desc SWITCH print switch description\n"
|
|
|
|
|
" dump-tables SWITCH print table stats\n"
|
2014-03-23 23:20:04 -07:00
|
|
|
|
" dump-table-features SWITCH print table features\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
" mod-port SWITCH IFACE ACT modify port behavior\n"
|
2013-09-07 03:02:32 -07:00
|
|
|
|
" mod-table SWITCH MOD modify flow table behavior\n"
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
" get-frags SWITCH print fragment handling behavior\n"
|
|
|
|
|
" set-frags SWITCH FRAG_MODE set fragment handling behavior\n"
|
2010-01-19 22:35:18 -08:00
|
|
|
|
" dump-ports SWITCH [PORT] print port statistics\n"
|
2014-05-07 23:18:46 -07:00
|
|
|
|
" dump-ports-desc SWITCH [PORT] print port descriptions\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
" dump-flows SWITCH print all flow entries\n"
|
|
|
|
|
" dump-flows SWITCH FLOW print matching FLOWs\n"
|
|
|
|
|
" dump-aggregate SWITCH print aggregate flow statistics\n"
|
|
|
|
|
" dump-aggregate SWITCH FLOW print aggregate stats for FLOWs\n"
|
2010-09-16 15:36:57 -07:00
|
|
|
|
" queue-stats SWITCH [PORT [QUEUE]] dump queue stats\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
" add-flow SWITCH FLOW add flow described by FLOW\n"
|
|
|
|
|
" add-flows SWITCH FILE add flows from FILE\n"
|
|
|
|
|
" mod-flows SWITCH FLOW modify actions of matching FLOWs\n"
|
|
|
|
|
" del-flows SWITCH [FLOW] delete matching FLOWs\n"
|
2011-06-15 17:16:51 -07:00
|
|
|
|
" replace-flows SWITCH FILE replace flows with those in FILE\n"
|
2012-01-25 14:41:22 -08:00
|
|
|
|
" diff-flows SOURCE1 SOURCE2 compare flows from two sources\n"
|
2012-01-25 16:30:28 -08:00
|
|
|
|
" packet-out SWITCH IN_PORT ACTIONS PACKET...\n"
|
|
|
|
|
" execute ACTIONS on PACKET\n"
|
2012-07-12 14:18:05 -07:00
|
|
|
|
" monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
|
2012-01-25 14:41:22 -08:00
|
|
|
|
" print packets received from SWITCH\n"
|
|
|
|
|
" snoop SWITCH snoop on SWITCH and its controller\n"
|
2013-09-01 18:30:17 -07:00
|
|
|
|
" add-group SWITCH GROUP add group described by GROUP\n"
|
|
|
|
|
" add-group SWITCH FILE add group from FILE\n"
|
|
|
|
|
" mod-group SWITCH GROUP modify specific group\n"
|
|
|
|
|
" del-groups SWITCH [GROUP] delete matching GROUPs\n"
|
|
|
|
|
" dump-group-features SWITCH print group features\n"
|
2014-05-07 23:49:00 -07:00
|
|
|
|
" dump-groups SWITCH [GROUP] print group description\n"
|
2013-09-01 18:30:17 -07:00
|
|
|
|
" dump-group-stats SWITCH [GROUP] print group statistics\n"
|
2013-10-24 15:54:03 -07:00
|
|
|
|
" queue-get-config SWITCH PORT print queue information for port\n"
|
2013-09-13 15:03:33 -07:00
|
|
|
|
" add-meter SWITCH METER add meter described by METER\n"
|
|
|
|
|
" mod-meter SWITCH METER modify specific METER\n"
|
|
|
|
|
" del-meter SWITCH METER delete METER\n"
|
|
|
|
|
" del-meters SWITCH delete all meters\n"
|
|
|
|
|
" dump-meter SWITCH METER print METER configuration\n"
|
|
|
|
|
" dump-meters SWITCH print all meter configuration\n"
|
|
|
|
|
" meter-stats SWITCH [METER] print meter statistics\n"
|
|
|
|
|
" meter-features SWITCH print meter features\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
"\nFor OpenFlow switches and controllers:\n"
|
2011-11-30 13:07:38 -08:00
|
|
|
|
" probe TARGET probe whether TARGET is up\n"
|
|
|
|
|
" ping TARGET [N] latency of N-byte echos\n"
|
|
|
|
|
" benchmark TARGET N COUNT bandwidth of COUNT N-byte echos\n"
|
2013-08-06 09:45:07 -07:00
|
|
|
|
"SWITCH or TARGET is an active OpenFlow connection method.\n"
|
|
|
|
|
"\nOther commands:\n"
|
2013-11-22 13:17:23 -08:00
|
|
|
|
" ofp-parse FILE print messages read from FILE\n"
|
|
|
|
|
" ofp-parse-pcap PCAP print OpenFlow read from PCAP\n",
|
2009-07-08 13:19:16 -07:00
|
|
|
|
program_name, program_name);
|
|
|
|
|
vconn_usage(true, false, false);
|
2011-12-20 15:31:34 -08:00
|
|
|
|
daemon_usage();
|
2012-11-19 14:59:32 +09:00
|
|
|
|
ofp_version_usage();
|
2009-07-08 13:19:16 -07:00
|
|
|
|
vlog_usage();
|
|
|
|
|
printf("\nOther options:\n"
|
|
|
|
|
" --strict use strict match for flow commands\n"
|
2011-07-27 14:58:10 -07:00
|
|
|
|
" --readd replace flows that haven't changed\n"
|
2010-12-07 13:32:01 -08:00
|
|
|
|
" -F, --flow-format=FORMAT force particular flow format\n"
|
2011-12-09 15:48:26 -08:00
|
|
|
|
" -P, --packet-in-format=FRMT force particular packet in format\n"
|
2010-11-23 14:34:21 -08:00
|
|
|
|
" -m, --more be more verbose printing OpenFlow\n"
|
2012-02-07 16:17:13 -08:00
|
|
|
|
" --timestamp (monitor, snoop) print timestamps\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
" -t, --timeout=SECS give up after SECS seconds\n"
|
2012-07-12 13:32:47 -07:00
|
|
|
|
" --sort[=field] sort in ascending order\n"
|
|
|
|
|
" --rsort[=field] sort in descending order\n"
|
2014-07-28 10:31:25 -07:00
|
|
|
|
" --unixctl=SOCKET set control socket name\n"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
" -h, --help display this help message\n"
|
|
|
|
|
" -V, --version display version information\n");
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-20 15:31:34 -08:00
|
|
|
|
static void
|
|
|
|
|
ofctl_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
|
|
|
|
const char *argv[] OVS_UNUSED, void *exiting_)
|
|
|
|
|
{
|
|
|
|
|
bool *exiting = exiting_;
|
|
|
|
|
*exiting = true;
|
2012-02-14 20:53:59 -08:00
|
|
|
|
unixctl_command_reply(conn, NULL);
|
2011-12-20 15:31:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void run(int retval, const char *message, ...)
|
|
|
|
|
PRINTF_FORMAT(2, 3);
|
|
|
|
|
|
2012-11-15 21:28:56 -08:00
|
|
|
|
static void
|
|
|
|
|
run(int retval, const char *message, ...)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
if (retval) {
|
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
|
|
va_start(args, message);
|
2011-03-31 14:50:58 -07:00
|
|
|
|
ovs_fatal_valist(retval, message, args);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generic commands. */
|
|
|
|
|
|
2012-10-10 13:33:54 -07:00
|
|
|
|
static int
|
2010-01-22 14:37:10 -05:00
|
|
|
|
open_vconn_socket(const char *name, struct vconn **vconnp)
|
|
|
|
|
{
|
|
|
|
|
char *vconn_name = xasprintf("unix:%s", name);
|
2012-10-10 13:33:54 -07:00
|
|
|
|
int error;
|
|
|
|
|
|
2013-01-02 17:10:43 -08:00
|
|
|
|
error = vconn_open(vconn_name, get_allowed_ofp_versions(), DSCP_DEFAULT,
|
|
|
|
|
vconnp);
|
2012-10-10 13:33:54 -07:00
|
|
|
|
if (error && error != ENOENT) {
|
|
|
|
|
ovs_fatal(0, "%s: failed to open socket (%s)", name,
|
2013-06-24 10:54:49 -07:00
|
|
|
|
ovs_strerror(error));
|
2012-10-10 13:33:54 -07:00
|
|
|
|
}
|
2010-01-22 14:37:10 -05:00
|
|
|
|
free(vconn_name);
|
2012-10-10 13:33:54 -07:00
|
|
|
|
|
|
|
|
|
return error;
|
2010-01-22 14:37:10 -05:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-04 12:41:01 -08:00
|
|
|
|
enum open_target { MGMT, SNOOP };
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
static enum ofputil_protocol
|
2013-01-04 12:41:01 -08:00
|
|
|
|
open_vconn__(const char *name, enum open_target target,
|
2010-05-11 12:44:58 -07:00
|
|
|
|
struct vconn **vconnp)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2013-01-04 12:41:01 -08:00
|
|
|
|
const char *suffix = target == MGMT ? "mgmt" : "snoop";
|
2011-04-11 15:08:19 -07:00
|
|
|
|
char *datapath_name, *datapath_type, *socket_name;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol;
|
2011-04-11 15:08:19 -07:00
|
|
|
|
char *bridge_path;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
int ofp_version;
|
2012-10-10 13:33:54 -07:00
|
|
|
|
int error;
|
2010-01-22 14:37:10 -05:00
|
|
|
|
|
2013-01-04 12:41:01 -08:00
|
|
|
|
bridge_path = xasprintf("%s/%s.%s", ovs_rundir(), name, suffix);
|
2011-04-11 15:08:19 -07:00
|
|
|
|
|
|
|
|
|
ofproto_parse_name(name, &datapath_name, &datapath_type);
|
2013-01-04 12:41:01 -08:00
|
|
|
|
socket_name = xasprintf("%s/%s.%s", ovs_rundir(), datapath_name, suffix);
|
2011-04-11 15:08:19 -07:00
|
|
|
|
free(datapath_name);
|
|
|
|
|
free(datapath_type);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-07-21 11:12:45 -07:00
|
|
|
|
if (strchr(name, ':')) {
|
2013-01-04 12:37:17 -08:00
|
|
|
|
run(vconn_open(name, get_allowed_ofp_versions(), DSCP_DEFAULT, vconnp),
|
2012-11-19 14:59:32 +09:00
|
|
|
|
"connecting to %s", name);
|
2012-10-10 13:33:54 -07:00
|
|
|
|
} else if (!open_vconn_socket(name, vconnp)) {
|
|
|
|
|
/* Fall Through. */
|
|
|
|
|
} else if (!open_vconn_socket(bridge_path, vconnp)) {
|
|
|
|
|
/* Fall Through. */
|
|
|
|
|
} else if (!open_vconn_socket(socket_name, vconnp)) {
|
|
|
|
|
/* Fall Through. */
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
2011-07-20 14:31:54 -07:00
|
|
|
|
ovs_fatal(0, "%s is not a bridge or a socket", name);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-01-22 14:37:10 -05:00
|
|
|
|
|
2013-01-04 12:41:01 -08:00
|
|
|
|
if (target == SNOOP) {
|
|
|
|
|
vconn_set_recv_any_version(*vconnp);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-22 14:37:10 -05:00
|
|
|
|
free(bridge_path);
|
2011-04-11 15:08:19 -07:00
|
|
|
|
free(socket_name);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
|
2012-10-10 13:33:54 -07:00
|
|
|
|
VLOG_DBG("connecting to %s", vconn_get_name(*vconnp));
|
|
|
|
|
error = vconn_connect_block(*vconnp);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s: failed to connect to socket (%s)", name,
|
2013-06-24 10:54:49 -07:00
|
|
|
|
ovs_strerror(error));
|
2012-10-10 13:33:54 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofp_version = vconn_get_version(*vconnp);
|
|
|
|
|
protocol = ofputil_protocol_from_ofp_version(ofp_version);
|
|
|
|
|
if (!protocol) {
|
|
|
|
|
ovs_fatal(0, "%s: unsupported OpenFlow version 0x%02x",
|
|
|
|
|
name, ofp_version);
|
|
|
|
|
}
|
|
|
|
|
return protocol;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
static enum ofputil_protocol
|
2010-05-11 12:44:58 -07:00
|
|
|
|
open_vconn(const char *name, struct vconn **vconnp)
|
|
|
|
|
{
|
2013-01-04 12:41:01 -08:00
|
|
|
|
return open_vconn__(name, MGMT, vconnp);
|
2010-05-11 12:44:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
|
|
|
|
send_openflow_buffer(struct vconn *vconn, struct ofpbuf *buffer)
|
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpmsg_update_length(buffer);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
run(vconn_send_block(vconn, buffer), "failed to send packet to switch");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-11-19 14:59:32 +09:00
|
|
|
|
dump_transaction(struct vconn *vconn, struct ofpbuf *request)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpmsg_update_length(request);
|
2012-11-19 14:59:32 +09:00
|
|
|
|
run(vconn_transact(vconn, request, &reply), "talking to %s",
|
|
|
|
|
vconn_get_name(vconn));
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(reply), ofpbuf_size(reply), verbosity + 1);
|
2012-03-27 14:09:14 -07:00
|
|
|
|
ofpbuf_delete(reply);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
dump_trivial_transaction(const char *vconn_name, enum ofpraw raw)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
2012-11-19 14:59:32 +09:00
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
|
|
|
|
open_vconn(vconn_name, &vconn);
|
|
|
|
|
request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
|
|
|
|
|
dump_transaction(vconn, request);
|
|
|
|
|
vconn_close(vconn);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-08-21 13:55:37 +09:00
|
|
|
|
dump_stats_transaction(struct vconn *vconn, struct ofpbuf *request)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2014-03-30 01:31:50 -07:00
|
|
|
|
const struct ofp_header *request_oh = ofpbuf_data(request);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ovs_be32 send_xid = request_oh->xid;
|
|
|
|
|
enum ofpraw request_raw;
|
|
|
|
|
enum ofpraw reply_raw;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
bool done = false;
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpraw_decode_partial(&request_raw, ofpbuf_data(request), ofpbuf_size(request));
|
2012-07-19 23:23:17 -07:00
|
|
|
|
reply_raw = ofpraw_stats_request_to_reply(request_raw,
|
|
|
|
|
request_oh->version);
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
send_openflow_buffer(vconn, request);
|
|
|
|
|
while (!done) {
|
2011-03-11 11:56:25 -08:00
|
|
|
|
ovs_be32 recv_xid;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
|
|
|
|
|
|
run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
recv_xid = ((struct ofp_header *) ofpbuf_data(reply))->xid;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
if (send_xid == recv_xid) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofpraw raw;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(reply), ofpbuf_size(reply), verbosity + 1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpraw_decode(&raw, ofpbuf_data(reply));
|
2012-07-19 23:23:17 -07:00
|
|
|
|
if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
|
2012-06-11 11:15:31 -07:00
|
|
|
|
done = true;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
} else if (raw == reply_raw) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
done = !ofpmp_more(ofpbuf_data(reply));
|
2012-06-11 11:15:31 -07:00
|
|
|
|
} else {
|
|
|
|
|
ovs_fatal(0, "received bad reply: %s",
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_to_string(ofpbuf_data(reply), ofpbuf_size(reply),
|
2012-06-11 11:15:31 -07:00
|
|
|
|
verbosity + 1));
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
|
|
|
|
VLOG_DBG("received reply with xid %08"PRIx32" "
|
|
|
|
|
"!= expected %08"PRIx32, recv_xid, send_xid);
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
2012-07-03 10:25:35 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-08-21 13:55:37 +09:00
|
|
|
|
dump_trivial_stats_transaction(const char *vconn_name, enum ofpraw raw)
|
2012-07-03 10:25:35 -07:00
|
|
|
|
{
|
2012-08-21 13:55:37 +09:00
|
|
|
|
struct ofpbuf *request;
|
2012-07-03 10:25:35 -07:00
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
|
|
|
|
open_vconn(vconn_name, &vconn);
|
2012-08-21 13:55:37 +09:00
|
|
|
|
request = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
|
|
|
|
|
dump_stats_transaction(vconn, request);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-07 10:19:40 -07:00
|
|
|
|
/* Sends all of the 'requests', which should be requests that only have replies
|
|
|
|
|
* if an error occurs, and waits for them to succeed or fail. If an error does
|
|
|
|
|
* occur, prints it and exits with an error.
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
*
|
|
|
|
|
* Destroys all of the 'requests'. */
|
2010-11-16 10:50:52 -08:00
|
|
|
|
static void
|
2010-12-07 13:32:01 -08:00
|
|
|
|
transact_multiple_noreply(struct vconn *vconn, struct list *requests)
|
2010-11-16 10:50:52 -08:00
|
|
|
|
{
|
2010-12-07 13:32:01 -08:00
|
|
|
|
struct ofpbuf *request, *reply;
|
2010-11-16 10:50:52 -08:00
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
LIST_FOR_EACH (request, list_node, requests) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpmsg_update_length(request);
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run(vconn_transact_multiple_noreply(vconn, requests, &reply),
|
2010-11-16 10:50:52 -08:00
|
|
|
|
"talking to %s", vconn_get_name(vconn));
|
|
|
|
|
if (reply) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stderr, ofpbuf_data(reply), ofpbuf_size(reply), verbosity + 2);
|
2010-11-16 10:50:52 -08:00
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
/* Sends 'request', which should be a request that only has a reply if an error
|
|
|
|
|
* occurs, and waits for it to succeed or fail. If an error does occur, prints
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
* it and exits with an error.
|
|
|
|
|
*
|
|
|
|
|
* Destroys 'request'. */
|
2010-12-07 13:32:01 -08:00
|
|
|
|
static void
|
|
|
|
|
transact_noreply(struct vconn *vconn, struct ofpbuf *request)
|
|
|
|
|
{
|
|
|
|
|
struct list requests;
|
|
|
|
|
|
|
|
|
|
list_init(&requests);
|
|
|
|
|
list_push_back(&requests, &request->list_node);
|
|
|
|
|
transact_multiple_noreply(vconn, &requests);
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static void
|
|
|
|
|
fetch_switch_config(struct vconn *vconn, struct ofp_switch_config *config_)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_switch_config *config;
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct ofpbuf *reply;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofptype type;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
|
2012-11-19 14:59:32 +09:00
|
|
|
|
request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST,
|
|
|
|
|
vconn_get_version(vconn), 0);
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
run(vconn_transact(vconn, request, &reply),
|
|
|
|
|
"talking to %s", vconn_get_name(vconn));
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) {
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
ovs_fatal(0, "%s: bad reply to config request", vconn_get_name(vconn));
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
config = ofpbuf_pull(reply, sizeof *config);
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
*config_ = *config;
|
2012-02-03 12:33:06 -08:00
|
|
|
|
|
|
|
|
|
ofpbuf_delete(reply);
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-19 23:23:17 -07:00
|
|
|
|
set_switch_config(struct vconn *vconn, const struct ofp_switch_config *config)
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
|
2012-11-19 14:59:32 +09:00
|
|
|
|
request = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, vconn_get_version(vconn), 0);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpbuf_put(request, config, sizeof *config);
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
|
|
|
|
|
transact_noreply(vconn, request);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_show(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-05-04 17:27:16 -07:00
|
|
|
|
const char *vconn_name = argv[1];
|
2014-05-08 21:20:22 -07:00
|
|
|
|
enum ofp_version version;
|
2012-05-04 17:27:16 -07:00
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct ofpbuf *reply;
|
2014-05-08 21:20:22 -07:00
|
|
|
|
bool has_ports;
|
2012-05-04 17:27:16 -07:00
|
|
|
|
|
|
|
|
|
open_vconn(vconn_name, &vconn);
|
2014-05-08 21:20:22 -07:00
|
|
|
|
version = vconn_get_version(vconn);
|
|
|
|
|
request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0);
|
2012-05-04 17:27:16 -07:00
|
|
|
|
run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
|
|
|
|
|
|
2014-05-08 21:20:22 -07:00
|
|
|
|
has_ports = ofputil_switch_features_has_ports(reply);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(reply), ofpbuf_size(reply), verbosity + 1);
|
2012-05-04 17:27:16 -07:00
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
|
2014-05-08 21:20:22 -07:00
|
|
|
|
if (!has_ports) {
|
2014-05-07 23:18:46 -07:00
|
|
|
|
request = ofputil_encode_port_desc_stats_request(version, OFPP_ANY);
|
2014-05-08 21:20:22 -07:00
|
|
|
|
dump_stats_transaction(vconn, request);
|
2012-05-04 17:27:16 -07:00
|
|
|
|
}
|
2012-07-19 23:23:17 -07:00
|
|
|
|
dump_trivial_transaction(vconn_name, OFPRAW_OFPT_GET_CONFIG_REQUEST);
|
2012-11-19 14:59:32 +09:00
|
|
|
|
vconn_close(vconn);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_dump_desc(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_DESC_REQUEST);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_dump_tables(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-19 23:23:17 -07:00
|
|
|
|
dump_trivial_stats_transaction(argv[1], OFPRAW_OFPST_TABLE_REQUEST);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-23 23:20:04 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_dump_table_features(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
request = ofputil_encode_table_features_request(vconn_get_version(vconn));
|
|
|
|
|
if (request) {
|
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 21:20:22 -07:00
|
|
|
|
static bool fetch_port_by_stats(struct vconn *,
|
|
|
|
|
const char *port_name, ofp_port_t port_no,
|
|
|
|
|
struct ofputil_phy_port *);
|
|
|
|
|
|
|
|
|
|
/* Uses OFPT_FEATURES_REQUEST to attempt to fetch information about the port
|
|
|
|
|
* named 'port_name' or numbered 'port_no' into '*pp'. Returns true if
|
|
|
|
|
* successful, false on failure.
|
|
|
|
|
*
|
|
|
|
|
* This is only appropriate for OpenFlow 1.0, 1.1, and 1.2, which include a
|
|
|
|
|
* list of ports in OFPT_FEATURES_REPLY. */
|
2012-05-07 10:33:34 -07:00
|
|
|
|
static bool
|
2014-05-08 21:20:22 -07:00
|
|
|
|
fetch_port_by_features(struct vconn *vconn,
|
2013-06-19 16:58:44 -07:00
|
|
|
|
const char *port_name, ofp_port_t port_no,
|
2014-05-08 21:20:22 -07:00
|
|
|
|
struct ofputil_phy_port *pp)
|
2010-01-19 22:35:18 -08:00
|
|
|
|
{
|
2012-02-15 16:33:04 -08:00
|
|
|
|
struct ofputil_switch_features features;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
const struct ofp_header *oh;
|
2010-01-19 22:35:18 -08:00
|
|
|
|
struct ofpbuf *request, *reply;
|
2012-02-15 16:33:04 -08:00
|
|
|
|
enum ofperr error;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofptype type;
|
2012-02-15 16:33:04 -08:00
|
|
|
|
struct ofpbuf b;
|
2012-05-07 10:33:34 -07:00
|
|
|
|
bool found = false;
|
2010-01-19 22:35:18 -08:00
|
|
|
|
|
2010-11-16 10:28:10 -08:00
|
|
|
|
/* Fetch the switch's ofp_switch_features. */
|
2012-11-19 14:59:32 +09:00
|
|
|
|
request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
|
|
|
|
|
vconn_get_version(vconn), 0);
|
2014-05-08 21:20:22 -07:00
|
|
|
|
run(vconn_transact(vconn, request, &reply),
|
|
|
|
|
"talking to %s", vconn_get_name(vconn));
|
2010-01-19 22:35:18 -08:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
oh = ofpbuf_data(reply);
|
|
|
|
|
if (ofptype_decode(&type, ofpbuf_data(reply))
|
2012-07-19 23:23:17 -07:00
|
|
|
|
|| type != OFPTYPE_FEATURES_REPLY) {
|
2014-05-08 21:20:22 -07:00
|
|
|
|
ovs_fatal(0, "%s: received bad features reply", vconn_get_name(vconn));
|
2010-11-16 10:28:10 -08:00
|
|
|
|
}
|
2014-05-08 21:20:22 -07:00
|
|
|
|
if (!ofputil_switch_features_has_ports(reply)) {
|
|
|
|
|
/* The switch features reply does not contain a complete list of ports.
|
|
|
|
|
* Probably, there are more ports than will fit into a single 64 kB
|
|
|
|
|
* OpenFlow message. Use OFPST_PORT_DESC to get a complete list of
|
|
|
|
|
* ports. */
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
return fetch_port_by_stats(vconn, port_name, port_no, pp);
|
2012-05-07 10:33:34 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
error = ofputil_decode_switch_features(oh, &features, &b);
|
2012-02-15 16:33:04 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s: failed to decode features reply (%s)",
|
2014-05-08 21:20:22 -07:00
|
|
|
|
vconn_get_name(vconn), ofperr_to_string(error));
|
2012-02-15 16:33:04 -08:00
|
|
|
|
}
|
2010-11-16 10:28:10 -08:00
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
if (port_no != OFPP_NONE
|
2012-02-15 16:33:04 -08:00
|
|
|
|
? port_no == pp->port_no
|
|
|
|
|
: !strcmp(pp->name, port_name)) {
|
2012-05-07 10:33:34 -07:00
|
|
|
|
found = true;
|
2014-05-08 21:20:22 -07:00
|
|
|
|
break;
|
2010-01-19 22:35:18 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-07 10:33:34 -07:00
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 21:20:22 -07:00
|
|
|
|
/* Uses a OFPST_PORT_DESC request to attempt to fetch information about the
|
|
|
|
|
* port named 'port_name' or numbered 'port_no' into '*pp'. Returns true if
|
|
|
|
|
* successful, false on failure.
|
|
|
|
|
*
|
|
|
|
|
* This is most appropriate for OpenFlow 1.3 and later. Open vSwitch 1.7 and
|
|
|
|
|
* later also implements OFPST_PORT_DESC, as an extension, for OpenFlow 1.0,
|
|
|
|
|
* 1.1, and 1.2, so this can be used as a fallback in those versions when there
|
|
|
|
|
* are too many ports than fit in an OFPT_FEATURES_REPLY. */
|
2012-05-07 10:33:34 -07:00
|
|
|
|
static bool
|
2014-05-08 21:20:22 -07:00
|
|
|
|
fetch_port_by_stats(struct vconn *vconn,
|
2013-06-19 16:58:44 -07:00
|
|
|
|
const char *port_name, ofp_port_t port_no,
|
2012-05-07 10:33:34 -07:00
|
|
|
|
struct ofputil_phy_port *pp)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
ovs_be32 send_xid;
|
|
|
|
|
bool done = false;
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
2014-05-07 23:18:46 -07:00
|
|
|
|
request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn),
|
|
|
|
|
port_no);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
send_xid = ((struct ofp_header *) ofpbuf_data(request))->xid;
|
2012-05-07 10:33:34 -07:00
|
|
|
|
|
|
|
|
|
send_openflow_buffer(vconn, request);
|
|
|
|
|
while (!done) {
|
|
|
|
|
ovs_be32 recv_xid;
|
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
|
|
|
|
|
|
run(vconn_recv_block(vconn, &reply), "OpenFlow packet receive failed");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
recv_xid = ((struct ofp_header *) ofpbuf_data(reply))->xid;
|
2012-05-07 10:33:34 -07:00
|
|
|
|
if (send_xid == recv_xid) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
struct ofp_header *oh = ofpbuf_data(reply);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofptype type;
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
uint16_t flags;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
if (ofptype_pull(&type, &b)
|
|
|
|
|
|| type != OFPTYPE_PORT_DESC_STATS_REPLY) {
|
2012-05-07 10:33:34 -07:00
|
|
|
|
ovs_fatal(0, "received bad reply: %s",
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_to_string(ofpbuf_data(reply), ofpbuf_size(reply),
|
2012-05-07 10:33:34 -07:00
|
|
|
|
verbosity + 1));
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
flags = ofpmp_flags(oh);
|
|
|
|
|
done = !(flags & OFPSF_REPLY_MORE);
|
2012-05-07 10:33:34 -07:00
|
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
/* We've already found the port, but we need to drain
|
|
|
|
|
* the queue of any other replies for this request. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 23:23:17 -07:00
|
|
|
|
while (!ofputil_pull_phy_port(oh->version, &b, pp)) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
if (port_no != OFPP_NONE ? port_no == pp->port_no
|
|
|
|
|
: !strcmp(pp->name, port_name)) {
|
2012-05-07 10:33:34 -07:00
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
VLOG_DBG("received reply with xid %08"PRIx32" "
|
|
|
|
|
"!= expected %08"PRIx32, recv_xid, send_xid);
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 16:58:44 -07:00
|
|
|
|
static bool
|
|
|
|
|
str_to_ofp(const char *s, ofp_port_t *ofp_port)
|
|
|
|
|
{
|
|
|
|
|
bool ret;
|
|
|
|
|
uint32_t port_;
|
|
|
|
|
|
|
|
|
|
ret = str_to_uint(s, 10, &port_);
|
|
|
|
|
*ofp_port = u16_to_ofp(port_);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2012-05-07 10:33:34 -07:00
|
|
|
|
|
|
|
|
|
/* Opens a connection to 'vconn_name', fetches the port structure for
|
|
|
|
|
* 'port_name' (which may be a port name or number), and copies it into
|
|
|
|
|
* '*pp'. */
|
|
|
|
|
static void
|
|
|
|
|
fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
|
|
|
|
|
struct ofputil_phy_port *pp)
|
|
|
|
|
{
|
2014-05-08 21:20:22 -07:00
|
|
|
|
struct vconn *vconn;
|
2013-06-19 16:58:44 -07:00
|
|
|
|
ofp_port_t port_no;
|
2012-05-07 10:33:34 -07:00
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
|
|
/* Try to interpret the argument as a port number. */
|
2013-06-19 16:58:44 -07:00
|
|
|
|
if (!str_to_ofp(port_name, &port_no)) {
|
|
|
|
|
port_no = OFPP_NONE;
|
2012-05-07 10:33:34 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 21:20:22 -07:00
|
|
|
|
/* OpenFlow 1.0, 1.1, and 1.2 put the list of ports in the
|
|
|
|
|
* OFPT_FEATURES_REPLY message. OpenFlow 1.3 and later versions put it
|
|
|
|
|
* into the OFPST_PORT_DESC reply. Try it the correct way. */
|
|
|
|
|
open_vconn(vconn_name, &vconn);
|
|
|
|
|
found = (vconn_get_version(vconn) < OFP13_VERSION
|
|
|
|
|
? fetch_port_by_features(vconn, port_name, port_no, pp)
|
|
|
|
|
: fetch_port_by_stats(vconn, port_name, port_no, pp));
|
|
|
|
|
vconn_close(vconn);
|
2012-05-07 10:33:34 -07:00
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
ovs_fatal(0, "%s: couldn't find port `%s'", vconn_name, port_name);
|
|
|
|
|
}
|
2010-11-16 10:28:10 -08:00
|
|
|
|
}
|
2010-01-19 22:35:18 -08:00
|
|
|
|
|
2010-11-16 10:28:10 -08:00
|
|
|
|
/* Returns the port number corresponding to 'port_name' (which may be a port
|
|
|
|
|
* name or number) within the switch 'vconn_name'. */
|
2013-06-19 16:58:44 -07:00
|
|
|
|
static ofp_port_t
|
2010-11-16 10:28:10 -08:00
|
|
|
|
str_to_port_no(const char *vconn_name, const char *port_name)
|
|
|
|
|
{
|
2013-06-19 16:58:44 -07:00
|
|
|
|
ofp_port_t port_no;
|
2012-10-17 13:10:08 -07:00
|
|
|
|
|
|
|
|
|
if (ofputil_port_from_string(port_name, &port_no)) {
|
2010-11-16 10:28:10 -08:00
|
|
|
|
return port_no;
|
|
|
|
|
} else {
|
2012-02-15 16:33:04 -08:00
|
|
|
|
struct ofputil_phy_port pp;
|
2010-01-19 22:35:18 -08:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
fetch_ofputil_phy_port(vconn_name, port_name, &pp);
|
|
|
|
|
return pp.port_no;
|
2010-11-16 10:28:10 -08:00
|
|
|
|
}
|
2010-01-19 22:35:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
static bool
|
2012-02-10 13:30:23 -08:00
|
|
|
|
try_set_protocol(struct vconn *vconn, enum ofputil_protocol want,
|
|
|
|
|
enum ofputil_protocol *cur)
|
2010-12-07 13:32:01 -08:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofpbuf *request, *reply;
|
|
|
|
|
enum ofputil_protocol next;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
request = ofputil_encode_set_protocol(*cur, want, &next);
|
|
|
|
|
if (!request) {
|
2012-11-15 22:09:07 -08:00
|
|
|
|
return *cur == want;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run(vconn_transact_noreply(vconn, request, &reply),
|
|
|
|
|
"talking to %s", vconn_get_name(vconn));
|
|
|
|
|
if (reply) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
char *s = ofp_to_string(ofpbuf_data(reply), ofpbuf_size(reply), 2);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
VLOG_DBG("%s: failed to set protocol, switch replied: %s",
|
|
|
|
|
vconn_get_name(vconn), s);
|
|
|
|
|
free(s);
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*cur = next;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
static enum ofputil_protocol
|
|
|
|
|
set_protocol_for_flow_dump(struct vconn *vconn,
|
|
|
|
|
enum ofputil_protocol cur_protocol,
|
|
|
|
|
enum ofputil_protocol usable_protocols)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
char *usable_s;
|
|
|
|
|
int i;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
for (i = 0; i < ofputil_n_flow_dump_protocols; i++) {
|
|
|
|
|
enum ofputil_protocol f = ofputil_flow_dump_protocols[i];
|
|
|
|
|
if (f & usable_protocols & allowed_protocols
|
|
|
|
|
&& try_set_protocol(vconn, f, &cur_protocol)) {
|
|
|
|
|
return f;
|
2011-03-10 11:07:10 -08:00
|
|
|
|
}
|
2012-02-10 13:30:23 -08:00
|
|
|
|
}
|
2011-03-10 11:07:10 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
usable_s = ofputil_protocols_to_string(usable_protocols);
|
|
|
|
|
if (usable_protocols & allowed_protocols) {
|
|
|
|
|
ovs_fatal(0, "switch does not support any of the usable flow "
|
|
|
|
|
"formats (%s)", usable_s);
|
2011-03-10 11:07:10 -08:00
|
|
|
|
} else {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
|
|
|
|
|
ovs_fatal(0, "none of the usable flow formats (%s) is among the "
|
|
|
|
|
"allowed flow formats (%s)", usable_s, allowed_s);
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
static struct vconn *
|
|
|
|
|
prepare_dump_flows(int argc, char *argv[], bool aggregate,
|
|
|
|
|
struct ofpbuf **requestp)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol usable_protocols, protocol;
|
2011-08-08 14:48:48 -07:00
|
|
|
|
struct ofputil_flow_stats_request fsr;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
struct vconn *vconn;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
error = parse_ofp_flow_stats_request_str(&fsr, aggregate,
|
2013-08-20 18:41:45 -07:00
|
|
|
|
argc > 2 ? argv[2] : "",
|
2013-11-15 14:19:57 -08:00
|
|
|
|
&usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
protocol = open_vconn(argv[1], &vconn);
|
|
|
|
|
protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
|
2012-07-12 13:32:47 -07:00
|
|
|
|
*requestp = ofputil_encode_flow_stats_request(&fsr, protocol);
|
|
|
|
|
return vconn;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_dump_flows__(int argc, char *argv[], bool aggregate)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
|
|
|
|
vconn = prepare_dump_flows(argc, argv, aggregate, &request);
|
2012-08-21 13:55:37 +09:00
|
|
|
|
dump_stats_transaction(vconn, request);
|
2010-12-07 13:32:01 -08:00
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 13:32:47 -07:00
|
|
|
|
static int
|
|
|
|
|
compare_flows(const void *afs_, const void *bfs_)
|
|
|
|
|
{
|
|
|
|
|
const struct ofputil_flow_stats *afs = afs_;
|
|
|
|
|
const struct ofputil_flow_stats *bfs = bfs_;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
const struct match *a = &afs->match;
|
|
|
|
|
const struct match *b = &bfs->match;
|
2012-07-12 13:32:47 -07:00
|
|
|
|
const struct sort_criterion *sc;
|
|
|
|
|
|
|
|
|
|
for (sc = criteria; sc < &criteria[n_criteria]; sc++) {
|
|
|
|
|
const struct mf_field *f = sc->field;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (!f) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
unsigned int a_pri = afs->priority;
|
|
|
|
|
unsigned int b_pri = bfs->priority;
|
|
|
|
|
ret = a_pri < b_pri ? -1 : a_pri > b_pri;
|
2012-07-12 13:32:47 -07:00
|
|
|
|
} else {
|
|
|
|
|
bool ina, inb;
|
|
|
|
|
|
|
|
|
|
ina = mf_are_prereqs_ok(f, &a->flow) && !mf_is_all_wild(f, &a->wc);
|
|
|
|
|
inb = mf_are_prereqs_ok(f, &b->flow) && !mf_is_all_wild(f, &b->wc);
|
|
|
|
|
if (ina != inb) {
|
|
|
|
|
/* Skip the test for sc->order, so that missing fields always
|
|
|
|
|
* sort to the end whether we're sorting in ascending or
|
|
|
|
|
* descending order. */
|
|
|
|
|
return ina ? -1 : 1;
|
|
|
|
|
} else {
|
|
|
|
|
union mf_value aval, bval;
|
|
|
|
|
|
|
|
|
|
mf_get_value(f, &a->flow, &aval);
|
|
|
|
|
mf_get_value(f, &b->flow, &bval);
|
|
|
|
|
ret = memcmp(&aval, &bval, f->n_bytes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
|
return sc->order == SORT_ASC ? ret : -ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_dump_flows(int argc, char *argv[])
|
2010-12-07 13:32:01 -08:00
|
|
|
|
{
|
2012-07-12 13:32:47 -07:00
|
|
|
|
if (!n_criteria) {
|
|
|
|
|
return ofctl_dump_flows__(argc, argv, false);
|
|
|
|
|
} else {
|
|
|
|
|
struct ofputil_flow_stats *fses;
|
|
|
|
|
size_t n_fses, allocated_fses;
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct ofpbuf ofpacts;
|
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
ovs_be32 send_xid;
|
|
|
|
|
struct ds s;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
vconn = prepare_dump_flows(argc, argv, false, &request);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
send_xid = ((struct ofp_header *) ofpbuf_data(request))->xid;
|
2012-07-12 13:32:47 -07:00
|
|
|
|
send_openflow_buffer(vconn, request);
|
|
|
|
|
|
|
|
|
|
fses = NULL;
|
|
|
|
|
n_fses = allocated_fses = 0;
|
|
|
|
|
reply = NULL;
|
|
|
|
|
ofpbuf_init(&ofpacts, 0);
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofputil_flow_stats *fs;
|
|
|
|
|
|
|
|
|
|
if (n_fses >= allocated_fses) {
|
|
|
|
|
fses = x2nrealloc(fses, &allocated_fses, sizeof *fses);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs = &fses[n_fses];
|
|
|
|
|
if (!recv_flow_stats_reply(vconn, send_xid, &reply, fs,
|
|
|
|
|
&ofpacts)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fs->ofpacts = xmemdup(fs->ofpacts, fs->ofpacts_len);
|
|
|
|
|
n_fses++;
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
|
|
|
|
|
qsort(fses, n_fses, sizeof *fses, compare_flows);
|
|
|
|
|
|
|
|
|
|
ds_init(&s);
|
|
|
|
|
for (i = 0; i < n_fses; i++) {
|
|
|
|
|
ds_clear(&s);
|
|
|
|
|
ofp_print_flow_stats(&s, &fses[i]);
|
|
|
|
|
puts(ds_cstr(&s));
|
|
|
|
|
}
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_fses; i++) {
|
2014-04-29 15:50:38 -07:00
|
|
|
|
free(CONST_CAST(struct ofpact *, fses[i].ofpacts));
|
2012-07-12 13:32:47 -07:00
|
|
|
|
}
|
|
|
|
|
free(fses);
|
|
|
|
|
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_dump_aggregate(int argc, char *argv[])
|
2010-12-07 13:32:01 -08:00
|
|
|
|
{
|
2012-07-08 04:38:36 -07:00
|
|
|
|
return ofctl_dump_flows__(argc, argv, true);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-09-16 15:36:57 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_queue_stats(int argc, char *argv[])
|
2010-09-16 15:36:57 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
2012-08-21 13:55:37 +09:00
|
|
|
|
struct vconn *vconn;
|
2012-10-12 13:03:04 +09:00
|
|
|
|
struct ofputil_queue_stats_request oqs;
|
2010-09-16 15:36:57 -07:00
|
|
|
|
|
2012-08-21 13:55:37 +09:00
|
|
|
|
open_vconn(argv[1], &vconn);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
|
|
|
|
|
if (argc > 2 && argv[2][0] && strcasecmp(argv[2], "all")) {
|
2012-10-12 13:03:04 +09:00
|
|
|
|
oqs.port_no = str_to_port_no(argv[1], argv[2]);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
} else {
|
2012-11-26 18:17:08 +02:00
|
|
|
|
oqs.port_no = OFPP_ANY;
|
2010-09-16 15:36:57 -07:00
|
|
|
|
}
|
|
|
|
|
if (argc > 3 && argv[3][0] && strcasecmp(argv[3], "all")) {
|
2012-10-12 13:03:04 +09:00
|
|
|
|
oqs.queue_id = atoi(argv[3]);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
} else {
|
2012-10-12 13:03:04 +09:00
|
|
|
|
oqs.queue_id = OFPQ_ALL;
|
2010-09-16 15:36:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-10-12 13:03:04 +09:00
|
|
|
|
request = ofputil_encode_queue_stats_request(vconn_get_version(vconn), &oqs);
|
2012-08-21 13:55:37 +09:00
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
vconn_close(vconn);
|
2010-09-16 15:36:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-24 15:54:03 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_queue_get_config(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
const char *vconn_name = argv[1];
|
|
|
|
|
const char *port_name = argv[2];
|
|
|
|
|
enum ofputil_protocol protocol;
|
|
|
|
|
enum ofp_version version;
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
ofp_port_t port;
|
|
|
|
|
|
|
|
|
|
port = str_to_port_no(vconn_name, port_name);
|
|
|
|
|
|
|
|
|
|
protocol = open_vconn(vconn_name, &vconn);
|
|
|
|
|
version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
request = ofputil_encode_queue_get_config_request(version, port);
|
|
|
|
|
dump_transaction(vconn, request);
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
static enum ofputil_protocol
|
2013-08-20 18:41:45 -07:00
|
|
|
|
open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
|
|
|
|
|
enum ofputil_protocol usable_protocols)
|
2011-02-22 13:43:14 -08:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol cur_protocol;
|
|
|
|
|
char *usable_s;
|
|
|
|
|
int i;
|
2011-02-22 13:43:14 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
if (!(usable_protocols & allowed_protocols)) {
|
|
|
|
|
char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
|
|
|
|
|
usable_s = ofputil_protocols_to_string(usable_protocols);
|
|
|
|
|
ovs_fatal(0, "none of the usable flow formats (%s) is among the "
|
|
|
|
|
"allowed flow formats (%s)", usable_s, allowed_s);
|
2011-02-22 13:43:14 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* If the initial flow format is allowed and usable, keep it. */
|
|
|
|
|
cur_protocol = open_vconn(remote, vconnp);
|
|
|
|
|
if (usable_protocols & allowed_protocols & cur_protocol) {
|
|
|
|
|
return cur_protocol;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise try each flow format in turn. */
|
|
|
|
|
for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
|
|
|
|
|
enum ofputil_protocol f = 1 << i;
|
|
|
|
|
|
|
|
|
|
if (f != cur_protocol
|
|
|
|
|
&& f & usable_protocols & allowed_protocols
|
|
|
|
|
&& try_set_protocol(*vconnp, f, &cur_protocol)) {
|
|
|
|
|
return f;
|
|
|
|
|
}
|
2011-02-22 13:43:14 -08:00
|
|
|
|
}
|
2012-02-10 13:30:23 -08:00
|
|
|
|
|
|
|
|
|
usable_s = ofputil_protocols_to_string(usable_protocols);
|
|
|
|
|
ovs_fatal(0, "switch does not support any of the usable flow "
|
|
|
|
|
"formats (%s)", usable_s);
|
2011-02-22 13:43:14 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
|
2013-08-20 18:41:45 -07:00
|
|
|
|
size_t n_fms, enum ofputil_protocol usable_protocols)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct vconn *vconn;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
size_t i;
|
2011-03-11 13:19:12 -08:00
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
|
protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
|
2010-11-11 11:01:09 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
for (i = 0; i < n_fms; i++) {
|
|
|
|
|
struct ofputil_flow_mod *fm = &fms[i];
|
2011-02-22 13:43:14 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
|
2014-04-29 15:50:38 -07:00
|
|
|
|
free(CONST_CAST(struct ofpact *, fm->ofpacts));
|
2011-03-11 13:19:12 -08:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
vconn_close(vconn);
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2013-08-20 18:41:45 -07:00
|
|
|
|
enum ofputil_protocol usable_protocols;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
struct ofputil_flow_mod *fms = NULL;
|
|
|
|
|
size_t n_fms = 0;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
|
error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
|
2013-11-15 14:19:57 -08:00
|
|
|
|
&usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
|
ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
free(fms);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod(int argc, char *argv[], uint16_t command)
|
2012-02-10 13:30:23 -08:00
|
|
|
|
{
|
2011-03-11 13:19:12 -08:00
|
|
|
|
if (argc > 2 && !strcmp(argv[2], "-")) {
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod_file(argc, argv, command);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
} else {
|
|
|
|
|
struct ofputil_flow_mod fm;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2013-09-13 15:03:33 -07:00
|
|
|
|
enum ofputil_protocol usable_protocols;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
|
error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
|
2013-11-15 14:19:57 -08:00
|
|
|
|
&usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
|
ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2011-03-11 13:19:12 -08:00
|
|
|
|
}
|
2010-12-07 13:32:01 -08:00
|
|
|
|
|
2011-03-11 13:19:12 -08:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_add_flow(int argc, char *argv[])
|
2011-03-11 13:19:12 -08:00
|
|
|
|
{
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod(argc, argv, OFPFC_ADD);
|
2011-03-11 13:19:12 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_add_flows(int argc, char *argv[])
|
2011-03-11 13:19:12 -08:00
|
|
|
|
{
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod_file(argc, argv, OFPFC_ADD);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_mod_flows(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod(argc, argv, strict ? OFPFC_MODIFY_STRICT : OFPFC_MODIFY);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_del_flows(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_flow_mod(argc, argv, strict ? OFPFC_DELETE_STRICT : OFPFC_DELETE);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
static void
|
|
|
|
|
set_packet_in_format(struct vconn *vconn,
|
|
|
|
|
enum nx_packet_in_format packet_in_format)
|
|
|
|
|
{
|
2012-10-04 11:41:16 +09:00
|
|
|
|
struct ofpbuf *spif;
|
|
|
|
|
|
|
|
|
|
spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn),
|
|
|
|
|
packet_in_format);
|
2011-12-09 15:48:26 -08:00
|
|
|
|
transact_noreply(vconn, spif);
|
|
|
|
|
VLOG_DBG("%s: using user-specified packet in format %s",
|
|
|
|
|
vconn_get_name(vconn),
|
|
|
|
|
ofputil_packet_in_format_to_string(packet_in_format));
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-13 17:54:04 -08:00
|
|
|
|
static int
|
|
|
|
|
monitor_set_invalid_ttl_to_controller(struct vconn *vconn)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_switch_config config;
|
|
|
|
|
enum ofp_config_flags flags;
|
|
|
|
|
|
|
|
|
|
fetch_switch_config(vconn, &config);
|
|
|
|
|
flags = ntohs(config.flags);
|
|
|
|
|
if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
|
|
|
|
|
/* Set the invalid ttl config. */
|
|
|
|
|
flags |= OFPC_INVALID_TTL_TO_CONTROLLER;
|
|
|
|
|
|
|
|
|
|
config.flags = htons(flags);
|
|
|
|
|
set_switch_config(vconn, &config);
|
|
|
|
|
|
|
|
|
|
/* Then retrieve the configuration to see if it really took. OpenFlow
|
|
|
|
|
* doesn't define error reporting for bad modes, so this is all we can
|
|
|
|
|
* do. */
|
|
|
|
|
fetch_switch_config(vconn, &config);
|
|
|
|
|
flags = ntohs(config.flags);
|
|
|
|
|
if (!(flags & OFPC_INVALID_TTL_TO_CONTROLLER)) {
|
|
|
|
|
ovs_fatal(0, "setting invalid_ttl_to_controller failed (this "
|
|
|
|
|
"switch probably doesn't support mode)");
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-09 13:30:53 -08:00
|
|
|
|
/* Converts hex digits in 'hex' to an OpenFlow message in '*msgp'. The
|
|
|
|
|
* caller must free '*msgp'. On success, returns NULL. On failure, returns
|
|
|
|
|
* an error message and stores NULL in '*msgp'. */
|
|
|
|
|
static const char *
|
|
|
|
|
openflow_from_hex(const char *hex, struct ofpbuf **msgp)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_header *oh;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = ofpbuf_new(strlen(hex) / 2);
|
|
|
|
|
*msgp = NULL;
|
|
|
|
|
|
|
|
|
|
if (ofpbuf_put_hex(msg, hex, NULL)[0] != '\0') {
|
|
|
|
|
ofpbuf_delete(msg);
|
|
|
|
|
return "Trailing garbage in hex data";
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(msg) < sizeof(struct ofp_header)) {
|
2012-02-09 13:30:53 -08:00
|
|
|
|
ofpbuf_delete(msg);
|
|
|
|
|
return "Message too short for OpenFlow";
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
oh = ofpbuf_data(msg);
|
|
|
|
|
if (ofpbuf_size(msg) != ntohs(oh->length)) {
|
2012-02-09 13:30:53 -08:00
|
|
|
|
ofpbuf_delete(msg);
|
|
|
|
|
return "Message size does not match length in OpenFlow header";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*msgp = msg;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_send(struct unixctl_conn *conn, int argc,
|
|
|
|
|
const char *argv[], void *vconn_)
|
|
|
|
|
{
|
|
|
|
|
struct vconn *vconn = vconn_;
|
|
|
|
|
struct ds reply;
|
|
|
|
|
bool ok;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
ok = true;
|
|
|
|
|
ds_init(&reply);
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
|
const char *error_msg;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
error_msg = openflow_from_hex(argv[i], &msg);
|
|
|
|
|
if (error_msg) {
|
|
|
|
|
ds_put_format(&reply, "%s\n", error_msg);
|
|
|
|
|
ok = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "send: ");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stderr, ofpbuf_data(msg), ofpbuf_size(msg), verbosity);
|
2012-02-09 13:30:53 -08:00
|
|
|
|
|
|
|
|
|
error = vconn_send_block(vconn, msg);
|
|
|
|
|
if (error) {
|
|
|
|
|
ofpbuf_delete(msg);
|
2013-06-24 10:54:49 -07:00
|
|
|
|
ds_put_format(&reply, "%s\n", ovs_strerror(error));
|
2012-02-09 13:30:53 -08:00
|
|
|
|
ok = false;
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_cstr(&reply, "sent\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-14 20:53:59 -08:00
|
|
|
|
|
|
|
|
|
if (ok) {
|
|
|
|
|
unixctl_command_reply(conn, ds_cstr(&reply));
|
|
|
|
|
} else {
|
|
|
|
|
unixctl_command_reply_error(conn, ds_cstr(&reply));
|
|
|
|
|
}
|
2012-02-09 13:30:53 -08:00
|
|
|
|
ds_destroy(&reply);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-26 13:41:48 -08:00
|
|
|
|
struct barrier_aux {
|
|
|
|
|
struct vconn *vconn; /* OpenFlow connection for sending barrier. */
|
|
|
|
|
struct unixctl_conn *conn; /* Connection waiting for barrier response. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
|
|
|
|
const char *argv[] OVS_UNUSED, void *aux_)
|
|
|
|
|
{
|
|
|
|
|
struct barrier_aux *aux = aux_;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
if (aux->conn) {
|
2012-02-14 20:53:59 -08:00
|
|
|
|
unixctl_command_reply_error(conn, "already waiting for barrier reply");
|
2012-01-26 13:41:48 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 16:01:47 +09:00
|
|
|
|
msg = ofputil_encode_barrier_request(vconn_get_version(aux->vconn));
|
2012-01-26 13:41:48 -08:00
|
|
|
|
error = vconn_send_block(aux->vconn, msg);
|
|
|
|
|
if (error) {
|
|
|
|
|
ofpbuf_delete(msg);
|
2013-06-24 10:54:49 -07:00
|
|
|
|
unixctl_command_reply_error(conn, ovs_strerror(error));
|
2012-01-26 13:41:48 -08:00
|
|
|
|
} else {
|
|
|
|
|
aux->conn = conn;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-26 15:45:34 -08:00
|
|
|
|
static void
|
|
|
|
|
ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
|
|
|
|
const char *argv[], void *aux OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
|
|
fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);
|
|
|
|
|
if (fd < 0) {
|
2013-06-24 10:54:49 -07:00
|
|
|
|
unixctl_command_reply_error(conn, ovs_strerror(errno));
|
2012-01-26 15:45:34 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fflush(stderr);
|
|
|
|
|
dup2(fd, STDERR_FILENO);
|
|
|
|
|
close(fd);
|
2012-02-14 20:53:59 -08:00
|
|
|
|
unixctl_command_reply(conn, NULL);
|
2012-01-26 15:45:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-12 14:18:05 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_block(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
2012-07-12 16:32:56 -07:00
|
|
|
|
const char *argv[] OVS_UNUSED, void *blocked_)
|
2012-07-12 14:18:05 -07:00
|
|
|
|
{
|
2012-07-12 16:32:56 -07:00
|
|
|
|
bool *blocked = blocked_;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2012-07-12 16:32:56 -07:00
|
|
|
|
if (!*blocked) {
|
|
|
|
|
*blocked = true;
|
|
|
|
|
unixctl_command_reply(conn, NULL);
|
|
|
|
|
} else {
|
2012-07-12 14:18:05 -07:00
|
|
|
|
unixctl_command_reply(conn, "already blocking");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_unblock(struct unixctl_conn *conn, int argc OVS_UNUSED,
|
2012-07-12 16:32:56 -07:00
|
|
|
|
const char *argv[] OVS_UNUSED, void *blocked_)
|
2012-07-12 14:18:05 -07:00
|
|
|
|
{
|
2012-07-12 16:32:56 -07:00
|
|
|
|
bool *blocked = blocked_;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2012-07-12 16:32:56 -07:00
|
|
|
|
if (*blocked) {
|
|
|
|
|
*blocked = false;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
unixctl_command_reply(conn, NULL);
|
2012-07-12 16:32:56 -07:00
|
|
|
|
} else {
|
|
|
|
|
unixctl_command_reply(conn, "already unblocked");
|
2012-07-12 14:18:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 13:58:29 -07:00
|
|
|
|
/* Prints to stdout all of the messages received on 'vconn'.
|
|
|
|
|
*
|
|
|
|
|
* Iff 'reply_to_echo_requests' is true, sends a reply to any echo request
|
|
|
|
|
* received on 'vconn'. */
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2013-05-03 13:58:29 -07:00
|
|
|
|
monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests)
|
2010-05-11 12:44:58 -07:00
|
|
|
|
{
|
2012-01-26 13:41:48 -08:00
|
|
|
|
struct barrier_aux barrier_aux = { vconn, NULL };
|
2011-12-20 15:31:34 -08:00
|
|
|
|
struct unixctl_server *server;
|
|
|
|
|
bool exiting = false;
|
2012-07-12 16:32:56 -07:00
|
|
|
|
bool blocked = false;
|
2012-01-27 09:53:17 -08:00
|
|
|
|
int error;
|
2011-12-20 15:31:34 -08:00
|
|
|
|
|
2012-01-27 09:53:17 -08:00
|
|
|
|
daemon_save_fd(STDERR_FILENO);
|
2011-12-20 15:31:34 -08:00
|
|
|
|
daemonize_start();
|
2014-07-28 10:31:25 -07:00
|
|
|
|
error = unixctl_server_create(unixctl_path, &server);
|
2011-12-20 15:31:34 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(error, "failed to create unixctl server");
|
|
|
|
|
}
|
|
|
|
|
unixctl_command_register("exit", "", 0, 0, ofctl_exit, &exiting);
|
2012-02-09 13:30:53 -08:00
|
|
|
|
unixctl_command_register("ofctl/send", "OFMSG...", 1, INT_MAX,
|
|
|
|
|
ofctl_send, vconn);
|
2012-01-26 13:41:48 -08:00
|
|
|
|
unixctl_command_register("ofctl/barrier", "", 0, 0,
|
|
|
|
|
ofctl_barrier, &barrier_aux);
|
2012-01-26 15:45:34 -08:00
|
|
|
|
unixctl_command_register("ofctl/set-output-file", "FILE", 1, 1,
|
|
|
|
|
ofctl_set_output_file, NULL);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2012-07-12 16:32:56 -07:00
|
|
|
|
unixctl_command_register("ofctl/block", "", 0, 0, ofctl_block, &blocked);
|
|
|
|
|
unixctl_command_register("ofctl/unblock", "", 0, 0, ofctl_unblock,
|
|
|
|
|
&blocked);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2011-12-20 15:31:34 -08:00
|
|
|
|
daemonize_complete();
|
|
|
|
|
|
2010-05-11 12:44:58 -07:00
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofpbuf *b;
|
2011-12-20 15:31:34 -08:00
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
unixctl_server_run(server);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
|
2012-07-12 16:32:56 -07:00
|
|
|
|
while (!blocked) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofptype type;
|
2012-01-26 13:41:48 -08:00
|
|
|
|
|
2011-12-20 15:31:34 -08:00
|
|
|
|
retval = vconn_recv(vconn, &b);
|
|
|
|
|
if (retval == EAGAIN) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
run(retval, "vconn_recv");
|
2012-03-07 13:52:55 -08:00
|
|
|
|
|
2012-02-07 16:17:13 -08:00
|
|
|
|
if (timestamp) {
|
2013-09-12 18:19:04 -07:00
|
|
|
|
char *s = xastrftime_msec("%Y-%m-%d %H:%M:%S.###: ",
|
|
|
|
|
time_wall_msec(), true);
|
2012-02-07 16:17:13 -08:00
|
|
|
|
fputs(s, stderr);
|
2013-05-02 16:16:06 -07:00
|
|
|
|
free(s);
|
2012-02-07 16:17:13 -08:00
|
|
|
|
}
|
2012-03-07 13:52:55 -08:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofptype_decode(&type, ofpbuf_data(b));
|
|
|
|
|
ofp_print(stderr, ofpbuf_data(b), ofpbuf_size(b), verbosity + 2);
|
2014-05-16 12:08:53 -07:00
|
|
|
|
fflush(stderr);
|
2012-01-26 13:41:48 -08:00
|
|
|
|
|
2013-05-03 13:58:29 -07:00
|
|
|
|
switch ((int) type) {
|
|
|
|
|
case OFPTYPE_BARRIER_REPLY:
|
|
|
|
|
if (barrier_aux.conn) {
|
|
|
|
|
unixctl_command_reply(barrier_aux.conn, NULL);
|
|
|
|
|
barrier_aux.conn = NULL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OFPTYPE_ECHO_REQUEST:
|
|
|
|
|
if (reply_to_echo_requests) {
|
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
reply = make_echo_reply(ofpbuf_data(b));
|
2013-05-03 13:58:29 -07:00
|
|
|
|
retval = vconn_send_block(vconn, reply);
|
|
|
|
|
if (retval) {
|
|
|
|
|
ovs_fatal(retval, "failed to send echo reply");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2012-01-26 13:41:48 -08:00
|
|
|
|
}
|
2013-05-03 13:58:29 -07:00
|
|
|
|
ofpbuf_delete(b);
|
2011-12-20 15:31:34 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exiting) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vconn_run(vconn);
|
|
|
|
|
vconn_run_wait(vconn);
|
2012-07-12 16:32:56 -07:00
|
|
|
|
if (!blocked) {
|
|
|
|
|
vconn_recv_wait(vconn);
|
|
|
|
|
}
|
2011-12-20 15:31:34 -08:00
|
|
|
|
unixctl_server_wait(server);
|
|
|
|
|
poll_block();
|
2010-05-11 12:44:58 -07:00
|
|
|
|
}
|
2012-02-03 12:33:06 -08:00
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
unixctl_server_destroy(server);
|
2010-05-11 12:44:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_monitor(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct vconn *vconn;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
int i;
|
2013-08-20 18:41:45 -07:00
|
|
|
|
enum ofputil_protocol usable_protocols;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
for (i = 2; i < argc; i++) {
|
|
|
|
|
const char *arg = argv[i];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-07-12 14:18:05 -07:00
|
|
|
|
if (isdigit((unsigned char) *arg)) {
|
|
|
|
|
struct ofp_switch_config config;
|
|
|
|
|
|
|
|
|
|
fetch_switch_config(vconn, &config);
|
|
|
|
|
config.miss_send_len = htons(atoi(arg));
|
|
|
|
|
set_switch_config(vconn, &config);
|
|
|
|
|
} else if (!strcmp(arg, "invalid_ttl")) {
|
2012-01-13 17:54:04 -08:00
|
|
|
|
monitor_set_invalid_ttl_to_controller(vconn);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
} else if (!strncmp(arg, "watch:", 6)) {
|
|
|
|
|
struct ofputil_flow_monitor_request fmr;
|
|
|
|
|
struct ofpbuf *msg;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
|
error = parse_flow_monitor_request(&fmr, arg + 6,
|
|
|
|
|
&usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
|
|
|
|
msg = ofpbuf_new(0);
|
|
|
|
|
ofputil_append_flow_monitor_request(&fmr, msg);
|
2012-08-21 13:55:37 +09:00
|
|
|
|
dump_stats_transaction(vconn, msg);
|
2014-05-16 12:08:53 -07:00
|
|
|
|
fflush(stdout);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
} else {
|
|
|
|
|
ovs_fatal(0, "%s: unsupported \"monitor\" argument", arg);
|
2012-01-13 17:54:04 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
2012-01-27 09:22:41 -08:00
|
|
|
|
if (preferred_packet_in_format >= 0) {
|
|
|
|
|
set_packet_in_format(vconn, preferred_packet_in_format);
|
|
|
|
|
} else {
|
2012-11-19 14:59:34 +09:00
|
|
|
|
enum ofp_version version = vconn_get_version(vconn);
|
|
|
|
|
|
|
|
|
|
switch (version) {
|
|
|
|
|
case OFP10_VERSION: {
|
|
|
|
|
struct ofpbuf *spif, *reply;
|
|
|
|
|
|
|
|
|
|
spif = ofputil_make_set_packet_in_format(vconn_get_version(vconn),
|
|
|
|
|
NXPIF_NXM);
|
|
|
|
|
run(vconn_transact_noreply(vconn, spif, &reply),
|
|
|
|
|
"talking to %s", vconn_get_name(vconn));
|
|
|
|
|
if (reply) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
char *s = ofp_to_string(ofpbuf_data(reply), ofpbuf_size(reply), 2);
|
2012-11-19 14:59:34 +09:00
|
|
|
|
VLOG_DBG("%s: failed to set packet in format to nxm, controller"
|
|
|
|
|
" replied: %s. Falling back to the switch default.",
|
|
|
|
|
vconn_get_name(vconn), s);
|
|
|
|
|
free(s);
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case OFP11_VERSION:
|
|
|
|
|
case OFP12_VERSION:
|
|
|
|
|
case OFP13_VERSION:
|
2014-03-03 15:22:32 +02:00
|
|
|
|
case OFP14_VERSION:
|
2014-05-07 13:42:24 -07:00
|
|
|
|
case OFP15_VERSION:
|
2012-11-19 14:59:34 +09:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2012-01-27 09:22:41 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-03 13:58:29 -07:00
|
|
|
|
monitor_vconn(vconn, true);
|
2010-05-11 12:44:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_snoop(int argc OVS_UNUSED, char *argv[])
|
2010-05-11 12:44:58 -07:00
|
|
|
|
{
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
2013-01-04 12:41:01 -08:00
|
|
|
|
open_vconn__(argv[1], SNOOP, &vconn);
|
2013-05-03 13:58:29 -07:00
|
|
|
|
monitor_vconn(vconn, false);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_dump_ports(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-01-19 22:35:18 -08:00
|
|
|
|
struct ofpbuf *request;
|
2012-08-21 13:55:37 +09:00
|
|
|
|
struct vconn *vconn;
|
2013-06-19 16:58:44 -07:00
|
|
|
|
ofp_port_t port;
|
2010-01-19 22:35:18 -08:00
|
|
|
|
|
2012-08-21 13:55:37 +09:00
|
|
|
|
open_vconn(argv[1], &vconn);
|
2012-11-26 18:17:08 +02:00
|
|
|
|
port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
|
2012-10-06 19:39:49 +09:00
|
|
|
|
request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port);
|
2012-08-21 13:55:37 +09:00
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
vconn_close(vconn);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 14:42:04 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_dump_ports_desc(int argc OVS_UNUSED, char *argv[])
|
2012-05-04 14:42:04 -07:00
|
|
|
|
{
|
2014-05-07 23:18:46 -07:00
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
ofp_port_t port;
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
|
|
|
|
|
request = ofputil_encode_port_desc_stats_request(vconn_get_version(vconn),
|
|
|
|
|
port);
|
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
vconn_close(vconn);
|
2012-05-04 14:42:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_probe(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
struct ofpbuf *reply;
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
2012-08-01 16:01:50 +09:00
|
|
|
|
request = make_echo_request(vconn_get_version(vconn));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
run(vconn_transact(vconn, request, &reply), "talking to %s", argv[1]);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(reply) != sizeof(struct ofp_header)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ovs_fatal(0, "reply does not match request");
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-25 16:30:28 -08:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_packet_out(int argc, char *argv[])
|
2012-01-25 16:30:28 -08:00
|
|
|
|
{
|
2012-08-08 12:19:57 +09:00
|
|
|
|
enum ofputil_protocol protocol;
|
2012-01-25 16:30:28 -08:00
|
|
|
|
struct ofputil_packet_out po;
|
2012-07-03 22:17:14 -07:00
|
|
|
|
struct ofpbuf ofpacts;
|
2012-01-25 16:30:28 -08:00
|
|
|
|
struct vconn *vconn;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2012-01-25 16:30:28 -08:00
|
|
|
|
int i;
|
2013-12-07 15:07:01 -08:00
|
|
|
|
enum ofputil_protocol usable_protocols; /* XXX: Use in proto selection */
|
2012-01-25 16:30:28 -08:00
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_init(&ofpacts, 64);
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
|
error = ofpacts_parse_actions(argv[3], &ofpacts, &usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2012-01-25 16:30:28 -08:00
|
|
|
|
|
|
|
|
|
po.buffer_id = UINT32_MAX;
|
2012-09-20 08:40:29 -07:00
|
|
|
|
po.in_port = str_to_port_no(argv[1], argv[2]);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
po.ofpacts = ofpbuf_data(&ofpacts);
|
|
|
|
|
po.ofpacts_len = ofpbuf_size(&ofpacts);
|
2012-01-25 16:30:28 -08:00
|
|
|
|
|
2012-08-08 12:19:57 +09:00
|
|
|
|
protocol = open_vconn(argv[1], &vconn);
|
2012-01-25 16:30:28 -08:00
|
|
|
|
for (i = 4; i < argc; i++) {
|
|
|
|
|
struct ofpbuf *packet, *opo;
|
|
|
|
|
const char *error_msg;
|
|
|
|
|
|
|
|
|
|
error_msg = eth_from_hex(argv[i], &packet);
|
|
|
|
|
if (error_msg) {
|
|
|
|
|
ovs_fatal(0, "%s", error_msg);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
po.packet = ofpbuf_data(packet);
|
|
|
|
|
po.packet_len = ofpbuf_size(packet);
|
2012-08-08 12:19:57 +09:00
|
|
|
|
opo = ofputil_encode_packet_out(&po, protocol);
|
2012-01-25 16:30:28 -08:00
|
|
|
|
transact_noreply(vconn, opo);
|
|
|
|
|
ofpbuf_delete(packet);
|
|
|
|
|
}
|
|
|
|
|
vconn_close(vconn);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2012-01-25 16:30:28 -08:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_mod_port(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-05-24 14:01:02 -07:00
|
|
|
|
struct ofp_config_flag {
|
|
|
|
|
const char *name; /* The flag's name. */
|
|
|
|
|
enum ofputil_port_config bit; /* Bit to turn on or off. */
|
|
|
|
|
bool on; /* Value to set the bit to. */
|
|
|
|
|
};
|
|
|
|
|
static const struct ofp_config_flag flags[] = {
|
|
|
|
|
{ "up", OFPUTIL_PC_PORT_DOWN, false },
|
|
|
|
|
{ "down", OFPUTIL_PC_PORT_DOWN, true },
|
|
|
|
|
{ "stp", OFPUTIL_PC_NO_STP, false },
|
|
|
|
|
{ "receive", OFPUTIL_PC_NO_RECV, false },
|
|
|
|
|
{ "receive-stp", OFPUTIL_PC_NO_RECV_STP, false },
|
|
|
|
|
{ "flood", OFPUTIL_PC_NO_FLOOD, false },
|
|
|
|
|
{ "forward", OFPUTIL_PC_NO_FWD, false },
|
|
|
|
|
{ "packet-in", OFPUTIL_PC_NO_PACKET_IN, false },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct ofp_config_flag *flag;
|
2012-02-15 16:33:04 -08:00
|
|
|
|
enum ofputil_protocol protocol;
|
|
|
|
|
struct ofputil_port_mod pm;
|
|
|
|
|
struct ofputil_phy_port pp;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct vconn *vconn;
|
2012-05-24 14:01:02 -07:00
|
|
|
|
const char *command;
|
|
|
|
|
bool not;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
fetch_ofputil_phy_port(argv[1], argv[2], &pp);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
pm.port_no = pp.port_no;
|
|
|
|
|
memcpy(pm.hw_addr, pp.hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
pm.config = 0;
|
|
|
|
|
pm.mask = 0;
|
|
|
|
|
pm.advertise = 0;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-05-24 14:01:02 -07:00
|
|
|
|
if (!strncasecmp(argv[3], "no-", 3)) {
|
|
|
|
|
command = argv[3] + 3;
|
|
|
|
|
not = true;
|
|
|
|
|
} else if (!strncasecmp(argv[3], "no", 2)) {
|
|
|
|
|
command = argv[3] + 2;
|
|
|
|
|
not = true;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
2012-05-24 14:01:02 -07:00
|
|
|
|
command = argv[3];
|
|
|
|
|
not = false;
|
|
|
|
|
}
|
|
|
|
|
for (flag = flags; flag < &flags[ARRAY_SIZE(flags)]; flag++) {
|
|
|
|
|
if (!strcasecmp(command, flag->name)) {
|
|
|
|
|
pm.mask = flag->bit;
|
|
|
|
|
pm.config = flag->on ^ not ? flag->bit : 0;
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2012-05-24 14:01:02 -07:00
|
|
|
|
ovs_fatal(0, "unknown mod-port command '%s'", argv[3]);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-05-24 14:01:02 -07:00
|
|
|
|
found:
|
2012-02-15 16:33:04 -08:00
|
|
|
|
protocol = open_vconn(argv[1], &vconn);
|
|
|
|
|
transact_noreply(vconn, ofputil_encode_port_mod(&pm, protocol));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-07 03:02:32 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_mod_table(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_protocol protocol, usable_protocols;
|
|
|
|
|
struct ofputil_table_mod tm;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
char *error;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
error = parse_ofp_table_mod(&tm, argv[2], argv[3], &usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protocol = open_vconn(argv[1], &vconn);
|
|
|
|
|
if (!(protocol & usable_protocols)) {
|
|
|
|
|
for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
|
|
|
|
|
enum ofputil_protocol f = 1 << i;
|
|
|
|
|
if (f != protocol
|
|
|
|
|
&& f & usable_protocols
|
|
|
|
|
&& try_set_protocol(vconn, f, &protocol)) {
|
|
|
|
|
protocol = f;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(protocol & usable_protocols)) {
|
|
|
|
|
char *usable_s = ofputil_protocols_to_string(usable_protocols);
|
|
|
|
|
ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofp_switch_config config;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
fetch_switch_config(vconn, &config);
|
|
|
|
|
puts(ofputil_frag_handling_to_string(ntohs(config.flags)));
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofp_switch_config config;
|
|
|
|
|
enum ofp_config_flags mode;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
ovs_be16 flags;
|
|
|
|
|
|
|
|
|
|
if (!ofputil_frag_handling_from_string(argv[2], &mode)) {
|
|
|
|
|
ovs_fatal(0, "%s: unknown fragment handling mode", argv[2]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
fetch_switch_config(vconn, &config);
|
|
|
|
|
flags = htons(mode) | (config.flags & htons(~OFPC_FRAG_MASK));
|
|
|
|
|
if (flags != config.flags) {
|
|
|
|
|
/* Set the configuration. */
|
|
|
|
|
config.flags = flags;
|
|
|
|
|
set_switch_config(vconn, &config);
|
|
|
|
|
|
|
|
|
|
/* Then retrieve the configuration to see if it really took. OpenFlow
|
|
|
|
|
* doesn't define error reporting for bad modes, so this is all we can
|
|
|
|
|
* do. */
|
|
|
|
|
fetch_switch_config(vconn, &config);
|
|
|
|
|
if (flags != config.flags) {
|
|
|
|
|
ovs_fatal(0, "%s: setting fragment handling mode failed (this "
|
|
|
|
|
"switch probably doesn't support mode \"%s\")",
|
|
|
|
|
argv[1], ofputil_frag_handling_to_string(mode));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-06 09:45:07 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_ofp_parse(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
const char *filename = argv[1];
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
|
|
file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
|
|
|
|
|
if (file == NULL) {
|
|
|
|
|
ovs_fatal(errno, "%s: open", filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_init(&b, 65536);
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofp_header *oh;
|
|
|
|
|
size_t length, tail_len;
|
|
|
|
|
void *tail;
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
ofpbuf_clear(&b);
|
|
|
|
|
oh = ofpbuf_put_uninit(&b, sizeof *oh);
|
|
|
|
|
n = fread(oh, 1, sizeof *oh, file);
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (n < sizeof *oh) {
|
|
|
|
|
ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
length = ntohs(oh->length);
|
|
|
|
|
if (length < sizeof *oh) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
ovs_fatal(0, "%s: %"PRIuSIZE"-byte message is too short for OpenFlow",
|
2013-08-06 09:45:07 -07:00
|
|
|
|
filename, length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tail_len = length - sizeof *oh;
|
|
|
|
|
tail = ofpbuf_put_uninit(&b, tail_len);
|
|
|
|
|
n = fread(tail, 1, tail_len, file);
|
|
|
|
|
if (n < tail_len) {
|
|
|
|
|
ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(&b), ofpbuf_size(&b), verbosity + 2);
|
2013-08-06 09:45:07 -07:00
|
|
|
|
}
|
|
|
|
|
ofpbuf_uninit(&b);
|
|
|
|
|
|
|
|
|
|
if (file != stdin) {
|
|
|
|
|
fclose(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-22 13:17:23 -08:00
|
|
|
|
static bool
|
|
|
|
|
is_openflow_port(ovs_be16 port_, char *ports[])
|
|
|
|
|
{
|
|
|
|
|
uint16_t port = ntohs(port_);
|
|
|
|
|
if (ports[0]) {
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; ports[i]; i++) {
|
|
|
|
|
if (port == atoi(ports[i])) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
return port == OFP_PORT || port == OFP_OLD_PORT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_ofp_parse_pcap(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct tcp_reader *reader;
|
|
|
|
|
FILE *file;
|
|
|
|
|
int error;
|
|
|
|
|
bool first;
|
|
|
|
|
|
2014-01-23 17:24:03 +01:00
|
|
|
|
file = ovs_pcap_open(argv[1], "rb");
|
2013-11-22 13:17:23 -08:00
|
|
|
|
if (!file) {
|
|
|
|
|
ovs_fatal(errno, "%s: open failed", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reader = tcp_reader_open();
|
|
|
|
|
first = true;
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofpbuf *packet;
|
|
|
|
|
long long int when;
|
|
|
|
|
struct flow flow;
|
2014-02-26 18:08:04 -08:00
|
|
|
|
const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
|
2013-11-22 13:17:23 -08:00
|
|
|
|
|
2014-01-23 17:24:03 +01:00
|
|
|
|
error = ovs_pcap_read(file, &packet, &when);
|
2013-11-22 13:17:23 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-02-26 18:08:04 -08:00
|
|
|
|
flow_extract(packet, &md, &flow);
|
2013-11-22 13:17:23 -08:00
|
|
|
|
if (flow.dl_type == htons(ETH_TYPE_IP)
|
|
|
|
|
&& flow.nw_proto == IPPROTO_TCP
|
|
|
|
|
&& (is_openflow_port(flow.tp_src, argv + 2) ||
|
|
|
|
|
is_openflow_port(flow.tp_dst, argv + 2))) {
|
|
|
|
|
struct ofpbuf *payload = tcp_reader_run(reader, &flow, packet);
|
|
|
|
|
if (payload) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
while (ofpbuf_size(payload) >= sizeof(struct ofp_header)) {
|
2013-11-22 13:17:23 -08:00
|
|
|
|
const struct ofp_header *oh;
|
2014-03-30 01:31:50 -07:00
|
|
|
|
void *data = ofpbuf_data(payload);
|
2013-11-22 13:17:23 -08:00
|
|
|
|
int length;
|
|
|
|
|
|
|
|
|
|
/* Align OpenFlow on 8-byte boundary for safe access. */
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_shift(payload, -((intptr_t) data & 7));
|
2013-11-22 13:17:23 -08:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
oh = ofpbuf_data(payload);
|
2013-11-22 13:17:23 -08:00
|
|
|
|
length = ntohs(oh->length);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(payload) < length) {
|
2013-11-22 13:17:23 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!first) {
|
|
|
|
|
putchar('\n');
|
|
|
|
|
}
|
|
|
|
|
first = false;
|
|
|
|
|
|
|
|
|
|
if (timestamp) {
|
|
|
|
|
char *s = xastrftime_msec("%H:%M:%S.### ", when, true);
|
|
|
|
|
fputs(s, stdout);
|
|
|
|
|
free(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n",
|
|
|
|
|
IP_ARGS(flow.nw_src), ntohs(flow.tp_src),
|
|
|
|
|
IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst));
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(payload), length, verbosity + 1);
|
2013-11-22 13:17:23 -08:00
|
|
|
|
ofpbuf_pull(payload, length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_delete(packet);
|
|
|
|
|
}
|
|
|
|
|
tcp_reader_close(reader);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_ping(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
size_t max_payload = 65535 - sizeof(struct ofp_header);
|
|
|
|
|
unsigned int payload;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
payload = argc > 2 ? atoi(argv[2]) : 64;
|
|
|
|
|
if (payload > max_payload) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
ovs_fatal(0, "payload must be between 0 and %"PRIuSIZE" bytes", max_payload);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
|
struct timeval start, end;
|
|
|
|
|
struct ofpbuf *request, *reply;
|
2012-07-19 23:23:17 -07:00
|
|
|
|
const struct ofp_header *rpy_hdr;
|
|
|
|
|
enum ofptype type;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-11-19 14:59:32 +09:00
|
|
|
|
request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST,
|
|
|
|
|
vconn_get_version(vconn), payload);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
random_bytes(ofpbuf_put_uninit(request, payload), payload);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-03-31 16:23:50 -07:00
|
|
|
|
xgettimeofday(&start);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
|
2011-03-31 16:23:50 -07:00
|
|
|
|
xgettimeofday(&end);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
rpy_hdr = ofpbuf_data(reply);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
if (ofptype_pull(&type, reply)
|
|
|
|
|
|| type != OFPTYPE_ECHO_REPLY
|
2014-03-30 01:31:50 -07:00
|
|
|
|
|| ofpbuf_size(reply) != payload
|
2014-04-03 11:51:54 -07:00
|
|
|
|
|| memcmp(ofpbuf_l3(request), ofpbuf_l3(reply), payload)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
printf("Reply does not match request. Request:\n");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, request, ofpbuf_size(request), verbosity + 2);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
printf("Reply:\n");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, reply, ofpbuf_size(reply), verbosity + 2);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
lib/ofpbuf: Compact
This patch shrinks the struct ofpbuf from 104 to 48 bytes on 64-bit
systems, or from 52 to 36 bytes on 32-bit systems (counting in the
'l7' removal from an earlier patch). This may help contribute to
cache efficiency, and will speed up initializing, copying and
manipulating ofpbufs. This is potentially important for the DPDK
datapath, but the rest of the code base may also see a little benefit.
Changes are:
- Remove 'l7' pointer (previous patch).
- Use offsets instead of layer pointers for l2_5, l3, and l4 using
'l2' as basis. Usually 'data' is the same as 'l2', but this is not
always the case (e.g., when parsing or constructing a packet), so it
can not be easily used as the offset basis. Also, packet parsing is
faster if we do not need to maintain the offsets each time we pull
data from the ofpbuf.
- Use uint32_t for 'allocated' and 'size', as 2^32 is enough even for
largest possible messages/packets.
- Use packed enum for 'source'.
- Rearrange to avoid unnecessary padding.
- Remove 'private_p', which was used only in two cases, both of which
had the invariant ('l2' == 'data'), so we can temporarily use 'l2'
as a private pointer.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2014-03-24 09:17:01 -07:00
|
|
|
|
printf("%"PRIu32" bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_size(reply), argv[1], ntohl(rpy_hdr->xid),
|
2009-07-08 13:19:16 -07:00
|
|
|
|
(1000*(double)(end.tv_sec - start.tv_sec))
|
|
|
|
|
+ (.001*(end.tv_usec - start.tv_usec)));
|
|
|
|
|
ofpbuf_delete(request);
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
size_t max_payload = 65535 - sizeof(struct ofp_header);
|
|
|
|
|
struct timeval start, end;
|
|
|
|
|
unsigned int payload_size, message_size;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
double duration;
|
|
|
|
|
int count;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
payload_size = atoi(argv[2]);
|
|
|
|
|
if (payload_size > max_payload) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
ovs_fatal(0, "payload must be between 0 and %"PRIuSIZE" bytes", max_payload);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
message_size = sizeof(struct ofp_header) + payload_size;
|
|
|
|
|
|
|
|
|
|
count = atoi(argv[3]);
|
|
|
|
|
|
|
|
|
|
printf("Sending %d packets * %u bytes (with header) = %u bytes total\n",
|
|
|
|
|
count, message_size, count * message_size);
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
2011-03-31 16:23:50 -07:00
|
|
|
|
xgettimeofday(&start);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
|
struct ofpbuf *request, *reply;
|
|
|
|
|
|
2012-11-19 14:59:32 +09:00
|
|
|
|
request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST,
|
|
|
|
|
vconn_get_version(vconn), payload_size);
|
2012-07-19 23:23:17 -07:00
|
|
|
|
ofpbuf_put_zeros(request, payload_size);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
run(vconn_transact(vconn, request, &reply), "transact");
|
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
2011-03-31 16:23:50 -07:00
|
|
|
|
xgettimeofday(&end);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
|
|
|
|
|
duration = ((1000*(double)(end.tv_sec - start.tv_sec))
|
|
|
|
|
+ (.001*(end.tv_usec - start.tv_usec)));
|
|
|
|
|
printf("Finished in %.1f ms (%.0f packets/s) (%.0f bytes/s)\n",
|
|
|
|
|
duration, count / (duration / 1000.0),
|
|
|
|
|
count * message_size / (duration / 1000.0));
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-01 18:30:17 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms,
|
|
|
|
|
size_t n_gms)
|
|
|
|
|
{
|
|
|
|
|
struct ofputil_group_mod *gm;
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
open_vconn(remote, &vconn);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_gms; i++) {
|
|
|
|
|
gm = &gms[i];
|
|
|
|
|
request = ofputil_encode_group_mod(vconn_get_version(vconn), gm);
|
|
|
|
|
if (request) {
|
|
|
|
|
transact_noreply(vconn, request);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
|
|
|
|
|
{
|
|
|
|
|
struct ofputil_group_mod *gms = NULL;
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
size_t n_gms = 0;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
error = parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms,
|
|
|
|
|
&usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
|
|
|
|
ofctl_group_mod__(argv[1], gms, n_gms);
|
|
|
|
|
free(gms);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_group_mod(int argc, char *argv[], uint16_t command)
|
|
|
|
|
{
|
|
|
|
|
if (argc > 2 && !strcmp(argv[2], "-")) {
|
|
|
|
|
ofctl_group_mod_file(argc, argv, command);
|
|
|
|
|
} else {
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
struct ofputil_group_mod gm;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "",
|
|
|
|
|
&usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
|
|
|
|
ofctl_group_mod__(argv[1], &gm, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_add_group(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_group_mod(argc, argv, OFPGC11_ADD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_add_groups(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_group_mod_file(argc, argv, OFPGC11_ADD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_mod_group(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_group_mod(argc, argv, OFPGC11_MODIFY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_del_groups(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_group_mod(argc, argv, OFPGC11_DELETE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_dump_group_stats(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
struct ofputil_group_mod gm;
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
uint32_t group_id;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
memset(&gm, 0, sizeof gm);
|
|
|
|
|
|
|
|
|
|
error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
|
|
|
|
|
argc > 2 ? argv[2] : "",
|
|
|
|
|
&usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
group_id = gm.group_id;
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
request = ofputil_encode_group_stats_request(vconn_get_version(vconn),
|
|
|
|
|
group_id);
|
|
|
|
|
if (request) {
|
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_dump_group_desc(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
2014-05-07 23:49:00 -07:00
|
|
|
|
uint32_t group_id;
|
2013-09-01 18:30:17 -07:00
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
|
2014-05-07 23:49:00 -07:00
|
|
|
|
if (argc < 3 || !ofputil_group_from_string(argv[2], &group_id)) {
|
|
|
|
|
group_id = OFPG11_ALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request = ofputil_encode_group_desc_request(vconn_get_version(vconn),
|
|
|
|
|
group_id);
|
2013-09-01 18:30:17 -07:00
|
|
|
|
if (request) {
|
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_dump_group_features(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *request;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
|
|
|
|
open_vconn(argv[1], &vconn);
|
|
|
|
|
request = ofputil_encode_group_features_request(vconn_get_version(vconn));
|
|
|
|
|
if (request) {
|
|
|
|
|
dump_stats_transaction(vconn, request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
|
|
|
|
usage();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-11 12:02:27 -08:00
|
|
|
|
/* replace-flows and diff-flows commands. */
|
|
|
|
|
|
|
|
|
|
/* A flow table entry, possibly with two different versions. */
|
|
|
|
|
struct fte {
|
|
|
|
|
struct cls_rule rule; /* Within a "struct classifier". */
|
|
|
|
|
struct fte_version *versions[2];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* One version of a Flow Table Entry. */
|
|
|
|
|
struct fte_version {
|
|
|
|
|
ovs_be64 cookie;
|
|
|
|
|
uint16_t idle_timeout;
|
|
|
|
|
uint16_t hard_timeout;
|
|
|
|
|
uint16_t flags;
|
2012-07-03 22:17:14 -07:00
|
|
|
|
struct ofpact *ofpacts;
|
|
|
|
|
size_t ofpacts_len;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Frees 'version' and the data that it owns. */
|
|
|
|
|
static void
|
|
|
|
|
fte_version_free(struct fte_version *version)
|
|
|
|
|
{
|
|
|
|
|
if (version) {
|
2014-04-29 15:50:38 -07:00
|
|
|
|
free(CONST_CAST(struct ofpact *, version->ofpacts));
|
2011-03-11 12:02:27 -08:00
|
|
|
|
free(version);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns true if 'a' and 'b' are the same, false if they differ.
|
|
|
|
|
*
|
|
|
|
|
* Ignores differences in 'flags' because there's no way to retrieve flags from
|
|
|
|
|
* an OpenFlow switch. We have to assume that they are the same. */
|
|
|
|
|
static bool
|
|
|
|
|
fte_version_equals(const struct fte_version *a, const struct fte_version *b)
|
|
|
|
|
{
|
|
|
|
|
return (a->cookie == b->cookie
|
|
|
|
|
&& a->idle_timeout == b->idle_timeout
|
|
|
|
|
&& a->hard_timeout == b->hard_timeout
|
2012-07-03 22:17:14 -07:00
|
|
|
|
&& ofpacts_equal(a->ofpacts, a->ofpacts_len,
|
|
|
|
|
b->ofpacts, b->ofpacts_len));
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-23 10:16:31 -07:00
|
|
|
|
/* Clears 's', then if 's' has a version 'index', formats 'fte' and version
|
|
|
|
|
* 'index' into 's', followed by a new-line. */
|
2011-03-11 12:02:27 -08:00
|
|
|
|
static void
|
2012-07-23 10:16:31 -07:00
|
|
|
|
fte_version_format(const struct fte *fte, int index, struct ds *s)
|
2011-03-11 12:02:27 -08:00
|
|
|
|
{
|
2012-07-23 10:16:31 -07:00
|
|
|
|
const struct fte_version *version = fte->versions[index];
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ds_clear(s);
|
|
|
|
|
if (!version) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cls_rule_format(&fte->rule, s);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
if (version->cookie != htonll(0)) {
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie));
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
if (version->idle_timeout != OFP_FLOW_PERMANENT) {
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ds_put_format(s, " idle_timeout=%"PRIu16, version->idle_timeout);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
if (version->hard_timeout != OFP_FLOW_PERMANENT) {
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ds_put_format(s, " hard_timeout=%"PRIu16, version->hard_timeout);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-14 14:08:20 -07:00
|
|
|
|
ds_put_cstr(s, " actions=");
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ofpacts_format(version->ofpacts, version->ofpacts_len, s);
|
|
|
|
|
|
|
|
|
|
ds_put_char(s, '\n');
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct fte *
|
|
|
|
|
fte_from_cls_rule(const struct cls_rule *cls_rule)
|
|
|
|
|
{
|
|
|
|
|
return cls_rule ? CONTAINER_OF(cls_rule, struct fte, rule) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Frees 'fte' and its versions. */
|
|
|
|
|
static void
|
|
|
|
|
fte_free(struct fte *fte)
|
|
|
|
|
{
|
|
|
|
|
if (fte) {
|
|
|
|
|
fte_version_free(fte->versions[0]);
|
|
|
|
|
fte_version_free(fte->versions[1]);
|
2012-08-20 11:29:43 -07:00
|
|
|
|
cls_rule_destroy(&fte->rule);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
free(fte);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Frees all of the FTEs within 'cls'. */
|
|
|
|
|
static void
|
|
|
|
|
fte_free_all(struct classifier *cls)
|
|
|
|
|
{
|
2014-07-21 21:00:04 -07:00
|
|
|
|
struct fte *fte;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2014-07-21 21:00:04 -07:00
|
|
|
|
CLS_FOR_EACH_SAFE (fte, rule, cls) {
|
2011-03-11 12:02:27 -08:00
|
|
|
|
classifier_remove(cls, &fte->rule);
|
|
|
|
|
fte_free(fte);
|
|
|
|
|
}
|
2012-03-27 14:09:14 -07:00
|
|
|
|
classifier_destroy(cls);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Searches 'cls' for an FTE matching 'rule', inserting a new one if
|
|
|
|
|
* necessary. Sets 'version' as the version of that rule with the given
|
|
|
|
|
* 'index', replacing any existing version, if any.
|
|
|
|
|
*
|
|
|
|
|
* Takes ownership of 'version'. */
|
|
|
|
|
static void
|
2012-08-07 15:28:18 -07:00
|
|
|
|
fte_insert(struct classifier *cls, const struct match *match,
|
|
|
|
|
unsigned int priority, struct fte_version *version, int index)
|
2011-03-11 12:02:27 -08:00
|
|
|
|
{
|
|
|
|
|
struct fte *old, *fte;
|
|
|
|
|
|
|
|
|
|
fte = xzalloc(sizeof *fte);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
cls_rule_init(&fte->rule, match, priority);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
fte->versions[index] = version;
|
|
|
|
|
|
2011-05-11 14:06:48 -07:00
|
|
|
|
old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
|
2011-03-11 12:02:27 -08:00
|
|
|
|
if (old) {
|
|
|
|
|
fte_version_free(old->versions[index]);
|
|
|
|
|
fte->versions[!index] = old->versions[!index];
|
2012-08-20 11:29:43 -07:00
|
|
|
|
cls_rule_destroy(&old->rule);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
free(old);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reads the flows in 'filename' as flow table entries in 'cls' for the version
|
2012-02-10 13:30:23 -08:00
|
|
|
|
* with the specified 'index'. Returns the flow formats able to represent the
|
|
|
|
|
* flows that were read. */
|
|
|
|
|
static enum ofputil_protocol
|
2011-03-11 12:02:27 -08:00
|
|
|
|
read_flows_from_file(const char *filename, struct classifier *cls, int index)
|
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol usable_protocols;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
int line_number;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct ds s;
|
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
|
|
file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
|
|
|
|
|
if (file == NULL) {
|
|
|
|
|
ovs_fatal(errno, "%s: open", filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_init(&s);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
usable_protocols = OFPUTIL_P_ANY;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
line_number = 0;
|
|
|
|
|
while (!ds_get_preprocessed_line(&s, file, &line_number)) {
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct fte_version *version;
|
2011-08-08 14:46:38 -07:00
|
|
|
|
struct ofputil_flow_mod fm;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2013-08-20 18:41:45 -07:00
|
|
|
|
enum ofputil_protocol usable;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2013-11-15 14:19:57 -08:00
|
|
|
|
error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
|
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
|
usable_protocols &= usable;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
|
|
|
|
version = xmalloc(sizeof *version);
|
2012-03-24 01:02:26 -07:00
|
|
|
|
version->cookie = fm.new_cookie;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
version->idle_timeout = fm.idle_timeout;
|
|
|
|
|
version->hard_timeout = fm.hard_timeout;
|
2013-08-26 16:23:50 -07:00
|
|
|
|
version->flags = fm.flags & (OFPUTIL_FF_SEND_FLOW_REM
|
|
|
|
|
| OFPUTIL_FF_EMERG);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
version->ofpacts = fm.ofpacts;
|
|
|
|
|
version->ofpacts_len = fm.ofpacts_len;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
fte_insert(cls, &fm.match, fm.priority, version, index);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
|
|
|
|
|
if (file != stdin) {
|
|
|
|
|
fclose(file);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return usable_protocols;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-03 11:31:03 -07:00
|
|
|
|
static bool
|
|
|
|
|
recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
|
|
|
|
|
struct ofpbuf **replyp,
|
|
|
|
|
struct ofputil_flow_stats *fs, struct ofpbuf *ofpacts)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *reply = *replyp;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
2012-07-03 23:01:59 -07:00
|
|
|
|
bool more;
|
2012-07-03 11:31:03 -07:00
|
|
|
|
|
|
|
|
|
/* Get a flow stats reply message, if we don't already have one. */
|
|
|
|
|
if (!reply) {
|
2012-07-19 23:23:17 -07:00
|
|
|
|
enum ofptype type;
|
|
|
|
|
enum ofperr error;
|
2012-07-03 11:31:03 -07:00
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
run(vconn_recv_block(vconn, &reply),
|
|
|
|
|
"OpenFlow packet receive failed");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
} while (((struct ofp_header *) ofpbuf_data(reply))->xid != send_xid);
|
2012-07-03 11:31:03 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
error = ofptype_decode(&type, ofpbuf_data(reply));
|
2012-07-19 23:23:17 -07:00
|
|
|
|
if (error || type != OFPTYPE_FLOW_STATS_REPLY) {
|
2012-07-03 11:31:03 -07:00
|
|
|
|
ovs_fatal(0, "received bad reply: %s",
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_to_string(ofpbuf_data(reply), ofpbuf_size(reply),
|
2012-07-03 11:31:03 -07:00
|
|
|
|
verbosity + 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pull an individual flow stats reply out of the message. */
|
|
|
|
|
retval = ofputil_decode_flow_stats_reply(fs, reply, false, ofpacts);
|
|
|
|
|
switch (retval) {
|
|
|
|
|
case 0:
|
|
|
|
|
*replyp = reply;
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case EOF:
|
2014-04-02 15:44:21 -07:00
|
|
|
|
more = ofpmp_more(reply->frame);
|
2012-07-03 11:31:03 -07:00
|
|
|
|
ofpbuf_delete(reply);
|
2012-07-16 10:23:58 -07:00
|
|
|
|
reply = NULL;
|
2012-07-03 23:01:59 -07:00
|
|
|
|
if (!more) {
|
2012-07-03 11:31:03 -07:00
|
|
|
|
*replyp = NULL;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ovs_fatal(0, "parse error in reply (%s)",
|
|
|
|
|
ofperr_to_string(retval));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-11 12:02:27 -08:00
|
|
|
|
/* Reads the OpenFlow flow table from 'vconn', which has currently active flow
|
2012-02-10 13:30:23 -08:00
|
|
|
|
* format 'protocol', and adds them as flow table entries in 'cls' for the
|
2011-03-11 12:02:27 -08:00
|
|
|
|
* version with the specified 'index'. */
|
|
|
|
|
static void
|
2012-02-10 13:30:23 -08:00
|
|
|
|
read_flows_from_switch(struct vconn *vconn,
|
|
|
|
|
enum ofputil_protocol protocol,
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct classifier *cls, int index)
|
|
|
|
|
{
|
2011-08-08 14:48:48 -07:00
|
|
|
|
struct ofputil_flow_stats_request fsr;
|
2012-07-03 11:31:03 -07:00
|
|
|
|
struct ofputil_flow_stats fs;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct ofpbuf *request;
|
2012-07-03 11:31:03 -07:00
|
|
|
|
struct ofpbuf ofpacts;
|
|
|
|
|
struct ofpbuf *reply;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
ovs_be32 send_xid;
|
|
|
|
|
|
|
|
|
|
fsr.aggregate = false;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_init_catchall(&fsr.match);
|
2012-11-26 18:17:08 +02:00
|
|
|
|
fsr.out_port = OFPP_ANY;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
fsr.table_id = 0xff;
|
2011-12-30 17:56:08 -08:00
|
|
|
|
fsr.cookie = fsr.cookie_mask = htonll(0);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
request = ofputil_encode_flow_stats_request(&fsr, protocol);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
send_xid = ((struct ofp_header *) ofpbuf_data(request))->xid;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
send_openflow_buffer(vconn, request);
|
|
|
|
|
|
2012-07-03 11:31:03 -07:00
|
|
|
|
reply = NULL;
|
|
|
|
|
ofpbuf_init(&ofpacts, 0);
|
|
|
|
|
while (recv_flow_stats_reply(vconn, send_xid, &reply, &fs, &ofpacts)) {
|
|
|
|
|
struct fte_version *version;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2012-07-03 11:31:03 -07:00
|
|
|
|
version = xmalloc(sizeof *version);
|
|
|
|
|
version->cookie = fs.cookie;
|
|
|
|
|
version->idle_timeout = fs.idle_timeout;
|
|
|
|
|
version->hard_timeout = fs.hard_timeout;
|
|
|
|
|
version->flags = 0;
|
|
|
|
|
version->ofpacts_len = fs.ofpacts_len;
|
|
|
|
|
version->ofpacts = xmemdup(fs.ofpacts, fs.ofpacts_len);
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
fte_insert(cls, &fs.match, fs.priority, version, index);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
2012-07-03 11:31:03 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol, struct list *packets)
|
2011-03-11 12:02:27 -08:00
|
|
|
|
{
|
|
|
|
|
const struct fte_version *version = fte->versions[index];
|
2011-08-08 14:46:38 -07:00
|
|
|
|
struct ofputil_flow_mod fm;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct ofpbuf *ofm;
|
|
|
|
|
|
2012-09-04 12:43:53 -07:00
|
|
|
|
minimatch_expand(&fte->rule.match, &fm.match);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
fm.priority = fte->rule.priority;
|
2012-03-24 01:02:26 -07:00
|
|
|
|
fm.cookie = htonll(0);
|
|
|
|
|
fm.cookie_mask = htonll(0);
|
|
|
|
|
fm.new_cookie = version->cookie;
|
2013-04-17 13:02:15 -07:00
|
|
|
|
fm.modify_cookie = true;
|
2011-05-12 09:58:01 -07:00
|
|
|
|
fm.table_id = 0xff;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
fm.command = command;
|
|
|
|
|
fm.idle_timeout = version->idle_timeout;
|
|
|
|
|
fm.hard_timeout = version->hard_timeout;
|
|
|
|
|
fm.buffer_id = UINT32_MAX;
|
2012-11-26 18:17:08 +02:00
|
|
|
|
fm.out_port = OFPP_ANY;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
fm.flags = version->flags;
|
|
|
|
|
if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
|
|
|
|
|
command == OFPFC_MODIFY_STRICT) {
|
2012-07-03 22:17:14 -07:00
|
|
|
|
fm.ofpacts = version->ofpacts;
|
|
|
|
|
fm.ofpacts_len = version->ofpacts_len;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
} else {
|
2012-07-03 22:17:14 -07:00
|
|
|
|
fm.ofpacts = NULL;
|
|
|
|
|
fm.ofpacts_len = 0;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
2014-05-30 17:14:14 +09:00
|
|
|
|
fm.delete_reason = OFPRR_DELETE;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofm = ofputil_encode_flow_mod(&fm, protocol);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
list_push_back(packets, &ofm->list_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
|
2011-03-11 12:02:27 -08:00
|
|
|
|
{
|
|
|
|
|
enum { FILE_IDX = 0, SWITCH_IDX = 1 };
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol usable_protocols, protocol;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct classifier cls;
|
|
|
|
|
struct list requests;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
struct fte *fte;
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, NULL);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
usable_protocols = read_flows_from_file(argv[2], &cls, FILE_IDX);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
protocol = open_vconn(argv[1], &vconn);
|
|
|
|
|
protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
|
|
|
|
|
|
|
|
|
|
read_flows_from_switch(vconn, protocol, &cls, SWITCH_IDX);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
|
|
|
|
list_init(&requests);
|
|
|
|
|
|
|
|
|
|
/* Delete flows that exist on the switch but not in the file. */
|
2014-07-11 02:29:07 -07:00
|
|
|
|
CLS_FOR_EACH (fte, rule, &cls) {
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct fte_version *file_ver = fte->versions[FILE_IDX];
|
|
|
|
|
struct fte_version *sw_ver = fte->versions[SWITCH_IDX];
|
|
|
|
|
|
|
|
|
|
if (sw_ver && !file_ver) {
|
|
|
|
|
fte_make_flow_mod(fte, SWITCH_IDX, OFPFC_DELETE_STRICT,
|
2012-02-10 13:30:23 -08:00
|
|
|
|
protocol, &requests);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add flows that exist in the file but not on the switch.
|
|
|
|
|
* Update flows that exist in both places but differ. */
|
2014-07-11 02:29:07 -07:00
|
|
|
|
CLS_FOR_EACH (fte, rule, &cls) {
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct fte_version *file_ver = fte->versions[FILE_IDX];
|
|
|
|
|
struct fte_version *sw_ver = fte->versions[SWITCH_IDX];
|
|
|
|
|
|
2011-07-27 14:58:10 -07:00
|
|
|
|
if (file_ver
|
|
|
|
|
&& (readd || !sw_ver || !fte_version_equals(sw_ver, file_ver))) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
transact_multiple_noreply(vconn, &requests);
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
|
|
|
|
|
fte_free_all(&cls);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
read_flows_from_source(const char *source, struct classifier *cls, int index)
|
|
|
|
|
{
|
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
|
|
if (source[0] == '/' || source[0] == '.'
|
|
|
|
|
|| (!strchr(source, ':') && !stat(source, &s))) {
|
|
|
|
|
read_flows_from_file(source, cls, index);
|
|
|
|
|
} else {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
protocol = open_vconn(source, &vconn);
|
|
|
|
|
protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY);
|
|
|
|
|
read_flows_from_switch(vconn, protocol, cls, index);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
|
2011-03-11 12:02:27 -08:00
|
|
|
|
{
|
|
|
|
|
bool differences = false;
|
|
|
|
|
struct classifier cls;
|
2012-07-23 10:16:31 -07:00
|
|
|
|
struct ds a_s, b_s;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct fte *fte;
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, NULL);
|
2011-03-11 12:02:27 -08:00
|
|
|
|
read_flows_from_source(argv[1], &cls, 0);
|
|
|
|
|
read_flows_from_source(argv[2], &cls, 1);
|
|
|
|
|
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ds_init(&a_s);
|
|
|
|
|
ds_init(&b_s);
|
|
|
|
|
|
2014-07-11 02:29:07 -07:00
|
|
|
|
CLS_FOR_EACH (fte, rule, &cls) {
|
2011-03-11 12:02:27 -08:00
|
|
|
|
struct fte_version *a = fte->versions[0];
|
|
|
|
|
struct fte_version *b = fte->versions[1];
|
|
|
|
|
|
|
|
|
|
if (!a || !b || !fte_version_equals(a, b)) {
|
2012-07-23 10:16:31 -07:00
|
|
|
|
fte_version_format(fte, 0, &a_s);
|
|
|
|
|
fte_version_format(fte, 1, &b_s);
|
|
|
|
|
if (strcmp(ds_cstr(&a_s), ds_cstr(&b_s))) {
|
|
|
|
|
if (a_s.length) {
|
|
|
|
|
printf("-%s", ds_cstr(&a_s));
|
|
|
|
|
}
|
|
|
|
|
if (b_s.length) {
|
|
|
|
|
printf("+%s", ds_cstr(&b_s));
|
|
|
|
|
}
|
|
|
|
|
differences = true;
|
2011-03-11 12:02:27 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-23 10:16:31 -07:00
|
|
|
|
ds_destroy(&a_s);
|
|
|
|
|
ds_destroy(&b_s);
|
|
|
|
|
|
2011-03-11 12:02:27 -08:00
|
|
|
|
fte_free_all(&cls);
|
|
|
|
|
|
|
|
|
|
if (differences) {
|
|
|
|
|
exit(2);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-13 15:03:33 -07:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_meter_mod__(const char *bridge, const char *str, int command)
|
|
|
|
|
{
|
|
|
|
|
struct ofputil_meter_mod mm;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
enum ofputil_protocol protocol;
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
enum ofp_version version;
|
|
|
|
|
|
|
|
|
|
if (str) {
|
|
|
|
|
char *error;
|
|
|
|
|
error = parse_ofp_meter_mod_str(&mm, str, command, &usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
usable_protocols = OFPUTIL_P_OF13_UP;
|
|
|
|
|
mm.command = command;
|
|
|
|
|
mm.meter.meter_id = OFPM13_ALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
|
|
|
|
|
version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
transact_noreply(vconn, ofputil_encode_meter_mod(version, &mm));
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_meter_request__(const char *bridge, const char *str,
|
|
|
|
|
enum ofputil_meter_request_type type)
|
|
|
|
|
{
|
|
|
|
|
struct ofputil_meter_mod mm;
|
|
|
|
|
struct vconn *vconn;
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
enum ofputil_protocol protocol;
|
|
|
|
|
enum ofp_version version;
|
|
|
|
|
|
|
|
|
|
if (str) {
|
|
|
|
|
char *error;
|
|
|
|
|
error = parse_ofp_meter_mod_str(&mm, str, -1, &usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
usable_protocols = OFPUTIL_P_OF13_UP;
|
|
|
|
|
mm.meter.meter_id = OFPM13_ALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
|
|
|
|
|
version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
transact_noreply(vconn, ofputil_encode_meter_request(version,
|
|
|
|
|
type,
|
|
|
|
|
mm.meter.meter_id));
|
|
|
|
|
vconn_close(vconn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_add_meter(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_meter_mod__(argv[1], argv[2], OFPMC13_ADD);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_mod_meter(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_meter_mod__(argv[1], argv[2], OFPMC13_MODIFY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_del_meters(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_meter_mod__(argv[1], argc > 2 ? argv[2] : NULL, OFPMC13_DELETE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_dump_meters(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_meter_request__(argv[1], argc > 2 ? argv[2] : NULL,
|
|
|
|
|
OFPUTIL_METER_CONFIG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_meter_stats(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_meter_request__(argv[1], argc > 2 ? argv[2] : NULL,
|
|
|
|
|
OFPUTIL_METER_STATS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofctl_meter_features(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
ofctl_meter_request__(argv[1], NULL, OFPUTIL_METER_FEATURES);
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-11 12:02:27 -08:00
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
/* Undocumented commands for unit testing. */
|
|
|
|
|
|
2011-02-22 16:24:19 -08:00
|
|
|
|
static void
|
2013-08-20 18:41:45 -07:00
|
|
|
|
ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
|
|
|
|
|
enum ofputil_protocol usable_protocols)
|
2011-02-22 16:24:19 -08:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol = 0;
|
|
|
|
|
char *usable_s;
|
|
|
|
|
size_t i;
|
2011-02-22 16:24:19 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
usable_s = ofputil_protocols_to_string(usable_protocols);
|
|
|
|
|
printf("usable protocols: %s\n", usable_s);
|
|
|
|
|
free(usable_s);
|
|
|
|
|
|
|
|
|
|
if (!(usable_protocols & allowed_protocols)) {
|
|
|
|
|
ovs_fatal(0, "no usable protocol");
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
|
|
|
|
|
protocol = 1 << i;
|
|
|
|
|
if (protocol & usable_protocols & allowed_protocols) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-01 14:55:14 -08:00
|
|
|
|
ovs_assert(is_pow2(protocol));
|
2012-02-10 13:30:23 -08:00
|
|
|
|
|
|
|
|
|
printf("chosen protocol: %s\n", ofputil_protocol_to_string(protocol));
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_fms; i++) {
|
|
|
|
|
struct ofputil_flow_mod *fm = &fms[i];
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = ofputil_encode_flow_mod(fm, protocol);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(msg), ofpbuf_size(msg), verbosity);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofpbuf_delete(msg);
|
|
|
|
|
|
2014-04-29 15:50:38 -07:00
|
|
|
|
free(CONST_CAST(struct ofpact *, fm->ofpacts));
|
2011-02-22 16:24:19 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "parse-flow FLOW": parses the argument as a flow (like add-flow) and prints
|
|
|
|
|
* it back to stdout. */
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
|
2011-02-22 16:24:19 -08:00
|
|
|
|
{
|
2013-08-20 18:41:45 -07:00
|
|
|
|
enum ofputil_protocol usable_protocols;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
struct ofputil_flow_mod fm;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2011-02-22 16:24:19 -08:00
|
|
|
|
|
2013-11-15 14:19:57 -08:00
|
|
|
|
error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, &usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
|
ofctl_parse_flows__(&fm, 1, usable_protocols);
|
2011-02-22 16:24:19 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-12 13:57:53 -08:00
|
|
|
|
/* "parse-flows FILENAME": reads the named file as a sequence of flows (like
|
|
|
|
|
* add-flows) and prints each of the flows back to stdout. */
|
2010-10-01 13:08:14 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
|
2010-10-01 13:08:14 -07:00
|
|
|
|
{
|
2013-08-20 18:41:45 -07:00
|
|
|
|
enum ofputil_protocol usable_protocols;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
struct ofputil_flow_mod *fms = NULL;
|
|
|
|
|
size_t n_fms = 0;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2010-10-01 13:08:14 -07:00
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
|
error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms,
|
2013-11-15 14:19:57 -08:00
|
|
|
|
&usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
ovs_fatal(0, "%s", error);
|
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
|
ofctl_parse_flows__(fms, n_fms, usable_protocols);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
free(fms);
|
2010-10-01 13:08:14 -07:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2014-05-09 18:18:17 -07:00
|
|
|
|
ofctl_parse_nxm__(bool oxm, enum ofp_version version)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
|
|
|
|
struct ds in;
|
|
|
|
|
|
|
|
|
|
ds_init(&in);
|
2012-05-09 12:15:11 -07:00
|
|
|
|
while (!ds_get_test_line(&in, stdin)) {
|
2010-11-09 17:00:59 -08:00
|
|
|
|
struct ofpbuf nx_match;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match match;
|
2011-12-23 12:23:24 -08:00
|
|
|
|
ovs_be64 cookie, cookie_mask;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
int match_len;
|
|
|
|
|
|
|
|
|
|
/* Convert string to nx_match. */
|
|
|
|
|
ofpbuf_init(&nx_match, 0);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
if (oxm) {
|
|
|
|
|
match_len = oxm_match_from_string(ds_cstr(&in), &nx_match);
|
|
|
|
|
} else {
|
|
|
|
|
match_len = nx_match_from_string(ds_cstr(&in), &nx_match);
|
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Convert nx_match to match. */
|
2012-01-03 13:30:45 -08:00
|
|
|
|
if (strict) {
|
2012-08-01 16:01:45 +09:00
|
|
|
|
if (oxm) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
error = oxm_pull_match(&nx_match, &match);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
} else {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
error = nx_pull_match(&nx_match, match_len, &match,
|
2012-08-01 16:01:45 +09:00
|
|
|
|
&cookie, &cookie_mask);
|
|
|
|
|
}
|
2012-01-03 13:30:45 -08:00
|
|
|
|
} else {
|
2012-08-01 16:01:45 +09:00
|
|
|
|
if (oxm) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
error = oxm_pull_match_loose(&nx_match, &match);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
} else {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
error = nx_pull_match_loose(&nx_match, match_len, &match,
|
2012-08-01 16:01:45 +09:00
|
|
|
|
&cookie, &cookie_mask);
|
|
|
|
|
}
|
2012-01-03 13:30:45 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
if (!error) {
|
|
|
|
|
char *out;
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Convert match back to nx_match. */
|
2010-11-09 17:00:59 -08:00
|
|
|
|
ofpbuf_uninit(&nx_match);
|
|
|
|
|
ofpbuf_init(&nx_match, 0);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
if (oxm) {
|
2014-05-09 18:18:17 -07:00
|
|
|
|
match_len = oxm_put_match(&nx_match, &match, version);
|
2013-07-22 15:47:19 -07:00
|
|
|
|
out = oxm_match_to_string(&nx_match, match_len);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
} else {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_len = nx_put_match(&nx_match, &match,
|
2012-08-01 16:01:45 +09:00
|
|
|
|
cookie, cookie_mask);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
out = nx_match_to_string(ofpbuf_data(&nx_match), match_len);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
|
|
|
|
puts(out);
|
|
|
|
|
free(out);
|
|
|
|
|
} else {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
printf("nx_pull_match() returned error %s\n",
|
|
|
|
|
ofperr_get_name(error));
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_uninit(&nx_match);
|
|
|
|
|
}
|
|
|
|
|
ds_destroy(&in);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-11 09:56:12 -07:00
|
|
|
|
/* "parse-nxm": reads a series of NXM nx_match specifications as strings from
|
|
|
|
|
* stdin, does some internal fussing with them, and then prints them back as
|
|
|
|
|
* strings on stdout. */
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_parse_nxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2012-06-11 09:56:12 -07:00
|
|
|
|
{
|
2014-05-09 18:18:17 -07:00
|
|
|
|
return ofctl_parse_nxm__(false, 0);
|
2012-06-11 09:56:12 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-05-09 18:18:17 -07:00
|
|
|
|
/* "parse-oxm VERSION": reads a series of OXM nx_match specifications as
|
|
|
|
|
* strings from stdin, does some internal fussing with them, and then prints
|
|
|
|
|
* them back as strings on stdout. VERSION must specify an OpenFlow version,
|
|
|
|
|
* e.g. "OpenFlow12". */
|
2012-06-11 09:56:12 -07:00
|
|
|
|
static void
|
2014-05-09 18:18:17 -07:00
|
|
|
|
ofctl_parse_oxm(int argc OVS_UNUSED, char *argv[])
|
2012-06-11 09:56:12 -07:00
|
|
|
|
{
|
2014-05-09 18:18:17 -07:00
|
|
|
|
enum ofp_version version = ofputil_version_from_string(argv[1]);
|
|
|
|
|
if (version < OFP12_VERSION) {
|
|
|
|
|
ovs_fatal(0, "%s: not a valid version for OXM", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ofctl_parse_nxm__(true, version);
|
2012-06-11 09:56:12 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
static void
|
2012-07-17 09:39:23 -07:00
|
|
|
|
print_differences(const char *prefix,
|
|
|
|
|
const void *a_, size_t a_len,
|
2012-07-03 22:17:14 -07:00
|
|
|
|
const void *b_, size_t b_len)
|
|
|
|
|
{
|
|
|
|
|
const uint8_t *a = a_;
|
|
|
|
|
const uint8_t *b = b_;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MIN(a_len, b_len); i++) {
|
|
|
|
|
if (a[i] != b[i]) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
printf("%s%2"PRIuSIZE": %02"PRIx8" -> %02"PRIx8"\n",
|
2012-07-17 09:39:23 -07:00
|
|
|
|
prefix, i, a[i], b[i]);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (i = a_len; i < b_len; i++) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
printf("%s%2"PRIuSIZE": (none) -> %02"PRIx8"\n", prefix, i, b[i]);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
}
|
|
|
|
|
for (i = b_len; i < a_len; i++) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
printf("%s%2"PRIuSIZE": %02"PRIx8" -> (none)\n", prefix, i, a[i]);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-08-07 16:09:07 -07:00
|
|
|
|
ofctl_parse_ofp10_actions__(bool instructions)
|
2012-07-03 22:17:14 -07:00
|
|
|
|
{
|
|
|
|
|
struct ds in;
|
|
|
|
|
|
|
|
|
|
ds_init(&in);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
|
2012-07-03 22:17:14 -07:00
|
|
|
|
struct ofpbuf of10_out;
|
|
|
|
|
struct ofpbuf of10_in;
|
|
|
|
|
struct ofpbuf ofpacts;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
size_t size;
|
|
|
|
|
struct ds s;
|
|
|
|
|
|
|
|
|
|
/* Parse hex bytes. */
|
|
|
|
|
ofpbuf_init(&of10_in, 0);
|
|
|
|
|
if (ofpbuf_put_hex(&of10_in, ds_cstr(&in), NULL)[0] != '\0') {
|
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert to ofpacts. */
|
|
|
|
|
ofpbuf_init(&ofpacts, 0);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
size = ofpbuf_size(&of10_in);
|
2014-08-07 16:09:07 -07:00
|
|
|
|
error = (instructions
|
|
|
|
|
? ofpacts_pull_openflow_instructions
|
|
|
|
|
: ofpacts_pull_openflow_actions)(
|
|
|
|
|
&of10_in, ofpbuf_size(&of10_in), OFP10_VERSION, &ofpacts);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
if (error) {
|
2014-08-06 14:07:55 -07:00
|
|
|
|
printf("bad OF1.0 actions: %s\n\n", ofperr_get_name(error));
|
2012-07-03 22:17:14 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
ofpbuf_uninit(&of10_in);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_push_uninit(&of10_in, size);
|
|
|
|
|
|
|
|
|
|
/* Print cls_rule. */
|
|
|
|
|
ds_init(&s);
|
2013-10-14 14:08:20 -07:00
|
|
|
|
ds_put_cstr(&s, "actions=");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpacts_format(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &s);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
puts(ds_cstr(&s));
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
|
|
|
|
|
/* Convert back to ofp10 actions and print differences from input. */
|
|
|
|
|
ofpbuf_init(&of10_out, 0);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpacts_put_openflow_actions(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &of10_out,
|
2013-10-24 13:19:31 -07:00
|
|
|
|
OFP10_VERSION);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
print_differences("", ofpbuf_data(&of10_in), ofpbuf_size(&of10_in),
|
|
|
|
|
ofpbuf_data(&of10_out), ofpbuf_size(&of10_out));
|
2012-07-03 22:17:14 -07:00
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
ofpbuf_uninit(&of10_in);
|
|
|
|
|
ofpbuf_uninit(&of10_out);
|
|
|
|
|
}
|
|
|
|
|
ds_destroy(&in);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-07 16:09:07 -07:00
|
|
|
|
/* "parse-ofp10-actions": reads a series of OpenFlow 1.0 action specifications
|
|
|
|
|
* as hex bytes from stdin, converts them to ofpacts, prints them as strings
|
|
|
|
|
* on stdout, and then converts them back to hex bytes and prints any
|
|
|
|
|
* differences from the input. */
|
|
|
|
|
static void
|
|
|
|
|
ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
ofctl_parse_ofp10_actions__(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "parse-ofp10-instructions": reads a series of OpenFlow 1.0 action
|
|
|
|
|
* specifications as hex bytes from stdin, converts them to ofpacts, prints
|
|
|
|
|
* them as strings on stdout, and then converts them back to hex bytes and
|
|
|
|
|
* prints any differences from the input. */
|
|
|
|
|
static void
|
|
|
|
|
ofctl_parse_ofp10_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
ofctl_parse_ofp10_actions__(true);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-17 09:39:23 -07:00
|
|
|
|
/* "parse-ofp10-match": reads a series of ofp10_match specifications as hex
|
|
|
|
|
* bytes from stdin, converts them to cls_rules, prints them as strings on
|
|
|
|
|
* stdout, and then converts them back to hex bytes and prints any differences
|
2012-07-21 09:56:28 -07:00
|
|
|
|
* from the input.
|
|
|
|
|
*
|
|
|
|
|
* The input hex bytes may contain "x"s to represent "don't-cares", bytes whose
|
|
|
|
|
* values are ignored in the input and will be set to zero when OVS converts
|
|
|
|
|
* them back to hex bytes. ovs-ofctl actually sets "x"s to random bits when
|
|
|
|
|
* it does the conversion to hex, to ensure that in fact they are ignored. */
|
2012-07-17 09:39:23 -07:00
|
|
|
|
static void
|
|
|
|
|
ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
2012-07-21 09:56:28 -07:00
|
|
|
|
struct ds expout;
|
2012-07-17 09:39:23 -07:00
|
|
|
|
struct ds in;
|
|
|
|
|
|
|
|
|
|
ds_init(&in);
|
2012-07-21 09:56:28 -07:00
|
|
|
|
ds_init(&expout);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
|
2012-07-21 09:56:28 -07:00
|
|
|
|
struct ofpbuf match_in, match_expout;
|
2012-07-17 09:39:23 -07:00
|
|
|
|
struct ofp10_match match_out;
|
|
|
|
|
struct ofp10_match match_normal;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match match;
|
2012-07-21 09:56:28 -07:00
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
/* Parse hex bytes to use for expected output. */
|
|
|
|
|
ds_clear(&expout);
|
|
|
|
|
ds_put_cstr(&expout, ds_cstr(&in));
|
|
|
|
|
for (p = ds_cstr(&expout); *p; p++) {
|
|
|
|
|
if (*p == 'x') {
|
|
|
|
|
*p = '0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_init(&match_expout, 0);
|
|
|
|
|
if (ofpbuf_put_hex(&match_expout, ds_cstr(&expout), NULL)[0] != '\0') {
|
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
|
|
|
|
}
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(&match_expout) != sizeof(struct ofp10_match)) {
|
lib/ofpbuf: Compact
This patch shrinks the struct ofpbuf from 104 to 48 bytes on 64-bit
systems, or from 52 to 36 bytes on 32-bit systems (counting in the
'l7' removal from an earlier patch). This may help contribute to
cache efficiency, and will speed up initializing, copying and
manipulating ofpbufs. This is potentially important for the DPDK
datapath, but the rest of the code base may also see a little benefit.
Changes are:
- Remove 'l7' pointer (previous patch).
- Use offsets instead of layer pointers for l2_5, l3, and l4 using
'l2' as basis. Usually 'data' is the same as 'l2', but this is not
always the case (e.g., when parsing or constructing a packet), so it
can not be easily used as the offset basis. Also, packet parsing is
faster if we do not need to maintain the offsets each time we pull
data from the ofpbuf.
- Use uint32_t for 'allocated' and 'size', as 2^32 is enough even for
largest possible messages/packets.
- Use packed enum for 'source'.
- Rearrange to avoid unnecessary padding.
- Remove 'private_p', which was used only in two cases, both of which
had the invariant ('l2' == 'data'), so we can temporarily use 'l2'
as a private pointer.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2014-03-24 09:17:01 -07:00
|
|
|
|
ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE,
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_size(&match_expout), sizeof(struct ofp10_match));
|
2012-07-21 09:56:28 -07:00
|
|
|
|
}
|
2012-07-17 09:39:23 -07:00
|
|
|
|
|
2012-07-21 09:56:28 -07:00
|
|
|
|
/* Parse hex bytes for input. */
|
|
|
|
|
for (p = ds_cstr(&in); *p; p++) {
|
|
|
|
|
if (*p == 'x') {
|
|
|
|
|
*p = "0123456789abcdef"[random_uint32() & 0xf];
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-07-17 09:39:23 -07:00
|
|
|
|
ofpbuf_init(&match_in, 0);
|
|
|
|
|
if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
|
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
|
|
|
|
}
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(&match_in) != sizeof(struct ofp10_match)) {
|
lib/ofpbuf: Compact
This patch shrinks the struct ofpbuf from 104 to 48 bytes on 64-bit
systems, or from 52 to 36 bytes on 32-bit systems (counting in the
'l7' removal from an earlier patch). This may help contribute to
cache efficiency, and will speed up initializing, copying and
manipulating ofpbufs. This is potentially important for the DPDK
datapath, but the rest of the code base may also see a little benefit.
Changes are:
- Remove 'l7' pointer (previous patch).
- Use offsets instead of layer pointers for l2_5, l3, and l4 using
'l2' as basis. Usually 'data' is the same as 'l2', but this is not
always the case (e.g., when parsing or constructing a packet), so it
can not be easily used as the offset basis. Also, packet parsing is
faster if we do not need to maintain the offsets each time we pull
data from the ofpbuf.
- Use uint32_t for 'allocated' and 'size', as 2^32 is enough even for
largest possible messages/packets.
- Use packed enum for 'source'.
- Rearrange to avoid unnecessary padding.
- Remove 'private_p', which was used only in two cases, both of which
had the invariant ('l2' == 'data'), so we can temporarily use 'l2'
as a private pointer.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2014-03-24 09:17:01 -07:00
|
|
|
|
ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE,
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_size(&match_in), sizeof(struct ofp10_match));
|
2012-07-17 09:39:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert to cls_rule and print. */
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofputil_match_from_ofp10_match(ofpbuf_data(&match_in), &match);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_print(&match);
|
2012-07-17 09:39:23 -07:00
|
|
|
|
|
|
|
|
|
/* Convert back to ofp10_match and print differences from input. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ofputil_match_to_ofp10_match(&match, &match_out);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
print_differences("", ofpbuf_data(&match_expout), ofpbuf_size(&match_expout),
|
2012-07-17 09:39:23 -07:00
|
|
|
|
&match_out, sizeof match_out);
|
|
|
|
|
|
|
|
|
|
/* Normalize, then convert and compare again. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ofputil_normalize_match(&match);
|
|
|
|
|
ofputil_match_to_ofp10_match(&match, &match_normal);
|
2012-07-17 09:39:23 -07:00
|
|
|
|
print_differences("normal: ", &match_out, sizeof match_out,
|
|
|
|
|
&match_normal, sizeof match_normal);
|
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
|
|
ofpbuf_uninit(&match_in);
|
2012-07-21 09:56:28 -07:00
|
|
|
|
ofpbuf_uninit(&match_expout);
|
2012-07-17 09:39:23 -07:00
|
|
|
|
}
|
|
|
|
|
ds_destroy(&in);
|
2012-07-21 09:56:28 -07:00
|
|
|
|
ds_destroy(&expout);
|
2012-07-17 09:39:23 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-06-09 15:49:16 -07:00
|
|
|
|
/* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
|
2012-08-07 15:28:18 -07:00
|
|
|
|
* bytes from stdin, converts them to "struct match"es, prints them as strings
|
|
|
|
|
* on stdout, and then converts them back to hex bytes and prints any
|
|
|
|
|
* differences from the input. */
|
2012-06-09 15:49:16 -07:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2012-06-09 15:49:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct ds in;
|
|
|
|
|
|
|
|
|
|
ds_init(&in);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
|
2012-06-09 15:49:16 -07:00
|
|
|
|
struct ofpbuf match_in;
|
|
|
|
|
struct ofp11_match match_out;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match match;
|
2012-06-09 15:49:16 -07:00
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
|
|
|
|
/* Parse hex bytes. */
|
|
|
|
|
ofpbuf_init(&match_in, 0);
|
|
|
|
|
if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
|
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
|
|
|
|
}
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(&match_in) != sizeof(struct ofp11_match)) {
|
lib/ofpbuf: Compact
This patch shrinks the struct ofpbuf from 104 to 48 bytes on 64-bit
systems, or from 52 to 36 bytes on 32-bit systems (counting in the
'l7' removal from an earlier patch). This may help contribute to
cache efficiency, and will speed up initializing, copying and
manipulating ofpbufs. This is potentially important for the DPDK
datapath, but the rest of the code base may also see a little benefit.
Changes are:
- Remove 'l7' pointer (previous patch).
- Use offsets instead of layer pointers for l2_5, l3, and l4 using
'l2' as basis. Usually 'data' is the same as 'l2', but this is not
always the case (e.g., when parsing or constructing a packet), so it
can not be easily used as the offset basis. Also, packet parsing is
faster if we do not need to maintain the offsets each time we pull
data from the ofpbuf.
- Use uint32_t for 'allocated' and 'size', as 2^32 is enough even for
largest possible messages/packets.
- Use packed enum for 'source'.
- Rearrange to avoid unnecessary padding.
- Remove 'private_p', which was used only in two cases, both of which
had the invariant ('l2' == 'data'), so we can temporarily use 'l2'
as a private pointer.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2014-03-24 09:17:01 -07:00
|
|
|
|
ovs_fatal(0, "Input is %"PRIu32" bytes, expected %"PRIuSIZE,
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_size(&match_in), sizeof(struct ofp11_match));
|
2012-06-09 15:49:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Convert to match. */
|
2014-03-30 01:31:50 -07:00
|
|
|
|
error = ofputil_match_from_ofp11_match(ofpbuf_data(&match_in), &match);
|
2012-06-09 15:49:16 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
printf("bad ofp11_match: %s\n\n", ofperr_get_name(error));
|
|
|
|
|
ofpbuf_uninit(&match_in);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Print match. */
|
|
|
|
|
match_print(&match);
|
2012-06-09 15:49:16 -07:00
|
|
|
|
|
|
|
|
|
/* Convert back to ofp11_match and print differences from input. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ofputil_match_to_ofp11_match(&match, &match_out);
|
2012-06-09 15:49:16 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
print_differences("", ofpbuf_data(&match_in), ofpbuf_size(&match_in),
|
2012-07-03 22:14:29 -07:00
|
|
|
|
&match_out, sizeof match_out);
|
|
|
|
|
putchar('\n');
|
2012-06-09 15:49:16 -07:00
|
|
|
|
|
2012-07-03 22:14:29 -07:00
|
|
|
|
ofpbuf_uninit(&match_in);
|
|
|
|
|
}
|
|
|
|
|
ds_destroy(&in);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "parse-ofp11-actions": reads a series of OpenFlow 1.1 action specifications
|
|
|
|
|
* as hex bytes from stdin, converts them to ofpacts, prints them as strings
|
|
|
|
|
* on stdout, and then converts them back to hex bytes and prints any
|
|
|
|
|
* differences from the input. */
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2012-07-03 22:14:29 -07:00
|
|
|
|
{
|
|
|
|
|
struct ds in;
|
|
|
|
|
|
|
|
|
|
ds_init(&in);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
|
2012-07-03 22:14:29 -07:00
|
|
|
|
struct ofpbuf of11_out;
|
|
|
|
|
struct ofpbuf of11_in;
|
|
|
|
|
struct ofpbuf ofpacts;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
size_t size;
|
|
|
|
|
struct ds s;
|
|
|
|
|
|
|
|
|
|
/* Parse hex bytes. */
|
|
|
|
|
ofpbuf_init(&of11_in, 0);
|
|
|
|
|
if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
|
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
2012-06-09 15:49:16 -07:00
|
|
|
|
}
|
2012-07-03 22:14:29 -07:00
|
|
|
|
|
|
|
|
|
/* Convert to ofpacts. */
|
|
|
|
|
ofpbuf_init(&ofpacts, 0);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
size = ofpbuf_size(&of11_in);
|
|
|
|
|
error = ofpacts_pull_openflow_actions(&of11_in, ofpbuf_size(&of11_in),
|
2013-10-24 13:19:31 -07:00
|
|
|
|
OFP11_VERSION, &ofpacts);
|
2012-07-03 22:14:29 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
ofpbuf_uninit(&of11_in);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_push_uninit(&of11_in, size);
|
|
|
|
|
|
|
|
|
|
/* Print cls_rule. */
|
|
|
|
|
ds_init(&s);
|
2013-10-14 14:08:20 -07:00
|
|
|
|
ds_put_cstr(&s, "actions=");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpacts_format(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &s);
|
2012-07-03 22:14:29 -07:00
|
|
|
|
puts(ds_cstr(&s));
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
|
|
|
|
|
/* Convert back to ofp11 actions and print differences from input. */
|
|
|
|
|
ofpbuf_init(&of11_out, 0);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpacts_put_openflow_actions(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &of11_out,
|
2013-10-24 13:19:31 -07:00
|
|
|
|
OFP11_VERSION);
|
2012-07-03 22:14:29 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
print_differences("", ofpbuf_data(&of11_in), ofpbuf_size(&of11_in),
|
|
|
|
|
ofpbuf_data(&of11_out), ofpbuf_size(&of11_out));
|
2012-06-09 15:49:16 -07:00
|
|
|
|
putchar('\n');
|
|
|
|
|
|
2012-07-03 22:14:29 -07:00
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
ofpbuf_uninit(&of11_in);
|
|
|
|
|
ofpbuf_uninit(&of11_out);
|
|
|
|
|
}
|
|
|
|
|
ds_destroy(&in);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* "parse-ofp11-instructions": reads a series of OpenFlow 1.1 instruction
|
|
|
|
|
* specifications as hex bytes from stdin, converts them to ofpacts, prints
|
|
|
|
|
* them as strings on stdout, and then converts them back to hex bytes and
|
|
|
|
|
* prints any differences from the input. */
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2012-07-03 22:14:29 -07:00
|
|
|
|
{
|
|
|
|
|
struct ds in;
|
|
|
|
|
|
|
|
|
|
ds_init(&in);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
|
2012-07-03 22:14:29 -07:00
|
|
|
|
struct ofpbuf of11_out;
|
|
|
|
|
struct ofpbuf of11_in;
|
|
|
|
|
struct ofpbuf ofpacts;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
size_t size;
|
|
|
|
|
struct ds s;
|
2013-06-05 13:18:09 -07:00
|
|
|
|
const char *table_id;
|
|
|
|
|
char *instructions;
|
|
|
|
|
|
|
|
|
|
/* Parse table_id separated with the follow-up instructions by ",", if
|
|
|
|
|
* any. */
|
|
|
|
|
instructions = ds_cstr(&in);
|
|
|
|
|
table_id = NULL;
|
|
|
|
|
if (strstr(instructions, ",")) {
|
|
|
|
|
table_id = strsep(&instructions, ",");
|
|
|
|
|
}
|
2012-07-03 22:14:29 -07:00
|
|
|
|
|
|
|
|
|
/* Parse hex bytes. */
|
|
|
|
|
ofpbuf_init(&of11_in, 0);
|
2013-06-05 13:18:09 -07:00
|
|
|
|
if (ofpbuf_put_hex(&of11_in, instructions, NULL)[0] != '\0') {
|
2012-07-03 22:14:29 -07:00
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert to ofpacts. */
|
|
|
|
|
ofpbuf_init(&ofpacts, 0);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
size = ofpbuf_size(&of11_in);
|
|
|
|
|
error = ofpacts_pull_openflow_instructions(&of11_in, ofpbuf_size(&of11_in),
|
2013-10-24 13:19:31 -07:00
|
|
|
|
OFP11_VERSION, &ofpacts);
|
2013-06-28 19:44:03 +03:00
|
|
|
|
if (!error) {
|
2013-10-23 12:52:57 -07:00
|
|
|
|
/* Verify actions, enforce consistency. */
|
2013-06-28 19:44:03 +03:00
|
|
|
|
struct flow flow;
|
|
|
|
|
memset(&flow, 0, sizeof flow);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
error = ofpacts_check_consistency(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts),
|
2013-11-15 14:19:57 -08:00
|
|
|
|
&flow, OFPP_MAX,
|
|
|
|
|
table_id ? atoi(table_id) : 0,
|
|
|
|
|
255, OFPUTIL_P_OF11_STD);
|
2013-06-28 19:44:03 +03:00
|
|
|
|
}
|
2012-07-03 22:14:29 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
ofpbuf_uninit(&of11_in);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_push_uninit(&of11_in, size);
|
|
|
|
|
|
|
|
|
|
/* Print cls_rule. */
|
|
|
|
|
ds_init(&s);
|
2013-10-14 14:08:20 -07:00
|
|
|
|
ds_put_cstr(&s, "actions=");
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpacts_format(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &s);
|
2012-07-03 22:14:29 -07:00
|
|
|
|
puts(ds_cstr(&s));
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
|
|
|
|
|
/* Convert back to ofp11 instructions and print differences from
|
|
|
|
|
* input. */
|
|
|
|
|
ofpbuf_init(&of11_out, 0);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpacts_put_openflow_instructions(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts),
|
2013-10-24 13:19:31 -07:00
|
|
|
|
&of11_out, OFP13_VERSION);
|
2012-07-03 22:14:29 -07:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
print_differences("", ofpbuf_data(&of11_in), ofpbuf_size(&of11_in),
|
|
|
|
|
ofpbuf_data(&of11_out), ofpbuf_size(&of11_out));
|
2012-07-03 22:14:29 -07:00
|
|
|
|
putchar('\n');
|
|
|
|
|
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
ofpbuf_uninit(&of11_in);
|
|
|
|
|
ofpbuf_uninit(&of11_out);
|
2012-06-09 15:49:16 -07:00
|
|
|
|
}
|
|
|
|
|
ds_destroy(&in);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-23 10:28:14 -07:00
|
|
|
|
/* "parse-pcap PCAP": read packets from PCAP and print their flows. */
|
|
|
|
|
static void
|
|
|
|
|
ofctl_parse_pcap(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
FILE *pcap;
|
|
|
|
|
|
2014-01-23 17:24:03 +01:00
|
|
|
|
pcap = ovs_pcap_open(argv[1], "rb");
|
2013-09-23 10:28:14 -07:00
|
|
|
|
if (!pcap) {
|
|
|
|
|
ovs_fatal(errno, "%s: open failed", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
struct ofpbuf *packet;
|
|
|
|
|
struct flow flow;
|
2014-02-26 18:08:04 -08:00
|
|
|
|
const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
|
2013-09-23 10:28:14 -07:00
|
|
|
|
int error;
|
|
|
|
|
|
2014-01-23 17:24:03 +01:00
|
|
|
|
error = ovs_pcap_read(pcap, &packet, NULL);
|
2013-09-23 10:28:14 -07:00
|
|
|
|
if (error == EOF) {
|
|
|
|
|
break;
|
|
|
|
|
} else if (error) {
|
|
|
|
|
ovs_fatal(error, "%s: read failed", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-26 18:08:04 -08:00
|
|
|
|
flow_extract(packet, &md, &flow);
|
2013-09-23 10:28:14 -07:00
|
|
|
|
flow_print(stdout, &flow);
|
|
|
|
|
putchar('\n');
|
|
|
|
|
ofpbuf_delete(packet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 22:17:10 -07:00
|
|
|
|
/* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and
|
|
|
|
|
* mask values to and from various formats and prints the results. */
|
|
|
|
|
static void
|
|
|
|
|
ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match match;
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
|
|
|
|
char *string_s;
|
|
|
|
|
struct ofputil_flow_mod fm;
|
|
|
|
|
|
|
|
|
|
struct ofpbuf nxm;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match nxm_match;
|
2012-07-19 22:17:10 -07:00
|
|
|
|
int nxm_match_len;
|
|
|
|
|
char *nxm_s;
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct ofp10_match of10_raw;
|
|
|
|
|
struct match of10_match;
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct ofp11_match of11_raw;
|
|
|
|
|
struct match of11_match;
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
|
|
|
|
enum ofperr error;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error_s;
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
|
enum ofputil_protocol usable_protocols; /* Unused for now. */
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_init_catchall(&match);
|
|
|
|
|
match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
|
|
|
|
|
match.wc.masks.vlan_tci = htons(strtoul(argv[2], NULL, 16));
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
|
|
|
|
/* Convert to and from string. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
|
2012-07-19 22:17:10 -07:00
|
|
|
|
printf("%s -> ", string_s);
|
|
|
|
|
fflush(stdout);
|
2013-11-15 14:19:57 -08:00
|
|
|
|
error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
if (error_s) {
|
|
|
|
|
ovs_fatal(0, "%s", error_s);
|
|
|
|
|
}
|
2012-07-19 22:17:10 -07:00
|
|
|
|
printf("%04"PRIx16"/%04"PRIx16"\n",
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ntohs(fm.match.flow.vlan_tci),
|
|
|
|
|
ntohs(fm.match.wc.masks.vlan_tci));
|
2012-08-17 13:59:46 -07:00
|
|
|
|
free(string_s);
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
|
|
|
|
/* Convert to and from NXM. */
|
|
|
|
|
ofpbuf_init(&nxm, 0);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
nxm_match_len = nx_put_match(&nxm, &match, htonll(0), htonll(0));
|
2014-03-30 01:31:50 -07:00
|
|
|
|
nxm_s = nx_match_to_string(ofpbuf_data(&nxm), nxm_match_len);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL);
|
2012-07-19 22:17:10 -07:00
|
|
|
|
printf("NXM: %s -> ", nxm_s);
|
|
|
|
|
if (error) {
|
|
|
|
|
printf("%s\n", ofperr_to_string(error));
|
|
|
|
|
} else {
|
|
|
|
|
printf("%04"PRIx16"/%04"PRIx16"\n",
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ntohs(nxm_match.flow.vlan_tci),
|
|
|
|
|
ntohs(nxm_match.wc.masks.vlan_tci));
|
2012-07-19 22:17:10 -07:00
|
|
|
|
}
|
|
|
|
|
free(nxm_s);
|
|
|
|
|
ofpbuf_uninit(&nxm);
|
|
|
|
|
|
2012-07-23 11:36:46 +09:00
|
|
|
|
/* Convert to and from OXM. */
|
|
|
|
|
ofpbuf_init(&nxm, 0);
|
2014-05-09 18:18:17 -07:00
|
|
|
|
nxm_match_len = oxm_put_match(&nxm, &match, OFP12_VERSION);
|
2013-07-22 15:47:19 -07:00
|
|
|
|
nxm_s = oxm_match_to_string(&nxm, nxm_match_len);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
error = oxm_pull_match(&nxm, &nxm_match);
|
2012-07-23 11:36:46 +09:00
|
|
|
|
printf("OXM: %s -> ", nxm_s);
|
|
|
|
|
if (error) {
|
|
|
|
|
printf("%s\n", ofperr_to_string(error));
|
|
|
|
|
} else {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
uint16_t vid = ntohs(nxm_match.flow.vlan_tci) &
|
2012-07-23 11:36:46 +09:00
|
|
|
|
(VLAN_VID_MASK | VLAN_CFI);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
uint16_t mask = ntohs(nxm_match.wc.masks.vlan_tci) &
|
2012-07-23 11:36:46 +09:00
|
|
|
|
(VLAN_VID_MASK | VLAN_CFI);
|
|
|
|
|
|
|
|
|
|
printf("%04"PRIx16"/%04"PRIx16",", vid, mask);
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlan_tci)) {
|
|
|
|
|
printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_match.flow.vlan_tci));
|
2012-07-23 11:36:46 +09:00
|
|
|
|
} else {
|
|
|
|
|
printf("--\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(nxm_s);
|
|
|
|
|
ofpbuf_uninit(&nxm);
|
|
|
|
|
|
2012-07-19 22:17:10 -07:00
|
|
|
|
/* Convert to and from OpenFlow 1.0. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ofputil_match_to_ofp10_match(&match, &of10_raw);
|
|
|
|
|
ofputil_match_from_ofp10_match(&of10_raw, &of10_match);
|
2012-07-19 22:17:10 -07:00
|
|
|
|
printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ntohs(of10_raw.dl_vlan),
|
|
|
|
|
(of10_raw.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
|
|
|
|
|
of10_raw.dl_vlan_pcp,
|
|
|
|
|
(of10_raw.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
|
|
|
|
|
ntohs(of10_match.flow.vlan_tci),
|
|
|
|
|
ntohs(of10_match.wc.masks.vlan_tci));
|
2012-07-19 22:17:10 -07:00
|
|
|
|
|
|
|
|
|
/* Convert to and from OpenFlow 1.1. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ofputil_match_to_ofp11_match(&match, &of11_raw);
|
|
|
|
|
ofputil_match_from_ofp11_match(&of11_raw, &of11_match);
|
2012-07-19 22:17:10 -07:00
|
|
|
|
printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ntohs(of11_raw.dl_vlan),
|
|
|
|
|
(of11_raw.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
|
|
|
|
|
of11_raw.dl_vlan_pcp,
|
|
|
|
|
(of11_raw.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
|
|
|
|
|
ntohs(of11_match.flow.vlan_tci),
|
|
|
|
|
ntohs(of11_match.wc.masks.vlan_tci));
|
2012-07-19 22:17:10 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-03-26 13:46:35 -07:00
|
|
|
|
/* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
|
|
|
|
|
* version. */
|
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_print_error(int argc OVS_UNUSED, char *argv[])
|
2012-03-26 13:46:35 -07:00
|
|
|
|
{
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
int version;
|
|
|
|
|
|
|
|
|
|
error = ofperr_from_name(argv[1]);
|
|
|
|
|
if (!error) {
|
|
|
|
|
ovs_fatal(0, "unknown error \"%s\"", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (version = 0; version <= UINT8_MAX; version++) {
|
2012-08-01 16:01:49 +09:00
|
|
|
|
const char *name = ofperr_domain_get_name(version);
|
2013-06-24 13:49:40 -07:00
|
|
|
|
if (name) {
|
|
|
|
|
int vendor = ofperr_get_vendor(error, version);
|
|
|
|
|
int type = ofperr_get_type(error, version);
|
|
|
|
|
int code = ofperr_get_code(error, version);
|
|
|
|
|
|
|
|
|
|
if (vendor != -1 || type != -1 || code != -1) {
|
|
|
|
|
printf("%s: vendor %#x, type %d, code %d\n",
|
|
|
|
|
name, vendor, type, code);
|
|
|
|
|
}
|
2012-03-26 13:46:35 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-30 14:18:03 -08:00
|
|
|
|
/* "encode-error-reply ENUM REQUEST": Encodes an error reply to REQUEST for the
|
|
|
|
|
* error named ENUM and prints the error reply in hex. */
|
|
|
|
|
static void
|
|
|
|
|
ofctl_encode_error_reply(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
const struct ofp_header *oh;
|
|
|
|
|
struct ofpbuf request, *reply;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
|
|
|
|
error = ofperr_from_name(argv[1]);
|
|
|
|
|
if (!error) {
|
|
|
|
|
ovs_fatal(0, "unknown error \"%s\"", argv[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_init(&request, 0);
|
|
|
|
|
if (ofpbuf_put_hex(&request, argv[2], NULL)[0] != '\0') {
|
|
|
|
|
ovs_fatal(0, "Trailing garbage in hex data");
|
|
|
|
|
}
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(&request) < sizeof(struct ofp_header)) {
|
2012-11-30 14:18:03 -08:00
|
|
|
|
ovs_fatal(0, "Request too short");
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
oh = ofpbuf_data(&request);
|
|
|
|
|
if (ofpbuf_size(&request) != ntohs(oh->length)) {
|
2012-11-30 14:18:03 -08:00
|
|
|
|
ovs_fatal(0, "Request size inconsistent");
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
reply = ofperr_encode_reply(error, ofpbuf_data(&request));
|
2012-11-30 14:18:03 -08:00
|
|
|
|
ofpbuf_uninit(&request);
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ovs_hex_dump(stdout, ofpbuf_data(reply), ofpbuf_size(reply), 0, false);
|
2012-11-30 14:18:03 -08:00
|
|
|
|
ofpbuf_delete(reply);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-12 13:57:53 -08:00
|
|
|
|
/* "ofp-print HEXSTRING [VERBOSITY]": Converts the hex digits in HEXSTRING into
|
|
|
|
|
* binary data, interpreting them as an OpenFlow message, and prints the
|
2014-06-18 11:12:36 -07:00
|
|
|
|
* OpenFlow message on stdout, at VERBOSITY (level 2 by default).
|
|
|
|
|
*
|
|
|
|
|
* Alternative usage: "ofp-print [VERBOSITY] - < HEXSTRING_FILE", where
|
|
|
|
|
* HEXSTRING_FILE contains the HEXSTRING. */
|
2011-01-12 13:57:53 -08:00
|
|
|
|
static void
|
2012-07-08 04:38:36 -07:00
|
|
|
|
ofctl_ofp_print(int argc, char *argv[])
|
2011-01-12 13:57:53 -08:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf packet;
|
2014-06-18 11:12:36 -07:00
|
|
|
|
char *buffer;
|
|
|
|
|
int verbosity = 2;
|
|
|
|
|
struct ds line;
|
|
|
|
|
|
|
|
|
|
ds_init(&line);
|
|
|
|
|
|
|
|
|
|
if (!strcmp(argv[argc-1], "-")) {
|
|
|
|
|
if (ds_get_line(&line, stdin)) {
|
|
|
|
|
VLOG_FATAL("Failed to read stdin");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer = line.string;
|
|
|
|
|
verbosity = argc > 2 ? atoi(argv[1]) : verbosity;
|
|
|
|
|
} else if (argc > 2) {
|
|
|
|
|
buffer = argv[1];
|
|
|
|
|
verbosity = atoi(argv[2]);
|
|
|
|
|
} else {
|
|
|
|
|
buffer = argv[1];
|
|
|
|
|
}
|
2011-01-12 13:57:53 -08:00
|
|
|
|
|
2014-06-18 11:12:36 -07:00
|
|
|
|
ofpbuf_init(&packet, strlen(buffer) / 2);
|
|
|
|
|
if (ofpbuf_put_hex(&packet, buffer, NULL)[0] != '\0') {
|
2011-01-12 13:57:53 -08:00
|
|
|
|
ovs_fatal(0, "trailing garbage following hex bytes");
|
|
|
|
|
}
|
2014-06-18 11:12:36 -07:00
|
|
|
|
ofp_print(stdout, ofpbuf_data(&packet), ofpbuf_size(&packet), verbosity);
|
2011-01-12 13:57:53 -08:00
|
|
|
|
ofpbuf_uninit(&packet);
|
2014-06-18 11:12:36 -07:00
|
|
|
|
ds_destroy(&line);
|
2011-01-12 13:57:53 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-09 10:00:22 -08:00
|
|
|
|
/* "encode-hello BITMAP...": Encodes each BITMAP as an OpenFlow hello message
|
|
|
|
|
* and dumps each message in hex. */
|
|
|
|
|
static void
|
|
|
|
|
ofctl_encode_hello(int argc OVS_UNUSED, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
uint32_t bitmap = strtol(argv[1], NULL, 0);
|
|
|
|
|
struct ofpbuf *hello;
|
|
|
|
|
|
|
|
|
|
hello = ofputil_encode_hello(bitmap);
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ovs_hex_dump(stdout, ofpbuf_data(hello), ofpbuf_size(hello), 0, false);
|
|
|
|
|
ofp_print(stdout, ofpbuf_data(hello), ofpbuf_size(hello), verbosity);
|
2012-11-09 10:00:22 -08:00
|
|
|
|
ofpbuf_delete(hello);
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-23 11:49:39 -07:00
|
|
|
|
static const struct command all_commands[] = {
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "show", 1, 1, ofctl_show },
|
|
|
|
|
{ "monitor", 1, 3, ofctl_monitor },
|
|
|
|
|
{ "snoop", 1, 1, ofctl_snoop },
|
|
|
|
|
{ "dump-desc", 1, 1, ofctl_dump_desc },
|
|
|
|
|
{ "dump-tables", 1, 1, ofctl_dump_tables },
|
2014-03-23 23:20:04 -07:00
|
|
|
|
{ "dump-table-features", 1, 1, ofctl_dump_table_features },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "dump-flows", 1, 2, ofctl_dump_flows },
|
|
|
|
|
{ "dump-aggregate", 1, 2, ofctl_dump_aggregate },
|
|
|
|
|
{ "queue-stats", 1, 3, ofctl_queue_stats },
|
2013-10-24 15:54:03 -07:00
|
|
|
|
{ "queue-get-config", 2, 2, ofctl_queue_get_config },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "add-flow", 2, 2, ofctl_add_flow },
|
|
|
|
|
{ "add-flows", 2, 2, ofctl_add_flows },
|
|
|
|
|
{ "mod-flows", 2, 2, ofctl_mod_flows },
|
|
|
|
|
{ "del-flows", 1, 2, ofctl_del_flows },
|
|
|
|
|
{ "replace-flows", 2, 2, ofctl_replace_flows },
|
|
|
|
|
{ "diff-flows", 2, 2, ofctl_diff_flows },
|
2013-09-13 15:03:33 -07:00
|
|
|
|
{ "add-meter", 2, 2, ofctl_add_meter },
|
|
|
|
|
{ "mod-meter", 2, 2, ofctl_mod_meter },
|
|
|
|
|
{ "del-meter", 2, 2, ofctl_del_meters },
|
|
|
|
|
{ "del-meters", 1, 1, ofctl_del_meters },
|
|
|
|
|
{ "dump-meter", 2, 2, ofctl_dump_meters },
|
|
|
|
|
{ "dump-meters", 1, 1, ofctl_dump_meters },
|
|
|
|
|
{ "meter-stats", 1, 2, ofctl_meter_stats },
|
|
|
|
|
{ "meter-features", 1, 1, ofctl_meter_features },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "packet-out", 4, INT_MAX, ofctl_packet_out },
|
|
|
|
|
{ "dump-ports", 1, 2, ofctl_dump_ports },
|
2014-05-07 23:18:46 -07:00
|
|
|
|
{ "dump-ports-desc", 1, 2, ofctl_dump_ports_desc },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "mod-port", 3, 3, ofctl_mod_port },
|
2013-09-07 03:02:32 -07:00
|
|
|
|
{ "mod-table", 3, 3, ofctl_mod_table },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "get-frags", 1, 1, ofctl_get_frags },
|
|
|
|
|
{ "set-frags", 2, 2, ofctl_set_frags },
|
|
|
|
|
{ "probe", 1, 1, ofctl_probe },
|
|
|
|
|
{ "ping", 1, 2, ofctl_ping },
|
|
|
|
|
{ "benchmark", 3, 3, ofctl_benchmark },
|
2013-09-01 18:30:17 -07:00
|
|
|
|
|
2013-11-22 13:17:23 -08:00
|
|
|
|
{ "ofp-parse", 1, 1, ofctl_ofp_parse },
|
|
|
|
|
{ "ofp-parse-pcap", 1, INT_MAX, ofctl_ofp_parse_pcap },
|
|
|
|
|
|
2013-09-01 18:30:17 -07:00
|
|
|
|
{ "add-group", 1, 2, ofctl_add_group },
|
|
|
|
|
{ "add-groups", 1, 2, ofctl_add_groups },
|
|
|
|
|
{ "mod-group", 1, 2, ofctl_mod_group },
|
|
|
|
|
{ "del-groups", 1, 2, ofctl_del_groups },
|
2014-05-07 23:49:00 -07:00
|
|
|
|
{ "dump-groups", 1, 2, ofctl_dump_group_desc },
|
2013-09-01 18:30:17 -07:00
|
|
|
|
{ "dump-group-stats", 1, 2, ofctl_dump_group_stats },
|
|
|
|
|
{ "dump-group-features", 1, 1, ofctl_dump_group_features },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "help", 0, INT_MAX, ofctl_help },
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
|
|
|
|
/* Undocumented commands for testing. */
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "parse-flow", 1, 1, ofctl_parse_flow },
|
|
|
|
|
{ "parse-flows", 1, 1, ofctl_parse_flows },
|
|
|
|
|
{ "parse-nx-match", 0, 0, ofctl_parse_nxm },
|
|
|
|
|
{ "parse-nxm", 0, 0, ofctl_parse_nxm },
|
2014-05-09 18:18:17 -07:00
|
|
|
|
{ "parse-oxm", 1, 1, ofctl_parse_oxm },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "parse-ofp10-actions", 0, 0, ofctl_parse_ofp10_actions },
|
2014-08-07 16:09:07 -07:00
|
|
|
|
{ "parse-ofp10-instructions", 0, 0, ofctl_parse_ofp10_instructions },
|
2012-07-17 09:39:23 -07:00
|
|
|
|
{ "parse-ofp10-match", 0, 0, ofctl_parse_ofp10_match },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match },
|
|
|
|
|
{ "parse-ofp11-actions", 0, 0, ofctl_parse_ofp11_actions },
|
|
|
|
|
{ "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },
|
2013-09-23 10:28:14 -07:00
|
|
|
|
{ "parse-pcap", 1, 1, ofctl_parse_pcap },
|
2012-07-19 22:17:10 -07:00
|
|
|
|
{ "check-vlan", 2, 2, ofctl_check_vlan },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "print-error", 1, 1, ofctl_print_error },
|
2012-11-30 14:18:03 -08:00
|
|
|
|
{ "encode-error-reply", 2, 2, ofctl_encode_error_reply },
|
2012-07-08 04:38:36 -07:00
|
|
|
|
{ "ofp-print", 1, 2, ofctl_ofp_print },
|
2012-11-09 10:00:22 -08:00
|
|
|
|
{ "encode-hello", 1, 1, ofctl_encode_hello },
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{ NULL, 0, 0, NULL },
|
|
|
|
|
};
|
2013-07-19 10:04:47 -07:00
|
|
|
|
|
|
|
|
|
static const struct command *get_all_commands(void)
|
|
|
|
|
{
|
|
|
|
|
return all_commands;
|
|
|
|
|
}
|