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