Merge "Refactor RouterAdvertisementDaemon with FdEventsReader" into main

This commit is contained in:
KH Shi 2023-11-23 05:50:11 +00:00 • committed by Gerrit Code Review
commit 093433c8b8

View file

@ -18,6 +18,7 @@ package android.net.ip;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.SOCK_NONBLOCK;
import static android.system.OsConstants.SOCK_RAW;
import static android.system.OsConstants.SOL_SOCKET;
import static android.system.OsConstants.SO_SNDTIMEO;
@ -38,12 +39,19 @@ import android.net.LinkAddress;
import android.net.MacAddress;
import android.net.TrafficStats;
import android.net.util.SocketUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.internal.annotations.GuardedBy;
import com.android.net.module.util.FdEventsReader;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.structs.Icmpv6Header;
import com.android.net.module.util.structs.LlaOption;
@ -103,6 +111,10 @@ public class RouterAdvertisementDaemon {
private static final int DAY_IN_SECONDS = 86_400;
// Commands for IpServer to control RouterAdvertisementDaemon
private static final int CMD_START = 1;
private static final int CMD_STOP = 2;
private final InterfaceParams mInterface;
private final InetSocketAddress mAllNodes;
@ -120,9 +132,13 @@ public class RouterAdvertisementDaemon {
@GuardedBy("mLock")
private RaParams mRaParams;
// To be accessed only from RaMessageHandler
private RsPacketListener mRsPacketListener;
private volatile FileDescriptor mSocket;
private volatile MulticastTransmitter mMulticastTransmitter;
private volatile UnicastResponder mUnicastResponder;
private volatile RaMessageHandler mRaMessageHandler;
private volatile HandlerThread mRaHandlerThread;
/** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
public static class RaParams {
@ -244,6 +260,74 @@ public class RouterAdvertisementDaemon {
}
}
private class RaMessageHandler extends Handler {
RaMessageHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CMD_START:
mRsPacketListener = new RsPacketListener(this);
mRsPacketListener.start();
break;
case CMD_STOP:
if (mRsPacketListener != null) {
mRsPacketListener.stop();
mRsPacketListener = null;
}
break;
default:
Log.e(TAG, "Unknown message, cmd = " + String.valueOf(msg.what));
break;
}
}
}
private class RsPacketListener extends FdEventsReader<RsPacketListener.RecvBuffer> {
private static final class RecvBuffer {
// The recycled buffer for receiving Router Solicitations from clients.
// If the RS is larger than IPV6_MIN_MTU the packets are truncated.
// This is fine since currently only byte 0 is examined anyway.
final byte[] mBytes = new byte[IPV6_MIN_MTU];
final InetSocketAddress mSrcAddr = new InetSocketAddress(0);
}
RsPacketListener(@NonNull Handler handler) {
super(handler, new RecvBuffer());
}
@Override
protected int recvBufSize(@NonNull RecvBuffer buffer) {
return buffer.mBytes.length;
}
@Override
protected FileDescriptor createFd() {
return mSocket;
}
@Override
protected int readPacket(@NonNull FileDescriptor fd, @NonNull RecvBuffer buffer)
throws Exception {
return Os.recvfrom(
fd, buffer.mBytes, 0, buffer.mBytes.length, 0 /* flags */, buffer.mSrcAddr);
}
@Override
protected final void handlePacket(@NonNull RecvBuffer buffer, int length) {
// Do the least possible amount of validations.
if (buffer.mSrcAddr == null
|| length <= 0
|| buffer.mBytes[0] != asByte(ICMPV6_ROUTER_SOLICITATION)) {
return;
}
maybeSendRA(buffer.mSrcAddr);
}
}
public RouterAdvertisementDaemon(InterfaceParams ifParams) {
mInterface = ifParams;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
@ -274,26 +358,36 @@ public class RouterAdvertisementDaemon {
/** Start router advertisement daemon. */
public boolean start() {
if (!createSocket()) {
Log.e(TAG, "Failed to start RouterAdvertisementDaemon.");
return false;
}
mMulticastTransmitter = new MulticastTransmitter();
mMulticastTransmitter.start();
mUnicastResponder = new UnicastResponder();
mUnicastResponder.start();
mRaHandlerThread = new HandlerThread(TAG);
mRaHandlerThread.start();
mRaMessageHandler = new RaMessageHandler(mRaHandlerThread.getLooper());
return true;
return sendMessage(CMD_START);
}
/** Stop router advertisement daemon. */
public void stop() {
if (!sendMessage(CMD_STOP)) {
Log.e(TAG, "RouterAdvertisementDaemon has been stopped or was never started.");
return;
}
mRaHandlerThread.quitSafely();
mRaHandlerThread = null;
mRaMessageHandler = null;
closeSocket();
// Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
// the thread's termination.
maybeNotifyMulticastTransmitter();
mMulticastTransmitter = null;
mUnicastResponder = null;
}
@GuardedBy("mLock")
@ -503,7 +597,7 @@ public class RouterAdvertisementDaemon {
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_NEIGHBOR);
try {
mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
mSocket = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMPV6);
// Setting SNDTIMEO is purely for defensive purposes.
Os.setsockoptTimeval(
mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
@ -565,34 +659,12 @@ public class RouterAdvertisementDaemon {
}
}
private final class UnicastResponder extends Thread {
private final InetSocketAddress mSolicitor = new InetSocketAddress(0);
// The recycled buffer for receiving Router Solicitations from clients.
// If the RS is larger than IPV6_MIN_MTU the packets are truncated.
// This is fine since currently only byte 0 is examined anyway.
private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
@Override
public void run() {
while (isSocketValid()) {
try {
// Blocking receive.
final int rval = Os.recvfrom(
mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
// Do the least possible amount of validation.
if (rval < 1 || mSolicitation[0] != asByte(ICMPV6_ROUTER_SOLICITATION)) {
continue;
}
} catch (ErrnoException | SocketException e) {
if (isSocketValid()) {
Log.e(TAG, "recvfrom error: " + e);
}
continue;
}
maybeSendRA(mSolicitor);
}
private boolean sendMessage(int cmd) {
if (mRaMessageHandler == null) {
return false;
}
return mRaMessageHandler.sendMessage(Message.obtain(mRaMessageHandler, cmd));
}
// TODO: Consider moving this to run on a provided Looper as a Handler,