This patch extends the vswitchd schema to allow specifying settings specific to mirror hardware offload. The settings include: mirror-offload, flow-dst-addr, bond-port-other, mirror-tunnel-addr, output-src-vlan, and output-dst-vlan. The mirror-offload setting is a boolean variable that can be used to enable mirror offloading. The flow-dst-addr variable allows for filtering of traffic to be mirrored based on the destination MAC address. The mirror-tunnel-addr variable enables mirroring to ports attached to bridges other than the traffic source bridge. The output-src-vlan and the output-dst-vlan are used to specify the VLAN tag for ingress and egress traffic. This allows a VLAN tag to be added to mirrored traffic, which is used to trigger device mirroring by means of the VLAN tag. The added VLAN tag can be used to denote the source of the mirrored traffic. The added tag can be stripped if the destination VNF has VLAN stripping enabled. An example of configuring egress traffic mirroring with VLAN tag 100 is as follows: ovs-vsctl -- set bridge br-int mirror=@m1 -- --id=@vnic1 get port vnic1 -- --id=@m1 create mirror name=egress mirror-offload=1 select-src-port=@vnic1 mirror-tunnel-addr="0000\:87\:02.0" output-src-vlan=100 An example of configuring ingress traffic mirroring with VLAN tag 200 is as follows: ovs-vsctl -- set bridge br-int mirror=@m2 -- --id=@vnic1 get port vnic1 -- --id=@m2 create mirror name=ingress mirror-offload=1 select-dst-port=@vnic1 mirror-tunnel-addr="0000\:87\:02.1" output-dst-vlan=200 An example of configuring per-flow egress traffic mirroring with VLAN tag 300 is as follows: ovs-vsctl -- set bridge br-int mirror=@m3 -- --id=@vnic1 get port vnic1-- --id=@m3 create mirror name=perflow mirror-offload=1 select-src-port=@vnic1 flow-dst-mac="52\:54\:00\:00\:00\:01" mirror-tunnel-addr="0000\:18\:00.0" output-src-vlan=300 Note: Users can use the default ovs-vsctl commands to inspect and remove the mirror configuration settings.
Signed-off-by: Liang-Min Wang <liang-min.w...@intel.com Tested-by: Timothy Miskell <timothy.misk...@intel.com> Suggested-by: Munish Mehan <mm6...@att.com> --- vswitchd/bridge.c | 268 ++++++++++++++++++++++++++++++++++++- vswitchd/vswitch.ovsschema | 24 +++- vswitchd/vswitch.xml | 54 ++++++++ 3 files changed, 342 insertions(+), 4 deletions(-) diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 5ed7e8234..a7f903d9c 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -38,6 +38,7 @@ #include "mac-learning.h" #include "mcast-snooping.h" #include "netdev.h" +#include "netdev-provider.h" #include "netdev-offload.h" #include "nx-match.h" #include "ofproto/bond.h" @@ -330,6 +331,9 @@ static void mirror_destroy(struct mirror *); static bool mirror_configure(struct mirror *); static void mirror_refresh_stats(struct mirror *); +static void mirror_offload_destroy(struct mirror *); +static bool mirror_offload_configure(struct mirror *); + static void iface_configure_lacp(struct iface *, struct lacp_member_settings *); static bool iface_create(struct bridge *, const struct ovsrec_interface *, @@ -423,6 +427,35 @@ if_notifier_changed(struct if_notifier *notifier OVS_UNUSED) seq_wait(ifaces_changed, last_ifaces_changed); return changed; } + +static struct port * +port_lookup_all(const char *port_name) +{ + struct bridge *br; + struct port *port = NULL; + int found = 0; + + HMAP_FOR_EACH (br, node, &all_bridges) { + struct port *temp_port = NULL; + temp_port = port_lookup(br, port_name); + if (temp_port) { + if (!port) { + port = temp_port; + } + found++; + } + } + + if (found) { + if (found > 1) { + VLOG_INFO("More than one bridge owns port with name:%s\n", + port_name); + } + return port; + } + return NULL; +} + /* Public functions. */ @@ -5055,14 +5088,225 @@ mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg) return m; } +static struct netdev *get_netdev_from_port(struct mirror *m, + struct port **port, const char *name) +{ + struct port *temp_port; + struct iface *iface; + + *port = NULL; + temp_port = port_lookup(m->bridge, name); + if (temp_port) { + LIST_FOR_EACH (iface, port_elem, &temp_port->ifaces) { + if (iface) { + *port = temp_port; + return iface->netdev; + } + } + } + /* try different bridges */ + temp_port = port_lookup_all(name); + if (temp_port) { + LIST_FOR_EACH (iface, port_elem, &temp_port->ifaces) { + if (iface) { + *port = temp_port; + return iface->netdev; + } + } + } + return NULL; +} + +static void +release_mirror_offload_info(struct mirror_offload_info *info) +{ + if (info->src) { + free(info->src); + } + if (info->dst) { + free(info->dst); + } + if (info->flow_dst_mac) { + free(info->flow_dst_mac); + } + if (info->flow_src_mac) { + free(info->flow_src_mac); + } + if (info->output_src_tags) { + free(info->output_src_tags); + } + if (info->output_dst_tags) { + free(info->output_dst_tags); + } + if (info->name) { + free(info->name); + } + if (info->mirror_tunnel_addr) { + free(info->mirror_tunnel_addr); + } +} + +static int +set_mirror_offload_info(struct mirror *m, struct mirror_offload_info *info) +{ + const struct ovsrec_mirror *cfg = m->cfg; + struct port *port = NULL; + int i; + + if (m->name) { + info->name = xmalloc(strlen(m->name)+1); + ovs_strzcpy(info->name, m->name, strlen(m->name)); + } + + if (cfg->mirror_tunnel_addr) { + info->mirror_tunnel_addr = xmalloc(strlen(cfg->mirror_tunnel_addr) + + 1); + ovs_strzcpy(info->mirror_tunnel_addr, cfg->mirror_tunnel_addr, strlen(cfg->mirror_tunnel_addr)); + } else { + VLOG_ERR("mirror-offload configuration fails because" + " lack of tunnel device\n"); + return -1; + } + + /* source port */ + info->n_src_port = cfg->n_select_src_port; + if (info->n_src_port) { + info->src = xmalloc(sizeof(struct netdev *)*info->n_src_port); + info->flow_dst_mac = xmalloc(sizeof(struct eth_addr)* + info->n_src_port); + if (info->n_src_port != cfg->n_output_src_vlan) { + VLOG_ERR("src port count:%d ouput src vlan count:%ld", + info->n_src_port, cfg->n_output_src_vlan); + return -1; + } + info->output_src_tags = xmalloc(sizeof(uint16_t)*info->n_src_port); + } + + if (info->n_src_port) { + /* find netdev instance for each port */ + for (i = 0; i < info->n_src_port; i++) { + info->src[i] = get_netdev_from_port(m, &port, + cfg->select_src_port[i]->name); + if (!info->src[i]) { + VLOG_ERR("src-port: %s is not a netdev device\n", + cfg->select_src_port[i]->name); + return -1; + } + } + memset(info->flow_dst_mac, 0, sizeof(struct eth_addr)* + info->n_src_port); + + /* + * for source port, flow is separated by + * different dst mac addr + */ + if (cfg->n_flow_dst_mac) { + int dst_count = (info->n_src_port > cfg->n_flow_dst_mac)? + cfg->n_flow_dst_mac:info->n_src_port; + for (i = 0; i < dst_count; i++) { + eth_addr_from_string(cfg->flow_dst_mac[i], + &info->flow_dst_mac[i]); + } + } + + if (cfg->n_output_src_vlan) { + int count = (cfg->n_output_src_vlan > info->n_src_port)? + info->n_src_port:cfg->n_output_src_vlan; + for (i = 0; i < count; i++) + info->output_src_tags[i] = cfg->output_src_vlan[i] & 0xFFF; + } + } + + /* dst ports */ + info->n_dst_port = cfg->n_select_dst_port; + if (info->n_dst_port) { + info->dst = xmalloc(sizeof(struct netdev *)*info->n_dst_port); + info->flow_src_mac = xmalloc(sizeof(struct eth_addr)* + info->n_dst_port); + if (info->n_dst_port != cfg->n_output_dst_vlan) { + VLOG_ERR("dst port count:%d ouput dst vlan count:%ld\n", + info->n_dst_port, cfg->n_output_dst_vlan); + return -1; + } + info->output_dst_tags = xmalloc(sizeof(uint16_t)*info->n_dst_port); + } + + if (info->n_dst_port) { + for (i = 0; i < info->n_dst_port; i++) { + info->dst[i] = get_netdev_from_port(m, &port, + cfg->select_dst_port[i]->name); + if (!info->dst[i]) { + VLOG_ERR("dst-port: %s is not a netdev device\n", + cfg->select_dst_port[i]->name); + return -1; + } + } + memset(info->flow_src_mac, 0, sizeof(struct eth_addr)* + info->n_dst_port); + + /* + * for destination port, flow is separated by + * different src mac addr + */ + if (cfg->n_flow_src_mac) { + int src_count = (info->n_dst_port > cfg->n_flow_src_mac)? + cfg->n_flow_src_mac:info->n_dst_port; + for (i = 0; i < src_count; i++) { + eth_addr_from_string(cfg->flow_src_mac[i], + &info->flow_src_mac[i]); + } + } + + if (cfg->n_output_dst_vlan) { + int count = (cfg->n_output_dst_vlan > info->n_dst_port)? + info->n_dst_port:cfg->n_output_dst_vlan; + for (i = 0; i < count; i++) + info->output_dst_tags[i] = cfg->output_dst_vlan[i] & 0xFFF; + } + } + + VLOG_INFO("sucess creating mirror-offload(%s): with %d src-port" + " streams %d dst-port streams to tunnel %s\n", + cfg->name, info->n_src_port, info->n_dst_port, + info->mirror_tunnel_addr?info->mirror_tunnel_addr:"none"); + return 0; +} + +static void +mirror_offload_destroy(struct mirror *m) +{ + struct mirror_offload_info info; + + memset(&info, 0, sizeof(struct mirror_offload_info)); + info.add_mirror = false; + if (m->name) { + info.name = malloc(strlen(m->name)+1); + if (info.name) { + ovs_strzcpy(info.name, m->name, strlen(m->name)); + } + } + + netdev_mirror_offload_put(&info); + if (info.name) { + free(info.name); + } + if (info.mirror_tunnel_addr) { + free(info.mirror_tunnel_addr); + } +} + static void mirror_destroy(struct mirror *m) { if (m) { struct bridge *br = m->bridge; - if (br->ofproto) { - ofproto_mirror_unregister(br->ofproto, m); + if (m->cfg && m->cfg->mirror_offload) { + mirror_offload_destroy(m); + } else { + if (br->ofproto) { + ofproto_mirror_unregister(br->ofproto, m); + } } hmap_remove(&br->mirrors, &m->hmap_node); @@ -5094,12 +5338,32 @@ mirror_collect_ports(struct mirror *m, *n_out_portsp = n_out_ports; } +static bool +mirror_offload_configure(struct mirror *m) +{ + struct mirror_offload_info info; + + memset(&info, 0, sizeof(struct mirror_offload_info)); + info.add_mirror = true; + if (set_mirror_offload_info(m, &info)) { + release_mirror_offload_info(&info); + return false; + } + + netdev_mirror_offload_put(&info); + release_mirror_offload_info(&info); + return true; +} + static bool mirror_configure(struct mirror *m) { const struct ovsrec_mirror *cfg = m->cfg; struct ofproto_mirror_settings s; + if (cfg->mirror_offload) { + return mirror_offload_configure(m); + } /* Set name. */ if (strcmp(cfg->name, m->name)) { free(m->name); diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index 0666c8c76..4a1a34a1f 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "8.2.0", - "cksum": "1076640191 26427", + "version": "8.2.1", + "cksum": "4051567316 27206", "tables": { "Open_vSwitch": { "columns": { @@ -418,8 +418,18 @@ "columns": { "name": { "type": "string"}, + "mirror_tunnel_addr": { + "type": "string"}, "select_all": { "type": "boolean"}, + "mirror_offload": { + "type": "boolean"}, + "flow_src_mac": { + "type": {"key": {"type": "string"}, + "min": 0, "max": "unlimited"}}, + "flow_dst_mac": { + "type": {"key": {"type": "string"}, + "min": 0, "max": "unlimited"}}, "select_src_port": { "type": {"key": {"type": "uuid", "refTable": "Port", @@ -440,6 +450,16 @@ "refTable": "Port", "refType": "weak"}, "min": 0, "max": 1}}, + "output_src_vlan": { + "type": {"key": {"type": "integer", + "minInteger": 0, + "maxInteger": 4294967295}, + "min": 0, "max": 4096}}, + "output_dst_vlan": { + "type": {"key": {"type": "integer", + "minInteger": 0, + "maxInteger": 4294967295}, + "min": 0, "max": 4096}}, "output_vlan": { "type": {"key": {"type": "integer", "minInteger": 1, diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 4597a215d..6f3ff2f1f 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -4869,11 +4869,37 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \ selected VLANs. </p> + <column name="mirror_tunnel_addr"> + BDF string of the tunnel device on which mirrored traffic will be + transmitted. This parameter is only supported in OVS-DPDK. + </column> + <column name="select_all"> If true, every packet arriving or departing on any port is selected for mirroring. </column> + <column name="mirror_offload"> + If true, a hw-assisted port mirroring is configured instead + default mirroring. This parameter is only supported in OVS-DPDK + </column> + + <column name="flow_src_mac"> + The source MAC address(es) for per-flow mirroring. Each MAC + address is separate by ','. This parametr is paired with + select_dst_port. A '0' MAC address indicates the requested mirror + is a per-port mirroring, otherwise it's a per-flow mirroring. + This parameter is only supported in OVS-DPDK. + </column> + + <column name="flow_dst_mac"> + The destination MAC address(es) for per-flow mirroring. Each MAC + address is separate by ','. This parametr is paired with + select_src_port. A '0' MAC address indicates the requested mirror + is a per-port mirroring, otherwise it's a per-flow mirroring. + This parameter is only supported in OVS-DPDK. + </column> + <column name="select_dst_port"> Ports on which departing packets are selected for mirroring. </column> @@ -4955,6 +4981,34 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \ </p> </column> + <column name="output_src_vlan"> + <p>Output VLAN for selected source port packets, if nonempty.</p> + <p> + <em>Please note:</em> This parametr is only supported in OVS-DPDK, + and this parameter is different than + <ref column="output-vlan"/> This vlan is used to add an additional + vlan tag on the mirror traffic, regardless it contains vlan or not. + The receive end could choose to filter out this additional vlan. + This option is provided so the mirrored traffic could maintain its + original vlan informaiton, and this mirror can be used to filter + out un-wanted traffic such as in <ref column="mirror_offload"/>. + </p> + </column> + + <column name="output_dst_vlan"> + <p>Output VLAN for selected destination port packets, if nonempty.</p> + <p> + <em>Please note:</em> This parametr is only supported in OVS-DPDK, + and this parameter is different than + <ref column="output-vlan"/> This vlan is used to add an additional + vlan tag on the mirror traffic, regardless it contains vlan or not. + The receive end could choose to filter out this additional vlan. + This option is provided so the mirrored traffic could maintain its + original vlan informaiton, and this mirror cab be used to filter + out un-wanted traffic such as in <ref column="mirror_offload"/>. + </p> + </column> + <column name="snaplen"> <p>Maximum per-packet number of bytes to mirror.</p> <p>A mirrored packet with size larger than <ref column="snaplen"/> -- 2.17.1 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev