Add sonic-dhcpmon as a submodule (#14285)

Why I did it
Add sonic-dhcpmon as a submodule

How to verify it
Tested dhcpmon on dualtor and single tor
This commit is contained in:
kellyyeh 2023-03-30 08:32:56 -07:00 committed by GitHub
parent 1ba1892c73
commit 2843923549
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 7 additions and 2069 deletions

3
.gitmodules vendored
View File

@ -112,3 +112,6 @@
[submodule "src/sonic-genl-packet"]
path = src/sonic-genl-packet
url = https://github.com/sonic-net/sonic-genl-packet
[submodule "src/dhcpmon"]
path = src/dhcpmon
url = https://github.com/sonic-net/sonic-dhcpmon.git

View File

@ -2,9 +2,10 @@
SPATH := $($(SONIC_DHCPMON)_SRC_PATH)
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/dhcpmon.mk rules/dhcpmon.dep
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
DEP_FILES += $(shell git ls-files $(SPATH))
SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files))
$(SONIC_DHCPMON)_CACHE_MODE := GIT_CONTENT_SHA
$(SONIC_DHCPMON)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
$(SONIC_DHCPMON)_DEP_FILES := $(DEP_FILES)
$(SONIC_DHCPMON)_SMDEP_FILES := $(SMDEP_FILES)
$(SONIC_DHCPMON)_SMDEP_PATHS := $(SPATH)

1
src/dhcpmon Submodule

@ -0,0 +1 @@
Subproject commit 24364a3c897df22f51e9951a381bfb6ea4996f01

View File

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

View File

@ -1,45 +0,0 @@
RM := rm -rf
DHCPMON_TARGET := dhcpmon
CP := cp
MKDIR := mkdir
CC := g++
MV := mv
PWD := $(shell pwd)
# All of the sources participating in the build are defined here
-include src/subdir.mk
-include objects.mk
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(strip $(C_DEPS)),)
-include $(C_DEPS)
endif
endif
# Add inputs and outputs from these tool invocations to the build variables
# All Target
all: sonic-dhcpmon
# Tool invocations
sonic-dhcpmon: $(OBJS) $(USER_OBJS)
@echo 'Building target: $@'
@echo 'Invoking: G++ C Linker'
$(CC) -o "$(DHCPMON_TARGET)" $(OBJS) $(USER_OBJS) $(LIBS)
@echo 'Finished building target: $@'
@echo ' '
# Other Targets
install:
$(MKDIR) -p $(DESTDIR)/usr/sbin
$(MV) $(DHCPMON_TARGET) $(DESTDIR)/usr/sbin
deinstall:
$(RM) $(DESTDIR)/usr/sbin/$(DHCPMON_TARGET)
$(RM) -rf $(DESTDIR)/usr/sbin
clean:
-$(RM) $(EXECUTABLES)$(OBJS)$(C_DEPS) $(DHCPMON_TARGET)
-@echo ' '
.PHONY: all clean dependents

View File

@ -1,5 +0,0 @@
sonic-dhcpmon (1.0.0-0) UNRELEASED; urgency=medium
* Initial release.
-- Tamer Ahmed <tamer.ahmed@microsoft.com> Mon, 09 Dec 2019 12:00:00 -0700

View File

@ -1 +0,0 @@
12

View File

@ -1,14 +0,0 @@
Source: sonic-dhcpmon
Section: devel
Priority: optional
Maintainer: Tamer Ahmed <tamer.ahmed@microsoft.com>
Build-Depends: debhelper (>= 12.0.0), libevent-dev, libexplain-dev
Standards-Version: 3.9.3
Homepage: https://github.com/Azure/sonic-buildimage
XS-Go-Import-Path: github.com/Azure/sonic-buildimage
Package: sonic-dhcpmon
Architecture: any
Built-Using: ${misc:Built-Using}
Depends: ${shlibs:Depends}
Description: SONiC DHCP Monitor

View File

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

View File

@ -1,4 +0,0 @@
USER_OBJS :=
LIBS := -levent -lexplain -lswsscommon -pthread -lboost_thread -lboost_system -lhiredis

View File

@ -1,869 +0,0 @@
/**
* @file dhcp_device.c
*
* device (interface) module
*/
#include <err.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/ether.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <syslog.h>
#include <libexplain/ioctl.h>
#include <linux/filter.h>
#include <netpacket/packet.h>
#include "subscriberstatetable.h"
#include "select.h"
#include "dhcp_devman.h"
#include "dhcp_device.h"
/** Counter print width */
#define DHCP_COUNTER_WIDTH 9
/** Start of Ether header of a captured frame */
#define ETHER_START_OFFSET 0
/** Start of IP header of a captured frame */
#define IP_START_OFFSET (ETHER_START_OFFSET + ETHER_HDR_LEN)
/** Start of UDP header of a captured frame */
#define UDP_START_OFFSET (IP_START_OFFSET + sizeof(struct ip))
/** Start of DHCP header of a captured frame */
#define DHCP_START_OFFSET (UDP_START_OFFSET + sizeof(struct udphdr))
/** Start of DHCP Options segment of a captured frame */
#define DHCP_OPTIONS_HEADER_SIZE 240
/** Offset of DHCP GIADDR */
#define DHCP_GIADDR_OFFSET 24
#define CLIENT_IF_PREFIX "Ethernet"
#define OP_LDHA (BPF_LD | BPF_H | BPF_ABS) /** bpf ldh Abs */
#define OP_LDHI (BPF_LD | BPF_H | BPF_IND) /** bpf ldh Ind */
#define OP_LDB (BPF_LD | BPF_B | BPF_ABS) /** bpf ldb Abs*/
#define OP_JEQ (BPF_JMP | BPF_JEQ | BPF_K) /** bpf jeq */
#define OP_JGT (BPF_JMP | BPF_JGT | BPF_K) /** bpf jgt */
#define OP_RET (BPF_RET | BPF_K) /** bpf ret */
#define OP_JSET (BPF_JMP | BPF_JSET | BPF_K) /** bpf jset */
#define OP_LDXB (BPF_LDX | BPF_B | BPF_MSH) /** bpf ldxb */
std::shared_ptr<swss::DBConnector> mConfigDbPtr = std::make_shared<swss::DBConnector> ("CONFIG_DB", 0);
std::shared_ptr<swss::DBConnector> mStateDbPtr = std::make_shared<swss::DBConnector> ("STATE_DB", 0);
std::shared_ptr<swss::Table> mStateDbMuxTablePtr = std::make_shared<swss::Table> (
mStateDbPtr.get(), "HW_MUX_CABLE_TABLE"
);
/* interface to vlan mapping */
std::unordered_map<std::string, std::string> vlan_map;
/* interface to port-channel mapping */
std::unordered_map<std::string, std::string> portchan_map;
/* interface to mgmt port mapping */
std::unordered_map<std::string, std::string> mgmt_map;
/** Berkeley Packet Filter program for "udp and (port 67 or port 68)".
* This program is obtained using the following command tcpdump:
* `tcpdump -dd "outbound and udp and (port 67 or port 68)"`
*/
static struct sock_filter dhcp_outbound_bpf_code[] = {
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0xfffff004}, // (000) ldh #fffff004
{.code = OP_JEQ, .jt = 0, .jf = 22, .k = 0x00000004}, // (001) jeq #0x04 jt 0 jf 22
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x0000000c}, // (002) ldh [12]
{.code = OP_JEQ, .jt = 0, .jf = 7, .k = 0x000086dd}, // (003) jeq #0x86dd jt 2 jf 9
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000014}, // (004) ldb [20]
{.code = OP_JEQ, .jt = 0, .jf = 18, .k = 0x00000011}, // (005) jeq #0x11 jt 4 jf 22
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000036}, // (006) ldh [54]
{.code = OP_JEQ, .jt = 15, .jf = 0, .k = 0x00000043}, // (007) jeq #0x43 jt 21 jf 6
{.code = OP_JEQ, .jt = 14, .jf = 0, .k = 0x00000044}, // (008) jeq #0x44 jt 21 jf 7
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000038}, // (009) ldh [56]
{.code = OP_JEQ, .jt = 12, .jf = 11, .k = 0x00000043}, // (010) jeq #0x43 jt 21 jf 20
{.code = OP_JEQ, .jt = 0, .jf = 12, .k = 0x00000800}, // (011) jeq #0x800 jt 10 jf 22
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000017}, // (012) ldb [23]
{.code = OP_JEQ, .jt = 0, .jf = 10, .k = 0x00000011}, // (013) jeq #0x11 jt 12 jf 22
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000014}, // (014) ldh [20]
{.code = OP_JSET, .jt = 8, .jf = 0, .k = 0x00001fff}, // (015) jset #0x1fff jt 22 jf 14
{.code = OP_LDXB, .jt = 0, .jf = 0, .k = 0x0000000e}, // (016) ldxb 4*([14]&0xf)
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x0000000e}, // (017) ldh [x + 14]
{.code = OP_JEQ, .jt = 4, .jf = 0, .k = 0x00000043}, // (018) jeq #0x43 jt 21 jf 17
{.code = OP_JEQ, .jt = 3, .jf = 0, .k = 0x00000044}, // (019) jeq #0x44 jt 21 jf 18
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x00000010}, // (020) ldh [x + 16]
{.code = OP_JEQ, .jt = 1, .jf = 0, .k = 0x00000043}, // (021) jeq #0x43 jt 21 jf 20
{.code = OP_JEQ, .jt = 0, .jf = 1, .k = 0x00000044}, // (022) jeq #0x44 jt 21 jf 22
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00040000}, // (023) ret #262144
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00000000}, // (024) ret #0
};
/** Berkeley Packet Filter program for "udp and (port 67 or port 68)".
* This program is obtained using the following command tcpdump:
* `tcpdump -dd "inbound and udp and (port 67 or port 68)"`
*/
static struct sock_filter dhcp_inbound_bpf_code[] = {
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0xfffff004}, // (000) ldh #fffff004
{.code = OP_JEQ, .jt = 22, .jf = 0, .k = 0x00000004}, // (001) jeq #0x04 jt 22 jf 0
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x0000000c}, // (002) ldh [12]
{.code = OP_JEQ, .jt = 0, .jf = 7, .k = 0x000086dd}, // (003) jeq #0x86dd jt 2 jf 9
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000014}, // (004) ldb [20]
{.code = OP_JEQ, .jt = 0, .jf = 18, .k = 0x00000011}, // (005) jeq #0x11 jt 4 jf 22
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000036}, // (006) ldh [54]
{.code = OP_JEQ, .jt = 15, .jf = 0, .k = 0x00000043}, // (007) jeq #0x43 jt 21 jf 6
{.code = OP_JEQ, .jt = 14, .jf = 0, .k = 0x00000044}, // (008) jeq #0x44 jt 21 jf 7
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000038}, // (009) ldh [56]
{.code = OP_JEQ, .jt = 12, .jf = 11, .k = 0x00000043}, // (010) jeq #0x43 jt 21 jf 20
{.code = OP_JEQ, .jt = 0, .jf = 12, .k = 0x00000800}, // (011) jeq #0x800 jt 10 jf 22
{.code = OP_LDB, .jt = 0, .jf = 0, .k = 0x00000017}, // (012) ldb [23]
{.code = OP_JEQ, .jt = 0, .jf = 10, .k = 0x00000011}, // (013) jeq #0x11 jt 12 jf 22
{.code = OP_LDHA, .jt = 0, .jf = 0, .k = 0x00000014}, // (014) ldh [20]
{.code = OP_JSET, .jt = 8, .jf = 0, .k = 0x00001fff}, // (015) jset #0x1fff jt 22 jf 14
{.code = OP_LDXB, .jt = 0, .jf = 0, .k = 0x0000000e}, // (016) ldxb 4*([14]&0xf)
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x0000000e}, // (017) ldh [x + 14]
{.code = OP_JEQ, .jt = 4, .jf = 0, .k = 0x00000043}, // (018) jeq #0x43 jt 21 jf 17
{.code = OP_JEQ, .jt = 3, .jf = 0, .k = 0x00000044}, // (019) jeq #0x44 jt 21 jf 18
{.code = OP_LDHI, .jt = 0, .jf = 0, .k = 0x00000010}, // (020) ldh [x + 16]
{.code = OP_JEQ, .jt = 1, .jf = 0, .k = 0x00000043}, // (021) jeq #0x43 jt 21 jf 20
{.code = OP_JEQ, .jt = 0, .jf = 1, .k = 0x00000044}, // (022) jeq #0x44 jt 21 jf 22
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00040000}, // (023) ret #262144
{.code = OP_RET, .jt = 0, .jf = 0, .k = 0x00000000}, // (024) ret #0
};
/** Filter program socket struct */
static struct sock_fprog dhcp_outbound_sock_bfp = {
.len = sizeof(dhcp_outbound_bpf_code) / sizeof(*dhcp_outbound_bpf_code), .filter = dhcp_outbound_bpf_code
};
static struct sock_fprog dhcp_inbound_sock_bfp = {
.len = sizeof(dhcp_inbound_bpf_code) / sizeof(*dhcp_inbound_bpf_code), .filter = dhcp_inbound_bpf_code
};
static uint8_t *rx_recv_buffer = NULL;
static uint8_t *tx_recv_buffer = NULL;
static uint32_t snap_length;
/** Aggregate device of DHCP interfaces. It contains aggregate counters from
all interfaces
*/
static dhcp_device_context_t aggregate_dev = {0};
/** Monitored DHCP message type */
static dhcp_message_type_t monitored_msgs[] = {
DHCP_MESSAGE_TYPE_DISCOVER,
DHCP_MESSAGE_TYPE_OFFER,
DHCP_MESSAGE_TYPE_REQUEST,
DHCP_MESSAGE_TYPE_ACK
};
/** update ethernet interface to vlan map
* VLAN_MEMBER|Vlan1000|Ethernet48
*/
void update_vlan_mapping(std::shared_ptr<swss::DBConnector> db_conn) {
auto match_pattern = std::string("VLAN_MEMBER|*");
auto keys = db_conn->keys(match_pattern);
for (auto &itr : keys) {
auto first = itr.find_first_of('|');
auto second = itr.find_last_of('|');
auto vlan = itr.substr(first + 1, second - first - 1);
auto interface = itr.substr(second + 1);
vlan_map[interface] = vlan;
syslog(LOG_INFO, "add <%s, %s> into interface vlan map\n", interface.c_str(), vlan.c_str());
}
}
/** update ethernet interface to port-channel map
* PORTCHANNEL_MEMBER|PortChannel101|Ethernet112
*/
void update_portchannel_mapping(std::shared_ptr<swss::DBConnector> db_conn) {
auto match_pattern = std::string("PORTCHANNEL_MEMBER|*");
auto keys = db_conn->keys(match_pattern);
for (auto &itr : keys) {
auto first = itr.find_first_of('|');
auto second = itr.find_last_of('|');
auto portchannel = itr.substr(first + 1, second - first - 1);
auto interface = itr.substr(second + 1);
portchan_map[interface] = portchannel;
syslog(LOG_INFO, "add <%s, %s> into interface port-channel map\n", interface.c_str(), portchannel.c_str());
}
}
/** update interface to mgmt map
*/
void update_mgmt_mapping() {
auto mgmt = dhcp_devman_get_mgmt_dev();
if (mgmt) {
auto name = std::string(mgmt->intf);
mgmt_map[name] = name;
}
}
dhcp_device_context_t *find_device_context(std::unordered_map<std::string, struct intf*> *intfs, std::string if_name) {
auto intf = intfs->find(if_name);
if (intf == intfs->end()) {
return NULL;
}
return intf->second->dev_context;
}
/** Number of monitored DHCP message type */
static uint8_t monitored_msg_sz = sizeof(monitored_msgs) / sizeof(*monitored_msgs);
/**
* @code handle_dhcp_option_53(context, dhcp_option, dir, iphdr, dhcphdr);
*
* @brief handle the logic related to DHCP option 53
*
* @param context Device (interface) context
* @param dhcp_option pointer to DHCP option buffer space
* @param dir packet direction
* @param iphdr pointer to packet IP header
* @param dhcphdr pointer to DHCP header
*
* @return none
*/
static void handle_dhcp_option_53(dhcp_device_context_t *context,
const u_char *dhcp_option,
dhcp_packet_direction_t dir,
struct ip *iphdr,
uint8_t *dhcphdr)
{
in_addr_t giaddr;
switch (dhcp_option[2])
{
// DHCP messages send by client
case DHCP_MESSAGE_TYPE_DISCOVER:
case DHCP_MESSAGE_TYPE_REQUEST:
case DHCP_MESSAGE_TYPE_DECLINE:
case DHCP_MESSAGE_TYPE_RELEASE:
case DHCP_MESSAGE_TYPE_INFORM:
giaddr = ntohl(dhcphdr[DHCP_GIADDR_OFFSET] << 24 | dhcphdr[DHCP_GIADDR_OFFSET + 1] << 16 |
dhcphdr[DHCP_GIADDR_OFFSET + 2] << 8 | dhcphdr[DHCP_GIADDR_OFFSET + 3]);
if ((context->giaddr_ip == giaddr && context->is_uplink && dir == DHCP_TX) ||
(!context->is_uplink && dir == DHCP_RX && iphdr->ip_dst.s_addr == INADDR_BROADCAST)) {
context->counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
aggregate_dev.counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
}
break;
// DHCP messages send by server
case DHCP_MESSAGE_TYPE_OFFER:
case DHCP_MESSAGE_TYPE_ACK:
case DHCP_MESSAGE_TYPE_NAK:
if ((context->giaddr_ip == iphdr->ip_dst.s_addr && context->is_uplink && dir == DHCP_RX) ||
(!context->is_uplink && dir == DHCP_TX)) {
context->counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
aggregate_dev.counters[DHCP_COUNTERS_CURRENT][dir][dhcp_option[2]]++;
}
break;
default:
syslog(LOG_WARNING, "handle_dhcp_option_53(%s): Unknown DHCP option 53 type %d", context->intf, dhcp_option[2]);
break;
}
}
/**
* @code client_packet_handler(dhcp_device_context_t *context, ssize_t buffer_sz);
*
* @brief packet handler to process received rx and tx packets
*
* @param context pointer to device (interface) context
* @param buffer_sz buffer that stores received packet data
*
* @return none
*/
static void client_packet_handler(dhcp_device_context_t *context, uint8_t *buffer,
ssize_t buffer_sz, dhcp_packet_direction_t dir)
{
struct ip *iphdr = (struct ip*) (buffer + IP_START_OFFSET);
struct udphdr *udp = (struct udphdr*) (buffer + UDP_START_OFFSET);
uint8_t *dhcphdr = buffer + DHCP_START_OFFSET;
int dhcp_option_offset = DHCP_START_OFFSET + DHCP_OPTIONS_HEADER_SIZE;
if (((unsigned)buffer_sz > UDP_START_OFFSET + sizeof(struct udphdr) + DHCP_OPTIONS_HEADER_SIZE) &&
(ntohs(udp->len) > DHCP_OPTIONS_HEADER_SIZE))
{
int dhcp_sz = ntohs(udp->len) < buffer_sz - UDP_START_OFFSET - sizeof(struct udphdr) ?
ntohs(udp->len) : buffer_sz - UDP_START_OFFSET - sizeof(struct udphdr);
int dhcp_option_sz = dhcp_sz - DHCP_OPTIONS_HEADER_SIZE;
const u_char *dhcp_option = buffer + dhcp_option_offset;
int offset = 0;
while ((offset < (dhcp_option_sz + 1)) && dhcp_option[offset] != 255) {
if (dhcp_option[offset] == OPTION_DHCP_MESSAGE_TYPE) {
if (offset < (dhcp_option_sz + 2)) {
handle_dhcp_option_53(context, &dhcp_option[offset], dir, iphdr, dhcphdr);
}
break; // break while loop since we are only interested in Option 53
}
if (dhcp_option[offset] == 0) { // DHCP Option Padding
offset++;
} else {
offset += dhcp_option[offset + 1] + 2;
}
}
} else {
syslog(LOG_WARNING, "read_callback(%s %s): read length (%ld) is too small to capture DHCP options",
context->intf, dir == DHCP_TX ? "TX" : "RX", buffer_sz);
}
}
static dhcp_device_context_t *interface_to_dev_context(std::unordered_map<std::string, struct intf*> *devices,
std::string ifname)
{
auto vlan = vlan_map.find(ifname);
if (vlan != vlan_map.end()) {
if (dual_tor_sock) {
std::string state;
mStateDbMuxTablePtr->hget(ifname, "state", state);
if (state == "standby") {
return NULL;
}
}
return find_device_context(devices, vlan->second);
} else {
auto port_channel = portchan_map.find(ifname);
if (port_channel != portchan_map.end()) {
return find_device_context(devices, port_channel->second);
}
else {
// mgmt interface check
auto mgmt = mgmt_map.find(ifname);
if (mgmt != mgmt_map.end()) {
return find_device_context(devices, mgmt->second);
}
}
}
return NULL;
}
/**
* @code read_tx_callback(fd, event, arg);
*
* @brief callback for libevent which is called every time out in order to read queued outgoing packet capture
*
* @param fd socket to read from
* @param event libevent triggered event
* @param arg user provided argument for callback (interface context)
*
* @return none
*/
static void read_tx_callback(int fd, short event, void *arg)
{
auto devices = (std::unordered_map<std::string, struct intf*> *)arg;
ssize_t buffer_sz;
struct sockaddr_ll sll;
socklen_t slen = sizeof sll;
dhcp_device_context_t *context = NULL;
while ((buffer_sz = recvfrom(fd, tx_recv_buffer, snap_length, MSG_DONTWAIT, (struct sockaddr *)&sll, &slen)) > 0)
{
char interfaceName[IF_NAMESIZE];
if (if_indextoname(sll.sll_ifindex, interfaceName) == NULL) {
syslog(LOG_WARNING, "invalid output interface index %d\n", sll.sll_ifindex);
continue;
}
std::string intf(interfaceName);
context = find_device_context(devices, intf);
if (context) {
client_packet_handler(context, tx_recv_buffer, buffer_sz, DHCP_TX);
}
}
}
/**
* @code read_rx_callback(fd, event, arg);
*
* @brief callback for libevent which is called every time out in order to read queued incoming packet capture
*
* @param fd socket to read from
* @param event libevent triggered event
* @param arg user provided argument for callback (interface context)
*
* @return none
*/
static void read_rx_callback(int fd, short event, void *arg)
{
auto devices = (std::unordered_map<std::string, struct intf*> *)arg;
ssize_t buffer_sz;
struct sockaddr_ll sll;
socklen_t slen = sizeof(sll);
dhcp_device_context_t *context = NULL;
while ((buffer_sz = recvfrom(fd, rx_recv_buffer, snap_length, MSG_DONTWAIT, (struct sockaddr *)&sll, &slen)) > 0)
{
char interfaceName[IF_NAMESIZE];
if (if_indextoname(sll.sll_ifindex, interfaceName) == NULL) {
syslog(LOG_WARNING, "invalid input interface index %d\n", sll.sll_ifindex);
continue;
}
std::string intf(interfaceName);
context = interface_to_dev_context(devices, intf);
if (context) {
client_packet_handler(context, rx_recv_buffer, buffer_sz, DHCP_RX);
}
}
}
/**
* @code dhcp_device_is_dhcp_inactive(counters);
*
* @brief Check if there were no DHCP activity
*
* @param counters current/snapshot counter
*
* @return true if there were no DHCP activity, false otherwise
*/
static bool dhcp_device_is_dhcp_inactive(uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
uint64_t *rx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_RX];
uint64_t *rx_counter_snapshot = counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX];
bool rv = true;
for (uint8_t i = 0; (i < monitored_msg_sz) && rv; i++) {
rv = rx_counters[monitored_msgs[i]] == rx_counter_snapshot[monitored_msgs[i]];
}
return rv;
}
/**
* @code dhcp_device_is_dhcp_msg_unhealthy(type, counters);
*
* @brief Check if DHCP relay is functioning properly for message of type 'type'.
* For every rx of message 'type', there should be increment of the same message type.
*
* @param type DHCP message type
* @param counters current/snapshot counter
*
* @return true if DHCP message 'type' is transmitted,false otherwise
*/
static bool dhcp_device_is_dhcp_msg_unhealthy(dhcp_message_type_t type,
uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
// check if DHCP message 'type' is being relayed
return ((counters[DHCP_COUNTERS_CURRENT][DHCP_RX][type] > counters[DHCP_COUNTERS_SNAPSHOT][DHCP_RX][type]) &&
(counters[DHCP_COUNTERS_CURRENT][DHCP_TX][type] <= counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX][type]) );
}
/**
* @code dhcp_device_check_positive_health(counters, counters_snapshot);
*
* @brief Check if DHCP relay is functioning properly for monitored messages (Discover, Offer, Request, ACK.)
* For every rx of monitored messages, there should be increment of the same message type.
*
* @param counters current/snapshot counter
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_positive_health(uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
bool is_dhcp_unhealthy = false;
for (uint8_t i = 0; (i < monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
is_dhcp_unhealthy = dhcp_device_is_dhcp_msg_unhealthy(monitored_msgs[i], counters);
}
// if we have rx DORA then we should have corresponding tx DORA (DORA being relayed)
if (is_dhcp_unhealthy) {
rv = DHCP_MON_STATUS_UNHEALTHY;
}
return rv;
}
/**
* @code dhcp_device_check_negative_health(counters);
*
* @brief Check that DHCP relayed messages are not being transmitted out of this interface/dev
* using its counters. The interface is negatively healthy if there are not DHCP message
* travelling through it.
*
* @param counters recent interface counter
* @param counters_snapshot snapshot counters
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_negative_health(uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
uint64_t *tx_counters = counters[DHCP_COUNTERS_CURRENT][DHCP_TX];
uint64_t *tx_counter_snapshot = counters[DHCP_COUNTERS_SNAPSHOT][DHCP_TX];
bool is_dhcp_unhealthy = false;
for (uint8_t i = 0; (i < monitored_msg_sz) && !is_dhcp_unhealthy; i++) {
is_dhcp_unhealthy = tx_counters[monitored_msgs[i]] > tx_counter_snapshot[monitored_msgs[i]];
}
// for negative validation, return unhealthy if DHCP packet are being
// transmitted out of the device/interface
if (is_dhcp_unhealthy) {
rv = DHCP_MON_STATUS_UNHEALTHY;
}
return rv;
}
/**
* @code dhcp_device_check_health(check_type, counters, counters_snapshot);
*
* @brief Check that DHCP relay is functioning properly given a check type. Positive check
* indicates for every rx of DHCP message of type 'type', there would increment of
* the corresponding TX of the same message type. While negative check indicates the
* device should not be actively transmitting any DHCP messages. If it does, it is
* considered unhealthy.
*
* @param check_type type of health check
* @param counters current/snapshot counter
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
static dhcp_mon_status_t dhcp_device_check_health(dhcp_mon_check_t check_type,
uint64_t counters[][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT])
{
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
if (dhcp_device_is_dhcp_inactive(aggregate_dev.counters)) {
rv = DHCP_MON_STATUS_INDETERMINATE;
} else if (check_type == DHCP_MON_CHECK_POSITIVE) {
rv = dhcp_device_check_positive_health(counters);
} else if (check_type == DHCP_MON_CHECK_NEGATIVE) {
rv = dhcp_device_check_negative_health(counters);
}
return rv;
}
/**
* @code dhcp_print_counters(vlan_intf, type, counters);
*
* @brief prints DHCP counters to sylsog.
*
* @param vlan_intf vlan interface name
* @param type counter type
* @param counters interface counter
*
* @return none
*/
static void dhcp_print_counters(const char *vlan_intf,
dhcp_counters_type_t type,
uint64_t counters[][DHCP_MESSAGE_TYPE_COUNT])
{
static const char *counter_desc[DHCP_COUNTERS_COUNT] = {
[DHCP_COUNTERS_CURRENT] = " Current",
[DHCP_COUNTERS_SNAPSHOT] = "Snapshot"
};
syslog(
LOG_NOTICE,
"[%*s-%*s rx/tx] Discover: %*lu/%*lu, Offer: %*lu/%*lu, Request: %*lu/%*lu, ACK: %*lu/%*lu\n",
IF_NAMESIZE, vlan_intf,
(int) strlen(counter_desc[type]), counter_desc[type],
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_DISCOVER],
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_DISCOVER],
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_OFFER],
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_OFFER],
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_REQUEST],
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_REQUEST],
DHCP_COUNTER_WIDTH, counters[DHCP_RX][DHCP_MESSAGE_TYPE_ACK],
DHCP_COUNTER_WIDTH, counters[DHCP_TX][DHCP_MESSAGE_TYPE_ACK]
);
}
/**
* @code init_socket();
*
* @brief initializes rx/tx sockets, bind it to interface and bpf program
*
* @return 0 on success, otherwise for failure
*/
static int init_socket()
{
int rv = -1;
do {
auto rx_sock = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));
auto tx_sock = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));
if (rx_sock < 0 || tx_sock < 0) {
syslog(LOG_ALERT, "socket: failed to open socket with '%s'\n", strerror(errno));
exit(1);
}
struct sockaddr_ll rx_addr;
memset(&rx_addr, 0, sizeof(rx_addr));
rx_addr.sll_ifindex = 0; // any interface
rx_addr.sll_family = AF_PACKET;
rx_addr.sll_protocol = htons(ETH_P_ALL);
if (bind(rx_sock, (struct sockaddr *) &rx_addr, sizeof(rx_addr))) {
syslog(LOG_ALERT, "bind: failed to bind to all interface with '%s'\n", strerror(errno));
break;
}
struct sockaddr_ll tx_addr;
memset(&tx_addr, 0, sizeof(tx_addr));
tx_addr.sll_ifindex = 0; // any interface
tx_addr.sll_family = AF_PACKET;
tx_addr.sll_protocol = htons(ETH_P_ALL);
if (bind(tx_sock, (struct sockaddr *) &tx_addr, sizeof(tx_addr))) {
syslog(LOG_ALERT, "bind: failed to bind to interface with '%s'\n", strerror(errno));
exit(1);
}
for (auto &itr : intfs) {
itr.second->dev_context->rx_sock = rx_sock;
itr.second->dev_context->tx_sock = tx_sock;
}
rv = 0;
} while (0);
return rv;
}
static void init_recv_buffers(int snaplen)
{
snap_length = snaplen;
rx_recv_buffer = (uint8_t *) malloc(snaplen);
if (rx_recv_buffer == NULL) {
syslog(LOG_ALERT, "malloc: failed to allocate memory for socket rx buffer '%s'\n", strerror(errno));
exit(1);
}
tx_recv_buffer = (uint8_t *) malloc(snaplen);
if (tx_recv_buffer == NULL) {
syslog(LOG_ALERT, "malloc: failed to allocate memory for socket tx buffer '%s'\n", strerror(errno));
exit(1);
}
}
/**
* @code initialize_intf_mac_and_ip_addr(context);
*
* @brief initializes device (interface) mac/ip addresses
*
* @param context pointer to device (interface) context
*
* @return 0 on success, otherwise for failure
*/
int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context)
{
int rv = -1;
do {
int fd;
struct ifreq ifr;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
syslog(LOG_ALERT, "socket: %s", strerror(errno));
break;
}
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name, context->intf, sizeof(ifr.ifr_name) - 1);
ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
// Get network address
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) {
syslog(LOG_ALERT, "ioctl: %s", explain_ioctl(fd, SIOCGIFADDR, &ifr));
break;
}
context->ip = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
// Get mac address
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) {
syslog(LOG_ALERT, "ioctl: %s", explain_ioctl(fd, SIOCGIFHWADDR, &ifr));
break;
}
memcpy(context->mac, ifr.ifr_hwaddr.sa_data, sizeof(context->mac));
close(fd);
rv = 0;
} while (0);
return rv;
}
/**
* @code dhcp_device_get_ip(context);
*
* @brief Accessor method
*
* @param context pointer to device (interface) context
*
* @return interface IP
*/
int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip)
{
int rv = -1;
if (context != NULL && ip != NULL) {
*ip = context->ip;
rv = 0;
}
return rv;
}
/**
* @code dhcp_device_get_aggregate_context();
*
* @brief Accessor method
*
* @return pointer to aggregate device (interface) context
*/
dhcp_device_context_t* dhcp_device_get_aggregate_context()
{
return &aggregate_dev;
}
/**
* @code dhcp_device_init(context, intf, is_uplink);
*
* @brief initializes device (interface) that handles packet capture per interface.
*/
int dhcp_device_init(dhcp_device_context_t **context, const char *intf, uint8_t is_uplink)
{
int rv = -1;
dhcp_device_context_t *dev_context = NULL;
if ((context != NULL) && (strlen(intf) < sizeof(dev_context->intf))) {
dev_context = (dhcp_device_context_t *) malloc(sizeof(dhcp_device_context_t));
if (dev_context != NULL) {
// set device name
strncpy(dev_context->intf, intf, sizeof(dev_context->intf) - 1);
dev_context->intf[sizeof(dev_context->intf) - 1] = '\0';
// set device meta data
if (initialize_intf_mac_and_ip_addr(dev_context) == 0) {
dev_context->is_uplink = is_uplink;
memset(dev_context->counters, 0, sizeof(dev_context->counters));
*context = dev_context;
rv = 0;
}
}
else {
syslog(LOG_ALERT, "malloc: failed to allocated device context memory for '%s'", dev_context->intf);
}
}
return rv;
}
/**
* @code dhcp_device_start_capture(snaplen, base, giaddr_ip);
*
* @brief starts packet capture on this interface
*/
int dhcp_device_start_capture(size_t snaplen, struct event_base *base, in_addr_t giaddr_ip)
{
int rv = -1;
struct event *rx_ev;
struct event *tx_ev;
int rx_sock = -1, tx_sock = -1;
do {
if (snaplen < UDP_START_OFFSET + sizeof(struct udphdr) + DHCP_OPTIONS_HEADER_SIZE) {
syslog(LOG_ALERT, "dhcp_device_start_capture: snap length is too low to capture DHCP options");
exit(1);
}
init_socket();
init_recv_buffers(snaplen);
update_vlan_mapping(mConfigDbPtr);
update_portchannel_mapping(mConfigDbPtr);
update_mgmt_mapping();
for (auto &itr : intfs) {
itr.second->dev_context->snaplen = snaplen;
itr.second->dev_context->giaddr_ip = giaddr_ip;
// all interface dev context has same rx/tx socket
rx_sock = itr.second->dev_context->rx_sock;
tx_sock = itr.second->dev_context->tx_sock;
}
if (rx_sock == -1 || tx_sock == -1) {
syslog(LOG_ALERT, "dhcp_device_start_capture: invalid rx_sock or tx_sock");
exit(1);
}
if (setsockopt(rx_sock, SOL_SOCKET, SO_ATTACH_FILTER, &dhcp_inbound_sock_bfp, sizeof(dhcp_inbound_sock_bfp)) != 0) {
syslog(LOG_ALERT, "setsockopt: failed to attach filter with '%s'\n", strerror(errno));
exit(1);
}
if (setsockopt(tx_sock, SOL_SOCKET, SO_ATTACH_FILTER, &dhcp_outbound_sock_bfp, sizeof(dhcp_outbound_sock_bfp)) != 0) {
syslog(LOG_ALERT, "setsockopt: failed to attach filter with '%s'\n", strerror(errno));
exit(1);
}
rx_ev = event_new(base, rx_sock, EV_READ | EV_PERSIST, read_rx_callback, &intfs);
tx_ev = event_new(base, tx_sock, EV_READ | EV_PERSIST, read_tx_callback, &intfs);
if (rx_ev == NULL || tx_ev == NULL) {
syslog(LOG_ALERT, "event_new: failed to allocate memory for libevent event '%s'\n", strerror(errno));
exit(1);
}
event_add(rx_ev, NULL);
event_add(tx_ev, NULL);
rv = 0;
} while (0);
return rv;
}
/**
* @code dhcp_device_shutdown(context);
*
* @brief shuts down device (interface). Also, stops packet capture on interface and cleans up any allocated memory
*/
void dhcp_device_shutdown(dhcp_device_context_t *context)
{
free(context);
}
/**
* @code dhcp_device_get_status(check_type, context);
*
* @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate
* status
*/
dhcp_mon_status_t dhcp_device_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context)
{
dhcp_mon_status_t rv = DHCP_MON_STATUS_HEALTHY;
if (context != NULL) {
rv = dhcp_device_check_health(check_type, context->counters);
}
return rv;
}
/**
* @code dhcp_device_update_snapshot(context);
*
* @brief Update device/interface counters snapshot
*/
void dhcp_device_update_snapshot(dhcp_device_context_t *context)
{
if (context != NULL) {
memcpy(context->counters[DHCP_COUNTERS_SNAPSHOT],
context->counters[DHCP_COUNTERS_CURRENT],
sizeof(context->counters[DHCP_COUNTERS_SNAPSHOT]));
}
}
/**
* @code dhcp_device_print_status(context, type);
*
* @brief prints status counters to syslog.
*/
void dhcp_device_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type)
{
if (context != NULL) {
dhcp_print_counters(context->intf, type, context->counters[type]);
}
}

View File

@ -1,197 +0,0 @@
/**
* @file dhcp_device.h
*
* device (interface) module
*/
#ifndef DHCP_DEVICE_H_
#define DHCP_DEVICE_H_
#include <stdint.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
extern bool dual_tor_sock;
extern std::unordered_map<std::string, struct intf*> intfs;
/**
* DHCP message types
**/
typedef enum
{
DHCP_MESSAGE_TYPE_DISCOVER = 1,
DHCP_MESSAGE_TYPE_OFFER = 2,
DHCP_MESSAGE_TYPE_REQUEST = 3,
DHCP_MESSAGE_TYPE_DECLINE = 4,
DHCP_MESSAGE_TYPE_ACK = 5,
DHCP_MESSAGE_TYPE_NAK = 6,
DHCP_MESSAGE_TYPE_RELEASE = 7,
DHCP_MESSAGE_TYPE_INFORM = 8,
DHCP_MESSAGE_TYPE_COUNT
} dhcp_message_type_t;
enum
{
OPTION_DHCP_MESSAGE_TYPE = 53,
};
/** packet direction */
typedef enum
{
DHCP_RX, /** RX DHCP packet */
DHCP_TX, /** TX DHCP packet */
DHCP_DIR_COUNT
} dhcp_packet_direction_t;
/** counters type */
typedef enum
{
DHCP_COUNTERS_CURRENT, /** DHCP current counters */
DHCP_COUNTERS_SNAPSHOT, /** DHCP snapshot counters */
DHCP_COUNTERS_COUNT
} dhcp_counters_type_t;
/** dhcp health status */
typedef enum
{
DHCP_MON_STATUS_HEALTHY, /** DHCP relay is healthy */
DHCP_MON_STATUS_UNHEALTHY, /** DHCP relay is unhealthy and is missing out on some packets */
DHCP_MON_STATUS_INDETERMINATE, /** DHCP relay health could not be determined */
} dhcp_mon_status_t;
/** dhcp check type */
typedef enum
{
DHCP_MON_CHECK_NEGATIVE, /** Presence of relayed DHCP packets activity is flagged as unhealthy state */
DHCP_MON_CHECK_POSITIVE, /** Validate that received DORA packets are relayed */
} dhcp_mon_check_t;
/** DHCP device (interface) context */
typedef struct
{
int rx_sock; /** Raw socket associated with this device/interface to count rx packets */
int tx_sock; /** Raw socket associated with this device/interface to count tx packets*/
in_addr_t ip; /** network address of this device (interface) */
uint8_t mac[ETHER_ADDR_LEN]; /** hardware address of this device (interface) */
in_addr_t giaddr_ip; /** Gateway IP address */
uint8_t is_uplink; /** north interface? */
char intf[IF_NAMESIZE]; /** device (interface) name */
size_t snaplen; /** snap length or buffer size */
uint64_t counters[DHCP_COUNTERS_COUNT][DHCP_DIR_COUNT][DHCP_MESSAGE_TYPE_COUNT];
/** current/snapshot counters of DHCP packets */
} dhcp_device_context_t;
/**
* @code initialize_intf_mac_and_ip_addr(context);
*
* @brief initializes device (interface) mac/ip addresses
*
* @param context pointer to device (interface) context
*
* @return 0 on success, otherwise for failure
*/
int initialize_intf_mac_and_ip_addr(dhcp_device_context_t *context);
/**
* @code dhcp_device_get_ip(context, ip);
*
* @brief Accessor method
*
* @param context pointer to device (interface) context
* @param ip(out) pointer to device IP
*
* @return 0 on success, otherwise for failure
*/
int dhcp_device_get_ip(dhcp_device_context_t *context, in_addr_t *ip);
/**
* @code dhcp_device_get_aggregate_context();
*
* @brief Accessor method
*
* @return pointer to aggregate device (interface) context
*/
dhcp_device_context_t* dhcp_device_get_aggregate_context();
/**
* @code dhcp_device_init(context, intf, is_uplink);
*
* @brief initializes device (interface) that handles packet capture per interface.
*
* @param context(inout) pointer to device (interface) context
* @param intf interface name
* @param is_uplink uplink interface
*
* @return 0 on success, otherwise for failure
*/
int dhcp_device_init(dhcp_device_context_t **context,
const char *intf,
uint8_t is_uplink);
/**
* @code dhcp_device_start_capture(snaplen, base, giaddr_ip);
*
* @brief starts packet capture on this interface
*
* @param snaplen length of packet capture
* @param base pointer to libevent base
* @param giaddr_ip gateway IP address
*
* @return 0 on success, otherwise for failure
*/
int dhcp_device_start_capture(size_t snaplen, struct event_base *base, in_addr_t giaddr_ip);
/**
* @code dhcp_device_shutdown(context);
*
* @brief shuts down device (interface). Also, stops packet capture on interface and cleans up any allocated memory
*
* @param context Device (interface) context
*
* @return nonedhcp_device_shutdown
*/
void dhcp_device_shutdown(dhcp_device_context_t *context);
/**
* @code dhcp_device_get_status(check_type, context);
*
* @brief collects DHCP relay status info for a given interface. If context is null, it will report aggregate
* status
*
* @param check_type Type of validation
* @param context Device (interface) context
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
dhcp_mon_status_t dhcp_device_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context);
/**
* @code dhcp_device_update_snapshot(context);
*
* @param context Device (interface) context
*
* @brief Update device/interface counters snapshot
*/
void dhcp_device_update_snapshot(dhcp_device_context_t *context);
/**
* @code dhcp_device_print_status(context, type);
*
* @brief prints status counters to syslog. If context is null, it will print aggregate status
*
* @param context Device (interface) context
* @param counters_type Counter type to be printed
*
* @return none
*/
void dhcp_device_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type);
#endif /* DHCP_DEVICE_H_ */

View File

@ -1,226 +0,0 @@
/**
* @file dhcp_devman.c
*
* Device (interface) manager
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include "dhcp_devman.h"
/** Prefix appended to Aggregation device */
#define AGG_DEV_PREFIX "Agg-"
/** intfs map of interfaces */
std::unordered_map<std::string, struct intf*> intfs;
/** dhcp_num_south_intf number of south interfaces */
static uint32_t dhcp_num_south_intf = 0;
/** dhcp_num_north_intf number of north interfaces */
static uint32_t dhcp_num_north_intf = 0;
/** dhcp_num_mgmt_intf number of mgmt interfaces */
static uint32_t dhcp_num_mgmt_intf = 0;
/** On Device vlan interface IP address corresponding vlan downlink IP
* This IP is used to filter Offer/Ack packet coming from DHCP server */
static in_addr_t vlan_ip = 0;
/* Device loopback interface ip, which will be used as the giaddr in dual tor setup. */
static in_addr_t loopback_ip = 0;
/* Whether the device is in dual tor mode, 0 as default for single tor mode. */
static int dual_tor_mode = 0;
/** mgmt interface */
static struct intf *mgmt_intf = NULL;
/**
* @code dhcp_devman_get_vlan_intf();
*
* Accessor method
*/
dhcp_device_context_t* dhcp_devman_get_agg_dev()
{
return dhcp_device_get_aggregate_context();
}
/**
* @code dhcp_devman_get_mgmt_dev();
*
* Accessor method
*/
dhcp_device_context_t* dhcp_devman_get_mgmt_dev()
{
return mgmt_intf ? mgmt_intf->dev_context : NULL;
}
/**
* @code dhcp_devman_shutdown();
*
* shuts down device (interface) manager. Also, stops packet capture on interface and cleans up any allocated
* memory
*/
void dhcp_devman_shutdown()
{
for (auto it = intfs.begin(); it != intfs.end();) {
auto inf = it->second;
dhcp_device_shutdown(inf->dev_context);
it = intfs.erase(it);
free(inf);
}
}
/**
* @code dhcp_devman_add_intf(name, is_uplink);
*
* @brief adds interface to the device manager.
*/
int dhcp_devman_add_intf(const char *name, char intf_type)
{
int rv = -1;
struct intf *dev = (struct intf*) malloc(sizeof(struct intf));
if (dev != NULL) {
dev->name = name;
dev->is_uplink = intf_type != 'd';
switch (intf_type)
{
case 'u':
dhcp_num_north_intf++;
break;
case 'd':
dhcp_num_south_intf++;
assert(dhcp_num_south_intf <= 1);
break;
case 'm':
dhcp_num_mgmt_intf++;
assert(dhcp_num_mgmt_intf <= 1);
mgmt_intf = dev;
break;
default:
break;
}
rv = dhcp_device_init(&dev->dev_context, dev->name, dev->is_uplink);
if (rv == 0 && intf_type == 'd') {
rv = dhcp_device_get_ip(dev->dev_context, &vlan_ip);
dhcp_device_context_t *agg_dev = dhcp_device_get_aggregate_context();
strncpy(agg_dev->intf, AGG_DEV_PREFIX, strlen(AGG_DEV_PREFIX) + 1);
strncpy(agg_dev->intf + strlen(AGG_DEV_PREFIX), name, sizeof(agg_dev->intf) - strlen(AGG_DEV_PREFIX) - 1);
agg_dev->intf[sizeof(agg_dev->intf) - 1] = '\0';
syslog(LOG_INFO, "dhcpmon add aggregate interface '%s'\n", agg_dev->intf);
}
std::string if_name;
if_name.assign(dev->name);
intfs[if_name] = dev;
}
else {
syslog(LOG_ALERT, "malloc: failed to allocate memory for intf '%s'\n", name);
}
return rv;
}
/**
* @code dhcp_devman_setup_dual_tor_mode(name);
*
* @brief set up dual tor mode: 1) set dual_tor_mode flag and 2) retrieve loopback_ip.
*/
int dhcp_devman_setup_dual_tor_mode(const char *name)
{
int rv = -1;
dhcp_device_context_t loopback_intf_context;
if (strlen(name) < sizeof(loopback_intf_context.intf)) {
strncpy(loopback_intf_context.intf, name, sizeof(loopback_intf_context.intf) - 1);
loopback_intf_context.intf[sizeof(loopback_intf_context.intf) - 1] = '\0';
} else {
syslog(LOG_ALERT, "loopback interface name (%s) is too long", name);
return rv;
}
if (initialize_intf_mac_and_ip_addr(&loopback_intf_context) == 0 &&
dhcp_device_get_ip(&loopback_intf_context, &loopback_ip) == 0) {
dual_tor_mode = 1;
} else {
syslog(LOG_ALERT, "failed to retrieve ip addr for loopback interface (%s)", name);
return rv;
}
rv = 0;
return rv;
}
/**
* @code dhcp_devman_start_capture(snaplen, base);
*
* @brief start packet capture on the devman interface list
*/
int dhcp_devman_start_capture(size_t snaplen, struct event_base *base)
{
int rv = -1;
if ((dhcp_num_south_intf == 1) && (dhcp_num_north_intf >= 1)) {
rv = dhcp_device_start_capture(snaplen, base, dual_tor_mode ? loopback_ip : vlan_ip);
if (rv != 0) {
syslog(LOG_ALERT, "Capturing DHCP packets on interface failed");
exit(1);
}
}
else {
syslog(LOG_ERR, "Invalid number of interfaces, downlink/south %d, uplink/north %d\n",
dhcp_num_south_intf, dhcp_num_north_intf);
}
return rv;
}
/**
* @code dhcp_devman_get_status(check_type, context);
*
* @brief collects DHCP relay status info.
*/
dhcp_mon_status_t dhcp_devman_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context)
{
return dhcp_device_get_status(check_type, context);
}
/**
* @code dhcp_devman_update_snapshot(context);
*
* @brief Update device/interface counters snapshot
*/
void dhcp_devman_update_snapshot(dhcp_device_context_t *context)
{
if (context == NULL) {
for (auto &itr : intfs) {
dhcp_device_update_snapshot(itr.second->dev_context);
}
dhcp_device_update_snapshot(dhcp_devman_get_agg_dev());
} else {
dhcp_device_update_snapshot(context);
}
}
/**
* @code dhcp_devman_print_status(context, type);
*
* @brief prints status counters to syslog, if context is null, it prints status counters for all interfaces
*/
void dhcp_devman_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type)
{
if (context == NULL) {
for (auto &itr : intfs) {
dhcp_device_print_status(itr.second->dev_context, type);
}
dhcp_device_print_status(dhcp_devman_get_agg_dev(), type);
} else {
dhcp_device_print_status(context, type);
}
}

View File

@ -1,132 +0,0 @@
/**
* @file dhcp_devman.h
*
* Device (interface) manager
*/
#ifndef DHCP_DEVMAN_H_
#define DHCP_DEVMAN_H_
#include <stdint.h>
#include <string>
#include <unordered_map>
#include "dhcp_device.h"
/** struct for interface information */
struct intf
{
const char *name; /** interface name */
uint8_t is_uplink; /** is uplink (north) interface */
dhcp_device_context_t *dev_context; /** device (interface_ context */
};
/**
* @code dhcp_devman_init();
*
* @brief initializes device (interface) manager that keeps track of interfaces and assert that there is one south
* interface and as many north interfaces
*
* @return none
*/
void dhcp_devman_init();
/**
* @code dhcp_devman_shutdown();
*
* @brief shuts down device (interface) manager. Also, stops packet capture on interface and cleans up any allocated
* memory
*
* @return none
*/
void dhcp_devman_shutdown();
/**
* @code dhcp_devman_get_vlan_intf();
*
* @brief Accessor method
*
* @return pointer to aggregate device (interface) context
*/
dhcp_device_context_t* dhcp_devman_get_agg_dev();
/**
* @code dhcp_devman_get_mgmt_intf_context();
*
* @brief Accessor method
*
* @return pointer to mgmt interface context
*/
dhcp_device_context_t* dhcp_devman_get_mgmt_dev();
/**
* @code dhcp_devman_add_intf(name, uplink);
*
* @brief adds interface to the device manager.
*
* @param name interface name
* @param intf_type 'u' for uplink (north) interface
* 'd' for downlink (south) interface
* 'm' for mgmt interface
*
* @return 0 on success, nonzero otherwise
*/
int dhcp_devman_add_intf(const char *name, char intf_type);
/**
* @code dhcp_devman_setup_dual_tor_mode(name);
*
* @brief set up dual tor mode: 1) set dual_tor_mode flag and 2) retrieve loopback_ip.
*
* @param name interface name
*
* @return 0 on success, nonzero otherwise
*/
int dhcp_devman_setup_dual_tor_mode(const char *name);
/**
* @code dhcp_devman_start_capture(snaplen, base);
*
* @brief start packet capture on the devman interface list
*
* @param snaplen packet packet capture snap length
* @param base libevent base
*
* @return 0 on success, nonzero otherwise
*/
int dhcp_devman_start_capture(size_t snaplen, struct event_base *base);
/**
* @code dhcp_devman_get_status(check_type, context);
*
* @brief collects DHCP relay status info.
*
* @param check_type Type of validation
* @param context pointer to device (interface) context
*
* @return DHCP_MON_STATUS_HEALTHY, DHCP_MON_STATUS_UNHEALTHY, or DHCP_MON_STATUS_INDETERMINATE
*/
dhcp_mon_status_t dhcp_devman_get_status(dhcp_mon_check_t check_type, dhcp_device_context_t *context);
/**
* @code dhcp_devman_update_snapshot(context);
*
* @param context Device (interface) context
*
* @brief Update device/interface counters snapshot
*/
void dhcp_devman_update_snapshot(dhcp_device_context_t *context);
/**
* @code dhcp_devman_print_status(context, type);
*
* @brief prints status counters to syslog
*
* @param context pointer to device (interface) context
* @param type Counter type to be printed
*
* @return none
*/
void dhcp_devman_print_status(dhcp_device_context_t *context, dhcp_counters_type_t type);
#endif /* DHCP_DEVMAN_H_ */

View File

@ -1,285 +0,0 @@
/**
* @file dhcp_mon.c
*
* @brief dhcp relay monitor module
*/
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <syslog.h>
#include <assert.h>
#include "dhcp_mon.h"
#include "dhcp_devman.h"
#include "events.h"
/** DHCP device/interface state */
typedef struct
{
dhcp_mon_check_t check_type; /** check type */
dhcp_device_context_t* (*get_context)(); /** functor to a device context accessor function */
int count; /** count in the number of unhealthy checks */
const char *msg; /** message to be printed if unhealthy state is determined */
} dhcp_mon_state_t;
/** window_interval_sec monitoring window for dhcp relay health checks */
static int window_interval_sec = 18;
/** dhcp_unhealthy_max_count max count of consecutive unhealthy statuses before reporting to syslog */
static int dhcp_unhealthy_max_count = 10;
/** dhcpmon debug mode control flag */
static bool debug_on = false;
/** libevent base struct */
static struct event_base *base;
/** libevent timeout event struct */
static struct event *ev_timeout = NULL;
/** libevent SIGINT signal event struct */
static struct event *ev_sigint;
/** libevent SIGTERM signal event struct */
static struct event *ev_sigterm;
/** libevent SIGUSR1 signal event struct */
static struct event *ev_sigusr1;
event_handle_t g_events_handle;
/** DHCP monitor state data for aggregate device for mgmt device */
static dhcp_mon_state_t state_data[] = {
[0] = {
.check_type = DHCP_MON_CHECK_POSITIVE,
.get_context = dhcp_devman_get_agg_dev,
.count = 0,
.msg = "dhcpmon detected disparity in DHCP Relay behavior. Duration: %d (sec) for vlan: '%s'\n"
},
[1] = {
.check_type = DHCP_MON_CHECK_NEGATIVE,
.get_context = dhcp_devman_get_mgmt_dev,
.count = 0,
.msg = "dhcpmon detected DHCP packets traveling through mgmt interface (please check BGP routes.)"
" Duration: %d (sec) for intf: '%s'\n"
}
};
/**
* @code signal_callback(fd, event, arg);
*
* @brief signal handler for dhcpmon. It will initiate shutdown when signal is caught
*
* @param fd libevent socket
* @param event event triggered
* @param arg pointer to user provided context (libevent base)
*
* @return none
*/
static void signal_callback(evutil_socket_t fd, short event, void *arg)
{
syslog(LOG_ALERT, "Received signal: '%s'\n", strsignal(fd));
dhcp_devman_print_status(NULL, DHCP_COUNTERS_CURRENT);
if ((fd == SIGTERM) || (fd == SIGINT)) {
dhcp_mon_stop();
}
}
/**
* @code check_dhcp_relay_health(state_data);
*
* @brief check DHCP relay overall health
*
* @param state_data pointer to dhcpmon state data
*
* @return none
*/
static void check_dhcp_relay_health(dhcp_mon_state_t *state_data)
{
dhcp_device_context_t *context = state_data->get_context();
dhcp_mon_status_t dhcp_mon_status = dhcp_devman_get_status(state_data->check_type, context);
switch (dhcp_mon_status)
{
case DHCP_MON_STATUS_UNHEALTHY:
if (++state_data->count > dhcp_unhealthy_max_count) {
auto duration = state_data->count * window_interval_sec;
std::string vlan(context->intf);
syslog(LOG_ALERT, state_data->msg, duration, context->intf);
if (state_data->check_type == DHCP_MON_CHECK_POSITIVE) {
event_params_t params = {
{ "vlan", vlan },
{ "duration", std::to_string(duration) }};
event_publish(g_events_handle, "dhcp-relay-disparity", &params);
}
dhcp_devman_print_status(context, DHCP_COUNTERS_SNAPSHOT);
dhcp_devman_print_status(context, DHCP_COUNTERS_CURRENT);
}
break;
case DHCP_MON_STATUS_HEALTHY:
state_data->count = 0;
break;
case DHCP_MON_STATUS_INDETERMINATE:
if (state_data->count) {
state_data->count++;
}
break;
default:
syslog(LOG_ERR, "DHCP Relay returned unknown status %d\n", dhcp_mon_status);
break;
}
}
/**
* @code timeout_callback(fd, event, arg);
*
* @brief periodic timer call back
*
* @param fd libevent socket
* @param event event triggered
* @param arg pointer user provided context (libevent base)
*
* @return none
*/
static void timeout_callback(evutil_socket_t fd, short event, void *arg)
{
for (uint8_t i = 0; i < sizeof(state_data) / sizeof(*state_data); i++) {
check_dhcp_relay_health(&state_data[i]);
}
dhcp_devman_update_snapshot(NULL);
if (debug_on) {
dhcp_devman_print_status(NULL, DHCP_COUNTERS_SNAPSHOT);
dhcp_devman_print_status(NULL, DHCP_COUNTERS_CURRENT);
}
}
/**
* @code dhcp_mon_init(window_sec, max_count);
*
* initializes event base and periodic timer event that continuously collects dhcp relay health status every window_sec
* seconds. It also writes to syslog when dhcp relay has been unhealthy for consecutive max_count checks.
*
*/
int dhcp_mon_init(int window_sec, int max_count)
{
int rv = -1;
do {
window_interval_sec = window_sec;
dhcp_unhealthy_max_count = max_count;
base = event_base_new();
if (base == NULL) {
syslog(LOG_ERR, "Could not initialize libevent!\n");
break;
}
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;
}
ev_sigusr1 = evsignal_new(base, SIGUSR1, signal_callback, base);
if (ev_sigusr1 == NULL) {
syslog(LOG_ERR, "Could not create SIGUSER1 libevent signal!\n");
break;
}
ev_timeout = event_new(base, -1, EV_PERSIST, timeout_callback, base);
if (ev_timeout == NULL) {
syslog(LOG_ERR, "Could not create libevent timer!\n");
break;
}
g_events_handle = events_init_publisher("sonic-events-dhcp-relay");
rv = 0;
} while (0);
return rv;
}
/**
* @code dhcp_mon_shutdown();
*
* @brief shuts down libevent loop
*/
void dhcp_mon_shutdown()
{
event_del(ev_timeout);
event_del(ev_sigint);
event_del(ev_sigterm);
event_del(ev_sigusr1);
event_free(ev_timeout);
event_free(ev_sigint);
event_free(ev_sigterm);
event_free(ev_sigusr1);
event_base_free(base);
events_deinit_publisher(g_events_handle);
}
/**
* @code dhcp_mon_start(snaplen, debug_mode);
*
* @brief start monitoring DHCP Relay
*/
int dhcp_mon_start(size_t snaplen, bool debug_mode)
{
int rv = -1;
debug_on = debug_mode;
do
{
if (dhcp_devman_start_capture(snaplen, base) != 0) {
break;
}
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 (evsignal_add(ev_sigusr1, NULL) != 0) {
syslog(LOG_ERR, "Could not add SIGUSR1 libevent signal!\n");
break;
}
struct timeval event_time = {.tv_sec = window_interval_sec, .tv_usec = 0};
if (evtimer_add(ev_timeout, &event_time) != 0) {
syslog(LOG_ERR, "Could not add event timer to libevent!\n");
break;
}
if (event_base_dispatch(base) != 0) {
syslog(LOG_ERR, "Could not start libevent dispatching loop!\n");
break;
}
rv = 0;
} while (0);
return rv;
}
/**
* @code dhcp_mon_stop();
*
* @brief stop monitoring DHCP Relay
*/
void dhcp_mon_stop()
{
event_base_loopexit(base, NULL);
}

View File

@ -1,55 +0,0 @@
/**
* @file dhcp_mon.h
*
* @brief dhcp relay monitor module
*
*/
#ifndef DHCP_MON_H_
#define DHCP_MON_H_
/**
* @code dhcp_mon_init(window_ssec, max_count);
*
* @brief initializes event base and periodic timer event that continuously collects dhcp relay health status every
* window_sec seconds. It also writes to syslog when dhcp relay has been unhealthy for consecutive max_count
* checks.
*
* @param window_sec time interval between health checks
* @param max_count max count of consecutive unhealthy statuses before reporting to syslog
*
* @return 0 upon success, otherwise upon failure
*/
int dhcp_mon_init(int window_sec, int max_count);
/**
* @code dhcp_mon_shutdown();
*
* @brief shuts down libevent loop
*
* @return none
*/
void dhcp_mon_shutdown();
/**
* @code dhcp_mon_start(snaplen, debug);
*
* @brief start monitoring DHCP Relay
*
* @param snaplen packet capture length
* @param debug turn on debug or not
*
* @return 0 upon success, otherwise upon failure
*/
int dhcp_mon_start(size_t snaplen, bool debug);
/**
* @code dhcp_mon_stop();
*
* @brief stop monitoring DHCP Relay
*
* @return none
*/
void dhcp_mon_stop();
#endif /* DHCP_MON_H_ */

View File

@ -1,191 +0,0 @@
/**
* @file main.c
*
* @brief: Main entry point for dhcpmon utility.
*
*/
#include <err.h>
#include <errno.h>
#include <libgen.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "subscriberstatetable.h"
#include "select.h"
#include "dhcp_mon.h"
#include "dhcp_devman.h"
#include "dhcp_device.h"
/** dhcpmon_default_snaplen: default snap length of packet being captured */
static const size_t dhcpmon_default_snaplen = 65535;
/** dhcpmon_default_health_check_window: default value for a time window, during which DHCP DORA packet counts are being
* collected */
static const uint32_t dhcpmon_default_health_check_window = 18;
/** dhcpmon_default_unhealthy_max_count: default max consecutive unhealthy status reported before reporting an issue
* with DHCP relay */
static const uint32_t dhcpmon_default_unhealthy_max_count = 10;
bool dual_tor_sock = false;
/**
* @code usage(prog);
*
* @brief prints help message about how to use dhcpmon utility
*
* @param prog program name
*
* @return none
*/
static void usage(const char *prog)
{
printf("Usage: %s -id <south interface> {-iu <north interface>}+ -im <mgmt interface> [-u <loopback interface>]"
"[-w <snapshot window in sec>] [-c <unhealthy status count>] [-s <snap length>] [-D] [-d]\n", prog);
printf("where\n");
printf("\tsouth interface: is a vlan interface,\n");
printf("\tnorth interface: is a TOR-T1 interface,\n");
printf("\tloopback interface: is the loopback interface for dual tor setup,\n");
printf("\tsnapshot window: during which DHCP counters are gathered and DHCP status is validated (default %d),\n",
dhcpmon_default_health_check_window);
printf("\tunhealthy status count: count of consecutive unhealthy status before writing an alert to syslog "
"(default %d),\n",
dhcpmon_default_unhealthy_max_count);
printf("\tsnap length: snap length of packet capture (default %ld),\n", dhcpmon_default_snaplen);
printf("\t-D: debug mode: print counter to syslog\n");
printf("\t-d: daemonize %s.\n", prog);
exit(EXIT_SUCCESS);
}
/**
* @code dhcpmon_daemonize();
*
* @brief make this utility run as a daemon.
*
* @return none
*/
static void dhcpmon_daemonize()
{
pid_t pid, sid;
pid = fork();
if (pid < 0) {
syslog(LOG_ALERT, "fork: %s", strerror(errno));
exit(EXIT_FAILURE);
}
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// this is the daemon running now
umask(0);
// Create a new SID for the child process
sid = setsid();
if (sid < 0) {
syslog(LOG_ALERT, "setsid: %s", strerror(errno));
exit(EXIT_FAILURE);
}
// Change the current working directory
if ((chdir("/")) < 0) {
syslog(LOG_ALERT, "chdir: %s", strerror(errno));
exit(EXIT_FAILURE);
}
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
/**
* @code main(argc, argv);
*
* @brief main entry point of dhcpmon utility
*
* @return int 0 on success, otherwise on failure
*/
int main(int argc, char **argv)
{
int rv = EXIT_FAILURE;
int i;
int window_interval = dhcpmon_default_health_check_window;
int max_unhealthy_count = dhcpmon_default_unhealthy_max_count;
size_t snaplen = dhcpmon_default_snaplen;
int make_daemon = 0;
bool debug_mode = false;
setlogmask(LOG_UPTO(LOG_INFO));
openlog(basename(argv[0]), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON);
for (i = 1; i < argc;) {
if ((argv[i] == NULL) || (argv[i][0] != '-')) {
break;
}
switch (argv[i][1])
{
case 'h':
usage(basename(argv[0]));
break;
case 'i':
if (dhcp_devman_add_intf(argv[i + 1], argv[i][2]) != 0) {
usage(basename(argv[0]));
}
i += 2;
break;
case 'u':
dual_tor_sock = true;
if (dhcp_devman_setup_dual_tor_mode(argv[i + 1]) != 0) {
usage(basename(argv[0]));
}
i += 2;
break;
case 'd':
make_daemon = 1;
i++;
break;
case 's':
snaplen = atoi(argv[i + 1]);
i += 2;
break;
case 'w':
window_interval = atoi(argv[i + 1]);
i += 2;
break;
case 'c':
max_unhealthy_count = atoi(argv[i + 1]);
i += 2;
break;
case 'D':
debug_mode = true;
i += 1;
break;
default:
fprintf(stderr, "%s: %c: Unknown option\n", basename(argv[0]), argv[i][1]);
usage(basename(argv[0]));
}
}
if (make_daemon) {
dhcpmon_daemonize();
}
if ((dhcp_mon_init(window_interval, max_unhealthy_count) == 0) &&
(dhcp_mon_start(snaplen, debug_mode) == 0)) {
rv = EXIT_SUCCESS;
dhcp_mon_shutdown();
}
dhcp_devman_shutdown();
closelog();
return rv;
}

View File

@ -1,29 +0,0 @@
# Add inputs and outputs from these tool invocations to the build variables
CC := g++
C_SRCS += \
../src/dhcp_device.cpp \
../src/dhcp_devman.cpp \
../src/dhcp_mon.cpp \
../src/main.cpp
OBJS += \
./src/dhcp_device.o \
./src/dhcp_devman.o \
./src/dhcp_mon.o \
./src/main.o
C_DEPS += \
./src/dhcp_device.d \
./src/dhcp_devman.d \
./src/dhcp_mon.d \
./src/main.d
# Each subdirectory must supply rules for building sources it contributes
src/%.o: src/%.cpp
@echo 'Building file: $<'
@echo 'Invoking: GCC C Compiler'
$(CC) -O3 -g3 -Wall -I$(PWD)/../sonic-swss-common/common -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<"
@echo 'Finished building: $<'
@echo ' '