summaryrefslogtreecommitdiff
path: root/common/device/com/android/net/module/util/netlink/InetDiagMessage.java
diff options
context:
space:
mode:
Diffstat (limited to 'common/device/com/android/net/module/util/netlink/InetDiagMessage.java')
-rw-r--r--common/device/com/android/net/module/util/netlink/InetDiagMessage.java505
1 files changed, 0 insertions, 505 deletions
diff --git a/common/device/com/android/net/module/util/netlink/InetDiagMessage.java b/common/device/com/android/net/module/util/netlink/InetDiagMessage.java
deleted file mode 100644
index f8b47164..00000000
--- a/common/device/com/android/net/module/util/netlink/InetDiagMessage.java
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * Copyright (C) 2018 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 com.android.net.module.util.netlink;
-
-import static android.os.Process.INVALID_UID;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.ENOENT;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.NETLINK_INET_DIAG;
-
-import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
-import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DESTROY;
-import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
-import static com.android.net.module.util.netlink.NetlinkConstants.hexify;
-import static com.android.net.module.util.netlink.NetlinkConstants.stringForAddressFamily;
-import static com.android.net.module.util.netlink.NetlinkConstants.stringForProtocol;
-import static com.android.net.module.util.netlink.NetlinkUtils.DEFAULT_RECV_BUFSIZE;
-import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS;
-import static com.android.net.module.util.netlink.NetlinkUtils.TCP_ALIVE_STATE_FILTER;
-import static com.android.net.module.util.netlink.NetlinkUtils.connectSocketToNetlink;
-import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-
-import android.net.util.SocketUtils;
-import android.os.Process;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.util.Log;
-import android.util.Range;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InterruptedIOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Predicate;
-
-/**
- * A NetlinkMessage subclass for netlink inet_diag messages.
- *
- * see also: <linux_src>/include/uapi/linux/inet_diag.h
- *
- * @hide
- */
-public class InetDiagMessage extends NetlinkMessage {
- public static final String TAG = "InetDiagMessage";
- private static final int TIMEOUT_MS = 500;
-
- /**
- * Construct an inet_diag_req_v2 message. This method will throw
- * {@link IllegalArgumentException} if local and remote are not both null or both non-null.
- */
- public static byte[] inetDiagReqV2(int protocol, InetSocketAddress local,
- InetSocketAddress remote, int family, short flags) {
- return inetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */,
- 0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES);
- }
-
- /**
- * Construct an inet_diag_req_v2 message. This method will throw
- * {@code IllegalArgumentException} if local and remote are not both null or both non-null.
- *
- * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
- * IPPROTO_UDP, or IPPROTO_UDPLITE.
- * @param local local socket address of the target socket. This will be packed into a
- * {@link StructInetDiagSockId}. Request to diagnose for all sockets if both of
- * local or remote address is null.
- * @param remote remote socket address of the target socket. This will be packed into a
- * {@link StructInetDiagSockId}. Request to diagnose for all sockets if both of
- * local or remote address is null.
- * @param family the ip family of the request message. This should be set to either AF_INET or
- * AF_INET6 for IPv4 or IPv6 sockets respectively.
- * @param flags message flags. See <linux_src>/include/uapi/linux/netlink.h.
- * @param pad for raw socket protocol specification.
- * @param idiagExt a set of flags defining what kind of extended information to report.
- * @param state a bit mask that defines a filter of socket states.
- *
- * @return bytes array representation of the message
- */
- public static byte[] inetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
- @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt,
- int state) throws IllegalArgumentException {
- // Request for all sockets if no specific socket is requested. Specify the local and remote
- // socket address information for target request socket.
- if ((local == null) != (remote == null)) {
- throw new IllegalArgumentException(
- "Local and remote must be both null or both non-null");
- }
- final StructInetDiagSockId id = ((local != null && remote != null)
- ? new StructInetDiagSockId(local, remote) : null);
- return inetDiagReqV2(protocol, id, family,
- SOCK_DIAG_BY_FAMILY, flags, pad, idiagExt, state);
- }
-
- /**
- * Construct an inet_diag_req_v2 message.
- *
- * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
- * IPPROTO_UDP, or IPPROTO_UDPLITE.
- * @param id inet_diag_sockid. See {@link StructInetDiagSockId}
- * @param family the ip family of the request message. This should be set to either AF_INET or
- * AF_INET6 for IPv4 or IPv6 sockets respectively.
- * @param type message types.
- * @param flags message flags. See <linux_src>/include/uapi/linux/netlink.h.
- * @param pad for raw socket protocol specification.
- * @param idiagExt a set of flags defining what kind of extended information to report.
- * @param state a bit mask that defines a filter of socket states.
- * @return bytes array representation of the message
- */
- public static byte[] inetDiagReqV2(int protocol, @Nullable StructInetDiagSockId id, int family,
- short type, short flags, int pad, int idiagExt, int state) {
- final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
- final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr();
- nlMsgHdr.nlmsg_len = bytes.length;
- nlMsgHdr.nlmsg_type = type;
- nlMsgHdr.nlmsg_flags = flags;
- nlMsgHdr.pack(byteBuffer);
- final StructInetDiagReqV2 inetDiagReqV2 =
- new StructInetDiagReqV2(protocol, id, family, pad, idiagExt, state);
-
- inetDiagReqV2.pack(byteBuffer);
- return bytes;
- }
-
- public StructInetDiagMsg inetDiagMsg;
-
- @VisibleForTesting
- public InetDiagMessage(@NonNull StructNlMsgHdr header) {
- super(header);
- inetDiagMsg = new StructInetDiagMsg();
- }
-
- /**
- * Parse an inet_diag_req_v2 message from buffer.
- */
- @Nullable
- public static InetDiagMessage parse(@NonNull StructNlMsgHdr header,
- @NonNull ByteBuffer byteBuffer) {
- final InetDiagMessage msg = new InetDiagMessage(header);
- msg.inetDiagMsg = StructInetDiagMsg.parse(byteBuffer);
- if (msg.inetDiagMsg == null) {
- return null;
- }
- return msg;
- }
-
- private static void closeSocketQuietly(final FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
- private static int lookupUidByFamily(int protocol, InetSocketAddress local,
- InetSocketAddress remote, int family, short flags,
- FileDescriptor fd)
- throws ErrnoException, InterruptedIOException {
- byte[] msg = inetDiagReqV2(protocol, local, remote, family, flags);
- NetlinkUtils.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS);
- ByteBuffer response = NetlinkUtils.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS);
-
- final NetlinkMessage nlMsg = NetlinkMessage.parse(response, NETLINK_INET_DIAG);
- if (nlMsg == null) {
- return INVALID_UID;
- }
- final StructNlMsgHdr hdr = nlMsg.getHeader();
- if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
- return INVALID_UID;
- }
- if (nlMsg instanceof InetDiagMessage) {
- return ((InetDiagMessage) nlMsg).inetDiagMsg.idiag_uid;
- }
- return INVALID_UID;
- }
-
- private static final int[] FAMILY = {AF_INET6, AF_INET};
-
- private static int lookupUid(int protocol, InetSocketAddress local,
- InetSocketAddress remote, FileDescriptor fd)
- throws ErrnoException, InterruptedIOException {
- int uid;
-
- for (int family : FAMILY) {
- /**
- * For exact match lookup, swap local and remote for UDP lookups due to kernel
- * bug which will not be fixed. See aosp/755889 and
- * https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html
- */
- if (protocol == IPPROTO_UDP) {
- uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd);
- } else {
- uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd);
- }
- if (uid != INVALID_UID) {
- return uid;
- }
- }
-
- /**
- * For UDP it's possible for a socket to send packets to arbitrary destinations, even if the
- * socket is not connected (and even if the socket is connected to a different destination).
- * If we want this API to work for such packets, then on miss we need to do a second lookup
- * with only the local address and port filled in.
- * Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard.
- */
- if (protocol == IPPROTO_UDP) {
- try {
- InetSocketAddress wildcard = new InetSocketAddress(
- Inet6Address.getByName("::"), 0);
- uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6,
- (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
- if (uid != INVALID_UID) {
- return uid;
- }
- wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0);
- uid = lookupUidByFamily(protocol, local, wildcard, AF_INET,
- (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
- if (uid != INVALID_UID) {
- return uid;
- }
- } catch (UnknownHostException e) {
- Log.e(TAG, e.toString());
- }
- }
- return INVALID_UID;
- }
-
- /**
- * Use an inet_diag socket to look up the UID associated with the input local and remote
- * address/port and protocol of a connection.
- */
- public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
- InetSocketAddress remote) {
- int uid = INVALID_UID;
- FileDescriptor fd = null;
- try {
- fd = NetlinkUtils.netlinkSocketForProto(NETLINK_INET_DIAG);
- NetlinkUtils.connectSocketToNetlink(fd);
- uid = lookupUid(protocol, local, remote, fd);
- } catch (ErrnoException | SocketException | IllegalArgumentException
- | InterruptedIOException e) {
- Log.e(TAG, e.toString());
- } finally {
- closeSocketQuietly(fd);
- }
- return uid;
- }
-
- /**
- * Construct an inet_diag_req_v2 message for querying alive TCP sockets from kernel.
- */
- public static byte[] buildInetDiagReqForAliveTcpSockets(int family) {
- return inetDiagReqV2(IPPROTO_TCP,
- null /* local addr */,
- null /* remote addr */,
- family,
- (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_DUMP) /* flag */,
- 0 /* pad */,
- 1 << NetlinkConstants.INET_DIAG_MEMINFO /* idiagExt */,
- TCP_ALIVE_STATE_FILTER);
- }
-
- private static void sendNetlinkDestroyRequest(FileDescriptor fd, int proto,
- InetDiagMessage diagMsg) throws InterruptedIOException, ErrnoException {
- final byte[] destroyMsg = InetDiagMessage.inetDiagReqV2(
- proto,
- diagMsg.inetDiagMsg.id,
- diagMsg.inetDiagMsg.idiag_family,
- SOCK_DESTROY,
- (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK),
- 0 /* pad */,
- 0 /* idiagExt */,
- 1 << diagMsg.inetDiagMsg.idiag_state
- );
- NetlinkUtils.sendMessage(fd, destroyMsg, 0, destroyMsg.length, IO_TIMEOUT_MS);
- NetlinkUtils.receiveNetlinkAck(fd);
- }
-
- private static void sendNetlinkDumpRequest(FileDescriptor fd, int proto, int states, int family)
- throws InterruptedIOException, ErrnoException {
- final byte[] dumpMsg = InetDiagMessage.inetDiagReqV2(
- proto,
- null /* id */,
- family,
- SOCK_DIAG_BY_FAMILY,
- (short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_DUMP),
- 0 /* pad */,
- 0 /* idiagExt */,
- states);
- NetlinkUtils.sendMessage(fd, dumpMsg, 0, dumpMsg.length, IO_TIMEOUT_MS);
- }
-
- private static int processNetlinkDumpAndDestroySockets(FileDescriptor dumpFd,
- FileDescriptor destroyFd, int proto, Predicate<InetDiagMessage> filter)
- throws InterruptedIOException, ErrnoException {
- int destroyedSockets = 0;
-
- while (true) {
- final ByteBuffer buf = NetlinkUtils.recvMessage(
- dumpFd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
-
- while (buf.remaining() > 0) {
- final int position = buf.position();
- final NetlinkMessage nlMsg = NetlinkMessage.parse(buf, NETLINK_INET_DIAG);
- if (nlMsg == null) {
- // Move to the position where parse started for error log.
- buf.position(position);
- Log.e(TAG, "Failed to parse netlink message: " + hexify(buf));
- break;
- }
-
- if (nlMsg.getHeader().nlmsg_type == NLMSG_DONE) {
- return destroyedSockets;
- }
-
- if (!(nlMsg instanceof InetDiagMessage)) {
- Log.wtf(TAG, "Received unexpected netlink message: " + nlMsg);
- continue;
- }
-
- final InetDiagMessage diagMsg = (InetDiagMessage) nlMsg;
- if (filter.test(diagMsg)) {
- try {
- sendNetlinkDestroyRequest(destroyFd, proto, diagMsg);
- destroyedSockets++;
- } catch (InterruptedIOException | ErrnoException e) {
- if (!(e instanceof ErrnoException
- && ((ErrnoException) e).errno == ENOENT)) {
- Log.e(TAG, "Failed to destroy socket: diagMsg=" + diagMsg + ", " + e);
- }
- }
- }
- }
- }
- }
-
- /**
- * Returns whether the InetDiagMessage is for adb socket or not
- */
- @VisibleForTesting
- public static boolean isAdbSocket(final InetDiagMessage msg) {
- // This is inaccurate since adb could run with ROOT_UID or other services can run with
- // SHELL_UID. But this check covers most cases and enough.
- // Note that getting service.adb.tcp.port system property is prohibited by sepolicy
- // TODO: skip the socket only if there is a listen socket owned by SHELL_UID with the same
- // source port as this socket
- return msg.inetDiagMsg.idiag_uid == Process.SHELL_UID;
- }
-
- /**
- * Returns whether the range contains the uid in the InetDiagMessage or not
- */
- @VisibleForTesting
- public static boolean containsUid(InetDiagMessage msg, Set<Range<Integer>> ranges) {
- for (final Range<Integer> range: ranges) {
- if (range.contains(msg.inetDiagMsg.idiag_uid)) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean isLoopbackAddress(InetAddress addr) {
- if (addr.isLoopbackAddress()) return true;
- if (!(addr instanceof Inet6Address)) return false;
-
- // Following check is for v4-mapped v6 address. StructInetDiagSockId contains v4-mapped v6
- // address as Inet6Address, See StructInetDiagSockId#parse
- final byte[] addrBytes = addr.getAddress();
- for (int i = 0; i < 10; i++) {
- if (addrBytes[i] != 0) return false;
- }
- return addrBytes[10] == (byte) 0xff
- && addrBytes[11] == (byte) 0xff
- && addrBytes[12] == 127;
- }
-
- /**
- * Returns whether the socket address in the InetDiagMessage is loopback or not
- */
- @VisibleForTesting
- public static boolean isLoopback(InetDiagMessage msg) {
- final InetAddress srcAddr = msg.inetDiagMsg.id.locSocketAddress.getAddress();
- final InetAddress dstAddr = msg.inetDiagMsg.id.remSocketAddress.getAddress();
- return isLoopbackAddress(srcAddr)
- || isLoopbackAddress(dstAddr)
- || srcAddr.equals(dstAddr);
- }
-
- private static void destroySockets(int proto, int states, Predicate<InetDiagMessage> filter)
- throws ErrnoException, SocketException, InterruptedIOException {
- FileDescriptor dumpFd = null;
- FileDescriptor destroyFd = null;
-
- try {
- dumpFd = NetlinkUtils.createNetLinkInetDiagSocket();
- destroyFd = NetlinkUtils.createNetLinkInetDiagSocket();
- connectSocketToNetlink(dumpFd);
- connectSocketToNetlink(destroyFd);
-
- for (int family : List.of(AF_INET, AF_INET6)) {
- try {
- sendNetlinkDumpRequest(dumpFd, proto, states, family);
- } catch (InterruptedIOException | ErrnoException e) {
- Log.e(TAG, "Failed to send netlink dump request: " + e);
- continue;
- }
- final int destroyedSockets = processNetlinkDumpAndDestroySockets(
- dumpFd, destroyFd, proto, filter);
- Log.d(TAG, "Destroyed " + destroyedSockets + " sockets"
- + ", proto=" + stringForProtocol(proto)
- + ", family=" + stringForAddressFamily(family)
- + ", states=" + states);
- }
- } finally {
- closeSocketQuietly(dumpFd);
- closeSocketQuietly(destroyFd);
- }
- }
-
- /**
- * Close tcp sockets that match the following condition
- * 1. TCP status is one of TCP_ESTABLISHED, TCP_SYN_SENT, and TCP_SYN_RECV
- * 2. Owner uid of socket is not in the exemptUids
- * 3. Owner uid of socket is in the ranges
- * 4. Socket is not loopback
- * 5. Socket is not adb socket
- *
- * @param ranges target uid ranges
- * @param exemptUids uids to skip close socket
- */
- public static void destroyLiveTcpSockets(Set<Range<Integer>> ranges, Set<Integer> exemptUids)
- throws SocketException, InterruptedIOException, ErrnoException {
- final long startTimeMs = SystemClock.elapsedRealtime();
- destroySockets(IPPROTO_TCP, TCP_ALIVE_STATE_FILTER,
- (diagMsg) -> !exemptUids.contains(diagMsg.inetDiagMsg.idiag_uid)
- && containsUid(diagMsg, ranges)
- && !isLoopback(diagMsg)
- && !isAdbSocket(diagMsg));
- final long durationMs = SystemClock.elapsedRealtime() - startTimeMs;
- Log.d(TAG, "Destroyed live tcp sockets for uids=" + ranges + " exemptUids=" + exemptUids
- + " in " + durationMs + "ms");
- }
-
- /**
- * Close tcp sockets that match the following condition
- * 1. TCP status is one of TCP_ESTABLISHED, TCP_SYN_SENT, and TCP_SYN_RECV
- * 2. Owner uid of socket is in the targetUids
- * 3. Socket is not loopback
- * 4. Socket is not adb socket
- *
- * @param ownerUids target uids to close sockets
- */
- public static void destroyLiveTcpSocketsByOwnerUids(Set<Integer> ownerUids)
- throws SocketException, InterruptedIOException, ErrnoException {
- final long startTimeMs = SystemClock.elapsedRealtime();
- destroySockets(IPPROTO_TCP, TCP_ALIVE_STATE_FILTER,
- (diagMsg) -> ownerUids.contains(diagMsg.inetDiagMsg.idiag_uid)
- && !isLoopback(diagMsg)
- && !isAdbSocket(diagMsg));
- final long durationMs = SystemClock.elapsedRealtime() - startTimeMs;
- Log.d(TAG, "Destroyed live tcp sockets for uids=" + ownerUids + " in " + durationMs + "ms");
- }
-
- @Override
- public String toString() {
- return "InetDiagMessage{ "
- + "nlmsghdr{"
- + (mHeader == null ? "" : mHeader.toString(NETLINK_INET_DIAG)) + "}, "
- + "inet_diag_msg{"
- + (inetDiagMsg == null ? "" : inetDiagMsg.toString()) + "} "
- + "}";
- }
-}