Skip to content

Commit

Permalink
ovn-controller: Introduce "inject-pkt" ovs-appctl command.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Ben Pfaff <[email protected]>
  • Loading branch information
justinpettit committed Jan 5, 2017
1 parent dc5a121 commit 714651c
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 8 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Post-v2.6.0
command or the ovn-ctl "--db-sb-create-insecure-remote" and
"--db-nb-create-insecure-remote" command-line options for
information regarding remote connection configuration.
* 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.
Expand Down
99 changes: 99 additions & 0 deletions ovn/controller/ofctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
6 changes: 5 additions & 1 deletion ovn/controller/ofctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions ovn/controller/ovn-controller.8.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
62 changes: 56 additions & 6 deletions ovn/controller/ovn-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 *
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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);

Expand All @@ -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();
Expand All @@ -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);
Expand All @@ -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();
}

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ovn/controller/pinctrl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 714651c

Please sign in to comment.