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