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
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 7c2be2c..f735e46 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.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.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.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 @@
}
/**
- * 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 cbe0ab0..4846df7 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 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 @@
*/
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 @@
mIfacacheInfo = structIfacacheInfo;
mFlags = flags;
}
+
private RtNetlinkAddressMessage(@NonNull StructNlMsgHdr header) {
this(header, null, null, null, 0);
}
@@ -158,6 +163,24 @@
// 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 @@
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 @@
// 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 @@
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 01126d2..1d08525 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 org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.Inet4Address;
import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -179,6 +180,57 @@
}
@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.
final String expectedDelAddressHex =