_debian/utilities/ovs-dpctl usr/sbin
_debian/utilities/ovs-kill usr/sbin
_debian/utilities/ovs-vsctl usr/sbin
+_debian/utilities/ovs-pcap usr/bin
+_debian/utilities/ovs-tcpundump usr/bin
_debian/vswitchd/ovs-vswitchd usr/sbin
-# Copyright (C) 2009 Nicira Networks, Inc.
+# Copyright (C) 2009, 2010 Nicira Networks, Inc.
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
ofproto/pinsched.h \
ofproto/status.c \
ofproto/status.h
+
+EXTRA_DIST += ofproto/ofproto-unixctl.man
--- /dev/null
+.SS "OFPROTO COMMANDS"
+These commands manage the core OpenFlow switch implementation (called
+\fBofproto\fR).
+.IP "\fBofproto/list\fR"
+Lists the names of the running ofproto instances. These are the names
+that may be used on \fBofproto/trace\fR.
+.IP "\fBofproto/trace \fItun_id in_port packet\fR"
+Traces the path of an imaginary packet through ofproto. The arguments
+are:
+.RS
+.IP "\fItun_id\fR"
+The tunnel ID on which the packet arrived. Use
+\fB0\fR if the packet did not arrive through a tunnel.
+.IP "\fIin_port\fR"
+The OpenFlow port on which the packet arrived. Use \fB65534\fR if the
+packet arrived on \fBOFPP_LOCAL\fR, the local port.
+.IP "\fIpacket\fR"
+A sequence of hex digits specifying the packet's contents. An
+Ethernet frame is at least 14 bytes long, so there must be at least 28
+hex digits. Obviously, it is inconvenient to type in the hex digits
+by hand, so the \fBovs\-pcap\fR(1) and \fBovs\-tcpundump\fR(1)
+utilities provide easier ways.
+.RS
+\fB\*(PN\fR will respond with extensive information on how the packet
+would be handled if it were to be received. The packet will not
+actually be sent.
* revalidating without a packet to refer to. */
const struct ofpbuf *packet;
+ /* If nonnull, called just before executing a resubmit action.
+ *
+ * This is normally null so the client has to set it manually after
+ * calling action_xlate_ctx_init(). */
+ void (*resubmit_hook)(struct action_xlate_ctx *, const struct rule *);
+
/* xlate_actions() initializes and uses these members. The client might want
* to look at them after it returns. */
struct mac_learning *ml;
};
+/* Map from dpif name to struct ofproto, for use by unixctl commands. */
+static struct shash all_ofprotos = SHASH_INITIALIZER(&all_ofprotos);
+
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
static const struct ofhooks default_ofhooks;
static int init_ports(struct ofproto *);
static void reinit_ports(struct ofproto *);
+static void ofproto_unixctl_init(void);
+
int
ofproto_create(const char *datapath, const char *datapath_type,
const struct ofhooks *ofhooks, void *aux,
*ofprotop = NULL;
+ ofproto_unixctl_init();
+
/* Connect to datapath and start listening for messages. */
error = dpif_open(datapath, datapath_type, &dpif);
if (error) {
p->datapath_id = pick_datapath_id(p);
VLOG_INFO("using datapath ID %016"PRIx64, p->datapath_id);
+ shash_add_once(&all_ofprotos, dpif_name(p->dpif), p);
+
*ofprotop = p;
return 0;
}
return;
}
+ shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif));
+
/* Destroy fail-open and in-band early, since they touch the classifier. */
fail_open_destroy(p->fail_open);
p->fail_open = NULL;
rule = rule_lookup(ctx->ofproto, &ctx->flow);
ctx->flow.in_port = old_in_port;
+ if (ctx->resubmit_hook) {
+ ctx->resubmit_hook(ctx, rule);
+ }
+
if (rule) {
ctx->recurse++;
do_xlate_actions(rule->actions, rule->n_actions, ctx);
ctx->ofproto = ofproto;
ctx->flow = *flow;
ctx->packet = packet;
+ ctx->resubmit_hook = NULL;
}
static int
return eth_addr_to_uint64(ea);
}
\f
+static void
+ofproto_unixctl_list(struct unixctl_conn *conn, const char *arg OVS_UNUSED,
+ void *aux OVS_UNUSED)
+{
+ const struct shash_node *node;
+ struct ds results;
+
+ ds_init(&results);
+ SHASH_FOR_EACH (node, &all_ofprotos) {
+ ds_put_format(&results, "%s\n", node->name);
+ }
+ unixctl_command_reply(conn, 200, ds_cstr(&results));
+ ds_destroy(&results);
+}
+
+struct ofproto_trace {
+ struct action_xlate_ctx ctx;
+ struct flow flow;
+ struct ds *result;
+};
+
+static void
+trace_format_rule(struct ds *result, int level, const struct rule *rule)
+{
+ ds_put_char_multiple(result, '\t', level);
+ if (!rule) {
+ ds_put_cstr(result, "No match\n");
+ return;
+ }
+
+ ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
+ ntohll(rule->flow_cookie));
+ cls_rule_format(&rule->cr, result);
+ ds_put_char(result, '\n');
+
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_cstr(result, "OpenFlow ");
+ ofp_print_actions(result, (const struct ofp_action_header *) rule->actions,
+ rule->n_actions * sizeof *rule->actions);
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_format_flow(struct ds *result, int level, const char *title,
+ struct ofproto_trace *trace)
+{
+ ds_put_char_multiple(result, '\t', level);
+ ds_put_format(result, "%s: ", title);
+ if (flow_equal(&trace->ctx.flow, &trace->flow)) {
+ ds_put_cstr(result, "unchanged");
+ } else {
+ flow_format(result, &trace->ctx.flow);
+ trace->flow = trace->ctx.flow;
+ }
+ ds_put_char(result, '\n');
+}
+
+static void
+trace_resubmit(struct action_xlate_ctx *ctx, const struct rule *rule)
+{
+ struct ofproto_trace *trace = CONTAINER_OF(ctx, struct ofproto_trace, ctx);
+ struct ds *result = trace->result;
+
+ ds_put_char(result, '\n');
+ trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
+ trace_format_rule(result, ctx->recurse + 1, rule);
+}
+
+static void
+ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
+ void *aux OVS_UNUSED)
+{
+ char *dpname, *in_port_s, *tun_id_s, *packet_s;
+ char *args = xstrdup(args_);
+ char *save_ptr = NULL;
+ struct ofproto *ofproto;
+ struct ofpbuf packet;
+ struct rule *rule;
+ struct ds result;
+ struct flow flow;
+ uint16_t in_port;
+ ovs_be32 tun_id;
+ char *s;
+
+ ofpbuf_init(&packet, strlen(args) / 2);
+ ds_init(&result);
+
+ dpname = strtok_r(args, " ", &save_ptr);
+ tun_id_s = strtok_r(NULL, " ", &save_ptr);
+ in_port_s = strtok_r(NULL, " ", &save_ptr);
+ packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */
+ if (!dpname || !in_port_s || !packet_s) {
+ unixctl_command_reply(conn, 501, "Bad command syntax");
+ goto exit;
+ }
+
+ ofproto = shash_find_data(&all_ofprotos, dpname);
+ if (!ofproto) {
+ unixctl_command_reply(conn, 501, "Unknown ofproto (use ofproto/list "
+ "for help)");
+ goto exit;
+ }
+
+ tun_id = ntohl(strtoul(tun_id_s, NULL, 10));
+ in_port = ofp_port_to_odp_port(atoi(in_port_s));
+
+ packet_s = ofpbuf_put_hex(&packet, packet_s, NULL);
+ packet_s += strspn(packet_s, " ");
+ if (*packet_s != '\0') {
+ unixctl_command_reply(conn, 501, "Trailing garbage in command");
+ goto exit;
+ }
+ if (packet.size < ETH_HEADER_LEN) {
+ unixctl_command_reply(conn, 501, "Packet data too short for Ethernet");
+ goto exit;
+ }
+
+ ds_put_cstr(&result, "Packet: ");
+ s = ofp_packet_to_string(packet.data, packet.size, packet.size);
+ ds_put_cstr(&result, s);
+ free(s);
+
+ flow_extract(&packet, tun_id, in_port, &flow);
+ ds_put_cstr(&result, "Flow: ");
+ flow_format(&result, &flow);
+ ds_put_char(&result, '\n');
+
+ rule = rule_lookup(ofproto, &flow);
+ trace_format_rule(&result, 0, rule);
+ if (rule) {
+ struct ofproto_trace trace;
+
+ trace.result = &result;
+ trace.flow = flow;
+ action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet);
+ trace.ctx.resubmit_hook = trace_resubmit;
+ xlate_actions(&trace.ctx, rule->actions, rule->n_actions);
+
+ ds_put_char(&result, '\n');
+ trace_format_flow(&result, 0, "Final flow", &trace);
+ ds_put_cstr(&result, "Datapath actions: ");
+ format_odp_actions(&result,
+ trace.ctx.out.actions, trace.ctx.out.n_actions);
+ }
+
+ unixctl_command_reply(conn, 200, ds_cstr(&result));
+
+exit:
+ ds_destroy(&result);
+ ofpbuf_uninit(&packet);
+ free(args);
+}
+
+static void
+ofproto_unixctl_init(void)
+{
+ static bool registered;
+ if (registered) {
+ return;
+ }
+ registered = true;
+
+ unixctl_command_register("ofproto/list", ofproto_unixctl_list, NULL);
+ unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL);
+}
+\f
static bool
default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet,
struct odp_actions *actions, tag_type *tags,
utilities/ovs-openflowd \
utilities/ovs-vsctl
bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl
+if HAVE_PYTHON
+bin_SCRIPTS += utilities/ovs-pcap utilities/ovs-tcpundump
+endif
noinst_SCRIPTS += utilities/ovs-pki-cgi utilities/ovs-parse-leaks
EXTRA_DIST += \
utilities/ovs-openflowd.8.in \
utilities/ovs-parse-leaks.8 \
utilities/ovs-parse-leaks.in \
+ utilities/ovs-pcap.1.in \
+ utilities/ovs-pcap.in \
utilities/ovs-pki-cgi.in \
utilities/ovs-pki.8.in \
utilities/ovs-pki.in \
+ utilities/ovs-tcpundump.1.in \
+ utilities/ovs-tcpundump.in \
utilities/ovs-vsctl.8.in
DISTCLEANFILES += \
utilities/ovs-appctl.8 \
utilities/ovs-ofctl.8 \
utilities/ovs-openflowd.8 \
utilities/ovs-parse-leaks \
+ utilities/ovs-pcap \
+ utilities/ovs-pcap.1 \
utilities/ovs-pki \
utilities/ovs-pki-cgi \
utilities/ovs-pki.8 \
+ utilities/ovs-tcpundump \
+ utilities/ovs-tcpundump.1 \
utilities/ovs-vsctl.8
man_MANS += \
utilities/ovs-ofctl.8 \
utilities/ovs-openflowd.8 \
utilities/ovs-parse-leaks.8 \
+ utilities/ovs-pcap.1 \
utilities/ovs-pki.8 \
+ utilities/ovs-tcpundump.1 \
utilities/ovs-vsctl.8
utilities_ovs_appctl_SOURCES = utilities/ovs-appctl.c
These commands are specific to \fBovs\-openflowd\fR.
.IP "\fBexit\fR"
Causes \fBovs\-openflowd\fR to gracefully terminate.
+.so ofproto/ofproto-unixctl.man
.so lib/vlog-unixctl.man
.
.SH "SEE ALSO"
--- /dev/null
+.TH ovs\-pcap 1 "December 2010" "Open vSwitch" "Open vSwitch Manual"
+.
+.SH NAME
+ovs\-pcap \- print packets from a pcap file as hex
+.
+.SH SYNOPSIS
+\fBovs\-pcap\fR \fIfile\fR
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovs\-pcap\fR program reads the pcap \fIfile\fR named on the
+command line and prints each packet's contents as a sequence of hex
+digits on a line of its own. This format is suitable for use with the
+\fBofproto/trace\fR command supported by \fBovs\-vswitchd\fR(8) and
+\fBovs-openflowd\fR(8).
+.
+.SH "OPTIONS"
+.so lib/common.man
+.
+.SH "SEE ALSO"
+.
+.BR ovs\-vswitchd (8),
+.BR ovs\-openflowd (8),
+.BR ovs\-tcpundump (1),
+.BR tcpdump (8),
+.BR wireshark (8).
--- /dev/null
+#! @PYTHON@
+#
+# Copyright (c) 2010 Nicira Networks.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import binascii
+import getopt
+import struct
+import sys
+
+class PcapException(Exception):
+ pass
+
+class PcapReader(object):
+ def __init__(self, file_name):
+ self.file = open(file_name, "rb")
+ header = self.file.read(24)
+ if len(header) != 24:
+ raise PcapException("end of file reading pcap header")
+ magic, version, thiszone, sigfigs, snaplen, network = \
+ struct.unpack(">6I", header)
+ if magic == 0xa1b2c3d4:
+ self.header_format = ">4I"
+ elif magic == 0xd4c3b2a1:
+ self.header_format = "<4I"
+ else:
+ raise PcapException("bad magic %u reading pcap file "
+ "(expected 0xa1b2c3d4 or 0xd4c3b2a1)" % magic)
+
+ def read(self):
+ header = self.file.read(16)
+ if len(header) == 0:
+ return None
+ elif len(header) != 16:
+ raise PcapException("end of file within pcap record header")
+
+ ts_sec, ts_usec, incl_len, orig_len = struct.unpack(self.header_format,
+ header)
+ packet = self.file.read(incl_len)
+ if len(packet) != incl_len:
+ raise PcapException("end of file reading pcap packet data")
+ return packet
+argv0 = sys.argv[0]
+
+def usage():
+ print """\
+%(argv0)s: print pcap file packet data as hex
+usage: %(argv0)s FILE
+where FILE is a PCAP file.
+
+The following options are also available:
+ -h, --help display this help message
+ -V, --version display version information\
+""" % {'argv0': argv0}
+ sys.exit(0)
+
+if __name__ == "__main__":
+ try:
+ try:
+ options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
+ ['help', 'version'])
+ except getopt.GetoptException, geo:
+ sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
+ sys.exit(1)
+
+ for key, value in options:
+ if key in ['-h', '--help']:
+ usage()
+ elif key in ['-V', '--version']:
+ print "ovs-pcap (Open vSwitch) @VERSION@"
+ else:
+ sys.exit(0)
+
+ if len(args) != 1:
+ sys.stderr.write("%s: exactly 1 non-option argument required "
+ "(use --help for help)\n" % argv0)
+ sys.exit(1)
+
+ reader = PcapReader(args[0])
+ while True:
+ packet = reader.read()
+ if packet is None:
+ break
+
+ print binascii.hexlify(packet)
+
+ except PcapException, e:
+ sys.stderr.write("%s: %s\n" % (argv0, e))
+ sys.exit(1)
+
+# Local variables:
+# mode: python
+# End:
--- /dev/null
+.TH ovs\-tcpundump 1 "December 2010" "Open vSwitch" "Open vSwitch Manual"
+.
+.SH NAME
+ovs\-tcpundump \- convert ``tcpdump \-xx'' output to hex strings
+.
+.SH SYNOPSIS
+\fBovs\-tcpundump < \fIfile\fR
+.so lib/common-syn.man
+.
+.SH DESCRIPTION
+The \fBovs\-tcpundump\fR program reads \fBtcpdump \-xx\fR output on
+stdin, looking for hexadecimal packet data, and dumps each Ethernet as
+a single hexadecimal string on stdout. This format is suitable for
+use with the \fBofproto/trace\fR command supported by
+\fBovs\-vswitchd\fR(8) and \fBovs-openflowd\fR(8).
+.PP
+At least two \fB\-x\fR or \fB\-X\fR options must be given, otherwise
+the output will omit the Ethernet header, which prevents the output
+from being using with \fBofproto/trace\fR.
+.
+.SH "OPTIONS"
+.so lib/common.man
+.
+.SH "SEE ALSO"
+.
+.BR ovs\-vswitchd (8),
+.BR ovs\-openflowd (8),
+.BR ovs\-pcap (1),
+.BR tcpdump (8),
+.BR wireshark (8).
--- /dev/null
+#! @PYTHON@
+#
+# Copyright (c) 2010 Nicira Networks.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import getopt
+import re
+import sys
+
+argv0 = sys.argv[0]
+
+def usage():
+ print """\
+%(argv0)s: print "tcpdump -xx" output as hex
+usage: %(argv0)s < FILE
+where FILE is output from "tcpdump -xx".
+
+The following options are also available:
+ -h, --help display this help message
+ -V, --version display version information\
+""" % {'argv0': argv0}
+ sys.exit(0)
+
+if __name__ == "__main__":
+ try:
+ options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
+ ['help', 'version'])
+ except getopt.GetoptPcapException, geo:
+ sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
+ sys.exit(1)
+
+ for key, value in options:
+ if key in ['-h', '--help']:
+ usage()
+ elif key in ['-V', '--version']:
+ print "ovs-pcap (Open vSwitch) @VERSION@"
+ else:
+ sys.exit(0)
+
+ if len(args) != 0:
+ sys.stderr.write("%s: non-option argument not supported "
+ "(use --help for help)\n" % argv0)
+ sys.exit(1)
+
+ packet = ''
+ regex = re.compile(r'^\s+0x([0-9a-fA-F]+): ((?: [0-9a-fA-F]{4})+)')
+ while True:
+ line = sys.stdin.readline()
+ if line == "":
+ break
+
+ m = regex.match(line)
+ if m is None or int(m.group(1)) == 0:
+ if packet != '':
+ print packet
+ packet = ''
+ if m:
+ packet += re.sub(r'\s', '', m.group(2), 0)
+ if packet != '':
+ print packet
+
+# Local variables:
+# mode: python
+# End:
Returns the hash value which would be used for \fImac\fR with \fIvlan\fR
if specified.
.
+.so ofproto/ofproto-unixctl.man
.so lib/vlog-unixctl.man
.so lib/stress-unixctl.man
.SH "SEE ALSO"