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

Commit 9ce5d602 authored by Erik Kline's avatar Erik Kline
Browse files

Add IpReachabilityMonitor#probeAll() to begin doing DNAv4/v6-like probing

Note that this change is not sufficient to force probing in all cases,
but does cause probing to occur on Linux if the target node hasn't been
confirmed as reachable in the past 5 seconds (delay_first_probe_time).

Bug: 18581716
Bug: 19866451
Bug: 20944464
Change-Id: I29393897118311b48c966c41e2cddb7a784f136f
parent 63b007e6
Loading
Loading
Loading
Loading
+47 −7
Original line number Diff line number Diff line
@@ -75,7 +75,6 @@ public class IpReachabilityMonitor {
    private boolean mRunning;
    final private Thread mObserverThread;

    // TODO: consider passing in a NetworkInterface object from the caller.
    public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
        mInterfaceName = ifName;
        int ifIndex = -1;
@@ -154,6 +153,12 @@ public class IpReachabilityMonitor {
        }
    }

    private boolean stillRunning() {
        synchronized (mLock) {
            return mRunning;
        }
    }

    public void updateLinkProperties(LinkProperties lp) {
        if (!mInterfaceName.equals(lp.getInterfaceName())) {
            // TODO: figure out how to cope with interface changes.
@@ -204,6 +209,47 @@ public class IpReachabilityMonitor {
        }
    }

    public void probeAll() {
        Set<InetAddress> ipProbeList = new HashSet<InetAddress>();
        synchronized (mLock) {
            ipProbeList.addAll(mIpWatchList);
        }
        for (InetAddress target : ipProbeList) {
            if (!stillRunning()) { break; }
            probeIp(target);
        }
    }

    private void probeIp(InetAddress ip) {
        // This currently does not cause neighbor probing if the target |ip|
        // has been confirmed reachable within the past "delay_probe_time"
        // seconds, i.e. within the past 5 seconds.
        //
        // TODO: replace with a transition directly to NUD_PROBE state once
        // kernels are updated to do so correctly.
        if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); }

        final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
                1, ip, StructNdMsg.NUD_DELAY, mInterfaceIndex, null);
        NetlinkSocket nlSocket = null;

        try {
            nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
            nlSocket.connectToKernel();
            nlSocket.sendMessage(msg, 0, msg.length, 300);
            final NetlinkMessage response = NetlinkMessage.parse(nlSocket.recvMessage(300));
            if (response != null && response instanceof NetlinkErrorMessage) {
                Log.e(TAG, "Error probing ip=" + response.toString());
            }
        } catch (ErrnoException | InterruptedIOException | SocketException e) {
            Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e);
        }

        if (nlSocket != null) {
            nlSocket.close();
        }
    }


    private final class NetlinkSocketObserver implements Runnable {
        private static final String TAG = "NetlinkSocketObserver";
@@ -242,12 +288,6 @@ public class IpReachabilityMonitor {
            if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
        }

        private boolean stillRunning() {
            synchronized (mLock) {
                return mRunning;
            }
        }

        private void clearNetlinkSocket() {
            if (mSocket != null) {
                mSocket.close();
+65 −0
Original line number Diff line number Diff line
@@ -21,9 +21,11 @@ import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlAttr;
import android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
import android.system.OsConstants;
import android.util.Log;

import java.net.InetAddress;
import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@@ -131,6 +133,34 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
        return bytes;
    }

    /**
     * A convenience method to create an RTM_NEWNEIGH message, to modify
     * the kernel's state information for a specific neighbor.
     */
    public static byte[] newNewNeighborMessage(
            int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
        final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
        nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
        nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_REPLACE;
        nlmsghdr.nlmsg_seq = seqNo;

        final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
        msg.mNdmsg = new StructNdMsg();
        msg.mNdmsg.ndm_family =
                (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
        msg.mNdmsg.ndm_ifindex = ifIndex;
        msg.mNdmsg.ndm_state = nudState;
        msg.mDestination = ip;
        msg.mLinkLayerAddr = llAddr;  // might be null

        final byte[] bytes = new byte[msg.getRequiredSpace()];
        nlmsghdr.nlmsg_len = bytes.length;
        final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        byteBuffer.order(ByteOrder.nativeOrder());
        msg.pack(byteBuffer);
        return bytes;
    }

    private StructNdMsg mNdmsg;
    private InetAddress mDestination;
    private byte[] mLinkLayerAddr;
@@ -166,6 +196,41 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
        return mCacheInfo;
    }

    public int getRequiredSpace() {
        int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
        if (mDestination != null) {
            spaceRequired += NetlinkConstants.alignedLengthOf(
                    StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
        }
        if (mLinkLayerAddr != null) {
            spaceRequired += NetlinkConstants.alignedLengthOf(
                    StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
        }
        // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
        // attributes appended.  Fix later, if necessary.
        return spaceRequired;
    }

    private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
        final StructNlAttr nlAttr = new StructNlAttr();
        nlAttr.nla_type = nlType;
        nlAttr.nla_value = nlValue;
        nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
        nlAttr.pack(byteBuffer);
    }

    public void pack(ByteBuffer byteBuffer) {
        getHeader().pack(byteBuffer) ;
        mNdmsg.pack(byteBuffer);

        if (mDestination != null) {
            packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
        }
        if (mLinkLayerAddr != null) {
            packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
        }
    }

    @Override
    public String toString() {
        final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
+1 −4
Original line number Diff line number Diff line
@@ -123,9 +123,7 @@ public class StructNdMsg {
        ndm_family = (byte) OsConstants.AF_UNSPEC;
    }

    public boolean pack(ByteBuffer byteBuffer) {
        if (!hasAvailableSpace(byteBuffer)) { return false; }

    public void pack(ByteBuffer byteBuffer) {
        // The ByteOrder must have already been set by the caller.  In most
        // cases ByteOrder.nativeOrder() is correct, with the exception
        // of usage within unittests.
@@ -136,7 +134,6 @@ public class StructNdMsg {
        byteBuffer.putShort(ndm_state);
        byteBuffer.put(ndm_flags);
        byteBuffer.put(ndm_type);
        return true;
    }

    public boolean nudConnected() {
+8 −0
Original line number Diff line number Diff line
@@ -116,6 +116,14 @@ public class StructNlAttr {
        }
    }

    public void pack(ByteBuffer byteBuffer) {
        final int originalPosition = byteBuffer.position();
        byteBuffer.putShort(nla_len);
        byteBuffer.putShort(nla_type);
        byteBuffer.put(nla_value);
        byteBuffer.position(originalPosition + getAlignedLength());
    }

    @Override
    public String toString() {
        return "StructNlAttr{ "
+1 −4
Original line number Diff line number Diff line
@@ -57,9 +57,7 @@ public class StructNlMsgErr {
        msg = null;
    }

    public boolean pack(ByteBuffer byteBuffer) {
        if (!hasAvailableSpace(byteBuffer)) { return false; }

    public void pack(ByteBuffer byteBuffer) {
        // The ByteOrder must have already been set by the caller.  In most
        // cases ByteOrder.nativeOrder() is correct, with the possible
        // exception of usage within unittests.
@@ -67,7 +65,6 @@ public class StructNlMsgErr {
        if (msg != null) {
            msg.pack(byteBuffer);
        }
        return true;
    }

    @Override
Loading