Add dhcprelay submodule (#12030)

This commit is contained in:
kellyyeh 2022-09-12 18:12:05 -07:00 committed by GitHub
parent 37ada92ad5
commit 2f7ca1d7b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 30 additions and 1586 deletions

4
.gitmodules vendored
View File

@ -90,3 +90,7 @@
path = src/linkmgrd
url = https://github.com/Azure/sonic-linkmgrd.git
branch = 202012
[submodule "src/dhcprelay"]
path = src/dhcprelay
url = https://github.com/sonic-net/sonic-dhcp-relay.git
branch = 202012

View File

@ -1,9 +0,0 @@
SPATH := $($(SONIC_DHCP6RELAY)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/dhcp6relay.mk rules/dhcp6relay.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
DEP_FILES += $(shell git ls-files $(SPATH))
$(SONIC_DHCP6RELAY)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_DHCP6RELAY)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(SONIC_DHCP6RELAY)_DEP_FILES := $(DEP_FILES)

View File

@ -1,12 +0,0 @@
# SONiC DHCPV6 RELAY Package
SONIC_DHCP6RELAY_VERSION = 1.0.0-0
SONIC_DHCP6RELAY_PKG_NAME = dhcp6relay
SONIC_DHCP6RELAY = sonic-$(SONIC_DHCP6RELAY_PKG_NAME)_$(SONIC_DHCP6RELAY_VERSION)_$(CONFIGURED_ARCH).deb
$(SONIC_DHCP6RELAY)_DEPENDS = $(LIBSWSSCOMMON) $(LIBHIREDIS) $(LIBSWSSCOMMON_DEV) $(LIBHIREDIS_DEV)
$(SONIC_DHCP6RELAY)_SRC_PATH = $(SRC_PATH)/$(SONIC_DHCP6RELAY_PKG_NAME)
SONIC_DPKG_DEBS += $(SONIC_DHCP6RELAY)
SONIC_DHCP6RELAY_DBG = sonic-$(SONIC_DHCP6RELAY_PKG_NAME)-dbgsym_$(SONIC_DHCP6RELAY_VERSION)_$(CONFIGURED_ARCH).deb
$(eval $(call add_derived_package,$(SONIC_DHCP6RELAY),$(SONIC_DHCP6RELAY_DBG)))

11
rules/dhcprelay.dep Normal file
View File

@ -0,0 +1,11 @@
SPATH := $($(SONIC_DHCPRELAY)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/dhcprelay.mk rules/dhcprelay.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files))
$(SONIC_DHCPRELAY)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_DHCPRELAY)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(SONIC_DHCPRELAY)_DEP_FILES := $(DEP_FILES)
$(SONIC_DHCPRELAY)_SMDEP_FILES := $(SMDEP_FILES)
$(SONIC_DHCPRELAY)_SMDEP_PATHS := $(SPATH)

12
rules/dhcprelay.mk Normal file
View File

@ -0,0 +1,12 @@
# SONiC DHCPV6 RELAY Package
SONIC_DHCPRELAY_VERSION = 1.0.0-0
SONIC_DHCPRELAY_PKG_NAME = dhcp6relay
SONIC_DHCPRELAY = sonic-$(SONIC_DHCPRELAY_PKG_NAME)_$(SONIC_DHCPRELAY_VERSION)_$(CONFIGURED_ARCH).deb
$(SONIC_DHCPRELAY)_DEPENDS = $(LIBSWSSCOMMON) $(LIBHIREDIS) $(LIBSWSSCOMMON_DEV) $(LIBHIREDIS_DEV)
$(SONIC_DHCPRELAY)_SRC_PATH = $(SRC_PATH)/dhcprelay
SONIC_DPKG_DEBS += $(SONIC_DHCPRELAY)
SONIC_DHCPRELAY_DBG = sonic-$(SONIC_DHCPRELAY_PKG_NAME)-dbgsym_$(SONIC_DHCPRELAY_VERSION)_$(CONFIGURED_ARCH).deb
$(eval $(call add_derived_package,$(SONIC_DHCPRELAY),$(SONIC_DHCPRELAY_DBG)))

View File

@ -6,10 +6,10 @@ DOCKER_DHCP_RELAY_DBG = $(DOCKER_DHCP_RELAY_STEM)-$(DBG_IMAGE_MARK).gz
$(DOCKER_DHCP_RELAY)_PATH = $(DOCKERS_PATH)/$(DOCKER_DHCP_RELAY_STEM)
$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(SONIC_DHCPMON) $(SONIC_DHCP6RELAY) $(LIBSWSSCOMMON)
$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(SONIC_DHCPMON) $(SONIC_DHCPRELAY) $(LIBSWSSCOMMON)
$(DOCKER_DHCP_RELAY)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_DEPENDS)
$(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) $(SONIC_DHCP6RELAY_DBG) $(SONIC_DHCPMON_DBG)
$(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) $(SONIC_DHCPRELAY_DBG) $(SONIC_DHCPMON_DBG)
$(DOCKER_DHCP_RELAY)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BUSTER)_DBG_IMAGE_PACKAGES)

View File

@ -1,5 +0,0 @@
debian/*
!debian/changelog
!debian/compat
!debian/control
!debian/rules

View File

@ -1,42 +0,0 @@
RM := rm -rf
DHCP6RELAY_TARGET := dhcp6relay
CP := cp
MKDIR := mkdir
CC := g++
MV := mv
LIBS := -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboost_system
CFLAGS += -Wall -std=c++17 -fPIE -I$(PWD)/../sonic-swss-common/common
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) $(LDFLAGS) -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

View File

@ -1,5 +0,0 @@
sonic-dhcp6relay (1.0.0-0) UNRELEASED; urgency=medium
* Initial release.
-- Kelly Yeh <kellyyeh@microsoft.com>

View File

@ -1 +0,0 @@
9

View File

@ -1,17 +0,0 @@
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

View File

@ -1,6 +0,0 @@
#!/usr/bin/make -f
export DEB_BUILD_MAINT_OPTIONS=hardening=+all
%:
dh $@ --parallel

View File

@ -1,152 +0,0 @@
#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 initialize_swss()
*
* @brief initialize DB tables and start SWSS listening thread
*
* @return none
*/
void initialize_swss(std::vector<relay_config> *vlans)
{
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 vlans 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 vlans 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.is_option_79 = true;
intf.interface = vlan;
intf.db = nullptr;
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" && v == "false") {
intf.is_option_79 = false;
}
}
vlans->push_back(intf);
}
}
/**
*@code stopSwssNotificationPoll
*
*@brief stop SWSS listening thread
*
*@return none
*/
void stopSwssNotificationPoll() {
pollSwssNotifcation = false;
};

View File

@ -1,75 +0,0 @@
#include <boost/thread.hpp>
#include "subscriberstatetable.h"
#include "select.h"
#include "relay.h"
/**
* @code void initialize_swss()
*
* @brief initialize DB tables and start SWSS listening thread
*
* @return none
*/
void initialize_swss(std::vector<relay_config> *vlans);
/**
* @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 vlans 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 vlans 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();

View File

@ -1,18 +0,0 @@
#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);
loop_relay(&vlans, &state_db);
}
catch (std::exception &e)
{
syslog(LOG_ERR, "An exception occurred.\n");
return 1;
}
return 0;
}

View File

@ -1,861 +0,0 @@
#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;
static std::string counter_table = "DHCPv6_COUNTER_TABLE|";
/* 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 = {{DHCPv6_MESSAGE_TYPE_UNKNOWN, "Unknown"},
{DHCPv6_MESSAGE_TYPE_SOLICIT, "Solicit"},
{DHCPv6_MESSAGE_TYPE_ADVERTISE, "Advertise"},
{DHCPv6_MESSAGE_TYPE_REQUEST, "Request"},
{DHCPv6_MESSAGE_TYPE_CONFIRM, "Confirm"},
{DHCPv6_MESSAGE_TYPE_RENEW, "Renew"},
{DHCPv6_MESSAGE_TYPE_REBIND, "Rebind"},
{DHCPv6_MESSAGE_TYPE_REPLY, "Reply"},
{DHCPv6_MESSAGE_TYPE_RELEASE, "Release"},
{DHCPv6_MESSAGE_TYPE_DECLINE, "Decline"},
{DHCPv6_MESSAGE_TYPE_RECONFIGURE, "Reconfigure"},
{DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST, "Information-Request"},
{DHCPv6_MESSAGE_TYPE_RELAY_FORW, "Relay-Forward"},
{DHCPv6_MESSAGE_TYPE_RELAY_REPL, "Relay-Reply"},
{DHCPv6_MESSAGE_TYPE_MALFORMED, "Malformed"}};
/**
* @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, "Unknown", toString(counters[DHCPv6_MESSAGE_TYPE_UNKNOWN]));
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, "Reconfigure", toString(counters[DHCPv6_MESSAGE_TYPE_RECONFIGURE]));
db->hset(counterVlan, "Information-Request", toString(counters[DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST]));
db->hset(counterVlan, "Relay-Forward", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_FORW]));
db->hset(counterVlan, "Relay-Reply", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_REPL]));
db->hset(counterVlan, "Malformed", toString(counters[DHCPv6_MESSAGE_TYPE_MALFORMED]));
}
/**
* @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 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) {
auto option = (const struct dhcpv6_option *)buffer;
uint8_t size = 4; // option-code + option-len
size += *(uint16_t *)(buffer);
(*out_end) = buffer + size + ntohs(option->option_length);
return option;
}
/**
* @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type);
*
* @brief send udp packet
*
* @param *buffer message buffer
* @param sockaddr_in6 target target socket
* @param n length of message
* @param relay_config *config pointer to relay_config
* @param uint8_t msg_type message type of dhcpv6 option of relayed message
*
* @return dhcpv6_option end of dhcpv6 message option
*/
void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type) {
std::string counterVlan = counter_table;
if(sendto(sock, buffer, n, 0, (const struct sockaddr *)&target, sizeof(target)) == -1)
syslog(LOG_ERR, "sendto: Failed to send to target address\n");
else if (counterMap.find(msg_type) != counterMap.end()) {
counters[msg_type]++;
update_counter(config->db, counterVlan.append(config->interface), msg_type);
} else {
syslog(LOG_WARNING, "unexpected message type %d(0x%x)\n", msg_type, msg_type);
}
}
/**
* @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 && 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) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
non_link_local = *in6;
break;
}
if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
link_local = *in6;
}
}
ifa_tmp = ifa_tmp->ifa_next;
}
freeifaddrs(ifa);
if(!IN6_IS_ADDR_LINKLOCAL(&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, int *server_sock, relay_config *config, int index);
*
* @brief prepare L3 socket for sending
*
* @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message
* @param server_sock pointer to socket binded to link_local address for relaying server message to client
* @param index scope id of interface
*
* @return none
*/
void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index) {
struct ifaddrs *ifa, *ifa_tmp;
sockaddr_in6 addr;
sockaddr_in6 ll_addr;
memset(&addr, 0, sizeof(addr));
memset(&ll_addr, 0, sizeof(ll_addr));
if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
syslog(LOG_ERR, "socket: Failed to create socket on interface %s\n", config->interface.c_str());
}
if ((*server_sock= socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
syslog(LOG_ERR, "socket: Failed to create socket on interface %s\n", config->interface.c_str());
}
int retry = 0;
bool bind_addr = false;
bool bind_ll_addr = false;
do {
if (getifaddrs(&ifa) == -1) {
syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces with %s\n", strerror(errno));
}
else {
ifa_tmp = ifa;
while (ifa_tmp) {
if (ifa_tmp->ifa_addr && (ifa_tmp->ifa_addr->sa_family == AF_INET6) && (strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0)) {
struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr;
if(!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
bind_addr = true;
in6->sin6_family = AF_INET6;
in6->sin6_port = htons(RELAY_PORT);
addr = *in6;
}
if(IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) {
bind_ll_addr = true;
in6->sin6_family = AF_INET6;
in6->sin6_port = htons(RELAY_PORT);
ll_addr = *in6;
}
}
ifa_tmp = ifa_tmp->ifa_next;
}
freeifaddrs(ifa);
}
if (bind_addr && bind_ll_addr) {
break;
}
syslog(LOG_WARNING, "Retry #%d to bind to sockets on interface %s\n", ++retry, config->interface.c_str());
sleep(5);
} while (retry < 6);
if ((!bind_addr) || (bind(*local_sock, (sockaddr *)&addr, sizeof(addr)) == -1)) {
syslog(LOG_ERR, "bind: Failed to bind socket to global ipv6 address on interface %s after %d retries with %s\n", config->interface.c_str(), retry, strerror(errno));
}
if ((!bind_ll_addr) || (bind(*server_sock, (sockaddr *)&ll_addr, sizeof(addr)) == -1)) {
syslog(LOG_ERR, "bind: Failed to bind socket to link local ipv6 address on interface %s after %d retries with %s\n", config->interface.c_str(), retry, strerror(errno));
}
}
/**
* @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);
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, &ether_hdr->ether_shost, sizeof(ether_hdr->ether_shost));
current_buffer_position += sizeof(ether_hdr->ether_shost);
}
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);
for(auto server: config->servers_sock) {
send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type);
}
}
/**
* @code relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config)
*
* @brief construct a relay-forward message encapsulated 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 config pointer to the relay interface config
*
* @return none
*/
void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) {
static uint8_t buffer[4096];
dhcpv6_relay_msg new_message;
auto current_buffer_position = buffer;
auto dhcp_relay_header = parse_dhcpv6_relay(msg);
if (dhcp_relay_header->hop_count >= HOP_LIMIT)
return;
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 = dhcp_relay_header->hop_count + 1;
memset(&new_message.link_address, 0, 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);
for(auto server: config->servers_sock) {
send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type);
}
}
/**
* @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 *config) {
static uint8_t buffer[4096];
uint8_t type = 0;
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);
auto position = current_position + sizeof(struct dhcpv6_option);
auto dhcpv6msg = parse_dhcpv6_hdr(position);
while ((current_position - msg) < len) {
auto option = parse_dhcpv6_opt(current_position, &tmp);
current_position = tmp;
if (current_position - msg > len || ntohs(option->option_length) > sizeof(buffer) - (current_buffer_position - buffer)) {
break;
}
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);
type = dhcpv6msg->msg_type;
break;
default:
break;
}
}
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(config->interface.c_str());
send_udp(sock, buffer, target_addr, current_buffer_position - buffer, config, type);
}
/**
* @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];
std::string counterVlan = counter_table;
int32_t len = recv(config->filter, message_buffer, 4096, 0);
if (len <= 0) {
syslog(LOG_WARNING, "recv: Failed to receive data at filter socket: %s\n", strerror(errno));
return;
}
char* ptr = (char *)message_buffer;
const uint8_t *current_position = (uint8_t *)ptr;
const uint8_t *tmp = NULL;
const uint8_t *prev = 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;
prev = current_position;
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;
if((current_position == prev) || (current_position >= (uint8_t *)ptr + sizeof(message_buffer))) {
return;
}
prev = current_position;
}
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);
auto option_position = current_position + sizeof(struct dhcpv6_msg);
switch (msg->msg_type) {
case DHCPv6_MESSAGE_TYPE_RELAY_FORW:
{
relay_relay_forw(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config);
break;
}
case DHCPv6_MESSAGE_TYPE_SOLICIT:
case DHCPv6_MESSAGE_TYPE_REQUEST:
case DHCPv6_MESSAGE_TYPE_CONFIRM:
case DHCPv6_MESSAGE_TYPE_RENEW:
case DHCPv6_MESSAGE_TYPE_REBIND:
case DHCPv6_MESSAGE_TYPE_RELEASE:
case DHCPv6_MESSAGE_TYPE_DECLINE:
case DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST:
{
while (option_position - message_buffer < len) {
auto option = parse_dhcpv6_opt(option_position, &tmp);
option_position = tmp;
if (ntohs(option->option_code) > DHCPv6_OPTION_LIMIT) {
counters[DHCPv6_MESSAGE_TYPE_MALFORMED]++;
update_counter(config->db, counterVlan.append(config->interface), DHCPv6_MESSAGE_TYPE_MALFORMED);
syslog(LOG_WARNING, "DHCPv6 option is invalid or contains malformed payload\n");
return;
}
}
counters[msg->msg_type]++;
update_counter(config->db, counterVlan.append(config->interface), msg->msg_type);
relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config);
break;
}
default:
{
syslog(LOG_WARNING, "DHCPv6 client message received was not relayed\n");
break;
}
}
}
/**
* @code void server_callback(evutil_socket_t fd, short event, void *arg);
*
* @brief callback for libevent that is called everytime data is received at the server socket
*
* @param fd filter socket
* @param event libevent triggered event
* @param arg callback argument provided by user
*
* @return none
*/
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]++;
std::string counterVlan = counter_table;
update_counter(config->db, counterVlan.append(config->interface), msg->msg_type);
if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) {
relay_relay_reply(config->server_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;
base = event_base_new();
if(base == NULL) {
syslog(LOG_ERR, "libevent: Failed to create base\n");
}
for(relay_config &vlan : *vlans) {
relay_config *config = &vlan;
int filter = 0;
int local_sock = 0;
int server_sock = 0;
int index = if_nametoindex(config->interface.c_str());
config->db = db;
std::string counterVlan = counter_table;
initialize_counter(config->db, counterVlan.append(config->interface));
filter = sock_open(index, &ether_relay_fprog);
prepare_socket(&local_sock, &server_sock, config, index);
config->local_sock = local_sock;
config->server_sock = server_sock;
sockets.push_back(filter);
sockets.push_back(local_sock);
sockets.push_back(server_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);
listen_event = event_new(base, filter, EV_READ|EV_PERSIST, callback, config);
server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, 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();
}

View File

@ -1,358 +0,0 @@
#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 8 //HOP_LIMIT reduced from 32 to 8 as stated in RFC8415
#define DHCPv6_OPTION_LIMIT 56 // DHCPv6 option code greater than 56 are currently unassigned
#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_UNKNOWN = 0,
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_RECONFIGURE = 10,
DHCPv6_MESSAGE_TYPE_INFORMATION_REQUEST = 11,
DHCPv6_MESSAGE_TYPE_RELAY_FORW = 12,
DHCPv6_MESSAGE_TYPE_RELAY_REPL = 13,
DHCPv6_MESSAGE_TYPE_MALFORMED = 14,
DHCPv6_MESSAGE_TYPE_COUNT
} dhcp_message_type_t;
struct relay_config {
int local_sock;
int server_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;
};
/* DHCPv6 messages and options */
struct dhcpv6_msg {
uint8_t msg_type;
uint8_t xid[3];
};
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, int *server_sock, relay_config *config, int index);
*
* @brief prepare L3 socket for sending
*
* @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message
* @param server_sock pointer to socket binded to link_local address for relaying server message to client
* @param index scope id of interface
*
* @return none
*/
void prepare_socket(int *local_sock, int *server_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_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config)
*
* @brief construct a relay-forward message encapsulated 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 config pointer to the relay interface config
*
* @return none
*/
void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_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 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, relay_config *config, uint8_t msg_type);
*
* @brief send udp packet
*
* @param *buffer message buffer
* @param sockaddr_in6 target target socket
* @param n length of message
* @param relay_config *config pointer to relay_config
* @param uint8_t msg_type message type of dhcpv6 option of relayed message
*
* @return dhcpv6_option end of dhcpv6 message option
*/
void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type);

View File

@ -1,23 +0,0 @@
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) -D__FILENAME__="$(subst src/,,$<)" $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
@echo 'Finished building: $<'
@echo ' '

1
src/dhcprelay Submodule

@ -0,0 +1 @@
Subproject commit 40ad1676e4d07c3eec2153017997bb04b33a94c8