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);
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{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
217                 + "inet_diag_msg{"
218                 + (mStructInetDiagMsg == null ? "" : mStructInetDiagMsg.toString()) + "} "
219                 + "}";
220     }
221 }
222