From: Lorand Jakab <loja...@cisco.com> 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. Signed-off-by: Lorand Jakab <loja...@cisco.com> Signed-off-by: Simon Horman <simon.hor...@netronome.com> --- v10 [Simon Horman] * Ensure that packets sent to controller have an ethernet header * Avoid popping ethernet header multiple times resulting in a corrupted packet v9 [Simon Horman] * Rebase: in particular update to use dp_packet rather than ofpbuf * Add dp_packet_is_l3() helper * Make flow_wc_map() LAYER_2/3 aware * mf_are_prereqs_ok() * Make Ethernet prerequisite of MPLS fields * Use if() rather than goto to handle LAYER_2/3 in odp_flow_key_from_flow__() * Only get in_xport if needed in compose_output_action__() v1 - v8 [Lorand Jakab] --- build-aux/extract-ofp-fields | 1 + include/openvswitch/flow.h | 16 ++++- include/openvswitch/meta-flow.h | 9 +-- include/openvswitch/ofp-print.h | 3 +- lib/dp-packet.h | 14 ++++- lib/dpif-netdev.c | 3 +- lib/dpif-netlink.c | 4 ++ lib/dpif.c | 6 +- lib/flow.c | 132 ++++++++++++++++++++++++++-------------- lib/flow.h | 3 + lib/match.c | 2 +- lib/meta-flow.c | 10 +++ lib/netdev-bsd.c | 2 + lib/netdev-dummy.c | 1 + lib/netdev-linux.c | 2 + lib/nx-match.c | 2 +- lib/odp-util.c | 91 +++++++++++++++------------ lib/odp-util.h | 2 +- lib/ofp-print.c | 13 ++-- lib/ofp-util.c | 2 +- ofproto/ofproto-dpif-rid.h | 2 +- ofproto/ofproto-dpif-xlate.c | 37 ++++++++--- ofproto/ofproto-dpif-xlate.h | 2 +- ofproto/ofproto-dpif.c | 2 +- tests/ofproto-dpif.at | 6 +- tests/tunnel-push-pop.at | 10 +-- tests/tunnel.at | 10 +-- 27 files changed, 257 insertions(+), 130 deletions(-) diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields index 8d43e4b14179..7f58788cedc5 100755 --- a/build-aux/extract-ofp-fields +++ b/build-aux/extract-ofp-fields @@ -35,6 +35,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 6f08f3a2312c..4e8e31b62578 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 35 +#define FLOW_WC_SEQ 36 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 8 @@ -52,6 +52,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 @@ -71,6 +76,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()! */ @@ -92,7 +101,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 pad2[2]; /* Pad to 64 bits. */ + uint8_t base_layer; /* Fields start at this layer */ + uint8_t pad2; /* Pad to 64 bits. */ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */ struct eth_addr dl_dst; /* Ethernet destination address. */ @@ -131,7 +141,7 @@ 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) + 216 - && FLOW_WC_SEQ == 35); + && FLOW_WC_SEQ == 36); /* Incremental points at which flow classification may be performed in * segments. diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h index e5f996208e97..90f3db2057e5 100644 --- a/include/openvswitch/meta-flow.h +++ b/include/openvswitch/meta-flow.h @@ -1020,7 +1020,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. @@ -1036,7 +1036,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. @@ -1054,7 +1054,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. @@ -1070,7 +1070,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. @@ -1793,6 +1793,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 58fd4039d61f..3e951173acc2 100644 --- a/include/openvswitch/ofp-print.h +++ b/include/openvswitch/ofp-print.h @@ -21,6 +21,7 @@ #include <stdint.h> #include <stdio.h> +#include <stdbool.h> struct ds; struct ofp10_match; @@ -41,7 +42,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/dp-packet.h b/lib/dp-packet.h index 000b09dd650c..b9b549e1e09f 100644 --- a/lib/dp-packet.h +++ b/lib/dp-packet.h @@ -250,12 +250,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; +} + +/* 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 4245a9a39616..89e6ef400cae 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -3251,7 +3251,8 @@ 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_)); + dp_packet_size(packet_), + dp_packet_is_l3(packet_)); odp_flow_key_format(key.data, key.size, &ds); diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c index 015ba20e00e6..b6b8b73494e7 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -2013,6 +2013,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 96f44adcb155..cf80b6a1f51c 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1391,7 +1391,8 @@ dpif_print_packet(struct dpif *dpif, struct dpif_upcall *upcall) char *packet; packet = ofp_packet_to_string(dp_packet_data(&upcall->packet), - dp_packet_size(&upcall->packet)); + dp_packet_size(&upcall->packet), + dp_packet_is_l3(&upcall->packet)); ds_init(&flow); odp_flow_key_format(upcall->key, upcall->key_len, &flow); @@ -1686,7 +1687,8 @@ log_execute_message(struct dpif *dpif, const struct dpif_execute *execute, char *packet; packet = ofp_packet_to_string(dp_packet_data(execute->packet), - dp_packet_size(execute->packet)); + dp_packet_size(execute->packet), + dp_packet_is_l3(execute->packet)); ds_put_format(&ds, "%s: %sexecute ", dpif_name(dpif), (subexecute ? "sub-" diff --git a/lib/flow.c b/lib/flow.c index 343ae6df7093..566dbd0feb06 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -124,7 +124,7 @@ struct mf_ctx { * away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are * defined as macros. */ -#if (FLOW_WC_SEQ != 35) +#if (FLOW_WC_SEQ != 36) #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 " @@ -439,18 +439,35 @@ invalid: arp_buf[1] = eth_addr_zero; } -/* Initializes 'flow' members from 'packet' and 'md' +/* Determines IP version if a layer 3 packet */ +static ovs_be16 +get_l3_eth_type(struct dp_packet *packet) +{ + struct ip_header *ip = dp_packet_l3(packet); + 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; + } +} + +/* 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, @@ -481,9 +498,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)) { @@ -531,37 +549,54 @@ 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); - miniflow_push_be16(mf, dl_type, dl_type); - miniflow_push_be16(mf, vlan_tci, vlan_tci); - } + /* 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); + } - /* Parse mpls. */ - if (OVS_UNLIKELY(eth_type_mpls(dl_type))) { - int count; - const void *mpls = data; + /* Parse mpls. */ + if (OVS_UNLIKELY(eth_type_mpls(dl_type))) { + int count; + const void *mpls = data; - packet->l2_5_ofs = (char *)data - l2; - count = parse_mpls(&data, &size); - miniflow_push_words_32(mf, mpls_lse, mpls, count); + packet->l2_5_ofs = (char *)data - frame; + count = parse_mpls(&data, &size); + miniflow_push_words_32(mf, mpls_lse, mpls, count); + } + } else { + /* We assume L3 packets are either IPv4 or IPv6. */ + packet->l3_ofs = 0; + miniflow_pad_from_64(mf, base_layer); + miniflow_push_uint8(mf, base_layer, LAYER_3); + miniflow_pad_to_64(mf, base_layer); + + dl_type = get_l3_eth_type(packet); + miniflow_pad_from_64(mf, dl_type); + miniflow_push_be16(mf, dl_type, dl_type); + miniflow_push_be16(mf, vlan_tci, 0); } /* 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))) { @@ -738,7 +773,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)); @@ -842,7 +877,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata) { int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); match_init_catchall(flow_metadata); if (flow->tunnel.tun_id != htonll(0)) { @@ -1248,7 +1283,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 == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); if (flow_tnl_dst_is_set(&flow->tunnel)) { if (flow->tunnel.flags & FLOW_TNL_F_KEY) { @@ -1295,10 +1330,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); @@ -1365,7 +1403,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 == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); flowmap_init(map); @@ -1388,15 +1426,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); @@ -1449,12 +1490,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 == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); 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 @@ -2076,7 +2118,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label)); /* Clear all L3 and L4 fields and dp_hash. */ - BUILD_ASSERT(FLOW_WC_SEQ == 35); + BUILD_ASSERT(FLOW_WC_SEQ == 36); 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 0196ee7b29bd..68810b9a35cb 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -39,6 +39,9 @@ struct dp_packet; struct pkt_metadata; struct match; +/* Maximum number of supported MPLS labels. */ +#define FLOW_MAX_MPLS_LABELS 3 + /* Some flow fields are mutually exclusive or only appear within the flow * pipeline. IPv6 headers are bigger than IPv4 and MPLS, and IPv6 ND packets * are bigger than TCP,UDP and IGMP packets. */ diff --git a/lib/match.c b/lib/match.c index 052130a62221..fd22c1962aac 100644 --- a/lib/match.c +++ b/lib/match.c @@ -1060,7 +1060,7 @@ match_format(const struct match *match, struct ds *s, int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); 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 ce60f2256440..ec2c545f3b61 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -369,6 +369,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)); @@ -456,6 +458,9 @@ mf_mask_field_and_prereqs__(const struct mf_field *mf, case MFP_VLAN_VID: WC_MASK_FIELD_MASK(wc, vlan_tci, htons(VLAN_CFI)); break; + case MFP_ETHERNET: + WC_MASK_FIELD(wc, base_layer); + break; case MFP_NONE: break; } @@ -492,6 +497,11 @@ mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct mf_bitmap *bm) case MFP_VLAN_VID: bitmap_set1(bm->bm, MFF_VLAN_TCI); break; + case MFP_ETHERNET: + bitmap_set1(bm->bm, MFF_ETH_SRC); + bitmap_set1(bm->bm, MFF_ETH_DST); + bitmap_set1(bm->bm, MFF_ETH_TYPE); + break; case MFP_NONE: break; } diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c index 49c05f49938b..828670b449a8 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 5acb4e194312..7be00bf95b82 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -263,6 +263,7 @@ dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s) dp_packet_clone(&s->rxbuf)); 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 2953964b6213..305ca7b2d840 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1045,6 +1045,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; @@ -1085,6 +1086,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 3c485705d26c..3a16cbe78df9 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -918,7 +918,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 == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); /* Metadata. */ if (match->wc.masks.dp_hash) { diff --git a/lib/odp-util.c b/lib/odp-util.c index 999530ae1496..bc54496b04c4 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -4285,7 +4285,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; @@ -4323,45 +4323,47 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms, nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, parms->odp_in_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)); + 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; + } } - 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)) { + + 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; } - } 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); - } - goto unencap; + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type); } - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type); - if (flow->dl_type == htons(ETH_TYPE_IP)) { struct ovs_key_ipv4 *ipv4_key; @@ -4765,7 +4767,13 @@ 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 (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"); @@ -5186,12 +5194,10 @@ 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 { + flow->base_layer = LAYER_3; } /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */ @@ -5511,6 +5517,13 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, { struct ovs_key_ethernet key, base, mask; + /* 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_flow->base_layer == LAYER_3 && flow->base_layer == LAYER_2) { + return; + } + get_ethernet_key(flow, &key); get_ethernet_key(base_flow, &base); get_ethernet_key(&wc->masks, &mask); diff --git a/lib/odp-util.h b/lib/odp-util.h index 4d797533f32c..016e95c36de2 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -141,7 +141,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 == 35); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); /* 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 69f90f30115d..f10e48625b7e 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, bool is_layer3) { struct ds ds = DS_EMPTY_INITIALIZER; struct dp_packet buf; @@ -66,6 +66,9 @@ ofp_packet_to_string(const void *data, size_t len) size_t l4_size; dp_packet_use_const(&buf, data, len); + if (is_layer3) { + buf.l3_ofs = 0; + } flow_extract(&buf, &flow); flow_format(&ds, &flow); @@ -200,7 +203,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, false); ds_put_cstr(string, packet); free(packet); } @@ -236,7 +239,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); @@ -3505,5 +3508,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-util.c b/lib/ofp-util.c index bb1535dfcc4b..43f648c28394 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -100,7 +100,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 == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h index 3efd85ebd51c..472d3a71a9ff 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 == 35); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); struct frozen_metadata { /* Metadata in struct flow. */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index cdf5a830cec1..2852dd1073fe 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -148,6 +148,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. */ @@ -566,7 +567,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 *); @@ -729,12 +730,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; @@ -825,7 +827,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); @@ -1063,7 +1065,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; @@ -1084,7 +1086,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; @@ -2421,7 +2423,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); } if (ctx->xin->xcache) { @@ -2947,7 +2949,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 == 35); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 36); memset(&flow_tnl, 0, sizeof flow_tnl); if (!xport) { @@ -2982,6 +2984,17 @@ 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; + odp_put_pop_eth_action(ctx->odp_actions); + } + } else if (flow->base_layer == LAYER_3) { + flow->base_layer = LAYER_2; + odp_put_push_eth_action(ctx->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; @@ -3571,6 +3584,7 @@ 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 *packet; ctx->xout->slow |= SLOW_CONTROLLER; @@ -3581,6 +3595,15 @@ execute_controller_action(struct xlate_ctx *ctx, int len, packet = dp_packet_clone(ctx->xin->packet); + if (flow->base_layer == LAYER_3) { + /* Push ethernet header without setting flow->base_layer to LAYER_2 + * because packet is a copy of the packet that has no relevance to + * subsequent action translation. + */ + odp_put_push_eth_action(ctx->odp_actions, &flow->dl_src, + &flow->dl_dst, flow->dl_type); + } + odp_execute_actions(NULL, &packet, 1, false, ctx->odp_actions->data, ctx->odp_actions->size, NULL); diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h index c4c23d5c3e43..f77375fac008 100644 --- a/ofproto/ofproto-dpif-xlate.h +++ b/ofproto/ofproto-dpif-xlate.h @@ -168,7 +168,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 a1e0407a62bb..3a0218201701 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -641,7 +641,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 4801ce059ec8..19ce93b633e8 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -4459,15 +4459,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.at b/tests/tunnel-push-pop.at index a7909d3268ff..f0a5ffcae055 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at @@ -90,28 +90,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.92,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)) ]) @@ -119,7 +119,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 9f17194b67f2..28930c0e605b 100644 --- a/tests/tunnel.at +++ b/tests/tunnel.at @@ -434,14 +434,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_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}),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: pkt_mark=0,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(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,0xb}{class=0xffff,type=2,len=4,0xc}),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: pkt_mark=0,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 @@ -475,7 +475,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_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=3,len=8,0x1234567890abcdef}),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: pkt_mark=0,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 @@ -510,13 +510,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: pkt_mark=0,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: pkt_mark=0,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,geneve({class=0xffff,type=0x1,len=0}),flags(df|key))),6081 -- 2.7.0.rc3.207.g0ac5344 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev