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:
parent
dc5a1213a2
commit
714651c7db
2
NEWS
2
NEWS
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user