@@ -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)) {
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>