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 com.android.cts.vpnfirewall;
18 
19 import android.system.ErrnoException;
20 import android.system.Os;
21 import android.util.Log;
22 
23 import java.io.ByteArrayInputStream;
24 import java.io.DataInputStream;
25 import java.io.FileDescriptor;
26 import java.io.IOException;
27 import java.net.Inet4Address;
28 
29 public class PingReflector extends Thread {
30     private static final String TAG = "PingReflector";
31 
32     private static final int PROTOCOL_ICMP = 0x01;
33 
34     private FileDescriptor mFd;
35     private byte[] mBuf;
36 
PingReflector(FileDescriptor fd, int mtu)37     public PingReflector(FileDescriptor fd, int mtu) {
38         super("PingReflector");
39         mFd = fd;
40         mBuf = new byte[mtu];
41     }
42 
run()43     public void run() {
44         Log.i(TAG, "PingReflector starting fd=" + mFd + " valid=" + mFd.valid());
45         while (!interrupted() && mFd.valid()) {
46             int len = readPacket(mBuf);
47             if (len > 0) {
48                 int version = mBuf[0] >> 4;
49                 if (version != 4) {
50                     Log.e(TAG, "Received packet version: " + version + ". Ignoring.");
51                     continue;
52                 }
53                 try {
54                     processPacket(mBuf, version, len, 0);
55                 } catch (IOException e) {
56                     Log.w(TAG, "Failed processing packet", e);
57                 }
58             }
59         }
60         Log.i(TAG, "PingReflector exiting fd=" + mFd + " valid=" + mFd.valid());
61     }
62 
processPacket(byte[] buf, int version, int len, int hdrLen)63     private void processPacket(byte[] buf, int version, int len, int hdrLen) throws IOException {
64         IcmpMessage echo = null;
65         IcmpMessage response = null;
66 
67         DataInputStream stream = new DataInputStream(new ByteArrayInputStream(buf));
68         Ipv4Packet packet = new Ipv4Packet(stream);
69         Log.i(TAG, "Packet contents:\n" + packet);
70 
71         if (packet.protocol != PROTOCOL_ICMP) {
72             Log.i(TAG, "Protocol is " + packet.protocol + " not ICMP. Ignoring.");
73             return;
74         }
75 
76         echo = new IcmpMessage(
77                 new DataInputStream(new ByteArrayInputStream(packet.data)), packet.data.length);
78         Log.i(TAG, "Ping packet:\n" + echo);
79 
80         // Swap src and dst IP addresses to route the packet back into the device.
81         Inet4Address tmp = packet.sourceAddress;
82         packet.sourceAddress = packet.destinationAddress;
83         packet.destinationAddress = tmp;
84 
85         packet.setData(echo.getEncoded());
86         writePacket(packet.getEncoded());
87         Log.i(TAG, "Wrote packet back");
88     }
89 
writePacket(byte[] buf)90     private void writePacket(byte[] buf) {
91         try {
92             Os.write(mFd, buf, 0, buf.length);
93         } catch (ErrnoException | IOException e) {
94             Log.e(TAG, "Error writing packet: " + e.getMessage(), e);
95         }
96     }
97 
readPacket(byte[] buf)98     private int readPacket(byte[] buf) {
99         try {
100             return Os.read(mFd, buf, 0, buf.length);
101         } catch (ErrnoException | IOException e) {
102             Log.e(TAG, "Error reading packet: " + e.getMessage(), e);
103             return -1;
104         }
105     }
106 }
107