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 <[email protected]> --- 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) _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
