blob: a18e713ae4419a2303a0d369651730267e7a70c9 [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;
32import java.net.InetAddress;
33import java.nio.ByteBuffer;
34import java.nio.ByteOrder;
Chiachang Wangfd007f02020-03-13 16:37:04 +080035import java.util.Objects;
junyulai3a5caf82019-01-03 18:50:15 +080036
37/** @hide */
Aaron Huang5dcbfa82020-01-09 22:04:21 +080038@SystemApi
Aaron Huang9deb7632019-04-23 22:17:16 +080039public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable {
Aaron Huang91caaee2019-10-02 01:39:46 +080040 private static final int IPV4_HEADER_LENGTH = 20;
41 private static final int UDP_HEADER_LENGTH = 8;
42
junyulai3a5caf82019-01-03 18:50:15 +080043 // This should only be constructed via static factory methods, such as
44 // nattKeepalivePacket
Aaron Huang5dcbfa82020-01-09 22:04:21 +080045 public NattKeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort,
46 @NonNull InetAddress dstAddress, int dstPort, @NonNull byte[] data) throws
junyulai3a5caf82019-01-03 18:50:15 +080047 InvalidPacketException {
48 super(srcAddress, srcPort, dstAddress, dstPort, data);
49 }
50
51 /**
52 * Factory method to create Nat-T keepalive packet structure.
Aaron Huang5dcbfa82020-01-09 22:04:21 +080053 * @hide
junyulai3a5caf82019-01-03 18:50:15 +080054 */
55 public static NattKeepalivePacketData nattKeepalivePacket(
56 InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
57 throws InvalidPacketException {
chiachangwangdf347442023-06-06 03:12:24 +000058 if (dstPort != NattSocketKeepalive.NATT_PORT) {
59 throw new InvalidPacketException(ERROR_INVALID_PORT);
60 }
junyulai3a5caf82019-01-03 18:50:15 +080061
62 if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
markchien46f41d42018-12-27 22:49:51 +080063 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
junyulai3a5caf82019-01-03 18:50:15 +080064 }
65
chiachangwangdf347442023-06-06 03:12:24 +000066 return nattKeepalivePacketv4(
67 (Inet4Address) srcAddress, srcPort,
68 (Inet4Address) dstAddress, dstPort);
69 }
junyulai3a5caf82019-01-03 18:50:15 +080070
chiachangwangdf347442023-06-06 03:12:24 +000071 private static NattKeepalivePacketData nattKeepalivePacketv4(
72 Inet4Address srcAddress, int srcPort, Inet4Address dstAddress, int dstPort)
73 throws InvalidPacketException {
junyulai3a5caf82019-01-03 18:50:15 +080074 int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
chiachangwangc6a2f6f2023-06-06 06:51:35 +000075 final ByteBuffer buf = ByteBuffer.allocate(length);
junyulai3a5caf82019-01-03 18:50:15 +080076 buf.order(ByteOrder.BIG_ENDIAN);
chiachangwangc6a2f6f2023-06-06 06:51:35 +000077 buf.putShort((short) 0x4500); // IP version and TOS
junyulai3a5caf82019-01-03 18:50:15 +080078 buf.putShort((short) length);
chiachangwangc6a2f6f2023-06-06 06:51:35 +000079 buf.putShort((short) 0); // ID
80 buf.putShort((short) 0x4000); // Flags(DF), offset
81 // Technically speaking, this should be reading/using the v4 sysctl
82 // /proc/sys/net/ipv4/ip_default_ttl. Use hard-coded 64 for simplicity.
83 buf.put((byte) 64); // TTL
junyulai3a5caf82019-01-03 18:50:15 +080084 buf.put((byte) OsConstants.IPPROTO_UDP);
85 int ipChecksumOffset = buf.position();
chiachangwangc6a2f6f2023-06-06 06:51:35 +000086 buf.putShort((short) 0); // IP checksum
junyulai3a5caf82019-01-03 18:50:15 +080087 buf.put(srcAddress.getAddress());
88 buf.put(dstAddress.getAddress());
89 buf.putShort((short) srcPort);
90 buf.putShort((short) dstPort);
chiachangwangc6a2f6f2023-06-06 06:51:35 +000091 buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length
junyulai3a5caf82019-01-03 18:50:15 +080092 int udpChecksumOffset = buf.position();
chiachangwangc6a2f6f2023-06-06 06:51:35 +000093 buf.putShort((short) 0); // UDP checksum
94 buf.put((byte) 0xff); // NAT-T keepalive
junyulai3a5caf82019-01-03 18:50:15 +080095 buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
96 buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
97
98 return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
99 }
Aaron Huang9deb7632019-04-23 22:17:16 +0800100
junyulai6b7cf0f2019-06-04 05:26:38 -0700101 /** Parcelable Implementation */
102 public int describeContents() {
103 return 0;
Aaron Huang9deb7632019-04-23 22:17:16 +0800104 }
junyulai6b7cf0f2019-06-04 05:26:38 -0700105
106 /** Write to parcel */
Aaron Huang5dcbfa82020-01-09 22:04:21 +0800107 public void writeToParcel(@NonNull Parcel out, int flags) {
Aaron Huang60011ce2020-03-18 19:24:31 +0800108 out.writeString(getSrcAddress().getHostAddress());
109 out.writeString(getDstAddress().getHostAddress());
110 out.writeInt(getSrcPort());
111 out.writeInt(getDstPort());
junyulai6b7cf0f2019-06-04 05:26:38 -0700112 }
113
114 /** Parcelable Creator */
Aaron Huang5dcbfa82020-01-09 22:04:21 +0800115 public static final @NonNull Parcelable.Creator<NattKeepalivePacketData> CREATOR =
junyulai6b7cf0f2019-06-04 05:26:38 -0700116 new Parcelable.Creator<NattKeepalivePacketData>() {
117 public NattKeepalivePacketData createFromParcel(Parcel in) {
118 final InetAddress srcAddress =
119 InetAddresses.parseNumericAddress(in.readString());
120 final InetAddress dstAddress =
121 InetAddresses.parseNumericAddress(in.readString());
122 final int srcPort = in.readInt();
123 final int dstPort = in.readInt();
124 try {
125 return NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort,
126 dstAddress, dstPort);
127 } catch (InvalidPacketException e) {
128 throw new IllegalArgumentException(
Aaron Huang60011ce2020-03-18 19:24:31 +0800129 "Invalid NAT-T keepalive data: " + e.getError());
junyulai6b7cf0f2019-06-04 05:26:38 -0700130 }
131 }
132
133 public NattKeepalivePacketData[] newArray(int size) {
134 return new NattKeepalivePacketData[size];
135 }
136 };
Chiachang Wangfd007f02020-03-13 16:37:04 +0800137
138 @Override
139 public boolean equals(@Nullable final Object o) {
140 if (!(o instanceof NattKeepalivePacketData)) return false;
141 final NattKeepalivePacketData other = (NattKeepalivePacketData) o;
Aaron Huang60011ce2020-03-18 19:24:31 +0800142 final InetAddress srcAddress = getSrcAddress();
143 final InetAddress dstAddress = getDstAddress();
144 return srcAddress.equals(other.getSrcAddress())
145 && dstAddress.equals(other.getDstAddress())
146 && getSrcPort() == other.getSrcPort()
147 && getDstPort() == other.getDstPort();
Chiachang Wangfd007f02020-03-13 16:37:04 +0800148 }
149
150 @Override
151 public int hashCode() {
Aaron Huang60011ce2020-03-18 19:24:31 +0800152 return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort());
Chiachang Wangfd007f02020-03-13 16:37:04 +0800153 }
junyulai3a5caf82019-01-03 18:50:15 +0800154}