357 lines
11 KiB
C++
357 lines
11 KiB
C++
//
|
|
// Copyright (C) 2012 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#include "shill/vpn/vpn_provider.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include <base/strings/string_util.h>
|
|
#if defined(__ANDROID__)
|
|
#include <dbus/service_constants.h>
|
|
#else
|
|
#include <chromeos/dbus/service_constants.h>
|
|
#endif // __ANDROID__
|
|
|
|
#include "shill/error.h"
|
|
#include "shill/logging.h"
|
|
#include "shill/manager.h"
|
|
#include "shill/process_manager.h"
|
|
#include "shill/profile.h"
|
|
#include "shill/store_interface.h"
|
|
#include "shill/vpn/l2tp_ipsec_driver.h"
|
|
#include "shill/vpn/openvpn_driver.h"
|
|
#include "shill/vpn/third_party_vpn_driver.h"
|
|
#include "shill/vpn/vpn_service.h"
|
|
|
|
using std::set;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
namespace shill {
|
|
|
|
namespace Logging {
|
|
static auto kModuleLogScope = ScopeLogger::kVPN;
|
|
static string ObjectID(const VPNProvider* v) { return "(vpn_provider)"; }
|
|
}
|
|
|
|
VPNProvider::VPNProvider(ControlInterface* control_interface,
|
|
EventDispatcher* dispatcher,
|
|
Metrics* metrics,
|
|
Manager* manager)
|
|
: control_interface_(control_interface),
|
|
dispatcher_(dispatcher),
|
|
metrics_(metrics),
|
|
manager_(manager) {}
|
|
|
|
VPNProvider::~VPNProvider() {}
|
|
|
|
void VPNProvider::Start() {}
|
|
|
|
void VPNProvider::Stop() {}
|
|
|
|
// static
|
|
bool VPNProvider::GetServiceParametersFromArgs(const KeyValueStore& args,
|
|
string* type_ptr,
|
|
string* name_ptr,
|
|
string* host_ptr,
|
|
Error* error) {
|
|
SLOG(nullptr, 2) << __func__;
|
|
string type = args.LookupString(kProviderTypeProperty, "");
|
|
if (type.empty()) {
|
|
Error::PopulateAndLog(
|
|
FROM_HERE, error, Error::kNotSupported, "Missing VPN type property.");
|
|
return false;
|
|
}
|
|
|
|
string host = args.LookupString(kProviderHostProperty, "");
|
|
if (host.empty()) {
|
|
Error::PopulateAndLog(
|
|
FROM_HERE, error, Error::kNotSupported, "Missing VPN host property.");
|
|
return false;
|
|
}
|
|
|
|
*type_ptr = type,
|
|
*host_ptr = host,
|
|
*name_ptr = args.LookupString(kNameProperty, "");
|
|
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool VPNProvider::GetServiceParametersFromStorage(const StoreInterface* storage,
|
|
const string& entry_name,
|
|
string* vpn_type_ptr,
|
|
string* name_ptr,
|
|
string* host_ptr,
|
|
Error* error) {
|
|
string service_type;
|
|
if (!storage->GetString(entry_name, kTypeProperty, &service_type) ||
|
|
service_type != kTypeVPN) {
|
|
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
|
|
"Unspecified or invalid network type");
|
|
return false;
|
|
}
|
|
if (!storage->GetString(entry_name, kProviderTypeProperty, vpn_type_ptr) ||
|
|
vpn_type_ptr->empty()) {
|
|
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
|
|
"VPN type not specified");
|
|
return false;
|
|
}
|
|
if (!storage->GetString(entry_name, kNameProperty, name_ptr) ||
|
|
name_ptr->empty()) {
|
|
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
|
|
"Network name not specified");
|
|
return false;
|
|
}
|
|
if (!storage->GetString(entry_name, kProviderHostProperty, host_ptr) ||
|
|
host_ptr->empty()) {
|
|
Error::PopulateAndLog(FROM_HERE, error, Error::kInvalidArguments,
|
|
"Host not specified");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ServiceRefPtr VPNProvider::GetService(const KeyValueStore& args,
|
|
Error* error) {
|
|
SLOG(this, 2) << __func__;
|
|
string type;
|
|
string name;
|
|
string host;
|
|
|
|
if (!GetServiceParametersFromArgs(args, &type, &name, &host, error)) {
|
|
return nullptr;
|
|
}
|
|
|
|
string storage_id = VPNService::CreateStorageIdentifier(args, error);
|
|
if (storage_id.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Find a service in the provider list which matches these parameters.
|
|
VPNServiceRefPtr service = FindService(type, name, host);
|
|
if (service == nullptr) {
|
|
service = CreateService(type, name, storage_id, error);
|
|
}
|
|
return service;
|
|
}
|
|
|
|
ServiceRefPtr VPNProvider::FindSimilarService(const KeyValueStore& args,
|
|
Error* error) const {
|
|
SLOG(this, 2) << __func__;
|
|
string type;
|
|
string name;
|
|
string host;
|
|
|
|
if (!GetServiceParametersFromArgs(args, &type, &name, &host, error)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Find a service in the provider list which matches these parameters.
|
|
VPNServiceRefPtr service = FindService(type, name, host);
|
|
if (!service) {
|
|
error->Populate(Error::kNotFound, "Matching service was not found");
|
|
}
|
|
|
|
return service;
|
|
}
|
|
|
|
bool VPNProvider::OnDeviceInfoAvailable(const string& link_name,
|
|
int interface_index) {
|
|
for (const auto& service : services_) {
|
|
if (service->driver()->ClaimInterface(link_name, interface_index)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void VPNProvider::RemoveService(VPNServiceRefPtr service) {
|
|
const auto it = std::find(services_.begin(), services_.end(), service);
|
|
if (it != services_.end()) {
|
|
services_.erase(it);
|
|
}
|
|
}
|
|
|
|
void VPNProvider::CreateServicesFromProfile(const ProfileRefPtr& profile) {
|
|
SLOG(this, 2) << __func__;
|
|
const StoreInterface* storage = profile->GetConstStorage();
|
|
KeyValueStore args;
|
|
args.SetString(kTypeProperty, kTypeVPN);
|
|
for (const auto& group : storage->GetGroupsWithProperties(args)) {
|
|
string type;
|
|
string name;
|
|
string host;
|
|
if (!GetServiceParametersFromStorage(storage,
|
|
group,
|
|
&type,
|
|
&name,
|
|
&host,
|
|
nullptr)) {
|
|
continue;
|
|
}
|
|
|
|
VPNServiceRefPtr service = FindService(type, name, host);
|
|
if (service != nullptr) {
|
|
// If the service already exists, it does not need to be configured,
|
|
// since PushProfile would have already called ConfigureService on it.
|
|
SLOG(this, 2) << "Service already exists " << group;
|
|
continue;
|
|
}
|
|
|
|
Error error;
|
|
service = CreateService(type, name, group, &error);
|
|
|
|
if (service == nullptr) {
|
|
LOG(ERROR) << "Could not create service for " << group;
|
|
continue;
|
|
}
|
|
|
|
if (!profile->ConfigureService(service)) {
|
|
LOG(ERROR) << "Could not configure service for " << group;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
VPNServiceRefPtr VPNProvider::CreateServiceInner(const string& type,
|
|
const string& name,
|
|
const string& storage_id,
|
|
Error* error) {
|
|
SLOG(this, 2) << __func__ << " type " << type << " name " << name
|
|
<< " storage id " << storage_id;
|
|
#if defined(DISABLE_VPN)
|
|
|
|
Error::PopulateAndLog(
|
|
FROM_HERE, error, Error::kNotSupported, "VPN is not supported.");
|
|
return nullptr;
|
|
|
|
#else
|
|
|
|
std::unique_ptr<VPNDriver> driver;
|
|
if (type == kProviderOpenVpn) {
|
|
driver.reset(new OpenVPNDriver(
|
|
control_interface_, dispatcher_, metrics_, manager_,
|
|
manager_->device_info(), ProcessManager::GetInstance()));
|
|
} else if (type == kProviderL2tpIpsec) {
|
|
driver.reset(new L2TPIPSecDriver(
|
|
control_interface_, dispatcher_, metrics_, manager_,
|
|
manager_->device_info(), ProcessManager::GetInstance()));
|
|
} else if (type == kProviderThirdPartyVpn) {
|
|
// For third party VPN host contains extension ID
|
|
driver.reset(new ThirdPartyVpnDriver(
|
|
control_interface_, dispatcher_, metrics_, manager_,
|
|
manager_->device_info()));
|
|
} else {
|
|
Error::PopulateAndLog(
|
|
FROM_HERE, error, Error::kNotSupported,
|
|
"Unsupported VPN type: " + type);
|
|
return nullptr;
|
|
}
|
|
|
|
VPNServiceRefPtr service = new VPNService(
|
|
control_interface_, dispatcher_, metrics_, manager_, driver.release());
|
|
service->set_storage_id(storage_id);
|
|
service->InitDriverPropertyStore();
|
|
if (!name.empty()) {
|
|
service->set_friendly_name(name);
|
|
}
|
|
return service;
|
|
|
|
#endif // DISABLE_VPN
|
|
}
|
|
|
|
VPNServiceRefPtr VPNProvider::CreateService(const string& type,
|
|
const string& name,
|
|
const string& storage_id,
|
|
Error* error) {
|
|
VPNServiceRefPtr service = CreateServiceInner(type, name, storage_id, error);
|
|
if (service) {
|
|
services_.push_back(service);
|
|
manager_->RegisterService(service);
|
|
}
|
|
|
|
return service;
|
|
}
|
|
|
|
VPNServiceRefPtr VPNProvider::FindService(const string& type,
|
|
const string& name,
|
|
const string& host) const {
|
|
for (const auto& service : services_) {
|
|
if (type == service->driver()->GetProviderType() &&
|
|
name == service->friendly_name() &&
|
|
host == service->driver()->GetHost()) {
|
|
return service;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ServiceRefPtr VPNProvider::CreateTemporaryService(
|
|
const KeyValueStore& args, Error* error) {
|
|
string type;
|
|
string name;
|
|
string host;
|
|
|
|
if (!GetServiceParametersFromArgs(args, &type, &name, &host, error)) {
|
|
return nullptr;
|
|
}
|
|
|
|
string storage_id = VPNService::CreateStorageIdentifier(args, error);
|
|
if (storage_id.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return CreateServiceInner(type, name, storage_id, error);
|
|
}
|
|
|
|
ServiceRefPtr VPNProvider::CreateTemporaryServiceFromProfile(
|
|
const ProfileRefPtr& profile, const std::string& entry_name, Error* error) {
|
|
string type;
|
|
string name;
|
|
string host;
|
|
if (!GetServiceParametersFromStorage(profile->GetConstStorage(),
|
|
entry_name,
|
|
&type,
|
|
&name,
|
|
&host,
|
|
error)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return CreateServiceInner(type, name, entry_name, error);
|
|
}
|
|
|
|
bool VPNProvider::HasActiveService() const {
|
|
for (const auto& service : services_) {
|
|
if (service->IsConnecting() || service->IsConnected()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void VPNProvider::DisconnectAll() {
|
|
for (const auto& service : services_) {
|
|
if (service->IsConnecting() || service->IsConnected()) {
|
|
service->Disconnect(nullptr, "user selected new config");
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace shill
|