Refactor: Introduce Builder for RtNetlinkNeighborMessage

This change refactors the creation of `RtNetlinkNeighborMessage` instances by introducing a new `Builder` class. The `newGetNeighborsRequest` and `newNewNeighborMessage` methods now use this builder to construct the messages, improving readability and maintainability.

Bug: 395153629
Test: atest RtNetlinkNeighborMessageTest
Flag: EXEMPT PURE_REFACTOR
Change-Id: I60c277a736266ca0b61f138dd8ef5272c6aa9e5b
This commit is contained in:
Nate Jiang 2025-09-11 10:59:24 -07:00
commit bc568b753b
2 changed files with 177 additions and 30 deletions

View file

@ -107,21 +107,17 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
* A convenience method to create an RTM_GETNEIGH request message.
*/
public static byte[] newGetNeighborsRequest(int seqNo) {
final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
final byte[] bytes = new byte[length];
short flags = NLM_F_REQUEST | NLM_F_DUMP;
final RtNetlinkNeighborMessage msg = new Builder()
.setNlMsgType(NetlinkConstants.RTM_GETNEIGH)
.setNlMsgFlags(flags)
.setNlMsgSeq(seqNo)
.build();
final byte[] bytes = new byte[msg.getHeader().nlmsg_len];
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byteBuffer.order(ByteOrder.nativeOrder());
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
nlmsghdr.nlmsg_len = length;
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH;
nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
nlmsghdr.nlmsg_seq = seqNo;
nlmsghdr.pack(byteBuffer);
final StructNdMsg ndmsg = new StructNdMsg();
ndmsg.pack(byteBuffer);
msg.pack(byteBuffer);
return bytes;
}
@ -130,29 +126,115 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
* the kernel's state information for a specific neighbor.
*/
public static byte[] newNewNeighborMessage(
int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
nlmsghdr.nlmsg_seq = seqNo;
int seqNo, @NonNull InetAddress ip, short nudState, int ifIndex,
@Nullable byte[] llAddr) {
short flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
final RtNetlinkNeighborMessage msg = new Builder()
.setNlMsgType(NetlinkConstants.RTM_NEWNEIGH)
.setNlMsgFlags(flags)
.setNlMsgSeq(seqNo)
.setIfIndex(ifIndex)
.setState(nudState)
.setDestination(ip)
.setLinkLayerAddress(llAddr)
.build();
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
msg.mNdmsg = new StructNdMsg();
msg.mNdmsg.ndm_family =
(byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
msg.mNdmsg.ndm_ifindex = ifIndex;
msg.mNdmsg.ndm_state = nudState;
msg.mDestination = ip;
msg.mLinkLayerAddr = llAddr; // might be null
final byte[] bytes = new byte[msg.getRequiredSpace()];
nlmsghdr.nlmsg_len = bytes.length;
final byte[] bytes = new byte[msg.getHeader().nlmsg_len];
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byteBuffer.order(ByteOrder.nativeOrder());
msg.pack(byteBuffer);
return bytes;
}
/**
* Builder for {@link RtNetlinkNeighborMessage}.
*/
public static class Builder {
private final StructNlMsgHdr mHeader = new StructNlMsgHdr();
private final StructNdMsg mNdmsg = new StructNdMsg();
@Nullable private InetAddress mDestination;
@Nullable private byte[] mLinkLayerAddr;
/**
* Build a {@link RtNetlinkNeighborMessage}.
*/
public RtNetlinkNeighborMessage build() {
if (mHeader.nlmsg_type == 0) {
throw new IllegalArgumentException("Netlink message type is not set");
}
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(mHeader);
msg.mNdmsg = mNdmsg;
msg.mDestination = mDestination;
if (mLinkLayerAddr != null) {
msg.mLinkLayerAddr = mLinkLayerAddr;
}
mHeader.nlmsg_len = msg.getRequiredSpace();
return msg;
}
/** Set the netlink message header type. */
public Builder setNlMsgType(short type) {
if (type != NetlinkConstants.RTM_NEWNEIGH
&& type != NetlinkConstants.RTM_GETNEIGH
&& type != NetlinkConstants.RTM_DELNEIGH) {
throw new IllegalArgumentException("Unsupported netlink message type: " + type);
}
mHeader.nlmsg_type = type;
return this;
}
/** Set the netlink message header flags. */
public Builder setNlMsgFlags(short flags) {
mHeader.nlmsg_flags = flags;
return this;
}
/** Set the netlink message header sequence number. Default is 0. */
public Builder setNlMsgSeq(int seq) {
if (seq < 0) {
throw new IllegalArgumentException("Negative sequence number: " + seq);
}
mHeader.nlmsg_seq = seq;
return this;
}
/** Set the interface index. */
public Builder setIfIndex(int ifindex) {
if (ifindex < 0) {
throw new IllegalArgumentException("Negative interface index: " + ifindex);
}
mNdmsg.ndm_ifindex = ifindex;
return this;
}
/** Set the neighbor unreachability detection (NUD) state. */
public Builder setState(short state) {
if (!StructNdMsg.isNudStateValid(state)) {
throw new IllegalArgumentException("Invalid NUD state: " + state);
}
mNdmsg.ndm_state = state;
return this;
}
/** Set the destination IP address. */
public Builder setDestination(@Nullable InetAddress destination) {
mDestination = destination;
if (mDestination == null) {
mNdmsg.ndm_family = (byte) OsConstants.AF_UNSPEC;
} else {
mNdmsg.ndm_family = (byte) ((destination instanceof Inet6Address)
? OsConstants.AF_INET6 : OsConstants.AF_INET);
}
return this;
}
/** Set the link-layer address. */
public Builder setLinkLayerAddress(@Nullable byte[] llAddr) {
mLinkLayerAddr = llAddr;
return this;
}
}
private StructNdMsg mNdmsg;
private InetAddress mDestination;
private byte[] mLinkLayerAddr;

View file

@ -22,9 +22,11 @@ import static com.android.net.module.util.netlink.StructNdMsg.NUD_STALE;
import static com.android.testutils.NetlinkTestUtils.makeDelNeighMessage;
import static com.android.testutils.NetlinkTestUtils.makeNewNeighMessage;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThrows;
import android.net.InetAddresses;
import android.system.OsConstants;
@ -34,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4;
import libcore.util.HexEncoding;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -204,4 +207,66 @@ public class RtNetlinkNeighborMessageTest {
}
}
}
@Test
public void testBuilder() {
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage.Builder()
.setNlMsgType(NetlinkConstants.RTM_NEWNEIGH)
.setNlMsgFlags(StructNlMsgHdr.NLM_F_REQUEST)
.setNlMsgSeq(1234)
.setIfIndex(14)
.setState(StructNdMsg.NUD_DELAY)
.setDestination(Inet4Address.LOOPBACK)
.setLinkLayerAddress(new byte[] { (byte) 1, (byte) 2, (byte) 3,
(byte) 4, (byte) 5, (byte) 6 })
.build();
assertEquals(NetlinkConstants.RTM_NEWNEIGH, msg.getHeader().nlmsg_type);
assertEquals(StructNlMsgHdr.NLM_F_REQUEST, msg.getHeader().nlmsg_flags);
assertEquals(1234, msg.getHeader().nlmsg_seq);
assertEquals(14, msg.getNdHeader().ndm_ifindex);
assertEquals(StructNdMsg.NUD_DELAY, msg.getNdHeader().ndm_state);
assertEquals(Inet4Address.LOOPBACK, msg.getDestination());
assertArrayEquals(new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6 },
msg.getLinkLayerAddress());
assertEquals(OsConstants.AF_INET, msg.getNdHeader().ndm_family);
}
@Test
public void testBuilder_nullDestination() {
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage.Builder()
.setNlMsgType(NetlinkConstants.RTM_NEWNEIGH)
.setNlMsgFlags(StructNlMsgHdr.NLM_F_REQUEST)
.setNlMsgSeq(1234)
.setIfIndex(14)
.setState(StructNdMsg.NUD_DELAY)
.setDestination(null)
.setLinkLayerAddress(new byte[] { (byte) 1, (byte) 2, (byte) 3,
(byte) 4, (byte) 5, (byte) 6 })
.build();
assertEquals(NetlinkConstants.RTM_NEWNEIGH, msg.getHeader().nlmsg_type);
assertEquals(StructNlMsgHdr.NLM_F_REQUEST, msg.getHeader().nlmsg_flags);
assertEquals(1234, msg.getHeader().nlmsg_seq);
assertEquals(14, msg.getNdHeader().ndm_ifindex);
assertEquals(StructNdMsg.NUD_DELAY, msg.getNdHeader().ndm_state);
assertEquals(null, msg.getDestination());
assertArrayEquals(new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6 },
msg.getLinkLayerAddress());
assertEquals(OsConstants.AF_UNSPEC, msg.getNdHeader().ndm_family);
}
@Test
public void testBuilder_negativeSeqNo() {
assertThrows(
IllegalArgumentException.class,
() -> new RtNetlinkNeighborMessage.Builder().setNlMsgSeq(-1));
}
@Test
public void testBuilder_unsupportedNlMsgType() {
assertThrows(
IllegalArgumentException.class,
() -> new RtNetlinkNeighborMessage.Builder()
.setNlMsgType(NetlinkConstants.RTM_NEWROUTE));
}
}