Merge "Support adding an IPv4 address via RTM_NEWADDR netlink message." into main

This commit is contained in:
Xiao Ma 2024-01-29 01:24:12 +00:00 • committed by Gerrit Code Review
commit 522197af33
3 changed files with 96 additions and 12 deletions

View file

@ -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 {

View file

@ -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;
}

View file

@ -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.