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 @@
inject-pkt
microflow
+ Injects microflow into the connected Open vSwitch
+ instance. microflow must contain an ingress logical
+ port (inport
argument) that is present on the Open
+ vSwitch instance.
+
+ The microflow argument describes the packet whose
+ forwarding is to be simulated, in the syntax of an OVN logical
+ expression, as described in ovn-sb
(5), to express
+ constraints. The parser understands prerequisites; for example,
+ if the expression refers to ip4.src
, there is no
+ need to explicitly state ip4
or eth.type ==
+ 0x800
.
+