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