Refactor a new NetlinkMonitor class out of IpNeighborMonitor.
This CL adds a new NetlinkMonitor that is a subclass of
PacketReader and a superclass of IpNeighborMonitor. The class can
be used to implement other simple "listen to netlink broadcasts
and take action on them" classes without requiring much new code
to be written.
Bug: 153694684
Test: atest NetworkStackTests:IpReachabilityMonitorTest
Change-Id: Ia7ffb0fab4b116f4e238ccc5f5da92a04c4c86e2
diff --git a/common/moduleutils/Android.bp b/common/moduleutils/Android.bp
index f32f8d8..93ee00c 100644
--- a/common/moduleutils/Android.bp
+++ b/common/moduleutils/Android.bp
@@ -53,6 +53,7 @@
srcs: [
"src/android/net/ip/InterfaceController.java",
"src/android/net/ip/IpNeighborMonitor.java",
+ "src/android/net/ip/NetlinkMonitor.java",
"src/android/net/netlink/*.java",
"src/android/net/shared/NetdUtils.java",
"src/android/net/shared/RouteUtils.java",
diff --git a/common/moduleutils/src/android/net/ip/IpNeighborMonitor.java b/common/moduleutils/src/android/net/ip/IpNeighborMonitor.java
index d6706d4..18a9f90 100644
--- a/common/moduleutils/src/android/net/ip/IpNeighborMonitor.java
+++ b/common/moduleutils/src/android/net/ip/IpNeighborMonitor.java
@@ -19,35 +19,20 @@
import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
import static android.net.netlink.NetlinkConstants.hexify;
import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
-import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
-import static android.system.OsConstants.AF_NETLINK;
import static android.system.OsConstants.NETLINK_ROUTE;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
import android.net.MacAddress;
-import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
import android.net.netlink.NetlinkSocket;
import android.net.netlink.RtNetlinkNeighborMessage;
import android.net.netlink.StructNdMsg;
-import android.net.util.PacketReader;
import android.net.util.SharedLog;
-import android.net.util.SocketUtils;
import android.os.Handler;
-import android.os.SystemClock;
import android.system.ErrnoException;
-import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.util.StringJoiner;
@@ -61,7 +46,7 @@
*
* @hide
*/
-public class IpNeighborMonitor extends PacketReader {
+public class IpNeighborMonitor extends NetlinkMonitor {
private static final String TAG = IpNeighborMonitor.class.getSimpleName();
private static final boolean DBG = false;
private static final boolean VDBG = false;
@@ -129,85 +114,27 @@
}
}
- // TODO: move NetworkStackUtils.closeSocketQuietly to somewhere accessible to this file.
- private void closeSocketQuietly(FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
public interface NeighborEventConsumer {
// Every neighbor event received on the netlink socket is passed in
// here. Subclasses should filter for events of interest.
public void accept(NeighborEvent event);
}
- private final SharedLog mLog;
private final NeighborEventConsumer mConsumer;
public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
- super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
- mLog = log.forSubComponent(TAG);
+ super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH);
mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
}
@Override
- protected FileDescriptor createFd() {
- FileDescriptor fd = null;
-
- try {
- fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
- Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
- NetlinkSocket.connectToKernel(fd);
-
- if (VDBG) {
- final SocketAddress nlAddr = Os.getsockname(fd);
- Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
- }
- } catch (ErrnoException|SocketException e) {
- logError("Failed to create rtnetlink socket", e);
- closeSocketQuietly(fd);
- return null;
+ public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) {
+ if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
+ mLog.e("non-rtnetlink neighbor msg: " + nlMsg);
+ return;
}
- return fd;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final long whenMs = SystemClock.elapsedRealtime();
-
- final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- parseNetlinkMessageBuffer(byteBuffer, whenMs);
- }
-
- private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
- while (byteBuffer.remaining() > 0) {
- final int position = byteBuffer.position();
- final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
- if (nlMsg == null || nlMsg.getHeader() == null) {
- byteBuffer.position(position);
- mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
- break;
- }
-
- if (nlMsg instanceof NetlinkErrorMessage) {
- mLog.e("netlink error: " + nlMsg);
- continue;
- } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
- mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
- continue;
- }
-
- evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
- }
- }
-
- private void evaluateRtNetlinkNeighborMessage(
- RtNetlinkNeighborMessage neighMsg, long whenMs) {
+ final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg;
final short msgType = neighMsg.getHeader().nlmsg_type;
final StructNdMsg ndMsg = neighMsg.getNdHeader();
if (ndMsg == null) {
diff --git a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
new file mode 100644
index 0000000..806f3ef
--- /dev/null
+++ b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ip;
+
+import static android.net.netlink.NetlinkConstants.hexify;
+import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
+import static android.system.OsConstants.AF_NETLINK;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_NONBLOCK;
+
+import android.annotation.NonNull;
+import android.net.netlink.NetlinkErrorMessage;
+import android.net.netlink.NetlinkMessage;
+import android.net.netlink.NetlinkSocket;
+import android.net.util.PacketReader;
+import android.net.util.SharedLog;
+import android.net.util.SocketUtils;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * A simple base class to listen for netlink broadcasts.
+ *
+ * Opens a netlink socket of the given family and binds to the specified groups. Polls the socket
+ * from the event loop of the passed-in {@link Handler}, and calls the subclass-defined
+ * {@link #processNetlinkMessage} method on the handler thread for each netlink message that
+ * arrives. Currently ignores all netlink errors.
+ */
+public class NetlinkMonitor extends PacketReader {
+ protected final SharedLog mLog;
+ protected final String mTag;
+ private final int mFamily;
+ private final int mBindGroups;
+
+ private static final boolean DBG = false;
+
+ /**
+ * Constructs a new {@code NetlinkMonitor} instance.
+ *
+ * @param h The Handler on which to poll for messages and on which to call
+ * {@link #processNetlinkMessage}.
+ * @param log A SharedLog to log to.
+ * @param tag The log tag to use for log messages.
+ * @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}.
+ * @param bindGroups the netlink groups to bind to.
+ */
+ public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag,
+ int family, int bindGroups) {
+ super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
+ mLog = log.forSubComponent(tag);
+ mTag = tag;
+ mFamily = family;
+ mBindGroups = bindGroups;
+ }
+
+ @Override
+ protected FileDescriptor createFd() {
+ FileDescriptor fd = null;
+
+ try {
+ fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily);
+ Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups));
+ NetlinkSocket.connectToKernel(fd);
+
+ if (DBG) {
+ final SocketAddress nlAddr = Os.getsockname(fd);
+ Log.d(mTag, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
+ }
+ } catch (ErrnoException | SocketException e) {
+ logError("Failed to create rtnetlink socket", e);
+ closeSocketQuietly(fd);
+ return null;
+ }
+
+ return fd;
+ }
+
+ @Override
+ protected void handlePacket(byte[] recvbuf, int length) {
+ final long whenMs = SystemClock.elapsedRealtime();
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ while (byteBuffer.remaining() > 0) {
+ final int position = byteBuffer.position();
+ final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
+ if (nlMsg == null || nlMsg.getHeader() == null) {
+ byteBuffer.position(position);
+ mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
+ break;
+ }
+
+ if (nlMsg instanceof NetlinkErrorMessage) {
+ mLog.e("netlink error: " + nlMsg);
+ continue;
+ }
+
+ processNetlinkMessage(nlMsg, whenMs);
+ }
+ }
+
+ // TODO: move NetworkStackUtils to frameworks/libs/net for NetworkStackUtils#closeSocketQuietly.
+ private void closeSocketQuietly(FileDescriptor fd) {
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ignored) {
+ }
+ }
+
+ /**
+ * Processes one netlink message. Must be overridden by subclasses.
+ * @param nlMsg the message to process.
+ * @param whenMs the timestamp, as measured by {@link SystemClock#elapsedRealtime}, when the
+ * message was received.
+ */
+ protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { }
+}