Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b501e7e2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Refactor to IpNeighborMonitor and single-threaded semantics"

parents efdca004 84714bff
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ package android.net.ip;
import static android.system.OsConstants.*;

import android.net.NetworkUtils;
import android.net.util.BlockingSocketReader;
import android.net.util.PacketReader;
import android.net.util.ConnectivityPacketSummary;
import android.os.Handler;
import android.system.ErrnoException;
@@ -65,7 +65,7 @@ public class ConnectivityPacketTracker {

    private final String mTag;
    private final LocalLog mLog;
    private final BlockingSocketReader mPacketListener;
    private final PacketReader mPacketListener;
    private boolean mRunning;
    private String mDisplayName;

@@ -101,7 +101,7 @@ public class ConnectivityPacketTracker {
        mDisplayName = null;
    }

    private final class PacketListener extends BlockingSocketReader {
    private final class PacketListener extends PacketReader {
        private final int mIfIndex;
        private final byte mHwAddr[];

+10 −0
Original line number Diff line number Diff line
@@ -815,6 +815,15 @@ public class IpClient extends StateMachine {
        pw.println(Objects.toString(provisioningConfig, "N/A"));
        pw.decreaseIndent();

        final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
        if (iprm != null) {
            pw.println();
            pw.println(mTag + " current IpReachabilityMonitor state:");
            pw.increaseIndent();
            iprm.dump(pw);
            pw.decreaseIndent();
        }

        pw.println();
        pw.println(mTag + " StateMachine dump:");
        pw.increaseIndent();
@@ -1237,6 +1246,7 @@ public class IpClient extends StateMachine {
            mIpReachabilityMonitor = new IpReachabilityMonitor(
                    mContext,
                    mInterfaceName,
                    getHandler(),
                    mLog,
                    new IpReachabilityMonitor.Callback() {
                        @Override
+236 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.ip;

import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
import android.net.netlink.NetlinkSocket;
import android.net.netlink.RtNetlinkNeighborMessage;
import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlMsgHdr;
import android.net.util.PacketReader;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.NetlinkSocketAddress;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;

import com.android.internal.util.BitUtils;

import libcore.io.IoUtils;
import libcore.io.Libcore;

import java.io.FileDescriptor;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.StringJoiner;


/**
 * IpNeighborMonitor.
 *
 * Monitors the kernel rtnetlink neighbor notifications and presents to callers
 * NeighborEvents describing each event. Callers can provide a consumer instance
 * to both filter (e.g. by interface index and IP address) and handle the
 * generated NeighborEvents.
 *
 * @hide
 */
public class IpNeighborMonitor extends PacketReader {
    private static final String TAG = IpNeighborMonitor.class.getSimpleName();
    private static final boolean DBG = false;
    private static final boolean VDBG = false;

    /**
     * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
     * for the given IP address on the specified interface index.
     *
     * @return 0 if the request was successfully passed to the kernel; otherwise return
     *         a non-zero error code.
     */
    public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
        final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
        if (DBG) { Log.d(TAG, msgSnippet); }

        final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
                1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);

        try {
            NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
        } catch (ErrnoException e) {
            Log.e(TAG, "Error " + msgSnippet + ": " + e);
            return -e.errno;
        }

        return 0;
    }

    public static class NeighborEvent {
        final long elapsedMs;
        final short msgType;
        final int ifindex;
        final InetAddress ip;
        final short nudState;
        final byte[] linkLayerAddr;

        public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
                short nudState, byte[] linkLayerAddr) {
            this.elapsedMs = elapsedMs;
            this.msgType = msgType;
            this.ifindex = ifindex;
            this.ip = ip;
            this.nudState = nudState;
            this.linkLayerAddr = linkLayerAddr;
        }

        boolean isConnected() {
            return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
                   StructNdMsg.isNudStateConnected(nudState);
        }

        boolean isValid() {
            return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
                   StructNdMsg.isNudStateValid(nudState);
        }

        @Override
        public String toString() {
            final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
            return j.add("@" + elapsedMs)
                    .add(NetlinkConstants.stringForNlMsgType(msgType))
                    .add("if=" + ifindex)
                    .add(ip.getHostAddress())
                    .add(StructNdMsg.stringForNudState(nudState))
                    .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]")
                    .toString();
        }
    }

    public interface NeighborEventConsumer {
        // Every neighbor event received on the netlink socket is passed in
        // here. Subclasses should filter for events of interest.
        public void accept(NeighborEvent event);
    }

    private final SharedLog mLog;
    private final NeighborEventConsumer mConsumer;

    public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
        super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
        mLog = log.forSubComponent(TAG);
        mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
    }

    @Override
    protected FileDescriptor createFd() {
        FileDescriptor fd = null;

        try {
            fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
            Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)));
            Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0)));

            if (VDBG) {
                final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd);
                Log.d(TAG, "bound to sockaddr_nl{"
                        + BitUtils.uint32(nlAddr.getPortId()) + ", "
                        + nlAddr.getGroupsMask()
                        + "}");
            }
        } catch (ErrnoException|SocketException e) {
            logError("Failed to create rtnetlink socket", e);
            IoUtils.closeQuietly(fd);
            return null;
        }

        return fd;
    }

    @Override
    protected void handlePacket(byte[] recvbuf, int length) {
        final long whenMs = SystemClock.elapsedRealtime();

        final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
        byteBuffer.order(ByteOrder.nativeOrder());

        parseNetlinkMessageBuffer(byteBuffer, whenMs);
    }

    private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
        while (byteBuffer.remaining() > 0) {
            final int position = byteBuffer.position();
            final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
            if (nlMsg == null || nlMsg.getHeader() == null) {
                byteBuffer.position(position);
                mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
                break;
            }

            final int srcPortId = nlMsg.getHeader().nlmsg_pid;
            if (srcPortId !=  0) {
                mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId));
                break;
            }

            if (nlMsg instanceof NetlinkErrorMessage) {
                mLog.e("netlink error: " + nlMsg);
                continue;
            } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
                mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
                continue;
            }

            evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
        }
    }

    private void evaluateRtNetlinkNeighborMessage(
            RtNetlinkNeighborMessage neighMsg, long whenMs) {
        final short msgType = neighMsg.getHeader().nlmsg_type;
        final StructNdMsg ndMsg = neighMsg.getNdHeader();
        if (ndMsg == null) {
            mLog.e("RtNetlinkNeighborMessage without ND message header!");
            return;
        }

        final int ifindex = ndMsg.ndm_ifindex;
        final InetAddress destination = neighMsg.getDestination();
        final short nudState =
                (msgType == NetlinkConstants.RTM_DELNEIGH)
                ? StructNdMsg.NUD_NONE
                : ndMsg.ndm_state;

        final NeighborEvent event = new NeighborEvent(
                whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress());

        if (VDBG) {
            Log.d(TAG, neighMsg.toString());
        }
        if (DBG) {
            Log.d(TAG, event.toString());
        }

        mConsumer.accept(event);
    }
}
+97 −289

File changed.

Preview size limit exceeded, changes collapsed.

+40 −94
Original line number Diff line number Diff line
@@ -16,16 +16,24 @@

package android.net.netlink;

import static android.system.OsConstants.AF_NETLINK;
import static android.system.OsConstants.EIO;
import static android.system.OsConstants.EPROTO;
import static android.system.OsConstants.ETIMEDOUT;
import static android.system.OsConstants.SO_RCVBUF;
import static android.system.OsConstants.SO_RCVTIMEO;
import static android.system.OsConstants.SO_SNDTIMEO;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOL_SOCKET;

import android.system.ErrnoException;
import android.system.NetlinkSocketAddress;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructTimeval;
import android.util.Log;
import libcore.io.IoUtils;
import libcore.io.Libcore;

import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
import java.net.SocketAddress;
@@ -37,28 +45,27 @@ import java.nio.ByteOrder;
/**
 * NetlinkSocket
 *
 * A small wrapper class to assist with AF_NETLINK socket operations.
 * A small static class to assist with AF_NETLINK socket operations.
 *
 * @hide
 */
public class NetlinkSocket implements Closeable {
public class NetlinkSocket {
    private static final String TAG = "NetlinkSocket";
    private static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
    private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;

    final private FileDescriptor mDescriptor;
    private NetlinkSocketAddress mAddr;
    private long mLastRecvTimeoutMs;
    private long mLastSendTimeoutMs;
    public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
    public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;

    public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
        final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";

        try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) {
        final long IO_TIMEOUT = 300L;
            nlSocket.connectToKernel();
            nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
            final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);

        FileDescriptor fd;

        try {
            fd = forProto(nlProto);
            connectToKernel(fd);
            sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT);
            final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
            // recvMessage() guaranteed to not return null if it did not throw.
            final NetlinkMessage response = NetlinkMessage.parse(bytes);
            if (response != null && response instanceof NetlinkErrorMessage &&
@@ -81,61 +88,30 @@ public class NetlinkSocket implements Closeable {
                    errmsg = response.toString();
                }
                Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
                throw new ErrnoException(errmsg, OsConstants.EPROTO);
                throw new ErrnoException(errmsg, EPROTO);
            }
        } catch (InterruptedIOException e) {
            Log.e(TAG, errPrefix, e);
            throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e);
            throw new ErrnoException(errPrefix, ETIMEDOUT, e);
        } catch (SocketException e) {
            Log.e(TAG, errPrefix, e);
            throw new ErrnoException(errPrefix, OsConstants.EIO, e);
        }
    }

    public NetlinkSocket(int nlProto) throws ErrnoException {
        mDescriptor = Os.socket(
                OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);

        Os.setsockoptInt(
                mDescriptor, OsConstants.SOL_SOCKET,
                OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE);
            throw new ErrnoException(errPrefix, EIO, e);
        }

    public NetlinkSocketAddress getLocalAddress() throws ErrnoException {
        return (NetlinkSocketAddress) Os.getsockname(mDescriptor);
        IoUtils.closeQuietly(fd);
    }

    public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException {
        Os.bind(mDescriptor, (SocketAddress)localAddr);
    }

    public void connectTo(NetlinkSocketAddress peerAddr)
            throws ErrnoException, SocketException {
        Os.connect(mDescriptor, (SocketAddress) peerAddr);
    }

    public void connectToKernel() throws ErrnoException, SocketException {
        connectTo(new NetlinkSocketAddress(0, 0));
    }

    /**
     * Wait indefinitely (or until underlying socket error) for a
     * netlink message of at most DEFAULT_RECV_BUFSIZE size.
     */
    public ByteBuffer recvMessage()
            throws ErrnoException, InterruptedIOException {
        return recvMessage(DEFAULT_RECV_BUFSIZE, 0);
    public static FileDescriptor forProto(int nlProto) throws ErrnoException {
        final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
        Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
        return fd;
    }

    /**
     * Wait up to |timeoutMs| (or until underlying socket error) for a
     * netlink message of at most DEFAULT_RECV_BUFSIZE size.
     */
    public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException {
        return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs);
    public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
        Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0)));
    }

    private void checkTimeout(long timeoutMs) {
    private static void checkTimeout(long timeoutMs) {
        if (timeoutMs < 0) {
            throw new IllegalArgumentException("Negative timeouts not permitted");
        }
@@ -147,21 +123,14 @@ public class NetlinkSocket implements Closeable {
     *
     * Multi-threaded calls with different timeouts will cause unexpected results.
     */
    public ByteBuffer recvMessage(int bufsize, long timeoutMs)
    public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)
            throws ErrnoException, IllegalArgumentException, InterruptedIOException {
        checkTimeout(timeoutMs);

        synchronized (mDescriptor) {
            if (mLastRecvTimeoutMs != timeoutMs) {
                Os.setsockoptTimeval(mDescriptor,
                        OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
                        StructTimeval.fromMillis(timeoutMs));
                mLastRecvTimeoutMs = timeoutMs;
            }
        }
        Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));

        ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
        int length = Os.read(mDescriptor, byteBuffer);
        int length = Os.read(fd, byteBuffer);
        if (length == bufsize) {
            Log.w(TAG, "maximum read");
        }
@@ -171,40 +140,17 @@ public class NetlinkSocket implements Closeable {
        return byteBuffer;
    }

    /**
     * Send a message to a peer to which this socket has previously connected.
     *
     * This blocks until completion or an error occurs.
     */
    public boolean sendMessage(byte[] bytes, int offset, int count)
            throws ErrnoException, InterruptedIOException {
        return sendMessage(bytes, offset, count, 0);
    }

    /**
     * Send a message to a peer to which this socket has previously connected,
     * waiting at most |timeoutMs| milliseconds for the send to complete.
     *
     * Multi-threaded calls with different timeouts will cause unexpected results.
     */
    public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs)
    public static int sendMessage(
            FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
            throws ErrnoException, IllegalArgumentException, InterruptedIOException {
        checkTimeout(timeoutMs);

        synchronized (mDescriptor) {
            if (mLastSendTimeoutMs != timeoutMs) {
                Os.setsockoptTimeval(mDescriptor,
                        OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
                        StructTimeval.fromMillis(timeoutMs));
                mLastSendTimeoutMs = timeoutMs;
            }
        }

        return (count == Os.write(mDescriptor, bytes, offset, count));
    }

    @Override
    public void close() {
        IoUtils.closeQuietly(mDescriptor);
        Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
        return Os.write(fd, bytes, offset, count);
    }
}
Loading