-
-
Save heggi/90390c135bef31640bbb8f74e1cb0fb4 to your computer and use it in GitHub Desktop.
KEA DHCP server option82 support
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
KEA_INCLUDE ?= /usr/include/kea | |
KEA_LIB ?= /usr/lib | |
OBJECTS = option82.o | |
MODULES = kea-hook-option82.so | |
DEPS = $(OBJECTS:.o=.d) | |
CXXFLAGS = -I $(KEA_INCLUDE) -fPIC -Wno-deprecated -std=c++11 | |
LDFLAGS = -L $(KEA_LIB) -shared -lkea-dhcpsrv -lkea-dhcp++ -lkea-hooks -lkea-log -lkea-util -lkea-exceptions | |
all: $(OBJECTS) $(MODULES) | |
kea-hook-option82.so: option82.o | |
$(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) option82.o | |
%.o: %.cc | |
$(CXX) -MMD -MP -c $(CXXFLAGS) -o $@ $< | |
clean: | |
rm -f *.o | |
rm -f *.d | |
rm -f *.so | |
install: | |
mkdir -p $(DESTDIR) | |
/usr/bin/install -c -m 644 kea-hook-option82.so $(DESTDIR)/kea-hook-option82.so | |
-include $(DEPS) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <hooks/hooks.h> | |
#include <dhcp/pkt4.h> | |
#include <log/macros.h> | |
#include <log/logger_support.h> | |
#include <util/encode/hex.h> | |
#include <util/io_utilities.h> | |
#include <string> | |
#include <vector> | |
using namespace isc::dhcp; | |
using namespace isc::hooks; | |
using namespace isc::util::encode; | |
/* | |
00: 00 00 00 09 - enterprise number | |
04: 1a - size | |
05: 0a - suboptions code (0a ??) | |
06: 18 - suboptions size | |
07: 00 00 00 00 | |
0B: 52 - options 82 | |
0C: 12 - size | |
0D: 01 - opt82_1 | |
0E: 06 - size | |
0F: 00 04 0f f1 01 01 - data circuit_id_opt | |
15: 02 - opt82_2 | |
16: 08 - size | |
17: 00 06 ec 30 91 11 d6 7a - data remote_id_opt | |
*/ | |
typedef std::map<uint8_t, OptionBuffer> OptionBufferMap; | |
isc::log::Logger option82_logger("option82_hook"); | |
namespace heggi { | |
namespace hooks { | |
class CiscoVSI { | |
private: | |
OptionBufferMap opt82_; | |
OptionBufferMap parseTLV(OptionBuffer buf) { | |
size_t offset = 0; | |
OptionBufferMap map; | |
while(offset < buf.size()) { | |
uint8_t opt_type = buf[offset++]; | |
if(opt_type == DHO_PAD) | |
continue; | |
if(offset + 1 > buf.size()) { | |
return map; | |
} | |
uint8_t opt_len = buf[offset++]; | |
if(offset + opt_len > buf.size()) { | |
return map; | |
} | |
auto opt = OptionBuffer(buf.begin() + offset, buf.begin() + offset + opt_len); | |
map.insert(std::make_pair(opt_type, opt)); | |
LOG_DEBUG(option82_logger, 99, "CiscoVSI::parseTLV add option %1, %2").arg(opt_type).arg(encodeHex(opt)); | |
offset += opt_len; | |
} | |
return map; | |
} | |
public: | |
CiscoVSI(){}; | |
CiscoVSI(OptionBufferIter first, OptionBufferIter last) { | |
//пропустим 2 первых байта чтобы исключить лишнюю вложенность | |
OptionBuffer data = OptionBuffer(first + 2, last); | |
LOG_DEBUG(option82_logger, 99, "CiscoVSI::CiscoVSI %1").arg(encodeHex(data)); | |
OptionBufferMap options = parseTLV(data); | |
//Find and parse opt82 suboptions | |
//opt 10 -> subopt 82 | |
auto it = options.find(82); | |
if(it != options.end()) { | |
opt82_ = parseTLV((*it).second); | |
} | |
} | |
bool empty() { | |
return opt82_.empty(); | |
} | |
OptionBuffer getOption(uint8_t type) { | |
auto it = opt82_.find(type); | |
if(it != opt82_.end()) { | |
return (*it).second; | |
} | |
return OptionBuffer(); //NULL | |
} | |
}; | |
class VSI { | |
private: | |
OptionBuffer data_; | |
OptionBufferMap vendors_; | |
public: | |
VSI(OptionPtr ptr) { | |
if(!ptr) return; | |
LOG_DEBUG(option82_logger, 99, "VSI::VSI %1").arg(encodeHex(ptr->getData())); | |
data_.assign(ptr->getData().begin(), ptr->getData().end()); | |
size_t offset = 0; | |
while(offset < data_.size()) { | |
uint32_t opt_type = isc::util::readUint32(&data_[offset], 4); | |
offset += 4; | |
if(opt_type == DHO_PAD) | |
continue; | |
if(offset + 1 > data_.size()) { | |
return; | |
} | |
uint8_t opt_len = data_[offset++]; | |
if(offset + opt_len > data_.size()) { | |
return; | |
} | |
auto opt = OptionBuffer(data_.begin() + offset, data_.begin() + offset + opt_len); | |
vendors_.insert(std::make_pair(opt_type, opt)); | |
offset += opt_len; | |
} | |
} | |
CiscoVSI getVendorCisco() { | |
auto it = vendors_.find(9); | |
if(it != vendors_.end()) { | |
OptionBuffer ptr = (*it).second; | |
return CiscoVSI(ptr.begin(), ptr.end()); | |
} | |
return CiscoVSI(); //NULL | |
} | |
}; | |
} | |
} | |
extern "C" { | |
int load(LibraryHandle& handle) { | |
return 0; | |
} | |
int version() { | |
return (KEA_HOOKS_VERSION); | |
} | |
/* IPv4 callouts */ | |
int host4_identifier(CalloutHandle& handle) { | |
//std::vector<std::string> env; | |
Pkt4Ptr pkt4; | |
OptionBuffer circuit_id, remote_id; | |
handle.getArgument("query4", pkt4); | |
OptionPtr rai = pkt4->getOption(DHO_DHCP_AGENT_OPTIONS); | |
if (rai) { | |
LOG_DEBUG(option82_logger, 99, "DHO_DHCP_AGENT_OPTIONS"); | |
auto vsi = heggi::hooks::VSI(rai->getOption(RAI_OPTION_VSI)); | |
auto ciscoVSI = vsi.getVendorCisco(); | |
if(!ciscoVSI.empty()) { | |
LOG_DEBUG(option82_logger, 99, "SubOption 9 Detected, try get subsuboption 1,2"); | |
circuit_id = ciscoVSI.getOption(1); | |
remote_id = ciscoVSI.getOption(2); | |
} else { | |
LOG_DEBUG(option82_logger, 99, "Try get suboptions 1,2"); | |
OptionPtr circuit_id_opt = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID); | |
OptionPtr remote_id_opt = rai->getOption(RAI_OPTION_REMOTE_ID); | |
if (remote_id_opt && circuit_id_opt) { | |
const OptionBuffer& circuit_id_vec = circuit_id_opt->getData(); | |
const OptionBuffer& remote_id_vec = remote_id_opt->getData(); | |
circuit_id.assign(circuit_id_vec.begin(), circuit_id_vec.end()); | |
remote_id.assign(remote_id_vec.begin(), remote_id_vec.end()); | |
} | |
} | |
if (!circuit_id.empty() && !remote_id.empty()) { | |
LOG_INFO(option82_logger, "CircuitID = %1").arg(encodeHex(circuit_id)); | |
LOG_INFO(option82_logger, "RemoteID = %1").arg(encodeHex(remote_id)); | |
//Add last byte of circuit_id to remote_id | |
int size = std::min((int)circuit_id.size(), 6); | |
OptionBuffer opt82_vec(remote_id.end() - size, remote_id.end()); | |
opt82_vec.push_back(circuit_id.back()); | |
LOG_INFO(option82_logger, "FlexID = %1").arg(encodeHex(opt82_vec)); | |
handle.setArgument("id_value", opt82_vec); | |
return 0; | |
} | |
} | |
return 0; | |
} | |
} // end extern "C" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment