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 <jpet...@ovn.org> --- 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(ofpacts_stub); + enum ofp_version version = rconn_get_version(swconn); + 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 79f0cb7..bf5ba01 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 9f4dad1..dda26b3 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 2533899..36dbcdf 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 * @@ -546,6 +554,9 @@ main(int argc, char *argv[]) ct_zone_list, &ct_zones); struct shash addr_sets = SHASH_INITIALIZER(&addr_sets); + struct pending_pkt pending_pkt = { .conn = NULL }; + unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt, + &pending_pkt); /* Main loop. */ exiting = false; @@ -620,6 +631,7 @@ main(int argc, char *argv[]) ofctrl_put(&flow_table, &pending_ct_zones, get_nb_cfg(ctx.ovnsb_idl)); + hmap_destroy(&flow_table); if (ctx.ovnsb_idl_txn) { int64_t cur_cfg = ofctrl_get_cur_cfg(); @@ -629,10 +641,32 @@ 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); } + /* 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); lport_index_destroy(&lports); ldatapath_index_destroy(&ldatapaths); @@ -649,7 +683,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 +883,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 af3c4b0..230580b 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); -- 1.9.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev