diff options
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.java | 505 |
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()) + "} " - + "}"; - } -} |