184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline/*
284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * Copyright (C) 2017 The Android Open Source Project
384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline *
484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * Licensed under the Apache License, Version 2.0 (the "License");
584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * you may not use this file except in compliance with the License.
684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * You may obtain a copy of the License at
784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline *
884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline *      http://www.apache.org/licenses/LICENSE-2.0
984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline *
1084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * Unless required by applicable law or agreed to in writing, software
1184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * distributed under the License is distributed on an "AS IS" BASIS,
1284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * See the License for the specific language governing permissions and
1484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * limitations under the License.
1584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline */
1684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
1784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klinepackage android.net.ip;
1884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
198bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Klineimport static android.net.netlink.NetlinkConstants.hexify;
208bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Klineimport static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
218bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Klineimport static android.net.netlink.NetlinkConstants.stringForNlMsgType;
228bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline
238bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Klineimport android.net.MacAddress;
2484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.netlink.NetlinkErrorMessage;
2584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.netlink.NetlinkMessage;
2684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.netlink.NetlinkSocket;
2784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.netlink.RtNetlinkNeighborMessage;
2884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.netlink.StructNdMsg;
2984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.netlink.StructNlMsgHdr;
3084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.util.PacketReader;
3184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.net.util.SharedLog;
3284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.os.Handler;
3384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.os.SystemClock;
3484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.system.ErrnoException;
3584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.system.NetlinkSocketAddress;
3684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.system.Os;
3784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.system.OsConstants;
3884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport android.util.Log;
3984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
4084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport com.android.internal.util.BitUtils;
4184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
4284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport libcore.io.IoUtils;
4384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport libcore.io.Libcore;
4484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
4584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.io.FileDescriptor;
4684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.net.InetAddress;
4784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.net.SocketAddress;
4884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.net.SocketException;
4984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.nio.ByteBuffer;
5084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.nio.ByteOrder;
5184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klineimport java.util.StringJoiner;
5284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
5384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
5484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline/**
5584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * IpNeighborMonitor.
5684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline *
5784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * Monitors the kernel rtnetlink neighbor notifications and presents to callers
5884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * NeighborEvents describing each event. Callers can provide a consumer instance
5984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * to both filter (e.g. by interface index and IP address) and handle the
6084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * generated NeighborEvents.
6184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline *
6284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline * @hide
6384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline */
6484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Klinepublic class IpNeighborMonitor extends PacketReader {
6584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private static final String TAG = IpNeighborMonitor.class.getSimpleName();
6684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private static final boolean DBG = false;
6784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private static final boolean VDBG = false;
6884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
6984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    /**
7084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline     * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
7184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline     * for the given IP address on the specified interface index.
7284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline     *
7384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline     * @return 0 if the request was successfully passed to the kernel; otherwise return
7484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline     *         a non-zero error code.
7584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline     */
7684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
7784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
7884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        if (DBG) { Log.d(TAG, msgSnippet); }
7984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
8084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
8184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
8284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
8384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        try {
8484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
8584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        } catch (ErrnoException e) {
8684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            Log.e(TAG, "Error " + msgSnippet + ": " + e);
8784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            return -e.errno;
8884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
8984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
9084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        return 0;
9184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
9284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
9384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    public static class NeighborEvent {
9484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final long elapsedMs;
9584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final short msgType;
9684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final int ifindex;
9784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final InetAddress ip;
9884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final short nudState;
998bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline        final MacAddress macAddr;
10084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
10184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
1028bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                short nudState, MacAddress macAddr) {
10384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            this.elapsedMs = elapsedMs;
10484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            this.msgType = msgType;
10584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            this.ifindex = ifindex;
10684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            this.ip = ip;
10784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            this.nudState = nudState;
1088bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline            this.macAddr = macAddr;
10984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
11084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
11184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        boolean isConnected() {
1128bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline            return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
11384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
11484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
11584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        boolean isValid() {
1168bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline            return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
11784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
11884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
11984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        @Override
12084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        public String toString() {
12184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
12284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            return j.add("@" + elapsedMs)
1238bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                    .add(stringForNlMsgType(msgType))
12484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                    .add("if=" + ifindex)
12584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                    .add(ip.getHostAddress())
12684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                    .add(StructNdMsg.stringForNudState(nudState))
1278bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                    .add("[" + macAddr + "]")
12884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                    .toString();
12984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
13084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
13184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
13284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    public interface NeighborEventConsumer {
13384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        // Every neighbor event received on the netlink socket is passed in
13484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        // here. Subclasses should filter for events of interest.
13584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        public void accept(NeighborEvent event);
13684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
13784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
13884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private final SharedLog mLog;
13984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private final NeighborEventConsumer mConsumer;
14084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
14184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
14284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
14384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        mLog = log.forSubComponent(TAG);
14484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
14584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
14684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
14784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    @Override
14884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    protected FileDescriptor createFd() {
14984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        FileDescriptor fd = null;
15084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
15184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        try {
15284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
15384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)));
15484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0)));
15584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
15684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            if (VDBG) {
15784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd);
15884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                Log.d(TAG, "bound to sockaddr_nl{"
15984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                        + BitUtils.uint32(nlAddr.getPortId()) + ", "
16084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                        + nlAddr.getGroupsMask()
16184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                        + "}");
16284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            }
16384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        } catch (ErrnoException|SocketException e) {
16484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            logError("Failed to create rtnetlink socket", e);
16584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            IoUtils.closeQuietly(fd);
16684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            return null;
16784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
16884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
16984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        return fd;
17084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
17184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
17284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    @Override
17384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    protected void handlePacket(byte[] recvbuf, int length) {
17484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final long whenMs = SystemClock.elapsedRealtime();
17584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
17684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
17784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        byteBuffer.order(ByteOrder.nativeOrder());
17884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
17984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        parseNetlinkMessageBuffer(byteBuffer, whenMs);
18084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
18184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
18284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
18384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        while (byteBuffer.remaining() > 0) {
18484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            final int position = byteBuffer.position();
18584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
18684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            if (nlMsg == null || nlMsg.getHeader() == null) {
18784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                byteBuffer.position(position);
1888bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
18984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                break;
19084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            }
19184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
19284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            final int srcPortId = nlMsg.getHeader().nlmsg_pid;
19384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            if (srcPortId !=  0) {
19484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId));
19584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                break;
19684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            }
19784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
19884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            if (nlMsg instanceof NetlinkErrorMessage) {
19984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                mLog.e("netlink error: " + nlMsg);
20084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                continue;
20184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
20284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
20384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                continue;
20484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            }
20584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
20684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
20784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
20884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
20984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
21084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    private void evaluateRtNetlinkNeighborMessage(
21184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            RtNetlinkNeighborMessage neighMsg, long whenMs) {
21284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final short msgType = neighMsg.getHeader().nlmsg_type;
21384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final StructNdMsg ndMsg = neighMsg.getNdHeader();
21484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        if (ndMsg == null) {
21584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            mLog.e("RtNetlinkNeighborMessage without ND message header!");
21684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            return;
21784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
21884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
21984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final int ifindex = ndMsg.ndm_ifindex;
22084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final InetAddress destination = neighMsg.getDestination();
22184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final short nudState =
2228bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                (msgType == RTM_DELNEIGH)
22384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                ? StructNdMsg.NUD_NONE
22484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline                : ndMsg.ndm_state;
22584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
22684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        final NeighborEvent event = new NeighborEvent(
2278bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                whenMs, msgType, ifindex, destination, nudState,
2288bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                getMacAddress(neighMsg.getLinkLayerAddress()));
22984714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
23084714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        if (VDBG) {
23184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            Log.d(TAG, neighMsg.toString());
23284714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
23384714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        if (DBG) {
23484714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline            Log.d(TAG, event.toString());
23584714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        }
23684714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline
23784714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline        mConsumer.accept(event);
23884714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline    }
2398bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline
2408bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline    private static MacAddress getMacAddress(byte[] linkLayerAddress) {
2418bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline        if (linkLayerAddress != null) {
2428bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline            try {
2438bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                return MacAddress.fromBytes(linkLayerAddress);
2448bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline            } catch (IllegalArgumentException e) {
2458bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline                Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
2468bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline            }
2478bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline        }
2488bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline
2498bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline        return null;
2508bd00d5f7ced34a45173e45537dc3100d62ee6a5Erik Kline    }
25184714bffa1a58fe1f6a114ae015f8e38be46f32dErik Kline}
252