Re: [ovs-dev] [PATCH 7/9] ovn-controller: Introduce "inject-pkt" ovs-appctl command.

2017-01-05 Thread Ben Pfaff
On Wed, Jan 04, 2017 at 06:13:09PM -0800, Justin Pettit wrote:
> 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 

This looks quite useful.  Thank you.

ofctrl_inject_packet() should check that rconn_get_version() does not
return -1, because that indicates that we can't encode or send a packet
right now.

At least in the long run we might want to break the pending_pkt handling
out of main() into help functions.

Acked-by: Ben Pfaff 
___
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev


[ovs-dev] [PATCH 7/9] ovn-controller: Introduce "inject-pkt" ovs-appctl command.

2017-01-04 Thread Justin Pettit
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