• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.net.ip;
18 
19 import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
20 import static android.net.netlink.NetlinkConstants.hexify;
21 import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
22 import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
23 import static android.system.OsConstants.AF_NETLINK;
24 import static android.system.OsConstants.NETLINK_ROUTE;
25 import static android.system.OsConstants.SOCK_DGRAM;
26 import static android.system.OsConstants.SOCK_NONBLOCK;
27 
28 import android.net.MacAddress;
29 import android.net.netlink.NetlinkErrorMessage;
30 import android.net.netlink.NetlinkMessage;
31 import android.net.netlink.NetlinkSocket;
32 import android.net.netlink.RtNetlinkNeighborMessage;
33 import android.net.netlink.StructNdMsg;
34 import android.net.util.NetworkStackUtils;
35 import android.net.util.PacketReader;
36 import android.net.util.SharedLog;
37 import android.os.Handler;
38 import android.os.SystemClock;
39 import android.system.ErrnoException;
40 import android.system.Os;
41 import android.system.OsConstants;
42 import android.util.Log;
43 
44 import java.io.FileDescriptor;
45 import java.net.InetAddress;
46 import java.net.SocketAddress;
47 import java.net.SocketException;
48 import java.nio.ByteBuffer;
49 import java.nio.ByteOrder;
50 import java.util.StringJoiner;
51 
52 
53 /**
54  * IpNeighborMonitor.
55  *
56  * Monitors the kernel rtnetlink neighbor notifications and presents to callers
57  * NeighborEvents describing each event. Callers can provide a consumer instance
58  * to both filter (e.g. by interface index and IP address) and handle the
59  * generated NeighborEvents.
60  *
61  * @hide
62  */
63 public class IpNeighborMonitor extends PacketReader {
64     private static final String TAG = IpNeighborMonitor.class.getSimpleName();
65     private static final boolean DBG = false;
66     private static final boolean VDBG = false;
67 
68     /**
69      * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
70      * for the given IP address on the specified interface index.
71      *
72      * @return 0 if the request was successfully passed to the kernel; otherwise return
73      *         a non-zero error code.
74      */
startKernelNeighborProbe(int ifIndex, InetAddress ip)75     public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
76         final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
77         if (DBG) { Log.d(TAG, msgSnippet); }
78 
79         final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
80                 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
81 
82         try {
83             NetlinkSocket.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
84         } catch (ErrnoException e) {
85             Log.e(TAG, "Error " + msgSnippet + ": " + e);
86             return -e.errno;
87         }
88 
89         return 0;
90     }
91 
92     public static class NeighborEvent {
93         final long elapsedMs;
94         final short msgType;
95         final int ifindex;
96         final InetAddress ip;
97         final short nudState;
98         final MacAddress macAddr;
99 
NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, short nudState, MacAddress macAddr)100         public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
101                 short nudState, MacAddress macAddr) {
102             this.elapsedMs = elapsedMs;
103             this.msgType = msgType;
104             this.ifindex = ifindex;
105             this.ip = ip;
106             this.nudState = nudState;
107             this.macAddr = macAddr;
108         }
109 
isConnected()110         boolean isConnected() {
111             return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
112         }
113 
isValid()114         boolean isValid() {
115             return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
116         }
117 
118         @Override
toString()119         public String toString() {
120             final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
121             return j.add("@" + elapsedMs)
122                     .add(stringForNlMsgType(msgType))
123                     .add("if=" + ifindex)
124                     .add(ip.getHostAddress())
125                     .add(StructNdMsg.stringForNudState(nudState))
126                     .add("[" + macAddr + "]")
127                     .toString();
128         }
129     }
130 
131     public interface NeighborEventConsumer {
132         // Every neighbor event received on the netlink socket is passed in
133         // here. Subclasses should filter for events of interest.
accept(NeighborEvent event)134         public void accept(NeighborEvent event);
135     }
136 
137     private final SharedLog mLog;
138     private final NeighborEventConsumer mConsumer;
139 
IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb)140     public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
141         super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
142         mLog = log.forSubComponent(TAG);
143         mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
144     }
145 
146     @Override
createFd()147     protected FileDescriptor createFd() {
148         FileDescriptor fd = null;
149 
150         try {
151             fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
152             Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
153             NetlinkSocket.connectToKernel(fd);
154 
155             if (VDBG) {
156                 final SocketAddress nlAddr = Os.getsockname(fd);
157                 Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
158             }
159         } catch (ErrnoException|SocketException e) {
160             logError("Failed to create rtnetlink socket", e);
161             NetworkStackUtils.closeSocketQuietly(fd);
162             return null;
163         }
164 
165         return fd;
166     }
167 
168     @Override
handlePacket(byte[] recvbuf, int length)169     protected void handlePacket(byte[] recvbuf, int length) {
170         final long whenMs = SystemClock.elapsedRealtime();
171 
172         final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
173         byteBuffer.order(ByteOrder.nativeOrder());
174 
175         parseNetlinkMessageBuffer(byteBuffer, whenMs);
176     }
177 
parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs)178     private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
179         while (byteBuffer.remaining() > 0) {
180             final int position = byteBuffer.position();
181             final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
182             if (nlMsg == null || nlMsg.getHeader() == null) {
183                 byteBuffer.position(position);
184                 mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
185                 break;
186             }
187 
188             final int srcPortId = nlMsg.getHeader().nlmsg_pid;
189             if (srcPortId !=  0) {
190                 mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId));
191                 break;
192             }
193 
194             if (nlMsg instanceof NetlinkErrorMessage) {
195                 mLog.e("netlink error: " + nlMsg);
196                 continue;
197             } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
198                 mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
199                 continue;
200             }
201 
202             evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
203         }
204     }
205 
evaluateRtNetlinkNeighborMessage( RtNetlinkNeighborMessage neighMsg, long whenMs)206     private void evaluateRtNetlinkNeighborMessage(
207             RtNetlinkNeighborMessage neighMsg, long whenMs) {
208         final short msgType = neighMsg.getHeader().nlmsg_type;
209         final StructNdMsg ndMsg = neighMsg.getNdHeader();
210         if (ndMsg == null) {
211             mLog.e("RtNetlinkNeighborMessage without ND message header!");
212             return;
213         }
214 
215         final int ifindex = ndMsg.ndm_ifindex;
216         final InetAddress destination = neighMsg.getDestination();
217         final short nudState =
218                 (msgType == RTM_DELNEIGH)
219                 ? StructNdMsg.NUD_NONE
220                 : ndMsg.ndm_state;
221 
222         final NeighborEvent event = new NeighborEvent(
223                 whenMs, msgType, ifindex, destination, nudState,
224                 getMacAddress(neighMsg.getLinkLayerAddress()));
225 
226         if (VDBG) {
227             Log.d(TAG, neighMsg.toString());
228         }
229         if (DBG) {
230             Log.d(TAG, event.toString());
231         }
232 
233         mConsumer.accept(event);
234     }
235 
getMacAddress(byte[] linkLayerAddress)236     private static MacAddress getMacAddress(byte[] linkLayerAddress) {
237         if (linkLayerAddress != null) {
238             try {
239                 return MacAddress.fromBytes(linkLayerAddress);
240             } catch (IllegalArgumentException e) {
241                 Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
242             }
243         }
244 
245         return null;
246     }
247 }
248