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