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.server.util.NetworkStackConstants.INFINITE_LEASE;
22 import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
23 import static com.android.server.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