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