summaryrefslogtreecommitdiff
path: root/ovn
diff options
context:
space:
mode:
authorJustin Pettit <jpettit@ovn.org>2016-12-23 16:34:48 -0800
committerJustin Pettit <jpettit@ovn.org>2017-01-05 13:51:17 -0800
commit714651c7db6a5cd8a22e2d0edb2d554718204bf3 (patch)
tree5296abd94bdcf0936c306557d3ffa5df4c3e2bc6 /ovn
parentdc5a1213a226766a66274cef24dd0a0719498eb0 (diff)
downloadopenvswitch-714651c7db6a5cd8a22e2d0edb2d554718204bf3.tar.gz
ovn-controller: Introduce "inject-pkt" ovs-appctl command.
Add the ability to inject a packet into the connected Open vSwitch instance. This is primarily useful for testing when a test requires side-effects from an actual packet, so ovn-trace won't do. Signed-off-by: Justin Pettit <jpettit@ovn.org> Acked-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'ovn')
-rw-r--r--ovn/controller/ofctrl.c99
-rw-r--r--ovn/controller/ofctrl.h6
-rw-r--r--ovn/controller/ovn-controller.8.xml20
-rw-r--r--ovn/controller/ovn-controller.c62
-rw-r--r--ovn/controller/pinctrl.h2
5 files changed, 181 insertions, 8 deletions
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index c79660bf4..ceff1389b 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -17,6 +17,7 @@
#include "bitmap.h"
#include "byte-order.h"
#include "dirs.h"
+#include "dp-packet.h"
#include "flow.h"
#include "hash.h"
#include "lflow.h"
@@ -70,6 +71,9 @@ static void ovn_flow_destroy(struct ovn_flow *);
/* OpenFlow connection to the switch. */
static struct rconn *swconn;
+/* Symbol table for OVN expressions. */
+static struct shash symtab;
+
/* Last seen sequence number for 'swconn'. When this differs from
* rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
static unsigned int seqno;
@@ -152,6 +156,7 @@ ofctrl_init(struct group_table *group_table)
tx_counter = rconn_packet_counter_create();
hmap_init(&installed_flows);
ovs_list_init(&flow_updates);
+ ovn_init_symtab(&symtab);
groups = group_table;
}
@@ -544,6 +549,7 @@ ofctrl_destroy(void)
rconn_destroy(swconn);
ovn_flow_table_destroy(&installed_flows);
rconn_packet_counter_destroy(tx_counter);
+ shash_destroy(&symtab);
}
int64_t
@@ -1067,3 +1073,96 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
cur_cfg = nb_cfg;
}
}
+
+/* Looks up the logical port with the name 'port_name' in 'br_int_'. If
+ * found, returns true and sets '*portp' to the OpenFlow port number
+ * assigned to the port. Otherwise, returns false. */
+static bool
+ofctrl_lookup_port(const void *br_int_, const char *port_name,
+ unsigned int *portp)
+{
+ const struct ovsrec_bridge *br_int = br_int_;
+
+ for (int i = 0; i < br_int->n_ports; i++) {
+ const struct ovsrec_port *port_rec = br_int->ports[i];
+ for (int j = 0; j < port_rec->n_interfaces; j++) {
+ const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
+ const char *iface_id = smap_get(&iface_rec->external_ids,
+ "iface-id");
+
+ if (iface_id && !strcmp(iface_id, port_name)) {
+ if (!iface_rec->n_ofport) {
+ continue;
+ }
+
+ int64_t ofport = iface_rec->ofport[0];
+ if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) {
+ continue;
+ }
+ *portp = ofport;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* Generates a packet described by 'flow_s' in the syntax of an OVN
+ * logical expression and injects it into 'br_int'. The flow
+ * description must contain an ingress logical port that is present on
+ * 'br_int'.
+ *
+ * Returns NULL if successful, otherwise an error message that the caller
+ * must free(). */
+char *
+ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, const char *flow_s,
+ const struct shash *addr_sets)
+{
+ enum ofp_version version = rconn_get_version(swconn);
+ if (version < 0) {
+ return xstrdup("OpenFlow channel not ready.");
+ }
+
+ struct flow uflow;
+ char *error = expr_parse_microflow(flow_s, &symtab, addr_sets,
+ ofctrl_lookup_port, br_int, &uflow);
+ if (error) {
+ return error;
+ }
+
+ /* The physical OpenFlow port was stored in the logical ingress
+ * port, so put it in the correct location for a flow structure. */
+ uflow.in_port.ofp_port = uflow.regs[MFF_LOG_INPORT - MFF_REG0];
+ uflow.regs[MFF_LOG_INPORT - MFF_REG0] = 0;
+
+ if (!uflow.in_port.ofp_port) {
+ return xstrdup("ingress port not found on hypervisor.");
+ }
+
+ uint64_t packet_stub[128 / 8];
+ struct dp_packet packet;
+ dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+ flow_compose(&packet, &uflow);
+
+ uint64_t ofpacts_stub[1024 / 8];
+ struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+ struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
+ resubmit->in_port = OFPP_IN_PORT;
+ resubmit->table_id = 0;
+
+ struct ofputil_packet_out po = {
+ .packet = dp_packet_data(&packet),
+ .packet_len = dp_packet_size(&packet),
+ .buffer_id = UINT32_MAX,
+ .in_port = uflow.in_port.ofp_port,
+ .ofpacts = ofpacts.data,
+ .ofpacts_len = ofpacts.size,
+ };
+ enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+ queue_msg(ofputil_encode_packet_out(&po, proto));
+ dp_packet_uninit(&packet);
+ ofpbuf_uninit(&ofpacts);
+
+ return NULL;
+}
diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
index 79f0cb780..bf5ba0102 100644
--- a/ovn/controller/ofctrl.h
+++ b/ovn/controller/ofctrl.h
@@ -23,11 +23,12 @@
#include "ovsdb-idl.h"
struct controller_ctx;
+struct group_table;
struct hmap;
struct match;
struct ofpbuf;
struct ovsrec_bridge;
-struct group_table;
+struct shash;
/* Interface for OVN main loop. */
void ofctrl_init(struct group_table *group_table);
@@ -43,6 +44,9 @@ struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source);
void ofctrl_ct_flush_zone(uint16_t zone_id);
+char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int,
+ const char *flow_s, const struct shash *addr_sets);
+
/* Flow table interfaces to the rest of ovn-controller. */
void ofctrl_add_flow(struct hmap *desired_flows, uint8_t table_id,
uint16_t priority, uint64_t cookie,
diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml
index 9f4dad1f4..dda26b372 100644
--- a/ovn/controller/ovn-controller.8.xml
+++ b/ovn/controller/ovn-controller.8.xml
@@ -300,6 +300,26 @@
<dd>
Lists each local logical port and its connection tracking zone.
</dd>
+
+ <dt><code>inject-pkt</code> <var>microflow</var></dt>
+ <dd>
+ <p>
+ Injects <var>microflow</var> into the connected Open vSwitch
+ instance. <var>microflow</var> must contain an ingress logical
+ port (<code>inport</code> argument) that is present on the Open
+ vSwitch instance.
+ </p>
+
+ <p>
+ The <var>microflow</var> argument describes the packet whose
+ forwarding is to be simulated, in the syntax of an OVN logical
+ expression, as described in <code>ovn-sb</code>(5), to express
+ constraints. The parser understands prerequisites; for example,
+ if the expression refers to <code>ip4.src</code>, there is no
+ need to explicitly state <code>ip4</code> or <code>eth.type ==
+ 0x800</code>.
+ </p>
+ </dd>
</dl>
</p>
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 16d1ce271..09312db4b 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -59,6 +59,7 @@ VLOG_DEFINE_THIS_MODULE(main);
static unixctl_cb_func ovn_controller_exit;
static unixctl_cb_func ct_zone_list;
+static unixctl_cb_func inject_pkt;
#define DEFAULT_BRIDGE_NAME "br-int"
#define DEFAULT_PROBE_INTERVAL_MSEC 5000
@@ -67,6 +68,13 @@ static void update_probe_interval(struct controller_ctx *);
static void parse_options(int argc, char *argv[]);
OVS_NO_RETURN static void usage(void);
+/* Pending packet to be injected into connected OVS. */
+struct pending_pkt {
+ /* Setting 'conn' indicates that a request is pending. */
+ struct unixctl_conn *conn;
+ char *flow_s;
+};
+
static char *ovs_remote;
struct local_datapath *
@@ -542,6 +550,10 @@ main(int argc, char *argv[])
unixctl_command_register("ct-zone-list", "", 0, 0,
ct_zone_list, &ct_zones);
+ struct pending_pkt pending_pkt = { .conn = NULL };
+ unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
+ &pending_pkt);
+
/* Main loop. */
exiting = false;
while (!exiting) {
@@ -593,6 +605,9 @@ main(int argc, char *argv[])
}
if (br_int && chassis) {
+ struct shash addr_sets = SHASH_INITIALIZER(&addr_sets);
+ addr_sets_init(&ctx, &addr_sets);
+
patch_run(&ctx, br_int, chassis, &local_datapaths);
enum mf_field_id mff_ovn_geneve = ofctrl_run(br_int,
@@ -602,8 +617,6 @@ main(int argc, char *argv[])
update_ct_zones(&local_lports, &local_datapaths, &ct_zones,
ct_zone_bitmap, &pending_ct_zones);
if (ctx.ovs_idl_txn) {
- struct shash addr_sets = SHASH_INITIALIZER(&addr_sets);
- addr_sets_init(&ctx, &addr_sets);
commit_ct_zones(br_int, &pending_ct_zones);
@@ -617,10 +630,8 @@ main(int argc, char *argv[])
ofctrl_put(&flow_table, &pending_ct_zones,
get_nb_cfg(ctx.ovnsb_idl));
- hmap_destroy(&flow_table);
- expr_addr_sets_destroy(&addr_sets);
- shash_destroy(&addr_sets);
+ hmap_destroy(&flow_table);
if (ctx.ovnsb_idl_txn) {
int64_t cur_cfg = ofctrl_get_cur_cfg();
@@ -630,8 +641,33 @@ main(int argc, char *argv[])
}
}
+ if (pending_pkt.conn) {
+ char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s,
+ &addr_sets);
+ if (error) {
+ unixctl_command_reply_error(pending_pkt.conn, error);
+ free(error);
+ } else {
+ unixctl_command_reply(pending_pkt.conn, NULL);
+ }
+ pending_pkt.conn = NULL;
+ free(pending_pkt.flow_s);
+ }
+
update_sb_monitors(ctx.ovnsb_idl, chassis,
&local_lports, &local_datapaths);
+
+ expr_addr_sets_destroy(&addr_sets);
+ shash_destroy(&addr_sets);
+ }
+
+ /* If we haven't handled the pending packet insertion
+ * request, the system is not ready. */
+ if (pending_pkt.conn) {
+ unixctl_command_reply_error(pending_pkt.conn,
+ "ovn-controller not ready.");
+ pending_pkt.conn = NULL;
+ free(pending_pkt.flow_s);
}
mcgroup_index_destroy(&mcgroups);
@@ -650,7 +686,7 @@ main(int argc, char *argv[])
unixctl_server_run(unixctl);
unixctl_server_wait(unixctl);
- if (exiting) {
+ if (exiting || pending_pkt.conn) {
poll_immediate_wake();
}
@@ -849,6 +885,20 @@ ct_zone_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
ds_destroy(&ds);
}
+static void
+inject_pkt(struct unixctl_conn *conn, int argc OVS_UNUSED,
+ const char *argv[], void *pending_pkt_)
+{
+ struct pending_pkt *pending_pkt = pending_pkt_;
+
+ if (pending_pkt->conn) {
+ unixctl_command_reply_error(conn, "already pending packet injection");
+ return;
+ }
+ pending_pkt->conn = conn;
+ pending_pkt->flow_s = xstrdup(argv[1]);
+}
+
/* Get the desired SB probe timer from the OVS database and configure it into
* the SB database. */
static void
diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
index af3c4b052..230580b47 100644
--- a/ovn/controller/pinctrl.h
+++ b/ovn/controller/pinctrl.h
@@ -21,10 +21,10 @@
#include "openvswitch/meta-flow.h"
+struct controller_ctx;
struct hmap;
struct lport_index;
struct ovsrec_bridge;
-struct controller_ctx;
struct sbrec_chassis;
void pinctrl_init(void);