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> --- v12 [Simon Horman] * Rebase * Provide commit_ether_action() v11 [Simon Horman] * Omit type field from push_eth action, it is not needed as the type of the packet is already known v10 [Simon Horman] * No Change v9 [Simon Horman] * Rebased v1 - v8 [Lorand Jakub] clanup: commit push/pop eth Signed-off-by: Simon Horman <simon.hor...@netronome.com> --- datapath/linux/compat/include/linux/openvswitch.h | 14 ++++ lib/dpif-netdev.c | 2 + lib/dpif.c | 2 + lib/odp-execute.c | 18 +++++ lib/odp-util.c | 83 ++++++++++++++++++++++- lib/packets.c | 24 +++++++ lib/packets.h | 4 ++ ofproto/ofproto-dpif-sflow.c | 7 ++ 8 files changed, 151 insertions(+), 3 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index f1e80db9db9d..4142fcec0301 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -745,6 +745,15 @@ 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; +}; + +/** * enum ovs_action_attr - Action types. * * @OVS_ACTION_ATTR_OUTPUT: Output packet to port. @@ -778,6 +787,9 @@ enum ovs_nat_attr { * is no MPLS label stack, as determined by ethertype, no action is taken. * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related * entries in the flow key. + * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the + * packet. + * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the packet. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -810,6 +822,8 @@ enum ovs_action_attr { * bits. */ OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */ OVS_ACTION_ATTR_TRUNC, /* u32 struct ovs_action_trunc. */ + 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*/ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 37c2631d2e29..24e032f555bd 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -4196,6 +4196,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_UNSPEC: case OVS_ACTION_ATTR_TRUNC: + case OVS_ACTION_ATTR_PUSH_ETH: + case OVS_ACTION_ATTR_POP_ETH: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/dpif.c b/lib/dpif.c index 5f1be41c6586..4c89e5d3ab5d 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1169,6 +1169,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_SET_MASKED: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_TRUNC: + 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 5a4390405d61..4471ff12d0d0 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -504,6 +504,8 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_PUSH_MPLS: case OVS_ACTION_ATTR_POP_MPLS: case OVS_ACTION_ATTR_TRUNC: + case OVS_ACTION_ATTR_POP_ETH: + case OVS_ACTION_ATTR_PUSH_ETH: return false; case OVS_ACTION_ATTR_UNSPEC: @@ -637,6 +639,22 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, 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); + } + 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 fd1ca9b5fd8a..ef213a69f6c7 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -121,6 +121,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: @@ -828,6 +830,16 @@ 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")", + ETH_ADDR_ARGS(eth->addresses.eth_src), + ETH_ADDR_ARGS(eth->addresses.eth_dst)); + 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("); @@ -1015,14 +1027,39 @@ 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 n1 = -1; + + if (ovs_scan(&s[n], "push_eth(src="ETH_ADDR_SCAN_FMT"," + "dst="ETH_ADDR_SCAN_FMT")%n", + ETH_ADDR_SCAN_ARGS(push.addresses.eth_src), + ETH_ADDR_SCAN_ARGS(push.addresses.eth_dst), &n1)) { + + 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; @@ -5349,6 +5386,26 @@ odp_put_userspace_action(uint32_t pid, return userdata_ofs; } +static void +odp_put_pop_eth_action(struct ofpbuf *odp_actions) +{ + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_ETH); +} + +static void +odp_put_push_eth_action(struct ofpbuf *odp_actions, + const struct eth_addr *eth_src, + const struct eth_addr *eth_dst) +{ + struct ovs_action_push_eth eth; + + eth.addresses.eth_src = *eth_src; + eth.addresses.eth_dst = *eth_dst; + + 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) @@ -5482,6 +5539,26 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, } static void +commit_ether_action(const struct flow *flow, struct flow *base_flow, + struct ofpbuf *odp_actions, struct flow_wildcards *wc, + bool use_masked) +{ + if (flow->base_layer != base_flow->base_layer) { + if (flow->base_layer == LAYER_2) { + odp_put_push_eth_action(odp_actions, &flow->dl_src, &flow->dl_dst); + base_flow->dl_src = flow->dl_src; + base_flow->dl_dst = flow->dl_dst; + } else { + odp_put_pop_eth_action(odp_actions); + } + base_flow->base_layer = flow->base_layer; + } + + commit_set_ether_addr_action(flow, base_flow, odp_actions, wc, + use_masked); +} + +static void pop_vlan(struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { @@ -5938,7 +6015,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, { enum slow_path_reason slow1, slow2; - commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked); + commit_ether_action(flow, base, odp_actions, wc, use_masked); slow1 = commit_set_nw_action(flow, base, odp_actions, wc, use_masked); commit_set_port_action(flow, base, odp_actions, wc, use_masked); slow2 = commit_set_icmp_action(flow, base, odp_actions, wc); diff --git a/lib/packets.c b/lib/packets.c index a27264c93cfe..1aaf16de01ec 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -220,6 +220,30 @@ 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) +{ + struct eth_header *eh; + + eh = dp_packet_resize_l2(packet, ETH_HEADER_LEN); + eh->eth_dst = *dst; + eh->eth_src = *src; +} + +/* 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 077ccfafa5a4..58de05ff2026 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -366,6 +366,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); +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 7d0aa36f026b..bb15fe6af3dc 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1174,6 +1174,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.7.0.rc3.207.g0ac5344 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev