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.hexify; 20 import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; 21 import static android.net.netlink.NetlinkConstants.stringForNlMsgType; 22 23 import android.net.MacAddress; 24 import android.net.netlink.NetlinkErrorMessage; 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.netlink.StructNlMsgHdr; 30 import android.net.util.PacketReader; 31 import android.net.util.SharedLog; 32 import android.os.Handler; 33 import android.os.SystemClock; 34 import android.system.ErrnoException; 35 import android.system.NetlinkSocketAddress; 36 import android.system.Os; 37 import android.system.OsConstants; 38 import android.util.Log; 39 40 import com.android.internal.util.BitUtils; 41 42 import libcore.io.IoUtils; 43 import libcore.io.Libcore; 44 45 import java.io.FileDescriptor; 46 import java.net.InetAddress; 47 import java.net.SocketAddress; 48 import java.net.SocketException; 49 import java.nio.ByteBuffer; 50 import java.nio.ByteOrder; 51 import 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 */ 64 public 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 { 84 NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg); 85 } 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; 99 final MacAddress macAddr; 100 101 public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, 102 short nudState, MacAddress macAddr) { 103 this.elapsedMs = elapsedMs; 104 this.msgType = msgType; 105 this.ifindex = ifindex; 106 this.ip = ip; 107 this.nudState = nudState; 108 this.macAddr = macAddr; 109 } 110 111 boolean isConnected() { 112 return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState); 113 } 114 115 boolean isValid() { 116 return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState); 117 } 118 119 @Override 120 public String toString() { 121 final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}"); 122 return j.add("@" + elapsedMs) 123 .add(stringForNlMsgType(msgType)) 124 .add("if=" + ifindex) 125 .add(ip.getHostAddress()) 126 .add(StructNdMsg.stringForNudState(nudState)) 127 .add("[" + macAddr + "]") 128 .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 { 152 fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE); 153 Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH))); 154 Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0))); 155 156 if (VDBG) { 157 final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd); 158 Log.d(TAG, "bound to sockaddr_nl{" 159 + BitUtils.uint32(nlAddr.getPortId()) + ", " 160 + nlAddr.getGroupsMask() 161 + "}"); 162 } 163 } catch (ErrnoException|SocketException e) { 164 logError("Failed to create rtnetlink socket", e); 165 IoUtils.closeQuietly(fd); 166 return null; 167 } 168 169 return fd; 170 } 171 172 @Override 173 protected void handlePacket(byte[] recvbuf, int length) { 174 final long whenMs = SystemClock.elapsedRealtime(); 175 176 final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); 177 byteBuffer.order(ByteOrder.nativeOrder()); 178 179 parseNetlinkMessageBuffer(byteBuffer, whenMs); 180 } 181 182 private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { 183 while (byteBuffer.remaining() > 0) { 184 final int position = byteBuffer.position(); 185 final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); 186 if (nlMsg == null || nlMsg.getHeader() == null) { 187 byteBuffer.position(position); 188 mLog.e("unparsable netlink msg: " + hexify(byteBuffer)); 189 break; 190 } 191 192 final int srcPortId = nlMsg.getHeader().nlmsg_pid; 193 if (srcPortId != 0) { 194 mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId)); 195 break; 196 } 197 198 if (nlMsg instanceof NetlinkErrorMessage) { 199 mLog.e("netlink error: " + nlMsg); 200 continue; 201 } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { 202 mLog.i("non-rtnetlink neighbor msg: " + nlMsg); 203 continue; 204 } 205 206 evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); 207 } 208 } 209 210 private void evaluateRtNetlinkNeighborMessage( 211 RtNetlinkNeighborMessage neighMsg, long whenMs) { 212 final short msgType = neighMsg.getHeader().nlmsg_type; 213 final StructNdMsg ndMsg = neighMsg.getNdHeader(); 214 if (ndMsg == null) { 215 mLog.e("RtNetlinkNeighborMessage without ND message header!"); 216 return; 217 } 218 219 final int ifindex = ndMsg.ndm_ifindex; 220 final InetAddress destination = neighMsg.getDestination(); 221 final short nudState = 222 (msgType == RTM_DELNEIGH) 223 ? StructNdMsg.NUD_NONE 224 : ndMsg.ndm_state; 225 226 final NeighborEvent event = new NeighborEvent( 227 whenMs, msgType, ifindex, destination, nudState, 228 getMacAddress(neighMsg.getLinkLayerAddress())); 229 230 if (VDBG) { 231 Log.d(TAG, neighMsg.toString()); 232 } 233 if (DBG) { 234 Log.d(TAG, event.toString()); 235 } 236 237 mConsumer.accept(event); 238 } 239 240 private static MacAddress getMacAddress(byte[] linkLayerAddress) { 241 if (linkLayerAddress != null) { 242 try { 243 return MacAddress.fromBytes(linkLayerAddress); 244 } catch (IllegalArgumentException e) { 245 Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress)); 246 } 247 } 248 249 return null; 250 } 251 } 252