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