[ovs-dev] [PATCH v2 2/3] userspace: add layer 3 flow and switching support
Lorand Jakab
lojakab at cisco.com
Tue Dec 24 14:02:35 UTC 2013
This commit relaxes the assumption that all packets have an Ethernet
header, and adds support for layer 3 flows. For each packet received on
the Linux kernel datapath the l2 and l3 members of struct ofpbuf are
intialized appropriately, and some functions now expect this (notably
flow_extract()), in order to differentiate between layer 2 and layer 3
packets. struct flow has now a new 'base_layer' member, because we
cannot assume that a flow has no Ethernet header when eth_src and
eth_dst are 0. For layer 3 packets, the protocol type is still stored
in the eth_type member.
Switching L2->L3 and L3->L2 are both implemented by adding the pop_eth
and push_eth actions respectively when a transition is detected. The
push_eth action puts 0s on both source and destination MACs. These
addresses can be modified with mod_dl_dst and mod_dl_src actions.
Added new prerequisite MFP_ETHERNET for fields MFF_ETH_SRC and
MFF_ETH_DST to avoid detecting layer 2 flows with all-zero MAC addresses
as layer 3.
Signed-off-by: Lorand Jakab <lojakab at cisco.com>
---
lib/bfd.c | 1 +
lib/dpif-linux.c | 9 +++++
lib/dpif.c | 6 ++--
lib/flow.c | 85 ++++++++++++++++++++++++++++----------------
lib/flow.h | 15 ++++++--
lib/match.c | 11 +++---
lib/meta-flow.c | 9 +++--
lib/meta-flow.h | 1 +
lib/netdev.c | 7 ++++
lib/nx-match.c | 2 +-
lib/odp-util.c | 16 ++++++---
lib/ofp-print.c | 15 +++++---
lib/ofp-print.h | 3 +-
lib/ofp-util.c | 2 +-
lib/packets.c | 2 ++
lib/pcap-file.c | 1 +
ofproto/ofproto-dpif-xlate.c | 19 ++++++++--
ofproto/ofproto-dpif-xlate.h | 3 +-
ofproto/ofproto-dpif.c | 3 +-
ofproto/ofproto.c | 1 +
tests/ofproto-dpif.at | 6 ++--
tests/vlan-splinters.at | 4 +--
22 files changed, 158 insertions(+), 63 deletions(-)
diff --git a/lib/bfd.c b/lib/bfd.c
index a4179f8..900f23f 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -561,6 +561,7 @@ bfd_put_packet(struct bfd *bfd, struct ofpbuf *p,
ovs_assert(!(bfd->flags & FLAG_POLL) || !(bfd->flags & FLAG_FINAL));
ofpbuf_reserve(p, 2); /* Properly align after the ethernet header. */
+ p->l2 = p->data;
eth = ofpbuf_put_uninit(p, sizeof *eth);
memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
memcpy(eth->eth_dst, bfd->eth_dst, ETH_ADDR_LEN);
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 482ba77..fe411ef 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -1479,6 +1479,15 @@ parse_odp_packet(struct ofpbuf *buf, struct dpif_upcall *upcall,
upcall->packet.data = (char *)upcall->packet.data + sizeof(struct nlattr);
upcall->packet.size = nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]);
+ /* Set the correct layer based on the presence of OVS_KEY_ATTR_ETHERNET */
+ if (nl_attr_find__(upcall->key, upcall->key_len, OVS_KEY_ATTR_ETHERNET)) {
+ upcall->packet.l2 = upcall->packet.data;
+ upcall->packet.l3 = NULL;
+ } else {
+ upcall->packet.l2 = NULL;
+ upcall->packet.l3 = upcall->packet.data;
+ }
+
*dp_ifindex = ovs_header->dp_ifindex;
return 0;
diff --git a/lib/dpif.c b/lib/dpif.c
index 6b519b8..8c01055 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1333,7 +1333,8 @@ dpif_recv(struct dpif *dpif, struct dpif_upcall *upcall, struct ofpbuf *buf)
char *packet;
packet = ofp_packet_to_string(upcall->packet.data,
- upcall->packet.size);
+ upcall->packet.size,
+ upcall->packet.l3);
ds_init(&flow);
odp_flow_key_format(upcall->key, upcall->key_len, &flow);
@@ -1535,7 +1536,8 @@ log_execute_message(struct dpif *dpif, const struct dpif_execute *execute,
char *packet;
packet = ofp_packet_to_string(execute->packet->data,
- execute->packet->size);
+ execute->packet->size,
+ execute->packet->l3);
ds_put_format(&ds, "%s: execute ", dpif_name(dpif));
format_odp_actions(&ds, execute->actions, execute->actions_len);
if (error) {
diff --git a/lib/flow.c b/lib/flow.c
index f1d2cad..673a6cd 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -49,6 +49,21 @@ const uint8_t flow_segment_u32s[4] = {
FLOW_U32S
};
+static ovs_be16
+get_l3_eth_type(struct ofpbuf *packet)
+{
+ struct ip_header *ip = packet->l3;
+ int ip_ver = IP_VER(ip->ip_ihl_ver);
+ switch (ip_ver) {
+ case 4:
+ return htons(ETH_TYPE_IP);
+ case 6:
+ return htons(ETH_TYPE_IPV6);
+ default:
+ return 0;
+ }
+}
+
static struct arp_eth_header *
pull_arp(struct ofpbuf *packet)
{
@@ -363,17 +378,14 @@ invalid:
}
/* Initializes 'flow' members from 'packet', 'skb_priority', 'tnl', and
- * 'in_port'.
+ * 'in_port'. Expects at least one of packet->l2 or packet->l3 to be set for
+ * indicating packet type. For layer 3 packets, packet->l2 must be NULL and
+ * packet->l3 set to the beginning of the layer 3 header.
*
* Initializes 'packet' header pointers as follows:
*
- * - packet->l2 to the start of the Ethernet header.
- *
- * - packet->l2_5 to the start of the MPLS shim header.
- *
- * - packet->l3 to just past the Ethernet header, or just past the
- * vlan_header if one is present, to the first byte of the payload of the
- * Ethernet frame.
+ * - packet->l2_5 to the start of the MPLS shim header, if one is present
+ * and packet->l2 is non-NULL
*
* - packet->l4 to just past the IPv4 header, if one is present and has a
* correct length, and otherwise NULL.
@@ -391,6 +403,8 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_mark,
COVERAGE_INC(flow_extract);
+ ovs_assert(packet->l2 != NULL || packet->l3 != NULL);
+
memset(flow, 0, sizeof *flow);
if (tnl) {
@@ -403,36 +417,47 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, uint32_t pkt_mark,
flow->skb_priority = skb_priority;
flow->pkt_mark = pkt_mark;
- packet->l2 = b.data;
packet->l2_5 = NULL;
- packet->l3 = NULL;
packet->l4 = NULL;
packet->l7 = NULL;
- if (b.size < sizeof *eth) {
- return;
- }
+ if (packet->l2) {
+ ovs_assert(packet->l2 == b.data);
+ packet->l3 = NULL;
+ flow->base_layer = LAYER_2;
- /* Link layer. */
- eth = b.data;
- memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN);
- memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN);
+ if (b.size < sizeof *eth) {
+ return;
+ }
- /* dl_type, vlan_tci. */
- ofpbuf_pull(&b, ETH_ADDR_LEN * 2);
- if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
- parse_vlan(&b, flow);
- }
- flow->dl_type = parse_ethertype(&b);
+ /* Link layer. */
+ eth = b.data;
+ memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN);
+ memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN);
- /* Parse mpls, copy l3 ttl. */
- if (eth_type_mpls(flow->dl_type)) {
- packet->l2_5 = b.data;
- parse_mpls(&b, flow);
+ /* dl_type, vlan_tci. */
+ ofpbuf_pull(&b, ETH_ADDR_LEN * 2);
+ if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
+ parse_vlan(&b, flow);
+ }
+ flow->dl_type = parse_ethertype(&b);
+
+ /* Parse mpls, copy l3 ttl. */
+ if (eth_type_mpls(flow->dl_type)) {
+ packet->l2_5 = b.data;
+ parse_mpls(&b, flow);
+ }
+
+ /* Network layer. */
+ packet->l3 = b.data;
+ } else {
+ ovs_assert(packet->l3 == b.data);
+ packet->l2 = NULL;
+ flow->base_layer = LAYER_3;
+ /* We assume L3 packets are either IPv4 or IPv6 */
+ flow->dl_type = get_l3_eth_type(packet);
}
- /* Network layer. */
- packet->l3 = b.data;
if (flow->dl_type == htons(ETH_TYPE_IP)) {
const struct ip_header *nh = pull_ip(&b);
if (nh) {
@@ -535,7 +560,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc)
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
fmd->tun_id = flow->tunnel.tun_id;
fmd->tun_src = flow->tunnel.ip_src;
diff --git a/lib/flow.h b/lib/flow.h
index 9e8549d..86dfd75 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -36,7 +36,7 @@ struct ofpbuf;
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 23
+#define FLOW_WC_SEQ 24
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -76,6 +76,10 @@ union flow_in_port {
odp_port_t odp_port;
};
+enum base_layer {
+ LAYER_2 = 0,
+ LAYER_3 = 1
+};
/*
* A flow in the network.
*
@@ -91,6 +95,10 @@ union flow_in_port {
* The fields are organized in four segments to facilitate staged lookup, where
* lower layer fields are first used to determine if the later fields need to
* be looked at. This enables better wildcarding for datapath flows.
+ *
+ * The starting layer is specified by 'base_layer'. When 'base_layer' is
+ * LAYER_3, dl_src, dl_tci, and vlan_tci are not used for matching. The
+ * dl_type field is still used to specify the layer 3 protocol.
*/
struct flow {
/* L1 */
@@ -100,6 +108,7 @@ struct flow {
uint32_t skb_priority; /* Packet priority for QoS. */
uint32_t pkt_mark; /* Packet mark. */
union flow_in_port in_port; /* Input port.*/
+ enum base_layer base_layer; /* Fields start at this layer */
/* L2 */
uint8_t dl_src[6]; /* Ethernet source address. */
@@ -134,8 +143,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(offsetof(struct flow, tp_dst) + 2
- == sizeof(struct flow_tnl) + 156
- && FLOW_WC_SEQ == 23);
+ == sizeof(struct flow_tnl) + 160
+ && FLOW_WC_SEQ == 24);
/* Incremental points at which flow classification may be performed in
* segments.
diff --git a/lib/match.c b/lib/match.c
index cc18a6a..9571510 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -81,9 +81,12 @@ match_wc_init(struct match *match, const struct flow *flow)
memset(&wc->masks.metadata, 0xff, sizeof wc->masks.metadata);
memset(&wc->masks.in_port, 0xff, sizeof wc->masks.in_port);
- memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
- memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
- memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+
+ if (flow->base_layer == LAYER_2) {
+ memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+ memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
+ memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
+ }
if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
@@ -856,7 +859,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%u,", priority);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 96e0efe..37e1848 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -236,7 +236,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
MF_FIELD_SIZES(mac),
MFM_FULLY,
MFS_ETHERNET,
- MFP_NONE,
+ MFP_ETHERNET,
true,
NXM_OF_ETH_SRC, "NXM_OF_ETH_SRC",
OXM_OF_ETH_SRC, "OXM_OF_ETH_SRC",
@@ -248,7 +248,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
MF_FIELD_SIZES(mac),
MFM_FULLY,
MFS_ETHERNET,
- MFP_NONE,
+ MFP_ETHERNET,
true,
NXM_OF_ETH_DST, "NXM_OF_ETH_DST",
OXM_OF_ETH_DST, "OXM_OF_ETH_DST",
@@ -1041,6 +1041,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
case MFP_NONE:
return true;
+ case MFP_ETHERNET:
+ return flow->base_layer == LAYER_2;
case MFP_ARP:
return (flow->dl_type == htons(ETH_TYPE_ARP) ||
flow->dl_type == htons(ETH_TYPE_RARP));
@@ -1118,6 +1120,9 @@ mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow *mask)
case MFP_VLAN_VID:
mask->vlan_tci |= htons(VLAN_CFI);
break;
+ case MFP_ETHERNET:
+ mask->base_layer = 0xff;
+ break;
case MFP_NONE:
break;
}
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index cf92556..426f527 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -185,6 +185,7 @@ enum OVS_PACKED_ENUM mf_prereqs {
MFP_NONE,
/* L2 requirements. */
+ MFP_ETHERNET,
MFP_ARP,
MFP_VLAN_VID,
MFP_IPV4,
diff --git a/lib/netdev.c b/lib/netdev.c
index 1bcd80f..62201de 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -558,6 +558,13 @@ netdev_rx_recv(struct netdev_rx *rx, struct ofpbuf *buffer)
if (buffer->size < ETH_TOTAL_MIN) {
ofpbuf_put_zeros(buffer, ETH_TOTAL_MIN - buffer->size);
}
+
+ /* We only receive layer 3 packets in netdev-vport, which doesn't
+ * implement an 'rx_recv' function and correctly setting the
+ * 'buffer->l3' pointer is dealt with elsewhere. We expect all
+ * packets received here to be layer 2.
+ */
+ buffer->l2 = buffer->data;
return 0;
} else {
return -retval;
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 983fd7d..18cb799 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -572,7 +572,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
/* Metadata. */
if (match->wc.masks.in_port.ofp_port) {
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 16f3e08..25c76bb 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -3171,12 +3171,10 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
memcpy(flow->dl_src, eth_key->eth_src, ETH_ADDR_LEN);
memcpy(flow->dl_dst, eth_key->eth_dst, ETH_ADDR_LEN);
- if (is_mask) {
- expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
- }
- }
- if (!is_mask) {
+ flow->base_layer = LAYER_2;
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
+ } else {
+ flow->base_layer = LAYER_3;
}
/* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
@@ -3373,6 +3371,14 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
return;
}
+ /* If we have a L3 --> L2 flow, the push_eth action takes care of setting
+ * the appropriate MAC source and destination addresses, no need to add a
+ * set action
+ */
+ if (base->base_layer == LAYER_3 && flow->base_layer == LAYER_2) {
+ return;
+ }
+
memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src);
memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst);
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 4c89b36..0bb179d 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -55,13 +55,20 @@ static void ofp_print_error(struct ds *, enum ofperr);
/* Returns a string that represents the contents of the Ethernet frame in the
* 'len' bytes starting at 'data'. The caller must free the returned string.*/
char *
-ofp_packet_to_string(const void *data, size_t len)
+ofp_packet_to_string(const void *data, size_t len, bool is_layer3)
{
struct ds ds = DS_EMPTY_INITIALIZER;
struct ofpbuf buf;
struct flow flow;
ofpbuf_use_const(&buf, data, len);
+
+ if (is_layer3) {
+ buf.l3 = buf.data;
+ } else {
+ buf.l2 = buf.data;
+ }
+
flow_extract(&buf, 0, 0, NULL, NULL, &flow);
flow_format(&ds, &flow);
@@ -157,7 +164,7 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
ds_put_char(string, '\n');
if (verbosity > 0) {
- char *packet = ofp_packet_to_string(pin.packet, pin.packet_len);
+ char *packet = ofp_packet_to_string(pin.packet, pin.packet_len, false);
ds_put_cstr(string, packet);
free(packet);
}
@@ -191,7 +198,7 @@ ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
if (po.buffer_id == UINT32_MAX) {
ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len);
if (verbosity > 0 && po.packet_len > 0) {
- char *packet = ofp_packet_to_string(po.packet, po.packet_len);
+ char *packet = ofp_packet_to_string(po.packet, po.packet_len, false);
ds_put_char(string, '\n');
ds_put_cstr(string, packet);
free(packet);
@@ -2827,5 +2834,5 @@ ofp_print(FILE *stream, const void *oh, size_t len, int verbosity)
void
ofp_print_packet(FILE *stream, const void *data, size_t len)
{
- print_and_free(stream, ofp_packet_to_string(data, len));
+ print_and_free(stream, ofp_packet_to_string(data, len, false));
}
diff --git a/lib/ofp-print.h b/lib/ofp-print.h
index 825e139..15aa196 100644
--- a/lib/ofp-print.h
+++ b/lib/ofp-print.h
@@ -21,6 +21,7 @@
#include <stdint.h>
#include <stdio.h>
+#include <stdbool.h>
struct ds;
struct ofp10_match;
@@ -39,7 +40,7 @@ void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity);
char *ofp_to_string(const void *, size_t, int verbosity);
char *ofp10_match_to_string(const struct ofp10_match *, int verbosity);
-char *ofp_packet_to_string(const void *data, size_t len);
+char *ofp_packet_to_string(const void *data, size_t len, bool is_layer3);
void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *);
void ofp_print_version(const struct ofp_header *, struct ds *);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index a0a372f..f0fceee 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
diff --git a/lib/packets.c b/lib/packets.c
index 6ebf728..7ab0480 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -417,6 +417,8 @@ eth_from_hex(const char *hex, struct ofpbuf **packetp)
return "Packet data too short for Ethernet";
}
+ packet->l2 = packet->data;
+
return NULL;
}
diff --git a/lib/pcap-file.c b/lib/pcap-file.c
index 4e3e7db..df8651d 100644
--- a/lib/pcap-file.c
+++ b/lib/pcap-file.c
@@ -185,6 +185,7 @@ pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when)
ofpbuf_delete(buf);
return error;
}
+ buf->l2 = buf->data;
*bufp = buf;
return 0;
}
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 848c778..024e566 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -135,6 +135,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. */
struct cfm *cfm; /* CFM handle or null. */
struct bfd *bfd; /* BFD handle or null. */
@@ -406,7 +407,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
const struct ofproto_port_queue *qdscp_list, size_t n_qdscp,
enum ofputil_port_config config,
enum ofputil_port_state state, bool is_tunnel,
- bool may_enable)
+ bool may_enable, bool is_layer3)
{
struct xport *xport = xport_lookup(ofport);
size_t i;
@@ -430,6 +431,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
xport->stp_port_no = stp_port_no;
xport->is_tunnel = is_tunnel;
xport->may_enable = may_enable;
+ xport->is_layer3 = is_layer3;
xport->odp_port = odp_port;
if (xport->netdev != netdev) {
@@ -1426,7 +1428,7 @@ xlate_normal(struct xlate_ctx *ctx)
}
/* Learn source MAC. */
- if (ctx->xin->may_learn) {
+ if (ctx->xin->may_learn && !(in_port->is_layer3)) {
update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle);
}
@@ -1679,6 +1681,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
struct flow_wildcards *wc = &ctx->xout->wc;
struct flow *flow = &ctx->xin->flow;
+ const struct xport *in_xport = get_ofp_port(ctx->xbridge, flow->in_port.ofp_port);
ovs_be16 flow_vlan_tci;
uint32_t flow_pkt_mark;
uint8_t flow_nw_tos;
@@ -1687,7 +1690,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24);
if (!xport) {
xlate_report(ctx, "Nonexistent output port");
@@ -1705,6 +1708,16 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
xport->xbundle);
}
+ if ((in_xport) && !(in_xport->is_layer3) && xport->is_layer3) {
+ odp_put_pop_eth_action(&ctx->xout->odp_actions);
+ }
+
+ if (flow->base_layer == LAYER_3 && !(xport->is_layer3)) {
+ flow->base_layer = LAYER_2;
+ odp_put_push_eth_action(&ctx->xout->odp_actions, flow->dl_src,
+ flow->dl_dst, flow->dl_type);
+ }
+
if (xport->peer) {
const struct xport *peer = xport->peer;
struct flow old_flow = ctx->xin->flow;
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 68076ca..6f097f9 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -147,7 +147,8 @@ void xlate_ofport_set(struct ofproto_dpif *, struct ofbundle *,
int stp_port_no, const struct ofproto_port_queue *qdscp,
size_t n_qdscp, enum ofputil_port_config,
enum ofputil_port_state, bool is_tunnel,
- bool may_enable) OVS_REQ_WRLOCK(xlate_rwlock);
+ bool may_enable, bool is_layer3)
+ OVS_REQ_WRLOCK(xlate_rwlock);
void xlate_ofport_remove(struct ofport_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
int xlate_receive(const struct dpif_backer *, struct ofpbuf *packet,
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index befa9f7..8bbd967 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -572,7 +572,8 @@ type_run(const char *type)
ofport->bfd, ofport->peer, stp_port,
ofport->qdscp, ofport->n_qdscp,
ofport->up.pp.config, ofport->up.pp.state,
- ofport->is_tunnel, ofport->may_enable);
+ ofport->is_tunnel, ofport->may_enable,
+ ofport->is_layer3);
}
ovs_rwlock_unlock(&xlate_rwlock);
}
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 75461e2..3cf6405 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2941,6 +2941,7 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
} else {
/* Ensure that the L3 header is 32-bit aligned. */
payload = ofpbuf_clone_data_with_headroom(po.packet, po.packet_len, 2);
+ payload->l2 = payload->data;
}
/* Verify actions against packet, then send packet if successful. */
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 7230854..2e548cc 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1355,15 +1355,15 @@ in_port=2 actions=output:1
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-odp_flow="in_port(p1)"
-br_flow="in_port=1"
+odp_flow="in_port(p1),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)"
+br_flow="in_port=1,dl_dst=00:00:00:00:00:00"
# Test command: ofproto/trace odp_flow with in_port as a name.
AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout])
AT_CHECK([tail -1 stdout], [0], [dnl
Datapath actions: 2
])
-odp_flow="in_port(1)"
+odp_flow="in_port(1),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)"
# Test command: ofproto/trace odp_flow
AT_CHECK([ovs-appctl ofproto/trace "$odp_flow"], [0], [stdout])
AT_CHECK([tail -1 stdout], [0], [dnl
diff --git a/tests/vlan-splinters.at b/tests/vlan-splinters.at
index 3cc6187..6ac2eaf 100644
--- a/tests/vlan-splinters.at
+++ b/tests/vlan-splinters.at
@@ -27,7 +27,7 @@ for args in '9 p2' '11 p3' '15 p4'; do
# Check that when a packet is received on $splinter_port, it is
# treated as if it had been received on p1 in the correct VLAN.
- AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($splinter_port)"],
+ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($splinter_port),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)"],
[0], [stdout])
AT_CHECK_UNQUOTED([sed -n '/^Flow/p; /^Datapath/p' stdout], [0], [dnl
Flow: metadata=0,in_port=$p1,dl_vlan=$vlan,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x05ff
@@ -36,7 +36,7 @@ Datapath actions: $access_port
# Check that when an OpenFlow action sends a packet to p1 on
# splintered VLAN $vlan, it is actually output to $splinter_port.
- AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($access_port)"],
+ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($access_port),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)"],
[0], [stdout])
AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $splinter_port
])
--
1.8.3.4 (Apple Git-47)
More information about the dev
mailing list