1 /*
2  * Copyright 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 com.android.net.module.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.RouteInfo;
22 
23 import java.net.Inet4Address;
24 import java.net.Inet6Address;
25 import java.net.InetAddress;
26 import java.net.UnknownHostException;
27 import java.util.Collection;
28 
29 /**
30  * Collection of network common utilities.
31  * @hide
32  */
33 public final class NetUtils {
34 
35     /**
36      * Check if IP address type is consistent between two InetAddress.
37      * @return true if both are the same type. False otherwise.
38      */
addressTypeMatches(@onNull InetAddress left, @NonNull InetAddress right)39     public static boolean addressTypeMatches(@NonNull InetAddress left,
40             @NonNull InetAddress right) {
41         return (((left instanceof Inet4Address) && (right instanceof Inet4Address))
42                 || ((left instanceof Inet6Address) && (right instanceof Inet6Address)));
43     }
44 
45     /**
46      * Find the route from a Collection of routes that best matches a given address.
47      * May return null if no routes are applicable, or the best route is not a route of
48      * {@link RouteInfo.RTN_UNICAST} type.
49      *
50      * @param routes a Collection of RouteInfos to chose from
51      * @param dest the InetAddress your trying to get to
52      * @return the RouteInfo from the Collection that best fits the given address
53      */
54     @Nullable
selectBestRoute(@ullable Collection<RouteInfo> routes, @Nullable InetAddress dest)55     public static RouteInfo selectBestRoute(@Nullable Collection<RouteInfo> routes,
56             @Nullable InetAddress dest) {
57         if ((routes == null) || (dest == null)) return null;
58 
59         RouteInfo bestRoute = null;
60 
61         // pick a longest prefix match under same address type
62         for (RouteInfo route : routes) {
63             if (addressTypeMatches(route.getDestination().getAddress(), dest)) {
64                 if ((bestRoute != null)
65                         && (bestRoute.getDestination().getPrefixLength()
66                         >= route.getDestination().getPrefixLength())) {
67                     continue;
68                 }
69                 if (route.matches(dest)) bestRoute = route;
70             }
71         }
72 
73         if (bestRoute != null && bestRoute.getType() == RouteInfo.RTN_UNICAST) {
74             return bestRoute;
75         } else {
76             return null;
77         }
78     }
79 
80     /**
81      * Get InetAddress masked with prefixLength.  Will never return null.
82      * @param address the IP address to mask with
83      * @param prefixLength the prefixLength used to mask the IP
84      */
getNetworkPart(InetAddress address, int prefixLength)85     public static InetAddress getNetworkPart(InetAddress address, int prefixLength) {
86         byte[] array = address.getAddress();
87         maskRawAddress(array, prefixLength);
88 
89         InetAddress netPart = null;
90         try {
91             netPart = InetAddress.getByAddress(array);
92         } catch (UnknownHostException e) {
93             throw new RuntimeException("getNetworkPart error - " + e.toString());
94         }
95         return netPart;
96     }
97 
98     /**
99      *  Masks a raw IP address byte array with the specified prefix length.
100      */
maskRawAddress(byte[] array, int prefixLength)101     public static void maskRawAddress(byte[] array, int prefixLength) {
102         if (prefixLength < 0 || prefixLength > array.length * 8) {
103             throw new RuntimeException("IP address with " + array.length
104                     + " bytes has invalid prefix length " + prefixLength);
105         }
106 
107         int offset = prefixLength / 8;
108         int remainder = prefixLength % 8;
109         byte mask = (byte) (0xFF << (8 - remainder));
110 
111         if (offset < array.length) array[offset] = (byte) (array[offset] & mask);
112 
113         offset++;
114 
115         for (; offset < array.length; offset++) {
116             array[offset] = 0;
117         }
118     }
119 }
120