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 java.io.ByteArrayOutputStream;
20 import java.io.DataInputStream;
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23 import java.net.Inet4Address;
24 import java.util.Arrays;
25 
26 public class Ipv4Packet {
27     private static final int HEADER_MIN_LENGTH = 20;
28 
29     int version;
30     int headerLength;
31     int type;
32     int totalLength;
33     int identification;
34     int flagsAndOffset;
35     int timeToLive;
36     int protocol;
37     Inet4Address sourceAddress;
38     Inet4Address destinationAddress;
39     byte[] options;
40     byte[] data;
41 
Ipv4Packet(DataInputStream stream)42     public Ipv4Packet(DataInputStream stream) throws IOException {
43         int versionIhl = stream.readUnsignedByte();
44         version = (versionIhl & 0xF0) >> 4;
45         headerLength = (versionIhl & 0x0F) * 4;
46 
47         type = stream.readUnsignedByte();
48         totalLength = stream.readUnsignedShort();
49         identification = stream.readUnsignedShort();
50         flagsAndOffset = stream.readUnsignedShort();
51         timeToLive = stream.readUnsignedByte();
52         protocol = stream.readUnsignedByte();
53         int checksum = stream.readUnsignedShort();
54 
55         byte[] address = new byte[4];
56 
57         stream.read(address, 0, address.length);
58         sourceAddress = (Inet4Address) Inet4Address.getByAddress(address);
59 
60         stream.read(address, 0, address.length);
61         destinationAddress = (Inet4Address) Inet4Address.getByAddress(address);
62 
63         if (headerLength < HEADER_MIN_LENGTH) {
64             throw new IllegalArgumentException("Header length = " + headerLength
65                     + " is less than HEADER_MIN_LENGTH = " + HEADER_MIN_LENGTH);
66         }
67         options = new byte[headerLength - HEADER_MIN_LENGTH];
68         stream.read(options, 0, options.length);
69 
70         if (totalLength < headerLength) {
71             throw new IllegalArgumentException("Total length = " + totalLength
72                     + " is less than header length = " + headerLength);
73         }
74         data = new byte[totalLength - headerLength];
75         stream.read(data, 0, data.length);
76 
77         byte[] original = new byte[totalLength];
78         stream.reset();
79         stream.readFully(original);
80         if (!Arrays.equals(original, getEncoded())) {
81             throw new IOException("Corrupted message. Checksum: " + checksum);
82         }
83     }
84 
setOptions(byte[] newOptions)85     public void setOptions(byte[] newOptions) {
86         options = newOptions;
87         headerLength = HEADER_MIN_LENGTH + options.length;
88     }
89 
setData(byte[] newData)90     public void setData(byte[] newData) {
91         data = newData;
92         totalLength = headerLength + data.length;
93     }
94 
getEncoded()95     public byte[] getEncoded() throws IOException {
96         ByteArrayOutputStream output = new ByteArrayOutputStream();
97         DataOutputStream stream = new DataOutputStream(output);
98 
99         stream.writeByte((version << 4) | (headerLength / 4));
100         stream.writeByte(type);
101         stream.writeShort(totalLength);
102         stream.writeShort(identification);
103         stream.writeShort(flagsAndOffset);
104         stream.writeByte(timeToLive);
105         stream.writeByte(protocol);
106 
107         int checksumPosition = stream.size();
108         stream.writeShort(/* checksum */ 0);
109         stream.write(sourceAddress.getAddress(), 0, 4);
110         stream.write(destinationAddress.getAddress(), 0, 4);
111         stream.write(options, 0, options.length);
112         stream.write(data, 0, data.length);
113         stream.close();
114 
115         byte[] result = output.toByteArray();
116         int checksum = Rfc1071.checksum(result, headerLength);
117         result[checksumPosition + 0] = (byte) ((checksum & 0xFF00) >> 8);
118         result[checksumPosition + 1] = (byte) ((checksum & 0x00FF));
119         return result;
120     }
121 
122     @Override
toString()123     public String toString() {
124         StringBuilder out = new StringBuilder(256);
125 
126         out.append("IPv4 Packet {");
127         out.append("\n  Version:        ").append(version);
128         out.append("\n  Header length:  ").append(headerLength);
129         out.append("\n  Type:           ").append(type);
130         out.append("\n  Total length:   ").append(totalLength);
131         out.append("\n  Identification: ").append(identification);
132         out.append("\n  Flags + offset: ").append(flagsAndOffset);
133         out.append("\n  Time to live:   ").append(timeToLive);
134         out.append("\n  Protocol:       ").append(protocol);
135         out.append("\n  Source:         ").append(sourceAddress.getHostAddress());
136         out.append("\n  Destination:    ").append(destinationAddress.getHostAddress());
137         out.append("\n  Options: [");
138         for (int i = 0 ; i < options.length; i++) {
139             if (i % 16 == 0) {
140                 out.append(String.format("\n%4s", ""));
141             }
142             out.append(String.format(" %02X", options[i] & 0xFF));
143         }
144         out.append("\n  ]");
145         out.append("\n  Data: [");
146         for (int i = 0 ; i < data.length; i++) {
147             if (i % 16 == 0) {
148                 out.append(String.format("\n%4s", ""));
149             }
150             out.append(String.format(" %02X", data[i] & 0xFF));
151         }
152         out.append("\n  ]");
153         out.append("\n}");
154         return out.toString();
155     }
156 }
157