2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 01:49:48 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
Francis Dupont
65c54cb880 Merge branch '3860-the-radius-hook-should-support-vendor-specific-attributes' into 'master'
Draft: Resolve "The RADIUS hook should support VENDOR Specific Attributes"

Closes #3860

See merge request isc-projects/kea!2759
2025-08-21 08:58:49 +00:00
Francis Dupont
88dbb7c529 [#3860] Checkpoint vendor in def 2025-08-21 10:58:39 +02:00
Francis Dupont
2c6fb35352 [#3860] More vsa 2025-08-21 10:58:39 +02:00
Francis Dupont
b52f4b9549 [#3860] Checkpoint: doing vsa 2025-08-21 10:58:39 +02:00
Francis Dupont
bd819aa229 [#3860] Added VENDOR 2025-08-21 10:58:39 +02:00
Francis Dupont
d7847c4bde [#3860] Added $INCLUDE 2025-08-21 10:58:38 +02:00
Francis Dupont
662e780069 [#4058] Improved ChangeLog 2025-08-21 09:41:56 +02:00
Francis Dupont
1d3472a2d1 [#4058] Added missing line and ChangeLog entry 2025-08-21 09:41:55 +02:00
15 changed files with 802 additions and 242 deletions

View File

@ -0,0 +1,4 @@
[bug] fdupont
When reusing an expired lease, kea-dhcp6 now correctly saves
the client hardware address in the lease.
(Gitlab #4058)

View File

@ -550,7 +550,7 @@ RADIUS dictionary. There are differences:
- Yes
- No
- since Kea 3.1.1
* - Support for Vendor Attributes

View File

@ -24,36 +24,6 @@ using namespace std;
namespace isc {
namespace radius {
AttributePtr
Attribute::fromText(const string& repr) {
if (repr.empty()) {
isc_throw(BadValue, "empty text attribute");
}
string trimed = str::trim(repr);
if (trimed.empty()) {
isc_throw(BadValue, "blank text attribute '" << repr << "'");
}
size_t equal = trimed.find('=');
if (equal == string::npos) {
isc_throw(BadValue, "can't find '=' in text attribute '"
<< repr << "'");
}
string name = str::trim(trimed.substr(0, equal));
if (name.empty()) {
isc_throw(BadValue, "empty attribute name in '" << repr << "'");
}
string value = str::trim(trimed.substr(equal + 1));
if (value.empty()) {
isc_throw(BadValue, "empty attribute value in '" << repr << "'");
}
AttrDefPtr def = AttrDefs::instance().getByName(name);
if (!def) {
isc_throw(NotFound, "can't find attribute definition for '"
<< name << "'");
}
return (Attribute::fromText(def, value));
}
AttributePtr
Attribute::fromText(const AttrDefPtr& def, const string& value) {
if (!def) {
@ -80,6 +50,8 @@ Attribute::fromText(const AttrDefPtr& def, const string& value) {
return (AttrIpv6Addr::fromText(def->type_, value));
case PW_TYPE_IPV6PREFIX:
return (AttrIpv6Prefix::fromText(def->type_, value));
case PW_TYPE_VSA:
return (AttrVsa::fromText(def->type_, value));
default:
// Impossible case.
isc_throw(OutOfRange, "unknown value type "
@ -131,6 +103,8 @@ Attribute::fromBytes(const AttrDefPtr& def, const vector<uint8_t>& value) {
return (AttrIpv6Addr::fromBytes(def->type_, value));
case PW_TYPE_IPV6PREFIX:
return (AttrIpv6Prefix::fromBytes(def->type_, value));
case PW_TYPE_VSA:
return (AttrVsa::fromBytes(def->type_, value));
default:
// Impossible case.
isc_throw(OutOfRange, "unknown value type "
@ -175,6 +149,18 @@ Attribute::fromIpv6Prefix(const uint8_t type, const uint8_t len,
return (AttributePtr(new AttrIpv6Prefix(type, len, value)));
}
AttributePtr
Attribute::fromVsa(const uint8_t type, const uint32_t vendor,
const std::string& value) {
return (AttributePtr(new AttrVsa(type, vendor, value)));
}
AttributePtr
Attribute::fromVsa(const uint8_t type, const uint32_t vendor,
const std::vector<uint8_t>& value) {
return (AttributePtr(new AttrVsa(type, vendor, value)));
}
string
Attribute::toString() const {
isc_throw(TypeError, "the attribute value type must be string, not "
@ -217,6 +203,18 @@ Attribute::toIpv6PrefixLen() const {
<< attrValueTypeToText(getValueType()));
}
uint32_t
Attribute::toVendorId() const {
isc_throw(TypeError, "the attribute value type must be vsa, not "
<< attrValueTypeToText(getValueType()));
}
string
Attribute::toVsaData() const {
isc_throw(TypeError, "the attribute value type must be vsa, not "
<< attrValueTypeToText(getValueType()));
}
AttrString::AttrString(const uint8_t type, const vector<uint8_t>& value)
: Attribute(type), value_() {
if (value.empty()) {
@ -260,7 +258,17 @@ AttrString::toText(size_t indent) const {
for (size_t i = 0; i < indent; i++) {
output << " ";
}
output << AttrDefs::instance().getName(getType()) << '=' << value_;
output << AttrDefs::instance().getName(getType()) << '=';
if (str::isPrintable(value_)) {
output << "'" << value_ << "'";
} else {
vector<uint8_t> binary;
binary.resize(value_.size());
if (binary.size() > 0) {
memmove(&binary[0], value_.c_str(), binary.size());
}
output << "0x" << encode::encodeHex(binary);
}
return (output.str());
}
@ -612,6 +620,102 @@ AttrIpv6Prefix::toElement() const {
return (output);
}
AttrVsa::AttrVsa(const uint8_t type, const uint32_t vendor,
const vector<uint8_t>& value)
: Attribute(type), vendor_(vendor), value_() {
if (value.empty()) {
isc_throw(BadValue, "value is empty");
}
if (value.size() > MAX_VSA_DATA_LEN) {
isc_throw(BadValue, "value is too large " << value.size()
<< " > " << MAX_VSA_DATA_LEN);
}
value_.resize(value.size());
memmove(&value_[0], &value[0], value_.size());
}
AttributePtr
AttrVsa::fromText(const uint8_t type, const string& repr) {
isc_throw(NotImplemented, "Can't decode vsa from text");
}
AttributePtr
AttrVsa::fromBytes(const uint8_t type, const vector<uint8_t>& bytes) {
if (bytes.empty()) {
isc_throw(BadValue, "empty attribute value");
}
if (bytes.size() < 5) {
isc_throw(BadValue, "value is too small " << bytes.size() << " < 5");
} else if (bytes.size() > MAX_STRING_LEN) {
isc_throw(BadValue, "value is too large " << bytes.size()
<< " > " << MAX_STRING_LEN);
}
uint32_t vendor = bytes[0] << 24;
vendor |= bytes[1] << 16;
vendor |= bytes[2] << 8;
vendor |= bytes[3];
vector<uint8_t> value;
value.resize(bytes.size() - 4);
if (value.size() > 0) {
memmove(&value[0], &bytes[4], value.size());
}
return (AttributePtr(new AttrVsa(type, vendor, value)));
}
string
AttrVsa::toText(size_t indent) const {
ostringstream output;
for (size_t i = 0; i < indent; i++) {
output << " ";
}
output << AttrDefs::instance().getName(getType()) << "=["
<< vendor_ << "]";
vector<uint8_t> binary;
binary.resize(value_.size());
if (binary.size() > 0) {
memmove(&binary[0], value_.c_str(), binary.size());
}
output << "0x" << encode::encodeHex(binary);
return (output.str());
}
std::vector<uint8_t>
AttrVsa::toBytes() const {
vector<uint8_t> output;
output.resize(2 + getValueLen());
output[0] = getType();
output[1] = 2 + getValueLen();
output[2] = (vendor_ & 0xff000000U) >> 24;
output[3] = (vendor_ & 0xff0000U) >> 16;
output[4] = (vendor_ & 0xff00U) >> 8;
output[5] = vendor_ & 0xffU;
if (output.size() > 6) {
memmove(&output[6], &value_[0], output.size() - 6);
}
return (output);
}
ElementPtr
AttrVsa::toElement() const {
ElementPtr output = Element::createMap();
AttrDefPtr def = AttrDefs::instance().getByType(getType());
if (def) {
output->set("name", Element::create(def->name_));
}
output->set("type", Element::create(static_cast<int>(getType())));
ostringstream vendor;
vendor << vendor_;
output->set("vendor", Element::create(vendor.str()));
vector<uint8_t> binary;
binary.resize(value_.size());
if (binary.size() > 0) {
memmove(&binary[0], value_.c_str(), binary.size());
}
string raw = encode::encodeHex(binary);
output->set("vsa-raw", Element::create(raw));
return (output);
}
void
Attributes::add(const ConstAttributePtr& attr) {
if (!attr) {

View File

@ -28,6 +28,9 @@ namespace radius {
/// @brief Maximum string size.
static constexpr size_t MAX_STRING_LEN = 253;
/// @brief Maximum vsa data size.
static constexpr size_t MAX_VSA_DATA_LEN = MAX_STRING_LEN - 4;
/// @brief Type error.
using isc::data::TypeError;
@ -66,13 +69,6 @@ public:
/// Generic factories.
/// @brief From text.
///
/// @param repr name=value representation.
/// @return pointer to the attribute.
/// @throw NotFound if the definition can't be found.
static AttributePtr fromText(const std::string& repr);
/// @brief From bytes (wire format).
///
/// @param bytes binary attribute.
@ -159,6 +155,28 @@ public:
const uint8_t len,
const asiolink::IOAddress& value);
/// @brief From Vendor ID and string data with type.
///
/// @note Requires the type to be of a standard vsa attribute.
///
/// @param type type of attribute.
/// @param vendor vendor id.
/// @param value vsa data.
static AttributePtr fromVsa(const uint8_t type,
const uint32_t vendor,
const std::string& value);
/// @brief From Vendor ID and binary data with type.
///
/// @note Requires the type to be of the Vendor Specific attribute (26).
///
/// @param type type of attribute.
/// @param vendor vendor id.
/// @param value vsa data.
static AttributePtr fromVsa(const uint8_t type,
const uint32_t vendor,
const std::vector<uint8_t>& value);
/// Generic get methods.
/// @brief Value length.
@ -223,6 +241,18 @@ public:
/// @throw TypeError if the attribute is not an ipv6prefix one.
virtual uint8_t toIpv6PrefixLen() const;
/// @brief To vendor id.
///
/// @return the vendor id.
/// @throw TypeError if the attribute is not a vsa one.
virtual uint32_t toVendorId() const;
/// @brief To vsa data.
///
/// @return the vsa data.
/// @throw TypeError if the attribute is not a vsa one.
virtual std::string toVsaData() const;
/// @brief Type.
const uint8_t type_;
};
@ -233,6 +263,7 @@ public:
/// @brief RADIUS attribute holding strings.
class AttrString : public Attribute {
protected:
/// @brief Constructor.
///
/// @param type attribute type.
@ -648,6 +679,109 @@ private:
asiolink::IOAddress value_;
};
/// @brief RADIUS attribute holding vsa.
class AttrVsa : public Attribute {
protected:
/// @brief Constructor.
///
/// @param type attribute type.
/// @param vendor vendor id.
/// @param value string vsa data.
AttrVsa(const uint8_t type, const uint32_t vendor,
const std::string& value)
: Attribute(type), vendor_(vendor), value_(value) {
if (value.empty()) {
isc_throw(BadValue, "value is empty");
}
if (value.size() > MAX_VSA_DATA_LEN) {
isc_throw(BadValue, "value is too large " << value.size()
<< " > " << MAX_VSA_DATA_LEN);
}
}
/// @brief Constructor.
///
/// @param type attribute type.
/// @param vendor vendor id.
/// @param value binary vsa data.
AttrVsa(const uint8_t type, const uint32_t vendor,
const std::vector<uint8_t>& value);
/// @brief From text.
///
/// @param type attribute type.
/// @param repr value representation.
/// @return pointer to the attribute or null.
/// @throw NotImplemented
static AttributePtr fromText(const uint8_t type, const std::string& repr);
/// @brief From bytes.
///
/// @param type attribute type.
/// @param bytes binary value.
/// @return pointer to the attribute or null.
static AttributePtr fromBytes(const uint8_t type,
const std::vector<uint8_t>& bytes);
/// Make Attribute a friend class.
friend class Attribute;
public:
/// @brief Get value type.
///
/// @return the value type.
virtual AttrValueType getValueType() const override {
return (PW_TYPE_VSA);
}
/// @brief Value length.
///
/// @return Value length.
virtual size_t getValueLen() const override {
return (4 + value_.size());
}
/// @brief Returns text representation of the attribute.
///
/// @param indent number of spaces before printing text.
/// @return string with text representation.
virtual std::string toText(size_t indent = 0) const override;
/// @brief To bytes.
///
/// @return binary representation.
virtual std::vector<uint8_t> toBytes() const override;
/// @brief To vendor id.
///
/// @return the vendor id.
virtual uint32_t toVendorId() const override {
return (vendor_);
}
/// @brief To vsa data.
///
/// @return the vsa data.
virtual std::string toVsaData() const override {
return (value_);
}
/// @brief Unparse attribute.
///
/// @return a pointer to unparsed attribute.
virtual data::ElementPtr toElement() const override;
private:
/// @brief Vendor id.
uint32_t vendor_;
/// @brief Value.
std::string value_;
};
/// @brief Collection of attributes.
class Attributes : public data::CfgToElement {
public:

View File

@ -34,6 +34,8 @@ attrValueTypeToText(const AttrValueType value) {
return ("ipv6addr");
case PW_TYPE_IPV6PREFIX:
return ("ipv6prefix");
case PW_TYPE_VSA:
return ("vsa");
default:
// Impossible case.
return ("unknown?");
@ -52,6 +54,8 @@ textToAttrValueType(const string& name) {
return (PW_TYPE_IPV6ADDR);
} else if (name == "ipv6prefix") {
return (PW_TYPE_IPV6PREFIX);
} else if (name == "vsa") {
return (PW_TYPE_VSA);
} else {
isc_throw(OutOfRange, "unknown AttrValueType name " << name);
}
@ -64,9 +68,9 @@ AttrDefs::instance() {
}
AttrDefPtr
AttrDefs::getByType(const uint8_t type) const {
AttrDefs::getByType(const uint8_t type, const uint32_t vendor) const {
auto const& idx = container_.get<0>();
auto it = idx.find(type);
auto it = idx.find(boost::make_tuple(vendor, type));
if (it != idx.end()) {
return (*it);
}
@ -74,15 +78,15 @@ AttrDefs::getByType(const uint8_t type) const {
}
AttrDefPtr
AttrDefs::getByName(const string& name) const {
AttrDefs::getByName(const string& name, const uint32_t vendor) const {
auto const& idx = container_.get<1>();
auto it = idx.find(name);
auto it = idx.find(boost::make_tuple(vendor, name));
if (it != idx.end()) {
return (*it);
}
auto alias = aliases_.find(name);
auto alias = aliases_.find(boost::make_tuple(vendor, name));
if (alias != aliases_.end()) {
auto ita = idx.find(alias->second);
auto ita = idx.find(boost::make_tuple(vendor, alias->name_));
if (ita != idx.end()) {
return (*ita);
}
@ -96,42 +100,51 @@ AttrDefs::add(AttrDefPtr def) {
return;
}
auto& idx1 = container_.get<1>();
auto it1 = idx1.find(def->name_);
auto it1 = idx1.find(boost::make_tuple(def->vendor_, def->name_));
if (it1 != idx1.end()) {
if ((def->type_ == (*it1)->type_) &&
(def->value_type_ == (*it1)->value_type_)) {
// Duplicate: ignore.
return;
}
isc_throw(BadValue, "Illegal attribute redefinition of '" << def->name_
<< "' type " << static_cast<unsigned>((*it1)->type_)
<< " value type " << attrValueTypeToText((*it1)->value_type_)
<< " by " << static_cast<unsigned>(def->type_)
<< " " << attrValueTypeToText(def->value_type_));
ostringstream msg;
msg << "Illegal attribute redefinition of '" << def->name_ << "'";
if (def->vendor_ != 0) {
msg << " vendor " << def->vendor_;
}
msg << " type " << static_cast<unsigned>((*it1)->type_)
<< " value type " << attrValueTypeToText((*it1)->value_type_)
<< " by " << static_cast<unsigned>(def->type_)
<< " " << attrValueTypeToText(def->value_type_);
isc_throw(BadValue, msg.str());
}
auto& idx0 = container_.get<0>();
auto it0 = idx0.find(def->type_);
auto it0 = idx0.find(boost::make_tuple(def->vendor_, def->type_));
if (it0 != idx0.end()) {
if (def->value_type_ == (*it0)->value_type_) {
// Alias.
auto p = pair<string, string>(def->name_, (*it0)->name_);
static_cast<void>(aliases_.insert(p));
AttrDefAlias alias(def->name_, (*it0)->name_, def->vendor_);
static_cast<void>(aliases_.insert(alias));
return;
}
isc_throw(BadValue, "Illegal attribute redefinition of '"
<< (*it0)->name_ << "' type "
<< static_cast<unsigned>((*it0)->type_) << " value type "
<< attrValueTypeToText((*it0)->value_type_)
<< " by '" << def->name_ << "' "
<< static_cast<unsigned>(def->type_) << " "
<< attrValueTypeToText(def->value_type_));
ostringstream msg;
msg << "Illegal attribute redefinition of '" << (*it0)->name_ << "'";
if (def->vendor_ != 0) {
msg << " vendor " << def->vendor_;
}
msg << " type " << static_cast<unsigned>((*it0)->type_)
<< " value type " << attrValueTypeToText((*it0)->value_type_)
<< " by '" << def->name_ << "' "
<< static_cast<unsigned>(def->type_) << " "
<< attrValueTypeToText(def->value_type_);
isc_throw(BadValue, msg.str());
}
static_cast<void>(container_.insert(def));
}
string
AttrDefs::getName(const uint8_t type) const {
AttrDefPtr def = getByType(type);
AttrDefs::getName(const uint8_t type, const uint32_t vendor) const {
AttrDefPtr def = getByType(type, vendor);
if (def) {
return (def->name_);
}
@ -172,6 +185,12 @@ AttrDefs::add(IntCstDefPtr def) {
// Duplicate: ignore.
return;
}
if (def->type_ == PW_VENDOR_SPECIFIC) {
// Vendor id special case.
isc_throw(BadValue, "Illegal vendor id redefinition of '"
<< def->name_ << "' value " << (*it)->value_
<< " by " << def->value_);
}
isc_throw(BadValue, "Illegal integer constant redefinition of '"
<< def->name_ << "' for attribute '" << getName(def->type_)
<< "' value " << (*it)->value_ << " by " << def->value_);
@ -180,7 +199,7 @@ AttrDefs::add(IntCstDefPtr def) {
}
void
AttrDefs::parseLine(const string& line) {
AttrDefs::parseLine(const string& line, unsigned int depth) {
// Ignore empty lines.
if (line.empty()) {
return;
@ -196,6 +215,14 @@ AttrDefs::parseLine(const string& line) {
if (tokens.empty()) {
return;
}
// $INCLUDE include.
if (tokens[0] == "$INCLUDE") {
if (tokens.size() != 2) {
isc_throw(Unexpected, "expected 2 tokens, got " << tokens.size());
}
readDictionary(tokens[1], depth + 1);
return;
}
// Attribute definition.
if (tokens[0] == "ATTRIBUTE") {
if (tokens.size() != 4) {
@ -216,6 +243,10 @@ AttrDefs::parseLine(const string& line) {
isc_throw(Unexpected, "can't parse attribute type " << type_str);
}
AttrValueType value_type = textToAttrValueType(tokens[3]);
if ((value_type == PW_TYPE_VSA) && (type != PW_VENDOR_SPECIFIC)) {
isc_throw(BadValue, "only Vendor-Specific (26) attribute can "
<< "have the vsa data type");
}
AttrDefPtr def(new AttrDef(type, name, value_type));
add(def);
return;
@ -251,11 +282,39 @@ AttrDefs::parseLine(const string& line) {
add(def);
return;
}
// Vendor id definition.
if (tokens[0] == "VENDOR") {
if (tokens.size() != 3) {
isc_throw(Unexpected, "expected 3 tokens, got " << tokens.size());
}
const string& name = tokens[1];
const string& value_str = tokens[2];
uint32_t value = 0;
try {
int64_t val = boost::lexical_cast<int64_t>(value_str);
if ((val < numeric_limits<int32_t>::min()) ||
(val > numeric_limits<uint32_t>::max())) {
isc_throw(Unexpected, "not 32 bit " << value_str);
}
value = static_cast<uint32_t>(val);
} catch (...) {
isc_throw(Unexpected, "can't parse integer value " << value_str);
}
if (value == 0) {
isc_throw(Unexpected, "0 is reserved");
}
IntCstDefPtr def(new IntCstDef(PW_VENDOR_SPECIFIC, name, value));
add(def);
return;
}
isc_throw(Unexpected, "unknown dictionary entry '" << tokens[0] << "'");
}
void
AttrDefs::readDictionary(const string& path) {
AttrDefs::readDictionary(const string& path, unsigned depth) {
if (depth >= 5) {
isc_throw(BadValue, "Too many nested $INCLUDE");
}
ifstream ifs(path);
if (!ifs.is_open()) {
isc_throw(BadValue, "can't open dictionary '" << path << "': "
@ -265,23 +324,24 @@ AttrDefs::readDictionary(const string& path) {
isc_throw(BadValue, "bad dictionary '" << path << "'");
}
try {
readDictionary(ifs);
readDictionary(ifs, depth);
ifs.close();
} catch (const exception& ex) {
ifs.close();
isc_throw(BadValue, ex.what() << " in dictionary '" << path << "'");
isc_throw(BadValue, ex.what() << " in dictionary '" << path << "'"
<< (depth > 0 ? "," : ""));
}
}
void
AttrDefs::readDictionary(istream& is) {
AttrDefs::readDictionary(istream& is, unsigned int depth) {
size_t lines = 0;
string line;
try {
while (is.good()) {
++lines;
getline(is, line);
parseLine(line);
parseLine(line, depth);
}
if (!is.eof()) {
isc_throw(BadValue, "I/O error: " << strerror(errno));

View File

@ -31,7 +31,8 @@ enum AttrValueType {
PW_TYPE_INTEGER,
PW_TYPE_IPADDR,
PW_TYPE_IPV6ADDR,
PW_TYPE_IPV6PREFIX
PW_TYPE_IPV6PREFIX,
PW_TYPE_VSA
};
/// @brief AttrValueType value -> name function.
@ -57,9 +58,10 @@ public:
/// @param type attribute type.
/// @param name attribute name.
/// @param value_type attribute value type.
/// @param vendor vendor id (default 0).
AttrDef(const uint8_t type, const std::string& name,
const AttrValueType value_type)
: type_(type), name_(name), value_type_(value_type) {
const AttrValueType value_type, const uint32_t vendor = 0)
: type_(type), name_(name), value_type_(value_type), vendor_(vendor) {
}
/// @brief type.
@ -70,6 +72,9 @@ public:
/// @brief value_type.
const AttrValueType value_type_;
/// @brief vendor id (default 0).
const uint32_t vendor_;
};
/// @brief Shared pointers to Attribute definition.
@ -78,7 +83,33 @@ typedef boost::shared_ptr<AttrDef> AttrDefPtr;
/// @brief List of Attribute definitions.
typedef std::list<AttrDef> AttrDefList;
/// @brief RADIUS attribute aliases.
class AttrDefAlias {
public:
/// @brief Constructor.
///
/// @param alias attribute alias name.
/// @param name attribute name.
/// @param vendor vendor id (default 0).
AttrDefAlias(const std::string& alias, const std::string& name,
const uint32_t vendor = 0)
: alias_(alias), name_(name), vendor_(vendor) {
}
/// @brief alias.
const std::string alias_;
/// @brief name.
const std::string name_;
/// @brief vendor id (default 0).
const uint32_t vendor_;
};
/// @brief RADIUS integer constant definitions.
///
/// Include vendor ids with Vendor-Specific attribute.
class IntCstDef {
public:
@ -115,23 +146,53 @@ public:
AttrDefPtr,
// Start specification of indexes here.
boost::multi_index::indexed_by<
// Hash index for by type.
// Hash index for by vendor and type.
boost::multi_index::hashed_unique<
boost::multi_index::member<
AttrDef, const uint8_t, &AttrDef::type_
boost::multi_index::composite_key<
AttrDef,
boost::multi_index::member<
AttrDef, const uint32_t, &AttrDef::vendor_
>,
boost::multi_index::member<
AttrDef, const uint8_t, &AttrDef::type_
>
>
>,
// Hash index for by name.
// Hash index for by vendor and name.
boost::multi_index::hashed_unique<
boost::multi_index::member<
AttrDef, const std::string, &AttrDef::name_
boost::multi_index::composite_key<
AttrDef,
boost::multi_index::member<
AttrDef, const uint32_t, &AttrDef::vendor_
>,
boost::multi_index::member<
AttrDef, const std::string, &AttrDef::name_
>
>
>
>
> AttrDefContainer;
/// @brief Type of the alias table (alias -> standard name map).
typedef std::unordered_map<std::string, std::string> AttrDefAliases;
typedef boost::multi_index_container<
// This container stores aliases.
AttrDefAlias,
// Start specification of indexes here.
boost::multi_index::indexed_by<
// Hash index for by vendor and alias.
boost::multi_index::hashed_unique<
boost::multi_index::composite_key<
AttrDefAlias,
boost::multi_index::member<
AttrDefAlias, const uint32_t, &AttrDefAlias::vendor_
>,
boost::multi_index::member<
AttrDefAlias, const std::string, &AttrDefAlias::alias_
>
>
>
>
> AttrDefAliases;
/// @brief Type of the integer constant definition container.
typedef boost::multi_index_container<
@ -173,17 +234,20 @@ public:
/// @return the single instance.
static AttrDefs& instance();
/// @brief Get attribute definition by type.
/// @brief Get attribute definition by type and vendor.
///
/// @param type type to look for.
/// @param vendor vendor id to look for (default 0).
/// @return pointer to the attribute definition or null.
AttrDefPtr getByType(const uint8_t type) const;
AttrDefPtr getByType(const uint8_t type, const uint32_t vendor = 0) const;
/// @brief Get attribute definition by name.
/// @brief Get attribute definition by name and vendor.
///
/// @param name name to look for.
/// @param vendor vendor id to look for (default 0).
/// @return pointer to the attribute definition or null.
AttrDefPtr getByName(const std::string& name) const;
AttrDefPtr getByName(const std::string& name,
const uint32_t vendor = 0) const;
/// @brief Add (or replace) an attribute definition.
///
@ -200,7 +264,8 @@ public:
/// @brief Get attribute name.
///
/// @param type type to look for.
std::string getName(const uint8_t type) const;
/// @param vendor vendor id to look for (default 0).
std::string getName(const uint8_t type, const uint32_t vendor = 0) const;
/// @brief Get integer constant definition by attribute type and name.
///
@ -223,18 +288,22 @@ public:
/// @brief Read a dictionary from a file.
///
/// Fills attribute and integer constant definition tables from
/// a dictionary file.
/// a dictionary file. Recursion depth is initialized to 0,
/// incremented by includes and limited to 5.
///
/// @param path dictionary file path.
void readDictionary(const std::string& path);
/// @param depth recursion depth.
void readDictionary(const std::string& path, unsigned int depth = 0);
/// @brief Read a dictionary from an input stream.
///
/// Fills attribute and integer constant definition tables from
/// a dictionary input stream.
/// a dictionary input stream. Recursion depth is initialized to 0,
/// incremented by includes and limited to 5.
///
/// @param is input stream.
void readDictionary(std::istream& is);
/// @param depth recursion depth.
void readDictionary(std::istream& is, unsigned int depth = 0);
/// @brief Check if a list of standard attribute definitions
/// are available and correct.
@ -255,7 +324,8 @@ protected:
/// @brief Parse a dictionary line.
///
/// @param line line to parse.
void parseLine(const std::string& line);
/// @param depth recursion depth.
void parseLine(const std::string& line, unsigned int depth);
/// @brief Attribute definition container.
AttrDefContainer container_;

View File

@ -26,7 +26,7 @@ ATTRIBUTE Framed-Route 22 string
ATTRIBUTE Framed-IPX-Network 23 ipaddr
ATTRIBUTE State 24 string
ATTRIBUTE Class 25 string
ATTRIBUTE Vendor-Specific 26 string
ATTRIBUTE Vendor-Specific 26 vsa
ATTRIBUTE Session-Timeout 27 integer
ATTRIBUTE Idle-Timeout 28 integer
ATTRIBUTE Termination-Action 29 integer
@ -252,3 +252,8 @@ VALUE Acct-Terminate-Cause Service-Unavailable 15
VALUE Acct-Terminate-Cause Callback 16
VALUE Acct-Terminate-Cause User-Error 17
VALUE Acct-Terminate-Cause Host-Request 18
# Examples
#$INCLUDE foobar
#VENDOR DSL-Forum 3561

View File

@ -74,6 +74,7 @@ const AttrDefList RadiusConfigParser::USED_STANDARD_ATTR_DEFS = {
{ PW_FRAMED_IP_ADDRESS, "Framed-IP-Address", PW_TYPE_IPADDR },
{ PW_REPLY_MESSAGE, "Reply-Message", PW_TYPE_STRING },
{ PW_CLASS, "Class", PW_TYPE_STRING },
{ PW_VENDOR_SPECIFIC, "Vendor-Specific", PW_TYPE_VSA },
{ PW_CALLING_STATION_ID, "Calling-Station-Id", PW_TYPE_STRING },
{ PW_ACCT_STATUS_TYPE, "Acct-Status-Type", PW_TYPE_INTEGER },
{ PW_ACCT_DELAY_TIME, "Acct-Delay-Time", PW_TYPE_INTEGER },

View File

@ -1755,12 +1755,12 @@ TEST_F(AccessTest, buildHWAddr4) {
EXPECT_LE(2, handler->env_.send_attrs_->size());
ConstAttributePtr user_name = handler->env_.send_attrs_->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
string expected = "User-Name=" + text;
string expected = "User-Name='" + text + "'";
EXPECT_EQ(expected, user_name->toText());
ConstAttributePtr calling_station_id =
handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
}
@ -1783,12 +1783,12 @@ TEST_F(AccessTest, buildHWAddr6) {
EXPECT_LE(2, handler->env_.send_attrs_->size());
ConstAttributePtr user_name = handler->env_.send_attrs_->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
string expected = "User-Name=" + text;
string expected = "User-Name='" + text + "'";
EXPECT_EQ(expected, user_name->toText());
ConstAttributePtr calling_station_id =
handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=08:00:27:58:f1:e8",
EXPECT_EQ("Calling-Station-Id='08:00:27:58:f1:e8'",
calling_station_id->toText());
}
@ -1809,7 +1809,7 @@ TEST_F(AccessTest, buildCanonHWAddr4) {
ConstAttributePtr calling_station_id =
handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
calling_station_id->toText());
}
@ -1830,7 +1830,7 @@ TEST_F(AccessTest, buildCanonHWAddr6) {
ConstAttributePtr calling_station_id =
handler->env_.send_attrs_->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=08-00-27-58-f1-e8",
EXPECT_EQ("Calling-Station-Id='08-00-27-58-f1-e8'",
calling_station_id->toText());
}

View File

@ -743,10 +743,10 @@ void AccountingTest::testBuildAcctLease4() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IP_ADDRESS);
ASSERT_TRUE(framed_ip_address);
@ -782,7 +782,7 @@ void AccountingTest::testBuildAcctLease4canon() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
calling_station_id->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -805,7 +805,7 @@ void AccountingTest::testBuildAcctLease4noDuid() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -827,7 +827,7 @@ void AccountingTest::testBuildAcctLease4noPop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -849,7 +849,7 @@ void AccountingTest::testBuildAcctLease4notPrintable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -873,7 +873,7 @@ void AccountingTest::testBuildAcctLease4Duid() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=05:06:07:08:09", user_name->toText());
EXPECT_EQ("User-Name='05:06:07:08:09'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -898,7 +898,7 @@ void AccountingTest::testBuildAcctLease4DuidPrintable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -920,7 +920,7 @@ void AccountingTest::testBuildAcctLease4Pop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -942,7 +942,7 @@ void AccountingTest::testBuildAcctLease4Pop0Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -962,7 +962,7 @@ void AccountingTest::testBuildAcctLease4noClientId() {
ASSERT_LE(4, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=20:e5:2a:b8:15:14", user_name->toText());
EXPECT_EQ("User-Name='20:e5:2a:b8:15:14'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
EXPECT_FALSE(calling_station_id);
EXPECT_EQ(0, attrs->count(PW_CLASS));
@ -1166,7 +1166,7 @@ void AccountingTest::testBuildAcctLease4ClassClientID() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct on IPv4/HwAddr can get the Class from host cache.
@ -1200,7 +1200,7 @@ void AccountingTest::testBuildAcctLease4ClassHwAddr() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct on IPv4/DUID can get the Class from host cache.
@ -1235,7 +1235,7 @@ void AccountingTest::testBuildAcctLease4ClassDuid() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct on IPv4/Flex can get the Class from host cache.
@ -1268,7 +1268,7 @@ void AccountingTest::testBuildAcctLease4ClassFlex() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct on IPv6 lease works.
@ -1292,10 +1292,10 @@ void AccountingTest::testBuildAcctLease6() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IPV6_ADDRESS);
ASSERT_TRUE(framed_ip_address);
@ -1335,10 +1335,10 @@ void AccountingTest::testBuildAcctLease6prefix() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
ConstAttributePtr delegated_prefix = attrs->get(PW_DELEGATED_IPV6_PREFIX);
ASSERT_TRUE(delegated_prefix);
@ -1376,7 +1376,7 @@ void AccountingTest::testBuildAcctLease6canon() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
calling_station_id->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1400,7 +1400,7 @@ void AccountingTest::testBuildAcctLease6noPop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
}
/// Verify that buildAcct on IPv6 lease works with not printable duid.
@ -1422,7 +1422,7 @@ void AccountingTest::testBuildAcctLease6notPrintable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1445,7 +1445,7 @@ void AccountingTest::testBuildAcctLease6Pop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1468,7 +1468,7 @@ void AccountingTest::testBuildAcctLease6Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1491,7 +1491,7 @@ void AccountingTest::testBuildAcctLease6Pop0Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1701,7 +1701,7 @@ void AccountingTest::testBuildAcctLease6ClassDUID() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct on IPv6/HwAddr can get the Class from host cache.
@ -1736,7 +1736,7 @@ void AccountingTest::testBuildAcctLease6ClassHwAddr() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct on IPv6/Flex can get the Class from host cache.
@ -1770,7 +1770,7 @@ void AccountingTest::testBuildAcctLease6ClassFlex() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct4 works.
@ -1794,10 +1794,10 @@ void AccountingTest::testBuildAcct4() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IP_ADDRESS);
ASSERT_TRUE(framed_ip_address);
@ -1872,7 +1872,7 @@ void AccountingTest::testBuildAcct4canon() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
calling_station_id->toText());
ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IP_ADDRESS);
EXPECT_EQ("Framed-IP-Address=192.0.2.1", framed_ip_address->toText());
@ -1897,7 +1897,7 @@ void AccountingTest::testBuildAcct4noPop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1919,7 +1919,7 @@ void AccountingTest::testBuildAcct4notPrintable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1941,7 +1941,7 @@ void AccountingTest::testBuildAcct4Pop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1963,7 +1963,7 @@ void AccountingTest::testBuildAcct4Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -1985,7 +1985,7 @@ void AccountingTest::testBuildAcct4Pop0Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2029,7 +2029,7 @@ void AccountingTest::testBuildAcct4noClientId() {
ASSERT_LE(4, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=20:e5:2a:b8:15:14", user_name->toText());
EXPECT_EQ("User-Name='20:e5:2a:b8:15:14'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2050,7 +2050,7 @@ void AccountingTest::testBuildAcct4noClientIdcanon() {
ASSERT_LE(4, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=20-e5-2a-b8-15-14", user_name->toText());
EXPECT_EQ("User-Name='20-e5-2a-b8-15-14'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2132,7 +2132,7 @@ void AccountingTest::testBuildAcct4ClassClientID() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct4 on HwAddr can get the Class from host cache.
@ -2167,7 +2167,7 @@ void AccountingTest::testBuildAcct4ClassHwAddr() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct4 on Flex can get the Class from host cache.
@ -2202,7 +2202,7 @@ void AccountingTest::testBuildAcct4ClassFlex() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct6 works.
@ -2226,10 +2226,10 @@ void AccountingTest::testBuildAcct6() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IPV6_ADDRESS);
ASSERT_TRUE(framed_ip_address);
@ -2270,10 +2270,10 @@ void AccountingTest::testBuildAcct6prefix() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20:e5:2a:b8:15:14",
EXPECT_EQ("Calling-Station-Id='20:e5:2a:b8:15:14'",
calling_station_id->toText());
ConstAttributePtr delegated_prefix = attrs->get(PW_DELEGATED_IPV6_PREFIX);
ASSERT_TRUE(delegated_prefix);
@ -2464,7 +2464,7 @@ void AccountingTest::testBuildAcct6canon() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr calling_station_id = attrs->get(PW_CALLING_STATION_ID);
ASSERT_TRUE(calling_station_id);
EXPECT_EQ("Calling-Station-Id=20-e5-2a-b8-15-14",
EXPECT_EQ("Calling-Station-Id='20-e5-2a-b8-15-14'",
calling_station_id->toText());
ConstAttributePtr framed_ip_address = attrs->get(PW_FRAMED_IPV6_ADDRESS);
EXPECT_EQ("Framed-IPv6-Address=2001:db8::1235",
@ -2491,7 +2491,7 @@ void AccountingTest::testBuildAcct6noPop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2514,7 +2514,7 @@ void AccountingTest::testBuildAcct6notPrintable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2537,7 +2537,7 @@ void AccountingTest::testBuildAcct6Pop0() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=01:02:03:04", user_name->toText());
EXPECT_EQ("User-Name='01:02:03:04'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2560,7 +2560,7 @@ void AccountingTest::testBuildAcct6Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2583,7 +2583,7 @@ void AccountingTest::testBuildAcct6Pop0Printable() {
ASSERT_LE(5, attrs->size());
ConstAttributePtr user_name = attrs->get(PW_USER_NAME);
ASSERT_TRUE(user_name);
EXPECT_EQ("User-Name=Foobar", user_name->toText());
EXPECT_EQ("User-Name='Foobar'", user_name->toText());
EXPECT_EQ(0, attrs->count(PW_CLASS));
}
@ -2690,7 +2690,7 @@ void AccountingTest::testBuildAcct6ClassDUID() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct6 on HwAddr can get the Class from host cache.
@ -2724,7 +2724,7 @@ void AccountingTest::testBuildAcct6ClassHwAddr() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that buildAcct6 on Flex can get the Class from host cache.
@ -2758,7 +2758,7 @@ void AccountingTest::testBuildAcct6ClassFlex() {
ASSERT_LE(6, attrs->size());
ConstAttributePtr cclass = attrs->get(PW_CLASS);
ASSERT_TRUE(cclass);
EXPECT_EQ("Class=foobar", cclass->toText());
EXPECT_EQ("Class='foobar'", cclass->toText());
}
/// Verify that lease4_select hook returns for fake allocations.

View File

@ -35,6 +35,9 @@ AttributeTest::compare(ConstAttributePtr first, ConstAttributePtr second) {
} else if (first->getValueType() == PW_TYPE_IPV6PREFIX) {
return ((first->toIpv6Prefix() == second->toIpv6Prefix()) &&
(first->toIpv6PrefixLen() == second->toIpv6PrefixLen()));
} else if (first->getValueType() == PW_TYPE_VSA) {
return ((first->toVendorId() == second->toVendorId()) &&
(first->toVsaData() == second->toVsaData()));
}
// Should not happen...
return (false);

View File

@ -28,22 +28,6 @@ using namespace isc::test;
namespace {
// Verify error cases for generic factory fromText.
TEST_F(AttributeTest, fromText) {
EXPECT_THROW_MSG(Attribute::fromText(""), BadValue,
"empty text attribute");
EXPECT_THROW_MSG(Attribute::fromText(" "), BadValue,
"blank text attribute ' '");
EXPECT_THROW_MSG(Attribute::fromText("foo-bar"), BadValue,
"can't find '=' in text attribute 'foo-bar'");
EXPECT_THROW_MSG(Attribute::fromText("=bar"), BadValue,
"empty attribute name in '=bar'");
EXPECT_THROW_MSG(Attribute::fromText("foo="), BadValue,
"empty attribute value in 'foo='");
EXPECT_THROW_MSG(Attribute::fromText("Foo-Bar=1"), NotFound,
"can't find attribute definition for 'Foo-Bar'");
}
// Verify error cases for factory fromText with definition.
TEST_F(AttributeTest, defFromText) {
AttrDefPtr def;
@ -107,18 +91,13 @@ TEST_F(AttributeTest, attrString) {
string to_string;
EXPECT_NO_THROW_LOG(to_string = attr->toString());
EXPECT_EQ("foobar", to_string);
EXPECT_EQ("User-Name=foobar", attr->toText());
EXPECT_EQ("User-Name='foobar'", attr->toText());
vector<uint8_t> binary = { 1, 8, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
EXPECT_EQ(binary, attr->toBytes());
string expected = "{ \"type\": 1, \"name\": \"User-Name\", ";
expected += " \"data\": \"foobar\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_text = Attribute::fromText("User-Name=foobar");
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
AttributePtr from_bytes = Attribute::fromBytes(binary);
ASSERT_TRUE(from_bytes);
EXPECT_TRUE(compare(from_bytes, attr))
@ -175,6 +154,10 @@ TEST_F(AttributeTest, attrString) {
"the attribute value type must be ipv6prefix, not string");
EXPECT_THROW_MSG(attr->toIpv6PrefixLen(), TypeError,
"the attribute value type must be ipv6prefix, not string");
EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
"the attribute value type must be vsa, not string");
EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
"the attribute value type must be vsa, not string");
}
// Verifies raw string attribute.
@ -188,18 +171,13 @@ TEST_F(AttributeTest, rawAttrString) {
string to_string;
EXPECT_NO_THROW_LOG(to_string = attr->toString());
EXPECT_EQ("\x01\x02\x03", to_string);
EXPECT_EQ("User-Name=\x01\x02\x03", attr->toText());
EXPECT_EQ("User-Name=0x010203", attr->toText());
vector<uint8_t> binary = { 1, 5, 1, 2, 3 };
EXPECT_EQ(binary, attr->toBytes());
string expected = "{ \"type\": 1, \"name\": \"User-Name\", ";
expected += " \"raw\": \"010203\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_text = Attribute::fromText("User-Name=\x01\x02\x03");
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
AttributePtr from_bytes = Attribute::fromBytes(binary);
ASSERT_TRUE(from_bytes);
EXPECT_TRUE(compare(from_bytes, attr))
@ -230,19 +208,6 @@ TEST_F(AttributeTest, attrInt) {
expected += " \"data\": \"15\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_text = Attribute::fromText("NAS-Port-Type=Ethernet");
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
AttributePtr attr2;
ASSERT_NO_THROW(attr2 = Attribute::fromInt(61, 1155));
ASSERT_TRUE(attr2);
AttributePtr from_text2 = Attribute::fromText("NAS-Port-Type=1155");
ASSERT_TRUE(from_text2);
EXPECT_TRUE(compare(from_text2, attr2))
<< from_text2->toText() << " != " << attr2->toText();
AttributePtr from_bytes = Attribute::fromBytes(binary);
ASSERT_TRUE(from_bytes);
EXPECT_TRUE(compare(from_bytes, attr))
@ -278,7 +243,12 @@ TEST_F(AttributeTest, attrInt) {
EXPECT_THROW_MSG(attr->toIpv6Prefix(), TypeError,
"the attribute value type must be ipv6prefix, not integer");
EXPECT_THROW_MSG(attr->toIpv6PrefixLen(), TypeError,
"the attribute value type must be ipv6prefix, not integer");
"the attribute value type must be ipv6prefix, not integer"
);
EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
"the attribute value type must be vsa, not integer");
EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
"the attribute value type must be vsa, not integer");
}
// Verifies IP address attribute.
@ -305,11 +275,6 @@ TEST_F(AttributeTest, attrIpAddr) {
expected += " \"data\": \"192.0.2.1\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_text = Attribute::fromText("Framed-IP-Address=192.0.2.1");
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
EXPECT_THROW_MSG(Attribute::fromIpAddr(8, IOAddress("2001:db8::1235")),
BadValue, "not v4 address 2001:db8::1235");
@ -349,6 +314,10 @@ TEST_F(AttributeTest, attrIpAddr) {
"the attribute value type must be ipv6prefix, not ipaddr");
EXPECT_THROW_MSG(attr->toIpv6PrefixLen(), TypeError,
"the attribute value type must be ipv6prefix, not ipaddr");
EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
"the attribute value type must be vsa, not ipaddr");
EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
"the attribute value type must be vsa, not ipaddr");
}
// Verifies IPv6 address attribute.
@ -377,19 +346,13 @@ TEST_F(AttributeTest, attrIpv6Addr) {
expected += " \"data\": \"2001:db8::1235\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_text =
Attribute::fromText("Framed-IPv6-Address=2001:db8::1235");
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
EXPECT_THROW_MSG(Attribute::fromIpv6Addr(168, IOAddress("192.0.2.1")),
BadValue, "not v6 address 192.0.2.1");
AttributePtr def_text = Attribute::fromText(def, "2001:db8::1235");
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
ASSERT_TRUE(def_text);
EXPECT_TRUE(compare(def_text, attr))
<< def_text->toText() << " != " << attr->toText();
AttributePtr from_bytes = Attribute::fromBytes(binary);
ASSERT_TRUE(from_bytes);
@ -426,6 +389,10 @@ TEST_F(AttributeTest, attrIpv6Addr) {
"the attribute value type must be ipv6prefix, not ipv6addr");
EXPECT_THROW_MSG(attr->toIpv6PrefixLen(), TypeError,
"the attribute value type must be ipv6prefix, not ipv6addr");
EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
"the attribute value type must be vsa, not ipv6addr");
EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
"the attribute value type must be vsa, not ipv6addr");
}
// Verifies IPv6 prefix attribute.
@ -457,28 +424,11 @@ TEST_F(AttributeTest, attrIpv6Prefix) {
expected += " \"data\": \"2001:db8::1235/128\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_text;
EXPECT_NO_THROW_LOG(from_text = Attribute::fromText("Delegated-IPv6-Prefix=2001:db8::1235/128"));
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
EXPECT_THROW_MSG(Attribute::fromText("Delegated-IPv6-Prefix=192.0.2.1/32"),
BadValue, "not v6 address 192.0.2.1");
AttributePtr def_text;
EXPECT_NO_THROW_LOG(def_text = Attribute::fromText(def, "2001:db8::1235/128"));
ASSERT_TRUE(from_text);
EXPECT_TRUE(compare(from_text, attr))
<< from_text->toText() << " != " << attr->toText();
string long_prefix = "Delegated-IPv6-Prefix=2001:db8::1235/300";
EXPECT_THROW_MSG(Attribute::fromText(long_prefix), BadValue,
"not 8 bit prefix length 2001:db8::1235/300");
long_prefix = "Delegated-IPv6-Prefix=2001:db8::1235/129";
EXPECT_THROW_MSG(Attribute::fromText(long_prefix), BadValue,
"too long prefix 129");
ASSERT_TRUE(def_text);
EXPECT_TRUE(compare(def_text, attr))
<< def_text->toText() << " != " << attr->toText();
AttributePtr from_bytes;
EXPECT_NO_THROW_LOG(from_bytes = Attribute::fromBytes(binary));
@ -533,6 +483,111 @@ TEST_F(AttributeTest, attrIpv6Prefix) {
"the attribute value type must be ipaddr, not ipv6prefix");
EXPECT_THROW_MSG(attr->toIpv6Addr(), TypeError,
"the attribute value type must be ipv6addr, not ipv6prefix");
EXPECT_THROW_MSG(attr->toVendorId(), TypeError,
"the attribute value type must be vsa, not ipv6prefix");
EXPECT_THROW_MSG(attr->toVsaData(), TypeError,
"the attribute value type must be vsa, not ipv6prefix");
}
// Verifies vsa attribute.
TEST_F(AttributeTest, attrVsa) {
// Using Vector-Specific (26) *only* vsa attribute.
AttrDefPtr def = AttrDefs::instance().getByType(PW_VENDOR_SPECIFIC);
ASSERT_TRUE(def);
EXPECT_EQ(26, def->type_);
EXPECT_EQ(PW_TYPE_VSA, def->value_type_);
AttributePtr attr;
ASSERT_NO_THROW(attr = Attribute::fromVsa(PW_VENDOR_SPECIFIC,
1234, "foobar"));
ASSERT_TRUE(attr);
EXPECT_EQ(26, attr->getType());
EXPECT_EQ(PW_TYPE_VSA, attr->getValueType());
uint32_t vendor = 0;
ASSERT_NO_THROW(vendor = attr->toVendorId());
EXPECT_EQ(1234, vendor);
EXPECT_EQ("Vendor-Specific=[1234]0x666F6F626172", attr->toText());
vector<uint8_t> binary = { 26, 12, 0, 0, 0x04, 0xd2,
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
EXPECT_EQ(binary, attr->toBytes());
string expected = "{ \"type\": 26, \"name\": \"Vendor-Specific\", ";
expected += " \"vendor\": \"1234\", \"vsa-raw\": \"666F6F626172\" }";
runToElementTest<Attribute>(expected, *attr);
AttributePtr from_bytes = Attribute::fromBytes(binary);
ASSERT_TRUE(from_bytes);
EXPECT_TRUE(compare(from_bytes, attr))
<< from_bytes->toText() << " != " << attr->toText();
vector<uint8_t> value = { 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
AttributePtr def_value = Attribute::fromVsa(PW_VENDOR_SPECIFIC,
1234, value);
ASSERT_TRUE(def_value);
EXPECT_TRUE(compare(def_value, attr))
<< def_value->toText() << " != " << attr->toText();
vector<uint8_t> bytes = { 0, 0, 0x04, 0xd2,
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
AttributePtr def_bytes = Attribute::fromBytes(def, bytes);
ASSERT_TRUE(def_bytes);
EXPECT_TRUE(compare(def_bytes, attr))
<< def_bytes->toText() << " != " << attr->toText();
EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234, ""),
BadValue, "value is empty");
vector<uint8_t> empty;
EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234, empty),
BadValue, "value is empty");
vector<uint8_t> small = { 26, 6, 0, 0, 0x4, 0xd2 };
EXPECT_THROW_MSG(Attribute::fromBytes(small), BadValue,
"value is too small 4 < 5");
vector<uint8_t> small2 = { 0, 0, 0x4, 0xd2 };
EXPECT_THROW_MSG(Attribute::fromBytes(def, small2), BadValue,
"value is too small 4 < 5");
string big_text(MAX_VSA_DATA_LEN, 'x');
EXPECT_NO_THROW_LOG(Attribute::fromVsa(PW_VENDOR_SPECIFIC,
1234, big_text));
vector<uint8_t> big_binary(MAX_VSA_DATA_LEN, 0x78);
EXPECT_NO_THROW_LOG(Attribute::fromVsa(PW_VENDOR_SPECIFIC,
1234, big_binary));
vector<uint8_t> big_value(MAX_STRING_LEN, 0x87);
EXPECT_NO_THROW_LOG(Attribute::fromBytes(def, big_value));
string too_big_text(MAX_VSA_DATA_LEN + 1, 'x');
EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234,
too_big_text), BadValue,
"value is too large 250 > 249");
vector<uint8_t> too_big_binary(MAX_VSA_DATA_LEN + 1, 0x87);
EXPECT_THROW_MSG(Attribute::fromVsa(PW_VENDOR_SPECIFIC, 1234,
too_big_binary), BadValue,
"value is too large 250 > 249");
vector<uint8_t> too_bigvalue(MAX_STRING_LEN + 1, 0x87);
EXPECT_THROW_MSG(Attribute::fromBytes(def, too_bigvalue), BadValue,
"value is too large 254 > 253");
EXPECT_THROW_MSG(attr->toString(), TypeError,
"the attribute value type must be string, not vsa");
EXPECT_THROW_MSG(attr->toBinary(), TypeError,
"the attribute value type must be string, not vsa");
EXPECT_THROW_MSG(attr->toInt(), TypeError,
"the attribute value type must be integer, not vsa");
EXPECT_THROW_MSG(attr->toIpAddr(), TypeError,
"the attribute value type must be ipaddr, not vsa");
EXPECT_THROW_MSG(attr->toIpv6Addr(), TypeError,
"the attribute value type must be ipv6addr, not vsa");
EXPECT_THROW_MSG(attr->toIpv6Prefix(), TypeError,
"the attribute value type must be ipv6prefix, not vsa");
EXPECT_THROW_MSG(attr->toIpv6PrefixLen(), TypeError,
"the attribute value type must be ipv6prefix, not vsa");
}
// Verifies basic methods for attribute collection.
@ -593,8 +648,8 @@ TEST_F(AttributeTest, attributesAddDel) {
attr.reset();
// toText.
string expected = "User-Name=foobar,\nUser-Name=foo,\n";
expected += "Service-Type=20,\nUser-Name=bar";
string expected = "User-Name='foobar',\nUser-Name='foo',\n";
expected += "Service-Type=20,\nUser-Name='bar'";
string got;
ASSERT_NO_THROW(got = attrs.toText());
EXPECT_EQ(expected, got) << expected << "\n" << got << "\n";

View File

@ -792,7 +792,7 @@ TEST_F(ConfigTest, attribute) {
ASSERT_EQ(1, attrs.size());
const ConstAttributePtr& first = *attrs.cbegin();
ASSERT_TRUE(first);
EXPECT_EQ("User-Name=foobar", first->toText());
EXPECT_EQ("User-Name='foobar'", first->toText());
// Another way to check.
string expected = "[ { "
@ -801,6 +801,15 @@ TEST_F(ConfigTest, attribute) {
" \"data\": \"foobar\" } ]";
runToElementTest<CfgAttributes>(expected, srv->attributes_);
// Vendor-Specific (26) does not support textual data.
srv->attributes_.clear();
attr = Element::createMap();
attr->set("data", Element::create("foobar"));
attr->set("type", Element::create(26));
expected = "can't create Vendor-Specific attribute from [foobar]: ";
expected += "Can't decode vsa from text";
EXPECT_THROW_MSG(parser.parse(srv, attr), ConfigError, expected);
// One of expr, data, raw
srv->attributes_.clear();
attr = Element::createMap();
@ -845,9 +854,7 @@ TEST_F(ConfigTest, attribute) {
EXPECT_EQ("", srv->attributes_.getTest(1));
const ConstAttributePtr& firstr = srv->attributes_.get(1);
ASSERT_TRUE(firstr);
expected = "User-Name=f\x01\x02";
expected += "bar";
EXPECT_EQ(expected, firstr->toText());
EXPECT_EQ("User-Name=0x660102626172", firstr->toText());
expected = "[ { "
" \"name\": \"User-Name\", "
" \"type\": 1, "

View File

@ -47,29 +47,49 @@ public:
/// @brief Destructor.
virtual ~DictionaryTest() {
AttrDefs::instance().clear();
static_cast<void>(remove(TEST_DICT));
}
/// @brief Parse a line.
///
/// @param line line to parse.
void parseLine(const string& line) {
/// @param depth recursion depth.
void parseLine(const string& line, unsigned int depth = 0) {
istringstream is(line + "\n");
AttrDefs::instance().readDictionary(is);
AttrDefs::instance().readDictionary(is, depth);
}
/// @brief Parse a list of lines.
///
/// @param lines list of lines.
void parseLines(const list<string>& lines) {
/// @param depth recursion depth.
void parseLines(const list<string>& lines, unsigned int depth = 0) {
string content;
for (auto const& line : lines) {
content += line + "\n";
}
istringstream is(content);
AttrDefs::instance().readDictionary(is);
AttrDefs::instance().readDictionary(is, depth);
}
/// @brief writes specified content to a file.
///
/// @param file_name name of file to be written.
/// @param content content to be written to file.
void writeFile(const std::string& file_name, const std::string& content) {
static_cast<void>(remove(file_name.c_str()));
ofstream out(file_name.c_str(), ios::trunc);
EXPECT_TRUE(out.is_open());
out << content;
out.close();
}
/// Name of a dictionary file used during tests.
static const char* TEST_DICT;
};
const char* DictionaryTest::TEST_DICT = "test-dict";
// Verifies standards definitions can be read from the dictionary.
TEST_F(DictionaryTest, standard) {
ASSERT_NO_THROW_LOG(AttrDefs::instance().readDictionary(TEST_DICTIONARY));
@ -111,8 +131,22 @@ TEST_F(DictionaryTest, parseLine) {
"expected 4 tokens, got 3 at line 1");
EXPECT_THROW_MSG(parseLine("VALUE My-Attribute My-Value 1"), BadValue,
"unknown attribute 'My-Attribute' at line 1");
EXPECT_THROW_MSG(parseLine("VENDOR my-vendor 4417"), BadValue,
"unknown dictionary entry 'VENDOR' at line 1");
EXPECT_THROW_MSG(parseLine("$INCLUDE"), BadValue,
"expected 2 tokens, got 1 at line 1");
EXPECT_THROW_MSG(parseLine("$INCLUDE foo bar"), BadValue,
"expected 2 tokens, got 3 at line 1");
EXPECT_THROW_MSG(parseLine("VENDOR my-vendor"), BadValue,
"expected 3 tokens, got 2 at line 1");
EXPECT_THROW_MSG(parseLine("VENDOR my-vendor 44 17"), BadValue,
"expected 3 tokens, got 4 at line 1");
EXPECT_THROW_MSG(parseLine("VENDOR my-vendor 0"), BadValue,
"0 is reserved at line 1");
EXPECT_THROW_MSG(parseLine("BEGIN-VENDOR my-vendor"), BadValue,
"unknown dictionary entry 'BEGIN-VENDOR' at line 1");
EXPECT_THROW_MSG(parseLine("END-VENDOR my-vendor"), BadValue,
"unknown dictionary entry 'END-VENDOR' at line 1");
}
// Verifies sequences attribute of (re)definitions.
@ -148,6 +182,19 @@ TEST_F(DictionaryTest, parseLines) {
expected = "Illegal attribute redefinition of 'Service-Type' ";
expected += "type 6 value type integer by 6 string at line 2";
EXPECT_THROW_MSG(parseLines(new_value_type), BadValue, expected);
// Only the attribute 26 (Vendor-Specific) can have the vsa data type.
list<string> bad_vsa = {
"ATTRIBUTE Attr126 126 vsa"
};
expected = "only Vendor-Specific (26) attribute can have ";
expected += "the vsa data type at line 1";
EXPECT_THROW_MSG(parseLines(bad_vsa), BadValue, expected);
list<string> vsa = {
"ATTRIBUTE Attr26 26 vsa"
};
EXPECT_NO_THROW_LOG(parseLines(vsa));
}
// Verifies integer constant definitions.
@ -196,12 +243,49 @@ TEST_F(DictionaryTest, integerConstant) {
EXPECT_THROW_MSG(parseLines(new_value), BadValue, expected);
}
// Verifies vendor id definitions.
TEST_F(DictionaryTest, vendorId) {
// Value must be an integer.
list<string> not_integer_val = {
"VENDOR My-Value Non-Integer"
};
EXPECT_THROW_MSG(parseLines(not_integer_val), BadValue,
"can't parse integer value Non-Integer at line 1");
// Positive case.
list<string> positive = {
"VENDOR ISC 2495"
};
EXPECT_NO_THROW_LOG(parseLines(positive));
// Redefine the same vendor id.
list<string> same = {
"VENDOR ISC 2495",
"VENDOR ISC 2495"
};
EXPECT_NO_THROW_LOG(parseLines(same));
// Redefine with a different value is not allowed.
list<string> new_value = {
"VENDOR ISC 2495",
"VENDOR ISC 24950",
};
string expected = "Illegal vendor id redefinition of ";
expected += "'ISC' value 2495 by 24950 at line 2";
EXPECT_THROW_MSG(parseLines(new_value), BadValue, expected);
}
// Verifies errors from bad dictionary files.
TEST_F(DictionaryTest, badFile) {
string expected = "can't open dictionary '/does-not-exist': ";
expected += "No such file or directory";
EXPECT_THROW_MSG(AttrDefs::instance().readDictionary("/does-not-exist"),
BadValue, expected);
list<string> bad_include = {
"$INCLUDE /does-not-exist"
};
expected += " at line 1";
EXPECT_THROW_MSG(parseLines(bad_include), BadValue, expected);
}
// Definitions of Standard attributes used by the hook.
@ -213,6 +297,39 @@ TEST_F(DictionaryTest, hookAttributes) {
checkStandardDefs(RadiusConfigParser::USED_STANDARD_ATTR_DEFS));
}
// Verifies the $INCLUDE entry.
TEST_F(DictionaryTest, include) {
list<string> include;
include.push_back("# Including the dictonary");
include.push_back(string("$INCLUDE ") + string(TEST_DICTIONARY));
include.push_back("# Dictionary included");
include.push_back("VENDOR ISC 2495");
EXPECT_NO_THROW_LOG(parseLines(include));
EXPECT_NO_THROW_LOG(AttrDefs::instance().
checkStandardDefs(RadiusConfigParser::USED_STANDARD_ATTR_DEFS));
auto isc = AttrDefs::instance().getByName(PW_VENDOR_SPECIFIC, "ISC");
ASSERT_TRUE(isc);
EXPECT_EQ(2495, isc->value_);
// max depth is 5.
EXPECT_THROW_MSG(parseLines(include, 4), BadValue,
"Too many nested $INCLUDE at line 2");
}
// Verifies the $INCLUDE entry can't eat the stack.
TEST_F(DictionaryTest, includeLimit) {
string include = "$INCLUDE " + string(TEST_DICT) + "\n";
writeFile(TEST_DICT, include);
string expected = "Too many nested $INCLUDE ";
expected += "at line 1 in dictionary 'test-dict', ";
expected += "at line 1 in dictionary 'test-dict', ";
expected += "at line 1 in dictionary 'test-dict', ";
expected += "at line 1 in dictionary 'test-dict', ";
expected += "at line 1";
EXPECT_THROW_MSG(parseLine(string("$INCLUDE ") + string(TEST_DICT)),
BadValue, expected);
}
namespace {
// RAII device freeing the glob buffer when going out of scope.

View File

@ -743,7 +743,7 @@ TEST_F(RequestTest, accept) {
ASSERT_EQ(1, received_attributes_->count(1));
const ConstAttributePtr& attr = received_attributes_->get(1);
ASSERT_TRUE(attr);
EXPECT_EQ("User-Name=user", attr->toText());
EXPECT_EQ("User-Name='user'", attr->toText());
}
/// Verify what happens with Accounting-Response response.
@ -938,7 +938,7 @@ TEST_F(RequestTest, badAccept) {
ASSERT_EQ(1, received_attributes_->count(1));
const ConstAttributePtr& attr = received_attributes_->get(1);
ASSERT_TRUE(attr);
EXPECT_EQ("User-Name=user", attr->toText());
EXPECT_EQ("User-Name='user'", attr->toText());
}
/// Verify what happens with bad Accounting-Response response.
@ -1326,7 +1326,7 @@ TEST_F(RequestTest, reject) {
ASSERT_EQ(1, received_attributes_->count(1));
const ConstAttributePtr& attr = received_attributes_->get(1);
ASSERT_TRUE(attr);
EXPECT_EQ("User-Name=user", attr->toText());
EXPECT_EQ("User-Name='user'", attr->toText());
}
/// Verify what happens with a backup authentication server.
@ -1427,7 +1427,7 @@ TEST_F(RequestTest, accept2) {
ASSERT_EQ(1, received_attributes_->count(1));
const ConstAttributePtr& attr = received_attributes_->get(1);
ASSERT_TRUE(attr);
EXPECT_EQ("User-Name=user", attr->toText());
EXPECT_EQ("User-Name='user'", attr->toText());
}
/// Verify what happens with a backup accounting server.