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:
parent
1ba1892c73
commit
2843923549
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -112,3 +112,6 @@
|
|||||||
[submodule "src/sonic-genl-packet"]
|
[submodule "src/sonic-genl-packet"]
|
||||||
path = src/sonic-genl-packet
|
path = src/sonic-genl-packet
|
||||||
url = https://github.com/sonic-net/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
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
SPATH := $($(SONIC_DHCPMON)_SRC_PATH)
|
SPATH := $($(SONIC_DHCPMON)_SRC_PATH)
|
||||||
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/dhcpmon.mk rules/dhcpmon.dep
|
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/dhcpmon.mk rules/dhcpmon.dep
|
||||||
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
|
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)_CACHE_MODE := GIT_CONTENT_SHA
|
||||||
$(SONIC_DHCPMON)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
|
$(SONIC_DHCPMON)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
|
||||||
$(SONIC_DHCPMON)_DEP_FILES := $(DEP_FILES)
|
$(SONIC_DHCPMON)_DEP_FILES := $(DEP_FILES)
|
||||||
|
$(SONIC_DHCPMON)_SMDEP_FILES := $(SMDEP_FILES)
|
||||||
|
$(SONIC_DHCPMON)_SMDEP_PATHS := $(SPATH)
|
||||||
|
1
src/dhcpmon
Submodule
1
src/dhcpmon
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 24364a3c897df22f51e9951a381bfb6ea4996f01
|
5
src/dhcpmon/.gitignore
vendored
5
src/dhcpmon/.gitignore
vendored
@ -1,5 +0,0 @@
|
|||||||
debian/*
|
|
||||||
!debian/changelog
|
|
||||||
!debian/compat
|
|
||||||
!debian/control
|
|
||||||
!debian/rules
|
|
@ -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
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||||||
12
|
|
@ -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
|
|
@ -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
|
|
@ -1,4 +0,0 @@
|
|||||||
USER_OBJS :=
|
|
||||||
|
|
||||||
LIBS := -levent -lexplain -lswsscommon -pthread -lboost_thread -lboost_system -lhiredis
|
|
||||||
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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_ */
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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_ */
|
|
@ -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", ¶ms);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
@ -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_ */
|
|
@ -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;
|
|
||||||
}
|
|
@ -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 ' '
|
|
Loading…
Reference in New Issue
Block a user