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

Commit 5a5745b3 authored by Erik Kline's avatar Erik Kline Committed by Android (Google) Code Review
Browse files

Merge "Add basic netlink library code." into mnc-dev

parents de803d3a 6193aa33
Loading
Loading
Loading
Loading
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.system.OsConstants;
import com.android.internal.util.HexDump;

import java.nio.ByteBuffer;


/**
 * Various constants and static helper methods for netlink communications.
 *
 * Values taken from:
 *
 *     <linux_src>/include/uapi/linux/netlink.h
 *     <linux_src>/include/uapi/linux/rtnetlink.h
 *
 * @hide
 */
public class NetlinkConstants {
    private NetlinkConstants() {}

    public static final int NLA_ALIGNTO = 4;

    public static final int alignedLengthOf(short length) {
        final int intLength = (int) length & 0xffff;
        return alignedLengthOf(intLength);
    }

    public static final int alignedLengthOf(int length) {
        if (length <= 0) { return 0; }
        return (((length + NLA_ALIGNTO - 1) / NLA_ALIGNTO) * NLA_ALIGNTO);
    }

    public static String stringForAddressFamily(int family) {
        if (family == OsConstants.AF_INET) { return "AF_INET"; }
        if (family == OsConstants.AF_INET6) { return "AF_INET6"; }
        if (family == OsConstants.AF_NETLINK) { return "AF_NETLINK"; }
        return String.valueOf(family);
    }

    public static String hexify(byte[] bytes) {
        if (bytes == null) { return "(null)"; }
        return HexDump.toHexString(bytes);
    }

    public static String hexify(ByteBuffer buffer) {
        if (buffer == null) { return "(null)"; }
        return HexDump.toHexString(
                buffer.array(), buffer.position(), buffer.remaining());
    }

    // Known values for struct nlmsghdr nlm_type.
    public static final short NLMSG_NOOP         = 1;   // Nothing
    public static final short NLMSG_ERROR        = 2;   // Error
    public static final short NLMSG_DONE         = 3;   // End of a dump
    public static final short NLMSG_OVERRUN      = 4;   // Data lost
    public static final short NLMSG_MAX_RESERVED = 15;  // Max reserved value

    public static final short RTM_NEWLINK        = 16;
    public static final short RTM_DELLINK        = 17;
    public static final short RTM_GETLINK        = 18;
    public static final short RTM_SETLINK        = 19;
    public static final short RTM_NEWADDR        = 20;
    public static final short RTM_DELADDR        = 21;
    public static final short RTM_GETADDR        = 22;
    public static final short RTM_NEWROUTE       = 24;
    public static final short RTM_DELROUTE       = 25;
    public static final short RTM_GETROUTE       = 26;
    public static final short RTM_NEWNEIGH       = 28;
    public static final short RTM_DELNEIGH       = 29;
    public static final short RTM_GETNEIGH       = 30;
    public static final short RTM_NEWRULE        = 32;
    public static final short RTM_DELRULE        = 33;
    public static final short RTM_GETRULE        = 34;
    public static final short RTM_NEWNDUSEROPT   = 68;

    public static String stringForNlMsgType(short nlm_type) {
        switch (nlm_type) {
            case NLMSG_NOOP: return "NLMSG_NOOP";
            case NLMSG_ERROR: return "NLMSG_ERROR";
            case NLMSG_DONE: return "NLMSG_DONE";
            case NLMSG_OVERRUN: return "NLMSG_OVERRUN";
            case RTM_NEWLINK: return "RTM_NEWLINK";
            case RTM_DELLINK: return "RTM_DELLINK";
            case RTM_GETLINK: return "RTM_GETLINK";
            case RTM_SETLINK: return "RTM_SETLINK";
            case RTM_NEWADDR: return "RTM_NEWADDR";
            case RTM_DELADDR: return "RTM_DELADDR";
            case RTM_GETADDR: return "RTM_GETADDR";
            case RTM_NEWROUTE: return "RTM_NEWROUTE";
            case RTM_DELROUTE: return "RTM_DELROUTE";
            case RTM_GETROUTE: return "RTM_GETROUTE";
            case RTM_NEWNEIGH: return "RTM_NEWNEIGH";
            case RTM_DELNEIGH: return "RTM_DELNEIGH";
            case RTM_GETNEIGH: return "RTM_GETNEIGH";
            case RTM_NEWRULE: return "RTM_NEWRULE";
            case RTM_DELRULE: return "RTM_DELRULE";
            case RTM_GETRULE: return "RTM_GETRULE";
            case RTM_NEWNDUSEROPT: return "RTM_NEWNDUSEROPT";
            default:
                return "unknown RTM type: " + String.valueOf(nlm_type);
        }
    }
}
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
import android.util.Log;

import java.nio.ByteBuffer;


/**
 * A NetlinkMessage subclass for netlink error messages.
 *
 * @hide
 */
public class NetlinkErrorMessage extends NetlinkMessage {

    public static NetlinkErrorMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
        final NetlinkErrorMessage errorMsg = new NetlinkErrorMessage(header);

        errorMsg.mNlMsgErr = StructNlMsgErr.parse(byteBuffer);
        if (errorMsg.mNlMsgErr == null) {
            return null;
        }

        return errorMsg;
    }

    private StructNlMsgErr mNlMsgErr;

    NetlinkErrorMessage(StructNlMsgHdr header) {
        super(header);
        mNlMsgErr = null;
    }

    public StructNlMsgErr getNlMsgError() {
        return mNlMsgErr;
    }

    @Override
    public String toString() {
        return "NetlinkErrorMessage{ "
                + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
                + "nlmsgerr{" + (mNlMsgErr == null ? "" : mNlMsgErr.toString()) + "} "
                + "}";
    }
}
+97 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkErrorMessage;
import android.net.netlink.RtNetlinkNeighborMessage;
import android.net.netlink.StructNlAttr;
import android.net.netlink.StructNlMsgHdr;
import android.util.Log;

import java.nio.ByteBuffer;


/**
 * NetlinkMessage base class for other, more specific netlink message types.
 *
 * Classes that extend NetlinkMessage should:
 *     - implement a public static parse(StructNlMsgHdr, ByteBuffer) method
 *     - returning either null (parse errors) or a new object of the subclass
 *       type (cast-able to NetlinkMessage)
 *
 * NetlinkMessage.parse() should be updated to know which nlmsg_type values
 * correspond with which message subclasses.
 *
 * @hide
 */
public class NetlinkMessage {
    private final static String TAG = "NetlinkMessage";

    public static NetlinkMessage parse(ByteBuffer byteBuffer) {
        final int startPosition = (byteBuffer != null) ? byteBuffer.position() : -1;
        final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(byteBuffer);
        if (nlmsghdr == null) {
            return null;
        }

        int payloadLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len);
        payloadLength -= StructNlMsgHdr.STRUCT_SIZE;
        if (payloadLength < 0 || payloadLength > byteBuffer.remaining()) {
            // Malformed message or runt buffer.  Pretend the buffer was consumed.
            byteBuffer.position(byteBuffer.limit());
            return null;
        }

        switch (nlmsghdr.nlmsg_type) {
            //case NetlinkConstants.NLMSG_NOOP:
            case NetlinkConstants.NLMSG_ERROR:
                return (NetlinkMessage) NetlinkErrorMessage.parse(byteBuffer);
            case NetlinkConstants.NLMSG_DONE:
                byteBuffer.position(byteBuffer.position() + payloadLength);
                return new NetlinkMessage(nlmsghdr);
            //case NetlinkConstants.NLMSG_OVERRUN:
            case NetlinkConstants.RTM_NEWNEIGH:
            case NetlinkConstants.RTM_DELNEIGH:
            case NetlinkConstants.RTM_GETNEIGH:
                return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
            default:
                if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
                    // Netlink control message.  Just parse the header for now,
                    // pretending the whole message was consumed.
                    byteBuffer.position(byteBuffer.position() + payloadLength);
                    return new NetlinkMessage(nlmsghdr);
                }
                return null;
        }
    }

    protected StructNlMsgHdr mHeader;

    public NetlinkMessage(StructNlMsgHdr nlmsghdr) {
        mHeader = nlmsghdr;
    }

    public StructNlMsgHdr getHeader() {
        return mHeader;
    }

    @Override
    public String toString() {
        return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}";
    }
}
+169 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 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;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;


/**
 * NetlinkSocket
 *
 * A small wrapper class to assist with AF_NETLINK socket operations.
 *
 * @hide
 */
public class NetlinkSocket implements Closeable {
    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 NetlinkSocket(int nlProto) throws ErrnoException {
        mDescriptor = Os.socket(
                OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);

        Libcore.os.setsockoptInt(
                mDescriptor, OsConstants.SOL_SOCKET,
                OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE);
    }

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

    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);
    }

    /**
     * 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);
    }

    private void checkTimeout(long timeoutMs) {
        if (timeoutMs < 0) {
            throw new IllegalArgumentException("Negative timeouts not permitted");
        }
    }

    /**
     * Wait up to |timeoutMs| (or until underlying socket error) for a
     * netlink message of at most |bufsize| size.
     *
     * Multi-threaded calls with different timeouts will cause unexpected results.
     */
    public ByteBuffer recvMessage(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;
            }
        }

        ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
        int length = Os.read(mDescriptor, byteBuffer);
        if (length == bufsize) {
            Log.w(TAG, "maximum read");
        }
        byteBuffer.position(0);
        byteBuffer.limit(length);
        byteBuffer.order(ByteOrder.nativeOrder());
        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)
            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);
    }
}
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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 android.net.netlink.StructNdaCacheInfo;
import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlAttr;
import android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
import android.util.Log;

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


/**
 * A NetlinkMessage subclass for netlink error messages.
 *
 * see also: &lt;linux_src&gt;/include/uapi/linux/neighbour.h
 *
 * @hide
 */
public class RtNetlinkNeighborMessage extends NetlinkMessage {
    public static final short NDA_UNSPEC    = 0;
    public static final short NDA_DST       = 1;
    public static final short NDA_LLADDR    = 2;
    public static final short NDA_CACHEINFO = 3;
    public static final short NDA_PROBES    = 4;
    public static final short NDA_VLAN      = 5;
    public static final short NDA_PORT      = 6;
    public static final short NDA_VNI       = 7;
    public static final short NDA_IFINDEX   = 8;
    public static final short NDA_MASTER    = 9;

    private static StructNlAttr findNextAttrOfType(short attrType, ByteBuffer byteBuffer) {
        while (byteBuffer != null && byteBuffer.remaining() > 0) {
            final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer);
            if (nlAttr == null) {
                break;
            }
            if (nlAttr.nla_type == attrType) {
                return StructNlAttr.parse(byteBuffer);
            }
            if (byteBuffer.remaining() < nlAttr.getAlignedLength()) {
                break;
            }
            byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength());
        }
        return null;
    }

    public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
        final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header);

        neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer);
        if (neighMsg.mNdmsg == null) {
            return null;
        }

        // Some of these are message-type dependent, and not always present.
        final int baseOffset = byteBuffer.position();
        StructNlAttr nlAttr = findNextAttrOfType(NDA_DST, byteBuffer);
        if (nlAttr != null) {
            neighMsg.mDestination = nlAttr.getValueAsInetAddress();
        }

        byteBuffer.position(baseOffset);
        nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer);
        if (nlAttr != null) {
            neighMsg.mLinkLayerAddr = nlAttr.nla_value;
        }

        byteBuffer.position(baseOffset);
        nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer);
        if (nlAttr != null) {
            neighMsg.mNumProbes = nlAttr.getValueAsInt(0);
        }

        byteBuffer.position(baseOffset);
        nlAttr = findNextAttrOfType(NDA_CACHEINFO, byteBuffer);
        if (nlAttr != null) {
            neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
        }

        final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
        final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
                neighMsg.mHeader.nlmsg_len - kMinConsumed);
        if (byteBuffer.remaining() < kAdditionalSpace) {
            byteBuffer.position(byteBuffer.limit());
        } else {
            byteBuffer.position(baseOffset + kAdditionalSpace);
        }

        return neighMsg;
    }

    /**
     * A convenience method to create an RTM_GETNEIGH request message.
     */
    public static byte[] newGetNeighborsRequest(int seqNo) {
        final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
        final byte[] bytes = new byte[length];
        final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
        byteBuffer.order(ByteOrder.nativeOrder());

        final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
        nlmsghdr.nlmsg_len = length;
        nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH;
        nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST|StructNlMsgHdr.NLM_F_DUMP;
        nlmsghdr.nlmsg_seq = seqNo;
        nlmsghdr.pack(byteBuffer);

        final StructNdMsg ndmsg = new StructNdMsg();
        ndmsg.pack(byteBuffer);

        return bytes;
    }

    private StructNdMsg mNdmsg;
    private InetAddress mDestination;
    private byte[] mLinkLayerAddr;
    private int mNumProbes;
    private StructNdaCacheInfo mCacheInfo;

    private RtNetlinkNeighborMessage(StructNlMsgHdr header) {
        super(header);
        mNdmsg = null;
        mDestination = null;
        mLinkLayerAddr = null;
        mNumProbes = 0;
        mCacheInfo = null;
    }

    public StructNdMsg getNdHeader() {
        return mNdmsg;
    }

    public InetAddress getDestination() {
        return mDestination;
    }

    public byte[] getLinkLayerAddress() {
        return mLinkLayerAddr;
    }

    public int getProbes() {
        return mNumProbes;
    }

    public StructNdaCacheInfo getCacheInfo() {
        return mCacheInfo;
    }

    @Override
    public String toString() {
        final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
        return "RtNetlinkNeighborMessage{ "
                + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
                + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, "
                + "destination{" + ipLiteral + "} "
                + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} "
                + "probes{" + mNumProbes + "} "
                + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} "
                + "}";
    }
}
Loading