1 /* 2 * Copyright (C) 2015 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 com.android.net.module.util; 18 19 import com.android.internal.annotations.VisibleForTesting; 20 21 import static android.system.OsConstants.IPPROTO_ICMPV6; 22 import static android.system.OsConstants.IPPROTO_TCP; 23 import static android.system.OsConstants.IPPROTO_UDP; 24 25 import java.net.Inet6Address; 26 import java.net.InetAddress; 27 import java.nio.ByteBuffer; 28 import java.nio.ShortBuffer; 29 30 /** 31 * @hide 32 */ 33 public class IpUtils { 34 /** 35 * Converts a signed short value to an unsigned int value. Needed 36 * because Java does not have unsigned types. 37 */ intAbs(short v)38 private static int intAbs(short v) { 39 return v & 0xFFFF; 40 } 41 42 /** 43 * Performs an IP checksum (used in IP header and across UDP 44 * payload) or ICMP checksum on the specified portion of a ByteBuffer. The seed 45 * allows the checksum to commence with a specified value. 46 */ 47 @VisibleForTesting checksum(ByteBuffer buf, int seed, int start, int end)48 public static int checksum(ByteBuffer buf, int seed, int start, int end) { 49 int sum = seed + 0xFFFF; // to make things work with empty / zero-filled buffer 50 final int bufPosition = buf.position(); 51 52 // set position of original ByteBuffer, so that the ShortBuffer 53 // will be correctly initialized 54 buf.position(start); 55 ShortBuffer shortBuf = buf.asShortBuffer(); 56 57 // re-set ByteBuffer position 58 buf.position(bufPosition); 59 60 final int numShorts = (end - start) / 2; 61 for (int i = 0; i < numShorts; i++) { 62 sum += intAbs(shortBuf.get(i)); 63 } 64 start += numShorts * 2; 65 66 // see if a singleton byte remains 67 if (end != start) { 68 short b = buf.get(start); 69 70 // make it unsigned 71 if (b < 0) { 72 b += 256; 73 } 74 75 sum += b * 256; // assumes bytebuffer is network order (ie. big endian) 76 } 77 78 sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF); // max sum is 0x1FFFE 79 sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF); // max sum is 0xFFFF 80 return sum ^ 0xFFFF; // u16 bitwise negation 81 } 82 pseudoChecksumIPv4( ByteBuffer buf, int headerOffset, int protocol, int transportLen)83 private static int pseudoChecksumIPv4( 84 ByteBuffer buf, int headerOffset, int protocol, int transportLen) { 85 int partial = protocol + transportLen; 86 partial += intAbs(buf.getShort(headerOffset + 12)); 87 partial += intAbs(buf.getShort(headerOffset + 14)); 88 partial += intAbs(buf.getShort(headerOffset + 16)); 89 partial += intAbs(buf.getShort(headerOffset + 18)); 90 return partial; 91 } 92 pseudoChecksumIPv6( ByteBuffer buf, int headerOffset, int protocol, int transportLen)93 private static int pseudoChecksumIPv6( 94 ByteBuffer buf, int headerOffset, int protocol, int transportLen) { 95 int partial = protocol + transportLen; 96 for (int offset = 8; offset < 40; offset += 2) { 97 partial += intAbs(buf.getShort(headerOffset + offset)); 98 } 99 return partial; 100 } 101 ipversion(ByteBuffer buf, int headerOffset)102 private static byte ipversion(ByteBuffer buf, int headerOffset) { 103 return (byte) ((buf.get(headerOffset) & (byte) 0xf0) >> 4); 104 } 105 ipChecksum(ByteBuffer buf, int headerOffset)106 public static short ipChecksum(ByteBuffer buf, int headerOffset) { 107 byte ihl = (byte) (buf.get(headerOffset) & 0x0f); 108 return (short) checksum(buf, 0, headerOffset, headerOffset + ihl * 4); 109 } 110 transportChecksum(ByteBuffer buf, int protocol, int ipOffset, int transportOffset, int transportLen)111 private static short transportChecksum(ByteBuffer buf, int protocol, 112 int ipOffset, int transportOffset, int transportLen) { 113 if (transportLen < 0) { 114 throw new IllegalArgumentException("Transport length < 0: " + transportLen); 115 } 116 int sum; 117 byte ver = ipversion(buf, ipOffset); 118 if (ver == 4) { 119 sum = pseudoChecksumIPv4(buf, ipOffset, protocol, transportLen); 120 } else if (ver == 6) { 121 sum = pseudoChecksumIPv6(buf, ipOffset, protocol, transportLen); 122 } else { 123 throw new UnsupportedOperationException("Checksum must be IPv4 or IPv6"); 124 } 125 126 sum = checksum(buf, sum, transportOffset, transportOffset + transportLen); 127 if (protocol == IPPROTO_UDP && sum == 0) { 128 sum = (short) 0xffff; 129 } 130 return (short) sum; 131 } 132 133 /** 134 * Calculate the UDP checksum for an UDP packet. 135 */ udpChecksum(ByteBuffer buf, int ipOffset, int transportOffset)136 public static short udpChecksum(ByteBuffer buf, int ipOffset, int transportOffset) { 137 int transportLen = intAbs(buf.getShort(transportOffset + 4)); 138 return transportChecksum(buf, IPPROTO_UDP, ipOffset, transportOffset, transportLen); 139 } 140 141 /** 142 * Calculate the TCP checksum for a TCP packet. 143 */ tcpChecksum(ByteBuffer buf, int ipOffset, int transportOffset, int transportLen)144 public static short tcpChecksum(ByteBuffer buf, int ipOffset, int transportOffset, 145 int transportLen) { 146 return transportChecksum(buf, IPPROTO_TCP, ipOffset, transportOffset, transportLen); 147 } 148 149 /** 150 * Calculate the ICMP checksum for an ICMPv4 packet. 151 */ icmpChecksum(ByteBuffer buf, int transportOffset, int transportLen)152 public static short icmpChecksum(ByteBuffer buf, int transportOffset, int transportLen) { 153 // ICMP checksum doesn't include pseudo-header. See RFC 792. 154 return (short) checksum(buf, 0, transportOffset, transportOffset + transportLen); 155 } 156 157 /** 158 * Calculate the ICMPv6 checksum for an ICMPv6 packet. 159 */ icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset, int transportLen)160 public static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset, 161 int transportLen) { 162 return transportChecksum(buf, IPPROTO_ICMPV6, ipOffset, transportOffset, transportLen); 163 } 164 addressAndPortToString(InetAddress address, int port)165 public static String addressAndPortToString(InetAddress address, int port) { 166 return String.format( 167 (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d", 168 address.getHostAddress(), port); 169 } 170 isValidUdpOrTcpPort(int port)171 public static boolean isValidUdpOrTcpPort(int port) { 172 return port > 0 && port < 65536; 173 } 174 } 175