sr: support define src ipv6 per encap policy

Can to define src ip of outer IPv6 Hdr for each encap policy.
Along with that, I decided to develop it as API version V2.
This is useful in the SRv6 MUP case.
For example, it will be possible to handle multiple UPF destinations.

Type: feature
Change-Id: I44ff7b54e8868619069621ab53e194e2c7a17435
Signed-off-by: Takeru Hayasaka <hayatake396@gmail.com>
diff --git a/src/vnet/srv6/sr.api b/src/vnet/srv6/sr.api
index 08ae9fa..8bebcd1 100644
--- a/src/vnet/srv6/sr.api
+++ b/src/vnet/srv6/sr.api
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-option version = "2.0.0";
+option version = "2.1.0";
 
 import "vnet/interface_types.api";
 import "vnet/ip/ip_types.api";
@@ -109,6 +109,65 @@
   vl_api_srv6_sid_list_t sids;
 };
 
+enum sr_policy_type : u8
+{
+  SR_API_POLICY_TYPE_DEFAULT = 0,
+  SR_API_POLICY_TYPE_SPRAY = 1,
+  SR_API_POLICY_TYPE_TEF = 2,
+};
+
+/** \brief IPv6 SR policy add
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param bsid is the bindingSID of the SR Policy
+    @param weight is the weight of the sid list. optional.
+    @param is_encap is the behavior of the SR policy. (0.SRH insert // 1.Encapsulation)
+    @param type is the SR policy param. (0.Default // 1.Spray // 2.Tef)
+    @param fib_table is the VRF where to install the FIB entry for the BSID
+    @param sids is a srv6_sid_list object
+    @param encap_src is a encaps IPv6 source addr. optional.
+*/
+autoreply define sr_policy_add_v2
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ip6_address_t bsid_addr;
+  u32 weight;
+  bool is_encap;
+  vl_api_sr_policy_type_t type [default=0x0];
+  u32 fib_table;
+  vl_api_srv6_sid_list_t sids;
+  vl_api_ip6_address_t encap_src;
+  option status="in_progress";
+};
+
+/** \brief IPv6 SR policy modification
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param bsid is the bindingSID of the SR Policy
+    @param sr_policy_index is the index of the SR policy
+    @param fib_table is the VRF where to install the FIB entry for the BSID
+    @param operation is the operation to perform (among the top ones)
+    @param sl_index is the index of the Segment List to modify/delete
+    @param weight is the weight of the sid list. optional.
+    @param sids is a srv6_sid_list object
+    @param encap_src is a encaps IPv6 source addr. optional.
+*/
+autoreply define sr_policy_mod_v2
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ip6_address_t bsid_addr;
+  u32 sr_policy_index;
+  u32 fib_table;
+  vl_api_sr_policy_op_t operation;
+  u32 sl_index;
+  u32 weight;
+  vl_api_srv6_sid_list_t sids;
+  vl_api_ip6_address_t encap_src;
+  option status="in_progress";
+};
+
 /** \brief IPv6 SR policy deletion
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
@@ -248,6 +307,29 @@
   vl_api_srv6_sid_list_t sid_lists[num_sid_lists];
 };
 
+/** \brief Dump the list of SR policies v2
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+*/
+define sr_policies_v2_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+define sr_policies_v2_details
+{
+  u32 context;
+  vl_api_ip6_address_t bsid;
+  vl_api_ip6_address_t encap_src;
+  vl_api_sr_policy_type_t type;
+  bool is_encap;
+  u32 fib_table;
+  u8 num_sid_lists;
+  vl_api_srv6_sid_list_t sid_lists[num_sid_lists];
+  option in_progress;
+};
+
 /** \brief Dump the list of SR policies along with actual segment list index on VPP
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
diff --git a/src/vnet/srv6/sr.h b/src/vnet/srv6/sr.h
index 02ccead..7d8aa73 100644
--- a/src/vnet/srv6/sr.h
+++ b/src/vnet/srv6/sr.h
@@ -112,6 +112,8 @@
 
   u8 is_encap;				/**< Mode (0 is SRH insert, 1 Encaps) */
 
+  ip6_address_t encap_src;
+
   u16 plugin;
   void *plugin_mem;
 } ip6_sr_policy_t;
@@ -345,11 +347,12 @@
 			     sr_p_plugin_callback_t * removal_fn);
 
 extern int sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments,
-			  u32 weight, u8 type, u32 fib_table, u8 is_encap,
-			  u16 plugin, void *plugin_mem);
-extern int sr_policy_mod (ip6_address_t * bsid, u32 index, u32 fib_table,
-			  u8 operation, ip6_address_t * segments,
-			  u32 sl_index, u32 weight);
+			  ip6_address_t *encap_src, u32 weight, u8 type,
+			  u32 fib_table, u8 is_encap, u16 plugin,
+			  void *plugin_mem);
+extern int sr_policy_mod (ip6_address_t *bsid, u32 index, u32 fib_table,
+			  u8 operation, ip6_address_t *segments,
+			  ip6_address_t *encap_src, u32 sl_index, u32 weight);
 extern int sr_policy_del (ip6_address_t * bsid, u32 index);
 
 extern int
diff --git a/src/vnet/srv6/sr_api.c b/src/vnet/srv6/sr_api.c
index 1b95125..5594fed 100644
--- a/src/vnet/srv6/sr_api.c
+++ b/src/vnet/srv6/sr_api.c
@@ -82,17 +82,16 @@
 
   ip6_address_decode (mp->bsid_addr, &bsid_addr);
 
-/*
- * sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments,
- *                u32 weight, u8 behavior, u32 fib_table, u8 is_encap,
- *                u16 behavior, void *plugin_mem)
- */
+  /*
+   * sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments,
+   *                ip6_address_t *encap_src,
+   *                u32 weight, u8 behavior, u32 fib_table, u8 is_encap,
+   *                u16 behavior, void *plugin_mem)
+   */
   int rv = 0;
-  rv = sr_policy_add (&bsid_addr,
-		      segments,
-		      ntohl (mp->sids.weight),
-		      mp->is_spray, ntohl (mp->fib_table), mp->is_encap, 0,
-		      NULL);
+  rv =
+    sr_policy_add (&bsid_addr, segments, NULL, ntohl (mp->sids.weight),
+		   mp->is_spray, ntohl (mp->fib_table), mp->is_encap, 0, NULL);
   vec_free (segments);
 
   REPLY_MACRO (VL_API_SR_POLICY_ADD_REPLY);
@@ -115,18 +114,93 @@
   ip6_address_decode (mp->bsid_addr, &bsid_addr);
 
   int rv = 0;
-/*
- * int
- * sr_policy_mod(ip6_address_t *bsid, u32 index, u32 fib_table,
- *               u8 operation, ip6_address_t *segments, u32 sl_index,
- *               u32 weight, u8 is_encap)
- */
-  rv = sr_policy_mod (&bsid_addr,
-		      ntohl (mp->sr_policy_index),
-		      ntohl (mp->fib_table),
-		      mp->operation,
-		      segments, ntohl (mp->sl_index),
-		      ntohl (mp->sids.weight));
+  /*
+   * int
+   * sr_policy_mod(ip6_address_t *bsid, u32 index, u32 fib_table,
+   *               u8 operation, ip6_address_t *segments,
+   *               ip6_address_t *encap_src, u32 sl_index,
+   *               u32 weight, u8 is_encap)
+   */
+  rv = sr_policy_mod (&bsid_addr, ntohl (mp->sr_policy_index),
+		      ntohl (mp->fib_table), mp->operation, segments, NULL,
+		      ntohl (mp->sl_index), ntohl (mp->sids.weight));
+  vec_free (segments);
+
+  REPLY_MACRO (VL_API_SR_POLICY_MOD_REPLY);
+}
+
+static void
+vl_api_sr_policy_add_v2_t_handler (vl_api_sr_policy_add_v2_t *mp)
+{
+  vl_api_sr_policy_add_v2_reply_t *rmp;
+  ip6_address_t *segments = 0, *seg;
+  ip6_address_t bsid_addr;
+  ip6_address_t encap_src;
+
+  int i;
+  for (i = 0; i < mp->sids.num_sids; i++)
+    {
+      vec_add2 (segments, seg, 1);
+      ip6_address_decode (mp->sids.sids[i], seg);
+    }
+
+  ip6_address_decode (mp->bsid_addr, &bsid_addr);
+  ip6_address_decode (mp->encap_src, &encap_src);
+
+  if (ip6_address_is_zero (&encap_src))
+    {
+      encap_src = *sr_get_encaps_source ();
+    }
+  /*
+   * sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments,
+   *                ip6_address_t *encap_src,
+   *                u32 weight, u8 behavior, u32 fib_table, u8 is_encap,
+   *                u16 behavior, void *plugin_mem)
+   */
+  int rv = 0;
+  rv =
+    sr_policy_add (&bsid_addr, segments, &encap_src, ntohl (mp->sids.weight),
+		   mp->type, ntohl (mp->fib_table), mp->is_encap, 0, NULL);
+  vec_free (segments);
+
+  REPLY_MACRO (VL_API_SR_POLICY_ADD_REPLY);
+}
+
+static void
+vl_api_sr_policy_mod_v2_t_handler (vl_api_sr_policy_mod_v2_t *mp)
+{
+  vl_api_sr_policy_mod_v2_reply_t *rmp;
+  ip6_address_t *segments = 0, *seg;
+  ip6_address_t bsid_addr;
+  ip6_address_t encap_src;
+
+  int i;
+  for (i = 0; i < mp->sids.num_sids; i++)
+    {
+      vec_add2 (segments, seg, 1);
+      ip6_address_decode (mp->sids.sids[i], seg);
+    }
+
+  ip6_address_decode (mp->bsid_addr, &bsid_addr);
+  ip6_address_decode (mp->encap_src, &encap_src);
+
+  if (ip6_address_is_zero (&encap_src))
+    {
+      encap_src = *sr_get_encaps_source ();
+    }
+
+  int rv = 0;
+  /*
+   * int
+   * sr_policy_mod(ip6_address_t *bsid, u32 index, u32 fib_table,
+   *               u8 operation, ip6_address_t *segments,
+   *               ip6_address_t *encap_src, u32 sl_index,
+   *               u32 weight, u8 is_encap)
+   */
+  rv =
+    sr_policy_mod (&bsid_addr, ntohl (mp->sr_policy_index),
+		   ntohl (mp->fib_table), mp->operation, segments, &encap_src,
+		   ntohl (mp->sl_index), ntohl (mp->sids.weight));
   vec_free (segments);
 
   REPLY_MACRO (VL_API_SR_POLICY_MOD_REPLY);
@@ -387,7 +461,68 @@
   /* *INDENT-ON* */
 }
 
+static void
+send_sr_policies_v2_details (ip6_sr_policy_t *t, vl_api_registration_t *reg,
+			     u32 context)
+{
+  vl_api_sr_policies_v2_details_t *rmp;
+  ip6_sr_main_t *sm = &sr_main;
 
+  u32 *sl_index, slidx = 0;
+  ip6_sr_sl_t *segment_list = 0;
+  ip6_address_t *segment;
+  vl_api_srv6_sid_list_t *api_sid_list;
+
+  rmp = vl_msg_api_alloc (sizeof (*rmp) + vec_len (t->segments_lists) *
+					    sizeof (vl_api_srv6_sid_list_t));
+  clib_memset (rmp, 0,
+	       (sizeof (*rmp) + vec_len (t->segments_lists) *
+				  sizeof (vl_api_srv6_sid_list_t)));
+
+  rmp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_SR_POLICIES_V2_DETAILS);
+  ip6_address_encode (&t->bsid, rmp->bsid);
+  ip6_address_encode (&t->encap_src, rmp->encap_src);
+  rmp->is_encap = t->is_encap;
+  rmp->type = t->type;
+  rmp->fib_table = htonl (t->fib_table);
+  rmp->num_sid_lists = vec_len (t->segments_lists);
+
+  /* Fill in all the segments lists */
+  vec_foreach (sl_index, t->segments_lists)
+    {
+      segment_list = pool_elt_at_index (sm->sid_lists, *sl_index);
+
+      api_sid_list = &rmp->sid_lists[sl_index - t->segments_lists];
+
+      api_sid_list->num_sids = vec_len (segment_list->segments);
+      api_sid_list->weight = htonl (segment_list->weight);
+      slidx = 0;
+      vec_foreach (segment, segment_list->segments)
+	{
+	  ip6_address_encode (segment, api_sid_list->sids[slidx++]);
+	}
+    }
+
+  rmp->context = context;
+  vl_api_send_msg (reg, (u8 *) rmp);
+}
+
+static void
+vl_api_sr_policies_v2_dump_t_handler (vl_api_sr_policies_v2_dump_t *mp)
+{
+  vl_api_registration_t *reg;
+  ip6_sr_main_t *sm = &sr_main;
+  ip6_sr_policy_t *t;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  pool_foreach (t, sm->sr_policies)
+    {
+      send_sr_policies_v2_details (t, reg, mp->context);
+    }
+}
 
 static void send_sr_policies_details_with_sl_index
   (ip6_sr_policy_t * t, vl_api_registration_t * reg, u32 context)
diff --git a/src/vnet/srv6/sr_policy_rewrite.c b/src/vnet/srv6/sr_policy_rewrite.c
index 8ad8571..69a4d5e 100644
--- a/src/vnet/srv6/sr_policy_rewrite.c
+++ b/src/vnet/srv6/sr_policy_rewrite.c
@@ -193,11 +193,12 @@
  * @brief SR rewrite string computation for IPv6 encapsulation (inline)
  *
  * @param sl is a vector of IPv6 addresses composing the Segment List
+ * @param src_v6addr is a encaps IPv6 source addr
  *
  * @return precomputed rewrite string for encapsulation
  */
 static inline u8 *
-compute_rewrite_encaps (ip6_address_t *sl, u8 type)
+compute_rewrite_encaps (ip6_address_t *sl, ip6_address_t *src_v6addr, u8 type)
 {
   ip6_header_t *iph;
   ip6_sr_header_t *srh;
@@ -225,8 +226,8 @@
   iph = (ip6_header_t *) rs;
   iph->ip_version_traffic_class_and_flow_label =
     clib_host_to_net_u32 (0 | ((6 & 0xF) << 28));
-  iph->src_address.as_u64[0] = sr_pr_encaps_src.as_u64[0];
-  iph->src_address.as_u64[1] = sr_pr_encaps_src.as_u64[1];
+  iph->src_address.as_u64[0] = src_v6addr->as_u64[0];
+  iph->src_address.as_u64[1] = src_v6addr->as_u64[1];
   iph->payload_length = header_length - IPv6_DEFAULT_HEADER_LENGTH;
   iph->protocol = IP_PROTOCOL_IPV6;
   iph->hop_limit = sr_pr_encaps_hop_limit;
@@ -370,18 +371,20 @@
  *
  * @param sr_policy is the SR policy where the SL will be added
  * @param sl is a vector of IPv6 addresses composing the Segment List
+ * @param encap_src is a encaps IPv6 source addr. optional.
  * @param weight is the weight of the SegmentList (for load-balancing purposes)
  * @param is_encap represents the mode (SRH insertion vs Encapsulation)
  *
  * @return pointer to the just created segment list
  */
 static inline ip6_sr_sl_t *
-create_sl (ip6_sr_policy_t * sr_policy, ip6_address_t * sl, u32 weight,
-	   u8 is_encap)
+create_sl (ip6_sr_policy_t *sr_policy, ip6_address_t *sl,
+	   ip6_address_t *encap_src, u32 weight, u8 is_encap)
 {
   ip6_sr_main_t *sm = &sr_main;
   ip6_sr_sl_t *segment_list;
   sr_policy_fn_registration_t *plugin = 0;
+  ip6_address_t encap_srcv6 = sr_pr_encaps_src;
 
   pool_get (sm->sid_lists, segment_list);
   clib_memset (segment_list, 0, sizeof (*segment_list));
@@ -400,8 +403,14 @@
 
   if (is_encap)
     {
-      segment_list->rewrite = compute_rewrite_encaps (sl, sr_policy->type);
+      if (encap_src)
+	{
+	  clib_memcpy_fast (&encap_srcv6, encap_src, sizeof (ip6_address_t));
+	}
+      segment_list->rewrite =
+	compute_rewrite_encaps (sl, &encap_srcv6, sr_policy->type);
       segment_list->rewrite_bsid = segment_list->rewrite;
+      sr_policy->encap_src = encap_srcv6;
     }
   else
     {
@@ -660,17 +669,19 @@
  *
  * @param bsid is the bindingSID of the SR Policy
  * @param segments is a vector of IPv6 address composing the segment list
+ * @param encap_src is a encaps IPv6 source addr. optional.
  * @param weight is the weight of the sid list. optional.
  * @param behavior is the behavior of the SR policy. (default//spray)
  * @param fib_table is the VRF where to install the FIB entry for the BSID
- * @param is_encap (bool) whether SR policy should behave as Encap/SRH Insertion
+ * @param is_encap (bool) whether SR policy should behave as Encap/SRH
+ * Insertion
  *
  * @return 0 if correct, else error
  */
 int
-sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments, u32 weight,
-	       u8 type, u32 fib_table, u8 is_encap, u16 plugin,
-	       void *ls_plugin_mem)
+sr_policy_add (ip6_address_t *bsid, ip6_address_t *segments,
+	       ip6_address_t *encap_src, u32 weight, u8 type, u32 fib_table,
+	       u8 is_encap, u16 plugin, void *ls_plugin_mem)
 {
   ip6_sr_main_t *sm = &sr_main;
   ip6_sr_policy_t *sr_policy = 0;
@@ -726,7 +737,7 @@
 	     NULL);
 
   /* Create a segment list and add the index to the SR policy */
-  create_sl (sr_policy, segments, weight, is_encap);
+  create_sl (sr_policy, segments, encap_src, weight, is_encap);
 
   /* If FIB doesnt exist, create them */
   if (sm->fib_table_ip6 == (u32) ~ 0)
@@ -856,6 +867,7 @@
  * @param fib_table is the VRF where to install the FIB entry for the BSID
  * @param operation is the operation to perform (among the top ones)
  * @param segments is a vector of IPv6 address composing the segment list
+ * @param encap_src is a encaps IPv6 source addr. optional.
  * @param sl_index is the index of the Segment List to modify/delete
  * @param weight is the weight of the sid list. optional.
  * @param is_encap Mode. Encapsulation or SRH insertion.
@@ -863,8 +875,8 @@
  * @return 0 if correct, else error
  */
 int
-sr_policy_mod (ip6_address_t * bsid, u32 index, u32 fib_table,
-	       u8 operation, ip6_address_t * segments, u32 sl_index,
+sr_policy_mod (ip6_address_t *bsid, u32 index, u32 fib_table, u8 operation,
+	       ip6_address_t *segments, ip6_address_t *encap_src, u32 sl_index,
 	       u32 weight)
 {
   ip6_sr_main_t *sm = &sr_main;
@@ -889,8 +901,8 @@
   if (operation == 1)		/* Add SR List to an existing SR policy */
     {
       /* Create the new SL */
-      segment_list =
-	create_sl (sr_policy, segments, weight, sr_policy->is_encap);
+      segment_list = create_sl (sr_policy, segments, encap_src, weight,
+				sr_policy->is_encap);
 
       /* Create a new LB DPO */
       if (sr_policy->type == SR_POLICY_TYPE_DEFAULT)
@@ -963,7 +975,7 @@
   int rv = -1;
   char is_del = 0, is_add = 0, is_mod = 0;
   char policy_set = 0;
-  ip6_address_t bsid, next_address;
+  ip6_address_t bsid, next_address, src_v6addr;
   u32 sr_policy_index = (u32) ~ 0, sl_index = (u32) ~ 0;
   u32 weight = (u32) ~ 0, fib_table = (u32) ~ 0;
   ip6_address_t *segments = 0, *this_seg;
@@ -972,6 +984,7 @@
   u8 type = SR_POLICY_TYPE_DEFAULT;
   u16 behavior = 0;
   void *ls_plugin_mem = 0;
+  ip6_address_t *encap_src = 0;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     {
@@ -995,6 +1008,10 @@
 	  clib_memcpy_fast (this_seg->as_u8, next_address.as_u8,
 			    sizeof (*this_seg));
 	}
+      else if (unformat (input, "v6src %U", unformat_ip6_address, &src_v6addr))
+	{
+	  encap_src = &src_v6addr;
+	}
       else if (unformat (input, "add sl"))
 	operation = 1;
       else if (unformat (input, "del sl index %d", &sl_index))
@@ -1059,8 +1076,8 @@
       if (vec_len (segments) == 0)
 	return clib_error_return (0, "No Segment List specified");
 
-      rv = sr_policy_add (&bsid, segments, weight, type, fib_table, is_encap,
-			  behavior, ls_plugin_mem);
+      rv = sr_policy_add (&bsid, segments, encap_src, weight, type, fib_table,
+			  is_encap, behavior, ls_plugin_mem);
 
       vec_free (segments);
     }
@@ -1078,9 +1095,9 @@
       if (operation == 3 && weight == (u32) ~ 0)
 	return clib_error_return (0, "No new weight for the SL specified");
 
-      rv = sr_policy_mod ((sr_policy_index != (u32) ~ 0 ? NULL : &bsid),
+      rv = sr_policy_mod ((sr_policy_index != (u32) ~0 ? NULL : &bsid),
 			  sr_policy_index, fib_table, operation, segments,
-			  sl_index, weight);
+			  encap_src, sl_index, weight);
 
       if (segments)
 	vec_free (segments);
@@ -1170,6 +1187,11 @@
     vlib_cli_output (vm, "\tBehavior: %s",
 		     (sr_policy->is_encap ? "Encapsulation" :
 		      "SRH insertion"));
+    if (sr_policy->is_encap)
+      {
+	vlib_cli_output (vm, "\tEncapSrcIP: %U", format_ip6_address,
+			 &sr_policy->encap_src);
+      }
     switch (sr_policy->type)
       {
       case SR_POLICY_TYPE_SPRAY:
diff --git a/src/vnet/srv6/sr_test.c b/src/vnet/srv6/sr_test.c
index 85f64e1..be89859 100644
--- a/src/vnet/srv6/sr_test.c
+++ b/src/vnet/srv6/sr_test.c
@@ -80,6 +80,18 @@
 }
 
 static int
+api_sr_policy_mod_v2 (vat_main_t *vam)
+{
+  return -1;
+}
+
+static int
+api_sr_policy_add_v2 (vat_main_t *vam)
+{
+  return -1;
+}
+
+static int
 api_sr_localsids_dump (vat_main_t *vam)
 {
   return -1;
@@ -92,6 +104,12 @@
 }
 
 static int
+api_sr_policies_v2_dump (vat_main_t *vam)
+{
+  return -1;
+}
+
+static int
 api_sr_policies_with_sl_index_dump (vat_main_t *vam)
 {
   return -1;
@@ -109,6 +127,11 @@
 }
 
 static void
+vl_api_sr_policies_v2_details_t_handler (vl_api_sr_policies_v2_details_t *mp)
+{
+}
+
+static void
 vl_api_sr_localsids_details_t_handler (vl_api_sr_localsids_details_t *mp)
 {
 }
diff --git a/test/test_srv6.py b/test/test_srv6.py
index 40b5337..a15c697 100644
--- a/test/test_srv6.py
+++ b/test/test_srv6.py
@@ -10,6 +10,7 @@
     SRv6LocalSIDBehaviors,
     VppSRv6LocalSID,
     VppSRv6Policy,
+    VppSRv6PolicyV2,
     SRv6PolicyType,
     VppSRv6Steering,
     SRv6PolicySteeringTypes,
@@ -248,6 +249,121 @@
         # cleanup interfaces
         self.teardown_interfaces()
 
+    def test_SRv6_T_Encaps_with_v6src(self):
+        """Test SRv6 Transit.Encaps behavior for IPv6 and select multiple src v6addr case."""
+        # send traffic to one destination interface
+        # source and destination are IPv6 only
+        self.setup_interfaces(ipv6=[True, True])
+
+        # configure FIB entries
+        route = VppIpRoute(
+            self, "a4::", 64, [VppRoutePath(self.pg1.remote_ip6, self.pg1.sw_if_index)]
+        )
+        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"
+        other_src_ip = "b1::"
+        # configure SRv6 Policy
+        # Note: segment list order: first -> last
+        sr_policy = VppSRv6PolicyV2(
+            self,
+            bsid=bsid,
+            is_encap=1,
+            sr_type=SRv6PolicyType.SR_POLICY_TYPE_DEFAULT,
+            weight=1,
+            fib_table=0,
+            segments=["a4::", "a5::", "a6::c7"],
+            encap_src=other_src_ip,
+            source=other_src_ip,
+        )
+        sr_policy.add_vpp_config()
+        self.sr_policy = sr_policy
+
+        # log the sr policies
+        self.logger.info(self.vapi.cli("show sr policies"))
+
+        # steer IPv6 traffic to a7::/64 into SRv6 Policy
+        # use the bsid of the above self.sr_policy
+        pol_steering = VppSRv6Steering(
+            self,
+            bsid=self.sr_policy.bsid,
+            prefix="a7::",
+            mask_width=64,
+            traffic_type=SRv6PolicySteeringTypes.SR_STEER_IPV6,
+            sr_policy_index=0,
+            table_id=0,
+            sw_if_index=0,
+        )
+        pol_steering.add_vpp_config()
+
+        # log the sr steering policies
+        self.logger.info(self.vapi.cli("show sr steering-policies"))
+
+        # 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 pg0->pg1
+        pkts.extend(
+            self.create_stream(
+                self.pg0, self.pg1, 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 pg0->pg1
+        pkts.extend(
+            self.create_stream(
+                self.pg0, self.pg1, packet_header, self.pg_packet_sizes, count
+            )
+        )
+
+        # create IPv6 packets with SRH and IPv6
+        # packets with segments-left 1, active segment a7::
+        packet_header = self.create_packet_header_IPv6_SRH_IPv6(
+            dst_inner, sidlist=["a8::", "a7::", "a6::"], segleft=1
+        )
+        # create traffic stream pg0->pg1
+        pkts.extend(
+            self.create_stream(
+                self.pg0, self.pg1, packet_header, self.pg_packet_sizes, count
+            )
+        )
+
+        # send packets and verify received packets
+        self.send_and_verify_pkts(
+            self.pg0, pkts, self.pg1, self.compare_rx_tx_packet_T_Encaps
+        )
+
+        # log the localsid counters
+        self.logger.info(self.vapi.cli("show sr localsid"))
+
+        # remove SR steering
+        pol_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 FIB entries
+        # done by tearDown
+
+        # cleanup interfaces
+        self.teardown_interfaces()
+
     @unittest.skipUnless(0, "PC to fix")
     def test_SRv6_T_Insert(self):
         """Test SRv6 Transit.Insert behavior (IPv6 only)."""
diff --git a/test/vpp_srv6.py b/test/vpp_srv6.py
index d789105..1b09103 100644
--- a/test/vpp_srv6.py
+++ b/test/vpp_srv6.py
@@ -6,6 +6,7 @@
 
 from vpp_object import VppObject
 from socket import inet_pton, inet_ntop, AF_INET, AF_INET6
+import copy
 
 
 class SRv6LocalSIDBehaviors:
@@ -28,6 +29,7 @@
     # from src/vnet/srv6/sr.h
     SR_POLICY_TYPE_DEFAULT = 0
     SR_POLICY_TYPE_SPRAY = 1
+    SR_POLICY_TYPE_TEF = 2
 
 
 class SRv6PolicySteeringTypes:
@@ -148,6 +150,79 @@
         )
 
 
+class VppSRv6PolicyV2(VppObject):
+    """
+    SRv6 Policy
+    """
+
+    def __init__(
+        self,
+        test,
+        bsid,
+        is_encap,
+        sr_type,
+        weight,
+        fib_table,
+        segments,
+        encap_src,
+        source,
+    ):
+        self._test = test
+        self.bsid = bsid
+        self.is_encap = is_encap
+        self.sr_type = sr_type
+        self.weight = weight
+        self.fib_table = fib_table
+        self.segments = segments
+        self.encap_src = encap_src
+        self.n_segments = len(segments)
+
+        # source not passed to API
+        # self.source = inet_pton(AF_INET6, source)
+        self.source = source
+        self._configured = False
+
+    def add_vpp_config(self):
+        self._test.vapi.sr_policy_add_v2(
+            bsid_addr=self.bsid,
+            weight=self.weight,
+            is_encap=self.is_encap,
+            type=self.sr_type,
+            fib_table=self.fib_table,
+            encap_src=self.encap_src,
+            sids={
+                "num_sids": self.n_segments,
+                "sids": self._get_fixed_segments(),
+                "weight": 1,
+            },
+        )
+        self._configured = True
+
+    def remove_vpp_config(self):
+        self._test.vapi.sr_policy_del(self.bsid)
+        self._configured = False
+
+    def query_vpp_config(self):
+        # no API to query SR Policies
+        # use _configured flag for now
+        return self._configured
+
+    def object_id(self):
+        return "%d;%s-><%s>;%d" % (
+            self.sr_type,
+            self.bsid,
+            ",".join(self.segments),
+            self.is_encap,
+        )
+
+    def _get_fixed_segments(self):
+        segs = copy.copy(self.segments)
+        # note: array expect size is 16
+        for _ in range(16 - self.n_segments):
+            segs.append("")
+        return segs
+
+
 class VppSRv6Steering(VppObject):
     """
     SRv6 Steering
@@ -177,7 +252,7 @@
     def add_vpp_config(self):
         self._test.vapi.sr_steering_add_del(
             is_del=0,
-            bsid=self.bsid,
+            bsid_addr=self.bsid,
             sr_policy_index=self.sr_policy_index,
             table_id=self.table_id,
             prefix={"address": self.prefix, "len": self.mask_width},
@@ -189,7 +264,7 @@
     def remove_vpp_config(self):
         self._test.vapi.sr_steering_add_del(
             is_del=1,
-            bsid=self.bsid,
+            bsid_addr=self.bsid,
             sr_policy_index=self.sr_policy_index,
             table_id=self.table_id,
             prefix={"address": self.prefix, "len": self.mask_width},