1 /* 2 * Copyright (C) 2016 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.*; 20 21 import android.net.NetworkUtils; 22 import android.net.util.BlockingSocketReader; 23 import android.net.util.ConnectivityPacketSummary; 24 import android.os.Handler; 25 import android.system.ErrnoException; 26 import android.system.Os; 27 import android.system.PacketSocketAddress; 28 import android.util.Log; 29 import android.util.LocalLog; 30 31 import libcore.io.IoBridge; 32 import libcore.util.HexEncoding; 33 34 import java.io.FileDescriptor; 35 import java.io.InterruptedIOException; 36 import java.io.IOException; 37 import java.net.NetworkInterface; 38 import java.net.SocketException; 39 40 41 /** 42 * Critical connectivity packet tracking daemon. 43 * 44 * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. 45 * 46 * This class's constructor, start() and stop() methods must only be called 47 * from the same thread on which the passed in |log| is accessed. 48 * 49 * Log lines include a hexdump of the packet, which can be decoded via: 50 * 51 * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /' 52 * | text2pcap - - 53 * | tcpdump -n -vv -e -r - 54 * 55 * @hide 56 */ 57 public class ConnectivityPacketTracker { 58 private static final String TAG = ConnectivityPacketTracker.class.getSimpleName(); 59 private static final boolean DBG = false; 60 private static final String MARK_START = "--- START ---"; 61 private static final String MARK_STOP = "--- STOP ---"; 62 63 private final String mTag; 64 private final Handler mHandler; 65 private final LocalLog mLog; 66 private final BlockingSocketReader mPacketListener; 67 68 public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) { 69 final String ifname; 70 final int ifindex; 71 final byte[] hwaddr; 72 final int mtu; 73 74 try { 75 ifname = netif.getName(); 76 ifindex = netif.getIndex(); 77 hwaddr = netif.getHardwareAddress(); 78 mtu = netif.getMTU(); 79 } catch (NullPointerException|SocketException e) { 80 throw new IllegalArgumentException("bad network interface", e); 81 } 82 83 mTag = TAG + "." + ifname; 84 mHandler = new Handler(); 85 mLog = log; 86 mPacketListener = new PacketListener(ifindex, hwaddr, mtu); 87 } 88 89 public void start() { 90 mLog.log(MARK_START); 91 mPacketListener.start(); 92 } 93 94 public void stop() { 95 mPacketListener.stop(); 96 mLog.log(MARK_STOP); 97 } 98 99 private final class PacketListener extends BlockingSocketReader { 100 private final int mIfIndex; 101 private final byte mHwAddr[]; 102 103 PacketListener(int ifindex, byte[] hwaddr, int mtu) { 104 super(mtu); 105 mIfIndex = ifindex; 106 mHwAddr = hwaddr; 107 } 108 109 @Override 110 protected FileDescriptor createSocket() { 111 FileDescriptor s = null; 112 try { 113 // TODO: Evaluate switching to SOCK_DGRAM and changing the 114 // BlockingSocketReader's read() to recvfrom(), so that this 115 // might work on non-ethernet-like links (via SLL). 116 s = Os.socket(AF_PACKET, SOCK_RAW, 0); 117 NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER); 118 Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex)); 119 } catch (ErrnoException | IOException e) { 120 logError("Failed to create packet tracking socket: ", e); 121 closeSocket(s); 122 return null; 123 } 124 return s; 125 } 126 127 @Override 128 protected void handlePacket(byte[] recvbuf, int length) { 129 final String summary = ConnectivityPacketSummary.summarize( 130 mHwAddr, recvbuf, length); 131 if (summary == null) return; 132 133 if (DBG) Log.d(mTag, summary); 134 addLogEntry(summary + 135 "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]"); 136 } 137 138 @Override 139 protected void logError(String msg, Exception e) { 140 Log.e(mTag, msg, e); 141 addLogEntry(msg + e); 142 } 143 144 private void addLogEntry(String entry) { 145 mHandler.post(() -> mLog.log(entry)); 146 } 147 } 148 } 149