diff --git a/doc/sphinx/arm/ext-radius.rst b/doc/sphinx/arm/ext-radius.rst index 7e4d038a74..ca1c2b4c65 100644 --- a/doc/sphinx/arm/ext-radius.rst +++ b/doc/sphinx/arm/ext-radius.rst @@ -550,7 +550,7 @@ RADIUS dictionary. There are differences: - Yes - - No + - since Kea 3.1.1 * - Support for Vendor Attributes diff --git a/src/hooks/dhcp/radius/client_dictionary.cc b/src/hooks/dhcp/radius/client_dictionary.cc index c247437ef0..01f7b9b641 100644 --- a/src/hooks/dhcp/radius/client_dictionary.cc +++ b/src/hooks/dhcp/radius/client_dictionary.cc @@ -172,6 +172,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_); @@ -259,6 +265,28 @@ AttrDefs::parseLine(const string& line, unsigned int depth) { 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(value_str); + if ((val < numeric_limits::min()) || + (val > numeric_limits::max())) { + isc_throw(Unexpected, "not 32 bit " << value_str); + } + value = static_cast(val); + } catch (...) { + isc_throw(Unexpected, "can't parse integer value " << value_str); + } + IntCstDefPtr def(new IntCstDef(PW_VENDOR_SPECIFIC, name, value)); + add(def); + return; + } isc_throw(Unexpected, "unknown dictionary entry '" << tokens[0] << "'"); } diff --git a/src/hooks/dhcp/radius/client_dictionary.h b/src/hooks/dhcp/radius/client_dictionary.h index c3b16e8edc..21ee11b9b1 100644 --- a/src/hooks/dhcp/radius/client_dictionary.h +++ b/src/hooks/dhcp/radius/client_dictionary.h @@ -79,6 +79,8 @@ typedef boost::shared_ptr AttrDefPtr; typedef std::list AttrDefList; /// @brief RADIUS integer constant definitions. +/// +/// Include vendor ids with Vendor-Specific attribute. class IntCstDef { public: diff --git a/src/hooks/dhcp/radius/data/dictionary b/src/hooks/dhcp/radius/data/dictionary index 6e252bc2df..f06dde01b3 100644 --- a/src/hooks/dhcp/radius/data/dictionary +++ b/src/hooks/dhcp/radius/data/dictionary @@ -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 diff --git a/src/hooks/dhcp/radius/tests/dictionary_unittests.cc b/src/hooks/dhcp/radius/tests/dictionary_unittests.cc index dc39ea9cb2..2fbd014bc9 100644 --- a/src/hooks/dhcp/radius/tests/dictionary_unittests.cc +++ b/src/hooks/dhcp/radius/tests/dictionary_unittests.cc @@ -131,8 +131,20 @@ 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("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. @@ -216,12 +228,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 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 positive = { + "VENDOR ISC 2495" + }; + EXPECT_NO_THROW_LOG(parseLines(positive)); + + // Redefine the same vendor id. + list same = { + "VENDOR ISC 2495", + "VENDOR ISC 2495" + }; + EXPECT_NO_THROW_LOG(parseLines(same)); + + // Redefine with a different value is not allowed. + list 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 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. @@ -239,13 +288,11 @@ TEST_F(DictionaryTest, include) { include.push_back("# Including the dictonary"); include.push_back(string("$INCLUDE ") + string(TEST_DICTIONARY)); include.push_back("# Dictionary included"); - // include.push_back("VALUE Vendor-Specific ISC 2495"); - include.push_back("VALUE ARAP-Security ISC 2495"); + 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"); - auto isc = AttrDefs::instance().getByName(PW_ARAP_SECURITY, "ISC"); + auto isc = AttrDefs::instance().getByName(PW_VENDOR_SPECIFIC, "ISC"); ASSERT_TRUE(isc); EXPECT_EQ(2495, isc->value_);