Home | History | Annotate | Download | only in server
      1 /*
      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 
     17 // 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.
     32 
     33 #include "NetworkController.h"
     34 
     35 #include "Fwmark.h"
     36 #include "LocalNetwork.h"
     37 #include "PhysicalNetwork.h"
     38 #include "RouteController.h"
     39 #include "VirtualNetwork.h"
     40 
     41 #include "cutils/misc.h"
     42 #define LOG_TAG "Netd"
     43 #include "log/log.h"
     44 #include "resolv_netid.h"
     45 
     46 namespace {
     47 
     48 // Keep these in sync with ConnectivityService.java.
     49 const unsigned MIN_NET_ID = 100;
     50 const unsigned MAX_NET_ID = 65535;
     51 
     52 }  // namespace
     53 
     54 const unsigned NetworkController::MIN_OEM_ID   =  1;
     55 const unsigned NetworkController::MAX_OEM_ID   = 50;
     56 // NetIds 51..98 are reserved for future use.
     57 const unsigned NetworkController::LOCAL_NET_ID = 99;
     58 
     59 // All calls to methods here are made while holding a write lock on mRWLock.
     60 class NetworkController::DelegateImpl : public PhysicalNetwork::Delegate {
     61 public:
     62     explicit DelegateImpl(NetworkController* networkController);
     63     virtual ~DelegateImpl();
     64 
     65     int modifyFallthrough(unsigned vpnNetId, const std::string& physicalInterface,
     66                           Permission permission, bool add) WARN_UNUSED_RESULT;
     67 
     68 private:
     69     int addFallthrough(const std::string& physicalInterface,
     70                        Permission permission) override WARN_UNUSED_RESULT;
     71     int removeFallthrough(const std::string& physicalInterface,
     72                           Permission permission) override WARN_UNUSED_RESULT;
     73 
     74     int modifyFallthrough(const std::string& physicalInterface, Permission permission,
     75                           bool add) WARN_UNUSED_RESULT;
     76 
     77     NetworkController* const mNetworkController;
     78 };
     79 
     80 NetworkController::DelegateImpl::DelegateImpl(NetworkController* networkController) :
     81         mNetworkController(networkController) {
     82 }
     83 
     84 NetworkController::DelegateImpl::~DelegateImpl() {
     85 }
     86 
     87 int NetworkController::DelegateImpl::modifyFallthrough(unsigned vpnNetId,
     88                                                        const std::string& physicalInterface,
     89                                                        Permission permission, bool add) {
     90     if (add) {
     91         if (int ret = RouteController::addVirtualNetworkFallthrough(vpnNetId,
     92                                                                     physicalInterface.c_str(),
     93                                                                     permission)) {
     94             ALOGE("failed to add fallthrough to %s for VPN netId %u", physicalInterface.c_str(),
     95                   vpnNetId);
     96             return ret;
     97         }
     98     } else {
     99         if (int ret = RouteController::removeVirtualNetworkFallthrough(vpnNetId,
    100                                                                        physicalInterface.c_str(),
    101                                                                        permission)) {
    102             ALOGE("failed to remove fallthrough to %s for VPN netId %u", physicalInterface.c_str(),
    103                   vpnNetId);
    104             return ret;
    105         }
    106     }
    107     return 0;
    108 }
    109 
    110 int NetworkController::DelegateImpl::addFallthrough(const std::string& physicalInterface,
    111                                                     Permission permission) {
    112     return modifyFallthrough(physicalInterface, permission, true);
    113 }
    114 
    115 int NetworkController::DelegateImpl::removeFallthrough(const std::string& physicalInterface,
    116                                                        Permission permission) {
    117     return modifyFallthrough(physicalInterface, permission, false);
    118 }
    119 
    120 int NetworkController::DelegateImpl::modifyFallthrough(const std::string& physicalInterface,
    121                                                        Permission permission, bool add) {
    122     for (const auto& entry : mNetworkController->mNetworks) {
    123         if (entry.second->getType() == Network::VIRTUAL) {
    124             if (int ret = modifyFallthrough(entry.first, physicalInterface, permission, add)) {
    125                 return ret;
    126             }
    127         }
    128     }
    129     return 0;
    130 }
    131 
    132 NetworkController::NetworkController() :
    133         mDelegateImpl(new NetworkController::DelegateImpl(this)), mDefaultNetId(NETID_UNSET) {
    134     mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID);
    135 }
    136 
    137 unsigned NetworkController::getDefaultNetwork() const {
    138     android::RWLock::AutoRLock lock(mRWLock);
    139     return mDefaultNetId;
    140 }
    141 
    142 int NetworkController::setDefaultNetwork(unsigned netId) {
    143     android::RWLock::AutoWLock lock(mRWLock);
    144 
    145     if (netId == mDefaultNetId) {
    146         return 0;
    147     }
    148 
    149     if (netId != NETID_UNSET) {
    150         Network* network = getNetworkLocked(netId);
    151         if (!network) {
    152             ALOGE("no such netId %u", netId);
    153             return -ENONET;
    154         }
    155         if (network->getType() != Network::PHYSICAL) {
    156             ALOGE("cannot set default to non-physical network with netId %u", netId);
    157             return -EINVAL;
    158         }
    159         if (int ret = static_cast<PhysicalNetwork*>(network)->addAsDefault()) {
    160             return ret;
    161         }
    162     }
    163 
    164     if (mDefaultNetId != NETID_UNSET) {
    165         Network* network = getNetworkLocked(mDefaultNetId);
    166         if (!network || network->getType() != Network::PHYSICAL) {
    167             ALOGE("cannot find previously set default network with netId %u", mDefaultNetId);
    168             return -ESRCH;
    169         }
    170         if (int ret = static_cast<PhysicalNetwork*>(network)->removeAsDefault()) {
    171             return ret;
    172         }
    173     }
    174 
    175     mDefaultNetId = netId;
    176     return 0;
    177 }
    178 
    179 uint32_t NetworkController::getNetworkForDns(unsigned* netId, uid_t uid) const {
    180     android::RWLock::AutoRLock lock(mRWLock);
    181     Fwmark fwmark;
    182     fwmark.protectedFromVpn = true;
    183     fwmark.permission = PERMISSION_SYSTEM;
    184     if (checkUserNetworkAccessLocked(uid, *netId) == 0) {
    185         // If a non-zero NetId was explicitly specified, and the user has permission for that
    186         // network, use that network's DNS servers. Do not fall through to the default network even
    187         // if the explicitly selected network is a split tunnel VPN or a VPN without DNS servers.
    188         fwmark.explicitlySelected = true;
    189     } else {
    190         // If the user is subject to a VPN and the VPN provides DNS servers, use those servers
    191         // (possibly falling through to the default network if the VPN doesn't provide a route to
    192         // them). Otherwise, use the default network's DNS servers.
    193         VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
    194         if (virtualNetwork && virtualNetwork->getHasDns()) {
    195             *netId = virtualNetwork->getNetId();
    196         } else {
    197             *netId = mDefaultNetId;
    198         }
    199     }
    200     fwmark.netId = *netId;
    201     return fwmark.intValue;
    202 }
    203 
    204 // Returns the NetId that a given UID would use if no network is explicitly selected. Specifically,
    205 // the VPN that applies to the UID if any; otherwise, the default network.
    206 unsigned NetworkController::getNetworkForUser(uid_t uid) const {
    207     android::RWLock::AutoRLock lock(mRWLock);
    208     if (VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid)) {
    209         return virtualNetwork->getNetId();
    210     }
    211     return mDefaultNetId;
    212 }
    213 
    214 // Returns the NetId that will be set when a socket connect()s. This is the bypassable VPN that
    215 // applies to the user if any; otherwise, the default network.
    216 //
    217 // In general, we prefer to always set the default network's NetId in connect(), so that if the VPN
    218 // is a split-tunnel and disappears later, the socket continues working (since the default network's
    219 // NetId is still valid). Secure VPNs will correctly grab the socket's traffic since they have a
    220 // high-priority routing rule that doesn't care what NetId the socket has.
    221 //
    222 // But bypassable VPNs have a very low priority rule, so we need to mark the socket with the
    223 // bypassable VPN's NetId if we expect it to get any traffic at all. If the bypassable VPN is a
    224 // split-tunnel, that's okay, because we have fallthrough rules that will direct the fallthrough
    225 // traffic to the default network. But it does mean that if the bypassable VPN goes away (and thus
    226 // the fallthrough rules also go away), the socket that used to fallthrough to the default network
    227 // will stop working.
    228 unsigned NetworkController::getNetworkForConnect(uid_t uid) const {
    229     android::RWLock::AutoRLock lock(mRWLock);
    230     VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
    231     if (virtualNetwork && !virtualNetwork->isSecure()) {
    232         return virtualNetwork->getNetId();
    233     }
    234     return mDefaultNetId;
    235 }
    236 
    237 unsigned NetworkController::getNetworkForInterface(const char* interface) const {
    238     android::RWLock::AutoRLock lock(mRWLock);
    239     for (const auto& entry : mNetworks) {
    240         if (entry.second->hasInterface(interface)) {
    241             return entry.first;
    242         }
    243     }
    244     return NETID_UNSET;
    245 }
    246 
    247 bool NetworkController::isVirtualNetwork(unsigned netId) const {
    248     android::RWLock::AutoRLock lock(mRWLock);
    249     Network* network = getNetworkLocked(netId);
    250     return network && network->getType() == Network::VIRTUAL;
    251 }
    252 
    253 int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission) {
    254     if (!((MIN_NET_ID <= netId && netId <= MAX_NET_ID) ||
    255           (MIN_OEM_ID <= netId && netId <= MAX_OEM_ID))) {
    256         ALOGE("invalid netId %u", netId);
    257         return -EINVAL;
    258     }
    259 
    260     if (isValidNetwork(netId)) {
    261         ALOGE("duplicate netId %u", netId);
    262         return -EEXIST;
    263     }
    264 
    265     PhysicalNetwork* physicalNetwork = new PhysicalNetwork(netId, mDelegateImpl);
    266     if (int ret = physicalNetwork->setPermission(permission)) {
    267         ALOGE("inconceivable! setPermission cannot fail on an empty network");
    268         delete physicalNetwork;
    269         return ret;
    270     }
    271 
    272     android::RWLock::AutoWLock lock(mRWLock);
    273     mNetworks[netId] = physicalNetwork;
    274     return 0;
    275 }
    276 
    277 int NetworkController::createVirtualNetwork(unsigned netId, bool hasDns, bool secure) {
    278     if (!(MIN_NET_ID <= netId && netId <= MAX_NET_ID)) {
    279         ALOGE("invalid netId %u", netId);
    280         return -EINVAL;
    281     }
    282 
    283     if (isValidNetwork(netId)) {
    284         ALOGE("duplicate netId %u", netId);
    285         return -EEXIST;
    286     }
    287 
    288     android::RWLock::AutoWLock lock(mRWLock);
    289     if (int ret = modifyFallthroughLocked(netId, true)) {
    290         return ret;
    291     }
    292     mNetworks[netId] = new VirtualNetwork(netId, hasDns, secure);
    293     return 0;
    294 }
    295 
    296 int NetworkController::destroyNetwork(unsigned netId) {
    297     if (netId == LOCAL_NET_ID) {
    298         ALOGE("cannot destroy local network");
    299         return -EINVAL;
    300     }
    301     if (!isValidNetwork(netId)) {
    302         ALOGE("no such netId %u", netId);
    303         return -ENONET;
    304     }
    305 
    306     // TODO: ioctl(SIOCKILLADDR, ...) to kill all sockets on the old network.
    307 
    308     android::RWLock::AutoWLock lock(mRWLock);
    309     Network* network = getNetworkLocked(netId);
    310 
    311     // If we fail to destroy a network, things will get stuck badly. Therefore, unlike most of the
    312     // other network code, ignore failures and attempt to clear out as much state as possible, even
    313     // if we hit an error on the way. Return the first error that we see.
    314     int ret = network->clearInterfaces();
    315 
    316     if (mDefaultNetId == netId) {
    317         if (int err = static_cast<PhysicalNetwork*>(network)->removeAsDefault()) {
    318             ALOGE("inconceivable! removeAsDefault cannot fail on an empty network");
    319             if (!ret) {
    320                 ret = err;
    321             }
    322         }
    323         mDefaultNetId = NETID_UNSET;
    324     } else if (network->getType() == Network::VIRTUAL) {
    325         if (int err = modifyFallthroughLocked(netId, false)) {
    326             if (!ret) {
    327                 ret = err;
    328             }
    329         }
    330     }
    331     mNetworks.erase(netId);
    332     delete network;
    333     _resolv_delete_cache_for_net(netId);
    334     return ret;
    335 }
    336 
    337 int NetworkController::addInterfaceToNetwork(unsigned netId, const char* interface) {
    338     if (!isValidNetwork(netId)) {
    339         ALOGE("no such netId %u", netId);
    340         return -ENONET;
    341     }
    342 
    343     unsigned existingNetId = getNetworkForInterface(interface);
    344     if (existingNetId != NETID_UNSET && existingNetId != netId) {
    345         ALOGE("interface %s already assigned to netId %u", interface, existingNetId);
    346         return -EBUSY;
    347     }
    348 
    349     android::RWLock::AutoWLock lock(mRWLock);
    350     return getNetworkLocked(netId)->addInterface(interface);
    351 }
    352 
    353 int NetworkController::removeInterfaceFromNetwork(unsigned netId, const char* interface) {
    354     if (!isValidNetwork(netId)) {
    355         ALOGE("no such netId %u", netId);
    356         return -ENONET;
    357     }
    358 
    359     android::RWLock::AutoWLock lock(mRWLock);
    360     return getNetworkLocked(netId)->removeInterface(interface);
    361 }
    362 
    363 Permission NetworkController::getPermissionForUser(uid_t uid) const {
    364     android::RWLock::AutoRLock lock(mRWLock);
    365     return getPermissionForUserLocked(uid);
    366 }
    367 
    368 void NetworkController::setPermissionForUsers(Permission permission,
    369                                               const std::vector<uid_t>& uids) {
    370     android::RWLock::AutoWLock lock(mRWLock);
    371     for (uid_t uid : uids) {
    372         mUsers[uid] = permission;
    373     }
    374 }
    375 
    376 int NetworkController::checkUserNetworkAccess(uid_t uid, unsigned netId) const {
    377     android::RWLock::AutoRLock lock(mRWLock);
    378     return checkUserNetworkAccessLocked(uid, netId);
    379 }
    380 
    381 int NetworkController::setPermissionForNetworks(Permission permission,
    382                                                 const std::vector<unsigned>& netIds) {
    383     android::RWLock::AutoWLock lock(mRWLock);
    384     for (unsigned netId : netIds) {
    385         Network* network = getNetworkLocked(netId);
    386         if (!network) {
    387             ALOGE("no such netId %u", netId);
    388             return -ENONET;
    389         }
    390         if (network->getType() != Network::PHYSICAL) {
    391             ALOGE("cannot set permissions on non-physical network with netId %u", netId);
    392             return -EINVAL;
    393         }
    394 
    395         // TODO: ioctl(SIOCKILLADDR, ...) to kill socets on the network that don't have permission.
    396 
    397         if (int ret = static_cast<PhysicalNetwork*>(network)->setPermission(permission)) {
    398             return ret;
    399         }
    400     }
    401     return 0;
    402 }
    403 
    404 int NetworkController::addUsersToNetwork(unsigned netId, const UidRanges& uidRanges) {
    405     android::RWLock::AutoWLock lock(mRWLock);
    406     Network* network = getNetworkLocked(netId);
    407     if (!network) {
    408         ALOGE("no such netId %u", netId);
    409         return -ENONET;
    410     }
    411     if (network->getType() != Network::VIRTUAL) {
    412         ALOGE("cannot add users to non-virtual network with netId %u", netId);
    413         return -EINVAL;
    414     }
    415     if (int ret = static_cast<VirtualNetwork*>(network)->addUsers(uidRanges)) {
    416         return ret;
    417     }
    418     return 0;
    419 }
    420 
    421 int NetworkController::removeUsersFromNetwork(unsigned netId, const UidRanges& uidRanges) {
    422     android::RWLock::AutoWLock lock(mRWLock);
    423     Network* network = getNetworkLocked(netId);
    424     if (!network) {
    425         ALOGE("no such netId %u", netId);
    426         return -ENONET;
    427     }
    428     if (network->getType() != Network::VIRTUAL) {
    429         ALOGE("cannot remove users from non-virtual network with netId %u", netId);
    430         return -EINVAL;
    431     }
    432     if (int ret = static_cast<VirtualNetwork*>(network)->removeUsers(uidRanges)) {
    433         return ret;
    434     }
    435     return 0;
    436 }
    437 
    438 int NetworkController::addRoute(unsigned netId, const char* interface, const char* destination,
    439                                 const char* nexthop, bool legacy, uid_t uid) {
    440     return modifyRoute(netId, interface, destination, nexthop, true, legacy, uid);
    441 }
    442 
    443 int NetworkController::removeRoute(unsigned netId, const char* interface, const char* destination,
    444                                    const char* nexthop, bool legacy, uid_t uid) {
    445     return modifyRoute(netId, interface, destination, nexthop, false, legacy, uid);
    446 }
    447 
    448 bool NetworkController::canProtect(uid_t uid) const {
    449     android::RWLock::AutoRLock lock(mRWLock);
    450     return ((getPermissionForUserLocked(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) ||
    451            mProtectableUsers.find(uid) != mProtectableUsers.end();
    452 }
    453 
    454 void NetworkController::allowProtect(const std::vector<uid_t>& uids) {
    455     android::RWLock::AutoWLock lock(mRWLock);
    456     mProtectableUsers.insert(uids.begin(), uids.end());
    457 }
    458 
    459 void NetworkController::denyProtect(const std::vector<uid_t>& uids) {
    460     android::RWLock::AutoWLock lock(mRWLock);
    461     for (uid_t uid : uids) {
    462         mProtectableUsers.erase(uid);
    463     }
    464 }
    465 
    466 bool NetworkController::isValidNetwork(unsigned netId) const {
    467     android::RWLock::AutoRLock lock(mRWLock);
    468     return getNetworkLocked(netId);
    469 }
    470 
    471 Network* NetworkController::getNetworkLocked(unsigned netId) const {
    472     auto iter = mNetworks.find(netId);
    473     return iter == mNetworks.end() ? NULL : iter->second;
    474 }
    475 
    476 VirtualNetwork* NetworkController::getVirtualNetworkForUserLocked(uid_t uid) const {
    477     for (const auto& entry : mNetworks) {
    478         if (entry.second->getType() == Network::VIRTUAL) {
    479             VirtualNetwork* virtualNetwork = static_cast<VirtualNetwork*>(entry.second);
    480             if (virtualNetwork->appliesToUser(uid)) {
    481                 return virtualNetwork;
    482             }
    483         }
    484     }
    485     return NULL;
    486 }
    487 
    488 Permission NetworkController::getPermissionForUserLocked(uid_t uid) const {
    489     auto iter = mUsers.find(uid);
    490     if (iter != mUsers.end()) {
    491         return iter->second;
    492     }
    493     return uid < FIRST_APPLICATION_UID ? PERMISSION_SYSTEM : PERMISSION_NONE;
    494 }
    495 
    496 int NetworkController::checkUserNetworkAccessLocked(uid_t uid, unsigned netId) const {
    497     Network* network = getNetworkLocked(netId);
    498     if (!network) {
    499         return -ENONET;
    500     }
    501 
    502     // If uid is INVALID_UID, this likely means that we were unable to retrieve the UID of the peer
    503     // (using SO_PEERCRED). Be safe and deny access to the network, even if it's valid.
    504     if (uid == INVALID_UID) {
    505         return -EREMOTEIO;
    506     }
    507     Permission userPermission = getPermissionForUserLocked(uid);
    508     if ((userPermission & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
    509         return 0;
    510     }
    511     if (network->getType() == Network::VIRTUAL) {
    512         return static_cast<VirtualNetwork*>(network)->appliesToUser(uid) ? 0 : -EPERM;
    513     }
    514     VirtualNetwork* virtualNetwork = getVirtualNetworkForUserLocked(uid);
    515     if (virtualNetwork && virtualNetwork->isSecure() &&
    516             mProtectableUsers.find(uid) == mProtectableUsers.end()) {
    517         return -EPERM;
    518     }
    519     Permission networkPermission = static_cast<PhysicalNetwork*>(network)->getPermission();
    520     return ((userPermission & networkPermission) == networkPermission) ? 0 : -EACCES;
    521 }
    522 
    523 int NetworkController::modifyRoute(unsigned netId, const char* interface, const char* destination,
    524                                    const char* nexthop, bool add, bool legacy, uid_t uid) {
    525     if (!isValidNetwork(netId)) {
    526         ALOGE("no such netId %u", netId);
    527         return -ENONET;
    528     }
    529     unsigned existingNetId = getNetworkForInterface(interface);
    530     if (existingNetId == NETID_UNSET) {
    531         ALOGE("interface %s not assigned to any netId", interface);
    532         return -ENODEV;
    533     }
    534     if (existingNetId != netId) {
    535         ALOGE("interface %s assigned to netId %u, not %u", interface, existingNetId, netId);
    536         return -ENOENT;
    537     }
    538 
    539     RouteController::TableType tableType;
    540     if (netId == LOCAL_NET_ID) {
    541         tableType = RouteController::LOCAL_NETWORK;
    542     } else if (legacy) {
    543         if ((getPermissionForUser(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
    544             tableType = RouteController::LEGACY_SYSTEM;
    545         } else {
    546             tableType = RouteController::LEGACY_NETWORK;
    547         }
    548     } else {
    549         tableType = RouteController::INTERFACE;
    550     }
    551 
    552     return add ? RouteController::addRoute(interface, destination, nexthop, tableType) :
    553                  RouteController::removeRoute(interface, destination, nexthop, tableType);
    554 }
    555 
    556 int NetworkController::modifyFallthroughLocked(unsigned vpnNetId, bool add) {
    557     if (mDefaultNetId == NETID_UNSET) {
    558         return 0;
    559     }
    560     Network* network = getNetworkLocked(mDefaultNetId);
    561     if (!network) {
    562         ALOGE("cannot find previously set default network with netId %u", mDefaultNetId);
    563         return -ESRCH;
    564     }
    565     if (network->getType() != Network::PHYSICAL) {
    566         ALOGE("inconceivable! default network must be a physical network");
    567         return -EINVAL;
    568     }
    569     Permission permission = static_cast<PhysicalNetwork*>(network)->getPermission();
    570     for (const auto& physicalInterface : network->getInterfaces()) {
    571         if (int ret = mDelegateImpl->modifyFallthrough(vpnNetId, physicalInterface, permission,
    572                                                        add)) {
    573             return ret;
    574         }
    575     }
    576     return 0;
    577 }
    578