Skip to content

Instantly share code, notes, and snippets.

@heggi

heggi/Makefile Secret

Created May 13, 2019 04:30
Show Gist options
  • Save heggi/90390c135bef31640bbb8f74e1cb0fb4 to your computer and use it in GitHub Desktop.
Save heggi/90390c135bef31640bbb8f74e1cb0fb4 to your computer and use it in GitHub Desktop.
KEA DHCP server option82 support
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)
#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