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
---
NEWS| 2 +
ovn/controller/ofctrl.c | 95 +
ovn/controller/ofctrl.h | 6 ++-
ovn/controller/ovn-controller.8.xml | 20
ovn/controller/ovn-controller.c | 50 ++-
ovn/controller/pinctrl.h| 2 +-
6 files changed, 172 insertions(+), 3 deletions(-)
diff --git a/NEWS b/NEWS
index daa9ff5..91d269b 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,8 @@ Post-v2.6.0
- put_dhcp_opts and put_dhcp_optsv6 actions may now be traced.
* Support for managing SSL and remote connection configuration in
northbound and southbound databases.
+ * New appctl "inject-pkt" command in ovn-controller that allows
+ packets to be injected into the connected OVS instance.
- Fixed regression in table stats maintenance introduced in OVS
2.3.0, wherein the number of OpenFlow table hits and misses was
not accurate.
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index c79660b..9c4019b 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,92 @@ 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)
+{
+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(of