blob: 70ae1a7b5b52ac8611835b14e0c103c449bc2c8c [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/neighbor_cache.h"
#include <linux/neighbour.h>
#include <utility>
#include <base/logging.h>
#include <base/stl_util.h>
namespace portier {
using std::string;
using std::vector;
using shill::IPAddress;
using KeyPair = std::pair<const IPAddress, const string>;
using NeighPair = std::pair<KeyPair, NeighborCacheEntry>;
namespace {
// Checks if the specified |nud_state| is one of the valid NUD states
// recognized by the Linux kernel. These states are specified in
// <linux/neighbour.h>. Note that dummy states are not recognized
// as valid.
bool IsValidNudState(uint8_t nud_state) {
switch (nud_state) {
case NUD_REACHABLE:
case NUD_PROBE:
case NUD_DELAY:
case NUD_STALE:
case NUD_INCOMPLETE:
case NUD_FAILED:
return true;
}
return false;
}
// Converts a NUD state into a relative score used for ranking the
// entries. The higher the score, the high priority that NUD state
// has when multiple entries can be used. This score is based on the
// order of preferred states in RFC 4389 Section 4.1.
int GetNudScore(uint8_t nud_state) {
switch (nud_state) {
case NUD_REACHABLE:
return 5;
case NUD_PROBE:
return 4;
case NUD_DELAY:
return 3;
case NUD_STALE:
return 2;
case NUD_INCOMPLETE:
return 1;
case NUD_FAILED:
return 0;
}
return -1;
}
} // namespace
NeighborCacheEntry::NeighborCacheEntry()
: is_router(false), nud_state(NUD_NONE) {}
bool NeighborCache::GetEntry(const IPAddress& ip_address,
const string& pg_name,
NeighborCacheEntry* entry_out) const {
DCHECK(entry_out);
auto it = entries_.find(KeyPair(ip_address, pg_name));
if (it != entries_.end()) {
*entry_out = it->second;
return true;
}
return false;
}
bool NeighborCache::GetInterfaceRouter(const std::string& if_name,
const std::string& pg_name,
NeighborCacheEntry* entry_out) const {
DCHECK(entry_out);
// Using an initial score of 0 to prevent routers in a FAILED states from
// being returned.
int nud_score = 0;
for (const auto& pair : entries_) {
// Check if potential match, skip if not.
if (!pair.second.is_router || pair.second.if_name != if_name ||
pair.first.second != pg_name) {
continue;
}
int new_nud_score = GetNudScore(pair.second.nud_state);
if (new_nud_score > nud_score) {
nud_score = new_nud_score;
*entry_out = pair.second;
}
}
return nud_score > 0;
}
bool NeighborCache::HasEntry(const IPAddress& ip_address,
const string& pg_name) const {
const KeyPair key(ip_address, pg_name);
return base::ContainsKey(entries_, key);
}
bool NeighborCache::InsertEntry(const std::string& pg_name,
const NeighborCacheEntry& entry) {
// Validating based on needs of IPv6 ND Proxying.
if (!entry.ip_address.IsValid() ||
entry.ip_address.family() != IPAddress::kFamilyIPv6 ||
!entry.ll_address.IsValid() || entry.if_name.size() == 0 ||
pg_name.size() == 0 || !IsValidNudState(entry.nud_state)) {
return false;
}
const KeyPair key(entry.ip_address, pg_name);
entries_[key] = entry;
return true;
}
void NeighborCache::RemoveEntry(const IPAddress& ip_address,
const string& pg_name) {
entries_.erase(KeyPair(ip_address, pg_name));
}
void NeighborCache::ClearForInterface(const string& if_name) {
for (auto it = entries_.begin(); it != entries_.end();) {
if (it->second.if_name == if_name) {
it = entries_.erase(it);
} else {
++it;
}
}
}
void NeighborCache::ClearForGroup(const string& pg_name) {
for (auto it = entries_.begin(); it != entries_.end();) {
if (it->first.second == pg_name) {
it = entries_.erase(it);
} else {
++it;
}
}
}
void NeighborCache::Clear() {
entries_.clear();
}
} // namespace portier