Verify VTI Modification using RTM_NEWLINK

This test checks that an existing VTI(6) can be
updated using the RTM_NEWLINK without the EXCL
flag.

Bug: 72532478
Test: aosp/kernel/tests/net/test/run_net_test.sh
Test: kernel_net_tests (on wahoo/walleye)
Change-Id: Iecb7bb7f070dfbdd098dbb773d18ca21f23a42da
diff --git a/net/test/iproute.py b/net/test/iproute.py
index 19b1e26..9cfafc6 100644
--- a/net/test/iproute.py
+++ b/net/test/iproute.py
@@ -282,6 +282,8 @@
       name = self._GetConstantName(nla_type, "RTAX_")
     elif command == -IFLA_LINKINFO:
       name = self._GetConstantName(nla_type, "IFLA_INFO_")
+    elif command == -IFLA_INFO_DATA:
+      name = self._GetConstantName(nla_type, "IFLA_VTI_")
     elif CommandSubject(command) == "ADDR":
       name = self._GetConstantName(nla_type, "IFA_")
     elif CommandSubject(command) == "LINK":
@@ -304,6 +306,8 @@
                 "RTAX_HOPLIMIT", "IFLA_CARRIER_CHANGES", "IFLA_GSO_MAX_SEGS",
                 "IFLA_GSO_MAX_SIZE", "RTA_UID"]:
       data = struct.unpack("=I", nla_data)[0]
+    elif name in ["IFLA_VTI_OKEY", "IFLA_VTI_IKEY"]:
+      data = struct.unpack("!I", nla_data)[0]
     elif name == "FRA_SUPPRESS_PREFIXLEN":
       data = struct.unpack("=i", nla_data)[0]
     elif name in ["IFLA_LINKMODE", "IFLA_OPERSTATE", "IFLA_CARRIER"]:
@@ -318,6 +322,8 @@
       data = self._ParseAttributes(-RTA_METRICS, None, nla_data, nested + 1)
     elif name == "IFLA_LINKINFO":
       data = self._ParseAttributes(-IFLA_LINKINFO, None, nla_data, nested + 1)
+    elif name == "IFLA_INFO_DATA":
+      data = self._ParseAttributes(-IFLA_INFO_DATA, None, nla_data)
     elif name == "RTA_CACHEINFO":
       data = RTACacheinfo(nla_data)
     elif name == "IFA_CACHEINFO":
@@ -350,8 +356,8 @@
     if CommandVerb(command) != "GET":
       flags |= netlink.NLM_F_ACK
     if CommandVerb(command) == "NEW":
-      if not flags & netlink.NLM_F_REPLACE:
-        flags |= (netlink.NLM_F_EXCL | netlink.NLM_F_CREATE)
+      if flags & (netlink.NLM_F_REPLACE | netlink.NLM_F_CREATE) == 0:
+        flags |= netlink.NLM_F_CREATE | netlink.NLM_F_EXCL
 
     super(IPRoute, self)._SendNlRequest(command, data, flags)
 
@@ -640,8 +646,7 @@
   def DeleteLink(self, dev_name):
     ifinfo = IfinfoMsg().Pack()
     ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
-    flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK
-    return self._SendNlRequest(RTM_DELLINK, ifinfo, flags)
+    return self._SendNlRequest(RTM_DELLINK, ifinfo)
 
   def GetIfinfo(self, dev_name):
     """Fetches information about the specified interface.
@@ -654,7 +659,7 @@
     """
     ifinfo = IfinfoMsg().Pack()
     ifinfo += self._NlAttrStr(IFLA_IFNAME, dev_name)
-    self._SendNlRequest(RTM_GETLINK, ifinfo, netlink.NLM_F_REQUEST)
+    self._SendNlRequest(RTM_GETLINK, ifinfo)
     hdr, data = cstruct.Read(self._Recv(), netlink.NLMsgHdr)
     if hdr.type == RTM_NEWLINK:
       return cstruct.Read(data, IfinfoMsg)
@@ -675,12 +680,18 @@
     attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs)
     return attrs["IFLA_STATS64"]
 
+  def GetVtiInfoData(self, dev_name):
+    """Returns an IFLA_INFO_DATA dict object for the specified interface."""
+    _, attrs = self.GetIfinfo(dev_name)
+    attrs = self._ParseAttributes(RTM_NEWLINK, IfinfoMsg, attrs)
+    return attrs["IFLA_LINKINFO"]["IFLA_INFO_DATA"]
+
   def GetRxTxPackets(self, dev_name):
     stats = self.GetIfaceStats(dev_name)
     return stats.rx_packets, stats.tx_packets
 
   def CreateVirtualTunnelInterface(self, dev_name, local_addr, remote_addr,
-                                   i_key=None, o_key=None):
+                                   i_key=None, o_key=None, is_update=False):
     """
     Create a Virtual Tunnel Interface that provides a proxy interface
     for IPsec tunnels.
@@ -725,7 +736,12 @@
 
     ifinfo += self._NlAttr(IFLA_LINKINFO, linkinfo)
 
-    return self._SendNlRequest(RTM_NEWLINK, ifinfo)
+    # Always pass CREATE to prevent _SendNlRequest() from incorrectly
+    # guessing the flags.
+    flags = netlink.NLM_F_CREATE
+    if not is_update:
+      flags |= netlink.NLM_F_EXCL
+    return self._SendNlRequest(RTM_NEWLINK, ifinfo, flags)
 
 
 if __name__ == "__main__":
diff --git a/net/test/xfrm_tunnel_test.py b/net/test/xfrm_tunnel_test.py
index 94e846e..ac340d9 100755
--- a/net/test/xfrm_tunnel_test.py
+++ b/net/test/xfrm_tunnel_test.py
@@ -106,6 +106,13 @@
 
 @unittest.skipUnless(net_test.LINUX_VERSION >= (3, 18, 0), "VTI Unsupported")
 class XfrmAddDeleteVtiTest(xfrm_base.XfrmBaseTest):
+  def verifyVtiInfoData(self, vti_info_data, version, local_addr, remote_addr, ikey, okey):
+    self.assertEquals(vti_info_data["IFLA_VTI_IKEY"], ikey)
+    self.assertEquals(vti_info_data["IFLA_VTI_OKEY"], okey)
+
+    family = AF_INET if version == 4 else AF_INET6
+    self.assertEquals(inet_ntop(family, vti_info_data["IFLA_VTI_LOCAL"]), local_addr)
+    self.assertEquals(inet_ntop(family, vti_info_data["IFLA_VTI_REMOTE"]), remote_addr)
 
   def testAddVti(self):
     """Test the creation of a Virtual Tunnel Interface."""
@@ -118,6 +125,25 @@
           remote_addr=_GetRemoteOuterAddress(version),
           o_key=_TEST_OKEY,
           i_key=_TEST_IKEY)
+      self.verifyVtiInfoData(self.iproute.GetVtiInfoData(_VTI_IFNAME),
+                             version, local_addr, _GetRemoteOuterAddress(version),
+                             _TEST_IKEY, _TEST_OKEY)
+
+      new_remote_addr = {4: net_test.IPV4_ADDR2, 6: net_test.IPV6_ADDR2}
+      new_okey = _TEST_OKEY + _VTI_NETID
+      new_ikey = _TEST_IKEY + _VTI_NETID
+      self.iproute.CreateVirtualTunnelInterface(
+          dev_name=_VTI_IFNAME,
+          local_addr=local_addr,
+          remote_addr=new_remote_addr[version],
+          o_key=new_okey,
+          i_key=new_ikey,
+          is_update=True)
+
+      self.verifyVtiInfoData(self.iproute.GetVtiInfoData(_VTI_IFNAME),
+                             version, local_addr, new_remote_addr[version],
+                             new_ikey, new_okey)
+
       if_index = self.iproute.GetIfIndex(_VTI_IFNAME)
 
       # Validate that the netlink interface matches the ioctl interface.