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

Commit 24a67b66 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 6398601 from 4b99aae3 to sc-release

Change-Id: Ib44397e4c7cd91376ad7681eac0b46ecd7c2e6c0
parents 97104d15 4b99aae3
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ filegroup {
    srcs: [
        "src/android/net/ip/InterfaceController.java",
        "src/android/net/ip/IpNeighborMonitor.java",
        "src/android/net/ip/NetlinkMonitor.java",
        "src/android/net/netlink/*.java",
        "src/android/net/shared/NetdUtils.java",
        "src/android/net/shared/RouteUtils.java",
+7 −80
Original line number Diff line number Diff line
@@ -19,35 +19,20 @@ package android.net.ip;
import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
import static android.net.netlink.NetlinkConstants.hexify;
import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
import static android.system.OsConstants.AF_NETLINK;
import static android.system.OsConstants.NETLINK_ROUTE;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOCK_NONBLOCK;

import android.net.MacAddress;
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.util.PacketReader;
import android.net.util.SharedLog;
import android.net.util.SocketUtils;
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;

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


@@ -61,7 +46,7 @@ import java.util.StringJoiner;
 *
 * @hide
 */
public class IpNeighborMonitor extends PacketReader {
public class IpNeighborMonitor extends NetlinkMonitor {
    private static final String TAG = IpNeighborMonitor.class.getSimpleName();
    private static final boolean DBG = false;
    private static final boolean VDBG = false;
@@ -129,85 +114,27 @@ public class IpNeighborMonitor extends PacketReader {
        }
    }

    // TODO: move NetworkStackUtils.closeSocketQuietly to somewhere accessible to this file.
    private void closeSocketQuietly(FileDescriptor fd) {
        try {
            SocketUtils.closeSocket(fd);
        } catch (IOException ignored) {
        }
    }

    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);
        super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH);
        mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
    }

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

        try {
            fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
            Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
            NetlinkSocket.connectToKernel(fd);

            if (VDBG) {
                final SocketAddress nlAddr = Os.getsockname(fd);
                Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
            }
        } catch (ErrnoException|SocketException e) {
            logError("Failed to create rtnetlink socket", e);
            closeSocketQuietly(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: " + hexify(byteBuffer));
                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);
        }
    public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) {
        if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
            mLog.e("non-rtnetlink neighbor msg: " + nlMsg);
            return;
        }

    private void evaluateRtNetlinkNeighborMessage(
            RtNetlinkNeighborMessage neighMsg, long whenMs) {
        final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg;
        final short msgType = neighMsg.getHeader().nlmsg_type;
        final StructNdMsg ndMsg = neighMsg.getNdHeader();
        if (ndMsg == null) {
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 static android.net.netlink.NetlinkConstants.hexify;
import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
import static android.system.OsConstants.AF_NETLINK;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOCK_NONBLOCK;

import android.annotation.NonNull;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.NetlinkMessage;
import android.net.netlink.NetlinkSocket;
import android.net.util.PacketReader;
import android.net.util.SharedLog;
import android.net.util.SocketUtils;
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.IOException;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * A simple base class to listen for netlink broadcasts.
 *
 * Opens a netlink socket of the given family and binds to the specified groups. Polls the socket
 * from the event loop of the passed-in {@link Handler}, and calls the subclass-defined
 * {@link #processNetlinkMessage} method on the handler thread for each netlink message that
 * arrives. Currently ignores all netlink errors.
 */
public class NetlinkMonitor extends PacketReader {
    protected final SharedLog mLog;
    protected final String mTag;
    private final int mFamily;
    private final int mBindGroups;

    private static final boolean DBG = false;

    /**
     * Constructs a new {@code NetlinkMonitor} instance.
     *
     * @param h The Handler on which to poll for messages and on which to call
     *          {@link #processNetlinkMessage}.
     * @param log A SharedLog to log to.
     * @param tag The log tag to use for log messages.
     * @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}.
     * @param bindGroups the netlink groups to bind to.
     */
    public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag,
            int family, int bindGroups) {
        super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
        mLog = log.forSubComponent(tag);
        mTag = tag;
        mFamily = family;
        mBindGroups = bindGroups;
    }

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

        try {
            fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily);
            Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups));
            NetlinkSocket.connectToKernel(fd);

            if (DBG) {
                final SocketAddress nlAddr = Os.getsockname(fd);
                Log.d(mTag, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
            }
        } catch (ErrnoException | SocketException e) {
            logError("Failed to create rtnetlink socket", e);
            closeSocketQuietly(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());

        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: " + hexify(byteBuffer));
                break;
            }

            if (nlMsg instanceof NetlinkErrorMessage) {
                mLog.e("netlink error: " + nlMsg);
                continue;
            }

            processNetlinkMessage(nlMsg, whenMs);
        }
    }

    // TODO: move NetworkStackUtils to frameworks/libs/net for NetworkStackUtils#closeSocketQuietly.
    private void closeSocketQuietly(FileDescriptor fd) {
        try {
            SocketUtils.closeSocket(fd);
        } catch (IOException ignored) {
        }
    }

    /**
     * Processes one netlink message. Must be overridden by subclasses.
     * @param nlMsg the message to process.
     * @param whenMs the timestamp, as measured by {@link SystemClock#elapsedRealtime}, when the
     *               message was received.
     */
    protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { }
}
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.netlink;

import java.nio.ByteBuffer;

/**
 * Base class for IPv6 neighbour discovery options.
 */
public class NdOption {
    public static final int STRUCT_SIZE = 2;

    /** The option type. */
    public final byte type;
    /** The length of the option in 8-byte units. Actually an unsigned 8-bit integer */
    public final int length;

    /** Constructs a new NdOption. */
    public NdOption(byte type, int length) {
        this.type = type;
        this.length = length;
    }

    /**
     * Parses a neighbour discovery option.
     *
     * Parses (and consumes) the option if it is of a known type. If the option is of an unknown
     * type, advances the buffer (so the caller can continue parsing if desired) and returns
     * {@link #UNKNOWN}. If the option claims a length of 0, returns null because parsing cannot
     * continue.
     *
     * No checks are performed on the length other than ensuring it is not 0, so if a caller wants
     * to deal with options that might overflow the structure that contains them, it must explicitly
     * set the buffer's limit to the position at which that structure ends.
     *
     * @param buf the buffer to parse.
     * @return a subclass of {@link NdOption}, or {@code null} for an unknown or malformed option.
     */
    public static NdOption parse(ByteBuffer buf) {
        if (buf == null || buf.remaining() < STRUCT_SIZE) return null;

        // Peek the type without advancing the buffer.
        byte type = buf.get(buf.position());
        int length = Byte.toUnsignedInt(buf.get(buf.position() + 1));
        if (length == 0) return null;

        switch (type) {
            case StructNdOptPref64.TYPE:
                return StructNdOptPref64.parse(buf);

            default:
                int newPosition = Math.min(buf.limit(), buf.position() + length * 8);
                buf.position(newPosition);
                return UNKNOWN;
        }
    }

    @Override
    public String toString() {
        return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length);
    }

    public static final NdOption UNKNOWN = new NdOption((byte) 0, 0);
}
+137 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.netlink;

import static android.system.OsConstants.AF_INET6;

import androidx.annotation.NonNull;

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

/**
 * A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages.
 */
public class NduseroptMessage extends NetlinkMessage {
    public static final int STRUCT_SIZE = 16;

    static final int NDUSEROPT_SRCADDR = 1;

    /** The address family. Presumably always AF_INET6. */
    public final byte family;
    /**
     * The total length in bytes of the options that follow this structure.
     * Actually a 16-bit unsigned integer.
     */
    public final int opts_len;
    /** The interface index on which the options were received. */
    public final int ifindex;
    /** The ICMP type of the packet that contained the options. */
    public final byte icmp_type;
    /** The ICMP code of the packet that contained the options. */
    public final byte icmp_code;

    /**
     * ND option that was in this message.
     * Even though the length field is called "opts_len", the kernel only ever sends one option per
     * message. It is unlikely that this will ever change as it would break existing userspace code.
     * But if it does, we can simply update this code, since userspace is typically newer than the
     * kernel.
     */
    public final NdOption option;

    /** The IP address that sent the packet containing the option. */
    public final InetAddress srcaddr;

    NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf)
            throws UnknownHostException {
        super(header);

        // The structure itself.
        buf.order(ByteOrder.nativeOrder());
        family = buf.get();
        buf.get();  // Skip 1 byte of padding.
        opts_len = Short.toUnsignedInt(buf.getShort());
        ifindex = buf.getInt();
        icmp_type = buf.get();
        icmp_code = buf.get();
        buf.order(ByteOrder.BIG_ENDIAN);
        buf.position(buf.position() + 6);  // Skip 6 bytes of padding.

        // The ND option.
        // Ensure we don't read past opts_len even if the option length is invalid.
        // Note that this check is not really necessary since if the option length is not valid,
        // this struct won't be very useful to the caller.
        int oldLimit = buf.limit();
        buf.limit(STRUCT_SIZE + opts_len);
        try {
            option = NdOption.parse(buf);
        } finally {
            buf.limit(oldLimit);
        }

        // The source address.
        int newPosition = STRUCT_SIZE + opts_len;
        if (newPosition >= buf.limit()) {
            throw new IllegalArgumentException("ND options extend past end of buffer");
        }
        buf.position(newPosition);

        StructNlAttr nla = StructNlAttr.parse(buf);
        if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) {
            throw new IllegalArgumentException("Invalid source address in ND useropt");
        }
        if (family == AF_INET6) {
            // InetAddress.getByAddress only looks at the ifindex if the address type needs one.
            srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex);
        } else {
            srcaddr = InetAddress.getByAddress(nla.nla_value);
        }
    }

    /**
     * Parses a StructNduseroptmsg from a {@link ByteBuffer}.
     *
     * @param header the netlink message header.
     * @param buf The buffer from which to parse the option. The buffer's byte order must be
     *            {@link java.nio.ByteOrder#BIG_ENDIAN}.
     * @return the parsed option, or {@code null} if the option could not be parsed successfully
     *         (for example, if it was truncated, or if the prefix length code was wrong).
     */
    public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) {
        if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
        try {
            return new NduseroptMessage(header, buf);
        } catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) {
            // Not great, but better than throwing an exception that might crash the caller.
            // Convention in this package is that null indicates that the option was truncated, so
            // callers must already handle it.
            return null;
        }
    }

    @Override
    public String toString() {
        return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)",
                family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type),
                Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress());
    }
}
Loading