1 /* 2 * Copyright (C) 2015 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.system.OsConstants.AF_NETLINK; 20 import static android.system.OsConstants.EIO; 21 import static android.system.OsConstants.EPROTO; 22 import static android.system.OsConstants.ETIMEDOUT; 23 import static android.system.OsConstants.SO_RCVBUF; 24 import static android.system.OsConstants.SO_RCVTIMEO; 25 import static android.system.OsConstants.SO_SNDTIMEO; 26 import static android.system.OsConstants.SOCK_DGRAM; 27 import static android.system.OsConstants.SOL_SOCKET; 28 29 import android.system.ErrnoException; 30 import android.system.NetlinkSocketAddress; 31 import android.system.Os; 32 import android.system.StructTimeval; 33 import android.util.Log; 34 import libcore.io.IoUtils; 35 import libcore.io.Libcore; 36 37 import java.io.FileDescriptor; 38 import java.io.InterruptedIOException; 39 import java.net.SocketAddress; 40 import java.net.SocketException; 41 import java.nio.ByteBuffer; 42 import java.nio.ByteOrder; 43 44 45 /** 46 * NetlinkSocket 47 * 48 * A small static class to assist with AF_NETLINK socket operations. 49 * 50 * @hide 51 */ 52 public class NetlinkSocket { 53 private static final String TAG = "NetlinkSocket"; 54 55 public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; 56 public static final int SOCKET_RECV_BUFSIZE = 64 * 1024; 57 58 public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException { 59 final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage"; 60 final long IO_TIMEOUT = 300L; 61 62 FileDescriptor fd; 63 64 try { 65 fd = forProto(nlProto); 66 connectToKernel(fd); 67 sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT); 68 final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT); 69 // recvMessage() guaranteed to not return null if it did not throw. 70 final NetlinkMessage response = NetlinkMessage.parse(bytes); 71 if (response != null && response instanceof NetlinkErrorMessage && 72 (((NetlinkErrorMessage) response).getNlMsgError() != null)) { 73 final int errno = ((NetlinkErrorMessage) response).getNlMsgError().error; 74 if (errno != 0) { 75 // TODO: consider ignoring EINVAL (-22), which appears to be 76 // normal when probing a neighbor for which the kernel does 77 // not already have / no longer has a link layer address. 78 Log.e(TAG, errPrefix + ", errmsg=" + response.toString()); 79 // Note: convert kernel errnos (negative) into userspace errnos (positive). 80 throw new ErrnoException(response.toString(), Math.abs(errno)); 81 } 82 } else { 83 final String errmsg; 84 if (response == null) { 85 bytes.position(0); 86 errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); 87 } else { 88 errmsg = response.toString(); 89 } 90 Log.e(TAG, errPrefix + ", errmsg=" + errmsg); 91 throw new ErrnoException(errmsg, EPROTO); 92 } 93 } catch (InterruptedIOException e) { 94 Log.e(TAG, errPrefix, e); 95 throw new ErrnoException(errPrefix, ETIMEDOUT, e); 96 } catch (SocketException e) { 97 Log.e(TAG, errPrefix, e); 98 throw new ErrnoException(errPrefix, EIO, e); 99 } 100 101 IoUtils.closeQuietly(fd); 102 } 103 104 public static FileDescriptor forProto(int nlProto) throws ErrnoException { 105 final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto); 106 Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE); 107 return fd; 108 } 109 110 public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException { 111 Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0))); 112 } 113 114 private static void checkTimeout(long timeoutMs) { 115 if (timeoutMs < 0) { 116 throw new IllegalArgumentException("Negative timeouts not permitted"); 117 } 118 } 119 120 /** 121 * Wait up to |timeoutMs| (or until underlying socket error) for a 122 * netlink message of at most |bufsize| size. 123 * 124 * Multi-threaded calls with different timeouts will cause unexpected results. 125 */ 126 public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs) 127 throws ErrnoException, IllegalArgumentException, InterruptedIOException { 128 checkTimeout(timeoutMs); 129 130 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs)); 131 132 ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); 133 int length = Os.read(fd, byteBuffer); 134 if (length == bufsize) { 135 Log.w(TAG, "maximum read"); 136 } 137 byteBuffer.position(0); 138 byteBuffer.limit(length); 139 byteBuffer.order(ByteOrder.nativeOrder()); 140 return byteBuffer; 141 } 142 143 /** 144 * Send a message to a peer to which this socket has previously connected, 145 * waiting at most |timeoutMs| milliseconds for the send to complete. 146 * 147 * Multi-threaded calls with different timeouts will cause unexpected results. 148 */ 149 public static int sendMessage( 150 FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs) 151 throws ErrnoException, IllegalArgumentException, InterruptedIOException { 152 checkTimeout(timeoutMs); 153 Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs)); 154 return Os.write(fd, bytes, offset, count); 155 } 156 } 157