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...@intel.com>

diff --git a/datapath/actions.c b/datapath/actions.c
index dcf8591..69f5d2a 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)
+                       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_)
 {
@@ -1079,6 +1139,14 @@ static int do_execute_actions(struct datapath *dp, 
struct sk_buff *skb,
                        err = pop_vlan(skb, key);
                        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;
+
                case OVS_ACTION_ATTR_RECIRC:
                        err = execute_recirc(dp, skb, key, a, rem);
                        if (nla_is_last(a, rem)) {
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 15685c7..4e0aed2 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -2375,6 +2375,8 @@ static int __ovs_nla_copy_actions(struct net *net, const 
struct nlattr *attr,
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
                        [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
                        [OVS_ACTION_ATTR_CT] = (u32)-1,
+                       [OVS_ACTION_ATTR_PUSH_NSH] = sizeof(struct 
ovs_action_push_nsh),
+                       [OVS_ACTION_ATTR_POP_NSH] = 0,
                };
                const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
@@ -2427,6 +2429,10 @@ static int __ovs_nla_copy_actions(struct net *net, const 
struct nlattr *attr,
                        vlan_tci = vlan->vlan_tci;
                        break;
 
+               case OVS_ACTION_ATTR_PUSH_NSH:
+               case OVS_ACTION_ATTR_POP_NSH:
+                       break;
+
                case OVS_ACTION_ATTR_RECIRC:
                        break;
 
diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
b/datapath/linux/compat/include/linux/openvswitch.h
index 6cde92a..b6b6e47 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -646,6 +646,17 @@ struct ovs_action_push_vlan {
        __be16 vlan_tci;        /* 802.1Q TCI (VLAN ID and priority). */
 };
 
+/**
+ * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
+ * @len: length of NSH header. Differs since different metadata type.
+ * @header: RAW value for the NSH header.
+ */
+#define NSH_HEADER_LEN_MAX     256
+struct ovs_action_push_nsh {
+       uint16_t len;
+       uint8_t header[NSH_HEADER_LEN_MAX]; /* NSH header */
+};
+
 /* Data path hash algorithm for computing Datapath hash.
  *
  * The algorithm type only specifies the fields in a flow
@@ -788,6 +799,8 @@ enum ovs_nat_attr {
  * is no MPLS label stack, as determined by ethertype, no action is taken.
  * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related
  * entries in the flow key.
+ * @OVS_ACTION_ATTR_PUSH_NSH: Push a Network Service Header into the packets.
+ * @OVS_ACTION_ATTR_POP_NSH: Strip the Network Service Header from packets.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -819,6 +832,8 @@ enum ovs_action_attr {
                                       * The data must be zero for the unmasked
                                       * bits. */
        OVS_ACTION_ATTR_CT,           /* Nested OVS_CT_ATTR_* . */
+       OVS_ACTION_ATTR_PUSH_NSH,     /* struct ovs_action_push_nsh. */
+       OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
 
 #ifndef __KERNEL__
        OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 61a939a..b776f60 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -4160,6 +4160,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
*packets_,
     case OVS_ACTION_ATTR_SAMPLE:
     case OVS_ACTION_ATTR_HASH:
     case OVS_ACTION_ATTR_UNSPEC:
+    case OVS_ACTION_ATTR_PUSH_NSH:
+    case OVS_ACTION_ATTR_POP_NSH:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index c4f24c7..d0bff3e 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1147,6 +1147,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_UNSPEC:
+    case OVS_ACTION_ATTR_PUSH_NSH:
+    case OVS_ACTION_ATTR_POP_NSH:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 2de81dc..e37402c 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -505,6 +505,8 @@ requires_datapath_assistance(const struct nlattr *a)
     case OVS_ACTION_ATTR_HASH:
     case OVS_ACTION_ATTR_PUSH_MPLS:
     case OVS_ACTION_ATTR_POP_MPLS:
+    case OVS_ACTION_ATTR_PUSH_NSH:
+    case OVS_ACTION_ATTR_POP_NSH:
         return false;
 
     case OVS_ACTION_ATTR_UNSPEC:
@@ -627,6 +629,10 @@ odp_execute_actions(void *dp, struct dp_packet_batch 
*batch, bool steal,
             }
             break;
 
+        case OVS_ACTION_ATTR_PUSH_NSH:
+        case OVS_ACTION_ATTR_POP_NSH:
+            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 8a2e521..f970e86 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -120,6 +120,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_NSH: return sizeof(struct ovs_action_push_nsh);
+    case OVS_ACTION_ATTR_POP_NSH: return 0;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -849,6 +851,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     case OVS_ACTION_ATTR_CT:
         format_odp_conntrack_action(ds, a);
         break;
+    case OVS_ACTION_ATTR_PUSH_NSH:
+    case OVS_ACTION_ATTR_POP_NSH:
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 982e676..d63911d 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1174,6 +1174,8 @@ dpif_sflow_read_actions(const struct flow *flow,
            dpif_sflow_pop_mpls_lse(sflow_actions);
            break;
        }
+       case OVS_ACTION_ATTR_PUSH_NSH:
+       case OVS_ACTION_ATTR_POP_NSH:
        case OVS_ACTION_ATTR_SAMPLE:
        case OVS_ACTION_ATTR_UNSPEC:
        case __OVS_ACTION_ATTR_MAX:
-- 
1.8.4.2

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to