This patch has been superseded by patch series https://mail.openvswitch.org/pipermail/ovs-dev/2017-February/328391.html, specifically https://mail.openvswitch.org/pipermail/ovs-dev/2017-February/328396.html
Please exclude from review. Regards, Jan > -----Original Message----- > From: [email protected] > [mailto:[email protected]] On Behalf Of Yi Yang > Sent: Monday, 06 February, 2017 14:05 > To: [email protected] > Cc: Simon Horman <[email protected]>; Jiri Benc <[email protected]> > Subject: [ovs-dev] [PATCH v3 10/16] userspace: add layer 3 flow and switching > support > > 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, MFF_ETH_DST, > MFF_VLAN_TCI, MFF_DL_VLAN, MFF_VLAN_VID and MFF_DL_VLAN_PCP. > > L3 packets are differentiated from L2 packets by the > absence of the OVS_KEY_ATTR_ETHERNET attribute in the flow key. > > Signed-off-by: Lorand Jakab <[email protected]> > Signed-off-by: Simon Horman <[email protected]> > Signed-off-by: Jiri Benc <[email protected]> > Signed-off-by: Yi Yang <[email protected]> > --- > build-aux/extract-ofp-fields | 1 + > include/openvswitch/flow.h | 17 +++++- > include/openvswitch/match.h | 1 + > include/openvswitch/meta-flow.h | 9 +-- > include/openvswitch/ofp-print.h | 8 ++- > lib/dp-packet.h | 14 ++++- > lib/dpif-netdev.c | 4 +- > lib/dpif-netlink.c | 4 ++ > lib/dpif.c | 7 +-- > lib/flow.c | 114 ++++++++++++++++++++++++------------- > lib/flow.h | 11 ++++ > lib/match.c | 9 ++- > lib/meta-flow.c | 2 + > lib/netdev-bsd.c | 2 + > lib/netdev-dummy.c | 1 + > lib/netdev-linux.c | 2 + > lib/nx-match.c | 2 +- > lib/odp-util.c | 122 > +++++++++++++++++++++++++++------------- > lib/odp-util.h | 2 +- > lib/ofp-print.c | 27 +++++++-- > lib/ofp-util.c | 2 +- > lib/packets.c | 15 ++++- > lib/packets.h | 2 + > ofproto/ofproto-dpif-rid.h | 2 +- > ofproto/ofproto-dpif-xlate.c | 29 +++++++--- > ofproto/ofproto-dpif-xlate.h | 2 +- > ofproto/ofproto-dpif.c | 2 +- > tests/ofproto-dpif.at | 6 +- > tests/tunnel-push-pop-ipv6.at | 10 ++-- > tests/tunnel-push-pop.at | 10 ++-- > tests/tunnel.at | 10 ++-- > 31 files changed, 311 insertions(+), 138 deletions(-) > > diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields > index 40f1bb2..a16784a 100755 > --- a/build-aux/extract-ofp-fields > +++ b/build-aux/extract-ofp-fields > @@ -39,6 +39,7 @@ FORMATTING = {"decimal": ("MFS_DECIMAL", 1, > 8), > "TCP flags": ("MFS_TCP_FLAGS", 2, 2)} > > PREREQS = {"none": "MFP_NONE", > + "Ethernet": "MFP_ETHERNET", > "ARP": "MFP_ARP", > "VLAN VID": "MFP_VLAN_VID", > "IPv4": "MFP_IPV4", > diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h > index df80dfe..93ed37e 100644 > --- a/include/openvswitch/flow.h > +++ b/include/openvswitch/flow.h > @@ -23,7 +23,7 @@ > /* 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 36 > +#define FLOW_WC_SEQ 37 > > /* Number of Open vSwitch extension 32-bit registers. */ > #define FLOW_N_REGS 16 > @@ -58,6 +58,11 @@ BUILD_ASSERT_DECL(FLOW_TNL_F_OAM == NX_TUN_FLAG_OAM); > > const char *flow_tun_flag_to_string(uint32_t flags); > > +enum base_layer { > + LAYER_2 = 0, > + LAYER_3 = 1 > +}; > + > /* Maximum number of supported MPLS labels. */ > #define FLOW_MAX_MPLS_LABELS 3 > > @@ -77,6 +82,10 @@ const char *flow_tun_flag_to_string(uint32_t flags); > * 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. > + * > * NOTE: Order of the fields is significant, any change in the order must be > * reflected in miniflow_extract()! > */ > @@ -98,6 +107,8 @@ struct flow { > ovs_u128 ct_label; /* Connection label. */ > uint32_t conj_id; /* Conjunction ID. */ > ofp_port_t actset_output; /* Output port in action set. */ > + uint8_t base_layer; /* Fields start at this layer */ > + uint8_t pad2[7]; /* Pad to 64 bits. */ > > /* L2, Order the same as in the Ethernet header! (64-bit aligned) */ > struct eth_addr dl_dst; /* Ethernet destination address. */ > @@ -135,8 +146,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % > sizeof(uint64_t) == 0); > > /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ > BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) > - == sizeof(struct flow_tnl) + 248 > - && FLOW_WC_SEQ == 36); > + == sizeof(struct flow_tnl) + 256 > + && FLOW_WC_SEQ == 37); > > /* Incremental points at which flow classification may be performed in > * segments. > diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h > index 0b5f050..28cc3b2 100644 > --- a/include/openvswitch/match.h > +++ b/include/openvswitch/match.h > @@ -99,6 +99,7 @@ void match_set_ct_mark(struct match *, uint32_t ct_mark); > void match_set_ct_mark_masked(struct match *, uint32_t ct_mark, uint32_t > mask); > void match_set_ct_label(struct match *, ovs_u128 ct_label); > void match_set_ct_label_masked(struct match *, ovs_u128 ct_label, ovs_u128 > mask); > +void match_set_base_layer(struct match *, uint8_t base_layer); > void match_set_skb_priority(struct match *, uint32_t skb_priority); > void match_set_dl_type(struct match *, ovs_be16); > void match_set_dl_src(struct match *, const struct eth_addr ); > diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h > index d5c0971..d30ecc1 100644 > --- a/include/openvswitch/meta-flow.h > +++ b/include/openvswitch/meta-flow.h > @@ -919,7 +919,7 @@ enum OVS_PACKED_ENUM mf_field_id { > * Type: be16. > * Maskable: bitwise. > * Formatting: hexadecimal. > - * Prerequisites: none. > + * Prerequisites: Ethernet. > * Access: read/write. > * NXM: NXM_OF_VLAN_TCI(4) since v1.1. > * OXM: none. > @@ -935,7 +935,7 @@ enum OVS_PACKED_ENUM mf_field_id { > * Type: be16 (low 12 bits). > * Maskable: no. > * Formatting: decimal. > - * Prerequisites: none. > + * Prerequisites: Ethernet. > * Access: read/write. > * NXM: none. > * OXM: none. > @@ -953,7 +953,7 @@ enum OVS_PACKED_ENUM mf_field_id { > * Type: be16 (low 12 bits). > * Maskable: bitwise. > * Formatting: decimal. > - * Prerequisites: none. > + * Prerequisites: Ethernet. > * Access: read/write. > * NXM: none. > * OXM: OXM_OF_VLAN_VID(6) since OF1.2 and v1.7. > @@ -969,7 +969,7 @@ enum OVS_PACKED_ENUM mf_field_id { > * Type: u8 (low 3 bits). > * Maskable: no. > * Formatting: decimal. > - * Prerequisites: none. > + * Prerequisites: Ethernet. > * Access: read/write. > * NXM: none. > * OXM: none. > @@ -1676,6 +1676,7 @@ enum OVS_PACKED_ENUM mf_prereqs { > MFP_NONE, > > /* L2 requirements. */ > + MFP_ETHERNET, > MFP_ARP, > MFP_VLAN_VID, > MFP_IPV4, > diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h > index 58fd403..dce80a7 100644 > --- a/include/openvswitch/ofp-print.h > +++ b/include/openvswitch/ofp-print.h > @@ -21,6 +21,9 @@ > > #include <stdint.h> > #include <stdio.h> > +#include <stdbool.h> > + > +#include <openvswitch/types.h> > > struct ds; > struct ofp10_match; > @@ -29,6 +32,7 @@ struct ofp_header; > struct ofputil_flow_stats; > struct ofputil_table_features; > struct ofputil_table_stats; > +struct dp_packet; > > #ifdef __cplusplus > extern "C" { > @@ -41,7 +45,9 @@ 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, > + ovs_be16 packet_ethertype); > +char *ofp_dp_packet_to_string(const struct dp_packet *); > > 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/dp-packet.h b/lib/dp-packet.h > index 17b7026..c9a3ce1 100644 > --- a/lib/dp-packet.h > +++ b/lib/dp-packet.h > @@ -262,12 +262,20 @@ dp_packet_equal(const struct dp_packet *a, const struct > dp_packet *b) > !memcmp(dp_packet_data(a), dp_packet_data(b), dp_packet_size(a)); > } > > -/* Get the start of the Ethernet frame. 'l3_ofs' marks the end of the l2 > - * headers, so return NULL if it is not set. */ > +static inline bool > +dp_packet_is_l3(const struct dp_packet *b) > +{ > + return b->l3_ofs == 0 || b->l2_5_ofs == 0; > +} > + > +/* Get the start of the Ethernet frame. Return NULL if 'b' is an l3 packet > + * or if 'l3_ofs', which marks the end of the l2 headers, is not set. */ > static inline void * > dp_packet_l2(const struct dp_packet *b) > { > - return (b->l3_ofs != UINT16_MAX) ? dp_packet_data(b) : NULL; > + return (b->l3_ofs != UINT16_MAX && !dp_packet_is_l3(b)) > + ? dp_packet_data(b) > + : NULL; > } > > /* Resets all layer offsets. 'l3' offset must be set before 'l2' can be > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c > index 73927c4..6acc5b6 100644 > --- a/lib/dpif-netdev.c > +++ b/lib/dpif-netdev.c > @@ -3959,9 +3959,7 @@ dp_netdev_upcall(struct dp_netdev_pmd_thread *pmd, > struct dp_packet *packet_, > > ofpbuf_init(&key, 0); > odp_flow_key_from_flow(&odp_parms, &key); > - packet_str = ofp_packet_to_string(dp_packet_data(packet_), > - dp_packet_size(packet_)); > - > + packet_str = ofp_dp_packet_to_string(packet_); > odp_flow_key_format(key.data, key.size, &ds); > > VLOG_DBG("%s: %s upcall:\n%s\n%s", dp->name, > diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c > index 9762a87..3f3abef 100644 > --- a/lib/dpif-netlink.c > +++ b/lib/dpif-netlink.c > @@ -2034,6 +2034,10 @@ parse_odp_packet(const struct dpif_netlink *dpif, > struct ofpbuf *buf, > (char *)dp_packet_data(&upcall->packet) + sizeof(struct > nlattr)); > dp_packet_set_size(&upcall->packet, > nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET])); > > + if (!nl_attr_find__(upcall->key, upcall->key_len, > OVS_KEY_ATTR_ETHERNET)) { > + dp_packet_set_l3(&upcall->packet, dp_packet_data(&upcall->packet)); > + } > + > *dp_ifindex = ovs_header->dp_ifindex; > > return 0; > diff --git a/lib/dpif.c b/lib/dpif.c > index 1559ed4..7e96f5e 100644 > --- a/lib/dpif.c > +++ b/lib/dpif.c > @@ -1427,9 +1427,7 @@ dpif_print_packet(struct dpif *dpif, struct dpif_upcall > *upcall) > struct ds flow; > char *packet; > > - packet = ofp_packet_to_string(dp_packet_data(&upcall->packet), > - dp_packet_size(&upcall->packet)); > - > + packet = ofp_dp_packet_to_string(&upcall->packet); > ds_init(&flow); > odp_flow_key_format(upcall->key, upcall->key_len, &flow); > > @@ -1722,8 +1720,7 @@ log_execute_message(struct dpif *dpif, const struct > dpif_execute *execute, > struct ds ds = DS_EMPTY_INITIALIZER; > char *packet; > > - packet = ofp_packet_to_string(dp_packet_data(execute->packet), > - dp_packet_size(execute->packet)); > + packet = ofp_dp_packet_to_string(execute->packet); > ds_put_format(&ds, "%s: %sexecute ", > dpif_name(dpif), > (subexecute ? "sub-" > diff --git a/lib/flow.c b/lib/flow.c > index fb7bfeb..a1ad2af 100644 > --- a/lib/flow.c > +++ b/lib/flow.c > @@ -125,7 +125,7 @@ struct mf_ctx { > * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are > * defined as macros. */ > > -#if (FLOW_WC_SEQ != 36) > +#if (FLOW_WC_SEQ != 37) > #define MINIFLOW_ASSERT(X) ovs_assert(X) > BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime " > "assertions enabled. Consider updating FLOW_WC_SEQ after " > @@ -516,18 +516,18 @@ parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, > uint8_t *nw_proto, > return parse_ipv6_ext_hdrs__(datap, sizep, nw_proto, nw_frag); > } > > -/* Initializes 'flow' members from 'packet' and 'md' > +/* Initializes 'flow' members from 'packet' and 'md'. > + * Expects packet->l3_ofs to be set to 0 for layer 3 packets. > * > - * Initializes 'packet' header l2 pointer to the start of the Ethernet > - * header, and the layer offsets as follows: > + * Initializes the layer offsets as follows: > * > * - packet->l2_5_ofs to the start of the MPLS shim header, or UINT16_MAX > - * when there is no MPLS shim header. > + * when there is no MPLS shim header, or Ethernet header > * > - * - packet->l3_ofs 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. UINT16_MAX if the frame is too short to contain an > - * Ethernet header. > + * - packet->l3_ofs (if not 0) 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. UINT16_MAX if the frame is too short > to > + * contain an Ethernet header. > * > * - packet->l4_ofs to just past the IPv4 header, if one is present and > * has at least the content used for the fields of interest for the > flow, > @@ -558,9 +558,10 @@ miniflow_extract(struct dp_packet *packet, struct > miniflow *dst) > uint64_t *values = miniflow_values(dst); > struct mf_ctx mf = { FLOWMAP_EMPTY_INITIALIZER, values, > values + FLOW_U64S }; > - const char *l2; > + const char *frame; > ovs_be16 dl_type; > uint8_t nw_frag, nw_tos, nw_ttl, nw_proto; > + bool is_l3 = dp_packet_is_l3(packet); > > /* Metadata. */ > if (flow_tnl_dst_is_set(&md->tunnel)) { > @@ -608,23 +609,47 @@ miniflow_extract(struct dp_packet *packet, struct > miniflow *dst) > } > > /* Initialize packet's layer pointer and offsets. */ > - l2 = data; > + frame = data; > dp_packet_reset_offsets(packet); > > - /* Must have full Ethernet header to proceed. */ > - if (OVS_UNLIKELY(size < sizeof(struct eth_header))) { > - goto out; > - } else { > - ovs_be16 vlan_tci; > + if (!is_l3) { > + /* No need to store a zero value for base_layer in the miniflow > + * which would cost an extra word of storage. */ > + BUILD_ASSERT(LAYER_2 == 0); > > - /* Link layer. */ > - ASSERT_SEQUENTIAL(dl_dst, dl_src); > - miniflow_push_macs(mf, dl_dst, data); > - /* dl_type, vlan_tci. */ > - vlan_tci = parse_vlan(&data, &size); > - dl_type = parse_ethertype(&data, &size); > + /* Must have full Ethernet header to proceed. */ > + if (OVS_UNLIKELY(size < sizeof(struct eth_header))) { > + goto out; > + } else { > + ovs_be16 vlan_tci; > + > + /* Link layer. */ > + ASSERT_SEQUENTIAL(dl_dst, dl_src); > + miniflow_push_macs(mf, dl_dst, data); > + /* dl_type, vlan_tci. */ > + vlan_tci = parse_vlan(&data, &size); > + dl_type = parse_ethertype(&data, &size); > + miniflow_push_be16(mf, dl_type, dl_type); > + miniflow_push_be16(mf, vlan_tci, vlan_tci); > + } > + } else { > + packet->l3_ofs = 0; > + /* miniflow_pad_from_64(mf, base_layer); > + * ^^^^ This is weird. It was present in the original Simon's > + * patch but gives assertion failures on (offset % 8 != 0) after > + * rebase. If I'm reading the macros correctly, the call should not > + * be here now, as the structure fields shifted after rebase and > + * this one happens to start on 8 bytes boundary now. This means > + * that after another rebase this can break silently. The > + * miniflow_pad_from_64 call should really evaluate to nothing if > + * the offset is divisible by 8 instead of crashing. */ > + miniflow_push_uint8(mf, base_layer, LAYER_3); > + miniflow_pad_to_64(mf, base_layer); > + > + dl_type = packet->md.packet_ethertype; > + miniflow_pad_from_64(mf, dl_type); > miniflow_push_be16(mf, dl_type, dl_type); > - miniflow_push_be16(mf, vlan_tci, vlan_tci); > + miniflow_push_be16(mf, vlan_tci, 0); > } > > /* Parse mpls. */ > @@ -632,13 +657,13 @@ miniflow_extract(struct dp_packet *packet, struct > miniflow *dst) > int count; > const void *mpls = data; > > - packet->l2_5_ofs = (char *)data - l2; > + packet->l2_5_ofs = (char *)data - frame; > count = parse_mpls(&data, &size); > miniflow_push_words_32(mf, mpls_lse, mpls, count); > } > > /* Network layer. */ > - packet->l3_ofs = (char *)data - l2; > + packet->l3_ofs = (char *)data - frame; > > nw_frag = 0; > if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) { > @@ -755,7 +780,7 @@ miniflow_extract(struct dp_packet *packet, struct > miniflow *dst) > goto out; > } > > - packet->l4_ofs = (char *)data - l2; > + packet->l4_ofs = (char *)data - frame; > miniflow_push_be32(mf, nw_frag, > BYTES_TO_BE32(nw_frag, nw_tos, nw_ttl, nw_proto)); > > @@ -869,7 +894,7 @@ flow_get_metadata(const struct flow *flow, struct match > *flow_metadata) > { > int i; > > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > match_init_catchall(flow_metadata); > if (flow->tunnel.tun_id != htonll(0)) { > @@ -925,6 +950,10 @@ flow_get_metadata(const struct flow *flow, struct match > *flow_metadata) > if (!ovs_u128_is_zero(flow->ct_label)) { > match_set_ct_label(flow_metadata, flow->ct_label); > } > + > + if (flow->base_layer != LAYER_2) { > + match_set_base_layer(flow_metadata, flow->base_layer); > + } > } > > const char *ct_state_to_string(uint32_t state) > @@ -1275,7 +1304,7 @@ void flow_wildcards_init_for_packet(struct > flow_wildcards *wc, > memset(&wc->masks, 0x0, sizeof wc->masks); > > /* Update this function whenever struct flow changes. */ > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > if (flow_tnl_dst_is_set(&flow->tunnel)) { > if (flow->tunnel.flags & FLOW_TNL_F_KEY) { > @@ -1323,10 +1352,13 @@ void flow_wildcards_init_for_packet(struct > flow_wildcards *wc, > > /* actset_output wildcarded. */ > > - WC_MASK_FIELD(wc, dl_dst); > - WC_MASK_FIELD(wc, dl_src); > + if (flow->base_layer == LAYER_2) { > + WC_MASK_FIELD(wc, dl_dst); > + WC_MASK_FIELD(wc, dl_src); > + WC_MASK_FIELD(wc, vlan_tci); > + } > + > WC_MASK_FIELD(wc, dl_type); > - WC_MASK_FIELD(wc, vlan_tci); > > if (flow->dl_type == htons(ETH_TYPE_IP)) { > WC_MASK_FIELD(wc, nw_src); > @@ -1393,7 +1425,7 @@ void > flow_wc_map(const struct flow *flow, struct flowmap *map) > { > /* Update this function whenever struct flow changes. */ > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > flowmap_init(map); > > @@ -1416,15 +1448,18 @@ flow_wc_map(const struct flow *flow, struct flowmap > *map) > FLOWMAP_SET(map, recirc_id); > FLOWMAP_SET(map, dp_hash); > FLOWMAP_SET(map, in_port); > - FLOWMAP_SET(map, dl_dst); > - FLOWMAP_SET(map, dl_src); > FLOWMAP_SET(map, dl_type); > - FLOWMAP_SET(map, vlan_tci); > FLOWMAP_SET(map, ct_state); > FLOWMAP_SET(map, ct_zone); > FLOWMAP_SET(map, ct_mark); > FLOWMAP_SET(map, ct_label); > > + if (flow->base_layer == LAYER_2) { > + FLOWMAP_SET(map, dl_dst); > + FLOWMAP_SET(map, dl_src); > + FLOWMAP_SET(map, vlan_tci); > + } > + > /* Ethertype-dependent fields. */ > if (OVS_LIKELY(flow->dl_type == htons(ETH_TYPE_IP))) { > FLOWMAP_SET(map, nw_src); > @@ -1477,12 +1512,13 @@ void > flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc) > { > /* Update this function whenever struct flow changes. */ > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); > memset(&wc->masks.regs, 0, sizeof wc->masks.regs); > wc->masks.actset_output = 0; > wc->masks.conj_id = 0; > + wc->masks.base_layer = 0; > } > > /* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or > @@ -1621,7 +1657,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards > *wc, int idx, > uint32_t > miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis) > { > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > uint32_t hash = basis; > > if (flow) { > @@ -1668,7 +1704,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst); > uint32_t > flow_hash_5tuple(const struct flow *flow, uint32_t basis) > { > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > uint32_t hash = basis; > > if (flow) { > @@ -2136,7 +2172,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 > mpls_eth_type, > > if (clear_flow_L3) { > /* Clear all L3 and L4 fields and dp_hash. */ > - BUILD_ASSERT(FLOW_WC_SEQ == 36); > + BUILD_ASSERT(FLOW_WC_SEQ == 37); > memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, > sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); > flow->dp_hash = 0; > diff --git a/lib/flow.h b/lib/flow.h > index 62315bc..fa7f274 100644 > --- a/lib/flow.h > +++ b/lib/flow.h > @@ -875,6 +875,8 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const > struct flow *flow) > md->ct_zone = flow->ct_zone; > md->ct_mark = flow->ct_mark; > md->ct_label = flow->ct_label; > + md->base_layer = flow->base_layer; > + md->packet_ethertype = flow->dl_type; > } > > /* Often, during translation we need to read a value from a flow('FLOW') and > @@ -884,6 +886,15 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const > struct flow *flow) > #define FLOW_WC_GET_AND_MASK_WC(FLOW, WC, FIELD) \ > (((WC) ? WC_MASK_FIELD(WC, FIELD) : NULL), ((FLOW)->FIELD)) > > +static inline bool is_ethernet(const struct flow *flow, > + struct flow_wildcards *wc) > +{ > + if (wc) { > + WC_MASK_FIELD(wc, base_layer); > + } > + return flow->base_layer == LAYER_2; > +} > + > static inline bool is_vlan(const struct flow *flow, > struct flow_wildcards *wc) > { > diff --git a/lib/match.c b/lib/match.c > index 3fcaec5..c551e57 100644 > --- a/lib/match.c > +++ b/lib/match.c > @@ -384,6 +384,13 @@ match_set_ct_label_masked(struct match *match, ovs_u128 > value, ovs_u128 mask) > } > > void > +match_set_base_layer(struct match *match, uint8_t base_layer) > +{ > + match->flow.base_layer = base_layer; > + match->wc.masks.base_layer = UINT8_MAX; > +} > + > +void > match_set_dl_type(struct match *match, ovs_be16 dl_type) > { > match->wc.masks.dl_type = OVS_BE16_MAX; > @@ -1075,7 +1082,7 @@ match_format(const struct match *match, struct ds *s, > int priority) > > int i; > > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > if (priority != OFP_DEFAULT_PRIORITY) { > ds_put_format(s, "%spriority=%s%d,", > diff --git a/lib/meta-flow.c b/lib/meta-flow.c > index b92950b..7ff872b 100644 > --- a/lib/meta-flow.c > +++ b/lib/meta-flow.c > @@ -386,6 +386,8 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct > flow *flow, > switch (mf->prereqs) { > case MFP_NONE: > return true; > + case MFP_ETHERNET: > + return is_ethernet(flow, wc); > case MFP_ARP: > return (flow->dl_type == htons(ETH_TYPE_ARP) || > flow->dl_type == htons(ETH_TYPE_RARP)); > diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c > index 94c515d..7855463 100644 > --- a/lib/netdev-bsd.c > +++ b/lib/netdev-bsd.c > @@ -580,6 +580,7 @@ netdev_rxq_bsd_recv_pcap(struct netdev_rxq_bsd *rxq, > struct dp_packet *buffer) > > if (ret > 0) { > dp_packet_set_size(buffer, dp_packet_size(buffer) + arg.retval); > + dp_packet_reset_offsets(buffer); > return 0; > } > if (ret == -1) { > @@ -606,6 +607,7 @@ netdev_rxq_bsd_recv_tap(struct netdev_rxq_bsd *rxq, > struct dp_packet *buffer) > ssize_t retval = read(rxq->fd, dp_packet_data(buffer), size); > if (retval >= 0) { > dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); > + dp_packet_reset_offsets(buffer); > return 0; > } else if (errno != EINTR) { > if (errno != EAGAIN) { > diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c > index 0657434..51bcf03 100644 > --- a/lib/netdev-dummy.c > +++ b/lib/netdev-dummy.c > @@ -273,6 +273,7 @@ dummy_packet_stream_run(struct netdev_dummy *dev, struct > dummy_packet_stream *s) > dp_packet_clone(&s->rxbuf), 0); > dp_packet_clear(&s->rxbuf); > } > + dp_packet_reset_offsets(&s->rxbuf); > } else if (retval != -EAGAIN) { > error = (retval < 0 ? -retval > : dp_packet_size(&s->rxbuf) ? EPROTO > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c > index 9ff1333..4566201 100644 > --- a/lib/netdev-linux.c > +++ b/lib/netdev-linux.c > @@ -1050,6 +1050,7 @@ netdev_linux_rxq_recv_sock(int fd, struct dp_packet > *buffer) > } > > dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); > + dp_packet_reset_offsets(buffer); > > for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh, cmsg)) > { > const struct tpacket_auxdata *aux; > @@ -1096,6 +1097,7 @@ netdev_linux_rxq_recv_tap(int fd, struct dp_packet > *buffer) > } > > dp_packet_set_size(buffer, dp_packet_size(buffer) + retval); > + dp_packet_reset_offsets(buffer); > return 0; > } > > diff --git a/lib/nx-match.c b/lib/nx-match.c > index e9d649b..124e56b 100644 > --- a/lib/nx-match.c > +++ b/lib/nx-match.c > @@ -962,7 +962,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const > struct match *match, > int match_len; > int i; > > - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > /* Metadata. */ > if (match->wc.masks.dp_hash) { > diff --git a/lib/odp-util.c b/lib/odp-util.c > index 36e611b..3b039e8 100644 > --- a/lib/odp-util.c > +++ b/lib/odp-util.c > @@ -4364,7 +4364,7 @@ odp_flow_key_from_flow__(const struct > odp_flow_key_parms *parms, > bool export_mask, struct ofpbuf *buf) > { > struct ovs_key_ethernet *eth_key; > - size_t encap; > + size_t encap = 0; > const struct flow *flow = parms->flow; > const struct flow *data = export_mask ? parms->mask : parms->flow; > > @@ -4402,41 +4402,43 @@ 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 (flow->base_layer == LAYER_2) { > + eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET, > + sizeof *eth_key); > + get_ethernet_key(data, eth_key); > > - if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) > { > - 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, > htons(ETH_TYPE_VLAN)); > - } > - nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); > - encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); > - if (flow->vlan_tci == htons(0)) { > - goto unencap; > + if (flow->vlan_tci != htons(0) || > + flow->dl_type == htons(ETH_TYPE_VLAN)) { > + 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, > + htons(ETH_TYPE_VLAN)); > + } > + nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); > + encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); > + if (flow->vlan_tci == htons(0)) { > + goto unencap; > + } > } > - } else { > - encap = 0; > - } > > - if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { > - /* For backwards compatibility with kernels that don't support > - * wildcarding, the following convention is used to encode the > - * OVS_KEY_ATTR_ETHERTYPE for key and mask: > - * > - * key mask matches > - * -------- -------- ------- > - * >0x5ff 0xffff Specified Ethernet II Ethertype. > - * >0x5ff 0 Any Ethernet II or non-Ethernet II frame. > - * <none> 0xffff Any non-Ethernet II frame (except valid > - * 802.3 SNAP packet with valid eth_type). > - */ > - if (export_mask) { > - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); > + if (ntohs(flow->dl_type) < ETH_TYPE_MIN) { > + /* For backwards compatibility with kernels that don't support > + * wildcarding, the following convention is used to encode the > + * OVS_KEY_ATTR_ETHERTYPE for key and mask: > + * > + * key mask matches > + * -------- -------- ------- > + * >0x5ff 0xffff Specified Ethernet II Ethertype. > + * >0x5ff 0 Any Ethernet II or non-Ethernet II frame. > + * <none> 0xffff Any non-Ethernet II frame (except valid > + * 802.3 SNAP packet with valid eth_type). > + */ > + if (export_mask) { > + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); > + } > + goto unencap; > } > - goto unencap; > } > > nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type); > @@ -4594,6 +4596,10 @@ 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); > } > + > + if (md->base_layer == LAYER_3) { > + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, md->packet_ethertype); > + } > } > > /* Generate packet metadata from the given ODP flow key. */ > @@ -4602,10 +4608,13 @@ odp_key_to_pkt_metadata(const struct nlattr *key, > size_t key_len, > struct pkt_metadata *md) > { > const struct nlattr *nla; > + 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 | 1u << OVS_KEY_ATTR_IPV4 | > + 1u << OVS_KEY_ATTR_IPV6; > > pkt_metadata_init(md, ODPP_NONE); > > @@ -4670,14 +4679,38 @@ 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: > + 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; > + case OVS_KEY_ATTR_IPV4: > + wanted_attrs &= ~(1u << OVS_KEY_ATTR_IPV4); > + break; > + case OVS_KEY_ATTR_IPV6: > + wanted_attrs &= ~(1u << OVS_KEY_ATTR_IPV6); > + break; > default: > break; > } > > if (!wanted_attrs) { > - return; /* Have everything. */ > + break; /* Have everything. */ > } > } > + > + /* OVS_KEY_ATTR_ETHERTYPE present and OVS_KEY_ATTR_ETHERNET absent > + * indicates Layer 3. */ > + if (!(wanted_attrs & (1u << OVS_KEY_ATTR_ETHERTYPE)) && > + wanted_attrs & (1u << OVS_KEY_ATTR_ETHERNET)) { > + md->base_layer = LAYER_3; > + md->packet_ethertype = ethertype; > + } else { > + md->base_layer = LAYER_2; > + } > + > } > > uint32_t > @@ -4841,7 +4874,15 @@ 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); > + 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 { > + flow->dl_type = htons(FLOW_DL_TYPE_NONE); > + } > + } else if (src_flow->base_layer == LAYER_3) { > + 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"); > @@ -5255,12 +5296,13 @@ odp_flow_key_to_flow__(const struct nlattr *key, > size_t key_len, > > 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->base_layer = LAYER_2; > expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; > + } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) { > + flow->base_layer = LAYER_3; > + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE; > + } else if (is_mask && src_flow->base_layer == LAYER_3) { > + flow->base_layer = LAYER_3; > } > > /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */ > diff --git a/lib/odp-util.h b/lib/odp-util.h > index 42011bc..f391e2a 100644 > --- a/lib/odp-util.h > +++ b/lib/odp-util.h > @@ -142,7 +142,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); > * add another field and forget to adjust this value. > */ > #define ODPUTIL_FLOW_KEY_BYTES 640 > -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > /* A buffer with sufficient size and alignment to hold an nlattr-formatted > flow > * key. An array of "struct nlattr" might not, in theory, be sufficiently > diff --git a/lib/ofp-print.c b/lib/ofp-print.c > index f7f7df2..eaf81b3 100644 > --- a/lib/ofp-print.c > +++ b/lib/ofp-print.c > @@ -55,10 +55,10 @@ > static void ofp_print_queue_name(struct ds *string, uint32_t port); > static void ofp_print_error(struct ds *, enum ofperr); > > -/* Returns a string that represents the contents of the Ethernet frame in the > +/* Returns a string that represents the contents of the packet 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, ovs_be16 packet_ethertype) > { > struct ds ds = DS_EMPTY_INITIALIZER; > struct dp_packet buf; > @@ -66,6 +66,11 @@ ofp_packet_to_string(const void *data, size_t len) > size_t l4_size; > > dp_packet_use_const(&buf, data, len); > + if (packet_ethertype) { > + /* This is a layer 3 packet */ > + buf.md.packet_ethertype = packet_ethertype; > + buf.l3_ofs = 0; > + } > flow_extract(&buf, &flow); > flow_format(&ds, &flow); > > @@ -96,6 +101,17 @@ ofp_packet_to_string(const void *data, size_t len) > return ds_cstr(&ds); > } > > +/* Returns a string that represents the contents of the packet in the > + * 'len' bytes starting at 'data'. The caller must free the returned > string.*/ > +char * > +ofp_dp_packet_to_string(const struct dp_packet *p) > +{ > + ovs_assert(!dp_packet_is_l3(p) || ntohs(p->md.packet_ethertype)); > + return ofp_packet_to_string(dp_packet_data(p), dp_packet_size(p), > + dp_packet_is_l3(p) ? p->md.packet_ethertype > + : htons(0)); > +} > + > static void > format_hex_arg(struct ds *s, const uint8_t *data, size_t len) > { > @@ -209,7 +225,7 @@ ofp_print_packet_in(struct ds *string, const struct > ofp_header *oh, > > if (verbosity > 0) { > char *packet = ofp_packet_to_string(public->packet, > - public->packet_len); > + public->packet_len, htons(0)); > ds_put_cstr(string, packet); > free(packet); > } > @@ -245,7 +261,8 @@ 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, > + htons(0)); > ds_put_char(string, '\n'); > ds_put_cstr(string, packet); > free(packet); > @@ -3726,5 +3743,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, htons(0))); > } > diff --git a/lib/ofp-util.c b/lib/ofp-util.c > index 0c9343e..b8872b5 100644 > --- a/lib/ofp-util.c > +++ b/lib/ofp-util.c > @@ -101,7 +101,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 == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > /* Initialize most of wc. */ > flow_wildcards_init_catchall(wc); > diff --git a/lib/packets.c b/lib/packets.c > index e440aaa..f9ad79c 100644 > --- a/lib/packets.c > +++ b/lib/packets.c > @@ -237,16 +237,24 @@ push_eth(struct dp_packet *packet, const struct > eth_addr *dst, > eh->eth_src = *src; > } > > -/* Removes Ethernet header, including all VLAN and MPLS headers, from > 'packet'. > +/* Removes Ethernet header, including VLAN header, from 'packet'. > * > * Previous to calling this function, 'ofpbuf_l3(packet)' must not be NULL */ > void > pop_eth(struct dp_packet *packet) > { > + char *l2_5 = dp_packet_l2_5(packet);; > + int increment; > + > ovs_assert(dp_packet_l3(packet) != NULL); > > - dp_packet_resize_l2_5(packet, -packet->l3_ofs); > - dp_packet_set_l2_5(packet, NULL); > + if (l2_5) { > + increment = packet->l2_5_ofs; > + } else { > + increment = packet->l3_ofs; > + } > + > + dp_packet_resize_l2(packet, -increment); > } > > /* Set ethertype of the packet. */ > @@ -256,6 +264,7 @@ set_ethertype(struct dp_packet *packet, ovs_be16 eth_type) > struct eth_header *eh = dp_packet_l2(packet); > > if (!eh) { > + packet->md.packet_ethertype = eth_type; > return; > } > > diff --git a/lib/packets.h b/lib/packets.h > index bf2e7c2..9ecb44e 100644 > --- a/lib/packets.h > +++ b/lib/packets.h > @@ -104,6 +104,8 @@ struct pkt_metadata { > uint32_t ct_mark; /* Connection mark. */ > ovs_u128 ct_label; /* Connection label. */ > union flow_in_port in_port; /* Input port. */ > + ovs_be16 packet_ethertype; /* Ethertype of the packet */ > + uint8_t base_layer; /* Packet starts at this layer */ > struct flow_tnl tunnel; /* Encapsulating tunnel parameters. Note that > * if 'ip_dst' == 0, the rest of the fields > may > * be uninitialized. */ > diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h > index c357591..dfe54ff 100644 > --- a/ofproto/ofproto-dpif-rid.h > +++ b/ofproto/ofproto-dpif-rid.h > @@ -99,7 +99,7 @@ struct rule; > /* Metadata for restoring pipeline context after recirculation. Helpers > * are inlined below to keep them together with the definition for easier > * updates. */ > -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); > +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > > struct frozen_metadata { > /* Metadata in struct flow. */ > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index 503a347..a6772eb 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -161,6 +161,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. */ > @@ -539,7 +540,7 @@ static void xlate_xport_set(struct xport *xport, > odp_port_t odp_port, > int stp_port_no, const struct rstp_port > *rstp_port, > enum ofputil_port_config config, > enum ofputil_port_state state, bool is_tunnel, > - bool may_enable); > + bool may_enable, bool is_layer3); > static void xlate_xbridge_remove(struct xlate_cfg *, struct xbridge *); > static void xlate_xbundle_remove(struct xlate_cfg *, struct xbundle *); > static void xlate_xport_remove(struct xlate_cfg *, struct xport *); > @@ -866,12 +867,13 @@ xlate_xport_set(struct xport *xport, odp_port_t > odp_port, > const struct bfd *bfd, const struct lldp *lldp, int > stp_port_no, > const struct rstp_port* rstp_port, > enum ofputil_port_config config, enum ofputil_port_state > state, > - bool is_tunnel, bool may_enable) > + bool is_tunnel, bool may_enable, bool is_layer3) > { > xport->config = config; > xport->state = state; > xport->stp_port_no = stp_port_no; > xport->is_tunnel = is_tunnel; > + xport->is_layer3 = is_layer3; > xport->may_enable = may_enable; > xport->odp_port = odp_port; > > @@ -962,7 +964,7 @@ xlate_xport_copy(struct xbridge *xbridge, struct xbundle > *xbundle, > xlate_xport_set(new_xport, xport->odp_port, xport->netdev, xport->cfm, > xport->bfd, xport->lldp, xport->stp_port_no, > xport->rstp_port, xport->config, xport->state, > - xport->is_tunnel, xport->may_enable); > + xport->is_tunnel, xport->may_enable, xport->is_layer3); > > if (xport->peer) { > struct xport *peer = xport_lookup(new_xcfg, xport->peer->ofport); > @@ -1200,7 +1202,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) > { > size_t i; > struct xport *xport; > @@ -1221,7 +1223,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct > ofbundle *ofbundle, > > xlate_xport_set(xport, odp_port, netdev, cfm, bfd, lldp, > stp_port_no, rstp_port, config, state, is_tunnel, > - may_enable); > + may_enable, is_layer3); > > if (xport->peer) { > xport->peer->peer = NULL; > @@ -2517,7 +2519,7 @@ xlate_normal(struct xlate_ctx *ctx) > > /* Learn source MAC. */ > bool is_grat_arp = is_gratuitous_arp(flow, wc); > - if (ctx->xin->allow_side_effects) { > + if (ctx->xin->allow_side_effects && !in_port->is_layer3) { > update_learning_table(ctx, in_xbundle, flow->dl_src, vlan, > is_grat_arp); > } > @@ -3087,7 +3089,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 == 36); > + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37); > memset(&flow_tnl, 0, sizeof flow_tnl); > > if (!xport) { > @@ -3132,6 +3134,14 @@ compose_output_action__(struct xlate_ctx *ctx, > ofp_port_t ofp_port, > } > } > > + if (xport->is_layer3) { > + if (flow->base_layer == LAYER_2) { > + flow->base_layer = LAYER_3; > + } > + } else if (flow->base_layer == LAYER_3) { > + flow->base_layer = LAYER_2; > + } > + > if (xport->peer) { > const struct xport *peer = xport->peer; > struct flow old_flow = ctx->xin->flow; > @@ -3811,9 +3821,14 @@ execute_controller_action(struct xlate_ctx *ctx, int > len, > uint16_t controller_id, > const uint8_t *userdata, size_t userdata_len) > { > + struct flow *flow = &ctx->xin->flow; > struct dp_packet_batch batch; > struct dp_packet *packet; > > + if (flow->base_layer == LAYER_3) { > + flow->base_layer = LAYER_2; > + } > + > ctx->xout->slow |= SLOW_CONTROLLER; > xlate_commit_actions(ctx); > if (!ctx->xin->packet) { > diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h > index 3986f26..5bffcb2 100644 > --- a/ofproto/ofproto-dpif-xlate.h > +++ b/ofproto/ofproto-dpif-xlate.h > @@ -163,7 +163,7 @@ void xlate_ofport_set(struct ofproto_dpif *, struct > ofbundle *, > const struct ofproto_port_queue *qdscp, > size_t n_qdscp, enum ofputil_port_config, > enum ofputil_port_state, bool is_tunnel, > - bool may_enable); > + bool may_enable, bool is_l3); > void xlate_ofport_remove(struct ofport_dpif *); > > struct ofproto_dpif * xlate_lookup_ofproto(const struct dpif_backer *, > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index 4007a3a..0b46d06 100644 > --- a/ofproto/ofproto-dpif.c > +++ b/ofproto/ofproto-dpif.c > @@ -466,7 +466,7 @@ type_run(const char *type) > ofport->rstp_port, ofport->qdscp, > ofport->n_qdscp, ofport->up.pp.config, > ofport->up.pp.state, ofport->is_tunnel, > - ofport->may_enable); > + ofport->may_enable, ofport->is_layer3); > } > } > xlate_txn_commit(); > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > index e861d9f..e1810cf 100644 > --- a/tests/ofproto-dpif.at > +++ b/tests/ofproto-dpif.at > @@ -4773,15 +4773,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/tunnel-push-pop-ipv6.at b/tests/tunnel-push-pop-ipv6.at > index 16dc571..3f3d5ee 100644 > --- a/tests/tunnel-push-pop-ipv6.at > +++ b/tests/tunnel-push-pop-ipv6.at > @@ -88,28 +88,28 @@ AT_CHECK([tail -1 stdout], [0], > > dnl Check VXLAN tunnel push > AT_CHECK([ovs-ofctl add-flow int-br action=2]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,d > st=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7b)),out_port(100) > ) > ]) > > dnl Check VXLAN tunnel push set tunnel id by flow and checksum > AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(4789),header(size=70,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,d > st=2001:cafe::93,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100) > ) > ]) > > dnl Check GRE tunnel push > AT_CHECK([ovs-ofctl add-flow int-br action=3]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(3),header(size=62,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,dst=2 > 001:cafe::92,label=0,proto=47,tclass=0x0,hlimit=64),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100)) > ]) > > dnl Check Geneve tunnel push > AT_CHECK([ovs-ofctl add-flow int-br > "actions=set_field:2001:cafe::92->tun_ipv6_dst,5"]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(6081),header(size=70,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,d > st=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(vni=0x7b)),out_port(100)) > ]) > @@ -117,7 +117,7 @@ AT_CHECK([tail -1 stdout], [0], > dnl Check Geneve tunnel push with options > AT_CHECK([ovs-ofctl add-tlv-map int-br > "{class=0xffff,type=0x80,len=4}->tun_metadata0"]) > AT_CHECK([ovs-ofctl add-flow int-br > "actions=set_field:2001:cafe::92->tun_ipv6_dst,set_field:0xa->tun_metadata0,5"]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(6081),header(size=78,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x86dd),ipv6(src=2001:cafe::88,d > st=2001:cafe::92,label=0,proto=17,tclass=0x0,hlimit=64),udp(src=0,dst=6081,csum=0xffff),geneve(crit,vni=0x7b,options({class=0xffff,type= > 0x80,len=4,0xa}))),out_port(100)) > ]) > diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at > index 4eeac41..e4687fc 100644 > --- a/tests/tunnel-push-pop.at > +++ b/tests/tunnel-push-pop.at > @@ -105,28 +105,28 @@ AT_CHECK([tail -1 stdout], [0], > > dnl Check VXLAN tunnel push > AT_CHECK([ovs-ofctl add-flow int-br action=2]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1 > .2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0x8000000,vni=0x7b)),out_port(100)) > ]) > > dnl Check VXLAN tunnel push set tunnel id by flow and checksum > AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:124,4"]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1 > .2.93,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0xffff),vxlan(flags=0x8000000,vni=0x7c)),out_port(100)) > ]) > > dnl Check GRE tunnel push > AT_CHECK([ovs-ofctl add-flow int-br action=3]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.9 > 2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100)) > ]) > > dnl Check Geneve tunnel push > AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:1.1.2.92->tun_dst,5"]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(6081),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1 > .2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)) > ]) > @@ -141,7 +141,7 @@ AT_CHECK([tail -1 stdout], [0], > dnl Check Geneve tunnel push with options > AT_CHECK([ovs-ofctl add-tlv-map int-br > "{class=0xffff,type=0x80,len=4}->tun_metadata0"]) > AT_CHECK([ovs-ofctl add-flow int-br > "actions=set_field:1.1.2.92->tun_dst,set_field:0xa->tun_metadata0,5"]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], > [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag= > no)'], [0], [stdout]) > AT_CHECK([tail -1 stdout], [0], > [Datapath actions: > tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1 > .2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa})) > ),out_port(100)) > ]) > diff --git a/tests/tunnel.at b/tests/tunnel.at > index 1ba209d..881d6ba 100644 > --- a/tests/tunnel.at > +++ b/tests/tunnel.at > @@ -488,14 +488,14 @@ AT_CHECK([tail -1 stdout], [0], > ]) > > dnl Option match > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}),flags(df|key)),in_port(6081),skb_mar > k(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}),flags(df|key)),in_port(6081),skb_mar > k(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], > [0], [stdout]) > AT_CHECK([tail -2 stdout], [0], > [Megaflow: > recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df- > csum+key,tun_metadata0=0xb/0xf,in_port=1,nw_frag=no > Datapath actions: 2 > ]) > > dnl Skip unknown option > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}{class=0xffff,type=2,len=4,0xc}),flags(d > f|key)),in_port(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], > [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0xb}{class=0xffff,type=2,len=4,0xc}),flags(d > f|key)),in_port(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], > [0], [stdout]) > AT_CHECK([tail -2 stdout], [0], > [Megaflow: > recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df- > csum+key,tun_metadata0=0xb/0xf,in_port=1,nw_frag=no > Datapath actions: 2 > @@ -529,7 +529,7 @@ AT_CHECK([ovs-ofctl del-tlv-map br0 > "{class=0xffff,type=3,len=4}->tun_metadata3" > AT_CHECK([ovs-ofctl add-tlv-map br0 > "{class=0xffff,type=3,len=8}->tun_metadata3"]) > > AT_CHECK([ovs-ofctl add-flow br0 tun_metadata3=0x1234567890abcdef,actions=2]) > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=3,len=8,0x1234567890abcdef}),flags(df|key)),in_p > ort(6081),skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=3,len=8,0x1234567890abcdef}),flags(df|key)),in_p > ort(6081),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], > [0], [stdout]) > AT_CHECK([tail -2 stdout], [0], > [Megaflow: > recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df- > csum+key,tun_metadata3=0x1234567890abcdef,in_port=1,nw_frag=no > Datapath actions: 2 > @@ -564,13 +564,13 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | > sort], > NXST_FLOW reply: > ]) > > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x12345678}),flags(df|key)),in_port(6081), > skb_mark(0),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=0,len=4,0x12345678}),flags(df|key)),in_port(6081), > skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], > [0], [stdout]) > AT_CHECK([tail -2 stdout], [0], > [Megaflow: > recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df- > csum+key,tun_metadata0,tun_metadata1=NP,tun_metadata2=NP,in_port=1,nw_frag=no > Datapath actions: 2 > ]) > > -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=1,len=0}),flags(df|key)),in_port(6081),skb_mark(0), > eth_type(0x0800),ipv4(frag=no)'], [0], [stdout]) > +AT_CHECK([ovs-appctl ofproto/trace ovs-dummy > 'recirc_id(0),tunnel(tun_id=0x0,src=1.1.1.1,dst=1.1.1.2,ttl=64,geneve({class=0xffff,type=1,len=0}),flags(df|key)),in_port(6081),skb_mark(0), > eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], > [0], [stdout]) > AT_CHECK([tail -2 stdout], [0], > [Megaflow: > recirc_id=0,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=1.1.1.2,tun_tos=0,tun_flags=+df- > csum+key,tun_metadata1,tun_metadata2=NP,in_port=1,nw_ecn=0,nw_frag=no > Datapath actions: > set(tunnel(tun_id=0x0,dst=1.1.1.1,ttl=64,tp_dst=6081,geneve({class=0xffff,type=0x1,len=0}),flags(df|key))),6081 > -- > 2.1.0 > > _______________________________________________ > dev mailing list > [email protected] > https://mail.openvswitch.org/mailman/listinfo/ovs-dev _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
