Home | History | Annotate | Download | only in ip
      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