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> --- 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 + 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 | 16 +++++- lib/match.c | 2 +- lib/meta-flow.c | 10 ++++ lib/meta-flow.h | 9 +-- 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-print.h | 3 +- lib/ofp-util.c | 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.at | 10 ++-- tests/tunnel.at | 10 ++-- tests/vlan-splinters.at | 4 +- 27 files changed, 248 insertions(+), 132 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/lib/dp-packet.h b/lib/dp-packet.h index bf4e7581cab3..37958d8d3a67 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 c4416f69b1e8..e9e586a7145b 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -3044,7 +3044,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 5e4839378c9c..bab2297541ac 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -2019,6 +2019,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 379984dbf97f..eee876913528 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); @@ -1687,7 +1688,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 2a7116b675da..d613d068674e 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -123,7 +123,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 " @@ -438,18 +438,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, @@ -480,9 +497,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)) { @@ -530,37 +548,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))) { @@ -737,7 +772,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)); @@ -841,7 +876,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)) { @@ -1247,7 +1282,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) { @@ -1294,10 +1329,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); @@ -1364,7 +1402,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); @@ -1387,15 +1425,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); @@ -1448,12 +1489,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 @@ -2075,7 +2117,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 5d78615a4199..f12d2182033e 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -40,7 +40,7 @@ struct match; /* 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 @@ -72,6 +72,11 @@ const char *flow_tun_flag_to_string(uint32_t flags); /* Maximum number of supported MPLS labels. */ #define FLOW_MAX_MPLS_LABELS 3 +enum base_layer { + LAYER_2 = 0, + LAYER_3 = 1 +}; + /* * A flow in the network. * @@ -88,6 +93,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()! */ @@ -109,7 +118,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[1]; /* Pad to 64 bits. */ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */ struct eth_addr dl_dst; /* Ethernet destination address. */ @@ -159,7 +169,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/lib/match.c b/lib/match.c index 95d34bc7011f..bb6ac9f7dd27 100644 --- a/lib/match.c +++ b/lib/match.c @@ -1038,7 +1038,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, "priority=%d,", priority); diff --git a/lib/meta-flow.c b/lib/meta-flow.c index 6bd0b999ad4e..24d85ae569e5 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -360,6 +360,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)); @@ -439,6 +441,9 @@ mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow_wildcards *wc) 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; } @@ -475,6 +480,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/meta-flow.h b/lib/meta-flow.h index 53bbc9ecb2a3..f1628c02337b 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -1019,7 +1019,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. @@ -1035,7 +1035,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. @@ -1053,7 +1053,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. @@ -1069,7 +1069,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. @@ -1777,6 +1777,7 @@ enum OVS_PACKED_ENUM mf_prereqs { MFP_NONE, /* L2 requirements. */ + MFP_ETHERNET, MFP_ARP, MFP_VLAN_VID, MFP_IPV4, diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c index edf04bfdc998..f7f14d1a330b 100644 --- a/lib/netdev-bsd.c +++ b/lib/netdev-bsd.c @@ -583,6 +583,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) { @@ -609,6 +610,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 df319de821fa..2fc101abc796 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 393b4cc8f774..b47ba0f8d430 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1041,6 +1041,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; @@ -1081,6 +1082,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 11bcd950de53..d73bdc727863 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -909,7 +909,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 f8f8c0ffbe6e..5674a48a2549 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -4249,7 +4249,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; @@ -4287,45 +4287,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; @@ -4730,7 +4732,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"); @@ -5151,12 +5159,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. */ @@ -5476,6 +5482,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 5525aee4e6c0..091d767026ef 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -53,10 +53,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; @@ -64,6 +64,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); @@ -139,7 +142,7 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, ds_put_char(string, '\n'); if (verbosity > 0) { - char *packet = ofp_packet_to_string(pin.packet, pin.packet_len); + char *packet = ofp_packet_to_string(pin.packet, pin.packet_len, false); ds_put_cstr(string, packet); free(packet); } @@ -173,7 +176,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); @@ -3516,5 +3519,5 @@ ofp_print(FILE *stream, const void *oh, size_t len, int verbosity) void ofp_print_packet(FILE *stream, const void *data, size_t len) { - print_and_free(stream, ofp_packet_to_string(data, len)); + print_and_free(stream, ofp_packet_to_string(data, len, false)); } diff --git a/lib/ofp-print.h b/lib/ofp-print.h index bbac18bd98c5..27cce8007a65 100644 --- a/lib/ofp-print.h +++ b/lib/ofp-print.h @@ -21,6 +21,7 @@ #include <stdint.h> #include <stdio.h> +#include <stdbool.h> struct ds; struct ofp10_match; @@ -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/ofp-util.c b/lib/ofp-util.c index effd96a30dbd..a4c0b0c93313 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -252,7 +252,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 4bbc7bf89a92..a09e97390a73 100644 --- a/ofproto/ofproto-dpif-rid.h +++ b/ofproto/ofproto-dpif-rid.h @@ -93,7 +93,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 recirc_metadata { /* Metadata in struct flow. */ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 57d877ff3b23..accd11e1d274 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. */ @@ -543,7 +544,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 *); @@ -706,12 +707,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; @@ -802,7 +804,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); @@ -1040,7 +1042,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; @@ -1061,7 +1063,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; @@ -2370,7 +2372,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) { @@ -2906,7 +2908,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) { @@ -2941,6 +2943,19 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, } } + if (xport->is_layer3) { + const struct xport *in_xport; + + in_xport = get_ofp_port(ctx->xbridge, flow->in_port.ofp_port); + if (in_xport && !in_xport->is_layer3) { + 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; diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h index 02067a7accbb..c360cea716c2 100644 --- a/ofproto/ofproto-dpif-xlate.h +++ b/ofproto/ofproto-dpif-xlate.h @@ -170,7 +170,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 8186f6b122fb..7c30501699fa 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -671,7 +671,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 4c2a9952b60d..4f7306cf5bee 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -4186,15 +4186,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 b29b47737449..242ffaf1bc46 100644 --- a/tests/tunnel-push-pop.at +++ b/tests/tunnel-push-pop.at @@ -74,28 +74,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=0x40),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=0x40),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=0x40),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=0x40),udp(src=0,dst=6081,csum=0x0),geneve(vni=0x7b)),out_port(100)) ]) @@ -103,7 +103,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=0x40),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 2c4a971ae6eb..4de85fe02043 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 diff --git a/tests/vlan-splinters.at b/tests/vlan-splinters.at index 883528da42ea..0b1b3db3d2e3 100644 --- a/tests/vlan-splinters.at +++ b/tests/vlan-splinters.at @@ -28,7 +28,7 @@ for args in '9 p2' '11 p3' '15 p4'; do # Check that when a packet is received on $splinter_port, it is # treated as if it had been received on p1 in the correct VLAN. - AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($splinter_port)"], + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($splinter_port),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)"], [0], [stdout]) AT_CHECK_UNQUOTED([sed -n '/^Flow/p; /^Datapath/p' stdout], [0], [dnl Flow: in_port=$p1,dl_vlan=$vlan,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x05ff @@ -37,7 +37,7 @@ Datapath actions: $access_port # Check that when an OpenFlow action sends a packet to p1 on # splintered VLAN $vlan, it is actually output to $splinter_port. - AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($access_port)"], + AT_CHECK([ovs-appctl ofproto/trace ovs-dummy "in_port($access_port),eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00)"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $splinter_port ]) -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev