• 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.system.OsConstants.NETLINK_ROUTE;
23 
24 import android.net.MacAddress;
25 import android.net.netlink.NetlinkMessage;
26 import android.net.netlink.NetlinkSocket;
27 import android.net.netlink.RtNetlinkNeighborMessage;
28 import android.net.netlink.StructNdMsg;
29 import android.net.util.SharedLog;
30 import android.os.Handler;
31 import android.system.ErrnoException;
32 import android.system.OsConstants;
33 import android.util.Log;
34 
35 import java.net.InetAddress;
36 import java.util.StringJoiner;
37 
38 
39 /**
40  * IpNeighborMonitor.
41  *
42  * Monitors the kernel rtnetlink neighbor notifications and presents to callers
43  * NeighborEvents describing each event. Callers can provide a consumer instance
44  * to both filter (e.g. by interface index and IP address) and handle the
45  * generated NeighborEvents.
46  *
47  * @hide
48  */
49 public class IpNeighborMonitor extends NetlinkMonitor {
50     private static final String TAG = IpNeighborMonitor.class.getSimpleName();
51     private static final boolean DBG = false;
52     private static final boolean VDBG = false;
53 
54     /**
55      * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
56      * for the given IP address on the specified interface index.
57      *
58      * @return 0 if the request was successfully passed to the kernel; otherwise return
59      *         a non-zero error code.
60      */
startKernelNeighborProbe(int ifIndex, InetAddress ip)61     public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
62         final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
63         if (DBG) { Log.d(TAG, msgSnippet); }
64 
65         final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
66                 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
67 
68         try {
69             NetlinkSocket.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
70         } catch (ErrnoException e) {
71             Log.e(TAG, "Error " + msgSnippet + ": " + e);
72             return -e.errno;
73         }
74 
75         return 0;
76     }
77 
78     public static class NeighborEvent {
79         final long elapsedMs;
80         final short msgType;
81         final int ifindex;
82         final InetAddress ip;
83         final short nudState;
84         final MacAddress macAddr;
85 
NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, short nudState, MacAddress macAddr)86         public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
87                 short nudState, MacAddress macAddr) {
88             this.elapsedMs = elapsedMs;
89             this.msgType = msgType;
90             this.ifindex = ifindex;
91             this.ip = ip;
92             this.nudState = nudState;
93             this.macAddr = macAddr;
94         }
95 
isConnected()96         boolean isConnected() {
97             return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
98         }
99 
isValid()100         boolean isValid() {
101             return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
102         }
103 
104         @Override
toString()105         public String toString() {
106             final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
107             return j.add("@" + elapsedMs)
108                     .add(stringForNlMsgType(msgType, NETLINK_ROUTE))
109                     .add("if=" + ifindex)
110                     .add(ip.getHostAddress())
111                     .add(StructNdMsg.stringForNudState(nudState))
112                     .add("[" + macAddr + "]")
113                     .toString();
114         }
115     }
116 
117     public interface NeighborEventConsumer {
118         // Every neighbor event received on the netlink socket is passed in
119         // here. Subclasses should filter for events of interest.
accept(NeighborEvent event)120         public void accept(NeighborEvent event);
121     }
122 
123     private final NeighborEventConsumer mConsumer;
124 
IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb)125     public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
126         super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH);
127         mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
128     }
129 
130     @Override
processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs)131     public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) {
132         if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
133             mLog.e("non-rtnetlink neighbor msg: " + nlMsg);
134             return;
135         }
136 
137         final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg;
138         final short msgType = neighMsg.getHeader().nlmsg_type;
139         final StructNdMsg ndMsg = neighMsg.getNdHeader();
140         if (ndMsg == null) {
141             mLog.e("RtNetlinkNeighborMessage without ND message header!");
142             return;
143         }
144 
145         final int ifindex = ndMsg.ndm_ifindex;
146         final InetAddress destination = neighMsg.getDestination();
147         final short nudState =
148                 (msgType == RTM_DELNEIGH)
149                 ? StructNdMsg.NUD_NONE
150                 : ndMsg.ndm_state;
151 
152         final NeighborEvent event = new NeighborEvent(
153                 whenMs, msgType, ifindex, destination, nudState,
154                 getMacAddress(neighMsg.getLinkLayerAddress()));
155 
156         if (VDBG) {
157             Log.d(TAG, neighMsg.toString());
158         }
159         if (DBG) {
160             Log.d(TAG, event.toString());
161         }
162 
163         mConsumer.accept(event);
164     }
165 
getMacAddress(byte[] linkLayerAddress)166     private static MacAddress getMacAddress(byte[] linkLayerAddress) {
167         if (linkLayerAddress != null) {
168             try {
169                 return MacAddress.fromBytes(linkLayerAddress);
170             } catch (IllegalArgumentException e) {
171                 Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
172             }
173         }
174 
175         return null;
176     }
177 }
178