blob: 9e6d80d67ec714c45c79206d4656a527a02f5974 [file] [log] [blame]
junyulai3a5caf82019-01-03 18:50:15 +08001/*
junyulai6b7cf0f2019-06-04 05:26:38 -07002 * Copyright (C) 2019 The Android Open Source Project
junyulai3a5caf82019-01-03 18:50:15 +08003 *
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
17package android.net;
18
Aaron Huang5dcbfa82020-01-09 22:04:21 +080019import static android.net.InvalidPacketException.ERROR_INVALID_IP_ADDRESS;
20import static android.net.InvalidPacketException.ERROR_INVALID_PORT;
markchien46f41d42018-12-27 22:49:51 +080021
Aaron Huang5dcbfa82020-01-09 22:04:21 +080022import android.annotation.NonNull;
Chiachang Wangfd007f02020-03-13 16:37:04 +080023import android.annotation.Nullable;
Aaron Huang5dcbfa82020-01-09 22:04:21 +080024import android.annotation.SystemApi;
junyulai6b7cf0f2019-06-04 05:26:38 -070025import android.os.Parcel;
Aaron Huang9deb7632019-04-23 22:17:16 +080026import android.os.Parcelable;
junyulai3a5caf82019-01-03 18:50:15 +080027import android.system.OsConstants;
28
Remi NGUYEN VAN7e3efdb2020-09-24 18:31:55 +090029import com.android.net.module.util.IpUtils;
30
junyulai3a5caf82019-01-03 18:50:15 +080031import java.net.Inet4Address;
chiachangwangc18f0bb2023-06-09 06:09:44 +000032import java.net.Inet6Address;
junyulai3a5caf82019-01-03 18:50:15 +080033import java.net.InetAddress;
chiachangwangc18f0bb2023-06-09 06:09:44 +000034import java.net.UnknownHostException;
junyulai3a5caf82019-01-03 18:50:15 +080035import java.nio.ByteBuffer;
36import java.nio.ByteOrder;
Chiachang Wangfd007f02020-03-13 16:37:04 +080037import java.util.Objects;
junyulai3a5caf82019-01-03 18:50:15 +080038
39/** @hide */
Aaron Huang5dcbfa82020-01-09 22:04:21 +080040@SystemApi
Aaron Huang9deb7632019-04-23 22:17:16 +080041public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
Aaron Huang91caaee2019-10-02 01:39:46 +080042 private static final int IPV4_HEADER_LENGTH = 20;
chiachangwangc18f0bb2023-06-09 06:09:44 +000043 private static final int IPV6_HEADER_LENGTH = 40;
Aaron Huang91caaee2019-10-02 01:39:46 +080044 private static final int UDP_HEADER_LENGTH = 8;
45
junyulai3a5caf82019-01-03 18:50:15 +080046 // This should only be constructed via static factory methods, such as
47 // nattKeepalivePacket
Aaron Huang5dcbfa82020-01-09 22:04:21 +080048 public NattKeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort,
49 @NonNull InetAddress dstAddress, int dstPort, @NonNull byte[] data) throws
junyulai3a5caf82019-01-03 18:50:15 +080050 InvalidPacketException {
51 super(srcAddress, srcPort, dstAddress, dstPort, data);
52 }
53
54 /**
55 * Factory method to create Nat-T keepalive packet structure.
Aaron Huang5dcbfa82020-01-09 22:04:21 +080056 * @hide
junyulai3a5caf82019-01-03 18:50:15 +080057 */
58 public static NattKeepalivePacketData nattKeepalivePacket(
59 InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
60 throws InvalidPacketException {
chiachangwangdf347442023-06-06 03:12:24 +000061 if (dstPort != NattSocketKeepalive.NATT_PORT) {
62 throw new InvalidPacketException(ERROR_INVALID_PORT);
63 }
junyulai3a5caf82019-01-03 18:50:15 +080064
chiachangwangc18f0bb2023-06-09 06:09:44 +000065 // Convert IPv4 mapped v6 address to v4 if any.
66 final InetAddress srcAddr, dstAddr;
67 try {
68 srcAddr = InetAddress.getByAddress(srcAddress.getAddress());
69 dstAddr = InetAddress.getByAddress(dstAddress.getAddress());
70 } catch (UnknownHostException e) {
markchien46f41d42018-12-27 22:49:51 +080071 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
junyulai3a5caf82019-01-03 18:50:15 +080072 }
73
chiachangwangc18f0bb2023-06-09 06:09:44 +000074 if (srcAddr instanceof Inet4Address && dstAddr instanceof Inet4Address) {
75 return nattKeepalivePacketv4(
76 (Inet4Address) srcAddr, srcPort, (Inet4Address) dstAddr, dstPort);
chiachangwang980e7672023-06-14 15:24:11 +000077 } else if (srcAddr instanceof Inet6Address && dstAddr instanceof Inet6Address) {
chiachangwangc18f0bb2023-06-09 06:09:44 +000078 return nattKeepalivePacketv6(
79 (Inet6Address) srcAddr, srcPort, (Inet6Address) dstAddr, dstPort);
80 } else {
81 // Destination address and source address should be the same IP family.
82 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
83 }
chiachangwangdf347442023-06-06 03:12:24 +000084 }
junyulai3a5caf82019-01-03 18:50:15 +080085
chiachangwangdf347442023-06-06 03:12:24 +000086 private static NattKeepalivePacketData nattKeepalivePacketv4(
87 Inet4Address srcAddress, int srcPort, Inet4Address dstAddress, int dstPort)
88 throws InvalidPacketException {
junyulai3a5caf82019-01-03 18:50:15 +080089 int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
chiachangwangc6a2f6f2023-06-06 06:51:35 +000090 final ByteBuffer buf = ByteBuffer.allocate(length);
junyulai3a5caf82019-01-03 18:50:15 +080091 buf.order(ByteOrder.BIG_ENDIAN);
chiachangwangc6a2f6f2023-06-06 06:51:35 +000092 buf.putShort((short) 0x4500); // IP version and TOS
junyulai3a5caf82019-01-03 18:50:15 +080093 buf.putShort((short) length);
chiachangwangc6a2f6f2023-06-06 06:51:35 +000094 buf.putShort((short) 0); // ID
95 buf.putShort((short) 0x4000); // Flags(DF), offset
96 // Technically speaking, this should be reading/using the v4 sysctl
97 // /proc/sys/net/ipv4/ip_default_ttl. Use hard-coded 64 for simplicity.
98 buf.put((byte) 64); // TTL
junyulai3a5caf82019-01-03 18:50:15 +080099 buf.put((byte) OsConstants.IPPROTO_UDP);
chiachangwangc18f0bb2023-06-09 06:09:44 +0000100 final int ipChecksumOffset = buf.position();
chiachangwangc6a2f6f2023-06-06 06:51:35 +0000101 buf.putShort((short) 0); // IP checksum
junyulai3a5caf82019-01-03 18:50:15 +0800102 buf.put(srcAddress.getAddress());
103 buf.put(dstAddress.getAddress());
104 buf.putShort((short) srcPort);
105 buf.putShort((short) dstPort);
chiachangwangc6a2f6f2023-06-06 06:51:35 +0000106 buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length
chiachangwangc18f0bb2023-06-09 06:09:44 +0000107 final int udpChecksumOffset = buf.position();
chiachangwangc6a2f6f2023-06-06 06:51:35 +0000108 buf.putShort((short) 0); // UDP checksum
109 buf.put((byte) 0xff); // NAT-T keepalive
junyulai3a5caf82019-01-03 18:50:15 +0800110 buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
111 buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
112
113 return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
114 }
Aaron Huang9deb7632019-04-23 22:17:16 +0800115
chiachangwangc18f0bb2023-06-09 06:09:44 +0000116 private static NattKeepalivePacketData nattKeepalivePacketv6(
117 Inet6Address srcAddress, int srcPort, Inet6Address dstAddress, int dstPort)
118 throws InvalidPacketException {
119 final ByteBuffer buf = ByteBuffer.allocate(IPV6_HEADER_LENGTH + UDP_HEADER_LENGTH + 1);
120 buf.order(ByteOrder.BIG_ENDIAN);
121 buf.putInt(0x60000000); // IP version, traffic class and flow label
122 buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // Payload length
123 buf.put((byte) OsConstants.IPPROTO_UDP); // Next header
124 // For native ipv6, this hop limit value should use the per interface v6 hoplimit sysctl.
125 // For 464xlat, this value should use the v4 ttl sysctl.
126 // Either way, for simplicity, just hard code 64.
127 buf.put((byte) 64); // Hop limit
128 buf.put(srcAddress.getAddress());
129 buf.put(dstAddress.getAddress());
130 // UDP
131 buf.putShort((short) srcPort);
132 buf.putShort((short) dstPort);
133 buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length = Payload length
134 final int udpChecksumOffset = buf.position();
135 buf.putShort((short) 0); // UDP checksum
136 buf.put((byte) 0xff); // NAT-T keepalive. 1 byte of data
137 buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV6_HEADER_LENGTH));
138 return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
139 }
junyulai6b7cf0f2019-06-04 05:26:38 -0700140 /** Parcelable Implementation */
141 public int describeContents() {
142 return 0;
Aaron Huang9deb7632019-04-23 22:17:16 +0800143 }
junyulai6b7cf0f2019-06-04 05:26:38 -0700144
145 /** Write to parcel */
Aaron Huang5dcbfa82020-01-09 22:04:21 +0800146 public void writeToParcel(@NonNull Parcel out, int flags) {
Aaron Huang60011ce2020-03-18 19:24:31 +0800147 out.writeString(getSrcAddress().getHostAddress());
148 out.writeString(getDstAddress().getHostAddress());
149 out.writeInt(getSrcPort());
150 out.writeInt(getDstPort());
junyulai6b7cf0f2019-06-04 05:26:38 -0700151 }
152
153 /** Parcelable Creator */
Aaron Huang5dcbfa82020-01-09 22:04:21 +0800154 public static final @NonNull Parcelable.Creator<NattKeepalivePacketData> CREATOR =
junyulai6b7cf0f2019-06-04 05:26:38 -0700155 new Parcelable.Creator<NattKeepalivePacketData>() {
156 public NattKeepalivePacketData createFromParcel(Parcel in) {
157 final InetAddress srcAddress =
158 InetAddresses.parseNumericAddress(in.readString());
159 final InetAddress dstAddress =
160 InetAddresses.parseNumericAddress(in.readString());
161 final int srcPort = in.readInt();
162 final int dstPort = in.readInt();
163 try {
164 return NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort,
165 dstAddress, dstPort);
166 } catch (InvalidPacketException e) {
167 throw new IllegalArgumentException(
Aaron Huang60011ce2020-03-18 19:24:31 +0800168 "Invalid NAT-T keepalive data: " + e.getError());
junyulai6b7cf0f2019-06-04 05:26:38 -0700169 }
170 }
171
172 public NattKeepalivePacketData[] newArray(int size) {
173 return new NattKeepalivePacketData[size];
174 }
175 };
Chiachang Wangfd007f02020-03-13 16:37:04 +0800176
177 @Override
178 public boolean equals(@Nullable final Object o) {
179 if (!(o instanceof NattKeepalivePacketData)) return false;
180 final NattKeepalivePacketData other = (NattKeepalivePacketData) o;
Aaron Huang60011ce2020-03-18 19:24:31 +0800181 final InetAddress srcAddress = getSrcAddress();
182 final InetAddress dstAddress = getDstAddress();
183 return srcAddress.equals(other.getSrcAddress())
184 && dstAddress.equals(other.getDstAddress())
185 && getSrcPort() == other.getSrcPort()
186 && getDstPort() == other.getDstPort();
Chiachang Wangfd007f02020-03-13 16:37:04 +0800187 }
188
189 @Override
190 public int hashCode() {
Aaron Huang60011ce2020-03-18 19:24:31 +0800191 return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort());
Chiachang Wangfd007f02020-03-13 16:37:04 +0800192 }
junyulai3a5caf82019-01-03 18:50:15 +0800193}