From a3d165e814ad817d4af30df4a767a896f0850557 Mon Sep 17 00:00:00 2001 From: Xiao Ma Date: Sun, 21 Jan 2024 03:49:45 +0900 Subject: [PATCH] Support adding an IPv4 address via RTM_NEWADDR netlink message. Usually we can call InterfaceController.setIPv4Address API to add an IPv4 address on the interface, in fact it ends up calling the native Netd API which doesn't support setting deprecation/expiration time. In user space we already have the netlink infra to send RTM_NEWADDR for IPv6 address, extend the function to support IPv4 as well, then we can deprecate the Netd API in IpClient. Bug: 129660498 Test: atest NetworkStaticLibTests Change-Id: I9fc7ba8d7035e0ed1df8c1f2baef9ec7e1806be3 --- .../net/module/util/netlink/NetlinkUtils.java | 21 ++++---- .../util/netlink/RtNetlinkAddressMessage.java | 35 ++++++++++++- .../netlink/RtNetlinkAddressMessageTest.java | 52 +++++++++++++++++++ 3 files changed, 96 insertions(+), 12 deletions(-) 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 7c2be2cfbe..f735e46f51 100644 --- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java +++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkUtils.java @@ -29,6 +29,7 @@ import static android.system.OsConstants.SOL_SOCKET; import static android.system.OsConstants.SO_RCVBUF; import static android.system.OsConstants.SO_RCVTIMEO; import static android.system.OsConstants.SO_SNDTIMEO; + import static com.android.net.module.util.netlink.NetlinkConstants.hexify; import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE; import static com.android.net.module.util.netlink.NetlinkConstants.RTNL_FAMILY_IP6MR; @@ -49,6 +50,7 @@ import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; import java.net.Inet6Address; +import java.net.InetAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -56,7 +58,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; -import java.util.stream.Collectors; /** * Utilities for netlink related class that may not be able to fit into a specific class. @@ -177,19 +178,19 @@ public class NetlinkUtils { } /** - * Send an RTM_NEWADDR message to kernel to add or update an IPv6 address. + * Send an RTM_NEWADDR message to kernel to add or update an IP address. * * @param ifIndex interface index. - * @param ip IPv6 address to be added. - * @param prefixlen IPv6 address prefix length. - * @param flags IPv6 address flags. - * @param scope IPv6 address scope. - * @param preferred The preferred lifetime of IPv6 address. - * @param valid The valid lifetime of IPv6 address. + * @param ip IP address to be added. + * @param prefixlen IP address prefix length. + * @param flags IP address flags. + * @param scope IP address scope. + * @param preferred The preferred lifetime of IP address. + * @param valid The valid lifetime of IP address. */ - public static boolean sendRtmNewAddressRequest(int ifIndex, @NonNull final Inet6Address ip, + public static boolean sendRtmNewAddressRequest(int ifIndex, @NonNull final InetAddress ip, short prefixlen, int flags, byte scope, long preferred, long valid) { - Objects.requireNonNull(ip, "IPv6 address to be added should not be null."); + Objects.requireNonNull(ip, "IP address to be added should not be null."); final byte[] msg = RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqNo*/, ip, prefixlen, flags, scope, ifIndex, preferred, valid); try { 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 cbe0ab0e93..4846df7880 100644 --- a/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java +++ b/staticlibs/device/com/android/net/module/util/netlink/RtNetlinkAddressMessage.java @@ -16,6 +16,7 @@ package com.android.net.module.util.netlink; +import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress; import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK; import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE; import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; @@ -28,6 +29,7 @@ import androidx.annotation.VisibleForTesting; import com.android.net.module.util.HexDump; +import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.ByteBuffer; @@ -48,6 +50,8 @@ import java.util.Objects; */ public class RtNetlinkAddressMessage extends NetlinkMessage { public static final short IFA_ADDRESS = 1; + public static final short IFA_LOCAL = 2; + public static final short IFA_BROADCAST = 4; public static final short IFA_CACHEINFO = 6; public static final short IFA_FLAGS = 8; @@ -71,6 +75,7 @@ public class RtNetlinkAddressMessage extends NetlinkMessage { mIfacacheInfo = structIfacacheInfo; mFlags = flags; } + private RtNetlinkAddressMessage(@NonNull StructNlMsgHdr header) { this(header, null, null, null, 0); } @@ -158,6 +163,24 @@ public class RtNetlinkAddressMessage extends NetlinkMessage { // still be packed to ByteBuffer even if the flag is 0. final StructNlAttr flags = new StructNlAttr(IFA_FLAGS, mFlags); flags.pack(byteBuffer); + + // Add the required IFA_LOCAL and IFA_BROADCAST attributes for IPv4 addresses. The IFA_LOCAL + // attribute represents the local address, which is equivalent to IFA_ADDRESS on a normally + // configured broadcast interface, however, for PPP interfaces, IFA_ADDRESS indicates the + // destination address and the local address is provided in the IFA_LOCAL attribute. If the + // IFA_LOCAL attribute is not present in the RTM_NEWADDR message, the kernel replies with an + // error netlink message with invalid parameters. IFA_BROADCAST is also required, otherwise + // the broadcast on the interface is 0.0.0.0. See include/uapi/linux/if_addr.h for details. + // For IPv6 addresses, the IFA_ADDRESS attribute applies and introduces no ambiguity. + if (mIpAddress instanceof Inet4Address) { + final StructNlAttr localAddress = new StructNlAttr(IFA_LOCAL, mIpAddress); + localAddress.pack(byteBuffer); + + final Inet4Address broadcast = + getBroadcastAddress((Inet4Address) mIpAddress, mIfaddrmsg.prefixLen); + final StructNlAttr broadcastAddress = new StructNlAttr(IFA_BROADCAST, broadcast); + broadcastAddress.pack(byteBuffer); + } } /** @@ -184,7 +207,7 @@ public class RtNetlinkAddressMessage extends NetlinkMessage { 0 /* tstamp */); msg.mFlags = flags; - final byte[] bytes = new byte[msg.getRequiredSpace()]; + final byte[] bytes = new byte[msg.getRequiredSpace(family)]; nlmsghdr.nlmsg_len = bytes.length; final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); byteBuffer.order(ByteOrder.nativeOrder()); @@ -237,7 +260,7 @@ public class RtNetlinkAddressMessage extends NetlinkMessage { // 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() { + private int getRequiredSpace(int family) { int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructIfaddrMsg.STRUCT_SIZE; // IFA_ADDRESS attr spaceRequired += NetlinkConstants.alignedLengthOf( @@ -247,6 +270,14 @@ public class RtNetlinkAddressMessage extends NetlinkMessage { StructNlAttr.NLA_HEADERLEN + StructIfacacheInfo.STRUCT_SIZE); // IFA_FLAGS "u32" attr spaceRequired += StructNlAttr.NLA_HEADERLEN + 4; + if (family == OsConstants.AF_INET) { + // IFA_LOCAL attr + spaceRequired += NetlinkConstants.alignedLengthOf( + StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length); + // IFA_BROADCAST attr + spaceRequired += NetlinkConstants.alignedLengthOf( + StructNlAttr.NLA_HEADERLEN + mIpAddress.getAddress().length); + } return spaceRequired; } 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 01126d2f82..1d08525fd5 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 @@ -42,6 +42,7 @@ import libcore.util.HexEncoding; import org.junit.Test; import org.junit.runner.RunWith; +import java.net.Inet4Address; import java.net.Inet6Address; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -178,6 +179,57 @@ public class RtNetlinkAddressMessageTest { assertArrayEquals(expectedNewAddress, bytes); } + @Test + public void testCreateRtmNewAddressMessage_IPv4Address() { + // Hexadecimal representation of our created packet. + final String expectedNewAddressHex = + // struct nlmsghdr + "4c000000" // length = 76 + + "1400" // type = 20 (RTM_NEWADDR) + + "0501" // flags = NLM_F_ACK | NLM_F_REQUEST | NLM_F_REPLACE + + "01000000" // seqno = 1 + + "00000000" // pid = 0 (send to kernel) + // struct IfaddrMsg + + "02" // family = inet + + "18" // prefix len = 24 + + "00" // flags = 0 + + "00" // scope = RT_SCOPE_UNIVERSE + + "14000000" // ifindex = 20 + // struct nlattr: IFA_ADDRESS + + "0800" // len + + "0100" // type + + "C0A80491" // IPv4 address = 192.168.4.145 + // struct nlattr: IFA_CACHEINFO + + "1400" // len + + "0600" // type + + "C0A80000" // preferred = 43200s + + "C0A80000" // valid = 43200s + + "00000000" // cstamp + + "00000000" // tstamp + // struct nlattr: IFA_FLAGS + + "0800" // len + + "0800" // type + + "00000000" // flags = 0 + // struct nlattr: IFA_LOCAL + + "0800" // len + + "0200" // type + + "C0A80491" // local address = 192.168.4.145 + // struct nlattr: IFA_BROADCAST + + "0800" // len + + "0400" // type + + "C0A804FF"; // broadcast address = 192.168.4.255 + final byte[] expectedNewAddress = + HexEncoding.decode(expectedNewAddressHex.toCharArray(), false); + + final Inet4Address ipAddress = + (Inet4Address) InetAddresses.parseNumericAddress("192.168.4.145"); + final byte[] bytes = RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqno */, + ipAddress, (short) 24 /* prefix len */, 0 /* flags */, + (byte) RT_SCOPE_UNIVERSE /* scope */, 20 /* ifindex */, + (long) 0xA8C0 /* preferred */, (long) 0xA8C0 /* valid */); + assertArrayEquals(expectedNewAddress, bytes); + } + @Test public void testCreateRtmDelAddressMessage() { // Hexadecimal representation of our created packet.