diff mbox

[ovs-dev,v2,09/17] userspace: add support for pop_eth and push_eth actions

Message ID 1482927990-74764-10-git-send-email-yi.y.yang@intel.com
State Superseded
Delegated to: pravin shelar
Headers show

Commit Message

Yang, Yi Dec. 28, 2016, 12:26 p.m. UTC
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 <lojakab@cisco.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: Jiri Benc <jbenc@redhat.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
---
 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 ++++
 7 files changed, 137 insertions(+), 3 deletions(-)

Comments

Jan Scheurich Dec. 30, 2016, 10:56 a.m. UTC | #1
This patch is not in line with the ongoing work to support L3 tunnels on 
legacy (non packet type-aware) OVS bridges as specified in
https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit?usp=sharing

To avoid extensive rework, we suggest to replace the patch with the 
final solution based on explicit packet_type field in struct flow.

Regards, Jan

On 2016-12-28 13:26, Yi Yang wrote:
> 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 <lojakab@cisco.com>
> Signed-off-by: Simon Horman <simon.horman@netronome.com>
> Signed-off-by: Jiri Benc <jbenc@redhat.com>
> Signed-off-by: Yi Yang <yi.y.yang@intel.com>
> ---
>   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 ++++
>   7 files changed, 137 insertions(+), 3 deletions(-)
>
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 0b73056..488712b 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -4607,6 +4607,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 53958c5..9634d44 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1186,6 +1186,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 4fcff16..d7aec06 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -543,6 +543,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:
> @@ -676,6 +678,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], &eth->addresses.eth_dst,
> +                         &eth->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 427dd65..a50e76e 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:
> @@ -834,6 +836,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(");
> @@ -1036,14 +1048,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;
> @@ -5328,6 +5365,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,
> +                      &eth, sizeof eth);
> +}
> +
>   void
>   odp_put_tunnel_action(const struct flow_tnl *tunnel,
>                         struct ofpbuf *odp_actions)
> @@ -5461,6 +5518,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)
>   {
> @@ -5921,7 +5998,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
>       enum slow_path_reason slow1, slow2;
>       bool mpls_done = false;
>   
> -    commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
> +    commit_ether_action(flow, base, odp_actions, wc, use_masked);
>       /* Make packet a non-MPLS packet before committing L3/4 actions,
>        * which would otherwise do nothing. */
>       if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
> diff --git a/lib/packets.c b/lib/packets.c
> index 13a063a..19d3bfa 100644
> --- a/lib/packets.c
> +++ b/lib/packets.c
> @@ -225,6 +225,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 0f1ad19..245f651 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 37992b4..f970a57 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1161,6 +1161,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:
diff mbox

Patch

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 0b73056..488712b 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -4607,6 +4607,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 53958c5..9634d44 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1186,6 +1186,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 4fcff16..d7aec06 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -543,6 +543,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:
@@ -676,6 +678,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], &eth->addresses.eth_dst,
+                         &eth->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 427dd65..a50e76e 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:
@@ -834,6 +836,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(");
@@ -1036,14 +1048,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;
@@ -5328,6 +5365,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,
+                      &eth, sizeof eth);
+}
+
 void
 odp_put_tunnel_action(const struct flow_tnl *tunnel,
                       struct ofpbuf *odp_actions)
@@ -5461,6 +5518,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)
 {
@@ -5921,7 +5998,7 @@  commit_odp_actions(const struct flow *flow, struct flow *base,
     enum slow_path_reason slow1, slow2;
     bool mpls_done = false;
 
-    commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
+    commit_ether_action(flow, base, odp_actions, wc, use_masked);
     /* Make packet a non-MPLS packet before committing L3/4 actions,
      * which would otherwise do nothing. */
     if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
diff --git a/lib/packets.c b/lib/packets.c
index 13a063a..19d3bfa 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -225,6 +225,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 0f1ad19..245f651 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 37992b4..f970a57 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1161,6 +1161,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: