blob: b29d61793a61aeaf10389c0db2ed0fa53a5008e0 [file] [log] [blame]
Erik Kline84714bf2017-05-19 09:29:48 +09001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.ip;
18
Erik Kline8bd00d52017-12-08 17:47:50 +090019import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
Remi NGUYEN VANe7e9f2b2019-01-18 19:05:29 +090020import static android.net.netlink.NetlinkConstants.hexify;
Erik Kline8bd00d52017-12-08 17:47:50 +090021import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +090022import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +090023import static android.system.OsConstants.AF_NETLINK;
24import static android.system.OsConstants.NETLINK_ROUTE;
25import static android.system.OsConstants.SOCK_DGRAM;
26import static android.system.OsConstants.SOCK_NONBLOCK;
Erik Kline8bd00d52017-12-08 17:47:50 +090027
28import android.net.MacAddress;
Erik Kline84714bf2017-05-19 09:29:48 +090029import android.net.netlink.NetlinkErrorMessage;
30import android.net.netlink.NetlinkMessage;
31import android.net.netlink.NetlinkSocket;
32import android.net.netlink.RtNetlinkNeighborMessage;
33import android.net.netlink.StructNdMsg;
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +090034import android.net.util.NetworkStackUtils;
Erik Kline84714bf2017-05-19 09:29:48 +090035import android.net.util.PacketReader;
36import android.net.util.SharedLog;
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +090037import android.net.util.SocketUtils;
Erik Kline84714bf2017-05-19 09:29:48 +090038import android.os.Handler;
39import android.os.SystemClock;
40import android.system.ErrnoException;
Erik Kline84714bf2017-05-19 09:29:48 +090041import android.system.Os;
42import android.system.OsConstants;
43import android.util.Log;
44
Erik Kline84714bf2017-05-19 09:29:48 +090045import java.io.FileDescriptor;
46import java.net.InetAddress;
47import java.net.SocketAddress;
48import java.net.SocketException;
49import java.nio.ByteBuffer;
50import java.nio.ByteOrder;
51import java.util.StringJoiner;
52
53
54/**
55 * IpNeighborMonitor.
56 *
57 * Monitors the kernel rtnetlink neighbor notifications and presents to callers
58 * NeighborEvents describing each event. Callers can provide a consumer instance
59 * to both filter (e.g. by interface index and IP address) and handle the
60 * generated NeighborEvents.
61 *
62 * @hide
63 */
64public class IpNeighborMonitor extends PacketReader {
65 private static final String TAG = IpNeighborMonitor.class.getSimpleName();
66 private static final boolean DBG = false;
67 private static final boolean VDBG = false;
68
69 /**
70 * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
71 * for the given IP address on the specified interface index.
72 *
73 * @return 0 if the request was successfully passed to the kernel; otherwise return
74 * a non-zero error code.
75 */
76 public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
77 final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
78 if (DBG) { Log.d(TAG, msgSnippet); }
79
80 final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
81 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
82
83 try {
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +090084 NetlinkSocket.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
Erik Kline84714bf2017-05-19 09:29:48 +090085 } catch (ErrnoException e) {
86 Log.e(TAG, "Error " + msgSnippet + ": " + e);
87 return -e.errno;
88 }
89
90 return 0;
91 }
92
93 public static class NeighborEvent {
94 final long elapsedMs;
95 final short msgType;
96 final int ifindex;
97 final InetAddress ip;
98 final short nudState;
Erik Kline8bd00d52017-12-08 17:47:50 +090099 final MacAddress macAddr;
Erik Kline84714bf2017-05-19 09:29:48 +0900100
101 public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
Erik Kline8bd00d52017-12-08 17:47:50 +0900102 short nudState, MacAddress macAddr) {
Erik Kline84714bf2017-05-19 09:29:48 +0900103 this.elapsedMs = elapsedMs;
104 this.msgType = msgType;
105 this.ifindex = ifindex;
106 this.ip = ip;
107 this.nudState = nudState;
Erik Kline8bd00d52017-12-08 17:47:50 +0900108 this.macAddr = macAddr;
Erik Kline84714bf2017-05-19 09:29:48 +0900109 }
110
111 boolean isConnected() {
Erik Kline8bd00d52017-12-08 17:47:50 +0900112 return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
Erik Kline84714bf2017-05-19 09:29:48 +0900113 }
114
115 boolean isValid() {
Erik Kline8bd00d52017-12-08 17:47:50 +0900116 return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
Erik Kline84714bf2017-05-19 09:29:48 +0900117 }
118
119 @Override
120 public String toString() {
121 final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
122 return j.add("@" + elapsedMs)
Erik Kline8bd00d52017-12-08 17:47:50 +0900123 .add(stringForNlMsgType(msgType))
Erik Kline84714bf2017-05-19 09:29:48 +0900124 .add("if=" + ifindex)
125 .add(ip.getHostAddress())
126 .add(StructNdMsg.stringForNudState(nudState))
Erik Kline8bd00d52017-12-08 17:47:50 +0900127 .add("[" + macAddr + "]")
Erik Kline84714bf2017-05-19 09:29:48 +0900128 .toString();
129 }
130 }
131
132 public interface NeighborEventConsumer {
133 // Every neighbor event received on the netlink socket is passed in
134 // here. Subclasses should filter for events of interest.
135 public void accept(NeighborEvent event);
136 }
137
138 private final SharedLog mLog;
139 private final NeighborEventConsumer mConsumer;
140
141 public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
142 super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
143 mLog = log.forSubComponent(TAG);
144 mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
145 }
146
147 @Override
148 protected FileDescriptor createFd() {
149 FileDescriptor fd = null;
150
151 try {
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900152 fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
153 SocketUtils.bindSocket(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +0900154 NetlinkSocket.connectToKernel(fd);
Erik Kline84714bf2017-05-19 09:29:48 +0900155
156 if (VDBG) {
Remi NGUYEN VAN811f6382019-01-20 12:08:21 +0900157 final SocketAddress nlAddr = Os.getsockname(fd);
158 Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
Erik Kline84714bf2017-05-19 09:29:48 +0900159 }
160 } catch (ErrnoException|SocketException e) {
161 logError("Failed to create rtnetlink socket", e);
Remi NGUYEN VAN231b52b2019-01-29 15:38:52 +0900162 NetworkStackUtils.closeSocketQuietly(fd);
Erik Kline84714bf2017-05-19 09:29:48 +0900163 return null;
164 }
165
166 return fd;
167 }
168
169 @Override
170 protected void handlePacket(byte[] recvbuf, int length) {
171 final long whenMs = SystemClock.elapsedRealtime();
172
173 final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
174 byteBuffer.order(ByteOrder.nativeOrder());
175
176 parseNetlinkMessageBuffer(byteBuffer, whenMs);
177 }
178
179 private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
180 while (byteBuffer.remaining() > 0) {
181 final int position = byteBuffer.position();
182 final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
183 if (nlMsg == null || nlMsg.getHeader() == null) {
184 byteBuffer.position(position);
Erik Kline8bd00d52017-12-08 17:47:50 +0900185 mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
Erik Kline84714bf2017-05-19 09:29:48 +0900186 break;
187 }
188
189 final int srcPortId = nlMsg.getHeader().nlmsg_pid;
190 if (srcPortId != 0) {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900191 mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId));
Erik Kline84714bf2017-05-19 09:29:48 +0900192 break;
193 }
194
195 if (nlMsg instanceof NetlinkErrorMessage) {
196 mLog.e("netlink error: " + nlMsg);
197 continue;
198 } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
199 mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
200 continue;
201 }
202
203 evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
204 }
205 }
206
207 private void evaluateRtNetlinkNeighborMessage(
208 RtNetlinkNeighborMessage neighMsg, long whenMs) {
209 final short msgType = neighMsg.getHeader().nlmsg_type;
210 final StructNdMsg ndMsg = neighMsg.getNdHeader();
211 if (ndMsg == null) {
212 mLog.e("RtNetlinkNeighborMessage without ND message header!");
213 return;
214 }
215
216 final int ifindex = ndMsg.ndm_ifindex;
217 final InetAddress destination = neighMsg.getDestination();
218 final short nudState =
Erik Kline8bd00d52017-12-08 17:47:50 +0900219 (msgType == RTM_DELNEIGH)
Erik Kline84714bf2017-05-19 09:29:48 +0900220 ? StructNdMsg.NUD_NONE
221 : ndMsg.ndm_state;
222
223 final NeighborEvent event = new NeighborEvent(
Erik Kline8bd00d52017-12-08 17:47:50 +0900224 whenMs, msgType, ifindex, destination, nudState,
225 getMacAddress(neighMsg.getLinkLayerAddress()));
Erik Kline84714bf2017-05-19 09:29:48 +0900226
227 if (VDBG) {
228 Log.d(TAG, neighMsg.toString());
229 }
230 if (DBG) {
231 Log.d(TAG, event.toString());
232 }
233
234 mConsumer.accept(event);
235 }
Erik Kline8bd00d52017-12-08 17:47:50 +0900236
237 private static MacAddress getMacAddress(byte[] linkLayerAddress) {
238 if (linkLayerAddress != null) {
239 try {
240 return MacAddress.fromBytes(linkLayerAddress);
241 } catch (IllegalArgumentException e) {
242 Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
243 }
244 }
245
246 return null;
247 }
Erik Kline84714bf2017-05-19 09:29:48 +0900248}