Add DHCPv6 Relay Agent (#8251)
* Added DHCPv6 Relay * Added DHCPv6 Counter
This commit is contained in:
parent
e3ac64954a
commit
f0e640f7e6
5
src/dhcp6relay/.gitignore
vendored
Normal file
5
src/dhcp6relay/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
debian/*
|
||||||
|
!debian/changelog
|
||||||
|
!debian/compat
|
||||||
|
!debian/control
|
||||||
|
!debian/rules
|
42
src/dhcp6relay/Makefile
Normal file
42
src/dhcp6relay/Makefile
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
RM := rm -rf
|
||||||
|
DHCP6RELAY_TARGET := dhcp6relay
|
||||||
|
CP := cp
|
||||||
|
MKDIR := mkdir
|
||||||
|
CC := g++
|
||||||
|
MV := mv
|
||||||
|
LIBS := -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboost_system -I $(PWD)/../sonic-swss-common/common
|
||||||
|
CFLAGS = -g -Wall -std=gnu11
|
||||||
|
PWD := $(shell pwd)
|
||||||
|
|
||||||
|
ifneq ($(MAKECMDGOALS),clean)
|
||||||
|
ifneq ($(strip $(C_DEPS)),)
|
||||||
|
-include $(C_DEPS) $(OBJS)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
-include src/subdir.mk
|
||||||
|
|
||||||
|
all: sonic-dhcp6relay
|
||||||
|
|
||||||
|
sonic-dhcp6relay: $(OBJS)
|
||||||
|
@echo 'Building target: $@'
|
||||||
|
@echo 'Invoking: G++ Linker'
|
||||||
|
$(CC) -o $(DHCP6RELAY_TARGET) $(OBJS) $(LIBS)
|
||||||
|
@echo 'Finished building target: $@'
|
||||||
|
@echo ' '
|
||||||
|
|
||||||
|
install:
|
||||||
|
$(MKDIR) -p $(DESTDIR)/usr/sbin
|
||||||
|
$(MV) $(DHCP6RELAY_TARGET) $(DESTDIR)/usr/sbin
|
||||||
|
|
||||||
|
deinstall:
|
||||||
|
$(RM) $(DESTDIR)/usr/sbin/$(DHCP6RELAY_TARGET)
|
||||||
|
$(RM) -rf $(DESTDIR)/usr/sbin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-$(RM) $(EXECUTABLES) $(C_DEPS) $(OBJS) $(DHCP6RELAY_TARGET)
|
||||||
|
-@echo ' '
|
||||||
|
|
||||||
|
.PHONY: all clean dependents
|
||||||
|
|
||||||
|
|
5
src/dhcp6relay/debian/changelog
Normal file
5
src/dhcp6relay/debian/changelog
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
sonic-dhcp6relay (1.0.0-0) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Initial release.
|
||||||
|
|
||||||
|
-- Kelly Yeh <kellyyeh@microsoft.com>
|
1
src/dhcp6relay/debian/compat
Normal file
1
src/dhcp6relay/debian/compat
Normal file
@ -0,0 +1 @@
|
|||||||
|
9
|
17
src/dhcp6relay/debian/control
Normal file
17
src/dhcp6relay/debian/control
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Source: sonic-dhcp6relay
|
||||||
|
Section: devel
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Kelly Yeh <kellyyeh@microsoft.com>
|
||||||
|
Build-Depends: debhelper (>= 8.0.0),
|
||||||
|
dh-systemd
|
||||||
|
Standards-Version: 3.9.3
|
||||||
|
Homepage: https://github.com/Azure/sonic-buildimage
|
||||||
|
XS-Go-Import-Path: github.com/Azure/sonic-buildimage
|
||||||
|
|
||||||
|
Package: sonic-dhcp6relay
|
||||||
|
Architecture: any
|
||||||
|
Built-Using: ${misc:Built-Using}
|
||||||
|
Depends: libevent-2.1-6,
|
||||||
|
libboost-thread1.71.0,
|
||||||
|
libboost-system1.71.0
|
||||||
|
Description: SONiC DHCPv6 Relay
|
4
src/dhcp6relay/debian/rules
Executable file
4
src/dhcp6relay/debian/rules
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@ --parallel
|
153
src/dhcp6relay/src/configInterface.cpp
Normal file
153
src/dhcp6relay/src/configInterface.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include <sstream>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "configInterface.h"
|
||||||
|
|
||||||
|
constexpr auto DEFAULT_TIMEOUT_MSEC = 1000;
|
||||||
|
|
||||||
|
bool pollSwssNotifcation = true;
|
||||||
|
std::shared_ptr<boost::thread> mSwssThreadPtr;
|
||||||
|
|
||||||
|
std::shared_ptr<swss::DBConnector> configDbPtr = std::make_shared<swss::DBConnector> ("CONFIG_DB", 0);
|
||||||
|
swss::SubscriberStateTable ipHelpersTable(configDbPtr.get(), "DHCP_RELAY");
|
||||||
|
swss::Select swssSelect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void deinitialize_swss()
|
||||||
|
*
|
||||||
|
* @brief initialize DB tables and start SWSS listening thread
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void initialize_swss(std::vector<relay_config> *vlans, swss::DBConnector *db)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
swssSelect.addSelectable(&ipHelpersTable);
|
||||||
|
get_dhcp(vlans);
|
||||||
|
mSwssThreadPtr = std::make_shared<boost::thread> (&handleSwssNotification, vlans);
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc &e) {
|
||||||
|
syslog(LOG_ERR, "Failed allocate memory. Exception details: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void deinitialize_swss()
|
||||||
|
*
|
||||||
|
* @brief deinitialize DB interface and join SWSS listening thread
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void deinitialize_swss()
|
||||||
|
{
|
||||||
|
stopSwssNotificationPoll();
|
||||||
|
mSwssThreadPtr->interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void get_dhcp(std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief initialize and get vlan table information from DHCP_RELAY
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void get_dhcp(std::vector<relay_config> *vlans) {
|
||||||
|
swss::Selectable *selectable;
|
||||||
|
int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC);
|
||||||
|
if (ret == swss::Select::ERROR) {
|
||||||
|
syslog(LOG_WARNING, "Select: returned ERROR");
|
||||||
|
} else if (ret == swss::Select::TIMEOUT) {
|
||||||
|
}
|
||||||
|
if (selectable == static_cast<swss::Selectable *> (&ipHelpersTable)) {
|
||||||
|
handleRelayNotification(ipHelpersTable, vlans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @code void handleSwssNotification(std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief main thread for handling SWSS notification
|
||||||
|
*
|
||||||
|
* @param context list of vlans/argument config that contains strings of server and option
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void handleSwssNotification(std::vector<relay_config> *vlans)
|
||||||
|
{
|
||||||
|
while (pollSwssNotifcation) {
|
||||||
|
get_dhcp(vlans);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief handles DHCPv6 relay configuration change notification
|
||||||
|
*
|
||||||
|
* @param ipHelpersTable DHCP table
|
||||||
|
* @param context list of vlans/argument config that contains strings of server and option
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector<relay_config> *vlans)
|
||||||
|
{
|
||||||
|
std::deque<swss::KeyOpFieldsValuesTuple> entries;
|
||||||
|
|
||||||
|
ipHelpersTable.pops(entries);
|
||||||
|
processRelayNotification(entries, vlans);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief process DHCPv6 relay servers and options configuration change notification
|
||||||
|
*
|
||||||
|
* @param entries queue of std::tuple<std::string, std::string, std::vector<FieldValueTuple>> entries in DHCP table
|
||||||
|
* @param context list of vlans/argument config that contains strings of server and option
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::vector<relay_config> *vlans)
|
||||||
|
{
|
||||||
|
std::vector<std::string> servers;
|
||||||
|
|
||||||
|
for (auto &entry: entries) {
|
||||||
|
std::string vlan = kfvKey(entry);
|
||||||
|
std::string operation = kfvOp(entry);
|
||||||
|
std::vector<swss::FieldValueTuple> fieldValues = kfvFieldsValues(entry);
|
||||||
|
|
||||||
|
relay_config intf;
|
||||||
|
intf.interface = vlan;
|
||||||
|
for (auto &fieldValue: fieldValues) {
|
||||||
|
std::string f = fvField(fieldValue);
|
||||||
|
std::string v = fvValue(fieldValue);
|
||||||
|
if(f == "dhcpv6_servers") {
|
||||||
|
std::stringstream ss(v);
|
||||||
|
while (ss.good()) {
|
||||||
|
std::string substr;
|
||||||
|
getline(ss, substr, ',');
|
||||||
|
intf.servers.push_back(substr);
|
||||||
|
}
|
||||||
|
syslog(LOG_DEBUG, "key: %s, Operation: %s, f: %s, v: %s", vlan.c_str(), operation.c_str(), f.c_str(), v.c_str());
|
||||||
|
}
|
||||||
|
if(f == "dhcpv6_option|rfc6939_support") {
|
||||||
|
if(v == "true")
|
||||||
|
intf.is_option_79 = true;
|
||||||
|
else if(v == "false")
|
||||||
|
intf.is_option_79 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vlans->push_back(intf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*@code stopSwssNotificationPoll
|
||||||
|
*
|
||||||
|
*@brief stop SWSS listening thread
|
||||||
|
*
|
||||||
|
*@return none
|
||||||
|
*/
|
||||||
|
void stopSwssNotificationPoll() {
|
||||||
|
pollSwssNotifcation = false;
|
||||||
|
};
|
75
src/dhcp6relay/src/configInterface.h
Normal file
75
src/dhcp6relay/src/configInterface.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#include <boost/thread.hpp>
|
||||||
|
#include "subscriberstatetable.h"
|
||||||
|
#include "select.h"
|
||||||
|
#include "relay.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void deinitialize_swss()
|
||||||
|
*
|
||||||
|
* @brief initialize DB tables and start SWSS listening thread
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void initialize_swss(std::vector<relay_config> *vlans, swss::DBConnector *db);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void deinitialize_swss()
|
||||||
|
*
|
||||||
|
* @brief deinitialize DB interface and join SWSS listening thread
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void deinitialize_swss();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void get_dhcp(std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief initialize and get vlan information from DHCP_RELAY
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void get_dhcp(std::vector<relay_config> *vlans);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void handleSwssNotification(std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief main thread for handling SWSS notification
|
||||||
|
*
|
||||||
|
* @param context list of vlans/argument config that contains strings of server and option
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void handleSwssNotification(std::vector<relay_config> *vlans);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief handles DHCPv6 relay configuration change notification
|
||||||
|
*
|
||||||
|
* @param ipHelpersTable DHCP table
|
||||||
|
* @param context list of vlans/argument config that contains strings of server and option
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void handleRelayNotification(swss::SubscriberStateTable &configMuxTable, std::vector<relay_config> *vlans);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::vector<relay_config> *vlans)
|
||||||
|
*
|
||||||
|
* @brief process DHCPv6 relay servers and options configuration change notification
|
||||||
|
*
|
||||||
|
* @param entries queue of std::tuple<std::string, std::string, std::vector<FieldValueTuple>> entries in DHCP table
|
||||||
|
* @param context list of vlans/argument config that contains strings of server and option
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void processRelayNotification(std::deque<swss::KeyOpFieldsValuesTuple> &entries, std::vector<relay_config> *vlans);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*@code stopSwssNotificationPoll
|
||||||
|
*
|
||||||
|
*@brief stop SWSS listening thread
|
||||||
|
*
|
||||||
|
*@return none
|
||||||
|
*/
|
||||||
|
void stopSwssNotificationPoll();
|
18
src/dhcp6relay/src/main.cpp
Normal file
18
src/dhcp6relay/src/main.cpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include "configInterface.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
try {
|
||||||
|
std::vector<relay_config> vlans;
|
||||||
|
swss::DBConnector state_db("STATE_DB", 0);
|
||||||
|
initialize_swss(&vlans, &state_db);
|
||||||
|
loop_relay(&vlans, &state_db);
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "An exception occurred.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
729
src/dhcp6relay/src/relay.cpp
Normal file
729
src/dhcp6relay/src/relay.cpp
Normal file
@ -0,0 +1,729 @@
|
|||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <event.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <event2/event.h>
|
||||||
|
#include <event2/bufferevent.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "configdb.h"
|
||||||
|
#include "sonicv2connector.h"
|
||||||
|
#include "dbconnector.h"
|
||||||
|
#include "configInterface.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct event *listen_event;
|
||||||
|
struct event *server_listen_event;
|
||||||
|
struct event_base *base;
|
||||||
|
struct event *ev_sigint;
|
||||||
|
struct event *ev_sigterm;
|
||||||
|
|
||||||
|
/* DHCPv6 filter */
|
||||||
|
/* sudo tcpdump -dd "ip6 dst ff02::1:2 && udp dst port 547" */
|
||||||
|
|
||||||
|
static struct sock_filter ether_relay_filter[] = {
|
||||||
|
|
||||||
|
{ 0x28, 0, 0, 0x0000000c },
|
||||||
|
{ 0x15, 0, 13, 0x000086dd },
|
||||||
|
{ 0x20, 0, 0, 0x00000026 },
|
||||||
|
{ 0x15, 0, 11, 0xff020000 },
|
||||||
|
{ 0x20, 0, 0, 0x0000002a },
|
||||||
|
{ 0x15, 0, 9, 0x00000000 },
|
||||||
|
{ 0x20, 0, 0, 0x0000002e },
|
||||||
|
{ 0x15, 0, 7, 0x00000000 },
|
||||||
|
{ 0x20, 0, 0, 0x00000032 },
|
||||||
|
{ 0x15, 0, 5, 0x00010002 },
|
||||||
|
{ 0x30, 0, 0, 0x00000014 },
|
||||||
|
{ 0x15, 0, 3, 0x00000011 },
|
||||||
|
{ 0x28, 0, 0, 0x00000038 },
|
||||||
|
{ 0x15, 0, 1, 0x00000223 },
|
||||||
|
{ 0x6, 0, 0, 0x00040000 },
|
||||||
|
{ 0x6, 0, 0, 0x00000000 },
|
||||||
|
};
|
||||||
|
const struct sock_fprog ether_relay_fprog = {
|
||||||
|
lengthof(ether_relay_filter),
|
||||||
|
ether_relay_filter
|
||||||
|
};
|
||||||
|
|
||||||
|
/* DHCPv6 Counter */
|
||||||
|
uint64_t counters[DHCPv6_MESSAGE_TYPE_COUNT];
|
||||||
|
std::map<int, std::string> counterMap = {{1, "Solicit"},
|
||||||
|
{2, "Advertise"},
|
||||||
|
{3, "Request"},
|
||||||
|
{4, "Confirm"},
|
||||||
|
{5, "Renew"},
|
||||||
|
{6, "Rebind"},
|
||||||
|
{7, "Reply"},
|
||||||
|
{8, "Release"},
|
||||||
|
{9, "Decline"},
|
||||||
|
{12, "Relay-Forward"},
|
||||||
|
{13, "Relay-Reply"}};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void initialize_counter(swss::DBConnector *db, std::string counterVlan);
|
||||||
|
*
|
||||||
|
* @brief initialize the counter by each Vlan
|
||||||
|
*
|
||||||
|
* @param swss::DBConnector *db state_db connector
|
||||||
|
* @param counterVlan counter table with interface name
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void initialize_counter(swss::DBConnector *db, std::string counterVlan) {
|
||||||
|
db->hset(counterVlan, "Solicit", toString(counters[DHCPv6_MESSAGE_TYPE_SOLICIT]));
|
||||||
|
db->hset(counterVlan, "Advertise", toString(counters[DHCPv6_MESSAGE_TYPE_ADVERTISE]));
|
||||||
|
db->hset(counterVlan, "Request", toString(counters[DHCPv6_MESSAGE_TYPE_REQUEST]));
|
||||||
|
db->hset(counterVlan, "Confirm", toString(counters[DHCPv6_MESSAGE_TYPE_CONFIRM]));
|
||||||
|
db->hset(counterVlan, "Renew", toString(counters[DHCPv6_MESSAGE_TYPE_RENEW]));
|
||||||
|
db->hset(counterVlan, "Rebind", toString(counters[DHCPv6_MESSAGE_TYPE_REBIND]));
|
||||||
|
db->hset(counterVlan, "Reply", toString(counters[DHCPv6_MESSAGE_TYPE_REPLY]));
|
||||||
|
db->hset(counterVlan, "Release", toString(counters[DHCPv6_MESSAGE_TYPE_RELEASE]));
|
||||||
|
db->hset(counterVlan, "Decline", toString(counters[DHCPv6_MESSAGE_TYPE_DECLINE]));
|
||||||
|
db->hset(counterVlan, "Relay-Forward", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_FORW]));
|
||||||
|
db->hset(counterVlan, "Relay-Reply", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_REPL]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void update_counter(swss::DBConnector *db, std::string CounterVlan, uint8_t msg_type);
|
||||||
|
*
|
||||||
|
* @brief update the counter in state_db with count of each DHCPv6 message type
|
||||||
|
*
|
||||||
|
* @param swss::DBConnector *db state_db connector
|
||||||
|
* @param counterVlan counter table with interface name
|
||||||
|
* @param msg_type dhcpv6 message type to be updated in counter
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void update_counter(swss::DBConnector *db, std::string counterVlan, uint8_t msg_type) {
|
||||||
|
db->hset(counterVlan, counterMap.find(msg_type)->second, toString(counters[msg_type]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code std::string toString(uint16_t count);
|
||||||
|
*
|
||||||
|
* @brief convert uint16_t to string
|
||||||
|
*
|
||||||
|
* @param count count of messages in counter
|
||||||
|
*
|
||||||
|
* @return count in string
|
||||||
|
*/
|
||||||
|
std::string toString(uint16_t count) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << count;
|
||||||
|
std::string countValue = ss.str();
|
||||||
|
return countValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code bool is_addr_gua(in6_addr addr);
|
||||||
|
*
|
||||||
|
* @brief check if address is global
|
||||||
|
*
|
||||||
|
* @param addr ipv6 address
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
bool is_addr_gua(in6_addr addr) {
|
||||||
|
auto masked = addr.__in6_u.__u6_addr8[0] & 0xe0;
|
||||||
|
return (masked ^ 0x20) == 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code is_addr_link_local(in6_addr addr);
|
||||||
|
*
|
||||||
|
* @brief check if address is link_local
|
||||||
|
*
|
||||||
|
* @param addr ipv6 address
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
bool is_addr_link_local(in6_addr addr) {
|
||||||
|
auto masked = ntohs(addr.__in6_u.__u6_addr16[0]) & 0xffc0;
|
||||||
|
return (masked ^ 0xfe80) == 0x0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through ethernet frame
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return ether_header end of ethernet header position
|
||||||
|
*/
|
||||||
|
const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end) {
|
||||||
|
(*out_end) = buffer + sizeof(struct ether_header);
|
||||||
|
return (const struct ether_header *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through ipv6 header
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return ip6_hdr end of ipv6 header position
|
||||||
|
*/
|
||||||
|
const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end) {
|
||||||
|
(*out_end) = buffer + sizeof(struct ip6_hdr);
|
||||||
|
return (struct ip6_hdr *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through udp header
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return udphdr end of udp header position
|
||||||
|
*/
|
||||||
|
const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end) {
|
||||||
|
(*out_end) = buffer + sizeof(struct udphdr);
|
||||||
|
return (const struct udphdr *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer);
|
||||||
|
*
|
||||||
|
* @brief parse through dhcpv6 header
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return dhcpv6_msg end of dhcpv6 header position
|
||||||
|
*/
|
||||||
|
const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer) {
|
||||||
|
return (const struct dhcpv6_msg *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer);
|
||||||
|
*
|
||||||
|
* @brief parse through dhcpv6 relay message
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return dhcpv6_relay_msg start of dhcpv6 relay message or end of dhcpv6 message type position
|
||||||
|
*/
|
||||||
|
const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) {
|
||||||
|
return (const struct dhcpv6_relay_msg *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through dhcpv6 option
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return dhcpv6_option end of dhcpv6 message option
|
||||||
|
*/
|
||||||
|
const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end) {
|
||||||
|
uint32_t size = 4; // option-code + option-len
|
||||||
|
size += ntohs(*(uint16_t *)(buffer + 2));
|
||||||
|
(*out_end) = buffer + size;
|
||||||
|
|
||||||
|
return (const struct dhcpv6_option *)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n);
|
||||||
|
*
|
||||||
|
* @brief send udp packet
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param sockaddr_in6 target target socket
|
||||||
|
* @param n length of message
|
||||||
|
*
|
||||||
|
* @return dhcpv6_option end of dhcpv6 message option
|
||||||
|
*/
|
||||||
|
void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n) {
|
||||||
|
if(sendto(sock, buffer, n, 0, (const struct sockaddr *)&target, sizeof(target)) == -1)
|
||||||
|
syslog(LOG_ERR, "sendto: Failed to send to target address\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);
|
||||||
|
*
|
||||||
|
* @brief embed the DHCPv6 message received into DHCPv6 relay forward message
|
||||||
|
*
|
||||||
|
* @param buffer pointer to buffer
|
||||||
|
* @param msg pointer to parsed DHCPv6 message
|
||||||
|
* @param msg_length length of DHCPv6 message
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length) {
|
||||||
|
struct dhcpv6_option option;
|
||||||
|
option.option_code = htons(OPTION_RELAY_MSG);
|
||||||
|
option.option_length = htons(msg_length);
|
||||||
|
memcpy(buffer, &option, sizeof(struct dhcpv6_option));
|
||||||
|
memcpy(buffer + sizeof(struct dhcpv6_option), msg, msg_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code sock_open(int ifindex, const struct sock_fprog *fprog);
|
||||||
|
*
|
||||||
|
* @brief prepare L2 socket to attach to "udp and port 547" filter
|
||||||
|
*
|
||||||
|
* @param ifindex interface index
|
||||||
|
* @param fprog bpf filter "udp and port 547"
|
||||||
|
*
|
||||||
|
* @return socket descriptor
|
||||||
|
*/
|
||||||
|
int sock_open(int ifindex, const struct sock_fprog *fprog)
|
||||||
|
{
|
||||||
|
if (!ifindex) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||||
|
if (s == -1) {
|
||||||
|
syslog(LOG_ERR, "socket: Failed to create socket\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_ll sll = {
|
||||||
|
.sll_family = AF_PACKET,
|
||||||
|
.sll_protocol = htons(ETH_P_ALL),
|
||||||
|
.sll_ifindex = ifindex
|
||||||
|
};
|
||||||
|
|
||||||
|
if (bind(s, (struct sockaddr *)&sll, sizeof sll) == -1) {
|
||||||
|
syslog(LOG_ERR, "bind: Failed to bind to specified interface\n");
|
||||||
|
(void) close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fprog && setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, fprog, sizeof *fprog) == -1)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "setsockopt: Failed to attach filter\n");
|
||||||
|
(void) close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code prepare_relay_config(relay_config *interface_config, int local_sock, int filter);
|
||||||
|
*
|
||||||
|
* @brief prepare for specified relay interface config: server and link address
|
||||||
|
*
|
||||||
|
* @param interface_config pointer to relay config to be prepared
|
||||||
|
* @param local_sock L3 socket used for relaying messages
|
||||||
|
* @param filter socket attached with filter
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void prepare_relay_config(relay_config *interface_config, int local_sock, int filter) {
|
||||||
|
struct ifaddrs *ifa, *ifa_tmp;
|
||||||
|
sockaddr_in6 non_link_local;
|
||||||
|
sockaddr_in6 link_local;
|
||||||
|
|
||||||
|
interface_config->local_sock = local_sock;
|
||||||
|
interface_config->filter = filter;
|
||||||
|
|
||||||
|
for(auto server: interface_config->servers) {
|
||||||
|
sockaddr_in6 tmp;
|
||||||
|
if(inet_pton(AF_INET6, server.c_str(), &tmp.sin6_addr) != 1)
|
||||||
|
{
|
||||||
|
syslog(LOG_WARNING, "inet_pton: Failed to convert IPv6 address\n");
|
||||||
|
}
|
||||||
|
tmp.sin6_family = AF_INET6;
|
||||||
|
tmp.sin6_flowinfo = 0;
|
||||||
|
tmp.sin6_port = htons(RELAY_PORT);
|
||||||
|
tmp.sin6_scope_id = 0;
|
||||||
|
interface_config->servers_sock.push_back(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getifaddrs(&ifa) == -1) {
|
||||||
|
syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ifa_tmp = ifa;
|
||||||
|
while (ifa_tmp) {
|
||||||
|
if (ifa_tmp->ifa_addr->sa_family == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr;
|
||||||
|
if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && is_addr_gua(in6->sin6_addr)) {
|
||||||
|
non_link_local = *in6;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && is_addr_link_local(in6->sin6_addr)) {
|
||||||
|
link_local = *in6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifa_tmp = ifa_tmp->ifa_next;
|
||||||
|
}
|
||||||
|
freeifaddrs(ifa);
|
||||||
|
|
||||||
|
if(is_addr_gua(non_link_local.sin6_addr)) {
|
||||||
|
interface_config->link_address = non_link_local;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
interface_config->link_address = link_local;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code prepare_socket(int *local_sock, relay_config *config);
|
||||||
|
*
|
||||||
|
* @brief prepare L3 socket for sending
|
||||||
|
*
|
||||||
|
* @param local_sock pointer to socket to be prepared
|
||||||
|
* @param config relay config that contains strings of server and interface addresses
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void prepare_socket(int *local_sock, relay_config *config, int index) {
|
||||||
|
int flag = 1;
|
||||||
|
sockaddr_in6 addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin6_family = AF_INET6;
|
||||||
|
addr.sin6_addr = in6addr_any;
|
||||||
|
addr.sin6_scope_id = index;
|
||||||
|
addr.sin6_port = htons(RELAY_PORT);
|
||||||
|
|
||||||
|
if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
|
||||||
|
syslog(LOG_ERR, "socket: Failed to create socket\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if((setsockopt(*local_sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) == -1) {
|
||||||
|
syslog(LOG_ERR, "setsockopt: Unable to set socket option\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(*local_sock, (sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||||
|
syslog(LOG_ERR, "bind: Failed to bind to socket\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
|
||||||
|
*
|
||||||
|
* @brief construct relay-forward message
|
||||||
|
*
|
||||||
|
* @param sock L3 socket for sending data to servers
|
||||||
|
* @param msg pointer to dhcpv6 message header position
|
||||||
|
* @param len size of data received
|
||||||
|
* @param ip_hdr pointer to IPv6 header
|
||||||
|
* @param ether_hdr pointer to Ethernet header
|
||||||
|
* @param config pointer to the relay interface config
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) {
|
||||||
|
static uint8_t buffer[4096];
|
||||||
|
auto current_buffer_position = buffer;
|
||||||
|
dhcpv6_relay_msg new_message;
|
||||||
|
new_message.msg_type = DHCPv6_MESSAGE_TYPE_RELAY_FORW;
|
||||||
|
memcpy(&new_message.peer_address, &ip_hdr->ip6_src, sizeof(in6_addr));
|
||||||
|
new_message.hop_count = 0;
|
||||||
|
|
||||||
|
memcpy(&new_message.link_address, &config->link_address.sin6_addr, sizeof(in6_addr));
|
||||||
|
memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg));
|
||||||
|
current_buffer_position += sizeof(dhcpv6_relay_msg);
|
||||||
|
|
||||||
|
|
||||||
|
auto dhcp_message_length = len;
|
||||||
|
relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length);
|
||||||
|
current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
|
||||||
|
|
||||||
|
if(config->is_option_79) {
|
||||||
|
linklayer_addr_option option79;
|
||||||
|
option79.link_layer_type = htons(1);
|
||||||
|
option79.option_code = htons(OPTION_CLIENT_LINKLAYER_ADDR);
|
||||||
|
option79.option_length = htons(2 + 6); // link_layer_type field + address
|
||||||
|
|
||||||
|
memcpy(current_buffer_position, &option79, sizeof(linklayer_addr_option));
|
||||||
|
current_buffer_position += sizeof(linklayer_addr_option);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(current_buffer_position, ðer_hdr->ether_shost, sizeof(ether_hdr->ether_shost));
|
||||||
|
current_buffer_position += sizeof(ether_hdr->ether_shost);
|
||||||
|
|
||||||
|
for(auto server: config->servers_sock) {
|
||||||
|
send_udp(sock, buffer, server, current_buffer_position - buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs);
|
||||||
|
*
|
||||||
|
* @brief relay and unwrap a relay-reply message
|
||||||
|
*
|
||||||
|
* @param sock L3 socket for sending data to servers
|
||||||
|
* @param msg pointer to dhcpv6 message header position
|
||||||
|
* @param len size of data received
|
||||||
|
* @param config relay interface config
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs) {
|
||||||
|
static uint8_t buffer[4096];
|
||||||
|
char ifname[configs->interface.size()];
|
||||||
|
struct sockaddr_in6 target_addr;
|
||||||
|
auto current_buffer_position = buffer;
|
||||||
|
auto current_position = msg;
|
||||||
|
const uint8_t *tmp = NULL;
|
||||||
|
auto dhcp_relay_header = parse_dhcpv6_relay(msg);
|
||||||
|
current_position += sizeof(struct dhcpv6_relay_msg);
|
||||||
|
|
||||||
|
while ((current_position - msg) != len) {
|
||||||
|
auto option = parse_dhcpv6_opt(current_position, &tmp);
|
||||||
|
current_position = tmp;
|
||||||
|
switch (ntohs(option->option_code)) {
|
||||||
|
case OPTION_RELAY_MSG:
|
||||||
|
memcpy(current_buffer_position, ((uint8_t *)option) + sizeof(struct dhcpv6_option), ntohs(option->option_length));
|
||||||
|
current_buffer_position += ntohs(option->option_length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(ifname, configs->interface.c_str());
|
||||||
|
memcpy(&target_addr.sin6_addr, &dhcp_relay_header->peer_address, sizeof(struct in6_addr));
|
||||||
|
target_addr.sin6_family = AF_INET6;
|
||||||
|
target_addr.sin6_flowinfo = 0;
|
||||||
|
target_addr.sin6_port = htons(CLIENT_PORT);
|
||||||
|
target_addr.sin6_scope_id = if_nametoindex(ifname);
|
||||||
|
|
||||||
|
send_udp(sock, buffer, target_addr, current_buffer_position - buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code callback(evutil_socket_t fd, short event, void *arg);
|
||||||
|
*
|
||||||
|
* @brief callback for libevent that is called everytime data is received at the filter socket
|
||||||
|
*
|
||||||
|
* @param fd filter socket
|
||||||
|
* @param event libevent triggered event
|
||||||
|
* @param arg callback argument provided by user
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void callback(evutil_socket_t fd, short event, void *arg) {
|
||||||
|
struct relay_config *config = (struct relay_config *)arg;
|
||||||
|
static uint8_t message_buffer[4096];
|
||||||
|
uint32_t len = recv(config->filter, message_buffer, 4096, 0);
|
||||||
|
if (len <= 0) {
|
||||||
|
syslog(LOG_WARNING, "recv: Failed to receive data at filter socket\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ptr = (char *)message_buffer;
|
||||||
|
const uint8_t *current_position = (uint8_t *)ptr;
|
||||||
|
const uint8_t *tmp = NULL;
|
||||||
|
|
||||||
|
auto ether_header = parse_ether_frame(current_position, &tmp);
|
||||||
|
current_position = tmp;
|
||||||
|
|
||||||
|
auto ip_header = parse_ip6_hdr(current_position, &tmp);
|
||||||
|
current_position = tmp;
|
||||||
|
|
||||||
|
if (ip_header->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_UDP) {
|
||||||
|
const struct ip6_ext *ext_header;
|
||||||
|
do {
|
||||||
|
ext_header = (const struct ip6_ext *)current_position;
|
||||||
|
current_position += ext_header->ip6e_len;
|
||||||
|
}
|
||||||
|
while (ext_header->ip6e_nxt != IPPROTO_UDP);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto udp_header = parse_udp(current_position, &tmp);
|
||||||
|
current_position = tmp;
|
||||||
|
|
||||||
|
auto msg = parse_dhcpv6_hdr(current_position);
|
||||||
|
counters[msg->msg_type]++;
|
||||||
|
update_counter(config->db, config->counterVlan, msg->msg_type);
|
||||||
|
|
||||||
|
relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_callback(evutil_socket_t fd, short event, void *arg) {
|
||||||
|
struct relay_config *config = (struct relay_config *)arg;
|
||||||
|
sockaddr_in6 from;
|
||||||
|
socklen_t len = sizeof(from);
|
||||||
|
int32_t data = 0;
|
||||||
|
static uint8_t message_buffer[4096];
|
||||||
|
|
||||||
|
if ((data = recvfrom(config->local_sock, message_buffer, 4096, 0, (sockaddr *)&from, &len)) == -1) {
|
||||||
|
syslog(LOG_WARNING, "recv: Failed to receive data from server\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto msg = parse_dhcpv6_hdr(message_buffer);
|
||||||
|
|
||||||
|
counters[msg->msg_type]++;
|
||||||
|
update_counter(config->db, config->counterVlan, msg->msg_type);
|
||||||
|
if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
|
||||||
|
relay_relay_reply(config->local_sock, message_buffer, data, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code signal_init();
|
||||||
|
*
|
||||||
|
* @brief initialize DHCPv6 Relay libevent signals
|
||||||
|
*/
|
||||||
|
int signal_init() {
|
||||||
|
int rv = -1;
|
||||||
|
do {
|
||||||
|
ev_sigint = evsignal_new(base, SIGINT, signal_callback, base);
|
||||||
|
if (ev_sigint == NULL) {
|
||||||
|
syslog(LOG_ERR, "Could not create SIGINT libevent signal\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev_sigterm = evsignal_new(base, SIGTERM, signal_callback, base);
|
||||||
|
if (ev_sigterm == NULL) {
|
||||||
|
syslog(LOG_ERR, "Could not create SIGTERM libevent signal\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = 0;
|
||||||
|
} while(0);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code signal_start();
|
||||||
|
*
|
||||||
|
* @brief start DHCPv6 Relay libevent base and add signals
|
||||||
|
*/
|
||||||
|
int signal_start()
|
||||||
|
{
|
||||||
|
int rv = -1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (evsignal_add(ev_sigint, NULL) != 0) {
|
||||||
|
syslog(LOG_ERR, "Could not add SIGINT libevent signal\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evsignal_add(ev_sigterm, NULL) != 0) {
|
||||||
|
syslog(LOG_ERR, "Could not add SIGTERM libevent signal\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_base_dispatch(base) != 0) {
|
||||||
|
syslog(LOG_ERR, "Could not start libevent dispatching loop\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code signal_callback(fd, event, arg);
|
||||||
|
*
|
||||||
|
* @brief signal handler for dhcp6relay. Initiate shutdown when signal is caught
|
||||||
|
*
|
||||||
|
* @param fd libevent socket
|
||||||
|
* @param event event triggered
|
||||||
|
* @param arg pointer to libevent base
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void signal_callback(evutil_socket_t fd, short event, void *arg)
|
||||||
|
{
|
||||||
|
syslog(LOG_ALERT, "Received signal: '%s'\n", strsignal(fd));
|
||||||
|
if ((fd == SIGTERM) || (fd == SIGINT)) {
|
||||||
|
dhcp6relay_stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code dhcp6relay_stop();
|
||||||
|
*
|
||||||
|
* @brief stop DHCPv6 Relay libevent loop upon signal
|
||||||
|
*/
|
||||||
|
void dhcp6relay_stop()
|
||||||
|
{
|
||||||
|
event_base_loopexit(base, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db);
|
||||||
|
*
|
||||||
|
* @brief main loop: configure sockets, create libevent base, start server listener thread
|
||||||
|
*
|
||||||
|
* @param vlans list of vlans retrieved from config_db
|
||||||
|
* @param db state_db connector
|
||||||
|
*/
|
||||||
|
void loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db) {
|
||||||
|
std::vector<int> sockets;
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i<vlans->size(); i++) {
|
||||||
|
struct relay_config config = vlans->at(i);
|
||||||
|
int filter = 0;
|
||||||
|
int local_sock = 0;
|
||||||
|
const char *ifname = config.interface.c_str();
|
||||||
|
int index = if_nametoindex(ifname);
|
||||||
|
config.db = db;
|
||||||
|
|
||||||
|
config.counterVlan = "DHCPv6_COUNTER_TABLE|";
|
||||||
|
config.counterVlan.append(ifname);
|
||||||
|
initialize_counter(config.db, config.counterVlan);
|
||||||
|
|
||||||
|
filter = sock_open(index, ðer_relay_fprog);
|
||||||
|
|
||||||
|
prepare_socket(&local_sock, &config, index);
|
||||||
|
sockets.push_back(filter);
|
||||||
|
sockets.push_back(local_sock);
|
||||||
|
|
||||||
|
prepare_relay_config(&config, local_sock, filter);
|
||||||
|
|
||||||
|
evutil_make_listen_socket_reuseable(filter);
|
||||||
|
evutil_make_socket_nonblocking(filter);
|
||||||
|
|
||||||
|
evutil_make_listen_socket_reuseable(local_sock);
|
||||||
|
evutil_make_socket_nonblocking(local_sock);
|
||||||
|
|
||||||
|
base = event_base_new();
|
||||||
|
if(base == NULL) {
|
||||||
|
syslog(LOG_ERR, "libevent: Failed to create base\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
listen_event = event_new(base, filter, EV_READ|EV_PERSIST, callback, (void *)&config);
|
||||||
|
server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, (void *)&config);
|
||||||
|
if (listen_event == NULL || server_listen_event == NULL) {
|
||||||
|
syslog(LOG_ERR, "libevent: Failed to create libevent\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
event_add(listen_event, NULL);
|
||||||
|
event_add(server_listen_event, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if((signal_init() == 0) && signal_start() == 0) {
|
||||||
|
shutdown();
|
||||||
|
for(std::size_t i = 0; i<sockets.size(); i++) {
|
||||||
|
close(sockets.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code shutdown();
|
||||||
|
*
|
||||||
|
* @brief free signals and terminate threads
|
||||||
|
*/
|
||||||
|
void shutdown() {
|
||||||
|
event_del(ev_sigint);
|
||||||
|
event_del(ev_sigterm);
|
||||||
|
event_free(ev_sigint);
|
||||||
|
event_free(ev_sigterm);
|
||||||
|
event_base_free(base);
|
||||||
|
deinitialize_swss();
|
||||||
|
}
|
357
src/dhcp6relay/src/relay.h
Normal file
357
src/dhcp6relay/src/relay.h
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <linux/if_packet.h>
|
||||||
|
#include <netinet/if_ether.h>
|
||||||
|
#include <netinet/ip6.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <event2/util.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define PACKED __attribute__ ((packed))
|
||||||
|
|
||||||
|
#define RELAY_PORT 547
|
||||||
|
#define CLIENT_PORT 546
|
||||||
|
#define HOP_LIMIT 32
|
||||||
|
|
||||||
|
#define lengthof(A) (sizeof (A) / sizeof (A)[0])
|
||||||
|
|
||||||
|
#define OPTION_RELAY_MSG 9
|
||||||
|
#define OPTION_CLIENT_LINKLAYER_ADDR 79
|
||||||
|
|
||||||
|
/* DHCPv6 message types */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DHCPv6_MESSAGE_TYPE_SOLICIT = 1,
|
||||||
|
DHCPv6_MESSAGE_TYPE_ADVERTISE = 2,
|
||||||
|
DHCPv6_MESSAGE_TYPE_REQUEST = 3,
|
||||||
|
DHCPv6_MESSAGE_TYPE_CONFIRM = 4,
|
||||||
|
DHCPv6_MESSAGE_TYPE_RENEW = 5,
|
||||||
|
DHCPv6_MESSAGE_TYPE_REBIND = 6,
|
||||||
|
DHCPv6_MESSAGE_TYPE_REPLY = 7,
|
||||||
|
DHCPv6_MESSAGE_TYPE_RELEASE = 8,
|
||||||
|
DHCPv6_MESSAGE_TYPE_DECLINE = 9,
|
||||||
|
DHCPv6_MESSAGE_TYPE_RELAY_FORW = 12,
|
||||||
|
DHCPv6_MESSAGE_TYPE_RELAY_REPL = 13,
|
||||||
|
|
||||||
|
DHCPv6_MESSAGE_TYPE_COUNT
|
||||||
|
} dhcp_message_type_t;
|
||||||
|
|
||||||
|
struct relay_config {
|
||||||
|
int local_sock;
|
||||||
|
int filter;
|
||||||
|
sockaddr_in6 link_address;
|
||||||
|
swss::DBConnector *db;
|
||||||
|
std::string interface;
|
||||||
|
std::vector<std::string> servers;
|
||||||
|
std::vector<sockaddr_in6> servers_sock;
|
||||||
|
bool is_option_79;
|
||||||
|
std::string counterVlan;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* DHCPv6 messages and options */
|
||||||
|
|
||||||
|
struct dhcpv6_msg {
|
||||||
|
uint8_t msg_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PACKED dhcpv6_relay_msg {
|
||||||
|
uint8_t msg_type;
|
||||||
|
uint8_t hop_count;
|
||||||
|
struct in6_addr link_address;
|
||||||
|
struct in6_addr peer_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct dhcpv6_option {
|
||||||
|
uint16_t option_code;
|
||||||
|
uint16_t option_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct linklayer_addr_option {
|
||||||
|
uint16_t option_code;
|
||||||
|
uint16_t option_length;
|
||||||
|
uint16_t link_layer_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code sock_open(int ifindex, const struct sock_fprog *fprog);
|
||||||
|
*
|
||||||
|
* @brief prepare L2 socket to attach to "udp and port 547" filter
|
||||||
|
*
|
||||||
|
* @param ifindex interface index
|
||||||
|
* @param fprog bpf filter "udp and port 547"
|
||||||
|
*
|
||||||
|
* @return socket descriptor
|
||||||
|
*/
|
||||||
|
int sock_open(int ifindex, const struct sock_fprog *fprog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code prepare_socket(int *local_sock, arg_config *config);
|
||||||
|
*
|
||||||
|
* @brief prepare L3 socket for sending
|
||||||
|
*
|
||||||
|
* @param local_sock pointer to socket to be prepared
|
||||||
|
* @param config argument config that contains strings of server and interface addresses
|
||||||
|
* @param index interface id
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void prepare_socket(int *local_sock, relay_config *config, int index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code prepare_relay_config(relay_config *interface_config, int local_sock, int filter);
|
||||||
|
*
|
||||||
|
* @brief prepare for specified relay interface config: server and link address
|
||||||
|
*
|
||||||
|
* @param interface_config pointer to relay config to be prepared
|
||||||
|
* @param local_sock L3 socket used for relaying messages
|
||||||
|
* @param filter socket attached with filter
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void prepare_relay_config(relay_config *interface_config, int local_sock, int filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);
|
||||||
|
*
|
||||||
|
* @brief embed the DHCPv6 message received into DHCPv6 relay forward message
|
||||||
|
*
|
||||||
|
* @param buffer pointer to buffer
|
||||||
|
* @param msg pointer to parsed DHCPv6 message
|
||||||
|
* @param msg_length length of DHCPv6 message
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
|
||||||
|
*
|
||||||
|
* @brief construct relay-forward message
|
||||||
|
*
|
||||||
|
* @param sock L3 socket for sending data to servers
|
||||||
|
* @param msg pointer to dhcpv6 message header position
|
||||||
|
* @param len size of data received
|
||||||
|
* @param ip_hdr pointer to IPv6 header
|
||||||
|
* @param ether_hdr pointer to Ethernet header
|
||||||
|
* @param config pointer to the relay interface config
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs);
|
||||||
|
*
|
||||||
|
* @brief relay and unwrap a relay-reply message
|
||||||
|
*
|
||||||
|
* @param sock L3 socket for sending data to servers
|
||||||
|
* @param msg pointer to dhcpv6 message header position
|
||||||
|
* @param len size of data received
|
||||||
|
* @param config relay interface config
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code loop_relay(std::vector<arg_config> *vlans, swss::DBConnector *db);
|
||||||
|
*
|
||||||
|
* @brief main loop: configure sockets, create libevent base, start server listener thread
|
||||||
|
*
|
||||||
|
* @param vlans list of vlans retrieved from config_db
|
||||||
|
* @param db state_db connector
|
||||||
|
*/
|
||||||
|
void loop_relay(std::vector<relay_config> *vlans, swss::DBConnector *db);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code signal_init();
|
||||||
|
*
|
||||||
|
* @brief initialize DHCPv6 Relay libevent signals
|
||||||
|
*/
|
||||||
|
int signal_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code signal_start();
|
||||||
|
*
|
||||||
|
* @brief start DHCPv6 Relay libevent base and add signals
|
||||||
|
*/
|
||||||
|
int signal_start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code dhcp6relay_stop();
|
||||||
|
*
|
||||||
|
* @brief stop DHCPv6 Relay libevent loop upon signal
|
||||||
|
*/
|
||||||
|
void dhcp6relay_stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code signal_callback(fd, event, arg);
|
||||||
|
*
|
||||||
|
* @brief signal handler for dhcp6relay. Initiate shutdown when signal is caught
|
||||||
|
*
|
||||||
|
* @param fd libevent socket
|
||||||
|
* @param event event triggered
|
||||||
|
* @param arg pointer to libevent base
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void signal_callback(evutil_socket_t fd, short event, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code shutdown();
|
||||||
|
*
|
||||||
|
* @brief free signals and terminate threads
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void initialize_counter(swss::DBConnector *db, std::string counterVlan);
|
||||||
|
*
|
||||||
|
* @brief initialize the counter by each Vlan
|
||||||
|
*
|
||||||
|
* @param swss::DBConnector *db state_db connector
|
||||||
|
* @param counterVlan counter table with interface name
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void initialize_counter(swss::DBConnector *db, std::string counterVlan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void update_counter(swss::DBConnector *db, std::string CounterVlan, uint8_t msg_type);
|
||||||
|
*
|
||||||
|
* @brief update the counter in state_db with count of each DHCPv6 message type
|
||||||
|
*
|
||||||
|
* @param swss::DBConnector *db state_db connector
|
||||||
|
* @param counterVlan counter table with interface name
|
||||||
|
* @param msg_type dhcpv6 message type to be updated in counter
|
||||||
|
*
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
void update_counter(swss::DBConnector *db, std::string counterVlan, uint8_t msg_type);
|
||||||
|
|
||||||
|
/* Helper functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code std::string toString(uint16_t count);
|
||||||
|
*
|
||||||
|
* @brief convert uint16_t to string
|
||||||
|
*
|
||||||
|
* @param count count of messages in counter
|
||||||
|
*
|
||||||
|
* @return count in string
|
||||||
|
*/
|
||||||
|
std::string toString(uint16_t count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code bool is_addr_gua(in6_addr addr);
|
||||||
|
*
|
||||||
|
* @brief check if address is global
|
||||||
|
*
|
||||||
|
* @param addr ipv6 address
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
bool is_addr_gua(in6_addr addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code is_addr_link_local(in6_addr addr);
|
||||||
|
*
|
||||||
|
* @brief check if address is link_local
|
||||||
|
*
|
||||||
|
* @param addr ipv6 address
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
bool is_addr_link_local(in6_addr addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through ethernet frame
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return ether_header end of ethernet header position
|
||||||
|
*/
|
||||||
|
const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through ipv6 header
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return ip6_hdr end of ipv6 header position
|
||||||
|
*/
|
||||||
|
const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through udp header
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return udphdr end of udp header position
|
||||||
|
*/
|
||||||
|
const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer);
|
||||||
|
*
|
||||||
|
* @brief parse through dhcpv6 header
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return dhcpv6_msg end of dhcpv6 header position
|
||||||
|
*/
|
||||||
|
const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer);
|
||||||
|
*
|
||||||
|
* @brief parse through dhcpv6 relay message
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return dhcpv6_relay_msg start of dhcpv6 relay message or end of dhcpv6 message type position
|
||||||
|
*/
|
||||||
|
const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
*
|
||||||
|
* @brief parse through dhcpv6 option
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param **out_end pointer
|
||||||
|
*
|
||||||
|
* @return dhcpv6_option end of dhcpv6 message option
|
||||||
|
*/
|
||||||
|
const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n);
|
||||||
|
*
|
||||||
|
* @brief send udp packet
|
||||||
|
*
|
||||||
|
* @param *buffer message buffer
|
||||||
|
* @param sockaddr_in6 target target socket
|
||||||
|
* @param n length of message
|
||||||
|
*
|
||||||
|
* @return dhcpv6_option end of dhcpv6 message option
|
||||||
|
*/
|
||||||
|
void send_udp(int sock, struct sockaddr_in6 target, uint8_t *buffer, uint32_t n);
|
||||||
|
|
23
src/dhcp6relay/src/subdir.mk
Normal file
23
src/dhcp6relay/src/subdir.mk
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
CC := g++
|
||||||
|
|
||||||
|
C_SRCS += \
|
||||||
|
../src/relay.c \
|
||||||
|
../src/configInterface.c \
|
||||||
|
../src/main.c
|
||||||
|
|
||||||
|
OBJS += \
|
||||||
|
./src/relay.o \
|
||||||
|
./src/configInterface.o \
|
||||||
|
./src/main.o
|
||||||
|
|
||||||
|
C_DEPS += \
|
||||||
|
./src/relay.d \
|
||||||
|
./src/configInterface.d \
|
||||||
|
./src/main.d
|
||||||
|
|
||||||
|
src/%.o: src/%.cpp
|
||||||
|
@echo 'Building file: $<'
|
||||||
|
@echo 'Invoking: GCC C++ Compiler'
|
||||||
|
$(CC) -std=c++17 -D__FILENAME__="$(subst src/,,$<)" $(LIBS) -Wall -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
|
||||||
|
@echo 'Finished building: $<'
|
||||||
|
@echo ' '
|
Loading…
Reference in New Issue
Block a user