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.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.net.MacAddress; 26 import android.net.util.SharedLog; 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.netlink.NetlinkMessage; 33 import com.android.net.module.util.netlink.NetlinkSocket; 34 import com.android.net.module.util.netlink.RtNetlinkNeighborMessage; 35 import com.android.net.module.util.netlink.StructNdMsg; 36 37 import java.net.InetAddress; 38 import java.util.StringJoiner; 39 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 NetlinkSocket.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 public static class NeighborEvent { 81 final long elapsedMs; 82 final short msgType; 83 final int ifindex; 84 final InetAddress ip; 85 final short nudState; 86 final MacAddress macAddr; 87 NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, short nudState, MacAddress macAddr)88 public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, 89 short nudState, MacAddress macAddr) { 90 this.elapsedMs = elapsedMs; 91 this.msgType = msgType; 92 this.ifindex = ifindex; 93 this.ip = ip; 94 this.nudState = nudState; 95 this.macAddr = macAddr; 96 } 97 isConnected()98 boolean isConnected() { 99 return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState); 100 } 101 isValid()102 boolean isValid() { 103 return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState); 104 } 105 106 @Override toString()107 public String toString() { 108 final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}"); 109 return j.add("@" + elapsedMs) 110 .add(stringForNlMsgType(msgType, NETLINK_ROUTE)) 111 .add("if=" + ifindex) 112 .add(ip.getHostAddress()) 113 .add(StructNdMsg.stringForNudState(nudState)) 114 .add("[" + macAddr + "]") 115 .toString(); 116 } 117 } 118 119 public interface NeighborEventConsumer { 120 // Every neighbor event received on the netlink socket is passed in 121 // here. Subclasses should filter for events of interest. accept(NeighborEvent event)122 public void accept(NeighborEvent event); 123 } 124 125 private final NeighborEventConsumer mConsumer; 126 IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb)127 public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) { 128 super(h, log, TAG, NETLINK_ROUTE, OsConstants.RTMGRP_NEIGH); 129 mConsumer = (cb != null) ? cb : (event) -> { /* discard */ }; 130 } 131 132 @Override processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs)133 public void processNetlinkMessage(NetlinkMessage nlMsg, final long whenMs) { 134 if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { 135 mLog.e("non-rtnetlink neighbor msg: " + nlMsg); 136 return; 137 } 138 139 final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) nlMsg; 140 final short msgType = neighMsg.getHeader().nlmsg_type; 141 final StructNdMsg ndMsg = neighMsg.getNdHeader(); 142 if (ndMsg == null) { 143 mLog.e("RtNetlinkNeighborMessage without ND message header!"); 144 return; 145 } 146 147 final int ifindex = ndMsg.ndm_ifindex; 148 final InetAddress destination = neighMsg.getDestination(); 149 final short nudState = 150 (msgType == RTM_DELNEIGH) 151 ? StructNdMsg.NUD_NONE 152 : ndMsg.ndm_state; 153 154 final NeighborEvent event = new NeighborEvent( 155 whenMs, msgType, ifindex, destination, nudState, 156 getMacAddress(neighMsg.getLinkLayerAddress())); 157 158 if (VDBG) { 159 Log.d(TAG, neighMsg.toString()); 160 } 161 if (DBG) { 162 Log.d(TAG, event.toString()); 163 } 164 165 mConsumer.accept(event); 166 } 167 getMacAddress(byte[] linkLayerAddress)168 private static MacAddress getMacAddress(byte[] linkLayerAddress) { 169 if (linkLayerAddress != null) { 170 try { 171 return MacAddress.fromBytes(linkLayerAddress); 172 } catch (IllegalArgumentException e) { 173 Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress)); 174 } 175 } 176 177 return null; 178 } 179 } 180