2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 22:35:15 +00:00

userspace: Switching of L3 packets in L2 pipeline

Ports have a new layer3 attribute if they send/receive L3 packets.

The packet_type included in structs dp_packet and flow is considered in
ofproto-dpif. The classical L2 match fields (dl_src, dl_dst, dl_type, and
vlan_tci, vlan_vid, vlan_pcp) now have Ethernet as pre-requisite.

A dummy ethernet header is pushed to L3 packets received from L3 ports
before the the pipeline processing starts. The ethernet header is popped
before sending a packet to a L3 port.

For datapath ports that can receive L2 or L3 packets, the packet_type
becomes part of the flow key for datapath flows and is handled
appropriately in dpif-netdev.

In the 'else' branch in flow_put_on_pmd() function, the additional check
flow_equal(&match.flow, &netdev_flow->flow) was removed, as a) the dpcls
lookup is sufficient to uniquely identify a flow and b) it caused false
negatives because the flow in netdev->flow may not properly masked.

In dpif_netdev_flow_put() we now use the same method for constructing the
netdev_flow_key as the one used when adding the flow to the dplcs to make sure
these always match. The function netdev_flow_key_from_flow() used so far was
not only inefficient but sometimes caused mismatches and subsequent flow
update failures.

The kernel datapath does not support the packet_type match field.
Instead it encodes the packet type implictly by the presence or absence of
the Ethernet attribute in the flow key and mask.
This patch filters the PACKET_TYPE attribute out of netlink flow key and
mask to be sent to the kernel datapath.

Signed-off-by: Lorand Jakab <lojakab@cisco.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: Jiri Benc <jbenc@redhat.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
Co-authored-by: Zoltan Balogh <zoltan.balogh@ericsson.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Jan Scheurich
2017-06-02 16:16:17 +00:00
committed by Ben Pfaff
parent 5978c2e315
commit beb75a40fd
23 changed files with 337 additions and 119 deletions

View File

@@ -173,6 +173,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
case OVS_KEY_ATTR_MPLS: return "mpls";
case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
case OVS_KEY_ATTR_PACKET_TYPE: return "packet_type";
case __OVS_KEY_ATTR_MAX:
default:
@@ -1947,6 +1948,7 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
[OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) },
[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = { .len = sizeof(struct ovs_key_ct_tuple_ipv4) },
[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = { .len = sizeof(struct ovs_key_ct_tuple_ipv6) },
[OVS_KEY_ATTR_PACKET_TYPE] = { .len = 4 },
};
/* Returns the correct length of the payload for a flow key attribute of the
@@ -2951,6 +2953,27 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
}
break;
case OVS_KEY_ATTR_PACKET_TYPE: {
ovs_be32 packet_type = nl_attr_get_be32(a);
uint16_t ns = pt_ns(packet_type);
uint16_t ns_type = pt_ns_type(packet_type);
if (!is_exact) {
ovs_be32 mask = nl_attr_get_be32(ma);
uint16_t mask_ns_type = pt_ns_type(mask);
if (mask == 0) {
ds_put_format(ds, "ns=%u,id=*", ns);
} else {
ds_put_format(ds, "ns=%u,id=%#"PRIx16"/%#"PRIx16,
ns, ns_type, mask_ns_type);
}
} else {
ds_put_format(ds, "ns=%u,id=%#"PRIx16, ns, ns_type);
}
break;
}
case OVS_KEY_ATTR_ETHERNET: {
const struct ovs_key_ethernet *mask = ma ? nl_attr_get(ma) : NULL;
const struct ovs_key_ethernet *key = nl_attr_get(a);
@@ -4417,7 +4440,8 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
size_t encap[FLOW_MAX_VLAN_HEADERS] = {0};
size_t max_vlans;
const struct flow *flow = parms->flow;
const struct flow *data = export_mask ? parms->mask : parms->flow;
const struct flow *mask = parms->mask;
const struct flow *data = export_mask ? mask : flow;
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
@@ -4476,36 +4500,44 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, data->in_port.odp_port);
}
eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
sizeof *eth_key);
get_ethernet_key(data, eth_key);
if (export_mask || flow->packet_type != htonl(PT_ETH)) {
nl_msg_put_be32(buf, OVS_KEY_ATTR_PACKET_TYPE, data->packet_type);
}
if (OVS_UNLIKELY(parms->probe)) {
max_vlans = FLOW_MAX_VLAN_HEADERS;
} else {
max_vlans = MIN(parms->support.max_vlan_headers, flow_vlan_limit);
}
for (int encaps = 0; encaps < max_vlans; encaps++) {
ovs_be16 tpid = flow->vlans[encaps].tpid;
if (flow->vlans[encaps].tci == htons(0)) {
if (eth_type_vlan(flow->dl_type)) {
/* If VLAN was truncated the tpid is in dl_type */
tpid = flow->dl_type;
} else {
break;
/* Conditionally add L2 attributes for Ethernet packets */
if (flow->packet_type == htonl(PT_ETH)) {
eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
sizeof *eth_key);
get_ethernet_key(data, eth_key);
for (int encaps = 0; encaps < max_vlans; encaps++) {
ovs_be16 tpid = flow->vlans[encaps].tpid;
if (flow->vlans[encaps].tci == htons(0)) {
if (eth_type_vlan(flow->dl_type)) {
/* If VLAN was truncated the tpid is in dl_type */
tpid = flow->dl_type;
} else {
break;
}
}
}
if (export_mask) {
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
} else {
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, tpid);
}
nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlans[encaps].tci);
encap[encaps] = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
if (flow->vlans[encaps].tci == htons(0)) {
goto unencap;
if (export_mask) {
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
} else {
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, tpid);
}
nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlans[encaps].tci);
encap[encaps] = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
if (flow->vlans[encaps].tci == htons(0)) {
goto unencap;
}
}
}
@@ -4658,8 +4690,10 @@ odp_flow_key_from_mask(const struct odp_flow_key_parms *parms,
/* Generate ODP flow key from the given packet metadata */
void
odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
odp_key_from_dp_packet(struct ofpbuf *buf, const struct dp_packet *packet)
{
const struct pkt_metadata *md = &packet->md;
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
if (flow_tnl_dst_is_set(&md->tunnel)) {
@@ -4701,18 +4735,29 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
if (md->in_port.odp_port != ODPP_NONE) {
nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port);
}
/* Add OVS_KEY_ATTR_ETHERNET for non-Ethernet packets */
if (pt_ns(packet->packet_type) == OFPHTN_ETHERTYPE) {
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE,
pt_ns_type_be(packet->packet_type));
}
}
/* Generate packet metadata from the given ODP flow key. */
void
odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
struct pkt_metadata *md)
odp_key_to_dp_packet(const struct nlattr *key, size_t key_len,
struct dp_packet *packet)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
const struct nlattr *nla;
struct pkt_metadata *md = &packet->md;
ovs_be32 packet_type = htonl(PT_UNKNOWN);
ovs_be16 ethertype = 0;
size_t left;
uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY |
1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
1u << OVS_KEY_ATTR_IN_PORT;
1u << OVS_KEY_ATTR_IN_PORT | 1u << OVS_KEY_ATTR_ETHERTYPE |
1u << OVS_KEY_ATTR_ETHERNET;
pkt_metadata_init(md, ODPP_NONE);
@@ -4792,14 +4837,32 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
md->in_port.odp_port = nl_attr_get_odp_port(nla);
wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT);
break;
case OVS_KEY_ATTR_ETHERNET:
/* Presence of OVS_KEY_ATTR_ETHERNET indicates Ethernet packet. */
packet_type = htonl(PT_ETH);
wanted_attrs &= ~(1u << OVS_KEY_ATTR_ETHERNET);
break;
case OVS_KEY_ATTR_ETHERTYPE:
ethertype = nl_attr_get_be16(nla);
wanted_attrs &= ~(1u << OVS_KEY_ATTR_ETHERTYPE);
break;
default:
break;
}
if (!wanted_attrs) {
return; /* Have everything. */
break; /* Have everything. */
}
}
if (packet_type == htonl(PT_ETH)) {
packet->packet_type = htonl(PT_ETH);
} else if (packet_type == htonl(PT_UNKNOWN) && ethertype != 0) {
packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
ntohs(ethertype));
} else {
VLOG_ERR_RL(&rl, "Packet without ETHERTYPE. Unknown packet_type.");
}
}
uint32_t
@@ -4963,7 +5026,19 @@ parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
*expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE;
} else {
if (!is_mask) {
flow->dl_type = htons(FLOW_DL_TYPE_NONE);
/* Default ethertype for well-known L3 packets. */
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
flow->dl_type = htons(ETH_TYPE_IP);
} else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
flow->dl_type = htons(ETH_TYPE_IPV6);
} else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
flow->dl_type = htons(ETH_TYPE_MPLS);
} else {
flow->dl_type = htons(FLOW_DL_TYPE_NONE);
}
} else if (src_flow->packet_type != htonl(PT_ETH)) {
/* dl_type is mandatory for non-Ethernet packets */
flow->dl_type = htons(0xffff);
} else if (ntohs(src_flow->dl_type) < ETH_TYPE_MIN) {
/* See comments in odp_flow_key_from_flow__(). */
VLOG_ERR_RL(&rl, "mask expected for non-Ethernet II frame");
@@ -5405,23 +5480,37 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
flow->in_port.odp_port = ODPP_NONE;
}
/* Ethernet header. */
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_PACKET_TYPE)) {
flow->packet_type
= nl_attr_get_be32(attrs[OVS_KEY_ATTR_PACKET_TYPE]);
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PACKET_TYPE;
} else if (!is_mask) {
flow->packet_type = htonl(PT_ETH);
}
/* Check for Ethernet header. */
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
const struct ovs_key_ethernet *eth_key;
eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
put_ethernet_key(eth_key, flow);
if (is_mask) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
if (!is_mask) {
flow->packet_type = htonl(PT_ETH);
}
}
if (!is_mask) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
}
else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) {
ovs_be16 ethertype = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]);
if (!is_mask) {
flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
ntohs(ethertype));
}
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE;
}
/* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow,
src_flow)) {
src_flow)) {
return ODP_FIT_ERROR;
}
@@ -5711,6 +5800,29 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
}
}
static void
commit_ether_action(const struct flow *flow, struct flow *base_flow,
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
bool use_masked)
{
if (flow->packet_type == htonl(PT_ETH)) {
if (base_flow->packet_type != htonl(PT_ETH)) {
odp_put_push_eth_action(odp_actions, &flow->dl_src, &flow->dl_dst);
base_flow->packet_type = flow->packet_type;
base_flow->dl_src = flow->dl_src;
base_flow->dl_dst = flow->dl_dst;
} else {
commit_set_ether_addr_action(flow, base_flow, odp_actions, wc,
use_masked);
}
} else {
if (base_flow->packet_type == htonl(PT_ETH)) {
odp_put_pop_eth_action(odp_actions);
base_flow->packet_type = flow->packet_type;
}
}
}
static void
commit_vlan_action(const struct flow* flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
@@ -6165,7 +6277,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
enum slow_path_reason slow1, slow2;
bool mpls_done = false;
commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
commit_ether_action(flow, base, odp_actions, wc, use_masked);
/* Make packet a non-MPLS packet before committing L3/4 actions,
* which would otherwise do nothing. */
if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {