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