if (!def) {
isc_throw(BadValue, "no attribute definition");
}
- container_.insert(pair<const uint8_t, AttributeValue>
- (def->type_, AttributeValue(def, attr, expr, test)));
+ container_.insert(AttributeValue(def, attr, expr, test));
}
bool
-CfgAttributes::del(const uint8_t type) {
- auto it = container_.find(type);
+CfgAttributes::del(const uint8_t type, const uint32_t vendor) {
+ auto it = container_.find(boost::make_tuple(vendor, type));
if (it != container_.end()) {
container_.erase(it);
return (true);
}
AttrDefPtr
-CfgAttributes::getDef(const uint8_t type) const {
- auto it = container_.find(type);
+CfgAttributes::getDef(const uint8_t type, const uint32_t vendor) const {
+ auto it = container_.find(boost::make_tuple(vendor, type));
if (it == container_.end()) {
return (AttrDefPtr());
}
- return (it->second.def_);
+ return (it->def_);
}
ConstAttributePtr
-CfgAttributes::get(const uint8_t type) const {
- auto it = container_.find(type);
+CfgAttributes::get(const uint8_t type, const uint32_t vendor) const {
+ auto it = container_.find(boost::make_tuple(vendor, type));
if (it == container_.end()) {
return (AttributePtr());
}
- return (it->second.attr_);
+ return (it->attr_);
}
ExpressionPtr
-CfgAttributes::getExpr(const uint8_t type) const {
- auto it = container_.find(type);
+CfgAttributes::getExpr(const uint8_t type, const uint32_t vendor) const {
+ auto it = container_.find(boost::make_tuple(vendor, type));
if (it == container_.end()) {
return (ExpressionPtr());
}
- return (it->second.expr_);
+ return (it->expr_);
}
string
-CfgAttributes::getTest(const uint8_t type) const {
- auto it = container_.find(type);
+CfgAttributes::getTest(const uint8_t type, const uint32_t vendor) const {
+ auto it = container_.find(boost::make_tuple(vendor, type));
if (it == container_.end()) {
return ("");
}
- return (it->second.test_);
+ return (it->test_);
}
Attributes
CfgAttributes::getAll() const {
Attributes attrs;
for (auto const& it : container_) {
- attrs.add(it.second.attr_);
+ attrs.add(it.attr_);
}
return (attrs);
}
CfgAttributes::getEvalAll(Pkt& pkt) {
Attributes attrs;
for (auto const& it : container_) {
- const ExpressionPtr& match_expr = it.second.expr_;
+ const ExpressionPtr& match_expr = it.expr_;
if (!match_expr) {
- attrs.add(it.second.attr_);
+ attrs.add(it.attr_);
continue;
}
string value = evaluateString(*match_expr, pkt);
if (value.empty()) {
continue;
}
- AttrDefPtr def = it.second.def_;
+ AttrDefPtr def = it.def_;
if (!def) {
continue;
}
CfgAttributes::toElement() const {
ElementPtr result = Element::createList();
for (auto const& it : container_) {
- AttrDefPtr def = it.second.def_;
+ AttrDefPtr def = it.def_;
if (!def) {
continue;
}
ElementPtr map;
- if (!it.second.test_.empty()) {
+ if (!it.test_.empty()) {
map = Element::createMap();
- map->set("type", Element::create(static_cast<int>(it.first)));
- map->set("expr", Element::create(it.second.test_));
+ map->set("type", Element::create(static_cast<int>(def->type_)));
+ map->set("expr", Element::create(it.test_));
map->set("name", Element::create(def->name_));
- } else if (it.second.attr_) {
- map = it.second.attr_->toElement();
+ } else if (it.attr_) {
+ map = it.attr_->toElement();
}
result->add(map);
}
#include <dhcp/pkt.h>
#include <eval/token.h>
#include <util/buffer.h>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/multi_index/composite_key.hpp>
#include <boost/shared_ptr.hpp>
#include <vector>
#include <map>
/// Deletes only the first occurrence.
///
/// @param type type of the attribute to delete.
+ /// @param vendor vendor id (default 0).
/// @return true if found.
- bool del(const uint8_t type);
+ bool del(const uint8_t type, const uint32_t vendor = 0);
/// @brief Clear the container.
void clear() {
/// @brief Counts instance of the attribute in the configuration.
///
/// @param type type of the attribute to count.
- /// @return count of attributes with the given type in the configuration.
- size_t count(const uint8_t type) const {
- return (container_.count(type));
+ /// @param vendor vendor id (default 0).
+ /// @return count of attributes with the given type and vendor
+ /// in the configuration.
+ size_t count(const uint8_t type, const uint32_t vendor = 0) const {
+ return (container_.count(boost::make_tuple(vendor, type)));
}
/// @brief Returns the definition of an attribute.
///
/// @param type type of the attribute.
+ /// @param vendor vendor id (default 0).
/// @return the definition of the attribute.
- AttrDefPtr getDef(const uint8_t type) const;
+ AttrDefPtr getDef(const uint8_t type, const uint32_t vendor = 0) const;
/// @brief Get instance of the attribute in the configuration.
///
/// @param type type of the attribute to retrieve.
+ /// @param vendor vendor id (default 0).
/// @return the first instance if exists.
- ConstAttributePtr get(const uint8_t type) const;
+ ConstAttributePtr get(const uint8_t type, const uint32_t vendor = 0) const;
/// @brief Returns the expression of an attribute.
///
/// @param type type of the attribute.
+ /// @param vendor vendor id (default 0).
/// @return the expression of the attribute.
- dhcp::ExpressionPtr getExpr(const uint8_t type) const;
+ dhcp::ExpressionPtr getExpr(const uint8_t type,
+ const uint32_t vendor = 0) const;
/// @brief Returns the text expression of an attribute.
///
/// @param type type of the attribute.
+ /// @param vendor vendor id (default 0).
/// @return the text expression of the attribute.
- std::string getTest(const uint8_t type) const;
+ std::string getTest(const uint8_t type, const uint32_t vendor = 0) const;
/// @brief Get all attributes in the configuration.
///
/// @brief Original expression.
std::string test_;
+
+ /// @brief Return the type.
+ uint8_t getType() const {
+ if (!def_) {
+ isc_throw(BadValue, "no attribute definition");
+ }
+ return (def_->type_);
+ }
+
+ /// @brief Return the vendor.
+ uint32_t getVendor() const {
+ if (!def_) {
+ isc_throw(BadValue, "no attribute definition");
+ }
+ return (def_->vendor_);
+ }
};
/// @brief The container.
- std::multimap<uint8_t, AttributeValue> container_;
+ boost::multi_index_container<
+ // This container stores AttributeValue objects.
+ AttributeValue,
+ // Start specification of indexes here.
+ boost::multi_index::indexed_by<
+ // Hash index for by vendor and type.
+ boost::multi_index::hashed_non_unique<
+ boost::multi_index::composite_key<
+ AttributeValue,
+ boost::multi_index::const_mem_fun<
+ AttributeValue, uint32_t, &AttributeValue::getVendor
+ >,
+ boost::multi_index::const_mem_fun<
+ AttributeValue, uint8_t, &AttributeValue::getType
+ >
+ >
+ >
+ >
+ > container_;
};
} // end of namespace isc::radius
if (value.empty()) {
isc_throw(BadValue, "empty attribute value");
}
+ AttributePtr attr = fromText0(def, value);
+ if (def->vendor_ == 0) {
+ return (attr);
+ }
+ // Encapsulate into a Vendor-Specific attribute.
+ const vector<uint8_t> vsa_data = attr->toBytes();
+ return (fromVsa(PW_VENDOR_SPECIFIC, def->vendor_, vsa_data));
+}
+
+AttributePtr
+Attribute::fromText0(const AttrDefPtr& def, const string& value) {
switch (static_cast<uint8_t>(def->value_type_)) {
case PW_TYPE_STRING:
return (AttrString::fromText(def->type_, value));
if (value.empty()) {
isc_throw(BadValue, "empty attribute value");
}
+ AttributePtr attr = fromBytes0(def, value);
+ if (def->vendor_ == 0) {
+ return (attr);
+ }
+ // Encapsulate into a Vendor-Specific attribute.
+ const vector<uint8_t> vsa_data = attr->toBytes();
+ return (fromVsa(PW_VENDOR_SPECIFIC, def->vendor_, vsa_data));
+}
+
+AttributePtr
+Attribute::fromBytes0(const AttrDefPtr& def, const vector<uint8_t>& value) {
switch (static_cast<uint8_t>(def->value_type_)) {
case PW_TYPE_STRING:
return (AttrString::fromBytes(def->type_, value));
<< attrValueTypeToText(getValueType()));
}
-string
+std::vector<uint8_t>
Attribute::toVsaData() const {
isc_throw(TypeError, "the attribute value type must be vsa, not "
<< attrValueTypeToText(getValueType()));
return (output);
}
+std::vector<uint8_t>
+AttrVsa::toVsaData() const {
+ vector<uint8_t> binary;
+ binary.resize(value_.size());
+ if (binary.size() > 0) {
+ memmove(&binary[0], &value_[0], binary.size());
+ }
+ return (binary);
+}
+
ElementPtr
AttrVsa::toElement() const {
ElementPtr output = Element::createMap();
/// From definition generic factories.
- /// @brief From text with definition.
+ /// @brief From text with definition (handle vendor).
+ ///
+ /// Handle Vendor-Specific encapsulation.
///
/// @param def pointer to attribute definition.
/// @param value textual value.
static AttributePtr fromText(const AttrDefPtr& def,
const std::string& value);
- /// @brief From bytes with definition.
+ /// @brief From bytes with definition (handle vendor).
+ ///
+ /// Handle Vendor-Specific encapsulation.
///
/// @param def pointer to attribute definition.
/// @param value binary value.
///
/// @return the vsa data.
/// @throw TypeError if the attribute is not a vsa one.
- virtual std::string toVsaData() const;
+ virtual std::vector<uint8_t> toVsaData() const;
/// @brief Type.
const uint8_t type_;
+
+private:
+
+ /// @brief From text with definition.
+ ///
+ /// @param def pointer to attribute definition.
+ /// @param value textual value.
+ /// @return pointer to the attribute.
+ /// @throw BadValue on errors.
+ static AttributePtr fromText0(const AttrDefPtr& def,
+ const std::string& value);
+
+ /// @brief From bytes with definition.
+ ///
+ /// @param def pointer to attribute definition.
+ /// @param value binary value.
+ /// @return pointer to the attribute.
+ /// @throw BadValue on errors.
+ static AttributePtr fromBytes0(const AttrDefPtr& def,
+ const std::vector<uint8_t>& value);
};
/// @brief RADIUS attribute derived classes: do not use them directly
/// @brief To vsa data.
///
/// @return the vsa data.
- virtual std::string toVsaData() const override {
- return (value_);
- }
+ virtual std::vector<uint8_t> toVsaData() const override;
/// @brief Unparse attribute.
///
"the attribute value type must be ipv6prefix, not vsa");
}
+// Verifies vendor fromText.
+TEST_F(AttributeTest, vendorFromText) {
+ // Using DSL-Forum (3561) Agent-Circuit-Id (1),
+ AttrDefPtr def(new AttrDef(1, "Agent-Circuit-Id", PW_TYPE_STRING, 3561));
+ AttributePtr attr;
+ ASSERT_NO_THROW(attr = Attribute::fromText(def, "foobar"));
+ ASSERT_TRUE(attr);
+ EXPECT_EQ(PW_VENDOR_SPECIFIC, attr->getType());
+ EXPECT_EQ(PW_TYPE_VSA, attr->getValueType());
+ EXPECT_EQ(3561, attr->toVendorId());
+ EXPECT_EQ(12, attr->getValueLen());
+ vector<uint8_t> vsa_data = { 1, 8, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72 };
+ EXPECT_EQ(vsa_data, attr->toVsaData());
+}
+
+// Verifies vendor fromBytes.
+TEST_F(AttributeTest, vendorFromBytes) {
+ // Using DSL-Forum (3561) Access-Loop-Encapsulation (144),
+ AttrDefPtr def(new AttrDef(144, "Access-Loop-Encapsulation",
+ PW_TYPE_STRING, 3561));
+ AttributePtr attr;
+ vector<uint8_t> value = { 2, 0, 0 };
+ ASSERT_NO_THROW(attr = Attribute::fromBytes(def, value));
+ ASSERT_TRUE(attr);
+ EXPECT_EQ(PW_VENDOR_SPECIFIC, attr->getType());
+ EXPECT_EQ(PW_TYPE_VSA, attr->getValueType());
+ EXPECT_EQ(3561, attr->toVendorId());
+ EXPECT_EQ(9, attr->getValueLen());
+ vector<uint8_t> vsa_data = { 144, 5, 2, 0, 0 };
+ EXPECT_EQ(vsa_data, attr->toVsaData());
+}
+
// Verifies basic methods for attribute collection.
TEST_F(AttributeTest, attributesBasic) {
Attributes attrs;
EXPECT_EQ(1, def->type_);
EXPECT_EQ("User-Name", def->name_);
EXPECT_EQ(PW_TYPE_STRING, def->value_type_);
+ EXPECT_EQ(0, def->vendor_);
def.reset();
// Type 0 is reserved.
EXPECT_FALSE(def);
def.reset();
+ // Only vendor 0 i.e. no vendor was populated.
+ ASSERT_NO_THROW(def = AttrDefs::instance().getByType(1, 1234));
+ EXPECT_FALSE(def);
+ def.reset();
+
// getByName.
ASSERT_NO_THROW(def = AttrDefs::instance().getByName("User-Name"));
ASSERT_TRUE(def);
EXPECT_EQ(1, def->type_);
EXPECT_EQ("User-Name", def->name_);
EXPECT_EQ(PW_TYPE_STRING, def->value_type_);
+ EXPECT_EQ(0, def->vendor_);
def.reset();
ASSERT_NO_THROW(def = AttrDefs::instance().getByName("Does-not-exist"));
EXPECT_FALSE(def);
def.reset();
+ ASSERT_NO_THROW(def = AttrDefs::instance().getByName("User-Name", 1234));
+ EXPECT_FALSE(def);
+ def.reset();
+
// getName.
string name;
ASSERT_NO_THROW(name = AttrDefs::instance().getName(1));
name.clear();
ASSERT_NO_THROW(name = AttrDefs::instance().getName(252));
EXPECT_EQ("Attribute-252", name);
+ name.clear();
+ ASSERT_NO_THROW(name = AttrDefs::instance().getName(1, 1234));
+ EXPECT_EQ("Attribute-1", name);
+ name.clear();
// add (new).
AttrDefPtr def1(new AttrDef(252, "Foo-Bar", PW_TYPE_IPADDR));
EXPECT_EQ(252, def->type_);
EXPECT_EQ("Foo-Bar", def->name_);
EXPECT_EQ(PW_TYPE_IPADDR, def->value_type_);
+ EXPECT_EQ(0, def->vendor_);
def.reset();
ASSERT_NO_THROW(def = AttrDefs::instance().getByName("Foo-Bar"));
ASSERT_TRUE(def);
EXPECT_EQ(252, def->type_);
EXPECT_EQ("Foo-Bar", def->name_);
EXPECT_EQ(PW_TYPE_IPADDR, def->value_type_);
+ EXPECT_EQ(0, def->vendor_);
def.reset();
// add (alias).
ASSERT_TRUE(got);
EXPECT_EQ(18, got->type_);
EXPECT_EQ(PW_TYPE_STRING, got->value_type_);
+ EXPECT_EQ(0, got->vendor_);
+ def.reset();
+
+ // add (vendor).
+ AttrDefPtr defv(new AttrDef(1, "Agent-Circuit-Id", PW_TYPE_STRING, 3561));
+ ASSERT_NO_THROW(AttrDefs::instance().add(defv));
+ ASSERT_NO_THROW(def = AttrDefs::instance().getByType(1, 3561));
+ ASSERT_TRUE(def);
+ EXPECT_EQ(1, def->type_);
+ EXPECT_EQ("Agent-Circuit-Id", def->name_);
+ EXPECT_EQ(PW_TYPE_STRING, def->value_type_);
+ EXPECT_EQ(3561, def->vendor_);
+ def.reset();
+ ASSERT_NO_THROW(def =
+ AttrDefs::instance().getByName("Agent-Circuit-Id", 3561));
+ ASSERT_TRUE(def);
+ EXPECT_EQ(1, def->type_);
+ EXPECT_EQ("Agent-Circuit-Id", def->name_);
+ EXPECT_EQ(PW_TYPE_STRING, def->value_type_);
+ EXPECT_EQ(3561, def->vendor_);
+ def.reset();
+ ASSERT_NO_THROW(name = AttrDefs::instance().getName(1, 3561));
+ EXPECT_EQ("Agent-Circuit-Id", name);
+ name.clear();
// add (change type).
ASSERT_NO_THROW(def = AttrDefs::instance().getByName("User-Password"));
ASSERT_TRUE(def);
EXPECT_EQ(2, def->type_);
EXPECT_EQ(PW_TYPE_STRING, def->value_type_);
+ EXPECT_EQ(0, def->vendor_);
AttrDefPtr def3(new AttrDef(17, "User-Password", PW_TYPE_STRING));
string expected = "Illegal attribute redefinition of ";
expected += "'User-Password' type 2 value type string by 17 string";
expected += "type 2 value type string by 'Password' 2 integer";
EXPECT_THROW_MSG(AttrDefs::instance().add(def5), BadValue, expected);
+ // Same with vendor.
+ ASSERT_NO_THROW(def =
+ AttrDefs::instance().getByName("Agent-Circuit-Id", 3561));
+ ASSERT_TRUE(def);
+ EXPECT_EQ(1, def->type_);
+ EXPECT_EQ(PW_TYPE_STRING, def->value_type_);
+ EXPECT_EQ(3561, def->vendor_);
+ AttrDefPtr def3v(new AttrDef(2, "Agent-Circuit-Id", PW_TYPE_STRING, 3561));
+ expected = "Illegal attribute redefinition of 'Agent-Circuit-Id' ";
+ expected += "vendor 3561 type 1 value type string by 2 string";
+ EXPECT_THROW_MSG(AttrDefs::instance().add(def3v), BadValue, expected);
+ AttrDefPtr def4v(new AttrDef(1, "Agent-Circuit-Id", PW_TYPE_INTEGER, 3561));
+ expected = "Illegal attribute redefinition of 'Agent-Circuit-Id' ";
+ expected += "vendor 3561 type 1 value type string by 1 integer";
+ EXPECT_THROW_MSG(AttrDefs::instance().add(def4v), BadValue, expected);
+ AttrDefPtr def5v(new AttrDef(1, "Agent-Remote-Id", PW_TYPE_INTEGER, 3561));
+ expected = "Illegal attribute redefinition of 'Agent-Circuit-Id' ";
+ expected += "vendor 3561 type 1 value type string by ";
+ expected += "'Agent-Remote-Id' 1 integer";
+ EXPECT_THROW_MSG(AttrDefs::instance().add(def5v), BadValue, expected);
+
// clear.
ASSERT_NO_THROW(AttrDefs::instance().clear());
ASSERT_NO_THROW(def = AttrDefs::instance().getByType(1));
EXPECT_FALSE(def);
def.reset();
+ ASSERT_NO_THROW(def = AttrDefs::instance().getByType(1, 3561));
+ EXPECT_FALSE(def);
+ def.reset();
ASSERT_NO_THROW(def = AttrDefs::instance().getByName("Foo-Bar"));
EXPECT_FALSE(def);
}