add classify session action set-sr-policy-index 37/8637/9
authorGabriel Ganne <gabriel.ganne@enea.com>
Mon, 2 Oct 2017 09:41:24 +0000 (11:41 +0200)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 10 Nov 2017 20:25:10 +0000 (20:25 +0000)
This allows to use the classifier to steer source routing packets instead
of using the "sr steer" command.
This way we can steer on anything instead of only the dst ip address.

test:
 * add add_node_next function to the VppPapiProvider class.
 * add simple test scenario using the classifier to steer packets with
   dest ip addr == a7::/8 to the source routing insert node.
 * use new interface indexes (3,4) instead of (0,1) to prevent a cleanup
   conflict with the other tests which attach a specific fib to the
   interface.

The test creates interfaces sepsrated from the other tests to prevent a
conflict in the cleaning of the ip6 fib index 1 which causes vpp not to
be able to find a default route on this table.

Change-Id: Ibacb30fab3ce53f0dfe848ca6a8cdf0d111d8336
Signed-off-by: Gabriel Ganne <gabriel.ganne@enea.com>
src/vnet/classify/classify.api
src/vnet/classify/vnet_classify.c
src/vnet/classify/vnet_classify.h
src/vnet/ip/ip_input_acl.c
src/vnet/srv6/sr_steering.md
test/test_srv6.py
test/vpp_papi_provider.py

index de9de77..9d666ee 100644 (file)
@@ -90,8 +90,13 @@ define classify_add_del_table_reply
            2: Classified IP packets will be looked up from the
               specified ipv6 fib table (configured by metadata as VRF id).
               Only valid for L3 input ACL node
+           3: Classified packet will be steered to source routig policy
+              of given index (in metadata).
+              This is only valid for IPv6 packets redirected to a source
+              routing node.
     @param metadata - valid only if action != 0
            VRF id if action is 1 or 2.
+           sr policy index if action is 3.
     @param match[] - for add, match value for session, required
 */
 autoreply define classify_add_del_session
index f2fe23b..d634550 100644 (file)
@@ -373,6 +373,8 @@ vnet_classify_entry_claim_resource (vnet_classify_entry_t *e)
     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
         fib_table_lock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
         break;
+    case CLASSIFY_ACTION_SET_SR_POLICY_INDEX:
+        break;
     }
 }
 
@@ -387,6 +389,8 @@ vnet_classify_entry_release_resource (vnet_classify_entry_t *e)
     case CLASSIFY_ACTION_SET_IP6_FIB_INDEX:
         fib_table_unlock (e->metadata, FIB_PROTOCOL_IP6, FIB_SOURCE_CLASSIFY);
         break;
+    case CLASSIFY_ACTION_SET_SR_POLICY_INDEX:
+        break;
     }
 }
 
@@ -2104,6 +2108,8 @@ int vnet_classify_add_del_session (vnet_classify_main_t * cm,
     e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6,
                                                      metadata,
                                                      FIB_SOURCE_CLASSIFY);
+  else if (e->action == CLASSIFY_ACTION_SET_SR_POLICY_INDEX)
+    e->metadata = metadata;
   else
     e->metadata = 0;
 
@@ -2172,6 +2178,8 @@ classify_session_command_fn (vlib_main_t * vm,
         action = 1;
       else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
         action = 2;
+      else if (unformat (input, "action set-sr-policy-index %d", &metadata))
+        action = 3;
       else
         {
           /* Try registered opaque-index unformat fns */
@@ -2217,7 +2225,7 @@ VLIB_CLI_COMMAND (classify_session_command, static) = {
     "classify session [hit-next|l2-hit-next|"
     "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
     "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
-    "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
+    "\n [action set-ip4-fib-id|set-ip6-fib-id|set-sr-policy-index <n>] [del]",
     .function = classify_session_command_fn,
 };
 
index 1eb5b14..c4a5a61 100644 (file)
@@ -66,6 +66,7 @@ typedef enum vnet_classify_action_t_
 {
   CLASSIFY_ACTION_SET_IP4_FIB_INDEX = 1,
   CLASSIFY_ACTION_SET_IP6_FIB_INDEX = 2,
+  CLASSIFY_ACTION_SET_SR_POLICY_INDEX = 3,
 } __attribute__ ((packed)) vnet_classify_action_t;
 
 struct _vnet_classify_main;
index b0b52ab..1dd5317 100644 (file)
@@ -288,6 +288,8 @@ ip_inacl_inline (vlib_main_t * vm,
                  if (e0->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX ||
                      e0->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
                    vnet_buffer (b0)->sw_if_index[VLIB_TX] = e0->metadata;
+                 else if (e0->action == CLASSIFY_ACTION_SET_SR_POLICY_INDEX)
+                   vnet_buffer (b0)->ip.adj_index[VLIB_TX] = e0->metadata;
                }
              else
                {
index cf446f8..7e91892 100644 (file)
@@ -1,5 +1,7 @@
 # Steering packets into a SR Policy     {#srv6_steering_doc}
 
+## steer packets uging the sr steering policy
+
 To steer packets in Transit into an SR policy (T.Insert, T.Encaps and T.Encaps.L2 behaviors), the user needs to create an 'sr steering policy'.
 
     sr steer l3 2001::/64 via sr policy index 1
@@ -9,3 +11,25 @@ To steer packets in Transit into an SR policy (T.Insert, T.Encaps and T.Encaps.L
     sr steer l2 TenGE0/1/0 via sr policy bsid cafe::1
 
 Disclaimer: The T.Encaps.L2 will steer L2 frames into an SR Policy. Notice that creating an SR steering policy for L2 frames will actually automatically *put the interface into promiscous mode*.
+
+## steer packets using the classifier
+
+Another way to steer packet is to use the classifier.
+
+First the user need to manually add the source routing node to the list of the
+ip6-inacl next nodes.
+Using the python api this can be donne with:
+
+    # jsonfiles = get list of json api files
+    vpp = VPP(jsonfiles)
+    vpp.add_node_next(node_name='ip6-inacl', next_name='sr-pl-rewrite-insert')
+
+Below is a classifier mask filtering all the packets from the interface
+TenGigabitEthernet5/0/0 on ip version and moving all ipv6 packets to the
+sr-pl-rewrite-insert node (dropping the others) and applying the source routing
+index 2.
+In essence, this means "apply this sr policy to all the packets from this interface)
+
+    vpp# classify table miss-next 0 current-data-flag 1  mask hex f000000000000000 skip 0
+    vpp# classify session acl-hit-next 1 table-index 0 match hex 6000000000000000 action set-sr-policy-index 2
+    vpp# set interface input acl intfc TenGigabitEthernet5/0/0 ip6-table 0
index a31b30e..4a2ef01 100644 (file)
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 import unittest
+import binascii
 from socket import AF_INET6
 
 from framework import VppTestCase, VppTestRunner
@@ -1165,6 +1166,150 @@ class TestSRv6(VppTestCase):
         # cleanup interfaces
         self.teardown_interfaces()
 
+    def test_SRv6_T_Insert_Classifier(self):
+        """ Test SRv6 Transit.Insert behavior (IPv6 only).
+            steer packets using the classifier
+        """
+        # send traffic to one destination interface
+        # source and destination are IPv6 only
+        self.setup_interfaces(ipv6=[False, False, False, True, True])
+
+        # configure FIB entries
+        route = VppIpRoute(self, "a4::", 64,
+                           [VppRoutePath(self.pg4.remote_ip6,
+                                         self.pg4.sw_if_index,
+                                         proto=DpoProto.DPO_PROTO_IP6)],
+                           is_ip6=1)
+        route.add_vpp_config()
+
+        # configure encaps IPv6 source address
+        # needs to be done before SR Policy config
+        # TODO: API?
+        self.vapi.cli("set sr encaps source addr a3::")
+
+        bsid = 'a3::9999:1'
+        # configure SRv6 Policy
+        # Note: segment list order: first -> last
+        sr_policy = VppSRv6Policy(
+            self, bsid=bsid,
+            is_encap=0,
+            sr_type=SRv6PolicyType.SR_POLICY_TYPE_DEFAULT,
+            weight=1, fib_table=0,
+            segments=['a4::', 'a5::', 'a6::c7'],
+            source='a3::')
+        sr_policy.add_vpp_config()
+        self.sr_policy = sr_policy
+
+        # log the sr policies
+        self.logger.info(self.vapi.cli("show sr policies"))
+
+        # add classify table
+        # mask on dst ip address prefix a7::/8
+        mask = '{:0<16}'.format('ff')
+        r = self.vapi.classify_add_del_table(
+            1,
+            binascii.unhexlify(mask),
+            match_n_vectors=(len(mask) - 1) // 32 + 1,
+            current_data_flag=1,
+            skip_n_vectors=2)  # data offset
+        self.assertIsNotNone(r, msg='No response msg for add_del_table')
+        table_index = r.new_table_index
+
+        # add the source routign node as a ip6 inacl netxt node
+        r = self.vapi.add_node_next('ip6-inacl',
+                                    'sr-pl-rewrite-insert')
+        inacl_next_node_index = r.node_index
+
+        match = '{:0<16}'.format('a7')
+        r = self.vapi.classify_add_del_session(
+            1,
+            table_index,
+            binascii.unhexlify(match),
+            hit_next_index=inacl_next_node_index,
+            action=3,
+            metadata=0)  # sr policy index
+        self.assertIsNotNone(r, msg='No response msg for add_del_session')
+
+        # log the classify table used in the steering policy
+        self.logger.info(self.vapi.cli("show classify table"))
+
+        r = self.vapi.input_acl_set_interface(
+            is_add=1,
+            sw_if_index=self.pg3.sw_if_index,
+            ip6_table_index=table_index)
+        self.assertIsNotNone(r,
+                             msg='No response msg for input_acl_set_interface')
+
+        # log the ip6 inacl
+        self.logger.info(self.vapi.cli("show inacl type ip6"))
+
+        # create packets
+        count = len(self.pg_packet_sizes)
+        dst_inner = 'a7::1234'
+        pkts = []
+
+        # create IPv6 packets without SRH
+        packet_header = self.create_packet_header_IPv6(dst_inner)
+        # create traffic stream pg3->pg4
+        pkts.extend(self.create_stream(self.pg3, self.pg4, packet_header,
+                                       self.pg_packet_sizes, count))
+
+        # create IPv6 packets with SRH
+        # packets with segments-left 1, active segment a7::
+        packet_header = self.create_packet_header_IPv6_SRH(
+            sidlist=['a8::', 'a7::', 'a6::'],
+            segleft=1)
+        # create traffic stream pg3->pg4
+        pkts.extend(self.create_stream(self.pg3, self.pg4, packet_header,
+                                       self.pg_packet_sizes, count))
+
+        # send packets and verify received packets
+        self.send_and_verify_pkts(self.pg3, pkts, self.pg4,
+                                  self.compare_rx_tx_packet_T_Insert)
+
+        # remove the interface l2 input feature
+        r = self.vapi.input_acl_set_interface(
+            is_add=0,
+            sw_if_index=self.pg3.sw_if_index,
+            ip6_table_index=table_index)
+        self.assertIsNotNone(r,
+                             msg='No response msg for input_acl_set_interface')
+
+        # log the ip6 inacl after cleaning
+        self.logger.info(self.vapi.cli("show inacl type ip6"))
+
+        # log the localsid counters
+        self.logger.info(self.vapi.cli("show sr localsid"))
+
+        # remove classifier SR steering
+        # classifier_steering.remove_vpp_config()
+        self.logger.info(self.vapi.cli("show sr steering policies"))
+
+        # remove SR Policies
+        self.sr_policy.remove_vpp_config()
+        self.logger.info(self.vapi.cli("show sr policies"))
+
+        # remove classify session and table
+        r = self.vapi.classify_add_del_session(
+            0,
+            table_index,
+            binascii.unhexlify(match))
+        self.assertIsNotNone(r, msg='No response msg for add_del_session')
+
+        r = self.vapi.classify_add_del_table(
+            0,
+            binascii.unhexlify(mask),
+            table_index=table_index)
+        self.assertIsNotNone(r, msg='No response msg for add_del_table')
+
+        self.logger.info(self.vapi.cli("show classify table"))
+
+        # remove FIB entries
+        # done by tearDown
+
+        # cleanup interfaces
+        self.teardown_interfaces()
+
     def compare_rx_tx_packet_T_Encaps(self, tx_pkt, rx_pkt):
         """ Compare input and output packet after passing T.Encaps
 
index 893ef0d..66126c5 100644 (file)
@@ -2753,3 +2753,13 @@ class VppPapiProvider(object):
         return self.api(
             self.papi.bier_disp_entry_dump,
             {'bde_tbl_id': bdti})
+
+    def add_node_next(self, node_name, next_name):
+        """ Set the next node for a given node request
+
+        :param node_name:
+        :param next_name:
+        """
+        return self.api(self.papi.add_node_next,
+                        {'node_name': node_name,
+                         'next_name': next_name})