@@ -312,6 +312,48 @@ static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
return 0;
}
+static int pop_eth(struct sk_buff *skb, struct sw_flow_key *key)
+{
+ if ((skb->protocol == htons(ETH_P_8021Q) ||
+ skb->protocol == htons(ETH_P_8021AD)) &&
+ !skb_vlan_tag_present(skb)) {
+ int err = skb_vlan_pop(skb);
+ if (unlikely(err))
+ return err;
+ }
+
+ skb_pull_rcsum(skb, ETH_HLEN);
+ skb_reset_mac_header(skb);
+ skb->mac_len -= ETH_HLEN;
+
+ invalidate_flow_key(key);
+ return 0;
+}
+
+static int push_eth(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_action_push_eth *ethh)
+{
+ struct ethhdr *hdr;
+
+ /* Add the new Ethernet header */
+ if (skb_cow_head(skb, ETH_HLEN) < 0)
+ return -ENOMEM;
+
+ skb_push(skb, ETH_HLEN);
+ skb_reset_mac_header(skb);
+ skb_reset_mac_len(skb);
+
+ hdr = eth_hdr(skb);
+ ether_addr_copy(hdr->h_source, ethh->addresses.eth_src);
+ ether_addr_copy(hdr->h_dest, ethh->addresses.eth_dst);
+ hdr->h_proto = skb->protocol;
+
+ skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
+
+ invalidate_flow_key(key);
+ return 0;
+}
+
/* 'src' is already properly masked. */
static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_)
{
@@ -1218,6 +1260,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
case OVS_ACTION_ATTR_POP_NSH:
err = pop_nsh(skb, key);
break;
+
+ case OVS_ACTION_ATTR_PUSH_ETH:
+ err = push_eth(skb, key, nla_data(a));
+ break;
+
+ case OVS_ACTION_ATTR_POP_ETH:
+ err = pop_eth(skb, key);
+ break;
}
if (unlikely(err)) {
@@ -310,6 +310,14 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb,
skb->ip_summed = CHECKSUM_NONE;
}
+#define skb_postpush_rcsum rpl_skb_postpush_rcsum
+static inline void skb_postpush_rcsum(struct sk_buff *skb,
+ const void *start, unsigned int len)
+{
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->csum = csum_partial(start, len, skb->csum);
+}
+
#define skb_pull_rcsum rpl_skb_pull_rcsum
static inline unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len)
{