From: Ben Pfaff Date: Fri, 23 Jun 2017 16:47:59 +0000 (+0000) Subject: userspace: Handling of versatile tunnel ports X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=875ab13020b16269e0401b11fe0efc74e2cd952a;p=ovs.git userspace: Handling of versatile tunnel ports In netdev_gre_build_header(), GRE protocol and VXLAN next_potocol is set based on packet_type of flow. If it's about an Ethernet packet, it is set to ETP_TYPE_TEB. Otherwise, if the name space is OFPHTN_ETHERNET, it is set according to the name space type. Signed-off-by: Jan Scheurich Signed-off-by: Ben Pfaff --- diff --git a/NEWS b/NEWS index 4ea7e628e..0f2604ff5 100644 --- a/NEWS +++ b/NEWS @@ -59,11 +59,9 @@ Post-v2.7.0 * OVN services are no longer restarted automatically after upgrade. - Add --cleanup option to command 'ovs-appctl exit' (see ovs-vswitchd(8)). - L3 tunneling: - * Add "layer3" options for tunnel ports that support non-Ethernet (L3) - payload (GRE, VXLAN-GPE). + * Use new tunnel port option "packet_type" to configure L2 vs. L3. * New vxlan tunnel extension "gpe" to support VXLAN-GPE tunnels. - * Transparently pop and push Ethernet headers at transmit/reception - of packets to/from L3 tunnels. + * New support for non-Ethernet (L3) payloads in GRE and VXLAN-GPE. - The BFD detection multiplier is now user-configurable. - Add experimental support for hardware offloading * HW offloading is disabled by default. diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml index 856e1ba8c..634ab69e4 100644 --- a/lib/meta-flow.xml +++ b/lib/meta-flow.xml @@ -26,19 +26,27 @@ networking technology in use are called called root fields. Open vSwitch 2.7 and earlier considered Ethernet fields to be root fields, and this remains the default mode of operation for Open vSwitch bridges. - In this mode, when a packet is received from a non-Ethernet interfaces, - such as a layer-3 LISP or GRE tunnel, Open vSwitch force-fits it to this + When a packet is received from a non-Ethernet interfaces, such as a layer-3 + LISP tunnel, Open vSwitch 2.7 and earlier force-fit the packet to this Ethernet-centric point of view by pretending that an Ethernet header is present whose Ethernet type that indicates the packet's actual type (and whose source and destination addresses are all-zero).

- Open vSwitch 2.8 and later supports the ``packet type-aware pipeline'' - concept introduced in OpenFlow 1.5. A bridge configured to be packet - type-aware can handle packets of multiple networking technologies, such as - Ethernet, IP, ARP, MPLS, or NSH in parallel. Such a bridge does not have - any root fields. + Open vSwitch 2.8 and later implement the ``packet type-aware pipeline'' + concept introduced in OpenFlow 1.5. Such a pipeline does not have any root + fields. Instead, a new metadata field, , + indicates the basic type of the packet, which can be Ethernet, IPv4, IPv6, + or another type. For backward compatibility, by default Open vSwitch 2.8 + imitates the behavior of Open vSwitch 2.7 and earlier. Later versions of + Open vSwitch may change the default, and in the meantime controllers can + turn off this legacy behavior, on a port-by-port basis, by setting + options:packet_type to ptap in the + Interface table. This is significant only for ports that can + handle non-Ethernet packets, which is currently just LISP, VXLAN-GPE, and + GRE tunnel ports. See ovs-vwitchd.conf.db(5) for more + information.

@@ -332,14 +340,6 @@ tcp,tp_src=0x07c0/0xfff0

mplsm
eth_type=0x8848
-

- These shorthand notations continue to work in packet type-aware bridges. - The absence of a packet_type match implies - packet_type=ethernet, so that shorthands match on Ethernet - packets with the implied eth_type. Please note that the shorthand - ip does not match packets of packet_type (1,0x800) for IPv4. -

-

Evolution of OpenFlow Fields

diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c index f863a189c..6cc83d347 100644 --- a/lib/netdev-bsd.c +++ b/lib/netdev-bsd.c @@ -1517,6 +1517,7 @@ netdev_bsd_update_flags(struct netdev *netdev_, enum netdev_flags off, \ GET_FEATURES, \ NULL, /* set_advertisement */ \ + NULL, /* get_pt_mode */ \ NULL, /* set_policing */ \ NULL, /* get_qos_type */ \ NULL, /* get_qos_capabilities */ \ diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c index bba4de378..5ad924469 100644 --- a/lib/netdev-dpdk.c +++ b/lib/netdev-dpdk.c @@ -3276,6 +3276,7 @@ unlock: GET_STATS, \ GET_FEATURES, \ NULL, /* set_advertisements */ \ + NULL, /* get_pt_mode */ \ \ netdev_dpdk_set_policing, \ netdev_dpdk_get_qos_types, \ diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index d189a8615..51d29d54a 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -1382,6 +1382,7 @@ netdev_dummy_update_flags(struct netdev *netdev_, \ NULL, /* get_features */ \ NULL, /* set_advertisements */ \ + NULL, /* get_pt_mode */ \ \ NULL, /* set_policing */ \ NULL, /* get_qos_types */ \ diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 6978c44c7..e1d87019f 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -2843,6 +2843,7 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off, \ GET_FEATURES, \ netdev_linux_set_advertisements, \ + NULL, /* get_pt_mode */ \ \ netdev_linux_set_policing, \ netdev_linux_get_qos_types, \ diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index c7a299345..7f3cf984e 100644 --- a/lib/netdev-native-tnl.c +++ b/lib/netdev-native-tnl.c @@ -463,10 +463,13 @@ netdev_gre_build_header(const struct netdev *netdev, greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE); - if (tnl_cfg->is_layer3) { - greh->protocol = params->flow->dl_type; - } else { + if (params->flow->packet_type == htonl(PT_ETH)) { greh->protocol = htons(ETH_TYPE_TEB); + } else if (pt_ns(params->flow->packet_type) == OFPHTN_ETHERTYPE) { + greh->protocol = pt_ns_type_be(params->flow->packet_type); + } else { + ovs_mutex_unlock(&dev->mutex); + return 1; } greh->flags = 0; @@ -575,8 +578,10 @@ netdev_vxlan_build_header(const struct netdev *netdev, put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS | VXLAN_HF_GPE)); put_16aligned_be32(&vxh->vx_vni, htonl(ntohll(params->flow->tunnel.tun_id) << 8)); - if (tnl_cfg->is_layer3) { - switch (ntohs(params->flow->dl_type)) { + if (params->flow->packet_type == htonl(PT_ETH)) { + vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET; + } else if (pt_ns(params->flow->packet_type) == OFPHTN_ETHERTYPE) { + switch (pt_ns_type(params->flow->packet_type)) { case ETH_TYPE_IP: vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_IPV4; break; @@ -586,9 +591,11 @@ netdev_vxlan_build_header(const struct netdev *netdev, case ETH_TYPE_TEB: vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET; break; + default: + goto drop; } } else { - vxh->vx_gpe.next_protocol = VXLAN_GPE_NP_ETHERNET; + goto drop; } } else { put_16aligned_be32(&vxh->vx_flags, htonl(VXLAN_FLAGS)); @@ -600,6 +607,10 @@ netdev_vxlan_build_header(const struct netdev *netdev, data->header_len += sizeof *vxh; data->tnl_type = OVS_VPORT_TYPE_VXLAN; return 0; + +drop: + ovs_mutex_unlock(&dev->mutex); + return 1; } struct dp_packet * diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index 79143d2c8..3c3c18113 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -474,6 +474,12 @@ struct netdev_class { int (*set_advertisements)(struct netdev *netdev, enum netdev_features advertise); + /* Returns 'netdev''s configured packet_type mode. + * + * This function may be set to null if it would always return + * NETDEV_PT_LEGACY_L2. */ + enum netdev_pt_mode (*get_pt_mode)(const struct netdev *netdev); + /* Attempts to set input rate limiting (policing) policy, such that up to * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative * burst size of 'kbits' kb. diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 640cdbe9c..7de58b8a3 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -98,18 +98,6 @@ netdev_vport_is_patch(const struct netdev *netdev) return class->get_config == get_patch_config; } -bool -netdev_vport_is_layer3(const struct netdev *dev) -{ - if (is_vport_class(netdev_get_class(dev))) { - struct netdev_vport *vport = netdev_vport_cast(dev); - - return vport->tnl_cfg.is_layer3; - } - - return false; -} - static bool netdev_vport_needs_dst_port(const struct netdev *dev) { @@ -407,6 +395,30 @@ parse_tunnel_ip(const char *value, bool accept_mcast, bool *flow, return 0; } +enum tunnel_layers { + TNL_L2 = 1 << 0, /* 1 if a tunnel type can carry Ethernet traffic. */ + TNL_L3 = 1 << 1 /* 1 if a tunnel type can carry L3 traffic. */ +}; +static enum tunnel_layers +tunnel_supported_layers(const char *type, + const struct netdev_tunnel_config *tnl_cfg) +{ + if (!strcmp(type, "lisp")) { + return TNL_L3; + } else if (!strcmp(type, "gre")) { + return TNL_L2 | TNL_L3; + } else if (!strcmp(type, "vxlan") && tnl_cfg->exts & OVS_VXLAN_EXT_GPE) { + return TNL_L2 | TNL_L3; + } else { + return TNL_L2; + } +} +static enum netdev_pt_mode +default_pt_mode(enum tunnel_layers layers) +{ + return layers == TNL_L3 ? NETDEV_PT_LEGACY_L3 : NETDEV_PT_LEGACY_L2; +} + static int set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) { @@ -414,16 +426,14 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) const char *name = netdev_get_name(dev_); const char *type = netdev_get_type(dev_); struct ds errors = DS_EMPTY_INITIALIZER; - bool needs_dst_port, has_csum, optional_layer3; + bool needs_dst_port, has_csum; uint16_t dst_proto = 0, src_proto = 0; struct netdev_tunnel_config tnl_cfg; struct smap_node *node; - bool is_layer3 = false; int err; has_csum = strstr(type, "gre") || strstr(type, "geneve") || strstr(type, "stt") || strstr(type, "vxlan"); - optional_layer3 = !strcmp(type, "gre"); memset(&tnl_cfg, 0, sizeof tnl_cfg); /* Add a default destination port for tunnel ports if none specified. */ @@ -437,7 +447,6 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) if (!strcmp(type, "lisp")) { tnl_cfg.dst_port = htons(LISP_DST_PORT); - tnl_cfg.is_layer3 = true; } if (!strcmp(type, "stt")) { @@ -501,9 +510,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) } } else if (!strcmp(node->key, "key") || !strcmp(node->key, "in_key") || - !strcmp(node->key, "out_key")) { + !strcmp(node->key, "out_key") || + !strcmp(node->key, "packet_type")) { /* Handled separately below. */ - } else if (!strcmp(node->key, "exts")) { + } else if (!strcmp(node->key, "exts") && !strcmp(type, "vxlan")) { char *str = xstrdup(node->value); char *ext, *save_ptr = NULL; @@ -515,7 +525,6 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GBP); } else if (!strcmp(type, "vxlan") && !strcmp(ext, "gpe")) { tnl_cfg.exts |= (1 << OVS_VXLAN_EXT_GPE); - optional_layer3 = true; } else { ds_put_format(&errors, "%s: unknown extension '%s'\n", name, ext); @@ -528,21 +537,44 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp) } else if (!strcmp(node->key, "egress_pkt_mark")) { tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10); tnl_cfg.set_egress_pkt_mark = true; - } else if (!strcmp(node->key, "layer3")) { - if (!strcmp(node->value, "true")) { - is_layer3 = true; - } } else { ds_put_format(&errors, "%s: unknown %s argument '%s'\n", name, type, node->key); } } - if (optional_layer3 && is_layer3) { - tnl_cfg.is_layer3 = is_layer3; - } else if (!optional_layer3 && is_layer3) { - ds_put_format(&errors, "%s: unknown %s argument '%s'\n", - name, type, "layer3"); + enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); + const char *full_type = (strcmp(type, "vxlan") ? type + : tnl_cfg.exts & OVS_VXLAN_EXT_GPE ? "VXLAN-GPE" + : "VXLAN (without GPE"); + const char *packet_type = smap_get(args, "packet_type"); + if (!packet_type) { + tnl_cfg.pt_mode = default_pt_mode(layers); + } else if (!strcmp(packet_type, "legacy_l2")) { + tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L2; + if (!(layers & TNL_L2)) { + ds_put_format(&errors, "%s: legacy_l2 configured on %s tunnel " + "that cannot carry L2 traffic\n", + name, full_type); + err = EINVAL; + goto out; + } + } else if (!strcmp(packet_type, "legacy_l3")) { + tnl_cfg.pt_mode = NETDEV_PT_LEGACY_L3; + if (!(layers & TNL_L3)) { + ds_put_format(&errors, "%s: legacy_l3 configured on %s tunnel " + "that cannot carry L3 traffic\n", + name, full_type); + err = EINVAL; + goto out; + } + } else if (!strcmp(packet_type, "ptap")) { + tnl_cfg.pt_mode = NETDEV_PT_AWARE; + } else { + ds_put_format(&errors, "%s: unknown packet_type '%s'\n", + name, packet_type); + err = EINVAL; + goto out; } if (!ipv6_addr_is_set(&tnl_cfg.ipv6_dst) && !tnl_cfg.ip_dst_flow) { @@ -675,9 +707,12 @@ get_tunnel_config(const struct netdev *dev, struct smap *args) smap_add(args, "csum", "true"); } - if (tnl_cfg.is_layer3 && (!strcmp("gre", type) || - !strcmp("vxlan", type))) { - smap_add(args, "layer3", "true"); + enum tunnel_layers layers = tunnel_supported_layers(type, &tnl_cfg); + if (tnl_cfg.pt_mode != default_pt_mode(layers)) { + smap_add(args, "packet_type", + tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" + : tnl_cfg.pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" + : "ptap"); } if (!tnl_cfg.dont_fragment) { @@ -809,6 +844,15 @@ get_stats(const struct netdev *netdev, struct netdev_stats *stats) return 0; } +static enum netdev_pt_mode +get_pt_mode(const struct netdev *netdev) +{ + struct netdev_vport *dev = netdev_vport_cast(netdev); + + return dev->tnl_cfg.pt_mode; +} + + #ifdef __linux__ static int @@ -873,6 +917,7 @@ netdev_vport_get_ifindex(const struct netdev *netdev_) \ NULL, /* get_features */ \ NULL, /* set_advertisements */ \ + get_pt_mode, \ \ NULL, /* set_policing */ \ NULL, /* get_qos_types */ \ diff --git a/lib/netdev-vport.h b/lib/netdev-vport.h index 048aa6ebf..9d756a265 100644 --- a/lib/netdev-vport.h +++ b/lib/netdev-vport.h @@ -31,7 +31,6 @@ void netdev_vport_tunnel_register(void); void netdev_vport_patch_register(void); bool netdev_vport_is_patch(const struct netdev *); -bool netdev_vport_is_layer3(const struct netdev *); char *netdev_vport_patch_peer(const struct netdev *netdev); diff --git a/lib/netdev.c b/lib/netdev.c index 765bf4b9c..a7840a84e 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -727,6 +727,14 @@ netdev_set_tx_multiq(struct netdev *netdev, unsigned int n_txq) return error; } +enum netdev_pt_mode +netdev_get_pt_mode(const struct netdev *netdev) +{ + return (netdev->netdev_class->get_pt_mode + ? netdev->netdev_class->get_pt_mode(netdev) + : NETDEV_PT_LEGACY_L2); +} + /* Sends 'batch' on 'netdev'. Returns 0 if successful (for every packet), * otherwise a positive errno value. Returns EAGAIN without blocking if * at least one the packets cannot be queued immediately. Returns EMSGSIZE diff --git a/lib/netdev.h b/lib/netdev.h index 31846fabf..998f942e2 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -71,6 +71,32 @@ struct smap; struct sset; struct ovs_action_push_tnl; +enum netdev_pt_mode { + /* The netdev is packet type aware. It can potentially carry any kind of + * packet. This "modern" mode is appropriate for both netdevs that handle + * only a single kind of packet (such as a virtual or physical Ethernet + * interface) and for those that can handle multiple (such as VXLAN-GPE or + * Geneve). */ + NETDEV_PT_AWARE, + + /* The netdev sends and receives only Ethernet frames. The netdev cannot + * carry packets other than Ethernet frames. This is a legacy mode for + * backward compability with controllers that are not prepared to handle + * OpenFlow 1.5+ "packet_type". */ + NETDEV_PT_LEGACY_L2, + + /* The netdev sends and receives only IPv4 and IPv6 packets. The netdev + * cannot carry Ethernet frames or other kinds of packets. + * + * IPv4 and IPv6 packets carried over the netdev are treated as Ethernet: + * when they are received, they are converted to Ethernet by adding a dummy + * header with the proper Ethertype; on tranmission, the Ethernet header is + * stripped. This is a legacy mode for backward compability with + * controllers that are not prepared to handle OpenFlow 1.5+ + * "packet_type". */ + NETDEV_PT_LEGACY_L3, +}; + /* Configuration specific to tunnels. */ struct netdev_tunnel_config { bool in_key_present; @@ -100,7 +126,7 @@ struct netdev_tunnel_config { bool csum; bool dont_fragment; - bool is_layer3; + enum netdev_pt_mode pt_mode; }; void netdev_run(void); @@ -140,6 +166,7 @@ void netdev_mtu_user_config(struct netdev *, bool); bool netdev_mtu_is_user_config(struct netdev *); int netdev_get_ifindex(const struct netdev *); int netdev_set_tx_multiq(struct netdev *, unsigned int n_txq); +enum netdev_pt_mode netdev_get_pt_mode(const struct netdev *); /* Packet reception. */ int netdev_rxq_open(struct netdev *, struct netdev_rxq **, int id); diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 0fe6584b7..ce364b361 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -165,7 +165,7 @@ struct xport { bool may_enable; /* May be enabled in bonds. */ bool is_tunnel; /* Is a tunnel port. */ - bool is_layer3; /* Is a layer 3 port. */ + enum netdev_pt_mode pt_mode; /* packet_type handling. */ struct cfm *cfm; /* CFM handle or null. */ struct bfd *bfd; /* BFD handle or null. */ @@ -905,7 +905,7 @@ xlate_xport_set(struct xport *xport, odp_port_t odp_port, xport->state = state; xport->stp_port_no = stp_port_no; xport->is_tunnel = is_tunnel; - xport->is_layer3 = netdev_vport_is_layer3(netdev); + xport->pt_mode = netdev_get_pt_mode(netdev); xport->may_enable = may_enable; xport->odp_port = odp_port; @@ -2691,7 +2691,10 @@ xlate_normal(struct xlate_ctx *ctx) /* Learn source MAC. */ bool is_grat_arp = is_gratuitous_arp(flow, wc); - if (ctx->xin->allow_side_effects && !in_port->is_layer3) { + if (ctx->xin->allow_side_effects + && flow->packet_type == htonl(PT_ETH) + && in_port->pt_mode != NETDEV_PT_LEGACY_L3 + ) { update_learning_table(ctx, in_xbundle, flow->dl_src, vlan, is_grat_arp); } @@ -3351,15 +3354,19 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, return; } - if (flow->packet_type == htonl(PT_ETH) && xport->is_layer3) { - /* Ethernet packet to L3 outport -> pop ethernet header. */ - flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, - ntohs(flow->dl_type)); - } else if (flow->packet_type != htonl(PT_ETH) && !xport->is_layer3) { - /* L2 outport and non-ethernet packet_type -> add dummy eth header. */ - flow->packet_type = htonl(PT_ETH); - flow->dl_dst = eth_addr_zero; - flow->dl_src = eth_addr_zero; + if (flow->packet_type == htonl(PT_ETH)) { + /* Strip Ethernet header for legacy L3 port. */ + if (xport->pt_mode == NETDEV_PT_LEGACY_L3) { + flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, + ntohs(flow->dl_type)); + } + } else { + /* Add dummy Ethernet header for legacy L2 port. */ + if (xport->pt_mode == NETDEV_PT_LEGACY_L2) { + flow->packet_type = htonl(PT_ETH); + flow->dl_dst = eth_addr_zero; + flow->dl_src = eth_addr_zero; + } } if (xport->peer) { @@ -6394,8 +6401,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) struct xport *in_port = get_ofp_port(xbridge, ctx.base_flow.in_port.ofp_port); - if (flow->packet_type != htonl(PT_ETH) && in_port && in_port->is_layer3 && - ctx.table_id == 0) { + if (flow->packet_type != htonl(PT_ETH) && in_port && + in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) { /* Add dummy Ethernet header to non-L2 packet if it's coming from a * L3 port. So all packets will be L2 packets for lookup. * The dl_type has already been set from the packet_type. */ diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index cc325ddd7..d19d486d9 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -2893,7 +2893,7 @@ bundle_update(struct ofbundle *bundle) bundle->floodable = true; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD - || netdev_vport_is_layer3(port->up.netdev) + || netdev_get_pt_mode(port->up.netdev) == NETDEV_PT_LEGACY_L3 || (bundle->ofproto->stp && !stp_forward_in_state(port->stp_state)) || (bundle->ofproto->rstp && !rstp_forward_in_state(port->rstp_state))) { bundle->floodable = false; @@ -2942,7 +2942,7 @@ bundle_add_port(struct ofbundle *bundle, ofp_port_t ofp_port, port->bundle = bundle; ovs_list_push_back(&bundle->ports, &port->bundle_node); if (port->up.pp.config & OFPUTIL_PC_NO_FLOOD - || netdev_vport_is_layer3(port->up.netdev) + || netdev_get_pt_mode(port->up.netdev) == NETDEV_PT_LEGACY_L3 || (bundle->ofproto->stp && !stp_forward_in_state(port->stp_state)) || (bundle->ofproto->rstp && !rstp_forward_in_state(port->rstp_state))) { bundle->floodable = false; diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c index fa99b3102..c6856a09e 100644 --- a/ofproto/tunnel.c +++ b/ofproto/tunnel.c @@ -50,7 +50,7 @@ struct tnl_match { bool in_key_flow; bool ip_src_flow; bool ip_dst_flow; - bool is_layer3; + enum netdev_pt_mode pt_mode; }; struct tnl_port { @@ -164,7 +164,7 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev, tnl_port->match.ip_dst_flow = cfg->ip_dst_flow; tnl_port->match.in_key_flow = cfg->in_key_flow; tnl_port->match.odp_port = odp_port; - tnl_port->match.is_layer3 = netdev_vport_is_layer3(netdev); + tnl_port->match.pt_mode = netdev_get_pt_mode(netdev); map = tnl_match_map(&tnl_port->match); existing_port = tnl_find_exact(&tnl_port->match, *map); @@ -564,8 +564,20 @@ tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) match.in_key_flow = in_key_flow; match.ip_dst_flow = ip_dst_flow; match.ip_src_flow = ip_src == IP_SRC_FLOW; - match.is_layer3 = flow->packet_type != htonl(PT_ETH); + /* Look for a legacy L2 or L3 tunnel port first. */ + if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) { + match.pt_mode = NETDEV_PT_LEGACY_L3; + } else { + match.pt_mode = NETDEV_PT_LEGACY_L2; + } + tnl_port = tnl_find_exact(&match, map); + if (tnl_port) { + return tnl_port; + } + + /* Then check for a packet type aware port. */ + match.pt_mode = NETDEV_PT_AWARE; tnl_port = tnl_find_exact(&match, map); if (tnl_port) { return tnl_port; @@ -614,11 +626,12 @@ tnl_match_fmt(const struct tnl_match *match, struct ds *ds) } else { ds_put_format(ds, ", key=%#"PRIx64, ntohll(match->in_key)); } - if (match->is_layer3) { - ds_put_cstr(ds, ", layer3"); - } - ds_put_format(ds, ", dp port=%"PRIu32, match->odp_port); + const char *pt_mode + = (match->pt_mode == NETDEV_PT_LEGACY_L2 ? "legacy_l2" + : match->pt_mode == NETDEV_PT_LEGACY_L3 ? "legacy_l3" + : "ptap"); + ds_put_format(ds, ", %s, dp port=%"PRIu32, pt_mode, match->odp_port); } static void diff --git a/tests/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at index 228a9af43..9ff7c897c 100644 --- a/tests/tunnel-push-pop-ipv6.at +++ b/tests/tunnel-push-pop-ipv6.at @@ -13,7 +13,7 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \ -- add-port int-br t4 -- set Interface t4 type=geneve \ options:remote_ip=flow options:key=123 ofport_request=5\ -- add-port int-br t5 -- set Interface t5 type=gre \ - options:remote_ip=2001:cafe::92 options:key=455 options:layer3=true ofport_request=6\ + options:remote_ip=2001:cafe::92 options:key=455 options:packet_type=legacy_l3 ofport_request=6\ ], [0]) AT_CHECK([ovs-appctl dpif/show], [0], [dnl @@ -27,7 +27,7 @@ dummy@ovs-dummy: hit:0 missed:0 t2 2/4789: (vxlan: key=123, remote_ip=2001:cafe::92) t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=2001:cafe::93) t4 5/6081: (geneve: key=123, remote_ip=flow) - t5 6/3: (gre: key=455, layer3=true, remote_ip=2001:cafe::92) + t5 6/3: (gre: key=455, packet_type=legacy_l3, remote_ip=2001:cafe::92) ]) dnl First setup dummy interface IP address, then add the route diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at index 5a2c42383..c376e719e 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at @@ -15,7 +15,7 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \ -- add-port int-br t5 -- set Interface t5 type=geneve \ options:remote_ip=1.1.2.93 options:out_key=flow options:egress_pkt_mark=1234 ofport_request=6\ -- add-port int-br t6 -- set Interface t6 type=gre \ - options:remote_ip=1.1.2.92 options:key=456 options:layer3=true ofport_request=7\ + options:remote_ip=1.1.2.92 options:key=456 options:packet_type=legacy_l3 ofport_request=7\ -- add-port int-br t7 -- set Interface t7 type=vxlan \ options:remote_ip=1.1.2.92 options:key=345 options:exts=gpe ofport_request=8\ ], [0]) @@ -32,7 +32,7 @@ dummy@ovs-dummy: hit:0 missed:0 t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=1.1.2.93) t4 5/6081: (geneve: key=123, remote_ip=flow) t5 6/6081: (geneve: egress_pkt_mark=1234, out_key=flow, remote_ip=1.1.2.93) - t6 7/3: (gre: key=456, layer3=true, remote_ip=1.1.2.92) + t6 7/3: (gre: key=456, packet_type=legacy_l3, remote_ip=1.1.2.92) t7 8/4789: (vxlan: key=345, remote_ip=1.1.2.92) ]) diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 500f05a24..abfe3976b 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2402,6 +2402,31 @@ including tunnel monitoring. + + +

+ A LISP tunnel sends and receives only IPv4 and IPv6 packets. This + option controls what how the tunnel represents the packets that it + sends and receives: +

+ +
    +
  • + By default, or if this option is legacy_l3, the + tunnel represents packets as Ethernet frames for compatibility + with legacy OpenFlow controllers that expect this behavior. +
  • +
  • + If this option is ptap, the tunnel represents + packets using the packet_type mechanism introduced + in OpenFlow 1.5. +
  • +
+
+
+ @@ -2421,21 +2446,42 @@ gpe: Support for Generic Protocol Encapsulation in accordance with IETF draft https://tools.ietf.org/html/draft-ietf-nvo3-vxlan-gpe. + Without this option, a VXLAN packet always encapsulates an + Ethernet frame. With this option, an VXLAN packet may also + encapsulate an IPv4, IPv6, NSH, or MPLS packet. - +

- By default, or if set to false, the tunnel carries L2 packets (with - an Ethernet header). If set to true, the tunnel carries L3 packets - (without an Ethernet header present). + This option controls what types of packets the tunnel sends and + receives and how it represents them:

-

- To set this option to true, the gpe extension must - also be enabled in . -

+
    +
  • + By default, or if this option is legacy_l2, the + tunnel sends and receives only Ethernet frames. +
  • +
  • + If this option is legacy_l3, the tunnel sends and + receives only non-Ethernet (L3) packet, but the packets are + represented as Ethernet frames for compatibility with legacy + OpenFlow controllers that expect this behavior. This requires + enabling gpe in . +
  • +
  • + If this option is ptap, Open vSwitch represents + packets in the tunnel using the packet_type + mechanism introduced in OpenFlow 1.5. This mechanism supports + any kind of packet, but actually sending and receiving + non-Ethernet packets requires additionally enabling + gpe in . +
  • +
@@ -2444,18 +2490,32 @@ gre interfaces support these options.

- +

- By default, or if set to false, the tunnel carries L2 packets (with - an Ethernet header). If set to true, the tunnel carries L3 packets - (without an Ethernet header present). + This option controls what types of packets the tunnel sends and + receives and how it represents them:

-

- A single GRE tunnel cannot carry both L2 and L3 packets, but the - same effect can be realized by creating two tunnels with different - layer3 settings and otherwise the same configuration. -

+
    +
  • + By default, or if this option is legacy_l2, the + tunnel sends and receives only Ethernet frames. +
  • +
  • + If this option is legacy_l3, the tunnel sends and + receives only non-Ethernet (L3) packet, but the packets are + represented as Ethernet frames for compatibility with legacy + OpenFlow controllers that expect this behavior. +
  • +
  • + If this option is ptap, the tunnel sends and + receives any kind of packet. Open vSwitch represents packets in + the tunnel using the packet_type mechanism + introduced in OpenFlow 1.5. +
  • +