1 /*
2  * Copyright (C) 2019 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;
18 
19 import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
20 import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
21 
22 import android.net.SocketKeepalive.InvalidPacketException;
23 import android.net.util.IpUtils;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.system.OsConstants;
27 
28 import java.net.Inet4Address;
29 import java.net.InetAddress;
30 import java.nio.ByteBuffer;
31 import java.nio.ByteOrder;
32 
33 /** @hide */
34 public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
35     // This should only be constructed via static factory methods, such as
36     // nattKeepalivePacket
NattKeepalivePacketData(InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort, byte[] data)37     private NattKeepalivePacketData(InetAddress srcAddress, int srcPort,
38             InetAddress dstAddress, int dstPort, byte[] data) throws
39             InvalidPacketException {
40         super(srcAddress, srcPort, dstAddress, dstPort, data);
41     }
42 
43     /**
44      * Factory method to create Nat-T keepalive packet structure.
45      */
nattKeepalivePacket( InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)46     public static NattKeepalivePacketData nattKeepalivePacket(
47             InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
48             throws InvalidPacketException {
49 
50         if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
51             throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
52         }
53 
54         if (dstPort != NattSocketKeepalive.NATT_PORT) {
55             throw new InvalidPacketException(ERROR_INVALID_PORT);
56         }
57 
58         int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
59         ByteBuffer buf = ByteBuffer.allocate(length);
60         buf.order(ByteOrder.BIG_ENDIAN);
61         buf.putShort((short) 0x4500);             // IP version and TOS
62         buf.putShort((short) length);
63         buf.putInt(0);                            // ID, flags, offset
64         buf.put((byte) 64);                       // TTL
65         buf.put((byte) OsConstants.IPPROTO_UDP);
66         int ipChecksumOffset = buf.position();
67         buf.putShort((short) 0);                  // IP checksum
68         buf.put(srcAddress.getAddress());
69         buf.put(dstAddress.getAddress());
70         buf.putShort((short) srcPort);
71         buf.putShort((short) dstPort);
72         buf.putShort((short) (length - 20));      // UDP length
73         int udpChecksumOffset = buf.position();
74         buf.putShort((short) 0);                  // UDP checksum
75         buf.put((byte) 0xff);                     // NAT-T keepalive
76         buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
77         buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
78 
79         return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
80     }
81 
82     /** Parcelable Implementation */
describeContents()83     public int describeContents() {
84         return 0;
85     }
86 
87     /** Write to parcel */
writeToParcel(Parcel out, int flags)88     public void writeToParcel(Parcel out, int flags) {
89         out.writeString(srcAddress.getHostAddress());
90         out.writeString(dstAddress.getHostAddress());
91         out.writeInt(srcPort);
92         out.writeInt(dstPort);
93     }
94 
95     /** Parcelable Creator */
96     public static final Parcelable.Creator<NattKeepalivePacketData> CREATOR =
97             new Parcelable.Creator<NattKeepalivePacketData>() {
98                 public NattKeepalivePacketData createFromParcel(Parcel in) {
99                     final InetAddress srcAddress =
100                             InetAddresses.parseNumericAddress(in.readString());
101                     final InetAddress dstAddress =
102                             InetAddresses.parseNumericAddress(in.readString());
103                     final int srcPort = in.readInt();
104                     final int dstPort = in.readInt();
105                     try {
106                         return NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort,
107                                     dstAddress, dstPort);
108                     } catch (InvalidPacketException e) {
109                         throw new IllegalArgumentException(
110                                 "Invalid NAT-T keepalive data: " + e.error);
111                     }
112                 }
113 
114                 public NattKeepalivePacketData[] newArray(int size) {
115                     return new NattKeepalivePacketData[size];
116                 }
117             };
118 }
119