1 /* 2 * Copyright (C) 2014 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.cts.net.hostside; 18 19 import static android.system.OsConstants.ICMP6_ECHO_REPLY; 20 import static android.system.OsConstants.ICMP6_ECHO_REQUEST; 21 import static android.system.OsConstants.ICMP_ECHO; 22 import static android.system.OsConstants.ICMP_ECHOREPLY; 23 24 import android.system.ErrnoException; 25 import android.system.Os; 26 import android.util.Log; 27 28 import java.io.FileDescriptor; 29 import java.io.IOException; 30 31 public class PacketReflector extends Thread { 32 33 private static int IPV4_HEADER_LENGTH = 20; 34 private static int IPV6_HEADER_LENGTH = 40; 35 36 private static int IPV4_ADDR_OFFSET = 12; 37 private static int IPV6_ADDR_OFFSET = 8; 38 private static int IPV4_ADDR_LENGTH = 4; 39 private static int IPV6_ADDR_LENGTH = 16; 40 41 private static int IPV4_PROTO_OFFSET = 9; 42 private static int IPV6_PROTO_OFFSET = 6; 43 44 private static final byte IPPROTO_ICMP = 1; 45 private static final byte IPPROTO_TCP = 6; 46 private static final byte IPPROTO_UDP = 17; 47 private static final byte IPPROTO_ICMPV6 = 58; 48 49 private static int ICMP_HEADER_LENGTH = 8; 50 private static int TCP_HEADER_LENGTH = 20; 51 private static int UDP_HEADER_LENGTH = 8; 52 53 private static final byte ICMP_ECHO = 8; 54 private static final byte ICMP_ECHOREPLY = 0; 55 56 private static String TAG = "PacketReflector"; 57 58 private FileDescriptor mFd; 59 private byte[] mBuf; 60 PacketReflector(FileDescriptor fd, int mtu)61 public PacketReflector(FileDescriptor fd, int mtu) { 62 super("PacketReflector"); 63 mFd = fd; 64 mBuf = new byte[mtu]; 65 } 66 swapBytes(byte[] buf, int pos1, int pos2, int len)67 private static void swapBytes(byte[] buf, int pos1, int pos2, int len) { 68 for (int i = 0; i < len; i++) { 69 byte b = buf[pos1 + i]; 70 buf[pos1 + i] = buf[pos2 + i]; 71 buf[pos2 + i] = b; 72 } 73 } 74 swapAddresses(byte[] buf, int version)75 private static void swapAddresses(byte[] buf, int version) { 76 int addrPos, addrLen; 77 switch(version) { 78 case 4: 79 addrPos = IPV4_ADDR_OFFSET; 80 addrLen = IPV4_ADDR_LENGTH; 81 break; 82 case 6: 83 addrPos = IPV6_ADDR_OFFSET; 84 addrLen = IPV6_ADDR_LENGTH; 85 break; 86 default: 87 throw new IllegalArgumentException(); 88 } 89 swapBytes(buf, addrPos, addrPos + addrLen, addrLen); 90 } 91 92 // Reflect TCP packets: swap the source and destination addresses, but don't change the ports. 93 // This is used by the test to "connect to itself" through the VPN. processTcpPacket(byte[] buf, int version, int len, int hdrLen)94 private void processTcpPacket(byte[] buf, int version, int len, int hdrLen) { 95 if (len < hdrLen + TCP_HEADER_LENGTH) { 96 return; 97 } 98 99 // Swap src and dst IP addresses. 100 swapAddresses(buf, version); 101 102 // Send the packet back. 103 writePacket(buf, len); 104 } 105 106 // Echo UDP packets: swap source and destination addresses, and source and destination ports. 107 // This is used by the test to check that the bytes it sends are echoed back. processUdpPacket(byte[] buf, int version, int len, int hdrLen)108 private void processUdpPacket(byte[] buf, int version, int len, int hdrLen) { 109 if (len < hdrLen + UDP_HEADER_LENGTH) { 110 return; 111 } 112 113 // Swap src and dst IP addresses. 114 swapAddresses(buf, version); 115 116 // Swap dst and src ports. 117 int portOffset = hdrLen; 118 swapBytes(buf, portOffset, portOffset + 2, 2); 119 120 // Send the packet back. 121 writePacket(buf, len); 122 } 123 processIcmpPacket(byte[] buf, int version, int len, int hdrLen)124 private void processIcmpPacket(byte[] buf, int version, int len, int hdrLen) { 125 if (len < hdrLen + ICMP_HEADER_LENGTH) { 126 return; 127 } 128 129 byte type = buf[hdrLen]; 130 if (!(version == 4 && type == ICMP_ECHO) && 131 !(version == 6 && type == (byte) ICMP6_ECHO_REQUEST)) { 132 return; 133 } 134 135 // Save the ping packet we received. 136 byte[] request = buf.clone(); 137 138 // Swap src and dst IP addresses, and send the packet back. 139 // This effectively pings the device to see if it replies. 140 swapAddresses(buf, version); 141 writePacket(buf, len); 142 143 // The device should have replied, and buf should now contain a ping response. 144 int received = readPacket(buf); 145 if (received != len) { 146 Log.i(TAG, "Reflecting ping did not result in ping response: " + 147 "read=" + received + " expected=" + len); 148 return; 149 } 150 151 byte replyType = buf[hdrLen]; 152 if ((type == ICMP_ECHO && replyType != ICMP_ECHOREPLY) 153 || (type == (byte) ICMP6_ECHO_REQUEST && replyType != (byte) ICMP6_ECHO_REPLY)) { 154 Log.i(TAG, "Received unexpected ICMP reply: original " + type 155 + ", reply " + replyType); 156 return; 157 } 158 159 // Compare the response we got with the original packet. 160 // The only thing that should have changed are addresses, type and checksum. 161 // Overwrite them with the received bytes and see if the packet is otherwise identical. 162 request[hdrLen] = buf[hdrLen]; // Type 163 request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1. 164 request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2. 165 166 // Since Linux kernel 4.2, net.ipv6.auto_flowlabels is set by default, and therefore 167 // the request and reply may have different IPv6 flow label: ignore that as well. 168 if (version == 6) { 169 request[1] = (byte)(request[1] & 0xf0 | buf[1] & 0x0f); 170 request[2] = buf[2]; 171 request[3] = buf[3]; 172 } 173 174 for (int i = 0; i < len; i++) { 175 if (buf[i] != request[i]) { 176 Log.i(TAG, "Received non-matching packet when expecting ping response."); 177 return; 178 } 179 } 180 181 // Now swap the addresses again and reflect the packet. This sends a ping reply. 182 swapAddresses(buf, version); 183 writePacket(buf, len); 184 } 185 writePacket(byte[] buf, int len)186 private void writePacket(byte[] buf, int len) { 187 try { 188 Os.write(mFd, buf, 0, len); 189 } catch (ErrnoException|IOException e) { 190 Log.e(TAG, "Error writing packet: " + e.getMessage()); 191 } 192 } 193 readPacket(byte[] buf)194 private int readPacket(byte[] buf) { 195 int len; 196 try { 197 len = Os.read(mFd, buf, 0, buf.length); 198 } catch (ErrnoException|IOException e) { 199 Log.e(TAG, "Error reading packet: " + e.getMessage()); 200 len = -1; 201 } 202 return len; 203 } 204 205 // Reads one packet from our mFd, and possibly writes the packet back. processPacket()206 private void processPacket() { 207 int len = readPacket(mBuf); 208 if (len < 1) { 209 return; 210 } 211 212 int version = mBuf[0] >> 4; 213 int addrPos, protoPos, hdrLen, addrLen; 214 if (version == 4) { 215 hdrLen = IPV4_HEADER_LENGTH; 216 protoPos = IPV4_PROTO_OFFSET; 217 addrPos = IPV4_ADDR_OFFSET; 218 addrLen = IPV4_ADDR_LENGTH; 219 } else if (version == 6) { 220 hdrLen = IPV6_HEADER_LENGTH; 221 protoPos = IPV6_PROTO_OFFSET; 222 addrPos = IPV6_ADDR_OFFSET; 223 addrLen = IPV6_ADDR_LENGTH; 224 } else { 225 return; 226 } 227 228 if (len < hdrLen) { 229 return; 230 } 231 232 byte proto = mBuf[protoPos]; 233 switch (proto) { 234 case IPPROTO_ICMP: 235 case IPPROTO_ICMPV6: 236 processIcmpPacket(mBuf, version, len, hdrLen); 237 break; 238 case IPPROTO_TCP: 239 processTcpPacket(mBuf, version, len, hdrLen); 240 break; 241 case IPPROTO_UDP: 242 processUdpPacket(mBuf, version, len, hdrLen); 243 break; 244 } 245 } 246 run()247 public void run() { 248 Log.i(TAG, "PacketReflector starting fd=" + mFd + " valid=" + mFd.valid()); 249 while (!interrupted() && mFd.valid()) { 250 processPacket(); 251 } 252 Log.i(TAG, "PacketReflector exiting fd=" + mFd + " valid=" + mFd.valid()); 253 } 254 } 255