Change dhcp6relay to be a submodule, and rename it to dhcprelay (#10711)

Why I did it
src/dhcprelay is being split out to be its own submodule.

How I did it
Add existing dhcprelay commits into the new repo.
Clean up Makefile (sonic-net/sonic-dhcp-relay@772625f)
Add LGTM config (sonic-net/sonic-dhcp-relay@5cc0889)
Add Azure pipeline config (sonic-net/sonic-dhcp-relay@c79cdb7)
Add submodule reference, renaming most references of dhcp6relay to dhcprelay (to reflect that this will not just be for IPv6 in the future).
How to verify it
Successful run of LGTM is tested at sonic-net/sonic-dhcp-relay#4. Failure run of LGTM is tested at sonic-net/sonic-dhcp-relay#3.

Azure pipeline is run for each commit/PR, and will build for amd64, armhf, and arm64. UT/code coverage check is not yet done.

Signed-off-by: Saikrishna Arcot <sarcot@microsoft.com>
This commit is contained in:
Saikrishna Arcot 2022-07-12 07:38:41 -07:00 committed by Ying Xie
parent f16e7ad8ed
commit d1f72a4878
19 changed files with 29 additions and 1583 deletions

3
.gitmodules vendored
View File

@ -113,3 +113,6 @@
[submodule "src/ptf-py3"] [submodule "src/ptf-py3"]
path = src/ptf-py3 path = src/ptf-py3
url = https://github.com/p4lang/ptf.git url = https://github.com/p4lang/ptf.git
[submodule "src/dhcprelay"]
path = src/dhcprelay
url = https://github.com/sonic-net/sonic-dhcp-relay.git

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)_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_BULLSEYE)_DBG_DEPENDS) $(DOCKER_DHCP_RELAY)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_BULLSEYE)_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_BULLSEYE)_DBG_IMAGE_PACKAGES) $(DOCKER_DHCP_RELAY)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_BULLSEYE)_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 @@
12

View File

@ -1,14 +0,0 @@
Source: sonic-dhcp6relay
Section: devel
Priority: optional
Maintainer: Kelly Yeh <kellyyeh@microsoft.com>
Build-Depends: debhelper (>= 12.0.0), libevent-dev, libboost-thread-dev, libboost-system-dev, libswsscommon-dev
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: ${shlibs:Depends}
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->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->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 6f94c2ededb39ef4cdab788e295a041b2aec12b4