1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.netlink; 18 19 import static android.net.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY; 20 import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; 21 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; 22 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; 23 import static android.os.Process.INVALID_UID; 24 import static android.system.OsConstants.AF_INET; 25 import static android.system.OsConstants.AF_INET6; 26 import static android.system.OsConstants.IPPROTO_UDP; 27 import static android.system.OsConstants.NETLINK_INET_DIAG; 28 29 import android.annotation.Nullable; 30 import android.net.util.SocketUtils; 31 import android.system.ErrnoException; 32 import android.util.Log; 33 34 import java.io.FileDescriptor; 35 import java.io.IOException; 36 import java.io.InterruptedIOException; 37 import java.net.Inet4Address; 38 import java.net.Inet6Address; 39 import java.net.InetSocketAddress; 40 import java.net.SocketException; 41 import java.net.UnknownHostException; 42 import java.nio.ByteBuffer; 43 import java.nio.ByteOrder; 44 45 /** 46 * A NetlinkMessage subclass for netlink inet_diag messages. 47 * 48 * see also: <linux_src>/include/uapi/linux/inet_diag.h 49 * 50 * @hide 51 */ 52 public class InetDiagMessage extends NetlinkMessage { 53 public static final String TAG = "InetDiagMessage"; 54 private static final int TIMEOUT_MS = 500; 55 InetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote, int family, short flags)56 public static byte[] InetDiagReqV2(int protocol, InetSocketAddress local, 57 InetSocketAddress remote, int family, short flags) { 58 return InetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */, 59 0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES); 60 } 61 62 /** 63 * Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException} 64 * if local and remote are not both null or both non-null. 65 * 66 * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP, 67 * IPPROTO_UDP, or IPPROTO_UDPLITE. 68 * @param local local socket address of the target socket. This will be packed into a 69 * {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of 70 * local or remote address is null. 71 * @param remote remote socket address of the target socket. This will be packed into a 72 * {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of 73 * local or remote address is null. 74 * @param family the ip family of the request message. This should be set to either AF_INET or 75 * AF_INET6 for IPv4 or IPv6 sockets respectively. 76 * @param flags message flags. See <linux_src>/include/uapi/linux/netlink.h. 77 * @param pad for raw socket protocol specification. 78 * @param idiagExt a set of flags defining what kind of extended information to report. 79 * @param state a bit mask that defines a filter of socket states. 80 * 81 * @return bytes array representation of the message 82 **/ InetDiagReqV2(int protocol, @Nullable InetSocketAddress local, @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt, int state)83 public static byte[] InetDiagReqV2(int protocol, @Nullable InetSocketAddress local, 84 @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt, 85 int state) throws NullPointerException { 86 final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE]; 87 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 88 byteBuffer.order(ByteOrder.nativeOrder()); 89 90 final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr(); 91 nlMsgHdr.nlmsg_len = bytes.length; 92 nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY; 93 nlMsgHdr.nlmsg_flags = flags; 94 nlMsgHdr.pack(byteBuffer); 95 final StructInetDiagReqV2 inetDiagReqV2 = 96 new StructInetDiagReqV2(protocol, local, remote, family, pad, idiagExt, state); 97 98 inetDiagReqV2.pack(byteBuffer); 99 return bytes; 100 } 101 102 public StructInetDiagMsg mStructInetDiagMsg; 103 InetDiagMessage(StructNlMsgHdr header)104 private InetDiagMessage(StructNlMsgHdr header) { 105 super(header); 106 mStructInetDiagMsg = new StructInetDiagMsg(); 107 } 108 parse(StructNlMsgHdr header, ByteBuffer byteBuffer)109 public static InetDiagMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { 110 final InetDiagMessage msg = new InetDiagMessage(header); 111 msg.mStructInetDiagMsg = StructInetDiagMsg.parse(byteBuffer); 112 return msg; 113 } 114 lookupUidByFamily(int protocol, InetSocketAddress local, InetSocketAddress remote, int family, short flags, FileDescriptor fd)115 private static int lookupUidByFamily(int protocol, InetSocketAddress local, 116 InetSocketAddress remote, int family, short flags, 117 FileDescriptor fd) 118 throws ErrnoException, InterruptedIOException { 119 byte[] msg = InetDiagReqV2(protocol, local, remote, family, flags); 120 NetlinkSocket.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS); 121 ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS); 122 123 final NetlinkMessage nlMsg = NetlinkMessage.parse(response, NETLINK_INET_DIAG); 124 final StructNlMsgHdr hdr = nlMsg.getHeader(); 125 if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) { 126 return INVALID_UID; 127 } 128 if (nlMsg instanceof InetDiagMessage) { 129 return ((InetDiagMessage) nlMsg).mStructInetDiagMsg.idiag_uid; 130 } 131 return INVALID_UID; 132 } 133 134 private static final int FAMILY[] = {AF_INET6, AF_INET}; 135 lookupUid(int protocol, InetSocketAddress local, InetSocketAddress remote, FileDescriptor fd)136 private static int lookupUid(int protocol, InetSocketAddress local, 137 InetSocketAddress remote, FileDescriptor fd) 138 throws ErrnoException, InterruptedIOException { 139 int uid; 140 141 for (int family : FAMILY) { 142 /** 143 * For exact match lookup, swap local and remote for UDP lookups due to kernel 144 * bug which will not be fixed. See aosp/755889 and 145 * https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html 146 */ 147 if (protocol == IPPROTO_UDP) { 148 uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd); 149 } else { 150 uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd); 151 } 152 if (uid != INVALID_UID) { 153 return uid; 154 } 155 } 156 157 /** 158 * For UDP it's possible for a socket to send packets to arbitrary destinations, even if the 159 * socket is not connected (and even if the socket is connected to a different destination). 160 * If we want this API to work for such packets, then on miss we need to do a second lookup 161 * with only the local address and port filled in. 162 * Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard. 163 */ 164 if (protocol == IPPROTO_UDP) { 165 try { 166 InetSocketAddress wildcard = new InetSocketAddress( 167 Inet6Address.getByName("::"), 0); 168 uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6, 169 (short) (NLM_F_REQUEST | NLM_F_DUMP), fd); 170 if (uid != INVALID_UID) { 171 return uid; 172 } 173 wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0); 174 uid = lookupUidByFamily(protocol, local, wildcard, AF_INET, 175 (short) (NLM_F_REQUEST | NLM_F_DUMP), fd); 176 if (uid != INVALID_UID) { 177 return uid; 178 } 179 } catch (UnknownHostException e) { 180 Log.e(TAG, e.toString()); 181 } 182 } 183 return INVALID_UID; 184 } 185 186 /** 187 * Use an inet_diag socket to look up the UID associated with the input local and remote 188 * address/port and protocol of a connection. 189 */ getConnectionOwnerUid(int protocol, InetSocketAddress local, InetSocketAddress remote)190 public static int getConnectionOwnerUid(int protocol, InetSocketAddress local, 191 InetSocketAddress remote) { 192 int uid = INVALID_UID; 193 FileDescriptor fd = null; 194 try { 195 fd = NetlinkSocket.forProto(NETLINK_INET_DIAG); 196 NetlinkSocket.connectToKernel(fd); 197 uid = lookupUid(protocol, local, remote, fd); 198 } catch (ErrnoException | SocketException | IllegalArgumentException 199 | InterruptedIOException e) { 200 Log.e(TAG, e.toString()); 201 } finally { 202 if (fd != null) { 203 try { 204 SocketUtils.closeSocket(fd); 205 } catch (IOException e) { 206 Log.e(TAG, e.toString()); 207 } 208 } 209 } 210 return uid; 211 } 212 213 @Override toString()214 public String toString() { 215 return "InetDiagMessage{ " 216 + "nlmsghdr{" 217 + (mHeader == null ? "" : mHeader.toString(NETLINK_INET_DIAG)) + "}, " 218 + "inet_diag_msg{" 219 + (mStructInetDiagMsg == null ? "" : mStructInetDiagMsg.toString()) + "} " 220 + "}"; 221 } 222 } 223