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