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