diff mbox

[ovs-dev,CudaMailTagged,RFC,v2,4/5] Add push_nsh/pop_nsh flow actions for kernel data path

Message ID 1468344999-11949-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
Network Service Header is pushed to/stripped from packets with
the data path flow actions push_nsh and pop_nsh.

Signed-off-by: Johnson Li <johnson.li@intel.com>
diff mbox

Patch

diff --git a/datapath/actions.c b/datapath/actions.c
index 7ef1bae..0c2927e 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -29,6 +29,7 @@ 
 #include <linux/in6.h>
 #include <linux/if_arp.h>
 #include <linux/if_vlan.h>
+#include <linux/if_ether.h>
 
 #include <net/dst.h>
 #include <net/ip.h>
@@ -37,6 +38,7 @@ 
 #include <net/dsfield.h>
 #include <net/mpls.h>
 #include <net/sctp/checksum.h>
+#include <net/nsh.h>
 
 #include "datapath.h"
 #include "conntrack.h"
@@ -252,6 +254,64 @@  static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
 			     ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
 }
 
+static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key)
+{
+	struct nsh_hdr *nsh_hdr = NULL;
+	u16 length = 0;
+
+	nsh_hdr = (struct nsh_hdr *)(skb->data);
+	length  = nsh_hdr->base.length << 2;
+
+	switch (nsh_hdr->base.next_proto) {
+	case NSH_P_IPV4:
+		skb->protocol = htons(ETH_P_IP);
+		key->eth.type = htons(ETH_P_IP);
+		break;
+	case NSH_P_IPV6:
+		skb->protocol = htons(ETH_P_IPV6);
+		key->eth.type = htons(ETH_P_IPV6);
+		break;
+	case NSH_P_ETHERNET:
+		skb->protocol = htons(ETH_P_TEB);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	__skb_pull(skb, length);
+	skb_reset_mac_header(skb);
+	skb_reset_mac_len(skb);
+
+	memset(&key->nsh, 0, sizeof(key->nsh));
+
+	return 0;
+}
+
+static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
+		    const struct ovs_action_push_nsh *nsh)
+{
+	if (nsh->len > 0 && nsh->len <= 256) {
+		struct nsh_hdr *nsh_hdr = NULL;
+
+		if (skb_cow_head(skb, nsh->len) < 0)
+			return -ENOMEM;
+
+		skb_push(skb, nsh->len);
+		nsh_hdr = (struct nsh_hdr *)(skb->data);
+		memcpy(nsh_hdr, nsh->header, nsh->len);
+
+		if (!skb->inner_protocol)
+			ovs_skb_set_inner_protocol(skb, skb->protocol);
+
+		skb->protocol = htons(ETH_P_NSH); /* 0x894F */
+		key->eth.type = htons(ETH_P_NSH);
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /* 'src' is already properly masked. */
 static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_)
 {
@@ -1150,6 +1210,14 @@  static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 			if (err)
 				return err == -EINPROGRESS ? 0 : err;
 			break;
+
+		case OVS_ACTION_ATTR_PUSH_NSH:
+			err = push_nsh(skb, key, nla_data(a));
+			break;
+
+		case OVS_ACTION_ATTR_POP_NSH:
+			err = pop_nsh(skb, key);
+			break;
 		}
 
 		if (unlikely(err)) {