Home | History | Annotate | Download | only in netlink
      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