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:
182
lib/odp-util.c
182
lib/odp-util.c
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user