1 /*
2  * Copyright (C) 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 android.net.ip;
18 
19 import static android.net.INetd.IF_STATE_DOWN;
20 import static android.net.INetd.IF_STATE_UP;
21 
22 import android.net.INetd;
23 import android.net.InterfaceConfigurationParcel;
24 import android.net.LinkAddress;
25 import android.net.util.SharedLog;
26 import android.os.RemoteException;
27 import android.os.ServiceSpecificException;
28 import android.system.OsConstants;
29 
30 import java.net.Inet4Address;
31 import java.net.InetAddress;
32 
33 
34 /**
35  * Encapsulates the multiple IP configuration operations performed on an interface.
36  *
37  * TODO: refactor/eliminate the redundant ways to set and clear addresses.
38  *
39  * @hide
40  */
41 public class InterfaceController {
42     private final static boolean DBG = false;
43 
44     private final String mIfName;
45     private final INetd mNetd;
46     private final SharedLog mLog;
47 
InterfaceController(String ifname, INetd netd, SharedLog log)48     public InterfaceController(String ifname, INetd netd, SharedLog log) {
49         mIfName = ifname;
50         mNetd = netd;
51         mLog = log;
52     }
53 
54     /**
55      * Set the IPv4 address and also optionally bring the interface up or down.
56      */
setInterfaceConfiguration(final LinkAddress ipv4Addr, final Boolean setIfaceUp)57     public boolean setInterfaceConfiguration(final LinkAddress ipv4Addr,
58             final Boolean setIfaceUp) {
59         if (!(ipv4Addr.getAddress() instanceof Inet4Address)) {
60             throw new IllegalArgumentException("Invalid or mismatched Inet4Address");
61         }
62         // Note: currently netd only support INetd#IF_STATE_UP and #IF_STATE_DOWN.
63         // Other flags would be ignored.
64 
65         final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel();
66         ifConfig.ifName = mIfName;
67         ifConfig.ipv4Addr = ipv4Addr.getAddress().getHostAddress();
68         ifConfig.prefixLength = ipv4Addr.getPrefixLength();
69         // Netd ignores hwaddr in interfaceSetCfg.
70         ifConfig.hwAddr = "";
71         if (setIfaceUp == null) {
72             // Empty array means no change.
73             ifConfig.flags = new String[0];
74         } else {
75             // Netd ignores any flag that's not IF_STATE_UP or IF_STATE_DOWN in interfaceSetCfg.
76             ifConfig.flags = setIfaceUp.booleanValue()
77                     ? new String[] {IF_STATE_UP} : new String[] {IF_STATE_DOWN};
78         }
79         try {
80             mNetd.interfaceSetCfg(ifConfig);
81         } catch (RemoteException | ServiceSpecificException e) {
82             logError("Setting IPv4 address to %s/%d failed: %s",
83                     ifConfig.ipv4Addr, ifConfig.prefixLength, e);
84             return false;
85         }
86         return true;
87     }
88 
89     /**
90      * Set the IPv4 address of the interface.
91      */
setIPv4Address(final LinkAddress address)92     public boolean setIPv4Address(final LinkAddress address) {
93         return setInterfaceConfiguration(address, null);
94     }
95 
96     /**
97      * Clear the IPv4Address of the interface.
98      */
clearIPv4Address()99     public boolean clearIPv4Address() {
100         return setIPv4Address(new LinkAddress("0.0.0.0/0"));
101     }
102 
setEnableIPv6(boolean enabled)103     private boolean setEnableIPv6(boolean enabled) {
104         try {
105             mNetd.interfaceSetEnableIPv6(mIfName, enabled);
106         } catch (RemoteException | ServiceSpecificException e) {
107             logError("%s IPv6 failed: %s", (enabled ? "enabling" : "disabling"), e);
108             return false;
109         }
110         return true;
111     }
112 
113     /**
114      * Enable IPv6 on the interface.
115      */
enableIPv6()116     public boolean enableIPv6() {
117         return setEnableIPv6(true);
118     }
119 
120     /**
121      * Disable IPv6 on the interface.
122      */
disableIPv6()123     public boolean disableIPv6() {
124         return setEnableIPv6(false);
125     }
126 
127     /**
128      * Enable or disable IPv6 privacy extensions on the interface.
129      * @param enabled Whether the extensions should be enabled.
130      */
setIPv6PrivacyExtensions(boolean enabled)131     public boolean setIPv6PrivacyExtensions(boolean enabled) {
132         try {
133             mNetd.interfaceSetIPv6PrivacyExtensions(mIfName, enabled);
134         } catch (RemoteException | ServiceSpecificException e) {
135             logError("error %s IPv6 privacy extensions: %s",
136                     (enabled ? "enabling" : "disabling"), e);
137             return false;
138         }
139         return true;
140     }
141 
142     /**
143      * Set IPv6 address generation mode on the interface.
144      *
145      * <p>IPv6 should be disabled before changing the mode.
146      */
setIPv6AddrGenModeIfSupported(int mode)147     public boolean setIPv6AddrGenModeIfSupported(int mode) {
148         try {
149             mNetd.setIPv6AddrGenMode(mIfName, mode);
150         } catch (RemoteException e) {
151             logError("Unable to set IPv6 addrgen mode: %s", e);
152             return false;
153         } catch (ServiceSpecificException e) {
154             if (e.errorCode != OsConstants.EOPNOTSUPP) {
155                 logError("Unable to set IPv6 addrgen mode: %s", e);
156                 return false;
157             }
158         }
159         return true;
160     }
161 
162     /**
163      * Add an address to the interface.
164      */
addAddress(LinkAddress addr)165     public boolean addAddress(LinkAddress addr) {
166         return addAddress(addr.getAddress(), addr.getPrefixLength());
167     }
168 
169     /**
170      * Add an address to the interface.
171      */
addAddress(InetAddress ip, int prefixLen)172     public boolean addAddress(InetAddress ip, int prefixLen) {
173         try {
174             mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen);
175         } catch (ServiceSpecificException | RemoteException e) {
176             logError("failed to add %s/%d: %s", ip, prefixLen, e);
177             return false;
178         }
179         return true;
180     }
181 
182     /**
183      * Remove an address from the interface.
184      */
removeAddress(InetAddress ip, int prefixLen)185     public boolean removeAddress(InetAddress ip, int prefixLen) {
186         try {
187             mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen);
188         } catch (ServiceSpecificException | RemoteException e) {
189             logError("failed to remove %s/%d: %s", ip, prefixLen, e);
190             return false;
191         }
192         return true;
193     }
194 
195     /**
196      * Remove all addresses from the interface.
197      */
clearAllAddresses()198     public boolean clearAllAddresses() {
199         try {
200             mNetd.interfaceClearAddrs(mIfName);
201         } catch (Exception e) {
202             logError("Failed to clear addresses: %s", e);
203             return false;
204         }
205         return true;
206     }
207 
logError(String fmt, Object... args)208     private void logError(String fmt, Object... args) {
209         mLog.e(String.format(fmt, args));
210     }
211 }
212