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.Inet6Address; 33 import java.net.InetAddress; 34 import java.net.UnknownHostException; 35 import java.nio.ByteBuffer; 36 import java.nio.ByteOrder; 37 import java.util.Objects; 38 39 /** @hide */ 40 @SystemApi 41 public final class NattKeepalivePacketData extends KeepalivePacketData implements Parcelable { 42 private static final int IPV4_HEADER_LENGTH = 20; 43 private static final int IPV6_HEADER_LENGTH = 40; 44 private static final int UDP_HEADER_LENGTH = 8; 45 46 // This should only be constructed via static factory methods, such as 47 // nattKeepalivePacket NattKeepalivePacketData(@onNull InetAddress srcAddress, int srcPort, @NonNull InetAddress dstAddress, int dstPort, @NonNull byte[] data)48 public NattKeepalivePacketData(@NonNull InetAddress srcAddress, int srcPort, 49 @NonNull InetAddress dstAddress, int dstPort, @NonNull byte[] data) throws 50 InvalidPacketException { 51 super(srcAddress, srcPort, dstAddress, dstPort, data); 52 } 53 54 /** 55 * Factory method to create Nat-T keepalive packet structure. 56 * @hide 57 */ nattKeepalivePacket( InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)58 public static NattKeepalivePacketData nattKeepalivePacket( 59 InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort) 60 throws InvalidPacketException { 61 if (dstPort != NattSocketKeepalive.NATT_PORT) { 62 throw new InvalidPacketException(ERROR_INVALID_PORT); 63 } 64 65 // 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) { 71 throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); 72 } 73 74 if (srcAddr instanceof Inet4Address && dstAddr instanceof Inet4Address) { 75 return nattKeepalivePacketv4( 76 (Inet4Address) srcAddr, srcPort, (Inet4Address) dstAddr, dstPort); 77 } else if (srcAddr instanceof Inet6Address && dstAddr instanceof Inet6Address) { 78 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 } 84 } 85 nattKeepalivePacketv4( Inet4Address srcAddress, int srcPort, Inet4Address dstAddress, int dstPort)86 private static NattKeepalivePacketData nattKeepalivePacketv4( 87 Inet4Address srcAddress, int srcPort, Inet4Address dstAddress, int dstPort) 88 throws InvalidPacketException { 89 int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1; 90 final ByteBuffer buf = ByteBuffer.allocate(length); 91 buf.order(ByteOrder.BIG_ENDIAN); 92 buf.putShort((short) 0x4500); // IP version and TOS 93 buf.putShort((short) length); 94 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 99 buf.put((byte) OsConstants.IPPROTO_UDP); 100 final int ipChecksumOffset = buf.position(); 101 buf.putShort((short) 0); // IP checksum 102 buf.put(srcAddress.getAddress()); 103 buf.put(dstAddress.getAddress()); 104 buf.putShort((short) srcPort); 105 buf.putShort((short) dstPort); 106 buf.putShort((short) (UDP_HEADER_LENGTH + 1)); // UDP length 107 final int udpChecksumOffset = buf.position(); 108 buf.putShort((short) 0); // UDP checksum 109 buf.put((byte) 0xff); // NAT-T keepalive 110 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 } 115 nattKeepalivePacketv6( Inet6Address srcAddress, int srcPort, Inet6Address dstAddress, int dstPort)116 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 } 140 /** Parcelable Implementation */ describeContents()141 public int describeContents() { 142 return 0; 143 } 144 145 /** Write to parcel */ writeToParcel(@onNull Parcel out, int flags)146 public void writeToParcel(@NonNull Parcel out, int flags) { 147 out.writeString(getSrcAddress().getHostAddress()); 148 out.writeString(getDstAddress().getHostAddress()); 149 out.writeInt(getSrcPort()); 150 out.writeInt(getDstPort()); 151 } 152 153 /** Parcelable Creator */ 154 public static final @NonNull Parcelable.Creator<NattKeepalivePacketData> CREATOR = 155 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( 168 "Invalid NAT-T keepalive data: " + e.getError()); 169 } 170 } 171 172 public NattKeepalivePacketData[] newArray(int size) { 173 return new NattKeepalivePacketData[size]; 174 } 175 }; 176 177 @Override equals(@ullable final Object o)178 public boolean equals(@Nullable final Object o) { 179 if (!(o instanceof NattKeepalivePacketData)) return false; 180 final NattKeepalivePacketData other = (NattKeepalivePacketData) o; 181 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(); 187 } 188 189 @Override hashCode()190 public int hashCode() { 191 return Objects.hash(getSrcAddress(), getDstAddress(), getSrcPort(), getDstPort()); 192 } 193 } 194