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 17package android.net.netlink; 18 19import android.system.ErrnoException; 20import android.system.NetlinkSocketAddress; 21import android.system.Os; 22import android.system.OsConstants; 23import android.system.StructTimeval; 24import android.util.Log; 25import libcore.io.IoUtils; 26import libcore.io.Libcore; 27 28import java.io.Closeable; 29import java.io.FileDescriptor; 30import java.io.InterruptedIOException; 31import java.net.SocketAddress; 32import java.net.SocketException; 33import java.nio.ByteBuffer; 34import java.nio.ByteOrder; 35 36 37/** 38 * NetlinkSocket 39 * 40 * A small wrapper class to assist with AF_NETLINK socket operations. 41 * 42 * @hide 43 */ 44public class NetlinkSocket implements Closeable { 45 private static final String TAG = "NetlinkSocket"; 46 private static final int SOCKET_RECV_BUFSIZE = 64 * 1024; 47 private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; 48 49 final private FileDescriptor mDescriptor; 50 private NetlinkSocketAddress mAddr; 51 private long mLastRecvTimeoutMs; 52 private long mLastSendTimeoutMs; 53 54 public NetlinkSocket(int nlProto) throws ErrnoException { 55 mDescriptor = Os.socket( 56 OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); 57 58 Libcore.os.setsockoptInt( 59 mDescriptor, OsConstants.SOL_SOCKET, 60 OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE); 61 } 62 63 public NetlinkSocketAddress getLocalAddress() throws ErrnoException { 64 return (NetlinkSocketAddress) Os.getsockname(mDescriptor); 65 } 66 67 public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException { 68 Os.bind(mDescriptor, (SocketAddress)localAddr); 69 } 70 71 public void connectTo(NetlinkSocketAddress peerAddr) 72 throws ErrnoException, SocketException { 73 Os.connect(mDescriptor, (SocketAddress) peerAddr); 74 } 75 76 public void connectToKernel() throws ErrnoException, SocketException { 77 connectTo(new NetlinkSocketAddress(0, 0)); 78 } 79 80 /** 81 * Wait indefinitely (or until underlying socket error) for a 82 * netlink message of at most DEFAULT_RECV_BUFSIZE size. 83 */ 84 public ByteBuffer recvMessage() 85 throws ErrnoException, InterruptedIOException { 86 return recvMessage(DEFAULT_RECV_BUFSIZE, 0); 87 } 88 89 /** 90 * Wait up to |timeoutMs| (or until underlying socket error) for a 91 * netlink message of at most DEFAULT_RECV_BUFSIZE size. 92 */ 93 public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException { 94 return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs); 95 } 96 97 private void checkTimeout(long timeoutMs) { 98 if (timeoutMs < 0) { 99 throw new IllegalArgumentException("Negative timeouts not permitted"); 100 } 101 } 102 103 /** 104 * Wait up to |timeoutMs| (or until underlying socket error) for a 105 * netlink message of at most |bufsize| size. 106 * 107 * Multi-threaded calls with different timeouts will cause unexpected results. 108 */ 109 public ByteBuffer recvMessage(int bufsize, long timeoutMs) 110 throws ErrnoException, IllegalArgumentException, InterruptedIOException { 111 checkTimeout(timeoutMs); 112 113 synchronized (mDescriptor) { 114 if (mLastRecvTimeoutMs != timeoutMs) { 115 Os.setsockoptTimeval(mDescriptor, 116 OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, 117 StructTimeval.fromMillis(timeoutMs)); 118 mLastRecvTimeoutMs = timeoutMs; 119 } 120 } 121 122 ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); 123 int length = Os.read(mDescriptor, byteBuffer); 124 if (length == bufsize) { 125 Log.w(TAG, "maximum read"); 126 } 127 byteBuffer.position(0); 128 byteBuffer.limit(length); 129 byteBuffer.order(ByteOrder.nativeOrder()); 130 return byteBuffer; 131 } 132 133 /** 134 * Send a message to a peer to which this socket has previously connected. 135 * 136 * This blocks until completion or an error occurs. 137 */ 138 public boolean sendMessage(byte[] bytes, int offset, int count) 139 throws ErrnoException, InterruptedIOException { 140 return sendMessage(bytes, offset, count, 0); 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 boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs) 150 throws ErrnoException, IllegalArgumentException, InterruptedIOException { 151 checkTimeout(timeoutMs); 152 153 synchronized (mDescriptor) { 154 if (mLastSendTimeoutMs != timeoutMs) { 155 Os.setsockoptTimeval(mDescriptor, 156 OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, 157 StructTimeval.fromMillis(timeoutMs)); 158 mLastSendTimeoutMs = timeoutMs; 159 } 160 } 161 162 return (count == Os.write(mDescriptor, bytes, offset, count)); 163 } 164 165 @Override 166 public void close() { 167 IoUtils.closeQuietly(mDescriptor); 168 } 169} 170