blob: 6e1598649012eb099642b631660df3bb5799909b [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "portier/group_mgr.h"
#include <algorithm>
#include <utility>
#include <base/logging.h>
#include <base/stl_util.h>
#include <base/strings/string_util.h>
namespace portier {
using std::string;
using std::vector;
using std::shared_ptr;
using Code = Status::Code;
using NameList = vector<string>;
using GroupListPair = std::pair<const string, NameList>;
using NameNamePair = std::pair<const string, string>;
namespace {
// Groups names should be easy to type/remember group names. These
// names will likely be typed on a shell. Group names can contain
// alphanumeric characters or underscores.
bool IsValidGroupName(const string& pg_name) {
for (char c : pg_name) {
if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) && c != '_' &&
c != '-') {
return false;
}
}
return pg_name.size() > 0;
}
} // namespace
// Constructor.
GroupManager::GroupManager() {}
// Proxy groups.
Status GroupManager::CreateProxyGroup(const string& pg_name) {
if (HasProxyGroup(pg_name)) {
return Status(Code::ALREADY_EXISTS)
<< "A proxy group named " << pg_name << " already exists";
}
if (!IsValidGroupName(pg_name)) {
return Status(Code::INVALID_ARGUMENT)
<< "Invalid proxy group name " << pg_name;
}
proxy_groups_.insert(GroupListPair(pg_name, NameList()));
return Status();
}
Status GroupManager::DestroyProxyGroup(const string& pg_name) {
if (!HasProxyGroup(pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "The proxy group " << pg_name << " does not exist";
}
// Step 1: Remove all interfaces from the proxy group.
// Use copy of list as the contents will change during the loop.
auto if_names = proxy_groups_.at(pg_name);
for (const string& if_name : if_names) {
RemoveInterfaceFromProxyGroup(if_name, pg_name);
}
// Step 2: Remove the proxy group from the list of groups.
proxy_groups_.erase(pg_name);
return Status();
}
void GroupManager::DestroyAllProxyGroups() {
auto pg_names = GetGroupNames();
for (const auto& pg_name : pg_names) {
DestroyProxyGroup(pg_name);
}
}
bool GroupManager::HasProxyGroup(const string& pg_name) const {
return base::ContainsKey(proxy_groups_, pg_name);
}
vector<string> GroupManager::GetGroupNames() const {
vector<string> pg_names;
for (const GroupListPair& pair : proxy_groups_) {
pg_names.push_back(pair.first);
}
return pg_names;
}
// Group membership.
Status GroupManager::AddInterfaceToProxyGroup(const string& if_name,
const string& pg_name) {
if (!HasProxyGroup(pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "The proxy group " << pg_name << " does not exist";
}
if (IsInterfaceMember(if_name)) {
string other_pg_name;
GetProxyGroupOfInterface(if_name, &other_pg_name);
return Status(Code::ALREADY_EXISTS)
<< "Interface " << if_name << " is already a member of group "
<< other_pg_name;
}
proxy_groups_.at(pg_name).push_back(if_name);
proxy_memberships_.insert(NameNamePair(if_name, pg_name));
return Status();
}
Status GroupManager::RemoveInterfaceFromProxyGroup(const string& if_name,
const string& pg_name) {
if (!IsInterfaceMember(if_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Interface " << if_name << " is not a member of any group";
}
if (!HasProxyGroup(pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Proxy group " << pg_name << " does not exist";
}
if (!IsInterfaceMemberOfProxyGroup(if_name, pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Interface " << if_name << " is not a member of the proxy group "
<< pg_name;
}
// If interface is upstream, then remove it as upstream.
if (IsInterfaceMemberOfProxyGroup(if_name, pg_name)) {
proxy_group_upstreams_.erase(pg_name);
}
auto& members = proxy_groups_.at(pg_name);
for (auto it = members.begin(); it != members.end(); it++) {
if (*it == if_name) {
members.erase(it);
break;
}
}
proxy_memberships_.erase(if_name);
return Status();
}
bool GroupManager::IsInterfaceMember(const string& if_name) const {
return base::ContainsKey(proxy_memberships_, if_name);
}
bool GroupManager::IsInterfaceMemberOfProxyGroup(const string& if_name,
const string& pg_name) const {
if (!HasProxyGroup(pg_name)) {
return false;
}
if (!IsInterfaceMember(if_name)) {
return false;
}
return proxy_memberships_.at(if_name) == pg_name;
}
Status GroupManager::GetGroupMembers(const string& pg_name,
vector<string>* if_names_out) const {
DCHECK(if_names_out != nullptr);
if (!HasProxyGroup(pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Proxy group " << pg_name << " does not exist";
}
*if_names_out = proxy_groups_.at(pg_name);
return Status();
}
bool GroupManager::GetProxyGroupOfInterface(const string& if_name,
string* pg_name) const {
DCHECK(pg_name != nullptr);
if (!IsInterfaceMember(if_name)) {
return false;
}
*pg_name = proxy_memberships_.at(if_name);
return true;
}
// Group upstream interface membership.
bool GroupManager::IsInterfaceUpstream(const std::string& if_name,
const std::string& pg_name) const {
if (!base::ContainsKey(proxy_group_upstreams_, pg_name)) {
return false;
}
return proxy_group_upstreams_.at(pg_name) == if_name;
}
bool GroupManager::GetProxyGroupUpstream(const string& pg_name,
string* if_name) const {
DCHECK(if_name != nullptr);
if (!HasProxyGroup(pg_name) ||
!base::ContainsKey(proxy_group_upstreams_, pg_name)) {
return false;
}
*if_name = proxy_group_upstreams_.at(pg_name);
return true;
}
Status GroupManager::SetProxyGroupUpstream(const string& if_name,
const string& pg_name) {
if (!IsInterfaceMember(if_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Interface " << if_name << " is not a member of any group";
}
if (!HasProxyGroup(pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Proxy group " << pg_name << " does not exist";
}
if (!IsInterfaceMemberOfProxyGroup(if_name, pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Interface " << if_name << " is not a member of proxy group "
<< pg_name;
}
if (base::ContainsKey(proxy_group_upstreams_, pg_name)) {
string upstream;
GetProxyGroupUpstream(pg_name, &upstream);
return Status(Code::ALREADY_EXISTS)
<< "Proxy group " << pg_name << " already has an upstream interface "
<< upstream;
}
proxy_group_upstreams_.insert(NameNamePair(pg_name, if_name));
return Status();
}
Status GroupManager::RemoveProxyGroupUpstream(const std::string& pg_name) {
if (!HasProxyGroup(pg_name)) {
return Status(Code::DOES_NOT_EXIST)
<< "Proxy group " << pg_name << " does not exist";
}
if (base::ContainsKey(proxy_group_upstreams_, pg_name)) {
proxy_group_upstreams_.erase(pg_name);
}
return Status();
}
} // namespace portier