diff options
author | Justin Pettit <jpettit@ovn.org> | 2016-12-23 16:34:48 -0800 |
---|---|---|
committer | Justin Pettit <jpettit@ovn.org> | 2017-01-05 13:51:17 -0800 |
commit | 714651c7db6a5cd8a22e2d0edb2d554718204bf3 (patch) | |
tree | 5296abd94bdcf0936c306557d3ffa5df4c3e2bc6 /ovn | |
parent | dc5a1213a226766a66274cef24dd0a0719498eb0 (diff) | |
download | openvswitch-714651c7db6a5cd8a22e2d0edb2d554718204bf3.tar.gz |
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>
Diffstat (limited to 'ovn')
-rw-r--r-- | ovn/controller/ofctrl.c | 99 | ||||
-rw-r--r-- | ovn/controller/ofctrl.h | 6 | ||||
-rw-r--r-- | ovn/controller/ovn-controller.8.xml | 20 | ||||
-rw-r--r-- | ovn/controller/ovn-controller.c | 62 | ||||
-rw-r--r-- | ovn/controller/pinctrl.h | 2 |
5 files changed, 181 insertions, 8 deletions
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c index c79660bf4..ceff1389b 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,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; +} diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h index 79f0cb780..bf5ba0102 100644 --- a/ovn/controller/ofctrl.h +++ b/ovn/controller/ofctrl.h @@ -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, diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml index 9f4dad1f4..dda26b372 100644 --- a/ovn/controller/ovn-controller.8.xml +++ b/ovn/controller/ovn-controller.8.xml @@ -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> diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c index 16d1ce271..09312db4b 100644 --- a/ovn/controller/ovn-controller.c +++ b/ovn/controller/ovn-controller.c @@ -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 diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h index af3c4b052..230580b47 100644 --- a/ovn/controller/pinctrl.h +++ b/ovn/controller/pinctrl.h @@ -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); |