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