2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

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>
This commit is contained in:
Justin Pettit 2016-12-23 16:34:48 -08:00
parent dc5a1213a2
commit 714651c7db
6 changed files with 183 additions and 8 deletions

2
NEWS
View File

@ -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.

View File

@ -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;
}

View File

@ -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,

View File

@ -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>

View File

@ -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

View File

@ -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);