blob: 3bc0e706c70f8bfa77b5d168e3d424c97dc09af1 [file] [log] [blame]
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -05001/*
2 * Copyright (C) 2014 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
Sreeram Ramachandran72604072014-05-21 13:19:43 -070017// THREAD-SAFETY
18// -------------
19// The methods in this file are called from multiple threads (from CommandListener, FwmarkServer
20// and DnsProxyListener). So, all accesses to shared state are guarded by a lock.
21//
22// In some cases, a single non-const method acquires and releases the lock several times, like so:
23// if (isValidNetwork(...)) { // isValidNetwork() acquires and releases the lock.
24// setDefaultNetwork(...); // setDefaultNetwork() also acquires and releases the lock.
25//
26// It might seem that this allows races where the state changes between the two statements, but in
27// fact there are no races because:
28// 1. This pattern only occurs in non-const methods (i.e., those that mutate state).
29// 2. Only CommandListener calls these non-const methods. The others call only const methods.
30// 3. CommandListener only processes one command at a time. I.e., it's serialized.
31// Thus, no other mutation can occur in between the two statements above.
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -050032
33#include "NetworkController.h"
34
Sreeram Ramachandran6a773532014-07-11 09:10:20 -070035#include "LocalNetwork.h"
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070036#include "PhysicalNetwork.h"
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070037#include "RouteController.h"
Sreeram Ramachandran4043f012014-06-23 12:41:37 -070038#include "VirtualNetwork.h"
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070039
Sreeram Ramachandraned4bd1f2014-07-05 12:31:05 -070040#include "cutils/misc.h"
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070041#define LOG_TAG "Netd"
42#include "log/log.h"
Sreeram Ramachandran72604072014-05-21 13:19:43 -070043#include "resolv_netid.h"
44
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070045namespace {
46
47// Keep these in sync with ConnectivityService.java.
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070048const unsigned MIN_NET_ID = 10;
49const unsigned MAX_NET_ID = 65535;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070050
51} // namespace
52
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070053NetworkController::NetworkController() : mDefaultNetId(NETID_UNSET) {
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -050054}
55
56unsigned NetworkController::getDefaultNetwork() const {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070057 android::RWLock::AutoRLock lock(mRWLock);
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -050058 return mDefaultNetId;
59}
60
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070061int NetworkController::setDefaultNetwork(unsigned netId) {
62 android::RWLock::AutoWLock lock(mRWLock);
63
64 if (netId == mDefaultNetId) {
65 return 0;
Sreeram Ramachandran72604072014-05-21 13:19:43 -070066 }
67
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070068 if (netId != NETID_UNSET) {
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -070069 Network* network = getNetworkLocked(netId);
70 if (!network || network->getType() != Network::PHYSICAL) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070071 ALOGE("invalid netId %u", netId);
72 return -EINVAL;
73 }
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -070074 if (int ret = static_cast<PhysicalNetwork*>(network)->addAsDefault()) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070075 return ret;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070076 }
77 }
78
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070079 if (mDefaultNetId != NETID_UNSET) {
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -070080 Network* network = getNetworkLocked(mDefaultNetId);
81 if (!network || network->getType() != Network::PHYSICAL) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070082 ALOGE("cannot find previously set default network with netId %u", mDefaultNetId);
83 return -ESRCH;
84 }
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -070085 if (int ret = static_cast<PhysicalNetwork*>(network)->removeAsDefault()) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070086 return ret;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070087 }
88 }
89
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -070090 mDefaultNetId = netId;
91 return 0;
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -050092}
93
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -070094unsigned NetworkController::getNetworkForUser(uid_t uid, unsigned requestedNetId,
95 bool forDns) const {
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -050096 android::RWLock::AutoRLock lock(mRWLock);
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -070097 VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
98 if (virtualNetwork && (!forDns || virtualNetwork->getHasDns())) {
99 return virtualNetwork->getNetId();
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -0500100 }
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700101 return getNetworkLocked(requestedNetId) ? requestedNetId : mDefaultNetId;
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -0500102}
103
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700104unsigned NetworkController::getNetworkForInterface(const char* interface) const {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700105 android::RWLock::AutoRLock lock(mRWLock);
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700106 for (const auto& entry : mNetworks) {
Sreeram Ramachandran4043f012014-06-23 12:41:37 -0700107 if (entry.second->hasInterface(interface)) {
108 return entry.first;
109 }
110 }
Paul Jensen35c77e32014-04-10 14:57:54 -0400111 return NETID_UNSET;
Szymon Jakubczaka0efaec2014-02-14 17:09:43 -0500112}
113
Sreeram Ramachandran6a773532014-07-11 09:10:20 -0700114unsigned NetworkController::getNetIdForLocalNetwork() const {
115 return MIN_NET_ID - 1;
116}
117
118int NetworkController::createLocalNetwork(unsigned netId) {
119 // TODO: Enable this check after removing the getNetIdForLocalNetwork() hack.
120 if (false) {
121 if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
122 ALOGE("invalid netId %u", netId);
123 return -EINVAL;
124 }
125 }
126
127 if (isValidNetwork(netId)) {
128 ALOGE("duplicate netId %u", netId);
129 return -EEXIST;
130 }
131
132 android::RWLock::AutoWLock lock(mRWLock);
133 mNetworks[netId] = new LocalNetwork(netId);
134 return 0;
135}
136
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700137int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission) {
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700138 if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
Paul Jensenae37e8a2014-04-28 10:35:51 -0400139 ALOGE("invalid netId %u", netId);
Lorenzo Colitti96f261e2014-06-23 15:09:54 +0900140 return -EINVAL;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700141 }
142
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700143 if (isValidNetwork(netId)) {
144 ALOGE("duplicate netId %u", netId);
145 return -EEXIST;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700146 }
147
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700148 PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId);
149 if (int ret = physicalNetwork->setPermission(permission)) {
150 ALOGE("inconceivable! setPermission cannot fail on an empty network");
151 delete physicalNetwork;
Lorenzo Colitti96f261e2014-06-23 15:09:54 +0900152 return ret;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700153 }
154
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700155 android::RWLock::AutoWLock lock(mRWLock);
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700156 mNetworks[netId] = physicalNetwork;
Lorenzo Colitti96f261e2014-06-23 15:09:54 +0900157 return 0;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700158}
159
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700160int NetworkController::createVirtualNetwork(unsigned netId, bool hasDns) {
Sreeram Ramachandran4043f012014-06-23 12:41:37 -0700161 if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
162 ALOGE("invalid netId %u", netId);
163 return -EINVAL;
164 }
165
166 if (isValidNetwork(netId)) {
167 ALOGE("duplicate netId %u", netId);
168 return -EEXIST;
169 }
170
171 android::RWLock::AutoWLock lock(mRWLock);
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700172 mNetworks[netId] = new VirtualNetwork(netId, hasDns);
Sreeram Ramachandran4043f012014-06-23 12:41:37 -0700173 return 0;
174}
175
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700176int NetworkController::destroyNetwork(unsigned netId) {
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700177 if (!isValidNetwork(netId)) {
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700178 ALOGE("invalid netId %u", netId);
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700179 return -EINVAL;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700180 }
181
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700182 // TODO: ioctl(SIOCKILLADDR, ...) to kill all sockets on the old network.
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700183
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700184 android::RWLock::AutoWLock lock(mRWLock);
185 Network* network = getNetworkLocked(netId);
186 if (int ret = network->clearInterfaces()) {
187 return ret;
188 }
189 if (mDefaultNetId == netId) {
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700190 if (int ret = static_cast<PhysicalNetwork*>(network)->removeAsDefault()) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700191 ALOGE("inconceivable! removeAsDefault cannot fail on an empty network");
192 return ret;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700193 }
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700194 mDefaultNetId = NETID_UNSET;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700195 }
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700196 mNetworks.erase(netId);
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700197 delete network;
Sreeram Ramachandran3ced0692014-04-18 09:49:13 -0700198 _resolv_delete_cache_for_net(netId);
Lorenzo Colitti96f261e2014-06-23 15:09:54 +0900199 return 0;
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700200}
201
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700202int NetworkController::addInterfaceToNetwork(unsigned netId, const char* interface) {
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700203 if (!isValidNetwork(netId)) {
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700204 ALOGE("invalid netId %u", netId);
Lorenzo Colittif7fc8ec2014-06-18 00:41:58 +0900205 return -EINVAL;
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700206 }
207
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700208 unsigned existingNetId = getNetworkForInterface(interface);
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700209 if (existingNetId != NETID_UNSET && existingNetId != netId) {
210 ALOGE("interface %s already assigned to netId %u", interface, existingNetId);
211 return -EBUSY;
212 }
213
214 android::RWLock::AutoWLock lock(mRWLock);
215 return getNetworkLocked(netId)->addInterface(interface);
216}
217
218int NetworkController::removeInterfaceFromNetwork(unsigned netId, const char* interface) {
219 if (!isValidNetwork(netId)) {
220 ALOGE("invalid netId %u", netId);
221 return -EINVAL;
222 }
223
224 android::RWLock::AutoWLock lock(mRWLock);
225 return getNetworkLocked(netId)->removeInterface(interface);
226}
227
228Permission NetworkController::getPermissionForUser(uid_t uid) const {
229 android::RWLock::AutoRLock lock(mRWLock);
Sreeram Ramachandraned4bd1f2014-07-05 12:31:05 -0700230 return getPermissionForUserLocked(uid);
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700231}
232
233void NetworkController::setPermissionForUsers(Permission permission,
234 const std::vector<uid_t>& uids) {
235 android::RWLock::AutoWLock lock(mRWLock);
236 for (uid_t uid : uids) {
Sreeram Ramachandraned4bd1f2014-07-05 12:31:05 -0700237 mUsers[uid] = permission;
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700238 }
239}
240
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700241bool NetworkController::canUserSelectNetwork(uid_t uid, unsigned netId) const {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700242 android::RWLock::AutoRLock lock(mRWLock);
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700243 Network* network = getNetworkLocked(netId);
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700244 if (!network || uid == INVALID_UID) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700245 return false;
246 }
Sreeram Ramachandraned4bd1f2014-07-05 12:31:05 -0700247 Permission userPermission = getPermissionForUserLocked(uid);
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700248 if ((userPermission & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
249 return true;
250 }
251 if (network->getType() == Network::VIRTUAL) {
252 return static_cast<VirtualNetwork*>(network)->appliesToUser(uid);
253 }
254 VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
255 if (virtualNetwork && mProtectableUsers.find(uid) == mProtectableUsers.end()) {
256 return false;
257 }
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700258 Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission();
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700259 return (userPermission & networkPermission) == networkPermission;
260}
261
262int NetworkController::setPermissionForNetworks(Permission permission,
263 const std::vector<unsigned>& netIds) {
264 android::RWLock::AutoWLock lock(mRWLock);
265 for (unsigned netId : netIds) {
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700266 Network* network = getNetworkLocked(netId);
267 if (!network || network->getType() != Network::PHYSICAL) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700268 ALOGE("invalid netId %u", netId);
269 return -EINVAL;
270 }
271
272 // TODO: ioctl(SIOCKILLADDR, ...) to kill socets on the network that don't have permission.
273
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700274 if (int ret = static_cast<PhysicalNetwork*>(network)->setPermission(permission)) {
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700275 return ret;
276 }
277 }
278 return 0;
279}
280
Sreeram Ramachandranb1425cc2014-06-23 18:54:27 -0700281int NetworkController::addUsersToNetwork(unsigned netId, const UidRanges& uidRanges) {
282 android::RWLock::AutoWLock lock(mRWLock);
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700283 Network* network = getNetworkLocked(netId);
284 if (!network || network->getType() != Network::VIRTUAL) {
Sreeram Ramachandranb1425cc2014-06-23 18:54:27 -0700285 ALOGE("invalid netId %u", netId);
286 return -EINVAL;
287 }
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700288 if (int ret = static_cast<VirtualNetwork*>(network)->addUsers(uidRanges)) {
Sreeram Ramachandranb1425cc2014-06-23 18:54:27 -0700289 return ret;
290 }
291 return 0;
292}
293
294int NetworkController::removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges) {
295 android::RWLock::AutoWLock lock(mRWLock);
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700296 Network* network = getNetworkLocked(netId);
297 if (!network || network->getType() != Network::VIRTUAL) {
Sreeram Ramachandranb1425cc2014-06-23 18:54:27 -0700298 ALOGE("invalid netId %u", netId);
299 return -EINVAL;
300 }
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700301 if (int ret = static_cast<VirtualNetwork*>(network)->removeUsers(uidRanges)) {
Sreeram Ramachandranb1425cc2014-06-23 18:54:27 -0700302 return ret;
303 }
304 return 0;
305}
306
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700307int NetworkController::addRoute(unsigned netId, const char* interface, const char* destination,
308 const char* nexthop, bool legacy, uid_t uid) {
309 return modifyRoute(netId, interface, destination, nexthop, true, legacy, uid);
310}
311
312int NetworkController::removeRoute(unsigned netId, const char* interface, const char* destination,
313 const char* nexthop, bool legacy, uid_t uid) {
314 return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid);
315}
316
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700317bool NetworkController::canProtect(uid_t uid) const {
318 android::RWLock::AutoRLock lock(mRWLock);
319 return ((getPermissionForUserLocked(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) ||
320 mProtectableUsers.find(uid) != mProtectableUsers.end();
321}
322
Sreeram Ramachandran89dad012014-07-02 10:09:49 -0700323void NetworkController::allowProtect(const std::vector<uid_t>& uids) {
324 android::RWLock::AutoWLock lock(mRWLock);
325 mProtectableUsers.insert(uids.begin(), uids.end());
326}
327
328void NetworkController::denyProtect(const std::vector<uid_t>& uids) {
329 android::RWLock::AutoWLock lock(mRWLock);
330 for (uid_t uid : uids) {
331 mProtectableUsers.erase(uid);
332 }
333}
334
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700335bool NetworkController::isValidNetwork(unsigned netId) const {
336 android::RWLock::AutoRLock lock(mRWLock);
337 return getNetworkLocked(netId);
338}
339
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700340Network* NetworkController::getNetworkLocked(unsigned netId) const {
Sreeram Ramachandran36ed53e2014-07-01 19:01:56 -0700341 auto iter = mNetworks.find(netId);
342 return iter == mNetworks.end() ? NULL : iter->second;
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700343}
344
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700345VirtualNetwork* NetworkController::getVirtualNetworkForUserLocked(uid_t uid) const {
346 for (const auto& entry : mNetworks) {
347 if (entry.second->getType() == Network::VIRTUAL) {
348 VirtualNetwork* virtualNetwork = static_cast<VirtualNetwork*>(entry.second);
349 if (virtualNetwork->appliesToUser(uid)) {
350 return virtualNetwork;
351 }
352 }
353 }
354 return NULL;
355}
356
Sreeram Ramachandraned4bd1f2014-07-05 12:31:05 -0700357Permission NetworkController::getPermissionForUserLocked(uid_t uid) const {
358 auto iter = mUsers.find(uid);
359 if (iter != mUsers.end()) {
360 return iter->second;
361 }
362 return uid < FIRST_APPLICATION_UID ? PERMISSION_SYSTEM : PERMISSION_NONE;
363}
364
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700365int NetworkController::modifyRoute(unsigned netId, const char* interface, const char* destination,
366 const char* nexthop, bool add, bool legacy, uid_t uid) {
Sreeram Ramachandrane09b20a2014-07-05 17:15:14 -0700367 unsigned existingNetId = getNetworkForInterface(interface);
Sreeram Ramachandranf4f6c8d2014-06-23 09:54:06 -0700368 if (netId == NETID_UNSET || existingNetId != netId) {
369 ALOGE("interface %s assigned to netId %u, not %u", interface, existingNetId, netId);
Lorenzo Colittif7fc8ec2014-06-18 00:41:58 +0900370 return -ENOENT;
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700371 }
372
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700373 RouteController::TableType tableType;
374 if (legacy) {
Sreeram Ramachandraned4bd1f2014-07-05 12:31:05 -0700375 if ((getPermissionForUser(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
Sreeram Ramachandran5009d5e2014-07-03 12:20:48 -0700376 tableType = RouteController::LEGACY_SYSTEM;
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700377 } else {
Sreeram Ramachandran5009d5e2014-07-03 12:20:48 -0700378 tableType = RouteController::LEGACY_NETWORK;
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700379 }
380 } else {
381 tableType = RouteController::INTERFACE;
382 }
383
Sreeram Ramachandraneb27b7e2014-07-01 14:30:30 -0700384 return add ? RouteController::addRoute(interface, destination, nexthop, tableType) :
385 RouteController::removeRoute(interface, destination, nexthop, tableType);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700386}