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.PacketReader; 23 import android.net.util.ConnectivityPacketSummary; 24 import android.net.util.InterfaceParams; 25 import android.os.Handler; 26 import android.system.ErrnoException; 27 import android.system.Os; 28 import android.system.PacketSocketAddress; 29 import android.text.TextUtils; 30 import android.util.Log; 31 import android.util.LocalLog; 32 33 import libcore.io.IoBridge; 34 import libcore.util.HexEncoding; 35 36 import java.io.FileDescriptor; 37 import java.io.InterruptedIOException; 38 import java.io.IOException; 39 import java.net.SocketException; 40 41 42 /** 43 * Critical connectivity packet tracking daemon. 44 * 45 * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. 46 * 47 * This class's constructor, start() and stop() methods must only be called 48 * from the same thread on which the passed in |log| is accessed. 49 * 50 * Log lines include a hexdump of the packet, which can be decoded via: 51 * 52 * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /' 53 * | text2pcap - - 54 * | tcpdump -n -vv -e -r - 55 * 56 * @hide 57 */ 58 public class ConnectivityPacketTracker { 59 private static final String TAG = ConnectivityPacketTracker.class.getSimpleName(); 60 private static final boolean DBG = false; 61 private static final String MARK_START = "--- START ---"; 62 private static final String MARK_STOP = "--- STOP ---"; 63 private static final String MARK_NAMED_START = "--- START (%s) ---"; 64 private static final String MARK_NAMED_STOP = "--- STOP (%s) ---"; 65 66 private final String mTag; 67 private final LocalLog mLog; 68 private final PacketReader mPacketListener; 69 private boolean mRunning; 70 private String mDisplayName; 71 72 public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) { 73 if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams"); 74 75 mTag = TAG + "." + ifParams.name; 76 mLog = log; 77 mPacketListener = new PacketListener(h, ifParams); 78 } 79 80 public void start(String displayName) { 81 mRunning = true; 82 mDisplayName = displayName; 83 mPacketListener.start(); 84 } 85 86 public void stop() { 87 mPacketListener.stop(); 88 mRunning = false; 89 mDisplayName = null; 90 } 91 92 private final class PacketListener extends PacketReader { 93 private final InterfaceParams mInterface; 94 95 PacketListener(Handler h, InterfaceParams ifParams) { 96 super(h, ifParams.defaultMtu); 97 mInterface = ifParams; 98 } 99 100 @Override 101 protected FileDescriptor createFd() { 102 FileDescriptor s = null; 103 try { 104 s = Os.socket(AF_PACKET, SOCK_RAW, 0); 105 NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER); 106 Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mInterface.index)); 107 } catch (ErrnoException | IOException e) { 108 logError("Failed to create packet tracking socket: ", e); 109 closeFd(s); 110 return null; 111 } 112 return s; 113 } 114 115 @Override 116 protected void handlePacket(byte[] recvbuf, int length) { 117 final String summary = ConnectivityPacketSummary.summarize( 118 mInterface.macAddr, recvbuf, length); 119 if (summary == null) return; 120 121 if (DBG) Log.d(mTag, summary); 122 addLogEntry(summary + 123 "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]"); 124 } 125 126 @Override 127 protected void onStart() { 128 final String msg = TextUtils.isEmpty(mDisplayName) 129 ? MARK_START 130 : String.format(MARK_NAMED_START, mDisplayName); 131 mLog.log(msg); 132 } 133 134 @Override 135 protected void onStop() { 136 String msg = TextUtils.isEmpty(mDisplayName) 137 ? MARK_STOP 138 : String.format(MARK_NAMED_STOP, mDisplayName); 139 if (!mRunning) msg += " (packet listener stopped unexpectedly)"; 140 mLog.log(msg); 141 } 142 143 @Override 144 protected void logError(String msg, Exception e) { 145 Log.e(mTag, msg, e); 146 addLogEntry(msg + e); 147 } 148 149 private void addLogEntry(String entry) { 150 mLog.log(entry); 151 } 152 } 153 } 154