• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.net.module.util.netlink;
18 
19 import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
20 import static android.system.OsConstants.AF_NETLINK;
21 import static android.system.OsConstants.EIO;
22 import static android.system.OsConstants.EPROTO;
23 import static android.system.OsConstants.ETIMEDOUT;
24 import static android.system.OsConstants.NETLINK_INET_DIAG;
25 import static android.system.OsConstants.SOCK_CLOEXEC;
26 import static android.system.OsConstants.SOCK_DGRAM;
27 import static android.system.OsConstants.SOL_SOCKET;
28 import static android.system.OsConstants.SO_RCVBUF;
29 import static android.system.OsConstants.SO_RCVTIMEO;
30 import static android.system.OsConstants.SO_SNDTIMEO;
31 
32 import android.net.util.SocketUtils;
33 import android.system.ErrnoException;
34 import android.system.Os;
35 import android.system.StructTimeval;
36 import android.util.Log;
37 
38 import androidx.annotation.NonNull;
39 import androidx.annotation.Nullable;
40 
41 import java.io.FileDescriptor;
42 import java.io.IOException;
43 import java.io.InterruptedIOException;
44 import java.net.SocketException;
45 import java.nio.ByteBuffer;
46 import java.nio.ByteOrder;
47 
48 /**
49  * Utilities for netlink related class that may not be able to fit into a specific class.
50  * @hide
51  */
52 public class NetlinkUtils {
53     private static final String TAG = "NetlinkUtils";
54     /** Corresponds to enum from bionic/libc/include/netinet/tcp.h. */
55     private static final int TCP_ESTABLISHED = 1;
56     private static final int TCP_SYN_SENT = 2;
57     private static final int TCP_SYN_RECV = 3;
58 
59     public static final int TCP_ALIVE_STATE_FILTER =
60             (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV);
61 
62     public static final int UNKNOWN_MARK = 0xffffffff;
63     public static final int NULL_MASK = 0;
64 
65     // Initial mark value corresponds to the initValue in system/netd/include/Fwmark.h.
66     public static final int INIT_MARK_VALUE = 0;
67     // Corresponds to enum definition in bionic/libc/kernel/uapi/linux/inet_diag.h
68     public static final int INET_DIAG_INFO = 2;
69     public static final int INET_DIAG_MARK = 15;
70 
71     public static final long IO_TIMEOUT_MS = 300L;
72 
73     public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
74     public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
75 
76     /**
77      * Return whether the input ByteBuffer contains enough remaining bytes for
78      * {@code StructNlMsgHdr}.
79      */
enoughBytesRemainForValidNlMsg(@onNull final ByteBuffer bytes)80     public static boolean enoughBytesRemainForValidNlMsg(@NonNull final ByteBuffer bytes) {
81         return bytes.remaining() >= StructNlMsgHdr.STRUCT_SIZE;
82     }
83 
84     /**
85      * Parse netlink error message
86      *
87      * @param bytes byteBuffer to parse netlink error message
88      * @return NetlinkErrorMessage if bytes contains valid NetlinkErrorMessage, else {@code null}
89      */
90     @Nullable
parseNetlinkErrorMessage(ByteBuffer bytes)91     private static NetlinkErrorMessage parseNetlinkErrorMessage(ByteBuffer bytes) {
92         final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes);
93         if (nlmsghdr == null || nlmsghdr.nlmsg_type != NetlinkConstants.NLMSG_ERROR) {
94             return null;
95         }
96         return NetlinkErrorMessage.parse(nlmsghdr, bytes);
97     }
98 
99     /**
100      * Receive netlink ack message and check error
101      *
102      * @param fd fd to read netlink message
103      */
receiveNetlinkAck(final FileDescriptor fd)104     public static void receiveNetlinkAck(final FileDescriptor fd)
105             throws InterruptedIOException, ErrnoException {
106         final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
107         // recvMessage() guaranteed to not return null if it did not throw.
108         final NetlinkErrorMessage response = parseNetlinkErrorMessage(bytes);
109         if (response != null && response.getNlMsgError() != null) {
110             final int errno = response.getNlMsgError().error;
111             if (errno != 0) {
112                 // TODO: consider ignoring EINVAL (-22), which appears to be
113                 // normal when probing a neighbor for which the kernel does
114                 // not already have / no longer has a link layer address.
115                 Log.e(TAG, "receiveNetlinkAck, errmsg=" + response.toString());
116                 // Note: convert kernel errnos (negative) into userspace errnos (positive).
117                 throw new ErrnoException(response.toString(), Math.abs(errno));
118             }
119         } else {
120             final String errmsg;
121             if (response == null) {
122                 bytes.position(0);
123                 errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
124             } else {
125                 errmsg = response.toString();
126             }
127             Log.e(TAG, "receiveNetlinkAck, errmsg=" + errmsg);
128             throw new ErrnoException(errmsg, EPROTO);
129         }
130     }
131 
132     /**
133      * Send one netlink message to kernel via netlink socket.
134      *
135      * @param nlProto netlink protocol type.
136      * @param msg the raw bytes of netlink message to be sent.
137      */
sendOneShotKernelMessage(int nlProto, byte[] msg)138     public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
139         final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
140         final FileDescriptor fd = netlinkSocketForProto(nlProto);
141 
142         try {
143             connectSocketToNetlink(fd);
144             sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT_MS);
145             receiveNetlinkAck(fd);
146         } catch (InterruptedIOException e) {
147             Log.e(TAG, errPrefix, e);
148             throw new ErrnoException(errPrefix, ETIMEDOUT, e);
149         } catch (SocketException e) {
150             Log.e(TAG, errPrefix, e);
151             throw new ErrnoException(errPrefix, EIO, e);
152         } finally {
153             try {
154                 SocketUtils.closeSocket(fd);
155             } catch (IOException e) {
156                 // Nothing we can do here
157             }
158         }
159     }
160 
161     /**
162      * Create netlink socket with the given netlink protocol type.
163      *
164      * @return fd the fileDescriptor of the socket.
165      * @throws ErrnoException if the FileDescriptor not connect to be created successfully
166      */
netlinkSocketForProto(int nlProto)167     public static FileDescriptor netlinkSocketForProto(int nlProto) throws ErrnoException {
168         final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
169         Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
170         return fd;
171     }
172 
173     /**
174      * Construct a netlink inet_diag socket.
175      */
createNetLinkInetDiagSocket()176     public static FileDescriptor createNetLinkInetDiagSocket() throws ErrnoException {
177         return Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_INET_DIAG);
178     }
179 
180     /**
181      * Connect the given file descriptor to the Netlink interface to the kernel.
182      *
183      * The fd must be a SOCK_DGRAM socket : create it with {@link #netlinkSocketForProto}
184      *
185      * @throws ErrnoException if the {@code fd} could not connect to kernel successfully
186      * @throws SocketException if there is an error accessing a socket.
187      */
connectSocketToNetlink(FileDescriptor fd)188     public static void connectSocketToNetlink(FileDescriptor fd)
189             throws ErrnoException, SocketException {
190         Os.connect(fd, makeNetlinkSocketAddress(0, 0));
191     }
192 
checkTimeout(long timeoutMs)193     private static void checkTimeout(long timeoutMs) {
194         if (timeoutMs < 0) {
195             throw new IllegalArgumentException("Negative timeouts not permitted");
196         }
197     }
198 
199     /**
200      * Wait up to |timeoutMs| (or until underlying socket error) for a
201      * netlink message of at most |bufsize| size.
202      *
203      * Multi-threaded calls with different timeouts will cause unexpected results.
204      */
recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)205     public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)
206             throws ErrnoException, IllegalArgumentException, InterruptedIOException {
207         checkTimeout(timeoutMs);
208 
209         Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
210 
211         final ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
212         final int length = Os.read(fd, byteBuffer);
213         if (length == bufsize) {
214             Log.w(TAG, "maximum read");
215         }
216         byteBuffer.position(0);
217         byteBuffer.limit(length);
218         byteBuffer.order(ByteOrder.nativeOrder());
219         return byteBuffer;
220     }
221 
222     /**
223      * Send a message to a peer to which this socket has previously connected.
224      *
225      * This waits at most |timeoutMs| milliseconds for the send to complete, will get the exception
226      * if it times out.
227      */
sendMessage( FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)228     public static int sendMessage(
229             FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
230             throws ErrnoException, IllegalArgumentException, InterruptedIOException {
231         checkTimeout(timeoutMs);
232         Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
233         return Os.write(fd, bytes, offset, count);
234     }
235 
NetlinkUtils()236     private NetlinkUtils() {}
237 }
238