1 /* 2 * Copyright (C) 2018 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.dhcp; 18 19 import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address; 20 import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; 21 22 import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE; 23 import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU; 24 import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU; 25 26 import static java.lang.Integer.toUnsignedLong; 27 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.net.IpPrefix; 31 import android.net.LinkAddress; 32 import android.net.shared.Inet4AddressUtils; 33 import android.util.ArraySet; 34 35 import java.net.Inet4Address; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.HashSet; 39 import java.util.Set; 40 41 /** 42 * Parameters used by the DhcpServer to serve requests. 43 * 44 * <p>Instances are immutable. Use {@link DhcpServingParams.Builder} to instantiate. 45 * @hide 46 */ 47 public class DhcpServingParams { 48 public static final int MTU_UNSET = 0; 49 public static final int MIN_PREFIX_LENGTH = 16; 50 public static final int MAX_PREFIX_LENGTH = 30; 51 52 /** Server inet address and prefix to serve */ 53 @NonNull 54 public final LinkAddress serverAddr; 55 56 /** 57 * Default routers to be advertised to DHCP clients. May be empty. 58 * This set is provided by {@link DhcpServingParams.Builder} and is immutable. 59 */ 60 @NonNull 61 public final Set<Inet4Address> defaultRouters; 62 63 /** 64 * DNS servers to be advertised to DHCP clients. May be empty. 65 * This set is provided by {@link DhcpServingParams.Builder} and is immutable. 66 */ 67 @NonNull 68 public final Set<Inet4Address> dnsServers; 69 70 /** 71 * Excluded addresses that the DHCP server is not allowed to assign to clients. 72 * This set is provided by {@link DhcpServingParams.Builder} and is immutable. 73 */ 74 @NonNull 75 public final Set<Inet4Address> excludedAddrs; 76 77 // DHCP uses uint32. Use long for clearer code, and check range when building. 78 public final long dhcpLeaseTimeSecs; 79 public final int linkMtu; 80 81 /** 82 * Indicates whether the DHCP server should send the ANDROID_METERED vendor-specific option. 83 */ 84 public final boolean metered; 85 86 /** 87 * Checked exception thrown when some parameters used to build {@link DhcpServingParams} are 88 * missing or invalid. 89 */ 90 public static class InvalidParameterException extends Exception { InvalidParameterException(String message)91 public InvalidParameterException(String message) { 92 super(message); 93 } 94 } 95 DhcpServingParams(@onNull LinkAddress serverAddr, @NonNull Set<Inet4Address> defaultRouters, @NonNull Set<Inet4Address> dnsServers, @NonNull Set<Inet4Address> excludedAddrs, long dhcpLeaseTimeSecs, int linkMtu, boolean metered)96 private DhcpServingParams(@NonNull LinkAddress serverAddr, 97 @NonNull Set<Inet4Address> defaultRouters, 98 @NonNull Set<Inet4Address> dnsServers, @NonNull Set<Inet4Address> excludedAddrs, 99 long dhcpLeaseTimeSecs, int linkMtu, boolean metered) { 100 this.serverAddr = serverAddr; 101 this.defaultRouters = defaultRouters; 102 this.dnsServers = dnsServers; 103 this.excludedAddrs = excludedAddrs; 104 this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; 105 this.linkMtu = linkMtu; 106 this.metered = metered; 107 } 108 109 /** 110 * Create parameters from a stable AIDL-compatible parcel. 111 * @throws InvalidParameterException The parameters parcelable is null or invalid. 112 */ fromParcelableObject(@ullable DhcpServingParamsParcel parcel)113 public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel) 114 throws InvalidParameterException { 115 if (parcel == null) { 116 throw new InvalidParameterException("Null serving parameters"); 117 } 118 final LinkAddress serverAddr = new LinkAddress( 119 intToInet4AddressHTH(parcel.serverAddr), 120 parcel.serverAddrPrefixLength); 121 return new Builder() 122 .setServerAddr(serverAddr) 123 .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters)) 124 .setDnsServers(toInet4AddressSet(parcel.dnsServers)) 125 .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs)) 126 .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs) 127 .setLinkMtu(parcel.linkMtu) 128 .setMetered(parcel.metered) 129 .build(); 130 } 131 toInet4AddressSet(@ullable int[] addrs)132 private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) { 133 if (addrs == null) { 134 return new HashSet<>(0); 135 } 136 137 final HashSet<Inet4Address> res = new HashSet<>(); 138 for (int addr : addrs) { 139 res.add(intToInet4AddressHTH(addr)); 140 } 141 return res; 142 } 143 144 @NonNull getServerInet4Addr()145 public Inet4Address getServerInet4Addr() { 146 return (Inet4Address) serverAddr.getAddress(); 147 } 148 149 /** 150 * Get the served prefix mask as an IPv4 address. 151 * 152 * <p>For example, if the served prefix is 192.168.42.0/24, this will return 255.255.255.0. 153 */ 154 @NonNull getPrefixMaskAsAddress()155 public Inet4Address getPrefixMaskAsAddress() { 156 return getPrefixMaskAsInet4Address(serverAddr.getPrefixLength()); 157 } 158 159 /** 160 * Get the server broadcast address. 161 * 162 * <p>For example, if the server {@link LinkAddress} is 192.168.42.1/24, this will return 163 * 192.168.42.255. 164 */ 165 @NonNull getBroadcastAddress()166 public Inet4Address getBroadcastAddress() { 167 return Inet4AddressUtils.getBroadcastAddress( 168 getServerInet4Addr(), serverAddr.getPrefixLength()); 169 } 170 171 /** 172 * Utility class to create new instances of {@link DhcpServingParams} while checking validity 173 * of the parameters. 174 */ 175 public static class Builder { 176 private LinkAddress mServerAddr; 177 private Set<Inet4Address> mDefaultRouters; 178 private Set<Inet4Address> mDnsServers; 179 private Set<Inet4Address> mExcludedAddrs; 180 private long mDhcpLeaseTimeSecs; 181 private int mLinkMtu = MTU_UNSET; 182 private boolean mMetered; 183 184 /** 185 * Set the server address and served prefix for the DHCP server. 186 * 187 * <p>This parameter is required. 188 */ setServerAddr(@onNull LinkAddress serverAddr)189 public Builder setServerAddr(@NonNull LinkAddress serverAddr) { 190 this.mServerAddr = serverAddr; 191 return this; 192 } 193 194 /** 195 * Set the default routers to be advertised to DHCP clients. 196 * 197 * <p>Each router must be inside the served prefix. This may be an empty set, but it must 198 * always be set explicitly before building the {@link DhcpServingParams}. 199 */ setDefaultRouters(@onNull Set<Inet4Address> defaultRouters)200 public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) { 201 this.mDefaultRouters = defaultRouters; 202 return this; 203 } 204 205 /** 206 * Set the default routers to be advertised to DHCP clients. 207 * 208 * <p>Each router must be inside the served prefix. This may be an empty list of routers, 209 * but it must always be set explicitly before building the {@link DhcpServingParams}. 210 */ setDefaultRouters(@onNull Inet4Address... defaultRouters)211 public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) { 212 return setDefaultRouters(makeArraySet(defaultRouters)); 213 } 214 215 /** 216 * Convenience method to build the parameters with no default router. 217 * 218 * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address. 219 */ withNoDefaultRouter()220 public Builder withNoDefaultRouter() { 221 return setDefaultRouters(); 222 } 223 224 /** 225 * Set the DNS servers to be advertised to DHCP clients. 226 * 227 * <p>This may be an empty set, but it must always be set explicitly before building the 228 * {@link DhcpServingParams}. 229 */ setDnsServers(@onNull Set<Inet4Address> dnsServers)230 public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) { 231 this.mDnsServers = dnsServers; 232 return this; 233 } 234 235 /** 236 * Set the DNS servers to be advertised to DHCP clients. 237 * 238 * <p>This may be an empty list of servers, but it must always be set explicitly before 239 * building the {@link DhcpServingParams}. 240 */ setDnsServers(@onNull Inet4Address... dnsServers)241 public Builder setDnsServers(@NonNull Inet4Address... dnsServers) { 242 return setDnsServers(makeArraySet(dnsServers)); 243 } 244 245 /** 246 * Convenience method to build the parameters with no DNS server. 247 * 248 * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address. 249 */ withNoDnsServer()250 public Builder withNoDnsServer() { 251 return setDnsServers(); 252 } 253 254 /** 255 * Set excluded addresses that the DHCP server is not allowed to assign to clients. 256 * 257 * <p>This parameter is optional. DNS servers and default routers are always excluded 258 * and do not need to be set here. 259 */ setExcludedAddrs(@onNull Set<Inet4Address> excludedAddrs)260 public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) { 261 this.mExcludedAddrs = excludedAddrs; 262 return this; 263 } 264 265 /** 266 * Set excluded addresses that the DHCP server is not allowed to assign to clients. 267 * 268 * <p>This parameter is optional. DNS servers and default routers are always excluded 269 * and do not need to be set here. 270 */ setExcludedAddrs(@onNull Inet4Address... excludedAddrs)271 public Builder setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { 272 return setExcludedAddrs(makeArraySet(excludedAddrs)); 273 } 274 275 /** 276 * Set the lease time for leases assigned by the DHCP server. 277 * 278 * <p>This parameter is required. 279 */ setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs)280 public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { 281 this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs; 282 return this; 283 } 284 285 /** 286 * Set the link MTU to be advertised to DHCP clients. 287 * 288 * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter 289 * is optional and defaults to {@link #MTU_UNSET}. 290 */ setLinkMtu(int linkMtu)291 public Builder setLinkMtu(int linkMtu) { 292 this.mLinkMtu = linkMtu; 293 return this; 294 } 295 296 /** 297 * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option. 298 * 299 * <p>If not set, the default value is false. 300 */ setMetered(boolean metered)301 public Builder setMetered(boolean metered) { 302 this.mMetered = metered; 303 return this; 304 } 305 306 /** 307 * Create a new {@link DhcpServingParams} instance based on parameters set in the builder. 308 * 309 * <p>This method has no side-effects. If it does not throw, a valid 310 * {@link DhcpServingParams} is returned. 311 * @return The constructed parameters. 312 * @throws InvalidParameterException At least one parameter is missing or invalid. 313 */ 314 @NonNull build()315 public DhcpServingParams build() throws InvalidParameterException { 316 if (mServerAddr == null) { 317 throw new InvalidParameterException("Missing serverAddr"); 318 } 319 if (mDefaultRouters == null) { 320 throw new InvalidParameterException("Missing defaultRouters"); 321 } 322 if (mDnsServers == null) { 323 // Empty set is OK, but enforce explicitly setting it 324 throw new InvalidParameterException("Missing dnsServers"); 325 } 326 if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) { 327 throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs); 328 } 329 if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) { 330 throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu); 331 } 332 if (!mServerAddr.isIpv4()) { 333 throw new InvalidParameterException("serverAddr must be IPv4"); 334 } 335 if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH 336 || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) { 337 throw new InvalidParameterException("Prefix length is not in supported range"); 338 } 339 340 final IpPrefix prefix = makeIpPrefix(mServerAddr); 341 for (Inet4Address addr : mDefaultRouters) { 342 if (!prefix.contains(addr)) { 343 throw new InvalidParameterException(String.format( 344 "Default router %s is not in server prefix %s", addr, mServerAddr)); 345 } 346 } 347 348 final Set<Inet4Address> excl = new HashSet<>(); 349 if (mExcludedAddrs != null) { 350 excl.addAll(mExcludedAddrs); 351 } 352 excl.add((Inet4Address) mServerAddr.getAddress()); 353 excl.addAll(mDefaultRouters); 354 excl.addAll(mDnsServers); 355 356 return new DhcpServingParams(mServerAddr, 357 Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)), 358 Collections.unmodifiableSet(new HashSet<>(mDnsServers)), 359 Collections.unmodifiableSet(excl), 360 mDhcpLeaseTimeSecs, mLinkMtu, mMetered); 361 } 362 } 363 364 /** 365 * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress. 366 */ 367 @NonNull makeIpPrefix(@onNull LinkAddress addr)368 static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) { 369 return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); 370 } 371 makeArraySet(T[] elements)372 private static <T> ArraySet<T> makeArraySet(T[] elements) { 373 final ArraySet<T> set = new ArraySet<>(elements.length); 374 set.addAll(Arrays.asList(elements)); 375 return set; 376 } 377 } 378