1 /*
2  * Copyright (C) 2020 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 java.net.Inet4Address;
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 
23 /**
24  * Collection of utilities to work with IPv4 addresses.
25  * @hide
26  */
27 public class Inet4AddressUtils {
28 
29     /**
30      * Convert a IPv4 address from an integer to an InetAddress (0x04030201 -> 1.2.3.4)
31      *
32      * <p>This method uses the higher-order int bytes as the lower-order IPv4 address bytes,
33      * which is an unusual convention. Consider {@link #intToInet4AddressHTH(int)} instead.
34      * @param hostAddress an int coding for an IPv4 address, where higher-order int byte is
35      *                    lower-order IPv4 address byte
36      */
intToInet4AddressHTL(int hostAddress)37     public static Inet4Address intToInet4AddressHTL(int hostAddress) {
38         return intToInet4AddressHTH(Integer.reverseBytes(hostAddress));
39     }
40 
41     /**
42      * Convert a IPv4 address from an integer to an InetAddress (0x01020304 -> 1.2.3.4)
43      * @param hostAddress an int coding for an IPv4 address
44      */
intToInet4AddressHTH(int hostAddress)45     public static Inet4Address intToInet4AddressHTH(int hostAddress) {
46         byte[] addressBytes = { (byte) (0xff & (hostAddress >> 24)),
47                 (byte) (0xff & (hostAddress >> 16)),
48                 (byte) (0xff & (hostAddress >> 8)),
49                 (byte) (0xff & hostAddress) };
50 
51         try {
52             return (Inet4Address) InetAddress.getByAddress(addressBytes);
53         } catch (UnknownHostException e) {
54             throw new AssertionError();
55         }
56     }
57 
58     /**
59      * Convert an IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x01020304)
60      *
61      * <p>This conversion can help order IP addresses: considering the ordering
62      * 192.0.2.1 < 192.0.2.2 < ..., resulting ints will follow that ordering if read as unsigned
63      * integers with {@link Integer#toUnsignedLong}.
64      * @param inetAddr is an InetAddress corresponding to the IPv4 address
65      * @return the IP address as integer
66      */
inet4AddressToIntHTH(Inet4Address inetAddr)67     public static int inet4AddressToIntHTH(Inet4Address inetAddr)
68             throws IllegalArgumentException {
69         byte [] addr = inetAddr.getAddress();
70         return ((addr[0] & 0xff) << 24) | ((addr[1] & 0xff) << 16)
71                 | ((addr[2] & 0xff) << 8) | (addr[3] & 0xff);
72     }
73 
74     /**
75      * Convert a IPv4 address from an InetAddress to an integer (1.2.3.4 -> 0x04030201)
76      *
77      * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
78      * which is an unusual convention. Consider {@link #inet4AddressToIntHTH(Inet4Address)} instead.
79      * @param inetAddr is an InetAddress corresponding to the IPv4 address
80      * @return the IP address as integer
81      */
inet4AddressToIntHTL(Inet4Address inetAddr)82     public static int inet4AddressToIntHTL(Inet4Address inetAddr) {
83         return Integer.reverseBytes(inet4AddressToIntHTH(inetAddr));
84     }
85 
86     /**
87      * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0xffff8000)
88      * @return the IPv4 netmask as an integer
89      */
prefixLengthToV4NetmaskIntHTH(int prefixLength)90     public static int prefixLengthToV4NetmaskIntHTH(int prefixLength)
91             throws IllegalArgumentException {
92         if (prefixLength < 0 || prefixLength > 32) {
93             throw new IllegalArgumentException("Invalid prefix length (0 <= prefix <= 32)");
94         }
95         // (int)a << b is equivalent to a << (b & 0x1f): can't shift by 32 (-1 << 32 == -1)
96         return prefixLength == 0 ? 0 : 0xffffffff << (32 - prefixLength);
97     }
98 
99     /**
100      * Convert a network prefix length to an IPv4 netmask integer (prefixLength 17 -> 0x0080ffff).
101      *
102      * <p>This method stores the higher-order IPv4 address bytes in the lower-order int bytes,
103      * which is an unusual convention. Consider {@link #prefixLengthToV4NetmaskIntHTH(int)} instead.
104      * @return the IPv4 netmask as an integer
105      */
prefixLengthToV4NetmaskIntHTL(int prefixLength)106     public static int prefixLengthToV4NetmaskIntHTL(int prefixLength)
107             throws IllegalArgumentException {
108         return Integer.reverseBytes(prefixLengthToV4NetmaskIntHTH(prefixLength));
109     }
110 
111     /**
112      * Convert an IPv4 netmask to a prefix length, checking that the netmask is contiguous.
113      * @param netmask as a {@code Inet4Address}.
114      * @return the network prefix length
115      * @throws IllegalArgumentException the specified netmask was not contiguous.
116      * @hide
117      */
netmaskToPrefixLength(Inet4Address netmask)118     public static int netmaskToPrefixLength(Inet4Address netmask) {
119         // inetAddressToInt returns an int in *network* byte order.
120         int i = inet4AddressToIntHTH(netmask);
121         int prefixLength = Integer.bitCount(i);
122         int trailingZeros = Integer.numberOfTrailingZeros(i);
123         if (trailingZeros != 32 - prefixLength) {
124             throw new IllegalArgumentException("Non-contiguous netmask: " + Integer.toHexString(i));
125         }
126         return prefixLength;
127     }
128 
129     /**
130      * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
131      */
getImplicitNetmask(Inet4Address address)132     public static int getImplicitNetmask(Inet4Address address) {
133         int firstByte = address.getAddress()[0] & 0xff;  // Convert to an unsigned value.
134         if (firstByte < 128) {
135             return 8;
136         } else if (firstByte < 192) {
137             return 16;
138         } else if (firstByte < 224) {
139             return 24;
140         } else {
141             return 32;  // Will likely not end well for other reasons.
142         }
143     }
144 
145     /**
146      * Get the broadcast address for a given prefix.
147      *
148      * <p>For example 192.168.0.1/24 -> 192.168.0.255
149      */
getBroadcastAddress(Inet4Address addr, int prefixLength)150     public static Inet4Address getBroadcastAddress(Inet4Address addr, int prefixLength)
151             throws IllegalArgumentException {
152         final int intBroadcastAddr = inet4AddressToIntHTH(addr)
153                 | ~prefixLengthToV4NetmaskIntHTH(prefixLength);
154         return intToInet4AddressHTH(intBroadcastAddr);
155     }
156 
157     /**
158      * Get a prefix mask as Inet4Address for a given prefix length.
159      *
160      * <p>For example 20 -> 255.255.240.0
161      */
getPrefixMaskAsInet4Address(int prefixLength)162     public static Inet4Address getPrefixMaskAsInet4Address(int prefixLength)
163             throws IllegalArgumentException {
164         return intToInet4AddressHTH(prefixLengthToV4NetmaskIntHTH(prefixLength));
165     }
166 }
167