From: Lorand Jakab <loja...@cisco.com> These actions will allow L2->L3 and L3->L2 switching, and are supposed to be added to flows installed in the datapath transparently by ovs-vswitchd.
Signed-off-by: Lorand Jakab <loja...@cisco.com> Signed-off-by: Simon Horman <simon.hor...@netronome.com> --- v9 [Simon Horman] * Rebased v1 - v8 [Lorand Jakub] --- datapath/linux/compat/include/linux/openvswitch.h | 13 +++++ lib/dpif-netdev.c | 2 + lib/dpif.c | 2 + lib/odp-execute.c | 18 ++++++ lib/odp-util.c | 69 ++++++++++++++++++++++- lib/odp-util.h | 5 ++ lib/packets.c | 25 ++++++++ lib/packets.h | 4 ++ ofproto/ofproto-dpif-sflow.c | 7 +++ 9 files changed, 143 insertions(+), 2 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 3b39ebbc3d6a..8548d3b8b86b 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -739,6 +739,16 @@ enum ovs_nat_attr { #define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1) /** + * struct ovs_action_push_eth - %OVS_ACTION_ATTR_PUSH_ETH action argument. + * @addresses: Source and destination MAC addresses. + * @eth_type: Ethernet type + */ +struct ovs_action_push_eth { + struct ovs_key_ethernet addresses; + __be16 eth_type; +}; + +/** * enum ovs_action_attr - Action types. * * @OVS_ACTION_ATTR_OUTPUT: Output packet to port. @@ -802,11 +812,14 @@ enum ovs_action_attr { * The data must be zero for the unmasked * bits. */ OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */ + OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */ + OVS_ACTION_ATTR_POP_ETH, /* No argument. */ #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */ #endif + __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index cd72e624245e..c4416f69b1e8 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -3629,6 +3629,8 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_HASH: + case OVS_ACTION_ATTR_PUSH_ETH: + case OVS_ACTION_ATTR_POP_ETH: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/dpif.c b/lib/dpif.c index 38e40bac9ab4..379984dbf97f 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1146,6 +1146,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt, case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: + case OVS_ACTION_ATTR_PUSH_ETH: + case OVS_ACTION_ATTR_POP_ETH: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/odp-execute.c b/lib/odp-execute.c index b5204b270677..3d553a7d3090 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -503,6 +503,8 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: + case OVS_ACTION_ATTR_POP_ETH: + case OVS_ACTION_ATTR_PUSH_ETH: return false; case OVS_ACTION_ATTR_UNSPEC: @@ -623,6 +625,22 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal, } break; + case OVS_ACTION_ATTR_PUSH_ETH: { + const struct ovs_action_push_eth *eth = nl_attr_get(a); + + for (i = 0; i < cnt; i++) { + push_eth(packets[i], ð->addresses.eth_dst, + ð->addresses.eth_src, eth->eth_type); + } + break; + } + + case OVS_ACTION_ATTR_POP_ETH: + for (i = 0; i < cnt; i++) { + pop_eth(packets[i]); + } + break; + case OVS_ACTION_ATTR_OUTPUT: case OVS_ACTION_ATTR_TUNNEL_PUSH: case OVS_ACTION_ATTR_TUNNEL_POP: diff --git a/lib/odp-util.c b/lib/odp-util.c index 346064d56413..f8f8c0ffbe6e 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -120,6 +120,8 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE; + case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth); + case OVS_ACTION_ATTR_POP_ETH: return 0; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: @@ -827,6 +829,18 @@ format_odp_action(struct ds *ds, const struct nlattr *a) format_odp_key_attr(nl_attr_get(a), NULL, NULL, ds, true); ds_put_cstr(ds, ")"); break; + case OVS_ACTION_ATTR_PUSH_ETH: { + const struct ovs_action_push_eth *eth = nl_attr_get(a); + ds_put_format(ds, "push_eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT + ",type=0x%04"PRIx16")", + ETH_ADDR_ARGS(eth->addresses.eth_src), + ETH_ADDR_ARGS(eth->addresses.eth_dst), + ntohs(eth->eth_type)); + break; + } + case OVS_ACTION_ATTR_POP_ETH: + ds_put_cstr(ds, "pop_eth"); + break; case OVS_ACTION_ATTR_PUSH_VLAN: { const struct ovs_action_push_vlan *vlan = nl_attr_get(a); ds_put_cstr(ds, "push_vlan("); @@ -1011,14 +1025,43 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions) odp_put_userspace_action(pid, user_data, user_data_size, tunnel_out_port, include_actions, actions); res = n + n1; + goto out; } else if (s[n] == ')') { odp_put_userspace_action(pid, user_data, user_data_size, ODPP_NONE, include_actions, actions); res = n + 1; - } else { - res = -EINVAL; + goto out; + } + } + + { + struct ovs_action_push_eth push; + int eth_type = 0; + int n1 = -1; + + if (ovs_scan(&s[n], "push_eth(src="ETH_ADDR_SCAN_FMT"," + "dst="ETH_ADDR_SCAN_FMT",type=%i)%n", + ETH_ADDR_SCAN_ARGS(push.addresses.eth_src), + ETH_ADDR_SCAN_ARGS(push.addresses.eth_dst), + ð_type, &n1)) { + + push.eth_type = htons(eth_type); + + nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_ETH, + &push, sizeof push); + + res = n + n1; + goto out; } } + + if (!strncmp(&s[n], "pop_eth", 7)) { + nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_ETH); + res = 7; + goto out; + } + + res = -EINVAL; out: ofpbuf_uninit(&buf); return res; @@ -5291,6 +5334,28 @@ odp_put_userspace_action(uint32_t pid, } void +odp_put_pop_eth_action(struct ofpbuf *odp_actions) +{ + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_ETH); +} + +void +odp_put_push_eth_action(struct ofpbuf *odp_actions, + const struct eth_addr *eth_src, + const struct eth_addr *eth_dst, + const ovs_be16 eth_type) +{ + struct ovs_action_push_eth eth; + + eth.addresses.eth_src = *eth_src; + eth.addresses.eth_dst = *eth_dst; + eth.eth_type = eth_type; + + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_ETH, + ð, sizeof eth); +} + +void odp_put_tunnel_action(const struct flow_tnl *tunnel, struct ofpbuf *odp_actions) { diff --git a/lib/odp-util.h b/lib/odp-util.h index 51cf5c3680e9..4d797533f32c 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -317,6 +317,11 @@ size_t odp_put_userspace_action(uint32_t pid, odp_port_t tunnel_out_port, bool include_actions, struct ofpbuf *odp_actions); +void odp_put_pop_eth_action(struct ofpbuf *odp_actions); +void odp_put_push_eth_action(struct ofpbuf *odp_actions, + const struct eth_addr *eth_src, + const struct eth_addr *eth_dst, + const ovs_be16 eth_type); void odp_put_tunnel_action(const struct flow_tnl *tunnel, struct ofpbuf *odp_actions); diff --git a/lib/packets.c b/lib/packets.c index d82341d71829..9e44253e98e9 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -220,6 +220,31 @@ eth_pop_vlan(struct dp_packet *packet) } } +/* Push Ethernet header onto 'packet' assuming it is layer 3 */ +void +push_eth(struct dp_packet *packet, const struct eth_addr *dst, + const struct eth_addr *src, ovs_be16 type) +{ + struct eth_header *eh; + + eh = dp_packet_resize_l2(packet, ETH_HEADER_LEN); + eh->eth_dst = *dst; + eh->eth_src = *src; + eh->eth_type = type; +} + +/* Removes Ethernet header, including all VLAN and MPLS headers, from 'packet'. + * + * Previous to calling this function, 'ofpbuf_l3(packet)' must not be NULL */ +void +pop_eth(struct dp_packet *packet) +{ + ovs_assert(dp_packet_l3(packet) != NULL); + + dp_packet_resize_l2_5(packet, -packet->l3_ofs); + dp_packet_set_l2_5(packet, NULL); +} + /* Set ethertype of the packet. */ static void set_ethertype(struct dp_packet *packet, ovs_be16 eth_type) diff --git a/lib/packets.h b/lib/packets.h index 834e8a4986f0..f761af906aed 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -390,6 +390,10 @@ struct eth_header { }); BUILD_ASSERT_DECL(ETH_HEADER_LEN == sizeof(struct eth_header)); +void push_eth(struct dp_packet *packet, const struct eth_addr *dst, + const struct eth_addr *src, ovs_be16 type); +void pop_eth(struct dp_packet *packet); + #define LLC_DSAP_SNAP 0xaa #define LLC_SSAP_SNAP 0xaa #define LLC_CNTL_SNAP 3 diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index f11699cd9401..015ea96c9c92 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1171,6 +1171,13 @@ dpif_sflow_read_actions(const struct flow *flow, dpif_sflow_pop_mpls_lse(sflow_actions); break; } + case OVS_ACTION_ATTR_PUSH_ETH: + case OVS_ACTION_ATTR_POP_ETH: + /* TODO: SFlow does not currently define a MAC-in-MAC + * encapsulation structure. We could use an extension + * structure to report this. + */ + break; case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: -- 2.1.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev