Add a method helper to create RTM_DELADDR message in user space.

Bug: 260934173
Bug: 263222068
Test: atest NetworkStaticLibTests
Change-Id: Ibddaed91b12d3f27778d0bd2171bc5be38ff8bda
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
index 0f7bd80..73c88c7 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java
@@ -196,6 +196,27 @@
     }
 
     /**
+     * Send an RTM_DELADDR message to kernel to delete an IPv6 address.
+     *
+     * @param ifIndex interface index.
+     * @param ip IPv6 address to be deleted.
+     * @param prefixlen IPv6 address prefix length.
+     */
+    public static boolean sendRtmDelAddressRequest(int ifIndex, final Inet6Address ip,
+            short prefixlen) {
+        Objects.requireNonNull(ip, "IPv6 address to be deleted should not be null.");
+        final byte[] msg = RtNetlinkAddressMessage.newRtmDelAddressMessage(1 /* seqNo*/, ip,
+                prefixlen, ifIndex);
+        try {
+            NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
+            return true;
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Fail to send RTM_DELADDR to delete " + ip.getHostAddress(), e);
+            return false;
+        }
+    }
+
+    /**
      * Create netlink socket with the given netlink protocol type.
      *
      * @return fd the fileDescriptor of the socket.
diff --git a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java
index 2829b92..cbe0ab0 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java
@@ -161,7 +161,7 @@
     }
 
     /**
-     * A convenience method to create an RTM_NEWADDR message.
+     * A convenience method to create a RTM_NEWADDR message.
      */
     public static byte[] newRtmNewAddressMessage(int seqNo, @NonNull final InetAddress ip,
             short prefixlen, int flags, byte scope, int ifIndex, long preferred, long valid) {
@@ -192,6 +192,51 @@
         return bytes;
     }
 
+    /**
+     * A convenience method to create a RTM_DELADDR message.
+     */
+    public static byte[] newRtmDelAddressMessage(int seqNo, @NonNull final InetAddress ip,
+            short prefixlen, int ifIndex) {
+        Objects.requireNonNull(ip, "IP address to be deleted via netlink message cannot be null");
+
+        final int ifaAddrAttrLength = NetlinkConstants.alignedLengthOf(
+                StructNlAttr.NLA_HEADERLEN + ip.getAddress().length);
+        final int length = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE
+                + ifaAddrAttrLength;
+        final byte[] bytes = new byte[length];
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+        byteBuffer.order(ByteOrder.nativeOrder());
+
+        final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+        nlmsghdr.nlmsg_len = length;
+        nlmsghdr.nlmsg_type = NetlinkConstants.RTM_DELADDR;
+        nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+        nlmsghdr.nlmsg_seq = seqNo;
+        nlmsghdr.pack(byteBuffer);
+
+        final byte family =
+                (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+        // Actually kernel ignores scope and flags(only deal with IFA_F_MANAGETEMPADDR, it
+        // indicates that all relevant IPv6 temporary addresses should be deleted as well when
+        // user space intends to delete a global IPv6 address with IFA_F_MANAGETEMPADDR), so
+        // far IFA_F_MANAGETEMPADDR flag isn't used in user space, it's fine to ignore it.
+        // However, we need to add IFA_FLAGS attribute in RTM_DELADDR if flags parsing should
+        // be supported in the future.
+        final StructIfaddrMsg ifaddrmsg = new StructIfaddrMsg(family, prefixlen,
+                (short) 0 /* flags */, (short) 0 /* scope */, ifIndex);
+        ifaddrmsg.pack(byteBuffer);
+
+        final StructNlAttr address = new StructNlAttr(IFA_ADDRESS, ip);
+        address.pack(byteBuffer);
+
+        return bytes;
+    }
+
+    // This function helper gives the required buffer size for IFA_ADDRESS, IFA_CACHEINFO and
+    // IFA_FLAGS attributes encapsulation. However, that's not a mandatory requirement for all
+    // RtNetlinkAddressMessage, e.g. RTM_DELADDR sent from user space to kernel to delete an
+    // IP address only requires IFA_ADDRESS attribute. The caller should check if these attributes
+    // are necessary to carry when constructing a RtNetlinkAddressMessage.
     private int getRequiredSpace() {
         int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE;
         // IFA_ADDRESS attr
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java
index 99d96b5..01126d2 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkAddressMessageTest.java
@@ -179,6 +179,34 @@
     }
 
     @Test
+    public void testCreateRtmDelAddressMessage() {
+        // Hexadecimal representation of our created packet.
+        final String expectedDelAddressHex =
+                // struct nlmsghdr
+                "2C000000" + // length = 44
+                "1500" +     // type = 21 (RTM_DELADDR)
+                "0500" +     // flags = NLM_F_ACK | NLM_F_REQUEST
+                "01000000" + // seqno = 1
+                "00000000" + // pid = 0 (send to kernel)
+                // struct IfaddrMsg
+                "0A" +       // family = inet6
+                "40" +       // prefix len = 64
+                "00" +       // flags = 0
+                "00" +       // scope = RT_SCOPE_UNIVERSE
+                "3B000000" + // ifindex = 59
+                // struct nlattr: IFA_ADDRESS
+                "1400" +     // len
+                "0100" +     // type
+                "20010DB8000100000000000000000100"; // IP address = 2001:db8:1::100
+        final byte[] expectedDelAddress =
+                HexEncoding.decode(expectedDelAddressHex.toCharArray(), false);
+
+        final byte[] bytes = RtNetlinkAddressMessage.newRtmDelAddressMessage(1 /* seqno */,
+                TEST_GLOBAL_ADDRESS, (short) 64 /* prefix len */, 59 /* ifindex */);
+        assertArrayEquals(expectedDelAddress, bytes);
+    }
+
+    @Test
     public void testCreateRtmNewAddressMessage_nullIpAddress() {
         assertThrows(NullPointerException.class,
                 () -> RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqno */,
@@ -189,6 +217,13 @@
     }
 
     @Test
+    public void testCreateRtmDelAddressMessage_nullIpAddress() {
+        assertThrows(NullPointerException.class,
+                () -> RtNetlinkAddressMessage.newRtmDelAddressMessage(1 /* seqno */,
+                        null /* IP address */, (short) 0 /* prefix len */, 59 /* ifindex */));
+    }
+
+    @Test
     public void testCreateRtmNewAddressMessage_u32Flags() {
         // Hexadecimal representation of our created packet.
         final String expectedNewAddressHex =