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.NETLINK_ROUTE; 26 import static android.system.OsConstants.SOCK_CLOEXEC; 27 import static android.system.OsConstants.SOCK_DGRAM; 28 import static android.system.OsConstants.SOL_SOCKET; 29 import static android.system.OsConstants.SO_RCVBUF; 30 import static android.system.OsConstants.SO_RCVTIMEO; 31 import static android.system.OsConstants.SO_SNDTIMEO; 32 33 import static com.android.net.module.util.netlink.NetlinkConstants.hexify; 34 import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE; 35 import static com.android.net.module.util.netlink.NetlinkConstants.RTNL_FAMILY_IP6MR; 36 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP; 37 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; 38 39 import android.net.util.SocketUtils; 40 import android.system.ErrnoException; 41 import android.system.Os; 42 import android.system.OsConstants; 43 import android.system.StructTimeval; 44 import android.util.Log; 45 46 import androidx.annotation.NonNull; 47 import androidx.annotation.Nullable; 48 49 import java.io.FileDescriptor; 50 import java.io.IOException; 51 import java.io.InterruptedIOException; 52 import java.net.Inet6Address; 53 import java.net.InetAddress; 54 import java.net.SocketException; 55 import java.nio.ByteBuffer; 56 import java.nio.ByteOrder; 57 import java.util.ArrayList; 58 import java.util.List; 59 import java.util.Objects; 60 import java.util.function.Consumer; 61 62 /** 63 * Utilities for netlink related class that may not be able to fit into a specific class. 64 * @hide 65 */ 66 public class NetlinkUtils { 67 private static final String TAG = "NetlinkUtils"; 68 /** Corresponds to enum from bionic/libc/include/netinet/tcp.h. */ 69 private static final int TCP_ESTABLISHED = 1; 70 private static final int TCP_SYN_SENT = 2; 71 private static final int TCP_SYN_RECV = 3; 72 73 public static final int TCP_ALIVE_STATE_FILTER = 74 (1 << TCP_ESTABLISHED) | (1 << TCP_SYN_SENT) | (1 << TCP_SYN_RECV); 75 76 public static final int UNKNOWN_MARK = 0xffffffff; 77 public static final int NULL_MASK = 0; 78 79 // Initial mark value corresponds to the initValue in system/netd/include/Fwmark.h. 80 public static final int INIT_MARK_VALUE = 0; 81 // Corresponds to enum definition in bionic/libc/kernel/uapi/linux/inet_diag.h 82 public static final int INET_DIAG_INFO = 2; 83 public static final int INET_DIAG_MARK = 15; 84 85 public static final long IO_TIMEOUT_MS = 300L; 86 87 public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; 88 public static final int SOCKET_RECV_BUFSIZE = 64 * 1024; 89 public static final int SOCKET_DUMP_RECV_BUFSIZE = 128 * 1024; 90 91 /** 92 * Return whether the input ByteBuffer contains enough remaining bytes for 93 * {@code StructNlMsgHdr}. 94 */ enoughBytesRemainForValidNlMsg(@onNull final ByteBuffer bytes)95 public static boolean enoughBytesRemainForValidNlMsg(@NonNull final ByteBuffer bytes) { 96 return bytes.remaining() >= StructNlMsgHdr.STRUCT_SIZE; 97 } 98 99 /** 100 * Parse netlink error message 101 * 102 * @param bytes byteBuffer to parse netlink error message 103 * @return NetlinkErrorMessage if bytes contains valid NetlinkErrorMessage, else {@code null} 104 */ 105 @Nullable parseNetlinkErrorMessage(ByteBuffer bytes)106 private static NetlinkErrorMessage parseNetlinkErrorMessage(ByteBuffer bytes) { 107 final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes); 108 if (nlmsghdr == null || nlmsghdr.nlmsg_type != NetlinkConstants.NLMSG_ERROR) { 109 return null; 110 } 111 112 final int messageLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len); 113 final int payloadLength = messageLength - StructNlMsgHdr.STRUCT_SIZE; 114 if (payloadLength < 0 || payloadLength > bytes.remaining()) { 115 // Malformed message or runt buffer. Pretend the buffer was consumed. 116 bytes.position(bytes.limit()); 117 return null; 118 } 119 120 return NetlinkErrorMessage.parse(nlmsghdr, bytes); 121 } 122 123 /** 124 * Receive netlink ack message and check error 125 * 126 * @param fd fd to read netlink message 127 */ receiveNetlinkAck(final FileDescriptor fd)128 public static void receiveNetlinkAck(final FileDescriptor fd) 129 throws InterruptedIOException, ErrnoException { 130 final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS); 131 // recvMessage() guaranteed to not return null if it did not throw. 132 final NetlinkErrorMessage response = parseNetlinkErrorMessage(bytes); 133 if (response != null && response.getNlMsgError() != null) { 134 final int errno = response.getNlMsgError().error; 135 if (errno != 0) { 136 // TODO: consider ignoring EINVAL (-22), which appears to be 137 // normal when probing a neighbor for which the kernel does 138 // not already have / no longer has a link layer address. 139 Log.e(TAG, "receiveNetlinkAck, errmsg=" + response.toString()); 140 // Note: convert kernel errnos (negative) into userspace errnos (positive). 141 throw new ErrnoException(response.toString(), Math.abs(errno)); 142 } 143 } else { 144 final String errmsg; 145 if (response == null) { 146 bytes.position(0); 147 errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); 148 } else { 149 errmsg = response.toString(); 150 } 151 Log.e(TAG, "receiveNetlinkAck, errmsg=" + errmsg); 152 throw new ErrnoException(errmsg, EPROTO); 153 } 154 } 155 156 /** 157 * Send one netlink message to kernel via netlink socket. 158 * 159 * @param nlProto netlink protocol type. 160 * @param msg the raw bytes of netlink message to be sent. 161 */ sendOneShotKernelMessage(int nlProto, byte[] msg)162 public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException { 163 final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage"; 164 final FileDescriptor fd = netlinkSocketForProto(nlProto, SOCKET_RECV_BUFSIZE); 165 166 try { 167 connectToKernel(fd); 168 sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT_MS); 169 receiveNetlinkAck(fd); 170 } catch (InterruptedIOException e) { 171 Log.e(TAG, errPrefix, e); 172 throw new ErrnoException(errPrefix, ETIMEDOUT, e); 173 } catch (SocketException e) { 174 Log.e(TAG, errPrefix, e); 175 throw new ErrnoException(errPrefix, EIO, e); 176 } finally { 177 closeSocketQuietly(fd); 178 } 179 } 180 181 /** 182 * Send an RTM_NEWADDR message to kernel to add or update an IP address. 183 * 184 * @param ifIndex interface index. 185 * @param ip IP address to be added. 186 * @param prefixlen IP address prefix length. 187 * @param flags IP address flags. 188 * @param scope IP address scope. 189 * @param preferred The preferred lifetime of IP address. 190 * @param valid The valid lifetime of IP address. 191 */ sendRtmNewAddressRequest(int ifIndex, @NonNull final InetAddress ip, short prefixlen, int flags, byte scope, long preferred, long valid)192 public static boolean sendRtmNewAddressRequest(int ifIndex, @NonNull final InetAddress ip, 193 short prefixlen, int flags, byte scope, long preferred, long valid) { 194 Objects.requireNonNull(ip, "IP address to be added should not be null."); 195 final byte[] msg = RtNetlinkAddressMessage.newRtmNewAddressMessage(1 /* seqNo*/, ip, 196 prefixlen, flags, scope, ifIndex, preferred, valid); 197 try { 198 NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg); 199 return true; 200 } catch (ErrnoException e) { 201 Log.e(TAG, "Fail to send RTM_NEWADDR to add " + ip.getHostAddress(), e); 202 return false; 203 } 204 } 205 206 /** 207 * Send an RTM_DELADDR message to kernel to delete an IPv6 address. 208 * 209 * @param ifIndex interface index. 210 * @param ip IPv6 address to be deleted. 211 * @param prefixlen IPv6 address prefix length. 212 */ sendRtmDelAddressRequest(int ifIndex, final Inet6Address ip, short prefixlen)213 public static boolean sendRtmDelAddressRequest(int ifIndex, final Inet6Address ip, 214 short prefixlen) { 215 Objects.requireNonNull(ip, "IPv6 address to be deleted should not be null."); 216 final byte[] msg = RtNetlinkAddressMessage.newRtmDelAddressMessage(1 /* seqNo*/, ip, 217 prefixlen, ifIndex); 218 try { 219 NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg); 220 return true; 221 } catch (ErrnoException e) { 222 Log.e(TAG, "Fail to send RTM_DELADDR to delete " + ip.getHostAddress(), e); 223 return false; 224 } 225 } 226 227 /** 228 * Create netlink socket with the given netlink protocol type and buffersize. 229 * 230 * @param nlProto the netlink protocol 231 * @param bufferSize the receive buffer size to set when the value is not 0 232 * 233 * @return fd the fileDescriptor of the socket. 234 * @throws ErrnoException if the FileDescriptor not connect to be created successfully 235 */ netlinkSocketForProto(int nlProto, int bufferSize)236 public static FileDescriptor netlinkSocketForProto(int nlProto, int bufferSize) 237 throws ErrnoException { 238 final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, nlProto); 239 if (bufferSize > 0) { 240 Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, bufferSize); 241 } 242 return fd; 243 } 244 245 /** 246 * Create netlink socket with the given netlink protocol type. Receive buffer size is not set. 247 * 248 * @param nlProto the netlink protocol 249 * 250 * @return fd the fileDescriptor of the socket. 251 * @throws ErrnoException if the FileDescriptor not connect to be created successfully 252 */ netlinkSocketForProto(int nlProto)253 public static FileDescriptor netlinkSocketForProto(int nlProto) 254 throws ErrnoException { 255 return netlinkSocketForProto(nlProto, 0); 256 } 257 258 /** 259 * Construct a netlink inet_diag socket. 260 */ createNetLinkInetDiagSocket()261 public static FileDescriptor createNetLinkInetDiagSocket() throws ErrnoException { 262 return netlinkSocketForProto(NETLINK_INET_DIAG); 263 } 264 265 /** 266 * Connect the given file descriptor to the Netlink interface to the kernel. 267 * 268 * The fd must be a SOCK_DGRAM socket : create it with {@link #netlinkSocketForProto} 269 * 270 * @throws ErrnoException if the {@code fd} could not connect to kernel successfully 271 * @throws SocketException if there is an error accessing a socket. 272 */ connectToKernel(@onNull FileDescriptor fd)273 public static void connectToKernel(@NonNull FileDescriptor fd) 274 throws ErrnoException, SocketException { 275 Os.connect(fd, makeNetlinkSocketAddress(0, 0)); 276 } 277 checkTimeout(long timeoutMs)278 private static void checkTimeout(long timeoutMs) { 279 if (timeoutMs < 0) { 280 throw new IllegalArgumentException("Negative timeouts not permitted"); 281 } 282 } 283 284 /** 285 * Wait up to |timeoutMs| (or until underlying socket error) for a 286 * netlink message of at most |bufsize| size. 287 * 288 * Multi-threaded calls with different timeouts will cause unexpected results. 289 */ recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)290 public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs) 291 throws ErrnoException, IllegalArgumentException, InterruptedIOException { 292 checkTimeout(timeoutMs); 293 294 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs)); 295 296 final ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); 297 final int length = Os.read(fd, byteBuffer); 298 if (length == bufsize) { 299 Log.w(TAG, "maximum read"); 300 } 301 byteBuffer.position(0); 302 byteBuffer.limit(length); 303 byteBuffer.order(ByteOrder.nativeOrder()); 304 return byteBuffer; 305 } 306 307 /** 308 * Send a message to a peer to which this socket has previously connected. 309 * 310 * This waits at most |timeoutMs| milliseconds for the send to complete, will get the exception 311 * if it times out. 312 */ sendMessage( FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)313 public static int sendMessage( 314 FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs) 315 throws ErrnoException, IllegalArgumentException, InterruptedIOException { 316 checkTimeout(timeoutMs); 317 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs)); 318 return Os.write(fd, bytes, offset, count); 319 } 320 321 private static final long CLOCK_TICKS_PER_SECOND = Os.sysconf(OsConstants._SC_CLK_TCK); 322 323 /** 324 * Convert the system time in clock ticks(clock_t type in times(), not in clock()) to 325 * milliseconds. times() clock_t ticks at the kernel's USER_HZ (100) while clock() clock_t 326 * ticks at CLOCKS_PER_SEC (1000000). 327 * 328 * See the NOTES on https://man7.org/linux/man-pages/man2/times.2.html for the difference 329 * of clock_t used in clock() and times(). 330 */ ticksToMilliSeconds(int intClockTicks)331 public static long ticksToMilliSeconds(int intClockTicks) { 332 final long longClockTicks = intClockTicks & 0xffffffffL; 333 return (longClockTicks * 1000) / CLOCK_TICKS_PER_SECOND; 334 } 335 NetlinkUtils()336 private NetlinkUtils() {} 337 getAndProcessNetlinkDumpMessagesWithFd( FileDescriptor fd, byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass, Consumer<T> func)338 private static <T extends NetlinkMessage> void getAndProcessNetlinkDumpMessagesWithFd( 339 FileDescriptor fd, byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass, 340 Consumer<T> func) 341 throws SocketException, InterruptedIOException, ErrnoException { 342 // connecToKernel throws ErrnoException and SocketException, should be handled by caller 343 connectToKernel(fd); 344 345 // sendMessage throws InterruptedIOException and ErrnoException, 346 // should be handled by caller 347 sendMessage(fd, dumpRequestMessage, 0, dumpRequestMessage.length, IO_TIMEOUT_MS); 348 349 while (true) { 350 // recvMessage throws ErrnoException, InterruptedIOException 351 // should be handled by caller 352 final ByteBuffer buf = recvMessage( 353 fd, NetlinkUtils.DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS); 354 355 while (buf.remaining() > 0) { 356 final int position = buf.position(); 357 final NetlinkMessage nlMsg = NetlinkMessage.parse(buf, nlFamily); 358 if (nlMsg == null) { 359 // Move to the position where parse started for error log. 360 buf.position(position); 361 Log.e(TAG, "Failed to parse netlink message: " + hexify(buf)); 362 break; 363 } 364 365 if (nlMsg.getHeader().nlmsg_type == NLMSG_DONE) { 366 return; 367 } 368 369 if (!msgClass.isInstance(nlMsg)) { 370 Log.wtf(TAG, "Received unexpected netlink message: " + nlMsg); 371 continue; 372 } 373 374 final T msg = (T) nlMsg; 375 func.accept(msg); 376 } 377 } 378 } 379 /** 380 * Sends a netlink dump request and processes the returned dump messages 381 * 382 * @param <T> extends NetlinkMessage 383 * @param dumpRequestMessage netlink dump request message to be sent 384 * @param nlFamily netlink family 385 * @param msgClass expected class of the netlink message 386 * @param func function defined by caller to handle the dump messages 387 * @throws SocketException when fails to connect socket to kernel 388 * @throws InterruptedIOException when fails to read the dumpFd 389 * @throws ErrnoException when fails to create dump fd, send dump request 390 * or receive messages 391 */ getAndProcessNetlinkDumpMessages( byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass, Consumer<T> func)392 public static <T extends NetlinkMessage> void getAndProcessNetlinkDumpMessages( 393 byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass, 394 Consumer<T> func) 395 throws SocketException, InterruptedIOException, ErrnoException { 396 // Create socket 397 final FileDescriptor fd = netlinkSocketForProto(nlFamily, SOCKET_DUMP_RECV_BUFSIZE); 398 try { 399 getAndProcessNetlinkDumpMessagesWithFd(fd, dumpRequestMessage, nlFamily, 400 msgClass, func); 401 } finally { 402 closeSocketQuietly(fd); 403 } 404 } 405 406 /** 407 * Construct a RTM_GETROUTE message for dumping multicast IPv6 routes from kernel. 408 */ newIpv6MulticastRouteDumpRequest()409 private static byte[] newIpv6MulticastRouteDumpRequest() { 410 final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); 411 nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETROUTE; 412 nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 413 final short shortZero = 0; 414 415 // family must be RTNL_FAMILY_IP6MR to dump IPv6 multicast routes. 416 // dstLen, srcLen, tos and scope must be zero in FIB dump request. 417 // protocol, flags must be 0, and type must be RTN_MULTICAST (if not 0) for multicast 418 // dump request. 419 // table or RTA_TABLE attributes can be used to dump a specific routing table. 420 // RTA_OIF attribute can be used to dump only routes containing given oif. 421 // Here no attributes are set so the kernel can return all multicast routes. 422 final StructRtMsg rtMsg = 423 new StructRtMsg(RTNL_FAMILY_IP6MR /* family */, shortZero /* dstLen */, 424 shortZero /* srcLen */, shortZero /* tos */, shortZero /* table */, 425 shortZero /* protocol */, shortZero /* scope */, shortZero /* type */, 426 0L /* flags */); 427 final RtNetlinkRouteMessage msg = 428 new RtNetlinkRouteMessage(nlmsghdr, rtMsg); 429 430 final int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructRtMsg.STRUCT_SIZE; 431 nlmsghdr.nlmsg_len = spaceRequired; 432 final byte[] bytes = new byte[NetlinkConstants.alignedLengthOf(spaceRequired)]; 433 final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); 434 byteBuffer.order(ByteOrder.nativeOrder()); 435 msg.pack(byteBuffer); 436 return bytes; 437 } 438 439 /** 440 * Get the list of IPv6 multicast route messages from kernel. 441 */ getIpv6MulticastRoutes()442 public static List<RtNetlinkRouteMessage> getIpv6MulticastRoutes() { 443 final byte[] dumpMsg = newIpv6MulticastRouteDumpRequest(); 444 List<RtNetlinkRouteMessage> routes = new ArrayList<>(); 445 Consumer<RtNetlinkRouteMessage> handleNlDumpMsg = (msg) -> { 446 if (msg.getRtmFamily() == RTNL_FAMILY_IP6MR) { 447 // Sent rtmFamily RTNL_FAMILY_IP6MR in dump request to make sure ipv6 448 // multicast routes are included in netlink reply messages, the kernel 449 // may also reply with other kind of routes, so we filter them out here. 450 routes.add(msg); 451 } 452 }; 453 try { 454 NetlinkUtils.<RtNetlinkRouteMessage>getAndProcessNetlinkDumpMessages( 455 dumpMsg, NETLINK_ROUTE, RtNetlinkRouteMessage.class, 456 handleNlDumpMsg); 457 } catch (SocketException | InterruptedIOException | ErrnoException e) { 458 Log.e(TAG, "Failed to dump multicast routes"); 459 return routes; 460 } 461 462 return routes; 463 } 464 closeSocketQuietly(final FileDescriptor fd)465 private static void closeSocketQuietly(final FileDescriptor fd) { 466 try { 467 SocketUtils.closeSocket(fd); 468 } catch (IOException e) { 469 // Nothing we can do here 470 } 471 } 472 } 473