diff mbox

[ovs-dev,RFC,v2,5/5] Add push_eth/pop_eth flow actions for kernel data path

Message ID 1468345006-11991-1-git-send-email-johnson.li@intel.com
State Changes Requested
Headers show

Commit Message

Johnson.Li July 12, 2016, 5:36 p.m. UTC
Ethernet header could be pushed to/stripped from packets with
flow action push_eth/pop_eth, origin packets will be treated as
payload even the packets are Ethernet packets.

Derived from work by Lorand Jakub and Simon Horman.
Cc: Lorand Jakab <lojakab@cisco.com>
Cc: Simon Horman <simon.horman@netronome.com>
---
This is extracted from l3 flow support paches currently
being targeted at net-next by Simon Horman.

Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: Johnson Li <johnson.li@intel.com>
diff mbox

Patch

diff --git a/datapath/actions.c b/datapath/actions.c
index 0c2927e..eda9106 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -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)) {
diff --git a/datapath/linux/compat/include/linux/skbuff.h b/datapath/linux/compat/include/linux/skbuff.h
index 376dfda..03ac37c 100644
--- a/datapath/linux/compat/include/linux/skbuff.h
+++ b/datapath/linux/compat/include/linux/skbuff.h
@@ -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)
 {