Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ dhcp6relay/debian/*
!dhcp6relay/debian/compat
!dhcp6relay/debian/control
!dhcp6relay/debian/rules
dhcp4relay/debian/*
!dhcp4relay/debian/changelog
!dhcp4relay/debian/compat
!dhcp4relay/debian/control
!dhcp4relay/debian/rules
106 changes: 106 additions & 0 deletions dhcp4relay/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
.ONESHELL:
SHELL = /bin/bash

RM := rm -rf
WORKING_DIR := $(abspath .)
BUILD_DIR := build
BUILD_TEST_DIR := build-test
DHCP4RELAY_TARGET := $(BUILD_DIR)/dhcp4relay
DHCP4RELAY_TEST_TARGET := $(BUILD_TEST_DIR)/dhcp4relay-test
CP := cp
MKDIR := mkdir
MV := mv
FIND := find
GCOVR := gcovr

LD_PCAPPLUSPLUS_LIB := -lPcap++ -lPacket++ -lCommon++

#pcap plus plus zip file pcappp_v24.09.zip
PCAPPPVAR := 24.09
PCAPPPZIP_FILE := pcappp_v${PCAPPPVAR}.zip
PCAPPLUSPLUS_DIR := $(WORKING_DIR)/PcapPlusPlus-$(PCAPPPVAR)
PCAPPP_DONE = $(WORKING_DIR)/pcappp.stamp
INCLUDE_DIR = $(PCAPPLUSPLUS_DIR)/include
LIB_DIR = $(PCAPPLUSPLUS_DIR)/lib

override LDLIBS += -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboost_system $(LD_PCAPPLUSPLUS_LIB) -lpcap
override CPPFLAGS += -Wall -std=c++17 -fPIE -I/usr/include/swss -I$(INCLUDE_DIR)
override CPPFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)"
override LDFLAGS += -L$(LIB_DIR) -Wl,-rpath=$(abspath $(LIB_DIR))
CPPFLAGS_TEST := --coverage -fprofile-arcs -ftest-coverage -fprofile-generate -fsanitize=address -DUNIT_TEST
LDLIBS_TEST := --coverage -lgtest -lgmock -pthread -lstdc++fs -fsanitize=address
PWD := $(shell pwd)

.PHONY: $(PCAPPP_DONE)
$(PCAPPP_DONE):

# Remove stale files
rm -rf $(PCAPPLUSPLUS_DIR)

unzip ${PCAPPPZIP_FILE}
pushd $(PCAPPLUSPLUS_DIR)

# Create a git repository here for stg to apply patches
git init
git add -f *
git commit -qm "initial commit"

# Apply patches
stg init
stg import -s ../patch/series
popd

cd $(PCAPPLUSPLUS_DIR) && cmake -S . -B build && cmake --build build
cd build && make && sudo cmake --install .

touch $@

all: $(DHCP4RELAY_TARGET) $(DHCP4RELAY_TEST_TARGET)

-include src/subdir.mk
-include test/subdir.mk

# Use different build directories based on whether it's a regular build or a
# test build. This is because in the test build, code coverage is enabled,
# which means the object files that get built will be different
OBJS = $(SRCS:%.cpp=$(BUILD_DIR)/%.o)
TEST_OBJS = $(TEST_SRCS:%.cpp=$(BUILD_TEST_DIR)/%.o)

ifneq ($(MAKECMDGOALS),clean)
-include $(OBJS:%.o=%.d)
-include $(TEST_OBJS:%.o=%.d)
endif

$(BUILD_DIR)/%.o: %.cpp
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<

$(DHCP4RELAY_TARGET): $(PCAPPP_DONE) $(OBJS)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

$(BUILD_TEST_DIR)/%.o: %.cpp
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(CPPFLAGS_TEST) $(LDLIBS) -c -o $@ $<

$(DHCP4RELAY_TEST_TARGET): $(TEST_OBJS)
$(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_TEST) -o $@

test: $(DHCP4RELAY_TEST_TARGET)
sudo ASAN_OPTIONS=detect_leaks=0 ./$(DHCP4RELAY_TEST_TARGET) --gtest_output=xml:$(DHCP4RELAY_TEST_TARGET)-test-result.xml || true
$(GCOVR) -r ./ --html --html-details -o $(DHCP4RELAY_TEST_TARGET)-code-coverage.html
$(GCOVR) -r ./ --xml-pretty -o $(DHCP4RELAY_TEST_TARGET)-code-coverage.xml

install: $(DHCP4RELAY_TARGET)
install -D $(DHCP4RELAY_TARGET) $(DESTDIR)/usr/sbin/$(notdir $(DHCP4RELAY_TARGET))

uninstall:
$(RM) $(DESTDIR)/usr/sbin/$(notdir $(DHCP4RELAY_TARGET))

clean:
-$(RM) $(BUILD_DIR) $(BUILD_TEST_DIR) *.html *.xml
$(FIND) . -name *.gcda -exec rm -f {} \;
$(FIND) . -name *.gcno -exec rm -f {} \;
$(FIND) . -name *.gcov -exec rm -f {} \;
-@echo ' '

.PHONY: all clean test install uninstall
5 changes: 5 additions & 0 deletions dhcp4relay/debian/changelog
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
sonic-dhcp4relay (1.0.0-0) UNRELEASED; urgency=medium

* Initial release.

-- Ashutosh Agrawal <ashu@cisco.com> Thu, 16 Jan 2025 09:11:40 -0700
1 change: 1 addition & 0 deletions dhcp4relay/debian/compat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
12
14 changes: 14 additions & 0 deletions dhcp4relay/debian/control
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Source: sonic-dhcp4relay
Section: devel
Priority: optional
Maintainer: Ashutosh Agrawal <ashu@cisco.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-dhcp4relay
Architecture: any
Built-Using: ${misc:Built-Using}
Depends: ${shlibs:Depends}
Description: SONiC DHCPv4 Relay
6 changes: 6 additions & 0 deletions dhcp4relay/debian/rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/make -f

export DEB_BUILD_MAINT_OPTIONS=hardening=+all

%:
dh $@ --parallel
78 changes: 78 additions & 0 deletions dhcp4relay/src/dhcp4_sender.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "dhcp4_sender.h"

#include <arpa/inet.h>
#include <errno.h>
#include <syslog.h>

#include <cstring>

/**
* @code bool send_udp(int sock, uint8_t *buffer, struct sockaddr_in target, uint32_t len, const char* src_ip, bool use_src_ip);
*
* @brief send udp packet and return true if successful
*
* @param *buffer message buffer
* @param sockaddr_in target target socket
* @param len length of message
* @param src_ip source IP address as string (optional)
* @param use_src_ip if true, use src_ip as source address
*
* @return boolean True if packet successfully sent
*/
#ifndef UNIT_TEST
bool send_udp(int sock, uint8_t *buffer, struct sockaddr_in target, uint32_t len, in_addr src_ip, bool use_src_ip) {
/* Pad additional bytes if length is lesser than 300
* to make DHCP packet length to minimum of 300 bytes */
if (len < BOOTP_MIN_LEN) {
auto pad_len = BOOTP_MIN_LEN - len;
memset(buffer+len, 0, pad_len);
len = BOOTP_MIN_LEN;
}

if (use_src_ip && src_ip.s_addr != 0) {
// Enable IP_PKTINFO on the socket
int on = 1;
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));

struct msghdr msg = {};
struct iovec iov = {};
char cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];

iov.iov_base = buffer;
iov.iov_len = len;

msg.msg_name = &target;
msg.msg_namelen = sizeof(target);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);

struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));

struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
memset(pktinfo, 0, sizeof(struct in_pktinfo));
pktinfo->ipi_spec_dst = src_ip;

msg.msg_controllen = cmsg->cmsg_len;

if (sendmsg(sock, &msg, 0) == -1) {
char server_addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(target.sin_addr), server_addr, INET_ADDRSTRLEN);
syslog(LOG_ERR, "sendmsg: Failed to send to target address: %s, error: %s\n", server_addr, strerror(errno));
return false;
}
} else {
if (sendto(sock, buffer, len, 0, (const struct sockaddr *)&target, sizeof(target)) == -1) {
char server_addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(target.sin_addr), server_addr, INET_ADDRSTRLEN);
syslog(LOG_ERR, "sendto: Failed to send to target address: %s, error: %s\n", server_addr, strerror(errno));
return false;
}
}
return true;
}
#endif
22 changes: 22 additions & 0 deletions dhcp4relay/src/dhcp4_sender.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <netinet/in.h>
#include <sys/socket.h>

#include <string>

#define BOOTP_MIN_LEN 300
/**
* @code bool send_udp(int sock, uint8_t *buffer, struct sockaddr_in target, uint32_t len, const char* src_ip, bool use_src_ip);
*
* @brief send udp packet and return true if successful
*
* @param *buffer message buffer
* @param sockaddr_in target target socket
* @param len length of message
* @param src_ip source IP address as string (optional)
* @param use_src_ip if true, use src_ip as source address
*
* @return boolean True if packet successfully sent
*/
bool send_udp(int sock, uint8_t *buffer, struct sockaddr_in target, uint32_t len, in_addr src_ip, bool use_src_ip);
Loading
Loading