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.