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