Skip to content
Open
6 changes: 6 additions & 0 deletions changelog_unreleased/3860-radius-vendor-attributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[func] fdupont
Added support of RADIUS dictionary includes, vendor
attributes and integer translations to the RADIUS hook
library for compatibility with previous versions using
the FreeRADIUS client library.
(Gitlab #2860)
21 changes: 19 additions & 2 deletions doc/sphinx/arm/ext-radius.rst
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,20 @@ At the service level, three sections can be configured:
- ``expr`` - is the last of the three ways to specify the attribute content.
It specifies an evaluation expression on the DHCP query packet.

- ``vendor`` - since Kea 3.1.2 is the vendor id of the attribute.
It allways contents a string with the vendor name or an integer litteral.

Attributes are supported only for the access service.

.. note::

Vendor-Specific attribute can be specified in two ways: using a ``raw``
value which must include the vendor and the vsa data, note that the ``data``
value is no longer supported sine Kea 3.1.2, and the ``expr`` value
is evaluated to the content of the attribute. The second way was added
by 3.1.2 and allows to specify a vendor attribute which is automatically
embedded into a Vendor-Specific attribute.

- The ``peer-updates`` boolean flag (default ``true``) controls whether lease
updates coming from an active High-Availability (HA) partner should result in
an accounting request. This may be desirable to remove duplicates if HA
Expand Down Expand Up @@ -550,13 +561,13 @@ RADIUS dictionary. There are differences:

- Yes

- No
- since Kea 3.1.2

* - Support for Vendor Attributes

- Yes

- No
- since Kea 3.1.2

* - Attribute Names and Attribute Values

Expand All @@ -570,6 +581,12 @@ RADIUS dictionary. There are differences:

- Must have an associated attribute definition in the dictionary.

* - Attribute and Integer Value name spaces

- flat name spaces allowing duplicates.

- since Kea 3.1.2 different name spaces per vendor.

* - Reply-Message Presence in the Kea Logs

- Only as part of the aggregated list of attributes in ``RADIUS_AUTHENTICATION_ACCEPTED``, ``RADIUS_ACCESS_CACHE_INSERT``, ``RADIUS_ACCESS_CACHE_GET`` log messages.
Expand Down
56 changes: 30 additions & 26 deletions src/hooks/dhcp/radius/cfg_attribute.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,12 @@ CfgAttributes::add(const AttrDefPtr& def,
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);
Expand All @@ -43,46 +42,46 @@ CfgAttributes::del(const uint8_t type) {
}

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);
}
Expand All @@ -91,16 +90,16 @@ Attributes
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;
}
Expand All @@ -117,18 +116,23 @@ ElementPtr
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.attr_) {
map = it.attr_->toElement();
} else 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();
if (def->vendor_ != 0) {
ostringstream vendor;
vendor << def->vendor_;
map->set("vendor", Element::create(vendor.str()));
}
}
result->add(map);
}
Expand Down
64 changes: 55 additions & 9 deletions src/hooks/dhcp/radius/cfg_attribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#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>
Expand Down Expand Up @@ -81,8 +85,9 @@ class CfgAttributes : public data::CfgToElement {
/// 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() {
Expand All @@ -92,34 +97,41 @@ class CfgAttributes : public data::CfgToElement {
/// @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.
///
Expand Down Expand Up @@ -182,10 +194,44 @@ class CfgAttributes : public data::CfgToElement {

/// @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
Expand Down
Loading